@hyphen/hyphen-components 7.3.2 → 7.3.4

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.
Files changed (221) hide show
  1. package/dist/css/utilities.css +1 -1
  2. package/dist/css/variables.css +18 -28
  3. package/dist/hyphen-components.cjs.development.js +5873 -5019
  4. package/dist/hyphen-components.cjs.development.js.map +1 -1
  5. package/dist/hyphen-components.cjs.production.min.js +18 -2
  6. package/dist/hyphen-components.cjs.production.min.js.map +1 -1
  7. package/dist/hyphen-components.esm.js +5731 -4844
  8. package/dist/hyphen-components.esm.js.map +1 -1
  9. package/dist/index.d.ts +2799 -57
  10. package/dist/index.js +0 -1
  11. package/package.json +18 -19
  12. package/src/components/Badge/Badge.module.scss +6 -0
  13. package/src/components/Badge/Badge.stories.tsx +1 -0
  14. package/src/components/Badge/Badge.test.tsx +3 -2
  15. package/src/components/Badge/Badge.tsx +5 -3
  16. package/src/components/Box/Box.tsx +5 -2
  17. package/src/components/Button/Button.module.scss +1 -1
  18. package/src/components/Button/Button.test.tsx +2 -2
  19. package/src/components/Calendar/Calendar.test.tsx +262 -0
  20. package/src/components/Card/Card.tsx +2 -0
  21. package/src/components/CheckboxInput/components/Checkbox.module.scss +1 -1
  22. package/src/components/CheckboxInput/components/Checkbox.tsx +2 -0
  23. package/src/components/Details/Details.module.scss +2 -2
  24. package/src/components/Details/Details.tsx +2 -0
  25. package/src/components/Drawer/Drawer.stories.tsx +1 -1
  26. package/src/components/Drawer/Drawer.test.tsx +494 -56
  27. package/src/components/Drawer/Drawer.tsx +7 -1
  28. package/src/components/DropdownMenu/DropdownMenu.test.tsx +532 -12
  29. package/src/components/FormControl/FormControl.tsx +2 -0
  30. package/src/components/Formik/Formik.stories.tsx +30 -7
  31. package/src/components/Formik/FormikSelectInput/FormikSelectInput.tsx +6 -5
  32. package/src/components/Formik/FormikToggleGroup/FormikToggleGroup.tsx +1 -1
  33. package/src/components/HelpText/HelpText.tsx +2 -0
  34. package/src/components/Icon/Icon.stories.tsx +1 -1
  35. package/src/components/Icon/Icon.tsx +2 -0
  36. package/src/components/Modal/Modal.test.tsx +630 -81
  37. package/src/components/Modal/Modal.tsx +2 -0
  38. package/src/components/Modal/components/ModalFooter/ModalFooter.test.tsx +2 -2
  39. package/src/components/Popover/Popover.tsx +2 -0
  40. package/src/components/RadioGroup/RadioInput/RadioInput.tsx +2 -0
  41. package/src/components/SelectInput/SelectInput.stories.tsx +22 -22
  42. package/src/components/SelectInput/SelectInput.tsx +13 -9
  43. package/src/components/SelectInputInset/SelectInputInset.tsx +2 -0
  44. package/src/components/Sidebar/Sidebar.module.scss +4 -0
  45. package/src/components/Sidebar/Sidebar.stories.tsx +8 -4
  46. package/src/components/Sidebar/Sidebar.test.tsx +7 -4
  47. package/src/components/Sidebar/Sidebar.tsx +7 -4
  48. package/src/components/Table/Table.stories.tsx +102 -52
  49. package/src/components/TextInput/TextInput.tsx +2 -0
  50. package/src/components/TextInputInset/TextInputInset.tsx +2 -0
  51. package/src/components/TextareaInputInset/TextareaInputInset.tsx +2 -0
  52. package/src/components/TimePickerNative/TimePickerNative.stories.tsx +0 -1
  53. package/src/components/Toast/Toast.store.ts +1 -1
  54. package/src/components/Toast/Toast.stories.tsx +3 -2
  55. package/src/components/Toast/Toast.test.tsx +8 -6
  56. package/src/components/Toggle/Toggle.tsx +2 -0
  57. package/src/components/ToggleGroup/ToggleGroup.tsx +2 -0
  58. package/src/docs/Colors.mdx +0 -13
  59. package/src/index.ts +2 -0
  60. package/src/lib/getColumnKeys.ts +3 -3
  61. package/src/lib/mergeRefs.ts +1 -1
  62. package/src/lib/tokens.ts +4 -4
  63. package/dist/components/Alert/Alert.constants.d.ts +0 -8
  64. package/dist/components/Alert/Alert.d.ts +0 -42
  65. package/dist/components/Alert/Alert.stories.d.ts +0 -12
  66. package/dist/components/Alert/Alert.types.d.ts +0 -7
  67. package/dist/components/AspectRatio/AspectRatio.d.ts +0 -3
  68. package/dist/components/AspectRatio/AspectRatio.stories.d.ts +0 -6
  69. package/dist/components/Badge/Badge.d.ts +0 -24
  70. package/dist/components/Badge/Badge.stories.d.ts +0 -8
  71. package/dist/components/Box/Box.d.ts +0 -247
  72. package/dist/components/Box/Box.stories.d.ts +0 -46
  73. package/dist/components/Button/Button.constants.d.ts +0 -3
  74. package/dist/components/Button/Button.d.ts +0 -53
  75. package/dist/components/Button/Button.stories.d.ts +0 -16
  76. package/dist/components/Calendar/Calendar.d.ts +0 -7
  77. package/dist/components/Calendar/Calendar.stories.d.ts +0 -12
  78. package/dist/components/Card/Card.d.ts +0 -17
  79. package/dist/components/Card/Card.stories.d.ts +0 -8
  80. package/dist/components/Card/components/CardFooter/CardFooter.d.ts +0 -13
  81. package/dist/components/Card/components/CardHeader/CardHeader.d.ts +0 -13
  82. package/dist/components/Card/components/CardSection/CardSection.d.ts +0 -46
  83. package/dist/components/Card/components/index.d.ts +0 -3
  84. package/dist/components/CheckboxInput/CheckboxInput.d.ts +0 -72
  85. package/dist/components/CheckboxInput/CheckboxInput.stories.d.ts +0 -18
  86. package/dist/components/CheckboxInput/components/Checkbox.d.ts +0 -71
  87. package/dist/components/CheckboxInput/components/CheckboxIcon.d.ts +0 -27
  88. package/dist/components/Collapsible/Collapsible.d.ts +0 -5
  89. package/dist/components/Collapsible/Collapsible.stories.d.ts +0 -9
  90. package/dist/components/Details/Details.d.ts +0 -15
  91. package/dist/components/Details/Details.stories.d.ts +0 -6
  92. package/dist/components/Details/DetailsSummary.d.ts +0 -7
  93. package/dist/components/Drawer/Drawer.d.ts +0 -105
  94. package/dist/components/Drawer/Drawer.stories.d.ts +0 -62
  95. package/dist/components/DropdownMenu/DropdownMenu.d.ts +0 -25
  96. package/dist/components/DropdownMenu/DropdownMenu.stories.d.ts +0 -9
  97. package/dist/components/FormControl/FormControl.d.ts +0 -38
  98. package/dist/components/FormLabel/FormLabel.d.ts +0 -41
  99. package/dist/components/FormLabel/FormLabel.stories.d.ts +0 -6
  100. package/dist/components/Formik/Formik.stories.d.ts +0 -18
  101. package/dist/components/Formik/FormikCheckboxInput/FormikCheckboxInput.d.ts +0 -12
  102. package/dist/components/Formik/FormikRadioGroup/FormikRadioGroup.d.ts +0 -12
  103. package/dist/components/Formik/FormikSelectInput/FormikSelectInput.d.ts +0 -13
  104. package/dist/components/Formik/FormikSelectInputInset/FormikSelectInputInset.d.ts +0 -12
  105. package/dist/components/Formik/FormikSelectInputNative/FormikSelectInputNative.d.ts +0 -12
  106. package/dist/components/Formik/FormikSwitch/FormikSwitch.d.ts +0 -12
  107. package/dist/components/Formik/FormikTextInput/FormikTextInput.d.ts +0 -12
  108. package/dist/components/Formik/FormikTextInputInset/FormikTextInputInset.d.ts +0 -12
  109. package/dist/components/Formik/FormikTextareaInput/FormikTextareaInput.d.ts +0 -12
  110. package/dist/components/Formik/FormikTextareaInputInset/FormikTextareaInputInset.d.ts +0 -12
  111. package/dist/components/Formik/FormikTimePicker/FormikTimePicker.d.ts +0 -12
  112. package/dist/components/Formik/FormikTimePickerNative/FormikTimePickerNative.d.ts +0 -12
  113. package/dist/components/Formik/FormikToggleGroup/FormikToggleGroup.d.ts +0 -20
  114. package/dist/components/Formik/FormikToggleGroupMulti/FormikToggleGroupMulti.d.ts +0 -18
  115. package/dist/components/Heading/Heading.constants.d.ts +0 -10
  116. package/dist/components/Heading/Heading.d.ts +0 -35
  117. package/dist/components/Heading/Heading.stories.d.ts +0 -9
  118. package/dist/components/HelpText/HelpText.d.ts +0 -12
  119. package/dist/components/Icon/Icon.d.ts +0 -22
  120. package/dist/components/Icon/Icon.stories.d.ts +0 -10
  121. package/dist/components/InputValidationMessage/InputValidationMessage.d.ts +0 -9
  122. package/dist/components/Modal/Modal.d.ts +0 -83
  123. package/dist/components/Modal/Modal.stories.d.ts +0 -13
  124. package/dist/components/Modal/components/ModalBody/ModalBody.d.ts +0 -4
  125. package/dist/components/Modal/components/ModalFooter/ModalFooter.d.ts +0 -4
  126. package/dist/components/Modal/components/ModalHeader/ModalHeader.d.ts +0 -21
  127. package/dist/components/Modal/components/index.d.ts +0 -4
  128. package/dist/components/Pagination/Pagination.d.ts +0 -51
  129. package/dist/components/Pagination/Pagination.stories.d.ts +0 -8
  130. package/dist/components/Pagination/Pagination.utilities.d.ts +0 -10
  131. package/dist/components/Popover/Popover.d.ts +0 -8
  132. package/dist/components/Popover/Popover.stories.d.ts +0 -7
  133. package/dist/components/RadioGroup/RadioGroup.d.ts +0 -75
  134. package/dist/components/RadioGroup/RadioGroup.stories.d.ts +0 -16
  135. package/dist/components/RadioGroup/RadioInput/RadioInput.d.ts +0 -57
  136. package/dist/components/RadioGroup/RadioInput/RadioInputIcon.d.ts +0 -27
  137. package/dist/components/RangeInput/RangeInput.d.ts +0 -29
  138. package/dist/components/RangeInput/RangeInput.stories.d.ts +0 -7
  139. package/dist/components/ResponsiveProvider/ResponsiveProvider.d.ts +0 -17
  140. package/dist/components/ResponsiveProvider/ResponsiveProvider.stories.d.ts +0 -7
  141. package/dist/components/SelectInput/SelectInput.d.ts +0 -148
  142. package/dist/components/SelectInput/SelectInput.stories.d.ts +0 -24
  143. package/dist/components/SelectInputInset/SelectInputInset.d.ts +0 -92
  144. package/dist/components/SelectInputInset/SelectInputInset.stories.d.ts +0 -12
  145. package/dist/components/SelectInputNative/SelectInputNative.d.ts +0 -45
  146. package/dist/components/SelectInputNative/SelectInputNative.stories.d.ts +0 -19
  147. package/dist/components/Sidebar/Sidebar.d.ts +0 -57
  148. package/dist/components/Sidebar/Sidebar.stories.d.ts +0 -9
  149. package/dist/components/Spinner/Spinner.d.ts +0 -12
  150. package/dist/components/Spinner/Spinner.stories.d.ts +0 -8
  151. package/dist/components/Switch/Switch.d.ts +0 -64
  152. package/dist/components/Switch/Switch.stories.d.ts +0 -12
  153. package/dist/components/Table/Table.d.ts +0 -86
  154. package/dist/components/Table/Table.stories.d.ts +0 -31
  155. package/dist/components/Table/TableBody/TableBody.d.ts +0 -52
  156. package/dist/components/Table/TableBody/TableBodyCell/TableBodyCell.d.ts +0 -45
  157. package/dist/components/Table/TableHead/TableHead.d.ts +0 -46
  158. package/dist/components/Table/TableHead/TableHeaderCell/TableHeaderCell.d.ts +0 -65
  159. package/dist/components/Table/common/TableRow/TableRow.d.ts +0 -67
  160. package/dist/components/TextInput/TextInput.d.ts +0 -106
  161. package/dist/components/TextInput/TextInput.stories.d.ts +0 -19
  162. package/dist/components/TextInputInset/TextInputInset.d.ts +0 -102
  163. package/dist/components/TextInputInset/TextInputInset.stories.d.ts +0 -13
  164. package/dist/components/TextareaInput/TextareaInput.d.ts +0 -97
  165. package/dist/components/TextareaInput/TextareaInput.stories.d.ts +0 -23
  166. package/dist/components/TextareaInputInset/TextareaInputInset.d.ts +0 -105
  167. package/dist/components/TextareaInputInset/TextareaInputInset.stories.d.ts +0 -12
  168. package/dist/components/ThemeProvider/ThemeProvider.d.ts +0 -15
  169. package/dist/components/ThemeProvider/ThemeProvider.stories.d.ts +0 -6
  170. package/dist/components/TimePicker/TimePicker.d.ts +0 -35
  171. package/dist/components/TimePicker/TimePicker.stories.d.ts +0 -12
  172. package/dist/components/TimePickerNative/TimePickerNative.d.ts +0 -39
  173. package/dist/components/TimePickerNative/TimePickerNative.stories.d.ts +0 -11
  174. package/dist/components/Toast/Toast.store.d.ts +0 -36
  175. package/dist/components/Toast/Toast.stories.d.ts +0 -14
  176. package/dist/components/Toast/Toast.types.d.ts +0 -75
  177. package/dist/components/Toast/ToastContainer.d.ts +0 -43
  178. package/dist/components/Toast/ToastNotification.d.ts +0 -28
  179. package/dist/components/Toast/index.d.ts +0 -4
  180. package/dist/components/Toast/toast.d.ts +0 -20
  181. package/dist/components/Toast/useToasts.d.ts +0 -14
  182. package/dist/components/Toggle/Toggle.d.ts +0 -7
  183. package/dist/components/Toggle/Toggle.stories.d.ts +0 -11
  184. package/dist/components/ToggleGroup/ToggleGroup.d.ts +0 -19
  185. package/dist/components/ToggleGroup/ToggleGroup.stories.d.ts +0 -12
  186. package/dist/components/Tooltip/Tooltip.d.ts +0 -8
  187. package/dist/components/Tooltip/Tooltip.stories.d.ts +0 -8
  188. package/dist/constants/keyCodes.d.ts +0 -2
  189. package/dist/css/index.css +0 -36
  190. package/dist/hooks/index.d.ts +0 -6
  191. package/dist/hooks/useBreakpoint/useBreakpoint.d.ts +0 -9
  192. package/dist/hooks/useBreakpoint/useBreakpoint.stories.d.ts +0 -6
  193. package/dist/hooks/useIsMobile/useIsMobile.d.ts +0 -1
  194. package/dist/hooks/useIsMobile/useIsMobile.stories.d.ts +0 -6
  195. package/dist/hooks/useIsomorphicLayoutEffect/useIsomorphicLayoutEffect.d.ts +0 -2
  196. package/dist/hooks/useOpenClose/useOpenClose.d.ts +0 -39
  197. package/dist/hooks/useOpenClose/useOpenClose.stories.d.ts +0 -6
  198. package/dist/hooks/useTheme/useTheme.d.ts +0 -5
  199. package/dist/hooks/useTheme/useTheme.stories.d.ts +0 -6
  200. package/dist/hooks/useWindowSize/useWindowSize.d.ts +0 -7
  201. package/dist/hooks/useWindowSize/useWindowSize.stories.d.ts +0 -6
  202. package/dist/lib/cssShorthandToClasses.d.ts +0 -4
  203. package/dist/lib/doesStringIncludeCssUnit.d.ts +0 -1
  204. package/dist/lib/generateResponsiveClasses.d.ts +0 -2
  205. package/dist/lib/getAutoCompleteValue.d.ts +0 -1
  206. package/dist/lib/getColumnKeys.d.ts +0 -3
  207. package/dist/lib/getDimensionCss.d.ts +0 -12
  208. package/dist/lib/getElementType.d.ts +0 -14
  209. package/dist/lib/getFlexCss.d.ts +0 -9
  210. package/dist/lib/index.d.ts +0 -15
  211. package/dist/lib/isFunction.d.ts +0 -3
  212. package/dist/lib/mergeRefs.d.ts +0 -2
  213. package/dist/lib/prefersReducedMotion.d.ts +0 -1
  214. package/dist/lib/react-children-utilities/filter.d.ts +0 -3
  215. package/dist/lib/react-children-utilities/index.d.ts +0 -1
  216. package/dist/lib/reactRouterClickHandler.d.ts +0 -12
  217. package/dist/lib/resolveValue.d.ts +0 -3
  218. package/dist/lib/tokens.d.ts +0 -22
  219. package/dist/modes.d.ts +0 -8
  220. package/dist/types/index.d.ts +0 -103
  221. package/dist/types/lib.types.d.ts +0 -3
