Skip to content

Commit

Permalink
feat(Search): update to v1 API
Browse files Browse the repository at this point in the history
* Add "Component Explorer" functionality
* Use Input, clean up unused props
* Category search
* Update Search parts to use ElementType
* Add specs
  • Loading branch information
jeffcarbs committed Sep 10, 2016
1 parent 2d2815f commit 0d5c987
Show file tree
Hide file tree
Showing 12 changed files with 1,964 additions and 2 deletions.
200 changes: 200 additions & 0 deletions docs/app/Examples/modules/Search/Types/Search.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,200 @@
import _ from 'lodash'
import faker from 'faker'
import React, { Component } from 'react'
import { Search, Select, Grid, Header } from 'stardust'

const searchResults = _.times(5, () => {
return {
title: faker.company.companyName(),
description: faker.company.catchPhrase(),
image: 'http://semantic-ui.com/images/wireframe/image.png',
}
})

const categoryResults = _.range(0, 3).reduce((memo, index) => {
const category = faker.company.bsBuzz()

memo[category] = {
name: category,
results: _.times(5, () => {
return {
title: faker.company.companyName(),
description: faker.company.catchPhrase(),
image: 'http://semantic-ui.com/images/wireframe/image.png',
}
}),
}

return memo
}, {})

const prettyPrint = (object) => JSON.stringify(object, null, 2)

const componentPropsOptions = {
aligned: ['left', 'right'],
category: [true, false],
fluid: [true, false],
icon: ['search', 'dropdown', 'calendar'],
loading: [true, false],
maxResults: _.range(1, 20),
minCharacters: _.range(1, 5),
noResultsDescription: ['There are no matching results', 'Could not find anything'],
noResultsMessage: ['No results', 'Nothing here'],
placeholder: ['Search people...', 'Search'],
scrolling: [true, false],
searchFields: [['description'], ['title', 'description']],
selectFirstResult: [true, false],
selection: [true, false],
showNoResults: [true, false],
size: Search._meta.props.size,
source: [true],
}

export default class SearchSelectionExample extends Component {
componentWillMount() {
this.setState({
minCharacters: 1,
value: '',
componentProps: {},
})
}

getResults = () => {
const { componentProps } = this.state

return componentProps.category ? categoryResults : searchResults
}

getFilteredResults = () => {
const { componentProps, filteredCategoryResults, filteredSearchResults } = this.state

return componentProps.category ? filteredCategoryResults : filteredSearchResults
}

handleChange = (e, result) => this.setState({ value: result.title })
handleSearchChange = (e, value) => {
this.setState({ value })

const { source, minCharacters = 1 } = this.state.componentProps

if (value.length < minCharacters) {
this.setState({ results: undefined })
} else if (!source) {
this.fetchOptions()
}
}

fetchOptions = () => {
this.setState({ loading: true })

setTimeout(() => {
const re = new RegExp(_.escapeRegExp(this.state.value), 'i')
const isMatch = (result) => re.test(result.title)

const filteredCategoryResults = _.reduce(categoryResults, (memo, categoryData, name) => {
const filteredResults = _.filter(categoryData.results, isMatch)

if (filteredResults.length) {
memo[name] = { name, results: filteredResults }
}

return memo
}, {})

const filteredSearchResults = _.filter(searchResults, isMatch)

this.setState({
loading: false,
filteredCategoryResults,
filteredSearchResults,
})
}, 500)
}

toggleComponentProp = (propName, e) => {
this.setState({
componentProps: {
...this.state.componentProps,
[propName]: e.target.checked ? componentPropsOptions[propName][0] : undefined,
},
})
}

setComponentProp = (propName, e, value) => {
this.setState({
componentProps: {
...this.state.componentProps,
[propName]: value,
},
})
}

renderComponentProp = (values, propName) => {
const currentValue = this.state.componentProps[propName]
const active = currentValue !== undefined

const dropdownOptions = values.map((value) => {
return { text: prettyPrint(value), value }
})

return (
<p key={propName}>
<strong>{propName}:</strong>
<br />
<label>
{' '}
<input
type='checkbox'
checked={active}
onChange={_.partial(this.toggleComponentProp, propName)}
/>
</label>
{' '}
<Select
options={dropdownOptions}
value={currentValue}
disabled={!active}
onChange={_.partial(this.setComponentProp, propName)}
/>
</p>
)
}

render() {
const { loading, value, componentProps } = this.state

let source
let results

if (componentProps.source) {
source = this.getResults()
} else {
results = this.getFilteredResults()
}

const searchProps = {
results,
loading,
value,
onChange: this.handleChange,
onSearchChange: this.handleSearchChange,
..._.omit(componentProps, _.isUndefined),
source,
}

return (
<Grid>
<Grid.Column width={6}>
{_.map(componentPropsOptions, this.renderComponentProp)}
</Grid.Column>
<Grid.Column width={6}>
<Search {...searchProps} />
</Grid.Column>
<Grid.Column width={4}>
<Header>State</Header>
<pre>{prettyPrint(searchProps)}</pre>
</Grid.Column>
</Grid>
)
}
}
17 changes: 17 additions & 0 deletions docs/app/Examples/modules/Search/index.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
import React from 'react'
import ComponentExample from 'docs/app/Components/ComponentDoc/ComponentExample'
import ExampleSection from 'docs/app/Components/ComponentDoc/ExampleSection'

const SearchExamples = () => (
<div>
<ExampleSection title='Types'>
<ComponentExample
title='Search'
description='A search box'
examplePath='modules/Search/Types/Search'
/>
</ExampleSection>
</div>
)

export default SearchExamples
5 changes: 3 additions & 2 deletions src/elements/Input/Input.js
Original file line number Diff line number Diff line change
Expand Up @@ -54,6 +54,7 @@ export default class Input extends Component {
children: PropTypes.node,
className: PropTypes.string,
icon: PropTypes.string,
inputClassName: PropTypes.string,
type: PropTypes.string,
}

Expand All @@ -67,7 +68,7 @@ export default class Input extends Component {
}

render() {
const { className, children, icon, type } = this.props
const { className, children, icon, inputClassName, type } = this.props
// Semantic supports actions and labels on either side of an input.
// The element must be on the same side as the indicated class.
// We first determine the left/right classes for each type of child,
Expand Down Expand Up @@ -105,7 +106,7 @@ export default class Input extends Component {
<ElementType {...rest} className={classes}>
{isLeftLabeled && labelChildren}
{isLeftAction && actionChildren}
<input {...inputProps} type={type} />
<input {...inputProps} className={inputClassName} type={type} />
{icon && <Icon name={icon} />}
{isRightLabeled && labelChildren}
{isRightAction && actionChildren}
Expand Down
Loading

0 comments on commit 0d5c987

Please sign in to comment.