diff --git a/editor.planx.uk/src/@planx/components/Send/Public.test.tsx b/editor.planx.uk/src/@planx/components/Send/Public.test.tsx index 004b2c3c16..1f8da99d69 100644 --- a/editor.planx.uk/src/@planx/components/Send/Public.test.tsx +++ b/editor.planx.uk/src/@planx/components/Send/Public.test.tsx @@ -1,11 +1,12 @@ import { SendIntegration } from "@opensystemslab/planx-core/types"; -import { waitFor } from "@testing-library/react"; +import { screen, waitFor } from "@testing-library/react"; import axios from "axios"; import { FullStore, useStore } from "pages/FlowEditor/lib/store"; import React from "react"; import { act } from "react-dom/test-utils"; +import * as ReactNavi from "react-navi"; import { setup } from "testUtils"; -import { it,vi } from "vitest"; +import { it, vi } from "vitest"; import { axe } from "vitest-axe"; import hasuraEventsResponseMock from "./mocks/hasuraEventsResponseMock"; @@ -16,6 +17,10 @@ const { getState, setState } = useStore; let initialState: FullStore; +vi.spyOn(ReactNavi, "useNavigation").mockImplementation( + () => ({ navigate: vi.fn() }) as any, +); + /** * Adds a small tick to allow MUI to render (e.g. card transitions) */ 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 de4372a8b8..92b2505f37 100644 --- a/editor.planx.uk/src/@planx/components/shared/Preview/Card.tsx +++ b/editor.planx.uk/src/@planx/components/shared/Preview/Card.tsx @@ -3,13 +3,11 @@ 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"; -import { ApplicationPath } from "types"; -import SaveResumeButton from "./SaveResumeButton"; +import OrNavigationButton from "./OrNavigationButton"; interface Props { children: React.ReactNode; @@ -41,6 +39,7 @@ const InnerContainer = styled(Box)(({ theme }) => ({ * @param {object} props Component props * @param {bool} props.handleSubmit if included then show the Continue button * @param {bool} props.isValid if falsey then disable Continue button, otherwise enable + * @param {bool} props.isTestWarningWrapper if truthy then show navigate to publish Or button */ const Card: React.FC = ({ children, @@ -49,21 +48,7 @@ const Card: React.FC = ({ ...props }) => { const theme = useTheme(); - 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 && !hasSent; + const [visibleNode] = useStore((state) => [state.currentCard]); const { track } = useAnalyticsTracking(); useEffect(() => { @@ -101,7 +86,7 @@ const Card: React.FC = ({ Continue )} - {showSaveResumeButton && } + diff --git a/editor.planx.uk/src/@planx/components/shared/Preview/NavigateToPublishedButton.tsx b/editor.planx.uk/src/@planx/components/shared/Preview/NavigateToPublishedButton.tsx new file mode 100644 index 0000000000..90e50667c2 --- /dev/null +++ b/editor.planx.uk/src/@planx/components/shared/Preview/NavigateToPublishedButton.tsx @@ -0,0 +1,27 @@ +import Link from "@mui/material/Link"; +import Typography from "@mui/material/Typography"; +import { clearLocalFlow } from "lib/local"; +import { useStore } from "pages/FlowEditor/lib/store"; +import React from "react"; +import { useNavigation } from "react-navi"; + +const NavigateToPublishedButton: React.FC = () => { + const { navigate } = useNavigation(); + const id = useStore().id; + + const handleClick = () => { + clearLocalFlow(id); + navigate("published?analytics=false"); + window.location.reload(); + }; + + return ( + + + Go to the published version of this service + + + ); +}; + +export default NavigateToPublishedButton; diff --git a/editor.planx.uk/src/@planx/components/shared/Preview/OrNavigationButton.tsx b/editor.planx.uk/src/@planx/components/shared/Preview/OrNavigationButton.tsx new file mode 100644 index 0000000000..a6378a095b --- /dev/null +++ b/editor.planx.uk/src/@planx/components/shared/Preview/OrNavigationButton.tsx @@ -0,0 +1,77 @@ +import Box from "@mui/material/Box"; +import { styled } from "@mui/material/styles"; +import Typography from "@mui/material/Typography"; +import { ComponentType as TYPES } from "@opensystemslab/planx-core/types"; +import { useStore } from "pages/FlowEditor/lib/store"; +import React from "react"; +import { ApplicationPath } from "types"; + +import { contentFlowSpacing } from "./Card"; +import NavigateToPublishedButton from "./NavigateToPublishedButton"; +import SaveResumeButton from "./SaveResumeButton"; + +type OrNavigationType = "save-resume" | "navigate-to-published"; + +export const InnerContainer = styled(Box)(({ theme }) => ({ + "& p": { + ...contentFlowSpacing(theme), + }, +})); + +const BUTTON_COMPONENTS = { + "save-resume": SaveResumeButton, + "navigate-to-published": NavigateToPublishedButton, +} as const; + +const TEST_ENVIRONMENTS = new Set(["preview", "draft"]); + +const OrNavigationButton = ({ + handleSubmit, +}: { + handleSubmit: ((data?: any) => void) | undefined; +}) => { + const [path, breadcrumbs, flow] = useStore((state) => [ + state.path, + state.breadcrumbs, + state.flow, + ]); + + const endOfUrl = window.location.pathname.split("/").slice(-1)[0]; + + const isTestEnvironment: boolean = TEST_ENVIRONMENTS.has(endOfUrl); + + const defineNavigationType = (): OrNavigationType | undefined => { + // 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 && !hasSent; + + if (showSaveResumeButton && !isTestEnvironment) { + return "save-resume"; + } + + if (!showSaveResumeButton && isTestEnvironment) { + return "navigate-to-published"; + } + }; + + const orNavigationType = defineNavigationType(); + + const ButtonComponent = + orNavigationType && BUTTON_COMPONENTS[orNavigationType]; + + return ( + ButtonComponent && ( + + or + + + ) + ); +}; + +export default OrNavigationButton; diff --git a/editor.planx.uk/src/@planx/components/shared/Preview/SaveResumeButton.tsx b/editor.planx.uk/src/@planx/components/shared/Preview/SaveResumeButton.tsx index 96bb8cd30c..c47ca17cb7 100644 --- a/editor.planx.uk/src/@planx/components/shared/Preview/SaveResumeButton.tsx +++ b/editor.planx.uk/src/@planx/components/shared/Preview/SaveResumeButton.tsx @@ -1,20 +1,9 @@ -import Box from "@mui/material/Box"; import Link from "@mui/material/Link"; -import { styled } from "@mui/material/styles"; import Typography from "@mui/material/Typography"; import { useAnalyticsTracking } from "pages/FlowEditor/lib/analytics/provider"; import { useStore } from "pages/FlowEditor/lib/store"; import React from "react"; import { ApplicationPath } from "types"; - -import { contentFlowSpacing } from "./Card"; - -const InnerContainer = styled(Box)(({ theme }) => ({ - "& p": { - ...contentFlowSpacing(theme), - }, -})); - const SaveResumeButton: React.FC = () => { const saveToEmail = useStore((state) => state.saveToEmail); const { trackEvent } = useAnalyticsTracking(); @@ -33,19 +22,14 @@ const SaveResumeButton: React.FC = () => { } }; - const onClick = () => handleClick(); - return ( - - or - - - {saveToEmail - ? "Save and return to this application later" - : "Resume an application you have already started"} - - - + + + {saveToEmail + ? "Save and return to this application later" + : "Resume an application you have already started"} + + ); }; diff --git a/editor.planx.uk/src/pages/Preview/TestWarningPage.tsx b/editor.planx.uk/src/pages/Preview/TestWarningPage.tsx new file mode 100644 index 0000000000..e3a6c12bd0 --- /dev/null +++ b/editor.planx.uk/src/pages/Preview/TestWarningPage.tsx @@ -0,0 +1,24 @@ +import Box from "@mui/material/Box"; +import Card from "@planx/components/shared/Preview/Card"; +import { CardHeader } from "@planx/components/shared/Preview/CardHeader/CardHeader"; +import React, { PropsWithChildren, useState } from "react"; + +export const TestWarningPage = ({ children }: PropsWithChildren) => { + const [hasAcknowledgedWarning, setHasAcknowledgedWarning] = useState(false); + return ( + <> + {hasAcknowledgedWarning ? ( + children + ) : ( + + setHasAcknowledgedWarning(true)}> + + + + )} + + ); +}; diff --git a/editor.planx.uk/src/routes/views/draft.tsx b/editor.planx.uk/src/routes/views/draft.tsx index 7404cec968..ff355ab358 100644 --- a/editor.planx.uk/src/routes/views/draft.tsx +++ b/editor.planx.uk/src/routes/views/draft.tsx @@ -5,6 +5,7 @@ import { publicClient } from "lib/graphql"; import { NaviRequest, NotFoundError } from "navi"; import { useStore } from "pages/FlowEditor/lib/store"; import PublicLayout from "pages/layout/PublicLayout"; +import { TestWarningPage } from "pages/Preview/TestWarningPage"; import React from "react"; import { View } from "react-navi"; import { Flow, GlobalSettings } from "types"; @@ -39,7 +40,9 @@ export const draftView = async (req: NaviRequest) => { return ( - + + + ); }; diff --git a/editor.planx.uk/src/routes/views/preview.tsx b/editor.planx.uk/src/routes/views/preview.tsx index 14b22ea015..f12fa6f810 100644 --- a/editor.planx.uk/src/routes/views/preview.tsx +++ b/editor.planx.uk/src/routes/views/preview.tsx @@ -3,6 +3,7 @@ import axios, { AxiosError } from "axios"; import { NaviRequest, NotFoundError } from "navi"; import { useStore } from "pages/FlowEditor/lib/store"; import PublicLayout from "pages/layout/PublicLayout"; +import { TestWarningPage } from "pages/Preview/TestWarningPage"; import React from "react"; import { View } from "react-navi"; import { getTeamFromDomain } from "routes/utils"; @@ -40,7 +41,9 @@ export const previewView = async (req: NaviRequest) => { return ( - + + + ); };