uikit-react-public 0.30.1 → 0.32.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.
Files changed (49) hide show
  1. package/dist/components/Heading/Heading.stories.d.ts +5 -7
  2. package/dist/components/MenuNew/Menu.context.d.ts +9 -0
  3. package/dist/components/MenuNew/Menu.d.ts +2 -1
  4. package/dist/components/MenuNew/MenuHeading.d.ts +1 -1
  5. package/dist/components/MenuNew/MenuItemText.d.ts +2 -1
  6. package/dist/components/MenuNew/PrimaryMenuItem.d.ts +1 -1
  7. package/dist/components/Select/Select.types.d.ts +1 -1
  8. package/dist/components/Sidebar/Sidebar.d.ts +12 -0
  9. package/dist/components/Sidebar/Sidebar.stories.d.ts +38 -0
  10. package/dist/components/Sidebar/__tests__/Sidebar.test.d.ts +1 -0
  11. package/dist/components/Sidebar/index.d.ts +2 -0
  12. package/dist/components/Snackbar/Snackbar.d.ts +9 -5
  13. package/dist/components/Snackbar/Snackbar.stories.d.ts +2 -1
  14. package/dist/components/Snackbar/Snackbars.d.ts +19 -0
  15. package/dist/components/Snackbar/Snackbars.stories.d.ts +52 -0
  16. package/dist/components/Snackbar/__tests__/Snackbars.test.d.ts +1 -0
  17. package/dist/components/Snackbar/index.d.ts +2 -0
  18. package/dist/components/index.d.ts +4 -1
  19. package/dist/index.js +5740 -5217
  20. package/lib/components/Dropdown/Dropdown.stories.tsx +69 -74
  21. package/lib/components/Heading/Documentation.mdx +4 -14
  22. package/lib/components/Heading/Heading.stories.tsx +16 -30
  23. package/lib/components/MenuNew/Menu.context.tsx +18 -0
  24. package/lib/components/MenuNew/Menu.tsx +14 -9
  25. package/lib/components/MenuNew/MenuDivider.tsx +12 -1
  26. package/lib/components/MenuNew/MenuHeading.tsx +6 -0
  27. package/lib/components/MenuNew/MenuItemText.tsx +27 -3
  28. package/lib/components/MenuNew/MenuSection.tsx +11 -0
  29. package/lib/components/MenuNew/PrimaryMenuItem.tsx +71 -2
  30. package/lib/components/Select/Select.types.ts +1 -7
  31. package/lib/components/Select/__tests__/Select.test.tsx +112 -0
  32. package/lib/components/Select/__tests__/__snapshots__/Select.test.tsx.snap +5 -0
  33. package/lib/components/Select/subcomponents/CustomSelect.tsx +75 -3
  34. package/lib/components/Select/subcomponents/FilterInput.tsx +1 -1
  35. package/lib/components/Sidebar/Sidebar.stories.tsx +94 -0
  36. package/lib/components/Sidebar/Sidebar.tsx +208 -0
  37. package/lib/components/Sidebar/__tests__/Sidebar.test.tsx +96 -0
  38. package/lib/components/Sidebar/__tests__/__snapshots__/Sidebar.test.tsx.snap +272 -0
  39. package/lib/components/Sidebar/index.ts +2 -0
  40. package/lib/components/Snackbar/Snackbar.stories.tsx +11 -0
  41. package/lib/components/Snackbar/Snackbar.tsx +162 -118
  42. package/lib/components/Snackbar/Snackbars.stories.tsx +130 -0
  43. package/lib/components/Snackbar/Snackbars.tsx +377 -0
  44. package/lib/components/Snackbar/__tests__/Snackbar.test.tsx +104 -0
  45. package/lib/components/Snackbar/__tests__/Snackbars.test.tsx +377 -0
  46. package/lib/components/Snackbar/__tests__/__snapshots__/Snackbar.test.tsx.snap +36 -19
  47. package/lib/components/Snackbar/index.ts +6 -0
  48. package/lib/components/index.ts +10 -1
  49. package/package.json +1 -1
