Skip to content

Commit 0126ca3

Browse files
Merge branch 'deriv-com:main' into yaswanth/FEQ-1858_Implement_circular_progressbar
2 parents 3b81738 + 8f71df3 commit 0126ca3

15 files changed

+409
-161
lines changed

package-lock.json

+16
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

package.json

+1
Original file line numberDiff line numberDiff line change
@@ -84,6 +84,7 @@
8484
"storybook": "^8.0.4",
8585
"ts-jest": "^29.1.2",
8686
"typescript": "^5.4.3",
87+
"usehooks-ts": "^3.0.2",
8788
"vite": "^5.2.6",
8889
"vite-plugin-classname": "^0.0.3",
8990
"vite-plugin-dts": "^3.7.3",
+84
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,84 @@
1+
/* Style the accordion section */
2+
$border: 1px solid var(--du-general-active);
3+
$animation-duration: 0.3s;
4+
5+
.deriv-accordion {
6+
position: relative;
7+
display: inline-flex;
8+
flex-direction: column;
9+
min-width: 100%;
10+
background-color: var(--du-general-main-2);
11+
padding: 24px;
12+
13+
&--compact {
14+
padding: 16px;
15+
}
16+
17+
&--underline {
18+
border-bottom: $border;
19+
}
20+
21+
&--shadow,
22+
&--bordered {
23+
border-radius: $border-radius-2;
24+
margin-bottom: 8px;
25+
26+
&:last-child {
27+
margin-bottom: 0;
28+
}
29+
}
30+
31+
&--bordered {
32+
border: $border;
33+
}
34+
35+
&--shadow {
36+
box-shadow: $deriv-box-shadow;
37+
}
38+
39+
&__header {
40+
outline: none;
41+
width: 100%;
42+
display: flex;
43+
align-items: center;
44+
justify-content: space-between;
45+
color: var(--du-text-general);
46+
cursor: pointer;
47+
}
48+
49+
&__icon {
50+
margin-left: auto;
51+
display: inline-block;
52+
transition: all $animation-duration ease-in-out;
53+
54+
&--active {
55+
transform: rotate(-180deg);
56+
}
57+
}
58+
&__content {
59+
width: 100%;
60+
overflow: auto;
61+
opacity: 0;
62+
background-color: white;
63+
transition: all $animation-duration ease;
64+
65+
&--active {
66+
opacity: 1;
67+
}
68+
}
69+
70+
&__text {
71+
margin-top: 24px;
72+
color: var(--du-text-general);
73+
74+
&--compact {
75+
margin-top: 16px;
76+
}
77+
}
78+
}
79+
.accordion__section {
80+
position: relative;
81+
display: inline-flex;
82+
flex-direction: column;
83+
min-width: 100%;
84+
}

src/components/Accordion/Chevron.svg

+3
Loading
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,107 @@
1+
import React from "react";
2+
import { render } from "@testing-library/react";
3+
import { Accordion } from "..";
4+
import userEvent from "@testing-library/user-event";
5+
6+
describe("Accordion", () => {
7+
it("renders correctly", () => {
8+
const { getByText } = render(
9+
<Accordion title="Test Title">Test Content</Accordion>,
10+
);
11+
12+
expect(getByText("Test Title")).toBeInTheDocument();
13+
});
14+
15+
it("opens and closes when the header is clicked", async () => {
16+
const { getByRole } = render(
17+
<Accordion title="Test Title">Test Content</Accordion>,
18+
);
19+
const button = getByRole("button");
20+
21+
expect(button).toHaveAttribute("aria-expanded", "false");
22+
23+
await userEvent.click(button);
24+
expect(button).toHaveAttribute("aria-expanded", "true");
25+
26+
await userEvent.click(button);
27+
expect(button).toHaveAttribute("aria-expanded", "false");
28+
});
29+
30+
it("opens by default when defaultOpen is passed", async () => {
31+
const { getByRole } = render(
32+
<Accordion title="Test Title" defaultOpen>
33+
Test Content
34+
</Accordion>,
35+
);
36+
const button = getByRole("button");
37+
expect(button).toHaveAttribute("aria-expanded", "true");
38+
39+
await userEvent.click(button);
40+
expect(button).toHaveAttribute("aria-expanded", "false");
41+
});
42+
43+
it("opens by default when defaultOpen is passed", async () => {
44+
const { getByRole } = render(
45+
<Accordion title="Test Title" defaultOpen>
46+
Test Content
47+
</Accordion>,
48+
);
49+
const button = getByRole("button");
50+
expect(button).toHaveAttribute("aria-expanded", "true");
51+
52+
await userEvent.click(button);
53+
expect(button).toHaveAttribute("aria-expanded", "false");
54+
});
55+
56+
it("renders correctly with underline variant", () => {
57+
const { container } = render(
58+
<Accordion title="Test Title" variant="underline">
59+
Test Content
60+
</Accordion>,
61+
);
62+
63+
expect(container.firstChild).toHaveClass("deriv-accordion--underline");
64+
});
65+
66+
it("renders correctly with bordered variant", () => {
67+
const { container } = render(
68+
<Accordion title="Test Title" variant="bordered">
69+
Test Content
70+
</Accordion>,
71+
);
72+
73+
expect(container.firstChild).toHaveClass("deriv-accordion--bordered");
74+
});
75+
76+
it("renders correctly with shadow variant", () => {
77+
const { container } = render(
78+
<Accordion title="Test Title" variant="shadow">
79+
Test Content
80+
</Accordion>,
81+
);
82+
83+
expect(container.firstChild).toHaveClass("deriv-accordion--shadow");
84+
});
85+
86+
it("renders correctly when isCompact is true", () => {
87+
const { container } = render(
88+
<Accordion title="Test Title" isCompact={true}>
89+
Test Content
90+
</Accordion>,
91+
);
92+
93+
expect(container.firstChild).toHaveClass("deriv-accordion--compact");
94+
});
95+
96+
it("renders correctly when isCompact is false", () => {
97+
const { container } = render(
98+
<Accordion title="Test Title" isCompact={false}>
99+
Test Content
100+
</Accordion>,
101+
);
102+
103+
expect(container.firstChild).not.toHaveClass(
104+
"deriv-accordion--compact",
105+
);
106+
});
107+
});

