Skip to content

Commit

Permalink
feat(Dropdown): make deburr opt-in, and deburr input (#2223)
Browse files Browse the repository at this point in the history
* feat(Dropdown): make deburr opt-in, and deburr input

* feat(Dropdown): remake non-filter test

* docs(Dropdown): add custom search function example

* style(Dropdown): sort props and imports
  • Loading branch information
patrikmolsson authored and levithomason committed Nov 4, 2017
1 parent 50c29ad commit 64bf4a1
Show file tree
Hide file tree
Showing 6 changed files with 127 additions and 11 deletions.
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
import _ from 'lodash'
import React from 'react'
import { Dropdown } from 'semantic-ui-react'

const caseSensitiveSearch = (options, query) => {
const re = new RegExp(_.escapeRegExp(query))
return options.filter(opt => re.test(opt.text))
}

const options = [
{ key: 'a', value: 'a', text: 'UPPERCASE' },
{ key: 'b', value: 'b', text: 'lowercase' },
]

const DropdownExampleCustomSearchFunction = () => (
<Dropdown
fluid
options={options}
placeholder={'Try to search for case or CASE'}
search={caseSensitiveSearch}
selection
/>
)

export default DropdownExampleCustomSearchFunction
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
import React from 'react'
import { Dropdown } from 'semantic-ui-react'

const options = [
{ key: 'a', value: 'a', text: 'Café with accent' },
{ key: 'b', value: 'b', text: 'Cafe without accent' },
{ key: 'c', value: 'c', text: 'Déjà vu' },
{ key: 'd', value: 'd', text: 'Deja vu' },
{ key: 'e', value: 'e', text: 'Scandinavian å ä æ ø ö' },
{ key: 'f', value: 'f', text: 'Scandinavian a a ae o o' },
]

const DropdownExampleSearchSelection = () => (
<Dropdown
deburr
fluid
options={options}
placeholder='Try to search for "Deja vu"'
search
selection
/>
)

export default DropdownExampleSearchSelection
10 changes: 10 additions & 0 deletions docs/app/Examples/modules/Dropdown/Usage/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -104,6 +104,16 @@ const DropdownUsageExamples = () => (
selection.
</Message>
</ComponentExample>
<ComponentExample
title='Search Deburr'
description='A dropdown allows the search to ignore diacritics.'
examplePath='modules/Dropdown/Usage/DropdownExampleDeburrSearch'
/>
<ComponentExample
title='Custom Search Function'
description='A dropdown allows you to provide your own search function.'
examplePath='modules/Dropdown/Usage/DropdownExampleCustomSearchFunction'
/>
<ComponentExample
title='Upward'
description='A dropdown can open its menu upward.'
Expand Down
3 changes: 3 additions & 0 deletions src/modules/Dropdown/Dropdown.d.ts
Original file line number Diff line number Diff line change
Expand Up @@ -50,6 +50,9 @@ export interface DropdownProps {
/** A compact dropdown has no minimum width. */
compact?: boolean;

/** Whether or not the dropdown should strip diacritics in options and input search */
deburr?: boolean;

/** Initial value of open. */
defaultOpen?: boolean;

Expand Down
16 changes: 12 additions & 4 deletions src/modules/Dropdown/Dropdown.js
Original file line number Diff line number Diff line change
Expand Up @@ -90,6 +90,9 @@ export default class Dropdown extends Component {
/** A compact dropdown has no minimum width. */
compact: PropTypes.bool,

/** Whether or not the dropdown should strip diacritics in options and input search */
deburr: PropTypes.bool,

/** Initial value of open. */
defaultOpen: PropTypes.bool,

Expand Down Expand Up @@ -354,6 +357,7 @@ export default class Dropdown extends Component {
additionLabel: 'Add ',
additionPosition: 'top',
closeOnBlur: true,
deburr: false,
icon: 'dropdown',
minCharacters: 1,
noResultsMessage: 'No results found.',
Expand Down Expand Up @@ -787,7 +791,7 @@ export default class Dropdown extends Component {
// There are times when we need to calculate the options based on a value
// that hasn't yet been persisted to state.
getMenuOptions = (value = this.state.value, options = this.props.options) => {
const { multiple, search, allowAdditions, additionPosition, additionLabel } = this.props
const { additionLabel, additionPosition, allowAdditions, deburr, multiple, search } = this.props
const { searchQuery } = this.state

let filteredOptions = options
Expand All @@ -802,9 +806,13 @@ export default class Dropdown extends Component {
if (_.isFunction(search)) {
filteredOptions = search(filteredOptions, searchQuery)
} else {
const re = new RegExp(_.escapeRegExp(searchQuery), 'i')
// remove diacritics on search
filteredOptions = _.filter(filteredOptions, opt => re.test(_.deburr(opt.text)))
// remove diacritics on search input and options, if deburr prop is set
const strippedQuery = deburr ? _.deburr(searchQuery) : searchQuery

const re = new RegExp(_.escapeRegExp(strippedQuery), 'i')

filteredOptions = _.filter(filteredOptions, opt =>
re.test(deburr ? _.deburr(opt.text) : opt.text))
}
}

Expand Down
60 changes: 53 additions & 7 deletions test/specs/modules/Dropdown/Dropdown-test.js
Original file line number Diff line number Diff line change
Expand Up @@ -682,22 +682,68 @@ describe('Dropdown', () => {
.find('.selected')
.should.contain.text('a2')
})
it('filter after diacritics', () => {
it('filters diacritics on options when using deburr prop', () => {
const inputText = 'floresti'
const textToFind = 'FLOREŞTI'

const opts = [
{ text: 'FLOREŞTI', value: '1' },
{ text: 'ŞANŢU FLOREŞTI', value: '2' },
{ text: 'FLOREŞTI Alba', value: '3' },
{ text: textToFind, value: '1' },
{ text: `ŞANŢU ${textToFind}`, value: '2' },
{ text: `${textToFind} Alba`, value: '3' },
]

// search for 'floresti'
wrapperMount(<Dropdown options={opts} search selection />)
wrapperMount(<Dropdown options={opts} search deburr selection />)
.simulate('click')
.find('input.search')
.simulate('change', { target: { value: inputText } })

wrapper
.find('.selected')
.should.contain.text(textToFind)
})
it('filters diacritics on input when using deburr prop', () => {
const inputText = 'FLORÉŞTI'
const textToFind = 'FLORESTI'

const opts = [
{ text: textToFind, value: '1' },
{ text: `SANTU ${textToFind}`, value: '2' },
{ text: `${textToFind} Alba`, value: '3' },
]

// search for 'floresti'
wrapperMount(<Dropdown options={opts} search deburr selection />)
.simulate('click')
.find('input.search')
.simulate('change', { target: { value: 'floresti' } })
.simulate('change', { target: { value: inputText } })

wrapper
.find('.selected')
.should.contain.text('FLOREŞTI')
.should.contain.text(textToFind)
})
it('should not filter diacritics when deburr is not set', () => {
const inputText = 'FLORÉŞTI'
const textToFind = 'FLORESTI'

// Add this in case the default 'no results text' changes.
const noResultsText = 'NoResultsFound'

const opts = [
{ text: textToFind, value: '1' },
{ text: `SANTU ${textToFind}`, value: '2' },
{ text: `${textToFind} Alba`, value: '3' },
]

// search for 'floresti'
wrapperMount(<Dropdown options={opts} search selection noResultsMessage={noResultsText} />)
.simulate('click')
.find('input.search')
.simulate('change', { target: { value: inputText } })

wrapper
.find('.message')
.should.contain.text(noResultsText)
})
it('still works after encountering "no results"', () => {
const opts = [
Expand Down

0 comments on commit 64bf4a1

Please sign in to comment.