@patternfly/react-core 5.0.0-alpha.29 → 5.0.0-alpha.30

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.
@@ -10,7 +10,15 @@ import CubeIcon from '@patternfly/react-icons/dist/esm/icons/cube-icon';
10
10
 
11
11
  ## Examples
12
12
 
13
- ### Single
13
+ ### Single select
14
+
15
+ To let users select a single item from a list, use a single select list.
16
+
17
+ A select list may use other properties for additional customization. Select each checkbox in the example below to visualize the following behavior:
18
+
19
+ - To prevent a toggle click from opening a select list, use the `isDisabled` property.
20
+ - To adjust the direction a select menu opens, use the `direction` property. The menu in the following example expands upwards. By default, select lists open upwards.
21
+ - To add an icon to a select toggle, use the `toggleIcon` property.
14
22
 
15
23
  ```js
16
24
  import React from 'react';
@@ -144,7 +152,9 @@ class SingleSelectInput extends React.Component {
144
152
  }
145
153
  ```
146
154
 
147
- ### Single with description
155
+ ### With item descriptions
156
+
157
+ To give more context to a `<SelectOption>` in a list, use the `description` property.
148
158
 
149
159
  ```js
150
160
  import React from 'react';
@@ -232,7 +242,9 @@ class SingleSelectDescription extends React.Component {
232
242
  }
233
243
  ```
234
244
 
235
- ### Grouped single
245
+ ### With grouped items
246
+
247
+ To group related select options together, use 1 or more `<SelectGroup>` components and title each group using the `label` property.
236
248
 
237
249
  ```js
238
250
  import React from 'react';
@@ -312,7 +324,104 @@ class GroupedSingleSelectInput extends React.Component {
312
324
  }
313
325
  ```
314
326
 
315
- ### Validated
327
+ ### Favoriting items
328
+
329
+ To allow users to favorite items in a select list, use the `onFavorite` callback. When users click the favorite button, the item is duplicated and placed in a separated group at the top of the menu. To change the name of the group use the `favoritesLabel` property.
330
+
331
+ ```js
332
+ import React from 'react';
333
+ import { Select, SelectOption, SelectVariant, SelectGroup } from '@patternfly/react-core';
334
+
335
+ class FavoritesSelect extends React.Component {
336
+ constructor(props) {
337
+ super(props);
338
+ this.state = {
339
+ isOpen: false,
340
+ selected: null,
341
+ favorites: []
342
+ };
343
+
344
+ this.onToggle = (_event, isOpen) => {
345
+ this.setState({
346
+ isOpen
347
+ });
348
+ };
349
+
350
+ this.onSelect = (event, selection, isPlaceholder) => {
351
+ if (isPlaceholder) this.clearSelection();
352
+ else {
353
+ this.setState({
354
+ selected: selection,
355
+ isOpen: false
356
+ });
357
+ console.log('selected:', selection);
358
+ }
359
+ };
360
+
361
+ this.clearSelection = () => {
362
+ this.setState({
363
+ selected: null,
364
+ isOpen: false
365
+ });
366
+ };
367
+
368
+ this.onFavorite = (itemId, isFavorite) => {
369
+ if (isFavorite) {
370
+ this.setState({
371
+ favorites: this.state.favorites.filter(id => id !== itemId)
372
+ });
373
+ } else
374
+ this.setState({
375
+ favorites: [...this.state.favorites, itemId]
376
+ });
377
+ };
378
+
379
+ this.options = [
380
+ <SelectGroup label="Status" key="group1">
381
+ <SelectOption id={'option-1'} key={0} value="Running" description="This is a description." />
382
+ <SelectOption id={'option-2'} key={1} value="Stopped" />
383
+ <SelectOption id={'option-3'} key={2} value="Down (disabled)" isDisabled />
384
+ <SelectOption id={'option-4'} key={3} value="Degraded" />
385
+ <SelectOption id={'option-5'} key={4} value="Needs maintenance" />
386
+ </SelectGroup>,
387
+ <SelectGroup label="Vendor names" key="group2">
388
+ <SelectOption id={'option-6'} key={5} value="Dell" />
389
+ <SelectOption id={'option-7'} key={6} value="Samsung" description="This is a description." />
390
+ <SelectOption id={'option-8'} key={7} value="Hewlett-Packard" />
391
+ </SelectGroup>
392
+ ];
393
+ }
394
+
395
+ render() {
396
+ const { isOpen, selected, favorites } = this.state;
397
+ const titleId = 'grouped-single-select-id';
398
+ return (
399
+ <Select
400
+ variant={SelectVariant.typeahead}
401
+ typeAheadAriaLabel="Select value"
402
+ onToggle={this.onToggle}
403
+ onSelect={this.onSelect}
404
+ selections={selected}
405
+ isOpen={isOpen}
406
+ placeholderText="Favorites"
407
+ aria-labelledby={titleId}
408
+ isGrouped
409
+ onFavorite={this.onFavorite}
410
+ favorites={favorites}
411
+ onClear={this.clearSelection}
412
+ >
413
+ {this.options}
414
+ </Select>
415
+ );
416
+ }
417
+ }
418
+ ```
419
+
420
+ ### Validated selections
421
+
422
+ To validate selections that users make, pass a validation state to the `validated` property. Validating selections can let users know if the selections they make would cause issues or errors.
423
+
424
+ The example below passes an "error" state when you choose “select a title”, a "warning" state when you choose "other", and a "success" state for any other item selected from the menu.
316
425
 
317
426
  ```js
318
427
  import React from 'react';
@@ -411,19 +520,152 @@ class ValidatedSelect extends React.Component {
411
520
  }
412
521
  ```
413
522
 
414
- ### Checkbox input
523
+ ### Styled placeholder text
524
+
525
+ To add a toggle label to a select, use the `placeholderText` property. The following example displays "Filter by status" in the toggle before a selection is made.
526
+
527
+ To fade the color of `placeholderText` to gray, use the `hasPlaceholderStyle` property.
415
528
 
416
529
  ```js
417
530
  import React from 'react';
418
- import { Select, SelectOption, SelectVariant, Divider } from '@patternfly/react-core';
531
+ import { Select, SelectOption, SelectVariant } from '@patternfly/react-core';
419
532
 
420
- class CheckboxSelectInput extends React.Component {
533
+ function SelectWithPlaceholderStyle() {
534
+ const [isOpen, setIsOpen] = React.useState(false);
535
+ const [selected, setSelected] = React.useState([]);
536
+
537
+ const options = [
538
+ <SelectOption key={0} value="Active" />,
539
+ <SelectOption key={1} value="Cancelled" />,
540
+ <SelectOption key={2} value="Paused" />
541
+ ];
542
+
543
+ const onToggle = (_event, isOpen) => setIsOpen(isOpen);
544
+
545
+ const onSelect = (event, selection, isPlaceholder) => {
546
+ setSelected(selection);
547
+ setIsOpen(false);
548
+ };
549
+
550
+ const clearSelection = () => {
551
+ setSelected(null);
552
+ setIsOpen(false);
553
+ };
554
+
555
+ const titleId = 'placeholder-style-select-id';
556
+
557
+ return (
558
+ <div>
559
+ <span id={titleId} hidden>
560
+ Placeholder styles
561
+ </span>
562
+ <Select
563
+ variant={SelectVariant.single}
564
+ hasPlaceholderStyle
565
+ aria-label="Select input"
566
+ onToggle={onToggle}
567
+ onSelect={onSelect}
568
+ onClear={clearSelection}
569
+ selections={selected}
570
+ isOpen={isOpen}
571
+ placeholderText="Filter by status"
572
+ aria-labelledby={titleId}
573
+ >
574
+ {options}
575
+ </Select>
576
+ </div>
577
+ );
578
+ }
579
+ ```
580
+
581
+ ### Placeholder select options
582
+
583
+ To set a `<SelectOption>` as a placeholder, use the `isPlaceholder` property. The following example sets the "Filter by status" as a placeholder so that it is pre-selected.
584
+
585
+ ```js
586
+
587
+ import React from 'react';
588
+ import { Select, SelectOption, SelectVariant } from '@patternfly/react-core';
589
+
590
+ function SelectWithPlaceholderStyle() {
591
+ const [isOpen, setIsOpen] = React.useState(false);
592
+ const [selected, setSelected] = React.useState([]);
593
+
594
+ const options = [
595
+ <SelectOption key={0} value="Filter by status" isPlaceholder />,
596
+ <SelectOption key={1} value="Active" />,
597
+ <SelectOption key={2} value="Cancelled" />,
598
+ <SelectOption key={3} value="Paused" />
599
+ ];
600
+
601
+ const onToggle = (_event, isOpen) => setIsOpen(isOpen);
602
+
603
+ const onSelect = (event, selection, isPlaceholder) => {
604
+ setSelected(selection);
605
+ setIsOpen(false);
606
+ };
607
+
608
+ const clearSelection = () => {
609
+ setSelected(null);
610
+ setIsOpen(false);
611
+ };
612
+
613
+ const titleId = 'placeholder-style-select-option-id';
614
+
615
+ return (
616
+ <div>
617
+ <span id={titleId} hidden>
618
+ Placeholder styles - select option
619
+ </span>
620
+ <Select
621
+ variant={SelectVariant.single}
622
+ hasPlaceholderStyle
623
+ aria-label="Select input"
624
+ onToggle={onToggle}
625
+ onSelect={onSelect}
626
+ onClear={clearSelection}
627
+ selections={selected}
628
+ isOpen={isOpen}
629
+ aria-labelledby={titleId}
630
+ >
631
+ {options}
632
+ </Select>
633
+ </div>
634
+ );
635
+ }
636
+ ```
637
+
638
+ ### With a footer
639
+
640
+ You can add a `footer` to a `<Select>` list to hold actions that users can take on menu items.
641
+
642
+ ```js
643
+ import React from 'react';
644
+ import CubeIcon from '@patternfly/react-icons/dist/esm/icons/cube-icon';
645
+ import { Select, SelectOption, SelectVariant, SelectDirection, Divider, Button } from '@patternfly/react-core';
646
+
647
+ class SelectWithFooter extends React.Component {
421
648
  constructor(props) {
422
649
  super(props);
650
+ this.options = [
651
+ <SelectOption key={0} value="Select a title" isPlaceholder />,
652
+ <SelectOption key={1} value="Mr" />,
653
+ <SelectOption key={2} value="Miss" />,
654
+ <SelectOption key={3} value="Mrs" />,
655
+ <SelectOption key={4} value="Ms" />,
656
+ <Divider component="li" key={5} />,
657
+ <SelectOption key={6} value="Dr" />,
658
+ <SelectOption key={7} value="Other" />
659
+ ];
660
+
661
+ this.toggleRef = React.createRef();
423
662
 
424
663
  this.state = {
664
+ isToggleIcon: false,
425
665
  isOpen: false,
426
- selected: []
666
+ selected: null,
667
+ isDisabled: false,
668
+ direction: SelectDirection.down
427
669
  };
428
670
 
429
671
  this.onToggle = (_event, isOpen) => {
@@ -432,54 +674,53 @@ class CheckboxSelectInput extends React.Component {
432
674
  });
433
675
  };
434
676
 
435
- this.onSelect = (event, selection) => {
436
- const { selected } = this.state;
437
- if (selected.includes(selection)) {
438
- this.setState(
439
- prevState => ({ selected: prevState.selected.filter(item => item !== selection) }),
440
- () => console.log('selections: ', this.state.selected)
441
- );
442
- } else {
443
- this.setState(
444
- prevState => ({ selected: [...prevState.selected, selection] }),
445
- () => console.log('selections: ', this.state.selected)
446
- );
677
+ this.onSelect = (event, selection, isPlaceholder) => {
678
+ if (isPlaceholder) this.clearSelection();
679
+ else {
680
+ this.setState({
681
+ selected: selection,
682
+ isOpen: false
683
+ });
684
+ console.log('selected:', selection);
685
+ this.toggleRef.current.focus();
447
686
  }
448
687
  };
449
688
 
450
689
  this.clearSelection = () => {
451
690
  this.setState({
452
- selected: []
691
+ selected: null,
692
+ isOpen: false
453
693
  });
454
694
  };
455
-
456
- this.options = [
457
- <SelectOption key={0} value="Active" description="This is a description" />,
458
- <SelectOption key={1} value="Cancelled" />,
459
- <SelectOption key={2} value="Paused" />,
460
- <Divider key={3} />,
461
- <SelectOption key={4} value="Warning" />,
462
- <SelectOption key={5} value="Restarted" />
463
- ];
464
- }
695
+ }
465
696
 
466
697
  render() {
467
- const { isOpen, selected } = this.state;
468
- const titleId = 'checkbox-select-id';
698
+ const { isOpen, selected, isDisabled, direction, isToggleIcon } = this.state;
699
+ const titleId = 'title-id-footer';
469
700
  return (
470
701
  <div>
471
702
  <span id={titleId} hidden>
472
- Checkbox Title
703
+ Title
473
704
  </span>
474
705
  <Select
475
- variant={SelectVariant.checkbox}
706
+ toggleIcon={isToggleIcon && <CubeIcon />}
707
+ toggleRef={this.toggleRef}
708
+ variant={SelectVariant.single}
476
709
  aria-label="Select Input"
477
710
  onToggle={this.onToggle}
478
711
  onSelect={this.onSelect}
479
712
  selections={selected}
480
713
  isOpen={isOpen}
481
- placeholderText="Filter by status"
482
714
  aria-labelledby={titleId}
715
+ isDisabled={isDisabled}
716
+ direction={direction}
717
+ footer={
718
+ <>
719
+ <Button variant="link" isInline>
720
+ Action
721
+ </Button>
722
+ </>
723
+ }
483
724
  >
484
725
  {this.options}
485
726
  </Select>
@@ -489,19 +730,36 @@ class CheckboxSelectInput extends React.Component {
489
730
  }
490
731
  ```
491
732
 
492
- ### Checkbox input with counts
733
+ ### With view more
734
+
735
+ To reduce the processing load for long select lists, replace overflow items with a "View more" link at the bottom of the select menu.
736
+
737
+ Adjust the number of items shown above the "View more" link with the `numOptions` property. The following example passes 3 items into this property.
493
738
 
494
739
  ```js
495
740
  import React from 'react';
496
- import { Select, SelectOption, SelectVariant, Divider } from '@patternfly/react-core';
741
+ import { Select, SelectOption, SelectVariant } from '@patternfly/react-core';
497
742
 
498
- class CheckboxSelectWithCounts extends React.Component {
743
+ class SelectViewMore extends React.Component {
499
744
  constructor(props) {
500
745
  super(props);
746
+ this.options = [
747
+ <SelectOption key={0} value="Select a title" isPlaceholder />,
748
+ <SelectOption key={1} value="Mr" />,
749
+ <SelectOption key={2} value="Miss" />,
750
+ <SelectOption key={3} value="Mrs" />,
751
+ <SelectOption key={4} value="Ms" />,
752
+ <SelectOption key={5} value="Dr" />,
753
+ <SelectOption key={6} value="Other" />
754
+ ];
755
+
756
+ this.toggleRef = React.createRef();
501
757
 
502
758
  this.state = {
503
759
  isOpen: false,
504
- selected: []
760
+ selected: null,
761
+ numOptions: 3,
762
+ isLoading: false
505
763
  };
506
764
 
507
765
  this.onToggle = (_event, isOpen) => {
@@ -510,55 +768,64 @@ class CheckboxSelectWithCounts extends React.Component {
510
768
  });
511
769
  };
512
770
 
513
- this.onSelect = (event, selection) => {
514
- const { selected } = this.state;
515
- if (selected.includes(selection)) {
516
- this.setState(
517
- prevState => ({ selected: prevState.selected.filter(item => item !== selection) }),
518
- () => console.log('selections: ', this.state.selected)
519
- );
520
- } else {
521
- this.setState(
522
- prevState => ({ selected: [...prevState.selected, selection] }),
523
- () => console.log('selections: ', this.state.selected)
524
- );
771
+ this.onSelect = (event, selection, isPlaceholder) => {
772
+ if (isPlaceholder) this.clearSelection();
773
+ else {
774
+ this.setState({
775
+ selected: selection,
776
+ isOpen: false
777
+ });
778
+ console.log('selected:', selection);
779
+ this.toggleRef.current.focus();
525
780
  }
526
781
  };
527
782
 
528
783
  this.clearSelection = () => {
529
784
  this.setState({
530
- selected: []
785
+ selected: null,
786
+ isOpen: false
531
787
  });
532
788
  };
533
789
 
534
- this.options = [
535
- <SelectOption key={0} value="Active" description="This is a description" itemCount={3} />,
536
- <SelectOption key={1} value="Cancelled" itemCount={1} />,
537
- <SelectOption key={2} value="Paused" itemCount={15} />,
538
- <SelectOption key={3} value="Warning" itemCount={2} />,
539
- <SelectOption key={4} value="Restarted" itemCount={8} />
540
- ];
790
+ this.simulateNetworkCall = callback => {
791
+ setTimeout(callback, 2000);
792
+ };
793
+
794
+ this.onViewMoreClick = () => {
795
+ // Set select loadingVariant to spinner then simulate network call before loading more options
796
+ this.setState({ isLoading: true });
797
+ this.simulateNetworkCall(() => {
798
+ const newLength =
799
+ this.state.numOptions + 3 <= this.options.length ? this.state.numOptions + 3 : this.options.length;
800
+ this.setState({ numOptions: newLength, isLoading: false });
801
+ });
802
+ };
541
803
  }
542
804
 
543
805
  render() {
544
- const { isOpen, selected } = this.state;
545
- const titleId = 'checkbox-select-with-counts-id';
806
+ const { isOpen, selected, isToggleIcon, numOptions, loadingVariant, isLoading } = this.state;
807
+ const titleId = 'title-id-view-more';
546
808
  return (
547
809
  <div>
548
810
  <span id={titleId} hidden>
549
- Checkbox With Counts Title
811
+ Title
550
812
  </span>
551
813
  <Select
552
- variant={SelectVariant.checkbox}
814
+ variant={SelectVariant.single}
815
+ toggleRef={this.toggleRef}
553
816
  aria-label="Select Input"
554
817
  onToggle={this.onToggle}
555
818
  onSelect={this.onSelect}
556
819
  selections={selected}
557
820
  isOpen={isOpen}
558
- placeholderText="Filter by status"
559
821
  aria-labelledby={titleId}
822
+ {...(!isLoading &&
823
+ numOptions < this.options.length && {
824
+ loadingVariant: { text: 'View more', onClick: this.onViewMoreClick }
825
+ })}
826
+ {...(isLoading && { loadingVariant: 'spinner' })}
560
827
  >
561
- {this.options}
828
+ {this.options.slice(0, numOptions)}
562
829
  </Select>
563
830
  </div>
564
831
  );
@@ -566,13 +833,17 @@ class CheckboxSelectWithCounts extends React.Component {
566
833
  }
567
834
  ```
