How to Develop Extensions - duhdugg/preaction-cms GitHub Wiki
You can extend Preaction CMS in the following ways:
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)
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
andgetPropsDataValueHandler
props to provide a UI for customizing your component.
- The Settings component must accept a
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.
- https://github.com/duhdugg/preactioncms-blockext-example
- https://github.com/duhdugg/preactioncms-blockext-socialbadge
- https://github.com/duhdugg/preactioncms-blockext-businesslocation
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.
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 anorder
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:
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 }
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.