8
8
ShorthandRenderFunction ,
9
9
ShorthandValue ,
10
10
ComponentEventHandler ,
11
+ ShorthandCollection ,
11
12
} from '../../types'
12
13
import { ComponentSlotStylesInput , ComponentVariablesInput } from '../../themes/types'
13
14
import Downshift , {
@@ -29,7 +30,7 @@ import {
29
30
UIComponentProps ,
30
31
} from '../../lib'
31
32
import keyboardKey from 'keyboard-key'
32
- import Indicator from '../Indicator/Indicator'
33
+ import Indicator , { IndicatorProps } from '../Indicator/Indicator'
33
34
import List from '../List/List'
34
35
import Ref from '../Ref/Ref'
35
36
import DropdownItem from './DropdownItem'
@@ -38,9 +39,11 @@ import DropdownSearchInput, { DropdownSearchInputProps } from './DropdownSearchI
38
39
import Button from '../Button/Button'
39
40
import { screenReaderContainerStyles } from '../../lib/accessibility/Styles/accessibilityStyles'
40
41
import ListItem from '../List/ListItem'
42
+ import Icon , { IconProps } from '../Icon/Icon'
41
43
42
44
export interface DropdownSlotClassNames {
43
45
container : string
46
+ clearIndicator : string
44
47
triggerButton : string
45
48
itemsList : string
46
49
selectedItems : string
@@ -50,14 +53,20 @@ export interface DropdownProps extends UIComponentProps<DropdownProps, DropdownS
50
53
/** The index of the currently active selected item, if dropdown has a multiple selection. */
51
54
activeSelectedIndex ?: number
52
55
56
+ /** A dropdown can be clearable and let users remove their selection. */
57
+ clearable ?: boolean
58
+
59
+ /** A slot for a clearing indicator. */
60
+ clearIndicator ?: ShorthandValue
61
+
53
62
/** The initial value for the index of the currently active selected item, in a multiple selection. */
54
63
defaultActiveSelectedIndex ?: number
55
64
56
65
/** The initial value for the search query, if the dropdown is also a search. */
57
66
defaultSearchQuery ?: string
58
67
59
68
/** The initial value or value array, if the array has multiple selection. */
60
- defaultValue ?: ShorthandValue | ShorthandValue [ ]
69
+ defaultValue ?: ShorthandValue | ShorthandCollection
61
70
62
71
/** A dropdown can take the width of its container. */
63
72
fluid ?: boolean
@@ -86,7 +95,7 @@ export interface DropdownProps extends UIComponentProps<DropdownProps, DropdownS
86
95
inline ?: boolean
87
96
88
97
/** Array of props for generating list options (Dropdown.Item[]) and selected item labels(Dropdown.SelectedItem[]), if it's a multiple selection. */
89
- items ?: ShorthandValue [ ]
98
+ items ?: ShorthandCollection
90
99
91
100
/**
92
101
* Function to be passed to create string from selected item, if it's a shorthand object. Used when dropdown also has a search function.
@@ -141,7 +150,7 @@ export interface DropdownProps extends UIComponentProps<DropdownProps, DropdownS
141
150
renderSelectedItem ?: ShorthandRenderFunction
142
151
143
152
/** A dropdown can have a search field instead of trigger button. Can receive a custom search function that will replace the default equivalent. */
144
- search ?: boolean | ( ( items : ShorthandValue [ ] , searchQuery : string ) => ShorthandValue [ ] )
153
+ search ?: boolean | ( ( items : ShorthandCollection , searchQuery : string ) => ShorthandCollection )
145
154
146
155
/** Component for the search input query. */
147
156
searchInput ?: ShorthandValue
@@ -156,7 +165,7 @@ export interface DropdownProps extends UIComponentProps<DropdownProps, DropdownS
156
165
triggerButton ?: ShorthandValue
157
166
158
167
/** Sets currently selected value(s) (controlled mode). */
159
- value ?: ShorthandValue | ShorthandValue [ ]
168
+ value ?: ShorthandValue | ShorthandCollection
160
169
}
161
170
162
171
export interface DropdownState {
@@ -165,7 +174,7 @@ export interface DropdownState {
165
174
focused : boolean
166
175
isOpen ?: boolean
167
176
searchQuery ?: string
168
- value : ShorthandValue | ShorthandValue [ ]
177
+ value : ShorthandValue | ShorthandCollection
169
178
}
170
179
171
180
/**
@@ -192,6 +201,8 @@ class Dropdown extends AutoControlledComponent<Extendable<DropdownProps>, Dropdo
192
201
content : false ,
193
202
} ) ,
194
203
activeSelectedIndex : PropTypes . number ,
204
+ clearable : PropTypes . bool ,
205
+ clearIndicator : customPropTypes . itemShorthand ,
195
206
defaultActiveSelectedIndex : PropTypes . number ,
196
207
defaultSearchQuery : PropTypes . string ,
197
208
defaultValue : PropTypes . oneOfType ( [
@@ -226,6 +237,7 @@ class Dropdown extends AutoControlledComponent<Extendable<DropdownProps>, Dropdo
226
237
227
238
static defaultProps : DropdownProps = {
228
239
as : 'div' ,
240
+ clearIndicator : 'close' ,
229
241
itemToString : item => {
230
242
if ( ! item || React . isValidElement ( item ) ) {
231
243
return ''
@@ -263,8 +275,16 @@ class Dropdown extends AutoControlledComponent<Extendable<DropdownProps>, Dropdo
263
275
unhandledProps,
264
276
rtl,
265
277
} : RenderResultConfig < DropdownProps > ) {
266
- const { search, multiple, getA11yStatusMessage, itemToString, toggleIndicator } = this . props
267
- const { defaultHighlightedIndex, searchQuery } = this . state
278
+ const {
279
+ clearable,
280
+ clearIndicator,
281
+ search,
282
+ multiple,
283
+ getA11yStatusMessage,
284
+ itemToString,
285
+ toggleIndicator,
286
+ } = this . props
287
+ const { defaultHighlightedIndex, searchQuery, value } = this . state
268
288
269
289
return (
270
290
< ElementType className = { classes . root } { ...unhandledProps } >
@@ -293,6 +313,8 @@ class Dropdown extends AutoControlledComponent<Extendable<DropdownProps>, Dropdo
293
313
{ refKey : 'innerRef' } ,
294
314
{ suppressRefError : true } ,
295
315
)
316
+ const showClearIndicator = clearable && ! this . isValueEmpty ( value )
317
+
296
318
return (
297
319
< Ref innerRef = { innerRef } >
298
320
< div
@@ -315,13 +337,32 @@ class Dropdown extends AutoControlledComponent<Extendable<DropdownProps>, Dropdo
315
337
)
316
338
: this . renderTriggerButton ( styles , rtl , getToggleButtonProps ) }
317
339
</ div >
318
- { Indicator . create ( toggleIndicator , {
319
- defaultProps : {
320
- direction : isOpen ? 'top' : 'bottom' ,
321
- onClick : getToggleButtonProps ( ) . onClick ,
322
- styles : styles . toggleIndicator ,
323
- } ,
324
- } ) }
340
+ { showClearIndicator
341
+ ? Icon . create ( clearIndicator , {
342
+ defaultProps : {
343
+ className : Dropdown . slotClassNames . clearIndicator ,
344
+ styles : styles . clearIndicator ,
345
+ xSpacing : 'none' ,
346
+ } ,
347
+ overrideProps : ( predefinedProps : IconProps ) => ( {
348
+ onClick : ( e , iconProps : IconProps ) => {
349
+ _ . invoke ( predefinedProps , 'onClick' , e , iconProps )
350
+ this . handleClear ( )
351
+ } ,
352
+ } ) ,
353
+ } )
354
+ : Indicator . create ( toggleIndicator , {
355
+ defaultProps : {
356
+ direction : isOpen ? 'top' : 'bottom' ,
357
+ styles : styles . toggleIndicator ,
358
+ } ,
359
+ overrideProps : ( predefinedProps : IndicatorProps ) => ( {
360
+ onClick : ( e , indicatorProps : IndicatorProps ) => {
361
+ _ . invoke ( predefinedProps , 'onClick' , e , indicatorProps )
362
+ getToggleButtonProps ( ) . onClick ( e )
363
+ } ,
364
+ } ) ,
365
+ } ) }
325
366
{ this . renderItemsList (
326
367
styles ,
327
368
variables ,
@@ -392,7 +433,7 @@ class Dropdown extends AutoControlledComponent<Extendable<DropdownProps>, Dropdo
392
433
const { searchQuery, value } = this . state
393
434
394
435
const noPlaceholder =
395
- searchQuery . length > 0 || ( multiple && ( value as ShorthandValue [ ] ) . length > 0 )
436
+ searchQuery . length > 0 || ( multiple && ( value as ShorthandCollection ) . length > 0 )
396
437
397
438
return DropdownSearchInput . create ( searchInput || { } , {
398
439
defaultProps : {
@@ -510,7 +551,7 @@ class Dropdown extends AutoControlledComponent<Extendable<DropdownProps>, Dropdo
510
551
511
552
private renderSelectedItems ( variables , rtl : boolean ) {
512
553
const { renderSelectedItem } = this . props
513
- const value = this . state . value as ShorthandValue [ ]
554
+ const value = this . state . value as ShorthandCollection
514
555
515
556
if ( value . length === 0 ) {
516
557
return null
@@ -570,10 +611,10 @@ class Dropdown extends AutoControlledComponent<Extendable<DropdownProps>, Dropdo
570
611
}
571
612
}
572
613
573
- private getItemsFilteredBySearchQuery = ( ) : ShorthandValue [ ] => {
614
+ private getItemsFilteredBySearchQuery = ( ) : ShorthandCollection => {
574
615
const { items, itemToString, multiple, search } = this . props
575
616
const { searchQuery, value } = this . state
576
- const filteredItems = multiple ? _ . difference ( items , value as ShorthandValue [ ] ) : items
617
+ const filteredItems = multiple ? _ . difference ( items , value as ShorthandCollection ) : items
577
618
578
619
if ( search ) {
579
620
if ( _ . isFunction ( search ) ) {
@@ -627,7 +668,7 @@ class Dropdown extends AutoControlledComponent<Extendable<DropdownProps>, Dropdo
627
668
this . handleSelectedItemRemove ( e , item , predefinedProps , DropdownSelectedItemProps )
628
669
} ,
629
670
onClick : ( e : React . SyntheticEvent , DropdownSelectedItemProps : DropdownSelectedItemProps ) => {
630
- const { value } = this . state as { value : ShorthandValue [ ] }
671
+ const { value } = this . state as { value : ShorthandCollection }
631
672
this . trySetState ( {
632
673
activeSelectedIndex : value . indexOf ( item ) ,
633
674
} )
@@ -729,7 +770,7 @@ class Dropdown extends AutoControlledComponent<Extendable<DropdownProps>, Dropdo
729
770
) {
730
771
return
731
772
}
732
- const { value } = this . state as { value : ShorthandValue [ ] }
773
+ const { value } = this . state as { value : ShorthandCollection }
733
774
if ( value . length > 0 ) {
734
775
this . trySetState ( { activeSelectedIndex : value . length - 1 } )
735
776
}
@@ -742,12 +783,21 @@ class Dropdown extends AutoControlledComponent<Extendable<DropdownProps>, Dropdo
742
783
if (
743
784
multiple &&
744
785
( searchQuery === '' || this . inputRef . current . selectionStart === 0 ) &&
745
- ( value as ShorthandValue [ ] ) . length > 0
786
+ ( value as ShorthandCollection ) . length > 0
746
787
) {
747
788
this . removeItemFromValue ( )
748
789
}
749
790
}
750
791
792
+ private handleClear = ( ) => {
793
+ const initialState = this . getInitialAutoControlledState ( this . props )
794
+
795
+ this . setState ( { value : initialState . value } )
796
+
797
+ this . tryFocusSearchInput ( )
798
+ this . tryFocusTriggerButton ( )
799
+ }
800
+
751
801
private handleContainerClick = ( ) => {
752
802
this . tryFocusSearchInput ( )
753
803
}
@@ -797,7 +847,7 @@ class Dropdown extends AutoControlledComponent<Extendable<DropdownProps>, Dropdo
797
847
private handleSelectedChange = ( item : ShorthandValue ) => {
798
848
const { items, multiple, getA11ySelectionMessage } = this . props
799
849
const newState = {
800
- value : multiple ? [ ...( this . state . value as ShorthandValue [ ] ) , item ] : item ,
850
+ value : multiple ? [ ...( this . state . value as ShorthandCollection ) , item ] : item ,
801
851
searchQuery : this . getSelectedItemAsString ( item ) ,
802
852
}
803
853
@@ -834,7 +884,7 @@ class Dropdown extends AutoControlledComponent<Extendable<DropdownProps>, Dropdo
834
884
) {
835
885
const { activeSelectedIndex, value } = this . state as {
836
886
activeSelectedIndex : number
837
- value : ShorthandValue [ ]
887
+ value : ShorthandCollection
838
888
}
839
889
const previousKey = rtl ? keyboardKey . ArrowRight : keyboardKey . ArrowLeft
840
890
const nextKey = rtl ? keyboardKey . ArrowLeft : keyboardKey . ArrowRight
@@ -894,7 +944,7 @@ class Dropdown extends AutoControlledComponent<Extendable<DropdownProps>, Dropdo
894
944
895
945
private removeItemFromValue ( item ?: ShorthandValue ) {
896
946
const { getA11ySelectionMessage } = this . props
897
- let value = this . state . value as ShorthandValue [ ]
947
+ let value = this . state . value as ShorthandCollection
898
948
let poppedItem = item
899
949
900
950
if ( poppedItem ) {
@@ -932,9 +982,8 @@ class Dropdown extends AutoControlledComponent<Extendable<DropdownProps>, Dropdo
932
982
*/
933
983
private getSelectedItemAsString = ( value : ShorthandValue ) : string => {
934
984
const { itemToString, multiple, placeholder } = this . props
935
- const isValueEmpty = _ . isArray ( value ) ? value . length < 1 : ! value
936
985
937
- if ( isValueEmpty ) {
986
+ if ( this . isValueEmpty ( value ) ) {
938
987
return placeholder
939
988
}
940
989
@@ -944,10 +993,15 @@ class Dropdown extends AutoControlledComponent<Extendable<DropdownProps>, Dropdo
944
993
945
994
return itemToString ( value )
946
995
}
996
+
997
+ private isValueEmpty = ( value : ShorthandValue | ShorthandCollection ) => {
998
+ return _ . isArray ( value ) ? value . length < 1 : ! value
999
+ }
947
1000
}
948
1001
949
1002
Dropdown . slotClassNames = {
950
1003
container : `${ Dropdown . className } __container` ,
1004
+ clearIndicator : `${ Dropdown . className } __clear-indicator` ,
951
1005
triggerButton : `${ Dropdown . className } __trigger-button` ,
952
1006
itemsList : `${ Dropdown . className } __items-list` ,
953
1007
selectedItems : `${ Dropdown . className } __selected-items` ,
0 commit comments