feat: migrate TreeSelect component

This commit is contained in:
chenjiahan 2020-07-12 15:34:08 +08:00
parent d04c8c1e24
commit 7a197fc90f
13 changed files with 1281 additions and 24 deletions

View File

@ -1,6 +1,24 @@
# 不兼容更新 # 不兼容更新
## 重命名徽标属性 ## v-model API 变更
以下改动是为了适配 Vue 3 的 v-model API 用法变更:
- Circle: `v-model` 重命名为 `v-model:currentRate`
- Popup: `v-model` 重命名为 `v-model:show`
- Switch: v-model 对应的属性 `value` 重命名为 `modelValue`,事件由 `input` 重命名为 `update:modelValue`
- Sidebar: v-model 对应的属性 `activeKey` 重命名为 `modelValue`,事件由 `input` 重命名为 `update:modelValue`
- TreeSelect: `active-id.sync` 重命名为 `v-model:active-id`
- TreeSelect: `main-active-index.sync` 重命名为 `v-model:main-active-index`
## API 命名调整
以下改动是为了规范 API 命名:
- TreeSelect: `navclick` 事件重命名为 `click-nav`
- TreeSelect: `itemclick` 事件重命名为 `click-item`
### 重命名徽标属性
在之前的版本中,我们通过 info 属性来展示图标右上角的徽标信息,为了表达更符合社区的命名习惯,我们将这个属性重命名为 badge影响以下组件 在之前的版本中,我们通过 info 属性来展示图标右上角的徽标信息,为了表达更符合社区的命名习惯,我们将这个属性重命名为 badge影响以下组件
@ -14,13 +32,6 @@
同时内部使用的 Info 组件也会重命名为 Badge。 同时内部使用的 Info 组件也会重命名为 Badge。
## v-model API 变更
- Circle: `v-model` 调整为 `v-model:currentRate`
- Popup: `v-model` 调整为 `v-model:show`
- Switch: v-model 对应的属性 `value` 调整为 `modelValue`,事件由 `input` 调整为 `update:modelValue`
- Sidebar: v-model 对应的属性 `activeKey` 调整为 `modelValue`,事件由 `input` 调整为 `update:modelValue`
## 废弃个别组件 ## 废弃个别组件
- SwitchCell: 移除此组件,可以直接使用 Cell 和 Switch 组件代替 - SwitchCell: 移除此组件,可以直接使用 Cell 和 Switch 组件代替

View File

@ -0,0 +1,170 @@
# TreeSelect
### Install
```js
import Vue from 'vue';
import { TreeSelect } from 'vant';
Vue.use(TreeSelect);
```
## Usage
### Radio Mode
```html
<van-tree-select
v-model:active-id="activeId"
v-model:main-active-index="activeIndex"
:items="items"
/>
```
```js
export default {
data() {
return {
items,
activeId: 1,
activeIndex: 0,
};
},
};
```
### Multiple Mode
```html
<van-tree-select
v-model:active-id="activeIds"
v-model:main-active-index="activeIndex"
:items="items"
/>
```
```js
export default {
data() {
return {
items,
activeIds: [1, 2],
activeIndex: 0,
};
},
};
```
### Custom Content
```html
<van-tree-select v-model:main-active-index="active" height="55vw" :items="items">
<template #content>
<van-image
v-if="active === 0"
src="https://img.yzcdn.cn/vant/apple-1.jpg"
/>
<van-image
v-if="active === 1"
src="https://img.yzcdn.cn/vant/apple-2.jpg"
/>
</template>
</van-tree-select>
```
```js
export default {
data() {
return {
active: 0,
items: [{ text: 'Group 1' }, { text: 'Group 2' }],
};
},
};
```
### Show Badge
```html
<van-tree-select
v-model:main-active-index="activeIndex"
height="55vw"
:items="items"
/>
```
```js
export default {
data() {
return {
activeIndex: 0,
items: [
{ text: 'Group 1', children: [], dot: true },
{ text: 'Group 2', children: [], badge: 5 },
],
};
},
};
```
## API
### Props
| Attribute | Description | Type | Default |
| --- | --- | --- | --- |
| items | Required datasets for the component | _Item[]_ | `[]` |
| height | Height | _number \| string_ | `300` |
| main-active-index | The index of selected parent node | _number \| string_ | `0` |
| active-id | Id of selected item | _number \| string \|<br>(number \| string)[]_ | `0` |
| max `v2.2.0` | Maximum number of selected items | _number \| string_ | `Infinity` |
| selected-icon `v2.9.0` | Selected icon | _string_ | `success` |
### Events
| Event | Description | Arguments |
| --- | --- | --- |
| click-nav | triggered when parent node is selected | index: index of selected parent |
| click-item | triggered when item is selected | data: selected item |
### Slots
| Name | Description |
| ------- | -------------------- |
| content | Custom right content |
### Data Structure of Item
`items` should be an array contains specified tree objects.
In every tree object, `text` property defines `id` stands for the unique key while the `children` contains sub-tree objects.
```js
[
{
// name of the parent node
text: 'Group 1',
// badge
badge: 3,
// Whether to show red dot
dot: true,
// ClassName of parent node
className: 'my-class',
// leaves of this parent node
children: [
{
// name of the leaf node
text: 'Washington',
// id of the leaf node, component highlights leaf node by comparing the activeId with this.
id: 1,
// disable options
disabled: true,
},
{
text: 'Baltimore',
id: 2,
},
],
},
];
```

