Skip to content

Commit

Permalink
changed focus from main element to h1
Browse files Browse the repository at this point in the history
  • Loading branch information
rawanBairouti committed Aug 27, 2023
1 parent cff39a0 commit 031298b
Show file tree
Hide file tree
Showing 7 changed files with 233 additions and 135 deletions.
55 changes: 35 additions & 20 deletions src/components/MainHeading.test.tsx
Original file line number Diff line number Diff line change
@@ -1,30 +1,45 @@
import { MainHeading } from "./MainHeading";
import { render, screen } from "@testing-library/react";
import React from "react";
import { MainHeading } from './MainHeading';
import { render, screen } from '@testing-library/react';
import React from 'react';
import { MAIN_HEADING_ID } from './MainHeading';

describe(MainHeading.name, () => {
const headingId = "1234";
const headingText = "Hello";
const ariaLabelText = "HeLlOooo";
const headingText = 'Hello';
const ariaLabelText = 'HeLlOooo';

test("renders the children in an h1", () => {
test('renders the children in an h1', () => {
render(<MainHeading>{headingText}</MainHeading>);
expect(screen.getByRole("heading", { level: 1, name: headingText })).toBeVisible();
expect(
screen.getByRole('heading', { level: 1, name: headingText })
).toBeVisible();
});
test("gives the h1 a tabindex of -1", () => {
test('gives the h1 a tabindex of -1', () => {
render(<MainHeading>{headingText}</MainHeading>);
const heading = screen.getByRole("heading", { level: 1, name: headingText });
expect(heading.hasAttribute("tabindex")).toBe(true);
const heading = screen.getByRole('heading', {
level: 1,
name: headingText,
});
expect(heading.hasAttribute('tabindex')).toBe(true);
expect(heading.tabIndex).toBe(-1);
});
test("passes the optional ID to the h1 element", () => {
render(<MainHeading id={headingId}>{headingText}</MainHeading>);
const heading = screen.getByRole("heading", { level: 1, name: headingText });
expect(heading.id).toBe(headingId);
});
test("passes the optional aria-label to the h1 element", () => {
render(<MainHeading id={headingId} ariaLabel={ariaLabelText}>{headingText}</MainHeading>);
const heading = screen.getByRole("heading", { level: 1, name: ariaLabelText });
test('passes the optional aria-label to the h1 element', () => {
render(
<MainHeading ariaLabel={ariaLabelText}>{headingText}</MainHeading>
);
const heading = screen.getByRole('heading', {
level: 1,
name: ariaLabelText,
});
expect(heading.textContent).toBe(headingText);
});
});
test('assigns constant id to the h1', () => {
render(<MainHeading>{headingText}</MainHeading>);
const h1 = screen.getByRole('heading', { level: 1 });
expect(h1.id).toBe(MAIN_HEADING_ID);
});
test('main heading id is main-heading', () => {
render(<MainHeading>{headingText}</MainHeading>);
const h1 = screen.getByRole('heading', { level: 1 });
expect(h1.id).toBe('main-heading');
});
});
24 changes: 15 additions & 9 deletions src/components/MainHeading.tsx
Original file line number Diff line number Diff line change
@@ -1,16 +1,22 @@
import { ChildrenProps } from "../utilities/children-props";
import React from "react";
import "./MainHeading.css";
import { ChildrenProps } from '../utilities/children-props';
import React from 'react';
import './MainHeading.css';

type MainHeadingProps = {
id?: string;
ariaLabel?: string;
}
};

export const MainHeading = ({ id, ariaLabel, children }: MainHeadingProps & ChildrenProps) => {
export const MAIN_HEADING_ID = 'main-heading';

export const MainHeading = ({
ariaLabel,
children,
}: MainHeadingProps & ChildrenProps) => {
return (
<div className={"main-heading-container"}>
<h1 id={id} aria-label={ariaLabel} tabIndex={-1}>{children}</h1>
<div className={'main-heading-container'}>
<h1 id={MAIN_HEADING_ID} aria-label={ariaLabel} tabIndex={-1}>
{children}
</h1>
</div>
);
};
};
7 changes: 3 additions & 4 deletions src/layout/Layout.test.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ import { MemoryRouter } from "react-router-dom";
import userEvent from "@testing-library/user-event";
import { UserEvent } from "@testing-library/user-event/dist/types/setup/setup";
import { DocumentStateContext } from "../contexts/DocumentStateContext";
import { MAIN_HEADING_ID } from "../components/MainHeading";

describe(Layout.name, () => {
let user: UserEvent;
Expand All @@ -18,11 +19,9 @@ describe(Layout.name, () => {
});
});
test("should have a link to skip to the main landmark", () => {
const skipNav: HTMLLinkElement = screen.getByRole("link", { name: "Skip to content" });
const main = screen.getByRole("main");
const skipNav: HTMLLinkElement = screen.getByRole("link", { name: "Skip to content" });

expect(skipNav.href).toContain("#main-content");
expect(main.id).toEqual("main-content");
expect(skipNav.href).toContain(`#${MAIN_HEADING_ID}`);
});
test("skip-nav link should be the first focusable element", async () => {
const skipNav = screen.getByRole("link", { name: "Skip to content" });
Expand Down
13 changes: 8 additions & 5 deletions src/layout/Layout.tsx
Original file line number Diff line number Diff line change
@@ -1,17 +1,20 @@
import React from "react";
import { RouterOutlet } from "./RouterOutlet";
import { HeaderBar } from "../components/HeaderBar";
import { MAIN_HEADING_ID } from "../components/MainHeading";
import "./Layout.css";

export const Layout = () => {
return (
<div className={"layout"} data-testid={"app-layout"}>
<a href={"#main-content"} className={"skip-nav"}>Skip to content</a>
<div className={'layout'} data-testid={'app-layout'}>
<a href={`#${MAIN_HEADING_ID}`} className={'skip-nav'}>
Skip to content
</a>
<header>
<HeaderBar/>
<HeaderBar />
</header>
<main id={"main-content"} tabIndex={-1} className={"main-content"}>
<RouterOutlet/>
<main id={'main-content'} tabIndex={-1} className={'main-content'}>
<RouterOutlet />
</main>
</div>
);
Expand Down
86 changes: 57 additions & 29 deletions src/pages/accessibility-issues/AccessibilityIssues.tsx
Original file line number Diff line number Diff line change
@@ -1,40 +1,68 @@
import React, { useState } from "react";
import "../Pages.css";
import { Form } from "../../components/form/Form";
import { SERVER_ENDPOINTS } from "../../utilities/server-endpoints";
import { FormField, FormFieldType } from "../../components/form/FormField";
import { validateEmail } from "../../hooks/form-validation/validateEmail";
import { validatePhone } from "../../hooks/form-validation/validatePhone";
import { validateRequired } from "../../hooks/form-validation/validateRequired";
import { MainHeading } from "../../components/MainHeading";
import React, { useState } from 'react';
import '../Pages.css';
import { Form } from '../../components/form/Form';
import { SERVER_ENDPOINTS } from '../../utilities/server-endpoints';
import { FormField, FormFieldType } from '../../components/form/FormField';
import { validateEmail } from '../../hooks/form-validation/validateEmail';
import { validatePhone } from '../../hooks/form-validation/validatePhone';
import { validateRequired } from '../../hooks/form-validation/validateRequired';
import { MainHeading } from '../../components/MainHeading';
import { MAIN_HEADING_ID } from '../../components/MainHeading';

export type AccessibilityFormData = {
name?: string
email?: string
phone?: string
description: string
}
name?: string;
email?: string;
phone?: string;
description: string;
};

export const ACCESSIBILITY_PAGE_HEADING = "Report Accessibility Issues";
export const ACCESSIBILITY_PAGE_HEADING = 'Report Accessibility Issues';

export const AccessibilityIssues = () => {
const [successMessage] = useState("Your issue has been reported, thank you!");
const [successMessage] = useState(
'Your issue has been reported, thank you!'
);
return (
<>
<MainHeading id={"form-label"}>{ACCESSIBILITY_PAGE_HEADING}</MainHeading>
<MainHeading>{ACCESSIBILITY_PAGE_HEADING}</MainHeading>

<Form successMessage={successMessage} ariaLabelledBy={"form-label"}
submitEndpoint={SERVER_ENDPOINTS.ACCESSIBILITY_ISSUES}>
<label htmlFor={"name"} className={"form-label"}>Your Name (optional)</label>
<FormField id={"name"} autoComplete={"name"} name={"name"}/>
<label htmlFor={"email"} className={"form-label"}>Your email (optional)</label>
<FormField id={"email"} name={"email"} validator={validateEmail} autoComplete={"email"}/>
<label htmlFor={"phone"} className={"form-label"}>Your phone number (optional)</label>
<FormField id={"phone"} name={"phone"} validator={validatePhone} autoComplete={"tel"}/>
<label htmlFor={"description"} className={"form-label"}>What do you want to tell us? (required)</label>
<FormField id={"description"} name={"description"} validator={validateRequired}
inputType={FormFieldType.TEXTAREA}/>
<Form
successMessage={successMessage}
ariaLabelledBy={MAIN_HEADING_ID}
submitEndpoint={SERVER_ENDPOINTS.ACCESSIBILITY_ISSUES}
>
<label htmlFor={'name'} className={'form-label'}>
Your Name (optional)
</label>
<FormField id={'name'} autoComplete={'name'} name={'name'} />
<label htmlFor={'email'} className={'form-label'}>
Your email (optional)
</label>
<FormField
id={'email'}
name={'email'}
validator={validateEmail}
autoComplete={'email'}
/>
<label htmlFor={'phone'} className={'form-label'}>
Your phone number (optional)
</label>
<FormField
id={'phone'}
name={'phone'}
validator={validatePhone}
autoComplete={'tel'}
/>
<label htmlFor={'description'} className={'form-label'}>
What do you want to tell us? (required)
</label>
<FormField
id={'description'}
name={'description'}
validator={validateRequired}
inputType={FormFieldType.TEXTAREA}
/>
</Form>
</>
);
};
};
Loading

0 comments on commit 031298b

Please sign in to comment.