@remkoj/optimizely-cms-react 3.1.1 → 3.2.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.md CHANGED
@@ -19,6 +19,8 @@ Regardless of which export you're using, the following components are available.
19
19
  | `<CmsEditable {...props} />` | This is a basic wrapper that performs the logic needed to inject the appropriate attributes into the HTML. <br/>It will use a `div` element by default for that, but you can set any React component to be used on the `as` property. The only requirement is that the property set for this accepts the `data-*` attributes injected and outputs these into the page. Any additional attributes will be passed to the output component. |
20
20
  | `<RichText {...props} />` | This is a renderer for the structured HTML output of Optimizely CMS, leveraging the `ComponentFactory` to resolve the actual React components used for the output. It prefixes all types with `RichText`<br/>The library exports a `DefaultComponents` constant, which can be used to populate the ComponentFactory with the basic HTML elements |
21
21
  | `<OptimizelyComposition {...props} />` | This is the renderer for a Visual Builder Experience. This will render the composition using the `ComponentFactory` from the context and `<CmsContent />` component.<br/><br/>The style definitions of Visual Builder will be provided to the `layoutProps` property of your CmsComponent |
22
+ | `getFactory()` | Create an instance of the `DefaultComponentFactory` and return it. See below for more information on using the Component Factory. |
23
+ | `DefaultComponentFactory` | Default implementation of the `ComponentFactory` interface. See below for more information on using the Component Factory. |
22
24
 
23
25
  ### Server Side components & functions
24
26
  In addition to the components above, the following methods are available in the `@remkoj/optimizely-cms-react/rsc` export only:
@@ -36,3 +38,12 @@ In addition to the components above, the following methods are available in the
36
38
  | --- | --- |
37
39
  | `<OptimizelyCms {...props} />` | React context to be used client side, without defaults. This needs to be initialized completely by your code. |
38
40
  | `useOptimizelyCms()` | React Hook to retrieve the current Optimizely CMS Context in your client-side component. |
