Skip to content

Commit

Permalink
feat: create Accordion component (#4505)
Browse files Browse the repository at this point in the history
  • Loading branch information
adrians5j authored Feb 10, 2025
2 parents 08c3431 + 0198736 commit 0093c96
Show file tree
Hide file tree
Showing 34 changed files with 845 additions and 327 deletions.
1 change: 1 addition & 0 deletions packages/admin-ui/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@
"@fortawesome/react-fontawesome": "^0.1.17",
"@material-design-icons/svg": "^0.14.13",
"@radix-ui/react-accessible-icon": "^1.1.0",
"@radix-ui/react-accordion": "^1.2.2",
"@radix-ui/react-avatar": "^1.1.0",
"@radix-ui/react-checkbox": "^1.1.2",
"@radix-ui/react-dialog": "^1.1.4",
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,11 @@ const isColorWithAlpha = variantName => {

const createTailwindConfigTheme = normalizedFigmaExport => {
return {
animation: {
"accordion-down": "accordion-down 0.2s ease-out",
"accordion-up": "accordion-up 0.2s ease-out",
"skeleton-pulse": "skeleton-pulse 1400ms ease-in-out infinite"
},
backgroundColor: normalizedFigmaExport.reduce(
(acc, { type, variantName }) => {
if (type === "backgroundColor") {
Expand Down Expand Up @@ -126,6 +131,20 @@ const createTailwindConfigTheme = normalizedFigmaExport => {

return acc;
}, {}),
keyframes: {
"accordion-down": {
from: { height: "0" },
to: { height: "var(--radix-accordion-content-height)" }
},
"accordion-up": {
from: { height: "var(--radix-accordion-content-height)" },
to: { height: "0" }
},
"skeleton-pulse": {
"0%, 100%": { backgroundColor: "hsl(var(--bg-neutral-dimmed))" },
"50%": { backgroundColor: "hsl(var(--bg-neutral-muted))" }
}
},
margin: normalizedFigmaExport.reduce((acc, { type, variantName }) => {
if (type === "margin") {
acc[variantName] = `var(--margin-${variantName})`;
Expand Down
257 changes: 257 additions & 0 deletions packages/admin-ui/src/Accordion/Accordion.stories.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,257 @@
import React from "react";
import type { Meta, StoryObj } from "@storybook/react";
import { Accordion, type AccordionItemProps as BaseAccordionItemProps } from "./Accordion";

import { ReactComponent as WarningIcon } from "@material-design-icons/svg/outlined/insert_chart.svg";
import { ReactComponent as ArrowUp } from "@material-design-icons/svg/outlined/arrow_upward.svg";
import { ReactComponent as ArrowDown } from "@material-design-icons/svg/outlined/arrow_downward.svg";
import { ReactComponent as EditIcon } from "@material-design-icons/svg/outlined/edit.svg";
import { ReactComponent as TrashIcon } from "@material-design-icons/svg/outlined/delete.svg";

const meta: Meta<typeof Accordion> = {
title: "Components/Accordion",
component: Accordion,
tags: ["autodocs"],
argTypes: {}
};

export default meta;

type Story = StoryObj<typeof Accordion>;

interface AccordionItemProps extends Omit<BaseAccordionItemProps, "value" | "title" | "children"> {
index: number;
}

const AccordionItem = (props: AccordionItemProps) => {
return (
<Accordion.Item
value={`Accordion item ${props.index}`}
title={`Accordion item ${props.index}`}
{...props}
>
Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed elit sem, pretium sit amet
convallis nec, consequat non nulla. Nunc sit amet sagittis tellus. Etiam venenatis, odio
sed consectetur consectetur, quam quam blandit ante, semper maximus lorem est vel dolor.
Praesent ac neque rutrum, elementum turpis et, vulputate enim. In ex lorem,
</Accordion.Item>
);
};

export const Default: Story = {
decorators: [
Story => (
<div className="wby-w-[750px] wby-p-[50px] wby-min-h-[500px] wby-bg-[#f6f7f8]">
<Story />
</div>
)
],
args: {
children: (
<>
<AccordionItem index={1} />
<AccordionItem index={2} />
<AccordionItem index={3} />
</>
)
}
};

export const WithDescriptions: Story = {
...Default,
args: {
children: (
<>
<AccordionItem
index={1}
description="Lorem ipsum dolor sit amet, consectetur adipiscing elit."
/>
<AccordionItem
index={2}
description="Lorem ipsum dolor sit amet, consectetur adipiscing elit."
/>
<AccordionItem
index={3}
description="Lorem ipsum dolor sit amet, consectetur adipiscing elit."
/>
</>
)
},
argTypes: {}
};

export const WithIcon: Story = {
...Default,
args: {
children: (
<>
<AccordionItem
index={1}
description="Lorem ipsum dolor sit amet, consectetur adipiscing elit."
icon={<Accordion.Item.Icon icon={<WarningIcon />} label={"Warning icon"} />}
/>
<AccordionItem
index={2}
description="Lorem ipsum dolor sit amet, consectetur adipiscing elit."
icon={<Accordion.Item.Icon icon={<WarningIcon />} label={"Warning icon"} />}
/>
</>
)
}
};

export const WithActionsIcon: Story = {
...Default,
args: {
children: (
<>
<AccordionItem
index={1}
description="Lorem ipsum dolor sit amet, consectetur adipiscing elit."
icon={<Accordion.Item.Icon icon={<WarningIcon />} label={"Warning icon"} />}
actions={
<>
<Accordion.Item.Action icon={<ArrowUp />} />
<Accordion.Item.Action icon={<ArrowDown />} />
<Accordion.Item.Action.Separator />
<Accordion.Item.Action icon={<EditIcon />} />
<Accordion.Item.Action icon={<TrashIcon />} />
</>
}
/>
<AccordionItem
index={2}
description="Lorem ipsum dolor sit amet, consectetur adipiscing elit."
icon={<Accordion.Item.Icon icon={<WarningIcon />} label={"Warning icon"} />}
actions={
<>
<Accordion.Item.Action icon={<ArrowUp />} />
<Accordion.Item.Action icon={<ArrowDown />} />
<Accordion.Item.Action.Separator />
<Accordion.Item.Action icon={<EditIcon />} />
<Accordion.Item.Action icon={<TrashIcon />} />
</>
}
/>
</>
)
}
};

export const WithHandleIcon: Story = {
...Default,
args: {
children: (
<>
<AccordionItem
index={1}
description="Lorem ipsum dolor sit amet, consectetur adipiscing elit."
icon={<Accordion.Item.Icon icon={<WarningIcon />} label={"Warning icon"} />}
handle={<Accordion.Item.Handle />}
/>
<AccordionItem
index={2}
description="Lorem ipsum dolor sit amet, consectetur adipiscing elit."
handle={<Accordion.Item.Handle />}
/>
</>
)
}
};

export const WithMultipleOpenedItems: Story = {
...Default,
args: {
type: "multiple",
children: (
<>
<AccordionItem
index={1}
description="Lorem ipsum dolor sit amet, consectetur adipiscing elit."
/>
<AccordionItem
index={2}
description="Lorem ipsum dolor sit amet, consectetur adipiscing elit."
/>
</>
)
}
};

export const ContainerVariant: Story = {
...Default,
args: {
variant: "container",
children: (
<>
<AccordionItem
index={1}
description="Lorem ipsum dolor sit amet, consectetur adipiscing elit."
icon={<Accordion.Item.Icon icon={<WarningIcon />} label={"Warning icon"} />}
/>
<AccordionItem
index={2}
description="Lorem ipsum dolor sit amet, consectetur adipiscing elit."
icon={<Accordion.Item.Icon icon={<WarningIcon />} label={"Warning icon"} />}
/>
</>
)
}
};

export const LightBackground: Story = {
...Default,
decorators: [
Story => (
<div className="wby-w-[750px] wby-p-[50px] wby-min-h-[500px]">
<Story />
</div>
)
],
args: {
background: "light",
children: (
<>
<AccordionItem
index={1}
description="Lorem ipsum dolor sit amet, consectetur adipiscing elit."
icon={<Accordion.Item.Icon icon={<WarningIcon />} label={"Warning icon"} />}
/>
<AccordionItem
index={2}
description="Lorem ipsum dolor sit amet, consectetur adipiscing elit."
icon={<Accordion.Item.Icon icon={<WarningIcon />} label={"Warning icon"} />}
/>
</>
)
}
};

export const ContainerVariantWithLightBackground: Story = {
...Default,
decorators: [
Story => (
<div className="wby-w-[750px] wby-p-[50px] wby-min-h-[500px]">
<Story />
</div>
)
],
args: {
variant: "container",
background: "light",
children: (
<>
<AccordionItem
index={1}
description="Lorem ipsum dolor sit amet, consectetur adipiscing elit."
icon={<Accordion.Item.Icon icon={<WarningIcon />} label={"Warning icon"} />}
/>
<AccordionItem
index={2}
description="Lorem ipsum dolor sit amet, consectetur adipiscing elit."
icon={<Accordion.Item.Icon icon={<WarningIcon />} label={"Warning icon"} />}
/>
</>
)
}
};
62 changes: 62 additions & 0 deletions packages/admin-ui/src/Accordion/Accordion.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,62 @@
import React, { useMemo } from "react";
import { makeDecoratable, withStaticProps } from "~/utils";
import { AccordionRoot } from "./components/AccordionRoot";
import { AccordionItem, type AccordionItemProps } from "./components/AccordionItem";
import { cva, type VariantProps } from "class-variance-authority";

const accordionVariants = cva("wby-group w-full", {
variants: {
variant: {
container: "wby-accordion-variant-container wby-gap-xs wby-flex wby-flex-col",
underline: "wby-accordion-variant-underline "
},
background: {
base: "wby-accordion-background-base",
light: "wby-accordion-background-light",
transparent: "wby-accordion-background-transparent"
}
},
defaultVariants: {
variant: "underline",
background: "base"
}
});

type AccordionProps = React.ComponentPropsWithoutRef<typeof AccordionRoot> &
VariantProps<typeof accordionVariants> & {
children: React.ReactNode;
};

const AccordionBase = ({
children,
variant,
background,
className,
...baseRootProps
}: AccordionProps) => {
const rootProps = useMemo(() => {
const rootProps = { ...baseRootProps };
if (rootProps.type !== "multiple") {
// For single accordion, make it collapsible by default.
rootProps.collapsible = rootProps.collapsible !== false;
}
return rootProps;
}, [baseRootProps]);

return (
<AccordionRoot
{...rootProps}
className={accordionVariants({ variant, background, className })}
>
{children}
</AccordionRoot>
);
};

const DecoratableAccordion = makeDecoratable("Accordion", AccordionBase);

const Accordion = withStaticProps(DecoratableAccordion, {
Item: AccordionItem
});

export { Accordion, type AccordionProps, type AccordionItemProps };
Loading

0 comments on commit 0093c96

Please sign in to comment.