diff --git a/api.planx.uk/types.ts b/api.planx.uk/types.ts index 496d195e1e..a5d5c2e5da 100644 --- a/api.planx.uk/types.ts +++ b/api.planx.uk/types.ts @@ -44,7 +44,6 @@ export interface UserData { data?: Record; auto?: boolean; override?: Record; - feedback?: string; } export type Breadcrumb = Record; diff --git a/editor.planx.uk/src/@planx/components/PlanningConstraints/Public.tsx b/editor.planx.uk/src/@planx/components/PlanningConstraints/Public.tsx index 5042e782c9..2f50fb494a 100644 --- a/editor.planx.uk/src/@planx/components/PlanningConstraints/Public.tsx +++ b/editor.planx.uk/src/@planx/components/PlanningConstraints/Public.tsx @@ -9,7 +9,6 @@ import Card from "@planx/components/shared/Preview/Card"; import QuestionHeader from "@planx/components/shared/Preview/QuestionHeader"; import type { PublicProps } from "@planx/components/ui"; import DelayedLoadingIndicator from "components/DelayedLoadingIndicator"; -import { useFormik } from "formik"; import capitalize from "lodash/capitalize"; import { useStore } from "pages/FlowEditor/lib/store"; import { handleSubmit } from "pages/Preview/Node"; @@ -136,8 +135,7 @@ function Component(props: Props) { disclaimer={props.disclaimer} constraints={constraints} metadata={metadata} - previousFeedback={props.previouslySubmittedData?.feedback} - handleSubmit={(values: { feedback?: string }) => { + handleSubmit={() => { const _constraints: Array< EnhancedGISResponse | GISResponse["constraints"] > = []; @@ -175,7 +173,6 @@ function Component(props: Props) { }; props.handleSubmit?.({ - ...values, data: passportData, }); }} @@ -201,32 +198,15 @@ export type PlanningConstraintsContentProps = { disclaimer: string; constraints: GISResponse["constraints"]; metadata: GISResponse["metadata"]; - handleSubmit: (values: { feedback: string }) => void; + handleSubmit: () => void; refreshConstraints: () => void; - previousFeedback?: string; }; export function PlanningConstraintsContent( props: PlanningConstraintsContentProps, ) { - const { - title, - description, - disclaimer, - constraints, - metadata, - handleSubmit, - refreshConstraints, - previousFeedback, - } = props; - const formik = useFormik({ - initialValues: { - feedback: previousFeedback || "", - }, - onSubmit: (values) => { - handleSubmit?.(values); - }, - }); + const { title, description, constraints, metadata, refreshConstraints, disclaimer } = + props; const error = constraints.error || undefined; const showError = error || !Object.values(constraints)?.length; @@ -239,7 +219,7 @@ export function PlanningConstraintsContent( ); return ( - + {showError && ( { expect(screen.getByText("About the property")).toBeInTheDocument(); expect(screen.getByText("Property type")).toBeInTheDocument(); - expect(screen.getByText("Change")).toBeInTheDocument(); - expect(screen.queryByText("Report an inaccuracy")).not.toBeInTheDocument(); - await user.click(screen.getByTestId("continue-button")); expect(handleSubmit).toHaveBeenCalledTimes(1); }); @@ -67,26 +64,7 @@ test("renders correctly when property override is toggled off", async () => { expect(screen.getByText("Property type")).toBeInTheDocument(); expect(screen.queryByText("Change")).not.toBeInTheDocument(); - expect(screen.getByText("Report an inaccuracy")).toBeInTheDocument(); await user.click(screen.getByTestId("continue-button")); expect(handleSubmit).toHaveBeenCalledTimes(1); }); - -test("retains previously submitted feedback when going back", async () => { - const { user } = setup( - - - , - ); - - expect(screen.getByText("Report an inaccuracy")).toBeInTheDocument(); - - // expand the feedback input - await user.click(screen.getByText("Report an inaccuracy")); - expect(screen.getByText("My property type is wrong")).toBeInTheDocument(); -}); diff --git a/editor.planx.uk/src/@planx/components/PropertyInformation/Public.tsx b/editor.planx.uk/src/@planx/components/PropertyInformation/Public.tsx index 1c86fb91a6..09aab9c4f4 100644 --- a/editor.planx.uk/src/@planx/components/PropertyInformation/Public.tsx +++ b/editor.planx.uk/src/@planx/components/PropertyInformation/Public.tsx @@ -9,7 +9,6 @@ import QuestionHeader from "@planx/components/shared/Preview/QuestionHeader"; import { SummaryListTable } from "@planx/components/shared/Preview/SummaryList"; import type { PublicProps } from "@planx/components/ui"; import { Feature } from "@turf/helpers"; -import { useFormik } from "formik"; import { publicClient } from "lib/graphql"; import find from "lodash/find"; import { useAnalyticsTracking } from "pages/FlowEditor/lib/analyticsProvider"; @@ -19,7 +18,6 @@ import React from "react"; import type { SiteAddress } from "../FindProperty/model"; import { FETCH_BLPU_CODES } from "../FindProperty/Public"; -import FeedbackInput from "../shared/FeedbackInput"; import { ErrorSummaryContainer } from "../shared/Preview/ErrorSummaryContainer"; import type { PropertyInformation } from "./model"; @@ -46,7 +44,6 @@ function Component(props: PublicProps) { } titleBoundary={passport.data?.["property.boundary.title"]} blpuCodes={blpuCodes} - previousFeedback={props.previouslySubmittedData?.feedback} overrideAnswer={overrideAnswer} handleSubmit={props.handleSubmit} /> @@ -78,7 +75,6 @@ export interface PresentationalProps { localAuthorityDistrict?: string[]; titleBoundary?: Feature; blpuCodes?: any; - previousFeedback?: string; overrideAnswer: (fn: string) => void; handleSubmit?: handleSubmit; } @@ -104,20 +100,10 @@ export function Presentational(props: PresentationalProps) { localAuthorityDistrict, titleBoundary, blpuCodes, - previousFeedback, overrideAnswer, handleSubmit, } = props; const teamName = useStore((state) => state.teamName); - const formik = useFormik({ - initialValues: { - feedback: previousFeedback || "", - }, - onSubmit: (values) => { - handleSubmit?.(values); - }, - }); - const propertyDetails: PropertyDetail[] = [ { heading: "Address", @@ -143,7 +129,7 @@ export function Presentational(props: PresentationalProps) { ]; return ( - +

@@ -179,15 +165,6 @@ export function Presentational(props: PresentationalProps) { overrideAnswer={overrideAnswer} /> )} - {!showPropertyTypeOverride && ( - - - - )} ); } diff --git a/editor.planx.uk/src/@planx/components/Result/Public/index.tsx b/editor.planx.uk/src/@planx/components/Result/Public/index.tsx index 1f131027c5..cdfdaf144a 100644 --- a/editor.planx.uk/src/@planx/components/Result/Public/index.tsx +++ b/editor.planx.uk/src/@planx/components/Result/Public/index.tsx @@ -2,14 +2,12 @@ import ErrorOutline from "@mui/icons-material/ErrorOutline"; import Box from "@mui/material/Box"; import { styled } from "@mui/material/styles"; import Typography from "@mui/material/Typography"; -import FeedbackInput from "@planx/components/shared/FeedbackInput"; import Card from "@planx/components/shared/Preview/Card"; import SimpleExpand from "@planx/components/shared/Preview/SimpleExpand"; import { WarningContainer } from "@planx/components/shared/Preview/WarningContainer"; -import { useFormik } from "formik"; import { Store, useStore } from "pages/FlowEditor/lib/store"; import type { handleSubmit } from "pages/Preview/Node"; -import React, { useEffect, useState } from "react"; +import React from "react"; import { FONT_WEIGHT_SEMI_BOLD } from "theme"; import type { Node, TextContent } from "types"; @@ -95,32 +93,10 @@ const Result: React.FC = ({ reasonsTitle = "", responses, disclaimer, - previouslySubmittedData, }) => { - const formik = useFormik({ - initialValues: { - feedback: previouslySubmittedData?.feedback || "", - }, - onSubmit: (values, { resetForm }) => { - if (values.feedback) { - resetForm(); - } - handleSubmit?.({ feedback: values.feedback }); - }, - }); const visibleResponses = responses.filter((r) => !r.hidden); const hiddenResponses = responses.filter((r) => r.hidden); - const [showSubmitButton, setShowSubmitButton] = useState( - Boolean(handleSubmit), - ); - - useEffect(() => { - if (handleSubmit) return; - - setShowSubmitButton(formik.values.feedback.length > 0); - }, [formik.values.feedback]); - return ( = ({ description={description} color={headingColor} /> - + {reasonsTitle} @@ -178,11 +151,6 @@ const Result: React.FC = ({ )} - ); diff --git a/editor.planx.uk/src/@planx/components/shared/FeedbackInput.tsx b/editor.planx.uk/src/@planx/components/shared/FeedbackInput.tsx deleted file mode 100644 index 393d745dfb..0000000000 --- a/editor.planx.uk/src/@planx/components/shared/FeedbackInput.tsx +++ /dev/null @@ -1,28 +0,0 @@ -import { styled } from "@mui/material/styles"; -import Typography from "@mui/material/Typography"; -import React from "react"; -import ReactMarkdown from "react-markdown"; -import CollapsibleInput from "ui/public/CollapsibleInput"; - -interface Props { - handleChange: (e: React.ChangeEvent) => void; - value: string; - text: string; -} - -const StyledReactMarkdown = styled(ReactMarkdown)(() => ({ - "& p": { - margin: 0, - textAlign: "left", - }, -})); - -const FeedbackInput: React.FC = ({ text, ...componentProps }: Props) => ( - - - {text} - - -); - -export default FeedbackInput; diff --git a/editor.planx.uk/src/components/Feedback/index.test.tsx b/editor.planx.uk/src/components/Feedback/index.test.tsx index 15be73e5c0..181d6e09d9 100644 --- a/editor.planx.uk/src/components/Feedback/index.test.tsx +++ b/editor.planx.uk/src/components/Feedback/index.test.tsx @@ -143,11 +143,46 @@ describe("Feedback component triage journey", () => { await user.click(getByText("feedback")); await user.click(getByRole("button", { name: "Comment" })); + await waitFor(() => {}); + expect(getByText("Share a comment")).toBeInTheDocument(); + + await user.type(getByTestId("userCommentTextarea"), "What about 3D maps"); + + await user.click(getByText("Send feedback")); + await waitFor(() => { - expect(getByText("Share a comment")).toBeInTheDocument(); + expect(getInternalFeedbackMetadata).toBeCalledTimes(1); + expect(insertFeedbackMutation).toBeCalledTimes(1); + expect(getByText("Thank you for your feedback.")).toBeInTheDocument(); }); + }); + + test("Selecting 'Inaccuracy' from triage scrolls the comment form into view", async () => { + const { getByText, getByRole, user } = setup(); + + await user.click(getByText("feedback")); + await user.click(getByRole("button", { name: "Inaccuracy" })); - await user.type(getByTestId("userCommentTextarea"), "This page is great"); + expect(scrollIntoViewMock).toBeCalledTimes(2); + + await waitFor(() => { + expect(getByText("Report an inaccuracy")).toBeInTheDocument(); + }); + }); + + test("Submitting 'Inaccuracy' form changes view to thank you message", async () => { + const { getByText, getByTestId, getByRole, user } = setup(); + + await user.click(getByText("feedback")); + await user.click(getByRole("button", { name: "Inaccuracy" })); + + await waitFor(() => {}); + expect(getByText("Report an inaccuracy")).toBeInTheDocument(); + + await user.type( + getByTestId("userCommentTextarea"), + "This information is wrong", + ); await user.click(getByText("Send feedback")); diff --git a/editor.planx.uk/src/components/Feedback/index.tsx b/editor.planx.uk/src/components/Feedback/index.tsx index 982a9fa70d..361e734f86 100644 --- a/editor.planx.uk/src/components/Feedback/index.tsx +++ b/editor.planx.uk/src/components/Feedback/index.tsx @@ -2,6 +2,7 @@ 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 RuleIcon from "@mui/icons-material/Rule"; import WarningIcon from "@mui/icons-material/Warning"; import Box from "@mui/material/Box"; import Container from "@mui/material/Container"; @@ -81,7 +82,7 @@ export type FeedbackFormInput = { }; const Feedback: React.FC = () => { - type FeedbackCategory = "issue" | "idea" | "comment"; + type FeedbackCategory = "issue" | "idea" | "comment" | "inaccuracy"; type View = "banner" | "triage" | FeedbackCategory | "thanks"; @@ -250,6 +251,12 @@ const Feedback: React.FC = () => { label="Comment" showArrow /> + handleFeedbackViewClick("inaccuracy")} + Icon={RuleIcon} + label="Inaccuracy" + showArrow + /> @@ -288,6 +295,38 @@ const Feedback: React.FC = () => { ); } + function ReportAnInaccuracy(): FCReturn { + const issueFormInputs: FeedbackFormInput[] = [ + { + name: "userComment", + label: "What data is inaccurate?", + id: "inaccuracy-input", + }, + ]; + + return ( + + + + + + + + Report an inaccuracy + + + + + + + + + ); + } + function ShareAnIdea(): FCReturn { const shareFormInputs: FeedbackFormInput[] = [ { @@ -380,6 +419,8 @@ const Feedback: React.FC = () => { return ; case "comment": return ; + case "inaccuracy": + return ; case "thanks": return ; } diff --git a/editor.planx.uk/src/pages/FlowEditor/lib/store/index.ts b/editor.planx.uk/src/pages/FlowEditor/lib/store/index.ts index 23ea9f0e18..bc3b32961c 100644 --- a/editor.planx.uk/src/pages/FlowEditor/lib/store/index.ts +++ b/editor.planx.uk/src/pages/FlowEditor/lib/store/index.ts @@ -26,7 +26,6 @@ export declare namespace Store { data?: Record; auto?: boolean; override?: Record; - feedback?: string; }; export type breadcrumbs = Record; export type cachedBreadcrumbs = Record | undefined; 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 1c4797e906..ea8a152d34 100644 --- a/editor.planx.uk/src/pages/FlowEditor/lib/store/preview.ts +++ b/editor.planx.uk/src/pages/FlowEditor/lib/store/preview.ts @@ -283,17 +283,10 @@ export const previewStore: StateCreator< if (userData) { // add breadcrumb - const { - answers = [], - data = {}, - auto = false, - override, - feedback, - } = userData; + const { answers = [], data = {}, auto = false, override } = userData; const breadcrumb: Store.userData = { auto: Boolean(auto) }; if (answers?.length > 0) breadcrumb.answers = answers; - if (feedback) breadcrumb.feedback = feedback; const filteredData = objectWithoutNullishValues(data); if (Object.keys(filteredData).length > 0) breadcrumb.data = filteredData; diff --git a/editor.planx.uk/src/pages/Preview/Questions.tsx b/editor.planx.uk/src/pages/Preview/Questions.tsx index 36e9885f3e..f9a10c74ec 100644 --- a/editor.planx.uk/src/pages/Preview/Questions.tsx +++ b/editor.planx.uk/src/pages/Preview/Questions.tsx @@ -135,17 +135,16 @@ const Questions = ({ previewEnvironment }: QuestionsProps) => { data = undefined, answers = [], auto = false, - feedback = undefined, } = (() => { try { - const { answers = [], data, auto, feedback } = userData as any; - return { answers: answers.filter(Boolean), data, auto, feedback }; + const { answers = [], data, auto } = userData as any; + return { answers: answers.filter(Boolean), data, auto }; } catch (err) { return {}; } })(); - record(id, { answers, data, auto, feedback }); + record(id, { answers, data, auto }); }; const goBack = useCallback(() => { diff --git a/editor.planx.uk/src/ui/public/CollapsibleInput.stories.tsx b/editor.planx.uk/src/ui/public/CollapsibleInput.stories.tsx deleted file mode 100644 index c796706e03..0000000000 --- a/editor.planx.uk/src/ui/public/CollapsibleInput.stories.tsx +++ /dev/null @@ -1,38 +0,0 @@ -import FeedbackInput from "@planx/components/shared/FeedbackInput"; -import { Meta, StoryObj } from "@storybook/react"; -import React, { useState } from "react"; - -import CollapsibleInput, { Props } from "./CollapsibleInput"; - -const meta = { - title: "Design System/Atoms/Form Elements/CollapsibleInput", - component: CollapsibleInput, -} satisfies Meta; - -export default meta; - -type Story = StoryObj; - -// TODO clarify use of args.children versus render here -export const Basic = { - args: { - name: "Feedback", - value: "feedback", - children:

This is a child element.

, - ariaLabel: "Feedback", - }, - render: (_args: Props) => { - /* eslint-disable react-hooks/rules-of-hooks */ - // See https://github.com/storybookjs/storybook/issues/21115 - const [text, setText] = useState(""); - return ( - { - setText(ev.target.value); - }} - value={text} - /> - ); - }, -} satisfies Story; diff --git a/editor.planx.uk/src/ui/public/CollapsibleInput.tsx b/editor.planx.uk/src/ui/public/CollapsibleInput.tsx deleted file mode 100644 index c2e6dfdf3b..0000000000 --- a/editor.planx.uk/src/ui/public/CollapsibleInput.tsx +++ /dev/null @@ -1,37 +0,0 @@ -import Collapse from "@mui/material/Collapse"; -import Link from "@mui/material/Link"; -import React, { useState } from "react"; -import Input from "ui/shared/Input"; - -export interface Props { - children: JSX.Element[] | JSX.Element; - handleChange: (ev: React.ChangeEvent) => void; - value: string; - name: string; - ariaLabel: string; -} - -const CollapsibleInput: React.FC = (props: Props) => { - const [expanded, setExpanded] = useState(false); - - return ( - <> - setExpanded((x) => !x)}> - {props.children} - - - - - - ); -}; - -export default CollapsibleInput; diff --git a/hasura.planx.uk/migrations/1708073303980_insert_into_public_feedback_type_enum/down.sql b/hasura.planx.uk/migrations/1708073303980_insert_into_public_feedback_type_enum/down.sql new file mode 100644 index 0000000000..6621f760af --- /dev/null +++ b/hasura.planx.uk/migrations/1708073303980_insert_into_public_feedback_type_enum/down.sql @@ -0,0 +1 @@ +DELETE FROM "public"."feedback_type_enum" WHERE "value" = 'inaccuracy'; diff --git a/hasura.planx.uk/migrations/1708073303980_insert_into_public_feedback_type_enum/up.sql b/hasura.planx.uk/migrations/1708073303980_insert_into_public_feedback_type_enum/up.sql new file mode 100644 index 0000000000..5a663f0e21 --- /dev/null +++ b/hasura.planx.uk/migrations/1708073303980_insert_into_public_feedback_type_enum/up.sql @@ -0,0 +1 @@ +INSERT INTO "public"."feedback_type_enum"("comment", "value") VALUES (E'A user is reporting inaccurate data or content', E'inaccuracy');