@coinbase/cds-mcp-server 8.19.1 → 8.20.1

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 (38) hide show
  1. package/CHANGELOG.md +8 -0
  2. package/mcp-docs/mobile/components/AvatarButton.txt +1 -1
  3. package/mcp-docs/mobile/components/Button.txt +1 -1
  4. package/mcp-docs/mobile/components/CheckboxCell.txt +1 -1
  5. package/mcp-docs/mobile/components/Chip.txt +2 -1
  6. package/mcp-docs/mobile/components/ContentCell.txt +1 -0
  7. package/mcp-docs/mobile/components/IconButton.txt +1 -1
  8. package/mcp-docs/mobile/components/InputChip.txt +7 -3
  9. package/mcp-docs/mobile/components/Interactable.txt +1 -1
  10. package/mcp-docs/mobile/components/ListCell.txt +1 -0
  11. package/mcp-docs/mobile/components/MediaChip.txt +2 -1
  12. package/mcp-docs/mobile/components/Pressable.txt +1 -1
  13. package/mcp-docs/mobile/components/RadioCell.txt +1 -1
  14. package/mcp-docs/mobile/components/Select.txt +12 -12
  15. package/mcp-docs/mobile/components/SelectAlpha.txt +1211 -0
  16. package/mcp-docs/mobile/components/SelectChip.txt +2 -1
  17. package/mcp-docs/mobile/components/SelectOption.txt +3 -2
  18. package/mcp-docs/mobile/components/SlideButton.txt +1 -1
  19. package/mcp-docs/mobile/routes.txt +1 -0
  20. package/mcp-docs/web/components/AvatarButton.txt +1 -1
  21. package/mcp-docs/web/components/Button.txt +1 -1
  22. package/mcp-docs/web/components/CheckboxCell.txt +1 -1
  23. package/mcp-docs/web/components/Chip.txt +2 -2
  24. package/mcp-docs/web/components/IconButton.txt +1 -1
  25. package/mcp-docs/web/components/InputChip.txt +7 -2
  26. package/mcp-docs/web/components/Interactable.txt +1 -1
  27. package/mcp-docs/web/components/Link.txt +1 -1
  28. package/mcp-docs/web/components/MediaChip.txt +2 -2
  29. package/mcp-docs/web/components/Pressable.txt +1 -1
  30. package/mcp-docs/web/components/RadioCell.txt +1 -1
  31. package/mcp-docs/web/components/Select.txt +16 -16
  32. package/mcp-docs/web/components/SelectAlpha.txt +1115 -0
  33. package/mcp-docs/web/components/SelectChip.txt +3 -2
  34. package/mcp-docs/web/components/SelectOption.txt +2 -2
  35. package/mcp-docs/web/components/SidebarItem.txt +1 -1
  36. package/mcp-docs/web/components/TileButton.txt +1 -1
  37. package/mcp-docs/web/routes.txt +1 -0
  38. package/package.json +1 -1
