1.4.3 • Published 3 months ago

@dwp/casa-looper-plugin v1.4.3

Weekly downloads
-
License
ISC
Repository
-
Last release
3 months ago

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:

VariableDescription
loopContexts[]Array of ephemeral JourneyContext instances associated with this seed waypoint. These are additionally decorated with attributes below ...
loopContexts[].editUrlSend 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[].removeUrlSend the user to this URL so they can confirm removal of this contest
loopContexts[].isCompleteIf 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.
createUrlSend 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

HookDescription
looper.remove_prerenderCalled just before the "remove" page is rendered. Executes on both GET and POST requests
looper.remove_preredirectCalled 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!

1.4.3

3 months ago

1.4.2

7 months ago

1.4.1

10 months ago

1.4.0

10 months ago

1.2.7

1 year ago

1.2.6

1 year ago

1.3.3

11 months ago

1.3.2

12 months ago

1.2.5

1 year ago

1.2.4

1 year ago

1.2.3

1 year ago

1.2.2

1 year ago

1.2.1

1 year ago

1.2.0

2 years ago

1.1.1

2 years ago

1.1.0

2 years ago

1.0.4

2 years ago

1.0.3

2 years ago

1.0.2

2 years ago

1.0.1

2 years ago