Skip to content

Commit

Permalink
add async dropdown options example
Browse files Browse the repository at this point in the history
  • Loading branch information
levithomason committed Mar 10, 2016
1 parent f1db707 commit cc91c86
Show file tree
Hide file tree
Showing 4 changed files with 138 additions and 105 deletions.
39 changes: 39 additions & 0 deletions docs/app/Examples/modules/Dropdown/Content/AsyncOptions.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,39 @@
import _ from 'lodash'
import React, { Component } from 'react'
import { Button, Dropdown } from 'stardust'
import faker from 'faker'

export default class DropdownAsyncOptions extends Component {
state = { isFetching: false }

updateItems = () => {
this.setState({ isFetching: true })

// fake api call
setTimeout(() => {
this.setState({
isFetching: false,
options: _.times(50, () => {
const name = faker.name.findName()
return { text: name, value: name }
}),
})
}, 1000)
}

render() {
const { options, isFetching } = this.state
return (
<div>
<Button onClick={this.updateItems}>
Fetch Items
</Button>
<Dropdown
className={`search selection multiple ${isFetching && 'disabled loading'}`}
defaultText='Dropdown'
options={options}
/>
</div>
)
}
}
17 changes: 17 additions & 0 deletions docs/app/Examples/modules/Dropdown/Content/Content.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
import React, { Component } from 'react'
import ComponentExample from 'docs/app/Components/ComponentDoc/ComponentExample'
import ExampleSection from 'docs/app/Components/ComponentDoc/ExampleSection'

export default class DropdownContentExamples extends Component {
render() {
return (
<ExampleSection title='Content'>
<ComponentExample
title='Async Options'
description="A dropdown's options can change over time"
examplePath='modules/Dropdown/Content/AsyncOptions'
/>
</ExampleSection>
)
}
}
2 changes: 2 additions & 0 deletions docs/app/Examples/modules/Dropdown/DropdownExamples.js
Original file line number Diff line number Diff line change
@@ -1,12 +1,14 @@
import React, { Component } from 'react'
import Types from './Types/Types'
import Content from './Content/Content'
import States from './States/States'

