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

fix: show help text if defined for Content components #3012

Merged
merged 1 commit into from
Apr 12, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
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
18 changes: 18 additions & 0 deletions editor.planx.uk/src/@planx/components/Content/Public.test.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@ test("const { user } = setups correctly", async () => {
background: "#fff",
color: "#0B0C0C",
});
expect(screen.queryByTestId("more-info-button")).not.toBeInTheDocument();

await user.click(screen.getByTestId("continue-button"));

Expand All @@ -37,3 +38,20 @@ it("should not have any accessibility violations", async () => {
const results = await axe(container);
expect(results).toHaveNoViolations();
});

test("should display and open more information link if help text is provided", async () => {
const handleSubmit = jest.fn();

const { user } = setup(
<Content
content="This is a warning about doors"
handleSubmit={handleSubmit}
info="The number of doors impact your project fee."
/>,
);

expect(screen.getByTestId("more-info-button")).toBeInTheDocument();

await user.click(screen.getByTestId("more-info-button"));
expect(screen.findByText("Why does it matter?")).toBeTruthy();
});
78 changes: 74 additions & 4 deletions editor.planx.uk/src/@planx/components/Content/Public.tsx
Original file line number Diff line number Diff line change
@@ -1,13 +1,21 @@
import { mostReadable } from "@ctrl/tinycolor";
import HelpIcon from "@mui/icons-material/Help";
import Box from "@mui/material/Box";
import { styled } from "@mui/material/styles";
import Typography from "@mui/material/Typography";
import type { Content } from "@planx/components/Content/model";
import Card from "@planx/components/shared/Preview/Card";
import { PublicProps } from "@planx/components/ui";
import { useAnalyticsTracking } from "pages/FlowEditor/lib/analytics/provider";
import React from "react";
import { getContrastTextColor } from "styleUtils";
import { emptyContent } from "ui/editor/RichTextInput";
import ReactMarkdownOrHtml from "ui/shared/ReactMarkdownOrHtml";

import MoreInfo from "../shared/Preview/MoreInfo";
import MoreInfoSection from "../shared/Preview/MoreInfoSection";
import { HelpButton, Image } from "../shared/Preview/QuestionHeader";

export type Props = PublicProps<Content>;

const Content = styled(Box, {
Expand All @@ -32,19 +40,81 @@ Content.defaultProps = {
};

const ContentComponent: React.FC<Props> = (props) => {
const {
handleSubmit,
color,
content,
info,
policyRef,
howMeasured,
definitionImg,
} = props;

const [open, setOpen] = React.useState(false);
const { trackEvent } = useAnalyticsTracking();

const handleHelpClick = () => {
setOpen(true);
trackEvent({ event: "helpClick", metadata: {} }); // This returns a promise but we don't need to await for it
};

return (
<Card handleSubmit={props.handleSubmit} isValid>
<Card handleSubmit={handleSubmit} isValid>
<Content
color={props.color}
color={color}
data-testid="content"
p={props.color === "#ffffff" || !props.color ? 0 : 2}
p={color === "#ffffff" || !color ? 0 : 2}
>
<ReactMarkdownOrHtml
source={props.content}
source={content}
openLinksOnNewTab
manuallyIncrementHeaders
/>
</Content>
{!!(info || policyRef || howMeasured) && (
<Typography variant="subtitle1" component="div">
<HelpButton
variant="help"
title={`More information`}
aria-label={`See more information about this content`}
Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This whole block is borrowed straight from our existing <QuestionHeader />.

Ideally this aria-label would smartly parse out the h1 from the rich text rather than say "this content" but I think this should be sufficient for now?

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Agreed - I think this is fine for now, and possibly fine full stop?

There's only one context for "this content", and not multiple options (like our aria-labels on change buttons for example".

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

One for possible future discussion — should the content component also have a title, separated from the rich text input? It would mitigate the "every page must have a H1" issue we've seen highlighted elsewhere, although a review of the current usage of this component would be needed.

Copy link
Member Author

@jessicamcinchak jessicamcinchak Apr 12, 2024

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We currently have a rich text helper method incrementHeaderElements() which automatically adjusts header heirarchy to help combat that "every page must have an H1" issue - it's not perfect, but does help catch a number of user-errors!

Agree that another possible way forward here would be to require more structured input for Content nodes - but then they basically become Notices? Let's keep an eye on it 👍

onClick={handleHelpClick}
aria-haspopup="dialog"
data-testid="more-info-button"
>
<HelpIcon /> More information
</HelpButton>
</Typography>
)}
<MoreInfo open={open} handleClose={() => setOpen(false)}>
{info && info !== emptyContent ? (
<MoreInfoSection title="Why does it matter?">
<ReactMarkdownOrHtml source={info} openLinksOnNewTab />
</MoreInfoSection>
) : undefined}
{policyRef && policyRef !== emptyContent ? (
<MoreInfoSection title="Source">
<ReactMarkdownOrHtml source={policyRef} openLinksOnNewTab />
</MoreInfoSection>
) : undefined}
{howMeasured && howMeasured !== emptyContent ? (
<MoreInfoSection title="How is it defined?">
<>
{definitionImg && (
<Image
src={definitionImg}
alt=""
aria-describedby="howMeasured"
/>
)}
<ReactMarkdownOrHtml
source={howMeasured}
openLinksOnNewTab
id="howMeasured"
/>
</>
</MoreInfoSection>
) : undefined}
</MoreInfo>
</Card>
);
};
Expand Down
Loading