Skip to content

Commit

Permalink
feat: add event hooks to existing components (#1757)
Browse files Browse the repository at this point in the history
- accordion

Add event tests for remaining form field components
  • Loading branch information
booc0mtaco authored Sep 18, 2023
1 parent e3f2bc1 commit 9045cf5
Show file tree
Hide file tree
Showing 10 changed files with 107 additions and 33 deletions.
27 changes: 27 additions & 0 deletions src/components/Accordion/Accordion.test.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -76,4 +76,31 @@ describe('<Accordion />', () => {
});
expect(onClose).toHaveBeenCalledTimes(1);
});

it('should call onOpen callback when accordion opens', async () => {
const user = userEvent.setup();
const onClose = jest.fn();
const onOpen = jest.fn();
render(
<Accordion headingAs="h2">
<Accordion.Row>
<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.getByTestId('accordion-button');

await act(async () => {
await user.click(accordionButton);
});
expect(onOpen).toHaveBeenCalledTimes(1);
expect(onClose).not.toHaveBeenCalled();
});
});
16 changes: 14 additions & 2 deletions src/components/Accordion/Accordion.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ import { Disclosure } from '@headlessui/react';
import clsx from 'clsx';
import React, { createContext, useContext } from 'react';
import { ENTER_KEYCODE, SPACEBAR_KEYCODE } from '../../util/keycodes';
import type { Size } from '../../util/variant-types';
import Button from '../Button';
import Heading, { type HeadingElement } from '../Heading';
import Icon from '../Icon';
Expand All @@ -27,7 +28,7 @@ type AccordionProps = {
/**
* Various Accordion sizes. Defaults to 'md'.
*/
size?: 'sm' | 'md';
size?: Extract<Size, 'sm' | 'md'>;
};

