Merge pull request #24741 from ant-design/master

chore: merge master into feature
This commit is contained in:
偏右 2020-06-05 13:38:32 +08:00 committed by GitHub
commit d1bdaee607
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
63 changed files with 699 additions and 277 deletions

3
.github/tests_checker.yml vendored Normal file
View File

@ -0,0 +1,3 @@
comment: 'Could you please add tests to make sure this change works as expected?',
fileExtensions: [.ts', '.tsx', '.json']
testDir: '__tests__'

View File

@ -15,6 +15,29 @@ timeline: true
---
## 4.3.1
`2020-06-02`
- Tabs
- 🐞 Fix Tabs `hideAdd` not working. [#24621](https://github.com/ant-design/ant-design/pull/24621)
- 🐞 Fix Tabs dropdown has extra horizontal scrollbar in Firefox. [#24677](https://github.com/ant-design/ant-design/pull/24677)
- 🐞 Fix Tabs inkbar border lost when use with `react-sticky`. [#24643](https://github.com/ant-design/ant-design/pull/24643)
- 💄 Adjust Tabs `tabBarStyle` style display. [#24620](https://github.com/ant-design/ant-design/pull/24620)
- Button
- 🐞 Fix Button quick set `loading` from `delay` to `false` will still change to the loading status. [#24678](https://github.com/ant-design/ant-design/pull/24678)
- 🐞 Fix the inconsistent style of Text Button in `danger`. [#24622](https://github.com/ant-design/ant-design/pull/24622) [@morenyang](https://github.com/morenyang)
- 🐞 Fix Table no showing scrollbar inside Row. [#24661](https://github.com/ant-design/ant-design/pull/24661) [@zt123123](https://github.com/zt123123)
- 🐞 Fix Drawer get `dropdownMatchSelectWidth` as dom prop warning. [#24651](https://github.com/ant-design/ant-design/pull/24651)
- 🐞 Adjust Steps to support React.Fragment with `children`. [#24644](https://github.com/ant-design/ant-design/pull/24644)
- 🐞 Fix Upload delete icon cannot be navigate via keyboard. [#24615](https://github.com/ant-design/ant-design/pull/24615) [@morenyang](https://github.com/morenyang)
- 🐞 Fix multiple Select search input cursor missing issue. [#24631](https://github.com/ant-design/ant-design/pull/24631)
- 🐞 Fix Radio.Group using `options` get `Element type is invalid` error. [#24631](https://github.com/ant-design/ant-design/pull/24631)
- RTL
- 💄 Adjust Notification default placement to `topLeft` in RTL. [#24632](https://github.com/ant-design/ant-design/pull/24632)
- TypeScript
- 🛠 Export Tabs `TabPaneProps` definition. [#24648](https://github.com/ant-design/ant-design/pull/24648)
## 4.3.0
`2020-05-31`
@ -81,7 +104,7 @@ timeline: true
- 💄 Optimize Table filter dropdown style in RTL. [#24529](https://github.com/ant-design/ant-design/pull/24529)
- 💄 Fix Cascader dropdown style in RTL. [#24520](https://github.com/ant-design/ant-design/pull/24520)
- TypeScript
- Form export `RuleObject` and `RuleRender` types. [#24541](https://github.com/ant-design/ant-design/pull/24541) [@sorteam](https://github.com/sorteam)
- 🛠 Form export `RuleObject` and `RuleRender` types. [#24541](https://github.com/ant-design/ant-design/pull/24541) [@sorteam](https://github.com/sorteam)
## 4.2.5

View File

@ -15,6 +15,29 @@ timeline: true
---
## 4.3.1
`2020-06-02`
- Tabs
- 🐞 修复 Tabs `hideAdd` 无效的问题。[#24621](https://github.com/ant-design/ant-design/pull/24621)
- 🐞 修复 Tabs 下拉菜单内在 Firefox 下出现水平滚动条。[#24677](https://github.com/ant-design/ant-design/pull/24677)
- 🐞 修复 Tabs 配合 `react-sticky` 使用时下划线遗失问题。[#24643](https://github.com/ant-design/ant-design/pull/24643)
- 💄 调整 Tabs `tabBarStyle` 的展示样式。[#24620](https://github.com/ant-design/ant-design/pull/24620)
- Button
- 🐞 修复 Button 将 `loading``delay` 快速切换至 `false` 时仍然会变成加载状态的问题。[#24678](https://github.com/ant-design/ant-design/pull/24678)
- 🐞 修复 Text Button 在 `danger` 时样式不一致的问题。[#24622](https://github.com/ant-design/ant-design/pull/24622) [@morenyang](https://github.com/morenyang)
- 🐞 修复 Table 包裹在 Row 中时无法展现横向滚动条的问题。[#24661](https://github.com/ant-design/ant-design/pull/24661) [@zt123123](https://github.com/zt123123)
- 🐞 修复 Drawer 会报 dom 节点设置 `dropdownMatchSelectWidth` 的警告信息。[#24651](https://github.com/ant-design/ant-design/pull/24651)
- 🐞 调整 Steps 现支持 `children` 下使用 React.Fragment。[#24644](https://github.com/ant-design/ant-design/pull/24644)
- 🐞 解决 Upload 删除图标键盘无法导航操作的问题。[#24615](https://github.com/ant-design/ant-design/pull/24615) [@morenyang](https://github.com/morenyang)
- 🐞 修复多选 Select 输入框的光标展示问题。[#24631](https://github.com/ant-design/ant-design/pull/24631)
- 🐞 修复 Radio.Group 设置 `options` 报错的问题。[#24631](https://github.com/ant-design/ant-design/pull/24631)
- RTL
- 💄 调整 Notification RTL 模式下默认弹窗位置为左上。[#24632](https://github.com/ant-design/ant-design/pull/24632)
- TypeScript
- 🛠 导出 Tabs `TabPaneProps` 定义。[#24648](https://github.com/ant-design/ant-design/pull/24648)
## 4.3.0
`2020-05-31`
@ -81,7 +104,7 @@ timeline: true
- 💄 优化 Table 筛选下拉框在 RTL 模式下样式。[#24529](https://github.com/ant-design/ant-design/pull/24529)
- 💄 优化 Cascader RTL 模式下拉框的样式。[#24520](https://github.com/ant-design/ant-design/pull/24520)
- TypeScript
- Form 导出 `RuleObject``RuleRender` 定义。[#24541](https://github.com/ant-design/ant-design/pull/24541) [@sorteam](https://github.com/sorteam)
- 🛠 Form 导出 `RuleObject``RuleRender` 定义。[#24541](https://github.com/ant-design/ant-design/pull/24541) [@sorteam](https://github.com/sorteam)
## 4.2.5

View File

@ -1,5 +1,6 @@
import React, { Component } from 'react';
import { mount, render } from 'enzyme';
import { act } from 'react-dom/test-utils';
import { SearchOutlined } from '@ant-design/icons';
import Button from '..';
import ConfigProvider from '../../config-provider';
@ -165,6 +166,24 @@ describe('Button', () => {
expect(wrapper.hasClass('ant-btn-loading')).toBe(false);
});
it('reset when loading back of delay', () => {
jest.useFakeTimers();
const wrapper = mount(<Button loading={{ delay: 1000 }} />);
wrapper.update();
wrapper.setProps({ loading: { delay: 2000 } });
wrapper.update();
wrapper.setProps({ loading: false });
act(() => {
jest.runAllTimers();
wrapper.update();
});
expect(wrapper.find('.ant-btn-loading')).toHaveLength(0);
jest.useRealTimers();
});
it('should not clickable when button is loading', () => {
const onClick = jest.fn();
const wrapper = mount(

View File

@ -121,16 +121,32 @@ interface CompoundedComponent
__ANT_BUTTON: boolean;
}
type Loading = number | boolean;
const InternalButton: React.ForwardRefRenderFunction<unknown, ButtonProps> = (props, ref) => {
const {
loading,
prefixCls: customizePrefixCls,
type,
danger,
shape,
size: customizeSize,
className,
children,
icon,
ghost,
block,
...rest
} = props;
const size = React.useContext(SizeContext);
const [loading, setLoading] = React.useState(props.loading);
const [innerLoading, setLoading] = React.useState<Loading>(!!loading);
const [hasTwoCNChar, setHasTwoCNChar] = React.useState(false);
const { getPrefixCls, autoInsertSpaceInButton, direction } = React.useContext(ConfigContext);
const buttonRef = (ref as any) || React.createRef<HTMLElement>();
let delayTimeout: number;
const delayTimeoutRef = React.useRef<number>();
const isNeedInserted = () => {
const { icon, children, type } = props;
return React.Children.count(children) === 1 && !icon && type !== 'link' && type !== 'text';
};
@ -149,18 +165,24 @@ const InternalButton: React.ForwardRefRenderFunction<unknown, ButtonProps> = (pr
}
};
// =============== Update Loading ===============
let loadingOrDelay: Loading;
if (typeof loading === 'object' && loading.delay) {
loadingOrDelay = loading.delay || true;
} else {
loadingOrDelay = !!loading;
}
React.useEffect(() => {
if (props.loading && typeof props.loading !== 'boolean') {
clearTimeout(delayTimeout);
clearTimeout(delayTimeoutRef.current);
if (typeof loadingOrDelay === 'number') {
delayTimeoutRef.current = window.setTimeout(() => {
setLoading(loadingOrDelay);
}, loadingOrDelay);
} else {
setLoading(loadingOrDelay);
}
if (props.loading && typeof props.loading !== 'boolean' && props.loading.delay) {
delayTimeout = window.setTimeout(() => {
setLoading(props.loading);
}, props.loading.delay);
} else if (props.loading !== loading) {
setLoading(props.loading);
}
}, [props.loading]);
}, [loadingOrDelay]);
React.useEffect(() => {
fixTwoCNChar();
@ -168,26 +190,13 @@ const InternalButton: React.ForwardRefRenderFunction<unknown, ButtonProps> = (pr
const handleClick = (e: React.MouseEvent<HTMLButtonElement | HTMLAnchorElement, MouseEvent>) => {
const { onClick } = props;
if (loading) {
if (innerLoading) {
return;
}
if (onClick) {
(onClick as React.MouseEventHandler<HTMLButtonElement | HTMLAnchorElement>)(e);
}
};
const {
prefixCls: customizePrefixCls,
type,
danger,
shape,
size: customizeSize,
className,
children,
icon,
ghost,
block,
...rest
} = props;
devWarning(
!(typeof icon === 'string' && icon.length > 2),
@ -212,7 +221,7 @@ const InternalButton: React.ForwardRefRenderFunction<unknown, ButtonProps> = (pr
break;
}
const iconType = loading ? 'loading' : icon;
const iconType = innerLoading ? 'loading' : icon;
const classes = classNames(prefixCls, className, {
[`${prefixCls}-${type}`]: type,
@ -220,7 +229,7 @@ const InternalButton: React.ForwardRefRenderFunction<unknown, ButtonProps> = (pr
[`${prefixCls}-${sizeCls}`]: sizeCls,
[`${prefixCls}-icon-only`]: !children && children !== 0 && iconType,
[`${prefixCls}-background-ghost`]: ghost,
[`${prefixCls}-loading`]: loading,
[`${prefixCls}-loading`]: innerLoading,
[`${prefixCls}-two-chinese-chars`]: hasTwoCNChar && autoInsertSpace,
[`${prefixCls}-block`]: block,
[`${prefixCls}-dangerous`]: !!danger,
@ -228,10 +237,10 @@ const InternalButton: React.ForwardRefRenderFunction<unknown, ButtonProps> = (pr
});
const iconNode =
icon && !loading ? (
icon && !innerLoading ? (
icon
) : (
<LoadingIcon existIcon={!!icon} prefixCls={prefixCls} loading={loading} />
<LoadingIcon existIcon={!!icon} prefixCls={prefixCls} loading={!!innerLoading} />
);
const kids =

View File

@ -23,14 +23,23 @@ class App extends React.Component {
};
enterLoading = index => {
const newLoadings = [...this.state.loadings];
newLoadings[index] = true;
this.setState({
loadings: newLoadings,
this.setState(({ loadings }) => {
const newLoadings = [...loadings];
newLoadings[index] = true;
return {
loadings: newLoadings,
};
});
setTimeout(() => {
newLoadings[index] = false;
this.setState({ loadings: newLoadings });
this.setState(({ loadings }) => {
const newLoadings = [...loadings];
newLoadings[index] = false;
return {
loadings: newLoadings,
};
});
}, 6000);
};

View File

@ -127,15 +127,27 @@ describe('Calendar', () => {
MockDate.reset();
});
it('should trigger onPanelChange when click last month of date', () => {
const onPanelChange = jest.fn();
const date = new Moment('1990-09-03');
const wrapper = mount(<Calendar onPanelChange={onPanelChange} value={date} />);
describe('onPanelChange', () => {
it('trigger when click last month of date', () => {
const onPanelChange = jest.fn();
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);
expect(onPanelChange).toHaveBeenCalled();
expect(onPanelChange.mock.calls[0][0].month()).toEqual(date.month() - 1);
});
it('not trigger when in same month', () => {
const onPanelChange = jest.fn();
const date = new Moment('1990-09-03');
const wrapper = mount(<Calendar onPanelChange={onPanelChange} value={date} />);
wrapper.find('.ant-picker-cell').at(10).simulate('click');
expect(onPanelChange).not.toHaveBeenCalled();
});
});
it('switch should work correctly without prop mode', async () => {

View File

@ -1,4 +1,5 @@
import * as React from 'react';
import useMergedState from 'rc-util/lib/hooks/useMergedState';
import classNames from 'classnames';
import padStart from 'lodash/padStart';
import { PickerPanel as RCPickerPanel } from 'rc-picker';
@ -62,13 +63,13 @@ export interface CalendarProps<DateType> {
}
function generateCalendar<DateType>(generateConfig: GenerateConfig<DateType>) {
function isSameYear(date1: DateType, date2: DateType) {
return date1 && date2 && generateConfig.getYear(date1) === generateConfig.getYear(date2);
}
function isSameMonth(date1: DateType, date2: DateType) {
return (
date1 === date2 ||
(date1 &&
date2 &&
generateConfig.getYear(date1) === generateConfig.getYear(date2) &&
generateConfig.getMonth(date1) === generateConfig.getMonth(date2))
isSameYear(date1, date2) && generateConfig.getMonth(date1) === generateConfig.getMonth(date2)
);
}
@ -106,15 +107,15 @@ function generateCalendar<DateType>(generateConfig: GenerateConfig<DateType>) {
// ====================== State =======================
// Value
const [innerValue, setInnerValue] = React.useState(
() => value || defaultValue || generateConfig.getNow(),
);
const mergedValue = value || innerValue;
const [mergedValue, setMergedValue] = useMergedState(() => value || generateConfig.getNow(), {
defaultValue,
value,
});
// Mode
const [innerMode, setInnerMode] = React.useState(() => mode || 'month');
const mergedMode = mode || innerMode;
const [mergedMode, setMergedMode] = useMergedState('month', {
value: mode,
});
const panelMode = React.useMemo<'month' | 'date'>(
() => (mergedMode === 'year' ? 'month' : 'date'),
[mergedMode],
@ -142,10 +143,16 @@ function generateCalendar<DateType>(generateConfig: GenerateConfig<DateType>) {
};
const triggerChange = (date: DateType) => {
setInnerValue(date);
setMergedValue(date);
if (!isSameDate(date, mergedValue)) {
triggerPanelChange(date, mergedMode);
// Trigger when month panel switch month
if (
(panelMode === 'date' && !isSameMonth(date, mergedValue)) ||
(panelMode === 'month' && !isSameYear(date, mergedValue))
) {
triggerPanelChange(date, mergedMode);
}
if (onChange) {
onChange(date);
@ -154,7 +161,7 @@ function generateCalendar<DateType>(generateConfig: GenerateConfig<DateType>) {
};
const triggerModeChange = (newMode: CalendarMode) => {
setInnerMode(newMode);
setMergedMode(newMode);
triggerPanelChange(mergedValue, newMode);
};

View File

@ -3,6 +3,7 @@ import { mount } from 'enzyme';
import ConfigProvider from '..';
import DatePicker from '../../date-picker';
import Slider from '../../slider';
import Drawer from '../../drawer';
describe('ConfigProvider.GetPopupContainer', () => {
it('Datepicker', () => {
@ -25,4 +26,15 @@ describe('ConfigProvider.GetPopupContainer', () => {
wrapper.find('.ant-slider-handle').first().simulate('mouseenter');
expect(getPopupContainer).toHaveBeenCalled();
});
it('drawer', () => {
const getPopupContainer = jest.fn(node => node.parentNode);
const Demo = ({ visible }) => (
<ConfigProvider getPopupContainer={getPopupContainer}>
<Drawer visible={visible} />
</ConfigProvider>
);
mount(<Demo visible />);
expect(getPopupContainer).toHaveBeenCalled();
});
});

View File

@ -11,6 +11,21 @@ exports[`renders ./components/drawer/demo/basic-right.md correctly 1`] = `
</button>
`;
exports[`renders ./components/drawer/demo/config-provider.md correctly 1`] = `
<div
class="site-drawer-render-in-current-wrapper"
>
<button
class="ant-btn ant-btn-primary"
type="button"
>
<span>
Open
</span>
</button>
</div>
`;
exports[`renders ./components/drawer/demo/form-in-drawer.md correctly 1`] = `
<button
class="ant-btn ant-btn-primary"

View File

@ -0,0 +1,57 @@
---
order: 99
title:
zh-CN: ConfigProvider
en-US: ConfigProvider
debug: true
---
## zh-CN
支持 ConfigProvider 配置。
## en-US
config by ConfigProvider.
```tsx
import React, { useState, useRef } from 'react';
import { Drawer, ConfigProvider, Button } from 'antd';
const App: React.FC = () => {
const domRef = useRef();
const [visible, setVisible] = useState(false);
const showDrawer = () => {
setVisible(true);
};
const onClose = () => {
setVisible(false);
};
return (
<ConfigProvider
getPopupContainer={() => {
return domRef.current;
}}
>
<div ref={domRef} className="site-drawer-render-in-current-wrapper">
<Button type="primary" onClick={showDrawer}>
Open
</Button>
<Drawer
style={{ position: 'absolute' }}
title="ConfigProvider"
placement="right"
onClose={onClose}
visible={visible}
>
<p>Some contents...</p>
<p>Some contents...</p>
<p>Some contents...</p>
</Drawer>
</div>
</ConfigProvider>
);
};
ReactDOM.render(<App />, mountNode);
```

View File

@ -6,7 +6,7 @@ import classNames from 'classnames';
import omit from 'omit.js';
import { ConfigConsumerProps } from '../config-provider';
import { withConfigConsumer } from '../config-provider/context';
import { withConfigConsumer, ConfigConsumer } from '../config-provider/context';
import { tuple } from '../_util/type';
const DrawerContext = React.createContext<Drawer | null>(null);
@ -253,54 +253,77 @@ class Drawer extends React.Component<DrawerProps & ConfigConsumerProps, IDrawerS
// render Provider for Multi-level drawer
renderProvider = (value: Drawer) => {
const { prefixCls, placement, className, mask, direction, visible, ...rest } = this.props;
this.parentDrawer = value;
const drawerClassName = classNames(className, {
'no-mask': !mask,
[`${prefixCls}-rtl`]: direction === 'rtl',
});
const offsetStyle = mask ? this.getOffsetStyle() : {};
return (
<DrawerContext.Provider value={this}>
<RcDrawer
handler={false}
{...omit(rest, [
'zIndex',
'style',
'closable',
'destroyOnClose',
'drawerStyle',
'headerStyle',
'bodyStyle',
'footerStyle',
'footer',
'locale',
'title',
'push',
'visible',
'getPopupContainer',
'rootPrefixCls',
'getPrefixCls',
'renderEmpty',
'csp',
'pageHeader',
'autoInsertSpaceInButton',
'width',
'height',
'dropdownMatchSelectWidth',
])}
{...offsetStyle}
prefixCls={prefixCls}
open={visible}
showMask={mask}
placement={placement}
style={this.getRcDrawerStyle()}
className={drawerClassName}
>
{this.renderBody()}
</RcDrawer>
</DrawerContext.Provider>
<ConfigConsumer>
{({ getPopupContainer, getPrefixCls }) => {
const {
prefixCls: customizePrefixCls,
placement,
className,
mask,
direction,
visible,
...rest
} = this.props;
const prefixCls = getPrefixCls('select', customizePrefixCls);
const drawerClassName = classNames(className, {
'no-mask': !mask,
[`${prefixCls}-rtl`]: direction === 'rtl',
});
const offsetStyle = mask ? this.getOffsetStyle() : {};
return (
<DrawerContext.Provider value={this}>
<RcDrawer
handler={false}
{...omit(rest, [
'zIndex',
'style',
'closable',
'destroyOnClose',
'drawerStyle',
'headerStyle',
'bodyStyle',
'footerStyle',
'footer',
'locale',
'title',
'push',
'visible',
'getPopupContainer',
'rootPrefixCls',
'getPrefixCls',
'renderEmpty',
'csp',
'pageHeader',
'autoInsertSpaceInButton',
'width',
'height',
'dropdownMatchSelectWidth',
])}
getContainer={
// 有可能为 false所以不能直接判断
rest.getContainer === undefined
? () => (getPopupContainer ? getPopupContainer(document.body) : undefined)
: rest.getContainer
}
{...offsetStyle}
prefixCls={prefixCls}
open={visible}
showMask={mask}
placement={placement}
style={this.getRcDrawerStyle()}
className={drawerClassName}
>
{this.renderBody()}
</RcDrawer>
</DrawerContext.Provider>
);
}}
</ConfigConsumer>
);
};

View File

@ -12,6 +12,9 @@ cover: https://gw.alipayobjects.com/zos/alicdn/TIIiKrsQT/Dropdown.svg
当页面上的操作命令过多时,用此组件可以收纳操作元素。点击或移入触点,会出现一个下拉菜单。可在列表中进行选择,并执行相应的命令。
- 用于收罗一组命令操作。
- Select 用于选择,而 Dropdown 是命令集合。
## API
属性如下

View File

@ -58,7 +58,6 @@
border-radius: @border-radius-base;
outline: none;
box-shadow: @box-shadow-base;
-webkit-transform: translate3d(0, 0, 0);
&-item-group-title {
padding: 5px @control-padding-horizontal;
@ -118,6 +117,14 @@
}
}
> .@{iconfont-css-prefix} + span > a {
color: @text-color;
transition: all 0.3s;
&:hover {
color: @text-color;
}
}
&:first-child {
& when (@dropdown-edge-child-vertical-padding = 0) {
border-radius: @border-radius-base @border-radius-base 0 0;
@ -268,7 +275,8 @@
}
.@{dropdown-prefix-cls}-menu-item,
.@{dropdown-prefix-cls}-menu-submenu-title,
.@{dropdown-prefix-cls}-menu-item > a {
.@{dropdown-prefix-cls}-menu-item > a,
.@{dropdown-prefix-cls}-menu-item > .@{iconfont-css-prefix} + span > a {
color: @text-color-secondary-dark;
.@{dropdown-prefix-cls}-menu-submenu-arrow::after {
color: @text-color-secondary-dark;

View File

@ -7,11 +7,11 @@ title:
## zh-CN
动态增加、减少表单项。
动态增加、减少表单项。`add` 方法参数可用于设置初始值。
## en-US
Add or remove form items dynamically.
Add or remove form items dynamically. `add` function support config initial value.
```jsx
import { Form, Input, Button } from 'antd';

View File

@ -0,0 +1,20 @@
// Fix Input component height issue in IE11
@media screen and (-ms-high-contrast: active), (-ms-high-contrast: none) {
.@{ant-prefix}-input {
height: @input-height-base;
&-lg {
height: @input-height-lg;
}
&-sm {
height: @input-height-sm;
}
&-affix-wrapper {
> input.@{ant-prefix}-input {
height: auto;
}
}
}
}

View File

@ -47,3 +47,4 @@
@import './search-input';
@import './rtl';
@import './IE11';

View File

@ -1,9 +1,12 @@
/* eslint-disable no-template-curly-in-string */
import Pagination from 'rc-pagination/lib/locale/he_IL';
import DatePicker from '../date-picker/locale/he_IL';
import TimePicker from '../time-picker/locale/he_IL';
import Calendar from '../calendar/locale/he_IL';
import { Locale } from '../locale-provider';
const typeTemplate = '${label} הוא לא ${type} תקין';
const localeValues: Locale = {
locale: 'he',
Pagination,
@ -63,6 +66,55 @@ const localeValues: Locale = {
PageHeader: {
back: 'חזרה',
},
Form: {
defaultValidateMessages: {
default: 'ערך השדה שגוי ${label}',
required: 'בבקשה הזן ${label}',
enum: '${label} חייב להיות אחד מערכים אלו [${enum}]',
whitespace: '${label} לא יכול להיות ריק',
date: {
format: '${label} תאריך לא תקין',
parse: '${label} לא ניתן להמיר לתאריך',
invalid: '${label} הוא לא תאריך תקין',
},
types: {
string: typeTemplate,
method: typeTemplate,
array: typeTemplate,
object: typeTemplate,
number: typeTemplate,
date: typeTemplate,
boolean: typeTemplate,
integer: typeTemplate,
float: typeTemplate,
regexp: typeTemplate,
email: typeTemplate,
url: typeTemplate,
hex: typeTemplate,
},
string: {
len: '${label} חייב להיות ${len} תווים',
min: '${label} חייב להיות ${min} תווים',
max: '${label} מקסימום ${max} תווים',
range: '${label} חייב להיות בין ${min}-${max} תווים',
},
number: {
len: '${label} חייב להיות שווה ל ${len}',
min: '${label} ערך מינימלי הוא ${min}',
max: '${label} ערך מקסימלי הוא ${max}',
range: '${label} חייב להיות בין ${min}-${max}',
},
array: {
len: 'חייב להיות ${len} ${label}',
min: 'מינימום ${min} ${label}',
max: 'מקסימום ${max} ${label}',
range: 'הסכום של ${label} חייב להיות בין ${min}-${max}',
},
pattern: {
mismatch: '${label} לא תואם לתבנית ${pattern}',
},
},
},
};
export default localeValues;

View File

@ -1302,30 +1302,28 @@ exports[`renders ./components/menu/demo/vertical.md correctly 1`] = `
class="ant-menu-submenu-title"
role="button"
>
<span>
<span
aria-label="appstore"
class="anticon anticon-appstore"
role="img"
<span
aria-label="appstore"
class="anticon anticon-appstore"
role="img"
>
<svg
aria-hidden="true"
class=""
data-icon="appstore"
fill="currentColor"
focusable="false"
height="1em"
viewBox="64 64 896 896"
width="1em"
>
<svg
aria-hidden="true"
class=""
data-icon="appstore"
fill="currentColor"
focusable="false"
height="1em"
viewBox="64 64 896 896"
width="1em"
>
<path
d="M464 144H160c-8.8 0-16 7.2-16 16v304c0 8.8 7.2 16 16 16h304c8.8 0 16-7.2 16-16V160c0-8.8-7.2-16-16-16zm-52 268H212V212h200v200zm452-268H560c-8.8 0-16 7.2-16 16v304c0 8.8 7.2 16 16 16h304c8.8 0 16-7.2 16-16V160c0-8.8-7.2-16-16-16zm-52 268H612V212h200v200zM464 544H160c-8.8 0-16 7.2-16 16v304c0 8.8 7.2 16 16 16h304c8.8 0 16-7.2 16-16V560c0-8.8-7.2-16-16-16zm-52 268H212V612h200v200zm452-268H560c-8.8 0-16 7.2-16 16v304c0 8.8 7.2 16 16 16h304c8.8 0 16-7.2 16-16V560c0-8.8-7.2-16-16-16zm-52 268H612V612h200v200z"
/>
</svg>
</span>
<span>
Navigation Two
</span>
<path
d="M464 144H160c-8.8 0-16 7.2-16 16v304c0 8.8 7.2 16 16 16h304c8.8 0 16-7.2 16-16V160c0-8.8-7.2-16-16-16zm-52 268H212V212h200v200zm452-268H560c-8.8 0-16 7.2-16 16v304c0 8.8 7.2 16 16 16h304c8.8 0 16-7.2 16-16V160c0-8.8-7.2-16-16-16zm-52 268H612V212h200v200zM464 544H160c-8.8 0-16 7.2-16 16v304c0 8.8 7.2 16 16 16h304c8.8 0 16-7.2 16-16V560c0-8.8-7.2-16-16-16zm-52 268H212V612h200v200zm452-268H560c-8.8 0-16 7.2-16 16v304c0 8.8 7.2 16 16 16h304c8.8 0 16-7.2 16-16V560c0-8.8-7.2-16-16-16zm-52 268H612V612h200v200z"
/>
</svg>
</span>
<span>
Navigation Two
</span>
<i
class="ant-menu-submenu-arrow"

View File

@ -35,15 +35,7 @@ ReactDOM.render(
<Menu.Item key="4">Option 4</Menu.Item>
</Menu.ItemGroup>
</SubMenu>
<SubMenu
key="sub2"
title={
<span>
<AppstoreOutlined />
<span>Navigation Two</span>
</span>
}
>
<SubMenu key="sub2" icon={<AppstoreOutlined />} title="Navigation Two">
<Menu.Item key="5">Option 5</Menu.Item>
<Menu.Item key="6">Option 6</Menu.Item>
<SubMenu key="sub3" title="Submenu">

View File

@ -27,6 +27,7 @@ const ConfirmDialog = (props: ConfirmDialogProps) => {
okButtonProps,
cancelText,
cancelButtonProps,
direction,
} = props;
devWarning(
@ -53,6 +54,7 @@ const ConfirmDialog = (props: ConfirmDialogProps) => {
const classString = classNames(
contentPrefixCls,
`${contentPrefixCls}-${props.type}`,
{ [`${contentPrefixCls}-rtl`]: direction === 'rtl' },
props.className,
);

View File

@ -111,6 +111,7 @@ export interface ModalFuncProps {
autoFocusButton?: null | 'ok' | 'cancel';
transitionName?: string;
maskTransitionName?: string;
direction?: string;
}
export interface ModalLocale {

View File

@ -44,7 +44,9 @@ When requiring users to interact with the application, but without jumping to a
#### Note
> The state of Modal will be preserved at it's component lifecycle by default, if you wish to open it with a brand new state everytime, set `destroyOnClose` on it.
> - The state of Modal will be preserved at it's component lifecycle by default, if you wish to open it with a brand new state everytime, set `destroyOnClose` on it.
>
> - `Modal.method()` RTL mode only supports hooks.
### Modal.method()

View File

@ -48,7 +48,9 @@ cover: https://gw.alipayobjects.com/zos/alicdn/3StSdUlSH/Modal.svg
#### 注意
> `<Modal />` 默认关闭后状态不会自动清空, 如果希望每次打开都是新内容,请设置 `destroyOnClose`
> - `<Modal />` 默认关闭后状态不会自动清空, 如果希望每次打开都是新内容,请设置 `destroyOnClose`
>
> - `Modal.method()` RTL 模式仅支持 hooks 用法。
### Modal.method()

View File

@ -2,6 +2,7 @@
@import '../../style/mixins/index';
@dialog-prefix-cls: ~'@{ant-prefix}-modal';
@confirm-prefix-cls: ~'@{ant-prefix}-modal-confirm';
@dialog-wrap-rtl-cls: ~'@{dialog-prefix-cls}-wrap-rtl';
.@{dialog-prefix-cls} {
@ -29,6 +30,38 @@
}
}
}
&-confirm {
&-body {
.@{dialog-wrap-rtl-cls} & {
direction: rtl;
}
> .@{iconfont-css-prefix} {
.@{dialog-wrap-rtl-cls} & {
float: right;
margin-right: 0;
margin-left: 16px;
}
+ .@{confirm-prefix-cls}-title + .@{confirm-prefix-cls}-content {
.@{dialog-wrap-rtl-cls} & {
margin-right: 38px;
margin-left: 0;
}
}
}
}
&-btns {
.@{dialog-wrap-rtl-cls} & {
float: left;
}
button + button {
.@{dialog-wrap-rtl-cls} & {
margin-right: 8px;
margin-left: 0;
}
}
}
}
}
.@{dialog-prefix-cls}-centered {

View File

@ -3,6 +3,7 @@ import { ModalFuncProps } from '../Modal';
import ConfirmDialog from '../ConfirmDialog';
import defaultLocale from '../../locale/default';
import LocaleReceiver from '../../locale-provider/LocaleReceiver';
import { ConfigContext } from '../../config-provider';
export interface HookModalProps {
afterClose: () => void;
@ -26,6 +27,7 @@ const HookModal: React.RefForwardingComponent<HookModalRef, HookModalProps> = (
) => {
const [visible, setVisible] = React.useState(true);
const [innerConfig, setInnerConfig] = React.useState(config);
const { direction } = React.useContext(ConfigContext);
function close() {
setVisible(false);
@ -53,6 +55,7 @@ const HookModal: React.RefForwardingComponent<HookModalRef, HookModalProps> = (
innerConfig.okText ||
(innerConfig.okCancel ? modalLocale.okText : modalLocale.justOkText)
}
direction={direction}
cancelText={innerConfig.cancelText || modalLocale.cancelText}
/>
)}

View File

@ -4,4 +4,4 @@ title: Components Overview
type: Overview
---
`antd` provides plenty of UI components to enrich your web applications, and we will improve components expirerence consistently.
`antd` provides plenty of UI components to enrich your web applications, and we will improve components expirerence consistently. We also recommand some great [Third-Party Libraries](/docs/react/recommendation) additionlly.

View File

@ -4,4 +4,4 @@ title: 组件总览
type: 组件总览
---
`antd` 为 Web 应用提供了丰富的基础 UI 组件,我们还将持续探索企业级应用的最佳 UI 实践。
`antd` 为 Web 应用提供了丰富的基础 UI 组件,我们还将持续探索企业级应用的最佳 UI 实践。除了官方组件,我们也提供了[社区精选组件](/docs/react/recommendation)作为必要的补充。

View File

@ -1,9 +1,16 @@
import Radio from './radio';
import InternalRadio from './radio';
import Group from './group';
import Button from './radioButton';
import { RadioProps } from './interface';
export * from './interface';
interface CompoundedComponent
extends React.ForwardRefExoticComponent<RadioProps & React.RefAttributes<HTMLElement>> {
Group: typeof Group;
Button: typeof Button;
}
const Radio = InternalRadio as CompoundedComponent;
Radio.Button = Button;
Radio.Group = Group;
export { Button, Group };

View File

@ -1,19 +1,11 @@
import * as React from 'react';
import RcCheckbox from 'rc-checkbox';
import classNames from 'classnames';
import RadioGroup from './group';
import RadioButton from './radioButton';
import { RadioProps, RadioChangeEvent } from './interface';
import { ConfigContext } from '../config-provider';
import RadioGroupContext from './context';
import { composeRef } from '../_util/ref';
interface CompoundedComponent
extends React.ForwardRefExoticComponent<RadioProps & React.RefAttributes<HTMLElement>> {
Group: typeof RadioGroup;
Button: typeof RadioButton;
}
const InternalRadio: React.ForwardRefRenderFunction<unknown, RadioProps> = (props, ref) => {
const context = React.useContext(RadioGroupContext);
const { getPrefixCls, direction } = React.useContext(ConfigContext);
@ -60,10 +52,9 @@ const InternalRadio: React.ForwardRefRenderFunction<unknown, RadioProps> = (prop
);
};
const Radio = React.forwardRef<unknown, RadioProps>(InternalRadio) as CompoundedComponent;
const Radio = React.forwardRef<unknown, RadioProps>(InternalRadio);
Radio.displayName = 'Radio';
Radio.Group = RadioGroup;
Radio.Button = RadioButton;
Radio.defaultProps = {
type: 'radio',
};

View File

@ -90,7 +90,7 @@ class Select<ValueType extends SelectValue = SelectValue> extends React.Componen
className,
size: customizeSize,
listHeight = 256,
listItemHeight = 32,
listItemHeight = 24,
getPopupContainer,
dropdownClassName,
bordered,

View File

@ -574,6 +574,9 @@
@table-footer-bg: @background-color-light;
@table-footer-color: @heading-color;
@table-header-bg-sm: @table-header-bg;
@table-font-size: @font-size-base;
@table-font-size-md: @table-font-size;
@table-font-size-sm: @table-font-size;
// Sorter
// Legacy: `table-header-sort-active-bg` is used for hover not real active
@table-header-sort-active-bg: darken(@table-header-bg, 3%);

View File

@ -62,7 +62,7 @@ const columns = [
| --- | --- | --- | --- |
| tableLayout | [table-layout](https://developer.mozilla.org/en-US/docs/Web/CSS/table-layout) attribute of table element | - \| `auto` \| `fixed` | -<hr />`fixed` when header/columns are fixed, or using `column.ellipsis` |
| bordered | Whether to show all table borders | boolean | `false` |
| columns | Columns of table | [ColumnProps](#Column)\[] | - |
| columns | Columns of table | [ColumnsType](#Column)\[] | - |
| components | Override default table elements | [TableComponents](https://git.io/fANxz) | - |
| dataSource | Data record array to be displayed | object\[] | - |
| expandable | Config expandable content | [expandable](#expandable) | - |
@ -220,14 +220,14 @@ Properties for row selection.
```tsx
import { Table } from 'antd';
import { ColumnProps } from 'antd/es/table';
import { ColumnsType } from 'antd/es/table';
interface User {
key: number,
name: string;
}
const columns: ColumnProps<User>[] = [{
const columns: ColumnsType<User>[] = [{
key: 'name',
title: 'Name',
dataIndex: 'name',

View File

@ -67,7 +67,7 @@ const columns = [
| --- | --- | --- | --- |
| tableLayout | 表格元素的 [table-layout](https://developer.mozilla.org/zh-CN/docs/Web/CSS/table-layout) 属性,设为 `fixed` 表示内容不会影响列的布局 | - \| `auto` \| `fixed` | 无<hr />固定表头/列或使用了 `column.ellipsis` 时,默认值为 `fixed` |
| bordered | 是否展示外边框和列边框 | boolean | false |
| columns | 表格列的配置描述,具体项见下表 | [ColumnProps](#Column)\[] | - |
| columns | 表格列的配置描述,具体项见下表 | [ColumnsType](#Column)\[] | - |
| components | 覆盖默认的 table 元素 | [TableComponents](https://git.io/fANxz) | - |
| dataSource | 数据数组 | object\[] | - |
| expandable | 配置展开属性 | [expandable](#expandable) | - |
@ -225,14 +225,14 @@ const columns = [
```tsx
import { Table } from 'antd';
import { ColumnProps } from 'antd/es/table';
import { ColumnsType } from 'antd/es/table';
interface User {
key: number;
name: string;
}
const columns: ColumnProps<User>[] = [{
const columns: ColumnsType<User>[] = [{
key: 'name',
title: 'Name',
dataIndex: 'name',

View File

@ -10,6 +10,7 @@
@table-header-sort-active-filter-bg: lighten(@table-header-sort-active-bg, 2%);
.@{table-prefix-cls}-wrapper {
max-width: 100%;
.clearfix;
}
@ -18,6 +19,7 @@
position: relative;
z-index: 0;
clear: both;
font-size: @table-font-size;
background: @table-bg;
border-radius: @border-radius-base;

View File

@ -1,7 +1,9 @@
@import './index';
.table-size(@size, @padding-vertical, @padding-horizontal) {
.table-size(@size, @padding-vertical, @padding-horizontal, @font-size) {
.@{table-prefix-cls}.@{table-prefix-cls}-@{size} {
font-size: @font-size;
.@{table-prefix-cls}-title,
.@{table-prefix-cls}-footer,
.@{table-prefix-cls}-thead > tr > th,
@ -48,12 +50,12 @@
// ================================================================
// = Middle =
// ================================================================
.table-size(~'middle', @table-padding-vertical-md, @table-padding-horizontal-md);
.table-size(~'middle', @table-padding-vertical-md, @table-padding-horizontal-md, @table-font-size-md);
// ================================================================
// = Small =
// ================================================================
.table-size(~'small', @table-padding-vertical-sm, @table-padding-horizontal-sm);
.table-size(~'small', @table-padding-vertical-sm, @table-padding-horizontal-sm, @table-font-size-sm);
.@{table-prefix-cls}-small {
.@{table-prefix-cls}-thead > tr > th {

View File

@ -1,6 +1,5 @@
import * as React from 'react';
import RcTabs, { TabPane, TabsProps as RcTabsProps } from 'rc-tabs';
import { TabPaneProps } from 'rc-tabs/lib/sugar/TabPane';
import RcTabs, { TabPane, TabsProps as RcTabsProps, TabPaneProps } from 'rc-tabs';
import { EditableConfig } from 'rc-tabs/lib/interface';
import classNames from 'classnames';
import EllipsisOutlined from '@ant-design/icons/EllipsisOutlined';

View File

@ -3,7 +3,8 @@
@import './index';
.@{tab-prefix-cls}-card {
> .@{tab-prefix-cls}-nav {
> .@{tab-prefix-cls}-nav,
> div > .@{tab-prefix-cls}-nav {
.@{tab-prefix-cls}-tab {
margin: 0;
padding: @tabs-card-horizontal-padding;
@ -25,7 +26,8 @@
// ========================== Top & Bottom ==========================
&.@{tab-prefix-cls}-top,
&.@{tab-prefix-cls}-bottom {
> .@{tab-prefix-cls}-nav {
> .@{tab-prefix-cls}-nav,
> div > .@{tab-prefix-cls}-nav {
.@{tab-prefix-cls}-tab:not(:last-of-type) {
margin-right: @tabs-card-gutter;
}
@ -33,7 +35,8 @@
}
&.@{tab-prefix-cls}-top {
> .@{tab-prefix-cls}-nav {
> .@{tab-prefix-cls}-nav,
> div > .@{tab-prefix-cls}-nav {
.@{tab-prefix-cls}-tab {
border-radius: @border-radius-base @border-radius-base 0 0;
@ -44,7 +47,8 @@
}
}
&.@{tab-prefix-cls}-bottom {
> .@{tab-prefix-cls}-nav {
> .@{tab-prefix-cls}-nav,
> div > .@{tab-prefix-cls}-nav {
.@{tab-prefix-cls}-tab {
border-radius: 0 0 @border-radius-base @border-radius-base;
@ -58,7 +62,8 @@
// ========================== Left & Right ==========================
&.@{tab-prefix-cls}-left,
&.@{tab-prefix-cls}-right {
> .@{tab-prefix-cls}-nav {
> .@{tab-prefix-cls}-nav,
> div > .@{tab-prefix-cls}-nav {
.@{tab-prefix-cls}-tab:not(:last-of-type) {
margin-bottom: @tabs-card-gutter;
}
@ -66,7 +71,8 @@
}
&.@{tab-prefix-cls}-left {
> .@{tab-prefix-cls}-nav {
> .@{tab-prefix-cls}-nav,
> div > .@{tab-prefix-cls}-nav {
.@{tab-prefix-cls}-tab {
border-radius: @border-radius-base 0 0 @border-radius-base;
@ -77,7 +83,8 @@
}
}
&.@{tab-prefix-cls}-right {
> .@{tab-prefix-cls}-nav {
> .@{tab-prefix-cls}-nav,
> div > .@{tab-prefix-cls}-nav {
.@{tab-prefix-cls}-tab {
border-radius: 0 @border-radius-base @border-radius-base 0;

View File

@ -19,6 +19,7 @@
max-height: 200px;
margin: 0;
padding: @dropdown-edge-child-vertical-padding 0;
overflow-x: hidden;
overflow-y: auto;
text-align: left;
list-style-type: none;

View File

@ -15,7 +15,8 @@
overflow: hidden;
// ========================== Navigation ==========================
> .@{tab-prefix-cls}-nav {
> .@{tab-prefix-cls}-nav,
> div > .@{tab-prefix-cls}-nav {
position: relative;
display: flex;
flex: none;

View File

@ -6,7 +6,8 @@
&-bottom {
flex-direction: column;
> .@{tab-prefix-cls}-nav {
> .@{tab-prefix-cls}-nav,
> div > .@{tab-prefix-cls}-nav {
margin: @tabs-bar-margin;
&::before {
@ -54,7 +55,8 @@
}
&-top {
> .@{tab-prefix-cls}-nav {
> .@{tab-prefix-cls}-nav,
> div > .@{tab-prefix-cls}-nav {
&::before {
bottom: 0;
}
@ -66,7 +68,8 @@
}
&-bottom {
> .@{tab-prefix-cls}-nav {
> .@{tab-prefix-cls}-nav,
> div > .@{tab-prefix-cls}-nav {
order: 1;
margin-top: @margin-md;
margin-bottom: 0;
@ -80,7 +83,8 @@
}
}
> .@{tab-prefix-cls}-content-holder {
> .@{tab-prefix-cls}-content-holder,
> div > .@{tab-prefix-cls}-content-holder {
order: 0;
}
}
@ -88,7 +92,8 @@
// ========================== Left & Right ==========================
&-left,
&-right {
> .@{tab-prefix-cls}-nav {
> .@{tab-prefix-cls}-nav,
> div > .@{tab-prefix-cls}-nav {
flex-direction: column;
min-width: 50px;
@ -148,13 +153,15 @@
}
&-left {
> .@{tab-prefix-cls}-nav {
> .@{tab-prefix-cls}-nav,
> div > .@{tab-prefix-cls}-nav {
.@{tab-prefix-cls}-ink-bar {
right: 0;
}
}
> .@{tab-prefix-cls}-content-holder {
> .@{tab-prefix-cls}-content-holder,
> div > .@{tab-prefix-cls}-content-holder {
margin-left: -@border-width-base;
border-left: @border-width-base @border-style-base @border-color-split;
@ -165,7 +172,8 @@
}
&-right {
> .@{tab-prefix-cls}-nav {
> .@{tab-prefix-cls}-nav,
> div > .@{tab-prefix-cls}-nav {
order: 1;
.@{tab-prefix-cls}-ink-bar {
@ -173,7 +181,8 @@
}
}
> .@{tab-prefix-cls}-content-holder {
> .@{tab-prefix-cls}-content-holder,
> div > .@{tab-prefix-cls}-content-holder {
order: 0;
margin-right: -@border-width-base;
border-right: @border-width-base @border-style-base @border-color-split;

View File

@ -1,11 +1,12 @@
@import '../../style/themes/index';
@import '../../style/mixins/index';
@import './index';
@tab-prefix-cls: ~'@{ant-prefix}-tabs';
.@{tab-prefix-cls}-rtl {
direction: rtl;
> .@{tab-prefix-cls}-nav {
.@{tab-prefix-cls}-nav {
.@{tab-prefix-cls}-tab {
margin: @tabs-horizontal-margin-rtl;
@ -59,3 +60,14 @@
}
}
}
.@{tab-prefix-cls}-dropdown {
&-rtl {
direction: rtl;
}
&-menu-item {
.@{tab-prefix-cls}-dropdown-rtl & {
text-align: right;
}
}
}

View File

@ -8,15 +8,19 @@ export interface CheckableTagProps {
style?: React.CSSProperties;
checked: boolean;
onChange?: (checked: boolean) => void;
onClick?: (e: React.MouseEventHandler<HTMLElement>) => void;
}
const CheckableTag: React.FC<CheckableTagProps> = props => {
const { getPrefixCls } = React.useContext(ConfigContext);
const handleClick = () => {
const { checked, onChange } = props;
const handleClick = (e: React.MouseEventHandler) => {
const { checked, onChange, onClick } = props;
if (onChange) {
onChange(!checked);
}
if (onClick) {
onClick(e);
}
};
const { prefixCls: customizePrefixCls, className, checked, ...restProps } = props;

View File

@ -1,7 +1,7 @@
// Jest Snapshot v1, https://goo.gl/fbAQLP
exports[`renders ./components/tag/demo/animation.md correctly 1`] = `
<div>
Array [
<div
style="margin-bottom:16px"
>
@ -97,7 +97,7 @@ exports[`renders ./components/tag/demo/animation.md correctly 1`] = `
</span>
</span>
</div>
</div>
</div>,
<span
class="ant-tag site-tag-plus"
>
@ -126,17 +126,17 @@ exports[`renders ./components/tag/demo/animation.md correctly 1`] = `
</svg>
</span>
New Tag
</span>
</div>
</span>,
]
`;
exports[`renders ./components/tag/demo/basic.md correctly 1`] = `
<div>
Array [
<span
class="ant-tag"
>
Tag 1
</span>
</span>,
<span
class="ant-tag"
>
@ -145,7 +145,7 @@ exports[`renders ./components/tag/demo/basic.md correctly 1`] = `
>
Link
</a>
</span>
</span>,
<span
class="ant-tag"
>
@ -171,7 +171,7 @@ exports[`renders ./components/tag/demo/basic.md correctly 1`] = `
/>
</svg>
</span>
</span>
</span>,
<span
class="ant-tag"
>
@ -197,8 +197,8 @@ exports[`renders ./components/tag/demo/basic.md correctly 1`] = `
/>
</svg>
</span>
</span>
</div>
</span>,
]
`;
exports[`renders ./components/tag/demo/checkable.md correctly 1`] = `
@ -340,14 +340,14 @@ Array [
`;
exports[`renders ./components/tag/demo/control.md correctly 1`] = `
<div>
Array [
<span
class="ant-tag edit-tag"
>
<span>
Unremovable
</span>
</span>
</span>,
<span
class="ant-tag edit-tag"
>
@ -375,7 +375,7 @@ exports[`renders ./components/tag/demo/control.md correctly 1`] = `
/>
</svg>
</span>
</span>
</span>,
<span
class="ant-tag edit-tag"
>
@ -403,7 +403,7 @@ exports[`renders ./components/tag/demo/control.md correctly 1`] = `
/>
</svg>
</span>
</span>
</span>,
<span
class="ant-tag site-tag-plus"
>
@ -432,12 +432,12 @@ exports[`renders ./components/tag/demo/control.md correctly 1`] = `
</svg>
</span>
New Tag
</span>
</div>
</span>,
]
`;
exports[`renders ./components/tag/demo/controlled.md correctly 1`] = `
<div>
Array [
<span
class="ant-tag"
>
@ -463,8 +463,8 @@ exports[`renders ./components/tag/demo/controlled.md correctly 1`] = `
/>
</svg>
</span>
</span>
<br />
</span>,
<br />,
<button
class="ant-btn ant-btn-sm"
type="button"
@ -472,12 +472,12 @@ exports[`renders ./components/tag/demo/controlled.md correctly 1`] = `
<span>
Toggle
</span>
</button>
</div>
</button>,
]
`;
exports[`renders ./components/tag/demo/icon.md correctly 1`] = `
<div>
Array [
<span
class="ant-tag ant-tag-has-color"
style="background-color:#55acee"
@ -505,7 +505,7 @@ exports[`renders ./components/tag/demo/icon.md correctly 1`] = `
<span>
Twitter
</span>
</span>
</span>,
<span
class="ant-tag ant-tag-has-color"
style="background-color:#cd201f"
@ -533,7 +533,7 @@ exports[`renders ./components/tag/demo/icon.md correctly 1`] = `
<span>
Youtube
</span>
</span>
</span>,
<span
class="ant-tag ant-tag-has-color"
style="background-color:#3b5999"
@ -561,7 +561,7 @@ exports[`renders ./components/tag/demo/icon.md correctly 1`] = `
<span>
Facebook
</span>
</span>
</span>,
<span
class="ant-tag ant-tag-has-color"
style="background-color:#55acee"
@ -589,8 +589,8 @@ exports[`renders ./components/tag/demo/icon.md correctly 1`] = `
<span>
LinkedIn
</span>
</span>
</div>
</span>,
]
`;
exports[`renders ./components/tag/demo/status.md correctly 1`] = `

View File

@ -42,6 +42,30 @@ describe('Tag', () => {
expect(wrapper.find('.ant-tag:not(.ant-tag-hidden)').length).toBe(1);
});
it('should trigger onClick', () => {
const onClick = jest.fn();
const wrapper = mount(<Tag onClick={onClick} />);
wrapper.find('.ant-tag').simulate('click');
expect(onClick).toHaveBeenCalledWith(
expect.objectContaining({
type: 'click',
preventDefault: expect.any(Function),
}),
);
});
it('should trigger onClick on CheckableTag', () => {
const onClick = jest.fn();
const wrapper = mount(<Tag.CheckableTag onClick={onClick} />);
wrapper.find('.ant-tag').simulate('click');
expect(onClick).toHaveBeenCalledWith(
expect.objectContaining({
type: 'click',
preventDefault: expect.any(Function),
}),
);
});
// https://github.com/ant-design/ant-design/issues/20344
it('should not trigger onClick when click close icon', () => {
const onClose = jest.fn();

View File

@ -78,7 +78,7 @@ class EditableTagGroup extends React.Component {
const { tags, inputVisible, inputValue } = this.state;
const tagChild = tags.map(this.forMap);
return (
<div>
<>
<div style={{ marginBottom: 16 }}>
<TweenOneGroup
enter={{
@ -113,7 +113,7 @@ class EditableTagGroup extends React.Component {
<PlusOutlined /> New Tag
</Tag>
)}
</div>
</>
);
}
}

View File

@ -26,7 +26,7 @@ function preventDefault(e) {
}
ReactDOM.render(
<div>
<>
<Tag>Tag 1</Tag>
<Tag>
<a href="https://github.com/ant-design/ant-design/issues/1862">Link</a>
@ -37,7 +37,7 @@ ReactDOM.render(
<Tag closable onClose={preventDefault}>
Prevent Default
</Tag>
</div>,
</>,
mountNode,
);
```

View File

@ -78,7 +78,7 @@ class EditableTagGroup extends React.Component {
render() {
const { tags, inputVisible, inputValue, editInputIndex, editInputValue } = this.state;
return (
<div>
<>
{tags.map((tag, index) => {
if (editInputIndex === index) {
return (
@ -143,7 +143,7 @@ class EditableTagGroup extends React.Component {
<PlusOutlined /> New Tag
</Tag>
)}
</div>
</>
);
}
}

View File

@ -23,7 +23,7 @@ class Demo extends React.Component {
render() {
return (
<div>
<>
<Tag
closable
visible={this.state.visible}
@ -35,7 +35,7 @@ class Demo extends React.Component {
<Button size="small" onClick={() => this.setState({ visible: !this.state.visible })}>
Toggle
</Button>
</div>
</>
);
}
}

View File

@ -27,7 +27,7 @@ import {
} from '@ant-design/icons';
ReactDOM.render(
<div>
<>
<Tag icon={<TwitterOutlined />} color="#55acee">
Twitter
</Tag>
@ -40,7 +40,7 @@ ReactDOM.render(
<Tag icon={<LinkedinOutlined />} color="#55acee">
LinkedIn
</Tag>
</div>,
</>,
mountNode,
);
```

View File

@ -3,7 +3,7 @@ order: 9
title: Third-Party Libraries
---
`antd` is designed to provide high-quality React UI components which follow the Ant Design Specification. So, we are not going to implement other functions which are not relative to `antd`'s target, and recommend using the excellent third-party library which have come from the React community:
`antd` is built to implement [a set of high-quality React UI components](/components/overview) which follow Ant Design specification. It is impossible to include all useful components in one package, so we also recommend that using other great third-party libraries in React community.
| Category | Recommended Components |
| --- | --- |
@ -32,9 +32,10 @@ title: Third-Party Libraries
| Context Menu | [react-contextmenu](https://github.com/vkbansal/react-contextmenu/) [react-contexify](https://github.com/fkhadra/react-contexify) |
| Emoji | [emoji-mart](https://github.com/missive/emoji-mart) |
| Split View | [react-split-pane](https://github.com/tomkp/react-split-pane) |
| Image Crop | [react-image-crop](https://github.com/DominicTobias/react-image-crop) |
| Image Crop | [antd-img-crop](https://github.com/nanxiaobei/antd-img-crop) [react-image-crop](https://github.com/DominicTobias/react-image-crop) |
| Trend Lines | [react-sparklines](https://github.com/borisyankov/react-sparklines) |
| Keywords highlight | [react-highlight-words](https://github.com/bvaughn/react-highlight-words) |
| Text Loop | [react-text-loop](https://github.com/braposo/react-text-loop) |
| Animation | [react-move](https://github.com/react-tools/react-move) [Ant Motion](https://motion.ant.design/components/tween-one) [react-spring](https://www.react-spring.io) |
## Products we are using ✨

View File

@ -3,7 +3,7 @@ order: 9
title: 社区精选组件
---
`antd` Ant Design 设计规范的 React 实现,所以我们倾向于只提供符合该规范、且带有视觉展现的 UI 组件,也尽量不重复造轮子。我们推荐使用以下社区已有的优秀实现,与 antd 形成互补
`antd`基于 Ant Design 设计规范实现的 [高质量 React 组件库](/components/overview),我们倾向于只提供符合该规范且带有视觉展现的 UI 组件,也尽量不重复造轮子。我们推荐使用以下社区已有的优秀实现,与 antd 形成互补
| 类型 | 推荐组件 |
| --- | --- |
@ -32,9 +32,10 @@ title: 社区精选组件
| 右键菜单 | [react-contextmenu](https://github.com/vkbansal/react-contextmenu/) [react-contexify](https://github.com/fkhadra/react-contexify) |
| Emoji | [emoji-mart](https://github.com/missive/emoji-mart) |
| 分割面板 | [react-split-pane](https://github.com/tomkp/react-split-pane) |
| 图片裁切 | [react-image-crop](https://github.com/DominicTobias/react-image-crop) |
| 图片裁切 | [antd-img-crop](https://github.com/nanxiaobei/antd-img-crop) [react-image-crop](https://github.com/DominicTobias/react-image-crop) |
| 趋势线 | [react-sparklines](https://github.com/borisyankov/react-sparklines) |
| 关键字高亮 | [react-highlight-words](https://github.com/bvaughn/react-highlight-words) |
| 文字轮播 | [react-text-loop](https://github.com/braposo/react-text-loop) |
| 动画 | [react-move](https://github.com/react-tools/react-move) [Ant Motion](https://motion.ant.design/components/tween-one) [react-spring](https://www.react-spring.io) |
## 推荐产品 ✨

View File

@ -5,13 +5,13 @@ title: Replace Moment.js
## How to use DatePicker with a custom date library like dayjs
Consider of bundle size, you can replace momentjs with customize date library. We provide two ways to customize date library:
Considering it's bundle size, you might want to replace Moment.js with a different date library of your choice. We provide two ways to customize which date library is used:
### Custom component
The first way is use `generatePicker` (or `generateCalendar`) helps to create Picker components.
First, we initialize an antd demo of `create-react-app`. You can refer to [Use in TypeScript](/docs/react/use-in-typescript), or you can start directly here [init antd](https://github.com/xiaohuoni/antd4-generate-picker/commit/47fec964e36d48bd15760f8f5abcb9655c259aa6)
First, we initialize an antd demo with `create-react-app`. You can refer to [Use in TypeScript](/docs/react/use-in-typescript), or you can start directly here [init antd](https://github.com/xiaohuoni/antd4-generate-picker/commit/47fec964e36d48bd15760f8f5abcb9655c259aa6)
#### DatePicker.tsx
@ -95,12 +95,12 @@ Modify `src/App.tsx`,import `dayjs` and custom component.
+ import format from 'dayjs';
```
If the above steps do not work correctly, you can refer [antd4-generate-picker/antd-ts](https://github.com/xiaohuoni/antd4-generate-picker/tree/master/antd-ts).
If the above steps do not work correctly, you can refer to [antd4-generate-picker/antd-ts](https://github.com/xiaohuoni/antd4-generate-picker/tree/master/antd-ts).
If you need JavaScript code, you can refer [antd4-generate-picker/antd-demo](https://github.com/xiaohuoni/antd4-generate-picker/tree/master/antd-demo).
If you need JavaScript code, you can refer to [antd4-generate-picker/antd-demo](https://github.com/xiaohuoni/antd4-generate-picker/tree/master/antd-demo).
If you are the user of [umi](https://umijs.org/), you can ref [antd4-use-dayjs-replace-moment](https://github.com/xiaohuoni/antd4-use-dayjs-replace-moment).
If you use [umi](https://umijs.org/), you can reference [antd4-use-dayjs-replace-moment](https://github.com/xiaohuoni/antd4-use-dayjs-replace-moment).
### Webpack plugin
We also provide another implementation. We provide `antd-dayjs-webpack-plugin` plugin to replace `momentjs` to `Day.js` directly without changing a line of existing code. More info at [antd-dayjs-webpack-plugin](https://github.com/ant-design/antd-dayjs-webpack-plugin).
We also provide another implementation, which we provide with `antd-dayjs-webpack-plugin`, replacing `momentjs` with `Day.js` directly without changing a line of existing code. More info can be found at [antd-dayjs-webpack-plugin](https://github.com/ant-design/antd-dayjs-webpack-plugin).

View File

@ -138,8 +138,10 @@ module.exports = {
plugin: CracoLessPlugin,
options: {
lessLoaderOptions: {
modifyVars: { '@primary-color': '#1DA57A' },
javascriptEnabled: true,
lessOptions: {
modifyVars: { '@primary-color': '#1DA57A' },
javascriptEnabled: true,
},
},
},
},

View File

@ -136,8 +136,10 @@ module.exports = {
plugin: CracoLessPlugin,
options: {
lessLoaderOptions: {
modifyVars: { '@primary-color': '#1DA57A' },
javascriptEnabled: true,
lessOptions: {
modifyVars: { '@primary-color': '#1DA57A' },
javascriptEnabled: true,
},
},
},
},

View File

@ -146,8 +146,10 @@ module.exports = {
plugin: CracoLessPlugin,
options: {
lessLoaderOptions: {
modifyVars: { '@primary-color': '#1DA57A' },
javascriptEnabled: true,
lessOptions: {
modifyVars: { '@primary-color': '#1DA57A' },
javascriptEnabled: true,
},
},
},
},

View File

@ -148,8 +148,10 @@ module.exports = {
plugin: CracoLessPlugin,
options: {
lessLoaderOptions: {
modifyVars: { '@primary-color': '#1DA57A' },
javascriptEnabled: true,
lessOptions: {
modifyVars: { '@primary-color': '#1DA57A' },
javascriptEnabled: true,
},
},
},
},

View File

@ -1,6 +1,6 @@
{
"name": "antd",
"version": "4.3.0",
"version": "4.3.1",
"description": "An enterprise-class UI design language and React components implementation",
"keywords": [
"ant",
@ -47,7 +47,7 @@
"scripts": {
"api-collection": "antd-tools run api-collection",
"authors": "git log --format='%aN <%aE>' | sort -u | grep -v 'users.noreply.github.com' | grep -v 'gitter.im' | grep -v '.local>' | grep -v 'alibaba-inc.com' | grep -v 'alipay.com' | grep -v 'taobao.com' > AUTHORS.txt",
"build": "npm run compile && npm run dist",
"build": "npm run compile && NODE_OPTIONS='--max-old-space-size=4096' npm run dist",
"bundlesize": "bundlesize",
"check-commit": "node ./scripts/check-commit",
"prestart": "npm run version",
@ -73,7 +73,7 @@
"lint:md": "remark . -f -q",
"lint:script": "eslint . --ext '.js,.jsx,.ts,.tsx'",
"lint:style": "stylelint '{site,components}/**/*.less' --syntax less",
"pre-publish": "npm run check-commit && npm run test-all",
"pre-publish": "npm run test-all -- --skip-build",
"prettier": "prettier -c --write '**/*'",
"pretty-quick": "pretty-quick",
"pub": "npm run version && antd-tools run pub",
@ -136,14 +136,14 @@
"rc-steps": "~4.0.0",
"rc-switch": "~3.2.0",
"rc-table": "~7.7.2",
"rc-tabs": "~11.2.0",
"rc-tabs": "~11.3.1",
"rc-tooltip": "~4.2.0",
"rc-tree": "~3.3.0",
"rc-tree-select": "~3.2.0",
"rc-trigger": "~4.3.0",
"rc-upload": "~3.1.0",
"rc-util": "^5.0.1",
"scroll-into-view-if-needed": "^2.2.20",
"scroll-into-view-if-needed": "^2.2.25",
"warning": "^4.0.3"
},
"devDependencies": {

View File

@ -1,24 +1,37 @@
#!/bin/sh
cho "[TEST ALL] check-commit" && \
if [ "$1" != "--skip-build" ]; then
echo "[TEST ALL] dist"
npm run dist
echo "[TEST ALL] compile"
npm run compile
else
echo "Skip build..."
fi
echo "[TEST ALL] check-commit"
npm run check-commit
echo "[TEST ALL] lint" && \
npm run lint && \
echo "[TEST ALL] dist" && \
npm run dist && \
echo "[TEST ALL] dekko dist" && \
node ./tests/dekko/dist.test.js && \
echo "[TEST ALL] dist test" && \
LIB_DIR=dist npm test && \
echo "[TEST ALL] compile" && \
npm run compile && \
echo "[TEST ALL] dekko lib" && \
node ./tests/dekko/lib.test.js && \
echo "[TEST ALL] test es" && \
LIB_DIR=es npm test && \
echo "[TEST ALL] test lib" && \
LIB_DIR=lib npm test && \
echo "[TEST ALL] test" && \
npm test && \
echo "[TEST ALL] test node" && \
echo "[TEST ALL] lint"
npm run lint
echo "[TEST ALL] dekko dist"
node ./tests/dekko/dist.test.js
echo "[TEST ALL] dist test"
LIB_DIR=dist npm test
echo "[TEST ALL] dekko lib"
echo "[TEST ALL] test es"
LIB_DIR=es npm test
echo "[TEST ALL] test lib"
LIB_DIR=lib npm test
echo "[TEST ALL] test"
npm test
echo "[TEST ALL] test node"
npm run test-node

View File

@ -217,8 +217,8 @@
.highlight {
position: relative;
.ant-tabs-nav-scroll {
text-align: center;
.ant-tabs-nav-list {
margin: 0 auto;
}
pre {

View File

@ -7,6 +7,13 @@ import { getChildren } from 'jsonml.js/lib/utils';
import { getMetaDescription, getLocalizedPathname, getThemeConfig, getMenuItems } from '../utils';
import './ComponentOverview.less';
const onClickCard = href => {
window.gtag('event', '点击', {
event_category: '组件总览卡片',
event_label: href,
});
};
const { Title } = Typography;
const ComponentOverview = ({
componentsData = [],
@ -54,9 +61,10 @@ const ComponentOverview = ({
const url = `${component.filename
.replace(/(\/index)?((\.zh-cn)|(\.en-us))?\.md$/i, '')
.toLowerCase()}/`;
const href = getLocalizedPathname(url, locale === 'zh-CN');
return (
<Col xs={24} sm={12} lg={8} xl={6} key={component.title}>
<Link to={getLocalizedPathname(url, locale === 'zh-CN')}>
<Link to={href} onClick={() => onClickCard(href)}>
<Card
size="small"
className="components-overview-card"

View File

@ -48,7 +48,7 @@ const MORE_LIST: MoreProps[] = [
title: '折柱饼 +3 个套路,简单图表你真的会用吗?',
description: '本文一句话概括:数据可视化中,如何用最简单的图表高效地传递信息。',
img: 'https://gw.alipayobjects.com/mdn/rms_08e378/afts/img/A*Q1DMQpLCDQgAAAAAAAAAAABkARQnAQ',
date: '2020-11-28',
date: '2019-11-28',
source: 'zhihu',
href: 'https://zhuanlan.zhihu.com/p/93808863',
},

View File

@ -110,7 +110,7 @@ if (process.env.RUN_ENV === 'PRODUCTION') {
// Reduce non-minified dist files size
config.optimization.usedExports = true;
// use esbuild
if (process.env.ESBUILD) {
if (process.env.ESBUILD || process.env.CSB_REPO) {
config.optimization.minimizer[0] = new EsbuildPlugin();
}