Browse Source

🔨 Refactor Tag 🔨

pull/13784/head
afc163 6 years ago
parent
commit
b828741dc0
No known key found for this signature in database GPG Key ID: 5F00908D72002306
  1. 24
      components/tag/__tests__/__snapshots__/demo.test.js.snap
  2. 23
      components/tag/__tests__/__snapshots__/index.test.js.snap
  3. 8
      components/tag/__tests__/index.test.js
  4. 145
      components/tag/index.tsx
  5. 4
      components/tag/style/index.less

24
components/tag/__tests__/__snapshots__/demo.test.js.snap

@ -4,13 +4,11 @@ exports[`renders ./components/tag/demo/basic.md correctly 1`] = `
<div>
<div
class="ant-tag"
data-show="true"
>
Tag 1
</div>
<div
class="ant-tag"
data-show="true"
>
<a
href="https://github.com/ant-design/ant-design/issues/1862"
@ -20,7 +18,6 @@ exports[`renders ./components/tag/demo/basic.md correctly 1`] = `
</div>
<div
class="ant-tag"
data-show="true"
>
Tag 2
<i
@ -43,7 +40,6 @@ exports[`renders ./components/tag/demo/basic.md correctly 1`] = `
</div>
<div
class="ant-tag"
data-show="true"
>
Prevent Default
<i
@ -97,67 +93,56 @@ exports[`renders ./components/tag/demo/colorful.md correctly 1`] = `
<div>
<div
class="ant-tag ant-tag-magenta"
data-show="true"
>
magenta
</div>
<div
class="ant-tag ant-tag-red"
data-show="true"
>
red
</div>
<div
class="ant-tag ant-tag-volcano"
data-show="true"
>
volcano
</div>
<div
class="ant-tag ant-tag-orange"
data-show="true"
>
orange
</div>
<div
class="ant-tag ant-tag-gold"
data-show="true"
>
gold
</div>
<div
class="ant-tag ant-tag-lime"
data-show="true"
>
lime
</div>
<div
class="ant-tag ant-tag-green"
data-show="true"
>
green
</div>
<div
class="ant-tag ant-tag-cyan"
data-show="true"
>
cyan
</div>
<div
class="ant-tag ant-tag-blue"
data-show="true"
>
blue
</div>
<div
class="ant-tag ant-tag-geekblue"
data-show="true"
>
geekblue
</div>
<div
class="ant-tag ant-tag-purple"
data-show="true"
>
purple
</div>
@ -170,28 +155,24 @@ exports[`renders ./components/tag/demo/colorful.md correctly 1`] = `
<div>
<div
class="ant-tag ant-tag-has-color"
data-show="true"
style="background-color:#f50"
>
#f50
</div>
<div
class="ant-tag ant-tag-has-color"
data-show="true"
style="background-color:#2db7f5"
>
#2db7f5
</div>
<div
class="ant-tag ant-tag-has-color"
data-show="true"
style="background-color:#87d068"
>
#87d068
</div>
<div
class="ant-tag ant-tag-has-color"
data-show="true"
style="background-color:#108ee9"
>
#108ee9
@ -204,13 +185,11 @@ exports[`renders ./components/tag/demo/control.md correctly 1`] = `
<div>
<div
class="ant-tag"
data-show="true"
>
Unremovable
</div>
<div
class="ant-tag"
data-show="true"
>
Tag 2
<i
@ -233,7 +212,6 @@ exports[`renders ./components/tag/demo/control.md correctly 1`] = `
</div>
<div
class="ant-tag"
data-show="true"
>
Tag 3
<i
@ -256,7 +234,6 @@ exports[`renders ./components/tag/demo/control.md correctly 1`] = `
</div>
<div
class="ant-tag"
data-show="true"
style="background:#fff;border-style:dashed"
>
<i
@ -285,7 +262,6 @@ exports[`renders ./components/tag/demo/controlled.md correctly 1`] = `
<div>
<div
class="ant-tag"
data-show="true"
>
Movies
<i

23
components/tag/__tests__/__snapshots__/index.test.js.snap

@ -1,28 +1,37 @@
// Jest Snapshot v1, https://goo.gl/fbAQLP
exports[`Tag visibility can be controlled by visible with hidden as initial value 1`] = `<span />`;
exports[`Tag visibility can be controlled by visible with hidden as initial value 1`] = `
<div
class="ant-tag ant-tag-hidden"
/>
`;
exports[`Tag visibility can be controlled by visible with hidden as initial value 2`] = `
<div
class="ant-tag"
data-show="true"
/>
`;
exports[`Tag visibility can be controlled by visible with hidden as initial value 3`] = `<span />`;
exports[`Tag visibility can be controlled by visible with hidden as initial value 3`] = `
<div
class="ant-tag ant-tag-hidden"
/>
`;
exports[`Tag visibility can be controlled by visible with visible as initial value 1`] = `
<div
class="ant-tag ant-tag-zoom-appear"
data-show="true"
class="ant-tag"
/>
`;
exports[`Tag visibility can be controlled by visible with visible as initial value 2`] = `<span />`;
exports[`Tag visibility can be controlled by visible with visible as initial value 2`] = `
<div
class="ant-tag ant-tag-hidden"
/>
`;
exports[`Tag visibility can be controlled by visible with visible as initial value 3`] = `
<div
class="ant-tag"
data-show="true"
/>
`;

8
components/tag/__tests__/index.test.js

@ -15,12 +15,12 @@ describe('Tag', () => {
const onClose = jest.fn();
const wrapper = mount(<Tag closable onClose={onClose} />);
expect(wrapper.find('.anticon-close').length).toBe(1);
expect(wrapper.find('.ant-tag').length).toBe(1);
expect(wrapper.find('div.ant-tag:not(.ant-tag-hidden)').length).toBe(1);
wrapper.find('.anticon-close').simulate('click');
expect(onClose).toBeCalled();
jest.runAllTimers();
wrapper.update();
expect(wrapper.find('.ant-tag').length).toBe(0);
expect(wrapper.find('div.ant-tag:not(.ant-tag-hidden)').length).toBe(0);
});
it('should not be closed when prevent default', () => {
@ -29,10 +29,10 @@ describe('Tag', () => {
};
const wrapper = mount(<Tag closable onClose={onClose} />);
expect(wrapper.find('.anticon-close').length).toBe(1);
expect(wrapper.find('.ant-tag').length).toBe(1);
expect(wrapper.find('div.ant-tag:not(.ant-tag-hidden)').length).toBe(1);
wrapper.find('.anticon-close').simulate('click');
jest.runAllTimers();
expect(wrapper.find('.ant-tag').length).toBe(1);
expect(wrapper.find('div.ant-tag:not(.ant-tag-hidden)').length).toBe(1);
});
describe('visibility', () => {

145
components/tag/index.tsx

@ -1,5 +1,4 @@
import * as React from 'react';
import * as ReactDOM from 'react-dom';
import Animate from 'rc-animate';
import classNames from 'classnames';
import omit from 'omit.js';
@ -14,23 +13,26 @@ export interface TagProps extends React.HTMLAttributes<HTMLDivElement> {
prefixCls?: string;
className?: string;
color?: string;
/** 标签是否可以关闭 */
closable?: boolean;
visible?: boolean;
/** 关闭时的回调 */
onClose?: Function;
/** 动画关闭后的回调 */
afterClose?: Function;
style?: React.CSSProperties;
}
export interface TagState {
closing: boolean;
closed: boolean;
interface TagState {
visible: boolean;
mounted: boolean;
}
interface InnterTagProps extends TagProps {
show: boolean;
}
const InnerTag = ({ show, ...restProps }: InnterTagProps) => {
const divProps = omit(restProps, ['onClose', 'afterClose', 'color', 'visible', 'closable']);
return <div {...divProps} />;
};
class Tag extends React.Component<TagProps, TagState> {
static CheckableTag = CheckableTag;
static defaultProps = {
@ -38,84 +40,40 @@ class Tag extends React.Component<TagProps, TagState> {
closable: false,
};
static getDerivedStateFromProps(nextProps: TagProps, state: TagState) {
static getDerivedStateFromProps(nextProps: TagProps) {
if ('visible' in nextProps) {
let newState: Partial<TagState> = {
return {
visible: nextProps.visible,
mounted: true,
};
if (!state.mounted) {
newState = {
...newState,
closed: !nextProps.visible,
};
}
return newState;
}
return null;
}
state = {
closing: false,
closed: false,
visible: true,
mounted: false,
};
componentDidUpdate(_prevProps: TagProps, prevState: TagState) {
if (prevState.visible && !this.state.visible) {
this.close();
} else if (!prevState.visible && this.state.visible) {
this.show();
}
}
handleIconClick = (e: React.MouseEvent<HTMLElement>) => {
const onClose = this.props.onClose;
setVisible(visible: boolean, e: React.MouseEvent<HTMLElement>) {
const { onClose } = this.props;
if (onClose) {
onClose(e);
}
if (e.defaultPrevented || 'visible' in this.props) {
if (e.defaultPrevented) {
return;
}
this.setState({ visible: false });
};
close = () => {
if (this.state.closing || this.state.closed) {
return;
if (!('visible' in this.props)) {
this.setState({ visible });
}
const dom = ReactDOM.findDOMNode(this) as HTMLElement;
dom.style.width = `${dom.getBoundingClientRect().width}px`;
// It's Magic Code, don't know why
dom.style.width = `${dom.getBoundingClientRect().width}px`;
this.setState({
closing: true,
});
};
}
show = () => {
this.setState({
closed: false,
});
handleIconClick = (e: React.MouseEvent<HTMLElement>) => {
this.setVisible(false, e);
};
animationEnd = (_: string, existed: boolean) => {
if (!existed && !this.state.closed) {
this.setState({
closed: true,
closing: false,
});
const afterClose = this.props.afterClose;
if (afterClose) {
afterClose();
}
} else {
this.setState({
closed: false,
});
animationEnd = () => {
const { afterClose } = this.props;
if (afterClose) {
afterClose();
}
};
@ -128,44 +86,55 @@ class Tag extends React.Component<TagProps, TagState> {
);
}
render() {
const { prefixCls, closable, color, className, children, style, ...otherProps } = this.props;
const closeIcon = closable ? <Icon type="close" onClick={this.handleIconClick} /> : '';
getTagStyle() {
const { color, style } = this.props;
const isPresetColor = this.isPresetColor(color);
const classString = classNames(
return {
backgroundColor: color && !isPresetColor ? color : undefined,
...style,
};
}
getTagClassName() {
const { prefixCls, className, color } = this.props;
const { visible } = this.state;
const isPresetColor = this.isPresetColor(color);
return classNames(
prefixCls,
{
[`${prefixCls}-${color}`]: isPresetColor,
[`${prefixCls}-has-color`]: color && !isPresetColor,
[`${prefixCls}-close`]: this.state.closing,
[`${prefixCls}-hidden`]: !visible,
},
className,
);
// fix https://fb.me/react-unknown-prop
const divProps = omit(otherProps, ['onClose', 'afterClose', 'visible']);
const tagStyle = {
backgroundColor: color && !isPresetColor ? color : null,
...style,
};
const tag = this.state.closed ? (
<span />
) : (
<div data-show={!this.state.closing} {...divProps} className={classString} style={tagStyle}>
{children}
{closeIcon}
</div>
);
}
renderCloseIcon() {
const { closable } = this.props;
return closable ? <Icon type="close" onClick={this.handleIconClick} /> : null;
}
render() {
const { prefixCls, children, ...otherProps } = this.props;
const { visible } = this.state;
return (
<Wave>
<Animate
component=""
showProp="data-show"
showProp="show"
transitionName={`${prefixCls}-zoom`}
transitionAppear
onEnd={this.animationEnd}
>
{tag}
<InnerTag
show={visible}
{...otherProps}
className={this.getTagClassName()}
style={this.getTagStyle()}
>
{children}
{this.renderCloseIcon()}
</InnerTag>
</Animate>
</Wave>
);

4
components/tag/style/index.less

@ -94,6 +94,10 @@
animation-fill-mode: both;
}
&-hidden {
display: none;
}
@colors: pink, magenta, red, volcano, orange, yellow, gold, cyan, lime, green, blue, geekblue,
purple;

Loading…
Cancel
Save