@@ -1,23 +1,543 @@
1
- import { render, screen } from '@testing-library/react';
2
1
  import React from 'react';
2
+ import { render, screen, fireEvent, waitFor } from '@testing-library/react';
3
+ import userEvent from '@testing-library/user-event';
3
4
  import {
4
5
  DropdownMenu,
5
6
  DropdownMenuTrigger,
6
7
  DropdownMenuContent,
7
8
  DropdownMenuItem,
9
+ DropdownMenuLabel,
10
+ DropdownMenuSeparator,
11
+ DropdownMenuShortcut,
12
+ DropdownMenuGroup,
13
+ DropdownMenuSub,
14
+ DropdownMenuSubTrigger,
15
+ DropdownMenuSubContent,
8
16
  } from './DropdownMenu';
9
17
 
10
18
  describe('DropdownMenu', () => {
11
- test('renders item when menu is open', () => {
12
- render(
13
- <DropdownMenu open>
14
- <DropdownMenuTrigger>Open</DropdownMenuTrigger>
15
- <DropdownMenuContent>
16
- <DropdownMenuItem>Item</DropdownMenuItem>
17
- </DropdownMenuContent>
18
- </DropdownMenu>
19
- );
20
-
21
- expect(screen.getByText('Item')).toBeInTheDocument();
19
+ describe('Basic rendering', () => {
20
+ test('renders item when menu is open', () => {
21
+ render(
22
+ <DropdownMenu open>
23
+ <DropdownMenuTrigger>Open</DropdownMenuTrigger>
24
+ <DropdownMenuContent>
25
+ <DropdownMenuItem>Item</DropdownMenuItem>
26
+ </DropdownMenuContent>
27
+ </DropdownMenu>
28
+ );
29
+
30
+ expect(screen.getByText('Item')).toBeInTheDocument();
31
+ });
32
+
33
+ test('does not render content when menu is closed', () => {
34
+ render(
35
+ <DropdownMenu open={false}>
36
+ <DropdownMenuTrigger>Open</DropdownMenuTrigger>
37
+ <DropdownMenuContent>
38
+ <DropdownMenuItem>Item</DropdownMenuItem>
39
+ </DropdownMenuContent>
40
+ </DropdownMenu>
41
+ );
42
+
43
+ expect(screen.queryByText('Item')).not.toBeInTheDocument();
44
+ });
45
+
46
+ test('renders trigger button', () => {
47
+ render(
48
+ <DropdownMenu>
49
+ <DropdownMenuTrigger>Open Menu</DropdownMenuTrigger>
50
+ <DropdownMenuContent>
51
+ <DropdownMenuItem>Item</DropdownMenuItem>
52
+ </DropdownMenuContent>
53
+ </DropdownMenu>
54
+ );
55
+
56
+ expect(screen.getByText('Open Menu')).toBeInTheDocument();
57
+ });
58
+ });
59
+
60
+ describe('Opening and closing', () => {
61
+ test('opens menu when trigger is clicked', async () => {
62
+ const user = userEvent.setup();
63
+ render(
64
+ <DropdownMenu>
65
+ <DropdownMenuTrigger>Open Menu</DropdownMenuTrigger>
66
+ <DropdownMenuContent>
67
+ <DropdownMenuItem>Item 1</DropdownMenuItem>
68
+ </DropdownMenuContent>
69
+ </DropdownMenu>
70
+ );
71
+
72
+ const trigger = screen.getByText('Open Menu');
73
+ await user.click(trigger);
74
+
75
+ await waitFor(() => {
76
+ expect(screen.getByText('Item 1')).toBeInTheDocument();
77
+ });
78
+ });
79
+
80
+ test('controlled open state works', () => {
81
+ const { rerender } = render(
82
+ <DropdownMenu open={false}>
83
+ <DropdownMenuTrigger>Open Menu</DropdownMenuTrigger>
84
+ <DropdownMenuContent>
85
+ <DropdownMenuItem>Item</DropdownMenuItem>
86
+ </DropdownMenuContent>
87
+ </DropdownMenu>
88
+ );
89
+
90
+ // Verify the menu starts closed
91
+ expect(screen.queryByText('Item')).not.toBeInTheDocument();
92
+
93
+ // Rerender with open = true
94
+ rerender(
95
+ <DropdownMenu open={true}>
96
+ <DropdownMenuTrigger>Open Menu</DropdownMenuTrigger>
97
+ <DropdownMenuContent>
98
+ <DropdownMenuItem>Item</DropdownMenuItem>
99
+ </DropdownMenuContent>
100
+ </DropdownMenu>
101
+ );
102
+
103
+ expect(screen.getByText('Item')).toBeInTheDocument();
104
+ });
105
+ });
106
+
107
+ describe('Menu items', () => {
108
+ test('renders multiple menu items', () => {
109
+ render(
110
+ <DropdownMenu open>
111
+ <DropdownMenuTrigger>Open</DropdownMenuTrigger>
112
+ <DropdownMenuContent>
113
+ <DropdownMenuItem>Item 1</DropdownMenuItem>
114
+ <DropdownMenuItem>Item 2</DropdownMenuItem>
115
+ <DropdownMenuItem>Item 3</DropdownMenuItem>
116
+ </DropdownMenuContent>
117
+ </DropdownMenu>
118
+ );
119
+
120
+ expect(screen.getByText('Item 1')).toBeInTheDocument();
121
+ expect(screen.getByText('Item 2')).toBeInTheDocument();
122
+ expect(screen.getByText('Item 3')).toBeInTheDocument();
123
+ });
124
+
125
+ test('calls onSelect when item is clicked', async () => {
126
+ const handleSelect = jest.fn();
127
+ render(
128
+ <DropdownMenu open>
129
+ <DropdownMenuTrigger>Open</DropdownMenuTrigger>
130
+ <DropdownMenuContent>
131
+ <DropdownMenuItem onSelect={handleSelect}>
132
+ Clickable Item
133
+ </DropdownMenuItem>
134
+ </DropdownMenuContent>
135
+ </DropdownMenu>
136
+ );
137
+
138
+ const item = screen.getByText('Clickable Item');
139
+ fireEvent.click(item);
140
+
141
+ expect(handleSelect).toHaveBeenCalled();
142
+ });
143
+
144
+ test('renders disabled menu items with correct styling', () => {
145
+ render(
146
+ <DropdownMenu open>
147
+ <DropdownMenuTrigger>Open</DropdownMenuTrigger>
148
+ <DropdownMenuContent>
149
+ <DropdownMenuItem disabled>Disabled Item</DropdownMenuItem>
150
+ </DropdownMenuContent>
151
+ </DropdownMenu>
152
+ );
153
+
154
+ const item = screen.getByText('Disabled Item');
155
+ expect(item).toHaveClass('cursor-not-allowed', 'font-color-disabled');
156
+ });
157
+
158
+ test('does not call onSelect when disabled item is clicked', async () => {
159
+ const handleSelect = jest.fn();
160
+ render(
161
+ <DropdownMenu open>
162
+ <DropdownMenuTrigger>Open</DropdownMenuTrigger>
163
+ <DropdownMenuContent>
164
+ <DropdownMenuItem disabled onSelect={handleSelect}>
165
+ Disabled Item
166
+ </DropdownMenuItem>
167
+ </DropdownMenuContent>
168
+ </DropdownMenu>
169
+ );
170
+
171
+ const item = screen.getByText('Disabled Item');
172
+ fireEvent.click(item);
173
+
174
+ expect(handleSelect).not.toHaveBeenCalled();
175
+ });
176
+
177
+ test('renders item with inset styling', () => {
178
+ render(
179
+ <DropdownMenu open>
180
+ <DropdownMenuTrigger>Open</DropdownMenuTrigger>
181
+ <DropdownMenuContent>
182
+ <DropdownMenuItem inset>Inset Item</DropdownMenuItem>
183
+ </DropdownMenuContent>
184
+ </DropdownMenu>
185
+ );
186
+
187
+ const item = screen.getByText('Inset Item');
188
+ expect(item).toHaveClass('p-left-md');
189
+ });
190
+
191
+ test('applies custom className to item', () => {
192
+ render(
193
+ <DropdownMenu open>
194
+ <DropdownMenuTrigger>Open</DropdownMenuTrigger>
195
+ <DropdownMenuContent>
196
+ <DropdownMenuItem className="custom-class">
197
+ Custom Item
198
+ </DropdownMenuItem>
199
+ </DropdownMenuContent>
200
+ </DropdownMenu>
201
+ );
202
+
203
+ const item = screen.getByText('Custom Item');
204
+ expect(item).toHaveClass('custom-class');
205
+ });
206
+ });
207
+
208
+ describe('Menu label', () => {
209
+ test('renders menu label', () => {
210
+ render(
211
+ <DropdownMenu open>
212
+ <DropdownMenuTrigger>Open</DropdownMenuTrigger>
213
+ <DropdownMenuContent>
214
+ <DropdownMenuLabel>Section Label</DropdownMenuLabel>
215
+ <DropdownMenuItem>Item</DropdownMenuItem>
216
+ </DropdownMenuContent>
217
+ </DropdownMenu>
218
+ );
219
+
220
+ expect(screen.getByText('Section Label')).toBeInTheDocument();
221
+ });
222
+
223
+ test('renders label with inset styling', () => {
224
+ render(
225
+ <DropdownMenu open>
226
+ <DropdownMenuTrigger>Open</DropdownMenuTrigger>
227
+ <DropdownMenuContent>
228
+ <DropdownMenuLabel inset>Inset Label</DropdownMenuLabel>
229
+ </DropdownMenuContent>
230
+ </DropdownMenu>
231
+ );
232
+
233
+ const label = screen.getByText('Inset Label');
234
+ expect(label).toHaveClass('p-left-md');
235
+ });
236
+
237
+ test('applies custom className to label', () => {
238
+ render(
239
+ <DropdownMenu open>
240
+ <DropdownMenuTrigger>Open</DropdownMenuTrigger>
241
+ <DropdownMenuContent>
242
+ <DropdownMenuLabel className="custom-label-class">
243
+ Custom Label
244
+ </DropdownMenuLabel>
245
+ </DropdownMenuContent>
246
+ </DropdownMenu>
247
+ );
248
+
249
+ const label = screen.getByText('Custom Label');
250
+ expect(label).toHaveClass('custom-label-class');
251
+ });
252
+ });
253
+
254
+ describe('Menu separator', () => {
255
+ test('renders menu separator', () => {
256
+ render(
257
+ <DropdownMenu open>
258
+ <DropdownMenuTrigger>Open</DropdownMenuTrigger>
259
+ <DropdownMenuContent>
260
+ <DropdownMenuItem>Item 1</DropdownMenuItem>
261
+ <DropdownMenuSeparator data-testid="separator" />
262
+ <DropdownMenuItem>Item 2</DropdownMenuItem>
263
+ </DropdownMenuContent>
264
+ </DropdownMenu>
265
+ );
266
+
267
+ expect(screen.getByTestId('separator')).toBeInTheDocument();
268
+ });
269
+
270
+ test('applies correct separator styling', () => {
271
+ render(
272
+ <DropdownMenu open>
273
+ <DropdownMenuTrigger>Open</DropdownMenuTrigger>
274
+ <DropdownMenuContent>
275
+ <DropdownMenuSeparator data-testid="separator" />
276
+ </DropdownMenuContent>
277
+ </DropdownMenu>
278
+ );
279
+
280
+ const separator = screen.getByTestId('separator');
281
+ expect(separator).toHaveClass('h-2xs', 'm-v-xs', 'bw-top-sm');
282
+ });
283
+ });
284
+
285
+ describe('Menu shortcut', () => {
286
+ test('renders keyboard shortcut', () => {
287
+ render(
288
+ <DropdownMenu open>
289
+ <DropdownMenuTrigger>Open</DropdownMenuTrigger>
290
+ <DropdownMenuContent>
291
+ <DropdownMenuItem>
292
+ Save
293
+ <DropdownMenuShortcut>⌘S</DropdownMenuShortcut>
294
+ </DropdownMenuItem>
295
+ </DropdownMenuContent>
296
+ </DropdownMenu>
297
+ );
298
+
299
+ expect(screen.getByText('⌘S')).toBeInTheDocument();
300
+ });
301
+
302
+ test('applies shortcut styling', () => {
303
+ render(
304
+ <DropdownMenu open>
305
+ <DropdownMenuTrigger>Open</DropdownMenuTrigger>
306
+ <DropdownMenuContent>
307
+ <DropdownMenuItem>
308
+ <DropdownMenuShortcut data-testid="shortcut">
309
+ ⌘K
310
+ </DropdownMenuShortcut>
311
+ </DropdownMenuItem>
312
+ </DropdownMenuContent>
313
+ </DropdownMenu>
314
+ );
315
+
316
+ const shortcut = screen.getByTestId('shortcut');
317
+ expect(shortcut).toHaveClass('m-left-auto', 'font-size-xs');
318
+ });
319
+ });
320
+
321
+ describe('Menu group', () => {
322
+ test('renders grouped items', () => {
323
+ render(
324
+ <DropdownMenu open>
325
+ <DropdownMenuTrigger>Open</DropdownMenuTrigger>
326
+ <DropdownMenuContent>
327
+ <DropdownMenuGroup>
328
+ <DropdownMenuItem>Grouped Item 1</DropdownMenuItem>
329
+ <DropdownMenuItem>Grouped Item 2</DropdownMenuItem>
330
+ </DropdownMenuGroup>
331
+ </DropdownMenuContent>
332
+ </DropdownMenu>
333
+ );
334
+
335
+ expect(screen.getByText('Grouped Item 1')).toBeInTheDocument();
336
+ expect(screen.getByText('Grouped Item 2')).toBeInTheDocument();
337
+ });
338
+ });
339
+
340
+ describe('Submenu', () => {
341
+ test('renders submenu trigger', () => {
342
+ render(
343
+ <DropdownMenu open>
344
+ <DropdownMenuTrigger>Open</DropdownMenuTrigger>
345
+ <DropdownMenuContent>
346
+ <DropdownMenuSub>
347
+ <DropdownMenuSubTrigger>More Options</DropdownMenuSubTrigger>
348
+ <DropdownMenuSubContent>
349
+ <DropdownMenuItem>Sub Item</DropdownMenuItem>
350
+ </DropdownMenuSubContent>
351
+ </DropdownMenuSub>
352
+ </DropdownMenuContent>
353
+ </DropdownMenu>
354
+ );
355
+
356
+ expect(screen.getByText('More Options')).toBeInTheDocument();
357
+ });
358
+
359
+ test('submenu trigger has chevron icon', () => {
360
+ render(
361
+ <DropdownMenu open>
362
+ <DropdownMenuTrigger>Open</DropdownMenuTrigger>
363
+ <DropdownMenuContent>
364
+ <DropdownMenuSub>
365
+ <DropdownMenuSubTrigger>More Options</DropdownMenuSubTrigger>
366
+ <DropdownMenuSubContent>
367
+ <DropdownMenuItem>Sub Item</DropdownMenuItem>
368
+ </DropdownMenuSubContent>
369
+ </DropdownMenuSub>
370
+ </DropdownMenuContent>
371
+ </DropdownMenu>
372
+ );
373
+
374
+ const subtrigger = screen
375
+ .getByText('More Options')
376
+ .closest('[role="menuitem"]');
377
+ // The Icon component uses data-testid with "icon-testid--" prefix
378
+ const icon = subtrigger?.querySelector('svg');
379
+ expect(icon).toBeInTheDocument();
380
+ });
381
+
382
+ test('renders submenu trigger with inset styling', () => {
383
+ render(
384
+ <DropdownMenu open>
385
+ <DropdownMenuTrigger>Open</DropdownMenuTrigger>
386
+ <DropdownMenuContent>
387
+ <DropdownMenuSub>
388
+ <DropdownMenuSubTrigger inset>
389
+ Inset Submenu
390
+ </DropdownMenuSubTrigger>
391
+ <DropdownMenuSubContent>
392
+ <DropdownMenuItem>Sub Item</DropdownMenuItem>
393
+ </DropdownMenuSubContent>
394
+ </DropdownMenuSub>
395
+ </DropdownMenuContent>
396
+ </DropdownMenu>
397
+ );
398
+
399
+ const subtrigger = screen
400
+ .getByText('Inset Submenu')
401
+ .closest('[role="menuitem"]');
402
+ expect(subtrigger).toHaveClass('p-left-md');
403
+ });
404
+ });
405
+
406
+ describe('Content styling', () => {
407
+ test('applies correct content styling classes', () => {
408
+ render(
409
+ <DropdownMenu open>
410
+ <DropdownMenuTrigger>Open</DropdownMenuTrigger>
411
+ <DropdownMenuContent data-testid="content">
412
+ <DropdownMenuItem>Item</DropdownMenuItem>
413
+ </DropdownMenuContent>
414
+ </DropdownMenu>
415
+ );
416
+
417
+ const content = screen.getByTestId('content');
418
+ expect(content).toHaveClass(
419
+ 'z-index-popover',
420
+ 'p-xs',
421
+ 'overflow-hidden',
422
+ 'br-sm',
423
+ 'bw-sm'
424
+ );
425
+ });
426
+
427
+ test('applies custom className to content', () => {
428
+ render(
429
+ <DropdownMenu open>
430
+ <DropdownMenuTrigger>Open</DropdownMenuTrigger>
431
+ <DropdownMenuContent className="custom-content-class">
432
+ <DropdownMenuItem>Item</DropdownMenuItem>
433
+ </DropdownMenuContent>
434
+ </DropdownMenu>
435
+ );
436
+
437
+ const content = screen.getByText('Item').closest('[role="menu"]');
438
+ expect(content).toHaveClass('custom-content-class');
439
+ });
440
+ });
441
+
442
+ describe('Keyboard navigation', () => {
443
+ test('focuses first item when menu opens', async () => {
444
+ const user = userEvent.setup();
445
+ render(
446
+ <DropdownMenu>
447
+ <DropdownMenuTrigger>Open</DropdownMenuTrigger>
448
+ <DropdownMenuContent>
449
+ <DropdownMenuItem>First Item</DropdownMenuItem>
450
+ <DropdownMenuItem>Second Item</DropdownMenuItem>
451
+ </DropdownMenuContent>
452
+ </DropdownMenu>
453
+ );
454
+
455
+ const trigger = screen.getByText('Open');
456
+ await user.click(trigger);
457
+
458
+ await waitFor(() => {
459
+ expect(screen.getByText('First Item')).toBeInTheDocument();
460
+ });
461
+ });
462
+
463
+ test('closes menu when escape is pressed', async () => {
464
+ const user = userEvent.setup();
465
+ render(
466
+ <DropdownMenu>
467
+ <DropdownMenuTrigger>Open</DropdownMenuTrigger>
468
+ <DropdownMenuContent>
469
+ <DropdownMenuItem>Item</DropdownMenuItem>
470
+ </DropdownMenuContent>
471
+ </DropdownMenu>
472
+ );
473
+
474
+ const trigger = screen.getByText('Open');
475
+ await user.click(trigger);
476
+
477
+ await waitFor(() => {
478
+ expect(screen.getByText('Item')).toBeInTheDocument();
479
+ });
480
+
481
+ await user.keyboard('{Escape}');
482
+
483
+ await waitFor(() => {
484
+ expect(screen.queryByText('Item')).not.toBeInTheDocument();
485
+ });
486
+ });
487
+ });
488
+
489
+ describe('Ref forwarding', () => {
490
+ test('forwards ref to DropdownMenuItem', () => {
491
+ const ref = React.createRef<HTMLDivElement>();
492
+ render(
493
+ <DropdownMenu open>
494
+ <DropdownMenuTrigger>Open</DropdownMenuTrigger>
495
+ <DropdownMenuContent>
496
+ <DropdownMenuItem ref={ref}>Item with ref</DropdownMenuItem>
497
+ </DropdownMenuContent>
498
+ </DropdownMenu>
499
+ );
500
+
501
+ expect(ref.current).toBeInstanceOf(HTMLDivElement);
502
+ });
503
+
504
+ test('forwards ref to DropdownMenuLabel', () => {
505
+ const ref = React.createRef<HTMLDivElement>();
506
+ render(
507
+ <DropdownMenu open>
508
+ <DropdownMenuTrigger>Open</DropdownMenuTrigger>
509
+ <DropdownMenuContent>
510
+ <DropdownMenuLabel ref={ref}>Label with ref</DropdownMenuLabel>
511
+ </DropdownMenuContent>
512
+ </DropdownMenu>
513
+ );
514
+
515
+ expect(ref.current).toBeInstanceOf(HTMLDivElement);
516
+ });
517
+
518
+ test('forwards ref to DropdownMenuSeparator', () => {
519
+ const ref = React.createRef<HTMLDivElement>();
520
+ render(
521
+ <DropdownMenu open>
522
+ <DropdownMenuTrigger>Open</DropdownMenuTrigger>
523
+ <DropdownMenuContent>
524
+ <DropdownMenuSeparator ref={ref} />
525
+ </DropdownMenuContent>
526
+ </DropdownMenu>
527
+ );
528
+
529
+ expect(ref.current).toBeInstanceOf(HTMLDivElement);
530
+ });
531
+ });
532
+
533
+ describe('Display names', () => {
534
+ test('components have display names defined', () => {
535
+ expect(DropdownMenuItem.displayName).toBeDefined();
536
+ expect(DropdownMenuLabel.displayName).toBeDefined();
537
+ expect(DropdownMenuSeparator.displayName).toBeDefined();
538
+ expect(DropdownMenuShortcut.displayName).toBe('DropdownMenuShortcut');
539
+ expect(DropdownMenuSubTrigger.displayName).toBeDefined();
540
+ expect(DropdownMenuSubContent.displayName).toBeDefined();
541
+ });
22
542
  });
23
543
  });
@@ -76,3 +76,5 @@ export const FormControl = React.forwardRef<HTMLDivElement, FormControlProps>(
76
76
  );
77
77
  }
78
78
  );
