Skip to content

Commit e352abb

Browse files
committed
Add newOptionPosition to configure whether to append or prepend the new option to the listbox when allowNew is enabled, resolves #77
1 parent 4c4760a commit e352abb

File tree

4 files changed

+42
-3
lines changed

4 files changed

+42
-3
lines changed

readme.md

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -86,6 +86,7 @@ function CountrySelector() {
8686
- [`isDisabled`](#isDisabled-optional)
8787
- [`isInvalid`](#isInvalid-optional)
8888
- [`labelText`](#labelText-optional)
89+
- [`newOptionPosition`](#newOptionPosition-optional)
8990
- [`newOptionText`](#newOptionText-optional)
9091
- [`noOptionsText`](#noOptionsText-optional)
9192
- [`onAdd`](#onAdd-required)
@@ -196,6 +197,10 @@ Marks the input as invalid. When true this should be used along with the `ariaEr
196197

197198
The label text used to describe the component and input. _Please note_ that the label is visually hidden with CSS in the example code. Defaults to: `"Select tags"`.
198199

200+
#### newOptionPosition (optional)
201+
202+
The position of the option shown when the `allowNew` prop is enabled, either `"first"` or `"last"`. Defaults to `"last"`.
203+
199204
#### newOptionText (optional)
200205

201206
The option text shown when the `allowNew` prop is enabled. The placeholder `%value%` will be replaced by the current input value. Defaults to `"Add %value%"`.

src/components/ReactTags.tsx

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -80,6 +80,7 @@ type ReactTagsProps = {
8080
isDisabled?: boolean
8181
isInvalid?: boolean
8282
labelText?: string
83+
newOptionPosition?: 'first' | 'last'
8384
newOptionText?: string
8485
noOptionsText?: string
8586
onAdd: OnAdd
@@ -125,6 +126,7 @@ function ReactTags(
125126
isDisabled = false,
126127
isInvalid = false,
127128
labelText = 'Select tags',
129+
newOptionPosition = 'last',
128130
newOptionText = 'Add %value%',
129131
noOptionsText = 'No options found for %value%',
130132
onAdd,
@@ -162,6 +164,7 @@ function ReactTags(
162164
activateFirstOption,
163165
allowNew,
164166
collapseOnSelect,
167+
newOptionPosition,
165168
newOptionText,
166169
noOptionsText,
167170
onAdd,

src/hooks/useManager.ts

Lines changed: 15 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -42,6 +42,7 @@ export type ManagerProps = {
4242
activateFirstOption: boolean
4343
allowNew: boolean
4444
collapseOnSelect: boolean
45+
newOptionPosition: 'first' | 'last'
4546
newOptionText: string
4647
noOptionsText: string
4748
onAdd: OnAdd
@@ -66,6 +67,7 @@ export function useManager({
6667
activateFirstOption,
6768
allowNew,
6869
collapseOnSelect,
70+
newOptionPosition,
6971
newOptionText,
7072
noOptionsText,
7173
onAdd,
@@ -91,7 +93,9 @@ export function useManager({
9193

9294
if (value) {
9395
if (allowNew) {
94-
opts.push({
96+
const method = newOptionPosition === 'first' ? 'unshift' : 'push'
97+
98+
opts[method]({
9599
disabled: onValidate ? !onValidate(value) : false,
96100
label: newOptionText,
97101
value: NewOptionValue,
@@ -108,7 +112,16 @@ export function useManager({
108112
}
109113

110114
return opts
111-
}, [allowNew, newOptionText, noOptionsText, onValidate, suggestions, suggestionsTransform, value])
115+
}, [
116+
allowNew,
117+
newOptionPosition,
118+
newOptionText,
119+
noOptionsText,
120+
onValidate,
121+
suggestions,
122+
suggestionsTransform,
123+
value,
124+
])
112125

113126
const optionIndex = lastActiveOption ? findTagIndex(lastActiveOption, options) : -1
114127
const activeIndex = activateFirstOption ? Math.max(optionIndex, 0) : optionIndex

src/test/ReactTags.test.tsx

Lines changed: 19 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -760,7 +760,25 @@ describe('React Tags Autocomplete', () => {
760760

761761
it('displays the new tag option', async () => {
762762
await userEvent.type(harness.input, 'blah')
763-
expect(screen.queryByText('Add blah'))
763+
expect(harness.options[0].textContent).toBe('Add blah')
764+
})
765+
766+
it('displays new tag option text with configured text', async () => {
767+
harness.rerender({ newOptionText: 'Create "%value%"' })
768+
await userEvent.type(harness.input, 'blah')
769+
expect(harness.options[0].textContent).toBe('Create "blah"')
770+
})
771+
772+
it('displays the new tag option first in the option list', async () => {
773+
harness.rerender({ newOptionPosition: 'first', suggestions })
774+
await userEvent.type(harness.input, 'En')
775+
expect(harness.options[0].textContent).toBe('Add En')
776+
})
777+
778+
it('displays the new tag option last in the option list', async () => {
779+
harness.rerender({ newOptionPosition: 'last', suggestions })
780+
await userEvent.type(harness.input, 'En')
781+
expect(harness.options[harness.options.length - 1].textContent).toBe('Add En')
764782
})
765783

766784
it('does not highlight the new tag option text', async () => {

0 commit comments

Comments
 (0)