11import MuiAutocomplete from '@mui/material/Autocomplete'
22import React , { JSX , useState } from 'react'
3-
4- import type { AutocompleteProps , AutocompleteRenderInputParams } from '@mui/material/Autocomplete'
53import ArrowDropDownIcon from '@mui/icons-material/ExpandMore'
6- import { TextField } from './TextField'
74
5+ import type {
6+ AutocompleteChangeDetails ,
7+ AutocompleteChangeReason ,
8+ AutocompleteProps ,
9+ AutocompleteRenderInputParams ,
10+ } from '@mui/material/Autocomplete'
11+ import { TextField } from './TextField'
812import type { TextFieldProps } from './TextField'
913
1014export interface EnhancedAutocompleteProps <
11- T extends { label : string } ,
15+ T ,
1216 Multiple extends boolean | undefined = undefined ,
1317 DisableClearable extends boolean | undefined = undefined ,
1418 FreeSolo extends boolean | undefined = undefined ,
@@ -19,6 +23,8 @@ export interface EnhancedAutocompleteProps<
1923 helperText ?: TextFieldProps [ 'helperText' ]
2024 /** A required label for the Autocomplete to ensure accessibility. */
2125 label : string
26+ /** Optional field to hide label, mostly used in Key value components */
27+ hideLabel ?: boolean
2228 /** Removes the top margin from the input label, if desired. */
2329 noMarginTop ?: boolean
2430 /** Element to show when the Autocomplete search yields no results. */
@@ -27,76 +33,105 @@ export interface EnhancedAutocompleteProps<
2733 renderInput ?: ( _params : AutocompleteRenderInputParams ) => React . ReactNode
2834 /** Label for the "select all" option. */
2935 selectAllLabel ?: string
36+ /** Removes the "select all" option for multiselect */
37+ disableSelectAll ?: boolean
3038 textFieldProps ?: Partial < TextFieldProps >
3139 width ?: 'small' | 'medium' | 'large'
3240}
3341
34- /**
35- * An Autocomplete component that provides a user-friendly select input
36- * allowing selection between options.
37- *
38- * @example
39- * <Autocomplete
40- * label="Select a Fruit"
41- * onSelectionChange={(selected) => console.log(selected)}
42- * options={[
43- * {
44- * label: 'Apple',
45- * value: 'apple',
46- * }
47- * ]}
48- * />
49- */
5042export function Autocomplete <
51- T extends { label : string } ,
43+ T ,
5244 Multiple extends boolean | undefined = undefined ,
5345 DisableClearable extends boolean | undefined = undefined ,
5446 FreeSolo extends boolean | undefined = undefined ,
5547> ( props : EnhancedAutocompleteProps < T , Multiple , DisableClearable , FreeSolo > ) {
5648 const {
5749 clearOnBlur,
5850 defaultValue,
59- disablePortal = true ,
51+ disablePortal = false ,
6052 errorText = '' ,
6153 helperText,
54+ hideLabel = false ,
6255 label,
6356 limitTags = 2 ,
6457 loading = false ,
6558 loadingText,
59+ multiple,
60+ disableSelectAll = false ,
6661 noOptionsText,
6762 onBlur,
6863 options,
6964 placeholder,
7065 renderInput,
66+ selectAllLabel = '' ,
7167 textFieldProps,
7268 value,
7369 onChange,
7470 width = 'medium' ,
7571 ...rest
7672 } = props
73+
7774 const [ inPlaceholder , setInPlaceholder ] = useState ( '' )
7875
76+ // --- select-all logic ---
77+ const isSelectAllActive = multiple && Array . isArray ( value ) && value . length === options . length
78+
79+ const selectAllText = isSelectAllActive ? 'Deselect All' : 'Select All'
80+ const selectAllOption = { label : `${ selectAllText } ${ selectAllLabel } ` } as unknown as T
81+ const optionsWithSelectAll = [ selectAllOption , ...options ]
82+
83+ const handleChange : AutocompleteProps < T , Multiple , DisableClearable , FreeSolo > [ 'onChange' ] = (
84+ e ,
85+ newValue ,
86+ reason : AutocompleteChangeReason ,
87+ details ?: AutocompleteChangeDetails < T > ,
88+ ) => {
89+ if ( ! onChange ) return
90+
91+ // if they clicked the "Select All" option
92+ if ( details ?. option === selectAllOption ) {
93+ const next = isSelectAllActive ? ( [ ] as unknown as typeof newValue ) : ( options as unknown as typeof newValue )
94+ onChange ( e , next , reason , details )
95+ } else onChange ( e , newValue , reason , details )
96+ }
97+ // --------------------------
98+
7999 return (
80100 < MuiAutocomplete
81- options = { options }
101+ options = { multiple && ! disableSelectAll && options . length > 0 ? optionsWithSelectAll : options }
102+ multiple = { multiple }
103+ disableCloseOnSelect = { multiple }
104+ clearOnBlur = { clearOnBlur }
105+ data-qa-autocomplete = { label }
106+ defaultValue = { defaultValue }
107+ disablePortal = { disablePortal }
108+ limitTags = { limitTags }
109+ loading = { loading }
110+ loadingText = { loadingText || 'Loading...' }
111+ noOptionsText = { noOptionsText || < i > You have no options to choose from</ i > }
112+ onBlur = { onBlur }
113+ onOpen = { ( ) => setInPlaceholder ( 'Search' ) }
114+ onClose = { ( ) => setInPlaceholder ( placeholder || '' ) }
115+ popupIcon = { < ArrowDropDownIcon /> }
82116 renderInput = {
83117 renderInput ||
84118 ( ( params ) => (
85119 < TextField
120+ hideLabel = { hideLabel }
86121 label = { label }
87122 width = { width }
88123 loading = { loading }
89- placeholder = { inPlaceholder || ( placeholder ?? 'Select an option' ) }
124+ placeholder = { inPlaceholder || placeholder || 'Select an option' }
90125 { ...params }
91126 error = { ! ! errorText }
92127 helperText = { helperText }
93128 InputProps = { {
94129 ...params . InputProps ,
95130 ...textFieldProps ?. InputProps ,
96131 sx : {
97- overflow : 'hidden ' ,
98- whiteSpace : 'nowrap ' ,
99- textOverflow : 'ellipsis' ,
132+ display : 'flex ' ,
133+ flexWrap : 'wrap ' ,
134+ gap : 1 ,
100135 paddingRight : '44px' ,
101136 } ,
102137 } }
@@ -106,21 +141,9 @@ export function Autocomplete<
106141 />
107142 ) )
108143 }
109- clearOnBlur = { clearOnBlur }
110- data-qa-autocomplete = { label }
111- defaultValue = { defaultValue }
112- disablePortal = { disablePortal }
113- limitTags = { limitTags }
114- loading = { loading }
115- loadingText = { loadingText || 'Loading...' }
116- noOptionsText = { noOptionsText || < i > You have no options to choose from</ i > }
117- onBlur = { onBlur }
118- onOpen = { ( ) => setInPlaceholder ( 'Search' ) }
119- onClose = { ( ) => setInPlaceholder ( placeholder || '' ) }
120- popupIcon = { < ArrowDropDownIcon /> }
121144 value = { value }
145+ onChange = { handleChange }
122146 { ...rest }
123- onChange = { onChange }
124147 />
125148 )
126149}
0 commit comments