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 = {
extends: ['eslint-config-airbnb'],
extends: ['airbnb', 'prettier'],
env: {
browser: true,
node: true,
@ -8,18 +8,27 @@ const eslintrc = {
es6: true,
},
parser: 'babel-eslint',
plugins: [
'markdown',
'react',
'babel',
],
plugins: ['markdown', 'react', 'babel'],
rules: {
'react/jsx-one-expression-per-line': 0,
'react/prop-types': 0,
'react/forbid-prop-types': 0,
'import/no-extraneous-dependencies': ['error', {
devDependencies: ['site/**', 'tests/**', 'scripts/**', '**/*.test.js', '**/__tests__/*', '*.config.js', '**/*.md'],
}],
'react/jsx-indent': 0,
'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'] }],
'jsx-a11y/no-static-element-interactions': 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": {
"comment-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', () => {
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();
});

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@ -12,7 +12,7 @@ class AffixMounter extends React.Component {
});
}
getTarget = () => this.container
getTarget = () => this.container;
render() {
return (
@ -21,7 +21,9 @@ class AffixMounter extends React.Component {
height: 100,
overflowY: 'scroll',
}}
ref={(node) => { this.container = node; }}
ref={node => {
this.container = node;
}}
>
<div
className="background"
@ -32,12 +34,12 @@ class AffixMounter extends React.Component {
>
<Affix
target={() => this.container}
ref={(ele) => { this.affix = ele; }}
ref={ele => {
this.affix = ele;
}}
{...this.props}
>
<Button type="primary">
Fixed at the top of container
</Button>
<Button type="primary">Fixed at the top of container</Button>
</Affix>
</div>
</div>
@ -56,9 +58,14 @@ describe('Affix Render', () => {
jest.useRealTimers();
});
const scrollTo = (top) => {
const scrollTo = top => {
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;
events.scroll({
@ -86,7 +93,9 @@ describe('Affix Render', () => {
it('support offsetBottom', () => {
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();
scrollTo(0);
@ -102,7 +111,9 @@ describe('Affix Render', () => {
it('updatePosition when offsetTop changed', () => {
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();
scrollTo(100);

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@ -10,12 +10,17 @@ describe('AutoComplete with Custom Input Element Render', () => {
const wrapper = mount(
<AutoComplete dataSource={['12345', '23456', '34567']}>
<textarea />
</AutoComplete>
</AutoComplete>,
);
expect(wrapper.find('textarea').length).toBe(1);
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
expect(dropdownWrapper.find('MenuItem').length).toBe(3);
@ -26,7 +31,7 @@ describe('AutoComplete with Custom Input Element Render', () => {
mount(
<AutoComplete dataSource={[]}>
<input ref={mockRef} />
</AutoComplete>
</AutoComplete>,
);
expect(mockRef).toHaveBeenCalled();
});

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@ -1,7 +1,7 @@
@import "../../style/themes/default";
@import "../../style/mixins/index";
@import '../../style/themes/default';
@import '../../style/mixins/index';
@backtop-prefix-cls: ~"@{ant-prefix}-back-top";
@backtop-prefix-cls: ~'@{ant-prefix}-back-top';
.@{backtop-prefix-cls} {
.reset-component;
@ -20,12 +20,12 @@
background-color: @back-top-bg;
color: @back-top-color;
text-align: center;
transition: all .3s @ease-in-out;
transition: all 0.3s @ease-in-out;
overflow: hidden;
&:hover {
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;
width: 14px;
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';
function getNumberArray(num: string | number | undefined | null) {
return num ?
num.toString()
.split('')
.reverse()
.map(i => Number(i)) : [];
return num
? num
.toString()
.split('')
.reverse()
.map(i => Number(i))
: [];
}
export interface ScrollNumberProps {
@ -31,8 +33,7 @@ export interface ScrollNumberState {
export default class ScrollNumber extends Component<ScrollNumberProps, ScrollNumberState> {
static defaultProps = {
count: null,
onAnimated() {
},
onAnimated() {},
};
lastCount: any;
@ -71,49 +72,63 @@ export default class ScrollNumber extends Component<ScrollNumberProps, ScrollNum
}
this.lastCount = this.state.count;
// 复原数字初始位置
this.setState({
animateStarted: true,
}, () => {
// 等待数字位置复原完毕
// 开始设置完整的数字
setTimeout(() => {
this.setState({
animateStarted: false,
count: nextProps.count,
}, () => {
const onAnimated = this.props.onAnimated;
if (onAnimated) {
onAnimated();
}
});
}, 5);
});
this.setState(
{
animateStarted: true,
},
() => {
// 等待数字位置复原完毕
// 开始设置完整的数字
setTimeout(() => {
this.setState(
{
animateStarted: false,
count: nextProps.count,
},
() => {
const onAnimated = this.props.onAnimated;
if (onAnimated) {
onAnimated();
}
},
);
}, 5);
},
);
}
}
renderNumberList(position: number) {
const childrenToReturn: React.ReactElement<any>[] = [];
for (let i = 0; i < 30; i++) {
const currentClassName = (position === i) ? 'current' : '';
childrenToReturn.push(<p key={i.toString()} className={currentClassName}>{i % 10}</p>);
const currentClassName = position === i ? 'current' : '';
childrenToReturn.push(
<p key={i.toString()} className={currentClassName}>
{i % 10}
</p>,
);
}
return childrenToReturn;
}
renderCurrentNumber(prefixCls: string, num: number, i: number) {
const position = this.getPositionByNum(num, i);
const removeTransition = this.state.animateStarted ||
(getNumberArray(this.lastCount)[i] === undefined);
return createElement('span', {
className: `${prefixCls}-only`,
style: {
transition: removeTransition ? 'none' : undefined,
msTransform: `translateY(${-position * 100}%)`,
WebkitTransform: `translateY(${-position * 100}%)`,
transform: `translateY(${-position * 100}%)`,
const removeTransition =
this.state.animateStarted || getNumberArray(this.lastCount)[i] === undefined;
return createElement(
'span',
{
className: `${prefixCls}-only`,
style: {
transition: removeTransition ? 'none' : undefined,
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) {
@ -122,13 +137,18 @@ export default class ScrollNumber extends Component<ScrollNumberProps, ScrollNum
return 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) => {
const {
prefixCls: customizePrefixCls,
className, style, title, component = 'sup', displayComponent,
className,
style,
title,
component = 'sup',
displayComponent,
} = this.props;
// fix https://fb.me/react-unknown-prop
const restProps = omit(this.props, [
@ -156,18 +176,10 @@ export default class ScrollNumber extends Component<ScrollNumberProps, ScrollNum
className: `${prefixCls}-custom-component`,
});
}
return createElement(
component as any,
newProps,
this.renderNumberElement(prefixCls),
);
}
return createElement(component as any, newProps, this.renderNumberElement(prefixCls));
};
render() {
return (
<ConfigConsumer>
{this.renderScrollNumber}
</ConfigConsumer>
);
return <ConfigConsumer>{this.renderScrollNumber}</ConfigConsumer>;
}
}

View File

@ -24,7 +24,12 @@ describe('Badge', () => {
it('should have an overriden title attribute', () => {
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
@ -32,7 +37,7 @@ describe('Badge', () => {
const wrapper = mount(
<Tooltip title="Fix the error">
<Badge status="error" />
</Tooltip>
</Tooltip>,
);
wrapper.find('Badge').simulate('mouseenter');
jest.runAllTimers();
@ -59,7 +64,12 @@ describe('Badge', () => {
});
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();
});
});

View File

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

View File

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

View File

@ -16,7 +16,12 @@ export interface BreadcrumbProps {
routes?: Route[];
params?: any;
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;
className?: string;
}
@ -36,9 +41,7 @@ function getBreadcrumbName(route: Route, params: any) {
function defaultItemRender(route: Route, params: any, routes: Route[], paths: string[]) {
const isLastItem = routes.indexOf(route) === routes.length - 1;
const name = getBreadcrumbName(route, params);
return isLastItem
? <span>{name}</span>
: <a href={`#/${paths.join('/')}`}>{name}</a>;
return isLastItem ? <span>{name}</span> : <a href={`#/${paths.join('/')}`}>{name}</a>;
}
export default class Breadcrumb extends React.Component<BreadcrumbProps, any> {
@ -62,7 +65,7 @@ export default class Breadcrumb extends React.Component<BreadcrumbProps, any> {
warning(
!('linkRender' in props || 'nameRender' in props),
'`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;
const {
prefixCls: customizePrefixCls,
separator, style, className, routes, params = {},
children, itemRender = defaultItemRender,
separator,
style,
className,
routes,
params = {},
children,
itemRender = defaultItemRender,
} = this.props;
const prefixCls = getPrefixCls('breadcrumb', customizePrefixCls);
if (routes && routes.length > 0) {
const paths: string[] = [];
crumbs = routes.map((route) => {
crumbs = routes.map(route => {
route.path = route.path || '';
let path: string = route.path.replace(/^\//, '');
Object.keys(params).forEach(key => {
@ -98,7 +106,7 @@ export default class Breadcrumb extends React.Component<BreadcrumbProps, any> {
}
warning(
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, {
separator,
@ -111,13 +119,9 @@ export default class Breadcrumb extends React.Component<BreadcrumbProps, any> {
{crumbs}
</div>
);
}
};
render() {
return (
<ConfigConsumer>
{this.renderBreadcrumb}
</ConfigConsumer>
);
return <ConfigConsumer>{this.renderBreadcrumb}</ConfigConsumer>;
}
}

View File

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

View File

@ -19,11 +19,11 @@ describe('Breadcrumb', () => {
mount(
<Breadcrumb>
<MyCom />
</Breadcrumb>
</Breadcrumb>,
);
expect(errorSpy.mock.calls).toHaveLength(1);
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}
<Breadcrumb.Item>Home</Breadcrumb.Item>
{undefined}
</Breadcrumb>
</Breadcrumb>,
);
expect(errorSpy).not.toHaveBeenCalled();
expect(wrapper).toMatchSnapshot();
@ -47,7 +47,7 @@ describe('Breadcrumb', () => {
<Breadcrumb.Item />
<Breadcrumb.Item>xxx</Breadcrumb.Item>
<Breadcrumb.Item>yyy</Breadcrumb.Item>
</Breadcrumb>
</Breadcrumb>,
);
expect(wrapper).toMatchSnapshot();
});

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@ -1,8 +1,8 @@
@import "../../style/themes/default";
@import "../../style/mixins/index";
@import "./mixin";
@import '../../style/themes/default';
@import '../../style/mixins/index';
@import './mixin';
@btn-prefix-cls: ~"@{ant-prefix}-btn";
@btn-prefix-cls: ~'@{ant-prefix}-btn';
// for compatible
@btn-ghost-color: @text-color;
@ -82,16 +82,16 @@
right: -1px;
background: @component-background;
opacity: 0.35;
content: "";
content: '';
border-radius: inherit;
z-index: 1;
transition: opacity .2s;
transition: opacity 0.2s;
pointer-events: none;
display: none;
}
.@{iconfont-css-prefix} {
transition: margin-left .3s @ease-in-out;
transition: margin-left 0.3s @ease-in-out;
}
&&-loading:before {
@ -150,12 +150,12 @@
}
&-two-chinese-chars:first-letter {
letter-spacing: .34em;
letter-spacing: 0.34em;
}
&-two-chinese-chars > *:not(.@{iconfont-css-prefix}) {
letter-spacing: .34em;
margin-right: -.34em;
letter-spacing: 0.34em;
margin-right: -0.34em;
}
&-block {
@ -163,13 +163,14 @@
}
.christmas&-primary:before {
content: "";
content: '';
display: block;
position: absolute;
top: -6px;
left: 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;
opacity: 1;
}

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@ -17,7 +17,9 @@ describe('Card', () => {
function fakeResizeWindowTo(wrapper, width) {
Object.defineProperties(wrapper.instance().container, {
offsetWidth: {
get() { return width; },
get() {
return width;
},
configurable: true,
},
});
@ -37,7 +39,11 @@ describe('Card', () => {
});
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();
});
@ -45,7 +51,7 @@ describe('Card', () => {
const wrapper = mount(
<Card title="Card title" extra={<Button>Button</Button>} style={{ width: 300 }}>
<p>Card content</p>
</Card>
</Card>,
);
expect(wrapper.render()).toMatchSnapshot();
});

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@ -4,58 +4,79 @@ import KeyCode from 'rc-util/lib/KeyCode';
import Cascader from '..';
import focusTest from '../../../tests/shared/focusTest';
const options = [{
value: 'zhejiang',
label: 'Zhejiang',
children: [{
value: 'hangzhou',
label: 'Hangzhou',
children: [{
value: 'xihu',
label: 'West Lake',
}],
}],
}, {
value: 'jiangsu',
label: 'Jiangsu',
children: [{
value: 'nanjing',
label: 'Nanjing',
children: [{
value: 'zhonghuamen',
label: 'Zhong Hua Men',
}],
}],
}];
const options = [
{
value: 'zhejiang',
label: 'Zhejiang',
children: [
{
value: 'hangzhou',
label: 'Hangzhou',
children: [
{
value: 'xihu',
label: 'West Lake',
},
],
},
],
},
{
value: 'jiangsu',
label: 'Jiangsu',
children: [
{
value: 'nanjing',
label: 'Nanjing',
children: [
{
value: 'zhonghuamen',
label: 'Zhong Hua Men',
},
],
},
],
},
];
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', () => {
focusTest(Cascader);
it('popup correctly when panel is hidden', () => {
const wrapper = mount(
<Cascader options={options} />
);
expect(render(wrapper.find('Trigger').instance().getComponent())).toMatchSnapshot();
const wrapper = mount(<Cascader options={options} />);
expect(
render(
wrapper
.find('Trigger')
.instance()
.getComponent(),
),
).toMatchSnapshot();
});
it('popup correctly when panel is open', () => {
const onPopupVisibleChange = jest.fn();
const wrapper = mount(
<Cascader options={options} onPopupVisibleChange={onPopupVisibleChange} />
<Cascader options={options} onPopupVisibleChange={onPopupVisibleChange} />,
);
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);
});
it('support controlled mode', () => {
const wrapper = mount(
<Cascader options={options} />
);
const wrapper = mount(<Cascader options={options} />);
wrapper.setProps({
value: ['zhejiang', 'hangzhou', 'xihu'],
});
@ -63,38 +84,99 @@ describe('Cascader', () => {
});
it('popup correctly with defaultValue', () => {
const wrapper = mount(
<Cascader options={options} defaultValue={['zhejiang', 'hangzhou']} />
);
const wrapper = mount(<Cascader options={options} defaultValue={['zhejiang', 'hangzhou']} />);
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', () => {
const wrapper = mount(
<Cascader options={options} defaultValue={['zhejiang', 'hangzhou']} />
);
expect(wrapper.find('Trigger').instance().getComponent().props.visible).toBe(false);
const wrapper = mount(<Cascader options={options} defaultValue={['zhejiang', 'hangzhou']} />);
expect(
wrapper
.find('Trigger')
.instance()
.getComponent().props.visible,
).toBe(false);
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', () => {
const onChange = jest.fn();
const wrapper = mount(<Cascader options={options} onChange={onChange} />);
wrapper.find('input').simulate('click');
let popupWrapper = mount(wrapper.find('Trigger').instance().getComponent());
popupWrapper.find('.ant-cascader-menu').at(0).find('.ant-cascader-menu-item').at(0)
let popupWrapper = mount(
wrapper
.find('Trigger')
.instance()
.getComponent(),
);
popupWrapper
.find('.ant-cascader-menu')
.at(0)
.find('.ant-cascader-menu-item')
.at(0)
.simulate('click');
expect(render(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)
expect(
render(
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');
expect(render(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)
expect(
render(
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');
expect(render(wrapper.find('Trigger').instance().getComponent())).toMatchSnapshot();
expect(
render(
wrapper
.find('Trigger')
.instance()
.getComponent(),
),
).toMatchSnapshot();
expect(onChange).toHaveBeenCalledWith(['zhejiang', 'hangzhou', 'xihu'], expect.anything());
});
@ -112,7 +194,12 @@ describe('Cascader', () => {
wrapper.find('input').simulate('click');
wrapper.find('input').simulate('change', { target: { value: '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();
});
@ -121,14 +208,22 @@ describe('Cascader', () => {
wrapper.find('input').simulate('click');
wrapper.find('input').simulate('change', { target: { value: '__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();
});
it('should support to clear selection', () => {
const wrapper = mount(<Cascader options={options} defaultValue={['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('');
});
@ -140,35 +235,33 @@ describe('Cascader', () => {
popupVisible
defaultValue={['zhejiang', 'hangzhou']}
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);
});
it('should clear search input when clear selection', () => {
const wrapper = mount(
<Cascader
options={options}
defaultValue={['zhejiang', 'hangzhou']}
showSearch
/>
<Cascader options={options} defaultValue={['zhejiang', 'hangzhou']} showSearch />,
);
wrapper.find('input').simulate('click');
wrapper.find('input').simulate('change', { target: { value: '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('');
});
it('should not trigger visible change when click search input', () => {
const onPopupVisibleChange = jest.fn();
const wrapper = mount(
<Cascader
options={options}
showSearch
onPopupVisibleChange={onPopupVisibleChange}
/>
<Cascader options={options} showSearch onPopupVisibleChange={onPopupVisibleChange} />,
);
wrapper.find('input').simulate('focus');
expect(onPopupVisibleChange).toHaveBeenCalledTimes(0);
@ -192,29 +285,40 @@ describe('Cascader', () => {
});
it('can use fieldNames', () => {
const customerOptions = [{
code: 'zhejiang',
name: 'Zhejiang',
items: [{
code: 'hangzhou',
name: 'Hangzhou',
items: [{
code: 'xihu',
name: 'West Lake',
}],
}],
}, {
code: 'jiangsu',
name: 'Jiangsu',
items: [{
code: 'nanjing',
name: 'Nanjing',
items: [{
code: 'zhonghuamen',
name: 'Zhong Hua Men',
}],
}],
}];
const customerOptions = [
{
code: 'zhejiang',
name: 'Zhejiang',
items: [
{
code: 'hangzhou',
name: 'Hangzhou',
items: [
{
code: 'xihu',
name: 'West Lake',
},
],
},
],
},
{
code: 'jiangsu',
name: 'Jiangsu',
items: [
{
code: 'nanjing',
name: 'Nanjing',
items: [
{
code: 'zhonghuamen',
name: 'Zhong Hua Men',
},
],
},
],
},
];
const wrapper = mount(
<Cascader
options={customerOptions}
@ -223,38 +327,54 @@ describe('Cascader', () => {
label: 'name',
value: 'code',
}}
/>
/>,
);
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
it('can use filedNames too, for compatibility', () => {
const errorSpy = jest.spyOn(console, 'error').mockImplementation(() => {});
const customerOptions = [{
code: 'zhejiang',
name: 'Zhejiang',
items: [{
code: 'hangzhou',
name: 'Hangzhou',
items: [{
code: 'xihu',
name: 'West Lake',
}],
}],
}, {
code: 'jiangsu',
name: 'Jiangsu',
items: [{
code: 'nanjing',
name: 'Nanjing',
items: [{
code: 'zhonghuamen',
name: 'Zhong Hua Men',
}],
}],
}];
const customerOptions = [
{
code: 'zhejiang',
name: 'Zhejiang',
items: [
{
code: 'hangzhou',
name: 'Hangzhou',
items: [
{
code: 'xihu',
name: 'West Lake',
},
],
},
],
},
{
code: 'jiangsu',
name: 'Jiangsu',
items: [
{
code: 'nanjing',
name: 'Nanjing',
items: [
{
code: 'zhonghuamen',
name: 'Zhong Hua Men',
},
],
},
],
},
];
const wrapper = mount(
<Cascader
options={customerOptions}
@ -263,12 +383,17 @@ describe('Cascader', () => {
label: 'name',
value: 'code',
}}
/>
/>,
);
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(
'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();
});
@ -300,7 +425,7 @@ describe('Cascader', () => {
wrapper.find('input').simulate('change', { target: { value: 'a' } });
expect(wrapper.find('.ant-cascader-menu-item').length).toBe(2);
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,
names: FilledFieldNamesType,
) => React.ReactNode;
sort?: (a: CascaderOptionType[], b: CascaderOptionType[], inputValue: string, names: FilledFieldNamesType) => number;
sort?: (
a: CascaderOptionType[],
b: CascaderOptionType[],
inputValue: string,
names: FilledFieldNamesType,
) => number;
matchInputWidth?: boolean;
limit?: number | false;
}
@ -104,14 +109,23 @@ export interface CascaderState {
const defaultLimit = 50;
function highlightKeyword(str: string, keyword: string, prefixCls: string | undefined) {
return str.split(keyword)
.map((node: string, index: number) => index === 0 ? node : [
<span className={`${prefixCls}-menu-item-keyword`} key="seperator">{keyword}</span>,
node,
]);
return str.split(keyword).map((node: string, index: number) =>
index === 0
? 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);
}
@ -123,14 +137,19 @@ function defaultRenderFilteredOption(
) {
return path.map((option, index) => {
const label = option[names.label];
const node = (label as string).indexOf(inputValue) > -1 ?
highlightKeyword(label as string, inputValue, prefixCls) : label;
const node =
(label as string).indexOf(inputValue) > -1
? highlightKeyword(label as string, inputValue, prefixCls)
: label;
return index === 0 ? node : [' / ', node];
});
}
function defaultSortFilteredOption(
a: CascaderOptionType[], b: CascaderOptionType[], inputValue: string, names: FilledFieldNamesType,
a: CascaderOptionType[],
b: CascaderOptionType[],
inputValue: string,
names: FilledFieldNamesType,
) {
function callback(elem: CascaderOptionType) {
return (elem[names.label] as string).indexOf(inputValue) > -1;
@ -181,8 +200,7 @@ export default class Cascader extends React.Component<CascaderProps, CascaderSta
inputValue: '',
inputFocused: false,
popupVisible: props.popupVisible,
flattenOptions:
props.showSearch ? this.flattenTree(props.options, props) : undefined,
flattenOptions: props.showSearch ? this.flattenTree(props.options, props) : undefined,
};
}
@ -209,7 +227,7 @@ export default class Cascader extends React.Component<CascaderProps, CascaderSta
return;
}
this.setValue(value, selectedOptions);
}
};
handlePopupVisibleChange = (popupVisible: boolean) => {
if (!('popupVisible' in this.props)) {
@ -224,13 +242,13 @@ export default class Cascader extends React.Component<CascaderProps, CascaderSta
if (onPopupVisibleChange) {
onPopupVisibleChange(popupVisible);
}
}
};
handleInputBlur = () => {
this.setState({
inputFocused: false,
});
}
};
handleInputClick = (e: React.MouseEvent<HTMLInputElement>) => {
const { inputFocused, popupVisible } = this.state;
@ -241,18 +259,18 @@ export default class Cascader extends React.Component<CascaderProps, CascaderSta
e.nativeEvent.stopImmediatePropagation();
}
}
}
};
handleKeyDown = (e: React.KeyboardEvent<HTMLInputElement>) => {
if (e.keyCode === KeyCode.BACKSPACE) {
e.stopPropagation();
}
}
};
handleInputChange = (e: React.ChangeEvent<HTMLInputElement>) => {
const inputValue = e.target.value;
this.setState({ inputValue });
}
};
setValue = (value: string[], selectedOptions: CascaderOptionType[] = []) => {
if (!('value' in this.props)) {
@ -262,14 +280,15 @@ export default class Cascader extends React.Component<CascaderProps, CascaderSta
if (onChange) {
onChange(value, selectedOptions);
}
}
};
getLabel() {
const { options, displayRender = defaultDisplayRender as Function } = this.props;
const names = getFilledFieldNames(this.props);
const value = this.state.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],
{ childrenKeyName: names.children },
);
@ -286,7 +305,7 @@ export default class Cascader extends React.Component<CascaderProps, CascaderSta
} else {
this.setState({ inputValue: '' });
}
}
};
flattenTree(
options: CascaderOptionType[],
@ -296,19 +315,13 @@ export default class Cascader extends React.Component<CascaderProps, CascaderSta
const names: FilledFieldNamesType = getFilledFieldNames(props);
let flattenOptions = [] as CascaderOptionType[][];
const childrenName = names.children;
options.forEach((option) => {
options.forEach(option => {
const path = ancestor.concat(option);
if (props.changeOnSelect || !option[childrenName] || !option[childrenName].length) {
flattenOptions.push(path);
}
if (option[childrenName]) {
flattenOptions = flattenOptions.concat(
this.flattenTree(
option[childrenName],
props,
path,
),
);
flattenOptions = flattenOptions.concat(this.flattenTree(option[childrenName], props, path));
}
});
return flattenOptions;
@ -332,7 +345,7 @@ export default class Cascader extends React.Component<CascaderProps, CascaderSta
let matchCount = 0;
// Perf optimization to filter items only below the limit
flattenOptions.some((path) => {
flattenOptions.some(path => {
const match = filter(this.state.inputValue, path, names);
if (match) {
filtered.push(path);
@ -343,9 +356,9 @@ export default class Cascader extends React.Component<CascaderProps, CascaderSta
} else {
warning(
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));
@ -361,7 +374,9 @@ export default class Cascader extends React.Component<CascaderProps, CascaderSta
} 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() {
@ -374,14 +389,26 @@ export default class Cascader extends React.Component<CascaderProps, CascaderSta
saveInput = (node: Input) => {
this.input = node;
}
};
renderCascader = ({ getPopupContainer: getContextPopupContainer, getPrefixCls }: ConfigConsumerProps) => {
renderCascader = ({
getPopupContainer: getContextPopupContainer,
getPrefixCls,
}: ConfigConsumerProps) => {
const { props, state } = this;
const {
prefixCls: customizePrefixCls, inputPrefixCls: customizeInputPrefixCls,
children, placeholder, size, disabled,
className, style, allowClear, showSearch = false, suffixIcon, ...otherProps
prefixCls: customizePrefixCls,
inputPrefixCls: customizeInputPrefixCls,
children,
placeholder,
size,
disabled,
className,
style,
allowClear,
showSearch = false,
suffixIcon,
...otherProps
} = props;
const { value, inputFocused } = state;
@ -392,26 +419,26 @@ export default class Cascader extends React.Component<CascaderProps, CascaderSta
[`${inputPrefixCls}-lg`]: size === 'large',
[`${inputPrefixCls}-sm`]: size === 'small',
});
const clearIcon = (allowClear && !disabled && value.length > 0) || state.inputValue ? (
<Icon
type="close-circle"
theme="filled"
className={`${prefixCls}-picker-clear`}
onClick={this.clearSelection}
/>
) : null;
const clearIcon =
(allowClear && !disabled && value.length > 0) || state.inputValue ? (
<Icon
type="close-circle"
theme="filled"
className={`${prefixCls}-picker-clear`}
onClick={this.clearSelection}
/>
) : null;
const arrowCls = classNames({
[`${prefixCls}-picker-arrow`]: true,
[`${prefixCls}-picker-arrow-expand`]: state.popupVisible,
});
const pickerCls = classNames(
className, `${prefixCls}-picker`, {
[`${prefixCls}-picker-with-value`]: state.inputValue,
[`${prefixCls}-picker-disabled`]: disabled,
[`${prefixCls}-picker-${size}`]: !!size,
[`${prefixCls}-picker-show-search`]: !!showSearch,
[`${prefixCls}-picker-focused`]: inputFocused,
});
const pickerCls = classNames(className, `${prefixCls}-picker`, {
[`${prefixCls}-picker-with-value`]: state.inputValue,
[`${prefixCls}-picker-disabled`]: disabled,
[`${prefixCls}-picker-${size}`]: !!size,
[`${prefixCls}-picker-show-search`]: !!showSearch,
[`${prefixCls}-picker-focused`]: inputFocused,
});
// Fix bug of https://github.com/facebook/react/pull/5004
// and https://fb.me/react-unknown-prop
@ -447,39 +474,34 @@ export default class Cascader extends React.Component<CascaderProps, CascaderSta
this.cachedOptions = options;
}
const dropdownMenuColumnStyle: { width?: number, height?: string } = {};
const isNotFound = (options || []).length === 1 && options[0].value === 'ANT_CASCADER_NOT_FOUND';
const dropdownMenuColumnStyle: { width?: number; height?: string } = {};
const isNotFound =
(options || []).length === 1 && options[0].value === 'ANT_CASCADER_NOT_FOUND';
if (isNotFound) {
dropdownMenuColumnStyle.height = 'auto'; // Height of one row.
}
// 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) {
dropdownMenuColumnStyle.width = this.input.input.offsetWidth;
}
const inputIcon = suffixIcon && (
React.isValidElement<{ className?: string }>(suffixIcon)
? React.cloneElement(
suffixIcon,
{
className: classNames({
[suffixIcon.props.className!]: suffixIcon.props.className,
[`${prefixCls}-picker-arrow`]: true,
}),
},
) : <span className={`${prefixCls}-picker-arrow`}>{suffixIcon}</span>) || (
<Icon type="down" className={arrowCls} />
);
const inputIcon = (suffixIcon &&
(React.isValidElement<{ className?: string }>(suffixIcon) ? (
React.cloneElement(suffixIcon, {
className: classNames({
[suffixIcon.props.className!]: suffixIcon.props.className,
[`${prefixCls}-picker-arrow`]: true,
}),
})
) : (
<span className={`${prefixCls}-picker-arrow`}>{suffixIcon}</span>
))) || <Icon type="down" className={arrowCls} />;
const input = children || (
<span
style={style}
className={pickerCls}
>
<span className={`${prefixCls}-picker-label`}>
{this.getLabel()}
</span>
<span style={style} className={pickerCls}>
<span className={`${prefixCls}-picker-label`}>{this.getLabel()}</span>
<Input
{...inputProps}
ref={this.saveInput}
@ -500,9 +522,7 @@ export default class Cascader extends React.Component<CascaderProps, CascaderSta
</span>
);
const expandIcon = (
<Icon type="right" />
);
const expandIcon = <Icon type="right" />;
const loadingIcon = (
<span className={`${prefixCls}-menu-item-loading-icon`}>
@ -530,13 +550,9 @@ export default class Cascader extends React.Component<CascaderProps, CascaderSta
{input}
</RcCascader>
);
}
};
render() {
return (
<ConfigConsumer>
{this.renderCascader}
</ConfigConsumer>
);
return <ConfigConsumer>{this.renderCascader}</ConfigConsumer>;
}
}

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@ -3,10 +3,7 @@ import { mount } from 'enzyme';
import moment from 'moment';
import MockDate from 'mockdate';
import DatePicker from '..';
import {
selectDate,
openPanel,
} from './utils';
import { selectDate, openPanel } from './utils';
const { MonthPicker, WeekPicker, RangePicker } = DatePicker;
@ -20,18 +17,14 @@ describe('DatePicker', () => {
});
it('should focus trigger input after select date in DatePicker', () => {
const wrapper = mount(
<DatePicker />
);
const wrapper = mount(<DatePicker />);
openPanel(wrapper);
selectDate(wrapper, moment('2016-11-23'));
expect(wrapper.find('.ant-calendar-picker-input').getDOMNode()).toBe(document.activeElement);
});
it('should focus trigger input after select date in RangePicker', () => {
const wrapper = mount(
<RangePicker />
);
const wrapper = mount(<RangePicker />);
openPanel(wrapper);
selectDate(wrapper, moment('2016-11-23'), 0);
selectDate(wrapper, moment('2016-11-28'), 1);
@ -39,19 +32,21 @@ describe('DatePicker', () => {
});
it('should focus trigger input after select date in MonthPicker', () => {
const wrapper = mount(
<MonthPicker />
);
const wrapper = mount(<MonthPicker />);
openPanel(wrapper);
wrapper.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');
wrapper
.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);
});
it('should focus trigger input after select date in WeekPicker', () => {
const wrapper = mount(
<WeekPicker />
);
const wrapper = mount(<WeekPicker />);
openPanel(wrapper);
selectDate(wrapper, moment('2016-11-23'));
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', () => {
it('render MonthPicker', () => {
const birthday = moment('2000-01-01', 'YYYY-MM-DD').locale('zh-cn');
const wrapper = mount(
<MonthPicker open />
);
const wrapper = mount(<MonthPicker open />);
wrapper.setProps({ value: birthday });
expect(render(wrapper.find('Trigger').instance().getComponent())).toMatchSnapshot();
expect(
render(
wrapper
.find('Trigger')
.instance()
.getComponent(),
),
).toMatchSnapshot();
});
it('render WeekPicker', () => {
const birthday = moment('2000-01-01', 'YYYY-MM-DD').locale('zh-cn');
const wrapper = mount(
<WeekPicker open />
);
const wrapper = mount(<WeekPicker open />);
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 onOpenChangeFn = jest.fn();
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());
calendarWrapper.find('.ant-calendar-date').at(0).simulate('click');
const calendarWrapper = mount(
wrapper
.find('Trigger')
.instance()
.getComponent(),
);
calendarWrapper
.find('.ant-calendar-date')
.at(0)
.simulate('click');
expect(onChangeFn).toHaveBeenCalled();
expect(onOpenChangeFn).not.toHaveBeenCalled();
});
@ -24,10 +32,21 @@ describe('DatePicker with showTime', () => {
const onChangeFn = jest.fn();
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');
expect(onOkFn).toHaveBeenCalled();
expect(onOpenChangeFn).toHaveBeenCalledWith(false);
@ -39,22 +58,33 @@ describe('DatePicker with showTime', () => {
const onChangeFn = jest.fn();
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');
expect(onOpenChangeFn).toHaveBeenCalledWith(false);
expect(onChangeFn).toHaveBeenCalled();
});
it('should have correct className when use12Hours is true', () => {
const wrapper = mount(
<DatePicker showTime={{ use12Hours: true }} open />
const wrapper = mount(<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);
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);
});
});
@ -64,16 +94,39 @@ describe('RangePicker with showTime', () => {
const onChangeFn = jest.fn();
const onOpenChangeFn = jest.fn();
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());
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);
const calendarWrapper = mount(
wrapper
.find('Trigger')
.instance()
.getComponent(),
);
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(onOpenChangeFn).not.toHaveBeenCalled();
});
@ -89,12 +142,23 @@ describe('RangePicker with showTime', () => {
onOk={onOkFn}
onChange={onChangeFn}
onOpenChange={onOpenChangeFn}
/>
/>,
);
const calendarWrapper = mount(wrapper.find('Trigger').instance().getComponent());
calendarWrapper.find('.ant-calendar-date').at(10).simulate('click');
calendarWrapper.find('.ant-calendar-date').at(11).simulate('click');
const calendarWrapper = mount(
wrapper
.find('Trigger')
.instance()
.getComponent(),
);
calendarWrapper
.find('.ant-calendar-date')
.at(10)
.simulate('click');
calendarWrapper
.find('.ant-calendar-date')
.at(11)
.simulate('click');
onChangeFn.mockClear();
calendarWrapper.find('.ant-calendar-ok-btn').simulate('click');
expect(onOkFn).toHaveBeenCalled();

View File

@ -8,7 +8,9 @@ export function selectDate(wrapper, date, index) {
}
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) {
@ -16,7 +18,10 @@ export function openPanel(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) {

View File

@ -60,7 +60,7 @@ export default function createPicker(TheCalendar: React.ComponentClass): any {
if (value && !interopDefault(moment).isMoment(value)) {
throw new Error(
'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 = {
@ -74,17 +74,15 @@ export default function createPicker(TheCalendar: React.ComponentClass): any {
const { renderExtraFooter } = this.props;
const { prefixCls } = this;
return renderExtraFooter ? (
<div className={`${prefixCls}-footer-extra`}>
{renderExtraFooter(...args)}
</div>
<div className={`${prefixCls}-footer-extra`}>{renderExtraFooter(...args)}</div>
) : null;
}
};
clearSelection = (e: React.MouseEvent<HTMLElement>) => {
e.preventDefault();
e.stopPropagation();
this.handleChange(null);
}
};
handleChange = (value: moment.Moment | null) => {
const props = this.props;
@ -95,11 +93,11 @@ export default function createPicker(TheCalendar: React.ComponentClass): any {
});
}
props.onChange(value, (value && value.format(props.format)) || '');
}
};
handleCalendarChange = (value: moment.Moment) => {
this.setState({ showDate: value });
}
};
handleOpenChange = (open: boolean) => {
const { onOpenChange } = this.props;
@ -126,7 +124,7 @@ export default function createPicker(TheCalendar: React.ComponentClass): any {
saveInput = (node: any) => {
this.input = node;
}
};
renderPicker = ({ getPrefixCls }: ConfigConsumerProps) => {
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
this.prefixCls = prefixCls;
const placeholder = ('placeholder' in props)
? props.placeholder : locale.lang.placeholder;
const placeholder = 'placeholder' in props ? props.placeholder : locale.lang.placeholder;
const disabledTime = props.showTime ? props.disabledTime : null;
@ -171,7 +168,10 @@ export default function createPicker(TheCalendar: React.ComponentClass): any {
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 = (
<TheCalendar
{...calendarProps}
@ -195,28 +195,27 @@ export default function createPicker(TheCalendar: React.ComponentClass): any {
/>
);
const clearIcon = (!props.disabled && props.allowClear && value) ? (
<Icon
type="close-circle"
className={`${prefixCls}-picker-clear`}
onClick={this.clearSelection}
theme="filled"
/>
) : null;
const clearIcon =
!props.disabled && props.allowClear && value ? (
<Icon
type="close-circle"
className={`${prefixCls}-picker-clear`}
onClick={this.clearSelection}
theme="filled"
/>
) : null;
const inputIcon = suffixIcon && (
React.isValidElement<{ className?: string }>(suffixIcon)
? React.cloneElement(
suffixIcon,
{
className: classNames({
[suffixIcon.props.className!]: suffixIcon.props.className,
[`${prefixCls}-picker-icon`]: true,
}),
},
) : <span className={`${prefixCls}-picker-icon`}>{suffixIcon}</span>) || (
<Icon type="calendar" className={`${prefixCls}-picker-icon`} />
);
const inputIcon = (suffixIcon &&
(React.isValidElement<{ className?: string }>(suffixIcon) ? (
React.cloneElement(suffixIcon, {
className: classNames({
[suffixIcon.props.className!]: suffixIcon.props.className,
[`${prefixCls}-picker-icon`]: true,
}),
})
) : (
<span className={`${prefixCls}-picker-icon`}>{suffixIcon}</span>
))) || <Icon type="calendar" className={`${prefixCls}-picker-icon`} />;
const dataOrAriaProps = getDataOrAriaProps(props);
const input = ({ value: inputValue }: { value: moment.Moment | null }) => (
@ -260,14 +259,10 @@ export default function createPicker(TheCalendar: React.ComponentClass): any {
</RcDatePicker>
</span>
);
}
};
render() {
return (
<ConfigConsumer>
{this.renderPicker}
</ConfigConsumer>
);
return <ConfigConsumer>{this.renderPicker}</ConfigConsumer>;
}
}
polyfill(CalenderWrapper);

View File

@ -7,7 +7,9 @@ import RangePicker from './RangePicker';
import WeekPicker from './WeekPicker';
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');

View File

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

View File

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

View File

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

View File

@ -38,7 +38,7 @@
position: absolute;
top: 0;
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;
font-size: 16px;
display: inline-block;
@ -214,7 +214,9 @@
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 {
background: @primary-color;
color: @text-color-inverse;
@ -243,7 +245,7 @@
padding-right: 5px;
padding-left: 5px;
&:before {
content: " ";
content: ' ';
position: absolute;
top: -1px;
left: 5px;

View File

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

View File

@ -9,7 +9,8 @@
background: @component-background;
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%;
}
}
@ -19,11 +20,11 @@
}
.@{calendar-prefix-cls}-month-panel-header {
.calendarPanelHeader(~"@{calendar-prefix-cls}-month-panel");
.calendarPanelHeader(~'@{calendar-prefix-cls}-month-panel');
}
.@{calendar-prefix-cls}-month-panel-body {
height: ~"calc(100% - 40px)";
height: ~'calc(100% - 40px)';
}
.@{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 {
.reset-component;
@ -65,7 +65,7 @@
margin-top: -7px;
line-height: 14px;
font-size: @font-size-sm;
transition: all .3s;
transition: all 0.3s;
user-select: none;
z-index: 1;
}
@ -89,7 +89,7 @@
}
&-icon {
font-family: "anticon";
font-family: 'anticon';
font-size: @font-size-base;
color: @disabled-color;
display: inline-block;

View File

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

View File

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

View File

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

View File

@ -9,7 +9,8 @@
background: @component-background;
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%;
}
}
@ -19,11 +20,11 @@
}
.@{calendar-prefix-cls}-year-panel-header {
.calendarPanelHeader(~"@{calendar-prefix-cls}-year-panel");
.calendarPanelHeader(~'@{calendar-prefix-cls}-year-panel');
}
.@{calendar-prefix-cls}-year-panel-body {
height: ~"calc(100% - 40px)";
height: ~'calc(100% - 40px)';
}
.@{calendar-prefix-cls}-year-panel-table {

View File

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

View File

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

View File

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

View File

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

View File

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

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