mirror of
https://github.com/ant-design/ant-design.git
synced 2024-11-27 20:49:53 +08:00
Customize Input Component of AutoComplete (#4483)
* cherry-pick * remove useless files * remove useless files * Group ts * update interface * ReactElement
This commit is contained in:
parent
47fcd7651f
commit
46c200896f
@ -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" />
|
||||
|
20
components/auto-complete/__tests__/ac.test.js
Normal file
20
components/auto-complete/__tests__/ac.test.js
Normal 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');
|
||||
});
|
||||
});
|
58
components/auto-complete/demo/antd.md
Normal file
58
components/auto-complete/demo/antd.md
Normal 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);
|
||||
````
|
@ -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 />` |
|
@ -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}
|
||||
|
@ -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 />` |
|
@ -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;
|
||||
}
|
||||
|
||||
}
|
||||
}
|
@ -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,
|
||||
|
@ -37,6 +37,7 @@ export interface SelectProps {
|
||||
dropdownMenuStyle?: React.CSSProperties;
|
||||
onChange?: (value: SelectValue) => void;
|
||||
tokenSeparators?: string[];
|
||||
getInputElement?: () => React.ReactElement<any>;
|
||||
}
|
||||
|
||||
export interface OptionProps {
|
||||
|
@ -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",
|
||||
|
Loading…
Reference in New Issue
Block a user