From 68e07ca6da32d678eb03408df4104b7d582d9b4c Mon Sep 17 00:00:00 2001 From: yanzhuang Date: Thu, 1 Jul 2021 17:03:01 +0800 Subject: [PATCH 01/81] Utils: fix isScroll (#21098) --- src/utils/dom.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/utils/dom.js b/src/utils/dom.js index a88df3242..c3ee8bafa 100644 --- a/src/utils/dom.js +++ b/src/utils/dom.js @@ -183,7 +183,7 @@ export const isScroll = (el, vertical) => { : getStyle(el, 'overflow-x') : getStyle(el, 'overflow'); - return overflow.match(/(scroll|auto)/); + return overflow.match(/(scroll|auto|overlay)/); }; export const getScrollContainer = (el, vertical) => { From 034da49dd9912580988c9394dc42b108ddbb603f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E5=A5=BD=E5=A4=9A=E5=A4=A7=E7=B1=B3?= Date: Thu, 8 Jul 2021 15:26:00 +0800 Subject: [PATCH 02/81] Descriptions: add description component (#21129) --- components.json | 4 +- examples/demo-styles/descriptions.scss | 13 ++ examples/demo-styles/index.scss | 1 + examples/docs/en-US/descriptions.md | 191 ++++++++++++++++++ examples/docs/es/descriptions.md | 191 ++++++++++++++++++ examples/docs/fr-FR/descriptions.md | 191 ++++++++++++++++++ examples/docs/zh-CN/descriptions.md | 191 ++++++++++++++++++ examples/nav.config.json | 16 ++ packages/descriptions-item/index.js | 8 + packages/descriptions/index.js | 8 + packages/descriptions/src/description-item.js | 30 +++ packages/descriptions/src/descriptions-row.js | 115 +++++++++++ packages/descriptions/src/index.js | 180 +++++++++++++++++ packages/theme-chalk/src/common/var.scss | 7 + .../theme-chalk/src/descriptions-item.scss | 32 +++ packages/theme-chalk/src/descriptions.scss | 111 ++++++++++ packages/theme-chalk/src/index.scss | 2 + src/index.js | 8 +- test/unit/specs/descriptions.spec.js | 159 +++++++++++++++ types/descriptions-item.d.ts | 37 ++++ types/descriptions.d.ts | 52 +++++ types/element-ui.d.ts | 8 + 22 files changed, 1553 insertions(+), 2 deletions(-) create mode 100644 examples/demo-styles/descriptions.scss create mode 100644 examples/docs/en-US/descriptions.md create mode 100644 examples/docs/es/descriptions.md create mode 100644 examples/docs/fr-FR/descriptions.md create mode 100644 examples/docs/zh-CN/descriptions.md create mode 100644 packages/descriptions-item/index.js create mode 100644 packages/descriptions/index.js create mode 100644 packages/descriptions/src/description-item.js create mode 100644 packages/descriptions/src/descriptions-row.js create mode 100644 packages/descriptions/src/index.js create mode 100644 packages/theme-chalk/src/descriptions-item.scss create mode 100644 packages/theme-chalk/src/descriptions.scss create mode 100644 test/unit/specs/descriptions.spec.js create mode 100644 types/descriptions-item.d.ts create mode 100644 types/descriptions.d.ts diff --git a/components.json b/components.json index ce0595f02..478a87d6c 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 000000000..8823e93cb --- /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 1638bd094..43c2f44a8 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 000000000..2e93ad5ea --- /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 000000000..2e93ad5ea --- /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 000000000..2e93ad5ea --- /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 000000000..2d6b39a0b --- /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 d3532e47a..c717d63d2 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 000000000..d9b3af1ef --- /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 000000000..ccee18ca1 --- /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 000000000..d764543c5 --- /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 000000000..9fc05a1dc --- /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 000000000..560c3ac5a --- /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 79c91397a..400fdd0f6 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 000000000..34148f787 --- /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 000000000..fe575fddd --- /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 276b1a5c2..2a9fbd4d8 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 609246ddf..385b9ea6f 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 000000000..b2c93777d --- /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 000000000..428dc4d31 --- /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 000000000..5d247765f --- /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 d0983c1c6..8dccab6d7 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 From 10bb2df826f307180b1e3bf1aed5d2c6a3c5b466 Mon Sep 17 00:00:00 2001 From: bliberi Date: Fri, 9 Jul 2021 05:37:54 +0200 Subject: [PATCH 03/81] Translation: update it.js (#21133) added missing translations --- src/locale/lang/it.js | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/src/locale/lang/it.js b/src/locale/lang/it.js index 42f8505d4..d3de52e81 100644 --- a/src/locale/lang/it.js +++ b/src/locale/lang/it.js @@ -20,7 +20,7 @@ export default { nextYear: 'Anno successivo', prevMonth: 'Mese precedente', nextMonth: 'Mese successivo', - year: '', + year: 'anno', month1: 'Gennaio', month2: 'Febbraio', month3: 'Marzo', @@ -72,7 +72,7 @@ export default { }, pagination: { goto: 'Vai a', - pagesize: '/page', + pagesize: '/pagina', total: 'Totale {total}', pageClassifier: '' }, @@ -106,14 +106,14 @@ export default { hasCheckedFormat: '{checked}/{total} selezionati' }, image: { - error: 'FAILED' // to be translated + error: 'ERRORE' }, pageHeader: { - title: 'Back' // to be translated + title: 'Indietro' }, popconfirm: { - confirmButtonText: 'Yes', // to be translated - cancelButtonText: 'No' // to be translated + confirmButtonText: 'Sì', + cancelButtonText: 'No' }, empty: { description: 'Nessun dato' From 960bbcb5629d2ae7ab5379a7937bbd09f7049553 Mon Sep 17 00:00:00 2001 From: lichao <18709270892@163.com> Date: Fri, 9 Jul 2021 16:34:30 +0800 Subject: [PATCH 04/81] RadioGroup: fix RadioGroup used in component causes exception #17908 (#20783) --- packages/radio/src/radio-group.vue | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/packages/radio/src/radio-group.vue b/packages/radio/src/radio-group.vue index 342de3a2b..4cbc6a5c1 100644 --- a/packages/radio/src/radio-group.vue +++ b/packages/radio/src/radio-group.vue @@ -43,7 +43,9 @@ return (this.elFormItem || {}).elFormItemSize; }, _elTag() { - return (this.$vnode.data || {}).tag || 'div'; + let tag = (this.$vnode.data || {}).tag; + if (!tag || tag === 'component') tag = 'div'; + return tag; }, radioGroupSize() { return this.size || this._elFormItemSize || (this.$ELEMENT || {}).size; From 6bbc0465638f618be7631d721b9c1a7f4ac66290 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E5=A5=BD=E5=A4=9A=E5=A4=A7=E7=B1=B3?= Date: Fri, 9 Jul 2021 18:04:34 +0800 Subject: [PATCH 05/81] chore: fix lint and some errors (#21136) * chore: fix lint * chore: fix lint and some errors --- components.json | 4 ++-- packages/descriptions-item/index.js | 2 +- .../src/{description-item.js => descriptions-item.js} | 0 src/locale/lang/it.js | 4 ++-- test/unit/specs/descriptions.spec.js | 4 ---- 5 files changed, 5 insertions(+), 9 deletions(-) rename packages/descriptions/src/{description-item.js => descriptions-item.js} (100%) diff --git a/components.json b/components.json index 478a87d6c..49772f488 100644 --- a/components.json +++ b/components.json @@ -84,6 +84,6 @@ "skeleton": "./packages/skeleton/index.js", "skeleton-item": "./packages/skeleton-item/index.js", "empty": "./packages/empty/index.js", - "descriptions": "./packages/description/index.js", - "descriptions-item": "./packages/description-item/index.js" + "descriptions": "./packages/descriptions/index.js", + "descriptions-item": "./packages/descriptions-item/index.js" } diff --git a/packages/descriptions-item/index.js b/packages/descriptions-item/index.js index d9b3af1ef..b14a8b831 100644 --- a/packages/descriptions-item/index.js +++ b/packages/descriptions-item/index.js @@ -1,4 +1,4 @@ -import DescriptionsItem from '../descriptions/src/description-item'; +import DescriptionsItem from '../descriptions/src/descriptions-item'; /* istanbul ignore next */ DescriptionsItem.install = function install(Vue) { diff --git a/packages/descriptions/src/description-item.js b/packages/descriptions/src/descriptions-item.js similarity index 100% rename from packages/descriptions/src/description-item.js rename to packages/descriptions/src/descriptions-item.js diff --git a/src/locale/lang/it.js b/src/locale/lang/it.js index d3de52e81..e8906d5d8 100644 --- a/src/locale/lang/it.js +++ b/src/locale/lang/it.js @@ -106,10 +106,10 @@ export default { hasCheckedFormat: '{checked}/{total} selezionati' }, image: { - error: 'ERRORE' + error: 'ERRORE' }, pageHeader: { - title: 'Indietro' + title: 'Indietro' }, popconfirm: { confirmButtonText: 'Sì', diff --git a/test/unit/specs/descriptions.spec.js b/test/unit/specs/descriptions.spec.js index b2c93777d..2ceafcf96 100644 --- a/test/unit/specs/descriptions.spec.js +++ b/test/unit/specs/descriptions.spec.js @@ -1,7 +1,3 @@ -// 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', () => { From 09e789bad20bd58de0a291fded28cbf48cb27f48 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E5=A5=BD=E5=A4=9A=E5=A4=A7=E7=B1=B3?= Date: Wed, 21 Jul 2021 15:20:25 +0800 Subject: [PATCH 06/81] Result: add result component (#21171) --- components.json | 3 +- examples/docs/en-US/result.md | 76 ++++++++++++++ examples/docs/es/result.md | 76 ++++++++++++++ examples/docs/fr-FR/result.md | 76 ++++++++++++++ examples/docs/zh-CN/result.md | 76 ++++++++++++++ examples/nav.config.json | 16 +++ packages/result/index.js | 8 ++ packages/result/src/icon-error.vue | 13 +++ packages/result/src/icon-info.vue | 13 +++ packages/result/src/icon-success.vue | 13 +++ packages/result/src/icon-warning.vue | 13 +++ packages/result/src/index.vue | 65 ++++++++++++ packages/skeleton/src/index.vue | 2 +- packages/theme-chalk/src/common/var.scss | 13 +++ packages/theme-chalk/src/index.scss | 1 + packages/theme-chalk/src/result.scss | 61 ++++++++++++ src/index.js | 5 +- test/unit/specs/descriptions.spec.js | 14 +-- test/unit/specs/result.spec.js | 120 +++++++++++++++++++++++ types/element-ui.d.ts | 6 +- types/result.d.ts | 33 +++++++ 21 files changed, 692 insertions(+), 11 deletions(-) create mode 100644 examples/docs/en-US/result.md create mode 100644 examples/docs/es/result.md create mode 100644 examples/docs/fr-FR/result.md create mode 100644 examples/docs/zh-CN/result.md create mode 100644 packages/result/index.js create mode 100644 packages/result/src/icon-error.vue create mode 100644 packages/result/src/icon-info.vue create mode 100644 packages/result/src/icon-success.vue create mode 100644 packages/result/src/icon-warning.vue create mode 100644 packages/result/src/index.vue create mode 100644 packages/theme-chalk/src/result.scss create mode 100644 test/unit/specs/result.spec.js create mode 100644 types/result.d.ts diff --git a/components.json b/components.json index 49772f488..62d748707 100644 --- a/components.json +++ b/components.json @@ -85,5 +85,6 @@ "skeleton-item": "./packages/skeleton-item/index.js", "empty": "./packages/empty/index.js", "descriptions": "./packages/descriptions/index.js", - "descriptions-item": "./packages/descriptions-item/index.js" + "descriptions-item": "./packages/descriptions-item/index.js", + "result": "./packages/result/index.js" } diff --git a/examples/docs/en-US/result.md b/examples/docs/en-US/result.md new file mode 100644 index 000000000..d23a5b01f --- /dev/null +++ b/examples/docs/en-US/result.md @@ -0,0 +1,76 @@ +## Result + +Used to give feedback on the result of user's operation or access exception. + +### Basic usage + +:::demo + +```html + + + + + + + + + + + + + + + + + + + + + + +``` + +::: + +### Customized content + +:::demo + +```html + + + + +``` + +::: + +### Result Attributes + +| Attribute | Description | Type | Accepted Values | Default | +|------------- |---------------- |---------------- |---------------------- |-------- | +| title | title | string | — | — | +| sub-title | sub title | string | — | — | +| icon | icon type | string | success / warning / info / error | info | + +### Result Slots + +| Name | Description | +|------|--------| +| icon | custom icon | +| title | custom title | +| subTitle | custom sub title | +| extra | custom extra area | diff --git a/examples/docs/es/result.md b/examples/docs/es/result.md new file mode 100644 index 000000000..d23a5b01f --- /dev/null +++ b/examples/docs/es/result.md @@ -0,0 +1,76 @@ +## Result + +Used to give feedback on the result of user's operation or access exception. + +### Basic usage + +:::demo + +```html + + + + + + + + + + + + + + + + + + + + + + +``` + +::: + +### Customized content + +:::demo + +```html + + + + +``` + +::: + +### Result Attributes + +| Attribute | Description | Type | Accepted Values | Default | +|------------- |---------------- |---------------- |---------------------- |-------- | +| title | title | string | — | — | +| sub-title | sub title | string | — | — | +| icon | icon type | string | success / warning / info / error | info | + +### Result Slots + +| Name | Description | +|------|--------| +| icon | custom icon | +| title | custom title | +| subTitle | custom sub title | +| extra | custom extra area | diff --git a/examples/docs/fr-FR/result.md b/examples/docs/fr-FR/result.md new file mode 100644 index 000000000..d23a5b01f --- /dev/null +++ b/examples/docs/fr-FR/result.md @@ -0,0 +1,76 @@ +## Result + +Used to give feedback on the result of user's operation or access exception. + +### Basic usage + +:::demo + +```html + + + + + + + + + + + + + + + + + + + + + + +``` + +::: + +### Customized content + +:::demo + +```html + + + + +``` + +::: + +### Result Attributes + +| Attribute | Description | Type | Accepted Values | Default | +|------------- |---------------- |---------------- |---------------------- |-------- | +| title | title | string | — | — | +| sub-title | sub title | string | — | — | +| icon | icon type | string | success / warning / info / error | info | + +### Result Slots + +| Name | Description | +|------|--------| +| icon | custom icon | +| title | custom title | +| subTitle | custom sub title | +| extra | custom extra area | diff --git a/examples/docs/zh-CN/result.md b/examples/docs/zh-CN/result.md new file mode 100644 index 000000000..2d6e54926 --- /dev/null +++ b/examples/docs/zh-CN/result.md @@ -0,0 +1,76 @@ +## Result 结果 + +用于对用户的操作结果或者异常状态做反馈。 + +### 基础用法 + +:::demo + +```html + + + + + + + + + + + + + + + + + + + + + + +``` + +::: + +### 自定义内容 + +:::demo + +```html + + + + +``` + +::: + +### Result Attributes + +| 参数 | 说明 | 类型 | 可选值 | 默认值 | +|------------- |---------------- |---------------- |---------------------- |-------- | +| title | 标题 | string | — | — | +| sub-title | 二级标题 | string | — | — | +| icon | 图标类型 | string | success / warning / info / error | info | + +### Result Slots + +| Name | 说明 | +|------|--------| +| icon | 自定义图标 | +| title | 自定义标题 | +| subTitle | 自定义二级标题 | +| extra | 自定义底部额外区域 | diff --git a/examples/nav.config.json b/examples/nav.config.json index c717d63d2..3377ac933 100644 --- a/examples/nav.config.json +++ b/examples/nav.config.json @@ -188,6 +188,10 @@ { "path": "/descriptions", "title": "Descriptions 描述列表" + }, + { + "path": "/result", + "title": "Result 结果" } ] }, @@ -494,6 +498,10 @@ { "path": "/descriptions", "title": "Descriptions" + }, + { + "path": "/result", + "title": "Result" } ] }, @@ -804,6 +812,10 @@ { "path": "/descriptions", "title": "Descriptions" + }, + { + "path": "/result", + "title": "Result" } ] }, @@ -1114,6 +1126,10 @@ { "path": "/descriptions", "title": "Descriptions" + }, + { + "path": "/result", + "title": "Result" } ] }, diff --git a/packages/result/index.js b/packages/result/index.js new file mode 100644 index 000000000..b967c163a --- /dev/null +++ b/packages/result/index.js @@ -0,0 +1,8 @@ +import Result from './src/index.vue'; + +/* istanbul ignore next */ +Result.install = function(Vue) { + Vue.component(Result.name, Result); +}; + +export default Result; diff --git a/packages/result/src/icon-error.vue b/packages/result/src/icon-error.vue new file mode 100644 index 000000000..7888f7b75 --- /dev/null +++ b/packages/result/src/icon-error.vue @@ -0,0 +1,13 @@ + + + diff --git a/packages/result/src/icon-info.vue b/packages/result/src/icon-info.vue new file mode 100644 index 000000000..b019d1846 --- /dev/null +++ b/packages/result/src/icon-info.vue @@ -0,0 +1,13 @@ + + + diff --git a/packages/result/src/icon-success.vue b/packages/result/src/icon-success.vue new file mode 100644 index 000000000..7f8217c16 --- /dev/null +++ b/packages/result/src/icon-success.vue @@ -0,0 +1,13 @@ + + + diff --git a/packages/result/src/icon-warning.vue b/packages/result/src/icon-warning.vue new file mode 100644 index 000000000..a5aaa8d9a --- /dev/null +++ b/packages/result/src/icon-warning.vue @@ -0,0 +1,13 @@ + + + diff --git a/packages/result/src/index.vue b/packages/result/src/index.vue new file mode 100644 index 000000000..19b300bb8 --- /dev/null +++ b/packages/result/src/index.vue @@ -0,0 +1,65 @@ + + diff --git a/packages/skeleton/src/index.vue b/packages/skeleton/src/index.vue index 150c200e6..e89746252 100644 --- a/packages/skeleton/src/index.vue +++ b/packages/skeleton/src/index.vue @@ -6,7 +6,7 @@ { { template: ` - + ` }, @@ -29,8 +29,8 @@ describe('Descriptions', () => { { template: ` - {{ item }} - {{ item }} + ` }, true @@ -44,7 +44,7 @@ describe('Descriptions', () => { { template: ` - {{ item }} + {{ item }} ` }, @@ -58,7 +58,7 @@ describe('Descriptions', () => { vm = createVue({ template: ` - {{ item }} + {{ item }} `, data() { @@ -79,7 +79,7 @@ describe('Descriptions', () => { template: ` - {{ item }} + {{ item }} `, data() { @@ -102,7 +102,7 @@ describe('Descriptions', () => { template: ` - {{ item }} + {{ item }} ` }, true); diff --git a/test/unit/specs/result.spec.js b/test/unit/specs/result.spec.js new file mode 100644 index 000000000..0ef5ad5f4 --- /dev/null +++ b/test/unit/specs/result.spec.js @@ -0,0 +1,120 @@ +import { createVue, destroyVM, waitImmediate } from '../util'; + +const AXIOM = 'Rem is the best girl'; + +describe('Result', () => { + + let vm; + afterEach(() => { + destroyVM(vm); + }); + + it('render test', () => { + vm = createVue({ + template: '' + }, true); + expect(vm.$el.querySelector('.el-result__icon')).to.exist; + expect(Array.from(vm.$el.classList)).to.contain('el-result'); + }); + + it('should render title props', () => { + vm = createVue({ + template: '', + data() { + return { + title: AXIOM + }; + } + }, true); + expect(vm.$el.querySelector('.el-result__title').innerText).to.be.equal(AXIOM); + }); + + it('should render sub-title props', () => { + vm = createVue({ + template: '', + data() { + return { + subTitle: AXIOM + }; + } + }, true); + expect(vm.$el.querySelector('.el-result__subtitle').innerText).to.be.equal(AXIOM); + }); + + it('should render icon props', async() => { + vm = createVue({ + template: '', + data() { + return { + icon: 'success' + }; + } + }, true); + expect(vm.$el.querySelector('.el-result__icon svg')).to.exist; + expect(Array.from(vm.$el.querySelector('.el-result__icon svg').classList)).to.contain('icon-success'); + vm.icon = 'error'; + await waitImmediate(); + expect(vm.$el.querySelector('.el-result__icon svg')).to.exist; + expect(Array.from(vm.$el.querySelector('.el-result__icon svg').classList)).to.contain('icon-error'); + vm.icon = 'warning'; + await waitImmediate(); + expect(vm.$el.querySelector('.el-result__icon svg')).to.exist; + expect(Array.from(vm.$el.querySelector('.el-result__icon svg').classList)).to.contain('icon-warning'); + vm.icon = 'info'; + await waitImmediate(); + expect(vm.$el.querySelector('.el-result__icon svg')).to.exist; + expect(Array.from(vm.$el.querySelector('.el-result__icon svg').classList)).to.contain('icon-info'); + }); + + it('should render icon slots', () => { + vm = createVue({ + template: '', + data() { + return { + icon: AXIOM + }; + } + }, true); + expect(vm.$el.querySelector('.el-result__icon')).to.exist; + expect(vm.$el.querySelector('.el-result__icon').innerText).to.be.equal(AXIOM); + }); + + it('should render title slots', () => { + vm = createVue({ + template: '', + data() { + return { + title: AXIOM + }; + } + }, true); + expect(vm.$el.querySelector('.el-result__title')).to.exist; + expect(vm.$el.querySelector('.el-result__title').innerText).to.be.equal(AXIOM); + }); + + it('should render sub-title slots', () => { + vm = createVue({ + template: '', + data() { + return { + subTitle: AXIOM + }; + } + }, true); + expect(vm.$el.querySelector('.el-result__subtitle')).to.exist; + expect(vm.$el.querySelector('.el-result__subtitle').innerText).to.be.equal(AXIOM); + }); + + it('should render extra slots', () => { + vm = createVue({ + template: '', + data() { + return { + extra: AXIOM + }; + } + }, true); + expect(vm.$el.querySelector('.el-result__extra')).to.exist; + expect(vm.$el.querySelector('.el-result__extra').innerText).to.be.equal(AXIOM); + }); +}); diff --git a/types/element-ui.d.ts b/types/element-ui.d.ts index 8dccab6d7..c4ee1af20 100644 --- a/types/element-ui.d.ts +++ b/types/element-ui.d.ts @@ -87,6 +87,7 @@ import { ElEmpty } from './empty' import { ElSpinner } from './spinner' import { ElDescriptions } from './descriptions' import { ElDescriptionsItem } from './descriptions-item' +import { ElResult } from './result' export interface InstallationOptions { locale: any, @@ -371,4 +372,7 @@ export class Spinner extends ElSpinner {} export class Descripitions extends ElDescriptions {} /** Description Item Component */ -export class DescripitionsItem extends ElDescriptionsItem {} \ No newline at end of file +export class DescripitionsItem extends ElDescriptionsItem {} + +/** Result Component */ +export class Result extends ElResult {} \ No newline at end of file diff --git a/types/result.d.ts b/types/result.d.ts new file mode 100644 index 000000000..dc56f149b --- /dev/null +++ b/types/result.d.ts @@ -0,0 +1,33 @@ +import { ElementUIComponent } from './component' +import { VNode } from 'vue' + +interface ElResultSlots { + /* title slot: custom title */ + title: VNode[] + + /* icon slot: custom icon */ + icon: VNode[] + + /* subTitle slot: custom sub title */ + subTitle: VNode[] + + /* extra slot: custom extra area, display on the top right */ + extra: VNode[] + + [key: string]: VNode[] +} + +/** Used to give feedback on the result of user's operation or access exception. **/ +export declare class ElResult extends ElementUIComponent { + + /* title */ + title: string + + /* sub title */ + subTitle: string + + /* icon type */ + icon: 'success' | 'warning' | 'info' | 'error' + + $slots: ElResultSlots +} From 1ef72a32833fbc136d2336bb99385661e642064a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E5=A5=BD=E5=A4=9A=E5=A4=A7=E7=B1=B3?= Date: Fri, 23 Jul 2021 17:22:18 +0800 Subject: [PATCH 07/81] Message: fix message[type] (#21088) --- packages/message/src/main.js | 18 +++++++++++------- 1 file changed, 11 insertions(+), 7 deletions(-) diff --git a/packages/message/src/main.js b/packages/message/src/main.js index 3799e7f2e..b6bad3d14 100644 --- a/packages/message/src/main.js +++ b/packages/message/src/main.js @@ -2,6 +2,7 @@ import Vue from 'vue'; import Main from './main.vue'; import { PopupManager } from 'element-ui/src/utils/popup'; import { isVNode } from 'element-ui/src/utils/vdom'; +import { isObject } from 'element-ui/src/utils/types'; let MessageConstructor = Vue.extend(Main); let instance; @@ -44,14 +45,17 @@ const Message = function(options) { }; ['success', 'warning', 'info', 'error'].forEach(type => { - Message[type] = options => { - if (typeof options === 'string') { - options = { - message: options - }; + Message[type] = (options) => { + if (isObject(options) && !isVNode(options)) { + return Message({ + ...options, + type + }); } - options.type = type; - return Message(options); + return Message({ + type, + message: options + }); }; }); From f1252dcf6151b421efe40187965029aebc552461 Mon Sep 17 00:00:00 2001 From: Nekojita1 Date: Wed, 28 Jul 2021 15:05:24 +0800 Subject: [PATCH 08/81] Carousel: reset the timer when setActiveItem method is called (#20846) --- packages/carousel/src/main.vue | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/packages/carousel/src/main.vue b/packages/carousel/src/main.vue index b5d90e4a0..197cb83fc 100644 --- a/packages/carousel/src/main.vue +++ b/packages/carousel/src/main.vue @@ -236,6 +236,11 @@ export default { this.timer = setInterval(this.playSlides, this.interval); }, + resetTimer() { + this.pauseTimer(); + this.startTimer(); + }, + setActiveItem(index) { if (typeof index === 'string') { const filteredItems = this.items.filter(item => item.name === index); @@ -260,6 +265,7 @@ export default { if (oldIndex === this.activeIndex) { this.resetItemPosition(oldIndex); } + this.resetTimer(); }, prev() { From 19f25baffc8e672e23d35f74d863b4529203a136 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E5=A5=BD=E5=A4=9A=E5=A4=A7=E7=B1=B3?= Date: Wed, 28 Jul 2021 15:57:11 +0800 Subject: [PATCH 09/81] Cascader: fix emitPath (#21185) --- packages/cascader-panel/src/cascader-panel.vue | 14 +++++++++++--- packages/cascader-panel/src/store.js | 10 +++------- packages/cascader/src/cascader.vue | 14 +++++++++++--- 3 files changed, 25 insertions(+), 13 deletions(-) diff --git a/packages/cascader-panel/src/cascader-panel.vue b/packages/cascader-panel/src/cascader-panel.vue index d3fe6fb81..135391da8 100644 --- a/packages/cascader-panel/src/cascader-panel.vue +++ b/packages/cascader-panel/src/cascader-panel.vue @@ -157,7 +157,7 @@ export default { }, mounted() { - if (!isEmpty(this.value)) { + if (!this.isEmptyValue(this.value)) { this.syncCheckedValue(); } }, @@ -195,13 +195,21 @@ export default { node.syncCheckState(this.checkedValue); }); }, + isEmptyValue(val) { + const { multiple, config } = this; + const { emitPath } = config; + if (multiple || emitPath) { + return isEmpty(val); + } + return false; + }, syncActivePath() { const { store, multiple, activePath, checkedValue } = this; if (!isEmpty(activePath)) { const nodes = activePath.map(node => this.getNodeByValue(node.getValue())); this.expandNodes(nodes); - } else if (!isEmpty(checkedValue)) { + } else if (!this.isEmptyValue(checkedValue)) { const value = multiple ? checkedValue[0] : checkedValue; const checkedNode = this.getNodeByValue(value) || {}; const nodes = (checkedNode.pathNodes || []).slice(0, -1); @@ -361,7 +369,7 @@ export default { const nodes = this.getFlattedNodes(leafOnly); return nodes.filter(node => node.checked); } else { - return isEmpty(checkedValue) + return this.isEmptyValue(checkedValue) ? [] : [this.getNodeByValue(checkedValue)]; } diff --git a/packages/cascader-panel/src/store.js b/packages/cascader-panel/src/store.js index 90e68bad8..9a0ede219 100644 --- a/packages/cascader-panel/src/store.js +++ b/packages/cascader-panel/src/store.js @@ -51,12 +51,8 @@ export default class Store { } getNodeByValue(value) { - if (value) { - const nodes = this.getFlattedNodes(false, !this.config.lazy) - .filter(node => (valueEquals(node.path, value) || node.value === value)); - return nodes && nodes.length ? nodes[0] : null; - } - return null; + const nodes = this.getFlattedNodes(false, !this.config.lazy) + .filter(node => (valueEquals(node.path, value) || node.value === value)); + return nodes && nodes.length ? nodes[0] : null; } - } diff --git a/packages/cascader/src/cascader.vue b/packages/cascader/src/cascader.vue index 0546fc4ab..2458d923c 100644 --- a/packages/cascader/src/cascader.vue +++ b/packages/cascader/src/cascader.vue @@ -231,7 +231,7 @@ export default { data() { return { dropDownVisible: false, - checkedValue: this.value || null, + checkedValue: this.value, inputHover: false, inputValue: null, presentText: null, @@ -350,7 +350,7 @@ export default { this.inputInitialHeight = input.$el.offsetHeight || InputSizeMap[this.realSize] || 40; } - if (!isEmpty(this.value)) { + if (!this.isEmptyValue(this.value)) { this.computePresentContent(); } @@ -485,9 +485,17 @@ export default { } }); }, + isEmptyValue(val) { + const { multiple } = this; + const { emitPath } = this.panel.config; + if (multiple || emitPath) { + return isEmpty(val); + } + return false; + }, computePresentText() { const { checkedValue, config } = this; - if (!isEmpty(checkedValue)) { + if (!this.isEmptyValue(checkedValue)) { const node = this.panel.getNodeByValue(checkedValue); if (node && (config.checkStrictly || node.isLeaf)) { this.presentText = node.getText(this.showAllLevels, this.separator); From 494386733724e9742577834c7e838c3a656887d9 Mon Sep 17 00:00:00 2001 From: pofore Date: Thu, 29 Jul 2021 11:39:12 +0800 Subject: [PATCH 10/81] Select: fix select filterable bug (#17494) --- packages/select/src/select.vue | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/packages/select/src/select.vue b/packages/select/src/select.vue index 9bca678ef..4c5fb6cdd 100644 --- a/packages/select/src/select.vue +++ b/packages/select/src/select.vue @@ -83,13 +83,12 @@ :tabindex="(multiple && filterable) ? '-1' : null" @focus="handleFocus" @blur="handleBlur" - @keyup.native="debouncedOnInputChange" + @input="debouncedOnInputChange" @keydown.native.down.stop.prevent="navigateOptions('next')" @keydown.native.up.stop.prevent="navigateOptions('prev')" @keydown.native.enter.prevent="selectOption" @keydown.native.esc.stop.prevent="visible = false" @keydown.native.tab="visible = false" - @paste.native="debouncedOnInputChange" @mouseenter.native="inputHovering = true" @mouseleave.native="inputHovering = false">