mirror of
https://github.com/ant-design/ant-design.git
synced 2025-01-18 22:36:31 +08:00
Merge pull request #24371 from ant-design/master
chore: merge master into feature
This commit is contained in:
commit
ae8603838a
@ -22,10 +22,10 @@ references:
|
||||
ignore: gh-pages
|
||||
- dist:
|
||||
requires:
|
||||
- setup
|
||||
- setup
|
||||
- compile:
|
||||
requires:
|
||||
- setup
|
||||
- setup
|
||||
- lint:
|
||||
requires:
|
||||
- setup
|
||||
@ -166,7 +166,7 @@ workflows:
|
||||
<<: *workflow
|
||||
triggers:
|
||||
- schedule:
|
||||
cron: "0 0 * * *"
|
||||
cron: '0 0 * * *'
|
||||
filters:
|
||||
branches:
|
||||
only:
|
||||
|
33
.github/workflows/lighthouse-ci.yml
vendored
33
.github/workflows/lighthouse-ci.yml
vendored
@ -1,19 +1,24 @@
|
||||
name: Lighthouse
|
||||
on: push
|
||||
name: Lighthouse Check
|
||||
on: [pull_request]
|
||||
|
||||
jobs:
|
||||
lighthouse:
|
||||
lighthouse-check:
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- uses: actions/checkout@v1
|
||||
- name: Audit URLs using Lighthouse
|
||||
uses: treosh/lighthouse-ci-action@v2
|
||||
- uses: actions/checkout@master
|
||||
- run: mkdir /tmp/artifacts
|
||||
- name: Run Lighthouse
|
||||
uses: foo-software/lighthouse-check-action@master
|
||||
id: lighthouseCheck
|
||||
with:
|
||||
urls: |
|
||||
https://ant.design
|
||||
https://ant.design/docs/react/introduce-cn
|
||||
https://ant.design/components/button-cn
|
||||
- name: Save results
|
||||
uses: actions/upload-artifact@v1
|
||||
accessToken: ${{ secrets.GITHUB_TOKEN }}
|
||||
outputDirectory: /tmp/artifacts
|
||||
emulatedFormFactor: desktop
|
||||
timeout: 1200
|
||||
prCommentEnabled: false
|
||||
urls: 'https://preview-${{ github.event.pull_request.number }}-ant-design.surge.sh,https://preview-${{ github.event.pull_request.number }}-ant-design.surge.sh/components/button'
|
||||
- name: Upload artifacts
|
||||
uses: actions/upload-artifact@master
|
||||
with:
|
||||
name: lighthouse-results
|
||||
path: '.lighthouseci' # This will save the Lighthouse results as .json files
|
||||
name: Lighthouse reports
|
||||
path: /tmp/artifacts
|
||||
|
31
.github/workflows/ui-ci.yml
vendored
Normal file
31
.github/workflows/ui-ci.yml
vendored
Normal file
@ -0,0 +1,31 @@
|
||||
name: UI-TEST
|
||||
|
||||
on:
|
||||
issue_comment:
|
||||
types: [created]
|
||||
|
||||
jobs:
|
||||
ui:
|
||||
runs-on: ubuntu-latest
|
||||
if: github.event.issue.pull_request != '' && contains(github.event.comment.body, '/ui')
|
||||
steps:
|
||||
- name: checkout
|
||||
uses: actions/checkout@master
|
||||
|
||||
- name: install
|
||||
run: npm install
|
||||
|
||||
- name: dist
|
||||
run: npm run dist
|
||||
|
||||
- name: test
|
||||
run: npm run test:image
|
||||
|
||||
- name: VERCEL Now Deployment
|
||||
uses: amondnet/now-deployment@v2.0.3
|
||||
with:
|
||||
zeit-token: ${{ secrets.VERCEL_TOKEN }}
|
||||
now-project-id: ${{ secrets.VERCEL_PROJECT_ID}}
|
||||
now-org-id: ${{ secrets.VERCEL_ORG_ID}}
|
||||
working-directory: ./jest-stare
|
||||
if: failure()
|
4
.gitignore
vendored
4
.gitignore
vendored
@ -58,3 +58,7 @@ site/theme/template/Resources/**/*.jsx
|
||||
site/theme/template/NotFound.jsx
|
||||
scripts/previewEditor/index.html
|
||||
components/version/version.tsx
|
||||
|
||||
# Image snapshot diff
|
||||
__diff_output__/
|
||||
/jest-stare
|
||||
|
24
.jest.image.js
Normal file
24
.jest.image.js
Normal file
@ -0,0 +1,24 @@
|
||||
const { moduleNameMapper, transformIgnorePatterns } = require('./.jest');
|
||||
|
||||
// jest config for image snapshots
|
||||
module.exports = {
|
||||
setupFiles: ['./tests/setup.js'],
|
||||
moduleFileExtensions: ['ts', 'tsx', 'js', 'md'],
|
||||
moduleNameMapper,
|
||||
transform: {
|
||||
'\\.tsx?$': './node_modules/@ant-design/tools/lib/jest/codePreprocessor',
|
||||
'\\.js$': './node_modules/@ant-design/tools/lib/jest/codePreprocessor',
|
||||
'\\.md$': './node_modules/@ant-design/tools/lib/jest/demoPreprocessor',
|
||||
'\\.(jpg|png|gif|svg)$': './node_modules/@ant-design/tools/lib/jest/imagePreprocessor',
|
||||
},
|
||||
testRegex: 'image\\.test\\.js$',
|
||||
testEnvironment: 'node',
|
||||
transformIgnorePatterns,
|
||||
snapshotSerializers: ['enzyme-to-json/serializer'],
|
||||
globals: {
|
||||
'ts-jest': {
|
||||
tsConfigFile: './tsconfig.test.json',
|
||||
},
|
||||
},
|
||||
reporters: ['default', 'jest-stare'],
|
||||
};
|
2
.jest.js
2
.jest.js
@ -26,7 +26,7 @@ module.exports = {
|
||||
'^react-dnd-test-backend$': 'react-dnd-test-backend/dist/cjs',
|
||||
'^react-dnd-test-utils$': 'react-dnd-test-utils/dist/cjs',
|
||||
},
|
||||
testPathIgnorePatterns: ['/node_modules/', 'dekko', 'node'],
|
||||
testPathIgnorePatterns: ['/node_modules/', 'dekko', 'node', 'image.test.js'],
|
||||
transform: {
|
||||
'\\.tsx?$': './node_modules/@ant-design/tools/lib/jest/codePreprocessor',
|
||||
'\\.js$': './node_modules/@ant-design/tools/lib/jest/codePreprocessor',
|
||||
|
@ -1,5 +1,4 @@
|
||||
import * as React from 'react';
|
||||
import * as ReactDOM from 'react-dom';
|
||||
import CloseOutlined from '@ant-design/icons/CloseOutlined';
|
||||
import CheckCircleOutlined from '@ant-design/icons/CheckCircleOutlined';
|
||||
import ExclamationCircleOutlined from '@ant-design/icons/ExclamationCircleOutlined';
|
||||
@ -12,7 +11,7 @@ import CloseCircleFilled from '@ant-design/icons/CloseCircleFilled';
|
||||
import Animate from 'rc-animate';
|
||||
import classNames from 'classnames';
|
||||
|
||||
import { ConfigConsumer, ConfigConsumerProps } from '../config-provider';
|
||||
import { ConfigContext } from '../config-provider';
|
||||
import getDataOrAriaProps from '../_util/getDataOrAriaProps';
|
||||
import ErrorBoundary from './ErrorBoundary';
|
||||
import { replaceElement } from '../_util/reactNode';
|
||||
@ -48,11 +47,6 @@ export interface AlertProps {
|
||||
onClick?: React.MouseEventHandler<HTMLDivElement>;
|
||||
}
|
||||
|
||||
export interface AlertState {
|
||||
closing: boolean;
|
||||
closed: boolean;
|
||||
}
|
||||
|
||||
const iconMapFilled = {
|
||||
success: CheckCircleFilled,
|
||||
info: InfoCircleFilled,
|
||||
@ -67,66 +61,67 @@ const iconMapOutlined = {
|
||||
warning: ExclamationCircleOutlined,
|
||||
};
|
||||
|
||||
export default class Alert extends React.Component<AlertProps, AlertState> {
|
||||
static ErrorBoundary = ErrorBoundary;
|
||||
interface AlertInterface extends React.FC<AlertProps> {
|
||||
ErrorBoundary: typeof ErrorBoundary;
|
||||
}
|
||||
|
||||
state = {
|
||||
closing: false,
|
||||
closed: false,
|
||||
};
|
||||
const Alert: AlertInterface = ({
|
||||
description,
|
||||
prefixCls: customizePrefixCls,
|
||||
message,
|
||||
banner,
|
||||
className = '',
|
||||
style,
|
||||
onMouseEnter,
|
||||
onMouseLeave,
|
||||
onClick,
|
||||
showIcon,
|
||||
closable,
|
||||
closeText,
|
||||
...props
|
||||
}) => {
|
||||
const [closing, setClosing] = React.useState(false);
|
||||
const [closed, setClosed] = React.useState(false);
|
||||
|
||||
handleClose = (e: React.MouseEvent<HTMLButtonElement>) => {
|
||||
const ref = React.useRef<HTMLElement>();
|
||||
const { getPrefixCls, direction } = React.useContext(ConfigContext);
|
||||
const prefixCls = getPrefixCls('alert', customizePrefixCls);
|
||||
|
||||
const handleClose = (e: React.MouseEvent<HTMLButtonElement>) => {
|
||||
e.preventDefault();
|
||||
const dom = ReactDOM.findDOMNode(this) as HTMLElement;
|
||||
const dom = ref.current as HTMLElement;
|
||||
dom.style.height = `${dom.offsetHeight}px`;
|
||||
// Magic code
|
||||
// 重复一次后才能正确设置 height
|
||||
dom.style.height = `${dom.offsetHeight}px`;
|
||||
|
||||
this.setState({
|
||||
closing: true,
|
||||
});
|
||||
this.props.onClose?.(e);
|
||||
setClosing(true);
|
||||
props.onClose?.(e);
|
||||
};
|
||||
|
||||
animationEnd = () => {
|
||||
this.setState({
|
||||
closing: false,
|
||||
closed: true,
|
||||
});
|
||||
this.props.afterClose?.();
|
||||
const animationEnd = () => {
|
||||
setClosing(false);
|
||||
setClosed(true);
|
||||
props.afterClose?.();
|
||||
};
|
||||
|
||||
getShowIcon() {
|
||||
const { banner, showIcon } = this.props;
|
||||
// banner 模式默认有 Icon
|
||||
return banner && showIcon === undefined ? true : showIcon;
|
||||
}
|
||||
|
||||
getType() {
|
||||
const { banner, type } = this.props;
|
||||
const getType = () => {
|
||||
const { type } = props;
|
||||
if (type !== undefined) {
|
||||
return type;
|
||||
}
|
||||
// banner 模式默认为警告
|
||||
return banner ? 'warning' : 'info';
|
||||
}
|
||||
};
|
||||
|
||||
getClosable() {
|
||||
const { closable, closeText } = this.props;
|
||||
// closeable when closeText is assigned
|
||||
return closeText ? true : closable;
|
||||
}
|
||||
// closeable when closeText is assigned
|
||||
const isClosable = closeText ? true : closable;
|
||||
const type = getType();
|
||||
|
||||
getIconType() {
|
||||
const { description } = this.props;
|
||||
const renderIconNode = () => {
|
||||
const { icon } = props;
|
||||
// use outline icon in alert with description
|
||||
return (description ? iconMapOutlined : iconMapFilled)[this.getType()] || null;
|
||||
}
|
||||
|
||||
renderIconNode({ prefixCls }: { prefixCls: string }) {
|
||||
const { icon } = this.props;
|
||||
const iconType = this.getIconType();
|
||||
const iconType = (description ? iconMapOutlined : iconMapFilled)[type] || null;
|
||||
if (icon) {
|
||||
return replaceElement(icon, <span className={`${prefixCls}-icon`}>{icon}</span>, () => ({
|
||||
className: classNames(`${prefixCls}-icon`, {
|
||||
@ -135,14 +130,13 @@ export default class Alert extends React.Component<AlertProps, AlertState> {
|
||||
}));
|
||||
}
|
||||
return React.createElement(iconType, { className: `${prefixCls}-icon` });
|
||||
}
|
||||
};
|
||||
|
||||
renderCloseIcon({ prefixCls }: { prefixCls: string }) {
|
||||
const { closeText } = this.props;
|
||||
return this.getClosable() ? (
|
||||
const renderCloseIcon = () => {
|
||||
return isClosable ? (
|
||||
<button
|
||||
type="button"
|
||||
onClick={this.handleClose}
|
||||
onClick={handleClose}
|
||||
className={`${prefixCls}-close-icon`}
|
||||
tabIndex={0}
|
||||
>
|
||||
@ -153,74 +147,53 @@ export default class Alert extends React.Component<AlertProps, AlertState> {
|
||||
)}
|
||||
</button>
|
||||
) : null;
|
||||
}
|
||||
|
||||
renderAlert = ({ getPrefixCls, direction }: ConfigConsumerProps) => {
|
||||
const {
|
||||
description,
|
||||
prefixCls: customizePrefixCls,
|
||||
message,
|
||||
banner,
|
||||
className = '',
|
||||
style,
|
||||
onMouseEnter,
|
||||
onMouseLeave,
|
||||
onClick,
|
||||
} = this.props;
|
||||
const { closing, closed } = this.state;
|
||||
|
||||
const prefixCls = getPrefixCls('alert', customizePrefixCls);
|
||||
|
||||
const isShowIcon = this.getShowIcon();
|
||||
const type = this.getType();
|
||||
const closable = this.getClosable();
|
||||
|
||||
const alertCls = classNames(
|
||||
prefixCls,
|
||||
`${prefixCls}-${type}`,
|
||||
{
|
||||
[`${prefixCls}-closing`]: closing,
|
||||
[`${prefixCls}-with-description`]: !!description,
|
||||
[`${prefixCls}-no-icon`]: !isShowIcon,
|
||||
[`${prefixCls}-banner`]: !!banner,
|
||||
[`${prefixCls}-closable`]: closable,
|
||||
[`${prefixCls}-rtl`]: direction === 'rtl',
|
||||
},
|
||||
className,
|
||||
);
|
||||
|
||||
const closeIcon = this.renderCloseIcon({ prefixCls });
|
||||
|
||||
const dataOrAriaProps = getDataOrAriaProps(this.props);
|
||||
|
||||
const iconNode = this.renderIconNode({ prefixCls });
|
||||
|
||||
return closed ? null : (
|
||||
<Animate
|
||||
component=""
|
||||
showProp="data-show"
|
||||
transitionName={`${prefixCls}-slide-up`}
|
||||
onEnd={this.animationEnd}
|
||||
>
|
||||
<div
|
||||
data-show={!closing}
|
||||
className={alertCls}
|
||||
style={style}
|
||||
onMouseEnter={onMouseEnter}
|
||||
onMouseLeave={onMouseLeave}
|
||||
onClick={onClick}
|
||||
{...dataOrAriaProps}
|
||||
>
|
||||
{isShowIcon ? iconNode : null}
|
||||
<span className={`${prefixCls}-message`}>{message}</span>
|
||||
<span className={`${prefixCls}-description`}>{description}</span>
|
||||
{closeIcon}
|
||||
</div>
|
||||
</Animate>
|
||||
);
|
||||
};
|
||||
|
||||
render() {
|
||||
return <ConfigConsumer>{this.renderAlert}</ConfigConsumer>;
|
||||
}
|
||||
}
|
||||
// banner 模式默认有 Icon
|
||||
const isShowIcon = banner && showIcon === undefined ? true : showIcon;
|
||||
|
||||
const alertCls = classNames(
|
||||
prefixCls,
|
||||
`${prefixCls}-${type}`,
|
||||
{
|
||||
[`${prefixCls}-closing`]: closing,
|
||||
[`${prefixCls}-with-description`]: !!description,
|
||||
[`${prefixCls}-no-icon`]: !isShowIcon,
|
||||
[`${prefixCls}-banner`]: !!banner,
|
||||
[`${prefixCls}-closable`]: isClosable,
|
||||
[`${prefixCls}-rtl`]: direction === 'rtl',
|
||||
},
|
||||
className,
|
||||
);
|
||||
|
||||
const dataOrAriaProps = getDataOrAriaProps(props);
|
||||
|
||||
return closed ? null : (
|
||||
<Animate
|
||||
component=""
|
||||
showProp="data-show"
|
||||
transitionName={`${prefixCls}-slide-up`}
|
||||
onEnd={animationEnd}
|
||||
>
|
||||
<div
|
||||
ref={ref}
|
||||
data-show={!closing}
|
||||
className={alertCls}
|
||||
style={style}
|
||||
onMouseEnter={onMouseEnter}
|
||||
onMouseLeave={onMouseLeave}
|
||||
onClick={onClick}
|
||||
{...dataOrAriaProps}
|
||||
>
|
||||
{isShowIcon ? renderIconNode() : null}
|
||||
<span className={`${prefixCls}-message`}>{message}</span>
|
||||
<span className={`${prefixCls}-description`}>{description}</span>
|
||||
{renderCloseIcon()}
|
||||
</div>
|
||||
</Animate>
|
||||
);
|
||||
};
|
||||
|
||||
Alert.ErrorBoundary = ErrorBoundary;
|
||||
|
||||
export default Alert;
|
||||
|
@ -16,6 +16,7 @@ export interface AvatarProps {
|
||||
src?: string;
|
||||
/** Srcset of image avatar */
|
||||
srcSet?: string;
|
||||
draggable?: boolean;
|
||||
/** icon to be used in avatar */
|
||||
icon?: React.ReactNode;
|
||||
style?: React.CSSProperties;
|
||||
@ -106,6 +107,7 @@ export default class Avatar extends React.Component<AvatarProps, AvatarState> {
|
||||
icon,
|
||||
className,
|
||||
alt,
|
||||
draggable,
|
||||
...others
|
||||
} = this.props;
|
||||
|
||||
@ -142,7 +144,15 @@ export default class Avatar extends React.Component<AvatarProps, AvatarState> {
|
||||
|
||||
let { children } = this.props;
|
||||
if (src && isImgExist) {
|
||||
children = <img src={src} srcSet={srcSet} onError={this.handleImgLoadError} alt={alt} />;
|
||||
children = (
|
||||
<img
|
||||
src={src}
|
||||
draggable={draggable}
|
||||
srcSet={srcSet}
|
||||
onError={this.handleImgLoadError}
|
||||
alt={alt}
|
||||
/>
|
||||
);
|
||||
} else if (icon) {
|
||||
children = icon;
|
||||
} else {
|
||||
|
@ -22,6 +22,12 @@
|
||||
line-height: @line-height-base;
|
||||
.btn;
|
||||
.btn-default;
|
||||
|
||||
// Fix loading button animation
|
||||
// https://github.com/ant-design/ant-design/issues/24323
|
||||
> span {
|
||||
display: inline-block;
|
||||
}
|
||||
|
||||
&-primary {
|
||||
.btn-primary;
|
||||
|
@ -16,10 +16,7 @@ describe('Calendar', () => {
|
||||
rtlTest(Calendar, true);
|
||||
|
||||
function openSelect(wrapper, className) {
|
||||
wrapper
|
||||
.find(className)
|
||||
.find('.ant-select-selector')
|
||||
.simulate('mousedown');
|
||||
wrapper.find(className).find('.ant-select-selector').simulate('mousedown');
|
||||
}
|
||||
|
||||
function findSelectItem(wrapper) {
|
||||
@ -27,18 +24,13 @@ describe('Calendar', () => {
|
||||
}
|
||||
|
||||
function clickSelectItem(wrapper, index = 0) {
|
||||
findSelectItem(wrapper)
|
||||
.at(index)
|
||||
.simulate('click');
|
||||
findSelectItem(wrapper).at(index).simulate('click');
|
||||
}
|
||||
|
||||
it('Calendar should be selectable', () => {
|
||||
const onSelect = jest.fn();
|
||||
const wrapper = mount(<Calendar onSelect={onSelect} />);
|
||||
wrapper
|
||||
.find('.ant-picker-cell')
|
||||
.at(0)
|
||||
.simulate('click');
|
||||
wrapper.find('.ant-picker-cell').at(0).simulate('click');
|
||||
expect(onSelect).toHaveBeenCalledWith(expect.anything());
|
||||
const value = onSelect.mock.calls[0][0];
|
||||
expect(Moment.isMoment(value)).toBe(true);
|
||||
@ -50,14 +42,8 @@ describe('Calendar', () => {
|
||||
const wrapper = mount(
|
||||
<Calendar onSelect={onSelect} validRange={validRange} defaultValue={Moment('2018-02-02')} />,
|
||||
);
|
||||
wrapper
|
||||
.find('[title="2018-02-01"]')
|
||||
.at(0)
|
||||
.simulate('click');
|
||||
wrapper
|
||||
.find('[title="2018-02-02"]')
|
||||
.at(0)
|
||||
.simulate('click');
|
||||
wrapper.find('[title="2018-02-01"]').at(0).simulate('click');
|
||||
wrapper.find('[title="2018-02-02"]').at(0).simulate('click');
|
||||
expect(onSelect.mock.calls.length).toBe(1);
|
||||
});
|
||||
|
||||
@ -67,10 +53,7 @@ describe('Calendar', () => {
|
||||
const wrapper = mount(
|
||||
<Calendar onSelect={onSelect} validRange={validRange} defaultValue={Moment('2018-02-02')} />,
|
||||
);
|
||||
wrapper
|
||||
.find('[title="2018-02-20"]')
|
||||
.at(0)
|
||||
.simulate('click');
|
||||
wrapper.find('[title="2018-02-20"]').at(0).simulate('click');
|
||||
const elem = wrapper.find('[title="2018-02-20"]').hasClass('ant-picker-cell-disabled');
|
||||
expect(elem).toEqual(true);
|
||||
expect(onSelect.mock.calls.length).toBe(0);
|
||||
@ -87,32 +70,13 @@ describe('Calendar', () => {
|
||||
mode="year"
|
||||
/>,
|
||||
);
|
||||
expect(
|
||||
wrapper
|
||||
.find('[title="2018-01"]')
|
||||
.at(0)
|
||||
.hasClass('ant-picker-cell-disabled'),
|
||||
).toBe(true);
|
||||
expect(
|
||||
wrapper
|
||||
.find('[title="2018-02"]')
|
||||
.at(0)
|
||||
.hasClass('ant-picker-cell-disabled'),
|
||||
).toBe(false);
|
||||
expect(
|
||||
wrapper
|
||||
.find('[title="2018-06"]')
|
||||
.at(0)
|
||||
.hasClass('ant-picker-cell-disabled'),
|
||||
).toBe(true);
|
||||
wrapper
|
||||
.find('[title="2018-01"]')
|
||||
.at(0)
|
||||
.simulate('click');
|
||||
wrapper
|
||||
.find('[title="2018-03"]')
|
||||
.at(0)
|
||||
.simulate('click');
|
||||
expect(wrapper.find('[title="2018-01"]').at(0).hasClass('ant-picker-cell-disabled')).toBe(true);
|
||||
expect(wrapper.find('[title="2018-02"]').at(0).hasClass('ant-picker-cell-disabled')).toBe(
|
||||
false,
|
||||
);
|
||||
expect(wrapper.find('[title="2018-06"]').at(0).hasClass('ant-picker-cell-disabled')).toBe(true);
|
||||
wrapper.find('[title="2018-01"]').at(0).simulate('click');
|
||||
wrapper.find('[title="2018-03"]').at(0).simulate('click');
|
||||
expect(onSelect.mock.calls.length).toBe(1);
|
||||
});
|
||||
|
||||
@ -155,7 +119,7 @@ describe('Calendar', () => {
|
||||
});
|
||||
|
||||
it('Calendar should support locale', () => {
|
||||
MockDate.set(Moment('2018-10-19'));
|
||||
MockDate.set(Moment('2018-10-19').valueOf());
|
||||
// eslint-disable-next-line global-require
|
||||
const zhCN = require('../locale/zh_CN').default;
|
||||
const wrapper = mount(<Calendar locale={zhCN} />);
|
||||
@ -168,10 +132,7 @@ describe('Calendar', () => {
|
||||
const date = new Moment('1990-09-03');
|
||||
const wrapper = mount(<Calendar onPanelChange={onPanelChange} value={date} />);
|
||||
|
||||
wrapper
|
||||
.find('.ant-picker-cell')
|
||||
.at(0)
|
||||
.simulate('click');
|
||||
wrapper.find('.ant-picker-cell').at(0).simulate('click');
|
||||
|
||||
expect(onPanelChange).toHaveBeenCalled();
|
||||
expect(onPanelChange.mock.calls[0][0].month()).toEqual(date.month() - 1);
|
||||
@ -242,10 +203,7 @@ describe('Calendar', () => {
|
||||
/>,
|
||||
);
|
||||
openSelect(wrapper, '.ant-picker-calendar-year-select');
|
||||
wrapper
|
||||
.find('.ant-select-item-option')
|
||||
.last()
|
||||
.simulate('click');
|
||||
wrapper.find('.ant-select-item-option').last().simulate('click');
|
||||
expect(onValueChange).toHaveBeenCalledWith(value.year('2019').month('2'));
|
||||
});
|
||||
|
||||
@ -283,10 +241,7 @@ describe('Calendar', () => {
|
||||
type="date"
|
||||
/>,
|
||||
);
|
||||
wrapper
|
||||
.find('input[type="radio"]')
|
||||
.at(1)
|
||||
.simulate('change');
|
||||
wrapper.find('input[type="radio"]').at(1).simulate('change');
|
||||
expect(onTypeChange).toHaveBeenCalledWith('year');
|
||||
});
|
||||
|
||||
@ -324,9 +279,7 @@ describe('Calendar', () => {
|
||||
openSelect(wrapperWithYear, '.ant-select');
|
||||
wrapperWithYear.update();
|
||||
|
||||
findSelectItem(wrapperWithYear)
|
||||
.last()
|
||||
.simulate('click');
|
||||
findSelectItem(wrapperWithYear).last().simulate('click');
|
||||
|
||||
expect(onYearChange).toHaveBeenCalled();
|
||||
|
||||
@ -371,9 +324,7 @@ describe('Calendar', () => {
|
||||
openSelect(wrapperWithMonth, '.ant-select');
|
||||
wrapperWithMonth.update();
|
||||
|
||||
findSelectItem(wrapperWithMonth)
|
||||
.last()
|
||||
.simulate('click');
|
||||
findSelectItem(wrapperWithMonth).last().simulate('click');
|
||||
|
||||
expect(onMonthChange).toHaveBeenCalled();
|
||||
|
||||
@ -391,10 +342,7 @@ describe('Calendar', () => {
|
||||
<Calendar fullscreen={false} headerRender={headerRenderWithTypeChange} />,
|
||||
);
|
||||
|
||||
wrapperWithTypeChange
|
||||
.find('.ant-radio-button-input')
|
||||
.last()
|
||||
.simulate('change');
|
||||
wrapperWithTypeChange.find('.ant-radio-button-input').last().simulate('change');
|
||||
expect(onTypeChange).toHaveBeenCalled();
|
||||
});
|
||||
|
||||
@ -402,23 +350,13 @@ describe('Calendar', () => {
|
||||
const wrapper = mount(
|
||||
<Calendar dateFullCellRender={() => <div className="light">Bamboo</div>} />,
|
||||
);
|
||||
expect(
|
||||
wrapper
|
||||
.find('.light')
|
||||
.first()
|
||||
.text(),
|
||||
).toEqual('Bamboo');
|
||||
expect(wrapper.find('.light').first().text()).toEqual('Bamboo');
|
||||
});
|
||||
|
||||
it('monthFullCellRender', () => {
|
||||
const wrapper = mount(
|
||||
<Calendar mode="year" monthFullCellRender={() => <div className="bamboo">Light</div>} />,
|
||||
);
|
||||
expect(
|
||||
wrapper
|
||||
.find('.bamboo')
|
||||
.first()
|
||||
.text(),
|
||||
).toEqual('Light');
|
||||
expect(wrapper.find('.bamboo').first().text()).toEqual('Light');
|
||||
});
|
||||
});
|
||||
|
@ -11,7 +11,7 @@ describe('DatePicker', () => {
|
||||
focusTest(DatePicker, { refFocus: true });
|
||||
|
||||
beforeEach(() => {
|
||||
MockDate.set(moment('2016-11-22'));
|
||||
MockDate.set(moment('2016-11-22').valueOf());
|
||||
});
|
||||
|
||||
afterEach(() => {
|
||||
|
@ -194,7 +194,7 @@ Provide linkage between forms. If a sub form with `name` prop update, it will au
|
||||
| isFieldsTouched | Check if fields have been operated. Check if all fields is touched when `allTouched` is `true` | (nameList?: [NamePath](#NamePath)[], allTouched?: boolean) => boolean |
|
||||
| isFieldValidating | Check fields if is in validating | (name: [NamePath](#NamePath)) => boolean |
|
||||
| resetFields | Reset fields to `initialValues` | (fields?: [NamePath](#NamePath)[]) => void |
|
||||
| scrollToField | Scroll to field position | (name: [NamePath](#NamePath), options: [[ScrollOptions](https://github.com/stipsan/scroll-into-view-if-needed/blob/ece40bd9143f48caf4b99503425ecb16b0ad8249/src/types.ts#L10)]) => void |
|
||||
| scrollToField | Scroll to field position | (name: [NamePath](#NamePath), options: [[ScrollOptions](https://github.com/stipsan/scroll-into-view-if-needed/tree/ece40bd9143f48caf4b99503425ecb16b0ad8249#options)]) => void |
|
||||
| setFields | Set fields status | (fields: [FieldData](#FieldData)[]) => void |
|
||||
| setFieldsValue | Set fields value | (values) => void |
|
||||
| submit | Submit the form. It's same as click `submit` button | () => void |
|
||||
|
@ -195,7 +195,7 @@ Form 通过增量更新方式,只更新被修改的字段相关组件以达到
|
||||
| isFieldsTouched | 检查一组字段是否被用户操作过,`allTouched` 为 `true` 时检查是否所有字段都被操作过 | (nameList?: [NamePath](#NamePath)[], allTouched?: boolean) => boolean |
|
||||
| isFieldValidating | 检查一组字段是否正在校验 | (name: [NamePath](#NamePath)) => boolean |
|
||||
| resetFields | 重置一组字段到 `initialValues` | (fields?: [NamePath](#NamePath)[]) => void |
|
||||
| scrollToField | 滚动到对应字段位置 | (name: [NamePath](#NamePath), options: [[ScrollOptions](https://github.com/stipsan/scroll-into-view-if-needed/blob/ece40bd9143f48caf4b99503425ecb16b0ad8249/src/types.ts#L10)]) => void |
|
||||
| scrollToField | 滚动到对应字段位置 | (name: [NamePath](#NamePath), options: [[ScrollOptions](https://github.com/stipsan/scroll-into-view-if-needed/tree/ece40bd9143f48caf4b99503425ecb16b0ad8249#options)]) => void |
|
||||
| setFields | 设置一组字段状态 | (fields: [FieldData](#FieldData)[]) => void |
|
||||
| setFieldsValue | 设置表单的值 | (values) => void |
|
||||
| submit | 提交表单,与点击 `submit` 按钮效果相同 | () => void |
|
||||
|
Binary file not shown.
After Width: | Height: | Size: 10 KiB |
37
components/grid/__tests__/image.test.js
Normal file
37
components/grid/__tests__/image.test.js
Normal file
@ -0,0 +1,37 @@
|
||||
import React from 'react';
|
||||
import { Col, Row } from '..';
|
||||
import imageTest from '../../../tests/shared/imageTest';
|
||||
|
||||
describe('Grid image', () => {
|
||||
imageTest(
|
||||
<>
|
||||
<Row>
|
||||
<Col>col</Col>
|
||||
</Row>
|
||||
<Row>
|
||||
<Col>col</Col>
|
||||
<Col>col</Col>
|
||||
<Col>col</Col>
|
||||
<Col>col</Col>
|
||||
</Row>
|
||||
<Row>
|
||||
<Col span={24}>col</Col>
|
||||
</Row>
|
||||
<Row>
|
||||
<Col span={12}>col-12</Col>
|
||||
<Col span={12}>col-12</Col>
|
||||
</Row>
|
||||
<Row>
|
||||
<Col span={8}>col-8</Col>
|
||||
<Col span={8}>col-8</Col>
|
||||
<Col span={8}>col-8</Col>
|
||||
</Row>
|
||||
<Row>
|
||||
<Col span={6}>col-6</Col>
|
||||
<Col span={6}>col-6</Col>
|
||||
<Col span={6}>col-6</Col>
|
||||
<Col span={6}>col-6</Col>
|
||||
</Row>
|
||||
</>,
|
||||
);
|
||||
});
|
@ -47,5 +47,6 @@
|
||||
position: absolute;
|
||||
top: 0;
|
||||
right: 0;
|
||||
z-index: 1;
|
||||
margin: 8px 8px 0 0;
|
||||
}
|
||||
|
@ -107708,6 +107708,9 @@ exports[`Locale Provider should display the text as id 1`] = `
|
||||
>
|
||||
<thead>
|
||||
<tr>
|
||||
<th>
|
||||
Mg
|
||||
</th>
|
||||
<th>
|
||||
Sn
|
||||
</th>
|
||||
@ -107726,13 +107729,20 @@ exports[`Locale Provider should display the text as id 1`] = `
|
||||
<th>
|
||||
Sb
|
||||
</th>
|
||||
<th>
|
||||
Mg
|
||||
</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
<tr>
|
||||
<td
|
||||
class="ant-picker-cell"
|
||||
title="2017-08-27"
|
||||
>
|
||||
<div
|
||||
class="ant-picker-cell-inner"
|
||||
>
|
||||
27
|
||||
</div>
|
||||
</td>
|
||||
<td
|
||||
class="ant-picker-cell"
|
||||
title="2017-08-28"
|
||||
@ -107793,6 +107803,8 @@ exports[`Locale Provider should display the text as id 1`] = `
|
||||
2
|
||||
</div>
|
||||
</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td
|
||||
class="ant-picker-cell ant-picker-cell-in-view"
|
||||
title="2017-09-03"
|
||||
@ -107803,8 +107815,6 @@ exports[`Locale Provider should display the text as id 1`] = `
|
||||
3
|
||||
</div>
|
||||
</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td
|
||||
class="ant-picker-cell ant-picker-cell-in-view"
|
||||
title="2017-09-04"
|
||||
@ -107865,6 +107875,8 @@ exports[`Locale Provider should display the text as id 1`] = `
|
||||
9
|
||||
</div>
|
||||
</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td
|
||||
class="ant-picker-cell ant-picker-cell-in-view"
|
||||
title="2017-09-10"
|
||||
@ -107875,8 +107887,6 @@ exports[`Locale Provider should display the text as id 1`] = `
|
||||
10
|
||||
</div>
|
||||
</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td
|
||||
class="ant-picker-cell ant-picker-cell-in-view"
|
||||
title="2017-09-11"
|
||||
@ -107937,6 +107947,8 @@ exports[`Locale Provider should display the text as id 1`] = `
|
||||
16
|
||||
</div>
|
||||
</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td
|
||||
class="ant-picker-cell ant-picker-cell-in-view"
|
||||
title="2017-09-17"
|
||||
@ -107947,8 +107959,6 @@ exports[`Locale Provider should display the text as id 1`] = `
|
||||
17
|
||||
</div>
|
||||
</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td
|
||||
class="ant-picker-cell ant-picker-cell-in-view ant-picker-cell-today"
|
||||
title="2017-09-18"
|
||||
@ -108009,6 +108019,8 @@ exports[`Locale Provider should display the text as id 1`] = `
|
||||
23
|
||||
</div>
|
||||
</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td
|
||||
class="ant-picker-cell ant-picker-cell-in-view"
|
||||
title="2017-09-24"
|
||||
@ -108019,8 +108031,6 @@ exports[`Locale Provider should display the text as id 1`] = `
|
||||
24
|
||||
</div>
|
||||
</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td
|
||||
class="ant-picker-cell ant-picker-cell-in-view"
|
||||
title="2017-09-25"
|
||||
@ -108081,6 +108091,8 @@ exports[`Locale Provider should display the text as id 1`] = `
|
||||
30
|
||||
</div>
|
||||
</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td
|
||||
class="ant-picker-cell"
|
||||
title="2017-10-01"
|
||||
@ -108091,8 +108103,6 @@ exports[`Locale Provider should display the text as id 1`] = `
|
||||
1
|
||||
</div>
|
||||
</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td
|
||||
class="ant-picker-cell"
|
||||
title="2017-10-02"
|
||||
@ -108153,16 +108163,6 @@ exports[`Locale Provider should display the text as id 1`] = `
|
||||
7
|
||||
</div>
|
||||
</td>
|
||||
<td
|
||||
class="ant-picker-cell"
|
||||
title="2017-10-08"
|
||||
>
|
||||
<div
|
||||
class="ant-picker-cell-inner"
|
||||
>
|
||||
8
|
||||
</div>
|
||||
</td>
|
||||
</tr>
|
||||
</tbody>
|
||||
</table>
|
||||
@ -109767,6 +109767,9 @@ exports[`Locale Provider should display the text as id 1`] = `
|
||||
>
|
||||
<thead>
|
||||
<tr>
|
||||
<th>
|
||||
Mg
|
||||
</th>
|
||||
<th>
|
||||
Sn
|
||||
</th>
|
||||
@ -109785,13 +109788,20 @@ exports[`Locale Provider should display the text as id 1`] = `
|
||||
<th>
|
||||
Sb
|
||||
</th>
|
||||
<th>
|
||||
Mg
|
||||
</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
<tr>
|
||||
<td
|
||||
class="ant-picker-cell"
|
||||
title="2017-08-27"
|
||||
>
|
||||
<div
|
||||
class="ant-picker-cell-inner"
|
||||
>
|
||||
27
|
||||
</div>
|
||||
</td>
|
||||
<td
|
||||
class="ant-picker-cell"
|
||||
title="2017-08-28"
|
||||
@ -109852,6 +109862,8 @@ exports[`Locale Provider should display the text as id 1`] = `
|
||||
2
|
||||
</div>
|
||||
</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td
|
||||
class="ant-picker-cell ant-picker-cell-in-view"
|
||||
title="2017-09-03"
|
||||
@ -109862,8 +109874,6 @@ exports[`Locale Provider should display the text as id 1`] = `
|
||||
3
|
||||
</div>
|
||||
</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td
|
||||
class="ant-picker-cell ant-picker-cell-in-view"
|
||||
title="2017-09-04"
|
||||
@ -109924,6 +109934,8 @@ exports[`Locale Provider should display the text as id 1`] = `
|
||||
9
|
||||
</div>
|
||||
</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td
|
||||
class="ant-picker-cell ant-picker-cell-in-view"
|
||||
title="2017-09-10"
|
||||
@ -109934,8 +109946,6 @@ exports[`Locale Provider should display the text as id 1`] = `
|
||||
10
|
||||
</div>
|
||||
</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td
|
||||
class="ant-picker-cell ant-picker-cell-in-view"
|
||||
title="2017-09-11"
|
||||
@ -109996,6 +110006,8 @@ exports[`Locale Provider should display the text as id 1`] = `
|
||||
16
|
||||
</div>
|
||||
</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td
|
||||
class="ant-picker-cell ant-picker-cell-in-view"
|
||||
title="2017-09-17"
|
||||
@ -110006,8 +110018,6 @@ exports[`Locale Provider should display the text as id 1`] = `
|
||||
17
|
||||
</div>
|
||||
</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td
|
||||
class="ant-picker-cell ant-picker-cell-in-view ant-picker-cell-today"
|
||||
title="2017-09-18"
|
||||
@ -110068,6 +110078,8 @@ exports[`Locale Provider should display the text as id 1`] = `
|
||||
23
|
||||
</div>
|
||||
</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td
|
||||
class="ant-picker-cell ant-picker-cell-in-view"
|
||||
title="2017-09-24"
|
||||
@ -110078,8 +110090,6 @@ exports[`Locale Provider should display the text as id 1`] = `
|
||||
24
|
||||
</div>
|
||||
</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td
|
||||
class="ant-picker-cell ant-picker-cell-in-view"
|
||||
title="2017-09-25"
|
||||
@ -110140,6 +110150,8 @@ exports[`Locale Provider should display the text as id 1`] = `
|
||||
30
|
||||
</div>
|
||||
</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td
|
||||
class="ant-picker-cell"
|
||||
title="2017-10-01"
|
||||
@ -110150,8 +110162,6 @@ exports[`Locale Provider should display the text as id 1`] = `
|
||||
1
|
||||
</div>
|
||||
</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td
|
||||
class="ant-picker-cell"
|
||||
title="2017-10-02"
|
||||
@ -110212,16 +110222,6 @@ exports[`Locale Provider should display the text as id 1`] = `
|
||||
7
|
||||
</div>
|
||||
</td>
|
||||
<td
|
||||
class="ant-picker-cell"
|
||||
title="2017-10-08"
|
||||
>
|
||||
<div
|
||||
class="ant-picker-cell-inner"
|
||||
>
|
||||
8
|
||||
</div>
|
||||
</td>
|
||||
</tr>
|
||||
</tbody>
|
||||
</table>
|
||||
@ -110303,6 +110303,9 @@ exports[`Locale Provider should display the text as id 1`] = `
|
||||
>
|
||||
<thead>
|
||||
<tr>
|
||||
<th>
|
||||
Mg
|
||||
</th>
|
||||
<th>
|
||||
Sn
|
||||
</th>
|
||||
@ -110321,73 +110324,10 @@ exports[`Locale Provider should display the text as id 1`] = `
|
||||
<th>
|
||||
Sb
|
||||
</th>
|
||||
<th>
|
||||
Mg
|
||||
</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
<tr>
|
||||
<td
|
||||
class="ant-picker-cell"
|
||||
title="2017-09-25"
|
||||
>
|
||||
<div
|
||||
class="ant-picker-cell-inner"
|
||||
>
|
||||
25
|
||||
</div>
|
||||
</td>
|
||||
<td
|
||||
class="ant-picker-cell"
|
||||
title="2017-09-26"
|
||||
>
|
||||
<div
|
||||
class="ant-picker-cell-inner"
|
||||
>
|
||||
26
|
||||
</div>
|
||||
</td>
|
||||
<td
|
||||
class="ant-picker-cell"
|
||||
title="2017-09-27"
|
||||
>
|
||||
<div
|
||||
class="ant-picker-cell-inner"
|
||||
>
|
||||
27
|
||||
</div>
|
||||
</td>
|
||||
<td
|
||||
class="ant-picker-cell"
|
||||
title="2017-09-28"
|
||||
>
|
||||
<div
|
||||
class="ant-picker-cell-inner"
|
||||
>
|
||||
28
|
||||
</div>
|
||||
</td>
|
||||
<td
|
||||
class="ant-picker-cell"
|
||||
title="2017-09-29"
|
||||
>
|
||||
<div
|
||||
class="ant-picker-cell-inner"
|
||||
>
|
||||
29
|
||||
</div>
|
||||
</td>
|
||||
<td
|
||||
class="ant-picker-cell"
|
||||
title="2017-09-30"
|
||||
>
|
||||
<div
|
||||
class="ant-picker-cell-inner"
|
||||
>
|
||||
30
|
||||
</div>
|
||||
</td>
|
||||
<td
|
||||
class="ant-picker-cell ant-picker-cell-in-view"
|
||||
title="2017-10-01"
|
||||
@ -110398,8 +110338,6 @@ exports[`Locale Provider should display the text as id 1`] = `
|
||||
1
|
||||
</div>
|
||||
</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td
|
||||
class="ant-picker-cell ant-picker-cell-in-view"
|
||||
title="2017-10-02"
|
||||
@ -110460,6 +110398,8 @@ exports[`Locale Provider should display the text as id 1`] = `
|
||||
7
|
||||
</div>
|
||||
</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td
|
||||
class="ant-picker-cell ant-picker-cell-in-view"
|
||||
title="2017-10-08"
|
||||
@ -110470,8 +110410,6 @@ exports[`Locale Provider should display the text as id 1`] = `
|
||||
8
|
||||
</div>
|
||||
</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td
|
||||
class="ant-picker-cell ant-picker-cell-in-view"
|
||||
title="2017-10-09"
|
||||
@ -110532,6 +110470,8 @@ exports[`Locale Provider should display the text as id 1`] = `
|
||||
14
|
||||
</div>
|
||||
</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td
|
||||
class="ant-picker-cell ant-picker-cell-in-view"
|
||||
title="2017-10-15"
|
||||
@ -110542,8 +110482,6 @@ exports[`Locale Provider should display the text as id 1`] = `
|
||||
15
|
||||
</div>
|
||||
</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td
|
||||
class="ant-picker-cell ant-picker-cell-in-view"
|
||||
title="2017-10-16"
|
||||
@ -110604,6 +110542,8 @@ exports[`Locale Provider should display the text as id 1`] = `
|
||||
21
|
||||
</div>
|
||||
</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td
|
||||
class="ant-picker-cell ant-picker-cell-in-view"
|
||||
title="2017-10-22"
|
||||
@ -110614,8 +110554,6 @@ exports[`Locale Provider should display the text as id 1`] = `
|
||||
22
|
||||
</div>
|
||||
</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td
|
||||
class="ant-picker-cell ant-picker-cell-in-view"
|
||||
title="2017-10-23"
|
||||
@ -110676,6 +110614,8 @@ exports[`Locale Provider should display the text as id 1`] = `
|
||||
28
|
||||
</div>
|
||||
</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td
|
||||
class="ant-picker-cell ant-picker-cell-in-view"
|
||||
title="2017-10-29"
|
||||
@ -110686,8 +110626,6 @@ exports[`Locale Provider should display the text as id 1`] = `
|
||||
29
|
||||
</div>
|
||||
</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td
|
||||
class="ant-picker-cell ant-picker-cell-in-view"
|
||||
title="2017-10-30"
|
||||
@ -110748,6 +110686,8 @@ exports[`Locale Provider should display the text as id 1`] = `
|
||||
4
|
||||
</div>
|
||||
</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td
|
||||
class="ant-picker-cell"
|
||||
title="2017-11-05"
|
||||
@ -110758,6 +110698,66 @@ exports[`Locale Provider should display the text as id 1`] = `
|
||||
5
|
||||
</div>
|
||||
</td>
|
||||
<td
|
||||
class="ant-picker-cell"
|
||||
title="2017-11-06"
|
||||
>
|
||||
<div
|
||||
class="ant-picker-cell-inner"
|
||||
>
|
||||
6
|
||||
</div>
|
||||
</td>
|
||||
<td
|
||||
class="ant-picker-cell"
|
||||
title="2017-11-07"
|
||||
>
|
||||
<div
|
||||
class="ant-picker-cell-inner"
|
||||
>
|
||||
7
|
||||
</div>
|
||||
</td>
|
||||
<td
|
||||
class="ant-picker-cell"
|
||||
title="2017-11-08"
|
||||
>
|
||||
<div
|
||||
class="ant-picker-cell-inner"
|
||||
>
|
||||
8
|
||||
</div>
|
||||
</td>
|
||||
<td
|
||||
class="ant-picker-cell"
|
||||
title="2017-11-09"
|
||||
>
|
||||
<div
|
||||
class="ant-picker-cell-inner"
|
||||
>
|
||||
9
|
||||
</div>
|
||||
</td>
|
||||
<td
|
||||
class="ant-picker-cell"
|
||||
title="2017-11-10"
|
||||
>
|
||||
<div
|
||||
class="ant-picker-cell-inner"
|
||||
>
|
||||
10
|
||||
</div>
|
||||
</td>
|
||||
<td
|
||||
class="ant-picker-cell"
|
||||
title="2017-11-11"
|
||||
>
|
||||
<div
|
||||
class="ant-picker-cell-inner"
|
||||
>
|
||||
11
|
||||
</div>
|
||||
</td>
|
||||
</tr>
|
||||
</tbody>
|
||||
</table>
|
||||
@ -111373,6 +111373,9 @@ exports[`Locale Provider should display the text as id 1`] = `
|
||||
>
|
||||
<thead>
|
||||
<tr>
|
||||
<th>
|
||||
Mg
|
||||
</th>
|
||||
<th>
|
||||
Sn
|
||||
</th>
|
||||
@ -111391,13 +111394,27 @@ exports[`Locale Provider should display the text as id 1`] = `
|
||||
<th>
|
||||
Sb
|
||||
</th>
|
||||
<th>
|
||||
Mg
|
||||
</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
<tr>
|
||||
<td
|
||||
class="ant-picker-cell"
|
||||
title="2017-08-27"
|
||||
>
|
||||
<div
|
||||
class="ant-picker-cell-inner ant-picker-calendar-date"
|
||||
>
|
||||
<div
|
||||
class="ant-picker-calendar-date-value"
|
||||
>
|
||||
27
|
||||
</div>
|
||||
<div
|
||||
class="ant-picker-calendar-date-content"
|
||||
/>
|
||||
</div>
|
||||
</td>
|
||||
<td
|
||||
class="ant-picker-cell"
|
||||
title="2017-08-28"
|
||||
@ -111500,6 +111517,8 @@ exports[`Locale Provider should display the text as id 1`] = `
|
||||
/>
|
||||
</div>
|
||||
</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td
|
||||
class="ant-picker-cell ant-picker-cell-in-view"
|
||||
title="2017-09-03"
|
||||
@ -111517,8 +111536,6 @@ exports[`Locale Provider should display the text as id 1`] = `
|
||||
/>
|
||||
</div>
|
||||
</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td
|
||||
class="ant-picker-cell ant-picker-cell-in-view"
|
||||
title="2017-09-04"
|
||||
@ -111621,6 +111638,8 @@ exports[`Locale Provider should display the text as id 1`] = `
|
||||
/>
|
||||
</div>
|
||||
</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td
|
||||
class="ant-picker-cell ant-picker-cell-in-view"
|
||||
title="2017-09-10"
|
||||
@ -111638,8 +111657,6 @@ exports[`Locale Provider should display the text as id 1`] = `
|
||||
/>
|
||||
</div>
|
||||
</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td
|
||||
class="ant-picker-cell ant-picker-cell-in-view"
|
||||
title="2017-09-11"
|
||||
@ -111742,6 +111759,8 @@ exports[`Locale Provider should display the text as id 1`] = `
|
||||
/>
|
||||
</div>
|
||||
</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td
|
||||
class="ant-picker-cell ant-picker-cell-in-view"
|
||||
title="2017-09-17"
|
||||
@ -111759,8 +111778,6 @@ exports[`Locale Provider should display the text as id 1`] = `
|
||||
/>
|
||||
</div>
|
||||
</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td
|
||||
class="ant-picker-cell ant-picker-cell-in-view ant-picker-cell-today ant-picker-cell-selected"
|
||||
title="2017-09-18"
|
||||
@ -111863,6 +111880,8 @@ exports[`Locale Provider should display the text as id 1`] = `
|
||||
/>
|
||||
</div>
|
||||
</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td
|
||||
class="ant-picker-cell ant-picker-cell-in-view"
|
||||
title="2017-09-24"
|
||||
@ -111880,8 +111899,6 @@ exports[`Locale Provider should display the text as id 1`] = `
|
||||
/>
|
||||
</div>
|
||||
</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td
|
||||
class="ant-picker-cell ant-picker-cell-in-view"
|
||||
title="2017-09-25"
|
||||
@ -111984,6 +112001,8 @@ exports[`Locale Provider should display the text as id 1`] = `
|
||||
/>
|
||||
</div>
|
||||
</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td
|
||||
class="ant-picker-cell"
|
||||
title="2017-10-01"
|
||||
@ -112001,8 +112020,6 @@ exports[`Locale Provider should display the text as id 1`] = `
|
||||
/>
|
||||
</div>
|
||||
</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td
|
||||
class="ant-picker-cell"
|
||||
title="2017-10-02"
|
||||
@ -112105,23 +112122,6 @@ exports[`Locale Provider should display the text as id 1`] = `
|
||||
/>
|
||||
</div>
|
||||
</td>
|
||||
<td
|
||||
class="ant-picker-cell"
|
||||
title="2017-10-08"
|
||||
>
|
||||
<div
|
||||
class="ant-picker-cell-inner ant-picker-calendar-date"
|
||||
>
|
||||
<div
|
||||
class="ant-picker-calendar-date-value"
|
||||
>
|
||||
08
|
||||
</div>
|
||||
<div
|
||||
class="ant-picker-calendar-date-content"
|
||||
/>
|
||||
</div>
|
||||
</td>
|
||||
</tr>
|
||||
</tbody>
|
||||
</table>
|
||||
|
@ -174,7 +174,7 @@ describe('Locale Provider', () => {
|
||||
));
|
||||
|
||||
beforeAll(() => {
|
||||
MockDate.set(moment('2017-09-18T03:30:07.795'));
|
||||
MockDate.set(moment('2017-09-18T03:30:07.795').valueOf());
|
||||
});
|
||||
|
||||
afterAll(() => {
|
||||
|
@ -1,5 +1,4 @@
|
||||
import * as React from 'react';
|
||||
import * as ReactDOM from 'react-dom';
|
||||
import Button from '../button';
|
||||
import { LegacyButtonType, ButtonProps, convertLegacyProps } from '../button/button';
|
||||
|
||||
@ -11,40 +10,34 @@ export interface ActionButtonProps {
|
||||
buttonProps?: ButtonProps;
|
||||
}
|
||||
|
||||
export interface ActionButtonState {
|
||||
loading: ButtonProps['loading'];
|
||||
}
|
||||
const ActionButton: React.FC<ActionButtonProps> = props => {
|
||||
const clickedRef = React.useRef<boolean>(false);
|
||||
const ref = React.useRef<any>();
|
||||
const [loading, setLoading] = React.useState<ButtonProps['loading']>(false);
|
||||
|
||||
export default class ActionButton extends React.Component<ActionButtonProps, ActionButtonState> {
|
||||
timeoutId: number;
|
||||
|
||||
clicked: boolean;
|
||||
|
||||
state = {
|
||||
loading: false,
|
||||
};
|
||||
|
||||
componentDidMount() {
|
||||
if (this.props.autoFocus) {
|
||||
const $this = ReactDOM.findDOMNode(this) as HTMLInputElement;
|
||||
this.timeoutId = setTimeout(() => $this.focus());
|
||||
React.useEffect(() => {
|
||||
let timeoutId: number;
|
||||
if (props.autoFocus) {
|
||||
const $this = ref.current as HTMLInputElement;
|
||||
timeoutId = setTimeout(() => $this.focus());
|
||||
}
|
||||
}
|
||||
return () => {
|
||||
if (timeoutId) {
|
||||
clearTimeout(timeoutId);
|
||||
}
|
||||
};
|
||||
}, []);
|
||||
|
||||
componentWillUnmount() {
|
||||
clearTimeout(this.timeoutId);
|
||||
}
|
||||
|
||||
handlePromiseOnOk(returnValueOfOnOk?: PromiseLike<any>) {
|
||||
const { closeModal } = this.props;
|
||||
const handlePromiseOnOk = (returnValueOfOnOk?: PromiseLike<any>) => {
|
||||
const { closeModal } = props;
|
||||
if (!returnValueOfOnOk || !returnValueOfOnOk.then) {
|
||||
return;
|
||||
}
|
||||
this.setState({ loading: true });
|
||||
setLoading(true);
|
||||
returnValueOfOnOk.then(
|
||||
(...args: any[]) => {
|
||||
// It's unnecessary to set loading=false, for the Modal will be unmounted after close.
|
||||
// this.setState({ loading: false });
|
||||
// setState({ loading: false });
|
||||
closeModal(...args);
|
||||
},
|
||||
(e: Error) => {
|
||||
@ -52,18 +45,18 @@ export default class ActionButton extends React.Component<ActionButtonProps, Act
|
||||
// eslint-disable-next-line no-console
|
||||
console.error(e);
|
||||
// See: https://github.com/ant-design/ant-design/issues/6183
|
||||
this.setState({ loading: false });
|
||||
this.clicked = false;
|
||||
setLoading(false);
|
||||
clickedRef.current = false;
|
||||
},
|
||||
);
|
||||
}
|
||||
};
|
||||
|
||||
onClick = () => {
|
||||
const { actionFn, closeModal } = this.props;
|
||||
if (this.clicked) {
|
||||
const onClick = () => {
|
||||
const { actionFn, closeModal } = props;
|
||||
if (clickedRef.current) {
|
||||
return;
|
||||
}
|
||||
this.clicked = true;
|
||||
clickedRef.current = true;
|
||||
if (!actionFn) {
|
||||
closeModal();
|
||||
return;
|
||||
@ -72,7 +65,7 @@ export default class ActionButton extends React.Component<ActionButtonProps, Act
|
||||
if (actionFn.length) {
|
||||
returnValueOfOnOk = actionFn(closeModal);
|
||||
// https://github.com/ant-design/ant-design/issues/23358
|
||||
this.clicked = false;
|
||||
clickedRef.current = false;
|
||||
} else {
|
||||
returnValueOfOnOk = actionFn();
|
||||
if (!returnValueOfOnOk) {
|
||||
@ -80,21 +73,21 @@ export default class ActionButton extends React.Component<ActionButtonProps, Act
|
||||
return;
|
||||
}
|
||||
}
|
||||
this.handlePromiseOnOk(returnValueOfOnOk);
|
||||
handlePromiseOnOk(returnValueOfOnOk);
|
||||
};
|
||||
|
||||
render() {
|
||||
const { type, children, buttonProps } = this.props;
|
||||
const { loading } = this.state;
|
||||
return (
|
||||
<Button
|
||||
{...convertLegacyProps(type)}
|
||||
onClick={this.onClick}
|
||||
loading={loading}
|
||||
{...buttonProps}
|
||||
>
|
||||
{children}
|
||||
</Button>
|
||||
);
|
||||
}
|
||||
}
|
||||
const { type, children, buttonProps } = props;
|
||||
return (
|
||||
<Button
|
||||
{...convertLegacyProps(type)}
|
||||
onClick={onClick}
|
||||
loading={loading}
|
||||
{...buttonProps}
|
||||
ref={ref}
|
||||
>
|
||||
{children}
|
||||
</Button>
|
||||
);
|
||||
};
|
||||
|
||||
export default ActionButton;
|
||||
|
@ -9,7 +9,7 @@ import { getConfirmLocale } from './locale';
|
||||
import Button from '../button';
|
||||
import { LegacyButtonType, ButtonProps, convertLegacyProps } from '../button/button';
|
||||
import LocaleReceiver from '../locale-provider/LocaleReceiver';
|
||||
import { ConfigConsumer, ConfigConsumerProps } from '../config-provider';
|
||||
import { ConfigContext } from '../config-provider';
|
||||
|
||||
let mousePosition: { x: number; y: number } | null;
|
||||
export const destroyFns: Array<() => void> = [];
|
||||
@ -119,46 +119,41 @@ export interface ModalLocale {
|
||||
justOkText: string;
|
||||
}
|
||||
|
||||
export default class Modal extends React.Component<ModalProps, {}> {
|
||||
static destroyAll: () => void;
|
||||
interface ModalInterface extends React.FC<ModalProps> {
|
||||
useModal: typeof useModal;
|
||||
}
|
||||
|
||||
static useModal = useModal;
|
||||
const Modal: ModalInterface = props => {
|
||||
const { getPopupContainer: getContextPopupContainer, getPrefixCls, direction } = React.useContext(
|
||||
ConfigContext,
|
||||
);
|
||||
|
||||
static defaultProps = {
|
||||
width: 520,
|
||||
transitionName: 'zoom',
|
||||
maskTransitionName: 'fade',
|
||||
confirmLoading: false,
|
||||
visible: false,
|
||||
okType: 'primary' as LegacyButtonType,
|
||||
};
|
||||
|
||||
handleCancel = (e: React.MouseEvent<HTMLButtonElement>) => {
|
||||
const { onCancel } = this.props;
|
||||
const handleCancel = (e: React.MouseEvent<HTMLButtonElement>) => {
|
||||
const { onCancel } = props;
|
||||
if (onCancel) {
|
||||
onCancel(e);
|
||||
}
|
||||
};
|
||||
|
||||
handleOk = (e: React.MouseEvent<HTMLButtonElement>) => {
|
||||
const { onOk } = this.props;
|
||||
const handleOk = (e: React.MouseEvent<HTMLButtonElement>) => {
|
||||
const { onOk } = props;
|
||||
if (onOk) {
|
||||
onOk(e);
|
||||
}
|
||||
};
|
||||
|
||||
renderFooter = (locale: ModalLocale) => {
|
||||
const { okText, okType, cancelText, confirmLoading } = this.props;
|
||||
const renderFooter = (locale: ModalLocale) => {
|
||||
const { okText, okType, cancelText, confirmLoading } = props;
|
||||
return (
|
||||
<>
|
||||
<Button onClick={this.handleCancel} {...this.props.cancelButtonProps}>
|
||||
<Button onClick={handleCancel} {...props.cancelButtonProps}>
|
||||
{cancelText || locale.cancelText}
|
||||
</Button>
|
||||
<Button
|
||||
{...convertLegacyProps(okType)}
|
||||
loading={confirmLoading}
|
||||
onClick={this.handleOk}
|
||||
{...this.props.okButtonProps}
|
||||
onClick={handleOk}
|
||||
{...props.okButtonProps}
|
||||
>
|
||||
{okText || locale.okText}
|
||||
</Button>
|
||||
@ -166,54 +161,58 @@ export default class Modal extends React.Component<ModalProps, {}> {
|
||||
);
|
||||
};
|
||||
|
||||
renderModal = ({
|
||||
getPopupContainer: getContextPopupContainer,
|
||||
getPrefixCls,
|
||||
direction,
|
||||
}: ConfigConsumerProps) => {
|
||||
const {
|
||||
prefixCls: customizePrefixCls,
|
||||
footer,
|
||||
visible,
|
||||
wrapClassName,
|
||||
centered,
|
||||
getContainer,
|
||||
closeIcon,
|
||||
...restProps
|
||||
} = this.props;
|
||||
const {
|
||||
prefixCls: customizePrefixCls,
|
||||
footer,
|
||||
visible,
|
||||
wrapClassName,
|
||||
centered,
|
||||
getContainer,
|
||||
closeIcon,
|
||||
...restProps
|
||||
} = props;
|
||||
|
||||
const prefixCls = getPrefixCls('modal', customizePrefixCls);
|
||||
const defaultFooter = (
|
||||
<LocaleReceiver componentName="Modal" defaultLocale={getConfirmLocale()}>
|
||||
{this.renderFooter}
|
||||
</LocaleReceiver>
|
||||
);
|
||||
const prefixCls = getPrefixCls('modal', customizePrefixCls);
|
||||
const defaultFooter = (
|
||||
<LocaleReceiver componentName="Modal" defaultLocale={getConfirmLocale()}>
|
||||
{renderFooter}
|
||||
</LocaleReceiver>
|
||||
);
|
||||
|
||||
const closeIconToRender = (
|
||||
<span className={`${prefixCls}-close-x`}>
|
||||
{closeIcon || <CloseOutlined className={`${prefixCls}-close-icon`} />}
|
||||
</span>
|
||||
);
|
||||
const wrapClassNameExtended = classNames(wrapClassName, {
|
||||
[`${prefixCls}-centered`]: !!centered,
|
||||
[`${prefixCls}-wrap-rtl`]: direction === 'rtl',
|
||||
});
|
||||
return (
|
||||
<Dialog
|
||||
{...restProps}
|
||||
getContainer={getContainer === undefined ? getContextPopupContainer : getContainer}
|
||||
prefixCls={prefixCls}
|
||||
wrapClassName={wrapClassNameExtended}
|
||||
footer={footer === undefined ? defaultFooter : footer}
|
||||
visible={visible}
|
||||
mousePosition={mousePosition}
|
||||
onClose={this.handleCancel}
|
||||
closeIcon={closeIconToRender}
|
||||
/>
|
||||
);
|
||||
};
|
||||
const closeIconToRender = (
|
||||
<span className={`${prefixCls}-close-x`}>
|
||||
{closeIcon || <CloseOutlined className={`${prefixCls}-close-icon`} />}
|
||||
</span>
|
||||
);
|
||||
|
||||
render() {
|
||||
return <ConfigConsumer>{this.renderModal}</ConfigConsumer>;
|
||||
}
|
||||
}
|
||||
const wrapClassNameExtended = classNames(wrapClassName, {
|
||||
[`${prefixCls}-centered`]: !!centered,
|
||||
[`${prefixCls}-wrap-rtl`]: direction === 'rtl',
|
||||
});
|
||||
return (
|
||||
<Dialog
|
||||
{...restProps}
|
||||
getContainer={getContainer === undefined ? getContextPopupContainer : getContainer}
|
||||
prefixCls={prefixCls}
|
||||
wrapClassName={wrapClassNameExtended}
|
||||
footer={footer === undefined ? defaultFooter : footer}
|
||||
visible={visible}
|
||||
mousePosition={mousePosition}
|
||||
onClose={handleCancel}
|
||||
closeIcon={closeIconToRender}
|
||||
/>
|
||||
);
|
||||
};
|
||||
|
||||
Modal.useModal = useModal;
|
||||
|
||||
Modal.defaultProps = {
|
||||
width: 520,
|
||||
transitionName: 'zoom',
|
||||
maskTransitionName: 'fade',
|
||||
confirmLoading: false,
|
||||
visible: false,
|
||||
okType: 'primary' as LegacyButtonType,
|
||||
};
|
||||
|
||||
export default Modal;
|
||||
|
@ -52,15 +52,15 @@ describe('Modal', () => {
|
||||
|
||||
it('onCancel should be called', () => {
|
||||
const onCancel = jest.fn();
|
||||
const wrapper = mount(<Modal onCancel={onCancel} />).instance();
|
||||
wrapper.handleCancel();
|
||||
const wrapper = mount(<Modal visible onCancel={onCancel} />);
|
||||
wrapper.find('.ant-btn').first().simulate('click');
|
||||
expect(onCancel).toHaveBeenCalled();
|
||||
});
|
||||
|
||||
it('onOk should be called', () => {
|
||||
const onOk = jest.fn();
|
||||
const wrapper = mount(<Modal onOk={onOk} />).instance();
|
||||
wrapper.handleOk();
|
||||
const wrapper = mount(<Modal visible onOk={onOk} />);
|
||||
wrapper.find('.ant-btn').last().simulate('click');
|
||||
expect(onOk).toHaveBeenCalled();
|
||||
});
|
||||
|
||||
|
@ -30,8 +30,8 @@ When requiring users to interact with the application, but without jumping to a
|
||||
| maskStyle | Style for modal's mask element. | object | {} |
|
||||
| okText | Text of the OK button | string\|ReactNode | `OK` |
|
||||
| okType | Button `type` of the OK button | string | `primary` |
|
||||
| okButtonProps | The ok button props | [ButtonProps](/components/button) | - |
|
||||
| cancelButtonProps | The cancel button props | [ButtonProps](/components/button) | - |
|
||||
| okButtonProps | The ok button props | [ButtonProps](/components/button/#API) | - |
|
||||
| cancelButtonProps | The cancel button props | [ButtonProps](/components/button/#API) | - |
|
||||
| style | Style of floating layer, typically used at least for adjusting the position. | CSSProperties | - |
|
||||
| title | The modal dialog's title | string\|ReactNode | - |
|
||||
| visible | Whether the modal dialog is visible or not | boolean | false |
|
||||
@ -57,26 +57,26 @@ There are five ways to display the information based on the content's nature:
|
||||
|
||||
The items listed above are all functions, expecting a settings object as parameter. The properties of the object are follows:
|
||||
|
||||
| Property | Description | Type | Default |
|
||||
| --- | --- | --- | --- |
|
||||
| autoFocusButton | Specify which button to autofocus | null\| `ok` \| `cancel` | `ok` |
|
||||
| cancelText | Text of the Cancel button with Modal.confirm | string | `Cancel` |
|
||||
| centered | Centered Modal | Boolean | `false` |
|
||||
| className | className of container | string | - |
|
||||
| content | Content | string\|ReactNode | - |
|
||||
| icon | custom icon (`Added in 3.12.0`) | ReactNode | `<QuestionCircle />` |
|
||||
| keyboard | Whether support press esc to close | Boolean | true |
|
||||
| mask | Whether show mask or not. | Boolean | true |
|
||||
| maskClosable | Whether to close the modal dialog when the mask (area outside the modal) is clicked | Boolean | `false` |
|
||||
| okText | Text of the OK button | string | `OK` |
|
||||
| okType | Button `type` of the OK button | string | `primary` |
|
||||
| okButtonProps | The ok button props | [ButtonProps](/components/button) | - |
|
||||
| cancelButtonProps | The cancel button props | [ButtonProps](/components/button) | - |
|
||||
| title | Title | string\|ReactNode | - |
|
||||
| width | Width of the modal dialog | string\|number | 416 |
|
||||
| zIndex | The `z-index` of the Modal | Number | 1000 |
|
||||
| onCancel | Specify a function that will be called when the user clicks the Cancel button. The parameter of this function is a function whose execution should include closing the dialog. You can also just return a promise and when the promise is resolved, the modal dialog will also be closed | function(close) | - |
|
||||
| onOk | Specify a function that will be called when the user clicks the OK button. The parameter of this function is a function whose execution should include closing the dialog. You can also just return a promise and when the promise is resolved, the modal dialog will also be closed | function(close) | - |
|
||||
| Property | Description | Type | Default | Version |
|
||||
| --- | --- | --- | --- | --- |
|
||||
| autoFocusButton | Specify which button to autofocus | null\| `ok` \| `cancel` | `ok` | |
|
||||
| cancelText | Text of the Cancel button with Modal.confirm | string | `Cancel` | |
|
||||
| centered | Centered Modal | Boolean | `false` | |
|
||||
| className | className of container | string | - | |
|
||||
| content | Content | string\|ReactNode | - | |
|
||||
| icon | custom icon | ReactNode | [<QuestionCircle /\>](/components/icon/) | 3.12.0 |
|
||||
| keyboard | Whether support press esc to close | Boolean | true | |
|
||||
| mask | Whether show mask or not. | Boolean | true | |
|
||||
| maskClosable | Whether to close the modal dialog when the mask (area outside the modal) is clicked | Boolean | `false` | |
|
||||
| okText | Text of the OK button | string | `OK` | |
|
||||
| okType | Button `type` of the OK button | string | `primary` | |
|
||||
| okButtonProps | The ok button props | [ButtonProps](/components/button/#API) | - | |
|
||||
| cancelButtonProps | The cancel button props | [ButtonProps](/components/button/#API) | - | |
|
||||
| title | Title | string\|ReactNode | - | |
|
||||
| width | Width of the modal dialog | string\|number | 416 | |
|
||||
| zIndex | The `z-index` of the Modal | Number | 1000 | |
|
||||
| onCancel | Specify a function that will be called when the user clicks the Cancel button. The parameter of this function is a function whose execution should include closing the dialog. You can also just return a promise and when the promise is resolved, the modal dialog will also be closed | function(close) | - | |
|
||||
| onOk | Specify a function that will be called when the user clicks the OK button. The parameter of this function is a function whose execution should include closing the dialog. You can also just return a promise and when the promise is resolved, the modal dialog will also be closed | function(close) | - | |
|
||||
|
||||
All the `Modal.method`s will return a reference, and then we can update and close the modal dialog by the reference.
|
||||
|
||||
|
@ -15,7 +15,7 @@ function modalWarn(props: ModalFuncProps) {
|
||||
return confirm(withWarn(props));
|
||||
}
|
||||
|
||||
type Modal = typeof OriginModal & ModalStaticFunctions;
|
||||
type Modal = typeof OriginModal & ModalStaticFunctions & { destroyAll: () => void };
|
||||
const Modal = OriginModal as Modal;
|
||||
|
||||
Modal.info = function infoFn(props: ModalFuncProps) {
|
||||
|
@ -34,8 +34,8 @@ title: Modal
|
||||
| maskStyle | 遮罩样式 | object | {} |
|
||||
| okText | 确认按钮文字 | string\|ReactNode | 确定 |
|
||||
| okType | 确认按钮类型 | string | primary |
|
||||
| okButtonProps | ok 按钮 props | [ButtonProps](/components/button) | - |
|
||||
| cancelButtonProps | cancel 按钮 props | [ButtonProps](/components/button) | - |
|
||||
| okButtonProps | ok 按钮 props | [ButtonProps](/components/button/#API) | - |
|
||||
| cancelButtonProps | cancel 按钮 props | [ButtonProps](/components/button/#API) | - |
|
||||
| style | 可用于设置浮层的样式,调整浮层位置等 | CSSProperties | - |
|
||||
| title | 标题 | string\|ReactNode | - |
|
||||
| visible | 对话框是否可见 | boolean | - |
|
||||
@ -61,24 +61,24 @@ title: Modal
|
||||
|
||||
以上均为一个函数,参数为 object,具体属性如下:
|
||||
|
||||
| 参数 | 说明 | 类型 | 默认值 |
|
||||
| --- | --- | --- | --- |
|
||||
| autoFocusButton | 指定自动获得焦点的按钮 | null\| `ok` \| `cancel` | `ok` |
|
||||
| cancelText | 设置 Modal.confirm 取消按钮文字 | string | 取消 |
|
||||
| centered | 垂直居中展示 Modal | Boolean | `false` |
|
||||
| className | 容器类名 | string | - |
|
||||
| content | 内容 | string\|ReactNode | - |
|
||||
| icon | 自定义图标(3.12.0 新增) | ReactNode | `<QuestionCircle />` |
|
||||
| maskClosable | 点击蒙层是否允许关闭 | Boolean | `false` |
|
||||
| okText | 确认按钮文字 | string | 确定 |
|
||||
| okType | 确认按钮类型 | string | primary |
|
||||
| okButtonProps | ok 按钮 props | [ButtonProps](/components/button) | - |
|
||||
| cancelButtonProps | cancel 按钮 props | [ButtonProps](/components/button) | - |
|
||||
| title | 标题 | string\|ReactNode | - |
|
||||
| width | 宽度 | string\|number | 416 |
|
||||
| zIndex | 设置 Modal 的 `z-index` | Number | 1000 |
|
||||
| onCancel | 取消回调,参数为关闭函数,返回 promise 时 resolve 后自动关闭 | function(close) | - |
|
||||
| onOk | 点击确定回调,参数为关闭函数,返回 promise 时 resolve 后自动关闭 | function(close) | - |
|
||||
| 参数 | 说明 | 类型 | 默认值 | 版本 |
|
||||
| --- | --- | --- | --- | --- |
|
||||
| autoFocusButton | 指定自动获得焦点的按钮 | null\| `ok` \| `cancel` | `ok` | |
|
||||
| cancelText | 设置 Modal.confirm 取消按钮文字 | string | 取消 | |
|
||||
| centered | 垂直居中展示 Modal | Boolean | `false` | |
|
||||
| className | 容器类名 | string | - | |
|
||||
| content | 内容 | string\|ReactNode | - | |
|
||||
| icon | 自定义图标 | ReactNode | [<QuestionCircle /\>](/components/icon/) | 3.12.0 |
|
||||
| maskClosable | 点击蒙层是否允许关闭 | Boolean | `false` | |
|
||||
| okText | 确认按钮文字 | string | 确定 | |
|
||||
| okType | 确认按钮类型 | string | primary | |
|
||||
| okButtonProps | ok 按钮 props | [ButtonProps](/components/button/#API) | - | |
|
||||
| cancelButtonProps | cancel 按钮 props | [ButtonProps](/components/button/#API) | - | |
|
||||
| title | 标题 | string\|ReactNode | - | |
|
||||
| width | 宽度 | string\|number | 416 | |
|
||||
| zIndex | 设置 Modal 的 `z-index` | Number | 1000 | |
|
||||
| onCancel | 取消回调,参数为关闭函数,返回 promise 时 resolve 后自动关闭 | function(close) | - | |
|
||||
| onOk | 点击确定回调,参数为关闭函数,返回 promise 时 resolve 后自动关闭 | function(close) | - | |
|
||||
|
||||
以上函数调用后,会返回一个引用,可以通过该引用更新和关闭弹窗。
|
||||
|
||||
|
@ -50,4 +50,23 @@ describe('notification.hooks', () => {
|
||||
expect(document.querySelectorAll('.my-test-notification-notice').length).toBe(1);
|
||||
expect(document.querySelector('.hook-test-result').innerHTML).toEqual('bamboo');
|
||||
});
|
||||
|
||||
it('should be same hook', () => {
|
||||
let count = 0;
|
||||
|
||||
const Demo = () => {
|
||||
const [, forceUpdate] = React.useState({});
|
||||
const [api] = notification.useNotification();
|
||||
|
||||
React.useEffect(() => {
|
||||
count += 1;
|
||||
expect(count).toEqual(1);
|
||||
forceUpdate();
|
||||
}, [api]);
|
||||
|
||||
return null;
|
||||
};
|
||||
|
||||
mount(<Demo />);
|
||||
});
|
||||
});
|
||||
|
@ -46,19 +46,20 @@ export default function createUseNotification(
|
||||
}
|
||||
|
||||
// Fill functions
|
||||
const hookAPI: any = {
|
||||
open: notify,
|
||||
};
|
||||
const hookApiRef = React.useRef<any>({});
|
||||
|
||||
hookApiRef.current.open = notify;
|
||||
|
||||
['success', 'info', 'warning', 'error'].forEach(type => {
|
||||
hookAPI[type] = (args: ArgsProps) =>
|
||||
hookAPI.open({
|
||||
hookApiRef.current[type] = (args: ArgsProps) =>
|
||||
hookApiRef.current.open({
|
||||
...args,
|
||||
type,
|
||||
});
|
||||
});
|
||||
|
||||
return [
|
||||
hookAPI,
|
||||
hookApiRef.current,
|
||||
<ConfigConsumer key="holder">
|
||||
{(context: ConfigConsumerProps) => {
|
||||
({ getPrefixCls } = context);
|
||||
|
@ -14,7 +14,7 @@ describe('Statistic', () => {
|
||||
rtlTest(Statistic);
|
||||
|
||||
beforeAll(() => {
|
||||
MockDate.set(moment('2018-11-28 00:00:00'));
|
||||
MockDate.set(moment('2018-11-28 00:00:00').valueOf());
|
||||
});
|
||||
|
||||
afterAll(() => {
|
||||
@ -102,7 +102,7 @@ describe('Statistic', () => {
|
||||
const wrapper = mount(<Statistic.Countdown value={now} onFinish={onFinish} />);
|
||||
wrapper.update();
|
||||
|
||||
MockDate.set(moment('2019-11-28 00:00:00'));
|
||||
MockDate.set(moment('2019-11-28 00:00:00').valueOf());
|
||||
jest.runAllTimers();
|
||||
|
||||
expect(onFinish).toHaveBeenCalled();
|
||||
|
@ -299,7 +299,6 @@
|
||||
|
||||
// Grid system
|
||||
@grid-columns: 24;
|
||||
@grid-gutter-width: 0;
|
||||
|
||||
// Layout
|
||||
@layout-body-background: #f0f2f5;
|
||||
|
@ -797,4 +797,28 @@ describe('Table.rowSelection', () => {
|
||||
.simulate('change', { target: { checked: true } });
|
||||
expect(onChange.mock.calls[0][1]).toEqual([expect.objectContaining({ name: 'bamboo' })]);
|
||||
});
|
||||
|
||||
it('do not cache selected keys', () => {
|
||||
const onChange = jest.fn();
|
||||
const wrapper = mount(
|
||||
<Table
|
||||
dataSource={[{ name: 'light' }, { name: 'bamboo' }]}
|
||||
rowSelection={{ onChange }}
|
||||
rowKey="name"
|
||||
/>,
|
||||
);
|
||||
|
||||
wrapper
|
||||
.find('tbody input')
|
||||
.first()
|
||||
.simulate('change', { target: { checked: true } });
|
||||
expect(onChange).toHaveBeenCalledWith(['light'], [{ name: 'light' }]);
|
||||
|
||||
wrapper.setProps({ dataSource: [{ name: 'bamboo' }] });
|
||||
wrapper
|
||||
.find('tbody input')
|
||||
.first()
|
||||
.simulate('change', { target: { checked: true } });
|
||||
expect(onChange).toHaveBeenCalledWith(['bamboo'], [{ name: 'bamboo' }]);
|
||||
});
|
||||
});
|
||||
|
@ -117,12 +117,21 @@ export default function useSelection<RecordType>(
|
||||
|
||||
const setSelectedKeys = React.useCallback(
|
||||
(keys: Key[]) => {
|
||||
setInnerSelectedKeys(keys);
|
||||
const availableKeys: Key[] = [];
|
||||
const records: RecordType[] = [];
|
||||
|
||||
const records = keys.map(key => getRecordByKey(key));
|
||||
keys.forEach(key => {
|
||||
const record = getRecordByKey(key);
|
||||
if (record !== undefined) {
|
||||
availableKeys.push(key);
|
||||
records.push(record);
|
||||
}
|
||||
});
|
||||
|
||||
setInnerSelectedKeys(availableKeys);
|
||||
|
||||
if (onSelectionChange) {
|
||||
onSelectionChange(keys, records);
|
||||
onSelectionChange(availableKeys, records);
|
||||
}
|
||||
},
|
||||
[setInnerSelectedKeys, getRecordByKey, onSelectionChange],
|
||||
|
@ -72,7 +72,7 @@ class Upload extends React.Component<UploadProps, UploadState> {
|
||||
this.clearProgressTimer();
|
||||
}
|
||||
|
||||
saveUpload = (node: typeof RcUpload) => {
|
||||
saveUpload = (node: any) => {
|
||||
this.upload = node;
|
||||
};
|
||||
|
||||
|
@ -50,6 +50,10 @@ toc: false
|
||||
- https://gw.alipayobjects.com/zos/basement_prod/7b9ed3f2-6f05-4ddb-bac3-d55feb71e0ac.svg
|
||||
- 在 Figma 使用 Ant Design 进行设计
|
||||
- https://www.antforfigma.com
|
||||
- Figma 开源组件包
|
||||
- https://i.imgur.com/XoLEWxA.png
|
||||
- 代码级精确度的免费开源 Figma 完全组件库
|
||||
- https://www.figma.com/community/file/831698976089873405
|
||||
- 全新 Chart 组件包
|
||||
- https://gw.alipayobjects.com/zos/basement_prod/a9dc586a-fe0a-4c7d-ab4f-f5ed779b963d.svg
|
||||
- 桌面组件 Chart 模板包
|
||||
|
111
netlify.toml
111
netlify.toml
@ -1,111 +0,0 @@
|
||||
[build]
|
||||
publish = "_site"
|
||||
command = "npm run site"
|
||||
|
||||
[[redirects]]
|
||||
from = "/docs/resource/download"
|
||||
to = "/docs/spec/download"
|
||||
status = 301
|
||||
force = false
|
||||
|
||||
[[redirects]]
|
||||
from = "/docs/resource/download-cn"
|
||||
to = "/docs/spec/download-cn"
|
||||
status = 301
|
||||
force = false
|
||||
|
||||
[[redirects]]
|
||||
from = "/docs/resource/reference"
|
||||
to = "/docs/spec/reference"
|
||||
status = 301
|
||||
force = false
|
||||
|
||||
[[redirects]]
|
||||
from = "/docs/resource/reference-cn"
|
||||
to = "/docs/spec/reference-cn"
|
||||
status = 301
|
||||
force = false
|
||||
|
||||
[[redirects]]
|
||||
from = "/docs/spec/feature"
|
||||
to = "/docs/spec/values"
|
||||
status = 301
|
||||
force = false
|
||||
|
||||
[[redirects]]
|
||||
from = "/docs/spec/feature-cn"
|
||||
to = "/docs/spec/values-cn"
|
||||
status = 301
|
||||
force = false
|
||||
|
||||
[[redirects]]
|
||||
from = "/docs/pattern/advanced-search"
|
||||
to = "/docs/spec/overview"
|
||||
status = 301
|
||||
force = false
|
||||
|
||||
[[redirects]]
|
||||
from = "/docs/pattern/advanced-search-cn"
|
||||
to = "/docs/spec/overview-cn"
|
||||
status = 301
|
||||
force = false
|
||||
|
||||
[[redirects]]
|
||||
from = "/docs/pattern/complex-table"
|
||||
to = "/docs/spec/overview"
|
||||
status = 301
|
||||
force = false
|
||||
|
||||
[[redirects]]
|
||||
from = "/docs/pattern/complex-table-cn"
|
||||
to = "/docs/spec/overview-cn"
|
||||
status = 301
|
||||
force = false
|
||||
|
||||
[[redirects]]
|
||||
from = "/docs/pattern/form"
|
||||
to = "/docs/spec/overview"
|
||||
status = 301
|
||||
force = false
|
||||
|
||||
[[redirects]]
|
||||
from = "/docs/pattern/form-cn"
|
||||
to = "/docs/spec/overview-cn"
|
||||
status = 301
|
||||
force = false
|
||||
|
||||
[[redirects]]
|
||||
from = "/docs/pattern/list"
|
||||
to = "/docs/spec/overview"
|
||||
status = 301
|
||||
force = false
|
||||
|
||||
[[redirects]]
|
||||
from = "/docs/pattern/list-cn"
|
||||
to = "/docs/spec/overview-cn"
|
||||
status = 301
|
||||
force = false
|
||||
|
||||
[[redirects]]
|
||||
from = "/docs/pattern/navigation"
|
||||
to = "/docs/spec/overview"
|
||||
status = 301
|
||||
force = false
|
||||
|
||||
[[redirects]]
|
||||
from = "/docs/pattern/navigation-cn"
|
||||
to = "/docs/spec/overview-cn"
|
||||
status = 301
|
||||
force = false
|
||||
|
||||
[[redirects]]
|
||||
from = "/docs/pattern/table"
|
||||
to = "/docs/spec/overview"
|
||||
status = 301
|
||||
force = false
|
||||
|
||||
[[redirects]]
|
||||
from = "/docs/pattern/table-cn"
|
||||
to = "/docs/spec/overview-cn"
|
||||
status = 301
|
||||
force = false
|
16
package.json
16
package.json
@ -62,7 +62,7 @@
|
||||
"deploy": "bisheng gh-pages --push-only --dotfiles",
|
||||
"deploy:china-mirror": "git checkout gh-pages && git pull origin gh-pages && git push git@gitee.com:ant-design/ant-design.git gh-pages",
|
||||
"dist": "antd-tools run dist",
|
||||
"lint": "npm run lint:tsc && npm run lint:script && npm run lint:demo && npm run lint:style && npm run lint:deps && npm run lint:md",
|
||||
"lint": "npm run tsc && npm run lint:script && npm run lint:demo && npm run lint:style && npm run lint:deps && npm run lint:md",
|
||||
"lint-fix": "npm run lint-fix:script && npm run lint-fix:demo && npm run lint-fix:style",
|
||||
"lint-fix:demo": "eslint-tinker ./components/*/demo/*.md",
|
||||
"lint-fix:script": "npm run lint:script -- --fix",
|
||||
@ -72,7 +72,6 @@
|
||||
"lint:md": "remark . -f -q",
|
||||
"lint:script": "eslint . --ext '.js,.jsx,.ts,.tsx'",
|
||||
"lint:style": "stylelint '{site,components}/**/*.less' --syntax less",
|
||||
"lint:tsc": "npm run tsc -- --noEmit",
|
||||
"pre-publish": "npm run check-commit && npm run test-all",
|
||||
"prettier": "prettier -c --write '**/*'",
|
||||
"pretty-quick": "pretty-quick",
|
||||
@ -87,8 +86,9 @@
|
||||
"test:update": "jest --config .jest.js --no-cache --update-snapshot",
|
||||
"test-all": "./scripts/test-all.sh",
|
||||
"test-node": "jest --config .jest.node.js --no-cache",
|
||||
"tsc": "tsc",
|
||||
"tsc": "tsc --noEmit",
|
||||
"site:test": "jest --config .jest.site.js --cache=false",
|
||||
"test:image": "npm install puppeteer@2.1.1 --no-save && jest --config .jest.image.js --no-cache",
|
||||
"version": "node ./scripts/generate-version"
|
||||
},
|
||||
"husky": {
|
||||
@ -110,7 +110,7 @@
|
||||
"classnames": "~2.2.6",
|
||||
"copy-to-clipboard": "^3.2.0",
|
||||
"lodash": "^4.17.13",
|
||||
"moment": "~2.25.3",
|
||||
"moment": "^2.25.3",
|
||||
"omit.js": "^1.0.2",
|
||||
"prop-types": "^15.7.2",
|
||||
"raf": "^3.4.1",
|
||||
@ -159,8 +159,10 @@
|
||||
"@types/enzyme": "^3.10.5",
|
||||
"@types/gtag.js": "^0.0.3",
|
||||
"@types/jest": "^25.1.0",
|
||||
"@types/jest-image-snapshot": "^3.1.0",
|
||||
"@types/lodash": "^4.14.139",
|
||||
"@types/prop-types": "^15.7.1",
|
||||
"@types/puppeteer": "^3.0.0",
|
||||
"@types/raf": "^3.4.0",
|
||||
"@types/react": "^16.9.21",
|
||||
"@types/react-color": "^3.0.1",
|
||||
@ -213,6 +215,8 @@
|
||||
"inquirer": "^7.1.0",
|
||||
"intersection-observer": "^0.10.0",
|
||||
"jest": "^26.0.0",
|
||||
"jest-image-snapshot": "^4.0.0",
|
||||
"jest-stare": "^2.0.1",
|
||||
"jquery": "^3.4.1",
|
||||
"jsdom": "^16.0.0",
|
||||
"jsonml.js": "^0.1.0",
|
||||
@ -220,7 +224,7 @@
|
||||
"logrocket": "^1.0.0",
|
||||
"logrocket-react": "^4.0.0",
|
||||
"lz-string": "^1.4.4",
|
||||
"mockdate": "^2.0.2",
|
||||
"mockdate": "^3.0.0",
|
||||
"node-fetch": "^2.6.0",
|
||||
"open": "^7.0.3",
|
||||
"preact": "^10.0.0",
|
||||
@ -228,7 +232,7 @@
|
||||
"prettier": "^2.0.1",
|
||||
"pretty-quick": "^2.0.0",
|
||||
"querystring": "^0.2.0",
|
||||
"rc-footer": "^0.6.0",
|
||||
"rc-footer": "^0.6.3",
|
||||
"rc-queue-anim": "^1.6.12",
|
||||
"rc-scroll-anim": "^2.5.8",
|
||||
"rc-tween-one": "^2.4.1",
|
||||
|
12
tests/index.html
Normal file
12
tests/index.html
Normal file
@ -0,0 +1,12 @@
|
||||
<!DOCTYPE html>
|
||||
<html lang="en">
|
||||
<head>
|
||||
<meta charset="UTF-8" />
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
|
||||
<title>Amazing Antd</title>
|
||||
<link rel="stylesheet" href="antd.css" />
|
||||
</head>
|
||||
<body>
|
||||
<div id="root"></div>
|
||||
</body>
|
||||
</html>
|
@ -53,7 +53,7 @@ export default function demoTest(component: string, options: Options = {}) {
|
||||
testMethod = test.skip;
|
||||
}
|
||||
testMethod(`renders ${file} correctly`, () => {
|
||||
MockDate.set(moment('2016-11-22').toDate());
|
||||
MockDate.set(moment('2016-11-22').valueOf());
|
||||
const demo = require(`../.${file}`).default; // eslint-disable-line global-require, import/no-dynamic-require
|
||||
const wrapper = render(demo);
|
||||
|
||||
|
38
tests/shared/imageTest.ts
Normal file
38
tests/shared/imageTest.ts
Normal file
@ -0,0 +1,38 @@
|
||||
import React from 'react';
|
||||
// Reference: https://github.com/ant-design/ant-design/pull/24003#discussion_r427267386
|
||||
// eslint-disable-next-line import/no-unresolved
|
||||
import puppeteer, { Browser, Page } from 'puppeteer';
|
||||
import { toMatchImageSnapshot } from 'jest-image-snapshot';
|
||||
import ReactDOMServer from 'react-dom/server';
|
||||
|
||||
expect.extend({ toMatchImageSnapshot });
|
||||
|
||||
// eslint-disable-next-line jest/no-export
|
||||
export default function imageTest(component: React.ReactElement) {
|
||||
describe(`Image test`, () => {
|
||||
let browser: Browser;
|
||||
let page: Page;
|
||||
|
||||
beforeAll(async () => {
|
||||
browser = await puppeteer.launch();
|
||||
page = await browser.newPage();
|
||||
await page.goto(`file://${process.cwd()}/tests/index.html`);
|
||||
await page.addStyleTag({ path: `${process.cwd()}/dist/antd.css` });
|
||||
});
|
||||
|
||||
afterAll(() => {
|
||||
browser.close();
|
||||
});
|
||||
|
||||
it('component image screenshot should correct', async () => {
|
||||
const html = ReactDOMServer.renderToString(component);
|
||||
await page.evaluate(innerHTML => {
|
||||
document.querySelector('#root')!.innerHTML = innerHTML;
|
||||
}, html);
|
||||
|
||||
const image = await page.screenshot();
|
||||
|
||||
expect(image).toMatchImageSnapshot();
|
||||
});
|
||||
});
|
||||
}
|
@ -9,7 +9,7 @@ export default function rtlTest(Component: React.ComponentType, mockDate?: boole
|
||||
describe(`rtl render`, () => {
|
||||
it(`component should be rendered correctly in RTL direction`, () => {
|
||||
if (mockDate) {
|
||||
MockDate.set(Moment('2000-09-28').toDate());
|
||||
MockDate.set(Moment('2000-09-28').valueOf());
|
||||
}
|
||||
const wrapper = mount(
|
||||
<ConfigProvider direction="rtl">
|
||||
|
@ -1,8 +1,7 @@
|
||||
import moment from 'moment';
|
||||
import MockDate from 'mockdate';
|
||||
|
||||
export function setMockDate(dateString = '2017-09-18T03:30:07.795') {
|
||||
MockDate.set(moment(dateString).toDate());
|
||||
MockDate.set(dateString);
|
||||
}
|
||||
|
||||
export function resetMockDate() {
|
||||
|
Loading…
Reference in New Issue
Block a user