568
835
 
569
- ### Checkbox input no badge
836
+ ### Checkbox select
837
+
838
+ To let users select multiple list options via checkbox input, use a checkbox select. To create a checkbox select, pass `variant={SelectVariant.checkbox}` into the `<Select>` component.
839
+
840
+ By default, a badge is displayed in the menu toggle that indicates the number of items a user has selected.
570
841
 
571
842
  ```js
572
843
  import React from 'react';
573
- import { Select, SelectOption, SelectVariant } from '@patternfly/react-core';
844
+ import { Select, SelectOption, SelectVariant, Divider } from '@patternfly/react-core';
574
845
 
575
- class CheckboxSelectInputNoBadge extends React.Component {
846
+ class CheckboxSelectInput extends React.Component {
576
847
  constructor(props) {
577
848
  super(props);
578
849
 
@@ -609,16 +880,18 @@ class CheckboxSelectInputNoBadge extends React.Component {
609
880
  };
610
881
 
611
882
  this.options = [
612
- <SelectOption key={0} value="Debug" />,
613
- <SelectOption key={1} value="Info" />,
614
- <SelectOption key={2} value="Warn" />,
615
- <SelectOption key={3} value="Error" />
883
+ <SelectOption key={0} value="Active" description="This is a description" />,
884
+ <SelectOption key={1} value="Cancelled" />,
885
+ <SelectOption key={2} value="Paused" />,
886
+ <Divider key={3} />,
887
+ <SelectOption key={4} value="Warning" />,
888
+ <SelectOption key={5} value="Restarted" />
616
889
  ];
617
890
  }
618
891
 
619
892
  render() {
620
893
  const { isOpen, selected } = this.state;
621
- const titleId = 'checkbox-no-badge-select-id';
894
+ const titleId = 'checkbox-select-id';
622
895
  return (
623
896
  <div>
624
897
  <span id={titleId} hidden>
@@ -630,7 +903,6 @@ class CheckboxSelectInputNoBadge extends React.Component {
630
903
  onToggle={this.onToggle}
631
904
  onSelect={this.onSelect}
632
905
  selections={selected}
633
- isCheckboxSelectionBadgeHidden
634
906
  isOpen={isOpen}
635
907
  placeholderText="Filter by status"
636
908
  aria-labelledby={titleId}
@@ -643,7 +915,9 @@ class CheckboxSelectInputNoBadge extends React.Component {
643
915
  }
644
916
  ```
645
917
 
646
- ### Grouped checkbox input
918
+ ### Checkbox select with grouped items
919
+
920
+ You can use groups alongside checkbox input. The item count badge will display the number of items selected across all groups.
647
921
 
648
922
  ```js
649
923
  import React from 'react';
@@ -726,26 +1000,24 @@ class GroupedCheckboxSelectInput extends React.Component {
726
1000
  }
727
1001
  ```
728
1002
 
729
- ### Grouped single with filtering
1003
+ ### Checkbox select with custom badge
1004
+
1005
+ To change the default badge text for a checkbox select, use the `customBadgeText` property. The following example uses `customBadgeText` to display "all" in the badge once all menu items are selected.
730
1006
 
731
1007
  ```js
732
1008
  import React from 'react';
733
- import { Select, SelectOption, SelectGroup, SelectVariant, Checkbox } from '@patternfly/react-core';
1009
+ import { Select, SelectOption, SelectGroup, SelectVariant } from '@patternfly/react-core';
734
1010
 
735
- class FilteringSingleSelectInput extends React.Component {
1011
+ class FilteringCheckboxSelectInputWithBadging extends React.Component {
736
1012
  constructor(props) {
737
1013
  super(props);
738
1014
 
739
1015
  this.state = {
740
1016
  isOpen: false,
741
- selected: '',
742
- isCreatable: false,
743
- isInputValuePersisted: false,
744
- isInputFilterPersisted: false
1017
+ selected: [],
1018
+ customBadgeText: 0
745
1019
  };
746
1020
 
747
- this.toggleRef = React.createRef();
748
-
749
1021
  this.options = [
750
1022
  <SelectGroup label="Status" key="group1">
751
1023
  <SelectOption key={0} value="Running" />
@@ -762,12 +1034,30 @@ class FilteringSingleSelectInput extends React.Component {
762
1034
  ];
763
1035
 
764
1036
  this.onToggle = (_event, isOpen) => {
765
- this.setState({ isOpen });
1037
+ this.setState({
1038
+ isOpen
1039
+ });
766
1040
  };
767
1041
 
768
1042
  this.onSelect = (event, selection) => {
769
- this.setState({ selected: selection, isOpen: false }), console.log('selected: ', selection);
770
- this.toggleRef.current.focus();
1043
+ const { selected } = this.state;
1044
+ if (selected.includes(selection)) {
1045
+ this.setState(
1046
+ prevState => ({
1047
+ selected: prevState.selected.filter(item => item !== selection),
1048
+ customBadgeText: this.setBadgeText(prevState.selected.length - 1)
1049
+ }),
1050
+ () => console.log('selections: ', this.state.selected)
1051
+ );
1052
+ } else {
1053
+ this.setState(
1054
+ prevState => ({
1055
+ selected: [...prevState.selected, selection],
1056
+ customBadgeText: this.setBadgeText(prevState.selected.length + 1)
1057
+ }),
1058
+ () => console.log('selections: ', this.state.selected)
1059
+ );
1060
+ }
771
1061
  };
772
1062
 
773
1063
  this.onFilter = (_, textInput) => {
@@ -783,48 +1073,39 @@ class FilteringSingleSelectInput extends React.Component {
783
1073
  });
784
1074
  if (filteredGroup.props.children.length > 0) return filteredGroup;
785
1075
  })
786
- .filter(Boolean);
1076
+ .filter(newGroup => newGroup);
787
1077
  return filteredGroups;
788
1078
  }
789
1079
  };
790
1080
 
791
- this.toggleCreatable = checked => {
792
- this.setState({
793
- isCreatable: checked
794
- });
795
- };
796
-
797
- this.toggleInputValuePersisted = checked => {
1081
+ this.clearSelection = () => {
798
1082
  this.setState({
799
- isInputValuePersisted: checked
1083
+ selected: [],
1084
+ customBadgeText: this.setBadgeText(0)
800
1085
  });
801
1086
  };
802
1087
 
803
- this.toggleInputFilterPersisted = checked => {
804
- this.setState({
805
- isInputFilterPersisted: checked
806
- });
1088
+ this.setBadgeText = selected => {
1089
+ if (selected === 7) {
1090
+ return 'All';
1091
+ }
1092
+ if (selected === 0) {
1093
+ return 0;
1094
+ }
1095
+ return null;
807
1096
  };
808
1097
  }
809
1098
 
810
1099
  render() {
811
- const {
812
- isOpen,
813
- selected,
814
- filteredOptions,
815
- isInputValuePersisted,
816
- isInputFilterPersisted,
817
- isCreatable
818
- } = this.state;
819
- const titleId = 'single-filtering-select-id';
1100
+ const { isOpen, selected, filteredOptions, customBadgeText } = this.state;
1101
+ const titleId = 'checkbox-filtering-custom-badging-select-id';
820
1102
  return (
821
1103
  <div>
822
1104
  <span id={titleId} hidden>
823
- Single select with filter
1105
+ Checkbox Title
824
1106
  </span>
825
1107
  <Select
826
- variant={SelectVariant.single}
827
- toggleRef={this.toggleRef}
1108
+ variant={SelectVariant.checkbox}
828
1109
  onToggle={this.onToggle}
829
1110
  onSelect={this.onSelect}
830
1111
  selections={selected}
@@ -832,51 +1113,28 @@ class FilteringSingleSelectInput extends React.Component {
832
1113
  placeholderText="Filter by status"
833
1114
  aria-labelledby={titleId}
834
1115
  onFilter={this.onFilter}
1116
+ onClear={this.clearSelection}
835
1117
  isGrouped
836
1118
  hasInlineFilter
837
- isCreatable={isCreatable}
838
- isInputValuePersisted={isInputValuePersisted}
839
- isInputFilterPersisted={isInputFilterPersisted}
1119
+ customBadgeText={customBadgeText}
840
1120
  >
841
1121
  {this.options}
842
1122
  </Select>
843
- <Checkbox
844
- label="isInputValuePersisted"
845
- isChecked={isInputValuePersisted}
846
- onChange={(_event, checked) => this.toggleInputValuePersisted(checked)}
847
- aria-label="toggle input value persisted"
848
- id="toggle-inline-filter-input-value-persisted"
849
- name="toggle-inline-filter-input-value-persisted"
850
- />
851
- <Checkbox
852
- label="isInputFilterPersisted"
853
- isChecked={isInputFilterPersisted}
854
- onChange={(_event, checked) => this.toggleInputFilterPersisted(checked)}
855
- aria-label="toggle input filter persisted"
856
- id="toggle-inline-filter-input-filter-persisted"
857
- name="toggle-inline-filter-input-filter-persisted"
858
- />
859
- <Checkbox
860
- label="isCreatable"
861
- isChecked={this.state.isCreatable}
862
- onChange={(_event, checked) => this.toggleCreatable(checked)}
863
- aria-label="toggle creatable checkbox"
864
- id="toggle-inline-filter-creatable-typeahead"
865
- name="toggle-inline-filter-creatable-typeahead"
866
- />
867
1123
  </div>
868
1124
  );
869
1125
  }
870
1126
  }
871
1127
  ```
872
1128
 
873
- ### Grouped checkbox input with filtering
1129
+ ### Checkbox select without selected count
1130
+
1131
+ To remove the default item count badge, use the `isCheckboxSelectionBadgeHidden` property.
874
1132
 
875
1133
  ```js
876
1134
  import React from 'react';
877
- import { Select, SelectOption, SelectGroup, SelectVariant } from '@patternfly/react-core';
1135
+ import { Select, SelectOption, SelectVariant } from '@patternfly/react-core';
878
1136
 
879
- class FilteringCheckboxSelectInput extends React.Component {
1137
+ class CheckboxSelectInputNoBadge extends React.Component {
880
1138
  constructor(props) {
881
1139
  super(props);
882
1140
 
@@ -885,21 +1143,6 @@ class FilteringCheckboxSelectInput extends React.Component {
885
1143
  selected: []
886
1144
  };
887
1145
 
888
- this.options = [
889
- <SelectGroup label="Status" key="group1">
890
- <SelectOption key={0} value="Running" />
891
- <SelectOption key={1} value="Stopped" />
892
- <SelectOption key={2} value="Down" />
893
- <SelectOption key={3} value="Degraded" />
894
- <SelectOption key={4} value="Needs maintenance" />
895
- </SelectGroup>,
896
- <SelectGroup label="Vendor names" key="group2">
897
- <SelectOption key={5} value="Dell" />
898
- <SelectOption key={6} value="Samsung" isDisabled />
899
- <SelectOption key={7} value="Hewlett-Packard" />
900
- </SelectGroup>
901
- ];
902
-
903
1146
  this.onToggle = (_event, isOpen) => {
904
1147
  this.setState({
905
1148
  isOpen
@@ -921,34 +1164,23 @@ class FilteringCheckboxSelectInput extends React.Component {
921
1164
  }
922
1165
  };
923
1166
 
924
- this.onFilter = (_, textInput) => {
925
- if (textInput === '') {
926
- return this.options;
927
- } else {
928
- let filteredGroups = this.options
929
- .map(group => {
930
- let filteredGroup = React.cloneElement(group, {
931
- children: group.props.children.filter(item => {
932
- return item.props.value.toLowerCase().includes(textInput.toLowerCase());
933
- })
934
- });
935
- if (filteredGroup.props.children.length > 0) return filteredGroup;
936
- })
937
- .filter(newGroup => newGroup);
938
- return filteredGroups;
939
- }
940
- };
941
-
942
1167
  this.clearSelection = () => {
943
1168
  this.setState({
944
1169
  selected: []
945
1170
  });
946
1171
  };
1172
+
1173
+ this.options = [
1174
+ <SelectOption key={0} value="Debug" />,
1175
+ <SelectOption key={1} value="Info" />,
1176
+ <SelectOption key={2} value="Warn" />,
1177
+ <SelectOption key={3} value="Error" />
1178
+ ];
947
1179
  }
948
1180
 
949
1181
  render() {
950
- const { isOpen, selected, filteredOptions } = this.state;
951
- const titleId = 'grouped-checkbox-filtering-select-id';
1182
+ const { isOpen, selected } = this.state;
1183
+ const titleId = 'checkbox-no-badge-select-id';
952
1184
  return (
953
1185
  <div>
954
1186
  <span id={titleId} hidden>
@@ -956,16 +1188,14 @@ class FilteringCheckboxSelectInput extends React.Component {
956
1188
  </span>
957
1189
  <Select
958
1190
  variant={SelectVariant.checkbox}
1191
+ aria-label="Select Input"
959
1192
  onToggle={this.onToggle}
960
1193
  onSelect={this.onSelect}
961
1194
  selections={selected}
1195
+ isCheckboxSelectionBadgeHidden
962
1196
  isOpen={isOpen}
963
1197
  placeholderText="Filter by status"
964
1198
  aria-labelledby={titleId}
965
- onFilter={this.onFilter}
966
- onClear={this.clearSelection}
967
- isGrouped
968
- hasInlineFilter
969
1199
  >
970
1200
  {this.options}
971
1201
  </Select>
@@ -975,13 +1205,15 @@ class FilteringCheckboxSelectInput extends React.Component {
975
1205
  }
976
1206
  ```
977
1207
 
978
- ### Grouped checkbox input with filtering and placeholder text
1208
+ ### Checkbox select with item counts
1209
+
1210
+ To show users the number of items that a `<SelectOption>` would match, use the `itemCount` property. The numerical value you pass into `itemCount` is displayed to the right of each menu item.
979
1211
 
980
1212
  ```js
981
1213
  import React from 'react';
982
- import { Select, SelectOption, SelectGroup, SelectVariant } from '@patternfly/react-core';
1214
+ import { Select, SelectOption, SelectVariant, Divider } from '@patternfly/react-core';
983
1215
 
984
- class FilteringCheckboxSelectInputWithPlaceholder extends React.Component {
1216
+ class CheckboxSelectWithCounts extends React.Component {
985
1217
  constructor(props) {
986
1218
  super(props);
987
1219
 
@@ -990,21 +1222,6 @@ class FilteringCheckboxSelectInputWithPlaceholder extends React.Component {
990
1222
  selected: []
991
1223
  };
992
1224
 
993
- this.options = [
994
- <SelectGroup label="Status" key="group1">
995
- <SelectOption key={0} value="Running" />
996
- <SelectOption key={1} value="Stopped" />
997
- <SelectOption key={2} value="Down" />
998
- <SelectOption key={3} value="Degraded" />
999
- <SelectOption key={4} value="Needs maintenance" />
1000
- </SelectGroup>,
1001
- <SelectGroup label="Vendor names" key="group2">
1002
- <SelectOption key={5} value="Dell" />
1003
- <SelectOption key={6} value="Samsung" isDisabled />
1004
- <SelectOption key={7} value="Hewlett-Packard" />
1005
- </SelectGroup>
1006
- ];
1007
-
1008
1225
  this.onToggle = (_event, isOpen) => {
1009
1226
  this.setState({
1010
1227
  isOpen
@@ -1026,52 +1243,38 @@ class FilteringCheckboxSelectInputWithPlaceholder extends React.Component {
1026
1243
  }
1027
1244
  };
1028
1245
 
1029
- this.onFilter = (_, textInput) => {
1030
- if (textInput === '') {
1031
- return this.options;
1032
- } else {
1033
- let filteredGroups = this.options
1034
- .map(group => {
1035
- let filteredGroup = React.cloneElement(group, {
1036
- children: group.props.children.filter(item => {
1037
- return item.props.value.toLowerCase().includes(textInput.toLowerCase());
1038
- })
1039
- });
1040
- if (filteredGroup.props.children.length > 0) return filteredGroup;
1041
- })
1042
- .filter(newGroup => newGroup);
1043
- return filteredGroups;
1044
- }
1045
- };
1046
-
1047
1246
  this.clearSelection = () => {
1048
1247
  this.setState({
1049
1248
  selected: []
1050
1249
  });
1051
1250
  };
1251
+
1252
+ this.options = [
1253
+ <SelectOption key={0} value="Active" description="This is a description" itemCount={3} />,
1254
+ <SelectOption key={1} value="Cancelled" itemCount={1} />,
1255
+ <SelectOption key={2} value="Paused" itemCount={15} />,
1256
+ <SelectOption key={3} value="Warning" itemCount={2} />,
1257
+ <SelectOption key={4} value="Restarted" itemCount={8} />
1258
+ ];
1052
1259
  }
1053
1260
 
1054
1261
  render() {
1055
- const { isOpen, selected, filteredOptions } = this.state;
1056
- const titleId = 'checkbox-filtering-with-placeholder-select-id';
1262
+ const { isOpen, selected } = this.state;
1263
+ const titleId = 'checkbox-select-with-counts-id';
1057
1264
  return (
1058
1265
  <div>
1059
1266
  <span id={titleId} hidden>
1060
- Checkbox Title
1267
+ Checkbox With Counts Title
1061
1268
  </span>
1062
1269
  <Select
1063
1270
  variant={SelectVariant.checkbox}
1271
+ aria-label="Select Input"
1064
1272
  onToggle={this.onToggle}
1065
1273
  onSelect={this.onSelect}
1066
1274
  selections={selected}
1067
1275
  isOpen={isOpen}
1068
1276
  placeholderText="Filter by status"
1069
1277
  aria-labelledby={titleId}
1070
- onFilter={this.onFilter}
1071
- onClear={this.clearSelection}
1072
- isGrouped
1073
- hasInlineFilter
1074
- inlineFilterPlaceholderText="Filter by status"
1075
1278
  >
1076
1279
  {this.options}
1077
1280
  </Select>
@@ -1081,121 +1284,83 @@ class FilteringCheckboxSelectInputWithPlaceholder extends React.Component {
1081
1284
  }
1082
1285
  ```
