Skip to content

Commit

Permalink
EUI-8679/8770: WorkbasketFiltersComponent and JudicialUser field fixes
Browse files Browse the repository at this point in the history
EUI-8679: Remove JudicialUser FormControl values from the overall FormGroup value stored and passed as search criteria by the `WorkbasketFiltersComponent` when applying filters; EUI-8770: Ensure the `WorkbasketFiltersComponent` announces the selected jurisdiction and case type via the `JurisdictionService`; in the `WriteJudicialUserField` component, get the current jurisdiction and case type from the `JurisdictionService` if no case info is present.
  • Loading branch information
Daniel-Lam committed Sep 11, 2023
1 parent 502d2cb commit 9b94f98
Show file tree
Hide file tree
Showing 7 changed files with 166 additions and 28 deletions.
4 changes: 4 additions & 0 deletions RELEASE-NOTES.md
Original file line number Diff line number Diff line change
@@ -1,4 +1,8 @@
## RELEASE NOTES
### Version 6.19.5-hotfix-EUI-8679-8770
**EUI-8679** Remove JudicialUser FormControl values from the overall FormGroup value stored and passed as search criteria by the `WorkbasketFiltersComponent` when applying filters
**EUI-8770** Ensure the `WorkbasketFiltersComponent` announces the selected jurisdiction and case type via the `JurisdictionService`; in the `WriteJudicialUserField` component, get the current jurisdiction and case type from the `JurisdictionService` if no case info is present

### Version 6.19.5-secure-doc-case-creation.1
**EXUI-623** Make secure docstore work during case creation