View File

@ -0,0 +1,180 @@
# TreeSelect 分类选择
### 引入
```js
import Vue from 'vue';
import { TreeSelect } from 'vant';
Vue.use(TreeSelect);
```
## 代码演示
### 单选模式
`item`为分类显示所需的数据,数据格式见下方示例。`main-active-index`表示左侧高亮选项的索引,`active-id`表示右侧高亮选项的 id
```html
<van-tree-select
v-model:active-id="activeId"
v-model:main-active-index="activeIndex"
:items="items"
/>
```
```js
export default {
data() {
return {
items,
activeId: 1,
activeIndex: 0,
};
},
};
```
### 多选模式
`active-id`为数组格式时,可以选中多个右侧选项
```html
<van-tree-select
v-model:active-id="activeIds"
v-model:main-active-index="activeIndex"
:items="items"
/>
```
```js
export default {
data() {
return {
items,
activeIds: [1, 2],
activeIndex: 0,
};
},
};
```
### 自定义内容
通过`content`插槽可以自定义右侧区域的内容
```html
<van-tree-select
v-model:main-active-index="active"
height="55vw"
:items="items"
>
<template #content>
<van-image
v-if="active === 0"
src="https://img.yzcdn.cn/vant/apple-1.jpg"
/>
<van-image
v-if="active === 1"
src="https://img.yzcdn.cn/vant/apple-2.jpg"
/>
</template>
</van-tree-select>
```
```js
export default {
data() {
return {
active: 0,
items: [{ text: '分组 1' }, { text: '分组 2' }],
};
},
};
```
### 徽标提示
设置`dot`属性后,会在图标右上角展示一个小红点。设置`badge`属性后,会在图标右上角展示相应的徽标
```html
<van-tree-select
v-model:main-active-index="activeIndex"
height="55vw"
:items="items"
/>
```
```js
export default {
data() {
return {
activeIndex: 0,
items: [
{ text: '浙江', children: [], dot: true },
{ text: '江苏', children: [], badge: 5 },
],
};
},
};
```
## API
### Props
| 参数 | 说明 | 类型 | 默认值 |
| --- | --- | --- | --- |
| items | 分类显示所需的数据 | _Item[]_ | `[]` |
| height | 高度,默认单位为`px` | _number \| string_ | `300` |
| main-active-index | 左侧选中项的索引 | _number \| string_ | `0` |
| active-id | 右侧选中项的 id支持传入数组 | _number \| string \|<br>(number \| string)[]_ | `0` |
| max `v2.2.0` | 右侧项最大选中个数 | _number \| string_ | `Infinity` |
| selected-icon `v2.9.0` | 自定义右侧栏选中状态的图标 | _string_ | `success` |
### Events
| 事件名 | 说明 | 回调参数 |
| ---------- | -------------------- | ------------------------- |
| click-nav | 点击左侧导航时触发 | index被点击的导航的索引 |
| click-item | 点击右侧选择项时触发 | data: 该点击项的数据 |
### Slots
| 名称 | 说明 |
| ------- | ------------------ |
| content | 自定义右侧区域内容 |
### Item 数据结构
`items` 整体为一个数组,数组内包含一系列描述分类的对象,每个分类里,`text`表示当前分类的名称,`children`表示分类里的可选项。
```js
[
{
// 导航名称
text: '所有城市',
// 导航名称右上角徽标2.5.6 版本开始支持
badge: 3,
// 是否在导航名称右上角显示小红点
dot: true,
// 导航节点额外类名
className: 'my-class',
// 该导航下所有的可选项
children: [
{
// 名称
text: '温州',
// id作为匹配选中状态的标识符
id: 1,
// 禁用选项
disabled: true,
},
{
text: '杭州',
id: 2,
},
],
},
];
```

