diff --git a/CHANGELOG.md b/CHANGELOG.md index cc0bbe8e..9a0a224d 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,9 +1,12 @@ ## 2025-01-20, Version 22.0.0, @Rhodine-orleans-lindsay * Adds session timeout warning - - user can stay on page or exit form - - adds exit html - - updates confirmation html to a static page - - allows for customisation of session timeout warning dialog content and exit page content + - user can stay on page or exit form + - adds exit html + - user can stay signed in or save and exit the form if the form is a save and exit form + - adds default save-and-exit html + - updates confirmation html to a static page + - allows for customisation of session timeout warning dialog content, exit and save-and-exit page content, and exit and save-and-exit steps + - Potential **_breaking change_**: Static pages should use the ```{{/index.js` to the desired path name: + +```js +// customising exit step name +module.exports = { + name: 'sandbox', + exitStep: '/leave', + steps: { + ... + '/leave': { + template: 'exit' + } + } + ... +} +``` + +```js +// customising save-and-exit step name +module.exports = { + name: 'sandbox', + saveAndExitStep: '/sign-out', + steps: { + ... + '/sign-out': { + template: 'save-and-exit' + } + } + ... +} +``` + # UTILITIES # Autofill Utility diff --git a/components/session-timeout-warning/index.js b/components/session-timeout-warning/index.js index 45ccec41..58eeb076 100644 --- a/components/session-timeout-warning/index.js +++ b/components/session-timeout-warning/index.js @@ -51,6 +51,17 @@ module.exports = superclass => class extends superclass { superLocals.message = req.translate('exit.message'); return superLocals; } + + // set the content on /save-and-exit page + if (req.form.options.route === '/save-and-exit' && config.saveExitFormContent === true) { + superLocals.saveExitFormContent = true; + return superLocals; + } else if (req.form.options.route === '/save-and-exit' && config.saveExitFormContent === false) { + superLocals.header = req.translate('save-and-exit.header'); + superLocals.title = req.translate('save-and-exit.title'); + superLocals.message = req.translate('save-and-exit.message'); + return superLocals; + } return superLocals; } }; diff --git a/config/hof-defaults.js b/config/hof-defaults.js index 3b6d6152..8f178174 100644 --- a/config/hof-defaults.js +++ b/config/hof-defaults.js @@ -16,6 +16,7 @@ const defaults = { getAccessibility: false, sessionTimeoutWarningContent: false, exitFormContent: false, + saveExitFormContent: false, viewEngine: 'html', protocol: process.env.PROTOCOL || 'http', noCache: process.env.NO_CACHE || false, diff --git a/controller/controller.js b/controller/controller.js index f4ecac7c..503a2f71 100644 --- a/controller/controller.js +++ b/controller/controller.js @@ -109,10 +109,14 @@ module.exports = class Controller extends BaseController { // only include fields that aren't dependents to mitigate duplicate fields on the page fields = fields.filter(field => !req.form.options.fields[field.key].dependent); + const exitStep = req.form.options.exitStep || '/exit'; + const saveAndExitStep = req.form.options.saveAndExitStep || '/save-and-exit'; return _.extend({}, locals, { fields, route, baseUrl: req.baseUrl, + exitStep, + saveAndExitStep, skipToMain: this.getFirstFormItem(req.form.options.fields), title: this.getTitle(route, lookup, req.form.options.fields, res.locals), journeyHeaderURL: this.getJourneyHeaderURL(req.baseUrl), diff --git a/frontend/template-partials/translations/src/en/save-and-exit.json b/frontend/template-partials/translations/src/en/save-and-exit.json new file mode 100644 index 00000000..9d235af5 --- /dev/null +++ b/frontend/template-partials/translations/src/en/save-and-exit.json @@ -0,0 +1,4 @@ +{ + "header": "You have been signed out", + "message": "Any answers you saved have not been affected. You can sign back in to your application by returning to the start page." +} diff --git a/frontend/template-partials/views/partials/head.html b/frontend/template-partials/views/partials/head.html index 63670a17..44ce551a 100644 --- a/frontend/template-partials/views/partials/head.html +++ b/frontend/template-partials/views/partials/head.html @@ -26,6 +26,6 @@ {{/deIndex}} diff --git a/frontend/template-partials/views/partials/session-timeout-warning.html b/frontend/template-partials/views/partials/session-timeout-warning.html index 5f7f36ac..14a04afe 100644 --- a/frontend/template-partials/views/partials/session-timeout-warning.html +++ b/frontend/template-partials/views/partials/session-timeout-warning.html @@ -8,15 +8,31 @@ data-url-redirect="/session-timeout" class="modal-dialog dialog" role="dialog" aria-live="polite" aria-labelledby="dialog-title" aria-describedby="at-timer"> diff --git a/frontend/template-partials/views/save-and-exit.html b/frontend/template-partials/views/save-and-exit.html new file mode 100644 index 00000000..a1fb9007 --- /dev/null +++ b/frontend/template-partials/views/save-and-exit.html @@ -0,0 +1,17 @@ +{{ partials-navigation}} + {{/propositionHeader}} + + {{$header}} + {{header}} + {{/header}} + + {{$content}} +