Expand Down
2 changes: 1 addition & 1 deletion package.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"name": "@hmcts/ccd-case-ui-toolkit",
"version": "6.19.5-secure-doc-case-creation.1",
"version": "6.19.5-hotfix-EUI-8679-8770",
"engines": {
"yarn": "^3.5.0",
"npm": "^8.10.0"
Expand Down
2 changes: 1 addition & 1 deletion projects/ccd-case-ui-toolkit/package.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"name": "@hmcts/ccd-case-ui-toolkit",
"version": "6.19.5-secure-doc-case-creation.1",
"version": "6.19.5-hotfix-EUI-8679-8770",
"engines": {
"yarn": "^3.5.0",
"npm": "^8.10.0"
Expand Down
Original file line number Diff line number Diff line change
@@ -1,12 +1,12 @@
import { CUSTOM_ELEMENTS_SCHEMA, Pipe, PipeTransform } from '@angular/core';
import { ComponentFixture, TestBed, waitForAsync } from '@angular/core/testing';
import { ComponentFixture, TestBed, fakeAsync, tick, waitForAsync } from '@angular/core/testing';
import { FormGroup, ReactiveFormsModule, ValidationErrors, Validators } from '@angular/forms';
import { MatAutocompleteModule } from '@angular/material/autocomplete';
import { By } from '@angular/platform-browser';
import { of, throwError } from 'rxjs';
import { BehaviorSubject, of, throwError } from 'rxjs';
import { Constants } from '../../../commons/constants';
import { HmctsServiceDetail } from '../../../domain/case-flag';
import { CaseField, FieldType } from '../../../domain/definition';
import { CaseField, CaseTypeLite, FieldType, Jurisdiction } from '../../../domain/definition';
import { JudicialUserModel } from '../../../domain/jurisdiction';
import { CaseFlagRefdataService, FieldsUtils, FormValidatorsService, JurisdictionService, SessionStorageService } from '../../../services';
import { FirstErrorPipe, IsCompoundPipe, PaletteUtilsModule } from '../utils';
Expand Down Expand Up @@ -499,4 +499,58 @@ describe('WriteJudicialUserFieldComponent', () => {
const errorMessageElement = fixture.debugElement.query(By.css('.error-message')).nativeElement;
expect(errorMessageElement.textContent).toContain('Judicial User is required');
});

it('should get the jurisdiction and case type via the JurisdictionService if there is no case info', fakeAsync(() => {
sessionStorageService.getItem.and.returnValue(null);
const dummyJurisdictionAndCaseType = {
id: 'J1',
name: 'Jurisdiction 1',
description: 'Dummy jurisdiction',
caseTypes: [],
currentCaseType: {
id: 'CT1',
name: 'Case Type 1'
} as CaseTypeLite
} as Jurisdiction;
Object.defineProperty(jurisdictionService, 'selectedJurisdictionBS', { value: new BehaviorSubject(null) });
jurisdictionService.selectedJurisdictionBS.next(dummyJurisdictionAndCaseType);
spyOn(jurisdictionService.selectedJurisdictionBS, 'subscribe').and.callThrough();
component.setJurisdictionAndCaseType();
tick();
expect(jurisdictionService.selectedJurisdictionBS.subscribe).toHaveBeenCalled();
expect(component.jurisdiction).toEqual(dummyJurisdictionAndCaseType.id);
expect(component.caseType).toEqual(dummyJurisdictionAndCaseType.currentCaseType.id);
}));

it('should not get the jurisdiction and case type via the JurisdictionService if there is case info', () => {
Object.defineProperty(jurisdictionService, 'selectedJurisdictionBS', { value: new BehaviorSubject(null) });
spyOn(jurisdictionService.selectedJurisdictionBS, 'subscribe');
component.setJurisdictionAndCaseType();
expect(jurisdictionService.selectedJurisdictionBS.subscribe).not.toHaveBeenCalled();
expect(component.jurisdiction).toEqual('SSCS');
expect(component.caseType).toEqual('Benefit');
});

it('should unsubscribe from any subscriptions when the component is destroyed', fakeAsync(() => {
sessionStorageService.getItem.and.returnValue(null);
const dummyJurisdictionAndCaseType = {
id: 'J1',
name: 'Jurisdiction 1',
description: 'Dummy jurisdiction',
caseTypes: [],
currentCaseType: {
id: 'CT1',
name: 'Case Type 1'
} as CaseTypeLite
} as Jurisdiction;
Object.defineProperty(jurisdictionService, 'selectedJurisdictionBS', { value: new BehaviorSubject(null) });
jurisdictionService.selectedJurisdictionBS.next(dummyJurisdictionAndCaseType);
spyOn(jurisdictionService.selectedJurisdictionBS, 'subscribe').and.callThrough();
component.setJurisdictionAndCaseType();
tick();
expect(jurisdictionService.selectedJurisdictionBS.subscribe).toHaveBeenCalled();
spyOn(component.jurisdictionSubscription, 'unsubscribe');
component.ngOnDestroy();
expect(component.jurisdictionSubscription.unsubscribe).toHaveBeenCalled();
}));
});
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
import { Component, OnInit } from '@angular/core';
import { Component, OnDestroy, OnInit } from '@angular/core';
import { AbstractControl, FormControl, ValidationErrors, Validators } from '@angular/forms';
import { Observable, of } from 'rxjs';
import { Observable, Subscription, of } from 'rxjs';
import { catchError, debounceTime, filter, map, switchMap, take, tap } from 'rxjs/operators';
import { Constants } from '../../../commons/constants';
import { JudicialUserModel } from '../../../domain/jurisdiction';
Expand All @@ -13,7 +13,7 @@ import { IsCompoundPipe } from '../utils/is-compound.pipe';
styleUrls: ['./write-judicial-user-field.component.scss'],
templateUrl: './write-judicial-user-field.component.html'
})
export class WriteJudicialUserFieldComponent extends WriteComplexFieldComponent implements OnInit {
export class WriteJudicialUserFieldComponent extends WriteComplexFieldComponent implements OnInit, OnDestroy {

public readonly minSearchCharacters = 2;

Expand All @@ -27,6 +27,7 @@ export class WriteJudicialUserFieldComponent extends WriteComplexFieldComponent
public errors: ValidationErrors;
public invalidSearchTerm = false;
public judicialUserSelected = false;
public jurisdictionSubscription: Subscription;

constructor(private readonly jurisdictionService: JurisdictionService,
private readonly sessionStorageService: SessionStorageService,
Expand Down Expand Up @@ -106,6 +107,16 @@ export class WriteJudicialUserFieldComponent extends WriteComplexFieldComponent
const caseInfo = JSON.parse(caseInfoStr);
this.jurisdiction = caseInfo?.jurisdiction;
this.caseType = caseInfo?.caseType;
} else {
// If there is no case info, attempt to get the current jurisdiction and case type via the JurisdictionService
this.jurisdictionSubscription = this.jurisdictionService.selectedJurisdictionBS.subscribe((jurisdiction) => {
if (jurisdiction) {
this.jurisdiction = jurisdiction.id;
if (jurisdiction.currentCaseType) {
this.caseType = jurisdiction.currentCaseType.id;
}
}
});
}
}

Expand Down Expand Up @@ -155,4 +166,8 @@ export class WriteJudicialUserFieldComponent extends WriteComplexFieldComponent
this.judicialUserControl.setValidators(Validators.required);
}
}

