Skip to content
Open
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
2 changes: 1 addition & 1 deletion src/catalogue/components/catalogue.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -103,7 +103,7 @@ const Catalogue = <U extends Record<string, any> = {}, T extends readOnlyTab = a
return (
<div className="tree__title">
<div className="tree__title--text">
<EllipsisText value={item.title} watchParentSizeChange maxWidth="100%" />
<EllipsisText value={item.title} dynamic maxWidth="100%" />
</div>
{edit && renderNodeHover(item)}
</div>
Expand Down
2 changes: 1 addition & 1 deletion src/chat/conversations/groupTitle/index.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,7 @@ const GroupTitle: React.FC<GroupTitleProps> = (props) => {
maxWidth="100%"
placement="right"
destroyTooltipOnHide
watchParentSizeChange
dynamic
/>
)}
</div>
Expand Down
2 changes: 1 addition & 1 deletion src/chat/conversations/item/index.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -44,7 +44,7 @@ const Item: React.FC<ConversationsItemProps> = (props) => {
{info.icon && <div className={`${prefixCls}__item__icon`}>{info.icon}</div>}
<div className={`${prefixCls}__item__title`}>
<EllipsisText
watchParentSizeChange
dynamic
value={info.title}
placement="right"
maxWidth="100%"
Expand Down
27 changes: 27 additions & 0 deletions src/ellipsisText/__tests__/ellipsisText.test.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@ import EllipsisText from '../index';

const defaultProps = {
value: '我是很长长长长长长长长长长长长长长长长长长长长长长长长长长长长长长长长的文本',
dynamic: true,
};

let wrapper: RenderResult, element;
Expand Down Expand Up @@ -194,3 +195,29 @@ describe('test ellipsis text if set max width', () => {
fireEvent.mouseLeave(element);
});
});

describe('use Typography by default (dynamic not set)', () => {
afterEach(() => {
cleanup();
});

test('renders antd Typography.Text instead of the measured span', () => {
const { container, getByText } = render(<EllipsisText {...defaultProps} dynamic={false} />);
const { value } = defaultProps;

expect(getByText(value)).toBeInTheDocument();
expect(getByText(value).className.split(' ')).toContain('ant-typography');
expect(container.querySelector('.dtc-ellipsis-text')).toBeNull();
});

test('renders Typography.Text when dynamic prop is omitted', () => {
const { container } = render(<EllipsisText value="省略 dynamic 时的默认渲染" />);
expect(container.querySelector('.ant-typography')).not.toBeNull();
expect(container.querySelector('.dtc-ellipsis-text')).toBeNull();
});

test('renders content when value is a function', () => {
const { getByText } = render(<EllipsisText value={() => '函数返回值'} dynamic={false} />);
expect(getByText('函数返回值')).toBeInTheDocument();
});
});
163 changes: 163 additions & 0 deletions src/ellipsisText/__tests__/useEllipsisTextStyle.test.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,163 @@
import React from 'react';
import { cleanup, render } from '@testing-library/react';
import { act, renderHook } from '@testing-library/react-hooks';

import useEllipsisTextStyle from '../useEllipsisTextStyle';
import { getRangeWidth, getStyle, getTextContainerWidth } from '../utils';

jest.mock('../utils', () => {
const actual = jest.requireActual('../utils');
return {
...actual,
getValidContainerElement: jest.fn(),
getRangeWidth: jest.fn(),
getStyle: jest.fn(),
getTextContainerWidth: jest.fn(),
};
});

