From 2a80ba91e14e9a05fb90a735de597d05792f25e7 Mon Sep 17 00:00:00 2001 From: Katie McFaul Date: Wed, 24 Apr 2019 10:27:17 -0400 Subject: [PATCH 01/11] feat(Select): add typeahead single variant, update selected state css, updated tests --- .../src/components/Select/Select.d.ts | 2 + .../src/components/Select/Select.js | 86 ++++- .../src/components/Select/Select.md | 80 ++++ .../src/components/Select/Select.test.js | 20 + .../src/components/Select/SelectOption.js | 1 + .../src/components/Select/SelectToggle.js | 32 +- .../Select/__snapshots__/Select.test.js.snap | 345 +++++++++++++++++- .../__snapshots__/SelectToggle.test.js.snap | 6 +- .../src/components/Select/selectConstants.js | 3 +- 9 files changed, 547 insertions(+), 28 deletions(-) diff --git a/packages/patternfly-4/react-core/src/components/Select/Select.d.ts b/packages/patternfly-4/react-core/src/components/Select/Select.d.ts index 29e4409cd48..bf1174cdc2d 100644 --- a/packages/patternfly-4/react-core/src/components/Select/Select.d.ts +++ b/packages/patternfly-4/react-core/src/components/Select/Select.d.ts @@ -3,12 +3,14 @@ import { HTMLProps, FormEvent, ReactNode } from 'react'; export const SelectVariant: { single: 'single'; checkbox: 'checkbox'; + typeahead: 'typeahead'; }; export interface SelectProps extends HTMLProps { isExpanded?: boolean; isGrouped?: boolean; onToggle(value: boolean): void; + onClear() : void; placeholderText?: string | ReactNode; selections?: string | Array; variant?: string; diff --git a/packages/patternfly-4/react-core/src/components/Select/Select.js b/packages/patternfly-4/react-core/src/components/Select/Select.js index 30b6773dfd8..0d173474112 100644 --- a/packages/patternfly-4/react-core/src/components/Select/Select.js +++ b/packages/patternfly-4/react-core/src/components/Select/Select.js @@ -1,7 +1,10 @@ import React from 'react'; import styles from '@patternfly/patternfly/components/Select/select.css'; import badgeStyles from '@patternfly/patternfly/components/Badge/badge.css'; +import formStyles from '@patternfly/patternfly/components/FormControl/form-control.css'; +import buttonStyles from '@patternfly/patternfly/components/Button/button.css'; import { css } from '@patternfly/react-styles'; +import { TimesCircleIcon } from '@patternfly/react-icons'; import PropTypes from 'prop-types'; import SingleSelect from './SingleSelect'; import CheckboxSelect from './CheckboxSelect'; @@ -32,8 +35,10 @@ const propTypes = { onSelect: PropTypes.func.isRequired, /** Callback for toggle button behavior */ onToggle: PropTypes.func.isRequired, + /** Callback for typeahead clear button */ + onClear: PropTypes.func, /** Variant of rendered Select */ - variant: PropTypes.oneOf(['single', 'checkbox']), + variant: PropTypes.oneOf(['single', 'checkbox', 'typeahead']), /** Width of the select container as a number of px or string percentage */ width: PropTypes.oneOfType([PropTypes.string, PropTypes.number]), /** Additional props are spread to the container
    */ @@ -51,18 +56,50 @@ const defaultProps = { placeholderText: null, variant: SelectVariant.single, width: null, + onClear: Function.prototype }; class Select extends React.Component { parentRef = React.createRef(); - state = { openedOnEnter: false }; + state = { + openedOnEnter: false, + typeaheadValue: null, + filteredChildren: this.props.children + }; onEnter = () => { this.setState({ openedOnEnter: true }); }; onClose = () => { - this.setState({ openedOnEnter: false }); + this.setState({ + openedOnEnter: false, + typeaheadValue: null, + filteredChildren: this.props.children + }); + }; + + onChange = e => { + const input = new RegExp(e.target.value, 'i'); + this.setState({ + typeaheadValue: e.target.value, + filteredChildren: + e.target.value !== '' + ? this.props.children.filter(child => child.props.value.search(input) === 0) + : this.props.children + }); + }; + + onClick = e => { + e.stopPropagation(); + }; + + clearSelection = e => { + e.stopPropagation(); + this.setState({ + typeaheadValue: '', + filteredChildren: this.props.children + }); }; render() { @@ -72,6 +109,7 @@ class Select extends React.Component { variant, onToggle, onSelect, + onClear, isExpanded, isGrouped, selections, @@ -81,7 +119,7 @@ class Select extends React.Component { width, ...props } = this.props; - const { openedOnEnter } = this.state; + const { openedOnEnter, typeaheadValue, filteredChildren } = this.state; const selectToggleId = `pf-toggle-id-${currentId++}`; let childPlaceholderText = null; if (!selections && !placeholderText) { @@ -105,7 +143,7 @@ class Select extends React.Component { onEnter={this.onEnter} onClose={this.onClose} aria-labelledby={`${ariaLabelledBy} ${selectToggleId}`} - isCheckbox={variant === SelectVariant.checkbox} + variant={variant} > {variant === SelectVariant.single && (
    @@ -126,6 +164,33 @@ class Select extends React.Component {
    )} + {variant === SelectVariant.typeahead && ( + +
    + +
    + {selections && ( + + )} +
    + )} {variant === SelectVariant.single && isExpanded && ( )} + {variant === SelectVariant.typeahead && isExpanded && ( + + {filteredChildren} + + )} ); diff --git a/packages/patternfly-4/react-core/src/components/Select/Select.md b/packages/patternfly-4/react-core/src/components/Select/Select.md index 0356599b91c..f85bc8d9f4f 100644 --- a/packages/patternfly-4/react-core/src/components/Select/Select.md +++ b/packages/patternfly-4/react-core/src/components/Select/Select.md @@ -243,3 +243,83 @@ class GroupedCheckboxSelectInput extends React.Component { } } ``` + +## Typeahead Select Input +```js +import React from 'react'; +import { Select, SelectOption, SelectVariant, CheckboxSelectGroup, CheckboxSelectOption } from '@patternfly/react-core'; + +class TypeaheadSelectInput 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 = { + isExpanded: false, + selected: null + }; + + this.onToggle = isExpanded => { + this.setState({ + isExpanded + }); + }; + + this.onSelect = (event, selection, isPlaceholder) => { + if (isPlaceholder) this.clearSelection(); + else { + this.setState({ + selected: selection, + isExpanded: false + }); + console.log('selected:', selection); + } + }; + + this.clearSelection = () => { + this.setState({ + selected: null, + isExpanded: false + }); + }; + } + + render() { + const { isExpanded, selected } = this.state; + const titleId = 'title-id'; + return ( +
    + + +
    + ); + } +} +``` \ No newline at end of file diff --git a/packages/patternfly-4/react-core/src/components/Select/Select.test.js b/packages/patternfly-4/react-core/src/components/Select/Select.test.js index cdd86e06a25..ae28a8ea323 100644 --- a/packages/patternfly-4/react-core/src/components/Select/Select.test.js +++ b/packages/patternfly-4/react-core/src/components/Select/Select.test.js @@ -72,6 +72,26 @@ describe('checkbox select', () => { }); }); +describe('typeahead select', () => { + test('renders closed successfully', () => { + const view = mount( + + ); + expect(view).toMatchSnapshot(); + }); + + test('renders expanded successfully', () => { + const view = mount( + + ); + expect(view).toMatchSnapshot(); + }); +}); + describe('API', () => { test('click on item', () => { const mockToggle = jest.fn(); diff --git a/packages/patternfly-4/react-core/src/components/Select/SelectOption.js b/packages/patternfly-4/react-core/src/components/Select/SelectOption.js index 60726180efd..4b569a21af1 100644 --- a/packages/patternfly-4/react-core/src/components/Select/SelectOption.js +++ b/packages/patternfly-4/react-core/src/components/Select/SelectOption.js @@ -1,6 +1,7 @@ import React from 'react'; import styles from '@patternfly/patternfly/components/Select/select.css'; import { css } from '@patternfly/react-styles'; +import { CheckIcon } from '@patternfly/react-icons'; import PropTypes from 'prop-types'; import { SelectContext, KeyTypes } from './selectConstants'; import { CheckIcon } from '@patternfly/react-icons'; diff --git a/packages/patternfly-4/react-core/src/components/Select/SelectToggle.js b/packages/patternfly-4/react-core/src/components/Select/SelectToggle.js index d77e882ef31..09c13af7633 100644 --- a/packages/patternfly-4/react-core/src/components/Select/SelectToggle.js +++ b/packages/patternfly-4/react-core/src/components/Select/SelectToggle.js @@ -3,7 +3,7 @@ import styles from '@patternfly/patternfly/components/Select/select.css'; import { css } from '@patternfly/react-styles'; import PropTypes from 'prop-types'; import { CaretDownIcon } from '@patternfly/react-icons'; -import { KeyTypes } from './selectConstants'; +import { KeyTypes, SelectVariant } from './selectConstants'; const propTypes = { /** HTML ID of dropdown toggle */ @@ -32,8 +32,8 @@ const propTypes = { isPlain: PropTypes.bool, /** Type of the toggle button, defaults to 'button' */ type: PropTypes.string, - /** Flag for checkbox variant keyboard interaction */ - isCheckbox: PropTypes.bool, + /** Flag for variant, determines toggle rules and interaction */ + variant: PropTypes.oneOf(['single', 'checkbox', 'typeahead']), /** Additional props are spread to the container + ); } } diff --git a/packages/patternfly-4/react-core/src/components/Select/__snapshots__/Select.test.js.snap b/packages/patternfly-4/react-core/src/components/Select/__snapshots__/Select.test.js.snap index 37f669436f7..735662740d6 100644 --- a/packages/patternfly-4/react-core/src/components/Select/__snapshots__/Select.test.js.snap +++ b/packages/patternfly-4/react-core/src/components/Select/__snapshots__/Select.test.js.snap @@ -7,6 +7,7 @@ exports[`checkbox select renders checkbox select groups successfully 1`] = ` className="" isExpanded={true} isGrouped={true} + onClear={[Function]} onSelect={[MockFunction]} onToggle={[MockFunction]} placeholderText={null} @@ -27,7 +28,6 @@ exports[`checkbox select renders checkbox select groups successfully 1`] = ` className="" id="pf-toggle-id-4" isActive={false} - isCheckbox={true} isExpanded={true} isFocused={false} isHovered={false} @@ -37,6 +37,7 @@ exports[`checkbox select renders checkbox select groups successfully 1`] = ` onToggle={[MockFunction]} parentRef={null} type="button" + variant="checkbox" > + )} + + )} {variant === SelectVariant.single && isExpanded && ( )} - {variant === SelectVariant.typeahead && isExpanded && ( + {(variant === SelectVariant.typeahead || variant === SelectVariant.typeahead_multi) && isExpanded && ( { + this.setState({ + isExpanded + }); + }; + + 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: [], + isExpanded: false, + }); + }; + } + + render() { + const { isExpanded, selected } = this.state; + const titleId = 'title-id'; + + return ( +
    + + +
    + ); + } +} ``` \ No newline at end of file diff --git a/packages/patternfly-4/react-core/src/components/Select/Select.test.js b/packages/patternfly-4/react-core/src/components/Select/Select.test.js index ae28a8ea323..40799e70653 100644 --- a/packages/patternfly-4/react-core/src/components/Select/Select.test.js +++ b/packages/patternfly-4/react-core/src/components/Select/Select.test.js @@ -76,7 +76,7 @@ describe('typeahead select', () => { test('renders closed successfully', () => { const view = mount( ); expect(view).toMatchSnapshot(); @@ -85,7 +85,51 @@ describe('typeahead select', () => { test('renders expanded successfully', () => { const view = mount( + ); + expect(view).toMatchSnapshot(); + }); + + test('renders selected successfully', () => { + const view = mount( + + ); + expect(view).toMatchSnapshot(); + }); +}); + +describe('typeahead multi select', () => { + test('renders closed successfully', () => { + const view = mount( + + ); + expect(view).toMatchSnapshot(); + }); + + test('renders expanded successfully', () => { + const view = mount( + + ); + expect(view).toMatchSnapshot(); + }); + + test('renders selected successfully', () => { + const view = mount( + ); expect(view).toMatchSnapshot(); diff --git a/packages/patternfly-4/react-core/src/components/Select/SelectToggle.js b/packages/patternfly-4/react-core/src/components/Select/SelectToggle.js index 09c13af7633..0cac1d356e1 100644 --- a/packages/patternfly-4/react-core/src/components/Select/SelectToggle.js +++ b/packages/patternfly-4/react-core/src/components/Select/SelectToggle.js @@ -33,7 +33,7 @@ const propTypes = { /** Type of the toggle button, defaults to 'button' */ type: PropTypes.string, /** Flag for variant, determines toggle rules and interaction */ - variant: PropTypes.oneOf(['single', 'checkbox', 'typeahead']), + variant: PropTypes.oneOf(['single', 'checkbox', 'typeahead', 'typeaheadmulti']), /** Additional props are spread to the container + + +
+ + + +`; + +exports[`typeahead multi select renders selected successfully 1`] = ` + + + + + + + + + + + +
    + +
  • + +
  • +
    + +
  • + +
  • +
    + +
  • + +
  • +
    + +
  • + +
  • +
    +
+
+ + +`; + +exports[`typeahead select renders closed successfully 1`] = ` + + + + + + + + + + + +`; + +exports[`typeahead select renders expanded successfully 1`] = ` + + + + + + + + + + +
    + +
  • + +
  • +
    + +
  • + +
  • +
    + +
  • + +
  • +
    + +
  • + +
  • +
    +
+
+ + +`; + +exports[`typeahead select renders selected successfully 1`] = ` + + + + + + + + + + + +
    + +
  • + +
  • +
    + +
  • + +
  • +
    + +
  • + +
  • +
    + +
  • + +
  • +
diff --git a/packages/patternfly-4/react-core/src/components/Select/selectConstants.js b/packages/patternfly-4/react-core/src/components/Select/selectConstants.js index 3eefa470fe3..c6ebe237b2f 100644 --- a/packages/patternfly-4/react-core/src/components/Select/selectConstants.js +++ b/packages/patternfly-4/react-core/src/components/Select/selectConstants.js @@ -8,7 +8,8 @@ export const SelectContext = React.createContext({ export const SelectVariant = { single: 'single', checkbox: 'checkbox', - typeahead: 'typeahead' + typeahead: 'typeahead', + typeahead_multi: 'typeaheadmulti' }; export const KeyTypes = { From df966dae8f7fb69b2c82754a606b528ce7f5f4a9 Mon Sep 17 00:00:00 2001 From: Katie McFaul Date: Wed, 24 Apr 2019 15:21:30 -0400 Subject: [PATCH 04/11] fix(Select): add button wrapping to typeahead carat, update tests --- .../src/components/Select/SelectToggle.js | 8 +- .../Select/__snapshots__/Select.test.js.snap | 313 ++++++++++-------- 2 files changed, 176 insertions(+), 145 deletions(-) diff --git a/packages/patternfly-4/react-core/src/components/Select/SelectToggle.js b/packages/patternfly-4/react-core/src/components/Select/SelectToggle.js index 0cac1d356e1..44c50f01e53 100644 --- a/packages/patternfly-4/react-core/src/components/Select/SelectToggle.js +++ b/packages/patternfly-4/react-core/src/components/Select/SelectToggle.js @@ -1,5 +1,6 @@ import React, { Component } from 'react'; import styles from '@patternfly/patternfly/components/Select/select.css'; +import buttonStyles from '@patternfly/patternfly/components/Button/button.css'; import { css } from '@patternfly/react-styles'; import PropTypes from 'prop-types'; import { CaretDownIcon } from '@patternfly/react-icons'; @@ -157,7 +158,12 @@ class SelectToggle extends Component { onKeyDown={this.onKeyDown} > {children} - + {isTypeahead && ( + + )} + {!isTypeahead && } ); } diff --git a/packages/patternfly-4/react-core/src/components/Select/__snapshots__/Select.test.js.snap b/packages/patternfly-4/react-core/src/components/Select/__snapshots__/Select.test.js.snap index 53adec3f7f4..19e051a59ed 100644 --- a/packages/patternfly-4/react-core/src/components/Select/__snapshots__/Select.test.js.snap +++ b/packages/patternfly-4/react-core/src/components/Select/__snapshots__/Select.test.js.snap @@ -1115,33 +1115,37 @@ exports[`typeahead multi select renders closed successfully 1`] = ` value="" /> - - - - - + + + + + @@ -1210,33 +1214,37 @@ exports[`typeahead multi select renders expanded successfully 1`] = ` value="" /> - - - - - + + + + + - - - - - + + + + + - - - - - + + + + + @@ -1988,33 +2005,37 @@ exports[`typeahead select renders expanded successfully 1`] = ` value="" /> - - - - - + + + + + - - - - - + + + + + Date: Wed, 24 Apr 2019 15:49:04 -0400 Subject: [PATCH 05/11] feat(Select): prevent space from exiting input field for multiTA, edit regex parse with MDN escape s --- .../patternfly-4/react-core/src/components/Select/Select.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/patternfly-4/react-core/src/components/Select/Select.js b/packages/patternfly-4/react-core/src/components/Select/Select.js index 7fabec1a13a..10bb03f6888 100644 --- a/packages/patternfly-4/react-core/src/components/Select/Select.js +++ b/packages/patternfly-4/react-core/src/components/Select/Select.js @@ -91,7 +91,7 @@ class Select extends React.Component { try { input = new RegExp(e.target.value, 'i'); } catch (err) { - input = new RegExp(e.target.value.replace(/[^a-z0-9áéíóúñü \.,_-]/gim, ''), 'i'); + input = new RegExp(e.target.value.replace(/[.*+?^${}()|[\]\\]/g, '\\$&'), 'i'); } this.setState({ typeaheadValue: e.target.value, From e8a3b633e84967cee256c2242860850137cfb4ae Mon Sep 17 00:00:00 2001 From: Katie McFaul Date: Wed, 24 Apr 2019 17:07:20 -0400 Subject: [PATCH 06/11] feat(Select): rename multi variant, add onChange test, prevent toggle on click for TA variants, clea --- .../components/Select/CheckboxSelectInput.js | 70 ---- .../Select/GroupedCheckboxSelectInput.js | 78 ---- .../src/components/Select/Select.d.ts | 4 +- .../src/components/Select/Select.js | 22 +- .../src/components/Select/Select.md | 2 +- .../src/components/Select/Select.test.js | 44 ++- .../src/components/Select/SelectToggle.js | 12 +- .../Select/__snapshots__/Select.test.js.snap | 346 +++++++++++++++++- .../src/components/Select/selectConstants.js | 2 +- 9 files changed, 395 insertions(+), 185 deletions(-) delete mode 100644 packages/patternfly-4/react-core/src/components/Select/CheckboxSelectInput.js delete mode 100644 packages/patternfly-4/react-core/src/components/Select/GroupedCheckboxSelectInput.js diff --git a/packages/patternfly-4/react-core/src/components/Select/CheckboxSelectInput.js b/packages/patternfly-4/react-core/src/components/Select/CheckboxSelectInput.js deleted file mode 100644 index 09febe47119..00000000000 --- a/packages/patternfly-4/react-core/src/components/Select/CheckboxSelectInput.js +++ /dev/null @@ -1,70 +0,0 @@ -import React from 'react'; -import { Select, SelectVariant, CheckboxSelectOption } from '@patternfly/react-core'; - -class CheckboxSelectInput extends React.Component { - state = { - isExpanded: false, - selected: [] - }; - - onToggle = isExpanded => { - this.setState({ - isExpanded - }); - }; - - 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) - ); - } - }; - - clearSelection = () => { - this.setState({ - selected: [] - }); - }; - - options = [ - , - , - , - , - - ]; - - render() { - const { isExpanded, selected } = this.state; - const titleId = 'checkbox-select-id'; - return ( -
- - -
- ); - } -} - -export default CheckboxSelectInput; diff --git a/packages/patternfly-4/react-core/src/components/Select/GroupedCheckboxSelectInput.js b/packages/patternfly-4/react-core/src/components/Select/GroupedCheckboxSelectInput.js deleted file mode 100644 index 2da1cc9e3b5..00000000000 --- a/packages/patternfly-4/react-core/src/components/Select/GroupedCheckboxSelectInput.js +++ /dev/null @@ -1,78 +0,0 @@ -import React from 'react'; -import { Select, SelectVariant, CheckboxSelectGroup, CheckboxSelectOption } from '@patternfly/react-core'; - -class GroupedCheckboxSelectInput extends React.Component { - state = { - isExpanded: false, - selected: [] - }; - - onToggle = isExpanded => { - this.setState({ - isExpanded - }); - }; - - 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) - ); - } - }; - - clearSelection = () => { - this.setState({ - selected: [] - }); - }; - - options = [ - - - - - - - , - - - - - - ]; - - render() { - const { isExpanded, selected } = this.state; - const titleId = 'grouped-checkbox-select-id'; - return ( -
- - -
- ); - } -} - -export default GroupedCheckboxSelectInput; diff --git a/packages/patternfly-4/react-core/src/components/Select/Select.d.ts b/packages/patternfly-4/react-core/src/components/Select/Select.d.ts index 2500cf7dfa5..d6f0abca0c3 100644 --- a/packages/patternfly-4/react-core/src/components/Select/Select.d.ts +++ b/packages/patternfly-4/react-core/src/components/Select/Select.d.ts @@ -4,14 +4,14 @@ export const SelectVariant: { single: 'single'; checkbox: 'checkbox'; typeahead: 'typeahead'; - typeahead_multi: 'typeaheadmulti'; + typeaheadMulti: 'typeaheadmulti'; }; export interface SelectProps extends HTMLProps { isExpanded?: boolean; isGrouped?: boolean; onToggle(value: boolean): void; - onClear() : void; + onClear?() : void; placeholderText?: string | ReactNode; selections?: string | Array; variant?: string; diff --git a/packages/patternfly-4/react-core/src/components/Select/Select.js b/packages/patternfly-4/react-core/src/components/Select/Select.js index 10bb03f6888..12551d3240d 100644 --- a/packages/patternfly-4/react-core/src/components/Select/Select.js +++ b/packages/patternfly-4/react-core/src/components/Select/Select.js @@ -73,17 +73,11 @@ class Select extends React.Component { }; onClose = () => { - if (this.props.variant === SelectVariant.typeahead_multi) { - this.setState({ - openedOnEnter: false - }); - } else { - this.setState({ - openedOnEnter: false, - typeaheadValue: null, - filteredChildren: this.props.children - }); - } + this.setState({ + openedOnEnter: false, + typeaheadValue: null, + filteredChildren: this.props.children + }); }; onChange = e => { @@ -140,7 +134,7 @@ class Select extends React.Component { (childPlaceholder[0] && childPlaceholder[0].props.value) || (children[0] && children[0].props.value); } let selectedChips = null; - if (variant === SelectVariant.typeahead_multi) { + if (variant === SelectVariant.typeaheadMulti) { selectedChips = ( {selections && @@ -216,7 +210,7 @@ class Select extends React.Component { )} )} - {variant === SelectVariant.typeahead_multi && ( + {variant === SelectVariant.typeaheadMulti && (
{selections && selections.length > 0 && selectedChips} @@ -267,7 +261,7 @@ class Select extends React.Component { {children} )} - {(variant === SelectVariant.typeahead || variant === SelectVariant.typeahead_multi) && isExpanded && ( + {(variant === SelectVariant.typeahead || variant === SelectVariant.typeaheadMulti) && isExpanded && ( + {selectOptions} + + ); + const inst = view.instance(); + inst.onChange(mockEvent); + view.update(); + expect(view).toMatchSnapshot(); + }); }); describe('typeahead multi select', () => { test('renders closed successfully', () => { const view = mount( - {selectOptions} ); @@ -113,7 +132,7 @@ describe('typeahead multi select', () => { test('renders expanded successfully', () => { const view = mount( - {selectOptions} ); @@ -123,7 +142,7 @@ describe('typeahead multi select', () => { test('renders selected successfully', () => { const view = mount( + {selectOptions} + + ); + const inst = view.instance(); + inst.onChange(mockEvent); + view.update(); + expect(view).toMatchSnapshot(); + }); }); describe('API', () => { diff --git a/packages/patternfly-4/react-core/src/components/Select/SelectToggle.js b/packages/patternfly-4/react-core/src/components/Select/SelectToggle.js index 44c50f01e53..b443aade5dc 100644 --- a/packages/patternfly-4/react-core/src/components/Select/SelectToggle.js +++ b/packages/patternfly-4/react-core/src/components/Select/SelectToggle.js @@ -98,7 +98,8 @@ class SelectToggle extends Component { (event.key === KeyTypes.Tab && variant === SelectVariant.checkbox) || (event.key === KeyTypes.Tab && !isExpanded) || (event.key !== KeyTypes.Enter && event.key !== KeyTypes.Space) || - (event.key === KeyTypes.Space && variant === SelectVariant.typeahead) + (event.key === KeyTypes.Space && + (variant === SelectVariant.typeahead || variant === SelectVariant.typeaheadMulti)) ) return; event.preventDefault(); @@ -130,7 +131,7 @@ class SelectToggle extends Component { type, ...props } = this.props; - const isTypeahead = variant === SelectVariant.typeahead || variant === SelectVariant.typeahead_multi; + const isTypeahead = variant === SelectVariant.typeahead || variant === SelectVariant.typeaheadMulti; const ToggleComponent = isTypeahead ? 'div' : 'button'; return ( { - onToggle && onToggle(!isExpanded); - if (isExpanded) onClose && onClose(); + if (isTypeahead) onToggle && onToggle(true); + else { + onToggle && onToggle(!isExpanded); + if (isExpanded) onClose && onClose(); + } }} aria-expanded={isExpanded} aria-haspopup={(variant !== SelectVariant.checkbox && 'listbox') || null} diff --git a/packages/patternfly-4/react-core/src/components/Select/__snapshots__/Select.test.js.snap b/packages/patternfly-4/react-core/src/components/Select/__snapshots__/Select.test.js.snap index 19e051a59ed..17d533f5235 100644 --- a/packages/patternfly-4/react-core/src/components/Select/__snapshots__/Select.test.js.snap +++ b/packages/patternfly-4/react-core/src/components/Select/__snapshots__/Select.test.js.snap @@ -1077,9 +1077,9 @@ exports[`typeahead multi select renders closed successfully 1`] = ` } > `; +exports[`typeahead multi select test onChange 1`] = ` + +
+ + +
    + + } + type="button" + variant="typeahead" + > +
    +
    + +
    + +
    + + +
      + + + +`; + exports[`typeahead select renders closed successfully 1`] = ` `; + +exports[`typeahead select test onChange 1`] = ` + + + + +
        + + } + type="button" + variant="typeahead" + > +
        +
        + +
        + +
        + + +
          + + + +`; diff --git a/packages/patternfly-4/react-core/src/components/Select/selectConstants.js b/packages/patternfly-4/react-core/src/components/Select/selectConstants.js index c6ebe237b2f..91571e2d238 100644 --- a/packages/patternfly-4/react-core/src/components/Select/selectConstants.js +++ b/packages/patternfly-4/react-core/src/components/Select/selectConstants.js @@ -9,7 +9,7 @@ export const SelectVariant = { single: 'single', checkbox: 'checkbox', typeahead: 'typeahead', - typeahead_multi: 'typeaheadmulti' + typeaheadMulti: 'typeaheadmulti' }; export const KeyTypes = { From 9ff50c5ea832f631e37a1e16fe27f833f846033a Mon Sep 17 00:00:00 2001 From: Katie McFaul Date: Wed, 24 Apr 2019 17:17:17 -0400 Subject: [PATCH 07/11] feat(Select): add ariaLabelTypeAhead, update labels on examples --- .../src/components/Select/Select.js | 10 +++-- .../src/components/Select/Select.md | 8 ++-- .../Select/__snapshots__/Select.test.js.snap | 37 ++++++++++++------- 3 files changed, 35 insertions(+), 20 deletions(-) diff --git a/packages/patternfly-4/react-core/src/components/Select/Select.js b/packages/patternfly-4/react-core/src/components/Select/Select.js index 12551d3240d..3b670716799 100644 --- a/packages/patternfly-4/react-core/src/components/Select/Select.js +++ b/packages/patternfly-4/react-core/src/components/Select/Select.js @@ -32,6 +32,8 @@ const propTypes = { 'aria-label': PropTypes.string, /** Id of label for the Select aria-labelledby */ ariaLabelledBy: PropTypes.string, + /** Label for input field of type ahead select variants */ + ariaLabelTypeAhead: PropTypes.string, /** Callback for selection behavior */ onSelect: PropTypes.func.isRequired, /** Callback for toggle button behavior */ @@ -53,6 +55,7 @@ const defaultProps = { isGrouped: false, 'aria-label': null, ariaLabelledBy: null, + ariaLabelTypeAhead: null, selections: null, placeholderText: null, variant: SelectVariant.single, @@ -120,6 +123,7 @@ class Select extends React.Component { isGrouped, selections, ariaLabelledBy, + ariaLabelTypeAhead, 'aria-label': ariaLabel, placeholderText, width, @@ -189,7 +193,7 @@ class Select extends React.Component { 0 && selectedChips} Date: Wed, 24 Apr 2019 17:24:33 -0400 Subject: [PATCH 08/11] feat(Select): prevent open on enter for typeahead --- .../react-core/src/components/Select/SelectToggle.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/patternfly-4/react-core/src/components/Select/SelectToggle.js b/packages/patternfly-4/react-core/src/components/Select/SelectToggle.js index b443aade5dc..381ece004fc 100644 --- a/packages/patternfly-4/react-core/src/components/Select/SelectToggle.js +++ b/packages/patternfly-4/react-core/src/components/Select/SelectToggle.js @@ -98,7 +98,7 @@ class SelectToggle extends Component { (event.key === KeyTypes.Tab && variant === SelectVariant.checkbox) || (event.key === KeyTypes.Tab && !isExpanded) || (event.key !== KeyTypes.Enter && event.key !== KeyTypes.Space) || - (event.key === KeyTypes.Space && + ((event.key === KeyTypes.Space || event.key === KeyTypes.Enter) && (variant === SelectVariant.typeahead || variant === SelectVariant.typeaheadMulti)) ) return; From 0407f42ecac5d669933f4048d0a7fc34005c205b Mon Sep 17 00:00:00 2001 From: Katie McFaul Date: Thu, 25 Apr 2019 10:43:31 -0400 Subject: [PATCH 09/11] feat(Select): add aria params, control where toggle aria params are assigned, add no results display --- .../src/components/Select/Select.d.ts | 4 + .../src/components/Select/Select.js | 31 ++- .../src/components/Select/Select.md | 6 +- .../src/components/Select/SelectToggle.js | 29 ++- .../Select/__snapshots__/Select.test.js.snap | 233 +++++++++++++----- .../__snapshots__/SelectToggle.test.js.snap | 12 + 6 files changed, 240 insertions(+), 75 deletions(-) diff --git a/packages/patternfly-4/react-core/src/components/Select/Select.d.ts b/packages/patternfly-4/react-core/src/components/Select/Select.d.ts index d6f0abca0c3..e7e0a6278d8 100644 --- a/packages/patternfly-4/react-core/src/components/Select/Select.d.ts +++ b/packages/patternfly-4/react-core/src/components/Select/Select.d.ts @@ -17,6 +17,10 @@ export interface SelectProps extends HTMLProps { variant?: string; width?: string | number; ariaLabelledBy?: string; + ariaLabelTypeAhead?: string; + ariaLabelClear?: string; + ariaLabelToggle?: string; + ariaLabelRemove?: string; } declare const Select: React.FunctionComponent; diff --git a/packages/patternfly-4/react-core/src/components/Select/Select.js b/packages/patternfly-4/react-core/src/components/Select/Select.js index 3b670716799..70b896cd0a4 100644 --- a/packages/patternfly-4/react-core/src/components/Select/Select.js +++ b/packages/patternfly-4/react-core/src/components/Select/Select.js @@ -34,6 +34,12 @@ const propTypes = { ariaLabelledBy: PropTypes.string, /** Label for input field of type ahead select variants */ ariaLabelTypeAhead: PropTypes.string, + /** Label for clear selection button of type ahead select variants */ + ariaLabelClear: PropTypes.string, + /** Label for toggle of select variants */ + ariaLabelToggle: PropTypes.string, + /** Label for remove chip button of multiple type ahead select variant */ + ariaLabelRemove: PropTypes.string, /** Callback for selection behavior */ onSelect: PropTypes.func.isRequired, /** Callback for toggle button behavior */ @@ -56,6 +62,9 @@ const defaultProps = { 'aria-label': null, ariaLabelledBy: null, ariaLabelTypeAhead: null, + ariaLabelClear: null, + ariaLabelToggle: null, + ariaLabelRemove: null, selections: null, placeholderText: null, variant: SelectVariant.single, @@ -90,12 +99,16 @@ class Select extends React.Component { } catch (err) { input = new RegExp(e.target.value.replace(/[.*+?^${}()|[\]\\]/g, '\\$&'), 'i'); } + const filteredChildren = + e.target.value !== '' + ? this.props.children.filter(child => child.props.value.search(input) === 0) + : this.props.children; + if (filteredChildren.length === 0) { + filteredChildren.push(
          No results found
          ); + } this.setState({ typeaheadValue: e.target.value, - filteredChildren: - e.target.value !== '' - ? this.props.children.filter(child => child.props.value.search(input) === 0) - : this.props.children + filteredChildren }); }; @@ -124,6 +137,9 @@ class Select extends React.Component { selections, ariaLabelledBy, ariaLabelTypeAhead, + ariaLabelClear, + ariaLabelToggle, + ariaLabelRemove, 'aria-label': ariaLabel, placeholderText, width, @@ -143,7 +159,7 @@ class Select extends React.Component { {selections && selections.map(item => ( - onSelect(e, item)}> + onSelect(e, item)} closeBtnAriaLabel={ariaLabelRemove}> {item} ))} @@ -165,8 +181,9 @@ class Select extends React.Component { onToggle={onToggle} onEnter={this.onEnter} onClose={this.onClose} - aria-labelledby={`${ariaLabelledBy} ${selectToggleId}`} + ariaLabelledBy={`${ariaLabelledBy} ${selectToggleId}`} variant={variant} + ariaLabelToggle={ariaLabelToggle} > {variant === SelectVariant.single && (
          @@ -207,7 +224,7 @@ class Select extends React.Component { this.clearSelection(e); onClear && onClear(e); }} - aria-label="Clear all" + aria-label={ariaLabelClear} > diff --git a/packages/patternfly-4/react-core/src/components/Select/Select.md b/packages/patternfly-4/react-core/src/components/Select/Select.md index 35767ec8325..2d1e74ee19c 100644 --- a/packages/patternfly-4/react-core/src/components/Select/Select.md +++ b/packages/patternfly-4/react-core/src/components/Select/Select.md @@ -293,7 +293,7 @@ class TypeaheadSelectInput extends React.Component { render() { const { isExpanded, selected } = this.state; - const titleId = 'title-id'; + const titleId = 'typeahead-select-id'; return (