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');
+ });
});