From ab0f0292807c3221c927c538f40a1cb48e5b751b Mon Sep 17 00:00:00 2001 From: Zeke Zhang <958414905@qq.com> Date: Fri, 27 Sep 2024 11:04:18 +0800 Subject: [PATCH] fix: refresh block data when closing sub-page by clicking page menu (#5331) * fix: refresh block data when closing sub-page by clicking page menu * test: add e2e test --- .../__e2e__/submit/refreshData.test.ts | 26 +- .../actions/__e2e__/submit/templates.ts | 739 ++++++++++++++++++ .../schema-component/antd/action/context.tsx | 34 +- 3 files changed, 795 insertions(+), 4 deletions(-) diff --git a/packages/core/client/src/modules/actions/__e2e__/submit/refreshData.test.ts b/packages/core/client/src/modules/actions/__e2e__/submit/refreshData.test.ts index 5b58bdb305..d15e3d7f8e 100644 --- a/packages/core/client/src/modules/actions/__e2e__/submit/refreshData.test.ts +++ b/packages/core/client/src/modules/actions/__e2e__/submit/refreshData.test.ts @@ -8,7 +8,7 @@ */ import { expect, test } from '@nocobase/test/e2e'; -import { submitInReferenceTemplateBlock } from './templates'; +import { shouldRefreshDataWhenSubpageIsClosedByPageMenu, submitInReferenceTemplateBlock } from './templates'; test.describe('Submit: should refresh data after submit', () => { test('submit in reference template block', async ({ page, mockPage, clearBlockTemplates, mockRecord }) => { @@ -83,4 +83,28 @@ test.describe('Submit: should refresh data after submit', () => { page.getByLabel('block-item-CardItem-collection-table').getByRole('button', { name: 'abc456' }), ).toBeVisible(); }); + + test('should refresh data when subpage is closed by page menu', async ({ page, mockPage, mockRecord }) => { + const nocoPage = await mockPage(shouldRefreshDataWhenSubpageIsClosedByPageMenu).waitForInit(); + await mockRecord('testRefresh', { name: 'abcdefg' }); + await nocoPage.goto(); + const pageUid = await nocoPage.getUid(); + + // 1. Initially, there should be a row with "abcdefg" on the page + await expect(page.getByRole('button', { name: 'abcdefg', exact: true })).toBeVisible(); + + // 2. Click the "Edit" button to open the subpage + await page.getByLabel('action-Action.Link-Edit-').click(); + + // 3. On the subpage, open a popup, fill out the form, and submit + await page.getByLabel('Edit', { exact: true }).getByLabel('action-Action.Link-Edit-').click(); + await page.getByLabel('block-item-CollectionField-').getByRole('textbox').fill('1234567890'); + await page.getByLabel('action-Action-Submit-submit-').click(); + + // 4. Close the subpage by clicking on the menu at the top of the page + await page.getByLabel(pageUid).click(); + + // 5. The data in the block on the page should be up-to-date + await page.getByRole('button', { name: '1234567890', exact: true }).click(); + }); }); diff --git a/packages/core/client/src/modules/actions/__e2e__/submit/templates.ts b/packages/core/client/src/modules/actions/__e2e__/submit/templates.ts index 0b12f0ff77..39c76a3ed0 100644 --- a/packages/core/client/src/modules/actions/__e2e__/submit/templates.ts +++ b/packages/core/client/src/modules/actions/__e2e__/submit/templates.ts @@ -863,3 +863,742 @@ export const submitInReferenceTemplateBlock: PageConfig = { 'x-index': 1, }, }; +export const shouldRefreshDataWhenSubpageIsClosedByPageMenu = { + collections: [ + { + name: 'testRefresh', + fields: [ + { + name: 'name', + interface: 'input', + }, + ], + }, + ], + pageSchema: { + _isJSONSchemaObject: true, + version: '2.0', + type: 'void', + 'x-component': 'Page', + properties: { + f60xwf8ufc1: { + _isJSONSchemaObject: true, + version: '2.0', + type: 'void', + 'x-component': 'Grid', + 'x-initializer': 'page:addBlock', + properties: { + c383hu4oxba: { + _isJSONSchemaObject: true, + version: '2.0', + type: 'void', + 'x-component': 'Grid.Row', + 'x-app-version': '1.3.25-beta', + properties: { + igrzb8z8p47: { + _isJSONSchemaObject: true, + version: '2.0', + type: 'void', + 'x-component': 'Grid.Col', + 'x-app-version': '1.3.25-beta', + properties: { + czd5rlqiiug: { + _isJSONSchemaObject: true, + version: '2.0', + type: 'void', + 'x-decorator': 'TableBlockProvider', + 'x-acl-action': 'testRefresh:list', + 'x-use-decorator-props': 'useTableBlockDecoratorProps', + 'x-decorator-props': { + collection: 'testRefresh', + 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.25-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.25-beta', + 'x-uid': 'u883ena2rat', + 'x-async': false, + 'x-index': 1, + }, + '0s7l6sopxd1': { + _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.25-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.25-beta', + properties: { + jxbi8tufxur: { + _isJSONSchemaObject: true, + version: '2.0', + type: 'void', + 'x-decorator': 'DndContext', + 'x-component': 'Space', + 'x-component-props': { + split: '|', + }, + 'x-app-version': '1.3.25-beta', + properties: { + '5ip8j17sa1q': { + 'x-uid': 'tmlwp0igqn3', + _isJSONSchemaObject: true, + version: '2.0', + type: 'void', + title: '{{ t("Edit") }}', + 'x-action': 'update', + 'x-toolbar': 'ActionSchemaToolbar', + 'x-settings': 'actionSettings:edit', + 'x-component': 'Action.Link', + 'x-component-props': { + openMode: 'page', + icon: 'EditOutlined', + }, + 'x-action-context': { + dataSource: 'main', + collection: 'testRefresh', + }, + 'x-decorator': 'ACLActionProvider', + 'x-designer-props': { + linkageAction: true, + }, + properties: { + drawer: { + _isJSONSchemaObject: true, + version: '2.0', + type: 'void', + title: '{{ t("Edit record") }}', + 'x-component': 'Action.Container', + 'x-component-props': { + className: 'nb-action-popup', + }, + properties: { + tabs: { + _isJSONSchemaObject: true, + version: '2.0', + type: 'void', + 'x-component': 'Tabs', + 'x-component-props': {}, + 'x-initializer': 'popup:addTab', + properties: { + tab1: { + _isJSONSchemaObject: true, + version: '2.0', + type: 'void', + title: '{{t("Edit")}}', + 'x-component': 'Tabs.TabPane', + 'x-designer': 'Tabs.Designer', + 'x-component-props': {}, + properties: { + grid: { + _isJSONSchemaObject: true, + version: '2.0', + type: 'void', + 'x-component': 'Grid', + 'x-initializer': 'popup:common:addBlock', + properties: { + a5o0u6ic4zi: { + _isJSONSchemaObject: true, + version: '2.0', + type: 'void', + 'x-component': 'Grid.Row', + 'x-app-version': '1.3.25-beta', + properties: { + o94fslh753r: { + _isJSONSchemaObject: true, + version: '2.0', + type: 'void', + 'x-component': 'Grid.Col', + 'x-app-version': '1.3.25-beta', + properties: { + bxmjip1l3zo: { + _isJSONSchemaObject: true, + version: '2.0', + type: 'void', + 'x-decorator': 'TableBlockProvider', + 'x-acl-action': 'testRefresh:list', + 'x-use-decorator-props': 'useTableBlockDecoratorProps', + 'x-decorator-props': { + collection: 'testRefresh', + 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.25-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.25-beta', + 'x-uid': 'cbina6hzuul', + 'x-async': false, + 'x-index': 1, + }, + nol8niatans: { + _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.25-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.25-beta', + properties: { + l3z75xol1mi: { + _isJSONSchemaObject: true, + version: '2.0', + type: 'void', + 'x-decorator': 'DndContext', + 'x-component': 'Space', + 'x-component-props': { + split: '|', + }, + 'x-app-version': '1.3.25-beta', + properties: { + ls8c2cy2m89: { + _isJSONSchemaObject: true, + version: '2.0', + type: 'void', + title: '{{ t("Edit") }}', + 'x-action': 'update', + 'x-toolbar': 'ActionSchemaToolbar', + 'x-settings': 'actionSettings:edit', + 'x-component': 'Action.Link', + 'x-component-props': { + openMode: 'drawer', + icon: 'EditOutlined', + }, + 'x-action-context': { + dataSource: 'main', + collection: 'testRefresh', + }, + 'x-decorator': 'ACLActionProvider', + 'x-designer-props': { + linkageAction: true, + }, + properties: { + drawer: { + _isJSONSchemaObject: true, + version: '2.0', + type: 'void', + title: '{{ t("Edit record") }}', + 'x-component': 'Action.Container', + 'x-component-props': { + className: 'nb-action-popup', + }, + properties: { + tabs: { + _isJSONSchemaObject: true, + version: '2.0', + type: 'void', + 'x-component': 'Tabs', + 'x-component-props': {}, + 'x-initializer': 'popup:addTab', + properties: { + tab1: { + _isJSONSchemaObject: true, + version: '2.0', + type: 'void', + title: '{{t("Edit")}}', + 'x-component': + 'Tabs.TabPane', + 'x-designer': + 'Tabs.Designer', + 'x-component-props': {}, + properties: { + grid: { + _isJSONSchemaObject: + true, + version: '2.0', + type: 'void', + 'x-component': 'Grid', + 'x-initializer': + 'popup:common:addBlock', + properties: { + c8inwkebx1a: { + _isJSONSchemaObject: + true, + version: '2.0', + type: 'void', + 'x-component': + 'Grid.Row', + 'x-app-version': + '1.3.25-beta', + properties: { + a5gbhpfx4ay: { + _isJSONSchemaObject: + true, + version: '2.0', + type: 'void', + 'x-component': + 'Grid.Col', + 'x-app-version': + '1.3.25-beta', + properties: { + hekz785yebu: { + _isJSONSchemaObject: + true, + version: + '2.0', + type: 'void', + 'x-acl-action-props': + { + skipScopeCheck: + false, + }, + 'x-acl-action': + 'testRefresh:update', + 'x-decorator': + 'FormBlockProvider', + 'x-use-decorator-props': + 'useEditFormBlockDecoratorProps', + 'x-decorator-props': + { + action: + 'get', + dataSource: + 'main', + collection: + 'testRefresh', + }, + 'x-toolbar': + 'BlockSchemaToolbar', + 'x-settings': + 'blockSettings:editForm', + 'x-component': + 'CardItem', + 'x-app-version': + '1.3.25-beta', + properties: + { + '1r799t325wo': + { + _isJSONSchemaObject: + true, + version: + '2.0', + type: 'void', + 'x-component': + 'FormV2', + 'x-use-component-props': + 'useEditFormBlockProps', + 'x-app-version': + '1.3.25-beta', + properties: + { + grid: { + _isJSONSchemaObject: + true, + version: + '2.0', + type: 'void', + 'x-component': + 'Grid', + 'x-initializer': + 'form:configureFields', + 'x-app-version': + '1.3.25-beta', + properties: + { + w7mpjt5r510: + { + _isJSONSchemaObject: + true, + version: + '2.0', + type: 'void', + 'x-component': + 'Grid.Row', + 'x-app-version': + '1.3.25-beta', + properties: + { + '1tncur8pho6': + { + _isJSONSchemaObject: + true, + version: + '2.0', + type: 'void', + 'x-component': + 'Grid.Col', + 'x-app-version': + '1.3.25-beta', + properties: + { + name: { + _isJSONSchemaObject: + true, + version: + '2.0', + type: 'string', + 'x-toolbar': + 'FormItemSchemaToolbar', + 'x-settings': + 'fieldSettings:FormItem', + 'x-component': + 'CollectionField', + 'x-decorator': + 'FormItem', + 'x-collection-field': + 'testRefresh.name', + 'x-component-props': + {}, + 'x-app-version': + '1.3.25-beta', + 'x-uid': + 'w2b29tftqa5', + 'x-async': + false, + 'x-index': 1, + }, + }, + 'x-uid': + 'vmqxwcj1sah', + 'x-async': + false, + 'x-index': 1, + }, + }, + 'x-uid': + 'jx174p4aj50', + 'x-async': + false, + 'x-index': 1, + }, + }, + 'x-uid': + 'nnf7799bh1o', + 'x-async': + false, + 'x-index': 1, + }, + w12hok3wqww: + { + _isJSONSchemaObject: + true, + version: + '2.0', + type: 'void', + 'x-initializer': + 'editForm:configureActions', + 'x-component': + 'ActionBar', + 'x-component-props': + { + layout: + 'one-column', + }, + 'x-app-version': + '1.3.25-beta', + properties: + { + tpy6isposk0: + { + _isJSONSchemaObject: + true, + version: + '2.0', + title: + '{{ t("Submit") }}', + 'x-action': + 'submit', + 'x-component': + 'Action', + 'x-use-component-props': + 'useUpdateActionProps', + 'x-toolbar': + 'ActionSchemaToolbar', + 'x-settings': + 'actionSettings:updateSubmit', + 'x-component-props': + { + type: 'primary', + htmlType: + 'submit', + }, + 'x-action-settings': + { + triggerWorkflows: + [], + }, + type: 'void', + 'x-app-version': + '1.3.25-beta', + 'x-uid': + 'witfr2ignds', + 'x-async': + false, + 'x-index': 1, + }, + }, + 'x-uid': + '95h3woyegid', + 'x-async': + false, + 'x-index': 2, + }, + }, + 'x-uid': + 'jz4pvx7vdyn', + 'x-async': + false, + 'x-index': 1, + }, + }, + 'x-uid': + 'w9r4yyxzwo2', + 'x-async': + false, + 'x-index': 1, + }, + }, + 'x-uid': + 'n94upy7yd47', + 'x-async': + false, + 'x-index': 1, + }, + }, + 'x-uid': + 'bvg41neww0r', + 'x-async': false, + 'x-index': 1, + }, + }, + 'x-uid': 'z459apnn13m', + 'x-async': false, + 'x-index': 1, + }, + }, + 'x-uid': '2t3sx3nedi1', + 'x-async': false, + 'x-index': 1, + }, + }, + 'x-uid': '88zf1r5n5ej', + 'x-async': false, + 'x-index': 1, + }, + }, + 'x-uid': 't3xqeq01rdj', + 'x-async': false, + 'x-index': 1, + }, + }, + 'x-uid': 'c1i3hb0bu0f', + 'x-async': false, + 'x-index': 1, + }, + }, + 'x-uid': 'fbgaaj9td5t', + 'x-async': false, + 'x-index': 1, + }, + }, + 'x-uid': 'dhdum7zv2wv', + 'x-async': false, + 'x-index': 1, + }, + }, + 'x-uid': 'vl8ub53ikv4', + 'x-async': false, + 'x-index': 2, + }, + }, + 'x-uid': 'rzmt46s0nfk', + 'x-async': false, + 'x-index': 1, + }, + }, + 'x-uid': '7ywukcy6ce7', + 'x-async': false, + 'x-index': 1, + }, + }, + 'x-uid': 'u1914umcr4j', + 'x-async': false, + 'x-index': 1, + }, + }, + 'x-uid': 'cvv09ko8h3e', + 'x-async': false, + 'x-index': 1, + }, + }, + 'x-uid': 'o06ez3hp081', + 'x-async': false, + 'x-index': 1, + }, + }, + 'x-uid': 'dhdv3671m57', + 'x-async': false, + 'x-index': 1, + }, + }, + 'x-uid': 'u1ero8piyk0', + 'x-async': false, + 'x-index': 1, + }, + }, + 'x-async': false, + 'x-index': 1, + }, + }, + 'x-uid': 'nonwnta1tpk', + 'x-async': false, + 'x-index': 1, + }, + }, + 'x-uid': '46nco16r81v', + 'x-async': false, + 'x-index': 1, + }, + bpfpuc2t2tw: { + _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.25-beta', + properties: { + name: { + _isJSONSchemaObject: true, + version: '2.0', + 'x-collection-field': 'testRefresh.name', + 'x-component': 'CollectionField', + 'x-component-props': { + ellipsis: true, + }, + 'x-read-pretty': true, + 'x-decorator': null, + 'x-decorator-props': { + labelStyle: { + display: 'none', + }, + }, + 'x-app-version': '1.3.25-beta', + 'x-uid': '0dq29e4kbly', + 'x-async': false, + 'x-index': 1, + }, + }, + 'x-uid': 'l1df5slauzp', + 'x-async': false, + 'x-index': 2, + }, + }, + 'x-uid': '6ydl9ber22f', + 'x-async': false, + 'x-index': 2, + }, + }, + 'x-uid': 'xczcehxr2m5', + 'x-async': false, + 'x-index': 1, + }, + }, + 'x-uid': 'hwa7l2ppe6l', + 'x-async': false, + 'x-index': 1, + }, + }, + 'x-uid': '54nsmh38l7k', + 'x-async': false, + 'x-index': 1, + }, + }, + 'x-uid': 'tcwxtcalzkm', + 'x-async': false, + 'x-index': 1, + }, + }, + 'x-uid': '5g1v6hmn04q', + 'x-async': true, + 'x-index': 1, + }, +}; diff --git a/packages/core/client/src/schema-component/antd/action/context.tsx b/packages/core/client/src/schema-component/antd/action/context.tsx index eddcfc6237..14617b3b49 100644 --- a/packages/core/client/src/schema-component/antd/action/context.tsx +++ b/packages/core/client/src/schema-component/antd/action/context.tsx @@ -8,7 +8,9 @@ */ import { useFieldSchema } from '@formily/react'; -import React, { createContext, useEffect, useState } from 'react'; +import _ from 'lodash'; +import React, { createContext, useEffect, useMemo, useRef, useState } from 'react'; +import { useParams } from 'react-router-dom'; import { useDataBlockRequest } from '../../../data-source'; import { useCurrentPopupContext } from '../page/PagePopups'; import { getBlockService, storeBlockService } from '../page/pagePopupUtils'; @@ -17,22 +19,48 @@ import { ActionContextProps } from './types'; export const ActionContext = createContext({}); ActionContext.displayName = 'ActionContext'; +/** + * Used to determine if the user closed the sub-page by clicking on the page menu + * @returns + */ +const useIsSubPageClosedByPageMenu = () => { + // Used to trigger re-rendering when URL changes + const params = useParams(); + const prevParamsRef = useRef({}); + const fieldSchema = useFieldSchema(); + + const isSubPageClosedByPageMenu = useMemo(() => { + const result = + _.isEmpty(params['*']) && + fieldSchema?.['x-component-props']?.openMode === 'page' && + !!prevParamsRef.current['*']?.includes(fieldSchema['x-uid']); + + prevParamsRef.current = params; + + return result; + }, [fieldSchema, params]); + + return isSubPageClosedByPageMenu; +}; + export const ActionContextProvider: React.FC = (props) => { const [submitted, setSubmitted] = useState(false); //是否有提交记录 const { visible } = { ...props, ...props.value } || {}; const { setSubmitted: setParentSubmitted } = { ...props, ...props.value }; const service = useBlockServiceInActionButton(); + const isSubPageClosedByPageMenu = useIsSubPageClosedByPageMenu(); useEffect(() => { - if (visible === false && submitted && service) { + if (visible === false && service && !service.loading && (submitted || isSubPageClosedByPageMenu)) { service.refresh(); + service.loading = true; setParentSubmitted?.(true); //传递给上一层 } return () => { setSubmitted(false); }; - }, [visible, service?.refresh, setParentSubmitted]); + }, [visible, service?.refresh, setParentSubmitted, isSubPageClosedByPageMenu]); return (