Skip to content

Commit

Permalink
chore(components): add tests
Browse files Browse the repository at this point in the history
  • Loading branch information
danielsimao committed Oct 2, 2023
1 parent d7e82b8 commit 101f96a
Show file tree
Hide file tree
Showing 3 changed files with 200 additions and 7 deletions.
118 changes: 117 additions & 1 deletion packages/components/src/Popover/__tests__/Popover.test.tsx
Original file line number Diff line number Diff line change
@@ -1,8 +1,10 @@
import { render } from '@testing-library/react';
import { render, screen, waitFor, waitForElementToBeRemoved } from '@testing-library/react';
import { createRef } from 'react';
import { testA11y } from '@interlay/test-utils';
import userEvent from '@testing-library/user-event';

import { Popover, PopoverBody, PopoverContent, PopoverFooter, PopoverHeader, PopoverTrigger } from '..';
import { CTA } from '../../CTA';

describe('Popover', () => {
it('should render correctly', () => {
Expand Down Expand Up @@ -55,4 +57,118 @@ describe('Popover', () => {
</Popover>
);
});

it('should able to open by using trigger', async () => {
render(
<Popover>
<PopoverTrigger>
<CTA>trigger</CTA>
</PopoverTrigger>
<PopoverContent>
<PopoverHeader>header</PopoverHeader>
<PopoverBody>body</PopoverBody>
<PopoverFooter>footer</PopoverFooter>
</PopoverContent>
</Popover>
);

userEvent.click(screen.getByRole('button', { name: /trigger/i }));

await waitFor(() => {
expect(screen.getByRole('dialog', { name: /header/i }));
});
});

it('should able to close popover by using ESC', async () => {
render(
<Popover>
<PopoverTrigger>
<CTA>trigger</CTA>
</PopoverTrigger>
<PopoverContent>
<PopoverHeader>header</PopoverHeader>
<PopoverBody>body</PopoverBody>
<PopoverFooter>footer</PopoverFooter>
</PopoverContent>
</Popover>
);

userEvent.click(screen.getByRole('button', { name: /trigger/i }));

await waitFor(() => {
expect(screen.getByRole('dialog', { name: /header/i }));
});

userEvent.keyboard('{Escape}');

await waitForElementToBeRemoved(screen.getByRole('dialog', { name: /header/i }));
});

it('should able to close popover by clicking outside of the component', async () => {
render(
<Popover>
<PopoverTrigger>
<CTA>trigger</CTA>
</PopoverTrigger>
<PopoverContent>
<PopoverHeader>header</PopoverHeader>
<PopoverBody>body</PopoverBody>
<PopoverFooter>footer</PopoverFooter>
</PopoverContent>
</Popover>
);

userEvent.click(screen.getByRole('button', { name: /trigger/i }));

await waitFor(() => {
expect(screen.getByRole('dialog', { name: /header/i }));
});

userEvent.click(document.body);

await waitForElementToBeRemoved(screen.getByRole('dialog', { name: /header/i }));
});

it('should emit onOpenChange', async () => {
const handleOpenChange = jest.fn();

render(
<Popover onOpenChange={handleOpenChange}>
<PopoverTrigger>
<CTA>trigger</CTA>
</PopoverTrigger>
<PopoverContent>
<PopoverHeader>header</PopoverHeader>
<PopoverBody>body</PopoverBody>
<PopoverFooter>footer</PopoverFooter>
</PopoverContent>
</Popover>
);

userEvent.click(screen.getByRole('button', { name: /trigger/i }));

await waitFor(() => {
expect(screen.getByRole('dialog', { name: /header/i }));
});

expect(handleOpenChange).toHaveBeenCalledTimes(1);
expect(handleOpenChange).toHaveBeenCalledWith(true);
});

it('should be default open', async () => {
render(
<Popover defaultOpen>
<PopoverTrigger>
<CTA>trigger</CTA>
</PopoverTrigger>
<PopoverContent>
<PopoverHeader>header</PopoverHeader>
<PopoverBody>body</PopoverBody>
<PopoverFooter>footer</PopoverFooter>
</PopoverContent>
</Popover>
);

expect(screen.getByRole('dialog', { name: /header/i }));
});
});
31 changes: 27 additions & 4 deletions packages/components/src/Switch/Switch.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@ import { AriaSwitchProps, useSwitch } from '@react-aria/switch';
import { mergeProps } from '@react-aria/utils';
import { useToggleState } from '@react-stately/toggle';
import { PressEvent } from '@react-types/shared';
import { ChangeEvent, forwardRef, HTMLAttributes, useRef } from 'react';
import { ChangeEvent, ChangeEventHandler, forwardRef, HTMLAttributes, useRef } from 'react';
import { Placement } from '@interlay/theme';
import { useDOMRef } from '@interlay/hooks';

Expand All @@ -14,6 +14,7 @@ import { StyledInput, StyledLabel, StyledSwitch, StyledWrapper } from './Switch.

type Props = {
onChange?: (e: ChangeEvent<HTMLInputElement>) => void;
onValueChange?: (isSelected: boolean) => void;
onPress?: (e: PressEvent) => void;
labelProps?: TextProps;
labelPlacement?: Extract<Placement, 'left' | 'right'>;
Expand All @@ -26,11 +27,26 @@ type InheritAttrs = Omit<AriaSwitchProps, keyof Props>;
type SwitchProps = Props & NativeAttrs & InheritAttrs;

const Switch = forwardRef<HTMLLabelElement, SwitchProps>(
({ children, onChange, className, style, hidden, labelProps, labelPlacement, ...props }, ref): JSX.Element => {
(
{
children,
onChange,
onValueChange,
className,
style,
hidden,
labelProps,
labelPlacement,
isSelected,
isReadOnly,
...props
},
ref
): JSX.Element => {
const labelRef = useDOMRef(ref);
const inputRef = useRef<HTMLInputElement>(null);

const ariaProps: AriaSwitchProps = { children, ...props };
const ariaProps: AriaSwitchProps = { children, isSelected, isReadOnly, ...props };

const state = useToggleState(ariaProps);
const { inputProps } = useSwitch(ariaProps, state, inputRef);
Expand All @@ -41,6 +57,13 @@ const Switch = forwardRef<HTMLLabelElement, SwitchProps>(

const { pressProps } = usePress(props);

const handleChange: ChangeEventHandler<HTMLInputElement> = (e) => {
const isSelected = e.target.checked;

onChange?.(e);
onValueChange?.(isSelected);
};

return (
<StyledWrapper
ref={labelRef}
Expand All @@ -49,7 +72,7 @@ const Switch = forwardRef<HTMLLabelElement, SwitchProps>(
hidden={hidden}
style={style}
>
<StyledInput {...mergeProps(inputProps, focusProps, pressProps, { onChange })} ref={inputRef} />
<StyledInput {...mergeProps(inputProps, focusProps, pressProps, { onChange: handleChange })} ref={inputRef} />
<StyledSwitch $isChecked={inputProps.checked} $isFocusVisible={isFocusVisible} />
{children && <StyledLabel {...labelProps}>{children}</StyledLabel>}
</StyledWrapper>
Expand Down
58 changes: 56 additions & 2 deletions packages/components/src/Switch/__tests__/Switch.test.tsx
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
import { render } from '@testing-library/react';
import { createRef } from 'react';
import { render, screen, waitFor } from '@testing-library/react';
import { createRef, useState } from 'react';
import { testA11y } from '@interlay/test-utils';
import userEvent from '@testing-library/user-event';

import { Switch } from '..';

Expand All @@ -22,4 +23,57 @@ describe('Switch', () => {
it('should pass a11y', async () => {
await testA11y(<Switch>switch</Switch>);
});

it('should emit onChange and onValueChange', async () => {
const handleChange = jest.fn();
const handleValueChange = jest.fn();

render(
<Switch onChange={handleChange} onValueChange={handleValueChange}>
switch
</Switch>
);

userEvent.click(screen.getByRole('switch', { name: /switch/i }));

await waitFor(() => {
expect(handleChange).toHaveBeenCalledTimes(1);
expect(handleValueChange).toHaveBeenCalledTimes(1);
expect(handleValueChange).toHaveBeenCalledWith(true);
});
});

it('should control value', async () => {
const Component = () => {
const [state, setState] = useState(false);

return (
<Switch isSelected={state} onValueChange={(isSelected) => setState(isSelected)}>
switch
</Switch>
);
};

render(<Component />);

expect(screen.getByRole('switch', { name: /switch/i })).not.toBeChecked();

userEvent.click(screen.getByRole('switch', { name: /switch/i }));

await waitFor(() => {
expect(screen.getByRole('switch', { name: /switch/i })).toBeChecked();
});
});

it('should be disabled', async () => {
render(<Switch isDisabled>switch</Switch>);

expect(screen.getByRole('switch', { name: /switch/i })).toBeDisabled();
});

it('should be read only', async () => {
render(<Switch isReadOnly>switch</Switch>);

expect(screen.getByRole('switch', { name: /switch/i })).toHaveAttribute('aria-readonly', 'true');
});
});

0 comments on commit 101f96a

Please sign in to comment.