Flagging New Pages in Navigations

Wouldn’t it be nice if you could draw your website visitor’s attention to articles your team has recently published, let’s say your latest case study or important investor news? 

With Scrivito you can! In this tutorial, we are going to show you how pages in a navigation can be flagged as new, based on the date they were created. For this, we will extend the child navigation widget we built a while ago to shine a light on the general principle of creating hierarchy-based navigations.

So, to follow along, we recommend to first go through Building a Child Navigation Widget, and then come back here to learn how new items can be flagged. However, if you’re just interested in the details, read on.

Like all articles in this section, this one is based on the Scrivito Example App, too.

The basics

For rendering navigations based on the page hierarchy, the Scrivito.ChildListTag helper component is available. By default, it renders a <ul> made up of <li> elements linking to the pages that can be found directly below the current page:

Copy
<Scrivito.ChildListTag />

However, this component is highly flexible. It lets you pass in the object to use as the parent page as well as specify a method to be called for rendering the individual child pages. All this can be seen in the above-mentioned article.

Before extending the rendering method, let’s spend a moment on the approach we are going to take.

When is a page new?

That’s a matter of definition, of course. Below, we’ll regard a page as new if it’s less than two weeks old.

But how do we determine the creation date of a page in order to compute its age? All we need to do is add a custom attribute for capturing this date. We can then use this attribute in the ChildListTag’s rendering method we’ll refine further down.

Introducing the page “birthday” attribute

Okay, createdAt should adequately reflect the new attribute’s purpose, so we will have to add it as a date attribute to all of the Scrivito Example App’s page class definitions. Fortunately, the way the Scrivito Example App is built supports us with adding attributes to several class definitions at once: it lets us place them in a file named “_defaultPageAttributes.js”:

src/Objs/_defaultPageAttributes.js
Copy
const defaultPageAttributes = {
  body: ["widgetlist", { only: "SectionWidget" }],
  navigationBackgroundImage: [
    "reference",
    { only: ["Image", "Video", "Download"] },
  ],
  navigationBackgroundImageGradient: ["enum", { values: ["yes", "no"] }],
  navigationHeight: [
    "enum",
    {
      values: ["small", "medium-height", "full-height"],
    },
  ],
  navigationSection: "widgetlist",
  title: "string",
  createdAt: "date",
};

export default defaultPageAttributes;

This file gets imported into every page class definition like Homepage or Page. To set the value of this attribute for new instances of these classes, we would normally specify the initialContent key in the classes’ editing configuration, but, analogously to the “_defaultPageAttributes.js” file, the “_defaultPageEditingConfig.js” file makes things easier:

src/Objs/_defaultPageEditingConfig.js
Copy
const defaultPageInitialContent = {
  body: [
    new SectionWidget({
      content: [new HeadlineWidget({ style: "h1" })],
    }),
  ],
  navigationHeight: "small",
  navigationBackgroundImageGradient: "no",
  createdAt: () => new Date(),
};
// …

As you might guess, defaultPageInitialContent becomes the value of initialContent in the editing configuration of the Example App’s page classes. We are providing here the current date and time to createdAt using a function that returns a Date.

Voilà! If you want to try this out in the web (or developer) console, create a Page object and get its createdAt attribute.

To delete the test page, simply execute page.destroy().

Rendering the “new” flag

Now that we have the date at which a page was created, we can render something based on this date. As indicated, the ChildNavigationWidget on which this tutorial is based uses a Scrivito.ChildListTag component to generate the navigation. The component delegates the rendering of the child objects to a function, renderChild. We’ve added to the return value of renderChild a call to a new function for rendering the flag, newFlag, which we’ve provided underneath:

src/Widgets/ChildNavigationWidget/ChildNavigationWidgetComponent.js
Copy
    <Scrivito.ChildListTag
      tag='ul'
      className='page-list'
      parent={ parentPage }
      renderChild={ renderChild }
    />

// …
function renderChild(child) {
  let className = '';

  if (child.id() === Scrivito.currentPage().id()) {
    className = 'strong';
  }

  return (
    <li className={ `fa fa-caret-right ${className}` } >
      <Scrivito.LinkTag to={ child }>
        { child.get('title') }
      </Scrivito.LinkTag>
      { newFlag(child) }
    </li>
  );
}

const currentDate = Date.now();
const fortnight = 1000 * 60 * 60 * 24 * 14;

function newFlag(child) {
  const createdAt = child.get('createdAt');
  if (createdAt && currentDate - createdAt < fortnight) {
    return (
      <i className="fa fa-star fa-1x small initialism"> new</i>
    );
  }
}

The purpose of newFlag is to return the markup for the flag if the child page passed to it is new, i.e. less than 14 days old in our case. To achieve this, the createdAt value of the passed-in child (if present) is subtracted from the currentDate, resulting in the child’s age, which must be less than 1000 * 60 * 60 * 24 * 14 milliseconds, the equivalent of a fortnight. If this is true, an <i> element representing our flag is returned.

Note that we used a constant on the module level to store the current date. Had we used Date.now inside of the component, it wouldn’t be pure anymore.

We’re done!

Want more?

Of course, you can style your flags the way you want it. You could style them even differently, depending on the parent page, for example. You could also make the icon and the flag’s text configurable.

How about changing the flag’s text to “updated” if the page isn’t new and the most recent change to it is less than a week ago? For things like this, CMS objects feature the lastChanged method you could use in the comparison.