11// Libraries
2- import React , { Component , ChangeEvent , CSSProperties } from 'react'
2+ import React , { FC , ChangeEvent , CSSProperties , useState } from 'react'
33
44// Components
55import {
@@ -11,9 +11,6 @@ import {
1111 ComponentColor ,
1212} from '@influxdata/clockface'
1313
14- // Decorators
15- import { ErrorHandling } from 'src/shared/decorators/errors'
16-
1714interface Props {
1815 testID : string
1916 className ?: string
@@ -22,104 +19,45 @@ interface Props {
2219 selectedOption : string
2320 onSelect : ( option : string ) => void
2421 onChangeSearchTerm ?: ( value : string ) => void
25- buttonSize : ComponentSize
26- buttonColor : ComponentColor
2722 buttonStatus : ComponentStatus
2823 buttonTestID : string
29- menuTheme : DropdownMenuTheme
3024 menuTestID : string
3125 options : ( string | number ) [ ]
3226 emptyText : string
3327 style ?: CSSProperties
3428}
3529
36- @ErrorHandling
37- export default class SearchableDropdown extends Component < Props > {
38- public static defaultProps = {
39- buttonSize : ComponentSize . Small ,
40- buttonColor : ComponentColor . Default ,
41- buttonStatus : ComponentStatus . Default ,
42- menuTheme : DropdownMenuTheme . Onyx ,
43- testID : 'searchable-dropdown' ,
44- buttonTestID : 'searchable-dropdown--button' ,
45- menuTestID : 'searchable-dropdown--menu' ,
46- }
47-
48- public render ( ) {
49- const {
50- searchTerm,
51- searchPlaceholder,
52- buttonSize,
53- buttonColor,
54- buttonStatus,
55- buttonTestID,
56- selectedOption,
57- testID,
58- className,
59- style,
60- menuTheme,
61- menuTestID,
62- } = this . props
30+ const SearchableDropdown : FC < Props > = ( {
31+ buttonStatus = ComponentStatus . Default ,
32+ testID = 'searchable-dropdown' ,
33+ buttonTestID = 'searchable-dropdown--button' ,
34+ menuTestID = 'searchable-dropdown--menu' ,
35+ searchTerm = '' ,
36+ searchPlaceholder,
37+ selectedOption,
38+ className,
39+ style,
40+ options,
41+ onChangeSearchTerm,
42+ emptyText,
43+ onSelect,
44+ } ) => {
45+ const [ isSearchActive , setIsSearchActive ] = useState ( false )
6346
64- return (
65- < Dropdown
66- testID = { testID }
67- className = { className }
68- style = { style }
69- button = { ( active , onClick ) => (
70- < Dropdown . Button
71- active = { active }
72- onClick = { onClick }
73- testID = { buttonTestID }
74- color = { buttonColor }
75- size = { buttonSize }
76- status = { buttonStatus }
77- >
78- { buttonStatus === ComponentStatus . Loading
79- ? 'Loading...'
80- : selectedOption }
81- </ Dropdown . Button >
82- ) }
83- menu = { onCollapse => (
84- < Dropdown . Menu
85- onCollapse = { onCollapse }
86- theme = { menuTheme }
87- testID = { menuTestID }
88- >
89- < div className = "searchable-dropdown--input-container" >
90- < Input
91- onChange = { this . handleChange }
92- value = { searchTerm }
93- placeholder = { searchPlaceholder }
94- size = { buttonSize }
95- autoFocus = { true }
96- />
97- </ div >
98- { this . filteredMenuOptions }
99- </ Dropdown . Menu >
100- ) }
101- />
102- )
47+ const handleChange = ( e : ChangeEvent < HTMLInputElement > ) : void => {
48+ onChangeSearchTerm ( e . target . value )
10349 }
10450
105- private get filteredMenuOptions ( ) : JSX . Element [ ] | JSX . Element {
106- const {
107- searchTerm,
108- options,
109- emptyText,
110- selectedOption,
111- onSelect,
112- } = this . props
113-
114- const filteredOptions = options . filter ( option =>
115- `${ option } ` . toLocaleLowerCase ( ) . includes ( searchTerm . toLocaleLowerCase ( ) )
116- )
51+ const filteredOptions = options . filter ( option =>
52+ `${ option } ` . toLocaleLowerCase ( ) . includes ( searchTerm . toLocaleLowerCase ( ) )
53+ )
11754
118- if ( ! filteredOptions . length ) {
119- return < div className = "searchable-dropdown--empty" > { emptyText } </ div >
120- }
55+ let body : JSX . Element | JSX . Element [ ] = (
56+ < div className = "searchable-dropdown--empty" > { emptyText } </ div >
57+ )
12158
122- return filteredOptions . map ( option => (
59+ if ( filteredOptions . length ) {
60+ body = filteredOptions . map ( option => (
12361 < Dropdown . Item
12462 key = { option }
12563 value = { option }
@@ -132,11 +70,51 @@ export default class SearchableDropdown extends Component<Props> {
13270 ) )
13371 }
13472
135- private handleChange = ( e : ChangeEvent < HTMLInputElement > ) : void => {
136- const { onChangeSearchTerm} = this . props
137-
138- if ( onChangeSearchTerm ) {
139- onChangeSearchTerm ( e . target . value )
140- }
141- }
73+ return (
74+ < Dropdown
75+ testID = { testID }
76+ className = { className }
77+ style = { style }
78+ button = { ( active , onClick ) => (
79+ < Dropdown . Button
80+ active = { active }
81+ onClick = { onClick }
82+ testID = { buttonTestID }
83+ color = { ComponentColor . Default }
84+ size = { ComponentSize . Small }
85+ status = { buttonStatus }
86+ >
87+ { buttonStatus === ComponentStatus . Loading
88+ ? 'Loading...'
89+ : selectedOption }
90+ </ Dropdown . Button >
91+ ) }
92+ menu = { onCollapse => (
93+ < Dropdown . Menu
94+ onCollapse = { ( ) => {
95+ if ( isSearchActive === false ) {
96+ onCollapse ( )
97+ }
98+ } }
99+ theme = { DropdownMenuTheme . Onyx }
100+ testID = { menuTestID }
101+ >
102+ < div className = "searchable-dropdown--input-container" >
103+ < Input
104+ onFocus = { ( ) => setIsSearchActive ( true ) }
105+ onChange = { handleChange }
106+ onBlur = { ( ) => setIsSearchActive ( false ) }
107+ value = { searchTerm }
108+ placeholder = { searchPlaceholder }
109+ size = { ComponentSize . Small }
110+ autoFocus = { true }
111+ />
112+ </ div >
113+ { body }
114+ </ Dropdown . Menu >
115+ ) }
116+ />
117+ )
142118}
119+
120+ export default SearchableDropdown
0 commit comments