View File

@ -0,0 +1,65 @@
const group1 = [
{
text: 'Delaware',
id: 1,
},
{
text: 'Florida',
id: 2,
},
{
text: 'Georqia',
id: 3,
disabled: true,
},
{
text: 'Indiana',
id: 4,
},
];
const group2 = [
{
text: 'Alabama',
id: 5,
},
{
text: 'Kansas',
id: 6,
},
{
text: 'Louisiana',
id: 7,
},
{
text: 'Texas',
id: 8,
},
];
const group3 = [
{
text: 'Alabama',
id: 9,
},
{
text: 'Kansas',
id: 10,
},
];
export const enUSData = [
{
text: 'Group 1',
children: group1,
},
{
text: 'Group 2',
children: group2,
},
{
text: 'Group 3',
disabled: true,
children: group3,
},
];

View File

@ -0,0 +1,65 @@
const zhejiang = [
{
text: '杭州',
id: 1,
},
{
text: '温州',
id: 2,
},
{
text: '宁波',
id: 3,
disabled: true,
},
{
text: '义乌',
id: 4,
},
];
const jiangsu = [
{
text: '南京',
id: 5,
},
{
text: '无锡',
id: 6,
},
{
text: '徐州',
id: 7,
},
{
text: '苏州',
id: 8,
},
];
const fujian = [
{
text: '泉州',
id: 9,
},
{
text: '厦门',
id: 10,
},
];
export const zhCNData = [
{
text: '浙江',
children: zhejiang,
},
{
text: '江苏',
children: jiangsu,
},
{
text: '福建',
disabled: true,
children: fujian,
},
];

View File

@ -0,0 +1,107 @@
<template>
<demo-section>
<demo-block :title="t('radioMode')">
<van-tree-select
v-model:active-id="activeId"
v-model:main-active-index="activeIndex"
:items="items"
/>
</demo-block>
<demo-block :title="t('multipleMode')">
<van-tree-select
v-model:active-id="activeIds"
v-model:main-active-index="activeIndex2"
:items="items"
/>
</demo-block>
<demo-block :title="t('customContent')">
<van-tree-select
v-model:main-active-index="activeIndex3"
height="55vw"
:items="simpleItems"
>
<template slot="content">
<van-image
v-if="activeIndex3 === 0"
:show-loading="false"
src="https://img.yzcdn.cn/vant/apple-1.jpg"
/>
<van-image
v-if="activeIndex3 === 1"
:show-loading="false"
src="https://img.yzcdn.cn/vant/apple-2.jpg"
/>
</template>
</van-tree-select>
</demo-block>
<demo-block :title="t('showBadge')">
<van-tree-select
v-model:active-id="activeId2"
v-model:main-active-index="activeIndex4"
height="55vw"
:items="badgeItems"
/>
</demo-block>
</demo-section>
</template>
<script>
import { zhCNData } from './data-zh';
import { enUSData } from './data-en';
import { deepClone } from '../../utils/deep-clone';
export default {
i18n: {
'zh-CN': {
showBadge: '徽标提示',
radioMode: '单选模式',
multipleMode: '多选模式',
customContent: '自定义内容',
data: zhCNData,
dataSimple: [{ text: '分组 1' }, { text: '分组 2' }],
},
'en-US': {
showBadge: 'Show Badge',
radioMode: 'Radio Mode',
multipleMode: 'Multiple Mode',
customContent: 'Custom Content',
data: enUSData,
dataSimple: [{ text: 'Group 1' }, { text: 'Group 2' }],
},
},
data() {
return {
activeId: 1,
activeId2: 1,
activeIds: [1, 2],
activeIndex: 0,
activeIndex2: 0,
activeIndex3: 0,
activeIndex4: 0,
};
},
computed: {
items() {
return this.t('data');
},
simpleItems() {
return this.t('dataSimple');
},
badgeItems() {
const data = deepClone(this.t('data')).slice(0, 2);
data[0].dot = true;
data[1].badge = 5;
return data;
},
},
};
</script>

