diff --git a/services/ui-src/src/components/accordions/AccordionItem.test.tsx b/services/ui-src/src/components/accordions/AccordionItem.test.tsx new file mode 100644 index 00000000..f947bc4d --- /dev/null +++ b/services/ui-src/src/components/accordions/AccordionItem.test.tsx @@ -0,0 +1,31 @@ +import { render, screen } from "@testing-library/react"; +import { axe } from "jest-axe"; +import { RouterWrappedComponent } from "utils/testing/setupJest"; +import { Accordion } from "@chakra-ui/react"; +import { AccordionItem } from "components"; + +const accordionItemComponent = ( + + + + + +); + +describe("Test AccordionItem", () => { + beforeEach(() => { + render(accordionItemComponent); + }); + + test("AccordionItem is visible", () => { + expect(screen.getByTestId("accordion-item")).toBeVisible(); + }); +}); + +describe("Test AccordionItem accessibility", () => { + it("Should not have basic accessibility issues", async () => { + const { container } = render(accordionItemComponent); + const results = await axe(container); + expect(results).toHaveNoViolations(); + }); +}); diff --git a/services/ui-src/src/components/accordions/AccordionItem.tsx b/services/ui-src/src/components/accordions/AccordionItem.tsx new file mode 100644 index 00000000..84839083 --- /dev/null +++ b/services/ui-src/src/components/accordions/AccordionItem.tsx @@ -0,0 +1,59 @@ +import { ReactChild } from "react"; +import { + AccordionButton, + AccordionItem as AccordionItemRoot, + AccordionPanel, + Image, + Text, +} from "@chakra-ui/react"; +import plusIcon from "assets/icons/icon_plus.png"; +import minusIcon from "assets/icons/icon_minus.png"; + +export const AccordionItem = ({ label, children, ...props }: Props) => { + return ( + + {({ isExpanded }) => ( + <> + + {label} + {isExpanded + + {children} + + )} + + ); +}; + +interface Props { + children?: ReactChild | ReactChild[]; + [key: string]: any; +} + +const sx = { + root: { + borderStyle: "none", + }, + accordionButton: { + minHeight: "3.5rem", + bg: "palette.gray_lightest", + textAlign: "left", + }, + accordionPanel: { + padding: "1.5rem 1rem 0.5rem", + ".mobile &": { + padding: "0.5rem 0", + }, + }, + accordionIcon: { + width: "1rem", + }, +}; diff --git a/services/ui-src/src/components/accordions/FaqAccordion.test.tsx b/services/ui-src/src/components/accordions/FaqAccordion.test.tsx new file mode 100644 index 00000000..7ac51dff --- /dev/null +++ b/services/ui-src/src/components/accordions/FaqAccordion.test.tsx @@ -0,0 +1,50 @@ +import { render, screen } from "@testing-library/react"; +import { axe } from "jest-axe"; +import userEvent from "@testing-library/user-event"; +import { RouterWrappedComponent } from "utils/testing/setupJest"; +import { FaqAccordion } from "components"; + +const accordionItems = [ + { + question: "Question?", + answer: "Answer!", + }, +]; + +const faqAccordionComponent = ( + + + +); + +describe("Test FaqAccordion", () => { + beforeEach(() => { + render(faqAccordionComponent); + }); + + test("FaqAccordion is visible", () => { + expect(screen.getByText(accordionItems[0].question)).toBeVisible(); + }); + + test("FaqAccordion default closed state only shows the question", () => { + expect(screen.getByText(accordionItems[0].question)).toBeVisible(); + expect(screen.getByText(accordionItems[0].answer)).not.toBeVisible(); + }); + + test("FaqAccordion should show answer on click", async () => { + const faqQuestion = screen.getByText(accordionItems[0].question); + expect(faqQuestion).toBeVisible(); + expect(screen.getByText(accordionItems[0].answer)).not.toBeVisible(); + await userEvent.click(faqQuestion); + expect(faqQuestion).toBeVisible(); + expect(screen.getByText(accordionItems[0].answer)).toBeVisible(); + }); +}); + +describe("Test FaqAccordion accessibility", () => { + it("Should not have basic accessibility issues", async () => { + const { container } = render(faqAccordionComponent); + const results = await axe(container); + expect(results).toHaveNoViolations(); + }); +}); diff --git a/services/ui-src/src/components/accordions/FaqAccordion.tsx b/services/ui-src/src/components/accordions/FaqAccordion.tsx new file mode 100644 index 00000000..1c152824 --- /dev/null +++ b/services/ui-src/src/components/accordions/FaqAccordion.tsx @@ -0,0 +1,33 @@ +import { Accordion, Box, Text } from "@chakra-ui/react"; +import { AccordionItem } from "components"; +import { AnyObject } from "types"; + +export const FaqAccordion = ({ accordionItems, ...props }: Props) => { + return ( + + {accordionItems.map((item: AnyObject, index: number) => ( + + + {item.answer} + + + ))} + + ); +}; + +interface Props { + accordionItems: AnyObject; +} + +const sx = { + item: { + marginBottom: "1.5rem", + borderStyle: "none", + }, + answerBox: { + ".mobile &": { + paddingLeft: "1rem", + }, + }, +}; diff --git a/services/ui-src/src/components/app/App.tsx b/services/ui-src/src/components/app/App.tsx index 8ddbcbb5..88f399ac 100644 --- a/services/ui-src/src/components/app/App.tsx +++ b/services/ui-src/src/components/app/App.tsx @@ -1,13 +1,17 @@ import { useContext } from "react"; import { Routes, Route } from "react-router-dom"; -import { Container, Divider, Flex, Heading, Stack } from "@chakra-ui/react"; import { + AppRoutes, + Error, Header, LoginCognito, LoginIDM, PostLogoutRedirect, Footer, + Timeout, } from "components"; +import { Container, Divider, Flex, Heading, Stack } from "@chakra-ui/react"; +import { ErrorBoundary } from "react-error-boundary"; import { makeMediaQueryClasses, UserContext, useStore } from "utils"; export const App = () => { @@ -32,8 +36,14 @@ export const App = () => { const authenticatedRoutes = ( <> {user && ( - + +
+ + + + +