Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

#658 Allow Create functionality [WIP] #660

Closed
wants to merge 3 commits into from
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
5 changes: 2 additions & 3 deletions examples/src/app.js
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@ import CustomRender from './components/CustomRender';
import Multiselect from './components/Multiselect';
import NumericSelect from './components/NumericSelect';
import States from './components/States';
import AllowCreate from './components/AllowCreate';

ReactDOM.render(
<div>
Expand All @@ -19,9 +20,7 @@ ReactDOM.render(
<NumericSelect label="Numeric Values" />
<CustomRender label="Custom Render Methods"/>
<CustomComponents label="Custom Option and Value Components" />
{/*
<SelectedValuesField label="Option Creation (tags mode)" options={FLAVOURS} allowCreate hint="Enter a value that's NOT in the list, then hit return" />
*/}
<AllowCreate label="Option Creation (tags mode)" allowCreate />
</div>,
document.getElementById('example')
);
61 changes: 61 additions & 0 deletions examples/src/components/AllowCreate.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,61 @@
import React from 'react';
import Select from 'react-select';

const FLAVOURS = [
{ label: 'Chocolate', value: 'chocolate' },
{ label: 'Vanilla', value: 'vanilla' },
{ label: 'Strawberry', value: 'strawberry' },
{ label: 'Caramel', value: 'caramel' },
{ label: 'Cookies and Cream', value: 'cookiescream' },
{ label: 'Peppermint', value: 'peppermint' },
];

var AllowCreate = React.createClass({
displayName: 'AllowCreate',

propTypes: {
allowCreate: React.PropTypes.bool,
label: React.PropTypes.string,
},

getInitialState () {
return {
disabled: false,
crazy: false,
options: FLAVOURS,
value: [],
};
},

onLabelClick (data, event) {
console.log(data, event);
},

handleSelectChange (value){
this.setState({ value });
},

renderHint () {
return (
<div className="hint">Create options in tag mode</div>
);
},

render () {
return (
<div className="section">
<h3 className="section-heading">{this.props.label}</h3>
<Select
allowCreate={this.props.allowCreate}
value={this.state.value}
multi={true}
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Generally preferred that boolean props that are true don't have an explicit declaration. remove the ={true} e.g. https://github.com/JedWatson/react-select/blob/master/examples/src/components/NumericSelect.js#L66

placeholder="Select your favourite(s)"
options={this.state.options}
onChange={this.handleSelectChange} />
{this.renderHint()}
</div>
);
}
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

ES6 extra comma },

});

module.exports = AllowCreate;
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

New line at the end of the file please :)

4 changes: 2 additions & 2 deletions src/Option.js
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@ const Option = React.createClass({
onFocus: React.PropTypes.func, // method to handle mouseEnter on option element
onUnfocus: React.PropTypes.func, // method to handle mouseLeave on option element
option: React.PropTypes.object.isRequired, // object that is base for that option
addLabelText: React.PropTypes.string, // text to display with value while creating new option
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Minor fix, can you align the comment with the lines above.

},
blockEvent (event) {
event.preventDefault();
Expand Down Expand Up @@ -39,7 +40,6 @@ const Option = React.createClass({
render () {
var { option } = this.props;
var className = classNames(this.props.className, option.className);

return option.disabled ? (
<div className={className}
onMouseDown={this.blockEvent}
Expand All @@ -53,7 +53,7 @@ const Option = React.createClass({
onMouseEnter={this.handleMouseEnter}
onMouseMove={this.handleMouseMove}
title={option.title}>
{this.props.children}
{ option.create ? this.props.addLabelText.replace('{label}', option.label) : this.props.children }
</div>
);
}
Expand Down
17 changes: 15 additions & 2 deletions src/Select.js
Original file line number Diff line number Diff line change
Expand Up @@ -582,9 +582,19 @@ const Select = React.createClass({
},

renderMenu (options, valueArray, focusedOption) {
if (options && options.length) {
if (options && options.length || this.props.allowCreate) {
let Option = this.props.optionComponent;
let renderLabel = this.props.optionRenderer || this.getOptionLabel;
let inputValue = this.state.inputValue.trim();
if (this.props.allowCreate && inputValue) {
options = options.slice();
let newOption = this.props.newOptionCreator ? this.props.newOptionCreator(inputValue) : {
value: inputValue,
label: inputValue,
create: true
};
options.unshift(newOption);
}
return options.map((option, i) => {
let isSelected = valueArray && valueArray.indexOf(option) > -1;
let isFocused = option === focusedOption;
Expand All @@ -606,12 +616,15 @@ const Select = React.createClass({
option={option}
isSelected={isSelected}
ref={optionRef}
addLabelText={this.props.addLabelText}
>
{renderLabel(option)}
</Option>
);
});
} else {
} else if (this.props.allowCreate){

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

What needs to go in here?

} else{
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

One more space } else {

return (
<div className="Select-noresults">
{this.props.noResultsText}
Expand Down
2 changes: 1 addition & 1 deletion test/Async-test.js
Original file line number Diff line number Diff line change
Expand Up @@ -178,7 +178,7 @@ describe('Async', () => {
noResultsText="Searching..."
placeholder="Loading..."
/>);
})
});
});

describe('with a cache', () => {
Expand Down
26 changes: 6 additions & 20 deletions test/Select-test.js
Original file line number Diff line number Diff line change
Expand Up @@ -1047,7 +1047,7 @@ describe('Select', () => {
describe('with allowCreate=true', () => {

// TODO: allowCreate hasn't been implemented yet in 1.x
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

remove comment

return;
// return;
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I guess you can just remove the line completely, and the TODO comment as well :) when finished


beforeEach(() => {

Expand All @@ -1060,20 +1060,22 @@ describe('Select', () => {
{ value: 'three', label: 'Three' },
{ value: 'zzzzz', label: 'test value' }
];

setValueProp('xyz');

// Render an instance of the component
wrapper = createControlWithWrapper({
value: 'one',
options: options,
allowCreate: true,
searchable: true,
addLabelText: 'Add {label} to values?'
}, {
wireUpOnChangeToValue: true
});
});

it('has an "Add xyz" option when entering xyz', () => {
typeSearchText('xyz');

expect(ReactDOM.findDOMNode(instance), 'queried for', '.Select-menu .Select-option',
'to have items satisfying', 'to have text', 'Add xyz to values?');
});
Expand Down Expand Up @@ -1108,28 +1110,12 @@ describe('Select', () => {

it('displays an add option when a value with spaces is entered', () => {

typeSearchText('got');
typeSearchText(' got ');

expect(ReactDOM.findDOMNode(instance).querySelectorAll('.Select-menu .Select-option')[0],
'to have text', 'Add got to values?');
});

it('displays an add option when a value with spaces is entered', () => {

typeSearchText('got');

expect(ReactDOM.findDOMNode(instance).querySelectorAll('.Select-menu .Select-option')[0],
'to have text', 'Add got to values?');
});

it('displays an add option when a label with spaces is entered', () => {

typeSearchText('test');

expect(ReactDOM.findDOMNode(instance).querySelectorAll('.Select-menu .Select-option')[0],
'to have text', 'Add test to values?');
});

it('does not display the option label when an existing value is entered', () => {

typeSearchText('zzzzz');
Expand Down