Creating a Custom Widget

Previously, we built an FAQ page and left it up to the editors to add the questions and answers as they saw fit. However, too many options can be a detriment and too time consuming. By making a specialized widget available to your editors, they will know exactly what information is needed for an FAQ and be able to add them with out needing to decide on styling or layout. In this tutorial, we are going to build an FAQ widget. So let’s get started.

To begin, create a folder, “src/Widgets/FaqWidget/”. Then, in this folder, create the file for the widget class, “FaqWidgetClass.js”.

Create the widget class (the model)

We’ll create a widget class with the attributes we want to make available. In our case, we want the editors to be able to update and add a question and a corresponding answer. Add the file, “FaqWidgetClass.js” to the FaqWidget folder and include this:

src/Widgets/FaqWidget/FaqWidgetClass.js
Copy
import * as Scrivito from 'scrivito';

Scrivito.provideWidgetClass('FaqWidget', {
  attributes: {
    question: 'string',
    answer: 'html',
  },
});

Optionally provide widget properties

This part is optional because we are only using a string and an html attribute in the model above. Attributes of these types can be easily updated on the page. However, should your editors prefer using widget properties dialogs, we are including this simple example of how to make attributes editable on them. This also allows you to add some hints to the editors regarding what information should be in each field. The properties (dialog) can also be updated later for additional attributes that don’t show up on the page, e.g. tags for filtering FAQs.

src/Widgets/FaqWidget/FaqWidgetEditingConfig.js
Copy
import * as Scrivito from 'scrivito';
import factWidgetIcon from '../../assets/images/fact_widget.svg';

Scrivito.provideEditingConfig('FaqWidget', {
  title: 'FaqWidget',
  thumbnail: `/${factWidgetIcon}`,
  description: 'A frequently asked question.',

  attributes: {
    question: {
      title: 'Question',
      description: 'State the frequently asked question',
    },

    answer: {
      title: 'Answer',
      description: 'Provide an eloquent answer',
    },
  },

  properties: ['question', 'answer'],
});

The first title and description keys are for the widget selection dialog. The title will be the name of the widget in the widget browser. The widget’s description will show when hovering over the widget in the widget selection dialog. Note that for the widget selection dialog we are using a thumbnail belonging to a different widget; feel free to provide your own one and reference it here.

Next, the attributes section lets you configure for the widget properties dialog each widget attribute including a title and a description. The title is typically the name of the attribute but can be anything which represents it. The description is used to provide some hints to the editor what the field should contain.

Provide the component (the view)

Now let’s provide the React component to Scrivito to have the widget rendered.

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

Scrivito.provideComponent('FaqWidget', ({ widget }) =>
  <div className='faq'>
    <Scrivito.ContentTag tag='h3' content={ widget } attribute='question' />
    <Scrivito.ContentTag content={ widget } attribute='answer'/>
  </div>
);

Again, wrapped in a single tag, you can add whatever you deem appropriate here, including markup, styling etc. For the content we want an editor to be able to update, we provide Scrivito.ContentTags, Scrivito’s general-purpose tool for rendering attributes.

Add some styling

Note that in the above example we included className='faq' to the outer div. This will allow us to style each instance of the FAQ widget the same and provide consistency to the page. One of several ways to add the styling is as follows:

Create a file, “src/assets/stylesheets/_faq.scss”, and add @import "_faq"; to the /* import */ section of “src/stylesheets/index.scss”. Yes, this is optional, and you can add the following code to the “index.scss” file directly if you prefer.

src/stylesheets/index.scss
Copy
.faq {
  background-color: $theme-greylight;
  margin: 20px 0;
  padding: 0 0 10px 5px;
  border-bottom: 1px solid $brand-primary;
}

The Scrivito example app uses Sass for its CSS which has many benefits. In this case, it lets us use variables for colors to keep them consistent within the website. Should you decide to change the colors later, this allows you to change them in one place, and all of the site will be updated in an instant.

Check it out!

If you have already followed and created an FaqPage type, go to that page on your site. Alternatively, you could create an FAQ using an existing page type. To do this, click the page menu and select “Create page”. Pick a page type you like.

Now your editors can add FaqWidgets. Enter the questions and answers, and each FAQ will look consistent, like this:

Restrict FAQ pages to FAQ widgets

Finally, you could limit the FaqPage to only allow the editor to choose FaqWidgets by changing question: 'widgetlist', in “src/Objs/FaqPage/FaqPageObjClass.js” to question: ['widgetlist', { only: 'FaqWidget' }],. This simple change takes more of the guess work out of the way and considerably speeds up content input.

You're done, you've created a custom widget – you are on fire!