refactor(components)!: [select-v2] value-key is used for object select (#13263)

refactor(components): [select-v2] value-key is used for object select
This commit is contained in:
qiang
2023-10-13 00:08:34 -05:00
committed by GitHub
parent 4b5a9bf279
commit db0e65577d
4 changed files with 65 additions and 53 deletions

View File

@@ -3,7 +3,7 @@
v-model="value"
:options="options"
placeholder="Please select"
value-key="value.name"
value-key="name"
/>
</template>
@@ -11,7 +11,7 @@
import { ref } from 'vue'
const initials = ['a', 'b', 'c', 'd', 'e', 'f', 'g', 'h', 'i', 'j']
const value = ref()
const value = ref({ name: 'Option 1', test: 'test 0' })
const options = Array.from({ length: 1000 }).map((_, idx) => ({
value: {
name: `Option ${idx + 1}`,

View File

@@ -272,22 +272,19 @@ describe('Select', () => {
it('default value is Object', async () => {
const wrapper = createSelect({
data: () => ({
valueKey: 'value',
value: {
value: '1',
label: 'option_a',
},
valueKey: 'id',
value: { id: 1 },
options: [
{
value: '1',
value: { id: 1 },
label: 'option_a',
},
{
value: '2',
value: { id: 2 },
label: 'option_b',
},
{
value: '3',
value: { id: 3 },
label: 'option_c',
},
],
@@ -298,9 +295,7 @@ describe('Select', () => {
expect(wrapper.find(`.${PLACEHOLDER_CLASS_NAME}`).text()).toBe(
vm.options[0].label
)
expect(wrapper.find(`.${PLACEHOLDER_CLASS_NAME}`).text()).toBe(
vm.value.label
)
expect(vm.value).toEqual(vm.options[0].value)
})
it('sync set value and options', async () => {
@@ -352,22 +347,19 @@ describe('Select', () => {
return {
options: [
{
id: 'id 1',
value: 'value 1',
value: { id: 1 },
label: 'option 1',
},
{
id: 'id 2',
value: 'value 2',
value: { id: 2 },
label: 'option 2',
},
{
id: 'id 3',
value: 'value 3',
value: { id: 3 },
label: 'option 3',
},
],
value: '',
value: undefined,
valueKey: 'id',
}
},
@@ -378,12 +370,11 @@ describe('Select', () => {
const options = getOptions()
options[1].click()
await nextTick()
expect(vm.value).toBe(vm.options[1].id)
vm.valueKey = 'value'
await nextTick()
expect(vm.value).toEqual(vm.options[1].value)
options[2].click()
await nextTick()
expect(vm.value).toBe(vm.options[2].value)
expect(vm.value).toEqual(vm.options[2].value)
})
it('disabled option', async () => {
@@ -616,18 +607,15 @@ describe('Select', () => {
return {
options: [
{
id: 'id 1',
value: 'value 1',
value: { id: 1, value: 'a' },
label: 'option 1',
},
{
id: 'id 2',
value: 'value 2',
value: { id: 2, value: 'b' },
label: 'option 2',
},
{
id: 'id 3',
value: 'value 3',
value: { id: 3, value: 'c' },
label: 'option 3',
},
],
@@ -644,13 +632,24 @@ describe('Select', () => {
options[1].click()
await nextTick()
expect(vm.value.length).toBe(1)
expect(vm.value[0]).toBe(vm.options[1].id)
vm.valueKey = 'value'
await nextTick()
expect(vm.value).toContainEqual(vm.options[1].value)
options[2].click()
await nextTick()
expect(vm.value.length).toBe(2)
expect(vm.value[1]).toBe(vm.options[2].value)
expect(vm.value).toContainEqual(vm.options[2].value)
options[2].click()
await nextTick()
expect(vm.value.length).toBe(1)
expect(vm.value).not.toContainEqual(vm.options[2].value)
vm.valueKey = 'value'
await nextTick()
expect(vm.value.length).toBe(1)
expect(vm.value).toContainEqual(vm.options[1].value)
options[0].click()
await nextTick()
expect(vm.value.length).toBe(2)
expect(vm.value).toContainEqual(vm.options[0].value)
})
})

View File

@@ -1,4 +1,12 @@
import { computed, defineComponent, inject, ref, unref, watch } from 'vue'
import {
computed,
defineComponent,
inject,
ref,
toRaw,
unref,
watch,
} from 'vue'
import { get } from 'lodash-unified'
import { isObject, isUndefined } from '@element-plus/utils'
import {
@@ -69,7 +77,7 @@ export default defineComponent({
return (
arr &&
arr.some((item) => {
return get(item, valueKey) === get(target, valueKey)
return toRaw(get(item, valueKey)) === get(target, valueKey)
})
)
}
@@ -83,11 +91,10 @@ export default defineComponent({
}
const isItemSelected = (modelValue: any[] | any, target: Option) => {
const { valueKey } = select.props
if (select.props.multiple) {
return contains(modelValue, get(target, valueKey))
return contains(modelValue, target.value)
}
return isEqual(modelValue, get(target, valueKey))
return isEqual(modelValue, target.value)
}
const isItemDisabled = (modelValue: any[] | any, selected: boolean) => {

View File

@@ -176,7 +176,7 @@ const useSelect = (props: ExtractPropTypes<typeof SelectProps>, emit) => {
const valueMap = new Map()
filteredOptions.value.forEach((option, index) => {
valueMap.set(getValueKey(option), { option, index })
valueMap.set(getValueKey(option.value), { option, index })
})
return valueMap
})
@@ -409,7 +409,7 @@ const useSelect = (props: ExtractPropTypes<typeof SelectProps>, emit) => {
if (props.multiple) {
let selectedOptions = (props.modelValue as any[]).slice()
const index = getValueIndex(selectedOptions, getValueKey(option))
const index = getValueIndex(selectedOptions, option.value)
if (index > -1) {
selectedOptions = [
...selectedOptions.slice(0, index),
@@ -421,7 +421,7 @@ const useSelect = (props: ExtractPropTypes<typeof SelectProps>, emit) => {
props.multipleLimit <= 0 ||
selectedOptions.length < props.multipleLimit
) {
selectedOptions = [...selectedOptions, getValueKey(option)]
selectedOptions = [...selectedOptions, option.value]
states.cachedOptions.push(option)
selectNewOption(option)
updateHoveringIndex(idx)
@@ -445,7 +445,7 @@ const useSelect = (props: ExtractPropTypes<typeof SelectProps>, emit) => {
} else {
selectedIndex.value = idx
states.selectedLabel = option.label
update(getValueKey(option))
update(option.value)
expanded.value = false
states.isComposing = false
states.isSilentBlur = byClick
@@ -457,20 +457,21 @@ const useSelect = (props: ExtractPropTypes<typeof SelectProps>, emit) => {
}
}
const deleteTag = (event: MouseEvent, tag: Option) => {
const { valueKey } = props
const index = (props.modelValue as Array<any>).indexOf(get(tag, valueKey))
const deleteTag = (event: MouseEvent, option: Option) => {
let selectedOptions = (props.modelValue as any[]).slice()
const index = getValueIndex(selectedOptions, option.value)
if (index > -1 && !selectDisabled.value) {
const value = [
selectedOptions = [
...(props.modelValue as Array<unknown>).slice(0, index),
...(props.modelValue as Array<unknown>).slice(index + 1),
]
states.cachedOptions.splice(index, 1)
update(value)
emit('remove-tag', get(tag, valueKey))
update(selectedOptions)
emit('remove-tag', option.value)
states.softFocus = true
removeNewOption(tag)
removeNewOption(option)
return nextTick(focusAndUpdatePopup)
}
event.stopPropagation()
@@ -673,8 +674,12 @@ const useSelect = (props: ExtractPropTypes<typeof SelectProps>, emit) => {
states.previousValue = props.modelValue.toString()
for (const value of props.modelValue) {
if (filteredOptionsValueMap.value.has(value)) {
const { index, option } = filteredOptionsValueMap.value.get(value)
const selectValue = getValueKey(value)
if (filteredOptionsValueMap.value.has(selectValue)) {
const { index, option } =
filteredOptionsValueMap.value.get(selectValue)
states.cachedOptions.push(option)
if (!initHovering) {
updateHoveringIndex(index)
@@ -691,7 +696,8 @@ const useSelect = (props: ExtractPropTypes<typeof SelectProps>, emit) => {
states.previousValue = props.modelValue
const options = filteredOptions.value
const selectedItemIndex = options.findIndex(
(option) => getValueKey(option) === getValueKey(props.modelValue)
(option) =>
getValueKey(option.value) === getValueKey(props.modelValue)
)
if (~selectedItemIndex) {
states.selectedLabel = options[selectedItemIndex].label