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

Aizad/FEQ-1566/Added Accordion Component #106

Merged
merged 23 commits into from
Apr 3, 2024
Merged
Show file tree
Hide file tree
Changes from 13 commits
Commits
Show all changes
23 commits
Select commit Hold shift + click to select a range
89c9050
feat: Accordion component
aizad-deriv Mar 6, 2024
44d4c81
chore: made improvements for Accordion component
aizad-deriv Mar 6, 2024
1c8c5fa
chore: added variants for accordion component
aizad-deriv Mar 6, 2024
f705cf7
chore: added sizing
aizad-deriv Mar 6, 2024
7b7a468
chore: added new props
aizad-deriv Mar 6, 2024
02e516c
chore: added storybook for Accordion component
aizad-deriv Mar 7, 2024
13461ab
chore: change props inside of Accordion title and content
aizad-deriv Mar 7, 2024
1688a12
Merge branch 'main' of github.com:deriv-com/ui into aizad/FEQ-1566/ad…
aizad-deriv Mar 7, 2024
aea0f58
chore: remove redundant styles
aizad-deriv Mar 7, 2024
dfe2e2a
Merge branch 'main' of github.com:deriv-com/ui into aizad/FEQ-1566/ad…
aizad-deriv Mar 7, 2024
0627157
chore: update css values based on latest changes
aizad-deriv Mar 7, 2024
b1cc35a
Merge branch 'main' of github.com:deriv-com/ui into aizad/FEQ-1566/ad…
aizad-deriv Mar 8, 2024
9061554
chore: refactor styles and add changes to storybook
aizad-deriv Mar 8, 2024
f74d5af
chore: update component
aizad-deriv Apr 2, 2024
2c7bfbb
chore: replace existing accordion with new one
aizad-deriv Apr 2, 2024
fea6511
chore: updated storybook
aizad-deriv Apr 2, 2024
3f61c06
chore: fix types in storybook
aizad-deriv Apr 2, 2024
200cd40
Merge branch 'main' of github.com:deriv-com/ui into aizad/FEQ-1566/ad…
aizad-deriv Apr 2, 2024
ffc37b6
chore: updated structure based on latest changes
aizad-deriv Apr 2, 2024
8e0b092
chore: update import statement in storybook
aizad-deriv Apr 2, 2024
8067980
chores: added test cases
aizad-deriv Apr 2, 2024
fd967f9
chore: resolve comments
aizad-deriv Apr 2, 2024
80f8010
Merge branch 'main' into aizad/FEQ-1566/add-accordion
shayan-deriv Apr 3, 2024
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
87 changes: 87 additions & 0 deletions lib/components/Accordion/Accordion.scss
Original file line number Diff line number Diff line change
@@ -0,0 +1,87 @@
$border: 1px solid var(--du-general-active);
$animation-duration: 0.3s;

.deriv-accordion {
position: relative;
display: inline-flex;
flex-direction: column;
min-width: 100%;

&__wrapper {
width: 100%;
background-color: var(--du-general-main-2);
padding: 24px;

&--compact {
padding: 16px;
}

&--underline {
border-bottom: $border;
}

&--shadow,
&--bordered {
border-radius: $border-radius-2;
margin-bottom: 8px;

&:last-child {
margin-bottom: 0;
}
}

&--bordered {
border: $border;
}

&--shadow {
box-shadow: $deriv-box-shadow;
}
}

&__header {
outline: none;
width: 100%;
display: flex;
align-items: center;
justify-content: space-between;

&--border-bottom {
border-bottom: 1px solid var(--du-border-normal-1);
}
}

&__icon {
margin-left: auto;
display: inline-block;
transition: all $animation-duration ease-in-out;
&:hover {
cursor: pointer;
}

&--active {
transform: rotate(-180deg);
}
}

&__content {
width: 100%;
overflow: hidden;
max-height: 0px;
opacity: 0;
transition: all $animation-duration ease-in-out;

&--no-animations {
transition: none;
}

&--active {
opacity: 1;
padding-top: 24px;

.deriv-accordion__content--compact {
padding-top: 16px;
}
}
}
}
3 changes: 3 additions & 0 deletions lib/components/Accordion/Chevron.svg
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
155 changes: 155 additions & 0 deletions lib/components/Accordion/index.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,155 @@
import {
ReactNode,
memo,
useCallback,
useEffect,
useRef,
useState,
} from "react";
import clsx from "clsx";
import Chevron from "./Chevron.svg";
import "./Accordion.scss";

