Skip to content

Commit

Permalink
[added] disable individual dropdown/combobox options
Browse files Browse the repository at this point in the history
  • Loading branch information
jquense committed Nov 2, 2015
1 parent 5d1b530 commit 1058e3f
Show file tree
Hide file tree
Showing 9 changed files with 146 additions and 104 deletions.
30 changes: 21 additions & 9 deletions docs/components/pages/ComboBox.api.md
Original file line number Diff line number Diff line change
Expand Up @@ -21,53 +21,65 @@ the string value of the {widgetName} will be returned.

### onSelect?{ type: 'Function(Any value)' }

This handler fires when an item has been selected from the list. It fires before the `onChange` handler, and fires
This handler fires when an item has been selected from the list. It fires before the `onChange` handler, and fires
regardless of whether the value has actually changed.

<EditableExample codeText={require('../examples/onSelect')(widgetName)}/>

### data?{ type: 'Array<Any>' }

An array of possible values for the {widgetName}. If an array of `objects` is provided you
should usethe `valueField` and `textField` props, to specify which object
should use the `valueField` and `textField` props, to specify which object
properties comprise the value field (such as an id) and the field used to label the item.

### valueField?{ type: 'String' }

A dataItem field name for uniquely identifying items in the `data` list. A `valueField` is required
A dataItem field name for uniquely identifying items in the `data` list. A `valueField` is required
when the `value` prop is not itself a dataItem. A `valueField` is useful when specifying the selected item, by
its `id` instead of using the model as the value.


When a `valueField` is not provided, the {widgetName} will use strict equality checks (`===`) to locate
When a `valueField` is not provided, the {widgetName} will use strict equality checks (`===`) to locate
the `value` in the `data` list.

<EditableExample codeText={require('../examples/valueField')(widgetName)}/>

### textField?{ type: 'String | Function(dataItem)' }

Specify which data item field to display in the ${widgetName} and selected item. The textField` prop
Specify which data item field to display in the ${widgetName} and selected item. The textField` prop
may also also used as to find an item in the list as you type. Providing an accessor function allows for computed text values

<EditableExample codeText={require('../examples/textField')(widgetName, false, true)}/>

### itemComponent?{ type: 'Component' }

This component is used to render each possible item in the DropdownList. The default component
This component is used to render each possible item in the ${widgetName}. The default component
renders the text of the selected item (specified by `textfield`)

<EditableExample codeText={require('../examples/itemComponent')(widgetName)}/>

### disabled?{ type: '[Boolean, Array]' }

Disable the widget, if an `Array` of values is passed in only those values will be disabled.

<EditableExample codeText={require('../examples/disabled')(widgetName, 'disabled', false)}/>

### readOnly?{ type: '[Boolean, Array]' }

Place the {widgetName} in a read-only mode, If an `Array` of values is passed in only those values will be read-only.

<EditableExample codeText={require('../examples/disabled')(widgetName, 'readOnly', false)}/>

### groupBy?{ type: 'String | Function(Any dataItem)' }

Determines how to group the {widgetName} dropdown list. Providing a `string` will group
Determines how to group the {widgetName}. Providing a `string` will group
the `data` array by that property. You can also provide a function which should return the group value.

<EditableExample codeText={require('../examples/groupby')(widgetName)}/>

### groupComponent?{ type: 'Component' }

This component is used to render each option group, when `groupBy` is specified. By
This component is used to render each option group, when `groupBy` is specified. By
default the `groupBy` value will be used.

<EditableExample codeText={require('../examples/groupComponent')(widgetName)}/>
Expand All @@ -80,7 +92,7 @@ are always "startsWith", meaning it will search from the start of the `textField
### filter?{ type: '[Boolean, String, Function(dataItem, searchTerm)]', default: 'false' }

Specify a filtering method used to reduce the items in the dropdown as you type. It can be used in conjunction with
the `suggest` prop or instead of it. There are a few prebuilt filtering methods that can be specified
the `suggest` prop or instead of it. There are a few built-in filtering methods that can be specified
by passing the `String` name. You can explicitly opt out of filtering by setting filter
to `false`

Expand Down
36 changes: 24 additions & 12 deletions docs/components/pages/DropdownList.api.md
Original file line number Diff line number Diff line change
Expand Up @@ -12,59 +12,71 @@ Change event Handler that is called when the value is changed.

### onSelect?{ type: 'Function(Any value)' }

This handler fires when an item has been selected from the list. It fires before the `onChange` handler, and fires
This handler fires when an item has been selected from the list. It fires before the `onChange` handler, and fires
regardless of whether the value has actually changed.

<EditableExample codeText={require('../examples/onSelect')(widgetName)}/>

### data?{ type: 'Array<Any>' }

provide an array of possible values for the DropdownList. If an array of `objects` is provided you
provide an array of possible values for the ${widgetName}. If an array of `objects` is provided you
should use the `valueField` and `textField` props, to specify which object
properties comprise the value field (such as an id) and the field used to label the item.

### valueField?{ type: 'String' }

A dataItem field name for uniquely identifying items in the `data` list. A `valueField` is required
A dataItem field name for uniquely identifying items in the `data` list. A `valueField` is required
when the `value` prop is not itself a dataItem. A `valueField` is useful when specifying the selected item, by
its `id` instead of using the model as the value.

When a `valueField` is not provided, the {widgetName} will use strict equality checks (`===`) to locate
When a `valueField` is not provided, the {widgetName} will use strict equality checks (`===`) to locate
the `value` in the `data` list.

<EditableExample codeText={require('../examples/valueField')(widgetName)}/>

### textField?{ type: 'String | Function(dataItem)' }

{`Specify which data item field to display in the ${widgetName} and selected item. The `}`textField`{`prop
{`Specify which data item field to display in the ${widgetName} and selected item. The `}`textField`{`prop
may also also used as to find an item in the list as you type. Providing an accessor function allows for computed text values`}

