From 4f78411bb8b3d87220c4e73ef9be32a1eef5da18 Mon Sep 17 00:00:00 2001 From: gimjin Date: Fri, 10 Mar 2023 15:30:56 +0800 Subject: [PATCH] feat(components): add el-text component (#11653) * test(components): [text] el-text unit test * docs(components): [text] el-text website documentation * feat(components): [text] el-text implementation * fix(components): [text] prop 'as' rename 'tag' * docs(components): [text] rename slot default, optimize document * test(components): [text] render text & class change the execution order * fix(components): [text] use template and render function together --- .../crowdin/en-US/pages/component.json | 4 ++ docs/en-US/component/text.md | 65 +++++++++++++++++++ docs/examples/text/basic.vue | 8 +++ docs/examples/text/mixed.vue | 26 ++++++++ docs/examples/text/override.vue | 19 ++++++ docs/examples/text/sizes.vue | 5 ++ docs/examples/text/truncated.vue | 8 +++ packages/components/index.ts | 1 + .../components/text/__tests__/text.test.tsx | 47 ++++++++++++++ packages/components/text/index.ts | 8 +++ packages/components/text/src/text.ts | 39 +++++++++++ packages/components/text/src/text.vue | 28 ++++++++ packages/components/text/style/css.ts | 2 + packages/components/text/style/index.ts | 2 + packages/element-plus/component.ts | 2 + packages/theme-chalk/src/common/var.scss | 21 ++++++ packages/theme-chalk/src/index.scss | 1 + packages/theme-chalk/src/text.scss | 45 +++++++++++++ 18 files changed, 331 insertions(+) create mode 100644 docs/en-US/component/text.md create mode 100644 docs/examples/text/basic.vue create mode 100644 docs/examples/text/mixed.vue create mode 100644 docs/examples/text/override.vue create mode 100644 docs/examples/text/sizes.vue create mode 100644 docs/examples/text/truncated.vue create mode 100644 packages/components/text/__tests__/text.test.tsx create mode 100644 packages/components/text/index.ts create mode 100644 packages/components/text/src/text.ts create mode 100644 packages/components/text/src/text.vue create mode 100644 packages/components/text/style/css.ts create mode 100644 packages/components/text/style/index.ts create mode 100644 packages/theme-chalk/src/text.scss diff --git a/docs/.vitepress/crowdin/en-US/pages/component.json b/docs/.vitepress/crowdin/en-US/pages/component.json index 0edfd00130..441f8467b9 100644 --- a/docs/.vitepress/crowdin/en-US/pages/component.json +++ b/docs/.vitepress/crowdin/en-US/pages/component.json @@ -30,6 +30,10 @@ "link": "/link", "text": "Link" }, + { + "link": "/text", + "text": "Text" + }, { "link": "/scrollbar", "text": "Scrollbar" diff --git a/docs/en-US/component/text.md b/docs/en-US/component/text.md new file mode 100644 index 0000000000..64a388a0b3 --- /dev/null +++ b/docs/en-US/component/text.md @@ -0,0 +1,65 @@ +--- +title: Text +lang: en-US +--- + +# Text + +Used for text. + +## Basic + +:::demo Use the `type` attribute to define Text's type. + +text/basic + +::: + +## Sizes + +:::demo Use attribute `size` to set additional sizes with `large`, `default` or `small`. + +text/sizes + +::: + +## Ellipsis + +:::demo Pass the `truncated` prop to render an ellipsis when the text exceeds the width of the viewport or max-width set. + +text/truncated + +::: + +## Override + +:::demo Use attribute `tag` to override element + +text/override + +::: + +## Mixed + +:::demo Text mixed component + +text/mixed + +::: + +## API + +### Attributes + +| Name | Description | Type | Default | +| --------- | ------------------ | ------------------------------------------------------------------ | ------- | +| type | text type | ^[enum]`'primary' \| 'success' \| 'warning' \| 'danger' \| 'info'` | — | +| size | text size | ^[enum]`'large' \| 'default' \| 'small'` | default | +| truncated | render ellipsis | ^[boolean] | false | +| tag | custom element tag | ^[string] | span | + +### Slots + +| Name | Description | +| ------- | --------------- | +| default | default content | diff --git a/docs/examples/text/basic.vue b/docs/examples/text/basic.vue new file mode 100644 index 0000000000..0871655c3b --- /dev/null +++ b/docs/examples/text/basic.vue @@ -0,0 +1,8 @@ + diff --git a/docs/examples/text/mixed.vue b/docs/examples/text/mixed.vue new file mode 100644 index 0000000000..ca811d5edb --- /dev/null +++ b/docs/examples/text/mixed.vue @@ -0,0 +1,26 @@ + + + diff --git a/docs/examples/text/override.vue b/docs/examples/text/override.vue new file mode 100644 index 0000000000..03c3a3583f --- /dev/null +++ b/docs/examples/text/override.vue @@ -0,0 +1,19 @@ + diff --git a/docs/examples/text/sizes.vue b/docs/examples/text/sizes.vue new file mode 100644 index 0000000000..d99b902473 --- /dev/null +++ b/docs/examples/text/sizes.vue @@ -0,0 +1,5 @@ + diff --git a/docs/examples/text/truncated.vue b/docs/examples/text/truncated.vue new file mode 100644 index 0000000000..68e939c14a --- /dev/null +++ b/docs/examples/text/truncated.vue @@ -0,0 +1,8 @@ + diff --git a/packages/components/index.ts b/packages/components/index.ts index a3bf610159..03b816605b 100644 --- a/packages/components/index.ts +++ b/packages/components/index.ts @@ -58,6 +58,7 @@ export * from './table' export * from './table-v2' export * from './tabs' export * from './tag' +export * from './text' export * from './time-picker' export * from './time-select' export * from './timeline' diff --git a/packages/components/text/__tests__/text.test.tsx b/packages/components/text/__tests__/text.test.tsx new file mode 100644 index 0000000000..19d5d61879 --- /dev/null +++ b/packages/components/text/__tests__/text.test.tsx @@ -0,0 +1,47 @@ +import { mount } from '@vue/test-utils' +import { describe, expect, test } from 'vitest' +import Text from '../src/text.vue' + +const AXIOM = 'Rem is the best girl' + +describe('Text.vue', () => { + test('render text & class', () => { + const wrapper = mount(() => ( + AXIOM, + }} + /> + )) + const vm = wrapper.vm + + expect(vm.$el.classList.contains('el-text')).toEqual(true) + expect(wrapper.text()).toEqual(AXIOM) + }) + + test('type', () => { + const wrapper = mount(() => ) + const vm = wrapper.vm + expect(vm.$el.classList.contains('el-text--success')).toEqual(true) + }) + + test('size', () => { + const wrapper = mount(() => ) + const vm = wrapper.vm + expect(vm.$el.className.includes('el-text--large')).toEqual(true) + expect(vm.$el.className.includes('el-text--default')).toEqual(false) + expect(vm.$el.className.includes('el-text--small')).toEqual(false) + }) + + test('truncated', () => { + const wrapper = mount(() => ) + const vm = wrapper.vm + expect(vm.$el.className.includes('is-truncated')).toEqual(true) + }) + + test('tag', () => { + const wrapper = mount(() => ) + const vm = wrapper.vm + expect(vm.$el.tagName).toEqual('DEL') + }) +}) diff --git a/packages/components/text/index.ts b/packages/components/text/index.ts new file mode 100644 index 0000000000..e155582eb2 --- /dev/null +++ b/packages/components/text/index.ts @@ -0,0 +1,8 @@ +import { withInstall } from '@element-plus/utils' + +import Text from './src/text.vue' + +export const ElText = withInstall(Text) +export default ElText + +export * from './src/text' diff --git a/packages/components/text/src/text.ts b/packages/components/text/src/text.ts new file mode 100644 index 0000000000..ce86e80818 --- /dev/null +++ b/packages/components/text/src/text.ts @@ -0,0 +1,39 @@ +import { buildProps } from '@element-plus/utils' +import { componentSizes } from '@element-plus/constants' +import type Text from './text.vue' + +import type { ExtractPropTypes } from 'vue' + +export const textProps = buildProps({ + /** + * @description text type + */ + type: { + type: String, + values: ['primary', 'success', 'info', 'warning', 'danger', ''], + default: '', + }, + /** + * @description text size + */ + size: { + type: String, + values: componentSizes, + default: '', + }, + /** + * @description render ellipsis + */ + truncated: { + type: Boolean, + }, + /** + * @description custom element tag + */ + tag: { + type: String, + default: 'span', + }, +} as const) +export type TextProps = ExtractPropTypes +export type TextInstance = InstanceType diff --git a/packages/components/text/src/text.vue b/packages/components/text/src/text.vue new file mode 100644 index 0000000000..fb9e06c7d5 --- /dev/null +++ b/packages/components/text/src/text.vue @@ -0,0 +1,28 @@ + + + diff --git a/packages/components/text/style/css.ts b/packages/components/text/style/css.ts new file mode 100644 index 0000000000..397dbe0f79 --- /dev/null +++ b/packages/components/text/style/css.ts @@ -0,0 +1,2 @@ +import '@element-plus/components/base/style/css' +import '@element-plus/theme-chalk/el-text.css' diff --git a/packages/components/text/style/index.ts b/packages/components/text/style/index.ts new file mode 100644 index 0000000000..acecb0fe4e --- /dev/null +++ b/packages/components/text/style/index.ts @@ -0,0 +1,2 @@ +import '@element-plus/components/base/style' +import '@element-plus/theme-chalk/src/text.scss' diff --git a/packages/element-plus/component.ts b/packages/element-plus/component.ts index 8bbf917afd..45b5921615 100644 --- a/packages/element-plus/component.ts +++ b/packages/element-plus/component.ts @@ -91,6 +91,7 @@ import { ElTable, ElTableColumn } from '@element-plus/components/table' import { ElAutoResizer, ElTableV2 } from '@element-plus/components/table-v2' import { ElTabPane, ElTabs } from '@element-plus/components/tabs' import { ElTag } from '@element-plus/components/tag' +import { ElText } from '@element-plus/components/text' import { ElTimePicker } from '@element-plus/components/time-picker' import { ElTimeSelect } from '@element-plus/components/time-select' import { ElTimeline, ElTimelineItem } from '@element-plus/components/timeline' @@ -191,6 +192,7 @@ export default [ ElTabs, ElTabPane, ElTag, + ElText, ElTimePicker, ElTimeSelect, ElTimeline, diff --git a/packages/theme-chalk/src/common/var.scss b/packages/theme-chalk/src/common/var.scss index 56a140f32b..832a751922 100644 --- a/packages/theme-chalk/src/common/var.scss +++ b/packages/theme-chalk/src/common/var.scss @@ -923,6 +923,27 @@ $tag-icon-size: map.merge( $tag-icon-size ); +// Text +// css3 var in packages/theme-chalk/src/text.scss +$text: () !default; +$text: map.merge( + ( + 'font-size': getCssVar('font-size', 'base'), + 'color': getCssVar('text-color', 'regular'), + ), + $text +); + +$text-font-size: () !default; +$text-font-size: map.merge( + ( + 'large': getCssVar('font-size', 'medium'), + 'default': getCssVar('font-size', 'base'), + 'small': getCssVar('font-size', 'extra-small'), + ), + $text-font-size +); + // Tree // css3 var in packages/theme-chalk/src/tree.scss $tree: () !default; diff --git a/packages/theme-chalk/src/index.scss b/packages/theme-chalk/src/index.scss index 6814fefd36..0aabebb73a 100644 --- a/packages/theme-chalk/src/index.scss +++ b/packages/theme-chalk/src/index.scss @@ -85,6 +85,7 @@ @use './table-v2.scss'; @use './tabs.scss'; @use './tag.scss'; +@use './text.scss'; @use './time-picker.scss'; @use './time-select.scss'; @use './timeline-item.scss'; diff --git a/packages/theme-chalk/src/text.scss b/packages/theme-chalk/src/text.scss new file mode 100644 index 0000000000..5009c72bef --- /dev/null +++ b/packages/theme-chalk/src/text.scss @@ -0,0 +1,45 @@ +@use 'sass:map'; + +@use 'mixins/mixins' as *; +@use 'mixins/var' as *; +@use 'common/var' as *; + +@include b(text) { + @include set-component-css-var('text', $text); +} + +@include b(text) { + align-self: center; + margin: 0; + padding: 0; + font-size: getCssVar('text', 'font-size'); + color: getCssVar('text', 'color'); + word-break: break-all; + + @include when(truncated) { + display: inline-block; + max-width: 100%; + text-overflow: ellipsis; + white-space: nowrap; + overflow: hidden; + } + + @each $size in (large, default, small) { + @include m($size) { + @include set-css-var-value( + ('text', 'font-size'), + map.get($text-font-size, $size) + ); + } + } + + @each $type in $types { + &.#{bem('text', '', $type)} { + @include css-var-from-global(('text', 'color'), ('color', $type)); + } + } + + &>.#{bem('icon')} { + vertical-align: -2px; + } +}