mirror of
https://github.com/ant-design/ant-design.git
synced 2025-01-18 14:13:37 +08:00
refactor(badge): 🛠 improve code (#24601)
* refactor(badge): improve code * refactor(badge): improve code * increase coverage
This commit is contained in:
parent
b95207d58e
commit
162b6979af
@ -1,7 +1,6 @@
|
||||
import * as React from 'react';
|
||||
import omit from 'omit.js';
|
||||
import classNames from 'classnames';
|
||||
import { ConfigConsumer, ConfigConsumerProps } from '../config-provider';
|
||||
import { ConfigContext } from '../config-provider';
|
||||
import { cloneElement } from '../_util/reactNode';
|
||||
|
||||
function getNumberArray(num: string | number | undefined | null) {
|
||||
@ -51,15 +50,27 @@ export interface ScrollNumberState {
|
||||
count?: string | number | null;
|
||||
}
|
||||
|
||||
const ScrollNumber: React.FC<ScrollNumberProps> = props => {
|
||||
const ScrollNumber: React.FC<ScrollNumberProps> = ({
|
||||
prefixCls: customizePrefixCls,
|
||||
count: customizeCount,
|
||||
className,
|
||||
style,
|
||||
title,
|
||||
component = 'sup',
|
||||
displayComponent,
|
||||
onAnimated = () => {},
|
||||
...restProps
|
||||
}) => {
|
||||
const [animateStarted, setAnimateStarted] = React.useState(true);
|
||||
const [count, setCount] = React.useState(props.count);
|
||||
const [prevCount, setPrevCount] = React.useState(props.count);
|
||||
const [lastCount, setLastCount] = React.useState(props.count);
|
||||
const [count, setCount] = React.useState(customizeCount);
|
||||
const [prevCount, setPrevCount] = React.useState(customizeCount);
|
||||
const [lastCount, setLastCount] = React.useState(customizeCount);
|
||||
const { getPrefixCls } = React.useContext(ConfigContext);
|
||||
const prefixCls = getPrefixCls('scroll-number', customizePrefixCls);
|
||||
|
||||
if (prevCount !== props.count) {
|
||||
if (prevCount !== customizeCount) {
|
||||
setAnimateStarted(true);
|
||||
setPrevCount(props.count);
|
||||
setPrevCount(customizeCount);
|
||||
}
|
||||
|
||||
React.useEffect(() => {
|
||||
@ -70,10 +81,8 @@ const ScrollNumber: React.FC<ScrollNumberProps> = props => {
|
||||
// performing the transition.
|
||||
timeout = setTimeout(() => {
|
||||
setAnimateStarted(false);
|
||||
setCount(props.count);
|
||||
if (props.onAnimated) {
|
||||
props.onAnimated();
|
||||
}
|
||||
setCount(customizeCount);
|
||||
onAnimated();
|
||||
});
|
||||
}
|
||||
return () => {
|
||||
@ -81,7 +90,7 @@ const ScrollNumber: React.FC<ScrollNumberProps> = props => {
|
||||
clearTimeout(timeout);
|
||||
}
|
||||
};
|
||||
}, [animateStarted, count, props.count, props.onAnimated]);
|
||||
}, [animateStarted, customizeCount, onAnimated]);
|
||||
|
||||
const getPositionByNum = (num: number, i: number) => {
|
||||
const currentCount = Math.abs(Number(count));
|
||||
@ -106,7 +115,7 @@ const ScrollNumber: React.FC<ScrollNumberProps> = props => {
|
||||
return num;
|
||||
};
|
||||
|
||||
const renderCurrentNumber = (prefixCls: string, num: number | string, i: number) => {
|
||||
const renderCurrentNumber = (num: number | string, i: number) => {
|
||||
if (typeof num === 'number') {
|
||||
const position = getPositionByNum(num, i);
|
||||
const removeTransition = animateStarted || getNumberArray(lastCount)[i] === undefined;
|
||||
@ -133,35 +142,18 @@ const ScrollNumber: React.FC<ScrollNumberProps> = props => {
|
||||
);
|
||||
};
|
||||
|
||||
const renderNumberElement = (prefixCls: string) => {
|
||||
const renderNumberElement = () => {
|
||||
if (count && Number(count) % 1 === 0) {
|
||||
return getNumberArray(count)
|
||||
.map((num, i) => renderCurrentNumber(prefixCls, num, i))
|
||||
.map((num, i) => renderCurrentNumber(num, i))
|
||||
.reverse();
|
||||
}
|
||||
return count;
|
||||
};
|
||||
|
||||
const renderScrollNumber = ({ getPrefixCls }: ConfigConsumerProps) => {
|
||||
const {
|
||||
prefixCls: customizePrefixCls,
|
||||
className,
|
||||
style,
|
||||
title,
|
||||
component = 'sup',
|
||||
displayComponent,
|
||||
} = props;
|
||||
// fix https://fb.me/react-unknown-prop
|
||||
const restProps = omit(props, [
|
||||
'count',
|
||||
'onAnimated',
|
||||
'component',
|
||||
'prefixCls',
|
||||
'displayComponent',
|
||||
]);
|
||||
const prefixCls = getPrefixCls('scroll-number', customizePrefixCls);
|
||||
const newProps = {
|
||||
...restProps,
|
||||
style,
|
||||
className: classNames(prefixCls, className),
|
||||
title: title as string,
|
||||
};
|
||||
@ -183,15 +175,7 @@ const ScrollNumber: React.FC<ScrollNumberProps> = props => {
|
||||
),
|
||||
});
|
||||
}
|
||||
return React.createElement(component as any, newProps, renderNumberElement(prefixCls));
|
||||
};
|
||||
|
||||
return <ConfigConsumer>{renderScrollNumber}</ConfigConsumer>;
|
||||
};
|
||||
|
||||
ScrollNumber.defaultProps = {
|
||||
count: null,
|
||||
onAnimated() {},
|
||||
return React.createElement(component as any, newProps, renderNumberElement());
|
||||
};
|
||||
|
||||
export default ScrollNumber;
|
||||
|
@ -17,9 +17,6 @@ exports[`Badge badge should support float number 1`] = `
|
||||
exports[`Badge badge should support float number 2`] = `
|
||||
<Badge
|
||||
count="3.5"
|
||||
dot={false}
|
||||
overflowCount={99}
|
||||
showZero={false}
|
||||
>
|
||||
<span
|
||||
className="ant-badge ant-badge-not-a-wrapper"
|
||||
@ -51,7 +48,6 @@ exports[`Badge badge should support float number 2`] = `
|
||||
count="3.5"
|
||||
data-show={true}
|
||||
key="scrollNumber"
|
||||
onAnimated={[Function]}
|
||||
prefixCls="ant-scroll-number"
|
||||
title="3.5"
|
||||
>
|
||||
@ -938,9 +934,6 @@ exports[`Badge should be compatible with borderColor style 1`] = `
|
||||
exports[`Badge should render when count is changed 1`] = `
|
||||
<Badge
|
||||
count={10}
|
||||
dot={false}
|
||||
overflowCount={99}
|
||||
showZero={false}
|
||||
>
|
||||
<span
|
||||
className="ant-badge ant-badge-not-a-wrapper"
|
||||
@ -972,7 +965,6 @@ exports[`Badge should render when count is changed 1`] = `
|
||||
count={10}
|
||||
data-show={true}
|
||||
key="scrollNumber"
|
||||
onAnimated={[Function]}
|
||||
prefixCls="ant-scroll-number"
|
||||
title={10}
|
||||
>
|
||||
@ -1185,9 +1177,6 @@ exports[`Badge should render when count is changed 1`] = `
|
||||
exports[`Badge should render when count is changed 2`] = `
|
||||
<Badge
|
||||
count={11}
|
||||
dot={false}
|
||||
overflowCount={99}
|
||||
showZero={false}
|
||||
>
|
||||
<span
|
||||
className="ant-badge ant-badge-not-a-wrapper"
|
||||
@ -1219,7 +1208,6 @@ exports[`Badge should render when count is changed 2`] = `
|
||||
count={11}
|
||||
data-show={true}
|
||||
key="scrollNumber"
|
||||
onAnimated={[Function]}
|
||||
prefixCls="ant-scroll-number"
|
||||
title={11}
|
||||
>
|
||||
@ -1625,9 +1613,6 @@ exports[`Badge should render when count is changed 2`] = `
|
||||
exports[`Badge should render when count is changed 3`] = `
|
||||
<Badge
|
||||
count={11}
|
||||
dot={false}
|
||||
overflowCount={99}
|
||||
showZero={false}
|
||||
>
|
||||
<span
|
||||
className="ant-badge ant-badge-not-a-wrapper"
|
||||
@ -1659,7 +1644,6 @@ exports[`Badge should render when count is changed 3`] = `
|
||||
count={11}
|
||||
data-show={true}
|
||||
key="scrollNumber"
|
||||
onAnimated={[Function]}
|
||||
prefixCls="ant-scroll-number"
|
||||
title={11}
|
||||
>
|
||||
@ -2064,10 +2048,7 @@ exports[`Badge should render when count is changed 3`] = `
|
||||
|
||||
exports[`Badge should render when count is changed 4`] = `
|
||||
<Badge
|
||||
count={10}
|
||||
dot={false}
|
||||
overflowCount={99}
|
||||
showZero={false}
|
||||
count={111}
|
||||
>
|
||||
<span
|
||||
className="ant-badge ant-badge-not-a-wrapper"
|
||||
@ -2096,17 +2077,16 @@ exports[`Badge should render when count is changed 4`] = `
|
||||
>
|
||||
<ScrollNumber
|
||||
className="ant-badge-count ant-badge-multiple-words"
|
||||
count={10}
|
||||
count="99+"
|
||||
data-show={true}
|
||||
key="scrollNumber"
|
||||
onAnimated={[Function]}
|
||||
prefixCls="ant-scroll-number"
|
||||
title={10}
|
||||
title={111}
|
||||
>
|
||||
<sup
|
||||
className="ant-scroll-number ant-badge-count ant-badge-multiple-words"
|
||||
data-show={true}
|
||||
title={10}
|
||||
title={111}
|
||||
>
|
||||
<span
|
||||
className="ant-scroll-number-only"
|
||||
@ -2503,11 +2483,59 @@ exports[`Badge should render when count is changed 4`] = `
|
||||
`;
|
||||
|
||||
exports[`Badge should render when count is changed 5`] = `
|
||||
<Badge
|
||||
count={10}
|
||||
>
|
||||
<span
|
||||
className="ant-badge ant-badge-not-a-wrapper"
|
||||
>
|
||||
<Animate
|
||||
animation={Object {}}
|
||||
component=""
|
||||
componentProps={Object {}}
|
||||
onAppear={[Function]}
|
||||
onEnd={[Function]}
|
||||
onEnter={[Function]}
|
||||
onLeave={[Function]}
|
||||
showProp="data-show"
|
||||
transitionAppear={true}
|
||||
transitionEnter={true}
|
||||
transitionLeave={true}
|
||||
transitionName=""
|
||||
>
|
||||
<AnimateChild
|
||||
animation={Object {}}
|
||||
key="scrollNumber"
|
||||
transitionAppear={true}
|
||||
transitionEnter={true}
|
||||
transitionLeave={true}
|
||||
transitionName=""
|
||||
>
|
||||
<ScrollNumber
|
||||
className="ant-badge-count ant-badge-multiple-words"
|
||||
count={10}
|
||||
data-show={true}
|
||||
key="scrollNumber"
|
||||
prefixCls="ant-scroll-number"
|
||||
title={10}
|
||||
>
|
||||
<sup
|
||||
className="ant-scroll-number ant-badge-count ant-badge-multiple-words"
|
||||
data-show={true}
|
||||
title={10}
|
||||
>
|
||||
99+
|
||||
</sup>
|
||||
</ScrollNumber>
|
||||
</AnimateChild>
|
||||
</Animate>
|
||||
</span>
|
||||
</Badge>
|
||||
`;
|
||||
|
||||
exports[`Badge should render when count is changed 6`] = `
|
||||
<Badge
|
||||
count={9}
|
||||
dot={false}
|
||||
overflowCount={99}
|
||||
showZero={false}
|
||||
>
|
||||
<span
|
||||
className="ant-badge ant-badge-not-a-wrapper"
|
||||
@ -2539,7 +2567,6 @@ exports[`Badge should render when count is changed 5`] = `
|
||||
count={9}
|
||||
data-show={true}
|
||||
key="scrollNumber"
|
||||
onAnimated={[Function]}
|
||||
prefixCls="ant-scroll-number"
|
||||
title={9}
|
||||
>
|
||||
|
@ -66,6 +66,9 @@ describe('Badge', () => {
|
||||
wrapper.setProps({ count: 11 });
|
||||
jest.runAllTimers();
|
||||
expect(wrapper).toMatchSnapshot();
|
||||
wrapper.setProps({ count: 111 });
|
||||
jest.runAllTimers();
|
||||
expect(wrapper).toMatchSnapshot();
|
||||
wrapper.setProps({ count: 10 });
|
||||
jest.runAllTimers();
|
||||
expect(wrapper).toMatchSnapshot();
|
||||
|
@ -1,10 +1,9 @@
|
||||
import * as React from 'react';
|
||||
import Animate from 'rc-animate';
|
||||
import omit from 'omit.js';
|
||||
import classNames from 'classnames';
|
||||
import ScrollNumber from './ScrollNumber';
|
||||
import { PresetColorTypes, PresetColorType, PresetStatusColorType } from '../_util/colors';
|
||||
import { ConfigConsumer, ConfigConsumerProps } from '../config-provider';
|
||||
import { ConfigContext } from '../config-provider';
|
||||
import { LiteralUnion } from '../_util/type';
|
||||
import { cloneElement } from '../_util/reactNode';
|
||||
|
||||
@ -33,16 +32,33 @@ function isPresetColor(color?: string): boolean {
|
||||
return (PresetColorTypes as any[]).indexOf(color) !== -1;
|
||||
}
|
||||
|
||||
const Badge: React.FC<BadgeProps> = props => {
|
||||
const Badge: React.FC<BadgeProps> = ({
|
||||
prefixCls: customizePrefixCls,
|
||||
scrollNumberPrefixCls: customizeScrollNumberPrefixCls,
|
||||
children,
|
||||
status,
|
||||
text,
|
||||
color,
|
||||
count = null,
|
||||
overflowCount = 99,
|
||||
dot = false,
|
||||
title,
|
||||
offset,
|
||||
style,
|
||||
className,
|
||||
showZero = false,
|
||||
...restProps
|
||||
}) => {
|
||||
const { getPrefixCls, direction } = React.useContext(ConfigContext);
|
||||
const prefixCls = getPrefixCls('badge', customizePrefixCls);
|
||||
|
||||
const getNumberedDisplayCount = () => {
|
||||
const { count, overflowCount } = props;
|
||||
const displayCount =
|
||||
(count as number) > (overflowCount as number) ? `${overflowCount}+` : count;
|
||||
return displayCount as string | number | null;
|
||||
};
|
||||
|
||||
const hasStatus = (): boolean => {
|
||||
const { status, color } = props;
|
||||
return !!status || !!color;
|
||||
};
|
||||
|
||||
@ -52,7 +68,6 @@ const Badge: React.FC<BadgeProps> = props => {
|
||||
};
|
||||
|
||||
const isDot = () => {
|
||||
const { dot } = props;
|
||||
return (dot && !isZero()) || hasStatus();
|
||||
};
|
||||
|
||||
@ -65,7 +80,6 @@ const Badge: React.FC<BadgeProps> = props => {
|
||||
};
|
||||
|
||||
const getScrollNumberTitle = () => {
|
||||
const { title, count } = props;
|
||||
if (title) {
|
||||
return title;
|
||||
}
|
||||
@ -73,7 +87,6 @@ const Badge: React.FC<BadgeProps> = props => {
|
||||
};
|
||||
|
||||
const getStyleWithOffset = () => {
|
||||
const { offset, style } = props;
|
||||
return offset
|
||||
? {
|
||||
right: -parseInt(offset[0] as string, 10),
|
||||
@ -83,30 +96,18 @@ const Badge: React.FC<BadgeProps> = props => {
|
||||
: style;
|
||||
};
|
||||
|
||||
const getBadgeClassName = (prefixCls: string, direction: string = 'ltr') => {
|
||||
const { className, children } = props;
|
||||
return classNames(className, prefixCls, {
|
||||
[`${prefixCls}-status`]: hasStatus(),
|
||||
[`${prefixCls}-not-a-wrapper`]: !children,
|
||||
[`${prefixCls}-rtl`]: direction === 'rtl',
|
||||
}) as string;
|
||||
};
|
||||
|
||||
const isHidden = () => {
|
||||
const { showZero } = props;
|
||||
const displayCount = getDisplayCount();
|
||||
const isEmpty = displayCount === null || displayCount === undefined || displayCount === '';
|
||||
return (isEmpty || (isZero() && !showZero)) && !isDot();
|
||||
};
|
||||
|
||||
const renderStatusText = (prefixCls: string) => {
|
||||
const { text } = props;
|
||||
const renderStatusText = () => {
|
||||
const hidden = isHidden();
|
||||
return hidden || !text ? null : <span className={`${prefixCls}-status-text`}>{text}</span>;
|
||||
};
|
||||
|
||||
const renderDisplayComponent = () => {
|
||||
const { count } = props;
|
||||
const customNode = count as React.ReactElement<any>;
|
||||
if (!customNode || typeof customNode !== 'object') {
|
||||
return undefined;
|
||||
@ -119,18 +120,17 @@ const Badge: React.FC<BadgeProps> = props => {
|
||||
});
|
||||
};
|
||||
|
||||
const renderBadgeNumber = (prefixCls: string, scrollNumberPrefixCls: string) => {
|
||||
const { status, count, color } = props;
|
||||
|
||||
const renderBadgeNumber = () => {
|
||||
const scrollNumberPrefixCls = getPrefixCls('scroll-number', customizeScrollNumberPrefixCls);
|
||||
const displayCount = getDisplayCount();
|
||||
const dot = isDot();
|
||||
const bDot = isDot();
|
||||
const hidden = isHidden();
|
||||
|
||||
const scrollNumberCls = classNames({
|
||||
[`${prefixCls}-dot`]: dot,
|
||||
[`${prefixCls}-count`]: !dot,
|
||||
[`${prefixCls}-dot`]: bDot,
|
||||
[`${prefixCls}-count`]: !bDot,
|
||||
[`${prefixCls}-multiple-words`]:
|
||||
!dot && count && count.toString && count.toString().length > 1,
|
||||
!bDot && count && count.toString && count.toString().length > 1,
|
||||
[`${prefixCls}-status-${status}`]: !!status,
|
||||
[`${prefixCls}-status-${color}`]: isPresetColor(color),
|
||||
});
|
||||
@ -155,33 +155,6 @@ const Badge: React.FC<BadgeProps> = props => {
|
||||
);
|
||||
};
|
||||
|
||||
const renderBadge = ({ getPrefixCls, direction }: ConfigConsumerProps) => {
|
||||
const {
|
||||
prefixCls: customizePrefixCls,
|
||||
scrollNumberPrefixCls: customizeScrollNumberPrefixCls,
|
||||
children,
|
||||
status,
|
||||
text,
|
||||
color,
|
||||
...restProps
|
||||
} = props;
|
||||
const omitArr = [
|
||||
'count',
|
||||
'showZero',
|
||||
'overflowCount',
|
||||
'className',
|
||||
'style',
|
||||
'dot',
|
||||
'offset',
|
||||
'title',
|
||||
];
|
||||
|
||||
const prefixCls = getPrefixCls('badge', customizePrefixCls);
|
||||
const scrollNumberPrefixCls = getPrefixCls('scroll-number', customizeScrollNumberPrefixCls);
|
||||
|
||||
const scrollNumber = renderBadgeNumber(prefixCls, scrollNumberPrefixCls);
|
||||
const statusText = renderStatusText(prefixCls);
|
||||
|
||||
const statusCls = classNames({
|
||||
[`${prefixCls}-status-dot`]: hasStatus(),
|
||||
[`${prefixCls}-status-${status}`]: !!status,
|
||||
@ -192,16 +165,18 @@ const Badge: React.FC<BadgeProps> = props => {
|
||||
statusStyle.background = color;
|
||||
}
|
||||
|
||||
const badgeClassName = classNames(className, prefixCls, {
|
||||
[`${prefixCls}-status`]: hasStatus(),
|
||||
[`${prefixCls}-not-a-wrapper`]: !children,
|
||||
[`${prefixCls}-rtl`]: direction === 'rtl',
|
||||
});
|
||||
|
||||
// <Badge status="success" />
|
||||
if (!children && hasStatus()) {
|
||||
const styleWithOffset = getStyleWithOffset();
|
||||
const statusTextColor = styleWithOffset && styleWithOffset.color;
|
||||
return (
|
||||
<span
|
||||
{...omit(restProps, omitArr)}
|
||||
className={getBadgeClassName(prefixCls, direction)}
|
||||
style={styleWithOffset}
|
||||
>
|
||||
<span {...restProps} className={badgeClassName} style={styleWithOffset}>
|
||||
<span className={statusCls} style={statusStyle} />
|
||||
<span style={{ color: statusTextColor }} className={`${prefixCls}-status-text`}>
|
||||
{text}
|
||||
@ -211,7 +186,7 @@ const Badge: React.FC<BadgeProps> = props => {
|
||||
}
|
||||
|
||||
return (
|
||||
<span {...omit(restProps, omitArr)} className={getBadgeClassName(prefixCls, direction)}>
|
||||
<span {...restProps} className={badgeClassName}>
|
||||
{children}
|
||||
<Animate
|
||||
component=""
|
||||
@ -219,21 +194,11 @@ const Badge: React.FC<BadgeProps> = props => {
|
||||
transitionName={children ? `${prefixCls}-zoom` : ''}
|
||||
transitionAppear
|
||||
>
|
||||
{scrollNumber}
|
||||
{renderBadgeNumber()}
|
||||
</Animate>
|
||||
{statusText}
|
||||
{renderStatusText()}
|
||||
</span>
|
||||
);
|
||||
};
|
||||
|
||||
return <ConfigConsumer>{renderBadge}</ConfigConsumer>;
|
||||
};
|
||||
|
||||
Badge.defaultProps = {
|
||||
count: null,
|
||||
showZero: false,
|
||||
dot: false,
|
||||
overflowCount: 99,
|
||||
};
|
||||
|
||||
export default Badge;
|
||||
|
Loading…
Reference in New Issue
Block a user