Adding an Option to a Widget

As mentioned in the introduction to the Scrivito Example Application, all widget classes are defined in the files contained in subdirectories of the “src/widgets” directory of the repository. Let’s add to the headline widget a means to change the background color of the headline.

In the example app, the “HeadlineWidget” model class is defined as follows:

src/Widgets/HeadlineWidget/HeadlineWidgetClass.js
Copy
import * as Scrivito from 'scrivito';
import { registerTextExtract } from '../../utils/textExtractRegistry';

const HeadlineWidget = Scrivito.provideWidgetClass('HeadlineWidget', {
  attributes: {
    headline: 'string',
    level: ['enum', { values: ['h1', 'h2', 'h3', 'h4', 'h5', 'h6'] }],
    style: ['enum', { values: ['h1', 'h2', 'h3', 'h4', 'h5', 'h6'] }],
    alignment: ['enum', { values: ['left', 'center', 'right'] }],
    showDividingLine: ['enum', { values: ['yes', 'no'] }],
    showMargin: ['enum', { values: ['yes', 'no'] }],
  },
});

registerTextExtract('HeadlineWidget', [
  { attribute: 'headline', type: 'string' },
]);

export default HeadlineWidget;

The example app uses Bootstrap for styling. We're going to stick to the set of colors provided by Bootstrap and offer them to editors as headline background color options.

As you can see from the HeadlineWidget definition above, there are already two attributes for letting editors select one of several values, the level and style attributes. They are attributes of the enum type which enables selecting a single value, or none (as opposed to the multienum type for selecting several values).

Adding functionality or styling options to a widget or page class involves three steps:  add the attribute, make it editable, and render its value.

Add the attribute

We'll call the new attribute bgColor and insert it directly below headline:

src/Widgets/HeadlineWidget/HeadlineWidgetClass.js
Copy
import * as Scrivito from 'scrivito';

Scrivito.provideWidgetClass('HeadlineWidget', {
  attributes: {
    headline: 'string',
    bgColor: ['enum', { values: ['primary', 'success', 'info', 'warning', 'danger'] }],
    level: ['enum', { values: ['h1', 'h2', 'h3', 'h4', 'h5', 'h6'] }],
    
    // ...
  },
});

Note that the “bg-” prefix has been removed from the color names above. We'll restore the prefix later on when we extend the rendering function.

Make the attribute editable

All that needs to be done to make the new attribute accessible on the properties dialog of headline widgets, is to provide a specification for it in the provideEditingConfig call for the HeadlineWidget class. We've inserted the specification right at the top of the attributes object:

src/Widgets/HeadlineWidget/HeadlineWidgetEditingConfig.js
Copy
import * as Scrivito from 'scrivito';

Scrivito.provideEditingConfig('HeadlineWidget', {
  title: 'Headline',
  thumbnail: `/${headlineWidgetIcon}`,
  attributes: {
    bgColor: {
      title: 'Background color',
      description: 'The background color of the headline text',
    },
    // style, level, …
  },

  properties: [
    'bgColor',
    // style, level, …
  ],

  initialContent: {
  // …
  },

  titleForContent: widget => widget.get('headline'),
});

Don't forget to also add the new attribute to the properties list like shown above. This list defines the attributes to show up on the “General” tab of the properties dialog of the actual widgets.

Providing display titles for enum attribute values

You can provide display titles for each enum value to make them more editor friendly. The style attribute configuration, for example, maps its values “h1” through “h6” to “Heading *” titles:

Copy
// ...
  attributes: {
    style: {
      title: 'Style',
      description: 'Size and font of this headline. Default: Heading 1',
      values: [
        { value: 'h1', title: 'Heading 1' },
        { value: 'h2', title: 'Heading 2' },
        { value: 'h3', title: 'Heading 3' },
        { value: 'h4', title: 'Heading 4' },
        { value: 'h5', title: 'Heading 5' },
        { value: 'h6', title: 'Heading 6' },
      ],
    },
// ...

Initializing attributes

Furthermore, you can initialize attributes on creation of a widget (or page). For this, the initialContent key is available in Scrivito.provideEditingConfig. Use it as follows to set the bgColor value to primary when a headline widget is created:

Copy
// ...
  attributes: {
    bgColor: {
      title: 'Background color',
      description: 'The background color of the headline text',
    },

    // style, level, ...
  },

  initialContent: {
    bgColor: 'primary',
  },
// ...

Opening the properties dialog of a headline widget now reveals the new attribute and lets you select a background color.

However, closing the dialog after chosing a color doesn't have any effect. So, let's render the attribute value as a style.

Render the attribute value

All rendering of widgets and pages is done by a React component that has been attached to the widget or page class using a call to provideComponent. For the headline widget (and many other widgets) the main task of the component is to collect the CSS class names from the attribute values and use them as the className prop value in the call to Scrivito.ContentTag, which does the actual rendering.  

src/Widgets/HeadlineWidget/HeadlineWidgetComponent.js
Copy
import * as React from 'react';
import * as Scrivito from 'scrivito';

Scrivito.provideComponent('HeadlineWidget', ({ widget }) => {
  const style = widget.get('style') || 'h1';
  const level = widget.get('level') || style;
  const classNames = [style];
  if (widget.get('alignment')) {
    classNames.push(`text-${widget.get('alignment')}`);
  }
  if (widget.get('showDividingLine') === 'yes') {
    classNames.push('border-bottom');
  }
  if (widget.get('showMargin') === 'no') {
    classNames.push('no-margin');
  }

  return <Scrivito.ContentTag
    tag={ level }
    content={ widget }
    attribute="headline"
    className={ classNames.join(' ') }
  />;
});

Now, let's add the background color class to the classNames array. This only needs to be done if a color has been selected for the headline background, and, if so, the “bg-” prefix needs to be restored, too:

Copy
const bgColor = widget.get('bgColor');

if (bgColor) {
  classNames.push(`bg-${bgColor}`);
}

After inserting the above lines of code before the return statement and saving the file, the background color becomes visible immediately after selecting it on the properties dialog.

That's it! You've customized a Scrivito widget by adding an attribute to the model, extending the properties dialog configuration, and rendering the attribute value!