diff --git a/eq-author-api/constants/allDataVersions.js b/eq-author-api/constants/allDataVersions.js new file mode 100644 index 0000000000..d8bc8f353f --- /dev/null +++ b/eq-author-api/constants/allDataVersions.js @@ -0,0 +1 @@ +module.exports = ["1", "3"]; diff --git a/eq-author-api/migrations/addAllowableDataVersions.js b/eq-author-api/migrations/addAllowableDataVersions.js new file mode 100644 index 0000000000..76d83d60d1 --- /dev/null +++ b/eq-author-api/migrations/addAllowableDataVersions.js @@ -0,0 +1,18 @@ +const { getOptions } = require("../schema/resolvers/utils"); + +module.exports = (questionnaire) => { + const allQuestionnaireOptions = getOptions({ questionnaire }); + + if ( + questionnaire.collectionLists?.lists?.length > 0 || + questionnaire.supplementaryData || + allQuestionnaireOptions?.some((option) => option.dynamicAnswer) + ) { + questionnaire.dataVersion = "3"; + questionnaire.allowableDataVersions = ["3"]; + } else { + questionnaire.allowableDataVersions = ["1", "3"]; + } + + return questionnaire; +}; diff --git a/eq-author-api/migrations/addAllowableDataVersions.test.js b/eq-author-api/migrations/addAllowableDataVersions.test.js new file mode 100644 index 0000000000..ad8672709a --- /dev/null +++ b/eq-author-api/migrations/addAllowableDataVersions.test.js @@ -0,0 +1,82 @@ +const addAllowableDataVersions = require("./addAllowableDataVersions"); + +describe("addAllowableDataVersions", () => { + it("should set dataVersion to 3 and add allowableDataVersions with data version 3 when questionnaire has collection list", () => { + const questionnaire = { + id: "questionnaire-1", + title: "Questionnaire 1", + collectionLists: { + lists: [{ id: "list-1" }], + }, + }; + + const updatedQuestionnaire = addAllowableDataVersions(questionnaire); + + expect(updatedQuestionnaire.dataVersion).toEqual("3"); + expect(updatedQuestionnaire.allowableDataVersions).toEqual(["3"]); + }); + + it("should set dataVersion to 3 and add allowableDataVersions with data version 3 when questionnaire has supplementary data", () => { + const questionnaire = { + id: "questionnaire-1", + title: "Questionnaire 1", + supplementaryData: { id: "supplementary-data-1" }, + }; + + const updatedQuestionnaire = addAllowableDataVersions(questionnaire); + + expect(updatedQuestionnaire.dataVersion).toEqual("3"); + expect(updatedQuestionnaire.allowableDataVersions).toEqual(["3"]); + }); + + it("should set dataVersion to 3 and add allowableDataVersions with data version 3 when questionnaire has dynamic answer", () => { + const questionnaire = { + id: "questionnaire-1", + title: "Questionnaire 1", + sections: [ + { + id: "section-1", + folders: [ + { + id: "folder-1", + pages: [ + { + id: "page-1", + answers: [ + { + id: "answer-1", + options: [ + { + id: "option-1", + dynamicAnswer: true, + }, + ], + }, + ], + }, + ], + }, + ], + }, + ], + }; + + const updatedQuestionnaire = addAllowableDataVersions(questionnaire); + + expect(updatedQuestionnaire.dataVersion).toEqual("3"); + expect(updatedQuestionnaire.allowableDataVersions).toEqual(["3"]); + }); + + it("should add allowableDataVersions with data versions 1 and 3 without updating dataVersion when questionnaire has no collection lists, supplementary data or dynamic answers", () => { + const questionnaire = { + id: "questionnaire-1", + title: "Questionnaire 1", + dataVersion: "1", + }; + + const updatedQuestionnaire = addAllowableDataVersions(questionnaire); + + expect(updatedQuestionnaire.dataVersion).toEqual("1"); + expect(updatedQuestionnaire.allowableDataVersions).toEqual(["1", "3"]); + }); +}); diff --git a/eq-author-api/migrations/index.js b/eq-author-api/migrations/index.js index 0eb6f51efe..65a48a73e3 100644 --- a/eq-author-api/migrations/index.js +++ b/eq-author-api/migrations/index.js @@ -53,6 +53,7 @@ const migrations = [ require("./addFieldsToListCollectorFolderContents"), require("./addAdditonalContentsToAddItemPage"), require("./updateHealthThemeToPandemicMonitoring"), + require("./addAllowableDataVersions"), ]; const currentVersion = migrations.length; diff --git a/eq-author-api/schema/resolvers/base.js b/eq-author-api/schema/resolvers/base.js index 3447c9865c..c77225062d 100644 --- a/eq-author-api/schema/resolvers/base.js +++ b/eq-author-api/schema/resolvers/base.js @@ -26,6 +26,7 @@ const { AWAITING_APPROVAL, UPDATES_REQUIRED, } = require("../../constants/publishStatus"); +const allDataVersions = require("../../constants/allDataVersions"); const { DURATION_LOOKUP } = require("../../constants/durationTypes"); const { @@ -149,7 +150,8 @@ const createNewQuestionnaire = (input) => { qcodes: true, navigation: false, hub: false, - dataVersion: "1", + dataVersion: "3", + allowableDataVersions: allDataVersions, createdAt: new Date(), metadata: [], sections: [createSection()], @@ -658,6 +660,7 @@ const Resolvers = { const pages = getPages(ctx); onSectionDeleted(ctx, removedSection, pages); + setDataVersion(ctx); if (!ctx.questionnaire.sections.length) { ctx.questionnaire.sections.push(createSection()); @@ -727,6 +730,7 @@ const Resolvers = { const pages = getPages(ctx); onFolderDeleted(ctx, removedFolder, pages); + setDataVersion(ctx); if (!section.folders.length) { section.folders.push(createFolder()); @@ -862,6 +866,7 @@ const Resolvers = { const deletedAnswer = first(remove(page.answers, { id: input.id })); onAnswerDeleted(ctx, page, deletedAnswer, pages); + setDataVersion(ctx); return page; }), diff --git a/eq-author-api/schema/resolvers/importing.js b/eq-author-api/schema/resolvers/importing.js index 449b27217a..d599f3b1e1 100644 --- a/eq-author-api/schema/resolvers/importing.js +++ b/eq-author-api/schema/resolvers/importing.js @@ -7,6 +7,7 @@ const { remapAllNestedIds, getSectionsByIds, } = require("./utils"); +const removeExtraSpaces = require("../../utils/removeExtraSpaces"); const createFolder = require("../../src/businessLogic/createFolder"); @@ -15,6 +16,8 @@ const { UserInputError } = require("apollo-server-express"); const { createMutation } = require("./createMutation"); +const { setDataVersion } = require("./utils"); + module.exports = { Mutation: { importQuestions: createMutation( @@ -45,6 +48,7 @@ module.exports = { } pages.forEach((page) => { + removeExtraSpaces(page); if (page.answers.length === 1) { if (page.answers[0].repeatingLabelAndInputListId) { page.answers[0].repeatingLabelAndInputListId = ""; @@ -89,6 +93,7 @@ module.exports = { ...strippedPages.map((page) => createFolder({ pages: [page] })) ); } + setDataVersion(ctx); return section; } @@ -121,13 +126,14 @@ module.exports = { ); } - let sectionsWithoutLogic = []; + const strippedSections = []; // Re-create UUIDs, strip QCodes, routing and skip conditions from imported pages // Keep piping intact for now - will show "[Deleted answer]" to users when piped ID not resolvable sourceSections.forEach((section) => { remapAllNestedIds(section); + removeExtraSpaces(section); section.displayConditions = null; section.questionnaireId = ctx.questionnaire.id; section.folders.forEach((folder) => { @@ -163,7 +169,7 @@ module.exports = { if (section.repeatingSectionListId) { section.repeatingSectionListId = ""; } - sectionsWithoutLogic.push(section); + strippedSections.push(section); }); const section = getSectionById(ctx, sectionId); @@ -173,8 +179,10 @@ module.exports = { ); } - destinationSections.splice(insertionIndex, 0, ...sectionsWithoutLogic); + destinationSections.splice(insertionIndex, 0, ...strippedSections); ctx.questionnaire.hub = ctx.questionnaire.sections.length > 1; + setDataVersion(ctx); + return destinationSections; } ), diff --git a/eq-author-api/schema/resolvers/pages/index.js b/eq-author-api/schema/resolvers/pages/index.js index 3163d5dc83..b6e1df7fa4 100644 --- a/eq-author-api/schema/resolvers/pages/index.js +++ b/eq-author-api/schema/resolvers/pages/index.js @@ -20,6 +20,7 @@ const { createQuestionPage } = require("./questionPage"); const deleteFirstPageSkipConditions = require("../../../src/businessLogic/deleteFirstPageSkipConditions"); const deleteLastPageRouting = require("../../../src/businessLogic/deleteLastPageRouting"); const onFolderDeleted = require("../../../src/businessLogic/onFolderDeleted"); +const { setDataVersion } = require("../utils/setDataVersion"); const Resolvers = {}; @@ -86,6 +87,7 @@ Resolvers.Mutation = { deleteFirstPageSkipConditions(ctx); deleteLastPageRouting(ctx); + setDataVersion(ctx); return section; }), diff --git a/eq-author-api/schema/resolvers/utils/setDataVersion.js b/eq-author-api/schema/resolvers/utils/setDataVersion.js index 86ccfe7d7e..a821fee695 100644 --- a/eq-author-api/schema/resolvers/utils/setDataVersion.js +++ b/eq-author-api/schema/resolvers/utils/setDataVersion.js @@ -1,15 +1,18 @@ const { getAnswers } = require("./answerGetters"); +const allDataVersions = require("../../../constants/allDataVersions"); const setDataVersion = ({ questionnaire }) => { - questionnaire.dataVersion = "1"; + questionnaire.allowableDataVersions = allDataVersions; if (questionnaire.collectionLists?.lists?.length) { questionnaire.dataVersion = "3"; + questionnaire.allowableDataVersions = ["3"]; return; } if (questionnaire.supplementaryData) { questionnaire.dataVersion = "3"; + questionnaire.allowableDataVersions = ["3"]; return; } @@ -18,6 +21,7 @@ const setDataVersion = ({ questionnaire }) => { return answer.options?.some((option) => { if (option.dynamicAnswer) { questionnaire.dataVersion = "3"; + questionnaire.allowableDataVersions = ["3"]; return true; } return false; diff --git a/eq-author-api/schema/tests/questionnaire.test.js b/eq-author-api/schema/tests/questionnaire.test.js index d8c1b21256..eb55a4cb58 100644 --- a/eq-author-api/schema/tests/questionnaire.test.js +++ b/eq-author-api/schema/tests/questionnaire.test.js @@ -1,4 +1,5 @@ const { last, findIndex, find } = require("lodash"); +const { CHECKBOX, RADIO } = require("../../constants/answerTypes"); jest.mock("node-fetch"); const fetch = require("node-fetch"); @@ -35,6 +36,9 @@ const { setQuestionnaireLocked, updateSubmission, } = require("../../tests/utils/contextBuilder/questionnaire"); +const { + updateOption, +} = require("../../tests/utils/contextBuilder/option/updateOption"); const { createAnswer, @@ -164,6 +168,66 @@ describe("questionnaire", () => { expect(queriedShortTitleQuestionnaire.displayName).toEqual("short title"); }); + it("should set data version to 3 and remove data version 1 from allowable data versions if questionnaire contains dynamic answers", async () => { + const ctx = await buildContext({ + supplementaryData: null, + sections: [ + { + folders: [ + { + pages: [ + { + answers: [ + { + type: CHECKBOX, + options: [ + { + label: "checkbox-option-1", + }, + ], + }, + ], + }, + { + answers: [ + { + type: RADIO, + options: [ + { + id: "radio-option-1", + }, + ], + }, + ], + }, + ], + }, + ], + }, + ], + }); + + expect(ctx.questionnaire.dataVersion).toEqual("3"); + expect(ctx.questionnaire.allowableDataVersions).toEqual(["1", "3"]); + + const option = + ctx.questionnaire.sections[0].folders[0].pages[1].answers[0].options[0]; + + const update = { + id: option.id, + label: "Dynamic option 1", + description: "Dynamic option description", + value: "dynamic-option-1", + qCode: "dynamic-option-1", + dynamicAnswer: true, + }; + + updateOption(ctx, update); + + expect(ctx.questionnaire.dataVersion).toEqual("3"); + expect(ctx.questionnaire.allowableDataVersions).toEqual(["3"]); + }); + describe("starring", () => { it("should throw user input error if questionnaire ID doesn't exist", () => { expect( diff --git a/eq-author-api/schema/typeDefs.js b/eq-author-api/schema/typeDefs.js index 8c348f761b..f77ab93712 100644 --- a/eq-author-api/schema/typeDefs.js +++ b/eq-author-api/schema/typeDefs.js @@ -62,6 +62,7 @@ type Questionnaire { navigation: Boolean hub: Boolean dataVersion: String + allowableDataVersions: [String] createdAt: DateTime updatedAt: DateTime createdBy: User! @@ -1311,6 +1312,7 @@ input UpdateQuestionnaireInput { editors: [ID!] isPublic: Boolean permission: String + dataVersion: String } diff --git a/eq-author-api/tests/utils/contextBuilder/index.js b/eq-author-api/tests/utils/contextBuilder/index.js index 42306040c3..2d405d7eac 100644 --- a/eq-author-api/tests/utils/contextBuilder/index.js +++ b/eq-author-api/tests/utils/contextBuilder/index.js @@ -73,7 +73,8 @@ const buildContext = async (questionnaireConfig, userConfig = {}) => { const { questionnaire } = ctx; ctx.questionnaire.sections = []; ctx.comments = comments || {}; - ctx.questionnaire.supplementaryData = supplementaryData || {}; + ctx.questionnaire.supplementaryData = + supplementaryData === null ? null : supplementaryData || {}; if (Array.isArray(sections)) { for (let section of sections) { diff --git a/eq-author-api/utils/removeExtraSpaces.js b/eq-author-api/utils/removeExtraSpaces.js new file mode 100644 index 0000000000..9675b491dd --- /dev/null +++ b/eq-author-api/utils/removeExtraSpaces.js @@ -0,0 +1,36 @@ +const removeExtraSpaces = (inputData) => { + // Does not remove extra spaces from inputData if inputData is null or undefined + if (inputData !== null && inputData !== undefined) { + // If inputData is a string, remove extra spaces + if (typeof inputData === "string") { + // If inputData starts and end with HTML tags, remove extra spaces from the content inside the tags, including leading and trailing spaces + if (/^<[^>]+>.*<\/[^>]+>$/.test(inputData)) { + const startTags = inputData.substring(0, inputData.indexOf(">") + 1); + const endTags = inputData.substring(inputData.lastIndexOf("<")); + const inputDataContent = inputData.substring( + startTags.length, + inputData.lastIndexOf("<") + ); + inputData = `${startTags}${inputDataContent + .replace(/\s+/g, " ") + .trim()}${endTags}`; + } else { + inputData = inputData.replace(/\s+/g, " ").trim(); + } + } + // If inputData is an array, recursively call removeExtraSpaces to remove extra spaces from each of its items + else if (Array.isArray(inputData)) { + inputData = inputData.map((item) => removeExtraSpaces(item)); + } + // If inputData is an object, loop through each of its keys and recursively call removeExtraSpaces to remove extra spaces from each of its values + else if (typeof inputData === "object") { + Object.keys(inputData).forEach((key) => { + inputData[key] = removeExtraSpaces(inputData[key]); + }); + } + } + + return inputData; +}; + +module.exports = removeExtraSpaces; diff --git a/eq-author-api/utils/removeExtraSpaces.test.js b/eq-author-api/utils/removeExtraSpaces.test.js new file mode 100644 index 0000000000..fafbe28579 --- /dev/null +++ b/eq-author-api/utils/removeExtraSpaces.test.js @@ -0,0 +1,107 @@ +const removeExtraSpaces = require("./removeExtraSpaces"); + +describe("removeExtraSpaces", () => { + it("should remove extra spaces from string values", () => { + const sectionTitleWithExtraSpaces = " Section 1 "; + + const sectionTitleWithoutExtraSpaces = removeExtraSpaces( + sectionTitleWithExtraSpaces + ); + + expect(sectionTitleWithoutExtraSpaces).toEqual("Section 1"); + }); + + it("should remove extra spaces from string values with HTML tags", () => { + const sectionTitleWithExtraSpaces = "

Section 1

"; + + const sectionTitleWithoutExtraSpaces = removeExtraSpaces( + sectionTitleWithExtraSpaces + ); + + expect(sectionTitleWithoutExtraSpaces).toEqual("

Section 1

"); + }); + + it("should remove extra spaces from arrays of strings", () => { + const answerLabelsWithExtraSpaces = [ + "Answer 1 ", + " Answer 2", + " Answer 3 ", + ]; + + const answerLabelsWithoutExtraSpaces = removeExtraSpaces( + answerLabelsWithExtraSpaces + ); + + expect(answerLabelsWithoutExtraSpaces).toEqual([ + "Answer 1", + "Answer 2", + "Answer 3", + ]); + }); + + it("should remove extra spaces from all string values in object", () => { + const sectionWithExtraSpaces = { + id: "section-with-extra-spaces-1", + title: " Section with extra spaces 1 ", + folders: [ + { + id: "folder-with-extra-spaces-1", + alias: " Folder with extra spaces 1", + pages: [ + { + id: "page-with-extra-spaces-1", + title: "Page with extra spaces 1 ", + answers: [ + { + id: "answer-with-extra-spaces-1", + label: " Answer with extra spaces 1", + }, + ], + }, + ], + }, + ], + }; + + const sectionWithoutExtraSpaces = removeExtraSpaces(sectionWithExtraSpaces); + + expect(sectionWithoutExtraSpaces).toMatchObject({ + id: "section-with-extra-spaces-1", + title: "Section with extra spaces 1", + folders: [ + { + id: "folder-with-extra-spaces-1", + alias: "Folder with extra spaces 1", + pages: [ + { + id: "page-with-extra-spaces-1", + title: "Page with extra spaces 1", + answers: [ + { + id: "answer-with-extra-spaces-1", + label: "Answer with extra spaces 1", + }, + ], + }, + ], + }, + ], + }); + }); + + it("should remove extra spaces from arrays of strings in objects", () => { + const page = { + id: "page-1", + title: " Page 1", + answerLabels: ["Answer 1", "Answer 2 ", "Answer 3"], // Mock value to test array of strings in an object - `answerLabels` is not an attribute of `page` in Author + }; + + const pageWithoutExtraSpaces = removeExtraSpaces(page); + + expect(pageWithoutExtraSpaces).toMatchObject({ + id: "page-1", + title: "Page 1", + answerLabels: ["Answer 1", "Answer 2", "Answer 3"], + }); + }); +}); diff --git a/eq-author/src/App/importingContent/index.js b/eq-author/src/App/importingContent/index.js index 8ea61d2aea..ce31b7d29f 100644 --- a/eq-author/src/App/importingContent/index.js +++ b/eq-author/src/App/importingContent/index.js @@ -1,6 +1,7 @@ import React, { useState } from "react"; import { Query } from "react-apollo"; import PropTypes from "prop-types"; +import styled from "styled-components"; import { useParams } from "react-router-dom"; import { useMutation } from "@apollo/react-hooks"; @@ -26,12 +27,19 @@ import ReviewSectionsModal from "components/modals/ImportSectionReviewModal"; import SelectContentModal from "components/modals/ImportContentModal"; import QuestionPicker from "components/QuestionPicker"; import SectionPicker from "components/SectionPicker"; +import ExtraSpaceConfirmationModal from "components-themed/Modal"; import { ListCollectorQualifierPage, ListCollectorConfirmationPage, } from "constants/page-types"; +const ExtraSpaceModalWrapper = styled.div` + .modal-button-container { + margin-top: 1em; + } +`; + const ImportingContent = ({ stopImporting, targetInsideFolder, @@ -48,6 +56,11 @@ const ImportingContent = ({ const [reviewingSections, setReviewingSections] = useState(false); const [selectingSections, setSelectingSections] = useState(false); const [selectingContent, setSelectingContent] = useState(false); + const [showQuestionExtraSpaceModal, setShowQuestionExtraSpaceModal] = + useState(false); + const [showSectionExtraSpaceModal, setShowSectionExtraSpaceModal] = + useState(false); + /* * Data */ @@ -84,6 +97,8 @@ const ImportingContent = ({ setReviewingSections(false); setSelectingSections(false); setSelectingContent(false); + setShowQuestionExtraSpaceModal(false); + setShowSectionExtraSpaceModal(false); }; // Selecting a questionnaire @@ -152,91 +167,146 @@ const ImportingContent = ({ } }; - const onReviewQuestionsSubmit = (selectedQuestions) => { - const questionIds = selectedQuestions.map(({ id }) => id); - - let input = { - questionIds, - questionnaireId: questionnaireImportingFrom.id, - }; - - switch (currentEntityName) { - case "section": { - input.position = { - sectionId: currentEntityId, - index: 0, - }; - - break; + // Checks if inputData contains extra spaces, including all strings, array items and object values + const containsExtraSpaces = (inputData) => { + // Does not check for extra spaces if inputData is null or undefined + if (inputData != null) { + // Checks if inputData is a string containing extra spaces + if (typeof inputData === "string") { + // Removes opening and closing HTML tags from the start and end of the string + const inputDataWithoutTags = inputData + .replace(/^(<\/?[^>]+>)+/, "") + .replace(/(<\/?[^>]+>)+$/, ""); + + // Checks for consecutive, leading and trailing spaces + if ( + /\s{2,}/g.test(inputDataWithoutTags) || + inputDataWithoutTags.trim() !== inputDataWithoutTags + ) { + return true; + } } - case "folder": { - const { id: sectionId } = getSectionByFolderId( - sourceQuestionnaire, - currentEntityId - ); - - const { listId, position } = getFolderById( - sourceQuestionnaire, - currentEntityId + // If inputData is an array, recursively calls containsExtraSpaces to return true if any of its items contain extra spaces + else if (Array.isArray(inputData)) { + return inputData.some((element) => containsExtraSpaces(element)); + } + // If inputData is an object, recursively calls containsExtraSpaces to return true if any of its values contain extra spaces + else if (typeof inputData === "object") { + return Object.values(inputData).some((value) => + containsExtraSpaces(value) ); + } + // If inputData is a different type, return false as it does not contain extra spaces + else { + return false; + } + } + }; - input.position = { - sectionId, - }; + const onReviewQuestionsSubmit = (selectedQuestions) => { + const questionIds = selectedQuestions.map(({ id }) => id); + let questionContainsExtraSpaces = false; - if (targetInsideFolder) { - if (listId != null) { - input.position.folderId = currentEntityId; - input.position.index = 2; + selectedQuestions.forEach((selectedQuestion) => { + if (containsExtraSpaces(selectedQuestion)) { + questionContainsExtraSpaces = true; + } + }); + + if (questionContainsExtraSpaces && !showQuestionExtraSpaceModal) { + setReviewingQuestions(false); + setSelectingQuestions(false); + setReviewingSections(false); + setSelectingSections(false); + setSelectingContent(false); + setShowQuestionExtraSpaceModal(true); + } else { + let input = { + questionIds, + questionnaireId: questionnaireImportingFrom.id, + }; + + switch (currentEntityName) { + case "section": { + input.position = { + sectionId: currentEntityId, + index: 0, + }; + + break; + } + case "folder": { + const { id: sectionId } = getSectionByFolderId( + sourceQuestionnaire, + currentEntityId + ); + + const { listId, position } = getFolderById( + sourceQuestionnaire, + currentEntityId + ); + + input.position = { + sectionId, + }; + + if (targetInsideFolder) { + if (listId != null) { + input.position.folderId = currentEntityId; + input.position.index = 2; + } else { + input.position.folderId = currentEntityId; + input.position.index = 0; + } } else { - input.position.folderId = currentEntityId; - input.position.index = 0; + input.position.index = position + 1; } - } else { - input.position.index = position + 1; - } - - break; - } - case "page": { - const { id: sectionId } = getSectionByPageId( - sourceQuestionnaire, - currentEntityId - ); - - input.position = { - sectionId, - }; - const { id: folderId, position: folderPosition } = getFolderByPageId( - sourceQuestionnaire, - currentEntityId - ); - - const { pageType, position: positionOfPreviousPage } = getPageById( - sourceQuestionnaire, - currentEntityId - ); - - if (pageType === ListCollectorConfirmationPage) { - input.position.index = folderPosition + 1; - } else if (pageType === ListCollectorQualifierPage) { - input.position.folderId = folderId; - input.position.index = positionOfPreviousPage + 2; - } else { - input.position.folderId = folderId; - input.position.index = positionOfPreviousPage + 1; + break; } + case "page": { + const { id: sectionId } = getSectionByPageId( + sourceQuestionnaire, + currentEntityId + ); + + input.position = { + sectionId, + }; + + const { id: folderId, position: folderPosition } = getFolderByPageId( + sourceQuestionnaire, + currentEntityId + ); + + const { pageType, position: positionOfPreviousPage } = getPageById( + sourceQuestionnaire, + currentEntityId + ); + + if (pageType === ListCollectorConfirmationPage) { + input.position.index = folderPosition + 1; + } else if (pageType === ListCollectorQualifierPage) { + input.position.folderId = folderId; + input.position.index = positionOfPreviousPage + 2; + } else { + input.position.folderId = folderId; + input.position.index = positionOfPreviousPage + 1; + } - break; - } - default: { - throw new Error("Unknown entity"); + break; + } + default: { + throw new Error("Unknown entity"); + } } - } - importQuestions({ variables: { input } }); - onGlobalCancel(); + importQuestions({ + variables: { input }, + refetchQueries: ["GetQuestionnaire"], + }); + onGlobalCancel(); + } }; // Selecting sections to import @@ -279,57 +349,77 @@ const ImportingContent = ({ const onReviewSectionsSubmit = (selectedSections) => { const sectionIds = selectedSections.map(({ id }) => id); + let sectionContainsExtraSpaces = false; - let input = { - sectionIds, - questionnaireId: questionnaireImportingFrom.id, - }; - - switch (currentEntityName) { - case "section": { - const { position } = getSectionById( - sourceQuestionnaire, - currentEntityId - ); - - input.position = { - sectionId: currentEntityId, - index: position + 1, - }; - - break; + selectedSections.forEach((selectedSection) => { + if (containsExtraSpaces(selectedSection)) { + sectionContainsExtraSpaces = true; } - case "folder": { - const { id: sectionId, position: positionOfParentSection } = - getSectionByFolderId(sourceQuestionnaire, currentEntityId); + }); + + if (sectionContainsExtraSpaces && !showSectionExtraSpaceModal) { + setReviewingQuestions(false); + setSelectingQuestions(false); + setReviewingSections(false); + setSelectingSections(false); + setSelectingContent(false); + setShowQuestionExtraSpaceModal(false); + setShowSectionExtraSpaceModal(true); + } else { + let input = { + sectionIds, + questionnaireId: questionnaireImportingFrom.id, + }; + + switch (currentEntityName) { + case "section": { + const { position } = getSectionById( + sourceQuestionnaire, + currentEntityId + ); + + input.position = { + sectionId: currentEntityId, + index: position + 1, + }; + + break; + } + case "folder": { + const { id: sectionId, position: positionOfParentSection } = + getSectionByFolderId(sourceQuestionnaire, currentEntityId); - input.position = { - sectionId, - }; + input.position = { + sectionId, + }; - input.position.index = positionOfParentSection + 1; + input.position.index = positionOfParentSection + 1; - break; - } - case "page": { - const { id: sectionId, position: positionOfParentSection } = - getSectionByPageId(sourceQuestionnaire, currentEntityId); + break; + } + case "page": { + const { id: sectionId, position: positionOfParentSection } = + getSectionByPageId(sourceQuestionnaire, currentEntityId); - input.position = { - sectionId, - }; + input.position = { + sectionId, + }; - input.position.index = positionOfParentSection + 1; + input.position.index = positionOfParentSection + 1; - break; - } - default: { - throw new Error("Unknown entity"); + break; + } + default: { + throw new Error("Unknown entity"); + } } - } - importSections({ variables: { input } }); - onGlobalCancel(); + importSections({ + variables: { input }, + refetchQueries: ["GetQuestionnaire"], + }); + onGlobalCancel(); + } }; return ( @@ -367,7 +457,6 @@ const ImportingContent = ({ isOpen={selectingContent} questionnaire={questionnaireImportingFrom} onCancel={onGlobalCancel} - onConfirm={onReviewQuestionsSubmit} onBack={onBackFromReviewingQuestions} onSelectQuestions={onSelectQuestions} onSelectSections={onSelectSections} @@ -470,6 +559,46 @@ const ImportingContent = ({ }} )} + {showQuestionExtraSpaceModal && ( + + onReviewQuestionsSubmit(questionsToImport)} + onClose={onGlobalCancel} + > +

+ The selected content contains extra spaces at the start of lines + of text, between words, or at the end of lines of text. +

+

+ Extra spaces need to be removed before this content can be + imported. +

+
+
+ )} + {showSectionExtraSpaceModal && ( + + onReviewSectionsSubmit(sectionsToImport)} + onClose={onGlobalCancel} + > +

+ The selected content contains extra spaces at the start of lines + of text, between words, or at the end of lines of text. +

+

+ Extra spaces need to be removed before this content can be + imported. +

+
+
+ )} ); }; diff --git a/eq-author/src/App/importingContent/index.test.js b/eq-author/src/App/importingContent/index.test.js index eb353b6136..09ec0eb4eb 100644 --- a/eq-author/src/App/importingContent/index.test.js +++ b/eq-author/src/App/importingContent/index.test.js @@ -28,6 +28,9 @@ const listCollectorFolder = buildListCollectorFolders()[0]; destinationQuestionnaire.sections[0].folders[1] = listCollectorFolder; listCollectorFolder.position = 1; +const extraSpaceModalTitle = + "Confirm the removal of extra spaces from selected content"; + const sourceQuestionnaires = [ { id: "source-questionnaire-id", @@ -141,6 +144,114 @@ const sourceQuestionnaires = [ }, ], }, + { + id: "section-3", + title: "Section 3", + alias: "", + displayName: "Section 3", + folders: [ + { + id: "folder-3", + pages: [ + { + id: "page-5", + title: "Page 5", + pageType: "QuestionPage", + answers: [ + { + id: "answer-5", + type: "Number", + }, + ], + }, + { + id: "page-6", + title: "Page 6", + pageType: "QuestionPage", + answers: [ + { + id: "answer-6", + type: "Number", + label: "Answer 6 with extra spaces", + }, + ], + }, + ], + }, + ], + }, + { + id: "section-4", + title: "Section 4", + alias: "", + displayName: "Section 4", + folders: [ + { + id: "folder-4", + pages: [ + { + id: "page-7", + title: " Page 7", + pageType: "QuestionPage", + answers: [ + { + id: "answer-7", + type: "Number", + }, + ], + }, + ], + }, + ], + }, + { + id: "section-5", + title: "Section 5", + alias: "", + displayName: "Section 5", + folders: [ + { + id: "folder-5", + pages: [ + { + id: "page-8", + title: "

Page 8

", + pageType: "QuestionPage", + answers: [ + { + id: "answer-8", + type: "Number", + }, + ], + }, + ], + }, + ], + }, + { + id: "section-6", + title: "Section 6", + alias: "", + displayName: "Section 6", + folders: [ + { + id: "folder-6", + pages: [ + { + id: "page-9", + title: "

Page 9

", + pageType: "QuestionPage", + answers: [ + { + id: "answer-9", + type: "Number", + }, + ], + }, + ], + }, + ], + }, ], }, ]; @@ -523,6 +634,7 @@ describe("Importing content", () => { }, }, }, + refetchQueries: ["GetQuestionnaire"], }); }); @@ -572,6 +684,7 @@ describe("Importing content", () => { }, }, }, + refetchQueries: ["GetQuestionnaire"], }); }); @@ -620,6 +733,7 @@ describe("Importing content", () => { }, }, }, + refetchQueries: ["GetQuestionnaire"], }); }); @@ -668,6 +782,7 @@ describe("Importing content", () => { }, }, }, + refetchQueries: ["GetQuestionnaire"], }); }); @@ -718,6 +833,7 @@ describe("Importing content", () => { }, }, }, + refetchQueries: ["GetQuestionnaire"], }); }); @@ -768,6 +884,7 @@ describe("Importing content", () => { }, }, }, + refetchQueries: ["GetQuestionnaire"], }); }); @@ -817,6 +934,249 @@ describe("Importing content", () => { }, }, }, + refetchQueries: ["GetQuestionnaire"], + }); + }); + }); + + describe("Extra spaces", () => { + it("should display extra space confirmation modal before importing questions containing extra spaces", () => { + useParams.mockImplementation(() => ({ + questionnaireId: destinationQuestionnaire.id, + entityName: "page", + entityId: + destinationQuestionnaire.sections[0].folders[0].pages[0].id, + })); + + const mockImportQuestions = jest.fn(); + useMutation.mockImplementation(jest.fn(() => [mockImportQuestions])); + + const { getByTestId, getAllByTestId, getByText, queryByText } = + renderImportingContent(); + fireEvent.click(getByText(/All/)); + const allRows = getAllByTestId("table-row"); + fireEvent.click(allRows[0]); + fireEvent.click(getByTestId("confirm-btn")); + + const questionsButton = getByTestId( + "content-modal-select-questions-button" + ); + + fireEvent.click(questionsButton); + fireEvent.click(getByText("Page 6")); + fireEvent.click(getByTestId("button-group").children[1]); + fireEvent.click(getByTestId("button-group").children[0]); + + const sourceSection = sourceQuestionnaires[0].sections[2]; + const destinationSection = destinationQuestionnaire.sections[0]; + + expect( + queryByText("Import content from Source questionnaire 1") + ).not.toBeInTheDocument(); + + expect(mockImportQuestions).toHaveBeenCalledTimes(0); + + // Extra space confirmation modal + + expect(queryByText(extraSpaceModalTitle)).toBeInTheDocument(); + const extraSpaceModalConfirmButton = + getByTestId("btn-modal-positive"); + + fireEvent.click(extraSpaceModalConfirmButton); + expect(mockImportQuestions).toHaveBeenCalledTimes(1); + expect(mockImportQuestions).toHaveBeenCalledWith({ + variables: { + input: { + questionIds: [sourceSection.folders[0].pages[1].id], + questionnaireId: sourceQuestionnaires[0].id, + position: { + sectionId: destinationSection.id, + folderId: destinationSection.folders[0].id, + index: 1, + }, + }, + }, + refetchQueries: ["GetQuestionnaire"], + }); + }); + + it("should display extra space confirmation modal before importing questions containing trailing spaces", () => { + useParams.mockImplementation(() => ({ + questionnaireId: destinationQuestionnaire.id, + entityName: "page", + entityId: + destinationQuestionnaire.sections[0].folders[0].pages[0].id, + })); + + const mockImportQuestions = jest.fn(); + useMutation.mockImplementation(jest.fn(() => [mockImportQuestions])); + + const { getByTestId, getAllByTestId, getByText, queryByText } = + renderImportingContent(); + fireEvent.click(getByText(/All/)); + const allRows = getAllByTestId("table-row"); + fireEvent.click(allRows[0]); + fireEvent.click(getByTestId("confirm-btn")); + + const questionsButton = getByTestId( + "content-modal-select-questions-button" + ); + + fireEvent.click(questionsButton); + fireEvent.click(getByText("Page 7")); + fireEvent.click(getByTestId("button-group").children[1]); + fireEvent.click(getByTestId("button-group").children[0]); + + const sourceSection = sourceQuestionnaires[0].sections[3]; + const destinationSection = destinationQuestionnaire.sections[0]; + + expect( + queryByText("Import content from Source questionnaire 1") + ).not.toBeInTheDocument(); + + expect(mockImportQuestions).toHaveBeenCalledTimes(0); + + // Extra space confirmation modal + + expect(queryByText(extraSpaceModalTitle)).toBeInTheDocument(); + const extraSpaceModalConfirmButton = + getByTestId("btn-modal-positive"); + + fireEvent.click(extraSpaceModalConfirmButton); + expect(mockImportQuestions).toHaveBeenCalledTimes(1); + expect(mockImportQuestions).toHaveBeenCalledWith({ + variables: { + input: { + questionIds: [sourceSection.folders[0].pages[0].id], + questionnaireId: sourceQuestionnaires[0].id, + position: { + sectionId: destinationSection.id, + folderId: destinationSection.folders[0].id, + index: 1, + }, + }, + }, + refetchQueries: ["GetQuestionnaire"], + }); + }); + + it("should display extra space confirmation modal before importing questions including trailing spaces wrapped in tags", () => { + useParams.mockImplementation(() => ({ + questionnaireId: destinationQuestionnaire.id, + entityName: "page", + entityId: + destinationQuestionnaire.sections[0].folders[0].pages[0].id, + })); + + const mockImportQuestions = jest.fn(); + useMutation.mockImplementation(jest.fn(() => [mockImportQuestions])); + + const { getByTestId, getAllByTestId, getByText, queryByText } = + renderImportingContent(); + fireEvent.click(getByText(/All/)); + const allRows = getAllByTestId("table-row"); + fireEvent.click(allRows[0]); + fireEvent.click(getByTestId("confirm-btn")); + + const questionsButton = getByTestId( + "content-modal-select-questions-button" + ); + + fireEvent.click(questionsButton); + fireEvent.click(getByText("Page 8")); + fireEvent.click(getByTestId("button-group").children[1]); + fireEvent.click(getByTestId("button-group").children[0]); + + const sourceSection = sourceQuestionnaires[0].sections[4]; + const destinationSection = destinationQuestionnaire.sections[0]; + + expect( + queryByText("Import content from Source questionnaire 1") + ).not.toBeInTheDocument(); + + expect(mockImportQuestions).toHaveBeenCalledTimes(0); + + // Extra space confirmation modal + + expect(queryByText(extraSpaceModalTitle)).toBeInTheDocument(); + const extraSpaceModalConfirmButton = + getByTestId("btn-modal-positive"); + + fireEvent.click(extraSpaceModalConfirmButton); + expect(mockImportQuestions).toHaveBeenCalledTimes(1); + expect(mockImportQuestions).toHaveBeenCalledWith({ + variables: { + input: { + questionIds: [sourceSection.folders[0].pages[0].id], + questionnaireId: sourceQuestionnaires[0].id, + position: { + sectionId: destinationSection.id, + folderId: destinationSection.folders[0].id, + index: 1, + }, + }, + }, + refetchQueries: ["GetQuestionnaire"], + }); + }); + + it("should display extra space confirmation modal before importing questions including leading spaces wrapped in tags", () => { + useParams.mockImplementation(() => ({ + questionnaireId: destinationQuestionnaire.id, + entityName: "page", + entityId: + destinationQuestionnaire.sections[0].folders[0].pages[0].id, + })); + + const mockImportQuestions = jest.fn(); + useMutation.mockImplementation(jest.fn(() => [mockImportQuestions])); + + const { getByTestId, getAllByTestId, getByText, queryByText } = + renderImportingContent(); + fireEvent.click(getByText(/All/)); + const allRows = getAllByTestId("table-row"); + fireEvent.click(allRows[0]); + fireEvent.click(getByTestId("confirm-btn")); + + const questionsButton = getByTestId( + "content-modal-select-questions-button" + ); + + fireEvent.click(questionsButton); + fireEvent.click(getByText("Page 9")); + fireEvent.click(getByTestId("button-group").children[1]); + fireEvent.click(getByTestId("button-group").children[0]); + + const sourceSection = sourceQuestionnaires[0].sections[5]; + const destinationSection = destinationQuestionnaire.sections[0]; + + expect( + queryByText("Import content from Source questionnaire 1") + ).not.toBeInTheDocument(); + + expect(mockImportQuestions).toHaveBeenCalledTimes(0); + + // Extra space confirmation modal + + expect(queryByText(extraSpaceModalTitle)).toBeInTheDocument(); + const extraSpaceModalConfirmButton = + getByTestId("btn-modal-positive"); + + fireEvent.click(extraSpaceModalConfirmButton); + expect(mockImportQuestions).toHaveBeenCalledTimes(1); + expect(mockImportQuestions).toHaveBeenCalledWith({ + variables: { + input: { + questionIds: [sourceSection.folders[0].pages[0].id], + questionnaireId: sourceQuestionnaires[0].id, + position: { + sectionId: destinationSection.id, + folderId: destinationSection.folders[0].id, + index: 1, + }, + }, + }, + refetchQueries: ["GetQuestionnaire"], }); }); }); @@ -1079,6 +1439,7 @@ describe("Importing content", () => { }, }, }, + refetchQueries: ["GetQuestionnaire"], }); }); @@ -1127,6 +1488,7 @@ describe("Importing content", () => { }, }, }, + refetchQueries: ["GetQuestionnaire"], }); }); @@ -1175,6 +1537,245 @@ describe("Importing content", () => { }, }, }, + refetchQueries: ["GetQuestionnaire"], + }); + }); + + describe("Extra spaces", () => { + it("should display extra space confirmation modal before importing sections containing extra spaces", () => { + const mockImportSections = jest.fn(); + useParams.mockImplementation(() => ({ + questionnaireId: destinationQuestionnaire.id, + entityName: "section", + entityId: destinationQuestionnaire.sections[0].id, + })); + + useMutation.mockImplementation(jest.fn(() => [mockImportSections])); + const { getByTestId, getAllByTestId, getByText, queryByText } = + renderImportingContent(); + fireEvent.click(getByText(/All/)); + const allRows = getAllByTestId("table-row"); + fireEvent.click(allRows[0]); + fireEvent.click(getByTestId("confirm-btn")); + + const sectionsButton = getByTestId( + "content-modal-select-sections-button" + ); + + fireEvent.click(sectionsButton); + fireEvent.click(getByText("Section 3")); + fireEvent.click(getByTestId("button-group").children[1]); + fireEvent.click(getByTestId("button-group").children[0]); + + const sourceSection = sourceQuestionnaires[0].sections[2]; + const destinationSection = destinationQuestionnaire.sections[0]; + + // Test modal closes + expect( + queryByText("Import content from Source questionnaire 1") + ).not.toBeInTheDocument(); + + expect(mockImportSections).toHaveBeenCalledTimes(0); + + // Extra space confirmation modal + + expect(queryByText(extraSpaceModalTitle)).toBeInTheDocument(); + const extraSpaceModalConfirmButton = + getByTestId("btn-modal-positive"); + + fireEvent.click(extraSpaceModalConfirmButton); + expect(mockImportSections).toHaveBeenCalledTimes(1); + + expect(mockImportSections).toHaveBeenCalledWith({ + variables: { + input: { + sectionIds: [sourceSection.id], + questionnaireId: sourceQuestionnaires[0].id, + position: { + sectionId: destinationSection.id, + index: 1, + }, + }, + }, + refetchQueries: ["GetQuestionnaire"], + }); + }); + + it("should display extra space confirmation modal before importing sections containing trailing spaces", () => { + const mockImportSections = jest.fn(); + useParams.mockImplementation(() => ({ + questionnaireId: destinationQuestionnaire.id, + entityName: "section", + entityId: destinationQuestionnaire.sections[0].id, + })); + + useMutation.mockImplementation(jest.fn(() => [mockImportSections])); + const { getByTestId, getAllByTestId, getByText, queryByText } = + renderImportingContent(); + fireEvent.click(getByText(/All/)); + const allRows = getAllByTestId("table-row"); + fireEvent.click(allRows[0]); + fireEvent.click(getByTestId("confirm-btn")); + + const sectionsButton = getByTestId( + "content-modal-select-sections-button" + ); + + fireEvent.click(sectionsButton); + fireEvent.click(getByText("Section 4")); + fireEvent.click(getByTestId("button-group").children[1]); + fireEvent.click(getByTestId("button-group").children[0]); + + const sourceSection = sourceQuestionnaires[0].sections[3]; + const destinationSection = destinationQuestionnaire.sections[0]; + + // Test modal closes + expect( + queryByText("Import content from Source questionnaire 1") + ).not.toBeInTheDocument(); + + expect(mockImportSections).toHaveBeenCalledTimes(0); + + // Extra space confirmation modal + + expect(queryByText(extraSpaceModalTitle)).toBeInTheDocument(); + const extraSpaceModalConfirmButton = + getByTestId("btn-modal-positive"); + + fireEvent.click(extraSpaceModalConfirmButton); + expect(mockImportSections).toHaveBeenCalledTimes(1); + + expect(mockImportSections).toHaveBeenCalledWith({ + variables: { + input: { + sectionIds: [sourceSection.id], + questionnaireId: sourceQuestionnaires[0].id, + position: { + sectionId: destinationSection.id, + index: 1, + }, + }, + }, + refetchQueries: ["GetQuestionnaire"], + }); + }); + + it("should display extra space confirmation modal before importing sections containing tags with trailing spaces", () => { + const mockImportSections = jest.fn(); + useParams.mockImplementation(() => ({ + questionnaireId: destinationQuestionnaire.id, + entityName: "section", + entityId: destinationQuestionnaire.sections[0].id, + })); + + useMutation.mockImplementation(jest.fn(() => [mockImportSections])); + const { getByTestId, getAllByTestId, getByText, queryByText } = + renderImportingContent(); + fireEvent.click(getByText(/All/)); + const allRows = getAllByTestId("table-row"); + fireEvent.click(allRows[0]); + fireEvent.click(getByTestId("confirm-btn")); + + const sectionsButton = getByTestId( + "content-modal-select-sections-button" + ); + + fireEvent.click(sectionsButton); + fireEvent.click(getByText("Section 5")); + fireEvent.click(getByTestId("button-group").children[1]); + fireEvent.click(getByTestId("button-group").children[0]); + + const sourceSection = sourceQuestionnaires[0].sections[4]; + const destinationSection = destinationQuestionnaire.sections[0]; + + // Test modal closes + expect( + queryByText("Import content from Source questionnaire 1") + ).not.toBeInTheDocument(); + + expect(mockImportSections).toHaveBeenCalledTimes(0); + + // Extra space confirmation modal + + expect(queryByText(extraSpaceModalTitle)).toBeInTheDocument(); + const extraSpaceModalConfirmButton = + getByTestId("btn-modal-positive"); + + fireEvent.click(extraSpaceModalConfirmButton); + expect(mockImportSections).toHaveBeenCalledTimes(1); + + expect(mockImportSections).toHaveBeenCalledWith({ + variables: { + input: { + sectionIds: [sourceSection.id], + questionnaireId: sourceQuestionnaires[0].id, + position: { + sectionId: destinationSection.id, + index: 1, + }, + }, + }, + refetchQueries: ["GetQuestionnaire"], + }); + }); + + it("should display extra space confirmation modal before importing sections containing tags with leading spaces", () => { + const mockImportSections = jest.fn(); + useParams.mockImplementation(() => ({ + questionnaireId: destinationQuestionnaire.id, + entityName: "section", + entityId: destinationQuestionnaire.sections[0].id, + })); + + useMutation.mockImplementation(jest.fn(() => [mockImportSections])); + const { getByTestId, getAllByTestId, getByText, queryByText } = + renderImportingContent(); + fireEvent.click(getByText(/All/)); + const allRows = getAllByTestId("table-row"); + fireEvent.click(allRows[0]); + fireEvent.click(getByTestId("confirm-btn")); + + const sectionsButton = getByTestId( + "content-modal-select-sections-button" + ); + + fireEvent.click(sectionsButton); + fireEvent.click(getByText("Section 6")); + fireEvent.click(getByTestId("button-group").children[1]); + fireEvent.click(getByTestId("button-group").children[0]); + + const sourceSection = sourceQuestionnaires[0].sections[5]; + const destinationSection = destinationQuestionnaire.sections[0]; + + // Test modal closes + expect( + queryByText("Import content from Source questionnaire 1") + ).not.toBeInTheDocument(); + + expect(mockImportSections).toHaveBeenCalledTimes(0); + + // Extra space confirmation modal + + expect(queryByText(extraSpaceModalTitle)).toBeInTheDocument(); + const extraSpaceModalConfirmButton = + getByTestId("btn-modal-positive"); + + fireEvent.click(extraSpaceModalConfirmButton); + expect(mockImportSections).toHaveBeenCalledTimes(1); + + expect(mockImportSections).toHaveBeenCalledWith({ + variables: { + input: { + sectionIds: [sourceSection.id], + questionnaireId: sourceQuestionnaires[0].id, + position: { + sectionId: destinationSection.id, + index: 1, + }, + }, + }, + refetchQueries: ["GetQuestionnaire"], + }); }); }); }); diff --git a/eq-author/src/App/introduction/Preview/IntroductionPreview/__snapshots__/index.test.js.snap b/eq-author/src/App/introduction/Preview/IntroductionPreview/__snapshots__/index.test.js.snap index 80ceba4945..4ae258fd49 100644 --- a/eq-author/src/App/introduction/Preview/IntroductionPreview/__snapshots__/index.test.js.snap +++ b/eq-author/src/App/introduction/Preview/IntroductionPreview/__snapshots__/index.test.js.snap @@ -10,15 +10,15 @@ exports[`Introduction Preview should render 1`] = ` /> If the company details or structure have changed contact us on - + 0300 1234 931 - + or email - + surveys@ons.gov.uk - + - + + Notice is given under section 1 of the Statistics of Trade Act 1947. + + + Start survey + + + secondaryDescription

", + } + } + /> + + + +`; + +exports[`Introduction Preview should render the word 'the' when legal basis is NOTICE_FUELS 1`] = ` + + + If the company details or structure have changed contact us on + + + 0300 1234 931 + + + or email + + + surveys@ons.gov.uk + + bar

", + } + } + data-test="description" + /> + + + Notice is given under the Petroleum Act 1998 and Section 1 of the Statistics of Trade Act 1947. + Start survey diff --git a/eq-author/src/App/introduction/Preview/IntroductionPreview/index.js b/eq-author/src/App/introduction/Preview/IntroductionPreview/index.js index c67e455617..ca5d3e124e 100644 --- a/eq-author/src/App/introduction/Preview/IntroductionPreview/index.js +++ b/eq-author/src/App/introduction/Preview/IntroductionPreview/index.js @@ -94,7 +94,7 @@ const CollapsiblesContent = styled.div` padding: 0.2em 0 0.2em 1em; `; -const BlueUnderlined = styled.text` +const DummyLink = styled.text` color: ${colors.blue}; text-decoration: underline; `; @@ -135,13 +135,13 @@ const IntroductionPreview = ({ introduction }) => { If the company details or structure have changed contact us on{" "} {contactDetailsPhoneNumber !== "" ? ( - {contactDetailsPhoneNumber} + {contactDetailsPhoneNumber} ) : ( Phone number missing )}{" "} or email{" "} {contactDetailsEmailAddress !== "" ? ( - {contactDetailsEmailAddress} + {contactDetailsEmailAddress} ) : ( Email address missing )} @@ -155,18 +155,26 @@ const IntroductionPreview = ({ introduction }) => { data-test="description" dangerouslySetInnerHTML={{ __html: description }} /> - {legalBasisCode !== "VOLUNTARY" && ( + {previewQuestions && ( <> - - {legalBasisDescription} + + + View the questions you will be asked in this survey + + )} - {previewQuestions && ( + {legalBasisCode !== "VOLUNTARY" && ( <> - + - You can preview the questions you - may be asked in this survey. + {`Notice is given under ${ + legalBasisCode === "NOTICE_FUELS" + ? `the ${legalBasisDescription}` + : `${legalBasisDescription + ?.charAt(0) + .toLowerCase()}${legalBasisDescription?.slice(1)}` + }`} )} diff --git a/eq-author/src/App/introduction/Preview/IntroductionPreview/index.test.js b/eq-author/src/App/introduction/Preview/IntroductionPreview/index.test.js index e4716d82e1..d53dd29259 100644 --- a/eq-author/src/App/introduction/Preview/IntroductionPreview/index.test.js +++ b/eq-author/src/App/introduction/Preview/IntroductionPreview/index.test.js @@ -4,6 +4,11 @@ import { shallow } from "enzyme"; import { useQuery } from "@apollo/react-hooks"; import IntroductionPreview, { Collapsibles } from "."; +import { useQuestionnaire } from "components/QuestionnaireContext"; + +jest.mock("components/QuestionnaireContext", () => ({ + useQuestionnaire: jest.fn(), +})); jest.mock("@apollo/react-hooks", () => ({ ...jest.requireActual("@apollo/react-hooks"), @@ -18,6 +23,11 @@ useQuery.mockImplementation(() => ({ describe("Introduction Preview", () => { let props; + useQuestionnaire.mockImplementation(() => ({ + questionnaire: { + legalBasis: "NOTICE_1", + }, + })); beforeEach(() => { props = { loading: false, @@ -47,6 +57,16 @@ describe("Introduction Preview", () => { expect(shallow()).toMatchSnapshot(); }); + it("should render the word 'the' when legal basis is NOTICE_FUELS", () => { + useQuestionnaire.mockImplementation(() => ({ + questionnaire: { + legalBasis: "NOTICE_FUELS", + }, + })); + + expect(shallow()).toMatchSnapshot(); + }); + it("should not show incomplete collapsibles", () => { props.introduction.collapsibles = [ { id: "2", title: "collapsible title", description: "" }, diff --git a/eq-author/src/App/page/Design/answers/MultipleChoiceAnswer/__snapshots__/index.test.js.snap b/eq-author/src/App/page/Design/answers/MultipleChoiceAnswer/__snapshots__/index.test.js.snap index d484627595..6a5869f324 100644 --- a/eq-author/src/App/page/Design/answers/MultipleChoiceAnswer/__snapshots__/index.test.js.snap +++ b/eq-author/src/App/page/Design/answers/MultipleChoiceAnswer/__snapshots__/index.test.js.snap @@ -17,11 +17,15 @@ Array [ position: relative; } -.c2 { - font-weight: bold; +.c2 .modal-button-container { + margin-top: 1em; } .c3 { + font-weight: bold; +} + +.c4 { font-size: 1em; border: thin solid #999999; padding: 0.5em; @@ -37,41 +41,41 @@ Array [ overflow: hidden; } -.c3:hover { +.c4:hover { border-color: #3B7A9E; box-shadow: 0 0 0 1pt #3B7A9E; } -.c3:focus, -.c3:focus-within { +.c4:focus, +.c4:focus-within { border-color: transparent; outline: 3px solid #FDBD56; box-shadow: 0 0 0 3px #FDBD56; } -.c3::-webkit-input-placeholder { +.c4::-webkit-input-placeholder { color: #a3a3a3; } -.c3::-moz-placeholder { +.c4::-moz-placeholder { color: #a3a3a3; } -.c3:-ms-input-placeholder { +.c4:-ms-input-placeholder { color: #a3a3a3; } -.c3::placeholder { +.c4::placeholder { color: #a3a3a3; } -.c3:focus { +.c4:focus { outline: none; border-style: solid; border-color: #3B7A9E; } -.c3[disabled] { +.c4[disabled] { opacity: 0.8; pointer-events: none; } @@ -87,10 +91,13 @@ Array [
+