Movatterモバイル変換


[0]ホーム

URL:


Skip to content

Navigation Menu

Sign in
Appearance settings

Search code, repositories, users, issues, pull requests...

Provide feedback

We read every piece of feedback, and take your input very seriously.

Saved searches

Use saved searches to filter your results more quickly

Sign up
Appearance settings

rovelnic/cms-saas-starter

 
 

Repository files navigation

Note

Please followhttps://github.com/episerver/cms-saas-vercel-demo for general information about how to configure your CMS SaaS instance.

How to use Visual Builder in CMS (SaaS)

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:

  1. Go toSettings >Content Types.
  2. ClickCreate New and selectElement Type from the drop-down list.
  3. EnterParagraphElement for theName andDisplay name fields.
  4. ClickCreate.
  5. ClickAdd Property and selectText from the drop-down list.
  6. EnterText for theName in theConfigure Property page.
  7. Click on theText Type drop-down menu and selectXHTML string (>255).
  8. ClickSave.

Then in order to run the sample you need to do the following:

  1. Clone this repository.
  2. Create a new file, named.env.local.
  3. From the Optimizely CMS (SaaS) dashboard, copy theSingle key from theRender Content section.
  4. In the.env.local file, enter "GRAPH_SINGLE_KEY=" and paste yourSingle key from step 3.
  5. In the.env.local file, enter "CMS_URL=" and paste yourCMS url, for exampleapp-mysuperapp.cms.optimizely.com
  6. Runyarn install.
  7. Runyarn codegen to generate graphql queries.
  8. Runyarn dev to start the site. It will run onhttps://localhost

Now go toEdit Mode and create a new experience ofBlank Experience type.

adding experience

Type in the name and hitCreate Experience.

creating 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.

applications list

Add a new application website pointing to your local nextjs application running onhttps://localhost:3000.It should look like this:

adding new application

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.

hello world

A detailed process of creating this sample Hello World app

All parts of this repository are described step by step so if you prefer to build stuff yourselfthen please find the instructions below.

Prerequisites

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.

Adding element type

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.clicking add element type

Fill in the name and display name and hit theCreate button.adding element name

You will see an empty list of properties, hit theAdd property button and add a singleTextXHTML string property:adding property

After that you should see the newly created element type in the list.added element type

Graphql generation

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)

fill GRAPH_SINGLE_KEY

yarn codegen

You should see that it successfully generated the schema tosrc/graphql folder:

codegen result

After thatsrc/graphql should contain a few files which will let you write graphql queries.

Adding first element component

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

Adding the layout component

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.

Subscribing to content changes

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

Languages

  • TypeScript71.3%
  • CSS28.2%
  • JavaScript0.5%

[8]ページ先頭

©2009-2025 Movatter.jp