Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat(tag): support title api #3309

Merged
merged 5 commits into from
Sep 6, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
99 changes: 96 additions & 3 deletions src/tag/__tests__/vitest-tag.test.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -33,6 +33,56 @@ describe('Tag Component', () => {
expect(wrapper2.find('.t-tag__icon-close').exists()).toBeTruthy();
});

it('props.color is equal to #ff0000', () => {
const wrapper = mount({
render() {
return <Tag color={'#ff0000'}></Tag>;
},
});
const domWrapper = wrapper.findComponent(Tag);
expect(domWrapper.element.style.backgroundColor).toBe('rgb(255, 0, 0)');
});
it('props.color expect variant=dark', () => {
const wrapper = mount({
render() {
return <Tag color={'#ff0000'} variant={'dark'} theme={'primary'}></Tag>;
},
});
const domWrapper = wrapper.findComponent(Tag);
expect(domWrapper.element.style.backgroundColor).toBe('rgb(255, 0, 0)');
expect(domWrapper.element.style.color).toBe('white');
});
it('props.color expect variant=light', () => {
const wrapper = mount({
render() {
return <Tag color={'#ff0000'} variant={'light'}></Tag>;
},
});
const domWrapper = wrapper.findComponent(Tag);
expect(domWrapper.element.style.color).toBe('rgb(255, 0, 0)');
expect(domWrapper.element.style.backgroundColor).toBe('rgba(255, 0, 0, 0.1)');
});
it('props.color expect variant=outline', () => {
const wrapper = mount({
render() {
return <Tag color={'#ff0000'} variant={'outline'}></Tag>;
},
});
const domWrapper = wrapper.findComponent(Tag);
expect(domWrapper.element.style.borderColor).toBe('#ff0000');
expect(domWrapper.element.style.color).toBe('rgb(255, 0, 0)');
});
it('props.color expect variant=light-outline', () => {
const wrapper = mount({
render() {
return <Tag color={'#ff0000'} variant={'light-outline'}></Tag>;
},
});
const domWrapper = wrapper.findComponent(Tag);
expect(domWrapper.element.style.borderColor).toBe('#ff0000');
expect(domWrapper.element.style.color).toBe('rgb(255, 0, 0)');
});

it('props.content works fine', () => {
const wrapper = mount({
render() {
Expand Down Expand Up @@ -106,9 +156,19 @@ describe('Tag Component', () => {
return <Tag maxWidth={'150px'} content={'This is a long long long long long tag'}></Tag>;
},
});
const domWrapper1 = wrapper.find('.t-tag--text');
expect(domWrapper1.element.style.maxWidth).toBe('150px');
expect(domWrapper1.attributes('title')).toBe('This is a long long long long long tag');
const domWrapper = wrapper.find('.t-tag--text');
expect(domWrapper.attributes('title')).toBe('This is a long long long long long tag');
expect(domWrapper.element.style.maxWidth).toBe('150px');
});
it('props.maxWidth is equal to 150', () => {
const wrapper = mount({
render() {
return <Tag maxWidth={'150'} content={'This is a long long long long long tag'}></Tag>;
},
});
const domWrapper = wrapper.find('.t-tag--text');
expect(domWrapper.attributes('title')).toBe('This is a long long long long long tag');
expect(domWrapper.element.style.maxWidth).toBe('150px');
});

const shapeClassNameList = [{ 't-tag--square': false }, 't-tag--round', 't-tag--mark'];
Expand Down Expand Up @@ -156,6 +216,39 @@ describe('Tag Component', () => {
});
});

it('props.title is equal to This is a long tag', () => {
const wrapper = mount({
render() {
return (
<Tag title={'This is a long tag'} content={'This is a long long long long long tag'} maxWidth={'150px'}></Tag>
);
},
});
const domWrapper = wrapper.find('.t-tag--text');
expect(domWrapper.element.style.maxWidth).toBe('150px');
expect(domWrapper.attributes('title')).toBe('This is a long tag');
});
it('props.title is equal to ', () => {
const wrapper = mount({
render() {
return <Tag title={''} content={'This is a long long long long long tag'} maxWidth={'150px'}></Tag>;
},
});
const domWrapper = wrapper.find('.t-tag--text');
expect(domWrapper.element.style.maxWidth).toBe('150px');
expect(domWrapper.attributes('title')).toBeUndefined();
});
it('props.title is equal to undefined', () => {
const wrapper = mount({
render() {
return <Tag title={undefined} content={'This is a long long long long long tag'} maxWidth={'150px'}></Tag>;
},
});
const domWrapper = wrapper.find('.t-tag--text');
expect(domWrapper.element.style.maxWidth).toBe('150px');
expect(domWrapper.attributes('title')).toBeUndefined();
});

