# Add a Configuration Screen in App Builder
Entando widgets can be customized through an App Builder configuration screen that is itself a micro frontend. This tutorial splits the process into 3 steps:
- Modify an existing MFE (the target MFE) to take a configuration option
- Create a new MFE (the config MFE) to provide a user interface for the configuration option
- Set up the target MFE to use the configuration provided by the config MFE
# Prerequisites
# Step 1: Add a configuration option to your target MFE
Start by adding a configuration option to an existing MFE. If you don't already have one, you can create it via the React MFE tutorial.
# Add an Attribute to the Custom Element
- Replace the contents of
src/WidgetElement.js
with the following to add attribute handling to the custom element and re-render the app when an attribute changes. This enables thename
attribute of the custom element to be passed as a property to the React root component (App
).
import React from 'react';
import ReactDOM from 'react-dom';
import App from './App';
const ATTRIBUTES = {
name: 'name',
};
class WidgetElement extends HTMLElement {
static get observedAttributes() {
return Object.values(ATTRIBUTES);
}
attributeChangedCallback(name, oldValue, newValue) {
if (!Object.values(ATTRIBUTES).includes(name)) {
throw new Error(`Untracked changed attribute: ${name}`);
}
if (this.mountPoint && newValue !== oldValue) {
this.render();
}
}
connectedCallback() {
this.mountPoint = document.createElement('div');
this.appendChild(this.mountPoint);
this.render();
}
render() {
const name = this.getAttribute(ATTRIBUTES.name);
ReactDOM.render(<App name={name} />, this.mountPoint);
}
}
customElements.define('my-widget', WidgetElement);
export default WidgetElement;
- Replace the contents of
src/App.js
with the following. This component now displays thename
property, turning the static component from the React MFE tutorial into a more dynamic component.
import React from 'react';
import './App.css';
function App({name}) {
return (
<div className="App">
<header className="App-header">
<p>
Hello, {name}!
</p>
</header>
</div>
);
}
export default App;
- For test purposes, replace the contents of
public/index.html
with the following. This allows you to set a value for thename
attribute of the custom element.
<my-widget name="Jane" />
- Start the app and confirm that "Hello, Jane!" is displayed
cd my-widget
npm start
- Build the app
npm run build
- Load the updated
my-widget
files into Entando as was done for the React MFE tutorial
Note: If you followed the React MFE tutorial, only
js/main.GENERATED-ID.js
needs to be added or updated.
# Step 2: Create a config MFE
Next, create a new MFE for managing the configuration option. These steps are very similar to the React MFE tutorial.
TIP
This tutorial sets up a separate, standalone config MFE since that allows reuse across multiple target MFEs. You could also choose to include the config custom element in the target MFE, in which case the configUI will also reference the target MFE files.
- Generate a new React app
npx create-react-app my-widget-config --use-npm
- Start the app
cd my-widget-config
npm start
- Replace the contents of
src/App.js
with the following to add a simple form for managing a singlename
field
import React from 'react';
class App extends React.Component {
constructor(props) {
super(props);
this.state = { name: ''};
}
handleNameChange(value) {
this.setState(prevState => ({
...prevState,
name: value,
}));
}
render() {
const { name } = this.state;
return (
<div>
<h1>Sample Entando Widget Configuration</h1>
<label htmlFor="name">Name </label>
<input id="name" onChange={e => this.handleNameChange(e.target.value)} value={name} />
</div>
);
}
}
export default App;
TIP
- Use your preferred form handling library, e.g.Formik (opens new window)
- This MFE will be displayed within the App Builder, which currently uses PatternFly
v3 (opens new window) (
patternfly
andpatternfly-react
packages) for styling
- Add a
src/WidgetElement.js
component with the following content to set up the custom element for the config MFE
import React from 'react';
import ReactDOM from 'react-dom';
import App from './App';
class WidgetElement extends HTMLElement {
constructor() {
super();
this.reactRootRef = React.createRef();
this.mountPoint = null;
}
get config() {
return this.reactRootRef.current ? this.reactRootRef.current.state : {};
}
set config(value) {
return this.reactRootRef.current.setState(value);
}
connectedCallback() {
this.mountPoint = document.createElement('div');
this.appendChild(this.mountPoint);
ReactDOM.render(<App ref={this.reactRootRef} />, this.mountPoint);
}
}
customElements.define('my-widget-config', WidgetElement);
export default WidgetElement;
App Builder integration
- A config MFE must retain its state in a
config
property - The App Builder supplies the
config
property when the MFE is rendered - When a user saves the form, the App Builder automatically persists the configuration through Entando APIs
- Replace the contents of
src/index.js
with the following:
import './index.css';
import './WidgetElement';
- For test purposes, replace the body tag of
public/index.html
with the following and confirm the form renders correctly
<body>
<my-widget-config />
</body>
# Step 3: Build and configure the config MFE
- Build the app from the
my-widget-config
directory
npm run build
In the App Builder, go to
Administration
→File browser
→public
Click
Create folder
and name it "my-widget-config"Click
Save
Click
my-widget-config
Create a folder structure similar to your generated build directory:
my-widget-config/static/css
my-widget-config/static/js
Upload the css and js files from the corresponding directories under
my-widget-config/build/static
my-widget-config/build/static/css/main.073c9b0a.css
my-widget-config/build/static/js/main.b9eb8fa4.js
Note: The generated ID of each file name (e.g. '073c9b0a') may change after every build. These folders may also contain LICENSE.txt or .map files, but they are not applicable to this tutorial.
Go to
Components
→MFE & Widgets
and edit your target widget- Set
Config UI
to select the config custom element and its corresponding files. Note that the paths here reference "my-widget-config".
{ "customElement": "my-widget-config", "resources": [ "my-widget-config/static/js/main.e6c13ad2.js" ] }
- Set
Custom UI
to accept thename
config parameter
<#assign wp=JspTaglibs[ "/aps-core"]> <link rel="stylesheet" type="text/css" href="<@wp.resourceURL />my-widget/static/css/main.073c9b0a.css"> <script nonce="<@wp.cspNonce />" async src="<@wp.resourceURL />my-widget/static/js/main.e6296e83.js" ></script> <@wp.currentWidget param="config" configParam="name" var="configName" /> <my-widget name="${configName}" />
- Set
TIP
Multiple <@wp.currentWidget param
tags can be used when a config MFE supports more than one parameter.
Test the full setup by adding the widget into an existing page
Fill out the
name
field and clickSave
. You can update the widget configuration at any point by clickingSettings
from the widget actions in the Page Designer.Publish the page and confirm the target MFE is configured and displays correctly