diff --git a/components.json b/components.json
index ce0595f0233..478a87d6ca1 100644
--- a/components.json
+++ b/components.json
@@ -83,5 +83,7 @@
"popconfirm": "./packages/popconfirm/index.js",
"skeleton": "./packages/skeleton/index.js",
"skeleton-item": "./packages/skeleton-item/index.js",
- "empty": "./packages/empty/index.js"
+ "empty": "./packages/empty/index.js",
+ "descriptions": "./packages/description/index.js",
+ "descriptions-item": "./packages/description-item/index.js"
}
diff --git a/examples/demo-styles/descriptions.scss b/examples/demo-styles/descriptions.scss
new file mode 100644
index 00000000000..8823e93cb0b
--- /dev/null
+++ b/examples/demo-styles/descriptions.scss
@@ -0,0 +1,13 @@
+.demo-block.demo-descriptions {
+ .margin-top {
+ margin-top: 20px;
+ }
+
+ .my-label {
+ background: #E1F3D8;
+ }
+
+ .my-content {
+ background: #FDE2E2;
+ }
+}
diff --git a/examples/demo-styles/index.scss b/examples/demo-styles/index.scss
index 1638bd094d8..43c2f44a8c1 100644
--- a/examples/demo-styles/index.scss
+++ b/examples/demo-styles/index.scss
@@ -45,4 +45,5 @@
@import "./avatar.scss";
@import "./drawer.scss";
@import "./skeleton.scss";
+@import "./descriptions.scss";
diff --git a/examples/docs/en-US/descriptions.md b/examples/docs/en-US/descriptions.md
new file mode 100644
index 00000000000..2e93ad5eadb
--- /dev/null
+++ b/examples/docs/en-US/descriptions.md
@@ -0,0 +1,191 @@
+## Descriptions
+
+Display multiple fields in list form.
+
+### Basic usage
+
+:::demo
+
+```html
+
+ kooriookami
+ 18100000000
+ Suzhou
+
+ School
+
+ No.1188, Wuzhong Avenue, Wuzhong District, Suzhou, Jiangsu Province
+
+```
+:::
+
+### Sizes
+
+:::demo
+
+```html
+
+
+ Default
+ Medium
+ Small
+ Mini
+
+
+
+
+ Operation
+
+
+
+
+ Username
+
+ kooriookami
+
+
+
+
+ Telephone
+
+ 18100000000
+
+
+
+
+ Place
+
+ Suzhou
+
+
+
+
+ Remarks
+
+ School
+
+
+
+
+ Address
+
+ No.1188, Wuzhong Avenue, Wuzhong District, Suzhou, Jiangsu Province
+
+
+
+
+
+ Operation
+
+ kooriookami
+ 18100000000
+ Suzhou
+
+ School
+
+ No.1188, Wuzhong Avenue, Wuzhong District, Suzhou, Jiangsu Province
+
+
+
+
+```
+:::
+
+### Vertical List
+
+:::demo
+
+```html
+
+ kooriookami
+ 18100000000
+ Suzhou
+
+ School
+
+ No.1188, Wuzhong Avenue, Wuzhong District, Suzhou, Jiangsu Province
+
+
+
+ kooriookami
+ 18100000000
+ Suzhou
+
+ School
+
+ No.1188, Wuzhong Avenue, Wuzhong District, Suzhou, Jiangsu Province
+
+```
+:::
+
+### Customized Style
+
+:::demo
+
+```html
+
+ kooriookami
+ 18100000000
+ Suzhou
+
+ School
+
+ No.1188, Wuzhong Avenue, Wuzhong District, Suzhou, Jiangsu Province
+
+
+```
+:::
+
+### Descriptions Attributes
+| Attribute | Description | Type | Accepted Values | Default |
+|------------- |---------------- |---------------- |---------------------- |-------- |
+| border | with or without border | boolean | — | false |
+| column | numbers of `Descriptions Item` in one line | number | — | 3 |
+| direction | direction of list | string | vertical / horizontal | horizontal |
+| size | size of list | string | medium / small / mini | — |
+| title | title text, display on the top left | string | — | — |
+| extra | extra text, display on the top right | string | — | — |
+| colon | change default props colon value of Descriptions Item | boolean | — | true |
+| labelClassName | custom label class name | string | — | — |
+| contentClassName | custom content class name | string | — | — |
+| labelStyle | custom label style | object | — | — |
+| contentStyle | custom content style | object | — | — |
+
+### Descriptions Slots
+
+| Name | Description |
+|------|--------|
+| title | custom title, display on the top left |
+| extra | custom extra area, display on the top right |
+
+### Descriptions Item Attributes
+| Attribute | Description | Type | Accepted Values | Default |
+|------------- |---------------- |---------------- |---------------------- |-------- |
+| label | label text | string | — | — |
+| span | colspan of column | number | — | 1 |
+| labelClassName | custom label class name | string | — | — |
+| contentClassName | custom content class name | string | — | — |
+| labelStyle | custom label style | object | — | — |
+| contentStyle | custom content style | object | — | — |
+
+### Descriptions Item Slots
+
+| Name | Description |
+|------|--------|
+| label | custom label |
diff --git a/examples/docs/es/descriptions.md b/examples/docs/es/descriptions.md
new file mode 100644
index 00000000000..2e93ad5eadb
--- /dev/null
+++ b/examples/docs/es/descriptions.md
@@ -0,0 +1,191 @@
+## Descriptions
+
+Display multiple fields in list form.
+
+### Basic usage
+
+:::demo
+
+```html
+
+ kooriookami
+ 18100000000
+ Suzhou
+
+ School
+
+ No.1188, Wuzhong Avenue, Wuzhong District, Suzhou, Jiangsu Province
+
+```
+:::
+
+### Sizes
+
+:::demo
+
+```html
+
+
+ Default
+ Medium
+ Small
+ Mini
+
+
+
+
+ Operation
+
+
+
+
+ Username
+
+ kooriookami
+
+
+
+
+ Telephone
+
+ 18100000000
+
+
+
+
+ Place
+
+ Suzhou
+
+
+
+
+ Remarks
+
+ School
+
+
+
+
+ Address
+
+ No.1188, Wuzhong Avenue, Wuzhong District, Suzhou, Jiangsu Province
+
+
+
+
+
+ Operation
+
+ kooriookami
+ 18100000000
+ Suzhou
+
+ School
+
+ No.1188, Wuzhong Avenue, Wuzhong District, Suzhou, Jiangsu Province
+
+
+
+
+```
+:::
+
+### Vertical List
+
+:::demo
+
+```html
+
+ kooriookami
+ 18100000000
+ Suzhou
+
+ School
+
+ No.1188, Wuzhong Avenue, Wuzhong District, Suzhou, Jiangsu Province
+
+
+
+ kooriookami
+ 18100000000
+ Suzhou
+
+ School
+
+ No.1188, Wuzhong Avenue, Wuzhong District, Suzhou, Jiangsu Province
+
+```
+:::
+
+### Customized Style
+
+:::demo
+
+```html
+
+ kooriookami
+ 18100000000
+ Suzhou
+
+ School
+
+ No.1188, Wuzhong Avenue, Wuzhong District, Suzhou, Jiangsu Province
+
+
+```
+:::
+
+### Descriptions Attributes
+| Attribute | Description | Type | Accepted Values | Default |
+|------------- |---------------- |---------------- |---------------------- |-------- |
+| border | with or without border | boolean | — | false |
+| column | numbers of `Descriptions Item` in one line | number | — | 3 |
+| direction | direction of list | string | vertical / horizontal | horizontal |
+| size | size of list | string | medium / small / mini | — |
+| title | title text, display on the top left | string | — | — |
+| extra | extra text, display on the top right | string | — | — |
+| colon | change default props colon value of Descriptions Item | boolean | — | true |
+| labelClassName | custom label class name | string | — | — |
+| contentClassName | custom content class name | string | — | — |
+| labelStyle | custom label style | object | — | — |
+| contentStyle | custom content style | object | — | — |
+
+### Descriptions Slots
+
+| Name | Description |
+|------|--------|
+| title | custom title, display on the top left |
+| extra | custom extra area, display on the top right |
+
+### Descriptions Item Attributes
+| Attribute | Description | Type | Accepted Values | Default |
+|------------- |---------------- |---------------- |---------------------- |-------- |
+| label | label text | string | — | — |
+| span | colspan of column | number | — | 1 |
+| labelClassName | custom label class name | string | — | — |
+| contentClassName | custom content class name | string | — | — |
+| labelStyle | custom label style | object | — | — |
+| contentStyle | custom content style | object | — | — |
+
+### Descriptions Item Slots
+
+| Name | Description |
+|------|--------|
+| label | custom label |
diff --git a/examples/docs/fr-FR/descriptions.md b/examples/docs/fr-FR/descriptions.md
new file mode 100644
index 00000000000..2e93ad5eadb
--- /dev/null
+++ b/examples/docs/fr-FR/descriptions.md
@@ -0,0 +1,191 @@
+## Descriptions
+
+Display multiple fields in list form.
+
+### Basic usage
+
+:::demo
+
+```html
+
+ kooriookami
+ 18100000000
+ Suzhou
+
+ School
+
+ No.1188, Wuzhong Avenue, Wuzhong District, Suzhou, Jiangsu Province
+
+```
+:::
+
+### Sizes
+
+:::demo
+
+```html
+
+
+ Default
+ Medium
+ Small
+ Mini
+
+
+
+
+ Operation
+
+
+
+
+ Username
+
+ kooriookami
+
+
+
+
+ Telephone
+
+ 18100000000
+
+
+
+
+ Place
+
+ Suzhou
+
+
+
+
+ Remarks
+
+ School
+
+
+
+
+ Address
+
+ No.1188, Wuzhong Avenue, Wuzhong District, Suzhou, Jiangsu Province
+
+
+
+
+
+ Operation
+
+ kooriookami
+ 18100000000
+ Suzhou
+
+ School
+
+ No.1188, Wuzhong Avenue, Wuzhong District, Suzhou, Jiangsu Province
+
+
+
+
+```
+:::
+
+### Vertical List
+
+:::demo
+
+```html
+
+ kooriookami
+ 18100000000
+ Suzhou
+
+ School
+
+ No.1188, Wuzhong Avenue, Wuzhong District, Suzhou, Jiangsu Province
+
+
+
+ kooriookami
+ 18100000000
+ Suzhou
+
+ School
+
+ No.1188, Wuzhong Avenue, Wuzhong District, Suzhou, Jiangsu Province
+
+```
+:::
+
+### Customized Style
+
+:::demo
+
+```html
+
+ kooriookami
+ 18100000000
+ Suzhou
+
+ School
+
+ No.1188, Wuzhong Avenue, Wuzhong District, Suzhou, Jiangsu Province
+
+
+```
+:::
+
+### Descriptions Attributes
+| Attribute | Description | Type | Accepted Values | Default |
+|------------- |---------------- |---------------- |---------------------- |-------- |
+| border | with or without border | boolean | — | false |
+| column | numbers of `Descriptions Item` in one line | number | — | 3 |
+| direction | direction of list | string | vertical / horizontal | horizontal |
+| size | size of list | string | medium / small / mini | — |
+| title | title text, display on the top left | string | — | — |
+| extra | extra text, display on the top right | string | — | — |
+| colon | change default props colon value of Descriptions Item | boolean | — | true |
+| labelClassName | custom label class name | string | — | — |
+| contentClassName | custom content class name | string | — | — |
+| labelStyle | custom label style | object | — | — |
+| contentStyle | custom content style | object | — | — |
+
+### Descriptions Slots
+
+| Name | Description |
+|------|--------|
+| title | custom title, display on the top left |
+| extra | custom extra area, display on the top right |
+
+### Descriptions Item Attributes
+| Attribute | Description | Type | Accepted Values | Default |
+|------------- |---------------- |---------------- |---------------------- |-------- |
+| label | label text | string | — | — |
+| span | colspan of column | number | — | 1 |
+| labelClassName | custom label class name | string | — | — |
+| contentClassName | custom content class name | string | — | — |
+| labelStyle | custom label style | object | — | — |
+| contentStyle | custom content style | object | — | — |
+
+### Descriptions Item Slots
+
+| Name | Description |
+|------|--------|
+| label | custom label |
diff --git a/examples/docs/zh-CN/descriptions.md b/examples/docs/zh-CN/descriptions.md
new file mode 100644
index 00000000000..2d6b39a0b6b
--- /dev/null
+++ b/examples/docs/zh-CN/descriptions.md
@@ -0,0 +1,191 @@
+## Descriptions 描述列表
+
+列表形式展示多个字段。
+
+### 基础用法
+
+:::demo
+
+```html
+
+ kooriookami
+ 18100000000
+ 苏州市
+
+ 学校
+
+ 江苏省苏州市吴中区吴中大道 1188 号
+
+```
+:::
+
+### 不同尺寸
+
+:::demo
+
+```html
+
+
+ 默认
+ 中等
+ 小型
+ 超小
+
+
+
+
+ 操作
+
+
+
+
+ 用户名
+
+ kooriookami
+
+
+
+
+ 手机号
+
+ 18100000000
+
+
+
+
+ 居住地
+
+ 苏州市
+
+
+
+
+ 备注
+
+ 学校
+
+
+
+
+ 联系地址
+
+ 江苏省苏州市吴中区吴中大道 1188 号
+
+
+
+
+
+ 操作
+
+ kooriookami
+ 18100000000
+ 苏州市
+
+ 学校
+
+ 江苏省苏州市吴中区吴中大道 1188 号
+
+
+
+
+```
+:::
+
+### 垂直列表
+
+:::demo
+
+```html
+
+ kooriookami
+ 18100000000
+ 苏州市
+
+ 学校
+
+ 江苏省苏州市吴中区吴中大道 1188 号
+
+
+
+ kooriookami
+ 18100000000
+ 苏州市
+
+ 学校
+
+ 江苏省苏州市吴中区吴中大道 1188 号
+
+```
+:::
+
+### 自定义样式
+
+:::demo
+
+```html
+
+ kooriookami
+ 18100000000
+ 苏州市
+
+ 学校
+
+ 江苏省苏州市吴中区吴中大道 1188 号
+
+
+```
+:::
+
+### Descriptions Attributes
+| 参数 | 说明 | 类型 | 可选值 | 默认值 |
+|------------- |---------------- |---------------- |---------------------- |-------- |
+| border | 是否带有边框 | boolean | — | false |
+| column | 一行 `Descriptions Item` 的数量 | number | — | 3 |
+| direction | 排列的方向 | string | vertical / horizontal | horizontal |
+| size | 列表的尺寸 | string | medium / small / mini | — |
+| title | 标题文本,显示在左上方 | string | — | — |
+| extra | 操作区文本,显示在右上方 | string | — | — |
+| colon | 是否显示冒号 | boolean | — | true |
+| labelClassName | 自定义标签类名 | string | — | — |
+| contentClassName | 自定义内容类名 | string | — | — |
+| labelStyle | 自定义标签样式 | object | — | — |
+| contentStyle | 自定义内容样式 | object | — | — |
+
+### Descriptions Slots
+
+| Name | 说明 |
+|------|--------|
+| title | 自定义标题,显示在左上方 |
+| extra | 自定义操作区,显示在右上方 |
+
+### Descriptions Item Attributes
+| 参数 | 说明 | 类型 | 可选值 | 默认值 |
+|------------- |---------------- |---------------- |---------------------- |-------- |
+| label | 标签文本 | string | — | — |
+| span | 列的数量 | number | — | 1 |
+| labelClassName | 自定义标签类名 | string | — | — |
+| contentClassName | 自定义内容类名 | string | — | — |
+| labelStyle | 自定义标签样式 | object | — | — |
+| contentStyle | 自定义内容样式 | object | — | — |
+
+### Descriptions Item Slots
+
+| Name | 说明 |
+|------|--------|
+| label | 自定义标签文本 |
diff --git a/examples/nav.config.json b/examples/nav.config.json
index d3532e47af8..c717d63d2d2 100644
--- a/examples/nav.config.json
+++ b/examples/nav.config.json
@@ -184,6 +184,10 @@
{
"path": "/empty",
"title": "Empty 空状态"
+ },
+ {
+ "path": "/descriptions",
+ "title": "Descriptions 描述列表"
}
]
},
@@ -486,6 +490,10 @@
{
"path": "/empty",
"title": "Empty"
+ },
+ {
+ "path": "/descriptions",
+ "title": "Descriptions"
}
]
},
@@ -792,6 +800,10 @@
{
"path": "/empty",
"title": "Empty"
+ },
+ {
+ "path": "/descriptions",
+ "title": "Descriptions"
}
]
},
@@ -1098,6 +1110,10 @@
{
"path": "/empty",
"title": "Empty"
+ },
+ {
+ "path": "/descriptions",
+ "title": "Descriptions"
}
]
},
diff --git a/packages/descriptions-item/index.js b/packages/descriptions-item/index.js
new file mode 100644
index 00000000000..d9b3af1ef42
--- /dev/null
+++ b/packages/descriptions-item/index.js
@@ -0,0 +1,8 @@
+import DescriptionsItem from '../descriptions/src/description-item';
+
+/* istanbul ignore next */
+DescriptionsItem.install = function install(Vue) {
+ Vue.component(DescriptionsItem.name, DescriptionsItem);
+};
+
+export default DescriptionsItem;
diff --git a/packages/descriptions/index.js b/packages/descriptions/index.js
new file mode 100644
index 00000000000..ccee18ca1ac
--- /dev/null
+++ b/packages/descriptions/index.js
@@ -0,0 +1,8 @@
+import Descriptions from './src/index';
+
+/* istanbul ignore next */
+Descriptions.install = function install(Vue) {
+ Vue.component(Descriptions.name, Descriptions);
+};
+
+export default Descriptions;
diff --git a/packages/descriptions/src/description-item.js b/packages/descriptions/src/description-item.js
new file mode 100644
index 00000000000..d764543c50c
--- /dev/null
+++ b/packages/descriptions/src/description-item.js
@@ -0,0 +1,30 @@
+export default {
+ name: 'ElDescriptionsItem',
+ props: {
+ label: {
+ type: String,
+ default: ''
+ },
+ span: {
+ type: Number,
+ default: 1
+ },
+ contentClassName: {
+ type: String,
+ default: ''
+ },
+ contentStyle: {
+ type: Object
+ },
+ labelClassName: {
+ type: String,
+ default: ''
+ },
+ labelStyle: {
+ type: Object
+ }
+ },
+ render() {
+ return null;
+ }
+};
diff --git a/packages/descriptions/src/descriptions-row.js b/packages/descriptions/src/descriptions-row.js
new file mode 100644
index 00000000000..9fc05a1dcb9
--- /dev/null
+++ b/packages/descriptions/src/descriptions-row.js
@@ -0,0 +1,115 @@
+export default {
+ name: 'ElDescriptionsRow',
+ props: {
+ row: {
+ type: Array
+ }
+ },
+ inject: ['elDescriptions'],
+ render(h) {
+ const { elDescriptions } = this;
+ const row = (this.row || []).map(item => {
+ return {
+ ...item,
+ label: item.slots.label || item.props.label,
+ ...['labelClassName', 'contentClassName', 'labelStyle', 'contentStyle'].reduce((res, key) => {
+ res[key] = item.props[key] || elDescriptions[key];
+ return res;
+ }, {})
+ };
+ });
+ if (elDescriptions.direction === 'vertical') {
+ return (
+
+
+ {
+ row.map(item => {
+ return (
+ {item.label} |
+ );
+ })
+ }
+
+
+ {
+ row.map(item =>{
+ return (
+ {item.slots.default} |
+ );
+ })
+ }
+
+
+ );
+ }
+ if (elDescriptions.border) {
+ return (
+
+
+ {
+ row.map(item=> {
+ return ([
+ {item.label} | ,
+ {item.slots.default} |
+ ]);
+ })
+ }
+
+
+ );
+ }
+ return (
+
+
+ {
+ row.map(item=> {
+ return (
+
+
+ {item.props.label}
+ {item.slots.default}
+
+ | );
+ })
+ }
+
+
+ );
+ }
+};
diff --git a/packages/descriptions/src/index.js b/packages/descriptions/src/index.js
new file mode 100644
index 00000000000..560c3ac5a2e
--- /dev/null
+++ b/packages/descriptions/src/index.js
@@ -0,0 +1,180 @@
+import DescriptionsRow from './descriptions-row';
+import { isFunction } from 'element-ui/src/utils/types';
+
+export default {
+ name: 'ElDescriptions',
+ components: {
+ [DescriptionsRow.name]: DescriptionsRow
+ },
+ props: {
+ border: {
+ type: Boolean,
+ default: false
+ },
+ column: {
+ type: Number,
+ default: 3
+ },
+ direction: {
+ type: String,
+ default: 'horizontal'
+ },
+ size: {
+ type: String
+ // validator: isValidComponentSize,
+ },
+ title: {
+ type: String,
+ default: ''
+ },
+ extra: {
+ type: String,
+ default: ''
+ },
+ labelStyle: {
+ type: Object
+ },
+ contentStyle: {
+ type: Object
+ },
+ labelClassName: {
+ type: String,
+ default: ''
+ },
+ contentClassName: {
+ type: String,
+ default: ''
+ },
+ colon: {
+ type: Boolean,
+ default: true
+ }
+ },
+ computed: {
+ descriptionsSize() {
+ return this.size || (this.$ELEMENT || {}).size;
+ }
+ },
+ provide() {
+ return {
+ elDescriptions: this
+ };
+ },
+ methods: {
+ getOptionProps(vnode) {
+ if (vnode.componentOptions) {
+ const componentOptions = vnode.componentOptions;
+ const { propsData = {}, Ctor = {} } = componentOptions;
+ const props = (Ctor.options || {}).props || {};
+ const res = {};
+ for (const k in props) {
+ const v = props[k];
+ const defaultValue = v.default;
+ if (defaultValue !== undefined) {
+ res[k] = isFunction(defaultValue) ? defaultValue.call(vnode) : defaultValue;
+ }
+ }
+ return { ...res, ...propsData };
+ }
+ return {};
+ },
+ getSlots(vnode) {
+ let componentOptions = vnode.componentOptions || {};
+ const children = vnode.children || componentOptions.children || [];
+ const slots = {};
+ children.forEach(child => {
+ if (!this.isEmptyElement(child)) {
+ const name = (child.data && child.data.slot) || 'default';
+ slots[name] = slots[name] || [];
+ if (child.tag === 'template') {
+ slots[name].push(child.children);
+ } else {
+ slots[name].push(child);
+ }
+ }
+ });
+ return { ...slots };
+ },
+ isEmptyElement(c) {
+ return !(c.tag || (c.text && c.text.trim() !== ''));
+ },
+ filledNode(node, span, count, isLast = false) {
+ if (!node.props) {
+ node.props = {};
+ }
+ if (span > count) {
+ node.props.span = count;
+ }
+ if (isLast) {
+ // set the max span, cause of the last td
+ node.props.span = count;
+ }
+ return node;
+ },
+ getRows() {
+ const children = ((this.$slots.default || []).filter(vnode => vnode.tag &&
+ vnode.componentOptions && vnode.componentOptions.Ctor.options.name === 'ElDescriptionsItem'));
+ const nodes = children.map(vnode => {
+ return {
+ props: this.getOptionProps(vnode),
+ slots: this.getSlots(vnode),
+ vnode
+ };
+ });
+ const rows = [];
+ let temp = [];
+ let count = this.column;
+
+ nodes.forEach((node, index) => {
+ const span = node.props.span || 1;
+
+ if (index === children.length - 1) {
+ temp.push(this.filledNode(node, span, count, true));
+ rows.push(temp);
+ return;
+ }
+
+ if (span < count) {
+ count -= span;
+ temp.push(node);
+ } else {
+ temp.push(this.filledNode(node, span, count));
+ rows.push(temp);
+ count = this.column;
+ temp = [];
+ }
+ });
+
+ return rows;
+ }
+ },
+ render() {
+ const { title, extra, border, descriptionsSize, $slots } = this;
+ const rows = this.getRows();
+
+ return (
+
+ {
+ (title || extra || $slots.title || $slots.extra)
+ ?
+ : null
+ }
+
+
+
+ {rows.map(row => (
+
+ ))}
+
+
+
+ );
+ }
+};
diff --git a/packages/theme-chalk/src/common/var.scss b/packages/theme-chalk/src/common/var.scss
index 79c91397a19..400fdd0f6e3 100644
--- a/packages/theme-chalk/src/common/var.scss
+++ b/packages/theme-chalk/src/common/var.scss
@@ -971,6 +971,13 @@ $--empty-image-width: 160px !default;
$--empty-description-margin-top: 20px !default;
$--empty-bottom-margin-top: 20px !default;
+/* Descriptions
+-------------------------- */
+$--descriptions-header-margin-bottom: 20px !default;
+$--descriptions-title-font-size: 16px !default;
+$--descriptions-table-border: 1px solid $--border-color-lighter !default;
+$--descriptions-item-bordered-label-background: #fafafa !default;
+
/* Skeleton
--------------------------*/
$--skeleton-color: #f2f2f2 !default;
diff --git a/packages/theme-chalk/src/descriptions-item.scss b/packages/theme-chalk/src/descriptions-item.scss
new file mode 100644
index 00000000000..34148f787a0
--- /dev/null
+++ b/packages/theme-chalk/src/descriptions-item.scss
@@ -0,0 +1,32 @@
+@import 'mixins/mixins';
+@import 'common/var';
+
+@include b(descriptions-item) {
+
+ @include e(container) {
+ display: flex;
+ }
+
+ @include e(label) {
+ &.has-colon {
+ &::after {
+ content: ':';
+ position: relative;
+ top: -0.5px;
+ }
+ }
+ &.is-bordered-label {
+ font-weight: bold;
+ color: $--color-text-secondary;
+ background: $--descriptions-item-bordered-label-background;
+ }
+
+ &:not(.is-bordered-label) {
+ margin-right: 10px;
+ }
+ }
+
+ @include e(content) {
+
+ }
+}
diff --git a/packages/theme-chalk/src/descriptions.scss b/packages/theme-chalk/src/descriptions.scss
new file mode 100644
index 00000000000..fe575fddda2
--- /dev/null
+++ b/packages/theme-chalk/src/descriptions.scss
@@ -0,0 +1,111 @@
+@import 'mixins/mixins';
+@import 'common/var';
+@import 'descriptions-item';
+
+@include b(descriptions) {
+ box-sizing: border-box;
+ font-size: $--font-size-base;
+ color: $--color-text-primary;
+
+ @include e(header) {
+ display: flex;
+ justify-content: space-between;
+ align-items: center;
+ margin-bottom: $--descriptions-header-margin-bottom;
+
+ @include e(title) {
+ font-size: $--descriptions-title-font-size;
+ font-weight: bold;
+ }
+ }
+
+ @include e(body) {
+ color: $--color-text-regular;
+ background-color: $--color-white;
+
+ table {
+ border-collapse: collapse;
+ width: 100%;
+ table-layout: fixed;
+
+ th, td {
+ box-sizing: border-box;
+ text-align: left;
+ font-weight: normal;
+ line-height: 1.5;
+
+ @include when(left) {
+ text-align: left;
+ }
+
+ @include when(center) {
+ text-align: center;
+ }
+
+ @include when(right) {
+ text-align: right;
+ }
+ }
+ }
+ }
+
+ .is-bordered {
+ table-layout: auto;
+ th, td {
+ border: $--descriptions-table-border;
+ padding: 12px 10px;
+ }
+ }
+
+ :not(.is-bordered) {
+ th, td {
+ padding-bottom: 12px;
+ }
+ }
+
+ @include m(medium) {
+ &.is-bordered {
+ th, td {
+ padding: 10px;
+ }
+ }
+
+ &:not(.is-bordered) {
+ th, td {
+ padding-bottom: 10px;
+ }
+ }
+ }
+
+ @include m(small) {
+ font-size: 12px;
+
+ &.is-bordered {
+ th, td {
+ padding: 8px 10px;
+ }
+ }
+
+ &:not(.is-bordered) {
+ th, td {
+ padding-bottom: 8px;
+ }
+ }
+ }
+
+ @include m(mini) {
+ font-size: 12px;
+
+ &.is-bordered {
+ th, td {
+ padding: 6px 10px;
+ }
+ }
+
+ &:not(.is-bordered) {
+ th, td {
+ padding-bottom: 6px;
+ }
+ }
+ }
+}
diff --git a/packages/theme-chalk/src/index.scss b/packages/theme-chalk/src/index.scss
index 276b1a5c29e..2a9fbd4d8d2 100644
--- a/packages/theme-chalk/src/index.scss
+++ b/packages/theme-chalk/src/index.scss
@@ -81,3 +81,5 @@
@import "./skeleton.scss";
@import "./skeleton-item.scss";
@import "./empty.scss";
+@import "./descriptions.scss";
+@import "./descriptions-item.scss";
diff --git a/src/index.js b/src/index.js
index 609246ddffa..385b9ea6f12 100644
--- a/src/index.js
+++ b/src/index.js
@@ -85,6 +85,8 @@ import Popconfirm from '../packages/popconfirm/index.js';
import Skeleton from '../packages/skeleton/index.js';
import SkeletonItem from '../packages/skeleton-item/index.js';
import Empty from '../packages/empty/index.js';
+import Descriptions from '../packages/descriptions/index.js';
+import DescriptionsItem from '../packages/descriptions-item/index.js';
import locale from 'element-ui/src/locale';
import CollapseTransition from 'element-ui/src/transitions/collapse-transition';
@@ -169,6 +171,8 @@ const components = [
Skeleton,
SkeletonItem,
Empty,
+ Descriptions,
+ DescriptionsItem,
CollapseTransition
];
@@ -293,5 +297,7 @@ export default {
Popconfirm,
Skeleton,
SkeletonItem,
- Empty
+ Empty,
+ Descriptions,
+ DescriptionsItem
};
diff --git a/test/unit/specs/descriptions.spec.js b/test/unit/specs/descriptions.spec.js
new file mode 100644
index 00000000000..b2c93777de0
--- /dev/null
+++ b/test/unit/specs/descriptions.spec.js
@@ -0,0 +1,159 @@
+// import Descriptions from 'packages/descriptions';
+// import DescriptionsItem from '../src/description-item';
+
+// import { createTest, destroyVM, createVue, waitImmediate, wait} from '../util';
+import { destroyVM, createVue, waitImmediate } from '../util';
+
+describe('Descriptions', () => {
+ let vm;
+
+ afterEach(() => {
+ destroyVM(vm);
+ });
+
+ it('render test', () => {
+ vm = createVue(
+ {
+ template: `
+
+
+
+ `
+ },
+ true
+ );
+ const el = vm.$el;
+ expect(el.querySelector('.el-descriptions__title').textContent).to.equal('title');
+ expect(el.querySelector('.el-descriptions__extra').textContent).to.equal('extra');
+ expect(el.querySelectorAll('.el-descriptions-item__label').length).to.equal(4);
+ });
+
+ it('should render border props', () => {
+ vm = createVue(
+ {
+ template: `
+
+ {{ item }}
+ {
+ vm = createVue(
+ {
+ template: `
+
+ {{ item }}
+
+ `
+ },
+ true);
+
+ expect(Array.from(vm.$el.querySelector('.el-descriptions-item__label').classList)).to.contain('label-class-name');
+ expect(Array.from(vm.$el.querySelector('.el-descriptions-item__content').classList)).to.contain('content-class-name');
+ });
+
+ it('should render column props', async() => {
+ vm = createVue({
+ template: `
+
+ {{ item }}
+
+ `,
+ data() {
+ return {
+ border: false
+ };
+ }
+ }, true);
+
+ expect(vm.$el.querySelector('tr').children.length).to.equal(5);
+ vm.border = true;
+ await waitImmediate();
+ expect(vm.$el.querySelector('tr').children.length).to.equal(10);
+ });
+
+ it('should render direction props', async() => {
+ vm = createVue({
+
+ template: `
+
+ {{ item }}
+
+ `,
+ data() {
+ return {
+ direction: 'horizontal'
+ };
+ }
+ }, true);
+
+ expect(vm.$el.querySelector('tr').children.length).to.equal(10);
+ expect(vm.$el.querySelectorAll('tr')[0].children[0].innerHTML).to.equal(vm.$el.querySelectorAll('tr')[0].children[1].innerHTML);
+ vm.direction = 'vertical';
+ await waitImmediate();
+ expect(vm.$el.querySelector('tr').children.length).to.equal(5);
+ expect(vm.$el.querySelectorAll('tr')[0].children[0].innerHTML).to.equal(vm.$el.querySelectorAll('tr')[1].children[0].innerHTML);
+ });
+
+ it('should render title slots', async() => {
+ vm = createVue({
+ template: `
+
+ title
+ {{ item }}
+
+ `
+ }, true);
+
+ expect(vm.$el.querySelector('.el-descriptions__title').innerText).to.equal('title');
+ });
+
+ it('should render span props', async() => {
+ vm = createVue({
+ template: `
+
+ 1
+ 2
+ 3
+
+ `
+ }, true);
+
+ expect(vm.$el.querySelectorAll('td')[1].getAttribute('colSpan')).to.equal('2');
+ });
+
+ it('re-rendered when slots is updated', async() => {
+ const CHANGE_VALUE = 'company';
+ vm = createVue({
+ template: `
+
+
+
+ {{remark}}
+
+
+
+
+ `,
+ data() {
+ return {
+ remarks: ['school', 'hospital']
+ };
+ },
+ methods: {
+ onClick() {
+ this.$set(this.remarks, 0, CHANGE_VALUE);
+ }
+ }
+ }, true);
+ vm.$el.querySelector('button').click();
+ await waitImmediate();
+ expect(vm.$el.querySelector('.el-tag').innerText).to.equal(CHANGE_VALUE);
+ });
+});
diff --git a/types/descriptions-item.d.ts b/types/descriptions-item.d.ts
new file mode 100644
index 00000000000..428dc4d31f5
--- /dev/null
+++ b/types/descriptions-item.d.ts
@@ -0,0 +1,37 @@
+import { ElementUIComponent } from './component'
+import { VNode } from 'vue'
+
+interface ElDescriptionsItemSlots {
+ /* label slot: custom label */
+ label: VNode[]
+
+ /* default slot: custom content */
+ default: VNode[]
+
+ [key: string]: VNode[]
+}
+
+/** description item. **/
+export declare class ElDescriptionsItem extends ElementUIComponent {
+
+ /* label text */
+ label: string
+
+ /* the number of columns included */
+ span: number
+
+ /* custom label class name */
+ labelClassName: string
+
+ /* custom content class name */
+ contentClassName: string
+
+ /* custom label style */
+ labelStyle: object
+
+ /* custom content style */
+ contentStyle: object
+
+ $slots: ElDescriptionsItemSlots
+
+}
diff --git a/types/descriptions.d.ts b/types/descriptions.d.ts
new file mode 100644
index 00000000000..5d247765fa3
--- /dev/null
+++ b/types/descriptions.d.ts
@@ -0,0 +1,52 @@
+import { ElementUIComponent } from './component'
+import { VNode } from 'vue'
+
+interface ElDescriptionsSlots {
+ /* title slot: custom title, display on the top left */
+ title: VNode[]
+
+ /* title slot: custom extra area, display on the top right */
+ extra: VNode[]
+
+ [key: string]: VNode[]
+}
+
+/** Display multiple fields in list form. **/
+export declare class ElDescriptions extends ElementUIComponent {
+
+ /* with or without border */
+ border: boolean
+
+ /* numbers of Descriptions Item in one line */
+ column: number
+
+ /* direction of list */
+ direction: 'vertical' | 'horizontal'
+
+ /* size of list */
+ size: 'medium' | 'small' | 'mini'
+
+ /* title text, display on the top left */
+ title: string
+
+ /* extra text, display on the top right */
+ extra: string
+
+ /* change default props colon value of Descriptions Item */
+ colon: boolean
+
+ /* custom label class name */
+ labelClassName: string
+
+ /* custom content class name */
+ contentClassName: string
+
+ /* custom label style */
+ labelStyle: object
+
+ /* custom content style */
+ contentStyle: object
+
+ $slots: ElDescriptionsSlots
+
+}
diff --git a/types/element-ui.d.ts b/types/element-ui.d.ts
index d0983c1c650..8dccab6d7de 100644
--- a/types/element-ui.d.ts
+++ b/types/element-ui.d.ts
@@ -85,6 +85,8 @@ import { ElSkeletonItem } from './skeleton-item'
import { ElCascaderPanel } from './cascader-panel'
import { ElEmpty } from './empty'
import { ElSpinner } from './spinner'
+import { ElDescriptions } from './descriptions'
+import { ElDescriptionsItem } from './descriptions-item'
export interface InstallationOptions {
locale: any,
@@ -364,3 +366,9 @@ export class Empty extends ElEmpty {}
/** Spinner Component */
export class Spinner extends ElSpinner {}
+
+/** Description Component */
+export class Descripitions extends ElDescriptions {}
+
+/** Description Item Component */
+export class DescripitionsItem extends ElDescriptionsItem {}
\ No newline at end of file