mirror of
https://gitee.com/ant-design/ant-design.git
synced 2024-12-01 19:49:59 +08:00
support prettier
This commit is contained in:
parent
16c3a9453e
commit
dca9715186
27
.eslintrc.js
27
.eslintrc.js
@ -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
7
.prettierignore
Normal file
@ -0,0 +1,7 @@
|
||||
**/*.md
|
||||
**/*.svg
|
||||
**/*.ejs
|
||||
**/*.html
|
||||
package.json
|
||||
.umi
|
||||
.umi-production
|
19
.prettierrc
Normal file
19
.prettierrc
Normal file
@ -0,0 +1,19 @@
|
||||
{
|
||||
"singleQuote": true,
|
||||
"trailingComma": "all",
|
||||
"printWidth": 100,
|
||||
"overrides": [
|
||||
{
|
||||
"files": ".prettierrc",
|
||||
"options": {
|
||||
"parser": "json"
|
||||
}
|
||||
},
|
||||
{
|
||||
"files": ".stylelintrc",
|
||||
"options": {
|
||||
"parser": "json"
|
||||
}
|
||||
}
|
||||
]
|
||||
}
|
@ -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,
|
||||
|
@ -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();
|
||||
});
|
||||
|
@ -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');
|
||||
});
|
||||
|
@ -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;
|
||||
}
|
||||
|
@ -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];
|
||||
}
|
||||
};
|
||||
|
@ -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);
|
||||
|
@ -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) {
|
||||
|
@ -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);
|
||||
|
@ -9,9 +9,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) {
|
||||
@ -26,10 +26,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,
|
||||
};
|
||||
@ -101,8 +99,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);
|
||||
}
|
||||
});
|
||||
@ -181,10 +178,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',
|
||||
@ -198,7 +195,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);
|
||||
@ -264,18 +266,24 @@ export default class Affix extends React.Component<AffixProps, AffixState> {
|
||||
|
||||
saveFixedNode = (node: HTMLDivElement) => {
|
||||
this.fixedNode = node;
|
||||
}
|
||||
};
|
||||
|
||||
savePlaceholderNode = (node: HTMLDivElement) => {
|
||||
this.placeholderNode = node;
|
||||
}
|
||||
};
|
||||
|
||||
render() {
|
||||
const className = classNames({
|
||||
[this.props.prefixCls || 'ant-affix']: 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}>
|
||||
|
@ -1,4 +1,4 @@
|
||||
@import "../../style/themes/default";
|
||||
@import '../../style/themes/default';
|
||||
|
||||
.@{ant-prefix}-affix {
|
||||
position: fixed;
|
||||
|
@ -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');
|
||||
});
|
||||
|
@ -5,7 +5,7 @@ import Icon, { ThemeType } from '../icon';
|
||||
import classNames from 'classnames';
|
||||
import getDataOrAriaProps from '../_util/getDataOrAriaProps';
|
||||
|
||||
function noop() { }
|
||||
function noop() {}
|
||||
|
||||
export interface AlertProps {
|
||||
/**
|
||||
@ -35,8 +35,8 @@ export interface AlertProps {
|
||||
}
|
||||
|
||||
export interface AlertState {
|
||||
closing: boolean,
|
||||
closed: boolean
|
||||
closing: boolean;
|
||||
closed: boolean;
|
||||
}
|
||||
|
||||
export default class Alert extends React.Component<AlertProps, AlertState> {
|
||||
@ -57,7 +57,7 @@ export default class Alert extends React.Component<AlertProps, AlertState> {
|
||||
closing: false,
|
||||
});
|
||||
(this.props.onClose || noop)(e);
|
||||
}
|
||||
};
|
||||
|
||||
animationEnd = () => {
|
||||
this.setState({
|
||||
@ -65,12 +65,18 @@ export default class Alert extends React.Component<AlertProps, AlertState> {
|
||||
closing: true,
|
||||
});
|
||||
(this.props.afterClose || noop)();
|
||||
}
|
||||
};
|
||||
|
||||
render() {
|
||||
const {
|
||||
description, prefixCls = 'ant-alert', message, closeText, banner,
|
||||
className = '', style, icon,
|
||||
description,
|
||||
prefixCls = 'ant-alert',
|
||||
message,
|
||||
closeText,
|
||||
banner,
|
||||
className = '',
|
||||
style,
|
||||
icon,
|
||||
} = this.props;
|
||||
let { closable, type, showIcon, iconType } = this.props;
|
||||
|
||||
@ -111,13 +117,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`}>
|
||||
@ -127,19 +138,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
|
||||
|
@ -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: #404040;
|
||||
}
|
||||
@ -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;
|
||||
}
|
||||
|
||||
|
@ -38,17 +38,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;
|
||||
@ -79,7 +86,7 @@ type Section = {
|
||||
top: number;
|
||||
};
|
||||
|
||||
export type AnchorContainer = HTMLElement | Window;
|
||||
export type AnchorContainer = HTMLElement | Window;
|
||||
|
||||
export interface AnchorProps {
|
||||
prefixCls?: string;
|
||||
@ -91,7 +98,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 {
|
||||
@ -110,7 +120,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> {
|
||||
@ -181,7 +194,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;
|
||||
@ -190,7 +203,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 = '';
|
||||
@ -203,7 +216,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);
|
||||
@ -217,7 +232,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 '';
|
||||
@ -233,11 +248,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;
|
||||
}
|
||||
};
|
||||
|
||||
render() {
|
||||
const {
|
||||
@ -259,7 +274,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 = {
|
||||
@ -268,12 +283,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}
|
||||
@ -281,7 +293,9 @@ export default class Anchor extends React.Component<AnchorProps, AnchorState> {
|
||||
</div>
|
||||
);
|
||||
|
||||
return !affix ? anchorContent : (
|
||||
return !affix ? (
|
||||
anchorContent
|
||||
) : (
|
||||
<Affix offsetTop={offsetTop} target={getContainer}>
|
||||
{anchorContent}
|
||||
</Affix>
|
||||
|
@ -47,15 +47,10 @@ export default class AnchorLink extends React.Component<AnchorLinkProps, any> {
|
||||
onClick(e, { title, href });
|
||||
}
|
||||
scrollTo(href);
|
||||
}
|
||||
};
|
||||
|
||||
render() {
|
||||
const {
|
||||
prefixCls,
|
||||
href,
|
||||
title,
|
||||
children,
|
||||
} = this.props;
|
||||
const { prefixCls, href, title, children } = this.props;
|
||||
const active = this.context.antAnchor.activeLink === href;
|
||||
const wrapperClassName = classNames(`${prefixCls}-link`, {
|
||||
[`${prefixCls}-link-active`]: active,
|
||||
|
@ -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');
|
||||
|
@ -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;
|
||||
|
@ -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,
|
||||
);
|
||||
}
|
||||
}
|
||||
|
@ -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();
|
||||
});
|
||||
|
@ -5,12 +5,15 @@ import Select, { AbstractSelectProps, SelectValue, OptionProps, OptGroupProps }
|
||||
import Input from '../input';
|
||||
import InputElement from './InputElement';
|
||||
|
||||
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>;
|
||||
@ -18,9 +21,9 @@ export interface AutoCompleteInputProps {
|
||||
}
|
||||
|
||||
export type ValidInputElement =
|
||||
HTMLInputElement |
|
||||
HTMLTextAreaElement |
|
||||
React.ReactElement<AutoCompleteInputProps>;
|
||||
| HTMLInputElement
|
||||
| HTMLTextAreaElement
|
||||
| React.ReactElement<AutoCompleteInputProps>;
|
||||
|
||||
export interface AutoCompleteProps extends AbstractSelectProps {
|
||||
value?: SelectValue;
|
||||
@ -33,9 +36,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,11 +85,17 @@ export default class AutoComplete extends React.Component<AutoCompleteProps, {}>
|
||||
|
||||
saveSelect = (node: any) => {
|
||||
this.select = node;
|
||||
}
|
||||
};
|
||||
|
||||
render() {
|
||||
const {
|
||||
size, className = '', notFoundContent, prefixCls, optionLabelProp, dataSource, children,
|
||||
size,
|
||||
className = '',
|
||||
notFoundContent,
|
||||
prefixCls,
|
||||
optionLabelProp,
|
||||
dataSource,
|
||||
children,
|
||||
} = this.props;
|
||||
|
||||
const cls = classNames({
|
||||
@ -96,28 +108,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 (
|
||||
|
@ -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;
|
||||
|
@ -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;
|
||||
|
@ -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,10 @@ export default class Avatar extends React.Component<AvatarProps, AvatarState> {
|
||||
if (errorFlag !== false) {
|
||||
this.setState({ isImgExist: false });
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
render() {
|
||||
const {
|
||||
prefixCls, shape, size, src, srcSet, icon, className, alt, ...others
|
||||
} = this.props;
|
||||
const { prefixCls, shape, size, src, srcSet, icon, className, alt, ...others } = this.props;
|
||||
|
||||
const { isImgExist, scale } = this.state;
|
||||
|
||||
@ -103,23 +103,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 {
|
||||
@ -132,13 +128,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}
|
||||
@ -146,21 +144,14 @@ 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>
|
||||
);
|
||||
|
@ -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;
|
||||
|
@ -10,13 +10,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;
|
||||
@ -52,7 +52,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();
|
||||
@ -69,7 +69,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;
|
||||
@ -88,7 +88,7 @@ export default class BackTop extends React.Component<BackTopProps, any> {
|
||||
this.setState({
|
||||
visible: scrollTop > (visibilityHeight as number),
|
||||
});
|
||||
}
|
||||
};
|
||||
|
||||
componentDidMount() {
|
||||
const getTarget = this.props.target || getDefaultTarget;
|
||||
|
@ -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;
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -4,11 +4,13 @@ import omit from 'omit.js';
|
||||
import classNames from 'classnames';
|
||||
|
||||
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 default class ScrollNumber extends Component<ScrollNumberProps, ScrollNum
|
||||
static defaultProps = {
|
||||
prefixCls: 'ant-scroll-number',
|
||||
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(num: number, i: number) {
|
||||
const position = this.getPositionByNum(num, i);
|
||||
const removeTransition = this.state.animateStarted ||
|
||||
(getNumberArray(this.lastCount)[i] === undefined);
|
||||
return createElement('span', {
|
||||
className: `${this.props.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: `${this.props.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() {
|
||||
@ -122,7 +137,8 @@ export default class ScrollNumber extends Component<ScrollNumberProps, ScrollNum
|
||||
return count;
|
||||
}
|
||||
return getNumberArray(count)
|
||||
.map((num, i) => this.renderCurrentNumber(num, i)).reverse();
|
||||
.map((num, i) => this.renderCurrentNumber(num, i))
|
||||
.reverse();
|
||||
}
|
||||
|
||||
render() {
|
||||
@ -151,10 +167,6 @@ export default class ScrollNumber extends Component<ScrollNumberProps, ScrollNum
|
||||
className: `${prefixCls}-custom-component`,
|
||||
});
|
||||
}
|
||||
return createElement(
|
||||
component as any,
|
||||
newProps,
|
||||
this.renderNumberElement(),
|
||||
);
|
||||
return createElement(component as any, newProps, this.renderNumberElement());
|
||||
}
|
||||
}
|
||||
|
@ -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();
|
||||
});
|
||||
});
|
||||
|
@ -42,12 +42,7 @@ export default class Badge extends React.Component<BadgeProps, any> {
|
||||
};
|
||||
|
||||
getBadgeClassName() {
|
||||
const {
|
||||
prefixCls,
|
||||
className,
|
||||
status,
|
||||
children,
|
||||
} = this.props;
|
||||
const { prefixCls, className, status, children } = this.props;
|
||||
return classNames(className, prefixCls, {
|
||||
[`${prefixCls}-status`]: !!status,
|
||||
[`${prefixCls}-not-a-wrapper`]: !children,
|
||||
@ -76,7 +71,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;
|
||||
}
|
||||
|
||||
@ -94,38 +90,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() {
|
||||
const { prefixCls, 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() {
|
||||
const {
|
||||
count,
|
||||
prefixCls,
|
||||
scrollNumberPrefixCls,
|
||||
status,
|
||||
} = this.props;
|
||||
const { count, prefixCls, scrollNumberPrefixCls, status } = this.props;
|
||||
|
||||
const displayCount = this.getDispayCount();
|
||||
const isDot = this.isDot();
|
||||
@ -134,7 +125,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,
|
||||
});
|
||||
|
||||
|
@ -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;
|
||||
|
@ -15,7 +15,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;
|
||||
}
|
||||
@ -35,9 +40,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,19 +65,25 @@ 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.',
|
||||
);
|
||||
}
|
||||
|
||||
render() {
|
||||
let crumbs;
|
||||
const {
|
||||
separator, prefixCls, style, className, routes, params = {},
|
||||
children, itemRender = defaultItemRender,
|
||||
separator,
|
||||
prefixCls,
|
||||
style,
|
||||
className,
|
||||
routes,
|
||||
params = {},
|
||||
children,
|
||||
itemRender = defaultItemRender,
|
||||
} = this.props;
|
||||
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 => {
|
||||
@ -96,7 +105,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,
|
||||
|
@ -17,10 +17,7 @@ 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,
|
||||
};
|
||||
|
||||
@ -28,9 +25,17 @@ export default class BreadcrumbItem extends React.Component<BreadcrumbItemProps,
|
||||
const { prefixCls, separator, children, ...restProps } = this.props;
|
||||
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 (
|
||||
|
@ -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();
|
||||
});
|
||||
|
@ -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();
|
||||
});
|
||||
});
|
||||
|
@ -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;
|
||||
}
|
||||
|
@ -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();
|
||||
});
|
||||
|
@ -9,7 +9,7 @@ export interface ButtonGroupProps {
|
||||
prefixCls?: string;
|
||||
}
|
||||
|
||||
const ButtonGroup: React.SFC<ButtonGroupProps> = (props) => {
|
||||
const ButtonGroup: React.SFC<ButtonGroupProps> = props => {
|
||||
const { prefixCls = 'ant-btn-group', size, className, ...others } = props;
|
||||
|
||||
// large => lg
|
||||
@ -25,9 +25,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} />;
|
||||
};
|
||||
|
@ -19,10 +19,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)) {
|
||||
@ -55,12 +58,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;
|
||||
@ -168,7 +173,17 @@ export default class Button extends React.Component<ButtonProps, any> {
|
||||
|
||||
render() {
|
||||
const {
|
||||
type, shape, size, className, children, icon, prefixCls, ghost, loading: _loadingProp, block, ...rest
|
||||
type,
|
||||
shape,
|
||||
size,
|
||||
className,
|
||||
children,
|
||||
icon,
|
||||
prefixCls,
|
||||
ghost,
|
||||
loading: _loadingProp,
|
||||
block,
|
||||
...rest
|
||||
} = this.props;
|
||||
|
||||
const { loading, hasTwoCNChar } = this.state;
|
||||
@ -202,10 +217,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) {
|
||||
@ -217,7 +234,8 @@ export default class Button extends React.Component<ButtonProps, any> {
|
||||
title={title}
|
||||
ref={this.saveButtonRef}
|
||||
>
|
||||
{iconNode}{kids}
|
||||
{iconNode}
|
||||
{kids}
|
||||
</a>
|
||||
);
|
||||
}
|
||||
@ -228,14 +246,15 @@ 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>
|
||||
);
|
||||
|
@ -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: #fff;
|
||||
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;
|
||||
}
|
||||
|
@ -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,20 @@
|
||||
.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)`; #fff; ~`colorPalette("@{color}", 5)`);
|
||||
.button-color(~`colorPalette('@{color}', 5) `; #fff; ~`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 +89,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 +109,7 @@
|
||||
> a:only-child {
|
||||
color: currentColor;
|
||||
&:after {
|
||||
content: "";
|
||||
content: '';
|
||||
position: absolute;
|
||||
top: 0;
|
||||
left: 0;
|
||||
@ -161,9 +169,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;
|
||||
|
@ -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> {
|
||||
@ -110,7 +110,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')) {
|
||||
@ -125,7 +125,7 @@ export default class Header extends React.Component<HeaderProps, any> {
|
||||
if (onValueChange) {
|
||||
onValueChange(newValue);
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
onMonthChange = (month: string) => {
|
||||
const newValue = this.props.value.clone();
|
||||
@ -134,24 +134,26 @@ 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;
|
||||
}
|
||||
};
|
||||
|
||||
render() {
|
||||
const { type, value, prefixCls, locale, fullscreen } = this.props;
|
||||
const yearSelect = this.getYearSelectElement(value.year());
|
||||
const monthSelect = type === 'date' ?
|
||||
this.getMonthSelectElement(value.month(), this.getMonthsLocale(value)) : null;
|
||||
const monthSelect =
|
||||
type === 'date'
|
||||
? this.getMonthSelectElement(value.month(), this.getMonthsLocale(value))
|
||||
: null;
|
||||
const size = (fullscreen ? 'default' : 'small') as any;
|
||||
const typeSwitch = (
|
||||
<Group onChange={this.onTypeChange} value={type} size={size}>
|
||||
|
@ -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();
|
||||
});
|
||||
|
@ -10,7 +10,9 @@ import enUS from './locale/en_US';
|
||||
|
||||
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 {
|
||||
@ -80,7 +82,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 = {
|
||||
@ -97,7 +99,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!,
|
||||
});
|
||||
}
|
||||
}
|
||||
@ -106,29 +108,21 @@ export default class Calendar extends React.Component<CalendarProps, CalendarSta
|
||||
const { prefixCls, monthCellRender = noop as Function } = this.props;
|
||||
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 { prefixCls, dateCellRender = noop as Function } = this.props;
|
||||
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)) {
|
||||
@ -141,23 +135,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;
|
||||
@ -171,7 +165,7 @@ export default class Calendar extends React.Component<CalendarProps, CalendarSta
|
||||
|
||||
onSelect = (value: moment.Moment) => {
|
||||
this.setValue(value, 'select');
|
||||
}
|
||||
};
|
||||
|
||||
getDateRange = (
|
||||
validRange: [moment.Moment, moment.Moment],
|
||||
@ -180,13 +174,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;
|
||||
}
|
||||
};
|
||||
|
||||
renderCalendar = (locale: any, localeCode: string) => {
|
||||
const { state, props } = this;
|
||||
@ -194,12 +188,19 @@ export default class Calendar extends React.Component<CalendarProps, CalendarSta
|
||||
if (value && localeCode) {
|
||||
value.locale(localeCode);
|
||||
}
|
||||
const { prefixCls, style, className, fullscreen, dateFullCellRender, monthFullCellRender } = props;
|
||||
const type = (mode === 'year') ? 'month' : 'date';
|
||||
const {
|
||||
prefixCls,
|
||||
style,
|
||||
className,
|
||||
fullscreen,
|
||||
dateFullCellRender,
|
||||
monthFullCellRender,
|
||||
} = props;
|
||||
const type = mode === 'year' ? 'month' : 'date';
|
||||
|
||||
let cls = className || '';
|
||||
if (fullscreen) {
|
||||
cls += (` ${prefixCls}-fullscreen`);
|
||||
cls += ` ${prefixCls}-fullscreen`;
|
||||
}
|
||||
|
||||
const monthCellRender = monthFullCellRender || this.monthCellRender;
|
||||
@ -238,7 +239,7 @@ export default class Calendar extends React.Component<CalendarProps, CalendarSta
|
||||
/>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
};
|
||||
|
||||
getDefaultLocale = () => {
|
||||
const result = {
|
||||
@ -250,14 +251,11 @@ export default class Calendar extends React.Component<CalendarProps, CalendarSta
|
||||
...(this.props.locale || {}).lang,
|
||||
};
|
||||
return result;
|
||||
}
|
||||
};
|
||||
|
||||
render() {
|
||||
return (
|
||||
<LocaleReceiver
|
||||
componentName="Calendar"
|
||||
defaultLocale={this.getDefaultLocale}
|
||||
>
|
||||
<LocaleReceiver componentName="Calendar" defaultLocale={this.getDefaultLocale}>
|
||||
{this.renderCalendar}
|
||||
</LocaleReceiver>
|
||||
);
|
||||
|
@ -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;
|
||||
|
@ -15,13 +15,16 @@ export default (props: CardMetaProps) => {
|
||||
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}
|
||||
|
@ -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();
|
||||
});
|
||||
|
@ -70,7 +70,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.',
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
@ -104,11 +107,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;
|
||||
@ -125,11 +128,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;
|
||||
}
|
||||
|
||||
@ -144,8 +146,24 @@ export default class Card extends React.Component<CardProps, CardState> {
|
||||
|
||||
render() {
|
||||
const {
|
||||
prefixCls = 'ant-card', className, extra, headStyle = {}, bodyStyle = {}, noHovering, hoverable, title, loading,
|
||||
bordered = true, type, cover, actions, tabList, children, activeTabKey, defaultActiveTabKey, ...others
|
||||
prefixCls = 'ant-card',
|
||||
className,
|
||||
extra,
|
||||
headStyle = {},
|
||||
bodyStyle = {},
|
||||
noHovering,
|
||||
hoverable,
|
||||
title,
|
||||
loading,
|
||||
bordered = true,
|
||||
type,
|
||||
cover,
|
||||
actions,
|
||||
tabList,
|
||||
children,
|
||||
activeTabKey,
|
||||
defaultActiveTabKey,
|
||||
...others
|
||||
} = this.props;
|
||||
|
||||
const classString = classNames(prefixCls, className, {
|
||||
@ -159,14 +177,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`} />
|
||||
@ -229,16 +244,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}>
|
||||
@ -256,11 +274,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}
|
||||
|
@ -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: rgba(0, 0, 0, 0.09);
|
||||
@card-radius: @border-radius-sm;
|
||||
@ -11,7 +11,7 @@
|
||||
background: @component-background;
|
||||
border-radius: @card-radius;
|
||||
position: relative;
|
||||
transition: all .3s;
|
||||
transition: all 0.3s;
|
||||
|
||||
&-hoverable {
|
||||
cursor: pointer;
|
||||
@ -88,11 +88,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;
|
||||
@ -143,7 +145,7 @@
|
||||
|
||||
&:hover {
|
||||
color: @primary-color;
|
||||
transition: color .3s;
|
||||
transition: color 0.3s;
|
||||
}
|
||||
|
||||
& > .anticon {
|
||||
@ -179,7 +181,7 @@
|
||||
|
||||
&-padding-transition &-head,
|
||||
&-padding-transition &-body {
|
||||
transition: padding .3s;
|
||||
transition: padding 0.3s;
|
||||
}
|
||||
|
||||
&-type-inner &-head {
|
||||
@ -244,7 +246,12 @@
|
||||
height: 14px;
|
||||
margin: 4px 0;
|
||||
border-radius: @card-radius;
|
||||
background: linear-gradient(90deg, rgba(207, 216, 220, .2), rgba(207, 216, 220, .4), rgba(207, 216, 220, .2));
|
||||
background: linear-gradient(
|
||||
90deg,
|
||||
rgba(207, 216, 220, 0.2),
|
||||
rgba(207, 216, 220, 0.4),
|
||||
rgba(207, 216, 220, 0.2)
|
||||
);
|
||||
animation: card-loading 1.4s ease infinite;
|
||||
background-size: 600% 600%;
|
||||
}
|
||||
@ -253,9 +260,9 @@
|
||||
@keyframes card-loading {
|
||||
0%,
|
||||
100% {
|
||||
background-position: 0 50%;
|
||||
}
|
||||
50% {
|
||||
background-position: 100% 50%;
|
||||
}
|
||||
background-position: 0 50%;
|
||||
}
|
||||
50% {
|
||||
background-position: 100% 50%;
|
||||
}
|
||||
}
|
||||
|
@ -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');
|
||||
|
@ -8,10 +8,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();
|
||||
|
@ -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 {
|
||||
|
@ -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.",
|
||||
);
|
||||
});
|
||||
});
|
||||
|
@ -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;
|
||||
@ -183,8 +202,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,
|
||||
};
|
||||
}
|
||||
|
||||
@ -211,7 +229,7 @@ export default class Cascader extends React.Component<CascaderProps, CascaderSta
|
||||
return;
|
||||
}
|
||||
this.setValue(value, selectedOptions);
|
||||
}
|
||||
};
|
||||
|
||||
handlePopupVisibleChange = (popupVisible: boolean) => {
|
||||
if (!('popupVisible' in this.props)) {
|
||||
@ -226,13 +244,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;
|
||||
@ -243,18 +261,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)) {
|
||||
@ -264,14 +282,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 },
|
||||
);
|
||||
@ -288,7 +307,7 @@ export default class Cascader extends React.Component<CascaderProps, CascaderSta
|
||||
} else {
|
||||
this.setState({ inputValue: '' });
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
flattenTree(
|
||||
options: CascaderOptionType[],
|
||||
@ -298,19 +317,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;
|
||||
@ -334,7 +347,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);
|
||||
@ -345,9 +358,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));
|
||||
@ -363,7 +376,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() {
|
||||
@ -376,13 +391,23 @@ export default class Cascader extends React.Component<CascaderProps, CascaderSta
|
||||
|
||||
saveInput = (node: Input) => {
|
||||
this.input = node;
|
||||
}
|
||||
};
|
||||
|
||||
renderCascader = ({ getPopupContainer: getContextPopupContainer }: ConfigProviderProps) => {
|
||||
const { props, state } = this;
|
||||
const {
|
||||
prefixCls, inputPrefixCls, children, placeholder, size, disabled,
|
||||
className, style, allowClear, showSearch = false, suffixIcon, ...otherProps
|
||||
prefixCls,
|
||||
inputPrefixCls,
|
||||
children,
|
||||
placeholder,
|
||||
size,
|
||||
disabled,
|
||||
className,
|
||||
style,
|
||||
allowClear,
|
||||
showSearch = false,
|
||||
suffixIcon,
|
||||
...otherProps
|
||||
} = props;
|
||||
const { value, inputFocused } = state;
|
||||
|
||||
@ -390,26 +415,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
|
||||
@ -445,39 +470,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}
|
||||
@ -498,9 +518,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`}>
|
||||
@ -527,13 +545,9 @@ export default class Cascader extends React.Component<CascaderProps, CascaderSta
|
||||
{input}
|
||||
</RcCascader>
|
||||
);
|
||||
}
|
||||
};
|
||||
|
||||
render() {
|
||||
return (
|
||||
<ConfigConsumer>
|
||||
{this.renderCascader}
|
||||
</ConfigConsumer>
|
||||
);
|
||||
return <ConfigConsumer>{this.renderCascader}</ConfigConsumer>;
|
||||
}
|
||||
}
|
||||
|
@ -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);
|
||||
}
|
||||
|
@ -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;
|
||||
}
|
||||
};
|
||||
|
||||
render() {
|
||||
const { props, context } = this;
|
||||
|
@ -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,7 +117,7 @@ class CheckboxGroup extends React.Component<CheckboxGroupProps, CheckboxGroupSta
|
||||
if (onChange) {
|
||||
onChange(value);
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
render() {
|
||||
const { props, state } = this;
|
||||
|
@ -10,12 +10,7 @@ describe('Checkbox', () => {
|
||||
const onMouseEnter = jest.fn();
|
||||
const onMouseLeave = jest.fn();
|
||||
|
||||
const wrapper = shallow(
|
||||
<Checkbox
|
||||
onMouseEnter={onMouseEnter}
|
||||
onMouseLeave={onMouseLeave}
|
||||
/>
|
||||
);
|
||||
const wrapper = shallow(<Checkbox onMouseEnter={onMouseEnter} onMouseLeave={onMouseLeave} />);
|
||||
|
||||
wrapper.simulate('mouseenter');
|
||||
expect(onMouseEnter).toHaveBeenCalled();
|
||||
|
@ -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');
|
||||
});
|
||||
|
@ -1,4 +1,4 @@
|
||||
@import "../../style/themes/default";
|
||||
@import "./mixin";
|
||||
@import '../../style/themes/default';
|
||||
@import './mixin';
|
||||
|
||||
.antCheckboxFn();
|
||||
|
@ -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;
|
||||
}
|
||||
|
||||
|
@ -24,20 +24,21 @@ export default class Collapse extends React.Component<CollapseProps, any> {
|
||||
static defaultProps = {
|
||||
prefixCls: 'ant-collapse',
|
||||
bordered: true,
|
||||
openAnimation: { ...animation, appear() { } },
|
||||
openAnimation: { ...animation, appear() {} },
|
||||
};
|
||||
|
||||
renderExpandIcon = () => {
|
||||
return (
|
||||
<Icon type="right" className={`arrow`} />
|
||||
);
|
||||
}
|
||||
return <Icon type="right" className={`arrow`} />;
|
||||
};
|
||||
|
||||
render() {
|
||||
const { prefixCls, className = '', bordered } = this.props;
|
||||
const collapseClassName = classNames({
|
||||
[`${prefixCls}-borderless`]: !bordered,
|
||||
}, className);
|
||||
const collapseClassName = classNames(
|
||||
{
|
||||
[`${prefixCls}-borderless`]: !bordered,
|
||||
},
|
||||
className,
|
||||
);
|
||||
return (
|
||||
<RcCollapse
|
||||
{...this.props}
|
||||
|
@ -17,9 +17,12 @@ export interface CollapsePanelProps {
|
||||
export default class CollapsePanel extends React.Component<CollapsePanelProps, {}> {
|
||||
render() {
|
||||
const { prefixCls, className = '', showArrow = true } = this.props;
|
||||
const collapsePanelClassName = classNames({
|
||||
[`${prefixCls}-no-arrow`]: !showArrow,
|
||||
}, className);
|
||||
const collapsePanelClassName = classNames(
|
||||
{
|
||||
[`${prefixCls}-no-arrow`]: !showArrow,
|
||||
},
|
||||
className,
|
||||
);
|
||||
return <RcCollapse.Panel {...this.props} className={collapsePanelClassName} />;
|
||||
}
|
||||
}
|
||||
|
@ -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();
|
||||
}
|
||||
|
@ -25,30 +25,21 @@ export interface CommentProps {
|
||||
export default class Comment extends React.Component<CommentProps, {}> {
|
||||
static defaultProps = {
|
||||
prefixCls: 'ant-comment',
|
||||
}
|
||||
};
|
||||
|
||||
getAction(actions: React.ReactNode[]) {
|
||||
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 = (children: any) => {
|
||||
const { prefixCls } = this.props;
|
||||
|
||||
return (
|
||||
<div className={classNames(`${prefixCls}-nested`)}>
|
||||
{children}
|
||||
</div>
|
||||
);
|
||||
}
|
||||
return <div className={classNames(`${prefixCls}-nested`)}>{children}</div>;
|
||||
};
|
||||
|
||||
render() {
|
||||
const {
|
||||
@ -70,31 +61,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>
|
||||
);
|
||||
|
@ -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;
|
||||
|
@ -7,19 +7,15 @@ export interface ConfigProviderProps {
|
||||
|
||||
const ConfigContext: Context<ConfigProviderProps | null> = createReactContext({});
|
||||
|
||||
const ConfigProvider: React.SFC<ConfigProviderProps> = (props) => {
|
||||
const ConfigProvider: React.SFC<ConfigProviderProps> = props => {
|
||||
const { getPopupContainer, children } = props;
|
||||
const config = {
|
||||
getPopupContainer,
|
||||
};
|
||||
|
||||
return (
|
||||
<ConfigContext.Provider value={config}>
|
||||
{children}
|
||||
</ConfigContext.Provider>
|
||||
);
|
||||
}
|
||||
return <ConfigContext.Provider value={config}>{children}</ConfigContext.Provider>;
|
||||
};
|
||||
|
||||
export const ConfigConsumer = ConfigContext.Consumer;
|
||||
export const ConfigConsumer = ConfigContext.Consumer;
|
||||
|
||||
export default ConfigProvider;
|
||||
export default ConfigProvider;
|
||||
|
@ -1,2 +1,2 @@
|
||||
// placeholder
|
||||
@import "../../style/themes/default";
|
||||
@import '../../style/themes/default';
|
||||
|
@ -20,7 +20,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;
|
||||
@ -33,7 +33,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;
|
||||
}
|
||||
@ -57,7 +59,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);
|
||||
}
|
||||
@ -88,7 +90,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,
|
||||
@ -102,14 +104,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;
|
||||
@ -126,7 +128,7 @@ class RangePicker extends React.Component<any, RangePickerState> {
|
||||
e.stopPropagation();
|
||||
this.setState({ value: [] });
|
||||
this.handleChange([]);
|
||||
}
|
||||
};
|
||||
|
||||
clearHoverValue = () => this.setState({ hoverValue: [] });
|
||||
|
||||
@ -138,12 +140,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)) {
|
||||
@ -162,7 +161,7 @@ class RangePicker extends React.Component<any, RangePickerState> {
|
||||
if (!open) {
|
||||
this.focus();
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
handleShowDateChange = (showDate: RangePickerValue) => this.setState({ showDate });
|
||||
|
||||
@ -172,10 +171,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;
|
||||
}
|
||||
@ -183,7 +182,7 @@ class RangePicker extends React.Component<any, RangePickerState> {
|
||||
value,
|
||||
showDate: getShowDateFromValue(value) || showDate,
|
||||
}));
|
||||
}
|
||||
};
|
||||
|
||||
handleRangeClick = (value: RangePickerPresetRange) => {
|
||||
if (typeof value === 'function') {
|
||||
@ -200,7 +199,7 @@ class RangePicker extends React.Component<any, RangePickerState> {
|
||||
if (onOpenChange) {
|
||||
onOpenChange(false);
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
setValue(value: RangePickerValue, hidePanel?: boolean) {
|
||||
this.handleChange(value);
|
||||
@ -219,7 +218,7 @@ class RangePicker extends React.Component<any, RangePickerState> {
|
||||
|
||||
savePicker = (node: HTMLSpanElement) => {
|
||||
this.picker = node;
|
||||
}
|
||||
};
|
||||
|
||||
renderFooter = (...args: any[]) => {
|
||||
const { prefixCls, ranges, renderExtraFooter, tagPrefixCls } = this.props;
|
||||
@ -231,7 +230,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
|
||||
@ -246,23 +245,34 @@ 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];
|
||||
}
|
||||
};
|
||||
|
||||
render() {
|
||||
const { state, props } = this;
|
||||
const { value, showDate, hoverValue, open } = state;
|
||||
const {
|
||||
prefixCls, popupStyle, style,
|
||||
disabledDate, disabledTime,
|
||||
showTime, showToday,
|
||||
ranges, onOk, locale, localeCode, format,
|
||||
dateRender, onCalendarChange, suffixIcon,
|
||||
prefixCls,
|
||||
popupStyle,
|
||||
style,
|
||||
disabledDate,
|
||||
disabledTime,
|
||||
showTime,
|
||||
showToday,
|
||||
ranges,
|
||||
onOk,
|
||||
locale,
|
||||
localeCode,
|
||||
format,
|
||||
dateRender,
|
||||
onCalendarChange,
|
||||
suffixIcon,
|
||||
} = props;
|
||||
|
||||
fixLocale(value, localeCode);
|
||||
@ -291,10 +301,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
|
||||
@ -326,32 +336,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
|
||||
|
@ -44,7 +44,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 = {
|
||||
@ -56,30 +56,26 @@ class WeekPicker extends React.Component<any, WeekPickerState> {
|
||||
weekDateRender = (current: any) => {
|
||||
const selectedValue = this.state.value;
|
||||
const { prefixCls } = this.props;
|
||||
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;
|
||||
@ -100,7 +96,7 @@ class WeekPicker extends React.Component<any, WeekPickerState> {
|
||||
e.preventDefault();
|
||||
e.stopPropagation();
|
||||
this.handleChange(null);
|
||||
}
|
||||
};
|
||||
|
||||
focus() {
|
||||
this.input.focus();
|
||||
@ -112,13 +108,26 @@ class WeekPicker extends React.Component<any, WeekPickerState> {
|
||||
|
||||
saveInput = (node: any) => {
|
||||
this.input = node;
|
||||
}
|
||||
};
|
||||
|
||||
render() {
|
||||
const {
|
||||
prefixCls, className, disabled, pickerClass, popupStyle,
|
||||
pickerInputClass, format, allowClear, locale, localeCode, disabledDate,
|
||||
style, onFocus, onBlur, id, suffixIcon,
|
||||
prefixCls,
|
||||
className,
|
||||
disabled,
|
||||
pickerClass,
|
||||
popupStyle,
|
||||
pickerInputClass,
|
||||
format,
|
||||
allowClear,
|
||||
locale,
|
||||
localeCode,
|
||||
disabledDate,
|
||||
style,
|
||||
onFocus,
|
||||
onBlur,
|
||||
id,
|
||||
suffixIcon,
|
||||
} = this.props;
|
||||
|
||||
const { open } = this.state;
|
||||
@ -127,8 +136,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
|
||||
@ -142,28 +151,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 (
|
||||
@ -184,11 +192,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}
|
||||
|
@ -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();
|
||||
});
|
||||
|
@ -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');
|
||||
});
|
||||
});
|
||||
|
@ -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);
|
||||
|
@ -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();
|
||||
});
|
||||
});
|
||||
|
@ -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);
|
||||
|
@ -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();
|
||||
});
|
||||
});
|
||||
|
@ -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();
|
||||
|
@ -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) {
|
||||
|
@ -58,7 +58,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 = {
|
||||
@ -71,17 +71,15 @@ export default function createPicker(TheCalendar: React.ComponentClass): any {
|
||||
renderFooter = (...args: any[]) => {
|
||||
const { prefixCls, renderExtraFooter } = this.props;
|
||||
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;
|
||||
@ -92,11 +90,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;
|
||||
@ -123,15 +121,14 @@ export default function createPicker(TheCalendar: React.ComponentClass): any {
|
||||
|
||||
saveInput = (node: any) => {
|
||||
this.input = node;
|
||||
}
|
||||
};
|
||||
|
||||
render() {
|
||||
const { value, showDate, open } = this.state;
|
||||
const props = omit(this.props, ['onChange']);
|
||||
const { prefixCls, locale, localeCode, suffixIcon } = props;
|
||||
|
||||
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;
|
||||
|
||||
@ -162,7 +159,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}
|
||||
@ -186,28 +186,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 }) => (
|
||||
|
@ -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');
|
||||
|
||||
|
@ -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;
|
||||
}
|
||||
|
@ -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",
|
||||
|
@ -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: {
|
||||
|
@ -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: #fff;
|
||||
@ -243,7 +245,7 @@
|
||||
padding-right: 5px;
|
||||
padding-left: 5px;
|
||||
&:before {
|
||||
content: " ";
|
||||
content: ' ';
|
||||
position: absolute;
|
||||
top: -1px;
|
||||
left: 5px;
|
||||
|
@ -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 {
|
||||
|
@ -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 {
|
||||
|
@ -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;
|
||||
|
@ -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;
|
||||
|
@ -90,7 +90,7 @@
|
||||
}
|
||||
|
||||
li:last-child:after {
|
||||
content: "";
|
||||
content: '';
|
||||
height: 202px;
|
||||
display: block;
|
||||
}
|
||||
|
@ -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;
|
||||
|
@ -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 {
|
||||
|
@ -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';
|
||||
|
@ -28,12 +28,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: {},
|
||||
prefixCls: 'ant-calendar',
|
||||
inputPrefixCls: 'ant-input',
|
||||
@ -51,35 +48,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();
|
||||
@ -91,7 +88,7 @@ export default function wrapPicker(Picker: React.ComponentClass<any>, defaultFor
|
||||
|
||||
savePicker = (node: any) => {
|
||||
this.picker = node;
|
||||
}
|
||||
};
|
||||
|
||||
getDefaultLocale = () => {
|
||||
const result = {
|
||||
@ -103,7 +100,7 @@ export default function wrapPicker(Picker: React.ComponentClass<any>, defaultFor
|
||||
...(this.props.locale || {}).lang,
|
||||
};
|
||||
return result;
|
||||
}
|
||||
};
|
||||
|
||||
renderPicker = (locale: any, localeCode: string) => {
|
||||
const props = this.props;
|
||||
@ -121,7 +118,7 @@ export default function wrapPicker(Picker: React.ComponentClass<any>, defaultFor
|
||||
const rcTimePickerProps = {
|
||||
...generateShowHourMinuteSecond(timeFormat),
|
||||
format: timeFormat,
|
||||
use12Hours: (props.showTime && props.showTime.use12Hours),
|
||||
use12Hours: props.showTime && props.showTime.use12Hours,
|
||||
};
|
||||
const columns = getColumns(rcTimePickerProps);
|
||||
const timePickerCls = `${prefixCls}-time-picker-column-${columns}`;
|
||||
@ -152,14 +149,11 @@ export default function wrapPicker(Picker: React.ComponentClass<any>, defaultFor
|
||||
onMouseLeave={this.handleMouseLeave}
|
||||
/>
|
||||
);
|
||||
}
|
||||
};
|
||||
|
||||
render() {
|
||||
return (
|
||||
<LocaleReceiver
|
||||
componentName="DatePicker"
|
||||
defaultLocale={this.getDefaultLocale}
|
||||
>
|
||||
<LocaleReceiver componentName="DatePicker" defaultLocale={this.getDefaultLocale}>
|
||||
{this.renderPicker}
|
||||
</LocaleReceiver>
|
||||
);
|
||||
|
@ -20,12 +20,16 @@ export default function Divider({
|
||||
dashed,
|
||||
...restProps
|
||||
}: DividerProps) {
|
||||
const orientationPrefix = (orientation.length > 0) ? '-' + orientation : orientation;
|
||||
const orientationPrefix = orientation.length > 0 ? '-' + orientation : orientation;
|
||||
const classString = classNames(
|
||||
className, `${prefixCls}-divider`, `${prefixCls}-divider-${type}`, {
|
||||
[`${prefixCls}-divider-with-text${orientationPrefix}`]: children,
|
||||
[`${prefixCls}-divider-dashed`]: !!dashed,
|
||||
});
|
||||
className,
|
||||
`${prefixCls}-divider`,
|
||||
`${prefixCls}-divider-${type}`,
|
||||
{
|
||||
[`${prefixCls}-divider-with-text${orientationPrefix}`]: children,
|
||||
[`${prefixCls}-divider-dashed`]: !!dashed,
|
||||
},
|
||||
);
|
||||
return (
|
||||
<div className={classString} {...restProps}>
|
||||
{children && <span className={`${prefixCls}-divider-inner-text`}>{children}</span>}
|
||||
|
@ -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%;
|
||||
|
@ -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();
|
||||
});
|
||||
|
@ -23,19 +23,14 @@ class DrawerEventTester extends React.Component {
|
||||
this.setState({
|
||||
visible: true,
|
||||
});
|
||||
}
|
||||
};
|
||||
|
||||
render() {
|
||||
const { visible } = this.state;
|
||||
return (
|
||||
<div>
|
||||
<Button onClick={this.open}>open</Button>
|
||||
<Drawer
|
||||
visible={visible}
|
||||
onClose={this.onClose}
|
||||
getContainer={false}
|
||||
{...this.props}
|
||||
>
|
||||
<Drawer visible={visible} onClose={this.onClose} getContainer={false} {...this.props}>
|
||||
Here is content of Drawer
|
||||
</Drawer>
|
||||
</div>
|
||||
@ -43,7 +38,6 @@ class DrawerEventTester extends React.Component {
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
describe('Drawer', () => {
|
||||
it('render correctly', () => {
|
||||
const wrapper = mount(<DrawerEventTester />);
|
||||
|
@ -48,7 +48,7 @@ class MultiDrawer extends React.Component {
|
||||
visible={visible}
|
||||
>
|
||||
<Button type="primary" id="open_two_drawer" onClick={this.showChildrenDrawer}>
|
||||
Two-level drawer
|
||||
Two-level drawer
|
||||
</Button>
|
||||
<Drawer
|
||||
title="Two-level Drawer"
|
||||
@ -59,9 +59,7 @@ class MultiDrawer extends React.Component {
|
||||
onClose={this.onChildrenDrawerClose}
|
||||
visible={childrenDrawer}
|
||||
>
|
||||
<div id="two_drawer_text">
|
||||
This is two-level drawer
|
||||
</div>
|
||||
<div id="two_drawer_text">This is two-level drawer</div>
|
||||
</Drawer>
|
||||
<div
|
||||
style={{
|
||||
|
Some files were not shown because too many files have changed in this diff Show More
Loading…
Reference in New Issue
Block a user