mirror of
https://gitee.com/element-plus/element-plus.git
synced 2024-12-12 12:25:22 +08:00
fix(components): [select] backspace delete disabled option (#11995)
* fix(components): [select] backspace delete disabled option * fix(components): [select] findLastIndex * fix(components): [select] simple polyfill findLastIndex in test file * fix(components): [select] add test for backspace * chore: lint
This commit is contained in:
parent
5c1306127f
commit
067028ba3c
@ -1914,10 +1914,10 @@ describe('Select', () => {
|
|||||||
await nextTick()
|
await nextTick()
|
||||||
|
|
||||||
expect(innerInputEl.placeholder).toBe('')
|
expect(innerInputEl.placeholder).toBe('')
|
||||||
|
|
||||||
selectInput.trigger('keydown', {
|
selectInput.trigger('keydown', {
|
||||||
key: EVENT_CODE.backspace,
|
key: EVENT_CODE.backspace,
|
||||||
})
|
})
|
||||||
|
|
||||||
await nextTick()
|
await nextTick()
|
||||||
expect(innerInputEl.placeholder).toBe(placeholder)
|
expect(innerInputEl.placeholder).toBe(placeholder)
|
||||||
vi.useRealTimers()
|
vi.useRealTimers()
|
||||||
@ -2297,5 +2297,67 @@ describe('Select', () => {
|
|||||||
expect(vm.value).toBe(2)
|
expect(vm.value).toBe(2)
|
||||||
expect(findInnerInput().value).toBe('z')
|
expect(findInnerInput().value).toBe('z')
|
||||||
})
|
})
|
||||||
|
// fix: https://github.com/element-plus/element-plus/issues/11991
|
||||||
|
it('backspace key should not delete disabled options', async () => {
|
||||||
|
const options = [
|
||||||
|
{
|
||||||
|
value: 'Option1',
|
||||||
|
label: 'Option1',
|
||||||
|
disable: true,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
value: 'Option2',
|
||||||
|
label: 'Option2',
|
||||||
|
disable: false,
|
||||||
|
},
|
||||||
|
]
|
||||||
|
const value = ['Option2', 'Option1']
|
||||||
|
const wrapper = _mount(
|
||||||
|
`
|
||||||
|
<el-select v-model="value"
|
||||||
|
multiple
|
||||||
|
filterable
|
||||||
|
>
|
||||||
|
<el-option
|
||||||
|
v-for="option in options"
|
||||||
|
:key="option.value"
|
||||||
|
:value="option.value"
|
||||||
|
:label="option.label"
|
||||||
|
:disabled="option.disable"
|
||||||
|
>
|
||||||
|
</el-option>
|
||||||
|
</el-select>
|
||||||
|
`,
|
||||||
|
() => ({
|
||||||
|
value,
|
||||||
|
options,
|
||||||
|
})
|
||||||
|
)
|
||||||
|
await nextTick()
|
||||||
|
const selectInput = wrapper.find('.el-select__input')
|
||||||
|
expect(wrapper.findAll('.el-tag').length).toBe(2)
|
||||||
|
// need trigger keydown twice because first keydown just select option, and second keydown is to delete
|
||||||
|
await selectInput.trigger('keydown', {
|
||||||
|
code: EVENT_CODE.backspace,
|
||||||
|
key: EVENT_CODE.backspace,
|
||||||
|
})
|
||||||
|
await selectInput.trigger('keydown', {
|
||||||
|
code: EVENT_CODE.backspace,
|
||||||
|
key: EVENT_CODE.backspace,
|
||||||
|
})
|
||||||
|
await nextTick()
|
||||||
|
expect(wrapper.findAll('.el-tag').length).toBe(1)
|
||||||
|
await selectInput.trigger('keydown', {
|
||||||
|
code: EVENT_CODE.backspace,
|
||||||
|
key: EVENT_CODE.backspace,
|
||||||
|
})
|
||||||
|
await selectInput.trigger('keydown', {
|
||||||
|
code: EVENT_CODE.backspace,
|
||||||
|
key: EVENT_CODE.backspace,
|
||||||
|
})
|
||||||
|
await nextTick()
|
||||||
|
// after the second deletion, an el-tag still exist
|
||||||
|
expect(wrapper.findAll('.el-tag').length).toBe(1)
|
||||||
|
})
|
||||||
})
|
})
|
||||||
})
|
})
|
||||||
|
@ -11,7 +11,12 @@ import {
|
|||||||
watch,
|
watch,
|
||||||
} from 'vue'
|
} from 'vue'
|
||||||
import { isObject, toRawType } from '@vue/shared'
|
import { isObject, toRawType } from '@vue/shared'
|
||||||
import { get, isEqual, debounce as lodashDebounce } from 'lodash-unified'
|
import {
|
||||||
|
findLastIndex,
|
||||||
|
get,
|
||||||
|
isEqual,
|
||||||
|
debounce as lodashDebounce,
|
||||||
|
} from 'lodash-unified'
|
||||||
import {
|
import {
|
||||||
CHANGE_EVENT,
|
CHANGE_EVENT,
|
||||||
EVENT_CODE,
|
EVENT_CODE,
|
||||||
@ -40,6 +45,7 @@ export function useSelectStates(props) {
|
|||||||
return reactive({
|
return reactive({
|
||||||
options: new Map(),
|
options: new Map(),
|
||||||
cachedOptions: new Map(),
|
cachedOptions: new Map(),
|
||||||
|
disabledOptions: new Map(),
|
||||||
createdLabel: null,
|
createdLabel: null,
|
||||||
createdSelected: false,
|
createdSelected: false,
|
||||||
selected: props.multiple ? [] : ({} as any),
|
selected: props.multiple ? [] : ({} as any),
|
||||||
@ -627,11 +633,16 @@ export const useSelect = (props, states: States, ctx) => {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const getLastNotDisabledIndex = (value) =>
|
||||||
|
findLastIndex(value, (it) => !states.disabledOptions.has(it))
|
||||||
|
|
||||||
const deletePrevTag = (e) => {
|
const deletePrevTag = (e) => {
|
||||||
if (e.code === EVENT_CODE.delete) return
|
if (e.code === EVENT_CODE.delete) return
|
||||||
if (e.target.value.length <= 0 && !toggleLastOptionHitState()) {
|
if (e.target.value.length <= 0 && !toggleLastOptionHitState()) {
|
||||||
const value = props.modelValue.slice()
|
const value = props.modelValue.slice()
|
||||||
value.pop()
|
const lastNotDisabledIndex = getLastNotDisabledIndex(value)
|
||||||
|
if (lastNotDisabledIndex < 0) return
|
||||||
|
value.splice(lastNotDisabledIndex, 1)
|
||||||
ctx.emit(UPDATE_MODEL_EVENT, value)
|
ctx.emit(UPDATE_MODEL_EVENT, value)
|
||||||
emitChange(value)
|
emitChange(value)
|
||||||
}
|
}
|
||||||
@ -754,6 +765,7 @@ export const useSelect = (props, states: States, ctx) => {
|
|||||||
states.filteredOptionsCount++
|
states.filteredOptionsCount++
|
||||||
states.options.set(vm.value, vm)
|
states.options.set(vm.value, vm)
|
||||||
states.cachedOptions.set(vm.value, vm)
|
states.cachedOptions.set(vm.value, vm)
|
||||||
|
vm.disabled && states.disabledOptions.set(vm.value, vm)
|
||||||
}
|
}
|
||||||
|
|
||||||
const onOptionDestroy = (key, vm: SelectOptionProxy) => {
|
const onOptionDestroy = (key, vm: SelectOptionProxy) => {
|
||||||
@ -772,7 +784,10 @@ export const useSelect = (props, states: States, ctx) => {
|
|||||||
|
|
||||||
const toggleLastOptionHitState = (hit?: boolean) => {
|
const toggleLastOptionHitState = (hit?: boolean) => {
|
||||||
if (!Array.isArray(states.selected)) return
|
if (!Array.isArray(states.selected)) return
|
||||||
const option = states.selected[states.selected.length - 1]
|
const lastNotDisabledIndex = getLastNotDisabledIndex(
|
||||||
|
states.selected.map((it) => it.value)
|
||||||
|
)
|
||||||
|
const option = states.selected[lastNotDisabledIndex]
|
||||||
if (!option) return
|
if (!option) return
|
||||||
|
|
||||||
if (hit === true || hit === false) {
|
if (hit === true || hit === false) {
|
||||||
|
Loading…
Reference in New Issue
Block a user