11// Libraries
2- import React , { MouseEvent , forwardRef } from 'react'
2+ import React , { MouseEvent , forwardRef , useState } from 'react'
33
44// Components
55import { Dropdown , DropdownRef } from '../'
@@ -17,6 +17,7 @@ import {
1717 ComponentStatus ,
1818 StandardFunctionProps ,
1919} from '../../../Types'
20+ import { Input } from '../../Inputs'
2021
2122export interface MultiSelectDropdownProps extends StandardFunctionProps {
2223 /** Text to render in button as currently selected option */
@@ -43,6 +44,9 @@ export interface MultiSelectDropdownProps extends StandardFunctionProps {
4344 menuMaxHeight ?: number
4445 /** Renders the menu element above the button instead of below */
4546 dropUp ?: boolean
47+ /** Enables the search bar in the dropdown menu */
48+ isSearchable ?: boolean
49+ searchbarInputPlaceholder ?: string
4650}
4751
4852export type MultiSelectDropdownRef = DropdownRef
@@ -69,13 +73,17 @@ export const MultiSelectDropdown = forwardRef<
6973 buttonStatus = ComponentStatus . Default ,
7074 menuMaxHeight,
7175 selectedOptions,
76+ isSearchable = false ,
77+ searchbarInputPlaceholder = 'Search' ,
7278 } ,
7379 ref
7480 ) => {
7581 const buttonText = selectedOptions . length
7682 ? selectedOptions . join ( ', ' )
7783 : emptyText
7884
85+ const [ filterString , setFilterString ] = useState ( '' )
86+
7987 const button = (
8088 active : boolean ,
8189 onClick : ( e : MouseEvent < HTMLElement > ) => void
@@ -92,32 +100,79 @@ export const MultiSelectDropdown = forwardRef<
92100 </ Dropdown . Button >
93101 )
94102
103+ const NoResults = ( ) => (
104+ < Dropdown . Item
105+ key = "no-values-in-filter"
106+ testID = "nothing-in-filter-typeAhead"
107+ disabled = { true }
108+ >
109+ { filterString . length > 0
110+ ? `no matches for ${ filterString } `
111+ : 'No results' }
112+ </ Dropdown . Item >
113+ )
114+
115+ const handleFiltering = ( e : any ) => {
116+ const filterStr = e . currentTarget . value
117+ setFilterString ( filterStr )
118+ }
119+
120+ const clearFilter = ( ) => {
121+ setFilterString ( '' )
122+ }
123+
95124 const menu = ( ) => (
96- < Dropdown . Menu theme = { menuTheme } maxHeight = { menuMaxHeight } >
97- { options . map ( o => {
98- if ( o === DROPDOWN_DIVIDER_SHORTCODE ) {
99- return < Dropdown . Divider key = { o } />
100- }
101-
102- if ( o . includes ( DROPDOWN_DIVIDER_SHORTCODE ) ) {
103- const dividerText = o . replace ( DROPDOWN_DIVIDER_SHORTCODE , '' )
104- return < Dropdown . Divider key = { o } text = { dividerText } />
105- }
106-
107- return (
108- < Dropdown . Item
109- key = { o }
110- type = { indicator }
111- value = { o }
112- title = { o }
113- selected = { selectedOptions . includes ( o ) }
114- onClick = { onSelect }
115- >
116- { o }
117- </ Dropdown . Item >
118- )
119- } ) }
120- </ Dropdown . Menu >
125+ < >
126+ { isSearchable && (
127+ < Dropdown . Menu theme = { menuTheme } style = { { paddingBottom : 0 } } >
128+ < Input
129+ placeholder = { searchbarInputPlaceholder }
130+ value = { filterString }
131+ onChange = { handleFiltering }
132+ onClear = { clearFilter }
133+ />
134+ </ Dropdown . Menu >
135+ ) }
136+ < Dropdown . Menu theme = { menuTheme } maxHeight = { menuMaxHeight } >
137+ { options . map ( o => {
138+ // case-insensitive search
139+ if (
140+ isSearchable &&
141+ ! o . toUpperCase ( ) . includes ( filterString . toUpperCase ( ) )
142+ ) {
143+ return
144+ }
145+
146+ if ( o === DROPDOWN_DIVIDER_SHORTCODE ) {
147+ return < Dropdown . Divider key = { o } />
148+ }
149+
150+ if ( o . includes ( DROPDOWN_DIVIDER_SHORTCODE ) ) {
151+ const dividerText = o . replace ( DROPDOWN_DIVIDER_SHORTCODE , '' )
152+ return < Dropdown . Divider key = { o } text = { dividerText } />
153+ }
154+
155+ return (
156+ < Dropdown . Item
157+ key = { o }
158+ type = { indicator }
159+ value = { o }
160+ title = { o }
161+ selected = { selectedOptions . includes ( o ) }
162+ onClick = { onSelect }
163+ >
164+ { o }
165+ </ Dropdown . Item >
166+ )
167+ } ) }
168+
169+ { options . filter ( option =>
170+ option . toUpperCase ( ) . includes ( filterString . toUpperCase ( ) )
171+ ) . length === 0 ? (
172+ < NoResults />
173+ ) : null }
174+ </ Dropdown . Menu >
175+ </ >
121176 )
122177
123178 return (
0 commit comments