diff --git a/packages/core/client/src/modules/blocks/filter-blocks/form/__e2e__/schemaInitializer.test.ts b/packages/core/client/src/modules/blocks/filter-blocks/form/__e2e__/schemaInitializer.test.ts index f1f8fde1ac..c348f5316a 100644 --- a/packages/core/client/src/modules/blocks/filter-blocks/form/__e2e__/schemaInitializer.test.ts +++ b/packages/core/client/src/modules/blocks/filter-blocks/form/__e2e__/schemaInitializer.test.ts @@ -8,7 +8,7 @@ */ import { Page, createBlockInPage, expect, oneEmptyFilterFormBlock, test } from '@nocobase/test/e2e'; -import { oneFilterFormWithInherit } from './templates'; +import { displayManyTOManyAssociationFields, oneFilterFormWithInherit } from './templates'; const deleteButton = async (page: Page, name: string) => { await page.getByLabel(`action-Action-${name}-`).hover(); @@ -82,6 +82,33 @@ test.describe('configure fields', () => { await expect(page.getByLabel('block-item-Markdown.Void-general-filter-form')).toBeVisible(); }); + test('display manyToMany association fields', async ({ page, mockPage, mockRecord }) => { + const nocoPage = await mockPage(displayManyTOManyAssociationFields).waitForInit(); + const record = await mockRecord('users', 2); + await nocoPage.goto(); + + // display the `users.roles` field + await page.getByLabel('schema-initializer-Grid-filterForm:configureFields-users').hover(); + await page.getByRole('menuitem', { name: 'Roles right' }).hover(); + await page.getByRole('menuitem', { name: 'Role name' }).click(); + + // before filter + await expect(page.getByRole('button', { name: record.roles.map((item) => item.name).join(',') })).toBeVisible(); + + // input a some value to filter + await page.getByLabel('block-item-CollectionField-').getByRole('textbox').fill('root'); + await page.getByLabel('action-Action-Filter-submit-').click({ + position: { + x: 10, + y: 10, + }, + }); + + // expect: should only display the record with the `root` role + await expect(page.getByRole('button', { name: 'root,admin,member' })).toBeVisible(); + await expect(page.getByRole('button', { name: record.roles.map((item) => item.name).join(',') })).not.toBeVisible(); + }); + test.pgOnly('display inherit fields', async ({ page, mockPage }) => { await mockPage(oneFilterFormWithInherit).goto(); diff --git a/packages/core/client/src/modules/blocks/filter-blocks/form/__e2e__/templates.ts b/packages/core/client/src/modules/blocks/filter-blocks/form/__e2e__/templates.ts index ca5ce130b8..083b6fc132 100644 --- a/packages/core/client/src/modules/blocks/filter-blocks/form/__e2e__/templates.ts +++ b/packages/core/client/src/modules/blocks/filter-blocks/form/__e2e__/templates.ts @@ -1035,3 +1035,318 @@ export const T4798 = { 'x-index': 1, }, }; +export const displayManyTOManyAssociationFields = { + pageSchema: { + _isJSONSchemaObject: true, + version: '2.0', + type: 'void', + 'x-component': 'Page', + properties: { + '9r802f25xpw': { + _isJSONSchemaObject: true, + version: '2.0', + type: 'void', + 'x-component': 'Grid', + 'x-initializer': 'page:addBlock', + properties: { + y7zqna9ok64: { + _isJSONSchemaObject: true, + version: '2.0', + type: 'void', + 'x-component': 'Grid.Row', + 'x-app-version': '1.3.13-beta', + properties: { + e01ptiv7169: { + _isJSONSchemaObject: true, + version: '2.0', + type: 'void', + 'x-component': 'Grid.Col', + 'x-app-version': '1.3.13-beta', + properties: { + '8c03fmw7mv0': { + 'x-uid': 'osrsw28bg16', + _isJSONSchemaObject: true, + version: '2.0', + type: 'void', + 'x-decorator': 'FilterFormBlockProvider', + 'x-use-decorator-props': 'useFilterFormBlockDecoratorProps', + 'x-decorator-props': { + dataSource: 'main', + collection: 'users', + }, + 'x-toolbar': 'BlockSchemaToolbar', + 'x-settings': 'blockSettings:filterForm', + 'x-component': 'CardItem', + 'x-filter-targets': [ + { + uid: 'vl641x6hrd6', + }, + ], + 'x-app-version': '1.3.13-beta', + properties: { + vikjl7uaxai: { + _isJSONSchemaObject: true, + version: '2.0', + type: 'void', + 'x-component': 'FormV2', + 'x-use-component-props': 'useFilterFormBlockProps', + 'x-app-version': '1.3.13-beta', + properties: { + grid: { + _isJSONSchemaObject: true, + version: '2.0', + type: 'void', + 'x-component': 'Grid', + 'x-initializer': 'filterForm:configureFields', + 'x-app-version': '1.3.13-beta', + 'x-uid': 'f38j0uj5vvq', + 'x-async': false, + 'x-index': 1, + }, + '0av2ioa3zjr': { + _isJSONSchemaObject: true, + version: '2.0', + type: 'void', + 'x-initializer': 'filterForm:configureActions', + 'x-component': 'ActionBar', + 'x-component-props': { + layout: 'one-column', + style: { + float: 'right', + }, + }, + 'x-app-version': '1.3.13-beta', + properties: { + lsxm71r395m: { + _isJSONSchemaObject: true, + version: '2.0', + title: '{{ t("Filter") }}', + 'x-action': 'submit', + 'x-component': 'Action', + 'x-use-component-props': 'useFilterBlockActionProps', + 'x-designer': 'Action.Designer', + 'x-component-props': { + type: 'primary', + htmlType: 'submit', + }, + 'x-action-settings': {}, + type: 'void', + 'x-app-version': '1.3.13-beta', + 'x-uid': '6kj3gzqqubb', + 'x-async': false, + 'x-index': 1, + }, + '7ukzfx8trmp': { + _isJSONSchemaObject: true, + version: '2.0', + title: '{{ t("Reset") }}', + 'x-component': 'Action', + 'x-use-component-props': 'useResetBlockActionProps', + 'x-designer': 'Action.Designer', + 'x-action-settings': {}, + type: 'void', + 'x-app-version': '1.3.13-beta', + 'x-uid': '48rt2qm0rip', + 'x-async': false, + 'x-index': 2, + }, + }, + 'x-uid': 'rv5qfqgaxs1', + 'x-async': false, + 'x-index': 2, + }, + }, + 'x-uid': 'pmc05hvyobp', + 'x-async': false, + 'x-index': 1, + }, + }, + 'x-async': false, + 'x-index': 1, + }, + }, + 'x-uid': 'bzkbofpet3m', + 'x-async': false, + 'x-index': 1, + }, + }, + 'x-uid': 'wlpzuhipi0n', + 'x-async': false, + 'x-index': 1, + }, + ektxc4qt6td: { + _isJSONSchemaObject: true, + version: '2.0', + type: 'void', + 'x-component': 'Grid.Row', + 'x-app-version': '1.3.13-beta', + properties: { + zftl6dfyeia: { + _isJSONSchemaObject: true, + version: '2.0', + type: 'void', + 'x-component': 'Grid.Col', + 'x-app-version': '1.3.13-beta', + properties: { + broo6eq0m2p: { + _isJSONSchemaObject: true, + version: '2.0', + type: 'void', + 'x-decorator': 'TableBlockProvider', + 'x-acl-action': 'users:list', + 'x-use-decorator-props': 'useTableBlockDecoratorProps', + 'x-decorator-props': { + collection: 'users', + dataSource: 'main', + action: 'list', + params: { + pageSize: 20, + }, + rowKey: 'id', + showIndex: true, + dragSort: false, + }, + 'x-toolbar': 'BlockSchemaToolbar', + 'x-settings': 'blockSettings:table', + 'x-component': 'CardItem', + 'x-filter-targets': [], + 'x-app-version': '1.3.13-beta', + properties: { + actions: { + _isJSONSchemaObject: true, + version: '2.0', + type: 'void', + 'x-initializer': 'table:configureActions', + 'x-component': 'ActionBar', + 'x-component-props': { + style: { + marginBottom: 'var(--nb-spacing)', + }, + }, + 'x-app-version': '1.3.13-beta', + 'x-uid': 'w46bhgi0e9b', + 'x-async': false, + 'x-index': 1, + }, + c4fknjf3dgj: { + _isJSONSchemaObject: true, + version: '2.0', + type: 'array', + 'x-initializer': 'table:configureColumns', + 'x-component': 'TableV2', + 'x-use-component-props': 'useTableBlockProps', + 'x-component-props': { + rowKey: 'id', + rowSelection: { + type: 'checkbox', + }, + }, + 'x-app-version': '1.3.13-beta', + properties: { + actions: { + _isJSONSchemaObject: true, + version: '2.0', + type: 'void', + title: '{{ t("Actions") }}', + 'x-action-column': 'actions', + 'x-decorator': 'TableV2.Column.ActionBar', + 'x-component': 'TableV2.Column', + 'x-toolbar': 'TableColumnSchemaToolbar', + 'x-initializer': 'table:configureItemActions', + 'x-settings': 'fieldSettings:TableColumn', + 'x-toolbar-props': { + initializer: 'table:configureItemActions', + }, + 'x-app-version': '1.3.13-beta', + properties: { + '5mb0ksk9n7d': { + _isJSONSchemaObject: true, + version: '2.0', + type: 'void', + 'x-decorator': 'DndContext', + 'x-component': 'Space', + 'x-component-props': { + split: '|', + }, + 'x-app-version': '1.3.13-beta', + 'x-uid': 'b8lt0cpa8sk', + 'x-async': false, + 'x-index': 1, + }, + }, + 'x-uid': 'uygsokuvjjo', + 'x-async': false, + 'x-index': 1, + }, + blco479x6xr: { + _isJSONSchemaObject: true, + version: '2.0', + type: 'void', + 'x-decorator': 'TableV2.Column.Decorator', + 'x-toolbar': 'TableColumnSchemaToolbar', + 'x-settings': 'fieldSettings:TableColumn', + 'x-component': 'TableV2.Column', + 'x-app-version': '1.3.13-beta', + properties: { + roles: { + _isJSONSchemaObject: true, + version: '2.0', + 'x-collection-field': 'users.roles', + 'x-component': 'CollectionField', + 'x-component-props': { + fieldNames: { + value: 'name', + label: 'name', + }, + ellipsis: true, + size: 'small', + }, + 'x-read-pretty': true, + 'x-decorator': null, + 'x-decorator-props': { + labelStyle: { + display: 'none', + }, + }, + 'x-app-version': '1.3.13-beta', + 'x-uid': 'gocdkziqwlz', + 'x-async': false, + 'x-index': 1, + }, + }, + 'x-uid': '3pfwlp96981', + 'x-async': false, + 'x-index': 2, + }, + }, + 'x-uid': 'c6nm8b9emvw', + 'x-async': false, + 'x-index': 2, + }, + }, + 'x-uid': 'vl641x6hrd6', + 'x-async': false, + 'x-index': 1, + }, + }, + 'x-uid': '5x1f6kzfwce', + 'x-async': false, + 'x-index': 1, + }, + }, + 'x-uid': 'o9e7bi2jvp7', + 'x-async': false, + 'x-index': 2, + }, + }, + 'x-uid': 'bcez1hvmvm0', + 'x-async': false, + 'x-index': 1, + }, + }, + 'x-uid': '1z5t01zmibi', + 'x-async': true, + 'x-index': 1, + }, + keepUid: true, +}; diff --git a/packages/core/client/src/schema-initializer/utils.ts b/packages/core/client/src/schema-initializer/utils.ts index bd0e03ef3d..cba8c5d018 100644 --- a/packages/core/client/src/schema-initializer/utils.ts +++ b/packages/core/client/src/schema-initializer/utils.ts @@ -492,25 +492,27 @@ export const useAssociatedFormItemInitializerFields = (options?: any) => { return groups; }; -const getItem = ( +const associationFieldToMenu = ( field: FieldOptions, schemaName: string, collectionName: string, getCollectionFields, processedCollections: string[], ) => { - if (field.interface === 'm2o') { - if (processedCollections.includes(field.target)) return null; + if (field.target && field.uiSchema) { + if (processedCollections.includes(field.target) || processedCollections.length >= 1) return; const subFields = getCollectionFields(field.target); + if (!subFields?.length) return; + return { type: 'subMenu', name: schemaName, title: field.uiSchema?.title, children: subFields .map((subField) => - getItem(subField, `${schemaName}.${subField.name}`, collectionName, getCollectionFields, [ + associationFieldToMenu(subField, `${schemaName}.${subField.name}`, collectionName, getCollectionFields, [ ...processedCollections, field.target, ]), @@ -519,12 +521,11 @@ const getItem = ( } as SchemaInitializerItemType; } - if (isAssocField(field)) return null; + if (!field.uiSchema) return; const schema = { type: 'string', name: schemaName, - // 'x-designer': 'FormItem.FilterFormDesigner', 'x-toolbar': 'FormItemSchemaToolbar', 'x-settings': 'fieldSettings:FilterFormItem', 'x-designer-props': { @@ -551,13 +552,10 @@ const getItem = ( export const useFilterAssociatedFormItemInitializerFields = () => { const { name, fields } = useCollection_deprecated(); const { getCollectionFields } = useCollectionManager_deprecated(); - const interfaces = ['m2o']; - const groups = fields - ?.filter((field) => { - return interfaces.includes(field.interface); - }) - ?.map((field) => getItem(field, field.name, name, getCollectionFields, [])); - return groups; + return fields + ?.filter((field) => field.target && field.uiSchema) + .map((field) => associationFieldToMenu(field, field.name, name, getCollectionFields, [])) + .filter(Boolean); }; export const useInheritsFormItemInitializerFields = (options?) => {