79
+
80
+ FormControl.displayName = 'FormControl';
@@ -1,6 +1,6 @@
1
1
  import React, { ChangeEvent } from 'react';
2
2
  import { Calendar } from '../Calendar/Calendar';
3
- import { Field, Form, Formik } from 'formik';
3
+ import { Field, Form, Formik, FormikHelpers } from 'formik';
4
4
  import { FormikTextInput } from './FormikTextInput/FormikTextInput';
5
5
  import { FormikCheckboxInput } from './FormikCheckboxInput/FormikCheckboxInput';
6
6
  import { FormikSelectInput } from './FormikSelectInput/FormikSelectInput';
@@ -36,6 +36,28 @@ const meta = {
36
36
 
37
37
  export default meta;
38
38
 
39
+ type FormValues = {
40
+ firstName: string;
41
+ lastName: string;
42
+ email: string;
43
+ phone: string;
44
+ areTermsChecked: boolean;
45
+ areTermsChecked2: boolean;
46
+ flavor: string | null;
47
+ flavor2: string | null;
48
+ colors: Array<{ value: string; label: string }>;
49
+ colors2: string;
50
+ sizes: string | null;
51
+ timePicker: string | null;
52
+ timePickerNative: string | null;
53
+ dateInput: Date | undefined;
54
+ message: string;
55
+ country: string;
56
+ availability: string;
57
+ locations: string[];
58
+ meal: string;
59
+ };
60
+
39
61
  export const FormikForm = () =>
40
62
  (() => {
41
63
  const flavorOptions = [
@@ -94,8 +116,8 @@ export const FormikForm = () =>
94
116
  downtime: '5.26 minutes/year',
95
117
  },
96
118
  ];
97
- const handleValidation = (values: Record<string, any>) => {
98
- const errors: Record<string, string> = {};
119
+ const handleValidation = (values: FormValues) => {
120
+ const errors: Partial<Record<keyof FormValues, string>> = {};
99
121
  if (!values.firstName) {
100
122
  errors.firstName = 'required';
101
123
  }
@@ -151,8 +173,8 @@ export const FormikForm = () =>
151
173
  return errors;
152
174
  };
153
175
  const handleSubmit = (
154
- values: Record<string, any>,
155
- { setSubmitting }: any
176
+ values: FormValues,
177
+ { setSubmitting }: FormikHelpers<FormValues>
156
178
  ) => {
157
179
  // Make API calls here
158
180
  setTimeout(() => {
@@ -163,8 +185,8 @@ export const FormikForm = () =>
163
185
  );
164
186
  }, 2000);
165
187
  };
166
- const formatOutput = (
167
- values: Record<string, any>,
188
+ const formatOutput = <T extends Record<string, unknown>>(
189
+ values: T,
168
190
  isSubmitting?: boolean
169
191
  ) => {
170
192
  return { ...values, isSubmitting };
@@ -191,6 +213,7 @@ export const FormikForm = () =>
191
213
  country: '',
192
214
  availability: '99',
193
215
  locations: [] as string[],
216
+ meal: '',
194
217
  }}
195
218
  validate={handleValidation}
196
219
  onSubmit={handleSubmit}
@@ -16,7 +16,7 @@ export interface FormikSelectInputProps
16
16
  errors: FormikErrors<FormikValues>;
17
17
  };
18
18
  onChange?: SelectInputProps['onChange'];
19
- error?: any[] | string;
19
+ error?: string | Array<{ label?: string }>;
20
20
  }
