Skip to content
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
29 changes: 24 additions & 5 deletions packages/components/typography/__tests__/typography.test.tsx
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
import React from 'react';
import { render, fireEvent, vi, mockDelay } from '@test/utils';
import { fireEvent, mockDelay, render, vi } from '@test/utils';
import { AngryIcon, SmileIcon } from 'tdesign-icons-react';
import { Typography } from '..';

Expand All @@ -17,23 +17,23 @@ describe('Typography 组件测试', () => {
mockGetCanvasContext.mockReturnValue({
font: vi.fn(),
measureText: vi.fn(),
});
} as unknown as CanvasRenderingContext2D);
mockGetCanvasToDataURL.mockReturnValue('test');
});

test('title 测试', async () => {
const { container } = render(<Title>{shortText}</Title>);
expect(container.firstChild.innerHTML).toBe(shortText);
expect((container.firstChild as HTMLElement).innerHTML).toBe(shortText);
});

test('paragraph 测试', async () => {
const { container } = render(<Paragraph>{shortText}</Paragraph>);
expect(container.firstChild.innerHTML).toBe(shortText);
expect((container.firstChild as HTMLElement).innerHTML).toBe(shortText);
});

test('text 测试', async () => {
const { container } = render(<Text>{shortText}</Text>);
expect(container.firstChild.innerHTML).toBe(shortText);
expect((container.firstChild as HTMLElement).innerHTML).toBe(shortText);
});

test('text code 测试', async () => {
Expand Down Expand Up @@ -116,4 +116,23 @@ describe('Typography 组件测试', () => {
fireEvent.click(container.querySelector('.t-button'));
expect(container.querySelector('.t-icon-smile')).toBeTruthy();
});

test('ellipsis 模式下 HTML 标签不被解析为真实元素', async () => {
const htmlStrings = [
{ text: '<b>bold text</b>', tag: 'b' },
{ text: '<a href="https://example.com">link</a>', tag: 'a' },
{ text: '<script>alert("xss")</script>', tag: 'script' },
];

for (const { text, tag } of htmlStrings) {
const { container, unmount } = render(
<div style={{ width: 200 }}>
<Text ellipsis>{text}</Text>
</div>,
);
expect(container.querySelector(tag)).toBeNull();
expect(container.querySelector('.t-typography-ellipsis-symbol-wrapper')).toHaveTextContent('...');
unmount();
}
});
});
14 changes: 12 additions & 2 deletions packages/components/typography/ellipsis/Truncate.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -146,13 +146,23 @@ export default class Truncate extends React.Component<TruncateProps, TruncateSta
return this.createMarkup(content) as unknown as string;
};

escapeHtml = (html: string) =>
html
.replace(/&/g, '&amp;')
.replace(/</g, '&lt;')
.replace(/>/g, '&gt;')
.replace(/"/g, '&quot;')
.replace(/'/g, '&#39;');

// Shim innerText to consistently break lines at <br/> but not at \n
innerText = (node: HTMLElement) => {
const div = document.createElement('div');
const contentKey = 'innerText' in window.HTMLElement.prototype ? 'innerText' : 'textContent';

const content = node.innerHTML.replace(/\r\n|\r|\n/g, ' ');
div.innerHTML = this.extractReplaceLinksKeys(content);
const replacedHtml = this.extractReplaceLinksKeys(node.innerHTML);
const escapedHtml = this.escapeHtml(replacedHtml);
const content = escapedHtml.replace(/\r\n|\r|\n/g, ' ');
div.innerHTML = content;

let text = div[contentKey];

Expand Down
6 changes: 6 additions & 0 deletions packages/tdesign-react/.changelog/pr-4117.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
---
pr_number: 4117
contributor: RylanBot
---

- fix(Typography): 修复开启 `ellipsis` 时,字符串被渲染为 HTML 标签的问题 @RylanBot ([#4117](https://github.com/Tencent/tdesign-react/pull/4117))