Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
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": "7.2.57",
"version": "7.2.57-exui-3582-rc1",
"engines": {
"node": ">=18.19.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": "7.2.57",
"version": "7.2.57-exui-3582-rc1",
"engines": {
"node": ">=18.19.0"
},
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -738,6 +738,12 @@ export class CaseEditPageComponent implements OnInit, AfterViewChecked, OnDestro
this.formValueService.removeUnnecessaryFields(caseEventData.data, caseFields, clearEmpty, clearNonCase,
fromPreviousPage, this.currentPage.case_fields);

// removeHiddenFields while are hidden in the UI
// Only remove hidden fields if editForm and its controls are available
if (this.editForm?.controls?.['data']?.['controls']) {
this.formValueService.removeHiddenField(caseEventData.data, caseFields, clearNonCase, this.editForm.controls['data']['controls']);
}

return caseEventData;
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -295,6 +295,7 @@ describe('CaseEditComponent', () => {
mockabstractConfig = createSpyObj<AbstractAppConfig>('AbstractAppConfig', ['logMessage']);
spyOn(validPageListCaseFieldsService, 'deleteNonValidatedFields');
spyOn(validPageListCaseFieldsService, 'validPageListCaseFields');
formValueService.removeHiddenField = jasmine.createSpy('removeHiddenField').and.callFake((...args: any[]) => {});

route = {
queryParams: of({ Origin: 'viewDraft' }),
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -330,6 +330,12 @@ export class CaseEditComponent implements OnInit, OnDestroy {
this.formValueService.removeUnnecessaryFields(caseEventData.data, pageListCaseFields, true, true);
}

// removeHiddenFields while are hidden in the UI
// Only remove hidden fields if editForm and its controls are available
if (form?.controls?.['data']?.['controls']) {
this.formValueService.removeHiddenField(caseEventData.data, pageListCaseFields, true, form.controls['data']['controls']);
}

caseEventData.event_token = eventTrigger.event_token;
caseEventData.ignore_warning = this.ignoreWarning;
if (this.confirmation) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -1075,5 +1075,220 @@ describe('FormValueService', () => {
dollar: '$'
});
});

describe('removeHiddenField', () => {
let formControls: any;
let caseFields: CaseField[];
let data: any;

beforeEach(() => {
formControls = {};
caseFields = [];
data = {};
});

it('should set field to null if hidden and not retain_hidden_value', () => {
data = { field1: 'value1', field2: 'value2' };
caseFields = [
{ id: 'field1', display_context: 'MANDATORY', hidden: false, retain_hidden_value: false, field_type: { type: 'Text' } } as any,
{ id: 'field2', display_context: 'MANDATORY', hidden: false, retain_hidden_value: false, field_type: { type: 'Text' } } as any
];
formControls = {
field1: { caseField: { hidden: true } },
field2: { caseField: { hidden: false } }
};
formValueService.removeHiddenField(data, caseFields, true, formControls);
expect(data.field1).toBeNull();
expect(data.field2).toBe('value2');
});

it('should not set field to null if retain_hidden_value is true', () => {
data = { field1: 'value1' };
caseFields = [
{ id: 'field1', display_context: 'MANDATORY', hidden: false, retain_hidden_value: true, field_type: { type: 'Text' } } as any
];
formControls = {
field1: { caseField: { hidden: true } }
};
formValueService.removeHiddenField(data, caseFields, true, formControls);
expect(data.field1).toBe('value1');
});

it('should not touch readonly fields', () => {
spyOn(FormValueService as any, 'isReadOnly').and.returnValue(true);
data = { field1: 'value1' };
caseFields = [
{ id: 'field1', display_context: 'READONLY', hidden: false, field_type: { type: 'Text' } } as any
];
formControls = {
field1: { caseField: { hidden: true } }
};
formValueService.removeHiddenField(data, caseFields, true, formControls);
expect(data.field1).toBe('value1');
});

it('should skip if formControls[field.id] is undefined', () => {
data = { field1: 'value1' };
caseFields = [
{ id: 'field1', display_context: 'MANDATORY', hidden: false, field_type: { type: 'Text' } } as any
];
formControls = {};
formValueService.removeHiddenField(data, caseFields, true, formControls);
expect(data.field1).toBe('value1');
});

it('should skip if field is hidden', () => {
data = { field1: 'value1' };
caseFields = [
{ id: 'field1', display_context: 'MANDATORY', hidden: true, field_type: { type: 'Text' } } as any
];
formControls = {
field1: { caseField: { hidden: true } }
};
formValueService.removeHiddenField(data, caseFields, true, formControls);
expect(data.field1).toBe('value1');
});

it('should recurse into complex fields', () => {
data = {
complex1: {
sub1: 'val1',
sub2: 'val2'
}
};
caseFields = [
{
id: 'complex1',
display_context: 'MANDATORY',
hidden: false,
field_type: {
type: 'Complex',
complex_fields: [
{ id: 'sub1', display_context: 'MANDATORY', hidden: false, field_type: { type: 'Text' } },
{ id: 'sub2', display_context: 'MANDATORY', hidden: false, field_type: { type: 'Text' } }
]
}
} as any
];
formControls = {
complex1: {
caseField: { hidden: false },
controls: {
sub1: { caseField: { hidden: true } },
sub2: { caseField: { hidden: false } }
}
}
};
formValueService.removeHiddenField(data, caseFields, true, formControls);
expect(data.complex1.sub1).toBeNull();
expect(data.complex1.sub2).toBe('val2');
});

it('should recurse into collection of complex fields', () => {
data = {
collection1: [
{ value: { sub1: 'a', sub2: 'b' } },
{ value: { sub1: 'c', sub2: 'd' } }
]
};
caseFields = [
{
id: 'collection1',
display_context: 'MANDATORY',
hidden: false,
field_type: {
type: 'Collection',
collection_field_type: {
type: 'Complex',
complex_fields: [
{ id: 'sub1', display_context: 'MANDATORY', hidden: false, field_type: { type: 'Text' } },
{ id: 'sub2', display_context: 'MANDATORY', hidden: false, field_type: { type: 'Text' } }
]
}
}
} as any
];
formControls = {
collection1: {
caseField: { hidden: false },
controls: [
{
controls: {
value: {
caseField: {
field_type: {
complex_fields: [
{ id: 'sub1', display_context: 'MANDATORY', hidden: false, field_type: { type: 'Text' } },
{ id: 'sub2', display_context: 'MANDATORY', hidden: false, field_type: { type: 'Text' } }
]
}
},
controls: {
sub1: { caseField: { hidden: true } },
sub2: { caseField: { hidden: false } }
}
}
}
},
{
controls: {
value: {
caseField: {
field_type: {
complex_fields: [
{ id: 'sub1', display_context: 'MANDATORY', hidden: false, field_type: { type: 'Text' } },
{ id: 'sub2', display_context: 'MANDATORY', hidden: false, field_type: { type: 'Text' } }
]
}
},
controls: {
sub1: { caseField: { hidden: false } },
sub2: { caseField: { hidden: true } }
}
}
}
}
]
}
};
formValueService.removeHiddenField(data, caseFields, true, formControls);
expect(data.collection1[0].value.sub1).toBeNull();
expect(data.collection1[0].value.sub2).toBe('b');
expect(data.collection1[1].value.sub1).toBe('c');
expect(data.collection1[1].value.sub2).toBeNull();
});

it('should do nothing if clearNonCase is false', () => {
data = { field1: 'value1' };
caseFields = [
{ id: 'field1', display_context: 'MANDATORY', hidden: false, field_type: { type: 'Text' } } as any
];
formControls = {
field1: { caseField: { hidden: true } }
};
formValueService.removeHiddenField(data, caseFields, false, formControls);
expect(data.field1).toBe('value1');
});

it('should skip if data is null', () => {
data = null;
caseFields = [
{ id: 'field1', display_context: 'MANDATORY', hidden: false, field_type: { type: 'Text' } } as any
];
formControls = {
field1: { caseField: { hidden: true } }
};
formValueService.removeHiddenField(data, caseFields, true, formControls);
expect(data).toBeNull();
});

it('should skip if caseFields is empty', () => {
data = { field1: 'value1' };
caseFields = [];
formControls = {};
formValueService.removeHiddenField(data, caseFields, true, formControls);
expect(data.field1).toBe('value1');
});
});
});
});
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
import { Injectable } from '@angular/core';