public ngOnDestroy(): void {
this.jurisdictionSubscription?.unsubscribe();
}
}
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
import { Component, CUSTOM_ELEMENTS_SCHEMA, DebugElement, Input } from '@angular/core';
import { ComponentFixture, TestBed, waitForAsync } from '@angular/core/testing';
import { ComponentFixture, fakeAsync, TestBed, tick, waitForAsync } from '@angular/core/testing';
import { FormControl, FormGroup, FormsModule, ReactiveFormsModule } from '@angular/forms';
import { By } from '@angular/platform-browser';
import { ActivatedRoute, Router } from '@angular/router';
Expand Down Expand Up @@ -193,7 +193,7 @@ describe('Clear localStorage for workbasket filters', () => {
httpService = createSpyObj<HttpService>('httpService', ['get', 'post']);
jurisdictionService = new JurisdictionService(httpService);
windowMockService = createSpyObj<WindowService>('windowService', ['clearLocalStorage', 'locationAssign',
'getLocalStorage', 'removeLocalStorage']);
'getLocalStorage', 'removeLocalStorage', 'setLocalStorage']);
resetCaseTypes(JURISDICTION_2, CASE_TYPES_2);
activatedRoute = {
queryParams: of({}),
Expand Down Expand Up @@ -442,7 +442,6 @@ describe('with defaults', () => {
}));

