diff --git a/packages/core/client-v2/src/flow/models/blocks/filter-form/FilterFormItemModel.tsx b/packages/core/client-v2/src/flow/models/blocks/filter-form/FilterFormItemModel.tsx index 1f6af011491..09c304c4621 100644 --- a/packages/core/client-v2/src/flow/models/blocks/filter-form/FilterFormItemModel.tsx +++ b/packages/core/client-v2/src/flow/models/blocks/filter-form/FilterFormItemModel.tsx @@ -62,9 +62,25 @@ function isRecord(value: unknown): value is Record { return !!value && typeof value === 'object' && !Array.isArray(value); } +interface CollectionFieldWithAssociationMarker { + isAssociationField?: () => boolean; + target?: unknown; +} + +function hasAssociationMarker(value: unknown): value is CollectionFieldWithAssociationMarker { + return isRecord(value) && (typeof value.isAssociationField === 'function' || 'target' in value); +} + +const isAssociationCollectionField = (collectionField: unknown) => { + if (!hasAssociationMarker(collectionField)) { + return false; + } + return collectionField.isAssociationField ? collectionField.isAssociationField() : Boolean(collectionField.target); +}; + const normalizeAssociationDefaultFilterValue = (value: any, fieldModel: any) => { const collectionField = fieldModel?.context?.collectionField; - if (!collectionField?.isAssociationField?.()) { + if (!isAssociationCollectionField(collectionField)) { return value; } @@ -171,8 +187,7 @@ const buildFilterFormFieldItem = ({ if (!binding) { return; } - const isAssociation = - typeof field?.isAssociationField === 'function' ? field.isAssociationField() : Boolean(field?.target); + const isAssociation = isAssociationCollectionField(field); const fieldModel = isAssociation && ctxWithFlags.engine?.getModelClass?.('FilterFormRecordSelectFieldModel') ? 'FilterFormRecordSelectFieldModel' @@ -507,10 +522,7 @@ export class FilterFormItemModel extends FilterableItemModel<{ const formValue = this.context.form?.getFieldValue(this.props.name); const modelValue = fieldModel.getFilterValue ? fieldModel.getFilterValue() : formValue; const collectionField = (fieldModel as any)?.context?.collectionField; - const isAssociationField = - typeof collectionField?.isAssociationField === 'function' - ? collectionField.isAssociationField() - : !!collectionField?.target; + const isAssociationField = isAssociationCollectionField(collectionField); const shouldUseFormValue = isAssociationField && !this.mounted && @@ -547,11 +559,7 @@ export class FilterFormItemModel extends FilterableItemModel<{ return value; } const collectionField = (fieldModel as any)?.context?.collectionField; - const isAssociation = - typeof collectionField?.isAssociationField === 'function' - ? collectionField.isAssociationField() - : !!collectionField?.target; - if (!isAssociation) { + if (!isAssociationCollectionField(collectionField)) { return value; } const normalizedFieldNames = normalizeAssociationFieldNames( @@ -798,6 +806,13 @@ FilterFormItemModel.registerFlow({ }, defaultOperator: { use: 'defaultOperator', + hideInSettings(ctx) { + const collectionField = + ctx.collectionField || + ctx.model?.context?.collectionField || + ctx.model?.subModels?.field?.context?.collectionField; + return isAssociationCollectionField(collectionField); + }, }, operatorComponentProps: { use: 'operatorComponentProps', diff --git a/packages/core/client-v2/src/flow/models/blocks/filter-form/__tests__/FilterFormItemModel.defineChildren.test.ts b/packages/core/client-v2/src/flow/models/blocks/filter-form/__tests__/FilterFormItemModel.defineChildren.test.ts index 1d80a1585d7..4ce162ef167 100644 --- a/packages/core/client-v2/src/flow/models/blocks/filter-form/__tests__/FilterFormItemModel.defineChildren.test.ts +++ b/packages/core/client-v2/src/flow/models/blocks/filter-form/__tests__/FilterFormItemModel.defineChildren.test.ts @@ -33,6 +33,42 @@ class DummyCollectionBlockModel extends CollectionBlockModel { } describe('FilterFormItemModel defineChildren association fields', () => { + it('hides default operator setting for association filter fields', () => { + const engine = new FlowEngine(); + engine.registerModels({ + FilterFormItemModel, + }); + + const filterItem = engine.createModel({ + uid: 'association-filter-item-settings', + use: 'FilterFormItemModel', + }); + + const defaultOperatorStep = filterItem.getFlow('filterFormItemSettings')?.steps?.defaultOperator as { + hideInSettings?: (ctx: { + collectionField?: unknown; + model?: { + subModels?: { + field?: { + context?: { + collectionField?: unknown; + }; + }; + }; + }; + }) => boolean; + }; + expect(defaultOperatorStep?.hideInSettings?.({ collectionField: { isAssociationField: () => true } })).toBe(true); + expect( + defaultOperatorStep?.hideInSettings?.({ + model: { subModels: { field: { context: { collectionField: { target: 'departments' } } } } }, + }), + ).toBe(true); + expect(defaultOperatorStep?.hideInSettings?.({ collectionField: { interface: 'input', type: 'string' } })).toBe( + false, + ); + }); + it('groups association target fields and supports recursive paths', async () => { const engine = new FlowEngine(); engine.registerModels({