export default class DropdownExamples extends Component {
render() {
return (
<div>
<Types />
<Content />
<States />
</div>
)
Expand Down
185 changes: 80 additions & 105 deletions src/modules/Dropdown/Dropdown.js
Original file line number Diff line number Diff line change
Expand Up @@ -3,121 +3,95 @@ import $ from 'jquery'
import cx from 'classnames'
import React, { Component, PropTypes } from 'react'
import META from '../../utils/Meta'
import getUnhandledProps from '../../utils/getUnhandledProps'
import { getPluginProps, getComponentProps } from '../../utils/propUtils'
import { customPropTypes } from '../../utils/propUtils'

import DropdownDivider from './DropdownDivider'
import DropdownItem from './DropdownItem'
import DropdownMenu from './DropdownMenu'

const pluginPropTypes = {
// Settings
action: PropTypes.string,
allowAdditions: PropTypes.bool,
allowCategorySelection: PropTypes.bool,
apiSettings: PropTypes.object,
fields: PropTypes.object,
forceSelection: PropTypes.bool,
glyphWidth: PropTypes.number,
label: PropTypes.object,
match: PropTypes.string,
maxSelections: PropTypes.number,
on: PropTypes.string,
placeholder: PropTypes.oneOf([
PropTypes.string,
PropTypes.bool,
]),
saveRemoteData: PropTypes.bool,
useLabels: PropTypes.bool,

// Additional Settings
direction: PropTypes.string,
keepOnScreen: PropTypes.bool,
context: PropTypes.object,
fullTextSearch: PropTypes.bool,
preserveHTML: PropTypes.bool,
sortSelect: PropTypes.bool,
showOnFocus: PropTypes.bool,
allowTab: PropTypes.bool,
transition: PropTypes.string,
duration: PropTypes.number,
keys: PropTypes.object,
delay: PropTypes.object,

// Callbacks
onChange: PropTypes.func,
onShow: PropTypes.func,
onHide: PropTypes.func,
onNoResults: PropTypes.func,
onLabelSelect: PropTypes.func,
onLabelRemove: PropTypes.func,
onLabelCreate: PropTypes.func,
onAdd: PropTypes.func,
onRemove: PropTypes.func,
}

export default class Dropdown extends Component {
static propTypes = {
...pluginPropTypes,
text: PropTypes.string,
defaultText: PropTypes.string,
className: PropTypes.string,
children: customPropTypes.mutuallyExclusive(['options']),
icon: PropTypes.string,
options: PropTypes.arrayOf(PropTypes.shape({
value: PropTypes.string,
text: PropTypes.string,
})),

// Settings
action: PropTypes.string,
allowAdditions: PropTypes.bool,
allowCategorySelection: PropTypes.bool,
apiSettings: PropTypes.object,
fields: PropTypes.object,
forceSelection: PropTypes.bool,
glyphWidth: PropTypes.number,
label: PropTypes.object,
match: PropTypes.string,
maxSelections: PropTypes.number,
on: PropTypes.string,
placeholder: PropTypes.oneOf([
defaultValue: PropTypes.oneOfType([
PropTypes.string,
PropTypes.bool,
PropTypes.number,
]),
saveRemoteData: PropTypes.bool,
useLabels: PropTypes.bool,

// Additional Settings
direction: PropTypes.string,
keepOnScreen: PropTypes.bool,
context: PropTypes.object,
fullTextSearch: PropTypes.bool,
preserveHTML: PropTypes.bool,
sortSelect: PropTypes.bool,
showOnFocus: PropTypes.bool,
allowTab: PropTypes.bool,
transition: PropTypes.string,
duration: PropTypes.number,
keys: PropTypes.object,
delay: PropTypes.object,

// Callbacks
onChange: PropTypes.func,
onShow: PropTypes.func,
onHide: PropTypes.func,
onNoResults: PropTypes.func,
onLabelSelect: PropTypes.func,
onLabelRemove: PropTypes.func,
onLabelCreate: PropTypes.func,
onAdd: PropTypes.func,
onRemove: PropTypes.func,
options: PropTypes.arrayOf(PropTypes.shape({
value: PropTypes.oneOfType([
PropTypes.string,
PropTypes.number,
]),
text: PropTypes.oneOfType([
PropTypes.string,
PropTypes.number,
]),
})),
};

static defaultProps = {
icon: 'dropdown',
};

componentDidMount() {
this.element = $(this.refs.element)
this.element.dropdown({
// Settings
action: this.props.action,
allowAdditions: this.props.allowAdditions,
allowCategorySelection: this.props.allowCategorySelection,
apiSettings: this.props.apiSettings,
fields: this.props.fields,
forceSelection: this.props.forceSelection,
glyphWidth: this.props.glyphWidth,
label: this.props.label,
match: this.props.match,
maxSelections: this.props.maxSelections,
on: this.props.on,
placeholder: this.props.placeholder,
saveRemoveData: this.props.saveRemoteData,
useLabels: this.props.useLabels,

// Additional Settings
direction: this.props.delay,
keepOnScreen: this.props.keys,
context: this.props.duration,
fullTextSearch: this.props.transition,
preserveHTML: this.props.allowTab,
sortSelect: this.props.showOnFocus,
showOnFocus: this.props.sortSelect,
allowTab: this.props.preserveHTML,
transition: this.props.fullTextSearch,
duration: this.props.context,
keys: this.props.keepOnScreen,
delay: this.props.direction,

// Callbacks
onChange: this.props.onChange,
onHide: this.props.onHide,
onShow: this.props.onShow,
onNoResults: this.props.onNoResults,
onLabelSelect: this.props.onLabelSelect,
onLabelRemove: this.props.onLabelRemove,
onLabelCreate: this.props.onLabelCreate,
onAdd: this.props.onAdd,
onRemove: this.props.onRemove,
})
this.refresh()
}

componentDidUpdate(prevProps, prevState) {
this.element.dropdown('refresh')
if (prevProps === this.props) {
this.refresh()
}
}

componentWillUnmount() {
Expand All @@ -138,39 +112,40 @@ export default class Dropdown extends Component {
return this.element.dropdown(...arguments)
}

refresh() {
this.element = $(this.refs.element)
this.element.dropdown(getPluginProps(this.props, pluginPropTypes))
}

render() {
const { children, className, icon, options, text } = this.props
const items = _.map(options, (opt, i) => {
return (
<DropdownItem key={`${opt.value}-${i}`} data-value={opt.value}>
{opt.text}
</DropdownItem>
)
})
const { children, className, defaultText, icon, options, text } = this.props
const classes = cx(
'sd-dropdown',
'ui',
className,
'dropdown'
)

const iconClasses = cx(
'sd-dropdown-icon',
icon,
'icon'
)

const props = getUnhandledProps(this)
const items = _.map(options, (opt, i) => (
<DropdownItem key={`${opt.value}-${i}`} data-value={opt.value}>
{opt.text}
</DropdownItem>
))

const hiddenInput = !_.includes(classes, 'selection') ? null : (
<input type='hidden' defaultValue={props.defaultValue} />
<input type='hidden' defaultValue={this.props.defaultValue} />
)

return (
<div {...props} className={classes} ref='element'>
<div {...getComponentProps(this.props, pluginPropTypes)} className={classes} ref='element'>
{hiddenInput}
{text}
{text && <div className='text'>{text}</div>}
{icon && <i className={iconClasses} />}
{defaultText && <div className='default text'>{defaultText}</div>}
<DropdownMenu>
{children || items}
</DropdownMenu>
Expand Down

0 comments on commit cc91c86

Please sign in to comment.