From c2836035c6e04d2a6f19427c013b4c8cf677be7c Mon Sep 17 00:00:00 2001 From: Ian Jones <51156018+ianjon3s@users.noreply.github.com> Date: Wed, 17 Jan 2024 16:33:46 +0000 Subject: [PATCH 01/13] feat: Apply theme colours across app (#2658) --- .../src/@planx/components/Content/Public.tsx | 2 +- .../@planx/components/FileUploadAndLabel/Modal.tsx | 4 +++- .../@planx/components/FileUploadAndLabel/Public.tsx | 1 - .../src/@planx/components/Notice/Public.tsx | 12 ++++++++---- .../src/@planx/components/shared/Preview/Card.tsx | 2 +- .../components/shared/PrivateFileUpload/Dropzone.tsx | 2 +- editor.planx.uk/src/theme.ts | 4 ++-- editor.planx.uk/src/ui/public/NextStepsList.tsx | 7 ++++--- 8 files changed, 20 insertions(+), 14 deletions(-) diff --git a/editor.planx.uk/src/@planx/components/Content/Public.tsx b/editor.planx.uk/src/@planx/components/Content/Public.tsx index a000dd16b9..c583a111c8 100644 --- a/editor.planx.uk/src/@planx/components/Content/Public.tsx +++ b/editor.planx.uk/src/@planx/components/Content/Public.tsx @@ -20,7 +20,7 @@ const Content = styled(Box, { theme.palette.text.primary, ])?.toHexString() || theme.palette.text.primary, "& a": { - color: getContrastTextColor(color || "#fff", theme.palette.primary.main), + color: getContrastTextColor(color || "#fff", theme.palette.link.main), }, "& *:first-child": { marginTop: 0, diff --git a/editor.planx.uk/src/@planx/components/FileUploadAndLabel/Modal.tsx b/editor.planx.uk/src/@planx/components/FileUploadAndLabel/Modal.tsx index fcac2b3a9e..db5aafc6e1 100644 --- a/editor.planx.uk/src/@planx/components/FileUploadAndLabel/Modal.tsx +++ b/editor.planx.uk/src/@planx/components/FileUploadAndLabel/Modal.tsx @@ -115,6 +115,7 @@ export const FileTaggingModal = ({ - - - - - -
- - Button colour - - The button background colour should be either a dark or light - colour. The text will be programatically selected to contrast - with the selected colour (being either black or white). - - - - See our guide for setting button colours - - - - - - formik.setFieldValue("buttonColor", color) - } - label="Button colour" - /> - - - - - - Preview: - - - - - - - - - -
-
- -
- - Text link colour - - The text link colour should be a dark colour that contrasts - with white ("#ffffff"). - - - - See our guide for setting text link colours - - - - - - formik.setFieldValue("linkColor", color) - } - label="Text link colour" - /> - - - - - - Preview: - - - Example text link - - - - - - -
-
- -
- - Favicon - - Set the favicon to be used in the browser tab. The favicon - should be 32x32px and in .ico or .png format. - - - - See our guide for favicons - - - - Favicon: - - - - - .ico or .png - - - - - - - -
-
- - )} - - ); -}; - -export default DesignSettings; diff --git a/editor.planx.uk/src/pages/FlowEditor/components/Settings/DesignSettings/ButtonForm.tsx b/editor.planx.uk/src/pages/FlowEditor/components/Settings/DesignSettings/ButtonForm.tsx new file mode 100644 index 0000000000..ddeb8d32c9 --- /dev/null +++ b/editor.planx.uk/src/pages/FlowEditor/components/Settings/DesignSettings/ButtonForm.tsx @@ -0,0 +1,61 @@ +import Button from "@mui/material/Button"; +import Link from "@mui/material/Link"; +import { useFormik } from "formik"; +import React from "react"; +import ColorPicker from "ui/editor/ColorPicker"; +import InputDescription from "ui/editor/InputDescription"; +import InputRow from "ui/shared/InputRow"; +import InputRowItem from "ui/shared/InputRowItem"; + +import { DesignPreview, EXAMPLE_COLOUR, SettingsForm } from "."; + +export const ButtonForm: React.FC = () => { + const formik = useFormik<{ + buttonColor: string; + }>({ + initialValues: { + buttonColor: EXAMPLE_COLOUR, + }, + onSubmit: () => {}, + validate: () => {}, + }); + + return ( + + + The button background colour should be either a dark or light + colour. The text will be programmatically selected to contrast with + the selected colour (being either black or white). + + + + See our guide for setting button colours + + + + } + input={ + + + formik.setFieldValue("buttonColor", color)} + label="Button colour" + /> + + + } + preview={ + + + + } + /> + ); +}; diff --git a/editor.planx.uk/src/pages/FlowEditor/components/Settings/DesignSettings/FaviconForm.tsx b/editor.planx.uk/src/pages/FlowEditor/components/Settings/DesignSettings/FaviconForm.tsx new file mode 100644 index 0000000000..729f011d21 --- /dev/null +++ b/editor.planx.uk/src/pages/FlowEditor/components/Settings/DesignSettings/FaviconForm.tsx @@ -0,0 +1,57 @@ +import Link from "@mui/material/Link"; +import Typography from "@mui/material/Typography"; +import { useFormik } from "formik"; +import React from "react"; +import InputDescription from "ui/editor/InputDescription"; +import InputRow from "ui/shared/InputRow"; +import InputRowItem from "ui/shared/InputRowItem"; +import InputRowLabel from "ui/shared/InputRowLabel"; +import PublicFileUploadButton from "ui/shared/PublicFileUploadButton"; + +import { EXAMPLE_COLOUR, SettingsForm } from "."; + +export const FaviconForm: React.FC = () => { + const formik = useFormik<{ + textLinkColor: string; + }>({ + initialValues: { + textLinkColor: EXAMPLE_COLOUR, + }, + onSubmit: () => {}, + validate: () => {}, + }); + + return ( + + + Set the favicon to be used in the browser tab. The favicon should be + 32x32px and in .ico or .png format. + + + See our guide for favicons + + + } + input={ + + Favicon: + + + + + .ico or .png + + + } + /> + ); +}; diff --git a/editor.planx.uk/src/pages/FlowEditor/components/Settings/DesignSettings/TextLinkForm.tsx b/editor.planx.uk/src/pages/FlowEditor/components/Settings/DesignSettings/TextLinkForm.tsx new file mode 100644 index 0000000000..f89d7c0223 --- /dev/null +++ b/editor.planx.uk/src/pages/FlowEditor/components/Settings/DesignSettings/TextLinkForm.tsx @@ -0,0 +1,57 @@ +import Link from "@mui/material/Link"; +import { useFormik } from "formik"; +import React from "react"; +import ColorPicker from "ui/editor/ColorPicker"; +import InputDescription from "ui/editor/InputDescription"; +import InputRow from "ui/shared/InputRow"; +import InputRowItem from "ui/shared/InputRowItem"; + +import { DesignPreview, EXAMPLE_COLOUR, SettingsForm } from "."; + +export const TextLinkForm: React.FC = () => { + const formik = useFormik<{ + linkColor: string; + }>({ + initialValues: { + linkColor: EXAMPLE_COLOUR, + }, + onSubmit: () => {}, + validate: () => {}, + }); + + return ( + + + The text link colour should be a dark colour that contrasts with + white ("#ffffff"). + + + + See our guide for setting text link colours + + + + } + input={ + + + formik.setFieldValue("linkColor", color)} + label="Text link colour" + /> + + + } + preview={ + + Example text link + + } + /> + ); +}; diff --git a/editor.planx.uk/src/pages/FlowEditor/components/Settings/DesignSettings/ThemeAndLogoForm.tsx b/editor.planx.uk/src/pages/FlowEditor/components/Settings/DesignSettings/ThemeAndLogoForm.tsx new file mode 100644 index 0000000000..8e1f2e8286 --- /dev/null +++ b/editor.planx.uk/src/pages/FlowEditor/components/Settings/DesignSettings/ThemeAndLogoForm.tsx @@ -0,0 +1,127 @@ +import Link from "@mui/material/Link"; +import { getContrastRatio, useTheme } from "@mui/material/styles"; +import Typography from "@mui/material/Typography"; +import { Team, TeamTheme } from "@opensystemslab/planx-core/types"; +import { useFormik } from "formik"; +import { useStore } from "pages/FlowEditor/lib/store"; +import React, { useEffect, useState } from "react"; +import ColorPicker from "ui/editor/ColorPicker"; +import InputDescription from "ui/editor/InputDescription"; +import InputRow from "ui/shared/InputRow"; +import InputRowItem from "ui/shared/InputRowItem"; +import InputRowLabel from "ui/shared/InputRowLabel"; +import PublicFileUploadButton from "ui/shared/PublicFileUploadButton"; + +import { DesignPreview, SettingsForm } from "."; + +type FormValues = Pick; + +export const ThemeAndLogoForm: React.FC<{ + team: Team; + onSuccess: () => void; +}> = ({ team, onSuccess }) => { + const theme = useTheme(); + + useEffect(() => { + setInitialValues({ + primaryColour: team.theme?.primaryColour, + logo: team.theme?.logo, + }); + }, [team]); + + const [initialValues, setInitialValues] = useState({ + primaryColour: "", + logo: "", + }); + + const formik = useFormik({ + initialValues, + onSubmit: async (values, { resetForm }) => { + const isSuccess = await useStore.getState().updateTeamTheme(values); + if (isSuccess) { + onSuccess(); + // Reset "dirty" status to disable Save & Reset buttons + resetForm({ values }); + } + }, + validateOnBlur: false, + validateOnChange: false, + enableReinitialize: true, + validate: ({ primaryColour }) => { + const isContrastThresholdMet = + getContrastRatio("#FFF", primaryColour) > + theme.palette.contrastThreshold; + + if (!isContrastThresholdMet) { + return { + primaryColour: + "Theme colour does not meet accessibility contrast requirements (3:1)", + }; + } + }, + }); + + return ( + + + The theme colour and logo, are used in the header of the service. + The theme colour should be a dark colour that contrasts with white + ("#ffffff"). The logo should contrast with a dark background colour + (your theme colour) and have a transparent background. + + + + See our guide for setting theme colours and logos + + + + } + input={ + <> + + + + formik.setFieldValue("primaryColour", color) + } + label="Theme colour" + /> + + + + Logo: + + formik.setFieldValue("logo", newUrl)} + /> + + + .png or .svg + + + + } + preview={ + + {formik.values.logo ? ( + council logo + ) : ( + + {team?.name} + + )} + + } + /> + ); +}; diff --git a/editor.planx.uk/src/pages/FlowEditor/components/Settings/DesignSettings/index.tsx b/editor.planx.uk/src/pages/FlowEditor/components/Settings/DesignSettings/index.tsx new file mode 100644 index 0000000000..4bc13a3951 --- /dev/null +++ b/editor.planx.uk/src/pages/FlowEditor/components/Settings/DesignSettings/index.tsx @@ -0,0 +1,158 @@ +import Alert from "@mui/material/Alert"; +import Box from "@mui/material/Box"; +import Button from "@mui/material/Button"; +import Snackbar from "@mui/material/Snackbar"; +import { styled } from "@mui/material/styles"; +import Typography from "@mui/material/Typography"; +import { Team } from "@opensystemslab/planx-core/types"; +import { FormikProps, getIn } from "formik"; +import { hasFeatureFlag } from "lib/featureFlags"; +import { useStore } from "pages/FlowEditor/lib/store"; +import React, { useEffect, useState } from "react"; +import EditorRow from "ui/editor/EditorRow"; +import { FeaturePlaceholder } from "ui/editor/FeaturePlaceholder"; +import InputGroup from "ui/editor/InputGroup"; +import InputLegend from "ui/editor/InputLegend"; +import ErrorWrapper from "ui/shared/ErrorWrapper"; + +import { ButtonForm } from "./ButtonForm"; +import { FaviconForm } from "./FaviconForm"; +import { TextLinkForm } from "./TextLinkForm"; +import { ThemeAndLogoForm } from "./ThemeAndLogoForm"; + +export const DesignPreview = styled(Box)(({ theme }) => ({ + border: `2px solid ${theme.palette.border.input}`, + padding: theme.spacing(2), + boxShadow: "4px 4px 0px rgba(150, 150, 150, 0.5)", +})); + +export const EXAMPLE_COLOUR = "#007078"; + +type SettingsFormProps = { + legend: string; + description: React.ReactElement; + input: React.ReactElement; + formik: FormikProps; + preview?: React.ReactElement; +}; + +const useTeam = () => { + const [team, setTeam] = useState({} as Team); + + useEffect(() => { + const fetchTeam = async () => { + try { + const fetchedTeam = await useStore.getState().fetchCurrentTeam(); + if (!fetchedTeam) throw Error("Unable to find team"); + setTeam(fetchedTeam); + } catch (error) { + console.error("Error fetching team:", error); + } + }; + + fetchTeam(); + }, []); + + return team; +}; + +export const SettingsForm: React.FC = ({ + formik, + legend, + description, + input, + preview, +}) => { + return ( + +
+ + {legend} + {description} + {input} + + {preview && ( + + + Preview: + + {preview} + + )} + + + + + + +
+
+ ); +}; + +const DesignSettings: React.FC = () => { + const isUsingFeatureFlag = hasFeatureFlag("SHOW_TEAM_SETTINGS"); + const team = useTeam(); + const [open, setOpen] = useState(false); + + const handleClose = ( + _event?: React.SyntheticEvent | Event, + reason?: string, + ) => { + if (reason === "clickaway") { + return; + } + + setOpen(false); + }; + + return ( + <> + + + Design + + + How your service appears to public users + + + {!isUsingFeatureFlag ? ( + + {" "} + + ) : ( + <> + setOpen(true)} /> + + + + + + Theme updated successfully + + + + )} + + ); +}; + +export default DesignSettings; diff --git a/editor.planx.uk/src/pages/FlowEditor/lib/store/team.ts b/editor.planx.uk/src/pages/FlowEditor/lib/store/team.ts index 7506f3c70b..c7b1e11c2d 100644 --- a/editor.planx.uk/src/pages/FlowEditor/lib/store/team.ts +++ b/editor.planx.uk/src/pages/FlowEditor/lib/store/team.ts @@ -24,6 +24,7 @@ export interface TeamStore { initTeamStore: (slug: string) => Promise; clearTeamStore: () => void; fetchCurrentTeam: () => Promise; + updateTeamTheme: (theme: Partial) => Promise; } export const teamStore: StateCreator< @@ -115,4 +116,10 @@ export const teamStore: StateCreator< const team = await $client.team.getBySlug(teamSlug); return team; }, + + updateTeamTheme: async (theme: Partial) => { + const { teamId, $client } = get(); + const isSuccess = await $client.team.updateTheme(teamId, theme); + return isSuccess; + }, }); From 58f9108ce675364ef627c1363c4f52cb59f40768 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Dafydd=20Ll=C5=B7r=20Pearson?= Date: Wed, 17 Jan 2024 16:56:10 +0000 Subject: [PATCH 03/13] feat: Wire up design settings (button colour) (#2669) --- .../Settings/DesignSettings/ButtonForm.tsx | 63 +++++++++++++++---- .../Settings/DesignSettings/index.tsx | 2 +- 2 files changed, 51 insertions(+), 14 deletions(-) diff --git a/editor.planx.uk/src/pages/FlowEditor/components/Settings/DesignSettings/ButtonForm.tsx b/editor.planx.uk/src/pages/FlowEditor/components/Settings/DesignSettings/ButtonForm.tsx index ddeb8d32c9..d621cffc8c 100644 --- a/editor.planx.uk/src/pages/FlowEditor/components/Settings/DesignSettings/ButtonForm.tsx +++ b/editor.planx.uk/src/pages/FlowEditor/components/Settings/DesignSettings/ButtonForm.tsx @@ -1,23 +1,47 @@ import Button from "@mui/material/Button"; import Link from "@mui/material/Link"; +import { darken, useTheme } from "@mui/material/styles"; +import { Team, TeamTheme } from "@opensystemslab/planx-core/types"; import { useFormik } from "formik"; -import React from "react"; +import { useStore } from "pages/FlowEditor/lib/store"; +import React, { useEffect, useState } from "react"; +import { getContrastTextColor } from "styleUtils"; import ColorPicker from "ui/editor/ColorPicker"; import InputDescription from "ui/editor/InputDescription"; import InputRow from "ui/shared/InputRow"; import InputRowItem from "ui/shared/InputRowItem"; -import { DesignPreview, EXAMPLE_COLOUR, SettingsForm } from "."; +import { DesignPreview, SettingsForm } from "."; -export const ButtonForm: React.FC = () => { - const formik = useFormik<{ - buttonColor: string; - }>({ - initialValues: { - buttonColor: EXAMPLE_COLOUR, +type FormValues = Pick; + +export const ButtonForm: React.FC<{ team: Team; onSuccess: () => void }> = ({ + team, + onSuccess, +}) => { + const theme = useTheme(); + + useEffect(() => { + setInitialValues({ actionColour: team.theme?.actionColour }); + }, [team]); + + const [initialValues, setInitialValues] = useState({ + actionColour: "", + }); + + const formik = useFormik({ + initialValues, + onSubmit: async (values, { resetForm }) => { + const isSuccess = await useStore.getState().updateTeamTheme(values); + if (isSuccess) { + onSuccess(); + // Reset "dirty" status to disable Save & Reset buttons + resetForm({ values }); + } }, - onSubmit: () => {}, - validate: () => {}, + validateOnBlur: false, + validateOnChange: false, + enableReinitialize: true, }); return ( @@ -42,8 +66,8 @@ export const ButtonForm: React.FC = () => { formik.setFieldValue("buttonColor", color)} + color={formik.values.actionColour} + onChange={(color) => formik.setFieldValue("actionColour", color)} label="Button colour" /> @@ -51,7 +75,20 @@ export const ButtonForm: React.FC = () => { } preview={ - diff --git a/editor.planx.uk/src/pages/FlowEditor/components/Settings/DesignSettings/index.tsx b/editor.planx.uk/src/pages/FlowEditor/components/Settings/DesignSettings/index.tsx index 4bc13a3951..c31b0afc94 100644 --- a/editor.planx.uk/src/pages/FlowEditor/components/Settings/DesignSettings/index.tsx +++ b/editor.planx.uk/src/pages/FlowEditor/components/Settings/DesignSettings/index.tsx @@ -137,7 +137,7 @@ const DesignSettings: React.FC = () => { ) : ( <> setOpen(true)} /> - + setOpen(true)} /> From 3a8015b5a21e8c758ebf4b55257c9dbf3f5b3cdb Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Dafydd=20Ll=C5=B7r=20Pearson?= Date: Thu, 18 Jan 2024 10:16:26 +0000 Subject: [PATCH 04/13] feat: Wire up design settings (text link colour) (#2670) * feat: Refeactor into more generic components * fix: From -> Form * feat: Wire up design settings (link colour) * fix: Display all validation errors --- .../Settings/DesignSettings/TextLinkForm.tsx | 62 +++++++++++++++---- .../Settings/DesignSettings/index.tsx | 4 +- 2 files changed, 51 insertions(+), 15 deletions(-) diff --git a/editor.planx.uk/src/pages/FlowEditor/components/Settings/DesignSettings/TextLinkForm.tsx b/editor.planx.uk/src/pages/FlowEditor/components/Settings/DesignSettings/TextLinkForm.tsx index f89d7c0223..7977aabcd5 100644 --- a/editor.planx.uk/src/pages/FlowEditor/components/Settings/DesignSettings/TextLinkForm.tsx +++ b/editor.planx.uk/src/pages/FlowEditor/components/Settings/DesignSettings/TextLinkForm.tsx @@ -1,22 +1,56 @@ import Link from "@mui/material/Link"; +import { getContrastRatio, useTheme } from "@mui/material/styles"; +import { Team, TeamTheme } from "@opensystemslab/planx-core/types"; import { useFormik } from "formik"; -import React from "react"; +import { useStore } from "pages/FlowEditor/lib/store"; +import React, { useEffect, useState } from "react"; import ColorPicker from "ui/editor/ColorPicker"; import InputDescription from "ui/editor/InputDescription"; import InputRow from "ui/shared/InputRow"; import InputRowItem from "ui/shared/InputRowItem"; -import { DesignPreview, EXAMPLE_COLOUR, SettingsForm } from "."; +import { DesignPreview, SettingsForm } from "."; -export const TextLinkForm: React.FC = () => { - const formik = useFormik<{ - linkColor: string; - }>({ - initialValues: { - linkColor: EXAMPLE_COLOUR, +type FormValues = Pick; + +export const TextLinkForm: React.FC<{ team: Team; onSuccess: () => void }> = ({ + team, + onSuccess, +}) => { + const theme = useTheme(); + + useEffect(() => { + setInitialValues({ linkColour: team.theme?.linkColour }); + }, [team]); + + const [initialValues, setInitialValues] = useState({ + linkColour: "", + }); + + const formik = useFormik({ + initialValues, + onSubmit: async (values, { resetForm }) => { + const isSuccess = await useStore.getState().updateTeamTheme(values); + if (isSuccess) { + onSuccess(); + // Reset "dirty" status to disable Save & Reset buttons + resetForm({ values }); + } + }, + validateOnBlur: false, + validateOnChange: false, + enableReinitialize: true, + validate: ({ linkColour }) => { + const isContrastThresholdMet = + getContrastRatio("#FFF", linkColour) > theme.palette.contrastThreshold; + + if (!isContrastThresholdMet) { + return { + linkColour: + "Colour does not meet accessibility contrast requirements (3:1)", + }; + } }, - onSubmit: () => {}, - validate: () => {}, }); return ( @@ -40,8 +74,8 @@ export const TextLinkForm: React.FC = () => { formik.setFieldValue("linkColor", color)} + color={formik.values.linkColour} + onChange={(color) => formik.setFieldValue("linkColour", color)} label="Text link colour" /> @@ -49,7 +83,9 @@ export const TextLinkForm: React.FC = () => { } preview={ - Example text link + + Example text link + } /> diff --git a/editor.planx.uk/src/pages/FlowEditor/components/Settings/DesignSettings/index.tsx b/editor.planx.uk/src/pages/FlowEditor/components/Settings/DesignSettings/index.tsx index c31b0afc94..acbefe943c 100644 --- a/editor.planx.uk/src/pages/FlowEditor/components/Settings/DesignSettings/index.tsx +++ b/editor.planx.uk/src/pages/FlowEditor/components/Settings/DesignSettings/index.tsx @@ -80,7 +80,7 @@ export const SettingsForm: React.FC = ({ )} @@ -138,7 +138,7 @@ const DesignSettings: React.FC = () => { <> setOpen(true)} /> setOpen(true)} /> - + setOpen(true)} /> Date: Thu, 18 Jan 2024 14:00:50 +0000 Subject: [PATCH 05/13] feat: UI setup for feedback component hidden behind feature flag (#2632) * wip: UI setup for feedback component * fix: Back button test ID * fix: Orphaned form labels * fix: Tests for E2E * fix: Tests for E2E * feat: add new feature flag for the internal feedback feature * feat: conditionally render feedback footer content * feat: conditionally render the new MoreInfo feedback content * chore: add todo as per feedback * chore: make feature flag check static --------- Co-authored-by: Mike Heneghan --- e2e/tests/ui-driven/src/globalHelpers.ts | 4 +- .../components/shared/Preview/MoreInfo.tsx | 18 +- editor.planx.uk/src/components/Feedback.tsx | 260 ++++++++++++++++++ .../src/components/FeedbackPhaseBanner.tsx | 83 ++++++ .../src/components/MoreInfoFeedback.tsx | 82 ++++++ editor.planx.uk/src/lib/featureFlags.ts | 1 + .../src/pages/Preview/Questions.tsx | 8 +- .../src/pages/layout/PublicLayout.tsx | 7 +- editor.planx.uk/src/theme.ts | 2 +- .../src/ui/public/FeedbackDisclaimer.tsx | 16 ++ .../src/ui/public/FeedbackOption.tsx | 79 ++++++ 11 files changed, 548 insertions(+), 12 deletions(-) create mode 100644 editor.planx.uk/src/components/Feedback.tsx create mode 100644 editor.planx.uk/src/components/FeedbackPhaseBanner.tsx create mode 100644 editor.planx.uk/src/components/MoreInfoFeedback.tsx create mode 100644 editor.planx.uk/src/ui/public/FeedbackDisclaimer.tsx create mode 100644 editor.planx.uk/src/ui/public/FeedbackOption.tsx diff --git a/e2e/tests/ui-driven/src/globalHelpers.ts b/e2e/tests/ui-driven/src/globalHelpers.ts index a6f8552b90..52be7509c0 100644 --- a/e2e/tests/ui-driven/src/globalHelpers.ts +++ b/e2e/tests/ui-driven/src/globalHelpers.ts @@ -139,7 +139,7 @@ export async function clickContinue({ export async function clickBack({ page }: { page: Page }) { const waitPromise = waitForDebugLog(page); // assume debug message is triggered on state transition - await page.getByRole("button", { name: "Back", exact: true }).click(); + await page.getByTestId("backButton").click(); await waitPromise; } @@ -193,7 +193,7 @@ export async function answerChecklist({ }); await expect(checklist).toBeVisible(); for (const answer of answers) { - await page.locator("label", { hasText: answer }).click(); + await page.getByLabel(answer, { exact: true }).click(); } } diff --git a/editor.planx.uk/src/@planx/components/shared/Preview/MoreInfo.tsx b/editor.planx.uk/src/@planx/components/shared/Preview/MoreInfo.tsx index f2e135bec9..2d6bd4edec 100644 --- a/editor.planx.uk/src/@planx/components/shared/Preview/MoreInfo.tsx +++ b/editor.planx.uk/src/@planx/components/shared/Preview/MoreInfo.tsx @@ -1,8 +1,11 @@ import CloseIcon from "@mui/icons-material/Close"; +import Box from "@mui/material/Box"; import Container from "@mui/material/Container"; import Drawer, { DrawerProps } from "@mui/material/Drawer"; import IconButton from "@mui/material/IconButton"; import { styled } from "@mui/material/styles"; +import MoreInfoFeedbackComponent from "components/MoreInfoFeedback"; +import { hasFeatureFlag } from "lib/featureFlags"; import React from "react"; const PREFIX = "MoreInfo"; @@ -34,22 +37,22 @@ const Root = styled(Drawer, { [`& .${classes.drawerPaper}`]: { width: "100%", maxWidth: drawerWidth, - backgroundColor: theme.palette.background.default, + backgroundColor: theme.palette.background.paper, border: 0, boxShadow: "-4px 0 0 rgba(0,0,0,0.1)", }, })); -const DrawerContent = styled("div")(({ theme }) => ({ - padding: theme.spacing(2.5, 4, 6, 0), +const DrawerContent = styled(Box)(({ theme }) => ({ + padding: theme.spacing(2.5, 4, 2, 0), fontSize: "1rem", lineHeight: "1.5", [theme.breakpoints.up("sm")]: { - padding: theme.spacing(6, 4, 6, 1), + padding: theme.spacing(6, 4, 4, 1), }, })); -const CloseButton = styled("div")(({ theme }) => ({ +const CloseButton = styled(Box)(({ theme }) => ({ display: "flex", alignItems: "center", justifyContent: "flex-end", @@ -59,6 +62,8 @@ const CloseButton = styled("div")(({ theme }) => ({ color: theme.palette.text.primary, })); +const isUsingFeatureFlag = hasFeatureFlag("SHOW_INTERNAL_FEEDBACK"); + interface IMoreInfo { open: boolean; children: (JSX.Element | string | undefined)[] | JSX.Element; @@ -90,9 +95,10 @@ const MoreInfo: React.FC = ({ open, children, handleClose }) => ( - + {children} + {isUsingFeatureFlag && } ); diff --git a/editor.planx.uk/src/components/Feedback.tsx b/editor.planx.uk/src/components/Feedback.tsx new file mode 100644 index 0000000000..55623756f5 --- /dev/null +++ b/editor.planx.uk/src/components/Feedback.tsx @@ -0,0 +1,260 @@ +import ArrowBackIcon from "@mui/icons-material/ArrowBack"; +import CloseIcon from "@mui/icons-material/Close"; +import LightbulbIcon from "@mui/icons-material/Lightbulb"; +import MoreHorizIcon from "@mui/icons-material/MoreHoriz"; +import WarningIcon from "@mui/icons-material/Warning"; +import Box from "@mui/material/Box"; +import Button from "@mui/material/Button"; +import Container from "@mui/material/Container"; +import IconButton from "@mui/material/IconButton"; +import { styled } from "@mui/material/styles"; +import Typography from "@mui/material/Typography"; +import { contentFlowSpacing } from "@planx/components/shared/Preview/Card"; +import FeedbackPhaseBanner from "components/FeedbackPhaseBanner"; +import { BackButton } from "pages/Preview/Questions"; +import React from "react"; +import FeedbackDisclaimer from "ui/public/FeedbackDisclaimer"; +import FeedbackOption from "ui/public/FeedbackOption"; +import InputLabel from "ui/public/InputLabel"; +import Input from "ui/shared/Input"; + +const FeedbackWrapper = styled(Box)(({ theme }) => ({ + backgroundColor: theme.palette.background.paper, + borderTop: `1px solid ${theme.palette.border.main}`, +})); + +const FeedbackRow = styled(Box)(({ theme }) => ({ + maxWidth: theme.breakpoints.values.formWrap, + padding: theme.spacing(2, 0, 4), +})); + +const FeedbackHeader = styled(Box)(({ theme }) => ({ + padding: theme.spacing(1, 0), + position: "relative", + display: "flex", + justifyContent: "space-between", + alignItems: "center", +})); + +const FeedbackTitle = styled(Box)(({ theme }) => ({ + position: "relative", + display: "flex", + alignItems: "center", + "& svg": { + width: "28px", + height: "auto", + color: theme.palette.primary.dark, + marginRight: theme.spacing(1), + }, +})); + +const CloseButton = styled("div")(({ theme }) => ({ + display: "flex", + alignItems: "center", + justifyContent: "flex-end", + color: theme.palette.text.primary, +})); + +const FeedbackBody = styled(Box)(({ theme }) => ({ + maxWidth: theme.breakpoints.values.formWrap, +})); + +const FeedbackForm = styled("form")(({ theme }) => ({ + "& > *": { + ...contentFlowSpacing(theme), + }, +})); + +const FeedbackComponent: React.FC = () => { + return ( + <> + + + + + + + + + + What would you like to share? + + + + + + + + + + + + + + + + + + + + + + + Back + + + + + + + + + + + Report an issue + + + + + + + + + + + + + + + + + + + + + + + + + Back + + + + + + + + + + + Share an idea + + + + + + + + + + + + + + + + + + + + Back + + + + + + + + + + + Share a comment + + + + + + + + + + + + + + + + + + + Thank you for sharing feedback + + + + + + + + + + We appreciate it lorem ipsum dolor sit amet, consectetuer + adipiscing elit. Aenean commodo ligula eget dolor. + + + + + + + ); +}; + +export default FeedbackComponent; diff --git a/editor.planx.uk/src/components/FeedbackPhaseBanner.tsx b/editor.planx.uk/src/components/FeedbackPhaseBanner.tsx new file mode 100644 index 0000000000..37809ce5e0 --- /dev/null +++ b/editor.planx.uk/src/components/FeedbackPhaseBanner.tsx @@ -0,0 +1,83 @@ +import Box from "@mui/material/Box"; +import Button from "@mui/material/Button"; +import Container from "@mui/material/Container"; +import Link from "@mui/material/Link"; +import { styled } from "@mui/material/styles"; +import Typography from "@mui/material/Typography"; +import React from "react"; + +const Root = styled(Box)(({ theme }) => ({ + width: "100%", + backgroundColor: theme.palette.background.paper, +})); + +const ReportButton = styled(Button)(({ theme }) => ({ + backgroundColor: theme.palette.common.white, + color: theme.palette.primary.main, + padding: "0.7em 1em", +})); + +const Inner = styled(Box)(({ theme }) => ({ + width: "100%", + display: "flex", + justifyContent: "space-between", + alignItems: "center", + flexWrap: "wrap", + padding: theme.spacing(0.75, 0), +})); + +const PhaseWrap = styled(Box)(({ theme }) => ({ + display: "flex", + justifyContent: "start", + alignItems: "start", + padding: theme.spacing(0.5, 1, 0.5, 0), +})); + +const BetaFlag = styled(Box)(({ theme }) => ({ + betaIcon: { + width: "100%", + [theme.breakpoints.up("sm")]: { + width: "auto", + marginRight: theme.spacing(2), + }, + }, +})); + +export default function FeedbackPhaseBanner(): FCReturn { + return ( + + + + + + PUBLIC BETA + + + This is a new service. Your feedback will help us + improve it. + + + Report an issue with this page + + + + ); +} diff --git a/editor.planx.uk/src/components/MoreInfoFeedback.tsx b/editor.planx.uk/src/components/MoreInfoFeedback.tsx new file mode 100644 index 0000000000..530f56a721 --- /dev/null +++ b/editor.planx.uk/src/components/MoreInfoFeedback.tsx @@ -0,0 +1,82 @@ +import CancelIcon from "@mui/icons-material/Cancel"; +import CheckCircleIcon from "@mui/icons-material/CheckCircle"; +import Box from "@mui/material/Box"; +import Button from "@mui/material/Button"; +import Container from "@mui/material/Container"; +import { styled } from "@mui/material/styles"; +import Typography from "@mui/material/Typography"; +import { contentFlowSpacing } from "@planx/components/shared/Preview/Card"; +import React from "react"; +import FeedbackDisclaimer from "ui/public/FeedbackDisclaimer"; +import FeedbackOption from "ui/public/FeedbackOption"; +import Input from "ui/shared/Input"; + +const MoreInfoFeedback = styled(Box)(({ theme }) => ({ + borderTop: `2px solid ${theme.palette.border.main}`, + padding: theme.spacing(2.5, 4, 8, 0), + [theme.breakpoints.up("sm")]: { + padding: theme.spacing(3, 4, 8, 1), + }, +})); + +const FeedbackBody = styled(Box)(({ theme }) => ({ + padding: theme.spacing(1, 0), + "& form > * + *": { + ...contentFlowSpacing(theme), + }, +})); + +const MoreInfoFeedbackComponent: React.FC = () => { + return ( + <> + + + + Did this help to answer your question? + + + + + + + + + + + + Please help us to improve this service by sharing feedback + + +
+ + + + +
+
+
+ + + + + Thank you for sharing feedback + + + + We appreciate it lorem ipsum dolor sit amet, consectetuer + adipiscing elit. Aenean commodo ligula eget dolor. + + + + + + ); +}; + +export default MoreInfoFeedbackComponent; diff --git a/editor.planx.uk/src/lib/featureFlags.ts b/editor.planx.uk/src/lib/featureFlags.ts index 7bf56fbe6f..d676604615 100644 --- a/editor.planx.uk/src/lib/featureFlags.ts +++ b/editor.planx.uk/src/lib/featureFlags.ts @@ -2,6 +2,7 @@ const AVAILABLE_FEATURE_FLAGS = [ "DISABLE_SAVE_AND_RETURN", "SHOW_TEAM_SETTINGS", + "SHOW_INTERNAL_FEEDBACK", ] as const; type featureFlag = (typeof AVAILABLE_FEATURE_FLAGS)[number]; diff --git a/editor.planx.uk/src/pages/Preview/Questions.tsx b/editor.planx.uk/src/pages/Preview/Questions.tsx index 58b1dafaf7..5f83ef110a 100644 --- a/editor.planx.uk/src/pages/Preview/Questions.tsx +++ b/editor.planx.uk/src/pages/Preview/Questions.tsx @@ -23,7 +23,7 @@ const BackBar = styled(Box)(() => ({ zIndex: "1000", })); -const BackButton = styled(ButtonBase)(({ theme, hidden }) => ({ +export const BackButton = styled(ButtonBase)(({ theme, hidden }) => ({ visibility: "visible", pointerEvents: "auto", display: "flex", @@ -170,7 +170,11 @@ const Questions = ({ previewEnvironment }: QuestionsProps) => {