diff --git a/packages/payload/src/admin/components/elements/WhereBuilder/field-types.tsx b/packages/payload/src/admin/components/elements/WhereBuilder/field-types.tsx index 55598af862..9c95045263 100644 --- a/packages/payload/src/admin/components/elements/WhereBuilder/field-types.tsx +++ b/packages/payload/src/admin/components/elements/WhereBuilder/field-types.tsx @@ -77,6 +77,15 @@ const contains = { value: 'contains', } +const filterOperators = (operators, hasMany = false) => { + if (hasMany) { + return operators.filter( + (operator) => operator.value !== 'equals' && operator.value !== 'not_equals', + ) + } + return operators +} + const fieldTypeConditions = { checkbox: { component: 'Text', @@ -100,7 +109,7 @@ const fieldTypeConditions = { }, number: { component: 'Number', - operators: [...base, ...numeric], + operators: (hasMany) => filterOperators([...base, ...numeric], hasMany), }, point: { component: 'Point', @@ -120,11 +129,11 @@ const fieldTypeConditions = { }, select: { component: 'Select', - operators: [...base], + operators: (hasMany) => filterOperators([...base], hasMany), }, text: { component: 'Text', - operators: [...base, like, contains], + operators: (hasMany) => filterOperators([...base, like, contains], hasMany), }, textarea: { component: 'Text', diff --git a/packages/payload/src/admin/components/elements/WhereBuilder/index.tsx b/packages/payload/src/admin/components/elements/WhereBuilder/index.tsx index f1c46abecd..df1919486b 100644 --- a/packages/payload/src/admin/components/elements/WhereBuilder/index.tsx +++ b/packages/payload/src/admin/components/elements/WhereBuilder/index.tsx @@ -22,36 +22,44 @@ const baseClass = 'where-builder' const reduceFields = (fields, i18n) => flattenTopLevelFields(fields).reduce((reduced, field) => { + let operators = [] + if (typeof fieldTypes[field.type] === 'object') { - const operatorKeys = new Set() - const operators = fieldTypes[field.type].operators.reduce((acc, operator) => { - if (!operatorKeys.has(operator.value)) { - operatorKeys.add(operator.value) - return [ - ...acc, - { - ...operator, - label: i18n.t(`operators:${operator.label}`), - }, - ] - } - return acc - }, []) - - const formattedField = { - label: getTranslation(field.label || field.name, i18n), - value: field.name, - ...fieldTypes[field.type], - operators, - props: { - ...field, - }, + if (typeof fieldTypes[field.type].operators === 'function') { + operators = fieldTypes[field.type].operators( + 'hasMany' in field && field.hasMany ? true : false, + ) + } else { + operators = fieldTypes[field.type].operators } + } - return [...reduced, formattedField] + const operatorKeys = new Set() + const filteredOperators = operators.reduce((acc, operator) => { + if (!operatorKeys.has(operator.value)) { + operatorKeys.add(operator.value) + return [ + ...acc, + { + ...operator, + label: i18n.t(`operators:${operator.label}`), + }, + ] + } + return acc + }, []) + + const formattedField = { + label: getTranslation(field.label || field.name, i18n), + value: field.name, + ...fieldTypes[field.type], + operators: filteredOperators, + props: { + ...field, + }, } - return reduced + return [...reduced, formattedField] }, []) /** @@ -185,7 +193,7 @@ const WhereBuilder: React.FC = (props) => { iconStyle="with-border" onClick={() => { if (reducedFields.length > 0) - dispatchConditions({ field: reducedFields[0].value, type: 'add' }) + dispatchConditions({ type: 'add', field: reducedFields[0].value }) }} > {t('or')} @@ -203,7 +211,7 @@ const WhereBuilder: React.FC = (props) => { iconStyle="with-border" onClick={() => { if (reducedFields.length > 0) - dispatchConditions({ field: reducedFields[0].value, type: 'add' }) + dispatchConditions({ type: 'add', field: reducedFields[0].value }) }} > {t('addFilter')} diff --git a/test/fields/e2e.spec.ts b/test/fields/e2e.spec.ts index cc3bd9f62f..9f2ebb45f2 100644 --- a/test/fields/e2e.spec.ts +++ b/test/fields/e2e.spec.ts @@ -355,6 +355,29 @@ describe('fields', () => { await saveDocAndAssert(page) await expect(field.locator('.rs__value-container')).toContainText('One') }) + + test('should not allow filtering by hasMany field / equals / not equals', async () => { + await page.goto(url.list) + + await page.locator('.list-controls__toggle-columns').click() + await page.locator('.list-controls__toggle-where').click() + await page.waitForSelector('.list-controls__where.rah-static--height-auto') + await page.locator('.where-builder__add-first-filter').click() + + const conditionField = page.locator('.condition__field') + await conditionField.click() + + const dropdownFieldOptions = conditionField.locator('.rs__option') + await dropdownFieldOptions.locator('text=Select Has Many').nth(0).click() + + const operatorField = page.locator('.condition__operator') + await operatorField.click() + + const dropdownOperatorOptions = operatorField.locator('.rs__option') + + await expect(dropdownOperatorOptions.locator('text=equals')).toBeHidden() + await expect(dropdownOperatorOptions.locator('text=not equals')).toBeHidden() + }) }) describe('point', () => {