Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat(Dropdown): make deburr opt-in, and deburr input #2223

Merged
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
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 @@ -100,6 +100,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 @@ -658,22 +658,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