View File

@ -0,0 +1,128 @@
// Utils
import { createNamespace, addUnit, isDef } from '../utils';
// Components
import Icon from '../icon';
import Sidebar from '../sidebar';
import SidebarItem from '../sidebar-item';
const [createComponent, bem] = createNamespace('tree-select');
export default createComponent({
props: {
max: {
type: [Number, String],
default: Infinity,
},
items: {
type: Array,
default: () => [],
},
height: {
type: [Number, String],
default: 300,
},
activeId: {
type: [Number, String, Array],
default: 0,
},
selectedIcon: {
type: String,
default: 'success',
},
mainActiveIndex: {
type: [Number, String],
default: 0,
},
},
emits: [
'click-nav',
'click-item',
'update:activeId',
'update:mainActiveIndex',
],
setup(props, { emit, slots }) {
return function () {
const { items, height, activeId, selectedIcon, mainActiveIndex } = props;
const selectedItem = items[+mainActiveIndex] || {};
const subItems = selectedItem.children || [];
const isMultiple = Array.isArray(activeId);
function isActiveItem(id) {
return isMultiple ? activeId.indexOf(id) !== -1 : activeId === id;
}
const Navs = items.map((item) => (
<SidebarItem
dot={item.dot}
info={isDef(item.badge) ? item.badge : item.info}
title={item.text}
disabled={item.disabled}
class={[bem('nav-item'), item.className]}
/>
));
function Content() {
if (slots.content) {
return slots.content();
}
return subItems.map((item) => (
<div
key={item.id}
class={[
'van-ellipsis',
bem('item', {
active: isActiveItem(item.id),
disabled: item.disabled,
}),
]}
onClick={() => {
if (!item.disabled) {
let newActiveId = item.id;
if (isMultiple) {
newActiveId = activeId.slice();
const index = newActiveId.indexOf(item.id);
if (index !== -1) {
newActiveId.splice(index, 1);
} else if (newActiveId.length < props.max) {
newActiveId.push(item.id);
}
}
emit('update:activeId', newActiveId);
emit('click-item', item);
}
}}
>
{item.text}
{isActiveItem(item.id) && (
<Icon name={selectedIcon} class={bem('selected')} />
)}
</div>
));
}
return (
<div class={bem()} style={{ height: addUnit(height) }}>
<Sidebar
class={bem('nav')}
modelValue={mainActiveIndex}
onChange={(index) => {
emit('update:mainActiveIndex', index);
emit('click-nav', index);
}}
>
{Navs}
</Sidebar>
<div class={bem('content')}>{Content()}</div>
</div>
);
};
},
});

View File

@ -0,0 +1,51 @@
@import '../style/var';
.van-tree-select {
position: relative;
display: flex;
font-size: @tree-select-font-size;
user-select: none;
&__nav {
flex: 1;
overflow-y: auto;
background-color: @tree-select-nav-background-color;
-webkit-overflow-scrolling: touch;
&-item {
padding: @tree-select-nav-item-padding;
}
}
&__content {
flex: 2;
overflow-y: auto;
background-color: @tree-select-content-background-color;
-webkit-overflow-scrolling: touch;
}
&__item {
position: relative;
padding: 0 32px 0 @padding-md;
font-weight: @font-weight-bold;
line-height: @tree-select-item-height;
cursor: pointer;
&--active {
color: @tree-select-item-active-color;
}
&--disabled {
color: @tree-select-item-disabled-color;
cursor: not-allowed;
}
}
&__selected {
position: absolute;
top: 50%;
right: @padding-md;
margin-top: -@padding-xs;
font-size: @tree-select-item-selected-size;
}
}

View File

