@@ -6,7 +6,7 @@ import classNames from 'classnames';
6
6
import Mousetrap from 'mousetrap' ;
7
7
8
8
import VisuallyHidden from '@reach/visually-hidden' ;
9
- import ReactAutocomplete from 'react-autocomplete ' ;
9
+ import Autosuggest from 'react-autosuggest ' ;
10
10
11
11
import { urlHandler } from '../../utils' ;
12
12
import { BaseComponent } from '../base-component' ;
@@ -20,26 +20,32 @@ class Search extends BaseComponent {
20
20
this . useShadow = false ;
21
21
this . defaultMaxResults = 10 ;
22
22
23
+ // Autosuggest is a controlled component.
24
+ // This means that you need to provide an input value
25
+ // and an onChange handler that updates this value (see below).
26
+ // Suggestions also need to be provided to the Autosuggest,
27
+ // and they are initially empty because the Autosuggest is closed.
23
28
this . state = {
24
- isOpen : false ,
25
29
value : '' ,
30
+ suggestions : [ ] ,
26
31
} ;
27
32
28
33
this . receiveIframeMessage = this . receiveIframeMessage . bind ( this ) ;
34
+ this . onChange = this . onChange . bind ( this ) ;
29
35
this . toggleSearch = this . toggleSearch . bind ( this ) ;
30
- this . clearSearch = this . clearSearch . bind ( this ) ;
36
+ // this.clearSearch = this.clearSearch.bind(this);
31
37
this . closeSearch = this . closeSearch . bind ( this ) ;
32
38
this . openSearch = this . openSearch . bind ( this ) ;
33
39
34
- this . data = [ ] ;
40
+ this . items = [ ] ;
35
41
for ( const patternType in window . patternPaths ) {
36
42
if ( window . patternPaths . hasOwnProperty ( patternType ) ) {
37
43
for ( const pattern in window . patternPaths [ patternType ] ) {
38
44
if ( window . patternPaths [ patternType ] . hasOwnProperty ( pattern ) ) {
39
45
const obj = { } ;
40
46
obj . label = patternType + '-' + pattern ;
41
47
obj . id = window . patternPaths [ patternType ] [ pattern ] ;
42
- this . data . push ( obj ) ;
48
+ this . items . push ( obj ) ;
43
49
}
44
50
}
45
51
}
@@ -58,7 +64,7 @@ class Search extends BaseComponent {
58
64
}
59
65
60
66
rendered ( ) {
61
- this . inputElement = this . input ;
67
+ this . inputElement = this . querySelector ( '.js-c-typeahead__input' ) ;
62
68
}
63
69
64
70
static props = {
@@ -71,19 +77,6 @@ class Search extends BaseComponent {
71
77
// External Redux store not yet in use
72
78
_stateChanged ( state ) { }
73
79
74
- // update the iframe via the history api handler
75
- passPath ( item ) {
76
- this . setState ( { value : item } ) ;
77
- this . closeSearch ( ) ;
78
- const obj = JSON . stringify ( {
79
- event : 'patternLab.updatePath' ,
80
- path : urlHandler . getFileName ( item ) ,
81
- } ) ;
82
- document
83
- . querySelector ( '.pl-js-iframe' )
84
- . contentWindow . postMessage ( obj , urlHandler . targetOrigin ) ;
85
- }
86
-
87
80
toggleSearch ( ) {
88
81
if ( ! this . state . isOpen ) {
89
82
this . openSearch ( ) ;
@@ -131,10 +124,14 @@ class Search extends BaseComponent {
131
124
}
132
125
}
133
126
134
- // highlights keywords in the search results in a react-friendly way + limits total number / max displayed
135
- filterAndLimitResults ( ) {
136
- const data = this . data ;
127
+ getSuggestionValue = suggestion => suggestion . label ;
137
128
129
+ renderSuggestion ( item , { query, isHighlighted } ) {
130
+ return < span > { item . highlightedLabel } </ span > ;
131
+ }
132
+
133
+ // highlights keywords in the search results in a react-friendly way + limits total number / max displayed
134
+ getSuggestions ( value ) {
138
135
const maxResults = this . props . maxResults
139
136
? this . props . maxResults
140
137
: this . defaultMaxResults ;
@@ -150,8 +147,8 @@ class Search extends BaseComponent {
150
147
minMatchCharLength : 1 ,
151
148
keys : [ 'label' ] ,
152
149
} ;
153
- const fuse = new Fuse ( data , fuseOptions ) ;
154
- const results = fuse . search ( this . state . value ? this . state . value : '' ) ;
150
+ const fuse = new Fuse ( this . items , fuseOptions ) ;
151
+ const results = fuse . search ( value ) ;
155
152
156
153
const highlighter = function ( item ) {
157
154
const resultItem = item ;
@@ -202,9 +199,51 @@ class Search extends BaseComponent {
202
199
}
203
200
}
204
201
202
+ // Autosuggest calls this when a search result is selected
203
+ onChange = ( event , { newValue } ) => {
204
+ const patternName = urlHandler . getFileName ( newValue ) ;
205
+
206
+ if ( patternName ) {
207
+ const obj = JSON . stringify ( {
208
+ event : 'patternLab.updatePath' ,
209
+ path : patternName ,
210
+ } ) ;
211
+
212
+ document
213
+ . querySelector ( '.pl-js-iframe' )
214
+ . contentWindow . postMessage ( obj , urlHandler . targetOrigin ) ;
215
+ }
216
+
217
+ this . setState ( {
218
+ value : newValue ,
219
+ } ) ;
220
+ } ;
221
+
222
+ // Autosuggest calls this every time you need to update suggestions.
223
+ onSuggestionsFetchRequested = ( { value } ) => {
224
+ this . setState ( { isOpen : true } ) ;
225
+
226
+ this . setState ( {
227
+ suggestions : this . getSuggestions ( value ) ,
228
+ } ) ;
229
+ } ;
230
+
231
+ // Autosuggest calls this every time you need to clear suggestions.
232
+ onSuggestionsClearRequested = ( ) => {
233
+ this . setState ( {
234
+ suggestions : [ ] ,
235
+ } ) ;
236
+
237
+ this . setState ( { isOpen : false } ) ;
238
+ } ;
239
+
240
+ onSuggestionSelected ( ) {
241
+ this . setState ( { isOpen : false } ) ;
242
+ }
243
+
205
244
render ( ) {
206
- const open = this . state . isOpen ;
207
- const currentValue = this . state . value ;
245
+ const { value , suggestions } = this . state ;
246
+
208
247
const shouldShowClearButton = this . props . showClearButton
209
248
? this . props . showClearButton
210
249
: true ;
@@ -213,61 +252,54 @@ class Search extends BaseComponent {
213
252
? this . props . clearButtonText
214
253
: 'Clear Search Results' ;
215
254
255
+ // no CSS for these Autosuggest selectors yet -- not yet needed
256
+ const theme = {
257
+ container : classNames ( 'pl-c-typeahead' ) ,
258
+ containerOpen : classNames ( 'pl-c-typeahead--open' ) ,
259
+ input : classNames ( 'pl-c-typeahead__input' , 'js-c-typeahead__input' , {
260
+ [ `pl-c-typeahead__input--with-clear-button` ] : shouldShowClearButton ,
261
+ } ) ,
262
+ inputOpen : classNames ( 'pl-c-typeahead__input--open' ) ,
263
+ inputFocused : classNames ( 'pl-c-typeahead__input--focused' ) ,
264
+ suggestionsContainer : classNames ( 'pl-c-typeahead__menu' ) ,
265
+ suggestionsContainerOpen : classNames ( 'pl-is-open' ) ,
266
+ suggestionsList : classNames ( 'pl-c-typeahead__results' ) ,
267
+ suggestion : classNames ( 'pl-c-typeahead__result' ) ,
268
+ suggestionFirst : classNames ( 'pl-c-typeahead__result--first' ) ,
269
+ suggestionHighlighted : classNames ( 'pl-has-cursor' ) ,
270
+ sectionContainer : classNames (
271
+ 'pl-c-typeahead__section-container'
272
+ ) /* [1] */ ,
273
+ sectionContainerFirst : classNames (
274
+ 'pl-c-typeahead__section-container--first'
275
+ ) /* [1] */ ,
276
+ sectionTitle : classNames ( 'pl-c-typeahead__section-title' ) /* [1] */ ,
277
+ } ;
278
+
279
+ // Autosuggest will pass through all these props to the input.
280
+ const inputProps = {
281
+ placeholder : this . props . placeholder
282
+ ? this . props . placeholder
283
+ : 'Find a Pattern' ,
284
+ value,
285
+ onChange : this . onChange ,
286
+ } ;
287
+
216
288
return (
217
- < div className = { 'pl-c-typeahead-wrapper' } >
218
- < ReactAutocomplete
219
- items = { this . filterAndLimitResults ( ) }
220
- ref = { el => ( this . input = el ) }
221
- wrapperProps = { {
222
- className : classNames ( 'pl-c-typeahead' ) ,
223
- } }
224
- //setting autoHighlight to false seems to help prevent an occasional JS error from firing (relating to the dom-scroll-into-view library)
225
- autoHighlight = { false }
226
- onMenuVisibilityChange = { isOpen => this . setState ( { isOpen } ) }
227
- getItemValue = { item => item . label }
228
- renderItem = { ( item , highlighted ) => (
229
- < div
230
- className = { classNames ( 'pl-c-typeahead__result' , {
231
- [ `pl-has-cursor` ] : highlighted ,
232
- } ) }
233
- key = { item . id }
234
- >
235
- { item . highlightedLabel }
236
- </ div >
237
- ) }
238
- inputProps = { {
239
- className : classNames ( 'pl-c-typeahead__input' , {
240
- [ `pl-c-typeahead__input--with-clear-button` ] : shouldShowClearButton ,
241
- } ) ,
242
- placeholder : this . props . placeholder
243
- ? this . props . placeholder
244
- : 'Find a Pattern' ,
245
- } }
246
- renderMenu = { ( items , value , style ) => (
247
- < div
248
- className = { classNames ( 'pl-c-typeahead__menu' , {
249
- [ `pl-is-open` ] : open ,
250
- } ) }
251
- >
252
- < div
253
- className = { 'pl-c-typeahead__results' }
254
- style = { { ...style , ...this . menuStyle } }
255
- children = { items }
256
- />
257
- </ div >
258
- ) }
259
- value = { this . state . value }
260
- onChange = { e => {
261
- e . target . value !== '' && e . target . value !== undefined
262
- ? this . setState ( { value : e . target . value } )
263
- : this . setState ( { value : '' } ) ;
264
- } }
265
- onSelect = { value => this . passPath ( value ) }
289
+ < div className = { classNames ( 'pl-c-typeahead-wrapper' ) } >
290
+ < Autosuggest
291
+ theme = { theme }
292
+ suggestions = { suggestions }
293
+ onSuggestionsFetchRequested = { this . onSuggestionsFetchRequested }
294
+ onSuggestionsClearRequested = { this . onSuggestionsClearRequested }
295
+ getSuggestionValue = { this . getSuggestionValue }
296
+ renderSuggestion = { this . renderSuggestion }
297
+ inputProps = { inputProps }
266
298
/>
267
299
{ shouldShowClearButton && (
268
300
< button
269
301
className = { classNames ( 'pl-c-typeahead__clear-button' , {
270
- [ `pl-is-visible` ] : currentValue !== '' ,
302
+ [ `pl-is-visible` ] : value !== '' ,
271
303
} ) }
272
304
onClick = { ( ) => {
273
305
this . clearSearch ( ) ;
@@ -280,7 +312,7 @@ class Search extends BaseComponent {
280
312
width = "16"
281
313
className = { 'pl-c-typeahead__clear-button-icon' }
282
314
>
283
- < title > Clear Search Results </ title >
315
+ < title > { clearButtonText } </ title >
284
316
< path d = "M12.207 10.793l-1.414 1.414-2.793-2.793-2.793 2.793-1.414-1.414 2.793-2.793-2.793-2.793 1.414-1.414 2.793 2.793 2.793-2.793 1.414 1.414-2.793 2.793 2.793 2.793z" />
285
317
</ svg >
286
318
</ button >
0 commit comments