diff --git a/packages/optimizely-cms-sdk/src/infer.ts b/packages/optimizely-cms-sdk/src/infer.ts index 1f0c8d13..fe718e65 100644 --- a/packages/optimizely-cms-sdk/src/infer.ts +++ b/packages/optimizely-cms-sdk/src/infer.ts @@ -33,6 +33,7 @@ import { PublicRawFileAsset, PublicVideoAsset, } from './model/assets.js'; +import { DisplayTemplate } from './model/displayTemplates.js'; /** Forces Intellisense to resolve types */ export type Prettify = { @@ -200,9 +201,28 @@ type InferFromContentType = Prettify< InferredBase & InferProps & InferExperience & InferSection >; +/** Infers the TypeScript type for a display setting */ +type InferFromDisplayTemplate = T extends { + settings: infer S; +} + ? { + [K in keyof S]: S[K] extends { + choices: Record; + editor: infer E; + } + ? E extends 'select' + ? keyof S[K]['choices'] + : E extends 'checkbox' + ? boolean + : never + : never; + } + : {}; + /** Infers the Graph response types of `T`. `T` can be a content type or a property */ // prettier-ignore export type Infer = - T extends AnyContentType ? InferFromContentType -: T extends AnyProperty ? InferFromProperty -: unknown; + T extends DisplayTemplate ? InferFromDisplayTemplate + : T extends AnyContentType ? InferFromContentType + : T extends AnyProperty ? InferFromProperty + : unknown; diff --git a/packages/optimizely-cms-sdk/src/model/__test__/parseDisplaySettings.test.ts b/packages/optimizely-cms-sdk/src/model/__test__/parseDisplaySettings.test.ts index 129c548d..8cbf2782 100644 --- a/packages/optimizely-cms-sdk/src/model/__test__/parseDisplaySettings.test.ts +++ b/packages/optimizely-cms-sdk/src/model/__test__/parseDisplaySettings.test.ts @@ -33,4 +33,4 @@ describe('parseDisplaySettings', () => { const result = parseDisplaySettings(input); expect(result).toEqual(undefined); }); -}); +}); \ No newline at end of file diff --git a/packages/optimizely-cms-sdk/src/model/displayTemplates.ts b/packages/optimizely-cms-sdk/src/model/displayTemplates.ts index b7126954..e15a5e83 100644 --- a/packages/optimizely-cms-sdk/src/model/displayTemplates.ts +++ b/packages/optimizely-cms-sdk/src/model/displayTemplates.ts @@ -1,4 +1,4 @@ -import { DisplaySettingsType } from '../infer.js'; +import { DisplaySettingsType, Infer } from '../infer.js'; import { BaseTypes } from './contentTypes.js'; // section node types @@ -64,17 +64,17 @@ export type DisplayTemplate = T & { export function parseDisplaySettings( displaySettings?: DisplaySettingsType[] | null -): Record | undefined { +): Record | undefined { if (!displaySettings) { return undefined; // Return undefined if displaySettings is not provided } - const result: Record = {}; // Initialize an empty object + const result: Record = {}; // Initialize an empty object // Iterate over the input array - for (const item of displaySettings) { + for (const { key, value } of displaySettings) { // Assign the value to the key in the result object - result[item.key] = item.value; + result[key] = value === 'true' ? true : value === 'false' ? false : value; } return result; diff --git a/packages/optimizely-cms-sdk/src/model/index.ts b/packages/optimizely-cms-sdk/src/model/index.ts index a2a83944..2c2f86ab 100644 --- a/packages/optimizely-cms-sdk/src/model/index.ts +++ b/packages/optimizely-cms-sdk/src/model/index.ts @@ -12,7 +12,7 @@ export function contentType( /** Defines a Optimizely CMS display template */ export function displayTemplate( options: T -): T & { __type: 'displayTemplate' } { +): DisplayTemplate { return { ...options, __type: 'displayTemplate' }; } diff --git a/packages/optimizely-cms-sdk/src/react/server.tsx b/packages/optimizely-cms-sdk/src/react/server.tsx index bb64a54e..4a55436c 100644 --- a/packages/optimizely-cms-sdk/src/react/server.tsx +++ b/packages/optimizely-cms-sdk/src/react/server.tsx @@ -11,6 +11,7 @@ import { DisplaySettingsType, ExperienceCompositionNode, InferredContentReference, + Infer, } from '../infer.js'; import { isComponentNode } from '../util/baseTypeUtil.js'; import { parseDisplaySettings } from '../model/displayTemplates.js'; @@ -89,7 +90,7 @@ type OptimizelyComponentProps = { __composition?: ExperienceCompositionNode; }; - displaySettings?: Record; + displaySettings?: Record; }; export async function OptimizelyComponent({ @@ -127,12 +128,12 @@ export type StructureContainerProps = { node: ExperienceStructureNode; children: React.ReactNode; index?: number; - displaySettings?: Record; + displaySettings?: Record; }; export type ComponentContainerProps = { node: ExperienceComponentNode; children: React.ReactNode; - displaySettings?: Record; + displaySettings?: Record; }; export type StructureContainer = ( props: StructureContainerProps @@ -150,7 +151,7 @@ export function OptimizelyExperience({ }) { return nodes.map((node) => { const tag = getDisplayTemplateTag(node.displayTemplateKey); - const parsedDisplaySettings = parseDisplaySettings(node.displaySettings); + const parsedDisplaySettings = parseDisplaySettings(node.displaySettings); if (isComponentNode(node)) { const Wrapper = ComponentWrapper ?? React.Fragment; diff --git a/samples/nextjs-template/src/components/Article.tsx b/samples/nextjs-template/src/components/Article.tsx index 734136a0..c83cda72 100644 --- a/samples/nextjs-template/src/components/Article.tsx +++ b/samples/nextjs-template/src/components/Article.tsx @@ -1,4 +1,4 @@ -import { contentType, Infer } from '@optimizely/cms-sdk'; +import { contentType, displayTemplate, Infer } from '@optimizely/cms-sdk'; import { getPreviewUtils } from '@optimizely/cms-sdk/react/server'; export const ArticleContentType = contentType({ @@ -21,17 +21,37 @@ export const ArticleContentType = contentType({ }, }); +export const TeaserDisplayTemplate = displayTemplate({ + key: 'TeaserDisplayTemplate', + displayName: 'TeaserDisplayTemplate', + isDefault: false, + baseType: '_component', + settings: { + orientation: { + editor: 'select', + displayName: 'Teaser Orientation', + sortOrder: 0, + choices: { + vertical: { displayName: 'Vertical', sortOrder: 1 }, + horizontal: { displayName: 'Horizontal', sortOrder: 2 }, + }, + }, + }, +}); + type Props = { opti: Infer; + displaySettings?: Infer; }; -export default function Article({ opti }: Props) { +export default function Article({ opti, displaySettings }: Props) { const { pa } = getPreviewUtils(opti); return (

{opti.heading}

{opti.subtitle}

+

{displaySettings?.orientation}

); } +