From ea427bd1daf6e3ca58a78456b73c3ab57d6a9062 Mon Sep 17 00:00:00 2001 From: Tim van der Horst Date: Tue, 8 Sep 2020 17:09:15 +1000 Subject: [PATCH] Add a new `blurBehavior` of `add-or-clear` `add-or-clear` is similar to `add`, but clears the input if `onBeforeAdd` returns `false` --- README.md | 2 +- src/ChipInput.js | 17 +++++++++++++---- src/ChipInput.spec.js | 34 ++++++++++++++++++++++++++++++++++ stories/index.js | 6 ++++++ typings/index.d.ts | 2 +- 5 files changed, 55 insertions(+), 6 deletions(-) diff --git a/README.md b/README.md index cd1bb88..3dde92a 100644 --- a/README.md +++ b/README.md @@ -40,7 +40,7 @@ import ChipInput from 'material-ui-chip-input' |---|---|---|---| |allowDuplicates|`bool`|`false`|Allows duplicate chips if set to true.| |alwaysShowPlaceholder|`bool`||If true, the placeholder will always be visible.| -|blurBehavior|`enum`|`'clear'`|Behavior when the chip input is blurred: `'clear'` clears the input, `'add'` creates a chip and `'ignore'` keeps the input.| +|blurBehavior|`enum`|`'clear'`|Behavior when the chip input is blurred: `'clear'` clears the input, `'add'` creates a chip, `'add-or-clear'` either creates a chip or clears the input on failure (see: `onBeforeAdd`), and `'ignore'` keeps the input.| |chipRenderer|`func`||A function of the type `({ value, text, chip, isFocused, isDisabled, isReadOnly, handleClick, handleDelete, className }, key) => node` that returns a chip based on the given properties. This can be used to customize chip styles. Each item in the `dataSource` array will be passed to `chipRenderer` as arguments `chip`, `value` and `text`. If `dataSource` is an array of objects and `dataSourceConfig` is present, then `value` and `text` will instead correspond to the object values defined in `dataSourceConfig`. If `dataSourceConfig` is not set and `dataSource` is an array of objects, then a custom `chipRenderer` must be set. `chip` is always the raw value from `dataSource`, either an object or a string.| |clearInputValueOnChange|`bool`|`false`|Whether the input value should be cleared if the `value` prop is changed.| |dataSource|`array`||Data source for auto complete. This should be an array of strings or objects.| diff --git a/src/ChipInput.js b/src/ChipInput.js index d64fb30..e5063ae 100644 --- a/src/ChipInput.js +++ b/src/ChipInput.js @@ -263,7 +263,11 @@ class ChipInput extends React.Component { this.setState({ focusedChip: null }) } const value = event.target.value + let addChipOptions switch (this.props.blurBehavior) { + case 'add-or-clear': + addChipOptions = { clearInputOnFail: true } + // falls through case 'add': if (this.props.delayBeforeAdd) { // Lets assume that we only want to add the existing content as chip, when @@ -273,13 +277,13 @@ class ChipInput extends React.Component { this.inputBlurTimeout = setTimeout(() => { const numChipsAfter = (this.props.value || this.state.chips).length if (numChipsBefore === numChipsAfter) { - this.handleAddChip(value) + this.handleAddChip(value, addChipOptions) } else { this.clearInput() } }, 150) } else { - this.handleAddChip(value) + this.handleAddChip(value, addChipOptions) } break case 'clear': @@ -385,11 +389,16 @@ class ChipInput extends React.Component { /** * Handles adding a chip. * @param {string|object} chip Value of the chip, either a string or an object (if dataSourceConfig is set) + * @param {object=} options Additional options + * @param {boolean=} options.clearInputOnFail If `true`, and `onBeforeAdd` returns `false`, clear the input * @returns True if the chip was added (or at least `onAdd` was called), false if adding the chip was prevented */ - handleAddChip (chip) { + handleAddChip (chip, options) { if (this.props.onBeforeAdd && !this.props.onBeforeAdd(chip)) { this._preventChipCreation = true + if (options != null && options.clearInputOnFail) { + this.clearInput() + } return false } this.clearInput() @@ -651,7 +660,7 @@ ChipInput.propTypes = { /** If true, the placeholder will always be visible. */ alwaysShowPlaceholder: PropTypes.bool, /** Behavior when the chip input is blurred: `'clear'` clears the input, `'add'` creates a chip and `'ignore'` keeps the input. */ - blurBehavior: PropTypes.oneOf(['clear', 'add', 'ignore']), + blurBehavior: PropTypes.oneOf(['clear', 'add', 'add-or-clear', 'ignore']), /** A function of the type `({ value, text, chip, isFocused, isDisabled, isReadOnly, handleClick, handleDelete, className }, key) => node` that returns a chip based on the given properties. This can be used to customize chip styles. Each item in the `dataSource` array will be passed to `chipRenderer` as arguments `chip`, `value` and `text`. If `dataSource` is an array of objects and `dataSourceConfig` is present, then `value` and `text` will instead correspond to the object values defined in `dataSourceConfig`. If `dataSourceConfig` is not set and `dataSource` is an array of objects, then a custom `chipRenderer` must be set. `chip` is always the raw value from `dataSource`, either an object or a string. */ chipRenderer: PropTypes.func, /** Whether the input value should be cleared if the `value` prop is changed. */ diff --git a/src/ChipInput.spec.js b/src/ChipInput.spec.js index 5c5bd6c..462a17a 100644 --- a/src/ChipInput.spec.js +++ b/src/ChipInput.spec.js @@ -545,6 +545,40 @@ describe('blurBehavior modes', () => { tree.update() expect(tree.find('Chip').map((chip) => chip.text())).toEqual(['a', 'b', 'blur']) }) + + it('adds the input on blur with blurBehavior set to add add-or-clear, if onBeforeAdd passes', () => { + const handleChange = jest.fn() + const tree = mount( + value === 'c'} + /> + ) + tree.find('input').getDOMNode().value = 'c' + tree.find('input').simulate('blur') + + expect(tree.find('input').getDOMNode().value).toBe('') + + expect(handleChange.mock.calls[0][0]).toEqual(['a', 'b', 'c']) + + tree.update() + expect(tree.find('Chip').map((chip) => chip.text())).toEqual(['a', 'b', 'c']) + }) + + it('clears the input on blur with blurBehavior set to add-or-clear, if onBeforeAdd fails', () => { + const tree = mount( + value === 'c'} + /> + ) + tree.find('input').simulate('change', { target: { value: 'foo' } }) + tree.find('input').simulate('blur') + expect(tree.find('input').getDOMNode().value).toBe('') + }) }) describe('keys', () => { diff --git a/stories/index.js b/stories/index.js index 355e8d9..d2d3ebd 100644 --- a/stories/index.js +++ b/stories/index.js @@ -211,6 +211,12 @@ storiesOf('ChipInput', module) ) */ .add('add text input value on blur', () => ) + .add('add number input value on blur, or clear input if not a number', () => + !Number.isNaN(parseFloat(value))} + /> + ) .add('clear text input value on blur ', () => ( )) diff --git a/typings/index.d.ts b/typings/index.d.ts index 96b44da..1b99f31 100644 --- a/typings/index.d.ts +++ b/typings/index.d.ts @@ -30,7 +30,7 @@ type Omit = Pick>; export interface BaseTextFieldProps extends Omit { allowDuplicates?: boolean; alwaysShowPlaceholder?: boolean; - blurBehavior?: 'clear' | 'add' | 'ignore'; + blurBehavior?: 'clear' | 'add' | 'add-or-clear' | 'ignore'; chipRenderer?: ChipRenderer; classes?: Record; clearInputValueOnChange?: boolean;