# 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:

  1. Modify an existing MFE (the target MFE) to take a configuration option
  2. Create a new MFE (the config MFE) to provide a user interface for the configuration option
  3. 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

  1. 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 the name 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;
  1. Replace the contents of src/App.js with the following. This component now displays the name 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;
  1. For test purposes, replace the contents of public/index.html with the following. This allows you to set a value for the name attribute of the custom element.
<my-widget name="Jane" />
  1. Start the app and confirm that "Hello, Jane!" is displayed
cd my-widget
npm start
  1. Build the app
npm run build
  1. 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.

  1. Generate a new React app
npx create-react-app my-widget-config --use-npm
  1. Start the app
cd my-widget-config
npm start
  1. Replace the contents of src/App.js with the following to add a simple form for managing a single name 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

  1. 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
  1. Replace the contents of src/index.js with the following:
    import './index.css';
    import './WidgetElement';
  1. 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

  1. Build the app from the my-widget-config directory
npm run build
  1. In the App Builder, go to AdministrationFile browserpublic

  2. Click Create folder and name it "my-widget-config"

  3. Click Save

  4. Click my-widget-config

  5. Create a folder structure similar to your generated build directory:

    • my-widget-config/static/css
    • my-widget-config/static/js
  6. 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.

  1. Go to ComponentsMFE & 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 the name 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}" />
    

TIP

Multiple <@wp.currentWidget param tags can be used when a config MFE supports more than one parameter.

  1. Test the full setup by adding the widget into an existing page

  2. Fill out the name field and click Save. You can update the widget configuration at any point by clicking Settings from the widget actions in the Page Designer.

  3. Publish the page and confirm the target MFE is configured and displays correctly