Skip to content

Commit

Permalink
Add additional fields to OneOfBlock type (#2878)
Browse files Browse the repository at this point in the history
  • Loading branch information
johnnyomair authored Jan 30, 2025
1 parent 8446e25 commit 7e7a4aa
Show file tree
Hide file tree
Showing 4 changed files with 77 additions and 25 deletions.
6 changes: 6 additions & 0 deletions .changeset/old-adults-whisper.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
---
"@comet/blocks-api": patch
"@comet/cms-api": patch
---

Fix `title` field not added to types in `createLinkBlock`
68 changes: 45 additions & 23 deletions packages/api/blocks-api/src/blocks/factories/createOneOfBlock.ts
Original file line number Diff line number Diff line change
Expand Up @@ -26,10 +26,10 @@ import { BlockFactoryNameOrOptions } from "./types";

type BaseBlockMap = Record<string, Block<BlockDataInterface, BlockInputInterface>>;

interface OneOfBlockDataInterface<BlockMap extends BaseBlockMap> extends BlockDataInterface {
type OneOfBlockDataInterface<BlockMap extends BaseBlockMap, Data extends BlockDataInterface> = Data & {
attachedBlocks: SupportedBlocksDataInterfaces<BlockMap>[];
activeType?: keyof BlockMap;
}
};

type SupportedBlocksDataInterfaces<BlockMap extends BaseBlockMap> = {
[Typename in keyof BlockMap]: BlockDataInterface & {
Expand All @@ -38,10 +38,14 @@ type SupportedBlocksDataInterfaces<BlockMap extends BaseBlockMap> = {
};
}[keyof BlockMap];

interface OneOfBlockInputInterface<BlockMap extends BaseBlockMap> extends SimpleBlockInputInterface {
type OneOfBlockInputInterface<
BlockMap extends BaseBlockMap,
Data extends BlockDataInterface,
Input extends SimpleBlockInputInterface<Data>,
> = Input & {
attachedBlocks: SupportedBlocksInputInterfaces<BlockMap>[];
activeType?: keyof BlockMap;
}
};

type SupportedBlocksInputInterfaces<BlockMap extends BaseBlockMap> = {
[Typename in keyof BlockMap]: SimpleBlockInputInterface & {
Expand Down Expand Up @@ -91,13 +95,13 @@ export function BaseOneOfBlockItemData<BlockMap extends BaseBlockMap>({
return OneOfBlockItemData;
}

export function BaseOneOfBlockData<BlockMap extends BaseBlockMap>({
export function BaseOneOfBlockData<BlockMap extends BaseBlockMap, Data extends BlockDataInterface = BlockDataInterface>({
supportedBlocks,
OneOfBlockItemData,
}: {
supportedBlocks: BlockMap;
OneOfBlockItemData: Type<OneOfBlockItemDataInterface>;
}): Type<OneOfBlockDataInterface<BlockMap>> {
}): Type<OneOfBlockDataInterface<BlockMap, Data>> {
class OneOfBlockData extends BlockData {
@Transform(({ value }: { value: OneOfBlockItemDataInterface[] }) =>
value
Expand All @@ -111,7 +115,7 @@ export function BaseOneOfBlockData<BlockMap extends BaseBlockMap>({
})
.map((item) => plainToInstance(OneOfBlockItemData, item)),
)
attachedBlocks: OneOfBlockDataInterface<BlockMap>["attachedBlocks"][number][];
attachedBlocks: OneOfBlockDataInterface<BlockMap, Data>["attachedBlocks"][number][];

// index of blocks
activeType?: string;
Expand Down Expand Up @@ -141,7 +145,8 @@ export function BaseOneOfBlockData<BlockMap extends BaseBlockMap>({
}
}

return OneOfBlockData;
// Type cast to suppress error: 'OneOfBlockData' is assignable to the constraint of type 'Data', but 'Data' could be instantiated with a different subtype of constraint 'BlockDataInterface'.
return OneOfBlockData as unknown as Type<OneOfBlockDataInterface<BlockMap, Data>>;
}

interface OneOfBlockItemInputInterface extends BlockInput {
Expand Down Expand Up @@ -195,17 +200,21 @@ export function BaseOneOfBlockItemInput<BlockMap extends BaseBlockMap>({
return OneOfBlockItemInput;
}

export function BaseOneOfBlockInput<BlockMap extends BaseBlockMap>({
export function BaseOneOfBlockInput<
BlockMap extends BaseBlockMap,
Data extends BlockDataInterface = BlockDataInterface,
Input extends SimpleBlockInputInterface<Data> = SimpleBlockInputInterface<Data>,
>({
supportedBlocks,
allowEmpty,
OneOfBlockData,
OneOfBlockItemInput,
}: {
supportedBlocks: BlockMap;
allowEmpty?: boolean;
OneOfBlockData: Type<OneOfBlockDataInterface<BlockMap>>;
OneOfBlockData: Type<OneOfBlockDataInterface<BlockMap, Data>>;
OneOfBlockItemInput: Type<OneOfBlockItemInputInterface>;
}): Type<OneOfBlockInputInterface<BlockMap>> {
}): Type<OneOfBlockInputInterface<BlockMap, Data, Input>> {
for (const block in supportedBlocks) {
if (!supportedBlocks[block]) {
throw new Error(`Supported block '${block}' is undefined. This is most likely due to a circular import`);
Expand All @@ -214,7 +223,7 @@ export function BaseOneOfBlockInput<BlockMap extends BaseBlockMap>({

const supportedBlockTypes: Array<keyof BlockMap | null> = Object.keys(supportedBlocks);

class OneOfBlockInput extends BlockInput {
class OneOfBlockInput extends BlockInput<Data> {
@Transform(({ value }: { value: OneOfBlockItemInputInterface[] }) =>
value
.filter((item) => {
Expand All @@ -229,14 +238,14 @@ export function BaseOneOfBlockInput<BlockMap extends BaseBlockMap>({
)
@ArrayMinSize(allowEmpty ? 0 : 1)
@ValidateNested({ each: true })
attachedBlocks: OneOfBlockInputInterface<BlockMap>["attachedBlocks"];
attachedBlocks: OneOfBlockInputInterface<BlockMap, Data, Input>["attachedBlocks"];

@IsOptional()
@IsIn(supportedBlockTypes)
@BlockField({ nullable: allowEmpty })
activeType?: string;

transformToBlockData(): OneOfBlockDataInterface<BlockMap> {
transformToBlockData(): OneOfBlockDataInterface<BlockMap, Data> {
const { attachedBlocks, activeType, ...additionalFields } = this;

return plainToInstance(OneOfBlockData, {
Expand All @@ -247,31 +256,44 @@ export function BaseOneOfBlockInput<BlockMap extends BaseBlockMap>({
}
}

return OneOfBlockInput;
// Type cast to suppress error: 'OneOfBlockInput' is assignable to the constraint of type 'Input', but 'Input' could be instantiated with a different subtype of constraint 'SimpleBlockInputInterface<Data>'.
return OneOfBlockInput as unknown as Type<OneOfBlockInputInterface<BlockMap, Data, Input>>;
}

export type OneOfBlock<BlockMap extends BaseBlockMap> = Block<OneOfBlockDataInterface<BlockMap>, OneOfBlockInputInterface<BlockMap>>;

export interface CreateOneOfBlockOptions<BlockMap extends BaseBlockMap> {
export type OneOfBlock<
BlockMap extends BaseBlockMap,
Data extends BlockDataInterface = BlockDataInterface,
Input extends SimpleBlockInputInterface<Data> = SimpleBlockInputInterface<Data>,
> = Block<OneOfBlockDataInterface<BlockMap, Data>, OneOfBlockInputInterface<BlockMap, Data, Input>>;

export interface CreateOneOfBlockOptions<
BlockMap extends BaseBlockMap,
Data extends BlockDataInterface = BlockDataInterface,
Input extends SimpleBlockInputInterface<Data> = SimpleBlockInputInterface<Data>,
> {
supportedBlocks: BlockMap;
allowEmpty?: boolean;
OneOfBlockItemData?: Type<OneOfBlockItemDataInterface>;
OneOfBlockItemInput?: Type<OneOfBlockItemInputInterface>;
OneOfBlockData?: Type<OneOfBlockDataInterface<BlockMap>>;
OneOfBlockInput?: Type<OneOfBlockInputInterface<BlockMap>>;
OneOfBlockData?: Type<OneOfBlockDataInterface<BlockMap, Data>>;
OneOfBlockInput?: Type<OneOfBlockInputInterface<BlockMap, Data, Input>>;
}

export function createOneOfBlock<BlockMap extends BaseBlockMap>(
export function createOneOfBlock<
BlockMap extends BaseBlockMap,
Data extends BlockDataInterface = BlockDataInterface,
Input extends SimpleBlockInputInterface<Data> = SimpleBlockInputInterface<Data>,
>(
{
supportedBlocks,
allowEmpty = true,
OneOfBlockItemData = BaseOneOfBlockItemData({ supportedBlocks }),
OneOfBlockItemInput = BaseOneOfBlockItemInput({ supportedBlocks, OneOfBlockItemData }),
OneOfBlockData = BaseOneOfBlockData({ supportedBlocks, OneOfBlockItemData }),
OneOfBlockInput = BaseOneOfBlockInput({ supportedBlocks, allowEmpty, OneOfBlockData, OneOfBlockItemInput }),
}: CreateOneOfBlockOptions<BlockMap>,
}: CreateOneOfBlockOptions<BlockMap, Data, Input>,
nameOrOptions: BlockFactoryNameOrOptions,
): OneOfBlock<BlockMap> {
): OneOfBlock<BlockMap, Data, Input> {
class Meta extends AnnotationBlockMeta {
get fields(): BlockMetaField[] {
const attachedBlocksField: BlockMetaField = {
Expand Down
25 changes: 25 additions & 0 deletions packages/api/cms-api/src/blocks/createLinkBlock.test.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
import { ExternalLinkBlock } from "@comet/blocks-api";

import { InternalLinkBlock } from "../page-tree/blocks/internal-link.block";
import { createLinkBlock } from "./createLinkBlock";

describe("createLinkBlock", () => {
it("should have a title", () => {
const LinkBlock = createLinkBlock({
supportedBlocks: {
internal: InternalLinkBlock,
external: ExternalLinkBlock,
},
});

expect(
LinkBlock.blockInputFactory({
attachedBlocks: [],
activeType: "internal",
title: "Test",
})
.transformToBlockData()
.transformToSave().title,
).toBe("Test");
});
});
3 changes: 1 addition & 2 deletions packages/api/cms-api/src/blocks/createLinkBlock.ts
Original file line number Diff line number Diff line change
Expand Up @@ -10,14 +10,13 @@ import {
BlockInputInterface,
createOneOfBlock,
CreateOneOfBlockOptions,
OneOfBlock,
} from "@comet/blocks-api";
import { IsOptional, IsString } from "class-validator";

function createLinkBlock<BlockMap extends Record<string, Block<BlockDataInterface, BlockInputInterface>>>(
{ supportedBlocks, allowEmpty = false }: CreateOneOfBlockOptions<BlockMap>,
nameOrOptions: BlockFactoryNameOrOptions = "Link",
): OneOfBlock<BlockMap> {
) {
class LinkBlockItemData extends BaseOneOfBlockItemData({ supportedBlocks }) {}

class LinkBlockItemInput extends BaseOneOfBlockItemInput({ supportedBlocks, OneOfBlockItemData: LinkBlockItemData }) {}
Expand Down

0 comments on commit 7e7a4aa

Please sign in to comment.