import { AbstractControl } from '@angular/forms';
import { CaseField, FieldTypeEnum } from '../../domain';
import { FieldsUtils } from '../fields';
import { FieldTypeSanitiser } from './field-type-sanitiser';
Expand Down Expand Up @@ -365,6 +365,64 @@ export class FormValueService {
}
}
}
// exui-3582 When a form field becomes hidden based on user’s input in the event journey,
// its stored value must be cleared and it must not be submitted or persisted.
public removeHiddenField(data: object, caseFields: CaseField[], clearNonCase: boolean, formControls: { [key: string]: AbstractControl }): void {
if (clearNonCase && data && caseFields && caseFields.length > 0) {
for (const field of caseFields) {
if (!FormValueService.isLabel(field) && FormValueService.isReadOnly(field)) {
// Retain anything that is readonly and not a label.
continue;
}
// Check if formControls[field.id] exists before accessing its properties
const caseField = formControls[field.id] ? formControls[field.id]['caseField'] as CaseField : undefined;
if (caseField === undefined || field.hidden === true) {
continue;
}

const hasValue = data.hasOwnProperty(field.id) && data[field.id] != null &&
(typeof data[field.id] !== 'object' || Object.keys(data[field.id]).length > 0);

if (
caseField?.hidden === true &&
field.display_context !== 'HIDDEN' &&
field.display_context !== 'HIDDEN_TEMP' &&
!field.retain_hidden_value &&
field.id !== 'caseLinks' &&
hasValue
) {
data[field.id] = null;
continue; // If field is now hidden, skip checking its children
}
if (field.field_type) {
switch (field.field_type.type) {
case 'Complex':
const complexData = data[field.id] ?? data['value'];
if (complexData && formControls[field.id] && formControls[field.id]['controls']) {
this.removeHiddenField(complexData, field.field_type.complex_fields, clearNonCase, formControls[field.id]['controls']);
}
break;
case 'Collection':
const collection = data[field.id];
if (collection && Array.isArray(collection) && field.field_type.collection_field_type.type === 'Complex') {
collection.forEach((item, index) => {
if (formControls[field.id] && formControls[field.id]['controls'] && formControls[field.id]['controls'][index]) {
const itemControls = formControls[field.id]?.['controls']?.[index]?.['controls']?.['value'];
const collectionData = item['value'] ?? item;
if (collectionData && itemControls?.['controls']) {
this.removeHiddenField(collectionData, field.field_type.collection_field_type.complex_fields, clearNonCase, itemControls['controls']);
}
}
});
}
break;
default:
break;
}
}
}
}
}

/**
* Remove any empty collection fields where a value of greater than zero is specified in the field's {@link FieldType}
Expand Down