-
Notifications
You must be signed in to change notification settings - Fork 8
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Merge branch 'feature/accordion-component' into q/1.0
- Loading branch information
Showing
5 changed files
with
237 additions
and
0 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,93 @@ | ||
import React, { useState } from 'react'; | ||
|
||
import { spacing, Stack } from '../../spacing'; | ||
import { Box } from '../box/Box'; | ||
import { Icon } from '../icon/Icon.component'; | ||
|
||
import styled from 'styled-components'; | ||
|
||
import { Text } from '../text/Text.component'; | ||
|
||
export type AccordionProps = { | ||
title: string; | ||
id: string; | ||
children: React.ReactNode; | ||
style?: React.CSSProperties; | ||
}; | ||
|
||
const AccordionHeader = styled.button` | ||
-webkit-appearance: none; | ||
-moz-appearance: none; | ||
appearance: none; | ||
border: none; | ||
gap: ${spacing.r8}; | ||
width: 100%; | ||
cursor: pointer; | ||
background-color: transparent; | ||
color: ${(props) => props.theme.textPrimary}; | ||
padding: ${spacing.r4}; | ||
width: 100%; | ||
`; | ||
const AccordionContainer = styled.div<{ | ||
isOpen: boolean; | ||
}>` | ||
overflow: hidden; | ||
opacity: ${(props) => (props.isOpen ? 1 : 0)}; | ||
transition: height 0.3s ease-in, opacity 0.3s ease-in, visibility 0.3s; | ||
visibility: ${(props) => (props.isOpen ? 'visible' : 'hidden')}; | ||
`; | ||
const Wrapper = styled.div` | ||
padding-block: ${spacing.r8}; | ||
`; | ||
|
||
export const Accordion = ({ title, id, style, children }: AccordionProps) => { | ||
const [isOpen, setIsOpen] = useState(false); | ||
|
||
const handleToggleContent = () => { | ||
setIsOpen((prev) => !prev); | ||
}; | ||
|
||
return ( | ||
<Box style={{ width: '100%', height: 'auto' }}> | ||
<h3 style={{ margin: 0 }}> | ||
<AccordionHeader | ||
id={`Accordion-header-${id}`} | ||
onClick={handleToggleContent} | ||
aria-controls={id} | ||
aria-expanded={isOpen} | ||
onKeyDown={(e) => | ||
(e.key === 'Enter' || e.key === ' ') && handleToggleContent | ||
} | ||
> | ||
<Stack direction="horizontal" gap="r8"> | ||
<Icon | ||
name="Chevron-up" | ||
size="lg" | ||
style={{ | ||
transform: isOpen ? 'rotate(0deg)' : 'rotate(180deg)', | ||
transition: 'transform 0.3s ease-in', | ||
}} | ||
/> | ||
<Text isEmphazed>{title}</Text> | ||
</Stack> | ||
</AccordionHeader> | ||
</h3> | ||
|
||
<AccordionContainer | ||
ref={(element) => { | ||
if (isOpen) { | ||
element?.style.setProperty('height', element.scrollHeight + 'px'); | ||
} else { | ||
element?.style.setProperty('height', '0px'); | ||
} | ||
}} | ||
isOpen={isOpen} | ||
id={id} | ||
aria-labelledby={`Accordion-header-${id}`} | ||
role="region" | ||
> | ||
<Wrapper style={style}>{children}</Wrapper> | ||
</AccordionContainer> | ||
</Box> | ||
); | ||
}; |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,52 @@ | ||
import { render, screen } from '@testing-library/react'; | ||
import React from 'react'; | ||
import { Accordion } from './Accordion.component'; | ||
import userEvent from '@testing-library/user-event'; | ||
import { QueryClient, QueryClientProvider } from 'react-query'; | ||
|
||
describe('Accordion', () => { | ||
const selectors = { | ||
accordionToggle: () => screen.getByRole('button'), | ||
accordionContainer: () => screen.getByRole('region'), | ||
accordionContent: () => screen.queryByText(/Test content/i), | ||
}; | ||
const renderAccordion = () => { | ||
const queryClient = new QueryClient(); | ||
render( | ||
<QueryClientProvider client={queryClient}> | ||
<Accordion title="Advanced Testings" id="test-accordion"> | ||
<div>Test content</div> | ||
</Accordion> | ||
</QueryClientProvider>, | ||
); | ||
}; | ||
it('should render the Accordion component with title and content', () => { | ||
renderAccordion(); | ||
|
||
const accordionToggle = selectors.accordionToggle(); | ||
expect(accordionToggle).toBeInTheDocument(); | ||
const accordionContent = selectors.accordionContent(); | ||
expect(accordionContent).toBeInTheDocument(); | ||
}); | ||
|
||
it('should toggle the content when clicking on the accordion header', () => { | ||
renderAccordion(); | ||
const accordionToggle = selectors.accordionToggle(); | ||
const accordionContent = selectors.accordionContent(); | ||
expect(accordionContent).not.toBeVisible(); | ||
userEvent.click(accordionToggle); | ||
expect(accordionContent).toBeVisible(); | ||
}); | ||
|
||
it('should toggle the content when pressing the enter key or space key on the accordion header', () => { | ||
renderAccordion(); | ||
const accordionToggle = selectors.accordionToggle(); | ||
const accordionContent = selectors.accordionContent(); | ||
expect(accordionContent).not.toBeVisible(); | ||
accordionToggle.focus(); | ||
userEvent.keyboard('{enter}'); | ||
expect(accordionContent).toBeVisible(); | ||
userEvent.keyboard('{space}'); | ||
expect(accordionContent).not.toBeVisible(); | ||
}); | ||
}); |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,26 @@ | ||
import { | ||
Meta, | ||
Story, | ||
Canvas, | ||
Primary, | ||
Controls, | ||
Unstyled, | ||
Source, | ||
Title, | ||
} from '@storybook/blocks'; | ||
import { Accordion } from '../../src/lib/components/accordion/Accordion.component'; | ||
|
||
import * as Stories from './accordion.stories'; | ||
|
||
<Meta of={Stories} name="Guideline" /> | ||
|
||
# Accordion | ||
|
||
Accordions are used to toggle the visibility of content. | ||
It is used to hide non essential information or to reduce the amount of information displayed on the screen. | ||
|
||
## Playground | ||
|
||
<Canvas of={Stories.Playground} layout="fullscreen" /> | ||
|
||
<Controls of={Stories.Playground} /> |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,65 @@ | ||
import { Meta, StoryObj } from '@storybook/react'; | ||
import React from 'react'; | ||
|
||
import { | ||
Accordion, | ||
AccordionProps, | ||
} from '../../src/lib/components/accordion/Accordion.component'; | ||
import { Stack } from '../../src/lib/spacing'; | ||
import { Button } from '../../src/lib/components/buttonv2/Buttonv2.component'; | ||
|
||
type AccordionStory = StoryObj<AccordionProps>; | ||
|
||
const meta: Meta<AccordionProps> = { | ||
title: 'Components/Accordion', | ||
component: Accordion, | ||
args: { | ||
title: 'Accordion title', | ||
children: ( | ||
<Stack direction="vertical" gap="r8"> | ||
<div>This is the content of the accordion.</div> | ||
<Button label={'Check'} onClick={() => console.log('click')}></Button> | ||
</Stack> | ||
), | ||
}, | ||
argTypes: { | ||
children: { | ||
control: { disable: true }, | ||
description: 'Content of the accordion', | ||
table: { | ||
type: { summary: 'React.ReactNode' }, | ||
}, | ||
}, | ||
title: { | ||
control: { type: 'text' }, | ||
description: 'Title of the accordion', | ||
table: { | ||
type: { summary: 'string' }, | ||
}, | ||
}, | ||
style: { | ||
control: { disable: true }, | ||
description: 'Use this to style the accordion content container', | ||
table: { type: { summary: 'CSSProperties' } }, | ||
}, | ||
id: { | ||
control: { disable: true }, | ||
table: { type: { summary: 'string' } }, | ||
description: 'Unique id for the accordion content container', | ||
}, | ||
}, | ||
}; | ||
|
||
export default meta; | ||
|
||
export const Playground: AccordionStory = {}; | ||
|
||
export const Stacked: AccordionStory = { | ||
render: (args) => ( | ||
<Stack direction="vertical" gap="r8"> | ||
<Accordion {...args} /> | ||
<Accordion {...args} /> | ||
<Accordion {...args} style={{ backgroundColor: 'grey' }} /> | ||
</Stack> | ||
), | ||
}; |