@ -0,0 +1,90 @@
// Jest Snapshot v1, https://goo.gl/fbAQLP
exports[`renders demo correctly 1`] = `
<div>
<div>
<div class="van-tree-select" style="height: 300px;">
<div class="van-sidebar van-tree-select__nav"><a class="van-sidebar-item van-sidebar-item--select van-tree-select__nav-item">
<div class="van-sidebar-item__text">浙江
<!---->
</div>
</a><a class="van-sidebar-item van-tree-select__nav-item">
<div class="van-sidebar-item__text">江苏
<!---->
</div>
</a><a class="van-sidebar-item van-sidebar-item--disabled van-tree-select__nav-item">
<div class="van-sidebar-item__text">福建
<!---->
</div>
</a></div>
<div class="van-tree-select__content">
<div class="van-ellipsis van-tree-select__item van-tree-select__item--active">杭州<i class="van-icon van-icon-success van-tree-select__selected">
<!----></i></div>
<div class="van-ellipsis van-tree-select__item">温州</div>
<div class="van-ellipsis van-tree-select__item van-tree-select__item--disabled">宁波</div>
<div class="van-ellipsis van-tree-select__item">义乌</div>
</div>
</div>
</div>
<div>
<div class="van-tree-select" style="height: 300px;">
<div class="van-sidebar van-tree-select__nav"><a class="van-sidebar-item van-sidebar-item--select van-tree-select__nav-item">
<div class="van-sidebar-item__text">浙江
<!---->
</div>
</a><a class="van-sidebar-item van-tree-select__nav-item">
<div class="van-sidebar-item__text">江苏
<!---->
</div>
</a><a class="van-sidebar-item van-sidebar-item--disabled van-tree-select__nav-item">
<div class="van-sidebar-item__text">福建
<!---->
</div>
</a></div>
<div class="van-tree-select__content">
<div class="van-ellipsis van-tree-select__item van-tree-select__item--active">杭州<i class="van-icon van-icon-success van-tree-select__selected">
<!----></i></div>
<div class="van-ellipsis van-tree-select__item van-tree-select__item--active">温州<i class="van-icon van-icon-success van-tree-select__selected">
<!----></i></div>
<div class="van-ellipsis van-tree-select__item van-tree-select__item--disabled">宁波</div>
<div class="van-ellipsis van-tree-select__item">义乌</div>
</div>
</div>
</div>
<div>
<div class="van-tree-select" style="height: 55vw;">
<div class="van-sidebar van-tree-select__nav"><a class="van-sidebar-item van-sidebar-item--select van-tree-select__nav-item">
<div class="van-sidebar-item__text">分组 1
<!---->
</div>
</a><a class="van-sidebar-item van-tree-select__nav-item">
<div class="van-sidebar-item__text">分组 2
<!---->
</div>
</a></div>
<div class="van-tree-select__content">
<div class="van-image"><img src="https://img.yzcdn.cn/vant/apple-1.jpg" class="van-image__img"></div>
<!---->
</div>
</div>
</div>
<div>
<div class="van-tree-select" style="height: 55vw;">
<div class="van-sidebar van-tree-select__nav"><a class="van-sidebar-item van-sidebar-item--select van-tree-select__nav-item">
<div class="van-sidebar-item__text">浙江<div class="van-info van-info--dot van-sidebar-item__info"></div>
</div>
</a><a class="van-sidebar-item van-tree-select__nav-item">
<div class="van-sidebar-item__text">江苏<div class="van-info van-sidebar-item__info">5</div>
</div>
</a></div>
<div class="van-tree-select__content">
<div class="van-ellipsis van-tree-select__item van-tree-select__item--active">杭州<i class="van-icon van-icon-success van-tree-select__selected">
<!----></i></div>
<div class="van-ellipsis van-tree-select__item">温州</div>
<div class="van-ellipsis van-tree-select__item van-tree-select__item--disabled">宁波</div>
<div class="van-ellipsis van-tree-select__item">义乌</div>
</div>
</div>
</div>
</div>
`;

View File

@ -0,0 +1,41 @@
// Jest Snapshot v1, https://goo.gl/fbAQLP
exports[`content slot 1`] = `
<div class="van-tree-select" style="height: 300px;">
<div class="van-sidebar van-tree-select__nav"><a class="van-sidebar-item van-sidebar-item--select van-tree-select__nav-item">
<div class="van-sidebar-item__text">group1
<!---->
</div>
</a></div>
<div class="van-tree-select__content">Custom Content</div>
</div>
`;
exports[`empty list 1`] = `
<div class="van-tree-select" style="height: 300px;">
<div class="van-sidebar van-tree-select__nav"></div>
<div class="van-tree-select__content"></div>
</div>
`;
exports[`height prop 1`] = `
<div class="van-tree-select" style="height: 100vh;">
<div class="van-sidebar van-tree-select__nav"></div>
<div class="van-tree-select__content"></div>
</div>
`;
exports[`nav info 1`] = `
<div class="van-tree-select" style="height: 300px;">
<div class="van-sidebar van-tree-select__nav"><a class="van-sidebar-item van-sidebar-item--select van-tree-select__nav-item">
<div class="van-sidebar-item__text">group1<div class="van-info van-sidebar-item__info">3</div>
</div>
</a></div>
<div class="van-tree-select__content"></div>
</div>
`;
exports[`selected-icon prop 1`] = `
<div class="van-ellipsis van-tree-select__item van-tree-select__item--active">city1<i class="van-icon van-icon-foo van-tree-select__selected">
<!----></i></div>
`;