describe('Test useEllipsisTextStyle', () => {
const mockGetRangeWidth = getRangeWidth as jest.Mock;
const mockGetStyle = getStyle as jest.Mock;
const mockGetTextContainerWidth = getTextContainerWidth as jest.Mock;

beforeEach(() => {
cleanup();
jest.clearAllMocks();
});

it('should return a ref, overflow state, style, and trigger function', () => {
const { result } = renderHook(() => useEllipsisTextStyle('Test Text'));
const [ref, isOverflow, style, updateTextStyle] = result.current;

expect(ref).toBeInstanceOf(Object);
expect(typeof isOverflow).toBe('boolean');
expect(style).toBeInstanceOf(Object);
expect(typeof updateTextStyle).toBe('function');
});

it('should calculate overflow correctly', () => {
const { result } = renderHook(() => useEllipsisTextStyle('Test Text'));
const [ref, , , updateTextStyle] = result.current;

render(<span ref={ref}></span>);

mockGetRangeWidth.mockReturnValue(150);
mockGetTextContainerWidth.mockReturnValue(100);

act(() => {
updateTextStyle();
});

const [, isOverflow, style] = result.current;

expect(mockGetTextContainerWidth).toHaveBeenCalled();
expect(isOverflow).toBe(true);
expect(style.maxWidth).toBe(100);
});

it('should not overflow if the container width is sufficient', () => {
const { result } = renderHook(() => useEllipsisTextStyle('Test Text'));
const [ref, , , updateTextStyle] = result.current;

render(<span ref={ref}></span>);

mockGetRangeWidth.mockReturnValue(80);
mockGetTextContainerWidth.mockReturnValue(100);

act(() => {
updateTextStyle();
});
const [, isOverflow, style] = result.current;

expect(isOverflow).toBe(false);
expect(style.maxWidth).toBe(100);
});

it('should inherit cursor style from parent', () => {
const { result } = renderHook(() => useEllipsisTextStyle('Test Text'));
const [ref, , , updateTextStyle] = result.current;

render(<span ref={ref}></span>);

mockGetTextContainerWidth.mockReturnValue(100);
mockGetStyle.mockReturnValue('pointer');

act(() => {
updateTextStyle();
});
const [, , style] = result.current;

expect(style.cursor).toBe('pointer');
});

it('should have cursor style as default when text is not overflowing and parent cursor is default', () => {
const { result } = renderHook(() => useEllipsisTextStyle('Test Text'));
const [ref, , , updateTextStyle] = result.current;

render(<span ref={ref}></span>);

mockGetRangeWidth.mockReturnValue(80);
mockGetTextContainerWidth.mockReturnValue(100);
mockGetStyle.mockReturnValue('default');

act(() => {
updateTextStyle();
});
const [, isOverflow, style] = result.current;

expect(isOverflow).toBe(false);
expect(style.cursor).toBe('default');
});

it('should have cursor style as pointer when text is overflowing and parent cursor is default', () => {
const { result } = renderHook(() => useEllipsisTextStyle('Test Text'));
const [ref, , , updateTextStyle] = result.current;

render(<span ref={ref}></span>);

mockGetRangeWidth.mockReturnValue(150);
mockGetTextContainerWidth.mockReturnValue(100);
mockGetStyle.mockReturnValue('default');

act(() => {
updateTextStyle();
});
const [, isOverflow, style] = result.current;

expect(isOverflow).toBe(true);
expect(style.cursor).toBe('pointer');
});

it('should set container width when container width < maxWidth', () => {
const { result } = renderHook(() => useEllipsisTextStyle('Test Text', 120));
const [ref, , , updateTextStyle] = result.current;

render(<span ref={ref}></span>);

mockGetTextContainerWidth.mockReturnValue(100);

act(() => {
updateTextStyle();
});
const [, , style] = result.current;

expect(style.maxWidth).toBe(100);
});

it('should set maxWidth when container width > maxWidth ', () => {
const { result } = renderHook(() => useEllipsisTextStyle('Test Text', 80));
const [ref, , , updateTextStyle] = result.current;

render(<span ref={ref}></span>);

mockGetTextContainerWidth.mockReturnValue(80);

act(() => {
updateTextStyle();
});
const [, , style] = result.current;

expect(style.maxWidth).toBe(80);
});
});
38 changes: 38 additions & 0 deletions src/ellipsisText/demos/dynamic.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,38 @@
import React, { useState } from 'react';
import { Radio, Slider } from 'antd';
import { EllipsisText } from 'dt-react-component';

const LONG_TEXT =
'这是一段很长的文本,用来对比默认省略与动态测量的差异,你可以尝试拖动滑块和切换模式来观察变化';

