Skip to content

Commit 2ae9611

Browse files
authored
InputChips: trim before validate, no comma if pasting (#505)
1 parent 6b99c43 commit 2ae9611

File tree

3 files changed

+49
-17
lines changed

3 files changed

+49
-17
lines changed

CHANGELOG.md

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,16 @@ All notable changes to this project will be documented in this file.
55
The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/),
66
and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
77

8+
## [UNRELEASED]
9+
10+
### Added
11+
12+
- `InputChips` now immediately converts pasted content to chips instead of waiting for a comma as it does when the user is typing.
13+
14+
### Fixed
15+
16+
- `InputChips` now trims whitespace from each value before calling `validate`.
17+
818
## [0.7.16] - 2020-01-30
919

1020
### Fixed

packages/components/src/Form/Inputs/InputChips/InputChips.test.tsx

Lines changed: 20 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -51,17 +51,27 @@ test('values are added when a comma is last character entered', () => {
5151
<InputChips onChange={onChangeMock} values={[]} placeholder="type here" />
5252
)
5353
const input = getByPlaceholderText('type here')
54-
// values not yet added if user pastes comma separated list
55-
fireEvent.change(input, { target: { value: 'tag1,tag2' } })
56-
expect(onChangeMock).not.toHaveBeenCalled()
5754

58-
// if the last character is a comma, values are added
59-
fireEvent.change(input, { target: { value: 'tag1,tag2,' } })
55+
// if the last character entered is a comma, values are added
56+
fireEvent.change(input, { target: { value: 'tag1,' } })
6057
expect(onChangeMock).toHaveBeenCalledTimes(1)
61-
expect(onChangeMock).toHaveBeenCalledWith(['tag1', 'tag2'])
58+
expect(onChangeMock).toHaveBeenCalledWith(['tag1'])
6259
expect(input).toHaveValue('')
6360
})
6461

62+
test('values are added when pasting', () => {
63+
const onChangeMock = jest.fn()
64+
const { getByPlaceholderText } = renderWithTheme(
65+
<InputChips onChange={onChangeMock} values={[]} placeholder="type here" />
66+
)
67+
const input = getByPlaceholderText('type here')
68+
fireEvent.paste(input)
69+
// If a paste is detected before the value change
70+
// no need for the last character to be a comma
71+
fireEvent.change(input, { target: { value: 'tag1, tag2' } })
72+
expect(onChangeMock).toHaveBeenCalledWith(['tag1', 'tag2'])
73+
})
74+
6575
test('values are added on blur', () => {
6676
const onChangeMock = jest.fn()
6777
const { getByPlaceholderText } = renderWithTheme(
@@ -151,7 +161,8 @@ test('new values are validated', () => {
151161
expect(input).toHaveValue('tag2')
152162
expect(onInvalidMock).toHaveBeenCalledWith(['tag2'])
153163

154-
fireEvent.change(input, { target: { value: 'tag1,' } })
164+
// value should be trimmed before validation
165+
fireEvent.change(input, { target: { value: ' tag1,' } })
155166
expect(onChangeMock).toHaveBeenCalledTimes(1)
156167
expect(onChangeMock).toHaveBeenCalledWith(['tag1'])
157168
expect(input).toHaveValue('')
@@ -171,7 +182,8 @@ test('duplicate values are not added', () => {
171182
)
172183
const input = getByPlaceholderText('type here')
173184

174-
fireEvent.change(input, { target: { value: 'tag1,' } })
185+
// value should be trimmed before validation
186+
fireEvent.change(input, { target: { value: ' tag1,' } })
175187
expect(onChangeMock).toHaveBeenCalledTimes(0)
176188
expect(onDuplicateMock).toHaveBeenCalledWith(['tag1'])
177189
expect(input).toHaveValue('tag1')

packages/components/src/Form/Inputs/InputChips/InputChips.tsx

Lines changed: 19 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -63,16 +63,17 @@ function getUpdatedValues(
6363
// Values may be separated by ',' '\t' and ' '
6464
const inputValues: string[] = inputValue.split(/[,\t]+/)
6565
inputValues.forEach((val: string) => {
66-
if (val.trim() === '') return
66+
const trimmedValue = val.trim()
67+
if (trimmedValue === '') return
6768
// Make sure each value is valid and doesn't already exist
68-
if (validate && !validate(val)) {
69-
unusedValues.push(val)
70-
return invalidValues.push(val)
71-
} else if (currentValues && currentValues.includes(val)) {
72-
unusedValues.push(val)
73-
return duplicateValues.push(val)
69+
if (validate && !validate(trimmedValue)) {
70+
unusedValues.push(trimmedValue)
71+
return invalidValues.push(trimmedValue)
72+
} else if (currentValues && currentValues.includes(trimmedValue)) {
73+
unusedValues.push(trimmedValue)
74+
return duplicateValues.push(trimmedValue)
7475
} else {
75-
return validValues.push(val)
76+
return validValues.push(trimmedValue)
7677
}
7778
})
7879

@@ -164,11 +165,19 @@ export const InputChipsInternal = forwardRef(
164165
}
165166
}
166167

168+
const isPasting = React.useRef(false)
169+
function handlePaste() {
170+
isPasting.current = true
171+
}
172+
167173
function handleInputChange(e: FormEvent<HTMLInputElement>) {
168174
const { value } = e.currentTarget
169175
// If the last character is a comma, update the values
170-
if (value[value.length - 1] === ',') {
176+
// Or, if the user pastes content, we assume that the final value is complete
177+
// even if there's no comma at the end
178+
if (isPasting.current || value[value.length - 1] === ',') {
171179
updateValues(value)
180+
isPasting.current = false
172181
} else {
173182
setInputValue(value)
174183
}
@@ -199,6 +208,7 @@ export const InputChipsInternal = forwardRef(
199208
onKeyDown={handleKeyDown}
200209
showClear={values.length > 0}
201210
onClear={handleClear}
211+
onPaste={handlePaste}
202212
{...props}
203213
>
204214
{chips}

0 commit comments

Comments
 (0)