From 18ac5a9493ea6f2f41541c9f625aa5e81a31b0db Mon Sep 17 00:00:00 2001 From: Jo Humphrey <31373245+jamdelion@users.noreply.github.com> Date: Mon, 28 Oct 2024 16:41:54 +0000 Subject: [PATCH] feat: add basic in-page feedback component (#3841) --- .../components/Feedback/Feedback.stories.tsx | 19 +++ .../components/Feedback/Public.test.tsx | 32 +++++ .../src/@planx/components/Feedback/Public.tsx | 119 ++++++++++++++++++ .../Feedback/components/FaceBox.tsx | 52 ++++++++ .../src/@planx/components/Feedback/styled.ts | 15 +++ .../src/@planx/components/Feedback/types.ts | 11 ++ .../src/ui/images/feedback_filled-01.svg | 9 ++ .../src/ui/images/feedback_filled-02.svg | 9 ++ .../src/ui/images/feedback_filled-03.svg | 9 ++ .../src/ui/images/feedback_filled-04.svg | 9 ++ .../src/ui/images/feedback_filled-05.svg | 9 ++ .../src/ui/images/feedback_stroke-01.svg | 9 ++ .../src/ui/images/feedback_stroke-02.svg | 9 ++ .../src/ui/images/feedback_stroke-03.svg | 9 ++ .../src/ui/images/feedback_stroke-04.svg | 9 ++ .../src/ui/images/feedback_stroke-05.svg | 9 ++ editor.planx.uk/src/ui/public/InputLabel.tsx | 2 +- 17 files changed, 339 insertions(+), 1 deletion(-) create mode 100644 editor.planx.uk/src/@planx/components/Feedback/Feedback.stories.tsx create mode 100644 editor.planx.uk/src/@planx/components/Feedback/Public.test.tsx create mode 100644 editor.planx.uk/src/@planx/components/Feedback/Public.tsx create mode 100644 editor.planx.uk/src/@planx/components/Feedback/components/FaceBox.tsx create mode 100644 editor.planx.uk/src/@planx/components/Feedback/styled.ts create mode 100644 editor.planx.uk/src/@planx/components/Feedback/types.ts create mode 100644 editor.planx.uk/src/ui/images/feedback_filled-01.svg create mode 100644 editor.planx.uk/src/ui/images/feedback_filled-02.svg create mode 100644 editor.planx.uk/src/ui/images/feedback_filled-03.svg create mode 100644 editor.planx.uk/src/ui/images/feedback_filled-04.svg create mode 100644 editor.planx.uk/src/ui/images/feedback_filled-05.svg create mode 100644 editor.planx.uk/src/ui/images/feedback_stroke-01.svg create mode 100644 editor.planx.uk/src/ui/images/feedback_stroke-02.svg create mode 100644 editor.planx.uk/src/ui/images/feedback_stroke-03.svg create mode 100644 editor.planx.uk/src/ui/images/feedback_stroke-04.svg create mode 100644 editor.planx.uk/src/ui/images/feedback_stroke-05.svg diff --git a/editor.planx.uk/src/@planx/components/Feedback/Feedback.stories.tsx b/editor.planx.uk/src/@planx/components/Feedback/Feedback.stories.tsx new file mode 100644 index 0000000000..c976a46c5f --- /dev/null +++ b/editor.planx.uk/src/@planx/components/Feedback/Feedback.stories.tsx @@ -0,0 +1,19 @@ +import { Meta, StoryObj } from "@storybook/react"; + +import Public from "./Public"; + +const meta = { + title: "PlanX Components/Feedback", + component: Public, +} satisfies Meta; + +type Story = StoryObj; + +export default meta; + +export const Basic = { + args: { + title: "Tell us what you think", + privacyPolicyLink: "https://www.planx.uk/", + }, +} satisfies Story; diff --git a/editor.planx.uk/src/@planx/components/Feedback/Public.test.tsx b/editor.planx.uk/src/@planx/components/Feedback/Public.test.tsx new file mode 100644 index 0000000000..0b18fd6151 --- /dev/null +++ b/editor.planx.uk/src/@planx/components/Feedback/Public.test.tsx @@ -0,0 +1,32 @@ +import { screen } from "@testing-library/react"; +import React from "react"; +import { setup } from "testUtils"; +import { vi } from "vitest"; +import { axe } from "vitest-axe"; + +import FeedbackComponent from "./Public"; + +const handleSubmit = vi.fn(); + +describe("when the Feedback component is rendered", async () => { + it("should not have any accessibility violations", async () => { + const { container } = setup( + , + ); + const results = await axe(container); + expect(results).toHaveNoViolations(); + }); + it("should call handleSubmit when the continue button is pressed", async () => { + const { user } = setup( + , + ); + + await user.click(screen.getByTestId("feedback-button-terrible")); + await user.click(screen.getByTestId("continue-button")); + + expect(handleSubmit).toHaveBeenCalled(); + }); +}); diff --git a/editor.planx.uk/src/@planx/components/Feedback/Public.tsx b/editor.planx.uk/src/@planx/components/Feedback/Public.tsx new file mode 100644 index 0000000000..a26a2d7fe5 --- /dev/null +++ b/editor.planx.uk/src/@planx/components/Feedback/Public.tsx @@ -0,0 +1,119 @@ +import Box from "@mui/material/Box"; +import Grid from "@mui/material/Grid"; +import Link from "@mui/material/Link"; +import Typography from "@mui/material/Typography"; +import Card from "@planx/components/shared/Preview/Card"; +import { CardHeader } from "@planx/components/shared/Preview/CardHeader/CardHeader"; +import type { PublicProps } from "@planx/components/ui"; +import { useFormik } from "formik"; +import React from "react"; +import RichTextInput from "ui/editor/RichTextInput/RichTextInput"; +import TerribleFace from "ui/images/feedback_filled-01.svg"; +import PoorFace from "ui/images/feedback_filled-02.svg"; +import NeutralFace from "ui/images/feedback_filled-03.svg"; +import GoodFace from "ui/images/feedback_filled-04.svg"; +import ExcellentFace from "ui/images/feedback_filled-05.svg"; +import InputLabel from "ui/public/InputLabel"; + +import { getPreviouslySubmittedData, makeData } from "../shared/utils"; +import { FaceBox } from "./components/FaceBox"; +import { StyledToggleButtonGroup } from "./styled"; +import { FeedbackComponentProps, FormProps } from "./types"; + +const FeedbackComponent = ( + props: PublicProps, +): FCReturn => { + const formik = useFormik({ + initialValues: getPreviouslySubmittedData(props) ?? { + feedbackScore: "", + feedback: "", + }, + onSubmit: (values) => { + props.handleSubmit?.(makeData(props, values)); + }, + }); + + const handleFeedbackChange = ( + event: React.MouseEvent, + newValue: string | null, + ) => { + if (newValue !== null) { + formik.setFieldValue("feedbackScore", newValue); + } + }; + + return ( + + + + This service is a work in progress, any feedback you share about your + experience will help us to improve it. + + + Don't share any personal or financial information in your feedback. If + you do we will act according to our{" "} + privacy policy. + + + + + + + + + + + + + + {/* */} + + + + + + The information collected here isn't monitored by planning officers. + Don't use it to give extra information about your project or submission. + If you do, it cannot be used to assess your project. + + + ); +}; + +export default FeedbackComponent; diff --git a/editor.planx.uk/src/@planx/components/Feedback/components/FaceBox.tsx b/editor.planx.uk/src/@planx/components/Feedback/components/FaceBox.tsx new file mode 100644 index 0000000000..a12a1186a9 --- /dev/null +++ b/editor.planx.uk/src/@planx/components/Feedback/components/FaceBox.tsx @@ -0,0 +1,52 @@ +import Box from "@mui/material/Box"; +import Grid from "@mui/material/Grid"; +import ToggleButton from "@mui/material/ToggleButton"; +import Typography from "@mui/material/Typography"; +import React, { ReactElement } from "react"; + +interface FaceBoxProps { + icon: string; + label: string; + altText: string; + testId?: string; + value: string; +} + +export const FaceBox = ({ + icon, + label, + altText, + testId, + value, +}: FaceBoxProps): ReactElement => { + return ( + + + ({ + p: theme.spacing(2), + border: `2px solid ${theme.palette.border.main} `, + width: "120px", + maxHeight: "120px", + display: "flex", + flexDirection: "column", + justifyContent: "center", + alignItems: "center", + })} + > + {altText} + + {label} + + + + + ); +}; diff --git a/editor.planx.uk/src/@planx/components/Feedback/styled.ts b/editor.planx.uk/src/@planx/components/Feedback/styled.ts new file mode 100644 index 0000000000..5439945726 --- /dev/null +++ b/editor.planx.uk/src/@planx/components/Feedback/styled.ts @@ -0,0 +1,15 @@ +import { styled } from "@mui/material/styles"; +import ToggleButtonGroup, { + toggleButtonGroupClasses, +} from "@mui/material/ToggleButtonGroup"; + +export const StyledToggleButtonGroup = styled(ToggleButtonGroup)( + ({ theme }) => ({ + [`& .${toggleButtonGroupClasses.grouped}`]: { + border: 0, + padding: 0, + marginTop: theme.spacing(1), + }, + paddingBottom: theme.spacing(3.5), + }), +); diff --git a/editor.planx.uk/src/@planx/components/Feedback/types.ts b/editor.planx.uk/src/@planx/components/Feedback/types.ts new file mode 100644 index 0000000000..d39b88080a --- /dev/null +++ b/editor.planx.uk/src/@planx/components/Feedback/types.ts @@ -0,0 +1,11 @@ +import { BaseNodeData } from "../shared"; + +export interface FeedbackComponentProps extends BaseNodeData { + title: string; + privacyPolicyLink?: string; + fn?: string; +} +export interface FormProps { + feedbackScore: string; + feedback: string; +} diff --git a/editor.planx.uk/src/ui/images/feedback_filled-01.svg b/editor.planx.uk/src/ui/images/feedback_filled-01.svg new file mode 100644 index 0000000000..bd3307e0ac --- /dev/null +++ b/editor.planx.uk/src/ui/images/feedback_filled-01.svg @@ -0,0 +1,9 @@ + + + + + + + + + \ No newline at end of file diff --git a/editor.planx.uk/src/ui/images/feedback_filled-02.svg b/editor.planx.uk/src/ui/images/feedback_filled-02.svg new file mode 100644 index 0000000000..95c5609458 --- /dev/null +++ b/editor.planx.uk/src/ui/images/feedback_filled-02.svg @@ -0,0 +1,9 @@ + + + + + + + + + \ No newline at end of file diff --git a/editor.planx.uk/src/ui/images/feedback_filled-03.svg b/editor.planx.uk/src/ui/images/feedback_filled-03.svg new file mode 100644 index 0000000000..7912058369 --- /dev/null +++ b/editor.planx.uk/src/ui/images/feedback_filled-03.svg @@ -0,0 +1,9 @@ + + + + + + + + + \ No newline at end of file diff --git a/editor.planx.uk/src/ui/images/feedback_filled-04.svg b/editor.planx.uk/src/ui/images/feedback_filled-04.svg new file mode 100644 index 0000000000..7062cc9287 --- /dev/null +++ b/editor.planx.uk/src/ui/images/feedback_filled-04.svg @@ -0,0 +1,9 @@ + + + + + + + + + \ No newline at end of file diff --git a/editor.planx.uk/src/ui/images/feedback_filled-05.svg b/editor.planx.uk/src/ui/images/feedback_filled-05.svg new file mode 100644 index 0000000000..db58d2375d --- /dev/null +++ b/editor.planx.uk/src/ui/images/feedback_filled-05.svg @@ -0,0 +1,9 @@ + + + + + + + + + \ No newline at end of file diff --git a/editor.planx.uk/src/ui/images/feedback_stroke-01.svg b/editor.planx.uk/src/ui/images/feedback_stroke-01.svg new file mode 100644 index 0000000000..0a9c8261e6 --- /dev/null +++ b/editor.planx.uk/src/ui/images/feedback_stroke-01.svg @@ -0,0 +1,9 @@ + + + + + + + + + \ No newline at end of file diff --git a/editor.planx.uk/src/ui/images/feedback_stroke-02.svg b/editor.planx.uk/src/ui/images/feedback_stroke-02.svg new file mode 100644 index 0000000000..2d0f2e3441 --- /dev/null +++ b/editor.planx.uk/src/ui/images/feedback_stroke-02.svg @@ -0,0 +1,9 @@ + + + + + + + + + \ No newline at end of file diff --git a/editor.planx.uk/src/ui/images/feedback_stroke-03.svg b/editor.planx.uk/src/ui/images/feedback_stroke-03.svg new file mode 100644 index 0000000000..a6714c098f --- /dev/null +++ b/editor.planx.uk/src/ui/images/feedback_stroke-03.svg @@ -0,0 +1,9 @@ + + + + + + + + + \ No newline at end of file diff --git a/editor.planx.uk/src/ui/images/feedback_stroke-04.svg b/editor.planx.uk/src/ui/images/feedback_stroke-04.svg new file mode 100644 index 0000000000..0c23e56845 --- /dev/null +++ b/editor.planx.uk/src/ui/images/feedback_stroke-04.svg @@ -0,0 +1,9 @@ + + + + + + + + + \ No newline at end of file diff --git a/editor.planx.uk/src/ui/images/feedback_stroke-05.svg b/editor.planx.uk/src/ui/images/feedback_stroke-05.svg new file mode 100644 index 0000000000..6a0063a8e8 --- /dev/null +++ b/editor.planx.uk/src/ui/images/feedback_stroke-05.svg @@ -0,0 +1,9 @@ + + + + + + + + + \ No newline at end of file diff --git a/editor.planx.uk/src/ui/public/InputLabel.tsx b/editor.planx.uk/src/ui/public/InputLabel.tsx index 816006a974..5932b5325a 100644 --- a/editor.planx.uk/src/ui/public/InputLabel.tsx +++ b/editor.planx.uk/src/ui/public/InputLabel.tsx @@ -10,7 +10,7 @@ const Root = styled("label")(() => ({ export default function InputLabel(props: { label: string; - children: ReactNode; + children?: ReactNode; hidden?: boolean; htmlFor?: string; id?: string;