Skip to content

Commit fd8ee86

Browse files
vancecenovlan1
andauthored
feat(Message): add unit test and fix problems (#779)
* feat(Message): add unit test and fix problems * chore: test * feat(message): support object type of link * test(message): 提升覆盖率 * test: 提升覆盖率 --------- Co-authored-by: novlan1 <[email protected]>
1 parent 1314791 commit fd8ee86

File tree

12 files changed

+901
-207
lines changed

12 files changed

+901
-207
lines changed

site/test-coverage.js

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -34,7 +34,7 @@ module.exports = {
3434
list: { statements: '8%', branches: '0%', functions: '0%', lines: '8.69%' },
3535
loading: { statements: '98.43%', branches: '96.07%', functions: '100%', lines: '98.3%' },
3636
locale: { statements: '74.07%', branches: '62.5%', functions: '83.33%', lines: '75%' },
37-
message: { statements: '12.37%', branches: '0%', functions: '0%', lines: '12.76%' },
37+
message: { statements: '100%', branches: '94.44%', functions: '100%', lines: '100%' },
3838
navbar: { statements: '12.9%', branches: '0%', functions: '0%', lines: '13.79%' },
3939
noticeBar: { statements: '6.38%', branches: '0%', functions: '0%', lines: '6.52%' },
4040
overlay: { statements: '100%', branches: '100%', functions: '100%', lines: '100%' },

src/message/Message.tsx

Lines changed: 29 additions & 163 deletions
Original file line numberDiff line numberDiff line change
@@ -1,40 +1,25 @@
1-
import React, { useEffect, useState, useRef } from 'react';
2-
import classNames from 'classnames';
31
import { useTimeout } from 'ahooks';
2+
import classNames from 'classnames';
43
import { isObject } from 'lodash-es';
4+
import React, { useEffect, useRef, useState } from 'react';
55
import { CSSTransition } from 'react-transition-group';
66
import { CheckCircleFilledIcon, CloseIcon, ErrorCircleFilledIcon, InfoCircleFilledIcon } from 'tdesign-icons-react';
7-
8-
import type { TdMessageProps, MessageMarquee } from './type';
9-
import useMessageCssTransition from './hooks/useMessageCssTransition';
10-
import useDefaultProps from '../hooks/useDefaultProps';
11-
import { usePrefixClass } from '../hooks/useClass';
12-
13-
import type { StyledProps, TNode } from '../common';
14-
import parseTNode from '../_util/parseTNode';
157
import { convertUnit } from '../_util/convertUnit';
16-
import { messageDefaultProps } from './defaultProps';
17-
8+
import parseTNode from '../_util/parseTNode';
9+
import type { StyledProps, TNode } from '../common';
10+
import { usePrefixClass } from '../hooks/useClass';
11+
import useDefaultProps from '../hooks/useDefaultProps';
1812
import Link from '../link';
13+
import { messageDefaultProps } from './defaultProps';
14+
import useMarqueeAnimation from './hooks/useMarqueeAnimation';
15+
import useMessageCssTransition from './hooks/useMessageCssTransition';
16+
import type { TdMessageProps } from './type';
1917

2018
export interface MessageProps extends TdMessageProps, StyledProps {
2119
container?: Element;
2220
children?: React.ReactNode;
2321
}
2422

25-
interface IInnerState {
26-
duration: number;
27-
offset: number;
28-
listWidth: number;
29-
itemWidth: number;
30-
scroll: {
31-
marquee: boolean;
32-
speed: number;
33-
loop: number;
34-
delay: number;
35-
};
36-
}
37-
3823
const iconDefault = {
3924
info: <InfoCircleFilledIcon size={22} />,
4025
success: <CheckCircleFilledIcon size={22} />,
@@ -79,27 +64,10 @@ const Message: React.FC<MessageProps> = (originProps) => {
7964

8065
const name = usePrefixClass('message');
8166

82-
/**
83-
* 获取visibleChange函数引用
84-
*/
85-
const scrollingHandler = useRef<null | (() => void)>(null);
86-
8767
/**
8868
* duration为0时,取消倒计时
8969
*/
9070
const [messageDuration] = useState<number | null>(duration || null);
91-
const [state, setState] = useState<IInnerState>({
92-
duration: 0,
93-
offset: 0,
94-
listWidth: 0,
95-
itemWidth: 0,
96-
scroll: {
97-
marquee: false,
98-
speed: 50,
99-
loop: -1, // 值为 -1 表示循环播放,值为 0 表示不循环播放
100-
delay: 300,
101-
},
102-
});
10371

10472
const [messageVisible, setVisible] = useState<boolean>(visible ?? defaultVisible);
10573

@@ -125,139 +93,38 @@ const Message: React.FC<MessageProps> = (originProps) => {
12593
onDurationEnd?.();
12694
}, messageDuration);
12795

128-
const handleScrolling = () => {
129-
if (!marquee || (isObject(marquee) && (marquee as MessageMarquee))?.loop === 0) {
130-
return;
131-
}
132-
133-
const { loop, speed, delay } = state.scroll;
134-
135-
const scroll = {
136-
marquee: true,
137-
// 负数统一当作循环播放
138-
loop: Math.max(
139-
props.marquee === true || (props.marquee as MessageMarquee)?.loop == null
140-
? loop
141-
: (props.marquee as MessageMarquee)?.loop,
142-
-1,
143-
),
144-
// 速度必须为正数
145-
speed: Math.max(
146-
props.marquee === true || (props.marquee as MessageMarquee)?.speed == null
147-
? speed
148-
: (props.marquee as MessageMarquee)?.speed,
149-
1,
150-
),
151-
// 延迟不可为负数
152-
delay: Math.max(
153-
props.marquee === true || (props.marquee as MessageMarquee)?.delay == null
154-
? delay
155-
: (props.marquee as MessageMarquee)?.delay,
156-
0,
157-
),
158-
};
159-
setState({
160-
...state,
161-
scroll,
162-
offset: 0,
163-
});
164-
165-
// 设置动画
166-
setTimeout(() => {
167-
const textWrapDOMWidth = textWrapRef.current?.getBoundingClientRect().width;
168-
const textDOMWidth = textRef.current?.getBoundingClientRect().width;
169-
170-
setState({
171-
...state,
172-
scroll,
173-
offset: -textDOMWidth,
174-
duration: textDOMWidth / state.scroll.speed,
175-
listWidth: textWrapDOMWidth,
176-
itemWidth: textDOMWidth,
177-
});
178-
}, state.scroll.delay);
179-
};
180-
181-
scrollingHandler.current = handleScrolling;
182-
183-
useEffect(() => {
184-
scrollingHandler.current();
185-
}, []);
186-
187-
const animateStyle = {
188-
transform: state.offset ? `translateX(${state.offset}px)` : '',
189-
transitionDuration: `${state.duration}s`,
190-
transitionTimingFunction: 'linear',
191-
};
192-
193-
const resetTransition = () => {
194-
setState({
195-
...state,
196-
duration: 0,
197-
offset: state.listWidth,
198-
});
199-
200-
setTimeout(() => {
201-
setState({
202-
...state,
203-
offset: -state.itemWidth,
204-
duration: (state.itemWidth + state.listWidth) / state.scroll.speed,
205-
});
206-
}, 0);
207-
};
208-
209-
// 动画结束后,初始化动画
210-
const handleTransitionend = () => {
211-
resetTransition();
212-
213-
if (state.scroll.loop === -1) {
214-
return;
215-
}
216-
217-
const newState = {
218-
...state,
219-
scroll: {
220-
...state.scroll,
221-
loop: -(-state.scroll.loop),
222-
},
223-
};
224-
225-
if (state.scroll.loop === 0) {
226-
newState.scroll.marquee = false;
227-
}
228-
229-
setState(newState);
230-
};
231-
23296
const onLinkClick = (e) => {
23397
props.onLinkClick?.(e);
23498
};
99+
235100
const leftIcon = parseTNode(icon, null, iconDefault[theme] || iconDefault.info);
236101

237102
const getLinkContent = () => {
238103
if (typeof link === 'string') {
239104
return <Link theme="primary" content={link} />;
240105
}
241-
if (isObject(link)) {
106+
107+
// React element is also an object,need to add extra judgement
108+
if (isObject(link) && !React.isValidElement(link) && typeof link !== 'function') {
242109
return <Link theme="primary" {...link} />;
243110
}
244-
return parseTNode(link);
111+
112+
return parseTNode(link as TNode);
245113
};
246114

247115
const clickCloseButton = (e) => {
248116
setVisible(false);
249-
onCloseBtnClick?.(e);
117+
onCloseBtnClick?.({ e });
250118
};
251119

252-
const renderCloseBtn = () => {
253-
if (!closeBtn) return;
254-
255-
return closeBtn === true ? (
256-
<CloseIcon className={`${name}--close-btn`} size={22} onClick={clickCloseButton} />
120+
const renderCloseBtn = () =>
121+
closeBtn === true ? (
122+
<CloseIcon className={`${name}--close-btn`} size={22} />
257123
) : (
258124
parseTNode(closeBtn as string | TNode)
259125
);
260-
};
126+
127+
useMarqueeAnimation(marquee, textWrapRef, textRef);
261128

262129
return (
263130
<CSSTransition in={messageVisible} appear {...cssTransitionState.props} unmountOnExit>
@@ -268,21 +135,20 @@ const Message: React.FC<MessageProps> = (originProps) => {
268135
>
269136
<div className={`${name}__icon--left`}>{leftIcon}</div>
270137
<div ref={textWrapRef} className={classNames(`${name}__text-wrap`, { [`${name}__text-nowrap`]: marquee })}>
271-
<div
272-
ref={textRef}
273-
className={`${name}__text`}
274-
style={state.scroll.marquee ? animateStyle : {}}
275-
onTransitionEnd={handleTransitionend}
276-
>
138+
<div ref={textRef} className={`${name}__text`}>
277139
{parseTNode(content) || parseTNode(children)}
278140
</div>
279141
</div>
280142
{link && (
281-
<div className={`${name}__link`} onClick={onLinkClick}>
143+
<div className={`${name}__link`} onClick={(e) => onLinkClick({ e })}>
282144
{getLinkContent()}
283145
</div>
284146
)}
285-
{renderCloseBtn()}
147+
{closeBtn && (
148+
<div className={`${name}__close-wrap ${name}__icon--right`} onClick={clickCloseButton}>
149+
{renderCloseBtn()}
150+
</div>
151+
)}
286152
</div>
287153
</CSSTransition>
288154
);
Lines changed: 54 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,54 @@
1+
// Vitest Snapshot v1, https://vitest.dev/guide/snapshot.html
2+
3+
exports[`Message > props > marquee 1`] = `
4+
<div
5+
class="t-message__text"
6+
>
7+
hello marquee
8+
</div>
9+
`;
10+
11+
exports[`Message > props > marquee 2`] = `
12+
<div
13+
class="t-message__text"
14+
style="transform: translateX(500px); transition-duration: 0s;"
15+
>
16+
hello marquee
17+
</div>
18+
`;
19+
20+
exports[`Message > props > marquee 3`] = `
21+
<div
22+
class="t-message__text"
23+
style="transform: translateX(500px); transition-duration: 0s;"
24+
>
25+
hello marquee
26+
</div>
27+
`;
28+
29+
exports[`Message > props > marquee 4`] = `
30+
<div
31+
class="t-message__text"
32+
style="transform: translateX(-300px); transition-duration: 3s; transition-timing-function: linear;"
33+
>
34+
hello marquee
35+
</div>
36+
`;
37+
38+
exports[`Message > props > marquee 5`] = `
39+
<div
40+
class="t-message__text"
41+
style="transform: translateX(500px); transition-duration: 0s; transition-timing-function: linear;"
42+
>
43+
hello marquee
44+
</div>
45+
`;
46+
47+
exports[`Message > props > marquee 6`] = `
48+
<div
49+
class="t-message__text"
50+
style="transform: translateX(500px); transition-duration: 0s; transition-timing-function: linear;"
51+
>
52+
hello marquee
53+
</div>
54+
`;

0 commit comments

Comments
 (0)