1083
1286
 
1084
- ### Grouped checkbox input with filtering and custom badging
1287
+ ### Checkbox select with a footer
1288
+
1289
+ You can combine a footer with checkbox input to allow users to apply an action to multiple items.
1085
1290
 
1086
1291
  ```js
1087
1292
  import React from 'react';
1088
- import { Select, SelectOption, SelectGroup, SelectVariant } from '@patternfly/react-core';
1293
+ import { Select, SelectOption, SelectVariant, Button } from '@patternfly/react-core';
1089
1294
 
1090
- class FilteringCheckboxSelectInputWithBadging extends React.Component {
1295
+ class SelectWithFooterCheckbox extends React.Component {
1091
1296
  constructor(props) {
1092
1297
  super(props);
1093
-
1094
1298
  this.state = {
1095
1299
  isOpen: false,
1096
1300
  selected: [],
1097
- customBadgeText: 0
1301
+ numOptions: 3,
1302
+ isLoading: false
1098
1303
  };
1099
1304
 
1305
+ this.toggleRef = React.createRef();
1306
+
1100
1307
  this.options = [
1101
- <SelectGroup label="Status" key="group1">
1102
- <SelectOption key={0} value="Running" />
1103
- <SelectOption key={1} value="Stopped" />
1104
- <SelectOption key={2} value="Down" />
1105
- <SelectOption key={3} value="Degraded" />
1106
- <SelectOption key={4} value="Needs maintenance" />
1107
- </SelectGroup>,
1108
- <SelectGroup label="Vendor names" key="group2">
1109
- <SelectOption key={5} value="Dell" />
1110
- <SelectOption key={6} value="Samsung" isDisabled />
1111
- <SelectOption key={7} value="Hewlett-Packard" />
1112
- </SelectGroup>
1308
+ <SelectOption key={0} value="Active" description="This is a description" />,
1309
+ <SelectOption key={1} value="Cancelled" />,
1310
+ <SelectOption key={2} value="Paused" />,
1311
+ <SelectOption key={4} value="Warning" />,
1312
+ <SelectOption key={5} value="Restarted" />
1113
1313
  ];
1114
1314
 
1115
1315
  this.onToggle = (_event, isOpen) => {
1116
- this.setState({
1117
- isOpen
1118
- });
1316
+ this.setState({ isOpen });
1119
1317
  };
1120
1318
 
1121
1319
  this.onSelect = (event, selection) => {
1122
- const { selected } = this.state;
1123
- if (selected.includes(selection)) {
1124
- this.setState(
1125
- prevState => ({
1126
- selected: prevState.selected.filter(item => item !== selection),
1127
- customBadgeText: this.setBadgeText(prevState.selected.length - 1)
1128
- }),
1129
- () => console.log('selections: ', this.state.selected)
1130
- );
1131
- } else {
1132
- this.setState(
1133
- prevState => ({
1134
- selected: [...prevState.selected, selection],
1135
- customBadgeText: this.setBadgeText(prevState.selected.length + 1)
1136
- }),
1137
- () => console.log('selections: ', this.state.selected)
1138
- );
1139
- }
1320
+ this.setState({ selected: selection, isOpen: false }), console.log('selected: ', selection);
1321
+ this.toggleRef.current.focus();
1140
1322
  };
1141
1323
 
1142
1324
  this.onFilter = (_, textInput) => {
1143
1325
  if (textInput === '') {
1144
1326
  return this.options;
1145
1327
  } else {
1146
- let filteredGroups = this.options
1147
- .map(group => {
1148
- let filteredGroup = React.cloneElement(group, {
1149
- children: group.props.children.filter(item => {
1150
- return item.props.value.toLowerCase().includes(textInput.toLowerCase());
1151
- })
1152
- });
1153
- if (filteredGroup.props.children.length > 0) return filteredGroup;
1154
- })
1155
- .filter(newGroup => newGroup);
1156
- return filteredGroups;
1328
+ this.setState(
1329
+ prevState => ({ selected: [...prevState.selected, selection] }),
1330
+ () => console.log('selections: ', this.state.selected)
1331
+ );
1157
1332
  }
1158
1333
  };
1159
1334
 
1160
1335
  this.clearSelection = () => {
1161
1336
  this.setState({
1162
- selected: [],
1163
- customBadgeText: this.setBadgeText(0)
1337
+ selected: []
1164
1338
  });
1165
1339
  };
1166
-
1167
- this.setBadgeText = selected => {
1168
- if (selected === 7) {
1169
- return 'All';
1170
- }
1171
- if (selected === 0) {
1172
- return 0;
1173
- }
1174
- return null;
1175
- };
1176
1340
  }
1177
1341
 
1178
1342
  render() {
1179
- const { isOpen, selected, filteredOptions, customBadgeText } = this.state;
1180
- const titleId = 'checkbox-filtering-custom-badging-select-id';
1343
+ const { isOpen, selected, isDisabled, direction, isToggleIcon } = this.state;
1344
+ const titleId = 'title-id-footer-checkbox';
1181
1345
  return (
1182
1346
  <div>
1183
1347
  <span id={titleId} hidden>
1184
- Checkbox Title
1348
+ Title
1185
1349
  </span>
1186
1350
  <Select
1187
- variant={SelectVariant.checkbox}
1351
+ variant={SelectVariant.single}
1352
+ toggleRef={this.toggleRef}
1188
1353
  onToggle={this.onToggle}
1189
1354
  onSelect={this.onSelect}
1190
1355
  selections={selected}
1191
1356
  isOpen={isOpen}
1192
1357
  placeholderText="Filter by status"
1193
1358
  aria-labelledby={titleId}
1194
- onFilter={this.onFilter}
1195
- onClear={this.clearSelection}
1196
- isGrouped
1197
- hasInlineFilter
1198
- customBadgeText={customBadgeText}
1359
+ footer={
1360
+ <Button variant="link" isInline>
1361
+ Action
1362
+ </Button>
1363
+ }
1199
1364
  >
1200
1365
  {this.options}
1201
1366
  </Select>
@@ -1205,491 +1370,441 @@ class FilteringCheckboxSelectInputWithBadging extends React.Component {
1205
1370
  }
1206
1371
  ```
1207
1372
 
1208
- ### Typeahead
1373
+ ### Checkbox select with view more
1374
+
1375
+ When a "view more" link is used alongside checkbox input, selections that users make prior to clicking "view more" are persisted after the click.
1209
1376
 
1210
1377
  ```js
1211
1378
  import React from 'react';
1212
- import { Checkbox, Select, SelectOption, SelectVariant } from '@patternfly/react-core';
1379
+ import { Select, SelectOption, SelectVariant } from '@patternfly/react-core';
1213
1380
 
1214
- class TypeaheadSelectInput extends React.Component {
1381
+ class SelectViewMoreCheckbox extends React.Component {
1215
1382
  constructor(props) {
1216
1383
  super(props);
1217
- this.defaultOptions = [
1218
- { value: 'Alabama' },
1219
- { value: 'Florida', description: 'This is a description' },
1220
- { value: 'New Jersey' },
1221
- { value: 'New Mexico' },
1222
- { value: 'New York' },
1223
- { value: 'North Carolina' }
1224
- ];
1225
1384
 
1226
1385
  this.state = {
1227
- options: this.defaultOptions,
1228
1386
  isOpen: false,
1229
- selected: null,
1230
- isDisabled: false,
1231
- isCreatable: false,
1232
- isCreateOptionOnTop: false,
1233
- isInputValuePersisted: false,
1234
- isInputFilterPersisted: false,
1235
- hasOnCreateOption: false,
1236
- resetOnSelect: true
1387
+ selected: [],
1388
+ numOptions: 3,
1389
+ isLoading: false
1237
1390
  };
1238
1391
 
1392
+ this.options = [
1393
+ <SelectOption key={0} value="Active" description="This is a description" />,
1394
+ <SelectOption key={1} value="Cancelled" />,
1395
+ <SelectOption key={2} value="Paused" />,
1396
+ <SelectOption key={4} value="Warning" />,
1397
+ <SelectOption key={5} value="Restarted" />,
1398
+ <SelectOption key={6} value="Down" />,
1399
+ <SelectOption key={7} value="Disabled" />,
1400
+ <SelectOption key={8} value="Needs maintenance " />,
1401
+ <SelectOption key={9} value="Degraded " />
1402
+ ];
1403
+
1239
1404
  this.onToggle = (_event, isOpen) => {
1240
1405
  this.setState({
1241
1406
  isOpen
1242
1407
  });
1243
1408
  };
1244
1409
 
1245
- this.onSelect = (event, selection, isPlaceholder) => {
1246
- if (isPlaceholder) this.clearSelection();
1247
- else {
1248
- this.setState({
1249
- selected: selection,
1250
- isOpen: this.state.resetOnSelect ? false : this.state.isOpen
1251
- });
1252
- console.log('selected:', selection);
1410
+ this.onSelect = (event, selection) => {
1411
+ const { selected } = this.state;
1412
+ if (selected.includes(selection)) {
1413
+ this.setState(
1414
+ prevState => ({ selected: prevState.selected.filter(item => item !== selection) }),
1415
+ () => console.log('selections: ', this.state.selected)
1416
+ );
1417
+ } else {
1418
+ this.setState(
1419
+ prevState => ({ selected: [...prevState.selected, selection] }),
1420
+ () => console.log('selections: ', this.state.selected)
1421
+ );
1253
1422
  }
1254
1423
  };
1255
1424
 
1256
- this.onCreateOption = newValue => {
1425
+ this.clearSelection = () => {
1257
1426
  this.setState({
1258
- options: [...this.state.options, { value: newValue }]
1427
+ selected: []
1259
1428
  });
1260
1429
  };
1261
1430
 
1262
- this.clearSelection = () => {
1263
- this.setState({
1264
- selected: null,
1265
- isOpen: false,
1266
- options: this.defaultOptions
1267
- });
1268
- };
1269
-
1270
- this.toggleDisabled = checked => {
1271
- this.setState({
1272
- isDisabled: checked
1273
- });
1274
- };
1275
-
1276
- this.toggleCreatable = checked => {
1277
- this.setState({
1278
- isCreatable: checked
1279
- });
1280
- };
1281
-
1282
- this.toggleCreateOptionOnTop = checked => {
1283
- this.setState({
1284
- isCreateOptionOnTop: checked
1285
- });
1286
- };
1287
-
1288
- this.toggleCreateNew = checked => {
1289
- this.setState({
1290
- hasOnCreateOption: checked
1291
- });
1292
- };
1293
-
1294
- this.toggleInputValuePersisted = checked => {
1295
- this.setState({
1296
- isInputValuePersisted: checked
1297
- });
1298
- };
1299
-
1300
- this.toggleInputFilterPersisted = checked => {
1301
- this.setState({
1302
- isInputFilterPersisted: checked
1303
- });
1431
+ this.simulateNetworkCall = callback => {
1432
+ setTimeout(callback, 2000);
1304
1433
  };
1305
1434
 
1306
- this.toggleResetOnSelect = checked => {
1307
- this.setState({
1308
- resetOnSelect: checked
1435
+ this.onViewMoreClick = () => {
1436
+ // Set select loadingVariant to spinner then simulate network call before loading more options
1437
+ this.setState({ isLoading: true });
1438
+ this.simulateNetworkCall(() => {
1439
+ const newLength =
1440
+ this.state.numOptions + 3 <= this.options.length ? this.state.numOptions + 3 : this.options.length;
1441
+ this.setState({ numOptions: newLength, isLoading: false });
1309
1442
  });
1310
1443
  };
1311
1444
  }
1312
1445
 
1313
1446
  render() {
1314
- const {
1315
- isOpen,
1316
- selected,
1317
- isDisabled,
1318
- isCreatable,
1319
- isCreateOptionOnTop,
1320
- hasOnCreateOption,
1321
- isInputValuePersisted,
1322
- isInputFilterPersisted,
1323
- resetOnSelect,
1324
- options
1325
- } = this.state;
1326
- const titleId = 'typeahead-select-id-1';
1447
+ const { isOpen, selected, numOptions, isLoading } = this.state;
1448
+ const titleId = 'view-more-checkbox-select-id';
1327
1449
  return (
1328
1450
  <div>
1329
1451
  <span id={titleId} hidden>
1330
- Select a state
1452
+ Checkbox View more check
1331
1453
  </span>
1332
1454
  <Select
1333
- variant={SelectVariant.typeahead}
1334
- typeAheadAriaLabel="Select a state"
1455
+ variant={SelectVariant.checkbox}
1456
+ aria-label="Select input"
1335
1457
  onToggle={this.onToggle}
1336
1458
  onSelect={this.onSelect}
1337
- onClear={this.clearSelection}
1338
1459
  selections={selected}
1339
1460
  isOpen={isOpen}
1461
+ placeholderText="Filter by status"
1340
1462
  aria-labelledby={titleId}
1341
- isInputValuePersisted={isInputValuePersisted}
1342
- isInputFilterPersisted={isInputFilterPersisted}
1343
- placeholderText="Select a state"
1344
- isDisabled={isDisabled}
1345
- isCreatable={isCreatable}
1346
- isCreateOptionOnTop={isCreateOptionOnTop}
1347
- onCreateOption={(hasOnCreateOption && this.onCreateOption) || undefined}
1348
- shouldResetOnSelect={resetOnSelect}
1463
+ {...(!isLoading &&
1464
+ numOptions < this.options.length && {
1465
+ loadingVariant: { text: 'View more', onClick: this.onViewMoreClick }
1466
+ })}
1467
+ {...(isLoading && { loadingVariant: 'spinner' })}
1349
1468
  >
1350
- {options.map((option, index) => (
1351
- <SelectOption
1352
- isDisabled={option.disabled}
1353
- key={index}
1354
- value={option.value}
1355
- {...(option.description && { description: option.description })}
1356
- />
1357
- ))}
1469
+ {this.options.slice(0, numOptions)}
1358
1470
  </Select>
1359
- <Checkbox
1360
- label="isDisabled"
1361
- isChecked={this.state.isDisabled}
1362
- onChange={(_event, checked) => this.toggleDisabled(checked)}
1363
- aria-label="toggle disabled checkbox"
1364
- id="toggle-disabled-typeahead"
1365
- name="toggle-disabled-typeahead"
1366
- />
1367
- <Checkbox
1368
- label="isCreatable"
1369
- isChecked={this.state.isCreatable}
1370
- onChange={(_event, checked) => this.toggleCreatable(checked)}
1371
- aria-label="toggle creatable checkbox"
1372
- id="toggle-creatable-typeahead"
1373
- name="toggle-creatable-typeahead"
1374
- />
1375
- <Checkbox
1376
- label="isCreateOptionOnTop"
1377
- isChecked={this.state.isCreateOptionOnTop}
1378
- onChange={(_event, checked) => this.toggleCreateOptionOnTop(checked)}
1379
- aria-label="toggle createOptionOnTop checkbox"
1380
- id="toggle-create-option-on-top-typeahead"
1381
- name="toggle-create-option-on-top-typeahead"
1382
- />
1383
- <Checkbox
1384
- label="onCreateOption"
1385
- isChecked={this.state.hasOnCreateOption}
1386
- onChange={(_event, checked) => this.toggleCreateNew(checked)}
1387
- aria-label="toggle new checkbox"
1388
- id="toggle-new-typeahead"
1389
- name="toggle-new-typeahead"
1390
- />
1391
- <Checkbox
1392
- label="isInputValuePersisted"
1393
- isChecked={isInputValuePersisted}
1394
- onChange={(_event, checked) => this.toggleInputValuePersisted(checked)}
1395
- aria-label="toggle input value persisted"
1396
- id="toggle-input-value-persisted"
1397
- name="toggle-input-value-persisted"
1398
- />
1399
- <Checkbox
1400
- label="isInputFilterPersisted"
1401
- isChecked={isInputFilterPersisted}
1402
- onChange={(_event, checked) => this.toggleInputFilterPersisted(checked)}
1403
- aria-label="toggle input filter persisted"
1404
- id="toggle-input-filter-persisted"
1405
- name="toggle-input-filter-persisted"
1406
- />
1407
- <Checkbox
1408
- label="shouldResetOnSelect"
1409
- isChecked={this.state.resetOnSelect}
1410
- onChange={(_event, checked) => this.toggleResetOnSelect(checked)}
1411
- aria-label="toggle reset checkbox"
1412
- id="toggle-reset-typeahead"
1413
- name="toggle-reset-typeahead"
1414
- />
1415
1471
  </div>
1416
1472
  );
1417
1473
  }
1418
1474
  }
1419
1475
  ```
1420
1476
 
1421
- ### Grouped typeahead
1477
+ ### Filtering with placeholder text
1478
+
1479
+ To preload a filter search bar with placeholder text, use the `inlineFilterPlaceholderText` property. The following example preloads the search bar with "Filter by status".
1422
1480
 
