diff --git a/.gitignore b/.gitignore index f2704de5..5a2da265 100755 --- a/.gitignore +++ b/.gitignore @@ -2,5 +2,6 @@ node_modules dist dist-v2 +lib/cypress/screenshots/ lib/cypress/videos -.next \ No newline at end of file +.next diff --git a/lib/rsc/common.ts b/lib/rsc/common.ts new file mode 100644 index 00000000..b909028e --- /dev/null +++ b/lib/rsc/common.ts @@ -0,0 +1,70 @@ +import { storyblokInit as sbInit } from "@storyblok/js"; + +import { + SbReactComponentsMap, + SbReactSDKOptions, + StoryblokClient, +} from "../types"; + +let storyblokApiInstance: StoryblokClient = null; +let componentsMap: SbReactComponentsMap = {}; +let enableFallbackComponent: boolean = false; +let customFallbackComponent: React.ElementType = null; + +export const useStoryblokApi = (): StoryblokClient => { + if (!storyblokApiInstance) { + console.error( + "You can't use getStoryblokApi if you're not loading apiPlugin." + ); + } + + return storyblokApiInstance; +}; + +export const setComponents = (newComponentsMap: SbReactComponentsMap) => { + componentsMap = newComponentsMap; + return componentsMap; +}; + +export const getComponent = (componentKey: string) => { + if (!componentsMap[componentKey]) { + console.error(`Component ${componentKey} doesn't exist.`); + return false; + } + + return componentsMap[componentKey]; +}; + +export const getEnableFallbackComponent = () => enableFallbackComponent; +export const getCustomFallbackComponent = () => customFallbackComponent; + +export const storyblokInit = ( + pluginOptions: SbReactSDKOptions = {} +): (() => StoryblokClient) => { + if (storyblokApiInstance) { + return () => storyblokApiInstance; + } + + const { storyblokApi } = sbInit(pluginOptions); + storyblokApiInstance = storyblokApi; + + componentsMap = pluginOptions.components; + enableFallbackComponent = pluginOptions.enableFallbackComponent; + customFallbackComponent = pluginOptions.customFallbackComponent; + + return () => storyblokApi; +}; + +export { default as StoryblokComponent } from "./storyblok-component"; +export { + storyblokEditable, + apiPlugin, + loadStoryblokBridge, + useStoryblokBridge, + registerStoryblokBridge, + renderRichText, + RichTextResolver, + RichTextSchema, +} from "@storyblok/js"; + +export * from "../types"; diff --git a/lib/rsc/index.ts b/lib/rsc/index.ts index 477e23f3..2e8e3a6b 100644 --- a/lib/rsc/index.ts +++ b/lib/rsc/index.ts @@ -1,4 +1,4 @@ -export { default as BridgeLoader } from '../bridge-loader'; -export { default as StoryblokBridgeLoader } from '../bridge-loader'; -export * from '../common'; -export { default as StoryblokStory } from '../story'; +export * from "./common"; +export { default as StoryblokStory } from "./story"; +export { default as BridgeLoader } from "../bridge-loader"; +export { default as StoryblokBridgeLoader } from "../bridge-loader"; diff --git a/lib/rsc/story.tsx b/lib/rsc/story.tsx new file mode 100644 index 00000000..aed63e3a --- /dev/null +++ b/lib/rsc/story.tsx @@ -0,0 +1,24 @@ +"use client"; + +import React, { forwardRef } from "react"; +import { useStoryblokState } from "../common/client"; +import { ISbStoryData, StoryblokBridgeConfigV2 } from "../types"; +import { StoryblokComponent } from "./common"; + +interface StoryblokStoryRSCProps { + story: ISbStoryData; + bridgeOptions: StoryblokBridgeConfigV2; + [key: string]: unknown; +} + +const StoryblokStory = forwardRef( + ({ story, bridgeOptions, ...restProps }, ref) => { + if (typeof story.content === "string") { + story.content = JSON.parse(story.content); + } + story = useStoryblokState(story, bridgeOptions); + return ; + } +); + +export default StoryblokStory; diff --git a/lib/rsc/storyblok-component.tsx b/lib/rsc/storyblok-component.tsx new file mode 100644 index 00000000..1ec207c8 --- /dev/null +++ b/lib/rsc/storyblok-component.tsx @@ -0,0 +1,54 @@ +import React, { forwardRef } from "react"; +import { + getComponent, + getEnableFallbackComponent, + getCustomFallbackComponent, +} from "./index"; +import type { SbBlokData } from "../types"; + +interface StoryblokComponentProps { + blok: SbBlokData; + [key: string]: unknown; +} + +const StoryblokComponent = forwardRef( + ({ blok, ...restProps }, ref) => { + if (!blok) { + console.error( + "Please provide a 'blok' property to the StoryblokComponent" + ); + return ( +
Please provide a blok property to the StoryblokComponent
+ ); + } + + const Component = getComponent(blok.component); + + if (Component) { + return ; + } + + if (getEnableFallbackComponent()) { + const CustomFallbackComponent = getCustomFallbackComponent(); + + if (CustomFallbackComponent) { + return ; + } else { + return ( + <> +

+ Component could not be found for blok{" "} + {blok.component}! Is it configured correctly? +

+ + ); + } + } + + return
; + } +); + +StoryblokComponent.displayName = "StoryblokComponent"; + +export default StoryblokComponent; diff --git a/playground-next13-live-editing/app/page.tsx b/playground-next13-live-editing/app/page.tsx index 05f9bca5..64308e08 100644 --- a/playground-next13-live-editing/app/page.tsx +++ b/playground-next13-live-editing/app/page.tsx @@ -2,8 +2,8 @@ import type { ISbStoriesParams, StoryblokClient, } from '@storyblok/react/rsc'; +import { getStoryblokApi } from "@/lib/storyblok"; import { - getStoryblokApi, StoryblokStory, } from '@storyblok/react/rsc'; diff --git a/playground-next13-live-editing/lib/storyblok.ts b/playground-next13-live-editing/lib/storyblok.ts new file mode 100644 index 00000000..9218b0c4 --- /dev/null +++ b/playground-next13-live-editing/lib/storyblok.ts @@ -0,0 +1,6 @@ +import { apiPlugin, storyblokInit } from "@storyblok/react/rsc"; + +export const getStoryblokApi = storyblokInit({ + accessToken: "OurklwV5XsDJTIE1NJaD2wtt", + use: [apiPlugin], +}); diff --git a/playground-next13-rsc/app/page.tsx b/playground-next13-rsc/app/page.tsx index 9c2d9fce..82ff1613 100644 --- a/playground-next13-rsc/app/page.tsx +++ b/playground-next13-rsc/app/page.tsx @@ -2,8 +2,8 @@ import type { ISbStoriesParams, StoryblokClient, } from '@storyblok/react/rsc'; +import { getStoryblokApi } from "@/lib/storyblok"; import { - getStoryblokApi, StoryblokComponent, } from '@storyblok/react/rsc'; diff --git a/playground-next13-rsc/lib/storyblok.ts b/playground-next13-rsc/lib/storyblok.ts new file mode 100644 index 00000000..9218b0c4 --- /dev/null +++ b/playground-next13-rsc/lib/storyblok.ts @@ -0,0 +1,6 @@ +import { apiPlugin, storyblokInit } from "@storyblok/react/rsc"; + +export const getStoryblokApi = storyblokInit({ + accessToken: "OurklwV5XsDJTIE1NJaD2wtt", + use: [apiPlugin], +});