From 19ad390c3b65a59b0f2bbfc7b8471f1a5db8f4f6 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E4=BA=8C=E8=B4=A7=E6=9C=BA=E5=99=A8=E4=BA=BA?= Date: Thu, 27 May 2021 14:48:48 +0800 Subject: [PATCH 01/22] feat: TreeSelect support treeLine (#30705) * feat: TreeSelect support treeline * docs: Update doc * tests: Update snapshot * docs: rm onlt * fix: TS definition --- .../__tests__/__snapshots__/demo.test.js.snap | 108 ++++++++++++++++++ components/tree-select/demo/async.md | 1 + components/tree-select/demo/treeLine.md | 56 +++++++++ components/tree-select/index.en-US.md | 9 +- components/tree-select/index.tsx | 12 +- components/tree-select/index.zh-CN.md | 29 ++--- components/tree-select/style/index.less | 4 +- components/tree/style/mixin.less | 17 ++- site/bisheng.config.js | 4 - 9 files changed, 204 insertions(+), 36 deletions(-) create mode 100644 components/tree-select/demo/treeLine.md diff --git a/components/tree-select/__tests__/__snapshots__/demo.test.js.snap b/components/tree-select/__tests__/__snapshots__/demo.test.js.snap index ec3fd447e2..ed526d3ef8 100644 --- a/components/tree-select/__tests__/__snapshots__/demo.test.js.snap +++ b/components/tree-select/__tests__/__snapshots__/demo.test.js.snap @@ -381,3 +381,111 @@ exports[`renders ./components/tree-select/demo/treeData.md correctly 1`] = ` `; + +exports[`renders ./components/tree-select/demo/treeLine.md correctly 1`] = ` +
+
+ +
+
+ +
+
+
+
+ + + + +
+ +
+
+
+`; diff --git a/components/tree-select/demo/async.md b/components/tree-select/demo/async.md index a670e59694..b9db90f5c5 100644 --- a/components/tree-select/demo/async.md +++ b/components/tree-select/demo/async.md @@ -45,6 +45,7 @@ class Demo extends React.Component { treeData: this.state.treeData.concat([ this.genTreeNode(id, false), this.genTreeNode(id, true), + this.genTreeNode(id, true), ]), }); resolve(); diff --git a/components/tree-select/demo/treeLine.md b/components/tree-select/demo/treeLine.md new file mode 100644 index 0000000000..dfbf7dac6f --- /dev/null +++ b/components/tree-select/demo/treeLine.md @@ -0,0 +1,56 @@ +--- +order: 6 +title: + zh-CN: 线性样式 + en-US: Show Tree Line +--- + +## zh-CN + +通过 `treeLine` 配置线性样式。 + +## en-US + +Use `treeLine` to show the line style. + +```tsx +import { TreeSelect, Switch, Space } from 'antd'; + +const { TreeNode } = TreeSelect; + +const Demo = () => { + const [treeLine, setTreeLine] = React.useState(true); + const [showLeafIcon, setShowLeafIcon] = React.useState(false); + + return ( + + setTreeLine(!treeLine)} + /> + setShowLeafIcon(!showLeafIcon)} + /> + + + + + + + + + + + + + ); +}; + +ReactDOM.render(, mountNode); +``` diff --git a/components/tree-select/index.en-US.md b/components/tree-select/index.en-US.md index 480b48634e..7fe711cef6 100644 --- a/components/tree-select/index.en-US.md +++ b/components/tree-select/index.en-US.md @@ -50,6 +50,7 @@ Tree selection control. | treeDefaultExpandedKeys | Default expanded treeNodes | string\[] | - | | | treeExpandedKeys | Set expanded keys | string\[] | - | | | treeIcon | Shows the icon before a TreeNode's title. There is no default style; you must set a custom style for it if set to `true` | boolean | false | | +| treeLine | Show the line. Ref [Tree - showLine](/components/tree/#components-tree-demo-line) | boolean \| object | false | 4.17.0 | | treeNodeFilterProp | Will be used for filtering if `filterTreeNode` returns true | string | `value` | | | treeNodeLabelProp | Will render as content of select | string | `title` | | | value | To set the current selected treeNode(s) | string \| string\[] | - | | @@ -62,10 +63,10 @@ Tree selection control. ### Tree Methods -| Name | Description | Version | -| --- | --- | --- | -| blur() | Remove focus | | -| focus() | Get focus | | +| Name | Description | Version | +| ------- | ------------ | ------- | +| blur() | Remove focus | | +| focus() | Get focus | | ### TreeNode props diff --git a/components/tree-select/index.tsx b/components/tree-select/index.tsx index de31c68179..2ed5c9471b 100644 --- a/components/tree-select/index.tsx +++ b/components/tree-select/index.tsx @@ -11,7 +11,7 @@ import omit from 'rc-util/lib/omit'; import { DefaultValueType } from 'rc-tree-select/lib/interface'; import { ConfigContext } from '../config-provider'; import devWarning from '../_util/devWarning'; -import { AntTreeNodeProps } from '../tree'; +import { AntTreeNodeProps, TreeProps } from '../tree'; import getIcons from '../select/utils/iconUtil'; import renderSwitcherIcon from '../tree/utils/iconUtil'; import SizeContext, { SizeType } from '../config-provider/SizeContext'; @@ -30,11 +30,18 @@ export type SelectValue = RawValue | RawValue[] | LabeledValue | LabeledValue[]; export interface TreeSelectProps extends Omit< RcTreeSelectProps, - 'showTreeIcon' | 'treeMotion' | 'inputIcon' | 'mode' | 'getInputElement' | 'backfill' + | 'showTreeIcon' + | 'treeMotion' + | 'inputIcon' + | 'mode' + | 'getInputElement' + | 'backfill' + | 'treeLine' > { suffixIcon?: React.ReactNode; size?: SizeType; bordered?: boolean; + treeLine?: TreeProps['showLine']; } export interface RefTreeSelectProps { @@ -140,6 +147,7 @@ const InternalTreeSelect = ( treeCheckable={ treeCheckable ? : treeCheckable } + treeLine={!!treeLine} inputIcon={suffixIcon} multiple={multiple} removeIcon={removeIcon} diff --git a/components/tree-select/index.zh-CN.md b/components/tree-select/index.zh-CN.md index 31c8549ed8..379965ca89 100644 --- a/components/tree-select/index.zh-CN.md +++ b/components/tree-select/index.zh-CN.md @@ -51,6 +51,7 @@ cover: https://gw.alipayobjects.com/zos/alicdn/Ax4DA0njr/TreeSelect.svg | treeDefaultExpandedKeys | 默认展开的树节点 | string\[] | - | | | treeExpandedKeys | 设置展开的树节点 | string\[] | - | | | treeIcon | 是否展示 TreeNode title 前的图标,没有默认样式,如设置为 true,需要自行定义图标相关样式 | boolean | false | | +| treeLine | 是否展示线条样式,请参考 [Tree - showLine](/components/tree/#components-tree-demo-line) | boolean \| object | false | 4.17.0 | | treeNodeFilterProp | 输入项过滤对应的 treeNode 属性 | string | `value` | | | treeNodeLabelProp | 作为显示的 prop 设置 | string | `title` | | | value | 指定当前选中的条目 | string \| string\[] | - | | @@ -63,25 +64,25 @@ cover: https://gw.alipayobjects.com/zos/alicdn/Ax4DA0njr/TreeSelect.svg ### Tree 方法 -| 名称 | 描述 | 版本 | -| --- | --- | --- | -| blur() | 移除焦点 | | -| focus() | 获取焦点 | | +| 名称 | 描述 | 版本 | +| ------- | -------- | ---- | +| blur() | 移除焦点 | | +| focus() | 获取焦点 | | ### TreeNode props > 建议使用 treeData 来代替 TreeNode,免去手工构造麻烦 -| 参数 | 说明 | 类型 | 默认值 | 版本 | -| --- | --- | --- | --- | --- | -| checkable | 当树为 Checkbox 时,设置独立节点是否展示 Checkbox | boolean | - | | -| disableCheckbox | 禁掉 Checkbox | boolean | false | | -| disabled | 是否禁用 | boolean | false | | -| isLeaf | 是否是叶子节点 | boolean | false | | -| key | 此项必须设置(其值在整个树范围内唯一) | string | - | | -| selectable | 是否可选 | boolean | true | | -| title | 树节点显示的内容 | ReactNode | `---` | | -| value | 默认根据此属性值进行筛选(其值在整个树范围内唯一) | string | - | | +| 参数 | 说明 | 类型 | 默认值 | 版本 | +| --------------- | -------------------------------------------------- | --------- | ------ | ---- | +| checkable | 当树为 Checkbox 时,设置独立节点是否展示 Checkbox | boolean | - | | +| disableCheckbox | 禁掉 Checkbox | boolean | false | | +| disabled | 是否禁用 | boolean | false | | +| isLeaf | 是否是叶子节点 | boolean | false | | +| key | 此项必须设置(其值在整个树范围内唯一) | string | - | | +| selectable | 是否可选 | boolean | true | | +| title | 树节点显示的内容 | ReactNode | `---` | | +| value | 默认根据此属性值进行筛选(其值在整个树范围内唯一) | string | - | | ## FAQ diff --git a/components/tree-select/style/index.less b/components/tree-select/style/index.less index e6672b5872..b2cf719f36 100644 --- a/components/tree-select/style/index.less +++ b/components/tree-select/style/index.less @@ -11,7 +11,7 @@ .@{tree-select-prefix-cls} { // ======================= Dropdown ======================= &-dropdown { - padding: @padding-xs (@padding-xs / 2) 0; + padding: @padding-xs (@padding-xs / 2); &-rtl { direction: rtl; @@ -24,8 +24,6 @@ align-items: stretch; .@{select-tree-prefix-cls}-treenode { - padding-bottom: @padding-xs; - .@{select-tree-prefix-cls}-node-content-wrapper { flex: auto; } diff --git a/components/tree/style/mixin.less b/components/tree/style/mixin.less index 197c206625..b8030a77db 100644 --- a/components/tree/style/mixin.less +++ b/components/tree/style/mixin.less @@ -1,7 +1,6 @@ @import '../../style/mixins/index'; @tree-prefix-cls: ~'@{ant-prefix}-tree'; -@tree-node-prefix-cls: ~'@{tree-prefix-cls}-treenode'; @select-tree-prefix-cls: ~'@{ant-prefix}-select-tree'; @tree-motion: ~'@{ant-prefix}-motion-collapse'; @tree-node-padding: (@padding-xs / 2); @@ -259,15 +258,15 @@ } } } -} -.@{tree-node-prefix-cls}-leaf-last { - .@{tree-prefix-cls}-switcher { - &-leaf-line { - &::before { - top: auto !important; - bottom: auto !important; - height: @tree-title-height - 10px !important; + .@{custom-tree-node-prefix-cls}-leaf-last { + .@{custom-tree-prefix-cls}-switcher { + &-leaf-line { + &::before { + top: auto !important; + bottom: auto !important; + height: @tree-title-height - 10px !important; + } } } } diff --git a/site/bisheng.config.js b/site/bisheng.config.js index d9609f0df8..05583ab76c 100644 --- a/site/bisheng.config.js +++ b/site/bisheng.config.js @@ -59,10 +59,6 @@ module.exports = { javascriptEnabled: true, }, webpackConfig(config) { - config.cache = { - type: 'filesystem', - }; - config.resolve.alias = { 'antd/lib': path.join(process.cwd(), 'components'), 'antd/es': path.join(process.cwd(), 'components'), From 5fc63ffafbce845b57cb830e5f49d47be5bbfe5e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E4=BA=8C=E8=B4=A7=E6=9C=BA=E5=99=A8=E4=BA=BA?= Date: Thu, 27 May 2021 16:51:14 +0800 Subject: [PATCH 02/22] feat: Table.Summary support fixed top position (#30726) * chore: bump rc-table * docs: Update demo & snapshot --- .../__tests__/__snapshots__/demo.test.js.snap | 16 ++++- components/table/demo/sticky.md | 58 +++++++++++-------- package.json | 2 +- 3 files changed, 51 insertions(+), 25 deletions(-) diff --git a/components/table/__tests__/__snapshots__/demo.test.js.snap b/components/table/__tests__/__snapshots__/demo.test.js.snap index 691070f852..9d310558d6 100644 --- a/components/table/__tests__/__snapshots__/demo.test.js.snap +++ b/components/table/__tests__/__snapshots__/demo.test.js.snap @@ -16637,7 +16637,21 @@ exports[`renders ./components/table/demo/sticky.md correctly 1`] = ` colspan="2" style="position:sticky;left:0" > - Fix Left + ( - - - - Fix Left - - - Scroll Context - - Fix Right - - - )} - sticky - />, - mountNode, -); +const Demo = () => { + const [fixedTop, setFixedTop] = React.useState(false); + + return ( + ( + + + + { + setFixedTop(!fixedTop); + }} + /> + + + Scroll Context + + Fix Right + + + )} + sticky + /> + ); +}; + +ReactDOM.render(, mountNode); ``` diff --git a/package.json b/package.json index 40cd609294..0885becc57 100644 --- a/package.json +++ b/package.json @@ -138,7 +138,7 @@ "rc-slider": "~9.7.1", "rc-steps": "~4.1.0", "rc-switch": "~3.2.0", - "rc-table": "~7.15.1", + "rc-table": "~7.16.0", "rc-tabs": "~11.9.1", "rc-textarea": "~0.3.0", "rc-tooltip": "~5.1.1", From 7a3bf8287f1c62466da336864374c98c61f94857 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E4=BA=8C=E8=B4=A7=E6=9C=BA=E5=99=A8=E4=BA=BA?= Date: Fri, 4 Jun 2021 14:44:41 +0800 Subject: [PATCH 03/22] feat: Form `rule` support `warningOnly` that not block form submit (#30829) * chore: bump rc-field-form * docs: Demo driven * refactor: Use event instead * chore: collection logic update * chore: clean up * chore: show warning * chore: warning no need required mark * feat: warning example * docs: mix error * chore: tmp list * chore: magic code * chore: fix motion * chore: fix style * chore: clean up useless code * chore: RM useless import * chore: RM useless file * test: Update snapshot * chore: Should reset * fix: Memo logic * fix: Form basic test case * fix: lint * chore: back of ref * test: Update snapshot * test: RM ueseless test case * chore: RM useless code --- components/_util/motion.tsx | 5 +- .../__snapshots__/components.test.js.snap | 18 +- components/form/ErrorList.tsx | 157 +++++++------- components/form/FormItem.tsx | 195 +++++++++--------- components/form/FormItemInput.tsx | 17 +- components/form/FormList.tsx | 3 +- .../__tests__/__snapshots__/demo.test.js.snap | 132 ++++++++++-- components/form/__tests__/index.test.js | 54 +++-- components/form/__tests__/list.test.js | 28 --- components/form/context.tsx | 12 +- components/form/demo/basic.md | 1 + components/form/demo/warning-only.md | 73 +++++++ components/form/hooks/useCacheErrors.ts | 47 ----- components/form/hooks/useDebounce.ts | 19 ++ components/form/index.en-US.md | 33 +-- components/form/index.zh-CN.md | 33 +-- components/form/style/index.less | 79 ++++--- components/form/style/status.less | 4 +- package.json | 4 +- 19 files changed, 554 insertions(+), 360 deletions(-) create mode 100644 components/form/demo/warning-only.md delete mode 100644 components/form/hooks/useCacheErrors.ts create mode 100644 components/form/hooks/useDebounce.ts diff --git a/components/_util/motion.tsx b/components/_util/motion.tsx index 2ded358b41..d3f14263dd 100644 --- a/components/_util/motion.tsx +++ b/components/_util/motion.tsx @@ -3,7 +3,10 @@ import { MotionEvent } from 'rc-motion/lib/interface'; // ================== Collapse Motion ================== const getCollapsedHeight: MotionEventHandler = () => ({ height: 0, opacity: 0 }); -const getRealHeight: MotionEventHandler = node => ({ height: node.scrollHeight, opacity: 1 }); +const getRealHeight: MotionEventHandler = node => { + const { scrollHeight } = node; + return { height: scrollHeight, opacity: 1 }; +}; const getCurrentHeight: MotionEventHandler = node => ({ height: node.offsetHeight }); const skipOpacityTransition: MotionEndEventHandler = (_, event: MotionEvent) => event?.deadline === true || (event as TransitionEvent).propertyName === 'height'; diff --git a/components/config-provider/__tests__/__snapshots__/components.test.js.snap b/components/config-provider/__tests__/__snapshots__/components.test.js.snap index 8c444ddc00..f7c90838c6 100644 --- a/components/config-provider/__tests__/__snapshots__/components.test.js.snap +++ b/components/config-provider/__tests__/__snapshots__/components.test.js.snap @@ -13272,9 +13272,10 @@ exports[`ConfigProvider components Form configProvider 1`] = `
+
= pro ); const errorListDom = ( - + ); diff --git a/components/form/__tests__/__snapshots__/demo.test.js.snap b/components/form/__tests__/__snapshots__/demo.test.js.snap index 97b1b68242..2374e8aaa5 100644 --- a/components/form/__tests__/__snapshots__/demo.test.js.snap +++ b/components/form/__tests__/__snapshots__/demo.test.js.snap @@ -1074,7 +1074,7 @@ exports[`renders ./components/form/demo/disabled-input-debug.md correctly 1`] =
Date: Wed, 9 Jun 2021 12:18:52 +0800 Subject: [PATCH 06/22] feat(module:popconfirm): support closing based on promise (#30871) * feat(module:popconfirm): support closing based on promise fix: typos fix: fix tests fix: fix ActionButton for Popconfirm * chore: cleanup * test: add test --- components/{modal => _util}/ActionButton.tsx | 39 ++++++++++++------- components/modal/ConfirmDialog.tsx | 6 +-- components/modal/demo/async.md | 2 +- components/modal/index.tsx | 1 - .../__tests__/__snapshots__/demo.test.js.snap | 11 ++++++ components/popconfirm/__tests__/index.test.js | 18 +++++++++ components/popconfirm/demo/promise.md | 37 ++++++++++++++++++ components/popconfirm/index.tsx | 23 ++++++----- 8 files changed, 109 insertions(+), 28 deletions(-) rename components/{modal => _util}/ActionButton.tsx (71%) create mode 100644 components/popconfirm/demo/promise.md diff --git a/components/modal/ActionButton.tsx b/components/_util/ActionButton.tsx similarity index 71% rename from components/modal/ActionButton.tsx rename to components/_util/ActionButton.tsx index 853faef598..7c88bfcfd1 100644 --- a/components/modal/ActionButton.tsx +++ b/components/_util/ActionButton.tsx @@ -5,10 +5,16 @@ import { LegacyButtonType, ButtonProps, convertLegacyProps } from '../button/but export interface ActionButtonProps { type?: LegacyButtonType; actionFn?: (...args: any[]) => any | PromiseLike; - closeModal: Function; + close: Function; autoFocus?: boolean; prefixCls: string; buttonProps?: ButtonProps; + emitEvent?: boolean; + quitOnNullishReturnValue?: boolean; +} + +function isThenable(thing?: PromiseLike): boolean { + return !!(thing && !!thing.then); } const ActionButton: React.FC = props => { @@ -30,16 +36,16 @@ const ActionButton: React.FC = props => { }, []); const handlePromiseOnOk = (returnValueOfOnOk?: PromiseLike) => { - const { closeModal } = props; - if (!returnValueOfOnOk || !returnValueOfOnOk.then) { + const { close } = props; + if (!isThenable(returnValueOfOnOk)) { return; } setLoading(true); - returnValueOfOnOk.then( + returnValueOfOnOk!.then( (...args: any[]) => { - // It's unnecessary to set loading=false, for the Modal will be unmounted after close. - // setState({ loading: false }); - closeModal(...args); + setLoading(false); + close(...args); + clickedRef.current = false; }, (e: Error) => { // Emit error when catch promise reject @@ -52,25 +58,32 @@ const ActionButton: React.FC = props => { ); }; - const onClick = () => { - const { actionFn, closeModal } = props; + const onClick = (e: React.MouseEvent) => { + const { actionFn, close } = props; if (clickedRef.current) { return; } clickedRef.current = true; if (!actionFn) { - closeModal(); + close(); return; } let returnValueOfOnOk; - if (actionFn.length) { - returnValueOfOnOk = actionFn(closeModal); + if (props.emitEvent) { + returnValueOfOnOk = actionFn(e); + if (props.quitOnNullishReturnValue && !isThenable(returnValueOfOnOk)) { + clickedRef.current = false; + close(e); + return; + } + } else if (actionFn.length) { + returnValueOfOnOk = actionFn(close); // https://github.com/ant-design/ant-design/issues/23358 clickedRef.current = false; } else { returnValueOfOnOk = actionFn(); if (!returnValueOfOnOk) { - closeModal(); + close(); return; } } diff --git a/components/modal/ConfirmDialog.tsx b/components/modal/ConfirmDialog.tsx index 8bce5edcae..11d7278afe 100644 --- a/components/modal/ConfirmDialog.tsx +++ b/components/modal/ConfirmDialog.tsx @@ -1,7 +1,7 @@ import * as React from 'react'; import classNames from 'classnames'; import Dialog, { ModalFuncProps } from './Modal'; -import ActionButton from './ActionButton'; +import ActionButton from '../_util/ActionButton'; import devWarning from '../_util/devWarning'; import ConfigProvider from '../config-provider'; import { getTransitionName } from '../_util/motion'; @@ -68,7 +68,7 @@ const ConfirmDialog = (props: ConfirmDialogProps) => { const cancelButton = okCancel && ( {
`; + +exports[`renders ./components/popconfirm/demo/promise.md correctly 1`] = ` + +`; diff --git a/components/popconfirm/__tests__/index.test.js b/components/popconfirm/__tests__/index.test.js index 6382705301..20e0332849 100644 --- a/components/popconfirm/__tests__/index.test.js +++ b/components/popconfirm/__tests__/index.test.js @@ -131,6 +131,24 @@ describe('Popconfirm', () => { expect(onVisibleChange).toHaveBeenLastCalledWith(false, eventObject); }); + it('should support onConfirm to return Promise', async () => { + const confirm = () => new Promise(res => setTimeout(res, 300)); + const onVisibleChange = jest.fn(); + const popconfirm = mount( + + show me your code + , + ); + + const triggerNode = popconfirm.find('span').at(0); + triggerNode.simulate('click'); + expect(onVisibleChange).toHaveBeenCalledTimes(1); + + popconfirm.find('.ant-btn').at(0).simulate('click'); + await sleep(400); + expect(onVisibleChange).toHaveBeenCalledWith(false, eventObject); + }); + it('should support customize icon', () => { const wrapper = mount( custom-icon}> diff --git a/components/popconfirm/demo/promise.md b/components/popconfirm/demo/promise.md new file mode 100644 index 0000000000..ebbab1cb32 --- /dev/null +++ b/components/popconfirm/demo/promise.md @@ -0,0 +1,37 @@ +--- +order: 7 +title: + zh-CN: 基于 Promise 的异步关闭 + en-US: Asynchronously close on Promise +--- + +## zh-CN + +点击确定后异步关闭 Popconfirm,例如提交表单。 + +## en-US + +Asynchronously close a popconfirm when the OK button is pressed. For example, you can use this pattern when you submit a form. + +```jsx +import { Button, Popconfirm } from 'antd'; + +const App = () => { + const confirm = () => + new Promise(resolve => { + setTimeout(() => resolve(), 3000); + }); + + return ( + console.log('visible change')} + > + + + ); +}; + +ReactDOM.render(, mountNode); +``` diff --git a/components/popconfirm/index.tsx b/components/popconfirm/index.tsx index 33bf616113..f8cce04f30 100644 --- a/components/popconfirm/index.tsx +++ b/components/popconfirm/index.tsx @@ -12,6 +12,7 @@ import { ConfigContext } from '../config-provider'; import { getRenderPropValue, RenderFunction } from '../_util/getRenderPropValue'; import { cloneElement } from '../_util/reactNode'; import { getTransitionName } from '../_util/motion'; +import ActionButton from '../_util/ActionButton'; export interface PopconfirmProps extends AbstractTooltipProps { title: React.ReactNode | RenderFunction; @@ -40,6 +41,7 @@ export interface PopconfirmLocale { } const Popconfirm = React.forwardRef((props, ref) => { + const { getPrefixCls } = React.useContext(ConfigContext); const [visible, setVisible] = useMergedState(false, { value: props.visible, defaultValue: props.defaultVisible, @@ -54,11 +56,12 @@ const Popconfirm = React.forwardRef((props, ref) => { props.onVisibleChange?.(value, e); }; - const onConfirm = (e: React.MouseEvent) => { + const close = (e: React.MouseEvent) => { settingVisible(false, e); - props.onConfirm?.call(this, e); }; + const onConfirm = (e: React.MouseEvent) => props.onConfirm?.call(this, e); + const onCancel = (e: React.MouseEvent) => { settingVisible(false, e); props.onCancel?.call(this, e); @@ -90,21 +93,21 @@ const Popconfirm = React.forwardRef((props, ref) => { - +
); }; - const { getPrefixCls } = React.useContext(ConfigContext); - const { prefixCls: customizePrefixCls, placement, From eb70f005139bf6e0e4f1254ca37b6a539595967b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E4=BA=8C=E8=B4=A7=E6=9C=BA=E5=99=A8=E4=BA=BA?= Date: Wed, 9 Jun 2021 15:36:59 +0800 Subject: [PATCH 07/22] feat: ConfigProvider static function support `iconPrefixCls` (#30925) * feat: Static iconPrefixCls * feat: message & notification support iconPrefixCls * docs: Update doc * chore: Force update * fix: ts def --- components/config-provider/index.en-US.md | 3 +- components/config-provider/index.tsx | 26 ++++-- components/config-provider/index.zh-CN.md | 3 +- components/message/__tests__/config.test.js | 15 ++-- components/message/index.tsx | 34 +++++--- components/modal/ConfirmDialog.tsx | 86 ++++++++++--------- components/modal/__tests__/confirm.test.js | 9 +- components/modal/confirm.tsx | 8 +- .../notification/__tests__/index.test.js | 15 ++-- components/notification/index.tsx | 38 ++++---- 10 files changed, 139 insertions(+), 98 deletions(-) diff --git a/components/config-provider/index.en-US.md b/components/config-provider/index.en-US.md index bc20d84eff..3b8e578784 100644 --- a/components/config-provider/index.en-US.md +++ b/components/config-provider/index.en-US.md @@ -61,7 +61,8 @@ Setting `Modal`、`Message`、`Notification` rootPrefixCls. ```jsx ConfigProvider.config({ - prefixCls: 'ant', + prefixCls: 'ant', // 4.13.0+ + iconPrefixCls: 'anticon', // 4.17.0+ }); ``` diff --git a/components/config-provider/index.tsx b/components/config-provider/index.tsx index 2c669a4c69..7d2936af1a 100644 --- a/components/config-provider/index.tsx +++ b/components/config-provider/index.tsx @@ -84,11 +84,19 @@ interface ProviderChildrenProps extends ConfigProviderProps { } export const defaultPrefixCls = 'ant'; +export const defaultIconPrefixCls = 'anticon'; let globalPrefixCls: string; +let globalIconPrefixCls: string; -const setGlobalConfig = (params: Pick) => { - if (params.prefixCls !== undefined) { - globalPrefixCls = params.prefixCls; +const setGlobalConfig = ({ + prefixCls, + iconPrefixCls, +}: Pick) => { + if (prefixCls !== undefined) { + globalPrefixCls = prefixCls; + } + if (iconPrefixCls !== undefined) { + globalIconPrefixCls = iconPrefixCls; } }; @@ -96,11 +104,16 @@ function getGlobalPrefixCls() { return globalPrefixCls || defaultPrefixCls; } +function getGlobalIconPrefixCls() { + return globalIconPrefixCls || defaultIconPrefixCls; +} + export const globalConfig = () => ({ getPrefixCls: (suffixCls?: string, customizePrefixCls?: string) => { if (customizePrefixCls) return customizePrefixCls; return suffixCls ? `${getGlobalPrefixCls()}-${suffixCls}` : getGlobalPrefixCls(); }, + getIconPrefixCls: getGlobalIconPrefixCls, getRootPrefixCls: (rootPrefixCls?: string, customizePrefixCls?: string) => { // Customize rootPrefixCls is first priority if (rootPrefixCls) { @@ -187,9 +200,10 @@ const ProviderChildren: React.FC = props => { }, ); - const memoIconContextValue = React.useMemo(() => ({ prefixCls: iconPrefixCls, csp }), [ - iconPrefixCls, - ]); + const memoIconContextValue = React.useMemo( + () => ({ prefixCls: iconPrefixCls, csp }), + [iconPrefixCls], + ); let childNode = children; // Additional Form provider diff --git a/components/config-provider/index.zh-CN.md b/components/config-provider/index.zh-CN.md index 2ba888256c..3d79fffa63 100644 --- a/components/config-provider/index.zh-CN.md +++ b/components/config-provider/index.zh-CN.md @@ -62,7 +62,8 @@ export default () => ( ```jsx ConfigProvider.config({ - prefixCls: 'ant', + prefixCls: 'ant', // 4.13.0+ + iconPrefixCls: 'anticon', // 4.17.0+ }); ``` diff --git a/components/message/__tests__/config.test.js b/components/message/__tests__/config.test.js index 4852433655..dc71498b48 100644 --- a/components/message/__tests__/config.test.js +++ b/components/message/__tests__/config.test.js @@ -96,19 +96,20 @@ describe('message.config', () => { }); it('should be able to global config rootPrefixCls', () => { - ConfigProvider.config({ prefixCls: 'prefix-test' }); + ConfigProvider.config({ prefixCls: 'prefix-test', iconPrefixCls: 'bamboo' }); message.info('last'); - expect(document.querySelectorAll('.ant-message-notice').length).toBe(0); - expect(document.querySelectorAll('.prefix-test-message-notice').length).toBe(1); - ConfigProvider.config({ prefixCls: 'ant' }); + expect(document.querySelectorAll('.ant-message-notice')).toHaveLength(0); + expect(document.querySelectorAll('.prefix-test-message-notice')).toHaveLength(1); + expect(document.querySelectorAll('.bamboo-info-circle')).toHaveLength(1); + ConfigProvider.config({ prefixCls: 'ant', iconPrefixCls: null }); }); it('should be able to config prefixCls', () => { message.config({ prefixCls: 'prefix-test', }); message.info('last'); - expect(document.querySelectorAll('.ant-message-notice').length).toBe(0); - expect(document.querySelectorAll('.prefix-test-notice').length).toBe(1); + expect(document.querySelectorAll('.ant-message-notice')).toHaveLength(0); + expect(document.querySelectorAll('.prefix-test-notice')).toHaveLength(1); message.config({ prefixCls: '', // can be set to empty, ant default value is set in ConfigProvider }); @@ -119,7 +120,7 @@ describe('message.config', () => { transitionName: '', }); message.info('last'); - expect(document.querySelectorAll('.ant-move-up-enter').length).toBe(0); + expect(document.querySelectorAll('.ant-move-up-enter')).toHaveLength(0); message.config({ transitionName: 'ant-move-up', }); diff --git a/components/message/index.tsx b/components/message/index.tsx index 1638baedd7..f2415c2b17 100755 --- a/components/message/index.tsx +++ b/components/message/index.tsx @@ -11,7 +11,7 @@ import CloseCircleFilled from '@ant-design/icons/CloseCircleFilled'; import CheckCircleFilled from '@ant-design/icons/CheckCircleFilled'; import InfoCircleFilled from '@ant-design/icons/InfoCircleFilled'; import createUseMessage from './hooks/useMessage'; -import { globalConfig } from '../config-provider'; +import ConfigProvider, { globalConfig } from '../config-provider'; type NoticeType = 'info' | 'success' | 'error' | 'warning' | 'loading'; @@ -74,16 +74,18 @@ function getRCNotificationInstance( callback: (info: { prefixCls: string; rootPrefixCls: string; + iconPrefixCls: string; instance: RCNotificationInstance; }) => void, ) { const { prefixCls: customizePrefixCls } = args; - const { getPrefixCls, getRootPrefixCls } = globalConfig(); + const { getPrefixCls, getRootPrefixCls, getIconPrefixCls } = globalConfig(); const prefixCls = getPrefixCls('message', customizePrefixCls || localPrefixCls); const rootPrefixCls = getRootPrefixCls(args.rootPrefixCls, prefixCls); + const iconPrefixCls = getIconPrefixCls(); if (messageInstance) { - callback({ prefixCls, rootPrefixCls, instance: messageInstance }); + callback({ prefixCls, rootPrefixCls, iconPrefixCls, instance: messageInstance }); return; } @@ -97,7 +99,7 @@ function getRCNotificationInstance( RCNotification.newInstance(instanceConfig, (instance: any) => { if (messageInstance) { - callback({ prefixCls, rootPrefixCls, instance: messageInstance }); + callback({ prefixCls, rootPrefixCls, iconPrefixCls, instance: messageInstance }); return; } messageInstance = instance; @@ -106,7 +108,7 @@ function getRCNotificationInstance( (messageInstance as any).config = instanceConfig; } - callback({ prefixCls, rootPrefixCls, instance }); + callback({ prefixCls, rootPrefixCls, iconPrefixCls, instance }); }); } @@ -139,7 +141,11 @@ export interface ArgsProps { onClick?: (e: React.MouseEvent) => void; } -function getRCNoticeProps(args: ArgsProps, prefixCls: string): NoticeContent { +function getRCNoticeProps( + args: ArgsProps, + prefixCls: string, + iconPrefixCls?: string, +): NoticeContent { const duration = args.duration !== undefined ? args.duration : defaultDuration; const IconComponent = typeToIcon[args.type]; const messageClass = classNames(`${prefixCls}-custom-content`, { @@ -152,10 +158,12 @@ function getRCNoticeProps(args: ArgsProps, prefixCls: string): NoticeContent { style: args.style || {}, className: args.className, content: ( -
- {args.icon || (IconComponent && )} - {args.content} -
+ +
+ {args.icon || (IconComponent && )} + {args.content} +
+
), onClose: args.onClose, onClick: args.onClick, @@ -172,8 +180,10 @@ function notice(args: ArgsProps): MessageType { return resolve(true); }; - getRCNotificationInstance(args, ({ prefixCls, instance }) => { - instance.notice(getRCNoticeProps({ ...args, key: target, onClose: callback }, prefixCls)); + getRCNotificationInstance(args, ({ prefixCls, iconPrefixCls, instance }) => { + instance.notice( + getRCNoticeProps({ ...args, key: target, onClose: callback }, prefixCls, iconPrefixCls), + ); }); }); const result: any = () => { diff --git a/components/modal/ConfirmDialog.tsx b/components/modal/ConfirmDialog.tsx index 11d7278afe..42a672e15c 100644 --- a/components/modal/ConfirmDialog.tsx +++ b/components/modal/ConfirmDialog.tsx @@ -11,6 +11,7 @@ interface ConfirmDialogProps extends ModalFuncProps { close: (...args: any[]) => void; autoFocusButton?: null | 'ok' | 'cancel'; rootPrefixCls: string; + iconPrefixCls?: string; } const ConfirmDialog = (props: ConfirmDialogProps) => { @@ -33,6 +34,7 @@ const ConfirmDialog = (props: ConfirmDialogProps) => { direction, prefixCls, rootPrefixCls, + iconPrefixCls, bodyStyle, closable = false, closeIcon, @@ -78,33 +80,33 @@ const ConfirmDialog = (props: ConfirmDialogProps) => { ); return ( - close({ triggerCancel: true })} - visible={visible} - title="" - footer="" - transitionName={getTransitionName(rootPrefixCls, 'zoom', props.transitionName)} - maskTransitionName={getTransitionName(rootPrefixCls, 'fade', props.maskTransitionName)} - mask={mask} - maskClosable={maskClosable} - maskStyle={maskStyle} - style={style} - width={width} - zIndex={zIndex} - afterClose={afterClose} - keyboard={keyboard} - centered={centered} - getContainer={getContainer} - closable={closable} - closeIcon={closeIcon} - modalRender={modalRender} - focusTriggerAfterClose={focusTriggerAfterClose} - > -
- + + close({ triggerCancel: true })} + visible={visible} + title="" + footer="" + transitionName={getTransitionName(rootPrefixCls, 'zoom', props.transitionName)} + maskTransitionName={getTransitionName(rootPrefixCls, 'fade', props.maskTransitionName)} + mask={mask} + maskClosable={maskClosable} + maskStyle={maskStyle} + style={style} + width={width} + zIndex={zIndex} + afterClose={afterClose} + keyboard={keyboard} + centered={centered} + getContainer={getContainer} + closable={closable} + closeIcon={closeIcon} + modalRender={modalRender} + focusTriggerAfterClose={focusTriggerAfterClose} + > +
{icon} {props.title === undefined ? null : ( @@ -112,22 +114,22 @@ const ConfirmDialog = (props: ConfirmDialogProps) => { )}
{props.content}
- -
- {cancelButton} - - {okText} - +
+ {cancelButton} + + {okText} + +
-
-
+
+ ); }; diff --git a/components/modal/__tests__/confirm.test.js b/components/modal/__tests__/confirm.test.js index 90c16e7872..7689d6ebf5 100644 --- a/components/modal/__tests__/confirm.test.js +++ b/components/modal/__tests__/confirm.test.js @@ -1,5 +1,7 @@ +import * as React from 'react'; import TestUtils, { act } from 'react-dom/test-utils'; import CSSMotion from 'rc-motion'; +import { SmileOutlined } from '@ant-design/icons'; import { genCSSMotion } from 'rc-motion/lib/CSSMotion'; import KeyCode from 'rc-util/lib/KeyCode'; import { resetWarned } from 'rc-util/lib/warning'; @@ -472,13 +474,14 @@ describe('Modal.confirm triggers callbacks correctly', () => { it('should be able to global config rootPrefixCls', () => { jest.useFakeTimers(); - ConfigProvider.config({ prefixCls: 'my' }); - confirm({ title: 'title' }); + ConfigProvider.config({ prefixCls: 'my', iconPrefixCls: 'bamboo' }); + confirm({ title: 'title', icon: }); jest.runAllTimers(); expect(document.querySelectorAll('.ant-btn').length).toBe(0); expect(document.querySelectorAll('.my-btn').length).toBe(2); + expect(document.querySelectorAll('.bamboo-smile').length).toBe(1); expect(document.querySelectorAll('.my-modal-confirm').length).toBe(1); - ConfigProvider.config({ prefixCls: 'ant' }); + ConfigProvider.config({ prefixCls: 'ant', iconPrefixCls: null }); jest.useRealTimers(); }); diff --git a/components/modal/confirm.tsx b/components/modal/confirm.tsx index d5bc1198a3..61ce3493b3 100644 --- a/components/modal/confirm.tsx +++ b/components/modal/confirm.tsx @@ -18,9 +18,7 @@ function getRootPrefixCls() { type ConfigUpdate = ModalFuncProps | ((prevConfig: ModalFuncProps) => ModalFuncProps); -export type ModalFunc = ( - props: ModalFuncProps, -) => { +export type ModalFunc = (props: ModalFuncProps) => { destroy: () => void; update: (configUpdate: ConfigUpdate) => void; }; @@ -60,16 +58,18 @@ export default function confirm(config: ModalFuncProps) { */ setTimeout(() => { const runtimeLocale = getConfirmLocale(); - const { getPrefixCls } = globalConfig(); + const { getPrefixCls, getIconPrefixCls } = globalConfig(); // because Modal.config  set rootPrefixCls, which is different from other components const rootPrefixCls = getPrefixCls(undefined, getRootPrefixCls()); const prefixCls = customizePrefixCls || `${rootPrefixCls}-modal`; + const iconPrefixCls = getIconPrefixCls(); ReactDOM.render( , diff --git a/components/notification/__tests__/index.test.js b/components/notification/__tests__/index.test.js index 5a35e0c217..205c2aedf0 100644 --- a/components/notification/__tests__/index.test.js +++ b/components/notification/__tests__/index.test.js @@ -102,11 +102,12 @@ describe('notification', () => { }); it('should be able to global config rootPrefixCls', () => { - ConfigProvider.config({ prefixCls: 'prefix-test' }); - notification.open({ message: 'Notification Title', duration: 0 }); - expect(document.querySelectorAll('.ant-notification-notice').length).toBe(0); - expect(document.querySelectorAll('.prefix-test-notification-notice').length).toBe(1); - ConfigProvider.config({ prefixCls: 'ant' }); + ConfigProvider.config({ prefixCls: 'prefix-test', iconPrefixCls: 'bamboo' }); + notification.success({ message: 'Notification Title', duration: 0 }); + expect(document.querySelectorAll('.ant-notification-notice')).toHaveLength(0); + expect(document.querySelectorAll('.prefix-test-notification-notice')).toHaveLength(1); + expect(document.querySelectorAll('.bamboo-check-circle')).toHaveLength(1); + ConfigProvider.config({ prefixCls: 'ant', iconPrefixCls: null }); }); it('should be able to config prefixCls', () => { @@ -117,8 +118,8 @@ describe('notification', () => { message: 'Notification Title', duration: 0, }); - expect(document.querySelectorAll('.ant-notification-notice').length).toBe(0); - expect(document.querySelectorAll('.prefix-test-notice').length).toBe(1); + expect(document.querySelectorAll('.ant-notification-notice')).toHaveLength(0); + expect(document.querySelectorAll('.prefix-test-notice')).toHaveLength(1); notification.config({ prefixCls: '', }); diff --git a/components/notification/index.tsx b/components/notification/index.tsx index ef885fcabc..c488a61f85 100755 --- a/components/notification/index.tsx +++ b/components/notification/index.tsx @@ -8,7 +8,7 @@ import CloseCircleOutlined from '@ant-design/icons/CloseCircleOutlined'; import ExclamationCircleOutlined from '@ant-design/icons/ExclamationCircleOutlined'; import InfoCircleOutlined from '@ant-design/icons/InfoCircleOutlined'; import createUseNotification from './hooks/useNotification'; -import { globalConfig } from '../config-provider'; +import ConfigProvider, { globalConfig } from '../config-provider'; export type NotificationPlacement = 'topLeft' | 'topRight' | 'bottomLeft' | 'bottomRight'; @@ -108,7 +108,11 @@ function getPlacementStyle( function getNotificationInstance( args: ArgsProps, - callback: (info: { prefixCls: string; instance: RCNotificationInstance }) => void, + callback: (info: { + prefixCls: string; + iconPrefixCls: string; + instance: RCNotificationInstance; + }) => void, ) { const { placement = defaultPlacement, @@ -118,14 +122,15 @@ function getNotificationInstance( closeIcon = defaultCloseIcon, prefixCls: customizePrefixCls, } = args; - const { getPrefixCls } = globalConfig(); + const { getPrefixCls, getIconPrefixCls } = globalConfig(); const prefixCls = getPrefixCls('notification', customizePrefixCls || defaultPrefixCls); + const iconPrefixCls = getIconPrefixCls(); const cacheKey = `${prefixCls}-${placement}`; const cacheInstance = notificationInstance[cacheKey]; if (cacheInstance) { Promise.resolve(cacheInstance).then(instance => { - callback({ prefixCls: `${prefixCls}-notice`, instance }); + callback({ prefixCls: `${prefixCls}-notice`, iconPrefixCls, instance }); }); return; @@ -154,6 +159,7 @@ function getNotificationInstance( resolve(notification); callback({ prefixCls: `${prefixCls}-notice`, + iconPrefixCls, instance: notification, }); }, @@ -188,7 +194,7 @@ export interface ArgsProps { closeIcon?: React.ReactNode; } -function getRCNoticeProps(args: ArgsProps, prefixCls: string) { +function getRCNoticeProps(args: ArgsProps, prefixCls: string, iconPrefixCls?: string) { const { duration: durationArg, icon, @@ -221,15 +227,17 @@ function getRCNoticeProps(args: ArgsProps, prefixCls: string) { return { content: ( -
- {iconNode} -
- {autoMarginTag} - {message} + +
+ {iconNode} +
+ {autoMarginTag} + {message} +
+
{description}
+ {btn ? {btn} : null}
-
{description}
- {btn ? {btn} : null} -
+ ), duration, closable: true, @@ -244,8 +252,8 @@ function getRCNoticeProps(args: ArgsProps, prefixCls: string) { } function notice(args: ArgsProps) { - getNotificationInstance(args, ({ prefixCls, instance }) => { - instance.notice(getRCNoticeProps(args, prefixCls)); + getNotificationInstance(args, ({ prefixCls, iconPrefixCls, instance }) => { + instance.notice(getRCNoticeProps(args, prefixCls, iconPrefixCls)); }); } From 0b1f4da2ebf2fb19d0a881aed19112b72a44b1b6 Mon Sep 17 00:00:00 2001 From: Jehu Date: Thu, 10 Jun 2021 00:38:55 +0800 Subject: [PATCH 08/22] feat: add skeleton btn block (#30902) close #30832 --- components/skeleton/Button.tsx | 4 +- .../__tests__/__snapshots__/demo.test.js.snap | 60 +++++++++++++++---- .../__snapshots__/index.test.js.snap | 10 ++++ components/skeleton/__tests__/index.test.js | 4 ++ components/skeleton/demo/element.md | 16 ++++- components/skeleton/index.en-US.md | 15 ++--- components/skeleton/index.zh-CN.md | 25 ++++---- components/skeleton/style/index.less | 11 ++++ 8 files changed, 110 insertions(+), 35 deletions(-) diff --git a/components/skeleton/Button.tsx b/components/skeleton/Button.tsx index f357680308..4f51d08ab9 100644 --- a/components/skeleton/Button.tsx +++ b/components/skeleton/Button.tsx @@ -6,11 +6,12 @@ import { ConfigConsumer, ConfigConsumerProps } from '../config-provider'; export interface SkeletonButtonProps extends Omit { size?: 'large' | 'small' | 'default'; + block?: boolean; } const SkeletonButton = (props: SkeletonButtonProps) => { const renderSkeletonButton = ({ getPrefixCls }: ConfigConsumerProps) => { - const { prefixCls: customizePrefixCls, className, active } = props; + const { prefixCls: customizePrefixCls, className, active, block = false } = props; const prefixCls = getPrefixCls('skeleton', customizePrefixCls); const otherProps = omit(props, ['prefixCls']); const cls = classNames( @@ -18,6 +19,7 @@ const SkeletonButton = (props: SkeletonButtonProps) => { `${prefixCls}-element`, { [`${prefixCls}-active`]: active, + [`${prefixCls}-block`]: block, }, className, ); diff --git a/components/skeleton/__tests__/__snapshots__/demo.test.js.snap b/components/skeleton/__tests__/__snapshots__/demo.test.js.snap index c36b365457..d93c28bb3c 100644 --- a/components/skeleton/__tests__/__snapshots__/demo.test.js.snap +++ b/components/skeleton/__tests__/__snapshots__/demo.test.js.snap @@ -118,18 +118,6 @@ Array [ />
-
-
- -
-
,
,
, +
+ +
, +
, +
,
@@ -222,6 +219,45 @@ Array [
+
+
+ +
+
+
+
+ +
+
+
+
diff --git a/components/skeleton/__tests__/__snapshots__/index.test.js.snap b/components/skeleton/__tests__/__snapshots__/index.test.js.snap index e248c8ebdd..92219002db 100644 --- a/components/skeleton/__tests__/__snapshots__/index.test.js.snap +++ b/components/skeleton/__tests__/__snapshots__/index.test.js.snap @@ -250,6 +250,16 @@ exports[`Skeleton button element active 1`] = `
`; +exports[`Skeleton button element block 1`] = ` +
+ +
+`; + exports[`Skeleton button element shape 1`] = `
{ const wrapper = genSkeletonButton({ active: true }); expect(wrapper.render()).toMatchSnapshot(); }); + it('block', () => { + const wrapper = genSkeletonButton({ block: true }); + expect(wrapper.render()).toMatchSnapshot(); + }); it('size', () => { const wrapperDefault = genSkeletonButton({ size: 'default' }); expect(wrapperDefault.render()).toMatchSnapshot(); diff --git a/components/skeleton/demo/element.md b/components/skeleton/demo/element.md index 6828f93965..a1978de629 100644 --- a/components/skeleton/demo/element.md +++ b/components/skeleton/demo/element.md @@ -19,6 +19,7 @@ import { Skeleton, Space, Divider, Switch, Form, Radio } from 'antd'; class Demo extends React.Component { state = { active: false, + block: false, size: 'default', buttonShape: 'default', avatarShape: 'circle', @@ -28,6 +29,10 @@ class Demo extends React.Component { this.setState({ active: checked }); }; + handleBlockChange = checked => { + this.setState({ block: checked }); + }; + handleSizeChange = e => { this.setState({ size: e.target.value }); }; @@ -37,23 +42,28 @@ class Demo extends React.Component { }; render() { - const { active, size, buttonShape, avatarShape } = this.state; + const { active, size, buttonShape, avatarShape, block } = this.state; return ( <> - - +

+ +
+
+ + + Default diff --git a/components/skeleton/index.en-US.md b/components/skeleton/index.en-US.md index 4507aa96fb..f192d8c047 100644 --- a/components/skeleton/index.en-US.md +++ b/components/skeleton/index.en-US.md @@ -38,9 +38,9 @@ Provide a placeholder while you wait for content to load, or to visualise conten ### SkeletonTitleProps -| Property | Description | Type | Default | -| --- | --- | --- | --- | -| width | Set the width of title | number \| string | - | +| Property | Description | Type | Default | +| -------- | ---------------------- | ---------------- | ------- | +| width | Set the width of title | number \| string | - | ### SkeletonParagraphProps @@ -54,12 +54,13 @@ Provide a placeholder while you wait for content to load, or to visualise conten | Property | Description | Type | Default | | --- | --- | --- | --- | | active | Show animation effect | boolean | false | +| block | Option to fit button width to its parent width | boolean | false | | shape | Set the shape of button | `circle` \| `round` \| `default` | - | | size | Set the size of button | `large` \| `small` \| `default` | - | ### SkeletonInputProps -| Property | Description | Type | Default | -| --- | --- | --- | --- | -| active | Show animation effect | boolean | false | -| size | Set the size of input | `large` \| `small` \| `default` | - | +| Property | Description | Type | Default | +| -------- | --------------------- | ------------------------------- | ------- | +| active | Show animation effect | boolean | false | +| size | Set the size of input | `large` \| `small` \| `default` | - | diff --git a/components/skeleton/index.zh-CN.md b/components/skeleton/index.zh-CN.md index 1e580da146..34a3c9ae87 100644 --- a/components/skeleton/index.zh-CN.md +++ b/components/skeleton/index.zh-CN.md @@ -39,9 +39,9 @@ cover: https://gw.alipayobjects.com/zos/alicdn/KpcciCJgv/Skeleton.svg ### SkeletonTitleProps -| 属性 | 说明 | 类型 | 默认值 | -| --- | --- | --- | --- | -| width | 设置标题占位图的宽度 | number \| string | - | +| 属性 | 说明 | 类型 | 默认值 | +| ----- | -------------------- | ---------------- | ------ | +| width | 设置标题占位图的宽度 | number \| string | - | ### SkeletonParagraphProps @@ -52,15 +52,16 @@ cover: https://gw.alipayobjects.com/zos/alicdn/KpcciCJgv/Skeleton.svg ### SkeletonButtonProps -| 属性 | 说明 | 类型 | 默认值 | -| --- | --- | --- | --- | -| active | 是否展示动画效果 | boolean | false | -| shape | 指定按钮的形状 | `circle` \| `round` \| `default` | - | -| size | 设置按钮的大小 | `large` \| `small` \| `default` | - | +| 属性 | 说明 | 类型 | 默认值 | +| ------ | ------------------------------ | -------------------------------- | ------ | +| active | 是否展示动画效果 | boolean | false | +| block | 将按钮宽度调整为其父宽度的选项 | boolean | false | +| shape | 指定按钮的形状 | `circle` \| `round` \| `default` | - | +| size | 设置按钮的大小 | `large` \| `small` \| `default` | - | ### SkeletonInputProps -| 属性 | 说明 | 类型 | 默认值 | -| --- | --- | --- | --- | -| active | 是否展示动画效果 | boolean | false | -| size | 设置输入框的大小 | `large` \| `small` \| `default` | - | +| 属性 | 说明 | 类型 | 默认值 | +| ------ | ---------------- | ------------------------------- | ------ | +| active | 是否展示动画效果 | boolean | false | +| size | 设置输入框的大小 | `large` \| `small` \| `default` | - | diff --git a/components/skeleton/style/index.less b/components/skeleton/style/index.less index 39e31053f7..0eed9f5b15 100644 --- a/components/skeleton/style/index.less +++ b/components/skeleton/style/index.less @@ -109,6 +109,15 @@ } } + // Skeleton Block Button + &.@{skeleton-prefix-cls}-block { + width: 100%; + + .@{skeleton-button-prefix-cls} { + width: 100%; + } + } + // Skeleton element &-element { display: inline-block; @@ -214,10 +223,12 @@ .skeleton-element-button-size(@size) { width: @size * 2; + min-width: @size * 2; .skeleton-element-common-size(@size); &.@{skeleton-button-prefix-cls}-circle { width: @size; + min-width: @size; border-radius: 50%; } From 62949a6bd61140f0f6fac5b3e347372b54b4a067 Mon Sep 17 00:00:00 2001 From: afc163 Date: Fri, 11 Jun 2021 08:39:17 +0800 Subject: [PATCH 09/22] feat: change Drawer close icon placement and add extra property (#30908) * feat: Drawer supports extra * docs: add description for drawer extra demo * docs: add extra in API * update snapshot * fix rtl style * fix demo typescript errors * update snapshot * design for close icon only * update snapshot * update demo and default width * add size prop * improve demo * update snapshot * update demo * update snapshot --- .../__snapshots__/components.test.js.snap | 318 +++++++------ .../__snapshots__/Drawer.test.js.snap | 449 ++++++++++-------- .../__snapshots__/DrawerEvent.test.js.snap | 53 ++- .../__tests__/__snapshots__/demo.test.js.snap | 149 +++++- components/drawer/demo/basic-right.md | 10 +- components/drawer/demo/extra.md | 71 +++ components/drawer/demo/form-in-drawer.md | 20 +- components/drawer/demo/placement.md | 4 +- components/drawer/demo/render-in-current.md | 4 +- components/drawer/demo/size.md | 69 +++ components/drawer/index.en-US.md | 4 +- components/drawer/index.tsx | 57 +-- components/drawer/index.zh-CN.md | 4 +- components/drawer/style/drawer.less | 31 +- components/drawer/style/rtl.less | 4 +- 15 files changed, 784 insertions(+), 463 deletions(-) create mode 100644 components/drawer/demo/extra.md create mode 100644 components/drawer/demo/size.md diff --git a/components/config-provider/__tests__/__snapshots__/components.test.js.snap b/components/config-provider/__tests__/__snapshots__/components.test.js.snap index 072278c75d..1b0e3748bc 100644 --- a/components/config-provider/__tests__/__snapshots__/components.test.js.snap +++ b/components/config-provider/__tests__/__snapshots__/components.test.js.snap @@ -12211,7 +12211,7 @@ exports[`ConfigProvider components Drawer configProvider 1`] = ` />
- + + + +
- + + + +
- + + + +
- + + + +
- + + + +
- + + + +
- + + + +
- + + + +
- + + + +
- + + + +
- Test Title -
- + + + +
+ Test Title +
+
- + + + +
- + + + +
- + + + +
- + +
- + + + +
`; +exports[`renders ./components/drawer/demo/extra.md correctly 1`] = ` +
+
+
+ + + + +
+
+
+ +
+
+`; + exports[`renders ./components/drawer/demo/form-in-drawer.md correctly 1`] = ` `; @@ -217,7 +322,7 @@ exports[`renders ./components/drawer/demo/render-in-current.md correctly 1`] = ` />
- Basic Drawer +
+ Basic Drawer +
`; +exports[`renders ./components/drawer/demo/size.md correctly 1`] = ` +
+
+ +
+
+ +
+
+`; + exports[`renders ./components/drawer/demo/user-profile.md correctly 1`] = `
{ - +

Some contents...

Some contents...

Some contents...

diff --git a/components/drawer/demo/extra.md b/components/drawer/demo/extra.md new file mode 100644 index 0000000000..d346bd0628 --- /dev/null +++ b/components/drawer/demo/extra.md @@ -0,0 +1,71 @@ +--- +order: 1.1 +title: + zh-CN: 额外操作 + en-US: Extra Actions +--- + +## zh-CN + +在 Ant Design 规范中,操作按钮建议放在抽屉的右上角,可以使用 `extra` 属性来实现。 + +## en-US + +Extra actions should be placed at corner of drawer in Ant Design, you can using `extra` prop for that. + +```tsx +import React, { useState } from 'react'; +import { Drawer, Button, Space, Radio } from 'antd'; +import { DrawerProps } from 'antd/es/drawer'; +import { RadioChangeEvent } from 'antd/es/radio'; + +const App: React.FC = () => { + const [visible, setVisible] = useState(false); + const [placement, setPlacement] = useState('right'); + const showDrawer = () => { + setVisible(true); + }; + const onChange = (e: RadioChangeEvent) => { + setPlacement(e.target.value); + }; + const onClose = () => { + setVisible(false); + }; + return ( + <> + + + top + right + bottom + left + + + + + + + + } + > +

Some contents...

+

Some contents...

+

Some contents...

+
+ + ); +}; + +ReactDOM.render(, mountNode); +``` diff --git a/components/drawer/demo/form-in-drawer.md b/components/drawer/demo/form-in-drawer.md index 7d38c01481..262575becf 100644 --- a/components/drawer/demo/form-in-drawer.md +++ b/components/drawer/demo/form-in-drawer.md @@ -14,7 +14,7 @@ title: Use a form in Drawer with a submit button. ```jsx -import { Drawer, Form, Button, Col, Row, Input, Select, DatePicker } from 'antd'; +import { Drawer, Form, Button, Col, Row, Input, Select, DatePicker, Space } from 'antd'; import { PlusOutlined } from '@ant-design/icons'; const { Option } = Select; @@ -37,8 +37,8 @@ class DrawerForm extends React.Component { render() { return ( <> - - + extra={ + + -
+ } > diff --git a/components/drawer/demo/placement.md b/components/drawer/demo/placement.md index bf8e891f0f..65d0821aa8 100644 --- a/components/drawer/demo/placement.md +++ b/components/drawer/demo/placement.md @@ -7,7 +7,7 @@ title: ## zh-CN -自定义位置,点击触发按钮抽屉从相应的位置滑出,点击遮罩区关闭 +自定义位置,点击触发按钮抽屉从相应的位置滑出,点击遮罩区关闭。 ## en-US @@ -42,7 +42,7 @@ class App extends React.Component { return ( <> - + top right bottom diff --git a/components/drawer/demo/render-in-current.md b/components/drawer/demo/render-in-current.md index 10aaac0d02..f454857ff3 100644 --- a/components/drawer/demo/render-in-current.md +++ b/components/drawer/demo/render-in-current.md @@ -7,11 +7,11 @@ title: ## zh-CN -渲染在当前 dom 里。自定义容器,查看 getContainer。 +渲染在当前 dom 里。自定义容器,查看 `getContainer`。 ## en-US -Render in current dom. custom container, check getContainer. +Render in current dom. custom container, check `getContainer`. ```jsx import { Drawer, Button } from 'antd'; diff --git a/components/drawer/demo/size.md b/components/drawer/demo/size.md new file mode 100644 index 0000000000..5be09bb39f --- /dev/null +++ b/components/drawer/demo/size.md @@ -0,0 +1,69 @@ +--- +order: 10 +title: + zh-CN: 预设宽度 + en-US: Presetted size +--- + +## zh-CN + +抽屉的默认宽度为 `378px`,另外还提供一个大号抽屉 `736px`,可以用 `size` 属性来设置。 + +## en-US + +The default width (or height) of Drawer is `378px`, and there is a presetted large size `736px`. + +```tsx +import React, { useState } from 'react'; +import { Drawer, Button, Space } from 'antd'; +import { DrawerProps } from 'antd/es/drawer'; + +const App: React.FC = () => { + const [visible, setVisible] = useState(false); + const [size, setSize] = useState(); + const showDefaultDrawer = () => { + setSize('default'); + setVisible(true); + }; + const showLargeDrawer = () => { + setSize('large'); + setVisible(true); + }; + const onClose = () => { + setVisible(false); + }; + return ( + <> + + + + + + + + + } + > +

Some contents...

+

Some contents...

+

Some contents...

+ + + ); +}; + +ReactDOM.render(, mountNode); +``` diff --git a/components/drawer/index.en-US.md b/components/drawer/index.en-US.md index 0c5bebdfa5..d89889927c 100644 --- a/components/drawer/index.en-US.md +++ b/components/drawer/index.en-US.md @@ -28,6 +28,7 @@ A Drawer is a panel that is typically overlaid on top of a page and slides in fr | contentWrapperStyle | Style of the drawer wrapper of content part | CSSProperties | - | | | destroyOnClose | Whether to unmount child components on closing drawer or not | boolean | false | | | drawerStyle | Style of the popup layer element | object | - | | +| extra | Extra actions area at corner | ReactNode | - | 4.17.0 | | footer | The footer for Drawer | ReactNode | - | | | footerStyle | Style of the drawer footer part | CSSProperties | - | | | forceRender | Prerender Drawer component forcely | boolean | false | | @@ -41,8 +42,9 @@ A Drawer is a panel that is typically overlaid on top of a page and slides in fr | placement | The placement of the Drawer | `top` \| `right` \| `bottom` \| `left` | `right` | | | push | Nested drawers push behavior | boolean \| { distance: string \| number } | { distance: 180 } | 4.5.0+ | | style | Style of wrapper element which **contains mask** compare to `drawerStyle` | CSSProperties | - | | +| size | presetted size of drawer, default `378px` and large `736px` | 'default' \| 'large' | 'default' | 4.17.0 | | title | The title for Drawer | ReactNode | - | | | visible | Whether the Drawer dialog is visible or not | boolean | false | | -| width | Width of the Drawer dialog | string \| number | 256 | | +| width | Width of the Drawer dialog | string \| number | 378 | | | zIndex | The `z-index` of the Drawer | number | 1000 | | | onClose | Specify a callback that will be called when a user clicks mask, close button or Cancel button | function(e) | - | | diff --git a/components/drawer/index.tsx b/components/drawer/index.tsx index a4f87f1649..a3052bffec 100644 --- a/components/drawer/index.tsx +++ b/components/drawer/index.tsx @@ -1,6 +1,5 @@ import * as React from 'react'; import RcDrawer from 'rc-drawer'; -import getScrollBarSize from 'rc-util/lib/getScrollBarSize'; import CloseOutlined from '@ant-design/icons/CloseOutlined'; import classNames from 'classnames'; import { ConfigContext, DirectionType } from '../config-provider'; @@ -23,6 +22,9 @@ type getContainerFunc = () => HTMLElement; const PlacementTypes = tuple('top', 'right', 'bottom', 'left'); type placementType = typeof PlacementTypes[number]; +const SizeTypes = tuple('default', 'large'); +type sizeType = typeof SizeTypes[number]; + export interface PushState { distance: string | number; } @@ -36,6 +38,7 @@ export interface DrawerProps { mask?: boolean; maskStyle?: React.CSSProperties; style?: React.CSSProperties; + size?: sizeType; /** Wrapper dom node style of header and body */ drawerStyle?: React.CSSProperties; headerStyle?: React.CSSProperties; @@ -54,6 +57,7 @@ export interface DrawerProps { className?: string; handler?: React.ReactNode; keyboard?: boolean; + extra?: React.ReactNode; footer?: React.ReactNode; footerStyle?: React.CSSProperties; level?: string | string[] | null | undefined; @@ -72,8 +76,9 @@ const defaultPushState: PushState = { distance: 180 }; const Drawer = React.forwardRef( ( { - width = 256, - height = 256, + width, + height, + size = 'default', closable = true, placement = 'right' as placementType, maskClosable = true, @@ -97,6 +102,7 @@ const Drawer = React.forwardRef( onClose, footer, footerStyle, + extra, ...rest }, ref, @@ -168,9 +174,11 @@ const Drawer = React.forwardRef( } const offsetStyle: any = {}; if (placement === 'left' || placement === 'right') { - offsetStyle.width = width; + const defaultWidth = size === 'large' ? 736 : 378; + offsetStyle.width = typeof width === 'undefined' ? defaultWidth : width; } else { - offsetStyle.height = height; + const defaultHeight = size === 'large' ? 736 : 378; + offsetStyle.height = typeof height === 'undefined' ? defaultHeight : height; } return offsetStyle; }; @@ -205,36 +213,29 @@ const Drawer = React.forwardRef( }; }; - function renderCloseIcon() { - return ( - closable && ( - - ) - ); - } + const closeIconNode = closable && ( + + ); function renderHeader() { if (!title && !closable) { return null; } - const headerClassName = title ? `${prefixCls}-header` : `${prefixCls}-header-no-title`; return ( -
- {title &&
{title}
} - {closable && renderCloseIcon()} +
+
+ {closeIconNode} + {title &&
{title}
} +
+ {extra &&
{extra}
}
); } diff --git a/components/drawer/index.zh-CN.md b/components/drawer/index.zh-CN.md index c084bfa4cb..61ed1c3a1d 100644 --- a/components/drawer/index.zh-CN.md +++ b/components/drawer/index.zh-CN.md @@ -27,6 +27,7 @@ cover: https://gw.alipayobjects.com/zos/alicdn/7z8NJQhFb/Drawer.svg | contentWrapperStyle | 可用于设置 Drawer 包裹内容部分的样式 | CSSProperties | - | | | destroyOnClose | 关闭时销毁 Drawer 里的子元素 | boolean | false | | | drawerStyle | 用于设置 Drawer 弹出层的样式 | CSSProperties | - | | +| extra | 抽屉右上角的操作区域 | ReactNode | - | 4.17.0 | | footer | 抽屉的页脚 | ReactNode | - | | | footerStyle | 抽屉页脚部件的样式 | CSSProperties | - | | | forceRender | 预渲染 Drawer 内元素 | boolean | false | | @@ -39,9 +40,10 @@ cover: https://gw.alipayobjects.com/zos/alicdn/7z8NJQhFb/Drawer.svg | maskStyle | 遮罩样式 | CSSProperties | {} | | | placement | 抽屉的方向 | `top` \| `right` \| `bottom` \| `left` | `right` | | | push | 用于设置多层 Drawer 的推动行为 | boolean \| { distance: string \| number } | { distance: 180 } | 4.5.0+ | +| size | 预设抽屉宽度(或高度),default `378px` 和 large `736px` | 'default' \| 'large' | 'default' | 4.17.0 | | style | 可用于设置 Drawer 最外层容器的样式,和 `drawerStyle` 的区别是作用节点包括 `mask` | CSSProperties | - | | | title | 标题 | ReactNode | - | | | visible | Drawer 是否可见 | boolean | - | | -| width | 宽度 | string \| number | 256 | | +| width | 宽度 | string \| number | 378 | | | zIndex | 设置 Drawer 的 `z-index` | number | 1000 | | | onClose | 点击遮罩层或右上角叉或取消按钮的回调 | function(e) | - | | diff --git a/components/drawer/style/drawer.less b/components/drawer/style/drawer.less index 45f824bb22..e63531a49b 100644 --- a/components/drawer/style/drawer.less +++ b/components/drawer/style/drawer.less @@ -148,12 +148,8 @@ } &-close { - position: absolute; - top: 0; - right: 0; - z-index: @zindex-popup-close; - display: block; - padding: @drawer-header-close-padding; + display: inline-block; + margin-right: 12px; color: @modal-close-color; font-weight: 700; font-size: @font-size-lg; @@ -174,26 +170,29 @@ color: @icon-color-hover; text-decoration: none; } - - .@{drawer-prefix-cls}-header-no-title & { - margin-right: var(--scroll-bar); - /* stylelint-disable-next-line function-calc-no-invalid */ - padding-right: ~'calc(@{drawer-header-close-padding} - var(--scroll-bar))'; - } } &-header { position: relative; + display: flex; + align-items: center; + justify-content: space-between; padding: @drawer-header-padding; color: @text-color; background: @drawer-bg; border-bottom: @border-width-base @border-style-base @border-color-split; border-radius: @border-radius-base @border-radius-base 0 0; - } - &-header-no-title { - color: @text-color; - background: @drawer-bg; + &-title { + display: flex; + align-items: center; + justify-content: space-between; + } + + &-close-only { + padding-bottom: 0; + border: none; + } } &-wrapper-body { diff --git a/components/drawer/style/rtl.less b/components/drawer/style/rtl.less index 5963c8a7c1..f710bfa7dd 100644 --- a/components/drawer/style/rtl.less +++ b/components/drawer/style/rtl.less @@ -9,8 +9,8 @@ &-close { .@{drawer-prefix-cls}-rtl & { - right: auto; - left: 0; + margin-right: 0; + margin-left: 12px; } } } From ba8eac0b7b412a2535bb435b388ecafa2b022ef8 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E4=BA=8C=E8=B4=A7=E6=9C=BA=E5=99=A8=E4=BA=BA?= Date: Thu, 17 Jun 2021 21:45:03 +0800 Subject: [PATCH 10/22] feat: InputNumber formatter support additional info (#31030) --- components/input-number/index.en-US.md | 2 +- components/input-number/index.zh-CN.md | 2 +- package.json | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/components/input-number/index.en-US.md b/components/input-number/index.en-US.md index fc13a9bcb5..a62563137b 100644 --- a/components/input-number/index.en-US.md +++ b/components/input-number/index.en-US.md @@ -20,7 +20,7 @@ When a numeric value needs to be provided. | decimalSeparator | Decimal separator | string | - | - | | defaultValue | The initial value | number | - | - | | disabled | If disable the input | boolean | false | - | -| formatter | Specifies the format of the value presented | function(value: number \| string): string | - | - | +| formatter | Specifies the format of the value presented | function(value: number \| string, info: { userTyping: boolean, input: string }): string | - | info: 4.17.0 | | keyboard | If enable keyboard behavior | boolean | true | 4.12.0 | | max | The max value | number | [Number.MAX_SAFE_INTEGER](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Number/MAX_SAFE_INTEGER) | - | | min | The min value | number | [Number.MIN_SAFE_INTEGER](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Number/MIN_SAFE_INTEGER) | - | diff --git a/components/input-number/index.zh-CN.md b/components/input-number/index.zh-CN.md index ef5266e4e3..0e6e122d31 100644 --- a/components/input-number/index.zh-CN.md +++ b/components/input-number/index.zh-CN.md @@ -23,7 +23,7 @@ cover: https://gw.alipayobjects.com/zos/alicdn/XOS8qZ0kU/InputNumber.svg | decimalSeparator | 小数点 | string | - | - | | defaultValue | 初始值 | number | - | - | | disabled | 禁用 | boolean | false | - | -| formatter | 指定输入框展示值的格式 | function(value: number \| string): string | - | - | +| formatter | 指定输入框展示值的格式 | function(value: number \| string, info: { userTyping: boolean, input: string }): string | - | info: 4.17.0 | | keyboard | 是否启用键盘快捷行为 | boolean | true | 4.12.0 | | max | 最大值 | number | [Number.MAX_SAFE_INTEGER](https://developer.mozilla.org/zh-CN/docs/Web/JavaScript/Reference/Global_Objects/Number/MAX_SAFE_INTEGER) | - | | min | 最小值 | number | [Number.MIN_SAFE_INTEGER](https://developer.mozilla.org/zh-CN/docs/Web/JavaScript/Reference/Global_Objects/Number/MIN_SAFE_INTEGER) | - | diff --git a/package.json b/package.json index d0617de808..c46ae67079 100644 --- a/package.json +++ b/package.json @@ -124,7 +124,7 @@ "rc-dropdown": "~3.2.0", "rc-field-form": "~1.21.0-2", "rc-image": "~5.2.4", - "rc-input-number": "~7.1.0", + "rc-input-number": "~7.2.0", "rc-mentions": "~1.6.1", "rc-menu": "~9.0.9", "rc-motion": "^2.4.4", From a350421b6a0f27d3c44f68366a548ae881afae7f Mon Sep 17 00:00:00 2001 From: Jehu Date: Wed, 23 Jun 2021 17:38:16 +0800 Subject: [PATCH 11/22] feat: Transfer support locale.notFoundContent as array (#31088) * feat: transfer support locale.notFoundContent as array * feat: transfer support locale.notFoundContent as array with test --- components/transfer/__tests__/index.test.js | 13 +++++++++++++ components/transfer/index.en-US.md | 2 +- components/transfer/index.tsx | 2 +- components/transfer/index.zh-CN.md | 2 +- components/transfer/list.tsx | 11 ++++++++--- 5 files changed, 24 insertions(+), 6 deletions(-) diff --git a/components/transfer/__tests__/index.test.js b/components/transfer/__tests__/index.test.js index fbb5a6ccb8..db98cb72ab 100644 --- a/components/transfer/__tests__/index.test.js +++ b/components/transfer/__tests__/index.test.js @@ -283,6 +283,19 @@ describe('Transfer', () => { expect(headerText(wrapper)).toEqual('1/2 People'); }); + it('should display the correct notFoundContent', () => { + const wrapper = mount( + , + ); + + expect( + wrapper.find(TransferList).at(0).find('.ant-transfer-list-body-not-found').at(0).text(), + ).toEqual('No Source'); + expect( + wrapper.find(TransferList).at(1).find('.ant-transfer-list-body-not-found').at(0).text(), + ).toEqual('No Target'); + }); + it('should just check the filtered item when click on check all after search by input', () => { const filterOption = (inputValue, option) => option.description.indexOf(inputValue) > -1; const renderFunc = item => item.title; diff --git a/components/transfer/index.en-US.md b/components/transfer/index.en-US.md index ce924e4d2f..72939a9990 100644 --- a/components/transfer/index.en-US.md +++ b/components/transfer/index.en-US.md @@ -26,7 +26,7 @@ One or more elements can be selected from either column, one click on the proper | filterOption | A function to determine whether an item should show in search result list | (inputValue, option): boolean | - | | | footer | A function used for rendering the footer | (props) => ReactNode | - | | | listStyle | A custom CSS style used for rendering the transfer columns | object \| ({direction: `left` \| `right`}) => object | - | | -| locale | The i18n text including filter, empty text, item unit, etc | { itemUnit: string; itemsUnit: string; searchPlaceholder: string; notFoundContent: ReactNode; } | { itemUnit: `item`, itemsUnit: `items`, notFoundContent: `The list is empty`, searchPlaceholder: `Search here` } | | +| locale | The i18n text including filter, empty text, item unit, etc | { itemUnit: string; itemsUnit: string; searchPlaceholder: string; notFoundContent: ReactNode \| ReactNode[]; } | { itemUnit: `item`, itemsUnit: `items`, notFoundContent: `The list is empty`, searchPlaceholder: `Search here` } | | | oneWay | Display as single direction style | boolean | false | 4.3.0 | | operations | A set of operations that are sorted from top to bottom | string\[] | \[`>`, `<`] | | | operationStyle | A custom CSS style used for rendering the operations column | object | - | | diff --git a/components/transfer/index.tsx b/components/transfer/index.tsx index 10a32e3110..6feb72bdb1 100644 --- a/components/transfer/index.tsx +++ b/components/transfer/index.tsx @@ -47,7 +47,7 @@ export type SelectAllLabel = export interface TransferLocale { titles: React.ReactNode[]; - notFoundContent?: React.ReactNode; + notFoundContent?: React.ReactNode | React.ReactNode[]; searchPlaceholder: string; itemUnit: string; itemsUnit: string; diff --git a/components/transfer/index.zh-CN.md b/components/transfer/index.zh-CN.md index 6a6cebbda7..21171cc209 100644 --- a/components/transfer/index.zh-CN.md +++ b/components/transfer/index.zh-CN.md @@ -29,7 +29,7 @@ cover: https://gw.alipayobjects.com/zos/alicdn/QAXskNI4G/Transfer.svg | filterOption | 接收 `inputValue` `option` 两个参数,当 `option` 符合筛选条件时,应返回 true,反之则返回 false | (inputValue, option): boolean | - | | | footer | 底部渲染函数 | (props) => ReactNode | - | | | listStyle | 两个穿梭框的自定义样式 | object\|({direction: `left` \| `right`}) => object | - | | -| locale | 各种语言 | { itemUnit: string; itemsUnit: string; searchPlaceholder: string; notFoundContent: ReactNode; } | { itemUnit: `项`, itemsUnit: `项`, searchPlaceholder: `请输入搜索内容` } | | +| locale | 各种语言 | { itemUnit: string; itemsUnit: string; searchPlaceholder: string; notFoundContent: ReactNode \| ReactNode[]; } | { itemUnit: `项`, itemsUnit: `项`, searchPlaceholder: `请输入搜索内容` } | | | oneWay | 展示为单向样式 | boolean | false | 4.3.0 | | operations | 操作文案集合,顺序从上至下 | string\[] | \[`>`, `<`] | | | pagination | 使用分页样式,自定义渲染列表下无效 | boolean \| { pageSize: number } | false | 4.3.0 | diff --git a/components/transfer/list.tsx b/components/transfer/list.tsx index 1ed6990c15..3a67d5bfd8 100644 --- a/components/transfer/list.tsx +++ b/components/transfer/list.tsx @@ -75,7 +75,7 @@ interface TransferListState { } export default class TransferList< - RecordType extends KeyWiseTransferItem + RecordType extends KeyWiseTransferItem, > extends React.PureComponent, TransferListState> { static defaultProps = { dataSource: [], @@ -183,7 +183,7 @@ export default class TransferList< searchPlaceholder: string, filterValue: string, filteredItems: RecordType[], - notFoundContent: React.ReactNode, + notFoundContent: React.ReactNode | React.ReactNode, filteredRenderItems: RenderedItem[], checkedKeys: string[], renderList?: RenderListFunction, @@ -210,6 +210,11 @@ export default class TransferList< selectedKeys: checkedKeys, }); + const getNotFoundContent = () => { + const contentIndex = this.props.direction === 'left' ? 0 : 1; + return Array.isArray(notFoundContent) ? notFoundContent[contentIndex] : notFoundContent; + }; + let bodyNode: React.ReactNode; // We should wrap customize list body in a classNamed div to use flex layout. if (customize) { @@ -218,7 +223,7 @@ export default class TransferList< bodyNode = filteredItems.length ? ( bodyContent ) : ( -
{notFoundContent}
+
{getNotFoundContent()}
); } From 5d8b46ce852071f48708dda307f7d4fee1b2ecbc Mon Sep 17 00:00:00 2001 From: xrkffgg Date: Wed, 30 Jun 2021 12:10:23 +0800 Subject: [PATCH 12/22] feat: Transfer support customize footer (#31108) close #28082 --- .../__tests__/__snapshots__/demo.test.js.snap | 6 +++--- components/transfer/demo/advanced.md | 19 ++++++++++++++----- components/transfer/index.en-US.md | 2 +- components/transfer/index.zh-CN.md | 2 +- components/transfer/list.tsx | 9 +++++++-- 5 files changed, 26 insertions(+), 12 deletions(-) diff --git a/components/transfer/__tests__/__snapshots__/demo.test.js.snap b/components/transfer/__tests__/__snapshots__/demo.test.js.snap index bd95e5f856..6ecc28cbf1 100644 --- a/components/transfer/__tests__/__snapshots__/demo.test.js.snap +++ b/components/transfer/__tests__/__snapshots__/demo.test.js.snap @@ -146,11 +146,11 @@ exports[`renders ./components/transfer/demo/advanced.md correctly 1`] = ` >
@@ -361,7 +361,7 @@ exports[`renders ./components/transfer/demo/advanced.md correctly 1`] = ` type="button" > - reload + Right button reload
diff --git a/components/transfer/demo/advanced.md b/components/transfer/demo/advanced.md index 670e85fe6d..f80dc3e5a8 100644 --- a/components/transfer/demo/advanced.md +++ b/components/transfer/demo/advanced.md @@ -50,11 +50,20 @@ class App extends React.Component { this.setState({ targetKeys }); }; - renderFooter = () => ( - - ); + renderFooter = (props, { direction }) => { + if (direction === 'left') { + return ( + + ); + } + return ( + + ); + }; render() { return ( diff --git a/components/transfer/index.en-US.md b/components/transfer/index.en-US.md index 72939a9990..5e91aba876 100644 --- a/components/transfer/index.en-US.md +++ b/components/transfer/index.en-US.md @@ -24,7 +24,7 @@ One or more elements can be selected from either column, one click on the proper | dataSource | Used for setting the source data. The elements that are part of this array will be present the left column. Except the elements whose keys are included in `targetKeys` prop | [RecordType extends TransferItem = TransferItem](https://git.io/vMM64)\[] | \[] | | | disabled | Whether disabled transfer | boolean | false | | | filterOption | A function to determine whether an item should show in search result list | (inputValue, option): boolean | - | | -| footer | A function used for rendering the footer | (props) => ReactNode | - | | +| footer | A function used for rendering the footer | (props, { direction }) => ReactNode | - | direction: 4.17.0 | | listStyle | A custom CSS style used for rendering the transfer columns | object \| ({direction: `left` \| `right`}) => object | - | | | locale | The i18n text including filter, empty text, item unit, etc | { itemUnit: string; itemsUnit: string; searchPlaceholder: string; notFoundContent: ReactNode \| ReactNode[]; } | { itemUnit: `item`, itemsUnit: `items`, notFoundContent: `The list is empty`, searchPlaceholder: `Search here` } | | | oneWay | Display as single direction style | boolean | false | 4.3.0 | diff --git a/components/transfer/index.zh-CN.md b/components/transfer/index.zh-CN.md index 21171cc209..be0e3001df 100644 --- a/components/transfer/index.zh-CN.md +++ b/components/transfer/index.zh-CN.md @@ -27,7 +27,7 @@ cover: https://gw.alipayobjects.com/zos/alicdn/QAXskNI4G/Transfer.svg | dataSource | 数据源,其中的数据将会被渲染到左边一栏中,`targetKeys` 中指定的除外 | [RecordType extends TransferItem = TransferItem](https://git.io/vMM64)\[] | \[] | | | disabled | 是否禁用 | boolean | false | | | filterOption | 接收 `inputValue` `option` 两个参数,当 `option` 符合筛选条件时,应返回 true,反之则返回 false | (inputValue, option): boolean | - | | -| footer | 底部渲染函数 | (props) => ReactNode | - | | +| footer | 底部渲染函数 | (props, { direction }) => ReactNode | - | direction: 4.17.0 | | listStyle | 两个穿梭框的自定义样式 | object\|({direction: `left` \| `right`}) => object | - | | | locale | 各种语言 | { itemUnit: string; itemsUnit: string; searchPlaceholder: string; notFoundContent: ReactNode \| ReactNode[]; } | { itemUnit: `项`, itemsUnit: `项`, searchPlaceholder: `请输入搜索内容` } | | | oneWay | 展示为单向样式 | boolean | false | 4.3.0 | diff --git a/components/transfer/list.tsx b/components/transfer/list.tsx index 3a67d5bfd8..b55b2157a9 100644 --- a/components/transfer/list.tsx +++ b/components/transfer/list.tsx @@ -59,7 +59,10 @@ export interface TransferListProps extends TransferLocale { itemUnit: string; itemsUnit: string; renderList?: RenderListFunction; - footer?: (props: TransferListProps) => React.ReactNode; + footer?: ( + props: TransferListProps, + info?: { direction: TransferDirection }, + ) => React.ReactNode; onScroll: (e: React.UIEvent) => void; disabled?: boolean; direction: TransferDirection; @@ -319,10 +322,12 @@ export default class TransferList< showSelectAll, showRemove, pagination, + direction, } = this.props; // Custom Layout - const footerDom = footer && footer(this.props); + const footerDom = + footer && (footer.length < 2 ? footer(this.props) : footer(this.props, { direction })); const listCls = classNames(prefixCls, { [`${prefixCls}-with-pagination`]: !!pagination, From 846577b6947069eab4ed77c78533a9d0ba95a557 Mon Sep 17 00:00:00 2001 From: Pengsha Ying <810998652@qq.com> Date: Fri, 2 Jul 2021 01:55:04 -0500 Subject: [PATCH 13/22] feat(Cascader): title attributes are added to the selected content text by default (#31237) * update cascader snapshot * feat(cascader): Add a friendlier title prompt to the label --- .../cascader/__tests__/__snapshots__/demo.test.js.snap | 1 + .../__tests__/__snapshots__/index.test.js.snap | 1 + components/cascader/index.tsx | 10 ++++++++-- .../form/__tests__/__snapshots__/demo.test.js.snap | 1 + .../input/__tests__/__snapshots__/demo.test.js.snap | 1 + 5 files changed, 12 insertions(+), 2 deletions(-) diff --git a/components/cascader/__tests__/__snapshots__/demo.test.js.snap b/components/cascader/__tests__/__snapshots__/demo.test.js.snap index e03a8e0158..14c593e25d 100644 --- a/components/cascader/__tests__/__snapshots__/demo.test.js.snap +++ b/components/cascader/__tests__/__snapshots__/demo.test.js.snap @@ -209,6 +209,7 @@ exports[`renders ./components/cascader/demo/default-value.md correctly 1`] = ` > Zhejiang / Hangzhou / West Lake diff --git a/components/cascader/__tests__/__snapshots__/index.test.js.snap b/components/cascader/__tests__/__snapshots__/index.test.js.snap index c39cdae51c..91b22f9010 100644 --- a/components/cascader/__tests__/__snapshots__/index.test.js.snap +++ b/components/cascader/__tests__/__snapshots__/index.test.js.snap @@ -1117,6 +1117,7 @@ exports[`Cascader support controlled mode 1`] = ` > Zhejiang / Hangzhou / West Lake diff --git a/components/cascader/index.tsx b/components/cascader/index.tsx index 517328f8bb..d5be7cc3e7 100644 --- a/components/cascader/index.tsx +++ b/components/cascader/index.tsx @@ -316,7 +316,7 @@ class Cascader extends React.Component { }; getLabel() { - const { options, displayRender = defaultDisplayRender as Function } = this.props; + const { options, displayRender = defaultDisplayRender } = this.props; const names = getFilledFieldNames(this.props); const { value } = this.state; const unwrappedValue = Array.isArray(value[0]) ? value[0] : value; @@ -618,9 +618,15 @@ class Cascader extends React.Component { inputIcon = ; } + const label = this.getLabel(); const input: React.ReactElement = children || ( - {this.getLabel()} + + {label} + Zhejiang / Hangzhou / West Lake diff --git a/components/input/__tests__/__snapshots__/demo.test.js.snap b/components/input/__tests__/__snapshots__/demo.test.js.snap index e1f336cdc1..7c5eb15c62 100644 --- a/components/input/__tests__/__snapshots__/demo.test.js.snap +++ b/components/input/__tests__/__snapshots__/demo.test.js.snap @@ -632,6 +632,7 @@ Array [ > Zhejiang / Hangzhou / West Lake From 6196bf01e99da19662218041115a767658ecf3dc Mon Sep 17 00:00:00 2001 From: Aminul Islam Date: Mon, 5 Jul 2021 08:58:48 +0600 Subject: [PATCH 14/22] feat: Added bn_BD Bangla language (#31257) * feat: Added bn_BD Bangla language * fix: added missing provider * changed to bn-bd * added test * updated test snap * update i18n docs --- components/calendar/locale/bn_BD.tsx | 3 + components/date-picker/locale/bn_BD.tsx | 27 + .../__snapshots__/index.test.js.snap | 35332 +++++++++------- .../locale-provider/__tests__/index.test.js | 9 +- components/locale-provider/bn_BD.tsx | 3 + components/locale/bn_BD.tsx | 134 + components/time-picker/locale/bn_BD.tsx | 8 + docs/react/i18n.en-US.md | 1 + docs/react/i18n.zh-CN.md | 1 + package.json | 4 +- 10 files changed, 20360 insertions(+), 15162 deletions(-) create mode 100644 components/calendar/locale/bn_BD.tsx create mode 100644 components/date-picker/locale/bn_BD.tsx create mode 100644 components/locale-provider/bn_BD.tsx create mode 100644 components/locale/bn_BD.tsx create mode 100644 components/time-picker/locale/bn_BD.tsx diff --git a/components/calendar/locale/bn_BD.tsx b/components/calendar/locale/bn_BD.tsx new file mode 100644 index 0000000000..be32e7c32e --- /dev/null +++ b/components/calendar/locale/bn_BD.tsx @@ -0,0 +1,3 @@ +import bnBD from '../../date-picker/locale/bn_BD'; + +export default bnBD; diff --git a/components/date-picker/locale/bn_BD.tsx b/components/date-picker/locale/bn_BD.tsx new file mode 100644 index 0000000000..2093adefd8 --- /dev/null +++ b/components/date-picker/locale/bn_BD.tsx @@ -0,0 +1,27 @@ +import CalendarLocale from 'rc-picker/lib/locale/bn_BD'; +import TimePickerLocale from '../../time-picker/locale/bn_BD'; +import { PickerLocale } from '../generatePicker'; + +// Merge into a locale object +const locale: PickerLocale = { + lang: { + placeholder: 'তারিখ নির্বাচন', + yearPlaceholder: 'বছর নির্বাচন', + quarterPlaceholder: 'কোয়ার্টার নির্বাচন', + monthPlaceholder: 'মাস নির্বাচন', + weekPlaceholder: 'সপ্তাহ নির্বাচন', + rangePlaceholder: ['শুরুর তারিখ', 'শেষ তারিখ'], + rangeYearPlaceholder: ['শুরুর বছর', 'শেষ বছর'], + rangeMonthPlaceholder: ['শুরুর মাস', 'শেষ মাস'], + rangeWeekPlaceholder: ['শুরুর সপ্তাহ', 'শেষ সপ্তাহ'], + ...CalendarLocale, + }, + timePickerLocale: { + ...TimePickerLocale, + }, +}; + +// All settings at: +// https://github.com/ant-design/ant-design/blob/master/components/date-picker/locale/example.json + +export default locale; diff --git a/components/locale-provider/__tests__/__snapshots__/index.test.js.snap b/components/locale-provider/__tests__/__snapshots__/index.test.js.snap index aa481d7cea..47cddf9f08 100644 --- a/components/locale-provider/__tests__/__snapshots__/index.test.js.snap +++ b/components/locale-provider/__tests__/__snapshots__/index.test.js.snap @@ -16920,6 +16920,5024 @@ exports[`Locale Provider should display the text as bg 1`] = `
`; +exports[`Locale Provider should display the text as bn-bd 1`] = ` +
+ + +
+
+ + + + + + +
+
+
+
+
+
+
+
+ + +
+ + +
+ + +
+
+
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+ রবি + + সোম + + মঙ্গল + + বুধ + + বৃহ + + শুক্র + + শনি +
+
+ 27 +
+
+
+ 28 +
+
+
+ 29 +
+
+
+ 30 +
+
+
+ 31 +
+
+
+ 1 +
+
+
+ 2 +
+
+
+ 3 +
+
+
+ 4 +
+
+
+ 5 +
+
+
+ 6 +
+
+
+ 7 +
+
+
+ 8 +
+
+
+ 9 +
+
+
+ 10 +
+
+
+ 11 +
+
+
+ 12 +
+
+
+ 13 +
+
+
+ 14 +
+
+
+ 15 +
+
+
+ 16 +
+
+
+ 17 +
+
+
+ 18 +
+
+
+ 19 +
+
+
+ 20 +
+
+
+ 21 +
+
+
+ 22 +
+
+
+ 23 +
+
+
+ 24 +
+
+
+ 25 +
+
+
+ 26 +
+
+
+ 27 +
+
+
+ 28 +
+
+
+ 29 +
+
+
+ 30 +
+
+
+ 1 +
+
+
+ 2 +
+
+
+ 3 +
+
+
+ 4 +
+
+
+ 5 +
+
+
+ 6 +
+
+
+ 7 +
+
+ + + + + + + +
+
+ + + + + + +
+
+
+
+
+
+
+
+
    +
  • +
    + 00 +
    +
  • +
  • +
    + 01 +
    +
  • +
  • +
    + 02 +
    +
  • +
  • +
    + 03 +
    +
  • +
  • +
    + 04 +
    +
  • +
  • +
    + 05 +
    +
  • +
  • +
    + 06 +
    +
  • +
  • +
    + 07 +
    +
  • +
  • +
    + 08 +
    +
  • +
  • +
    + 09 +
    +
  • +
  • +
    + 10 +
    +
  • +
  • +
    + 11 +
    +
  • +
  • +
    + 12 +
    +
  • +
  • +
    + 13 +
    +
  • +
  • +
    + 14 +
    +
  • +
  • +
    + 15 +
    +
  • +
  • +
    + 16 +
    +
  • +
  • +
    + 17 +
    +
  • +
  • +
    + 18 +
    +
  • +
  • +
    + 19 +
    +
  • +
  • +
    + 20 +
    +
  • +
  • +
    + 21 +
    +
  • +
  • +
    + 22 +
    +
  • +
  • +
    + 23 +
    +
  • +
+
    +
  • +
    + 00 +
    +
  • +
  • +
    + 01 +
    +
  • +
  • +
    + 02 +
    +
  • +
  • +
    + 03 +
    +
  • +
  • +
    + 04 +
    +
  • +
  • +
    + 05 +
    +
  • +
  • +
    + 06 +
    +
  • +
  • +
    + 07 +
    +
  • +
  • +
    + 08 +
    +
  • +
  • +
    + 09 +
    +
  • +
  • +
    + 10 +
    +
  • +
  • +
    + 11 +
    +
  • +
  • +
    + 12 +
    +
  • +
  • +
    + 13 +
    +
  • +
  • +
    + 14 +
    +
  • +
  • +
    + 15 +
    +
  • +
  • +
    + 16 +
    +
  • +
  • +
    + 17 +
    +
  • +
  • +
    + 18 +
    +
  • +
  • +
    + 19 +
    +
  • +
  • +
    + 20 +
    +
  • +
  • +
    + 21 +
    +
  • +
  • +
    + 22 +
    +
  • +
  • +
    + 23 +
    +
  • +
  • +
    + 24 +
    +
  • +
  • +
    + 25 +
    +
  • +
  • +
    + 26 +
    +
  • +
  • +
    + 27 +
    +
  • +
  • +
    + 28 +
    +
  • +
  • +
    + 29 +
    +
  • +
  • +
    + 30 +
    +
  • +
  • +
    + 31 +
    +
  • +
  • +
    + 32 +
    +
  • +
  • +
    + 33 +
    +
  • +
  • +
    + 34 +
    +
  • +
  • +
    + 35 +
    +
  • +
  • +
    + 36 +
    +
  • +
  • +
    + 37 +
    +
  • +
  • +
    + 38 +
    +
  • +
  • +
    + 39 +
    +
  • +
  • +
    + 40 +
    +
  • +
  • +
    + 41 +
    +
  • +
  • +
    + 42 +
    +
  • +
  • +
    + 43 +
    +
  • +
  • +
    + 44 +
    +
  • +
  • +
    + 45 +
    +
  • +
  • +
    + 46 +
    +
  • +
  • +
    + 47 +
    +
  • +
  • +
    + 48 +
    +
  • +
  • +
    + 49 +
    +
  • +
  • +
    + 50 +
    +
  • +
  • +
    + 51 +
    +
  • +
  • +
    + 52 +
    +
  • +
  • +
    + 53 +
    +
  • +
  • +
    + 54 +
    +
  • +
  • +
    + 55 +
    +
  • +
  • +
    + 56 +
    +
  • +
  • +
    + 57 +
    +
  • +
  • +
    + 58 +
    +
  • +
  • +
    + 59 +
    +
  • +
+
    +
  • +
    + 00 +
    +
  • +
  • +
    + 01 +
    +
  • +
  • +
    + 02 +
    +
  • +
  • +
    + 03 +
    +
  • +
  • +
    + 04 +
    +
  • +
  • +
    + 05 +
    +
  • +
  • +
    + 06 +
    +
  • +
  • +
    + 07 +
    +
  • +
  • +
    + 08 +
    +
  • +
  • +
    + 09 +
    +
  • +
  • +
    + 10 +
    +
  • +
  • +
    + 11 +
    +
  • +
  • +
    + 12 +
    +
  • +
  • +
    + 13 +
    +
  • +
  • +
    + 14 +
    +
  • +
  • +
    + 15 +
    +
  • +
  • +
    + 16 +
    +
  • +
  • +
    + 17 +
    +
  • +
  • +
    + 18 +
    +
  • +
  • +
    + 19 +
    +
  • +
  • +
    + 20 +
    +
  • +
  • +
    + 21 +
    +
  • +
  • +
    + 22 +
    +
  • +
  • +
    + 23 +
    +
  • +
  • +
    + 24 +
    +
  • +
  • +
    + 25 +
    +
  • +
  • +
    + 26 +
    +
  • +
  • +
    + 27 +
    +
  • +
  • +
    + 28 +
    +
  • +
  • +
    + 29 +
    +
  • +
  • +
    + 30 +
    +
  • +
  • +
    + 31 +
    +
  • +
  • +
    + 32 +
    +
  • +
  • +
    + 33 +
    +
  • +
  • +
    + 34 +
    +
  • +
  • +
    + 35 +
    +
  • +
  • +
    + 36 +
    +
  • +
  • +
    + 37 +
    +
  • +
  • +
    + 38 +
    +
  • +
  • +
    + 39 +
    +
  • +
  • +
    + 40 +
    +
  • +
  • +
    + 41 +
    +
  • +
  • +
    + 42 +
    +
  • +
  • +
    + 43 +
    +
  • +
  • +
    + 44 +
    +
  • +
  • +
    + 45 +
    +
  • +
  • +
    + 46 +
    +
  • +
  • +
    + 47 +
    +
  • +
  • +
    + 48 +
    +
  • +
  • +
    + 49 +
    +
  • +
  • +
    + 50 +
    +
  • +
  • +
    + 51 +
    +
  • +
  • +
    + 52 +
    +
  • +
  • +
    + 53 +
    +
  • +
  • +
    + 54 +
    +
  • +
  • +
    + 55 +
    +
  • +
  • +
    + 56 +
    +
  • +
  • +
    + 57 +
    +
  • +
  • +
    + 58 +
    +
  • +
  • +
    + 59 +
    +
  • +
+
+
+ +
+
+
+
+
+
+ +
+
+ + + + + +
+
+ +
+
+ + + + + +
+
+
+
+
+
+
+
+
+
+ + +
+ + +
+ + +
+
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+ রবি + + সোম + + মঙ্গল + + বুধ + + বৃহ + + শুক্র + + শনি +
+
+ 27 +
+
+
+ 28 +
+
+
+ 29 +
+
+
+ 30 +
+
+
+ 31 +
+
+
+ 1 +
+
+
+ 2 +
+
+
+ 3 +
+
+
+ 4 +
+
+
+ 5 +
+
+
+ 6 +
+
+
+ 7 +
+
+
+ 8 +
+
+
+ 9 +
+
+
+ 10 +
+
+
+ 11 +
+
+
+ 12 +
+
+
+ 13 +
+
+
+ 14 +
+
+
+ 15 +
+
+
+ 16 +
+
+
+ 17 +
+
+
+ 18 +
+
+
+ 19 +
+
+
+ 20 +
+
+
+ 21 +
+
+
+ 22 +
+
+
+ 23 +
+
+
+ 24 +
+
+
+ 25 +
+
+
+ 26 +
+
+
+ 27 +
+
+
+ 28 +
+
+
+ 29 +
+
+
+ 30 +
+
+
+ 1 +
+
+
+ 2 +
+
+
+ 3 +
+
+
+ 4 +
+
+
+ 5 +
+
+
+ 6 +
+
+
+ 7 +
+
+
+
+
+
+
+
+ + +
+ + +
+ + +
+
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+ রবি + + সোম + + মঙ্গল + + বুধ + + বৃহ + + শুক্র + + শনি +
+
+ 1 +
+
+
+ 2 +
+
+
+ 3 +
+
+
+ 4 +
+
+
+ 5 +
+
+
+ 6 +
+
+
+ 7 +
+
+
+ 8 +
+
+
+ 9 +
+
+
+ 10 +
+
+
+ 11 +
+
+
+ 12 +
+
+
+ 13 +
+
+
+ 14 +
+
+
+ 15 +
+
+
+ 16 +
+
+
+ 17 +
+
+
+ 18 +
+
+
+ 19 +
+
+
+ 20 +
+
+
+ 21 +
+
+
+ 22 +
+
+
+ 23 +
+
+
+ 24 +
+
+
+ 25 +
+
+
+ 26 +
+
+
+ 27 +
+
+
+ 28 +
+
+
+ 29 +
+
+
+ 30 +
+
+
+ 31 +
+
+
+ 1 +
+
+
+ 2 +
+
+
+ 3 +
+
+
+ 4 +
+
+
+ 5 +
+
+
+ 6 +
+
+
+ 7 +
+
+
+ 8 +
+
+
+ 9 +
+
+
+ 10 +
+
+
+ 11 +
+
+
+
+
+
+
+
+
+
+ + Click to confirm + +
+
+
+
+ +
+ +
+
+
+
+
+
+ + + + + + 0 আইটেম + + +
+ +
+
+ + +
+
+
+ + + + + + 0 আইটেম + + +
+ +
+
+
+
+
+
+ + + + + 2017 + +
+ +
+
+
+ + + + + সেপ্ট + +
+ +
+
+ + +
+
+
+
+
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+ রবি + + সোম + + মঙ্গল + + বুধ + + বৃহ + + শুক্র + + শনি +
+
+
+ 27 +
+
+
+
+
+
+ 28 +
+
+
+
+
+
+ 29 +
+
+
+
+
+
+ 30 +
+
+
+
+
+
+ 31 +
+
+
+
+
+
+ 01 +
+
+
+
+
+
+ 02 +
+
+
+
+
+
+ 03 +
+
+
+
+
+
+ 04 +
+
+
+
+
+
+ 05 +
+
+
+
+
+
+ 06 +
+
+
+
+
+
+ 07 +
+
+
+
+
+
+ 08 +
+
+
+
+
+
+ 09 +
+
+
+
+
+
+ 10 +
+
+
+
+
+
+ 11 +
+
+
+
+
+
+ 12 +
+
+
+
+
+
+ 13 +
+
+
+
+
+
+ 14 +
+
+
+
+
+
+ 15 +
+
+
+
+
+
+ 16 +
+
+
+
+
+
+ 17 +
+
+
+
+
+
+ 18 +
+
+
+
+
+
+ 19 +
+
+
+
+
+
+ 20 +
+
+
+
+
+
+ 21 +
+
+
+
+
+
+ 22 +
+
+
+
+
+
+ 23 +
+
+
+
+
+
+ 24 +
+
+
+
+
+
+ 25 +
+
+
+
+
+
+ 26 +
+
+
+
+
+
+ 27 +
+
+
+
+
+
+ 28 +
+
+
+
+
+
+ 29 +
+
+
+
+
+
+ 30 +
+
+
+
+
+
+ 01 +
+
+
+
+
+
+ 02 +
+
+
+
+
+
+ 03 +
+
+
+
+
+
+ 04 +
+
+
+
+
+
+ 05 +
+
+
+
+
+
+ 06 +
+
+
+
+
+
+ 07 +
+
+
+
+
+
+
+
+
+
+
+
+
+
+ + + + + + + + + + + + + +
+
+ + Name + + + + + + +
+
+ Age +
+
+
+ + + + + + + + + +
+
+ কোনও ডেটা নেই +
+
+
+
+
+
+
+
+
+
+
+ +`; + exports[`Locale Provider should display the text as by 1`] = `