21
21
 
22
22
  export const FormikSelectInput: React.FC<FormikSelectInputProps> = ({
@@ -29,13 +29,14 @@ export const FormikSelectInput: React.FC<FormikSelectInputProps> = ({
29
29
  error: errorProp,
30
30
  ...props
31
31
  }) => {
32
- let errorMessage;
33
- const error = errorProp ?? (getIn(touched, name) && getIn(errors, name));
32
+ let errorMessage: string | undefined;
33
+ const error: unknown =
34
+ errorProp ?? (getIn(touched, name) && getIn(errors, name));
34
35
 
35
36
  if (typeof error === 'string') {
36
37
  errorMessage = error;
37
- } else if (error && typeof error !== 'string') {
38
- errorMessage = error?.find((err: any) => err?.label)?.label;
38
+ } else if (Array.isArray(error)) {
39
+ errorMessage = error.find((err) => err?.label)?.label;
39
40
  }
40
41
 
41
42
  return (
@@ -15,7 +15,7 @@ export interface FormikToggleGroupProps {
15
15
  form: {
16
16
  touched: FormikTouched<FormikValues>;
17
17
  errors: FormikErrors<FormikValues>;
18
- setFieldValue: (field: string, value: any) => void;
18
+ setFieldValue: (field: string, value: unknown, shouldValidate?: boolean) => void;
19
19
  };
20
20
  options: Array<{
21
21
  id: string;
@@ -31,3 +31,5 @@ export const HelpText = forwardRef<HTMLDivElement, HelpTextProps>(
31
31
  </Box>
32
32
  )
33
33
  );
34
+
35
+ HelpText.displayName = 'HelpText';
@@ -41,7 +41,7 @@ export const Colors = () => (
41
41
  );
42
42
 
43
43
  export const UnknownIcon = () => (
44
- <Icon name={'does-not-exist' as any} className="font-size-2xl" />
44
+ <Icon name={'does-not-exist' as IconName} className="font-size-2xl" />
45
45
  );
46
46
 
47
47
  export const AvailableIcons = () => (