@@ -10,228 +10,202 @@ import keycode from 'keycode';
1010import Menu from '../Menu/Menu' ;
1111import Popover from '../Popover/Popover' ;
1212import PropTypes from 'prop-types' ;
13- import React , { PureComponent } from 'react' ;
13+ import React , { useEffect , useState } from 'react' ;
1414
15- class SearchInput extends PureComponent {
16- constructor ( props ) {
17- super ( props ) ;
18- this . state = {
19- isExpanded : false ,
20- searchExpanded : false ,
21- value : props . inputProps ?. value ? props . inputProps . value : ''
22- } ;
23- }
15+ const SearchInput = React . forwardRef ( ( {
16+ className,
17+ compact,
18+ disabled,
19+ inputProps,
20+ inputGroupAddonProps,
21+ inputGroupProps,
22+ inShellbar,
23+ listProps,
24+ localizedText,
25+ noSearchBtn,
26+ onChange,
27+ onEnter,
28+ onSelect,
29+ placeholder,
30+ popoverProps,
31+ readOnly,
32+ searchList,
33+ subStringSearch,
34+ searchBtnProps,
35+ validationOverlayProps,
36+ validationState,
37+ ...rest
38+ } , ref ) => {
39+ const [ isExpanded , setIsExpanded ] = useState ( false ) ;
40+ const [ searchExpanded , setSearchExpanded ] = useState ( false ) ;
41+ const [ value , setValue ] = useState ( inputProps ?. value || '' ) ;
2442
25- filterList = ( list , query ) => {
26- return this . props . subStringSearch ? list . filter ( ( item ) => {
43+ const filterList = ( list , query ) => {
44+ return subStringSearch ? list . filter ( ( item ) => {
2745 return item . text . toLowerCase ( ) . includes ( query . toLowerCase ( ) ) ;
2846 } ) : list . filter ( ( item ) => item . text . toLowerCase ( ) . startsWith ( query . toLowerCase ( ) ) ) ;
29- }
47+ } ;
3048
31- handleKeyPress = event => {
49+ const handleKeyPress = event => {
3250 if ( keycode ( event ) === 'enter' ) {
33- this . props . onEnter ( this . state . value ) ;
51+ onEnter ( value ) ;
3452 }
3553 } ;
3654
37- handleListItemClick = ( event , item ) => {
38- this . setState ( {
39- value : item . text ,
40- isExpanded : false ,
41- searchExpanded : false
42- } ) ;
55+ const handleListItemClick = ( event , item ) => {
56+ setValue ( item . text ) ;
57+ setIsExpanded ( false ) ;
58+ setSearchExpanded ( false ) ;
4359 item ?. callback ( ) ;
44- this . props . onSelect ( event , item ) ;
60+ onSelect ( event , item ) ;
4561 } ;
4662
47- handleChange = event => {
63+ const handleChange = event => {
4864 let filteredResult ;
49- if ( this . props . searchList ) {
50- filteredResult = this . filterList ( this . props . searchList , event . target . value ) ;
65+ if ( searchList ) {
66+ filteredResult = filterList ( searchList , event . target . value ) ;
5167 }
52- this . setState ( {
53- value : event . target . value ,
54- isExpanded : true
55- } ) ;
56- this . props . onChange ( event , filteredResult ) ;
68+ setValue ( event . target . value ) ;
69+ setIsExpanded ( true ) ;
70+ onChange ( event , filteredResult ) ;
5771 } ;
5872
59- handleClick = ( ) => {
60- if ( ! this . props . readOnly ) {
61- this . setState ( prevState => ( {
62- isExpanded : ! prevState . isExpanded
63- } ) ) ;
73+ const handleClick = ( ) => {
74+ if ( ! readOnly ) {
75+ setIsExpanded ( ! isExpanded ) ;
6476 }
6577 } ;
6678
67- handleClickOutside = ( ) => {
68- this . setState ( {
69- isExpanded : false ,
70- searchExpanded : false
71- } ) ;
72- } ;
73-
74- handleSearchBtn = ( ) => {
75- this . setState ( prevState => ( {
76- searchExpanded : ! prevState . searchExpanded
77- } ) ) ;
78-
79- if ( this . state . searchExpanded && this . state . isExpanded ) {
80- this . setState ( {
81- isExpanded : false
82- } ) ;
83- }
79+ const handleClickOutside = ( ) => {
80+ setIsExpanded ( false ) ;
81+ setSearchExpanded ( false ) ;
8482 } ;
8583
86- handleEsc = event => {
84+ const handleEsc = event => {
8785 if (
88- ( event . keyCode === 27 && this . state . isExpanded === true ) ||
89- ( event . keyCode === 27 && this . state . searchExpanded === true )
86+ ( event . keyCode === 27 && isExpanded === true ) ||
87+ ( event . keyCode === 27 && searchExpanded === true )
9088 ) {
91- this . setState ( {
92- isExpanded : false ,
93- searchExpanded : false ,
94- value : ''
95- } ) ;
89+ setIsExpanded ( false ) ;
90+ setSearchExpanded ( false ) ;
91+ setValue ( '' ) ;
9692 }
9793 } ;
9894
99- componentDidMount ( ) {
100- document . addEventListener ( 'keydown' , this . handleEsc , false ) ;
101- }
102- componentWillUnmount ( ) {
103- document . removeEventListener ( 'keydown' , this . handleEsc , false ) ;
104- }
95+ useEffect ( ( ) => {
96+ document . addEventListener ( 'keydown' , handleEsc , false ) ;
10597
106- render ( ) {
107- const {
108- placeholder,
109- inShellbar,
110- onEnter,
111- searchList,
112- subStringSearch,
113- onChange,
114- onSelect,
115- noSearchBtn,
116- compact,
117- className,
118- inputProps,
119- inputGroupAddonProps,
120- inputGroupProps,
121- listProps,
122- searchBtnProps,
123- popoverProps,
124- validationOverlayProps,
125- validationState,
126- disabled,
127- readOnly,
128- localizedText,
129- ...rest
130- } = this . props ;
98+ return ( ) => {
99+ document . removeEventListener ( 'keydown' , handleEsc , false ) ;
100+ } ;
101+ } ) ;
131102
132- let inputGroupClasses = inputGroupProps && inputGroupProps . className ;
133103
134- inputGroupClasses = ! inShellbar ? classnames (
135- inputGroupClasses ,
136- 'fd-input-group--control' ,
137- {
138- [ `is-${ validationState ?. state } ` ] : validationState ?. state
139- }
140- ) : inputGroupClasses ;
141- let filteredResult = this . state . value && this . props . searchList ? this . filterList ( this . props . searchList , this . state . value ) : this . props . searchList ;
142- const popoverBody = (
143- < Menu >
144- < Menu . List { ...listProps } >
145- { filteredResult && filteredResult . length > 0 ? (
146- filteredResult . map ( ( item , index ) => {
147- return (
148- subStringSearch ? ( < Menu . Item
149- key = { index }
150- onClick = { ( e ) => this . handleListItemClick ( e , item ) } >
151- { item . text }
152- </ Menu . Item > ) :
153- (
154- < Menu . Item
155- key = { index }
156- onClick = { ( e ) => this . handleListItemClick ( e , item ) } >
157- < strong > { this . state . value } </ strong >
158- { this . state . value && this . state . value . length
159- ? item . text . substring ( this . state . value . length )
160- : item . text }
161- </ Menu . Item >
162- )
163- ) ;
164- } )
165- ) : (
166- < Menu . Item > No result</ Menu . Item >
167- ) }
168- </ Menu . List >
169- </ Menu >
170- ) ;
104+ let inputGroupClasses = inputGroupProps && inputGroupProps . className ;
171105
172- const inputGroup = (
173- < InputGroup
174- { ...inputGroupProps }
175- className = { inputGroupClasses }
176- compact = { compact }
106+ inputGroupClasses = ! inShellbar ? classnames (
107+ inputGroupClasses ,
108+ 'fd-input-group--control' ,
109+ {
110+ [ `is-${ validationState ?. state } ` ] : validationState ?. state
111+ }
112+ ) : inputGroupClasses ;
113+ let filteredResult = value && searchList ? filterList ( searchList , value ) : searchList ;
114+ const popoverBody = (
115+ < Menu >
116+ < Menu . List { ...listProps } >
117+ { filteredResult && filteredResult . length > 0 ? (
118+ filteredResult . map ( ( item , index ) => {
119+ return (
120+ subStringSearch ? ( < Menu . Item
121+ key = { index }
122+ onClick = { ( e ) => handleListItemClick ( e , item ) } >
123+ { item . text }
124+ </ Menu . Item > ) :
125+ (
126+ < Menu . Item
127+ key = { index }
128+ onClick = { ( e ) => handleListItemClick ( e , item ) } >
129+ < strong > { value } </ strong >
130+ { value && value . length
131+ ? item . text . substring ( value . length )
132+ : item . text }
133+ </ Menu . Item >
134+ )
135+ ) ;
136+ } )
137+ ) : (
138+ < Menu . Item > No result</ Menu . Item >
139+ ) }
140+ </ Menu . List >
141+ </ Menu >
142+ ) ;
143+
144+ const inputGroup = (
145+ < InputGroup
146+ { ...inputGroupProps }
147+ className = { inputGroupClasses }
148+ compact = { compact }
149+ disabled = { disabled }
150+ readOnly = { readOnly }
151+ validationState = { validationState } >
152+ < FormInput
153+ { ...inputProps }
177154 disabled = { disabled }
155+ onChange = { handleChange }
156+ onClick = { handleClick }
157+ onKeyPress = { handleKeyPress }
158+ placeholder = { placeholder }
178159 readOnly = { readOnly }
179- validationState = { validationState } >
180- < FormInput
181- { ...inputProps }
182- disabled = { disabled }
183- onChange = { this . handleChange }
184- onClick = { this . handleClick }
185- onKeyPress = { this . handleKeyPress }
186- placeholder = { placeholder }
187- readOnly = { readOnly }
188- value = { this . state . value } />
160+ value = { value } />
189161
190- { ! ( noSearchBtn || readOnly ) && (
191- < InputGroup . Addon { ...inputGroupAddonProps } isButton >
192- < Button { ...searchBtnProps }
193- aria-label = { localizedText . searchBtnLabel }
194- disabled = { disabled }
195- glyph = 'search'
196- onClick = { this . handleClick }
197- option = 'transparent' />
198- </ InputGroup . Addon >
199- ) }
200- </ InputGroup >
201- ) ;
162+ { ! ( noSearchBtn || readOnly ) && (
163+ < InputGroup . Addon { ...inputGroupAddonProps } isButton >
164+ < Button { ...searchBtnProps }
165+ aria-label = { localizedText . searchBtnLabel }
166+ disabled = { disabled }
167+ glyph = 'search'
168+ onClick = { handleClick }
169+ option = 'transparent' />
170+ </ InputGroup . Addon >
171+ ) }
172+ </ InputGroup >
173+ ) ;
202174
203- const wrappedInputGroup = (
204- < FormValidationOverlay
205- { ...validationOverlayProps }
206- control = { inputGroup }
207- validationState = { validationState } />
208- ) ;
175+ const wrappedInputGroup = (
176+ < FormValidationOverlay
177+ { ...validationOverlayProps }
178+ control = { inputGroup }
179+ validationState = { validationState } />
180+ ) ;
209181
210- return (
211- < div { ...rest } className = { className } >
212- < Popover
213- { ...popoverProps }
214- body = {
215- ( < >
216- { validationState &&
182+ return (
183+ < div
184+ { ...rest }
185+ className = { className }
186+ ref = { ref } >
187+ < Popover
188+ { ...popoverProps }
189+ body = {
190+ ( < >
191+ { validationState &&
217192 < FormMessage
218193 { ...validationOverlayProps ?. formMessageProps }
219194 type = { validationState . state } >
220195 { validationState . text }
221196 </ FormMessage >
222- }
223- { popoverBody }
224- </ > ) }
225- control = { wrappedInputGroup }
226- disableKeyPressHandler
227- disabled = { readOnly }
228- noArrow
229- onClickOutside = { this . handleClickOutside }
230- widthSizingType = 'minTarget' />
231- </ div >
232- ) ;
233- }
234- }
197+ }
198+ { popoverBody }
199+ </ > ) }
200+ control = { wrappedInputGroup }
201+ disableKeyPressHandler
202+ disabled = { readOnly }
203+ noArrow
204+ onClickOutside = { handleClickOutside }
205+ widthSizingType = 'minTarget' />
206+ </ div >
207+ ) ;
208+ } ) ;
235209
236210SearchInput . displayName = 'SearchInput' ;
237211
@@ -294,7 +268,6 @@ SearchInput.propTypes = {
294268 /** Text of the validation message */
295269 text : PropTypes . string
296270 } ) ,
297- /** Callback function when the change event fires on the component */
298271 /**
299272 * Callback function; triggered when a change event is fired on the underlying `<input>`.
300273 *
0 commit comments