Refactor: introduce injectLocale (#5289)

* refactor: extract injectLocale and refactor Pagination, ref: #5103

* refactor: use injectLocale in Popconfirm, ref: #5103

* refactor: use injectLocale in TimePicker

* refactor: use injectLocale in Transfer

* refactor: use injectLocale in TreeSelect

* refactor: remove useless code in AutoComplete

* test: update snapshot
This commit is contained in:
Benjy Cui 2017-03-17 15:23:25 +08:00 committed by GitHub
parent 983474f81c
commit 8f81594f91
12 changed files with 164 additions and 218 deletions

View File

@ -71,10 +71,6 @@ export default class AutoComplete extends React.Component<AutoCompleteProps, any
showSearch: false,
};
static contextTypes = {
antLocale: React.PropTypes.object,
};
getInputElement = () => {
const { children } = this.props;
const element = children && React.isValidElement(children) && children.type !== Option ?

View File

@ -0,0 +1,32 @@
import React, { PropTypes } from 'react';
export interface ComponentProps {
locale?: any;
}
export interface ComponentContext {
antLocale?: { [key: string]: any };
}
export default (componentName: string, defaultLocale) => (
function<P>(Component: typeof React.Component): React.ComponentClass<P> {
return class extends Component<P & ComponentProps, any> {
static contextTypes = {
antLocale: PropTypes.object,
};
context: ComponentContext;
getLocale() {
const { antLocale } = this.context;
const localeFromContext = antLocale && antLocale[componentName];
const localeFromProps: any = this.props.locale || {};
return {
...defaultLocale,
...(localeFromContext || {}),
...localeFromProps,
};
}
};
}
);

View File

@ -1,87 +1,54 @@
import React from 'react';
import RcPagination from 'rc-pagination';
import zhCN from 'rc-pagination/lib/locale/zh_CN';
import classNames from 'classnames';
import injectLocale from '../locale-provider/injectLocale';
import Select from '../select';
import MiniSelect from './MiniSelect';
import zhCN from 'rc-pagination/lib/locale/zh_CN';
export interface PaginationProps {
/** 当前页数*/
current?: number;
/** 默认的当前页数*/
defaultCurrent?: number;
/** 数据总数*/
total: number;
/** 初始的每页条数*/
defaultCurrent?: number;
current?: number;
defaultPageSize?: number;
/** 每页条数*/
pageSize?: number;
/** 页码改变的回调,参数是改变后的页码*/
onChange?: (page: number, pageSize: number) => void;
/** 是否可以改变 pageSize */
showSizeChanger?: boolean;
/** 指定每页可以显示多少条*/
pageSizeOptions?: Array<string>;
/** pageSize 变化的回调 */
pageSizeOptions?: string[];
onShowSizeChange?: (current: number, size: number) => void;
/** 是否可以快速跳转至某页*/
showQuickJumper?: boolean;
/** 当为「small」时是小尺寸分页 */
size?: string;
/** 当添加该属性时,显示为简单分页*/
simple?: boolean;
/** 用于显示总共有多少条数据*/
showTotal?: (total: number) => React.ReactNode;
size?: string;
simple?: boolean;
style?: React.CSSProperties;
className?: string;
locale?: Object;
className?: string;
prefixCls?: string;
selectPrefixCls?: string;
}
export interface PaginationContext {
antLocale?: {
Pagination?: any,
};
}
export default class Pagination extends React.Component<PaginationProps, any> {
abstract class Pagination extends React.Component<PaginationProps, any> {
static defaultProps = {
locale: zhCN,
className: '',
prefixCls: 'ant-pagination',
selectPrefixCls: 'ant-select',
};
static contextTypes = {
antLocale: React.PropTypes.object,
};
context: PaginationContext;
abstract getLocale()
render() {
let className = this.props.className;
let selectComponentClass = Select as React.ReactNode;
let locale;
if (this.context.antLocale && this.context.antLocale.Pagination) {
locale = this.context.antLocale.Pagination;
} else {
locale = this.props.locale;
}
if (this.props.size === 'small') {
className += ' mini';
selectComponentClass = MiniSelect;
}
const { className, size, ...restProps } = this.props;
const locale = this.getLocale();
const isSmall = size === 'small';
return (
<RcPagination
selectComponentClass={selectComponentClass}
selectPrefixCls={this.props.selectPrefixCls}
{...this.props}
{...restProps}
className={classNames(className, { mini: isSmall })}
selectComponentClass={isSmall ? MiniSelect : Select}
locale={locale}
className={className}
/>
);
}
}
const injectPaginationLocale = injectLocale('Pagination', zhCN);
export default injectPaginationLocale<PaginationProps>(Pagination as any);

View File

@ -274,7 +274,7 @@ exports[`test renders ./components/pagination/demo/jump.md correctly 1`] = `
exports[`test renders ./components/pagination/demo/mini.md correctly 1`] = `
<div>
<ul
class="ant-pagination mini"
class="ant-pagination mini"
unselectable="unselectable">
<li
class="ant-pagination-disabled ant-pagination-prev"
@ -323,7 +323,7 @@ exports[`test renders ./components/pagination/demo/mini.md correctly 1`] = `
</li>
</ul>
<ul
class="ant-pagination mini"
class="ant-pagination mini"
unselectable="unselectable">
<li
class="ant-pagination-disabled ant-pagination-prev"
@ -410,7 +410,7 @@ exports[`test renders ./components/pagination/demo/mini.md correctly 1`] = `
</div>
</ul>
<ul
class="ant-pagination mini"
class="ant-pagination mini"
unselectable="unselectable">
<span
class="ant-pagination-total-text">

View File

@ -1,8 +1,8 @@
import React from 'react';
import Tooltip from '../tooltip';
import { AbstractTooltipProps } from '../tooltip';
import Tooltip, { AbstractTooltipProps } from '../tooltip';
import Icon from '../icon';
import Button from '../button';
import injectLocale from '../locale-provider/injectLocale';
export interface PopconfirmProps extends AbstractTooltipProps {
title: React.ReactNode;
@ -12,13 +12,7 @@ export interface PopconfirmProps extends AbstractTooltipProps {
cancelText?: React.ReactNode;
}
export interface PopconfirmContext {
antLocale?: {
Popconfirm?: any,
};
}
export default class Popconfirm extends React.Component<PopconfirmProps, any> {
abstract class Popconfirm extends React.Component<PopconfirmProps, any> {
static defaultProps = {
prefixCls: 'ant-popover',
transitionName: 'zoom-big',
@ -26,12 +20,6 @@ export default class Popconfirm extends React.Component<PopconfirmProps, any> {
trigger: 'click',
};
static contextTypes = {
antLocale: React.PropTypes.object,
};
context: PopconfirmContext;
constructor(props: PopconfirmProps) {
super(props);
@ -40,25 +28,27 @@ export default class Popconfirm extends React.Component<PopconfirmProps, any> {
};
}
abstract getLocale()
componentWillReceiveProps(nextProps: PopconfirmProps) {
if ('visible' in nextProps) {
this.setState({ visible: nextProps.visible });
}
}
confirm = (e) => {
onConfirm = (e) => {
this.setVisible(false);
const onConfirm = this.props.onConfirm;
const { onConfirm } = this.props;
if (onConfirm) {
onConfirm.call(this, e);
}
}
cancel = (e) => {
onCancel = (e) => {
this.setVisible(false);
const onCancel = this.props.onCancel;
const { onCancel } = this.props;
if (onCancel) {
onCancel.call(this, e);
}
@ -74,22 +64,15 @@ export default class Popconfirm extends React.Component<PopconfirmProps, any> {
this.setState({ visible });
}
const onVisibleChange = props.onVisibleChange;
const { onVisibleChange } = props;
if (onVisibleChange) {
onVisibleChange(visible);
}
}
render() {
const { props, context } = this;
const { prefixCls, title, placement, ...restProps } = props;
let { okText, cancelText } = props;
const popconfirmLocale = context.antLocale && context.antLocale.Popconfirm;
if (popconfirmLocale) {
okText = okText || popconfirmLocale.okText;
cancelText = cancelText || popconfirmLocale.cancelText;
}
const { prefixCls, title, placement, okText, cancelText, ...restProps } = this.props;
const popconfirmLocale = this.getLocale();
const overlay = (
<div>
@ -99,8 +82,12 @@ export default class Popconfirm extends React.Component<PopconfirmProps, any> {
<div className={`${prefixCls}-message-title`}>{title}</div>
</div>
<div className={`${prefixCls}-buttons`}>
<Button onClick={this.cancel} size="small">{cancelText || '取消'}</Button>
<Button onClick={this.confirm} type="primary" size="small">{okText || '确定'}</Button>
<Button onClick={this.onCancel} size="small">
{cancelText || popconfirmLocale.cancelText}
</Button>
<Button onClick={this.onConfirm} type="primary" size="small">
{okText || popconfirmLocale.okText}
</Button>
</div>
</div>
</div>
@ -118,3 +105,9 @@ export default class Popconfirm extends React.Component<PopconfirmProps, any> {
);
}
}
const injectPopconfirmLocale = injectLocale('Popconfirm', {
cancelText: '取消',
okText: '确定',
});
export default injectPopconfirmLocale<PopconfirmProps>(Popconfirm as any);

View File

@ -1,19 +1,18 @@
import React from 'react';
import RcTable from 'rc-table';
import FilterDropdown from './filterDropdown';
import classNames from 'classnames';
import assign from 'object-assign';
import Pagination, { PaginationProps } from '../pagination';
import Icon from '../icon';
import Spin from '../spin';
import classNames from 'classnames';
import { flatArray, treeMap, flatFilter, normalizeColumns } from './util';
import assign from 'object-assign';
import Spin, { SpinProps } from '../spin';
import warning from '../_util/warning';
import FilterDropdown from './filterDropdown';
import createStore, { Store } from './createStore';
import SelectionBox from './SelectionBox';
import SelectionCheckboxAll, { SelectionDecorator } from './SelectionCheckboxAll';
import Column, { ColumnProps } from './Column';
import ColumnGroup from './ColumnGroup';
import { SpinProps } from '../spin';
import { flatArray, treeMap, flatFilter, normalizeColumns } from './util';
function noop() {
}

View File

@ -3,44 +3,28 @@ import moment from 'moment';
import RcTimePicker from 'rc-time-picker/lib/TimePicker';
import classNames from 'classnames';
import assign from 'object-assign';
import injectLocale from '../locale-provider/injectLocale';
import defaultLocale from './locale/zh_CN';
// TimePicker
export interface TimePickerProps {
className?: string;
size?: 'large' | 'default' | 'small';
/** 默认时间 */
value?: moment.Moment;
/** 初始默认时间 */
defaultValue?: moment.Moment;
/** 展示的时间格式 : "HH:mm:ss"、"HH:mm"、"mm:ss" */
format?: string;
/** 时间发生变化的回调 */
onChange?: (time: moment.Moment, timeString: string) => void;
/** 禁用全部操作 */
disabled?: boolean;
/** 没有值的时候显示的内容 */
placeholder?: string;
/** 隐藏禁止选择的选项 */
hideDisabledOptions?: boolean;
/** 禁止选择部分小时选项 */
disabledHours?: Function;
/** 禁止选择部分分钟选项 */
disabledMinutes?: Function;
/** 禁止选择部分秒选项 */
disabledSeconds?: Function;
disabledHours?: () => number[];
disabledMinutes?: (selectedHour: number) => number[];
disabledSeconds?: (selectedHour: number, selectedMinute: number) => number[];
style?: React.CSSProperties;
getPopupContainer?: (trigger: any) => any;
addon?: Function;
}
export interface TimePickerContext {
antLocale?: {
TimePicker?: any,
};
}
export default class TimePicker extends React.Component<TimePickerProps, any> {
abstract class TimePicker extends React.Component<TimePickerProps, any> {
static defaultProps = {
prefixCls: 'ant-time-picker',
align: {
@ -55,14 +39,9 @@ export default class TimePicker extends React.Component<TimePickerProps, any> {
transitionName: 'slide-up',
};
static contextTypes = {
antLocale: React.PropTypes.object,
};
context: TimePickerContext;
timePickerRef: any;
constructor(props) {
constructor(props: TimePickerProps) {
super(props);
const value = props.value || props.defaultValue;
if (value && !moment.isMoment(value)) {
@ -76,7 +55,9 @@ export default class TimePicker extends React.Component<TimePickerProps, any> {
};
}
componentWillReceiveProps(nextProps) {
abstract getLocale()
componentWillReceiveProps(nextProps: TimePickerProps) {
if ('value' in nextProps) {
this.setState({ value: nextProps.value });
}
@ -92,12 +73,6 @@ export default class TimePicker extends React.Component<TimePickerProps, any> {
}
}
getLocale() {
const antLocale = this.context.antLocale;
const timePickerLocale = (antLocale && antLocale.TimePicker) || defaultLocale;
return timePickerLocale;
}
saveTimePicker = (timePickerRef) => {
this.timePickerRef = timePickerRef;
}
@ -138,3 +113,6 @@ export default class TimePicker extends React.Component<TimePickerProps, any> {
);
}
}
const injectTimePickerLocale = injectLocale('TimePicker', defaultLocale);
export default injectTimePickerLocale<TimePickerProps>(TimePicker as any);

View File

@ -18,6 +18,7 @@ const listCommonProps = {
disabled: true,
}],
checkedKeys: ['a'],
notFoundContent: 'Not Found',
lazy: false,
};

View File

@ -1,10 +1,9 @@
import React from 'react';
import { PropTypes } from 'react';
import React, { PropTypes } from 'react';
import classNames from 'classnames';
import List from './list';
import { TransferListProps } from './list';
import List, { TransferListProps } from './list';
import Operation from './operation';
import Search from './search';
import injectLocale from '../locale-provider/injectLocale';
function noop() {
}
@ -41,14 +40,7 @@ export interface TransferProps {
onScroll?: (direction: 'left' | 'right', e: Event) => void;
}
export interface TransferContext {
antLocale?: {
Transfer?: any,
};
}
const defaultTitles = ['', ''];
export default class Transfer extends React.Component<TransferProps, any> {
abstract class Transfer extends React.Component<TransferProps, any> {
// For high-level customized Transfer @dqaria
static List = List;
static Operation = Operation;
@ -81,11 +73,6 @@ export default class Transfer extends React.Component<TransferProps, any> {
lazy: PropTypes.oneOfType([PropTypes.object, PropTypes.bool]),
};
static contextTypes = {
antLocale: PropTypes.object,
};
context: TransferContext;
splitedDataSource: any;
constructor(props: TransferProps) {
@ -100,6 +87,8 @@ export default class Transfer extends React.Component<TransferProps, any> {
};
}
abstract getLocale()
componentWillReceiveProps(nextProps: TransferProps) {
const { sourceSelectedKeys, targetSelectedKeys } = this.state;
if (nextProps.targetKeys !== this.props.targetKeys ||
@ -270,17 +259,12 @@ export default class Transfer extends React.Component<TransferProps, any> {
handleRightScroll = (e) => this.handleScroll('right', e);
getTitles(): string[] {
const { props, context } = this;
const { props } = this;
if (props.titles) {
return props.titles;
}
const transferLocale = context &&
context.antLocale &&
context.antLocale.Transfer;
if (transferLocale) {
return transferLocale.titles || [];
}
return defaultTitles;
const transferLocale = this.getLocale();
return transferLocale.titles;
}
getSelectedKeysName(direction) {
@ -288,10 +272,20 @@ export default class Transfer extends React.Component<TransferProps, any> {
}
render() {
const locale = this.getLocale();
const {
prefixCls = 'ant-transfer', operations = [], showSearch, notFoundContent,
searchPlaceholder, body, footer, listStyle, className = '',
filterOption, render, lazy,
prefixCls = 'ant-transfer',
className,
operations = [],
showSearch,
notFoundContent = locale.notFoundContent,
searchPlaceholder = locale.searchPlaceholder,
body,
footer,
listStyle,
filterOption,
render,
lazy,
} = this.props;
const { leftFilter, rightFilter, sourceSelectedKeys, targetSelectedKeys } = this.state;
@ -305,6 +299,7 @@ export default class Transfer extends React.Component<TransferProps, any> {
return (
<div className={cls}>
<List
prefixCls={`${prefixCls}-list`}
titleText={titles[0]}
dataSource={leftDataSource}
filter={leftFilter}
@ -319,22 +314,24 @@ export default class Transfer extends React.Component<TransferProps, any> {
showSearch={showSearch}
searchPlaceholder={searchPlaceholder}
notFoundContent={notFoundContent}
itemUnit={locale.itemUnit}
itemsUnit={locale.itemsUnit}
body={body}
footer={footer}
prefixCls={`${prefixCls}-list`}
lazy={lazy}
onScroll={this.handleLeftScroll}
/>
<Operation
className={`${prefixCls}-operation`}
rightActive={rightActive}
rightArrowText={operations[0]}
moveToRight={this.moveToRight}
leftActive={leftActive}
leftArrowText={operations[1]}
moveToLeft={this.moveToLeft}
className={`${prefixCls}-operation`}
/>
<List
prefixCls={`${prefixCls}-list`}
titleText={titles[1]}
dataSource={rightDataSource}
filter={rightFilter}
@ -349,9 +346,10 @@ export default class Transfer extends React.Component<TransferProps, any> {
showSearch={showSearch}
searchPlaceholder={searchPlaceholder}
notFoundContent={notFoundContent}
itemUnit={locale.itemUnit}
itemsUnit={locale.itemsUnit}
body={body}
footer={footer}
prefixCls={`${prefixCls}-list`}
lazy={lazy}
onScroll={this.handleRightScroll}
/>
@ -359,3 +357,10 @@ export default class Transfer extends React.Component<TransferProps, any> {
);
}
}
const injectTransferLocale = injectLocale('Transfer', {
titles: ['', ''],
searchPlaceholder: 'Search',
notFoundContent: 'Not Found',
});
export default injectTransferLocale<TransferProps>(Transfer as any);

View File

@ -3,10 +3,10 @@ import classNames from 'classnames';
import Animate from 'rc-animate';
import PureRenderMixin from 'rc-util/lib/PureRenderMixin';
import assign from 'object-assign';
import Checkbox from '../checkbox';
import { TransferItem } from './index';
import Search from './search';
import Item from './item';
import Checkbox from '../checkbox';
function noop() {
}
@ -18,34 +18,28 @@ function isRenderResultPlainObject(result) {
export interface TransferListProps {
prefixCls: string;
titleText: string;
dataSource: TransferItem[];
filter?: string;
showSearch?: boolean;
searchPlaceholder?: string;
titleText?: string;
filter: string;
filterOption: (filterText: any, item: any) => boolean;
style?: React.CSSProperties;
checkedKeys: string[];
handleFilter: (e: any) => void;
handleSelect: (selectedItem: any, checked: boolean) => void;
handleSelectAll: (dataSource: any[], checkAll: boolean) => void;
handleClear: () => void;
render?: (item: any) => any;
showSearch?: boolean;
searchPlaceholder: string;
notFoundContent: React.ReactNode;
itemUnit: string;
itemsUnit: string;
body?: (props: any) => any;
footer?: (props: any) => void;
checkedKeys: string[];
checkStatus?: boolean;
position?: string;
notFoundContent?: React.ReactNode;
filterOption: (filterText: any, item: any) => boolean;
lazy?: boolean | {};
onScroll: Function;
}
export interface TransferListContext {
antLocale?: {
Transfer?: any,
};
}
export default class TransferList extends React.Component<TransferListProps, any> {
static defaultProps = {
dataSource: [],
@ -55,11 +49,6 @@ export default class TransferList extends React.Component<TransferListProps, any
lazy: {},
};
static contextTypes = {
antLocale: React.PropTypes.object,
};
context: TransferListContext;
timer: number;
constructor(props) {
@ -131,11 +120,9 @@ export default class TransferList extends React.Component<TransferListProps, any
const {
prefixCls, dataSource, titleText, checkedKeys, lazy,
body = noop, footer = noop, showSearch, style, filter,
onScroll,
searchPlaceholder, notFoundContent, itemUnit, itemsUnit, onScroll,
} = this.props;
let { searchPlaceholder, notFoundContent } = this.props;
// Custom Layout
const footerDom = footer(assign({}, this.props));
const bodyDom = body(assign({}, this.props));
@ -175,14 +162,7 @@ export default class TransferList extends React.Component<TransferListProps, any
);
});
let unit = '';
const antLocale = this.context.antLocale;
if (antLocale && antLocale.Transfer) {
const transferLocale = antLocale.Transfer;
unit = dataSource.length > 1 ? transferLocale.itemsUnit : transferLocale.itemUnit;
searchPlaceholder = searchPlaceholder || transferLocale.searchPlaceholder;
notFoundContent = notFoundContent || transferLocale.notFoundContent;
}
const unit = dataSource.length > 1 ? itemsUnit : itemUnit;
const search = showSearch ? (
<div className={`${prefixCls}-body-search-wrapper`}>
@ -190,7 +170,7 @@ export default class TransferList extends React.Component<TransferListProps, any
prefixCls={`${prefixCls}-search`}
onChange={this.handleFilter}
handleClear={this.handleClear}
placeholder={searchPlaceholder || 'Search'}
placeholder={searchPlaceholder}
value={filter}
/>
</div>
@ -209,7 +189,7 @@ export default class TransferList extends React.Component<TransferListProps, any
{showItems}
</Animate>
<div className={`${prefixCls}-body-not-found`}>
{notFoundContent || 'Not Found'}
{notFoundContent}
</div>
</div>
);

View File

@ -1,11 +1,12 @@
import React from 'react';
import RcTreeSelect, { TreeNode, SHOW_ALL, SHOW_PARENT, SHOW_CHILD } from 'rc-tree-select';
import classNames from 'classnames';
import { TreeSelectProps, TreeSelectContext } from './interface';
import { TreeSelectProps } from './interface';
import injectLocale from '../locale-provider/injectLocale';
export { TreeSelectProps };
export default class TreeSelect extends React.Component<TreeSelectProps, any> {
abstract class TreeSelect extends React.Component<TreeSelectProps, any> {
static TreeNode = TreeNode;
static SHOW_ALL = SHOW_ALL;
static SHOW_PARENT = SHOW_PARENT;
@ -19,16 +20,17 @@ export default class TreeSelect extends React.Component<TreeSelectProps, any> {
dropdownClassName: 'ant-select-tree-dropdown',
};
static contextTypes = {
antLocale: React.PropTypes.object,
};
context: TreeSelectContext;
abstract getLocale()
render() {
const props = this.props;
let {
size, className = '', notFoundContent, prefixCls, dropdownStyle,
const locale = this.getLocale();
const { props } = this;
const {
prefixCls,
className,
size,
notFoundContent = locale.notFoundContent,
dropdownStyle,
} = this.props;
const cls = classNames({
@ -36,11 +38,6 @@ export default class TreeSelect extends React.Component<TreeSelectProps, any> {
[`${prefixCls}-sm`]: size === 'small',
}, className);
const { antLocale } = this.context;
if (antLocale && antLocale.Select) {
notFoundContent = notFoundContent || antLocale.Select.notFoundContent;
}
let checkable = props.treeCheckable;
if (checkable) {
checkable = <span className={`${prefixCls}-tree-checkbox-inner`} />;
@ -48,7 +45,7 @@ export default class TreeSelect extends React.Component<TreeSelectProps, any> {
return (
<RcTreeSelect
{...this.props}
{...props}
dropdownStyle={{ maxHeight: '100vh', overflow: 'auto', ...dropdownStyle }}
treeCheckable={checkable}
className={cls}
@ -57,3 +54,7 @@ export default class TreeSelect extends React.Component<TreeSelectProps, any> {
);
}
}
// Use Select's locale
const injectSelectLocale = injectLocale('Select', {});
export default injectSelectLocale<TreeSelectProps>(TreeSelect as any);

View File

@ -41,9 +41,3 @@ export interface TreeSelectProps {
treeCheckStrictly?: boolean;
getPopupContainer?: (triggerNode: React.ReactNode) => HTMLElement;
}
export interface TreeSelectContext {
antLocale?: {
Select?: any,
};
}