diff --git a/editor.planx.uk/src/@planx/components/Confirmation/Public.test.tsx b/editor.planx.uk/src/@planx/components/Confirmation/Public.test.tsx index 5d98b00970..0fb6c1807d 100644 --- a/editor.planx.uk/src/@planx/components/Confirmation/Public.test.tsx +++ b/editor.planx.uk/src/@planx/components/Confirmation/Public.test.tsx @@ -1,3 +1,6 @@ +import { ComponentType as TYPES } from "@opensystemslab/planx-core/types"; +import { act, screen, waitFor } from "@testing-library/react"; +import { FullStore, useStore } from "pages/FlowEditor/lib/store"; import React from "react"; import { setup } from "testUtils"; import { vi } from "vitest"; @@ -5,6 +8,10 @@ import { axe } from "vitest-axe"; import ConfirmationComponent from "./Public"; +const { getState, setState } = useStore; + +let initialState: FullStore; + vi.mock("@opensystemslab/planx-core", () => { return { CoreDomainClient: vi.fn().mockImplementation(() => ({ @@ -33,3 +40,85 @@ it("should not have any accessibility violations", async () => { const results = await axe(container); expect(results).toHaveNoViolations(); }); + +describe("Confirmation component", () => { + const handleSubmit = vi.fn(); + + beforeAll(() => (initialState = getState())); + + afterEach(() => waitFor(() => setState(initialState))); + + it("hides the 'Continue' button if it's the final card in the flow", () => { + act(() => + setState({ + flow: { + _root: { edges: ["Send", "Confirmation"] }, + Send: { type: TYPES.Send }, + Confirmation: { type: TYPES.Confirmation }, + }, + breadcrumbs: { Send: { auto: false } }, + }), + ); + + expect(getState().upcomingCardIds()).toEqual(["Confirmation"]); + expect(getState().isFinalCard()).toEqual(true); + + const { user } = setup( + , + ); + + expect(screen.queryByText("Continue")).not.toBeInTheDocument(); + }); + + it("shows the 'Continue' button if there are nodes following it", () => { + act(() => + setState({ + flow: { + _root: { edges: ["Send", "Confirmation", "Feedback", "Notice"] }, + Send: { type: TYPES.Send }, + Confirmation: { type: TYPES.Confirmation }, + Feedback: { type: TYPES.Feedback }, + Notice: { type: TYPES.Notice }, + }, + breadcrumbs: { Send: { auto: false } }, + }), + ); + + expect(getState().upcomingCardIds()).toEqual([ + "Confirmation", + "Feedback", + "Notice", + ]); + expect(getState().isFinalCard()).toEqual(false); + + const { user } = setup( + , + ); + + expect(screen.queryByText("Continue")).toBeInTheDocument(); + }); +}); diff --git a/editor.planx.uk/src/@planx/components/Confirmation/Public.tsx b/editor.planx.uk/src/@planx/components/Confirmation/Public.tsx index f6cd94fd2c..661d663f4f 100644 --- a/editor.planx.uk/src/@planx/components/Confirmation/Public.tsx +++ b/editor.planx.uk/src/@planx/components/Confirmation/Public.tsx @@ -85,6 +85,7 @@ interface PresentationalProps extends Props { } export function Presentational(props: PresentationalProps) { + const isFinalCard = useStore().isFinalCard(); return ( )} - + {Object.entries(props.applicableDetails).map(([k, v], i) => ( diff --git a/editor.planx.uk/src/@planx/components/shared/Preview/Card.test.tsx b/editor.planx.uk/src/@planx/components/shared/Preview/Card.test.tsx index 949d8abe02..b7c40da8e8 100644 --- a/editor.planx.uk/src/@planx/components/shared/Preview/Card.test.tsx +++ b/editor.planx.uk/src/@planx/components/shared/Preview/Card.test.tsx @@ -1,4 +1,5 @@ import Button from "@mui/material/Button"; +import { ComponentType as TYPES } from "@opensystemslab/planx-core/types"; import { act, screen, waitFor } from "@testing-library/react"; import { FullStore, useStore } from "pages/FlowEditor/lib/store"; import React from "react"; @@ -50,6 +51,29 @@ describe("Card component", () => { expect(screen.queryByText(saveButtonText)).not.toBeInTheDocument(); }); + it("hides the Save/Resume option if the user has already passed Send", () => { + act(() => + setState({ + path: ApplicationPath.SaveAndReturn, + flow: { + _root: { edges: ["Send", "Confirmation", "Feedback", "Notice"] }, + Send: { type: TYPES.Send }, + Confirmation: { type: TYPES.Confirmation }, + Feedback: { type: TYPES.Feedback }, + Notice: { type: TYPES.Notice }, + }, + breadcrumbs: { Send: { auto: false } }, + }), + ); + const children =

Confirmation Page

; + setup(); + + expect(screen.queryByText("Confirmation Page")).toBeInTheDocument(); + expect(screen.queryByText("Continue")).toBeInTheDocument(); + expect(screen.queryByText(resumeButtonText)).not.toBeInTheDocument(); + expect(screen.queryByText(saveButtonText)).not.toBeInTheDocument(); + }); + it("updates state to navigate to the 'Resume' page if the 'Resume' button is clicked", async () => { act(() => setState({ path: ApplicationPath.SaveAndReturn })); const children = ; diff --git a/editor.planx.uk/src/@planx/components/shared/Preview/Card.tsx b/editor.planx.uk/src/@planx/components/shared/Preview/Card.tsx index 72c8e261af..de4372a8b8 100644 --- a/editor.planx.uk/src/@planx/components/shared/Preview/Card.tsx +++ b/editor.planx.uk/src/@planx/components/shared/Preview/Card.tsx @@ -3,6 +3,7 @@ import Button from "@mui/material/Button"; import Container from "@mui/material/Container"; import Fade from "@mui/material/Fade"; import { styled, Theme, useTheme } from "@mui/material/styles"; +import { ComponentType as TYPES } from "@opensystemslab/planx-core/types"; import { useAnalyticsTracking } from "pages/FlowEditor/lib/analytics/provider"; import { useStore } from "pages/FlowEditor/lib/store"; import React, { useEffect } from "react"; @@ -48,12 +49,21 @@ const Card: React.FC = ({ ...props }) => { const theme = useTheme(); - const [path, visibleNode] = useStore((state) => [ + const [path, visibleNode, breadcrumbs, flow] = useStore((state) => [ state.path, state.currentCard, + state.breadcrumbs, + state.flow, ]); + + // Check if we have a Send node in our breadcrumbs + // This is a better/more immediate proxy for "submitted" in the frontend because actual send events that populate lowcal_sessions.submitted_at are queued via Hasura + const hasSent = Object.keys(breadcrumbs).some( + (breadcrumbNodeId: string) => flow[breadcrumbNodeId]?.type === TYPES.Send, + ); + const showSaveResumeButton = - path === ApplicationPath.SaveAndReturn && handleSubmit; + path === ApplicationPath.SaveAndReturn && handleSubmit && !hasSent; const { track } = useAnalyticsTracking(); useEffect(() => { diff --git a/editor.planx.uk/src/pages/FlowEditor/lib/store/preview.ts b/editor.planx.uk/src/pages/FlowEditor/lib/store/preview.ts index ef3a4f164c..a0f502d524 100644 --- a/editor.planx.uk/src/pages/FlowEditor/lib/store/preview.ts +++ b/editor.planx.uk/src/pages/FlowEditor/lib/store/preview.ts @@ -670,9 +670,8 @@ export const previewStore: StateCreator< }, isFinalCard: () => { - // Temporarily always returns false until upcomingCardIds is optimised - // OSL Slack explanation: https://bit.ly/3x38IRY - return false; + const { upcomingCardIds } = get(); + return upcomingCardIds().length === 1; }, restore: false, diff --git a/editor.planx.uk/src/pages/Preview/Node.tsx b/editor.planx.uk/src/pages/Preview/Node.tsx index caf83b6c6b..c61cfdbe3b 100644 --- a/editor.planx.uk/src/pages/Preview/Node.tsx +++ b/editor.planx.uk/src/pages/Preview/Node.tsx @@ -75,16 +75,13 @@ interface Props { } const Node: React.FC = (props) => { - const [childNodesOf, isFinalCard, resetPreview, cachedBreadcrumbs] = useStore( - (state) => [ - state.childNodesOf, - state.isFinalCard(), - state.resetPreview, - state.cachedBreadcrumbs, - ], - ); - - const handleSubmit = isFinalCard ? undefined : props.handleSubmit; + const [childNodesOf, resetPreview, cachedBreadcrumbs] = useStore((state) => [ + state.childNodesOf, + state.resetPreview, + state.cachedBreadcrumbs, + ]); + + const handleSubmit = props.handleSubmit; const nodeId = props.node.id; const previouslySubmittedData =