@@ -0,0 +1,377 @@
1
+ import { act, render, screen } from '@testing-library/react';
2
+ import { afterEach, beforeEach, describe, expect, test, vi } from 'vitest';
3
+ import { ThemeContextProvider } from '../../../theme/useTheme';
4
+ import Snackbars, { type SnackbarsItem } from '../Snackbars';
5
+
6
+ const renderSnackbars = (
7
+ items: SnackbarsItem[],
8
+ onDismiss = vi.fn(),
9
+ props: Partial<React.ComponentProps<typeof Snackbars>> = {}
10
+ ) =>
11
+ render(
12
+ <ThemeContextProvider>
13
+ <Snackbars
14
+ items={items}
15
+ onDismiss={onDismiss}
16
+ duration={0}
17
+ {...props}
18
+ />
19
+ </ThemeContextProvider>
20
+ );
21
+
22
+ describe('Snackbars', () => {
23
+ beforeEach(() => {
24
+ vi.useFakeTimers();
25
+ });
26
+
27
+ afterEach(() => {
28
+ vi.clearAllTimers();
29
+ vi.useRealTimers();
30
+ });
31
+
32
+ test('renders a single snackbar with default lifecycle wrapper', () => {
33
+ renderSnackbars([{ id: 'saved', children: 'Saved' }]);
34
+
35
+ expect(screen.getByTestId('ucl-uikit-snackbars')).toBeDefined();
36
+ expect(screen.getByTestId('ucl-uikit-snackbars-saved')).toBeDefined();
37
+ expect(screen.getByText('Saved')).toBeDefined();
38
+ });
39
+
40
+ test('passes snackbar props through to each rendered snackbar', () => {
41
+ const onFocus = vi.fn();
42
+ const onBlur = vi.fn();
43
+
44
+ renderSnackbars([
45
+ {
46
+ id: 'saved',
47
+ children: 'Saved',
48
+ closeButtonAriaLabel: 'Close saved notification',
49
+ onFocus,
50
+ onBlur,
51
+ tabIndex: 0,
52
+ title: 'Saved notification',
53
+ },
54
+ ]);
55
+
56
+ const snackbar = screen.getByTestId('ucl-uikit-snackbars-saved');
57
+
58
+ expect(snackbar).toHaveAttribute('title', 'Saved notification');
59
+ expect(
60
+ screen.getByLabelText('Close saved notification')
61
+ ).toBeInTheDocument();
62
+
63
+ act(() => {
64
+ snackbar.focus();
65
+ });
66
+
67
+ expect(onFocus).toHaveBeenCalledTimes(1);
68
+
69
+ act(() => {
70
+ snackbar.blur();
71
+ });
72
+
73
+ expect(onBlur).toHaveBeenCalledTimes(1);
74
+ });
75
+
76
+ test('renders up to maxVisible snackbars', () => {
77
+ renderSnackbars(
78
+ [
79
+ { id: 'one', children: 'One' },
80
+ { id: 'two', children: 'Two' },
81
+ { id: 'three', children: 'Three' },
82
+ ],
83
+ vi.fn(),
84
+ { maxVisible: 2 }
85
+ );
86
+
87
+ expect(screen.getByText('One')).toBeDefined();
88
+ expect(screen.getByText('Two')).toBeDefined();
89
+ expect(screen.queryByText('Three')).toBeNull();
90
+ });
91
+
92
+ test('close action dismisses after transition duration', () => {
93
+ const onDismiss = vi.fn();
94
+ renderSnackbars([{ id: 'saved', children: 'Saved' }], onDismiss, {
95
+ transitionDuration: 200,
96
+ });
97
+
98
+ const closeButton = screen.getByTestId(
99
+ 'ucl-uikit-snackbars-saved-close-button'
100
+ );
101
+
102
+ act(() => {
103
+ closeButton.click();
104
+ });
105
+
106
+ expect(closeButton).toBeDisabled();
107
+ expect(onDismiss).not.toHaveBeenCalled();
108
+
109
+ act(() => {
110
+ vi.advanceTimersByTime(200);
111
+ });
112
+
113
+ expect(onDismiss).toHaveBeenCalledWith('saved');
114
+ });
115
+
116
+ test('close action is disabled while exiting', () => {
117
+ const onClose = vi.fn();
118
+ const onDismiss = vi.fn();
119
+ renderSnackbars([{ id: 'saved', children: 'Saved', onClose }], onDismiss, {
120
+ transitionDuration: 200,
121
+ });
122
+
123
+ const closeButton = screen.getByTestId(
124
+ 'ucl-uikit-snackbars-saved-close-button'
125
+ );
126
+
127
+ act(() => {
128
+ closeButton.click();
129
+ closeButton.click();
130
+ });
131
+
132
+ expect(closeButton).toBeDisabled();
133
+ expect(onClose).toHaveBeenCalledTimes(1);
134
+ expect(onDismiss).not.toHaveBeenCalled();
135
+ });
136
+
137
+ test('undo action calls onUndo and dismisses after transition duration', () => {
138
+ const onDismiss = vi.fn();
139
+ const onUndo = vi.fn();
140
+ renderSnackbars(
141
+ [
142
+ {
143
+ id: 'undoable',
144
+ action: 'undo',
145
+ children: 'Undoable',
146
+ onUndo,
147
+ },
148
+ ],
149
+ onDismiss,
150
+ { transitionDuration: 200 }
151
+ );
152
+
153
+ act(() => {
154
+ screen.getByTestId('ucl-uikit-snackbars-undoable-undo-button').click();
155
+ });
156
+
157
+ expect(
158
+ screen.getByTestId('ucl-uikit-snackbars-undoable-undo-button')
159
+ ).toBeDisabled();
160
+ expect(onUndo).toHaveBeenCalledTimes(1);
161
+ expect(onDismiss).not.toHaveBeenCalled();
162
+
163
+ act(() => {
164
+ vi.advanceTimersByTime(200);
165
+ });
166
+
167
+ expect(onDismiss).toHaveBeenCalledWith('undoable');
168
+ });
169
+
170
+ test('undo action is disabled while exiting', () => {
171
+ const onDismiss = vi.fn();
172
+ const onUndo = vi.fn();
173
+ renderSnackbars(
174
+ [
175
+ {
176
+ id: 'undoable',
177
+ action: 'undo',
178
+ children: 'Undoable',
179
+ onUndo,
180
+ },
181
+ ],
182
+ onDismiss,
183
+ { transitionDuration: 200 }
184
+ );
185
+
186
+ const undoButton = screen.getByTestId(
187
+ 'ucl-uikit-snackbars-undoable-undo-button'
188
+ );
189
+
190
+ act(() => {
191
+ undoButton.click();
192
+ undoButton.click();
193
+ });
194
+
195
+ expect(undoButton).toBeDisabled();
196
+ expect(onUndo).toHaveBeenCalledTimes(1);
197
+ expect(onDismiss).not.toHaveBeenCalled();
198
+ });
199
+
200
+ test('auto-dismisses after duration and transition duration', () => {
201
+ const onDismiss = vi.fn();
202
+ renderSnackbars([{ id: 'saved', children: 'Saved' }], onDismiss, {
203
+ duration: 100,
204
+ transitionDuration: 200,
205
+ });
206
+
207
+ act(() => {
208
+ vi.advanceTimersByTime(100);
209
+ });
210
+
211
+ expect(onDismiss).not.toHaveBeenCalled();
212
+
213
+ act(() => {
214
+ vi.advanceTimersByTime(200);
215
+ });
216
+
217
+ expect(onDismiss).toHaveBeenCalledWith('saved');
218
+ });
219
+
220
+ test('does not auto-dismiss when duration is zero', () => {
221
+ const onDismiss = vi.fn();
222
+ renderSnackbars([{ id: 'saved', children: 'Saved' }], onDismiss, {
223
+ duration: 0,
224
+ transitionDuration: 50,
225
+ });
226
+
227
+ act(() => {
228
+ vi.advanceTimersByTime(1000);
229
+ });
230
+
231
+ expect(onDismiss).not.toHaveBeenCalled();
232
+ });
233
+
234
+ test('undo action still auto-dismisses when duration is zero', () => {
235
+ const onDismiss = vi.fn();
236
+ renderSnackbars(
237
+ [{ id: 'undoable', action: 'undo', children: 'Undoable' }],
238
+ onDismiss,
239
+ {
240
+ duration: 0,
241
+ transitionDuration: 50,
242
+ }
243
+ );
244
+
245
+ act(() => {
246
+ vi.advanceTimersByTime(5000);
247
+ });
248
+
249
+ expect(onDismiss).not.toHaveBeenCalled();
250
+
251
+ act(() => {
252
+ vi.advanceTimersByTime(50);
253
+ });
254
+
255
+ expect(onDismiss).toHaveBeenCalledWith('undoable');
256
+ });
257
+
258
+ test('undo action item duration zero falls back to the collection duration', () => {
259
+ const onDismiss = vi.fn();
260
+ renderSnackbars(
261
+ [
262
+ {
263
+ id: 'undoable',
264
+ action: 'undo',
265
+ children: 'Undoable',
266
+ duration: 0,
267
+ },
268
+ ],
269
+ onDismiss,
270
+ {
271
+ duration: 100,
272
+ transitionDuration: 50,
273
+ }
274
+ );
275
+
276
+ act(() => {
277
+ vi.advanceTimersByTime(100);
278
+ });
279
+
280
+ expect(onDismiss).not.toHaveBeenCalled();
281
+
282
+ act(() => {
283
+ vi.advanceTimersByTime(50);
284
+ });
285
+
286
+ expect(onDismiss).toHaveBeenCalledWith('undoable');
287
+ });
288
+
289
+ test('pauses auto-dismiss while a snackbar has focus', () => {
290
+ const onDismiss = vi.fn();
291
+ renderSnackbars([{ id: 'saved', children: 'Saved' }], onDismiss, {
292
+ duration: 100,
293
+ transitionDuration: 50,
294
+ });
295
+
296
+ act(() => {
297
+ vi.advanceTimersByTime(60);
298
+ });
299
+
300
+ act(() => {
301
+ screen.getByTestId('ucl-uikit-snackbars-saved-close-button').focus();
302
+ });
303
+
304
+ act(() => {
305
+ vi.advanceTimersByTime(500);
306
+ });
307
+
308
+ expect(onDismiss).not.toHaveBeenCalled();
309
+
310
+ act(() => {
311
+ screen.getByTestId('ucl-uikit-snackbars-saved-close-button').blur();
312
+ });
313
+
314
+ act(() => {
315
+ vi.advanceTimersByTime(39);
316
+ });
317
+
318
+ expect(onDismiss).not.toHaveBeenCalled();
319
+
320
+ act(() => {
321
+ vi.advanceTimersByTime(1);
322
+ });
323
+
324
+ expect(onDismiss).not.toHaveBeenCalled();
325
+
326
+ act(() => {
327
+ vi.advanceTimersByTime(50);
328
+ });
329
+
330
+ expect(onDismiss).toHaveBeenCalledWith('saved');
331
+ });
332
+
333
+ test('adding a snackbar does not refresh existing snackbar duration timers', () => {
334
+ const onDismiss = vi.fn();
335
+ const { rerender } = render(
336
+ <ThemeContextProvider>
337
+ <Snackbars
338
+ items={[{ id: 'first', children: 'First' }]}
339
+ onDismiss={onDismiss}
340
+ duration={100}
341
+ transitionDuration={50}
342
+ />
343
+ </ThemeContextProvider>
344
+ );
345
+
346
+ act(() => {
347
+ vi.advanceTimersByTime(75);
348
+ });
349
+
350
+ rerender(
351
+ <ThemeContextProvider>
352
+ <Snackbars
353
+ items={[
354
+ { id: 'first', children: 'First' },
355
+ { id: 'second', children: 'Second' },
356
+ ]}
357
+ onDismiss={onDismiss}
358
+ duration={100}
359
+ transitionDuration={50}
360
+ />
361
+ </ThemeContextProvider>
362
+ );
363
+
364
+ act(() => {
365
+ vi.advanceTimersByTime(25);
366
+ });
367
+
368
+ expect(onDismiss).not.toHaveBeenCalled();
369
+
370
+ act(() => {
371
+ vi.advanceTimersByTime(50);
372
+ });
373
+
374
+ expect(onDismiss).toHaveBeenCalledTimes(1);
375
+ expect(onDismiss).toHaveBeenCalledWith('first');
376
+ });
377
+ });
@@ -1,22 +1,23 @@
1
1
  // Vitest Snapshot v1, https://vitest.dev/guide/snapshot.html