['dark', 'light', 'outline', 'light-outline'].forEach((item) => {
it(`props.variant is equal to ${item}`, () => {
const wrapper = mount({
Expand Down
10 changes: 9 additions & 1 deletion src/tag/props.ts
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,10 @@ export default {
/** 标签是否可关闭 */
closable: Boolean,
/** 自定义标签颜色 */
color: String,
color: {
type: String,
default: '',
},
/** 组件子元素 */
content: {
type: [String, Function] as PropType<TdTagProps['content']>,
Expand Down Expand Up @@ -58,6 +61,11 @@ export default {
return ['default', 'primary', 'warning', 'danger', 'success'].includes(val);
},
},
/** 标签标题,在标签hover时展示,默认为标签内容 */
title: {
type: String,
default: '',
},
/** 标签风格变体 */
variant: {
type: String as PropType<TdTagProps['variant']>,
Expand Down
6 changes: 5 additions & 1 deletion src/tag/tag.en-US.md
Original file line number Diff line number Diff line change
@@ -1,12 +1,13 @@
:: BASE_DOC ::

## API

### Tag Props

name | type | default | description | required
-- | -- | -- | -- | --
closable | Boolean | false | \- | N
color | String | '' | custom color | N
color | String | - | self-defined tag color | N
content | String / Slot / Function | - | Typescript:`string \| TNode`。[see more ts definition](https://github.com/Tencent/tdesign-vue/blob/develop/src/common.ts) | N
default | String / Slot / Function | - | Typescript:`string \| TNode`。[see more ts definition](https://github.com/Tencent/tdesign-vue/blob/develop/src/common.ts) | N
disabled | Boolean | false | \- | N
Expand All @@ -15,6 +16,7 @@ maxWidth | String / Number | - | \- | N
shape | String | square | options: square/round/mark | N
size | String | medium | options: small/medium/large。Typescript:`SizeEnum`。[see more ts definition](https://github.com/Tencent/tdesign-vue/blob/develop/src/common.ts) | N
theme | String | default | options: default/primary/warning/danger/success | N
title | String | - | title of tag | N
variant | String | dark | options: dark/light/outline/light-outline | N
onClick | Function | | Typescript:`(context: { e: MouseEvent }) => void`<br/> | N
onClose | Function | | Typescript:`(context: { e: MouseEvent }) => void`<br/> | N
Expand All @@ -26,6 +28,7 @@ name | params | description
click | `(context: { e: MouseEvent })` | \-
close | `(context: { e: MouseEvent })` | \-


### CheckTag Props

name | type | default | description | required
Expand All @@ -49,6 +52,7 @@ name | params | description
change | `(checked: boolean, context: CheckTagChangeContext)` | [see more ts definition](https://github.com/Tencent/tdesign-vue/tree/develop/src/tag/type.ts)。<br/>`interface CheckTagChangeContext { e: MouseEvent \| KeyboardEvent; value: string \| number }`<br/>
click | `(context: { e: MouseEvent })` | \-


### CheckTagGroup Props

name | type | default | description | required
Expand Down
12 changes: 8 additions & 4 deletions src/tag/tag.md
Original file line number Diff line number Diff line change
Expand Up @@ -5,12 +5,13 @@
{{ custom-color }}

## API

### Tag Props

名称 | 类型 | 默认值 | 说明 | 必传
名称 | 类型 | 默认值 | 描述 | 必传
-- | -- | -- | -- | --
closable | Boolean | false | 标签是否可关闭 | N
color | String | '' | 颜色 | N
color | String | - | 自定义标签颜色 | N
content | String / Slot / Function | - | 组件子元素。TS 类型:`string \| TNode`。[通用类型定义](https://github.com/Tencent/tdesign-vue/blob/develop/src/common.ts) | N
default | String / Slot / Function | - | 组件子元素,同 `content`。TS 类型:`string \| TNode`。[通用类型定义](https://github.com/Tencent/tdesign-vue/blob/develop/src/common.ts) | N
disabled | Boolean | false | 标签禁用态,失效标签不能触发事件。默认风格(theme=default)才有禁用态 | N
Expand All @@ -19,6 +20,7 @@ maxWidth | String / Number | - | 标签最大宽度,宽度超出后会出现
shape | String | square | 标签类型,有三种:方形、圆角方形、标记型。可选项:square/round/mark | N
size | String | medium | 标签尺寸。可选项:small/medium/large。TS 类型:`SizeEnum`。[通用类型定义](https://github.com/Tencent/tdesign-vue/blob/develop/src/common.ts) | N
theme | String | default | 组件风格,用于描述组件不同的应用场景。可选项:default/primary/warning/danger/success | N
title | String | - | 标签标题,在标签hover时展示,默认为标签内容 | N
variant | String | dark | 标签风格变体。可选项:dark/light/outline/light-outline | N
onClick | Function | | TS 类型:`(context: { e: MouseEvent }) => void`<br/>点击时触发 | N
onClose | Function | | TS 类型:`(context: { e: MouseEvent }) => void`<br/>如果关闭按钮存在,点击关闭按钮时触发 | N
Expand All @@ -30,9 +32,10 @@ onClose | Function | | TS 类型:`(context: { e: MouseEvent }) => void`<br/>
click | `(context: { e: MouseEvent })` | 点击时触发
close | `(context: { e: MouseEvent })` | 如果关闭按钮存在,点击关闭按钮时触发


### CheckTag Props

名称 | 类型 | 默认值 | 说明 | 必传
名称 | 类型 | 默认值 | 描述 | 必传
-- | -- | -- | -- | --
checked | Boolean | - | 标签选中的状态,默认风格(theme=default)才有选中态。支持语法糖 `v-model` | N
defaultChecked | Boolean | - | 标签选中的状态,默认风格(theme=default)才有选中态。非受控属性 | N
Expand All @@ -53,9 +56,10 @@ onClick | Function | | TS 类型:`(context: { e: MouseEvent }) => void`<br/>
change | `(checked: boolean, context: CheckTagChangeContext)` | 状态切换时触发。[详细类型定义](https://github.com/Tencent/tdesign-vue/tree/develop/src/tag/type.ts)。<br/>`interface CheckTagChangeContext { e: MouseEvent \| KeyboardEvent; value: string \| number }`<br/>
click | `(context: { e: MouseEvent })` | 点击标签时触发


### CheckTagGroup Props

名称 | 类型 | 默认值 | 说明 | 必传
名称 | 类型 | 默认值 | 描述 | 必传
-- | -- | -- | -- | --
checkedProps | Object | - | 透传标签选中态属性。TS 类型:`TdTagProps` | N
multiple | Boolean | false | 是否支持选中多个标签 | N
Expand Down
26 changes: 19 additions & 7 deletions src/tag/tag.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ import Vue from 'vue';
import { CloseIcon as TdCloseIcon } from 'tdesign-icons-vue';
import { ScopedSlotReturnValue } from 'vue/types/vnode';
import tinycolor from 'tinycolor2';
import isString from 'lodash/isString';
import props from './props';
import mixins from '../utils/mixins';
import getConfigReceiverMixins, { TagConfig, getGlobalIconMixins } from '../config-provider/config-receiver';
Expand Down Expand Up @@ -98,6 +99,22 @@ export default mixins(getConfigReceiverMixins<Vue, TagConfig>('tag'), getGlobalI
}
return style;
},
renderTitle(tagContent: string) {
if (!this.maxWidth) {
return undefined;
}

const vProps = (this.$vnode.componentOptions.propsData as TdTagProps) || {};
if (Reflect.has(vProps, 'title')) {
return vProps.title || undefined;
}

if (tagContent) {
return tagContent;
}

return undefined;
},
},

render() {
Expand All @@ -106,18 +123,13 @@ export default mixins(getConfigReceiverMixins<Vue, TagConfig>('tag'), getGlobalI
// 标签内容
const tagContent: TNodeReturnValue = renderContent(this, 'default', 'content');

const title = typeof tagContent === 'string' ? tagContent : '';
const titleAttribute = title && this.maxWidth ? { title } : undefined;
const title = this.renderTitle(isString(tagContent) ? tagContent : '');
// 图标
const icon = renderTNodeJSX(this, 'icon');
return (
<div class={this.tagClass} onClick={this.handleClick} style={this.tagStyle}>
{icon}
<span
class={this.maxWidth ? `${this.componentName}--text` : undefined}
style={this.textStyle}
attrs={titleAttribute}
>
<span class={this.maxWidth ? `${this.componentName}--text` : undefined} style={this.textStyle} title={title}>
{tagContent}
</span>
{!this.disabled ? closeIcon : undefined}
Expand Down
8 changes: 7 additions & 1 deletion src/tag/type.ts
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,8 @@ export interface TdTagProps {
*/
closable?: boolean;
/**
* 自定义颜色
* 自定义标签颜色
* @default ''
*/
color?: string;
/**
Expand Down Expand Up @@ -52,6 +53,11 @@ export interface TdTagProps {
* @default default
*/
theme?: 'default' | 'primary' | 'warning' | 'danger' | 'success';
/**
* 标签标题,在标签hover时展示,默认为标签内容
* @default ''
*/
title?: string;
/**
* 标签风格变体
* @default dark
Expand Down
2 changes: 1 addition & 1 deletion test/snap/__snapshots__/csr.test.js.snap
Original file line number Diff line number Diff line change
Expand Up @@ -107195,11 +107195,11 @@ exports[`csr snapshot test > csr test ./src/tag/_example/long-text.vue 1`] = `
<div>
<div
class="t-tag t-tag--default t-tag--dark t-tag--ellipsis"
title="默认超八个字超长文本标签超长省略文本标签"
>
<span
class="t-tag--text"
style="max-width: 150px;"
title="默认超八个字超长文本标签超长省略文本标签"
>
默认超八个字超长文本标签超长省略文本标签
</span>
Expand Down
2 changes: 1 addition & 1 deletion test/snap/__snapshots__/ssr.test.js.snap
Original file line number Diff line number Diff line change
Expand Up @@ -1119,7 +1119,7 @@ exports[`ssr snapshot test > renders ./src/tag/_example/disabled.vue correctly 1

exports[`ssr snapshot test > renders ./src/tag/_example/icon.vue correctly 1`] = `"<div data-server-rendered=\\"true\\" class=\\"t-space t-space-horizontal\\" style=\\"gap:16px;\\"><div class=\\"t-space-item\\"><div class=\\"t-tag t-tag--default t-tag--dark\\"><svg fill=\\"none\\" viewBox=\\"0 0 24 24\\" width=\\"1em\\" height=\\"1em\\" class=\\"t-icon t-icon-discount\\"><path fill=\\"currentColor\\" d=\\"M11.5 1.19l10.5.8.81 10.51-10.93 10.93L.56 12.13 11.5 1.18zm.76 2.06l-8.87 8.87 8.49 8.49 8.87-8.87-.61-7.88-7.88-.6zm3.86 4.63a1 1 0 10-1.41 1.41 1 1 0 001.41-1.41zm-2.83-1.42a3 3 0 114.24 4.25 3 3 0 01-4.24-4.25z\\"></path></svg><span>函数图标</span></div></div><div class=\\"t-space-item\\"><div class=\\"t-tag t-tag--default t-tag--dark\\"><svg fill=\\"none\\" viewBox=\\"0 0 24 24\\" width=\\"1em\\" height=\\"1em\\" class=\\"t-icon t-icon-discount\\"><path fill=\\"currentColor\\" d=\\"M11.5 1.19l10.5.8.81 10.51-10.93 10.93L.56 12.13 11.5 1.18zm.76 2.06l-8.87 8.87 8.49 8.49 8.87-8.87-.61-7.88-7.88-.6zm3.86 4.63a1 1 0 10-1.41 1.41 1 1 0 001.41-1.41zm-2.83-1.42a3 3 0 114.24 4.25 3 3 0 01-4.24-4.25z\\"></path></svg><span> 插槽图标 </span></div></div></div>"`;

exports[`ssr snapshot test > renders ./src/tag/_example/long-text.vue correctly 1`] = `"<div data-server-rendered=\\"true\\"><div title=\\"默认超八个字超长文本标签超长省略文本标签\\" class=\\"t-tag t-tag--default t-tag--dark t-tag--ellipsis\\"><span class=\\"t-tag--text\\" style=\\"max-width:150px;\\">默认超八个字超长文本标签超长省略文本标签</span></div></div>"`;
exports[`ssr snapshot test > renders ./src/tag/_example/long-text.vue correctly 1`] = `"<div data-server-rendered=\\"true\\"><div class=\\"t-tag t-tag--default t-tag--dark t-tag--ellipsis\\"><span title=\\"默认超八个字超长文本标签超长省略文本标签\\" class=\\"t-tag--text\\" style=\\"max-width:150px;\\">默认超八个字超长文本标签超长省略文本标签</span></div></div>"`;

exports[`ssr snapshot test > renders ./src/tag/_example/plain.vue correctly 1`] = `"<div data-server-rendered=\\"true\\" class=\\"t-space t-space-horizontal\\" style=\\"gap:16px;\\"><div class=\\"t-space-item\\"><div class=\\"t-tag t-tag--primary t-tag--outline\\"><span>标签一</span></div></div><div class=\\"t-space-item\\"><div class=\\"t-tag t-tag--success t-tag--outline\\"><span>标签二</span></div></div><div class=\\"t-space-item\\"><div class=\\"t-tag t-tag--default t-tag--outline\\"><span>标签三</span></div></div><div class=\\"t-space-item\\"><div class=\\"t-tag t-tag--warning t-tag--outline\\"><span>标签四</span></div></div><div class=\\"t-space-item\\"><div class=\\"t-tag t-tag--danger t-tag--outline\\"><span>标签五</span></div></div></div>"`;

Expand Down
Loading