Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat: add option to allow overwriting state values from query param population #1260

Merged
merged 8 commits into from
Jun 5, 2024
95 changes: 65 additions & 30 deletions designer/client/field-edit.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,7 @@ export function FieldEdit({
required = true,
exposeToContext = false,
allowPrePopulation = false,
allowPrePopulationOverwrite = false,
disableChangingFromSummary = false,
} = options;
const isFileUploadField = selectedComponent.type === "FileUploadField";
Expand Down Expand Up @@ -228,37 +229,71 @@ export function FieldEdit({
</div>
</div>
{isListField && (
<div className="govuk-checkboxes govuk-form-group">
<div className="govuk-checkboxes__item">
<input
type="checkbox"
id="field-options-allow-pre-population"
className={`govuk-checkboxes__input`}
name="options.allowPrePopulation"
checked={allowPrePopulation}
onChange={(e) =>
dispatch({
type: Actions.EDIT_OPTIONS_ALLOW_PRE_POPULATION,
payload: e.target.checked,
})
}
/>
<label
className="govuk-label govuk-checkboxes__label"
htmlFor="field-options-allow-pre-population"
>
{i18n("common.allowPrePopulationOption.title", {
component:
ComponentTypes.find(
(componentType) => componentType.name === type
)?.title ?? "",
})}
</label>
<span className="govuk-hint govuk-checkboxes__hint">
{i18n("common.allowPrePopulationOption.helpText")}
</span>
<>
<div className="govuk-checkboxes govuk-form-group">
<div className="govuk-checkboxes__item">
<input
type="checkbox"
id="field-options-allow-pre-population"
className={`govuk-checkboxes__input`}
name="options.allowPrePopulation"
checked={allowPrePopulation}
onChange={(e) =>
dispatch({
type: Actions.EDIT_OPTIONS_ALLOW_PRE_POPULATION,
payload: e.target.checked,
})
}
/>
<label
className="govuk-label govuk-checkboxes__label"
htmlFor="field-options-allow-pre-population"
>
{i18n("common.allowPrePopulationOption.title", {
component:
ComponentTypes.find(
(componentType) => componentType.name === type
)?.title ?? "",
})}
</label>
<span className="govuk-hint govuk-checkboxes__hint">
{i18n("common.allowPrePopulationOption.helpText")}
</span>
</div>
</div>
</div>
<div className="govuk-checkboxes govuk-form-group">
<div className="govuk-checkboxes__item">
<input
type="checkbox"
id="field-options-allow-overwrite-from-query-param"
className={`govuk-checkboxes__input`}
name="options.allowPrePopulationOverwrite"
checked={allowPrePopulationOverwrite}
onChange={(e) =>
dispatch({
type:
Actions.EDIT_OPTIONS_ALLOW_OVERWRITE_FROM_QUERY_PARAM,
payload: e.target.checked,
})
}
/>
<label
className="govuk-label govuk-checkboxes__label"
htmlFor="field-options-allow-pre-population"
>
{i18n("common.allowPrePopulationOverwriteOption.title", {
component:
ComponentTypes.find(
(componentType) => componentType.name === type
)?.title ?? "",
})}
</label>
<span className="govuk-hint govuk-checkboxes__hint">
{i18n("common.allowPrePopulationOverwriteOption.helpText")}
</span>
</div>
</div>
</>
)}
<div
className="govuk-checkboxes govuk-form-group"
Expand Down
4 changes: 4 additions & 0 deletions designer/client/i18n/translations/en.translation.json
Original file line number Diff line number Diff line change
Expand Up @@ -77,6 +77,10 @@
"helpText": "Tick this box if you want this field to be available to be pre-populated by query parameter",
"title": "Allow pre-population"
},
"allowPrePopulationOverwriteOption": {
"helpText": "Tick this box if you want query parameters to override state values for this field when passed in. This will only have an effect if 'Allow pre-population' is also ticked",
"title": "Allow overwriting from query parameter"
},
"disableChangingFromSummaryOption": {
"helpText": "Tick this box if you want the change button to be removed for this field on the summary screen",
"title": "Disable changing from summary"
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -151,6 +151,14 @@ export function optionsReducer(state, action: OptionsActions) {
options: { ...options, allowPrePopulation: payload },
},
};
case Options.EDIT_OPTIONS_ALLOW_PRE_POPULATION_OVERWRITE:
return {
...state,
selectedComponent: {
...selectedComponent,
options: { ...options, allowPrePopulationOverwrite: payload },
},
};
case Options.EDIT_OPTIONS_DISABLE_CHANGING_FROM_SUMMARY:
return {
...state,
Expand Down
1 change: 1 addition & 0 deletions designer/client/reducers/component/types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -54,6 +54,7 @@ export enum Options {
EDIT_OPTIONS_SUFFIX = "EDIT_OPTIONS_SUFFIX",
EDIT_OPTIONS_EXPOSE_TO_CONTEXT = "EDIT_OPTIONS_EXPOSE_TO_CONTEXT",
EDIT_OPTIONS_ALLOW_PRE_POPULATION = "EDIT_OPTIONS_ALLOW_PRE_POPULATION",
EDIT_OPTIONS_ALLOW_PRE_POPULATION_OVERWRITE = "EDIT_OPTIONS_ALLOW_PRE_POPULATION_OVERWRITE",
EDIT_OPTIONS_DISABLE_CHANGING_FROM_SUMMARY = "EDIT_OPTIONS_DISABLE_CHANGING_FROM_SUMMARY",
}

Expand Down
1 change: 1 addition & 0 deletions model/src/components/types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -127,6 +127,7 @@ interface ListFieldBase {
bold?: boolean;
exposeToContext?: boolean;
allowPrePopulation?: boolean;
allowPrePopulationOverwrite?: boolean;
disableChangingFromSummary?: boolean;
customValidationMessages?: Record<string, string>;
};
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -71,7 +71,18 @@ export class ComponentCollection {
getPrePopulatedItems() {
return this.formItems
.filter((item) => item.options?.allowPrePopulation)
.map((item) => item.getStateSchemaKeys())
.map((item) => {
// to access the schema we need to use the component name to retrieve the value from getStateSchemaKeys
const schema = item.getStateSchemaKeys()[item.name];

return {
[item.name]: {
schema,
allowPrePopulationOverwrite:
item.options.allowPrePopulationOverwrite,
},
};
})
.reduce((acc, curr) => merge(acc, curr), {});
}

Expand Down
9 changes: 7 additions & 2 deletions runner/src/server/plugins/engine/helpers.ts
Original file line number Diff line number Diff line change
Expand Up @@ -89,11 +89,16 @@ export function getValidStateFromQueryParameters(
) {
return Object.entries(queryParameters).reduce<Record<string, any>>(
(acc, [key, value]) => {
if (reach(prePopFields, key) === undefined || reach(state, key)) {
const prePopField = reach(prePopFields, key);
const stateValue = reach(state, key);
if (
!prePopField ||
(stateValue && !prePopField.allowPrePopulationOverwrite)
) {
return acc;
}

const result = reach(prePopFields, key).validate(value);
const result = prePopField.schema.validate(value);
if (result.error) {
return acc;
}
Expand Down
35 changes: 31 additions & 4 deletions runner/test/cases/server/plugins/engine/helpers.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -307,7 +307,9 @@ suite("Helpers", () => {
aBadQueryParam: "A value",
};
const prePopFields = {
eggType: Joi.string().required(),
eggType: {
schema: Joi.string().required(),
},
};
expect(
Object.keys(getValidStateFromQueryParameters(prePopFields, query))
Expand All @@ -321,7 +323,9 @@ suite("Helpers", () => {
eggType: "Hard boiled",
};
const prePopFields = {
eggType: Joi.string().required(),
eggType: {
schema: Joi.string().required(),
},
};
const state = {
eggType: "Fried",
Expand All @@ -334,13 +338,34 @@ suite("Helpers", () => {
).to.equal(0);
});

test("Should allow the value to be overwritten if allowPrePopulationOverwrite is true", () => {
const query = {
eggType: "Hard boiled",
};
const prePopFields = {
eggType: {
schema: Joi.string().required(),
allowPrePopulationOverwrite: true,
},
};
const state = {
eggType: "Fried",
};

expect(
getValidStateFromQueryParameters(prePopFields, query, state)
).to.equal({ eggType: "Hard boiled" });
});

test("Should be able to update nested object values", () => {
const query = {
"yourEggs.eggType": "Fried egg",
};
const prePopFields = {
yourEggs: {
eggType: Joi.string().required(),
eggType: {
schema: Joi.string().required(),
},
},
};
expect(
Expand All @@ -354,7 +379,9 @@ suite("Helpers", () => {
};
const prePopFields = {
yourEggs: {
eggType: Joi.string().valid("boiled", "fried", "poached"),
eggType: {
schema: Joi.string().valid("boiled", "fried", "poached"),
},
},
};
expect(
Expand Down
Loading