1423
1481
  ```js
1424
1482
  import React from 'react';
1425
- import { Checkbox, Select, SelectGroup, SelectOption, SelectVariant, Divider } from '@patternfly/react-core';
1483
+ import { Select, SelectOption, SelectGroup, SelectVariant } from '@patternfly/react-core';
1426
1484
 
1427
- class GroupedTypeaheadSelectInput extends React.Component {
1485
+ class FilteringCheckboxSelectInputWithPlaceholder extends React.Component {
1428
1486
  constructor(props) {
1429
1487
  super(props);
1430
1488
 
1431
1489
  this.state = {
1432
- options: [
1433
- <SelectGroup label="Status" key="group1">
1434
- <SelectOption key={0} value="Running" />
1435
- <SelectOption key={1} value="Stopped" />
1436
- <SelectOption key={2} value="Down" />
1437
- <SelectOption key={3} value="Degraded" />
1438
- <SelectOption key={4} value="Needs maintenance" />
1439
- </SelectGroup>,
1440
- <Divider key="divider" />,
1441
- <SelectGroup label="Vendor names" key="group2">
1442
- <SelectOption key={5} value="Dell" />
1443
- <SelectOption key={6} value="Samsung" isDisabled />
1444
- <SelectOption key={7} value="Hewlett-Packard" />
1445
- </SelectGroup>
1446
- ],
1447
- newOptions: [],
1448
1490
  isOpen: false,
1449
- selected: null,
1450
- isCreatable: false,
1451
- hasOnCreateOption: false
1491
+ selected: []
1452
1492
  };
1453
1493
 
1494
+ this.options = [
1495
+ <SelectGroup label="Status" key="group1">
1496
+ <SelectOption key={0} value="Running" />
1497
+ <SelectOption key={1} value="Stopped" />
1498
+ <SelectOption key={2} value="Down" />
1499
+ <SelectOption key={3} value="Degraded" />
1500
+ <SelectOption key={4} value="Needs maintenance" />
1501
+ </SelectGroup>,
1502
+ <SelectGroup label="Vendor names" key="group2">
1503
+ <SelectOption key={5} value="Dell" />
1504
+ <SelectOption key={6} value="Samsung" isDisabled />
1505
+ <SelectOption key={7} value="Hewlett-Packard" />
1506
+ </SelectGroup>
1507
+ ];
1508
+
1454
1509
  this.onToggle = (_event, isOpen) => {
1455
1510
  this.setState({
1456
1511
  isOpen
1457
1512
  });
1458
1513
  };
1459
1514
 
1460
- this.onSelect = (event, selection, isPlaceholder) => {
1461
- if (isPlaceholder) this.clearSelection();
1462
- else {
1463
- this.setState({
1464
- selected: selection,
1465
- isOpen: false
1466
- });
1467
- console.log('selected:', selection);
1515
+ this.onSelect = (event, selection) => {
1516
+ const { selected } = this.state;
1517
+ if (selected.includes(selection)) {
1518
+ this.setState(
1519
+ prevState => ({ selected: prevState.selected.filter(item => item !== selection) }),
1520
+ () => console.log('selections: ', this.state.selected)
1521
+ );
1522
+ } else {
1523
+ this.setState(
1524
+ prevState => ({ selected: [...prevState.selected, selection] }),
1525
+ () => console.log('selections: ', this.state.selected)
1526
+ );
1468
1527
  }
1469
1528
  };
1470
1529
 
1471
- this.onCreateOption = newValue => {
1472
- this.setState({
1473
- newOptions: [...this.state.newOptions, <SelectOption key={newValue} value={newValue} />]
1474
- });
1530
+ this.onFilter = (_, textInput) => {
1531
+ if (textInput === '') {
1532
+ return this.options;
1533
+ } else {
1534
+ let filteredGroups = this.options
1535
+ .map(group => {
1536
+ let filteredGroup = React.cloneElement(group, {
1537
+ children: group.props.children.filter(item => {
1538
+ return item.props.value.toLowerCase().includes(textInput.toLowerCase());
1539
+ })
1540
+ });
1541
+ if (filteredGroup.props.children.length > 0) return filteredGroup;
1542
+ })
1543
+ .filter(newGroup => newGroup);
1544
+ return filteredGroups;
1545
+ }
1475
1546
  };
1476
1547
 
1477
1548
  this.clearSelection = () => {
1478
1549
  this.setState({
1479
- selected: null,
1480
- isOpen: false
1481
- });
1482
- };
1483
-
1484
- this.toggleCreatable = checked => {
1485
- this.setState({
1486
- isCreatable: checked
1487
- });
1488
- };
1489
-
1490
- this.toggleCreateNew = checked => {
1491
- this.setState({
1492
- hasOnCreateOption: checked
1550
+ selected: []
1493
1551
  });
1494
1552
  };
1495
1553
  }
1496
1554
 
1497
1555
  render() {
1498
- const { isOpen, selected, isDisabled, isCreatable, hasOnCreateOption, options, newOptions } = this.state;
1499
- const titleId = 'grouped-typeahead-select-id';
1500
- const allOptions =
1501
- newOptions.length > 0
1502
- ? options.concat(
1503
- <SelectGroup label="Created" key="create-group">
1504
- {newOptions}
1505
- </SelectGroup>
1506
- )
1507
- : options;
1556
+ const { isOpen, selected, filteredOptions } = this.state;
1557
+ const titleId = 'checkbox-filtering-with-placeholder-select-id';
1508
1558
  return (
1509
1559
  <div>
1510
1560
  <span id={titleId} hidden>
1511
- Select a state
1561
+ Checkbox Title
1512
1562
  </span>
1513
1563
  <Select
1514
- variant={SelectVariant.typeahead}
1515
- typeAheadAriaLabel="Select a state"
1564
+ variant={SelectVariant.checkbox}
1516
1565
  onToggle={this.onToggle}
1517
1566
  onSelect={this.onSelect}
1518
- onClear={this.clearSelection}
1519
1567
  selections={selected}
1520
1568
  isOpen={isOpen}
1569
+ placeholderText="Filter by status"
1521
1570
  aria-labelledby={titleId}
1522
- placeholderText="Select a state"
1571
+ onFilter={this.onFilter}
1572
+ onClear={this.clearSelection}
1523
1573
  isGrouped
1524
- isCreatable={isCreatable}
1525
- onCreateOption={(hasOnCreateOption && this.onCreateOption) || undefined}
1574
+ hasInlineFilter
1575
+ inlineFilterPlaceholderText="Filter by status"
1526
1576
  >
1527
- {allOptions}
1577
+ {this.options}
1528
1578
  </Select>
1529
- <Checkbox
1530
- label="isCreatable"
1531
- isChecked={this.state.isCreatable}
1532
- onChange={(_event, checked) => this.toggleCreatable(checked)}
1533
- aria-label="toggle creatable checkbox"
1534
- id="toggle-creatable-grouped-typeahead"
1535
- name="toggle-creatable-grouped-typeahead"
1536
- />
1537
- <Checkbox
1538
- label="onCreateOption"
1539
- isChecked={this.state.hasOnCreateOption}
1540
- onChange={(_event, checked) => this.toggleCreateNew(checked)}
1541
- aria-label="toggle new checkbox"
1542
- id="toggle-new-grouped-typeahead"
1543
- name="toggle-new-grouped-typeahead"
1544
- />
1545
1579
  </div>
1546
1580
  );
1547
1581
  }
1548
1582
  }
1549
1583
  ```
1550
1584
 
1551
- ### Custom filtering
1585
+ ### Inline filtering
1586
+
1587
+ To allow users to filter select lists using text input, use the `hasInlineFilter` property. Filtering behavior can be further customized with other properties, as shown in the example below. Select each checkbox to visualize the following behavior:
1588
+
1589
+ - To persist filter results on blur, use the `isInputValuePersisted` property.
1590
+ - To persist a filter that a user has searched, use the `isInputFilterPersisted` property.
1591
+ - To allow users to add new items to a select list, use the `isCreatable` property. When this property is applied and a user searches for an option that doesn't exist, they will be prompted to "create" the item.
1552
1592
 
1553
1593
  ```js
1554
1594
  import React from 'react';
1555
- import { Select, SelectOption, SelectVariant } from '@patternfly/react-core';
1595
+ import { Select, SelectOption, SelectGroup, SelectVariant, Checkbox } from '@patternfly/react-core';
1556
1596
 
1557
- class TypeaheadSelectInput extends React.Component {
1597
+ class FilteringSingleSelectInput extends React.Component {
1558
1598
  constructor(props) {
1559
1599
  super(props);
1560
- this.options = [
1561
- <SelectOption key={0} value="Alabama" />,
1562
- <SelectOption key={1} value="Florida" />,
1563
- <SelectOption key={2} value="New Jersey" />,
1564
- <SelectOption key={3} value="New Mexico" />,
1565
- <SelectOption key={4} value="New York" />,
1566
- <SelectOption key={5} value="North Carolina" />
1567
- ];
1600
+
1568
1601
  this.state = {
1569
1602
  isOpen: false,
1570
- selected: null
1603
+ selected: '',
1604
+ isCreatable: false,
1605
+ isInputValuePersisted: false,
1606
+ isInputFilterPersisted: false
1571
1607
  };
1572
1608
 
1609
+ this.options = [
1610
+ <SelectGroup label="Status" key="group1">
1611
+ <SelectOption key={0} value="Running" />
1612
+ <SelectOption key={1} value="Stopped" />
1613
+ <SelectOption key={2} value="Down" />
1614
+ <SelectOption key={3} value="Degraded" />
1615
+ <SelectOption key={4} value="Needs maintenance" />
1616
+ </SelectGroup>,
1617
+ <SelectGroup label="Vendor names" key="group2">
1618
+ <SelectOption key={5} value="Dell" />
1619
+ <SelectOption key={6} value="Samsung" isDisabled />
1620
+ <SelectOption key={7} value="Hewlett-Packard" />
1621
+ </SelectGroup>
1622
+ ];
1623
+
1573
1624
  this.onToggle = (_event, isOpen) => {
1574
1625
  this.setState({
1575
1626
  isOpen
1576
1627
  });
1577
1628
  };
1578
1629
 
1579
- this.onSelect = (event, selection, isPlaceholder) => {
1580
- if (isPlaceholder) this.clearSelection();
1581
- else {
1582
- this.setState({
1583
- selected: selection,
1584
- isOpen: false
1585
- });
1586
- console.log('selected:', selection);
1630
+ this.onSelect = (event, selection) => {
1631
+ this.setState({ selected: selection, isOpen: false }), console.log('selected: ', selection);
1632
+ };
1633
+
1634
+ this.onFilter = (_, textInput) => {
1635
+ if (textInput === '') {
1636
+ return this.options;
1637
+ } else {
1638
+ let filteredGroups = this.options
1639
+ .map(group => {
1640
+ let filteredGroup = React.cloneElement(group, {
1641
+ children: group.props.children.filter(item => {
1642
+ return item.props.value.toLowerCase().includes(textInput.toLowerCase());
1643
+ })
1644
+ });
1645
+ if (filteredGroup.props.children.length > 0) return filteredGroup;
1646
+ })
1647
+ .filter(Boolean);
1648
+ return filteredGroups;
1587
1649
  }
1588
1650
  };
1589
1651
 
1590
- this.clearSelection = () => {
1652
+ this.toggleCreatable = (_, checked) => {
1591
1653
  this.setState({
1592
- selected: null,
1593
- isOpen: false
1654
+ isCreatable: checked
1594
1655
  });
1595
1656
  };
1596
1657
 
1597
- this.customFilter = (_, value) => {
1598
- if (!value) {
1599
- return this.options;
1600
- }
1658
+ this.toggleInputValuePersisted = (_, checked) => {
1659
+ this.setState({
1660
+ isInputValuePersisted: checked
1661
+ });
1662
+ };
1601
1663
 
1602
- const input = new RegExp(value, 'i');
1603
- return this.options.filter(child => input.test(child.props.value));
1664
+ this.toggleInputFilterPersisted = (_, checked) => {
1665
+ this.setState({
1666
+ isInputFilterPersisted: checked
1667
+ });
1604
1668
  };
1605
1669
  }
1606
1670
 
1607
1671
  render() {
1608
- const { isOpen, selected } = this.state;
1609
- const titleId = 'typeahead-select-id-2';
1672
+ const {
1673
+ isOpen,
1674
+ selected,
1675
+ filteredOptions,
1676
+ isInputValuePersisted,
1677
+ isInputFilterPersisted,
1678
+ isCreatable
1679
+ } = this.state;
1680
+ const titleId = 'single-filtering-select-id';
1610
1681
  return (
1611
1682
  <div>
1612
1683
  <span id={titleId} hidden>
1613
- Select a state
1684
+ Single select with filter
1614
1685
  </span>
1615
1686
  <Select
1616
- variant={SelectVariant.typeahead}
1617
- typeAheadAriaLabel="Select a state"
1687
+ variant={SelectVariant.single}
1618
1688
  onToggle={this.onToggle}
1619
1689
  onSelect={this.onSelect}
1620
- onClear={this.clearSelection}
1621
- onFilter={this.customFilter}
1622
1690
  selections={selected}
1623
1691
  isOpen={isOpen}
1692
+ placeholderText="Filter by status"
1624
1693
  aria-labelledby={titleId}
1625
- placeholderText="Select a state"
1694
+ onFilter={this.onFilter}
1695
+ isGrouped
1696
+ hasInlineFilter
1697
+ isCreatable={isCreatable}
1698
+ isInputValuePersisted={isInputValuePersisted}
1699
+ isInputFilterPersisted={isInputFilterPersisted}
1626
1700
  >
1627
1701
  {this.options}
1628
1702
  </Select>
1703
+ <Checkbox
1704
+ label="isInputValuePersisted"
1705
+ isChecked={isInputValuePersisted}
1706
+ onChange={this.toggleInputValuePersisted}
1707
+ aria-label="toggle input value persisted"
1708
+ id="toggle-inline-filter-input-value-persisted"
1709
+ name="toggle-inline-filter-input-value-persisted"
1710
+ />
1711
+ <Checkbox
1712
+ label="isInputFilterPersisted"
1713
+ isChecked={isInputFilterPersisted}
1714
+ onChange={this.toggleInputFilterPersisted}
1715
+ aria-label="toggle input filter persisted"
1716
+ id="toggle-inline-filter-input-filter-persisted"
1717
+ name="toggle-inline-filter-input-filter-persisted"
1718
+ />
1719
+ <Checkbox
1720
+ label="isCreatable"
1721
+ isChecked={this.state.isCreatable}
1722
+ onChange={this.toggleCreatable}
1723
+ aria-label="toggle creatable checkbox"
1724
+ id="toggle-inline-filter-creatable-typeahead"
1725
+ name="toggle-inline-filter-creatable-typeahead"
1726
+ />
1629
1727
  </div>
1630
1728
  );
1631
1729
  }
1632
1730
  }
1633
1731
  ```
1634
1732
 
1635
- ### Multiple
1733
+ ### Typeahead
1734
+
1735
+ Typeahead is a select variant that replaces the typical button toggle for opening the select menu with a text input and button toggle combo. As a user types in the text input, the select menu will provide suggestions by filtering the select options.
1736
+
1737
+ To make a typeahead, pass `variant=typeahead` into the `<Select>` component. To specify a label for the input field, use the `typeAheadAriaLabel` property.
1738
+
1739
+ A few additional customization options are shown in the example below. Select each checkbox to visualize the following behavior:
1740
+
1741
+ - To place a created item at the top of a typeahead list use the `isCreateOptionOnTop` property.
1742
+ - To trigger a callback for newly created items, use the `onCreateOption` property.
1743
+ - To reset the typeahead value after a user makes a selection, use the `shouldResetOnSelect` property.
1636
1744
 
