feat: add api for transfer (#29288)

* feat: add api for transfer

* feat: Transfer new feature

* Update components/transfer/__tests__/index.test.js

Co-authored-by: afc163 <afc163@gmail.com>

* Update components/transfer/demo/advanced.md

Co-authored-by: afc163 <afc163@gmail.com>

* Update components/transfer/index.en-US.md

Co-authored-by: afc163 <afc163@gmail.com>

* Update components/transfer/index.tsx

Co-authored-by: afc163 <afc163@gmail.com>

* fix: change another way

* fix: add test

* fix: defaultFooterRender test failed

* fix: comment in English

* fix: delete unnecessary blank line

* fix: fix describe error

* fix: add version and Type Guard

* fix: transfer docs

* fix: docs

* fix: docs grammar err

Co-authored-by: SimpleZhang <simplezhang@yeahka.com>
Co-authored-by: afc163 <afc163@gmail.com>
This commit is contained in:
Eating-Eating 2021-02-22 18:41:02 +08:00 committed by GitHub
parent 744ddc4fdf
commit 15580483ce
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
7 changed files with 69 additions and 26 deletions

View File

@ -216,7 +216,7 @@ exports[`renders ./components/transfer/demo/advanced.md correctly 1`] = `
</button>
</div>
<div
class="ant-transfer-list ant-transfer-list-with-footer"
class="ant-transfer-list"
style="width:250px;height:300px"
>
<div
@ -352,19 +352,6 @@ exports[`renders ./components/transfer/demo/advanced.md correctly 1`] = `
</div>
</div>
</div>
<div
class="ant-transfer-list-footer"
>
<button
class="ant-btn ant-btn-sm"
style="float:right;margin:5px"
type="button"
>
<span>
reload
</span>
</button>
</div>
</div>
</div>
`;

View File

@ -558,3 +558,31 @@ describe('immutable data', () => {
expect(wrapper).toMatchRenderedSnapshot();
});
});
describe('footer render source and target', () => {
// https://github.com/ant-design/ant-design/issues/28082
it('currently render footer', () => {
const differentFooter = () => ({
source: (
<Button size="small" className="sourceFooter">
reload
</Button>
),
target: (
<Button size="small" className="targetFooter">
reload
</Button>
),
});
const defaultFooter = () => (
<Button size="small" className="defaultFooter">
reload
</Button>
);
const wrapper = mount(<Transfer footer={differentFooter} />);
const wrapper2 = mount(<Transfer footer={defaultFooter} />);
expect(wrapper.exists('.sourceFooter')).toEqual(true);
expect(wrapper.exists('.targetFooter')).toEqual(true);
expect(wrapper2.exists('.defaultFooter')).toEqual(true);
});
});

View File

@ -50,11 +50,13 @@ class App extends React.Component {
this.setState({ targetKeys });
};
renderFooter = () => (
<Button size="small" style={{ float: 'right', margin: 5 }} onClick={this.getMock}>
reload
</Button>
);
renderFooter = () => ({
source: (
<Button size="small" style={{ float: 'right', margin: 5 }} onClick={this.getMock}>
reload
</Button>
),
});
render() {
return (

View File

@ -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) => ReactNode \| { source: ReactNode, target: ReactNode } | - | { source: ReactNode, target: ReactNode }: 4.12.3 |
| 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` } | |
| oneWay | Display as single direction style | boolean | false | 4.3.0 |

View File

@ -58,7 +58,6 @@ export interface TransferLocale {
removeAll: string;
removeCurrent: string;
}
export interface TransferProps<RecordType> {
prefixCls?: string;
className?: string;
@ -77,7 +76,14 @@ export interface TransferProps<RecordType> {
showSearch?: boolean;
filterOption?: (inputValue: string, item: RecordType) => boolean;
locale?: Partial<TransferLocale>;
footer?: (props: TransferListProps<RecordType>) => React.ReactNode;
footer?: (
props: TransferListProps<RecordType>,
) =>
| React.ReactNode
| {
source?: React.ReactNode;
target?: React.ReactNode;
};
rowKey?: (record: RecordType) => string;
onSearch?: (direction: TransferDirection, value: string) => void;
onScroll?: (direction: TransferDirection, e: React.SyntheticEvent<HTMLUListElement>) => void;

View File

@ -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) => ReactNode \| { source: ReactNode, target: ReactNode } | - | { source: ReactNode, target: ReactNode }: 4.12.3 |
| listStyle | 两个穿梭框的自定义样式 | object\|({direction: `left` \| `right`}) => object | - | |
| locale | 各种语言 | { itemUnit: string; itemsUnit: string; searchPlaceholder: string; notFoundContent: ReactNode; } | { itemUnit: `项`, itemsUnit: `项`, searchPlaceholder: `请输入搜索内容` } | |
| oneWay | 展示为单向样式 | boolean | false | 4.3.0 |

View File

@ -39,7 +39,10 @@ export interface RenderedItem<RecordType> {
}
type RenderListFunction<T> = (props: TransferListBodyProps<T>) => React.ReactNode;
type FooterRender = {
source?: React.ReactNode;
target?: React.ReactNode;
};
export interface TransferListProps<RecordType> extends TransferLocale {
prefixCls: string;
titleText: React.ReactNode;
@ -59,7 +62,7 @@ export interface TransferListProps<RecordType> extends TransferLocale {
itemUnit: string;
itemsUnit: string;
renderList?: RenderListFunction<RecordType>;
footer?: (props: TransferListProps<RecordType>) => React.ReactNode;
footer?: (props: TransferListProps<RecordType>) => React.ReactNode | FooterRender;
onScroll: (e: React.UIEvent<HTMLUListElement>) => void;
disabled?: boolean;
direction: TransferDirection;
@ -312,10 +315,27 @@ export default class TransferList<
showSelectAll,
showRemove,
pagination,
direction,
} = this.props;
// Custom Layout
const footerDom = footer && footer(this.props);
// Distinguish different footer
const tempDom = footer && footer(this.props);
let footerDom;
function isFooterRender(obj: any): obj is FooterRender {
return obj.source || obj.target;
}
if (tempDom) {
if (isFooterRender(tempDom)) {
if (direction === 'left') {
footerDom = tempDom.source;
} else {
footerDom = tempDom.target;
}
} else {
footerDom = tempDom;
}
}
const listCls = classNames(prefixCls, {
[`${prefixCls}-with-pagination`]: pagination,