From 2c5b097242f4e403e68b5ba57069f01cd7baf5e3 Mon Sep 17 00:00:00 2001
From: Johannes Obermair <48853629+johnnyomair@users.noreply.github.com>
Date: Tue, 12 Dec 2023 12:24:54 +0100
Subject: [PATCH] Infer additional item fields in `BlocksBlock` and `ListBlock`
(#1481)
Additional fields in the `item` prop of `AdditionalItemContextMenuItems`
and `AdditionalItemContent` will be typed correctly if the
`additionalItemFields` option is strongly typed.
---
.changeset/silly-lemons-compete.md | 7 +
.../admin/src/common/blocks/LinkListBlock.tsx | 4 -
demo/admin/src/pages/PageContentBlock.tsx | 4 -
.../userGroupAdditionalItemFields.ts | 4 +-
.../blocks/factories/createBlocksBlock.tsx | 198 +++++++++++-------
.../src/blocks/factories/createListBlock.tsx | 151 +++++++------
.../listBlock/createUseAdminComponent.tsx | 4 +-
7 files changed, 221 insertions(+), 151 deletions(-)
create mode 100644 .changeset/silly-lemons-compete.md
diff --git a/.changeset/silly-lemons-compete.md b/.changeset/silly-lemons-compete.md
new file mode 100644
index 0000000000..66c90b5723
--- /dev/null
+++ b/.changeset/silly-lemons-compete.md
@@ -0,0 +1,7 @@
+---
+"@comet/blocks-admin": patch
+---
+
+Infer additional item fields in `BlocksBlock` and `ListBlock`
+
+Additional fields in the `item` prop of `AdditionalItemContextMenuItems` and `AdditionalItemContent` will be typed correctly if the `additionalItemFields` option is strongly typed.
diff --git a/demo/admin/src/common/blocks/LinkListBlock.tsx b/demo/admin/src/common/blocks/LinkListBlock.tsx
index 9a1ce34f68..0b183c681b 100644
--- a/demo/admin/src/common/blocks/LinkListBlock.tsx
+++ b/demo/admin/src/common/blocks/LinkListBlock.tsx
@@ -17,13 +17,9 @@ export const LinkListBlock = createListBlock({
...userGroupAdditionalItemFields,
},
AdditionalItemContextMenuItems: ({ item, onChange, onMenuClose }) => {
- // TODO fix typing: infer additional fields somehow
- // @ts-expect-error missing additional field
return ;
},
AdditionalItemContent: ({ item }) => {
- // TODO fix typing: infer additional fields somehow
- // @ts-expect-error missing additional field
return ;
},
});
diff --git a/demo/admin/src/pages/PageContentBlock.tsx b/demo/admin/src/pages/PageContentBlock.tsx
index f8a1714d6b..4a1373ebe8 100644
--- a/demo/admin/src/pages/PageContentBlock.tsx
+++ b/demo/admin/src/pages/PageContentBlock.tsx
@@ -37,13 +37,9 @@ export const PageContentBlock = createBlocksBlock({
...userGroupAdditionalItemFields,
},
AdditionalItemContextMenuItems: ({ item, onChange, onMenuClose }) => {
- // TODO fix typing: infer additional fields somehow
- // @ts-expect-error missing additional field
return ;
},
AdditionalItemContent: ({ item }) => {
- // TODO fix typing: infer additional fields somehow
- // @ts-expect-error missing additional field
return ;
},
});
diff --git a/demo/admin/src/userGroups/userGroupAdditionalItemFields.ts b/demo/admin/src/userGroups/userGroupAdditionalItemFields.ts
index a185519609..13d31d39a3 100644
--- a/demo/admin/src/userGroups/userGroupAdditionalItemFields.ts
+++ b/demo/admin/src/userGroups/userGroupAdditionalItemFields.ts
@@ -1,6 +1,8 @@
+import { GQLUserGroup } from "@src/graphql.generated";
+
const userGroupAdditionalItemFields = {
userGroup: {
- defaultValue: "All",
+ defaultValue: "All" as GQLUserGroup,
},
};
diff --git a/packages/admin/blocks-admin/src/blocks/factories/createBlocksBlock.tsx b/packages/admin/blocks-admin/src/blocks/factories/createBlocksBlock.tsx
index 855dc6b2b4..5915e941ed 100644
--- a/packages/admin/blocks-admin/src/blocks/factories/createBlocksBlock.tsx
+++ b/packages/admin/blocks-admin/src/blocks/factories/createBlocksBlock.tsx
@@ -21,7 +21,14 @@ import { deduplicateBlockDependencies } from "../helpers/deduplicateBlockDepende
import { BlockDependency, BlockInterface, BlockState, DispatchSetStateAction, PreviewContent } from "../types";
import { resolveNewState } from "../utils";
-interface BlocksBlockItem {
+// Using {} instead of Record because never and unknown are incompatible.
+// eslint-disable-next-line @typescript-eslint/ban-types
+type DefaultAdditionalItemFields = {};
+
+type BlocksBlockItem<
+ T extends BlockInterface = BlockInterface,
+ AdditionalItemFields extends Record = DefaultAdditionalItemFields,
+> = {
[key: string]: unknown;
key: string;
type: string;
@@ -29,63 +36,76 @@ interface BlocksBlockItem {
selected: boolean;
props: BlockState;
slideIn: boolean;
-}
+} & AdditionalItemFields;
-type RemovedBlocksBlockItem = BlocksBlockItem & { removedAt: number };
+type RemovedBlocksBlockItem<
+ T extends BlockInterface = BlockInterface,
+ AdditionalItemFields extends Record = DefaultAdditionalItemFields,
+> = BlocksBlockItem & { removedAt: number };
-export interface BlocksBlockState {
- blocks: BlocksBlockItem[];
+export interface BlocksBlockState = DefaultAdditionalItemFields> {
+ blocks: BlocksBlockItem[];
}
-export interface BlocksBlockFragment {
- blocks: {
- [key: string]: unknown;
- key: string;
- type: string;
- visible: boolean;
- // eslint-disable-next-line @typescript-eslint/no-explicit-any
- props: any;
- }[];
+export interface BlocksBlockFragment = DefaultAdditionalItemFields> {
+ blocks: Array<
+ {
+ [key: string]: unknown;
+ key: string;
+ type: string;
+ visible: boolean;
+ // eslint-disable-next-line @typescript-eslint/no-explicit-any
+ props: any;
+ } & AdditionalItemFields
+ >;
}
-export interface BlocksBlockOutput {
- blocks: {
- [key: string]: unknown;
- key: string;
- type: string;
- visible: boolean;
- // eslint-disable-next-line @typescript-eslint/no-explicit-any
- props: any;
- }[];
+export interface BlocksBlockOutput = DefaultAdditionalItemFields> {
+ blocks: Array<
+ {
+ [key: string]: unknown;
+ key: string;
+ type: string;
+ visible: boolean;
+ // eslint-disable-next-line @typescript-eslint/no-explicit-any
+ props: any;
+ } & AdditionalItemFields
+ >;
}
type BlockType = string;
-interface AdditionalItemField {
+interface BlocksBlockAdditionalItemField {
defaultValue: Value;
}
-interface CreateBlocksBlockOptions {
+interface CreateBlocksBlockOptions> {
name: string;
displayName?: React.ReactNode;
supportedBlocks: Record;
- additionalItemFields?: Record;
+ additionalItemFields?: {
+ [Key in keyof AdditionalItemFields]: BlocksBlockAdditionalItemField;
+ };
AdditionalItemContextMenuItems?: React.FunctionComponent<{
- item: BlocksBlockItem;
- onChange: (item: BlocksBlockItem) => void;
+ item: BlocksBlockItem;
+ onChange: (item: BlocksBlockItem) => void;
onMenuClose: () => void;
}>;
- AdditionalItemContent?: React.FunctionComponent<{ item: BlocksBlockItem }>;
+ AdditionalItemContent?: React.FunctionComponent<{ item: BlocksBlockItem }>;
}
-export function createBlocksBlock({
+export function createBlocksBlock = DefaultAdditionalItemFields>({
supportedBlocks,
name,
displayName = ,
- additionalItemFields = {},
+ additionalItemFields,
AdditionalItemContextMenuItems,
AdditionalItemContent,
-}: CreateBlocksBlockOptions): BlockInterface {
+}: CreateBlocksBlockOptions): BlockInterface<
+ BlocksBlockFragment,
+ BlocksBlockState,
+ BlocksBlockOutput
+> {
if (Object.keys(supportedBlocks).length === 0) {
throw new Error("Blocks block with no supported block is not allowed. Please specify at least two supported blocks.");
}
@@ -102,7 +122,11 @@ export function createBlocksBlock({
return Object.entries(supportedBlocks).find(([, block]) => block.name === targetBlock.name)?.[0] ?? null;
}
- const BlocksBlock: BlockInterface = {
+ const BlocksBlock: BlockInterface<
+ BlocksBlockFragment,
+ BlocksBlockState,
+ BlocksBlockOutput
+ > = {
...createBlockSkeleton(),
name,
@@ -112,21 +136,21 @@ export function createBlocksBlock({
defaultValues: () => ({ blocks: [] }),
input2State: (input) => {
- const blocks: BlocksBlockItem[] = [];
+ const blocks: BlocksBlockItem[] = [];
- for (const item of input.blocks) {
- const block = blockForType(item.type);
+ for (const child of input.blocks) {
+ const block = blockForType(child.type);
if (!block) {
// eslint-disable-next-line no-console
- console.warn(`Unknown block type "${item.type}"`);
+ console.warn(`Unknown block type "${child.type}"`);
continue;
}
blocks.push({
- ...item,
- props: block.input2State(item.props),
- ...Object.keys(additionalItemFields).reduce((fields, field) => ({ ...fields, [field]: item[field] }), {}),
+ ...child,
+ props: block.input2State(child.props),
+ ...Object.keys(additionalItemFields ?? {}).reduce((fields, field) => ({ ...fields, [field]: child[field] }), {}),
selected: false,
slideIn: false,
});
@@ -136,40 +160,45 @@ export function createBlocksBlock({
blocks,
};
},
- state2Output: (s) => {
+
+ state2Output: (state) => {
return {
- blocks: s.blocks.map((c) => {
- const block = blockForType(c.type);
+ blocks: state.blocks.map((child) => {
+ const block = blockForType(child.type);
if (!block) {
- throw new Error(`No Block found for type ${c.type}`); // for TS
+ throw new Error(`No Block found for type ${child.type}`); // for TS
}
return {
- key: c.key,
- visible: c.visible,
- type: c.type,
- props: block.state2Output(c.props),
- ...Object.keys(additionalItemFields).reduce((fields, field) => ({ ...fields, [field]: c[field] }), {}),
+ key: child.key,
+ visible: child.visible,
+ type: child.type,
+ props: block.state2Output(child.props),
+ // Type cast to suppress "'AdditionalItemFields' could be instantiated with a different subtype of constraint 'Record'" error
+ ...(Object.keys(additionalItemFields ?? {}).reduce(
+ (fields, field) => ({ ...fields, [field]: child[field] }),
+ {},
+ ) as AdditionalItemFields),
};
}),
};
},
output2State: async (output, context) => {
- const state: BlocksBlockState = {
+ const state: BlocksBlockState = {
blocks: [],
};
- for (const item of output.blocks) {
- const block = blockForType(item.type);
+ for (const child of output.blocks) {
+ const block = blockForType(child.type);
if (!block) {
- throw new Error(`No Block found for type ${item.type}`);
+ throw new Error(`No Block found for type ${child.type}`);
}
state.blocks.push({
slideIn: false,
- ...item,
- props: await block.output2State(item.props, context),
+ ...child,
+ props: await block.output2State(child.props, context),
selected: false,
});
}
@@ -181,20 +210,24 @@ export function createBlocksBlock({
return {
adminRoute: previewCtx.parentUrl,
blocks: state.blocks
- .filter((c) => (previewCtx.showVisibleOnly ? c.visible : true)) // depending on context show all blocks or only visible blocks
- .map((c) => {
- const blockAdminRoute = `${previewCtx.parentUrl}/${c.key}/blocks`;
- const block = blockForType(c.type);
+ .filter((child) => (previewCtx.showVisibleOnly ? child.visible : true)) // depending on context show all blocks or only visible blocks
+ .map((child) => {
+ const blockAdminRoute = `${previewCtx.parentUrl}/${child.key}/blocks`;
+ const block = blockForType(child.type);
if (!block) {
- throw new Error(`No Block found for type ${c.type}`); // for TS
+ throw new Error(`No Block found for type ${child.type}`); // for TS
}
return {
- key: c.key,
- visible: c.visible,
- type: c.type,
+ key: child.key,
+ visible: child.visible,
+ type: child.type,
adminRoute: blockAdminRoute,
- props: block.createPreviewState(c.props, { ...previewCtx, parentUrl: blockAdminRoute }),
- ...Object.keys(additionalItemFields).reduce((fields, field) => ({ ...fields, [field]: c[field] }), {}),
+ props: block.createPreviewState(child.props, { ...previewCtx, parentUrl: blockAdminRoute }),
+ // Type cast to suppress "'AdditionalItemFields' could be instantiated with a different subtype of constraint 'Record'" error
+ ...(Object.keys(additionalItemFields ?? {}).reduce(
+ (fields, field) => ({ ...fields, [field]: child[field] }),
+ {},
+ ) as AdditionalItemFields),
};
}),
adminMeta: { route: previewCtx.parentUrl },
@@ -233,15 +266,15 @@ export function createBlocksBlock({
},
replaceDependenciesInOutput: (output, replacements) => {
- const newOutput: BlocksBlockOutput = { blocks: [] };
+ const newOutput: BlocksBlockOutput = { blocks: [] };
- for (const c of output.blocks) {
- const block = blockForType(c.type);
+ for (const child of output.blocks) {
+ const block = blockForType(child.type);
if (!block) {
- throw new Error(`No Block found for type ${c.type}`);
+ throw new Error(`No Block found for type ${child.type}`);
}
- newOutput.blocks.push({ ...c, props: block.replaceDependenciesInOutput(c.props, replacements) });
+ newOutput.blocks.push({ ...child, props: block.replaceDependenciesInOutput(child.props, replacements) });
}
return newOutput;
@@ -279,7 +312,7 @@ export function createBlocksBlock({
}, [state.blocks, updateState]);
const handleUndoClick = React.useCallback(
- (removedBlocks: RemovedBlocksBlockItem[] | undefined) => {
+ (removedBlocks: RemovedBlocksBlockItem[] | undefined) => {
if (!removedBlocks) {
return;
}
@@ -287,7 +320,9 @@ export function createBlocksBlock({
updateState((prevState) => {
const blocks = [...prevState.blocks];
removedBlocks?.forEach((removedBlock) => {
- const { removedAt, ...block } = removedBlock;
+ const { removedAt } = removedBlock;
+ const block: BlocksBlockItem = { ...removedBlock };
+ delete block.removedAt;
blocks.splice(removedAt, 0, block);
});
@@ -362,14 +397,17 @@ export function createBlocksBlock({
if (!block) {
throw new Error(`No Block found for type ${type}`);
}
- const newItem: BlocksBlockItem = {
+ const newItem: BlocksBlockItem = {
key,
type,
visible: true,
selected: false,
props: block.defaultValues(),
slideIn: true,
- ...Object.entries(additionalItemFields).reduce((fields, [field, { defaultValue }]) => ({ ...fields, [field]: defaultValue }), {}),
+ ...(Object.entries(additionalItemFields ?? {}).reduce(
+ (fields, [field, { defaultValue }]) => ({ ...fields, [field]: defaultValue }),
+ {},
+ ) as AdditionalItemFields),
};
const newBlocks = [...state.blocks];
@@ -416,7 +454,7 @@ export function createBlocksBlock({
const { content } = response;
updateState((prevState) => {
- const newBlocks: BlocksBlockItem[] = content.map((block) => {
+ const newBlocks: BlocksBlockItem[] = content.map((block) => {
const type = typeForBlock(block);
if (!type) {
@@ -430,7 +468,8 @@ export function createBlocksBlock({
visible: block.visible,
props: block.state,
slideIn: true,
- ...block.additionalFields,
+ // Type cast to suppress "'AdditionalItemFields' could be instantiated with a different subtype of constraint 'Record'" error
+ ...(block.additionalFields as AdditionalItemFields),
};
});
@@ -491,7 +530,10 @@ export function createBlocksBlock({
name: blockInterface.name,
visible: block.visible,
state: block.props,
- additionalFields: Object.keys(additionalItemFields).reduce((fields, field) => ({ ...fields, [field]: block[field] }), {}),
+ additionalFields: Object.keys(additionalItemFields ?? {}).reduce(
+ (fields, field) => ({ ...fields, [field]: block[field] }),
+ {},
+ ),
};
});
@@ -613,7 +655,7 @@ export function createBlocksBlock({
name: block.name,
visible: data.visible,
state: data.props,
- additionalFields: Object.keys(additionalItemFields).reduce(
+ additionalFields: Object.keys(additionalItemFields ?? {}).reduce(
(fields, field) => ({ ...fields, [field]: data[field] }),
{},
),
diff --git a/packages/admin/blocks-admin/src/blocks/factories/createListBlock.tsx b/packages/admin/blocks-admin/src/blocks/factories/createListBlock.tsx
index 57617d5ae9..cd29294587 100644
--- a/packages/admin/blocks-admin/src/blocks/factories/createListBlock.tsx
+++ b/packages/admin/blocks-admin/src/blocks/factories/createListBlock.tsx
@@ -20,44 +20,52 @@ import { deduplicateBlockDependencies } from "../helpers/deduplicateBlockDepende
import { BlockDependency, BlockInterface, BlockState, PreviewContent } from "../types";
import { createUseAdminComponent } from "./listBlock/createUseAdminComponent";
-export interface ListBlockItem {
+// Using {} instead of Record because never and unknown are incompatible.
+// eslint-disable-next-line @typescript-eslint/ban-types
+type DefaultAdditionalItemFields = {};
+
+export type ListBlockItem = DefaultAdditionalItemFields> = {
[key: string]: unknown;
key: string;
visible: boolean;
props: BlockState;
selected: boolean;
slideIn: boolean;
-}
+} & AdditionalItemFields;
-export interface ListBlockState {
- blocks: ListBlockItem[];
+export interface ListBlockState = DefaultAdditionalItemFields> {
+ blocks: ListBlockItem[];
}
-export interface ListBlockFragment {
- blocks: Array<{
- [key: string]: unknown;
- key: string;
- visible: boolean;
- // eslint-disable-next-line @typescript-eslint/no-explicit-any
- props: any;
- }>;
+export interface ListBlockFragment = DefaultAdditionalItemFields> {
+ blocks: Array<
+ {
+ [key: string]: unknown;
+ key: string;
+ visible: boolean;
+ // eslint-disable-next-line @typescript-eslint/no-explicit-any
+ props: any;
+ } & AdditionalItemFields
+ >;
}
-export interface ListBlockOutput {
- blocks: Array<{
- [key: string]: unknown;
- key: string;
- visible: boolean;
- // eslint-disable-next-line @typescript-eslint/no-explicit-any
- props: any;
- }>;
+export interface ListBlockOutput = DefaultAdditionalItemFields> {
+ blocks: Array<
+ {
+ [key: string]: unknown;
+ key: string;
+ visible: boolean;
+ // eslint-disable-next-line @typescript-eslint/no-explicit-any
+ props: any;
+ } & AdditionalItemFields
+ >;
}
-export interface AdditionalItemField {
+export interface ListBlockAdditionalItemField {
defaultValue: Value;
}
-interface CreateListBlockOptions {
+interface CreateListBlockOptions> {
name: string;
displayName?: React.ReactNode;
itemName?: React.ReactNode;
@@ -65,16 +73,18 @@ interface CreateListBlockOptions {
block: T;
maxVisibleBlocks?: number;
createDefaultListEntry?: boolean;
- additionalItemFields?: Record;
+ additionalItemFields?: {
+ [Key in keyof AdditionalItemFields]: ListBlockAdditionalItemField;
+ };
AdditionalItemContextMenuItems?: React.FunctionComponent<{
- item: ListBlockItem;
- onChange: (item: ListBlockItem) => void;
+ item: ListBlockItem;
+ onChange: (item: ListBlockItem) => void;
onMenuClose: () => void;
}>;
- AdditionalItemContent?: React.FunctionComponent<{ item: ListBlockItem }>;
+ AdditionalItemContent?: React.FunctionComponent<{ item: ListBlockItem }>;
}
-export function createListBlock({
+export function createListBlock = DefaultAdditionalItemFields>({
name,
block,
displayName = ,
@@ -82,12 +92,20 @@ export function createListBlock({
itemsName = ,
maxVisibleBlocks,
createDefaultListEntry,
- additionalItemFields = {},
+ additionalItemFields,
AdditionalItemContextMenuItems,
AdditionalItemContent,
-}: CreateListBlockOptions): BlockInterface, ListBlockOutput> {
+}: CreateListBlockOptions): BlockInterface<
+ ListBlockFragment,
+ ListBlockState,
+ ListBlockOutput
+> {
const useAdminComponent = createUseAdminComponent({ block, maxVisibleBlocks, additionalItemFields });
- const BlockListBlock: BlockInterface, ListBlockOutput> = {
+ const BlockListBlock: BlockInterface<
+ ListBlockFragment,
+ ListBlockState,
+ ListBlockOutput
+ > = {
...createBlockSkeleton(),
name,
@@ -103,10 +121,11 @@ export function createListBlock({
props: block.defaultValues(),
selected: false,
slideIn: false,
- ...Object.entries(additionalItemFields).reduce(
+ // Type cast to suppress "'AdditionalItemFields' could be instantiated with a different subtype of constraint 'Record'" error
+ ...(Object.entries(additionalItemFields ?? {}).reduce(
(fields, [field, { defaultValue }]) => ({ ...fields, [field]: defaultValue }),
{},
- ),
+ ) as AdditionalItemFields),
},
]
: [],
@@ -114,16 +133,16 @@ export function createListBlock({
category: block.category,
- input2State: (s) => {
+ input2State: (input) => {
return {
- ...s,
- blocks: s.blocks.map((c) => {
+ ...input,
+ blocks: input.blocks.map((child) => {
return {
- ...c,
- key: c.key,
- visible: c.visible,
- props: block.input2State(c.props),
- ...Object.keys(additionalItemFields).reduce((fields, field) => ({ ...fields, [field]: c[field] }), {}),
+ ...child,
+ key: child.key,
+ visible: child.visible,
+ props: block.input2State(child.props),
+ ...Object.keys(additionalItemFields ?? {}).reduce((fields, field) => ({ ...fields, [field]: child[field] }), {}),
selected: false,
slideIn: false,
};
@@ -131,29 +150,33 @@ export function createListBlock({
};
},
- state2Output: (s) => {
+ state2Output: (state) => {
return {
- blocks: s.blocks.map((c) => {
+ blocks: state.blocks.map((child) => {
return {
- key: c.key,
- visible: c.visible,
- props: block.state2Output(c.props),
- ...Object.keys(additionalItemFields).reduce((fields, field) => ({ ...fields, [field]: c[field] }), {}),
+ key: child.key,
+ visible: child.visible,
+ props: block.state2Output(child.props),
+ // Type cast to suppress "'AdditionalItemFields' could be instantiated with a different subtype of constraint 'Record'" error
+ ...(Object.keys(additionalItemFields ?? {}).reduce(
+ (fields, field) => ({ ...fields, [field]: child[field] }),
+ {},
+ ) as AdditionalItemFields),
};
}),
};
},
output2State: async (output, context) => {
- const state: ListBlockState = {
+ const state: ListBlockState = {
blocks: [],
};
- for (const item of output.blocks) {
+ for (const child of output.blocks) {
state.blocks.push({
slideIn: false,
- ...item,
- props: await block.output2State(item.props, context),
+ ...child,
+ props: await block.output2State(child.props, context),
selected: false,
});
}
@@ -165,15 +188,19 @@ export function createListBlock({
return {
adminRoute: previewCtx.parentUrl,
blocks: state.blocks
- .filter((c) => (previewCtx.showVisibleOnly ? c.visible : true)) // depending on context show all blocks or only visible blocks
- .map((c) => {
- const blockAdminRoute = `${previewCtx.parentUrl}/${c.key}/edit`;
+ .filter((child) => (previewCtx.showVisibleOnly ? child.visible : true)) // depending on context show all blocks or only visible blocks
+ .map((child) => {
+ const blockAdminRoute = `${previewCtx.parentUrl}/${child.key}/edit`;
return {
- key: c.key,
- visible: c.visible,
- props: block.createPreviewState(c.props, { ...previewCtx, parentUrl: blockAdminRoute }),
- ...Object.keys(additionalItemFields).reduce((fields, field) => ({ ...fields, [field]: c[field] }), {}),
+ key: child.key,
+ visible: child.visible,
+ props: block.createPreviewState(child.props, { ...previewCtx, parentUrl: blockAdminRoute }),
+ // Type cast to suppress "'AdditionalItemFields' could be instantiated with a different subtype of constraint 'Record'" error
+ ...(Object.keys(additionalItemFields ?? {}).reduce(
+ (fields, field) => ({ ...fields, [field]: child[field] }),
+ {},
+ ) as AdditionalItemFields),
adminRoute: blockAdminRoute,
adminMeta: { route: blockAdminRoute },
};
@@ -200,12 +227,12 @@ export function createListBlock({
},
replaceDependenciesInOutput: (output, replacements) => {
- const newOutput: ListBlockOutput = { ...output, blocks: [] };
+ const newOutput: ListBlockOutput = { ...output, blocks: [] };
- for (const c of output.blocks) {
+ for (const child of output.blocks) {
newOutput.blocks.push({
- ...c,
- props: block.replaceDependenciesInOutput(c.props, replacements),
+ ...child,
+ props: block.replaceDependenciesInOutput(child.props, replacements),
});
}
@@ -346,7 +373,7 @@ export function createListBlock({
name: block.name,
visible: data.visible,
state: data.props,
- additionalFields: Object.keys(additionalItemFields).reduce(
+ additionalFields: Object.keys(additionalItemFields ?? {}).reduce(
(fields, field) => ({ ...fields, [field]: data[field] }),
{},
),
diff --git a/packages/admin/blocks-admin/src/blocks/factories/listBlock/createUseAdminComponent.tsx b/packages/admin/blocks-admin/src/blocks/factories/listBlock/createUseAdminComponent.tsx
index 2cb42477d6..b8a1ea71e9 100644
--- a/packages/admin/blocks-admin/src/blocks/factories/listBlock/createUseAdminComponent.tsx
+++ b/packages/admin/blocks-admin/src/blocks/factories/listBlock/createUseAdminComponent.tsx
@@ -7,12 +7,12 @@ import { CannotPasteBlockDialog } from "../../../clipboard/CannotPasteBlockDialo
import { ClipboardContent, useBlockClipboard } from "../../../clipboard/useBlockClipboard";
import { BlockAdminComponentProps, BlockInterface, BlockState, DispatchSetStateAction } from "../../types";
import { resolveNewState } from "../../utils";
-import { AdditionalItemField, ListBlockState } from "../createListBlock";
+import { ListBlockAdditionalItemField, ListBlockState } from "../createListBlock";
interface CreateListBlockUseAdminComponentOptions {
block: T;
maxVisibleBlocks?: number;
- additionalItemFields?: Record;
+ additionalItemFields?: Record;
}
type ListBlockUseAdminComponentProps = BlockAdminComponentProps>;