-
Notifications
You must be signed in to change notification settings - Fork 65
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
feat(component): add AccordionPanel component #933
Merged
Merged
Changes from 13 commits
Commits
Show all changes
14 commits
Select commit
Hold shift + click to select a range
f02f323
feat(component): add AccordionGroup component
bc-athorne 1b0240d
Merge branch 'bigcommerce:master' into CHI-1984
bc-athorne b0cc682
feat(component): add PR feedback
bc-athorne 9196659
feat(component): add styled wrapper for panel component
bc-athorne dd27dcf
feat(component): move logic to hook
bc-athorne 866b4d5
feat(component): more PR feedback
bc-athorne 560ad41
feat(component): remove commented code
bc-athorne 113fd93
feat(component): export hook logic and add PR feedback
bc-athorne 8f1747d
--wip-- [skip ci]
chanceaclark 830a3b6
--wip-- [skip ci]
chanceaclark 8a52eb9
Merge pull request #1 from chanceaclark/chore/refactor-accordion
bc-athorne 66eafd9
Merge branch 'bigcommerce:master' into CHI-1984
bc-athorne cab177e
feat(component): refactor hook
bc-athorne ace7036
feat(component): fix testing variables
bc-athorne File filter
Filter by extension
Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
There are no files selected for viewing
49 changes: 49 additions & 0 deletions
49
packages/big-design/src/components/AccordionPanel/Accordion/Accordion.tsx
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,49 @@ | ||
import { ExpandMoreIcon } from '@bigcommerce/big-design-icons'; | ||
import React, { memo } from 'react'; | ||
|
||
import { useUniqueId } from '../../../hooks'; | ||
|
||
import { StyledAccordionButton, StyledAccordionContent } from './styled'; | ||
|
||
export interface AccordionProps { | ||
children?: React.ReactNode; | ||
defaultExpanded?: boolean; | ||
header: string; | ||
iconLeft?: React.ReactNode; | ||
isExpanded: boolean; | ||
onClick: React.MouseEventHandler<HTMLButtonElement>; | ||
} | ||
|
||
export const Accordion: React.FC<AccordionProps> = memo( | ||
({ children, header, iconLeft, isExpanded, onClick }) => { | ||
const accordionId = useUniqueId('accordion'); | ||
const accordionItemId = useUniqueId('accordion-item'); | ||
|
||
return ( | ||
<> | ||
<StyledAccordionButton | ||
aria-controls={accordionItemId} | ||
aria-expanded={isExpanded} | ||
iconLeft={iconLeft} | ||
iconRight={<ExpandMoreIcon className="collapse-icon" color="secondary70" />} | ||
id={accordionId} | ||
isExpanded={isExpanded} | ||
onClick={onClick} | ||
variant="subtle" | ||
> | ||
{header} | ||
</StyledAccordionButton> | ||
<StyledAccordionContent | ||
aria-labelledby={accordionId} | ||
display={isExpanded ? 'block' : 'none'} | ||
hidden={!isExpanded} | ||
iconLeft={iconLeft} | ||
id={accordionItemId} | ||
role="region" | ||
> | ||
{children} | ||
</StyledAccordionContent> | ||
</> | ||
); | ||
}, | ||
); |
4 changes: 4 additions & 0 deletions
4
packages/big-design/src/components/AccordionPanel/Accordion/index.ts
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,4 @@ | ||
import { AccordionProps as _AccordionProps } from './Accordion'; | ||
|
||
export { Accordion } from './Accordion'; | ||
export type AccordionProps = _AccordionProps; |
58 changes: 58 additions & 0 deletions
58
packages/big-design/src/components/AccordionPanel/Accordion/styled.tsx
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,58 @@ | ||
import { theme as defaultTheme, remCalc } from '@bigcommerce/big-design-theme'; | ||
import styled, { css } from 'styled-components'; | ||
|
||
import { withTransition } from '../../../mixins/transitions'; | ||
import { Box } from '../../Box'; | ||
import { StyleableButton } from '../../Button/private'; | ||
|
||
interface StyledAccordionButtonProps { | ||
isExpanded: boolean; | ||
} | ||
|
||
interface StyledAccordionContentProps { | ||
iconLeft: React.ReactNode; | ||
} | ||
|
||
export const StyledAccordionButton = styled(StyleableButton)<StyledAccordionButtonProps>` | ||
border-top: ${({ theme }) => theme.border.box}; | ||
border-radius: 0; | ||
padding: ${({ theme }) => theme.spacing.xLarge}; | ||
text-align: left; | ||
width: 100%; | ||
span { | ||
width: 100%; | ||
color: ${({ theme }) => theme.colors.secondary70}; | ||
grid-template-columns: ${({ iconLeft, theme }) => | ||
iconLeft | ||
? `${theme.spacing.xLarge} 1fr ${theme.spacing.medium}` | ||
: `1fr ${theme.spacing.medium}`}; | ||
} | ||
|
||
${({ isExpanded }) => | ||
isExpanded && | ||
css` | ||
border-bottom: ${({ theme }) => theme.border.box}; | ||
`} | ||
|
||
&:focus { | ||
z-index: ${({ theme }) => theme.zIndex.fixed}; | ||
} | ||
|
||
.collapse-icon { | ||
${withTransition(['transform'])} | ||
|
||
${({ isExpanded }) => | ||
isExpanded && | ||
css` | ||
transform: rotate(-180deg); | ||
`} | ||
} | ||
`; | ||
|
||
export const StyledAccordionContent = styled(Box)<StyledAccordionContentProps>` | ||
padding: ${({ theme }) => theme.spacing.xLarge}}; | ||
padding-left: ${({ iconLeft, theme }) => (iconLeft ? remCalc(60) : `${theme.spacing.xLarge}`)}; | ||
`; | ||
|
||
StyledAccordionButton.defaultProps = { theme: defaultTheme }; | ||
StyledAccordionContent.defaultProps = { theme: defaultTheme }; |
23 changes: 23 additions & 0 deletions
23
packages/big-design/src/components/AccordionPanel/AccordionPanel.tsx
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,23 @@ | ||
import React, { memo } from 'react'; | ||
|
||
import { Panel } from '../Panel'; | ||
|
||
import { Accordion, AccordionProps } from './Accordion'; | ||
import { StyledAccordionPanelWrapper } from './styled'; | ||
|
||
export interface AccordionPanelProps { | ||
header: string; | ||
panels: AccordionProps[]; | ||
} | ||
|
||
export const AccordionPanel: React.FC<AccordionPanelProps> = memo(({ header, panels }) => { | ||
return ( | ||
<Panel header={header}> | ||
<StyledAccordionPanelWrapper> | ||
{panels.map((panel, index) => ( | ||
<Accordion {...panel} key={index} /> | ||
))} | ||
</StyledAccordionPanelWrapper> | ||
</Panel> | ||
); | ||
}); |
9 changes: 9 additions & 0 deletions
9
packages/big-design/src/components/AccordionPanel/__snapshots__/spec.tsx.snap
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,9 @@ | ||
// Jest Snapshot v1, https://goo.gl/fbAQLP | ||
|
||
exports[`it renders accordion panel header 1`] = ` | ||
<h2 | ||
class="styled__StyledH2-sc-tqnj75-2 styled__StyledH2-sc-1h6ef3q-1 kEGvNd fjdBDH" | ||
> | ||
Accordion Panel Header | ||
</h2> | ||
`; |
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,5 @@ | ||
import { AccordionPanelProps as _AccordionPanelProps } from './AccordionPanel'; | ||
|
||
export { AccordionPanel } from './AccordionPanel'; | ||
export { useAccordionPanel } from './useAccordionPanel'; | ||
export type AccordionPanelProps = _AccordionPanelProps; |
137 changes: 137 additions & 0 deletions
137
packages/big-design/src/components/AccordionPanel/spec.tsx
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,137 @@ | ||
import { ErrorIcon } from '@bigcommerce/big-design-icons'; | ||
import userEvent from '@testing-library/user-event'; | ||
import React, { useState } from 'react'; | ||
|
||
import { render, screen } from '@test/utils'; | ||
|
||
import { Text } from '../Typography'; | ||
|
||
import { AccordionProps } from './Accordion'; | ||
|
||
import { AccordionPanel, useAccordionPanel } from '.'; | ||
|
||
let panels: AccordionProps[]; | ||
|
||
const TestComponent: React.FC = () => { | ||
({ panels } = useAccordionPanel([ | ||
{ | ||
header: 'Panel Header', | ||
children: <Text>This is a child component</Text>, | ||
}, | ||
])); | ||
|
||
return <AccordionPanel header="Accordion Panel Header" panels={panels} />; | ||
}; | ||
|
||
const TestComponentWithoutHook: React.FC = () => { | ||
const [isExpanded, setIsExpanded] = useState(false); | ||
|
||
const testPanels: AccordionProps[] = [ | ||
{ | ||
header: 'Panel Header', | ||
isExpanded, | ||
children: <Text>This is a child component</Text>, | ||
onClick: () => setIsExpanded(!isExpanded), | ||
}, | ||
]; | ||
|
||
return <AccordionPanel header="Accordion Panel Header" panels={testPanels} />; | ||
}; | ||
|
||
const TestComponentWithDefault: React.FC = () => { | ||
({ panels } = useAccordionPanel([ | ||
{ | ||
defaultExpanded: true, | ||
header: 'Panel Header', | ||
iconLeft: <ErrorIcon />, | ||
children: <Text>This is a child component</Text>, | ||
}, | ||
])); | ||
|
||
return <AccordionPanel header="Accordion Panel Header" panels={panels} />; | ||
}; | ||
|
||
test('it renders accordion panel header', async () => { | ||
render(<TestComponent />); | ||
|
||
const header = await screen.findByRole('heading', { name: /Accordion Panel Header/i }); | ||
|
||
expect(header).toMatchSnapshot(); | ||
expect(header).toBeVisible(); | ||
}); | ||
|
||
test('it renders accordion panel children', async () => { | ||
render(<TestComponent />); | ||
|
||
const child = await screen.findByText('Panel Header'); | ||
|
||
expect(child).toBeVisible(); | ||
}); | ||
|
||
test('accordion renders header', async () => { | ||
render(<TestComponent />); | ||
|
||
const header = await screen.findByText('Panel Header'); | ||
|
||
expect(header).toBeVisible(); | ||
}); | ||
|
||
test('accordion collapses and expands on click', async () => { | ||
render(<TestComponent />); | ||
|
||
const buttonPanel = await screen.findByRole('button'); | ||
|
||
await userEvent.click(buttonPanel); | ||
|
||
const panelChild = await screen.findByRole('region'); | ||
|
||
expect(panelChild).toBeVisible(); | ||
|
||
await userEvent.click(buttonPanel); | ||
|
||
expect(panelChild).not.toBeVisible(); | ||
}); | ||
|
||
test('accordion renders children', async () => { | ||
render(<TestComponent />); | ||
|
||
const buttonPanel = await screen.findByRole('button'); | ||
|
||
await userEvent.click(buttonPanel); | ||
|
||
const child = await screen.findByRole('region'); | ||
|
||
expect(child).toBeVisible(); | ||
}); | ||
|
||
test('if defaultExpanded is set to true, accordion panel is expanded', async () => { | ||
render(<TestComponentWithDefault />); | ||
|
||
const panelChild = await screen.findByRole('region'); | ||
|
||
expect(panelChild).toBeVisible(); | ||
}); | ||
|
||
test('it renders icon if iconLeft prop is defined', async () => { | ||
render(<TestComponentWithDefault />); | ||
|
||
const icons = (await screen.findByRole('button')).querySelectorAll('svg'); | ||
|
||
const icon = icons[0]; | ||
|
||
expect(icon).toBeVisible(); | ||
}); | ||
|
||
test('it renders header and children accordion elements without hook', async () => { | ||
render(<TestComponentWithoutHook />); | ||
|
||
const header = await screen.findByText('Panel Header'); | ||
const buttonPanel = await screen.findByRole('button'); | ||
|
||
await userEvent.click(buttonPanel); | ||
|
||
const child = await screen.findByRole('region'); | ||
|
||
expect(child).toBeVisible(); | ||
expect(header).toBeVisible(); | ||
}); |
20 changes: 20 additions & 0 deletions
20
packages/big-design/src/components/AccordionPanel/styled.tsx
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,20 @@ | ||
import { theme as defaultTheme } from '@bigcommerce/big-design-theme'; | ||
import styled from 'styled-components'; | ||
|
||
import { Flex } from '../Flex'; | ||
|
||
export const StyledAccordionPanelWrapper = styled(Flex)` | ||
flex-direction: column; | ||
|
||
margin-bottom: -${({ theme }) => theme.spacing.medium}; | ||
margin-left: -${({ theme }) => theme.spacing.medium}; | ||
margin-right: -${({ theme }) => theme.spacing.medium}; | ||
|
||
${({ theme }) => theme.breakpoints.tablet} { | ||
margin-bottom: -${({ theme }) => theme.spacing.xLarge}; | ||
margin-left: -${({ theme }) => theme.spacing.xLarge}; | ||
margin-right: -${({ theme }) => theme.spacing.xLarge}; | ||
} | ||
`; | ||
|
||
StyledAccordionPanelWrapper.defaultProps = { theme: defaultTheme }; |
25 changes: 25 additions & 0 deletions
25
packages/big-design/src/components/AccordionPanel/useAccordionPanel.ts
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,25 @@ | ||
import { useState } from 'react'; | ||
|
||
import { AccordionProps } from './Accordion'; | ||
|
||
type InitialPanels = Array<Omit<AccordionProps, 'isExpanded' | 'onClick'>>; | ||
|
||
export const useAccordionPanel = (initialPanels: InitialPanels) => { | ||
const [expandedPanels, setExpandedPanels] = useState(() => { | ||
return initialPanels.map(({ defaultExpanded }) => defaultExpanded ?? false); | ||
}); | ||
|
||
const handleOnClick = (panelIndex: number) => () => { | ||
setExpandedPanels( | ||
expandedPanels.map((isExpanded, index) => (index === panelIndex ? !isExpanded : isExpanded)), | ||
); | ||
}; | ||
|
||
return { | ||
panels: expandedPanels.map<AccordionProps>((isExpanded, index) => ({ | ||
...initialPanels[index], | ||
isExpanded, | ||
onClick: handleOnClick(index), | ||
})), | ||
}; | ||
}; |
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 |
---|---|---|
@@ -1,3 +1,4 @@ | ||
export * from './AccordionPanel'; | ||
export * from './Alert'; | ||
export * from './Badge'; | ||
export * from './Box'; | ||
|
Oops, something went wrong.
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Maybe we should just assign this to a local const instead of using this type of JavaScript sorcery.
Same below...