type AccordionVariants = "underline" | "bordered" | "shadow";

type AccordionSection = {
title: ReactNode;
content: ReactNode;
};

type AccordionSectionProps = {
disableAnimation?: boolean;
isActiveSection: boolean;
isCompact?: boolean;
section: AccordionSection;
sectionIndex: number;
setActiveIndex: (index: number | null) => void;
variant?: AccordionVariants;
};

type AccordionProps = {
allowMultiple?: boolean;
disableAnimation?: boolean;
isCompact?: AccordionSectionProps["isCompact"];
sections: AccordionSection[];
variant?: AccordionSectionProps["variant"];
};

const AccordionVariant = {
bordered: "deriv-accordion__wrapper--bordered",
shadow: "deriv-accordion__wrapper--shadow",
underline: "deriv-accordion__wrapper--underline",
} as const;

const AccordionSection = memo(
({
disableAnimation = false,
isActiveSection,
isCompact = false,
section,
sectionIndex,
setActiveIndex,
variant = "underline",
}: AccordionSectionProps) => {
const toggleSection = () => {
const nextIndex = isActiveSection ? null : sectionIndex;
setActiveIndex(nextIndex);
};

const [height, setHeight] = useState("");
const heightRef = useRef<HTMLDivElement>(null);

useEffect(() => {
const scrollHeight = heightRef.current?.scrollHeight;
setHeight(`${scrollHeight}px`);
}, [isActiveSection]);

return (
<div
className={clsx(
"deriv-accordion__wrapper",
AccordionVariant[variant],
{
"deriv-accordion__wrapper--compact": isCompact,
},
)}
>
<div
className={clsx("deriv-accordion__header", {
"deriv-accordion__header--active": isActiveSection,
})}
>
<div>{section.title}</div>
<div
className="deriv-accordion__icon"
onClick={toggleSection}
>
<img
src={Chevron}
className={clsx("deriv-accordion__icon", {
"deriv-accordion__icon--active":
isActiveSection,
})}
/>
</div>
</div>
{isActiveSection && (
<div
className={clsx("deriv-accordion__content", {
"deriv-accordion__content--active": isActiveSection,
"deriv-accordion__content--compact": !isCompact,
"deriv-accordion__content--no-animations":
disableAnimation,
})}
ref={heightRef}
style={{ maxHeight: isActiveSection ? height : "0px" }}
>
{section.content}
</div>
)}
</div>
);
},
);

export const Accordion = ({
allowMultiple = false,
disableAnimation = false,
isCompact = false,
sections,
variant,
}: AccordionProps) => {
const [activeIndexes, setActiveIndexes] = useState<number[]>([]);

const setActiveIndex = useCallback(
(index: number) => {
setActiveIndexes((prevIndexes) => {
if (prevIndexes.includes(index)) {
return prevIndexes.filter((i) => i !== index);
} else if (allowMultiple) {
return [...prevIndexes, index];
} else {
return [index];
}
});
},
[allowMultiple],
);

return (
<div className="deriv-accordion">
{sections.map((section, sectionIndex) => (
<AccordionSection
disableAnimation={disableAnimation}
isActiveSection={activeIndexes.includes(sectionIndex)}
isCompact={isCompact}
key={sectionIndex}
section={section}
sectionIndex={sectionIndex}
setActiveIndex={() => setActiveIndex(sectionIndex)}
variant={variant}
/>
))}
</div>
);
};
5 changes: 3 additions & 2 deletions lib/main.ts
Original file line number Diff line number Diff line change
@@ -1,18 +1,19 @@
export { Accordion } from "./components/Accordion";
export { ActionScreen } from "./components/ActionScreen";
export { Button } from "./components/Button";
export { Checkbox } from "./components/Checkbox";
export { Divider } from "./components/Divider";
export { Dropdown } from "./components/Dropdown";
export { InlineMessage } from "./components/InlineMessage";
export { Input } from "./components/Input";
export { LinearProgressBar } from './components/LinearProgressBar'
export { LinearProgressBar } from "./components/LinearProgressBar";
export { Loader } from "./components/Loader";
export { Modal } from "./components/Modal";
export { PageLayout } from "./components/PageLayout";
export { PasswordInput } from "./components/PasswordInput";
export { SideNote } from "./components/SideNote";
export { Table } from "./components/Table";
export { Tab, Tabs } from "./components/Tabs";
export { Table } from "./components/Table";
export { Text } from "./components/Text";
export { TextArea } from "./components/TextArea";
export { ToggleSwitch } from "./components/ToggleSwitch";
Expand Down
129 changes: 129 additions & 0 deletions src/stories/Accordion.stories.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,129 @@
import type { Meta, StoryObj } from "@storybook/react";
import { Accordion } from "../../lib/main";

