From 26b865b5dd73d851a2c4488f068f83dea53c27f3 Mon Sep 17 00:00:00 2001 From: nicolethoen Date: Fri, 3 Mar 2023 14:19:08 -0500 Subject: [PATCH 1/7] fix conflicts --- .../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 ( -
- - -
- ); -} -``` From 9d5a6bdcc32663b3c22021d37d501627232f7699 Mon Sep 17 00:00:00 2001 From: Erin Donehoo Date: Thu, 24 Aug 2023 13:29:31 -0400 Subject: [PATCH 2/7] docs(select): Adds documentation for React examples. --- .../src/components/Select/examples/Select.md | 27 +++++++++++++------ 1 file changed, 19 insertions(+), 8 deletions(-) diff --git a/packages/react-core/src/components/Select/examples/Select.md b/packages/react-core/src/components/Select/examples/Select.md index c2dcc8a7afe..0a78772f91c 100644 --- a/packages/react-core/src/components/Select/examples/Select.md +++ b/packages/react-core/src/components/Select/examples/Select.md @@ -13,9 +13,14 @@ import BellIcon from '@patternfly/react-icons/dist/esm/icons/bell-icon'; ## Examples -`Select` builds off of the Menu component suite to wrap commonly used properties and functions for a select menu. See the [Menu documentation](/components/menus/menu) for a full list of properties that may be passed through `Select` to further customize the select menu, or the [custom menu examples](/components/menus/custom-menus) for additional examples of fully functional menus. +Select builds off of the menu component suite to wrap commonly used properties and functions for a select menu. See the [menu documentation](/components/menus/menu) for a full list of properties that may be passed through select to further customize the select menu, or the [custom menu examples](/components/menus/custom-menus) for additional examples of fully functional menus. -### Single +### Single select +Single select dropdown menus allow your users to select a single option from a list of options. To change the name of the options, use the ‘itemId’ property. + +The initial text is listed in the select box to guide what options the user can select from. To change this text, alter the ‘React.useState’ property. + +You can also add more options by adding ``. You can follow the format of the preexisting options to ensure proper application. ```ts file="./SelectBasic.tsx" @@ -27,21 +32,27 @@ Showcases different option variants and customizations that are commonly used in ```ts file="./SelectOptionVariations.tsx" -``` +### Grouped single select +You can change a single select component to have multiple groups. To do this, you have to add the component `` and list the desired options below. -### Grouped single +You can also change the text above the group by inserting the property `label` and the desired name of the group by altering ``. ```ts file="./SelectGrouped.tsx" ``` -### Checkbox +### Checkbox select +You can use a checkbox select to allow your users to check multiple options in one select component. You can change the name of each checkbox by altering the `` component. You can also change the name of the initial prompt in ``. +You also have the option to disable one of the checkmark options. To do this, you can pass the variable `isDisabled` inside of `` ```ts file="./SelectCheckbox.tsx" ``` ### Typeahead +A typeahead allows your users to type their response to narrow it down from the list of options. You can change the name of the checks by altering both the `ItemId` and `children` properties. + +You can also change the name of the initial prompt under `placeholder` in ``. ```ts file="./SelectTypeahead.tsx" @@ -53,13 +64,13 @@ Showcases different option variants and customizations that are commonly used in ``` -### Multiple typeahead with chips - +### Multiple typeahead +A typeahead can also be used to select multiple options from its list. ```ts file="./SelectMultiTypeahead.tsx" ``` -### Multiple typeahead with create option +### Typeahead with create option ```ts file="./SelectMultiTypeaheadCreatable.tsx" From 831c138fe01fbe33d63f8061ad0aa85decd41d0c Mon Sep 17 00:00:00 2001 From: Erin Donehoo Date: Fri, 8 Sep 2023 14:58:30 -0400 Subject: [PATCH 3/7] Adds content to new select React examples. --- .../src/components/Select/examples/Select.md | 64 +++++++++++++------ 1 file changed, 46 insertions(+), 18 deletions(-) diff --git a/packages/react-core/src/components/Select/examples/Select.md b/packages/react-core/src/components/Select/examples/Select.md index 0a78772f91c..18935e9db34 100644 --- a/packages/react-core/src/components/Select/examples/Select.md +++ b/packages/react-core/src/components/Select/examples/Select.md @@ -13,82 +13,110 @@ import BellIcon from '@patternfly/react-icons/dist/esm/icons/bell-icon'; ## Examples -Select builds off of the menu component suite to wrap commonly used properties and functions for a select menu. See the [menu documentation](/components/menus/menu) for a full list of properties that may be passed through select to further customize the select menu, or the [custom menu examples](/components/menus/custom-menus) for additional examples of fully functional menus. +Select builds off of the menu component suite to adapt commonly used properties and functions to create a select menu. See the [menu documentation](/components/menus/menu) for a full list of properties that may be used to further customize a select menu. View the [custom menu examples](/components/menus/custom-menus) to see examples of fully functional select menus. ### Single select -Single select dropdown menus allow your users to select a single option from a list of options. To change the name of the options, use the ‘itemId’ property. +To let users select a single item from a list, use a single select menu. -The initial text is listed in the select box to guide what options the user can select from. To change this text, alter the ‘React.useState’ property. +You can add multiple `` components to build out a list of menu items. For each select option, pass a relevant option label to the `value` property. -You can also add more options by adding ``. You can follow the format of the preexisting options to ensure proper application. +To prevent a toggle click from opening a select list, use the `isDisabled` property. In the following example, select the checkbox to observe this behavior. ```ts file="./SelectBasic.tsx" ``` -### Option variations +### Select option variants -Showcases different option variants and customizations that are commonly used in a select menu. For a more complete list, see the [Menu documentation](/components/menus/menu). +The following example showcases different option variants and customizations that are commonly used in a select menu. + +To create these variants, you can pass different properties into a `` component. + +This example provides examples of: + +- An option with a description, which is created by using the `description` property. +- An option with a link, which is created by passing a URL into the `to` property. For external links, use the `isExternalLink` property so that the option is styled with an outbound link icon. +- An option with an icon, which is created by using the `icon` property. +- An option that is disabled by using the `isDisabled` property. ```ts file="./SelectOptionVariations.tsx" -### Grouped single select -You can change a single select component to have multiple groups. To do this, you have to add the component `` and list the desired options below. +``` -You can also change the text above the group by inserting the property `label` and the desired name of the group by altering ``. +### With grouped items +To group related select options together, use 1 or more `` components and title each group using the `label` property. ```ts file="./SelectGrouped.tsx" ``` ### Checkbox select -You can use a checkbox select to allow your users to check multiple options in one select component. You can change the name of each checkbox by altering the `` component. You can also change the name of the initial prompt in ``. +To let users select multiple list options via checkbox input, use a checkbox select. + +To create a checkbox select, pass `hasCheckbox` into each `` component. To indicate that an option is selected, use the `isSelected` property. + +By default, the menu toggle will display a badge to indicate the number of items that a user has selected. -You also have the option to disable one of the checkmark options. To do this, you can pass the variable `isDisabled` inside of `` ```ts file="./SelectCheckbox.tsx" ``` ### Typeahead -A typeahead allows your users to type their response to narrow it down from the list of options. You can change the name of the checks by altering both the `ItemId` and `children` properties. +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 enters characters into the text input, the select menu will provide suggestions by filtering the select options. -You can also change the name of the initial prompt under `placeholder` in ``. +To make a typeahead, pass `variant=typeahead` into the `` component and link an `onClick` function to the `` component. ```ts file="./SelectTypeahead.tsx" ``` ### Typeahead with create option +If a user enters a value into a typeahead select menu that does not exist, you can allow them to create an option of that value. + +To enable the creation ability, pass `value='create'` into a `` component. Additionally, you should pass a button with an `onClick` event into the `` component to display the creation prompt after a user enters a new value. You can use the `placeholder` property to change the default text shown in the text input. + +The following example outlines the code implementation required to create a working typeahead menu that allows for creation. ```ts file="./SelectTypeaheadCreatable.tsx" ``` -### Multiple typeahead -A typeahead can also be used to select multiple options from its list. +### Multiple typeahead with chips +A multiple typeahead can be used to allow users to select multiple options from a list. Selected items appear as chips in the select toggle. + +When more items than the allowed limit are selected, overflowing items will be hidden under a "more" button. The following example hides items after more than 3 are selected. To show hidden items, select the “more” button. Select "show less" to hide extra items again. + ```ts file="./SelectMultiTypeahead.tsx" ``` -### Typeahead with create option +### Multiple typeahead with create option ```ts file="./SelectMultiTypeaheadCreatable.tsx" ``` ### Multiple typeahead with checkboxes +By default, a multiple typeahead select allows you to select multiple menu items, placing a checkmark beside selected items. Like basic checkbox select menus, you can add checkboxes to your menu items. This approach may be more accurate and comprehensive for menu scenarios, like filtering. ```ts file="./SelectMultiTypeaheadCheckbox.tsx" ``` -### View more +### 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. + +You can adjust the number of items shown above the "View more" link as needed. The following example passes 3 items into this property. ```ts file="./SelectViewMore.tsx" ``` -### Footer +### With a footer + +You can add a `` component to a select menu to hold additional actions that users can take on menu items, through elements such as link buttons. A footer will be placed beneath a divider at the end of the select menu. + ```ts file="./SelectFooter.tsx" From b257fe728fe6fd9f7f2b67db6c8b033933bf96ff Mon Sep 17 00:00:00 2001 From: Erin Donehoo Date: Fri, 27 Oct 2023 14:39:58 -0400 Subject: [PATCH 4/7] Updates typeahead with create example. --- packages/react-core/src/components/Select/examples/Select.md | 1 + 1 file changed, 1 insertion(+) diff --git a/packages/react-core/src/components/Select/examples/Select.md b/packages/react-core/src/components/Select/examples/Select.md index 18935e9db34..1e093bbc8f6 100644 --- a/packages/react-core/src/components/Select/examples/Select.md +++ b/packages/react-core/src/components/Select/examples/Select.md @@ -91,6 +91,7 @@ When more items than the allowed limit are selected, overflowing items will be h ``` ### Multiple typeahead with create option +If the text that is entered into a typeahead doesn't match a menu item, users can choose to create a new option that matches the text input. Selecting the "create" option will also select the new item, which will appear as a chip in the toggle. ```ts file="./SelectMultiTypeaheadCreatable.tsx" From 4416448ce5926107f779ccc12faae6c09108bd9e Mon Sep 17 00:00:00 2001 From: Erin Donehoo <105813956+edonehoo@users.noreply.github.com> Date: Fri, 27 Oct 2023 15:46:53 -0400 Subject: [PATCH 5/7] Clarify mult typeahead with create content. --- packages/react-core/src/components/Select/examples/Select.md | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/packages/react-core/src/components/Select/examples/Select.md b/packages/react-core/src/components/Select/examples/Select.md index 1e093bbc8f6..95bbe03c4a7 100644 --- a/packages/react-core/src/components/Select/examples/Select.md +++ b/packages/react-core/src/components/Select/examples/Select.md @@ -91,7 +91,8 @@ When more items than the allowed limit are selected, overflowing items will be h ``` ### Multiple typeahead with create option -If the text that is entered into a typeahead doesn't match a menu item, users can choose to create a new option that matches the text input. Selecting the "create" option will also select the new item, which will appear as a chip in the toggle. + +When a multiple typeahead offers users a create option, each item that a user creates will be selected and place a chip for each item into the select toggle. ```ts file="./SelectMultiTypeaheadCreatable.tsx" From b1d86562356e1560ebf015270a1c92fbbb45a105 Mon Sep 17 00:00:00 2001 From: Erin Donehoo <105813956+edonehoo@users.noreply.github.com> Date: Fri, 27 Oct 2023 16:17:48 -0400 Subject: [PATCH 6/7] Finalizing wording. --- packages/react-core/src/components/Select/examples/Select.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/react-core/src/components/Select/examples/Select.md b/packages/react-core/src/components/Select/examples/Select.md index 95bbe03c4a7..f30a7136978 100644 --- a/packages/react-core/src/components/Select/examples/Select.md +++ b/packages/react-core/src/components/Select/examples/Select.md @@ -92,7 +92,7 @@ When more items than the allowed limit are selected, overflowing items will be h ### Multiple typeahead with create option -When a multiple typeahead offers users a create option, each item that a user creates will be selected and place a chip for each item into the select toggle. +When a multiple typeahead offers users a create option, each item that a user creates will be selected. A chip will be placed in the select toggle for each item that is created and selected. ```ts file="./SelectMultiTypeaheadCreatable.tsx" From 152ff5dbfd3bd9671175bce36d3389272e21a5a1 Mon Sep 17 00:00:00 2001 From: Erin Donehoo Date: Wed, 15 Nov 2023 15:17:15 -0500 Subject: [PATCH 7/7] Updates content from review. --- .../src/components/Select/examples/Select.md | 18 +++++++++--------- 1 file changed, 9 insertions(+), 9 deletions(-) diff --git a/packages/react-core/src/components/Select/examples/Select.md b/packages/react-core/src/components/Select/examples/Select.md index 1e093bbc8f6..3fe39843a71 100644 --- a/packages/react-core/src/components/Select/examples/Select.md +++ b/packages/react-core/src/components/Select/examples/Select.md @@ -20,7 +20,7 @@ To let users select a single item from a list, use a single select menu. You can add multiple `` components to build out a list of menu items. For each select option, pass a relevant option label to the `value` property. -To prevent a toggle click from opening a select list, use the `isDisabled` property. In the following example, select the checkbox to observe this behavior. +To disable the select menu toggle, use the `isDisabled` property. In the following example, select the checkbox to observe this behavior. ```ts file="./SelectBasic.tsx" @@ -53,7 +53,7 @@ To group related select options together, use 1 or more `` componen ### Checkbox select To let users select multiple list options via checkbox input, use a checkbox select. -To create a checkbox select, pass `hasCheckbox` into each `` component. To indicate that an option is selected, use the `isSelected` property. +To create a checkbox select, pass `role="menu"` to the `