merge support prettier

This commit is contained in:
陈帅 2018-12-07 20:02:01 +08:00
parent df356a586c
commit 8e11f0bdb9
368 changed files with 8987 additions and 6409 deletions

View File

@ -1,5 +1,5 @@
const eslintrc = { const eslintrc = {
extends: ['eslint-config-airbnb'], extends: ['airbnb', 'prettier'],
env: { env: {
browser: true, browser: true,
node: true, node: true,
@ -8,18 +8,27 @@ const eslintrc = {
es6: true, es6: true,
}, },
parser: 'babel-eslint', parser: 'babel-eslint',
plugins: [ plugins: ['markdown', 'react', 'babel'],
'markdown',
'react',
'babel',
],
rules: { rules: {
'react/jsx-one-expression-per-line': 0, 'react/jsx-one-expression-per-line': 0,
'react/prop-types': 0, 'react/prop-types': 0,
'react/forbid-prop-types': 0, 'react/forbid-prop-types': 0,
'import/no-extraneous-dependencies': ['error', { 'react/jsx-indent': 0,
devDependencies: ['site/**', 'tests/**', 'scripts/**', '**/*.test.js', '**/__tests__/*', '*.config.js', '**/*.md'], 'react/jsx-wrap-multilines': ['error', { declaration: false, assignment: false }],
}], 'import/no-extraneous-dependencies': [
'error',
{
devDependencies: [
'site/**',
'tests/**',
'scripts/**',
'**/*.test.js',
'**/__tests__/*',
'*.config.js',
'**/*.md',
],
},
],
'react/jsx-filename-extension': [1, { extensions: ['.js', '.jsx', '.md'] }], 'react/jsx-filename-extension': [1, { extensions: ['.js', '.jsx', '.md'] }],
'jsx-a11y/no-static-element-interactions': 0, 'jsx-a11y/no-static-element-interactions': 0,
'jsx-a11y/anchor-has-content': 0, 'jsx-a11y/anchor-has-content': 0,

7
.prettierignore Normal file
View File

@ -0,0 +1,7 @@
**/*.md
**/*.svg
**/*.ejs
**/*.html
package.json
.umi
.umi-production

19
.prettierrc Normal file
View File

@ -0,0 +1,19 @@
{
"singleQuote": true,
"trailingComma": "all",
"printWidth": 100,
"overrides": [
{
"files": ".prettierrc",
"options": {
"parser": "json"
}
},
{
"files": ".stylelintrc",
"options": {
"parser": "json"
}
}
]
}

View File

@ -1,5 +1,5 @@
{ {
"extends": "stylelint-config-standard", "extends": ["stylelint-config-standard", "stylelint-config-prettier"],
"rules": { "rules": {
"comment-empty-line-before": null, "comment-empty-line-before": null,
"declaration-empty-line-before": null, "declaration-empty-line-before": null,

View File

@ -14,7 +14,7 @@ describe('antd', () => {
it('should hint when import all components in dev mode', () => { it('should hint when import all components in dev mode', () => {
expect(warnSpy).toBeCalledWith( expect(warnSpy).toBeCalledWith(
'You are using a whole package of antd, please use https://www.npmjs.com/package/babel-plugin-import to reduce app bundle size.' 'You are using a whole package of antd, please use https://www.npmjs.com/package/babel-plugin-import to reduce app bundle size.',
); );
warnSpy.mockRestore(); warnSpy.mockRestore();
}); });

View File

@ -88,7 +88,7 @@ describe('Test utils function', () => {
}); });
}); });
it('delayRaf', (done) => { it('delayRaf', done => {
jest.useRealTimers(); jest.useRealTimers();
let bamboo = false; let bamboo = false;
@ -118,9 +118,13 @@ describe('Test utils function', () => {
it('triggerEvent', () => { it('triggerEvent', () => {
const button = document.createElement('button'); const button = document.createElement('button');
button.addEventListener('click', () => { button.addEventListener(
button.style.width = '100px'; 'click',
}, true); () => {
button.style.width = '100px';
},
true,
);
triggerEvent(button, 'click'); triggerEvent(button, 'click');
expect(button.style.width).toBe('100px'); expect(button.style.width).toBe('100px');
}); });

View File

@ -1,10 +1,12 @@
export default function isFlexSupported() { export default function isFlexSupported() {
if (typeof window !== 'undefined' && window.document && window.document.documentElement) { if (typeof window !== 'undefined' && window.document && window.document.documentElement) {
const { documentElement } = window.document; const { documentElement } = window.document;
return 'flex' in documentElement.style || return (
'flex' in documentElement.style ||
'webkitFlex' in documentElement.style || 'webkitFlex' in documentElement.style ||
'Flex' in documentElement.style || 'Flex' in documentElement.style ||
'msFlex' in documentElement.style; 'msFlex' in documentElement.style
);
} }
return false; return false;
} }

View File

@ -1,7 +1,7 @@
import raf from 'raf'; import raf from 'raf';
interface RafMap { interface RafMap {
[id: number]: number, [id: number]: number;
} }
let id: number = 0; let id: number = 0;
@ -31,4 +31,4 @@ export default function wrapperRaf(callback: () => void, delayFrames: number = 1
wrapperRaf.cancel = function(id: number) { wrapperRaf.cancel = function(id: number) {
raf.cancel(ids[id]); raf.cancel(ids[id]);
delete ids[id]; delete ids[id];
} };

View File

@ -1,6 +1,6 @@
import warning from 'warning'; import warning from 'warning';
const warned: { [msg: string]: boolean} = {}; const warned: { [msg: string]: boolean } = {};
export default (valid: boolean, message: string): void => { export default (valid: boolean, message: string): void => {
if (!valid && !warned[message]) { if (!valid && !warned[message]) {
warning(false, message); warning(false, message);

View File

@ -13,7 +13,7 @@ function isHidden(element: HTMLElement) {
return !element || element.offsetParent === null; return !element || element.offsetParent === null;
} }
export default class Wave extends React.Component<{insertExtraNode?: boolean}> { export default class Wave extends React.Component<{ insertExtraNode?: boolean }> {
private instance?: { private instance?: {
cancel: () => void; cancel: () => void;
}; };
@ -22,7 +22,7 @@ export default class Wave extends React.Component<{insertExtraNode?: boolean}> {
private clickWaveTimeoutId: number; private clickWaveTimeoutId: number;
private animationStartId: number; private animationStartId: number;
private animationStart: boolean = false; private animationStart: boolean = false;
private destroy: boolean = false private destroy: boolean = false;
isNotGrey(color: string) { isNotGrey(color: string) {
const match = (color || '').match(/rgba?\((\d*), (\d*), (\d*)(, [\.\d]*)?\)/); const match = (color || '').match(/rgba?\((\d*), (\d*), (\d*)(, [\.\d]*)?\)/);
@ -45,15 +45,16 @@ export default class Wave extends React.Component<{insertExtraNode?: boolean}> {
node.setAttribute(attributeName, 'true'); node.setAttribute(attributeName, 'true');
// Not white or transparnt or grey // Not white or transparnt or grey
styleForPesudo = styleForPesudo || document.createElement('style'); styleForPesudo = styleForPesudo || document.createElement('style');
if (waveColor && if (
waveColor !== '#ffffff' && waveColor &&
waveColor !== 'rgb(255, 255, 255)' && waveColor !== '#ffffff' &&
this.isNotGrey(waveColor) && waveColor !== 'rgb(255, 255, 255)' &&
!/rgba\(\d*, \d*, \d*, 0\)/.test(waveColor) && // any transparent rgba color this.isNotGrey(waveColor) &&
waveColor !== 'transparent') { !/rgba\(\d*, \d*, \d*, 0\)/.test(waveColor) && // any transparent rgba color
waveColor !== 'transparent'
) {
extraNode.style.borderColor = waveColor; extraNode.style.borderColor = waveColor;
styleForPesudo.innerHTML = styleForPesudo.innerHTML = `[ant-click-animating-without-extra-node]:after { border-color: ${waveColor}; }`;
`[ant-click-animating-without-extra-node]:after { border-color: ${waveColor}; }`;
if (!document.body.contains(styleForPesudo)) { if (!document.body.contains(styleForPesudo)) {
document.body.appendChild(styleForPesudo); document.body.appendChild(styleForPesudo);
} }
@ -63,13 +64,15 @@ export default class Wave extends React.Component<{insertExtraNode?: boolean}> {
} }
TransitionEvents.addStartEventListener(node, this.onTransitionStart); TransitionEvents.addStartEventListener(node, this.onTransitionStart);
TransitionEvents.addEndEventListener(node, this.onTransitionEnd); TransitionEvents.addEndEventListener(node, this.onTransitionEnd);
} };
bindAnimationEvent = (node: HTMLElement) => { bindAnimationEvent = (node: HTMLElement) => {
if (!node || if (
!node.getAttribute || !node ||
node.getAttribute('disabled') || !node.getAttribute ||
node.className.indexOf('disabled') >= 0) { node.getAttribute('disabled') ||
node.className.indexOf('disabled') >= 0
) {
return; return;
} }
const onClick = (e: MouseEvent) => { const onClick = (e: MouseEvent) => {
@ -99,7 +102,7 @@ export default class Wave extends React.Component<{insertExtraNode?: boolean}> {
node.removeEventListener('click', onClick, true); node.removeEventListener('click', onClick, true);
}, },
}; };
} };
getAttributeName() { getAttributeName() {
const { insertExtraNode } = this.props; const { insertExtraNode } = this.props;
@ -132,14 +135,14 @@ export default class Wave extends React.Component<{insertExtraNode?: boolean}> {
if (!this.animationStart) { if (!this.animationStart) {
this.resetEffect(node); this.resetEffect(node);
} }
} };
onTransitionEnd = (e: AnimationEvent) => { onTransitionEnd = (e: AnimationEvent) => {
if (!e || e.animationName !== 'fadeEffect') { if (!e || e.animationName !== 'fadeEffect') {
return; return;
} }
this.resetEffect(e.target as HTMLElement); this.resetEffect(e.target as HTMLElement);
} };
removeExtraStyleNode() { removeExtraStyleNode() {
if (styleForPesudo) { if (styleForPesudo) {

View File

@ -12,7 +12,7 @@ class AffixMounter extends React.Component {
}); });
} }
getTarget = () => this.container getTarget = () => this.container;
render() { render() {
return ( return (
@ -21,7 +21,9 @@ class AffixMounter extends React.Component {
height: 100, height: 100,
overflowY: 'scroll', overflowY: 'scroll',
}} }}
ref={(node) => { this.container = node; }} ref={node => {
this.container = node;
}}
> >
<div <div
className="background" className="background"
@ -32,12 +34,12 @@ class AffixMounter extends React.Component {
> >
<Affix <Affix
target={() => this.container} target={() => this.container}
ref={(ele) => { this.affix = ele; }} ref={ele => {
this.affix = ele;
}}
{...this.props} {...this.props}
> >
<Button type="primary"> <Button type="primary">Fixed at the top of container</Button>
Fixed at the top of container
</Button>
</Affix> </Affix>
</div> </div>
</div> </div>
@ -56,9 +58,14 @@ describe('Affix Render', () => {
jest.useRealTimers(); jest.useRealTimers();
}); });
const scrollTo = (top) => { const scrollTo = top => {
wrapper.instance().affix.fixedNode.parentNode.getBoundingClientRect = jest.fn(() => ({ wrapper.instance().affix.fixedNode.parentNode.getBoundingClientRect = jest.fn(() => ({
bottom: 100, height: 28, left: 0, right: 0, top: 50 - top, width: 195, bottom: 100,
height: 28,
left: 0,
right: 0,
top: 50 - top,
width: 195,
})); }));
wrapper.instance().container.scrollTop = top; wrapper.instance().container.scrollTop = top;
events.scroll({ events.scroll({
@ -86,7 +93,9 @@ describe('Affix Render', () => {
it('support offsetBottom', () => { it('support offsetBottom', () => {
document.body.innerHTML = '<div id="mounter" />'; document.body.innerHTML = '<div id="mounter" />';
wrapper = mount(<AffixMounter offsetBottom={0} />, { attachTo: document.getElementById('mounter') }); wrapper = mount(<AffixMounter offsetBottom={0} />, {
attachTo: document.getElementById('mounter'),
});
jest.runAllTimers(); jest.runAllTimers();
scrollTo(0); scrollTo(0);
@ -102,7 +111,9 @@ describe('Affix Render', () => {
it('updatePosition when offsetTop changed', () => { it('updatePosition when offsetTop changed', () => {
document.body.innerHTML = '<div id="mounter" />'; document.body.innerHTML = '<div id="mounter" />';
wrapper = mount(<AffixMounter offsetTop={0} />, { attachTo: document.getElementById('mounter') }); wrapper = mount(<AffixMounter offsetTop={0} />, {
attachTo: document.getElementById('mounter'),
});
jest.runAllTimers(); jest.runAllTimers();
scrollTo(100); scrollTo(100);

View File

@ -10,9 +10,9 @@ import getScroll from '../_util/getScroll';
import { throttleByAnimationFrameDecorator } from '../_util/throttleByAnimationFrame'; import { throttleByAnimationFrameDecorator } from '../_util/throttleByAnimationFrame';
function getTargetRect(target: HTMLElement | Window | null): ClientRect { function getTargetRect(target: HTMLElement | Window | null): ClientRect {
return target !== window ? return target !== window
(target as HTMLElement).getBoundingClientRect() : ? (target as HTMLElement).getBoundingClientRect()
{ top: 0, left: 0, bottom: 0 } as ClientRect; : ({ top: 0, left: 0, bottom: 0 } as ClientRect);
} }
function getOffset(element: HTMLElement, target: HTMLElement | Window | null) { function getOffset(element: HTMLElement, target: HTMLElement | Window | null) {
@ -27,10 +27,8 @@ function getOffset(element: HTMLElement, target: HTMLElement | Window | null) {
const clientLeft = docElem.clientLeft || 0; const clientLeft = docElem.clientLeft || 0;
return { return {
top: elemRect.top - targetRect.top + top: elemRect.top - targetRect.top + scrollTop - clientTop,
scrollTop - clientTop, left: elemRect.left - targetRect.left + scrollLeft - clientLeft,
left: elemRect.left - targetRect.left +
scrollLeft - clientLeft,
width: elemRect.width, width: elemRect.width,
height: elemRect.height, height: elemRect.height,
}; };
@ -102,8 +100,7 @@ export default class Affix extends React.Component<AffixProps, AffixState> {
} }
this.setState({ affixStyle: affixStyle as React.CSSProperties }, () => { this.setState({ affixStyle: affixStyle as React.CSSProperties }, () => {
const affixed = !!this.state.affixStyle; const affixed = !!this.state.affixStyle;
if ((affixStyle && !originalAffixStyle) || if ((affixStyle && !originalAffixStyle) || (!affixStyle && originalAffixStyle)) {
(!affixStyle && originalAffixStyle)) {
onChange(affixed); onChange(affixed);
} }
}); });
@ -182,10 +179,10 @@ export default class Affix extends React.Component<AffixProps, AffixState> {
}); });
} else if ( } else if (
scrollTop < elemOffset.top + elemSize.height + (offsetBottom as number) - targetInnerHeight && scrollTop < elemOffset.top + elemSize.height + (offsetBottom as number) - targetInnerHeight &&
offsetMode.bottom offsetMode.bottom
) { ) {
// Fixed Bottom // Fixed Bottom
const targetBottomOffet = targetNode === window ? 0 : (window.innerHeight - targetRect.bottom); const targetBottomOffet = targetNode === window ? 0 : window.innerHeight - targetRect.bottom;
const width = elemOffset.width; const width = elemOffset.width;
this.setAffixStyle(e, { this.setAffixStyle(e, {
position: 'fixed', position: 'fixed',
@ -199,7 +196,12 @@ export default class Affix extends React.Component<AffixProps, AffixState> {
}); });
} else { } else {
const { affixStyle } = this.state; const { affixStyle } = this.state;
if (e.type === 'resize' && affixStyle && affixStyle.position === 'fixed' && affixNode.offsetWidth) { if (
e.type === 'resize' &&
affixStyle &&
affixStyle.position === 'fixed' &&
affixNode.offsetWidth
) {
this.setAffixStyle(e, { ...affixStyle, width: affixNode.offsetWidth }); this.setAffixStyle(e, { ...affixStyle, width: affixNode.offsetWidth });
} else { } else {
this.setAffixStyle(e, null); this.setAffixStyle(e, null);
@ -265,11 +267,11 @@ export default class Affix extends React.Component<AffixProps, AffixState> {
saveFixedNode = (node: HTMLDivElement) => { saveFixedNode = (node: HTMLDivElement) => {
this.fixedNode = node; this.fixedNode = node;
} };
savePlaceholderNode = (node: HTMLDivElement) => { savePlaceholderNode = (node: HTMLDivElement) => {
this.placeholderNode = node; this.placeholderNode = node;
} };
renderAffix = ({ getPrefixCls }: ConfigConsumerProps) => { renderAffix = ({ getPrefixCls }: ConfigConsumerProps) => {
const { prefixCls } = this.props; const { prefixCls } = this.props;
@ -277,7 +279,13 @@ export default class Affix extends React.Component<AffixProps, AffixState> {
[getPrefixCls('affix', prefixCls)]: this.state.affixStyle, [getPrefixCls('affix', prefixCls)]: this.state.affixStyle,
}); });
const props = omit(this.props, ['prefixCls', 'offsetTop', 'offsetBottom', 'target', 'onChange']); const props = omit(this.props, [
'prefixCls',
'offsetTop',
'offsetBottom',
'target',
'onChange',
]);
const placeholderStyle = { ...this.state.placeholderStyle, ...this.props.style }; const placeholderStyle = { ...this.state.placeholderStyle, ...this.props.style };
return ( return (
<div {...props} style={placeholderStyle} ref={this.savePlaceholderNode}> <div {...props} style={placeholderStyle} ref={this.savePlaceholderNode}>
@ -289,10 +297,6 @@ export default class Affix extends React.Component<AffixProps, AffixState> {
}; };
render() { render() {
return ( return <ConfigConsumer>{this.renderAffix}</ConfigConsumer>;
<ConfigConsumer>
{this.renderAffix}
</ConfigConsumer>
);
} }
} }

View File

@ -1,4 +1,4 @@
@import "../../style/themes/default"; @import '../../style/themes/default';
.@{ant-prefix}-affix { .@{ant-prefix}-affix {
position: fixed; position: fixed;

View File

@ -21,7 +21,7 @@ describe('Alert', () => {
closable closable
onClose={onClose} onClose={onClose}
afterClose={afterClose} afterClose={afterClose}
/> />,
); );
wrapper.find('.ant-alert-close-icon').simulate('click'); wrapper.find('.ant-alert-close-icon').simulate('click');
expect(onClose).toBeCalled(); expect(onClose).toBeCalled();
@ -31,26 +31,20 @@ describe('Alert', () => {
describe('data and aria props', () => { describe('data and aria props', () => {
it('sets data attributes on input', () => { it('sets data attributes on input', () => {
const wrapper = mount( const wrapper = mount(<Alert data-test="test-id" data-id="12345" />);
<Alert data-test="test-id" data-id="12345" />
);
const input = wrapper.find('.ant-alert').getDOMNode(); const input = wrapper.find('.ant-alert').getDOMNode();
expect(input.getAttribute('data-test')).toBe('test-id'); expect(input.getAttribute('data-test')).toBe('test-id');
expect(input.getAttribute('data-id')).toBe('12345'); expect(input.getAttribute('data-id')).toBe('12345');
}); });
it('sets aria attributes on input', () => { it('sets aria attributes on input', () => {
const wrapper = mount( const wrapper = mount(<Alert aria-describedby="some-label" />);
<Alert aria-describedby="some-label" />
);
const input = wrapper.find('.ant-alert').getDOMNode(); const input = wrapper.find('.ant-alert').getDOMNode();
expect(input.getAttribute('aria-describedby')).toBe('some-label'); expect(input.getAttribute('aria-describedby')).toBe('some-label');
}); });
it('sets role attribute on input', () => { it('sets role attribute on input', () => {
const wrapper = mount( const wrapper = mount(<Alert role="status" />);
<Alert role="status" />
);
const input = wrapper.find('.ant-alert').getDOMNode(); const input = wrapper.find('.ant-alert').getDOMNode();
expect(input.getAttribute('role')).toBe('status'); expect(input.getAttribute('role')).toBe('status');
}); });

View File

@ -6,7 +6,7 @@ import classNames from 'classnames';
import { ConfigConsumer, ConfigConsumerProps } from '../config-provider'; import { ConfigConsumer, ConfigConsumerProps } from '../config-provider';
import getDataOrAriaProps from '../_util/getDataOrAriaProps'; import getDataOrAriaProps from '../_util/getDataOrAriaProps';
function noop() { } function noop() {}
export interface AlertProps { export interface AlertProps {
/** /**
@ -36,8 +36,8 @@ export interface AlertProps {
} }
export interface AlertState { export interface AlertState {
closing: boolean, closing: boolean;
closed: boolean closed: boolean;
} }
export default class Alert extends React.Component<AlertProps, AlertState> { export default class Alert extends React.Component<AlertProps, AlertState> {
@ -58,7 +58,7 @@ export default class Alert extends React.Component<AlertProps, AlertState> {
closing: false, closing: false,
}); });
(this.props.onClose || noop)(e); (this.props.onClose || noop)(e);
} };
animationEnd = () => { animationEnd = () => {
this.setState({ this.setState({
@ -66,12 +66,18 @@ export default class Alert extends React.Component<AlertProps, AlertState> {
closing: true, closing: true,
}); });
(this.props.afterClose || noop)(); (this.props.afterClose || noop)();
} };
renderAlert = ({ getPrefixCls }: ConfigConsumerProps) => { renderAlert = ({ getPrefixCls }: ConfigConsumerProps) => {
const { const {
description, prefixCls: customizePrefixCls, message, closeText, banner, description,
className = '', style, icon, prefixCls: customizePrefixCls,
message,
closeText,
banner,
className = '',
style,
icon,
} = this.props; } = this.props;
let { closable, type, showIcon, iconType } = this.props; let { closable, type, showIcon, iconType } = this.props;
@ -114,13 +120,18 @@ export default class Alert extends React.Component<AlertProps, AlertState> {
closable = true; closable = true;
} }
const alertCls = classNames(prefixCls, `${prefixCls}-${type}`, { const alertCls = classNames(
[`${prefixCls}-close`]: !this.state.closing, prefixCls,
[`${prefixCls}-with-description`]: !!description, `${prefixCls}-${type}`,
[`${prefixCls}-no-icon`]: !showIcon, {
[`${prefixCls}-banner`]: !!banner, [`${prefixCls}-close`]: !this.state.closing,
[`${prefixCls}-closable`]: closable, [`${prefixCls}-with-description`]: !!description,
}, className); [`${prefixCls}-no-icon`]: !showIcon,
[`${prefixCls}-banner`]: !!banner,
[`${prefixCls}-closable`]: closable,
},
className,
);
const closeIcon = closable ? ( const closeIcon = closable ? (
<a onClick={this.handleClose} className={`${prefixCls}-close-icon`}> <a onClick={this.handleClose} className={`${prefixCls}-close-icon`}>
@ -130,19 +141,17 @@ export default class Alert extends React.Component<AlertProps, AlertState> {
const dataOrAriaProps = getDataOrAriaProps(this.props); const dataOrAriaProps = getDataOrAriaProps(this.props);
const iconNode = icon && ( const iconNode = (icon &&
React.isValidElement<{ className?: string }>(icon) (React.isValidElement<{ className?: string }>(icon) ? (
? React.cloneElement( React.cloneElement(icon, {
icon, className: classNames({
{ [icon.props.className as string]: icon.props.className,
className: classNames({ [`${prefixCls}-icon`]: true,
[icon.props.className as string]: icon.props.className, }),
[`${prefixCls}-icon`]: true, })
}), ) : (
}, <span className={`${prefixCls}-icon`}>{icon}</span>
) : <span className={`${prefixCls}-icon`}>{icon}</span>) || ( ))) || <Icon className={`${prefixCls}-icon`} type={iconType} theme={iconTheme} />;
<Icon className={`${prefixCls}-icon`} type={iconType} theme={iconTheme} />
);
return this.state.closed ? null : ( return this.state.closed ? null : (
<Animate <Animate
@ -159,13 +168,9 @@ export default class Alert extends React.Component<AlertProps, AlertState> {
</div> </div>
</Animate> </Animate>
); );
} };
render() { render() {
return ( return <ConfigConsumer>{this.renderAlert}</ConfigConsumer>;
<ConfigConsumer>
{this.renderAlert}
</ConfigConsumer>
);
} }
} }

View File

@ -1,7 +1,7 @@
@import "../../style/themes/default"; @import '../../style/themes/default';
@import "../../style/mixins/index"; @import '../../style/mixins/index';
@alert-prefix-cls: ~"@{ant-prefix}-alert"; @alert-prefix-cls: ~'@{ant-prefix}-alert';
@alert-message-color: @heading-color; @alert-message-color: @heading-color;
@alert-text-color: @text-color; @alert-text-color: @text-color;
@ -76,7 +76,7 @@
.@{iconfont-css-prefix}-close { .@{iconfont-css-prefix}-close {
color: @alert-close-color; color: @alert-close-color;
transition: color .3s; transition: color 0.3s;
&:hover { &:hover {
color: @icon-color-hover; color: @icon-color-hover;
} }
@ -131,12 +131,12 @@
margin: 0; margin: 0;
padding-top: 0; padding-top: 0;
padding-bottom: 0; padding-bottom: 0;
transition: all .3s @ease-in-out-circ; transition: all 0.3s @ease-in-out-circ;
transform-origin: 50% 0; transform-origin: 50% 0;
} }
&-slide-up-leave { &-slide-up-leave {
animation: antAlertSlideUpOut .3s @ease-in-out-circ; animation: antAlertSlideUpOut 0.3s @ease-in-out-circ;
animation-fill-mode: both; animation-fill-mode: both;
} }

View File

@ -39,17 +39,24 @@ function easeInOutCubic(t: number, b: number, c: number, d: number) {
const cc = c - b; const cc = c - b;
t /= d / 2; t /= d / 2;
if (t < 1) { if (t < 1) {
return cc / 2 * t * t * t + b; return (cc / 2) * t * t * t + b;
} }
return cc / 2 * ((t -= 2) * t * t + 2) + b; return (cc / 2) * ((t -= 2) * t * t + 2) + b;
} }
const sharpMatcherRegx = /#([^#]+)$/; const sharpMatcherRegx = /#([^#]+)$/;
function scrollTo(href: string, offsetTop = 0, getContainer: () => AnchorContainer, callback = () => { }) { function scrollTo(
href: string,
offsetTop = 0,
getContainer: () => AnchorContainer,
callback = () => {},
) {
const container = getContainer(); const container = getContainer();
const scrollTop = getScroll(container, true); const scrollTop = getScroll(container, true);
const sharpLinkMatch = sharpMatcherRegx.exec(href); const sharpLinkMatch = sharpMatcherRegx.exec(href);
if (!sharpLinkMatch) { return; } if (!sharpLinkMatch) {
return;
}
const targetElement = document.getElementById(sharpLinkMatch[1]); const targetElement = document.getElementById(sharpLinkMatch[1]);
if (!targetElement) { if (!targetElement) {
return; return;
@ -80,7 +87,7 @@ type Section = {
top: number; top: number;
}; };
export type AnchorContainer = HTMLElement | Window; export type AnchorContainer = HTMLElement | Window;
export interface AnchorProps { export interface AnchorProps {
prefixCls?: string; prefixCls?: string;
@ -92,7 +99,10 @@ export interface AnchorProps {
affix?: boolean; affix?: boolean;
showInkInFixed?: boolean; showInkInFixed?: boolean;
getContainer?: () => AnchorContainer; getContainer?: () => AnchorContainer;
onClick?: (e: React.MouseEvent<HTMLElement>, link: { title: React.ReactNode, href: string }) => void; onClick?: (
e: React.MouseEvent<HTMLElement>,
link: { title: React.ReactNode; href: string },
) => void;
} }
export interface AnchorState { export interface AnchorState {
@ -111,7 +121,10 @@ export interface AntAnchor {
unregisterLink: (link: string) => void; unregisterLink: (link: string) => void;
activeLink: string | null; activeLink: string | null;
scrollTo: (link: string) => void; scrollTo: (link: string) => void;
onClick?: (e: React.MouseEvent<HTMLElement>, link: { title: React.ReactNode, href: string }) => void; onClick?: (
e: React.MouseEvent<HTMLElement>,
link: { title: React.ReactNode; href: string },
) => void;
} }
export default class Anchor extends React.Component<AnchorProps, AnchorState> { export default class Anchor extends React.Component<AnchorProps, AnchorState> {
@ -183,7 +196,7 @@ export default class Anchor extends React.Component<AnchorProps, AnchorState> {
this.setState({ this.setState({
activeLink: this.getCurrentAnchor(offsetTop, bounds), activeLink: this.getCurrentAnchor(offsetTop, bounds),
}); });
} };
handleScrollTo = (link: string) => { handleScrollTo = (link: string) => {
const { offsetTop, getContainer } = this.props as AnchorDefaultProps; const { offsetTop, getContainer } = this.props as AnchorDefaultProps;
@ -192,7 +205,7 @@ export default class Anchor extends React.Component<AnchorProps, AnchorState> {
scrollTo(link, offsetTop, getContainer, () => { scrollTo(link, offsetTop, getContainer, () => {
this.animating = false; this.animating = false;
}); });
} };
getCurrentAnchor(offsetTop = 0, bounds = 5): string { getCurrentAnchor(offsetTop = 0, bounds = 5): string {
const activeLink = ''; const activeLink = '';
@ -205,7 +218,9 @@ export default class Anchor extends React.Component<AnchorProps, AnchorState> {
const container = getContainer(); const container = getContainer();
this.links.forEach(link => { this.links.forEach(link => {
const sharpLinkMatch = sharpMatcherRegx.exec(link.toString()); const sharpLinkMatch = sharpMatcherRegx.exec(link.toString());
if (!sharpLinkMatch) { return; } if (!sharpLinkMatch) {
return;
}
const target = document.getElementById(sharpLinkMatch[1]); const target = document.getElementById(sharpLinkMatch[1]);
if (target) { if (target) {
const top = getOffsetTop(target, container); const top = getOffsetTop(target, container);
@ -219,7 +234,7 @@ export default class Anchor extends React.Component<AnchorProps, AnchorState> {
}); });
if (linkSections.length) { if (linkSections.length) {
const maxSection = linkSections.reduce((prev, curr) => curr.top > prev.top ? curr : prev); const maxSection = linkSections.reduce((prev, curr) => (curr.top > prev.top ? curr : prev));
return maxSection.link; return maxSection.link;
} }
return ''; return '';
@ -235,11 +250,11 @@ export default class Anchor extends React.Component<AnchorProps, AnchorState> {
if (linkNode) { if (linkNode) {
this.inkNode.style.top = `${(linkNode as any).offsetTop + linkNode.clientHeight / 2 - 4.5}px`; this.inkNode.style.top = `${(linkNode as any).offsetTop + linkNode.clientHeight / 2 - 4.5}px`;
} }
} };
saveInkNode = (node: HTMLSpanElement) => { saveInkNode = (node: HTMLSpanElement) => {
this.inkNode = node; this.inkNode = node;
} };
renderAnchor = ({ getPrefixCls }: ConfigConsumerProps) => { renderAnchor = ({ getPrefixCls }: ConfigConsumerProps) => {
const { const {
@ -268,7 +283,7 @@ export default class Anchor extends React.Component<AnchorProps, AnchorState> {
const wrapperClass = classNames(className, `${prefixCls}-wrapper`); const wrapperClass = classNames(className, `${prefixCls}-wrapper`);
const anchorClass = classNames(prefixCls, { const anchorClass = classNames(prefixCls, {
'fixed': !affix && !showInkInFixed, fixed: !affix && !showInkInFixed,
}); });
const wrapperStyle = { const wrapperStyle = {
@ -277,12 +292,9 @@ export default class Anchor extends React.Component<AnchorProps, AnchorState> {
}; };
const anchorContent = ( const anchorContent = (
<div <div className={wrapperClass} style={wrapperStyle}>
className={wrapperClass}
style={wrapperStyle}
>
<div className={anchorClass}> <div className={anchorClass}>
<div className={`${prefixCls}-ink`} > <div className={`${prefixCls}-ink`}>
<span className={inkClass} ref={this.saveInkNode} /> <span className={inkClass} ref={this.saveInkNode} />
</div> </div>
{children} {children}
@ -290,18 +302,16 @@ export default class Anchor extends React.Component<AnchorProps, AnchorState> {
</div> </div>
); );
return !affix ? anchorContent : ( return !affix ? (
anchorContent
) : (
<Affix offsetTop={offsetTop} target={getContainer}> <Affix offsetTop={offsetTop} target={getContainer}>
{anchorContent} {anchorContent}
</Affix> </Affix>
); );
} };
render() { render() {
return( return <ConfigConsumer>{this.renderAnchor}</ConfigConsumer>;
<ConfigConsumer>
{this.renderAnchor}
</ConfigConsumer>
);
} }
} }

View File

@ -47,15 +47,10 @@ export default class AnchorLink extends React.Component<AnchorLinkProps, any> {
onClick(e, { title, href }); onClick(e, { title, href });
} }
scrollTo(href); scrollTo(href);
} };
renderAnchorLink = ({ getPrefixCls }: ConfigConsumerProps) => { renderAnchorLink = ({ getPrefixCls }: ConfigConsumerProps) => {
const { const { prefixCls: customizePrefixCls, href, title, children } = this.props;
prefixCls: customizePrefixCls,
href,
title,
children,
} = this.props;
const prefixCls = getPrefixCls('anchor', customizePrefixCls); const prefixCls = getPrefixCls('anchor', customizePrefixCls);
const active = this.context.antAnchor.activeLink === href; const active = this.context.antAnchor.activeLink === href;
const wrapperClassName = classNames(`${prefixCls}-link`, { const wrapperClassName = classNames(`${prefixCls}-link`, {
@ -77,13 +72,9 @@ export default class AnchorLink extends React.Component<AnchorLinkProps, any> {
{children} {children}
</div> </div>
); );
} };
render() { render() {
return ( return <ConfigConsumer>{this.renderAnchorLink}</ConfigConsumer>;
<ConfigConsumer>
{this.renderAnchorLink}
</ConfigConsumer>
);
} }
} }

View File

@ -9,7 +9,7 @@ describe('Anchor Render', () => {
const wrapper = mount( const wrapper = mount(
<Anchor> <Anchor>
<Link href="#API" title="API" /> <Link href="#API" title="API" />
</Anchor> </Anchor>,
); );
wrapper.find('a[href="#API"]').simulate('click'); wrapper.find('a[href="#API"]').simulate('click');
@ -22,7 +22,7 @@ describe('Anchor Render', () => {
const wrapper = mount( const wrapper = mount(
<Anchor> <Anchor>
<Link href="http://www.example.com/#API" title="API" /> <Link href="http://www.example.com/#API" title="API" />
</Anchor> </Anchor>,
); );
wrapper.find('a[href="http://www.example.com/#API"]').simulate('click'); wrapper.find('a[href="http://www.example.com/#API"]').simulate('click');
expect(wrapper.instance().state.activeLink).toBe('http://www.example.com/#API'); expect(wrapper.instance().state.activeLink).toBe('http://www.example.com/#API');
@ -39,7 +39,7 @@ describe('Anchor Render', () => {
const wrapper = mount( const wrapper = mount(
<Anchor> <Anchor>
<Link href="http://www.example.com/#API" title="API" /> <Link href="http://www.example.com/#API" title="API" />
</Anchor> </Anchor>,
); );
wrapper.instance().handleScroll(); wrapper.instance().handleScroll();
expect(wrapper.instance().state.activeLink).toBe('http://www.example.com/#API'); expect(wrapper.instance().state.activeLink).toBe('http://www.example.com/#API');
@ -57,7 +57,7 @@ describe('Anchor Render', () => {
const wrapper = mount( const wrapper = mount(
<Anchor> <Anchor>
<Link href="##API" title="API" /> <Link href="##API" title="API" />
</Anchor> </Anchor>,
); );
wrapper.instance().handleScrollTo('##API'); wrapper.instance().handleScrollTo('##API');
expect(wrapper.instance().state.activeLink).toBe('##API'); expect(wrapper.instance().state.activeLink).toBe('##API');
@ -70,7 +70,7 @@ describe('Anchor Render', () => {
const wrapper = mount( const wrapper = mount(
<Anchor> <Anchor>
<Link href="#API" title="API" /> <Link href="#API" title="API" />
</Anchor> </Anchor>,
); );
const removeListenerSpy = jest.spyOn(wrapper.instance().scrollEvent, 'remove'); const removeListenerSpy = jest.spyOn(wrapper.instance().scrollEvent, 'remove');
wrapper.unmount(); wrapper.unmount();
@ -81,7 +81,7 @@ describe('Anchor Render', () => {
const wrapper = mount( const wrapper = mount(
<Anchor> <Anchor>
<Link href="#API" title="API" /> <Link href="#API" title="API" />
</Anchor> </Anchor>,
); );
expect(wrapper.instance().links).toEqual(['#API']); expect(wrapper.instance().links).toEqual(['#API']);
wrapper.setProps({ children: null }); wrapper.setProps({ children: null });
@ -92,7 +92,11 @@ describe('Anchor Render', () => {
let anchorInstance = null; let anchorInstance = null;
function AnchorUpdate({ href }) { function AnchorUpdate({ href }) {
return ( return (
<Anchor ref={(c) => { anchorInstance = c; }}> <Anchor
ref={c => {
anchorInstance = c;
}}
>
<Link href={href} title="API" /> <Link href={href} title="API" />
</Anchor> </Anchor>
); );
@ -107,7 +111,9 @@ describe('Anchor Render', () => {
it('Anchor onClick event', () => { it('Anchor onClick event', () => {
let event; let event;
let link; let link;
const handleClick = (...arg) => { [event, link] = arg; }; const handleClick = (...arg) => {
[event, link] = arg;
};
const href = '#API'; const href = '#API';
const title = 'API'; const title = 'API';
@ -115,7 +121,7 @@ describe('Anchor Render', () => {
const wrapper = mount( const wrapper = mount(
<Anchor onClick={handleClick}> <Anchor onClick={handleClick}>
<Link href={href} title={title} /> <Link href={href} title={title} />
</Anchor> </Anchor>,
); );
wrapper.find(`a[href="${href}"]`).simulate('click'); wrapper.find(`a[href="${href}"]`).simulate('click');

View File

@ -1,5 +1,5 @@
@import "../../style/themes/default"; @import '../../style/themes/default';
@import "../../style/mixins/index"; @import '../../style/mixins/index';
@anchor-border-width: 2px; @anchor-border-width: 2px;
@ -38,7 +38,7 @@
border: 2px solid @primary-color; border: 2px solid @primary-color;
background-color: @component-background; background-color: @component-background;
left: 50%; left: 50%;
transition: top .3s ease-in-out; transition: top 0.3s ease-in-out;
transform: translateX(-50%); transform: translateX(-50%);
&.visible { &.visible {
display: inline-block; display: inline-block;
@ -57,7 +57,7 @@
&-title { &-title {
display: block; display: block;
position: relative; position: relative;
transition: all .3s; transition: all 0.3s;
color: @text-color; color: @text-color;
white-space: nowrap; white-space: nowrap;
overflow: hidden; overflow: hidden;

View File

@ -9,22 +9,28 @@ export default class InputElement extends React.Component<InputElementProps, any
private ele: HTMLInputElement; private ele: HTMLInputElement;
focus = () => { focus = () => {
this.ele.focus ? this.ele.focus() : (ReactDOM.findDOMNode(this.ele) as HTMLInputElement).focus(); this.ele.focus
} ? this.ele.focus()
: (ReactDOM.findDOMNode(this.ele) as HTMLInputElement).focus();
};
blur = () => { blur = () => {
this.ele.blur ? this.ele.blur() : (ReactDOM.findDOMNode(this.ele) as HTMLInputElement).blur(); this.ele.blur ? this.ele.blur() : (ReactDOM.findDOMNode(this.ele) as HTMLInputElement).blur();
} };
saveRef = (ele: HTMLInputElement) => { saveRef = (ele: HTMLInputElement) => {
this.ele = ele; this.ele = ele;
const { ref: childRef } = this.props.children as any; const { ref: childRef } = this.props.children as any;
if (typeof childRef === 'function') { if (typeof childRef === 'function') {
childRef(ele); childRef(ele);
} }
} };
render() { render() {
return React.cloneElement(this.props.children, { return React.cloneElement(
...this.props, this.props.children,
ref: this.saveRef, {
}, null); ...this.props,
ref: this.saveRef,
},
null,
);
} }
} }

View File

@ -10,12 +10,17 @@ describe('AutoComplete with Custom Input Element Render', () => {
const wrapper = mount( const wrapper = mount(
<AutoComplete dataSource={['12345', '23456', '34567']}> <AutoComplete dataSource={['12345', '23456', '34567']}>
<textarea /> <textarea />
</AutoComplete> </AutoComplete>,
); );
expect(wrapper.find('textarea').length).toBe(1); expect(wrapper.find('textarea').length).toBe(1);
wrapper.find('textarea').simulate('change', { target: { value: '123' } }); wrapper.find('textarea').simulate('change', { target: { value: '123' } });
const dropdownWrapper = mount(wrapper.find('Trigger').instance().getComponent()); const dropdownWrapper = mount(
wrapper
.find('Trigger')
.instance()
.getComponent(),
);
// should not filter data source defaultly // should not filter data source defaultly
expect(dropdownWrapper.find('MenuItem').length).toBe(3); expect(dropdownWrapper.find('MenuItem').length).toBe(3);
@ -26,7 +31,7 @@ describe('AutoComplete with Custom Input Element Render', () => {
mount( mount(
<AutoComplete dataSource={[]}> <AutoComplete dataSource={[]}>
<input ref={mockRef} /> <input ref={mockRef} />
</AutoComplete> </AutoComplete>,
); );
expect(mockRef).toHaveBeenCalled(); expect(mockRef).toHaveBeenCalled();
}); });

View File

@ -6,12 +6,15 @@ import Input from '../input';
import Select, { AbstractSelectProps, SelectValue, OptionProps, OptGroupProps } from '../select'; import Select, { AbstractSelectProps, SelectValue, OptionProps, OptGroupProps } from '../select';
import { ConfigConsumer, ConfigConsumerProps } from '../config-provider'; import { ConfigConsumer, ConfigConsumerProps } from '../config-provider';
export interface DataSourceItemObject { value: string; text: string; } export interface DataSourceItemObject {
value: string;
text: string;
}
export type DataSourceItemType = export type DataSourceItemType =
string | | string
DataSourceItemObject | | DataSourceItemObject
React.ReactElement<OptionProps> | | React.ReactElement<OptionProps>
React.ReactElement<OptGroupProps>; | React.ReactElement<OptGroupProps>;
export interface AutoCompleteInputProps { export interface AutoCompleteInputProps {
onChange?: React.FormEventHandler<any>; onChange?: React.FormEventHandler<any>;
@ -19,9 +22,9 @@ export interface AutoCompleteInputProps {
} }
export type ValidInputElement = export type ValidInputElement =
HTMLInputElement | | HTMLInputElement
HTMLTextAreaElement | | HTMLTextAreaElement
React.ReactElement<AutoCompleteInputProps>; | React.ReactElement<AutoCompleteInputProps>;
export interface AutoCompleteProps extends AbstractSelectProps { export interface AutoCompleteProps extends AbstractSelectProps {
value?: SelectValue; value?: SelectValue;
@ -34,9 +37,10 @@ export interface AutoCompleteProps extends AbstractSelectProps {
onSelect?: (value: SelectValue, option: Object) => any; onSelect?: (value: SelectValue, option: Object) => any;
onBlur?: (value: SelectValue) => void; onBlur?: (value: SelectValue) => void;
onFocus?: () => void; onFocus?: () => void;
children?: ValidInputElement | children?:
React.ReactElement<OptionProps> | | ValidInputElement
Array<React.ReactElement<OptionProps>>; | React.ReactElement<OptionProps>
| Array<React.ReactElement<OptionProps>>;
} }
function isSelectOptionOrSelectOptGroup(child: any): Boolean { function isSelectOptionOrSelectOptGroup(child: any): Boolean {
@ -59,15 +63,17 @@ export default class AutoComplete extends React.Component<AutoCompleteProps, {}>
getInputElement = () => { getInputElement = () => {
const { children } = this.props; const { children } = this.props;
const element = children && React.isValidElement(children) && children.type !== Option ? const element =
React.Children.only(this.props.children) : <Input />; children && React.isValidElement(children) && children.type !== Option ? (
React.Children.only(this.props.children)
) : (
<Input />
);
const elementProps = { ...element.props }; const elementProps = { ...element.props };
// https://github.com/ant-design/ant-design/pull/7742 // https://github.com/ant-design/ant-design/pull/7742
delete elementProps.children; delete elementProps.children;
return ( return <InputElement {...elementProps}>{element}</InputElement>;
<InputElement {...elementProps}>{element}</InputElement> };
);
}
focus() { focus() {
this.select.focus(); this.select.focus();
@ -79,12 +85,17 @@ export default class AutoComplete extends React.Component<AutoCompleteProps, {}>
saveSelect = (node: any) => { saveSelect = (node: any) => {
this.select = node; this.select = node;
} };
renderAutoComplete = ({ getPrefixCls }: ConfigConsumerProps) => { renderAutoComplete = ({ getPrefixCls }: ConfigConsumerProps) => {
const { const {
prefixCls: customizePrefixCls, prefixCls: customizePrefixCls,
size, className = '', notFoundContent, optionLabelProp, dataSource, children, size,
className = '',
notFoundContent,
optionLabelProp,
dataSource,
children,
} = this.props; } = this.props;
const prefixCls = getPrefixCls('select', customizePrefixCls); const prefixCls = getPrefixCls('select', customizePrefixCls);
@ -98,28 +109,30 @@ export default class AutoComplete extends React.Component<AutoCompleteProps, {}>
let options; let options;
const childArray = React.Children.toArray(children); const childArray = React.Children.toArray(children);
if (childArray.length && if (childArray.length && isSelectOptionOrSelectOptGroup(childArray[0])) {
isSelectOptionOrSelectOptGroup(childArray[0])
) {
options = children; options = children;
} else { } else {
options = dataSource ? dataSource.map((item) => { options = dataSource
if (React.isValidElement(item)) { ? dataSource.map(item => {
return item; if (React.isValidElement(item)) {
} return item;
switch (typeof item) { }
case 'string': switch (typeof item) {
return <Option key={item}>{item}</Option>; case 'string':
case 'object': return <Option key={item}>{item}</Option>;
return ( case 'object':
<Option key={(item as DataSourceItemObject).value}> return (
{(item as DataSourceItemObject).text} <Option key={(item as DataSourceItemObject).value}>
</Option> {(item as DataSourceItemObject).text}
); </Option>
default: );
throw new Error('AutoComplete[dataSource] only supports type `string[] | Object[]`.'); default:
} throw new Error(
}) : []; 'AutoComplete[dataSource] only supports type `string[] | Object[]`.',
);
}
})
: [];
} }
return ( return (
@ -135,13 +148,9 @@ export default class AutoComplete extends React.Component<AutoCompleteProps, {}>
{options} {options}
</Select> </Select>
); );
} };
render() { render() {
return ( return <ConfigConsumer>{this.renderAutoComplete}</ConfigConsumer>;
<ConfigConsumer>
{this.renderAutoComplete}
</ConfigConsumer>
);
} }
} }

View File

@ -1,10 +1,10 @@
@import "../../style/themes/default"; @import '../../style/themes/default';
@import "../../style/mixins/index"; @import '../../style/mixins/index';
@import "../../input/style/mixin"; @import '../../input/style/mixin';
@input-prefix-cls: ~"@{ant-prefix}-input"; @input-prefix-cls: ~'@{ant-prefix}-input';
@select-prefix-cls: ~"@{ant-prefix}-select"; @select-prefix-cls: ~'@{ant-prefix}-select';
@autocomplete-prefix-cls: ~"@{select-prefix-cls}-auto-complete"; @autocomplete-prefix-cls: ~'@{select-prefix-cls}-auto-complete';
.@{autocomplete-prefix-cls} { .@{autocomplete-prefix-cls} {
.reset-component; .reset-component;

View File

@ -38,14 +38,14 @@ describe('Avatar Render', () => {
class Foo extends React.Component { class Foo extends React.Component {
state = { state = {
src: LOAD_FAILURE_SRC, src: LOAD_FAILURE_SRC,
} };
handleImgError = () => { handleImgError = () => {
this.setState({ this.setState({
src: LOAD_SUCCESS_SRC, src: LOAD_SUCCESS_SRC,
}); });
return false; return false;
} };
render() { render() {
const { src } = this.state; const { src } = this.state;

View File

@ -51,9 +51,11 @@ export default class Avatar extends React.Component<AvatarProps, AvatarState> {
} }
componentDidUpdate(prevProps: AvatarProps, prevState: AvatarState) { componentDidUpdate(prevProps: AvatarProps, prevState: AvatarState) {
if (prevProps.children !== this.props.children if (
|| (prevState.scale !== this.state.scale && this.state.scale === 1) prevProps.children !== this.props.children ||
|| (prevState.isImgExist !== this.state.isImgExist)) { (prevState.scale !== this.state.scale && this.state.scale === 1) ||
prevState.isImgExist !== this.state.isImgExist
) {
this.setScale(); this.setScale();
} }
} }
@ -75,7 +77,7 @@ export default class Avatar extends React.Component<AvatarProps, AvatarState> {
}); });
} }
} }
} };
handleImgLoadError = () => { handleImgLoadError = () => {
const { onError } = this.props; const { onError } = this.props;
@ -83,12 +85,19 @@ export default class Avatar extends React.Component<AvatarProps, AvatarState> {
if (errorFlag !== false) { if (errorFlag !== false) {
this.setState({ isImgExist: false }); this.setState({ isImgExist: false });
} }
} };
renderAvatar = ({ getPrefixCls }: ConfigConsumerProps) => { renderAvatar = ({ getPrefixCls }: ConfigConsumerProps) => {
const { const {
prefixCls: customizePrefixCls, prefixCls: customizePrefixCls,
shape, size, src, srcSet, icon, className, alt, ...others shape,
size,
src,
srcSet,
icon,
className,
alt,
...others
} = this.props; } = this.props;
const { isImgExist, scale } = this.state; const { isImgExist, scale } = this.state;
@ -106,23 +115,19 @@ export default class Avatar extends React.Component<AvatarProps, AvatarState> {
[`${prefixCls}-icon`]: icon, [`${prefixCls}-icon`]: icon,
}); });
const sizeStyle: React.CSSProperties = typeof size === 'number' ? { const sizeStyle: React.CSSProperties =
width: size, typeof size === 'number'
height: size, ? {
lineHeight: `${size}px`, width: size,
fontSize: icon ? size / 2 : 18, height: size,
} : {}; lineHeight: `${size}px`,
fontSize: icon ? size / 2 : 18,
}
: {};
let children = this.props.children; let children = this.props.children;
if (src && isImgExist) { if (src && isImgExist) {
children = ( children = <img src={src} srcSet={srcSet} onError={this.handleImgLoadError} alt={alt} />;
<img
src={src}
srcSet={srcSet}
onError={this.handleImgLoadError}
alt={alt}
/>
);
} else if (icon) { } else if (icon) {
children = <Icon type={icon} />; children = <Icon type={icon} />;
} else { } else {
@ -135,13 +140,15 @@ export default class Avatar extends React.Component<AvatarProps, AvatarState> {
transform: transformString, transform: transformString,
}; };
const sizeChildrenStyle: React.CSSProperties = const sizeChildrenStyle: React.CSSProperties =
typeof size === 'number' ? { typeof size === 'number'
lineHeight: `${size}px`, ? {
} : {}; lineHeight: `${size}px`,
}
: {};
children = ( children = (
<span <span
className={`${prefixCls}-string`} className={`${prefixCls}-string`}
ref={span => this.avatarChildren = span} ref={span => (this.avatarChildren = span)}
style={{ ...sizeChildrenStyle, ...childrenStyle }} style={{ ...sizeChildrenStyle, ...childrenStyle }}
> >
{children} {children}
@ -149,31 +156,20 @@ export default class Avatar extends React.Component<AvatarProps, AvatarState> {
); );
} else { } else {
children = ( children = (
<span <span className={`${prefixCls}-string`} ref={span => (this.avatarChildren = span)}>
className={`${prefixCls}-string`}
ref={span => this.avatarChildren = span}
>
{children} {children}
</span> </span>
); );
} }
} }
return ( return (
<span <span {...others} style={{ ...sizeStyle, ...others.style }} className={classString}>
{...others}
style={{ ...sizeStyle, ...others.style }}
className={classString}
>
{children} {children}
</span> </span>
); );
} };
render() { render() {
return ( return <ConfigConsumer>{this.renderAvatar}</ConfigConsumer>;
<ConfigConsumer>
{this.renderAvatar}
</ConfigConsumer>
);
} }
} }

View File

@ -1,7 +1,7 @@
@import "../../style/themes/default"; @import '../../style/themes/default';
@import "../../style/mixins/index"; @import '../../style/mixins/index';
@avatar-prefix-cls: ~"@{ant-prefix}-avatar"; @avatar-prefix-cls: ~'@{ant-prefix}-avatar';
.@{avatar-prefix-cls} { .@{avatar-prefix-cls} {
.reset-component; .reset-component;

View File

@ -11,13 +11,13 @@ const easeInOutCubic = (t: number, b: number, c: number, d: number) => {
const cc = c - b; const cc = c - b;
t /= d / 2; t /= d / 2;
if (t < 1) { if (t < 1) {
return cc / 2 * t * t * t + b; return (cc / 2) * t * t * t + b;
} else { } else {
return cc / 2 * ((t -= 2) * t * t + 2) + b; return (cc / 2) * ((t -= 2) * t * t + 2) + b;
} }
}; };
function noop() { } function noop() {}
function getDefaultTarget() { function getDefaultTarget() {
return window; return window;
@ -54,7 +54,7 @@ export default class BackTop extends React.Component<BackTopProps, any> {
return window.pageYOffset || document.body.scrollTop || document.documentElement!.scrollTop; return window.pageYOffset || document.body.scrollTop || document.documentElement!.scrollTop;
} }
return (targetNode as HTMLElement).scrollTop; return (targetNode as HTMLElement).scrollTop;
} };
scrollToTop = (e: React.MouseEvent<HTMLDivElement>) => { scrollToTop = (e: React.MouseEvent<HTMLDivElement>) => {
const scrollTop = this.getCurrentScrollTop(); const scrollTop = this.getCurrentScrollTop();
@ -71,7 +71,7 @@ export default class BackTop extends React.Component<BackTopProps, any> {
}; };
raf(frameFunc); raf(frameFunc);
(this.props.onClick || noop)(e); (this.props.onClick || noop)(e);
} };
setScrollTop(value: number) { setScrollTop(value: number) {
const getTarget = this.props.target || getDefaultTarget; const getTarget = this.props.target || getDefaultTarget;
@ -90,7 +90,7 @@ export default class BackTop extends React.Component<BackTopProps, any> {
this.setState({ this.setState({
visible: scrollTop > (visibilityHeight as number), visible: scrollTop > (visibilityHeight as number),
}); });
} };
componentDidMount() { componentDidMount() {
const getTarget = this.props.target || getDefaultTarget; const getTarget = this.props.target || getDefaultTarget;
@ -138,13 +138,9 @@ export default class BackTop extends React.Component<BackTopProps, any> {
{backTopBtn} {backTopBtn}
</Animate> </Animate>
); );
} };
render() { render() {
return ( return <ConfigConsumer>{this.renderBackTop}</ConfigConsumer>;
<ConfigConsumer>
{this.renderBackTop}
</ConfigConsumer>
);
} }
} }

View File

@ -1,7 +1,7 @@
@import "../../style/themes/default"; @import '../../style/themes/default';
@import "../../style/mixins/index"; @import '../../style/mixins/index';
@backtop-prefix-cls: ~"@{ant-prefix}-back-top"; @backtop-prefix-cls: ~'@{ant-prefix}-back-top';
.@{backtop-prefix-cls} { .@{backtop-prefix-cls} {
.reset-component; .reset-component;
@ -20,12 +20,12 @@
background-color: @back-top-bg; background-color: @back-top-bg;
color: @back-top-color; color: @back-top-color;
text-align: center; text-align: center;
transition: all .3s @ease-in-out; transition: all 0.3s @ease-in-out;
overflow: hidden; overflow: hidden;
&:hover { &:hover {
background-color: @back-top-hover-bg; background-color: @back-top-hover-bg;
transition: all .3s @ease-in-out; transition: all 0.3s @ease-in-out;
} }
} }
@ -33,7 +33,8 @@
margin: 12px auto; margin: 12px auto;
width: 14px; width: 14px;
height: 16px; height: 16px;
background: url(data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAACQAAAAoCAYAAACWwljjAAAABGdBTUEAALGPC/xhBQAAAbtJREFUWAntmMtKw0AUhhMvS5cuxILgQlRUpIggIoKIIoigG1eC+AA+jo+i6FIXBfeuXIgoeKVeitVWJX5HWhhDksnUpp3FDPyZk3Nm5nycmZKkXhAEOXSA3lG7muTeRzmfy6HneUvIhnYkQK+Q9NhAA0Opg0vBEhjBKHiyb8iGMyQMOYuK41BcBSypAL+MYXSKjtFAW7EAGEO3qN4uMQbbAkXiSfRQJ1H6a+yhlkKRcAoVFYiweYNjtCVQJJpBz2GCiPt7fBOZQpFgDpUikse5HgnkM4Fi4QX0Fpc5wf9EbLqpUCy4jMoJSXWhFwbMNgWKhVbRhy5jirhs9fy/oFhgHVVTJEs7RLZ8sSEoJm6iz7SZDMbJ+/OKERQTttCXQRLToRUmrKWCYuA2+jbN0MB4OQobYShfdTCgn/sL1K36M7TLrN3n+758aPy2rrpR6+/od5E8tf/A1uLS9aId5T7J3CNYihkQ4D9PiMdMC7mp4rjB9kjFjZp8BlnVHJBuO1yFXIV0FdDF3RlyFdJVQBdv5AxVdIsq8apiZ2PyYO1EVykesGfZEESsCkweyR8MUW+V8uJ1gkYipmpdP1pm2aJVPEGzAAAAAElFTkSuQmCC) ~"100%/100%" no-repeat; background: url(data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAACQAAAAoCAYAAACWwljjAAAABGdBTUEAALGPC/xhBQAAAbtJREFUWAntmMtKw0AUhhMvS5cuxILgQlRUpIggIoKIIoigG1eC+AA+jo+i6FIXBfeuXIgoeKVeitVWJX5HWhhDksnUpp3FDPyZk3Nm5nycmZKkXhAEOXSA3lG7muTeRzmfy6HneUvIhnYkQK+Q9NhAA0Opg0vBEhjBKHiyb8iGMyQMOYuK41BcBSypAL+MYXSKjtFAW7EAGEO3qN4uMQbbAkXiSfRQJ1H6a+yhlkKRcAoVFYiweYNjtCVQJJpBz2GCiPt7fBOZQpFgDpUikse5HgnkM4Fi4QX0Fpc5wf9EbLqpUCy4jMoJSXWhFwbMNgWKhVbRhy5jirhs9fy/oFhgHVVTJEs7RLZ8sSEoJm6iz7SZDMbJ+/OKERQTttCXQRLToRUmrKWCYuA2+jbN0MB4OQobYShfdTCgn/sL1K36M7TLrN3n+758aPy2rrpR6+/od5E8tf/A1uLS9aId5T7J3CNYihkQ4D9PiMdMC7mp4rjB9kjFjZp8BlnVHJBuO1yFXIV0FdDF3RlyFdJVQBdv5AxVdIsq8apiZ2PyYO1EVykesGfZEESsCkweyR8MUW+V8uJ1gkYipmpdP1pm2aJVPEGzAAAAAElFTkSuQmCC)
~'100%/100%' no-repeat;
} }
} }

View File

@ -5,11 +5,13 @@ import classNames from 'classnames';
import { ConfigConsumer, ConfigConsumerProps } from '../config-provider'; import { ConfigConsumer, ConfigConsumerProps } from '../config-provider';
function getNumberArray(num: string | number | undefined | null) { function getNumberArray(num: string | number | undefined | null) {
return num ? return num
num.toString() ? num
.split('') .toString()
.reverse() .split('')
.map(i => Number(i)) : []; .reverse()
.map(i => Number(i))
: [];
} }
export interface ScrollNumberProps { export interface ScrollNumberProps {
@ -31,8 +33,7 @@ export interface ScrollNumberState {
export default class ScrollNumber extends Component<ScrollNumberProps, ScrollNumberState> { export default class ScrollNumber extends Component<ScrollNumberProps, ScrollNumberState> {
static defaultProps = { static defaultProps = {
count: null, count: null,
onAnimated() { onAnimated() {},
},
}; };
lastCount: any; lastCount: any;
@ -71,49 +72,63 @@ export default class ScrollNumber extends Component<ScrollNumberProps, ScrollNum
} }
this.lastCount = this.state.count; this.lastCount = this.state.count;
// 复原数字初始位置 // 复原数字初始位置
this.setState({ this.setState(
animateStarted: true, {
}, () => { animateStarted: true,
// 等待数字位置复原完毕 },
// 开始设置完整的数字 () => {
setTimeout(() => { // 等待数字位置复原完毕
this.setState({ // 开始设置完整的数字
animateStarted: false, setTimeout(() => {
count: nextProps.count, this.setState(
}, () => { {
const onAnimated = this.props.onAnimated; animateStarted: false,
if (onAnimated) { count: nextProps.count,
onAnimated(); },
} () => {
}); const onAnimated = this.props.onAnimated;
}, 5); if (onAnimated) {
}); onAnimated();
}
},
);
}, 5);
},
);
} }
} }
renderNumberList(position: number) { renderNumberList(position: number) {
const childrenToReturn: React.ReactElement<any>[] = []; const childrenToReturn: React.ReactElement<any>[] = [];
for (let i = 0; i < 30; i++) { for (let i = 0; i < 30; i++) {
const currentClassName = (position === i) ? 'current' : ''; const currentClassName = position === i ? 'current' : '';
childrenToReturn.push(<p key={i.toString()} className={currentClassName}>{i % 10}</p>); childrenToReturn.push(
<p key={i.toString()} className={currentClassName}>
{i % 10}
</p>,
);
} }
return childrenToReturn; return childrenToReturn;
} }
renderCurrentNumber(prefixCls: string, num: number, i: number) { renderCurrentNumber(prefixCls: string, num: number, i: number) {
const position = this.getPositionByNum(num, i); const position = this.getPositionByNum(num, i);
const removeTransition = this.state.animateStarted || const removeTransition =
(getNumberArray(this.lastCount)[i] === undefined); this.state.animateStarted || getNumberArray(this.lastCount)[i] === undefined;
return createElement('span', { return createElement(
className: `${prefixCls}-only`, 'span',
style: { {
transition: removeTransition ? 'none' : undefined, className: `${prefixCls}-only`,
msTransform: `translateY(${-position * 100}%)`, style: {
WebkitTransform: `translateY(${-position * 100}%)`, transition: removeTransition ? 'none' : undefined,
transform: `translateY(${-position * 100}%)`, msTransform: `translateY(${-position * 100}%)`,
WebkitTransform: `translateY(${-position * 100}%)`,
transform: `translateY(${-position * 100}%)`,
},
key: i,
}, },
key: i, this.renderNumberList(position),
}, this.renderNumberList(position)); );
} }
renderNumberElement(prefixCls: string) { renderNumberElement(prefixCls: string) {
@ -122,13 +137,18 @@ export default class ScrollNumber extends Component<ScrollNumberProps, ScrollNum
return count; return count;
} }
return getNumberArray(count) return getNumberArray(count)
.map((num, i) => this.renderCurrentNumber(prefixCls, num, i)).reverse(); .map((num, i) => this.renderCurrentNumber(prefixCls, num, i))
.reverse();
} }
renderScrollNumber = ({ getPrefixCls }: ConfigConsumerProps) => { renderScrollNumber = ({ getPrefixCls }: ConfigConsumerProps) => {
const { const {
prefixCls: customizePrefixCls, prefixCls: customizePrefixCls,
className, style, title, component = 'sup', displayComponent, className,
style,
title,
component = 'sup',
displayComponent,
} = this.props; } = this.props;
// fix https://fb.me/react-unknown-prop // fix https://fb.me/react-unknown-prop
const restProps = omit(this.props, [ const restProps = omit(this.props, [
@ -156,18 +176,10 @@ export default class ScrollNumber extends Component<ScrollNumberProps, ScrollNum
className: `${prefixCls}-custom-component`, className: `${prefixCls}-custom-component`,
}); });
} }
return createElement( return createElement(component as any, newProps, this.renderNumberElement(prefixCls));
component as any, };
newProps,
this.renderNumberElement(prefixCls),
);
}
render() { render() {
return ( return <ConfigConsumer>{this.renderScrollNumber}</ConfigConsumer>;
<ConfigConsumer>
{this.renderScrollNumber}
</ConfigConsumer>
);
} }
} }

View File

@ -24,7 +24,12 @@ describe('Badge', () => {
it('should have an overriden title attribute', () => { it('should have an overriden title attribute', () => {
const badge = mount(<Badge count={10} title="Custom title" />); const badge = mount(<Badge count={10} title="Custom title" />);
expect(badge.find('.ant-scroll-number').getDOMNode().attributes.getNamedItem('title').value).toEqual('Custom title'); expect(
badge
.find('.ant-scroll-number')
.getDOMNode()
.attributes.getNamedItem('title').value,
).toEqual('Custom title');
}); });
// https://github.com/ant-design/ant-design/issues/10626 // https://github.com/ant-design/ant-design/issues/10626
@ -32,7 +37,7 @@ describe('Badge', () => {
const wrapper = mount( const wrapper = mount(
<Tooltip title="Fix the error"> <Tooltip title="Fix the error">
<Badge status="error" /> <Badge status="error" />
</Tooltip> </Tooltip>,
); );
wrapper.find('Badge').simulate('mouseenter'); wrapper.find('Badge').simulate('mouseenter');
jest.runAllTimers(); jest.runAllTimers();
@ -59,7 +64,12 @@ describe('Badge', () => {
}); });
it('should be compatible with borderColor style', () => { it('should be compatible with borderColor style', () => {
const wrapper = render(<Badge count={4} style={{ backgroundColor: '#fff', color: '#999', borderColor: '#d9d9d9' }} />); const wrapper = render(
<Badge
count={4}
style={{ backgroundColor: '#fff', color: '#999', borderColor: '#d9d9d9' }}
/>,
);
expect(wrapper).toMatchSnapshot(); expect(wrapper).toMatchSnapshot();
}); });
}); });

View File

@ -41,11 +41,7 @@ export default class Badge extends React.Component<BadgeProps, any> {
}; };
getBadgeClassName(prefixCls: string) { getBadgeClassName(prefixCls: string) {
const { const { className, status, children } = this.props;
className,
status,
children,
} = this.props;
return classNames(className, prefixCls, { return classNames(className, prefixCls, {
[`${prefixCls}-status`]: !!status, [`${prefixCls}-status`]: !!status,
[`${prefixCls}-not-a-wrapper`]: !children, [`${prefixCls}-not-a-wrapper`]: !children,
@ -74,7 +70,8 @@ export default class Badge extends React.Component<BadgeProps, any> {
getNumberedDispayCount() { getNumberedDispayCount() {
const { count, overflowCount } = this.props; const { count, overflowCount } = this.props;
const displayCount = (count as number) > (overflowCount as number) ? `${overflowCount}+` : count; const displayCount =
(count as number) > (overflowCount as number) ? `${overflowCount}+` : count;
return displayCount as string | number | null; return displayCount as string | number | null;
} }
@ -92,36 +89,33 @@ export default class Badge extends React.Component<BadgeProps, any> {
if (title) { if (title) {
return title; return title;
} }
return (typeof count === 'string' || typeof count === 'number') ? count : undefined; return typeof count === 'string' || typeof count === 'number' ? count : undefined;
} }
getStyleWithOffset() { getStyleWithOffset() {
const { offset, style } = this.props; const { offset, style } = this.props;
return offset ? { return offset
right: -parseInt(offset[0] as string, 10), ? {
marginTop: offset[1], right: -parseInt(offset[0] as string, 10),
...style, marginTop: offset[1],
} : style; ...style,
}
: style;
} }
renderStatusText(prefixCls: string) { renderStatusText(prefixCls: string) {
const { text } = this.props; const { text } = this.props;
const hidden = this.isHidden(); const hidden = this.isHidden();
return (hidden || !text) ? null : ( return hidden || !text ? null : <span className={`${prefixCls}-status-text`}>{text}</span>;
<span className={`${prefixCls}-status-text`}>{text}</span>
);
} }
renderDispayComponent() { renderDispayComponent() {
const { count } = this.props; const { count } = this.props;
return (count && typeof count === 'object') ? (count as React.ReactElement<any>) : undefined; return count && typeof count === 'object' ? (count as React.ReactElement<any>) : undefined;
} }
renderBadgeNumber(prefixCls: string, scrollNumberPrefixCls: string) { renderBadgeNumber(prefixCls: string, scrollNumberPrefixCls: string) {
const { const { count, status } = this.props;
count,
status,
} = this.props;
const displayCount = this.getDispayCount(); const displayCount = this.getDispayCount();
const isDot = this.isDot(); const isDot = this.isDot();
@ -130,7 +124,8 @@ export default class Badge extends React.Component<BadgeProps, any> {
const scrollNumberCls = classNames({ const scrollNumberCls = classNames({
[`${prefixCls}-dot`]: isDot, [`${prefixCls}-dot`]: isDot,
[`${prefixCls}-count`]: !isDot, [`${prefixCls}-count`]: !isDot,
[`${prefixCls}-multiple-words`]: !isDot && count && count.toString && count.toString().length > 1, [`${prefixCls}-multiple-words`]:
!isDot && count && count.toString && count.toString().length > 1,
[`${prefixCls}-status-${status}`]: !!status, [`${prefixCls}-status-${status}`]: !!status,
}); });
@ -205,13 +200,9 @@ export default class Badge extends React.Component<BadgeProps, any> {
{statusText} {statusText}
</span> </span>
); );
} };
render() { render() {
return ( return <ConfigConsumer>{this.renderBadge}</ConfigConsumer>;
<ConfigConsumer>
{this.renderBadge}
</ConfigConsumer>
);
} }
} }

View File

@ -1,8 +1,8 @@
@import "../../style/themes/default"; @import '../../style/themes/default';
@import "../../style/mixins/index"; @import '../../style/mixins/index';
@badge-prefix-cls: ~"@{ant-prefix}-badge"; @badge-prefix-cls: ~'@{ant-prefix}-badge';
@number-prefix-cls: ~"@{ant-prefix}-scroll-number"; @number-prefix-cls: ~'@{ant-prefix}-scroll-number';
.@{badge-prefix-cls} { .@{badge-prefix-cls} {
.reset-component; .reset-component;
@ -86,7 +86,7 @@
height: 100%; height: 100%;
border-radius: 50%; border-radius: 50%;
border: 1px solid @processing-color; border: 1px solid @processing-color;
content: ""; content: '';
animation: antStatusProcessing 1.2s infinite ease-in-out; animation: antStatusProcessing 1.2s infinite ease-in-out;
} }
} }
@ -108,12 +108,12 @@
&-zoom-appear, &-zoom-appear,
&-zoom-enter { &-zoom-enter {
animation: antZoomBadgeIn .3s @ease-out-back; animation: antZoomBadgeIn 0.3s @ease-out-back;
animation-fill-mode: both; animation-fill-mode: both;
} }
&-zoom-leave { &-zoom-leave {
animation: antZoomBadgeOut .3s @ease-in-back; animation: antZoomBadgeOut 0.3s @ease-in-back;
animation-fill-mode: both; animation-fill-mode: both;
} }
@ -147,7 +147,7 @@
overflow: hidden; overflow: hidden;
&-only { &-only {
display: inline-block; display: inline-block;
transition: all .3s @ease-in-out; transition: all 0.3s @ease-in-out;
height: @badge-height; height: @badge-height;
> p { > p {
height: @badge-height; height: @badge-height;

View File

@ -16,7 +16,12 @@ export interface BreadcrumbProps {
routes?: Route[]; routes?: Route[];
params?: any; params?: any;
separator?: React.ReactNode; separator?: React.ReactNode;
itemRender?: (route: any, params: any, routes: Array<any>, paths: Array<string>) => React.ReactNode; itemRender?: (
route: any,
params: any,
routes: Array<any>,
paths: Array<string>,
) => React.ReactNode;
style?: React.CSSProperties; style?: React.CSSProperties;
className?: string; className?: string;
} }
@ -36,9 +41,7 @@ function getBreadcrumbName(route: Route, params: any) {
function defaultItemRender(route: Route, params: any, routes: Route[], paths: string[]) { function defaultItemRender(route: Route, params: any, routes: Route[], paths: string[]) {
const isLastItem = routes.indexOf(route) === routes.length - 1; const isLastItem = routes.indexOf(route) === routes.length - 1;
const name = getBreadcrumbName(route, params); const name = getBreadcrumbName(route, params);
return isLastItem return isLastItem ? <span>{name}</span> : <a href={`#/${paths.join('/')}`}>{name}</a>;
? <span>{name}</span>
: <a href={`#/${paths.join('/')}`}>{name}</a>;
} }
export default class Breadcrumb extends React.Component<BreadcrumbProps, any> { export default class Breadcrumb extends React.Component<BreadcrumbProps, any> {
@ -62,7 +65,7 @@ export default class Breadcrumb extends React.Component<BreadcrumbProps, any> {
warning( warning(
!('linkRender' in props || 'nameRender' in props), !('linkRender' in props || 'nameRender' in props),
'`linkRender` and `nameRender` are removed, please use `itemRender` instead, ' + '`linkRender` and `nameRender` are removed, please use `itemRender` instead, ' +
'see: https://u.ant.design/item-render.', 'see: https://u.ant.design/item-render.',
); );
} }
@ -70,13 +73,18 @@ export default class Breadcrumb extends React.Component<BreadcrumbProps, any> {
let crumbs; let crumbs;
const { const {
prefixCls: customizePrefixCls, prefixCls: customizePrefixCls,
separator, style, className, routes, params = {}, separator,
children, itemRender = defaultItemRender, style,
className,
routes,
params = {},
children,
itemRender = defaultItemRender,
} = this.props; } = this.props;
const prefixCls = getPrefixCls('breadcrumb', customizePrefixCls); const prefixCls = getPrefixCls('breadcrumb', customizePrefixCls);
if (routes && routes.length > 0) { if (routes && routes.length > 0) {
const paths: string[] = []; const paths: string[] = [];
crumbs = routes.map((route) => { crumbs = routes.map(route => {
route.path = route.path || ''; route.path = route.path || '';
let path: string = route.path.replace(/^\//, ''); let path: string = route.path.replace(/^\//, '');
Object.keys(params).forEach(key => { Object.keys(params).forEach(key => {
@ -98,7 +106,7 @@ export default class Breadcrumb extends React.Component<BreadcrumbProps, any> {
} }
warning( warning(
element.type && element.type.__ANT_BREADCRUMB_ITEM, element.type && element.type.__ANT_BREADCRUMB_ITEM,
'Breadcrumb only accepts Breadcrumb.Item as it\'s children', "Breadcrumb only accepts Breadcrumb.Item as it's children",
); );
return cloneElement(element, { return cloneElement(element, {
separator, separator,
@ -111,13 +119,9 @@ export default class Breadcrumb extends React.Component<BreadcrumbProps, any> {
{crumbs} {crumbs}
</div> </div>
); );
} };
render() { render() {
return ( return <ConfigConsumer>{this.renderBreadcrumb}</ConfigConsumer>;
<ConfigConsumer>
{this.renderBreadcrumb}
</ConfigConsumer>
);
} }
} }

View File

@ -17,24 +17,26 @@ export default class BreadcrumbItem extends React.Component<BreadcrumbItemProps,
static propTypes = { static propTypes = {
prefixCls: PropTypes.string, prefixCls: PropTypes.string,
separator: PropTypes.oneOfType([ separator: PropTypes.oneOfType([PropTypes.string, PropTypes.element]),
PropTypes.string,
PropTypes.element,
]),
href: PropTypes.string, href: PropTypes.string,
}; };
renderBreadcrumbItem = ({ getPrefixCls }: ConfigConsumerProps) => { renderBreadcrumbItem = ({ getPrefixCls }: ConfigConsumerProps) => {
const { const { prefixCls: customizePrefixCls, separator, children, ...restProps } = this.props;
prefixCls: customizePrefixCls,
separator, children, ...restProps
} = this.props;
const prefixCls = getPrefixCls('breadcrumb', customizePrefixCls); const prefixCls = getPrefixCls('breadcrumb', customizePrefixCls);
let link; let link;
if ('href' in this.props) { if ('href' in this.props) {
link = <a className={`${prefixCls}-link`} {...restProps}>{children}</a>; link = (
<a className={`${prefixCls}-link`} {...restProps}>
{children}
</a>
);
} else { } else {
link = <span className={`${prefixCls}-link`} {...restProps}>{children}</span>; link = (
<span className={`${prefixCls}-link`} {...restProps}>
{children}
</span>
);
} }
if (children) { if (children) {
return ( return (
@ -45,13 +47,9 @@ export default class BreadcrumbItem extends React.Component<BreadcrumbItemProps,
); );
} }
return null; return null;
} };
render() { render() {
return ( return <ConfigConsumer>{this.renderBreadcrumbItem}</ConfigConsumer>;
<ConfigConsumer>
{this.renderBreadcrumbItem}
</ConfigConsumer>
);
} }
} }

View File

@ -19,11 +19,11 @@ describe('Breadcrumb', () => {
mount( mount(
<Breadcrumb> <Breadcrumb>
<MyCom /> <MyCom />
</Breadcrumb> </Breadcrumb>,
); );
expect(errorSpy.mock.calls).toHaveLength(1); expect(errorSpy.mock.calls).toHaveLength(1);
expect(errorSpy.mock.calls[0][0]).toMatch( expect(errorSpy.mock.calls[0][0]).toMatch(
'Breadcrumb only accepts Breadcrumb.Item as it\'s children' "Breadcrumb only accepts Breadcrumb.Item as it's children",
); );
}); });
@ -34,7 +34,7 @@ describe('Breadcrumb', () => {
{null} {null}
<Breadcrumb.Item>Home</Breadcrumb.Item> <Breadcrumb.Item>Home</Breadcrumb.Item>
{undefined} {undefined}
</Breadcrumb> </Breadcrumb>,
); );
expect(errorSpy).not.toHaveBeenCalled(); expect(errorSpy).not.toHaveBeenCalled();
expect(wrapper).toMatchSnapshot(); expect(wrapper).toMatchSnapshot();
@ -47,7 +47,7 @@ describe('Breadcrumb', () => {
<Breadcrumb.Item /> <Breadcrumb.Item />
<Breadcrumb.Item>xxx</Breadcrumb.Item> <Breadcrumb.Item>xxx</Breadcrumb.Item>
<Breadcrumb.Item>yyy</Breadcrumb.Item> <Breadcrumb.Item>yyy</Breadcrumb.Item>
</Breadcrumb> </Breadcrumb>,
); );
expect(wrapper).toMatchSnapshot(); expect(wrapper).toMatchSnapshot();
}); });

View File

@ -1,7 +1,5 @@
import React from 'react'; import React from 'react';
import { import { Route, Switch, Link, withRouter, MemoryRouter } from 'react-router-dom';
Route, Switch, Link, withRouter, MemoryRouter,
} from 'react-router-dom';
import { mount } from 'enzyme'; import { mount } from 'enzyme';
import Breadcrumb from '../index'; import Breadcrumb from '../index';
@ -24,24 +22,22 @@ const breadcrumbNameMap = {
'/apps/2/detail': 'Detail', '/apps/2/detail': 'Detail',
}; };
const Home = withRouter((props) => { const Home = withRouter(props => {
const { location, history } = props; const { location, history } = props;
const pathSnippets = location.pathname.split('/').filter(i => i); const pathSnippets = location.pathname.split('/').filter(i => i);
const extraBreadcrumbItems = pathSnippets.map((_, index) => { const extraBreadcrumbItems = pathSnippets.map((_, index) => {
const url = `/${pathSnippets.slice(0, index + 1).join('/')}`; const url = `/${pathSnippets.slice(0, index + 1).join('/')}`;
return ( return (
<Breadcrumb.Item key={url}> <Breadcrumb.Item key={url}>
<Link to={url}> <Link to={url}>{breadcrumbNameMap[url]}</Link>
{breadcrumbNameMap[url]}
</Link>
</Breadcrumb.Item> </Breadcrumb.Item>
); );
}); });
const breadcrumbItems = [( const breadcrumbItems = [
<Breadcrumb.Item key="home"> <Breadcrumb.Item key="home">
<Link to="/">Home</Link> <Link to="/">Home</Link>
</Breadcrumb.Item> </Breadcrumb.Item>,
)].concat(extraBreadcrumbItems); ].concat(extraBreadcrumbItems);
return ( return (
<div className="demo"> <div className="demo">
<div className="demo-nav"> <div className="demo-nav">
@ -52,9 +48,7 @@ const Home = withRouter((props) => {
<Route path="/apps" component={Apps} /> <Route path="/apps" component={Apps} />
<Route render={() => <span>Home Page</span>} /> <Route render={() => <span>Home Page</span>} />
</Switch> </Switch>
<Breadcrumb> <Breadcrumb>{breadcrumbItems}</Breadcrumb>
{breadcrumbItems}
</Breadcrumb>
</div> </div>
); );
}); });
@ -72,81 +66,94 @@ describe('react router', () => {
const wrapper = mount( const wrapper = mount(
<MemoryRouter initialEntries={['/']} initialIndex={0}> <MemoryRouter initialEntries={['/']} initialIndex={0}>
<Home /> <Home />
</MemoryRouter> </MemoryRouter>,
); );
expect(wrapper.find('BreadcrumbItem').length).toBe(1); expect(wrapper.find('BreadcrumbItem').length).toBe(1);
expect(wrapper.find('BreadcrumbItem .ant-breadcrumb-link').at(0).text()).toBe('Home'); expect(
wrapper.find('.demo-nav a').at(1).simulate('click'); wrapper
.find('BreadcrumbItem .ant-breadcrumb-link')
.at(0)
.text(),
).toBe('Home');
wrapper
.find('.demo-nav a')
.at(1)
.simulate('click');
expect(wrapper.find('BreadcrumbItem').length).toBe(2); expect(wrapper.find('BreadcrumbItem').length).toBe(2);
expect(wrapper.find('BreadcrumbItem .ant-breadcrumb-link').at(1).text()).toBe('Application List'); expect(
wrapper
.find('BreadcrumbItem .ant-breadcrumb-link')
.at(1)
.text(),
).toBe('Application List');
}); });
it('react router 3', () => { it('react router 3', () => {
const routes = [{ const routes = [
name: 'home', {
breadcrumbName: 'Home', name: 'home',
path: '/', breadcrumbName: 'Home',
childRoutes: [ path: '/',
{ childRoutes: [
name: 'apps', {
breadcrumbName: 'Application List', name: 'apps',
path: 'apps', breadcrumbName: 'Application List',
childRoutes: [ path: 'apps',
{ childRoutes: [
name: 'app', {
breadcrumbName: 'Application:id', name: 'app',
path: ':id', breadcrumbName: 'Application:id',
childRoutes: [ path: ':id',
{ childRoutes: [
name: 'detail', {
breadcrumbName: 'Detail', name: 'detail',
path: 'detail', breadcrumbName: 'Detail',
}, path: 'detail',
], },
}, ],
], },
}, ],
], },
}, ],
{ },
name: 'apps', {
breadcrumbName: 'Application List', name: 'apps',
path: 'apps', breadcrumbName: 'Application List',
childRoutes: [ path: 'apps',
{ childRoutes: [
name: 'app', {
breadcrumbName: 'Application:id', name: 'app',
path: ':id', breadcrumbName: 'Application:id',
childRoutes: [ path: ':id',
{ childRoutes: [
name: 'detail', {
breadcrumbName: 'Detail', name: 'detail',
path: 'detail', breadcrumbName: 'Detail',
}, path: 'detail',
], },
}, ],
], },
}, ],
{ },
name: 'app', {
breadcrumbName: 'Application:id', name: 'app',
path: ':id', breadcrumbName: 'Application:id',
childRoutes: [ path: ':id',
{ childRoutes: [
name: 'detail', {
breadcrumbName: 'Detail', name: 'detail',
path: 'detail', breadcrumbName: 'Detail',
}, path: 'detail',
], },
}, ],
{ },
name: 'detail', {
breadcrumbName: 'Detail', name: 'detail',
path: 'detail', breadcrumbName: 'Detail',
}]; path: 'detail',
const wrapper = mount( },
<Breadcrumb routes={routes} params={{ id: 1 }} /> ];
); const wrapper = mount(<Breadcrumb routes={routes} params={{ id: 1 }} />);
expect(wrapper).toMatchSnapshot(); expect(wrapper).toMatchSnapshot();
}); });
}); });

View File

@ -1,7 +1,7 @@
@import "../../style/themes/default"; @import '../../style/themes/default';
@import "../../style/mixins/index"; @import '../../style/mixins/index';
@breadcrumb-prefix-cls: ~"@{ant-prefix}-breadcrumb"; @breadcrumb-prefix-cls: ~'@{ant-prefix}-breadcrumb';
.@{breadcrumb-prefix-cls} { .@{breadcrumb-prefix-cls} {
.reset-component; .reset-component;
@ -14,7 +14,7 @@
a { a {
color: @breadcrumb-link-color; color: @breadcrumb-link-color;
transition: color .3s; transition: color 0.3s;
&:hover { &:hover {
color: @breadcrumb-link-color-hover; color: @breadcrumb-link-color-hover;
} }

View File

@ -6,9 +6,7 @@ import Icon from '../../icon';
describe('Button', () => { describe('Button', () => {
it('renders correctly', () => { it('renders correctly', () => {
const wrapper = render( const wrapper = render(<Button>Follow</Button>);
<Button>Follow</Button>
);
expect(wrapper).toMatchSnapshot(); expect(wrapper).toMatchSnapshot();
}); });
@ -20,41 +18,40 @@ describe('Button', () => {
}); });
it('renders Chinese characters correctly', () => { it('renders Chinese characters correctly', () => {
const wrapper = render( const wrapper = render(<Button>按钮</Button>);
<Button>按钮</Button>
);
expect(wrapper).toMatchSnapshot(); expect(wrapper).toMatchSnapshot();
// should not insert space when there is icon // should not insert space when there is icon
const wrapper1 = render( const wrapper1 = render(<Button icon="search">按钮</Button>);
<Button icon="search">按钮</Button>
);
expect(wrapper1).toMatchSnapshot(); expect(wrapper1).toMatchSnapshot();
// should not insert space when there is icon // should not insert space when there is icon
const wrapper2 = render( const wrapper2 = render(
<Button><Icon type="search" />按钮</Button> <Button>
<Icon type="search" />
按钮
</Button>,
); );
expect(wrapper2).toMatchSnapshot(); expect(wrapper2).toMatchSnapshot();
// should not insert space when there is icon // should not insert space when there is icon
const wrapper3 = render( const wrapper3 = render(<Button icon="search">按钮</Button>);
<Button icon="search">按钮</Button>
);
expect(wrapper3).toMatchSnapshot(); expect(wrapper3).toMatchSnapshot();
// should not insert space when there is icon while loading // should not insert space when there is icon while loading
const wrapper4 = render( const wrapper4 = render(
<Button icon="search" loading>按钮</Button> <Button icon="search" loading>
按钮
</Button>,
); );
expect(wrapper4).toMatchSnapshot(); expect(wrapper4).toMatchSnapshot();
// should insert space while loading // should insert space while loading
const wrapper5 = render( const wrapper5 = render(<Button loading>按钮</Button>);
<Button loading>按钮</Button>
);
expect(wrapper5).toMatchSnapshot(); expect(wrapper5).toMatchSnapshot();
}); });
it('renders Chinese characters correctly in HOC', () => { it('renders Chinese characters correctly in HOC', () => {
const Text = ({ children }) => <span>{children}</span>; const Text = ({ children }) => <span>{children}</span>;
const wrapper = mount( const wrapper = mount(
<Button><Text>按钮</Text></Button> <Button>
<Text>按钮</Text>
</Button>,
); );
expect(wrapper.find('.ant-btn').hasClass('ant-btn-two-chinese-chars')).toBe(true); expect(wrapper.find('.ant-btn').hasClass('ant-btn-two-chinese-chars')).toBe(true);
wrapper.setProps({ wrapper.setProps({
@ -70,9 +67,7 @@ describe('Button', () => {
}); });
it('have static property for type detecting', () => { it('have static property for type detecting', () => {
const wrapper = mount( const wrapper = mount(<Button>Button Text</Button>);
<Button>Button Text</Button>
);
// eslint-disable-next-line // eslint-disable-next-line
expect(wrapper.type().__ANT_BUTTON).toBe(true); expect(wrapper.type().__ANT_BUTTON).toBe(true);
}); });
@ -85,16 +80,18 @@ describe('Button', () => {
enterLoading = () => { enterLoading = () => {
this.setState({ loading: true }); this.setState({ loading: true });
} };
render() { render() {
const { loading } = this.state; const { loading } = this.state;
return <Button loading={loading} onClick={this.enterLoading}>Button</Button>; return (
<Button loading={loading} onClick={this.enterLoading}>
Button
</Button>
);
} }
} }
const wrapper = mount( const wrapper = mount(<DefaultButton />);
<DefaultButton />
);
wrapper.simulate('click'); wrapper.simulate('click');
expect(wrapper.find('.ant-btn-loading').length).toBe(1); expect(wrapper.find('.ant-btn-loading').length).toBe(1);
}); });
@ -108,54 +105,55 @@ describe('Button', () => {
enterLoading = () => { enterLoading = () => {
this.setState({ loading: { delay: 1000 } }); this.setState({ loading: { delay: 1000 } });
} };
render() { render() {
const { loading } = this.state; const { loading } = this.state;
return <Button loading={loading} onClick={this.enterLoading}>Button</Button>; return (
<Button loading={loading} onClick={this.enterLoading}>
Button
</Button>
);
} }
} }
const wrapper = mount( const wrapper = mount(<DefaultButton />);
<DefaultButton />
);
wrapper.simulate('click'); wrapper.simulate('click');
expect(wrapper.hasClass('ant-btn-loading')).toBe(false); expect(wrapper.hasClass('ant-btn-loading')).toBe(false);
}); });
it('should support link button', () => { it('should support link button', () => {
const wrapper = mount( const wrapper = mount(
<Button target="_blank" href="http://ant.design">link button</Button> <Button target="_blank" href="http://ant.design">
link button
</Button>,
); );
expect(wrapper.render()).toMatchSnapshot(); expect(wrapper.render()).toMatchSnapshot();
}); });
it('fixbug renders {0} , 0 and {false}', () => { it('fixbug renders {0} , 0 and {false}', () => {
const wrapper = render( const wrapper = render(<Button>{0}</Button>);
<Button>{0}</Button>
);
expect(wrapper).toMatchSnapshot(); expect(wrapper).toMatchSnapshot();
const wrapper1 = render( const wrapper1 = render(<Button>0</Button>);
<Button>0</Button>
);
expect(wrapper1).toMatchSnapshot(); expect(wrapper1).toMatchSnapshot();
const wrapper2 = render( const wrapper2 = render(<Button>{false}</Button>);
<Button>{false}</Button>
);
expect(wrapper2).toMatchSnapshot(); expect(wrapper2).toMatchSnapshot();
}); });
it('should has click wave effect', async () => { it('should has click wave effect', async () => {
const wrapper = mount( const wrapper = mount(<Button type="primary">button</Button>);
<Button type="primary">button</Button> wrapper
); .find('.ant-btn')
wrapper.find('.ant-btn').getDOMNode().click(); .getDOMNode()
.click();
await new Promise(resolve => setTimeout(resolve, 0)); await new Promise(resolve => setTimeout(resolve, 0));
expect(wrapper.render()).toMatchSnapshot(); expect(wrapper.render()).toMatchSnapshot();
}); });
it('should not render as link button when href is undefined', async () => { it('should not render as link button when href is undefined', async () => {
const wrapper = mount( const wrapper = mount(
<Button type="primary" href={undefined}>button</Button> <Button type="primary" href={undefined}>
button
</Button>,
); );
expect(wrapper.render()).toMatchSnapshot(); expect(wrapper.render()).toMatchSnapshot();
}); });

View File

@ -29,9 +29,13 @@ const ButtonGroup: React.SFC<ButtonGroupProps> = props => (
break; break;
} }
const classes = classNames(prefixCls, { const classes = classNames(
[`${prefixCls}-${sizeCls}`]: sizeCls, prefixCls,
}, className); {
[`${prefixCls}-${sizeCls}`]: sizeCls,
},
className,
);
return <div {...others} className={classes} />; return <div {...others} className={classes} />;
}} }}

View File

@ -20,10 +20,13 @@ function insertSpace(child: React.ReactChild, needInserted: boolean) {
} }
const SPACE = needInserted ? ' ' : ''; const SPACE = needInserted ? ' ' : '';
// strictNullChecks oops. // strictNullChecks oops.
if (typeof child !== 'string' && typeof child !== 'number' && if (
isString(child.type) && isTwoCNChar(child.props.children)) { typeof child !== 'string' &&
return React.cloneElement(child, {}, typeof child !== 'number' &&
child.props.children.split('').join(SPACE)); isString(child.type) &&
isTwoCNChar(child.props.children)
) {
return React.cloneElement(child, {}, child.props.children.split('').join(SPACE));
} }
if (typeof child === 'string') { if (typeof child === 'string') {
if (isTwoCNChar(child)) { if (isTwoCNChar(child)) {
@ -56,12 +59,14 @@ export type AnchorButtonProps = {
href: string; href: string;
target?: string; target?: string;
onClick?: React.MouseEventHandler<HTMLAnchorElement>; onClick?: React.MouseEventHandler<HTMLAnchorElement>;
} & BaseButtonProps & React.AnchorHTMLAttributes<HTMLAnchorElement>; } & BaseButtonProps &
React.AnchorHTMLAttributes<HTMLAnchorElement>;
export type NativeButtonProps = { export type NativeButtonProps = {
htmlType?: ButtonHTMLType; htmlType?: ButtonHTMLType;
onClick?: React.MouseEventHandler<HTMLButtonElement>; onClick?: React.MouseEventHandler<HTMLButtonElement>;
} & BaseButtonProps & React.ButtonHTMLAttributes<HTMLButtonElement>; } & BaseButtonProps &
React.ButtonHTMLAttributes<HTMLButtonElement>;
export type ButtonProps = AnchorButtonProps | NativeButtonProps; export type ButtonProps = AnchorButtonProps | NativeButtonProps;
@ -129,7 +134,7 @@ export default class Button extends React.Component<ButtonProps, any> {
saveButtonRef = (node: HTMLElement | null) => { saveButtonRef = (node: HTMLElement | null) => {
this.buttonNode = node; this.buttonNode = node;
} };
fixTwoCNChar() { fixTwoCNChar() {
// Fix for HOC usage like <FormatMessage /> // Fix for HOC usage like <FormatMessage />
@ -159,7 +164,7 @@ export default class Button extends React.Component<ButtonProps, any> {
if (onClick) { if (onClick) {
(onClick as React.MouseEventHandler<HTMLButtonElement | HTMLAnchorElement>)(e); (onClick as React.MouseEventHandler<HTMLButtonElement | HTMLAnchorElement>)(e);
} }
} };
isNeedInserted() { isNeedInserted() {
const { icon, children } = this.props; const { icon, children } = this.props;
@ -169,7 +174,15 @@ export default class Button extends React.Component<ButtonProps, any> {
renderButton = ({ getPrefixCls }: ConfigConsumerProps) => { renderButton = ({ getPrefixCls }: ConfigConsumerProps) => {
const { const {
prefixCls: customizePrefixCls, prefixCls: customizePrefixCls,
type, shape, size, className, children, icon, ghost, loading: _loadingProp, block, type,
shape,
size,
className,
children,
icon,
ghost,
loading: _loadingProp,
block,
...rest ...rest
} = this.props; } = this.props;
const { loading, hasTwoCNChar } = this.state; const { loading, hasTwoCNChar } = this.state;
@ -205,10 +218,12 @@ export default class Button extends React.Component<ButtonProps, any> {
const iconType = loading ? 'loading' : icon; const iconType = loading ? 'loading' : icon;
const iconNode = iconType ? <Icon type={iconType} /> : null; const iconNode = iconType ? <Icon type={iconType} /> : null;
const kids = (children || children === 0) const kids =
? React.Children.map(children, child => insertSpace(child, this.isNeedInserted())) : null; children || children === 0
? React.Children.map(children, child => insertSpace(child, this.isNeedInserted()))
: null;
const title= isChristmas ? 'Ho Ho Ho!' : rest.title; const title = isChristmas ? 'Ho Ho Ho!' : rest.title;
const linkButtonRestProps = rest as AnchorButtonProps; const linkButtonRestProps = rest as AnchorButtonProps;
if (linkButtonRestProps.href !== undefined) { if (linkButtonRestProps.href !== undefined) {
@ -220,7 +235,8 @@ export default class Button extends React.Component<ButtonProps, any> {
title={title} title={title}
ref={this.saveButtonRef} ref={this.saveButtonRef}
> >
{iconNode}{kids} {iconNode}
{kids}
</a> </a>
); );
} }
@ -231,24 +247,21 @@ export default class Button extends React.Component<ButtonProps, any> {
return ( return (
<Wave> <Wave>
<button <button
{...(otherProps as NativeButtonProps)} {...otherProps as NativeButtonProps}
type={htmlType || 'button'} type={htmlType || 'button'}
className={classes} className={classes}
onClick={this.handleClick} onClick={this.handleClick}
title={title} title={title}
ref={this.saveButtonRef} ref={this.saveButtonRef}
> >
{iconNode}{kids} {iconNode}
{kids}
</button> </button>
</Wave> </Wave>
); );
} };
render() { render() {
return ( return <ConfigConsumer>{this.renderButton}</ConfigConsumer>;
<ConfigConsumer>
{this.renderButton}
</ConfigConsumer>
);
} }
} }

View File

@ -1,8 +1,8 @@
@import "../../style/themes/default"; @import '../../style/themes/default';
@import "../../style/mixins/index"; @import '../../style/mixins/index';
@import "./mixin"; @import './mixin';
@btn-prefix-cls: ~"@{ant-prefix}-btn"; @btn-prefix-cls: ~'@{ant-prefix}-btn';
// for compatible // for compatible
@btn-ghost-color: @text-color; @btn-ghost-color: @text-color;
@ -82,16 +82,16 @@
right: -1px; right: -1px;
background: @component-background; background: @component-background;
opacity: 0.35; opacity: 0.35;
content: ""; content: '';
border-radius: inherit; border-radius: inherit;
z-index: 1; z-index: 1;
transition: opacity .2s; transition: opacity 0.2s;
pointer-events: none; pointer-events: none;
display: none; display: none;
} }
.@{iconfont-css-prefix} { .@{iconfont-css-prefix} {
transition: margin-left .3s @ease-in-out; transition: margin-left 0.3s @ease-in-out;
} }
&&-loading:before { &&-loading:before {
@ -150,12 +150,12 @@
} }
&-two-chinese-chars:first-letter { &-two-chinese-chars:first-letter {
letter-spacing: .34em; letter-spacing: 0.34em;
} }
&-two-chinese-chars > *:not(.@{iconfont-css-prefix}) { &-two-chinese-chars > *:not(.@{iconfont-css-prefix}) {
letter-spacing: .34em; letter-spacing: 0.34em;
margin-right: -.34em; margin-right: -0.34em;
} }
&-block { &-block {
@ -163,13 +163,14 @@
} }
.christmas&-primary:before { .christmas&-primary:before {
content: ""; content: '';
display: block; display: block;
position: absolute; position: absolute;
top: -6px; top: -6px;
left: 0; left: 0;
right: 0; right: 0;
background: url(data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAE0AAAAXCAYAAABOHMIhAAAAGXRFWHRTb2Z0d2FyZQBBZG9iZSBJbWFnZVJlYWR5ccllPAAABiZJREFUeNrsWMtPlFcUvzPMwIDysLyRR4uATDHWCiVgSmRlios2DeiiXUFs0nRBd6arxqQhJDapkYXhP4BqDKTQhZaFNQSCaBEVJjwdHsNr5DUMDDPDzPT3u7nTDEgRKrKgc5KT+z3uufec33de99P4fD4RpL2RNgjB3kn35MkTeRERESFiYmLkGBoaKnQ6nWSNRvPPZFxr+vv7k6KioiIdDsfa8vLyQkFBgcP3Bnel3MDAQArWI0eFhISE87nb7bZ7PJ4VvLYuLi5O5+fnu9+kMNfq6+tLjIyMzMY6KeBEbK/XarXReI3lPDZMWcc4v7GxYV1dXR3Jy8ub2E5HPvJ6vRSSDH0ku1wuAfsEZOV1IEFHoeNFdHS0yMrK2knR0Lm5uR+hxLdQMjbwHTZbB41h8RGwCdc9MzMzneHh4bGJiYlf4SN8ijkfwqiIncCAAR7Iz2GPSShudjqdfeCeqampvwBQfFxc3JdYqwTv8gB8/F48A8BgKecE14V+L7ju2tpae05OzkuCCZvkPOj8mizmC6vVKtmPu+bx48cC3qI1mUyFUOyywWD4SHlELBaLJmCHNcwAghuAOujtuF4FqHO4nsX4EsAS3I4TJ04ME1h8PDE9PS09TYZoY2Pj1729vd6lpSVfkDYTPG0UkfNDRUWFgQ5Gb2Mh0N29e9eG/GQfHh4W8/PzwUy/ObQ/gMfVVlZW1iAiZdQxp3nv3LljRoL/5erVq1UIxzSiiVD9X4EDYATynCwAzGO858hCQRoaGmJFZNJz8YIcBc4BF966dau6sLAwBxVSJCUlCSThQwuU3W6XkYUok1Vzm5znQx5bbm9v77p+/frPeNSNRzZ/ISBwrG4ZR48eLamtrf2+uLjYSEG9Xi/wTISFhQlWGXohyzO/CJlVl23KQRLbABoaHx+/Z1lUZ/Hq1SsJFj3JT3hmHx8fnydPTEzMj46OziHPW2w22wxeD4Kfgadh/4YEzU8Az4DhffAn5eXlX1y6dKkEoCTspAQ9Mjs7+0BBo8Fms1lkZGTsOo0QLLRNkvnR+fEJzIMHD0xtbW39CL8JTFtSbAOvBIyLHIGVm9VzE2gKuDAMSSpcT6KXyT137lx2cnLyMXhcGDb3wq3XuWF3d/fCzZs3P0c4v5eSknJQbYLo7Ox0gC2lpaVZ3Be67Th/dnZWoAJKsJC3XA8fPhxoamp6hMb+BaaMgWcUMGtszZjiFDNmvcDI91pzG0iY4ARwkwrxkcHBwUdgNrRMbnrqoRbkVzDcvn3bl5qaWsmcgFH4G8XdEGUWFhak51AuISFBnkoCTyFbyWKxCJwIxlC0fq2rq7tcVFRkRKskjh8/Lr0+kBjCCDV/knfdv3//WX19/R8IRRNemxlu4AXwKqM+EJwdj1HbPYSwh3sCPAJDABm2LLchCjS+5/kirKGhwWk0GrMuXrxYQuX9hm/XXTMXMY+srKwI5ApZrbYmZh7deEJhAUKjLe/pLTzSsCuHrK+1tbUJVe3P6upq87Vr174rKysrYHVj/uW+OH3IfEuw4F3ee/fuPQfAvwOs5yyE4CnlFOu7BWrTCWlreO6FACpBZGwUw4BvkANLobReHb3kGZYGsGzTq/zlO8AT1ru6uoZbWlqeA6gINJAfnz59OlVLoX8Jtebm5raampqfcMvQYgTknz9//sKVK1c+y83NTdIEuCnaKMuNGzd+6+np6cCtSTkAw9D9X8Dyh+dbgaaAC1XAnUlPTy+qqqq6cPbs2UzkmWjNljiDJzpwHFnCkW2yo6NjCKW8H54wjlezKvRT09LSTsJrz5w6dSoN+Yp51ADAPUj8VoDbDq9pxrwuJcNIYQllJTIi/xopBw/VA7DJp0+f9hA78CgL5F5C8J2CpoCj8sfA6WCe/FPRhsRlZmbGIs8Y4FFO5CJgtrSsvrRVGW1V93b1myoGnKAKEcHgnwsWpg1lNI0fphwrmdqbckeU18WrnlOjqp5/j7W3BWvfQVPKa5SBkcrYCNVB65TRTlWZ1lXiXVU5xbtlDb2SPaLWYwrgHIcqPg6Vc7fbX69Yoyqfa7/AeiegbWOEVhmsVcWDwPn224iDJgla8Hd38Hd3ELQgaIeI/hZgAIPEp0vmQJdoAAAAAElFTkSuQmCC) no-repeat 50% 0; background: url(data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAE0AAAAXCAYAAABOHMIhAAAAGXRFWHRTb2Z0d2FyZQBBZG9iZSBJbWFnZVJlYWR5ccllPAAABiZJREFUeNrsWMtPlFcUvzPMwIDysLyRR4uATDHWCiVgSmRlios2DeiiXUFs0nRBd6arxqQhJDapkYXhP4BqDKTQhZaFNQSCaBEVJjwdHsNr5DUMDDPDzPT3u7nTDEgRKrKgc5KT+z3uufec33de99P4fD4RpL2RNgjB3kn35MkTeRERESFiYmLkGBoaKnQ6nWSNRvPPZFxr+vv7k6KioiIdDsfa8vLyQkFBgcP3Bnel3MDAQArWI0eFhISE87nb7bZ7PJ4VvLYuLi5O5+fnu9+kMNfq6+tLjIyMzMY6KeBEbK/XarXReI3lPDZMWcc4v7GxYV1dXR3Jy8ub2E5HPvJ6vRSSDH0ku1wuAfsEZOV1IEFHoeNFdHS0yMrK2knR0Lm5uR+hxLdQMjbwHTZbB41h8RGwCdc9MzMzneHh4bGJiYlf4SN8ijkfwqiIncCAAR7Iz2GPSShudjqdfeCeqampvwBQfFxc3JdYqwTv8gB8/F48A8BgKecE14V+L7ju2tpae05OzkuCCZvkPOj8mizmC6vVKtmPu+bx48cC3qI1mUyFUOyywWD4SHlELBaLJmCHNcwAghuAOujtuF4FqHO4nsX4EsAS3I4TJ04ME1h8PDE9PS09TYZoY2Pj1729vd6lpSVfkDYTPG0UkfNDRUWFgQ5Gb2Mh0N29e9eG/GQfHh4W8/PzwUy/ObQ/gMfVVlZW1iAiZdQxp3nv3LljRoL/5erVq1UIxzSiiVD9X4EDYATynCwAzGO858hCQRoaGmJFZNJz8YIcBc4BF966dau6sLAwBxVSJCUlCSThQwuU3W6XkYUok1Vzm5znQx5bbm9v77p+/frPeNSNRzZ/ISBwrG4ZR48eLamtrf2+uLjYSEG9Xi/wTISFhQlWGXohyzO/CJlVl23KQRLbABoaHx+/Z1lUZ/Hq1SsJFj3JT3hmHx8fnydPTEzMj46OziHPW2w22wxeD4Kfgadh/4YEzU8Az4DhffAn5eXlX1y6dKkEoCTspAQ9Mjs7+0BBo8Fms1lkZGTsOo0QLLRNkvnR+fEJzIMHD0xtbW39CL8JTFtSbAOvBIyLHIGVm9VzE2gKuDAMSSpcT6KXyT137lx2cnLyMXhcGDb3wq3XuWF3d/fCzZs3P0c4v5eSknJQbYLo7Ox0gC2lpaVZ3Be67Th/dnZWoAJKsJC3XA8fPhxoamp6hMb+BaaMgWcUMGtszZjiFDNmvcDI91pzG0iY4ARwkwrxkcHBwUdgNrRMbnrqoRbkVzDcvn3bl5qaWsmcgFH4G8XdEGUWFhak51AuISFBnkoCTyFbyWKxCJwIxlC0fq2rq7tcVFRkRKskjh8/Lr0+kBjCCDV/knfdv3//WX19/R8IRRNemxlu4AXwKqM+EJwdj1HbPYSwh3sCPAJDABm2LLchCjS+5/kirKGhwWk0GrMuXrxYQuX9hm/XXTMXMY+srKwI5ApZrbYmZh7deEJhAUKjLe/pLTzSsCuHrK+1tbUJVe3P6upq87Vr174rKysrYHVj/uW+OH3IfEuw4F3ee/fuPQfAvwOs5yyE4CnlFOu7BWrTCWlreO6FACpBZGwUw4BvkANLobReHb3kGZYGsGzTq/zlO8AT1ru6uoZbWlqeA6gINJAfnz59OlVLoX8Jtebm5raampqfcMvQYgTknz9//sKVK1c+y83NTdIEuCnaKMuNGzd+6+np6cCtSTkAw9D9X8Dyh+dbgaaAC1XAnUlPTy+qqqq6cPbs2UzkmWjNljiDJzpwHFnCkW2yo6NjCKW8H54wjlezKvRT09LSTsJrz5w6dSoN+Yp51ADAPUj8VoDbDq9pxrwuJcNIYQllJTIi/xopBw/VA7DJp0+f9hA78CgL5F5C8J2CpoCj8sfA6WCe/FPRhsRlZmbGIs8Y4FFO5CJgtrSsvrRVGW1V93b1myoGnKAKEcHgnwsWpg1lNI0fphwrmdqbckeU18WrnlOjqp5/j7W3BWvfQVPKa5SBkcrYCNVB65TRTlWZ1lXiXVU5xbtlDb2SPaLWYwrgHIcqPg6Vc7fbX69Yoyqfa7/AeiegbWOEVhmsVcWDwPn224iDJgla8Hd38Hd3ELQgaIeI/hZgAIPEp0vmQJdoAAAAAElFTkSuQmCC)
no-repeat 50% 0;
background-size: 64px; background-size: 64px;
opacity: 1; opacity: 1;
} }

View File

@ -24,17 +24,21 @@
.button-variant-primary(@color; @background) { .button-variant-primary(@color; @background) {
.button-color(@color; @background; @background); .button-color(@color; @background; @background);
text-shadow: 0 -1px 0 rgba(0, 0, 0, .12); text-shadow: 0 -1px 0 rgba(0, 0, 0, 0.12);
box-shadow: 0 2px 0 rgba(0, 0, 0, .045); box-shadow: 0 2px 0 rgba(0, 0, 0, 0.045);
&:hover, &:hover,
&:focus { &:focus {
.button-color(@color; ~`colorPalette("@{background}", 5)`; ~`colorPalette("@{background}", 5)`); .button-color(
@color; ~`colorPalette('@{background}', 5) `; ~`colorPalette('@{background}', 5) `
);
} }
&:active, &:active,
&.active { &.active {
.button-color(@color; ~`colorPalette("@{background}", 7)`; ~`colorPalette("@{background}", 7)`); .button-color(
@color; ~`colorPalette('@{background}', 7) `; ~`colorPalette('@{background}', 7) `
);
} }
.button-disabled(); .button-disabled();
@ -60,16 +64,22 @@
.button-color(@color; @background; @border); .button-color(@color; @background; @border);
&:hover { &:hover {
.button-color(@btn-primary-color; ~`colorPalette("@{color}", 5)`; ~`colorPalette("@{color}", 5)`); .button-color(
@btn-primary-color; ~`colorPalette('@{color}', 5) `; ~`colorPalette('@{color}', 5) `
);
} }
&:focus { &:focus {
.button-color(~`colorPalette("@{color}", 5)`; @component-background; ~`colorPalette("@{color}", 5)`); .button-color(
~`colorPalette('@{color}', 5) `; @component-background; ~`colorPalette('@{color}', 5) `
);
} }
&:active, &:active,
&.active { &.active {
.button-color(@btn-primary-color; ~`colorPalette("@{color}", 7)`; ~`colorPalette("@{color}", 7)`); .button-color(
@btn-primary-color; ~`colorPalette('@{color}', 7) `; ~`colorPalette('@{color}', 7) `
);
} }
.button-disabled(); .button-disabled();
@ -81,12 +91,12 @@
&:hover, &:hover,
&:focus { &:focus {
.button-color(~`colorPalette("@{color}", 5)`; transparent; ~`colorPalette("@{color}", 5)`); .button-color(~`colorPalette('@{color}', 5) `; transparent; ~`colorPalette('@{color}', 5) `);
} }
&:active, &:active,
&.active { &.active {
.button-color(~`colorPalette("@{color}", 7)`; transparent; ~`colorPalette("@{color}", 7)`); .button-color(~`colorPalette('@{color}', 7) `; transparent; ~`colorPalette('@{color}', 7) `);
} }
.button-disabled(); .button-disabled();
@ -101,7 +111,7 @@
> a:only-child { > a:only-child {
color: currentColor; color: currentColor;
&:after { &:after {
content: ""; content: '';
position: absolute; position: absolute;
top: 0; top: 0;
left: 0; left: 0;
@ -161,9 +171,9 @@
white-space: nowrap; white-space: nowrap;
.button-size(@btn-height-base; @btn-padding-base; @font-size-base; @btn-border-radius-base); .button-size(@btn-height-base; @btn-padding-base; @font-size-base; @btn-border-radius-base);
user-select: none; user-select: none;
transition: all .3s @ease-in-out; transition: all 0.3s @ease-in-out;
position: relative; position: relative;
box-shadow: 0 2px 0 rgba(0, 0, 0, .015); box-shadow: 0 2px 0 rgba(0, 0, 0, 0.015);
> .@{iconfont-css-prefix} { > .@{iconfont-css-prefix} {
line-height: 1; line-height: 1;

View File

@ -15,7 +15,7 @@ export interface HeaderProps {
onValueChange?: (value: moment.Moment) => void; onValueChange?: (value: moment.Moment) => void;
onTypeChange?: (type: string) => void; onTypeChange?: (type: string) => void;
value: any; value: any;
validRange ?: [moment.Moment, moment.Moment]; validRange?: [moment.Moment, moment.Moment];
} }
export default class Header extends React.Component<HeaderProps, any> { export default class Header extends React.Component<HeaderProps, any> {
@ -27,13 +27,7 @@ export default class Header extends React.Component<HeaderProps, any> {
private calenderHeaderNode: HTMLDivElement; private calenderHeaderNode: HTMLDivElement;
getYearSelectElement(prefixCls: string, year: number) { getYearSelectElement(prefixCls: string, year: number) {
const { const { yearSelectOffset, yearSelectTotal, locale, fullscreen, validRange } = this.props;
yearSelectOffset,
yearSelectTotal,
locale,
fullscreen,
validRange,
} = this.props;
let start = year - (yearSelectOffset as number); let start = year - (yearSelectOffset as number);
let end = start + (yearSelectTotal as number); let end = start + (yearSelectTotal as number);
if (validRange) { if (validRange) {
@ -70,7 +64,7 @@ export default class Header extends React.Component<HeaderProps, any> {
return months; return months;
} }
getMonthSelectElement(prefixCls: string,month: number, months: number[]) { getMonthSelectElement(prefixCls: string, month: number, months: number[]) {
const { fullscreen, validRange, value } = this.props; const { fullscreen, validRange, value } = this.props;
const options: React.ReactElement<any>[] = []; const options: React.ReactElement<any>[] = [];
let start = 0; let start = 0;
@ -107,7 +101,7 @@ export default class Header extends React.Component<HeaderProps, any> {
newValue.year(parseInt(year, 10)); newValue.year(parseInt(year, 10));
// switch the month so that it remains within range when year changes // switch the month so that it remains within range when year changes
if (validRange) { if (validRange) {
const [ start, end ] = validRange; const [start, end] = validRange;
const newYear = newValue.get('year'); const newYear = newValue.get('year');
const newMonth = newValue.get('month'); const newMonth = newValue.get('month');
if (newYear === end.get('year') && newMonth > end.get('month')) { if (newYear === end.get('year') && newMonth > end.get('month')) {
@ -122,7 +116,7 @@ export default class Header extends React.Component<HeaderProps, any> {
if (onValueChange) { if (onValueChange) {
onValueChange(newValue); onValueChange(newValue);
} }
} };
onMonthChange = (month: string) => { onMonthChange = (month: string) => {
const newValue = this.props.value.clone(); const newValue = this.props.value.clone();
@ -131,28 +125,27 @@ export default class Header extends React.Component<HeaderProps, any> {
if (onValueChange) { if (onValueChange) {
onValueChange(newValue); onValueChange(newValue);
} }
} };
onTypeChange = (e: RadioChangeEvent) => { onTypeChange = (e: RadioChangeEvent) => {
const onTypeChange = this.props.onTypeChange; const onTypeChange = this.props.onTypeChange;
if (onTypeChange) { if (onTypeChange) {
onTypeChange(e.target.value); onTypeChange(e.target.value);
} }
} };
getCalenderHeaderNode = (node: HTMLDivElement) => { getCalenderHeaderNode = (node: HTMLDivElement) => {
this.calenderHeaderNode = node; this.calenderHeaderNode = node;
} };
renderHeader = ({ getPrefixCls }: ConfigConsumerProps) => { renderHeader = ({ getPrefixCls }: ConfigConsumerProps) => {
const { const { prefixCls: customizePrefixCls, type, value, locale, fullscreen } = this.props;
prefixCls: customizePrefixCls,
type, value, locale, fullscreen,
} = this.props;
const prefixCls = getPrefixCls('fullcalendar', customizePrefixCls); const prefixCls = getPrefixCls('fullcalendar', customizePrefixCls);
const yearSelect = this.getYearSelectElement(prefixCls, value.year()); const yearSelect = this.getYearSelectElement(prefixCls, value.year());
const monthSelect = type === 'date' ? const monthSelect =
this.getMonthSelectElement(prefixCls, value.month(), this.getMonthsLocale(value)) : null; type === 'date'
? this.getMonthSelectElement(prefixCls, value.month(), this.getMonthsLocale(value))
: null;
const size = (fullscreen ? 'default' : 'small') as any; const size = (fullscreen ? 'default' : 'small') as any;
const typeSwitch = ( const typeSwitch = (
<Group onChange={this.onTypeChange} value={type} size={size}> <Group onChange={this.onTypeChange} value={type} size={size}>
@ -168,13 +161,9 @@ export default class Header extends React.Component<HeaderProps, any> {
{typeSwitch} {typeSwitch}
</div> </div>
); );
} };
render() { render() {
return ( return <ConfigConsumer>{this.renderHeader}</ConfigConsumer>;
<ConfigConsumer>
{this.renderHeader}
</ConfigConsumer>
);
} }
} }

View File

@ -7,10 +7,11 @@ import Calendar from '..';
describe('Calendar', () => { describe('Calendar', () => {
it('Calendar should be selectable', () => { it('Calendar should be selectable', () => {
const onSelect = jest.fn(); const onSelect = jest.fn();
const wrapper = mount( const wrapper = mount(<Calendar onSelect={onSelect} />);
<Calendar onSelect={onSelect} /> wrapper
); .find('.ant-fullcalendar-cell')
wrapper.find('.ant-fullcalendar-cell').at(0).simulate('click'); .at(0)
.simulate('click');
expect(onSelect).toBeCalledWith(expect.anything()); expect(onSelect).toBeCalledWith(expect.anything());
const value = onSelect.mock.calls[0][0]; const value = onSelect.mock.calls[0][0];
expect(Moment.isMoment(value)).toBe(true); expect(Moment.isMoment(value)).toBe(true);
@ -20,10 +21,16 @@ describe('Calendar', () => {
const onSelect = jest.fn(); const onSelect = jest.fn();
const validRange = [Moment('2018-02-02'), Moment('2018-02-18')]; const validRange = [Moment('2018-02-02'), Moment('2018-02-18')];
const wrapper = mount( const wrapper = mount(
<Calendar onSelect={onSelect} validRange={validRange} defaultValue={Moment('2018-02-02')} /> <Calendar onSelect={onSelect} validRange={validRange} defaultValue={Moment('2018-02-02')} />,
); );
wrapper.find('[title="February 1, 2018"]').at(0).simulate('click'); wrapper
wrapper.find('[title="February 2, 2018"]').at(0).simulate('click'); .find('[title="February 1, 2018"]')
.at(0)
.simulate('click');
wrapper
.find('[title="February 2, 2018"]')
.at(0)
.simulate('click');
expect(onSelect.mock.calls.length).toBe(1); expect(onSelect.mock.calls.length).toBe(1);
}); });
@ -31,10 +38,15 @@ describe('Calendar', () => {
const onSelect = jest.fn(); const onSelect = jest.fn();
const validRange = [Moment('2018-02-02'), Moment('2018-02-18')]; const validRange = [Moment('2018-02-02'), Moment('2018-02-18')];
const wrapper = mount( const wrapper = mount(
<Calendar onSelect={onSelect} validRange={validRange} defaultValue={Moment('2018-02-02')} /> <Calendar onSelect={onSelect} validRange={validRange} defaultValue={Moment('2018-02-02')} />,
); );
wrapper.find('[title="February 20, 2018"]').at(0).simulate('click'); wrapper
const elem = wrapper.find('[title="February 20, 2018"]').hasClass('ant-fullcalendar-disabled-cell'); .find('[title="February 20, 2018"]')
.at(0)
.simulate('click');
const elem = wrapper
.find('[title="February 20, 2018"]')
.hasClass('ant-fullcalendar-disabled-cell');
expect(elem).toEqual(true); expect(elem).toEqual(true);
expect(onSelect.mock.calls.length).toBe(0); expect(onSelect.mock.calls.length).toBe(0);
}); });
@ -43,33 +55,64 @@ describe('Calendar', () => {
const onSelect = jest.fn(); const onSelect = jest.fn();
const validRange = [Moment('2018-02-02'), Moment('2018-05-18')]; const validRange = [Moment('2018-02-02'), Moment('2018-05-18')];
const wrapper = mount( const wrapper = mount(
<Calendar onSelect={onSelect} validRange={validRange} defaultValue={Moment('2018-02-02')} mode="year" /> <Calendar
onSelect={onSelect}
validRange={validRange}
defaultValue={Moment('2018-02-02')}
mode="year"
/>,
); );
expect(wrapper.find('[title="Jan"]').at(0).hasClass('ant-fullcalendar-month-panel-cell-disabled')).toBe(true); expect(
expect(wrapper.find('[title="Feb"]').at(0).hasClass('ant-fullcalendar-month-panel-cell-disabled')).toBe(false); wrapper
expect(wrapper.find('[title="Jun"]').at(0).hasClass('ant-fullcalendar-month-panel-cell-disabled')).toBe(true); .find('[title="Jan"]')
wrapper.find('[title="Jan"]').at(0).simulate('click'); .at(0)
wrapper.find('[title="Mar"]').at(0).simulate('click'); .hasClass('ant-fullcalendar-month-panel-cell-disabled'),
).toBe(true);
expect(
wrapper
.find('[title="Feb"]')
.at(0)
.hasClass('ant-fullcalendar-month-panel-cell-disabled'),
).toBe(false);
expect(
wrapper
.find('[title="Jun"]')
.at(0)
.hasClass('ant-fullcalendar-month-panel-cell-disabled'),
).toBe(true);
wrapper
.find('[title="Jan"]')
.at(0)
.simulate('click');
wrapper
.find('[title="Mar"]')
.at(0)
.simulate('click');
expect(onSelect.mock.calls.length).toBe(1); expect(onSelect.mock.calls.length).toBe(1);
}); });
it('months other than in valid range should not be shown in header', () => { it('months other than in valid range should not be shown in header', () => {
const validRange = [Moment('2017-02-02'), Moment('2018-05-18')]; const validRange = [Moment('2017-02-02'), Moment('2018-05-18')];
const wrapper = mount( const wrapper = mount(<Calendar validRange={validRange} />);
<Calendar validRange={validRange} /> wrapper
); .find('.ant-fullcalendar-year-select')
wrapper.find('.ant-fullcalendar-year-select').hostNodes().simulate('click'); .hostNodes()
wrapper.find('.ant-select-dropdown-menu-item').first().simulate('click'); .simulate('click');
wrapper.find('.ant-fullcalendar-month-select').hostNodes().simulate('click'); wrapper
.find('.ant-select-dropdown-menu-item')
.first()
.simulate('click');
wrapper
.find('.ant-fullcalendar-month-select')
.hostNodes()
.simulate('click');
// 2 years and 11 months // 2 years and 11 months
expect(wrapper.find('.ant-select-dropdown-menu-item').length).toBe(13); expect(wrapper.find('.ant-select-dropdown-menu-item').length).toBe(13);
}); });
it('getDateRange should returns a disabledDate function', () => { it('getDateRange should returns a disabledDate function', () => {
const validRange = [Moment('2018-02-02'), Moment('2018-05-18')]; const validRange = [Moment('2018-02-02'), Moment('2018-05-18')];
const wrapper = mount( const wrapper = mount(<Calendar validRange={validRange} defaultValue={Moment('2018-02-02')} />);
<Calendar validRange={validRange} defaultValue={Moment('2018-02-02')} />
);
const instance = wrapper.instance(); const instance = wrapper.instance();
const disabledDate = instance.getDateRange(validRange); const disabledDate = instance.getDateRange(validRange);
expect(disabledDate(Moment('2018-06-02'))).toBe(true); expect(disabledDate(Moment('2018-06-02'))).toBe(true);
@ -79,9 +122,7 @@ describe('Calendar', () => {
it('Calendar should change mode by prop', () => { it('Calendar should change mode by prop', () => {
const monthMode = 'month'; const monthMode = 'month';
const yearMode = 'year'; const yearMode = 'year';
const wrapper = mount( const wrapper = mount(<Calendar />);
<Calendar />
);
expect(wrapper.state().mode).toEqual(monthMode); expect(wrapper.state().mode).toEqual(monthMode);
wrapper.setProps({ mode: 'year' }); wrapper.setProps({ mode: 'year' });
expect(wrapper.state().mode).toEqual(yearMode); expect(wrapper.state().mode).toEqual(yearMode);
@ -91,9 +132,7 @@ describe('Calendar', () => {
const monthMode = 'month'; const monthMode = 'month';
const yearMode = 'year'; const yearMode = 'year';
const onPanelChangeStub = jest.fn(); const onPanelChangeStub = jest.fn();
const wrapper = mount( const wrapper = mount(<Calendar mode={yearMode} onPanelChange={onPanelChangeStub} />);
<Calendar mode={yearMode} onPanelChange={onPanelChangeStub} />
);
expect(wrapper.state().mode).toEqual(yearMode); expect(wrapper.state().mode).toEqual(yearMode);
wrapper.instance().setType('date'); wrapper.instance().setType('date');
expect(wrapper.state().mode).toEqual(monthMode); expect(wrapper.state().mode).toEqual(monthMode);
@ -104,9 +143,7 @@ describe('Calendar', () => {
MockDate.set(Moment('2018-10-19')); MockDate.set(Moment('2018-10-19'));
// eslint-disable-next-line // eslint-disable-next-line
const zhCN = require('../locale/zh_CN').default; const zhCN = require('../locale/zh_CN').default;
const wrapper = mount( const wrapper = mount(<Calendar locale={zhCN} />);
<Calendar locale={zhCN} />
);
expect(wrapper.render()).toMatchSnapshot(); expect(wrapper.render()).toMatchSnapshot();
MockDate.reset(); MockDate.reset();
}); });

View File

@ -10,7 +10,9 @@ import interopDefault from '../_util/interopDefault';
export { HeaderProps } from './Header'; export { HeaderProps } from './Header';
function noop() { return null; } function noop() {
return null;
}
function zerofixed(v: number) { function zerofixed(v: number) {
if (v < 10) { if (v < 10) {
@ -38,7 +40,7 @@ export interface CalendarProps {
onSelect?: (date?: moment.Moment) => void; onSelect?: (date?: moment.Moment) => void;
onChange?: (date?: moment.Moment) => void; onChange?: (date?: moment.Moment) => void;
disabledDate?: (current: moment.Moment) => boolean; disabledDate?: (current: moment.Moment) => boolean;
validRange ?: [moment.Moment, moment.Moment]; validRange?: [moment.Moment, moment.Moment];
} }
export interface CalendarState { export interface CalendarState {
@ -81,7 +83,7 @@ export default class Calendar extends React.Component<CalendarProps, CalendarSta
if (!interopDefault(moment).isMoment(value)) { if (!interopDefault(moment).isMoment(value)) {
throw new Error( throw new Error(
'The value/defaultValue of Calendar must be a moment object after `antd@2.0`, ' + 'The value/defaultValue of Calendar must be a moment object after `antd@2.0`, ' +
'see: https://u.ant.design/calendar-value', 'see: https://u.ant.design/calendar-value',
); );
} }
this.state = { this.state = {
@ -98,7 +100,7 @@ export default class Calendar extends React.Component<CalendarProps, CalendarSta
} }
if ('mode' in nextProps && nextProps.mode !== this.props.mode) { if ('mode' in nextProps && nextProps.mode !== this.props.mode) {
this.setState({ this.setState({
mode: nextProps.mode!, mode: nextProps.mode!,
}); });
} }
} }
@ -108,30 +110,22 @@ export default class Calendar extends React.Component<CalendarProps, CalendarSta
const { prefixCls } = this; const { prefixCls } = this;
return ( return (
<div className={`${prefixCls}-month`}> <div className={`${prefixCls}-month`}>
<div className={`${prefixCls}-value`}> <div className={`${prefixCls}-value`}>{value.localeData().monthsShort(value)}</div>
{value.localeData().monthsShort(value)} <div className={`${prefixCls}-content`}>{monthCellRender(value)}</div>
</div>
<div className={`${prefixCls}-content`}>
{monthCellRender(value)}
</div>
</div> </div>
); );
} };
dateCellRender = (value: moment.Moment) => { dateCellRender = (value: moment.Moment) => {
const { dateCellRender = noop as Function } = this.props; const { dateCellRender = noop as Function } = this.props;
const { prefixCls } = this; const { prefixCls } = this;
return ( return (
<div className={`${prefixCls}-date`}> <div className={`${prefixCls}-date`}>
<div className={`${prefixCls}-value`}> <div className={`${prefixCls}-value`}>{zerofixed(value.date())}</div>
{zerofixed(value.date())} <div className={`${prefixCls}-content`}>{dateCellRender(value)}</div>
</div>
<div className={`${prefixCls}-content`}>
{dateCellRender(value)}
</div>
</div> </div>
); );
} };
setValue = (value: moment.Moment, way: 'select' | 'changePanel') => { setValue = (value: moment.Moment, way: 'select' | 'changePanel') => {
if (!('value' in this.props)) { if (!('value' in this.props)) {
@ -144,23 +138,23 @@ export default class Calendar extends React.Component<CalendarProps, CalendarSta
} else if (way === 'changePanel') { } else if (way === 'changePanel') {
this.onPanelChange(value, this.state.mode); this.onPanelChange(value, this.state.mode);
} }
} };
setType = (type: string) => { setType = (type: string) => {
const mode = (type === 'date') ? 'month' : 'year'; const mode = type === 'date' ? 'month' : 'year';
if (this.state.mode !== mode) { if (this.state.mode !== mode) {
this.setState({ mode }); this.setState({ mode });
this.onPanelChange(this.state.value, mode); this.onPanelChange(this.state.value, mode);
} }
} };
onHeaderValueChange = (value: moment.Moment) => { onHeaderValueChange = (value: moment.Moment) => {
this.setValue(value, 'changePanel'); this.setValue(value, 'changePanel');
} };
onHeaderTypeChange = (type: string) => { onHeaderTypeChange = (type: string) => {
this.setType(type); this.setType(type);
} };
onPanelChange(value: moment.Moment, mode: CalendarMode | undefined) { onPanelChange(value: moment.Moment, mode: CalendarMode | undefined) {
const { onPanelChange, onChange } = this.props; const { onPanelChange, onChange } = this.props;
@ -174,7 +168,7 @@ export default class Calendar extends React.Component<CalendarProps, CalendarSta
onSelect = (value: moment.Moment) => { onSelect = (value: moment.Moment) => {
this.setValue(value, 'select'); this.setValue(value, 'select');
} };
getDateRange = ( getDateRange = (
validRange: [moment.Moment, moment.Moment], validRange: [moment.Moment, moment.Moment],
@ -183,13 +177,13 @@ export default class Calendar extends React.Component<CalendarProps, CalendarSta
if (!current) { if (!current) {
return false; return false;
} }
const [ startDate, endDate ] = validRange; const [startDate, endDate] = validRange;
const inRange = !current.isBetween(startDate, endDate, 'days', '[]'); const inRange = !current.isBetween(startDate, endDate, 'days', '[]');
if (disabledDate) { if (disabledDate) {
return (disabledDate(current) || inRange); return disabledDate(current) || inRange;
} }
return inRange; return inRange;
} };
getDefaultLocale = () => { getDefaultLocale = () => {
const result = { const result = {
@ -201,7 +195,7 @@ export default class Calendar extends React.Component<CalendarProps, CalendarSta
...(this.props.locale || {}).lang, ...(this.props.locale || {}).lang,
}; };
return result; return result;
} };
renderCalendar = (locale: any, localeCode: string) => { renderCalendar = (locale: any, localeCode: string) => {
const { state, props } = this; const { state, props } = this;
@ -211,9 +205,13 @@ export default class Calendar extends React.Component<CalendarProps, CalendarSta
} }
const { const {
prefixCls: customizePrefixCls, prefixCls: customizePrefixCls,
style, className, fullscreen, dateFullCellRender, monthFullCellRender, style,
className,
fullscreen,
dateFullCellRender,
monthFullCellRender,
} = props; } = props;
const type = (mode === 'year') ? 'month' : 'date'; const type = mode === 'year' ? 'month' : 'date';
const monthCellRender = monthFullCellRender || this.monthCellRender; const monthCellRender = monthFullCellRender || this.monthCellRender;
const dateCellRender = dateFullCellRender || this.dateCellRender; const dateCellRender = dateFullCellRender || this.dateCellRender;
@ -236,7 +234,7 @@ export default class Calendar extends React.Component<CalendarProps, CalendarSta
let cls = className || ''; let cls = className || '';
if (fullscreen) { if (fullscreen) {
cls += (` ${prefixCls}-fullscreen`); cls += ` ${prefixCls}-fullscreen`;
} }
return ( return (
@ -269,14 +267,11 @@ export default class Calendar extends React.Component<CalendarProps, CalendarSta
}} }}
</ConfigConsumer> </ConfigConsumer>
); );
} };
render() { render() {
return ( return (
<LocaleReceiver <LocaleReceiver componentName="Calendar" defaultLocale={this.getDefaultLocale}>
componentName="Calendar"
defaultLocale={this.getDefaultLocale}
>
{this.renderCalendar} {this.renderCalendar}
</LocaleReceiver> </LocaleReceiver>
); );

View File

@ -1,7 +1,7 @@
@import "../../style/themes/default"; @import '../../style/themes/default';
@import "../../style/mixins/index"; @import '../../style/mixins/index';
@full-calendar-prefix-cls: ~"@{ant-prefix}-fullcalendar"; @full-calendar-prefix-cls: ~'@{ant-prefix}-fullcalendar';
.@{full-calendar-prefix-cls} { .@{full-calendar-prefix-cls} {
.reset-component; .reset-component;
@ -84,7 +84,7 @@
&-month, &-month,
&-date { &-date {
text-align: center; text-align: center;
transition: all .3s; transition: all 0.3s;
} }
&-value { &-value {
@ -97,7 +97,7 @@
padding: 0; padding: 0;
background: transparent; background: transparent;
line-height: 24px; line-height: 24px;
transition: all .3s; transition: all 0.3s;
&:hover { &:hover {
background: @item-hover-bg; background: @item-hover-bg;
@ -180,7 +180,7 @@
height: 116px; height: 116px;
padding: 4px 8px; padding: 4px 8px;
border-top: 2px solid @border-color-split; border-top: 2px solid @border-color-split;
transition: background .3s; transition: background 0.3s;
&:hover { &:hover {
background: @item-hover-bg; background: @item-hover-bg;

View File

@ -14,18 +14,28 @@ export interface CardMetaProps {
export default (props: CardMetaProps) => ( export default (props: CardMetaProps) => (
<ConfigConsumer> <ConfigConsumer>
{({ getPrefixCls }: ConfigConsumerProps) => { {({ getPrefixCls }: ConfigConsumerProps) => {
const { prefixCls: customizePrefixCls, className, avatar, title, description, ...others } = props; const {
prefixCls: customizePrefixCls,
className,
avatar,
title,
description,
...others
} = props;
const prefixCls = getPrefixCls('card', customizePrefixCls); const prefixCls = getPrefixCls('card', customizePrefixCls);
const classString = classNames(`${prefixCls}-meta`, className); const classString = classNames(`${prefixCls}-meta`, className);
const avatarDom = avatar ? <div className={`${prefixCls}-meta-avatar`}>{avatar}</div> : null; const avatarDom = avatar ? <div className={`${prefixCls}-meta-avatar`}>{avatar}</div> : null;
const titleDom = title ? <div className={`${prefixCls}-meta-title`}>{title}</div> : null; const titleDom = title ? <div className={`${prefixCls}-meta-title`}>{title}</div> : null;
const descriptionDom = description ? const descriptionDom = description ? (
<div className={`${prefixCls}-meta-description`}>{description}</div> : null; <div className={`${prefixCls}-meta-description`}>{description}</div>
const MetaDetail = titleDom || descriptionDom ? ) : null;
<div className={`${prefixCls}-meta-detail`}> const MetaDetail =
{titleDom} titleDom || descriptionDom ? (
{descriptionDom} <div className={`${prefixCls}-meta-detail`}>
</div> : null; {titleDom}
{descriptionDom}
</div>
) : null;
return ( return (
<div {...others} className={classString}> <div {...others} className={classString}>
{avatarDom} {avatarDom}

View File

@ -17,7 +17,9 @@ describe('Card', () => {
function fakeResizeWindowTo(wrapper, width) { function fakeResizeWindowTo(wrapper, width) {
Object.defineProperties(wrapper.instance().container, { Object.defineProperties(wrapper.instance().container, {
offsetWidth: { offsetWidth: {
get() { return width; }, get() {
return width;
},
configurable: true, configurable: true,
}, },
}); });
@ -37,7 +39,11 @@ describe('Card', () => {
}); });
it('should still have padding when card which set padding to 0 is loading', () => { it('should still have padding when card which set padding to 0 is loading', () => {
const wrapper = mount(<Card loading bodyStyle={{ padding: 0 }}>xxx</Card>); const wrapper = mount(
<Card loading bodyStyle={{ padding: 0 }}>
xxx
</Card>,
);
expect(wrapper.render()).toMatchSnapshot(); expect(wrapper.render()).toMatchSnapshot();
}); });
@ -45,7 +51,7 @@ describe('Card', () => {
const wrapper = mount( const wrapper = mount(
<Card title="Card title" extra={<Button>Button</Button>} style={{ width: 300 }}> <Card title="Card title" extra={<Button>Button</Button>} style={{ width: 300 }}>
<p>Card content</p> <p>Card content</p>
</Card> </Card>,
); );
expect(wrapper.render()).toMatchSnapshot(); expect(wrapper.render()).toMatchSnapshot();
}); });

View File

@ -71,7 +71,10 @@ export default class Card extends React.Component<CardProps, CardState> {
!this.props.noHovering, !this.props.noHovering,
'`noHovering` of Card is deprecated, you can remove it safely or use `hoverable` instead.', '`noHovering` of Card is deprecated, you can remove it safely or use `hoverable` instead.',
); );
warning(!!this.props.noHovering, '`noHovering={false}` of Card is deprecated, use `hoverable` instead.'); warning(
!!this.props.noHovering,
'`noHovering={false}` of Card is deprecated, use `hoverable` instead.',
);
} }
} }
@ -105,11 +108,11 @@ export default class Card extends React.Component<CardProps, CardState> {
if (this.props.onTabChange) { if (this.props.onTabChange) {
this.props.onTabChange(key); this.props.onTabChange(key);
} }
} };
saveRef = (node: HTMLDivElement) => { saveRef = (node: HTMLDivElement) => {
this.container = node; this.container = node;
} };
isContainGrid() { isContainGrid() {
let containGrid; let containGrid;
@ -126,11 +129,10 @@ export default class Card extends React.Component<CardProps, CardState> {
return null; return null;
} }
const actionList = actions.map((action, index) => ( const actionList = actions.map((action, index) => (
<li style={{ width: `${100 / actions.length}%` }} key={`action-${index}`}> <li style={{ width: `${100 / actions.length}%` }} key={`action-${index}`}>
<span>{action}</span> <span>{action}</span>
</li> </li>
), ));
);
return actionList; return actionList;
} }
@ -146,8 +148,23 @@ export default class Card extends React.Component<CardProps, CardState> {
renderCard = ({ getPrefixCls }: ConfigConsumerProps) => { renderCard = ({ getPrefixCls }: ConfigConsumerProps) => {
const { const {
prefixCls: customizePrefixCls, prefixCls: customizePrefixCls,
className, extra, headStyle = {}, bodyStyle = {}, noHovering, hoverable, title, loading, className,
bordered = true, type, cover, actions, tabList, children, activeTabKey, defaultActiveTabKey, ...others extra,
headStyle = {},
bodyStyle = {},
noHovering,
hoverable,
title,
loading,
bordered = true,
type,
cover,
actions,
tabList,
children,
activeTabKey,
defaultActiveTabKey,
...others
} = this.props; } = this.props;
const prefixCls = getPrefixCls('card', customizePrefixCls); const prefixCls = getPrefixCls('card', customizePrefixCls);
@ -162,14 +179,11 @@ export default class Card extends React.Component<CardProps, CardState> {
[`${prefixCls}-type-${type}`]: !!type, [`${prefixCls}-type-${type}`]: !!type,
}); });
const loadingBlockStyle = (bodyStyle.padding === 0 || bodyStyle.padding === '0px') const loadingBlockStyle =
? { padding: 24 } : undefined; bodyStyle.padding === 0 || bodyStyle.padding === '0px' ? { padding: 24 } : undefined;
const loadingBlock = ( const loadingBlock = (
<div <div className={`${prefixCls}-loading-content`} style={loadingBlockStyle}>
className={`${prefixCls}-loading-content`}
style={loadingBlockStyle}
>
<Row gutter={8}> <Row gutter={8}>
<Col span={22}> <Col span={22}>
<div className={`${prefixCls}-loading-block`} /> <div className={`${prefixCls}-loading-block`} />
@ -232,16 +246,19 @@ export default class Card extends React.Component<CardProps, CardState> {
}; };
let head; let head;
const tabs = tabList && tabList.length ? ( const tabs =
<Tabs tabList && tabList.length ? (
{...extraProps} <Tabs
className={`${prefixCls}-head-tabs`} {...extraProps}
size="large" className={`${prefixCls}-head-tabs`}
onChange={this.onTabChange} size="large"
> onChange={this.onTabChange}
{tabList.map(item => <Tabs.TabPane tab={item.tab} disabled={item.disabled} key={item.key} />)} >
</Tabs> {tabList.map(item => (
) : null; <Tabs.TabPane tab={item.tab} disabled={item.disabled} key={item.key} />
))}
</Tabs>
) : null;
if (title || extra || tabs) { if (title || extra || tabs) {
head = ( head = (
<div className={`${prefixCls}-head`} style={headStyle}> <div className={`${prefixCls}-head`} style={headStyle}>
@ -259,11 +276,11 @@ export default class Card extends React.Component<CardProps, CardState> {
{loading ? loadingBlock : children} {loading ? loadingBlock : children}
</div> </div>
); );
const actionDom = actions && actions.length ? const actionDom =
<ul className={`${prefixCls}-actions`}>{this.getAction(actions)}</ul> : null; actions && actions.length ? (
const divProps = omit(others, [ <ul className={`${prefixCls}-actions`}>{this.getAction(actions)}</ul>
'onTabChange', ) : null;
]); const divProps = omit(others, ['onTabChange']);
return ( return (
<div {...divProps} className={classString} ref={this.saveRef}> <div {...divProps} className={classString} ref={this.saveRef}>
{head} {head}
@ -272,13 +289,9 @@ export default class Card extends React.Component<CardProps, CardState> {
{actionDom} {actionDom}
</div> </div>
); );
} };
render() { render() {
return ( return <ConfigConsumer>{this.renderCard}</ConfigConsumer>;
<ConfigConsumer>
{this.renderCard}
</ConfigConsumer>
);
} }
} }

View File

@ -1,7 +1,7 @@
@import "../../style/themes/default"; @import '../../style/themes/default';
@import "../../style/mixins/index"; @import '../../style/mixins/index';
@card-prefix-cls: ~"@{ant-prefix}-card"; @card-prefix-cls: ~'@{ant-prefix}-card';
@card-head-height: 48px; @card-head-height: 48px;
@card-hover-border: fade(@black, 9%); @card-hover-border: fade(@black, 9%);
@card-radius: @border-radius-sm; @card-radius: @border-radius-sm;
@ -14,7 +14,7 @@
background: @component-background; background: @component-background;
border-radius: @card-radius; border-radius: @card-radius;
position: relative; position: relative;
transition: all .3s; transition: all 0.3s;
&-hoverable { &-hoverable {
cursor: pointer; cursor: pointer;
@ -91,11 +91,13 @@
&-grid { &-grid {
border-radius: 0; border-radius: 0;
border: 0; border: 0;
box-shadow: 1px 0 0 0 @border-color-split, 0 1px 0 0 @border-color-split, 1px 1px 0 0 @border-color-split, 1px 0 0 0 @border-color-split inset, 0 1px 0 0 @border-color-split inset; box-shadow: 1px 0 0 0 @border-color-split, 0 1px 0 0 @border-color-split,
1px 1px 0 0 @border-color-split, 1px 0 0 0 @border-color-split inset,
0 1px 0 0 @border-color-split inset;
width: 33.33%; width: 33.33%;
float: left; float: left;
padding: @card-padding-base; padding: @card-padding-base;
transition: all .3s; transition: all 0.3s;
&:hover { &:hover {
position: relative; position: relative;
z-index: 1; z-index: 1;
@ -146,7 +148,7 @@
&:hover { &:hover {
color: @primary-color; color: @primary-color;
transition: color .3s; transition: color 0.3s;
} }
& > .anticon { & > .anticon {
@ -182,7 +184,7 @@
&-padding-transition &-head, &-padding-transition &-head,
&-padding-transition &-body { &-padding-transition &-body {
transition: padding .3s; transition: padding 0.3s;
} }
&-type-inner &-head { &-type-inner &-head {
@ -256,9 +258,9 @@
@keyframes card-loading { @keyframes card-loading {
0%, 0%,
100% { 100% {
background-position: 0 50%; background-position: 0 50%;
} }
50% { 50% {
background-position: 100% 50%; background-position: 100% 50%;
} }
} }

View File

@ -12,7 +12,11 @@ describe('Carousel', () => {
}); });
it('should has innerSlider', () => { it('should has innerSlider', () => {
const wrapper = mount(<Carousel><div /></Carousel>); const wrapper = mount(
<Carousel>
<div />
</Carousel>,
);
const { innerSlider } = wrapper.instance(); const { innerSlider } = wrapper.instance();
const innerSliderFromRefs = wrapper.instance().slick.innerSlider; const innerSliderFromRefs = wrapper.instance().slick.innerSlider;
expect(innerSlider).toBe(innerSliderFromRefs); expect(innerSlider).toBe(innerSliderFromRefs);
@ -20,7 +24,13 @@ describe('Carousel', () => {
}); });
it('should has prev, next and go function', () => { it('should has prev, next and go function', () => {
const wrapper = mount(<Carousel><div>1</div><div>2</div><div>3</div></Carousel>); const wrapper = mount(
<Carousel>
<div>1</div>
<div>2</div>
<div>3</div>
</Carousel>,
);
const { prev, next, goTo } = wrapper.instance(); const { prev, next, goTo } = wrapper.instance();
expect(typeof prev).toBe('function'); expect(typeof prev).toBe('function');
expect(typeof next).toBe('function'); expect(typeof next).toBe('function');
@ -39,7 +49,13 @@ describe('Carousel', () => {
it('should trigger autoPlay after window resize', async () => { it('should trigger autoPlay after window resize', async () => {
jest.useRealTimers(); jest.useRealTimers();
const wrapper = mount(<Carousel autoplay><div>1</div><div>2</div><div>3</div></Carousel>); const wrapper = mount(
<Carousel autoplay>
<div>1</div>
<div>2</div>
<div>3</div>
</Carousel>,
);
const spy = jest.spyOn(wrapper.instance().slick.innerSlider, 'autoPlay'); const spy = jest.spyOn(wrapper.instance().slick.innerSlider, 'autoPlay');
window.resizeTo(1000); window.resizeTo(1000);
expect(spy).not.toBeCalled(); expect(spy).not.toBeCalled();
@ -48,7 +64,13 @@ describe('Carousel', () => {
}); });
it('cancel resize listener when unmount', async () => { it('cancel resize listener when unmount', async () => {
const wrapper = mount(<Carousel autoplay><div>1</div><div>2</div><div>3</div></Carousel>); const wrapper = mount(
<Carousel autoplay>
<div>1</div>
<div>2</div>
<div>3</div>
</Carousel>,
);
const { onWindowResized } = wrapper.instance(); const { onWindowResized } = wrapper.instance();
const spy = jest.spyOn(wrapper.instance().onWindowResized, 'cancel'); const spy = jest.spyOn(wrapper.instance().onWindowResized, 'cancel');
const spy2 = jest.spyOn(window, 'removeEventListener'); const spy2 = jest.spyOn(window, 'removeEventListener');

View File

@ -9,10 +9,8 @@ if (typeof window !== 'undefined') {
return { return {
media: mediaQuery, media: mediaQuery,
matches: false, matches: false,
addListener() { addListener() {},
}, removeListener() {},
removeListener() {
},
}; };
}; };
window.matchMedia = window.matchMedia || matchMediaPolyfill; window.matchMedia = window.matchMedia || matchMediaPolyfill;
@ -108,11 +106,11 @@ export default class Carousel extends React.Component<CarouselProps, {}> {
if (autoplay && this.slick && this.slick.innerSlider && this.slick.innerSlider.autoPlay) { if (autoplay && this.slick && this.slick.innerSlider && this.slick.innerSlider.autoPlay) {
this.slick.innerSlider.autoPlay(); this.slick.innerSlider.autoPlay();
} }
} };
saveSlick = (node: any) => { saveSlick = (node: any) => {
this.slick = node; this.slick = node;
} };
next() { next() {
this.slick.slickNext(); this.slick.slickNext();
@ -145,13 +143,9 @@ export default class Carousel extends React.Component<CarouselProps, {}> {
<SlickCarousel ref={this.saveSlick} {...props} /> <SlickCarousel ref={this.saveSlick} {...props} />
</div> </div>
); );
} };
render() { render() {
return ( return <ConfigConsumer>{this.renderCarousel}</ConfigConsumer>;
<ConfigConsumer>
{this.renderCarousel}
</ConfigConsumer>
);
} }
} }

View File

@ -1,5 +1,5 @@
@import "../../style/themes/default"; @import '../../style/themes/default';
@import "../../style/mixins/index"; @import '../../style/mixins/index';
.@{ant-prefix}-carousel { .@{ant-prefix}-carousel {
.reset-component; .reset-component;
@ -49,7 +49,7 @@
&:before, &:before,
&:after { &:after {
content: ""; content: '';
display: table; display: table;
} }
@ -65,7 +65,7 @@
float: left; float: left;
height: 100%; height: 100%;
min-height: 1px; min-height: 1px;
[dir="rtl"] & { [dir='rtl'] & {
float: right; float: right;
} }
img { img {
@ -133,14 +133,14 @@
.slick-prev { .slick-prev {
left: -25px; left: -25px;
&:before { &:before {
content: "←"; content: '←';
} }
} }
.slick-next { .slick-next {
right: -25px; right: -25px;
&:before { &:before {
content: "→"; content: '→';
} }
} }
@ -174,7 +174,7 @@
outline: none; outline: none;
font-size: 0; font-size: 0;
color: transparent; color: transparent;
transition: all .5s; transition: all 0.5s;
padding: 0; padding: 0;
&:hover, &:hover,
&:focus { &:focus {

View File

@ -4,58 +4,79 @@ import KeyCode from 'rc-util/lib/KeyCode';
import Cascader from '..'; import Cascader from '..';
import focusTest from '../../../tests/shared/focusTest'; import focusTest from '../../../tests/shared/focusTest';
const options = [{ const options = [
value: 'zhejiang', {
label: 'Zhejiang', value: 'zhejiang',
children: [{ label: 'Zhejiang',
value: 'hangzhou', children: [
label: 'Hangzhou', {
children: [{ value: 'hangzhou',
value: 'xihu', label: 'Hangzhou',
label: 'West Lake', children: [
}], {
}], value: 'xihu',
}, { label: 'West Lake',
value: 'jiangsu', },
label: 'Jiangsu', ],
children: [{ },
value: 'nanjing', ],
label: 'Nanjing', },
children: [{ {
value: 'zhonghuamen', value: 'jiangsu',
label: 'Zhong Hua Men', label: 'Jiangsu',
}], children: [
}], {
}]; value: 'nanjing',
label: 'Nanjing',
children: [
{
value: 'zhonghuamen',
label: 'Zhong Hua Men',
},
],
},
],
},
];
function filter(inputValue, path) { function filter(inputValue, path) {
return path.some(option => (option.label).toLowerCase().indexOf(inputValue.toLowerCase()) > -1); return path.some(option => option.label.toLowerCase().indexOf(inputValue.toLowerCase()) > -1);
} }
describe('Cascader', () => { describe('Cascader', () => {
focusTest(Cascader); focusTest(Cascader);
it('popup correctly when panel is hidden', () => { it('popup correctly when panel is hidden', () => {
const wrapper = mount( const wrapper = mount(<Cascader options={options} />);
<Cascader options={options} /> expect(
); render(
expect(render(wrapper.find('Trigger').instance().getComponent())).toMatchSnapshot(); wrapper
.find('Trigger')
.instance()
.getComponent(),
),
).toMatchSnapshot();
}); });
it('popup correctly when panel is open', () => { it('popup correctly when panel is open', () => {
const onPopupVisibleChange = jest.fn(); const onPopupVisibleChange = jest.fn();
const wrapper = mount( const wrapper = mount(
<Cascader options={options} onPopupVisibleChange={onPopupVisibleChange} /> <Cascader options={options} onPopupVisibleChange={onPopupVisibleChange} />,
); );
wrapper.find('input').simulate('click'); wrapper.find('input').simulate('click');
expect(render(wrapper.find('Trigger').instance().getComponent())).toMatchSnapshot(); expect(
render(
wrapper
.find('Trigger')
.instance()
.getComponent(),
),
).toMatchSnapshot();
expect(onPopupVisibleChange).toHaveBeenCalledWith(true); expect(onPopupVisibleChange).toHaveBeenCalledWith(true);
}); });
it('support controlled mode', () => { it('support controlled mode', () => {
const wrapper = mount( const wrapper = mount(<Cascader options={options} />);
<Cascader options={options} />
);
wrapper.setProps({ wrapper.setProps({
value: ['zhejiang', 'hangzhou', 'xihu'], value: ['zhejiang', 'hangzhou', 'xihu'],
}); });
@ -63,38 +84,99 @@ describe('Cascader', () => {
}); });
it('popup correctly with defaultValue', () => { it('popup correctly with defaultValue', () => {
const wrapper = mount( const wrapper = mount(<Cascader options={options} defaultValue={['zhejiang', 'hangzhou']} />);
<Cascader options={options} defaultValue={['zhejiang', 'hangzhou']} />
);
wrapper.find('input').simulate('click'); wrapper.find('input').simulate('click');
expect(render(wrapper.find('Trigger').instance().getComponent())).toMatchSnapshot(); expect(
render(
wrapper
.find('Trigger')
.instance()
.getComponent(),
),
).toMatchSnapshot();
}); });
it('should support popupVisible', () => { it('should support popupVisible', () => {
const wrapper = mount( const wrapper = mount(<Cascader options={options} defaultValue={['zhejiang', 'hangzhou']} />);
<Cascader options={options} defaultValue={['zhejiang', 'hangzhou']} /> expect(
); wrapper
expect(wrapper.find('Trigger').instance().getComponent().props.visible).toBe(false); .find('Trigger')
.instance()
.getComponent().props.visible,
).toBe(false);
wrapper.setProps({ popupVisible: true }); wrapper.setProps({ popupVisible: true });
expect(wrapper.find('Trigger').instance().getComponent().props.visible).toBe(true); expect(
wrapper
.find('Trigger')
.instance()
.getComponent().props.visible,
).toBe(true);
}); });
it('can be selected', () => { it('can be selected', () => {
const onChange = jest.fn(); const onChange = jest.fn();
const wrapper = mount(<Cascader options={options} onChange={onChange} />); const wrapper = mount(<Cascader options={options} onChange={onChange} />);
wrapper.find('input').simulate('click'); wrapper.find('input').simulate('click');
let popupWrapper = mount(wrapper.find('Trigger').instance().getComponent()); let popupWrapper = mount(
popupWrapper.find('.ant-cascader-menu').at(0).find('.ant-cascader-menu-item').at(0) wrapper
.find('Trigger')
.instance()
.getComponent(),
);
popupWrapper
.find('.ant-cascader-menu')
.at(0)
.find('.ant-cascader-menu-item')
.at(0)
.simulate('click'); .simulate('click');
expect(render(wrapper.find('Trigger').instance().getComponent())).toMatchSnapshot(); expect(
popupWrapper = mount(wrapper.find('Trigger').instance().getComponent()); render(
popupWrapper.find('.ant-cascader-menu').at(1).find('.ant-cascader-menu-item').at(0) wrapper
.find('Trigger')
.instance()
.getComponent(),
),
).toMatchSnapshot();
popupWrapper = mount(
wrapper
.find('Trigger')
.instance()
.getComponent(),
);
popupWrapper
.find('.ant-cascader-menu')
.at(1)
.find('.ant-cascader-menu-item')
.at(0)
.simulate('click'); .simulate('click');
expect(render(wrapper.find('Trigger').instance().getComponent())).toMatchSnapshot(); expect(
popupWrapper = mount(wrapper.find('Trigger').instance().getComponent()); render(
popupWrapper.find('.ant-cascader-menu').at(2).find('.ant-cascader-menu-item').at(0) wrapper
.find('Trigger')
.instance()
.getComponent(),
),
).toMatchSnapshot();
popupWrapper = mount(
wrapper
.find('Trigger')
.instance()
.getComponent(),
);
popupWrapper
.find('.ant-cascader-menu')
.at(2)
.find('.ant-cascader-menu-item')
.at(0)
.simulate('click'); .simulate('click');
expect(render(wrapper.find('Trigger').instance().getComponent())).toMatchSnapshot(); expect(
render(
wrapper
.find('Trigger')
.instance()
.getComponent(),
),
).toMatchSnapshot();
expect(onChange).toHaveBeenCalledWith(['zhejiang', 'hangzhou', 'xihu'], expect.anything()); expect(onChange).toHaveBeenCalledWith(['zhejiang', 'hangzhou', 'xihu'], expect.anything());
}); });
@ -112,7 +194,12 @@ describe('Cascader', () => {
wrapper.find('input').simulate('click'); wrapper.find('input').simulate('click');
wrapper.find('input').simulate('change', { target: { value: 'z' } }); wrapper.find('input').simulate('change', { target: { value: 'z' } });
expect(wrapper.state('inputValue')).toBe('z'); expect(wrapper.state('inputValue')).toBe('z');
const popupWrapper = mount(wrapper.find('Trigger').instance().getComponent()); const popupWrapper = mount(
wrapper
.find('Trigger')
.instance()
.getComponent(),
);
expect(popupWrapper).toMatchSnapshot(); expect(popupWrapper).toMatchSnapshot();
}); });
@ -121,14 +208,22 @@ describe('Cascader', () => {
wrapper.find('input').simulate('click'); wrapper.find('input').simulate('click');
wrapper.find('input').simulate('change', { target: { value: '__notfoundkeyword__' } }); wrapper.find('input').simulate('change', { target: { value: '__notfoundkeyword__' } });
expect(wrapper.state('inputValue')).toBe('__notfoundkeyword__'); expect(wrapper.state('inputValue')).toBe('__notfoundkeyword__');
const popupWrapper = mount(wrapper.find('Trigger').instance().getComponent()); const popupWrapper = mount(
wrapper
.find('Trigger')
.instance()
.getComponent(),
);
expect(popupWrapper).toMatchSnapshot(); expect(popupWrapper).toMatchSnapshot();
}); });
it('should support to clear selection', () => { it('should support to clear selection', () => {
const wrapper = mount(<Cascader options={options} defaultValue={['zhejiang', 'hangzhou']} />); const wrapper = mount(<Cascader options={options} defaultValue={['zhejiang', 'hangzhou']} />);
expect(wrapper.find('.ant-cascader-picker-label').text()).toBe('Zhejiang / Hangzhou'); expect(wrapper.find('.ant-cascader-picker-label').text()).toBe('Zhejiang / Hangzhou');
wrapper.find('.ant-cascader-picker-clear').at(0).simulate('click'); wrapper
.find('.ant-cascader-picker-clear')
.at(0)
.simulate('click');
expect(wrapper.find('.ant-cascader-picker-label').text()).toBe(''); expect(wrapper.find('.ant-cascader-picker-label').text()).toBe('');
}); });
@ -140,35 +235,33 @@ describe('Cascader', () => {
popupVisible popupVisible
defaultValue={['zhejiang', 'hangzhou']} defaultValue={['zhejiang', 'hangzhou']}
onPopupVisibleChange={onPopupVisibleChange} onPopupVisibleChange={onPopupVisibleChange}
/> />,
); );
wrapper.find('.ant-cascader-picker-clear').at(0).simulate('click'); wrapper
.find('.ant-cascader-picker-clear')
.at(0)
.simulate('click');
expect(onPopupVisibleChange).toHaveBeenCalledWith(false); expect(onPopupVisibleChange).toHaveBeenCalledWith(false);
}); });
it('should clear search input when clear selection', () => { it('should clear search input when clear selection', () => {
const wrapper = mount( const wrapper = mount(
<Cascader <Cascader options={options} defaultValue={['zhejiang', 'hangzhou']} showSearch />,
options={options}
defaultValue={['zhejiang', 'hangzhou']}
showSearch
/>
); );
wrapper.find('input').simulate('click'); wrapper.find('input').simulate('click');
wrapper.find('input').simulate('change', { target: { value: 'xxx' } }); wrapper.find('input').simulate('change', { target: { value: 'xxx' } });
expect(wrapper.state('inputValue')).toBe('xxx'); expect(wrapper.state('inputValue')).toBe('xxx');
wrapper.find('.ant-cascader-picker-clear').at(0).simulate('click'); wrapper
.find('.ant-cascader-picker-clear')
.at(0)
.simulate('click');
expect(wrapper.state('inputValue')).toBe(''); expect(wrapper.state('inputValue')).toBe('');
}); });
it('should not trigger visible change when click search input', () => { it('should not trigger visible change when click search input', () => {
const onPopupVisibleChange = jest.fn(); const onPopupVisibleChange = jest.fn();
const wrapper = mount( const wrapper = mount(
<Cascader <Cascader options={options} showSearch onPopupVisibleChange={onPopupVisibleChange} />,
options={options}
showSearch
onPopupVisibleChange={onPopupVisibleChange}
/>
); );
wrapper.find('input').simulate('focus'); wrapper.find('input').simulate('focus');
expect(onPopupVisibleChange).toHaveBeenCalledTimes(0); expect(onPopupVisibleChange).toHaveBeenCalledTimes(0);
@ -192,29 +285,40 @@ describe('Cascader', () => {
}); });
it('can use fieldNames', () => { it('can use fieldNames', () => {
const customerOptions = [{ const customerOptions = [
code: 'zhejiang', {
name: 'Zhejiang', code: 'zhejiang',
items: [{ name: 'Zhejiang',
code: 'hangzhou', items: [
name: 'Hangzhou', {
items: [{ code: 'hangzhou',
code: 'xihu', name: 'Hangzhou',
name: 'West Lake', items: [
}], {
}], code: 'xihu',
}, { name: 'West Lake',
code: 'jiangsu', },
name: 'Jiangsu', ],
items: [{ },
code: 'nanjing', ],
name: 'Nanjing', },
items: [{ {
code: 'zhonghuamen', code: 'jiangsu',
name: 'Zhong Hua Men', name: 'Jiangsu',
}], items: [
}], {
}]; code: 'nanjing',
name: 'Nanjing',
items: [
{
code: 'zhonghuamen',
name: 'Zhong Hua Men',
},
],
},
],
},
];
const wrapper = mount( const wrapper = mount(
<Cascader <Cascader
options={customerOptions} options={customerOptions}
@ -223,38 +327,54 @@ describe('Cascader', () => {
label: 'name', label: 'name',
value: 'code', value: 'code',
}} }}
/> />,
); );
wrapper.instance().handleChange(['zhejiang', 'hangzhou', 'xihu'], customerOptions); wrapper.instance().handleChange(['zhejiang', 'hangzhou', 'xihu'], customerOptions);
expect(wrapper.find('.ant-cascader-picker-label').text().split('/').length).toBe(3); expect(
wrapper
.find('.ant-cascader-picker-label')
.text()
.split('/').length,
).toBe(3);
}); });
// https://github.com/ant-design/ant-design/issues/12970 // https://github.com/ant-design/ant-design/issues/12970
it('can use filedNames too, for compatibility', () => { it('can use filedNames too, for compatibility', () => {
const errorSpy = jest.spyOn(console, 'error').mockImplementation(() => {}); const errorSpy = jest.spyOn(console, 'error').mockImplementation(() => {});
const customerOptions = [{ const customerOptions = [
code: 'zhejiang', {
name: 'Zhejiang', code: 'zhejiang',
items: [{ name: 'Zhejiang',
code: 'hangzhou', items: [
name: 'Hangzhou', {
items: [{ code: 'hangzhou',
code: 'xihu', name: 'Hangzhou',
name: 'West Lake', items: [
}], {
}], code: 'xihu',
}, { name: 'West Lake',
code: 'jiangsu', },
name: 'Jiangsu', ],
items: [{ },
code: 'nanjing', ],
name: 'Nanjing', },
items: [{ {
code: 'zhonghuamen', code: 'jiangsu',
name: 'Zhong Hua Men', name: 'Jiangsu',
}], items: [
}], {
}]; code: 'nanjing',
name: 'Nanjing',
items: [
{
code: 'zhonghuamen',
name: 'Zhong Hua Men',
},
],
},
],
},
];
const wrapper = mount( const wrapper = mount(
<Cascader <Cascader
options={customerOptions} options={customerOptions}
@ -263,12 +383,17 @@ describe('Cascader', () => {
label: 'name', label: 'name',
value: 'code', value: 'code',
}} }}
/> />,
); );
wrapper.instance().handleChange(['zhejiang', 'hangzhou', 'xihu'], customerOptions); wrapper.instance().handleChange(['zhejiang', 'hangzhou', 'xihu'], customerOptions);
expect(wrapper.find('.ant-cascader-picker-label').text().split('/').length).toBe(3); expect(
wrapper
.find('.ant-cascader-picker-label')
.text()
.split('/').length,
).toBe(3);
expect(errorSpy).toHaveBeenLastCalledWith( expect(errorSpy).toHaveBeenLastCalledWith(
'Warning: `filedNames` of Cascader is a typo usage and deprecated, please use `fieldNames` instead.' 'Warning: `filedNames` of Cascader is a typo usage and deprecated, please use `fieldNames` instead.',
); );
errorSpy.mockReset(); errorSpy.mockReset();
}); });
@ -300,7 +425,7 @@ describe('Cascader', () => {
wrapper.find('input').simulate('change', { target: { value: 'a' } }); wrapper.find('input').simulate('change', { target: { value: 'a' } });
expect(wrapper.find('.ant-cascader-menu-item').length).toBe(2); expect(wrapper.find('.ant-cascader-menu-item').length).toBe(2);
expect(errorSpy).toBeCalledWith( expect(errorSpy).toBeCalledWith(
'Warning: \'limit\' of showSearch in Cascader should be positive number or false.' "Warning: 'limit' of showSearch in Cascader should be positive number or false.",
); );
}); });
}); });

View File

@ -40,7 +40,12 @@ export interface ShowSearchType {
prefixCls: string | undefined, prefixCls: string | undefined,
names: FilledFieldNamesType, names: FilledFieldNamesType,
) => React.ReactNode; ) => React.ReactNode;
sort?: (a: CascaderOptionType[], b: CascaderOptionType[], inputValue: string, names: FilledFieldNamesType) => number; sort?: (
a: CascaderOptionType[],
b: CascaderOptionType[],
inputValue: string,
names: FilledFieldNamesType,
) => number;
matchInputWidth?: boolean; matchInputWidth?: boolean;
limit?: number | false; limit?: number | false;
} }
@ -104,14 +109,23 @@ export interface CascaderState {
const defaultLimit = 50; const defaultLimit = 50;
function highlightKeyword(str: string, keyword: string, prefixCls: string | undefined) { function highlightKeyword(str: string, keyword: string, prefixCls: string | undefined) {
return str.split(keyword) return str.split(keyword).map((node: string, index: number) =>
.map((node: string, index: number) => index === 0 ? node : [ index === 0
<span className={`${prefixCls}-menu-item-keyword`} key="seperator">{keyword}</span>, ? node
node, : [
]); <span className={`${prefixCls}-menu-item-keyword`} key="seperator">
{keyword}
</span>,
node,
],
);
} }
function defaultFilterOption(inputValue: string, path: CascaderOptionType[], names: FilledFieldNamesType) { function defaultFilterOption(
inputValue: string,
path: CascaderOptionType[],
names: FilledFieldNamesType,
) {
return path.some(option => (option[names.label] as string).indexOf(inputValue) > -1); return path.some(option => (option[names.label] as string).indexOf(inputValue) > -1);
} }
@ -123,14 +137,19 @@ function defaultRenderFilteredOption(
) { ) {
return path.map((option, index) => { return path.map((option, index) => {
const label = option[names.label]; const label = option[names.label];
const node = (label as string).indexOf(inputValue) > -1 ? const node =
highlightKeyword(label as string, inputValue, prefixCls) : label; (label as string).indexOf(inputValue) > -1
? highlightKeyword(label as string, inputValue, prefixCls)
: label;
return index === 0 ? node : [' / ', node]; return index === 0 ? node : [' / ', node];
}); });
} }
function defaultSortFilteredOption( function defaultSortFilteredOption(
a: CascaderOptionType[], b: CascaderOptionType[], inputValue: string, names: FilledFieldNamesType, a: CascaderOptionType[],
b: CascaderOptionType[],
inputValue: string,
names: FilledFieldNamesType,
) { ) {
function callback(elem: CascaderOptionType) { function callback(elem: CascaderOptionType) {
return (elem[names.label] as string).indexOf(inputValue) > -1; return (elem[names.label] as string).indexOf(inputValue) > -1;
@ -181,8 +200,7 @@ export default class Cascader extends React.Component<CascaderProps, CascaderSta
inputValue: '', inputValue: '',
inputFocused: false, inputFocused: false,
popupVisible: props.popupVisible, popupVisible: props.popupVisible,
flattenOptions: flattenOptions: props.showSearch ? this.flattenTree(props.options, props) : undefined,
props.showSearch ? this.flattenTree(props.options, props) : undefined,
}; };
} }
@ -209,7 +227,7 @@ export default class Cascader extends React.Component<CascaderProps, CascaderSta
return; return;
} }
this.setValue(value, selectedOptions); this.setValue(value, selectedOptions);
} };
handlePopupVisibleChange = (popupVisible: boolean) => { handlePopupVisibleChange = (popupVisible: boolean) => {
if (!('popupVisible' in this.props)) { if (!('popupVisible' in this.props)) {
@ -224,13 +242,13 @@ export default class Cascader extends React.Component<CascaderProps, CascaderSta
if (onPopupVisibleChange) { if (onPopupVisibleChange) {
onPopupVisibleChange(popupVisible); onPopupVisibleChange(popupVisible);
} }
} };
handleInputBlur = () => { handleInputBlur = () => {
this.setState({ this.setState({
inputFocused: false, inputFocused: false,
}); });
} };
handleInputClick = (e: React.MouseEvent<HTMLInputElement>) => { handleInputClick = (e: React.MouseEvent<HTMLInputElement>) => {
const { inputFocused, popupVisible } = this.state; const { inputFocused, popupVisible } = this.state;
@ -241,18 +259,18 @@ export default class Cascader extends React.Component<CascaderProps, CascaderSta
e.nativeEvent.stopImmediatePropagation(); e.nativeEvent.stopImmediatePropagation();
} }
} }
} };
handleKeyDown = (e: React.KeyboardEvent<HTMLInputElement>) => { handleKeyDown = (e: React.KeyboardEvent<HTMLInputElement>) => {
if (e.keyCode === KeyCode.BACKSPACE) { if (e.keyCode === KeyCode.BACKSPACE) {
e.stopPropagation(); e.stopPropagation();
} }
} };
handleInputChange = (e: React.ChangeEvent<HTMLInputElement>) => { handleInputChange = (e: React.ChangeEvent<HTMLInputElement>) => {
const inputValue = e.target.value; const inputValue = e.target.value;
this.setState({ inputValue }); this.setState({ inputValue });
} };
setValue = (value: string[], selectedOptions: CascaderOptionType[] = []) => { setValue = (value: string[], selectedOptions: CascaderOptionType[] = []) => {
if (!('value' in this.props)) { if (!('value' in this.props)) {
@ -262,14 +280,15 @@ export default class Cascader extends React.Component<CascaderProps, CascaderSta
if (onChange) { if (onChange) {
onChange(value, selectedOptions); onChange(value, selectedOptions);
} }
} };
getLabel() { getLabel() {
const { options, displayRender = defaultDisplayRender as Function } = this.props; const { options, displayRender = defaultDisplayRender as Function } = this.props;
const names = getFilledFieldNames(this.props); const names = getFilledFieldNames(this.props);
const value = this.state.value; const value = this.state.value;
const unwrappedValue = Array.isArray(value[0]) ? value[0] : value; const unwrappedValue = Array.isArray(value[0]) ? value[0] : value;
const selectedOptions: CascaderOptionType[] = arrayTreeFilter(options, const selectedOptions: CascaderOptionType[] = arrayTreeFilter(
options,
(o: CascaderOptionType, level: number) => o[names.value] === unwrappedValue[level], (o: CascaderOptionType, level: number) => o[names.value] === unwrappedValue[level],
{ childrenKeyName: names.children }, { childrenKeyName: names.children },
); );
@ -286,7 +305,7 @@ export default class Cascader extends React.Component<CascaderProps, CascaderSta
} else { } else {
this.setState({ inputValue: '' }); this.setState({ inputValue: '' });
} }
} };
flattenTree( flattenTree(
options: CascaderOptionType[], options: CascaderOptionType[],
@ -296,19 +315,13 @@ export default class Cascader extends React.Component<CascaderProps, CascaderSta
const names: FilledFieldNamesType = getFilledFieldNames(props); const names: FilledFieldNamesType = getFilledFieldNames(props);
let flattenOptions = [] as CascaderOptionType[][]; let flattenOptions = [] as CascaderOptionType[][];
const childrenName = names.children; const childrenName = names.children;
options.forEach((option) => { options.forEach(option => {
const path = ancestor.concat(option); const path = ancestor.concat(option);
if (props.changeOnSelect || !option[childrenName] || !option[childrenName].length) { if (props.changeOnSelect || !option[childrenName] || !option[childrenName].length) {
flattenOptions.push(path); flattenOptions.push(path);
} }
if (option[childrenName]) { if (option[childrenName]) {
flattenOptions = flattenOptions.concat( flattenOptions = flattenOptions.concat(this.flattenTree(option[childrenName], props, path));
this.flattenTree(
option[childrenName],
props,
path,
),
);
} }
}); });
return flattenOptions; return flattenOptions;
@ -332,7 +345,7 @@ export default class Cascader extends React.Component<CascaderProps, CascaderSta
let matchCount = 0; let matchCount = 0;
// Perf optimization to filter items only below the limit // Perf optimization to filter items only below the limit
flattenOptions.some((path) => { flattenOptions.some(path => {
const match = filter(this.state.inputValue, path, names); const match = filter(this.state.inputValue, path, names);
if (match) { if (match) {
filtered.push(path); filtered.push(path);
@ -343,9 +356,9 @@ export default class Cascader extends React.Component<CascaderProps, CascaderSta
} else { } else {
warning( warning(
typeof limit !== 'number', typeof limit !== 'number',
'\'limit\' of showSearch in Cascader should be positive number or false.', "'limit' of showSearch in Cascader should be positive number or false.",
); );
filtered = flattenOptions.filter((path) => filter(this.state.inputValue, path, names)); filtered = flattenOptions.filter(path => filter(this.state.inputValue, path, names));
} }
filtered.sort((a, b) => sort(a, b, inputValue, names)); filtered.sort((a, b) => sort(a, b, inputValue, names));
@ -361,7 +374,9 @@ export default class Cascader extends React.Component<CascaderProps, CascaderSta
} as CascaderOptionType; } as CascaderOptionType;
}); });
} }
return [{ [names.label]: notFoundContent, [names.value]: 'ANT_CASCADER_NOT_FOUND', disabled: true }]; return [
{ [names.label]: notFoundContent, [names.value]: 'ANT_CASCADER_NOT_FOUND', disabled: true },
];
} }
focus() { focus() {
@ -374,14 +389,26 @@ export default class Cascader extends React.Component<CascaderProps, CascaderSta
saveInput = (node: Input) => { saveInput = (node: Input) => {
this.input = node; this.input = node;
} };
renderCascader = ({ getPopupContainer: getContextPopupContainer, getPrefixCls }: ConfigConsumerProps) => { renderCascader = ({
getPopupContainer: getContextPopupContainer,
getPrefixCls,
}: ConfigConsumerProps) => {
const { props, state } = this; const { props, state } = this;
const { const {
prefixCls: customizePrefixCls, inputPrefixCls: customizeInputPrefixCls, prefixCls: customizePrefixCls,
children, placeholder, size, disabled, inputPrefixCls: customizeInputPrefixCls,
className, style, allowClear, showSearch = false, suffixIcon, ...otherProps children,
placeholder,
size,
disabled,
className,
style,
allowClear,
showSearch = false,
suffixIcon,
...otherProps
} = props; } = props;
const { value, inputFocused } = state; const { value, inputFocused } = state;
@ -392,26 +419,26 @@ export default class Cascader extends React.Component<CascaderProps, CascaderSta
[`${inputPrefixCls}-lg`]: size === 'large', [`${inputPrefixCls}-lg`]: size === 'large',
[`${inputPrefixCls}-sm`]: size === 'small', [`${inputPrefixCls}-sm`]: size === 'small',
}); });
const clearIcon = (allowClear && !disabled && value.length > 0) || state.inputValue ? ( const clearIcon =
<Icon (allowClear && !disabled && value.length > 0) || state.inputValue ? (
type="close-circle" <Icon
theme="filled" type="close-circle"
className={`${prefixCls}-picker-clear`} theme="filled"
onClick={this.clearSelection} className={`${prefixCls}-picker-clear`}
/> onClick={this.clearSelection}
) : null; />
) : null;
const arrowCls = classNames({ const arrowCls = classNames({
[`${prefixCls}-picker-arrow`]: true, [`${prefixCls}-picker-arrow`]: true,
[`${prefixCls}-picker-arrow-expand`]: state.popupVisible, [`${prefixCls}-picker-arrow-expand`]: state.popupVisible,
}); });
const pickerCls = classNames( const pickerCls = classNames(className, `${prefixCls}-picker`, {
className, `${prefixCls}-picker`, { [`${prefixCls}-picker-with-value`]: state.inputValue,
[`${prefixCls}-picker-with-value`]: state.inputValue, [`${prefixCls}-picker-disabled`]: disabled,
[`${prefixCls}-picker-disabled`]: disabled, [`${prefixCls}-picker-${size}`]: !!size,
[`${prefixCls}-picker-${size}`]: !!size, [`${prefixCls}-picker-show-search`]: !!showSearch,
[`${prefixCls}-picker-show-search`]: !!showSearch, [`${prefixCls}-picker-focused`]: inputFocused,
[`${prefixCls}-picker-focused`]: inputFocused, });
});
// Fix bug of https://github.com/facebook/react/pull/5004 // Fix bug of https://github.com/facebook/react/pull/5004
// and https://fb.me/react-unknown-prop // and https://fb.me/react-unknown-prop
@ -447,39 +474,34 @@ export default class Cascader extends React.Component<CascaderProps, CascaderSta
this.cachedOptions = options; this.cachedOptions = options;
} }
const dropdownMenuColumnStyle: { width?: number, height?: string } = {}; const dropdownMenuColumnStyle: { width?: number; height?: string } = {};
const isNotFound = (options || []).length === 1 && options[0].value === 'ANT_CASCADER_NOT_FOUND'; const isNotFound =
(options || []).length === 1 && options[0].value === 'ANT_CASCADER_NOT_FOUND';
if (isNotFound) { if (isNotFound) {
dropdownMenuColumnStyle.height = 'auto'; // Height of one row. dropdownMenuColumnStyle.height = 'auto'; // Height of one row.
} }
// The default value of `matchInputWidth` is `true` // The default value of `matchInputWidth` is `true`
const resultListMatchInputWidth = (showSearch as ShowSearchType).matchInputWidth === false ? false : true; const resultListMatchInputWidth =
(showSearch as ShowSearchType).matchInputWidth === false ? false : true;
if (resultListMatchInputWidth && state.inputValue && this.input) { if (resultListMatchInputWidth && state.inputValue && this.input) {
dropdownMenuColumnStyle.width = this.input.input.offsetWidth; dropdownMenuColumnStyle.width = this.input.input.offsetWidth;
} }
const inputIcon = suffixIcon && ( const inputIcon = (suffixIcon &&
React.isValidElement<{ className?: string }>(suffixIcon) (React.isValidElement<{ className?: string }>(suffixIcon) ? (
? React.cloneElement( React.cloneElement(suffixIcon, {
suffixIcon, className: classNames({
{ [suffixIcon.props.className!]: suffixIcon.props.className,
className: classNames({ [`${prefixCls}-picker-arrow`]: true,
[suffixIcon.props.className!]: suffixIcon.props.className, }),
[`${prefixCls}-picker-arrow`]: true, })
}), ) : (
}, <span className={`${prefixCls}-picker-arrow`}>{suffixIcon}</span>
) : <span className={`${prefixCls}-picker-arrow`}>{suffixIcon}</span>) || ( ))) || <Icon type="down" className={arrowCls} />;
<Icon type="down" className={arrowCls} />
);
const input = children || ( const input = children || (
<span <span style={style} className={pickerCls}>
style={style} <span className={`${prefixCls}-picker-label`}>{this.getLabel()}</span>
className={pickerCls}
>
<span className={`${prefixCls}-picker-label`}>
{this.getLabel()}
</span>
<Input <Input
{...inputProps} {...inputProps}
ref={this.saveInput} ref={this.saveInput}
@ -500,9 +522,7 @@ export default class Cascader extends React.Component<CascaderProps, CascaderSta
</span> </span>
); );
const expandIcon = ( const expandIcon = <Icon type="right" />;
<Icon type="right" />
);
const loadingIcon = ( const loadingIcon = (
<span className={`${prefixCls}-menu-item-loading-icon`}> <span className={`${prefixCls}-menu-item-loading-icon`}>
@ -530,13 +550,9 @@ export default class Cascader extends React.Component<CascaderProps, CascaderSta
{input} {input}
</RcCascader> </RcCascader>
); );
} };
render() { render() {
return ( return <ConfigConsumer>{this.renderCascader}</ConfigConsumer>;
<ConfigConsumer>
{this.renderCascader}
</ConfigConsumer>
);
} }
} }

View File

@ -1,8 +1,8 @@
@import "../../style/themes/default"; @import '../../style/themes/default';
@import "../../style/mixins/index"; @import '../../style/mixins/index';
@import "../../input/style/mixin"; @import '../../input/style/mixin';
@cascader-prefix-cls: ~"@{ant-prefix}-cascader"; @cascader-prefix-cls: ~'@{ant-prefix}-cascader';
.@{cascader-prefix-cls} { .@{cascader-prefix-cls} {
.reset-component; .reset-component;
@ -28,7 +28,7 @@
background-color: @component-background; background-color: @component-background;
border-radius: @border-radius-base; border-radius: @border-radius-base;
outline: 0; outline: 0;
transition: color .3s; transition: color 0.3s;
&-with-value &-label { &-with-value &-label {
color: transparent; color: transparent;
@ -101,7 +101,7 @@
margin-top: -6px; margin-top: -6px;
line-height: 12px; line-height: 12px;
color: @disabled-color; color: @disabled-color;
transition: transform .2s; transition: transform 0.2s;
&&-expand { &&-expand {
transform: rotate(180deg); transform: rotate(180deg);
} }

View File

@ -53,10 +53,16 @@ export default class Checkbox extends React.Component<CheckboxProps, {}, {}> {
private rcCheckbox: any; private rcCheckbox: any;
shouldComponentUpdate(nextProps: CheckboxProps, nextState: {}, nextContext: CheckboxGroupContext) { shouldComponentUpdate(
return !shallowEqual(this.props, nextProps) || nextProps: CheckboxProps,
!shallowEqual(this.state, nextState) || nextState: {},
!shallowEqual(this.context.checkboxGroup, nextContext.checkboxGroup); nextContext: CheckboxGroupContext,
) {
return (
!shallowEqual(this.props, nextProps) ||
!shallowEqual(this.state, nextState) ||
!shallowEqual(this.context.checkboxGroup, nextContext.checkboxGroup)
);
} }
focus() { focus() {
@ -69,7 +75,7 @@ export default class Checkbox extends React.Component<CheckboxProps, {}, {}> {
saveCheckbox = (node: any) => { saveCheckbox = (node: any) => {
this.rcCheckbox = node; this.rcCheckbox = node;
} };
renderCheckbox = ({ getPrefixCls }: ConfigConsumerProps) => { renderCheckbox = ({ getPrefixCls }: ConfigConsumerProps) => {
const { props, context } = this; const { props, context } = this;
@ -120,13 +126,9 @@ export default class Checkbox extends React.Component<CheckboxProps, {}, {}> {
{children !== undefined && <span>{children}</span>} {children !== undefined && <span>{children}</span>}
</label> </label>
); );
} };
render() { render() {
return ( return <ConfigConsumer>{this.renderCheckbox}</ConfigConsumer>;
<ConfigConsumer>
{this.renderCheckbox}
</ConfigConsumer>
);
} }
} }

View File

@ -71,7 +71,7 @@ class CheckboxGroup extends React.Component<CheckboxGroupProps, CheckboxGroupSta
super(props); super(props);
this.state = { this.state = {
value: props.value || props.defaultValue || [], value: props.value || props.defaultValue || [],
}; };
} }
getChildContext() { getChildContext() {
@ -85,8 +85,7 @@ class CheckboxGroup extends React.Component<CheckboxGroupProps, CheckboxGroupSta
} }
shouldComponentUpdate(nextProps: CheckboxGroupProps, nextState: CheckboxGroupState) { shouldComponentUpdate(nextProps: CheckboxGroupProps, nextState: CheckboxGroupState) {
return !shallowEqual(this.props, nextProps) || return !shallowEqual(this.props, nextProps) || !shallowEqual(this.state, nextState);
!shallowEqual(this.state, nextState);
} }
getOptions() { getOptions() {
@ -106,7 +105,7 @@ class CheckboxGroup extends React.Component<CheckboxGroupProps, CheckboxGroupSta
toggleOption = (option: CheckboxOptionType) => { toggleOption = (option: CheckboxOptionType) => {
const optionIndex = this.state.value.indexOf(option.value); const optionIndex = this.state.value.indexOf(option.value);
const value = [...this.state.value]; const value = [...this.state.value];
if (optionIndex === - 1) { if (optionIndex === -1) {
value.push(option.value); value.push(option.value);
} else { } else {
value.splice(optionIndex, 1); value.splice(optionIndex, 1);
@ -118,14 +117,11 @@ class CheckboxGroup extends React.Component<CheckboxGroupProps, CheckboxGroupSta
if (onChange) { if (onChange) {
onChange(value); onChange(value);
} }
} };
renderGroup = ({ getPrefixCls }: ConfigConsumerProps) => { renderGroup = ({ getPrefixCls }: ConfigConsumerProps) => {
const { props, state } = this; const { props, state } = this;
const { const { prefixCls: customizePrefixCls, className, style, options, ...restProps } = props;
prefixCls: customizePrefixCls,
className, style, options, ...restProps
} = props;
const prefixCls = getPrefixCls('checkbox', customizePrefixCls); const prefixCls = getPrefixCls('checkbox', customizePrefixCls);
const groupPrefixCls = `${prefixCls}-group`; const groupPrefixCls = `${prefixCls}-group`;
@ -154,14 +150,10 @@ class CheckboxGroup extends React.Component<CheckboxGroupProps, CheckboxGroupSta
{children} {children}
</div> </div>
); );
} };
render() { render() {
return ( return <ConfigConsumer>{this.renderGroup}</ConfigConsumer>;
<ConfigConsumer>
{this.renderGroup}
</ConfigConsumer>
);
} }
} }

View File

@ -10,12 +10,7 @@ describe('Checkbox', () => {
const onMouseEnter = jest.fn(); const onMouseEnter = jest.fn();
const onMouseLeave = jest.fn(); const onMouseLeave = jest.fn();
const wrapper = mount( const wrapper = mount(<Checkbox onMouseEnter={onMouseEnter} onMouseLeave={onMouseLeave} />);
<Checkbox
onMouseEnter={onMouseEnter}
onMouseLeave={onMouseLeave}
/>
);
wrapper.find('label').simulate('mouseenter'); wrapper.find('label').simulate('mouseenter');
expect(onMouseEnter).toHaveBeenCalled(); expect(onMouseEnter).toHaveBeenCalled();

View File

@ -6,32 +6,47 @@ describe('CheckboxGroup', () => {
it('should work basically', () => { it('should work basically', () => {
const onChange = jest.fn(); const onChange = jest.fn();
const wrapper = mount( const wrapper = mount(
<Checkbox.Group options={['Apple', 'Pear', 'Orange']} onChange={onChange} /> <Checkbox.Group options={['Apple', 'Pear', 'Orange']} onChange={onChange} />,
); );
wrapper.find('.ant-checkbox-input').at(0).simulate('change'); wrapper
.find('.ant-checkbox-input')
.at(0)
.simulate('change');
expect(onChange).toBeCalledWith(['Apple']); expect(onChange).toBeCalledWith(['Apple']);
wrapper.find('.ant-checkbox-input').at(1).simulate('change'); wrapper
.find('.ant-checkbox-input')
.at(1)
.simulate('change');
expect(onChange).toBeCalledWith(['Apple', 'Pear']); expect(onChange).toBeCalledWith(['Apple', 'Pear']);
wrapper.find('.ant-checkbox-input').at(2).simulate('change'); wrapper
.find('.ant-checkbox-input')
.at(2)
.simulate('change');
expect(onChange).toBeCalledWith(['Apple', 'Pear', 'Orange']); expect(onChange).toBeCalledWith(['Apple', 'Pear', 'Orange']);
wrapper.find('.ant-checkbox-input').at(1).simulate('change'); wrapper
.find('.ant-checkbox-input')
.at(1)
.simulate('change');
expect(onChange).toBeCalledWith(['Apple', 'Orange']); expect(onChange).toBeCalledWith(['Apple', 'Orange']);
}); });
it('does not trigger onChange callback of both Checkbox and CheckboxGroup when CheckboxGroup is disabled', () => { it('does not trigger onChange callback of both Checkbox and CheckboxGroup when CheckboxGroup is disabled', () => {
const onChangeGroup = jest.fn(); const onChangeGroup = jest.fn();
const options = [ const options = [{ label: 'Apple', value: 'Apple' }, { label: 'Pear', value: 'Pear' }];
{ label: 'Apple', value: 'Apple' },
{ label: 'Pear', value: 'Pear' },
];
const groupWrapper = mount( const groupWrapper = mount(
<Checkbox.Group options={options} onChange={onChangeGroup} disabled /> <Checkbox.Group options={options} onChange={onChangeGroup} disabled />,
); );
groupWrapper.find('.ant-checkbox-input').at(0).simulate('change'); groupWrapper
.find('.ant-checkbox-input')
.at(0)
.simulate('change');
expect(onChangeGroup).not.toBeCalled(); expect(onChangeGroup).not.toBeCalled();
groupWrapper.find('.ant-checkbox-input').at(1).simulate('change'); groupWrapper
.find('.ant-checkbox-input')
.at(1)
.simulate('change');
expect(onChangeGroup).not.toBeCalled(); expect(onChangeGroup).not.toBeCalled();
}); });
@ -43,37 +58,31 @@ describe('CheckboxGroup', () => {
{ label: 'Orange', value: 'Orange', disabled: true }, { label: 'Orange', value: 'Orange', disabled: true },
]; ];
const groupWrapper = mount( const groupWrapper = mount(<Checkbox.Group options={options} onChange={onChangeGroup} />);
<Checkbox.Group options={options} onChange={onChangeGroup} /> groupWrapper
); .find('.ant-checkbox-input')
groupWrapper.find('.ant-checkbox-input').at(0).simulate('change'); .at(0)
.simulate('change');
expect(onChangeGroup).toBeCalledWith(['Apple']); expect(onChangeGroup).toBeCalledWith(['Apple']);
groupWrapper.find('.ant-checkbox-input').at(1).simulate('change'); groupWrapper
.find('.ant-checkbox-input')
.at(1)
.simulate('change');
expect(onChangeGroup).toBeCalledWith(['Apple']); expect(onChangeGroup).toBeCalledWith(['Apple']);
}); });
it('passes prefixCls down to checkbox', () => { it('passes prefixCls down to checkbox', () => {
const options = [ const options = [{ label: 'Apple', value: 'Apple' }, { label: 'Orange', value: 'Orange' }];
{ label: 'Apple', value: 'Apple' },
{ label: 'Orange', value: 'Orange' },
];
const wrapper = render( const wrapper = render(<Checkbox.Group prefixCls="my-checkbox" options={options} />);
<Checkbox.Group prefixCls="my-checkbox" options={options} />
);
expect(wrapper).toMatchSnapshot(); expect(wrapper).toMatchSnapshot();
}); });
it('should be controlled by value', () => { it('should be controlled by value', () => {
const options = [ const options = [{ label: 'Apple', value: 'Apple' }, { label: 'Orange', value: 'Orange' }];
{ label: 'Apple', value: 'Apple' },
{ label: 'Orange', value: 'Orange' },
];
const wrapper = mount( const wrapper = mount(<Checkbox.Group options={options} />);
<Checkbox.Group options={options} />
);
expect(wrapper.instance().state.value).toEqual([]); expect(wrapper.instance().state.value).toEqual([]);
wrapper.setProps({ value: ['Apple'] }); wrapper.setProps({ value: ['Apple'] });
@ -86,9 +95,12 @@ describe('CheckboxGroup', () => {
const wrapper = mount( const wrapper = mount(
<Checkbox.Group> <Checkbox.Group>
<Checkbox value="my" onChange={onChange} /> <Checkbox value="my" onChange={onChange} />
</Checkbox.Group> </Checkbox.Group>,
); );
wrapper.find('.ant-checkbox-input').at(0).simulate('change'); wrapper
.find('.ant-checkbox-input')
.at(0)
.simulate('change');
expect(onChange).toBeCalled(); expect(onChange).toBeCalled();
expect(onChange.mock.calls[0][0].target.value).toEqual('my'); expect(onChange.mock.calls[0][0].target.value).toEqual('my');
}); });

View File

@ -1,4 +1,4 @@
@import "../../style/themes/default"; @import '../../style/themes/default';
@import "./mixin"; @import './mixin';
.antCheckboxFn(); .antCheckboxFn();

View File

@ -1,7 +1,7 @@
@import "../../style/mixins/index"; @import '../../style/mixins/index';
.antCheckboxFn(@checkbox-prefix-cls: ~"@{ant-prefix}-checkbox") { .antCheckboxFn(@checkbox-prefix-cls: ~'@{ant-prefix}-checkbox') {
@checkbox-inner-prefix-cls: ~"@{checkbox-prefix-cls}-inner"; @checkbox-inner-prefix-cls: ~'@{checkbox-prefix-cls}-inner';
// 一般状态 // 一般状态
.@{checkbox-prefix-cls} { .@{checkbox-prefix-cls} {
.reset-component; .reset-component;
@ -28,7 +28,7 @@
height: 100%; height: 100%;
border-radius: @border-radius-sm; border-radius: @border-radius-sm;
border: 1px solid @checkbox-color; border: 1px solid @checkbox-color;
content: ""; content: '';
animation: antCheckboxEffect 0.36s ease-in-out; animation: antCheckboxEffect 0.36s ease-in-out;
animation-fill-mode: both; animation-fill-mode: both;
visibility: hidden; visibility: hidden;
@ -49,7 +49,7 @@
border: @checkbox-border-width @border-style-base @border-color-base; border: @checkbox-border-width @border-style-base @border-color-base;
border-radius: @border-radius-sm; border-radius: @border-radius-sm;
background-color: @checkbox-check-color; background-color: @checkbox-check-color;
transition: all .3s; transition: all 0.3s;
// Fix IE checked style // Fix IE checked style
// https://github.com/ant-design/ant-design/issues/12597 // https://github.com/ant-design/ant-design/issues/12597
border-collapse: separate; border-collapse: separate;
@ -68,7 +68,7 @@
border-top: 0; border-top: 0;
border-left: 0; border-left: 0;
content: ' '; content: ' ';
transition: all .1s @ease-in-back, opacity .1s; transition: all 0.1s @ease-in-back, opacity 0.1s;
opacity: 0; opacity: 0;
} }
} }
@ -96,7 +96,7 @@
border-top: 0; border-top: 0;
border-left: 0; border-left: 0;
content: ' '; content: ' ';
transition: all .2s @ease-out-back .1s; transition: all 0.2s @ease-out-back 0.1s;
opacity: 1; opacity: 1;
} }

View File

@ -24,21 +24,22 @@ export default class Collapse extends React.Component<CollapseProps, any> {
static defaultProps = { static defaultProps = {
bordered: true, bordered: true,
openAnimation: { ...animation, appear() { } }, openAnimation: { ...animation, appear() {} },
}; };
renderExpandIcon = () => { renderExpandIcon = () => {
return ( return <Icon type="right" className={`arrow`} />;
<Icon type="right" className={`arrow`} /> };
);
}
renderCollapse = ({ getPrefixCls }: ConfigConsumerProps) => { renderCollapse = ({ getPrefixCls }: ConfigConsumerProps) => {
const { prefixCls: customizePrefixCls, className = '', bordered } = this.props; const { prefixCls: customizePrefixCls, className = '', bordered } = this.props;
const prefixCls = getPrefixCls('collapse', customizePrefixCls); const prefixCls = getPrefixCls('collapse', customizePrefixCls);
const collapseClassName = classNames({ const collapseClassName = classNames(
[`${prefixCls}-borderless`]: !bordered, {
}, className); [`${prefixCls}-borderless`]: !bordered,
},
className,
);
return ( return (
<RcCollapse <RcCollapse
{...this.props} {...this.props}
@ -47,13 +48,9 @@ export default class Collapse extends React.Component<CollapseProps, any> {
expandIcon={this.renderExpandIcon} expandIcon={this.renderExpandIcon}
/> />
); );
} };
render() { render() {
return ( return <ConfigConsumer>{this.renderCollapse}</ConfigConsumer>;
<ConfigConsumer>
{this.renderCollapse}
</ConfigConsumer>
);
} }
} }

View File

@ -19,23 +19,18 @@ export default class CollapsePanel extends React.Component<CollapsePanelProps, {
renderCollapsePanel = ({ getPrefixCls }: ConfigConsumerProps) => { renderCollapsePanel = ({ getPrefixCls }: ConfigConsumerProps) => {
const { prefixCls: customizePrefixCls, className = '', showArrow = true } = this.props; const { prefixCls: customizePrefixCls, className = '', showArrow = true } = this.props;
const prefixCls = getPrefixCls('collapse', customizePrefixCls); const prefixCls = getPrefixCls('collapse', customizePrefixCls);
const collapsePanelClassName = classNames({ const collapsePanelClassName = classNames(
[`${prefixCls}-no-arrow`]: !showArrow, {
}, className); [`${prefixCls}-no-arrow`]: !showArrow,
return ( },
<RcCollapse.Panel className,
{...this.props}
prefixCls={prefixCls}
className={collapsePanelClassName}
/>
); );
} return (
<RcCollapse.Panel {...this.props} prefixCls={prefixCls} className={collapsePanelClassName} />
);
};
render() { render() {
return ( return <ConfigConsumer>{this.renderCollapsePanel}</ConfigConsumer>;
<ConfigConsumer>
{this.renderCollapsePanel}
</ConfigConsumer>
);
} }
} }

View File

@ -1,7 +1,7 @@
@import "../../style/themes/default"; @import '../../style/themes/default';
@import "../../style/mixins/index"; @import '../../style/mixins/index';
@collapse-prefix-cls: ~"@{ant-prefix}-collapse"; @collapse-prefix-cls: ~'@{ant-prefix}-collapse';
.collapse-close() { .collapse-close() {
transform: rotate(0); transform: rotate(0);
@ -33,7 +33,7 @@
color: @heading-color; color: @heading-color;
cursor: pointer; cursor: pointer;
position: relative; position: relative;
transition: all .3s; transition: all 0.3s;
.arrow { .arrow {
.iconfont-mixin(); .iconfont-mixin();
@ -64,7 +64,7 @@
} }
&-anim-active { &-anim-active {
transition: height .2s @ease-out; transition: height 0.2s @ease-out;
} }
&-content { &-content {
@ -88,7 +88,7 @@
} }
} }
& > &-item > &-header[aria-expanded="true"] { & > &-item > &-header[aria-expanded='true'] {
.anticon-right svg { .anticon-right svg {
.collapse-open(); .collapse-open();
} }

View File

@ -28,22 +28,13 @@ export default class Comment extends React.Component<CommentProps, {}> {
if (!actions || !actions.length) { if (!actions || !actions.length) {
return null; return null;
} }
const actionList = actions.map((action, index) => ( const actionList = actions.map((action, index) => <li key={`action-${index}`}>{action}</li>);
<li key={`action-${index}`}>
{action}
</li>
),
);
return actionList; return actionList;
} }
renderNested = (prefixCls: string, children: any) => { renderNested = (prefixCls: string, children: any) => {
return ( return <div className={classNames(`${prefixCls}-nested`)}>{children}</div>;
<div className={classNames(`${prefixCls}-nested`)}> };
{children}
</div>
);
}
renderComment = ({ getPrefixCls }: ConfigConsumerProps) => { renderComment = ({ getPrefixCls }: ConfigConsumerProps) => {
const { const {
@ -67,31 +58,22 @@ export default class Comment extends React.Component<CommentProps, {}> {
</div> </div>
); );
const actionDom = actions && actions.length const actionDom =
? <ul className={`${prefixCls}-actions`}>{this.getAction(actions)}</ul> actions && actions.length ? (
: null; <ul className={`${prefixCls}-actions`}>{this.getAction(actions)}</ul>
) : null;
const authorContent = ( const authorContent = (
<div className={`${prefixCls}-content-author`}> <div className={`${prefixCls}-content-author`}>
{author && ( {author && <span className={`${prefixCls}-content-author-name`}>{author}</span>}
<span className={`${prefixCls}-content-author-name`}> {datetime && <span className={`${prefixCls}-content-author-time`}>{datetime}</span>}
{author}
</span>
)}
{datetime && (
<span className={`${prefixCls}-content-author-time`}>
{datetime}
</span>
)}
</div> </div>
); );
const contentDom = ( const contentDom = (
<div className={`${prefixCls}-content`}> <div className={`${prefixCls}-content`}>
{authorContent} {authorContent}
<div className={`${prefixCls}-content-detail`}> <div className={`${prefixCls}-content-detail`}>{content}</div>
{content}
</div>
{actionDom} {actionDom}
</div> </div>
); );
@ -109,13 +91,9 @@ export default class Comment extends React.Component<CommentProps, {}> {
{children ? this.renderNested(prefixCls, children) : null} {children ? this.renderNested(prefixCls, children) : null}
</div> </div>
); );
} };
render() { render() {
return ( return <ConfigConsumer>{this.renderComment}</ConfigConsumer>;
<ConfigConsumer>
{this.renderComment}
</ConfigConsumer>
);
} }
} }

View File

@ -1,7 +1,7 @@
@import "../../style/themes/default"; @import '../../style/themes/default';
@import "../../style/mixins/index"; @import '../../style/mixins/index';
@comment-prefix-cls: ~"@{ant-prefix}-comment"; @comment-prefix-cls: ~'@{ant-prefix}-comment';
.@{comment-prefix-cls} { .@{comment-prefix-cls} {
position: relative; position: relative;
@ -44,7 +44,7 @@
} }
&-name { &-name {
transition: color .3s; transition: color 0.3s;
font-size: 14px; font-size: 14px;
color: @comment-author-name-color; color: @comment-author-name-color;
> * { > * {
@ -75,7 +75,7 @@
color: @comment-action-color; color: @comment-action-color;
> span { > span {
padding-right: 10px; padding-right: 10px;
transition: color .3s; transition: color 0.3s;
font-size: 12px; font-size: 12px;
color: @comment-action-color; color: @comment-action-color;
cursor: pointer; cursor: pointer;

View File

@ -6581,7 +6581,9 @@ exports[`ConfigProvider components DatePicker WeekPicker configProvider 1`] = `
<span <span
class="config-calendar-picker" class="config-calendar-picker"
> >
<span> <span
style="display:inline-block"
>
<input <input
class="config-calendar-picker-input config-input" class="config-calendar-picker-input config-input"
placeholder="Select date" placeholder="Select date"
@ -6615,7 +6617,9 @@ exports[`ConfigProvider components DatePicker WeekPicker normal 1`] = `
<span <span
class="ant-calendar-picker" class="ant-calendar-picker"
> >
<span> <span
style="display:inline-block"
>
<input <input
class="ant-calendar-picker-input ant-input" class="ant-calendar-picker-input ant-input"
placeholder="Select date" placeholder="Select date"
@ -6649,7 +6653,9 @@ exports[`ConfigProvider components DatePicker WeekPicker prefixCls 1`] = `
<span <span
class="prefix-WeekPicker-picker" class="prefix-WeekPicker-picker"
> >
<span> <span
style="display:inline-block"
>
<input <input
class="prefix-WeekPicker-picker-input ant-input" class="prefix-WeekPicker-picker-input ant-input"
placeholder="Select date" placeholder="Select date"
@ -11113,7 +11119,7 @@ exports[`ConfigProvider components Table configProvider 1`] = `
> >
<div <div
class="config-table-column-sorters" class="config-table-column-sorters"
title="Sort" title="Name"
> >
Name Name
<div <div
@ -11296,7 +11302,7 @@ exports[`ConfigProvider components Table normal 1`] = `
> >
<div <div
class="ant-table-column-sorters" class="ant-table-column-sorters"
title="Sort" title="Name"
> >
Name Name
<div <div
@ -11479,7 +11485,7 @@ exports[`ConfigProvider components Table prefixCls 1`] = `
> >
<div <div
class="prefix-Table-column-sorters" class="prefix-Table-column-sorters"
title="Sort" title="Name"
> >
Name Name
<div <div

View File

@ -72,11 +72,9 @@ describe('ConfigProvider', () => {
// configProvider // configProvider
it('configProvider', () => { it('configProvider', () => {
expect(render( expect(
<ConfigProvider prefixCls="config"> render(<ConfigProvider prefixCls="config">{renderComponent({})}</ConfigProvider>),
{renderComponent({})} ).toMatchSnapshot();
</ConfigProvider>
)).toMatchSnapshot();
}); });
}); });
} }
@ -94,22 +92,16 @@ describe('ConfigProvider', () => {
)); ));
// AutoComplete // AutoComplete
testPair('AutoComplete', props => ( testPair('AutoComplete', props => <AutoComplete {...props} />);
<AutoComplete {...props} />
));
// Avatar // Avatar
testPair('Avatar', props => ( testPair('Avatar', props => <Avatar {...props} />);
<Avatar {...props} />
));
// BackTop // BackTop
testPair('BackTop', props => ( testPair('BackTop', props => <BackTop visible {...props} />);
<BackTop visible {...props} />
));
// Badge // Badge
testPair('Badge', (props) => { testPair('Badge', props => {
const newProps = { const newProps = {
...props, ...props,
}; };
@ -171,15 +163,17 @@ describe('ConfigProvider', () => {
// Carousel // Carousel
testPair('Carousel', props => ( testPair('Carousel', props => (
<Carousel {...props}> <Carousel {...props}>
<div><h3>Bamboo</h3></div> <div>
<div><h3>Light</h3></div> <h3>Bamboo</h3>
</div>
<div>
<h3>Light</h3>
</div>
</Carousel> </Carousel>
)); ));
// Cascader // Cascader
testPair('Cascader', props => ( testPair('Cascader', props => <Cascader {...props} options={[]} showSearch />);
<Cascader {...props} options={[]} showSearch />
));
// Checkbox // Checkbox
testPair('Checkbox', props => ( testPair('Checkbox', props => (
@ -235,17 +229,13 @@ describe('ConfigProvider', () => {
}); });
// Divider // Divider
testPair('Divider', props => ( testPair('Divider', props => <Divider {...props} />);
<Divider {...props} />
));
// Drawer // Drawer
testPair('Drawer', props => ( testPair('Drawer', props => <Drawer {...props} visible getContainer={false} />);
<Drawer {...props} visible getContainer={false} />
));
// Dropdown // Dropdown
testPair('Dropdown', (props) => { testPair('Dropdown', props => {
const menu = ( const menu = (
<Menu {...props}> <Menu {...props}>
<Menu.Item {...props}>Bamboo</Menu.Item> <Menu.Item {...props}>Bamboo</Menu.Item>
@ -262,18 +252,14 @@ describe('ConfigProvider', () => {
// Form // Form
testPair('Form', props => ( testPair('Form', props => (
<Form {...props}> <Form {...props}>
<Form.Item <Form.Item {...props} validateStatus="error" help="Bamboo is Light">
{...props}
validateStatus="error"
help="Bamboo is Light"
>
<Input {...props} /> <Input {...props} />
</Form.Item> </Form.Item>
</Form> </Form>
)); ));
// Grid // Grid
testPair('Grid', (props) => { testPair('Grid', props => {
const rowProps = {}; const rowProps = {};
const colProps = {}; const colProps = {};
if (props.prefixCls) { if (props.prefixCls) {
@ -300,12 +286,10 @@ describe('ConfigProvider', () => {
)); ));
// InputNumber // InputNumber
testPair('InputNumber', props => ( testPair('InputNumber', props => <InputNumber {...props} />);
<InputNumber {...props} />
));
// Layout // Layout
testPair('Layout', (props) => { testPair('Layout', props => {
const siderProps = {}; const siderProps = {};
const headerProps = {}; const headerProps = {};
const contentProps = {}; const contentProps = {};
@ -339,7 +323,9 @@ describe('ConfigProvider', () => {
<List.Item {...props}> <List.Item {...props}>
<List.Item.Meta <List.Item.Meta
{...props} {...props}
avatar={<Avatar src="https://zos.alipayobjects.com/rmsportal/ODTLcjxAfvqbxHnVXCYX.png" />} avatar={
<Avatar src="https://zos.alipayobjects.com/rmsportal/ODTLcjxAfvqbxHnVXCYX.png" />
}
title="Ant Design" title="Ant Design"
description="Ant Design, a design language for background applications, is refined by Ant UED Team" description="Ant Design, a design language for background applications, is refined by Ant UED Team"
/> />
@ -349,20 +335,16 @@ describe('ConfigProvider', () => {
)); ));
// Mention // Mention
testPair('Mention', props => ( testPair('Mention', props => <Mention {...props} />);
<Mention {...props} />
));
// Menu // Menu
testPair('Menu', props => ( testPair('Menu', props => (
<Menu <Menu {...props} defaultOpenKeys={['bamboo']} mode="inline">
{...props}
defaultOpenKeys={['bamboo']}
mode="inline"
>
<Menu.SubMenu {...props} key="bamboo" title="bamboo"> <Menu.SubMenu {...props} key="bamboo" title="bamboo">
<Menu.ItemGroup {...props} key="g1" title="Item 1"> <Menu.ItemGroup {...props} key="g1" title="Item 1">
<Menu.Item {...props} key="1">Light</Menu.Item> <Menu.Item {...props} key="1">
Light
</Menu.Item>
</Menu.ItemGroup> </Menu.ItemGroup>
</Menu.SubMenu> </Menu.SubMenu>
</Menu> </Menu>
@ -371,10 +353,7 @@ describe('ConfigProvider', () => {
// Modal // Modal
testPair('Modal', props => ( testPair('Modal', props => (
<div> <div>
<Modal <Modal {...props} visible>
{...props}
visible
>
Bamboo is Little Light Bamboo is Little Light
</Modal> </Modal>
</div> </div>
@ -391,10 +370,7 @@ describe('ConfigProvider', () => {
// Popconfirm // Popconfirm
testPair('Popconfirm', props => ( testPair('Popconfirm', props => (
<div> <div>
<Popconfirm <Popconfirm {...props} visible>
{...props}
visible
>
<span>Bamboo</span> <span>Bamboo</span>
</Popconfirm> </Popconfirm>
</div> </div>
@ -403,19 +379,14 @@ describe('ConfigProvider', () => {
// Popover // Popover
testPair('Popover', props => ( testPair('Popover', props => (
<div> <div>
<Popover <Popover {...props} visible>
{...props}
visible
>
<span>Light</span> <span>Light</span>
</Popover> </Popover>
</div> </div>
)); ));
// Progress // Progress
testPair('Progress', props => ( testPair('Progress', props => <Progress {...props} />);
<Progress {...props} />
));
// Radio // Radio
testPair('Radio', props => ( testPair('Radio', props => (
@ -430,16 +401,11 @@ describe('ConfigProvider', () => {
)); ));
// Rate // Rate
testPair('Rate', props => ( testPair('Rate', props => <Rate {...props} />);
<Rate {...props} />
));
// Select // Select
testPair('Select', props => ( testPair('Select', props => (
<Select <Select {...props} open>
{...props}
open
>
<Select.OptGroup key="grp"> <Select.OptGroup key="grp">
<Select.Option key="Bamboo">Light</Select.Option> <Select.Option key="Bamboo">Light</Select.Option>
</Select.OptGroup> </Select.OptGroup>
@ -447,28 +413,22 @@ describe('ConfigProvider', () => {
)); ));
// Skeleton // Skeleton
testPair('Skeleton', props => ( testPair('Skeleton', props => <Skeleton title avatar paragraph {...props} />);
<Skeleton title avatar paragraph {...props} />
));
// Slider // Slider
testPair('Slider', (props) => { testPair('Slider', props => {
const myProps = { ...props }; const myProps = { ...props };
if (myProps.prefixCls) { if (myProps.prefixCls) {
myProps.tooltipPrefixCls = `${myProps.prefixCls}-tooltip`; myProps.tooltipPrefixCls = `${myProps.prefixCls}-tooltip`;
} }
return ( return <Slider tooltipVisible {...myProps} />;
<Slider tooltipVisible {...myProps} />
);
}); });
// Spin // Spin
testPair('Spin', props => ( testPair('Spin', props => <Spin {...props} />);
<Spin {...props} />
));
// Steps // Steps
testPair('Steps', (props) => { testPair('Steps', props => {
const myProps = { ...props }; const myProps = { ...props };
if (props.prefixCls) { if (props.prefixCls) {
myProps.iconPrefix = 'prefixIcon'; myProps.iconPrefix = 'prefixIcon';
@ -481,39 +441,42 @@ describe('ConfigProvider', () => {
}); });
// Switch // Switch
testPair('Switch', props => ( testPair('Switch', props => <Switch {...props} />);
<Switch {...props} />
));
// Table // Table
testPair('Table', (props) => { testPair('Table', props => {
const columns = [{ const columns = [
title: 'Name', {
dataIndex: 'name', title: 'Name',
filters: [{ dataIndex: 'name',
text: 'Joe', filters: [
value: 'Joe', {
}, { text: 'Joe',
text: 'Submenu', value: 'Joe',
value: 'Submenu', },
children: [{ {
text: 'Green', text: 'Submenu',
value: 'Green', value: 'Submenu',
}], children: [
}], {
filterDropdownVisible: true, text: 'Green',
onFilter: (value, record) => record.name.indexOf(value) === 0, value: 'Green',
sorter: (a, b) => a.name.length - b.name.length, },
}]; ],
},
],
filterDropdownVisible: true,
onFilter: (value, record) => record.name.indexOf(value) === 0,
sorter: (a, b) => a.name.length - b.name.length,
},
];
const myProps = { ...props }; const myProps = { ...props };
if (props.prefixCls) { if (props.prefixCls) {
myProps.dropdownPrefixCls = 'prefix-dropdown'; myProps.dropdownPrefixCls = 'prefix-dropdown';
} }
return ( return <Table columns={columns} {...props} />;
<Table columns={columns} {...props} />
);
}); });
// Tabs // Tabs
@ -551,12 +514,7 @@ describe('ConfigProvider', () => {
)); ));
// Transfer // Transfer
testPair('Transfer', props => ( testPair('Transfer', props => <Transfer {...props} dataSource={[]} />);
<Transfer
{...props}
dataSource={[]}
/>
));
// Tree // Tree
testPair('Tree', props => ( testPair('Tree', props => (
@ -582,11 +540,13 @@ describe('ConfigProvider', () => {
testPair('Upload', props => ( testPair('Upload', props => (
<Upload <Upload
{...props} {...props}
defaultFileList={[{ defaultFileList={[
uid: '1', {
name: 'xxx.png', uid: '1',
status: 'done', name: 'xxx.png',
}]} status: 'done',
},
]}
> >
<span /> <span />
</Upload> </Upload>

View File

@ -41,20 +41,12 @@ class ConfigProvider extends React.Component<ConfigProviderProps> {
getPrefixCls: this.getPrefixCls, getPrefixCls: this.getPrefixCls,
}; };
return ( return <ConfigContext.Provider value={config}>{children}</ConfigContext.Provider>;
<ConfigContext.Provider value={config}> };
{children}
</ConfigContext.Provider>
);
}
render() { render() {
return ( return <ConfigConsumer>{this.renderProvider}</ConfigConsumer>;
<ConfigConsumer>
{this.renderProvider}
</ConfigConsumer>
);
} }
} }
export default ConfigProvider; export default ConfigProvider;

View File

@ -1,2 +1,2 @@
// placeholder // placeholder
@import "../../style/themes/default"; @import '../../style/themes/default';

View File

@ -21,7 +21,7 @@ export interface RangePickerState {
} }
function getShowDateFromValue(value: RangePickerValue) { function getShowDateFromValue(value: RangePickerValue) {
const [ start, end ] = value; const [start, end] = value;
// value could be an empty array, then we should not reset showDate // value could be an empty array, then we should not reset showDate
if (!start && !end) { if (!start && !end) {
return; return;
@ -34,7 +34,9 @@ function formatValue(value: moment.Moment | undefined, format: string): string {
return (value && value.format(format)) || ''; return (value && value.format(format)) || '';
} }
function pickerValueAdapter(value?: moment.Moment | RangePickerValue): RangePickerValue | undefined { function pickerValueAdapter(
value?: moment.Moment | RangePickerValue,
): RangePickerValue | undefined {
if (!value) { if (!value) {
return; return;
} }
@ -58,7 +60,7 @@ function fixLocale(value: RangePickerValue | undefined, localeCode: string) {
if (!value || value.length === 0) { if (!value || value.length === 0) {
return; return;
} }
const [ start, end ] = value; const [start, end] = value;
if (start) { if (start) {
start!.locale(localeCode); start!.locale(localeCode);
} }
@ -87,7 +89,7 @@ class RangePicker extends React.Component<any, RangePickerState> {
}; };
} }
} }
if (('open' in nextProps) && prevState.open !== nextProps.open) { if ('open' in nextProps && prevState.open !== nextProps.open) {
state = { state = {
...state, ...state,
open: nextProps.open, open: nextProps.open,
@ -103,14 +105,14 @@ class RangePicker extends React.Component<any, RangePickerState> {
constructor(props: any) { constructor(props: any) {
super(props); super(props);
const value = props.value || props.defaultValue || []; const value = props.value || props.defaultValue || [];
const [ start, end ] = value; const [start, end] = value;
if ( if (
start && !interopDefault(moment).isMoment(start) || (start && !interopDefault(moment).isMoment(start)) ||
end && !interopDefault(moment).isMoment(end) (end && !interopDefault(moment).isMoment(end))
) { ) {
throw new Error( throw new Error(
'The value/defaultValue of RangePicker must be a moment object array after `antd@2.0`, ' + 'The value/defaultValue of RangePicker must be a moment object array after `antd@2.0`, ' +
'see: https://u.ant.design/date-picker-value', 'see: https://u.ant.design/date-picker-value',
); );
} }
const pickerValue = !value || isEmptyArray(value) ? props.defaultPickerValue : value; const pickerValue = !value || isEmptyArray(value) ? props.defaultPickerValue : value;
@ -127,7 +129,7 @@ class RangePicker extends React.Component<any, RangePickerState> {
e.stopPropagation(); e.stopPropagation();
this.setState({ value: [] }); this.setState({ value: [] });
this.handleChange([]); this.handleChange([]);
} };
clearHoverValue = () => this.setState({ hoverValue: [] }); clearHoverValue = () => this.setState({ hoverValue: [] });
@ -139,12 +141,9 @@ class RangePicker extends React.Component<any, RangePickerState> {
showDate: getShowDateFromValue(value) || showDate, showDate: getShowDateFromValue(value) || showDate,
})); }));
} }
const [ start, end ] = value; const [start, end] = value;
props.onChange(value, [ props.onChange(value, [formatValue(start, props.format), formatValue(end, props.format)]);
formatValue(start, props.format), };
formatValue(end, props.format),
]);
}
handleOpenChange = (open: boolean) => { handleOpenChange = (open: boolean) => {
if (!('open' in this.props)) { if (!('open' in this.props)) {
@ -163,7 +162,7 @@ class RangePicker extends React.Component<any, RangePickerState> {
if (!open) { if (!open) {
this.focus(); this.focus();
} }
} };
handleShowDateChange = (showDate: RangePickerValue) => this.setState({ showDate }); handleShowDateChange = (showDate: RangePickerValue) => this.setState({ showDate });
@ -173,10 +172,10 @@ class RangePicker extends React.Component<any, RangePickerState> {
if (this.state.open) { if (this.state.open) {
this.clearHoverValue(); this.clearHoverValue();
} }
} };
handleCalendarInputSelect = (value: RangePickerValue) => { handleCalendarInputSelect = (value: RangePickerValue) => {
const [ start ] = value; const [start] = value;
if (!start) { if (!start) {
return; return;
} }
@ -184,7 +183,7 @@ class RangePicker extends React.Component<any, RangePickerState> {
value, value,
showDate: getShowDateFromValue(value) || showDate, showDate: getShowDateFromValue(value) || showDate,
})); }));
} };
handleRangeClick = (value: RangePickerPresetRange) => { handleRangeClick = (value: RangePickerPresetRange) => {
if (typeof value === 'function') { if (typeof value === 'function') {
@ -201,7 +200,7 @@ class RangePicker extends React.Component<any, RangePickerState> {
if (onOpenChange) { if (onOpenChange) {
onOpenChange(false); onOpenChange(false);
} }
} };
setValue(value: RangePickerValue, hidePanel?: boolean) { setValue(value: RangePickerValue, hidePanel?: boolean) {
this.handleChange(value); this.handleChange(value);
@ -220,7 +219,7 @@ class RangePicker extends React.Component<any, RangePickerState> {
savePicker = (node: HTMLSpanElement) => { savePicker = (node: HTMLSpanElement) => {
this.picker = node; this.picker = node;
} };
renderFooter = (...args: any[]) => { renderFooter = (...args: any[]) => {
const { ranges, renderExtraFooter } = this.props; const { ranges, renderExtraFooter } = this.props;
@ -233,7 +232,7 @@ class RangePicker extends React.Component<any, RangePickerState> {
{renderExtraFooter(...args)} {renderExtraFooter(...args)}
</div> </div>
) : null; ) : null;
const operations = Object.keys(ranges || {}).map((range) => { const operations = Object.keys(ranges || {}).map(range => {
const value = ranges[range]; const value = ranges[range];
return ( return (
<Tag <Tag
@ -248,13 +247,14 @@ class RangePicker extends React.Component<any, RangePickerState> {
</Tag> </Tag>
); );
}); });
const rangeNode = (operations && operations.length > 0) ? ( const rangeNode =
<div className={`${prefixCls}-footer-extra ${prefixCls}-range-quick-selector`} key="range"> operations && operations.length > 0 ? (
{operations} <div className={`${prefixCls}-footer-extra ${prefixCls}-range-quick-selector`} key="range">
</div> {operations}
) : null; </div>
) : null;
return [rangeNode, customFooter]; return [rangeNode, customFooter];
} };
renderRangePicker = ({ getPrefixCls }: ConfigConsumerProps) => { renderRangePicker = ({ getPrefixCls }: ConfigConsumerProps) => {
const { state, props } = this; const { state, props } = this;
@ -262,11 +262,20 @@ class RangePicker extends React.Component<any, RangePickerState> {
const { const {
prefixCls: customizePrefixCls, prefixCls: customizePrefixCls,
tagPrefixCls: customizeTagPrefixCls, tagPrefixCls: customizeTagPrefixCls,
popupStyle, style, popupStyle,
disabledDate, disabledTime, style,
showTime, showToday, disabledDate,
ranges, onOk, locale, localeCode, format, disabledTime,
dateRender, onCalendarChange, suffixIcon, showTime,
showToday,
ranges,
onOk,
locale,
localeCode,
format,
dateRender,
onCalendarChange,
suffixIcon,
} = props; } = props;
const prefixCls = getPrefixCls('calendar', customizePrefixCls); const prefixCls = getPrefixCls('calendar', customizePrefixCls);
@ -303,10 +312,10 @@ class RangePicker extends React.Component<any, RangePickerState> {
calendarProps.mode = props.mode; calendarProps.mode = props.mode;
} }
const startPlaceholder = ('placeholder' in props) const startPlaceholder =
? props.placeholder[0] : locale.lang.rangePlaceholder[0]; 'placeholder' in props ? props.placeholder[0] : locale.lang.rangePlaceholder[0];
const endPlaceholder = ('placeholder' in props) const endPlaceholder =
? props.placeholder[1] : locale.lang.rangePlaceholder[1]; 'placeholder' in props ? props.placeholder[1] : locale.lang.rangePlaceholder[1];
const calendar = ( const calendar = (
<RangeCalendar <RangeCalendar
@ -338,32 +347,31 @@ class RangePicker extends React.Component<any, RangePickerState> {
if (props.showTime) { if (props.showTime) {
pickerStyle.width = (style && style.width) || 350; pickerStyle.width = (style && style.width) || 350;
} }
const [ startValue, endValue ] = value as RangePickerValue; const [startValue, endValue] = value as RangePickerValue;
const clearIcon = (!props.disabled && props.allowClear && value && (startValue || endValue)) ? ( const clearIcon =
<Icon !props.disabled && props.allowClear && value && (startValue || endValue) ? (
type="close-circle" <Icon
className={`${prefixCls}-picker-clear`} type="close-circle"
onClick={this.clearSelection} className={`${prefixCls}-picker-clear`}
theme="filled" onClick={this.clearSelection}
/> theme="filled"
) : null; />
) : null;
const inputIcon = suffixIcon && ( const inputIcon = (suffixIcon &&
React.isValidElement<{ className?: string }>(suffixIcon) (React.isValidElement<{ className?: string }>(suffixIcon) ? (
? React.cloneElement( React.cloneElement(suffixIcon, {
suffixIcon, className: classNames({
{ [suffixIcon.props.className!]: suffixIcon.props.className,
className: classNames({ [`${prefixCls}-picker-icon`]: true,
[suffixIcon.props.className!]: suffixIcon.props.className, }),
[`${prefixCls}-picker-icon`]: true, })
}), ) : (
}, <span className={`${prefixCls}-picker-icon`}>{suffixIcon}</span>
) : <span className={`${prefixCls}-picker-icon`}>{suffixIcon}</span>) || ( ))) || <Icon type="calendar" className={`${prefixCls}-picker-icon`} />;
<Icon type="calendar" className={`${prefixCls}-picker-icon`} />
);
const input = ({ value: inputValue }: { value: any }) => { const input = ({ value: inputValue }: { value: any }) => {
const [ start, end ] = inputValue; const [start, end] = inputValue;
return ( return (
<span className={props.pickerInputClass}> <span className={props.pickerInputClass}>
<input <input
@ -415,14 +423,10 @@ class RangePicker extends React.Component<any, RangePickerState> {
</RcDatePicker> </RcDatePicker>
</span> </span>
); );
} };
render() { render() {
return ( return <ConfigConsumer>{this.renderRangePicker}</ConfigConsumer>;
<ConfigConsumer>
{this.renderRangePicker}
</ConfigConsumer>
);
} }
} }

View File

@ -46,7 +46,7 @@ class WeekPicker extends React.Component<any, WeekPickerState> {
if (value && !interopDefault(moment).isMoment(value)) { if (value && !interopDefault(moment).isMoment(value)) {
throw new Error( throw new Error(
'The value/defaultValue of DatePicker or MonthPicker must be ' + 'The value/defaultValue of DatePicker or MonthPicker must be ' +
'a moment object after `antd@2.0`, see: https://u.ant.design/date-picker-value', 'a moment object after `antd@2.0`, see: https://u.ant.design/date-picker-value',
); );
} }
this.state = { this.state = {
@ -58,30 +58,26 @@ class WeekPicker extends React.Component<any, WeekPickerState> {
weekDateRender = (current: any) => { weekDateRender = (current: any) => {
const selectedValue = this.state.value; const selectedValue = this.state.value;
const { prefixCls } = this; const { prefixCls } = this;
if (selectedValue && if (
selectedValue &&
current.year() === selectedValue.year() && current.year() === selectedValue.year() &&
current.week() === selectedValue.week()) { current.week() === selectedValue.week()
) {
return ( return (
<div className={`${prefixCls}-selected-day`}> <div className={`${prefixCls}-selected-day`}>
<div className={`${prefixCls}-date`}> <div className={`${prefixCls}-date`}>{current.date()}</div>
{current.date()}
</div>
</div> </div>
); );
} }
return ( return <div className={`${prefixCls}-date`}>{current.date()}</div>;
<div className={`${prefixCls}-date`}> };
{current.date()}
</div>
);
}
handleChange = (value: moment.Moment | null) => { handleChange = (value: moment.Moment | null) => {
if (!('value' in this.props)) { if (!('value' in this.props)) {
this.setState({ value }); this.setState({ value });
} }
this.props.onChange(value, formatValue(value, this.props.format)); this.props.onChange(value, formatValue(value, this.props.format));
} };
handleOpenChange = (open: boolean) => { handleOpenChange = (open: boolean) => {
const { onOpenChange } = this.props; const { onOpenChange } = this.props;
@ -102,7 +98,7 @@ class WeekPicker extends React.Component<any, WeekPickerState> {
e.preventDefault(); e.preventDefault();
e.stopPropagation(); e.stopPropagation();
this.handleChange(null); this.handleChange(null);
} };
focus() { focus() {
this.input.focus(); this.input.focus();
@ -114,14 +110,26 @@ class WeekPicker extends React.Component<any, WeekPickerState> {
saveInput = (node: any) => { saveInput = (node: any) => {
this.input = node; this.input = node;
} };
renderWeekPicker = ({ getPrefixCls }: ConfigConsumerProps) => { renderWeekPicker = ({ getPrefixCls }: ConfigConsumerProps) => {
const { const {
prefixCls: customizePrefixCls, prefixCls: customizePrefixCls,
className, disabled, pickerClass, popupStyle, className,
pickerInputClass, format, allowClear, locale, localeCode, disabledDate, disabled,
style, onFocus, onBlur, id, suffixIcon, pickerClass,
popupStyle,
pickerInputClass,
format,
allowClear,
locale,
localeCode,
disabledDate,
style,
onFocus,
onBlur,
id,
suffixIcon,
} = this.props; } = this.props;
const prefixCls = getPrefixCls('calendar', customizePrefixCls); const prefixCls = getPrefixCls('calendar', customizePrefixCls);
@ -136,8 +144,8 @@ class WeekPicker extends React.Component<any, WeekPickerState> {
pickerValue.locale(localeCode); pickerValue.locale(localeCode);
} }
const placeholder = ('placeholder' in this.props) const placeholder =
? this.props.placeholder : locale.lang.placeholder; 'placeholder' in this.props ? this.props.placeholder : locale.lang.placeholder;
const calendar = ( const calendar = (
<Calendar <Calendar
@ -151,28 +159,27 @@ class WeekPicker extends React.Component<any, WeekPickerState> {
disabledDate={disabledDate} disabledDate={disabledDate}
/> />
); );
const clearIcon = (!disabled && allowClear && this.state.value) ? ( const clearIcon =
<Icon !disabled && allowClear && this.state.value ? (
type="close-circle" <Icon
className={`${prefixCls}-picker-clear`} type="close-circle"
onClick={this.clearSelection} className={`${prefixCls}-picker-clear`}
theme="filled" onClick={this.clearSelection}
/> theme="filled"
) : null; />
) : null;
const inputIcon = suffixIcon && ( const inputIcon = (suffixIcon &&
React.isValidElement<{ className?: string }>(suffixIcon) (React.isValidElement<{ className?: string }>(suffixIcon) ? (
? React.cloneElement( React.cloneElement(suffixIcon, {
suffixIcon, className: classNames({
{ [suffixIcon.props.className!]: suffixIcon.props.className,
className: classNames({ [`${prefixCls}-picker-icon`]: true,
[suffixIcon.props.className!]: suffixIcon.props.className, }),
[`${prefixCls}-picker-icon`]: true, })
}), ) : (
}, <span className={`${prefixCls}-picker-icon`}>{suffixIcon}</span>
) : <span className={`${prefixCls}-picker-icon`}>{suffixIcon}</span>) || ( ))) || <Icon type="calendar" className={`${prefixCls}-picker-icon`} />;
<Icon type="calendar" className={`${prefixCls}-picker-icon`} />
);
const input = ({ value }: { value: moment.Moment | undefined }) => { const input = ({ value }: { value: moment.Moment | undefined }) => {
return ( return (
@ -193,11 +200,7 @@ class WeekPicker extends React.Component<any, WeekPickerState> {
); );
}; };
return ( return (
<span <span className={classNames(className, pickerClass)} style={style} id={id}>
className={classNames(className, pickerClass)}
style={style}
id={id}
>
<RcDatePicker <RcDatePicker
{...this.props} {...this.props}
calendar={calendar} calendar={calendar}
@ -212,14 +215,10 @@ class WeekPicker extends React.Component<any, WeekPickerState> {
</RcDatePicker> </RcDatePicker>
</span> </span>
); );
} };
render() { render() {
return ( return <ConfigConsumer>{this.renderWeekPicker}</ConfigConsumer>;
<ConfigConsumer>
{this.renderWeekPicker}
</ConfigConsumer>
);
} }
} }

View File

@ -3,14 +3,7 @@ import { mount } from 'enzyme';
import moment from 'moment'; import moment from 'moment';
import MockDate from 'mockdate'; import MockDate from 'mockdate';
import DatePicker from '..'; import DatePicker from '..';
import { import { selectDate, openPanel, clearInput, nextYear, nextMonth, hasSelected } from './utils';
selectDate,
openPanel,
clearInput,
nextYear,
nextMonth,
hasSelected,
} from './utils';
import focusTest from '../../../tests/shared/focusTest'; import focusTest from '../../../tests/shared/focusTest';
describe('DatePicker', () => { describe('DatePicker', () => {
@ -28,10 +21,7 @@ describe('DatePicker', () => {
const locale = { const locale = {
lang: { lang: {
placeholder: 'Избери дата', placeholder: 'Избери дата',
rangePlaceholder: [ rangePlaceholder: ['Начална дата', 'Крайна дата'],
'Начална дата',
'Крайна дата',
],
today: 'Днес', today: 'Днес',
now: 'Сега', now: 'Сега',
backToToday: 'Към днес', backToToday: 'Към днес',
@ -63,9 +53,7 @@ describe('DatePicker', () => {
}, },
}; };
const birthday = moment('2000-01-01', 'YYYY-MM-DD'); const birthday = moment('2000-01-01', 'YYYY-MM-DD');
const wrapper = mount( const wrapper = mount(<DatePicker open locale={locale} value={birthday} />);
<DatePicker open locale={locale} value={birthday} />
);
expect(wrapper.render()).toMatchSnapshot(); expect(wrapper.render()).toMatchSnapshot();
}); });
@ -75,9 +63,9 @@ describe('DatePicker', () => {
state = { state = {
cleared: false, cleared: false,
value: moment(), value: moment(),
} };
onChange = (value) => { onChange = value => {
let { cleared } = this.state; let { cleared } = this.state;
let newValue = value; let newValue = value;
@ -91,7 +79,7 @@ describe('DatePicker', () => {
} }
this.setState({ value: newValue, cleared }); this.setState({ value: newValue, cleared });
} };
render() { render() {
const { value } = this.state; const { value } = this.state;
@ -118,9 +106,7 @@ describe('DatePicker', () => {
it('triggers onChange only when date was selected', () => { it('triggers onChange only when date was selected', () => {
const handleChange = jest.fn(); const handleChange = jest.fn();
const wrapper = mount( const wrapper = mount(<DatePicker onChange={handleChange} />);
<DatePicker onChange={handleChange} />
);
openPanel(wrapper); openPanel(wrapper);
nextYear(wrapper); nextYear(wrapper);
expect(handleChange).not.toBeCalled(); expect(handleChange).not.toBeCalled();
@ -131,9 +117,7 @@ describe('DatePicker', () => {
}); });
it('clear input', () => { it('clear input', () => {
const wrapper = mount( const wrapper = mount(<DatePicker />);
<DatePicker />
);
openPanel(wrapper); openPanel(wrapper);
selectDate(wrapper, moment('2016-11-23')); selectDate(wrapper, moment('2016-11-23'));
clearInput(wrapper); clearInput(wrapper);
@ -142,35 +126,27 @@ describe('DatePicker', () => {
}); });
it('sets data attributes on input', () => { it('sets data attributes on input', () => {
const wrapper = mount( const wrapper = mount(<DatePicker data-test="test-id" data-id="12345" />);
<DatePicker data-test="test-id" data-id="12345" />
);
const input = wrapper.find('.ant-calendar-picker-input').getDOMNode(); const input = wrapper.find('.ant-calendar-picker-input').getDOMNode();
expect(input.getAttribute('data-test')).toBe('test-id'); expect(input.getAttribute('data-test')).toBe('test-id');
expect(input.getAttribute('data-id')).toBe('12345'); expect(input.getAttribute('data-id')).toBe('12345');
}); });
it('sets aria attributes on input', () => { it('sets aria attributes on input', () => {
const wrapper = mount( const wrapper = mount(<DatePicker aria-label="some-label" aria-labelledby="label-id" />);
<DatePicker aria-label="some-label" aria-labelledby="label-id" />
);
const input = wrapper.find('.ant-calendar-picker-input').getDOMNode(); const input = wrapper.find('.ant-calendar-picker-input').getDOMNode();
expect(input.getAttribute('aria-label')).toBe('some-label'); expect(input.getAttribute('aria-label')).toBe('some-label');
expect(input.getAttribute('aria-labelledby')).toBe('label-id'); expect(input.getAttribute('aria-labelledby')).toBe('label-id');
}); });
it('sets role attribute on input', () => { it('sets role attribute on input', () => {
const wrapper = mount( const wrapper = mount(<DatePicker role="search" />);
<DatePicker role="search" />
);
const input = wrapper.find('.ant-calendar-picker-input').getDOMNode(); const input = wrapper.find('.ant-calendar-picker-input').getDOMNode();
expect(input.getAttribute('role')).toBe('search'); expect(input.getAttribute('role')).toBe('search');
}); });
it('changes year/month when under control', () => { it('changes year/month when under control', () => {
const wrapper = mount( const wrapper = mount(<DatePicker value={moment('2018-07-01')} />);
<DatePicker value={moment('2018-07-01')} />
);
openPanel(wrapper); openPanel(wrapper);
expect(wrapper.find('.ant-calendar-my-select').text()).toBe('Jul2018'); expect(wrapper.find('.ant-calendar-my-select').text()).toBe('Jul2018');
wrapper.find('.ant-calendar-prev-year-btn').simulate('click'); wrapper.find('.ant-calendar-prev-year-btn').simulate('click');
@ -183,9 +159,7 @@ describe('DatePicker', () => {
return current && current < moment().endOf('day'); return current && current < moment().endOf('day');
} }
const wrapper = mount( const wrapper = mount(<DatePicker disabledDate={disabledDate} />);
<DatePicker disabledDate={disabledDate} />
);
expect(wrapper.render()).toMatchSnapshot(); expect(wrapper.render()).toMatchSnapshot();
}); });

View File

@ -11,11 +11,15 @@ describe('MonthPicker', () => {
focusTest(MonthPicker); focusTest(MonthPicker);
it('reset select item when popup close', () => { it('reset select item when popup close', () => {
const wrapper = mount( const wrapper = mount(<MonthPicker value={moment('2018-07-01')} />);
<MonthPicker value={moment('2018-07-01')} />
);
openPanel(wrapper); openPanel(wrapper);
wrapper.find('.ant-calendar-month-panel-month').first().simulate('click'); wrapper
wrapper.find('.ant-calendar-month-panel-cell').at(6).hasClass('ant-calendar-month-panel-selected-cell'); .find('.ant-calendar-month-panel-month')
.first()
.simulate('click');
wrapper
.find('.ant-calendar-month-panel-cell')
.at(6)
.hasClass('ant-calendar-month-panel-selected-cell');
}); });
}); });

View File

@ -22,17 +22,18 @@ describe('RangePicker', () => {
it('show month panel according to value', () => { it('show month panel according to value', () => {
const birthday = moment('2000-01-01', 'YYYY-MM-DD').locale('zh-cn'); const birthday = moment('2000-01-01', 'YYYY-MM-DD').locale('zh-cn');
const wrapper = mount( const wrapper = mount(
<RangePicker <RangePicker getCalendarContainer={trigger => trigger} format="YYYY/MM/DD" showTime open />,
getCalendarContainer={trigger => trigger}
format="YYYY/MM/DD"
showTime
open
/>
); );
wrapper.setProps({ value: [birthday, birthday] }); wrapper.setProps({ value: [birthday, birthday] });
expect(render(wrapper.find('Trigger').instance().getComponent())) expect(
.toMatchSnapshot(); render(
wrapper
.find('Trigger')
.instance()
.getComponent(),
),
).toMatchSnapshot();
}); });
it('switch to corresponding month panel when click presetted ranges', () => { it('switch to corresponding month panel when click presetted ranges', () => {
@ -46,14 +47,24 @@ describe('RangePicker', () => {
format="YYYY/MM/DD" format="YYYY/MM/DD"
showTime showTime
open open
/> />,
); );
const rangeCalendarWrapper = mount(wrapper.find('Trigger').instance().getComponent()); const rangeCalendarWrapper = mount(
rangeCalendarWrapper.find('.ant-calendar-range-quick-selector Tag') wrapper
.simulate('click'); .find('Trigger')
expect(render(wrapper.find('Trigger').instance().getComponent())) .instance()
.toMatchSnapshot(); .getComponent(),
);
rangeCalendarWrapper.find('.ant-calendar-range-quick-selector Tag').simulate('click');
expect(
render(
wrapper
.find('Trigger')
.instance()
.getComponent(),
),
).toMatchSnapshot();
}); });
it('highlight range when hover presetted range', () => { it('highlight range when hover presetted range', () => {
@ -65,13 +76,22 @@ describe('RangePicker', () => {
getCalendarContainer={trigger => trigger} getCalendarContainer={trigger => trigger}
format="YYYY/MM/DD" format="YYYY/MM/DD"
open open
/> />,
); );
let rangeCalendarWrapper = mount(wrapper.find('Trigger').instance().getComponent()); let rangeCalendarWrapper = mount(
rangeCalendarWrapper.find('.ant-calendar-range-quick-selector Tag') wrapper
.simulate('mouseEnter'); .find('Trigger')
rangeCalendarWrapper = mount(wrapper.find('Trigger').instance().getComponent()); .instance()
.getComponent(),
);
rangeCalendarWrapper.find('.ant-calendar-range-quick-selector Tag').simulate('mouseEnter');
rangeCalendarWrapper = mount(
wrapper
.find('Trigger')
.instance()
.getComponent(),
);
expect(rangeCalendarWrapper.find('.ant-calendar-selected-day').length).toBe(2); expect(rangeCalendarWrapper.find('.ant-calendar-selected-day').length).toBe(2);
}); });
@ -82,10 +102,18 @@ describe('RangePicker', () => {
getCalendarContainer={trigger => trigger} getCalendarContainer={trigger => trigger}
onCalendarChange={onCalendarChangeFn} onCalendarChange={onCalendarChangeFn}
open open
/> />,
); );
const rangeCalendarWrapper = mount(wrapper.find('Trigger').instance().getComponent()); const rangeCalendarWrapper = mount(
rangeCalendarWrapper.find('.ant-calendar-cell').at(15).simulate('click'); wrapper
.find('Trigger')
.instance()
.getComponent(),
);
rangeCalendarWrapper
.find('.ant-calendar-cell')
.at(15)
.simulate('click');
expect(onCalendarChangeFn).toHaveBeenCalled(); expect(onCalendarChangeFn).toHaveBeenCalled();
}); });
@ -93,34 +121,57 @@ describe('RangePicker', () => {
it('should not throw error when value is reset to `[]`', () => { it('should not throw error when value is reset to `[]`', () => {
const birthday = moment('2000-01-01', 'YYYY-MM-DD'); const birthday = moment('2000-01-01', 'YYYY-MM-DD');
const wrapper = mount( const wrapper = mount(
<RangePicker <RangePicker getCalendarContainer={trigger => trigger} value={[birthday, birthday]} open />,
getCalendarContainer={trigger => trigger}
value={[birthday, birthday]}
open
/>
); );
wrapper.setProps({ value: [] }); wrapper.setProps({ value: [] });
const rangeCalendarWrapper = mount(wrapper.find('Trigger').instance().getComponent()); const rangeCalendarWrapper = mount(
expect(() => rangeCalendarWrapper.find('.ant-calendar-cell').at(15).simulate('click').simulate('click')) wrapper
.not.toThrow(); .find('Trigger')
.instance()
.getComponent(),
);
expect(() =>
rangeCalendarWrapper
.find('.ant-calendar-cell')
.at(15)
.simulate('click')
.simulate('click'),
).not.toThrow();
}); });
// issue: https://github.com/ant-design/ant-design/issues/7077 // issue: https://github.com/ant-design/ant-design/issues/7077
it('should not throw error when select after clear', () => { it('should not throw error when select after clear', () => {
const wrapper = mount( const wrapper = mount(<RangePicker getCalendarContainer={trigger => trigger} open />);
<RangePicker let rangeCalendarWrapper = mount(
getCalendarContainer={trigger => trigger} wrapper
open .find('Trigger')
/> .instance()
.getComponent(),
); );
let rangeCalendarWrapper = mount(wrapper.find('Trigger').instance().getComponent()); rangeCalendarWrapper
rangeCalendarWrapper.find('.ant-calendar-cell').at(15).simulate('click').simulate('click'); .find('.ant-calendar-cell')
.at(15)
.simulate('click')
.simulate('click');
wrapper.update(); wrapper.update();
wrapper.find('.ant-calendar-picker-clear').hostNodes().simulate('click'); wrapper
.find('.ant-calendar-picker-clear')
.hostNodes()
.simulate('click');
wrapper.find('.ant-calendar-picker-input').simulate('click'); wrapper.find('.ant-calendar-picker-input').simulate('click');
rangeCalendarWrapper = mount(wrapper.find('Trigger').instance().getComponent()); rangeCalendarWrapper = mount(
expect(() => rangeCalendarWrapper.find('.ant-calendar-cell').at(15).simulate('click').simulate('click')) wrapper
.not.toThrow(); .find('Trigger')
.instance()
.getComponent(),
);
expect(() =>
rangeCalendarWrapper
.find('.ant-calendar-cell')
.at(15)
.simulate('click')
.simulate('click'),
).not.toThrow();
}); });
it('clear hover value after panel close', () => { it('clear hover value after panel close', () => {
@ -128,16 +179,25 @@ describe('RangePicker', () => {
const wrapper = mount( const wrapper = mount(
<div> <div>
<RangePicker value={[moment(), moment().add(2, 'day')]} /> <RangePicker value={[moment(), moment().add(2, 'day')]} />
</div> </div>,
); );
wrapper.find('.ant-calendar-picker-input').simulate('click'); wrapper.find('.ant-calendar-picker-input').simulate('click');
wrapper.find('.ant-calendar-cell').at(25).simulate('click'); wrapper
wrapper.find('.ant-calendar-cell').at(27).simulate('mouseEnter'); .find('.ant-calendar-cell')
.at(25)
.simulate('click');
wrapper
.find('.ant-calendar-cell')
.at(27)
.simulate('mouseEnter');
document.dispatchEvent(new MouseEvent('mousedown')); document.dispatchEvent(new MouseEvent('mousedown'));
jest.runAllTimers(); jest.runAllTimers();
wrapper.find('.ant-calendar-picker-input').simulate('click'); wrapper.find('.ant-calendar-picker-input').simulate('click');
expect( expect(
wrapper.find('.ant-calendar-cell').at(23).hasClass('ant-calendar-in-range-cell') wrapper
.find('.ant-calendar-cell')
.at(23)
.hasClass('ant-calendar-in-range-cell'),
).toBe(true); ).toBe(true);
}); });
@ -145,19 +205,20 @@ describe('RangePicker', () => {
it('static range', () => { it('static range', () => {
const range = [moment().subtract(2, 'd'), moment()]; const range = [moment().subtract(2, 'd'), moment()];
const format = 'YYYY-MM-DD HH:mm:ss'; const format = 'YYYY-MM-DD HH:mm:ss';
const wrapper = mount( const wrapper = mount(<RangePicker ranges={{ 'recent two days': range }} format={format} />);
<RangePicker
ranges={{ 'recent two days': range }}
format={format}
/>
);
wrapper.find('.ant-calendar-picker-input').simulate('click'); wrapper.find('.ant-calendar-picker-input').simulate('click');
wrapper.find('.ant-calendar-range-quick-selector Tag').simulate('click'); wrapper.find('.ant-calendar-range-quick-selector Tag').simulate('click');
expect( expect(
wrapper.find('.ant-calendar-range-picker-input').first().getDOMNode().value wrapper
.find('.ant-calendar-range-picker-input')
.first()
.getDOMNode().value,
).toBe(range[0].format(format)); ).toBe(range[0].format(format));
expect( expect(
wrapper.find('.ant-calendar-range-picker-input').last().getDOMNode().value wrapper
.find('.ant-calendar-range-picker-input')
.last()
.getDOMNode().value,
).toBe(range[1].format(format)); ).toBe(range[1].format(format));
}); });
@ -165,18 +226,21 @@ describe('RangePicker', () => {
const range = [moment().subtract(2, 'd'), moment()]; const range = [moment().subtract(2, 'd'), moment()];
const format = 'YYYY-MM-DD HH:mm:ss'; const format = 'YYYY-MM-DD HH:mm:ss';
const wrapper = mount( const wrapper = mount(
<RangePicker <RangePicker ranges={{ 'recent two days': () => range }} format={format} />,
ranges={{ 'recent two days': () => range }}
format={format}
/>
); );
wrapper.find('.ant-calendar-picker-input').simulate('click'); wrapper.find('.ant-calendar-picker-input').simulate('click');
wrapper.find('.ant-calendar-range-quick-selector Tag').simulate('click'); wrapper.find('.ant-calendar-range-quick-selector Tag').simulate('click');
expect( expect(
wrapper.find('.ant-calendar-range-picker-input').first().getDOMNode().value wrapper
.find('.ant-calendar-range-picker-input')
.first()
.getDOMNode().value,
).toBe(range[0].format(format)); ).toBe(range[0].format(format));
expect( expect(
wrapper.find('.ant-calendar-range-picker-input').last().getDOMNode().value wrapper
.find('.ant-calendar-range-picker-input')
.last()
.getDOMNode().value,
).toBe(range[1].format(format)); ).toBe(range[1].format(format));
}); });
}); });
@ -193,12 +257,7 @@ describe('RangePicker', () => {
it('triggers onOk when click on preset range', () => { it('triggers onOk when click on preset range', () => {
const handleOk = jest.fn(); const handleOk = jest.fn();
const range = [moment().subtract(2, 'd'), moment()]; const range = [moment().subtract(2, 'd'), moment()];
const wrapper = mount( const wrapper = mount(<RangePicker ranges={{ 'recent two days': range }} onOk={handleOk} />);
<RangePicker
ranges={{ 'recent two days': range }}
onOk={handleOk}
/>
);
wrapper.find('.ant-calendar-picker-input').simulate('click'); wrapper.find('.ant-calendar-picker-input').simulate('click');
wrapper.find('.ant-calendar-range-quick-selector Tag').simulate('click'); wrapper.find('.ant-calendar-range-quick-selector Tag').simulate('click');
expect(handleOk).toBeCalledWith(range); expect(handleOk).toBeCalledWith(range);
@ -211,25 +270,46 @@ describe('RangePicker', () => {
selectDate(wrapper, moment('2017-09-18'), 0); selectDate(wrapper, moment('2017-09-18'), 0);
selectDate(wrapper, moment('2017-10-18'), 1); selectDate(wrapper, moment('2017-10-18'), 1);
wrapper.find('.ant-calendar-picker-input').simulate('click'); wrapper.find('.ant-calendar-picker-input').simulate('click');
expect(() => ( expect(() =>
wrapper.find('.ant-calendar-input').at(1).simulate('change', { target: { value: '2016-01-01' } }) wrapper
)).not.toThrow(); .find('.ant-calendar-input')
.at(1)
.simulate('change', { target: { value: '2016-01-01' } }),
).not.toThrow();
}); });
it('changes year/month when under control', () => { it('changes year/month when under control', () => {
const wrapper = mount(<RangePicker value={[moment('2018-07-01'), moment('2018-07-02')]} />); const wrapper = mount(<RangePicker value={[moment('2018-07-01'), moment('2018-07-02')]} />);
openPanel(wrapper); openPanel(wrapper);
expect(wrapper.find('.ant-calendar-my-select').first().text()).toBe('Jul2018'); expect(
wrapper.find('.ant-calendar-prev-year-btn').first().simulate('click'); wrapper
wrapper.find('.ant-calendar-prev-month-btn').first().simulate('click'); .find('.ant-calendar-my-select')
expect(wrapper.find('.ant-calendar-my-select').first().text()).toBe('Jun2017'); .first()
.text(),
).toBe('Jul2018');
wrapper
.find('.ant-calendar-prev-year-btn')
.first()
.simulate('click');
wrapper
.find('.ant-calendar-prev-month-btn')
.first()
.simulate('click');
expect(
wrapper
.find('.ant-calendar-my-select')
.first()
.text(),
).toBe('Jun2017');
}); });
// https://github.com/ant-design/ant-design/issues/11631 // https://github.com/ant-design/ant-design/issues/11631
it('triggers onOpenChange when click on preset range', () => { it('triggers onOpenChange when click on preset range', () => {
const handleOpenChange = jest.fn(); const handleOpenChange = jest.fn();
const range = [moment().subtract(2, 'd'), moment()]; const range = [moment().subtract(2, 'd'), moment()];
const wrapper = mount(<RangePicker onOpenChange={handleOpenChange} ranges={{ 'recent two days': range }} />); const wrapper = mount(
<RangePicker onOpenChange={handleOpenChange} ranges={{ 'recent two days': range }} />,
);
wrapper.find('.ant-calendar-picker-input').simulate('click'); wrapper.find('.ant-calendar-picker-input').simulate('click');
wrapper.find('.ant-calendar-range-quick-selector Tag').simulate('click'); wrapper.find('.ant-calendar-range-quick-selector Tag').simulate('click');
expect(handleOpenChange).toBeCalledWith(false); expect(handleOpenChange).toBeCalledWith(false);

View File

@ -9,9 +9,7 @@ describe('WeekPicker', () => {
focusTest(WeekPicker); focusTest(WeekPicker);
it('should support style prop', () => { it('should support style prop', () => {
const wrapper = mount( const wrapper = mount(<WeekPicker style={{ width: 400 }} />);
<WeekPicker style={{ width: 400 }} />
);
expect(wrapper.render()).toMatchSnapshot(); expect(wrapper.render()).toMatchSnapshot();
}); });
}); });

View File

@ -3,10 +3,7 @@ import { mount } from 'enzyme';
import moment from 'moment'; import moment from 'moment';
import MockDate from 'mockdate'; import MockDate from 'mockdate';
import DatePicker from '..'; import DatePicker from '..';
import { import { selectDate, openPanel } from './utils';
selectDate,
openPanel,
} from './utils';
const { MonthPicker, WeekPicker, RangePicker } = DatePicker; const { MonthPicker, WeekPicker, RangePicker } = DatePicker;
@ -20,18 +17,14 @@ describe('DatePicker', () => {
}); });
it('should focus trigger input after select date in DatePicker', () => { it('should focus trigger input after select date in DatePicker', () => {
const wrapper = mount( const wrapper = mount(<DatePicker />);
<DatePicker />
);
openPanel(wrapper); openPanel(wrapper);
selectDate(wrapper, moment('2016-11-23')); selectDate(wrapper, moment('2016-11-23'));
expect(wrapper.find('.ant-calendar-picker-input').getDOMNode()).toBe(document.activeElement); expect(wrapper.find('.ant-calendar-picker-input').getDOMNode()).toBe(document.activeElement);
}); });
it('should focus trigger input after select date in RangePicker', () => { it('should focus trigger input after select date in RangePicker', () => {
const wrapper = mount( const wrapper = mount(<RangePicker />);
<RangePicker />
);
openPanel(wrapper); openPanel(wrapper);
selectDate(wrapper, moment('2016-11-23'), 0); selectDate(wrapper, moment('2016-11-23'), 0);
selectDate(wrapper, moment('2016-11-28'), 1); selectDate(wrapper, moment('2016-11-28'), 1);
@ -39,19 +32,21 @@ describe('DatePicker', () => {
}); });
it('should focus trigger input after select date in MonthPicker', () => { it('should focus trigger input after select date in MonthPicker', () => {
const wrapper = mount( const wrapper = mount(<MonthPicker />);
<MonthPicker />
);
openPanel(wrapper); openPanel(wrapper);
wrapper.find('.ant-calendar-month-panel-month').first().simulate('click'); wrapper
wrapper.find('.ant-calendar-month-panel-cell').at(6).hasClass('ant-calendar-month-panel-selected-cell'); .find('.ant-calendar-month-panel-month')
.first()
.simulate('click');
wrapper
.find('.ant-calendar-month-panel-cell')
.at(6)
.hasClass('ant-calendar-month-panel-selected-cell');
expect(wrapper.find('.ant-calendar-picker-input').getDOMNode()).toBe(document.activeElement); expect(wrapper.find('.ant-calendar-picker-input').getDOMNode()).toBe(document.activeElement);
}); });
it('should focus trigger input after select date in WeekPicker', () => { it('should focus trigger input after select date in WeekPicker', () => {
const wrapper = mount( const wrapper = mount(<WeekPicker />);
<WeekPicker />
);
openPanel(wrapper); openPanel(wrapper);
selectDate(wrapper, moment('2016-11-23')); selectDate(wrapper, moment('2016-11-23'));
expect(wrapper.find('.ant-calendar-picker-input').getDOMNode()).toBe(document.activeElement); expect(wrapper.find('.ant-calendar-picker-input').getDOMNode()).toBe(document.activeElement);

View File

@ -8,19 +8,29 @@ const { MonthPicker, WeekPicker } = DatePicker;
describe('MonthPicker and WeekPicker', () => { describe('MonthPicker and WeekPicker', () => {
it('render MonthPicker', () => { it('render MonthPicker', () => {
const birthday = moment('2000-01-01', 'YYYY-MM-DD').locale('zh-cn'); const birthday = moment('2000-01-01', 'YYYY-MM-DD').locale('zh-cn');
const wrapper = mount( const wrapper = mount(<MonthPicker open />);
<MonthPicker open />
);
wrapper.setProps({ value: birthday }); wrapper.setProps({ value: birthday });
expect(render(wrapper.find('Trigger').instance().getComponent())).toMatchSnapshot(); expect(
render(
wrapper
.find('Trigger')
.instance()
.getComponent(),
),
).toMatchSnapshot();
}); });
it('render WeekPicker', () => { it('render WeekPicker', () => {
const birthday = moment('2000-01-01', 'YYYY-MM-DD').locale('zh-cn'); const birthday = moment('2000-01-01', 'YYYY-MM-DD').locale('zh-cn');
const wrapper = mount( const wrapper = mount(<WeekPicker open />);
<WeekPicker open />
);
wrapper.setProps({ value: birthday }); wrapper.setProps({ value: birthday });
expect(render(wrapper.find('Trigger').instance().getComponent())).toMatchSnapshot(); expect(
render(
wrapper
.find('Trigger')
.instance()
.getComponent(),
),
).toMatchSnapshot();
}); });
}); });

View File

@ -9,11 +9,19 @@ describe('DatePicker with showTime', () => {
const onChangeFn = jest.fn(); const onChangeFn = jest.fn();
const onOpenChangeFn = jest.fn(); const onOpenChangeFn = jest.fn();
const wrapper = mount( const wrapper = mount(
<DatePicker showTime open onChange={onChangeFn} onOpenChange={onOpenChangeFn} /> <DatePicker showTime open onChange={onChangeFn} onOpenChange={onOpenChangeFn} />,
); );
const calendarWrapper = mount(wrapper.find('Trigger').instance().getComponent()); const calendarWrapper = mount(
calendarWrapper.find('.ant-calendar-date').at(0).simulate('click'); wrapper
.find('Trigger')
.instance()
.getComponent(),
);
calendarWrapper
.find('.ant-calendar-date')
.at(0)
.simulate('click');
expect(onChangeFn).toHaveBeenCalled(); expect(onChangeFn).toHaveBeenCalled();
expect(onOpenChangeFn).not.toHaveBeenCalled(); expect(onOpenChangeFn).not.toHaveBeenCalled();
}); });
@ -24,10 +32,21 @@ describe('DatePicker with showTime', () => {
const onChangeFn = jest.fn(); const onChangeFn = jest.fn();
const wrapper = mount( const wrapper = mount(
<DatePicker showTime open onChange={onChangeFn} onOk={onOkFn} onOpenChange={onOpenChangeFn} /> <DatePicker
showTime
open
onChange={onChangeFn}
onOk={onOkFn}
onOpenChange={onOpenChangeFn}
/>,
); );
const calendarWrapper = mount(wrapper.find('Trigger').instance().getComponent()); const calendarWrapper = mount(
wrapper
.find('Trigger')
.instance()
.getComponent(),
);
calendarWrapper.find('.ant-calendar-ok-btn').simulate('click'); calendarWrapper.find('.ant-calendar-ok-btn').simulate('click');
expect(onOkFn).toHaveBeenCalled(); expect(onOkFn).toHaveBeenCalled();
expect(onOpenChangeFn).toHaveBeenCalledWith(false); expect(onOpenChangeFn).toHaveBeenCalledWith(false);
@ -39,22 +58,33 @@ describe('DatePicker with showTime', () => {
const onChangeFn = jest.fn(); const onChangeFn = jest.fn();
const wrapper = mount( const wrapper = mount(
<DatePicker showTime open onChange={onChangeFn} onOpenChange={onOpenChangeFn} /> <DatePicker showTime open onChange={onChangeFn} onOpenChange={onOpenChangeFn} />,
); );
const calendarWrapper = mount(wrapper.find('Trigger').instance().getComponent()); const calendarWrapper = mount(
wrapper
.find('Trigger')
.instance()
.getComponent(),
);
calendarWrapper.find('.ant-calendar-today-btn').simulate('click'); calendarWrapper.find('.ant-calendar-today-btn').simulate('click');
expect(onOpenChangeFn).toHaveBeenCalledWith(false); expect(onOpenChangeFn).toHaveBeenCalledWith(false);
expect(onChangeFn).toHaveBeenCalled(); expect(onChangeFn).toHaveBeenCalled();
}); });
it('should have correct className when use12Hours is true', () => { it('should have correct className when use12Hours is true', () => {
const wrapper = mount( const wrapper = mount(<DatePicker showTime={{ use12Hours: true }} open />);
<DatePicker showTime={{ use12Hours: true }} open /> const calendarWrapper = mount(
wrapper
.find('Trigger')
.instance()
.getComponent(),
); );
const calendarWrapper = mount(wrapper.find('Trigger').instance().getComponent());
expect(calendarWrapper.find('.ant-calendar-time-picker-column-4').length).toBe(0); expect(calendarWrapper.find('.ant-calendar-time-picker-column-4').length).toBe(0);
calendarWrapper.find('.ant-calendar-time-picker-btn').at(0).simulate('click'); calendarWrapper
.find('.ant-calendar-time-picker-btn')
.at(0)
.simulate('click');
expect(calendarWrapper.find('.ant-calendar-time-picker-column-4').hostNodes().length).toBe(1); expect(calendarWrapper.find('.ant-calendar-time-picker-column-4').hostNodes().length).toBe(1);
}); });
}); });
@ -64,16 +94,39 @@ describe('RangePicker with showTime', () => {
const onChangeFn = jest.fn(); const onChangeFn = jest.fn();
const onOpenChangeFn = jest.fn(); const onOpenChangeFn = jest.fn();
const wrapper = mount( const wrapper = mount(
<RangePicker showTime open onChange={onChangeFn} onOpenChange={onOpenChangeFn} /> <RangePicker showTime open onChange={onChangeFn} onOpenChange={onOpenChangeFn} />,
); );
const calendarWrapper = mount(wrapper.find('Trigger').instance().getComponent()); const calendarWrapper = mount(
expect(calendarWrapper.find('.ant-calendar-time-picker-btn').hasClass('ant-calendar-time-picker-btn-disabled')).toBe(true); wrapper
expect(calendarWrapper.find('.ant-calendar-ok-btn').hasClass('ant-calendar-ok-btn-disabled')).toBe(true); .find('Trigger')
calendarWrapper.find('.ant-calendar-date').at(10).simulate('click'); .instance()
calendarWrapper.find('.ant-calendar-date').at(11).simulate('click'); .getComponent(),
expect(calendarWrapper.find('.ant-calendar-time-picker-btn').hasClass('ant-calendar-time-picker-btn-disabled')).toBe(false); );
expect(calendarWrapper.find('.ant-calendar-ok-btn').hasClass('ant-calendar-ok-btn-disabled')).toBe(false); expect(
calendarWrapper
.find('.ant-calendar-time-picker-btn')
.hasClass('ant-calendar-time-picker-btn-disabled'),
).toBe(true);
expect(
calendarWrapper.find('.ant-calendar-ok-btn').hasClass('ant-calendar-ok-btn-disabled'),
).toBe(true);
calendarWrapper
.find('.ant-calendar-date')
.at(10)
.simulate('click');
calendarWrapper
.find('.ant-calendar-date')
.at(11)
.simulate('click');
expect(
calendarWrapper
.find('.ant-calendar-time-picker-btn')
.hasClass('ant-calendar-time-picker-btn-disabled'),
).toBe(false);
expect(
calendarWrapper.find('.ant-calendar-ok-btn').hasClass('ant-calendar-ok-btn-disabled'),
).toBe(false);
expect(onChangeFn).toHaveBeenCalled(); expect(onChangeFn).toHaveBeenCalled();
expect(onOpenChangeFn).not.toHaveBeenCalled(); expect(onOpenChangeFn).not.toHaveBeenCalled();
}); });
@ -89,12 +142,23 @@ describe('RangePicker with showTime', () => {
onOk={onOkFn} onOk={onOkFn}
onChange={onChangeFn} onChange={onChangeFn}
onOpenChange={onOpenChangeFn} onOpenChange={onOpenChangeFn}
/> />,
); );
const calendarWrapper = mount(wrapper.find('Trigger').instance().getComponent()); const calendarWrapper = mount(
calendarWrapper.find('.ant-calendar-date').at(10).simulate('click'); wrapper
calendarWrapper.find('.ant-calendar-date').at(11).simulate('click'); .find('Trigger')
.instance()
.getComponent(),
);
calendarWrapper
.find('.ant-calendar-date')
.at(10)
.simulate('click');
calendarWrapper
.find('.ant-calendar-date')
.at(11)
.simulate('click');
onChangeFn.mockClear(); onChangeFn.mockClear();
calendarWrapper.find('.ant-calendar-ok-btn').simulate('click'); calendarWrapper.find('.ant-calendar-ok-btn').simulate('click');
expect(onOkFn).toHaveBeenCalled(); expect(onOkFn).toHaveBeenCalled();

View File

@ -8,7 +8,9 @@ export function selectDate(wrapper, date, index) {
} }
export function hasSelected(wrapper, date) { export function hasSelected(wrapper, date) {
return wrapper.find({ title: date.format('LL'), role: 'gridcell' }).hasClass('ant-calendar-selected-day'); return wrapper
.find({ title: date.format('LL'), role: 'gridcell' })
.hasClass('ant-calendar-selected-day');
} }
export function openPanel(wrapper) { export function openPanel(wrapper) {
@ -16,7 +18,10 @@ export function openPanel(wrapper) {
} }
export function clearInput(wrapper) { export function clearInput(wrapper) {
wrapper.find('.ant-calendar-picker-clear').hostNodes().simulate('click'); wrapper
.find('.ant-calendar-picker-clear')
.hostNodes()
.simulate('click');
} }
export function nextYear(wrapper) { export function nextYear(wrapper) {

View File

@ -60,7 +60,7 @@ export default function createPicker(TheCalendar: React.ComponentClass): any {
if (value && !interopDefault(moment).isMoment(value)) { if (value && !interopDefault(moment).isMoment(value)) {
throw new Error( throw new Error(
'The value/defaultValue of DatePicker or MonthPicker must be ' + 'The value/defaultValue of DatePicker or MonthPicker must be ' +
'a moment object after `antd@2.0`, see: https://u.ant.design/date-picker-value', 'a moment object after `antd@2.0`, see: https://u.ant.design/date-picker-value',
); );
} }
this.state = { this.state = {
@ -74,17 +74,15 @@ export default function createPicker(TheCalendar: React.ComponentClass): any {
const { renderExtraFooter } = this.props; const { renderExtraFooter } = this.props;
const { prefixCls } = this; const { prefixCls } = this;
return renderExtraFooter ? ( return renderExtraFooter ? (
<div className={`${prefixCls}-footer-extra`}> <div className={`${prefixCls}-footer-extra`}>{renderExtraFooter(...args)}</div>
{renderExtraFooter(...args)}
</div>
) : null; ) : null;
} };
clearSelection = (e: React.MouseEvent<HTMLElement>) => { clearSelection = (e: React.MouseEvent<HTMLElement>) => {
e.preventDefault(); e.preventDefault();
e.stopPropagation(); e.stopPropagation();
this.handleChange(null); this.handleChange(null);
} };
handleChange = (value: moment.Moment | null) => { handleChange = (value: moment.Moment | null) => {
const props = this.props; const props = this.props;
@ -95,11 +93,11 @@ export default function createPicker(TheCalendar: React.ComponentClass): any {
}); });
} }
props.onChange(value, (value && value.format(props.format)) || ''); props.onChange(value, (value && value.format(props.format)) || '');
} };
handleCalendarChange = (value: moment.Moment) => { handleCalendarChange = (value: moment.Moment) => {
this.setState({ showDate: value }); this.setState({ showDate: value });
} };
handleOpenChange = (open: boolean) => { handleOpenChange = (open: boolean) => {
const { onOpenChange } = this.props; const { onOpenChange } = this.props;
@ -126,7 +124,7 @@ export default function createPicker(TheCalendar: React.ComponentClass): any {
saveInput = (node: any) => { saveInput = (node: any) => {
this.input = node; this.input = node;
} };
renderPicker = ({ getPrefixCls }: ConfigConsumerProps) => { renderPicker = ({ getPrefixCls }: ConfigConsumerProps) => {
const { value, showDate, open } = this.state; const { value, showDate, open } = this.state;
@ -139,8 +137,7 @@ export default function createPicker(TheCalendar: React.ComponentClass): any {
// https://github.com/facebook/react/issues/12397 // https://github.com/facebook/react/issues/12397
this.prefixCls = prefixCls; this.prefixCls = prefixCls;
const placeholder = ('placeholder' in props) const placeholder = 'placeholder' in props ? props.placeholder : locale.lang.placeholder;
? props.placeholder : locale.lang.placeholder;
const disabledTime = props.showTime ? props.disabledTime : null; const disabledTime = props.showTime ? props.disabledTime : null;
@ -171,7 +168,10 @@ export default function createPicker(TheCalendar: React.ComponentClass): any {
calendarProps.mode = props.mode; calendarProps.mode = props.mode;
} }
warning(!('onOK' in props), 'It should be `DatePicker[onOk]` or `MonthPicker[onOk]`, instead of `onOK`!'); warning(
!('onOK' in props),
'It should be `DatePicker[onOk]` or `MonthPicker[onOk]`, instead of `onOK`!',
);
const calendar = ( const calendar = (
<TheCalendar <TheCalendar
{...calendarProps} {...calendarProps}
@ -195,28 +195,27 @@ export default function createPicker(TheCalendar: React.ComponentClass): any {
/> />
); );
const clearIcon = (!props.disabled && props.allowClear && value) ? ( const clearIcon =
<Icon !props.disabled && props.allowClear && value ? (
type="close-circle" <Icon
className={`${prefixCls}-picker-clear`} type="close-circle"
onClick={this.clearSelection} className={`${prefixCls}-picker-clear`}
theme="filled" onClick={this.clearSelection}
/> theme="filled"
) : null; />
) : null;
const inputIcon = suffixIcon && ( const inputIcon = (suffixIcon &&
React.isValidElement<{ className?: string }>(suffixIcon) (React.isValidElement<{ className?: string }>(suffixIcon) ? (
? React.cloneElement( React.cloneElement(suffixIcon, {
suffixIcon, className: classNames({
{ [suffixIcon.props.className!]: suffixIcon.props.className,
className: classNames({ [`${prefixCls}-picker-icon`]: true,
[suffixIcon.props.className!]: suffixIcon.props.className, }),
[`${prefixCls}-picker-icon`]: true, })
}), ) : (
}, <span className={`${prefixCls}-picker-icon`}>{suffixIcon}</span>
) : <span className={`${prefixCls}-picker-icon`}>{suffixIcon}</span>) || ( ))) || <Icon type="calendar" className={`${prefixCls}-picker-icon`} />;
<Icon type="calendar" className={`${prefixCls}-picker-icon`} />
);
const dataOrAriaProps = getDataOrAriaProps(props); const dataOrAriaProps = getDataOrAriaProps(props);
const input = ({ value: inputValue }: { value: moment.Moment | null }) => ( const input = ({ value: inputValue }: { value: moment.Moment | null }) => (
@ -260,14 +259,10 @@ export default function createPicker(TheCalendar: React.ComponentClass): any {
</RcDatePicker> </RcDatePicker>
</span> </span>
); );
} };
render() { render() {
return ( return <ConfigConsumer>{this.renderPicker}</ConfigConsumer>;
<ConfigConsumer>
{this.renderPicker}
</ConfigConsumer>
);
} }
} }
polyfill(CalenderWrapper); polyfill(CalenderWrapper);

View File

@ -7,7 +7,9 @@ import RangePicker from './RangePicker';
import WeekPicker from './WeekPicker'; import WeekPicker from './WeekPicker';
import { DatePickerProps, DatePickerDecorator } from './interface'; import { DatePickerProps, DatePickerDecorator } from './interface';
const DatePicker = wrapPicker(createPicker(RcCalendar)) as React.ClassicComponentClass<DatePickerProps>; const DatePicker = wrapPicker(createPicker(RcCalendar)) as React.ClassicComponentClass<
DatePickerProps
>;
const MonthPicker = wrapPicker(createPicker(MonthCalendar), 'YYYY-MM'); const MonthPicker = wrapPicker(createPicker(MonthCalendar), 'YYYY-MM');

View File

@ -36,10 +36,12 @@ export interface DatePickerProps extends PickerProps, SinglePickerProps {
showTime?: TimePickerProps | boolean; showTime?: TimePickerProps | boolean;
showToday?: boolean; showToday?: boolean;
open?: boolean; open?: boolean;
disabledTime?: (current: moment.Moment) => { disabledTime?: (
disabledHours?: () => number[], current: moment.Moment,
disabledMinutes?: () => number[], ) => {
disabledSeconds?: () => number[], disabledHours?: () => number[];
disabledMinutes?: () => number[];
disabledSeconds?: () => number[];
}; };
onOpenChange?: (status: boolean) => void; onOpenChange?: (status: boolean) => void;
onOk?: (selectedTime: RangePickerValue) => void; onOk?: (selectedTime: RangePickerValue) => void;
@ -52,10 +54,10 @@ export interface MonthPickerProps extends PickerProps, SinglePickerProps {
} }
export type RangePickerValue = export type RangePickerValue =
undefined[] | | undefined[]
[moment.Moment] | | [moment.Moment]
[undefined, moment.Moment] | | [undefined, moment.Moment]
[moment.Moment, moment.Moment]; | [moment.Moment, moment.Moment];
export type RangePickerPresetRange = RangePickerValue | (() => RangePickerValue); export type RangePickerPresetRange = RangePickerValue | (() => RangePickerValue);
export interface RangePickerProps extends PickerProps { export interface RangePickerProps extends PickerProps {
@ -68,14 +70,17 @@ export interface RangePickerProps extends PickerProps {
onOk?: (selectedTime: moment.Moment) => void; onOk?: (selectedTime: moment.Moment) => void;
showTime?: TimePickerProps | boolean; showTime?: TimePickerProps | boolean;
ranges?: { ranges?: {
[range: string]: RangePickerPresetRange, [range: string]: RangePickerPresetRange;
}; };
placeholder?: [string, string]; placeholder?: [string, string];
mode?: string | string[]; mode?: string | string[];
disabledTime?: (current: moment.Moment, type: string) => { disabledTime?: (
disabledHours?: () => number[], current: moment.Moment,
disabledMinutes?: () => number[], type: string,
disabledSeconds?: () => number[], ) => {
disabledHours?: () => number[];
disabledMinutes?: () => number[];
disabledSeconds?: () => number[];
}; };
onPanelChange?: (value?: RangePickerValue, mode?: string | string[]) => void; onPanelChange?: (value?: RangePickerValue, mode?: string | string[]) => void;
} }

View File

@ -1,10 +1,7 @@
{ {
"lang": { "lang": {
"placeholder": "Select date", "placeholder": "Select date",
"rangePlaceholder": [ "rangePlaceholder": ["Start date", "End date"],
"Start date",
"End date"
],
"today": "Today", "today": "Today",
"now": "Now", "now": "Now",
"backToToday": "Back to today", "backToToday": "Back to today",

View File

@ -5,7 +5,7 @@ import TimePickerLocale from '../../time-picker/locale/it_IT';
const locale = { const locale = {
lang: { lang: {
placeholder: 'Selezionare la data', placeholder: 'Selezionare la data',
rangePlaceholder: ['Data d\'inizio', 'Data di fine'], rangePlaceholder: ["Data d'inizio", 'Data di fine'],
...CalendarLocale, ...CalendarLocale,
}, },
timePickerLocale: { timePickerLocale: {

View File

@ -38,7 +38,7 @@
position: absolute; position: absolute;
top: 0; top: 0;
color: @text-color-secondary; color: @text-color-secondary;
font-family: Arial, "Hiragino Sans GB", "Microsoft Yahei", "Microsoft Sans Serif", sans-serif; font-family: Arial, 'Hiragino Sans GB', 'Microsoft Yahei', 'Microsoft Sans Serif', sans-serif;
padding: 0 5px; padding: 0 5px;
font-size: 16px; font-size: 16px;
display: inline-block; display: inline-block;
@ -214,7 +214,9 @@
background: tint(@primary-color, 80%); background: tint(@primary-color, 80%);
} }
&-selected-date, &-selected-start-date, &-selected-end-date { &-selected-date,
&-selected-start-date,
&-selected-end-date {
.@{calendar-prefix-cls}-date { .@{calendar-prefix-cls}-date {
background: @primary-color; background: @primary-color;
color: @text-color-inverse; color: @text-color-inverse;
@ -243,7 +245,7 @@
padding-right: 5px; padding-right: 5px;
padding-left: 5px; padding-left: 5px;
&:before { &:before {
content: " "; content: ' ';
position: absolute; position: absolute;
top: -1px; top: -1px;
left: 5px; left: 5px;

View File

@ -15,11 +15,11 @@
} }
.@{calendar-prefix-cls}-decade-panel-header { .@{calendar-prefix-cls}-decade-panel-header {
.calendarPanelHeader(~"@{calendar-prefix-cls}-decade-panel"); .calendarPanelHeader(~'@{calendar-prefix-cls}-decade-panel');
} }
.@{calendar-prefix-cls}-decade-panel-body { .@{calendar-prefix-cls}-decade-panel-body {
height: ~"calc(100% - 40px)"; height: ~'calc(100% - 40px)';
} }
.@{calendar-prefix-cls}-decade-panel-table { .@{calendar-prefix-cls}-decade-panel-table {

View File

@ -9,7 +9,8 @@
background: @component-background; background: @component-background;
outline: none; outline: none;
> div { // TODO: this is a useless wrapper, and we need to remove it in rc-calendar > div {
// TODO: this is a useless wrapper, and we need to remove it in rc-calendar
height: 100%; height: 100%;
} }
} }
@ -19,11 +20,11 @@
} }
.@{calendar-prefix-cls}-month-panel-header { .@{calendar-prefix-cls}-month-panel-header {
.calendarPanelHeader(~"@{calendar-prefix-cls}-month-panel"); .calendarPanelHeader(~'@{calendar-prefix-cls}-month-panel');
} }
.@{calendar-prefix-cls}-month-panel-body { .@{calendar-prefix-cls}-month-panel-body {
height: ~"calc(100% - 40px)"; height: ~'calc(100% - 40px)';
} }
.@{calendar-prefix-cls}-month-panel-table { .@{calendar-prefix-cls}-month-panel-table {

View File

@ -1,4 +1,4 @@
@import "../../button/style/mixin"; @import '../../button/style/mixin';
.@{calendar-prefix-cls}-picker-container { .@{calendar-prefix-cls}-picker-container {
.reset-component; .reset-component;
@ -65,7 +65,7 @@
margin-top: -7px; margin-top: -7px;
line-height: 14px; line-height: 14px;
font-size: @font-size-sm; font-size: @font-size-sm;
transition: all .3s; transition: all 0.3s;
user-select: none; user-select: none;
z-index: 1; z-index: 1;
} }
@ -89,7 +89,7 @@
} }
&-icon { &-icon {
font-family: "anticon"; font-family: 'anticon';
font-size: @font-size-base; font-size: @font-size-base;
color: @disabled-color; color: @disabled-color;
display: inline-block; display: inline-block;

View File

@ -28,7 +28,7 @@
.@{calendar-prefix-cls}-date-panel { .@{calendar-prefix-cls}-date-panel {
&::after { &::after {
content: "."; content: '.';
display: block; display: block;
height: 0; height: 0;
clear: both; clear: both;
@ -134,7 +134,7 @@
z-index: 1; z-index: 1;
} }
&:before { &:before {
content: ""; content: '';
display: block; display: block;
background: @item-active-bg; background: @item-active-bg;
border-radius: 0; border-radius: 0;

View File

@ -90,7 +90,7 @@
} }
li:last-child:after { li:last-child:after {
content: ""; content: '';
height: 202px; height: 202px;
display: block; display: block;
} }

View File

@ -3,7 +3,7 @@
opacity: 0.5; opacity: 0.5;
} }
.@{calendar-prefix-cls}-body tr { .@{calendar-prefix-cls}-body tr {
transition: all .3s; transition: all 0.3s;
cursor: pointer; cursor: pointer;
&:hover { &:hover {
background: @primary-1; background: @primary-1;

View File

@ -9,7 +9,8 @@
background: @component-background; background: @component-background;
outline: none; outline: none;
> div { // TODO: this is a useless wrapper, and we need to remove it in rc-calendar > div {
// TODO: this is a useless wrapper, and we need to remove it in rc-calendar
height: 100%; height: 100%;
} }
} }
@ -19,11 +20,11 @@
} }
.@{calendar-prefix-cls}-year-panel-header { .@{calendar-prefix-cls}-year-panel-header {
.calendarPanelHeader(~"@{calendar-prefix-cls}-year-panel"); .calendarPanelHeader(~'@{calendar-prefix-cls}-year-panel');
} }
.@{calendar-prefix-cls}-year-panel-body { .@{calendar-prefix-cls}-year-panel-body {
height: ~"calc(100% - 40px)"; height: ~'calc(100% - 40px)';
} }
.@{calendar-prefix-cls}-year-panel-table { .@{calendar-prefix-cls}-year-panel-table {

View File

@ -1,17 +1,17 @@
@import "../../style/themes/default"; @import '../../style/themes/default';
@import "../../style/mixins/index"; @import '../../style/mixins/index';
@import "../../input/style/mixin"; @import '../../input/style/mixin';
@import "../../button/style/mixin"; @import '../../button/style/mixin';
@calendar-prefix-cls: ~"@{ant-prefix}-calendar"; @calendar-prefix-cls: ~'@{ant-prefix}-calendar';
@calendar-timepicker-prefix-cls: ~"@{ant-prefix}-calendar-time-picker"; @calendar-timepicker-prefix-cls: ~'@{ant-prefix}-calendar-time-picker';
@import "Picker"; @import 'Picker';
@import "Calendar"; @import 'Calendar';
@import "RangePicker"; @import 'RangePicker';
@import "TimePicker"; @import 'TimePicker';
@import "MonthPanel"; @import 'MonthPanel';
@import "YearPanel"; @import 'YearPanel';
@import "DecadePanel"; @import 'DecadePanel';
@import "MonthPicker"; @import 'MonthPicker';
@import "WeekPicker"; @import 'WeekPicker';

View File

@ -29,12 +29,9 @@ export default function wrapPicker(Picker: React.ComponentClass<any>, defaultFor
format: defaultFormat || 'YYYY-MM-DD', format: defaultFormat || 'YYYY-MM-DD',
transitionName: 'slide-up', transitionName: 'slide-up',
popupStyle: {}, popupStyle: {},
onChange() { onChange() {},
}, onOk() {},
onOk() { onOpenChange() {},
},
onOpenChange() {
},
locale: {}, locale: {},
}; };
@ -50,35 +47,35 @@ export default function wrapPicker(Picker: React.ComponentClass<any>, defaultFor
handleOpenChange = (open: boolean) => { handleOpenChange = (open: boolean) => {
const { onOpenChange } = this.props; const { onOpenChange } = this.props;
onOpenChange(open); onOpenChange(open);
} };
handleFocus: React.FocusEventHandler<HTMLInputElement> = (e) => { handleFocus: React.FocusEventHandler<HTMLInputElement> = e => {
const { onFocus } = this.props; const { onFocus } = this.props;
if (onFocus) { if (onFocus) {
onFocus(e); onFocus(e);
} }
} };
handleBlur: React.FocusEventHandler<HTMLInputElement> = (e) => { handleBlur: React.FocusEventHandler<HTMLInputElement> = e => {
const { onBlur } = this.props; const { onBlur } = this.props;
if (onBlur) { if (onBlur) {
onBlur(e); onBlur(e);
} }
} };
handleMouseEnter: React.MouseEventHandler<HTMLInputElement> = (e) => { handleMouseEnter: React.MouseEventHandler<HTMLInputElement> = e => {
const { onMouseEnter } = this.props; const { onMouseEnter } = this.props;
if (onMouseEnter) { if (onMouseEnter) {
onMouseEnter(e); onMouseEnter(e);
} }
} };
handleMouseLeave: React.MouseEventHandler<HTMLInputElement> = (e) => { handleMouseLeave: React.MouseEventHandler<HTMLInputElement> = e => {
const { onMouseLeave } = this.props; const { onMouseLeave } = this.props;
if (onMouseLeave) { if (onMouseLeave) {
onMouseLeave(e); onMouseLeave(e);
} }
} };
focus() { focus() {
this.picker.focus(); this.picker.focus();
@ -90,7 +87,7 @@ export default function wrapPicker(Picker: React.ComponentClass<any>, defaultFor
savePicker = (node: any) => { savePicker = (node: any) => {
this.picker = node; this.picker = node;
} };
getDefaultLocale = () => { getDefaultLocale = () => {
const result = { const result = {
@ -102,15 +99,18 @@ export default function wrapPicker(Picker: React.ComponentClass<any>, defaultFor
...(this.props.locale || {}).lang, ...(this.props.locale || {}).lang,
}; };
return result; return result;
} };
renderPicker = (locale: any, localeCode: string) => { renderPicker = (locale: any, localeCode: string) => {
return ( return (
<ConfigConsumer> <ConfigConsumer>
{({ getPrefixCls }: ConfigConsumerProps) => { {({ getPrefixCls }: ConfigConsumerProps) => {
const { const {
prefixCls: customizePrefixCls, inputPrefixCls: customizeInputPrefixCls, prefixCls: customizePrefixCls,
size, disabled, showTime, inputPrefixCls: customizeInputPrefixCls,
size,
disabled,
showTime,
} = this.props; } = this.props;
const prefixCls = getPrefixCls('calendar', customizePrefixCls); const prefixCls = getPrefixCls('calendar', customizePrefixCls);
const inputPrefixCls = getPrefixCls('input', customizeInputPrefixCls); const inputPrefixCls = getPrefixCls('input', customizeInputPrefixCls);
@ -127,7 +127,7 @@ export default function wrapPicker(Picker: React.ComponentClass<any>, defaultFor
const rcTimePickerProps = { const rcTimePickerProps = {
...generateShowHourMinuteSecond(timeFormat), ...generateShowHourMinuteSecond(timeFormat),
format: timeFormat, format: timeFormat,
use12Hours: (showTime && showTime.use12Hours), use12Hours: showTime && showTime.use12Hours,
}; };
const columns = getColumns(rcTimePickerProps); const columns = getColumns(rcTimePickerProps);
const timePickerCls = `${prefixCls}-time-picker-column-${columns}`; const timePickerCls = `${prefixCls}-time-picker-column-${columns}`;
@ -161,14 +161,11 @@ export default function wrapPicker(Picker: React.ComponentClass<any>, defaultFor
}} }}
</ConfigConsumer> </ConfigConsumer>
); );
} };
render() { render() {
return ( return (
<LocaleReceiver <LocaleReceiver componentName="DatePicker" defaultLocale={this.getDefaultLocale}>
componentName="DatePicker"
defaultLocale={this.getDefaultLocale}
>
{this.renderPicker} {this.renderPicker}
</LocaleReceiver> </LocaleReceiver>
); );

View File

@ -25,9 +25,8 @@ const Divider: React.SFC<DividerProps> = props => (
...restProps ...restProps
} = props; } = props;
const prefixCls = getPrefixCls('divider', customizePrefixCls); const prefixCls = getPrefixCls('divider', customizePrefixCls);
const orientationPrefix = (orientation.length > 0) ? '-' + orientation : orientation; const orientationPrefix = orientation.length > 0 ? '-' + orientation : orientation;
const classString = classNames( const classString = classNames(className, prefixCls, `${prefixCls}-${type}`, {
className, prefixCls, `${prefixCls}-${type}`, {
[`${prefixCls}-with-text${orientationPrefix}`]: children, [`${prefixCls}-with-text${orientationPrefix}`]: children,
[`${prefixCls}-dashed`]: !!dashed, [`${prefixCls}-dashed`]: !!dashed,
}); });

View File

@ -1,7 +1,7 @@
@import "../../style/themes/default"; @import '../../style/themes/default';
@import "../../style/mixins/index"; @import '../../style/mixins/index';
@divider-prefix-cls: ~"@{ant-prefix}-divider"; @divider-prefix-cls: ~'@{ant-prefix}-divider';
.@{divider-prefix-cls} { .@{divider-prefix-cls} {
.reset-component; .reset-component;
@ -39,7 +39,7 @@
margin: 16px 0; margin: 16px 0;
&:before, &:before,
&:after { &:after {
content: ""; content: '';
display: table-cell; display: table-cell;
position: relative; position: relative;
top: 50%; top: 50%;

View File

@ -5,80 +5,54 @@ import Drawer from '..';
describe('Drawer', () => { describe('Drawer', () => {
it('render correctly', () => { it('render correctly', () => {
const wrapper = render( const wrapper = render(
<Drawer <Drawer visible width={400} getContainer={false}>
visible
width={400}
getContainer={false}
>
Here is content of Drawer Here is content of Drawer
</Drawer> </Drawer>,
); );
expect(wrapper).toMatchSnapshot(); expect(wrapper).toMatchSnapshot();
}); });
it('render top drawer', () => { it('render top drawer', () => {
const wrapper = render( const wrapper = render(
<Drawer <Drawer visible height={400} placement="top" getContainer={false}>
visible
height={400}
placement="top"
getContainer={false}
>
Here is content of Drawer Here is content of Drawer
</Drawer> </Drawer>,
); );
expect(wrapper).toMatchSnapshot(); expect(wrapper).toMatchSnapshot();
}); });
it('have a title', () => { it('have a title', () => {
const wrapper = render( const wrapper = render(
<Drawer <Drawer visible title="Test Title" getContainer={false}>
visible
title="Test Title"
getContainer={false}
>
Here is content of Drawer Here is content of Drawer
</Drawer> </Drawer>,
); );
expect(wrapper).toMatchSnapshot(); expect(wrapper).toMatchSnapshot();
}); });
it('closable is false', () => { it('closable is false', () => {
const wrapper = render( const wrapper = render(
<Drawer <Drawer visible closable={false} getContainer={false}>
visible
closable={false}
getContainer={false}
>
Here is content of Drawer Here is content of Drawer
</Drawer> </Drawer>,
); );
expect(wrapper).toMatchSnapshot(); expect(wrapper).toMatchSnapshot();
}); });
it('destroyOnClose is true', () => { it('destroyOnClose is true', () => {
const wrapper = render( const wrapper = render(
<Drawer <Drawer destroyOnClose visible={false} getContainer={false}>
destroyOnClose
visible={false}
getContainer={false}
>
Here is content of Drawer Here is content of Drawer
</Drawer> </Drawer>,
); );
expect(wrapper).toMatchSnapshot(); expect(wrapper).toMatchSnapshot();
}); });
it('className is test_drawer', () => { it('className is test_drawer', () => {
const wrapper = render( const wrapper = render(
<Drawer <Drawer destroyOnClose visible={false} className="test_drawer" getContainer={false}>
destroyOnClose
visible={false}
className="test_drawer"
getContainer={false}
>
Here is content of Drawer Here is content of Drawer
</Drawer> </Drawer>,
); );
expect(wrapper).toMatchSnapshot(); expect(wrapper).toMatchSnapshot();
}); });

Some files were not shown because too many files have changed in this diff Show More