@@ -0,0 +1,1211 @@
1
+ # SelectAlpha
2
+
3
+ A flexible select component for both single and multi-selection, built for mobile applications with comprehensive accessibility support.
4
+
5
+ ## Import
6
+
7
+ ```tsx
8
+ import { Select } from '@coinbase/cds-mobile/alpha/select'
9
+ ```
10
+
11
+ ## Examples
12
+
13
+ ### Single Select
14
+
15
+ Basic single selection with predefined options for mobile interfaces.
16
+
17
+ ```jsx
18
+ function SingleSelectExample() {
19
+ const [value, setValue] = useState('1');
20
+
21
+ const options = [
22
+ { value: '1', label: 'Option 1' },
23
+ { value: '2', label: 'Option 2' },
24
+ { value: '3', label: 'Option 3' },
25
+ { value: '4', label: 'Option 4' },
26
+ ];
27
+
28
+ return (
29
+ <Select
30
+ label="Choose an option"
31
+ value={value}
32
+ onChange={setValue}
33
+ options={options}
34
+ placeholder="Select an option"
35
+ />
36
+ );
37
+ }
38
+ ```
39
+
40
+ ### Multi-Select
41
+
42
+ Multi-selection mode allows users to select multiple options from the list with touch-friendly controls.
43
+
44
+ ```jsx
45
+ function MultiSelectExample() {
46
+ const { value, onChange } = useMultiSelect({
47
+ initialValue: ['1', '3'],
48
+ });
49
+
50
+ const options = [
51
+ { value: '1', label: 'Option 1' },
52
+ { value: '2', label: 'Option 2' },
53
+ { value: '3', label: 'Option 3' },
54
+ { value: '4', label: 'Option 4' },
55
+ { value: '5', label: 'Option 5' },
56
+ ];
57
+
58
+ return (
59
+ <Select
60
+ type="multi"
61
+ label="Choose multiple options"
62
+ value={value}
63
+ onChange={onChange}
64
+ options={options}
65
+ placeholder="Select options"
66
+ selectAllLabel="Select all options"
67
+ clearAllLabel="Clear all selections"
68
+ />
69
+ );
70
+ }
71
+ ```
72
+
73
+ ### Accessibility Props
74
+
75
+ The mobile Select component supports comprehensive accessibility features including custom labels, hints, and roles.
76
+
77
+ ```jsx
78
+ function AccessibilityExample() {
79
+ const [value, setValue] = useState('2');
80
+
81
+ const options = [
82
+ { value: '1', label: 'High Priority' },
83
+ { value: '2', label: 'Medium Priority' },
84
+ { value: '3', label: 'Low Priority' },
85
+ ];
86
+
87
+ return (
88
+ <Select
89
+ label="Task Priority"
90
+ value={value}
91
+ onChange={setValue}
92
+ options={options}
93
+ accessibilityLabel="Select task priority level"
94
+ accessibilityHint="Choose the appropriate priority for this task"
95
+ accessibilityRoles={{
96
+ option: 'button',
97
+ }}
98
+ placeholder="Choose priority level"
99
+ helperText="Select the appropriate priority for this task"
100
+ />
101
+ );
102
+ }
103
+ ```
104
+
105
+ ### Variant Props
106
+
107
+ The mobile Select component supports different visual variants for various states and contexts.
108
+
109
+ ```jsx
110
+ function VariantExample() {
111
+ const [positiveValue, setPositiveValue] = useState('success');
112
+ const [negativeValue, setNegativeValue] = useState('');
113
+
114
+ const positiveOptions = [
115
+ { value: 'success', label: 'Success' },
116
+ { value: 'completed', label: 'Completed' },
117
+ { value: 'approved', label: 'Approved' },
118
+ ];
119
+
120
+ const negativeOptions = [
121
+ { value: 'error', label: 'Error' },
122
+ { value: 'failed', label: 'Failed' },
123
+ { value: 'rejected', label: 'Rejected' },
124
+ ];
125
+
126
+ return (
127
+ <VStack gap={4}>
128
+ <Select
129
+ label="Positive Status"
130
+ value={positiveValue}
131
+ onChange={setPositiveValue}
132
+ options={positiveOptions}
133
+ variant="positive"
134
+ helperText="This shows a positive state"
135
+ placeholder="Select positive status"
136
+ />
137
+
138
+ <Select
139
+ label="Negative Status"
140
+ value={negativeValue}
141
+ onChange={setNegativeValue}
142
+ options={negativeOptions}
143
+ variant="negative"
144
+ helperText="This shows an error state"
145
+ placeholder="Select negative status"
146
+ />
147
+ </VStack>
148
+ );
149
+ }
150
+ ```
151
+
152
+ ### Compact Mode
153
+
154
+ The Select component can be rendered in a compact size for denser mobile UIs.
155
+
156
+ ```jsx
157
+ function CompactExample() {
158
+ const [value, setValue] = useState('1');
159
+
160
+ const options = [
161
+ { value: '1', label: 'Small Option 1' },
162
+ { value: '2', label: 'Small Option 2' },
163
+ { value: '3', label: 'Small Option 3' },
164
+ ];
165
+
166
+ return (
167
+ <Select
168
+ compact
169
+ label="Compact Select"
170
+ value={value}
171
+ onChange={setValue}
172
+ options={options}
173
+ placeholder="Select an option"
174
+ helperText="This is a compact select component"
175
+ />
176
+ );
177
+ }
178
+ ```
179
+
180
+ ### Disabled States
181
+
182
+ Components can be disabled entirely or have individual options disabled.
183
+
184
+ ```jsx
185
+ function DisabledExample() {
186
+ const [value1, setValue1] = useState('2');
187
+ const [value2, setValue2] = useState('2');
188
+
189
+ const optionsWithDisabled = [
190
+ { value: '1', label: 'Option 1', disabled: true },
191
+ { value: '2', label: 'Option 2' },
192
+ { value: '3', label: 'Option 3' },
193
+ { value: '4', label: 'Option 4', disabled: true },
194
+ ];
195
+
196
+ return (
197
+ <VStack gap={4}>
198
+ <Select
199
+ label="Disabled Select"
200
+ value={value1}
201
+ onChange={setValue1}
202
+ options={optionsWithDisabled}
203
+ disabled
204
+ placeholder="This select is disabled"
205
+ />
206
+
207
+ <Select
208
+ label="Select with Disabled Options"
209
+ value={value2}
210
+ onChange={setValue2}
211
+ options={optionsWithDisabled}
212
+ placeholder="Some options are disabled"
213
+ />
214
+ </VStack>
215
+ );
216
+ }
217
+ ```
218
+
219
+ ### Options with Descriptions
220
+
221
+ Options can include descriptions for additional context, perfect for mobile interfaces.
222
+
223
+ ```jsx
224
+ function DescriptionExample() {
225
+ const [value, setValue] = useState('1');
226
+
227
+ const optionsWithDescriptions = [
228
+ { value: '1', label: 'Bitcoin', description: 'The first cryptocurrency' },
229
+ { value: '2', label: 'Ethereum', description: 'Smart contract platform' },
230
+ { value: '3', label: 'USDC', description: 'USD-backed stablecoin' },
231
+ { value: '4', label: 'Solana', description: 'High-performance blockchain' },
232
+ ];
233
+
234
+ return (
235
+ <Select
236
+ label="Select Cryptocurrency"
237
+ value={value}
238
+ onChange={setValue}
239
+ options={optionsWithDescriptions}
240
+ placeholder="Choose a cryptocurrency"
241
+ />
242
+ );
243
+ }
244
+ ```
245
+
246
+ ### Start Node
247
+
248
+ Add an icon or element at the start of the select control for better visual context.
249
+
250
+ ```jsx
251
+ function StartNodeExample() {
252
+ const [value, setValue] = useState('1');
253
+
254
+ const options = [
255
+ { value: '1', label: 'Search Result 1' },
256
+ { value: '2', label: 'Search Result 2' },
257
+ { value: '3', label: 'Search Result 3' },
258
+ ];
259
+
260
+ return (
261
+ <Select
262
+ label="Search"
263
+ value={value}
264
+ onChange={setValue}
265
+ options={options}
266
+ startNode={<Icon color="fg" name="search" />}
267
+ placeholder="Search for options"
268
+ />
269
+ );
270
+ }
271
+ ```
272
+
273
+ ### End Node
274
+
275
+ Add an icon or element at the end of the select control.
276
+
277
+ ```jsx
278
+ function EndNodeExample() {
279
+ const [value, setValue] = useState('1');
280
+
281
+ const options = [
282
+ { value: '1', label: 'Search Result 1' },
283
+ { value: '2', label: 'Search Result 2' },
284
+ { value: '3', label: 'Search Result 3' },
285
+ ];
286
+
287
+ return (
288
+ <Select
289
+ endNode={<Icon color="fg" name="search" />}
290
+ label="Single select - custom end node"
291
+ onChange={setValue}
292
+ options={options}
293
+ placeholder="Empty value"
294
+ value={value}
295
+ />
296
+ );
297
+ }
298
+ ```
299
+
300
+ ### Custom Icons
301
+
302
+ Add custom icons as accessories or media to options for enhanced visual hierarchy.
303
+
304
+ ```jsx
305
+ function CustomIconsExample() {
306
+ const [value, setValue] = useState('1');
307
+
308
+ const optionsWithIcons = [
309
+ {
310
+ value: '1',
311
+ label: 'Favorites',
312
+ accessory: <Icon color="fg" name="star" />,
313
+ media: <Icon color="fg" name="heart" />,
314
+ },
315
+ {
316
+ value: '2',
317
+ label: 'Verified',
318
+ accessory: <Icon color="fg" name="checkmark" />,
319
+ media: <Icon color="fg" name="search" />,
320
+ },
321
+ {
322
+ value: '3',
323
+ label: 'Settings',
324
+ accessory: <Icon color="fg" name="caretRight" />,
325
+ media: <Icon color="fg" name="gear" />,
326
+ },
327
+ ];
328
+
329
+ return (
330
+ <Select
331
+ label="Choose Action"
332
+ value={value}
333
+ onChange={setValue}
334
+ options={optionsWithIcons}
335
+ placeholder="Select an action"
336
+ />
337
+ );
338
+ }
339
+ ```
340
+
341
+ ### Empty State
342
+
343
+ Handle empty option lists with custom messages optimized for mobile screens.
344
+
345
+ ```jsx
346
+ function EmptyStateExample() {
347
+ const [value, setValue] = useState(null);
348
+
349
+ return (
350
+ <VStack gap={4}>
351
+ <Select
352
+ label="Empty Options"
353
+ value={value}
354
+ onChange={setValue}
355
+ options={[]}
356
+ emptyOptionsLabel="No options available at this time"
357
+ placeholder="No options"
358
+ />
359
+
360
+ <Select
361
+ label="Custom Empty Component"
362
+ value={value}
363
+ onChange={setValue}
364
+ options={[]}
365
+ SelectEmptyOptionsComponent={
366
+ <Text background="fgWarning" font="headline" padding={4}>
367
+ No items found. Try refreshing!
368
+ </Text>
369
+ }
370
+ placeholder="No options"
371
+ />
372
+ </VStack>
373
+ );
374
+ }
375
+ ```
376
+
377
+ ### Long Labels
378
+
379
+ Handle very long option labels that may wrap on smaller mobile screens.
380
+
381
+ ```jsx
382
+ function LongLabelsExample() {
383
+ const [value, setValue] = useState('1');
384
+
385
+ const longOptions = [
386
+ {
387
+ value: '1',
388
+ label:
389
+ 'This is an extremely long option label that should test how the component handles very long text content on mobile devices',
390
+ },
391
+ {
392
+ value: '2',
393
+ label:
394
+ 'Another super long option label with even more text to see how it wraps or truncates in the mobile UI',
395
+ },
396
+ {
397
+ value: '3',
398
+ label: 'Short',
399
+ },
400
+ {
401
+ value: '4',
402
+ label: 'A moderately long label that is somewhere between short and extremely long',
403
+ },
404
+ ];
405
+
406
+ return (
407
+ <Select
408
+ label="Select with Long Labels"
409
+ value={value}
410
+ onChange={setValue}
411
+ options={longOptions}
412
+ placeholder="Choose an option"
413
+ />
414
+ );
415
+ }
416
+ ```
417
+
418
+ ### Multi-Select with Max Display
419
+
420
+ Limit the number of selected items shown when using multi-select on mobile.
421
+
422
+ ```jsx
423
+ function MaxDisplayExample() {
424
+ const { value, onChange } = useMultiSelect({
425
+ initialValue: ['1', '2', '3', '4', '5'],
426
+ });
427
+
428
+ const options = Array.from({ length: 20 }, (_, i) => ({
429
+ value: (i + 1).toString(),
430
+ label: `Option ${i + 1}`,
431
+ }));
432
+
433
+ return (
434
+ <Select
435
+ type="multi"
436
+ label="Select Multiple Items"
437
+ value={value}
438
+ onChange={onChange}
439
+ options={options}
440
+ maxSelectedOptionsToShow={3}
441
+ placeholder="Select options"
442
+ helperText="Showing first 3 selected items"
443
+ />
444
+ );
445
+ }
446
+ ```
447
+
448
+ ### Custom Select All labels
449
+
450
+ Customize the select all functionality in multi-select mode for mobile.
451
+
452
+ ```jsx
453
+ function CustomSelectAllExample() {
454
+ const { value, onChange } = useMultiSelect({
455
+ initialValue: ['1'],
456
+ });
457
+
458
+ const options = [
459
+ { value: '1', label: 'Monday' },
460
+ { value: '2', label: 'Tuesday' },
461
+ { value: '3', label: 'Wednesday' },
462
+ { value: '4', label: 'Thursday' },
463
+ { value: '5', label: 'Friday' },
464
+ { value: '6', label: 'Saturday' },
465
+ { value: '7', label: 'Sunday' },
466
+ ];
467
+
468
+ return (
469
+ <Select
470
+ type="multi"
471
+ label="Select Days"
472
+ value={value}
473
+ onChange={onChange}
474
+ options={options}
475
+ selectAllLabel="Select all days of the week"
476
+ clearAllLabel="Clear all days"
477
+ placeholder="Choose days"
478
+ />
479
+ );
480
+ }
481
+ ```
482
+
483
+ ### Hide Select All
484
+
485
+ Hide the select all option for simpler multi-select interfaces.
486
+
487
+ ```jsx
488
+ function HideSelectAllExample() {
489
+ const { value, onChange } = useMultiSelect({
490
+ initialValue: ['1', '2'],
491
+ });
492
+
493
+ const options = [
494
+ { value: '1', label: 'Option 1' },
495
+ { value: '2', label: 'Option 2' },
496
+ { value: '3', label: 'Option 3' },
497
+ { value: '4', label: 'Option 4' },
498
+ { value: '5', label: 'Option 5' },
499
+ ];
500
+
501
+ return (
502
+ <Select
503
+ type="multi"
504
+ hideSelectAll
505
+ label="Multi-Select without Select All"
506
+ value={value}
507
+ onChange={onChange}
508
+ options={options}
509
+ placeholder="Choose options"
510
+ />
511
+ );
512
+ }
513
+ ```
514
+
515
+ ### Combined Features
516
+
517
+ Example combining multiple features for a rich mobile experience.
518
+
519
+ ```jsx
520
+ function CombinedFeaturesExample() {
521
+ const [value, setValue] = useState('1');
522
+
523
+ const options = [
524
+ {
525
+ value: '1',
526
+ label: 'Premium Account',
527
+ description: 'Access to all features',
528
+ accessory: <Icon color="fg" name="star" />,
529
+ media: <Icon color="fg" name="search" />,
530
+ },
531
+ {
532
+ value: '2',
533
+ label: 'Standard Account',
534
+ description: 'Basic features included',
535
+ media: <Icon color="fg" name="search" />,
536
+ },
537
+ {
538
+ value: '3',
539
+ label: 'Trial Account',
540
+ description: 'Limited time access',
541
+ disabled: true,
542
+ media: <Icon color="fg" name="clock" />,
543
+ },
544
+ ];
545
+
546
+ return (
547
+ <Select
548
+ label="Account Type"
549
+ value={value}
550
+ onChange={setValue}
551
+ options={options}
552
+ startNode={<Icon color="fg" name="filter" />}
553
+ variant="positive"
554
+ helperText="Choose your account type"
555
+ placeholder="Select account"
556
+ />
557
+ );
558
+ }
559
+ ```
560
+
561
+ ### Options with Only Description
562
+
563
+ Options that display only descriptions without labels.
564
+
565
+ ```jsx
566
+ function OnlyDescriptionExample() {
567
+ const [value, setValue] = useState('1');
568
+
569
+ const descriptionOnlyOptions = [
570
+ { value: '1', description: 'First description without a label' },
571
+ { value: '2', description: 'Second description only' },
572
+ { value: '3', description: 'Third item with just description' },
573
+ { value: '4', description: 'Fourth description-only option' },
574
+ ];
575
+
576
+ return (
577
+ <Select
578
+ label="Description-Only Options"
579
+ value={value}
580
+ onChange={setValue}
581
+ options={descriptionOnlyOptions}
582
+ placeholder="Select by description"
583
+ />
584
+ );
585
+ }
586
+ ```
587
+
588
+ ### No Visible Label
589
+
590
+ Select without a visible label (accessibility label/hint still required).
591
+
592
+ ```jsx
593
+ function NoLabelExample() {
594
+ const [value, setValue] = useState('1');
595
+
596
+ const options = [
597
+ { value: '1', label: 'Option 1' },
598
+ { value: '2', label: 'Option 2' },
599
+ { value: '3', label: 'Option 3' },
600
+ ];
601
+
602
+ return (
603
+ <Select
604
+ accessibilityLabel="Hidden label select"
605
+ accessibilityHint="This select has no visible label"
606
+ value={value}
607
+ onChange={setValue}
608
+ options={options}
609
+ placeholder="Select without visible label"
610
+ />
611
+ );
612
+ }
613
+ ```
614
+
615
+ ### Mixed Option Types
616
+
617
+ Options with varying properties in the same select.
618
+
619
+ ```jsx
620
+ function MixedOptionsExample() {
621
+ const [value, setValue] = useState('1');
622
+
623
+ const mixedOptions = [
624
+ { value: '1', label: 'Bitcoin', description: 'The original cryptocurrency' },
625
+ { value: '2', label: 'Ethereum' },
626
+ { value: '3', label: 'USDC', description: 'USD-backed stablecoin' },
627
+ { value: '4', label: 'Solana' },
628
+ { value: '5', label: 'Polygon', description: 'Layer 2 scaling solution' },
629
+ ];
630
+
631
+ return (
632
+ <Select
633
+ label="Mixed Option Types"
634
+ value={value}
635
+ onChange={setValue}
636
+ options={mixedOptions}
637
+ placeholder="Choose an asset"
638
+ />
639
+ );
640
+ }
641
+ ```
642
+
643
+ ### Variant Combinations
644
+
645
+ Combine compact mode with different variants.
646
+
647
+ ```jsx
648
+ function VariantCombinationsExample() {
649
+ const [value1, setValue1] = useState('1');
650
+ const [value2, setValue2] = useState('2');
651
+
652
+ const options = [
653
+ { value: '1', label: 'Option 1' },
654
+ { value: '2', label: 'Option 2' },
655
+ { value: '3', label: 'Option 3' },
656
+ ];
657
+
658
+ return (
659
+ <VStack gap={4}>
660
+ <Select
661
+ compact
662
+ variant="positive"
663
+ label="Compact + Positive"
664
+ value={value1}
665
+ onChange={setValue1}
666
+ options={options}
667
+ helperText="Success state in compact mode"
668
+ />
669
+
670
+ <Select
671
+ compact
672
+ variant="negative"
673
+ label="Compact + Negative"
674
+ value={value2}
675
+ onChange={setValue2}
676
+ options={options}
677
+ helperText="Error state in compact mode"
678
+ />
679
+ </VStack>
680
+ );
681
+ }
682
+ ```
683
+
684
+ ### Multi-Select with Descriptions
685
+
686
+ Multi-select mode with descriptive options.
687
+
688
+ ```jsx
689
+ function MultiSelectWithDescriptionsExample() {
690
+ const { value, onChange } = useMultiSelect({
691
+ initialValue: ['1', '2'],
692
+ });
693
+
694
+ const optionsWithDescriptions = [
695
+ { value: '1', label: 'Push Notifications', description: 'Get alerts on your device' },
696
+ { value: '2', label: 'Email Updates', description: 'Weekly newsletter' },
697
+ { value: '3', label: 'SMS Alerts', description: 'Text message notifications' },
698
+ { value: '4', label: 'In-App Messages', description: 'Messages within the app' },
699
+ ];
700
+
701
+ return (
702
+ <Select
703
+ type="multi"
704
+ label="Notification Preferences"
705
+ value={value}
706
+ onChange={onChange}
707
+ options={optionsWithDescriptions}
708
+ placeholder="Select notification types"
709
+ />
710
+ );
711
+ }
712
+ ```
713
+
714
+ ### Custom Styles
715
+
716
+ Apply custom styles to the Select component.
717
+
718
+ ```jsx
719
+ function CustomStylesExample() {
720
+ const [value, setValue] = useState('1');
721
+
722
+ const options = [
723
+ { value: '1', label: 'Custom Style 1' },
724
+ { value: '2', label: 'Custom Style 2' },
725
+ { value: '3', label: 'Custom Style 3' },
726
+ ];
727
+
728
+ return (
729
+ <Select
730
+ label="Custom Styled Select"
731
+ value={value}
732
+ onChange={setValue}
733
+ options={options}
734
+ styles={{
735
+ control: {
736
+ backgroundColor: '#e8f4fd',
737
+ borderRadius: 12,
738
+ padding: 16,
739
+ },
740
+ option: {
741
+ backgroundColor: '#f0f8ff',
742
+ },
743
+ optionBlendStyles: {
744
+ pressedBackground: '#0066cc',
745
+ },
746
+ }}
747
+ placeholder="Styled select"
748
+ />
749
+ );
750
+ }
751
+ ```
752
+
753
+ ### Custom Long Placeholder
754
+
755
+ Extended placeholder text for mobile screens.
756
+
757
+ ```jsx
758
+ function LongPlaceholderExample() {
759
+ const [value, setValue] = useState(null);
760
+
761
+ const options = [
762
+ { value: '1', label: 'Option 1' },
763
+ { value: '2', label: 'Option 2' },
764
+ { value: '3', label: 'Option 3' },
765
+ ];
766
+
767
+ return (
768
+ <Select
769
+ label="Select with Long Placeholder"
770
+ value={value}
771
+ onChange={setValue}
772
+ options={options}
773
+ placeholder="This is a very long placeholder text that provides detailed instructions"
774
+ />
775
+ );
776
+ }
777
+ ```
778
+
779
+ ### Options with Only Accessory
780
+
781
+ Options with accessory icons only.
782
+
783
+ ```jsx
784
+ function OnlyAccessoryExample() {
785
+ const [value, setValue] = useState('1');
786
+
787
+ const optionsWithAccessory = [
788
+ {
789
+ value: '1',
790
+ label: 'Starred',
791
+ accessory: <Icon color="fg" name="star" />,
792
+ },
793
+ {
794
+ value: '2',
795
+ label: 'Verified',
796
+ accessory: <Icon color="fg" name="checkmark" />,
797
+ },
798
+ {
799
+ value: '3',
800
+ label: 'Premium',
801
+ accessory: <Icon color="fg" name="search" />,
802
+ },
803
+ ];
804
+
805
+ return (
806
+ <Select
807
+ label="Options with Accessories"
808
+ value={value}
809
+ onChange={setValue}
810
+ options={optionsWithAccessory}
811
+ placeholder="Select an option"
812
+ />
813
+ );
814
+ }
815
+ ```
816
+
817
+ ### Options with Only Media
818
+
819
+ Options with media icons only.
820
+
821
+ ```jsx
822
+ function OnlyMediaExample() {
823
+ const [value, setValue] = useState('1');
824
+
825
+ const optionsWithMedia = [
826
+ {
827
+ value: '1',
828
+ label: 'Home',
829
+ media: <Icon color="fg" name="home" />,
830
+ },
831
+ {
832
+ value: '2',
833
+ label: 'Profile',
834
+ media: <Icon color="fg" name="user" />,
835
+ },
836
+ {
837
+ value: '3',
838
+ label: 'Settings',
839
+ media: <Icon color="fg" name="gear" />,
840
+ },
841
+ ];
842
+
843
+ return (
844
+ <Select
845
+ label="Navigation Options"
846
+ value={value}
847
+ onChange={setValue}
848
+ options={optionsWithMedia}
849
+ placeholder="Navigate to..."
850
+ />
851
+ );
852
+ }
853
+ ```
854
+
855
+ ### Options as React Nodes
856
+
857
+ Options with custom React node labels and descriptions.
858
+
859
+ ```jsx
860
+ function ReactNodeOptionsExample() {
861
+ const [value, setValue] = useState('1');
862
+
863
+ const reactNodeOptions = [
864
+ {
865
+ value: '1',
866
+ label: (
867
+ <Text font="title3" color="fgPrimary">
868
+ Bold Title 1
869
+ </Text>
870
+ ),
871
+ description: (
872
+ <Text font="caption" color="fgSecondary">
873
+ Subtitle text 1
874
+ </Text>
875
+ ),
876
+ },
877
+ {
878
+ value: '2',
879
+ label: (
880
+ <Text font="title3" color="fgPrimary">
881
+ Bold Title 2
882
+ </Text>
883
+ ),
884
+ description: (
885
+ <Text font="caption" color="fgSecondary">
886
+ Subtitle text 2
887
+ </Text>
888
+ ),
889
+ },
890
+ {
891
+ value: '3',
892
+ label: (
893
+ <Text font="title3" color="fgPrimary">
894
+ Bold Title 3
895
+ </Text>
896
+ ),
897
+ description: (
898
+ <Text font="caption" color="fgSecondary">
899
+ Subtitle text 3
900
+ </Text>
901
+ ),
902
+ },
903
+ ];
904
+
905
+ return (
906
+ <Select
907
+ label="Custom Formatted Options"
908
+ value={value}
909
+ onChange={setValue}
910
+ options={reactNodeOptions}
911
+ placeholder="Select styled option"
912
+ />
913
+ );
914
+ }
915
+ ```
916
+
917
+ ### Custom Select All Option
918
+
919
+ Customize the select all option component in multi-select.
920
+
921
+ ```jsx
922
+ function CustomSelectAllOptionExample() {
923
+ const { value, onChange } = useMultiSelect({
924
+ initialValue: ['1'],
925
+ });
926
+
927
+ const CustomSelectAllOption = ({ onChange, selected, disabled, label, style }) => {
928
+ return (
929
+ <Pressable
930
+ background={selected ? 'bgPositive' : 'bg'}
931
+ disabled={disabled}
932
+ onPress={() => onChange('select-all')}
933
+ paddingX={3}
934
+ paddingY={4}
935
+ style={style}
936
+ >
937
+ <HStack gap={2} alignItems="center">
938
+ <Icon
939
+ color={selected ? 'fgPositive' : 'fg'}
940
+ name={selected ? 'circleCheckmark' : 'circle'}
941
+ />
942
+ <Text color={selected ? 'fgPositive' : 'fg'} font="headline">
943
+ {String(label || 'Select Everything')}
944
+ </Text>
945
+ </HStack>
946
+ </Pressable>
947
+ );
948
+ };
949
+
950
+ const options = [
951
+ { value: '1', label: 'Option 1' },
952
+ { value: '2', label: 'Option 2' },
953
+ { value: '3', label: 'Option 3' },
954
+ { value: '4', label: 'Option 4' },
955
+ ];
956
+
957
+ return (
958
+ <Select
959
+ type="multi"
960
+ SelectAllOptionComponent={CustomSelectAllOption}
961
+ label="Custom Select All"
962
+ value={value}
963
+ onChange={onChange}
964
+ options={options}
965
+ placeholder="Select options"
966
+ selectAllLabel="Pick All Items"
967
+ />
968
+ );
969
+ }
970
+ ```
971
+
972
+ ### Edge Case Labels
973
+
974
+ Handle edge cases with empty or special character labels.
975
+
976
+ ```jsx
977
+ function EdgeCaseLabelsExample() {
978
+ const [value, setValue] = useState('3');
979
+
980
+ const edgeOptions = [
981
+ { value: '1', label: '' },
982
+ { value: '2', label: ' ' },
983
+ { value: '3', label: 'Normal Label' },
984
+ { value: '4', label: '\t\n' },
985
+ { value: '5', label: '🚀🌟💫' },
986
+ { value: '6', label: '©™®' },
987
+ ];
988
+
989
+ return (
990
+ <Select
991
+ label="Edge Case Labels"
992
+ value={value}
993
+ onChange={setValue}
994
+ options={edgeOptions}
995
+ placeholder="Select an option"
996
+ />
997
+ );
998
+ }
999
+ ```
1000
+
1001
+ ### Stress Test Many Options
1002
+
1003
+ Test performance with many options on mobile.
1004
+
1005
+ ```jsx
1006
+ function StressTestExample() {
1007
+ const { value, onChange } = useMultiSelect({
1008
+ initialValue: ['1', '5', '10'],
1009
+ });
1010
+
1011
+ const manyOptions = Array.from({ length: 100 }, (_, i) => ({
1012
+ value: (i + 1).toString(),
1013
+ label: `Option ${i + 1}`,
1014
+ description: i % 3 === 0 ? `Description for ${i + 1}` : undefined,
1015
+ disabled: i % 15 === 0,
1016
+ accessory: i % 10 === 0 ? <Icon color="fg" name="star" /> : undefined,
1017
+ }));
1018
+
1019
+ return (
1020
+ <Select
1021
+ type="multi"
1022
+ label="Stress Test - 100 Options"
1023
+ value={value}
1024
+ onChange={onChange}
1025
+ options={manyOptions}
1026
+ maxSelectedOptionsToShow={5}
1027
+ placeholder="Select from many"
1028
+ />
1029
+ );
1030
+ }
1031
+ ```
1032
+
1033
+ ### Custom styles
1034
+
1035
+ You can use custom styles on the various subcomponents in Select.
1036
+
1037
+ ```jsx
1038
+ function CustomStylesExample() {
1039
+ const exampleOptions = [
1040
+ { value: null, label: 'Remove selection' },
1041
+ { value: '1', label: 'Option 1' },
1042
+ { value: '2', label: 'Option 2' },
1043
+ { value: '3', label: 'Option 3' },
1044
+ { value: '4', label: 'Option 4' },
1045
+ ];
1046
+ const [value, setValue] = useState('1');
1047
+
1048
+ return (
1049
+ <Select
1050
+ label="Single select - custom styles"
1051
+ onChange={setValue}
1052
+ options={exampleOptions}
1053
+ styles={{
1054
+ control: {
1055
+ backgroundColor: 'lightgray',
1056
+ padding: 10,
1057
+ },
1058
+ option: {
1059
+ backgroundColor: 'lightblue',
1060
+ },
1061
+ optionBlendStyles: {
1062
+ pressedBackground: 'darkgreen',
1063
+ },
1064
+ }}
1065
+ value={value}
1066
+ />
1067
+ );
1068
+ }
1069
+ ```
1070
+
1071
+ ### Custom class names
1072
+
1073
+ You can use custom class names on the various subcomponents in Select.
1074
+
1075
+ ```jsx
1076
+ function CustomClassNamesExamples() {
1077
+ const exampleOptions = [
1078
+ { value: null, label: 'Remove selection' },
1079
+ { value: '1', label: 'Option 1' },
1080
+ { value: '2', label: 'Option 2' },
1081
+ { value: '3', label: 'Option 3' },
1082
+ { value: '4', label: 'Option 4' },
1083
+ ];
1084
+ const [value, setValue] = useState('1');
1085
+
1086
+ return (
1087
+ <Select
1088
+ classNames={{
1089
+ control: customControlStyles,
1090
+ option: customOptionStyles,
1091
+ }}
1092
+ label="Single select - class names"
1093
+ onChange={setValue}
1094
+ options={exampleOptions}
1095
+ placeholder="Empty value"
1096
+ value={value}
1097
+ />
1098
+ );
1099
+ }
1100
+ ```
1101
+
1102
+ ### Custom components
1103
+
1104
+ Select is highly customizable. Use the _Component_ props to customize the various subcomponents in Select.
1105
+
1106
+ #### Customizable subcomponents
1107
+
1108
+ - **SelectControlComponent**: Trigger component used to open and close the Select.
1109
+ - **SelectDropdownComponent**: Component which renders the dropdown menu and SelectOptionComponents.
1110
+ - **SelectOptionComponent**: Component which renders the content of an option in the select.
1111
+ - **SelectAllOptionComponent**: Component which renders the Select All option in a multi-select select menu.
1112
+ - **SelectEmptyDropdownContentsComponent**: Component which renders as the select menu's content when no options are passed in.
1113
+
1114
+ Below is a diagram to help visualize the Select anatomy.
1115
+
1116
+ ```text
1117
+ Select
1118
+ ├── SelectControlComponent (trigger to open/close)
1119
+ └── SelectDropdownComponent (dropdown menu)
1120
+ ├── SelectAllOptionComponent
1121
+ ├── SelectOptionComponent (option 1)
1122
+ ├── SelectOptionComponent (option 2)
1123
+ ├── SelectOptionComponent (option 3)
1124
+ └── SelectOptionComponent (option N...)
1125
+ ```
1126
+
1127
+ #### Example
1128
+
1129
+ ```jsx
1130
+ function CustomComponentExamples() {
1131
+ const exampleOptions = [
1132
+ { value: null, label: 'Remove selection' },
1133
+ { value: '1', label: 'Option 1' },
1134
+ { value: '2', label: 'Option 2' },
1135
+ { value: '3', label: 'Option 3' },
1136
+ { value: '4', label: 'Option 4' },
1137
+ ];
1138
+ const [value, setValue] = useState('1');
1139
+
1140
+ const CustomControlComponent: SelectControlComponent = ({ value, setOpen }) => {
1141
+ return <Button onPress={() => setOpen(true)}>{value ?? 'Empty value'}</Button>;
1142
+ };
1143
+
1144
+ const CustomOptionComponent: SelectOptionComponent = ({ value, onPress }) => {
1145
+ return (
1146
+ <HStack justifyContent="center">
1147
+ <Spinner size={4} />
1148
+ <Button transparent onPress={() => onPress?.(value)} width="80%">
1149
+ <Text>{value ?? 'Empty value'}</Text>
1150
+ </Button>
1151
+ <Spinner size={4} />
1152
+ </HStack>
1153
+ );
1154
+ };
1155
+
1156
+ return (
1157
+ <Select
1158
+ SelectOptionComponent={CustomOptionComponent}
1159
+ label="Single select - custom option component"
1160
+ onChange={setValue}
1161
+ options={exampleOptions}
1162
+ placeholder="Empty value"
1163
+ value={value}
1164
+ />
1165
+ );
1166
+ }
1167
+ ```
1168
+
1169
+ ## Props
1170
+
1171
+ | Prop | Type | Required | Default | Description |
1172
+ | --- | --- | --- | --- | --- |
1173
+ | `onChange` | `(value: Type extends multi ? SelectOptionValue \| SelectOptionValue[] \| null : SelectOptionValue \| null) => void` | Yes | `-` | - |
1174
+ | `options` | `(SelectOption<SelectOptionValue> & Pick<SelectOptionProps<Type, string>, media \| end \| accessory> & { Component?: SelectOptionComponent<Type, SelectOptionValue> \| undefined; })[]` | Yes | `-` | Array of options to display in the select dropdown |
1175
+ | `value` | `string \| SelectOptionValue[] \| null` | Yes | `-` | - |
1176
+ | `SelectAllOptionComponent` | `SelectOptionComponent<Type, SelectOptionValue>` | No | `-` | Custom component to render the Select All option |
1177
+ | `SelectControlComponent` | `SelectControlComponent<Type, SelectOptionValue>` | No | `-` | Custom component to render the select control |
1178
+ | `SelectDropdownComponent` | `SelectDropdownComponent<Type, SelectOptionValue>` | No | `-` | Custom component to render the dropdown container |
1179
+ | `SelectEmptyDropdownContentsComponent` | `SelectEmptyDropdownContentComponent` | No | `-` | Custom component to render when no options are available |
1180
+ | `SelectOptionComponent` | `SelectOptionComponent<Type, SelectOptionValue>` | No | `-` | Custom component to render individual options |
1181
+ | `accessibilityRoles` | `{ option?: AccessibilityRole; } \| undefined` | No | `-` | Accessibility roles for dropdown elements |
1182
+ | `accessory` | `ReactElement<CellAccessoryProps, string \| JSXElementConstructor<any>>` | No | `-` | - |
1183
+ | `clearAllLabel` | `string` | No | `-` | Label for the Clear All option in multi-select mode |
1184
+ | `compact` | `boolean` | No | `-` | Whether to use compact styling for the select |
1185
+ | `defaultOpen` | `boolean` | No | `-` | Initial open state when component mounts (uncontrolled mode) |
1186
+ | `disableClickOutsideClose` | `boolean` | No | `-` | Whether clicking outside the dropdown should close it |
1187
+ | `disabled` | `boolean` | No | `false` | Toggles input interactability and opacity |
1188
+ | `emptyOptionsLabel` | `string` | No | `-` | Label displayed when there are no options available |
1189
+ | `end` | `null \| string \| number \| false \| true \| ReactElement<any, string \| JSXElementConstructor<any>> \| Iterable<ReactNode> \| ReactPortal` | No | `-` | End-aligned content (e.g., value, status). Replaces the deprecated detail prop. |
1190
+ | `endNode` | `null \| string \| number \| false \| true \| ReactElement<any, string \| JSXElementConstructor<any>> \| Iterable<ReactNode> \| ReactPortal` | No | `-` | Adds content to the end of the inner input. Refer to diagram for location of endNode in InputStack component |
1191
+ | `helperText` | `null \| string \| number \| false \| true \| ReactElement<any, string \| JSXElementConstructor<any>> \| Iterable<ReactNode> \| ReactPortal` | No | `-` | Helper text displayed below the select |
1192
+ | `hiddenSelectedOptionsLabel` | `string` | No | `-` | Label to show for showcasing count of hidden selected options |
1193
+ | `hideSelectAll` | `boolean` | No | `-` | Whether to hide the Select All option in multi-select mode |
1194
+ | `label` | `null \| string \| number \| false \| true \| ReactElement<any, string \| JSXElementConstructor<any>> \| Iterable<ReactNode> \| ReactPortal` | No | `-` | Label displayed above the control |
1195
+ | `labelVariant` | `inside \| outside` | No | `'outside'` | The variant of the label. Only used when compact is not true. |
1196
+ | `maxSelectedOptionsToShow` | `number` | No | `-` | Maximum number of selected options to show before truncating |
1197
+ | `media` | `ReactElement` | No | `-` | Media rendered at the start of the cell (icon, avatar, image, etc). |
1198
+ | `open` | `boolean` | No | `-` | Controlled open state of the dropdown |
1199
+ | `placeholder` | `null \| string \| number \| false \| true \| ReactElement<any, string \| JSXElementConstructor<any>> \| Iterable<ReactNode> \| ReactPortal` | No | `-` | Placeholder text displayed when no option is selected |
1200
+ | `ref` | `null \| (instance: SelectRef \| null) => void \| RefObject<SelectRef>` | No | `-` | - |
1201
+ | `removeSelectedOptionAccessibilityLabel` | `string` | No | `-` | Accessibility label for each chip in a multi-select |
1202
+ | `selectAllLabel` | `string` | No | `-` | Label for the Select All option in multi-select mode |
1203
+ | `setOpen` | `((open: boolean \| ((open: boolean) => boolean)) => void)` | No | `-` | Callback to update the open state |
1204
+ | `startNode` | `null \| string \| number \| false \| true \| ReactElement<any, string \| JSXElementConstructor<any>> \| Iterable<ReactNode> \| ReactPortal` | No | `-` | Adds content to the start of the inner input. Refer to diagram for location of startNode in InputStack component |
1205
+ | `style` | `null \| false \| ViewStyle \| RegisteredStyle<ViewStyle> \| RecursiveArray<Falsy \| ViewStyle \| RegisteredStyle<ViewStyle>>` | No | `-` | Inline styles for the root element |
1206
+ | `styles` | `{ root?: StyleProp<ViewStyle>; control?: StyleProp<ViewStyle>; controlStartNode?: StyleProp<ViewStyle>; controlInputNode?: StyleProp<ViewStyle>; controlValueNode?: StyleProp<ViewStyle>; controlLabelNode?: StyleProp<ViewStyle>; controlHelperTextNode?: StyleProp<ViewStyle>; controlEndNode?: StyleProp<ViewStyle>; controlBlendStyles?: InteractableBlendStyles; dropdown?: StyleProp<ViewStyle>; option?: StyleProp<ViewStyle>; optionCell?: StyleProp<ViewStyle>; optionContent?: StyleProp<ViewStyle>; optionLabel?: StyleProp<ViewStyle>; optionDescription?: StyleProp<ViewStyle>; optionBlendStyles?: InteractableBlendStyles \| undefined; selectAllDivider?: StyleProp<ViewStyle>; emptyContentsContainer?: StyleProp<ViewStyle>; emptyContentsText?: StyleProp<ViewStyle>; } \| undefined` | No | `-` | Custom styles for different parts of the select |
1207
+ | `testID` | `string` | No | `-` | Test ID for the root element |
1208
+ | `type` | `single \| multi` | No | `-` | Whether the select allows single or multiple selections |
1209
+ | `variant` | `primary \| secondary \| positive \| negative \| foregroundMuted \| foreground` | No | `-` | Determines the sentiment of the input. Because we allow startContent and endContent to be custom ReactNode, the content placed inside these slots will not change colors according to the variant. You will have to add that yourself |
1210
+
1211
+