Skip to content

Commit

Permalink
Merge pull request #3095 from ONSdigital/EAR-2282-repeating-title
Browse files Browse the repository at this point in the history
Ear 2282 repeating title
  • Loading branch information
martyncolmer authored Jan 29, 2024
2 parents 3342156 + c6b03e0 commit a1f4c31
Show file tree
Hide file tree
Showing 12 changed files with 152 additions and 53 deletions.
13 changes: 13 additions & 0 deletions eq-author-api/src/businessLogic/onAnswerDeleted.js
Original file line number Diff line number Diff line change
Expand Up @@ -81,11 +81,24 @@ const removeAnswerFromPiping = (ctx, deletedAnswer, pages) => {
deletedAnswer.id,
"Deleted answer"
);

page.answers?.forEach((answer) => {
answer.label = updatePipingValue(
answer.label,
deletedAnswer.id,
"Deleted answer"
);
});
});

const sections = getSections(ctx);

sections.forEach((section) => {
section.title = updatePipingValue(
section.title,
deletedAnswer.id,
"Deleted answer"
);
section.introductionTitle = updatePipingValue(
section.introductionTitle,
deletedAnswer.id,
Expand Down
30 changes: 30 additions & 0 deletions eq-author-api/src/businessLogic/onAnswerUpdated.js
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ const cheerio = require("cheerio");
const {
getListById,
getSupplementaryDataAsCollectionListById,
getSections,
} = require("../../schema/resolvers/utils");

const updatePipingValue = (htmlText, answerId, newValue) => {
Expand All @@ -17,6 +18,34 @@ const updatePipingValue = (htmlText, answerId, newValue) => {
return htmlDoc.html();
};

const updatePipingInSections = (ctx, updatedAnswer) => {
const sections = getSections(ctx);

sections.forEach((section) => {
if (section.title?.includes(updatedAnswer.id)) {
section.title = updatePipingValue(
section.title,
updatedAnswer.id,
updatedAnswer.label.replace(/(<([^>]+)>)/gi, "")
);
}
if (section.introductionTitle?.includes(updatedAnswer.id)) {
section.introductionTitle = updatePipingValue(
section.introductionTitle,
updatedAnswer.id,
updatedAnswer.label.replace(/(<([^>]+)>)/gi, "")
);
}
if (section.introductionContent?.includes(updatedAnswer.id)) {
section.introductionContent = updatePipingValue(
section.introductionContent,
updatedAnswer.id,
updatedAnswer.label.replace(/(<([^>]+)>)/gi, "")
);
}
});
};

const updatePipingInAnswers = (updatedAnswer, pages) => {
if (updatedAnswer.label) {
pages.forEach((page) => {
Expand Down Expand Up @@ -102,4 +131,5 @@ const updatePipingRepeatingAnswer = (ctx, updatedAnswer, oldAnswer) => {
module.exports = (ctx, updatedAnswer, pages, oldAnswer) => {
updatePipingInAnswers(updatedAnswer, pages);
updatePipingRepeatingAnswer(ctx, updatedAnswer, oldAnswer);
updatePipingInSections(ctx, updatedAnswer);
};
10 changes: 10 additions & 0 deletions eq-author-api/src/businessLogic/onSectionUpdated.js
Original file line number Diff line number Diff line change
Expand Up @@ -38,6 +38,11 @@ const deletePiping = (answers, section, pages) => {
answer.id,
"Deleted answer"
);
section.title = updatePipingValue(
section.title,
answer.id,
"Deleted answer"
);
});
};

Expand All @@ -54,6 +59,11 @@ const updatePiping = (answers, section, pages) => {
answer.id,
answer.label || "Untitled answer"
);
section.title = updatePipingValue(
section.title,
answer.id,
answer.label || "Untitled answer"
);
});
};

Expand Down
6 changes: 6 additions & 0 deletions eq-author-api/src/validation/schemas/section.json
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,12 @@
},
{
"requiredWhenSectionSetting": "sectionSummary"
},
{
"validatePipingAnswerInTitle": true
},
{
"validatePipingMetadataInTitle": true
}
],
"errorMessage": "ERR_REQUIRED_WHEN_SETTING"
Expand Down
2 changes: 2 additions & 0 deletions eq-author/src/App/collectionLists/collectionListsPage.js
Original file line number Diff line number Diff line change
Expand Up @@ -110,6 +110,7 @@ const CollectionListsPage = ({
const handleDeleteList = (id) => () => {
deleteList({
variables: { input: { id: id } },
refetchQueries: ["GetQuestionnaire"],
});
};

Expand All @@ -127,6 +128,7 @@ const CollectionListsPage = ({
const handleDeleteAnswer = (answerId) => {
deleteAnswer({
variables: { input: { id: answerId } },
refetchQueries: ["GetQuestionnaire"],
});
};

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,6 @@ import { shallow } from "enzyme";

import { SectionEditor } from "App/section/Design/SectionEditor";
import RichTextEditor from "components/RichTextEditor";
import { sectionErrors } from "constants/validationMessages";
import suppressConsoleMessage from "tests/utils/supressConsol";

/*
Expand Down Expand Up @@ -96,7 +95,6 @@ describe("SectionEditor", () => {
onCloseDeleteConfirmModal: jest.fn(),
onMoveSectionDialog: jest.fn(),
onCloseMoveSectionDialog: jest.fn(),
getValidationError: jest.fn(),
};

const render = ({ ...props }) =>
Expand Down Expand Up @@ -170,27 +168,29 @@ describe("SectionEditor", () => {
requiredCompleted: false,
showOnHub: false,
sectionSummary: true,
validationErrorInfo: {
errors: [
{
type: "section",
field: "title",
errorCode: "ERR_REQUIRED_WHEN_SETTING",
},
],
},
questionnaire: {
id: "2",
navigation: false,
hub: false,
collapsibleSummary: false,
},
};
const getValidationError = jest.fn().mockReturnValue("Validation error");

const wrapper = render({ section, getValidationError });

const wrapper = render({ section });
expect(
wrapper
.find("[testSelector='txt-section-title']")
.prop("errorValidationMsg")
).toBe("Validation error");

expect(getValidationError).toHaveBeenCalledWith({
field: "title",
message: sectionErrors.SECTION_TITLE_NOT_ENTERED,
});
).toEqual(["Enter a section title"]);
});

it("should not autofocus the section title when its empty and navigation has just been turned on", () => {
Expand Down Expand Up @@ -224,24 +224,26 @@ describe("SectionEditor", () => {
});

it("should show an error when there is a validation error", () => {
const getValidationError = jest.fn().mockReturnValue("Validation error");
const wrapper = render({
section: {
...section1,
title: "",
validationErrorInfo: {
errors: [
{
type: "section",
field: "title",
errorCode: "ERR_REQUIRED_WHEN_SETTING",
},
],
},
},
getValidationError,
});
expect(
wrapper
.find("[testSelector='txt-section-title']")
.prop("errorValidationMsg")
).toBe("Validation error");

expect(getValidationError).toHaveBeenCalledWith({
field: "title",
message: sectionErrors.SECTION_TITLE_NOT_ENTERED,
});
).toEqual(["Enter a section title"]);
});

describe("DeleteConfirmDialog", () => {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -25,17 +25,21 @@ exports[`SectionEditor should render 1`] = `
controls={
Object {
"emphasis": true,
"piping": true,
}
}
disabled={false}
errorValidationMsg={null}
id="section-title"
isRepeatingSection={false}
label={
<DescribedText
description="This will be shown on the hub."
>
Section title
</DescribedText>
}
listId={null}
maxHeight={12}
multiline={false}
name="title"
Expand All @@ -48,6 +52,8 @@ exports[`SectionEditor should render 1`] = `
<SectionEditor__HorizontalRule />
<SectionIntroduction
handleUpdate={[Function]}
introductionContentErrorMessage={null}
introductionTitleErrorMessage={null}
section={
Object {
"alias": "alias",
Expand Down Expand Up @@ -147,17 +153,21 @@ exports[`SectionEditor should render 2`] = `
controls={
Object {
"emphasis": true,
"piping": true,
}
}
disabled={false}
errorValidationMsg={null}
id="section-title"
isRepeatingSection={false}
label={
<DescribedText
description="This will be shown on the hub."
>
Section title
</DescribedText>
}
listId={null}
maxHeight={12}
multiline={false}
name="title"
Expand All @@ -170,6 +180,8 @@ exports[`SectionEditor should render 2`] = `
<SectionEditor__HorizontalRule />
<SectionIntroduction
handleUpdate={[Function]}
introductionContentErrorMessage={null}
introductionTitleErrorMessage={null}
section={
Object {
"alias": "alias",
Expand Down
60 changes: 31 additions & 29 deletions eq-author/src/App/section/Design/SectionEditor/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -24,10 +24,7 @@ import getIdForObject from "utils/getIdForObject";
import MoveSectionModal from "./MoveSectionModal";
import MoveSectionQuery from "./MoveSectionModal/MoveSectionQuery";
import SectionIntroduction from "./SectionIntroduction";
import {
sectionErrors,
richTextEditorErrors,
} from "constants/validationMessages";
import { sectionErrors } from "constants/validationMessages";
import {
DELETE_SECTION_TITLE,
DELETE_PAGE_WARNING,
Expand All @@ -36,6 +33,7 @@ import {

const titleControls = {
emphasis: true,
piping: true,
};

const Padding = styled.div`
Expand All @@ -55,6 +53,18 @@ const SectionCanvas = styled.div`
const hasNavigation = (section) =>
get(section, ["questionnaire", "navigation"]);

const getMultipleErrorsByField = (field, errorMessages, validationErrors) => {
const errorArray = validationErrors.filter((error) => error.field === field);
const errMsgArray = errorArray.map(
(error) => errorMessages[error?.errorCode] || error?.errorCode
);

if (!errMsgArray.length) {
return null;
}
return errMsgArray;
};

export class SectionEditor extends React.Component {
static propTypes = {
section: propType(sectionFragment),
Expand Down Expand Up @@ -164,36 +174,28 @@ export class SectionEditor extends React.Component {
size="large"
testSelector="txt-section-title"
autoFocus={autoFocusTitle}
errorValidationMsg={
section &&
this.props.getValidationError({
field: "title",
message: sectionErrors.SECTION_TITLE_NOT_ENTERED,
})
}
listId={section.repeatingSectionListId}
isRepeatingSection={section.repeatingSection}
errorValidationMsg={getMultipleErrorsByField(
"title",
sectionErrors.TITLE,
section?.validationErrorInfo?.errors
)}
/>
<HorizontalRule />
<SectionIntroduction
section={section}
handleUpdate={handleUpdate}
introductionTitleErrorMessage={
section &&
this.props.getValidationError({
field: "introductionTitle",
label: "Introduction Title",
requiredMsg: sectionErrors.SECTION_INTRO_TITLE_NOT_ENTERED,
message: richTextEditorErrors.PIPING_TITLE_DELETED.message,
})
}
introductionContentErrorMessage={
section &&
this.props.getValidationError({
field: "introductionContent",
label: "Introduction Content",
requiredMsg: sectionErrors.SECTION_INTRO_CONTENT_NOT_ENTERED,
message: richTextEditorErrors.PIPING_TITLE_DELETED.message,
})
}
introductionTitleErrorMessage={getMultipleErrorsByField(
"introductionTitle",
sectionErrors.INTRO_TITLE,
section?.validationErrorInfo?.errors
)}
introductionContentErrorMessage={getMultipleErrorsByField(
"introductionContent",
sectionErrors.INTRO_CONTENT,
section?.validationErrorInfo?.errors
)}
/>
<HorizontalRule />
<SectionSummary
Expand Down
Loading

0 comments on commit a1f4c31

Please sign in to comment.