Skip to content

Commit f81963a

Browse files
committed
feat: sync code
sync code
2 parents 98a1700 + f9dc602 commit f81963a

File tree

10 files changed

+325
-97
lines changed

10 files changed

+325
-97
lines changed

src/components/editCell/__tests__/editCell.test.tsx

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,13 @@ import EditCell from '../index';
33
import { render, fireEvent, cleanup } from '@testing-library/react';
44
import '@testing-library/jest-dom/extend-expect';
55

6+
(global as any).document.createRange = () => ({
7+
selectNodeContents: jest.fn(),
8+
getBoundingClientRect: jest.fn(() => ({
9+
width: 500
10+
}))
11+
});
12+
613
const defaultProps = {
714
value: 'test editCell',
815
keyField: 'name',

src/components/editCell/index.tsx

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -60,7 +60,7 @@ export default class EditCell extends React.PureComponent<EditCellProps, EditCel
6060
</div>
6161
) : (
6262
<>
63-
<EllipsisText value={editValue} />
63+
<EllipsisText value={editValue} maxWidth={120}/>
6464
{
6565
!isView && <a onClick={this.onEdit}>修改</a>
6666
}
Lines changed: 128 additions & 24 deletions
Original file line numberDiff line numberDiff line change
@@ -1,28 +1,132 @@
1-
import React from 'react'
2-
import { render } from '@testing-library/react';
1+
import * as React from 'react';
2+
import { render, cleanup, fireEvent } from '@testing-library/react';
3+
import '@testing-library/jest-dom/extend-expect';
34
import EllipsisText from '../index';
45

5-
describe('ellipsisText Component test', () => {
6-
test('should render correct', () => {
7-
const expectValues: any = {
8-
value: '显示的文本',
9-
width: '100px',
10-
className: 'test-class-name',
11-
placement: 'topLeft'
12-
}
13-
const { getByTestId } = render(
14-
<EllipsisText
15-
value={expectValues.value}
16-
style={{ width: expectValues.width }}
17-
className={expectValues.className}
18-
placement={expectValues.placement}
19-
/>
6+
(global as any).document.createRange = () => ({
7+
selectNodeContents: jest.fn(),
8+
getBoundingClientRect: jest.fn(() => ({
9+
width: 500
10+
}))
11+
});
12+
13+
const defaultProps = {
14+
value: '我是很长长长长长长长长长长长长长长长长长长长长长长长长长长长长长长长长的文本'
15+
}
16+
17+
let wrapper, element
18+
describe('test ellipsis text if not set max width', () => {
19+
beforeEach(() => {
20+
jest.spyOn(document.documentElement, 'scrollWidth', 'get')
21+
.mockImplementation(() => 100);
22+
jest.spyOn(document.documentElement, 'offsetWidth', 'get')
23+
.mockImplementation(() => 600);
24+
Object.defineProperty(window, 'getComputedStyle', {
25+
value: jest.fn(() => ({
26+
paddingLeft: '0px',
27+
paddingRight: '0px'
28+
})
29+
)
30+
});
31+
32+
wrapper = render(
33+
<div>
34+
<EllipsisText {...defaultProps} />
35+
</div>
2036
);
37+
})
2138

22-
const oDiv = getByTestId('ellipsis_text');
23-
expect(oDiv).not.toBeNull();
24-
expect(oDiv.innerHTML).toEqual(expectValues.value);
25-
expect(oDiv.style.width).toEqual(expectValues.width);
26-
expect(oDiv.classList.contains(expectValues.className)).toEqual(true);
27-
});
28-
});
39+
afterEach(() => {
40+
cleanup()
41+
jest.restoreAllMocks()
42+
})
43+
44+
test('render correct value in ellipsis', () => {
45+
const { getByText } = wrapper
46+
const { value } = defaultProps
47+
element = getByText(value)
48+
49+
expect(element).toBeInTheDocument()
50+
expect(element.style.maxWidth).toBe('100px')
51+
})
52+
})
53+
54+
describe('test ellipsis text if set max width', () => {
55+
beforeEach(() => {
56+
Object.defineProperty(window, 'getComputedStyle', {
57+
value: () => ({
58+
getPropertyValue: (prop) => {
59+
return '';
60+
}
61+
})
62+
});
63+
64+
wrapper = render(
65+
<div>
66+
<EllipsisText {...defaultProps} maxWidth={100}/>
67+
</div>
68+
);
69+
})
70+
71+
afterEach(() => {
72+
cleanup()
73+
jest.restoreAllMocks()
74+
})
75+
76+
test('render correct value in ellipsis', () => {
77+
const { getByText } = wrapper
78+
const { value } = defaultProps
79+
element = getByText(value)
80+
81+
expect(element).toBeInTheDocument()
82+
expect(element.style.maxWidth).toBe('100px')
83+
})
84+
85+
test('render correct prompt info if mouse hover the text ', () => {
86+
const { getByText, getAllByText, baseElement } = wrapper
87+
const { value } = defaultProps
88+
element = getByText(value)
89+
90+
jest.useFakeTimers()
91+
fireEvent.mouseOver(element)
92+
jest.runAllTimers()
93+
94+
expect(baseElement.querySelector('.ant-tooltip-open')).toBeInTheDocument()
95+
expect(getAllByText(value).length).toBe(2)
96+
})
97+
})
98+
99+
describe('test ellipsis text if in IE8', () => {
100+
beforeEach(() => {
101+
jest.spyOn(document.documentElement, 'scrollWidth', 'get')
102+
.mockImplementation(() => 100);
103+
jest.spyOn(document.documentElement, 'offsetWidth', 'get')
104+
.mockImplementation(() => 100);
105+
Object.defineProperty(document.documentElement, 'currentStyle', {
106+
value: {
107+
paddingLeft: '0px',
108+
paddingRight: '0px'
109+
}
110+
});
111+
112+
wrapper = render(
113+
<div>
114+
<EllipsisText {...defaultProps}/>
115+
</div>
116+
);
117+
})
118+
119+
afterEach(() => {
120+
cleanup()
121+
jest.restoreAllMocks()
122+
})
123+
124+
test('render correct value in ellipsis', () => {
125+
const { getByText } = wrapper
126+
const { value } = defaultProps
127+
element = getByText(value)
128+
129+
expect(element).toBeInTheDocument()
130+
expect(element.style.maxWidth).toBe('0')
131+
})
132+
})

src/components/ellipsisText/index.tsx

Lines changed: 119 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -1,21 +1,123 @@
1-
import React from 'react'
2-
import { Tooltip } from 'antd';
3-
import { isEmpty } from 'lodash';
4-
type TooltipPlacement = "top" | "left" | "right" | "bottom" | "topLeft" | "topRight" | "bottomLeft" | "bottomRight" | "leftTop" | "leftBottom" | "rightTop" | "rightBottom"
5-
interface IProps {
6-
placement?: TooltipPlacement;
7-
style?: React.CSSProperties;
8-
className?: string;
9-
value: string | number;
1+
import React, { PureComponent } from "react";
2+
import { Tooltip } from "antd";
3+
import Resize from '../resize';
4+
5+
export interface EllipsisTextProps {
6+
value: string | number ;
7+
title?: string | number;
8+
className?: string;
9+
maxWidth?: string | number;
10+
[propName: string]: any;
11+
}
12+
13+
const initialState = {
14+
visible: false,
15+
isEllipsis: false,
16+
actMaxWidth: void 0
17+
};
18+
19+
type State = typeof initialState;
20+
21+
export interface NewHTMLElement extends HTMLElement {
22+
currentStyle?: CSSStyleDeclaration
1023
}
1124

12-
export default (props: IProps) => {
13-
let { value = '', className = '', style = {}, placement = undefined } = props;
25+
export default class EllipsisText extends PureComponent<EllipsisTextProps, State> {
26+
ellipsisRef: HTMLElement | null = null;
27+
state = {
28+
...initialState
29+
};
30+
31+
componentDidMount() {
32+
this.onResize();
33+
}
34+
35+
getRangeWidth = (ele: HTMLElement) => {
36+
const range = document.createRange();
37+
range.selectNodeContents(ele);
38+
const rangeWidth = range.getBoundingClientRect().width;
39+
return rangeWidth;
40+
};
41+
42+
getStyle = (dom: NewHTMLElement, attr: string) => {
43+
// Compatible width IE8
44+
const stylePadding =
45+
window?.getComputedStyle(dom)[attr] || dom.currentStyle[attr];
46+
47+
return parseInt(stylePadding.replace('px', ''));
48+
};
49+
50+
// The nearest block parent element
51+
getMaxWidth = (ele: HTMLElement) => {
52+
if (!ele) return;
53+
const { scrollWidth, offsetWidth, parentElement } = ele;
54+
// If inline element, find the parent element
55+
if (scrollWidth === 0) {
56+
return this.getMaxWidth(parentElement);
57+
}
58+
const ellipsisNode = this.ellipsisRef;
59+
ellipsisNode.style.display = "none";
60+
// Get the width of elements other than omitted text
61+
const rangeWidth = this.getRangeWidth(ele);
62+
const ellipsisWidth =
63+
offsetWidth -
64+
rangeWidth -
65+
this.getStyle(ele, "paddingRight") -
66+
this.getStyle(ele, "paddingLeft");
67+
68+
return ellipsisWidth < 0 ? 0 : ellipsisWidth;
69+
};
70+
71+
onResize = () => {
72+
const { maxWidth } = this.props;
73+
const ellipsisNode = this.ellipsisRef;
74+
const rangeWidth = this.getRangeWidth(ellipsisNode);
75+
const ellipsisWidth = this.getMaxWidth(ellipsisNode.parentElement);
76+
ellipsisNode.style.display = "inline-block";
77+
this.setState({
78+
actMaxWidth: ellipsisWidth,
79+
isEllipsis: rangeWidth > (maxWidth || ellipsisWidth)
80+
});
81+
};
82+
83+
handleVisibleChange = (visible: boolean) => {
84+
const { isEllipsis } = this.state;
85+
86+
this.setState({
87+
visible: visible && isEllipsis
88+
});
89+
};
90+
91+
render() {
92+
const { visible, actMaxWidth, isEllipsis } = this.state;
93+
const { title, value, className, maxWidth, ...other } = this.props;
94+
1495
return (
15-
<Tooltip placement={placement} title={`${value}`}>
16-
<span data-testid="ellipsis_text" className={isEmpty(className) ? 'dtc-ellipsis-text' : 'dtc-ellipsis-text ' + className} style={style}>
17-
{`${value}`}
18-
</span>
96+
<Resize onResize={maxWidth ? null:this.onResize}>
97+
<Tooltip
98+
title={title || value}
99+
visible={visible}
100+
onVisibleChange={this.handleVisibleChange}
101+
{...other}
102+
>
103+
<span
104+
ref={(ref) => (this.ellipsisRef = ref)}
105+
className={className}
106+
style={{
107+
whiteSpace: "nowrap",
108+
overflow: "hidden",
109+
textOverflow: "ellipsis",
110+
display: "inline-block",
111+
verticalAlign: "bottom",
112+
minWidth: "2em",
113+
maxWidth: maxWidth || actMaxWidth,
114+
cursor: isEllipsis ? "pointer" : "default"
115+
}}
116+
>
117+
{value}
118+
</span>
19119
</Tooltip>
20-
)
21-
}
120+
</Resize>
121+
);
122+
}
123+
}

src/components/ellipsisText/style.scss

Lines changed: 0 additions & 8 deletions
This file was deleted.

src/components/index.tsx

Lines changed: 25 additions & 23 deletions
Original file line numberDiff line numberDiff line change
@@ -1,31 +1,33 @@
11

2+
export { default as BreadcrumbRender } from './breadcrumb';
3+
export { default as ChromeDownload } from './chromeDownload';
24
export { default as Circle } from './circle';
3-
export { default as GoBack } from './goBack';
4-
export { default as SlidePane } from './slidePane';
5-
export { default as NotFound } from './notFound'
65
export { default as ContextMenu } from './contextMenu';
7-
export { default as ChromeDownload } from './chromeDownload';
8-
export { default as MultiSearchInput } from './multiSearchInput';
96
export { default as Cookies } from './cookies';
10-
export { default as ProgressBar } from './progressBar';
117
export { default as EasySelect } from './easySelect';
12-
export { default as TextMark } from './textMark';
13-
export { default as TableCell } from './tableCell';
14-
export { default as SpreadSheet } from './spreadsheet';
15-
export { default as SearchModal } from './searchModal';
16-
export { default as MarkdownRender } from './markdownRender';
17-
export { default as BreadcrumbRender } from './breadcrumb'
18-
export { default as Resize } from './resize';
19-
export { default as KeyEventListener } from './keyEventListener';
20-
export { default as FullScreenButton } from './fullscreen';
21-
export { default as ToolModal } from './toolModal';
22-
export { default as ScrollText } from './scrollText';
23-
export { default as SwitchWindow } from './window';
24-
export { default as ModalWithForm } from './modalWithForm';
25-
export { default as LoadError } from './loadError';
26-
export { default as RenderFormItem } from './formComponent';
8+
export { default as EditCell } from './editCell';
279
export { default as EditInput } from './editInput';
2810
export { default as EllipsisText } from './ellipsisText';
29-
export { default as MulSelectDropdown } from './mulSelectDropdown';
3011
export { default as ErrorBoundary } from './errorBoundary';
31-
export { default as EditCell } from './editCell';
12+
export { default as FullScreenButton } from './fullscreen';
13+
export { default as GlobalLoading } from './globalLoading';
14+
export { default as GoBack } from './goBack';
15+
export { default as KeyEventListener } from './keyEventListener';
16+
export { default as LoadError } from './loadError';
17+
export { default as MarkdownRender } from './markdownRender';
18+
export { default as ModalWithForm } from './modalWithForm';
19+
export { default as MulSelectDropdown } from './mulSelectDropdown';
20+
export { default as MultiSearchInput } from './multiSearchInput';
21+
export { default as NotFound } from './notFound';
22+
export { default as ProgressBar } from './progressBar';
23+
export { default as RenderFormItem } from './formComponent';
24+
export { default as Resize } from './resize';
25+
export { default as ScrollText } from './scrollText';
26+
export { default as SearchModal } from './searchModal';
27+
export { default as SlidePane } from './slidePane';
28+
export { default as SpreadSheet } from './spreadsheet';
29+
export { default as SwitchWindow } from './switchWindow';
30+
export { default as TableCell } from './tableCell';
31+
export { default as TextMark } from './textMark';
32+
export { default as ToolModal } from './toolModal';
33+

0 commit comments

Comments
 (0)