Upgrade guides

Upgrade Guide for Storefront Live Preview

To use the preview feature of the Page Editor, you have to make some adjustments to your Storefront. If you use the latest version of the Makaira Storefront Starterkit the preview will work out of the box. The Starterkit contains all changes which are described on this page, so you can also take a look there to find a working example.

Required code changes

📘

tl;dr - Patch with all necessary code changes

If you just want to see the code changes at a glance, just checkout the commit - there you can find all necessary changes that need to be applied to your code. The easiest way is to merge the stable branch of https://github.com/MakairaIO/storefront-starter-kit

If some changes are unclear you can find the explanations below.

Deprecation of the main section

The Makaira page structure no longer contains a main section. From now on, all pages consist only of a top and a bottom section. This was already the case for listing pages but now applies to all page types, especially landing pages.

To adopt these breaking changes, you have to make some edits to frontend/LandingPage/index.js
At first, you have to adopt the check in render function to match the new structure

const config = pageData.data.config || {}

if (!config.bottom && !config.top) return null

After that, you have to render two content elements components from now on, and you can also already include the <ProductList /> component. By doing this, your Storefront is able to display product grids, which will be possible in a future Makaira version.

<ContentElements elements={config.top?.elements} />
<ProductList />
<ContentElements elements={config.bottom?.elements} />

Introduction of the new preview

How does it work?

The Page Editor will load your Storefront at the URL https://YOUR_DOMAIN.COM/preview inside an iFrame. After successful load, the Makaira Backend will use the Window.postMessage() function to communicate with the Storefront. Initially, a handshake is made where the Makaira Admin UI asks the Storefront to report its current version, so that we know which functionality your Storefront currently supports. After that, the Makaira Admin UI will send the new page data via the postMessage function to the Storefront when changes are made in the edit screen.

📘

tl;dr The complete new preview.js file can be found here

This is also included in the patch file mentioned in the beginning of this guide.

1. New route "/preview"

Inside the folder pages/frontend create a new file preview.js. The content of this file should match the content of your main Storefront entry point. The entry point is normally located inside pages/frontend/entry.js. The only difference for the preview route is, that this route won't fetch the page data directly from the API, but receives the data by listening for messages.

You also have to register the new route in the server/index.js. You can do this by adding the following code

/**
* Route handler for preview endpoint.
*/
server.get('/preview', (req, res) => {
  app.render(req, res, '/frontend/preview', {
    ...req.params,
  })
})

🚧

The preview route must be palced above the default route server.get('*', (req, res) =>

2. Set the initial state of the preview route

In the next steps, we will assume, that you're using a class component for the preview page.

The initial state should contain a page type of "loading" so that we can display a loading screen until the data is received from the Page Editor. Also, the state option isPreview will be important later on, so that we can correctly update the state.

constructor(props) {
  super(props)

  this.state = {
    pageData: {
      type: 'loading',
    },
    isPreview: true,
  }
}

3. Listening for messages inside the preview route

To communicate with the Makaira Backend, the preview route needs an event handler for the message event.

componentDidMount() {
    window.addEventListener('message', this.updateStateForPreview)
}

componentWillUnmount() {
    window.removeEventListener('message', this.updateStateForPreview)
}

Next up, you'll have to add the event handler function itself. At first in this function you should make some security checks, so that only the Makaira Backend is allowed to update the page data via the postMessage function.
After this, the response for the reportVersion action is required. Here we will answer with the responseVersion action and the current version of your Storefront (at the moment "1.0" as version will be fine and allow all preview features) as a payload.
The following lines are the most important because they will update the state with the new preview page data when the action update is received.

updateStateForPreview = (event) => {
  const { source, payload, action } = event.data

  // Check if we get the data from makaira backend
  if (event.origin !== process.env.MAKAIRA_API_URL) return

  // Check if it is also send by the makaira backend
  if (source !== 'makaira-bridge') return

  // The makaira backend wants to know which version of the page editor preview
  // this storefront supports. We answer here with the set current version.
  if (action === 'reportVersion') {
    event.source.postMessage(
      {
        source: 'makaira-bridge',
        action: 'responseVersion',
        version: '1.0',
      },
      event.origin
    )
  // Update the state which will update the GlobalDataProvider when we receive
  // new page data from the makaira backend.
  } else if (action === 'update') {
    this.setState({ pageData: payload.data, isPreview: true })
  }
}

4. Update the render function of the preview route

The render method only differs at three points from the render method of your entry.js.
At first, we will load the pageData out of the state and not out of the props.

const { pageData } = this.state

Then we will add a check for the loading state directly after destructing the pageData variable.

if (type === 'loading') {
    return (
        <Head>
            <meta name="robots" content="noindex" />
        </Head>
    )
}

Here you should return a meta tag so that the preview route won't be indexed by any search engine. The Head tag is imported with import Head from 'next/head'
The last change in the render function, is to replace the properties that are applied to the GlobalDataProvider

<GlobalDataProvider {...this.state} params={{}}>

5. Update the GlobalDataProvider

One small change is also required at the GlobalDataProvider, so that all changes we supply as properties to the GlobalDataProvider are directly copied to the state.

static getDerivedStateFromProps(props, state) {
    if (state.isPreview) return props

    …
}

6. Adding a placeholder for not implemented components

Sometimes when creating a new page in the Page Editor, not all used components are already implemented in the Storefront. To directly see this in the preview, you can add a fallback component, which is displayed when no mapping for the component name could be found.

To add this placeholder, simply add a fallback to the component mapping inside patterns/core/ContentElements/index.js

const Component = components[entry.component] ?? NoComponent

and add the name of the component to the properties of the component.

<Component {...entry.properties.content} name={entry.component} />

You can find an example implementation of NoComponent in the Storefront Starterkit Repository.


Preview validation

To validate that all your changes work, you can start the Storefront locally and set the domain of the preview to localhost:PORT. Then open the developer tools of your browser. If everything works correctly, you'll find two log lines in the console.