npm package discovery and stats viewer.

Discover Tips

  • General search

    [free text search, go nuts!]

  • Package details

    pkg:[package-name]

  • User packages

    @[username]

Sponsor

Optimize Toolset

I’ve always been into building performant and accessible sites, but lately I’ve been taking it extremely seriously. So much so that I’ve been building a tool to help me optimize and monitor the sites that I build to make sure that I’m making an attempt to offer the best experience to those who visit them. If you’re into performant, accessible and SEO friendly sites, you might like it too! You can check it out at Optimize Toolset.

About

Hi, 👋, I’m Ryan Hefner  and I built this site for me, and you! The goal of this site was to provide an easy way for me to check the stats on my npm packages, both for prioritizing issues and updates, and to give me a little kick in the pants to keep up on stuff.

As I was building it, I realized that I was actually using the tool to build the tool, and figured I might as well put this out there and hopefully others will find it to be a fast and useful way to search and browse npm packages as I have.

If you’re interested in other things I’m working on, follow me on Twitter or check out the open source projects I’ve been publishing on GitHub.

I am also working on a Twitter bot for this site to tweet the most popular, newest, random packages from npm. Please follow that account now and it will start sending out packages soon–ish.

Open Software & Tools

This site wouldn’t be possible without the immense generosity and tireless efforts from the people who make contributions to the world and share their work via open source initiatives. Thank you 🙏

© 2024 – Pkg Stats / Ryan Hefner

@dwp/casa-looper-plugin

v1.4.3

Published

Catch multiple instances of data by looping over a journey segment

Downloads

1,513

Readme

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!