Skip to content

Commit

Permalink
Merge pull request #106 from deriv-com/aizad/FEQ-1566/add-accordion
Browse files Browse the repository at this point in the history
Aizad/FEQ-1566/Added Accordion Component
  • Loading branch information
shayan-deriv authored Apr 3, 2024
2 parents e46c69b + 80f8010 commit 8f71df3
Show file tree
Hide file tree
Showing 6 changed files with 388 additions and 4 deletions.
84 changes: 84 additions & 0 deletions src/components/Accordion/Accordion.scss
Original file line number Diff line number Diff line change
@@ -0,0 +1,84 @@
/* Style the accordion section */
$border: 1px solid var(--du-general-active);
$animation-duration: 0.3s;

.deriv-accordion {
position: relative;
display: inline-flex;
flex-direction: column;
min-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;
color: var(--du-text-general);
cursor: pointer;
}

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

&--active {
transform: rotate(-180deg);
}
}
&__content {
width: 100%;
overflow: auto;
opacity: 0;
background-color: white;
transition: all $animation-duration ease;

&--active {
opacity: 1;
}
}

&__text {
margin-top: 24px;
color: var(--du-text-general);

&--compact {
margin-top: 16px;
}
}
}
.accordion__section {
position: relative;
display: inline-flex;
flex-direction: column;
min-width: 100%;
}
3 changes: 3 additions & 0 deletions src/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.
107 changes: 107 additions & 0 deletions src/components/Accordion/__test__/Accordion.test.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,107 @@
import React from "react";
import { render } from "@testing-library/react";
import { Accordion } from "..";
import userEvent from "@testing-library/user-event";

describe("Accordion", () => {
it("renders correctly", () => {
const { getByText } = render(
<Accordion title="Test Title">Test Content</Accordion>,
);

expect(getByText("Test Title")).toBeInTheDocument();
});

it("opens and closes when the header is clicked", async () => {
const { getByRole } = render(
<Accordion title="Test Title">Test Content</Accordion>,
);
const button = getByRole("button");

expect(button).toHaveAttribute("aria-expanded", "false");

await userEvent.click(button);
expect(button).toHaveAttribute("aria-expanded", "true");

await userEvent.click(button);
expect(button).toHaveAttribute("aria-expanded", "false");
});

it("opens by default when defaultOpen is passed", async () => {
const { getByRole } = render(
<Accordion title="Test Title" defaultOpen>
Test Content
</Accordion>,
);
const button = getByRole("button");
expect(button).toHaveAttribute("aria-expanded", "true");

await userEvent.click(button);
expect(button).toHaveAttribute("aria-expanded", "false");
});

it("opens by default when defaultOpen is passed", async () => {
const { getByRole } = render(
<Accordion title="Test Title" defaultOpen>
Test Content
</Accordion>,
);
const button = getByRole("button");
expect(button).toHaveAttribute("aria-expanded", "true");

await userEvent.click(button);
expect(button).toHaveAttribute("aria-expanded", "false");
});

it("renders correctly with underline variant", () => {
const { container } = render(
<Accordion title="Test Title" variant="underline">
Test Content
</Accordion>,
);

expect(container.firstChild).toHaveClass("deriv-accordion--underline");
});

it("renders correctly with bordered variant", () => {
const { container } = render(
<Accordion title="Test Title" variant="bordered">
Test Content
</Accordion>,
);

expect(container.firstChild).toHaveClass("deriv-accordion--bordered");
});

it("renders correctly with shadow variant", () => {
const { container } = render(
<Accordion title="Test Title" variant="shadow">
Test Content
</Accordion>,
);

expect(container.firstChild).toHaveClass("deriv-accordion--shadow");
});

it("renders correctly when isCompact is true", () => {
const { container } = render(
<Accordion title="Test Title" isCompact={true}>
Test Content
</Accordion>,
);

expect(container.firstChild).toHaveClass("deriv-accordion--compact");
});

it("renders correctly when isCompact is false", () => {
const { container } = render(
<Accordion title="Test Title" isCompact={false}>
Test Content
</Accordion>,
);

expect(container.firstChild).not.toHaveClass(
"deriv-accordion--compact",
);
});
});
79 changes: 79 additions & 0 deletions src/components/Accordion/index.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,79 @@
import React, { useState, useRef, ReactNode, useEffect } from "react";
import Chevron from "./Chevron.svg";
import clsx from "clsx";
import "./Accordion.scss";

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

type AccordionProps = {
children: ReactNode;
defaultOpen?: boolean;
isCompact?: boolean;
title: string;
variant?: AccordionVariants;
};

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

export const Accordion = ({
defaultOpen = false,
children,
isCompact = false,
title,
variant = "underline",
}: AccordionProps) => {
const [active, setActive] = useState(defaultOpen);
const [setHeight, setHeightState] = useState(defaultOpen ? "auto" : "0px");

const content = useRef<HTMLDivElement | null>(null);

useEffect(() => {
const scrollHeight = content?.current?.scrollHeight;
setHeightState(active ? `${scrollHeight}px` : "0px");
}, [active]);

const toggleAccordion = () => setActive(!active);

return (
<div
className={clsx("deriv-accordion", AccordionVariant[variant], {
"deriv-accordion--compact": isCompact,
})}
>
<button
className={clsx("deriv-accordion__header", {
"deriv-accordion__header--active": active,
})}
onClick={toggleAccordion}
aria-expanded={active}
>
<p>{title}</p>
<img
src={Chevron}
className={clsx("deriv-accordion__icon", {
"deriv-accordion__icon--active": active,
})}
/>
</button>
<div
ref={content}
style={{ maxHeight: setHeight }}
className={clsx("deriv-accordion__content", {
"deriv-accordion__content--active": active,
})}
>
<div
className={clsx("deriv-accordion__text", {
"deriv-accordion__text--compact": isCompact,
})}
>
{children}
</div>
</div>
</div>
);
};
9 changes: 5 additions & 4 deletions src/main.ts
Original file line number Diff line number Diff line change
@@ -1,20 +1,21 @@
export { Accordion } from "./components/Accordion";
export { ActionScreen } from "./components/ActionScreen";
export { Button } from "./components/Button";
export {Badge} from "./components/Badge";
export { Badge } from "./components/Badge";
export { Checkbox } from "./components/Checkbox";
export { Divider } from "./components/Divider";
export { Dialog } from "./components/Dialog"
export { Dialog } from "./components/Dialog";
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
Loading

0 comments on commit 8f71df3

Please sign in to comment.