Skip to content

Commit

Permalink
feat(Accordion): allow empty or hidden accordion rows (#1767)
Browse files Browse the repository at this point in the history
- adding an 'isExpandable' set to false will hide the features that afford clicking
- add tests to verify this behavior
- add new snapshots
  • Loading branch information
booc0mtaco authored Sep 29, 2023
1 parent 79f1b9f commit e044a85
Show file tree
Hide file tree
Showing 5 changed files with 337 additions and 79 deletions.
8 changes: 8 additions & 0 deletions src/components/Accordion/Accordion.module.css
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,10 @@
height: 3.375rem;
}

.accordion-button--empty {
pointer-events: none;
}

/**
* Small variant.
*/
Expand Down Expand Up @@ -105,6 +109,10 @@
color: var(--eds-theme-color-text-neutral-strong);
}

.accordion-panel--hidden {
padding: 0;
}

/**
* Small variant.
*/
Expand Down
45 changes: 45 additions & 0 deletions src/components/Accordion/Accordion.stories.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -149,6 +149,51 @@ export const DefaultOpen: StoryObj<Args> = {
},
};

export const EmptyStackedOpen: StoryObj<Args> = {
args: {
children: (
<>
<Accordion.Row defaultOpen isExpandable={false}>
<Accordion.Button>Massa quam egestas massa.</Accordion.Button>
<Accordion.Panel>
Lorem ipsum dolor sit amet, consectetur adipiscing elit. Nulla amet,
massa ultricies iaculis. Quam lacus maecenas nibh malesuada. At
tristique et ullamcorper rhoncus amet pharetra aliquet tortor.
Suscipit dui, nunc sit dui tellus massa laoreet tellus.
</Accordion.Panel>
</Accordion.Row>
<Accordion.Row defaultOpen>
<Accordion.Button>Massa quam egestas massa.</Accordion.Button>
<Accordion.Panel>
Lorem ipsum dolor sit amet, consectetur adipiscing elit. Nulla amet,
massa ultricies iaculis. Quam lacus maecenas nibh malesuada. At
tristique et ullamcorper rhoncus amet pharetra aliquet tortor.
Suscipit dui, nunc sit dui tellus massa laoreet tellus.
</Accordion.Panel>
</Accordion.Row>
<Accordion.Row defaultOpen>
<Accordion.Button>Massa quam egestas massa.</Accordion.Button>
<Accordion.Panel>
Lorem ipsum dolor sit amet, consectetur adipiscing elit. Nulla amet,
massa ultricies iaculis. Quam lacus maecenas nibh malesuada. At
tristique et ullamcorper rhoncus amet pharetra aliquet tortor.
Suscipit dui, nunc sit dui tellus massa laoreet tellus.
</Accordion.Panel>
</Accordion.Row>
<Accordion.Row defaultOpen>
<Accordion.Button>Massa quam egestas massa.</Accordion.Button>
<Accordion.Panel>
Lorem ipsum dolor sit amet, consectetur adipiscing elit. Nulla amet,
massa ultricies iaculis. Quam lacus maecenas nibh malesuada. At
tristique et ullamcorper rhoncus amet pharetra aliquet tortor.
Suscipit dui, nunc sit dui tellus massa laoreet tellus.
</Accordion.Panel>
</Accordion.Row>
</>
),
},
};

export const SmallOpen: StoryObj<Args> = {
args: {
...DefaultOpen.args,
Expand Down
29 changes: 28 additions & 1 deletion src/components/Accordion/Accordion.test.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -95,12 +95,39 @@ describe('<Accordion />', () => {
</Accordion.Row>
</Accordion>,
);
const accordionButton = screen.getByTestId('accordion-button');
const accordionButton = screen.getByRole('button');

await act(async () => {
await user.click(accordionButton);
});
expect(onOpen).toHaveBeenCalledTimes(1);
expect(onClose).not.toHaveBeenCalled();
});

it('should not call onOpen callback when accordion opens on an empty row', async () => {
const user = userEvent.setup();
const onClose = jest.fn();
const onOpen = jest.fn();
render(
<Accordion headingAs="h2">
<Accordion.Row isExpandable={false}>
<Accordion.Button
data-testid="accordion-button"
onClose={onClose}
onOpen={onOpen}
>
Accordion Button
</Accordion.Button>
<Accordion.Panel>Accordion Panel</Accordion.Panel>
</Accordion.Row>
</Accordion>,
);
const accordionButton = screen.getByRole('button');

await act(async () => {
await user.click(accordionButton);
});
expect(onOpen).not.toHaveBeenCalled();
expect(onClose).not.toHaveBeenCalled();
});
});
62 changes: 40 additions & 22 deletions src/components/Accordion/Accordion.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -85,6 +85,10 @@ type AccordionRowProps = {
* Whether panel is expanded by default.
*/
defaultOpen?: boolean;
/**
* Whether the row can show expandable content
*/
isExpandable?: boolean;
};