41
+
42
+ ### Component Factory
43
+ The component factory allows resolution from the content type reported by Optimizely CMS into a React component that will be used to render that content. Each component must be registered through the `register()` or `registerAll()` methods. After registration, the components can be retrieved using `resolve()` and resolvability can be checked through `has()`. The JSDoc annotation has more details on the usage of each of these methods.
44
+
45
+ An entry within the factory consists of the following fields:
46
+ - ***type:*** The identifier of the content type, can either be an array of strings or a string. The implementation *should* normalize the type. The `DefaultComponentFactory` normalized to a string, using `/` as item separator.
47
+ - ***component:*** The component to do the actual rendering of the data. *The factory is indifferent to the actual type, so this could be the output of `next/dynamic` to trigger bundle splitting in Next.js.*
48
+ - ***useSuspense:*** When set to `true` the component will be wrapped in the `<Suspense></Suspense>` from React. Optional, the default value is `false`.
49
+ - ***loader:*** The component to show while Suspense is awaiting the actual component. This component is given to the `fallback` property of `<Suspense>`. Optional, the default value is `undefined`.
@@ -15,7 +15,7 @@ export declare class DefaultComponentFactory implements ComponentFactory {
15
15
  * with the factory.
16
16
  */
17
17
  constructor(initialComponents?: ComponentTypeDictionary);
18
- register(type: ComponentTypeHandle, component: ComponentType): void;
18
+ register(type: ComponentTypeHandle, component: ComponentType, useSuspense?: boolean, loader?: ComponentType): void;
19
19
  registerAll(components: ComponentTypeDictionary): void;
20
20
  has(type: ComponentTypeHandle): boolean;
21
21
  resolve(type: ComponentTypeHandle): undefined | ComponentType;
@@ -1,3 +1,5 @@
1
+ import { jsx as _jsx } from "react/jsx-runtime";
2
+ import { Suspense } from 'react';
1
3
  export const MERGE_SYMBOL = '/';
2
4
  export const EmptyComponentHandle = '$$fragment$$';
3
5
  /**
@@ -12,43 +14,62 @@ export class DefaultComponentFactory {
12
14
  * with the factory.
13
15
  */
14
16
  constructor(initialComponents) {
15
- this.registry = {};
16
- this.dbg = process.env.OPTIMIZELY_DEBUG == '1';
17
+ this.registry = new Map();
18
+ // Resolve debug mode
19
+ try {
20
+ this.dbg = process.env.OPTIMIZELY_DEBUG == '1';
21
+ }
22
+ catch {
23
+ this.dbg = false;
24
+ }
25
+ // Add provided default dictionary
17
26
  if (initialComponents)
18
27
  this.registerAll(initialComponents);
19
28
  }
20
- register(type, component) {
21
- type = processComponentTypeHandle(type);
29
+ register(type, component, useSuspense = false, loader) {
30
+ const registryKey = processComponentTypeHandle(type);
22
31
  if (this.dbg)
23
- console.log(`➕ [DefaultComponentFactory] Adding ${type}`);
24
- this.registry[type] = component;
32
+ console.log(`➕ [DefaultComponentFactory] Adding ${registryKey}`);
33
+ this.registry.set(registryKey, { type, component, useSuspense, loader });
25
34
  }
26
35
  registerAll(components) {
27
- components.forEach(c => this.register(c.type, c.component));
36
+ components.forEach(c => {
37
+ const registryKey = processComponentTypeHandle(c.type);
38
+ if (this.dbg)
39
+ console.log(`➕ [DefaultComponentFactory] Adding ${registryKey}`);
40
+ this.registry.set(registryKey, c);
41
+ });
28
42
  }
29
43
  has(type) {
30
- type = processComponentTypeHandle(type);
44
+ const registryKey = processComponentTypeHandle(type);
31
45
  if (this.dbg)
32
- console.log(`🔎 [DefaultComponentFactory] Checking for ${type}`);
33
- return Object.getOwnPropertyNames(this.registry).includes(type);
46
+ console.log(`🔎 [DefaultComponentFactory] Checking for ${registryKey}`);
47
+ return this.registry.has(registryKey);
34
48
  }
35
49
  resolve(type) {
36
- type = processComponentTypeHandle(type);
50
+ const registryKey = processComponentTypeHandle(type);
37
51
  if (this.dbg)
38
- console.log(`⚡ [DefaultComponentFactory] Resolving ${type}`);
39
- if (Object.getOwnPropertyNames(this.registry).includes(type))
40
- return this.registry[type];
41
- return undefined;
52
+ console.log(`⚡ [DefaultComponentFactory] Resolving ${registryKey}`);
53
+ const entry = this.registry.get(registryKey);
54
+ if (!entry)
55
+ return undefined; // The key is not registered in the factory
56
+ if (entry.useSuspense != true)
57
+ return entry.component; // There's no suspense, so we're using the component directly
58
+ // We need to wrap the component in a Supense
59
+ const EntryComponent = entry.component;
60
+ const EntryLoader = entry.loader;
61
+ function Suspended(props) {
62
+ return _jsx(Suspense, { fallback: EntryLoader && _jsx(EntryLoader, { ...props }), children: _jsx(EntryComponent, { ...props }) });
63
+ }
64
+ return Suspended;
42
65
  }
43
66
  extract() {
44
- const extracted = [];
45
- Object.getOwnPropertyNames(this.registry).map(typeKey => {
46
- extracted.push({
47
- type: typeKey,
48
- component: this.registry[typeKey]
49
- });
67
+ return Array.from(this.registry.entries()).map(e => {
68
+ return {
69
+ ...e[1],
70
+ type: e[0]
71
+ };
50
72
  });
51
- return extracted;
52
73
  }
53
74
  }
54
75
  function processComponentTypeHandle(handle) {
@@ -1 +1 @@
1
- {"version":3,"file":"default.js","sourceRoot":"","sources":["../../src/factory/default.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"default.js","sourceRoot":"","sources":["../../src/factory/default.tsx"],"names":[],"mappings":"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"}
@@ -7,10 +7,25 @@ export type ComponentType = (ReactComponentType<any>) | (ReactExoticComponent<an
7
7
  *
8
8
  */
9
9
  export type ComponentTypeHandle = string | string[];
10
- export type ComponentTypeDictionary = {
10
+ export type ComponentTypeDictionary = Array<ComponentTypeDictionaryEntry>;
11
+ export type ComponentTypeDictionaryEntry = {
12
+ /**
13
+ * The component type to register
14
+ */
11
15
  type: ComponentTypeHandle;
16
+ /**
17
+ * The component to bind to the type
18
+ */
12
19
  component: ComponentType;
13
- }[];
20
+ /**
21
+ * If set to 'true' the registered component will be wrapped in <Suspense />
22
+ */
23
+ useSuspense?: boolean;
24
+ /**
25
+ * The component to use as "loading" state by the <Suspense />
26
+ */
27
+ loader?: ComponentType;
28
+ };
14
29
  /**
15
30
  * Component Factory
16
31
  */
@@ -24,10 +39,13 @@ export interface ComponentFactory {
24
39
  has(type: ComponentTypeHandle): boolean;
25
40
  /**
26
41
  * Register an individual component
42
+ *
27
43
  * @param type The component type to register
28
- * @param componentType The component to bind to the type
44
+ * @param component The component to bind to the type
45
+ * @param useSuspense If set to 'true' the registered component will be wrapped in <Suspense />
46
+ * @param loader The component to use as "loading" state by the <Suspense />
29
47
  */
30
- register(type: ComponentTypeHandle, componentType: ComponentType): void;
48
+ register(type: ComponentTypeHandle, component: ComponentType, useSuspense?: boolean, loader?: ComponentType): void;
31
49
  /**
32
50
  * Register all components provided through the dictionary
33
51
  *
package/dist/types.d.ts CHANGED
@@ -1,7 +1,7 @@
1
1
  import type { PropsWithChildren, ComponentType as ReactComponentType } from "react";
2
2
  import type { DocumentNode } from "graphql";
3
3
  import type { TypedDocumentNode } from '@graphql-typed-document-node/core';
4
- import type { ContentLinkWithLocale, InlineContentLinkWithLocale } from "@remkoj/optimizely-graph-client";
4
+ import type { ContentLinkWithLocale, ContentLink, InlineContentLinkWithLocale } from "@remkoj/optimizely-graph-client";
5
5
  export type ContentType = string[];
6
6
  export type {
7
7
  /**
@@ -30,7 +30,8 @@ export type CmsComponentProps<T, L extends Record<string, any> = Record<string,
30
30
  */
31
31
  layoutProps?: L;
32
32
  }>;
33
- export type ContentQueryProps<LocaleType = string> = ContentLinkWithLocale<LocaleType> & {
33
+ export type ContentQueryProps<LocaleType = string> = ContentLink & {
34
+ locale?: Array<LocaleType> | LocaleType | null;
34
35
  path?: string | null;
35
36
  domain?: string | null;
36
37
  };
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "@remkoj/optimizely-cms-react",
3
3
  "license": "Apache-2.0",
4
- "version": "3.1.1",
4
+ "version": "3.2.0",
5
5
  "packageManager": "yarn@4.1.1",
6
6
  "repository": "https://github.com/remkoj/optimizely-dxp-clients.git",
7
7
  "author": "Remko Jantzen <693172+remkoj@users.noreply.github.com>",
@@ -28,7 +28,7 @@
28
28
  }
29
29
  },
30
30
  "devDependencies": {
31
- "@remkoj/optimizely-graph-client": "3.1.1",
31
+ "@remkoj/optimizely-graph-client": "3.2.0",
32
32
  "@types/crypto-js": "^4.2.2",
33
33
  "@types/node": "^22.9.0",
34
34
  "@types/react": "^18.3.12",