Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion examples/single.js
Original file line number Diff line number Diff line change
Expand Up @@ -72,7 +72,7 @@ class Test extends React.Component {
jack
</b>
</Option>
<Option value="11" text="lucy">lucy</Option>
<Option value={{ id: 11 }} text="lucy">lucy</Option>
<Option value="21" disabled text="disabled">disabled</Option>
<Option value="31" text="yiminghe">yiminghe</Option>
{[0, 1, 2, 3, 4, 5, 6, 7, 8, 9].map((i) => {
Expand Down
1 change: 1 addition & 0 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -69,6 +69,7 @@
"classnames": "2.x",
"component-classes": "1.x",
"dom-scroll-into-view": "1.x",
"lodash.isequal": "^4.5.0",
"prop-types": "^15.5.8",
"rc-animate": "2.x",
"rc-menu": "^5.0.11",
Expand Down
2 changes: 1 addition & 1 deletion src/Option.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@ import PropTypes from 'prop-types';

export default class Option extends React.Component {
static propTypes = {
value: PropTypes.string,
value: PropTypes.any,
};

static isSelectOption = true;
Expand Down
6 changes: 3 additions & 3 deletions src/PropTypes.js
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@ import PropTypes from 'prop-types';

function valueType(props, propName, componentName) {
const labelInValueShape = PropTypes.shape({
key: PropTypes.string.isRequired,
key: PropTypes.any.isRequired,
label: PropTypes.string,
});
if (props.labelInValue) {
Expand All @@ -25,8 +25,8 @@ function valueType(props, propName, componentName) {
);
} else {
const validate = PropTypes.oneOfType([
PropTypes.arrayOf(PropTypes.string),
PropTypes.string,
PropTypes.arrayOf(PropTypes.any),
PropTypes.any,
]);
return validate(...arguments);
}
Expand Down
9 changes: 7 additions & 2 deletions src/Select.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -22,11 +22,13 @@ import {
splitBySeparators,
findIndexInValueByLabel,
defaultFilterFn,
validateOptionValue,
} from './util';
import SelectTrigger from './SelectTrigger';
import { SelectPropTypes } from './PropTypes';
import { Item as MenuItem, ItemGroup as MenuItemGroup } from 'rc-menu';
import warning from 'warning';
import isEqual from 'lodash.isequal';

function noop() {}

Expand Down Expand Up @@ -424,7 +426,7 @@ export default class Select extends React.Component {
if (maybe !== null) {
label = maybe;
}
} else if (getValuePropValue(child) === value) {
} else if (isEqual(getValuePropValue(child), value)) {
label = this.getLabelFromOption(child);
}
});
Expand Down Expand Up @@ -895,6 +897,9 @@ export default class Select extends React.Component {
);

const childValue = getValuePropValue(child);

validateOptionValue(childValue, this.props);

if (this.filterOption(inputValue, child)) {
sel.push(
<MenuItem
Expand Down Expand Up @@ -1022,7 +1027,7 @@ export default class Select extends React.Component {
opacity,
}}
>
{value[0].label}
{String(value[0].label)}
</div>
);
}
Expand Down
16 changes: 16 additions & 0 deletions src/util.js
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,10 @@ export function getPropValue(child, prop) {
return child.props[prop];
}

export function isMultiple(props) {
return props.multiple;
}

export function isCombobox(props) {
return props.combobox;
}
Expand Down Expand Up @@ -146,3 +150,15 @@ export function defaultFilterFn(input, child) {
String(getPropValue(child, this.props.optionFilterProp)).indexOf(input) > -1
);
}

export function validateOptionValue(value, props) {
if (isSingleMode(props) || isMultiple(props)) {
return;
}
if (typeof value !== 'string') {
throw new Error(
`Invalid \`value\` of type \`${typeof value}\` supplied to Option, ` +
`expected \`string\` when \`tags/combobox\` is \`true\`.`
);
}
}
2 changes: 2 additions & 0 deletions tests/Select.combobox.spec.js
Original file line number Diff line number Diff line change
Expand Up @@ -4,11 +4,13 @@ import Select, { Option } from '../src';
import { mount, render } from 'enzyme';
import KeyCode from 'rc-util/lib/KeyCode';
import allowClearTest from './shared/allowClearTest';
import throwOptionValue from './shared/throwOptionValue';

const delay = timeout => new Promise(resolve => setTimeout(resolve, timeout));

describe('Select.combobox', () => {
allowClearTest('combobox');
throwOptionValue('combobox');

it('renders correctly', () => {
const wrapper = render(
Expand Down
16 changes: 16 additions & 0 deletions tests/Select.multiple.spec.js
Original file line number Diff line number Diff line change
Expand Up @@ -85,4 +85,20 @@ describe('Select.multiple', () => {
);
}).not.toThrow();
});

it('allow non-string value', () => {
const handleChange = jest.fn();

const wrapper = mount(
<Select multiple defaultValue={[1]} onChange={handleChange}>
<Option value={1}>1</Option>
<Option value={2}>2</Option>
</Select>
);

wrapper.find('.rc-select').simulate('click');
wrapper.find('MenuItem').at(1).simulate('click');

expect(handleChange).toBeCalledWith([1, 2]);
});
});
20 changes: 20 additions & 0 deletions tests/Select.spec.js
Original file line number Diff line number Diff line change
Expand Up @@ -707,4 +707,24 @@ describe('Select', () => {
expect(handleChange).toBeCalledWith('2');
expect(handleSelect).toBeCalledWith('2', expect.anything());
});

it('allow non-string value', () => {
const handleChange = jest.fn();

const wrapper = mount(
<Select defaultValue={1} onChange={handleChange}>
<Option value={1}>1</Option>
<Option value={true}>2</Option>
<Option value={{ value: 3 }}>3</Option>
</Select>
);

wrapper.find('.rc-select').simulate('click');
wrapper.find('MenuItem').at(1).simulate('click');
expect(handleChange).toBeCalledWith(true);

wrapper.find('.rc-select').simulate('click');
wrapper.find('MenuItem').at(2).simulate('click');
expect(handleChange).toBeCalledWith({ value: 3 });
});
});
2 changes: 2 additions & 0 deletions tests/Select.tags.spec.js
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ import allowClearTest from './shared/allowClearTest';
import blurTest from './shared/blurTest';
import renderTest from './shared/renderTest';
import removeSelectedTest from './shared/removeSelectedTest';
import throwOptionValue from './shared/throwOptionValue';

jest.unmock('react-dom');

Expand All @@ -15,6 +16,7 @@ describe('Select.tags', () => {
blurTest('tags');
renderTest('tags');
removeSelectedTest('tags');
throwOptionValue('tags');

it('allow user input tags', () => {
const wrapper = mount(
Expand Down
19 changes: 19 additions & 0 deletions tests/shared/throwOptionValue.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
import React from 'react';
import Select from '../../src/Select';
import Option from '../../src/Option';
import { mount } from 'enzyme';

export default function throwOptionValue(mode) {
it('warn option value type', () => {
const render = () => mount(
<Select {...{ [mode]: true }} open>
<Option value={1}>1</Option>
</Select>
);

expect(render).toThrow(
'Invalid `value` of type `number` supplied to Option, ' +
'expected `string` when `tags/combobox` is `true`.'
);
});
}