@hyphen/hyphen-components 7.3.2 → 7.3.3
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/css/utilities.css +1 -1
- package/dist/css/variables.css +18 -28
- package/dist/hyphen-components.cjs.development.js +5717 -5021
- package/dist/hyphen-components.cjs.development.js.map +1 -1
- package/dist/hyphen-components.cjs.production.min.js +18 -2
- package/dist/hyphen-components.cjs.production.min.js.map +1 -1
- package/dist/hyphen-components.esm.js +5618 -4846
- package/dist/hyphen-components.esm.js.map +1 -1
- package/dist/index.d.ts +2693 -57
- package/dist/index.js +0 -1
- package/package.json +18 -19
- package/src/components/Badge/Badge.module.scss +6 -0
- package/src/components/Badge/Badge.stories.tsx +1 -0
- package/src/components/Badge/Badge.test.tsx +3 -2
- package/src/components/Badge/Badge.tsx +5 -3
- package/src/components/Box/Box.tsx +5 -2
- package/src/components/Button/Button.module.scss +1 -1
- package/src/components/Button/Button.test.tsx +2 -2
- package/src/components/Calendar/Calendar.test.tsx +262 -0
- package/src/components/Card/Card.tsx +2 -0
- package/src/components/CheckboxInput/components/Checkbox.module.scss +1 -1
- package/src/components/CheckboxInput/components/Checkbox.tsx +2 -0
- package/src/components/Details/Details.module.scss +2 -2
- package/src/components/Details/Details.tsx +2 -0
- package/src/components/Drawer/Drawer.stories.tsx +1 -1
- package/src/components/Drawer/Drawer.test.tsx +494 -56
- package/src/components/Drawer/Drawer.tsx +7 -1
- package/src/components/DropdownMenu/DropdownMenu.test.tsx +532 -12
- package/src/components/FormControl/FormControl.tsx +2 -0
- package/src/components/Formik/Formik.stories.tsx +30 -7
- package/src/components/Formik/FormikSelectInput/FormikSelectInput.tsx +6 -5
- package/src/components/Formik/FormikToggleGroup/FormikToggleGroup.tsx +1 -1
- package/src/components/HelpText/HelpText.tsx +2 -0
- package/src/components/Icon/Icon.stories.tsx +1 -1
- package/src/components/Icon/Icon.tsx +2 -0
- package/src/components/Modal/Modal.test.tsx +630 -81
- package/src/components/Modal/Modal.tsx +2 -0
- package/src/components/Modal/components/ModalFooter/ModalFooter.test.tsx +2 -2
- package/src/components/Popover/Popover.tsx +2 -0
- package/src/components/RadioGroup/RadioInput/RadioInput.tsx +2 -0
- package/src/components/SelectInput/SelectInput.stories.tsx +22 -22
- package/src/components/SelectInput/SelectInput.tsx +13 -9
- package/src/components/SelectInputInset/SelectInputInset.tsx +2 -0
- package/src/components/Sidebar/Sidebar.module.scss +4 -0
- package/src/components/Sidebar/Sidebar.stories.tsx +8 -4
- package/src/components/Sidebar/Sidebar.test.tsx +7 -4
- package/src/components/Sidebar/Sidebar.tsx +7 -4
- package/src/components/Table/Table.stories.tsx +102 -52
- package/src/components/TextInput/TextInput.tsx +2 -0
- package/src/components/TextInputInset/TextInputInset.tsx +2 -0
- package/src/components/TextareaInputInset/TextareaInputInset.tsx +2 -0
- package/src/components/TimePickerNative/TimePickerNative.stories.tsx +0 -1
- package/src/components/Toast/Toast.store.ts +1 -1
- package/src/components/Toast/Toast.stories.tsx +3 -2
- package/src/components/Toast/Toast.test.tsx +8 -6
- package/src/components/Toggle/Toggle.tsx +2 -0
- package/src/components/ToggleGroup/ToggleGroup.tsx +2 -0
- package/src/docs/Colors.mdx +0 -13
- package/src/lib/getColumnKeys.ts +3 -3
- package/src/lib/mergeRefs.ts +1 -1
- package/src/lib/tokens.ts +4 -4
- package/dist/components/Alert/Alert.constants.d.ts +0 -8
- package/dist/components/Alert/Alert.d.ts +0 -42
- package/dist/components/Alert/Alert.stories.d.ts +0 -12
- package/dist/components/Alert/Alert.types.d.ts +0 -7
- package/dist/components/AspectRatio/AspectRatio.d.ts +0 -3
- package/dist/components/AspectRatio/AspectRatio.stories.d.ts +0 -6
- package/dist/components/Badge/Badge.d.ts +0 -24
- package/dist/components/Badge/Badge.stories.d.ts +0 -8
- package/dist/components/Box/Box.d.ts +0 -247
- package/dist/components/Box/Box.stories.d.ts +0 -46
- package/dist/components/Button/Button.constants.d.ts +0 -3
- package/dist/components/Button/Button.d.ts +0 -53
- package/dist/components/Button/Button.stories.d.ts +0 -16
- package/dist/components/Calendar/Calendar.d.ts +0 -7
- package/dist/components/Calendar/Calendar.stories.d.ts +0 -12
- package/dist/components/Card/Card.d.ts +0 -17
- package/dist/components/Card/Card.stories.d.ts +0 -8
- package/dist/components/Card/components/CardFooter/CardFooter.d.ts +0 -13
- package/dist/components/Card/components/CardHeader/CardHeader.d.ts +0 -13
- package/dist/components/Card/components/CardSection/CardSection.d.ts +0 -46
- package/dist/components/Card/components/index.d.ts +0 -3
- package/dist/components/CheckboxInput/CheckboxInput.d.ts +0 -72
- package/dist/components/CheckboxInput/CheckboxInput.stories.d.ts +0 -18
- package/dist/components/CheckboxInput/components/Checkbox.d.ts +0 -71
- package/dist/components/CheckboxInput/components/CheckboxIcon.d.ts +0 -27
- package/dist/components/Collapsible/Collapsible.d.ts +0 -5
- package/dist/components/Collapsible/Collapsible.stories.d.ts +0 -9
- package/dist/components/Details/Details.d.ts +0 -15
- package/dist/components/Details/Details.stories.d.ts +0 -6
- package/dist/components/Details/DetailsSummary.d.ts +0 -7
- package/dist/components/Drawer/Drawer.d.ts +0 -105
- package/dist/components/Drawer/Drawer.stories.d.ts +0 -62
- package/dist/components/DropdownMenu/DropdownMenu.d.ts +0 -25
- package/dist/components/DropdownMenu/DropdownMenu.stories.d.ts +0 -9
- package/dist/components/FormControl/FormControl.d.ts +0 -38
- package/dist/components/FormLabel/FormLabel.d.ts +0 -41
- package/dist/components/FormLabel/FormLabel.stories.d.ts +0 -6
- package/dist/components/Formik/Formik.stories.d.ts +0 -18
- package/dist/components/Formik/FormikCheckboxInput/FormikCheckboxInput.d.ts +0 -12
- package/dist/components/Formik/FormikRadioGroup/FormikRadioGroup.d.ts +0 -12
- package/dist/components/Formik/FormikSelectInput/FormikSelectInput.d.ts +0 -13
- package/dist/components/Formik/FormikSelectInputInset/FormikSelectInputInset.d.ts +0 -12
- package/dist/components/Formik/FormikSelectInputNative/FormikSelectInputNative.d.ts +0 -12
- package/dist/components/Formik/FormikSwitch/FormikSwitch.d.ts +0 -12
- package/dist/components/Formik/FormikTextInput/FormikTextInput.d.ts +0 -12
- package/dist/components/Formik/FormikTextInputInset/FormikTextInputInset.d.ts +0 -12
- package/dist/components/Formik/FormikTextareaInput/FormikTextareaInput.d.ts +0 -12
- package/dist/components/Formik/FormikTextareaInputInset/FormikTextareaInputInset.d.ts +0 -12
- package/dist/components/Formik/FormikTimePicker/FormikTimePicker.d.ts +0 -12
- package/dist/components/Formik/FormikTimePickerNative/FormikTimePickerNative.d.ts +0 -12
- package/dist/components/Formik/FormikToggleGroup/FormikToggleGroup.d.ts +0 -20
- package/dist/components/Formik/FormikToggleGroupMulti/FormikToggleGroupMulti.d.ts +0 -18
- package/dist/components/Heading/Heading.constants.d.ts +0 -10
- package/dist/components/Heading/Heading.d.ts +0 -35
- package/dist/components/Heading/Heading.stories.d.ts +0 -9
- package/dist/components/HelpText/HelpText.d.ts +0 -12
- package/dist/components/Icon/Icon.d.ts +0 -22
- package/dist/components/Icon/Icon.stories.d.ts +0 -10
- package/dist/components/InputValidationMessage/InputValidationMessage.d.ts +0 -9
- package/dist/components/Modal/Modal.d.ts +0 -83
- package/dist/components/Modal/Modal.stories.d.ts +0 -13
- package/dist/components/Modal/components/ModalBody/ModalBody.d.ts +0 -4
- package/dist/components/Modal/components/ModalFooter/ModalFooter.d.ts +0 -4
- package/dist/components/Modal/components/ModalHeader/ModalHeader.d.ts +0 -21
- package/dist/components/Modal/components/index.d.ts +0 -4
- package/dist/components/Pagination/Pagination.d.ts +0 -51
- package/dist/components/Pagination/Pagination.stories.d.ts +0 -8
- package/dist/components/Pagination/Pagination.utilities.d.ts +0 -10
- package/dist/components/Popover/Popover.d.ts +0 -8
- package/dist/components/Popover/Popover.stories.d.ts +0 -7
- package/dist/components/RadioGroup/RadioGroup.d.ts +0 -75
- package/dist/components/RadioGroup/RadioGroup.stories.d.ts +0 -16
- package/dist/components/RadioGroup/RadioInput/RadioInput.d.ts +0 -57
- package/dist/components/RadioGroup/RadioInput/RadioInputIcon.d.ts +0 -27
- package/dist/components/RangeInput/RangeInput.d.ts +0 -29
- package/dist/components/RangeInput/RangeInput.stories.d.ts +0 -7
- package/dist/components/ResponsiveProvider/ResponsiveProvider.d.ts +0 -17
- package/dist/components/ResponsiveProvider/ResponsiveProvider.stories.d.ts +0 -7
- package/dist/components/SelectInput/SelectInput.d.ts +0 -148
- package/dist/components/SelectInput/SelectInput.stories.d.ts +0 -24
- package/dist/components/SelectInputInset/SelectInputInset.d.ts +0 -92
- package/dist/components/SelectInputInset/SelectInputInset.stories.d.ts +0 -12
- package/dist/components/SelectInputNative/SelectInputNative.d.ts +0 -45
- package/dist/components/SelectInputNative/SelectInputNative.stories.d.ts +0 -19
- package/dist/components/Sidebar/Sidebar.d.ts +0 -57
- package/dist/components/Sidebar/Sidebar.stories.d.ts +0 -9
- package/dist/components/Spinner/Spinner.d.ts +0 -12
- package/dist/components/Spinner/Spinner.stories.d.ts +0 -8
- package/dist/components/Switch/Switch.d.ts +0 -64
- package/dist/components/Switch/Switch.stories.d.ts +0 -12
- package/dist/components/Table/Table.d.ts +0 -86
- package/dist/components/Table/Table.stories.d.ts +0 -31
- package/dist/components/Table/TableBody/TableBody.d.ts +0 -52
- package/dist/components/Table/TableBody/TableBodyCell/TableBodyCell.d.ts +0 -45
- package/dist/components/Table/TableHead/TableHead.d.ts +0 -46
- package/dist/components/Table/TableHead/TableHeaderCell/TableHeaderCell.d.ts +0 -65
- package/dist/components/Table/common/TableRow/TableRow.d.ts +0 -67
- package/dist/components/TextInput/TextInput.d.ts +0 -106
- package/dist/components/TextInput/TextInput.stories.d.ts +0 -19
- package/dist/components/TextInputInset/TextInputInset.d.ts +0 -102
- package/dist/components/TextInputInset/TextInputInset.stories.d.ts +0 -13
- package/dist/components/TextareaInput/TextareaInput.d.ts +0 -97
- package/dist/components/TextareaInput/TextareaInput.stories.d.ts +0 -23
- package/dist/components/TextareaInputInset/TextareaInputInset.d.ts +0 -105
- package/dist/components/TextareaInputInset/TextareaInputInset.stories.d.ts +0 -12
- package/dist/components/ThemeProvider/ThemeProvider.d.ts +0 -15
- package/dist/components/ThemeProvider/ThemeProvider.stories.d.ts +0 -6
- package/dist/components/TimePicker/TimePicker.d.ts +0 -35
- package/dist/components/TimePicker/TimePicker.stories.d.ts +0 -12
- package/dist/components/TimePickerNative/TimePickerNative.d.ts +0 -39
- package/dist/components/TimePickerNative/TimePickerNative.stories.d.ts +0 -11
- package/dist/components/Toast/Toast.store.d.ts +0 -36
- package/dist/components/Toast/Toast.stories.d.ts +0 -14
- package/dist/components/Toast/Toast.types.d.ts +0 -75
- package/dist/components/Toast/ToastContainer.d.ts +0 -43
- package/dist/components/Toast/ToastNotification.d.ts +0 -28
- package/dist/components/Toast/index.d.ts +0 -4
- package/dist/components/Toast/toast.d.ts +0 -20
- package/dist/components/Toast/useToasts.d.ts +0 -14
- package/dist/components/Toggle/Toggle.d.ts +0 -7
- package/dist/components/Toggle/Toggle.stories.d.ts +0 -11
- package/dist/components/ToggleGroup/ToggleGroup.d.ts +0 -19
- package/dist/components/ToggleGroup/ToggleGroup.stories.d.ts +0 -12
- package/dist/components/Tooltip/Tooltip.d.ts +0 -8
- package/dist/components/Tooltip/Tooltip.stories.d.ts +0 -8
- package/dist/constants/keyCodes.d.ts +0 -2
- package/dist/css/index.css +0 -36
- package/dist/hooks/index.d.ts +0 -6
- package/dist/hooks/useBreakpoint/useBreakpoint.d.ts +0 -9
- package/dist/hooks/useBreakpoint/useBreakpoint.stories.d.ts +0 -6
- package/dist/hooks/useIsMobile/useIsMobile.d.ts +0 -1
- package/dist/hooks/useIsMobile/useIsMobile.stories.d.ts +0 -6
- package/dist/hooks/useIsomorphicLayoutEffect/useIsomorphicLayoutEffect.d.ts +0 -2
- package/dist/hooks/useOpenClose/useOpenClose.d.ts +0 -39
- package/dist/hooks/useOpenClose/useOpenClose.stories.d.ts +0 -6
- package/dist/hooks/useTheme/useTheme.d.ts +0 -5
- package/dist/hooks/useTheme/useTheme.stories.d.ts +0 -6
- package/dist/hooks/useWindowSize/useWindowSize.d.ts +0 -7
- package/dist/hooks/useWindowSize/useWindowSize.stories.d.ts +0 -6
- package/dist/lib/cssShorthandToClasses.d.ts +0 -4
- package/dist/lib/doesStringIncludeCssUnit.d.ts +0 -1
- package/dist/lib/generateResponsiveClasses.d.ts +0 -2
- package/dist/lib/getAutoCompleteValue.d.ts +0 -1
- package/dist/lib/getColumnKeys.d.ts +0 -3
- package/dist/lib/getDimensionCss.d.ts +0 -12
- package/dist/lib/getElementType.d.ts +0 -14
- package/dist/lib/getFlexCss.d.ts +0 -9
- package/dist/lib/index.d.ts +0 -15
- package/dist/lib/isFunction.d.ts +0 -3
- package/dist/lib/mergeRefs.d.ts +0 -2
- package/dist/lib/prefersReducedMotion.d.ts +0 -1
- package/dist/lib/react-children-utilities/filter.d.ts +0 -3
- package/dist/lib/react-children-utilities/index.d.ts +0 -1
- package/dist/lib/reactRouterClickHandler.d.ts +0 -12
- package/dist/lib/resolveValue.d.ts +0 -3
- package/dist/lib/tokens.d.ts +0 -22
- package/dist/modes.d.ts +0 -8
- package/dist/types/index.d.ts +0 -103
- package/dist/types/lib.types.d.ts +0 -3
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import React from 'react';
|
|
2
|
-
import { fireEvent, render, screen } from '@testing-library/react';
|
|
2
|
+
import { fireEvent, render, screen, waitFor } from '@testing-library/react';
|
|
3
|
+
import userEvent from '@testing-library/user-event';
|
|
3
4
|
import {
|
|
4
5
|
Drawer,
|
|
5
6
|
DrawerTitle,
|
|
@@ -9,6 +10,7 @@ import {
|
|
|
9
10
|
DrawerProps,
|
|
10
11
|
DrawerProvider,
|
|
11
12
|
DrawerTrigger,
|
|
13
|
+
useDrawer,
|
|
12
14
|
} from './Drawer';
|
|
13
15
|
|
|
14
16
|
const renderDrawer = (props: Partial<DrawerProps> = {}) => {
|
|
@@ -32,82 +34,143 @@ const renderDrawer = (props: Partial<DrawerProps> = {}) => {
|
|
|
32
34
|
};
|
|
33
35
|
|
|
34
36
|
describe('Drawer', () => {
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
37
|
+
describe('Basic rendering', () => {
|
|
38
|
+
test('renders its children', () => {
|
|
39
|
+
renderDrawer();
|
|
40
|
+
fireEvent.click(screen.getByText('Open drawer'));
|
|
38
41
|
|
|
39
|
-
|
|
40
|
-
|
|
42
|
+
expect(screen.getByText('Drawer Title')).toBeInTheDocument();
|
|
43
|
+
});
|
|
41
44
|
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
45
|
+
test('it applies the aria label', () => {
|
|
46
|
+
renderDrawer();
|
|
47
|
+
fireEvent.click(screen.getByText('Open drawer'));
|
|
48
|
+
expect(screen.getByLabelText('Test Drawer')).toBeInTheDocument();
|
|
49
|
+
});
|
|
50
|
+
|
|
51
|
+
test('it opens and closes based on isOpen prop', () => {
|
|
52
|
+
const { rerender } = renderDrawer();
|
|
53
|
+
expect(screen.queryByLabelText('Test Drawer')).toBe(null);
|
|
47
54
|
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
55
|
+
rerender(
|
|
56
|
+
<Drawer isOpen={true} ariaLabel="Test Drawer" onDismiss={() => null} />
|
|
57
|
+
);
|
|
58
|
+
expect(screen.getByLabelText('Test Drawer')).toBeInTheDocument();
|
|
59
|
+
});
|
|
60
|
+
|
|
61
|
+
test('renders DrawerContent with correct data attribute', () => {
|
|
62
|
+
renderDrawer();
|
|
63
|
+
fireEvent.click(screen.getByText('Open drawer'));
|
|
51
64
|
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
);
|
|
55
|
-
expect(screen.getByLabelText('Test Drawer')).toBeInTheDocument();
|
|
65
|
+
expect(screen.getByTestId('drawer-content')).toBeInTheDocument();
|
|
66
|
+
});
|
|
56
67
|
});
|
|
57
68
|
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
// isOpen: true,
|
|
62
|
-
// ariaLabel: 'Test Drawer',
|
|
63
|
-
// onDismiss: mockedOnDismiss,
|
|
64
|
-
// });
|
|
69
|
+
describe('Focus management', () => {
|
|
70
|
+
test('it traps focus within the drawer', async () => {
|
|
71
|
+
const user = userEvent.setup();
|
|
65
72
|
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
73
|
+
// Render with an outside button to verify focus cannot escape
|
|
74
|
+
render(
|
|
75
|
+
<>
|
|
76
|
+
<button data-testid="outside-button">Outside</button>
|
|
77
|
+
<Drawer isOpen ariaLabel="Focus Trap Drawer">
|
|
78
|
+
<DrawerCloseButton />
|
|
79
|
+
<DrawerContent>
|
|
80
|
+
<button data-testid="inside-button">Inside</button>
|
|
81
|
+
</DrawerContent>
|
|
82
|
+
</Drawer>
|
|
83
|
+
</>
|
|
84
|
+
);
|
|
69
85
|
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
fireEvent.click(screen.getByText('Open drawer'));
|
|
86
|
+
const drawerContent = screen.getByTestId('drawer-content');
|
|
87
|
+
const insideButton = screen.getByTestId('inside-button');
|
|
73
88
|
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
89
|
+
// Focus the inside button
|
|
90
|
+
insideButton.focus();
|
|
91
|
+
expect(insideButton).toHaveFocus();
|
|
92
|
+
|
|
93
|
+
// Tab multiple times - focus should remain trapped within the drawer
|
|
94
|
+
await user.tab();
|
|
95
|
+
expect(drawerContent.contains(document.activeElement)).toBe(true);
|
|
78
96
|
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
isOpen: true,
|
|
97
|
+
await user.tab();
|
|
98
|
+
expect(drawerContent.contains(document.activeElement)).toBe(true);
|
|
82
99
|
|
|
83
|
-
|
|
100
|
+
await user.tab();
|
|
101
|
+
expect(drawerContent.contains(document.activeElement)).toBe(true);
|
|
84
102
|
});
|
|
85
|
-
fireEvent.click(screen.getByText('Open drawer'));
|
|
86
103
|
|
|
87
|
-
|
|
88
|
-
|
|
104
|
+
test('it does not trap focus when dangerouslyBypassFocusLock is true', async () => {
|
|
105
|
+
const user = userEvent.setup();
|
|
106
|
+
|
|
107
|
+
// Render with an outside button to verify focus can escape
|
|
108
|
+
render(
|
|
109
|
+
<>
|
|
110
|
+
<button data-testid="outside-button">Outside</button>
|
|
111
|
+
<Drawer
|
|
112
|
+
isOpen
|
|
113
|
+
ariaLabel="Bypass Focus Lock Drawer"
|
|
114
|
+
dangerouslyBypassFocusLock
|
|
115
|
+
>
|
|
116
|
+
<DrawerCloseButton />
|
|
117
|
+
<DrawerContent>
|
|
118
|
+
<button data-testid="inside-button">Inside</button>
|
|
119
|
+
</DrawerContent>
|
|
120
|
+
</Drawer>
|
|
121
|
+
</>
|
|
122
|
+
);
|
|
89
123
|
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
isOpen: true,
|
|
124
|
+
const outsideButton = screen.getByTestId('outside-button');
|
|
125
|
+
const insideButton = screen.getByTestId('inside-button');
|
|
93
126
|
|
|
94
|
-
|
|
127
|
+
// Focus the inside button
|
|
128
|
+
insideButton.focus();
|
|
129
|
+
expect(insideButton).toHaveFocus();
|
|
130
|
+
|
|
131
|
+
// Tab - with focus lock bypassed, focus should eventually escape to outside
|
|
132
|
+
await user.tab();
|
|
133
|
+
await user.tab();
|
|
134
|
+
await user.tab();
|
|
135
|
+
|
|
136
|
+
// Focus should be able to reach the outside button (not trapped inside)
|
|
137
|
+
outsideButton.focus();
|
|
138
|
+
expect(outsideButton).toHaveFocus();
|
|
95
139
|
});
|
|
96
|
-
|
|
140
|
+
});
|
|
141
|
+
|
|
142
|
+
describe('Scroll lock', () => {
|
|
143
|
+
test('it allows scrolling when dangerouslyBypassScrollLock is true', () => {
|
|
144
|
+
renderDrawer({
|
|
145
|
+
isOpen: true,
|
|
146
|
+
dangerouslyBypassScrollLock: true,
|
|
147
|
+
});
|
|
148
|
+
fireEvent.click(screen.getByText('Open drawer'));
|
|
97
149
|
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
expect(closeButton).toHaveFocus();
|
|
150
|
+
expect(document.body).not.toHaveStyle('overflow: hidden');
|
|
151
|
+
});
|
|
101
152
|
});
|
|
102
153
|
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
154
|
+
describe('Width customization', () => {
|
|
155
|
+
test('it renders with custom width', () => {
|
|
156
|
+
renderDrawer({
|
|
157
|
+
isOpen: true,
|
|
158
|
+
width: '500px',
|
|
159
|
+
});
|
|
160
|
+
fireEvent.click(screen.getByText('Open drawer'));
|
|
161
|
+
|
|
162
|
+
expect(screen.getByRole('dialog')).toHaveStyle('--drawer-width: 500px');
|
|
107
163
|
});
|
|
108
|
-
fireEvent.click(screen.getByText('Open drawer'));
|
|
109
164
|
|
|
110
|
-
|
|
165
|
+
test('it renders with token-based width', () => {
|
|
166
|
+
renderDrawer({
|
|
167
|
+
isOpen: true,
|
|
168
|
+
width: '300px',
|
|
169
|
+
});
|
|
170
|
+
fireEvent.click(screen.getByText('Open drawer'));
|
|
171
|
+
|
|
172
|
+
expect(screen.getByRole('dialog')).toHaveStyle('--drawer-width: 300px');
|
|
173
|
+
});
|
|
111
174
|
});
|
|
112
175
|
|
|
113
176
|
describe('Uncontrolled Drawer', () => {
|
|
@@ -122,6 +185,19 @@ describe('Drawer', () => {
|
|
|
122
185
|
fireEvent.click(screen.getByLabelText('close'));
|
|
123
186
|
expect(screen.queryByText('Uncontrolled Drawer')).toBe(null);
|
|
124
187
|
});
|
|
188
|
+
|
|
189
|
+
test('defaultIsOpen starts the drawer open', () => {
|
|
190
|
+
render(
|
|
191
|
+
<DrawerProvider defaultIsOpen>
|
|
192
|
+
<DrawerTrigger>Toggle</DrawerTrigger>
|
|
193
|
+
<Drawer ariaLabel="Default Open Drawer">
|
|
194
|
+
<DrawerContent>Default Open Content</DrawerContent>
|
|
195
|
+
</Drawer>
|
|
196
|
+
</DrawerProvider>
|
|
197
|
+
);
|
|
198
|
+
|
|
199
|
+
expect(screen.getByText('Default Open Content')).toBeInTheDocument();
|
|
200
|
+
});
|
|
125
201
|
});
|
|
126
202
|
|
|
127
203
|
describe('Controlled Drawer', () => {
|
|
@@ -157,4 +233,366 @@ describe('Drawer', () => {
|
|
|
157
233
|
expect(screen.queryByText('Controlled Drawer')).toBe(null);
|
|
158
234
|
});
|
|
159
235
|
});
|
|
236
|
+
|
|
237
|
+
describe('Placement', () => {
|
|
238
|
+
test('renders with left placement', () => {
|
|
239
|
+
render(
|
|
240
|
+
<Drawer isOpen ariaLabel="Left Drawer" placement="left">
|
|
241
|
+
<DrawerContent>Left Content</DrawerContent>
|
|
242
|
+
</Drawer>
|
|
243
|
+
);
|
|
244
|
+
|
|
245
|
+
expect(screen.getByText('Left Content')).toBeInTheDocument();
|
|
246
|
+
});
|
|
247
|
+
|
|
248
|
+
test('renders with right placement (default)', () => {
|
|
249
|
+
render(
|
|
250
|
+
<Drawer isOpen ariaLabel="Right Drawer">
|
|
251
|
+
<DrawerContent>Right Content</DrawerContent>
|
|
252
|
+
</Drawer>
|
|
253
|
+
);
|
|
254
|
+
|
|
255
|
+
expect(screen.getByText('Right Content')).toBeInTheDocument();
|
|
256
|
+
});
|
|
257
|
+
|
|
258
|
+
test('renders with top placement', () => {
|
|
259
|
+
render(
|
|
260
|
+
<Drawer isOpen ariaLabel="Top Drawer" placement="top">
|
|
261
|
+
<DrawerContent>Top Content</DrawerContent>
|
|
262
|
+
</Drawer>
|
|
263
|
+
);
|
|
264
|
+
|
|
265
|
+
expect(screen.getByText('Top Content')).toBeInTheDocument();
|
|
266
|
+
});
|
|
267
|
+
|
|
268
|
+
test('renders with bottom placement', () => {
|
|
269
|
+
render(
|
|
270
|
+
<Drawer isOpen ariaLabel="Bottom Drawer" placement="bottom">
|
|
271
|
+
<DrawerContent>Bottom Content</DrawerContent>
|
|
272
|
+
</Drawer>
|
|
273
|
+
);
|
|
274
|
+
|
|
275
|
+
expect(screen.getByText('Bottom Content')).toBeInTheDocument();
|
|
276
|
+
});
|
|
277
|
+
});
|
|
278
|
+
|
|
279
|
+
describe('DrawerProvider', () => {
|
|
280
|
+
test('provides context to children', () => {
|
|
281
|
+
render(
|
|
282
|
+
<DrawerProvider>
|
|
283
|
+
<DrawerTrigger>Trigger</DrawerTrigger>
|
|
284
|
+
<Drawer ariaLabel="Context Drawer">
|
|
285
|
+
<DrawerContent>Context Content</DrawerContent>
|
|
286
|
+
</Drawer>
|
|
287
|
+
</DrawerProvider>
|
|
288
|
+
);
|
|
289
|
+
|
|
290
|
+
fireEvent.click(screen.getByText('Trigger'));
|
|
291
|
+
expect(screen.getByText('Context Content')).toBeInTheDocument();
|
|
292
|
+
});
|
|
293
|
+
|
|
294
|
+
test('controlled DrawerProvider with open prop', () => {
|
|
295
|
+
const handleOpenChange = jest.fn();
|
|
296
|
+
render(
|
|
297
|
+
<DrawerProvider open={true} onOpenChange={handleOpenChange}>
|
|
298
|
+
<DrawerTrigger>Trigger</DrawerTrigger>
|
|
299
|
+
<Drawer ariaLabel="Controlled Provider Drawer">
|
|
300
|
+
<DrawerContent>Controlled Content</DrawerContent>
|
|
301
|
+
</Drawer>
|
|
302
|
+
</DrawerProvider>
|
|
303
|
+
);
|
|
304
|
+
|
|
305
|
+
expect(screen.getByText('Controlled Content')).toBeInTheDocument();
|
|
306
|
+
});
|
|
307
|
+
|
|
308
|
+
test('applies custom className to provider container', () => {
|
|
309
|
+
const { container } = render(
|
|
310
|
+
<DrawerProvider className="custom-container">
|
|
311
|
+
<DrawerTrigger>Trigger</DrawerTrigger>
|
|
312
|
+
<Drawer ariaLabel="Custom Class Drawer">
|
|
313
|
+
<DrawerContent>Content</DrawerContent>
|
|
314
|
+
</Drawer>
|
|
315
|
+
</DrawerProvider>
|
|
316
|
+
);
|
|
317
|
+
|
|
318
|
+
expect(container.querySelector('.custom-container')).toBeInTheDocument();
|
|
319
|
+
});
|
|
320
|
+
|
|
321
|
+
test('adds drawer-open class when open', () => {
|
|
322
|
+
const { container } = render(
|
|
323
|
+
<DrawerProvider defaultIsOpen>
|
|
324
|
+
<DrawerTrigger>Trigger</DrawerTrigger>
|
|
325
|
+
<Drawer ariaLabel="Open Drawer">
|
|
326
|
+
<DrawerContent>Content</DrawerContent>
|
|
327
|
+
</Drawer>
|
|
328
|
+
</DrawerProvider>
|
|
329
|
+
);
|
|
330
|
+
|
|
331
|
+
expect(container.querySelector('.drawer-open')).toBeInTheDocument();
|
|
332
|
+
});
|
|
333
|
+
});
|
|
334
|
+
|
|
335
|
+
describe('DrawerTrigger', () => {
|
|
336
|
+
test('sets correct ARIA attributes', () => {
|
|
337
|
+
render(
|
|
338
|
+
<DrawerProvider>
|
|
339
|
+
<DrawerTrigger>Open</DrawerTrigger>
|
|
340
|
+
<Drawer ariaLabel="ARIA Drawer">
|
|
341
|
+
<DrawerContent>Content</DrawerContent>
|
|
342
|
+
</Drawer>
|
|
343
|
+
</DrawerProvider>
|
|
344
|
+
);
|
|
345
|
+
|
|
346
|
+
const trigger = screen.getByText('Open');
|
|
347
|
+
expect(trigger).toHaveAttribute('aria-haspopup', 'dialog');
|
|
348
|
+
expect(trigger).toHaveAttribute('data-drawer', 'trigger');
|
|
349
|
+
});
|
|
350
|
+
|
|
351
|
+
test('supports asChild prop', () => {
|
|
352
|
+
render(
|
|
353
|
+
<DrawerProvider>
|
|
354
|
+
<DrawerTrigger asChild>
|
|
355
|
+
<span role="button">Custom Trigger</span>
|
|
356
|
+
</DrawerTrigger>
|
|
357
|
+
<Drawer ariaLabel="AsChild Drawer">
|
|
358
|
+
<DrawerContent>Content</DrawerContent>
|
|
359
|
+
</Drawer>
|
|
360
|
+
</DrawerProvider>
|
|
361
|
+
);
|
|
362
|
+
|
|
363
|
+
fireEvent.click(screen.getByText('Custom Trigger'));
|
|
364
|
+
expect(screen.getByText('Content')).toBeInTheDocument();
|
|
365
|
+
});
|
|
366
|
+
|
|
367
|
+
test('calls custom onClick handler', () => {
|
|
368
|
+
const handleClick = jest.fn();
|
|
369
|
+
render(
|
|
370
|
+
<DrawerProvider>
|
|
371
|
+
<DrawerTrigger onClick={handleClick}>Click Me</DrawerTrigger>
|
|
372
|
+
<Drawer ariaLabel="Click Drawer">
|
|
373
|
+
<DrawerContent>Content</DrawerContent>
|
|
374
|
+
</Drawer>
|
|
375
|
+
</DrawerProvider>
|
|
376
|
+
);
|
|
377
|
+
|
|
378
|
+
fireEvent.click(screen.getByText('Click Me'));
|
|
379
|
+
expect(handleClick).toHaveBeenCalled();
|
|
380
|
+
});
|
|
381
|
+
});
|
|
382
|
+
|
|
383
|
+
describe('DrawerCloseButton', () => {
|
|
384
|
+
test('renders close button with correct aria-label', () => {
|
|
385
|
+
renderDrawer();
|
|
386
|
+
fireEvent.click(screen.getByText('Open drawer'));
|
|
387
|
+
|
|
388
|
+
expect(screen.getByLabelText('close')).toBeInTheDocument();
|
|
389
|
+
});
|
|
390
|
+
|
|
391
|
+
test('has data-drawer="close" attribute', () => {
|
|
392
|
+
renderDrawer();
|
|
393
|
+
fireEvent.click(screen.getByText('Open drawer'));
|
|
394
|
+
|
|
395
|
+
expect(screen.getByLabelText('close')).toHaveAttribute(
|
|
396
|
+
'data-drawer',
|
|
397
|
+
'close'
|
|
398
|
+
);
|
|
399
|
+
});
|
|
400
|
+
|
|
401
|
+
test('calls custom onClose in standalone mode', () => {
|
|
402
|
+
const handleClose = jest.fn();
|
|
403
|
+
render(
|
|
404
|
+
<Drawer isOpen ariaLabel="Close Test Drawer">
|
|
405
|
+
<DrawerHeader>
|
|
406
|
+
<DrawerCloseButton onClose={handleClose} />
|
|
407
|
+
</DrawerHeader>
|
|
408
|
+
</Drawer>
|
|
409
|
+
);
|
|
410
|
+
|
|
411
|
+
fireEvent.click(screen.getByLabelText('close'));
|
|
412
|
+
expect(handleClose).toHaveBeenCalled();
|
|
413
|
+
});
|
|
414
|
+
});
|
|
415
|
+
|
|
416
|
+
describe('DrawerHeader', () => {
|
|
417
|
+
test('renders with correct data attribute', () => {
|
|
418
|
+
render(
|
|
419
|
+
<Drawer isOpen ariaLabel="Header Test Drawer">
|
|
420
|
+
<DrawerHeader data-testid="header">Header Content</DrawerHeader>
|
|
421
|
+
</Drawer>
|
|
422
|
+
);
|
|
423
|
+
|
|
424
|
+
expect(screen.getByTestId('header')).toHaveAttribute(
|
|
425
|
+
'data-drawer',
|
|
426
|
+
'header'
|
|
427
|
+
);
|
|
428
|
+
});
|
|
429
|
+
|
|
430
|
+
test('applies custom className', () => {
|
|
431
|
+
render(
|
|
432
|
+
<Drawer isOpen ariaLabel="Header Class Drawer">
|
|
433
|
+
<DrawerHeader className="custom-header">Header</DrawerHeader>
|
|
434
|
+
</Drawer>
|
|
435
|
+
);
|
|
436
|
+
|
|
437
|
+
expect(
|
|
438
|
+
screen.getByText('Header').closest('[data-drawer="header"]')
|
|
439
|
+
).toHaveClass('custom-header');
|
|
440
|
+
});
|
|
441
|
+
});
|
|
442
|
+
|
|
443
|
+
describe('DrawerTitle', () => {
|
|
444
|
+
test('renders with correct data attribute', () => {
|
|
445
|
+
render(
|
|
446
|
+
<Drawer isOpen ariaLabel="Title Test Drawer">
|
|
447
|
+
<DrawerHeader>
|
|
448
|
+
<DrawerTitle data-testid="title">Title</DrawerTitle>
|
|
449
|
+
</DrawerHeader>
|
|
450
|
+
</Drawer>
|
|
451
|
+
);
|
|
452
|
+
|
|
453
|
+
expect(screen.getByTestId('title')).toHaveAttribute(
|
|
454
|
+
'data-drawer',
|
|
455
|
+
'title'
|
|
456
|
+
);
|
|
457
|
+
});
|
|
458
|
+
});
|
|
459
|
+
|
|
460
|
+
describe('DrawerContent', () => {
|
|
461
|
+
test('renders with correct data attribute', () => {
|
|
462
|
+
render(
|
|
463
|
+
<Drawer isOpen ariaLabel="Content Test Drawer">
|
|
464
|
+
<DrawerContent data-testid="content">Content</DrawerContent>
|
|
465
|
+
</Drawer>
|
|
466
|
+
);
|
|
467
|
+
|
|
468
|
+
expect(screen.getByTestId('content')).toHaveAttribute(
|
|
469
|
+
'data-drawer',
|
|
470
|
+
'content'
|
|
471
|
+
);
|
|
472
|
+
});
|
|
473
|
+
});
|
|
474
|
+
|
|
475
|
+
describe('Overlay behavior', () => {
|
|
476
|
+
test('closeOnOverlayClick calls onDismiss when overlay is clicked', async () => {
|
|
477
|
+
const handleDismiss = jest.fn();
|
|
478
|
+
const { baseElement } = render(
|
|
479
|
+
<Drawer
|
|
480
|
+
isOpen
|
|
481
|
+
ariaLabel="Overlay Click Drawer"
|
|
482
|
+
onDismiss={handleDismiss}
|
|
483
|
+
>
|
|
484
|
+
<DrawerContent>Content</DrawerContent>
|
|
485
|
+
</Drawer>
|
|
486
|
+
);
|
|
487
|
+
|
|
488
|
+
expect(screen.getByText('Content')).toBeInTheDocument();
|
|
489
|
+
|
|
490
|
+
// Find the overlay element and click it
|
|
491
|
+
const overlay = baseElement.querySelector('.ReactModal__Overlay');
|
|
492
|
+
expect(overlay).toBeInTheDocument();
|
|
493
|
+
fireEvent.click(overlay!);
|
|
494
|
+
|
|
495
|
+
await waitFor(() => {
|
|
496
|
+
expect(handleDismiss).toHaveBeenCalledTimes(1);
|
|
497
|
+
});
|
|
498
|
+
});
|
|
499
|
+
|
|
500
|
+
test('closeOnOverlayClick=false prevents onDismiss when overlay is clicked', async () => {
|
|
501
|
+
const handleDismiss = jest.fn();
|
|
502
|
+
const { baseElement } = render(
|
|
503
|
+
<Drawer
|
|
504
|
+
isOpen
|
|
505
|
+
ariaLabel="Overlay Click Drawer"
|
|
506
|
+
onDismiss={handleDismiss}
|
|
507
|
+
closeOnOverlayClick={false}
|
|
508
|
+
>
|
|
509
|
+
<DrawerContent>Content</DrawerContent>
|
|
510
|
+
</Drawer>
|
|
511
|
+
);
|
|
512
|
+
|
|
513
|
+
expect(screen.getByText('Content')).toBeInTheDocument();
|
|
514
|
+
|
|
515
|
+
// Find the overlay element and click it
|
|
516
|
+
const overlay = baseElement.querySelector('.ReactModal__Overlay');
|
|
517
|
+
expect(overlay).toBeInTheDocument();
|
|
518
|
+
fireEvent.click(overlay!);
|
|
519
|
+
|
|
520
|
+
// onDismiss should NOT be called when closeOnOverlayClick is false
|
|
521
|
+
expect(handleDismiss).not.toHaveBeenCalled();
|
|
522
|
+
});
|
|
523
|
+
});
|
|
524
|
+
|
|
525
|
+
describe('Accessibility', () => {
|
|
526
|
+
test('applies ariaLabelledBy when provided', () => {
|
|
527
|
+
render(
|
|
528
|
+
<Drawer isOpen ariaLabelledBy="drawer-title">
|
|
529
|
+
<DrawerHeader>
|
|
530
|
+
<DrawerTitle id="drawer-title">Labelled Drawer</DrawerTitle>
|
|
531
|
+
</DrawerHeader>
|
|
532
|
+
<DrawerContent>Content</DrawerContent>
|
|
533
|
+
</Drawer>
|
|
534
|
+
);
|
|
535
|
+
|
|
536
|
+
const drawer = screen.getByTestId('drawer-content');
|
|
537
|
+
expect(drawer).toHaveAttribute('aria-labelledby', 'drawer-title');
|
|
538
|
+
});
|
|
539
|
+
|
|
540
|
+
test('allowPinchZoom prop is passed correctly', () => {
|
|
541
|
+
render(
|
|
542
|
+
<Drawer isOpen ariaLabel="Pinch Zoom Drawer" allowPinchZoom>
|
|
543
|
+
<DrawerContent>Pinch Content</DrawerContent>
|
|
544
|
+
</Drawer>
|
|
545
|
+
);
|
|
546
|
+
|
|
547
|
+
expect(screen.getByText('Pinch Content')).toBeInTheDocument();
|
|
548
|
+
});
|
|
549
|
+
});
|
|
550
|
+
|
|
551
|
+
describe('useDrawer hook', () => {
|
|
552
|
+
test('throws error when used outside DrawerProvider', () => {
|
|
553
|
+
const TestComponent = () => {
|
|
554
|
+
useDrawer();
|
|
555
|
+
return null;
|
|
556
|
+
};
|
|
557
|
+
|
|
558
|
+
expect(() => render(<TestComponent />)).toThrow(
|
|
559
|
+
'useDrawer must be used within a DrawerProvider.'
|
|
560
|
+
);
|
|
561
|
+
});
|
|
562
|
+
|
|
563
|
+
test('provides open state and toggle function', () => {
|
|
564
|
+
const TestComponent = () => {
|
|
565
|
+
const { open, toggleDrawer } = useDrawer();
|
|
566
|
+
return (
|
|
567
|
+
<div>
|
|
568
|
+
<span data-testid="state">{open ? 'open' : 'closed'}</span>
|
|
569
|
+
<button onClick={toggleDrawer}>Toggle</button>
|
|
570
|
+
</div>
|
|
571
|
+
);
|
|
572
|
+
};
|
|
573
|
+
|
|
574
|
+
render(
|
|
575
|
+
<DrawerProvider>
|
|
576
|
+
<TestComponent />
|
|
577
|
+
</DrawerProvider>
|
|
578
|
+
);
|
|
579
|
+
|
|
580
|
+
expect(screen.getByTestId('state')).toHaveTextContent('closed');
|
|
581
|
+
fireEvent.click(screen.getByText('Toggle'));
|
|
582
|
+
expect(screen.getByTestId('state')).toHaveTextContent('open');
|
|
583
|
+
});
|
|
584
|
+
});
|
|
585
|
+
|
|
586
|
+
describe('Ref forwarding', () => {
|
|
587
|
+
test('forwards ref to DrawerProvider container', () => {
|
|
588
|
+
const ref = React.createRef<HTMLDivElement>();
|
|
589
|
+
render(
|
|
590
|
+
<DrawerProvider ref={ref}>
|
|
591
|
+
<DrawerTrigger>Trigger</DrawerTrigger>
|
|
592
|
+
</DrawerProvider>
|
|
593
|
+
);
|
|
594
|
+
|
|
595
|
+
expect(ref.current).toBeInstanceOf(HTMLDivElement);
|
|
596
|
+
});
|
|
597
|
+
});
|
|
160
598
|
});
|
|
@@ -130,7 +130,7 @@ const DrawerTrigger = React.forwardRef<
|
|
|
130
130
|
/>
|
|
131
131
|
);
|
|
132
132
|
});
|
|
133
|
-
DrawerTrigger.displayName = '
|
|
133
|
+
DrawerTrigger.displayName = 'DrawerTrigger';
|
|
134
134
|
|
|
135
135
|
export type DrawerPlacementType = 'left' | 'right' | 'top' | 'bottom';
|
|
136
136
|
export interface DrawerProps {
|
|
@@ -331,6 +331,8 @@ const Drawer: React.FC<DrawerProps> = forwardRef<HTMLDivElement, DrawerProps>(
|
|
|
331
331
|
}
|
|
332
332
|
);
|
|
333
333
|
|
|
334
|
+
Drawer.displayName = 'Drawer';
|
|
335
|
+
|
|
334
336
|
const DrawerHeader = React.forwardRef<HTMLDivElement, BoxProps>(
|
|
335
337
|
({ className, ...props }, ref) => {
|
|
336
338
|
return (
|
|
@@ -363,6 +365,8 @@ const DrawerTitle = React.forwardRef<HTMLDivElement, BoxProps>(
|
|
|
363
365
|
}
|
|
364
366
|
);
|
|
365
367
|
|
|
368
|
+
DrawerTitle.displayName = 'DrawerTitle';
|
|
369
|
+
|
|
366
370
|
const DrawerCloseButton = forwardRef<
|
|
367
371
|
HTMLButtonElement,
|
|
368
372
|
ButtonProps & { onClose?: () => void }
|
|
@@ -417,6 +421,8 @@ const DrawerContent = React.forwardRef<HTMLDivElement, BoxProps>(
|
|
|
417
421
|
}
|
|
418
422
|
);
|
|
419
423
|
|
|
424
|
+
DrawerContent.displayName = 'DrawerContent';
|
|
425
|
+
|
|
420
426
|
export {
|
|
421
427
|
Drawer,
|
|
422
428
|
DrawerContent,
|