2
2
 
3
3
  exports[`Snackbar > snapshot: custom test id 1`] = `
4
- <output
5
- class="ucl-uikit-snackbar css-1rhqqf3"
4
+ <div
5
+ class="ucl-uikit-snackbar css-10q5wpo"
6
6
  data-testid="custom-test-id"
7
7
  >
8
8
  <svg
9
- class="ucl-uikit-icon css-1uzxprk"
9
+ aria-hidden="true"
10
+ class="ucl-uikit-icon css-1qgh0wp"
10
11
  data-testid="ucl-uikit-icon"
11
12
  fill="none"
12
13
  focusable="false"
13
- height="24"
14
+ height="32"
14
15
  stroke="currentColor"
15
16
  stroke-linecap="round"
16
17
  stroke-linejoin="round"
17
18
  stroke-width="2"
18
19
  viewBox="0 0 24 24"
19
- width="24"
20
+ width="32"
20
21
  xmlns="http://www.w3.org/2000/svg"
21
22
  >
22
23
  <path
@@ -26,39 +27,49 @@ exports[`Snackbar > snapshot: custom test id 1`] = `
26
27
  d="M22 4 12 14.01l-3-3"
27
28
  />
28
29
  </svg>
29
- <span>
30
+ <span
31
+ class="ucl-uikit-text css-5w4evg"
32
+ data-testid="ucl-uikit-text"
33
+ role="status"
34
+ >
30
35
  Test snackbar
