Эх сурвалжийг харах

Table: add expand row feature.

furybean 8 жил өмнө
parent
commit
b569b314b8

+ 93 - 2
examples/docs/en-US/table.md

@@ -1354,6 +1354,94 @@ Customize table column so it can be integrated with other components.
 ```
 :::
 
+### Expandable row
+
+When the row content is too long and you do not want to display the horizontal scroll bar, you can use the expandable row feature.
+:::demo Activate expandable row by adding type="expand" and `inline-template` attribute,The template for `el-table-column` will be rendered as the contents of the expanded row, you can access the same attributes as the` inline-template`。
+```html
+<template>
+  <el-table
+    :data="tableData3"
+    style="width: 100%">
+    <el-table-column type="expand" inline-template>
+      <div>
+      <p>State: {{ row.state }}</p>
+      <p>City: {{ row.city }}</p>
+      <p>Address: {{ row.address }}</p>
+      <p>Zip: {{ row.zip }}</p>
+      </div>
+    </el-table-column>
+    <el-table-column
+      label="Date"
+      prop="date">
+    </el-table-column>
+    <el-table-column
+      label="Name"
+      prop="name">
+    </el-table-column>
+  </el-table>
+</template>
+
+<script>
+  export default {
+    data() {
+      return {
+        tableData3: [{
+          date: '2016-05-03',
+          name: 'Tom',
+          state: 'California',
+          city: 'Los Angeles',
+          address: 'No. 189, Grove St, Los Angeles',
+          zip: 'CA 90036'
+        }, {
+          date: '2016-05-02',
+          name: 'Tom',
+          state: 'California',
+          city: 'Los Angeles',
+          address: 'No. 189, Grove St, Los Angeles',
+          zip: 'CA 90036'
+        }, {
+          date: '2016-05-04',
+          name: 'Tom',
+          state: 'California',
+          city: 'Los Angeles',
+          address: 'No. 189, Grove St, Los Angeles',
+          zip: 'CA 90036'
+        }, {
+          date: '2016-05-01',
+          name: 'Tom',
+          state: 'California',
+          city: 'Los Angeles',
+          address: 'No. 189, Grove St, Los Angeles',
+          zip: 'CA 90036'
+        }, {
+          date: '2016-05-08',
+          name: 'Tom',
+          state: 'California',
+          city: 'Los Angeles',
+          address: 'No. 189, Grove St, Los Angeles',
+          zip: 'CA 90036'
+        }, {
+          date: '2016-05-06',
+          name: 'Tom',
+          state: 'California',
+          city: 'Los Angeles',
+          address: 'No. 189, Grove St, Los Angeles',
+          zip: 'CA 90036'
+        }, {
+          date: '2016-05-07',
+          name: 'Tom',
+          state: 'California',
+          city: 'Los Angeles',
+          address: 'No. 189, Grove St, Los Angeles',
+          zip: 'CA 90036'
+        }]
+    }
+  }
+</script>
+```
+:::
+
 ### Table Attributes
 | Attribute      | Description          | Type      | Accepted Values       | Default  |
 |---------- |-------------- |---------- |--------------------------------  |-------- |
@@ -1369,7 +1457,9 @@ Customize table column so it can be integrated with other components.
 | row-style | function that returns custom style for a row,  or a string assigning custom style for every row | Function(row, index)/Object | — | — |
 | row-key | key of row data, used for optimizing rendering. Required if `reserve-selection` is on | Function(row)/String | — | — |
 | context | context of Table, e.g. `_self` refers to the current context, `$parent` parent context, `$root` root context, can be overridden by `context` in `el-table-column` | Object | - | current context where Table lies |
-| empty-text | Displayed text when data is empty. You can customize this area with `slot="empty"` | String | | - | No Data |
+| empty-text | Displayed text when data is empty. You can customize this area with `slot="empty"` | String | - | No Data |
+| default-expand-all | whether expand all rows by default, only works when the table has a column type="expand" | Boolean | - | false |
+| expand-row-keys | set expanded rows by this prop, prop's value is the keys of expand rows, you should set row-key before using this prop | Array | - | |
 
 ### Table Events
 | Event Name | Description | Parameters |
@@ -1387,6 +1477,7 @@ Customize table column so it can be integrated with other components.
 | sort-change | triggers when Table's sorting changes | { column, prop, order } |
 | filter-change | column's key. If you need to use the filter-change event, this attribute is mandatory to identify which column is being filtered | filters |
 | current-change | triggers when current row changes | currentRow, oldCurrentRow |
+| expand | triggers when user expands or collapses a row | row, expanded |
 
 ### Table Methods
 | Method | Description | Parameter |
@@ -1397,7 +1488,7 @@ Customize table column so it can be integrated with other components.
 ### Table-column Attributes
 | Attribute      | Description          | Type      | Accepted Values       | Default  |
 |---------- |-------------- |---------- |--------------------------------  |-------- |
-| type | type of the column. If set to `selection`, the column will display checkbox. If set to `index`, the column will display index of the row (staring from 1)  | string | selection/index | — |
+| type | type of the column. If set to `selection`, the column will display checkbox. If set to `index`, the column will display index of the row (staring from 1). If set to `expand`, the column will display expand icon.  | string | selection/index/expand | — |
 | label | column label | string | — | — |
 | column-key | column's key. If you need to use the filter-change event, you need this attribute to identify which column is being filtered | string | string | - | - |
 | prop |  field name. You can also use its alias: `property` | string | — | — |

+ 79 - 2
examples/docs/zh-CN/table.md

@@ -60,6 +60,7 @@
           province: '上海',
           city: '普陀区',
           address: '上海市普陀区金沙江路 1518 弄',
+          detailAddress: '金沙江路 1518 弄',
           zip: 200333
         }, {
           date: '2016-05-02',
@@ -67,6 +68,7 @@
           province: '上海',
           city: '普陀区',
           address: '上海市普陀区金沙江路 1518 弄',
+          detailAddress: '金沙江路 1518 弄',
           zip: 200333
         }, {
           date: '2016-05-04',
@@ -81,6 +83,7 @@
           province: '上海',
           city: '普陀区',
           address: '上海市普陀区金沙江路 1518 弄',
+          detailAddress: '金沙江路 1518 弄',
           zip: 200333
         }, {
           date: '2016-05-08',
@@ -88,6 +91,7 @@
           province: '上海',
           city: '普陀区',
           address: '上海市普陀区金沙江路 1518 弄',
+          detailAddress: '金沙江路 1518 弄',
           zip: 200333
         }, {
           date: '2016-05-06',
@@ -95,6 +99,7 @@
           province: '上海',
           city: '普陀区',
           address: '上海市普陀区金沙江路 1518 弄',
+          detailAddress: '金沙江路 1518 弄',
           zip: 200333
         }, {
           date: '2016-05-07',
@@ -102,6 +107,7 @@
           province: '上海',
           city: '普陀区',
           address: '上海市普陀区金沙江路 1518 弄',
+          detailAddress: '金沙江路 1518 弄',
           zip: 200333
         }],
         tableData4: [{
@@ -1363,6 +1369,74 @@
 ```
 :::
 
+### 展开行
+
+当行内容过多并且不想显示横向滚动条时,可以使用 Table 展开行功能。
+:::demo 通过设置 type="expand" 和 `inline-template` 属性可以开启展开行功能,`el-table-column` 的模板会被渲染成为展开行的内容,展开行可访问的属性与使用 `inline-template` 的时候相同。
+```html
+<template>
+  <el-table
+    :data="tableData3"
+    style="width: 100%">
+    <el-table-column type="expand" inline-template>
+      <div>
+      <p>省: {{ row.province }}</p>
+      <p>市: {{ row.city }}</p>
+      <p>住址: {{ row.detailAddress }}</p>
+      <p>邮编: {{ row.zip }}</p>
+      </div>
+    </el-table-column>
+    <el-table-column
+      label="日期"
+      prop="date">
+    </el-table-column>
+    <el-table-column
+      label="姓名"
+      prop="name">
+    </el-table-column>
+  </el-table>
+</template>
+
+<script>
+  export default {
+    data() {
+      return {
+        tableData3: [{
+          date: '2016-05-02',
+          name: '王小虎',
+          province: '上海',
+          city: '普陀区',
+          detailAddress: '金沙江路 1518 弄',
+          zip: 200333
+        }, {
+          date: '2016-05-04',
+          name: '王小虎',
+          province: '上海',
+          city: '普陀区',
+          detailAddress: '金沙江路 1518 弄',
+          zip: 200333
+        }, {
+          date: '2016-05-01',
+          name: '王小虎',
+          province: '上海',
+          city: '普陀区',
+          detailAddress: '金沙江路 1518 弄',
+          zip: 200333
+        }, {
+          date: '2016-05-03',
+          name: '王小虎',
+          province: '上海',
+          city: '普陀区',
+          detailAddress: '金沙江路 1518 弄',
+          zip: 200333
+        }]
+      }
+    }
+  }
+</script>
+```
+:::
+
 ### Table Attributes
 | 参数      | 说明          | 类型      | 可选值                           | 默认值  |
 |---------- |-------------- |---------- |--------------------------------  |-------- |
@@ -1377,7 +1451,9 @@
 | row-style | 行的 style 的回调方法,也可以使用一个固定的 Object 为所有行设置一样的 Style。 | Function(row, index)/Object | — | — |
 | row-key | 行数据的 Key,用来优化 Table 的渲染;在使用 reserve-selection 功能的情况下,该属性是必填的 | Function(row)/String | — | — |
 | context | 设置上下文环境,例如设置当前上下文就是 `_self`,父级就是 `$parent`,根组件 `$root`。优先读取 column 的 context 属性。 | Object | - | Table 所处上下文 |
-| empty-text | 空数据时显示的文本内容,也可以通过 `slot="empty"` 设置 | String | | - | 暂无数据 |
+| empty-text | 空数据时显示的文本内容,也可以通过 `slot="empty"` 设置 | String | - | 暂无数据 |
+| default-expand-all | 是否默认展开所有行,当 Table 中存在 type="expand" 的 Column 的时候有效 | Boolean | - | false |
+| expand-row-keys | 可以通过该属性设置 Table 目前的展开行,需要设置 row-key 属性才能使用,该属性为展开行的 keys 数组。| Array | - | |
 
 ### Table Events
 | 事件名 | 说明 | 参数 |
@@ -1395,6 +1471,7 @@
 | sort-change | 当表格的排序条件发生变化的时候会触发该事件 | { column, prop, order } |
 | filter-change | 当表格的筛选条件发生变化的时候会触发该事件,参数的值是一个对象,对象的 key 是 column 的 columnKey,对应的 value 为用户选择的筛选条件的数组。 | filters |
 | current-change | 当表格的当前行发生变化的时候会触发该事件,如果要高亮当前行,请打开表格的 highlight-current-row 属性 | currentRow, oldCurrentRow |
+| expand | 当用户对某一行展开或者关闭的上会触发该事件 | row, expanded |
 
 ### Table Methods
 | 方法名 | 说明 | 参数 |
@@ -1405,7 +1482,7 @@
 ### Table-column Attributes
 | 参数      | 说明          | 类型      | 可选值                           | 默认值  |
 |---------- |-------------- |---------- |--------------------------------  |-------- |
-| type | 对应列的类型。如果设置了 `selection` 则显示多选框如果设置了 `index` 则显示该行的索引(从 1 开始计算) | string | selection/index | — |
+| type | 对应列的类型。如果设置了 `selection` 则显示多选框如果设置了 `index` 则显示该行的索引(从 1 开始计算);如果设置了 expand 则显示为一个可展开的按钮 | string | selection/index/expand | — |
 | column-key | column 的 key,如果需要使用 filter-change 事件,则需要此属性标识是哪个 column 的筛选条件 | string | - | - |
 | label | 显示的标题 | string | — | — |
 | prop | 对应列内容的字段名,也可以使用 property 属性 | string | — | — |

+ 16 - 4
packages/table/src/table-body.js

@@ -38,7 +38,7 @@ export default {
         <tbody>
           {
             this._l(this.data, (row, $index) =>
-              <tr
+              [<tr
                 style={ this.rowStyle ? this.getRowStyle(row, $index) : null }
                 key={ this.$parent.rowKey ? this.getKeyOfRow(row, $index) : $index }
                 on-dblclick={ ($event) => this.handleDoubleClick($event, row) }
@@ -46,7 +46,7 @@ export default {
                 on-contextmenu={ ($event) => this.handleContextMenu($event, row) }
                 on-mouseenter={ _ => this.handleMouseEnter($index) }
                 on-mouseleave={ _ => this.handleMouseLeave() }
-                class={ this.getRowClass(row, $index) }>
+                class={ [this.getRowClass(row, $index)] }>
                 {
                   this._l(this.columns, (column, cellIndex) =>
                     <td
@@ -62,7 +62,15 @@ export default {
                 {
                   !this.fixed && this.layout.scrollY && this.layout.gutterWidth ? <td class="gutter" /> : ''
                 }
-              </tr>
+              </tr>,
+                this.store.states.expandRows.indexOf(row) > -1
+                ? (<tr>
+                    <td colspan={ this.columns.length } class="el-table__expanded-cell">
+                      { this.$parent.renderExpanded ? this.$parent.renderExpanded.call(this._renderProxy, h, { row, $index, store: this.store, _self: this.$parent.$vnode.context }) : ''}
+                    </td>
+                  </tr>)
+                : ''
+              ]
             )
           }
         </tbody>
@@ -178,7 +186,7 @@ export default {
 
       if (cell) {
         const column = getColumnByCell(table, cell);
-        const hoverState = table.hoverState = { cell, column, row };
+        const hoverState = table.hoverState = {cell, column, row};
         table.$emit('cell-mouse-enter', hoverState.row, hoverState.column, hoverState.cell, event);
       }
 
@@ -228,6 +236,10 @@ export default {
       this.store.commit('setCurrentRow', row);
 
       table.$emit('row-click', row, event, column);
+    },
+
+    handleExpandClick(row) {
+      this.store.commit('toggleRowExpanded', row);
     }
   }
 };

+ 47 - 2
packages/table/src/table-column.js

@@ -16,6 +16,12 @@ const defaults = {
     order: '',
     className: 'el-table-column--selection'
   },
+  expand: {
+    width: 48,
+    minWidth: 48,
+    realWidth: 48,
+    order: ''
+  },
   index: {
     width: 48,
     minWidth: 48,
@@ -48,6 +54,21 @@ const forced = {
       return <div>{ $index + 1 }</div>;
     },
     sortable: false
+  },
+  expand: {
+    renderHeader: function(h, {}) {
+      return '';
+    },
+    renderCell: function(h, { row, store }, proxy) {
+      const expanded = store.states.expandRows.indexOf(row) > -1;
+      return <div class={ 'el-table__expand-icon ' + (expanded ? 'el-table__expand-icon--expanded' : '') }
+                  on-click={ () => proxy.handleExpandClick(row) }>
+        <i class='el-icon el-icon-arrow-right'></i>
+      </div>;
+    },
+    sortable: false,
+    resizable: false,
+    className: 'el-table__expand-column'
   }
 };
 
@@ -225,9 +246,35 @@ export default {
 
     objectAssign(column, forced[type] || {});
 
+    this.columnConfig = column;
+
     let renderCell = column.renderCell;
     let _self = this;
 
+    if (type === 'expand') {
+      owner.renderExpanded = function(h, data) {
+        if (_self.$vnode.data.inlineTemplate) {
+          data._self = _self.context || data._self;
+          if (Object.prototype.toString.call(data._self) === '[object Object]') {
+            for (let prop in data._self) {
+              if (!data.hasOwnProperty(prop)) {
+                data[prop] = data._self[prop];
+              }
+            }
+          }
+          data._staticTrees = _self._staticTrees;
+          data.$options.staticRenderFns = _self.$options.staticRenderFns;
+          return _self.customRender.call(data);
+        }
+      };
+
+      column.renderCell = function(h, data) {
+        return <div class="cell">{ renderCell(h, data, this._renderProxy) }</div>;
+      };
+
+      return;
+    }
+
     column.renderCell = function(h, data) {
       if (_self.$vnode.data.inlineTemplate) {
         renderCell = function() {
@@ -261,8 +308,6 @@ export default {
           </el-tooltip>
         : <div class="cell">{ renderCell(h, data) }</div>;
     };
-
-    this.columnConfig = column;
   },
 
   destroyed() {

+ 55 - 1
packages/table/src/table-store.js

@@ -69,7 +69,9 @@ const TableStore = function(table, initialState = {}) {
     selectable: null,
     currentRow: null,
     hoverRow: null,
-    filters: {}
+    filters: {},
+    expandRows: [],
+    defaultExpandAll: false
   };
 
   for (let prop in initialState) {
@@ -85,6 +87,15 @@ TableStore.prototype.mutations = {
     states._data = data;
     states.data = sortData((data || []), states);
 
+    states.data.forEach((item) => {
+      if (!item.$extra) {
+        Object.defineProperty(item, '$extra', {
+          value: {},
+          enumerable: false
+        });
+      }
+    });
+
     this.updateCurrentRow();
 
     if (!states.reserveSelection) {
@@ -114,6 +125,11 @@ TableStore.prototype.mutations = {
       }
     }
 
+    const defaultExpandAll = states.defaultExpandAll;
+    if (defaultExpandAll) {
+      this.states.expandRows = (states.data || []).slice(0);
+    }
+
     Vue.nextTick(() => this.table.updateScrollY());
   },
 
@@ -218,6 +234,26 @@ TableStore.prototype.mutations = {
     this.updateAllSelected();
   },
 
+  toggleRowExpanded: function(states, row, expanded) {
+    const expandRows = states.expandRows;
+    if (typeof expanded !== 'undefined') {
+      const index = expandRows.indexOf(row);
+      if (expanded) {
+        if (index === -1) expandRows.push(row);
+      } else {
+        if (index !== -1) expandRows.splice(index, 1);
+      }
+    } else {
+      const index = expandRows.indexOf(row);
+      if (index === -1) {
+        expandRows.push(row);
+      } else {
+        expandRows.splice(index, 1);
+      }
+    }
+    this.table.$emit('expand', row, expandRows.indexOf(row) !== -1);
+  },
+
   toggleAllSelection: debounce(10, function(states) {
     const data = states.data || [];
     const value = !states.isAllSelected;
@@ -286,6 +322,22 @@ TableStore.prototype.clearSelection = function() {
   }
 };
 
+TableStore.prototype.setExpandRowKeys = function(rowKeys) {
+  const expandRows = [];
+  const data = this.states.data;
+  const rowKey = this.states.rowKey;
+  if (!rowKey) throw new Error('[Table] prop row-key should not be empty.');
+  const keysMap = getKeysMap(data, rowKey);
+  rowKeys.forEach((key) => {
+    const info = keysMap[key];
+    if (info) {
+      expandRows.push(info.row);
+    }
+  });
+
+  this.states.expandRows = expandRows;
+};
+
 TableStore.prototype.toggleRowSelection = function(row, selected) {
   const changed = toggleRowSelection(this.states, row, selected);
   if (changed) {
@@ -395,6 +447,8 @@ TableStore.prototype.commit = function(name, ...args) {
   const mutations = this.mutations;
   if (mutations[name]) {
     mutations[name].apply(this, [this.states].concat(args));
+  } else {
+    throw new Error(`Action not found: ${name}`);
   }
 };
 

+ 12 - 2
packages/table/src/table.vue

@@ -158,7 +158,11 @@
 
       highlightCurrentRow: Boolean,
 
-      emptyText: String
+      emptyText: String,
+
+      expandRowKeys: Array,
+
+      defaultExpandAll: Boolean
     },
 
     components: {
@@ -346,6 +350,10 @@
         handler(val) {
           this.store.commit('setData', val);
         }
+      },
+
+      expandRowKeys(newVal) {
+        this.store.setExpandRowKeys(newVal);
       }
     },
 
@@ -362,7 +370,8 @@
 
     data() {
       const store = new TableStore(this, {
-        rowKey: this.rowKey
+        rowKey: this.rowKey,
+        defaultExpandAll: this.defaultExpandAll
       });
       const layout = new TableLayout({
         store,
@@ -373,6 +382,7 @@
       return {
         store,
         layout,
+        renderExpanded: null,
         resizeProxyVisible: false
       };
     }

+ 38 - 0
packages/theme-default/src/table.css

@@ -66,6 +66,44 @@
       color: #5e6d82;
     }
 
+    @e expand-column {
+      .cell {
+        padding: 0;
+        text-align: center;
+      }
+    }
+
+    @e expand-icon {
+      position: relative;
+      cursor: pointer;
+      color: #666;
+      font-size: 12px;
+      transition: transform 0.2s ease-in-out;
+      height: 40px;
+
+      @m expanded {
+        transform: rotate(90deg);
+      }
+
+      > .el-icon {
+        position: absolute;
+        left: 50%;
+        top: 50%;
+        margin-left: -5px;
+        margin-top: -5px;
+      }
+    }
+
+    @e expanded-cell {
+      padding: 20px 50px;
+      background-color: #f9fafc;
+      box-shadow: inset 0 2px 0 #f4f4f4;
+
+      &:hover {
+        background-color: #f9fafc !important;
+      }
+    }
+
     @modifier fit {
       border-right: 0;
       border-bottom: 0;

+ 90 - 5
test/unit/specs/table.spec.js

@@ -8,11 +8,11 @@ const toArray = function(obj) {
 
 const getTestData = function() {
   return [
-    { name: 'Toy Story', release: '1995-11-22', director: 'John Lasseter', runtime: 80 },
-    { name: 'A Bug\'s Life', release: '1998-11-25', director: 'John Lasseter', runtime: 95 },
-    { name: 'Toy Story 2', release: '1999-11-24', director: 'John Lasseter', runtime: 92 },
-    { name: 'Monsters, Inc.', release: '2001-11-2', director: 'Peter Docter', runtime: 92 },
-    { name: 'Finding Nemo', release: '2003-5-30', director: 'Andrew Stanton', runtime: 100 }
+    { id: 1, name: 'Toy Story', release: '1995-11-22', director: 'John Lasseter', runtime: 80 },
+    { id: 2, name: 'A Bug\'s Life', release: '1998-11-25', director: 'John Lasseter', runtime: 95 },
+    { id: 3, name: 'Toy Story 2', release: '1999-11-24', director: 'John Lasseter', runtime: 92 },
+    { id: 4, name: 'Monsters, Inc.', release: '2001-11-2', director: 'Peter Docter', runtime: 92 },
+    { id: 5, name: 'Finding Nemo', release: '2003-5-30', director: 'Andrew Stanton', runtime: 100 }
   ];
 };
 
@@ -27,6 +27,7 @@ describe('Table', () => {
     const vm = createVue({
       template: `
         <el-table :data="testData">
+          <el-table-column prop="id" />
           <el-table-column prop="name" label="片名" />
           <el-table-column prop="release" label="发行日期" />
           <el-table-column prop="director" label="导演" />
@@ -957,6 +958,90 @@ describe('Table', () => {
           }, DELAY);
         });
       });
+
+      describe('= expand', () => {
+        const createInstance = function(extra) {
+          extra = extra || '';
+          return createVue({
+            template: `
+            <el-table row-key="id" :data="testData" @expand="handleExpand" ${extra}>
+              <el-table-column type="expand" inline-template>
+                <div>{{row.name}}</div>
+              </el-table-column>
+              <el-table-column prop="release" label="release" />
+              <el-table-column prop="director" label="director" />
+              <el-table-column prop="runtime" label="runtime" />
+            </el-table>
+          `,
+
+            created() {
+              this.testData = getTestData();
+            },
+
+            data() {
+              return { expandCount: 0, expandRowKeys: [] };
+            },
+
+            methods: {
+              handleExpand() {
+                this.expandCount++;
+              }
+            }
+          }, true);
+        };
+
+        it('works', done => {
+          const vm = createInstance();
+          setTimeout(_ => {
+            expect(vm.$el.querySelectorAll('td.el-table__expand-column').length).to.equal(5);
+            destroyVM(vm);
+            done();
+          }, DELAY);
+        });
+
+        it('should expand when click icon', done => {
+          const vm = createInstance();
+          setTimeout(_ => {
+            vm.$el.querySelector('td.el-table__expand-column .el-table__expand-icon').click();
+            setTimeout(_ => {
+              expect(vm.$el.querySelectorAll('.el-table__expanded-cell').length).to.equal(1);
+              expect(vm.expandCount).to.equal(1);
+              vm.$el.querySelector('td.el-table__expand-column .el-table__expand-icon').click();
+              setTimeout(_ => {
+                expect(vm.$el.querySelectorAll('.el-table__expanded-cell').length).to.equal(0);
+                expect(vm.expandCount).to.equal(2);
+                destroyVM(vm);
+                done();
+              }, DELAY);
+            }, DELAY);
+          }, DELAY);
+        });
+
+        it('should set expanded rows using expandRowKeys', done => {
+          const vm = createInstance(':expand-row-keys="expandRowKeys"');
+          setTimeout(_ => {
+            vm.expandRowKeys = [1, 3];
+            setTimeout(_ => {
+              expect(vm.$el.querySelectorAll('.el-table__expanded-cell').length).to.equal(2);
+              vm.expandRowKeys = [2];
+              setTimeout(_ => {
+                expect(vm.$el.querySelectorAll('.el-table__expanded-cell').length).to.equal(1);
+                destroyVM(vm);
+                done();
+              }, DELAY);
+            }, DELAY);
+          }, DELAY);
+        });
+
+        it('should default-expand-all when default-expand-all is true', done => {
+          const vm = createInstance('default-expand-all');
+          setTimeout(_ => {
+            expect(vm.$el.querySelectorAll('.el-table__expanded-cell').length).to.equal(5);
+            destroyVM(vm);
+            done();
+          }, DELAY);
+        });
+      });
     });
 
     describe('sortable', () => {