@dhasdk/simple-ui 1.0.8 → 1.0.10

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 (228) hide show
  1. package/README.md +2 -2
  2. package/index.css +1 -0
  3. package/{src/index.ts → index.d.ts} +8 -15
  4. package/index.js +34 -0
  5. package/index.mjs +5473 -0
  6. package/lib/Accordion.d.ts +36 -0
  7. package/lib/AppointmentPicker.d.ts +21 -0
  8. package/lib/Badge.d.ts +11 -0
  9. package/lib/Breadcrumbs.d.ts +13 -0
  10. package/lib/Button.d.ts +15 -0
  11. package/lib/ButtonGroup.d.ts +8 -0
  12. package/lib/Card.d.ts +11 -0
  13. package/lib/CharacterCounter.d.ts +11 -0
  14. package/lib/CheckBox.d.ts +30 -0
  15. package/lib/DatePicker.d.ts +7 -0
  16. package/lib/Input.d.ts +16 -0
  17. package/lib/List.d.ts +22 -0
  18. package/lib/Modal.d.ts +18 -0
  19. package/lib/Pill.d.ts +13 -0
  20. package/lib/ProgressBar.d.ts +19 -0
  21. package/lib/RadioGroup.d.ts +16 -0
  22. package/lib/RadioIcon.d.ts +3 -0
  23. package/lib/Search.d.ts +26 -0
  24. package/lib/SearchContent.d.ts +6 -0
  25. package/lib/SectionHeader.d.ts +18 -0
  26. package/lib/Select.d.ts +19 -0
  27. package/lib/Shield.d.ts +12 -0
  28. package/lib/SideBarNav.d.ts +21 -0
  29. package/lib/Skeleton.d.ts +15 -0
  30. package/lib/SkipLink.d.ts +23 -0
  31. package/lib/Slider.d.ts +14 -0
  32. package/lib/Status.d.ts +10 -0
  33. package/lib/Tabs.d.ts +26 -0
  34. package/lib/Toggle.d.ts +11 -0
  35. package/lib/Tooltip.d.ts +14 -0
  36. package/package.json +1 -1
  37. package/.babelrc +0 -12
  38. package/.storybook/main.ts +0 -35
  39. package/.storybook/preview.ts +0 -4
  40. package/BAKpostcss.config.jsBAK +0 -15
  41. package/BAKtailwind.config.mjsBAK +0 -99
  42. package/coverage/storybook/coverage-storybook.json +0 -32411
  43. package/coverage/storybook/lcov-report/Accordion.tsx.html +0 -805
  44. package/coverage/storybook/lcov-report/Badge.tsx.html +0 -346
  45. package/coverage/storybook/lcov-report/Breadcrumbs.tsx.html +0 -742
  46. package/coverage/storybook/lcov-report/Button.tsx.html +0 -448
  47. package/coverage/storybook/lcov-report/ButtonGroup.tsx.html +0 -403
  48. package/coverage/storybook/lcov-report/Card.tsx.html +0 -292
  49. package/coverage/storybook/lcov-report/CharacterCounter.tsx.html +0 -253
  50. package/coverage/storybook/lcov-report/CheckBox.tsx.html +0 -1555
  51. package/coverage/storybook/lcov-report/DatePicker.tsx.html +0 -826
  52. package/coverage/storybook/lcov-report/Input.tsx.html +0 -1012
  53. package/coverage/storybook/lcov-report/List.tsx.html +0 -364
  54. package/coverage/storybook/lcov-report/Modal.tsx.html +0 -745
  55. package/coverage/storybook/lcov-report/Pill.tsx.html +0 -358
  56. package/coverage/storybook/lcov-report/Search.tsx.html +0 -997
  57. package/coverage/storybook/lcov-report/SearchContent.tsx.html +0 -235
  58. package/coverage/storybook/lcov-report/SectionHeader.tsx.html +0 -358
  59. package/coverage/storybook/lcov-report/Select.tsx.html +0 -1012
  60. package/coverage/storybook/lcov-report/Shield.tsx.html +0 -802
  61. package/coverage/storybook/lcov-report/SideBarNav.tsx.html +0 -490
  62. package/coverage/storybook/lcov-report/Skeleton.tsx.html +0 -394
  63. package/coverage/storybook/lcov-report/Slider.tsx.html +0 -385
  64. package/coverage/storybook/lcov-report/Status.tsx.html +0 -322
  65. package/coverage/storybook/lcov-report/Tabs.tsx.html +0 -610
  66. package/coverage/storybook/lcov-report/Toggle.tsx.html +0 -373
  67. package/coverage/storybook/lcov-report/Tooltip.tsx.html +0 -496
  68. package/coverage/storybook/lcov-report/base.css +0 -224
  69. package/coverage/storybook/lcov-report/block-navigation.js +0 -87
  70. package/coverage/storybook/lcov-report/favicon.png +0 -0
  71. package/coverage/storybook/lcov-report/index.html +0 -476
  72. package/coverage/storybook/lcov-report/prettify.css +0 -1
  73. package/coverage/storybook/lcov-report/prettify.js +0 -2
  74. package/coverage/storybook/lcov-report/sort-arrow-sprite.png +0 -0
  75. package/coverage/storybook/lcov-report/sorter.js +0 -196
  76. package/coverage/storybook/lcov.info +0 -2312
  77. package/dist/README.md +0 -1815
  78. package/eslint.config.mjs +0 -13
  79. package/project.json +0 -11
  80. package/src/assets/img/Frame.svg +0 -5
  81. package/src/assets/img/backArrowRight.svg +0 -10
  82. package/src/assets/img/bc-separator.png +0 -0
  83. package/src/assets/img/calendar.png +0 -0
  84. package/src/assets/img/calendar.svg +0 -4
  85. package/src/assets/img/check.svg +0 -5
  86. package/src/assets/img/check_box.svg +0 -10
  87. package/src/assets/img/check_box_empty.svg +0 -10
  88. package/src/assets/img/check_box_fill.svg +0 -10
  89. package/src/assets/img/check_box_fill_empty.svg +0 -10
  90. package/src/assets/img/chevron-down-white.svg +0 -2
  91. package/src/assets/img/chevron-down.svg +0 -2
  92. package/src/assets/img/chevron-left.svg +0 -1
  93. package/src/assets/img/chevron-right-light.svg +0 -4
  94. package/src/assets/img/chevron-right.svg +0 -3
  95. package/src/assets/img/chevron-up-white.svg +0 -1
  96. package/src/assets/img/chevron-up.svg +0 -1
  97. package/src/assets/img/clock.svg +0 -6
  98. package/src/assets/img/close.svg +0 -1
  99. package/src/assets/img/close2.svg +0 -6
  100. package/src/assets/img/closeModal.svg +0 -10
  101. package/src/assets/img/close_icon_dark.svg +0 -10
  102. package/src/assets/img/close_small.svg +0 -3
  103. package/src/assets/img/emergency_home.svg +0 -10
  104. package/src/assets/img/first-aid-kit.svg +0 -7
  105. package/src/assets/img/heartbeat.svg +0 -4
  106. package/src/assets/img/home-gray.svg +0 -3
  107. package/src/assets/img/home.svg +0 -3
  108. package/src/assets/img/hospital.jpg +0 -0
  109. package/src/assets/img/indeterminate_check_box.svg +0 -10
  110. package/src/assets/img/indeterminate_check_box_fill.svg +0 -10
  111. package/src/assets/img/info_24_ 1d4ed8.svg +0 -3
  112. package/src/assets/img/info_24_ 2c6441.svg +0 -3
  113. package/src/assets/img/marker_check_by_default.svg +0 -10
  114. package/src/assets/img/marker_check_by_default_fill.svg +0 -10
  115. package/src/assets/img/minus-accordion.svg +0 -5
  116. package/src/assets/img/minus.svg +0 -3
  117. package/src/assets/img/open.svg +0 -1
  118. package/src/assets/img/pill-white.svg +0 -7
  119. package/src/assets/img/pill.svg +0 -5
  120. package/src/assets/img/plus-accordion.svg +0 -5
  121. package/src/assets/img/plus.svg +0 -4
  122. package/src/assets/img/prescription.svg +0 -6
  123. package/src/assets/img/search.svg +0 -10
  124. package/src/assets/img/search_icon_light.svg +0 -10
  125. package/src/assets/img/separator.svg +0 -3
  126. package/src/assets/img/stethoscope-white.svg +0 -8
  127. package/src/assets/img/stethoscope.svg +0 -8
  128. package/src/assets/img/thumb_up.svg +0 -10
  129. package/src/assets/img/vector.svg +0 -3
  130. package/src/assets/img/warning-badge-disabled.svg +0 -11
  131. package/src/assets/img/warning-badge-green.svg +0 -11
  132. package/src/assets/img/warning-badge-red.svg +0 -11
  133. package/src/assets/img/warning-badge-yellow.svg +0 -11
  134. package/src/assets/img/warning.svg +0 -10
  135. package/src/global.d.ts +0 -13
  136. package/src/lib/Accordian--Accordian.stories.tsx +0 -312
  137. package/src/lib/Accordion.spec.tsx +0 -384
  138. package/src/lib/Accordion.tsx +0 -240
  139. package/src/lib/AppointmentPicker.spec.tsx +0 -138
  140. package/src/lib/AppointmentPicker.tsx +0 -97
  141. package/src/lib/Badge--Badge.stories.tsx +0 -60
  142. package/src/lib/Badge.spec.tsx +0 -70
  143. package/src/lib/Badge.tsx +0 -87
  144. package/src/lib/Breadcrumbs-Breadcrumbs.stories.tsx +0 -114
  145. package/src/lib/Breadcrumbs.spec.tsx +0 -218
  146. package/src/lib/Breadcrumbs.tsx +0 -219
  147. package/src/lib/Button--Button.stories.tsx +0 -220
  148. package/src/lib/Button.spec.tsx +0 -241
  149. package/src/lib/Button.tsx +0 -121
  150. package/src/lib/ButtonGroup--ButtonGroup.stories.tsx +0 -129
  151. package/src/lib/ButtonGroup.spec.tsx +0 -89
  152. package/src/lib/ButtonGroup.tsx +0 -107
  153. package/src/lib/Card--Card.stories.tsx +0 -113
  154. package/src/lib/Card.spec.tsx +0 -112
  155. package/src/lib/Card.tsx +0 -69
  156. package/src/lib/CharacterCounter--CharacterCounter.stories.tsx +0 -169
  157. package/src/lib/CharacterCounter.spec.tsx +0 -123
  158. package/src/lib/CharacterCounter.tsx +0 -56
  159. package/src/lib/CheckBox--CheckBox.stories.tsx +0 -107
  160. package/src/lib/CheckBox.spec.tsx +0 -412
  161. package/src/lib/CheckBox.tsx +0 -491
  162. package/src/lib/DatePicker--DatePicker.stories.tsx +0 -228
  163. package/src/lib/DatePicker.spec.tsx +0 -424
  164. package/src/lib/DatePicker.tsx +0 -247
  165. package/src/lib/Input--Input.stories.tsx +0 -449
  166. package/src/lib/Input.spec.tsx +0 -281
  167. package/src/lib/Input.tsx +0 -309
  168. package/src/lib/List--List.stories.tsx +0 -157
  169. package/src/lib/List.spec.tsx +0 -211
  170. package/src/lib/List.tsx +0 -93
  171. package/src/lib/Modal--Modal.stories.tsx +0 -454
  172. package/src/lib/Modal.spec.tsx +0 -202
  173. package/src/lib/Modal.tsx +0 -220
  174. package/src/lib/Pill--Pill.stories.tsx +0 -98
  175. package/src/lib/Pill.spec.tsx +0 -103
  176. package/src/lib/Pill.tsx +0 -91
  177. package/src/lib/ProgressBar.spec.tsx +0 -106
  178. package/src/lib/ProgressBar.tsx +0 -112
  179. package/src/lib/RadioGroup.spec.tsx +0 -84
  180. package/src/lib/RadioGroup.tsx +0 -74
  181. package/src/lib/RadioIcon.tsx +0 -13
  182. package/src/lib/Search--Search.stories.tsx +0 -67
  183. package/src/lib/Search.spec.tsx +0 -182
  184. package/src/lib/Search.tsx +0 -304
  185. package/src/lib/SearchContent.tsx +0 -51
  186. package/src/lib/SectionHeader--SectionHeader.stories.tsx +0 -98
  187. package/src/lib/SectionHeader.spec.tsx +0 -60
  188. package/src/lib/SectionHeader.tsx +0 -91
  189. package/src/lib/Select--Select.stories.tsx +0 -387
  190. package/src/lib/Select.spec.tsx +0 -493
  191. package/src/lib/Select.tsx +0 -311
  192. package/src/lib/Shield--Shield.stories.tsx +0 -196
  193. package/src/lib/Shield.spec.tsx +0 -275
  194. package/src/lib/Shield.tsx +0 -239
  195. package/src/lib/SideBarNav--SideBarNav.stories.tsx +0 -136
  196. package/src/lib/SideBarNav.spec.tsx +0 -178
  197. package/src/lib/SideBarNav.tsx +0 -135
  198. package/src/lib/Skeleton--Skeleton.stories.tsx +0 -77
  199. package/src/lib/Skeleton.module.css +0 -16
  200. package/src/lib/Skeleton.spec.tsx +0 -83
  201. package/src/lib/Skeleton.tsx +0 -103
  202. package/src/lib/SkipLink.spec.tsx +0 -76
  203. package/src/lib/SkipLink.tsx +0 -48
  204. package/src/lib/Slider--Slider.stories.tsx +0 -108
  205. package/src/lib/Slider.module.css +0 -109
  206. package/src/lib/Slider.spec.tsx +0 -67
  207. package/src/lib/Slider.tsx +0 -101
  208. package/src/lib/Status--Status.stories.tsx +0 -93
  209. package/src/lib/Status.spec.tsx +0 -118
  210. package/src/lib/Status.tsx +0 -79
  211. package/src/lib/Tabs--Tabs.stories.tsx +0 -294
  212. package/src/lib/Tabs.spec.tsx +0 -249
  213. package/src/lib/Tabs.tsx +0 -188
  214. package/src/lib/Tester.spec.tsx +0 -17
  215. package/src/lib/Toggle--Toggle.stories.tsx +0 -162
  216. package/src/lib/Toggle.spec.tsx +0 -122
  217. package/src/lib/Toggle.tsx +0 -96
  218. package/src/lib/Tooltip--Tooltip.stories.tsx +0 -315
  219. package/src/lib/Tooltip.spec.tsx +0 -307
  220. package/src/lib/Tooltip.tsx +0 -137
  221. package/src/lib/bak-simple-ui.stories.tsx-bak +0 -24
  222. package/src/styles.css +0 -190
  223. package/tsconfig.json +0 -25
  224. package/tsconfig.lib.json +0 -42
  225. package/tsconfig.spec.json +0 -29
  226. package/tsconfig.storybook.json +0 -36
  227. package/vite.config.mts +0 -87
  228. package/vitest.setup.ts +0 -12
