Skip to content

Commit 567f59e

Browse files
authored
Merge pull request #1463 from privy-open-source/fix/asyncronous-select-option
fix(select): fix selected values when option items updated after search
2 parents 01e47c7 + 3ea3e30 commit 567f59e

File tree

3 files changed

+78
-17
lines changed

3 files changed

+78
-17
lines changed

src/components/select/Select.spec.ts

Lines changed: 38 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -476,7 +476,7 @@ it('should have clear button if prop `clearable` was provided', async () => {
476476

477477
await fireEvent.click(clear)
478478

479-
expect(model.value).toBeUndefined()
479+
expect(model.value).toStrictEqual({ text: '', value: undefined })
480480
})
481481

482482
it('should clear search keyword if click clear button when select was opened', async () => {
@@ -595,3 +595,40 @@ it('should show selected items when options updated', async () => {
595595

596596
expect(activator).toHaveTextContent('orange')
597597
})
598+
599+
it('should be keep selected when option changed after search', async () => {
600+
const options = ref<string[]>([
601+
'apple',
602+
'grape',
603+
'orange',
604+
])
605+
const model = ref('apple')
606+
const screen = render({
607+
components: { Select },
608+
template : `
609+
<Select
610+
v-model="model"
611+
:options="options"
612+
/>
613+
`,
614+
setup () {
615+
return { model, options }
616+
},
617+
})
618+
619+
const activator = screen.queryByTestId('select-activator')
620+
621+
expect(activator).toHaveTextContent('apple')
622+
623+
const select = screen.queryByTestId('select')
624+
const caret = screen.queryByTestId('select-caret-icon')
625+
626+
await fireEvent.click(caret)
627+
628+
expect(select).toHaveClass('select--open')
629+
630+
await fireEvent.update(screen.queryByTestId('select-search'), 'orange')
631+
await nextTick()
632+
633+
expect(activator).toHaveTextContent('apple')
634+
})

src/components/select/Select.vue

Lines changed: 21 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -190,7 +190,7 @@ import { isEqual } from '../utils/value'
190190
import { onStartTyping, watchPausable } from '@vueuse/core'
191191
import type { SizeVariant } from '../button'
192192
import type { MenuSizeVariant } from '../dropdown/'
193-
import { isNil } from 'lodash-es'
193+
import { isNil, uniqBy } from 'lodash-es'
194194
195195
defineOptions({
196196
models: {
@@ -377,25 +377,28 @@ const modelWatcher = watchPausable(() => props.modelValue, (value) => {
377377
378378
watch(items, (options) => {
379379
if (props.modelValue && options.length > 0) {
380-
localModel.value = props.multiple
380+
const value = props.multiple
381381
? filterSelected(options, props.modelValue as unknown[])
382382
: findSelected(options, props.modelValue)
383+
384+
localModel.value = props.multiple
385+
? uniqBy([...localModel.value as SelectItem[], ...value as SelectItem[]], 'value')
386+
: ((value as SelectItem).value === undefined ? localModel.value as SelectItem : value as SelectItem)
383387
}
384388
})
385389
386390
function setValue (item?: SelectItem) {
387-
let value: SelectItem | SelectItem[]
388-
389-
if (props.multiple) {
390-
if (item) {
391-
if (Array.isArray(localModel.value)) {
392-
value = localModel.value.some((val) => isEqual(val.value, item.value))
393-
? localModel.value.filter((val) => !isEqual(val.value, item.value))
394-
: [...localModel.value, item]
395-
}
396-
} else
397-
value = []
398-
} else
391+
// define default item value
392+
let value: SelectItem | SelectItem[] = props.multiple
393+
? []
394+
: { text: '', value: undefined }
395+
396+
if (props.multiple && item && Array.isArray(localModel.value)) {
397+
value = localModel.value.some((val) => isEqual(val.value, item.value))
398+
? localModel.value.filter((val) => !isEqual(val.value, item.value))
399+
: [...localModel.value, item]
400+
}
401+
if (!props.multiple && item)
399402
value = item
400403
401404
modelWatcher.pause()
@@ -431,7 +434,10 @@ function onClear () {
431434
}
432435
433436
function isSelected (item: SelectItem) {
434-
if (!localModel.value)
437+
if (props.multiple && localModel.value.length === 0)
438+
return false
439+
440+
if (!props.multiple && (localModel.value as SelectItem).value === undefined)
435441
return false
436442
437443
if (props.multiple && Array.isArray(localModel.value))

src/components/select/index.md

Lines changed: 19 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -51,6 +51,7 @@ description: Base form input.
5151
const province = ref('')
5252
const city = ref('')
5353
const multiValue = ref([])
54+
const provinces = ref([])
5455

5556
const provincesAdapter = defineAsyncAdapter(async (keyword, page, perPage) => {
5657
const response = await getProvinces(keyword, page, perPage)
@@ -258,6 +259,10 @@ You can set size of select via `size` prop. Available size are `lg`, `md`, `sm`,
258259
<p-select :options="optionsD" v-model="multiValue" multiple />
259260
</preview>
260261

262+
**Result :**
263+
264+
<pre><code>{{ multiValue || [] }}</code></pre>
265+
261266
```vue
262267
<template>
263268
<p-select :options="optionsD" v-model="multiValue" multiple />
@@ -360,11 +365,24 @@ It will take care of loading, inifinite load, and other stuff.
360365

361366
<pre><code>{{ province }}</code></pre>
362367

368+
### Multiple value
369+
<preview>
370+
<p-select
371+
:adapter="provincesAdapter"
372+
v-model="provinces"
373+
multiple />
374+
</preview>
375+
376+
**Result:**
377+
378+
<pre><code>{{ provinces }}</code></pre>
379+
363380
```vue
364381
<template>
365382
<p-select
366383
:adapter="provincesAdapter"
367-
v-model="province" />
384+
v-model="province"
385+
multiple />
368386
</template>
369387
370388
<script setup>

0 commit comments

Comments
 (0)