mirror of
https://github.com/nocobase/nocobase.git
synced 2026-09-19 02:23:00 +08:00
fix(data-source-manager): respect non-deletable v2 template fields (#9940)
* fix(data-source-manager): respect non-deletable v2 template fields * fix(data-source-manager): align v2 inherited field actions * fix(data-source-manager): fix v2 fields declaration build
This commit is contained in:
+11
-3
@@ -66,6 +66,7 @@ interface FieldFormProps {
|
||||
collection: Record<string, any>;
|
||||
interfaceName?: string;
|
||||
field?: Record<string, any>;
|
||||
override?: boolean;
|
||||
onSubmitted: () => void;
|
||||
}
|
||||
|
||||
@@ -1811,9 +1812,11 @@ export function FieldForm(props: FieldFormProps) {
|
||||
createMainOnly: props.mode === 'create' && props.dataSourceKey === 'main',
|
||||
disabledJSONB: props.mode === 'edit',
|
||||
isDialect: (dialect: string) => databaseDialect === dialect,
|
||||
isOverride: !!props.override,
|
||||
override: !!props.override,
|
||||
primaryKeyOnly: false,
|
||||
}),
|
||||
[databaseDialect, props.dataSourceKey, props.mode],
|
||||
[databaseDialect, props.dataSourceKey, props.mode, props.override],
|
||||
);
|
||||
const previousWatchedValuesRef = useRef<Record<string, any>>();
|
||||
const pendingInitialValuesRef = useRef<Record<string, any>>();
|
||||
@@ -2047,7 +2050,9 @@ export function FieldForm(props: FieldFormProps) {
|
||||
]);
|
||||
|
||||
const collectionTitle = compileLegacyTemplateText(get(props.collection, 'title') || props.collection.name, t);
|
||||
const title = `${collectionTitle} - ${props.mode === 'create' ? t('Add field') : t('Edit field')}`;
|
||||
const title = `${collectionTitle} - ${
|
||||
props.override ? t('Override field') : props.mode === 'create' ? t('Add field') : t('Edit field')
|
||||
}`;
|
||||
const existingFieldNames = new Set((props.collection.fields || []).map((field: Record<string, any>) => field.name));
|
||||
|
||||
return (
|
||||
@@ -2102,7 +2107,10 @@ export function FieldForm(props: FieldFormProps) {
|
||||
]}
|
||||
extra={t(fieldNameDescription)}
|
||||
>
|
||||
<Input autoComplete="off" disabled={props.mode === 'edit' || interfaceName === 'tableoid'} />
|
||||
<Input
|
||||
autoComplete="off"
|
||||
disabled={props.mode === 'edit' || props.override || interfaceName === 'tableoid'}
|
||||
/>
|
||||
</Form.Item>
|
||||
<FieldConfigureItemsRenderer
|
||||
items={mainConfigureItems}
|
||||
|
||||
+300
-15
@@ -16,6 +16,7 @@ import {
|
||||
App,
|
||||
Button,
|
||||
Cascader,
|
||||
Descriptions,
|
||||
Dropdown,
|
||||
Form,
|
||||
Input,
|
||||
@@ -26,6 +27,7 @@ import {
|
||||
Table as AntdTable,
|
||||
Tag,
|
||||
Tooltip,
|
||||
Typography,
|
||||
} from 'antd';
|
||||
import type { MenuProps } from 'antd';
|
||||
import type { ColumnsType } from 'antd/es/table';
|
||||
@@ -77,6 +79,13 @@ type ViewFieldRecord = {
|
||||
[key: string]: any;
|
||||
};
|
||||
|
||||
type InheritedFieldGroup = {
|
||||
collectionName: string;
|
||||
fields: Record<string, any>[];
|
||||
key: string;
|
||||
title?: React.ReactNode;
|
||||
};
|
||||
|
||||
type ViewFieldsResponse = {
|
||||
fields?: ViewFieldRecord[] | Record<string, ViewFieldRecord>;
|
||||
sources?: string[];
|
||||
@@ -293,6 +302,30 @@ function EditableFieldDisplayNameCell(props: {
|
||||
);
|
||||
}
|
||||
|
||||
function InheritedFieldDetails(props: { field: Record<string, any>; fieldInterface?: FieldInterfaceOption }) {
|
||||
const t = useT();
|
||||
const field = props.field;
|
||||
const fieldInterfaceTitle = getFieldInterfaceLabel(props.fieldInterface, field.interface);
|
||||
|
||||
return (
|
||||
<Descriptions column={1} bordered size="small">
|
||||
<Descriptions.Item label={t('Field display name')}>
|
||||
{compileLegacyTemplate(getEditableFieldDisplayName(field), t)}
|
||||
</Descriptions.Item>
|
||||
<Descriptions.Item label={t('Field name')}>{field.name}</Descriptions.Item>
|
||||
<Descriptions.Item label={t('Field type')}>
|
||||
<Tag>{field.type}</Tag>
|
||||
</Descriptions.Item>
|
||||
<Descriptions.Item label={t('Field interface')}>
|
||||
<Tag>{compileLegacyTemplate(fieldInterfaceTitle, t)}</Tag>
|
||||
</Descriptions.Item>
|
||||
{field.description ? (
|
||||
<Descriptions.Item label={t('Description')}>{compileLegacyTemplate(field.description, t)}</Descriptions.Item>
|
||||
) : null}
|
||||
</Descriptions>
|
||||
);
|
||||
}
|
||||
|
||||
function isReadOnlyFieldInterface(
|
||||
record: Record<string, any>,
|
||||
options: {
|
||||
@@ -388,6 +421,82 @@ function isSyncFieldsVisible(dataSourceKey: string, collection: Record<string, a
|
||||
return dataSourceKey === 'main';
|
||||
}
|
||||
|
||||
function getCollectionTemplateFields(collectionTemplate?: Record<string, any>) {
|
||||
const fields = collectionTemplate?.collection?.fields;
|
||||
return typeof fields === 'function' ? fields() : Array.isArray(fields) ? fields : [];
|
||||
}
|
||||
|
||||
export function isFieldDeleteDisabled(record: Record<string, any>, collectionTemplate?: Record<string, any>) {
|
||||
if (record.deletable === false) {
|
||||
return true;
|
||||
}
|
||||
return getCollectionTemplateFields(collectionTemplate).some(
|
||||
(field: Record<string, any>) => field?.name === record.name && field.deletable === false,
|
||||
);
|
||||
}
|
||||
|
||||
function normalizeRuntimeField(field: any, collectionName?: string) {
|
||||
const options = field?.options || field || {};
|
||||
return {
|
||||
...options,
|
||||
collectionName: options.collectionName || field?.collectionName || field?.collection?.name || collectionName,
|
||||
};
|
||||
}
|
||||
|
||||
function getRuntimeCollectionOwnFields(collection: any) {
|
||||
if (!collection) {
|
||||
return [];
|
||||
}
|
||||
if (collection.fields instanceof Map) {
|
||||
return Array.from(collection.fields.values()).map((field) => normalizeRuntimeField(field, collection.name));
|
||||
}
|
||||
return Array.isArray(collection.fields)
|
||||
? collection.fields.map((field) => normalizeRuntimeField(field, collection.name))
|
||||
: [];
|
||||
}
|
||||
|
||||
export function getInheritedFieldGroups(collection: any) {
|
||||
const groups: InheritedFieldGroup[] = [];
|
||||
const visitedCollections = new Set<string>();
|
||||
|
||||
const visitParentCollections = (currentCollection: any) => {
|
||||
const parentCollections =
|
||||
currentCollection?.inherits instanceof Map ? Array.from(currentCollection.inherits.values()) : [];
|
||||
parentCollections.forEach((parentCollection: any) => {
|
||||
if (!parentCollection?.name || visitedCollections.has(parentCollection.name)) {
|
||||
return;
|
||||
}
|
||||
visitedCollections.add(parentCollection.name);
|
||||
groups.push({
|
||||
collectionName: parentCollection.name,
|
||||
fields: getRuntimeCollectionOwnFields(parentCollection),
|
||||
key: parentCollection.name,
|
||||
title: parentCollection.title || parentCollection.options?.title || parentCollection.name,
|
||||
});
|
||||
visitParentCollections(parentCollection);
|
||||
});
|
||||
};
|
||||
|
||||
visitParentCollections(collection);
|
||||
return groups;
|
||||
}
|
||||
|
||||
export function isInheritedFieldOverridden(
|
||||
record: Record<string, any>,
|
||||
currentCollectionName: string,
|
||||
currentFieldsByName: Map<string, Record<string, any>>,
|
||||
) {
|
||||
const currentField = currentFieldsByName.get(record.name);
|
||||
return Boolean(
|
||||
currentField && (!currentField.collectionName || currentField.collectionName === currentCollectionName),
|
||||
);
|
||||
}
|
||||
|
||||
function getOverrideInitialField(record: Record<string, any>) {
|
||||
const { collectionName, key, sort, uiSchemaUid, ...rest } = record;
|
||||
return rest;
|
||||
}
|
||||
|
||||
function isAddFieldVisible(options: {
|
||||
collection: Record<string, any>;
|
||||
dataSourceType?: string;
|
||||
@@ -926,9 +1035,18 @@ export default function FieldsPage(props: FieldsPageProps) {
|
||||
}, {});
|
||||
}, [databaseDialect, ctx, dataSource?.options?.type, props.collection]);
|
||||
const presetFieldInterfaces = useMemo(() => getCollectionPresetFieldInterfaces(ctx), [ctx]);
|
||||
const collectionTemplate = useMemo(() => getCollectionTemplate(ctx, props.collection), [ctx, props.collection]);
|
||||
const currentFieldsByName = useMemo(() => {
|
||||
return new Map((request.data || []).map((field: Record<string, any>) => [String(field.name), field]));
|
||||
}, [request.data]);
|
||||
|
||||
const openFieldForm = useCallback(
|
||||
(mode: 'create' | 'edit', field?: Record<string, any>, interfaceName?: string) => {
|
||||
(
|
||||
mode: 'create' | 'edit',
|
||||
field?: Record<string, any>,
|
||||
interfaceName?: string,
|
||||
options?: { override?: boolean },
|
||||
) => {
|
||||
ctx.viewer.drawer({
|
||||
width: 800,
|
||||
closable: true,
|
||||
@@ -939,6 +1057,7 @@ export default function FieldsPage(props: FieldsPageProps) {
|
||||
collection={props.collection}
|
||||
interfaceName={interfaceName}
|
||||
field={field}
|
||||
override={options?.override}
|
||||
onSubmitted={() => request.refresh()}
|
||||
/>
|
||||
),
|
||||
@@ -967,8 +1086,9 @@ export default function FieldsPage(props: FieldsPageProps) {
|
||||
[fieldInterfaceGroups, openFieldForm, t],
|
||||
);
|
||||
|
||||
const collectionTemplate = useMemo(() => getCollectionTemplate(ctx, props.collection), [ctx, props.collection]);
|
||||
const TemplateSyncFieldsDrawer = collectionTemplate?.configure?.syncFields?.Component;
|
||||
const runtimeCollection = dataSource?.collectionManager?.getCollection?.(props.collection.name);
|
||||
const inheritedFieldGroups = getInheritedFieldGroups(runtimeCollection);
|
||||
|
||||
const openTemplateSyncFieldsDrawer = useCallback(() => {
|
||||
if (!TemplateSyncFieldsDrawer) {
|
||||
@@ -1007,14 +1127,45 @@ export default function FieldsPage(props: FieldsPageProps) {
|
||||
});
|
||||
}, [allFieldInterfaceGroups, ctx.viewer, fieldInterfacesByName, props.collection, request]);
|
||||
|
||||
const openInheritedFieldView = useCallback(
|
||||
(record: Record<string, any>, parentTitle?: React.ReactNode) => {
|
||||
const fieldInterface = record.interface ? fieldInterfacesByName[record.interface] : undefined;
|
||||
ctx.viewer.drawer({
|
||||
title: `${compileLegacyTemplate(parentTitle || record.collectionName || props.collection.name, t)} - ${t(
|
||||
'View',
|
||||
)}`,
|
||||
width: 520,
|
||||
closable: true,
|
||||
content: () => <InheritedFieldDetails field={record} fieldInterface={fieldInterface} />,
|
||||
});
|
||||
},
|
||||
[ctx.viewer, fieldInterfacesByName, props.collection.name, t],
|
||||
);
|
||||
|
||||
const openInheritedFieldOverride = useCallback(
|
||||
(record: Record<string, any>) => {
|
||||
if (isInheritedFieldOverridden(record, props.collection.name, currentFieldsByName)) {
|
||||
return;
|
||||
}
|
||||
openFieldForm('create', getOverrideInitialField(record), record.interface, { override: true });
|
||||
},
|
||||
[currentFieldsByName, openFieldForm, props.collection.name],
|
||||
);
|
||||
|
||||
const handleDelete = useCallback(
|
||||
(filterByTk: React.Key | React.Key[]) => {
|
||||
const keys = (Array.isArray(filterByTk) ? filterByTk : [filterByTk]).filter((key) => {
|
||||
const record = currentFieldsByName.get(String(key));
|
||||
return record && !isFieldDeleteDisabled(record, collectionTemplate);
|
||||
});
|
||||
if (!keys.length) {
|
||||
return;
|
||||
}
|
||||
modal.confirm({
|
||||
title: t('Delete record'),
|
||||
content: t('Are you sure you want to delete it?'),
|
||||
async onOk() {
|
||||
try {
|
||||
const keys = Array.isArray(filterByTk) ? filterByTk : [filterByTk];
|
||||
await Promise.all(
|
||||
keys.map((key) =>
|
||||
ctx.api.request({
|
||||
@@ -1035,7 +1186,18 @@ export default function FieldsPage(props: FieldsPageProps) {
|
||||
},
|
||||
});
|
||||
},
|
||||
[ctx.api, ctx.dataSourceManager, modal, notification, props.collection.name, props.dataSourceKey, request, t],
|
||||
[
|
||||
collectionTemplate,
|
||||
ctx.api,
|
||||
ctx.dataSourceManager,
|
||||
currentFieldsByName,
|
||||
modal,
|
||||
notification,
|
||||
props.collection.name,
|
||||
props.dataSourceKey,
|
||||
request,
|
||||
t,
|
||||
],
|
||||
);
|
||||
|
||||
const handleFieldInterfaceChange = useCallback(
|
||||
@@ -1145,6 +1307,102 @@ export default function FieldsPage(props: FieldsPageProps) {
|
||||
[ctx, message, notification, props, t],
|
||||
);
|
||||
|
||||
const syncFieldsVisible = isSyncFieldsVisible(props.dataSourceKey, props.collection, ctx);
|
||||
const configureFieldsDisabled = Boolean(dataSourceType?.disableConfigureFields);
|
||||
const fieldDeletionVisible = isMainDataSource && !configureFieldsDisabled;
|
||||
const addFieldVisible = isAddFieldVisible({
|
||||
collection: props.collection,
|
||||
ctx,
|
||||
dataSourceType: dataSource?.options?.type,
|
||||
fieldInterfaceGroups,
|
||||
});
|
||||
|
||||
const inheritedFieldColumns = useMemo<ColumnsType<Record<string, any>>>(
|
||||
() => [
|
||||
{
|
||||
title: t('Field display name'),
|
||||
dataIndex: ['uiSchema', 'title'],
|
||||
width: 240,
|
||||
render: (_, record) => compileLegacyTemplate(getEditableFieldDisplayName(record), t),
|
||||
},
|
||||
{ title: t('Field name'), dataIndex: 'name' },
|
||||
{ title: t('Field type'), dataIndex: 'type', render: (value) => <Tag>{value}</Tag> },
|
||||
{
|
||||
title: t('Field interface'),
|
||||
dataIndex: 'interface',
|
||||
width: 180,
|
||||
render: (value) => (
|
||||
<Tag>{compileLegacyTemplate(getFieldInterfaceLabel(fieldInterfacesByName[value], value), t)}</Tag>
|
||||
),
|
||||
},
|
||||
{
|
||||
title: t('Title field'),
|
||||
dataIndex: 'titleField',
|
||||
width: 120,
|
||||
render: (_, record) =>
|
||||
isTitleField(ctx.dataSourceManager, record) ? (
|
||||
<Tooltip title={t('Default title for each record')} placement="right">
|
||||
<Switch
|
||||
aria-label={`switch-title-field-${record.name}`}
|
||||
size="small"
|
||||
disabled={configureFieldsDisabled}
|
||||
loading={record.name === titleFieldLoadingKey}
|
||||
checked={record.name === (titleField || 'id')}
|
||||
onChange={(checked) => handleTitleFieldChange(record, checked)}
|
||||
/>
|
||||
</Tooltip>
|
||||
) : null,
|
||||
},
|
||||
{ title: t('Description'), dataIndex: 'description', ellipsis: true },
|
||||
{
|
||||
title: t('Actions'),
|
||||
width: 160,
|
||||
render: (_, record) => {
|
||||
const overridden = isInheritedFieldOverridden(record, props.collection.name, currentFieldsByName);
|
||||
return (
|
||||
<Space>
|
||||
{configureFieldsDisabled ? null : (
|
||||
<Typography.Link disabled={overridden} onClick={() => openInheritedFieldOverride(record)}>
|
||||
{t('Override')}
|
||||
</Typography.Link>
|
||||
)}
|
||||
<Typography.Link onClick={() => openInheritedFieldView(record, record.__parentTitle)}>
|
||||
{t('View')}
|
||||
</Typography.Link>
|
||||
</Space>
|
||||
);
|
||||
},
|
||||
},
|
||||
],
|
||||
[
|
||||
configureFieldsDisabled,
|
||||
ctx.dataSourceManager,
|
||||
currentFieldsByName,
|
||||
fieldInterfacesByName,
|
||||
handleTitleFieldChange,
|
||||
openInheritedFieldOverride,
|
||||
openInheritedFieldView,
|
||||
props.collection.name,
|
||||
t,
|
||||
titleField,
|
||||
titleFieldLoadingKey,
|
||||
],
|
||||
);
|
||||
|
||||
const inheritedGroupColumns = useMemo<ColumnsType<InheritedFieldGroup>>(
|
||||
() => [
|
||||
{
|
||||
dataIndex: 'title',
|
||||
render: (_, record) => (
|
||||
<strong>
|
||||
{t('Inherited fields')} - {compileLegacyTemplate(record.title || record.collectionName, t)}
|
||||
</strong>
|
||||
),
|
||||
},
|
||||
],
|
||||
[t],
|
||||
);
|
||||
|
||||
const handleSyncFields = useCallback(async () => {
|
||||
if (TemplateSyncFieldsDrawer) {
|
||||
openTemplateSyncFieldsDrawer();
|
||||
@@ -1187,16 +1445,6 @@ export default function FieldsPage(props: FieldsPageProps) {
|
||||
t,
|
||||
]);
|
||||
|
||||
const syncFieldsVisible = isSyncFieldsVisible(props.dataSourceKey, props.collection, ctx);
|
||||
const configureFieldsDisabled = Boolean(dataSourceType?.disableConfigureFields);
|
||||
const fieldDeletionVisible = isMainDataSource && !configureFieldsDisabled;
|
||||
const addFieldVisible = isAddFieldVisible({
|
||||
collection: props.collection,
|
||||
ctx,
|
||||
dataSourceType: dataSource?.options?.type,
|
||||
fieldInterfaceGroups,
|
||||
});
|
||||
|
||||
useEffect(() => {
|
||||
if (!fieldDeletionVisible) {
|
||||
setSelectedRowKeys([]);
|
||||
@@ -1292,7 +1540,14 @@ export default function FieldsPage(props: FieldsPageProps) {
|
||||
render: (_, record) => (
|
||||
<Space>
|
||||
<a onClick={() => openFieldForm('edit', record)}>{t('Edit')}</a>
|
||||
{fieldDeletionVisible ? <a onClick={() => handleDelete(record.name)}>{t('Delete')}</a> : null}
|
||||
{fieldDeletionVisible ? (
|
||||
<Typography.Link
|
||||
disabled={isFieldDeleteDisabled(record, collectionTemplate)}
|
||||
onClick={() => handleDelete(record.name)}
|
||||
>
|
||||
{t('Delete')}
|
||||
</Typography.Link>
|
||||
) : null}
|
||||
</Space>
|
||||
),
|
||||
});
|
||||
@@ -1303,6 +1558,7 @@ export default function FieldsPage(props: FieldsPageProps) {
|
||||
allFieldInterfaceGroups,
|
||||
configureFieldsDisabled,
|
||||
fieldDeletionVisible,
|
||||
collectionTemplate,
|
||||
ctx.dataSourceManager,
|
||||
dataSourceType,
|
||||
fieldInterfacesByName,
|
||||
@@ -1357,10 +1613,39 @@ export default function FieldsPage(props: FieldsPageProps) {
|
||||
? {
|
||||
selectedRowKeys,
|
||||
onChange: setSelectedRowKeys,
|
||||
getCheckboxProps: (record) => ({
|
||||
disabled: isFieldDeleteDisabled(record, collectionTemplate),
|
||||
}),
|
||||
}
|
||||
: undefined
|
||||
}
|
||||
/>
|
||||
{inheritedFieldGroups.some((group) => group.fields.length) ? (
|
||||
<AntdTable<InheritedFieldGroup>
|
||||
style={{ marginTop: 16 }}
|
||||
rowKey="key"
|
||||
columns={inheritedGroupColumns}
|
||||
dataSource={inheritedFieldGroups.filter((group) => group.fields.length)}
|
||||
pagination={false}
|
||||
showHeader={false}
|
||||
expandable={{
|
||||
defaultExpandAllRows: true,
|
||||
defaultExpandedRowKeys: inheritedFieldGroups.map((group) => group.key),
|
||||
expandedRowRender: (record) => (
|
||||
<AntdTable<Record<string, any>>
|
||||
rowKey="name"
|
||||
columns={inheritedFieldColumns}
|
||||
dataSource={record.fields.map((field) => ({
|
||||
...field,
|
||||
__parentTitle: record.title,
|
||||
}))}
|
||||
pagination={false}
|
||||
showHeader={false}
|
||||
/>
|
||||
),
|
||||
}}
|
||||
/>
|
||||
) : null}
|
||||
</>
|
||||
);
|
||||
}
|
||||
|
||||
+60
@@ -8,6 +8,7 @@
|
||||
*/
|
||||
|
||||
import { compileLegacyTemplate } from '../../../utils/compileLegacyTemplate';
|
||||
import { getInheritedFieldGroups, isFieldDeleteDisabled, isInheritedFieldOverridden } from '../FieldsPage';
|
||||
import { getPresetFieldRows } from '../CollectionsPage';
|
||||
|
||||
describe('getPresetFieldRows', () => {
|
||||
@@ -46,3 +47,62 @@ describe('getPresetFieldRows', () => {
|
||||
expect(compileLegacyTemplate(rows[0].field, t)).toBe('空间');
|
||||
});
|
||||
});
|
||||
|
||||
describe('field delete helpers', () => {
|
||||
it('disables deleting fields marked as non-deletable by the collection template', () => {
|
||||
const fileTemplate = {
|
||||
collection: {
|
||||
fields: [
|
||||
{ name: 'title', deletable: false },
|
||||
{ name: 'filename', deletable: false },
|
||||
],
|
||||
},
|
||||
};
|
||||
|
||||
expect(isFieldDeleteDisabled({ name: 'title' }, fileTemplate)).toBe(true);
|
||||
expect(isFieldDeleteDisabled({ name: 'customName' }, fileTemplate)).toBe(false);
|
||||
});
|
||||
|
||||
it('disables deleting fields whose record metadata is non-deletable', () => {
|
||||
expect(isFieldDeleteDisabled({ name: 'title', deletable: false })).toBe(true);
|
||||
expect(isFieldDeleteDisabled({ name: 'title', deletable: true })).toBe(false);
|
||||
});
|
||||
});
|
||||
|
||||
describe('inherited field helpers', () => {
|
||||
function createCollection(name: string, fields: Array<Record<string, unknown>>, inherits: unknown[] = []) {
|
||||
return {
|
||||
name,
|
||||
title: name,
|
||||
fields: new Map(fields.map((field) => [field.name, { options: field }])),
|
||||
inherits: new Map(inherits.map((collection: { name: string }) => [collection.name, collection])),
|
||||
};
|
||||
}
|
||||
|
||||
it('returns direct and ancestor inherited field groups using each parent collection own fields', () => {
|
||||
const grandparent = createCollection('grandparent', [{ name: 'grandparentField', interface: 'input' }]);
|
||||
const parent = createCollection('parent', [{ name: 'parentField', interface: 'input' }], [grandparent]);
|
||||
const child = createCollection('child', [{ name: 'childField', interface: 'input' }], [parent]);
|
||||
|
||||
expect(
|
||||
getInheritedFieldGroups(child).map((group) => ({
|
||||
collectionName: group.collectionName,
|
||||
fields: group.fields.map((field) => field.name),
|
||||
})),
|
||||
).toEqual([
|
||||
{ collectionName: 'parent', fields: ['parentField'] },
|
||||
{ collectionName: 'grandparent', fields: ['grandparentField'] },
|
||||
]);
|
||||
});
|
||||
|
||||
it('treats a same-name current collection field as an overridden inherited field', () => {
|
||||
const currentFieldsByName = new Map([
|
||||
['title', { name: 'title', collectionName: 'orders-1' }],
|
||||
['status', { name: 'status', collectionName: 'orders' }],
|
||||
]);
|
||||
|
||||
expect(isInheritedFieldOverridden({ name: 'title' }, 'orders-1', currentFieldsByName)).toBe(true);
|
||||
expect(isInheritedFieldOverridden({ name: 'status' }, 'orders-1', currentFieldsByName)).toBe(false);
|
||||
expect(isInheritedFieldOverridden({ name: 'amount' }, 'orders-1', currentFieldsByName)).toBe(false);
|
||||
});
|
||||
});
|
||||
|
||||
Reference in New Issue
Block a user