Customize Input Component of AutoComplete (#4483)

* cherry-pick

* remove useless files

* remove useless files

* Group ts

* update interface

* ReactElement
This commit is contained in:
陆离 2017-01-13 21:19:23 +08:00 committed by 偏右
parent 47fcd7651f
commit 46c200896f
10 changed files with 237 additions and 20 deletions

View File

@ -1,6 +1,48 @@
exports[`test renders ./components/auto-complete/demo/antd.md correctly 1`] = `
<div
class="ant-select-show-search ant-select-auto-complete ant-select ant-select-combobox ant-select-enabled"
style="width:200px;">
<div
aria-autocomplete="list"
aria-expanded="false"
aria-haspopup="true"
class="ant-select-selection
ant-select-selection--single"
role="combobox">
<div
class="ant-select-selection__rendered">
<div
class="ant-select-selection__placeholder"
style="display:block;user-select:none;-webkit-user-select:none;"
unselectable="unselectable">
input here
</div>
<ul>
<li
class="ant-select-search ant-select-search--inline">
<div
class="ant-select-search__field__wrap">
<textarea
class="ant-input ant-select-search__field" />
<span
class="ant-select-search__field__mirror" />
</div>
</li>
</ul>
</div>
<span
class="ant-select-arrow"
style="user-select:none;-webkit-user-select:none;"
unselectable="unselectable">
<b />
</span>
</div>
</div>
`;
exports[`test renders ./components/auto-complete/demo/basic.md correctly 1`] = `
<div
class="ant-select-show-search ant-select ant-select-combobox ant-select-enabled"
class="ant-select-show-search ant-select-auto-complete ant-select ant-select-combobox ant-select-enabled"
style="width:200px;">
<div
aria-autocomplete="list"
@ -23,7 +65,8 @@ exports[`test renders ./components/auto-complete/demo/basic.md correctly 1`] = `
<div
class="ant-select-search__field__wrap">
<input
class="ant-select-search__field"
class="ant-input ant-input ant-select-search__field"
type="text"
value="" />
<span
class="ant-select-search__field__mirror" />
@ -43,7 +86,7 @@ exports[`test renders ./components/auto-complete/demo/basic.md correctly 1`] = `
exports[`test renders ./components/auto-complete/demo/options.md correctly 1`] = `
<div
class="ant-select-show-search ant-select ant-select-combobox ant-select-enabled"
class="ant-select-show-search ant-select-auto-complete ant-select ant-select-combobox ant-select-enabled"
style="width:200px;">
<div
aria-autocomplete="list"
@ -66,7 +109,8 @@ exports[`test renders ./components/auto-complete/demo/options.md correctly 1`] =
<div
class="ant-select-search__field__wrap">
<input
class="ant-select-search__field"
class="ant-input ant-input ant-select-search__field"
type="text"
value="" />
<span
class="ant-select-search__field__mirror" />

View File

@ -0,0 +1,20 @@
import React from 'react';
import { mount } from 'enzyme';
import AutoComplete from '..';
describe('AutoComplete with Custom Input Element Render', () => {
it('AutoComplete with custom Input render perfectly', () => {
const wrapper = mount(
<AutoComplete dataSource={['12345', '23456', '34567']}>
<textarea />
</AutoComplete>
);
expect(wrapper.find('textarea').length).toBe(1);
wrapper.find('textarea').simulate('change', { target: { value: '123' } });
const dropdownWrapper = mount(wrapper.find('Trigger').node.getComponent());
expect(dropdownWrapper.find('MenuItem').length).toBe(1);
expect(dropdownWrapper.find('MenuItem').props().value).toBe('12345');
});
});

View File

@ -0,0 +1,58 @@
---
order: 3
title:
zh-CN: 自定义输入组件
en-US: Customize Input Component
---
## zh-CN
自定义输入组件。
## en-US
Customize Input Component
````jsx
import { AutoComplete } from 'antd';
function onSelect(value) {
console.log('onSelect', value);
}
const Complete = React.createClass({
getInitialState() {
return {
dataSource: [],
};
},
handleChange(value) {
this.setState({
dataSource: !value ? [] : [
value,
value + value,
value + value + value,
],
});
},
handleKeyPress(ev) {
console.log('handleKeyPress', ev);
},
render() {
const { dataSource } = this.state;
return (
<AutoComplete
dataSource={dataSource}
style={{ width: 200 }}
onSelect={onSelect}
onChange={this.handleChange}
placeholder="input here"
>
<textarea onKeyPress={this.handleKeyPress} />
</AutoComplete>
);
},
});
ReactDOM.render(<Complete />, mountNode);
````

View File

@ -29,3 +29,5 @@ Since `AutoComplete` is based on `Select`, so besides the following API, `AutoCo
| onSelect | Called when a option is selected. param is option's value and option instance. | function(value, option) | - |
| disabled | Whether disabled select | boolean | false |
| placeholder | placeholder of input | string | - |
| children (for dataSource) | Data source for autocomplet | React.ReactElement<OptionProps> / Array<React.ReactElement<OptionProps>> | - |
| children (for customize input element) | customize input element | HTMLInputElement / HTMLTextAreaElement / React.ReactElement<InputProps> | `<Input />` |

View File

@ -1,5 +1,7 @@
import React from 'react';
import { findDOMNode } from 'react-dom';
import Select, { OptionProps, OptGroupProps } from '../select';
import Input from '../input';
import { Option, OptGroup } from 'rc-select';
import classNames from 'classnames';
@ -11,6 +13,16 @@ export interface SelectedValue {
export interface DataSourceItemObject { value: string; text: string; };
export type DataSourceItemType = string | DataSourceItemObject;
export interface InputProps {
onChange?: React.FormEventHandler<any>;
value: any;
}
export type ValidInputElement =
HTMLInputElement |
HTMLTextAreaElement |
React.ReactElement<InputProps>;
export interface AutoCompleteProps {
size?: 'large' | 'small' | 'default';
className?: string;
@ -26,6 +38,25 @@ export interface AutoCompleteProps {
allowClear?: boolean;
onChange?: (value: string | Array<any> | SelectedValue | Array<SelectedValue>) => void;
disabled?: boolean;
children: ValidInputElement |
React.ReactElement<OptionProps> |
Array<React.ReactElement<OptionProps>>;
}
class InputElement extends React.Component<any, any> {
private ele: Element;
focus = () => {
(findDOMNode(this.ele) as HTMLInputElement).focus();
}
blur = () => {
(findDOMNode(this.ele) as HTMLInputElement).blur();
}
render() {
return React.cloneElement(this.props.children, {
...this.props,
ref: ele => this.ele = ele,
}, null);
}
}
export default class AutoComplete extends React.Component<AutoCompleteProps, any> {
@ -44,6 +75,13 @@ export default class AutoComplete extends React.Component<AutoCompleteProps, any
antLocale: React.PropTypes.object,
};
getInputElement = () => {
const { children } = this.props;
const element = children && React.isValidElement(children) && children.type !== Option ?
React.Children.only(this.props.children) :
<Input/>;
return <InputElement className="ant-input">{element}</InputElement>;
}
render() {
let {
size, className = '', notFoundContent, prefixCls, optionLabelProp, dataSource, children,
@ -54,22 +92,32 @@ export default class AutoComplete extends React.Component<AutoCompleteProps, any
[`${prefixCls}-sm`]: size === 'small',
[className]: !!className,
[`${prefixCls}-show-search`]: true,
[`${prefixCls}-auto-complete`]: true,
});
const options = children || (dataSource ? dataSource.map((item) => {
switch (typeof item) {
case 'string':
return <Option key={item}>{item}</Option>;
case 'object':
return (
<Option key={(item as DataSourceItemObject).value}>
{(item as DataSourceItemObject).text}
</Option>
);
default:
throw new Error('AutoComplete[dataSource] only supports type `string[] | Object[]`.');
}
}) : []);
let options;
const childArray = React.Children.toArray(children);
if (childArray.length && (childArray[0] as React.ReactElement<any>).type === Option) {
options = childArray;
} else {
options = dataSource ? dataSource.map((item) => {
if (React.isValidElement(item)) {
return item;
}
switch (typeof item) {
case 'string':
return <Option key={item}>{item}</Option>;
case 'object':
return (
<Option key={(item as DataSourceItemObject).value}>
{(item as DataSourceItemObject).text}
</Option>
);
default:
throw new Error('AutoComplete[dataSource] only supports type `string[] | Object[]`.');
}
}) : [];
}
return (
<Select
@ -77,6 +125,7 @@ export default class AutoComplete extends React.Component<AutoCompleteProps, any
className={cls}
optionLabelProp={optionLabelProp}
combobox
getInputElement={this.getInputElement}
notFoundContent={notFoundContent}
>
{options}

View File

@ -30,3 +30,5 @@ const dataSource = ['12345', '23456', '34567'];
| onSelect | 被选中时调用,参数为选中项的 value 值 | function(value, option) | 无 |
| disabled | 是否禁用 | boolean | false |
| placeholder | 输入框提示 | string | - |
| children (自动完成的数据源) | 自动完成的数据源 | React.ReactElement<OptionProps> / Array<React.ReactElement<OptionProps>> | - |
| children (自定义输入框) | 自定义输入框 | HTMLInputElement / HTMLTextAreaElement / React.ReactElement<InputProps> | `<Input />` |

View File

@ -0,0 +1,41 @@
@import "../../style/themes/default";
@import "../../style/mixins/index";
@import "../../input/style/mixin";
@input-prefix-cls: ~"@{ant-prefix}-input";
@select-prefix-cls: ~"@{ant-prefix}-select";
@autocomplete-prefix-cls: ~"@{select-prefix-cls}-auto-complete";
.@{autocomplete-prefix-cls}.@{select-prefix-cls} {
.@{select-prefix-cls} {
&-search--inline {
position: relative;
}
&-selection {
border: 0;
&--single {
height: auto;
}
&__rendered {
margin-left: 0;
margin-right: 0;
line-height: 0;
}
&__placeholder {
margin-left: 8px;
margin-right: 8px;
top: 12px;
}
}
}
.@{input-prefix-cls} {
border: 1px solid @border-color-base;
&:focus,
&:hover {
.hover;
}
}
}

View File

@ -58,7 +58,7 @@ export default class CheckboxGroup extends React.Component<CheckboxGroupProps, C
getOptions() {
const { options } = this.props;
// https://github.com/Microsoft/TypeScript/issues/7960
return (options as Array<any>).map(option => {
return (options as Array<CheckboxOptionType>).map(option => {
if (typeof option === 'string') {
return {
label: option,

View File

@ -37,6 +37,7 @@ export interface SelectProps {
dropdownMenuStyle?: React.CSSProperties;
onChange?: (value: SelectValue) => void;
tokenSeparators?: string[];
getInputElement?: () => React.ReactElement<any>;
}
export interface OptionProps {

View File

@ -56,7 +56,7 @@
"rc-progress": "~2.0.1",
"rc-radio": "~2.0.0",
"rc-rate": "~1.1.2",
"rc-select": "~6.6.1",
"rc-select": "~6.7.1",
"rc-slider": "~5.4.0",
"rc-steps": "~2.2.0",
"rc-switch": "~1.4.2",