@@ -1,493 +0,0 @@
1
- import React from "react";
2
- import { render, screen, fireEvent } from "@testing-library/react";
3
- import { vi } from "vitest";
4
- import { axe } from "vitest-axe";
5
- import { Select, SelectProps } from "./Select";
6
-
7
- describe("Select Component", () => {
8
- const options = [
9
- { name: "Option 1", value: "1" },
10
- { name: "Option 2", value: "2" },
11
- { name: "Option 3", value: "3" },
12
- ];
13
-
14
- it("renders the select dropdown with the default state, error, and variant fill", () => {
15
- render(<Select variant="fill" error options={options} setSelectedOption={vi.fn()} />);
16
- const button = screen.getByRole("button", { name: /Options/i });
17
-
18
- //clicks the button twice to open and close the menu, verifying correct chevrons render
19
- fireEvent.click(button);
20
- fireEvent.click(button);
21
-
22
- expect(button).toBeInTheDocument();
23
- expect(button).toHaveTextContent("Options");
24
- expect(screen.queryByRole("listbox")).not.toBeInTheDocument();
25
- });
26
-
27
- it("renders the dropdown label when provided", () => {
28
- render(<Select label="Dropdown Label" options={options} setSelectedOption={vi.fn()} />);
29
- const label = screen.getByText("Dropdown Label");
30
-
31
- expect(label).toBeInTheDocument();
32
- expect(label).toHaveClass("text-black");
33
- });
34
-
35
- it("toggles the dropdown menu on button click", () => {
36
- render(<Select options={options} setSelectedOption={vi.fn()} />);
37
- const button = screen.getByRole("button");
38
-
39
- // Menu should not be present initially
40
- expect(screen.queryByRole("listbox")).not.toBeInTheDocument();
41
-
42
- // Open the dropdown
43
- fireEvent.click(button);
44
- expect(screen.getByRole("listbox")).toBeInTheDocument();
45
-
46
- // Close the dropdown
47
- fireEvent.click(button);
48
- expect(screen.queryByRole("listbox")).not.toBeInTheDocument();
49
- });
50
-
51
-
52
- it("selects an option and calls the callback", () => {
53
- const setSelectedOptionMock = vi.fn();
54
- render(<Select options={options} setSelectedOption={setSelectedOptionMock} />);
55
-
56
- const button = screen.getByRole("button");
57
- fireEvent.click(button);
58
-
59
- const option = screen.getByText("Option 1");
60
- fireEvent.click(option);
61
-
62
- expect(setSelectedOptionMock).toHaveBeenCalledWith("1");
63
- expect(button).toHaveTextContent("Option 1");
64
- expect(screen.queryByRole("listbox")).not.toBeInTheDocument();
65
- });
66
-
67
- it("falls back to the option name if no value is provided", () => {
68
- const customOptions = [{ name: "No Value Option" }];
69
- const setSelectedOptionMock = vi.fn();
70
-
71
- render(<Select options={customOptions} setSelectedOption={setSelectedOptionMock} />);
72
- const button = screen.getByRole("button");
73
-
74
- fireEvent.click(button);
75
-
76
- const option = screen.getByText("No Value Option");
77
- fireEvent.click(option);
78
-
79
- expect(setSelectedOptionMock).toHaveBeenCalledWith("No Value Option");
80
- expect(button).toHaveTextContent("No Value Option");
81
- });
82
-
83
- it("closes the dropdown when clicking outside", () => {
84
- render(<Select options={options} setSelectedOption={vi.fn()} />);
85
- const button = screen.getByRole("button");
86
-
87
- // Open the dropdown
88
- fireEvent.click(button);
89
- expect(screen.getByRole("listbox")).toBeInTheDocument();
90
-
91
- // Click outside
92
- fireEvent.mouseDown(document.body);
93
- expect(screen.queryByRole("listbox")).not.toBeInTheDocument();
94
- });
95
-
96
-
97
- it("applies custom classes and merges them with default styles", () => {
98
- render(
99
- <Select
100
- className="custom-class"
101
- options={options}
102
- setSelectedOption={vi.fn()}
103
- />
104
- );
105
-
106
- const button = screen.getByRole("button");
107
- expect(button).toHaveClass("custom-class");
108
- });
109
-
110
- it("applies variant-specific styles", () => {
111
- render(<Select variant="outline" options={options} setSelectedOption={vi.fn()} />);
112
- const button = screen.getByRole("button");
113
-
114
- expect(button).toHaveClass("border-[#b3b3b3] bg-white border");
115
- });
116
-
117
- it("renders a disabled dropdown", () => {
118
- render(
119
- <Select
120
- options={options}
121
- setSelectedOption={vi.fn()}
122
- disabled
123
- />
124
- );
125
-
126
- const button = screen.getByRole("button");
127
- expect(button).toBeDisabled();
128
- expect(button).toHaveClass("disabled:bg-dha-mc-bottom-nav-background disabled:text-dha-mc-checkbox-inactive");
129
- });
130
-
131
-
132
- it('falls back to the default variant when an invalid variant is provided', () => {
133
- const { container } = render(
134
- <Select
135
- options={options}
136
- variant="InvalidVariant"
137
- setSelectedOption={vitest.fn()}
138
- />
139
- );
140
-
141
- // Verify that the classes for the "default" variant are applied
142
- const button = container.querySelector('button');
143
- expect(button).toHaveClass('hover:bg-gray-200');
144
- });
145
-
146
- });
147
-
148
-
149
- describe('Select Accessibility Tests', () => {
150
- const mockSetSelectedOption = vi.fn();
151
- const options = [
152
- { name: 'Option 1', value: '1' },
153
- { name: 'Option 2', value: '2' },
154
- { name: 'Option 3', value: '3' },
155
- ];
156
-
157
- const renderComponent = (props: Partial<SelectProps> = {}) =>
158
- render(
159
- <Select
160
- label="Choose an option"
161
- options={options}
162
- setSelectedOption={mockSetSelectedOption}
163
- {...props}
164
- />
165
- );
166
-
167
- it('should have no accessibility violations in the default state', async () => {
168
- const { container } = renderComponent();
169
- const results = await axe(container);
170
- expect(results).toHaveNoViolations();
171
- });
172
-
173
- it('should have no accessibility violations when open', async () => {
174
- const { container, getByRole } = renderComponent();
175
- const button = getByRole('button', { name: /Select options/i });
176
- fireEvent.click(button);
177
- const results = await axe(container);
178
- expect(results).toHaveNoViolations();
179
- });
180
-
181
- it('should include a label if provided', () => {
182
- const { getByText } = renderComponent({ label: 'Accessible Label' });
183
- expect(getByText('Accessible Label')).toBeInTheDocument();
184
- });
185
-
186
- it('should render options correctly', () => {
187
- const { getByRole, getByLabelText } = renderComponent();
188
- const button = getByRole('button', { name: /Select options/i });
189
- fireEvent.click(button);
190
-
191
- options.forEach((option) => {
192
- const optionElement = getByLabelText(`option ${option.name}`);
193
- expect(optionElement).toBeInTheDocument();
194
- });
195
- });
196
-
197
- it('should allow selecting an option', () => {
198
- const { getByRole, getByLabelText } = renderComponent();
199
- const button = getByRole('button', { name: /Select options/i });
200
- fireEvent.click(button);
201
-
202
- const optionToSelect = getByLabelText('option Option 2');
203
- fireEvent.click(optionToSelect);
204
-
205
- expect(mockSetSelectedOption).toHaveBeenCalledWith('2');
206
- expect(button).toHaveTextContent('Option 2');
207
- });
208
-
209
- it('should support disabled state', () => {
210
- const { getByRole } = renderComponent({ disabled: true });
211
- const button = getByRole('button', { name: /Select options/i });
212
- expect(button).toBeDisabled();
213
- });
214
-
215
- it('should handle custom classes without affecting accessibility', async () => {
216
- const { container } = renderComponent({ className: 'custom-class' });
217
- const results = await axe(container);
218
- expect(results).toHaveNoViolations();
219
- });
220
-
221
- it('should handle an empty options array gracefully', () => {
222
- const { queryByRole } = renderComponent({ options: [] });
223
- const dropdown = queryByRole('listbox');
224
- expect(dropdown).not.toBeInTheDocument();
225
- });
226
-
227
- it('should close the dropdown when clicking outside', () => {
228
- const { getByRole, container } = renderComponent();
229
- const button = getByRole('button', { name: /Options/i });
230
- fireEvent.click(button);
231
-
232
- fireEvent.mouseDown(container);
233
- expect(button).toHaveAttribute('aria-expanded', 'false');
234
- });
235
-
236
-
237
- it('should maintain focus management for keyboard users', () => {
238
- const options = [
239
- { name: 'Option 1', value: '1' },
240
- { name: 'Option 2', value: '2' },
241
- { name: 'Option 3', value: '3' },
242
- ];
243
-
244
- const { getByRole, getByLabelText } = renderComponent({
245
- options,
246
- optionsLabel: 'Choose an option',
247
- });
248
-
249
- const button = getByRole('button', { name: /Choose an option/i });
250
- fireEvent.click(button); // Open the dropdown
251
-
252
- const firstOption = getByLabelText('option Option 1');
253
- fireEvent.keyDown(firstOption, { key: 'Enter', code: 'Enter' }); // Simulate Enter key press
254
- // fireEvent.click(firstOption); // works, meaning keyboard is NOT. Need to fix ...
255
-
256
- expect(mockSetSelectedOption).toHaveBeenCalledWith('1'); // Verify correct value is selected
257
- expect(button).toHaveTextContent('Option 1'); // Verify button displays the selected option
258
- });
259
-
260
- it('should move focus to the next option with ArrowDown key', () => {
261
- renderComponent();
262
- const button = screen.getByRole('button', { name: /Select options/i });
263
- fireEvent.click(button);
264
-
265
- const listbox = screen.getByRole('listbox');
266
-
267
- // Press ArrowDown
268
- fireEvent.keyDown(listbox, { key: 'ArrowDown' });
269
-
270
- // Second option should be focused (because the first key press advances the focus to first option)
271
- expect(screen.getByRole('option', { name: 'option Option 2' })).toHaveFocus();
272
-
273
- // Press ArrowDown again
274
- fireEvent.keyDown(listbox, { key: 'ArrowDown' });
275
-
276
- // Third option should now be focused
277
- expect(screen.getByRole('option', { name: 'option Option 3' })).toHaveFocus();
278
- });
279
-
280
- it('should move focus to the previous option with ArrowUp key', () => {
281
- renderComponent();
282
- const button = screen.getByRole('button', { name: /Select options/i });
283
- fireEvent.click(button);
284
-
285
- const listbox = screen.getByRole('listbox');
286
-
287
- // Press ArrowUp
288
- fireEvent.keyDown(listbox, { key: 'ArrowUp' });
289
-
290
- // Wraps around to the last option
291
- expect(screen.getByRole('option', { name: 'option Option 3' })).toHaveFocus();
292
-
293
- // Press ArrowUp again
294
- fireEvent.keyDown(listbox, { key: 'ArrowUp' });
295
-
296
- // Second option should be focused
297
- expect(screen.getByRole('option', { name: 'option Option 2' })).toHaveFocus();
298
- });
299
-
300
-
301
- it('should move focus to the first option with Home key', () => {
302
- renderComponent();
303
- const button = screen.getByRole('button', { name: /Select options/i });
304
- fireEvent.click(button);
305
-
306
- const listbox = screen.getByRole('listbox');
307
-
308
- // Press Home
309
- fireEvent.keyDown(listbox, { key: 'Home' });
310
-
311
- // First option should be focused
312
- expect(screen.getByRole('option', { name: 'option Option 1' })).toHaveFocus();
313
- });
314
-
315
- it('should move focus to the last option with End key', () => {
316
- renderComponent();
317
- const button = screen.getByRole('button', { name: /Select options/i });
318
- fireEvent.click(button);
319
-
320
- const listbox = screen.getByRole('listbox');
321
-
322
- // Press End
323
- fireEvent.keyDown(listbox, { key: 'End' });
324
-
325
- // Last option should be focused
326
- expect(screen.getByRole('option', { name: 'option Option 3' })).toHaveFocus();
327
- });
328
-
329
- // it('should call preventDefault for navigation keys', () => {
330
- // renderComponent();
331
- // const button = screen.getByRole('button', { name: /Select options/i });
332
- // fireEvent.click(button);
333
-
334
- // const listbox = screen.getByRole('listbox');
335
-
336
- // // Spy on preventDefault
337
- // const preventDefaultSpy = vi.fn();
338
-
339
- // // Simulate the keydown event
340
- // fireEvent.keyDown(listbox, {
341
- // key: 'ArrowDown',
342
- // preventDefault: preventDefaultSpy,
343
- // });
344
-
345
- // // Ensure preventDefault was called
346
- // expect(preventDefaultSpy).toHaveBeenCalled();
347
- // });
348
-
349
- it('should close the select box when Escape key is pressed', () => {
350
- // Render the component
351
- renderComponent();
352
-
353
- // Open the dropdown
354
- const button = screen.getByRole('button', { name: /Select options/i });
355
- fireEvent.click(button);
356
-
357
- // Verify that the dropdown is open
358
- expect(screen.getByRole('listbox')).toBeInTheDocument();
359
-
360
- // Press the Escape key
361
- fireEvent.keyDown(screen.getByRole('listbox'), { key: 'Escape' });
362
-
363
- // Verify that the dropdown is closed
364
- expect(screen.queryByRole('listbox')).not.toBeInTheDocument();
365
- });
366
-
367
- it('should close the dropdown when focus moves outside', () => {
368
- renderComponent();
369
-
370
- // Open the dropdown
371
- const button = screen.getByRole('button', { name: /Select options/i });
372
- fireEvent.click(button);
373
- expect(screen.getByRole('listbox')).toBeInTheDocument();
374
-
375
- // Simulate focus moving outside
376
- fireEvent.focusIn(document.body);
377
-
378
- // Verify the dropdown is closed
379
- expect(screen.queryByRole('listbox')).not.toBeInTheDocument();
380
- });
381
-
382
- it('should select an option when Enter is pressed (using event.code)', () => {
383
- renderComponent();
384
-
385
- // Open the dropdown
386
- const button = screen.getByRole('button', { name: /Select options/i });
387
- fireEvent.click(button);
388
-
389
- // Verify that the dropdown is open
390
- expect(screen.getByRole('listbox')).toBeInTheDocument();
391
-
392
- // Press Enter (using event.code)
393
- fireEvent.keyDown(screen.getByRole('option', { name: 'option Option 1' }), {
394
- code: 'Enter',
395
- });
396
-
397
- // Verify that the dropdown is closed
398
- expect(screen.queryByRole('listbox')).not.toBeInTheDocument();
399
- });
400
-
401
- it('should select an option when Space key is pressed', () => {
402
- renderComponent();
403
-
404
- // Open the dropdown
405
- const button = screen.getByRole('button', { name: /Select options/i });
406
- fireEvent.click(button);
407
-
408
- // Verify that the dropdown is open
409
- expect(screen.getByRole('listbox')).toBeInTheDocument();
410
-
411
- // Press Space key
412
- fireEvent.keyDown(screen.getByRole('option', { name: 'option Option 1' }), {
413
- key: ' ',
414
- });
415
-
416
- // Verify that the dropdown is closed
417
- expect(screen.queryByRole('listbox')).not.toBeInTheDocument();
418
- });
419
-
420
- it('should close the select box when Escape key is pressed without selecting an option', () => {
421
- renderComponent();
422
-
423
- // Open the dropdown
424
- const button = screen.getByRole('button', { name: /Select options/i });
425
- fireEvent.click(button);
426
-
427
- // Verify that the dropdown is open
428
- expect(screen.getByRole('listbox')).toBeInTheDocument();
429
-
430
- // Press Escape key on an option
431
- fireEvent.keyDown(screen.getByRole('option', { name: 'option Option 1' }), {
432
- key: 'Escape',
433
- });
434
-
435
- // Verify that the dropdown is closed
436
- expect(screen.queryByRole('listbox')).not.toBeInTheDocument();
437
-
438
- // Ensure no option was selected
439
- expect(button).toHaveTextContent('Options');
440
- });
441
-
442
- it("positions dropdown above the button when there is insufficient space below", () => {
443
- // Set global dimensions so that containerHeight is 600.
444
- Object.defineProperty(window, "innerHeight", {
445
- writable: true,
446
- configurable: true,
447
- value: 600,
448
- });
449
- Object.defineProperty(document.body, "offsetHeight", {
450
- writable: true,
451
- configurable: true,
452
- value: 600,
453
- });
454
-
455
- const setSelectedOptionMock = vi.fn();
456
- const { container } = render(<Select options={options} setSelectedOption={setSelectedOptionMock} />);
457
-
458
- // Override container's bounding rect to simulate a button with top:200 and bottom:500.
459
- const outerContainer = container.firstChild as HTMLElement;
460
- outerContainer.getBoundingClientRect = () => ({
461
- top: 200,
462
- bottom: 500, // available space below = 600 - 500 = 100
463
- left: 0,
464
- right: 600,
465
- width: 600,
466
- height: 300,
467
- x: 0,
468
- y: 0,
469
- toJSON: () => {('')}
470
- });
471
-
472
- // Save the original descriptor for offsetHeight.
473
- const originalOffsetHeight = Object.getOwnPropertyDescriptor(HTMLDivElement.prototype, 'offsetHeight');
474
- // Override offsetHeight so that any element with role "listbox" reports a height of 150.
475
- Object.defineProperty(HTMLDivElement.prototype, 'offsetHeight', {
476
- configurable: true,
477
- get: function () {
478
- if (this.getAttribute && this.getAttribute('role') === 'listbox') {
479
- return 150;
480
- }
481
- return originalOffsetHeight && originalOffsetHeight.get ? originalOffsetHeight.get.call(this) : 0;
482
- },
483
- });
484
-
485
- // Open the dropdown so that updateDropdownPosition runs.
486
- const button = screen.getByRole("button");
487
- fireEvent.click(button);
488
-
489
- // With spaceBelow (100) < dropdownHeight (150) and container top (200) > 150,
490
- // updateDropdownPosition should set isAbove to true, causing the button to have "rounded-b-lg".
491
- expect(button.className).toMatch(/rounded-b-lg/);
492
- });
493
- });