src/components/Accordion/index.tsx

+79
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,79 @@
1+
import React, { useState, useRef, ReactNode, useEffect } from "react";
2+
import Chevron from "./Chevron.svg";
3+
import clsx from "clsx";
4+
import "./Accordion.scss";
5+
6+
type AccordionVariants = "underline" | "bordered" | "shadow";
7+
8+
type AccordionProps = {
9+
children: ReactNode;
10+
defaultOpen?: boolean;
11+
isCompact?: boolean;
12+
title: string;
13+
variant?: AccordionVariants;
14+
};
15+
16+
const AccordionVariant = {
17+
bordered: "deriv-accordion--bordered",
18+
shadow: "deriv-accordion--shadow",
19+
underline: "deriv-accordion--underline",
20+
} as const;
21+
22+
export const Accordion = ({
23+
defaultOpen = false,
24+
children,
25+
isCompact = false,
26+
title,
27+
variant = "underline",
28+
}: AccordionProps) => {
29+
const [active, setActive] = useState(defaultOpen);
30+
const [setHeight, setHeightState] = useState(defaultOpen ? "auto" : "0px");
31+
32+
const content = useRef<HTMLDivElement | null>(null);
33+
34+
useEffect(() => {
35+
const scrollHeight = content?.current?.scrollHeight;
36+
setHeightState(active ? `${scrollHeight}px` : "0px");
37+
}, [active]);
38+
39+
const toggleAccordion = () => setActive(!active);
40+
41+
return (
42+
<div
43+
className={clsx("deriv-accordion", AccordionVariant[variant], {
44+
"deriv-accordion--compact": isCompact,
45+
})}
46+
>
47+
<button
48+
className={clsx("deriv-accordion__header", {
49+
"deriv-accordion__header--active": active,
50+
})}
51+
onClick={toggleAccordion}
52+
aria-expanded={active}
53+
>
54+
<p>{title}</p>
55+
<img
56+
src={Chevron}
57+
className={clsx("deriv-accordion__icon", {
58+
"deriv-accordion__icon--active": active,
59+
})}
60+
/>
61+
</button>
62+
<div
63+
ref={content}
64+
style={{ maxHeight: setHeight }}
65+
className={clsx("deriv-accordion__content", {
66+
"deriv-accordion__content--active": active,
67+
})}
68+
>
69+
<div
70+
className={clsx("deriv-accordion__text", {
71+
"deriv-accordion__text--compact": isCompact,
72+
})}
73+
>
74+
{children}
75+
</div>
76+
</div>
77+
</div>
78+
);
79+
};

src/components/Tooltip/index.tsx

+2-2
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
import React, { ReactNode, useRef, useState } from "react";
22
import clsx from "clsx";
3-
import { useOnClickOutside } from "../../hooks/useOnClickOutside";
3+
import { useOnClickOutside } from "usehooks-ts";
44
import "./Tooltip.scss";
55

66
type TooltipPositionType = "top" | "bottom" | "left" | "right";
@@ -45,7 +45,7 @@ export const Tooltip = ({
4545
}
4646
};
4747

48-
const handleClickOutside = (event: globalThis.MouseEvent) => {
48+
const handleClickOutside = (event: MouseEvent | TouchEvent | FocusEvent) => {
4949
if (
5050
triggerAction === "click" &&
5151
active &&

src/hooks/index.ts

-1
Original file line numberDiff line numberDiff line change
@@ -1,2 +1 @@
11
export {useDevice} from './useDevice'
2-
export {useOnClickOutside} from './useOnClickOutside'

src/hooks/useDevice.ts

+1-1
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
import { useMediaQuery } from "./useMediaQuery";
1+
import { useMediaQuery } from "usehooks-ts";
22

33
/** A custom hook to check for the client device and determine the layout to be rendered */
44
export const useDevice = () => {

0 commit comments

Comments
 (0)