type AccordionButtonProps = {
Expand All @@ -45,9 +46,13 @@ type AccordionButtonProps = {
*/
headingAs?: HeadingElement;
/**
* Callback called when accordion is closed.
* Callback for when accordion is closed.
*/
onClose?: () => void;
/**
* Callback for when according is opened.
*/
onOpen?: () => void;
};

type AccordionPanelProps = {
Expand Down Expand Up @@ -138,6 +143,7 @@ const AccordionButton = ({
className,
headingAs,
onClose,
onOpen,
...other
}: AccordionButtonProps) => {
const {
Expand Down Expand Up @@ -168,12 +174,18 @@ const AccordionButton = ({
if (open && onClose) {
onClose();
}
if (!open && onOpen) {
onOpen();
}
}}
onKeyDown={(e) => {
if (e.key === SPACEBAR_KEYCODE || e.key === ENTER_KEYCODE) {
if (open && onClose) {
onClose();
}
if (!open && onOpen) {
onOpen();
}
}
}}
status="neutral"
Expand Down
12 changes: 1 addition & 11 deletions src/components/Breadcrumbs/Breadcrumbs.stories.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -28,17 +28,7 @@ export default {
parameters: {
badges: ['1.0'],
},
decorators: [
(Story) => (
<div
style={{
margin: '0.5rem',
}}
>
{Story()}
</div>
),
],
decorators: [(Story) => <div style={{ margin: '0.5rem' }}>{Story()}</div>],
} as Meta<Args>;

type Args = React.ComponentProps<typeof Breadcrumbs>;
Expand Down
3 changes: 1 addition & 2 deletions src/components/Breadcrumbs/Breadcrumbs.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,6 @@ import clsx from 'clsx';
import { debounce } from 'lodash';
import React, { createContext, useContext, type ReactNode } from 'react';
import { flattenReactChildren } from '../../util/flattenReactChildren';
// import BreadcrumbsItem from '../BreadcrumbsItem';
import Icon from '../Icon';
import Menu from '../Menu';
import styles from './Breadcrumbs.module.css';
Expand Down Expand Up @@ -250,7 +249,7 @@ export const BreadcrumbsItem = ({
aria-label="Show more breadcrumbs"
className={ellipsisButtonClassName}
>
...
</Menu.PlainButton>
<Menu.Items>{menuItems}</Menu.Items>
</Menu>
Expand Down
9 changes: 4 additions & 5 deletions src/components/Checkbox/Checkbox.test.tsx
Original file line number Diff line number Diff line change
@@ -1,14 +1,11 @@
import { generateSnapshots } from '@chanzuckerberg/story-utils';
import type { StoryFile } from '@storybook/testing-react';
import { composeStories } from '@storybook/testing-react';
import { render, screen } from '@testing-library/react';
import userEvent from '@testing-library/user-event';
import React from 'react';
import { Checkbox } from './Checkbox';
import * as stories from './Checkbox.stories';

const { Default } = composeStories(stories);

describe('<Checkbox />', () => {
generateSnapshots(stories as StoryFile);

Expand All @@ -18,12 +15,14 @@ describe('<Checkbox />', () => {
expect(container.firstChild).toMatchSnapshot();
});

test('should toggle the checkbox with space', async () => {
test('should toggle the checkbox with space and trigger onChange', async () => {
const user = userEvent.setup();
render(<Default />);
const onChange = jest.fn();
render(<Checkbox aria-label="test-checkbox" onChange={onChange} />);
const checkbox = screen.getByRole('checkbox');
checkbox.focus();
await user.keyboard(' ');
expect(checkbox).toBeChecked();
expect(onChange).toHaveBeenCalledTimes(1);
});
});
7 changes: 0 additions & 7 deletions src/components/InputField/InputField.test.ts

This file was deleted.

36 changes: 36 additions & 0 deletions src/components/InputField/InputField.test.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,36 @@
import { generateSnapshots } from '@chanzuckerberg/story-utils';
import type { StoryFile } from '@storybook/testing-react';
import { act, render, screen } from '@testing-library/react';

import userEvent from '@testing-library/user-event';
import React from 'react';
import { InputField } from './InputField';

import * as stories from './InputField.stories';

describe('<InputField />', () => {
generateSnapshots(stories as StoryFile);

it('handles changes to the text within the component', async () => {
const user = userEvent.setup();
const onChange = jest.fn();

render(
<InputField
aria-label="label"
data-testid="test-input"
onChange={onChange}
/>,
);
const input = screen.getByTestId('test-input');
const testText = 'typing';

input.focus();

await act(async () => {
await user.keyboard(testText);
});

expect(onChange).toHaveBeenCalledTimes(testText.length);
});
});
3 changes: 1 addition & 2 deletions src/components/Popover/Popover.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -26,7 +26,7 @@ export const PopoverContext = createContext<PopoverContextType>({});
/**
* `import {Popover} from "@chanzuckerberg/eds";`
*
* General-purpose floating menus that appear proximal to a trigger point
* General-purpose floating containers that appear proximal to a trigger point
*/
export const Popover = ({
placement = 'bottom-start',
Expand All @@ -36,7 +36,6 @@ export const Popover = ({
...other
}: PopoverProps) => {
const [referenceElement, setReferenceElement] = useState<Element>();

const [popperElement, setPopperElement] = useState<HTMLElement>();

// Leverage usePopper hook from Popper js for additional popover behavior and adds behavior to context for consumption by subcomponents.
Expand Down
27 changes: 23 additions & 4 deletions src/components/Radio/Radio.test.tsx
Original file line number Diff line number Diff line change
@@ -1,24 +1,43 @@
import { generateSnapshots } from '@chanzuckerberg/story-utils';
import type { StoryFile } from '@storybook/testing-react';
import { composeStories } from '@storybook/testing-react';
import { act, render, screen } from '@testing-library/react';
import userEvent from '@testing-library/user-event';
import React from 'react';
import { Radio } from './Radio';
import * as stories from './Radio.stories';

const { Default } = composeStories(stories);

describe('<Radio />', () => {
generateSnapshots(stories as StoryFile);

test('should toggle the radio with space', async () => {
const user = userEvent.setup();
render(<Default />);
const onChange = jest.fn();

function ControlledRadio() {
const [checked, setChecked] = React.useState(false);
const handleChange = () => {
setChecked(!checked);
onChange();
};

return (
<Radio
aria-label="test-radio"
checked={checked}
onChange={handleChange}
/>
);
}

render(<ControlledRadio />);
const radio = screen.getByRole('radio');
radio.focus();

await act(async () => {
await user.keyboard(' ');
});

expect(radio).toBeChecked();
expect(onChange).toHaveBeenCalledTimes(1);
});
});

0 comments on commit 9045cf5

Please sign in to comment.