31
36
  </span>
32
37
  <span
33
- class="css-1l2xg72"
38
+ class="css-1xjm0bq"
34
39
  >
35
40
  <button
36
- class="css-1nj6pk0"
41
+ class="css-bt8z9v"
37
42
  data-testid="custom-test-id-undo-button"
38
43
  >
39
- UNDO
44
+ <span
45
+ class="ucl-uikit-text css-1ccjqc4"
46
+ data-testid="ucl-uikit-text"
47
+ >
48
+ UNDO
49
+ </span>
40
50
  </button>
41
51
  </span>
42
- </output>
52
+ </div>
43
53
  `;
44
54
 
45
55
  exports[`Snackbar > snapshot: minimal props 1`] = `
46
- <output
47
- class="ucl-uikit-snackbar css-1rhqqf3"
56
+ <div
57
+ class="ucl-uikit-snackbar css-10q5wpo"
48
58
  data-testid="ucl-uikit-snackbar"
49
59
  >
50
60
  <svg
51
- class="ucl-uikit-icon css-1uzxprk"
61
+ aria-hidden="true"
62
+ class="ucl-uikit-icon css-1qgh0wp"
52
63
  data-testid="ucl-uikit-icon"
53
64
  fill="none"
54
65
  focusable="false"
55
- height="24"
66
+ height="32"
56
67
  stroke="currentColor"
