Merge pull request #1401 from Leopoldthecoder/select-refactor

Select: refactor and bug fix
pull/1402/merge
baiyaaaaa 2016-12-02 11:50:25 +08:00 committed by GitHub
commit d3afe22e89
9 changed files with 354 additions and 192 deletions

View File

@ -62,6 +62,16 @@
}]
}],
options4: [],
options5: [{
value: 'HTML',
label: 'HTML'
}, {
value: 'CSS',
label: 'CSS'
}, {
value: 'JavaScript',
label: 'JavaScript'
}],
cities: [{
value: 'Beijing',
label: 'Beijing'
@ -87,9 +97,10 @@
value4: '',
value5: [],
value6: '',
value7: [],
value7: '',
value8: '',
value9: [],
value10: [],
loading: false,
states: ["Alabama", "Alaska", "Arizona", "Arkansas", "California", "Colorado", "Connecticut", "Delaware", "Florida", "Georgia", "Hawaii", "Idaho", "Illinois", "Indiana", "Iowa", "Kansas", "Kentucky", "Louisiana", "Maine", "Maryland", "Massachusetts", "Michigan", "Minnesota", "Mississippi", "Missouri", "Montana", "Nebraska", "Nevada", "New Hampshire", "New Jersey", "New Mexico", "New York", "North Carolina", "North Dakota", "Ohio", "Oklahoma", "Oregon", "Pennsylvania", "Rhode Island", "South Carolina", "South Dakota", "Tennessee", "Texas", "Utah", "Vermont", "Virginia", "Washington", "West Virginia", "Wisconsin", "Wyoming"]
};
@ -569,15 +580,58 @@ Enter keywords and search data from server.
```
:::
### Create new items
Create and select new items that are not included in select options
:::demo By using the `allow-create` attribute, users can create new items by typing in the input box. Note that for `allow-create` to work, `filterable` must be `true`.
```html
<template>
<el-select
v-model="value10"
multiple
filterable
allow-create
placeholder="Choose tags for your article">
<el-option
v-for="item in options5"
:label="item.label"
:value="item.value">
</el-option>
</el-select>
</template>
<script>
export default {
data() {
return {
options5: [{
value: 'HTML',
label: 'HTML'
}, {
value: 'CSS',
label: 'CSS'
}, {
value: 'JavaScript',
label: 'JavaScript'
}],
value10: []
}
}
}
</script>
```
:::
### Select Attributes
| Attribute | Description | Type | Accepted Values | Default |
|---------- |-------------- |---------- |-------------------------------- |-------- |
| multiple | whether multiple-select is activated | boolean | — | false |
| disabled | whether Select is disabled | boolean | — | false |
| clearable | whether single select can be cleared | boolean | — | false |
| multiple-limit | maximum number of options user can select when `multiple` is `true`. No limit when set to 0 | number | — | 0 |
| name | the name attribute of select input | string | — | — |
| placeholder | placeholder | string | — | Select |
| filterable | whether Select is filterable | boolean | — | false |
| allow-create | whether creating new items is allowed. To use this, `filterable` must be true | boolean | — | false |
| filter-method | custom filter method | function | — | — |
| remote | whether options are loaded from server | boolean | — | false |
| remote-method | custom remote search method | function | — | — |

View File

@ -62,6 +62,16 @@
}]
}],
options4: [],
options5: [{
value: 'HTML',
label: 'HTML'
}, {
value: 'CSS',
label: 'CSS'
}, {
value: 'JavaScript',
label: 'JavaScript'
}],
cities: [{
value: 'Beijing',
label: '北京'
@ -87,9 +97,10 @@
value4: '',
value5: [],
value6: '',
value7: [],
value7: '',
value8: '',
value9: [],
value9: '',
value10: [],
loading: false,
states: ["Alabama", "Alaska", "Arizona", "Arkansas", "California", "Colorado", "Connecticut", "Delaware", "Florida", "Georgia", "Hawaii", "Idaho", "Illinois", "Indiana", "Iowa", "Kansas", "Kentucky", "Louisiana", "Maine", "Maryland", "Massachusetts", "Michigan", "Minnesota", "Mississippi", "Missouri", "Montana", "Nebraska", "Nevada", "New Hampshire", "New Jersey", "New Mexico", "New York", "North Carolina", "North Dakota", "Ohio", "Oklahoma", "Oregon", "Pennsylvania", "Rhode Island", "South Carolina", "South Dakota", "Tennessee", "Texas", "Utah", "Vermont", "Virginia", "Washington", "West Virginia", "Wisconsin", "Wyoming"]
};
@ -129,7 +140,6 @@
### 基础用法
适用广泛的基础单选
:::demo `v-model`的值为当前被选中的`el-option`的 value 属性值
```html
<template>
@ -499,7 +509,6 @@
### 远程搜索
从服务器搜索数据,输入关键字进行查找
:::demo 为了启用远程搜索,需要将`filterable`和`remote`设置为`true`,同时传入一个`remote-method`。`remote-method`为一个`Function`,它会在输入值发生变化时调用,参数为当前输入值。需要注意的是,如果`el-option`是通过`v-for`指令渲染出来的,此时需要为`el-option`添加`key`属性,且其值需具有唯一性,比如此例中的`item.value`。
```html
<template>
@ -573,15 +582,58 @@
```
:::
### 创建条目
可以创建并选中选项中不存在的条目
:::demo 使用`allow-create`属性即可通过在输入框中输入文字来创建新的条目。注意此时`filterable`必须为真。
```html
<template>
<el-select
v-model="value10"
multiple
filterable
allow-create
placeholder="请选择文章标签">
<el-option
v-for="item in options5"
:label="item.label"
:value="item.value">
</el-option>
</el-select>
</template>
<script>
export default {
data() {
return {
options5: [{
value: 'HTML',
label: 'HTML'
}, {
value: 'CSS',
label: 'CSS'
}, {
value: 'JavaScript',
label: 'JavaScript'
}],
value10: []
}
}
}
</script>
```
:::
### Select Attributes
| 参数 | 说明 | 类型 | 可选值 | 默认值 |
|---------- |-------------- |---------- |-------------------------------- |-------- |
| multiple | 是否多选 | boolean | — | false |
| disabled | 是否禁用 | boolean | — | false |
| clearable | 单选时是否可以清空选项 | boolean | — | false |
| multiple-limit | 多选时用户最多可以选择的项目数,为 0 则不限制 | number | — | 0 |
| name | select input 的 name 属性 | string | — | — |
| placeholder | 占位符 | string | — | 请选择 |
| filterable | 是否可搜索 | boolean | — | false |
| allow-create | 是否允许用户创建新条目,需配合 `filterable` 使用 | boolean | — | false |
| filter-method | 自定义过滤方法 | function | — | — |
| remote | 是否为远程搜索 | boolean | — | false |
| remote-method | 远程搜索方法 | function | — | — |

View File

@ -140,17 +140,15 @@ export default {
return (
<span class="el-pagination__sizes">
<el-select
size="small"
value={ this.$parent.internalPageSize }
on-change={ this.handleChange }
width={ 110 }>
on-input={ this.handleChange }>
{
this.pageSizes.map(item =>
<el-option
value={ item }
label={ item + ' ' + this.t('el.pagination.pagesize') }>
</el-option>
)
<el-option
value={ item }
label={ item + ' ' + this.t('el.pagination.pagesize') }>
</el-option>
)
}
</el-select>
</span>

View File

@ -4,7 +4,11 @@
@click.stop="selectOptionClick"
class="el-select-dropdown__item"
v-show="visible"
:class="{ 'selected': itemSelected, 'is-disabled': disabled || groupDisabled, 'hover': parent.hoverIndex === index }">
:class="{
'selected': itemSelected,
'is-disabled': disabled || groupDisabled || limitReached,
'hover': parent.hoverIndex === index
}">
<slot>
<span>{{ currentLabel }}</span>
</slot>
@ -30,6 +34,7 @@
type: Boolean,
default: false
},
created: Boolean,
disabled: {
type: Boolean,
default: false
@ -63,22 +68,20 @@
},
itemSelected() {
if (Object.prototype.toString.call(this.parent.selected) === '[object Object]') {
return this === this.parent.selected;
} else if (Array.isArray(this.parent.selected)) {
if (!this.parent.multiple) {
return this.value === this.parent.value;
} else {
return this.parent.value.indexOf(this.value) > -1;
}
},
currentSelected() {
return this.selected || (this.parent.multiple ? this.parent.value.indexOf(this.value) > -1 : this.parent.value === this.value);
}
},
watch: {
currentSelected(val) {
if (val === true) {
this.dispatch('ElSelect', 'addOptionToValue', this);
limitReached() {
if (this.parent.multiple) {
return !this.itemSelected &&
this.parent.value.length >= this.parent.multipleLimit &&
this.parent.multipleLimit > 0;
} else {
return false;
}
}
},
@ -103,7 +106,7 @@
queryChange(query) {
// query
let parsedQuery = query.replace(/(\^|\(|\)|\[|\]|\$|\*|\+|\.|\?|\\|\{|\}|\|)/g, '\\$1');
this.visible = new RegExp(parsedQuery, 'i').test(this.currentLabel);
this.visible = new RegExp(parsedQuery, 'i').test(this.currentLabel) || this.created;
if (!this.visible) {
this.parent.filteredOptionsCount--;
}
@ -122,10 +125,6 @@
this.parent.filteredOptionsCount++;
this.index = this.parent.options.indexOf(this);
if (this.currentSelected === true) {
this.dispatch('ElSelect', 'addOptionToValue', [this, true]);
}
this.$on('queryChange', this.queryChange);
this.$on('handleGroupDisabled', this.handleGroupDisabled);
this.$on('resetIndex', this.resetIndex);

View File

@ -1,19 +1,26 @@
<template>
<div
class="el-select"
v-clickoutside="handleClose"
:class="{ 'is-multiple': multiple, 'is-small': size === 'small' }">
<div class="el-select__tags" v-if="multiple" @click.stop="toggleMenu" ref="tags" :style="{ 'max-width': inputWidth - 32 + 'px' }">
v-clickoutside="handleClose">
<div
class="el-select__tags"
v-if="multiple"
@click.stop="toggleMenu"
ref="tags"
:style="{ 'max-width': inputWidth - 32 + 'px' }">
<transition-group @after-leave="resetInputHeight">
<el-tag
v-for="item in selected"
:key="item"
:key="item.value"
closable
:hit="item.hitState"
type="primary"
@close="deleteTag($event, item)"
close-transition>{{ item.currentLabel }}</el-tag>
close-transition>
{{ item.currentLabel }}
</el-tag>
</transition-group>
<input
type="text"
class="el-select__input"
@ -40,7 +47,7 @@
:disabled="disabled"
:readonly="!filterable || multiple"
@focus="toggleMenu"
@click="toggleMenu"
@click="handleIconClick"
@mousedown.native="handleMouseDown"
@keyup.native="debouncedOnInputChange"
@keydown.native.down.prevent="navigateOptions('next')"
@ -56,10 +63,18 @@
<el-select-menu
ref="popper"
v-show="visible && emptyText !== false">
<ul class="el-select-dropdown__list" v-show="options.length > 0 && filteredOptionsCount > 0 && !loading">
<ul
class="el-select-dropdown__list"
:class="{ 'is-empty': !allowCreate && filteredOptionsCount === 0 }"
v-show="options.length > 0 && !loading">
<el-option
:value="query"
created
v-if="showNewOption">
</el-option>
<slot></slot>
</ul>
<p class="el-select-dropdown__empty" v-if="emptyText">{{ emptyText }}</p>
<p class="el-select-dropdown__empty" v-if="emptyText && !allowCreate">{{ emptyText }}</p>
</el-select-menu>
</transition>
</div>
@ -70,6 +85,7 @@
import Locale from 'element-ui/src/mixins/locale';
import ElInput from 'element-ui/packages/input';
import ElSelectMenu from './select-dropdown.vue';
import ElOption from './option.vue';
import ElTag from 'element-ui/packages/tag';
import debounce from 'throttle-debounce/debounce';
import Clickoutside from 'element-ui/src/utils/clickoutside';
@ -94,22 +110,18 @@
},
showCloseIcon() {
let criteria = this.clearable && this.inputHovering && !this.multiple && this.options.indexOf(this.selected) > -1;
let criteria = this.clearable &&
this.inputHovering &&
!this.multiple &&
this.value !== undefined &&
this.value !== '';
if (!this.$el) return false;
this.$nextTick(() => {
let icon = this.$el.querySelector('.el-input__icon');
if (icon) {
if (criteria) {
icon.addEventListener('click', this.deleteSelected);
addClass(icon, 'is-show-close');
} else {
icon.removeEventListener('click', this.deleteSelected);
removeClass(icon, 'is-show-close');
}
criteria ? addClass(icon, 'is-show-close') : removeClass(icon, 'is-show-close');
}
});
return criteria;
},
@ -129,12 +141,19 @@
}
}
return null;
},
showNewOption() {
let hasExistingOption = this.options.filter(option => !option.created)
.some(option => option.currentLabel === this.query);
return this.filterable && this.allowCreate && this.query !== '' && !hasExistingOption;
}
},
components: {
ElInput,
ElSelectMenu,
ElOption,
ElTag
},
@ -143,15 +162,19 @@
props: {
name: String,
value: {},
size: String,
disabled: Boolean,
clearable: Boolean,
filterable: Boolean,
allowCreate: Boolean,
loading: Boolean,
remote: Boolean,
remoteMethod: Function,
filterMethod: Function,
multiple: Boolean,
multipleLimit: {
type: Number,
default: 0
},
placeholder: {
type: String,
default() {
@ -163,22 +186,21 @@
data() {
return {
options: [],
selected: {},
selected: this.multiple ? [] : {},
isSelect: true,
inputLength: 20,
inputWidth: 0,
valueChangeBySelected: false,
cachedPlaceHolder: '',
optionsCount: 0,
filteredOptionsCount: 0,
dropdownUl: null,
visible: false,
selectedLabel: '',
selectInit: false,
hoverIndex: -1,
query: '',
voidRemoteQuery: false,
bottomOverflowBeforeHidden: 0,
topOverflowBeforeHidden: 0,
optionsAllDisabled: false,
inputHovering: false,
currentPlaceholder: ''
@ -191,79 +213,25 @@
},
value(val) {
if (this.valueChangeBySelected) {
this.valueChangeBySelected = false;
return;
}
this.$nextTick(() => {
if (this.multiple && Array.isArray(val)) {
this.$nextTick(() => {
this.resetInputHeight();
});
this.selectedInit = true;
this.selected = [];
this.currentPlaceholder = this.cachedPlaceHolder;
val.forEach(item => {
let option = this.options.filter(option => option.value === item)[0];
if (option) {
this.$emit('addOptionToValue', option);
}
});
}
if (!this.multiple) {
let option = this.options.filter(option => option.value === val)[0];
if (option) {
this.$emit('addOptionToValue', option);
} else {
this.selected = {};
this.selectedLabel = '';
}
}
this.resetHoverIndex();
});
},
selected(val, oldVal) {
if (this.multiple) {
if (this.selected.length > 0) {
this.resetInputHeight();
if (val.length > 0) {
this.currentPlaceholder = '';
} else {
this.currentPlaceholder = this.cachedPlaceHolder;
}
this.$nextTick(() => {
this.resetInputHeight();
});
if (this.selectedInit) {
this.selectedInit = false;
return;
}
this.valueChangeBySelected = true;
const result = val.map(item => item.value);
this.$emit('input', result);
this.$emit('change', result);
this.dispatch('ElFormItem', 'el.form.change', val);
if (this.filterable) {
this.query = '';
this.hoverIndex = -1;
this.$refs.input.focus();
this.inputLength = 20;
}
} else {
if (this.selectedInit) {
this.selectedInit = false;
return;
}
if (val.value === oldVal.value) return;
this.$emit('input', val.value);
this.$emit('change', val.value);
}
this.selected = this.getSelected();
if (this.filterable && !this.multiple) {
this.inputLength = 20;
}
this.$emit('change', val);
this.dispatch('ElFormItem', 'el.form.change', val);
},
query(val) {
this.$nextTick(() => {
this.broadcast('ElSelectDropdown', 'updatePopper');
});
this.broadcast('ElSelectDropdown', 'updatePopper');
this.hoverIndex = -1;
if (this.multiple && this.filterable) {
this.resetInputHeight();
}
@ -283,27 +251,22 @@
visible(val) {
if (!val) {
this.$refs.reference.$el.querySelector('input').blur();
if (this.$el.querySelector('.el-input__icon')) {
removeClass(this.$el.querySelector('.el-input__icon'), 'is-reverse');
}
this.handleIconHide();
this.broadcast('ElSelectDropdown', 'destroyPopper');
if (this.$refs.input) {
this.$refs.input.blur();
}
this.query = '';
this.selectedLabel = '';
this.resetHoverIndex();
if (!this.multiple) {
if (this.dropdownUl && this.selected.$el) {
this.bottomOverflowBeforeHidden = this.selected.$el.getBoundingClientRect().bottom - this.$refs.popper.$el.getBoundingClientRect().bottom;
}
this.getOverflows();
if (this.selected && this.selected.value) {
this.selectedLabel = this.selected.currentLabel;
}
}
} else {
let icon = this.$el.querySelector('.el-input__icon');
if (icon && !hasClass(icon, 'el-icon-circle-close')) {
addClass(this.$el.querySelector('.el-input__icon'), 'is-reverse');
}
this.handleIconShow();
this.broadcast('ElSelectDropdown', 'updatePopper');
if (this.filterable) {
this.query = this.selectedLabel;
@ -318,19 +281,88 @@
this.dropdownUl = [].filter.call(dropdownChildNodes, item => item.tagName === 'UL')[0];
}
if (!this.multiple && this.dropdownUl) {
if (this.bottomOverflowBeforeHidden > 0) {
this.dropdownUl.scrollTop += this.bottomOverflowBeforeHidden;
}
this.setOverflow();
}
}
},
options(val) {
this.optionsAllDisabled = val.length === val.filter(item => item.disabled === true).length;
if (this.multiple) {
this.resetInputHeight();
}
}
},
methods: {
handleIconHide() {
let icon = this.$el.querySelector('.el-input__icon');
if (icon) {
removeClass(icon, 'is-reverse');
}
},
handleIconShow() {
let icon = this.$el.querySelector('.el-input__icon');
if (icon && !hasClass(icon, 'el-icon-circle-close')) {
addClass(icon, 'is-reverse');
}
},
getOverflows() {
if (this.dropdownUl && this.selected && this.selected.$el) {
let selectedRect = this.selected.$el.getBoundingClientRect();
let popperRect = this.$refs.popper.$el.getBoundingClientRect();
this.bottomOverflowBeforeHidden = selectedRect.bottom - popperRect.bottom;
this.topOverflowBeforeHidden = selectedRect.top - popperRect.top;
}
},
setOverflow() {
if (this.bottomOverflowBeforeHidden > 0) {
this.$nextTick(() => {
this.dropdownUl.scrollTop += this.bottomOverflowBeforeHidden;
});
} else if (this.topOverflowBeforeHidden < 0) {
this.$nextTick(() => {
this.dropdownUl.scrollTop += this.topOverflowBeforeHidden;
});
}
},
getSelected() {
if (!this.multiple) {
let option = this.options.filter(option => option.value === this.value)[0] ||
{ value: this.value, currentLabel: this.value };
this.selectedLabel = option.currentLabel;
return option;
}
let result = [];
if (Array.isArray(this.value)) {
this.value.forEach(value => {
let option = this.options.filter(option => option.value === value)[0];
if (option) {
result.push(option);
} else {
result.push({
value: this.value,
currentLabel: value,
hitState: false
});
}
});
}
return result;
},
handleIconClick(event) {
if (this.iconClass === 'circle-close') {
this.deleteSelected(event);
} else {
this.toggleMenu();
}
},
handleMouseDown(event) {
if (event.target.tagName !== 'INPUT') return;
if (this.visible) {
@ -363,22 +395,7 @@
deletePrevTag(e) {
if (e.target.value.length <= 0 && !this.toggleLastOptionHitState()) {
this.selected.pop();
}
},
addOptionToValue(option, init) {
if (this.multiple) {
if (this.selected.indexOf(option) === -1 && (this.remote ? this.value.indexOf(option.value) === -1 : true)) {
this.selectedInit = !!init;
this.selected.push(option);
this.resetHoverIndex();
}
} else {
this.selectedInit = !!init;
this.selected = option;
this.selectedLabel = option.currentLabel;
this.hoverIndex = option.index;
this.value.pop();
}
},
@ -418,20 +435,24 @@
handleOptionSelect(option) {
if (!this.multiple) {
this.selected = option;
this.selectedLabel = option.currentLabel;
this.$emit('input', option.value);
this.visible = false;
} else {
let optionIndex = -1;
this.selected.forEach((item, index) => {
if (item === option || item.currentValue === option.currentValue) {
this.value.forEach((item, index) => {
if (item === option.value) {
optionIndex = index;
}
});
if (optionIndex > -1) {
this.selected.splice(optionIndex, 1);
} else {
this.selected.push(option);
this.value.splice(optionIndex, 1);
} else if (this.multipleLimit <= 0 || this.value.length < this.multipleLimit) {
this.value.push(option.value);
}
if (option.created) {
this.query = '';
this.inputLength = 20;
this.$refs.input.focus();
}
}
},
@ -450,6 +471,7 @@
this.visible = true;
return;
}
if (this.options.length === 0 || this.filteredOptionsCount === 0) return;
if (!this.optionsAllDisabled) {
if (direction === 'next') {
this.hoverIndex++;
@ -479,8 +501,10 @@
},
resetScrollTop() {
let bottomOverflowDistance = this.options[this.hoverIndex].$el.getBoundingClientRect().bottom - this.$refs.popper.$el.getBoundingClientRect().bottom;
let topOverflowDistance = this.options[this.hoverIndex].$el.getBoundingClientRect().top - this.$refs.popper.$el.getBoundingClientRect().top;
let bottomOverflowDistance = this.options[this.hoverIndex].$el.getBoundingClientRect().bottom -
this.$refs.popper.$el.getBoundingClientRect().bottom;
let topOverflowDistance = this.options[this.hoverIndex].$el.getBoundingClientRect().top -
this.$refs.popper.$el.getBoundingClientRect().top;
if (bottomOverflowDistance > 0) {
this.dropdownUl.scrollTop += bottomOverflowDistance;
}
@ -497,8 +521,6 @@
deleteSelected(event) {
event.stopPropagation();
this.selected = {};
this.selectedLabel = '';
this.$emit('input', '');
this.$emit('change', '');
this.visible = false;
@ -507,13 +529,13 @@
deleteTag(event, tag) {
let index = this.selected.indexOf(tag);
if (index > -1) {
this.selected.splice(index, 1);
this.value.splice(index, 1);
}
event.stopPropagation();
},
onInputChange() {
if (this.filterable && this.selectedLabel !== this.value) {
if (this.filterable) {
this.query = this.selectedLabel;
}
},
@ -535,9 +557,11 @@
created() {
this.cachedPlaceHolder = this.currentPlaceholder = this.placeholder;
if (this.multiple) {
this.selectedInit = true;
this.selected = [];
if (this.multiple && !Array.isArray(this.value)) {
this.$emit('input', []);
}
if (!this.multiple && (!this.value || Array.isArray(this.value))) {
this.$emit('input', '');
}
if (this.remote) {
this.voidRemoteQuery = true;
@ -547,20 +571,18 @@
this.onInputChange();
});
this.$on('addOptionToValue', this.addOptionToValue);
this.$on('handleOptionClick', this.handleOptionSelect);
this.$on('onOptionDestroy', this.onOptionDestroy);
},
mounted() {
if (this.multiple && Array.isArray(this.value) && this.value.length > 0) {
this.currentPlaceholder = '';
}
addResizeListener(this.$el, this.resetInputWidth);
if (this.remote && this.multiple && Array.isArray(this.value)) {
this.selected = this.options.reduce((prev, curr) => {
return this.value.indexOf(curr.value) > -1 ? prev.concat(curr) : prev;
}, []);
this.$nextTick(() => {
this.resetInputHeight();
});
this.selected = this.getSelected();
if (this.remote && this.multiple) {
this.resetInputHeight();
}
this.$nextTick(() => {
if (this.$refs.reference.$el) {

View File

@ -23,6 +23,8 @@
width: 110px;
input {
padding-right: 25px;
border-radius: var(--border-radius-small);
height: 28px;
}
}

View File

@ -51,5 +51,9 @@
max-height: var(--select-dropdown-max-height);
box-sizing: border-box;
overflow-y: auto;
@when empty {
padding: 0;
}
}
}

View File

@ -12,13 +12,6 @@
display: block;
position: relative;
@when small {
& input {
border-radius: var(--border-radius-small);
height: 28px;
}
}
&:hover {
.el-input__inner {
border-color: var(--select-border-color-hover);

View File

@ -3,9 +3,10 @@ import Select from 'packages/select';
describe('Select', () => {
const getSelectVm = (configs = {}, options) => {
['multiple', 'clearable', 'filterable', 'remote'].forEach(config => {
['multiple', 'clearable', 'filterable', 'allowCreate', 'remote'].forEach(config => {
configs[config] = configs[config] || false;
});
configs.multipleLimit = configs.multipleLimit || 0;
if (!options) {
options = [{
value: '选项1',
@ -35,8 +36,10 @@ describe('Select', () => {
<el-select
v-model="value"
:multiple="multiple"
:multiple-limit="multipleLimit"
:clearable="clearable"
:filterable="filterable"
:allow-create="allowCreate"
:filterMethod="filterMethod"
:remote="remote"
:loading="loading"
@ -55,8 +58,10 @@ describe('Select', () => {
return {
options,
multiple: configs.multiple,
multipleLimit: configs.multipleLimit,
clearable: configs.clearable,
filterable: configs.filterable,
allowCreate: configs.allowCreate,
loading: false,
filterMethod: configs.filterMethod && configs.filterMethod(this),
remote: configs.remote,
@ -349,6 +354,23 @@ describe('Select', () => {
}, 100);
});
it('allow create', done => {
vm = getSelectVm({ filterable: true, allowCreate: true });
const select = vm.$children[0];
select.selectedLabel = 'new';
select.onInputChange();
select.visible = true;
setTimeout(() => {
const options = document.querySelectorAll('.el-select-dropdown__item span');
const target = [].filter.call(options, option => option.innerText === 'new');
target[0].click();
setTimeout(() => {
expect(select.value.indexOf('new') > -1).to.true;
done();
}, 50);
}, 50);
});
it('multiple select', done => {
vm = getSelectVm({ multiple: true });
const options = vm.$el.querySelectorAll('.el-select-dropdown__item');
@ -368,6 +390,20 @@ describe('Select', () => {
}, 100);
});
it('multiple limit', done => {
vm = getSelectVm({ multiple: true, multipleLimit: 1 });
const options = vm.$el.querySelectorAll('.el-select-dropdown__item');
options[1].click();
setTimeout(() => {
expect(vm.value.indexOf('选项2') > -1).to.true;
options[3].click();
setTimeout(() => {
expect(vm.value.indexOf('选项4')).to.equal(-1);
done();
}, 50);
}, 50);
});
it('multiple remote search', done => {
const remoteMethod = vm => {
return query => {
@ -387,21 +423,23 @@ describe('Select', () => {
remoteMethod
});
const select = vm.$children[0];
select.query = '面';
setTimeout(() => {
expect(select.filteredOptionsCount).to.equal(1);
select.query = '';
select.options[0].$el.click();
vm.$nextTick(() => {
expect(vm.value[0]).to.equal('选项4');
select.deletePrevTag({ target: select.$refs.input });
select.deletePrevTag({ target: select.$refs.input });
select.resetInputState({ keyCode: 1 });
vm.$nextTick(() => {
select.query = '面';
setTimeout(() => {
expect(select.filteredOptionsCount).to.equal(1);
select.query = '';
select.options[0].$el.click();
vm.$nextTick(() => {
expect(vm.value.length).to.equal(0);
done();
expect(vm.value[0]).to.equal('选项4');
select.deletePrevTag({ target: select.$refs.input });
select.deletePrevTag({ target: select.$refs.input });
select.resetInputState({ keyCode: 1 });
vm.$nextTick(() => {
expect(vm.value.length).to.equal(0);
done();
});
});
});
}, 250);
}, 250);
});
});
});