const AccordionContext = createContext<{
Expand All @@ -95,6 +99,10 @@ const AccordionContext = createContext<{
headingAs: 'h2',
});

const AccordionRowContext = createContext<{ isExpandable?: boolean }>({
isExpandable: true,
});

/**
* `import {Accordion} from "@chanzuckerberg/eds;`
*
Expand Down Expand Up @@ -140,10 +148,13 @@ const AccordionButton = ({
size,
} = useContext(AccordionContext);

const { isExpandable } = useContext(AccordionRowContext);

const componentClassName = clsx(
styles['accordion-button'],
size === 'sm' && styles['accordion-button--sm'],
hasOutline && styles['accordion-button--outline'],
!isExpandable && styles['accordion-button--empty'],
className,
);

Expand All @@ -159,19 +170,19 @@ const AccordionButton = ({
className={componentClassName}
fullWidth
onClick={() => {
if (open && onClose) {
if (open && isExpandable && onClose) {
onClose();
}
if (!open && onOpen) {
if (!open && isExpandable && onOpen) {
onOpen();
}
}}
onKeyDown={(e) => {
if (e.key === SPACEBAR_KEYCODE || e.key === ENTER_KEYCODE) {
if (open && onClose) {
if (open && isExpandable && onClose) {
onClose();
}
if (!open && onOpen) {
if (!open && isExpandable && onOpen) {
onOpen();
}
}
Expand All @@ -186,16 +197,18 @@ const AccordionButton = ({
>
{children}
</Heading>
<Icon
className={clsx(
styles['accordion-button__icon'],
open && styles['accordion-button__icon--open'],
)}
name={icon}
purpose="informative"
size="1.625rem"
title={open ? 'hide content' : 'show content'}
/>
{isExpandable && (
<Icon
className={clsx(
styles['accordion-button__icon'],
open && styles['accordion-button__icon--open'],
)}
name="expand-more"
purpose="informative"
size="1.625rem"
title={open ? 'hide content' : 'show content'}
/>
)}
</Button>
)}
</Disclosure.Button>
Expand All @@ -208,17 +221,19 @@ const AccordionPanel = ({
...other
}: AccordionPanelProps) => {
const { hasOutline, size } = useContext(AccordionContext);
const { isExpandable } = useContext(AccordionRowContext);

const componentClassName = clsx(
styles['accordion-panel'],
size === 'sm' && styles['accordion-panel--sm'],
hasOutline && styles['accordion-panel--outline'],
!isExpandable && styles['accordion-panel--hidden'],
className,
);

return (
<Disclosure.Panel className={componentClassName} {...other}>
{children}
{isExpandable && children}
</Disclosure.Panel>
);
};
Expand All @@ -227,6 +242,7 @@ const AccordionRow = ({
className,
defaultOpen,
children,
isExpandable = true,
...other
}: AccordionRowProps) => {
const { hasOutline } = useContext(AccordionContext);
Expand All @@ -236,13 +252,15 @@ const AccordionRow = ({
className,
);
return (
<Disclosure defaultOpen={defaultOpen}>
{({ open }) => (
<div className={componentClassName} {...other}>
{typeof children === 'function' ? children({ open }) : children}
</div>
)}
</Disclosure>
<AccordionRowContext.Provider value={{ isExpandable }}>
<Disclosure defaultOpen={defaultOpen}>
{({ open }) => (
<div className={componentClassName} {...other}>
{typeof children === 'function' ? children({ open }) : children}
</div>
)}
</Disclosure>
</AccordionRowContext.Provider>
);
};

Expand Down
Loading

0 comments on commit e044a85

Please sign in to comment.