<EditableExample codeText={require('../examples/textField')(widgetName)}/>

### valueComponent?{ type: 'Component' }

This component is used to render the selected value of the combobox. The default component
This component is used to render the selected value of the ${widgetName}. The default component
renders the text of the selected item (specified by `textfield`)

<EditableExample codeText={require('../examples/valueComponent')(widgetName)}/>

### itemComponent?{ type: 'Component' }

This component is used to render each possible item in the DropdownList. The default component
This component is used to render each possible item in the ${widgetName}. The default component
renders the text of the selected item (specified by `textfield`)

<EditableExample codeText={require('../examples/itemComponent')(widgetName)}/>

### disabled?{ type: '[Boolean, Array]' }

Disable the widget, if an `Array` of values is passed in only those values will be disabled.

<EditableExample codeText={require('../examples/disabled')(widgetName, 'disabled', false)}/>

### readOnly?{ type: '[Boolean, Array]' }

Place the {widgetName} in a read-only mode, If an `Array` of values is passed in only those values will be read-only.

<EditableExample codeText={require('../examples/disabled')(widgetName, 'readOnly', false)}/>

### groupBy?{ type: 'String | Function(Any dataItem)' }

Determines how to group the {widgetName} dropdown list. Providing a `string` will group
Determines how to group the {widgetName}. Providing a `string` will group
the `data` array by that property. You can also provide a function which should return the group value.

<EditableExample codeText={require('../examples/groupby')(widgetName)}/>

### groupComponent?{ type: 'Component' }

This component is used to render each option group, when `groupBy` is specified. By
This component is used to render each option group, when `groupBy` is specified. By
default the `groupBy` value will be used.


Expand All @@ -79,7 +91,7 @@ Text to display when the value is empty.

The string value of the current search being typed into the {widgetName}. When
unset (`undefined`) the {widgetName} will handle the filtering internally.
The `defaultSearchTerm` prop can be used to set an initialization value for uncontrolled widgets. searchTerm is only
The `defaultSearchTerm` prop can be used to set an initialization value for uncontrolled widgets. `searchTerm` is only
relevant when the `filter` prop is set.


Expand All @@ -105,7 +117,7 @@ when the `open` prop is set otherwise the widget open buttons won't work.

### filter?{ type: '[String, Function(dataItem, searchTerm)]', default: 'false' }

Specify a filtering method used to reduce the items in the dropdown as you type. There are a few prebuilt filtering
Specify a filtering method used to reduce the items in the dropdown as you type. There are a few built-in filtering
methods that can be specified by passing the `String` name.

To handle custom filtering techniques provide a `function` that returns `true` or `false` for each passed in item
Expand Down Expand Up @@ -168,4 +180,4 @@ Text to display when the the current filter does not return any results.
- <kbd>home</kbd> move focus to first item
- <kbd>end</kbd> move focus to last item
- <kbd>enter</kbd> select focused item
- <kbd>any key</kbd> search list for item starting with key
- <kbd>any key</kbd> search list for item starting with key
10 changes: 6 additions & 4 deletions src/Combobox.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@ import GroupableList from './ListGroupable';
import validateList from './util/validateListInterface';
import createUncontrolledWidget from 'uncontrollable';
import { dataItem, dataText, dataIndexOf } from './util/dataHelpers';
import { widgetEditable, widgetEnabled } from './util/interaction';
import { widgetEditable, widgetEnabled, isDisabled, isReadOnly } from './util/interaction';
import { instanceId, notify, isFirstFocusedRender } from './util/widgetHelpers';

