feat: support Others option in popup (#4015)

* feat: support Others option in popup

* chore: hide Other records in popup for edit form

* chore: rename 'Others' to 'Other records'

* fix: in other records, the data table does not need to filter itself

* feat: optimize title for association block

* fix: template

* fix: block title

* chore: fix e2e

* fix: should use compile

* fix: remove useVisible

* test: add e2e
This commit is contained in:
Zeke Zhang 2024-04-12 19:14:18 +08:00 committed by GitHub
parent 1658415402
commit 17793c2ab9
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
36 changed files with 802 additions and 252 deletions

View File

@ -496,6 +496,7 @@
"edit title": "edit title", "edit title": "edit title",
"Turn pages": "Turn pages", "Turn pages": "Turn pages",
"Others": "Others", "Others": "Others",
"Other records": "Other records",
"Save as template": "Save as template", "Save as template": "Save as template",
"Save as block template": "Save as block template", "Save as block template": "Save as block template",
"Block templates": "Block templates", "Block templates": "Block templates",

View File

@ -467,6 +467,7 @@
"edit title": "Editar título", "edit title": "Editar título",
"Turn pages": "Pasar páginas", "Turn pages": "Pasar páginas",
"Others": "Otros", "Others": "Otros",
"Other records": "Otros registros",
"Save as template": "Guardar como plantilla", "Save as template": "Guardar como plantilla",
"Save as block template": "Guardar como plantilla de bloque", "Save as block template": "Guardar como plantilla de bloque",
"Block templates": "Bloquear plantillas", "Block templates": "Bloquear plantillas",

View File

@ -482,6 +482,7 @@
"edit title": "modifier le titre", "edit title": "modifier le titre",
"Turn pages": "Tourner les pages", "Turn pages": "Tourner les pages",
"Others": "Autres", "Others": "Autres",
"Other records": "Autres enregistrements",
"Save as template": "Enregistrer en tant que modèle", "Save as template": "Enregistrer en tant que modèle",
"Save as block template": "Enregistrer en tant que modèle de bloc", "Save as block template": "Enregistrer en tant que modèle de bloc",
"Block templates": "Modèles de bloc", "Block templates": "Modèles de bloc",

View File

@ -393,7 +393,8 @@
"Add card": "カードを追加", "Add card": "カードを追加",
"edit title": "タイトルを編集", "edit title": "タイトルを編集",
"Turn pages": "ページをめくる", "Turn pages": "ページをめくる",
"Others": "Others", "Others": "その他",
"Other records": "他のレコード",
"Save as template": "テンプレートとして保存", "Save as template": "テンプレートとして保存",
"Save as block template": "ブロックテンプレートとして保存", "Save as block template": "ブロックテンプレートとして保存",
"Block templates": "ブロックテンプレート", "Block templates": "ブロックテンプレート",

View File

@ -514,6 +514,7 @@
"edit title": "제목 수정", "edit title": "제목 수정",
"Turn pages": "페이지 넘김", "Turn pages": "페이지 넘김",
"Others": "기타", "Others": "기타",
"Other records": "기타 레코드",
"Save as template": "템플릿으로 저장", "Save as template": "템플릿으로 저장",
"Save as block template": "블록 템플릿으로 저장", "Save as block template": "블록 템플릿으로 저장",
"Block templates": "블록 템플릿", "Block templates": "블록 템플릿",

View File

@ -430,6 +430,7 @@
"edit title": "editar título", "edit title": "editar título",
"Turn pages": "Virar páginas", "Turn pages": "Virar páginas",
"Others": "Outros", "Others": "Outros",
"Other records": "Outros registros",
"Save as template": "Salvar como modelo", "Save as template": "Salvar como modelo",
"Save as block template": "Salvar como modelo de bloco", "Save as block template": "Salvar como modelo de bloco",
"Block templates": "Modelos de bloco", "Block templates": "Modelos de bloco",

View File

@ -334,6 +334,7 @@
"edit title": "изменить заголовок", "edit title": "изменить заголовок",
"Turn pages": "Перелистывать страницы", "Turn pages": "Перелистывать страницы",
"Others": "Другие", "Others": "Другие",
"Other records": "Другие записи",
"Save as template": "Сохранить как шаблон", "Save as template": "Сохранить как шаблон",
"Save as block template": "Сохранить как шаблон Блока", "Save as block template": "Сохранить как шаблон Блока",
"Block templates": "Шаблоны Блока", "Block templates": "Шаблоны Блока",

View File

@ -333,6 +333,7 @@
"edit title": "başlığı düzenle", "edit title": "başlığı düzenle",
"Turn pages": "Sayfaları çevir", "Turn pages": "Sayfaları çevir",
"Others": "Diğerleri", "Others": "Diğerleri",
"Other records": "Diğer kayıtlar",
"Save as template": "Şablon olarak kaydet", "Save as template": "Şablon olarak kaydet",
"Save as block template": "Blok şablonu olarak kaydet", "Save as block template": "Blok şablonu olarak kaydet",
"Block templates": "Blok şablonları", "Block templates": "Blok şablonları",

View File

@ -484,6 +484,7 @@
"edit title": "редагувати назву", "edit title": "редагувати назву",
"Turn pages": "Переключати сторінки", "Turn pages": "Переключати сторінки",
"Others": "Інші", "Others": "Інші",
"Other records": "Інші записи",
"Save as template": "Зберегти як шаблон", "Save as template": "Зберегти як шаблон",
"Save as block template": "Зберегти як шаблон блока", "Save as block template": "Зберегти як шаблон блока",
"Block templates": "Шаблони блоків", "Block templates": "Шаблони блоків",

View File

@ -517,6 +517,7 @@
"edit title": "修改标题", "edit title": "修改标题",
"Turn pages": "翻页", "Turn pages": "翻页",
"Others": "其他", "Others": "其他",
"Other records": "其他记录",
"Save as template": "保存为模板", "Save as template": "保存为模板",
"Save as block template": "保存为区块模板", "Save as block template": "保存为区块模板",
"Block templates": "区块模板", "Block templates": "区块模板",

View File

@ -514,6 +514,7 @@
"edit title": "編輯標題", "edit title": "編輯標題",
"Turn pages": "翻頁", "Turn pages": "翻頁",
"Others": "其他", "Others": "其他",
"Other records": "其他記錄",
"Save as template": "儲存為模板", "Save as template": "儲存為模板",
"Save as block template": "儲存為區塊模板", "Save as block template": "儲存為區塊模板",
"Block templates": "區塊模板", "Block templates": "區塊模板",

View File

@ -1,19 +1,58 @@
import React, { useMemo } from 'react'; import React, { useMemo } from 'react';
import { useTranslation } from 'react-i18next'; import { useTranslation } from 'react-i18next';
import { useCollection_deprecated } from '../../collection-manager'; import { useCollectionManager, useDataBlockProps } from '../../data-source';
import { useCollection } from '../../data-source/collection/CollectionProvider';
import { useCompile } from '../../schema-component';
import { SchemaToolbar } from '../../schema-settings/GeneralSchemaDesigner'; import { SchemaToolbar } from '../../schema-settings/GeneralSchemaDesigner';
import { useSchemaTemplate } from '../../schema-templates'; import { useSchemaTemplate } from '../../schema-templates';
export const BlockSchemaToolbar = (props) => { export const BlockSchemaToolbar = (props) => {
const { t } = useTranslation(); const { t } = useTranslation();
const { name, title } = useCollection_deprecated(); const cm = useCollectionManager();
let { name: currentCollectionName, title: currentCollectionTitle } = useCollection();
const template = useSchemaTemplate(); const template = useSchemaTemplate();
const { association } = useDataBlockProps() || {};
const compile = useCompile();
if (association) {
const [collectionName] = association.split('.');
const { name, title } = cm.getCollection(collectionName);
currentCollectionName = name;
currentCollectionTitle = title;
}
const associationField = cm.getCollectionField(association);
const templateName = ['FormItem', 'ReadPrettyFormItem'].includes(template?.componentName) const templateName = ['FormItem', 'ReadPrettyFormItem'].includes(template?.componentName)
? `${template?.name} ${t('(Fields only)')}` ? `${template?.name} ${t('(Fields only)')}`
: template?.name; : template?.name;
const toolbarTitle = useMemo(() => { const toolbarTitle = useMemo(() => {
return [title || name, templateName].filter(Boolean); return [
}, [name, templateName, title]); getCollectionTitle({
collectionTitle: currentCollectionTitle,
collectionName: currentCollectionName,
associationField,
compile,
}),
templateName,
].filter(Boolean);
}, [compile, currentCollectionTitle, currentCollectionName, associationField, templateName]);
return <SchemaToolbar title={toolbarTitle} {...props} />; return <SchemaToolbar title={toolbarTitle} {...props} />;
}; };
function getCollectionTitle(arg0: {
collectionTitle: string;
collectionName: string;
associationField: any;
compile: any;
}) {
const { collectionTitle, collectionName, associationField, compile } = arg0;
if (associationField) {
return `${compile(collectionTitle || collectionName)} > ${compile(
associationField.uiSchema?.title || associationField.name,
)}`;
}
return collectionTitle || collectionName;
}

View File

@ -1,9 +1,9 @@
import { TableOutlined } from '@ant-design/icons'; import { TableOutlined } from '@ant-design/icons';
import React from 'react'; import React, { useCallback } from 'react';
import { useSchemaInitializer, useSchemaInitializerItem } from '../../../../application'; import { useSchemaInitializer, useSchemaInitializerItem } from '../../../../application';
import { useCollectionManager_deprecated } from '../../../../collection-manager'; import { useCollectionManager_deprecated } from '../../../../collection-manager';
import { DataBlockInitializer } from '../../../../schema-initializer/items/DataBlockInitializer';
import { Collection, CollectionFieldOptions } from '../../../../data-source/collection/Collection'; import { Collection, CollectionFieldOptions } from '../../../../data-source/collection/Collection';
import { DataBlockInitializer } from '../../../../schema-initializer/items/DataBlockInitializer';
import { createDetailsWithPaginationUISchema } from './createDetailsWithPaginationUISchema'; import { createDetailsWithPaginationUISchema } from './createDetailsWithPaginationUISchema';
export const DetailsBlockInitializer = ({ export const DetailsBlockInitializer = ({
@ -36,9 +36,8 @@ export const DetailsBlockInitializer = ({
showAssociationFields?: boolean; showAssociationFields?: boolean;
hideChildrenIfSingleCollection?: boolean; hideChildrenIfSingleCollection?: boolean;
}) => { }) => {
const { insert } = useSchemaInitializer();
const { getCollection } = useCollectionManager_deprecated();
const itemConfig = useSchemaInitializerItem(); const itemConfig = useSchemaInitializerItem();
const { createDetailsBlock } = useCreateDetailsBlock();
return ( return (
<DataBlockInitializer <DataBlockInitializer
{...itemConfig} {...itemConfig}
@ -48,8 +47,24 @@ export const DetailsBlockInitializer = ({
if (createBlockSchema) { if (createBlockSchema) {
return createBlockSchema(options); return createBlockSchema(options);
} }
createDetailsBlock(options);
}}
onlyCurrentDataSource={!!onlyCurrentDataSource}
hideSearch={hideSearch}
filter={filterCollections}
templateWrap={templateWrap}
showAssociationFields={showAssociationFields}
hideChildrenIfSingleCollection={hideChildrenIfSingleCollection}
/>
);
};
const { item } = options; export const useCreateDetailsBlock = () => {
const { insert } = useSchemaInitializer();
const { getCollection } = useCollectionManager_deprecated();
const createDetailsBlock = useCallback(
({ item }) => {
const collection = getCollection(item.name, item.dataSource); const collection = getCollection(item.name, item.dataSource);
const schema = createDetailsWithPaginationUISchema({ const schema = createDetailsWithPaginationUISchema({
collectionName: item.name, collectionName: item.name,
@ -61,13 +76,9 @@ export const DetailsBlockInitializer = ({
), ),
}); });
insert(schema); insert(schema);
}} },
onlyCurrentDataSource={!!onlyCurrentDataSource} [getCollection, insert],
hideSearch={hideSearch}
filter={filterCollections}
templateWrap={templateWrap}
showAssociationFields={showAssociationFields}
hideChildrenIfSingleCollection={hideChildrenIfSingleCollection}
/>
); );
return { createDetailsBlock };
}; };

View File

@ -1,4 +1,5 @@
import { createBlockInPage, expect, oneEmptyDetailsBlock, test } from '@nocobase/test/e2e'; import { createBlockInPage, expect, oneEmptyDetailsBlock, test } from '@nocobase/test/e2e';
import { oneEmptyTableWithUsers } from './templatesOfBug';
test.describe('where multi data details block can be added', () => { test.describe('where multi data details block can be added', () => {
test('page', async ({ page, mockPage }) => { test('page', async ({ page, mockPage }) => {
@ -7,6 +8,35 @@ test.describe('where multi data details block can be added', () => {
await createBlockInPage(page, 'Details'); await createBlockInPage(page, 'Details');
await expect(page.getByLabel('block-item-CardItem-users-details')).toBeVisible(); await expect(page.getByLabel('block-item-CardItem-users-details')).toBeVisible();
}); });
test('popup', async ({ page, mockPage }) => {
await mockPage(oneEmptyTableWithUsers).goto();
// 1. 打开弹窗,通过 Associated records 添加一个详情区块
await page.getByLabel('action-Action.Link-View').click();
await page.getByLabel('schema-initializer-Grid-popup').hover();
await page.getByRole('menuitem', { name: 'table Details right' }).hover();
await page.getByRole('menuitem', { name: 'Associated records right' }).hover();
await page.getByRole('menuitem', { name: 'Roles' }).click();
await page.mouse.move(300, 0);
await page.getByLabel('schema-initializer-Grid-details:configureFields-roles').hover();
await page.getByRole('menuitem', { name: 'Role UID' }).click();
await page.mouse.move(300, 0);
await expect(page.getByLabel('block-item-CollectionField-').getByText('admin')).toBeVisible();
// 2. 打开弹窗,通过 Other records 添加一个详情区块
await page.getByLabel('schema-initializer-Grid-popup').hover();
await page.getByRole('menuitem', { name: 'table Details right' }).hover();
await page.getByRole('menuitem', { name: 'Other records right' }).hover();
await page.getByRole('menuitem', { name: 'Users' }).click();
await page.mouse.move(300, 0);
await page.getByLabel('schema-initializer-Grid-details:configureFields-users').click();
await page.getByRole('menuitem', { name: 'Nickname' }).click();
await page.mouse.move(300, 0);
await expect(
page.getByLabel('block-item-CollectionField-users-details-users.nickname-Nickname').getByText('Super Admin'),
).toBeVisible();
});
}); });
test.describe('configure fields', () => { test.describe('configure fields', () => {

View File

@ -0,0 +1,222 @@
export const oneEmptyTableWithUsers = {
pageSchema: {
_isJSONSchemaObject: true,
version: '2.0',
type: 'void',
'x-component': 'Page',
properties: {
zvj8cbqvt05: {
_isJSONSchemaObject: true,
version: '2.0',
type: 'void',
'x-component': 'Grid',
'x-initializer': 'page:addBlock',
properties: {
j0zzcf3k2vc: {
_isJSONSchemaObject: true,
version: '2.0',
type: 'void',
'x-component': 'Grid.Row',
'x-app-version': '0.21.0-alpha.6',
properties: {
pn0pgjxjlz2: {
_isJSONSchemaObject: true,
version: '2.0',
type: 'void',
'x-component': 'Grid.Col',
'x-app-version': '0.21.0-alpha.6',
properties: {
o9zor60xpvi: {
_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': '0.21.0-alpha.6',
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': '0.21.0-alpha.6',
'x-uid': 'dnnfz8xyqh9',
'x-async': false,
'x-index': 1,
},
'3rr559rnt6k': {
_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': '0.21.0-alpha.6',
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-designer': 'TableV2.ActionColumnDesigner',
'x-initializer': 'table:configureItemActions',
'x-app-version': '0.21.0-alpha.6',
properties: {
cf7dj1iffh3: {
_isJSONSchemaObject: true,
version: '2.0',
type: 'void',
'x-decorator': 'DndContext',
'x-component': 'Space',
'x-component-props': {
split: '|',
},
'x-app-version': '0.21.0-alpha.6',
properties: {
oealnj6s2rw: {
'x-uid': 'e32p6hfhh35',
_isJSONSchemaObject: true,
version: '2.0',
type: 'void',
title: 'View record',
'x-action': 'view',
'x-toolbar': 'ActionSchemaToolbar',
'x-settings': 'actionSettings:view',
'x-component': 'Action.Link',
'x-component-props': {
openMode: 'drawer',
danger: false,
},
'x-decorator': 'ACLActionProvider',
'x-designer-props': {
linkageAction: true,
},
properties: {
drawer: {
_isJSONSchemaObject: true,
version: '2.0',
type: 'void',
title: '{{ t("View 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("Details")}}',
'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',
'x-uid': 'iuh8edqjzjf',
'x-async': false,
'x-index': 1,
},
},
'x-uid': 'c7s6mbv0ifc',
'x-async': false,
'x-index': 1,
},
},
'x-uid': 'l7mf3lk2rfi',
'x-async': false,
'x-index': 1,
},
},
'x-uid': '96mjki0iss6',
'x-async': false,
'x-index': 1,
},
},
'x-async': false,
'x-index': 1,
},
},
'x-uid': '4wz5e2y5mi3',
'x-async': false,
'x-index': 1,
},
},
'x-uid': 'jursnceu1wj',
'x-async': false,
'x-index': 1,
},
},
'x-uid': 'zysxygy87b3',
'x-async': false,
'x-index': 2,
},
},
'x-uid': '1cz65li561a',
'x-async': false,
'x-index': 1,
},
},
'x-uid': '3p85gkub5m6',
'x-async': false,
'x-index': 1,
},
},
'x-uid': 'gqwx7acdf2r',
'x-async': false,
'x-index': 1,
},
},
'x-uid': 'jzntdh10ctc',
'x-async': false,
'x-index': 1,
},
},
'x-uid': 'jr9hile0rrx',
'x-async': true,
'x-index': 1,
},
};

