From b043918fd95b921921dde8f8615d364f6665c0b5 Mon Sep 17 00:00:00 2001 From: edonehoo <105813956+edonehoo@users.noreply.github.com> Date: Thu, 9 Mar 2023 15:35:45 -0500 Subject: [PATCH] fix conflicts (#8399) Co-authored-by: nicolethoen --- .../src/components/Select/examples/Select.md | 2756 ++++++++--------- 1 file changed, 1366 insertions(+), 1390 deletions(-) diff --git a/packages/react-core/src/components/Select/examples/Select.md b/packages/react-core/src/components/Select/examples/Select.md index 812872ffe00..fba8061facb 100644 --- a/packages/react-core/src/components/Select/examples/Select.md +++ b/packages/react-core/src/components/Select/examples/Select.md @@ -10,7 +10,15 @@ import CubeIcon from '@patternfly/react-icons/dist/esm/icons/cube-icon'; ## Examples -### Single +### Single select + +To let users select a single item from a list, use a single select list. + +A select list may use other properties for additional customization. Select each checkbox in the example below to visualize the following behavior: + +- To prevent a toggle click from opening a select list, use the `isDisabled` property. +- 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. +- To add an icon to a select toggle, use the `toggleIcon` property. ```js import React from 'react'; @@ -144,7 +152,9 @@ class SingleSelectInput extends React.Component { } ``` -### Single with description +### With item descriptions + +To give more context to a `` in a list, use the `description` property. ```js import React from 'react'; @@ -232,7 +242,9 @@ class SingleSelectDescription extends React.Component { } ``` -### Grouped single +### With grouped items + +To group related select options together, use 1 or more `` components and title each group using the `label` property. ```js import React from 'react'; @@ -312,7 +324,104 @@ class GroupedSingleSelectInput extends React.Component { } ``` -### Validated +### Favoriting items + +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. + +```js +import React from 'react'; +import { Select, SelectOption, SelectVariant, SelectGroup } from '@patternfly/react-core'; + +class FavoritesSelect extends React.Component { + constructor(props) { + super(props); + this.state = { + isOpen: false, + selected: null, + favorites: [] + }; + + this.onToggle = (_event, isOpen) => { + this.setState({ + isOpen + }); + }; + + this.onSelect = (event, selection, isPlaceholder) => { + if (isPlaceholder) this.clearSelection(); + else { + this.setState({ + selected: selection, + isOpen: false + }); + console.log('selected:', selection); + } + }; + + this.clearSelection = () => { + this.setState({ + selected: null, + isOpen: false + }); + }; + + this.onFavorite = (itemId, isFavorite) => { + if (isFavorite) { + this.setState({ + favorites: this.state.favorites.filter(id => id !== itemId) + }); + } else + this.setState({ + favorites: [...this.state.favorites, itemId] + }); + }; + + this.options = [ + + + + + + + , + + + + + + ]; + } + + render() { + const { isOpen, selected, favorites } = this.state; + const titleId = 'grouped-single-select-id'; + return ( + + ); + } +} +``` + +### Validated selections + +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. + +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. ```js import React from 'react'; @@ -411,19 +520,152 @@ class ValidatedSelect extends React.Component { } ``` -### Checkbox input +### Styled placeholder text + +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. + +To fade the color of `placeholderText` to gray, use the `hasPlaceholderStyle` property. ```js import React from 'react'; -import { Select, SelectOption, SelectVariant, Divider } from '@patternfly/react-core'; +import { Select, SelectOption, SelectVariant } from '@patternfly/react-core'; -class CheckboxSelectInput extends React.Component { +function SelectWithPlaceholderStyle() { + const [isOpen, setIsOpen] = React.useState(false); + const [selected, setSelected] = React.useState([]); + + const options = [ + , + , + + ]; + + const onToggle = (_event, isOpen) => setIsOpen(isOpen); + + const onSelect = (event, selection, isPlaceholder) => { + setSelected(selection); + setIsOpen(false); + }; + + const clearSelection = () => { + setSelected(null); + setIsOpen(false); + }; + + const titleId = 'placeholder-style-select-id'; + + return ( +
+ + +
+ ); +} +``` + +### Placeholder select options + +To set a `` as a placeholder, use the `isPlaceholder` property. The following example sets the "Filter by status" as a placeholder so that it is pre-selected. + +```js + +import React from 'react'; +import { Select, SelectOption, SelectVariant } from '@patternfly/react-core'; + +function SelectWithPlaceholderStyle() { + const [isOpen, setIsOpen] = React.useState(false); + const [selected, setSelected] = React.useState([]); + + const options = [ + , + , + , + + ]; + + const onToggle = (_event, isOpen) => setIsOpen(isOpen); + + const onSelect = (event, selection, isPlaceholder) => { + setSelected(selection); + setIsOpen(false); + }; + + const clearSelection = () => { + setSelected(null); + setIsOpen(false); + }; + + const titleId = 'placeholder-style-select-option-id'; + + return ( +
+ + +
+ ); +} +``` + +### With a footer + +You can add a `footer` to a `} + toggleRef={this.toggleRef} + variant={SelectVariant.single} aria-label="Select Input" onToggle={this.onToggle} onSelect={this.onSelect} selections={selected} isOpen={isOpen} - placeholderText="Filter by status" aria-labelledby={titleId} + isDisabled={isDisabled} + direction={direction} + footer={ + <> + + + } > {this.options} @@ -489,19 +730,36 @@ class CheckboxSelectInput extends React.Component { } ``` -### Checkbox input with counts +### With view more + +To reduce the processing load for long select lists, replace overflow items with a "View more" link at the bottom of the select menu. + +Adjust the number of items shown above the "View more" link with the `numOptions` property. The following example passes 3 items into this property. ```js import React from 'react'; -import { Select, SelectOption, SelectVariant, Divider } from '@patternfly/react-core'; +import { Select, SelectOption, SelectVariant } from '@patternfly/react-core'; -class CheckboxSelectWithCounts extends React.Component { +class SelectViewMore extends React.Component { constructor(props) { super(props); + this.options = [ + , + , + , + , + , + , + + ]; + + this.toggleRef = React.createRef(); this.state = { isOpen: false, - selected: [] + selected: null, + numOptions: 3, + isLoading: false }; this.onToggle = (_event, isOpen) => { @@ -510,55 +768,64 @@ class CheckboxSelectWithCounts extends React.Component { }); }; - this.onSelect = (event, selection) => { - const { selected } = this.state; - if (selected.includes(selection)) { - this.setState( - prevState => ({ selected: prevState.selected.filter(item => item !== selection) }), - () => console.log('selections: ', this.state.selected) - ); - } else { - this.setState( - prevState => ({ selected: [...prevState.selected, selection] }), - () => console.log('selections: ', this.state.selected) - ); + this.onSelect = (event, selection, isPlaceholder) => { + if (isPlaceholder) this.clearSelection(); + else { + this.setState({ + selected: selection, + isOpen: false + }); + console.log('selected:', selection); + this.toggleRef.current.focus(); } }; this.clearSelection = () => { this.setState({ - selected: [] + selected: null, + isOpen: false }); }; - this.options = [ - , - , - , - , - - ]; + this.simulateNetworkCall = callback => { + setTimeout(callback, 2000); + }; + + this.onViewMoreClick = () => { + // Set select loadingVariant to spinner then simulate network call before loading more options + this.setState({ isLoading: true }); + this.simulateNetworkCall(() => { + const newLength = + this.state.numOptions + 3 <= this.options.length ? this.state.numOptions + 3 : this.options.length; + this.setState({ numOptions: newLength, isLoading: false }); + }); + }; } render() { - const { isOpen, selected } = this.state; - const titleId = 'checkbox-select-with-counts-id'; + const { isOpen, selected, isToggleIcon, numOptions, loadingVariant, isLoading } = this.state; + const titleId = 'title-id-view-more'; return (
); @@ -566,13 +833,17 @@ class CheckboxSelectWithCounts extends React.Component { } ``` -### Checkbox input no badge +### Checkbox select + +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 ` {this.options} - this.toggleInputValuePersisted(checked)} - aria-label="toggle input value persisted" - id="toggle-inline-filter-input-value-persisted" - name="toggle-inline-filter-input-value-persisted" - /> - this.toggleInputFilterPersisted(checked)} - aria-label="toggle input filter persisted" - id="toggle-inline-filter-input-filter-persisted" - name="toggle-inline-filter-input-filter-persisted" - /> - this.toggleCreatable(checked)} - aria-label="toggle creatable checkbox" - id="toggle-inline-filter-creatable-typeahead" - name="toggle-inline-filter-creatable-typeahead" - /> ); } } ``` -### Grouped checkbox input with filtering +### Checkbox select without selected count + +To remove the default item count badge, use the `isCheckboxSelectionBadgeHidden` property. ```js import React from 'react'; -import { Select, SelectOption, SelectGroup, SelectVariant } from '@patternfly/react-core'; +import { Select, SelectOption, SelectVariant } from '@patternfly/react-core'; -class FilteringCheckboxSelectInput extends React.Component { +class CheckboxSelectInputNoBadge extends React.Component { constructor(props) { super(props); @@ -885,21 +1143,6 @@ class FilteringCheckboxSelectInput extends React.Component { selected: [] }; - this.options = [ - - - - - - - , - - - - - - ]; - this.onToggle = (_event, isOpen) => { this.setState({ isOpen @@ -921,34 +1164,23 @@ class FilteringCheckboxSelectInput extends React.Component { } }; - this.onFilter = (_, textInput) => { - if (textInput === '') { - return this.options; - } else { - let filteredGroups = this.options - .map(group => { - let filteredGroup = React.cloneElement(group, { - children: group.props.children.filter(item => { - return item.props.value.toLowerCase().includes(textInput.toLowerCase()); - }) - }); - if (filteredGroup.props.children.length > 0) return filteredGroup; - }) - .filter(newGroup => newGroup); - return filteredGroups; - } - }; - this.clearSelection = () => { this.setState({ selected: [] }); }; + + this.options = [ + , + , + , + + ]; } render() { - const { isOpen, selected, filteredOptions } = this.state; - const titleId = 'grouped-checkbox-filtering-select-id'; + const { isOpen, selected } = this.state; + const titleId = 'checkbox-no-badge-select-id'; return (
@@ -975,13 +1205,15 @@ class FilteringCheckboxSelectInput extends React.Component { } ``` -### Grouped checkbox input with filtering and placeholder text +### Checkbox select with item counts + +To show users the number of items that a `` would match, use the `itemCount` property. The numerical value you pass into `itemCount` is displayed to the right of each menu item. ```js import React from 'react'; -import { Select, SelectOption, SelectGroup, SelectVariant } from '@patternfly/react-core'; +import { Select, SelectOption, SelectVariant, Divider } from '@patternfly/react-core'; -class FilteringCheckboxSelectInputWithPlaceholder extends React.Component { +class CheckboxSelectWithCounts extends React.Component { constructor(props) { super(props); @@ -990,21 +1222,6 @@ class FilteringCheckboxSelectInputWithPlaceholder extends React.Component { selected: [] }; - this.options = [ - - - - - - - , - - - - - - ]; - this.onToggle = (_event, isOpen) => { this.setState({ isOpen @@ -1026,52 +1243,38 @@ class FilteringCheckboxSelectInputWithPlaceholder extends React.Component { } }; - this.onFilter = (_, textInput) => { - if (textInput === '') { - return this.options; - } else { - let filteredGroups = this.options - .map(group => { - let filteredGroup = React.cloneElement(group, { - children: group.props.children.filter(item => { - return item.props.value.toLowerCase().includes(textInput.toLowerCase()); - }) - }); - if (filteredGroup.props.children.length > 0) return filteredGroup; - }) - .filter(newGroup => newGroup); - return filteredGroups; - } - }; - this.clearSelection = () => { this.setState({ selected: [] }); }; + + this.options = [ + , + , + , + , + + ]; } render() { - const { isOpen, selected, filteredOptions } = this.state; - const titleId = 'checkbox-filtering-with-placeholder-select-id'; + const { isOpen, selected } = this.state; + const titleId = 'checkbox-select-with-counts-id'; return (
@@ -1081,121 +1284,83 @@ class FilteringCheckboxSelectInputWithPlaceholder extends React.Component { } ``` -### Grouped checkbox input with filtering and custom badging +### Checkbox select with a footer + +You can combine a footer with checkbox input to allow users to apply an action to multiple items. ```js import React from 'react'; -import { Select, SelectOption, SelectGroup, SelectVariant } from '@patternfly/react-core'; +import { Select, SelectOption, SelectVariant, Button } from '@patternfly/react-core'; -class FilteringCheckboxSelectInputWithBadging extends React.Component { +class SelectWithFooterCheckbox extends React.Component { constructor(props) { super(props); - this.state = { isOpen: false, selected: [], - customBadgeText: 0 + numOptions: 3, + isLoading: false }; + this.toggleRef = React.createRef(); + this.options = [ - - - - - - - , - - - - - + , + , + , + , + ]; this.onToggle = (_event, isOpen) => { - this.setState({ - isOpen - }); + this.setState({ isOpen }); }; this.onSelect = (event, selection) => { - const { selected } = this.state; - if (selected.includes(selection)) { - this.setState( - prevState => ({ - selected: prevState.selected.filter(item => item !== selection), - customBadgeText: this.setBadgeText(prevState.selected.length - 1) - }), - () => console.log('selections: ', this.state.selected) - ); - } else { - this.setState( - prevState => ({ - selected: [...prevState.selected, selection], - customBadgeText: this.setBadgeText(prevState.selected.length + 1) - }), - () => console.log('selections: ', this.state.selected) - ); - } + this.setState({ selected: selection, isOpen: false }), console.log('selected: ', selection); + this.toggleRef.current.focus(); }; this.onFilter = (_, textInput) => { if (textInput === '') { return this.options; } else { - let filteredGroups = this.options - .map(group => { - let filteredGroup = React.cloneElement(group, { - children: group.props.children.filter(item => { - return item.props.value.toLowerCase().includes(textInput.toLowerCase()); - }) - }); - if (filteredGroup.props.children.length > 0) return filteredGroup; - }) - .filter(newGroup => newGroup); - return filteredGroups; + this.setState( + prevState => ({ selected: [...prevState.selected, selection] }), + () => console.log('selections: ', this.state.selected) + ); } }; this.clearSelection = () => { this.setState({ - selected: [], - customBadgeText: this.setBadgeText(0) + selected: [] }); }; - - this.setBadgeText = selected => { - if (selected === 7) { - return 'All'; - } - if (selected === 0) { - return 0; - } - return null; - }; } render() { - const { isOpen, selected, filteredOptions, customBadgeText } = this.state; - const titleId = 'checkbox-filtering-custom-badging-select-id'; + const { isOpen, selected, isDisabled, direction, isToggleIcon } = this.state; + const titleId = 'title-id-footer-checkbox'; return (
@@ -1205,491 +1370,441 @@ class FilteringCheckboxSelectInputWithBadging extends React.Component { } ``` -### Typeahead +### Checkbox select with view more + +When a "view more" link is used alongside checkbox input, selections that users make prior to clicking "view more" are persisted after the click. ```js import React from 'react'; -import { Checkbox, Select, SelectOption, SelectVariant } from '@patternfly/react-core'; +import { Select, SelectOption, SelectVariant } from '@patternfly/react-core'; -class TypeaheadSelectInput extends React.Component { +class SelectViewMoreCheckbox extends React.Component { constructor(props) { super(props); - this.defaultOptions = [ - { value: 'Alabama' }, - { value: 'Florida', description: 'This is a description' }, - { value: 'New Jersey' }, - { value: 'New Mexico' }, - { value: 'New York' }, - { value: 'North Carolina' } - ]; this.state = { - options: this.defaultOptions, isOpen: false, - selected: null, - isDisabled: false, - isCreatable: false, - isCreateOptionOnTop: false, - isInputValuePersisted: false, - isInputFilterPersisted: false, - hasOnCreateOption: false, - resetOnSelect: true + selected: [], + numOptions: 3, + isLoading: false }; + this.options = [ + , + , + , + , + , + , + , + , + + ]; + this.onToggle = (_event, isOpen) => { this.setState({ isOpen }); }; - this.onSelect = (event, selection, isPlaceholder) => { - if (isPlaceholder) this.clearSelection(); - else { - this.setState({ - selected: selection, - isOpen: this.state.resetOnSelect ? false : this.state.isOpen - }); - console.log('selected:', selection); + this.onSelect = (event, selection) => { + const { selected } = this.state; + if (selected.includes(selection)) { + this.setState( + prevState => ({ selected: prevState.selected.filter(item => item !== selection) }), + () => console.log('selections: ', this.state.selected) + ); + } else { + this.setState( + prevState => ({ selected: [...prevState.selected, selection] }), + () => console.log('selections: ', this.state.selected) + ); } }; - this.onCreateOption = newValue => { + this.clearSelection = () => { this.setState({ - options: [...this.state.options, { value: newValue }] + selected: [] }); }; - this.clearSelection = () => { - this.setState({ - selected: null, - isOpen: false, - options: this.defaultOptions - }); - }; - - this.toggleDisabled = checked => { - this.setState({ - isDisabled: checked - }); - }; - - this.toggleCreatable = checked => { - this.setState({ - isCreatable: checked - }); - }; - - this.toggleCreateOptionOnTop = checked => { - this.setState({ - isCreateOptionOnTop: checked - }); - }; - - this.toggleCreateNew = checked => { - this.setState({ - hasOnCreateOption: checked - }); - }; - - this.toggleInputValuePersisted = checked => { - this.setState({ - isInputValuePersisted: checked - }); - }; - - this.toggleInputFilterPersisted = checked => { - this.setState({ - isInputFilterPersisted: checked - }); + this.simulateNetworkCall = callback => { + setTimeout(callback, 2000); }; - this.toggleResetOnSelect = checked => { - this.setState({ - resetOnSelect: checked + this.onViewMoreClick = () => { + // Set select loadingVariant to spinner then simulate network call before loading more options + this.setState({ isLoading: true }); + this.simulateNetworkCall(() => { + const newLength = + this.state.numOptions + 3 <= this.options.length ? this.state.numOptions + 3 : this.options.length; + this.setState({ numOptions: newLength, isLoading: false }); }); }; } render() { - const { - isOpen, - selected, - isDisabled, - isCreatable, - isCreateOptionOnTop, - hasOnCreateOption, - isInputValuePersisted, - isInputFilterPersisted, - resetOnSelect, - options - } = this.state; - const titleId = 'typeahead-select-id-1'; + const { isOpen, selected, numOptions, isLoading } = this.state; + const titleId = 'view-more-checkbox-select-id'; return (
- this.toggleDisabled(checked)} - aria-label="toggle disabled checkbox" - id="toggle-disabled-typeahead" - name="toggle-disabled-typeahead" - /> - this.toggleCreatable(checked)} - aria-label="toggle creatable checkbox" - id="toggle-creatable-typeahead" - name="toggle-creatable-typeahead" - /> - this.toggleCreateOptionOnTop(checked)} - aria-label="toggle createOptionOnTop checkbox" - id="toggle-create-option-on-top-typeahead" - name="toggle-create-option-on-top-typeahead" - /> - this.toggleCreateNew(checked)} - aria-label="toggle new checkbox" - id="toggle-new-typeahead" - name="toggle-new-typeahead" - /> - this.toggleInputValuePersisted(checked)} - aria-label="toggle input value persisted" - id="toggle-input-value-persisted" - name="toggle-input-value-persisted" - /> - this.toggleInputFilterPersisted(checked)} - aria-label="toggle input filter persisted" - id="toggle-input-filter-persisted" - name="toggle-input-filter-persisted" - /> - this.toggleResetOnSelect(checked)} - aria-label="toggle reset checkbox" - id="toggle-reset-typeahead" - name="toggle-reset-typeahead" - />
); } } ``` -### Grouped typeahead +### Filtering with placeholder text + +To preload a filter search bar with placeholder text, use the `inlineFilterPlaceholderText` property. The following example preloads the search bar with "Filter by status". ```js import React from 'react'; -import { Checkbox, Select, SelectGroup, SelectOption, SelectVariant, Divider } from '@patternfly/react-core'; +import { Select, SelectOption, SelectGroup, SelectVariant } from '@patternfly/react-core'; -class GroupedTypeaheadSelectInput extends React.Component { +class FilteringCheckboxSelectInputWithPlaceholder extends React.Component { constructor(props) { super(props); this.state = { - options: [ - - - - - - - , - , - - - - - - ], - newOptions: [], isOpen: false, - selected: null, - isCreatable: false, - hasOnCreateOption: false + selected: [] }; + this.options = [ + + + + + + + , + + + + + + ]; + this.onToggle = (_event, isOpen) => { this.setState({ isOpen }); }; - this.onSelect = (event, selection, isPlaceholder) => { - if (isPlaceholder) this.clearSelection(); - else { - this.setState({ - selected: selection, - isOpen: false - }); - console.log('selected:', selection); + this.onSelect = (event, selection) => { + const { selected } = this.state; + if (selected.includes(selection)) { + this.setState( + prevState => ({ selected: prevState.selected.filter(item => item !== selection) }), + () => console.log('selections: ', this.state.selected) + ); + } else { + this.setState( + prevState => ({ selected: [...prevState.selected, selection] }), + () => console.log('selections: ', this.state.selected) + ); } }; - this.onCreateOption = newValue => { - this.setState({ - newOptions: [...this.state.newOptions, ] - }); + this.onFilter = (_, textInput) => { + if (textInput === '') { + return this.options; + } else { + let filteredGroups = this.options + .map(group => { + let filteredGroup = React.cloneElement(group, { + children: group.props.children.filter(item => { + return item.props.value.toLowerCase().includes(textInput.toLowerCase()); + }) + }); + if (filteredGroup.props.children.length > 0) return filteredGroup; + }) + .filter(newGroup => newGroup); + return filteredGroups; + } }; this.clearSelection = () => { this.setState({ - selected: null, - isOpen: false - }); - }; - - this.toggleCreatable = checked => { - this.setState({ - isCreatable: checked - }); - }; - - this.toggleCreateNew = checked => { - this.setState({ - hasOnCreateOption: checked + selected: [] }); }; } render() { - const { isOpen, selected, isDisabled, isCreatable, hasOnCreateOption, options, newOptions } = this.state; - const titleId = 'grouped-typeahead-select-id'; - const allOptions = - newOptions.length > 0 - ? options.concat( - - {newOptions} - - ) - : options; + const { isOpen, selected, filteredOptions } = this.state; + const titleId = 'checkbox-filtering-with-placeholder-select-id'; return (
- this.toggleCreatable(checked)} - aria-label="toggle creatable checkbox" - id="toggle-creatable-grouped-typeahead" - name="toggle-creatable-grouped-typeahead" - /> - this.toggleCreateNew(checked)} - aria-label="toggle new checkbox" - id="toggle-new-grouped-typeahead" - name="toggle-new-grouped-typeahead" - />
); } } ``` -### Custom filtering +### Inline filtering + +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: + +- To persist filter results on blur, use the `isInputValuePersisted` property. +- To persist a filter that a user has searched, use the `isInputFilterPersisted` property. +- 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. ```js import React from 'react'; -import { Select, SelectOption, SelectVariant } from '@patternfly/react-core'; +import { Select, SelectOption, SelectGroup, SelectVariant, Checkbox } from '@patternfly/react-core'; -class TypeaheadSelectInput extends React.Component { +class FilteringSingleSelectInput extends React.Component { constructor(props) { super(props); - this.options = [ - , - , - , - , - , - - ]; + this.state = { isOpen: false, - selected: null + selected: '', + isCreatable: false, + isInputValuePersisted: false, + isInputFilterPersisted: false }; + this.options = [ + + + + + + + , + + + + + + ]; + this.onToggle = (_event, isOpen) => { this.setState({ isOpen }); }; - this.onSelect = (event, selection, isPlaceholder) => { - if (isPlaceholder) this.clearSelection(); - else { - this.setState({ - selected: selection, - isOpen: false - }); - console.log('selected:', selection); + this.onSelect = (event, selection) => { + this.setState({ selected: selection, isOpen: false }), console.log('selected: ', selection); + }; + + this.onFilter = (_, textInput) => { + if (textInput === '') { + return this.options; + } else { + let filteredGroups = this.options + .map(group => { + let filteredGroup = React.cloneElement(group, { + children: group.props.children.filter(item => { + return item.props.value.toLowerCase().includes(textInput.toLowerCase()); + }) + }); + if (filteredGroup.props.children.length > 0) return filteredGroup; + }) + .filter(Boolean); + return filteredGroups; } }; - this.clearSelection = () => { + this.toggleCreatable = (_, checked) => { this.setState({ - selected: null, - isOpen: false + isCreatable: checked }); }; - this.customFilter = (_, value) => { - if (!value) { - return this.options; - } + this.toggleInputValuePersisted = (_, checked) => { + this.setState({ + isInputValuePersisted: checked + }); + }; - const input = new RegExp(value, 'i'); - return this.options.filter(child => input.test(child.props.value)); + this.toggleInputFilterPersisted = (_, checked) => { + this.setState({ + isInputFilterPersisted: checked + }); }; } render() { - const { isOpen, selected } = this.state; - const titleId = 'typeahead-select-id-2'; + const { + isOpen, + selected, + filteredOptions, + isInputValuePersisted, + isInputFilterPersisted, + isCreatable + } = this.state; + const titleId = 'single-filtering-select-id'; return (
+ + +
); } } ``` -### Multiple +### Typeahead + +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. + +To make a typeahead, pass `variant=typeahead` into the ` @@ -1753,37 +1891,61 @@ class MultiTypeaheadSelectInput extends React.Component { /> ))} + this.toggleDisabled(checked)} + aria-label="toggle disabled checkbox" + id="toggle-disabled-typeahead" + name="toggle-disabled-typeahead" + /> this.toggleCreatable(checked)} aria-label="toggle creatable checkbox" - id="toggle-creatable-typeahead-multi" - name="toggle-creatable-typeahead-multi" + id="toggle-creatable-typeahead" + name="toggle-creatable-typeahead" + /> + this.toggleCreateOptionOnTop(checked)} + aria-label="toggle createOptionOnTop checkbox" + id="toggle-create-option-on-top-typeahead" + name="toggle-create-option-on-top-typeahead" /> this.toggleCreateNew(checked)} aria-label="toggle new checkbox" - id="toggle-new-typeahead-multi" - name="toggle-new-typeahead-multi" + id="toggle-new-typeahead" + name="toggle-new-typeahead" /> this.toggleInputValuePersisted(checked)} + aria-label="toggle input value persisted" + id="toggle-input-value-persisted" + name="toggle-input-value-persisted" + /> + this.toggleInputFilterPersisted(checked)} + aria-label="toggle input filter persisted" + id="toggle-input-filter-persisted" + name="toggle-input-filter-persisted" /> this.toggleResetOnSelect(checked)} - aria-label="toggle multi reset checkbox" - id="toggle-reset-multi-typeahead" - name="toggle-reset-multi-typeahead" + aria-label="toggle reset checkbox" + id="toggle-reset-typeahead" + name="toggle-reset-typeahead" />
); @@ -1791,27 +1953,39 @@ class MultiTypeaheadSelectInput extends React.Component { } ``` -### Multiple with Custom Chip Group Props +### Grouped typeahead + +Typeahead matches items with user input across groups. ```js import React from 'react'; -import { Select, SelectOption, SelectVariant } from '@patternfly/react-core'; +import { Checkbox, Select, SelectGroup, SelectOption, SelectVariant, Divider } from '@patternfly/react-core'; -class MultiTypeaheadSelectInputWithChipGroupProps extends React.Component { +class GroupedTypeaheadSelectInput extends React.Component { constructor(props) { super(props); this.state = { options: [ - { value: 'Alabama', disabled: false }, - { value: 'Florida', disabled: false }, - { value: 'New Jersey', disabled: false }, - { value: 'New Mexico', disabled: false, description: 'This is a description' }, - { value: 'New York', disabled: false }, - { value: 'North Carolina', disabled: false } + + + + + + + , + , + + + + + ], + newOptions: [], isOpen: false, - selected: [] + selected: null, + isCreatable: false, + hasOnCreateOption: false }; this.onToggle = (_event, isOpen) => { @@ -1820,41 +1994,61 @@ class MultiTypeaheadSelectInputWithChipGroupProps extends React.Component { }); }; - this.onSelect = (event, selection) => { - const { selected } = this.state; - if (selected.includes(selection)) { - this.setState( - prevState => ({ selected: prevState.selected.filter(item => item !== selection) }), - () => console.log('selections: ', this.state.selected) - ); - } else { - this.setState( - prevState => ({ selected: [...prevState.selected, selection] }), - () => console.log('selections: ', this.state.selected) - ); + this.onSelect = (event, selection, isPlaceholder) => { + if (isPlaceholder) this.clearSelection(); + else { + this.setState({ + selected: selection, + isOpen: false + }); + console.log('selected:', selection); } }; + this.onCreateOption = newValue => { + this.setState({ + newOptions: [...this.state.newOptions, ] + }); + }; + this.clearSelection = () => { this.setState({ - selected: [], + selected: null, isOpen: false }); }; + + this.toggleCreatable = checked => { + this.setState({ + isCreatable: checked + }); + }; + + this.toggleCreateNew = checked => { + this.setState({ + hasOnCreateOption: checked + }); + }; } render() { - const { isOpen, selected, isCreatable, hasOnCreateOption } = this.state; - const titleId = 'multi-typeahead-custom-chip-group-props-id-1'; - + const { isOpen, selected, isDisabled, isCreatable, hasOnCreateOption, options, newOptions } = this.state; + const titleId = 'grouped-typeahead-select-id'; + const allOptions = + newOptions.length > 0 + ? options.concat( + + {newOptions} + + ) + : options; return (
+ this.toggleCreatable(checked)} + aria-label="toggle creatable checkbox" + id="toggle-creatable-grouped-typeahead" + name="toggle-creatable-grouped-typeahead" + /> + this.toggleCreateNew(checked)} + aria-label="toggle new checkbox" + id="toggle-new-grouped-typeahead" + name="toggle-new-grouped-typeahead" + />
); } } ``` -### Multiple with Render Custom Chip Group +### Typeahead with custom filtering + +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. ```js import React from 'react'; -import { Select, SelectOption, SelectVariant, ChipGroup, Chip } from '@patternfly/react-core'; +import { Select, SelectOption, SelectVariant } from '@patternfly/react-core'; -class MultiTypeaheadSelectInputWithChipGroupProps extends React.Component { +class TypeaheadSelectInput extends React.Component { constructor(props) { super(props); - + this.options = [ + , + , + , + , + , + + ]; this.state = { - options: [ - { value: 'Alabama', disabled: false }, - { value: 'Florida', disabled: false }, - { value: 'New Jersey', disabled: false }, - { value: 'New Mexico', disabled: false, description: 'This is a description' }, - { value: 'New York', disabled: false }, - { value: 'North Carolina', disabled: false } - ], isOpen: false, - selected: [] + selected: null }; this.onToggle = (_event, isOpen) => { @@ -1908,75 +2115,55 @@ class MultiTypeaheadSelectInputWithChipGroupProps extends React.Component { }); }; - this.onSelect = (event, selection) => { - const { selected } = this.state; - if (selected.includes(selection)) { - this.setState( - prevState => ({ selected: prevState.selected.filter(item => item !== selection) }), - () => console.log('selections: ', this.state.selected) - ); - } else { - this.setState( - prevState => ({ selected: [...prevState.selected, selection] }), - () => console.log('selections: ', this.state.selected) - ); + this.onSelect = (event, selection, isPlaceholder) => { + if (isPlaceholder) this.clearSelection(); + else { + this.setState({ + selected: selection, + isOpen: false + }); + console.log('selected:', selection); } }; this.clearSelection = () => { this.setState({ - selected: [], + selected: null, isOpen: false }); }; - this.chipGroupComponent = () => { - const { selected } = this.state; - return ( - - {(selected || []).map((currentChip, index) => ( - this.onSelect(event, currentChip)} - > - {currentChip} - - ))} - - ); + + this.customFilter = (_, value) => { + if (!value) { + return this.options; + } + + const input = new RegExp(value, 'i'); + return this.options.filter(child => input.test(child.props.value)); }; } render() { - const { isOpen, selected, isCreatable, hasOnCreateOption } = this.state; - const titleId = 'multi-typeahead-render-chip-group-props-id-1'; - + const { isOpen, selected } = this.state; + const titleId = 'typeahead-select-id-2'; return (
); @@ -1984,44 +2171,41 @@ class MultiTypeaheadSelectInputWithChipGroupProps extends React.Component { } ``` -### Multiple with custom objects +### Multiple typeahead + +To create a multiple typeahead select variant, pass `variant={SelectVariant.typeaheadMulti}` into the ` + this.toggleCreatable(checked)} + aria-label="toggle creatable checkbox" + id="toggle-creatable-typeahead-multi" + name="toggle-creatable-typeahead-multi" + /> + this.toggleCreateNew(checked)} + aria-label="toggle new checkbox" + id="toggle-new-typeahead-multi" + name="toggle-new-typeahead-multi" + /> + + this.toggleResetOnSelect(checked)} + aria-label="toggle multi reset checkbox" + id="toggle-reset-multi-typeahead" + name="toggle-reset-multi-typeahead" + />
); } } ``` -### Plain multiple typeahead +### Multiple typeahead with custom chips + +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. ```js import React from 'react'; import { Select, SelectOption, SelectVariant } from '@patternfly/react-core'; -class PlainSelectInput extends React.Component { +class MultiTypeaheadSelectInputWithChipGroupProps extends React.Component { constructor(props) { super(props); - this.options = [ - { value: 'Alabama', disabled: false }, - { value: 'Florida', disabled: false }, - { value: 'New Jersey', disabled: false }, - { value: 'New Mexico', disabled: false }, - { value: 'New York', disabled: false }, - { value: 'North Carolina', disabled: false } - ]; this.state = { + options: [ + { value: 'Alabama', disabled: false }, + { value: 'Florida', disabled: false }, + { value: 'New Jersey', disabled: false }, + { value: 'New Mexico', disabled: false, description: 'This is a description' }, + { value: 'New York', disabled: false }, + { value: 'North Carolina', disabled: false } + ], isOpen: false, - isPlain: true, selected: [] }; @@ -2136,8 +2389,8 @@ class PlainSelectInput extends React.Component { } render() { - const { isOpen, isPlain, selected } = this.state; - const titleId = 'plain-typeahead-select-id'; + const { isOpen, selected, isCreatable, hasOnCreateOption } = this.state; + const titleId = 'multi-typeahead-custom-chip-group-props-id-1'; return (
@@ -2145,6 +2398,7 @@ class PlainSelectInput extends React.Component { Select a state
@@ -2166,20 +2424,29 @@ class PlainSelectInput extends React.Component { } ``` -### Panel as a menu +### Multiple typeahead with chip group + +To customize chips even more, render a [``](/components/chip-group) component and pass it into the `chipGroupComponent` property of the ` - this.toggleDisabled(checked)} - aria-label="disabled checkbox panel" - id="toggle-disabled-panel" - name="toggle-disabled-panel" - /> - + this.clearSelection = () => { + this.setState({ + selected: [], + isOpen: false + }); + }; + this.chipGroupComponent = () => { + const { selected } = this.state; + return ( + + {(selected || []).map((currentChip, index) => ( + this.onSelect(event, currentChip)} + > + {currentChip} + + ))} + + ); + }; + } + + render() { + const { isOpen, selected, isCreatable, hasOnCreateOption } = this.state; + const titleId = 'multi-typeahead-render-chip-group-props-id-1'; + + return ( +
+ +
); } } ``` -### Appending document body vs parent - -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). +### Multiple typeahead with custom objects -Instead append to `"parent"` to achieve the same result without sacrificing accessibility. - -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. - -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). +A `` 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`. ```js import React from 'react'; -import { Select, SelectOption, Flex, FlexItem } from '@patternfly/react-core'; +import { Select, SelectOption, SelectVariant, Divider } from '@patternfly/react-core'; -class SelectDocumentBodyVsParent extends React.Component { +class MultiTypeaheadSelectInputCustomObjects extends React.Component { constructor(props) { super(props); - this.bodyOptions = [ - , - , - , - , - , - , - - ]; - - this.parentOptions = [ - , - , - , - , - , - , - + this.createState = (name, abbreviation, capital, founded) => { + return { + name: name, + abbreviation: abbreviation, + capital: capital, + founded: founded, + toString: function() { + return `${this.name} (${this.abbreviation}) - Founded: ${this.founded}`; + }, + compareTo: function(value) { + return this.toString() + .toLowerCase() + .includes(value.toString().toLowerCase()); + } + }; + }; + this.options = [ + , + , + , + , + , + , + ]; this.state = { - isBodyOpen: false, - isParentOpen: false, - bodySelected: null, - parentSelected: null - }; - - this.onBodyToggle = (_event, isBodyOpen) => { - this.setState({ - isBodyOpen - }); + isOpen: false, + selected: [] }; - this.onParentToggle = (_event, isParentOpen) => { + this.onToggle = (_event, isOpen) => { this.setState({ - isParentOpen + isOpen }); }; - this.onBodySelect = (event, selection, isPlaceholder) => { - if (isPlaceholder) this.clearSelection(); - else { - this.setState({ - bodySelected: selection, - isBodyOpen: false - }); - console.log('selected on document body:', selection); - } - }; - - this.onParentSelect = (event, selection, isPlaceholder) => { - if (isPlaceholder) this.clearSelection(); - else { - this.setState({ - parentSelected: selection, - isParentOpen: false - }); - console.log('selected on parent:', selection); + this.onSelect = (event, selection) => { + const { selected } = this.state; + if (selected.includes(selection)) { + this.setState( + prevState => ({ selected: prevState.selected.filter(item => item !== selection) }), + () => console.log('selections: ', this.state.selected) + ); + } else { + this.setState( + prevState => ({ selected: [...prevState.selected, selection] }), + () => console.log('selections: ', this.state.selected) + ); } }; this.clearSelection = () => { this.setState({ - selected: null, + selected: [], isOpen: false }); }; } render() { - const { isBodyOpen, isParentOpen, bodySelected, parentSelected } = this.state; + const { isOpen, selected } = this.state; + const titleId = 'multi-typeahead-select-id-2'; return ( - - - - - - - - +
+ + +
); } } ``` -### Favorites +### Plain multiple typeahead + +To plainly style a typeahead, use the `isPlain` property. ```js import React from 'react'; -import { Select, SelectOption, SelectVariant, SelectGroup } from '@patternfly/react-core'; +import { Select, SelectOption, SelectVariant } from '@patternfly/react-core'; -class FavoritesSelect extends React.Component { +class PlainSelectInput extends React.Component { constructor(props) { super(props); + this.options = [ + { value: 'Alabama', disabled: false }, + { value: 'Florida', disabled: false }, + { value: 'New Jersey', disabled: false }, + { value: 'New Mexico', disabled: false }, + { value: 'New York', disabled: false }, + { value: 'North Carolina', disabled: false } + ]; + this.state = { isOpen: false, - selected: null, - favorites: [] + isPlain: true, + selected: [] }; this.onToggle = (_event, isOpen) => { @@ -2389,103 +2663,74 @@ class FavoritesSelect extends React.Component { }); }; - this.onSelect = (event, selection, isPlaceholder) => { - if (isPlaceholder) this.clearSelection(); - else { - this.setState({ - selected: selection, - isOpen: false - }); - console.log('selected:', selection); + this.onSelect = (event, selection) => { + const { selected } = this.state; + if (selected.includes(selection)) { + this.setState( + prevState => ({ selected: prevState.selected.filter(item => item !== selection) }), + () => console.log('selections: ', this.state.selected) + ); + } else { + this.setState( + prevState => ({ selected: [...prevState.selected, selection] }), + () => console.log('selections: ', this.state.selected) + ); } }; this.clearSelection = () => { this.setState({ - selected: null, + selected: [], isOpen: false }); }; - - this.onFavorite = (itemId, isFavorite) => { - if (isFavorite) { - this.setState({ - favorites: this.state.favorites.filter(id => id !== itemId) - }); - } else - this.setState({ - favorites: [...this.state.favorites, itemId] - }); - }; - - this.options = [ - - - - - - - , - - - - - - ]; } render() { - const { isOpen, selected, favorites } = this.state; - const titleId = 'grouped-single-select-id'; + const { isOpen, isPlain, selected } = this.state; + const titleId = 'plain-typeahead-select-id'; + return ( - +
+ + +
); } } ``` -### Footer +### Custom menu content + +To add custom menu content, use the `customContent` property. ```js import React from 'react'; import CubeIcon from '@patternfly/react-icons/dist/esm/icons/cube-icon'; -import { Select, SelectOption, SelectVariant, SelectDirection, Divider, Button } from '@patternfly/react-core'; +import { Select, SelectOption, SelectDirection, Checkbox } from '@patternfly/react-core'; -class SelectWithFooter extends React.Component { +class SingleSelectInput extends React.Component { constructor(props) { super(props); - this.options = [ - , - , - , - , - , - , - , - - ]; - - this.toggleRef = React.createRef(); - this.state = { - isToggleIcon: false, isOpen: false, - selected: null, isDisabled: false, direction: SelectDirection.down }; @@ -2496,450 +2741,181 @@ class SelectWithFooter extends React.Component { }); }; - this.onSelect = (event, selection, isPlaceholder) => { - if (isPlaceholder) this.clearSelection(); - else { + this.toggleDisabled = checked => { + this.setState({ + isDisabled: checked + }); + }; + + this.toggleDirection = () => { + if (this.state.direction === SelectDirection.up) { this.setState({ - selected: selection, - isOpen: false + direction: SelectDirection.down + }); + } else { + this.setState({ + direction: SelectDirection.up }); - console.log('selected:', selection); - this.toggleRef.current.focus(); } }; - - this.clearSelection = () => { - this.setState({ - selected: null, - isOpen: false - }); - }; } render() { - const { isOpen, selected, isDisabled, direction, isToggleIcon } = this.state; - const titleId = 'title-id-footer'; + const { isOpen, selected, isDisabled, direction } = this.state; + const titleId = 'title-id-2'; return (
+ customContent="[Panel contents here]" + placeholderText="Filter by birth month" + /> + this.toggleDisabled(checked)} + aria-label="disabled checkbox panel" + id="toggle-disabled-panel" + name="toggle-disabled-panel" + /> +
); } } ``` -### Footer with checkboxes +### Appending document body vs parent + +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. + +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. + +- 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). ```js import React from 'react'; -import { Select, SelectOption, SelectVariant, Button } from '@patternfly/react-core'; +import { Select, SelectOption, Flex, FlexItem } from '@patternfly/react-core'; -class SelectWithFooterCheckbox extends React.Component { +class SelectDocumentBodyVsParent extends React.Component { constructor(props) { super(props); + this.bodyOptions = [ + , + , + , + , + , + , + + ]; + + this.parentOptions = [ + , + , + , + , + , + , + + ]; + this.state = { - isOpen: false, - selected: [], - numOptions: 3, - isLoading: false + isBodyOpen: false, + isParentOpen: false, + bodySelected: null, + parentSelected: null }; - this.options = [ - , - , - , - , - - ]; + this.onBodyToggle = (_event, isBodyOpen) => { + this.setState({ + isBodyOpen + }); + }; - this.onToggle = (_event, isOpen) => { + this.onParentToggle = (_event, isParentOpen) => { this.setState({ - isOpen + isParentOpen }); }; - this.onSelect = (event, selection) => { - const { selected } = this.state; - if (selected.includes(selection)) { - this.setState( - prevState => ({ selected: prevState.selected.filter(item => item !== selection) }), - () => console.log('selections: ', this.state.selected) - ); - } else { - this.setState( - prevState => ({ selected: [...prevState.selected, selection] }), - () => console.log('selections: ', this.state.selected) - ); + this.onBodySelect = (event, selection, isPlaceholder) => { + if (isPlaceholder) this.clearSelection(); + else { + this.setState({ + bodySelected: selection, + isBodyOpen: false + }); + console.log('selected on document body:', selection); + } + }; + + this.onParentSelect = (event, selection, isPlaceholder) => { + if (isPlaceholder) this.clearSelection(); + else { + this.setState({ + parentSelected: selection, + isParentOpen: false + }); + console.log('selected on parent:', selection); } }; this.clearSelection = () => { this.setState({ - selected: [] + selected: null, + isOpen: false }); }; } render() { - const { isOpen, selected, isDisabled, direction, isToggleIcon } = this.state; - const titleId = 'title-id-footer-checkbox'; + const { isBodyOpen, isParentOpen, bodySelected, parentSelected } = this.state; + return ( -
- - -
- ); - } -} -``` - -### View more - -```js -import React from 'react'; -import { Select, SelectOption, SelectVariant } from '@patternfly/react-core'; - -class SelectViewMore extends React.Component { - constructor(props) { - super(props); - this.options = [ - , - , - , - , - , - , - - ]; - - this.toggleRef = React.createRef(); - - this.state = { - isOpen: false, - selected: null, - numOptions: 3, - isLoading: false - }; - - this.onToggle = (_event, isOpen) => { - this.setState({ - isOpen - }); - }; - - this.onSelect = (event, selection, isPlaceholder) => { - if (isPlaceholder) this.clearSelection(); - else { - this.setState({ - selected: selection, - isOpen: false - }); - console.log('selected:', selection); - this.toggleRef.current.focus(); - } - }; - - this.clearSelection = () => { - this.setState({ - selected: null, - isOpen: false - }); - }; - - this.simulateNetworkCall = callback => { - setTimeout(callback, 2000); - }; - - this.onViewMoreClick = () => { - // Set select loadingVariant to spinner then simulate network call before loading more options - this.setState({ isLoading: true }); - this.simulateNetworkCall(() => { - const newLength = - this.state.numOptions + 3 <= this.options.length ? this.state.numOptions + 3 : this.options.length; - this.setState({ numOptions: newLength, isLoading: false }); - }); - }; - } - - render() { - const { isOpen, selected, isToggleIcon, numOptions, loadingVariant, isLoading } = this.state; - const titleId = 'title-id-view-more'; - return ( -
- - -
- ); - } -} -``` - -### View more with checkboxes - -```js -import React from 'react'; -import { Select, SelectOption, SelectVariant } from '@patternfly/react-core'; - -class SelectViewMoreCheckbox extends React.Component { - constructor(props) { - super(props); - - this.state = { - isOpen: false, - selected: [], - numOptions: 3, - isLoading: false - }; - - this.options = [ - , - , - , - , - , - , - , - , - - ]; - - this.onToggle = (_event, isOpen) => { - this.setState({ - isOpen - }); - }; - - this.onSelect = (event, selection) => { - const { selected } = this.state; - if (selected.includes(selection)) { - this.setState( - prevState => ({ selected: prevState.selected.filter(item => item !== selection) }), - () => console.log('selections: ', this.state.selected) - ); - } else { - this.setState( - prevState => ({ selected: [...prevState.selected, selection] }), - () => console.log('selections: ', this.state.selected) - ); - } - }; - - this.clearSelection = () => { - this.setState({ - selected: [] - }); - }; - - this.simulateNetworkCall = callback => { - setTimeout(callback, 2000); - }; - - this.onViewMoreClick = () => { - // Set select loadingVariant to spinner then simulate network call before loading more options - this.setState({ isLoading: true }); - this.simulateNetworkCall(() => { - const newLength = - this.state.numOptions + 3 <= this.options.length ? this.state.numOptions + 3 : this.options.length; - this.setState({ numOptions: newLength, isLoading: false }); - }); - }; - } - - render() { - const { isOpen, selected, numOptions, isLoading } = this.state; - const titleId = 'view-more-checkbox-select-id'; - return ( -
- - -
+ + + + + + + + ); } } ``` - -### With a style applied to the placeholder text - -```js -import React from 'react'; -import { Select, SelectOption } from '@patternfly/react-core'; - -function SelectWithPlaceholderStyle() { - const [isOpen, setIsOpen] = React.useState(false); - const [selected, setSelected] = React.useState([]); - - const options = [ - , - , - - ]; - - const onToggle = (_event, isOpen) => setIsOpen(isOpen); - - const onSelect = (event, selection, isPlaceholder) => { - setSelected(selection); - setIsOpen(false); - }; - - const clearSelection = () => { - setSelected(null); - setIsOpen(false); - }; - - const titleId = 'placeholder-style-select-id'; - - return ( -
- - -
- ); -} -``` - -### With a style applied to the placeholder option - -```js -import React from 'react'; -import { Select, SelectOption } from '@patternfly/react-core'; - -function SelectWithPlaceholderStyle() { - const [isOpen, setIsOpen] = React.useState(false); - const [selected, setSelected] = React.useState([]); - - const options = [ - , - , - , - - ]; - - const onToggle = (_event, isOpen) => setIsOpen(isOpen); - - const onSelect = (event, selection, isPlaceholder) => { - setSelected(selection); - setIsOpen(false); - }; - - const clearSelection = () => { - setSelected(null); - setIsOpen(false); - }; - - const titleId = 'placeholder-style-select-option-id'; - - return ( -
- - -
- ); -} -```