let defaultSuggest = f => f === true ? 'startsWith' : f ? f : 'eq'
Expand Down Expand Up @@ -41,8 +41,8 @@ let propTypes = {
onSelect: React.PropTypes.func,

autoFocus: React.PropTypes.bool,
disabled: CustomPropTypes.disabled,
readOnly: CustomPropTypes.readOnly,
disabled: CustomPropTypes.disabled.acceptsArray,
readOnly: CustomPropTypes.readOnly.acceptsArray,

suggest: CustomPropTypes.filter,
filter: CustomPropTypes.filter,
Expand Down Expand Up @@ -144,7 +144,7 @@ var ComboBox = React.createClass({
className, tabIndex, filter, suggest
, valueField, textField, groupBy
, messages, data, busy, dropUp, name, autoFocus
, placeholder, value, open, disabled, readOnly
, placeholder, value, open
, listComponent: List } = this.props;

List = List || (groupBy && GroupableList) || PlainList
Expand All @@ -156,6 +156,8 @@ var ComboBox = React.createClass({
let { focusedItem, selectedItem, focused } = this.state;

let items = this._data()
, disabled = isDisabled(this.props)
, readOnly = isReadOnly(this.props)
, valueItem = dataItem(data, value, valueField) // take value from the raw data
, inputID = instanceId(this, '_input')
, listID = instanceId(this, '_listbox')
Expand Down
11 changes: 6 additions & 5 deletions src/DropdownList.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@ import validateList from './util/validateListInterface';
import createUncontrolledWidget from 'uncontrollable';

import { dataItem, dataText, dataIndexOf } from './util/dataHelpers';
import { widgetEditable, widgetEnabled } from './util/interaction';
import { widgetEditable, widgetEnabled, isDisabled, isReadOnly } from './util/interaction';
import { instanceId, notify, isFirstFocusedRender } from './util/widgetHelpers';

let { omit, pick, result } = _;
Expand Down Expand Up @@ -48,9 +48,8 @@ var propTypes = {
dropUp: React.PropTypes.bool,
duration: React.PropTypes.number, //popup

disabled: CustomPropTypes.disabled,

readOnly: CustomPropTypes.readOnly,
disabled: CustomPropTypes.disabled.acceptsArray,
readOnly: CustomPropTypes.readOnly.acceptsArray,

messages: React.PropTypes.shape({
open: CustomPropTypes.message,
Expand Down Expand Up @@ -123,7 +122,7 @@ var DropdownList = React.createClass({
className, tabIndex, filter
, valueField, textField, groupBy
, messages, data, busy, dropUp
, placeholder, value, open, disabled, readOnly
, placeholder, value, open
, valueComponent: ValueComponent
, listComponent: List } = this.props;

Expand All @@ -136,6 +135,8 @@ var DropdownList = React.createClass({
let { focusedItem, selectedItem, focused } = this.state;

let items = this._data()
, disabled = isDisabled(this.props)
, readOnly = isReadOnly(this.props)
, valueItem = dataItem(data, value, valueField) // take value from the raw data
, listID = instanceId(this, '__listbox');

Expand Down
22 changes: 14 additions & 8 deletions src/List.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ import cn from 'classnames';
import _ from './util/_';
import { dataText, dataValue } from './util/dataHelpers';
import { instanceId, notify } from './util/widgetHelpers';
import { isDisabledItem, isReadOnlyItem } from './util/interaction';

let optionId = (id, idx)=> `${id}__option__${idx}`;

Expand All @@ -26,12 +27,13 @@ export default React.createClass({
optionComponent: CustomPropTypes.elementType,
itemComponent: CustomPropTypes.elementType,

selectedIndex: React.PropTypes.number,
focusedIndex: React.PropTypes.number,
valueField: React.PropTypes.string,
selected: React.PropTypes.any,
focused: React.PropTypes.any,
valueField: CustomPropTypes.accessor,
textField: CustomPropTypes.accessor,

optionID: React.PropTypes.func,
disabled: CustomPropTypes.disabled.acceptsArray,
readOnly: CustomPropTypes.readOnly.acceptsArray,

messages: React.PropTypes.shape({
emptyList: CustomPropTypes.message
Expand All @@ -41,7 +43,6 @@ export default React.createClass({

getDefaultProps(){
return {
optID: '',
onSelect: ()=>{},
optionComponent: ListOption,
ariaActiveDescendantKey: 'list',
Expand Down Expand Up @@ -72,7 +73,6 @@ export default React.createClass({
, focused, selected, messages, onSelect
, itemComponent: ItemComponent
, optionComponent: Option
, optionID
, ...props } = this.props
, id = instanceId(this)
, items;
Expand All @@ -83,22 +83,28 @@ export default React.createClass({
{_.result(messages.emptyList, this.props)}
</li>
) : data.map((item, idx) => {
var currentId = optionId(id, idx);
var currentId = optionId(id, idx)
, isDisabled = isDisabledItem(item, props)
, isReadOnly = isReadOnlyItem(item, props);

return (
<Option
key={'item_' + idx}
id={currentId}
dataItem={item}
disabled={isDisabled}
readOnly={isReadOnly}
focused={focused === item}
selected={selected === item}
onClick={onSelect.bind(null, item)}
onClick={isDisabled || isReadOnly ? undefined : onSelect.bind(null, item)}
>
{ ItemComponent
? <ItemComponent
item={item}
value={dataValue(item, valueField)}
text={dataText(item, textField)}
disabled={isDisabled}
readOnly={isReadOnly}
/>
: dataText(item, textField)
}
Expand Down
Loading

0 comments on commit 1058e3f

Please sign in to comment.