Use Accordion component from new macaw (#3712)

This commit is contained in:
Paweł Chyła 2023-07-10 10:24:34 +02:00 committed by GitHub
parent 093388c46f
commit 0ea8f32378
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
14 changed files with 220 additions and 381 deletions

View file

@ -0,0 +1,5 @@
---
"saleor-dashboard": patch
---
Replace all old Accordion components with new from macaw-ui

View file

@ -1,15 +1,15 @@
// @ts-strict-ignore // @ts-strict-ignore
import Skeleton from "@dashboard/components/Skeleton"; import Skeleton from "@dashboard/components/Skeleton";
import { ReorderEvent } from "@dashboard/types"; import { ReorderEvent } from "@dashboard/types";
import { Accordion, Divider, Typography } from "@material-ui/core"; import { Typography } from "@material-ui/core";
import { Accordion, Divider } from "@saleor/macaw-ui/next";
import React from "react"; import React from "react";
import { defineMessages, useIntl } from "react-intl"; import { defineMessages, useIntl } from "react-intl";
import AssignmentListFooter from "./AssignmentListFooter"; import AssignmentListFooter from "./AssignmentListFooter";
import AssignmentListHeader from "./AssignmentListHeader";
import Item from "./Item"; import Item from "./Item";
import SortableContainer from "./SortableContainer"; import SortableContainer from "./SortableContainer";
import { useExpanderStyles, useStyles } from "./styles"; import { useStyles } from "./styles";
import { AssignmentListProps } from "./types"; import { AssignmentListProps } from "./types";
const messages = defineMessages({ const messages = defineMessages({
@ -32,7 +32,6 @@ const AssignmentList: React.FC<AssignmentListProps> = props => {
const intl = useIntl(); const intl = useIntl();
const classes = useStyles(); const classes = useStyles();
const expanderClasses = useExpanderStyles();
const handleSortStart = () => { const handleSortStart = () => {
document.body.classList.add(classes.grabbing); document.body.classList.add(classes.grabbing);
@ -46,51 +45,61 @@ const AssignmentList: React.FC<AssignmentListProps> = props => {
const hasMoreItemsToBeSelected = totalCount !== items.length; const hasMoreItemsToBeSelected = totalCount !== items.length;
return ( return (
<Accordion classes={expanderClasses}> <Accordion paddingX={7} paddingBottom={2} paddingTop={4}>
<AssignmentListHeader <Accordion.Item value="accordionItemId">
assignCount={items.length} <Accordion.Trigger paddingBottom={4}>
itemsName={itemsName} {loading ? (
loading={loading} <Skeleton />
/>
<Divider />
{loading ? (
<Skeleton className={classes.skeleton} />
) : (
<>
<SortableContainer
axis="xy"
lockAxis="xy"
useDragHandle
onSortStart={handleSortStart}
onSortEnd={handleSortEnd}
>
<div>
{items.map((item, itemIndex) => (
<Item
key={itemIndex}
index={itemIndex}
item={item}
onDelete={removeItem}
sortable={!!reorderItem}
/>
))}
</div>
</SortableContainer>
{hasMoreItemsToBeSelected ? (
<AssignmentListFooter {...props} />
) : ( ) : (
<Typography <Typography variant="subtitle2" color="textSecondary">
color="textSecondary" {`${items.length} ${itemsName.toLowerCase()}`}
variant="subtitle1"
className={classes.infoMessage}
>
{intl.formatMessage(messages.allSelectedMessage, {
itemsName: itemsName.toLowerCase(),
})}
</Typography> </Typography>
)} )}
</> <Accordion.TriggerButton dataTestId="expand-icon" />
)} </Accordion.Trigger>
<Accordion.Content>
<Divider />
{loading ? (
<Skeleton className={classes.skeleton} />
) : (
<>
<SortableContainer
axis="xy"
lockAxis="xy"
useDragHandle
onSortStart={handleSortStart}
onSortEnd={handleSortEnd}
>
<div>
{items.map((item, itemIndex) => (
<Item
key={itemIndex}
index={itemIndex}
item={item}
onDelete={removeItem}
sortable={!!reorderItem}
/>
))}
</div>
</SortableContainer>
{hasMoreItemsToBeSelected ? (
<AssignmentListFooter {...props} />
) : (
<Typography
color="textSecondary"
variant="subtitle1"
className={classes.infoMessage}
>
{intl.formatMessage(messages.allSelectedMessage, {
itemsName: itemsName.toLowerCase(),
})}
</Typography>
)}
</>
)}
</Accordion.Content>
</Accordion.Item>
</Accordion> </Accordion>
); );
}; };

View file

@ -1,38 +0,0 @@
import HorizontalSpacer from "@dashboard/components/HorizontalSpacer";
import Skeleton from "@dashboard/components/Skeleton";
import { Typography } from "@material-ui/core";
import { AccordionSummary } from "@saleor/macaw-ui";
import React from "react";
import { useHeaderStyles } from "./styles";
interface AssignmentListHeaderProps {
assignCount: number;
itemsName: string;
loading: boolean;
}
const AssignmentListHeader: React.FC<AssignmentListHeaderProps> = ({
assignCount,
itemsName,
loading,
}) => {
const { container, skeleton, ...accordion } = useHeaderStyles();
return (
<div className={container}>
<AccordionSummary className={accordion.root}>
{loading ? (
<Skeleton className={skeleton} />
) : (
<Typography variant="subtitle2" color="textSecondary">
{`${assignCount} ${itemsName.toLowerCase()}`}
</Typography>
)}
</AccordionSummary>
<HorizontalSpacer spacing={1.5} />
</div>
);
};
export default AssignmentListHeader;

View file

@ -1,5 +1,5 @@
import DeletableItem from "@dashboard/components/DeletableItem"; import DeletableItem from "@dashboard/components/DeletableItem";
import { Divider, Typography } from "@material-ui/core"; import { Divider, Text } from "@saleor/macaw-ui/next";
import React from "react"; import React from "react";
import { SortableElement, SortableElementProps } from "react-sortable-hoc"; import { SortableElement, SortableElementProps } from "react-sortable-hoc";
@ -28,7 +28,7 @@ const Item = SortableElement(
data-test-id="button-drag-handle" data-test-id="button-drag-handle"
/> />
)} )}
<Typography>{name}</Typography> <Text size="small">{name}</Text>
</div> </div>
<DeletableItem id={id} onDelete={onDelete} /> <DeletableItem id={id} onDelete={onDelete} />
</div> </div>

View file

@ -53,7 +53,6 @@ export const useHeaderStyles = makeStyles(
export const useStyles = makeStyles( export const useStyles = makeStyles(
theme => ({ theme => ({
container: { container: {
padding: theme.spacing(1, 0),
display: "flex", display: "flex",
flexDirection: "row", flexDirection: "row",
justifyContent: "space-between", justifyContent: "space-between",

View file

@ -1,39 +1,11 @@
import { Typography } from "@material-ui/core"; import {
import AddIcon from "@material-ui/icons/Add"; Accordion as AccordionMacaw,
import RemoveIcon from "@material-ui/icons/Remove"; Box,
import { IconButton, makeStyles } from "@saleor/macaw-ui"; Divider,
import clsx from "clsx"; sprinkles,
import React from "react"; Text,
} from "@saleor/macaw-ui/next";
import Hr from "../Hr"; import React, { useState } from "react";
const useStyles = makeStyles(
theme => ({
content: {
padding: theme.spacing(3, 0),
},
expandButton: {
position: "relative",
right: theme.spacing(-2),
top: theme.spacing(0.5),
},
root: {
border: `1px solid ${theme.palette.divider}`,
borderRadius: 12,
padding: theme.spacing(0, 3),
},
title: {
display: "flex",
justifyContent: "space-between",
},
titleText: {
padding: theme.spacing(2, 0),
},
}),
{
name: "Accordion",
},
);
export interface AccordionProps { export interface AccordionProps {
className?: string; className?: string;
@ -42,38 +14,52 @@ export interface AccordionProps {
title: string; title: string;
} }
const AccordionItemId = "accordionItemId";
const Accordion: React.FC<AccordionProps> = ({ const Accordion: React.FC<AccordionProps> = ({
children, children,
className,
initialExpand, initialExpand,
quickPeek, quickPeek,
title, title,
...props className,
}) => { }) => {
const classes = useStyles({}); const [openedAccordionId, setOpendAccordionId] = useState<undefined | string>(
const [expanded, setExpanded] = React.useState(!!initialExpand); !!initialExpand ? AccordionItemId : undefined,
);
return ( return (
<div className={clsx(classes.root, className)} {...props}> <div className={className}>
<div className={classes.title}> <AccordionMacaw
<Typography className={classes.titleText}>{title}</Typography> value={openedAccordionId}
<div className={classes.expandButton}> onValueChange={value => setOpendAccordionId(value)}
<IconButton className={sprinkles({
variant="secondary" borderStyle: "solid",
onClick={() => setExpanded(!expanded)} borderWidth: 1,
> borderColor: "neutralPlain",
{expanded ? <RemoveIcon /> : <AddIcon />} paddingX: 4,
</IconButton> borderRadius: 5,
</div> })}
</div> >
{(expanded || !!quickPeek) && ( <AccordionMacaw.Item value={AccordionItemId}>
<> <AccordionMacaw.Trigger>
<Hr /> <Text paddingY={3} variant="body" size="small">
<div className={classes.content}> {title}
{quickPeek ? (expanded ? children : quickPeek) : children} </Text>
</div> <AccordionMacaw.TriggerButton dataTestId="expand-icon" />
</> </AccordionMacaw.Trigger>
)} <AccordionMacaw.Content>
<Divider />
<Box paddingY={3}>{children}</Box>
</AccordionMacaw.Content>
</AccordionMacaw.Item>
{!openedAccordionId && !!quickPeek && (
<>
<Divider />
<Box paddingY={4}>{quickPeek}</Box>
</>
)}
</AccordionMacaw>
</div> </div>
); );
}; };

View file

@ -88,6 +88,7 @@ export const Attributes: React.FC<AttributesProps> = ({
}} }}
/> />
</Text> </Text>
<Accordion.TriggerButton dataTestId="expand-icon" />
</Accordion.Trigger> </Accordion.Trigger>
<Accordion.Content> <Accordion.Content>
{attributes.length > 0 && ( {attributes.length > 0 && (

View file

@ -17,9 +17,10 @@ export const ChannelAvailabilityItemWrapper: React.FC<
> = ({ data: { name }, messages, children }) => ( > = ({ data: { name }, messages, children }) => (
<Accordion data-test-id="channel-availability-item"> <Accordion data-test-id="channel-availability-item">
<Accordion.Item value="channel-availability-item" gap={9}> <Accordion.Item value="channel-availability-item" gap={9}>
<Accordion.Trigger buttonDataTestId="expand-icon"> <Accordion.Trigger>
<Text variant={"bodyEmp"}>{name}</Text> <Text variant={"bodyEmp"}>{name}</Text>
<Label text={messages.availableDateText} /> <Label text={messages.availableDateText} />
<Accordion.TriggerButton dataTestId="expand-icon" />
</Accordion.Trigger> </Accordion.Trigger>
<Accordion.Content paddingLeft={3}>{children}</Accordion.Content> <Accordion.Content paddingLeft={3}>{children}</Accordion.Content>
</Accordion.Item> </Accordion.Item>

View file

@ -39,7 +39,7 @@ const FilterContentBodyNameField: React.FC<FilterContentBodyNameFieldProps> = ({
} }
label={filter.label} label={filter.label}
onClick={event => event.stopPropagation()} onClick={event => event.stopPropagation()}
onChange={() => onChange={() => {
onFilterPropertyChange({ onFilterPropertyChange({
payload: { payload: {
name: filter.name, name: filter.name,
@ -48,8 +48,8 @@ const FilterContentBodyNameField: React.FC<FilterContentBodyNameFieldProps> = ({
}, },
}, },
type: "set-property", type: "set-property",
}) });
} }}
/> />
</div> </div>
); );

View file

@ -1,80 +1,8 @@
import { Typography } from "@material-ui/core"; import { Accordion, Box, sprinkles, Text } from "@saleor/macaw-ui/next";
import {
Accordion,
AccordionDetails,
AccordionSummary,
makeStyles,
} from "@saleor/macaw-ui";
import React from "react"; import React from "react";
import TimelineEventHeader, { TitleElement } from "./TimelineEventHeader"; import TimelineEventHeader, { TitleElement } from "./TimelineEventHeader";
const useStyles = makeStyles(
theme => ({
dot: {
backgroundColor: theme.palette.primary.main,
borderRadius: "100%",
height: 7,
left: -28,
position: "absolute",
top: 6,
width: 7,
},
panel: {
"& .MuiAccordionDetails-root": {
padding: 0,
paddingTop: theme.spacing(2),
},
"&.Mui-expanded": {
margin: 0,
minHeight: 0,
},
"&:before": {
display: "none",
},
background: "none",
display: "",
margin: 0,
minHeight: 0,
width: "100%",
},
panelExpander: {
"&.MuiAccordionSummary-root.Mui-expanded": {
minHeight: 0,
},
"&> .MuiAccordionSummary-content": {
margin: 0,
},
"&> .MuiAccordionSummary-expandIcon": {
padding: 0,
position: "absolute",
right: theme.spacing(24),
},
margin: 0,
minHeight: 0,
padding: 0,
},
root: {
"&:last-child:after": {
background: theme.palette.background.default,
content: "''",
height: "calc(50% - 4px)",
left: -26,
position: "absolute",
top: "calc(50% + 4px)",
width: "2px",
},
alignItems: "center",
display: "flex",
marginBottom: theme.spacing(3),
marginTop: 0,
position: "relative",
width: "100%",
},
}),
{ name: "TimelineEvent" },
);
export interface TimelineEventProps { export interface TimelineEventProps {
children?: React.ReactNode; children?: React.ReactNode;
date: string; date: string;
@ -88,24 +16,48 @@ export const TimelineEvent: React.FC<TimelineEventProps> = props => {
const { children, date, secondaryTitle, title, titleElements, hasPlainDate } = const { children, date, secondaryTitle, title, titleElements, hasPlainDate } =
props; props;
const classes = useStyles(props); const hasChildren =
children && React.Children.toArray(children).filter(Boolean).length > 0;
return ( return (
<div className={classes.root}> <Box
<span className={classes.dot} /> display="flex"
{children ? ( alignItems="center"
<Accordion className={classes.panel} elevation={0}> marginBottom={5}
<AccordionSummary className={classes.panelExpander}> position="relative"
<TimelineEventHeader width="100%"
title={title} >
date={date} <Box
titleElements={titleElements} as="span"
hasPlainDate={hasPlainDate} position="absolute"
/> backgroundColor="interactiveNeutralPressing"
</AccordionSummary> borderRadius="100%"
<AccordionDetails> __height="7px"
<Typography>{children}</Typography> __width="7px"
</AccordionDetails> __left="-28px"
__top={hasChildren ? "13px" : "5px"}
/>
{hasChildren ? (
<Accordion
className={sprinkles({
width: "100%",
})}
>
<Accordion.Item value="accordionItemId">
<Accordion.Trigger>
<TimelineEventHeader
title={title}
date={date}
titleElements={titleElements}
hasPlainDate={hasPlainDate}
>
<Accordion.TriggerButton dataTestId="expand-icon" />
</TimelineEventHeader>
</Accordion.Trigger>
<Accordion.Content>
<Text>{children}</Text>
</Accordion.Content>
</Accordion.Item>
</Accordion> </Accordion>
) : ( ) : (
<TimelineEventHeader <TimelineEventHeader
@ -116,7 +68,7 @@ export const TimelineEvent: React.FC<TimelineEventProps> = props => {
hasPlainDate={hasPlainDate} hasPlainDate={hasPlainDate}
/> />
)} )}
</div> </Box>
); );
}; };
TimelineEvent.displayName = "TimelineEvent"; TimelineEvent.displayName = "TimelineEvent";

View file

@ -1,46 +1,10 @@
import useNavigator from "@dashboard/hooks/useNavigator"; import useNavigator from "@dashboard/hooks/useNavigator";
import { Typography } from "@material-ui/core"; import { Box, sprinkles, Text } from "@saleor/macaw-ui/next";
import { makeStyles } from "@saleor/macaw-ui"; import React, { ReactNode } from "react";
import React from "react";
import { DateTime } from "../Date"; import { DateTime } from "../Date";
import Link from "../Link"; import Link from "../Link";
const useStyles = makeStyles(
theme => ({
container: {
alignItems: "start",
display: "flex",
flexDirection: "row",
justifyContent: "space-between",
width: "100%",
},
title: {
wordBreak: "break-all",
},
date: {
color: theme.typography.caption.color,
paddingLeft: 24,
whiteSpace: "nowrap",
},
elementsContainer: {
alignItems: "center",
display: "flex",
flexDirection: "row",
flexWrap: "wrap",
},
secondaryTitle: {
color: "#9e9e9e",
fontSize: 14,
marginTop: theme.spacing(2),
},
titleElement: {
marginRight: theme.spacing(0.5),
},
}),
{ name: "TimelineEventHeader" },
);
export interface TitleElement { export interface TitleElement {
text: string; text: string;
link?: string; link?: string;
@ -52,26 +16,43 @@ export interface TimelineEventHeaderProps {
titleElements?: TitleElement[]; titleElements?: TitleElement[];
secondaryTitle?: string; secondaryTitle?: string;
hasPlainDate?: boolean; hasPlainDate?: boolean;
children?: ReactNode;
} }
export const TimelineEventHeader: React.FC< export const TimelineEventHeader: React.FC<
TimelineEventHeaderProps TimelineEventHeaderProps
> = props => { > = props => {
const { title, date, titleElements, secondaryTitle, hasPlainDate } = props; const { title, date, titleElements, secondaryTitle, hasPlainDate, children } =
props;
const navigate = useNavigator(); const navigate = useNavigator();
const classes = useStyles(props);
return ( return (
<div className={classes.container}> <Box
{title && <Typography className={classes.title}>{title}</Typography>} display="flex"
alignItems="center"
flexDirection="row"
justifyContent="space-between"
width="100%"
>
{title && (
<Text variant="caption" size="large" wordBreak="break-all">
{title}
</Text>
)}
{titleElements && ( {titleElements && (
<div className={classes.elementsContainer}> <Box
display="flex"
alignItems="center"
flexDirection="row"
flexWrap="wrap"
>
{titleElements.filter(Boolean).map(({ text, link }) => { {titleElements.filter(Boolean).map(({ text, link }) => {
if (link) { if (link) {
return ( return (
<Link <Link
className={classes.titleElement} className={sprinkles({
marginRight: 0.5,
})}
onClick={() => navigate(link)} onClick={() => navigate(link)}
> >
{text} {text}
@ -80,20 +61,26 @@ export const TimelineEventHeader: React.FC<
} }
return ( return (
<Typography className={classes.titleElement}>{text}</Typography> <Text variant="caption" size="large" marginRight={0.5}>
{text}
</Text>
); );
})} })}
</div> </Box>
)} )}
<Typography className={classes.date}> <Box display="flex" alignItems="center" gap={5} marginLeft="auto">
<DateTime date={date} plain={hasPlainDate} /> {children}
</Typography> <Text
{secondaryTitle && ( variant="caption"
<Typography className={classes.secondaryTitle}> size="large"
{secondaryTitle} color="textNeutralSubdued"
</Typography> whiteSpace="nowrap"
)} >
</div> <DateTime date={date} plain={hasPlainDate} />
</Text>
</Box>
{secondaryTitle && <Text marginTop={2}>{secondaryTitle}</Text>}
</Box>
); );
}; };

View file

@ -9,7 +9,7 @@ const useStyles = makeStyles(
marginBottom: vars.spacing.px, marginBottom: vars.spacing.px,
}, },
root: { root: {
marginTop: vars.spacing["s1.5"], marginTop: vars.spacing[1.5],
paddingLeft: vars.spacing[6], paddingLeft: vars.spacing[6],
paddingRight: vars.spacing[6], paddingRight: vars.spacing[6],
}, },

View file

@ -1,13 +1,6 @@
import { CardContent, Typography } from "@material-ui/core"; import { Accordion, sprinkles, Text } from "@saleor/macaw-ui/next";
import { Accordion, AccordionSummary } from "@saleor/macaw-ui";
import React from "react"; import React from "react";
import {
useAccordionStyles,
useExpanderStyles,
useSummaryStyles,
} from "./styles";
interface ChannelListProps { interface ChannelListProps {
summary: string; summary: string;
} }
@ -15,22 +8,22 @@ interface ChannelListProps {
export const ChannelsList: React.FC<ChannelListProps> = ({ export const ChannelsList: React.FC<ChannelListProps> = ({
summary, summary,
children, children,
}) => { }) => (
const classes = useAccordionStyles(); <Accordion>
const expanderClasses = useExpanderStyles({}); <Accordion.Item value="channelListItem">
const summaryClasses = useSummaryStyles({}); <Accordion.Trigger
className={sprinkles({
return ( paddingX: 6,
<Accordion classes={expanderClasses}> paddingTop: 0,
<CardContent className={classes.summaryContent}> paddingBottom: 8,
<AccordionSummary })}
className={summaryClasses.root} >
data-test-id="channels-variant-availability-summary" <Text variant="caption" size="large">
> {summary}
<Typography variant="caption">{summary}</Typography> </Text>
</AccordionSummary> <Accordion.TriggerButton dataTestId="expand-icon" />
</CardContent> </Accordion.Trigger>
{children} <Accordion.Content>{children}</Accordion.Content>
</Accordion> </Accordion.Item>
); </Accordion>
}; );

View file

@ -1,56 +0,0 @@
import { makeStyles } from "@saleor/macaw-ui";
export const useAccordionStyles = makeStyles(
() => ({
summaryContent: {
paddingTop: 0,
},
}),
{ name: "VariantDetailsChannelsAvailabilityCard" },
);
export const useExpanderStyles = makeStyles(
() => ({
expanded: {},
root: {
boxShadow: "none",
margin: 0,
padding: 0,
"&:before": {
content: "none",
},
"&$expanded": {
margin: 0,
border: "none",
},
},
}),
{ name: "VariantDetailsChannelsAvailabilityCardExpander" },
);
export const useSummaryStyles = makeStyles(
() => ({
expanded: {},
root: {
width: "100%",
border: "none",
margin: 0,
padding: 0,
minHeight: 0,
"&$expanded": {
minHeight: 0,
},
},
content: {
margin: 0,
"&$expanded": {
margin: 0,
},
},
}),
{ name: "VariantDetailsChannelsAvailabilityCardExpanderSummary" },
);