57
68
  stroke-linecap="round"
58
69
  stroke-linejoin="round"
59
70
  stroke-width="2"
60
71
  viewBox="0 0 24 24"
61
- width="24"
72
+ width="32"
62
73
  xmlns="http://www.w3.org/2000/svg"
63
74
  >
64
75
  <path
@@ -68,17 +79,23 @@ exports[`Snackbar > snapshot: minimal props 1`] = `
68
79
  d="M22 4 12 14.01l-3-3"
69
80
  />
70
81
  </svg>
71
- <span>
82
+ <span
83
+ class="ucl-uikit-text css-5w4evg"
84
+ data-testid="ucl-uikit-text"
85
+ role="status"
86
+ >
72
87
  Test snackbar
73
88
  </span>
74
89
  <span
75
- class="css-1l2xg72"
90
+ class="css-1xjm0bq"
76
91
  >
77
92
  <button
78
- class="ucl-uikit-icon-button css-14jzv8g"
93
+ aria-label="Dismiss notification"
94
+ class="ucl-uikit-icon-button css-wreorn"
79
95
  data-testid="ucl-uikit-snackbar-close-button"
80
96
  >
81
97
  <svg
98
+ aria-hidden="true"
82
99
  class="ucl-uikit-icon css-148hpxb"
83
100
  data-testid="ucl-uikit-icon"
84
101
  fill="none"
@@ -98,5 +115,5 @@ exports[`Snackbar > snapshot: minimal props 1`] = `
98
115
  </svg>
