fix(inherit): fix 'Add new' button for inherited collectons (#5049)

* fix(inherit): fix 'Add new' button for inherited collectons

* test: add e2e test
This commit is contained in:
Zeke Zhang 2024-08-14 09:33:32 +08:00 committed by GitHub
parent 2da27dd009
commit be4ae42376
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
6 changed files with 352 additions and 8 deletions

View File

@ -299,7 +299,7 @@ export class Collection {
} }
/** /**
* is inherited from other collection * is inherited from other collections
* @returns boolean * @returns boolean
*/ */
isInherited() { isInherited() {

View File

@ -0,0 +1,47 @@
/**
* This file is part of the NocoBase (R) project.
* Copyright (c) 2020-2024 NocoBase Co., Ltd.
* Authors: NocoBase Team.
*
* This project is dual-licensed under AGPL-3.0 and NocoBase Commercial License.
* For more information, please refer to: https://www.nocobase.com/agreement.
*/
import { expect, test } from '@nocobase/test/e2e';
import { T5084 } from './templates';
test.describe('Add new: inherit', () => {
test('creating block by child collection should work correctly', async ({ page, mockPage }) => {
await mockPage(T5084).goto();
// 1. click the "Add new" button, and then create a block, the block's collection should be "parent"
await page.getByRole('button', { name: 'plus Add new' }).click();
await page.getByLabel('schema-initializer-Grid-popup').hover();
await page.getByRole('menuitem', { name: 'form Form right' }).hover();
await page.getByRole('menuitem', { name: 'Current collection' }).click();
await page.getByLabel('block-item-CardItem-parent-form').hover();
await expect(page.getByLabel('block-item-CardItem-parent-form').getByText('parent')).toBeVisible();
// close popup
await page.getByLabel('drawer-Action.Container-parent-Add record-mask').click();
// 2. click the "child1" option, and then create a block, the block's collection should be "child1"
await page.getByRole('button', { name: 'down' }).hover();
await page.getByRole('menuitem', { name: 'child1' }).click();
await page.getByLabel('schema-initializer-Grid-popup').hover();
await page.getByRole('menuitem', { name: 'form Form right' }).hover();
await page.getByRole('menuitem', { name: 'Current collection' }).click();
await page.getByLabel('block-item-CardItem-child1-').hover();
await expect(page.getByLabel('block-item-CardItem-child1-').getByText('child1')).toBeVisible();
// close popup
await page.getByLabel('drawer-Action.Container-child1-Add record-mask').click();
// 3. click the "child2" option, and then create a block, the block's collection should be "child2"
await page.getByRole('button', { name: 'down' }).hover();
await page.getByRole('menuitem', { name: 'child2' }).click();
await page.getByLabel('schema-initializer-Grid-popup').hover();
await page.getByRole('menuitem', { name: 'form Form right' }).hover();
await page.getByRole('menuitem', { name: 'Current collection' }).click();
await page.getByLabel('block-item-CardItem-child2-').hover();
await expect(page.getByLabel('block-item-CardItem-child2-').getByText('child2')).toBeVisible();
});
});

View File

@ -0,0 +1,273 @@
/**
* This file is part of the NocoBase (R) project.
* Copyright (c) 2020-2024 NocoBase Co., Ltd.
* Authors: NocoBase Team.
*
* This project is dual-licensed under AGPL-3.0 and NocoBase Commercial License.
* For more information, please refer to: https://www.nocobase.com/agreement.
*/
export const T5084 = {
collections: [
{
name: 'parent',
},
{
name: 'child1',
inherits: ['parent'],
},
{
name: 'child2',
inherits: ['parent'],
},
],
pageSchema: {
_isJSONSchemaObject: true,
version: '2.0',
type: 'void',
'x-component': 'Page',
properties: {
'9z728grhuw5': {
_isJSONSchemaObject: true,
version: '2.0',
type: 'void',
'x-component': 'Grid',
'x-initializer': 'page:addBlock',
properties: {
qunhcwzgdrq: {
_isJSONSchemaObject: true,
version: '2.0',
type: 'void',
'x-component': 'Grid.Row',
'x-app-version': '1.3.0-alpha',
properties: {
'232zkncxw1p': {
_isJSONSchemaObject: true,
version: '2.0',
type: 'void',
'x-component': 'Grid.Col',
'x-app-version': '1.3.0-alpha',
properties: {
hj5wtj01v9m: {
_isJSONSchemaObject: true,
version: '2.0',
type: 'void',
'x-decorator': 'TableBlockProvider',
'x-acl-action': 'parent:list',
'x-use-decorator-props': 'useTableBlockDecoratorProps',
'x-decorator-props': {
collection: 'parent',
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.0-alpha',
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.0-alpha',
properties: {
uqw9g1a6bx2: {
'x-uid': 'atwcb9plntt',
_isJSONSchemaObject: true,
version: '2.0',
type: 'void',
'x-action': 'create',
'x-acl-action': 'create',
title: "{{t('Add new')}}",
'x-toolbar': 'ActionSchemaToolbar',
'x-settings': 'actionSettings:addNew',
'x-component': 'Action',
'x-decorator': 'ACLActionProvider',
'x-component-props': {
openMode: 'drawer',
type: 'primary',
component: 'CreateRecordAction',
icon: 'PlusOutlined',
},
'x-action-context': {
dataSource: 'main',
collection: 'parent',
},
'x-align': 'right',
'x-acl-action-props': {
skipScopeCheck: true,
},
'x-app-version': '1.3.0-alpha',
'x-enable-children': [
{
collection: 'child1',
},
{
collection: 'child2',
},
],
'x-allow-add-to-current': true,
properties: {
drawer: {
_isJSONSchemaObject: true,
version: '2.0',
type: 'void',
title: '{{ t("Add record") }}',
'x-component': 'Action.Container',
'x-component-props': {
className: 'nb-action-popup',
},
'x-app-version': '1.3.0-alpha',
properties: {
tabs: {
_isJSONSchemaObject: true,
version: '2.0',
type: 'void',
'x-component': 'Tabs',
'x-component-props': {},
'x-initializer': 'popup:addTab',
'x-initializer-props': {
gridInitializer: 'popup:addNew:addBlock',
},
'x-app-version': '1.3.0-alpha',
properties: {
tab1: {
_isJSONSchemaObject: true,
version: '2.0',
type: 'void',
title: '{{t("Add new")}}',
'x-component': 'Tabs.TabPane',
'x-designer': 'Tabs.Designer',
'x-component-props': {},
'x-app-version': '1.3.0-alpha',
properties: {
grid: {
_isJSONSchemaObject: true,
version: '2.0',
type: 'void',
'x-component': 'Grid',
'x-initializer': 'popup:addNew:addBlock',
'x-app-version': '1.3.0-alpha',
'x-uid': '9b9jmnkn2f5',
'x-async': false,
'x-index': 1,
},
},
'x-uid': 'flmsg97ib9h',
'x-async': false,
'x-index': 1,
},
},
'x-uid': 'xd5niej584x',
'x-async': false,
'x-index': 1,
},
},
'x-uid': 'nlmwpvv9vd3',
'x-async': false,
'x-index': 1,
},
},
'x-async': false,
'x-index': 1,
},
},
'x-uid': 'v0oc2ik3mqw',
'x-async': false,
'x-index': 1,
},
jecm8jee085: {
_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.0-alpha',
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.0-alpha',
properties: {
'4f5sce3w9fx': {
_isJSONSchemaObject: true,
version: '2.0',
type: 'void',
'x-decorator': 'DndContext',
'x-component': 'Space',
'x-component-props': {
split: '|',
},
'x-app-version': '1.3.0-alpha',
'x-uid': 'qtr5an6qbu6',
'x-async': false,
'x-index': 1,
},
},
'x-uid': 'r8msyk257y9',
'x-async': false,
'x-index': 1,
},
},
'x-uid': 'm2m7kgypy5z',
'x-async': false,
'x-index': 2,
},
},
'x-uid': 'g0mdajxejhl',
'x-async': false,
'x-index': 1,
},
},
'x-uid': 'r7uvf8c2gdq',
'x-async': false,
'x-index': 1,
},
},
'x-uid': 'fr8e99uwlgl',
'x-async': false,
'x-index': 1,
},
},
'x-uid': 'awqpmazhs8a',
'x-async': false,
'x-index': 1,
},
},
'x-uid': 'st43un0l7kl',
'x-async': true,
'x-index': 1,
},
};