View File

@ -11,9 +11,10 @@ export const FormBlockInitializer = ({
hideSearch, hideSearch,
createBlockSchema, createBlockSchema,
componentType = 'FormItem', componentType = 'FormItem',
templateWrap, templateWrap: customizeTemplateWrap,
showAssociationFields, showAssociationFields,
hideChildrenIfSingleCollection, hideChildrenIfSingleCollection,
hideOtherRecordsInPopup,
}: { }: {
filterCollections: (options: { collection?: Collection; associationField?: CollectionFieldOptions }) => boolean; filterCollections: (options: { collection?: Collection; associationField?: CollectionFieldOptions }) => boolean;
onlyCurrentDataSource: boolean; onlyCurrentDataSource: boolean;
@ -33,25 +34,22 @@ export const FormBlockInitializer = ({
) => any; ) => any;
showAssociationFields?: boolean; showAssociationFields?: boolean;
hideChildrenIfSingleCollection?: boolean; hideChildrenIfSingleCollection?: boolean;
/**
* Other records
*/
hideOtherRecordsInPopup?: boolean;
}) => { }) => {
const { insert } = useSchemaInitializer();
const itemConfig = useSchemaInitializerItem(); const itemConfig = useSchemaInitializerItem();
const { isCusomeizeCreate } = itemConfig; const { createFormBlock, templateWrap } = useCreateFormBlock();
const onCreateFormBlockSchema = useCallback( const onCreateFormBlockSchema = useCallback(
({ item }) => { (options) => {
if (createBlockSchema) { if (createBlockSchema) {
return createBlockSchema({ item }); return createBlockSchema(options);
} }
insert( createFormBlock(options);
createCreateFormBlockUISchema({
collectionName: item.collectionName || item.name,
dataSource: item.dataSource,
isCusomeizeCreate,
}),
);
}, },
[createBlockSchema, insert, isCusomeizeCreate], [createBlockSchema, createFormBlock],
); );
return ( return (
@ -59,13 +57,42 @@ export const FormBlockInitializer = ({
{...itemConfig} {...itemConfig}
icon={<FormOutlined />} icon={<FormOutlined />}
componentType={componentType} componentType={componentType}
templateWrap={(templateSchema, { item }) => { templateWrap={(templateSchema, options) => {
if (templateWrap) { if (customizeTemplateWrap) {
return templateWrap(templateSchema, { item }); return customizeTemplateWrap(templateSchema, options);
} }
return templateWrap(templateSchema, options);
}}
onCreateBlockSchema={onCreateFormBlockSchema}
filter={filterCollections}
onlyCurrentDataSource={onlyCurrentDataSource}
hideSearch={hideSearch}
showAssociationFields={showAssociationFields}
hideChildrenIfSingleCollection={hideChildrenIfSingleCollection}
hideOtherRecordsInPopup={hideOtherRecordsInPopup}
/>
);
};
export const useCreateFormBlock = () => {
const { insert } = useSchemaInitializer();
const itemConfig = useSchemaInitializerItem();
const { isCusomeizeCreate: isCustomizeCreate } = itemConfig;
const createFormBlock = ({ item }) => {
insert(
createCreateFormBlockUISchema({
collectionName: item.collectionName || item.name,
dataSource: item.dataSource,
isCusomeizeCreate: isCustomizeCreate,
}),
);
};
const templateWrap = (templateSchema, { item }) => {
const schema = createCreateFormBlockUISchema({ const schema = createCreateFormBlockUISchema({
isCusomeizeCreate, isCusomeizeCreate: isCustomizeCreate,
dataSource: item.dataSource, dataSource: item.dataSource,
templateSchema: templateSchema, templateSchema: templateSchema,
collectionName: item.name, collectionName: item.name,
@ -74,13 +101,10 @@ export const FormBlockInitializer = ({
schema['x-template-key'] = item.template.key; schema['x-template-key'] = item.template.key;
} }
return schema; return schema;
}} };
onCreateBlockSchema={onCreateFormBlockSchema}
filter={filterCollections} return {
onlyCurrentDataSource={onlyCurrentDataSource} createFormBlock,
hideSearch={hideSearch} templateWrap,
showAssociationFields={showAssociationFields} };
hideChildrenIfSingleCollection={hideChildrenIfSingleCollection}
/>
);
}; };

View File

@ -1,6 +1,7 @@
import { createBlockInPage, expect, oneEmptyForm, test } from '@nocobase/test/e2e';
import { T3106, T3469 } from './templatesOfBug';
import { uid } from '@formily/shared'; import { uid } from '@formily/shared';
import { createBlockInPage, expect, oneEmptyForm, test } from '@nocobase/test/e2e';
import { oneEmptyTableWithUsers } from '../../../details-multi/__e2e__/templatesOfBug';
import { T3106, T3469 } from './templatesOfBug';
test.describe('where creation form block can be added', () => { test.describe('where creation form block can be added', () => {
test('page', async ({ page, mockPage }) => { test('page', async ({ page, mockPage }) => {
@ -10,6 +11,27 @@ test.describe('where creation form block can be added', () => {
await createBlockInPage(page, 'Form'); await createBlockInPage(page, 'Form');
await expect(page.getByLabel('block-item-CardItem-users-form')).toBeVisible(); await expect(page.getByLabel('block-item-CardItem-users-form')).toBeVisible();
}); });
test('popup', async ({ page, mockPage }) => {
await mockPage(oneEmptyTableWithUsers).goto();
// 1. 打开弹窗,通过 Associated records 创建一个创建表单区块
await page.getByLabel('action-Action.Link-View').click();
await page.getByLabel('schema-initializer-Grid-popup').hover();
await page.getByRole('menuitem', { name: 'form Form (Add new) right' }).hover();
await page.getByRole('menuitem', { name: 'Associated records right' }).hover();
await page.getByRole('menuitem', { name: 'Roles' }).click();
await page.mouse.move(300, 0);
await expect(page.getByLabel('block-item-CardItem-roles-form')).toBeVisible();
// 2. 通过 Other records 创建一个创建表单区块
await page.getByLabel('schema-initializer-Grid-popup').hover();
await page.getByRole('menuitem', { name: 'form Form (Add new) right' }).hover();
await page.getByRole('menuitem', { name: 'Other records right' }).hover();
await page.getByRole('menuitem', { name: 'Users' }).click();
await page.mouse.move(300, 0);
await expect(page.getByLabel('block-item-CardItem-users-form')).toBeVisible();
});
}); });
test.describe('configure fields', () => { test.describe('configure fields', () => {

View File

@ -2,9 +2,9 @@ import { OrderedListOutlined } from '@ant-design/icons';
import React from 'react'; import React from 'react';
import { useSchemaInitializer, useSchemaInitializerItem } from '../../../../application'; import { useSchemaInitializer, useSchemaInitializerItem } from '../../../../application';
import { useCollectionManager_deprecated } from '../../../../collection-manager'; import { useCollectionManager_deprecated } from '../../../../collection-manager';
import { createGridCardBlockSchema } from './createGridCardBlockSchema';
import { DataBlockInitializer } from '../../../../schema-initializer/items/DataBlockInitializer';
import { Collection, CollectionFieldOptions } from '../../../../data-source/collection/Collection'; import { Collection, CollectionFieldOptions } from '../../../../data-source/collection/Collection';
import { DataBlockInitializer } from '../../../../schema-initializer/items/DataBlockInitializer';
import { createGridCardBlockUISchema } from './createGridCardBlockUISchema';
export const GridCardBlockInitializer = ({ export const GridCardBlockInitializer = ({
filterCollections, filterCollections,
@ -33,26 +33,19 @@ export const GridCardBlockInitializer = ({
) => any; ) => any;
showAssociationFields?: boolean; showAssociationFields?: boolean;
}) => { }) => {
const { insert } = useSchemaInitializer();
const { getCollection } = useCollectionManager_deprecated();
const itemConfig = useSchemaInitializerItem(); const itemConfig = useSchemaInitializerItem();
const { createGridCardBlock } = useCreateGridCardBlock();
return ( return (
<DataBlockInitializer <DataBlockInitializer
{...itemConfig} {...itemConfig}
icon={<OrderedListOutlined />} icon={<OrderedListOutlined />}
componentType={'GridCard'} componentType={'GridCard'}
onCreateBlockSchema={async ({ item }) => { onCreateBlockSchema={async (options) => {
if (createBlockSchema) { if (createBlockSchema) {
return createBlockSchema({ item }); return createBlockSchema(options);
} }
createGridCardBlock(options);
const collection = getCollection(item.name, item.dataSource);
const schema = createGridCardBlockSchema({
collectionName: item.name,
dataSource: item.dataSource,
rowKey: collection.filterTargetKey || 'id',
});
insert(schema);
}} }}
onlyCurrentDataSource={onlyCurrentDataSource} onlyCurrentDataSource={onlyCurrentDataSource}
hideSearch={hideSearch} hideSearch={hideSearch}
@ -61,3 +54,20 @@ export const GridCardBlockInitializer = ({
/> />
); );
}; };
export const useCreateGridCardBlock = () => {
const { insert } = useSchemaInitializer();
const { getCollection } = useCollectionManager_deprecated();
const createGridCardBlock = ({ item }) => {
const collection = getCollection(item.name, item.dataSource);
const schema = createGridCardBlockUISchema({
collectionName: item.name,
dataSource: item.dataSource,
rowKey: collection.filterTargetKey || 'id',
});
insert(schema);
};
return { createGridCardBlock };
};

View File

@ -1,4 +1,5 @@
import { createBlockInPage, expect, oneEmptyGridCardBlock, test } from '@nocobase/test/e2e'; import { createBlockInPage, expect, oneEmptyGridCardBlock, test } from '@nocobase/test/e2e';
import { oneEmptyTableWithUsers } from '../../details-multi/__e2e__/templatesOfBug';
test.describe('where grid card block can be added', () => { test.describe('where grid card block can be added', () => {
test('page', async ({ page, mockPage }) => { test('page', async ({ page, mockPage }) => {
@ -9,6 +10,37 @@ test.describe('where grid card block can be added', () => {
await expect(page.getByLabel('block-item-BlockItem-users-grid-card')).toBeVisible(); await expect(page.getByLabel('block-item-BlockItem-users-grid-card')).toBeVisible();
}); });
test('popup', async ({ page, mockPage }) => {
await mockPage(oneEmptyTableWithUsers).goto();
// 1. 打开弹窗,通过 Associated records 创建一个列表区块
await page.getByLabel('action-Action.Link-View').click();
await page.getByLabel('schema-initializer-Grid-popup').hover();
await page.getByRole('menuitem', { name: 'ordered-list Grid Card right' }).hover();
await page.getByRole('menuitem', { name: 'Associated records right' }).hover();
await page.getByRole('menuitem', { name: 'Roles' }).click();
await page.mouse.move(300, 0);
await page.getByLabel('schema-initializer-Grid-').nth(1).hover();
await page.getByRole('menuitem', { name: 'Role name' }).click();
await page.mouse.move(300, 0);
await expect(page.getByText('Root')).toBeVisible();
await expect(page.getByText('Admin')).toBeVisible();
await expect(page.getByText('Member')).toBeVisible();
// 2. 通过 Other records 创建一个列表区块
await page.getByLabel('schema-initializer-Grid-popup').hover();
await page.getByRole('menuitem', { name: 'ordered-list Grid Card right' }).hover();
await page.getByRole('menuitem', { name: 'Other records right' }).hover();
await page.getByRole('menuitem', { name: 'Users' }).click();
await page.mouse.move(300, 0);
await page.getByLabel('schema-initializer-Grid-details:configureFields-users').hover();
await page.getByRole('menuitem', { name: 'Nickname' }).click();
await page.mouse.move(300, 0);
await expect(
page.getByLabel('block-item-CollectionField-users-grid-card-users.nickname-Nickname').getByText('Super Admin'),
).toBeVisible();
});
test('data selector popup', async ({ page, mockPage }) => {}); test('data selector popup', async ({ page, mockPage }) => {});
}); });

View File

@ -1,4 +1,4 @@
import { createGridCardBlockSchema } from '../createGridCardBlockSchema'; import { createGridCardBlockUISchema } from '../createGridCardBlockUISchema';
describe('createGridCardBlockSchema', () => { describe('createGridCardBlockSchema', () => {
test('should return the correct schema', () => { test('should return the correct schema', () => {
@ -10,7 +10,7 @@ describe('createGridCardBlockSchema', () => {
rowKey: 'testRowKey', rowKey: 'testRowKey',
}; };
const schema = createGridCardBlockSchema(options); const schema = createGridCardBlockUISchema(options);
expect(schema).toMatchInlineSnapshot(` expect(schema).toMatchInlineSnapshot(`
{ {
@ -86,7 +86,7 @@ describe('createGridCardBlockSchema', () => {
rowKey: 'testRowKey', rowKey: 'testRowKey',
}; };
const schema = createGridCardBlockSchema(options); const schema = createGridCardBlockUISchema(options);
expect(schema).toMatchInlineSnapshot(` expect(schema).toMatchInlineSnapshot(`
{ {

View File

@ -1,6 +1,6 @@
import { ISchema } from '@formily/react'; import { ISchema } from '@formily/react';
export const createGridCardBlockSchema = (options: { export const createGridCardBlockUISchema = (options: {
dataSource: string; dataSource: string;
collectionName?: string; collectionName?: string;
association?: string; association?: string;

View File

@ -2,9 +2,9 @@ import { OrderedListOutlined } from '@ant-design/icons';
import React from 'react'; import React from 'react';
import { useSchemaInitializer, useSchemaInitializerItem } from '../../../../application'; import { useSchemaInitializer, useSchemaInitializerItem } from '../../../../application';
import { useCollectionManager_deprecated } from '../../../../collection-manager'; import { useCollectionManager_deprecated } from '../../../../collection-manager';
import { createListBlockSchema } from './createListBlockSchema';
import { DataBlockInitializer } from '../../../../schema-initializer/items/DataBlockInitializer';
import { Collection, CollectionFieldOptions } from '../../../../data-source/collection/Collection'; import { Collection, CollectionFieldOptions } from '../../../../data-source/collection/Collection';
import { DataBlockInitializer } from '../../../../schema-initializer/items/DataBlockInitializer';
import { createListBlockUISchema } from './createListBlockUISchema';
export const ListBlockInitializer = ({ export const ListBlockInitializer = ({
filterCollections, filterCollections,
@ -33,26 +33,19 @@ export const ListBlockInitializer = ({
) => any; ) => any;
showAssociationFields?: boolean; showAssociationFields?: boolean;
}) => { }) => {
const { getCollection } = useCollectionManager_deprecated();
const { insert } = useSchemaInitializer();
const itemConfig = useSchemaInitializerItem(); const itemConfig = useSchemaInitializerItem();
const { createListBlock } = useCreateListBlock();
return ( return (
<DataBlockInitializer <DataBlockInitializer
{...itemConfig} {...itemConfig}
icon={<OrderedListOutlined />} icon={<OrderedListOutlined />}
componentType={'List'} componentType={'List'}
onCreateBlockSchema={async ({ item }) => { onCreateBlockSchema={async (options) => {
if (createBlockSchema) { if (createBlockSchema) {
return createBlockSchema({ item }); return createBlockSchema(options);
} }
createListBlock(options);
const collection = getCollection(item.name, item.dataSource);
const schema = createListBlockSchema({
collectionName: item.name,
dataSource: item.dataSource,
rowKey: collection.filterTargetKey || 'id',
});
insert(schema);
}} }}
onlyCurrentDataSource={onlyCurrentDataSource} onlyCurrentDataSource={onlyCurrentDataSource}
hideSearch={hideSearch} hideSearch={hideSearch}
@ -61,3 +54,20 @@ export const ListBlockInitializer = ({
/> />
); );
}; };
export const useCreateListBlock = () => {
const { getCollection } = useCollectionManager_deprecated();
const { insert } = useSchemaInitializer();
const createListBlock = ({ item }) => {
const collection = getCollection(item.name, item.dataSource);
const schema = createListBlockUISchema({
collectionName: item.name,
dataSource: item.dataSource,
rowKey: collection.filterTargetKey || 'id',
});
insert(schema);
};
return { createListBlock };
};

View File

@ -1,4 +1,5 @@
import { createBlockInPage, expect, oneEmptyListBlock, test } from '@nocobase/test/e2e'; import { createBlockInPage, expect, oneEmptyListBlock, test } from '@nocobase/test/e2e';
import { oneEmptyTableWithUsers } from '../../details-multi/__e2e__/templatesOfBug';
test.describe('where list block can be added', () => { test.describe('where list block can be added', () => {
test('page', async ({ page, mockPage }) => { test('page', async ({ page, mockPage }) => {
@ -8,6 +9,37 @@ test.describe('where list block can be added', () => {
await createBlockInPage(page, 'List'); await createBlockInPage(page, 'List');
await expect(page.getByLabel('block-item-CardItem-users-list')).toBeVisible(); await expect(page.getByLabel('block-item-CardItem-users-list')).toBeVisible();
}); });
test('popup', async ({ page, mockPage }) => {
await mockPage(oneEmptyTableWithUsers).goto();
// 1. 打开弹窗,通过 Associated records 创建一个列表区块
await page.getByLabel('action-Action.Link-View').click();
await page.getByLabel('schema-initializer-Grid-popup').hover();
await page.getByRole('menuitem', { name: 'ordered-list List right' }).hover();
await page.getByRole('menuitem', { name: 'Associated records right' }).hover();
await page.getByRole('menuitem', { name: 'Roles' }).click();
await page.mouse.move(300, 0);
await page.getByLabel('schema-initializer-Grid-').nth(1).hover();
await page.getByRole('menuitem', { name: 'Role name' }).click();
await page.mouse.move(300, 0);
await expect(page.getByLabel('block-item-CollectionField-').getByText('Root')).toBeVisible();
await expect(page.getByLabel('block-item-CollectionField-').getByText('Admin')).toBeVisible();
await expect(page.getByLabel('block-item-CollectionField-').getByText('Member')).toBeVisible();
// 2. 通过 Other records 创建一个列表区块
await page.getByLabel('schema-initializer-Grid-popup').hover();
await page.getByRole('menuitem', { name: 'ordered-list List right' }).hover();
await page.getByRole('menuitem', { name: 'Other records right' }).hover();
await page.getByRole('menuitem', { name: 'Users' }).click();
await page.mouse.move(300, 0);
await page.getByLabel('schema-initializer-Grid-details:configureFields-users').hover();
await page.getByRole('menuitem', { name: 'Nickname' }).click();
await page.mouse.move(300, 0);
await expect(
page.getByLabel('block-item-CollectionField-users-list-users.nickname-Nickname').getByText('Super Admin'),
).toBeVisible();
});
}); });
test.describe('configure global actions', () => { test.describe('configure global actions', () => {

View File

@ -1,4 +1,4 @@
import { createListBlockSchema } from '../createListBlockSchema'; import { createListBlockUISchema } from '../createListBlockUISchema';
describe('createListBlockSchema', () => { describe('createListBlockSchema', () => {
test('should return the correct schema', () => { test('should return the correct schema', () => {
@ -20,7 +20,7 @@ describe('createListBlockSchema', () => {
rowKey: 'id', rowKey: 'id',
}; };
const schema = createListBlockSchema(options); const schema = createListBlockUISchema(options);
expect(schema).toMatchInlineSnapshot(` expect(schema).toMatchInlineSnapshot(`
{ {

View File

@ -1,6 +1,6 @@
import { ISchema } from '@formily/react'; import { ISchema } from '@formily/react';
export const createListBlockSchema = (options: { export const createListBlockUISchema = (options: {
dataSource: string; dataSource: string;
collectionName?: string; collectionName?: string;
association?: string; association?: string;

View File

@ -1,9 +1,9 @@
import { TableOutlined } from '@ant-design/icons'; import { TableOutlined } from '@ant-design/icons';
import React from 'react';
import { useSchemaInitializer, useSchemaInitializerItem } from '../../../../application/schema-initializer/context'; import { useSchemaInitializer, useSchemaInitializerItem } from '../../../../application/schema-initializer/context';
import { useCollectionManager_deprecated } from '../../../../collection-manager/hooks/useCollectionManager_deprecated'; import { useCollectionManager_deprecated } from '../../../../collection-manager/hooks/useCollectionManager_deprecated';
import { DataBlockInitializer } from '../../../../schema-initializer/items/DataBlockInitializer';
import React from 'react';
import { Collection, CollectionFieldOptions } from '../../../../data-source/collection/Collection'; import { Collection, CollectionFieldOptions } from '../../../../data-source/collection/Collection';
import { DataBlockInitializer } from '../../../../schema-initializer/items/DataBlockInitializer';
import { createTableBlockUISchema } from './createTableBlockUISchema'; import { createTableBlockUISchema } from './createTableBlockUISchema';
export const TableBlockInitializer = ({ export const TableBlockInitializer = ({
@ -28,26 +28,19 @@ export const TableBlockInitializer = ({
) => any; ) => any;
showAssociationFields?: boolean; showAssociationFields?: boolean;
}) => { }) => {
const { insert } = useSchemaInitializer();
const { getCollection } = useCollectionManager_deprecated();
const itemConfig = useSchemaInitializerItem(); const itemConfig = useSchemaInitializerItem();
const { createTableBlock } = useCreateTableBlock();
return ( return (
<DataBlockInitializer <DataBlockInitializer
{...itemConfig} {...itemConfig}
icon={<TableOutlined />} icon={<TableOutlined />}
componentType={'Table'} componentType={'Table'}
onCreateBlockSchema={async ({ item }) => { onCreateBlockSchema={async (options) => {
if (createBlockSchema) { if (createBlockSchema) {
return createBlockSchema({ item }); return createBlockSchema(options);
} }
createTableBlock(options);
const collection = getCollection(item.name, item.dataSource);
const schema = createTableBlockUISchema({
collectionName: item.name,
dataSource: item.dataSource,
rowKey: collection.filterTargetKey || 'id',
});
insert(schema);
}} }}
onlyCurrentDataSource={onlyCurrentDataSource} onlyCurrentDataSource={onlyCurrentDataSource}
hideSearch={hideSearch} hideSearch={hideSearch}
@ -56,3 +49,20 @@ export const TableBlockInitializer = ({
/> />
); );
}; };
export const useCreateTableBlock = () => {
const { insert } = useSchemaInitializer();
const { getCollection } = useCollectionManager_deprecated();
const createTableBlock = ({ item }) => {
const collection = getCollection(item.name, item.dataSource);
const schema = createTableBlockUISchema({
collectionName: item.name,
dataSource: item.dataSource,
rowKey: collection.filterTargetKey || 'id',
});
insert(schema);
};
return { createTableBlock };
};

View File

@ -50,6 +50,17 @@ test.describe('where table block can be added', () => {
.getByLabel('block-item-CardItem-parentTargetCollection-table') .getByLabel('block-item-CardItem-parentTargetCollection-table')
.getByText(childRecord.parentAssociationField[0].parentTargetText), .getByText(childRecord.parentAssociationField[0].parentTargetText),
).toBeVisible(); ).toBeVisible();
// 通过 Other records 创建一个表格区块
await page.getByLabel('schema-initializer-Grid-popup').hover();
await page.getByRole('menuitem', { name: 'table Table right' }).hover();
await page.getByRole('menuitem', { name: 'Other records right' }).hover();
await page.getByRole('menuitem', { name: 'Users' }).click();
await page.mouse.move(300, 0);
await page.getByLabel('schema-initializer-TableV2-table:configureColumns-users').hover();
await page.getByRole('menuitem', { name: 'Nickname' }).click();
await page.mouse.move(300, 0);
await expect(page.getByRole('button', { name: 'Super Admin' })).toBeVisible();
}); });
}); });

View File

@ -238,9 +238,10 @@ test.describe('add blocks to the popup', () => {
// 打开弹窗 // 打开弹窗
await page.getByLabel('action-Action.Link-View-view-roles-table-root').click(); await page.getByLabel('action-Action.Link-View-view-roles-table-root').click();
// 直接点击 Details 选项创建详情区块 // 点击 Details -> Current record 选项创建详情区块
await page.getByLabel('schema-initializer-Grid-popup').hover(); await page.getByLabel('schema-initializer-Grid-popup').hover();
await page.getByRole('menuitem', { name: 'table Details' }).click(); await page.getByRole('menuitem', { name: 'table Details' }).hover();
await page.getByRole('menuitem', { name: 'Current record' }).click();
await page.getByLabel('schema-initializer-Grid-details:configureFields-roles').hover(); await page.getByLabel('schema-initializer-Grid-details:configureFields-roles').hover();
await page.getByRole('menuitem', { name: 'Role UID' }).click(); await page.getByRole('menuitem', { name: 'Role UID' }).click();
await expect(page.getByLabel('block-item-CollectionField-Role').getByText('root')).toBeVisible(); await expect(page.getByLabel('block-item-CollectionField-Role').getByText('root')).toBeVisible();
@ -251,6 +252,7 @@ test.describe('add blocks to the popup', () => {
await page.getByRole('menuitem', { name: 'form Form (Edit)' }).click(); await page.getByRole('menuitem', { name: 'form Form (Edit)' }).click();
await page.getByLabel('schema-initializer-Grid-form:').hover(); await page.getByLabel('schema-initializer-Grid-form:').hover();
await page.getByRole('menuitem', { name: 'Role UID' }).click(); await page.getByRole('menuitem', { name: 'Role UID' }).click();
await page.mouse.move(300, 0);
await expect( await expect(
page.getByLabel('block-item-CollectionField-roles-form-roles.name-Role UID').getByRole('textbox'), page.getByLabel('block-item-CollectionField-roles-form-roles.name-Role UID').getByRole('textbox'),
).toHaveValue('root'); ).toHaveValue('root');

View File

@ -11,9 +11,14 @@ import {
useCreateAssociationListBlock, useCreateAssociationListBlock,
useCreateAssociationTableBlock, useCreateAssociationTableBlock,
useCreateEditFormBlock, useCreateEditFormBlock,
useCreateFormBlock,
useCreateTableBlock,
} from '../..'; } from '../..';
import { CompatibleSchemaInitializer } from '../../application/schema-initializer/CompatibleSchemaInitializer'; import { CompatibleSchemaInitializer } from '../../application/schema-initializer/CompatibleSchemaInitializer';
import { useCreateDetailsBlock } from '../../modules/blocks/data-blocks/details-multi/DetailsBlockInitializer';
import { useCreateSingleDetailsSchema } from '../../modules/blocks/data-blocks/details-single/RecordReadPrettyFormBlockInitializer'; import { useCreateSingleDetailsSchema } from '../../modules/blocks/data-blocks/details-single/RecordReadPrettyFormBlockInitializer';
import { useCreateGridCardBlock } from '../../modules/blocks/data-blocks/grid-card/GridCardBlockInitializer';
import { useCreateListBlock } from '../../modules/blocks/data-blocks/list/ListBlockInitializer';
import { gridRowColWrap } from '../utils'; import { gridRowColWrap } from '../utils';
const recursiveParent = (schema: Schema) => { const recursiveParent = (schema: Schema) => {
@ -52,9 +57,13 @@ function useRecordBlocks() {
createAssociationDetailsWithoutPagination, createAssociationDetailsWithoutPagination,
templateWrap: templateWrapOfAssociationDetailsWithoutPagination, templateWrap: templateWrapOfAssociationDetailsWithoutPagination,
} = useCreateAssociationDetailsWithoutPagination(); } = useCreateAssociationDetailsWithoutPagination();
const { createDetailsBlock } = useCreateDetailsBlock();
const collectionsNeedToDisplay = [currentCollection, ...collectionsWithView]; const collectionsNeedToDisplay = [currentCollection, ...collectionsWithView];
const createBlockSchema = useCallback( const createBlockSchema = useCallback(
({ item }) => { ({ item, fromOthersInPopup }) => {
if (fromOthersInPopup) {
return createDetailsBlock({ item });
}
if (item.associationField) { if (item.associationField) {
if (['hasOne', 'belongsTo'].includes(item.associationField.type)) { if (['hasOne', 'belongsTo'].includes(item.associationField.type)) {
return createAssociationDetailsWithoutPagination({ item }); return createAssociationDetailsWithoutPagination({ item });
@ -63,7 +72,12 @@ function useRecordBlocks() {
} }
return createSingleDetailsSchema({ item }); return createSingleDetailsSchema({ item });
}, },
[createAssociationDetailsBlock, createAssociationDetailsWithoutPagination, createSingleDetailsSchema], [
createAssociationDetailsBlock,
createAssociationDetailsWithoutPagination,
createDetailsBlock,
createSingleDetailsSchema,
],
); );
return { return {
filterCollections({ collection, associationField }) { filterCollections({ collection, associationField }) {
@ -103,7 +117,7 @@ function useRecordBlocks() {
dataSource: collection.dataSource, dataSource: collection.dataSource,
useComponentProps() { useComponentProps() {
const currentCollection = useCollection_deprecated(); const currentCollection = useCollection_deprecated();
const { createEditFormBlock, templateWrap } = useCreateEditFormBlock(); const { createEditFormBlock, templateWrap: templateWrapEdit } = useCreateEditFormBlock();
const collectionsNeedToDisplay = [currentCollection, ...collectionsWithView]; const collectionsNeedToDisplay = [currentCollection, ...collectionsWithView];
return { return {
@ -115,9 +129,10 @@ function useRecordBlocks() {
}, },
onlyCurrentDataSource: true, onlyCurrentDataSource: true,
hideSearch: true, hideSearch: true,
hideOtherRecordsInPopup: true,
componentType: 'FormItem', componentType: 'FormItem',
createBlockSchema: createEditFormBlock, createBlockSchema: createEditFormBlock,
templateWrap: templateWrap, templateWrap: templateWrapEdit,
showAssociationFields: true, showAssociationFields: true,
}; };
}, },
@ -133,6 +148,7 @@ function useRecordBlocks() {
dataSource: collection.dataSource, dataSource: collection.dataSource,
useComponentProps() { useComponentProps() {
const { createAssociationFormBlock, templateWrap } = useCreateAssociationFormBlock(); const { createAssociationFormBlock, templateWrap } = useCreateAssociationFormBlock();
const { createFormBlock, templateWrap: templateWrapCollection } = useCreateFormBlock();
return { return {
filterCollections({ collection, associationField }) { filterCollections({ collection, associationField }) {
if (associationField) { if (associationField) {
@ -143,38 +159,29 @@ function useRecordBlocks() {
onlyCurrentDataSource: true, onlyCurrentDataSource: true,
hideSearch: true, hideSearch: true,
componentType: 'FormItem', componentType: 'FormItem',
createBlockSchema: createAssociationFormBlock, createBlockSchema: ({ item, fromOthersInPopup }) => {
templateWrap: templateWrap, if (fromOthersInPopup) {
return createFormBlock({ item });
}
createAssociationFormBlock({ item });
},
templateWrap: (templateSchema, { item, fromOthersInPopup }) => {
if (fromOthersInPopup) {
return templateWrapCollection(templateSchema, { item });
}
templateWrap(templateSchema, { item });
},
showAssociationFields: true, showAssociationFields: true,
}; };
}, },
useVisible() {
const collection = useCollection();
return useMemo(
() =>
collection.fields.some(
(field) => canMakeAssociationBlock(field) && ['hasMany', 'belongsToMany'].includes(field.type),
),
[collection.fields],
);
},
}, },
{ {
name: 'table', name: 'table',
title: '{{t("Table")}}', title: '{{t("Table")}}',
Component: 'TableBlockInitializer', Component: 'TableBlockInitializer',
useVisible() {
const collection = useCollection();
return useMemo(
() =>
collection.fields.some(
(field) => canMakeAssociationBlock(field) && ['hasMany', 'belongsToMany'].includes(field.type),
),
[collection.fields],
);
},
useComponentProps() { useComponentProps() {
const { createAssociationTableBlock } = useCreateAssociationTableBlock(); const { createAssociationTableBlock } = useCreateAssociationTableBlock();
const { createTableBlock } = useCreateTableBlock();
return { return {
hideSearch: true, hideSearch: true,
@ -185,7 +192,12 @@ function useRecordBlocks() {
} }
return false; return false;
}, },
createBlockSchema: createAssociationTableBlock, createBlockSchema: ({ item, fromOthersInPopup }) => {
if (fromOthersInPopup) {
return createTableBlock({ item });
}
createAssociationTableBlock({ item });
},
showAssociationFields: true, showAssociationFields: true,
}; };
}, },
@ -194,18 +206,9 @@ function useRecordBlocks() {
name: 'list', name: 'list',
title: '{{t("List")}}', title: '{{t("List")}}',
Component: 'ListBlockInitializer', Component: 'ListBlockInitializer',
useVisible() {
const collection = useCollection();
return useMemo(
() =>
collection.fields.some(
(field) => canMakeAssociationBlock(field) && ['hasMany', 'belongsToMany'].includes(field.type),
),
[collection.fields],
);
},
useComponentProps() { useComponentProps() {
const { createAssociationListBlock } = useCreateAssociationListBlock(); const { createAssociationListBlock } = useCreateAssociationListBlock();
const { createListBlock } = useCreateListBlock();
return { return {
hideSearch: true, hideSearch: true,
@ -216,7 +219,12 @@ function useRecordBlocks() {
} }
return false; return false;
}, },
createBlockSchema: createAssociationListBlock, createBlockSchema: ({ item, fromOthersInPopup }) => {
if (fromOthersInPopup) {
return createListBlock({ item });
}
createAssociationListBlock({ item });
},
showAssociationFields: true, showAssociationFields: true,
}; };
}, },
@ -225,18 +233,9 @@ function useRecordBlocks() {
name: 'gridCard', name: 'gridCard',
title: '{{t("Grid Card")}}', title: '{{t("Grid Card")}}',
Component: 'GridCardBlockInitializer', Component: 'GridCardBlockInitializer',
useVisible() {
const collection = useCollection();
return useMemo(
() =>
collection.fields.some(
(field) => canMakeAssociationBlock(field) && ['hasMany', 'belongsToMany'].includes(field.type),
),
[collection.fields],
);
},
useComponentProps() { useComponentProps() {
const { createAssociationGridCardBlock } = useCreateAssociationGridCardBlock(); const { createAssociationGridCardBlock } = useCreateAssociationGridCardBlock();
const { createGridCardBlock } = useCreateGridCardBlock();
return { return {
hideSearch: true, hideSearch: true,
@ -247,7 +246,12 @@ function useRecordBlocks() {
} }
return false; return false;
}, },
createBlockSchema: createAssociationGridCardBlock, createBlockSchema: ({ item, fromOthersInPopup }) => {
if (fromOthersInPopup) {
return createGridCardBlock({ item });
}
createAssociationGridCardBlock({ item });
},
showAssociationFields: true, showAssociationFields: true,
}; };
}, },

View File

@ -8,11 +8,11 @@ import {
useGetSchemaInitializerMenuItems, useGetSchemaInitializerMenuItems,
useSchemaInitializer, useSchemaInitializer,
} from '../../application'; } from '../../application';
import { DataSource } from '../../data-source';
import { Collection, CollectionFieldOptions } from '../../data-source/collection/Collection'; import { Collection, CollectionFieldOptions } from '../../data-source/collection/Collection';
import { useCompile } from '../../schema-component'; import { useCompile } from '../../schema-component';
import { useSchemaTemplateManager } from '../../schema-templates'; import { useSchemaTemplateManager } from '../../schema-templates';
import { useCollectionDataSourceItems } from '../utils'; import { useCollectionDataSourceItems } from '../utils';
import { DataSource } from '../../data-source';
const MENU_ITEM_HEIGHT = 40; const MENU_ITEM_HEIGHT = 40;
const STEP = 15; const STEP = 15;
@ -260,8 +260,10 @@ export interface DataBlockInitializerProps {
templateSchema: any, templateSchema: any,
{ {
item, item,
fromOthersInPopup,
}: { }: {
item: any; item: any;
fromOthersInPopup?: boolean;
}, },
) => any; ) => any;
onCreateBlockSchema?: (args: any) => void; onCreateBlockSchema?: (args: any) => void;
@ -278,6 +280,14 @@ export interface DataBlockInitializerProps {
/** 如果只有一项数据表时,不显示 children 列表 */ /** 如果只有一项数据表时,不显示 children 列表 */
hideChildrenIfSingleCollection?: boolean; hideChildrenIfSingleCollection?: boolean;
items?: ReturnType<typeof useCollectionDataSourceItems>[]; items?: ReturnType<typeof useCollectionDataSourceItems>[];
/**
* Others
*/
fromOthersInPopup?: boolean;
/**
* Other records
*/
hideOtherRecordsInPopup?: boolean;
} }
export const DataBlockInitializer = (props: DataBlockInitializerProps) => { export const DataBlockInitializer = (props: DataBlockInitializerProps) => {
@ -295,6 +305,8 @@ export const DataBlockInitializer = (props: DataBlockInitializerProps) => {
hideChildrenIfSingleCollection, hideChildrenIfSingleCollection,
filterDataSource, filterDataSource,
items: itemsFromProps, items: itemsFromProps,
fromOthersInPopup,
hideOtherRecordsInPopup,
} = props; } = props;
const { insert, setVisible } = useSchemaInitializer(); const { insert, setVisible } = useSchemaInitializer();
const compile = useCompile(); const compile = useCompile();
@ -303,15 +315,15 @@ export const DataBlockInitializer = (props: DataBlockInitializerProps) => {
async ({ item }) => { async ({ item }) => {
if (item.template) { if (item.template) {
const s = await getTemplateSchemaByMode(item); const s = await getTemplateSchemaByMode(item);
templateWrap ? insert(templateWrap(s, { item })) : insert(s); templateWrap ? insert(templateWrap(s, { item, fromOthersInPopup })) : insert(s);
} else { } else {
if (onCreateBlockSchema) { if (onCreateBlockSchema) {
onCreateBlockSchema({ item }); onCreateBlockSchema({ item, fromOthersInPopup });
} }
} }
setVisible(false); setVisible(false);
}, },
[getTemplateSchemaByMode, insert, onCreateBlockSchema, setVisible, templateWrap], [fromOthersInPopup, getTemplateSchemaByMode, insert, onCreateBlockSchema, setVisible, templateWrap],
); );
const items = const items =
itemsFromProps || itemsFromProps ||
@ -323,6 +335,7 @@ export const DataBlockInitializer = (props: DataBlockInitializerProps) => {
onlyCurrentDataSource, onlyCurrentDataSource,
showAssociationFields, showAssociationFields,
dataBlockInitializerProps: props, dataBlockInitializerProps: props,
hideOtherRecordsInPopup,
}); });
const getMenuItems = useGetSchemaInitializerMenuItems(onClick); const getMenuItems = useGetSchemaInitializerMenuItems(onClick);
const childItems = useMemo(() => { const childItems = useMemo(() => {

View File

@ -1,11 +1,11 @@
import { TableOutlined } from '@ant-design/icons'; import { TableOutlined } from '@ant-design/icons';
import React, { useCallback } from 'react'; import React, { useCallback } from 'react';
import { useCollectionManager_deprecated } from '../../collection-manager';
import { SchemaInitializerItem, useSchemaInitializer, useSchemaInitializerItem } from '../../application'; import { SchemaInitializerItem, useSchemaInitializer, useSchemaInitializerItem } from '../../application';
import { useCollectionManager_deprecated } from '../../collection-manager';
import { createGridCardBlockUISchema } from '../../modules/blocks/data-blocks/grid-card/createGridCardBlockUISchema';
import { useSchemaTemplateManager } from '../../schema-templates'; import { useSchemaTemplateManager } from '../../schema-templates';
import { useRecordCollectionDataSourceItems } from '../utils'; import { useRecordCollectionDataSourceItems } from '../utils';
import { createGridCardBlockSchema } from '../../modules/blocks/data-blocks/grid-card/createGridCardBlockSchema';
/** /**
* @deprecated * @deprecated
@ -30,7 +30,7 @@ export const RecordAssociationGridCardBlockInitializer = () => {
insert(s); insert(s);
} else { } else {
insert( insert(
createGridCardBlockSchema({ createGridCardBlockUISchema({
rowKey: collection.filterTargetKey, rowKey: collection.filterTargetKey,
dataSource: collection.dataSource, dataSource: collection.dataSource,
association: resource, association: resource,
@ -53,7 +53,7 @@ export function useCreateAssociationGridCardBlock() {
const collection = getCollection(field.target); const collection = getCollection(field.target);
insert( insert(
createGridCardBlockSchema({ createGridCardBlockUISchema({
rowKey: collection.filterTargetKey, rowKey: collection.filterTargetKey,
dataSource: collection.dataSource, dataSource: collection.dataSource,
association: `${field.collectionName}.${field.name}`, association: `${field.collectionName}.${field.name}`,

View File

@ -3,9 +3,9 @@ import React, { useCallback } from 'react';
import { SchemaInitializerItem, useSchemaInitializer, useSchemaInitializerItem } from '../../application'; import { SchemaInitializerItem, useSchemaInitializer, useSchemaInitializerItem } from '../../application';
import { useCollectionManager_deprecated } from '../../collection-manager'; import { useCollectionManager_deprecated } from '../../collection-manager';
import { createListBlockUISchema } from '../../modules/blocks/data-blocks/list/createListBlockUISchema';
import { useSchemaTemplateManager } from '../../schema-templates'; import { useSchemaTemplateManager } from '../../schema-templates';
import { useRecordCollectionDataSourceItems } from '../utils'; import { useRecordCollectionDataSourceItems } from '../utils';
import { createListBlockSchema } from '../../modules/blocks/data-blocks/list/createListBlockSchema';
export const RecordAssociationListBlockInitializer = () => { export const RecordAssociationListBlockInitializer = () => {
const itemConfig = useSchemaInitializerItem(); const itemConfig = useSchemaInitializerItem();
@ -27,7 +27,7 @@ export const RecordAssociationListBlockInitializer = () => {
insert(s); insert(s);
} else { } else {
insert( insert(
createListBlockSchema({ createListBlockUISchema({
rowKey: collection.filterTargetKey, rowKey: collection.filterTargetKey,
dataSource: collection.dataSource, dataSource: collection.dataSource,
association: resource, association: resource,
@ -50,7 +50,7 @@ export function useCreateAssociationListBlock() {
const collection = getCollection(field.target); const collection = getCollection(field.target);
insert( insert(
createListBlockSchema({ createListBlockUISchema({
rowKey: collection.filterTargetKey, rowKey: collection.filterTargetKey,
dataSource: collection.dataSource, dataSource: collection.dataSource,
association: `${field.collectionName}.${field.name}`, association: `${field.collectionName}.${field.name}`,

View File

@ -842,6 +842,7 @@ export const useCollectionDataSourceItems = ({
showAssociationFields, showAssociationFields,
filterDataSource, filterDataSource,
dataBlockInitializerProps, dataBlockInitializerProps,
hideOtherRecordsInPopup,
}: { }: {
componentName; componentName;
filter?: (options: { collection?: Collection; associationField?: CollectionFieldOptions }) => boolean; filter?: (options: { collection?: Collection; associationField?: CollectionFieldOptions }) => boolean;
@ -849,6 +850,10 @@ export const useCollectionDataSourceItems = ({
showAssociationFields?: boolean; showAssociationFields?: boolean;
filterDataSource?: (dataSource?: DataSource) => boolean; filterDataSource?: (dataSource?: DataSource) => boolean;
dataBlockInitializerProps?: any; dataBlockInitializerProps?: any;
/**
* Other records
*/
hideOtherRecordsInPopup?: boolean;
}) => { }) => {
const { t } = useTranslation(); const { t } = useTranslation();
const dm = useDataSourceManager(); const dm = useDataSourceManager();
@ -936,11 +941,41 @@ export const useCollectionDataSourceItems = ({
], ],
}, },
}; };
const componentTypeMap = {
ReadPrettyFormItem: 'Details',
};
const otherRecords = {
name: 'otherRecords',
Component: DataBlockInitializer,
// 目的是使点击无效
onClick() {},
componentProps: {
icon: null,
title: t('Other records'),
name: 'otherRecords',
showAssociationFields: false,
onlyCurrentDataSource: false,
hideChildrenIfSingleCollection: false,
onCreateBlockSchema: dataBlockInitializerProps.onCreateBlockSchema,
fromOthersInPopup: true,
componentType: componentTypeMap[componentName] || componentName,
filter({ collection: c, associationField }) {
return true;
},
},
};
let children; let children;
const _associationRecords = associationFields.length ? associationRecords : null;
if (noAssociationMenu[0].children.length && associationFields.length) { if (noAssociationMenu[0].children.length && associationFields.length) {
children = [currentRecord, associationRecords]; if (hideOtherRecordsInPopup) {
children = [currentRecord, _associationRecords];
} else {
children = [currentRecord, _associationRecords, otherRecords];
}
} else if (noAssociationMenu[0].children.length) { } else if (noAssociationMenu[0].children.length) {
if (hideOtherRecordsInPopup) {
// 当可选数据表只有一个时,实现只点击一次区块 menu 就能创建区块 // 当可选数据表只有一个时,实现只点击一次区块 menu 就能创建区块
if (noAssociationMenu[0].children.length <= 1) { if (noAssociationMenu[0].children.length <= 1) {
noAssociationMenu[0].children = (noAssociationMenu[0].children[0]?.children as any) || []; noAssociationMenu[0].children = (noAssociationMenu[0].children[0]?.children as any) || [];
@ -948,7 +983,14 @@ export const useCollectionDataSourceItems = ({
} }
children = [currentRecord]; children = [currentRecord];
} else { } else {
children = [associationRecords]; children = [currentRecord, otherRecords];
}
} else {
if (hideOtherRecordsInPopup) {
children = [_associationRecords];
} else {
children = [_associationRecords, otherRecords];
}
} }
return [ return [
@ -956,10 +998,19 @@ export const useCollectionDataSourceItems = ({
name: 'records', name: 'records',
label: t('Records'), label: t('Records'),
type: 'subMenu', type: 'subMenu',
children, children: children.filter(Boolean),
}, },
]; ];
}, [associationFields, collection.dataSource, collection.name, dataBlockInitializerProps, noAssociationMenu, t]); }, [
associationFields,
collection.dataSource,
collection.name,
componentName,
dataBlockInitializerProps,
hideOtherRecordsInPopup,
noAssociationMenu,
t,
]);
} }
return noAssociationMenu; return noAssociationMenu;

View File

@ -1,4 +1,4 @@
import { test, expect } from '@nocobase/test/e2e'; import { expect, test } from '@nocobase/test/e2e';
import { emptyPageWithCalendarCollection, oneTableWithCalendarCollection } from './templates'; import { emptyPageWithCalendarCollection, oneTableWithCalendarCollection } from './templates';
test.describe('where can be added', () => { test.describe('where can be added', () => {
@ -18,7 +18,7 @@ test.describe('where can be added', () => {
test('association block in popup', async ({ page, mockPage, mockRecord }) => { test('association block in popup', async ({ page, mockPage, mockRecord }) => {
await mockPage(oneTableWithCalendarCollection).goto(); await mockPage(oneTableWithCalendarCollection).goto();
await mockRecord('toManyCalendar'); const record = await mockRecord('toManyCalendar');
// 打开弹窗 // 打开弹窗
await page.getByLabel('action-Action.Link-View-view-').first().click(); await page.getByLabel('action-Action.Link-View-view-').first().click();
@ -32,5 +32,16 @@ test.describe('where can be added', () => {
await page.getByRole('button', { name: 'OK', exact: true }).click(); await page.getByRole('button', { name: 'OK', exact: true }).click();
await expect(page.getByLabel('block-item-CardItem-calendar-').getByText('Sun', { exact: true })).toBeVisible(); await expect(page.getByLabel('block-item-CardItem-calendar-').getByText('Sun', { exact: true })).toBeVisible();
// 通过 Other records 创建一个日历区块
await page.getByLabel('schema-initializer-Grid-popup').hover();
await page.getByRole('menuitem', { name: 'form Calendar right' }).hover();
await page.getByRole('menuitem', { name: 'Other records right' }).hover();
await page.getByRole('menuitem', { name: 'calendar', exact: true }).click();
await page.mouse.move(300, 0);
await page.getByLabel('block-item-Select-Title field').getByTestId('select-single').click();
await page.getByRole('option', { name: 'title' }).click();
await page.getByRole('button', { name: 'OK', exact: true }).click();
await expect(page.getByText(record.manyToMany[0].title).first()).toBeVisible();
}); });
}); });

View File

@ -1,8 +1,9 @@
import { Plugin, canMakeAssociationBlock, useCollection } from '@nocobase/client'; import { Plugin } from '@nocobase/client';
import { generateNTemplate } from '../locale'; import { generateNTemplate } from '../locale';
import { CalendarV2 } from './calendar'; import { CalendarV2 } from './calendar';
import { calendarBlockSettings } from './calendar/Calender.Settings'; import { calendarBlockSettings } from './calendar/Calender.Settings';
import { CalendarCollectionTemplate } from './collection-templates/calendar'; import { CalendarCollectionTemplate } from './collection-templates/calendar';
import { useCalendarBlockDecoratorProps } from './hooks/useCalendarBlockDecoratorProps';
import { CalendarBlockProvider, useCalendarBlockProps } from './schema-initializer/CalendarBlockProvider'; import { CalendarBlockProvider, useCalendarBlockProps } from './schema-initializer/CalendarBlockProvider';
import { import {
CalendarActionInitializers_deprecated, CalendarActionInitializers_deprecated,
@ -14,9 +15,8 @@ import {
CalendarBlockInitializer, CalendarBlockInitializer,
RecordAssociationCalendarBlockInitializer, RecordAssociationCalendarBlockInitializer,
useCreateAssociationCalendarBlock, useCreateAssociationCalendarBlock,
useCreateCalendarBlock,
} from './schema-initializer/items'; } from './schema-initializer/items';
import { useMemo } from 'react';
import { useCalendarBlockDecoratorProps } from './hooks/useCalendarBlockDecoratorProps';
export class PluginCalendarClient extends Plugin { export class PluginCalendarClient extends Plugin {
async load() { async load() {
@ -28,18 +28,9 @@ export class PluginCalendarClient extends Plugin {
this.app.schemaInitializerManager.addItem('popup:common:addBlock', 'dataBlocks.calendar', { this.app.schemaInitializerManager.addItem('popup:common:addBlock', 'dataBlocks.calendar', {
title: generateNTemplate('Calendar'), title: generateNTemplate('Calendar'),
Component: 'CalendarBlockInitializer', Component: 'CalendarBlockInitializer',
useVisible() {
const collection = useCollection();
return useMemo(
() =>
collection.fields.some(
(field) => canMakeAssociationBlock(field) && ['hasMany', 'belongsToMany'].includes(field.type),
),
[collection.fields],
);
},
useComponentProps() { useComponentProps() {
const { createAssociationCalendarBlock } = useCreateAssociationCalendarBlock(); const { createAssociationCalendarBlock } = useCreateAssociationCalendarBlock();
const { createCalendarBlock } = useCreateCalendarBlock();
return { return {
onlyCurrentDataSource: true, onlyCurrentDataSource: true,
@ -49,7 +40,12 @@ export class PluginCalendarClient extends Plugin {
} }
return false; return false;
}, },
createBlockSchema: createAssociationCalendarBlock, createBlockSchema: ({ item, fromOthersInPopup }) => {
if (fromOthersInPopup) {
return createCalendarBlock({ item });
}
createAssociationCalendarBlock({ item });
},
showAssociationFields: true, showAssociationFields: true,
hideSearch: true, hideSearch: true,
}; };

View File

@ -14,8 +14,8 @@ import {
useSchemaInitializerItem, useSchemaInitializerItem,
} from '@nocobase/client'; } from '@nocobase/client';
import React, { useContext } from 'react'; import React, { useContext } from 'react';
import { createCalendarBlockUISchema } from '../createCalendarBlockUISchema';
import { useTranslation } from '../../../locale'; import { useTranslation } from '../../../locale';
import { createCalendarBlockUISchema } from '../createCalendarBlockUISchema';
export const CalendarBlockInitializer = ({ export const CalendarBlockInitializer = ({
filterCollections, filterCollections,
@ -30,23 +30,36 @@ export const CalendarBlockInitializer = ({
createBlockSchema?: (options: any) => any; createBlockSchema?: (options: any) => any;
showAssociationFields?: boolean; showAssociationFields?: boolean;
}) => { }) => {
const { insert } = useSchemaInitializer();
const { t } = useTranslation();
const { getCollectionField, getCollectionFieldsOptions } = useCollectionManager_deprecated();
const options = useContext(SchemaOptionsContext);
const { theme } = useGlobalTheme();
const itemConfig = useSchemaInitializerItem(); const itemConfig = useSchemaInitializerItem();
const { createCalendarBlock } = useCreateCalendarBlock();
return ( return (
<DataBlockInitializer <DataBlockInitializer
{...itemConfig} {...itemConfig}
componentType={'Calendar'} componentType={'Calendar'}
icon={<FormOutlined />} icon={<FormOutlined />}
onCreateBlockSchema={async ({ item }) => { onCreateBlockSchema={async (options) => {
if (createBlockSchema) { if (createBlockSchema) {
return createBlockSchema({ item }); return createBlockSchema(options);
} }
createCalendarBlock(options);
}}
onlyCurrentDataSource={onlyCurrentDataSource}
hideSearch={hideSearch}
filter={filterCollections}
showAssociationFields={showAssociationFields}
/>
);
};
export const useCreateCalendarBlock = () => {
const { insert } = useSchemaInitializer();
const { t } = useTranslation();
const { getCollectionField, getCollectionFieldsOptions } = useCollectionManager_deprecated();
const options = useContext(SchemaOptionsContext);
const { theme } = useGlobalTheme();
const createCalendarBlock = async ({ item }) => {
const stringFieldsOptions = getCollectionFieldsOptions(item.name, 'string', { dataSource: item.dataSource }); const stringFieldsOptions = getCollectionFieldsOptions(item.name, 'string', { dataSource: item.dataSource });
const dateFieldsOptions = getCollectionFieldsOptions(item.name, 'date', { const dateFieldsOptions = getCollectionFieldsOptions(item.name, 'date', {
association: ['o2o', 'obo', 'oho', 'm2o'], association: ['o2o', 'obo', 'oho', 'm2o'],
@ -103,11 +116,7 @@ export const CalendarBlockInitializer = ({
}, },
}), }),
); );
}} };
onlyCurrentDataSource={onlyCurrentDataSource}
hideSearch={hideSearch} return { createCalendarBlock };
filter={filterCollections}
showAssociationFields={showAssociationFields}
/>
);
}; };