1637
1745
  ```js
1638
1746
  import React from 'react';
1639
1747
  import { Checkbox, Select, SelectOption, SelectVariant } from '@patternfly/react-core';
1640
1748
 
1641
- class MultiTypeaheadSelectInput extends React.Component {
1749
+ class TypeaheadSelectInput extends React.Component {
1642
1750
  constructor(props) {
1643
1751
  super(props);
1752
+ this.defaultOptions = [
1753
+ { value: 'Alabama' },
1754
+ { value: 'Florida', description: 'This is a description' },
1755
+ { value: 'New Jersey' },
1756
+ { value: 'New Mexico' },
1757
+ { value: 'New York' },
1758
+ { value: 'North Carolina' }
1759
+ ];
1644
1760
 
1645
1761
  this.state = {
1646
- options: [
1647
- { value: 'Alabama', disabled: false },
1648
- { value: 'Florida', disabled: false },
1649
- { value: 'New Jersey', disabled: false },
1650
- { value: 'New Mexico', disabled: false, description: 'This is a description' },
1651
- { value: 'New York', disabled: false },
1652
- { value: 'North Carolina', disabled: false }
1653
- ],
1762
+ options: this.defaultOptions,
1654
1763
  isOpen: false,
1655
- selected: [],
1764
+ selected: null,
1765
+ isDisabled: false,
1656
1766
  isCreatable: false,
1767
+ isCreateOptionOnTop: false,
1768
+ isInputValuePersisted: false,
1769
+ isInputFilterPersisted: false,
1657
1770
  hasOnCreateOption: false,
1658
- hasDisabledOption: false,
1659
1771
  resetOnSelect: true
1660
1772
  };
1661
1773
 
1662
- this.onCreateOption = newValue => {
1663
- this.setState({
1664
- options: [...this.state.options, { value: newValue }]
1665
- });
1666
- };
1667
-
1668
1774
  this.onToggle = (_event, isOpen) => {
1669
1775
  this.setState({
1670
1776
  isOpen
1671
1777
  });
1672
1778
  };
1673
1779
 
1674
- this.onSelect = (event, selection) => {
1675
- const { selected } = this.state;
1676
- if (selected.includes(selection)) {
1677
- this.setState(
1678
- prevState => ({ selected: prevState.selected.filter(item => item !== selection) }),
1679
- () => console.log('selections: ', this.state.selected)
1680
- );
1681
- } else {
1682
- this.setState(
1683
- prevState => ({ selected: [...prevState.selected, selection] }),
1684
- () => console.log('selections: ', this.state.selected)
1685
- );
1780
+ this.onSelect = (event, selection, isPlaceholder) => {
1781
+ if (isPlaceholder) this.clearSelection();
1782
+ else {
1783
+ this.setState({
1784
+ selected: selection,
1785
+ isOpen: this.state.resetOnSelect ? false : this.state.isOpen
1786
+ });
1787
+ console.log('selected:', selection);
1686
1788
  }
1687
1789
  };
1688
1790
 
1791
+ this.onCreateOption = newValue => {
1792
+ this.setState({
1793
+ options: [...this.state.options, { value: newValue }]
1794
+ });
1795
+ };
1796
+
1689
1797
  this.clearSelection = () => {
1690
1798
  this.setState({
1691
- selected: [],
1692
- isOpen: false
1799
+ selected: null,
1800
+ isOpen: false,
1801
+ options: this.defaultOptions
1802
+ });
1803
+ };
1804
+
1805
+ this.toggleDisabled = checked => {
1806
+ this.setState({
1807
+ isDisabled: checked
1693
1808
  });
1694
1809
  };
1695
1810
 
@@ -1699,39 +1814,58 @@ class MultiTypeaheadSelectInput extends React.Component {
1699
1814
  });
1700
1815
  };
1701
1816
 
1702
- this.toggleCreateNew = checked => {
1817
+ this.toggleCreateOptionOnTop = checked => {
1703
1818
  this.setState({
1704
- hasOnCreateOption: checked
1819
+ isCreateOptionOnTop: checked
1705
1820
  });
1706
1821
  };
1707
1822
 
1708
- this.toggleOptionDisabled = toggleIndex => () => {
1709
- this.setState(prevState => ({
1710
- hasDisabledOption: !prevState.hasDisabledOption,
1711
- options: prevState.options.map((option, index) =>
1712
- index === toggleIndex ? { ...option, disabled: !option.disabled } : option
1713
- )
1714
- }));
1823
+ this.toggleCreateNew = checked => {
1824
+ this.setState({
1825
+ hasOnCreateOption: checked
1826
+ });
1715
1827
  };
1716
1828
 
1717
- this.toggleResetOnSelect = checked => {
1829
+ this.toggleInputValuePersisted = checked => {
1718
1830
  this.setState({
1719
- resetOnSelect: checked
1831
+ isInputValuePersisted: checked
1832
+ });
1833
+ };
1834
+
1835
+ this.toggleInputFilterPersisted = checked => {
1836
+ this.setState({
1837
+ isInputFilterPersisted: checked
1838
+ });
1839
+ };
1840
+
1841
+ this.toggleResetOnSelect = checked => {
1842
+ this.setState({
1843
+ resetOnSelect: checked
1720
1844
  });
1721
1845
  };
1722
1846
  }
1723
1847
 
1724
1848
  render() {
1725
- const { isOpen, selected, isCreatable, hasOnCreateOption, resetOnSelect, options } = this.state;
1726
- const titleId = 'multi-typeahead-select-id-1';
1727
-
1849
+ const {
1850
+ isOpen,
1851
+ selected,
1852
+ isDisabled,
1853
+ isCreatable,
1854
+ isCreateOptionOnTop,
1855
+ hasOnCreateOption,
1856
+ isInputValuePersisted,
1857
+ isInputFilterPersisted,
1858
+ resetOnSelect,
1859
+ options
1860
+ } = this.state;
1861
+ const titleId = 'typeahead-select-id-1';
1728
1862
  return (
1729
1863
  <div>
1730
1864
  <span id={titleId} hidden>
1731
1865
  Select a state
1732
1866
  </span>
1733
1867
  <Select
1734
- variant={SelectVariant.typeaheadMulti}
1868
+ variant={SelectVariant.typeahead}
1735
1869
  typeAheadAriaLabel="Select a state"
1736
1870
  onToggle={this.onToggle}
1737
1871
  onSelect={this.onSelect}
@@ -1739,8 +1873,12 @@ class MultiTypeaheadSelectInput extends React.Component {
1739
1873
  selections={selected}
1740
1874
  isOpen={isOpen}
1741
1875
  aria-labelledby={titleId}
1876
+ isInputValuePersisted={isInputValuePersisted}
1877
+ isInputFilterPersisted={isInputFilterPersisted}
1742
1878
  placeholderText="Select a state"
1879
+ isDisabled={isDisabled}
1743
1880
  isCreatable={isCreatable}
1881
+ isCreateOptionOnTop={isCreateOptionOnTop}
1744
1882
  onCreateOption={(hasOnCreateOption && this.onCreateOption) || undefined}
1745
1883
  shouldResetOnSelect={resetOnSelect}
1746
1884
  >
@@ -1753,37 +1891,61 @@ class MultiTypeaheadSelectInput extends React.Component {
1753
1891
  />
1754
1892
  ))}
1755
1893
  </Select>
1894
+ <Checkbox
1895
+ label="isDisabled"
1896
+ isChecked={this.state.isDisabled}
1897
+ onChange={(_event, checked) => this.toggleDisabled(checked)}
1898
+ aria-label="toggle disabled checkbox"
1899
+ id="toggle-disabled-typeahead"
1900
+ name="toggle-disabled-typeahead"
1901
+ />
1756
1902
  <Checkbox
1757
1903
  label="isCreatable"
1758
1904
  isChecked={this.state.isCreatable}
1759
1905
  onChange={(_event, checked) => this.toggleCreatable(checked)}
1760
1906
  aria-label="toggle creatable checkbox"
1761
- id="toggle-creatable-typeahead-multi"
1762
- name="toggle-creatable-typeahead-multi"
1907
+ id="toggle-creatable-typeahead"
1908
+ name="toggle-creatable-typeahead"
1909
+ />
1910
+ <Checkbox
1911
+ label="isCreateOptionOnTop"
1912
+ isChecked={this.state.isCreateOptionOnTop}
1913
+ onChange={(_event, checked) => this.toggleCreateOptionOnTop(checked)}
1914
+ aria-label="toggle createOptionOnTop checkbox"
1915
+ id="toggle-create-option-on-top-typeahead"
1916
+ name="toggle-create-option-on-top-typeahead"
1763
1917
  />
1764
1918
  <Checkbox
1765
1919
  label="onCreateOption"
1766
1920
  isChecked={this.state.hasOnCreateOption}
1767
1921
  onChange={(_event, checked) => this.toggleCreateNew(checked)}
1768
1922
  aria-label="toggle new checkbox"
1769
- id="toggle-new-typeahead-multi"
1770
- name="toggle-new-typeahead-multi"
1923
+ id="toggle-new-typeahead"
1924
+ name="toggle-new-typeahead"
1771
1925
  />
1772
1926
  <Checkbox
1773
- label="isDisabled (1st option only)"
1774
- isChecked={this.state.hasDisabledOption}
1775
- onChange={this.toggleOptionDisabled(0)}
1776
- aria-label="toggle disable first option"
1777
- id="toggle-disable-first-option"
1778
- name="toggle-disable-first-option"
1927
+ label="isInputValuePersisted"
1928
+ isChecked={isInputValuePersisted}
1929
+ onChange={(_event, checked) => this.toggleInputValuePersisted(checked)}
1930
+ aria-label="toggle input value persisted"
1931
+ id="toggle-input-value-persisted"
1932
+ name="toggle-input-value-persisted"
1933
+ />
1934
+ <Checkbox
1935
+ label="isInputFilterPersisted"
1936
+ isChecked={isInputFilterPersisted}
1937
+ onChange={(_event, checked) => this.toggleInputFilterPersisted(checked)}
1938
+ aria-label="toggle input filter persisted"
1939
+ id="toggle-input-filter-persisted"
1940
+ name="toggle-input-filter-persisted"
1779
1941
  />
1780
1942
  <Checkbox
1781
1943
  label="shouldResetOnSelect"
1782
1944
  isChecked={this.state.resetOnSelect}
1783
1945
  onChange={(_event, checked) => this.toggleResetOnSelect(checked)}
1784
- aria-label="toggle multi reset checkbox"
1785
- id="toggle-reset-multi-typeahead"
1786
- name="toggle-reset-multi-typeahead"
1946
+ aria-label="toggle reset checkbox"
1947
+ id="toggle-reset-typeahead"
1948
+ name="toggle-reset-typeahead"
1787
1949
  />
1788
1950
  </div>
1789
1951
  );
@@ -1791,27 +1953,39 @@ class MultiTypeaheadSelectInput extends React.Component {
1791
1953
  }
1792
1954
  ```
1793
1955
 
1794
- ### Multiple with Custom Chip Group Props
1956
+ ### Grouped typeahead
1957
+
1958
+ Typeahead matches items with user input across groups.
1795
1959
 
1796
1960
  ```js
1797
1961
  import React from 'react';
1798
- import { Select, SelectOption, SelectVariant } from '@patternfly/react-core';
1962
+ import { Checkbox, Select, SelectGroup, SelectOption, SelectVariant, Divider } from '@patternfly/react-core';
1799
1963
 
1800
- class MultiTypeaheadSelectInputWithChipGroupProps extends React.Component {
1964
+ class GroupedTypeaheadSelectInput extends React.Component {
1801
1965
  constructor(props) {
1802
1966
  super(props);
1803
1967
 
1804
1968
  this.state = {
1805
1969
  options: [
1806
- { value: 'Alabama', disabled: false },
1807
- { value: 'Florida', disabled: false },
1808
- { value: 'New Jersey', disabled: false },
1809
- { value: 'New Mexico', disabled: false, description: 'This is a description' },
1810
- { value: 'New York', disabled: false },
1811
- { value: 'North Carolina', disabled: false }
1970
+ <SelectGroup label="Status" key="group1">
1971
+ <SelectOption key={0} value="Running" />
1972
+ <SelectOption key={1} value="Stopped" />
1973
+ <SelectOption key={2} value="Down" />
1974
+ <SelectOption key={3} value="Degraded" />
1975
+ <SelectOption key={4} value="Needs maintenance" />
1976
+ </SelectGroup>,
1977
+ <Divider key="divider" />,
1978
+ <SelectGroup label="Vendor names" key="group2">
1979
+ <SelectOption key={5} value="Dell" />
1980
+ <SelectOption key={6} value="Samsung" isDisabled />
1981
+ <SelectOption key={7} value="Hewlett-Packard" />
1982
+ </SelectGroup>
1812
1983
  ],
1984
+ newOptions: [],
1813
1985
  isOpen: false,
1814
- selected: []
1986
+ selected: null,
1987
+ isCreatable: false,
1988
+ hasOnCreateOption: false
1815
1989
  };
1816
1990
 
1817
1991
  this.onToggle = (_event, isOpen) => {
@@ -1820,41 +1994,61 @@ class MultiTypeaheadSelectInputWithChipGroupProps extends React.Component {
1820
1994
  });
1821
1995
  };
1822
1996
 
1823
- this.onSelect = (event, selection) => {
1824
- const { selected } = this.state;
1825
- if (selected.includes(selection)) {
1826
- this.setState(
1827
- prevState => ({ selected: prevState.selected.filter(item => item !== selection) }),
1828
- () => console.log('selections: ', this.state.selected)
1829
- );
1830
- } else {
1831
- this.setState(
1832
- prevState => ({ selected: [...prevState.selected, selection] }),
1833
- () => console.log('selections: ', this.state.selected)
1834
- );
1997
+ this.onSelect = (event, selection, isPlaceholder) => {
1998
+ if (isPlaceholder) this.clearSelection();
1999
+ else {
2000
+ this.setState({
2001
+ selected: selection,
2002
+ isOpen: false
2003
+ });
2004
+ console.log('selected:', selection);
1835
2005
  }
1836
2006
  };
1837
2007
 
2008
+ this.onCreateOption = newValue => {
2009
+ this.setState({
2010
+ newOptions: [...this.state.newOptions, <SelectOption key={newValue} value={newValue} />]
2011
+ });
2012
+ };
2013
+
1838
2014
  this.clearSelection = () => {
1839
2015
  this.setState({
1840
- selected: [],
2016
+ selected: null,
1841
2017
  isOpen: false
1842
2018
  });
1843
2019
  };
2020
+
2021
+ this.toggleCreatable = checked => {
2022
+ this.setState({
2023
+ isCreatable: checked
2024
+ });
2025
+ };
2026
+
2027
+ this.toggleCreateNew = checked => {
2028
+ this.setState({
2029
+ hasOnCreateOption: checked
2030
+ });
2031
+ };
1844
2032
  }
1845
2033
 
1846
2034
  render() {
1847
- const { isOpen, selected, isCreatable, hasOnCreateOption } = this.state;
1848
- const titleId = 'multi-typeahead-custom-chip-group-props-id-1';
1849
-
2035
+ const { isOpen, selected, isDisabled, isCreatable, hasOnCreateOption, options, newOptions } = this.state;
2036
+ const titleId = 'grouped-typeahead-select-id';
2037
+ const allOptions =
2038
+ newOptions.length > 0
2039
+ ? options.concat(
2040
+ <SelectGroup label="Created" key="create-group">
2041
+ {newOptions}
2042
+ </SelectGroup>
2043
+ )
2044
+ : options;
1850
2045
  return (
1851
2046
  <div>
1852
2047
  <span id={titleId} hidden>
1853
2048
  Select a state
1854
2049
  </span>
1855
2050
  <Select
1856
- chipGroupProps={{ numChips: 1, expandedText: 'Hide', collapsedText: 'Show ${remaining}' }}
1857
- variant={SelectVariant.typeaheadMulti}
2051
+ variant={SelectVariant.typeahead}
1858
2052
  typeAheadAriaLabel="Select a state"
1859
2053
  onToggle={this.onToggle}
1860
2054
  onSelect={this.onSelect}
@@ -1863,43 +2057,56 @@ class MultiTypeaheadSelectInputWithChipGroupProps extends React.Component {
1863
2057
  isOpen={isOpen}
1864
2058
  aria-labelledby={titleId}
1865
2059
  placeholderText="Select a state"
2060
+ isGrouped
2061
+ isCreatable={isCreatable}
2062
+ onCreateOption={(hasOnCreateOption && this.onCreateOption) || undefined}
1866
2063
  >
1867
- {this.state.options.map((option, index) => (
1868
- <SelectOption
1869
- isDisabled={option.disabled}
1870
- key={index}
1871
- value={option.value}
1872
- {...(option.description && { description: option.description })}
1873
- />
1874
- ))}
2064
+ {allOptions}
1875
2065
  </Select>
2066
+ <Checkbox
2067
+ label="isCreatable"
2068
+ isChecked={this.state.isCreatable}
2069
+ onChange={(_event, checked) => this.toggleCreatable(checked)}
2070
+ aria-label="toggle creatable checkbox"
2071
+ id="toggle-creatable-grouped-typeahead"
2072
+ name="toggle-creatable-grouped-typeahead"
2073
+ />
2074
+ <Checkbox
2075
+ label="onCreateOption"
2076
+ isChecked={this.state.hasOnCreateOption}
2077
+ onChange={(_event, checked) => this.toggleCreateNew(checked)}
2078
+ aria-label="toggle new checkbox"
2079
+ id="toggle-new-grouped-typeahead"
2080
+ name="toggle-new-grouped-typeahead"
2081
+ />
1876
2082
  </div>
1877
2083
  );
1878
2084
  }
1879
2085
  }
1880
2086
  ```
1881
2087
 
1882
- ### Multiple with Render Custom Chip Group
2088
+ ### Typeahead with custom filtering
2089
+
2090
+ You can add custom filtering to a select list to better fit needs that aren't covered by inline filtering. If you use custom filtering, use the `onFilter` property to trigger a callback with your custom filter implementation.
1883
2091
 
1884
2092
  ```js
1885
2093
  import React from 'react';
1886
- import { Select, SelectOption, SelectVariant, ChipGroup, Chip } from '@patternfly/react-core';
2094
+ import { Select, SelectOption, SelectVariant } from '@patternfly/react-core';
1887
2095
 
1888
- class MultiTypeaheadSelectInputWithChipGroupProps extends React.Component {
2096
+ class TypeaheadSelectInput extends React.Component {
1889
2097
  constructor(props) {
1890
2098
  super(props);
1891
-
2099
+ this.options = [
2100
+ <SelectOption key={0} value="Alabama" />,
2101
+ <SelectOption key={1} value="Florida" />,
2102
+ <SelectOption key={2} value="New Jersey" />,
2103
+ <SelectOption key={3} value="New Mexico" />,
2104
+ <SelectOption key={4} value="New York" />,
2105
+ <SelectOption key={5} value="North Carolina" />
2106
+ ];
1892
2107
  this.state = {
1893
- options: [
1894
- { value: 'Alabama', disabled: false },
1895
- { value: 'Florida', disabled: false },
1896
- { value: 'New Jersey', disabled: false },
1897
- { value: 'New Mexico', disabled: false, description: 'This is a description' },
1898
- { value: 'New York', disabled: false },
1899
- { value: 'North Carolina', disabled: false }
1900
- ],
1901
2108
  isOpen: false,
1902
- selected: []
2109
+ selected: null
1903
2110
  };
1904
2111
 
1905
2112
  this.onToggle = (_event, isOpen) => {
@@ -1908,75 +2115,55 @@ class MultiTypeaheadSelectInputWithChipGroupProps extends React.Component {
1908
2115
  });
1909
2116
  };
1910
2117
 
1911
- this.onSelect = (event, selection) => {
1912
- const { selected } = this.state;
1913
- if (selected.includes(selection)) {
1914
- this.setState(
1915
- prevState => ({ selected: prevState.selected.filter(item => item !== selection) }),
1916
- () => console.log('selections: ', this.state.selected)
1917
- );
1918
- } else {
1919
- this.setState(
1920
- prevState => ({ selected: [...prevState.selected, selection] }),
1921
- () => console.log('selections: ', this.state.selected)
1922
- );
2118
+ this.onSelect = (event, selection, isPlaceholder) => {
2119
+ if (isPlaceholder) this.clearSelection();
2120
+ else {
2121
+ this.setState({
2122
+ selected: selection,
2123
+ isOpen: false
2124
+ });
2125
+ console.log('selected:', selection);
1923
2126
  }
1924
2127
  };
1925
2128
 
1926
2129
  this.clearSelection = () => {
1927
2130
  this.setState({
1928
- selected: [],
2131
+ selected: null,
1929
2132
  isOpen: false
1930
2133
  });
1931
2134
  };
1932
- this.chipGroupComponent = () => {
1933
- const { selected } = this.state;
1934
- return (
1935
- <ChipGroup>
1936
- {(selected || []).map((currentChip, index) => (
1937
- <Chip
1938
- isReadOnly={index === 0 ? true : false}
1939
- key={currentChip}
1940
- onClick={event => this.onSelect(event, currentChip)}
1941
- >
1942
- {currentChip}
1943
- </Chip>
1944
- ))}
1945
- </ChipGroup>
1946
- );
2135
+
2136
+ this.customFilter = (_, value) => {
2137
+ if (!value) {
2138
+ return this.options;
2139
+ }
2140
+
2141
+ const input = new RegExp(value, 'i');
2142
+ return this.options.filter(child => input.test(child.props.value));
1947
2143
  };
1948
2144
  }
1949
2145
 
1950
2146
  render() {
1951
- const { isOpen, selected, isCreatable, hasOnCreateOption } = this.state;
1952
- const titleId = 'multi-typeahead-render-chip-group-props-id-1';
1953
-
2147
+ const { isOpen, selected } = this.state;
2148
+ const titleId = 'typeahead-select-id-2';
1954
2149
  return (
1955
2150
  <div>
1956
2151
  <span id={titleId} hidden>
1957
2152
  Select a state
1958
2153
  </span>
1959
2154
  <Select
1960
- chipGroupProps={{ numChips: 1, expandedText: 'Hide', collapsedText: 'Show ${remaining}' }}
1961
- variant={SelectVariant.typeaheadMulti}
2155
+ variant={SelectVariant.typeahead}
1962
2156
  typeAheadAriaLabel="Select a state"
1963
2157
  onToggle={this.onToggle}
1964
2158
  onSelect={this.onSelect}
1965
2159
  onClear={this.clearSelection}
2160
+ onFilter={this.customFilter}
1966
2161
  selections={selected}
1967
2162
  isOpen={isOpen}
1968
2163
  aria-labelledby={titleId}
1969
2164
  placeholderText="Select a state"
1970
- chipGroupComponent={this.chipGroupComponent()}
1971
2165
  >
1972
- {this.state.options.map((option, index) => (
1973
- <SelectOption
1974
- isDisabled={option.disabled}
1975
- key={index}
1976
- value={option.value}
1977
- {...(option.description && { description: option.description })}
1978
- />
1979
- ))}
2166
+ {this.options}
1980
2167
  </Select>
1981
2168
  </div>
1982
2169
  );
@@ -1984,44 +2171,41 @@ class MultiTypeaheadSelectInputWithChipGroupProps extends React.Component {
1984
2171
  }
1985
2172
  ```
