From 8496e8f68778421a0314e368fac22aab540f9bd3 Mon Sep 17 00:00:00 2001 From: nuintun Date: Fri, 6 Jul 2018 23:48:37 +0800 Subject: [PATCH 01/16] Popconfirm support customize icon --- .../__tests__/__snapshots__/demo.test.js.snap | 8 +++++++ components/popconfirm/__tests__/index.test.js | 12 ++++++++++ components/popconfirm/demo/icon.md | 24 +++++++++++++++++++ components/popconfirm/index.en-US.md | 1 + components/popconfirm/index.tsx | 6 +++-- components/popconfirm/index.zh-CN.md | 1 + 6 files changed, 50 insertions(+), 2 deletions(-) create mode 100644 components/popconfirm/demo/icon.md diff --git a/components/popconfirm/__tests__/__snapshots__/demo.test.js.snap b/components/popconfirm/__tests__/__snapshots__/demo.test.js.snap index f1ad8b92a3..d84426db52 100644 --- a/components/popconfirm/__tests__/__snapshots__/demo.test.js.snap +++ b/components/popconfirm/__tests__/__snapshots__/demo.test.js.snap @@ -29,6 +29,14 @@ exports[`renders ./components/popconfirm/demo/dynamic-trigger.md correctly 1`] = `; +exports[`renders ./components/popconfirm/demo/icon.md correctly 1`] = ` + + Delete + +`; + exports[`renders ./components/popconfirm/demo/locale.md correctly 1`] = ` { expect(cancel).toHaveBeenCalled(); expect(onVisibleChange).toHaveBeenLastCalledWith(false); }); + + it('should support customize icon', () => { + const wrapper = mount( + custom-icon}> + show me your code + + ); + + const triggerNode = wrapper.find('span').at(0); + triggerNode.simulate('click'); + expect(wrapper.find('.customize-icon').length).toBe(1); + }); }); diff --git a/components/popconfirm/demo/icon.md b/components/popconfirm/demo/icon.md new file mode 100644 index 0000000000..cc54ebbc0f --- /dev/null +++ b/components/popconfirm/demo/icon.md @@ -0,0 +1,24 @@ +--- +order: 4 +title: + zh-CN: 自定义 Icon 图标 + en-US: Customize icon +--- + +## zh-CN + +使用 `icon` 自定义提示 `icon`。 + +## en-US + +Set `icon` props to customize the icon. + +````jsx +import { Popconfirm, Icon } from 'antd'; + +ReactDOM.render( + }> + Delete + , + mountNode); +```` diff --git a/components/popconfirm/index.en-US.md b/components/popconfirm/index.en-US.md index 4b3116203d..2e7a5ef517 100644 --- a/components/popconfirm/index.en-US.md +++ b/components/popconfirm/index.en-US.md @@ -22,6 +22,7 @@ The difference with the `confirm` modal dialog is that it's more lightweight tha | title | title of the confirmation box | string\|ReactNode | - | | onCancel | callback of cancel | function(e) | - | | onConfirm | callback of confirmation | function(e) | - | +| icon | customize icon of confirmation | ReactNode | <Icon type="exclamation-circle" /> | Consult [Tooltip's documentation](https://ant.design/components/tooltip/#API) to find more APIs. diff --git a/components/popconfirm/index.tsx b/components/popconfirm/index.tsx index 119733fa8a..04ab2a6f24 100644 --- a/components/popconfirm/index.tsx +++ b/components/popconfirm/index.tsx @@ -13,6 +13,7 @@ export interface PopconfirmProps extends AbstractTooltipProps { okText?: React.ReactNode; okType?: ButtonType; cancelText?: React.ReactNode; + icon?: React.ReactNode; } export interface PopconfirmState { @@ -31,6 +32,7 @@ export default class Popconfirm extends React.Component, }; private tooltip: any; @@ -92,12 +94,12 @@ export default class Popconfirm extends React.Component { - const { prefixCls, title, cancelText, okText, okType } = this.props; + const { prefixCls, title, cancelText, okText, okType, icon } = this.props; return (
- + {icon}
{title}
diff --git a/components/popconfirm/index.zh-CN.md b/components/popconfirm/index.zh-CN.md index 873ffe2c81..ea91989bae 100644 --- a/components/popconfirm/index.zh-CN.md +++ b/components/popconfirm/index.zh-CN.md @@ -23,6 +23,7 @@ title: Popconfirm | title | 确认框的描述 | string\|ReactNode | 无 | | onCancel | 点击取消的回调 | function(e) | 无 | | onConfirm | 点击确认的回调 | function(e) | 无 | +| icon | 自定义弹出气泡 Icon 图标 | ReactNode | <Icon type="exclamation-circle" /> | 更多属性请参考 [Tooltip](/components/tooltip/#API)。 From cc00ebe5e14418cf397686ed8174076e20d6cc4f Mon Sep 17 00:00:00 2001 From: afc163 Date: Fri, 20 Jul 2018 18:08:42 +0800 Subject: [PATCH 02/16] Change Transfer moving buttons order for convention close #11269 --- .../__tests__/__snapshots__/demo.test.js.snap | 4 +- .../__snapshots__/index.test.js.snap | 144 +++++++++--------- .../__tests__/__snapshots__/demo.test.js.snap | 24 +-- .../__snapshots__/index.test.js.snap | 20 +-- components/transfer/__tests__/index.test.js | 6 +- components/transfer/operation.tsx | 18 +-- 6 files changed, 108 insertions(+), 108 deletions(-) diff --git a/components/locale-provider/__tests__/__snapshots__/demo.test.js.snap b/components/locale-provider/__tests__/__snapshots__/demo.test.js.snap index 4d20b4537e..467981f100 100644 --- a/components/locale-provider/__tests__/__snapshots__/demo.test.js.snap +++ b/components/locale-provider/__tests__/__snapshots__/demo.test.js.snap @@ -386,7 +386,7 @@ exports[`renders ./components/locale-provider/demo/all.md correctly 1`] = ` type="button" >
diff --git a/components/locale-provider/__tests__/__snapshots__/index.test.js.snap b/components/locale-provider/__tests__/__snapshots__/index.test.js.snap index d6bee74941..6e4e5582e9 100644 --- a/components/locale-provider/__tests__/__snapshots__/index.test.js.snap +++ b/components/locale-provider/__tests__/__snapshots__/index.test.js.snap @@ -5761,7 +5761,7 @@ exports[`Locale Provider should display the text as ar 1`] = ` type="button" >
@@ -10358,7 +10358,7 @@ exports[`Locale Provider should display the text as bg 1`] = ` type="button" >
@@ -14955,7 +14955,7 @@ exports[`Locale Provider should display the text as ca 1`] = ` type="button" > @@ -19552,7 +19552,7 @@ exports[`Locale Provider should display the text as cs 1`] = ` type="button" > @@ -24149,7 +24149,7 @@ exports[`Locale Provider should display the text as de 1`] = ` type="button" > @@ -28746,7 +28746,7 @@ exports[`Locale Provider should display the text as el 1`] = ` type="button" > @@ -33343,7 +33343,7 @@ exports[`Locale Provider should display the text as en 1`] = ` type="button" > @@ -37940,7 +37940,7 @@ exports[`Locale Provider should display the text as en-gb 1`] = ` type="button" > @@ -42537,7 +42537,7 @@ exports[`Locale Provider should display the text as es 1`] = ` type="button" > @@ -47134,7 +47134,7 @@ exports[`Locale Provider should display the text as et 1`] = ` type="button" > @@ -51731,7 +51731,7 @@ exports[`Locale Provider should display the text as fa 1`] = ` type="button" > @@ -56328,7 +56328,7 @@ exports[`Locale Provider should display the text as fi 1`] = ` type="button" > @@ -60925,7 +60925,7 @@ exports[`Locale Provider should display the text as fr 1`] = ` type="button" > @@ -65522,7 +65522,7 @@ exports[`Locale Provider should display the text as fr 2`] = ` type="button" > @@ -70119,7 +70119,7 @@ exports[`Locale Provider should display the text as is 1`] = ` type="button" > @@ -74716,7 +74716,7 @@ exports[`Locale Provider should display the text as it 1`] = ` type="button" > @@ -79313,7 +79313,7 @@ exports[`Locale Provider should display the text as ja 1`] = ` type="button" > @@ -83910,7 +83910,7 @@ exports[`Locale Provider should display the text as ko 1`] = ` type="button" > @@ -88507,7 +88507,7 @@ exports[`Locale Provider should display the text as ku-iq 1`] = ` type="button" > @@ -93104,7 +93104,7 @@ exports[`Locale Provider should display the text as nb 1`] = ` type="button" > @@ -97701,7 +97701,7 @@ exports[`Locale Provider should display the text as nl 1`] = ` type="button" > @@ -102298,7 +102298,7 @@ exports[`Locale Provider should display the text as nl-be 1`] = ` type="button" > @@ -106895,7 +106895,7 @@ exports[`Locale Provider should display the text as pl 1`] = ` type="button" > @@ -111492,7 +111492,7 @@ exports[`Locale Provider should display the text as pt 1`] = ` type="button" > @@ -116089,7 +116089,7 @@ exports[`Locale Provider should display the text as pt-br 1`] = ` type="button" > @@ -120686,7 +120686,7 @@ exports[`Locale Provider should display the text as ru 1`] = ` type="button" > @@ -125283,7 +125283,7 @@ exports[`Locale Provider should display the text as sk 1`] = ` type="button" > @@ -129880,7 +129880,7 @@ exports[`Locale Provider should display the text as sl 1`] = ` type="button" > @@ -134477,7 +134477,7 @@ exports[`Locale Provider should display the text as sr 1`] = ` type="button" > @@ -139074,7 +139074,7 @@ exports[`Locale Provider should display the text as sv 1`] = ` type="button" > @@ -143671,7 +143671,7 @@ exports[`Locale Provider should display the text as th 1`] = ` type="button" > @@ -148268,7 +148268,7 @@ exports[`Locale Provider should display the text as tr 1`] = ` type="button" > @@ -152865,7 +152865,7 @@ exports[`Locale Provider should display the text as uk 1`] = ` type="button" > @@ -157462,7 +157462,7 @@ exports[`Locale Provider should display the text as vi 1`] = ` type="button" > @@ -162059,7 +162059,7 @@ exports[`Locale Provider should display the text as zh-cn 1`] = ` type="button" > @@ -166656,7 +166656,7 @@ exports[`Locale Provider should display the text as zh-tw 1`] = ` type="button" > diff --git a/components/transfer/__tests__/__snapshots__/demo.test.js.snap b/components/transfer/__tests__/__snapshots__/demo.test.js.snap index 3f2777dfcf..4475bb501e 100644 --- a/components/transfer/__tests__/__snapshots__/demo.test.js.snap +++ b/components/transfer/__tests__/__snapshots__/demo.test.js.snap @@ -91,10 +91,10 @@ exports[`renders ./components/transfer/demo/advanced.md correctly 1`] = ` type="button" > - to left + to right @@ -308,7 +308,7 @@ exports[`renders ./components/transfer/demo/basic.md correctly 1`] = ` type="button" > @@ -455,7 +455,7 @@ exports[`renders ./components/transfer/demo/custom-item.md correctly 1`] = ` type="button" > @@ -575,7 +575,7 @@ exports[`renders ./components/transfer/demo/large-data.md correctly 1`] = ` type="button" > @@ -713,7 +713,7 @@ exports[`renders ./components/transfer/demo/search.md correctly 1`] = ` type="button" > diff --git a/components/transfer/__tests__/__snapshots__/index.test.js.snap b/components/transfer/__tests__/__snapshots__/index.test.js.snap index 5f66af2e7c..fbaa3e8bb1 100644 --- a/components/transfer/__tests__/__snapshots__/index.test.js.snap +++ b/components/transfer/__tests__/__snapshots__/index.test.js.snap @@ -96,6 +96,14 @@ exports[`Transfer should render correctly 1`] = `
+ -
diff --git a/components/transfer/__tests__/index.test.js b/components/transfer/__tests__/index.test.js index d8b274191a..8af0ac1bbf 100644 --- a/components/transfer/__tests__/index.test.js +++ b/components/transfer/__tests__/index.test.js @@ -93,14 +93,14 @@ describe('Transfer', () => { it('should move selected keys to corresponding list', () => { const handleChange = jest.fn(); const wrapper = mount(); - wrapper.find(TransferOperation).find(Button).at(1).simulate('click'); // move selected keys to right list + wrapper.find(TransferOperation).find(Button).at(0).simulate('click'); // move selected keys to right list expect(handleChange).toHaveBeenCalledWith(['a', 'b'], 'right', ['a']); }); it('should move selected keys expect disabled to corresponding list', () => { const handleChange = jest.fn(); const wrapper = mount(); - wrapper.find(TransferOperation).find(Button).at(1).simulate('click'); // move selected keys to right list + wrapper.find(TransferOperation).find(Button).at(0).simulate('click'); // move selected keys to right list expect(handleChange).toHaveBeenCalledWith(['b'], 'right', ['b']); }); @@ -209,7 +209,7 @@ describe('Transfer', () => { .simulate('change', { target: { value: 'content2' } }); wrapper.find(TransferList).at(0).find('.ant-transfer-list-header input[type="checkbox"]').filterWhere(n => !n.prop('checked')) .simulate('change'); - wrapper.find(TransferOperation).find(Button).at(1).simulate('click'); + wrapper.find(TransferOperation).find(Button).at(0).simulate('click'); expect(handleChange).toHaveBeenCalledWith(['1', '3', '4'], 'right', ['1']); }); diff --git a/components/transfer/operation.tsx b/components/transfer/operation.tsx index c0acbcbcf0..21f19551d2 100644 --- a/components/transfer/operation.tsx +++ b/components/transfer/operation.tsx @@ -26,15 +26,6 @@ export default class Operation extends React.Component - + ); } From a88a3206948bfe8ad79bea66a836c867e0e8ef67 Mon Sep 17 00:00:00 2001 From: Emerson Laurentino Date: Sun, 22 Jul 2018 14:31:25 -0300 Subject: [PATCH 03/16] add headStyle to card component --- components/card/index.en-US.md | 1 + components/card/index.tsx | 5 +++-- components/card/index.zh-CN.md | 1 + 3 files changed, 5 insertions(+), 2 deletions(-) diff --git a/components/card/index.en-US.md b/components/card/index.en-US.md index a197c795f7..c6632169e2 100644 --- a/components/card/index.en-US.md +++ b/components/card/index.en-US.md @@ -23,6 +23,7 @@ A card can be used to display content related to a single subject. The content c | -------- | ----------- | ---- | ------- | | actions | The action list, shows at the bottom of the Card. | Array | - | | activeTabKey | Current TabPane's key | string | - | +| headStyle | Inline style to apply to the card head | object | - | | bodyStyle | Inline style to apply to the card content | object | - | | bordered | Toggles rendering of the border around the card | boolean | `true` | | cover | Card cover | ReactNode | - | diff --git a/components/card/index.tsx b/components/card/index.tsx index 759f81ccdb..e39f786297 100644 --- a/components/card/index.tsx +++ b/components/card/index.tsx @@ -27,6 +27,7 @@ export interface CardProps extends Omit, 't title?: React.ReactNode; extra?: React.ReactNode; bordered?: boolean; + headStyle?: React.CSSProperties; bodyStyle?: React.CSSProperties; style?: React.CSSProperties; loading?: boolean; @@ -134,7 +135,7 @@ export default class Card extends React.Component { } render() { const { - prefixCls = 'ant-card', className, extra, bodyStyle = {}, noHovering, hoverable, title, loading, + prefixCls = 'ant-card', className, extra, headStyle = {}, bodyStyle = {}, noHovering, hoverable, title, loading, bordered = true, type, cover, actions, tabList, children, activeTabKey, defaultActiveTabKey, ...others } = this.props; @@ -231,7 +232,7 @@ export default class Card extends React.Component { ) : null; if (title || extra || tabs) { head = ( -
+
{title &&
{title}
} {extra &&
{extra}
} diff --git a/components/card/index.zh-CN.md b/components/card/index.zh-CN.md index 155db4d8a7..ab7350ece3 100644 --- a/components/card/index.zh-CN.md +++ b/components/card/index.zh-CN.md @@ -24,6 +24,7 @@ cols: 1 | --- | --- | --- | --- | | actions | 卡片操作组,位置在卡片底部 | Array | - | | activeTabKey | 当前激活页签的 key | string | - | +| headStyle | 自定义标题区域样式 | object | - | | bodyStyle | 内容区域自定义样式 | object | - | | bordered | 是否有边框 | boolean | true | | cover | 卡片封面 | ReactNode | - | From ecff4997d90c318ae5d3ebe45365f8732bc5e8d2 Mon Sep 17 00:00:00 2001 From: paranoidjk Date: Tue, 24 Jul 2018 12:14:34 +0800 Subject: [PATCH 04/16] feat(Avatar): expose onError (#11285) * feat(Avatar): expose onError * refactor: test case coverage --- components/avatar/__tests__/Avatar.test.js | 40 +++++++++++++++++++++- components/avatar/index.en-US.md | 3 +- components/avatar/index.tsx | 11 +++++- components/avatar/index.zh-CN.md | 1 + 4 files changed, 52 insertions(+), 3 deletions(-) diff --git a/components/avatar/__tests__/Avatar.test.js b/components/avatar/__tests__/Avatar.test.js index 52e2d2ed64..877f3edb7c 100644 --- a/components/avatar/__tests__/Avatar.test.js +++ b/components/avatar/__tests__/Avatar.test.js @@ -15,7 +15,8 @@ describe('Avatar Render', () => { const wrapper = mount(Fallback, { attachTo: div }); wrapper.instance().setScale = jest.fn(() => wrapper.instance().setState({ scale: 0.5 })); - wrapper.setState({ isImgExist: false }); + + wrapper.find('img').simulate('error'); const children = wrapper.find('.ant-avatar-string'); expect(children.length).toBe(1); @@ -26,4 +27,41 @@ describe('Avatar Render', () => { wrapper.detach(); global.document.body.removeChild(div); }); + + it('should handle onError correctly', () => { + const LOAD_FAILURE_SRC = 'http://error.url'; + const LOAD_SUCCESS_SRC = 'https://zos.alipayobjects.com/rmsportal/ODTLcjxAfvqbxHnVXCYX.png'; + + const div = global.document.createElement('div'); + global.document.body.appendChild(div); + + class Foo extends React.Component { + state = { + src: LOAD_FAILURE_SRC, + } + + handleImgError = () => { + this.setState({ + src: LOAD_SUCCESS_SRC, + }); + return false; + } + + render() { + const { src } = this.state; + return ; + } + } + + const wrapper = mount(, { attachTo: div }); + // mock img load Error, since jsdom do not load resource by default + // https://github.com/jsdom/jsdom/issues/1816 + wrapper.find('img').simulate('error'); + + expect(wrapper.find(Avatar).instance().state.isImgExist).toBe(true); + expect(div.querySelector('img').getAttribute('src')).toBe(LOAD_SUCCESS_SRC); + + wrapper.detach(); + global.document.body.removeChild(div); + }); }); diff --git a/components/avatar/index.en-US.md b/components/avatar/index.en-US.md index 9b4f7efe3f..4e231e4c8b 100644 --- a/components/avatar/index.en-US.md +++ b/components/avatar/index.en-US.md @@ -14,4 +14,5 @@ Avatars can be used to represent people or objects. It supports images, `Icon`s, | shape | the shape of avatar | `circle` \| `square` | `circle` | | size | the size of the avatar | `large` \| `small` \| `default` | `default` | | src | the address of the image for an image avatar | string | - | -| alt | This attribute defines the alternative text describing the image | string | - | \ No newline at end of file +| alt | This attribute defines the alternative text describing the image | string | - | +| onError | handler when img load error,return false to prevent default fallback behavior | () => boolean | - | diff --git a/components/avatar/index.tsx b/components/avatar/index.tsx index 7b83bd9d3f..3da00b8bf2 100644 --- a/components/avatar/index.tsx +++ b/components/avatar/index.tsx @@ -17,6 +17,9 @@ export interface AvatarProps { className?: string; children?: any; alt?: string; + /* callback when img load error */ + /* return false to prevent Avatar show default fallback behavior, then you can do fallback by your self*/ + onError?: () => boolean; } export interface AvatarState { @@ -72,7 +75,13 @@ export default class Avatar extends React.Component { } } - handleImgLoadError = () => this.setState({ isImgExist: false }); + handleImgLoadError = () => { + const { onError } = this.props; + const errorFlag = onError ? onError() : undefined; + if (errorFlag !== false) { + this.setState({ isImgExist: false }); + } + } render() { const { diff --git a/components/avatar/index.zh-CN.md b/components/avatar/index.zh-CN.md index e6b4cc3ae8..3aa304b628 100644 --- a/components/avatar/index.zh-CN.md +++ b/components/avatar/index.zh-CN.md @@ -16,3 +16,4 @@ title: Avatar | size | 设置头像的大小 | Enum{ 'large', 'small', 'default' } | `default` | | src | 图片类头像的资源地址 | string | - | | alt | 图像无法显示时的替代文本 | string | - | +| onError | 图片加载失败的事件,返回 false 会关闭组件默认的 fallback 行为 | () => boolean | - | From 08e83193f2248e8c960bb66e760a1d850034bd50 Mon Sep 17 00:00:00 2001 From: Raphael Chauveau Date: Tue, 24 Jul 2018 08:49:23 +0200 Subject: [PATCH 05/16] Multiple row selection (#11406) This PR intends to solve issue #11404 (Multiple row selection in table component). The Chinese documentation is missing from the PR. Could someone please add it ? --- components/checkbox/Checkbox.tsx | 2 +- components/radio/interface.tsx | 2 +- components/table/Table.tsx | 88 +++++++++++++++---- .../__tests__/Table.rowSelection.test.js | 30 +++++++ components/table/index.en-US.md | 1 + components/table/interface.tsx | 5 +- 6 files changed, 107 insertions(+), 21 deletions(-) diff --git a/components/checkbox/Checkbox.tsx b/components/checkbox/Checkbox.tsx index 9a7b836304..1650770355 100644 --- a/components/checkbox/Checkbox.tsx +++ b/components/checkbox/Checkbox.tsx @@ -35,7 +35,7 @@ export interface CheckboxChangeEvent { target: CheckboxChangeEventTarget; stopPropagation: () => void; preventDefault: () => void; - nativeEvent: Event; + nativeEvent: MouseEvent; } export default class Checkbox extends React.Component { diff --git a/components/radio/interface.tsx b/components/radio/interface.tsx index c83aba012f..7206897e73 100644 --- a/components/radio/interface.tsx +++ b/components/radio/interface.tsx @@ -38,5 +38,5 @@ export interface RadioChangeEvent { target: RadioChangeEventTarget; stopPropagation: () => void; preventDefault: () => void; - nativeEvent: Event; + nativeEvent: MouseEvent; } diff --git a/components/table/Table.tsx b/components/table/Table.tsx index 1c52043fae..db68ddaaf2 100755 --- a/components/table/Table.tsx +++ b/components/table/Table.tsx @@ -124,6 +124,7 @@ export default class Table extends React.Component, TableState< // 减少状态 filters: this.getFiltersFromColumns(), pagination: this.getDefaultPagination(props), + pivot: undefined, }; this.CheckboxPropsCache = {}; @@ -250,6 +251,11 @@ export default class Table extends React.Component, TableState< } if (selectWay === 'onSelect' && rowSelection.onSelect) { rowSelection.onSelect(record!, checked!, selectedRows, nativeEvent!); + } else if (selectWay === 'onSelectMulti' && rowSelection.onSelectMulti) { + const changeRows = data.filter( + (row, i) => changeRowKeys!.indexOf(this.getRecordKey(row, i)) >= 0, + ); + rowSelection.onSelectMulti(checked!, selectedRows, changeRows); } else if (selectWay === 'onSelectAll' && rowSelection.onSelectAll) { const changeRows = data.filter( (row, i) => changeRowKeys!.indexOf(this.getRecordKey(row, i)) >= 0, @@ -456,21 +462,67 @@ export default class Table extends React.Component, TableState< const defaultSelection = this.store.getState().selectionDirty ? [] : this.getDefaultSelection(); let selectedRowKeys = this.store.getState().selectedRowKeys.concat(defaultSelection); let key = this.getRecordKey(record, rowIndex); - if (checked) { - selectedRowKeys.push(this.getRecordKey(record, rowIndex)); - } else { - selectedRowKeys = selectedRowKeys.filter((i: string) => key !== i); + const pivot = this.state.pivot; + const rows = this.getFlatCurrentPageData(); + let realIndex = rowIndex; + if (this.props.expandedRowRender) { + realIndex = rows.findIndex(row => this.getRecordKey(row, rowIndex) === key); + } + + if (nativeEvent.shiftKey && pivot !== undefined && realIndex !== pivot) { + const changeRowKeys = []; + const direction = Math.sign(pivot - realIndex); + const dist = Math.abs(pivot - realIndex); + let step = 0; + while (step <= dist) { + const i = realIndex + (step * direction); + step += 1; + const row = rows[i]; + const rowKey = this.getRecordKey(row, i); + const checkboxProps = this.getCheckboxPropsByItem(row, i); + if (!checkboxProps.disabled) { + if (selectedRowKeys.includes(rowKey)) { + if (!checked) { + selectedRowKeys = selectedRowKeys.filter((j: string) => rowKey !== j); + changeRowKeys.push(rowKey); + } + } else if (checked) { + selectedRowKeys.push(rowKey); + changeRowKeys.push(rowKey); + } + } + } + + this.setState({ pivot: realIndex }); + this.store.setState({ + selectionDirty: true, + }); + this.setSelectedRowKeys(selectedRowKeys, { + selectWay: 'onSelectMulti', + record, + checked, + changeRowKeys, + nativeEvent, + }); + } else { + if (checked) { + selectedRowKeys.push(this.getRecordKey(record, realIndex)); + } else { + selectedRowKeys = selectedRowKeys.filter((i: string) => key !== i); + } + + this.setState({ pivot: realIndex }); + this.store.setState({ + selectionDirty: true, + }); + this.setSelectedRowKeys(selectedRowKeys, { + selectWay: 'onSelect', + record, + checked, + changeRowKeys: void(0), + nativeEvent, + }); } - this.store.setState({ - selectionDirty: true, - }); - this.setSelectedRowKeys(selectedRowKeys, { - selectWay: 'onSelect', - record, - checked, - changeRowKeys: void(0), - nativeEvent, - }); } handleRadioSelect = (record: T, rowIndex: number, e: RadioChangeEvent) => { @@ -599,11 +651,11 @@ export default class Table extends React.Component, TableState< renderSelectionBox = (type: RowSelectionType | undefined) => { return (_: any, record: T, index: number) => { - let rowIndex = this.getRecordKey(record, index); // 从 1 开始 + const rowKey = this.getRecordKey(record, index); const props = this.getCheckboxPropsByItem(record, index); const handleChange = (e: RadioChangeEvent | CheckboxChangeEvent) => { - type === 'radio' ? this.handleRadioSelect(record, rowIndex, e) : - this.handleSelect(record, rowIndex, e); + type === 'radio' ? this.handleRadioSelect(record, index, e) : + this.handleSelect(record, index, e); }; return ( @@ -611,7 +663,7 @@ export default class Table extends React.Component, TableState< { expect(handleSelect.mock.calls[0][3].type).toBe('change'); }); + it('fires selectMulti event', () => { + const handleSelectMulti = jest.fn(); + const handleSelect = jest.fn(); + const rowSelection = { + onSelect: handleSelect, + onSelectMulti: handleSelectMulti, + }; + const wrapper = mount(createTable({ rowSelection })); + + wrapper.find('input').at(1).simulate('change', { + target: { checked: true }, + nativeEvent: { shiftKey: true }, + }); + expect(handleSelect).toBeCalled(); + + wrapper.find('input').at(3).simulate('change', { + target: { checked: true }, + nativeEvent: { shiftKey: true }, + }); + expect(handleSelectMulti).toBeCalledWith(true, + [data[0], data[1], data[2]], [data[1], data[2]]); + + wrapper.find('input').at(1).simulate('change', { + target: { checked: false }, + nativeEvent: { shiftKey: true }, + }); + expect(handleSelectMulti).toBeCalledWith(false, + [], [data[0], data[1], data[2]]); + }); + it('fires selectAll event', () => { const handleSelectAll = jest.fn(); const rowSelection = { diff --git a/components/table/index.en-US.md b/components/table/index.en-US.md index b91ad85f1d..8f2c1f31c9 100644 --- a/components/table/index.en-US.md +++ b/components/table/index.en-US.md @@ -162,6 +162,7 @@ Properties for row selection. | type | `checkbox` or `radio` | `checkbox` \| `radio` | `checkbox` | | onChange | Callback executed when selected rows change | Function(selectedRowKeys, selectedRows) | - | | onSelect | Callback executed when select/deselect one row | Function(record, selected, selectedRows, nativeEvent) | - | +| onSelectMulti | Callback executed when multiple rows are selected/unselected | Function(selected, selectedRows, changeRows) | - | | onSelectAll | Callback executed when select/deselect all rows | Function(selected, selectedRows, changeRows) | - | | onSelectInvert | Callback executed when row selection is inverted | Function(selectedRows) | - | diff --git a/components/table/interface.tsx b/components/table/interface.tsx index 9b85661b4b..c197408ce8 100644 --- a/components/table/interface.tsx +++ b/components/table/interface.tsx @@ -62,7 +62,7 @@ export interface TableLocale { export type RowSelectionType = 'checkbox' | 'radio'; export type SelectionSelectFn = (record: T, selected: boolean, selectedRows: Object[], nativeEvent: Event) => any; -export type TableSelectWay = 'onSelect' | 'onSelectAll' | 'onSelectInvert'; +export type TableSelectWay = 'onSelect' | 'onSelectMulti' | 'onSelectAll' | 'onSelectInvert'; export interface TableRowSelection { type?: RowSelectionType; @@ -70,12 +70,14 @@ export interface TableRowSelection { onChange?: (selectedRowKeys: string[] | number[], selectedRows: Object[]) => void; getCheckboxProps?: (record: T) => Object; onSelect?: SelectionSelectFn; + onSelectMulti?: (selected: boolean, selectedRows: Object[], changeRows: Object[]) => void; onSelectAll?: (selected: boolean, selectedRows: Object[], changeRows: Object[]) => void; onSelectInvert?: (selectedRows: Object[]) => void; selections?: SelectionItem[] | boolean; hideDefaultSelections?: boolean; fixed?: boolean; columnWidth?: string | number; + selectWay?: TableSelectWay; } export type SortOrder = 'descend' | 'ascend'; export interface SorterResult { @@ -138,6 +140,7 @@ export interface TableState { filters: TableStateFilters; sortColumn: ColumnProps | null; sortOrder?: SortOrder; + pivot?: number; } export type SelectionItemSelectFn = (key: string[]) => any; From b23d742f1bb51c5c428eb2d09f4b953c90bacf7e Mon Sep 17 00:00:00 2001 From: littleLane <857183384@qq.com> Date: Sat, 28 Jul 2018 14:57:00 +0800 Subject: [PATCH 06/16] feat: add rowSelection.columnTitle to Table --- components/table/Table.tsx | 3 ++- .../__tests__/Table.rowSelection.test.js | 21 +++++++++++++++++++ components/table/index.en-US.md | 1 + components/table/index.zh-CN.md | 1 + components/table/interface.tsx | 1 + 5 files changed, 26 insertions(+), 1 deletion(-) diff --git a/components/table/Table.tsx b/components/table/Table.tsx index db68ddaaf2..df0465b8a4 100755 --- a/components/table/Table.tsx +++ b/components/table/Table.tsx @@ -708,10 +708,11 @@ export default class Table extends React.Component, TableState< className: selectionColumnClass, fixed: rowSelection.fixed, width: rowSelection.columnWidth, + title: rowSelection.columnTitle, }; if (rowSelection.type !== 'radio') { const checkboxAllDisabled = data.every((item, index) => this.getCheckboxPropsByItem(item, index).disabled); - selectionColumn.title = ( + selectionColumn.title = selectionColumn.title || ( { expect(checkbox.props().indeterminate).toBe(false); }); }); + + // https://github.com/ant-design/ant-design/issues/11042 + it('add columnTitle for rowSelection', () => { + const wrapper = mount( + + ); + expect(wrapper.find('thead tr span').at(0).text()).toBe('多选'); + wrapper.setProps({ + rowSelection: { + type: 'radio', + columnTitle: '单选', + }, + }); + expect(wrapper.find('thead tr span').at(0).text()).toBe('单选'); + }); }); diff --git a/components/table/index.en-US.md b/components/table/index.en-US.md index 8f2c1f31c9..16c04cc589 100644 --- a/components/table/index.en-US.md +++ b/components/table/index.en-US.md @@ -154,6 +154,7 @@ Properties for row selection. | Property | Description | Type | Default | | -------- | ----------- | ---- | ------- | | columnWidth | Set the width of the selection column | string\|number | - | +| columnTitle | Set the title of the selection column | string\|React.ReactNode | - | | fixed | Fixed selection column on the left | boolean | - | | getCheckboxProps | Get Checkbox or Radio props | Function(record) | - | | hideDefaultSelections | Remove the default `Select All` and `Select Invert` selections | boolean | `false` | diff --git a/components/table/index.zh-CN.md b/components/table/index.zh-CN.md index 2e08e90394..18fcc69da5 100644 --- a/components/table/index.zh-CN.md +++ b/components/table/index.zh-CN.md @@ -154,6 +154,7 @@ const columns = [{ | 参数 | 说明 | 类型 | 默认值 | | --- | --- | --- | --- | | columnWidth | 自定义列表选择框宽度 | string\|number | - | +| columnTitle | 自定义列表选择框标题 | string\|React.ReactNode | - | | fixed | 把选择框列固定在左边 | boolean | - | | getCheckboxProps | 选择框的默认属性配置 | Function(record) | - | | hideDefaultSelections | 去掉『全选』『反选』两个默认选项 | boolean | false | diff --git a/components/table/interface.tsx b/components/table/interface.tsx index c197408ce8..4b4b712fd0 100644 --- a/components/table/interface.tsx +++ b/components/table/interface.tsx @@ -78,6 +78,7 @@ export interface TableRowSelection { fixed?: boolean; columnWidth?: string | number; selectWay?: TableSelectWay; + columnTitle?: string | React.ReactNode; } export type SortOrder = 'descend' | 'ascend'; export interface SorterResult { From 6804094fa2807e827520908864f308a23d89aabb Mon Sep 17 00:00:00 2001 From: Eden Wang Date: Sat, 28 Jul 2018 21:52:12 +0800 Subject: [PATCH 07/16] export mouse enter and mouse leave event for datepicker --- components/date-picker/RangePicker.tsx | 2 ++ components/date-picker/createPicker.tsx | 2 ++ components/date-picker/wrapPicker.tsx | 16 ++++++++++++++++ 3 files changed, 20 insertions(+) diff --git a/components/date-picker/RangePicker.tsx b/components/date-picker/RangePicker.tsx index 50e78ea797..ee8529feb7 100644 --- a/components/date-picker/RangePicker.tsx +++ b/components/date-picker/RangePicker.tsx @@ -352,6 +352,8 @@ class RangePicker extends React.Component { tabIndex={props.disabled ? -1 : 0} onFocus={props.onFocus} onBlur={props.onBlur} + onMouseEnter={props.onMouseEnter} + onMouseLeave={props.onMouseLeave} > , defaultFor } } + handleMouseEnter = (e: React.MouseEventHandler) => { + const { onMouseEnter } = this.props; + if (onMouseEnter) { + onMouseEnter(e); + } + } + + handleMouseLeave = (e: React.MouseEventHandler) => { + const { onMouseLeave } = this.props; + if (onMouseLeave) { + onMouseLeave(e); + } + } + focus() { this.picker.focus(); } @@ -134,6 +148,8 @@ export default function wrapPicker(Picker: React.ComponentClass, defaultFor onOpenChange={this.handleOpenChange} onFocus={this.handleFocus} onBlur={this.handleBlur} + onMouseEnter={this.handleMouseEnter} + onMouseLeave={this.handleMouseLeave} /> ); } From 84e995ff63980ff8acca9d73b318e62277d8e3ee Mon Sep 17 00:00:00 2001 From: Eden Wang Date: Sun, 29 Jul 2018 09:40:58 +0800 Subject: [PATCH 08/16] add test case for date picker in tooltip --- components/tooltip/__tests__/tooltip.test.js | 26 ++++++++++++++++++++ 1 file changed, 26 insertions(+) diff --git a/components/tooltip/__tests__/tooltip.test.js b/components/tooltip/__tests__/tooltip.test.js index fcfaad6582..bf09be119a 100644 --- a/components/tooltip/__tests__/tooltip.test.js +++ b/components/tooltip/__tests__/tooltip.test.js @@ -2,6 +2,7 @@ import React from 'react'; import { mount } from 'enzyme'; import Tooltip from '..'; import Button from '../../button'; +import DatePicker from '../../date-picker'; describe('Tooltip', () => { it('check `onVisibleChange` arguments', () => { @@ -174,4 +175,29 @@ describe('Tooltip', () => { jest.dontMock('rc-trigger', suit); }); + + it('should works for date picker', () => { + const onVisibleChange = jest.fn(); + + const wrapper = mount( + + + + ); + + expect(wrapper.find('span.ant-calendar-picker')).toHaveLength(1); + const picker = wrapper.find('span.ant-calendar-picker').at(0); + picker.simulate('mouseenter'); + expect(onVisibleChange).toBeCalledWith(true); + expect(wrapper.instance().tooltip.props.visible).toBe(true); + + picker.simulate('mouseleave'); + expect(onVisibleChange).toBeCalledWith(false); + expect(wrapper.instance().tooltip.props.visible).toBe(false); + }); }); From 522aee18c31696fe9423519cf9554e0333a0aac6 Mon Sep 17 00:00:00 2001 From: Eden Wang Date: Sun, 29 Jul 2018 10:34:43 +0800 Subject: [PATCH 09/16] add timeout to wait trigger result --- components/tooltip/__tests__/tooltip.test.js | 10 ++++++---- 1 file changed, 6 insertions(+), 4 deletions(-) diff --git a/components/tooltip/__tests__/tooltip.test.js b/components/tooltip/__tests__/tooltip.test.js index bf09be119a..ae7ab7b624 100644 --- a/components/tooltip/__tests__/tooltip.test.js +++ b/components/tooltip/__tests__/tooltip.test.js @@ -4,6 +4,8 @@ import Tooltip from '..'; import Button from '../../button'; import DatePicker from '../../date-picker'; +const delay = timeout => new Promise(resolve => setTimeout(resolve, timeout)); + describe('Tooltip', () => { it('check `onVisibleChange` arguments', () => { const onVisibleChange = jest.fn(); @@ -176,14 +178,12 @@ describe('Tooltip', () => { jest.dontMock('rc-trigger', suit); }); - it('should works for date picker', () => { + it('should works for date picker', async () => { const onVisibleChange = jest.fn(); const wrapper = mount( @@ -193,10 +193,12 @@ describe('Tooltip', () => { expect(wrapper.find('span.ant-calendar-picker')).toHaveLength(1); const picker = wrapper.find('span.ant-calendar-picker').at(0); picker.simulate('mouseenter'); + await delay(100); expect(onVisibleChange).toBeCalledWith(true); expect(wrapper.instance().tooltip.props.visible).toBe(true); picker.simulate('mouseleave'); + await delay(100); expect(onVisibleChange).toBeCalledWith(false); expect(wrapper.instance().tooltip.props.visible).toBe(false); }); From 068dc8ec388c33bc5572ca63f1be0533e9d81b2a Mon Sep 17 00:00:00 2001 From: Ilan Date: Sun, 29 Jul 2018 01:34:49 +0200 Subject: [PATCH 10/16] =?UTF-8?q?=E2=9C=A8=20popover=20that=20can=20be=20h?= =?UTF-8?q?overed=20and=20clicked?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../__tests__/__snapshots__/demo.test.js.snap | 11 +++ components/popover/demo/hover-with-click.md | 86 +++++++++++++++++++ 2 files changed, 97 insertions(+) create mode 100644 components/popover/demo/hover-with-click.md diff --git a/components/popover/__tests__/__snapshots__/demo.test.js.snap b/components/popover/__tests__/__snapshots__/demo.test.js.snap index f9d82fa3c0..860bc98ce3 100644 --- a/components/popover/__tests__/__snapshots__/demo.test.js.snap +++ b/components/popover/__tests__/__snapshots__/demo.test.js.snap @@ -43,6 +43,17 @@ exports[`renders ./components/popover/demo/control.md correctly 1`] = ` `; +exports[`renders ./components/popover/demo/hover-with-click.md correctly 1`] = ` + +`; + exports[`renders ./components/popover/demo/placement.md correctly 1`] = `
+ This is hover content. +
+); + +const clickContent = ( +
+ This is click content. +
+); + +const initalState = { + clicked: false, + hovered: false, +}; + +class App extends React.Component { + state = initalState; + + hide = () => { + this.setState({ + ...initalState, + }); + } + + handleHoverChange = (visible) => { + this.setState({ + hovered: visible, + clicked: false, + }); + } + + handleClickChange = (visible) => { + this.setState({ + clicked: visible, + hovered: false, + }); + } + + render() { + return ( + + Close]} + title="Click title" + trigger="click" + visible={this.state.clicked} + onVisibleChange={this.handleClickChange} + > + + + + ); + } +} + +ReactDOM.render(, mountNode); +```` From 97bf63086e2cb947cb1c9fe2168eed0e99bf8722 Mon Sep 17 00:00:00 2001 From: Ilan Date: Sun, 29 Jul 2018 01:46:52 +0200 Subject: [PATCH 11/16] =?UTF-8?q?=F0=9F=90=9B=20update=20popover=20hover?= =?UTF-8?q?=20and=20click=20example=20order?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- components/popover/demo/hover-with-click.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/components/popover/demo/hover-with-click.md b/components/popover/demo/hover-with-click.md index c3d3a14e5e..c507e81149 100644 --- a/components/popover/demo/hover-with-click.md +++ b/components/popover/demo/hover-with-click.md @@ -1,5 +1,5 @@ --- -order: 3 +order: 5 title: zh-CN: 悬停点击弹出窗口 en-US: Hover with click popover From f62dc47ae79318b0c5cc81e6fa5e831e916267b8 Mon Sep 17 00:00:00 2001 From: afc163 Date: Sun, 29 Jul 2018 15:11:03 +0800 Subject: [PATCH 12/16] upgrade rc-tabs to 9.3.x close #11261 https://github.com/react-component/tabs/pull/123 --- package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/package.json b/package.json index 889013d1b6..6278eb89ee 100644 --- a/package.json +++ b/package.json @@ -74,7 +74,7 @@ "rc-steps": "~3.1.0", "rc-switch": "~1.6.0", "rc-table": "~6.2.2", - "rc-tabs": "~9.2.0", + "rc-tabs": "~9.3.1", "rc-time-picker": "~3.3.0", "rc-tooltip": "~3.7.0", "rc-tree": "~1.12.0", From bd6ad1c19af0d5ef8e9bc4b868122c9c0de1f1f6 Mon Sep 17 00:00:00 2001 From: afc163 Date: Sun, 29 Jul 2018 15:15:39 +0800 Subject: [PATCH 13/16] upgrade rc-select to 8.1.x * support data-* attributes * fix #11268 --- package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/package.json b/package.json index 6278eb89ee..56b4be3768 100644 --- a/package.json +++ b/package.json @@ -69,7 +69,7 @@ "rc-pagination": "~1.16.1", "rc-progress": "~2.2.2", "rc-rate": "~2.4.0", - "rc-select": "~8.0.7", + "rc-select": "~8.1.1", "rc-slider": "~8.6.0", "rc-steps": "~3.1.0", "rc-switch": "~1.6.0", From 51d32baa62032955904c5d2313fe4082441b32d4 Mon Sep 17 00:00:00 2001 From: afc163 Date: Sun, 29 Jul 2018 15:19:59 +0800 Subject: [PATCH 14/16] fix tabs test snapshots --- .../__tests__/__snapshots__/demo.test.js.snap | 12 ++-- .../__tests__/__snapshots__/demo.test.js.snap | 15 +++++ .../__tests__/__snapshots__/demo.test.js.snap | 66 +++++++++---------- .../__snapshots__/index.test.js.snap | 6 +- 4 files changed, 57 insertions(+), 42 deletions(-) diff --git a/components/card/__tests__/__snapshots__/demo.test.js.snap b/components/card/__tests__/__snapshots__/demo.test.js.snap index 04136bc075..cd85a6dc9c 100644 --- a/components/card/__tests__/__snapshots__/demo.test.js.snap +++ b/components/card/__tests__/__snapshots__/demo.test.js.snap @@ -707,9 +707,6 @@ exports[`renders ./components/card/demo/tabs.md correctly 1`] = `
-
tab2
+
@@ -804,9 +804,6 @@ exports[`renders ./components/card/demo/tabs.md correctly 1`] = `
-
project
+
diff --git a/components/form/__tests__/__snapshots__/demo.test.js.snap b/components/form/__tests__/__snapshots__/demo.test.js.snap index f3ace75237..9635e2a5f9 100644 --- a/components/form/__tests__/__snapshots__/demo.test.js.snap +++ b/components/form/__tests__/__snapshots__/demo.test.js.snap @@ -527,6 +527,7 @@ exports[`renders ./components/form/demo/coordinated.md correctly 1`] = ` >
@@ -1612,6 +1615,7 @@ exports[`renders ./components/form/demo/register.md correctly 1`] = ` >
@@ -1683,6 +1689,7 @@ exports[`renders ./components/form/demo/register.md correctly 1`] = ` >