From d6975c61df59663fa2f00fbf2700a005619f15f1 Mon Sep 17 00:00:00 2001 From: msidolphin Date: Wed, 25 Aug 2021 17:23:13 +0800 Subject: [PATCH] feat(select-v2): support allow-create feature (#3017) * feat(select-v2): support allow-create feature support allow-create feature and refactored part of select-v2's code * fix: optimized code --- .../select-v2/__tests__/select.spec.ts | 128 +++++++++++++++++- packages/components/select-v2/src/defaults.ts | 11 ++ .../components/select-v2/src/option-item.vue | 29 +--- .../select-v2/src/select-dropdown.vue | 15 +- packages/components/select-v2/src/select.vue | 2 +- packages/components/select-v2/src/token.ts | 4 +- .../select-v2/src/useAllowCreate.ts | 82 +++++++++++ .../components/select-v2/src/useOption.ts | 14 ++ .../components/select-v2/src/useSelect.ts | 32 +++-- .../virtual-list/src/builders/buildList.ts | 9 ++ .../virtual-list/src/components/scrollbar.ts | 2 +- .../theme-chalk/src/select-dropdown-v2.scss | 4 +- packages/theme-chalk/src/select-v2.scss | 72 +++++++--- website/docs/en-US/select-v2.md | 43 +++++- website/docs/es/select-v2.md | 46 ++++++- website/docs/fr-FR/select-v2.md | 47 ++++++- website/docs/jp/select-v2.md | 45 +++++- website/docs/zh-CN/select-v2.md | 44 +++++- 18 files changed, 557 insertions(+), 72 deletions(-) create mode 100644 packages/components/select-v2/src/useAllowCreate.ts create mode 100644 packages/components/select-v2/src/useOption.ts diff --git a/packages/components/select-v2/__tests__/select.spec.ts b/packages/components/select-v2/__tests__/select.spec.ts index 7f8fa7459d..68234f86ae 100644 --- a/packages/components/select-v2/__tests__/select.spec.ts +++ b/packages/components/select-v2/__tests__/select.spec.ts @@ -20,6 +20,16 @@ const createData = (count = 1000) => { })) } +const clickClearButton = async wrapper => { + const select = wrapper.findComponent(Select) + const selectVm = select.vm as any + selectVm.states.comboBoxHovering = true + await nextTick + const clearBtn = wrapper.find(`.${selectVm.clearIcon}`) + expect(clearBtn.exists()).toBeTruthy() + await clearBtn.trigger('click') +} + interface SelectProps { popperClass?: string value?: string | string[] | number | number[] @@ -29,6 +39,7 @@ interface SelectProps { multiple?: boolean filterable?: boolean multipleLimit?: number + allowCreate?: boolean popperAppendToBody?: boolean placeholder?: string [key: string]: any @@ -64,6 +75,7 @@ const createSelect = (options: { :multiple-limit="multipleLimit" :popper-append-to-body="popperAppendToBody" :placeholder="placeholder" + :allow-create="allowCreate" @change="onChange" @visible-change="onVisibleChange" @remove-tah="onRemoveTag" @@ -79,6 +91,7 @@ const createSelect = (options: { options: createData(), value: '', popperClass: '', + allowCreate: false, disabled: false, clearable: false, multiple: false, @@ -313,13 +326,7 @@ describe('Select', () => { const vm = wrapper.vm as any vm.value = vm.options[1].value await nextTick - const select = wrapper.findComponent(Select) - const selectVm = select.vm as any - selectVm.states.comboBoxHovering = true - await nextTick - const clearBtn = wrapper.find(`.${selectVm.clearIcon}`) - expect(clearBtn.exists()).toBeTruthy() - await clearBtn.trigger('click') + await clickClearButton(wrapper) expect(vm.value).toBe('') const placeholder = wrapper.find(`.${PLACEHOLDER_CLASS_NAME}`) expect(placeholder.text()).toBe(DEFAULT_PLACEHOLDER) @@ -492,6 +499,113 @@ describe('Select', () => { }) }) + describe('allow-create', () => { + + it('single select', async() => { + const wrapper = createSelect({ + data: () => { + return { + allowCreate: true, + filterable: true, + clearable: true, + options: [ + { + value: '1', + label: 'option 1', + }, + { + value: '2', + label: 'option 2', + }, + { + value: '3', + label: 'option 3', + }, + ], + } + }, + }) + await nextTick + const vm = wrapper.vm as any + const input = wrapper.find('input') + await wrapper.trigger('click') + // create a new option + await input.trigger('compositionupdate', { + data: '1111', + }) + const options = getOptions() + const select = wrapper.findComponent(Select) + const selectVm = select.vm as any + expect(selectVm.filteredOptions.length).toBe(1) + // selected the new option + await options[0].click() + expect(vm.value).toBe('1111') + // closed the menu + await wrapper.trigger('click') + expect(selectVm.filteredOptions.length).toBe(4) + selectVm.handleClear() + expect(selectVm.filteredOptions.length).toBe(3) + }) + + it('multiple', async () => { + const wrapper = createSelect({ + data: () => { + return { + allowCreate: true, + filterable: true, + clearable: true, + multiple: true, + options: [ + { + value: '1', + label: 'option 1', + }, + { + value: '2', + label: 'option 2', + }, + { + value: '3', + label: 'option 3', + }, + ], + } + }, + }) + await nextTick + const vm = wrapper.vm as any + await wrapper.trigger('click') + await wrapper.find('input').trigger('compositionupdate', { + data: '1111', + }) + const options = getOptions() + const select = wrapper.findComponent(Select) + const selectVm = select.vm as any + expect(selectVm.filteredOptions.length).toBe(1) + // selected the new option + await options[0].click() + // closed the menu + await wrapper.trigger('click') + await wrapper.find('input').trigger('compositionupdate', { + data: '2222', + }) + await getOptions()[0].click() + expect(JSON.stringify(vm.value)).toBe(JSON.stringify(['1111', '2222'])) + await wrapper.trigger('click') + expect(selectVm.filteredOptions.length).toBe(5) + // remove tag + const tagCloseIcons = wrapper.findAll('.el-tag__close') + await tagCloseIcons[1].trigger('click') + expect(selectVm.filteredOptions.length).toBe(4) + // simulate backspace + await wrapper.find('input').trigger('keydown', { + key: EVENT_CODE.backspace, + }) + expect(selectVm.filteredOptions.length).toBe(3) + }) + + }) + it('render empty slot', async () => { const wrapper = createSelect({ data () { diff --git a/packages/components/select-v2/src/defaults.ts b/packages/components/select-v2/src/defaults.ts index 21972a8e12..d293134738 100644 --- a/packages/components/select-v2/src/defaults.ts +++ b/packages/components/select-v2/src/defaults.ts @@ -77,3 +77,14 @@ export const SelectProps = { default: 'value', }, } + +export const OptionProps = { + data: Array, + disabled: Boolean, + hovering: Boolean, + item: Object, + index: Number, + style: Object, + selected: Boolean, + created: Boolean, +} diff --git a/packages/components/select-v2/src/option-item.vue b/packages/components/select-v2/src/option-item.vue index 7539223f23..4c3410b083 100644 --- a/packages/components/select-v2/src/option-item.vue +++ b/packages/components/select-v2/src/option-item.vue @@ -6,6 +6,7 @@ 'el-select-dropdown__option-item': true, 'is-selected': selected, 'is-disabled': disabled, + 'is-craeted': created, 'hover': hovering }" @mouseenter="hoverItem" @@ -19,32 +20,16 @@ +``` +::: ### Remote search @@ -350,6 +390,7 @@ Some APIs are still undergoing (comparing to the non-virtualized select), becaus | autocomplete | select input 的 autocomplete 属性 | string | — | off | | placeholder | the autocomplete attribute of select input | string | — | Please select | | filterable | is filterable | boolean | — | false | +| allow-create | whether creating new items is allowed. To use this, `filterable` must be true | boolean | — | false | | no-data-text | displayed text when there is no options, you can also use slot empty | string | — | No Data | | popper-class | custom class name for Select's dropdown | string | — | — | | popper-append-to-body | whether to append the popper menu to body. If the positioning of the popper is wrong, you can try to set this prop to false | boolean | - | false | diff --git a/website/docs/es/select-v2.md b/website/docs/es/select-v2.md index 594fa236bc..aa8549de59 100644 --- a/website/docs/es/select-v2.md +++ b/website/docs/es/select-v2.md @@ -319,9 +319,50 @@ We can clear all the selected options at once, also applicable for single select ::: -### Create Option +### Crear nuevos items +Crear y seleccionar nuevos items que no están incluidas en las opciones de selección. -WIP👷‍♀️ +:::demo Al utilizar el atributo `allow-create`, los usuarios pueden crear nuevos elementos escribiendo en el cuadro del input. Tenga en cuenta que para que `allow-create` funcione, `filterable` debe ser `true`. +```html + + + +``` +::: ### Remote search @@ -350,6 +391,7 @@ Some APIs are still undergoing (comparing to the non-virtualized select), becaus | autocomplete | select input 的 autocomplete 属性 | string | — | off | | placeholder | the autocomplete attribute of select input | string | — | Please select | | filterable | is filterable | boolean | — | false | +| allow-create | si esta permitido crear nuevos items. Para usar esto, `filterable` debe ser `true`. | boolean | — | false | | no-data-text | displayed text when there is no options, you can also use slot empty | string | — | No Data | | popper-class | custom class name for Select's dropdown | string | — | — | | popper-append-to-body | whether to append the popper menu to body. If the positioning of the popper is wrong, you can try to set this prop to false | boolean | - | false | diff --git a/website/docs/fr-FR/select-v2.md b/website/docs/fr-FR/select-v2.md index 594fa236bc..036596b1e7 100644 --- a/website/docs/fr-FR/select-v2.md +++ b/website/docs/fr-FR/select-v2.md @@ -319,9 +319,51 @@ We can clear all the selected options at once, also applicable for single select ::: -### Create Option +### Créer des options -WIP👷‍♀️ +Vous pouvez entrer des choix dans le champ de sélection qui ne sont pas incluses dans le menu. + +:::demo En utilisant `allow-create`, peuvent créer de nouveaux choix en les entrant dans le champ d'input. Cette option ne marche que si `filterable` est activé. +```html + + + +``` +::: ### Remote search @@ -350,6 +392,7 @@ Some APIs are still undergoing (comparing to the non-virtualized select), becaus | autocomplete | select input 的 autocomplete 属性 | string | — | off | | placeholder | the autocomplete attribute of select input | string | — | Please select | | filterable | is filterable | boolean | — | false | +| allow-create | Si l'utilisateur peut créer des options. Dans ce cas `filterable` doit être activé. | boolean | — | false | | no-data-text | displayed text when there is no options, you can also use slot empty | string | — | No Data | | popper-class | custom class name for Select's dropdown | string | — | — | | popper-append-to-body | whether to append the popper menu to body. If the positioning of the popper is wrong, you can try to set this prop to false | boolean | - | false | diff --git a/website/docs/jp/select-v2.md b/website/docs/jp/select-v2.md index 594fa236bc..9daa719471 100644 --- a/website/docs/jp/select-v2.md +++ b/website/docs/jp/select-v2.md @@ -319,9 +319,49 @@ We can clear all the selected options at once, also applicable for single select ::: -### Create Option +### 新規アイテムの作成 +セレクトオプションに含まれないアイテムを新規に作成してセレクトする +:::demo `allow-create`属性を使うことで、ユーザは入力ボックスに入力することで新しいアイテムを作成することができます。なお、`allow-create` が動作するためには、`filterable` が `true` でなければならない。 +```html + -WIP👷‍♀️ + +``` +::: ### Remote search @@ -350,6 +390,7 @@ Some APIs are still undergoing (comparing to the non-virtualized select), becaus | autocomplete | select input 的 autocomplete 属性 | string | — | off | | placeholder | the autocomplete attribute of select input | string | — | Please select | | filterable | is filterable | boolean | — | false | +| allow-create | 新しいアイテムの作成を許可するかどうかを指定します。これを使うには、`filterable` がtrueでなければなりません。 | boolean | — | false | | no-data-text | displayed text when there is no options, you can also use slot empty | string | — | No Data | | popper-class | custom class name for Select's dropdown | string | — | — | | popper-append-to-body | whether to append the popper menu to body. If the positioning of the popper is wrong, you can try to set this prop to false | boolean | - | false | diff --git a/website/docs/zh-CN/select-v2.md b/website/docs/zh-CN/select-v2.md index 05614b2297..ec44389875 100644 --- a/website/docs/zh-CN/select-v2.md +++ b/website/docs/zh-CN/select-v2.md @@ -321,7 +321,48 @@ ### 创建临时选项 -WIP (该功能还在施工中👷‍♀️) +可以创建并选中选项中不存在的条目 +:::demo 使用`allow-create`属性即可通过在输入框中输入文字来创建新的条目。注意此时`filterable`必须为真。 +```html + + + +``` +::: ### 远程搜索 @@ -350,6 +391,7 @@ WIP (该功能还在施工中👷‍♀️) | autocomplete | select input 的 autocomplete 属性 | string | — | off | | placeholder | 占位符 | string | — | 请选择 | | filterable | 是否可搜索 | boolean | — | false | +| allow-create | 是否允许用户创建新条目,需配合 `filterable` 使用 | boolean | — | false | | no-data-text | 选项为空时显示的文字,也可以使用`#empty`设置 | string | — | 无数据 | | popper-class | Select 下拉框的类名 | string | — | — | | popper-append-to-body | 是否将弹出框插入至 body 元素。在弹出框的定位出现问题时,可将该属性设置为 false | boolean | - | false |