1986
2173
 
1987
- ### Multiple with custom objects
2174
+ ### Multiple typeahead
2175
+
2176
+ To create a multiple typeahead select variant, pass `variant={SelectVariant.typeaheadMulti}` into the `<Select>` component. Multiple typeaheads let users select multiple items from a select list. Selected items appear as chips in the select toggle.
2177
+
2178
+ When many items are selected, you can hide overflowing items under a "more" button. The following example hides items after more than 3 are selected. To show hidden items, select the “more” button. Click "show less" to hide extra items again.
1988
2179
 
1989
2180
  ```js
1990
2181
  import React from 'react';
1991
- import { Select, SelectOption, SelectVariant, Divider } from '@patternfly/react-core';
2182
+ import { Checkbox, Select, SelectOption, SelectVariant } from '@patternfly/react-core';
1992
2183
 
1993
- class MultiTypeaheadSelectInputCustomObjects extends React.Component {
2184
+ class MultiTypeaheadSelectInput extends React.Component {
1994
2185
  constructor(props) {
1995
2186
  super(props);
1996
- this.createState = (name, abbreviation, capital, founded) => {
1997
- return {
1998
- name: name,
1999
- abbreviation: abbreviation,
2000
- capital: capital,
2001
- founded: founded,
2002
- toString: function() {
2003
- return `${this.name} (${this.abbreviation}) - Founded: ${this.founded}`;
2004
- },
2005
- compareTo: function(value) {
2006
- return this.toString()
2007
- .toLowerCase()
2008
- .includes(value.toString().toLowerCase());
2009
- }
2010
- };
2011
- };
2012
- this.options = [
2013
- <SelectOption key={0} value={this.createState('Alabama', 'AL', 'Montgomery', 1846)} />,
2014
- <Divider component="li" key={111} />,
2015
- <SelectOption key={1} value={this.createState('Florida', 'FL', 'Tailahassee', 1845)} />,
2016
- <SelectOption key={2} value={this.createState('New Jersey', 'NJ', 'Trenton', 1787)} />,
2017
- <SelectOption key={3} value={this.createState('New Mexico', 'NM', 'Santa Fe', 1912)} />,
2018
- <SelectOption key={4} value={this.createState('New York', 'NY', 'Albany', 1788)} />,
2019
- <SelectOption key={5} value={this.createState('North Carolina', 'NC', 'Raleigh', 1789)} />
2020
- ];
2021
2187
 
2022
2188
  this.state = {
2189
+ options: [
2190
+ { value: 'Alabama', disabled: false },
2191
+ { value: 'Florida', disabled: false },
2192
+ { value: 'New Jersey', disabled: false },
2193
+ { value: 'New Mexico', disabled: false, description: 'This is a description' },
2194
+ { value: 'New York', disabled: false },
2195
+ { value: 'North Carolina', disabled: false }
2196
+ ],
2023
2197
  isOpen: false,
2024
- selected: []
2198
+ selected: [],
2199
+ isCreatable: false,
2200
+ hasOnCreateOption: false,
2201
+ hasDisabledOption: false,
2202
+ resetOnSelect: true
2203
+ };
2204
+
2205
+ this.onCreateOption = newValue => {
2206
+ this.setState({
2207
+ options: [...this.state.options, { value: newValue }]
2208
+ });
2025
2209
  };
2026
2210
 
2027
2211
  this.onToggle = (_event, isOpen) => {
@@ -2051,11 +2235,38 @@ class MultiTypeaheadSelectInputCustomObjects extends React.Component {
2051
2235
  isOpen: false
2052
2236
  });
2053
2237
  };
2238
+
2239
+ this.toggleCreatable = checked => {
2240
+ this.setState({
2241
+ isCreatable: checked
2242
+ });
2243
+ };
2244
+
2245
+ this.toggleCreateNew = checked => {
2246
+ this.setState({
2247
+ hasOnCreateOption: checked
2248
+ });
2249
+ };
2250
+
2251
+ this.toggleOptionDisabled = toggleIndex => () => {
2252
+ this.setState(prevState => ({
2253
+ hasDisabledOption: !prevState.hasDisabledOption,
2254
+ options: prevState.options.map((option, index) =>
2255
+ index === toggleIndex ? { ...option, disabled: !option.disabled } : option
2256
+ )
2257
+ }));
2258
+ };
2259
+
2260
+ this.toggleResetOnSelect = checked => {
2261
+ this.setState({
2262
+ resetOnSelect: checked
2263
+ });
2264
+ };
2054
2265
  }
2055
2266
 
2056
2267
  render() {
2057
- const { isOpen, selected } = this.state;
2058
- const titleId = 'multi-typeahead-select-id-2';
2268
+ const { isOpen, selected, isCreatable, hasOnCreateOption, resetOnSelect, options } = this.state;
2269
+ const titleId = 'multi-typeahead-select-id-1';
2059
2270
 
2060
2271
  return (
2061
2272
  <div>
@@ -2068,41 +2279,83 @@ class MultiTypeaheadSelectInputCustomObjects extends React.Component {
2068
2279
  onToggle={this.onToggle}
2069
2280
  onSelect={this.onSelect}
2070
2281
  onClear={this.clearSelection}
2071
- onFilter={this.customFilter}
2072
2282
  selections={selected}
2073
2283
  isOpen={isOpen}
2074
2284
  aria-labelledby={titleId}
2075
2285
  placeholderText="Select a state"
2286
+ isCreatable={isCreatable}
2287
+ onCreateOption={(hasOnCreateOption && this.onCreateOption) || undefined}
2288
+ shouldResetOnSelect={resetOnSelect}
2076
2289
  >
2077
- {this.options}
2290
+ {options.map((option, index) => (
2291
+ <SelectOption
2292
+ isDisabled={option.disabled}
2293
+ key={index}
2294
+ value={option.value}
2295
+ {...(option.description && { description: option.description })}
2296
+ />
2297
+ ))}
2078
2298
  </Select>
2299
+ <Checkbox
2300
+ label="isCreatable"
2301
+ isChecked={this.state.isCreatable}
2302
+ onChange={(_event, checked) => this.toggleCreatable(checked)}
2303
+ aria-label="toggle creatable checkbox"
2304
+ id="toggle-creatable-typeahead-multi"
2305
+ name="toggle-creatable-typeahead-multi"
2306
+ />
2307
+ <Checkbox
2308
+ label="onCreateOption"
2309
+ isChecked={this.state.hasOnCreateOption}
2310
+ onChange={(_event, checked) => this.toggleCreateNew(checked)}
2311
+ aria-label="toggle new checkbox"
2312
+ id="toggle-new-typeahead-multi"
2313
+ name="toggle-new-typeahead-multi"
2314
+ />
2315
+ <Checkbox
2316
+ label="isDisabled (1st option only)"
2317
+ isChecked={this.state.hasDisabledOption}
2318
+ onChange={this.toggleOptionDisabled(0)}
2319
+ aria-label="toggle disable first option"
2320
+ id="toggle-disable-first-option"
2321
+ name="toggle-disable-first-option"
2322
+ />
2323
+ <Checkbox
2324
+ label="shouldResetOnSelect"
2325
+ isChecked={this.state.resetOnSelect}
2326
+ onChange={(_event, checked) => this.toggleResetOnSelect(checked)}
2327
+ aria-label="toggle multi reset checkbox"
2328
+ id="toggle-reset-multi-typeahead"
2329
+ name="toggle-reset-multi-typeahead"
2330
+ />
2079
2331
  </div>
2080
2332
  );
2081
2333
  }
2082
2334
  }
2083
2335
  ```
2084
2336
 
2085
- ### Plain multiple typeahead
2337
+ ### Multiple typeahead with custom chips
2338
+
2339
+ To customize the appearance of chips, use the `chipGroupProps` property. The `numChips` property allows you to control the number of items shown, while the `expandedText` and `collapsedText` properties allow you to control the labels of the expansion and collapse chips.
2086
2340
 
2087
2341
  ```js
2088
2342
  import React from 'react';
2089
2343
  import { Select, SelectOption, SelectVariant } from '@patternfly/react-core';
2090
2344
 
2091
- class PlainSelectInput extends React.Component {
2345
+ class MultiTypeaheadSelectInputWithChipGroupProps extends React.Component {
2092
2346
  constructor(props) {
2093
2347
  super(props);
2094
- this.options = [
2095
- { value: 'Alabama', disabled: false },
2096
- { value: 'Florida', disabled: false },
2097
- { value: 'New Jersey', disabled: false },
2098
- { value: 'New Mexico', disabled: false },
2099
- { value: 'New York', disabled: false },
2100
- { value: 'North Carolina', disabled: false }
2101
- ];
2102
2348
 
2103
2349
  this.state = {
2350
+ options: [
2351
+ { value: 'Alabama', disabled: false },
2352
+ { value: 'Florida', disabled: false },
2353
+ { value: 'New Jersey', disabled: false },
2354
+ { value: 'New Mexico', disabled: false, description: 'This is a description' },
2355
+ { value: 'New York', disabled: false },
2356
+ { value: 'North Carolina', disabled: false }
2357
+ ],
2104
2358
  isOpen: false,
2105
- isPlain: true,
2106
2359
  selected: []
2107
2360
  };
2108
2361
 
@@ -2136,8 +2389,8 @@ class PlainSelectInput extends React.Component {
2136
2389
  }
2137
2390
 
2138
2391
  render() {
2139
- const { isOpen, isPlain, selected } = this.state;
2140
- const titleId = 'plain-typeahead-select-id';
2392
+ const { isOpen, selected, isCreatable, hasOnCreateOption } = this.state;
2393
+ const titleId = 'multi-typeahead-custom-chip-group-props-id-1';
2141
2394
 
2142
2395
  return (
2143
2396
  <div>
@@ -2145,6 +2398,7 @@ class PlainSelectInput extends React.Component {
2145
2398
  Select a state
2146
2399
  </span>
2147
2400
  <Select
2401
+ chipGroupProps={{ numChips: 1, expandedText: 'Hide', collapsedText: 'Show ${remaining}' }}
2148
2402
  variant={SelectVariant.typeaheadMulti}
2149
2403
  typeAheadAriaLabel="Select a state"
2150
2404
  onToggle={this.onToggle}
@@ -2152,12 +2406,16 @@ class PlainSelectInput extends React.Component {
2152
2406
  onClear={this.clearSelection}
2153
2407
  selections={selected}
2154
2408
  isOpen={isOpen}
2155
- isPlain={isPlain}
2156
2409
  aria-labelledby={titleId}
2157
2410
  placeholderText="Select a state"
2158
2411
  >
2159
- {this.options.map((option, index) => (
2160
- <SelectOption isDisabled={option.disabled} key={index} value={option.value} />
2412
+ {this.state.options.map((option, index) => (
2413
+ <SelectOption
2414
+ isDisabled={option.disabled}
2415
+ key={index}
2416
+ value={option.value}
2417
+ {...(option.description && { description: option.description })}
2418
+ />
2161
2419
  ))}
2162
2420
  </Select>
2163
2421
  </div>
@@ -2166,20 +2424,29 @@ class PlainSelectInput extends React.Component {
2166
2424
  }
2167
2425
  ```
2168
2426
 
2169
- ### Panel as a menu
2427
+ ### Multiple typeahead with chip group
2428
+
2429
+ To customize chips even more, render a [`<ChipGroup>`](/components/chip-group) component and pass it into the `chipGroupComponent` property of the `<Select>` component.
2170
2430
 
2171
2431
  ```js
2172
2432
  import React from 'react';
2173
- import CubeIcon from '@patternfly/react-icons/dist/esm/icons/cube-icon';
2174
- import { Select, SelectOption, SelectDirection, Checkbox } from '@patternfly/react-core';
2433
+ import { Select, SelectOption, SelectVariant, ChipGroup, Chip } from '@patternfly/react-core';
2175
2434
 
2176
- class SingleSelectInput extends React.Component {
2435
+ class MultiTypeaheadSelectInputWithChipGroupProps extends React.Component {
2177
2436
  constructor(props) {
2178
2437
  super(props);
2438
+
2179
2439
  this.state = {
2440
+ options: [
2441
+ { value: 'Alabama', disabled: false },
2442
+ { value: 'Florida', disabled: false },
2443
+ { value: 'New Jersey', disabled: false },
2444
+ { value: 'New Mexico', disabled: false, description: 'This is a description' },
2445
+ { value: 'New York', disabled: false },
2446
+ { value: 'North Carolina', disabled: false }
2447
+ ],
2180
2448
  isOpen: false,
2181
- isDisabled: false,
2182
- direction: SelectDirection.down
2449
+ selected: []
2183
2450
  };
2184
2451
 
2185
2452
  this.onToggle = (_event, isOpen) => {
@@ -2188,199 +2455,206 @@ class SingleSelectInput extends React.Component {
2188
2455
  });
2189
2456
  };
2190
2457
 
2191
- this.toggleDisabled = checked => {
2192
- this.setState({
2193
- isDisabled: checked
2194
- });
2195
- };
2196
-
2197
- this.toggleDirection = () => {
2198
- if (this.state.direction === SelectDirection.up) {
2199
- this.setState({
2200
- direction: SelectDirection.down
2201
- });
2458
+ this.onSelect = (event, selection) => {
2459
+ const { selected } = this.state;
2460
+ if (selected.includes(selection)) {
2461
+ this.setState(
2462
+ prevState => ({ selected: prevState.selected.filter(item => item !== selection) }),
2463
+ () => console.log('selections: ', this.state.selected)
2464
+ );
2202
2465
  } else {
2203
- this.setState({
2204
- direction: SelectDirection.up
2205
- });
2466
+ this.setState(
2467
+ prevState => ({ selected: [...prevState.selected, selection] }),
2468
+ () => console.log('selections: ', this.state.selected)
2469
+ );
2206
2470
  }
2207
2471
  };
2208
- }
2209
2472
 
2210
- render() {
2211
- const { isOpen, selected, isDisabled, direction } = this.state;
2212
- const titleId = 'title-id-2';
2213
- return (
2214
- <div>
2215
- <span id={titleId} hidden>
2216
- Title
2217
- </span>
2218
- <Select
2219
- aria-label="Select Input"
2220
- onToggle={this.onToggle}
2221
- isOpen={isOpen}
2222
- aria-labelledby={titleId}
2223
- isDisabled={isDisabled}
2224
- direction={direction}
2225
- customContent="[Panel contents here]"
2226
- placeholderText="Filter by birth month"
2227
- />
2228
- <Checkbox
2229
- label="isDisabled"
2230
- isChecked={this.state.isDisabled}
2231
- onChange={(_event, checked) => this.toggleDisabled(checked)}
2232
- aria-label="disabled checkbox panel"
2233
- id="toggle-disabled-panel"
2234
- name="toggle-disabled-panel"
2235
- />
2236
- <Checkbox
2237
- label="Expands up"
2238
- isChecked={direction === SelectDirection.up}
2239
- onChange={this.toggleDirection}
2240
- aria-label="direction checkbox panel"
2241
- id="toggle-direction-panel"
2242
- name="toggle-direction-panel"
2243
- />
2473
+ this.clearSelection = () => {
2474
+ this.setState({
2475
+ selected: [],
2476
+ isOpen: false
2477
+ });
2478
+ };
2479
+ this.chipGroupComponent = () => {
2480
+ const { selected } = this.state;
2481
+ return (
2482
+ <ChipGroup>
2483
+ {(selected || []).map((currentChip, index) => (
2484
+ <Chip
2485
+ isReadOnly={index === 0 ? true : false}
2486
+ key={currentChip}
2487
+ onClick={event => this.onSelect(event, currentChip)}
2488
+ >
2489
+ {currentChip}
2490
+ </Chip>
2491
+ ))}
2492
+ </ChipGroup>
2493
+ );
2494
+ };
2495
+ }
2496
+
2497
+ render() {
2498
+ const { isOpen, selected, isCreatable, hasOnCreateOption } = this.state;
2499
+ const titleId = 'multi-typeahead-render-chip-group-props-id-1';
2500
+
2501
+ return (
2502
+ <div>
2503
+ <span id={titleId} hidden>
2504
+ Select a state
2505
+ </span>
2506
+ <Select
2507
+ chipGroupProps={{ numChips: 1, expandedText: 'Hide', collapsedText: 'Show ${remaining}' }}
2508
+ variant={SelectVariant.typeaheadMulti}
2509
+ typeAheadAriaLabel="Select a state"
2510
+ onToggle={this.onToggle}
2511
+ onSelect={this.onSelect}
2512
+ onClear={this.clearSelection}
2513
+ selections={selected}
2514
+ isOpen={isOpen}
2515
+ aria-labelledby={titleId}
2516
+ placeholderText="Select a state"
2517
+ chipGroupComponent={this.chipGroupComponent()}
2518
+ >
2519
+ {this.state.options.map((option, index) => (
2520
+ <SelectOption
2521
+ isDisabled={option.disabled}
2522
+ key={index}
2523
+ value={option.value}
2524
+ {...(option.description && { description: option.description })}
2525
+ />
2526
+ ))}
2527
+ </Select>
2244
2528
  </div>
2245
2529
  );
2246
2530
  }
2247
2531
  }
2248
2532
  ```