View File

@ -0,0 +1,4 @@
import Demo from '../demo';
import { snapshotDemo } from '../../../test/demo';
snapshotDemo(Demo);

View File

@ -0,0 +1,345 @@
import TreeSelect from '..';
import { mount } from '../../../test';
test('empty list', () => {
expect(mount(TreeSelect)).toMatchSnapshot();
});
const mockItem = {
text: 'city1',
id: 1,
};
const mockItem2 = {
text: 'city2',
id: 2,
};
const mockItems = [
{
text: 'group1',
children: [mockItem],
},
{
text: 'group2',
children: [mockItem],
},
];
test('click-nav event', () => {
const onNavClick = jest.fn();
const onClickNav = jest.fn();
const wrapper = mount(TreeSelect, {
propsData: {
items: mockItems,
},
context: {
on: {
navclick: onNavClick,
'click-nav': onClickNav,
},
},
});
const navItems = wrapper.findAll('.van-tree-select__nav-item');
navItems.at(1).trigger('click');
expect(onNavClick).toHaveBeenCalledWith(1);
expect(onClickNav).toHaveBeenCalledWith(1);
});
test('click-item event', () => {
const onItemClick = jest.fn();
const onClickItem = jest.fn();
const wrapper = mount(TreeSelect, {
propsData: {
items: mockItems,
},
context: {
on: {
itemclick: onItemClick,
'click-item': onClickItem,
},
},
});
const items = wrapper.findAll('.van-tree-select__item');
items.at(0).trigger('click');
expect(onItemClick).toHaveBeenCalledWith(mockItem);
expect(onClickItem).toHaveBeenCalledWith(mockItem);
});
test('click disabled nav', () => {
const onClickNav = jest.fn();
const wrapper = mount(TreeSelect, {
propsData: {
items: [
{
text: 'group1',
children: [mockItem],
disabled: true,
},
],
},
context: {
on: {
'click-nav': onClickNav,
},
},
});
const items = wrapper.findAll('.van-tree-select__nav-item');
items.at(0).trigger('click');
expect(onClickNav).toHaveBeenCalledTimes(0);
});
test('click disabled item', () => {
const onClickItem = jest.fn();
const wrapper = mount(TreeSelect, {
propsData: {
items: [
{
text: 'group1',
children: [
{
...mockItem,
disabled: true,
},
],
},
],
},
context: {
on: {
'click-item': onClickItem,
},
},
});
const items = wrapper.findAll('.van-tree-select__item');
items.at(0).trigger('click');
expect(onClickItem).toHaveBeenCalledTimes(0);
});
test('content slot', () => {
const wrapper = mount(TreeSelect, {
propsData: {
items: [
{
text: 'group1',
},
],
},
scopedSlots: {
content: () => 'Custom Content',
},
});
expect(wrapper).toMatchSnapshot();
});
test('height prop', () => {
const wrapper = mount(TreeSelect, {
propsData: {
height: '100vh',
},
});
expect(wrapper).toMatchSnapshot();
});
test('nav info', () => {
const wrapper = mount(TreeSelect, {
propsData: {
items: [
{
text: 'group1',
info: 3,
},
],
},
});
expect(wrapper).toMatchSnapshot();
});
test('use sync modifier in main-active-index', () => {
const wrapper = mount({
template: `
<van-tree-select
:items="items"
:main-active-index.sync="mainActiveIndex"
/>
`,
data() {
return {
mainActiveIndex: -1,
items: mockItems,
};
},
});
const navItems = wrapper.findAll('.van-tree-select__nav-item');
navItems.at(0).trigger('click');
expect(wrapper.vm.mainActiveIndex).toEqual(0);
});
test('use sync modifier in active-id', () => {
const wrapper = mount({
template: `
<van-tree-select
:items="items"
:main-active-index="0"
:active-id.sync="activeId"
/>
`,
data() {
return {
activeId: mockItem.id,
mainActiveIndex: 0,
items: [
{
text: 'group1',
children: [mockItem, mockItem2],
},
],
};
},
});
const items = wrapper.findAll('.van-tree-select__item');
items.at(1).trigger('click');
expect(wrapper.vm.activeId).toEqual(mockItem2.id);
});
test('multiple select', () => {
const wrapper = mount({
template: `
<van-tree-select
:items="items"
:main-active-index="0"
:active-id.sync="activeId"
/>
`,
data() {
return {
activeId: [],
mainActiveIndex: 0,
items: [
{
text: 'group1',
children: [mockItem, mockItem2],
},
],
};
},
});
const items = wrapper.findAll('.van-tree-select__item');
items.at(0).trigger('click');
items.at(1).trigger('click');
expect(wrapper.vm.activeId).toEqual([mockItem.id, mockItem2.id]);
items.at(0).trigger('click');
items.at(1).trigger('click');
expect(wrapper.vm.activeId).toEqual([]);
});
test('max prop', () => {
const wrapper = mount({
template: `
<van-tree-select
:max="1"
:items="items"
:main-active-index="0"
:active-id.sync="activeId"
/>
`,
data() {
return {
activeId: [],
items: [
{
text: 'group1',
children: [mockItem, mockItem2],
},
],
};
},
});
const items = wrapper.findAll('.van-tree-select__item');
items.at(0).trigger('click');
items.at(1).trigger('click');
expect(wrapper.vm.activeId).toEqual([mockItem.id]);
});
test('className of nav', () => {
const wrapper = mount(TreeSelect, {
propsData: {
mainActiveIndex: 0,
items: [
{
text: 'group1',
className: 'my-class',
children: [],
},
],
},
});
const items = wrapper.findAll('.van-tree-select__nav-item');
expect(items.at(0).element.classList.contains('my-class')).toBeTruthy();
});
test('should sync value before trigger click-item event', (done) => {
const wrapper = mount({
template: `
<van-tree-select
:items="items"
:main-active-index="0"
:active-id.sync="activeId"
@click-item="onClickItem"
/>
`,
data() {
return {
activeId: mockItem.id,
mainActiveIndex: 0,
items: [
{
text: 'group1',
children: [mockItem, mockItem2],
},
],
};
},
methods: {
onClickItem() {
expect(wrapper.vm.activeId).toEqual(mockItem2.id);
done();
},
},
});
const items = wrapper.findAll('.van-tree-select__item');
items.at(1).trigger('click');
});
test('selected-icon prop', () => {
const wrapper = mount(TreeSelect, {
propsData: {
items: mockItems,
activeId: 1,
mainActiveIndex: 0,
selectedIcon: 'foo',
},
});
expect(wrapper.find('.van-tree-select__item')).toMatchSnapshot();
});

