From 0136a45b9fc776422c00510a5f6f0337517eb186 Mon Sep 17 00:00:00 2001 From: OgunyemiO <142920130+OgunyemiO@users.noreply.github.com> Date: Mon, 16 Sep 2024 13:01:59 +0100 Subject: [PATCH] Start date and end date added to Service Message Banner (#458) * Start date and end date added to Service Message * version number change * removed commented lines of code * removed translation from component * version number changed * unit test text changed * version updated * error handling message added * banner message errors modified * version number amended * error reworked and unit test * version change * AC04 AC05 AC06 and unit test covered * version number changed * error bug fix * version text change * message error bug fixes * version change * reworked greater than dates * unit test and version update * Update to release version number --------- Co-authored-by: RiteshHMCTS <74713687+RiteshHMCTS@users.noreply.github.com> Co-authored-by: Ritesh Dsouza Co-authored-by: Josh --- package.json | 2 +- projects/exui-common-lib/package.json | 2 +- .../service-message.component.html | 3 +- .../service-message.component.scss | 3 + .../service-message.component.spec.ts | 10 +- .../service-message.component.ts | 13 +- .../service-messages.component.html | 22 +- .../service-messages.component.spec.ts | 200 ++++++++++++++++-- .../service-messages.component.ts | 77 +++++-- .../src/lib/models/service-message.model.ts | 8 + 10 files changed, 302 insertions(+), 38 deletions(-) create mode 100644 projects/exui-common-lib/src/lib/components/service-message/service-message.component.scss create mode 100644 projects/exui-common-lib/src/lib/models/service-message.model.ts diff --git a/package.json b/package.json index cac17fec..2d8259c7 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "@hmcts/rpx-xui-common-lib", - "version": "2.0.28", + "version": "2.0.29", "engines": { "node": ">=18.19.0" }, diff --git a/projects/exui-common-lib/package.json b/projects/exui-common-lib/package.json index 062355a1..78b922c2 100644 --- a/projects/exui-common-lib/package.json +++ b/projects/exui-common-lib/package.json @@ -1,6 +1,6 @@ { "name": "@hmcts/rpx-xui-common-lib", - "version": "2.0.28", + "version": "2.0.29", "peerDependencies": { "launchdarkly-js-client-sdk": "^3.3.0", "ngx-pagination": "^3.2.1", diff --git a/projects/exui-common-lib/src/lib/components/service-message/service-message.component.html b/projects/exui-common-lib/src/lib/components/service-message/service-message.component.html index 0b625114..72400eb2 100644 --- a/projects/exui-common-lib/src/lib/components/service-message/service-message.component.html +++ b/projects/exui-common-lib/src/lib/components/service-message/service-message.component.html @@ -7,7 +7,8 @@
{{'Warning' | rpxTranslate}} -

+

+

{{'Hide message' | rpxTranslate}}
diff --git a/projects/exui-common-lib/src/lib/components/service-message/service-message.component.scss b/projects/exui-common-lib/src/lib/components/service-message/service-message.component.scss new file mode 100644 index 00000000..f7f5fe7f --- /dev/null +++ b/projects/exui-common-lib/src/lib/components/service-message/service-message.component.scss @@ -0,0 +1,3 @@ +.govuk-link--no-visited-state { + cursor: pointer; +} \ No newline at end of file diff --git a/projects/exui-common-lib/src/lib/components/service-message/service-message.component.spec.ts b/projects/exui-common-lib/src/lib/components/service-message/service-message.component.spec.ts index ba331de0..6ea8cd77 100644 --- a/projects/exui-common-lib/src/lib/components/service-message/service-message.component.spec.ts +++ b/projects/exui-common-lib/src/lib/components/service-message/service-message.component.spec.ts @@ -2,6 +2,7 @@ import { Pipe, PipeTransform } from '@angular/core'; import { ComponentFixture, TestBed, waitForAsync } from '@angular/core/testing'; import { RouterTestingModule } from '@angular/router/testing'; import { ServiceMessageComponent } from './service-message.component'; +import { ServiceMessages } from '../../models/service-message.model'; @Pipe({ name: 'rpxTranslate' }) class RpxTranslateMockPipe implements PipeTransform { @@ -35,7 +36,14 @@ describe('ServiceMessageComponent', () => { describe('onHideMessageEvent()', () => { it('should emit the id of the message to hide', () => { - const hideKey = 'caseworker-probate'; + const hideKey: ServiceMessages = { + roles: 'caseworker-probate', + index: 1, + message_en: 'New message', + message_cy: 'Anyyu', + begin: '2024-04-18T00:00:00', + end: '2024-04-19T00:00:00' + }; const hideMessageSpy = spyOn(component.hideMessage, 'emit'); component.onHideMessageEvent(hideKey); expect(hideMessageSpy).toHaveBeenCalledWith(hideKey); diff --git a/projects/exui-common-lib/src/lib/components/service-message/service-message.component.ts b/projects/exui-common-lib/src/lib/components/service-message/service-message.component.ts index 69fce59a..fc684838 100644 --- a/projects/exui-common-lib/src/lib/components/service-message/service-message.component.ts +++ b/projects/exui-common-lib/src/lib/components/service-message/service-message.component.ts @@ -1,16 +1,19 @@ import { Component, EventEmitter, Input, Output } from '@angular/core'; +import { ServiceMessages } from '../../models/service-message.model'; @Component({ selector: 'xuilib-service-message', - templateUrl: './service-message.component.html' + templateUrl: './service-message.component.html', + styleUrls: ['./service-message.component.scss'] }) export class ServiceMessageComponent { - @Input() public message: string; - @Input() public key: string; - @Output() public hideMessage = new EventEmitter(); + @Input() public message_en: string; + @Input() public message_cy?: string; + @Input() public key: ServiceMessages; + @Output() public hideMessage = new EventEmitter(); constructor() { } - public onHideMessageEvent(key: string) { + public onHideMessageEvent(key: ServiceMessages) { this.hideMessage.emit(key); } } diff --git a/projects/exui-common-lib/src/lib/components/service-messages/service-messages.component.html b/projects/exui-common-lib/src/lib/components/service-messages/service-messages.component.html index 4b216ee8..09c8fd7b 100644 --- a/projects/exui-common-lib/src/lib/components/service-messages/service-messages.component.html +++ b/projects/exui-common-lib/src/lib/components/service-messages/service-messages.component.html @@ -1,4 +1,20 @@ -
- +
+
+ + + +
+ {{'Warning' | rpxTranslate}} +

Error:

+

+
+
+
+
+ +
\ No newline at end of file diff --git a/projects/exui-common-lib/src/lib/components/service-messages/service-messages.component.spec.ts b/projects/exui-common-lib/src/lib/components/service-messages/service-messages.component.spec.ts index ba439182..a491fb49 100644 --- a/projects/exui-common-lib/src/lib/components/service-messages/service-messages.component.spec.ts +++ b/projects/exui-common-lib/src/lib/components/service-messages/service-messages.component.spec.ts @@ -4,6 +4,7 @@ import { RpxTranslationConfig, RpxTranslationService } from 'rpx-xui-translation import { of } from 'rxjs'; import { FeatureToggleService } from '../../services/feature-toggle/feature-toggle.service'; import { ServiceMessagesComponent } from './service-messages.component'; +import { ServiceMessages } from '../../models/service-message.model'; @Pipe({ name: 'rpxTranslate' }) class RpxTranslateMockPipe implements PipeTransform { @@ -17,14 +18,25 @@ describe('ServiceMessagesComponent', () => { let fixture: ComponentFixture; const mockFeatureToggleService = jasmine.createSpyObj('FeatureToggleService', ['getValue']); - const serviceMessagesFake = { - 'caseworker-divorce': 'Divorce users may experience longer loading times than usual in the system.
Click here to find out more.', - 'caseworker-divorce|caseworker-probate': 'Divorce and probate users may experience longer loading times than usual in the system.
Click here to find out more.', - 'caseworker-notfound': 'Not found users may experience longer loading times than usual in the system.
Click here to find out more.', - 'caseworker-probate': 'Probate users may experience longer loading times than usual in the system.
Click here to find out more.', - 'caseworker-probate|caseworker-withanotherscript': 'Probate and script users may experience longer loading times than usual in the system.
Click here to find out more.Ha!ipt> alert("WOW");*', - 'caseworker-probate|caseworker-withscript': 'Probate and script users may experience longer loading times than usual in the system.
Click here to find out more.**' - }; + const + serviceMessagesFake: ServiceMessages[] = [ + { + roles: 'caseworker-divorce', + index: 2, + message_en: 'Alert services notification', + message_cy: 'Anyyu', + begin: '2024-04-18T00:00:00', + end: '2034-05-19T00:00:00' + }, + { + roles: 'caseworker-probate', + index: 3, + message_en: 'Please submit all required forms today ', + message_cy: 'Anyyu', + begin: '2024-04-18T00:00:00', + end: '2044-04-20T00:00:00' + } + ]; beforeEach(waitForAsync(() => { TestBed.configureTestingModule({ @@ -46,6 +58,7 @@ describe('ServiceMessagesComponent', () => { fixture = TestBed.createComponent(ServiceMessagesComponent); component = fixture.componentInstance; component.userRoles = ['caseworker-divorce', 'caseworker-probate']; + component.originalMessages = []; fixture.detectChanges(); }); @@ -53,19 +66,180 @@ describe('ServiceMessagesComponent', () => { expect(component).toBeTruthy(); }); - describe('getServiceMessages()', () => { + describe('call createFilteredMessages on getServiceMessages() call', () => { it('should get the service messages and filter according to roles', () => { + const serveMsgSpy = spyOn(component, 'createFilteredMessages'); component.getServiceMessages(); - expect(component.filteredMessages.size).toBe(5); + expect(serveMsgSpy).toHaveBeenCalled(); }); }); + describe('call compareDates on createFilteredMessages call', () => { + it('should get the service messages and filter according to roles', () => { + const dateSpy = spyOn(component, 'compareDates'); + component['createFilteredMessages'](serviceMessagesFake); + fixture.autoDetectChanges(); + expect(dateSpy).toHaveBeenCalled(); + }); + }); + + it('should filter out message if start date is in the future', () => { + const serviceMessagesFake: ServiceMessages[] = [ + { + roles: 'caseworker-divorce', + index: 2, + message_en: 'Divorce and probate users may experience longer loading times than usual in the system.
Click here to find out more.', + message_cy: 'Anyyu', + begin: '2034-04-18T00:00:00', + end: '2034-05-19T00:00:00' + }, + { + roles: 'caseworker-probate', + index: 3, + message_en: 'Maintainance notices', + message_cy: 'Anyyu', + begin: '2024-04-18T00:00:00', + end: '2044-04-20T00:00:00' + } + ]; + component['createFilteredMessages'](serviceMessagesFake); + fixture.autoDetectChanges(); + expect(component.filteredMessages.length).toBe(1); + }); + + it('should show message when no start date or end date provided', () => { + const serviceMessagesFake: ServiceMessages[] = [ + { + roles: 'caseworker-divorce', + index: 2, + message_en: 'Happy birthday.', + message_cy: 'Anyyu', + } + ]; + component['createFilteredMessages'](serviceMessagesFake); + fixture.autoDetectChanges(); + expect(component.filteredMessages.length).toBe(1); + }); + + it('should show message when no start date provided', () => { + const serviceMessagesFake: ServiceMessages[] = [ + { + roles: 'caseworker-divorce', + index: 2, + message_en: 'Happy birthday.', + message_cy: 'Anyyu', + end: '2044-04-20T00:00:00' + } + ]; + component['createFilteredMessages'](serviceMessagesFake); + fixture.autoDetectChanges(); + expect(component.filteredMessages.length).toBe(1); + }); + + it('should show message when no end date provided', () => { + const serviceMessagesFake: ServiceMessages[] = [ + { + roles: 'caseworker-divorce', + index: 12, + message_en: 'Happy birthday.', + message_cy: 'Anyyu', + begin: '2024-04-20T00:00:00' + } + ]; + component['createFilteredMessages'](serviceMessagesFake); + fixture.autoDetectChanges(); + expect(component.filteredMessages.length).toBe(1); + }); + + it('should filter out message if end date is in the past', () => { + const serviceMessagesFake: ServiceMessages[] = [ + { + roles: 'caseworker-divorce', + index: 2, + message_en: 'Services up and running', + message_cy: 'Anyyu', + begin: '2024-03-18T00:00:00', + end: '2024-03-19T00:00:00' + } + ]; + component['createFilteredMessages'](serviceMessagesFake); + fixture.autoDetectChanges(); + expect(component.filteredMessages.length).toBe(0); + }); + + it('should filter out message if start date is greater than end date', () => { + const serviceMessagesFake: ServiceMessages[] = [ + { + roles: 'caseworker-divorce', + index: 8, + message_en: 'Judiciary experience required', + message_cy: 'Anyyu', + begin: '2024-09-03T00:00:00', + end: '2024-08-24T00:00:00' + } + ]; + component['createFilteredMessages'](serviceMessagesFake); + fixture.autoDetectChanges(); + expect(component.filteredMessages.length).toBe(0); + }); + + it('should filter out message if date format is wrong', () => { + const serviceMessagesFake: ServiceMessages[] = [ + { + roles: 'caseworker-divorce', + index: 2, + message_en: 'Date is in WRONG format', + message_cy: 'Anyyu', + begin: '04-04-2024T19:00:00', + end: '2024-04-24T00:00:00' + } + ]; + component['createFilteredMessages'](serviceMessagesFake); + fixture.autoDetectChanges(); + expect(component.filteredMessages.length).toBe(0); + }); describe('hideMessage()', () => { it('should add an item to the hidden message list', () => { component.hiddenBanners = []; - const testRole = 'test-message-1'; - component.hideMessage(testRole); + component.hideMessage(serviceMessagesFake[0]); expect((component.hiddenBanners).length).toBe(1); }); }); -}); + + it('should show error message if start date is greater than the end date', () => { + const serviceMessagesFake: ServiceMessages[] = [ + { + roles: 'caseworker-divorce', + index: 2, + message_en: 'Judiciary experience required', + message_cy: 'Anyyu', + begin: '2024-04-25T00:00:00', + end: '2024-04-24T00:00:00' + } + ]; + component.originalMessages = serviceMessagesFake; + component['compareDates'](serviceMessagesFake[0]); + fixture.detectChanges(); + expect(component.isBannerError).toBeTruthy(); + expect(component.bannerErrorMsgs).toContain({ message: `The start date is greater than the end date. Message index: ${serviceMessagesFake[0].index}`, index: 2 }); + }); + + it('should show error message if start date or end date is not well formed', () => { + const serviceMessagesFake: ServiceMessages[] = [ + { + roles: 'caseworker-divorce', + index: 10, + message_en: 'Malformed start or end date', + message_cy: 'Incorrect date', + begin: '2024-04-25 T00:00:00', + end: '2024-04-24T00:00:00' + } + ]; + component.originalMessages = serviceMessagesFake; + component['compareDates'](serviceMessagesFake[0]); + fixture.autoDetectChanges(); + expect(component.isBannerError).toBeTrue(); + expect(component.bannerErrorMsgs.length).toBe(1); + expect(component.bannerErrorMsgs).toContain({ message: `Invalid start date. Message index: ${serviceMessagesFake[0].index}`, index: 10 }); + }); +}); \ No newline at end of file diff --git a/projects/exui-common-lib/src/lib/components/service-messages/service-messages.component.ts b/projects/exui-common-lib/src/lib/components/service-messages/service-messages.component.ts index b90826fc..b3625c14 100644 --- a/projects/exui-common-lib/src/lib/components/service-messages/service-messages.component.ts +++ b/projects/exui-common-lib/src/lib/components/service-messages/service-messages.component.ts @@ -1,9 +1,7 @@ import { Component, Input, OnInit } from '@angular/core'; import { FeatureToggleService } from '../../services/feature-toggle/feature-toggle.service'; +import { ServiceMessages } from '../../models/service-message.model'; -export interface ServiceMessages { - [key: string]: string; -} @Component({ selector: 'xuilib-service-messages', @@ -11,7 +9,10 @@ export interface ServiceMessages { }) export class ServiceMessagesComponent implements OnInit { public hiddenBanners: string[]; - public filteredMessages = new Map(); + public filteredMessages: ServiceMessages[] = []; + public isBannerError: boolean; + public bannerErrorMsgs: { message: string, index: number }[] = []; + public originalMessages: ServiceMessages[] = []; @Input() public userRoles: string[]; @Input() public featureToggleKey: string; @@ -24,27 +25,77 @@ export class ServiceMessagesComponent implements OnInit { } public getServiceMessages(): void { - this.featureToggleService.getValue(this.featureToggleKey, null) + this.featureToggleService.getValue(this.featureToggleKey, null) .subscribe(messages => { + this.originalMessages = messages; if (!!messages) { this.createFilteredMessages(messages); } }); } - private createFilteredMessages(messages: ServiceMessages): void { + private createFilteredMessages(messages: ServiceMessages[]): void { this.hiddenBanners = JSON.parse(window.sessionStorage.getItem(this.serviceMessageCookie)) || []; - Object.keys(messages).forEach(key => { - const regEx = new RegExp(key); - if (this.userRoles.some(e => regEx.test(e)) && this.hiddenBanners.indexOf(key) === -1) { - this.filteredMessages.set(key, messages[key]); + + messages.forEach(key => { + const regEx = new RegExp(key.roles); + if (this.userRoles.some(e => regEx.test(e))) { + this.filteredMessages = messages.filter(msg => !this.hiddenBanners.includes(msg.message_en) + && this.compareDates(msg)); + } + }); + } + + private compareDates(msg: ServiceMessages): boolean { + let currentDateTime = new Date(); + let beginDate: Date | null = null; + let endDate: Date | null = null; + this.bannerErrorMsgs = []; + + // Parse beginDate if msg.begin is present and valid + if (msg.begin && !isNaN(Date.parse(msg.begin))) { + beginDate = new Date(msg.begin); + } + + if (msg.end && !isNaN(Date.parse(msg.end))) { + endDate = new Date(msg.end); + } + this.originalMessages.forEach(msg => { + // Only check for errors if both beginDate and endDate are present and valid + if ((msg.begin && !isNaN(Date.parse(msg.end))) && (msg.end && !isNaN(Date.parse(msg.end))) && new Date(msg.begin) > new Date(msg.end)) { + this.isBannerError = true; + this.bannerErrorMsgs.push({ + message: `The start date is greater than the end date. Message index: ${msg.index}`, + index: msg.index + }); + } + + // Check for invalid beginDate or endDate separately, if they are present + if (msg.begin && isNaN(Date.parse(msg.begin))) { + this.isBannerError = true; + this.bannerErrorMsgs.push({ + message: `Invalid start date. Message index: ${msg.index}`, + index: msg.index + }); + } + if (msg.end && isNaN(Date.parse(msg.end))) { + this.isBannerError = true; + this.bannerErrorMsgs.push({ + message: `Invalid end date. Message index: ${msg.index}`, + index: msg.index + }); } }); + + const beginDateOK = !msg.begin || (beginDate && beginDate < currentDateTime); + const endDateOK = !msg.end || (endDate && endDate >= currentDateTime); + + return beginDateOK && endDateOK; } - public hideMessage(key: string): void { - this.filteredMessages.delete(key); - this.hiddenBanners.push(key); + public hideMessage(msg: ServiceMessages): void { + this.filteredMessages = this.filteredMessages.filter(f => f.index !== msg.index) + this.hiddenBanners.push(msg.message_en); window.sessionStorage.setItem(this.serviceMessageCookie, JSON.stringify(this.hiddenBanners)); } } diff --git a/projects/exui-common-lib/src/lib/models/service-message.model.ts b/projects/exui-common-lib/src/lib/models/service-message.model.ts new file mode 100644 index 00000000..49b08da3 --- /dev/null +++ b/projects/exui-common-lib/src/lib/models/service-message.model.ts @@ -0,0 +1,8 @@ +export interface ServiceMessages { + index: number; + roles: string; + message_en: string; + message_cy: string; + begin?: string; + end?: string; + } \ No newline at end of file