2249
2533
 
2250
- ### Appending document body vs parent
2251
-
2252
- Avoid passing in `document.body` when passing a value to the `menuAppendTo` prop on the Select component, as it can cause accessibility issues. These issues can include, but are not limited to, being unable to enter the contents of the Select options via assistive technologies (like keyboards or screen readers).
2534
+ ### Multiple typeahead with custom objects
2253
2535
 
2254
- Instead append to `"parent"` to achieve the same result without sacrificing accessibility.
2255
-
2256
- In this example, while, when the dropdown is opened, both Select variants handle focus management within their dropdown contents the same way, you'll notice a difference when you try pressing the Tab key after selecting an option.
2257
-
2258
- For the `document.body` variant, the focus will be placed at the end of the page, since that is where the dropdown content is appended to in the DOM (rather than focus being placed on the second Select variant as one might expect). For the `"parent"` variant, however, the focus will be placed on the next tab-able element (the "Toggle JS code" button for the code editor in this case).
2536
+ A `<SelectOption>` can have an object passed into the `value` property in order to store additional data beyond just a string value. The object passed in must have a `toString` function that returns a string to display in the `SelectMenu`.
2259
2537
 
2260
2538
  ```js
2261
2539
  import React from 'react';
2262
- import { Select, SelectOption, Flex, FlexItem } from '@patternfly/react-core';
2540
+ import { Select, SelectOption, SelectVariant, Divider } from '@patternfly/react-core';
2263
2541
 
2264
- class SelectDocumentBodyVsParent extends React.Component {
2542
+ class MultiTypeaheadSelectInputCustomObjects extends React.Component {
2265
2543
  constructor(props) {
2266
2544
  super(props);
2267
- this.bodyOptions = [
2268
- <SelectOption key={0} value="Select a title - document body" isPlaceholder />,
2269
- <SelectOption key={1} value="Mr" />,
2270
- <SelectOption key={2} value="Miss" />,
2271
- <SelectOption key={3} value="Mrs" />,
2272
- <SelectOption key={4} value="Ms" />,
2273
- <SelectOption key={6} value="Dr" />,
2274
- <SelectOption key={7} value="Other" />
2275
- ];
2276
-
2277
- this.parentOptions = [
2278
- <SelectOption key={0} value="Select a title - parent" isPlaceholder />,
2279
- <SelectOption key={1} value="Mr" />,
2280
- <SelectOption key={2} value="Miss" />,
2281
- <SelectOption key={3} value="Mrs" />,
2282
- <SelectOption key={4} value="Ms" />,
2283
- <SelectOption key={6} value="Dr" />,
2284
- <SelectOption key={7} value="Other" />
2545
+ this.createState = (name, abbreviation, capital, founded) => {
2546
+ return {
2547
+ name: name,
2548
+ abbreviation: abbreviation,
2549
+ capital: capital,
2550
+ founded: founded,
2551
+ toString: function() {
2552
+ return `${this.name} (${this.abbreviation}) - Founded: ${this.founded}`;
2553
+ },
2554
+ compareTo: function(value) {
2555
+ return this.toString()
2556
+ .toLowerCase()
2557
+ .includes(value.toString().toLowerCase());
2558
+ }
2559
+ };
2560
+ };
2561
+ this.options = [
2562
+ <SelectOption key={0} value={this.createState('Alabama', 'AL', 'Montgomery', 1846)} />,
2563
+ <Divider component="li" key={111} />,
2564
+ <SelectOption key={1} value={this.createState('Florida', 'FL', 'Tailahassee', 1845)} />,
2565
+ <SelectOption key={2} value={this.createState('New Jersey', 'NJ', 'Trenton', 1787)} />,
2566
+ <SelectOption key={3} value={this.createState('New Mexico', 'NM', 'Santa Fe', 1912)} />,
2567
+ <SelectOption key={4} value={this.createState('New York', 'NY', 'Albany', 1788)} />,
2568
+ <SelectOption key={5} value={this.createState('North Carolina', 'NC', 'Raleigh', 1789)} />
2285
2569
  ];
2286
2570
 
2287
2571
  this.state = {
2288
- isBodyOpen: false,
2289
- isParentOpen: false,
2290
- bodySelected: null,
2291
- parentSelected: null
2292
- };
2293
-
2294
- this.onBodyToggle = (_event, isBodyOpen) => {
2295
- this.setState({
2296
- isBodyOpen
2297
- });
2572
+ isOpen: false,
2573
+ selected: []
2298
2574
  };
2299
2575
 
2300
- this.onParentToggle = (_event, isParentOpen) => {
2576
+ this.onToggle = (_event, isOpen) => {
2301
2577
  this.setState({
2302
- isParentOpen
2578
+ isOpen
2303
2579
  });
2304
2580
  };
2305
2581
 
2306
- this.onBodySelect = (event, selection, isPlaceholder) => {
2307
- if (isPlaceholder) this.clearSelection();
2308
- else {
2309
- this.setState({
2310
- bodySelected: selection,
2311
- isBodyOpen: false
2312
- });
2313
- console.log('selected on document body:', selection);
2314
- }
2315
- };
2316
-
2317
- this.onParentSelect = (event, selection, isPlaceholder) => {
2318
- if (isPlaceholder) this.clearSelection();
2319
- else {
2320
- this.setState({
2321
- parentSelected: selection,
2322
- isParentOpen: false
2323
- });
2324
- console.log('selected on parent:', selection);
2582
+ this.onSelect = (event, selection) => {
2583
+ const { selected } = this.state;
2584
+ if (selected.includes(selection)) {
2585
+ this.setState(
2586
+ prevState => ({ selected: prevState.selected.filter(item => item !== selection) }),
2587
+ () => console.log('selections: ', this.state.selected)
2588
+ );
2589
+ } else {
2590
+ this.setState(
2591
+ prevState => ({ selected: [...prevState.selected, selection] }),
2592
+ () => console.log('selections: ', this.state.selected)
2593
+ );
2325
2594
  }
2326
2595
  };
2327
2596
 
2328
2597
  this.clearSelection = () => {
2329
2598
  this.setState({
2330
- selected: null,
2599
+ selected: [],
2331
2600
  isOpen: false
2332
2601
  });
2333
2602
  };
2334
2603
  }
2335
2604
 
2336
2605
  render() {
2337
- const { isBodyOpen, isParentOpen, bodySelected, parentSelected } = this.state;
2606
+ const { isOpen, selected } = this.state;
2607
+ const titleId = 'multi-typeahead-select-id-2';
2338
2608
 
2339
2609
  return (
2340
- <Flex space={{ default: 'spacerMd' }} direction={{ default: 'column' }}>
2341
- <FlexItem>
2342
- <Select
2343
- aria-label="Select Input for Document Body"
2344
- onToggle={this.onBodyToggle}
2345
- onSelect={this.onBodySelect}
2346
- selections={bodySelected}
2347
- isOpen={isBodyOpen}
2348
- menuAppendTo={() => document.body}
2349
- >
2350
- {this.bodyOptions}
2351
- </Select>
2352
- </FlexItem>
2353
- <FlexItem>
2354
- <Select
2355
- aria-label="Select Input for Parent"
2356
- onToggle={this.onParentToggle}
2357
- onSelect={this.onParentSelect}
2358
- selections={parentSelected}
2359
- isOpen={isParentOpen}
2360
- menuAppendTo="parent"
2361
- >
2362
- {this.parentOptions}
2363
- </Select>
2364
- </FlexItem>
2365
- </Flex>
2610
+ <div>
2611
+ <span id={titleId} hidden>
2612
+ Select a state
2613
+ </span>
2614
+ <Select
2615
+ variant={SelectVariant.typeaheadMulti}
2616
+ typeAheadAriaLabel="Select a state"
2617
+ onToggle={this.onToggle}
2618
+ onSelect={this.onSelect}
2619
+ onClear={this.clearSelection}
2620
+ onFilter={this.customFilter}
2621
+ selections={selected}
2622
+ isOpen={isOpen}
2623
+ aria-labelledby={titleId}
2624
+ placeholderText="Select a state"
2625
+ >
2626
+ {this.options}
2627
+ </Select>
2628
+ </div>
2366
2629
  );
2367
2630
  }
2368
2631
  }
2369
2632
  ```
2370
2633
 
2371
- ### Favorites
2634
+ ### Plain multiple typeahead
2635
+
2636
+ To plainly style a typeahead, use the `isPlain` property.
2372
2637
 
2373
2638
  ```js
2374
2639
  import React from 'react';
2375
- import { Select, SelectOption, SelectVariant, SelectGroup } from '@patternfly/react-core';
2640
+ import { Select, SelectOption, SelectVariant } from '@patternfly/react-core';
2376
2641
 
2377
- class FavoritesSelect extends React.Component {
2642
+ class PlainSelectInput extends React.Component {
2378
2643
  constructor(props) {
2379
2644
  super(props);
2645
+ this.options = [
2646
+ { value: 'Alabama', disabled: false },
2647
+ { value: 'Florida', disabled: false },
2648
+ { value: 'New Jersey', disabled: false },
2649
+ { value: 'New Mexico', disabled: false },
2650
+ { value: 'New York', disabled: false },
2651
+ { value: 'North Carolina', disabled: false }
2652
+ ];
2653
+
2380
2654
  this.state = {
2381
2655
  isOpen: false,
2382
- selected: null,
2383
- favorites: []
2656
+ isPlain: true,
2657
+ selected: []
2384
2658
  };
2385
2659
 
2386
2660
  this.onToggle = (_event, isOpen) => {
@@ -2389,103 +2663,74 @@ class FavoritesSelect extends React.Component {
2389
2663
  });
2390
2664
  };
2391
2665
 
2392
- this.onSelect = (event, selection, isPlaceholder) => {
2393
- if (isPlaceholder) this.clearSelection();
2394
- else {
2395
- this.setState({
2396
- selected: selection,
2397
- isOpen: false
2398
- });
2399
- console.log('selected:', selection);
2666
+ this.onSelect = (event, selection) => {
2667
+ const { selected } = this.state;
2668
+ if (selected.includes(selection)) {
2669
+ this.setState(
2670
+ prevState => ({ selected: prevState.selected.filter(item => item !== selection) }),
2671
+ () => console.log('selections: ', this.state.selected)
2672
+ );
2673
+ } else {
2674
+ this.setState(
2675
+ prevState => ({ selected: [...prevState.selected, selection] }),
2676
+ () => console.log('selections: ', this.state.selected)
2677
+ );
2400
2678
  }
2401
2679
  };
2402
2680
 
2403
2681
  this.clearSelection = () => {
2404
2682
  this.setState({
2405
- selected: null,
2683
+ selected: [],
2406
2684
  isOpen: false
2407
2685
  });
2408
2686
  };
2409
-
2410
- this.onFavorite = (itemId, isFavorite) => {
2411
- if (isFavorite) {
2412
- this.setState({
2413
- favorites: this.state.favorites.filter(id => id !== itemId)
2414
- });
2415
- } else
2416
- this.setState({
2417
- favorites: [...this.state.favorites, itemId]
2418
- });
2419
- };
2420
-
2421
- this.options = [
2422
- <SelectGroup label="Status" key="group1">
2423
- <SelectOption id={'option-1'} key={0} value="Running" description="This is a description." />
2424
- <SelectOption id={'option-2'} key={1} value="Stopped" />
2425
- <SelectOption id={'option-3'} key={2} value="Down (disabled)" isDisabled />
2426
- <SelectOption id={'option-4'} key={3} value="Degraded" />
2427
- <SelectOption id={'option-5'} key={4} value="Needs maintenance" />
2428
- </SelectGroup>,
2429
- <SelectGroup label="Vendor names" key="group2">
2430
- <SelectOption id={'option-6'} key={5} value="Dell" />
2431
- <SelectOption id={'option-7'} key={6} value="Samsung" description="This is a description." />
2432
- <SelectOption id={'option-8'} key={7} value="Hewlett-Packard" />
2433
- </SelectGroup>
2434
- ];
2435
2687
  }
2436
2688
 
2437
2689
  render() {
2438
- const { isOpen, selected, favorites } = this.state;
2439
- const titleId = 'grouped-single-select-id';
2690
+ const { isOpen, isPlain, selected } = this.state;
2691
+ const titleId = 'plain-typeahead-select-id';
2692
+
2440
2693
  return (
2441
- <Select
2442
- variant={SelectVariant.typeahead}
2443
- typeAheadAriaLabel="Select value"
2444
- onToggle={this.onToggle}
2445
- onSelect={this.onSelect}
2446
- selections={selected}
2447
- isOpen={isOpen}
2448
- placeholderText="Favorites"
2449
- aria-labelledby={titleId}
2450
- isGrouped
2451
- onFavorite={this.onFavorite}
2452
- favorites={favorites}
2453
- onClear={this.clearSelection}
2454
- >
2455
- {this.options}
2456
- </Select>
2694
+ <div>
2695
+ <span id={titleId} hidden>
2696
+ Select a state
2697
+ </span>
2698
+ <Select
2699
+ variant={SelectVariant.typeaheadMulti}
2700
+ typeAheadAriaLabel="Select a state"
2701
+ onToggle={this.onToggle}
2702
+ onSelect={this.onSelect}
2703
+ onClear={this.clearSelection}
2704
+ selections={selected}
2705
+ isOpen={isOpen}
2706
+ isPlain={isPlain}
2707
+ aria-labelledby={titleId}
2708
+ placeholderText="Select a state"
2709
+ >
2710
+ {this.options.map((option, index) => (
2711
+ <SelectOption isDisabled={option.disabled} key={index} value={option.value} />
2712
+ ))}
2713
+ </Select>
2714
+ </div>
2457
2715
  );
2458
2716
  }
2459
2717
  }
2460
2718
  ```
2461
2719
 
2462
- ### Footer
2720
+ ### Custom menu content
2721
+
2722
+ To add custom menu content, use the `customContent` property.
2463
2723
 
2464
2724
  ```js
2465
2725
  import React from 'react';
2466
2726
  import CubeIcon from '@patternfly/react-icons/dist/esm/icons/cube-icon';
2467
- import { Select, SelectOption, SelectVariant, SelectDirection, Divider, Button } from '@patternfly/react-core';
2727
+ import { Select, SelectOption, SelectDirection, Checkbox } from '@patternfly/react-core';
2468
2728
 
2469
- class SelectWithFooter extends React.Component {
2729
+ class SingleSelectInput extends React.Component {
2470
2730
  constructor(props) {
2471
2731
  super(props);
2472
- this.options = [
2473
- <SelectOption key={0} value="Select a title" isPlaceholder />,
2474
- <SelectOption key={1} value="Mr" />,
2475
- <SelectOption key={2} value="Miss" />,
2476
- <SelectOption key={3} value="Mrs" />,
2477
- <SelectOption key={4} value="Ms" />,
2478
- <Divider component="li" key={5} />,
2479
- <SelectOption key={6} value="Dr" />,
2480
- <SelectOption key={7} value="Other" />
2481
- ];
2482
-
2483
- this.toggleRef = React.createRef();
2484
-
2485
2732
  this.state = {
2486
- isToggleIcon: false,
2487
2733
  isOpen: false,
2488
- selected: null,
2489
2734
  isDisabled: false,
2490
2735
  direction: SelectDirection.down
2491
2736
  };
@@ -2496,450 +2741,181 @@ class SelectWithFooter extends React.Component {
2496
2741
  });
2497
2742
  };
2498
2743
 
2499
- this.onSelect = (event, selection, isPlaceholder) => {
2500
- if (isPlaceholder) this.clearSelection();
2501
- else {
2744
+ this.toggleDisabled = checked => {
2745
+ this.setState({
2746
+ isDisabled: checked
2747
+ });
2748
+ };
2749
+
2750
+ this.toggleDirection = () => {
2751
+ if (this.state.direction === SelectDirection.up) {
2502
2752
  this.setState({
2503
- selected: selection,
2504
- isOpen: false
2753
+ direction: SelectDirection.down
2754
+ });
2755
+ } else {
2756
+ this.setState({
2757
+ direction: SelectDirection.up
2505
2758
  });
2506
- console.log('selected:', selection);
2507
- this.toggleRef.current.focus();
2508
2759
  }
2509
2760
  };
2510
-
2511
- this.clearSelection = () => {
2512
- this.setState({
2513
- selected: null,
2514
- isOpen: false
2515
- });
2516
- };
2517
2761
  }
2518
2762
 
2519
2763
  render() {
2520
- const { isOpen, selected, isDisabled, direction, isToggleIcon } = this.state;
2521
- const titleId = 'title-id-footer';
2764
+ const { isOpen, selected, isDisabled, direction } = this.state;
2765
+ const titleId = 'title-id-2';
2522
2766
  return (
2523
2767
  <div>
2524
2768
  <span id={titleId} hidden>
2525
2769
  Title
2526
2770
  </span>
2527
2771
  <Select
2528
- toggleIcon={isToggleIcon && <CubeIcon />}
2529
- toggleRef={this.toggleRef}
2530
- variant={SelectVariant.single}
2531
2772
  aria-label="Select Input"
2532
2773
  onToggle={this.onToggle}
2533
- onSelect={this.onSelect}
2534
- selections={selected}
2535
2774
  isOpen={isOpen}
2536
2775
  aria-labelledby={titleId}
2537
2776
  isDisabled={isDisabled}
2538
2777
  direction={direction}
2539
- footer={
2540
- <>
2541
- <Button variant="link" isInline>
2542
- Action
2543
- </Button>
2544
- </>
2545
- }
2546
- >
2547
- {this.options}
2548
- </Select>
2778
+ customContent="[Panel contents here]"
2779
+ placeholderText="Filter by birth month"
2780
+ />
2781
+ <Checkbox
2782
+ label="isDisabled"
2783
+ isChecked={this.state.isDisabled}
2784
+ onChange={(_event, checked) => this.toggleDisabled(checked)}
2785
+ aria-label="disabled checkbox panel"
2786
+ id="toggle-disabled-panel"
2787
+ name="toggle-disabled-panel"
2788
+ />
2789
+ <Checkbox
2790
+ label="Expands up"
2791
+ isChecked={direction === SelectDirection.up}
2792
+ onChange={this.toggleDirection}
2793
+ aria-label="direction checkbox panel"
2794
+ id="toggle-direction-panel"
2795
+ name="toggle-direction-panel"
2796
+ />
2549
2797
  </div>
2550
2798
  );
2551
2799
  }
2552
2800
  }
2553
2801
  ```
