Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

New Admin UI - Accordion Component #4505

Merged
merged 60 commits into from
Feb 10, 2025
Merged
Show file tree
Hide file tree
Changes from 38 commits
Commits
Show all changes
60 commits
Select commit Hold shift + click to select a range
9ea14e8
wip
adrians5j Jan 22, 2025
ac949f1
wip
adrians5j Jan 22, 2025
2bc9162
Merge remote-tracking branch 'refs/remotes/origin/feat/new-admin-ui' …
adrians5j Jan 24, 2025
11b2e36
wip
adrians5j Jan 24, 2025
83f16ee
wip
adrians5j Jan 26, 2025
0c84634
wip
adrians5j Jan 26, 2025
2e0b203
wip
adrians5j Jan 27, 2025
10b41f8
wip
adrians5j Jan 27, 2025
5cc2a8f
wip
adrians5j Jan 27, 2025
704561c
wip
adrians5j Jan 27, 2025
015611c
wip
adrians5j Feb 3, 2025
3ca70d2
wip
adrians5j Feb 3, 2025
4b5d40b
wip
adrians5j Feb 3, 2025
b5e60de
Merge remote-tracking branch 'refs/remotes/origin/feat/new-admin-ui' …
adrians5j Feb 4, 2025
99f2e65
wip
adrians5j Feb 5, 2025
b3808af
wip
adrians5j Feb 5, 2025
f12ab9a
wip
adrians5j Feb 5, 2025
83a1d09
wip
adrians5j Feb 5, 2025
483d6a1
wip
adrians5j Feb 5, 2025
9f7b6bb
wip
adrians5j Feb 5, 2025
f3de0f5
wip
adrians5j Feb 6, 2025
c6dd8e0
wip
adrians5j Feb 6, 2025
4ece986
wip
adrians5j Feb 6, 2025
2203129
wip
adrians5j Feb 6, 2025
2d65e51
wip
adrians5j Feb 6, 2025
c374ecb
wip
adrians5j Feb 6, 2025
e4971ef
wip
adrians5j Feb 6, 2025
9441782
wip
adrians5j Feb 6, 2025
1f16139
wip
adrians5j Feb 6, 2025
c204093
wip
adrians5j Feb 6, 2025
674f4ec
wip
adrians5j Feb 6, 2025
e66d20d
wip
adrians5j Feb 6, 2025
a9e4de2
wip
adrians5j Feb 6, 2025
74f78f4
wip
adrians5j Feb 6, 2025
d0487a9
wip
adrians5j Feb 6, 2025
e859420
wip
adrians5j Feb 6, 2025
60cda1d
wip
adrians5j Feb 6, 2025
bd4a7d4
wip
adrians5j Feb 6, 2025
c5afa9d
fix: rename skeletonPulse to skeleton-pulse
adrians5j Feb 7, 2025
cf9c947
fix: use TW numbers instead of px
adrians5j Feb 7, 2025
effc6fa
fix: remove redundant displayName setting
adrians5j Feb 7, 2025
844a55e
fix: make Icon label prop required again
adrians5j Feb 7, 2025
5e5cb9c
fix: use `cva`
adrians5j Feb 7, 2025
002ee3e
fix: remove default value
adrians5j Feb 7, 2025
b13d00c
fix: use `cva`
adrians5j Feb 7, 2025
6199d04
fix: use `cva`
adrians5j Feb 7, 2025
09dc98c
fix: remove context
adrians5j Feb 7, 2025
22efe18
fix: remove comment
adrians5j Feb 7, 2025
a7c769c
wip
adrians5j Feb 10, 2025
747fa3f
wip
adrians5j Feb 10, 2025
e408823
wip
adrians5j Feb 10, 2025
d2c65f3
wip
adrians5j Feb 10, 2025
1c05b9b
wip
adrians5j Feb 10, 2025
a6e9e4a
Merge remote-tracking branch 'refs/remotes/origin/feat/new-admin-ui' …
adrians5j Feb 10, 2025
cdb3bcb
wip
adrians5j Feb 10, 2025
64c3a74
wip
adrians5j Feb 10, 2025
42df0a0
wip
adrians5j Feb 10, 2025
ffad86a
wip
adrians5j Feb 10, 2025
c5cd0aa
wip
adrians5j Feb 10, 2025
0198736
wip
adrians5j Feb 10, 2025
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions packages/admin-ui/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@
"dependencies": {
"@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",
skeletonPulse: "skeletonPulse 1400ms ease-in-out infinite"
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Shall we stick to a camelCase or kebab-case naming convention? The kebab-case looks good, but I would change skeletonPulse 🙏

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Done.

},
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" }
},
skeletonPulse: {
"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-[600px] 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-[600px] 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-[600px] 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"} />}
/>
</>
)
}
};
52 changes: 52 additions & 0 deletions packages/admin-ui/src/Accordion/Accordion.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,52 @@
import React, { useMemo } from "react";
import { cn, makeDecoratable, withStaticProps } from "~/utils";
import { AccordionRoot } from "./components/AccordionRoot";
import { AccordionContext, useAccordion } from "./components/AccordionContext";
import { AccordionItem, type AccordionItemProps } from "./components/AccordionItem";

type AccordionProps = React.ComponentPropsWithoutRef<typeof AccordionRoot> & {
children: React.ReactNode;
variant?: "underline" | "container";
background?: "transparent" | "base" | "light";
};

const AccordionBase = ({
children,
variant,
background = "base",
...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={cn(
"w-full",
{ "wby-gap-xs wby-flex wby-flex-col": variant === "container" },
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Why not use the cva utility?

rootProps.className
)}
>
<AccordionContext.Provider value={{ variant, background }}>
{children}
</AccordionContext.Provider>
</AccordionRoot>
);
};

AccordionBase.displayName = "Accordion";
Copy link
Contributor

@leopuleo leopuleo Feb 6, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Do we need this? Maybe not, since we are not using ref


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

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

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