@dxos/react-ui-searchlist 0.8.4-main.84f28bd → 0.8.4-main.937b3ca

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 (78) hide show
  1. package/dist/lib/browser/index.mjs +744 -266
  2. package/dist/lib/browser/index.mjs.map +4 -4
  3. package/dist/lib/browser/meta.json +1 -1
  4. package/dist/lib/node-esm/index.mjs +744 -266
  5. package/dist/lib/node-esm/index.mjs.map +4 -4
  6. package/dist/lib/node-esm/meta.json +1 -1
  7. package/dist/types/src/components/Combobox/Combobox.d.ts +84 -0
  8. package/dist/types/src/components/Combobox/Combobox.d.ts.map +1 -0
  9. package/dist/types/src/components/Combobox/Combobox.stories.d.ts +21 -0
  10. package/dist/types/src/components/Combobox/Combobox.stories.d.ts.map +1 -0
  11. package/dist/types/src/components/Combobox/index.d.ts +2 -0
  12. package/dist/types/src/components/Combobox/index.d.ts.map +1 -0
  13. package/dist/types/src/components/Listbox/Listbox.d.ts +31 -0
  14. package/dist/types/src/components/Listbox/Listbox.d.ts.map +1 -0
  15. package/dist/types/src/components/Listbox/Listbox.stories.d.ts +21 -0
  16. package/dist/types/src/components/Listbox/Listbox.stories.d.ts.map +1 -0
  17. package/dist/types/src/components/Listbox/index.d.ts +2 -0
  18. package/dist/types/src/components/Listbox/index.d.ts.map +1 -0
  19. package/dist/types/src/components/SearchList/SearchList.d.ts +87 -0
  20. package/dist/types/src/components/SearchList/SearchList.d.ts.map +1 -0
  21. package/dist/types/src/components/SearchList/SearchList.stories.d.ts +28 -0
  22. package/dist/types/src/components/SearchList/SearchList.stories.d.ts.map +1 -0
  23. package/dist/types/src/components/SearchList/context.d.ts +33 -0
  24. package/dist/types/src/components/SearchList/context.d.ts.map +1 -0
  25. package/dist/types/src/components/SearchList/hooks/index.d.ts +5 -0
  26. package/dist/types/src/components/SearchList/hooks/index.d.ts.map +1 -0
  27. package/dist/types/src/components/SearchList/hooks/useGlobalFilter.d.ts +34 -0
  28. package/dist/types/src/components/SearchList/hooks/useGlobalFilter.d.ts.map +1 -0
  29. package/dist/types/src/components/SearchList/hooks/useSearchListInput.d.ts +12 -0
  30. package/dist/types/src/components/SearchList/hooks/useSearchListInput.d.ts.map +1 -0
  31. package/dist/types/src/components/SearchList/hooks/useSearchListItem.d.ts +10 -0
  32. package/dist/types/src/components/SearchList/hooks/useSearchListItem.d.ts.map +1 -0
  33. package/dist/types/src/components/SearchList/hooks/useSearchListResults.d.ts +36 -0
  34. package/dist/types/src/components/SearchList/hooks/useSearchListResults.d.ts.map +1 -0
  35. package/dist/types/src/components/SearchList/index.d.ts +3 -0
  36. package/dist/types/src/components/SearchList/index.d.ts.map +1 -0
  37. package/dist/types/src/components/index.d.ts +2 -0
  38. package/dist/types/src/components/index.d.ts.map +1 -1
  39. package/dist/types/src/index.d.ts +0 -1
  40. package/dist/types/src/index.d.ts.map +1 -1
  41. package/dist/types/src/translations.d.ts +4 -2
  42. package/dist/types/src/translations.d.ts.map +1 -1
  43. package/dist/types/tsconfig.tsbuildinfo +1 -1
  44. package/package.json +23 -19
  45. package/src/components/Combobox/Combobox.stories.tsx +62 -0
  46. package/src/components/Combobox/Combobox.tsx +356 -0
  47. package/src/components/Combobox/index.ts +5 -0
  48. package/src/components/Listbox/Listbox.stories.tsx +53 -0
  49. package/src/components/Listbox/Listbox.tsx +214 -0
  50. package/src/components/Listbox/index.ts +5 -0
  51. package/src/components/SearchList/SearchList.stories.tsx +534 -0
  52. package/src/components/SearchList/SearchList.tsx +559 -0
  53. package/src/components/SearchList/context.ts +43 -0
  54. package/src/components/SearchList/hooks/index.ts +8 -0
  55. package/src/components/SearchList/hooks/useGlobalFilter.tsx +61 -0
  56. package/src/components/SearchList/hooks/useSearchListInput.ts +14 -0
  57. package/src/components/SearchList/hooks/useSearchListItem.ts +14 -0
  58. package/src/components/SearchList/hooks/useSearchListResults.ts +104 -0
  59. package/src/components/SearchList/index.ts +6 -0
  60. package/src/components/index.ts +2 -0
  61. package/src/index.ts +0 -1
  62. package/src/translations.ts +4 -2
  63. package/src/types/command-score.d.ts +16 -0
  64. package/dist/types/src/components/SearchList.d.ts +0 -44
  65. package/dist/types/src/components/SearchList.d.ts.map +0 -1
  66. package/dist/types/src/components/SearchList.stories.d.ts +0 -15
  67. package/dist/types/src/components/SearchList.stories.d.ts.map +0 -1
  68. package/dist/types/src/composites/PopoverCombobox.d.ts +0 -32
  69. package/dist/types/src/composites/PopoverCombobox.d.ts.map +0 -1
  70. package/dist/types/src/composites/PopoverCombobox.stories.d.ts +0 -28
  71. package/dist/types/src/composites/PopoverCombobox.stories.d.ts.map +0 -1
  72. package/dist/types/src/composites/index.d.ts +0 -2
  73. package/dist/types/src/composites/index.d.ts.map +0 -1
  74. package/src/components/SearchList.stories.tsx +0 -47
  75. package/src/components/SearchList.tsx +0 -250
  76. package/src/composites/PopoverCombobox.stories.tsx +0 -44
  77. package/src/composites/PopoverCombobox.tsx +0 -186
  78. package/src/composites/index.ts +0 -5
