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 + + + +``` +::: + +### 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 + + + +``` +::: + +### 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 + + + +``` +::: + +### 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 + + + +``` +::: + +### 垂直列表 + +:::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) + ?
+
+ { $slots.title ? $slots.title : title} +
+
+ { $slots.extra ? $slots.extra : 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: ` + + + {{ 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