Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat: feedback component submits to feedback table + tidy ups #3900

Merged
merged 45 commits into from
Nov 12, 2024
Merged
Show file tree
Hide file tree
Changes from 43 commits
Commits
Show all changes
45 commits
Select commit Hold shift + click to select a range
918afc6
Clarify how-to doc
jamdelion Oct 28, 2024
576087e
Reorganise Feedback folder
jamdelion Oct 29, 2024
abcc570
Use latest planx-core hash
jamdelion Oct 29, 2024
9a12ba4
Use types.feedback
jamdelion Oct 29, 2024
b559588
Add types.feedback to more places
jamdelion Oct 29, 2024
20cba62
Fix import
jamdelion Oct 29, 2024
2e225b5
Add to preview node
jamdelion Oct 29, 2024
8ecfd4f
Add feedback to summaryList
jamdelion Oct 29, 2024
01187ec
Update doc
jamdelion Oct 29, 2024
de19573
Add basic editor test
jamdelion Oct 30, 2024
96a9872
Improve editor modal with more fields - not wired up yet
jamdelion Oct 30, 2024
637fa91
Add feature flag for dropdown menu
jamdelion Oct 30, 2024
40393bd
Update props
jamdelion Oct 30, 2024
7ff31e4
Render richtextinputs ok with reactorhtml component
jamdelion Oct 30, 2024
70648bc
Use placeholders if no inputs given
jamdelion Oct 30, 2024
c5ed74a
Remove validation line
jamdelion Oct 30, 2024
26bc608
Merge branch 'main' into jh/feedback-editor-side
jamdelion Oct 30, 2024
79e2377
Fixes after merge conflict
jamdelion Oct 30, 2024
d224f21
Merge branch 'main' into jh/follow-on-feedback-component
jamdelion Nov 1, 2024
ae23fc7
Make facebox text lowercase and improve storybook
jamdelion Nov 1, 2024
9f39ac9
Merge branch 'main' into jh/follow-on-feedback-component
jamdelion Nov 1, 2024
478a8cd
Reverse inappropriate linting
jamdelion Nov 1, 2024
a1d3e60
Add moreInfo component propbs into public view and update doc
jamdelion Nov 1, 2024
50c0c58
Use shared disclaimer component
jamdelion Nov 1, 2024
779d7be
Use defaultContent obj instead of placeholders
jamdelion Nov 1, 2024
015867c
Use Input instead of richtextinput in public view
jamdelion Nov 1, 2024
47d51bc
add aria label
jamdelion Nov 4, 2024
cc587ee
Merge branch 'main' into jh/follow-on-feedback-component
jamdelion Nov 4, 2024
38d134c
Merge branch 'main' into jh/follow-on-feedback-component
jamdelion Nov 4, 2024
0cb581e
On submit add to feedback table and breadcrumbs
jamdelion Nov 6, 2024
9d4fbe9
Merge branch 'main' into jh/follow-on-feedback-component
jamdelion Nov 6, 2024
7e7eb05
Fix unit test
jamdelion Nov 6, 2024
adeb9da
Fix type issue
jamdelion Nov 6, 2024
d380647
Add feedback log category for component feedback
jamdelion Nov 6, 2024
a8380f9
Undo changes to tables.yaml
jamdelion Nov 7, 2024
0fd922b
Merge branch 'main' into jh/follow-on-feedback-component
jamdelion Nov 7, 2024
fc55b80
style: Feedback component style refinement (#3925)
ianjon3s Nov 11, 2024
0aba609
Minor edits from PR comments - delete unneccessary code
jamdelion Nov 11, 2024
aa19903
Change feedback field to userComment
jamdelion Nov 11, 2024
72f3da8
Use capitlize type utility
jamdelion Nov 11, 2024
637f525
Change feedback_score column to number
jamdelion Nov 11, 2024
bb6f614
Add down.sql for add feedback_score column as int
jamdelion Nov 11, 2024
22fdc80
Fix disclaimer and default fallbacks
jamdelion Nov 11, 2024
bc4d4d8
Remove fallback in disclaimer
jamdelion Nov 11, 2024
fd5b0ab
Delete superfluous migrations
jamdelion Nov 12, 2024
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 2 additions & 2 deletions editor.planx.uk/docs/adding-a-new-component.md
jamdelion marked this conversation as resolved.
Show resolved Hide resolved
Original file line number Diff line number Diff line change
Expand Up @@ -59,7 +59,7 @@ function SetValueComponent(props: Props) {
}
```

5. `Public.tsx`
5. `Public.tsx` - note the props below are needed to display the 'More Information' sidebar correctly.

```typescript
import { PublicProps } from "@planx/components/shared/types";
Expand Down Expand Up @@ -156,4 +156,4 @@ function isTypeForBopsPayload(type?: TYPES) {
// ...
}
}
```
```
29 changes: 15 additions & 14 deletions editor.planx.uk/src/@planx/components/Feedback/Editor/Editor.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -14,17 +14,13 @@
import Input from "ui/shared/Input/Input";
import InputRow from "ui/shared/InputRow";

