@@ -9,7 +9,8 @@ import FormControl from '@mui/material/FormControl';
99import InputLabel from '@mui/material/InputLabel' ;
1010import CardActions from '@mui/material/CardActions' ;
1111import Card from '@mui/material/Card' ;
12- import { FieldModel , Filter , getOperatorsForField , searchStringToInitialFilters } from '../../utils' ;
12+ import { FieldModel , Filter , searchStringToInitialFilters } from '../../utils' ;
13+ import { OperatorKey , OperatorRegistry } from '../operators' ;
1314import KeyboardArrowDownIcon from '@mui/icons-material/KeyboardArrowDown' ;
1415import { Box , Menu } from '@mui/material' ;
1516import { styled } from '@mui/material/styles' ;
@@ -77,19 +78,22 @@ const SubmitAndExternal = styled('div')(({ theme }) => ({
7778
7879const FilterForm = ( props : FilterFormProps ) => {
7980 const { handleQuery, setFilters, handleClose, fieldTypeInfo, allowedGroupNames, promotedFilters } = props
80- const [ filters , localSetFilters ] = useState < Filter [ ] > ( searchStringToInitialFilters ( fieldTypeInfo . map ( ( x ) => x . name ) ) ) ;
81+ const initial = searchStringToInitialFilters ( fieldTypeInfo . map ( x => x . name ) )
82+ const [ filters , localSetFilters ] = useState < Filter [ ] > (
83+ initial . length ? initial : [ new Filter ( '' , OperatorKey . None , '' ) ]
84+ )
8185 const [ highlightedInputs , setHighlightedInputs ] = useState < { [ index : number ] : { field : boolean , operator : boolean , value : boolean } } > ( { } ) ;
8286 const [ commonFilterMenuOpen , setCommonFilterMenuOpen ] = useState < boolean > ( false )
8387 const buttonRef = React . useRef ( null ) ;
8488
8589 const handleAddFilter = ( ) => {
86- localSetFilters ( [ ...filters , new Filter ( ) ] ) ;
90+ localSetFilters ( [ ...filters , new Filter ( '' , OperatorKey . None , '' ) ] ) ;
8791 } ;
8892
8993 const handleRemoveFilter = ( index ) => {
9094 // If it's the last filter, just reset its values to default empty values
9195 if ( filters . length === 1 ) {
92- localSetFilters ( [ new Filter ( ) ] ) ;
96+ localSetFilters ( [ new Filter ( '' , OperatorKey . None , '' ) ] ) ;
9397 } else {
9498 // Otherwise, remove the filter normally
9599 localSetFilters (
@@ -100,64 +104,81 @@ const FilterForm = (props: FilterFormProps ) => {
100104 }
101105 } ;
102106
103- const handleFilterChange = ( index , key , value ) => {
104- const newFilters = filters . map ( ( filter , i ) => {
105- if ( i === index ) {
106- const updatedFilter = Object . assign ( new Filter ( ) , { ...filter , [ key ] : value } ) ;
107-
108- if ( key === "operator" ) {
109- if ( value === "is empty" || value === "is not empty" ) {
110- updatedFilter . value = '' ;
111- }
112-
113- if ( value === "equals one of" || filter . operator === "equals one of" ) {
114- updatedFilter . value = '' ;
115- }
116- }
107+ const handleFilterChange = (
108+ index : number ,
109+ key : 'field' | 'operator' | 'value' ,
110+ value : any
111+ ) => {
112+ const newFilters = filters . map ( ( filter , i ) => {
113+ if ( i !== index ) return filter ;
114+
115+ const updatedFilter = Object . assign (
116+ new Filter ( '' , OperatorKey . None , '' ) ,
117+ { ...filter , [ key ] : value }
118+ ) ;
119+
120+ if ( key === 'field' ) {
121+ const fieldInfo = fieldTypeInfo . find ( f => f . name === value ) ;
122+ const defaultOp = fieldInfo ?. getDefaultOperator ( ) ?? OperatorRegistry [ OperatorKey . None ] ;
123+ updatedFilter . operator = defaultOp ;
124+ updatedFilter . value = '' ;
125+ }
126+ else if ( key === 'operator' ) {
127+ updatedFilter . operator = OperatorRegistry [ value as OperatorKey ] ;
128+ updatedFilter . value = '' ;
129+ }
117130
118- return updatedFilter ;
119- }
120- return filter ;
121- } ) ;
131+ return updatedFilter ;
132+ } ) ;
122133
123- localSetFilters ( newFilters ) ;
134+ localSetFilters ( newFilters ) ;
124135 } ;
125136
137+
126138 const handleSubmit = ( event ) => {
127- event . preventDefault ( ) ;
128- const highlightedInputs = { } ;
129-
130- filters . forEach ( ( filter , index ) => {
131- highlightedInputs [ index ] = { field : false , operator : false , value : false } ;
132-
133- filter . field = filter . field ?? '' ;
134- filter . operator = filter . operator ?? '' ;
135- filter . value = filter . value ?? '' ;
136-
137- if ( filter . field === '' ) {
138- highlightedInputs [ index ] . field = true ;
139- }
140-
141- if ( filter . operator === '' ) {
142- highlightedInputs [ index ] . operator = true ;
143- }
144-
145- if ( filter . operator === 'is empty' || filter . operator === 'is not empty' ) {
146- filter . value = '' ;
147- } else if ( filter . value === '' ) {
148- highlightedInputs [ index ] . value = true ;
149- }
150- } ) ;
151-
152- const isSingleEmptyFilter = filters . length === 1 && ! filters [ 0 ] . field && ! filters [ 0 ] . operator && ! filters [ 0 ] . value ;
153-
154- setHighlightedInputs ( highlightedInputs ) ;
155- if ( isSingleEmptyFilter || ! Object . values ( highlightedInputs ) . some ( v => ( v as any ) . field || ( v as any ) . operator || ( v as any ) . value ) ) {
156- handleQuery ( filters ) ;
157- setFilters ( filters ) ;
158- handleClose ( ) ;
139+ event . preventDefault ( )
140+ const highlighted : Record < number , { field : boolean ; operator : boolean ; value : boolean } > = { }
141+
142+ filters . forEach ( ( filter , i ) => {
143+ highlighted [ i ] = { field : false , operator : false , value : false }
144+
145+ filter . field = filter . field ?? ''
146+ filter . value = filter . value ?? ''
147+
148+ if ( ! filter . field ) {
149+ highlighted [ i ] . field = true
159150 }
160- } ;
151+
152+ if ( ! filter . operator . key ) {
153+ highlighted [ i ] . operator = true
154+ }
155+
156+ if (
157+ filter . operator . key === OperatorKey . IsEmpty ||
158+ filter . operator . key === OperatorKey . IsNotEmpty
159+ ) {
160+ filter . value = ''
161+ } else if ( ! filter . value ) {
162+ highlighted [ i ] . value = true
163+ }
164+ } )
165+
166+ const isSingleEmpty =
167+ filters . length === 1 &&
168+ ! filters [ 0 ] . field &&
169+ ! filters [ 0 ] . operator . key &&
170+ ! filters [ 0 ] . value
171+
172+ setHighlightedInputs ( highlighted )
173+ if (
174+ isSingleEmpty ||
175+ ! Object . values ( highlighted ) . some ( v => v . field || v . operator || v . value )
176+ ) {
177+ handleQuery ( filters )
178+ setFilters ( filters )
179+ handleClose ?.( )
180+ }
181+ }
161182
162183 const handleMenuClose = ( ) => {
163184 setCommonFilterMenuOpen ( false )
@@ -241,34 +262,33 @@ const FilterForm = (props: FilterFormProps ) => {
241262 />
242263 </ FormControlMinWidth >
243264
244- < FormControlMinWidth sx = { highlightedInputs [ index ] ?. operator ? highlightedSx : null } >
245- < InputLabel id = "operator-label" > Operator</ InputLabel >
246- < Select
247- labelId = "operator-label"
248- label = "Operator"
249- value = { filter . operator }
250- onChange = { ( event ) =>
251- handleFilterChange ( index , "operator" , event . target . value )
252- }
253- >
254- < MenuItem value = "None" style = { { display : 'none' } } >
255- < em > None</ em >
256- </ MenuItem >
257-
258- { getOperatorsForField ( fieldTypeInfo . find ( obj => obj . name === filter . field ) ) ? (
259- getOperatorsForField ( fieldTypeInfo . find ( obj => obj . name === filter . field ) ) . map ( ( operator ) => (
260- < MenuItem key = { operator } value = { operator } >
261- { operator }
262- </ MenuItem >
263- ) )
264- ) : (
265- < MenuItem > </ MenuItem >
266- ) }
267-
268- </ Select >
265+ < FormControlMinWidth sx = { highlightedInputs [ index ] ?. operator ? highlightedSx : null } >
266+ < InputLabel id = "operator-label" > Operator</ InputLabel >
267+ < Select
268+ labelId = "operator-label"
269+ label = "Operator"
270+ value = { filter . operator . key }
271+ onChange = { event =>
272+ handleFilterChange ( index , "operator" , event . target . value as OperatorKey )
273+ }
274+ >
275+ < MenuItem value = { OperatorKey . None } >
276+ < em > None</ em >
277+ </ MenuItem >
278+ { ( ( ) => {
279+ const ops = fieldTypeInfo . find ( f => f . name === filter . field ) ?. getOperators ( ) ?? [ ] ;
280+ return ops . length > 0
281+ ? ops . map ( op => (
282+ < MenuItem key = { op . key } value = { op . key } >
283+ { op . label }
284+ </ MenuItem >
285+ ) )
286+ : < MenuItem /> ;
287+ } ) ( ) }
288+ </ Select >
269289 </ FormControlMinWidth >
270290
271- { filter . operator === "equals one of" ? (
291+ { filter . operator . key === OperatorKey . EqualsOneOf ? (
272292 < FormControlMinWidth sx = { highlightedInputs [ index ] ?. value ? highlightedSx : null } >
273293 < InputLabel id = "value-select-label" > Value</ InputLabel >
274294 < Select
@@ -288,13 +308,14 @@ const FilterForm = (props: FilterFormProps ) => {
288308 ) : fieldTypeInfo . find ( obj => obj . name === filter . field ) ?. allowableValues ?. length > 1 ? (
289309 < FormControlMinWidth sx = { highlightedInputs [ index ] ?. value ? highlightedSx : null } >
290310 < AsyncSelect
311+ key = { `async-${ index } -${ filter . operator . key } ` }
291312 id = { `value-select-${ index } ` }
292313 inputId = { `value-select-${ index } ` }
293314 aria-labelledby = { `value-select-${ index } ` }
294315 noOptionsMessage = { ( ) => 'Type to search...' }
295316 menuPortalTarget = { document . body }
296317 menuPosition = { 'fixed' }
297- isDisabled = { filter . operator === "is empty" || filter . operator === "is not empty" }
318+ isDisabled = { filter . operator . key === OperatorKey . IsEmpty || filter . operator . key === OperatorKey . IsNotEmpty }
298319 menuShouldBlockScroll = { true }
299320 // See here: https://stackoverflow.com/questions/77625507/my-react-project-with-react-18-2-0-version-and-react-select-5-4-0-v
300321 styles = { { menuPortal : ( base : any ) => ( { ...base , zIndex : 9999 } ) } }
@@ -309,8 +330,16 @@ const FilterForm = (props: FilterFormProps ) => {
309330 . map ( value => ( { label : value , value} ) )
310331 ) ;
311332 } }
312- onChange = { ( selected ) => handleFilterChange ( index , "value" , selected ?. length > 0 ? selected . map ( s => s . value ) . join ( ',' ) : undefined ) }
313- value = { filter . value ? filter . value . split ( ',' ) . map ( value => ( { label : value , value} ) ) : undefined }
333+ onChange = { ( selected ) => {
334+ const arr = Array . isArray ( selected ) ? selected : [ selected ] . filter ( Boolean )
335+ const val = arr . map ( s => s . value ) . join ( ',' )
336+ handleFilterChange ( index , 'value' , val )
337+ } }
338+ value = {
339+ filter . value
340+ ? ( filter . value as string ) . split ( ',' ) . map ( v => ( { label : v , value : v } ) )
341+ : null
342+ }
314343 />
315344 </ FormControlMinWidth >
316345 ) : fieldTypeInfo . find ( obj => obj . name === filter . field ) ?. allowableValues ?. length > 0 ? (
@@ -321,7 +350,7 @@ const FilterForm = (props: FilterFormProps ) => {
321350 label = "Value"
322351 id = { `value-select-${ index } ` }
323352 value = { filter . value }
324- disabled = { filter . operator === "is empty" || filter . operator === "is not empty" }
353+ disabled = { filter . operator . key === OperatorKey . IsEmpty || filter . operator . key === OperatorKey . IsNotEmpty }
325354 onChange = { ( event ) =>
326355 handleFilterChange ( index , "value" , event . target . value )
327356 }
@@ -340,7 +369,7 @@ const FilterForm = (props: FilterFormProps ) => {
340369 sx = { highlightedInputs [ index ] ?. value ? highlightedSx : null }
341370 variant = "outlined"
342371 value = { filter . value }
343- disabled = { filter . operator === "is empty" || filter . operator === "is not empty" }
372+ disabled = { filter . operator . key === OperatorKey . IsEmpty || filter . operator . key === OperatorKey . IsNotEmpty }
344373 onChange = { ( event ) =>
345374 handleFilterChange ( index , 'value' , event . target . value )
346375 }
0 commit comments