2554
2802
 
2555
- ### Footer with checkboxes
2803
+ ### Appending document body vs parent
2804
+
2805
+ Avoid passing in `document.body` to the `menuAppendTo` property. Doing so can cause accessibility issues because this prevents users from being able to enter the contents of the select options via assistive technologies (like keyboards or screen readers). Instead, pass in `parent` to achieve the same result without sacrificing accessibility.
2806
+
2807
+ The following example demonstrates both methods. When the dropdown is opened, both select variants manage focus the same way, but behave differently when the tab key is pressed after an option is selected.
2808
+
2809
+ - For the `document.body` variant, the focus will be placed at the end of the page, since that is where the dropdown content is appended to in the DOM (rather than focus being placed on the second Select variant as one might expect).
2810
+ - For the `parent` variant, however, the focus will be placed on the next tab-able element (the "Toggle JS code" button for the code editor in this case).
2556
2811
 
2557
2812
  ```js
2558
2813
  import React from 'react';
2559
- import { Select, SelectOption, SelectVariant, Button } from '@patternfly/react-core';
2814
+ import { Select, SelectOption, Flex, FlexItem } from '@patternfly/react-core';
2560
2815
 
2561
- class SelectWithFooterCheckbox extends React.Component {
2816
+ class SelectDocumentBodyVsParent extends React.Component {
2562
2817
  constructor(props) {
2563
2818
  super(props);
2819
+ this.bodyOptions = [
2820
+ <SelectOption key={0} value="Select a title - document body" isPlaceholder />,
2821
+ <SelectOption key={1} value="Mr" />,
2822
+ <SelectOption key={2} value="Miss" />,
2823
+ <SelectOption key={3} value="Mrs" />,
2824
+ <SelectOption key={4} value="Ms" />,
2825
+ <SelectOption key={6} value="Dr" />,
2826
+ <SelectOption key={7} value="Other" />
2827
+ ];
2828
+
2829
+ this.parentOptions = [
2830
+ <SelectOption key={0} value="Select a title - parent" isPlaceholder />,
2831
+ <SelectOption key={1} value="Mr" />,
2832
+ <SelectOption key={2} value="Miss" />,
2833
+ <SelectOption key={3} value="Mrs" />,
2834
+ <SelectOption key={4} value="Ms" />,
2835
+ <SelectOption key={6} value="Dr" />,
2836
+ <SelectOption key={7} value="Other" />
2837
+ ];
2838
+
2564
2839
  this.state = {
2565
- isOpen: false,
2566
- selected: [],
2567
- numOptions: 3,
2568
- isLoading: false
2840
+ isBodyOpen: false,
2841
+ isParentOpen: false,
2842
+ bodySelected: null,
2843
+ parentSelected: null
2569
2844
  };
2570
2845
 
2571
- this.options = [
2572
- <SelectOption key={0} value="Active" description="This is a description" />,
2573
- <SelectOption key={1} value="Cancelled" />,
2574
- <SelectOption key={2} value="Paused" />,
2575
- <SelectOption key={4} value="Warning" />,
2576
- <SelectOption key={5} value="Restarted" />
2577
- ];
2846
+ this.onBodyToggle = (_event, isBodyOpen) => {
2847
+ this.setState({
2848
+ isBodyOpen
2849
+ });
2850
+ };
2578
2851
 
2579
- this.onToggle = (_event, isOpen) => {
2852
+ this.onParentToggle = (_event, isParentOpen) => {
2580
2853
  this.setState({
2581
- isOpen
2854
+ isParentOpen
2582
2855
  });
2583
2856
  };
2584
2857
 
2585
- this.onSelect = (event, selection) => {
2586
- const { selected } = this.state;
2587
- if (selected.includes(selection)) {
2588
- this.setState(
2589
- prevState => ({ selected: prevState.selected.filter(item => item !== selection) }),
2590
- () => console.log('selections: ', this.state.selected)
2591
- );
2592
- } else {
2593
- this.setState(
2594
- prevState => ({ selected: [...prevState.selected, selection] }),
2595
- () => console.log('selections: ', this.state.selected)
2596
- );
2858
+ this.onBodySelect = (event, selection, isPlaceholder) => {
2859
+ if (isPlaceholder) this.clearSelection();
2860
+ else {
2861
+ this.setState({
2862
+ bodySelected: selection,
2863
+ isBodyOpen: false
2864
+ });
2865
+ console.log('selected on document body:', selection);
2866
+ }
2867
+ };
2868
+
2869
+ this.onParentSelect = (event, selection, isPlaceholder) => {
2870
+ if (isPlaceholder) this.clearSelection();
2871
+ else {
2872
+ this.setState({
2873
+ parentSelected: selection,
2874
+ isParentOpen: false
2875
+ });
2876
+ console.log('selected on parent:', selection);
2597
2877
  }
2598
2878
  };
2599
2879
 
2600
2880
  this.clearSelection = () => {
2601
2881
  this.setState({
2602
- selected: []
2882
+ selected: null,
2883
+ isOpen: false
2603
2884
  });
2604
2885
  };
2605
2886
  }
2606
2887
 
2607
2888
  render() {
2608
- const { isOpen, selected, isDisabled, direction, isToggleIcon } = this.state;
2609
- const titleId = 'title-id-footer-checkbox';
2889
+ const { isBodyOpen, isParentOpen, bodySelected, parentSelected } = this.state;
2890
+
2610
2891
  return (
2611
- <div>
2612
- <span id={titleId} hidden>
2613
- Title
2614
- </span>
2615
- <Select
2616
- variant={SelectVariant.checkbox}
2617
- aria-label="Select input"
2618
- onToggle={this.onToggle}
2619
- onSelect={this.onSelect}
2620
- selections={selected}
2621
- isOpen={isOpen}
2622
- placeholderText="Filter by status"
2623
- aria-labelledby={titleId}
2624
- footer={
2625
- <Button variant="link" isInline>
2626
- Action
2627
- </Button>
2628
- }
2629
- >
2630
- {this.options}
2631
- </Select>
2632
- </div>
2633
- );
2634
- }
2635
- }
2636
- ```
2637
-
2638
- ### View more
2639
-
2640
- ```js
2641
- import React from 'react';
2642
- import { Select, SelectOption, SelectVariant } from '@patternfly/react-core';
2643
-
2644
- class SelectViewMore extends React.Component {
2645
- constructor(props) {
2646
- super(props);
2647
- this.options = [
2648
- <SelectOption key={0} value="Select a title" isPlaceholder />,
2649
- <SelectOption key={1} value="Mr" />,
2650
- <SelectOption key={2} value="Miss" />,
2651
- <SelectOption key={3} value="Mrs" />,
2652
- <SelectOption key={4} value="Ms" />,
2653
- <SelectOption key={5} value="Dr" />,
2654
- <SelectOption key={6} value="Other" />
2655
- ];
2656
-
2657
- this.toggleRef = React.createRef();
2658
-
2659
- this.state = {
2660
- isOpen: false,
2661
- selected: null,
2662
- numOptions: 3,
2663
- isLoading: false
2664
- };
2665
-
2666
- this.onToggle = (_event, isOpen) => {
2667
- this.setState({
2668
- isOpen
2669
- });
2670
- };
2671
-
2672
- this.onSelect = (event, selection, isPlaceholder) => {
2673
- if (isPlaceholder) this.clearSelection();
2674
- else {
2675
- this.setState({
2676
- selected: selection,
2677
- isOpen: false
2678
- });
2679
- console.log('selected:', selection);
2680
- this.toggleRef.current.focus();
2681
- }
2682
- };
2683
-
2684
- this.clearSelection = () => {
2685
- this.setState({
2686
- selected: null,
2687
- isOpen: false
2688
- });
2689
- };
2690
-
2691
- this.simulateNetworkCall = callback => {
2692
- setTimeout(callback, 2000);
2693
- };
2694
-
2695
- this.onViewMoreClick = () => {
2696
- // Set select loadingVariant to spinner then simulate network call before loading more options
2697
- this.setState({ isLoading: true });
2698
- this.simulateNetworkCall(() => {
2699
- const newLength =
2700
- this.state.numOptions + 3 <= this.options.length ? this.state.numOptions + 3 : this.options.length;
2701
- this.setState({ numOptions: newLength, isLoading: false });
2702
- });
2703
- };
2704
- }
2705
-
2706
- render() {
2707
- const { isOpen, selected, isToggleIcon, numOptions, loadingVariant, isLoading } = this.state;
2708
- const titleId = 'title-id-view-more';
2709
- return (
2710
- <div>
2711
- <span id={titleId} hidden>
2712
- Title
2713
- </span>
2714
- <Select
2715
- variant={SelectVariant.single}
2716
- toggleRef={this.toggleRef}
2717
- aria-label="Select Input"
2718
- onToggle={this.onToggle}
2719
- onSelect={this.onSelect}
2720
- selections={selected}
2721
- isOpen={isOpen}
2722
- aria-labelledby={titleId}
2723
- {...(!isLoading &&
2724
- numOptions < this.options.length && {
2725
- loadingVariant: { text: 'View more', onClick: this.onViewMoreClick }
2726
- })}
2727
- {...(isLoading && { loadingVariant: 'spinner' })}
2728
- >
2729
- {this.options.slice(0, numOptions)}
2730
- </Select>
2731
- </div>
2732
- );
2733
- }
2734
- }
2735
- ```
2736
-
2737
- ### View more with checkboxes
2738
-
2739
- ```js
2740
- import React from 'react';
2741
- import { Select, SelectOption, SelectVariant } from '@patternfly/react-core';
2742
-
2743
- class SelectViewMoreCheckbox extends React.Component {
2744
- constructor(props) {
2745
- super(props);
2746
-
2747
- this.state = {
2748
- isOpen: false,
2749
- selected: [],
2750
- numOptions: 3,
2751
- isLoading: false
2752
- };
2753
-
2754
- this.options = [
2755
- <SelectOption key={0} value="Active" description="This is a description" />,
2756
- <SelectOption key={1} value="Cancelled" />,
2757
- <SelectOption key={2} value="Paused" />,
2758
- <SelectOption key={4} value="Warning" />,
2759
- <SelectOption key={5} value="Restarted" />,
2760
- <SelectOption key={6} value="Down" />,
2761
- <SelectOption key={7} value="Disabled" />,
2762
- <SelectOption key={8} value="Needs maintenance " />,
2763
- <SelectOption key={9} value="Degraded " />
2764
- ];
2765
-
2766
- this.onToggle = (_event, isOpen) => {
2767
- this.setState({
2768
- isOpen
2769
- });
2770
- };
2771
-
2772
- this.onSelect = (event, selection) => {
2773
- const { selected } = this.state;
2774
- if (selected.includes(selection)) {
2775
- this.setState(
2776
- prevState => ({ selected: prevState.selected.filter(item => item !== selection) }),
2777
- () => console.log('selections: ', this.state.selected)
2778
- );
2779
- } else {
2780
- this.setState(
2781
- prevState => ({ selected: [...prevState.selected, selection] }),
2782
- () => console.log('selections: ', this.state.selected)
2783
- );
2784
- }
2785
- };
2786
-
2787
- this.clearSelection = () => {
2788
- this.setState({
2789
- selected: []
2790
- });
2791
- };
2792
-
2793
- this.simulateNetworkCall = callback => {
2794
- setTimeout(callback, 2000);
2795
- };
2796
-
2797
- this.onViewMoreClick = () => {
2798
- // Set select loadingVariant to spinner then simulate network call before loading more options
2799
- this.setState({ isLoading: true });
2800
- this.simulateNetworkCall(() => {
2801
- const newLength =
2802
- this.state.numOptions + 3 <= this.options.length ? this.state.numOptions + 3 : this.options.length;
2803
- this.setState({ numOptions: newLength, isLoading: false });
2804
- });
2805
- };
2806
- }
2807
-
2808
- render() {
2809
- const { isOpen, selected, numOptions, isLoading } = this.state;
2810
- const titleId = 'view-more-checkbox-select-id';
2811
- return (
2812
- <div>
2813
- <span id={titleId} hidden>
2814
- Checkbox View more check
2815
- </span>
2816
- <Select
2817
- variant={SelectVariant.checkbox}
2818
- aria-label="Select input"
2819
- onToggle={this.onToggle}
2820
- onSelect={this.onSelect}
2821
- selections={selected}
2822
- isOpen={isOpen}
2823
- placeholderText="Filter by status"
2824
- aria-labelledby={titleId}
2825
- {...(!isLoading &&
2826
- numOptions < this.options.length && {
2827
- loadingVariant: { text: 'View more', onClick: this.onViewMoreClick }
2828
- })}
2829
- {...(isLoading && { loadingVariant: 'spinner' })}
2830
- >
2831
- {this.options.slice(0, numOptions)}
2832
- </Select>
2833
- </div>
2892
+ <Flex space={{ default: 'spacerMd' }} direction={{ default: 'column' }}>
2893
+ <FlexItem>
2894
+ <Select
2895
+ aria-label="Select Input for Document Body"
2896
+ onToggle={this.onBodyToggle}
2897
+ onSelect={this.onBodySelect}
2898
+ selections={bodySelected}
2899
+ isOpen={isBodyOpen}
2900
+ menuAppendTo={() => document.body}
2901
+ >
2902
+ {this.bodyOptions}
2903
+ </Select>
2904
+ </FlexItem>
2905
+ <FlexItem>
2906
+ <Select
2907
+ aria-label="Select Input for Parent"
2908
+ onToggle={this.onParentToggle}
2909
+ onSelect={this.onParentSelect}
2910
+ selections={parentSelected}
2911
+ isOpen={isParentOpen}
2912
+ menuAppendTo="parent"
2913
+ >
2914
+ {this.parentOptions}
2915
+ </Select>
2916
+ </FlexItem>
2917
+ </Flex>
2834
2918
  );
2835
2919
  }
2836
2920
  }
2837
2921
  ```
2838
-
2839
- ### With a style applied to the placeholder text
2840
-
2841
- ```js
2842
- import React from 'react';
2843
- import { Select, SelectOption } from '@patternfly/react-core';
2844
-
2845
- function SelectWithPlaceholderStyle() {
2846
- const [isOpen, setIsOpen] = React.useState(false);
2847
- const [selected, setSelected] = React.useState([]);
2848
-
2849
- const options = [
2850
- <SelectOption key={0} value="Active" />,
2851
- <SelectOption key={1} value="Cancelled" />,
2852
- <SelectOption key={2} value="Paused" />
2853
- ];
2854
-
2855
- const onToggle = (_event, isOpen) => setIsOpen(isOpen);
2856
-
2857
- const onSelect = (event, selection, isPlaceholder) => {
2858
- setSelected(selection);
2859
- setIsOpen(false);
2860
- };
2861
-
2862
- const clearSelection = () => {
2863
- setSelected(null);
2864
- setIsOpen(false);
2865
- };
2866
-
2867
- const titleId = 'placeholder-style-select-id';
2868
-
2869
- return (
2870
- <div>
2871
- <span id={titleId} hidden>
2872
- Placeholder styles
2873
- </span>
2874
- <Select
2875
- variant={SelectVariant.single}
2876
- hasPlaceholderStyle
2877
- aria-label="Select input"
2878
- onToggle={onToggle}
2879
- onSelect={onSelect}
2880
- onClear={clearSelection}
2881
- selections={selected}
2882
- isOpen={isOpen}
2883
- placeholderText="Filter by status"
2884
- aria-labelledby={titleId}
2885
- >
2886
- {options}
2887
- </Select>
2888
- </div>
2889
- );
2890
- }
2891
- ```
2892
-
2893
- ### With a style applied to the placeholder option
2894
-
2895
- ```js
2896
- import React from 'react';
2897
- import { Select, SelectOption } from '@patternfly/react-core';
2898
-
2899
- function SelectWithPlaceholderStyle() {
2900
- const [isOpen, setIsOpen] = React.useState(false);
2901
- const [selected, setSelected] = React.useState([]);
2902
-
2903
- const options = [
2904
- <SelectOption key={0} value="Filter by status" isPlaceholder />,
2905
- <SelectOption key={1} value="Active" />,
2906
- <SelectOption key={2} value="Cancelled" />,
2907
- <SelectOption key={3} value="Paused" />
2908
- ];
2909
-
2910
- const onToggle = (_event, isOpen) => setIsOpen(isOpen);
2911
-
2912
- const onSelect = (event, selection, isPlaceholder) => {
2913
- setSelected(selection);
2914
- setIsOpen(false);
2915
- };
2916
-
2917
- const clearSelection = () => {
2918
- setSelected(null);
2919
- setIsOpen(false);
2920
- };
2921
-
2922
- const titleId = 'placeholder-style-select-option-id';
2923
-
2924
- return (
2925
- <div>
2926
- <span id={titleId} hidden>
2927
- Placeholder styles - select option
2928
- </span>
2929
- <Select
2930
- variant={SelectVariant.single}
2931
- hasPlaceholderStyle
2932
- aria-label="Select input"
2933
- onToggle={onToggle}
2934
- onSelect={onSelect}
2935
- onClear={clearSelection}
2936
- selections={selected}
2937
- isOpen={isOpen}
2938
- aria-labelledby={titleId}
2939
- >
2940
- {options}
2941
- </Select>
2942
- </div>
2943
- );
2944
- }
2945
- ```