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
This commit is contained in:
gimjin 2023-03-10 15:30:56 +08:00 committed by GitHub
parent 6077e64021
commit 4f78411bb8
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
18 changed files with 331 additions and 0 deletions

View File

@ -30,6 +30,10 @@
"link": "/link",
"text": "Link"
},
{
"link": "/text",
"text": "Text"
},
{
"link": "/scrollbar",
"text": "Scrollbar"

View File

@ -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 |

View File

@ -0,0 +1,8 @@
<template>
<el-text class="mx-1">Default</el-text>
<el-text class="mx-1" type="primary">Primary</el-text>
<el-text class="mx-1" type="success">Success</el-text>
<el-text class="mx-1" type="info">Info</el-text>
<el-text class="mx-1" type="warning">Warning</el-text>
<el-text class="mx-1" type="danger">Danger</el-text>
</template>

View File

@ -0,0 +1,26 @@
<template>
<el-space direction="vertical">
<el-text>
<el-icon>
<ElementPlus />
</el-icon>
Element-Plus
</el-text>
<el-row>
<el-text>Rate</el-text>
<el-rate class="ml-1" />
</el-row>
<el-text>
This is text mixed icon
<el-icon>
<Bell />
</el-icon>
and component
<el-button>Button</el-button>
</el-text>
</el-space>
</template>
<script lang="ts" setup>
import { Bell, ElementPlus } from '@element-plus/icons-vue'
</script>

View File

@ -0,0 +1,19 @@
<template>
<el-space direction="vertical">
<el-text>span</el-text>
<el-text tag="p">This is a paragraph.</el-text>
<el-text tag="b">Bold</el-text>
<el-text tag="i">Italic</el-text>
<el-text>
This is
<el-text tag="sub" size="small">subscript</el-text>
</el-text>
<el-text>
This is
<el-text tag="sup" size="small">superscript</el-text>
</el-text>
<el-text tag="ins">Inserted</el-text>
<el-text tag="del">Deleted</el-text>
<el-text tag="mark">Marked</el-text>
</el-space>
</template>

View File

@ -0,0 +1,5 @@
<template>
<el-text class="mx-1" size="large">Large</el-text>
<el-text class="mx-1">Default</el-text>
<el-text class="mx-1" size="small">Small</el-text>
</template>

View File

@ -0,0 +1,8 @@
<template>
<el-text class="w-100px" truncated>Self element set width 100px</el-text>
<el-row>
<el-col :span="4">
<el-text truncated>Squeezed by parent element</el-text>
</el-col>
</el-row>
</template>

View File

@ -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'

View File

@ -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(() => (
<Text
v-slots={{
default: () => AXIOM,
}}
/>
))
const vm = wrapper.vm
expect(vm.$el.classList.contains('el-text')).toEqual(true)
expect(wrapper.text()).toEqual(AXIOM)
})
test('type', () => {
const wrapper = mount(() => <Text type="success" />)
const vm = wrapper.vm
expect(vm.$el.classList.contains('el-text--success')).toEqual(true)
})
test('size', () => {
const wrapper = mount(() => <Text size="large" />)
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(() => <Text truncated />)
const vm = wrapper.vm
expect(vm.$el.className.includes('is-truncated')).toEqual(true)
})
test('tag', () => {
const wrapper = mount(() => <Text tag="del" />)
const vm = wrapper.vm
expect(vm.$el.tagName).toEqual('DEL')
})
})

View File

@ -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'

View File

@ -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<typeof textProps>
export type TextInstance = InstanceType<typeof Text>

View File

@ -0,0 +1,28 @@
<template>
<component :is="tag" :class="textKls">
<slot />
</component>
</template>
<script lang="ts" setup>
import { computed } from 'vue'
import { useNamespace } from '@element-plus/hooks'
import { useFormSize } from '@element-plus/components/form'
import { textProps } from './text'
defineOptions({
name: 'ElText',
})
const props = defineProps(textProps)
const textSize = useFormSize()
const ns = useNamespace('text')
const textKls = computed(() => [
ns.b(),
ns.m(props.type),
ns.m(textSize.value),
ns.is('truncated', props.truncated),
])
</script>

View File

@ -0,0 +1,2 @@
import '@element-plus/components/base/style/css'
import '@element-plus/theme-chalk/el-text.css'

View File

@ -0,0 +1,2 @@
import '@element-plus/components/base/style'
import '@element-plus/theme-chalk/src/text.scss'

View File

@ -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,

View File

@ -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;

View File

@ -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';

View File

@ -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;
}
}