diff --git a/packages/ui/src/elements/WhereBuilder/Condition/index.tsx b/packages/ui/src/elements/WhereBuilder/Condition/index.tsx index 20ca8ed2e1..7cec20c6d6 100644 --- a/packages/ui/src/elements/WhereBuilder/Condition/index.tsx +++ b/packages/ui/src/elements/WhereBuilder/Condition/index.tsx @@ -26,8 +26,9 @@ import { useEffectEvent } from '../../../hooks/useEffectEvent.js' import { useTranslation } from '../../../providers/Translation/index.js' import { Button } from '../../Button/index.js' import { ReactSelect } from '../../ReactSelect/index.js' -import './index.scss' import { DefaultFilter } from './DefaultFilter/index.js' +import { getOperatorValueTypes } from './validOperators.js' +import './index.scss' const baseClass = 'condition' @@ -103,12 +104,25 @@ export const Condition: React.FC = (props) => { const handleOperatorChange = useCallback( async (operator: Option) => { + const operatorValueTypes = getOperatorValueTypes(reducedField.field.type) + const validOperatorValue = operatorValueTypes[operator.value] || 'any' + const isValidValue = + validOperatorValue === 'any' || + typeof value === validOperatorValue || + (validOperatorValue === 'boolean' && (value === 'true' || value === 'false')) + + if (!isValidValue) { + // if the current value is not valid for the new operator + // reset the value before passing it to updateCondition + setInternalValue(undefined) + } + await updateCondition({ andIndex, field: reducedField, operator: operator.value, orIndex, - value, + value: isValidValue ? value : undefined, }) }, [andIndex, reducedField, orIndex, updateCondition, value], diff --git a/packages/ui/src/elements/WhereBuilder/Condition/validOperators.ts b/packages/ui/src/elements/WhereBuilder/Condition/validOperators.ts new file mode 100644 index 0000000000..0c0672416c --- /dev/null +++ b/packages/ui/src/elements/WhereBuilder/Condition/validOperators.ts @@ -0,0 +1,34 @@ +export const getOperatorValueTypes = (fieldType) => { + return { + all: 'any', + contains: 'string', + equals: 'any', + /* + * exists: + * The expected value is boolean, but it's passed as a string ('true' or 'false'). + * Need to additionally check if the value is strictly 'true' or 'false' as a string, + * rather than using a direct typeof comparison. + * This is handled as: + * validOperatorValue === 'boolean' && (value === 'true' || value === 'false') + */ + exists: 'boolean', + /* + * greater_than, greater_than_equal, less_than, less_than_equal: + * Used for number and date fields: + * - For date fields, the value is an object (e.g., Mon Feb 17 2025 12:00:00 GMT+0000). + * - For number fields, the value is a string representing the number. + */ + greater_than: fieldType === 'date' ? 'object' : 'string', + greater_than_equal: fieldType === 'date' ? 'object' : 'string', + in: 'any', + intersects: 'any', + less_than: fieldType === 'date' ? 'object' : 'string', + less_than_equal: fieldType === 'date' ? 'object' : 'string', + like: 'string', + near: 'any', + not_equals: 'any', + not_in: 'any', + not_like: 'string', + within: 'any', + } +} diff --git a/test/admin/e2e/list-view/e2e.spec.ts b/test/admin/e2e/list-view/e2e.spec.ts index 567a7fdfd7..125cbda93d 100644 --- a/test/admin/e2e/list-view/e2e.spec.ts +++ b/test/admin/e2e/list-view/e2e.spec.ts @@ -413,7 +413,6 @@ describe('List View', () => { await expect(whereBuilder.locator('.condition__value input')).toHaveValue('') }) - // eslint-disable-next-line playwright/expect-expect test('should remove condition from URL when value is cleared', async () => { await page.goto(postsUrl.list)