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

Add the ability for categorization of react-select options #468

Closed
wants to merge 1 commit 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
2 changes: 1 addition & 1 deletion .eslintrc
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,7 @@
"quotes": [2, "single", "avoid-escape"],
"react/jsx-boolean-value": 1,
"react/jsx-no-undef": 1,
"react/jsx-quotes": 1,
"jsx-quotes": 1,
"react/jsx-sort-prop-types": 1,
"react/jsx-uses-react": 1,
"react/jsx-uses-vars": 1,
Expand Down
93 changes: 60 additions & 33 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -124,6 +124,32 @@ var getOptions = function(input, callback) {
/>
```

### Categorized Select

You can utilize categorized selections by setting `categoized={true}`. In this mode:

* You must pass in an object of arrays containing the options
* The keys of the object become to categories

```js
var Select = require('react-select');

var options = {
"Primary": [
{ value: "blue", label: "Blue" },
{ value: "green", label: "Green" },
{ value: "red", label: "Red" }
],
"Secondary": [
{ value: "yellow", label: "Yellow" },
{ value: "magenta", label: "Magenta" },
{ value: "cyan", label: "Cyan" }
]
};

<Select options={options} categorized={true} />
```

### Filtering options

You can control how options are filtered with the following properties:
Expand All @@ -147,39 +173,40 @@ For multi-select inputs, when providing a custom `filterOptions` method, remembe
### Further options


Property | Type | Description
:-----------------------|:--------------|:--------------------------------
addLabelText | string | text to display when allowCreate is true
allowCreate | bool | allow new options to be created in multi mode (displays an "Add \<option> ?" item when a value not already in the `options` array is entered)
asyncOptions | func | function to call to get options
autoload | bool | whether to auto-load the default async options set
backspaceRemoves | bool | whether pressing backspace removes the last item when there is no input value
className | string | className for the outer element
clearable | bool | should it be possible to reset value
clearAllText | string | title for the "clear" control when multi: true
clearValueText | string | title for the "clear" control
delimiter | string | delimiter to use to join multiple values
disableCache | bool | disables the options cache for asyncOptions
disabled | bool | whether the Select is disabled or not
filterOption | func | method to filter a single option: function(option, filterString)
filterOptions | func | method to filter the options array: function([options], filterString, [values])
ignoreCase | bool | whether to perform case-insensitive filtering
inputProps | object | custom attributes for the Input (in the Select-control) e.g: {'data-foo': 'bar'}
matchPos | string | (any, start) match the start or entire string when filtering
matchProp | string | (any, label, value) which option property to filter on
multi | bool | multi-value input
name | string | field name, for hidden <input /> tag
noResultsText | string | placeholder displayed when there are no matching search results
onBlur | func | onBlur handler: function(event) {}
onChange | func | onChange handler: function(newValue) {}
onFocus | func | onFocus handler: function(event) {}
optionRenderer | func | function which returns a custom way to render the options in the menu
options | array | array of options
placeholder | string | field placeholder, displayed when there's no value
searchable | bool | whether to enable searching feature or not
searchPromptText | string | label to prompt for search input
value | any | initial field value
valueRenderer | func | function which returns a custom way to render the value selected
Property | Type | Description
:-----------------------|:------------------|:--------------------------------
addLabelText | string | text to display when allowCreate is true
allowCreate | bool | allow new options to be created in multi mode (displays an "Add \<option> ?" item when a value not already in the `options` array is entered)
asyncOptions | func | function to call to get options
autoload | bool | whether to auto-load the default async options set
backspaceRemoves | bool | whether pressing backspace removes the last item when there is no input value
categorized | bool | whether the options being past in are categorized
className | string | className for the outer element
clearable | bool | should it be possible to reset value
clearAllText | string | title for the "clear" control when multi: true
clearValueText | string | title for the "clear" control
delimiter | string | delimiter to use to join multiple values
disableCache | bool | disables the options cache for asyncOptions
disabled | bool | whether the Select is disabled or not
filterOption | func | method to filter a single option: function(option, filterString)
filterOptions | func | method to filter the options array: function([options], filterString, [values])
ignoreCase | bool | whether to perform case-insensitive filtering
inputProps | object | custom attributes for the Input (in the Select-control) e.g: {'data-foo': 'bar'}
matchPos | string | (any, start) match the start or entire string when filtering
matchProp | string | (any, label, value) which option property to filter on
multi | bool | multi-value input
name | string | field name, for hidden <input /> tag
noResultsText | string | placeholder displayed when there are no matching search results
onBlur | func | onBlur handler: function(event) {}
onChange | func | onChange handler: function(newValue) {}
onFocus | func | onFocus handler: function(event) {}
optionRenderer | func | function which returns a custom way to render the options in the menu
options | array or object | array of options
placeholder | string | field placeholder, displayed when there's no value
searchable | bool | whether to enable searching feature or not
searchPromptText | string | label to prompt for search input
value | any | initial field value
valueRenderer | func | function which returns a custom way to render the value selected

### Methods

Expand Down
4 changes: 2 additions & 2 deletions examples/src/components/CustomRenderField.js
Original file line number Diff line number Diff line change
Expand Up @@ -31,7 +31,7 @@ var CustomRenderField = React.createClass({
<Select
delimiter={this.props.delimiter}
multi={this.props.multi}
allowCreate={true}
allowCreate
placeholder="Select your favourite"
options={ops}
optionRenderer={this.renderOption}
Expand All @@ -42,4 +42,4 @@ var CustomRenderField = React.createClass({
}
});

module.exports = CustomRenderField;
module.exports = CustomRenderField;
30 changes: 27 additions & 3 deletions examples/src/components/MultiSelectField.js
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@ var MultiSelectField = React.createClass({
getInitialState () {
return {
disabled: false,
categorized: false,
value: []
};
},
Expand All @@ -23,6 +24,9 @@ var MultiSelectField = React.createClass({
toggleDisabled (e) {
this.setState({ 'disabled': e.target.checked });
},
toggleCategorized (e) {
this.setState({ 'categorized': e.target.checked });
},
render () {
var ops = [
{ label: 'Chocolate', value: 'chocolate' },
Expand All @@ -32,20 +36,40 @@ var MultiSelectField = React.createClass({
{ label: 'Cookies and Cream', value: 'cookiescream' },
{ label: 'Peppermint', value: 'peppermint' }
];

if (this.state.categorized) {
ops = {
basic: [
{ label: 'Chocolate', value: 'chocolate' },
{ label: 'Vanilla', value: 'vanilla' },
{ label: 'Strawberry', value: 'strawberry' }
],
specialty: [
{ label: 'Caramel', value: 'caramel' },
{ label: 'Cookies and Cream', value: 'cookiescream' },
{ label: 'Peppermint', value: 'peppermint' }
]
};
}

return (
<div className="section">
<h3 className="section-heading">{this.props.label}</h3>
<Select multi={true} disabled={this.state.disabled} value={this.state.value} placeholder="Select your favourite(s)" options={ops} onChange={this.handleSelectChange} />
<Select multi categorized={this.state.categorized} disabled={this.state.disabled} value={this.state.value} placeholder="Select your favourite(s)" options={ops} onChange={this.handleSelectChange} />

<div className="checkbox-list">
<label className="checkbox">
<input type="checkbox" className="checkbox-control" checked={this.state.disabled} onChange={this.toggleDisabled} />
<span className="checkbox-label">Disabled</span>
</label>
<label className="checkbox">
<input type="checkbox" className="checkbonx-control" checked={this.state.categorized} onChange={this.toggleCategorized} />
<span className="checkbox-label">Categorized</span>
</label>
</div>
</div>
);
}
});

module.exports = MultiSelectField;
module.exports = MultiSelectField;
4 changes: 2 additions & 2 deletions examples/src/components/SelectedValuesField.js
Original file line number Diff line number Diff line change
Expand Up @@ -30,7 +30,7 @@ var SelectedValuesField = React.createClass({
allowCreate={this.props.allowCreate}
onOptionLabelClick={this.onLabelClick}
value={this.props.options.slice(1,3)}
multi={true}
multi
placeholder="Select your favourite(s)"
options={this.props.options}
onChange={logChange} />
Expand All @@ -40,4 +40,4 @@ var SelectedValuesField = React.createClass({
}
});

module.exports = SelectedValuesField;
module.exports = SelectedValuesField;
8 changes: 6 additions & 2 deletions examples/src/components/StatesField.js
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,7 @@ var StatesField = React.createClass({
},
getInitialState () {
return {
country: 'AU',
country: 'Both',
disabled: false,
searchable: this.props.searchable,
id: ++id,
Expand Down Expand Up @@ -56,7 +56,7 @@ var StatesField = React.createClass({
return (
<div className="section">
<h3 className="section-heading">{this.props.label}</h3>
<Select ref="stateSelect" options={ops} disabled={this.state.disabled} value={this.state.selectValue} onChange={this.updateValue} searchable={this.state.searchable} />
<Select ref="stateSelect" categorized={this.state.country === 'Both'} options={ops} disabled={this.state.disabled} value={this.state.selectValue} onChange={this.updateValue} searchable={this.state.searchable} />

<div style={{ marginTop: 14 }}>
<button type="button" onClick={this.focusStateSelect}>Focus Select</button>
Expand All @@ -78,6 +78,10 @@ var StatesField = React.createClass({
<input type="radio" className="checkbox-control" checked={this.state.country === 'US'} value="US" onChange={this.switchCountry}/>
<span className="checkbox-label">United States</span>
</label>
<label className="checkbox">
<input type="radio" className="checkbox-control" checked={this.state.country === 'Both'} value="Both" onChange={this.switchCountry}/>
<span className="checkbox-label">Both</span>
</label>
</div>
</div>
);
Expand Down
49 changes: 36 additions & 13 deletions examples/src/components/ValuesAsNumbersField.js
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@ var ValuesAsNumbersField = React.createClass({
propTypes: {
label: React.PropTypes.string
},

getInitialState () {
return {
options: [
Expand All @@ -20,14 +20,26 @@ var ValuesAsNumbersField = React.createClass({
{ value: 23, label: 'Twenty-three' },
{ value: 24, label: 'Twenty-four' }
],
categorizedOptions: {
'Category One': [
{ value: 10, label: 'Ten' },
{ value: 11, label: 'Eleven' }
],
'Category Two': [
{ value: 12, label: 'Twelve' },
{ value: 23, label: 'Twenty-three' },
{ value: 24, label: 'Twenty-four' }
]
},
matchPos: 'any',
matchValue: true,
matchLabel: true,
value: null,
multi: false
multi: false,
categorized: false
};
},

onChangeMatchStart(event) {
this.setState({
matchPos: event.target.checked ? 'start' : 'any'
Expand All @@ -45,43 +57,50 @@ var ValuesAsNumbersField = React.createClass({
matchLabel: event.target.checked
});
},


onChangeCategorized(event) {
this.setState({
categorized: event.target.checked
});
},

onChange(value, values) {
this.setState({
value: value
});
logChange(value, values);
},

onChangeMulti(event) {
this.setState({
multi: event.target.checked
});
},

render () {

var matchProp = 'any';

if (this.state.matchLabel && !this.state.matchValue) {
matchProp = 'label';
}

if (!this.state.matchLabel && this.state.matchValue) {
matchProp = 'value';
}

return (
<div className="section">
<h3 className="section-heading">{this.props.label}</h3>
<Select
searchable={true}
searchable
matchProp={matchProp}
matchPos={this.state.matchPos}
options={this.state.options}
options={this.state.categorized ? this.state.categorizedOptions : this.state.options}
onChange={this.onChange}
value={this.state.value}
multi={this.state.multi}
categorized={this.state.categorized}
/>
<div className="checkbox-list">
<label className="checkbox">
Expand All @@ -100,10 +119,14 @@ var ValuesAsNumbersField = React.createClass({
<input type="checkbox" className="checkbox-control" checked={this.state.matchPos === 'start'} onChange={this.onChangeMatchStart} />
<span className="checkbox-label">Only include matches from the start of the string</span>
</label>
<label className="checkbox">
<input type="checkbox" className="checkbox-control" checked={this.state.categorized} onChange={this.onChangeCategorized} />
<span className="checkbox-label">Categorized options</span>
</label>
</div>
</div>
);
}
});

module.exports = ValuesAsNumbersField;
module.exports = ValuesAsNumbersField;
5 changes: 5 additions & 0 deletions examples/src/data/states.js
Original file line number Diff line number Diff line change
Expand Up @@ -70,3 +70,8 @@ exports.US = [
{ value: 'WI', label: 'Wisconsin' },
{ value: 'WY', label: 'Wyoming' }
];

exports.Both = {
Australia: exports.AU,
'United States': exports.US
};
Loading