Merge branch 'feature-3.6.0'

This commit is contained in:
picodoth 2018-06-02 13:36:05 +08:00
commit e25323882a
33 changed files with 423 additions and 66 deletions

View File

@ -2,6 +2,7 @@ import * as React from 'react';
import * as ReactDOM from 'react-dom';
import PropTypes from 'prop-types';
import classNames from 'classnames';
import intersperse from 'intersperse';
import Animate from 'rc-animate';
import Row from '../grid/row';
import Col, { ColProps } from '../grid/col';
@ -65,15 +66,20 @@ export default class FormItem extends React.Component<FormItemProps, any> {
);
}
getHelpMsg() {
const props = this.props;
const onlyControl = this.getOnlyControl();
if (props.help === undefined && onlyControl) {
getHelpMessage() {
const { help } = this.props;
if (help === undefined && this.getOnlyControl()) {
const errors = this.getField().errors;
return errors ? errors.map((e: any) => e.message).join(', ') : '';
if (errors) {
return intersperse(errors.map((e: any, index: number) => (
React.isValidElement(e.message)
? React.cloneElement(e.message, { key: index })
: e.message
)), ' ');
}
return '';
}
return props.help;
return help;
}
getControls(children: React.ReactNode, recursively: boolean) {
@ -132,7 +138,7 @@ export default class FormItem extends React.Component<FormItemProps, any> {
renderHelp() {
const prefixCls = this.props.prefixCls;
const help = this.getHelpMsg();
const help = this.getHelpMessage();
const children = help ? (
<div className={`${prefixCls}-explain`} key="help">
{help}

View File

@ -0,0 +1,100 @@
// Jest Snapshot v1, https://goo.gl/fbAQLP
exports[`Form should display custom message 1`] = `
<form
class="ant-form ant-form-horizontal"
>
<div
class="ant-row ant-form-item ant-form-item-with-help"
>
<div
class="ant-form-item-label"
>
<label
class=""
for="account"
title="Account"
>
Account
</label>
</div>
<div
class="ant-form-item-control-wrapper"
>
<div
class="ant-form-item-control has-error"
>
<span
class="ant-form-item-children"
>
<input
data-__field="[object Object]"
data-__meta="[object Object]"
id="account"
value="antd"
/>
</span>
<div
class="ant-form-explain show-help-enter"
>
<span>
Account does not exist,
<a
href="https://www.alipay.com/"
rel="noopener noreferrer"
target="_blank"
>
Forgot account?
</a>
</span>
</div>
</div>
</div>
</div>
</form>
`;
exports[`Form should display two message 1`] = `
<form
class="ant-form ant-form-horizontal"
>
<div
class="ant-row ant-form-item ant-form-item-with-help"
>
<div
class="ant-form-item-label"
>
<label
class=""
for="account"
title="Account"
>
Account
</label>
</div>
<div
class="ant-form-item-control-wrapper"
>
<div
class="ant-form-item-control has-error"
>
<span
class="ant-form-item-children"
>
<input
data-__field="[object Object]"
data-__meta="[object Object]"
id="account"
value="+=-/"
/>
</span>
<div
class="ant-form-explain show-help-enter"
>
Error message 1 Error message 2
</div>
</div>
</div>
</div>
</form>
`;

View File

@ -0,0 +1,56 @@
import React from 'react';
import { mount } from 'enzyme';
import Form from '..';
describe('Form', () => {
it('should display two message', () => {
const rules = [{
pattern: /^\w+$/,
message: 'Error message 1',
}, {
pattern: /^\w+$/,
message: 'Error message 2',
}];
let myForm;
const Form1 = Form.create()(({ form }) => {
myForm = form;
return (
<Form>
<Form.Item label="Account">
{form.getFieldDecorator('account', { initialValue: '+=-/', rules })(<input />)}
</Form.Item>
</Form>
);
});
const wrapper = mount(<Form1 />);
myForm.validateFields();
wrapper.update();
expect(wrapper.render()).toMatchSnapshot();
});
it('should display custom message', () => {
const rules = [{
pattern: /^$/,
message: (<span>Account does not exist, <a rel="noopener noreferrer" href="https://www.alipay.com/" target="_blank">Forgot account?</a></span>),
}];
let myForm;
const Form1 = Form.create()(({ form }) => {
myForm = form;
return (
<Form>
<Form.Item label="Account">
{form.getFieldDecorator('account', { initialValue: 'antd', rules })(<input />)}
</Form.Item>
</Form>
);
});
const wrapper = mount(<Form1 />);
myForm.validateFields();
wrapper.update();
expect(wrapper.render()).toMatchSnapshot();
});
});

View File

@ -147,7 +147,7 @@ Note:
| enum | validate a value from a list of possible values | string | - |
| len | validate an exact length of a field | number | - |
| max | validate a max length of a field | number | - |
| message | validation error message | string | - |
| message | validation error message | string\|ReactNode | - |
| min | validate a min length of a field | number | - |
| pattern | validate from a regular expression | RegExp | - |
| required | indicates whether field is required | boolean | `false` |

View File

@ -148,7 +148,7 @@ CustomizedForm = Form.create({})(CustomizedForm);
| enum | 枚举类型 | string | - |
| len | 字段长度 | number | - |
| max | 最大长度 | number | - |
| message | 校验文案 | string | - |
| message | 校验文案 | string\|ReactNode | - |
| min | 最小长度 | number | - |
| pattern | 正则表达式校验 | RegExp | - |
| required | 是否必选 | boolean | `false` |

View File

@ -31,6 +31,8 @@ const dimensionMap = {
export type CollapseType = 'clickTrigger' | 'responsive';
export type SiderTheme = 'light' | 'dark';
export interface SiderProps extends React.HTMLAttributes<HTMLDivElement> {
prefixCls?: string;
collapsible?: boolean;
@ -42,6 +44,7 @@ export interface SiderProps extends React.HTMLAttributes<HTMLDivElement> {
width?: number | string;
collapsedWidth?: number | string;
breakpoint?: 'xs' | 'sm' | 'md' | 'lg' | 'xl' | 'xxl';
theme?: SiderTheme;
}
export interface SiderState {
@ -73,6 +76,7 @@ export default class Sider extends React.Component<SiderProps, SiderState> {
width: 200,
collapsedWidth: 80,
style: {},
theme: 'dark' as SiderTheme,
};
static childContextTypes = {
@ -174,7 +178,7 @@ export default class Sider extends React.Component<SiderProps, SiderState> {
}
render() {
const { prefixCls, className,
const { prefixCls, className, theme,
collapsible, reverseArrow, trigger, style, width, collapsedWidth,
...others,
} = this.props;
@ -210,7 +214,7 @@ export default class Sider extends React.Component<SiderProps, SiderState> {
minWidth: siderWidth, // https://github.com/ant-design/ant-design/issues/6349
width: siderWidth,
};
const siderCls = classNames(className, prefixCls, {
const siderCls = classNames(className, prefixCls, `${prefixCls}-${theme}`, {
[`${prefixCls}-collapsed`]: !!this.state.collapsed,
[`${prefixCls}-has-trigger`]: collapsible && trigger !== null && !zeroWidthTrigger,
[`${prefixCls}-below`]: !!this.state.below,

View File

@ -33,7 +33,7 @@ exports[`renders ./components/layout/demo/basic.md correctly 1`] = `
class="ant-layout"
>
<div
class="ant-layout-sider"
class="ant-layout-sider ant-layout-sider-dark"
style="flex:0 0 200px;max-width:200px;min-width:200px;width:200px"
>
<div
@ -71,7 +71,7 @@ exports[`renders ./components/layout/demo/basic.md correctly 1`] = `
Content
</div>
<div
class="ant-layout-sider"
class="ant-layout-sider ant-layout-sider-dark"
style="flex:0 0 200px;max-width:200px;min-width:200px;width:200px"
>
<div
@ -91,7 +91,7 @@ exports[`renders ./components/layout/demo/basic.md correctly 1`] = `
class="ant-layout"
>
<div
class="ant-layout-sider"
class="ant-layout-sider ant-layout-sider-dark"
style="flex:0 0 200px;max-width:200px;min-width:200px;width:200px"
>
<div
@ -128,7 +128,7 @@ exports[`renders ./components/layout/demo/custom-trigger.md correctly 1`] = `
class="ant-layout"
>
<div
class="ant-layout-sider"
class="ant-layout-sider ant-layout-sider-dark"
style="flex:0 0 200px;max-width:200px;min-width:200px;width:200px"
>
<div
@ -302,7 +302,7 @@ exports[`renders ./components/layout/demo/fixed-sider.md correctly 1`] = `
class="ant-layout"
>
<div
class="ant-layout-sider"
class="ant-layout-sider ant-layout-sider-dark"
style="overflow:auto;height:100vh;position:fixed;left:0;flex:0 0 200px;max-width:200px;min-width:200px;width:200px"
>
<div
@ -557,7 +557,7 @@ exports[`renders ./components/layout/demo/responsive.md correctly 1`] = `
class="ant-layout"
>
<div
class="ant-layout-sider"
class="ant-layout-sider ant-layout-sider-dark"
style="flex:0 0 200px;max-width:200px;min-width:200px;width:200px"
>
<div
@ -662,7 +662,7 @@ exports[`renders ./components/layout/demo/side.md correctly 1`] = `
style="min-height:100vh"
>
<div
class="ant-layout-sider ant-layout-sider-has-trigger"
class="ant-layout-sider ant-layout-sider-dark ant-layout-sider-has-trigger"
style="flex:0 0 200px;max-width:200px;min-width:200px;width:200px"
>
<div
@ -1006,7 +1006,7 @@ exports[`renders ./components/layout/demo/top-side.md correctly 1`] = `
style="padding:24px 0;background:#fff"
>
<div
class="ant-layout-sider"
class="ant-layout-sider ant-layout-sider-dark"
style="background:#fff;flex:0 0 200px;max-width:200px;min-width:200px;width:200px"
>
<div
@ -1176,7 +1176,7 @@ exports[`renders ./components/layout/demo/top-side-2.md correctly 1`] = `
class="ant-layout"
>
<div
class="ant-layout-sider"
class="ant-layout-sider ant-layout-sider-dark"
style="background:#fff;flex:0 0 200px;max-width:200px;min-width:200px;width:200px"
>
<div

View File

@ -55,4 +55,18 @@ describe('Layout', () => {
);
expect(wrapper.find('.ant-layout-sider').hasClass('ant-layout-sider-zero-width')).toBe(true);
});
it('detect ant-layout-sider-dark as default theme', async () => {
const wrapper = mount(
<Sider>Sider</Sider>
);
expect(wrapper.find('.ant-layout-sider').hasClass('ant-layout-sider-dark'));
});
it('detect ant-layout-sider-light when set light theme', async () => {
const wrapper = mount(
<Sider theme="light">Sider</Sider>
);
expect(wrapper.find('.ant-layout-sider').hasClass('ant-layout-sider-light'));
});
});

View File

@ -98,6 +98,7 @@ The sidebar.
| trigger | specify the customized trigger, set to null to hide the trigger | string\|ReactNode | - |
| width | width of the sidebar | number\|string | 200 |
| onCollapse | the callback function, executed by clicking the trigger or activating the responsive layout | (collapsed, type) => {} | - |
| theme | color theme of the sidebar | string: `light` `dark` | `dark` |
#### breakpoint width

View File

@ -99,6 +99,7 @@ title: Layout
| trigger | 自定义 trigger设置为 null 时隐藏 trigger | string\|ReactNode | - |
| width | 宽度 | number\|string | 200 |
| onCollapse | 展开-收起时的回调函数,有点击 trigger 以及响应式反馈两种方式可以触发 | (collapsed, type) => {} | - |
| theme | 主题颜色 | string: `light` `dark` | `dark` |
#### breakpoint width

View File

@ -110,3 +110,5 @@
}
}
}
@import './light';

View File

@ -0,0 +1,11 @@
.@{layout-prefix-cls} {
&-sider {
&-light {
background: @layout-sider-background-light;
}
&-light > &-trigger {
color: @layout-trigger-color-light;
background: @layout-trigger-background-light;
}
}
}

View File

@ -126,4 +126,15 @@ describe('List.pagination', () => {
.hasClass('ant-pagination-item-active')
).toBe(true);
});
it('specify the position of pagination', () => {
const wrapper = mount(createList({ pagination: { position: 'top' } }));
expect(wrapper.find('.ant-list').childAt(0).find('.ant-pagination')).toHaveLength(1);
wrapper.setProps({ pagination: { position: 'bottom' } });
expect(wrapper.find('.ant-list').children().last().find('.ant-pagination')).toHaveLength(1);
wrapper.setProps({ pagination: { position: 'both' } });
expect(wrapper.find('.ant-pagination')).toHaveLength(2);
expect(wrapper.find('.ant-list').childAt(0).find('.ant-pagination')).toHaveLength(1);
expect(wrapper.find('.ant-list').children().last().find('.ant-pagination')).toHaveLength(1);
});
});

View File

@ -1,7 +1,7 @@
---
category: Components
type: Data Display
title: List
title: List
cols: 1
---
@ -28,6 +28,16 @@ A list can be used to display content related to a single subject. The content c
| pagination | Pagination [config](https://ant.design/components/pagination/), hide it by setting it to false | boolean \| object | false |
| split | Toggles rendering of the split under the list item | boolean | true |
### pagination
Properties for pagination.
| Property | Description | Type | Default |
| -------- | ----------- | ---- | ------- |
| position | specify the position of `Pagination` | 'top' \| 'bottom' \| 'both' | 'bottom' |
More about pagination, please check [`Pagination`](/components/pagination/).
### List grid props
| Property | Description | Type | Default |

View File

@ -6,7 +6,7 @@ import LocaleReceiver from '../locale-provider/LocaleReceiver';
import defaultLocale from '../locale-provider/default';
import Spin from '../spin';
import Pagination from '../pagination';
import Pagination, { PaginationConfig } from '../pagination';
import { Row } from '../grid';
import Item from './Item';
@ -49,7 +49,7 @@ export interface ListProps {
itemLayout?: string;
loading?: boolean | SpinProps;
loadMore?: React.ReactNode;
pagination?: any;
pagination?: PaginationConfig;
prefixCls?: string;
rowKey?: any;
renderItem: any;
@ -200,8 +200,9 @@ export default class List extends React.Component<ListProps> {
...this.defaultPaginationProps,
total: dataSource.length,
current: paginationCurrent,
...pagination,
...pagination || {},
};
const largestPage = Math.ceil(
paginationProps.total / paginationProps.pageSize,
);
@ -258,15 +259,18 @@ export default class List extends React.Component<ListProps> {
);
}
const paginationPosition = paginationProps.position || 'bottom';
return (
<div className={classString} {...rest}>
{(paginationPosition === 'top' || paginationPosition === 'both') && paginationContent}
{header && <div className={`${prefixCls}-header`}>{header}</div>}
<Spin {...loadingProp}>
{childrenContent}
{children}
</Spin>
{footer && <div className={`${prefixCls}-footer`}>{footer}</div>}
{loadMore || paginationContent}
{loadMore || (paginationPosition === 'bottom' || paginationPosition === 'both') && paginationContent}
</div>
);
}

View File

@ -1,7 +1,7 @@
---
category: Components
type: Data Display
title: List
title: List
subtitle: 列表
cols: 1
---
@ -30,6 +30,16 @@ cols: 1
| size | list 的尺寸 | `default` \| `middle` \| `small` | `default` |
| split | 是否展示分割线 | boolean | true |
### pagination
分页的配置项。
| 参数 | 说明 | 类型 | 默认值 |
| --- | --- | --- | --- |
| position | 指定分页显示的位置 | 'top' \| 'bottom' \| 'both' | 'bottom' |
更多配置项,请查看 [`Pagination`](/components/pagination/)。
### List grid props
| 参数 | 说明 | 类型 | 默认值 |

View File

@ -61,3 +61,14 @@ exports[`renders ./components/message/demo/other.md correctly 1`] = `
</button>
</div>
`;
exports[`renders ./components/message/demo/thenable.md correctly 1`] = `
<button
class="ant-btn"
type="button"
>
<span>
Display a sequence of message
</span>
</button>
`;

View File

@ -85,6 +85,17 @@ describe('message', () => {
});
});
it('should be called like promise', () => {
jest.useRealTimers();
const defaultDuration = 3;
const now = Date.now();
message.info('whatever').then(() => {
// calculate the approximately duration value
const aboutDuration = parseInt((Date.now() - now) / 1000, 10);
expect(aboutDuration).toBe(defaultDuration);
});
});
// https://github.com/ant-design/ant-design/issues/8201
it('should hide message correctly', () => {
let hide;

View File

@ -0,0 +1,28 @@
---
order: 5
title:
zh-CN: Promise 接口
en-US: Promise interface
---
## zh-CN
可以通过 then 接口在关闭后运行 callback 。以上用例将在每个 message 将要结束时通过 then 显示新的 message 。
## en-US
`message` provides promise interface for `onClose`. The above example will display a new message when old message is about to finish.
````jsx
import { message, Button } from 'antd';
const success = () => {
message.loading('Action in progress..', 2.5)
.then(() => message.success('Loading finished', 2.5))
.then(() => message.info('Loading finished is finished', 2.5));
};
ReactDOM.render(
<Button onClick={success}>Display a sequence of message</Button>
, mountNode);
````

View File

@ -34,6 +34,12 @@ Methods for global configuration and destruction are also provided:
- `message.config(options)`
- `message.destroy()`
`afterClose` can be called in then-able interface:
- `message[level](content, [duration]).then(afterClose)`
- `message[level](content, [duration], onClose).then(afterClose)`
where `level` refers one static methods of `message`. The result of `then` method will be a Promise.
### message.config
```js

View File

@ -1,3 +1,4 @@
/* global Promise */
import * as React from 'react';
import Notification from 'rc-notification';
import Icon from '../icon';
@ -34,12 +35,22 @@ function getMessageInstance(callback: (i: any) => void) {
type NoticeType = 'info' | 'success' | 'error' | 'warning' | 'loading';
export interface ThenableArgument {
(_: any): any;
}
export interface MessageType {
(): void;
then: (fill: ThenableArgument, reject: ThenableArgument) => Promise<any>;
promise: Promise<any>;
}
function notice(
content: React.ReactNode,
duration: (() => void) | number = defaultDuration,
type: NoticeType,
onClose?: () => void,
) {
): MessageType {
const iconType = ({
info: 'info-circle',
success: 'check-circle',
@ -54,25 +65,36 @@ function notice(
}
const target = key++;
getMessageInstance((instance) => {
instance.notice({
key: target,
duration,
style: {},
content: (
<div className={`${prefixCls}-custom-content ${prefixCls}-${type}`}>
<Icon type={iconType} />
<span>{content}</span>
</div>
),
onClose,
const closePromise = new Promise((resolve) => {
const callback = () => {
if (typeof onClose === 'function') {
onClose();
}
return resolve(true);
};
getMessageInstance((instance) => {
instance.notice({
key: target,
duration,
style: {},
content: (
<div className={`${prefixCls}-custom-content ${prefixCls}-${type}`}>
<Icon type={iconType} />
<span>{content}</span>
</div>
),
onClose: callback,
});
});
});
return () => {
const result: any = () => {
if (messageInstance) {
messageInstance.removeNotice(target);
}
};
result.then = (filled: ThenableArgument, rejected: ThenableArgument) => closePromise.then(filled, rejected);
result.promise = closePromise;
return result;
}
type ConfigContent = React.ReactNode | string;

View File

@ -35,6 +35,13 @@ title: Message
- `message.config(options)`
- `message.destroy()`
组件同时提供 promise 接口
- `message[level](content, [duration]).then(afterClose)`
- `message[level](content, [duration], onClose).then(afterClose)`
其中`message[level]` 是组件已经提供的静态方法。`then` 接口返回值是 Promise 。
### message.config
```js

View File

@ -29,6 +29,10 @@ export interface PaginationProps {
itemRender?: (page: number, type: 'page' | 'prev' | 'next' | 'jump-prev' | 'jump-next') => React.ReactNode;
}
export interface PaginationConfig extends PaginationProps {
position?: 'top' | 'bottom' | 'both';
}
export type PaginationLocale = any;
export default class Pagination extends React.Component<PaginationProps, {}> {

View File

@ -1,4 +1,4 @@
import Pagination from './Pagination';
export { PaginationProps } from './Pagination';
export { PaginationProps, PaginationConfig } from './Pagination';
export default Pagination;

View File

@ -205,18 +205,22 @@
@grid-gutter-width : 0;
// Layout
@layout-body-background : #f0f2f5;
@layout-header-background : #001529;
@layout-footer-background : @layout-body-background;
@layout-header-height : 64px;
@layout-header-padding : 0 50px;
@layout-footer-padding : 24px 50px;
@layout-sider-background : @layout-header-background;
@layout-trigger-height : 48px;
@layout-trigger-background : #002140;
@layout-trigger-color : #fff;
@layout-zero-trigger-width : 36px;
@layout-zero-trigger-height : 42px;
@layout-body-background : #f0f2f5;
@layout-header-background : #001529;
@layout-footer-background : @layout-body-background;
@layout-header-height : 64px;
@layout-header-padding : 0 50px;
@layout-footer-padding : 24px 50px;
@layout-sider-background : @layout-header-background;
@layout-trigger-height : 48px;
@layout-trigger-background : #002140;
@layout-trigger-color : #fff;
@layout-zero-trigger-width : 36px;
@layout-zero-trigger-height : 42px;
// Layout light theme
@layout-sider-background-light : #fff;
@layout-trigger-background-light: #fff;
@layout-trigger-color-light : @text-color;
// z-index list
@zindex-affix : 10;

View File

@ -29,9 +29,9 @@ import {
TableStateFilters,
SelectionItemSelectFn,
SelectionInfo,
TablePaginationConfig,
TableSelectWay,
TableRowSelection,
PaginationConfig,
} from './interface';
import { RadioChangeEvent } from '../radio';
import { CheckboxChangeEvent } from '../checkbox';
@ -156,7 +156,7 @@ export default class Table<T> extends React.Component<TableProps<T>, TableState<
}
getDefaultPagination(props: TableProps<T>) {
const pagination: TablePaginationConfig = props.pagination || {};
const pagination: PaginationConfig = props.pagination || {};
return this.hasPagination(props) ?
{
...defaultPagination,

View File

@ -1,9 +1,10 @@
import * as React from 'react';
import { PaginationProps } from '../pagination';
import { SpinProps } from '../spin';
import { Store } from './createStore';
import { RadioChangeEvent } from '../radio';
import { CheckboxChangeEvent } from '../checkbox';
import { PaginationConfig } from '../pagination';
export { PaginationConfig } from '../pagination';
export type CompareFn<T> = ((a: T, b: T, sortOrder?: 'ascend' | 'descend') => number);
export type ColumnFilterItem = { text: string; value: string, children?: ColumnFilterItem[] };
@ -61,10 +62,6 @@ export interface TableLocale {
export type RowSelectionType = 'checkbox' | 'radio';
export type SelectionSelectFn<T> = (record: T, selected: boolean, selectedRows: Object[], nativeEvent: Event) => any;
export interface TablePaginationConfig extends PaginationProps {
position?: 'top' | 'bottom' | 'both';
}
export type TableSelectWay = 'onSelect' | 'onSelectAll' | 'onSelectInvert';
export interface TableRowSelection<T> {
@ -85,7 +82,7 @@ export interface TableProps<T> {
prefixCls?: string;
dropdownPrefixCls?: string;
rowSelection?: TableRowSelection<T>;
pagination?: TablePaginationConfig | false;
pagination?: PaginationConfig | false;
size?: 'default' | 'middle' | 'small';
dataSource?: T[];
components?: TableComponents;
@ -101,7 +98,7 @@ export interface TableProps<T> {
expandRowByClick?: boolean;
onExpandedRowsChange?: (expandedRowKeys: string[] | number[]) => void;
onExpand?: (expanded: boolean, record: T) => void;
onChange?: (pagination: TablePaginationConfig | boolean, filters: string[], sorter: Object) => any;
onChange?: (pagination: PaginationConfig | boolean, filters: string[], sorter: Object) => any;
loading?: boolean | SpinProps;
locale?: Object;
indentSize?: number;
@ -126,7 +123,7 @@ export interface TableStateFilters {
}
export interface TableState<T> {
pagination: TablePaginationConfig;
pagination: PaginationConfig;
filters: TableStateFilters;
sortColumn: ColumnProps<T> | null;
sortOrder: 'ascend' | 'descend' | undefined;

View File

@ -248,4 +248,28 @@ describe('Transfer', () => {
const wrapper = render(<Transfer {...sortedTargetKeyProps} render={item => item.title} />);
expect(wrapper).toMatchSnapshot();
});
it('should add custom styles when their props are provided', () => {
const style = {
backgroundColor: 'red',
};
const listStyle = {
backgroundColor: 'blue',
};
const operationStyle = {
backgroundColor: 'yellow',
};
const component = mount(<Transfer {...listCommonProps} style={style} listStyle={listStyle} operationStyle={operationStyle} />);
const wrapper = component.find('.ant-transfer');
const listSource = component.find('.ant-transfer-list').first();
const listTarget = component.find('.ant-transfer-list').last();
const operation = component.find('.ant-transfer-operation').first();
expect(wrapper.prop('style')).toHaveProperty('backgroundColor', 'red');
expect(listSource.prop('style')).toHaveProperty('backgroundColor', 'blue');
expect(listTarget.prop('style')).toHaveProperty('backgroundColor', 'blue');
expect(operation.prop('style')).toHaveProperty('backgroundColor', 'yellow');
});
});

View File

@ -22,7 +22,9 @@ 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 | |
| lazy | property of [react-lazy-load](https://github.com/loktar00/react-lazy-load) for lazy rendering items. Turn off it by set to `false`. | object\|boolean | `{ height: 32, offset: 32 }` |
| style | A custom CSS style used for rendering wrapper element. | object | |
| listStyle | A custom CSS style used for rendering the transfer columns. | object | |
| operationStyle | A custom CSS style used for rendering the operations column. | object | |
| notFoundContent | Text to display when a column is empty. | string\|ReactNode | 'The list is empty' |
| operations | A set of operations that are sorted from bottom to top. | string\[] | ['>', '<'] |
| render | The function to generate the item shown on a column. Based on an record (element of the dataSource array), this function should return a React element which is generated from that record. Also, it can return a plain object with `value` and `label`, `label` is a React element and `value` is for title | Function(record) | |

View File

@ -34,6 +34,7 @@ export interface TransferProps {
onSelectChange?: (sourceSelectedKeys: string[], targetSelectedKeys: string[]) => void;
style?: React.CSSProperties;
listStyle?: React.CSSProperties;
operationStyle?: React.CSSProperties;
titles?: string[];
operations?: string[];
showSearch?: boolean;
@ -75,7 +76,9 @@ export default class Transfer extends React.Component<TransferProps, any> {
targetKeys: PropTypes.array,
onChange: PropTypes.func,
height: PropTypes.number,
style: PropTypes.object,
listStyle: PropTypes.object,
operationStyle: PropTypes.object,
className: PropTypes.string,
titles: PropTypes.array,
operations: PropTypes.array,
@ -328,7 +331,9 @@ export default class Transfer extends React.Component<TransferProps, any> {
searchPlaceholder,
body,
footer,
style,
listStyle,
operationStyle,
filterOption,
render,
lazy,
@ -343,7 +348,7 @@ export default class Transfer extends React.Component<TransferProps, any> {
const titles = this.getTitles(locale);
return (
<div className={cls}>
<div className={cls} style={style}>
<List
prefixCls={`${prefixCls}-list`}
titleText={titles[0]}
@ -375,6 +380,7 @@ export default class Transfer extends React.Component<TransferProps, any> {
leftActive={leftActive}
leftArrowText={operations[1]}
moveToLeft={this.moveToLeft}
style={operationStyle}
/>
<List
prefixCls={`${prefixCls}-list`}

View File

@ -9,6 +9,7 @@ export interface TransferOperationProps {
moveToRight?: React.FormEventHandler<HTMLButtonElement>;
leftActive?: boolean;
rightActive?: boolean;
style?: React.CSSProperties;
}
export default class Operation extends React.Component<TransferOperationProps, any> {
@ -21,9 +22,10 @@ export default class Operation extends React.Component<TransferOperationProps, a
leftActive,
rightActive,
className,
style,
} = this.props;
return (
<div className={className}>
<div className={className} style={style}>
<Button
type="primary"
size="small"

View File

@ -46,6 +46,7 @@
"css-animation": "^1.2.5",
"dom-closest": "^0.2.0",
"enquire.js": "^2.1.1",
"intersperse": "^1.0.0",
"lodash": "^4.17.5",
"moment": "^2.19.3",
"omit.js": "^1.0.0",

View File

@ -98,4 +98,6 @@ declare module "lodash/debounce";
declare module "lodash/uniqBy";
declare module 'intersperse';
declare module "raf";