Customizing Widget List Rendering

In this tutorial, which is based on the Scrivito example app, we're going to show you how nicely styled lists can be rendered using nested widget lists.

For you to benefit from this tutorial, basic knowledge of how Scrivito's content classes work and how they integrate with React is required.

With Scrivito, every page is usually made up of widgets that are contained in a single widget list at the top level. To this widget list, plain content widgets (e.g. for text and images), but also structural widgets (columns, boxes, and the like) can be added.

Widget lists can be made available on pages and in widgets by providing the corresponding Scrivito class with one or several attributes of the widgetlist type.

The widgetlist type is the key to structuring content. For example, each column of a column widget needs to be represented by a widgetlist attribute for being able to accommodate text, image, and other content widgets, or even additional structural widgets.

Why apply custom logic?

Widget or page attributes of any type can be rendered using Scrivito.ContentTag. In the case of widgetlist attributes, Scrivito.ContentTag renders the individual widgets one after the other in the order they have in the widget list. This is the most common use case.

However, what if you wanted to display an ordered or unordered list, with each item being a structural widget for using more than one widget per list item? In this case, each widget contained in a widget list would have to be rendered individually so that custom styles can be applied to them.

Let's start.

Create the widgets

We require two widgets, a ListWidget that editors can add to pages, and a ListItemWidget for combining several widgets into one list item.

The ListWidget

In the example app, all widget classes are defined in dedicated folders in the ”src/Widgets” directory. Let's stick to this pattern and create a “ListWidget” folder and a file named “ListWidgetClass.js” in this directory first.

The ListWidget we are going to define is a regular widget type in the sense that editors can select it from the widget browser when they want to add a widget to a page or to a structural widget.

Our ListWidget displays its contents (any number of ListItemWidgets – we’ll define this type later) as an unordered list, but you can change this according to your demands. Place the code in the “src/Widgets/ListWidget” folder using the file names given below.

Jumping straight in, note that the widgetlist attribute, items, has an only parameter that restricts its contents to ListItemWidget instances. In other words, the only kind of widgets that can be added to any ListWidget are ListItemWidgets.

src/Widgets/ListWidget/ListWidgetClass.js
Copy
import * as Scrivito from 'scrivito';

Scrivito.provideWidgetClass('ListWidget', {
  attributes: {
    items: ['widgetlist', { only: 'ListItemWidget' }],
    cssClass: 'string',
  },
});

As you can see, next to the items widget list, the ListWidget class is equipped with one more attribute: cssClass. It lets editors specify the name of the CSS class to use for the list. The component of the widget renders it using Scrivito.ContentTag to which the specified CSS class name (or “list-group” as the default) is passed:

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

Scrivito.provideComponent('ListWidget', ({ widget }) =>
  <Scrivito.ContentTag
      tag='ul' 
      attribute='items'
      content={ widget }
      className={ widget.get('cssClass') || 'list-group' }
    />
);

All that’s left to do is to make the CSS class name editable on the properties view of the widget:

src/Widgets/ListWidget/ListWidgetEditingConfig.js
Copy
import * as Scrivito from 'scrivito';

Scrivito.provideEditingConfig('ListWidget', {
  title: 'List Widget',
  properties: ['cssClass'],
  attributes: {
    cssClass: {
      title: 'List CSS class',
    },
  },
});

The ListItemWidget

Let’s turn to the items that can be added to our ListWidget, ListItemWidgets. Like the former, the latter has two attributes, a content widget list and, again, cssClass. Note that we are using the onlyInside parameter in the model class to specify that a ListItemWidget can only be placed into a ListWidget.

src/Widgets/ListItemWidget/ListItemWidgetClass.js
Copy
import * as Scrivito from 'scrivito';

Scrivito.provideWidgetClass('ListItemWidget', {
  attributes: { 
    content: 'widgetlist',
    cssClass: 'string',
  },

  onlyInside: 'ListWidget',
});

By default, Scrivito renders each widget inside a <div> element, so we’ll use Scrivito.WidgetTag in the widget’s component to render each widget as an <li> element. This also allows us to add the custom styles provided via the cssClass attribute of the list items, independently of the class we’re applying to the list item via Scrivito.ContentTag.

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

Scrivito.provideComponent('ListItemWidget', ({ widget }) =>
  <Scrivito.WidgetTag tag='li' className={ widget.get('cssClass') || 'list-group-item' }>
    <Scrivito.ContentTag content={ widget } attribute='content' />
  </Scrivito.WidgetTag>
);

Finally, as with the ListWidget, we’ll make an individual list item’s CSS class name editable:

src/Widgets/ListItemWidget/ListItemWidgetEditingConfig.js
Copy
import * as Scrivito from 'scrivito';

Scrivito.provideEditingConfig('ListItemWidget', {
  title: 'List Item Widget',
  properties: ['cssClass'],
  attributes: {
    cssClass: {
      title: 'List item CSS class',
    },
  },
});

That’s it! Everything should be in place now. 

Use it! 

It should be possible now to place a ListWidget onto a page. 

Initially, a ListWidget is empty. Just add items to it and widgets to the items and arrange everything to your liking!

You can assign CSS classes to the list via its properties dialog. The same applies to the individual items.

There's potential everywhere!

You customized the display of widgetlist attributes, that's fantastic! Now, how about extending the result even further? You could, for example, use enum attributes for offering a set of CSS class names for each of the list styles, so editors don't have to enter them manually.