Skip to content

Commit

Permalink
feat(MultiSelect): make MultiSelectDropdown optionally searchable (#885)
Browse files Browse the repository at this point in the history
  • Loading branch information
ChitlangeSahas authored Nov 2, 2022
1 parent d59fb30 commit fc49fee
Show file tree
Hide file tree
Showing 4 changed files with 92 additions and 28 deletions.
6 changes: 5 additions & 1 deletion CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -1,5 +1,9 @@
# Changelog

### 6.3.9 (2022-11-02)

- [885](https://github.com/influxdata/clockface/pull/885): (MultiSelect): make MultiSelectDropdown optionally searchable through `isSearchable` prop

### 6.3.8 (2022-10-04)

- [857](https://github.com/influxdata/clockface/pull/857): (TimeInput): Fix the dropdown button width to accomodate for multi-character units
Expand Down Expand Up @@ -39,6 +43,7 @@
### 6.2.0 (2022-08-24)

- [814](https://github.com/influxdata/clockface/pull/814): Added Collapse functionality to the Draggable Resizer

### 6.1.1 (2022-08-17)

- [823](https://github.com/influxdata/clockface/pull/823): Typeahead Dropdown select text on focus works on all browsers
Expand All @@ -55,7 +60,6 @@

- [804](https://github.com/influxdata/clockface/pull/804): Add DoubleCaretVertical icon and trailingIcon prop to Dropdown.Button


### 6.0.0 (2022-08-01)

- [812](https://github.com/influxdata/clockface/pull/812): Remove unused MenuDropdown code
Expand Down
2 changes: 1 addition & 1 deletion package.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"name": "@influxdata/clockface",
"version": "6.3.8",
"version": "6.3.9",
"license": "MIT",
"main": "dist/index.js",
"style": "dist/index.css",
Expand Down
107 changes: 81 additions & 26 deletions src/Components/Dropdowns/Composed/MultiSelectDropdown.tsx
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
// Libraries
import React, {MouseEvent, forwardRef} from 'react'
import React, {MouseEvent, forwardRef, useState} from 'react'

// Components
import {Dropdown, DropdownRef} from '../'
Expand All @@ -17,6 +17,7 @@ import {
ComponentStatus,
StandardFunctionProps,
} from '../../../Types'
import {Input} from '../../Inputs'

export interface MultiSelectDropdownProps extends StandardFunctionProps {
/** Text to render in button as currently selected option */
Expand All @@ -43,6 +44,9 @@ export interface MultiSelectDropdownProps extends StandardFunctionProps {
menuMaxHeight?: number
/** Renders the menu element above the button instead of below */
dropUp?: boolean
/** Enables the search bar in the dropdown menu */
isSearchable?: boolean
searchbarInputPlaceholder?: string
}

export type MultiSelectDropdownRef = DropdownRef
Expand All @@ -69,13 +73,17 @@ export const MultiSelectDropdown = forwardRef<
buttonStatus = ComponentStatus.Default,
menuMaxHeight,
selectedOptions,
isSearchable = false,
searchbarInputPlaceholder = 'Search',
},
ref
) => {
const buttonText = selectedOptions.length
? selectedOptions.join(', ')
: emptyText

const [filterString, setFilterString] = useState('')

const button = (
active: boolean,
onClick: (e: MouseEvent<HTMLElement>) => void
Expand All @@ -92,32 +100,79 @@ export const MultiSelectDropdown = forwardRef<
</Dropdown.Button>
)

const NoResults = () => (
<Dropdown.Item
key="no-values-in-filter"
testID="nothing-in-filter-typeAhead"
disabled={true}
>
{filterString.length > 0
? `no matches for ${filterString}`
: 'No results'}
</Dropdown.Item>
)

const handleFiltering = (e: any) => {
const filterStr = e.currentTarget.value
setFilterString(filterStr)
}

const clearFilter = () => {
setFilterString('')
}

const menu = () => (
<Dropdown.Menu theme={menuTheme} maxHeight={menuMaxHeight}>
{options.map(o => {
if (o === DROPDOWN_DIVIDER_SHORTCODE) {
return <Dropdown.Divider key={o} />
}

if (o.includes(DROPDOWN_DIVIDER_SHORTCODE)) {
const dividerText = o.replace(DROPDOWN_DIVIDER_SHORTCODE, '')
return <Dropdown.Divider key={o} text={dividerText} />
}

return (
<Dropdown.Item
key={o}
type={indicator}
value={o}
title={o}
selected={selectedOptions.includes(o)}
onClick={onSelect}
>
{o}
</Dropdown.Item>
)
})}
</Dropdown.Menu>
<>
{isSearchable && (
<Dropdown.Menu theme={menuTheme} style={{paddingBottom: 0}}>
<Input
placeholder={searchbarInputPlaceholder}
value={filterString}
onChange={handleFiltering}
onClear={clearFilter}
/>
</Dropdown.Menu>
)}
<Dropdown.Menu theme={menuTheme} maxHeight={menuMaxHeight}>
{options.map(o => {
// case-insensitive search
if (
isSearchable &&
!o.toUpperCase().includes(filterString.toUpperCase())
) {
return
}

if (o === DROPDOWN_DIVIDER_SHORTCODE) {
return <Dropdown.Divider key={o} />
}

if (o.includes(DROPDOWN_DIVIDER_SHORTCODE)) {
const dividerText = o.replace(DROPDOWN_DIVIDER_SHORTCODE, '')
return <Dropdown.Divider key={o} text={dividerText} />
}

return (
<Dropdown.Item
key={o}
type={indicator}
value={o}
title={o}
selected={selectedOptions.includes(o)}
onClick={onSelect}
>
{o}
</Dropdown.Item>
)
})}

{options.filter(option =>
option.toUpperCase().includes(filterString.toUpperCase())
).length === 0 ? (
<NoResults />
) : null}
</Dropdown.Menu>
</>
)

return (
Expand Down
5 changes: 5 additions & 0 deletions src/Components/Dropdowns/Documentation/Dropdowns.stories.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -906,6 +906,11 @@ dropdownComposedStories.add(
emptyText={text('emptyText', 'None selected')}
selectedOptions={selectedOptions}
options={array('options', defaultMultiSelectOptions)}
isSearchable={boolean('isSearchable', true)}
searchbarInputPlaceholder={text(
'searchbarInputPlaceholder',
'Search'
)}
/>
<div className="story--test-buttons">
<button onClick={logRef}>Log Ref</button>
Expand Down

0 comments on commit fc49fee

Please # to comment.