diff --git a/eq-author-api/schema/resolvers/base.js b/eq-author-api/schema/resolvers/base.js index 25d1978b94..d1c0e86eec 100644 --- a/eq-author-api/schema/resolvers/base.js +++ b/eq-author-api/schema/resolvers/base.js @@ -69,34 +69,6 @@ const Resolvers = { ctx.repositories.Section.remove(args.input.id), undeleteSection: (_, args, ctx) => ctx.repositories.Section.undelete(args.input.id), - createSectionIntroduction: ( - _, - { input: { sectionId, introductionContent, introductionTitle } }, - ctx - ) => - ctx.repositories.Section.update({ - id: sectionId, - introductionEnabled: true, - introductionContent, - introductionTitle, - }), - updateSectionIntroduction: ( - _, - { input: { sectionId, introductionContent, introductionTitle } }, - ctx - ) => - ctx.repositories.Section.update({ - id: sectionId, - introductionContent, - introductionTitle, - }), - deleteSectionIntroduction: (_, { input: { sectionId } }, ctx) => - ctx.repositories.Section.update({ - id: sectionId, - introductionEnabled: false, - introductionContent: null, - introductionTitle: null, - }), moveSection: (_, args, ctx) => ctx.repositories.Section.move(args.input), duplicateSection: (_, args, ctx) => ctx.repositories.Section.duplicateSection( @@ -228,19 +200,12 @@ const Resolvers = { } return ctx.repositories.Section.getPosition({ id }); }, - introduction: section => (section.introductionEnabled ? section : null), availablePipingAnswers: ({ id }, args, ctx) => ctx.repositories.Section.getPipingAnswersForSection(id), availablePipingMetadata: ({ id }, args, ctx) => ctx.repositories.Section.getPipingMetadataForSection(id), }, - SectionIntroduction: { - section: ({ id: sectionId }, args, ctx) => { - return ctx.repositories.Section.getById(sectionId); - }, - }, - Page: { __resolveType: ({ pageType }) => pageType, position: ({ position, id }, args, ctx) => { diff --git a/eq-author-api/schema/typeDefs.js b/eq-author-api/schema/typeDefs.js index 413ce0dca3..8a878df882 100644 --- a/eq-author-api/schema/typeDefs.js +++ b/eq-author-api/schema/typeDefs.js @@ -43,16 +43,10 @@ type Section { pages: [Page] questionnaire: Questionnaire position: Int! - introduction: SectionIntroduction - availablePipingAnswers: [Answer!]! - availablePipingMetadata: [Metadata!]! -} - -type SectionIntroduction { - id: ID! introductionTitle: String introductionContent: String - section: Section! + availablePipingAnswers: [Answer!]! + availablePipingMetadata: [Metadata!]! } interface Page { @@ -455,9 +449,6 @@ type Mutation { undeleteSection(input: UndeleteSectionInput!): Section moveSection(input: MoveSectionInput!): Section duplicateSection(input: DuplicateSectionInput!): Section - createSectionIntroduction(input: CreateSectionIntroductionInput!): SectionIntroduction! - updateSectionIntroduction(input: UpdateSectionIntroductionInput!): SectionIntroduction! - deleteSectionIntroduction(input: DeleteSectionIntroductionInput!): SectionIntroduction! createPage(input: CreatePageInput!): Page updatePage(input: UpdatePageInput!): Page deletePage(input: DeletePageInput!): Page @@ -604,24 +595,10 @@ input UpdateSectionInput { id: ID! title: String alias: String -} - -input CreateSectionIntroductionInput { - sectionId: ID! introductionTitle: String introductionContent: String } -input UpdateSectionIntroductionInput { - sectionId: ID! - introductionTitle: String - introductionContent: String -} - -input DeleteSectionIntroductionInput { - sectionId: ID! -} - input DeleteSectionInput { id: ID! } diff --git a/eq-author-api/tests/schema/mutations/createSectionIntroduction.test.js b/eq-author-api/tests/schema/mutations/createSectionIntroduction.test.js deleted file mode 100644 index 2a3914f12f..0000000000 --- a/eq-author-api/tests/schema/mutations/createSectionIntroduction.test.js +++ /dev/null @@ -1,46 +0,0 @@ -const executeQuery = require("../../utils/executeQuery"); - -describe("createSectionIntroduction", () => { - const createSectionIntro = ` - mutation createSectionIntroduction($input: CreateSectionIntroductionInput!) { - createSectionIntroduction(input: $input) { - id - } - } - `; - - let repositories; - const SECTION_ID = "456"; - - beforeEach(() => { - repositories = { - Section: { - update: jest.fn().mockResolvedValueOnce({ id: "456" }), - }, - }; - }); - - it("should allow creation of Section introductions with prefilled values", async () => { - const input = { - sectionId: SECTION_ID, - introductionTitle: "foo", - introductionContent: "bar", - }; - - const result = await executeQuery( - createSectionIntro, - { input }, - { repositories } - ); - - expect(result.errors).toBeUndefined(); - expect(result.data.createSectionIntroduction.id).toBe(SECTION_ID); - - expect(repositories.Section.update).toHaveBeenCalledWith({ - id: SECTION_ID, - introductionEnabled: true, - introductionTitle: "foo", - introductionContent: "bar", - }); - }); -}); diff --git a/eq-author-api/tests/schema/mutations/deleteSectionIntroduction.test.js b/eq-author-api/tests/schema/mutations/deleteSectionIntroduction.test.js deleted file mode 100644 index 7b4a84dd97..0000000000 --- a/eq-author-api/tests/schema/mutations/deleteSectionIntroduction.test.js +++ /dev/null @@ -1,44 +0,0 @@ -const executeQuery = require("../../utils/executeQuery"); - -describe("deleteSectionIntroduction", () => { - const deleteSectionIntro = ` - mutation deleteSectionIntroduction($input: DeleteSectionIntroductionInput!) { - deleteSectionIntroduction(input: $input) { - id - } - } - `; - - let repositories; - const SECTION_ID = "456"; - - beforeEach(() => { - repositories = { - Section: { - update: jest.fn().mockResolvedValueOnce({ id: "456" }), - }, - }; - }); - - it("should allow deletion of Section introductions", async () => { - const input = { - sectionId: SECTION_ID, - }; - - const result = await executeQuery( - deleteSectionIntro, - { input }, - { repositories } - ); - - expect(result.errors).toBeUndefined(); - expect(result.data.deleteSectionIntroduction.id).toBe(SECTION_ID); - - expect(repositories.Section.update).toHaveBeenCalledWith({ - id: SECTION_ID, - introductionEnabled: false, - introductionTitle: null, - introductionContent: null, - }); - }); -}); diff --git a/eq-author-api/tests/schema/mutations/updateSectionIntroduction.test.js b/eq-author-api/tests/schema/mutations/updateSectionIntroduction.test.js deleted file mode 100644 index bfe35f4d86..0000000000 --- a/eq-author-api/tests/schema/mutations/updateSectionIntroduction.test.js +++ /dev/null @@ -1,57 +0,0 @@ -const executeQuery = require("../../utils/executeQuery"); - -describe("updateSectionIntroduction", () => { - const updateSectionIntro = ` - mutation updateSectionIntroduction($input: UpdateSectionIntroductionInput!) { - updateSectionIntroduction(input: $input) { - id - introductionContent - introductionTitle - - } - } - `; - - let repositories; - const SECTION_ID = "456"; - - beforeEach(() => { - repositories = { - Section: { - update: jest.fn().mockResolvedValueOnce({ - id: "456", - introductionEnabled: true, - introductionContent: "foo", - introductionTitle: "bar", - }), - }, - }; - }); - - it("should allow update of Section introductions", async () => { - const input = { - sectionId: SECTION_ID, - introductionContent: "foo", - introductionTitle: "bar", - }; - - const result = await executeQuery( - updateSectionIntro, - { input }, - { repositories } - ); - - expect(result.errors).toBeUndefined(); - expect(result.data.updateSectionIntroduction).toMatchObject({ - id: SECTION_ID, - introductionContent: "foo", - introductionTitle: "bar", - }); - - expect(repositories.Section.update).toHaveBeenCalledWith({ - id: SECTION_ID, - introductionContent: "foo", - introductionTitle: "bar", - }); - }); -}); diff --git a/eq-author/cypress/integration/authenticated/builder_spec.js b/eq-author/cypress/integration/authenticated/builder_spec.js index 05ce216b0c..f074930249 100644 --- a/eq-author/cypress/integration/authenticated/builder_spec.js +++ b/eq-author/cypress/integration/authenticated/builder_spec.js @@ -173,186 +173,129 @@ describe("builder", () => { changeQuestionnaireTitle(questionnaireTitle); }); - it("Can create a new section", () => { - checkIsOnDesignPage(); - - let prevHash; - - cy.hash() - .then(hash => { - prevHash = hash; - addSection(); - }) - .then(() => { - assertHash({ - previousPath: Routes.PAGE, - previousHash: prevHash, - currentPath: Routes.SECTION, - equality: { - questionnaireId: true, - sectionId: false, - }, + describe("Section", () => { + it("Can create a new section", () => { + checkIsOnDesignPage(); + + let prevHash; + + cy.hash() + .then(hash => { + prevHash = hash; + addSection(); + }) + .then(() => { + assertHash({ + previousPath: Routes.PAGE, + previousHash: prevHash, + currentPath: Routes.SECTION, + equality: { + questionnaireId: true, + sectionId: false, + }, + }); }); - }); - }); - - it("Can navigate to a section", () => { - checkIsOnDesignPage(); - - const initialHash = cy - .hash() - .should("match", questionPageRegex) - .then(hash => hash); - - addSection(); - - cy.hash().should("not.eq", initialHash); - const resultingHash = cy - .hash() - .should("match", sectionRegex) - .then(hash => hash); - - navigateToFirstSection(); - - cy.hash() - .should("match", sectionRegex) - .and("not.eq", resultingHash); - }); - - it("Can edit section alias and title", () => { - checkIsOnDesignPage(); + }); - navigateToFirstSection(); + it("Can navigate to a section", () => { + checkIsOnDesignPage(); - cy.get(testId("alias")).type("section alias"); + const initialHash = cy + .hash() + .should("match", questionPageRegex) + .then(hash => hash); - typeIntoDraftEditor( - testId("txt-section-title", "testid"), - "my new section" - ); + addSection(); - cy.get(testId("nav-section-link")).should("contain", "section alias"); - }); + cy.hash().should("not.eq", initialHash); + const resultingHash = cy + .hash() + .should("match", sectionRegex) + .then(hash => hash); - it("can add and edit a section introduction", () => { - checkIsOnDesignPage(); + navigateToFirstSection(); - navigateToFirstSection(); - - cy.get(testId("btn-add-intro")).click(); - - typeIntoDraftEditor( - testId("txt-introduction-title", "testid"), - "Section Introduction Title" - ); - typeIntoDraftEditor( - testId("txt-introduction-content", "testid"), - "Section Introduction Content" - ); - }); - - it("can delete a section introduction", () => { - checkIsOnDesignPage(); - - navigateToFirstSection(); - - cy.get(testId("btn-add-intro")).click(); - - cy.get(testId("section-intro-canvas")).within(() => { - cy.get(testId("btn-delete")).click(); + cy.hash() + .should("match", sectionRegex) + .and("not.eq", resultingHash); }); - cy.get(testId("btn-add-intro")); - }); - it("can undelete a section introduction", () => { - checkIsOnDesignPage(); + it("Can edit section alias, title and introduction details", () => { + checkIsOnDesignPage(); - navigateToFirstSection(); + navigateToFirstSection(); - cy.get(testId("btn-add-intro")).click(); + cy.get(testId("alias")).type("section alias"); - typeIntoDraftEditor( - testId("txt-introduction-title", "testid"), - "Section Introduction Title" - ); - typeIntoDraftEditor( - testId("txt-introduction-content", "testid"), - "Section Introduction Content" - ); - - cy.get(testId("section-intro-canvas")).within(() => { - cy.get(testId("btn-delete")).click(); - }); - - cy.get(testId("btn-add-intro")).should("be.visible"); - cy.get(testId("btn-undo")).should("be.visible"); - cy.get(testId("btn-undo")).click(); + typeIntoDraftEditor( + testId("txt-section-title", "testid"), + "my new section" + ); - cy.get(testId("section-intro-canvas")).within(() => { - cy.get(testId("txt-introduction-title", "testid")).should( - "contain", + typeIntoDraftEditor( + testId("txt-introduction-title", "testid"), "Section Introduction Title" ); - cy.get(testId("txt-introduction-content", "testid")).should( - "contain", + typeIntoDraftEditor( + testId("txt-introduction-content", "testid"), "Section Introduction Content" ); - }); - }); - it("can preview a section introducion", () => { - checkIsOnDesignPage(); + cy.get(testId("nav-section-link")).should("contain", "section alias"); + }); - navigateToFirstSection(); + it("Can preview a section once it has introduction content", () => { + checkIsOnDesignPage(); - cy.get(testId("preview")).click(); - cy.hash().should("match", /\/design$/); + navigateToFirstSection(); - cy.get(testId("btn-add-intro")).click(); + cy.get(testId("preview")).click(); + cy.hash().should("match", /\/design$/); - typeIntoDraftEditor( - testId("txt-introduction-title", "testid"), - "Section Introduction Title" - ); - typeIntoDraftEditor( - testId("txt-introduction-content", "testid"), - "Section Introduction Content" - ); + typeIntoDraftEditor( + testId("txt-introduction-title", "testid"), + "Section Introduction Title" + ); + typeIntoDraftEditor( + testId("txt-introduction-content", "testid"), + "Section Introduction Content" + ); - cy.get(testId("preview")).click(); - cy.hash().should("match", /\/preview$/); - }); + cy.get(testId("preview")).click(); + cy.hash().should("match", /\/preview$/); + }); - it("Can delete a section", () => { - checkIsOnDesignPage(); + it("Can delete a section", () => { + checkIsOnDesignPage(); - addSection(); + addSection(); - navigateToFirstSection(); + navigateToFirstSection(); - let prevHash; + let prevHash; - cy.hash().then(hash => { - prevHash = hash; - }); + cy.hash().then(hash => { + prevHash = hash; + }); - cy.get(testId("btn-delete")).click(); - cy.get(testId("delete-confirm-modal")).within(() => { - cy.get("button") - .contains("Delete") - .click(); - }); + cy.get(testId("btn-delete")).click(); + cy.get(testId("delete-confirm-modal")).within(() => { + cy.get("button") + .contains("Delete") + .click(); + }); - cy.then(() => { - assertHash({ - previousPath: Routes.SECTION, - previousHash: prevHash, - currentPath: Routes.PAGE, - equality: { - questionnaireId: true, - sectionId: false, - pageId: false, - }, + cy.then(() => { + assertHash({ + previousPath: Routes.SECTION, + previousHash: prevHash, + currentPath: Routes.PAGE, + equality: { + questionnaireId: true, + sectionId: false, + pageId: false, + }, + }); }); }); }); diff --git a/eq-author/cypress/integration/authenticated/piping_spec.js b/eq-author/cypress/integration/authenticated/piping_spec.js index 100e198a11..6836ec216b 100644 --- a/eq-author/cypress/integration/authenticated/piping_spec.js +++ b/eq-author/cypress/integration/authenticated/piping_spec.js @@ -41,8 +41,6 @@ const canPipePreviousAnswer = ({ selector }) => { cy.get(testId(selector, "testid")).should("contain", `[${ANSWER}]`); }; -const addSectionIntro = () => cy.get(testId("btn-add-intro")).click(); - const clickLastSection = () => cy .get(testId("nav-section-link")) @@ -107,7 +105,6 @@ describe("Piping", () => { describe("Section Introduction", () => { beforeEach(() => { clickLastSection(); - addSectionIntro(); }); it("Can pipe previous answer into section introduction title", () => { canPipePreviousAnswer({ selector: "txt-introduction-title" }); @@ -140,7 +137,6 @@ describe("Piping", () => { describe("Section Introduction", () => { beforeEach(() => { clickLastSection(); - addSectionIntro(); }); it("Can pipe metadata into section introduction title", () => { canPipeMetadata({ selector: "txt-introduction-title" }); diff --git a/eq-author/src/App/section/Design/MoveSectionModal/MoveSectionQuery.js b/eq-author/src/App/section/Design/SectionEditor/MoveSectionModal/MoveSectionQuery.js similarity index 100% rename from eq-author/src/App/section/Design/MoveSectionModal/MoveSectionQuery.js rename to eq-author/src/App/section/Design/SectionEditor/MoveSectionModal/MoveSectionQuery.js diff --git a/eq-author/src/App/section/Design/MoveSectionModal/__snapshots__/index.test.js.snap b/eq-author/src/App/section/Design/SectionEditor/MoveSectionModal/__snapshots__/index.test.js.snap similarity index 100% rename from eq-author/src/App/section/Design/MoveSectionModal/__snapshots__/index.test.js.snap rename to eq-author/src/App/section/Design/SectionEditor/MoveSectionModal/__snapshots__/index.test.js.snap diff --git a/eq-author/src/App/section/Design/MoveSectionModal/index.js b/eq-author/src/App/section/Design/SectionEditor/MoveSectionModal/index.js similarity index 100% rename from eq-author/src/App/section/Design/MoveSectionModal/index.js rename to eq-author/src/App/section/Design/SectionEditor/MoveSectionModal/index.js diff --git a/eq-author/src/App/section/Design/MoveSectionModal/index.test.js b/eq-author/src/App/section/Design/SectionEditor/MoveSectionModal/index.test.js similarity index 94% rename from eq-author/src/App/section/Design/MoveSectionModal/index.test.js rename to eq-author/src/App/section/Design/SectionEditor/MoveSectionModal/index.test.js index 72abc18e3d..48387c7b31 100644 --- a/eq-author/src/App/section/Design/MoveSectionModal/index.test.js +++ b/eq-author/src/App/section/Design/SectionEditor/MoveSectionModal/index.test.js @@ -2,7 +2,7 @@ import React from "react"; import { shallow } from "enzyme"; import PositionModal from "components/PositionModal"; -import MoveSectionModal from "App/section/Design/MoveSectionModal"; +import MoveSectionModal from "./"; import { buildQuestionnaire } from "tests/utils/createMockQuestionnaire"; diff --git a/eq-author/src/App/section/Design/MoveSectionModal/story.js b/eq-author/src/App/section/Design/SectionEditor/MoveSectionModal/story.js similarity index 100% rename from eq-author/src/App/section/Design/MoveSectionModal/story.js rename to eq-author/src/App/section/Design/SectionEditor/MoveSectionModal/story.js diff --git a/eq-author/src/App/section/Design/SectionEditor/SectionEditor.test.js b/eq-author/src/App/section/Design/SectionEditor/SectionEditor.test.js index 8a01fcc4ae..e76fc37ba1 100644 --- a/eq-author/src/App/section/Design/SectionEditor/SectionEditor.test.js +++ b/eq-author/src/App/section/Design/SectionEditor/SectionEditor.test.js @@ -7,11 +7,25 @@ describe("SectionEditor", () => { const section1 = { id: "section-1", title: "Section 1", + alias: "alias", + introductionTitle: "Intro title", + introductionContent: "Intro content", + questionnaire: { + id: "2", + navigation: true, + }, }; const section2 = { id: "section-2", title: "Section 2", + alias: "alias", + introductionTitle: "Intro title", + introductionContent: "Intro content", + questionnaire: { + id: "2", + navigation: true, + }, }; const match = { @@ -31,8 +45,6 @@ describe("SectionEditor", () => { onCloseMoveSectionDialog: jest.fn(), }; - let wrapper; - const render = ({ ...props }) => shallow( { /> ); - beforeEach(() => { - wrapper = render(); - }); - it("should render", () => { + const wrapper = render(); expect(wrapper).toMatchSnapshot(); wrapper.setProps({ section: section2 }); expect(wrapper).toMatchSnapshot(); }); it("should invoke change and update callbacks onUpdate", () => { + const wrapper = render(); const editors = wrapper.find(RichTextEditor); expect(editors.length).toBeGreaterThan(0); @@ -70,8 +80,65 @@ describe("SectionEditor", () => { }); }); + it("should show the section title when navigation is enabled", () => { + const section = { + ...section1, + questionnaire: { + id: "2", + navigation: true, + }, + }; + const wrapper = render({ section }); + expect(wrapper.find("[testSelector='txt-section-title']").length).toEqual( + 1 + ); + }); + + it("should not show the section title when navigation is disabled", () => { + const section = { + ...section1, + questionnaire: { + id: "2", + navigation: false, + }, + }; + const wrapper = render({ section }); + expect(wrapper.find("[testSelector='txt-section-title']").length).toEqual( + 0 + ); + }); + + it("should not autofocus the section title when its empty and navigation has just been turned on", () => { + const wrapper = render({ + section: { + ...section1, + title: "", + questionnaire: { + id: "2", + navigation: false, + }, + }, + }); + + wrapper.setProps({ + section: { + ...section1, + title: "", + questionnaire: { + id: "2", + navigation: true, + }, + }, + }); + + expect( + wrapper.find("[testSelector='txt-section-title']").prop("autoFocus") + ).toBe(false); + }); + describe("DeleteConfirmDialog", () => { let deleteConfirmDialog; + let wrapper; beforeEach(() => { wrapper = render({ showDeleteConfirmDialog: true }); diff --git a/eq-author/src/App/section/Design/SectionEditor/SectionIntroduction/IntroEditor.js b/eq-author/src/App/section/Design/SectionEditor/SectionIntroduction/IntroEditor.js deleted file mode 100644 index f4a3a9257a..0000000000 --- a/eq-author/src/App/section/Design/SectionEditor/SectionIntroduction/IntroEditor.js +++ /dev/null @@ -1,103 +0,0 @@ -import React from "react"; -import { propType } from "graphql-anywhere"; -import PropTypes from "prop-types"; -import styled from "styled-components"; -import { flowRight } from "lodash"; -import { connect } from "react-redux"; - -import { Buttons } from "App/questionPage/Design/EditorToolbar"; -import IconButtonDelete from "components/buttons/IconButtonDelete"; -import RichTextEditor from "components/RichTextEditor"; -import withEntityEditor from "components/withEntityEditor"; -import { colors, radius } from "constants/theme"; -import withPropRenamed from "enhancers/withPropRenamed"; -import { raiseToast } from "redux/toast/actions"; -import withChangeUpdate from "enhancers/withChangeUpdate"; - -import withDeleteSectionIntro from "./withDeleteSectionIntro"; -import withUpdateSectionIntro from "./withUpdateSectionIntro"; -import fragment from "./SectionIntroductionFragment.graphql"; - -const IntroToolbar = styled.div` - border: 1px solid ${colors.bordersLight}; - border-bottom: 0; - border-radius: ${radius} ${radius} 0 0; - display: flex; - padding: 1em 1em 0; -`; - -const IntroCanvas = styled.div` - padding: 0 1em; - border: 1px solid ${colors.bordersLight}; - border-top: 0; - background-color: ${colors.white}; - border-radius: 0 0 ${radius} ${radius}; -`; - -export const UnwrappedIntroEditor = ({ - sectionIntro, - deleteSectionIntro, - onChangeUpdate, -}) => ( -
- - - deleteSectionIntro(sectionIntro)} - data-test="btn-delete" - iconText="Delete Introduction" - /> - - - - - - -
-); - -UnwrappedIntroEditor.propTypes = { - sectionIntro: propType(fragment).isRequired, - onUpdate: PropTypes.func, - onChange: PropTypes.func, - deleteSectionIntro: PropTypes.func.isRequired, - onChangeUpdate: PropTypes.func.isRequired, -}; - -const wrappedSectionIntro = flowRight( - connect( - null, - { raiseToast } - ), - withDeleteSectionIntro, - withUpdateSectionIntro, - withPropRenamed("updateSectionIntro", "onUpdate"), - withEntityEditor("sectionIntro", fragment), - withChangeUpdate -)(UnwrappedIntroEditor); - -export default wrappedSectionIntro; diff --git a/eq-author/src/App/section/Design/SectionEditor/SectionIntroduction/IntroEditor.test.js b/eq-author/src/App/section/Design/SectionEditor/SectionIntroduction/IntroEditor.test.js deleted file mode 100644 index a237edc22b..0000000000 --- a/eq-author/src/App/section/Design/SectionEditor/SectionIntroduction/IntroEditor.test.js +++ /dev/null @@ -1,53 +0,0 @@ -import React from "react"; -import { shallow } from "enzyme"; -import { UnwrappedIntroEditor } from "./IntroEditor"; -import IconButtonDelete from "components/buttons/IconButtonDelete"; -import RichTextEditor from "components/RichTextEditor"; - -describe("IntroEditor", () => { - let defaultProps; - - beforeEach(() => { - defaultProps = { - sectionIntro: { - id: "1", - introductionTitle: "foo", - introductionContent: "bar", - }, - createSectionIntro: jest.fn(), - onUpdate: jest.fn(), - onChange: jest.fn(), - deleteSectionIntro: jest.fn(), - onChangeUpdate: jest.fn(), - }; - }); - - it("should render the editor", () => { - const wrapper = shallow(); - - expect(wrapper).toMatchSnapshot(); - }); - - it("should call deleteSectionIntro on delete click", () => { - const wrapper = shallow(); - - wrapper.find(IconButtonDelete).simulate("click"); - - expect(defaultProps.deleteSectionIntro).toHaveBeenCalledWith( - defaultProps.sectionIntro - ); - }); - - it("should call handleUpdate on blur", () => { - const wrapper = shallow(); - const editors = wrapper.find(RichTextEditor); - expect(editors.length).toEqual(2); - - editors.forEach((rte, i) => { - const change = { value: `

${i}

` }; - rte.simulate("update", change); - - expect(defaultProps.onChangeUpdate).toHaveBeenCalledWith(change); - }); - }); -}); diff --git a/eq-author/src/App/section/Design/SectionEditor/SectionIntroduction/SectionIntroductionFragment.graphql b/eq-author/src/App/section/Design/SectionEditor/SectionIntroduction/SectionIntroductionFragment.graphql deleted file mode 100644 index b9ae0a897d..0000000000 --- a/eq-author/src/App/section/Design/SectionEditor/SectionIntroduction/SectionIntroductionFragment.graphql +++ /dev/null @@ -1,5 +0,0 @@ -fragment SectionIntro on SectionIntroduction { - id - introductionTitle - introductionContent -} diff --git a/eq-author/src/App/section/Design/SectionEditor/SectionIntroduction/__snapshots__/IntroEditor.test.js.snap b/eq-author/src/App/section/Design/SectionEditor/SectionIntroduction/__snapshots__/IntroEditor.test.js.snap deleted file mode 100644 index bc8f54ba62..0000000000 --- a/eq-author/src/App/section/Design/SectionEditor/SectionIntroduction/__snapshots__/IntroEditor.test.js.snap +++ /dev/null @@ -1,55 +0,0 @@ -// Jest Snapshot v1, https://goo.gl/fbAQLP - -exports[`IntroEditor should render the editor 1`] = ` -
- - - - - - - - - -
-`; diff --git a/eq-author/src/App/section/Design/SectionEditor/SectionIntroduction/createSectionIntro.graphql b/eq-author/src/App/section/Design/SectionEditor/SectionIntroduction/createSectionIntro.graphql deleted file mode 100644 index fec1625844..0000000000 --- a/eq-author/src/App/section/Design/SectionEditor/SectionIntroduction/createSectionIntro.graphql +++ /dev/null @@ -1,13 +0,0 @@ -mutation createSectionIntroduction($input: CreateSectionIntroductionInput!) { - createSectionIntroduction(input: $input) { - id - introductionTitle - introductionContent - section { - id - introduction { - id - } - } - } -} diff --git a/eq-author/src/App/section/Design/SectionEditor/SectionIntroduction/deleteSectionIntro.graphql b/eq-author/src/App/section/Design/SectionEditor/SectionIntroduction/deleteSectionIntro.graphql deleted file mode 100644 index 2119ded613..0000000000 --- a/eq-author/src/App/section/Design/SectionEditor/SectionIntroduction/deleteSectionIntro.graphql +++ /dev/null @@ -1,13 +0,0 @@ -mutation deleteSectionIntroduction($input: DeleteSectionIntroductionInput!) { - deleteSectionIntroduction(input: $input) { - id - introductionTitle - introductionContent - section { - id - introduction { - id - } - } - } -} diff --git a/eq-author/src/App/section/Design/SectionEditor/SectionIntroduction/index.js b/eq-author/src/App/section/Design/SectionEditor/SectionIntroduction/index.js deleted file mode 100644 index 13d2b2c08d..0000000000 --- a/eq-author/src/App/section/Design/SectionEditor/SectionIntroduction/index.js +++ /dev/null @@ -1,68 +0,0 @@ -import React from "react"; -import { propType } from "graphql-anywhere"; -import PropTypes from "prop-types"; -import styled from "styled-components"; -import { TransitionGroup } from "react-transition-group"; - -import AddPage from "App/QuestionnaireDesignPage/icon-add-page.svg?inline"; -import MainCanvas from "components/MainCanvas"; -import Button from "components/buttons/Button"; -import IconText from "components/IconText"; -import FadeTransition from "components/transitions/FadeTransition"; -import { colors, radius } from "constants/theme"; - -import IntroEditor from "./IntroEditor"; -import withCreateSectionIntro from "./withCreateSectionIntro"; -import fragment from "./SectionIntroductionFragment.graphql"; - -const StyledMainCanvas = styled(MainCanvas)` - padding: 0 2em 1em; -`; - -export const AddIntroButton = styled(Button)` - width: 100%; - padding: 0.8em 1.8em 0.8em 0.8em; - border: 1px solid ${colors.bordersLight}; -`; - -export const IntroCanvas = styled.div` - padding: 0 1em; - border: 1px solid ${colors.bordersLight}; - border-top: 0; - background-color: ${colors.white}; - border-radius: 0 0 ${radius} ${radius}; -`; - -export const UnwrappedSectionIntroduction = props => { - const { sectionId, sectionIntro, createSectionIntro } = props; - return ( - - {sectionIntro && ( - - - - {" "} - - )} - - {!sectionIntro && ( - createSectionIntro(sectionId)} - data-test="btn-add-intro" - > - Add introduction - - )} - - ); -}; - -UnwrappedSectionIntroduction.propTypes = { - sectionId: PropTypes.string.isRequired, - sectionIntro: propType(fragment), - createSectionIntro: PropTypes.func.isRequired, -}; - -export default withCreateSectionIntro(UnwrappedSectionIntroduction); diff --git a/eq-author/src/App/section/Design/SectionEditor/SectionIntroduction/index.test.js b/eq-author/src/App/section/Design/SectionEditor/SectionIntroduction/index.test.js deleted file mode 100644 index 8a219a625f..0000000000 --- a/eq-author/src/App/section/Design/SectionEditor/SectionIntroduction/index.test.js +++ /dev/null @@ -1,45 +0,0 @@ -import React from "react"; -import { shallow } from "enzyme"; -import { UnwrappedSectionIntroduction, AddIntroButton } from "./"; -import IntroEditor from "./IntroEditor"; - -describe("SectionIntroduction", () => { - let defaultProps; - - beforeEach(() => { - defaultProps = { - sectionId: "1", - sectionIntro: { - id: "1", - introductionTitle: "foo", - introductionContent: "bar", - }, - createSectionIntro: jest.fn(), - }; - }); - - it("should render section disabled", () => { - defaultProps.sectionIntro = null; - - const wrapper = shallow(); - - expect(wrapper.find(AddIntroButton).exists()).toBeTruthy(); - }); - - it("should render section enabled", () => { - const wrapper = shallow(); - - expect(wrapper.find(IntroEditor).exists()).toBeTruthy(); - }); - - it("clicking add introduction should call createSectionIntro with correct parameters", () => { - defaultProps.sectionIntro = null; - - const wrapper = shallow(); - - wrapper.find('[data-test="btn-add-intro"]').simulate("click"); - expect(defaultProps.createSectionIntro).toHaveBeenCalledWith( - defaultProps.sectionId - ); - }); -}); diff --git a/eq-author/src/App/section/Design/SectionEditor/SectionIntroduction/updateSectionIntro.graphql b/eq-author/src/App/section/Design/SectionEditor/SectionIntroduction/updateSectionIntro.graphql deleted file mode 100644 index 6d38cae869..0000000000 --- a/eq-author/src/App/section/Design/SectionEditor/SectionIntroduction/updateSectionIntro.graphql +++ /dev/null @@ -1,7 +0,0 @@ -mutation updateSectionIntroduction($input: UpdateSectionIntroductionInput!) { - updateSectionIntroduction(input: $input) { - id - introductionTitle - introductionContent - } -} diff --git a/eq-author/src/App/section/Design/SectionEditor/SectionIntroduction/withCreateSectionIntro.js b/eq-author/src/App/section/Design/SectionEditor/SectionIntroduction/withCreateSectionIntro.js deleted file mode 100644 index 352ad740fd..0000000000 --- a/eq-author/src/App/section/Design/SectionEditor/SectionIntroduction/withCreateSectionIntro.js +++ /dev/null @@ -1,21 +0,0 @@ -import { graphql } from "react-apollo"; - -import createSectionMutation from "./createSectionIntro.graphql"; - -export const mapMutateToProps = ({ mutate }) => ({ - createSectionIntro(id) { - return mutate({ - variables: { - input: { - sectionId: id, - introductionTitle: null, - introductionContent: null, - }, - }, - }); - }, -}); - -export default graphql(createSectionMutation, { - props: mapMutateToProps, -}); diff --git a/eq-author/src/App/section/Design/SectionEditor/SectionIntroduction/withCreateSectionIntro.test.js b/eq-author/src/App/section/Design/SectionEditor/SectionIntroduction/withCreateSectionIntro.test.js deleted file mode 100644 index c3d83ab5c1..0000000000 --- a/eq-author/src/App/section/Design/SectionEditor/SectionIntroduction/withCreateSectionIntro.test.js +++ /dev/null @@ -1,30 +0,0 @@ -import { mapMutateToProps } from "./withCreateSectionIntro"; - -describe("withCreateSectionIntro", () => { - describe("mapMutateToProps", () => { - let props; - let mutate; - - beforeEach(() => { - mutate = jest.fn(() => Promise.resolve()); - props = mapMutateToProps({ mutate }); - }); - - it("should have a createSectionIntro prop", () => { - expect(props.createSectionIntro).toBeInstanceOf(Function); - }); - - it("should call mutate", () => { - props.createSectionIntro("1"); - expect(mutate).toHaveBeenCalledWith({ - variables: { - input: { - sectionId: "1", - introductionContent: null, - introductionTitle: null, - }, - }, - }); - }); - }); -}); diff --git a/eq-author/src/App/section/Design/SectionEditor/SectionIntroduction/withDeleteSectionIntro.js b/eq-author/src/App/section/Design/SectionEditor/SectionIntroduction/withDeleteSectionIntro.js deleted file mode 100644 index db0e809910..0000000000 --- a/eq-author/src/App/section/Design/SectionEditor/SectionIntroduction/withDeleteSectionIntro.js +++ /dev/null @@ -1,37 +0,0 @@ -import { graphql } from "react-apollo"; - -import deleteSectionIntroMutation from "./deleteSectionIntro.graphql"; - -export const displayToast = (ownProps, sectionIntro) => { - const { id, introductionTitle, introductionContent } = sectionIntro; - ownProps.raiseToast( - `Section${id}`, - "Section introduction deleted", - "undeleteSectionIntroduction", - { - id, - introductionTitle, - introductionContent, - } - ); -}; - -export const mapMutateToProps = ({ ownProps, mutate }) => ({ - deleteSectionIntro(sectionIntro) { - const mutation = mutate({ - variables: { - input: { - sectionId: sectionIntro.id, - }, - }, - }); - - return mutation - .then(() => displayToast(ownProps, sectionIntro)) - .then(() => mutation); - }, -}); - -export default graphql(deleteSectionIntroMutation, { - props: mapMutateToProps, -}); diff --git a/eq-author/src/App/section/Design/SectionEditor/SectionIntroduction/withDeleteSectionIntro.test.js b/eq-author/src/App/section/Design/SectionEditor/SectionIntroduction/withDeleteSectionIntro.test.js deleted file mode 100644 index 286c1fa50b..0000000000 --- a/eq-author/src/App/section/Design/SectionEditor/SectionIntroduction/withDeleteSectionIntro.test.js +++ /dev/null @@ -1,52 +0,0 @@ -import { mapMutateToProps } from "./withDeleteSectionIntro"; - -describe("enhancers > withDeleteSectionIntro", () => { - describe("mapMutateToProps", () => { - let props; - let mutate; - let sectionIntro; - let ownProps; - - beforeEach(() => { - mutate = jest.fn(() => Promise.resolve()); - ownProps = { - raiseToast: jest.fn(() => Promise.resolve()), - }; - props = mapMutateToProps({ ownProps, mutate }); - sectionIntro = { - id: 1, - introductionContent: "bar", - introductionTitle: "foo", - }; - }); - - it("should have an onUpdateSection prop", () => { - expect(props.deleteSectionIntro).toBeInstanceOf(Function); - }); - - it("should call mutate", () => { - props.deleteSectionIntro(sectionIntro); - expect(mutate).toHaveBeenCalledWith({ - variables: { - input: { - sectionId: sectionIntro.id, - }, - }, - }); - }); - it("should raise a toast with old intro object", () => { - props.deleteSectionIntro(sectionIntro).then(() => { - expect(ownProps.raiseToast).toHaveBeenCalledWith( - `Section${sectionIntro.id}`, - "Section introduction deleted", - "undeleteSectionIntroduction", - { - id: sectionIntro.id, - introductionTitle: sectionIntro.introductionTitle, - introductionContent: sectionIntro.introductionContent, - } - ); - }); - }); - }); -}); diff --git a/eq-author/src/App/section/Design/SectionEditor/SectionIntroduction/withUpdateSectionIntro.js b/eq-author/src/App/section/Design/SectionEditor/SectionIntroduction/withUpdateSectionIntro.js deleted file mode 100644 index 3edc8b33c0..0000000000 --- a/eq-author/src/App/section/Design/SectionEditor/SectionIntroduction/withUpdateSectionIntro.js +++ /dev/null @@ -1,21 +0,0 @@ -import { graphql } from "react-apollo"; - -import updateSectionMutation from "./updateSectionIntro.graphql"; - -export const mapMutateToProps = ({ mutate }) => ({ - updateSectionIntro({ id, introductionTitle, introductionContent }) { - return mutate({ - variables: { - input: { - sectionId: id, - introductionTitle, - introductionContent, - }, - }, - }); - }, -}); - -export default graphql(updateSectionMutation, { - props: mapMutateToProps, -}); diff --git a/eq-author/src/App/section/Design/SectionEditor/SectionIntroduction/withUpdateSectionIntro.test.js b/eq-author/src/App/section/Design/SectionEditor/SectionIntroduction/withUpdateSectionIntro.test.js deleted file mode 100644 index d09edcac8e..0000000000 --- a/eq-author/src/App/section/Design/SectionEditor/SectionIntroduction/withUpdateSectionIntro.test.js +++ /dev/null @@ -1,34 +0,0 @@ -import { mapMutateToProps } from "./withUpdateSectionIntro"; - -describe("withUpdateSectionIntro", () => { - describe("mapMutateToProps", () => { - let props; - let mutate; - - beforeEach(() => { - mutate = jest.fn(() => Promise.resolve()); - props = mapMutateToProps({ mutate }); - }); - - it("should have a updateSectionIntro prop", () => { - expect(props.updateSectionIntro).toBeInstanceOf(Function); - }); - - it("should call mutate", () => { - props.updateSectionIntro({ - id: "1", - introductionTitle: "foo", - introductionContent: "bar", - }); - expect(mutate).toHaveBeenCalledWith({ - variables: { - input: { - sectionId: "1", - introductionTitle: "foo", - introductionContent: "bar", - }, - }, - }); - }); - }); -}); diff --git a/eq-author/src/App/section/Design/SectionEditor/__snapshots__/SectionEditor.test.js.snap b/eq-author/src/App/section/Design/SectionEditor/__snapshots__/SectionEditor.test.js.snap index 00a8ea31cc..82f4918ebb 100644 --- a/eq-author/src/App/section/Design/SectionEditor/__snapshots__/SectionEditor.test.js.snap +++ b/eq-author/src/App/section/Design/SectionEditor/__snapshots__/SectionEditor.test.js.snap @@ -26,8 +26,9 @@ exports[`SectionEditor should render 1`] = ` "emphasis": true, } } + description="Displayed in section navigation" id="section-title" - label="Title" + label="Section title" multiline={false} name="title" onUpdate={[Function]} @@ -36,10 +37,51 @@ exports[`SectionEditor should render 1`] = ` testSelector="txt-section-title" value="Section 1" /> + + Section introduction + + + + + - `; @@ -69,8 +111,9 @@ exports[`SectionEditor should render 2`] = ` "emphasis": true, } } + description="Displayed in section navigation" id="section-title" - label="Title" + label="Section title" multiline={false} name="title" onUpdate={[Function]} @@ -79,9 +122,50 @@ exports[`SectionEditor should render 2`] = ` testSelector="txt-section-title" value="Section 2" /> + + Section introduction + + + + + - `; diff --git a/eq-author/src/App/section/Design/SectionEditor/index.js b/eq-author/src/App/section/Design/SectionEditor/index.js index dc7d88d278..3b3858eabe 100644 --- a/eq-author/src/App/section/Design/SectionEditor/index.js +++ b/eq-author/src/App/section/Design/SectionEditor/index.js @@ -1,37 +1,48 @@ import React from "react"; import PropTypes from "prop-types"; +import { propType } from "graphql-anywhere"; import CustomPropTypes from "custom-prop-types"; import styled from "styled-components"; -import { flip, partial } from "lodash"; +import { get, flip, partial } from "lodash"; -import RichTextEditor from "components/RichTextEditor"; import DeleteConfirmDialog from "components/DeleteConfirmDialog"; -import MoveSectionModal from "App/section/Design/MoveSectionModal"; -import MoveSectionQuery from "App/section/Design/MoveSectionModal/MoveSectionQuery"; +import RichTextEditor from "components/RichTextEditor"; +import { DescribedLabel } from "components/Forms"; -import getIdForObject from "utils/getIdForObject"; +import { colors, radius } from "constants/theme"; import sectionFragment from "graphql/fragments/section.graphql"; -import iconSection from "App/section/Design/SectionEditor/icon-dialog-section.svg"; +import getIdForObject from "utils/getIdForObject"; -import SectionIntroduction from "./SectionIntroduction"; +import MoveSectionModal from "./MoveSectionModal"; +import MoveSectionQuery from "./MoveSectionModal/MoveSectionQuery"; +import iconSection from "./icon-dialog-section.svg"; const titleControls = { emphasis: true, }; const Padding = styled.div` - padding: 0 2em; + padding: 0 2em 2em; `; const SectionCanvas = styled.div` padding: 0; `; +const IntroCanvas = styled.div` + padding: 1.5em 1.5em 0; + border: 1px solid ${colors.bordersLight}; + background-color: ${colors.white}; + border-radius: ${radius} ${radius}; +`; + +const hasNavigation = section => get(section, ["questionnaire", "navigation"]); + class SectionEditor extends React.Component { static propTypes = { - section: CustomPropTypes.section.isRequired, + section: propType(sectionFragment), onChange: PropTypes.func.isRequired, onUpdate: PropTypes.func.isRequired, onDeleteSectionConfirm: PropTypes.func.isRequired, @@ -43,6 +54,20 @@ class SectionEditor extends React.Component { match: CustomPropTypes.match, }; + state = { + autoFocusTitle: false, + }; + + previousNav = hasNavigation(this.props.section); + + componentDidUpdate(prevProps) { + if ( + hasNavigation(prevProps.section) !== hasNavigation(this.props.section) + ) { + this.previousNav = hasNavigation(this.props.section); + } + } + renderMoveSectionModal = ({ loading, error, data }) => { const { onMoveSectionDialog, @@ -77,6 +102,12 @@ class SectionEditor extends React.Component { } = this.props; const handleUpdate = partial(flip(onChange), onUpdate); + const navHasChanged = + this.previousNav !== hasNavigation(this.props.section); + const hasTitle = this.props.section.title; + + const autoFocusTitle = !navHasChanged && !hasTitle; + return ( - + {section.questionnaire.navigation && ( + + )} + + Section introduction + + + + + - ); } diff --git a/eq-author/src/App/section/Design/index.js b/eq-author/src/App/section/Design/index.js index 5f343f6ecd..62a901ceca 100644 --- a/eq-author/src/App/section/Design/index.js +++ b/eq-author/src/App/section/Design/index.js @@ -178,7 +178,9 @@ export class UnwrappedSectionRoute extends React.Component { render() { const { section = {} } = this.props; - const isPreviewEnabled = Boolean(section.introduction); + const hasIntroductionContent = Boolean( + section.introductionTitle || section.introductionContent + ); return ( {this.renderContent()} diff --git a/eq-author/src/App/section/Design/index.test.js b/eq-author/src/App/section/Design/index.test.js index 8e92793e56..2cd5a3dea0 100644 --- a/eq-author/src/App/section/Design/index.test.js +++ b/eq-author/src/App/section/Design/index.test.js @@ -36,7 +36,8 @@ const moveSectionMock = { id: "2", title: "foo", alias: "foo-alias", - introduction: null, + introductionTitle: "", + introductionContent: "", displayName: "foo", position: 0, pages: [ @@ -60,6 +61,7 @@ const moveSectionMock = { questionnaire: { __typename: "Questionnaire", id: "1", + navigation: true, questionnaireInfo: { __typename: "QuestionnaireInfo", totalSectionCount: 1, @@ -72,9 +74,8 @@ const moveSectionMock = { title: "foo", alias: "foo-alias", displayName: "foo", - introductionTitle: null, - introductionContent: null, - introductionEnabled: false, + introductionTitle: "", + introductionContent: "", position: 1, pages: [ { @@ -97,6 +98,7 @@ const moveSectionMock = { questionnaire: { __typename: "Questionnaire", id: "1", + navigation: true, questionnaireInfo: { __typename: "QuestionnaireInfo", totalSectionCount: 1, @@ -160,11 +162,13 @@ describe("SectionRoute", () => { alias: "foo-alias", displayName: "foo", description: "bar", - introduction: null, + introductionTitle: "", + introductionContent: "", position: 0, questionnaire: { __typename: "Questionnaire", id: "1", + navigation: true, questionnaireInfo: { __typename: "QuestionnaireInfo", totalSectionCount: 1, @@ -197,11 +201,13 @@ describe("SectionRoute", () => { alias: "foo-alias", displayName: "foo", description: "bar", - introduction: null, + introductionTitle: "", + introductionContent: "", position: 0, questionnaire: { __typename: "Questionnaire", id: "1", + navigation: true, questionnaireInfo: { __typename: "QuestionnaireInfo", totalSectionCount: 1, @@ -286,8 +292,11 @@ describe("SectionRoute", () => { description: "bar", introduction: null, position: 0, + introductionTitle: "", + introductionContent: "", questionnaire: { id: "1", + navigation: true, questionnaireInfo: { totalSectionCount: 1, }, @@ -361,6 +370,7 @@ describe("SectionRoute", () => { it("should enable move section button when multiple sections", () => { const questionnaire = { id: "1", + navigation: true, questionnaireInfo: { totalSectionCount: 2, }, @@ -381,6 +391,7 @@ describe("SectionRoute", () => { it("should call onDuplicateSection with the section id and position when the duplicate button is clicked", () => { const questionnaire = { id: "1", + navigation: true, questionnaireInfo: { totalSectionCount: 2, }, @@ -426,11 +437,8 @@ describe("SectionRoute", () => { match, section: { ...section, - introduction: { - id: "2", - introductionTitle: "", - introductionContent: "", - }, + introductionTitle: "Title", + introductionContent: "Content", }, ...mockHandlers, }); diff --git a/eq-author/src/App/section/Design/withUpdateSection.js b/eq-author/src/App/section/Design/withUpdateSection.js index ca8f9b4db9..ede3a8e436 100644 --- a/eq-author/src/App/section/Design/withUpdateSection.js +++ b/eq-author/src/App/section/Design/withUpdateSection.js @@ -3,9 +3,17 @@ import { graphql } from "react-apollo"; import updateSectionMutation from "graphql/updateSection.graphql"; export const mapMutateToProps = ({ mutate }) => ({ - onUpdateSection({ id, title, alias }) { + onUpdateSection({ + id, + title, + alias, + introductionTitle, + introductionContent, + }) { return mutate({ - variables: { input: { id, title, alias } }, + variables: { + input: { id, title, alias, introductionTitle, introductionContent }, + }, }); }, }); diff --git a/eq-author/src/App/section/Preview/SectionIntroPreview.js b/eq-author/src/App/section/Preview/SectionIntroPreview.js index 6da589b1a2..f1b1fab4c0 100644 --- a/eq-author/src/App/section/Preview/SectionIntroPreview.js +++ b/eq-author/src/App/section/Preview/SectionIntroPreview.js @@ -2,7 +2,7 @@ import React from "react"; import styled from "styled-components"; -import fragment from "App/section/Design/SectionEditor/SectionIntroduction/SectionIntroductionFragment.graphql"; +import fragment from "graphql/fragments/section.graphql"; import Error from "components/preview/Error"; import { propType } from "graphql-anywhere"; @@ -42,7 +42,7 @@ const TitleBlock = styled.h1` `; const SectionIntroPreview = ({ - introduction: { introductionTitle, introductionContent }, + section: { introductionTitle, introductionContent }, }) => ( @@ -63,7 +63,7 @@ const SectionIntroPreview = ({ ); SectionIntroPreview.propTypes = { - introduction: propType(fragment).isRequired, + section: propType(fragment).isRequired, }; export default SectionIntroPreview; diff --git a/eq-author/src/App/section/Preview/SectionIntroPreview.test.js b/eq-author/src/App/section/Preview/SectionIntroPreview.test.js index 2578958cf3..030c51837c 100644 --- a/eq-author/src/App/section/Preview/SectionIntroPreview.test.js +++ b/eq-author/src/App/section/Preview/SectionIntroPreview.test.js @@ -4,36 +4,36 @@ import { shallow } from "enzyme"; import SectionIntroPreview from "./SectionIntroPreview"; describe("SectionIntroPreview", () => { - let introduction; + let section; beforeEach(() => { - introduction = { + section = { id: "1", + title: "", + alias: "", introductionTitle: "

title

", introductionContent: "

Content

", + questionnaire: { + id: "1", + navigation: true, + }, }; }); it("should render with all content", () => { - const wrapper = shallow( - - ); + const wrapper = shallow(); expect(wrapper).toMatchSnapshot(); }); it("should render an error when the title is missing", () => { - introduction.introductionTitle = ""; - const wrapper = shallow( - - ); + section.introductionTitle = ""; + const wrapper = shallow(); expect(wrapper).toMatchSnapshot(); }); it("should render an error when the content is missing", () => { - introduction.introductionContent = ""; - const wrapper = shallow( - - ); + section.introductionContent = ""; + const wrapper = shallow(); expect(wrapper).toMatchSnapshot(); }); }); diff --git a/eq-author/src/App/section/Preview/__snapshots__/index.test.js.snap b/eq-author/src/App/section/Preview/__snapshots__/index.test.js.snap index 58e16789d7..42644f3a16 100644 --- a/eq-author/src/App/section/Preview/__snapshots__/index.test.js.snap +++ b/eq-author/src/App/section/Preview/__snapshots__/index.test.js.snap @@ -29,11 +29,17 @@ exports[`PreviewSectionRoute should show the section intro preview when it is fi preview={true} > diff --git a/eq-author/src/App/section/Preview/index.js b/eq-author/src/App/section/Preview/index.js index eeafb995a5..4d052c7172 100644 --- a/eq-author/src/App/section/Preview/index.js +++ b/eq-author/src/App/section/Preview/index.js @@ -20,13 +20,16 @@ export const UnwrappedPreviewSectionRoute = ({ match, data, loading }) => { const { section } = data; - if (!section.introduction) { + const hasIntroductionContent = + section.introductionTitle || section.introductionContent; + + if (!hasIntroductionContent) { return ; } return ( - + ); }; diff --git a/eq-author/src/App/section/Preview/index.test.js b/eq-author/src/App/section/Preview/index.test.js index 3fea31f588..4f34cebab7 100644 --- a/eq-author/src/App/section/Preview/index.test.js +++ b/eq-author/src/App/section/Preview/index.test.js @@ -36,10 +36,11 @@ describe("PreviewSectionRoute", () => { id: "1", alias: "", title: "", - introduction: { - id: "1", - introductionTitle: "", - introductionContent: "", + introductionTitle: "intro title", + introductionContent: "intro content", + questionnaire: { + id: "2", + navigation: true, }, }, }} @@ -58,7 +59,12 @@ describe("PreviewSectionRoute", () => { id: "1", alias: "", title: "", - introduction: null, + introductionTitle: "", + introductionContent: "", + questionnaire: { + id: "2", + navigation: true, + }, }, }} /> diff --git a/eq-author/src/components/Forms/DescribedLabel/__snapshots__/index.test.js.snap b/eq-author/src/components/Forms/DescribedLabel/__snapshots__/index.test.js.snap new file mode 100644 index 0000000000..fc84848bdb --- /dev/null +++ b/eq-author/src/components/Forms/DescribedLabel/__snapshots__/index.test.js.snap @@ -0,0 +1,22 @@ +// Jest Snapshot v1, https://goo.gl/fbAQLP + +exports[`DescribedLabel should render as just a label if no description provided 1`] = ` + +`; + +exports[`DescribedLabel should render with a label and description when description is provided 1`] = ` + +`; diff --git a/eq-author/src/components/Forms/DescribedLabel/index.js b/eq-author/src/components/Forms/DescribedLabel/index.js new file mode 100644 index 0000000000..8c7f40ca64 --- /dev/null +++ b/eq-author/src/components/Forms/DescribedLabel/index.js @@ -0,0 +1,35 @@ +import React from "react"; +import styled from "styled-components"; +import PropTypes from "prop-types"; + +import Label from "components/Forms/Label"; + +const MainLabel = styled("span")` + display: block; +`; + +const SubLabel = styled("span")` + font-weight: normal; + font-size: 0.9em; + display: block; +`; + +const DescribedLabel = ({ description, children, ...otherProps }) => { + if (!description) { + return ; + } + + return ( + + ); +}; + +DescribedLabel.propTypes = { + description: PropTypes.string, + children: PropTypes.node.isRequired, +}; + +export default DescribedLabel; diff --git a/eq-author/src/components/Forms/DescribedLabel/index.test.js b/eq-author/src/components/Forms/DescribedLabel/index.test.js new file mode 100644 index 0000000000..6db0b89290 --- /dev/null +++ b/eq-author/src/components/Forms/DescribedLabel/index.test.js @@ -0,0 +1,18 @@ +import React from "react"; +import { shallow } from "enzyme"; + +import DescribedLabel from "./"; + +describe("DescribedLabel", () => { + it("should render with a label and description when description is provided", () => { + const wrapper = shallow( + Label things + ); + expect(wrapper).toMatchSnapshot(); + }); + + it("should render as just a label if no description provided", () => { + const wrapper = shallow(Label things); + expect(wrapper).toMatchSnapshot(); + }); +}); diff --git a/eq-author/src/components/Forms/index.js b/eq-author/src/components/Forms/index.js index ae042c9fcb..6f285c16bb 100644 --- a/eq-author/src/components/Forms/index.js +++ b/eq-author/src/components/Forms/index.js @@ -5,3 +5,4 @@ export { default as Select } from "./Select"; export { default as TextArea } from "./TextArea"; export { default as Field } from "./Field"; export { default as Number } from "./Number"; +export { default as DescribedLabel } from "./DescribedLabel"; diff --git a/eq-author/src/components/RichTextEditor/RichTextEditor.test.js b/eq-author/src/components/RichTextEditor/RichTextEditor.test.js index 452aa1036e..2fbf1be5ba 100644 --- a/eq-author/src/components/RichTextEditor/RichTextEditor.test.js +++ b/eq-author/src/components/RichTextEditor/RichTextEditor.test.js @@ -139,12 +139,6 @@ describe("components/RichTextEditor", function() { expect(editorFocus).toHaveBeenCalled(); }); - it("should autoFocus", () => { - wrapper.instance().setEditorInstance(editorInstance); - wrapper.setProps({ autoFocus: true }); - expect(editorFocus).toHaveBeenCalled(); - }); - it("should autoFocus on mount if prop set", () => { wrapper = shallow(, { disableLifecycleMethods: true, diff --git a/eq-author/src/components/RichTextEditor/__snapshots__/RichTextEditor.test.js.snap b/eq-author/src/components/RichTextEditor/__snapshots__/RichTextEditor.test.js.snap index afb015780e..ee755fed2f 100644 --- a/eq-author/src/components/RichTextEditor/__snapshots__/RichTextEditor.test.js.snap +++ b/eq-author/src/components/RichTextEditor/__snapshots__/RichTextEditor.test.js.snap @@ -12,12 +12,11 @@ exports[`components/RichTextEditor should allow multiline input 1`] = ` onFocus={[Function]} onMouseDown={[Function]} > - + @@ -262,12 +261,11 @@ exports[`components/RichTextEditor should render 1`] = ` onFocus={[Function]} onMouseDown={[Function]} > - + @@ -514,12 +512,11 @@ exports[`components/RichTextEditor should render existing content 1`] = ` onFocus={[Function]} onMouseDown={[Function]} > - + diff --git a/eq-author/src/components/RichTextEditor/index.js b/eq-author/src/components/RichTextEditor/index.js index d9d02c2c39..17cb02c37b 100644 --- a/eq-author/src/components/RichTextEditor/index.js +++ b/eq-author/src/components/RichTextEditor/index.js @@ -23,7 +23,7 @@ import cheerio from "cheerio"; import { flow, uniq, map, keyBy, mapValues, isNull, trim } from "lodash/fp"; import { sharedStyles } from "components/Forms/css"; -import { Field, Label } from "components/Forms"; +import { Field, DescribedLabel } from "components/Forms"; const styleMap = { ITALIC: { @@ -165,6 +165,7 @@ class RichTextEditor extends React.Component { alias: PropTypes.string, }) ), + description: PropTypes.string, }; constructor(props) { @@ -197,9 +198,6 @@ class RichTextEditor extends React.Component { } componentDidUpdate(prevProps) { - if (!prevProps.autoFocus && this.props.autoFocus) { - this.focus(); - } /*eslint-disable react/no-did-update-set-state */ if ( prevProps.value !== this.props.value && @@ -454,6 +452,7 @@ class RichTextEditor extends React.Component { testSelector, id, placeholder, + description, ...otherProps } = this.props; @@ -466,7 +465,9 @@ class RichTextEditor extends React.Component { onFocus={this.handleFocus} data-test="rte-field" > - + + {label} + { - return { - type: UNDELETE_SECTION_INTRODUCTION_REQUEST, - }; -}; - -const undeleteSuccess = () => { - return { - type: UNDELETE_SECTION_INTRODUCTION_SUCCESS, - }; -}; - -const undeleteFailure = () => { - return { - type: UNDELETE_SECTION_INTRODUCTION_FAILURE, - }; -}; - -export const createUpdate = () => (proxy, result) => { - const id = `Section${result.data.createSectionIntroduction.id}`; - const section = proxy.readFragment({ id, fragment }); - - proxy.writeFragment({ - id, - fragment, - data: { ...section, introduction: result.data.createSectionIntroduction }, - }); -}; - -export const createUndelete = mutate => ({ - id, - introductionTitle, - introductionContent, -}) => - mutate({ - variables: { - input: { sectionId: id, introductionTitle, introductionContent }, - }, - update: createUpdate({ id, introductionTitle, introductionContent }), - }); - -export const undeleteSectionIntroduction = (id, introduction) => { - return (dispatch, getState, { client }) => { - const undelete = createUndelete(createMutate(client, query)); - dispatch(undeleteRequest()); - return undelete(introduction) - .then(() => dispatch(undeleteSuccess())) - .catch(() => dispatch(undeleteFailure())); - }; -}; diff --git a/eq-author/src/redux/undelete/undeleteSectionIntroduction.test.js b/eq-author/src/redux/undelete/undeleteSectionIntroduction.test.js deleted file mode 100644 index 1ee21a3cce..0000000000 --- a/eq-author/src/redux/undelete/undeleteSectionIntroduction.test.js +++ /dev/null @@ -1,144 +0,0 @@ -import { - createUndelete, - undeleteSectionIntroduction, - createUpdate, -} from "./undeleteSectionIntroduction"; -import thunk from "redux-thunk"; -import configureStore from "redux-mock-store"; -import fragment from "graphql/fragments/section.graphql"; - -describe("undelete Section Introduction", () => { - let introduction; - let dispatch, getState, client, mutate, proxy, result, context; - - beforeEach(() => { - dispatch = jest.fn(); - getState = jest.fn(); - mutate = jest.fn(() => Promise.resolve()); - - client = { - mutate, - }; - introduction = { - id: "1", - introductionTitle: "Foo", - introductionContent: "Bar", - }; - - context = { - sectionId: 1, - introductionTitle: "Foo", - introductionContent: "Bar", - }; - - result = { - data: { - createSectionIntroduction: { - id: 1, - introductionTitle: "Foo", - introductionContent: "Bar", - }, - }, - }; - - proxy = { - readFragment: jest.fn(() => ({ introduction: null })), - writeFragment: jest.fn(), - }; - }); - - it("should return a thunk function", () => { - expect(undeleteSectionIntroduction("Section1", introduction)).toEqual( - expect.any(Function) - ); - }); - - it("should call dispatch asynchronously when thunk is invoked", () => { - const thunk = undeleteSectionIntroduction("Section1", introduction); - thunk(dispatch, getState, { client }).then(() => { - expect(dispatch).toHaveBeenCalledTimes(2); - }); - }); - - it("should pass section to mutate", () => { - createUndelete(mutate)(introduction); - expect(mutate).toHaveBeenCalledWith({ - update: expect.any(Function), - variables: { - input: { - sectionId: introduction.id, - introductionContent: introduction.introductionContent, - introductionTitle: introduction.introductionTitle, - }, - }, - }); - }); - - describe("createUpdate", () => { - it("should call the correct query on the proxy", () => { - createUpdate(context)(proxy, result); - expect(proxy.readFragment).toHaveBeenCalledWith({ - id: "Section1", - fragment, - }); - }); - - it("should write data back to the proxy", () => { - createUpdate(context)(proxy, result); - expect(proxy.writeFragment).toHaveBeenCalledWith({ - id: "Section1", - fragment, - data: { - introduction: { - id: 1, - introductionTitle: "Foo", - introductionContent: "Bar", - }, - }, - }); - }); - }); - - describe("undelete section introduction intergration test", () => { - let middleware; - let mockStore; - let store; - - beforeEach(() => { - middleware = [thunk.withExtraArgument({ client })]; - mockStore = configureStore(middleware); - store = mockStore({}); - }); - - it("should send a request then a success action", () => { - return store - .dispatch(undeleteSectionIntroduction("Section1", introduction)) - .then(() => { - expect(store.getActions()).toEqual([ - { - type: "UNDELETE_SECTION_INTRODUCTION_REQUEST", - }, - { - type: "UNDELETE_SECTION_INTRODUCTION_SUCCESS", - }, - ]); - }); - }); - - it("should send a request then an error action", () => { - client.mutate = jest.fn(() => Promise.reject()); - return store - .dispatch(undeleteSectionIntroduction("Section1", introduction)) - .then(() => { - expect(store.getActions()).toEqual([ - { - type: "UNDELETE_SECTION_INTRODUCTION_REQUEST", - }, - { - type: "UNDELETE_SECTION_INTRODUCTION_FAILURE", - }, - ]); - }); - }); - }); -}); diff --git a/eq-publisher/src/api/queries.js b/eq-publisher/src/api/queries.js index 517233b717..3ce317143b 100644 --- a/eq-publisher/src/api/queries.js +++ b/eq-publisher/src/api/queries.js @@ -174,10 +174,8 @@ exports.getQuestionnaire = ` sections { id title - introduction { - introductionTitle - introductionContent - } + introductionTitle + introductionContent pages { ... on QuestionPage { id diff --git a/eq-publisher/src/eq_schema/Block.js b/eq-publisher/src/eq_schema/Block.js index 6f8ee1dd4c..e6bedef14d 100644 --- a/eq-publisher/src/eq_schema/Block.js +++ b/eq-publisher/src/eq_schema/Block.js @@ -53,11 +53,7 @@ class Block { } } - static buildIntroBlock( - { introductionTitle, introductionContent }, - groupId, - ctx - ) { + static buildIntroBlock(introductionTitle, introductionContent, groupId, ctx) { return { type: "Interstitial", id: `group${groupId}-introduction`, diff --git a/eq-publisher/src/eq_schema/Block.test.js b/eq-publisher/src/eq_schema/Block.test.js index 5f139dbf6d..63e094f738 100644 --- a/eq-publisher/src/eq_schema/Block.test.js +++ b/eq-publisher/src/eq_schema/Block.test.js @@ -114,14 +114,9 @@ describe("Block", () => { }); it("should handle piped values in title", () => { - // noinspection JSAnnotator - let introduction = { - introductionTitle: createPipeInText(), - introductionContent: "", - }; - const introBlock = Block.buildIntroBlock( - introduction, + createPipeInText(), + "", 0, createContext() ); @@ -130,14 +125,9 @@ describe("Block", () => { }); it("should handle piped values in title while stripping html", () => { - // noinspection JSAnnotator - let introduction = { - introductionTitle: createPipeInHtml(), - introductionContent: "", - }; - const introBlock = Block.buildIntroBlock( - introduction, + createPipeInHtml(), + "", 0, createContext() ); @@ -146,14 +136,9 @@ describe("Block", () => { }); it("should handle piped values in description", () => { - // noinspection JSAnnotator - let introduction = { - introductionTitle: "", - introductionContent: createPipeInHtml(), - }; - const introBlock = Block.buildIntroBlock( - introduction, + "", + createPipeInHtml(), 0, createContext() ); diff --git a/eq-publisher/src/eq_schema/Group.js b/eq-publisher/src/eq_schema/Group.js index 017bb6a5ce..c903fb2756 100644 --- a/eq-publisher/src/eq_schema/Group.js +++ b/eq-publisher/src/eq_schema/Group.js @@ -1,16 +1,15 @@ /* eslint-disable camelcase */ const Block = require("./Block"); -const { getInnerHTML } = require("../utils/HTMLUtils"); const { isEmpty, reject, flatten } = require("lodash"); const { buildAuthorConfirmationQuestion, } = require("./builders/confirmationPage/ConfirmationPage"); class Group { - constructor(id, title, pages, introduction, ctx) { - this.id = `group${id}`; - this.title = getInnerHTML(title); - this.blocks = this.buildBlocks(pages, id, introduction, ctx); + constructor(title, section, ctx) { + this.id = `group${section.id}`; + this.title = ctx.questionnaireJson.navigation ? title : ""; + this.blocks = this.buildBlocks(section, ctx); if (!isEmpty(ctx.routingGotos)) { this.filterContext(this.id, ctx); @@ -37,16 +36,16 @@ class Group { ); } - buildBlocks(pages, groupId, introduction, ctx) { + buildBlocks(section, ctx) { const blocks = flatten( - pages.map(page => { - const block = new Block(page, groupId, ctx); + section.pages.map(page => { + const block = new Block(page, section.id, ctx); if (page.confirmation) { return [ block, buildAuthorConfirmationQuestion( page, - groupId, + section.id, page.routingRuleSet, page.routing, ctx @@ -57,10 +56,18 @@ class Group { }) ); - if (!introduction) { + if (!section.introductionTitle || !section.introductionContent) { return blocks; } - return [Block.buildIntroBlock(introduction, groupId, ctx), ...blocks]; + return [ + Block.buildIntroBlock( + section.introductionTitle, + section.introductionContent, + section.id, + ctx + ), + ...blocks, + ]; } } diff --git a/eq-publisher/src/eq_schema/Group.test.js b/eq-publisher/src/eq_schema/Group.test.js index f0e744c4e8..cf2d53f701 100644 --- a/eq-publisher/src/eq_schema/Group.test.js +++ b/eq-publisher/src/eq_schema/Group.test.js @@ -1,15 +1,22 @@ /* eslint-disable camelcase */ const Group = require("./Group"); const Block = require("./Block"); -const ctx = {}; describe("Group", () => { + const createCtx = (options = {}) => ({ + questionnaireJson: { + navigation: true, + }, + ...options, + }); + const createGroupJSON = options => Object.assign( { id: "1", title: "Section 1", - introduction: null, + introductionTitle: "", + introductionContent: "", pages: [ { id: "2", @@ -22,13 +29,7 @@ describe("Group", () => { it("should build valid runner Group from Author section", () => { let groupJSON = createGroupJSON(); - const group = new Group( - groupJSON.id, - groupJSON.title, - groupJSON.pages, - groupJSON.introduction, - ctx - ); + const group = new Group(groupJSON.title, groupJSON, createCtx()); expect(group).toMatchObject({ id: "group1", @@ -37,34 +38,25 @@ describe("Group", () => { }); }); - it("should handle HTML values", () => { - let groupJSON = createGroupJSON({ title: "

Section 1

" }); + it("should output an empty title when the navigation is disabled", () => { + let groupJSON = createGroupJSON(); const group = new Group( - groupJSON.id, groupJSON.title, - groupJSON.pages, - groupJSON.introduction, - ctx + groupJSON, + createCtx({ questionnaireJson: { navigation: false } }) ); - expect(group).toMatchObject({ - title: "Section 1", + title: "", }); }); it("returns a schema with an introduction when there is one", () => { - const groupJSON = createGroupJSON(); + const groupJSON = createGroupJSON({ + introductionTitle: "Intro Title", + introductionContent: "Intro Content", + }); - const runnerJSON = new Group( - groupJSON.id, - groupJSON.title, - groupJSON.pages, - { - introductionTitle: "Intro Title", - introductionContent: "Intro Content", - }, - ctx - ); + const runnerJSON = new Group(groupJSON.title, groupJSON, createCtx()); expect(runnerJSON).toMatchObject({ id: "group1", @@ -81,45 +73,25 @@ describe("Group", () => { }); }); - it("returns a schema with an empty introduction when null title and content", () => { - const groupJSON = createGroupJSON(); + it("returns no introduction when null title and content", () => { + const groupJSON = createGroupJSON({ + introductionTitle: null, + introductionContent: null, + }); - const runnerJSON = new Group( - groupJSON.id, - groupJSON.title, - groupJSON.pages, - { - introductionTitle: null, - introductionContent: null, - }, - ctx - ); + const runnerJSON = new Group(groupJSON.title, groupJSON, createCtx()); expect(runnerJSON).toMatchObject({ id: "group1", title: "Section 1", - blocks: [ - { - id: "group1-introduction", - title: "", - description: "", - type: "Interstitial", - }, - expect.any(Block), - ], + blocks: [expect.any(Block)], }); }); it("returns a schema without an introduction when it is disabled", () => { const groupJSON = createGroupJSON(); - const runnerJSON = new Group( - groupJSON.id, - groupJSON.title, - groupJSON.pages, - groupJSON.introduction, - ctx - ); + const runnerJSON = new Group(groupJSON.title, groupJSON, createCtx()); expect(runnerJSON.blocks).toHaveLength(1); }); @@ -130,26 +102,29 @@ describe("Group", () => { id: 1, title: "Group 1", pages: [], - introduction: null, + introductionTitle: "", + introductionContent: "", }, { id: 2, title: "Group 2", pages: [], - introduction: null, + introductionTitle: "", + introductionContent: "", }, { id: 3, title: "Group 3", pages: [], - introduction: null, + introductionTitle: "", + introductionContent: "", }, ]; it("should add skip conditions to the required groups", () => { const groupsJson = createGroupsJSON(); - const ctx = { + const ctx = createCtx({ routingGotos: [ { group: "confirmation-group", @@ -161,11 +136,10 @@ describe("Group", () => { groupId: "group1", }, ], - }; + }); const runnerJson = groupsJson.map( - group => - new Group(group.id, group.title, group.pages, group.introduction, ctx) + group => new Group(group.title, group, ctx) ); const expectedrunnerJson = [ @@ -196,7 +170,7 @@ describe("Group", () => { it("can handle multiple skip conditions for each group", () => { const groupsJson = createGroupsJSON(); - const ctx = { + const ctx = createCtx({ routingGotos: [ { group: "confirmation-group", @@ -217,11 +191,10 @@ describe("Group", () => { groupId: "group1", }, ], - }; + }); const runnerJson = groupsJson.map( - group => - new Group(group.id, group.title, group.pages, group.introduction, ctx) + group => new Group(group.title, group, ctx) ); const expectedrunnerJson = [ @@ -311,10 +284,8 @@ describe("Group", () => { it("should build a confirmation page", () => { const ctx = ctxGenerator(null); const resultantJson = new Group( - ctx.questionnaireJson.sections[0].id, - ctx.questionnaireJson.sections[0].title, - ctx.questionnaireJson.sections[0].pages, - null, + "Section 1", + ctx.questionnaireJson.sections[0], ctx ); @@ -420,10 +391,8 @@ describe("Group", () => { const ctx = ctxGenerator(null, routing); const resultantJson = new Group( - ctx.questionnaireJson.sections[0].id, - ctx.questionnaireJson.sections[0].title, - ctx.questionnaireJson.sections[0].pages, - null, + "Group Title", + ctx.questionnaireJson.sections[0], ctx ); @@ -494,17 +463,12 @@ describe("Group", () => { }, }; - const resultantJson = new Group( - ctx.questionnaireJson.sections[0].id, - ctx.questionnaireJson.sections[0].title, - ctx.questionnaireJson.sections[0].pages, - null, - ctx - ); + const section = ctx.questionnaireJson.sections[0]; + const resultantJson = new Group(section.title, section, ctx); expect(resultantJson.blocks[1].questions[0].description).toEqual( `{{ answers['answer${ - ctx.questionnaireJson.sections[0].pages[0].answers[0].id + section.pages[0].answers[0].id }']|format_unordered_list }}` ); }); diff --git a/eq-publisher/src/eq_schema/Questionnaire.test.js b/eq-publisher/src/eq_schema/Questionnaire.test.js index 5ad3af0ba0..84b3a255cd 100644 --- a/eq-publisher/src/eq_schema/Questionnaire.test.js +++ b/eq-publisher/src/eq_schema/Questionnaire.test.js @@ -110,6 +110,7 @@ describe("Questionnaire", () => { it("should strip out HTML from navigation sections", () => { const questionnaire = new Questionnaire( createQuestionnaireJSON({ + navigation: true, sections: [ { id: "2", diff --git a/eq-publisher/src/eq_schema/Section.js b/eq-publisher/src/eq_schema/Section.js index 6e715387d9..c476be537f 100644 --- a/eq-publisher/src/eq_schema/Section.js +++ b/eq-publisher/src/eq_schema/Section.js @@ -4,15 +4,15 @@ const { getText } = require("../utils/HTMLUtils"); class Section { constructor(section, ctx) { this.id = `section${section.id}`; - this.title = getText(section.title); - this.groups = this.buildGroups(section.id, this.title, section, ctx); + if (ctx.questionnaireJson.navigation) { + this.title = getText(section.title); + } + this.groups = this.buildGroups(section, ctx); } - buildGroups(id, title, section, ctx) { - const { pages, introduction } = section; - + buildGroups(section, ctx) { // Sections always contain a single group currently - return [new Group(id, title, pages, introduction, ctx)]; + return [new Group(getText(section.title), section, ctx)]; } } diff --git a/eq-publisher/src/eq_schema/Section.test.js b/eq-publisher/src/eq_schema/Section.test.js index 0de4c730a8..6facdf6306 100644 --- a/eq-publisher/src/eq_schema/Section.test.js +++ b/eq-publisher/src/eq_schema/Section.test.js @@ -1,6 +1,5 @@ const Block = require("./Block"); const Section = require("./Section"); -const ctx = { routingGotos: [] }; describe("Section", () => { const createSectionJSON = options => @@ -17,9 +16,14 @@ describe("Section", () => { }, options ); + const createCtx = (options = {}) => ({ + routingGotos: [], + questionnaireJson: { navigation: true }, + ...options, + }); it("should build valid runner Section from Author section", () => { - const section = new Section(createSectionJSON(), ctx); + const section = new Section(createSectionJSON(), createCtx()); expect(section).toMatchObject({ id: "section1", @@ -33,4 +37,22 @@ describe("Section", () => { ], }); }); + + it("should not output title when navigation is disabled", () => { + const section = new Section( + createSectionJSON(), + createCtx({ questionnaireJson: { navigation: false } }) + ); + + expect(section).toMatchObject({ + id: "section1", + groups: [ + { + id: "group1", + title: "", + blocks: [expect.any(Block)], + }, + ], + }); + }); });