it('should submit filters when defaults could be selected, preserving the alerts', () => {

expect(workbasketHandler.applyFilters).toHaveBeenCalledWith({
selected: {
jurisdiction: JURISDICTION_2,
Expand Down Expand Up @@ -508,7 +507,7 @@ describe('with defaults', () => {
expect(component.workbasketInputsReady).toBeFalsy();
});

it('should have form group details added when apply button is clicked ', () => {
it('should have form group details added when apply button is clicked', () => {
component.selected.jurisdiction = JURISDICTION_2;
component.apply(true);

Expand Down Expand Up @@ -644,6 +643,31 @@ describe('with defaults', () => {
});
});

it('should remove any "_judicialUserControl"-suffixed FormControl values from the FormGroup value to be stored locally', () => {
const control = new FormControl('test');
const judicialUserControl = new FormControl('judicialUser1');
const formControls = {
name: control,
j1_judicialUserControl: judicialUserControl
};
const formGroup = new FormGroup(formControls);
component.formGroup = formGroup;
component.selected.jurisdiction = JURISDICTION_2;
component.selected.caseType = CASE_TYPES_2[2];
component.selected.caseState = DEFAULT_CASE_STATE;

workbasketHandler.applyFilters.calls.reset();

const button = de.query(By.css('button'));
button.nativeElement.click();

fixture.detectChanges();
// The "j1_judicialUserControl" property is expected to have been removed, leaving just the "name" property
// Need to check the second call to windowService.setLocalStorage(); the first one is for "savedQueryParams"
expect(windowService.setLocalStorage.calls.argsFor(1)).toEqual(
['workbasket-filter-form-group-value', JSON.stringify({ name: 'test' })]);
});

it('should update form group filters', () => {
const formGroupLocalStorage = {
regionList: 'london',
Expand All @@ -667,6 +691,15 @@ describe('with defaults', () => {
expect(component.formGroup.get('londonFRCList').value).toBe(null);
expect(component.formGroup.get('londonCourtList').value).toBe(null);
});

it('should announce the selected jurisdiction and case type via the JurisdictionService when filters are applied', () => {
spyOn(jurisdictionService, 'announceSelectedJurisdiction');
component.selected.jurisdiction = JURISDICTION_2;
component.selected.caseType = CASE_TYPES_2[0];
component.apply(false);
expect(component.selected.jurisdiction.currentCaseType).toEqual(CASE_TYPES_2[0]);
expect(jurisdictionService.announceSelectedJurisdiction).toHaveBeenCalledWith(component.selected.jurisdiction);
});
});

describe('with defaults and CRUD', () => {
Expand Down Expand Up @@ -1148,7 +1181,7 @@ describe('with no defaults', () => {
component = fixture.componentInstance;

component.jurisdictions = [
JURISDICTION_ONE,
JURISDICTION_ONE
];
component.formGroup = TEST_FORM_GROUP;
component.defaults = {};
Expand Down Expand Up @@ -1244,24 +1277,44 @@ describe('with no defaults', () => {
});
});

it('should remove localStorage and clear selected fields once reset button is clicked', async () => {
it('should remove localStorage and clear selected fields once reset button is clicked', fakeAsync(() => {
// Set some initial values for the jurisdiction, case type and case state
component.selected.jurisdiction = JURISDICTION_ONE;
component.onJurisdictionIdChange();
component.selected.caseType = CASE_TYPES_1[0];
component.onCaseTypeIdChange();
component.selected.caseState = CASE_TYPES_1[0].states[0];
fixture.detectChanges();
tick();
fixture.detectChanges();
let selector = de.query(By.css('#wb-jurisdiction'));
expect(selector.nativeElement.selectedIndex).toEqual(1);
selector = de.query(By.css('#wb-case-type'));
expect(selector.nativeElement.selectedIndex).toEqual(1);
selector = de.query(By.css('#wb-case-state'));
expect(selector.nativeElement.selectedIndex).toEqual(1);

spyOn(component, 'apply').and.callThrough();
component.reset();
// Use same time interval as the component does for setTimeout() in the reset() function
tick(500);
fixture.detectChanges();

await fixture
.whenStable()
.then(() => {
let selector = de.query(By.css('#wb-jurisdiction'));
expect(selector.nativeElement.selectedIndex).toEqual(0);
expect(selector.children[0].nativeElement.textContent).toEqual(SELECT_A_VALUE);
selector = de.query(By.css('#wb-case-type'));
expect(selector.nativeElement.selectedIndex).toEqual(-1);
selector = de.query(By.css('#wb-case-state'));
expect(selector.nativeElement.selectedIndex).toEqual(-1);
});
selector = de.query(By.css('#wb-jurisdiction'));
// Jurisdiction selection is left unchanged
expect(selector.nativeElement.selectedIndex).toEqual(1);
expect(selector.children[0].nativeElement.textContent).toEqual(SELECT_A_VALUE);
expect(selector.children[1].nativeElement.textContent).toEqual(JURISDICTION_ONE.name);
selector = de.query(By.css('#wb-case-type'));
expect(selector.nativeElement.selectedIndex).toEqual(-1);
selector = de.query(By.css('#wb-case-state'));
expect(selector.nativeElement.selectedIndex).toEqual(-1);

expect(windowService.removeLocalStorage).toHaveBeenCalled();
});
expect(windowService.removeLocalStorage).toHaveBeenCalledWith('workbasket-filter-form-group-value');
expect(windowService.removeLocalStorage).toHaveBeenCalledWith('savedQueryParams');
expect(component.apply).toHaveBeenCalledWith(true);
expect(windowService.setLocalStorage).toHaveBeenCalledWith('savedQueryParams', jasmine.any(String));
}));
});


Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -82,7 +82,7 @@ export class WorkbasketFiltersComponent implements OnInit {
});
}

public apply(init): void {
public apply(init: boolean): void {
// Save filters as query parameters for current route
const queryParams = {};
if (this.selected.jurisdiction) {
Expand Down Expand Up @@ -114,9 +114,21 @@ export class WorkbasketFiltersComponent implements OnInit {
if (init) {
this.windowService.setLocalStorage(SAVED_QUERY_PARAM_LOC_STORAGE, JSON.stringify(queryParams));
if (Object.keys(this.formGroup.controls).length > 0) {
// Find all "special case" JudicialUser FormControl keys and remove the corresponding values from the
// FormGroup value because these values are not intended to be stored and subsequently passed as query string
// parameters when calling searchCases API endpoint
const judicialUserControlValuesToRemove =
Object.keys(this.formGroup.controls).filter((key) => key.endsWith('_judicialUserControl'));
judicialUserControlValuesToRemove.forEach((controlKey) => delete this.formGroup.value[controlKey]);
this.windowService.setLocalStorage(FORM_GROUP_VAL_LOC_STORAGE, JSON.stringify(this.formGroup.value));
}
}
// Announce selected jurisdiction via JurisdictionService
if (this.selected.jurisdiction) {
// Set the selected case type as the current case type of the selected jurisdiction
this.selected.jurisdiction.currentCaseType = this.selected.caseType;
this.jurisdictionService.announceSelectedJurisdiction(this.selected.jurisdiction);
}
// Apply filters
this.onApply.emit({selected: this.selected, queryParams});
this.setFocusToTop();
Expand Down

0 comments on commit 9b94f98

Please sign in to comment.