import {
descriptionPlaceholder,
disclaimerPlaceholder,
freeformQuestionPlaceholder,
ratingQuestionPlaceholder,
titlePlaceholder,
} from "../components/placeholders";
import { defaultContent } from "../components/defaultContent";
import { Feedback, parseFeedback } from "../model";

type FeedbackEditorProps = EditorProps<TYPES.Feedback, Feedback>;

const HTML_TAG_REGEX = /<[^>]*>/g;

export const FeedbackEditor = (props: FeedbackEditorProps) => {
const formik = useFormik<Feedback>({
initialValues: parseFeedback(props.node?.data),
Expand All @@ -47,9 +43,9 @@
<InputLabel label="Title">
<Input
format="large"
placeholder={titlePlaceholder}
placeholder={defaultContent.title}
name="title"
value={formik.values.title || titlePlaceholder}
value={formik.values.title}
onChange={formik.handleChange}
/>
</InputLabel>
Expand All @@ -58,7 +54,7 @@
<InputLabel label="Description" htmlFor="description">
<RichTextInput
name="description"
value={formik.values.description || descriptionPlaceholder}
value={formik.values.description}
placeholder="Description"
onChange={formik.handleChange}
/>
Expand All @@ -68,7 +64,10 @@
<InputRow>
<InputLabel label="Rating question" htmlFor="ratingQuestion">
<RichTextInput
placeholder={ratingQuestionPlaceholder}
placeholder={defaultContent.ratingQuestion?.replaceAll(
HTML_TAG_REGEX,
"",
)}
Dismissed Show dismissed Hide dismissed
name="ratingQuestion"
value={formik.values.ratingQuestion}
onChange={formik.handleChange}
Expand All @@ -78,7 +77,10 @@
<InputRow>
<InputLabel label="Freeform question" htmlFor="freeformQuestion">
<RichTextInput
placeholder={freeformQuestionPlaceholder}
placeholder={defaultContent.freeformQuestion?.replaceAll(
HTML_TAG_REGEX,
"",
)}
Dismissed Show dismissed Hide dismissed
name="freeformQuestion"
value={formik.values.freeformQuestion}
onChange={formik.handleChange}
Expand All @@ -89,8 +91,7 @@
<InputLabel label="Disclaimer text" htmlFor="disclaimer">
<RichTextInput
name="disclaimer"
value={formik.values.disclaimer || disclaimerPlaceholder}
placeholder={disclaimerPlaceholder}
value={formik.values.disclaimer || defaultContent.disclaimer}
onChange={formik.handleChange}
/>
</InputLabel>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2,12 +2,15 @@ import { Meta, StoryObj } from "@storybook/react";
import React from "react";

import { FeedbackEditor } from "./Editor";

const meta = {
title: "Editor Components/Feedback",
component: FeedbackEditor,
} satisfies Meta<typeof FeedbackEditor>;

type Story = StoryObj<typeof meta>;
export default meta;

export const Basic = {
render: () => <FeedbackEditor />,
} satisfies Story;
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
import { Meta, StoryObj } from "@storybook/react";

import { defaultContent } from "../components/defaultContent";
import Public from "./Public";

const meta = {
Expand All @@ -12,8 +13,5 @@ type Story = StoryObj<typeof meta>;
export default meta;

export const Basic = {
args: {
title: "Tell us what you think",
feedbackRequired: false,
},
args: defaultContent,
} satisfies Story;
Original file line number Diff line number Diff line change
@@ -1,4 +1,8 @@
import { screen } from "@testing-library/react";
import {
getInternalFeedbackMetadata,
insertFeedbackMutation,
} from "lib/feedback";
import React from "react";
import { setup } from "testUtils";
import { vi } from "vitest";
Expand All @@ -7,6 +11,10 @@ import { axe } from "vitest-axe";
import FeedbackComponent from "./Public";

const handleSubmit = vi.fn();
vi.mock("lib/feedback", () => ({
getInternalFeedbackMetadata: vi.fn(),
insertFeedbackMutation: vi.fn(),
}));

describe("when the Feedback component is rendered", async () => {
it("should not have any accessibility violations", async () => {
Expand All @@ -31,6 +39,7 @@ describe("when the Feedback component is rendered", async () => {
await user.click(screen.getByTestId("feedback-button-terrible"));
await user.click(screen.getByTestId("continue-button"));

expect(handleSubmit).toHaveBeenCalled();
expect(getInternalFeedbackMetadata).toBeCalled();
expect(insertFeedbackMutation).toBeCalled();
});
});
76 changes: 55 additions & 21 deletions editor.planx.uk/src/@planx/components/Feedback/Public/Public.tsx
Original file line number Diff line number Diff line change
@@ -1,34 +1,57 @@
import Box from "@mui/material/Box";
import Grid from "@mui/material/Grid";
import Typography from "@mui/material/Typography";
import { Disclaimer } from "@planx/components/shared/Disclaimer";
import Card from "@planx/components/shared/Preview/Card";
import { CardHeader } from "@planx/components/shared/Preview/CardHeader/CardHeader";
import type { PublicProps } from "@planx/components/shared/types";
import { FeedbackView } from "components/Feedback/types";
import { useFormik } from "formik";
import {
getInternalFeedbackMetadata,
insertFeedbackMutation,
} from "lib/feedback";
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 Input from "ui/shared/Input/Input";
import ReactMarkdownOrHtml from "ui/shared/ReactMarkdownOrHtml/ReactMarkdownOrHtml";

import { getPreviouslySubmittedData, makeData } from "../../shared/utils";
import { FaceBox } from "../components/FaceBox";
import { Feedback, FormProps } from "../model";
import { StyledToggleButtonGroup } from "../styled";

export const PASSPORT_FEEDBACK_KEY = "_feedback";

const FeedbackComponent = (props: PublicProps<Feedback>): FCReturn => {
const handleSubmitFeedback = async (values: FormProps) => {
const metadata = await getInternalFeedbackMetadata();
const data = {
...metadata,
...values,
feedbackType: "component" as FeedbackView,
};
const submitFeedbackResult = await insertFeedbackMutation(data).catch(
(err) => {
console.error(err);
},
);
props.handleSubmit?.(makeData(props, values, PASSPORT_FEEDBACK_KEY));
if (!submitFeedbackResult) {
return;
}
};

const formik = useFormik<FormProps>({
initialValues: getPreviouslySubmittedData(props) ?? {
feedbackScore: "",
feedback: "",
},
onSubmit: (values) => {
props.handleSubmit?.(makeData(props, values));
userComment: "",
},
onSubmit: handleSubmitFeedback,
});

const handleFeedbackChange = (
Expand All @@ -42,13 +65,18 @@ const FeedbackComponent = (props: PublicProps<Feedback>): FCReturn => {

return (
<Card handleSubmit={formik.handleSubmit}>
<CardHeader title={props.title} />
<CardHeader
title={props.title}
info={props.info}
policyRef={props.policyRef}
howMeasured={props.howMeasured}
/>
<ReactMarkdownOrHtml
source={props.description}
id={"DESCRIPTION_TEXT"}
openLinksOnNewTab
/>
<Box pt={2} mb={3}>
<Box my={4}>
{props.ratingQuestion && (
<InputLabel
label={
Expand All @@ -67,34 +95,39 @@ const FeedbackComponent = (props: PublicProps<Feedback>): FCReturn => {
onChange={handleFeedbackChange}
aria-label="feedback score"
>
<Grid container columnSpacing={15} component="fieldset">
<Grid
container
columnSpacing={2}
component="fieldset"
direction={{ xs: "column", formWrap: "row" }}
>
<FaceBox
value="1"
value={1}
testId="feedback-button-terrible"
icon={TerribleFace}
label="Terrible"
altText="very unhappy face"
/>
<FaceBox
value="2"
value={2}
icon={PoorFace}
label="Poor"
altText="slightly unhappy face"
/>
<FaceBox
value="3"
value={3}
icon={NeutralFace}
label="Neutral"
altText="neutral face"
/>
<FaceBox
value="4"
value={4}
icon={GoodFace}
label="Good"
altText="smiling face"
/>
<FaceBox
value="5"
value={5}
icon={ExcellentFace}
label="Excellent"
altText="very happy face"
Expand All @@ -112,16 +145,17 @@ const FeedbackComponent = (props: PublicProps<Feedback>): FCReturn => {
}
/>
)}
<RichTextInput
name="feedback"
value={formik.values.feedback}
placeholder="What did you think?"
<Input
multiline={true}
rows={3}
name="userComment"
value={formik.values.userComment}
bordered
onChange={formik.handleChange}
aria-label="user comment"
/>
</Box>
<Typography variant="caption">
<ReactMarkdownOrHtml source={props?.disclaimer} id={"DISCLAIMER"} />
</Typography>
{props.disclaimer && <Disclaimer text={props.disclaimer} />}
</Card>
);
};
Expand Down
Original file line number Diff line number Diff line change
@@ -1,15 +1,15 @@
import Box from "@mui/material/Box";
import Grid from "@mui/material/Grid";
import ToggleButton from "@mui/material/ToggleButton";
import ToggleButton, { toggleButtonClasses } from "@mui/material/ToggleButton";
import Typography from "@mui/material/Typography";
import React, { ReactElement } from "react";

interface FaceBoxProps {
icon: string;
label: string;
label: Capitalize<string>;
altText: string;
testId?: string;
value: string;
value: number;
}

export const FaceBox = ({
Expand All @@ -20,29 +20,40 @@ export const FaceBox = ({
value,
}: FaceBoxProps): ReactElement => {
return (
<Grid item xs={2} key={label}>
<Grid item xs={2.4} key={label}>
<ToggleButton
value={value}
data-testid={testId}
sx={{
px: 0,
width: "100%",
textTransform: "none",
[`&.${toggleButtonClasses.selected}`]: {
borderColor: (theme) => theme.palette.primary.dark,
background: (theme) => theme.palette.background.paper,
},
}}
disableRipple
>
<Box
sx={(theme) => ({
p: theme.spacing(2),
p: theme.spacing(2, 1),
width: "100%",
border: `2px solid ${theme.palette.border.main} `,
width: "120px",
maxHeight: "120px",
display: "flex",
flexDirection: "column",
justifyContent: "center",
alignItems: "center",
gap: theme.spacing(0.5),
})}
>
<img src={icon} width={50} alt={altText} />
<Typography variant="body2" pt={0.5}>
<img src={icon} width={32} alt={altText} />
<Typography
jamdelion marked this conversation as resolved.
Show resolved Hide resolved
variant="body2"
sx={(theme) => ({
color: theme.palette.text.primary,
})}
>
{label}
</Typography>
</Box>
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
import { Feedback } from "../model";

export const defaultContent: Feedback = {
title: "Tell us what you think",
freeformQuestion:
"<strong>Please tell us more about your experience.</strong>",

ratingQuestion:
"<strong>How would you rate your experience with this service?</strong>",

description: `This service is a work in progress, any feedback you share about your experience will help us to improve it.
<br>
<br>
Don't share any personal or financial information in your feedback. If you do we will act according to our <a href="">privacy policy</a>.`,

disclaimer:
"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.",
feedbackRequired: false,
};
Loading
Loading