How to Develop Extensions - duhdugg/preaction-cms GitHub Wiki

You can extend Preaction CMS in the following ways:

Server-Side Middleware Extensions

When starting the server, lib/ext scans the ext directory for other directories containing an index.js file. If such a file exists and also exports a middleware function, that will be called ahead of the default middleware in Preaction CMS. The middleware function can either be an express instance, or a function which takes the arguments: (req, res, next)

Client-Side Block Extensions

By default, Preaction CMS only supports 3 block types: Content, Iframe, and Navigation. You can add more block types by putting them in the blockExtensions object in src/ext/index.js.

A block extension must meet the following requirements in order to work correctly:

  • It must export a React component as an ES Module
  • The component should accept a preaction object prop
  • The component should have an attribute extensionType set to 'block' (note: this isn't enforced now, but will be in the future)
  • The component should have an attribute label set to a string that gives the block a name (note: this is used to populate the "Add Block" menu)
  • The component must have an attribute Settings which is another React component that should meet the following requirements:
    • The Settings component must accept a propsData object prop
    • The Settings component must accept a getPropsDataValueHandler function prop
    • The Settings component should also render inputs which use the propsData and getPropsDataValueHandler props to provide a UI for customizing your component.

Take a look at the following examples to see extensions that meet these requirements. These projects are good boilerplates to use for creating your own block extension. It is not strictly necessary to use a project like these to build your extensions, but it should help with providing an environment for quick development iterations.

Example

Let's say you've decided to use the "Social Badge" extension from the link above in your Preaction CMS instance. You've cloned the repo, installed its dependencies, and compiled the extension using the build script from the package. You should have a preactioncms-blockext-socialbadge.esm.js file. This is the file you need to import into your src/ext/index.js file, which should now look like this:

import SocialBadge from './preactioncms-blockext-socialbadge.esm.js'
const blockExtensions = { SocialBadge }
const menuExtensions = {}
export { blockExtensions, menuExtensions }

After your src/ext/index.js file is updated, run yarn build.

You should be mindful of how each client-side extension impacts the bundle size when running yarn build. You can run yarn analyze to visualize this impact. If you notice that modules from the node_modules directory of the extension are being packaged into your client bundle, try removing that node_modules directory from the extension's project folder after building it, and before running yarn build on Preaction CMS.

You may also wish to do code-splitting on your block extensions to keep your core bundle to a minimum, where the extension will be loaded as a separate JS file only when a page is rendered which uses it. In this case, you can use the @loadable/component library in src/ext/index.js as follows:

import React from 'react'
import loadable from '@loadable/component'
import { Spinner } from '@preaction/bootstrap-clips'
const SocialBadge = loadable(
  () => import('./preactioncms-blockext-socialbadge.esm.js'),
  {
    resolveComponent: (module) => module.SocialBadge,
    fallback: <Spinner />,
  }
)
// note: this method requires that you also set the extensionType and label attributes here
// otherwise, this information will be blank until the component has loaded
SocialBadge.extensionType = 'block'
SocialBadge.label = 'Social Badges'
const blockExtensions = { SocialBadge }
const menuExtensions = {}
export { blockExtensions, menuExtensions }

Note: the above example also uses React to render a fallback (in this case, the Spinner component from @preaction/bootstrap-clips), although this is not required.

Client-Side Menu Extensions

You may also add menu extensions to Preaction CMS. A menu extension must meet the following requirements:

  • It exports a function which accepts the preaction object as its only argument.
  • The function returns an object which can be used to render a NavItem from the @preaction/bootstrap-clips library. It may also have an order number property which can be used to set the sorting priority of the menu item.

Take a look at the following example to see an extension that meets these requirements:

Example

Create the following menu extension at src/ext/menuext-example.js:

function example(preaction) {
  return {
    name:
      preaction.page && preaction.page.title
        ? `Search Wikipedia: ${preaction.page.title}`
        : 'Wikipedia',
    href: 'https://en.wikipedia.com',
    onClick: (event) => {
      if (preaction.page && preaction.page.title) {
        event.preventDefault()
        window.open(
          'https://en.wikipedia.org/w/index.php?search=' +
            encodeURIComponent(preaction.page.title),
          '_blank',
          'noopener,noreferrer'
        )
      }
    },
    order: 20,
  }
}

example.extensionType = 'menu'

export { example }

Note: This is a trivial example, but clicking on this menu item launches a Wikipedia search on the current page title.

To use this extension, your src/ext/index.js file should look like the following:

import { example } from './menuext-example.js'
const blockExtensions = {}
const menuExtensions = { example }
export { blockExtensions, menuExtensions }

Tracking Changes

You may wish to alter or remove the src/.gitignore and/or src/ext/.gitignore files after adding extensions to your local Preaction CMS repo.

⚠️ **GitHub.com Fallback** ⚠️