diff --git a/packages/components/src/Popover/__tests__/Popover.test.tsx b/packages/components/src/Popover/__tests__/Popover.test.tsx index de46fec41..a25b62ab3 100644 --- a/packages/components/src/Popover/__tests__/Popover.test.tsx +++ b/packages/components/src/Popover/__tests__/Popover.test.tsx @@ -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', () => { @@ -55,4 +57,118 @@ describe('Popover', () => { ); }); + + it('should able to open by using trigger', async () => { + render( + + + trigger + + + header + body + footer + + + ); + + 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( + + + trigger + + + header + body + footer + + + ); + + 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( + + + trigger + + + header + body + footer + + + ); + + 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( + + + trigger + + + header + body + footer + + + ); + + 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( + + + trigger + + + header + body + footer + + + ); + + expect(screen.getByRole('dialog', { name: /header/i })); + }); }); diff --git a/packages/components/src/Switch/Switch.tsx b/packages/components/src/Switch/Switch.tsx index ee61e5b5c..8e1d6ef0d 100644 --- a/packages/components/src/Switch/Switch.tsx +++ b/packages/components/src/Switch/Switch.tsx @@ -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'; @@ -14,6 +14,7 @@ import { StyledInput, StyledLabel, StyledSwitch, StyledWrapper } from './Switch. type Props = { onChange?: (e: ChangeEvent) => void; + onValueChange?: (isSelected: boolean) => void; onPress?: (e: PressEvent) => void; labelProps?: TextProps; labelPlacement?: Extract; @@ -26,11 +27,26 @@ type InheritAttrs = Omit; type SwitchProps = Props & NativeAttrs & InheritAttrs; const Switch = forwardRef( - ({ 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(null); - const ariaProps: AriaSwitchProps = { children, ...props }; + const ariaProps: AriaSwitchProps = { children, isSelected, isReadOnly, ...props }; const state = useToggleState(ariaProps); const { inputProps } = useSwitch(ariaProps, state, inputRef); @@ -41,6 +57,13 @@ const Switch = forwardRef( const { pressProps } = usePress(props); + const handleChange: ChangeEventHandler = (e) => { + const isSelected = e.target.checked; + + onChange?.(e); + onValueChange?.(isSelected); + }; + return ( ( hidden={hidden} style={style} > - + {children && {children}} diff --git a/packages/components/src/Switch/__tests__/Switch.test.tsx b/packages/components/src/Switch/__tests__/Switch.test.tsx index cf60bc161..9db22d991 100644 --- a/packages/components/src/Switch/__tests__/Switch.test.tsx +++ b/packages/components/src/Switch/__tests__/Switch.test.tsx @@ -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 '..'; @@ -22,4 +23,57 @@ describe('Switch', () => { it('should pass a11y', async () => { await testA11y(switch); }); + + it('should emit onChange and onValueChange', async () => { + const handleChange = jest.fn(); + const handleValueChange = jest.fn(); + + render( + + 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 ( + setState(isSelected)}> + switch + + ); + }; + + render(); + + 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); + + expect(screen.getByRole('switch', { name: /switch/i })).toBeDisabled(); + }); + + it('should be read only', async () => { + render(switch); + + expect(screen.getByRole('switch', { name: /switch/i })).toHaveAttribute('aria-readonly', 'true'); + }); });