fix(select): should not stop at invisible options by up and down (#2585)

* fix(select): should not stop at invisible options by up and down

this also fix another problem (#2562): about unselect user-created tag in ElSelect

fix #2563 #2562

* fix(select): checkDefaultFirstOption: exlucde disabled options

fix #2562 #2563

* test(select): add test for "default-first-option" (with navigation)

check default first option

re #2585
This commit is contained in:
Mao NianYou 2021-07-24 11:41:23 +08:00 committed by GitHub
parent 5b16f20725
commit d6fecf9b32
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
3 changed files with 73 additions and 32 deletions

View File

@ -20,6 +20,7 @@ interface SelectProps {
automaticDropdown?: boolean
multipleLimit?: number
popperClass?: string
defaultFirstOption?: boolean
}
const _mount = (template: string, data: any = () => ({}), otherObj?) => mount({
@ -42,7 +43,7 @@ function getOptions(): HTMLElement[] {
}
const getSelectVm = (configs: SelectProps = {}, options?) => {
['multiple', 'clearable', 'filterable', 'allowCreate', 'remote', 'collapseTags', 'automaticDropdown'].forEach(config => {
['multiple', 'clearable', 'defaultFirstOption', 'filterable', 'allowCreate', 'remote', 'collapseTags', 'automaticDropdown'].forEach(config => {
configs[config] = configs[config] || false
})
configs.multipleLimit = configs.multipleLimit || 0
@ -78,6 +79,7 @@ const getSelectVm = (configs: SelectProps = {}, options?) => {
:multiple-limit="multipleLimit"
:popper-class="popperClass"
:clearable="clearable"
:default-first-option="defaultFirstOption"
:filterable="filterable"
:collapse-tags="collapseTags"
:allow-create="allowCreate"
@ -99,6 +101,7 @@ const getSelectVm = (configs: SelectProps = {}, options?) => {
multiple: configs.multiple,
multipleLimit: configs.multipleLimit,
clearable: configs.clearable,
defaultFirstOption: configs.defaultFirstOption,
filterable: configs.filterable,
collapseTags: configs.collapseTags,
allowCreate: configs.allowCreate,
@ -449,6 +452,51 @@ describe('Select', () => {
expect(vm.value).toBe('')
})
test('check default first option', async () => {
const wrapper = getSelectVm({
filterable: true,
defaultFirstOption: true,
})
const select = wrapper.findComponent({ name: 'ElSelect' })
const selectVm = select.vm as any
const input = wrapper.find('input')
input.element.focus()
expect(selectVm.hoverIndex).toBe(0)
selectVm.navigateOptions('next')
expect(selectVm.hoverIndex).toBe(1)
})
test('check default first option when the very first option is disabled', async () => {
const demoOptions = [{
value: 'HTML',
label: 'HTML',
disabled: true,
}, {
value: 'CSS',
label: 'CSS',
disabled: false,
}, {
value: 'JavaScript',
label: 'JavaScript',
disabled: false,
}]
const wrapper = getSelectVm({
filterable: true,
defaultFirstOption: true,
}, demoOptions)
const select = wrapper.findComponent({ name: 'ElSelect' })
const selectVm = select.vm as any
const input = wrapper.find('input')
input.element.focus()
expect(selectVm.hoverIndex).toBe(1) // index 0 was skipped
selectVm.navigateOptions('next')
expect(selectVm.hoverIndex).toBe(2)
selectVm.navigateOptions('next')
expect(selectVm.hoverIndex).toBe(1) // index 0 was skipped
})
test('allow create', async () => {
const wrapper = getSelectVm({ filterable: true, allowCreate: true })
const select = wrapper.findComponent({ name: 'ElSelect' })

View File

@ -81,10 +81,16 @@ export function useOption(props, states) {
}
const queryChange = (query: string) => {
const regexp = new RegExp(escapeRegexpString(query), 'i')
states.visible = regexp.test(currentLabel.value) || props.created
if (!states.visible) {
select.filteredOptionsCount--
// not in filtering, just show original options only
if (!query) {
states.visible = !props.created
} else {
// in filtering, do filter by RegExp
const regexp = new RegExp(escapeRegexpString(query), 'i')
states.visible = regexp.test(currentLabel.value)
if (!states.visible && !props.created) {
select.filteredOptionsCount--
}
}
}

View File

@ -318,34 +318,21 @@ export const useSelect = (props, states: States, ctx) => {
}
}
/**
* find and highlight first option as default selected
* @remark
* - if the first option in dropdown list is user-created,
* it would be at the end of the optionsArray
* so find it and set hover.
* (NOTE: there must be only one user-created option in dropdown list with query)
* - if there's no user-created option in list, just find the first one as usual
* (NOTE: exclude options that are disabled or in disabled-group)
*/
const checkDefaultFirstOption = () => {
states.hoverIndex = -1
// highlight the created option
let hasCreated = false
for (let i = states.options.size - 1; i >= 0; i--) {
if (optionsArray.value[i].created) {
hasCreated = true
states.hoverIndex = i
break
}
}
if (hasCreated) return
for (let i = 0; i !== states.options.size; ++i) {
const option = optionsArray.value[i]
if (states.query) {
// highlight first options that passes the filter
if (!option.disabled && !option.groupDisabled && option.visible) {
states.hoverIndex = i
break
}
} else {
// highlight currently selected option
if (option.itemSelected) {
states.hoverIndex = i
break
}
}
}
const optionsInDropdown = optionsArray.value.filter(n => n.visible && !n.disabled && !n.groupDisabled)
const userCreatedOption = optionsInDropdown.filter(n => n.created)[0]
const firstOriginOption = optionsInDropdown[0]
states.hoverIndex = getValueIndex(optionsArray.value, userCreatedOption || firstOriginOption)
}
const setSelected = () => {