- Notifications
You must be signed in to change notification settings - Fork0
License
episerver/cms-vb-opticon-demo
Folders and files
| Name | Name | Last commit message | Last commit date | |
|---|---|---|---|---|
Repository files navigation
Note
Please followhttps://github.com/episerver/cms-saas-vercel-demo for general information about how to configure your CMS SaaS instance.
This repository contains a Hello World example of the Optimizely Visual Builder.Ensure you have a running an Optimizely CMS (SaaS) instance. For more information on CMS (SaaS) see thedeveloper documentation orend-user documentation.
If you have an empty CMS instance, and you want to see how it allworks go to your CMS (SaaS) instance and:
- Go toSettings >Content Types.
- ClickCreate New and selectElement Type from the drop-down list.
- EnterParagraphElement for theName andDisplay name fields.
- ClickCreate.
- ClickAdd Property and selectText from the drop-down list.
- EnterText for theName in theConfigure Property page.
- Click on theText Type drop-down menu and selectXHTML string (>255).
- ClickSave.
Then in order to run the sample you need to do the following:
- Clone this repository.
- Create a new file, named
.env.local. - From the Optimizely CMS (SaaS) dashboard, copy the
Single keyfrom theRender Content section. - In the
.env.localfile, enter "GRAPH_SINGLE_KEY=" and paste yourSingle keyfrom step 3. - In the
.env.localfile, enter "CMS_URL=" and paste yourCMSurl, for exampleapp-mysuperapp.cms.optimizely.com - Run
yarn install. - Run
yarn codegento generate graphql queries. - Run
yarn devto start the site. It will run onhttps://localhost
Now go toEdit Mode and create a new experience ofBlank Experience type.
Type in the name and hitCreate Experience.
If you would like to see the site preview in Edit Mode of your SaaS instanceyou will need to configure it in theApplications section ofSettings.
Add a new application website pointing to your local nextjs application running onhttps://localhost:3000.It should look like this:
Note
More info onApplications can be found herehttps://docs.developers.optimizely.com/content-management-system/v1.0.0-CMS-SaaS/docs/create-a-demo-site-using-cms-saas-and-netlify#cms-saas-ui-configuration
Now go back toEdit Mode and to yourMyExperience.Please add a new section, row, column and an element ofParagraph Type.
Fill in the textHello world! and you should see it in the preview.
All parts of this repository are described step by step so if you prefer to build stuff yourselfthen please find the instructions below.
We are going to create a simple Next.js app which will consume data from our SaaS instance
Let's create a new Next.js application based on thehello world example template
npx create-next-app@latest vb-test --use-yarn --example hello-world vb-test
Let's add graphql support by installing the following dependencies:
yarn add @apollo/client graphql
Now we need to install development tools which will generate objects based on your graphql schema.
yarn add --dev @graphql-codegen/cli @graphql-codegen/client-preset @parcel/watcher
Now let's add a configuration file for the codegen plugin. Please create a new file in the root foldercodegen.tsand paste the following code:
import{CodegenConfig}from"@graphql-codegen/cli";import{loadEnvConfig}from"@next/env";loadEnvConfig(process.cwd());constgraphUrl=process.env.GRAPH_URLconstgraphSingleKey=process.env.GRAPH_SINGLE_KEYconstconfig :CodegenConfig={schema:`https://${graphUrl}/content/v2?auth=${graphSingleKey}`,documents:["src/**/*.{ts,tsx}"],ignoreNoDocuments:true,generates:{'./src/graphql/':{preset:'client',plugins:[],}}}exportdefaultconfig
We now need to add a new script to package.json
"codegen": "graphql-codegen"
This script will generate types based your graphql schema.
Before we run the codegen let's add a simple Element type to our SaaS CMS instance.Please openSettings andContent types screen.
Click onCreate New... menu item and chooseElement type option.![]()
Fill in the name and display name and hit theCreate button.![]()
You will see an empty list of properties, hit theAdd property button and add a singleTextXHTML string property:![]()
After that you should see the newly created element type in the list.![]()
Now let's go back to our Next.js application and let's try to run the codegen script.First you will need to fill in your GRAPH_SINGLE_KEY into.env.local file (create it if it does not exist)
yarn codegen
You should see that it successfully generated the schema tosrc/graphql folder:
After thatsrc/graphql should contain a few files which will let you write graphql queries.
Let's create a new React component which will display ourParagraphElement.
The code will be something like this:
import{FragmentType,useFragment}from'../../graphql/fragment-masking'import{graphql}from'@/graphql'exportconstParagraphElementFragment=graphql(/* GraphQL */` fragment paragraphElement on ParagraphElement { Text { html } }`)constParagraphElementComponent=(props:{paragraphElement:FragmentType<typeofParagraphElementFragment>})=>{constparagraphElement=useFragment(ParagraphElementFragment,props.paragraphElement)//@ts-ignorereturn<divdangerouslySetInnerHTML={{__html:paragraphElement.Text?.html}}></div>}exportdefaultParagraphElementComponent
Now we need the master component which will be responsible for rendering the layout (sections/row/columns):
importReact,{FC,useEffect}from'react'import{useQuery}from'@apollo/client'import{graphql}from'@/graphql'importCompositionNodeComponentfrom'./CompositionNodeComponent'import{onContentSaved}from"@/helpers/onContentSaved";exportconstVisualBuilder=graphql(/* GraphQL */`query VisualBuilder($key: String, $version: String) { _Experience(where: { _metadata: { key: { eq: $key } } _or: { _metadata: { version: { eq: $version } } } }) { items { composition { grids: nodes { ... on CompositionStructureNode { key rows: nodes { ... on CompositionStructureNode { key columns: nodes { ... on CompositionStructureNode { key elements: nodes { ...compositionElementNode } } } } } } } } _metadata { key version, } } }}`)interfaceVisualBuilderProps{key?:string;version?:string;}constVisualBuilderComponent:FC<VisualBuilderProps>=({ key, version})=>{constvariables:Record<string,unknown>={};if(version){variables.version=version;}if(key){variables.key=key;}const{ data, refetch}=useQuery(VisualBuilder,{variables:variables});useEffect(()=>{onContentSaved(_=>{refetch();})},[]);constexperiences=data?._Experience?.items;if(!experiences){returnnull;}constexperience:any=experiences[experiences.length-1];if(!experience){returnnull;}return(<divclassName="relative w-full flex-1 vb:outline"><divclassName="relative w-full flex-1 vb:outline">{experience?.composition?.grids?.map((grid:any)=><divkey={grid.key}className="relative w-full flex flex-col flex-nowrap justify-start vb:grid"data-epi-block-id={grid.key}>{grid.rows?.map((row:any)=><divkey={row.key}className="flex-1 flex flex-row flex-nowrap justify-start vb:row">{row.columns?.map((column:any)=>(<divclassName="flex-1 flex flex-col flex-nowrap justify-start vb:col"key={column.key}>{column.elements?.map((element:any)=><divdata-epi-block-id={element?.key}key={element?.key}><CompositionNodeComponentcompositionElementNode={element}/></div>)}</div>))}</div>)}</div>)}</div></div>)}exportdefaultVisualBuilderComponent
It's basically a nested loop on a few levels. First we iterate over sections, then rows, then columns and finally elements.We are wrapping each of those layout items into basic tailwind grid classes.
In this simple example there is just one element type but we don't want to hardcode anything so here is a patternthat you can use to use a different element component pernodeType:
import{FragmentType,useFragment}from'../../graphql/fragment-masking'import{graphql}from'@/graphql'importParagraphElementComponentfrom'../elements/ParagraphElementComponent'exportconstCompositionElementNodeFragment=graphql(/* GraphQL */` fragment compositionElementNode on CompositionElementNode { key element { _metadata { types } ...paragraphElement } }`)constCompositionElementNodeComponent=(props:{compositionElementNode:FragmentType<typeofCompositionElementNodeFragment>})=>{constcompositionElementNode=useFragment(CompositionElementNodeFragment,props.compositionElementNode)constelement=compositionElementNode.elementswitch(element?.__typename){case"ParagraphElement":return<ParagraphElementComponentparagraphElement={element}/>default:return<>NotImplementedException</>}}exportdefaultCompositionElementNodeComponent
As you can see based onelement.__typename we can use different components - in ourexample we will useParagraphElementComponent.
You need to subscribe to a special event in order to know once content has been updated.
In this repo the subscription is already done inonContentSaved.ts
window.addEventListener("optimizely:cms:contentSaved",(event:any)=>{constmessage=event.detailasContentSavedEventArgs;});
where is defined as following:
interfaceContentSavedEventArgs{contentLink:string;previewUrl:string;isIndexed:boolean;properties:PropertySaved[];parentId?:string;sectionId?:string;}
More details here:https://docs.developers.optimizely.com/content-management-system/v1.0.0-CMS-SaaS/docs/enable-live-preview#refresh-the-applications-view-when-content-has-changed
About
Resources
License
Uh oh!
There was an error while loading.Please reload this page.
Stars
Watchers
Forks
Releases
Packages0
Uh oh!
There was an error while loading.Please reload this page.
Languages
- TypeScript71.3%
- CSS28.2%
- JavaScript0.5%