From df6965f875db78e59b4b58009ec9bb9e72d49829 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E5=A5=BD=E5=A4=9A=E5=A4=A7=E7=B1=B3?= Date: Tue, 1 Jun 2021 17:23:13 +0800 Subject: [PATCH] Skeleton: add skeleton component (#21038) --- components.json | 4 +- examples/demo-styles/index.scss | 1 + examples/demo-styles/skeleton.scss | 46 +++ examples/docs/en-US/skeleton.md | 315 +++++++++++++++++++ examples/docs/es/skeleton.md | 315 +++++++++++++++++++ examples/docs/fr-FR/skeleton.md | 315 +++++++++++++++++++ examples/docs/zh-CN/skeleton.md | 316 ++++++++++++++++++++ examples/nav.config.json | 16 + packages/skeleton-item/index.js | 8 + packages/skeleton/index.js | 8 + packages/skeleton/src/img-placeholder.vue | 16 + packages/skeleton/src/index.vue | 76 +++++ packages/skeleton/src/item.vue | 22 ++ packages/theme-chalk/src/common/var.scss | 10 + packages/theme-chalk/src/index.scss | 2 + packages/theme-chalk/src/skeleton-item.scss | 84 ++++++ packages/theme-chalk/src/skeleton.scss | 40 +++ src/index.js | 8 +- test/unit/specs/skeleton.spec.js | 106 +++++++ types/element-ui.d.ts | 8 + types/skeleton-item.d.ts | 7 + types/skeleton.d.ts | 33 ++ 22 files changed, 1754 insertions(+), 2 deletions(-) create mode 100644 examples/demo-styles/skeleton.scss create mode 100644 examples/docs/en-US/skeleton.md create mode 100644 examples/docs/es/skeleton.md create mode 100644 examples/docs/fr-FR/skeleton.md create mode 100644 examples/docs/zh-CN/skeleton.md create mode 100644 packages/skeleton-item/index.js create mode 100644 packages/skeleton/index.js create mode 100644 packages/skeleton/src/img-placeholder.vue create mode 100644 packages/skeleton/src/index.vue create mode 100644 packages/skeleton/src/item.vue create mode 100644 packages/theme-chalk/src/skeleton-item.scss create mode 100644 packages/theme-chalk/src/skeleton.scss create mode 100644 test/unit/specs/skeleton.spec.js create mode 100644 types/skeleton-item.d.ts create mode 100644 types/skeleton.d.ts diff --git a/components.json b/components.json index 754da2b61..2abbcbb4e 100644 --- a/components.json +++ b/components.json @@ -80,5 +80,7 @@ "cascader-panel": "./packages/cascader-panel/index.js", "avatar": "./packages/avatar/index.js", "drawer": "./packages/drawer/index.js", - "popconfirm": "./packages/popconfirm/index.js" + "popconfirm": "./packages/popconfirm/index.js", + "skeleton": "./packages/skeleton/index.js", + "skeleton-item": "./packages/skeleton-item/index.js" } diff --git a/examples/demo-styles/index.scss b/examples/demo-styles/index.scss index 2186b96d2..1638bd094 100644 --- a/examples/demo-styles/index.scss +++ b/examples/demo-styles/index.scss @@ -44,4 +44,5 @@ @import "./infinite-scroll.scss"; @import "./avatar.scss"; @import "./drawer.scss"; +@import "./skeleton.scss"; diff --git a/examples/demo-styles/skeleton.scss b/examples/demo-styles/skeleton.scss new file mode 100644 index 000000000..c349db827 --- /dev/null +++ b/examples/demo-styles/skeleton.scss @@ -0,0 +1,46 @@ +.demo-block.demo-skeleton { + .el-card { + margin-bottom: 16px; + } + + .card-header { + display: flex; + justify-content: space-between; + align-items: center; + } + + .time { + font-size: 13px; + color: #999; + } + + .bottom { + margin-top: 13px; + line-height: 12px; + } + + .button { + padding: 0; + min-height: auto; + } + + .image { + &.multi-content { + width: 400px; + height: 267px; + } + + width: 100%; + display: block; + } + + .clearfix:before, + .clearfix:after { + display: table; + content: ''; + } + + .clearfix:after { + clear: both; + } +} diff --git a/examples/docs/en-US/skeleton.md b/examples/docs/en-US/skeleton.md new file mode 100644 index 000000000..edf77d092 --- /dev/null +++ b/examples/docs/en-US/skeleton.md @@ -0,0 +1,315 @@ +## Skeleton + +When loading data, and you need a rich experience for visual and interactions for your end users, you can choose `skeleton`. + +### Basic usage + +The basic skeleton. + +:::demo + +```html + +``` + +::: + +### Configurable Rows + +You can configure the row numbers yourself, we are rendering a title row with 33% width of the others. + +:::demo + +```html + +``` + +::: + +### Animation +We have provided a switch flag indicating whether showing the loading animation, called `animated` when this is true, all children of `el-skeleton` will show animation + +:::demo + +```html + +``` + +::: + +### Customized Template +ElementPlus only provides the most common template, sometimes that could be a problem, so you have a slot named `template` to do that work. + +Also we have provided different types skeleton unit that you can choose, for more detailed info, please scroll down to the bottom of this page to see the API description. Also, when building your own customized skeleton structure, you should be structuring them as closer to the real DOM as possible, which avoiding the DOM bouncing caused by the height difference. + +:::demo + +```html + +``` + +::: + +### Loading state + +When `Loading` ends, we always need to show the real UI with data to our end users. with the attribtue `loading` we can control whether showing the DOM. You can also use slot `default` to structure the real DOM element. + +:::demo + +```html + + + +``` + +::: + + +### Rendering a list of data + +Most of the time, skeleton is used as indicators of rendering a list of data which haven't been fetched from server yet, then we need to create a list of skeleton out of no where to make it look like it is loading, with `count` attribute, you can control how many these templates you need to render to the browser. + + +:::tip +We do not recommend rendering lots of fake UI to the browser, it will still cause the performance issue, it also costs longer to destroy the skeleton. Keep `count` as small as it can be to make better user experience. +::: + +:::demo + +```html + + + +``` + +::: + +### Avoiding rendering bouncing. +Sometimes API responds very quickly, when that happens, the skeleton just gets rendered to the DOM then it needs to switch back to real DOM, that causes the sudden flashy. To avoid such thing, you can use the `throttle` attribute. + + +:::demo + +```html + + + +``` +::: + +### Skeleton Attributes + +| Attribute | Description | Type | Acceptable Value | Default | +| ------- | ---------------- | ------- | ------------ | ------ | +| animated | whether showing the animation | boolean | true / false | false | +| count | how many fake items to render to the DOM | number | integer | 1 | +| loading | whether showing the skeleton | boolean | true / false | true | +| rows | numbers of the row, only useful when no template slot were given | number | integer | 4 | +| throttle | Rendering delay in millseconds | number | integer | 0 | + + +### Skeleton Item Attributes +| Attribute | Description | Type | Acceptable Value | Default | +| ------- | ---------------- | ------- | ------------ | ------ | +| variant | The current rendering skeleton type | Enum(string) | p / text / h1 / h3 / text / caption / button / image / circle / rect | text | + + +### Skeleton Slots + +| Name | Description | +| ---- | ----------- | +| default | Real rendering DOM | +| template | Custom rendering skeleton template | diff --git a/examples/docs/es/skeleton.md b/examples/docs/es/skeleton.md new file mode 100644 index 000000000..edf77d092 --- /dev/null +++ b/examples/docs/es/skeleton.md @@ -0,0 +1,315 @@ +## Skeleton + +When loading data, and you need a rich experience for visual and interactions for your end users, you can choose `skeleton`. + +### Basic usage + +The basic skeleton. + +:::demo + +```html + +``` + +::: + +### Configurable Rows + +You can configure the row numbers yourself, we are rendering a title row with 33% width of the others. + +:::demo + +```html + +``` + +::: + +### Animation +We have provided a switch flag indicating whether showing the loading animation, called `animated` when this is true, all children of `el-skeleton` will show animation + +:::demo + +```html + +``` + +::: + +### Customized Template +ElementPlus only provides the most common template, sometimes that could be a problem, so you have a slot named `template` to do that work. + +Also we have provided different types skeleton unit that you can choose, for more detailed info, please scroll down to the bottom of this page to see the API description. Also, when building your own customized skeleton structure, you should be structuring them as closer to the real DOM as possible, which avoiding the DOM bouncing caused by the height difference. + +:::demo + +```html + +``` + +::: + +### Loading state + +When `Loading` ends, we always need to show the real UI with data to our end users. with the attribtue `loading` we can control whether showing the DOM. You can also use slot `default` to structure the real DOM element. + +:::demo + +```html + + + +``` + +::: + + +### Rendering a list of data + +Most of the time, skeleton is used as indicators of rendering a list of data which haven't been fetched from server yet, then we need to create a list of skeleton out of no where to make it look like it is loading, with `count` attribute, you can control how many these templates you need to render to the browser. + + +:::tip +We do not recommend rendering lots of fake UI to the browser, it will still cause the performance issue, it also costs longer to destroy the skeleton. Keep `count` as small as it can be to make better user experience. +::: + +:::demo + +```html + + + +``` + +::: + +### Avoiding rendering bouncing. +Sometimes API responds very quickly, when that happens, the skeleton just gets rendered to the DOM then it needs to switch back to real DOM, that causes the sudden flashy. To avoid such thing, you can use the `throttle` attribute. + + +:::demo + +```html + + + +``` +::: + +### Skeleton Attributes + +| Attribute | Description | Type | Acceptable Value | Default | +| ------- | ---------------- | ------- | ------------ | ------ | +| animated | whether showing the animation | boolean | true / false | false | +| count | how many fake items to render to the DOM | number | integer | 1 | +| loading | whether showing the skeleton | boolean | true / false | true | +| rows | numbers of the row, only useful when no template slot were given | number | integer | 4 | +| throttle | Rendering delay in millseconds | number | integer | 0 | + + +### Skeleton Item Attributes +| Attribute | Description | Type | Acceptable Value | Default | +| ------- | ---------------- | ------- | ------------ | ------ | +| variant | The current rendering skeleton type | Enum(string) | p / text / h1 / h3 / text / caption / button / image / circle / rect | text | + + +### Skeleton Slots + +| Name | Description | +| ---- | ----------- | +| default | Real rendering DOM | +| template | Custom rendering skeleton template | diff --git a/examples/docs/fr-FR/skeleton.md b/examples/docs/fr-FR/skeleton.md new file mode 100644 index 000000000..edf77d092 --- /dev/null +++ b/examples/docs/fr-FR/skeleton.md @@ -0,0 +1,315 @@ +## Skeleton + +When loading data, and you need a rich experience for visual and interactions for your end users, you can choose `skeleton`. + +### Basic usage + +The basic skeleton. + +:::demo + +```html + +``` + +::: + +### Configurable Rows + +You can configure the row numbers yourself, we are rendering a title row with 33% width of the others. + +:::demo + +```html + +``` + +::: + +### Animation +We have provided a switch flag indicating whether showing the loading animation, called `animated` when this is true, all children of `el-skeleton` will show animation + +:::demo + +```html + +``` + +::: + +### Customized Template +ElementPlus only provides the most common template, sometimes that could be a problem, so you have a slot named `template` to do that work. + +Also we have provided different types skeleton unit that you can choose, for more detailed info, please scroll down to the bottom of this page to see the API description. Also, when building your own customized skeleton structure, you should be structuring them as closer to the real DOM as possible, which avoiding the DOM bouncing caused by the height difference. + +:::demo + +```html + +``` + +::: + +### Loading state + +When `Loading` ends, we always need to show the real UI with data to our end users. with the attribtue `loading` we can control whether showing the DOM. You can also use slot `default` to structure the real DOM element. + +:::demo + +```html + + + +``` + +::: + + +### Rendering a list of data + +Most of the time, skeleton is used as indicators of rendering a list of data which haven't been fetched from server yet, then we need to create a list of skeleton out of no where to make it look like it is loading, with `count` attribute, you can control how many these templates you need to render to the browser. + + +:::tip +We do not recommend rendering lots of fake UI to the browser, it will still cause the performance issue, it also costs longer to destroy the skeleton. Keep `count` as small as it can be to make better user experience. +::: + +:::demo + +```html + + + +``` + +::: + +### Avoiding rendering bouncing. +Sometimes API responds very quickly, when that happens, the skeleton just gets rendered to the DOM then it needs to switch back to real DOM, that causes the sudden flashy. To avoid such thing, you can use the `throttle` attribute. + + +:::demo + +```html + + + +``` +::: + +### Skeleton Attributes + +| Attribute | Description | Type | Acceptable Value | Default | +| ------- | ---------------- | ------- | ------------ | ------ | +| animated | whether showing the animation | boolean | true / false | false | +| count | how many fake items to render to the DOM | number | integer | 1 | +| loading | whether showing the skeleton | boolean | true / false | true | +| rows | numbers of the row, only useful when no template slot were given | number | integer | 4 | +| throttle | Rendering delay in millseconds | number | integer | 0 | + + +### Skeleton Item Attributes +| Attribute | Description | Type | Acceptable Value | Default | +| ------- | ---------------- | ------- | ------------ | ------ | +| variant | The current rendering skeleton type | Enum(string) | p / text / h1 / h3 / text / caption / button / image / circle / rect | text | + + +### Skeleton Slots + +| Name | Description | +| ---- | ----------- | +| default | Real rendering DOM | +| template | Custom rendering skeleton template | diff --git a/examples/docs/zh-CN/skeleton.md b/examples/docs/zh-CN/skeleton.md new file mode 100644 index 000000000..a65831408 --- /dev/null +++ b/examples/docs/zh-CN/skeleton.md @@ -0,0 +1,316 @@ +## Skeleton 骨架屏 + +在需要等待加载内容的位置设置一个骨架屏,某些场景下比 Loading 的视觉效果更好。 + +### 基础用法 + +基础的骨架效果。 + +:::demo + +```html + +``` + +::: + +### 更多参数 + +可以配置骨架屏段落数量,以便更接近真实渲染效果。首行会被渲染一个长度 33% 的段首。 + +:::demo + +```html + +``` + +::: + +### 动画效果 + +显示动画效果。 + +:::demo + +```html + +``` + +::: + +### 自定义样式 + +ElementPlus 提供的排版模式有时候并不满足要求,当您想要用自己定义的模板时,可以通过一个具名 Slot 来自己设定模板。 + +我们提供了不同的模板单元可供使用,具体可选值请看 API 详细描述。 建议在描述模板的时候,尽量靠近真实的 DOM 结构,这样可以避免 DOM 高度差距引起的抖动。 +:::demo + +```html + +``` + +::: + +### Loading 状态 + +当 Loading 结束之后,我们往往需要显示真实的 UI,可以通过 `loading` 的值来控制是否显示真实的 DOM。然后通过 +具名 Slot 来设置当 loading 结束之后需要展示的 UI。 + +:::demo + +```html + + + +``` + +::: + +### 渲染多条数据 + +大多时候, 骨架屏都被用来渲染列表, 当我们需要在从服务器获取数据的时候来渲染一个假的 UI。利用 `count` 这个属性就能控制渲染多少条假的数据在页面上 + +:::tip +请注意, 请尽可能的将 `count` 的大小保持在最小状态, 即使是假的 UI, DOM 元素多了之后, 照样会引起性能问题, 并且在骨架屏销毁时所消耗的时间也会更长(相对的)。 +::: + +:::demo + + +```html + + + +``` + +::: + +### 防止渲染抖动 + +有的时候,API 的请求回来的特别快,往往骨架占位刚刚被渲染,真实的数据就已经回来了,用户的界面会突然一闪,此时为了避免这种情况,就需要通过 `throttle` 属性来避免这个问题。 + +:::demo + +```html + + + +``` + +::: + +### Skeleton Attributes + +| 参数 | 说明 | 类型 | 可选值 | 默认值 | +| -------- | ------------------------------------------- | ------- | ------------ | ------ | +| animated | 是否使用动画 | boolean | true / false | false | +| count | 渲染多少个 template, 建议使用尽可能小的数字 | number | integer | 1 | +| loading | 是否显示 skeleton 骨架屏 | boolean | true / false | true | +| rows | 骨架屏段落数量 | number | 正整数 | 4 | +| throttle | 延迟占位 DOM 渲染的时间, 单位是毫秒 | number | 正整数 | 0 | + +### Skeleton Item Attributes + +| 参数 | 说明 | 类型 | 可选值 | 默认值 | +| ------- | ------------------------ | ------------ | -------------------------------------------------------------------- | ------ | +| variant | 当前显示的占位元素的样式 | Enum(string) | p / text / h1 / h3 / text / caption / button / image / circle / rect | text | + +### Skeleton Slots + +| name | description | +| -------- | -------------------- | +| default | 用来展示真实 UI | +| template | 用来展示自定义占位符 | diff --git a/examples/nav.config.json b/examples/nav.config.json index f16534423..39f202eee 100644 --- a/examples/nav.config.json +++ b/examples/nav.config.json @@ -176,6 +176,10 @@ { "path": "/avatar", "title": "Avatar 头像" + }, + { + "path": "/skeleton", + "title": "Skeleton 骨架屏" } ] }, @@ -470,6 +474,10 @@ { "path": "/badge", "title": "Badge" + }, + { + "path": "/skeleton", + "title": "Skeleton" } ] }, @@ -768,6 +776,10 @@ { "path": "/badge", "title": "Badge" + }, + { + "path": "/skeleton", + "title": "Skeleton" } ] }, @@ -1066,6 +1078,10 @@ { "path": "/badge", "title": "Badge" + }, + { + "path": "/skeleton", + "title": "Skeleton" } ] }, diff --git a/packages/skeleton-item/index.js b/packages/skeleton-item/index.js new file mode 100644 index 000000000..e921e1c71 --- /dev/null +++ b/packages/skeleton-item/index.js @@ -0,0 +1,8 @@ +import SkeletonItem from '../skeleton/src/item'; + +/* istanbul ignore next */ +SkeletonItem.install = function(Vue) { + Vue.component(SkeletonItem.name, SkeletonItem); +}; + +export default SkeletonItem; diff --git a/packages/skeleton/index.js b/packages/skeleton/index.js new file mode 100644 index 000000000..9487e2149 --- /dev/null +++ b/packages/skeleton/index.js @@ -0,0 +1,8 @@ +import Skeleton from './src/index.vue'; + +/* istanbul ignore next */ +Skeleton.install = function(Vue) { + Vue.component(Skeleton.name, Skeleton); +}; + +export default Skeleton; diff --git a/packages/skeleton/src/img-placeholder.vue b/packages/skeleton/src/img-placeholder.vue new file mode 100644 index 000000000..ef88a3b30 --- /dev/null +++ b/packages/skeleton/src/img-placeholder.vue @@ -0,0 +1,16 @@ + + + diff --git a/packages/skeleton/src/index.vue b/packages/skeleton/src/index.vue new file mode 100644 index 000000000..150c200e6 --- /dev/null +++ b/packages/skeleton/src/index.vue @@ -0,0 +1,76 @@ + + diff --git a/packages/skeleton/src/item.vue b/packages/skeleton/src/item.vue new file mode 100644 index 000000000..cf4280c37 --- /dev/null +++ b/packages/skeleton/src/item.vue @@ -0,0 +1,22 @@ + + + \ No newline at end of file diff --git a/packages/theme-chalk/src/common/var.scss b/packages/theme-chalk/src/common/var.scss index 551dbe667..6db3e1314 100644 --- a/packages/theme-chalk/src/common/var.scss +++ b/packages/theme-chalk/src/common/var.scss @@ -964,6 +964,16 @@ $--avatar-medium-size: 36px !default; /// size|1|Avatar Size|3 $--avatar-small-size: 28px !default; + +/* Skeleton +--------------------------*/ +$--skeleton-color: #f2f2f2 !default; +$--skeleton-to-color: #e6e6e6 !default; + +/* Svg +--------------- */ +$--svg-monochrome-grey: #DCDDE0 !default; + /* Break-point --------------------------*/ $--sm: 768px !default; diff --git a/packages/theme-chalk/src/index.scss b/packages/theme-chalk/src/index.scss index 3572c9962..b3ca6d942 100644 --- a/packages/theme-chalk/src/index.scss +++ b/packages/theme-chalk/src/index.scss @@ -78,3 +78,5 @@ @import "./avatar.scss"; @import "./drawer.scss"; @import "./popconfirm.scss"; +@import "./skeleton.scss"; +@import "./skeleton-item.scss"; diff --git a/packages/theme-chalk/src/skeleton-item.scss b/packages/theme-chalk/src/skeleton-item.scss new file mode 100644 index 000000000..687c3c4ea --- /dev/null +++ b/packages/theme-chalk/src/skeleton-item.scss @@ -0,0 +1,84 @@ +@import 'mixins/mixins'; +@import 'common/var'; + +@mixin circle-size($size) { + width: $size; + height: $size; + line-height: $size; +} + +@include b(skeleton) { + @include e(item) { + background: $--skeleton-color; + display: inline-block; + height: 16px; + border-radius: $--border-radius-base; + width: 100%; + } + + @include e(circle) { + border-radius: 50%; + @include circle-size($--avatar-medium-size); + + @include m(lg) { + @include circle-size($--avatar-large-size); + } + + @include m(md) { + @include circle-size($--avatar-small-size); + } + } + + @include e(button) { + height: 40px; + width: 64px; + border-radius: 4px; + } + + @include e(p) { + width: 100%; + @include when(last) { + width: 61%; + } + + @include when(first) { + width: 33%; + } + } + + @include e(text) { + width: 100%; + height: $--font-size-small; + } + + @include e(caption) { + height: $--font-size-extra-small; + } + + @include e(h1) { + height: $--font-size-extra-large; + } + + @include e(h3) { + height: $--font-size-large; + } + + @include e(h5) { + height: $--font-size-medium; + } + + @include e(image) { + width: unset; + display: flex; + align-items: center; + justify-content: center; + border-radius: 0; + + svg { + fill: $--svg-monochrome-grey; + width: 22%; + height: 22%; + } + } + +} diff --git a/packages/theme-chalk/src/skeleton.scss b/packages/theme-chalk/src/skeleton.scss new file mode 100644 index 000000000..5bbfa08f3 --- /dev/null +++ b/packages/theme-chalk/src/skeleton.scss @@ -0,0 +1,40 @@ +@import 'mixins/mixins'; +@import 'common/var'; +@import "./skeleton-item.scss"; + +@mixin skeleton-color() { + background: linear-gradient( + 90deg, + $--skeleton-color 25%, + $--skeleton-to-color 37%, + $--skeleton-color 63% + ); + background-size: 400% 100%; + animation: #{$namespace}-skeleton-loading 1.4s ease infinite; +} + +@keyframes #{$namespace}-skeleton-loading { + 0% { + background-position: 100% 50%; + } + 100% { + background-position: 0 50%; + } +} + +@include b(skeleton) { + width: 100%; + @each $unit in (first-line, paragraph) { + @include e($unit) { + height: 16px; + margin-top: 16px; + background: $--skeleton-color; + } + } + + @include when(animated) { + .#{$namespace}-skeleton__item { + @include skeleton-color(); + } + } +} diff --git a/src/index.js b/src/index.js index 47f43aeb3..0dbd3a5b4 100644 --- a/src/index.js +++ b/src/index.js @@ -82,6 +82,8 @@ import CascaderPanel from '../packages/cascader-panel/index.js'; import Avatar from '../packages/avatar/index.js'; import Drawer from '../packages/drawer/index.js'; import Popconfirm from '../packages/popconfirm/index.js'; +import Skeleton from '../packages/skeleton/index.js'; +import SkeletonItem from '../packages/skeleton-item/index.js'; import locale from 'element-ui/src/locale'; import CollapseTransition from 'element-ui/src/transitions/collapse-transition'; @@ -163,6 +165,8 @@ const components = [ Avatar, Drawer, Popconfirm, + Skeleton, + SkeletonItem, CollapseTransition ]; @@ -284,5 +288,7 @@ export default { CascaderPanel, Avatar, Drawer, - Popconfirm + Popconfirm, + Skeleton, + SkeletonItem }; diff --git a/test/unit/specs/skeleton.spec.js b/test/unit/specs/skeleton.spec.js new file mode 100644 index 000000000..14ecd226d --- /dev/null +++ b/test/unit/specs/skeleton.spec.js @@ -0,0 +1,106 @@ +import Skeleton from 'packages/skeleton'; +import { createTest, destroyVM, createVue, waitImmediate, wait} from '../util'; + +const content = 'content'; + +describe('Skeleton.vue', () => { + let vm; + + afterEach(() => { + destroyVM(vm); + }); + + it('render test', () => { + vm = createTest(Skeleton, true); + const el = vm.$el; + expect(el.querySelectorAll('.el-skeleton__p').length).to.equal(4); + expect(Array.from(el.children[0].classList)).to.contain('el-skeleton'); + + }); + + it('should render with animation', () => { + vm = createTest(Skeleton, { + animated: true + }, true); + + const el = vm.$el; + + expect(Array.from(el.children[0].classList)).to.contain('is-animated'); + }); + + it('should render x times', async() => { + vm = createVue( + { + template: ` + + `, + data() { + return { + count: 1 + }; + } + }, + true + ); + + const wrapper = vm.$el; + expect(wrapper.querySelectorAll('.el-skeleton__p').length).to.equal(4); + + vm.count = 2; + + await waitImmediate(); + expect(wrapper.querySelectorAll('.el-skeleton__p').length).to.equal(8); + }); + + it('should render x rows', () => { + vm = createTest(Skeleton, { + rows: 5 + }, true); + + expect(vm.$el.querySelectorAll('.el-skeleton__p').length).to.equal(5); + }); + + it('should render default slots', () => { + vm = createVue( + { + template: ` + {{content}} + `, + data() { + return { + loading: false, + content + }; + } + }, true); + expect(vm.$el.textContent).to.be.equal(content); + }); + + it('should render template slots', () => { + vm = createVue( + { + template: ` + + `, + data() { + return { + loading: true, + content + }; + } + }, true); + expect(vm.$el.querySelector('.el-skeleton').textContent).to.be.equal(content); + }); + + it('should throttle rendering', async() => { + vm = createTest(Skeleton, { + throttle: 500 + }, true); + + expect((vm).uiLoading).to.be.equal(false); + await wait(600); + + expect(vm.uiLoading).to.be.equal(true); + }); + +}); diff --git a/types/element-ui.d.ts b/types/element-ui.d.ts index ea819dbb7..5db59b2f2 100644 --- a/types/element-ui.d.ts +++ b/types/element-ui.d.ts @@ -80,6 +80,8 @@ import { ElPageHeader } from './page-header' import { ElAvatar } from './avatar' import { ElDrawer } from './drawer' import { ElPopconfirm } from './popconfirm' +import { ElSkeleton } from './skeleton' +import { ElSkeletonItem } from './skeleton-item' export interface InstallationOptions { locale: any, @@ -344,3 +346,9 @@ export class Drawer extends ElDrawer {} /** Popconfirm Component */ export class Popconfirm extends ElPopconfirm {} + +/** Skeleton Component */ +export class Skeleton extends ElSkeleton {} + +/** Skeleton Item Component */ +export class SkeletonItem extends ElSkeletonItem {} diff --git a/types/skeleton-item.d.ts b/types/skeleton-item.d.ts new file mode 100644 index 000000000..68db50c27 --- /dev/null +++ b/types/skeleton-item.d.ts @@ -0,0 +1,7 @@ +import { ElementUIComponent } from './component' + +/** Skeleton Item Component */ +export declare class ElSkeletonItem extends ElementUIComponent { + /** The current rendering skeleton type; default: text */ + variant: 'p' | 'text' | 'h1' | 'h3' | 'text' | 'caption' | 'button' | 'image' | 'circle' | 'rect' +} diff --git a/types/skeleton.d.ts b/types/skeleton.d.ts new file mode 100644 index 000000000..65e4a3e9b --- /dev/null +++ b/types/skeleton.d.ts @@ -0,0 +1,33 @@ +import { ElementUIComponent } from './component' + + +interface ElSkeletonSlots { + /* default slot: Real rendering DOM */ + default: VNode[]; + + /* template slot: Custom rendering skeleton template */ + template: VNode[]; + + [key: string]: VNode[] +} + +/** When loading data, and you need a rich experience for visual and interactions for your end users */ +export declare class ElSkeleton extends ElementUIComponent { + /** whether showing the animation; default: false */ + animated: boolean + + /** how many fake items to render to the DOM; default: 1 */ + count: number + + /** whether showing the skeleton; default true */ + loading: boolean + + /** numbers of the row, only useful when no template slot were given; default: 4 */ + rows: boolean + + /** Rendering delay in millseconds; default: 0 */ + throttle: number + + $slots: ElSkeletonSlots + +}