{{#saveExitFormContent}}{{#t}}pages.save-and-exit.message{{/t}}{{/saveExitFormContent}}{{^saveExitFormContent}}{{{message}}}{{/saveExitFormContent}}

+ {{/content}} +{{/layout}} diff --git a/frontend/themes/gov-uk/client-js/session-timeout-dialog.js b/frontend/themes/gov-uk/client-js/session-timeout-dialog.js index 935f6274..135205fb 100644 --- a/frontend/themes/gov-uk/client-js/session-timeout-dialog.js +++ b/frontend/themes/gov-uk/client-js/session-timeout-dialog.js @@ -13,7 +13,7 @@ window.GOVUK.sessionDialog = { $fallBackElement: $('.govuk-timeout-warning-fallback'), dialogIsOpenClass: 'dialog-is-open', timers: [], - warningTextPrefix: 'To protect your information, this page will time out in ', + warningTextPrefix: $('.dialog-text-prefix').text(), warningTextSuffix: '.', warningText: $('.dialog-text').text(), warningTextExtra: '', diff --git a/index.js b/index.js index ae25be7b..9e733c37 100644 --- a/index.js +++ b/index.js @@ -154,6 +154,7 @@ function bootstrap(options) { res.locals.sessionTimeOutWarning = config.sessionTimeOutWarning; res.locals.sessionTimeoutWarningContent = config.sessionTimeoutWarningContent; res.locals.exitFormContent = config.exitFormContent; + res.locals.saveExitFormContent = config.saveExitFormContent; next(); }); diff --git a/lib/router.js b/lib/router.js index b7939d49..e146f612 100644 --- a/lib/router.js +++ b/lib/router.js @@ -30,6 +30,8 @@ function getWizardConfig(config) { // whitelist properties from the route's config that should be passed into the form wizard const props = [ 'confirmStep', + 'exitStep', + 'saveAndExitStep', 'params' ]; Object.assign(wizardConfig, _.pick(config.route, props)); diff --git a/sandbox/apps/sandbox/index.js b/sandbox/apps/sandbox/index.js index 8de53d06..36f89a9d 100644 --- a/sandbox/apps/sandbox/index.js +++ b/sandbox/apps/sandbox/index.js @@ -30,6 +30,7 @@ module.exports = { }, '/dob': { fields: ['dateOfBirth'], + locals: { showSaveAndExit: true }, next: '/address' }, '/address': { @@ -92,6 +93,7 @@ module.exports = { next: '/confirm' }, '/session-timeout': {}, - '/exit': {} + '/exit': {}, + '/save-and-exit': {} } }; diff --git a/sandbox/apps/sandbox/translations/src/en/pages.json b/sandbox/apps/sandbox/translations/src/en/pages.json index 12f59c9b..d8b7524c 100644 --- a/sandbox/apps/sandbox/translations/src/en/pages.json +++ b/sandbox/apps/sandbox/translations/src/en/pages.json @@ -58,9 +58,15 @@ "message": "We have cleared your information to keep it secure. Your information has not been saved." }, "session-timeout-warning": { - "dialog-title": "Your application will close soon", - "dialog-text": "If that happens, your progress will not be saved.", - "timeout-continue-button": "Stay on this page", - "dialog-exit-link": "Exit this form" + "dialog-title": "{{^showSaveAndExit}}Your application will close soon{{/showSaveAndExit}}{{#showSaveAndExit}}You will be signed out soon{{/showSaveAndExit}}", + "dialog-text": "{{^showSaveAndExit}}If that happens, your progress will not be saved.{{/showSaveAndExit}}{{#showSaveAndExit}}Any answers you have saved will not be affected, but your progress on this page will not be saved.{{/showSaveAndExit}}", + "timeout-continue-button": "{{^showSaveAndExit}}Stay on this page{{/showSaveAndExit}}{{#showSaveAndExit}}Stay signed in{{/showSaveAndExit}}", + "dialog-exit-link": "{{^showSaveAndExit}}Exit this form{{/showSaveAndExit}}{{#showSaveAndExit}}Sign out{{/showSaveAndExit}}" + }, + "save-and-exit": { + "header": "You have been signed out", + "paragraph-1": "Your form doesn't appear to have been worked on for 30 minutes so we closed it for security.", + "paragraph-2": "Any answers you saved have not been affected.", + "paragraph-3": "You can sign back in to your application at any time by returning to the start page." } } diff --git a/sandbox/apps/sandbox/views/save-and-exit.html b/sandbox/apps/sandbox/views/save-and-exit.html new file mode 100644 index 00000000..15d4c9e0 --- /dev/null +++ b/sandbox/apps/sandbox/views/save-and-exit.html @@ -0,0 +1,19 @@ +{{ partials-navigation}} + {{/propositionHeader}} + + {{$header}} + {{header}} + {{/header}} + + {{$content}} +

{{#t}}pages.save-and-exit.paragraph-1{{/t}}

+

{{#t}}pages.save-and-exit.paragraph-2{{/t}}

+

{{#t}}pages.save-and-exit.paragraph-3{{/t}}

+ {{/content}} +{{/layout}} diff --git a/test/components/session-timeout-warning.spec.js b/test/components/session-timeout-warning.spec.js index 5a16f464..24ea206c 100644 --- a/test/components/session-timeout-warning.spec.js +++ b/test/components/session-timeout-warning.spec.js @@ -113,6 +113,30 @@ describe('session timeout warning component', () => { locals.should.have.property('message').and.deep.equal('exit.message'); }); + it('sets the custom content to true on the save-and-exit page if saveExitFormContent is set to true', () => { + config.saveExitFormContent = true; + req.form = { + options: { + route: '/save-and-exit' + } + }; + const locals = instance.locals(req, res); + locals.should.have.property('saveExitFormContent').and.deep.equal(true); + }); + + it('does sets the default content on the save-and-exit page if saveExitFormContent is set to false', () => { + config.saveExitFormContent = false; + req.form = { + options: { + route: '/save-and-exit' + } + }; + const locals = instance.locals(req, res); + locals.should.have.property('header').and.deep.equal('save-and-exit.header'); + locals.should.have.property('title').and.deep.equal('save-and-exit.title'); + locals.should.have.property('message').and.deep.equal('save-and-exit.message'); + }); + afterEach(() => { Base.prototype.locals.restore(); }); diff --git a/test/integration/bootstrap.spec.js b/test/integration/bootstrap.spec.js index 83e005ab..9ecfd40b 100644 --- a/test/integration/bootstrap.spec.js +++ b/test/integration/bootstrap.spec.js @@ -200,5 +200,39 @@ describe('bootstrap()', () => { }); behaviourOptions.confirmStep.should.equal('/summary'); }); + + it('can pass the exit step to controllers', () => { + bs = bootstrap({ + fields: 'fields', + appConfig: appConfig, + routes: [{ + views: `${root}/apps/app_1/views`, + exitStep: '/leave', + steps: { + '/one': { + behaviours: behaviour + } + } + }] + }); + behaviourOptions.exitStep.should.equal('/leave'); + }); + + it('can pass the exit step to controllers', () => { + bs = bootstrap({ + fields: 'fields', + appConfig: appConfig, + routes: [{ + views: `${root}/apps/app_1/views`, + saveAndExitStep: '/sign-out', + steps: { + '/one': { + behaviours: behaviour + } + } + }] + }); + behaviourOptions.saveAndExitStep.should.equal('/sign-out'); + }); }); }); diff --git a/wizard/index.js b/wizard/index.js index 14747307..aee80049 100644 --- a/wizard/index.js +++ b/wizard/index.js @@ -67,6 +67,8 @@ const Wizard = (steps, fields, setts) => { options.route = route; options.appConfig = settings.appConfig; options.confirmStep = settings.confirmStep; + options.exitStep = settings.exitStep; + options.saveAndExitStep = settings.saveAndExitStep; options.clearSession = options.clearSession || false; options.fieldsConfig = _.cloneDeep(fields); options.sanitiseInputs = settings.sanitiseInputs;