View File

@ -302,10 +302,10 @@ module.exports = {
path: 'pagination', path: 'pagination',
title: 'Pagination 分页', title: 'Pagination 分页',
}, },
// { {
// path: 'sidebar', path: 'sidebar',
// title: 'Sidebar 侧边导航', title: 'Sidebar 侧边导航',
// }, },
// { // {
// path: 'tab', // path: 'tab',
// title: 'Tab 标签页', // title: 'Tab 标签页',
@ -314,10 +314,10 @@ module.exports = {
// path: 'tabbar', // path: 'tabbar',
// title: 'Tabbar 标签栏', // title: 'Tabbar 标签栏',
// }, // },
// { {
// path: 'tree-select', path: 'tree-select',
// title: 'TreeSelect 分类选择', title: 'TreeSelect 分类选择',
// }, },
], ],
}, },
{ {
@ -636,10 +636,10 @@ module.exports = {
path: 'pagination', path: 'pagination',
title: 'Pagination', title: 'Pagination',
}, },
// { {
// path: 'sidebar', path: 'sidebar',
// title: 'Sidebar', title: 'Sidebar',
// }, },
// { // {
// path: 'tab', // path: 'tab',
// title: 'Tab', // title: 'Tab',
@ -648,10 +648,10 @@ module.exports = {
// path: 'tabbar', // path: 'tabbar',
// title: 'Tabbar', // title: 'Tabbar',
// }, // },
// { {
// path: 'tree-select', path: 'tree-select',
// title: 'TreeSelect', title: 'TreeSelect',
// }, },
], ],
}, },
{ {