@dwp/casa-looper-plugin
TypeScript icon, indicating that this package has built-in type declarations

1.4.3 • Public • Published

CASA Looper Plugin

This plugin provides a method of implementing the "Add Another Thing" pattern described in the DWP Design System.

Usage

This plugin requires that you create at least 2 separate Express sub-apps:

  • One for your "parent" app that houses your main Plan, and
  • One for the "looper" app that captures data multiple times

You must also mount your looper CASA app on a parameterised route (see below):

import express from 'express';
import { MemoryStore } from 'express-session';
import { configure } from '@dwp/govuk-casa';
import { looper, looperParent } from '../plugins/looper/index.js';

// Mount URLs for the "main" Plan, and the "loop" Plan
const MOUNT_MAIN = '/main/';
const MOUNT_LOOP = '/loop/';

// Both apps must share a session
const session = {
  secret: 'secret',
  store: new MemoryStore(),
};

// Create a CASA app for your looping journey
const { mount: mountLoop } = configure({
  session,
  plugins: [
    looper({
      // The waypoint on your "parent" journey that will display a summary of
      // all captured data from each loop across the "looper" Plan
      seedWaypoint: 'summary',

      // The URL on which your "parent" app is mounted
      parentMountUrl: MOUNT_MAIN,

      // The last waypoint in your "looper" Plan. All journeys in the looper
      // must end on this waypoint.
      lastWaypoint: 'check-your-loop-answers',
    }),
  ],
});
const loopApp = mountLoop(express(), {
  route: '/:contextid', // Must use "contextid" parameter here
});

// Create a CASA app for your main, "parent" Plan
const { mount: mountMain } = configure({
  session,
  plugins: [
    looperParent({
      // As above
      seedWaypoint: 'summary',

      // The URL on which your "looper" app is mounted
      looperMountUrl: MOUNT_LOOP,

      // The last waypoint in your "looper" Plan. All journeys in the looper
      // must end on this waypoint.
      lastWaypoint: 'check-your-loop-answers',
    }),
  ],
});
const mainApp = mount(express());

// Mount everything into a top-level app
const app = express();
app.use(MOUNT_MAIN, mainApp);
app.use(MOUNT_LOOP, loopApp);
app.listen();

Templates

In your "seed" waypoint template, you will have the following variables available:

Variable Description
loopContexts[] Array of ephemeral JourneyContext instances associated with this seed waypoint. These are additionally decorated with attributes below ...
loopContexts[].editUrl Send the user to this URL to edit the context. You can use waypointUrl() to generate your own URL instead, but this default link will take you to the last page in the loop journey, assuming it is the "check your answers" summary for the loop questions. Refer to the "Add Another Thing" pattern for details on how this may need altering for you use-case.
loopContexts[].removeUrl Send the user to this URL so they can confirm removal of this contest
loopContexts[].isComplete If the user has reached the last waypoint in the loop Plan, then the context will be flagged as "complete". Incomplete contexts will prevent the user from progressing beyond the seed waypoint.
createUrl Send the user to this URL to create a new loop context

Hooks

The plugin provides a couple of extra hooks that you can use on the looping journey app. These are just Express middleware functions

Hook Description
looper.remove_prerender Called just before the "remove" page is rendered. Executes on both GET and POST requests
looper.remove_preredirect Called just before the user is redirected after removing (or cancelling the removal) of a loop context. Executed on POST requests

Example usage:

const { mount } = configure({
  session,
  plugins: [
    looper({
      seedWaypoint: 'summary',
      parentMountUrl: '/main/',
      lastWaypoint: 'check-your-loop-answers',
    }),
  ],
  hooks: [{
    hook: 'looper.remove_prerender',
    middleware: (req, res, next) => {
      // E.g. add some variables to `res.locals`
      next();
    }
  }],
});

Handling missing looper contexts

Sometimes, a journey context will no longer be available to the loop app; for example if the user removes a loop entry and then attempts to navigate back to that removed entry. In such cases, the user will be redirected to a /not-found route on your loop's mount point.

You can intercept this route handler to replace it with your own error page or route behaviours:

ancillaryRouter.prependUse('/not-found', (req, res, next) => {
  console.log('Intercepting /not-found route');
  next();
});

Notes

showSeedWaypointFirst

By default, the "seed waypoint" will be skipped on the first iteration, and only shown once that iteration is complete.

You can alter this behaviour by using the showSeedWaypointFirst flag on the looper parent application. This allows you, for example, to populate the seed waypoint template with data pulled from other sources (e.g. external APIs) before the user can then choose to "add another".

looperParent({
  // ...
  showSeedWaypointFirst: true
})

Different design patterns

Documentation is available here to explain how to use different design patterns for the Add another item page.

Customising seed waypoint

Documentation is available here to explain how the seed waypoint (Add another item page) can be customised.

Nested loops

This plugin does not currently support nested loops, so you cannot attach loop journeys to another loop journey. We have to draw the line somewhere!

Readme

Keywords

none

Package Sidebar

Install

npm i @dwp/casa-looper-plugin

Weekly Downloads

299

Version

1.4.3

License

ISC

Unpacked Size

47.5 kB

Total Files

44

Last publish

Collaborators

  • dwp-bot
  • jgdwp
  • lhokktyn