const meta = {
title: "Components/Accordion",
component: Accordion,
parameters: {
layout: "centered",
},
tags: ["autodocs"],
args: {
disableAnimation: false,
isCompact: false,
allowMultiple: false,
variant: "underline",
sections: [
{
title: "Section 1",
content: `
Lorem ipsum dolor sit amet, consectetur adipiscing elit. Quisque id erat sit amet arcu luctus pharetra. Cras scelerisque dolor non justo dignissim efficitur. Nunc non molestie nunc, venenatis maximus ante. Vestibulum mattis faucibus scelerisque. Nulla tempus semper congue. Phasellus maximus iaculis eleifend. Praesent elementum risus augue, eu viverra eros pulvinar sagittis. Aliquam ullamcorper vel metus at ultricies. Phasellus sollicitudin nibh lacus, nec malesuada felis dapibus eu. Suspendisse ut condimentum felis, vitae accumsan nunc. Nunc non bibendum diam, eget molestie metus. Nam orci sem, porttitor sed libero ut, rutrum tristique nulla. Praesent et odio posuere diam ultricies luctus nec eu nibh. Cras sit amet sagittis erat. Nulla viverra rhoncus magna, vitae aliquet ex consectetur at.
`,
},
{
title: "Section 2",
content: `
Aliquam vel libero et tortor sagittis condimentum. Nam id varius turpis, id pharetra eros. Mauris purus tortor, mattis quis eros in, molestie pharetra lorem. Morbi viverra urna purus, nec ornare purus aliquet et. Curabitur tempus nulla id leo eleifend, sit amet lobortis libero interdum. Proin nulla neque, imperdiet nec metus in, volutpat accumsan sem. Curabitur imperdiet et turpis at condimentum. Nunc nec quam fringilla, porta elit nec, pellentesque ligula.

`,
},
{
title: "Section 3",
content: `
Sed lobortis rutrum dui ut consequat. Vestibulum sollicitudin orci eget risus volutpat, quis congue neque pharetra. Nulla sed justo commodo tellus tincidunt lobortis. Nunc tortor augue, consequat eu odio nec, accumsan accumsan lorem. Suspendisse maximus ultricies turpis, consequat mollis diam tempor ut. Fusce a elementum est. Nulla odio elit, malesuada eu leo sit amet, malesuada accumsan magna. Maecenas molestie bibendum lorem, et ullamcorper dui euismod eget. Proin eget dui dapibus lacus fermentum mattis vitae iaculis lectus. Pellentesque in risus in nibh commodo imperdiet. Morbi at tempus dui. Mauris pellentesque mauris id erat blandit, ac ultrices metus efficitur. Donec porttitor neque eget elit volutpat gravida.

`,
},
],
},
argTypes: {
variant: {
options: ["underline", "bordered", "shadow"],
control: { type: "select" },
},
sections: {
control: {
disable: true,
},
},
},
} satisfies Meta<typeof Accordion>;

const styles = {
width: "500px",
color: "var(--du-text-general)",
};

const props = {
className: "theme--light",
style: styles,
};

export const AllowMultiple: Story = {
args: {
allowMultiple: true,
},
render: (args) => (
<div {...props}>
<Accordion {...args} />
</div>
),
};

export default meta;
type Story = StoryObj<typeof meta>;

export const Underline: Story = {
args: {
variant: "underline",
},
render: (args) => (
<div {...props}>
<Accordion {...args} />
</div>
),
};

export const Bordered: Story = {
args: {
variant: "bordered",
},
render: (args) => (
<div {...props}>
<Accordion {...args} />
</div>
),
};

export const Shadow: Story = {
args: {
variant: "shadow",
},
render: (args) => (
<div {...props}>
<Accordion {...args} />
</div>
),
};

export const DisabledAnimation: Story = {
args: {
disableAnimation: true,
},
render: (args) => (
<div {...props}>
<Accordion {...args} />
</div>
),
};

export const Compact: Story = {
args: {
isCompact: true,
},
render: (args) => (
<div {...props}>
<Accordion {...args} />
</div>
),
};