From 60e3241af9520668ba66fab1193e4af3bdf495d8 Mon Sep 17 00:00:00 2001 From: Greg Thompson Date: Fri, 29 Mar 2019 12:30:50 -0500 Subject: [PATCH] propagate options changes to consumer; update examples --- src-docs/src/views/selectable/data.js | 37 -------------- src-docs/src/views/selectable/selectable.tsx | 18 +++---- .../selectable/selectable_custom_render.js | 2 +- .../views/selectable/selectable_example.js | 40 ++++++++++++++- .../views/selectable/selectable_exclusion.tsx | 9 +--- .../views/selectable/selectable_search.tsx | 3 +- .../views/selectable/selectable_single.tsx | 38 ++++++++++++++ src/components/selectable/selectable.tsx | 7 +-- .../selectable_list/selectable_list.tsx | 51 +++++++++++++++---- 9 files changed, 133 insertions(+), 72 deletions(-) delete mode 100644 src-docs/src/views/selectable/data.js create mode 100644 src-docs/src/views/selectable/selectable_single.tsx diff --git a/src-docs/src/views/selectable/data.js b/src-docs/src/views/selectable/data.js deleted file mode 100644 index 440d0b5f4b1..00000000000 --- a/src-docs/src/views/selectable/data.js +++ /dev/null @@ -1,37 +0,0 @@ -export const Options = [ - { - label: 'Titan', - 'data-test-subj': 'titanOption', - }, - { - label: 'Enceladus is disabled', - disabled: true, - }, - { - label: 'Mimas', - checked: 'on', - }, - { - label: 'Dione', - }, - { - label: 'Iapetus', - checked: 'on', - }, - { - label: 'Phoebe', - }, - { - label: 'Rhea', - }, - { - label: - 'Pandora is one of Saturn\'s moons, named for a Titaness of Greek mythology', - }, - { - label: 'Tethys', - }, - { - label: 'Hyperion', - }, -]; diff --git a/src-docs/src/views/selectable/selectable.tsx b/src-docs/src/views/selectable/selectable.tsx index e2fe64788f7..6171a206e71 100644 --- a/src-docs/src/views/selectable/selectable.tsx +++ b/src-docs/src/views/selectable/selectable.tsx @@ -4,32 +4,28 @@ import { EuiSelectable } from '../../../../src/components/selectable'; import { Option } from '../../../../src/components/selectable/types'; import { Options } from './data'; -export default class extends Component<{}, { selectedOptions: Option[] }> { - options: Option[] = []; +export default class extends Component<{}, { options: Option[] }> { constructor(props: any) { super(props); - this.options = Options as Option[]; - const selectedOptions = this.options.filter(option => option.checked); - this.state = { - selectedOptions, + options: Options as Option[], }; } - onChange = (selectedOptions: Option[]) => { + onChange = (options: Option[]) => { this.setState({ - selectedOptions, + options, }); }; render() { - const { selectedOptions } = this.state; + const { options } = this.state; return ( this.onChange(selectedOptions)} + options={options} + onChange={this.onChange} listProps={{ bordered: true }}> {list => list} diff --git a/src-docs/src/views/selectable/selectable_custom_render.js b/src-docs/src/views/selectable/selectable_custom_render.js index 0c99fefba5d..00911c9db7f 100644 --- a/src-docs/src/views/selectable/selectable_custom_render.js +++ b/src-docs/src/views/selectable/selectable_custom_render.js @@ -74,7 +74,7 @@ export default class extends Component { this.onChange(options)} + onChange={this.onChange} {...customProps} > {(list, search) => ( diff --git a/src-docs/src/views/selectable/selectable_example.js b/src-docs/src/views/selectable/selectable_example.js index 68c2acb8551..e9c5f52fe17 100644 --- a/src-docs/src/views/selectable/selectable_example.js +++ b/src-docs/src/views/selectable/selectable_example.js @@ -1,5 +1,6 @@ import React from 'react'; import { Link } from 'react-router'; +import { Fragment } from 'react-is'; import { renderToHtml } from '../../services'; @@ -23,10 +24,13 @@ const selectablePopoverSource = require('!!raw-loader!./selectable_popover'); const selectablePopoverHtml = renderToHtml(SelectablePopover); import SelectableSearch from './selectable_search'; -import { Fragment } from 'react-is'; const selectableSearchSource = require('!!raw-loader!./selectable_search'); const selectableSearchHtml = renderToHtml(SelectableSearch); +import SelectableSingle from './selectable_single'; +const selectableSingleSource = require('!!raw-loader!./selectable_single'); +const selectableSingleHtml = renderToHtml(SelectableSingle); + import SelectableExclusion from './selectable_exclusion'; const selectableExclusionSource = require('!!raw-loader!./selectable_exclusion'); const selectableExclusionHtml = renderToHtml(SelectableExclusion); @@ -168,6 +172,40 @@ export const SelectableExample = { )} `, + }, + { + title: 'Single Selection', + source: [ + { + type: GuideSectionTypes.JS, + code: selectableSingleSource, + }, + { + type: GuideSectionTypes.HTML, + code: selectableSingleHtml, + }, + ], + text: ( + +

+ Selection can be restricted to a single option at a time with the singleSelection prop. + Passing true allows for 0 or 1 option to be selected, while + `always` requires 1 option to be selected at all times. + The default value is false. +

+
+ ), + props: { EuiSelectable }, + demo: , + snippet: ` + + {list => list} + + `, }, { title: 'Sizing and containers', diff --git a/src-docs/src/views/selectable/selectable_exclusion.tsx b/src-docs/src/views/selectable/selectable_exclusion.tsx index 4720adb21bd..39d6f796740 100644 --- a/src-docs/src/views/selectable/selectable_exclusion.tsx +++ b/src-docs/src/views/selectable/selectable_exclusion.tsx @@ -5,13 +5,11 @@ import { Option } from '../../../../src/components/selectable/types'; import { Options } from './data'; export default class extends Component<{}, { options: Option[] }> { - options: Option[] = []; constructor(props: any) { super(props); - this.options = Options as Option[]; this.state = { - options: this.options, + options: Options as Option[], }; } @@ -25,10 +23,7 @@ export default class extends Component<{}, { options: Option[] }> { const { options } = this.state; return ( - this.onChange(options)}> + {list => list} ); diff --git a/src-docs/src/views/selectable/selectable_search.tsx b/src-docs/src/views/selectable/selectable_search.tsx index f3657df84a8..0d780caa96f 100644 --- a/src-docs/src/views/selectable/selectable_search.tsx +++ b/src-docs/src/views/selectable/selectable_search.tsx @@ -5,7 +5,6 @@ import { Option } from '../../../../src/components/selectable/types'; import { Options } from './data'; export default class extends Component<{}, { options: Option[] }> { - options: Option[] = []; constructor(props: any) { super(props); @@ -30,7 +29,7 @@ export default class extends Component<{}, { options: Option[] }> { 'data-test-subj': 'selectableSearchHere', }} options={options} - onChange={() => this.onChange(options)}> + onChange={this.onChange}> {(list, search) => ( {search} diff --git a/src-docs/src/views/selectable/selectable_single.tsx b/src-docs/src/views/selectable/selectable_single.tsx new file mode 100644 index 00000000000..bcb4a80a238 --- /dev/null +++ b/src-docs/src/views/selectable/selectable_single.tsx @@ -0,0 +1,38 @@ +import React, { Component } from 'react'; + +import { EuiSelectable } from '../../../../src/components/selectable'; +import { Option } from '../../../../src/components/selectable/types'; +import { Options } from './data'; + +export default class extends Component<{}, { options: Option[] }> { + constructor(props: any) { + super(props); + + this.state = { + options: Options.map(option => { + const { checked, ...checklessOption } = option; + return { ...checklessOption }; + }) as Option[], + }; + } + + onChange = (options: Option[]) => { + this.setState({ + options, + }); + }; + + render() { + const { options } = this.state; + + return ( + + {list => list} + + ); + } +} diff --git a/src/components/selectable/selectable.tsx b/src/components/selectable/selectable.tsx index abd4f492d91..1e5829b2b16 100644 --- a/src/components/selectable/selectable.tsx +++ b/src/components/selectable/selectable.tsx @@ -36,7 +36,7 @@ type EuiSelectableOptionsListPropsWithDefaults = RequiredEuiSelectableOptionsLis export type EuiSelectableProps = Omit< HTMLAttributes, - 'children' + 'children' | 'onChange' > & CommonProps & { /** @@ -240,9 +240,10 @@ export class EuiSelectable extends Component< }; onOptionClick = (options: Option[]) => { - this.setState({ + this.setState(state => ({ options, - }); + visibleOptions: getMatchingOptions(options, state.searchValue), + })); if (this.props.onChange) { this.props.onChange(options); } diff --git a/src/components/selectable/selectable_list/selectable_list.tsx b/src/components/selectable/selectable_list/selectable_list.tsx index 0773040c5f5..1761fc7cd8e 100644 --- a/src/components/selectable/selectable_list/selectable_list.tsx +++ b/src/components/selectable/selectable_list/selectable_list.tsx @@ -225,24 +225,55 @@ export class EuiSelectableList extends Component { private onAddOption = (addedOption: Option) => { const { onOptionClick, options, singleSelection } = this.props; - if (singleSelection) { - options.map(option => delete option.checked); - } - addedOption.checked = 'on'; - onOptionClick(options); + + const updatedOptions = options.map(option => { + // if singleSelection is enabled, uncheck any selected option(s) + const updatedOption = { ...option }; + if (singleSelection) { + delete updatedOption.checked; + } + + // if this is the now-selected option, check it + if (option === addedOption) { + updatedOption.checked = 'on'; + } + + return updatedOption; + }); + + onOptionClick(updatedOptions); }; private onRemoveOption = (removedOption: Option) => { const { onOptionClick, singleSelection, options } = this.props; - if (singleSelection !== 'always') { - delete removedOption.checked; - } - onOptionClick(options); + + const updatedOptions = options.map(option => { + const updatedOption = { ...option }; + + if (option === removedOption && singleSelection !== 'always') { + delete updatedOption.checked; + } + + return updatedOption; + }); + + onOptionClick(updatedOptions); }; private onExcludeOption = (excludedOption: Option) => { const { onOptionClick, options } = this.props; excludedOption.checked = 'off'; - onOptionClick(options); + + const updatedOptions = options.map(option => { + const updatedOption = { ...option }; + + if (option === excludedOption) { + updatedOption.checked = 'off'; + } + + return updatedOption; + }); + + onOptionClick(updatedOptions); }; }