export default () => {
const [mode, setMode] = useState<'default' | 'dynamic'>('default');
const [maxWidth, setMaxWidth] = useState(180);
const dynamic = mode === 'dynamic';

return (
<div style={{ width: 300 }}>
<Radio.Group value={mode} onChange={(e) => setMode(e.target.value)}>
<Radio value="default">默认(Typography 轻量省略)</Radio>
<Radio value="dynamic">dynamic(动态测量)</Radio>
</Radio.Group>
<div style={{ marginTop: 8 }}>
<div>maxWidth:{maxWidth}px(仅 dynamic 时生效,默认模式下置灰)</div>
<Slider
min={80}
max={280}
value={maxWidth}
onChange={setMaxWidth}
disabled={!dynamic}
/>
</div>
<div style={{ marginTop: 8 }}>
{dynamic ? (
<EllipsisText dynamic value={LONG_TEXT} maxWidth={maxWidth} />
) : (
<EllipsisText value={LONG_TEXT} />
)}
</div>
</div>
);
};
2 changes: 2 additions & 0 deletions src/ellipsisText/demos/flex.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@ export default () => {
}}
>
<EllipsisText
dynamic
value={
'我是很长长长长长长长长长长长长长长长长长长长长长长长长长长长长长长长长的文本'
}
Expand All @@ -25,6 +26,7 @@ export default () => {
>
我是来捣乱的
<EllipsisText
dynamic
value={
'我是很长长长长长长长长长长长长长长长长长长长长长长长长长长长长长长长长的文本'
}
Expand Down
1 change: 1 addition & 0 deletions src/ellipsisText/demos/inlineElement.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@ export default () => {
>
<span>
<EllipsisText
dynamic
value={
'我是很长长长长长长长长长长长长长长长长长长长长长长长长长长长长长长长长的文本'
}
Expand Down
1 change: 1 addition & 0 deletions src/ellipsisText/demos/maxWidth.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,7 @@ export default () => {
</Radio.Group>
<Divider />
<EllipsisText
dynamic
value={
'我是很长长长长长长长长长长长长长长长长长长长长长长长长长长长长长长长长长长长长长长长长长长长长长长长长长长长长长长长长长长长长长长长长长长长长长长长长长长长长长长的文本'
}
Expand Down
2 changes: 2 additions & 0 deletions src/ellipsisText/demos/multiple.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -9,12 +9,14 @@ export default () => {
}}
>
<EllipsisText
dynamic
value={
'我是很长长长长长长长长长长长长长长长长长长长长长长长长长长长长长长长长的文本'
}
maxWidth={200}
/>
<EllipsisText
dynamic
value={
'我是很长长长长长长长长长长长长长长长长长长长长长长长长长长长长长长长长的文本'
}
Expand Down
60 changes: 60 additions & 0 deletions src/ellipsisText/demos/select.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,60 @@
import React, { useEffect } from 'react';
import { Select } from 'antd';
import { EllipsisText } from 'dt-react-component';

export default () => {
const [options, setOptions] = React.useState<{ label: string; value: string }[]>([]);

const sleep = (time: number) => {
return new Promise((resolve) => {
setTimeout(resolve, time);
});
};

const getOptions = async () => {
await sleep(2000);
const opt = [
{
label: '任务名称',
value: 'task-name-1',
},
{
label: '这是一个较长的任务名称',
value: 'task-name-2',
},
{
label: '这是一段特别长的任务名称',
value: 'task-name-3',
},
{
label: '这是一段特别长的任务名称,用于验证在文本过长时展示省略号',
value: 'task-name-4',
},
{
label: '这是一段特别长的任务名称,用于验证在文本过长时优雅地展示省略号,并通过悬停查看完整内容',
value: 'task-name-5',
},
];
setOptions(opt);
};

useEffect(() => {
getOptions();
}, []);

return (
<div style={{ width: 200 }}>
<Select style={{ width: '100%' }}>
{options.map((opt) => {
return (
<Select.Option value={opt.value} key={opt.value}>
<span>
<EllipsisText dynamic value={opt.label} maxWidth={'100%'} />
</span>
</Select.Option>
);
})}
</Select>
</div>
);
};
Loading
Loading