From fbec5e1426bfa50c97613a37e46d4f3a265ae36a Mon Sep 17 00:00:00 2001 From: Chris Chudzicki Date: Mon, 26 Aug 2024 11:15:09 -0400 Subject: [PATCH 01/16] remove default headers from Typography, fix tests --- .../ResourceCarousel.test.tsx | 48 ++++++++++++------- .../src/pages/PrivacyPage/PrivacyPage.tsx | 22 ++++++--- .../src/pages/TermsPage/TermsPage.tsx | 22 ++++++--- .../UserListListingComponent.tsx | 7 ++- .../src/components/Banner/Banner.tsx | 1 + .../src/components/Dialog/Dialog.tsx | 8 +++- .../components/ThemeProvider/typography.ts | 5 ++ 7 files changed, 81 insertions(+), 32 deletions(-) diff --git a/frontends/mit-learn/src/page-components/ResourceCarousel/ResourceCarousel.test.tsx b/frontends/mit-learn/src/page-components/ResourceCarousel/ResourceCarousel.test.tsx index ed54fb90f0..856e329c48 100644 --- a/frontends/mit-learn/src/page-components/ResourceCarousel/ResourceCarousel.test.tsx +++ b/frontends/mit-learn/src/page-components/ResourceCarousel/ResourceCarousel.test.tsx @@ -172,22 +172,38 @@ describe("ResourceCarousel", () => { expect(urlParams.get("professional")).toEqual("true") }) - it("Shows the correct title", async () => { - const config: ResourceCarouselProps["config"] = [ - { - label: "Resources", - data: { - type: "resources", - params: { resource_type: ["course", "program"], professional: true }, + it.each([ + { headerComponent: "h1", expectedTag: "H1" }, + { headerComponent: "h3", expectedTag: "H3" }, + ] as const)( + "Shows the correct title with correct heading level", + async ({ headerComponent, expectedTag }) => { + const config: ResourceCarouselProps["config"] = [ + { + label: "Resources", + data: { + type: "resources", + params: { + resource_type: ["course", "program"], + professional: true, + }, + }, }, - }, - ] - setMockResponse.get(urls.userMe.get(), {}) - setupApis() - renderWithProviders( - , - ) + ] + setMockResponse.get(urls.userMe.get(), {}) + setupApis() + renderWithProviders( + , + ) - await screen.findByRole("heading", { name: "My Favorite Carousel" }) - }) + const title = await screen.findByRole("heading", { + name: "My Favorite Carousel", + }) + expect(title.tagName).toBe(expectedTag) + }, + ) }) diff --git a/frontends/mit-learn/src/pages/PrivacyPage/PrivacyPage.tsx b/frontends/mit-learn/src/pages/PrivacyPage/PrivacyPage.tsx index 8e71dd999e..bd07cb1561 100644 --- a/frontends/mit-learn/src/pages/PrivacyPage/PrivacyPage.tsx +++ b/frontends/mit-learn/src/pages/PrivacyPage/PrivacyPage.tsx @@ -1,4 +1,10 @@ -import { Breadcrumbs, Container, Typography, styled } from "ol-components" +import { + Breadcrumbs, + Container, + Typography, + TypographyProps, + styled, +} from "ol-components" import MetaTags from "@/page-components/MetaTags/MetaTags" import * as urls from "@/common/urls" import React from "react" @@ -29,10 +35,12 @@ const BannerContainerInner = styled.div({ justifyContent: "center", }) -const Header = styled(Typography)(({ theme }) => ({ - alignSelf: "stretch", - color: theme.custom.colors.black, -})) +const Header = styled(Typography)>( + ({ theme }) => ({ + alignSelf: "stretch", + color: theme.custom.colors.black, + }), +) const BodyContainer = styled.div({ display: "flex", @@ -66,7 +74,9 @@ const PrivacyPage: React.FC = () => { ancestors={[{ href: urls.HOME, label: "Home" }]} current="Privacy Policy" /> -
Privacy Policy
+
+ Privacy Policy +
diff --git a/frontends/mit-learn/src/pages/TermsPage/TermsPage.tsx b/frontends/mit-learn/src/pages/TermsPage/TermsPage.tsx index 0e3a75e1c0..0e4b50f78c 100644 --- a/frontends/mit-learn/src/pages/TermsPage/TermsPage.tsx +++ b/frontends/mit-learn/src/pages/TermsPage/TermsPage.tsx @@ -1,4 +1,10 @@ -import { Breadcrumbs, Container, Typography, styled } from "ol-components" +import { + Breadcrumbs, + Container, + Typography, + TypographyProps, + styled, +} from "ol-components" import MetaTags from "@/page-components/MetaTags/MetaTags" import * as urls from "@/common/urls" import React from "react" @@ -29,10 +35,12 @@ const BannerContainerInner = styled.div({ justifyContent: "center", }) -const Header = styled(Typography)(({ theme }) => ({ - alignSelf: "stretch", - color: theme.custom.colors.black, -})) +const Header = styled(Typography)>( + ({ theme }) => ({ + alignSelf: "stretch", + color: theme.custom.colors.black, + }), +) const BodyContainer = styled.div({ display: "flex", @@ -67,7 +75,9 @@ const TermsPage: React.FC = () => { ancestors={[{ href: urls.HOME, label: "Home" }]} current="Terms of Service" /> -
Terms of Service
+
+ Terms of Service +
diff --git a/frontends/mit-learn/src/pages/UserListListingComponent/UserListListingComponent.tsx b/frontends/mit-learn/src/pages/UserListListingComponent/UserListListingComponent.tsx index 93733666d2..6ed4f8c84f 100644 --- a/frontends/mit-learn/src/pages/UserListListingComponent/UserListListingComponent.tsx +++ b/frontends/mit-learn/src/pages/UserListListingComponent/UserListListingComponent.tsx @@ -7,6 +7,7 @@ import { PlainList, Card, theme, + TypographyProps, } from "ol-components" import { RiListCheck3 } from "@remixicon/react" import { useUserListList } from "api/hooks/learningResources" @@ -15,7 +16,7 @@ import { manageListDialogs } from "@/page-components/ManageListDialogs/ManageLis import { userListView } from "@/common/urls" import UserListCardCondensed from "@/page-components/UserListCard/UserListCardCondensed" -const Header = styled(Typography)({ +const Header = styled(Typography)>({ marginBottom: "16px", }) @@ -71,7 +72,9 @@ const UserListListingComponent: React.FC = ( return ( -
{title}
+
+ {title} +
{!data?.results.length && !isLoading ? ( diff --git a/frontends/ol-components/src/components/Banner/Banner.tsx b/frontends/ol-components/src/components/Banner/Banner.tsx index 8babefdad7..2c1b209766 100644 --- a/frontends/ol-components/src/components/Banner/Banner.tsx +++ b/frontends/ol-components/src/components/Banner/Banner.tsx @@ -114,6 +114,7 @@ const Banner = ({ {avatar ?
{avatar}
: null} = ({ disableEnforceFocus, }) => { const [confirming, setConfirming] = useState(isSubmitting) + const titleId = useId() const handleConfirm = useCallback(async () => { try { @@ -115,6 +116,7 @@ const Dialog: React.FC = ({ disableEnforceFocus={disableEnforceFocus} PaperProps={PaperProps} TransitionComponent={Transition} + aria-labelledby={titleId} > = ({ {title && (
- {title} + + {title} +
)} diff --git a/frontends/ol-components/src/components/ThemeProvider/typography.ts b/frontends/ol-components/src/components/ThemeProvider/typography.ts index 22c6f575d0..c358c86857 100644 --- a/frontends/ol-components/src/components/ThemeProvider/typography.ts +++ b/frontends/ol-components/src/components/ThemeProvider/typography.ts @@ -147,6 +147,11 @@ const globalSettings: ThemeOptions["typography"] = { const component: NonNullable["MuiTypography"] = { defaultProps: { variantMapping: { + h1: "span", + h2: "span", + h3: "span", + h4: "span", + h5: "span", body1: "p", body2: "p", body3: "p", From 3d196db72ae88cea2c32605d366f8f1423ce2b90 Mon Sep 17 00:00:00 2001 From: Chris Chudzicki Date: Mon, 26 Aug 2024 11:32:44 -0400 Subject: [PATCH 02/16] add VisuallyHidden component --- .../VisuallyHidden/VisuallyHidden.tsx | 30 +++++++++++++++++++ frontends/ol-components/src/index.ts | 1 + 2 files changed, 31 insertions(+) create mode 100644 frontends/ol-components/src/components/VisuallyHidden/VisuallyHidden.tsx diff --git a/frontends/ol-components/src/components/VisuallyHidden/VisuallyHidden.tsx b/frontends/ol-components/src/components/VisuallyHidden/VisuallyHidden.tsx new file mode 100644 index 0000000000..c23456ea7f --- /dev/null +++ b/frontends/ol-components/src/components/VisuallyHidden/VisuallyHidden.tsx @@ -0,0 +1,30 @@ +import styled from "@emotion/styled" + +/** + * VisuallyHidden is a utility component that hides its children from sighted + * users, but keeps them accessible to screen readers. + * + * Often, screenreader-only content can be handled with an `aria-label`. However, + * occasionally we need actual elements. + * + * Example: + * - a visually hidden Heading for a section whose purpose is clear for sighted users + * - a visually hidden description used for aria-describeddby + * - There is an aria-descriptionby attribute that can be used to provide a + * without an actual element on the page. However, it is introduced in + * ARIA 1.3 (working draft), not compatible with some screen readers, and + * flagged problematic by our linting. + * + * The CSS here is based on https://inclusive-components.design/tooltips-toggletips/ + */ +const VisuallyHidden = styled.span({ + clipPath: "inset(100%)", + clip: "rect(1px, 1px, 1px, 1px)", + height: "1px", + overflow: "hidden", + position: "absolute", + whiteSpace: "nowrap", + width: "1px", +}) + +export { VisuallyHidden } diff --git a/frontends/ol-components/src/index.ts b/frontends/ol-components/src/index.ts index 7ee530994a..c8416defa9 100644 --- a/frontends/ol-components/src/index.ts +++ b/frontends/ol-components/src/index.ts @@ -193,6 +193,7 @@ export * from "./components/ThemeProvider/ThemeProvider" export * from "./components/TruncateText/TruncateText" export * from "./components/Radio/Radio" export * from "./components/RadioChoiceField/RadioChoiceField" +export * from "./components/VisuallyHidden/VisuallyHidden" export * from "./constants/imgConfigs" From 7ab9124497860fe8849933ca2262fcdd9a814f50 Mon Sep 17 00:00:00 2001 From: Chris Chudzicki Date: Mon, 26 Aug 2024 11:37:16 -0400 Subject: [PATCH 03/16] put all page content in "main" --- frontends/mit-learn/src/routes.tsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/frontends/mit-learn/src/routes.tsx b/frontends/mit-learn/src/routes.tsx index f1328693b3..fb5b338066 100644 --- a/frontends/mit-learn/src/routes.tsx +++ b/frontends/mit-learn/src/routes.tsx @@ -35,7 +35,7 @@ const PageWrapper = styled.div({ flexDirection: "column", }) -const PageWrapperInner = styled.div({ +const PageWrapperInner = styled.main({ flex: "1", }) From 0e9dc07a6e51fdbcbccc923a2f778d3adb6d3b85 Mon Sep 17 00:00:00 2001 From: Chris Chudzicki Date: Mon, 26 Aug 2024 12:29:55 -0400 Subject: [PATCH 04/16] fix homepage headers --- .../ResourceCarousel/ResourceCarousel.tsx | 33 +++++++++++-------- .../pages/HomePage/BrowseTopicsSection.tsx | 15 +++++++-- .../src/pages/HomePage/HeroSearch.tsx | 1 + .../mit-learn/src/pages/HomePage/HomePage.tsx | 7 +++- .../src/pages/HomePage/NewsEventsSection.tsx | 23 +++++++++---- .../pages/HomePage/TestimonialsSection.tsx | 4 ++- 6 files changed, 58 insertions(+), 25 deletions(-) diff --git a/frontends/mit-learn/src/page-components/ResourceCarousel/ResourceCarousel.tsx b/frontends/mit-learn/src/page-components/ResourceCarousel/ResourceCarousel.tsx index 86e6022f20..2a9ec40e19 100644 --- a/frontends/mit-learn/src/page-components/ResourceCarousel/ResourceCarousel.tsx +++ b/frontends/mit-learn/src/page-components/ResourceCarousel/ResourceCarousel.tsx @@ -8,6 +8,7 @@ import { TabButtonList, styled, Typography, + TypographyProps, } from "ol-components" import type { TabConfig } from "./types" import { LearningResource, PaginatedLearningResourceList } from "api" @@ -49,13 +50,15 @@ const HeaderRow = styled.div(({ theme }) => ({ }, })) -const HeaderText = styled(Typography)(({ theme }) => ({ - paddingRight: "16px", - [theme.breakpoints.down("sm")]: { - paddingBottom: "16px", - ...theme.typography.h5, - }, -})) +const HeaderText = styled(Typography)>( + ({ theme }) => ({ + paddingRight: "16px", + [theme.breakpoints.down("sm")]: { + paddingBottom: "16px", + ...theme.typography.h5, + }, + }), +) const ControlsContainer = styled.div(({ theme }) => ({ display: "flex", @@ -152,6 +155,10 @@ type ResourceCarouselProps = { className?: string isLoading?: boolean "data-testid"?: string + /** + * Element type for the carousel title + */ + headerComponent?: React.ElementType } /** * A tabbed carousel that fetches resources based on the configuration provided. @@ -170,6 +177,7 @@ const ResourceCarousel: React.FC = ({ className, isLoading, "data-testid": dataTestId, + headerComponent, }) => { const [tab, setTab] = React.useState("0") const [ref, setRef] = React.useState(null) @@ -210,15 +218,12 @@ const ResourceCarousel: React.FC = ({ ) return ( - + - {title} + + {title} + {config.length === 1 ? buttonsContainerElement : null} {config.length > 1 ? ( diff --git a/frontends/mit-learn/src/pages/HomePage/BrowseTopicsSection.tsx b/frontends/mit-learn/src/pages/HomePage/BrowseTopicsSection.tsx index 384672465f..bad7557396 100644 --- a/frontends/mit-learn/src/pages/HomePage/BrowseTopicsSection.tsx +++ b/frontends/mit-learn/src/pages/HomePage/BrowseTopicsSection.tsx @@ -1,5 +1,12 @@ import React from "react" -import { Container, styled, theme, Typography, ButtonLink } from "ol-components" +import { + Container, + styled, + theme, + Typography, + ButtonLink, + TypographyProps, +} from "ol-components" import { Link } from "react-router-dom" import { useLearningResourceTopics } from "api/hooks/learningResources" import { RiArrowRightLine } from "@remixicon/react" @@ -18,7 +25,7 @@ const Section = styled.section` } ` -const Title = styled(Typography)` +const Title = styled(Typography)>` text-align: center; ` @@ -102,7 +109,9 @@ const BrowseTopicsSection: React.FC = () => { return (
- Browse by Topic + + Browse by Topic + {topics?.results.map( ({ id, name, channel_url: channelUrl, icon }) => { diff --git a/frontends/mit-learn/src/pages/HomePage/HeroSearch.tsx b/frontends/mit-learn/src/pages/HomePage/HeroSearch.tsx index fe8314cc41..08827bb975 100644 --- a/frontends/mit-learn/src/pages/HomePage/HeroSearch.tsx +++ b/frontends/mit-learn/src/pages/HomePage/HeroSearch.tsx @@ -185,6 +185,7 @@ const HeroSearch: React.FC = () => { diff --git a/frontends/mit-learn/src/pages/HomePage/HomePage.tsx b/frontends/mit-learn/src/pages/HomePage/HomePage.tsx index ba9ec13d23..02dad47f8a 100644 --- a/frontends/mit-learn/src/pages/HomePage/HomePage.tsx +++ b/frontends/mit-learn/src/pages/HomePage/HomePage.tsx @@ -44,6 +44,7 @@ const HomePage: React.FC = () => { @@ -51,7 +52,11 @@ const HomePage: React.FC = () => { - + diff --git a/frontends/mit-learn/src/pages/HomePage/NewsEventsSection.tsx b/frontends/mit-learn/src/pages/HomePage/NewsEventsSection.tsx index cf7ae54175..125ae6aec9 100644 --- a/frontends/mit-learn/src/pages/HomePage/NewsEventsSection.tsx +++ b/frontends/mit-learn/src/pages/HomePage/NewsEventsSection.tsx @@ -7,6 +7,7 @@ import { Grid, useMuiBreakpointAtLeast, Card, + TypographyProps, } from "ol-components" import { useNewsEventsList, @@ -24,7 +25,7 @@ const Section = styled.section` } ` -const Title = styled(Typography)` +const Title = styled(Typography)>` text-align: center; margin-bottom: 8px; ` @@ -231,7 +232,9 @@ const NewsEventsSection: React.FC = () => { return (
- MIT Stories & Events + + MIT Stories & Events + See what's happening in the world of learning with the latest news, insights, and upcoming events at MIT. @@ -239,7 +242,9 @@ const NewsEventsSection: React.FC = () => { {isMobile ? ( - Stories + + Stories + {stories.map((item) => ( { - Events + + Events + {EventCards} @@ -259,7 +266,9 @@ const NewsEventsSection: React.FC = () => { - Stories + + Stories + {stories.map((item) => ( @@ -269,7 +278,9 @@ const NewsEventsSection: React.FC = () => { - Events + + Events + {EventCards} diff --git a/frontends/mit-learn/src/pages/HomePage/TestimonialsSection.tsx b/frontends/mit-learn/src/pages/HomePage/TestimonialsSection.tsx index 3606d50314..f9b58f5d90 100644 --- a/frontends/mit-learn/src/pages/HomePage/TestimonialsSection.tsx +++ b/frontends/mit-learn/src/pages/HomePage/TestimonialsSection.tsx @@ -297,7 +297,9 @@ const TestimonialsSection: React.FC = () => { return (
- From Our Community + + From Our Community + Millions of learners are reaching their goals with MIT's non-degree learning resources. Here's what they're saying. From c5848cda4b33123ebf82bbcf3ba43a0ba77649d8 Mon Sep 17 00:00:00 2001 From: Chris Chudzicki Date: Tue, 27 Aug 2024 13:37:06 -0400 Subject: [PATCH 05/16] remove heading element from cards --- .../ItemsListing/ItemsListing.test.tsx | 32 +++++++++++-------- .../ResourceCard/ResourceCard.test.tsx | 4 +-- .../LearningPathListingPage.test.tsx | 17 +++++----- .../src/components/Card/Card.tsx | 2 +- .../src/components/Card/ListCard.tsx | 2 +- .../LearningResourceCard.test.tsx | 21 +++++------- .../LearningResourceListCard.test.tsx | 18 ++++------- 7 files changed, 45 insertions(+), 51 deletions(-) diff --git a/frontends/mit-learn/src/page-components/ItemsListing/ItemsListing.test.tsx b/frontends/mit-learn/src/page-components/ItemsListing/ItemsListing.test.tsx index e791c0d4a7..10b770818b 100644 --- a/frontends/mit-learn/src/page-components/ItemsListing/ItemsListing.test.tsx +++ b/frontends/mit-learn/src/page-components/ItemsListing/ItemsListing.test.tsx @@ -175,11 +175,14 @@ describe("ItemsListing", () => { />, { user: {} }, ) + const titles = items.map((item) => item.resource.title) - const headings = screen.getAllByRole("heading", { - name: (value) => titles.includes(value), + + const role = sortable ? "button" : "link" + const cards = screen.getAllByRole(role, { + name: (value) => titles.some((title) => value.includes(title)), }) - expect(headings.map((h) => h.textContent)).toEqual(titles) + expect(cards.length).toBe(titles.length) if (sortable) { items.forEach(({ resource }) => { @@ -320,23 +323,24 @@ describe.each([ListType.LearningPath, ListType.UserList])( const patchResponse = new ControlledPromise() setMockResponse.patch(patchUrl(listType, active.id), patchResponse) - const titleEls1 = screen.getAllByRole("heading", { - name: (value) => titles.includes(value), + // dnd-kit draggables have role button (+ a roledescription) + const cards1 = screen.getAllByRole("button", { + name: (value) => titles.some((title) => value.includes(title)), }) - expect(titleEls1.map((el) => el.textContent)).toEqual(titles) + expect(cards1.length).toBe(titles.length) act(() => simulateDrag(from, to)) await waitFor(() => { - const titleEls2 = screen.getAllByRole("heading", { - name: (value) => titles.includes(value), + const cards2 = screen.getAllByRole("button", { + name: (value) => titles.some((title) => value.includes(title)), }) - expect(titleEls2).toEqual([ - titleEls1[0], - titleEls1[2], - titleEls1[3], - titleEls1[1], - titleEls1[4], + expect(cards2).toEqual([ + cards1[0], + cards1[2], + cards1[3], + cards1[1], + cards1[4], ]) }) }) diff --git a/frontends/mit-learn/src/page-components/ResourceCard/ResourceCard.test.tsx b/frontends/mit-learn/src/page-components/ResourceCard/ResourceCard.test.tsx index eab4c6980c..51c1bde37a 100644 --- a/frontends/mit-learn/src/page-components/ResourceCard/ResourceCard.test.tsx +++ b/frontends/mit-learn/src/page-components/ResourceCard/ResourceCard.test.tsx @@ -186,8 +186,8 @@ describe.each([ user: { is_learning_path_editor: true }, }) invariant(resource) - const cardTitle = screen.getByRole("heading", { name: resource.title }) - await user.click(cardTitle) + const link = screen.getByRole("link", { name: new RegExp(resource.title) }) + await user.click(link) expect( new URLSearchParams(location.current.search).get( RESOURCE_DRAWER_QUERY_PARAM, diff --git a/frontends/mit-learn/src/pages/LearningPathListingPage/LearningPathListingPage.test.tsx b/frontends/mit-learn/src/pages/LearningPathListingPage/LearningPathListingPage.test.tsx index cf0a72994d..6ce6a169da 100644 --- a/frontends/mit-learn/src/pages/LearningPathListingPage/LearningPathListingPage.test.tsx +++ b/frontends/mit-learn/src/pages/LearningPathListingPage/LearningPathListingPage.test.tsx @@ -45,13 +45,10 @@ describe("LearningPathListingPage", () => { it("Renders a card for each learning path", async () => { const { paths } = setup() const titles = paths.results.map((resource) => resource.title) - const headings = await screen.findAllByRole("heading", { - name: (value) => titles.includes(value), + const cards = await screen.findAllByRole("link", { + name: (name) => titles.some((title) => name.includes(title)), }) - - // for sanity - expect(headings.length).toBeGreaterThan(0) - expect(titles.length).toBe(headings.length) + expect(cards.length).toBeGreaterThan(0) // sanity }) it.each([ @@ -67,8 +64,8 @@ describe("LearningPathListingPage", () => { // Ensure the lists have loaded const path = paths.results[0] - await screen.findAllByRole("heading", { - name: path.title, + await screen.findAllByRole("link", { + name: new RegExp(path.title), }) const menuButton = screen.queryByRole("button", { name: `Edit list ${path.title}`, @@ -136,7 +133,9 @@ describe("LearningPathListingPage", () => { test("Clicking on list title navigates to list page", async () => { const { location, paths } = setup() const path = faker.helpers.arrayElement(paths.results) - const listTitle = await screen.findByRole("heading", { name: path.title }) + const listTitle = await screen.findByRole("link", { + name: new RegExp(path.title), + }) await user.click(listTitle) expect(location.current).toEqual( expect.objectContaining({ diff --git a/frontends/ol-components/src/components/Card/Card.tsx b/frontends/ol-components/src/components/Card/Card.tsx index 2147165ec8..19b9fc0461 100644 --- a/frontends/ol-components/src/components/Card/Card.tsx +++ b/frontends/ol-components/src/components/Card/Card.tsx @@ -83,7 +83,7 @@ const Info = styled.div<{ size?: Size }>` margin-bottom: ${({ size }) => (size === "small" ? 4 : 8)}px; ` -const Title = styled.h3<{ lines?: number; size?: Size }>` +const Title = styled.span<{ lines?: number; size?: Size }>` text-overflow: ellipsis; height: ${({ lines, size }) => { const lineHeightPx = size === "small" ? 18 : 20 diff --git a/frontends/ol-components/src/components/Card/ListCard.tsx b/frontends/ol-components/src/components/Card/ListCard.tsx index b32c073690..6307acb230 100644 --- a/frontends/ol-components/src/components/Card/ListCard.tsx +++ b/frontends/ol-components/src/components/Card/ListCard.tsx @@ -107,7 +107,7 @@ export const Info = styled.div` align-items: center; ` -export const Title = styled.h3` +export const Title = styled.span` flex-grow: 1; color: ${theme.custom.colors.darkGray2}; text-overflow: ellipsis; diff --git a/frontends/ol-components/src/components/LearningResourceCard/LearningResourceCard.test.tsx b/frontends/ol-components/src/components/LearningResourceCard/LearningResourceCard.test.tsx index 8660f1807a..a059281b20 100644 --- a/frontends/ol-components/src/components/LearningResourceCard/LearningResourceCard.test.tsx +++ b/frontends/ol-components/src/components/LearningResourceCard/LearningResourceCard.test.tsx @@ -1,6 +1,6 @@ import React from "react" import { BrowserRouter } from "react-router-dom" -import { screen, render, act } from "@testing-library/react" +import { screen, render } from "@testing-library/react" import { LearningResourceCard } from "./LearningResourceCard" import type { LearningResourceCardProps } from "./LearningResourceCard" import { DEFAULT_RESOURCE_IMG, embedlyCroppedImage } from "ol-utilities" @@ -11,10 +11,7 @@ import { ThemeProvider } from "../ThemeProvider/ThemeProvider" const setup = (props: LearningResourceCardProps) => { return render( - + , { wrapper: ThemeProvider }, ) @@ -30,7 +27,7 @@ describe("Learning Resource Card", () => { setup({ resource }) screen.getByText("Course") - screen.getByRole("heading", { name: resource.title }) + screen.getByText(resource.title) screen.getByText("Starts:") screen.getByText("January 01, 2026") }) @@ -94,20 +91,18 @@ describe("Learning Resource Card", () => { }, ) - test("Click to navigate", async () => { + test("Links to specified href", async () => { const resource = factories.learningResources.resource({ resource_type: ResourceTypeEnum.Course, platform: { code: PlatformEnum.Ocw }, }) - setup({ resource }) + setup({ resource, href: "/path/to/thing" }) - const heading = screen.getByRole("heading", { name: resource.title }) - await act(async () => { - await heading.click() + const link = screen.getByRole("link", { + name: new RegExp(resource.title), }) - - expect(window.location.search).toBe(`?resource=${resource.id}`) + expect(new URL(link.href).pathname).toBe("/path/to/thing") }) test("Click action buttons", async () => { diff --git a/frontends/ol-components/src/components/LearningResourceCard/LearningResourceListCard.test.tsx b/frontends/ol-components/src/components/LearningResourceCard/LearningResourceListCard.test.tsx index df930f2f53..9189cb40d1 100644 --- a/frontends/ol-components/src/components/LearningResourceCard/LearningResourceListCard.test.tsx +++ b/frontends/ol-components/src/components/LearningResourceCard/LearningResourceListCard.test.tsx @@ -1,6 +1,6 @@ import React from "react" import { BrowserRouter } from "react-router-dom" -import { screen, render, act } from "@testing-library/react" +import { screen, render } from "@testing-library/react" import { LearningResourceListCard } from "./LearningResourceListCard" import type { LearningResourceListCardProps } from "./LearningResourceListCard" import { DEFAULT_RESOURCE_IMG, embedlyCroppedImage } from "ol-utilities" @@ -11,10 +11,7 @@ import { ThemeProvider } from "../ThemeProvider/ThemeProvider" const setup = (props: LearningResourceListCardProps) => { return render( - + , { wrapper: ThemeProvider }, ) @@ -30,7 +27,7 @@ describe("Learning Resource List Card", () => { setup({ resource }) screen.getByText("Course") - screen.getByRole("heading", { name: resource.title }) + screen.getByText(resource.title) screen.getByText("Starts:") screen.getByText("January 01, 2026") }) @@ -92,14 +89,13 @@ describe("Learning Resource List Card", () => { platform: { code: PlatformEnum.Ocw }, }) - setup({ resource }) + setup({ resource, href: "/path/to/thing" }) - const heading = screen.getByRole("heading", { name: resource.title }) - await act(async () => { - await heading.click() + const card = screen.getByRole("link", { + name: new RegExp(resource.title), }) - expect(window.location.search).toBe(`?resource=${resource.id}`) + expect(card).toHaveAttribute("href", "/path/to/thing") }) test("Click action buttons", async () => { From af64e53dbab1e4a1a10869699eb1d45b78fb37ff Mon Sep 17 00:00:00 2001 From: Chris Chudzicki Date: Tue, 27 Aug 2024 14:07:05 -0400 Subject: [PATCH 06/16] better header on listing pages --- .../DepartmentListingPage.test.tsx | 4 ++-- .../DepartmentListingPage/DepartmentListingPage.tsx | 10 ++++------ .../src/pages/TopicListingPage/TopicsListingPage.tsx | 2 +- .../src/pages/UnitsListingPage/UnitsListingPage.tsx | 5 +++-- 4 files changed, 10 insertions(+), 11 deletions(-) diff --git a/frontends/mit-learn/src/pages/DepartmentListingPage/DepartmentListingPage.test.tsx b/frontends/mit-learn/src/pages/DepartmentListingPage/DepartmentListingPage.test.tsx index b65256f3c1..c00a4ea341 100644 --- a/frontends/mit-learn/src/pages/DepartmentListingPage/DepartmentListingPage.test.tsx +++ b/frontends/mit-learn/src/pages/DepartmentListingPage/DepartmentListingPage.test.tsx @@ -99,12 +99,12 @@ describe("DepartmentListingPage", () => { await screen.findByRole("heading", { name: schools[0].name, }) - ).closest("li") + ).closest("section") const school1 = ( await screen.findByRole("heading", { name: schools[1].name, }) - ).closest("li") + ).closest("section") invariant(school0) invariant(school1) diff --git a/frontends/mit-learn/src/pages/DepartmentListingPage/DepartmentListingPage.tsx b/frontends/mit-learn/src/pages/DepartmentListingPage/DepartmentListingPage.tsx index 2479707d8a..b7165eb714 100644 --- a/frontends/mit-learn/src/pages/DepartmentListingPage/DepartmentListingPage.tsx +++ b/frontends/mit-learn/src/pages/DepartmentListingPage/DepartmentListingPage.tsx @@ -3,7 +3,6 @@ import { Container, Typography, styled, - PlainList, List, ListItem, ListItemLink, @@ -130,10 +129,9 @@ const SchoolDepartments: React.FC = ({ courseCounts, programCounts, className, - as: Component = "div", }) => { return ( - +
{SCHOOL_ICONS[school.url] ?? } @@ -172,12 +170,12 @@ const SchoolDepartments: React.FC = ({ ) })} - +
) } -const SchoolList = styled(PlainList)(({ theme }) => ({ - "> li": { +const SchoolList = styled.div(({ theme }) => ({ + "> section": { marginTop: "40px", [theme.breakpoints.down("sm")]: { marginTop: "30px", diff --git a/frontends/mit-learn/src/pages/TopicListingPage/TopicsListingPage.tsx b/frontends/mit-learn/src/pages/TopicListingPage/TopicsListingPage.tsx index d2296ddffa..9e535856b2 100644 --- a/frontends/mit-learn/src/pages/TopicListingPage/TopicsListingPage.tsx +++ b/frontends/mit-learn/src/pages/TopicListingPage/TopicsListingPage.tsx @@ -42,7 +42,7 @@ type TopicBoxHeaderProps = { const TopicBoxHeader = styled( ({ title, icon, href, className }: TopicBoxHeaderProps) => { return ( - +
) } diff --git a/frontends/mit-learn/src/pages/ChannelPage/UnitChannelTemplate.tsx b/frontends/mit-learn/src/pages/ChannelPage/UnitChannelTemplate.tsx index d4884dc286..9ebb7e7b90 100644 --- a/frontends/mit-learn/src/pages/ChannelPage/UnitChannelTemplate.tsx +++ b/frontends/mit-learn/src/pages/ChannelPage/UnitChannelTemplate.tsx @@ -7,6 +7,7 @@ import { Stack, BannerBackground, Typography, + VisuallyHidden, } from "ol-components" import { SearchSubscriptionToggle } from "@/page-components/SearchSubscriptionToggle/SearchSubscriptionToggle" import { ChannelDetails } from "@/page-components/ChannelDetails/ChannelDetails" @@ -131,7 +132,8 @@ const UnitChannelTemplate: React.FC = ({ /> - + + {channel.data?.title} {channel.data ? ( ) : null} @@ -181,14 +183,18 @@ const UnitChannelTemplate: React.FC = ({
- + - +
+ What Learners Say + +
{children} ) diff --git a/frontends/mit-learn/src/pages/HomePage/HomePage.tsx b/frontends/mit-learn/src/pages/HomePage/HomePage.tsx index 02dad47f8a..fe4f4825c3 100644 --- a/frontends/mit-learn/src/pages/HomePage/HomePage.tsx +++ b/frontends/mit-learn/src/pages/HomePage/HomePage.tsx @@ -43,15 +43,17 @@ const HomePage: React.FC = () => { - +
+ +
- + { return ( + +

Search

+
@@ -233,6 +236,8 @@ const SearchPage: React.FC = () => {
Date: Tue, 27 Aug 2024 15:54:16 -0400 Subject: [PATCH 08/16] update privacy page headings --- .../src/pages/PrivacyPage/PrivacyPage.tsx | 36 ++++++++++++------- 1 file changed, 23 insertions(+), 13 deletions(-) diff --git a/frontends/mit-learn/src/pages/PrivacyPage/PrivacyPage.tsx b/frontends/mit-learn/src/pages/PrivacyPage/PrivacyPage.tsx index bd07cb1561..2528683377 100644 --- a/frontends/mit-learn/src/pages/PrivacyPage/PrivacyPage.tsx +++ b/frontends/mit-learn/src/pages/PrivacyPage/PrivacyPage.tsx @@ -50,10 +50,12 @@ const BodyContainer = styled.div({ gap: "20px", }) -const BodyText = styled(Typography)(({ theme }) => ({ - alignSelf: "stretch", - color: theme.custom.colors.black, -})) +const BodyText = styled(Typography)>( + ({ theme }) => ({ + alignSelf: "stretch", + color: theme.custom.colors.black, + }), +) const UnorderedList = styled.ul(({ theme }) => ({ width: "100%", @@ -80,14 +82,18 @@ const PrivacyPage: React.FC = () => { - Introduction + + Introduction + {SITE_NAME} provides information about MIT courses, programs, and learning materials to learners from across the world. This Privacy Statement explains how {SITE_NAME} collects, uses, and processes personal information about our learners. - What personal information we collect + + What personal information we collect + We may collect, use, store, and transfer different kinds of personal information about you, which we have grouped together as follows: @@ -104,7 +110,7 @@ const PrivacyPage: React.FC = () => {
  • IP addresses
  • Course progress and performance
  • - + How we collect personal information about you @@ -140,7 +146,9 @@ const PrivacyPage: React.FC = () => { "Help" section of the toolbar. If you reject our cookies, many functions and conveniences of this Site may not work properly. - How we use your personal information + + How we use your personal information + We collect, use, and process your personal information (1) to process transactions requested by you and meet our contractual @@ -178,7 +186,7 @@ const PrivacyPage: React.FC = () => { will always respect a request by you to stop processing your personal information (subject to our legal obligations). - + When we share your personal information @@ -199,7 +207,7 @@ const PrivacyPage: React.FC = () => { or otherwise address fraud, security or technical issues; or to protect the rights, property or safety of us, our users or others. - + How your information is stored and secured @@ -223,7 +231,7 @@ const PrivacyPage: React.FC = () => { based on verified business use cases and subject to auditing to verify appropriate applications. - + How long we keep your personal information @@ -236,7 +244,7 @@ const PrivacyPage: React.FC = () => { archival, scientific and historical research and for the defense of potential legal claims. - + Rights for Individuals in the European Economic Area (EEA) or United Kingdom (UK) @@ -299,7 +307,9 @@ const PrivacyPage: React.FC = () => {
    Address: 71 Queen Victoria Street, London, EC4V 4BE, United Kingdom
    - Additional Information + + Additional Information + We may change this Privacy Statement from time to time. If we make any significant changes in the way we treat your personal From d0fd4084ee8a1402e6987ba76dd0b677c17da46b Mon Sep 17 00:00:00 2001 From: Chris Chudzicki Date: Tue, 27 Aug 2024 16:33:48 -0400 Subject: [PATCH 09/16] improve dashboard headings --- .../ItemsListing/ItemsListingComponent.tsx | 17 +++++----- .../ResourceCarousel.test.tsx | 8 ++--- .../ResourceCarousel/ResourceCarousel.tsx | 6 ++-- .../UserListCard/UserListCardCondensed.tsx | 6 +--- .../pages/ChannelPage/UnitChannelTemplate.tsx | 2 +- .../src/pages/DashboardPage/DashboardPage.tsx | 31 ++++++++++++------- .../mit-learn/src/pages/HomePage/HomePage.tsx | 4 +-- 7 files changed, 38 insertions(+), 36 deletions(-) diff --git a/frontends/mit-learn/src/page-components/ItemsListing/ItemsListingComponent.tsx b/frontends/mit-learn/src/page-components/ItemsListing/ItemsListingComponent.tsx index 13627b7bf0..cb8640bd1b 100644 --- a/frontends/mit-learn/src/page-components/ItemsListing/ItemsListingComponent.tsx +++ b/frontends/mit-learn/src/page-components/ItemsListing/ItemsListingComponent.tsx @@ -26,13 +26,12 @@ type ItemsListingComponentProps = { condensed?: boolean } -const HeaderText = styled.div(({ theme }) => ({ - h3: { - ...theme.typography.h3, - [theme.breakpoints.down("sm")]: { - marginBottom: "24px", - ...theme.typography.h5, - }, +const HeaderText = styled.h1(({ theme }) => ({ + margin: 0, + ...theme.typography.h3, + [theme.breakpoints.down("sm")]: { + marginBottom: "24px", + ...theme.typography.h5, }, })) @@ -110,9 +109,7 @@ const ItemsListingComponent: React.FC = ({ marginBottom="24px" > - - {list?.title} - + {list?.title} {list?.description && ( {list.description} )} diff --git a/frontends/mit-learn/src/page-components/ResourceCarousel/ResourceCarousel.test.tsx b/frontends/mit-learn/src/page-components/ResourceCarousel/ResourceCarousel.test.tsx index 856e329c48..1cb35c439f 100644 --- a/frontends/mit-learn/src/page-components/ResourceCarousel/ResourceCarousel.test.tsx +++ b/frontends/mit-learn/src/page-components/ResourceCarousel/ResourceCarousel.test.tsx @@ -173,11 +173,11 @@ describe("ResourceCarousel", () => { }) it.each([ - { headerComponent: "h1", expectedTag: "H1" }, - { headerComponent: "h3", expectedTag: "H3" }, + { titleComponent: "h1", expectedTag: "H1" }, + { titleComponent: "h3", expectedTag: "H3" }, ] as const)( "Shows the correct title with correct heading level", - async ({ headerComponent, expectedTag }) => { + async ({ titleComponent, expectedTag }) => { const config: ResourceCarouselProps["config"] = [ { label: "Resources", @@ -194,7 +194,7 @@ describe("ResourceCarousel", () => { setupApis() renderWithProviders( , diff --git a/frontends/mit-learn/src/page-components/ResourceCarousel/ResourceCarousel.tsx b/frontends/mit-learn/src/page-components/ResourceCarousel/ResourceCarousel.tsx index 7b6f0dbfaf..771fb83cdf 100644 --- a/frontends/mit-learn/src/page-components/ResourceCarousel/ResourceCarousel.tsx +++ b/frontends/mit-learn/src/page-components/ResourceCarousel/ResourceCarousel.tsx @@ -158,7 +158,7 @@ type ResourceCarouselProps = { /** * Element type for the carousel title */ - headerComponent: React.ElementType + titleComponent: React.ElementType } /** * A tabbed carousel that fetches resources based on the configuration provided. @@ -177,7 +177,7 @@ const ResourceCarousel: React.FC = ({ className, isLoading, "data-testid": dataTestId, - headerComponent, + titleComponent, }) => { const [tab, setTab] = React.useState("0") const [ref, setRef] = React.useState(null) @@ -221,7 +221,7 @@ const ResourceCarousel: React.FC = ({ - + {title} {config.length === 1 ? buttonsContainerElement : null} diff --git a/frontends/mit-learn/src/page-components/UserListCard/UserListCardCondensed.tsx b/frontends/mit-learn/src/page-components/UserListCard/UserListCardCondensed.tsx index 9fa5debd43..1689b5bd71 100644 --- a/frontends/mit-learn/src/page-components/UserListCard/UserListCardCondensed.tsx +++ b/frontends/mit-learn/src/page-components/UserListCard/UserListCardCondensed.tsx @@ -52,11 +52,7 @@ const UserListCardCondensed = ({ - + {userList.title} diff --git a/frontends/mit-learn/src/pages/ChannelPage/UnitChannelTemplate.tsx b/frontends/mit-learn/src/pages/ChannelPage/UnitChannelTemplate.tsx index 9ebb7e7b90..233d8a084c 100644 --- a/frontends/mit-learn/src/pages/ChannelPage/UnitChannelTemplate.tsx +++ b/frontends/mit-learn/src/pages/ChannelPage/UnitChannelTemplate.tsx @@ -186,7 +186,7 @@ const UnitChannelTemplate: React.FC = ({ diff --git a/frontends/mit-learn/src/pages/DashboardPage/DashboardPage.tsx b/frontends/mit-learn/src/pages/DashboardPage/DashboardPage.tsx index 392c8eb927..7b0073d72a 100644 --- a/frontends/mit-learn/src/pages/DashboardPage/DashboardPage.tsx +++ b/frontends/mit-learn/src/pages/DashboardPage/DashboardPage.tsx @@ -18,6 +18,7 @@ import { TabPanel, Tabs, Typography, + TypographyProps, styled, } from "ol-components" import { Link } from "react-router-dom" @@ -206,14 +207,16 @@ const TabPanelStyled = styled(TabPanel)({ width: "100%", }) -const TitleText = styled(Typography)(({ theme }) => ({ - color: theme.custom.colors.black, - paddingBottom: "16px", - ...theme.typography.h3, - [theme.breakpoints.down("md")]: { - ...theme.typography.h5, - }, -})) +const TitleText = styled(Typography)>( + ({ theme }) => ({ + color: theme.custom.colors.black, + paddingBottom: "16px", + ...theme.typography.h3, + [theme.breakpoints.down("md")]: { + ...theme.typography.h5, + }, + }), +) const SubTitleText = styled(Typography)(({ theme }) => ({ color: theme.custom.colors.darkGray2, @@ -418,7 +421,7 @@ const DashboardPage: React.FC = () => { - + Your MIT Learning Journey @@ -432,6 +435,7 @@ const DashboardPage: React.FC = () => { { {topics?.map((topic, index) => ( { ))} {certification === true ? ( { /> ) : ( { /> )} { - Profile + Profile {isLoadingProfile || !profile ? ( ) : ( @@ -486,7 +495,7 @@ const DashboardPage: React.FC = () => { )} - Settings + Settings {isLoadingProfile || !profile ? ( ) : ( diff --git a/frontends/mit-learn/src/pages/HomePage/HomePage.tsx b/frontends/mit-learn/src/pages/HomePage/HomePage.tsx index fe4f4825c3..d55d25e523 100644 --- a/frontends/mit-learn/src/pages/HomePage/HomePage.tsx +++ b/frontends/mit-learn/src/pages/HomePage/HomePage.tsx @@ -45,7 +45,7 @@ const HomePage: React.FC = () => {
    @@ -55,7 +55,7 @@ const HomePage: React.FC = () => { From db020981802803a6703277255372eafd5e5b3432 Mon Sep 17 00:00:00 2001 From: Chris Chudzicki Date: Tue, 27 Aug 2024 17:04:21 -0400 Subject: [PATCH 10/16] fix a test --- .../ResourceCarousel/ResourceCarousel.test.tsx | 18 +++++++++++++++--- 1 file changed, 15 insertions(+), 3 deletions(-) diff --git a/frontends/mit-learn/src/page-components/ResourceCarousel/ResourceCarousel.test.tsx b/frontends/mit-learn/src/page-components/ResourceCarousel/ResourceCarousel.test.tsx index 1cb35c439f..6a40d54875 100644 --- a/frontends/mit-learn/src/page-components/ResourceCarousel/ResourceCarousel.test.tsx +++ b/frontends/mit-learn/src/page-components/ResourceCarousel/ResourceCarousel.test.tsx @@ -86,7 +86,11 @@ describe("ResourceCarousel", () => { const { resources } = setupApis({ autoResolve: true }) renderWithProviders( - , + , ) const tabs = await screen.findAllByRole("tab") @@ -123,7 +127,11 @@ describe("ResourceCarousel", () => { const { resources } = setupApis() renderWithProviders( - , + , ) if (expectTabs) { @@ -153,7 +161,11 @@ describe("ResourceCarousel", () => { setMockResponse.get(urls.userMe.get(), {}) setupApis() renderWithProviders( - , + , ) await waitFor(() => { expect(makeRequest).toHaveBeenCalledWith( From 0e67a984b2516a87cf62b96dfa6a1f843c649fab Mon Sep 17 00:00:00 2001 From: Chris Chudzicki Date: Tue, 27 Aug 2024 17:09:50 -0400 Subject: [PATCH 11/16] fix a ts issue --- .../mit-learn/src/pages/HomePage/UpcomingCoursesSection.tsx | 1 + 1 file changed, 1 insertion(+) diff --git a/frontends/mit-learn/src/pages/HomePage/UpcomingCoursesSection.tsx b/frontends/mit-learn/src/pages/HomePage/UpcomingCoursesSection.tsx index 5f4b922c76..10b6092146 100644 --- a/frontends/mit-learn/src/pages/HomePage/UpcomingCoursesSection.tsx +++ b/frontends/mit-learn/src/pages/HomePage/UpcomingCoursesSection.tsx @@ -37,6 +37,7 @@ const UpcomingCoursesSection: React.FC = () => { return ( From caf9c31a7d835d8ace7cc512eb1db5b0ce99fcfb Mon Sep 17 00:00:00 2001 From: Chris Chudzicki Date: Wed, 28 Aug 2024 12:41:59 -0400 Subject: [PATCH 12/16] fix a test --- .../CardTemplate/CardTemplate.test.tsx | 21 -- .../CardTemplate/CardTemplate.tsx | 250 ------------------ .../UserListCardCondensed.test.tsx | 5 +- .../UserListListingComponent.test.tsx | 4 +- 4 files changed, 4 insertions(+), 276 deletions(-) delete mode 100644 frontends/mit-learn/src/page-components/CardTemplate/CardTemplate.test.tsx delete mode 100644 frontends/mit-learn/src/page-components/CardTemplate/CardTemplate.tsx diff --git a/frontends/mit-learn/src/page-components/CardTemplate/CardTemplate.test.tsx b/frontends/mit-learn/src/page-components/CardTemplate/CardTemplate.test.tsx deleted file mode 100644 index 682049ab6c..0000000000 --- a/frontends/mit-learn/src/page-components/CardTemplate/CardTemplate.test.tsx +++ /dev/null @@ -1,21 +0,0 @@ -import React from "react" -import { render, screen } from "@testing-library/react" -import CardTemplate from "./CardTemplate" -import { DEFAULT_RESOURCE_IMG } from "ol-utilities" -import { makeImgConfig } from "ol-utilities/test-utils/factories" - -describe("CardTemplate", () => { - it("renders title and cover image", () => { - const title = "Test Title" - render( - , - ) - const heading = screen.getByRole("heading", { name: title }) - expect(heading).toHaveAccessibleName(title) - }) -}) diff --git a/frontends/mit-learn/src/page-components/CardTemplate/CardTemplate.tsx b/frontends/mit-learn/src/page-components/CardTemplate/CardTemplate.tsx deleted file mode 100644 index d88b45050c..0000000000 --- a/frontends/mit-learn/src/page-components/CardTemplate/CardTemplate.tsx +++ /dev/null @@ -1,250 +0,0 @@ -import React from "react" -import invariant from "tiny-invariant" -import { - MuiCard, - CardContent, - CardMedia, - styled, - TruncateText, -} from "ol-components" -import { RiDraggable } from "@remixicon/react" -import { - DEFAULT_RESOURCE_IMG, - EmbedlyConfig, - embedlyCroppedImage, -} from "ol-utilities" - -type CardVariant = "column" | "row" | "row-reverse" -type OnActivateCard = () => void -type CardTemplateProps = { - /** - * Whether the course picture and info display as a column or row. - */ - variant: CardVariant - className?: string - handleActivate?: OnActivateCard - extraDetails?: React.ReactNode - imgUrl: string - imgConfig: EmbedlyConfig - title?: string - bodySlot?: React.ReactNode - footerSlot?: React.ReactNode - footerActionSlot?: React.ReactNode - sortable?: boolean - suppressImage?: boolean -} - -const LIGHT_TEXT_COLOR = "#8c8c8c" -const SPACER = 0.75 -const SMALL_FONT_SIZE = 0.75 - -const StyledCard = styled(MuiCard)` - display: flex; - flex-direction: column; - - /* Ensure the resource image borders match card borders */ - .MuiCardMedia-root, - > .MuiCardContent-root { - border-radius: inherit; - } -` - -const Details = styled.div` - /* Make content flexbox so that we can control which child fills remaining space. */ - flex: 1; - display: flex; - flex-direction: column; - - > * { - /* - Flexbox doesn't have collapsing margins, so we need to avoid double spacing. - The column-gap property would be a nicer solution, but it doesn't have the - best browser support yet. - */ - margin-top: ${SPACER / 2}rem; - margin-bottom: ${SPACER / 2}rem; - - &:first-of-type { - margin-top: 0; - } - - &:last-child { - margin-bottom: 0; - } - } -` - -const StyledCardContent = styled(CardContent, { - shouldForwardProp: (prop) => prop !== "sortable", -})<{ - variant: CardVariant - sortable: boolean -}>` - display: flex; - flex-direction: ${({ variant }) => variant}; - ${({ variant }) => (variant === "column" ? "flex: 1;" : "")} - ${({ sortable }) => (sortable ? "padding-left: 4px;" : "")} -` - -/* - Last child of ol-lrc-content will take up any extra space (flex: 1) but - with its contents at the bottom of its box. - The default is stretch, we we do not want. -*/ -const FillSpaceContentEnd = styled.div` - flex: 1; - display: flex; - flex-direction: column; - justify-content: flex-end; - align-items: flex-start; -` - -const FooterRow = styled.div` - min-height: 2.5 * ${SMALL_FONT_SIZE}; /* ensure consistent spacing even if no date */ - display: flex; - flex-direction: row; - align-items: center; - justify-content: space-between; - width: 100%; -` - -const EllipsisTitle = styled(TruncateText)({ - fontWeight: "bold", - margin: 0, -}) - -const TitleButton = styled.button` - border: none; - background-color: white; - color: inherit; - display: block; - text-align: left; - padding: 0; - margin: 0; - - &:hover { - text-decoration: underline; - cursor: pointer; - } -` - -const DragHandle = styled.div` - display: flex; - align-items: center; - font-size: 40px; - align-self: stretch; - color: ${LIGHT_TEXT_COLOR}; - border-right: 1px solid ${LIGHT_TEXT_COLOR}; - margin-right: 16px; -` - -const CardMediaImage = styled(CardMedia)<{ - variant: CardVariant - component: string - alt: string -}>` - ${({ variant }) => - variant === "row" - ? "margin-right: 16px;" - : variant === "row-reverse" - ? "margin-left: 16px;" - : ""} -` - -type ImageProps = Pick< - CardTemplateProps, - "imgUrl" | "imgConfig" | "suppressImage" | "variant" -> -const CardImage: React.FC = ({ - imgUrl, - imgConfig, - suppressImage, - variant, -}) => { - if (suppressImage) return null - const dims = - variant === "column" - ? { height: imgConfig.height } - : { width: imgConfig.width, height: imgConfig.height } - - return ( - - ) -} - -const CardTemplate = ({ - variant, - className, - handleActivate, - extraDetails, - imgUrl, - imgConfig, - title, - bodySlot, - footerSlot, - footerActionSlot, - sortable = false, - suppressImage = false, -}: CardTemplateProps) => { - invariant( - !sortable || variant === "row-reverse", - "sortable only supported for variant='row-reverse'", - ) - - const image = ( - - ) - - return ( - - {variant === "column" ? image : null} - - {variant !== "column" ? image : null} -
    - {extraDetails} - {handleActivate ? ( - - - {title} - - - ) : ( - - {title} - - )} - {sortable ? null : ( - <> - {bodySlot} - - -
    {footerSlot}
    - {footerActionSlot} -
    -
    - - )} -
    - {sortable ? ( - - - - ) : null} -
    -
    - ) -} - -export default CardTemplate -export type { CardTemplateProps } diff --git a/frontends/mit-learn/src/page-components/UserListCard/UserListCardCondensed.test.tsx b/frontends/mit-learn/src/page-components/UserListCard/UserListCardCondensed.test.tsx index 06168e2b31..562600c4c3 100644 --- a/frontends/mit-learn/src/page-components/UserListCard/UserListCardCondensed.test.tsx +++ b/frontends/mit-learn/src/page-components/UserListCard/UserListCardCondensed.test.tsx @@ -8,7 +8,7 @@ import { renderWithProviders } from "@/test-utils" const userListFactory = factories.userLists describe("UserListCard", () => { - it("renders title and cover image", () => { + it("renders title", () => { const userList = userListFactory.userList() renderWithProviders( { userList={userList} />, ) - const heading = screen.getByRole("heading", { name: userList.title }) - expect(heading).toHaveAccessibleName(userList.title) + screen.getByText(userList.title) }) }) diff --git a/frontends/mit-learn/src/pages/UserListListingComponent/UserListListingComponent.test.tsx b/frontends/mit-learn/src/pages/UserListListingComponent/UserListListingComponent.test.tsx index c1aa9dfaf2..8157aa1027 100644 --- a/frontends/mit-learn/src/pages/UserListListingComponent/UserListListingComponent.test.tsx +++ b/frontends/mit-learn/src/pages/UserListListingComponent/UserListListingComponent.test.tsx @@ -82,8 +82,8 @@ describe("UserListListingComponent", () => { it("Renders a card for each user list", async () => { const { paths } = setup() const titles = paths.results.map((userList) => userList.title) - const headings = await screen.findAllByRole("heading", { - name: (value) => titles.includes(value), + const headings = await screen.findAllByRole("link", { + name: (name) => titles.some((title) => name.includes(title)), }) // for sanity From 242a5af2fea5606e1ce0336f52fbfada4c5badfb Mon Sep 17 00:00:00 2001 From: Chris Chudzicki Date: Wed, 28 Aug 2024 12:53:47 -0400 Subject: [PATCH 13/16] fix accidental testimonial breakage --- .../mit-learn/src/pages/ChannelPage/UnitChannelTemplate.tsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/frontends/mit-learn/src/pages/ChannelPage/UnitChannelTemplate.tsx b/frontends/mit-learn/src/pages/ChannelPage/UnitChannelTemplate.tsx index 233d8a084c..eeb699533b 100644 --- a/frontends/mit-learn/src/pages/ChannelPage/UnitChannelTemplate.tsx +++ b/frontends/mit-learn/src/pages/ChannelPage/UnitChannelTemplate.tsx @@ -193,7 +193,7 @@ const UnitChannelTemplate: React.FC = ({
    What Learners Say - +
    {children} From c2dd98f14b55f47baa68b813dcd92284ad8e5998 Mon Sep 17 00:00:00 2001 From: Chris Chudzicki Date: Wed, 28 Aug 2024 15:52:04 -0400 Subject: [PATCH 14/16] add some tests --- .../pages/ChannelPage/ChannelPage.test.tsx | 36 ++++++ .../DepartmentListingPage.test.tsx | 13 +++ .../src/pages/HomePage/HomePage.test.tsx | 109 +++++++----------- .../src/pages/SearchPage/SearchPage.test.tsx | 14 ++- .../TopicsListingPage.test.tsx | 20 +++- .../UnitsListingPage.test.tsx | 12 ++ frontends/ol-test-utilities/package.json | 1 + frontends/ol-test-utilities/src/assertions.ts | 26 +++++ frontends/ol-test-utilities/src/index.ts | 1 + yarn.lock | 8 ++ 10 files changed, 171 insertions(+), 69 deletions(-) create mode 100644 frontends/ol-test-utilities/src/assertions.ts diff --git a/frontends/mit-learn/src/pages/ChannelPage/ChannelPage.test.tsx b/frontends/mit-learn/src/pages/ChannelPage/ChannelPage.test.tsx index e6b9fcc9e1..3654e4140a 100644 --- a/frontends/mit-learn/src/pages/ChannelPage/ChannelPage.test.tsx +++ b/frontends/mit-learn/src/pages/ChannelPage/ChannelPage.test.tsx @@ -10,6 +10,7 @@ import { assertPartialMetas, } from "../../test-utils" import ChannelSearch from "./ChannelSearch" +import { assertHeadings } from "ol-test-utilities" jest.mock("./ChannelSearch", () => { const actual = jest.requireActual("./ChannelSearch") @@ -214,6 +215,22 @@ describe("ChannelPage", () => { expect(subscribedButton).toBeVisible() }, ) + + test("headings", async () => { + const { channel } = setupApis({ + search_filter: "topic=Physics", + }) + renderTestApp({ url: `/c/${channel.channel_type}/${channel.name}` }) + + await waitFor(() => { + assertHeadings([ + { level: 1, name: channel.title }, + { level: 2, name: `Search within ${channel.title}` }, + { level: 3, name: "Filter" }, + { level: 3, name: "Search Results" }, + ]) + }) + }) }) describe("Unit Channel Pages", () => { @@ -302,4 +319,23 @@ describe("Unit Channel Pages", () => { ) }) }) + + test("headings", async () => { + const { channel } = setupApis({ + search_filter: "offered_by=ocw", + channel_type: "unit", + }) + renderTestApp({ url: `/c/${channel.channel_type}/${channel.name}` }) + + await waitFor(() => { + assertHeadings([ + { level: 1, name: channel.title }, + { level: 2, name: "Featured Courses" }, + { level: 2, name: "What Learners Say" }, + { level: 2, name: `Search within ${channel.title}` }, + { level: 3, name: "Filter" }, + { level: 3, name: "Search Results" }, + ]) + }) + }) }) diff --git a/frontends/mit-learn/src/pages/DepartmentListingPage/DepartmentListingPage.test.tsx b/frontends/mit-learn/src/pages/DepartmentListingPage/DepartmentListingPage.test.tsx index c00a4ea341..8138a4bb73 100644 --- a/frontends/mit-learn/src/pages/DepartmentListingPage/DepartmentListingPage.test.tsx +++ b/frontends/mit-learn/src/pages/DepartmentListingPage/DepartmentListingPage.test.tsx @@ -5,6 +5,7 @@ import DepartmentListingPage from "./DepartmentListingPage" import { factories, setMockResponse, urls } from "api/test-utils" import invariant from "tiny-invariant" import { faker } from "@faker-js/faker/locale/en" +import { assertHeadings } from "ol-test-utilities" const makeSearchResponse = ( aggregations: Record, @@ -157,4 +158,16 @@ describe("DepartmentListingPage", () => { }) expect(link).toHaveAttribute("href", dept.channel_url) }) + + test("headings", async () => { + const { schools } = setupApis() + renderWithProviders() + + await waitFor(() => { + assertHeadings([ + { level: 1, name: "Browse by Academic Department" }, + ...schools.map(({ name }) => ({ level: 2, name })), + ]) + }) + }) }) diff --git a/frontends/mit-learn/src/pages/HomePage/HomePage.test.tsx b/frontends/mit-learn/src/pages/HomePage/HomePage.test.tsx index 1bdfcd9b06..4dbb341b00 100644 --- a/frontends/mit-learn/src/pages/HomePage/HomePage.test.tsx +++ b/frontends/mit-learn/src/pages/HomePage/HomePage.test.tsx @@ -14,9 +14,9 @@ import { within, waitFor, } from "../../test-utils" -import type { FeaturedApiFeaturedListRequest as FeaturedRequest } from "api" import invariant from "tiny-invariant" import * as routes from "@/common/urls" +import { assertHeadings } from "ol-test-utilities" const assertLinksTo = ( el: HTMLElement, @@ -46,29 +46,32 @@ const setupAPIs = () => { resources, ) - setMockResponse.get(urls.learningResources.featured({ limit: 12 }), resources) setMockResponse.get( - urls.learningResources.featured({ - free: true, - limit: 12, - }), - resources, - ) - setMockResponse.get( - urls.learningResources.featured({ - certification: true, - professional: false, - limit: 12, - }), - resources, - ) - setMockResponse.get( - urls.learningResources.featured({ - professional: true, - limit: 12, - }), + expect.stringContaining(urls.learningResources.featured()), resources, ) + // setMockResponse.get( + // urls.learningResources.featured({ + // free: true, + // limit: 12, + // }), + // resources, + // ) + // setMockResponse.get( + // urls.learningResources.featured({ + // certification: true, + // professional: false, + // limit: 12, + // }), + // resources, + // ) + // setMockResponse.get( + // urls.learningResources.featured({ + // professional: true, + // limit: 12, + // }), + // resources, + // ) setMockResponse.get( urls.newsEvents.list({ feed_type: ["news"], limit: 6, sortby: "-created" }), @@ -315,55 +318,12 @@ describe("Home Page Testimonials", () => { }) describe("Home Page Carousel", () => { - test.each<{ tab: string; params: FeaturedRequest }>([ - { - tab: "All", - params: { limit: 12, resource_type: ["course"] }, - }, - { - tab: "Free", - params: { limit: 12, resource_type: ["course"], free: true }, - }, - { - tab: "With Certificate", - params: { - resource_type: ["course"], - limit: 12, - certification: true, - professional: false, - }, - }, - { - tab: "Professional & Executive Learning", - params: { resource_type: ["course"], limit: 12, professional: true }, - }, - ])("Featured Courses Carousel Tabs", async ({ tab, params }) => { - const resources = learningResources.resources({ count: 12 }) - setupAPIs() - - // The tab buttons eager-load the resources so we need to set them all up. - - // This is for the clicked tab (which might be "All") - // We will check that its response is visible as cards. - setMockResponse.get( - urls.learningResources.featured({ ...params }), - resources, - ) - - renderWithProviders() - screen.findByRole("tab", { name: tab }).then(async (featuredTab) => { - await user.click(within(featuredTab).getByRole("tab", { name: tab })) - const [featuredPanel] = screen.getAllByRole("tabpanel") - await within(featuredPanel).findByText(resources.results[0].title) - }) - }) - test("Tabbed Carousel sanity check", async () => { setupAPIs() renderWithProviders() - screen.findAllByRole("tablist").then(([featured, media]) => { + await screen.findAllByRole("tablist").then(([featured, media]) => { within(featured).getByRole("tab", { name: "All" }) within(featured).getByRole("tab", { name: "Free" }) within(featured).getByRole("tab", { name: "With Certificate" }) @@ -376,3 +336,22 @@ describe("Home Page Carousel", () => { }) }) }) + +test("Headings", async () => { + setupAPIs() + + renderWithProviders() + await waitFor(() => { + assertHeadings([ + { level: 1, name: "Learn with MIT" }, + { level: 2, name: "Featured Courses" }, + { level: 2, name: "Continue Your Journey" }, + { level: 2, name: "Media" }, + { level: 2, name: "Browse by Topic" }, + { level: 2, name: "From Our Community" }, + { level: 2, name: "MIT Stories & Events" }, + { level: 3, name: "Stories" }, + { level: 3, name: "Events" }, + ]) + }) +}) diff --git a/frontends/mit-learn/src/pages/SearchPage/SearchPage.test.tsx b/frontends/mit-learn/src/pages/SearchPage/SearchPage.test.tsx index cb169cdd6d..bac2bf3cb1 100644 --- a/frontends/mit-learn/src/pages/SearchPage/SearchPage.test.tsx +++ b/frontends/mit-learn/src/pages/SearchPage/SearchPage.test.tsx @@ -14,6 +14,7 @@ import type { } from "api" import invariant from "tiny-invariant" import { Permissions } from "@/common/permissions" +import { assertHeadings } from "ol-test-utilities" const setMockApiResponses = ({ search, @@ -21,7 +22,7 @@ const setMockApiResponses = ({ }: { search?: Partial offerors?: PaginatedLearningResourceOfferorDetailList -}) => { +} = {}) => { setMockResponse.get(urls.userMe.get(), { [Permissions.Authenticated]: false, }) @@ -662,4 +663,15 @@ describe("Search Page pagination controls", () => { expect(items.at(-2)?.textContent).toBe("7") // "Last page" expect(items.at(-1)?.textContent).toBe("") // "Next" button }) + + test("headings", () => { + setMockApiResponses() + renderWithProviders() + + assertHeadings([ + { level: 1, name: "Search" }, + { level: 2, name: "Filter" }, + { level: 2, name: "Search Results" }, + ]) + }) }) diff --git a/frontends/mit-learn/src/pages/TopicListingPage/TopicsListingPage.test.tsx b/frontends/mit-learn/src/pages/TopicListingPage/TopicsListingPage.test.tsx index 9eaa60ca3f..91802d56df 100644 --- a/frontends/mit-learn/src/pages/TopicListingPage/TopicsListingPage.test.tsx +++ b/frontends/mit-learn/src/pages/TopicListingPage/TopicsListingPage.test.tsx @@ -4,6 +4,7 @@ import type { LearningResourcesSearchResponse } from "api" import TopicsListingPage from "./TopicsListingPage" import { factories, setMockResponse, urls } from "api/test-utils" import invariant from "tiny-invariant" +import { assertHeadings } from "ol-test-utilities" const makeSearchResponse = ( aggregations: Record, @@ -25,7 +26,10 @@ const makeSearchResponse = ( } } -describe("DepartmentListingPage", () => { +const sorter = (a: { name: string }, b: { name: string }) => + a.name.localeCompare(b.name) + +describe("TopicsListingPage", () => { const setupApis = () => { const make = factories.learningResources const t1 = make.topic({ parent: null }) @@ -68,8 +72,6 @@ describe("DepartmentListingPage", () => { makeSearchResponse(programCounts), ) - const sorter = (a: { name: string }, b: { name: string }) => - a.name.localeCompare(b.name) const sortedSubtopics1 = [topics.t1a, topics.t1b, topics.t1c].sort(sorter) const sortedSubtopics2 = [topics.t2a, topics.t2b].sort(sorter) @@ -149,4 +151,16 @@ describe("DepartmentListingPage", () => { expect(topic2).toHaveTextContent("Courses: 200") expect(topic2).toHaveTextContent("Programs: 20") }) + + test("headings", async () => { + const { topics } = setupApis() + const sorted = [topics.t1, topics.t2].sort(sorter) + renderWithProviders() + await waitFor(() => { + assertHeadings([ + { level: 1, name: "Browse by Topic" }, + ...sorted.map((t) => ({ level: 2, name: t.name })), + ]) + }) + }) }) diff --git a/frontends/mit-learn/src/pages/UnitsListingPage/UnitsListingPage.test.tsx b/frontends/mit-learn/src/pages/UnitsListingPage/UnitsListingPage.test.tsx index f0ed639a13..f7ad6b95e3 100644 --- a/frontends/mit-learn/src/pages/UnitsListingPage/UnitsListingPage.test.tsx +++ b/frontends/mit-learn/src/pages/UnitsListingPage/UnitsListingPage.test.tsx @@ -3,6 +3,7 @@ import { renderWithProviders, screen, waitFor, within } from "@/test-utils" import type { LearningResourcesSearchResponse } from "api" import UnitsListingPage from "./UnitsListingPage" import { factories, setMockResponse, urls } from "api/test-utils" +import { assertHeadings } from "ol-test-utilities" const makeSearchResponse = ( aggregations: Record, @@ -175,4 +176,15 @@ describe("DepartmentListingPage", () => { }) }) }) + + test("headings", async () => { + setupApis() + renderWithProviders() + + assertHeadings([ + { level: 1, name: "Academic & Professional Learning" }, + { level: 2, name: "Academic Units" }, + { level: 2, name: "Professional Units" }, + ]) + }) }) diff --git a/frontends/ol-test-utilities/package.json b/frontends/ol-test-utilities/package.json index d1fbac79e1..912aa20650 100644 --- a/frontends/ol-test-utilities/package.json +++ b/frontends/ol-test-utilities/package.json @@ -10,6 +10,7 @@ "@faker-js/faker": "^8.0.0", "@testing-library/react": "16.0.0", "css-mediaquery": "^0.1.2", + "dom-accessibility-api": "^0.7.0", "tiny-invariant": "^1.3.1" } } diff --git a/frontends/ol-test-utilities/src/assertions.ts b/frontends/ol-test-utilities/src/assertions.ts new file mode 100644 index 0000000000..7128ec0895 --- /dev/null +++ b/frontends/ol-test-utilities/src/assertions.ts @@ -0,0 +1,26 @@ +import { screen } from "@testing-library/react" +/** + * This is the library that @testing-library uses to compute accessible names. + */ +import { computeAccessibleName } from "dom-accessibility-api" + +type HeadingSpec = { + level: number + /** + * The accessible name of the heading. + * Can be a matcher like `expect.stringContaining("foo")`. + */ + // eslint-disable-next-line @typescript-eslint/no-explicit-any + name: any +} +const assertHeadings = (expected: HeadingSpec[]) => { + const headings = screen.getAllByRole("heading") + const actual = headings.map((heading) => { + const level = parseInt(heading.tagName[1], 10) + const name = computeAccessibleName(heading) + return { level, name } + }) + expect(actual).toEqual(expected) +} + +export { assertHeadings } diff --git a/frontends/ol-test-utilities/src/index.ts b/frontends/ol-test-utilities/src/index.ts index 5bd11487e9..ae10ad5a02 100644 --- a/frontends/ol-test-utilities/src/index.ts +++ b/frontends/ol-test-utilities/src/index.ts @@ -2,3 +2,4 @@ export { default as ControlledPromise } from "./ControlledPromise/ControlledProm export * from "./factories" export * from "./domQueries" export * from "./mocks/mocks" +export * from "./assertions" diff --git a/yarn.lock b/yarn.lock index 13500e370a..d531e73db2 100644 --- a/yarn.lock +++ b/yarn.lock @@ -8782,6 +8782,13 @@ __metadata: languageName: node linkType: hard +"dom-accessibility-api@npm:^0.7.0": + version: 0.7.0 + resolution: "dom-accessibility-api@npm:0.7.0" + checksum: 10/ac69d27099bc0650633545c461347a355c2794b330f69b6b67fd98df91be9a48547d85176758747841e10ee041905143b39b6094e72c704c265b0f4f9bb7ab28 + languageName: node + linkType: hard + "dom-converter@npm:^0.2.0": version: 0.2.0 resolution: "dom-converter@npm:0.2.0" @@ -15413,6 +15420,7 @@ __metadata: "@faker-js/faker": "npm:^8.0.0" "@testing-library/react": "npm:16.0.0" css-mediaquery: "npm:^0.1.2" + dom-accessibility-api: "npm:^0.7.0" tiny-invariant: "npm:^1.3.1" languageName: unknown linkType: soft From 3e8a3371b8984c447a472ba37843354d415c1649 Mon Sep 17 00:00:00 2001 From: Chris Chudzicki Date: Wed, 28 Aug 2024 16:46:58 -0400 Subject: [PATCH 15/16] remove some commented out code --- .../src/pages/HomePage/HomePage.test.tsx | 22 ------------------- 1 file changed, 22 deletions(-) diff --git a/frontends/mit-learn/src/pages/HomePage/HomePage.test.tsx b/frontends/mit-learn/src/pages/HomePage/HomePage.test.tsx index 4dbb341b00..3f1f9eb441 100644 --- a/frontends/mit-learn/src/pages/HomePage/HomePage.test.tsx +++ b/frontends/mit-learn/src/pages/HomePage/HomePage.test.tsx @@ -50,28 +50,6 @@ const setupAPIs = () => { expect.stringContaining(urls.learningResources.featured()), resources, ) - // setMockResponse.get( - // urls.learningResources.featured({ - // free: true, - // limit: 12, - // }), - // resources, - // ) - // setMockResponse.get( - // urls.learningResources.featured({ - // certification: true, - // professional: false, - // limit: 12, - // }), - // resources, - // ) - // setMockResponse.get( - // urls.learningResources.featured({ - // professional: true, - // limit: 12, - // }), - // resources, - // ) setMockResponse.get( urls.newsEvents.list({ feed_type: ["news"], limit: 6, sortby: "-created" }), From 271859bf617eb0c9fc54441bc9ad2a92d9eb2b60 Mon Sep 17 00:00:00 2001 From: Chris Chudzicki Date: Wed, 28 Aug 2024 16:54:41 -0400 Subject: [PATCH 16/16] add a comment --- frontends/mit-learn/src/pages/TermsPage/TermsPage.tsx | 1 + 1 file changed, 1 insertion(+) diff --git a/frontends/mit-learn/src/pages/TermsPage/TermsPage.tsx b/frontends/mit-learn/src/pages/TermsPage/TermsPage.tsx index 0e4b50f78c..2acaa50dd4 100644 --- a/frontends/mit-learn/src/pages/TermsPage/TermsPage.tsx +++ b/frontends/mit-learn/src/pages/TermsPage/TermsPage.tsx @@ -1,3 +1,4 @@ +// Not urrently linked to. See https://github.com/mitodl/hq/issues/4639 import { Breadcrumbs, Container,