Skip to content

Commit

Permalink
feat(Dropdown): Disable dropdown item (Semantic-Org#478)
Browse files Browse the repository at this point in the history
  • Loading branch information
jeffcarbs committed Sep 9, 2016
1 parent 9868333 commit 4b2ea2b
Show file tree
Hide file tree
Showing 5 changed files with 80 additions and 2 deletions.
15 changes: 15 additions & 0 deletions docs/app/Examples/modules/Dropdown/States/DisabledItem.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
import _ from 'lodash'
import faker from 'faker'
import React, { Component } from 'react'
import { Dropdown } from 'stardust'

const options = _.times(10, (i) => {
const name = faker.name.findName()
return { text: name, value: _.snakeCase(name), disabled: i % 3 == 0 }
})

const DropdownItemDisabledExample = () => (
<Dropdown text='Dropdown' options={options} />
)

export default DropdownItemDisabledExample
7 changes: 6 additions & 1 deletion docs/app/Examples/modules/Dropdown/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -43,9 +43,14 @@ const DropdownExamples = () => (
<ExampleSection title='States'>
<ComponentExample
title='Disabled'
description='A disabled dropdown menu or item does not allow user interaction'
description='A disabled dropdown menu does not allow user interaction'
examplePath='modules/Dropdown/States/Disabled'
/>
<ComponentExample
title='Disabled Item'
description='A disabled dropdown item does not allow user interaction'
examplePath='modules/Dropdown/States/DisabledItem'
/>
</ExampleSection>
</div>
)
Expand Down
9 changes: 8 additions & 1 deletion src/modules/Dropdown/Dropdown.js
Original file line number Diff line number Diff line change
Expand Up @@ -386,9 +386,10 @@ export default class Dropdown extends Component {
selectHighlightedItem = (e) => {
const { multiple, onAddItem, options } = this.props
const value = _.get(this.getSelectedItem(), 'value')
const disabled = _.get(this.getSelectedItem(), 'disabled')

// prevent selecting null if there was no selected item value
if (!value) return
if (!value || disabled) return

// notify the onAddItem prop if this is a new value
if (onAddItem && !_.some(options, { text: value })) onAddItem(value)
Expand Down Expand Up @@ -631,13 +632,19 @@ export default class Dropdown extends Component {
const options = this.getMenuOptions()
const lastIndex = options.length - 1

// Prevent infinite loop
if (_.every(options, 'disabled')) return

// next is after last, wrap to beginning
// next is before first, wrap to end
let nextIndex = selectedIndex + offset
if (nextIndex > lastIndex) nextIndex = 0
else if (nextIndex < 0) nextIndex = lastIndex

this.setState({ selectedIndex: nextIndex })

if (options[nextIndex].disabled) return this.moveSelectionBy(offset)

this.scrollSelectedItemIntoView()
}

Expand Down
11 changes: 11 additions & 0 deletions src/modules/Dropdown/DropdownItem.js
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@ function DropdownItem(props) {
active,
children,
className,
disabled,
description,
icon,
onClick,
Expand All @@ -25,11 +26,18 @@ function DropdownItem(props) {
} = props

const handleClick = (e) => {
if (props.disabled) {
e.stopPropagation()
e.nativeEvent.stopImmediatePropagation()
return
}

if (onClick) onClick(e, value)
}

const classes = cx(
useKeyOnly(active, 'active'),
useKeyOnly(disabled, 'disabled'),
useKeyOnly(selected, 'selected'),
'item',
className,
Expand Down Expand Up @@ -78,6 +86,9 @@ DropdownItem.propTypes = {
/** Additional text with less emphasis. */
description: PropTypes.string,

/** A dropdown item can be disabled. */
disabled: PropTypes.bool,

/** Add an icon to the item. */
icon: PropTypes.string,

Expand Down
40 changes: 40 additions & 0 deletions test/specs/modules/Dropdown/Dropdown-test.js
Original file line number Diff line number Diff line change
Expand Up @@ -193,6 +193,29 @@ describe('Dropdown Component', () => {
.find('.selected')
.should.contain.text('a2')
})
it('skips over disabled items', () => {
const opts = [
{ text: 'a1', value: 'a1' },
{ text: 'skip this one', value: 'skip this one', disabled: true },
{ text: 'a2', value: 'a2' },
]
// search for 'a'
wrapperMount(<Dropdown options={opts} search selection />)
.simulate('click')
.find('input.search')
.simulate('change', { target: { value: 'a' } })

wrapper
.find('.selected')
.should.contain.text('a1')

// move selection down
domEvent.keyDown(document, { key: 'ArrowDown' })

wrapper
.find('.selected')
.should.contain.text('a2')
})
it('scrolls the selected item into view', () => {
// get enough options to make the menu scrollable
const opts = getOptions(20)
Expand Down Expand Up @@ -260,6 +283,23 @@ describe('Dropdown Component', () => {

item.should.have.prop('active', true)
})
it('does not become active on enter when disabled', () => {
const disabledOptions = _.map(options, (o) => ({ ...o, disabled: true }))
const item = wrapperMount(<Dropdown options={disabledOptions} selection />)
.simulate('click')
.find('DropdownItem')
.at(0)

// initial item props
item.should.have.prop('selected', true)
item.should.have.prop('active', false)

// attempt to make active
domEvent.keyDown(document, { key: 'Enter' })

item.should.have.prop('active', false)
dropdownMenuIsOpen()
})
it('closes the menu', () => {
wrapperMount(<Dropdown options={options} selection />)
.simulate('click')
Expand Down

0 comments on commit 4b2ea2b

Please sign in to comment.