@@ -0,0 +1,534 @@
1
+ //
2
+ // Copyright 2025 DXOS.org
3
+ //
4
+
5
+ import { type Meta, type StoryObj } from '@storybook/react-vite';
6
+ import React, { useState } from 'react';
7
+
8
+ import { faker } from '@dxos/random';
9
+ import { withLayout, withTheme } from '@dxos/react-ui/testing';
10
+
11
+ import { translations } from '../../translations';
12
+
13
+ import { useSearchListInput, useSearchListItem, useSearchListResults } from './hooks';
14
+ import { SearchList } from './SearchList';
15
+
16
+ faker.seed(1234);
17
+
18
+ type StoryItem = {
19
+ id: string;
20
+ label: string;
21
+ icon?: string;
22
+ };
23
+
24
+ const defaultItems: StoryItem[] = faker.helpers.uniqueArray(faker.commerce.productName, 16).map((label) => ({
25
+ id: faker.string.uuid(),
26
+ label,
27
+ icon: 'ph--file--regular',
28
+ }));
29
+
30
+ //
31
+ // Default Story - Basic composition with SearchList.Item
32
+ //
33
+
34
+ type DefaultStoryProps = {
35
+ items?: StoryItem[];
36
+ };
37
+
38
+ const DefaultStory = ({ items = defaultItems }: DefaultStoryProps) => {
39
+ const { results, handleSearch } = useSearchListResults({ items });
40
+
41
+ return (
42
+ <div className='flex flex-col is-full bs-[400px]'>
43
+ <SearchList.Root onSearch={handleSearch}>
44
+ <SearchList.Input placeholder='Search items...' autoFocus />
45
+ <SearchList.Content>
46
+ <SearchList.Viewport>
47
+ {results.length > 0 ? (
48
+ results.map((item) => (
49
+ <SearchList.Item
50
+ key={item.id}
51
+ value={item.id}
52
+ label={item.label}
53
+ icon={item.icon}
54
+ onSelect={() => console.log('[SearchList.Item.onSelect]', item.id, item.label)}
55
+ />
56
+ ))
57
+ ) : (
58
+ <SearchList.Empty>No results found</SearchList.Empty>
59
+ )}
60
+ </SearchList.Viewport>
61
+ </SearchList.Content>
62
+ </SearchList.Root>
63
+ </div>
64
+ );
65
+ };
66
+
67
+ //
68
+ // Controlled Story - Controlled query state
69
+ //
70
+
71
+ const ControlledStory = ({ items = defaultItems }: DefaultStoryProps) => {
72
+ const [query, setQuery] = useState('');
73
+ const [results, setResults] = useState<StoryItem[]>(items);
74
+
75
+ const handleSearch = (searchQuery: string) => {
76
+ if (!searchQuery) {
77
+ setResults(items);
78
+ return;
79
+ }
80
+ const filtered = items.filter((item) => item.label.toLowerCase().includes(searchQuery.toLowerCase()));
81
+ setResults(filtered);
82
+ };
83
+
84
+ const handleQueryChange = (newQuery: string) => {
85
+ setQuery(newQuery);
86
+ handleSearch(newQuery);
87
+ };
88
+
89
+ return (
90
+ <div className='is-full bs-[400px] flex flex-col gap-2'>
91
+ <div className='text-sm text-description'>Controlled query: &quot;{query}&quot;</div>
92
+ <SearchList.Root onSearch={handleSearch} value={query}>
93
+ <SearchList.Input placeholder='Controlled search...' onChange={(e) => handleQueryChange(e.target.value)} />
94
+ <SearchList.Content>
95
+ <SearchList.Viewport>
96
+ {results.map((item) => (
97
+ <SearchList.Item
98
+ key={item.id}
99
+ value={item.id}
100
+ label={item.label}
101
+ icon={item.icon}
102
+ onSelect={() => console.log('[SearchList.Item.onSelect]', item.id)}
103
+ />
104
+ ))}
105
+ </SearchList.Viewport>
106
+ </SearchList.Content>
107
+ </SearchList.Root>
108
+ <button className='pli-2 plb-1 rounded bg-accentSurface text-accentText' onClick={() => handleQueryChange('')}>
109
+ Clear Query
110
+ </button>
111
+ </div>
112
+ );
113
+ };
114
+
115
+ //
116
+ // Custom Rendering Story - Custom components in Content using useSearchItem hook
117
+ //
118
+
119
+ type CustomItemProps = {
120
+ value: string;
121
+ label: string;
122
+ description: string;
123
+ onSelect?: () => void;
124
+ };
125
+
126
+ const CustomItem = ({ value, label, description, onSelect }: CustomItemProps) => {
127
+ const { selectedValue, registerItem, unregisterItem } = useSearchListItem();
128
+ const ref = React.useRef<HTMLDivElement>(null);
129
+ const isSelected = selectedValue === value;
130
+
131
+ React.useEffect(() => {
132
+ registerItem(value, ref.current, onSelect);
133
+ return () => unregisterItem(value);
134
+ }, [value, onSelect, registerItem, unregisterItem]);
135
+
136
+ // Scroll into view when selected.
137
+ React.useEffect(() => {
138
+ if (isSelected && ref.current) {
139
+ ref.current.scrollIntoView({ block: 'nearest', behavior: 'smooth' });
140
+ }
141
+ }, [isSelected]);
142
+
143
+ return (
144
+ <div
145
+ ref={ref}
146
+ role='option'
147
+ aria-selected={isSelected}
148
+ data-selected={isSelected}
149
+ className={`p-2 border-be border-separator cursor-pointer ${isSelected ? 'bg-hoverOverlay' : 'hover:bg-hoverOverlay'}`}
150
+ onClick={onSelect}
151
+ >
152
+ <div className='font-medium'>{label}</div>
153
+ <div className='text-xs text-description'>{description}</div>
154
+ </div>
155
+ );
156
+ };
157
+
158
+ const CustomRenderingStory = ({ items = defaultItems }: DefaultStoryProps) => {
159
+ const { results, handleSearch } = useSearchListResults({ items });
160
+
161
+ return (
162
+ <div className='is-full bs-[400px] flex flex-col'>
163
+ <SearchList.Root onSearch={handleSearch}>
164
+ <SearchList.Input placeholder='Search with custom rendering...' autoFocus />
165
+ <SearchList.Content>
166
+ <SearchList.Viewport>
167
+ {results.map((item) => (
168
+ <CustomItem
169
+ key={item.id}
170
+ value={item.id}
171
+ label={item.label}
172
+ description={`ID: ${item.id}`}
173
+ onSelect={() => console.log('[CustomItem.onSelect]', item.id, item.label)}
174
+ />
175
+ ))}
176
+ </SearchList.Viewport>
177
+ </SearchList.Content>
178
+ </SearchList.Root>
179
+ </div>
180
+ );
181
+ };
182
+
183
+ //
184
+ // With Empty Story - Show Empty component when no results
185
+ //
186
+
187
+ const WithEmptyStory = () => {
188
+ const [hasSearched, setHasSearched] = useState(false);
189
+
190
+ const handleSearch = (query: string) => {
191
+ setHasSearched(!!query);
192
+ };
193
+
194
+ return (
195
+ <div className='is-full bs-[400px] flex flex-col'>
196
+ <SearchList.Root onSearch={handleSearch}>
197
+ <SearchList.Input placeholder='Try searching for anything...' />
198
+ <SearchList.Content>
199
+ {hasSearched ? (
200
+ <SearchList.Empty classNames='text-center text-description p-4'>
201
+ <div className='text-lg'>🔍</div>
202
+ <div>No results found</div>
203
+ <div className='text-xs'>Try a different search term</div>
204
+ </SearchList.Empty>
205
+ ) : (
206
+ <SearchList.Empty classNames='text-center text-description p-4'>
207
+ <div>Start typing to search</div>
208
+ </SearchList.Empty>
209
+ )}
210
+ </SearchList.Content>
211
+ </SearchList.Root>
212
+ </div>
213
+ );
214
+ };
215
+
216
+ //
217
+ // Without Viewport Story - Content without scrolling
218
+ //
219
+
220
+ const WithoutViewportStory = ({ items = defaultItems }: DefaultStoryProps) => {
221
+ const { results, handleSearch } = useSearchListResults({ items });
222
+
223
+ return (
224
+ <div className='is-full bs-[300px] flex flex-col'>
225
+ <SearchList.Root onSearch={handleSearch}>
226
+ <SearchList.Input placeholder='Search without viewport (no scroll)...' classNames='shrink-0' />
227
+ <SearchList.Content>
228
+ {results.map((item) => (
229
+ <SearchList.Item
230
+ key={item.id}
231
+ value={item.id}
232
+ label={item.label}
233
+ icon={item.icon}
234
+ onSelect={() => console.log('[SearchList.Item.onSelect]', item.id)}
235
+ />
236
+ ))}
237
+ </SearchList.Content>
238
+ </SearchList.Root>
239
+ </div>
240
+ );
241
+ };
242
+
243
+ //
244
+ // With Icons Story - Various icon configurations
245
+ //
246
+
247
+ const iconsItems: StoryItem[] = [
248
+ { id: '1', label: 'Document', icon: 'ph--file-text--regular' },
249
+ { id: '2', label: 'Folder', icon: 'ph--folder--regular' },
250
+ { id: '3', label: 'Image', icon: 'ph--image--regular' },
251
+ { id: '4', label: 'Settings', icon: 'ph--gear--regular' },
252
+ { id: '5', label: 'No icon item' },
253
+ ];
254
+
255
+ const WithIconsStory = () => {
256
+ return (
257
+ <div className='is-full flex flex-col'>
258
+ <SearchList.Root>
259
+ <SearchList.Input placeholder='Search items with icons...' />
260
+ <SearchList.Content>
261
+ {iconsItems.map((item) => (
262
+ <SearchList.Item
263
+ key={item.id}
264
+ value={item.id}
265
+ label={item.label}
266
+ icon={item.icon}
267
+ onSelect={() => console.log('[SearchList.Item.onSelect]', item.id)}
268
+ />
269
+ ))}
270
+ </SearchList.Content>
271
+ </SearchList.Root>
272
+ </div>
273
+ );
274
+ };
275
+
276
+ //
277
+ // Custom Input Story - Demonstrate using hooks for custom input
278
+ //
279
+
280
+ const CustomInput = () => {
281
+ const { query, onQueryChange, selectedValue, onSelectedValueChange, getItemValues, triggerSelect } =
282
+ useSearchListInput();
283
+
284
+ const handleKeyDown = (event: React.KeyboardEvent<HTMLInputElement>) => {
285
+ const values = getItemValues();
286
+ if (values.length === 0) {
287
+ if (event.key === 'Escape') {
288
+ onQueryChange('');
289
+ }
290
+ return;
291
+ }
292
+
293
+ const currentIndex = selectedValue !== undefined ? values.indexOf(selectedValue) : -1;
294
+
295
+ switch (event.key) {
296
+ case 'ArrowDown': {
297
+ event.preventDefault();
298
+ const nextIndex = currentIndex === -1 ? 0 : Math.min(currentIndex + 1, values.length - 1);
299
+ const nextValue = values[nextIndex];
300
+ if (nextValue !== undefined) {
301
+ onSelectedValueChange(nextValue);
302
+ }
303
+ break;
304
+ }
305
+ case 'ArrowUp': {
306
+ event.preventDefault();
307
+ const prevIndex = currentIndex === -1 ? values.length - 1 : Math.max(currentIndex - 1, 0);
308
+ const prevValue = values[prevIndex];
309
+ if (prevValue !== undefined) {
310
+ onSelectedValueChange(prevValue);
311
+ }
312
+ break;
313
+ }
314
+ case 'Enter': {
315
+ if (selectedValue !== undefined) {
316
+ event.preventDefault();
317
+ triggerSelect();
318
+ }
319
+ break;
320
+ }
321
+ case 'Escape': {
322
+ event.preventDefault();
323
+ if (selectedValue !== undefined) {
324
+ onSelectedValueChange(undefined);
325
+ } else {
326
+ onQueryChange('');
327
+ }
328
+ break;
329
+ }
330
+ }
331
+ };
332
+
333
+ return (
334
+ <div className='flex gap-2 items-center p-2 bg-input rounded'>
335
+ <input
336
+ type='text'
337
+ value={query}
338
+ onChange={(ev) => onQueryChange(ev.target.value)}
339
+ onKeyDown={handleKeyDown}
340
+ placeholder='Custom input...'
341
+ className='bg-transparent outline-none grow'
342
+ />
343
+ {query && (
344
+ <button onClick={() => onQueryChange('')} className='text-description hover:text-baseText'>
345
+
346
+ </button>
347
+ )}
348
+ </div>
349
+ );
350
+ };
351
+
352
+ const CustomInputStory = ({ items = defaultItems }: DefaultStoryProps) => {
353
+ const { results, handleSearch } = useSearchListResults({ items });
354
+
355
+ return (
356
+ <div className='is-full bs-[400px] flex flex-col border border-separator'>
357
+ <SearchList.Root onSearch={handleSearch}>
358
+ <CustomInput />
359
+ <SearchList.Content>
360
+ <SearchList.Viewport>
361
+ {results.map((item) => (
362
+ <SearchList.Item
363
+ key={item.id}
364
+ value={item.id}
365
+ label={item.label}
366
+ icon={item.icon}
367
+ onSelect={() => console.log('[SearchList.Item.onSelect]', item.id)}
368
+ />
369
+ ))}
370
+ </SearchList.Viewport>
371
+ </SearchList.Content>
372
+ </SearchList.Root>
373
+ </div>
374
+ );
375
+ };
376
+
377
+ //
378
+ // With Disabled Items Story
379
+ //
380
+
381
+ const disabledItems: StoryItem[] = [
382
+ { id: '1', label: 'Available item 1', icon: 'ph--check--regular' },
383
+ { id: '2', label: 'Disabled item (cannot select)', icon: 'ph--prohibit--regular' },
384
+ { id: '3', label: 'Available item 2', icon: 'ph--check--regular' },
385
+ { id: '4', label: 'Disabled item 2', icon: 'ph--prohibit--regular' },
386
+ { id: '5', label: 'Available item 3', icon: 'ph--check--regular' },
387
+ ];
388
+
389
+ const WithDisabledItemsStory = () => {
390
+ return (
391
+ <div className='is-full flex flex-col'>
392
+ <SearchList.Root>
393
+ <SearchList.Input placeholder='Arrow keys skip disabled items...' autoFocus />
394
+ <SearchList.Content>
395
+ {disabledItems.map((item, index) => (
396
+ <SearchList.Item
397
+ key={item.id}
398
+ value={item.id}
399
+ label={item.label}
400
+ icon={item.icon}
401
+ disabled={index === 1 || index === 3}
402
+ onSelect={() => console.log('[SearchList.Item.onSelect]', item.id)}
403
+ />
404
+ ))}
405
+ </SearchList.Content>
406
+ </SearchList.Root>
407
+ </div>
408
+ );
409
+ };
410
+
411
+ //
412
+ // With Groups Story
413
+ //
414
+
415
+ type GroupedItem = StoryItem & { category: string };
416
+
417
+ const groupedItems: GroupedItem[] = [
418
+ { id: '1', label: 'Document 1', icon: 'ph--file-text--regular', category: 'Documents' },
419
+ { id: '2', label: 'Document 2', icon: 'ph--file-text--regular', category: 'Documents' },
420
+ { id: '3', label: 'Image 1', icon: 'ph--image--regular', category: 'Images' },
421
+ { id: '4', label: 'Image 2', icon: 'ph--image--regular', category: 'Images' },
422
+ { id: '5', label: 'Settings', icon: 'ph--gear--regular', category: 'Other' },
423
+ ];
424
+
425
+ const WithGroupsStory = () => {
426
+ const { results, handleSearch } = useSearchListResults({ items: groupedItems });
427
+
428
+ // Group items by category.
429
+ const grouped = results.reduce(
430
+ (acc, item) => {
431
+ if (!acc[item.category]) {
432
+ acc[item.category] = [];
433
+ }
434
+ acc[item.category].push(item);
435
+ return acc;
436
+ },
437
+ {} as Record<string, GroupedItem[]>,
438
+ );
439
+
440
+ return (
441
+ <div className='is-full bs-[400px] flex flex-col'>
442
+ <SearchList.Root onSearch={handleSearch}>
443
+ <SearchList.Input placeholder='Search grouped items...' autoFocus />
444
+ <SearchList.Content>
445
+ <SearchList.Viewport>
446
+ {Object.entries(grouped).map(([category, items]) => (
447
+ <SearchList.Group key={category} heading={category}>
448
+ {items.map((item) => (
449
+ <SearchList.Item
450
+ key={item.id}
451
+ value={item.id}
452
+ label={item.label}
453
+ icon={item.icon}
454
+ onSelect={() => console.log('[SearchList.Item.onSelect]', item.id, item.label)}
455
+ />
456
+ ))}
457
+ </SearchList.Group>
458
+ ))}
459
+ {results.length === 0 && <SearchList.Empty>No results found</SearchList.Empty>}
460
+ </SearchList.Viewport>
461
+ </SearchList.Content>
462
+ </SearchList.Root>
463
+ </div>
464
+ );
465
+ };
466
+
467
+ //
468
+ // Meta
469
+ //
470
+
471
+ const meta = {
472
+ title: 'ui/react-ui-searchlist/SearchList',
473
+ component: SearchList.Root as any,
474
+ decorators: [withTheme, withLayout({ layout: 'column' })],
475
+ parameters: {
476
+ layout: 'fullscreen',
477
+ translations,
478
+ },
479
+ } satisfies Meta<typeof DefaultStory>;
480
+
481
+ export default meta;
482
+
483
+ type Story = StoryObj<typeof meta>;
484
+
485
+ export const Default: Story = {
486
+ render: DefaultStory,
487
+ args: {
488
+ items: defaultItems,
489
+ },
490
+ };
491
+
492
+ export const Controlled: Story = {
493
+ render: ControlledStory,
494
+ args: {
495
+ items: defaultItems,
496
+ },
497
+ };
498
+
499
+ export const CustomRendering: Story = {
500
+ render: CustomRenderingStory,
501
+ args: {
502
+ items: defaultItems,
503
+ },
504
+ };
505
+
506
+ export const WithEmpty: Story = {
507
+ render: WithEmptyStory,
508
+ };
509
+
510
+ export const WithoutViewport: Story = {
511
+ render: WithoutViewportStory,
512
+ args: {
513
+ items: defaultItems,
514
+ },
515
+ };
516
+
517
+ export const WithIcons: Story = {
518
+ render: WithIconsStory,
519
+ };
520
+
521
+ export const CustomInputExample: Story = {
522
+ render: CustomInputStory,
523
+ args: {
524
+ items: defaultItems,
525
+ },
526
+ };
527
+
528
+ export const WithDisabledItems: Story = {
529
+ render: WithDisabledItemsStory,
530
+ };
531
+
532
+ export const WithGroups: Story = {
533
+ render: WithGroupsStory,
534
+ };