@dxos/react-ui-searchlist 0.8.3 → 0.8.4-main.1068cf700f

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