View File

@ -155,7 +155,7 @@ const PagePopupsItemProvider: FC<{
<PopupVisibleProvider visible={visible} setVisible={setVisible}> <PopupVisibleProvider visible={visible} setVisible={setVisible}>
<DataBlockProvider <DataBlockProvider
dataSource={context.dataSource} dataSource={context.dataSource}
collection={context.collection} collection={params.collection || context.collection}
association={context.association} association={context.association}
sourceId={params.sourceid} sourceId={params.sourceid}
filterByTk={params.filterbytk} filterByTk={params.filterbytk}

View File

@ -35,6 +35,8 @@ export interface PopupParams {
sourceid?: string; sourceid?: string;
/** tab uid */ /** tab uid */
tab?: string; tab?: string;
/** collection name */
collection?: string;
} }
export interface PopupContextStorage extends PopupContext { export interface PopupContextStorage extends PopupContext {
@ -101,9 +103,11 @@ export const getPopupParamsFromPath = _.memoize((path: string) => {
}); });
export const getPopupPathFromParams = (params: PopupParams) => { export const getPopupPathFromParams = (params: PopupParams) => {
const { popupuid: popupUid, tab, filterbytk, sourceid } = params; const { popupuid: popupUid, tab, filterbytk, sourceid, collection } = params;
const popupPath = [ const popupPath = [
popupUid, popupUid,
collection && 'collection',
collection,
filterbytk && 'filterbytk', filterbytk && 'filterbytk',
filterbytk, filterbytk,
sourceid && 'sourceid', sourceid && 'sourceid',
@ -148,15 +152,18 @@ export const usePagePopup = () => {
popupUid, popupUid,
recordData, recordData,
sourceId, sourceId,
collection: _collection,
}: { }: {
popupUid: string; popupUid: string;
recordData: Record<string, any>; recordData: Record<string, any>;
sourceId: string; sourceId: string;
tabKey?: string; tabKey?: string;
collection?: string;
}) => { }) => {
const filterByTK = cm.getFilterByTK(association || collection, recordData); const filterByTK = cm.getFilterByTK(association || collection, recordData);
return getPopupPathFromParams({ return getPopupPathFromParams({
popupuid: popupUid, popupuid: popupUid,
collection: _collection,
filterbytk: filterByTK, filterbytk: filterByTK,
sourceid: sourceId, sourceid: sourceId,
tab: tabKey, tab: tabKey,
@ -179,9 +186,12 @@ export const usePagePopup = () => {
({ ({
recordData, recordData,
parentRecordData, parentRecordData,
collectionNameUsedInURL,
}: { }: {
recordData?: Record<string, any>; recordData?: Record<string, any>;
parentRecordData?: Record<string, any>; parentRecordData?: Record<string, any>;
/** if this value exists, it will be saved in the URL */
collectionNameUsedInURL?: string;
} = {}) => { } = {}) => {
if (!isPopupVisibleControlledByURL()) { if (!isPopupVisibleControlledByURL()) {
return setVisibleFromAction?.(true); return setVisibleFromAction?.(true);
@ -190,7 +200,12 @@ export const usePagePopup = () => {
const sourceId = getSourceId(parentRecordData); const sourceId = getSourceId(parentRecordData);
recordData = recordData || record?.data; recordData = recordData || record?.data;
const pathname = getNewPathname({ popupUid: currentPopupUidWithoutOpened, recordData, sourceId }); const pathname = getNewPathname({
popupUid: currentPopupUidWithoutOpened,
recordData,
sourceId,
collection: collectionNameUsedInURL,
});
let url = location.pathname; let url = location.pathname;
if (_.last(url) === '/') { if (_.last(url) === '/') {
url = url.slice(0, -1); url = url.slice(0, -1);

View File

@ -12,7 +12,7 @@ import { observer, useField, useFieldSchema, useForm } from '@formily/react';
import { Button, Dropdown, MenuProps } from 'antd'; import { Button, Dropdown, MenuProps } from 'antd';
import { composeRef } from 'rc-util/lib/ref'; import { composeRef } from 'rc-util/lib/ref';
import React, { createRef, forwardRef, useEffect, useMemo } from 'react'; import React, { createRef, forwardRef, useEffect, useMemo } from 'react';
import { Collection, useDesignable } from '../../'; import { Collection, useCollectionManager, useDesignable } from '../../';
import { useACLActionParamsContext, useACLRolesCheck, useRecordPkValue } from '../../acl/ACLProvider'; import { useACLActionParamsContext, useACLRolesCheck, useRecordPkValue } from '../../acl/ACLProvider';
import { useCollectionManager_deprecated, useCollection_deprecated } from '../../collection-manager'; import { useCollectionManager_deprecated, useCollection_deprecated } from '../../collection-manager';
import { useTreeParentRecord } from '../../modules/blocks/data-blocks/table/TreeRecordProvider'; import { useTreeParentRecord } from '../../modules/blocks/data-blocks/table/TreeRecordProvider';
@ -65,7 +65,6 @@ function useAclCheckFn() {
const InternalCreateRecordAction = (props: any, ref) => { const InternalCreateRecordAction = (props: any, ref) => {
const fieldSchema = useFieldSchema(); const fieldSchema = useFieldSchema();
const openMode = fieldSchema?.['x-component-props']?.['openMode'];
const field: any = useField(); const field: any = useField();
const linkageRules: any[] = fieldSchema?.['x-linkage-rules'] || []; const linkageRules: any[] = fieldSchema?.['x-linkage-rules'] || [];
const values = useRecord(); const values = useRecord();
@ -73,6 +72,7 @@ const InternalCreateRecordAction = (props: any, ref) => {
const localVariables = useLocalVariables({ currentForm: { values } as any }); const localVariables = useLocalVariables({ currentForm: { values } as any });
const { openPopup } = usePagePopup(); const { openPopup } = usePagePopup();
const treeRecordData = useTreeParentRecord(); const treeRecordData = useTreeParentRecord();
const cm = useCollectionManager();
useEffect(() => { useEffect(() => {
field.stateOfLinkageRules = {}; field.stateOfLinkageRules = {};
@ -98,13 +98,22 @@ const InternalCreateRecordAction = (props: any, ref) => {
<div ref={buttonRef as React.Ref<HTMLButtonElement>}> <div ref={buttonRef as React.Ref<HTMLButtonElement>}>
<CreateAction <CreateAction
{...props} {...props}
onClick={(collection: Collection) => { onClick={(collection: Partial<Collection>) => {
collection = cm.getCollection(collection.name) || collection;
if (treeRecordData) { if (treeRecordData) {
openPopup({ openPopup({
recordData: treeRecordData, recordData: treeRecordData,
}); });
} else { } else {
openPopup(); // fix https://nocobase.height.app/T-5084/description
if (collection.isInherited?.()) {
openPopup({
collectionNameUsedInURL: collection.name,
});
} else {
openPopup();
}
} }
}} }}
/> />