diff --git a/package-lock.json b/package-lock.json index 7c88ac3..bd1a048 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,12 +1,12 @@ { "name": "@ukhomeoffice/asl-components", - "version": "13.5.2", + "version": "13.6.1", "lockfileVersion": 2, "requires": true, "packages": { "": { "name": "@ukhomeoffice/asl-components", - "version": "13.5.2", + "version": "13.6.1", "license": "MIT", "dependencies": { "@ukhomeoffice/asl-constants": "^2.1.5", @@ -7721,12 +7721,13 @@ } }, "node_modules/micromatch": { - "version": "4.0.5", - "resolved": "https://registry.npmjs.org/micromatch/-/micromatch-4.0.5.tgz", - "integrity": "sha512-DMy+ERcEW2q8Z2Po+WNXuw3c5YaUSFjAO5GsJqfEl7UjvtIuFKO6ZrKvcItdy98dwFI2N1tg3zNIdKaQT+aNdA==", + "version": "4.0.8", + "resolved": "https://registry.npmjs.org/micromatch/-/micromatch-4.0.8.tgz", + "integrity": "sha512-PXwfBhYu0hBCPw8Dn0E+WDYb7af3dSLVWKi3HGv84IdF4TyFoC0ysxFd0Goxw7nSv4T/PzEJQxsYsEiFCKo2BA==", "dev": true, + "license": "MIT", "dependencies": { - "braces": "^3.0.2", + "braces": "^3.0.3", "picomatch": "^2.3.1" }, "engines": { @@ -15239,12 +15240,12 @@ } }, "micromatch": { - "version": "4.0.5", - "resolved": "https://registry.npmjs.org/micromatch/-/micromatch-4.0.5.tgz", - "integrity": "sha512-DMy+ERcEW2q8Z2Po+WNXuw3c5YaUSFjAO5GsJqfEl7UjvtIuFKO6ZrKvcItdy98dwFI2N1tg3zNIdKaQT+aNdA==", + "version": "4.0.8", + "resolved": "https://registry.npmjs.org/micromatch/-/micromatch-4.0.8.tgz", + "integrity": "sha512-PXwfBhYu0hBCPw8Dn0E+WDYb7af3dSLVWKi3HGv84IdF4TyFoC0ysxFd0Goxw7nSv4T/PzEJQxsYsEiFCKo2BA==", "dev": true, "requires": { - "braces": "^3.0.2", + "braces": "^3.0.3", "picomatch": "^2.3.1" } }, diff --git a/package.json b/package.json index 2bab429..0c7e7cc 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "@ukhomeoffice/asl-components", - "version": "13.6.0", + "version": "13.6.1", "description": "React components for ASL layouts and elements", "main": "src/index.jsx", "styles": "styles/index.scss", diff --git a/src/condition-reminders/index.jsx b/src/condition-reminders/index.jsx index e89a4f2..86b2f1b 100644 --- a/src/condition-reminders/index.jsx +++ b/src/condition-reminders/index.jsx @@ -1,30 +1,49 @@ import React from 'react'; -import { format } from 'date-fns'; +import { formatDate, DATE_FORMAT } from '../utils'; import { Inset } from '../'; -function ConditionReminders({ reminders, dateFormat = 'dd/MM/yyyy' }) { - if (!reminders || reminders.length < 1) { +const SINGULAR_CONTENT = { + notice: 'A deadline and automated reminders have been set for this condition', + deadline_list_intro: 'This condition has a deadline set for:', + deadline_list_outro: + 'Licence holders will receive reminders 1 month before, 1 week before' + + ' and on the deadline date. ASRU will receive a reminder when the' + + ' deadline has passed.' +}; + +const PLURAL_CONTENT = { + notice: 'Deadlines and automated reminders have been set for this condition', + deadline_list_intro: 'This condition has deadlines set for:', + deadline_list_outro: + 'Licence holders will receive reminders 1 month before, 1 week before' + + ' and on each deadline date. ASRU will receive reminders when each' + + ' deadline has passed.' +}; + +function ConditionReminders({ reminders, dateFormat = DATE_FORMAT.short }) { + const activeReminders = (reminders ?? []).filter(reminder => reminder.deadline); + + if (activeReminders.length < 1) { return null; } + const content = activeReminders.length === 1 ? SINGULAR_CONTENT : PLURAL_CONTENT; + return (
-

Automated reminders have been set for this condition

+

{content.notice}

- Show when reminders have been scheduled + Show when deadlines and reminders have been scheduled -

This condition has a reminder scheduled for:

+

{content.deadline_list_intro}

    { reminders.map(reminder => ( -
  • {format(reminder.deadline, dateFormat)}
  • +
  • {formatDate(reminder.deadline, dateFormat)}
  • )) }
-

- Licence holders will receive reminders a month before, a week before and on the day the condition - is due to be met. ASRU will receive a reminder when the deadline has passed. -

+

{content.deadline_list_outro}

diff --git a/src/condition-reminders/index.spec.jsx b/src/condition-reminders/index.spec.jsx new file mode 100644 index 0000000..4801fde --- /dev/null +++ b/src/condition-reminders/index.spec.jsx @@ -0,0 +1,64 @@ +import { shallow, render } from 'enzyme'; +import React from 'react'; +import ConditionReminders from './index'; +import {describe, expect, test} from '@jest/globals' +import {v4 as uuid} from 'uuid' + +function reminderWithDeadline(deadline) { + return { + id: uuid(), + deadline + } +} + +describe('ConditionReminders component', () => { + test('Empty reminders should render nothing', () => { + for (const reminders of [null, []]) { + const wrapper = shallow(); + expect(wrapper.isEmptyRender()).toBe(true); + } + }) + + test('Reminders without a deadline should be ignored', () => { + const reminders = [{id: uuid()}] + const wrapper = shallow(); + expect(wrapper.isEmptyRender()).toBe(true); + }) + + test('When there is one reminder the copy should be singular', () => { + const reminders = [reminderWithDeadline("2025-01-01")] + const wrapper = render(); + expect(wrapper.text()) + .toContain( + "A deadline and automated reminders have been set for this condition" + ); + expect(wrapper.text()) + .toContain("This condition has a deadline set for:"); + expect(wrapper.text()) + .toContain( + "Licence holders will receive reminders 1 month before," + + " 1 week before and on the deadline date. ASRU will receive a reminder" + + " when the deadline has passed." + ); + }) + + test('When there are two reminders the copy should be plural', () => { + const reminders = [ + reminderWithDeadline("2025-01-01"), + reminderWithDeadline("2025-02-02") + ] + const wrapper = render(); + expect(wrapper.text()) + .toContain( + "Deadlines and automated reminders have been set for this condition" + ); + expect(wrapper.text()) + .toContain("This condition has deadlines set for:"); + expect(wrapper.text()) + .toContain( + "Licence holders will receive reminders 1 month before, " + + "1 week before and on each deadline date. ASRU will receive reminders " + + "when each deadline has passed." + ); + }) +}) diff --git a/src/utils.js b/src/utils.js index bfde3ac..b4880a4 100644 --- a/src/utils.js +++ b/src/utils.js @@ -1,6 +1,7 @@ const { stringify, parse } = require('qs'); const get = require('lodash/get'); const url = require('url'); +const { format: dateFormatter } = require('date-fns'); const getValue = ({ row, schema, key }) => { const accessor = schema.accessor || key; @@ -36,8 +37,23 @@ const getSort = (column, state) => ({ ascending: state.column === column ? !state.ascending : true }); +const DATE_FORMAT = { + long: 'd MMMM yyyy', + short: 'd/M/yyyy' +}; + +const formatDate = (date, format = DATE_FORMAT.long) => { + try { + return date ? dateFormatter(date, format) : '-'; + } catch (err) { + return 'Invalid date entered'; + } +}; + module.exports = { getValue, queryStringFromState, - getSort + getSort, + formatDate, + DATE_FORMAT }; diff --git a/src/utils.spec.js b/src/utils.spec.js new file mode 100644 index 0000000..d6dc0f0 --- /dev/null +++ b/src/utils.spec.js @@ -0,0 +1,18 @@ +const { formatDate, DATE_FORMAT } = require('./utils'); + +describe('formatDate', () => { + test('formats a valid date', () => { + expect(formatDate('2024-01-01', DATE_FORMAT.short)).toBe('1/1/2024'); + }); + + test('returns "-" for empty dates', () => { + expect(formatDate(null, DATE_FORMAT.short)).toBe('-'); + expect(formatDate(undefined, DATE_FORMAT.short)).toBe('-'); + expect(formatDate('', DATE_FORMAT.short)).toBe('-'); + }); + + test('returns "Invalid date entered" for invlaid dates', () => { + expect(formatDate('not a date', DATE_FORMAT.short)).toBe('Invalid date entered'); + expect(formatDate('24-01-01', DATE_FORMAT.short)).toBe('Invalid date entered'); + }); +});