99
116
  </button>
100
117
  </span>
101
- </output>
118
+ </div>
102
119
  `;
@@ -1,2 +1,8 @@
1
1
  export { default } from './Snackbar';
2
+ export { default as Snackbars } from './Snackbars';
2
3
  export type { SnackbarProps } from './Snackbar';
4
+ export type {
5
+ SnackbarsItem,
6
+ SnackbarsPlacement,
7
+ SnackbarsProps,
8
+ } from './Snackbars';
@@ -26,7 +26,13 @@ export { default as Overlay } from './Overlay';
26
26
  export type { OverlayProps } from './Overlay';
27
27
 
28
28
  export { default as Snackbar } from './Snackbar';
29
- export type { SnackbarProps } from './Snackbar';
29
+ export { Snackbars } from './Snackbar';
30
+ export type {
31
+ SnackbarProps,
32
+ SnackbarsItem,
33
+ SnackbarsPlacement,
34
+ SnackbarsProps,
35
+ } from './Snackbar';
30
36
 
31
37
  export { default as Avatar } from './Avatar';
32
38
  export type { AvatarProps } from './Avatar';
@@ -207,6 +213,9 @@ export type { DropdownMenuProps } from './DropdownMenu';
207
213
  export { default as DrawerMenu } from './DrawerMenu';
208
214
  export type { DrawerMenuProps } from './DrawerMenu';
209
215
 
216
+ export { default as Sidebar } from './Sidebar';
217
+ export type { SidebarProps } from './Sidebar';
218
+
210
219
  export { default as DropdownUserMenu } from './DropdownUserMenu';
211
220
  export type { DropdownUserMenuProps } from './DropdownUserMenu';
212
221
 
package/package.json CHANGED
@@ -2,7 +2,7 @@
2
2
  "name": "uikit-react-public",
3
3
  "private": false,
4
4
  "license": "UNLICENSED",
5
- "version": "0.30.1",
5
+ "version": "0.32.3",
6
6
  "type": "module",
7
7
  "main": "dist/index.js",
8
8
  "types": "dist/index.d.ts",