Editable container components
Fixed components provide some flexibility for authoring SPA content, however this approach is rigid and requires developers to define the exact composition of the editable content. To support the creation of exceptional experiences by authors, SPA Editor supports the use of container components in the SPA. Container components allow authors to drag and drop allowed components into the container, and author them, just like they can in traditional AEM Sites authoring!
In this chapter, we add an editable container to the home view allowing authors to compose and layout rich content experiences using Editable React components directly in the SPA.
Update the WKND App
To add a container component to the Home view:
- Import the AEM React Editable Component’s
ResponsiveGrid
component - Import and register custom Editable React Components (Text and Image) for use in the ResponsiveGrid component
Use the ResponsiveGrid component
To add an editable area to the Home view:
-
Open and edit
react-app/src/components/Home.js
-
Import the
ResponsiveGrid
component from@adobe/aem-react-editable-components
and add it to theHome
component. -
Set the following attributes on the
<ResponsiveGrid...>
componentpagePath = '/content/wknd-app/us/en/home'
itemPath = 'root/responsivegrid'
This instructs the
ResponsiveGrid
component to retrieve its content from the AEM resource:/content/wknd-app/us/en/home/jcr:content/root/responsivegrid
The
itemPath
maps to theresponsivegrid
node defined in theRemote SPA Page
AEM Template and is automatically created on new AEM Pages created from theRemote SPA Page
AEM Template.Update
Home.js
to add the<ResponsiveGrid...>
component.code language-javascript ... import { ResponsiveGrid } from '@adobe/aem-react-editable-components'; ... function Home() { return ( <div className="Home"> <ResponsiveGrid pagePath='/content/wknd-app/us/en/home' itemPath='root/responsivegrid'/> <EditableTitle pagePath='/content/wknd-app/us/en/home' itemPath='title'/> <Adventures /> </div> ); }
The Home.js
file should look like:
Create editable components
To get the full effect of the flexible authoring experience containers provide in SPA Editor. We’ve already create an editable Title component, but let’s make a few more that allow authors to use editable Text and Image components in the newly added ResponsiveGrid component.
The new editable Text and Image React components are created using the editable component defintion pattern expored in editable fixed components.
Editable text component
-
Open the SPA project in your IDE
-
Create a React component at
src/components/editable/core/Text.js
-
Add the following code to
Text.js
code language-javascript import React from 'react' const TextPlain = (props) => <div className={props.baseCssClass}><p className="cmp-text__paragraph">{props.text}</p></div>; const TextRich = (props) => { const text = props.text; const id = (props.id) ? props.id : (props.cqPath ? props.cqPath.substr(props.cqPath.lastIndexOf('/') + 1) : ""); return <div className={props.baseCssClass} id={id} data-rte-editelement dangerouslySetInnerHTML={{ __html: text }} /> }; export const Text = (props) => { if (!props.baseCssClass) { props.baseCssClass = 'cmp-text' } const { richText = false } = props return richText ? <TextRich {...props} /> : <TextPlain {...props} /> } export function textIsEmpty(props) { return props.text == null || props.text.length === 0; }
-
Create an editable React component at
src/components/editable/EditableText.js
-
Add the following code to
EditableText.js
code language-javascript import React from 'react' import { EditableComponent, MapTo } from '@adobe/aem-react-editable-components'; import { Text, textIsEmpty } from "./core/Text"; import { withConditionalPlaceHolder } from "./core/util/withConditionalPlaceholder"; import { withStandardBaseCssClass } from "./core/util/withStandardBaseCssClass"; const RESOURCE_TYPE = "wknd-app/components/text"; const EditConfig = { emptyLabel: "Text", isEmpty: textIsEmpty, resourceType: RESOURCE_TYPE }; export const WrappedText = (props) => { const Wrapped = withConditionalPlaceHolder(withStandardBaseCssClass(Text, "cmp-text"), textIsEmpty, "Text V2") return <Wrapped {...props} /> }; const EditableText = (props) => <EditableComponent config={EditConfig} {...props}><WrappedText /></EditableComponent> MapTo(RESOURCE_TYPE)(EditableText); export default EditableText;
The editable Text component implementation should look like:
Image component
-
Open the SPA project in your IDE
-
Create a React component at
src/components/editable/core/Image.js
-
Add the following code to
Image.js
code language-javascript import React from 'react' import { RoutedLink } from "./RoutedLink"; export const imageIsEmpty = (props) => (!props.src) || props.src.trim().length === 0 const ImageInnerContents = (props) => { return (<> <img src={props.src} className={props.baseCssClass + '__image'} alt={props.alt} /> { !!(props.title) && <span className={props.baseCssClass + '__title'} itemProp="caption">{props.title}</span> } { props.displayPopupTitle && (!!props.title) && <meta itemProp="caption" content={props.title} /> } </>); }; const ImageContents = (props) => { if (props.link && props.link.trim().length > 0) { return ( <RoutedLink className={props.baseCssClass + '__link'} isRouted={props.routed} to={props.link}> <ImageInnerContents {...props} /> </RoutedLink> ) } return <ImageInnerContents {...props} /> }; export const Image = (props) => { if (!props.baseCssClass) { props.baseCssClass = 'cmp-image' } const { isInEditor = false } = props; const cssClassName = (isInEditor) ? props.baseCssClass + ' cq-dd-image' : props.baseCssClass; return ( <div className={cssClassName}> <ImageContents {...props} /> </div> ) };
-
Create an editable React component at
src/components/editable/EditableImage.js
-
Add the following code to
EditableImage.js
import { EditableComponent, MapTo } from '@adobe/aem-react-editable-components';
import { Image, imageIsEmpty } from "./core/Image";
import React from 'react'
import { withConditionalPlaceHolder } from "./core/util/withConditionalPlaceholder";
import { withStandardBaseCssClass } from "./core/util/withStandardBaseCssClass";
const RESOURCE_TYPE = "wknd-app/components/image";
const EditConfig = {
emptyLabel: "Image",
isEmpty: imageIsEmpty,
resourceType: RESOURCE_TYPE
};
const WrappedImage = (props) => {
const Wrapped = withConditionalPlaceHolder(withStandardBaseCssClass(Image, "cmp-image"), imageIsEmpty, "Image V2");
return <Wrapped {...props}/>
}
const EditableImage = (props) => <EditableComponent config={EditConfig} {...props}><WrappedImage /></EditableComponent>
MapTo(RESOURCE_TYPE)(EditableImage);
export default EditableImage;
-
Create an SCSS file
src/components/editable/EditableImage.scss
that provides custom styles for theEditableImage.scss
. These styles target the editable React component’s CSS classes. -
Add the following SCSS to
EditableImage.scss
code language-css .cmp-image__image { margin: 1rem 0; width: 100%; border: 0; }
-
Import
EditableImage.scss
inEditableImage.js
code language-javascript ... import './EditableImage.scss'; ...
The editable Image component implementation should look like:
Import the editable components
The newly created EditableText
and EditableImage
React components are referenced in the SPA, and are dynamically instantiated based on the JSON returned by AEM. To ensure that these components are available to the SPA, create import statements for them in Home.js
-
Open the SPA project in your IDE
-
Open the file
src/Home.js
-
Add import statements for
AEMText
andAEMImage
code language-javascript ... // The following need to be imported, so that MapTo is run for the components import EditableText from './editable/EditableText'; import EditableImage from './editable/EditableImage'; ...
The result should look like:
If these imports are not added, the EditableText
and EditableImage
code is not be invoked by SPA, and thus, the components are not mapped to the provided resource types.
Configuring the container in AEM
AEM container components use policies to dictate their allowed components. This is a critical configuration when using SPA Editor, since only AEM Components that have mapped SPA component counterparts are render-able by the SPA. Ensure only the components which we’ve provided SPA implementations for are allowed:
EditableTitle
mapped towknd-app/components/title
EditableText
mapped towknd-app/components/text
EditableImage
mapped towknd-app/components/image
To configure the Remote SPA Page template’s reponsivegrid container:
-
Log in to AEM Author
-
Navigate to Tools > General > Templates > WKND App
-
Edit Report SPA Page
-
Select Structure in the mode switcher in the top right
-
Tap to select the Layout Container
-
Tap the Policy icon in the popup bar
-
On the right, under the Allowed Components tab, expand WKND APP - CONTENT
-
Ensure only following are selected:
- Image
- Text
- Title
-
Tap Done
Authoring the container in AEM
After the SPA updated to embed the <ResponsiveGrid...>
, wrappers for three editable React components (EditableTitle
, EditableText
, and EditableImage
), and AEM is updated with a matching Template policy, we can start authoring content in the container component.
-
Log in to AEM Author
-
Navigate to Sites > WKND App
-
Tap Home and select Edit from the top action bar
- A “Hello World” Text component displays, as this was automatically added when generating the project from the AEM Project archetype
-
Select Edit from the mode-selector in the top right of the Page Editor
-
Locate the Layout Container editable area beneath the Title
-
Open the Page Editor’s side bar, and select the Components view
-
Drag the following components into the Layout Container
- Image
- Title
-
Drag the components to reorder them to the following order:
- Title
- Image
- Text
-
Author the Title component
-
Tap the Title component, and tap the wrench icon to edit the Title component
-
Add the following text:
- Title: Summer is coming, let’s make the most of it!
- Type: H1
-
Tap Done
-
-
Author the Image component
- Drag an image in from the Side bar (after switching to the Assets view) on the Image component
- Tap the Image component, and tap the wrench icon to edit
- Check the Image is decorative checkbox
- Tap Done
-
Author the Text component
- Edit the Text component by tapping the Text component, and tapping the wrench icon
- Add the following text:
- Right now, you can get 15% on all 1-week adventures, and 20% off on all adventures that are 2 weeks or longer! At checkout, add the campaign code SUMMERISCOMING to get your discounts!
- Tap Done
-
Your components are now authored, but stack vertically.
Use AEM’s Layout Mode to allow us to adjust the size and layout of the components.
-
Switch to Layout Mode using the mode-selector in the top-right
-
Resize the Image and Text components, such that they are side by side
- Image component should be 8 columns wide
- Text component should be 3 columns wide
-
Preview your changes in AEM Page Editor
-
Refresh the WKND App running locally on http://localhost:3000 to see the authored changes!
Congratulations!
You’ve added a container component that allows for editable components to be added by authors to the WKND App! You now know how to:
- Use the AEM React Editable Component’s
ResponsiveGrid
component in the SPA - Create and register editable React components (Text and Image) for use in the SPA via the container component
- Configure the Remote SPA Page template to allow the SPA-enabled components
- Add editable components to the container component
- Author and layout components in SPA Editor
Next Steps
The next step uses this same technique to add an editable component to an Adventure Details route in the SPA.