diff --git a/frontends/main/src/app-pages/AboutPage/AboutPage.tsx b/frontends/main/src/app-pages/AboutPage/AboutPage.tsx index 0c1aecdefb..0b011cc018 100644 --- a/frontends/main/src/app-pages/AboutPage/AboutPage.tsx +++ b/frontends/main/src/app-pages/AboutPage/AboutPage.tsx @@ -10,6 +10,7 @@ import { import * as urls from "@/common/urls" import React from "react" import domeImage from "@/public/mit-dome-2.jpg" +import Image from "next/image" const WHAT_IS_MIT_OPEN_FRAGMENT_IDENTIFIER = "what-is-mit-learn" const NON_DEGREE_LEARNING_FRAGMENT_IDENTIFIER = "non-degree-learning" @@ -81,13 +82,15 @@ const SubHeaderTextContainer = styled.div({ alignSelf: "flex-start", }) -const SubHeaderImage = styled.img({ +const SubHeaderImageContainer = styled.div({ flexGrow: 1, alignSelf: "stretch", + position: "relative", +}) + +const SubHeaderImage = styled(Image)({ borderRadius: "8px", - backgroundSize: "cover", - backgroundPosition: "center", - backgroundImage: `url(${domeImage.src})`, + objectFit: "cover", [theme.breakpoints.down("md")]: { height: "300px", }, @@ -165,7 +168,13 @@ const AboutPage: React.FC = () => {
  • Continue your education at your own pace
  • - + + + diff --git a/frontends/main/src/app-pages/ChannelPage/ChannelPage.test.tsx b/frontends/main/src/app-pages/ChannelPage/ChannelPage.test.tsx index fdd6177a76..2c85049974 100644 --- a/frontends/main/src/app-pages/ChannelPage/ChannelPage.test.tsx +++ b/frontends/main/src/app-pages/ChannelPage/ChannelPage.test.tsx @@ -379,7 +379,7 @@ describe("Channel Pages, Topic only", () => { }) describe("Channel Pages, Unit only", () => { - it("Displays the channel title, banner, and avatar", async () => { + it("Displays the channel title, banner", async () => { const { channel } = setupApis({ search_filter: "offered_by=ocw", channel_type: "unit", @@ -388,9 +388,24 @@ describe("Channel Pages, Unit only", () => { url: `/c/${channel.channel_type}/${channel.name}`, }) - const title = await screen.findByRole("heading", { name: channel.title }) - getByImageSrc(title, `${window.origin}${channel.configuration.logo}`) + await screen.findByRole("heading", { name: channel.title }) }) + + it("Displays the channel logo", async () => { + const { channel } = setupApis({ + name: "ocw", + channel_type: "unit", + }) + renderWithProviders(, { + url: `/c/${channel.channel_type}/${channel.name}`, + }) + + const images = await screen.findAllByRole("img", { + name: "MIT OpenCourseWare", + }) + expect(images[0]).toHaveAttribute("src", "/images/unit_logos/ocw.svg") + }) + it("Displays a featured carousel if the channel type is 'unit'", async () => { const { channel } = setupApis({ search_filter: "offered_by=ocw", diff --git a/frontends/main/src/app-pages/ChannelPage/DefaultChannelTemplate.tsx b/frontends/main/src/app-pages/ChannelPage/DefaultChannelTemplate.tsx index cc9cdbb121..4114a0b4f4 100644 --- a/frontends/main/src/app-pages/ChannelPage/DefaultChannelTemplate.tsx +++ b/frontends/main/src/app-pages/ChannelPage/DefaultChannelTemplate.tsx @@ -1,5 +1,6 @@ import React from "react" import { styled, Breadcrumbs, Banner } from "ol-components" +import { backgroundSrcSetCSS } from "ol-utilities" import { SearchSubscriptionToggle } from "@/page-components/SearchSubscriptionToggle/SearchSubscriptionToggle" import { useChannelDetail } from "api/hooks/channels" import ChannelMenu from "@/components/ChannelMenu/ChannelMenu" @@ -10,6 +11,7 @@ import { CHANNEL_TYPE_BREADCRUMB_TARGETS, ChannelControls, } from "./ChannelPageTemplate" +import backgroundSteps from "@/public/images/backgrounds/background_steps.jpg" const ChildrenContainer = styled.div(({ theme }) => ({ paddingTop: "40px", @@ -57,6 +59,7 @@ const DefaultChannelTemplate: React.FC = ({ const channel = useChannelDetail(String(channelType), String(name)) const urlParams = new URLSearchParams(channel.data?.search_filter) const displayConfiguration = channel.data?.configuration + return ( <> = ({ title={channel.data?.title} header={displayConfiguration?.heading} subHeader={displayConfiguration?.sub_heading} - backgroundUrl={displayConfiguration?.banner_background} + backgroundUrl={ + displayConfiguration?.banner_background ?? + backgroundSrcSetCSS(backgroundSteps) + } extraActions={ diff --git a/frontends/main/src/app-pages/ChannelPage/TopicChannelTemplate.tsx b/frontends/main/src/app-pages/ChannelPage/TopicChannelTemplate.tsx index 412a25c23a..f4b2a5de69 100644 --- a/frontends/main/src/app-pages/ChannelPage/TopicChannelTemplate.tsx +++ b/frontends/main/src/app-pages/ChannelPage/TopicChannelTemplate.tsx @@ -23,8 +23,9 @@ import { useLearningResourceTopic, useLearningResourceTopics, } from "api/hooks/learningResources" -import { propsNotNil } from "ol-utilities" +import { propsNotNil, backgroundSrcSetCSS } from "ol-utilities" import invariant from "tiny-invariant" +import backgroundSteps from "@/public/images/backgrounds/background_steps.jpg" const ChildrenContainer = styled.div(({ theme }) => ({ paddingTop: "40px", @@ -222,6 +223,7 @@ const TopicChannelTemplateInternal: React.FC< ) : ( ) + return ( <> } backgroundUrl={ displayConfiguration?.banner_background ?? - "/images/backgrounds/background_steps.jpg" + backgroundSrcSetCSS(backgroundSteps) } extraActions={ diff --git a/frontends/main/src/app-pages/ChannelPage/UnitChannelTemplate.tsx b/frontends/main/src/app-pages/ChannelPage/UnitChannelTemplate.tsx index e939d42b29..d6e8b6c9e0 100644 --- a/frontends/main/src/app-pages/ChannelPage/UnitChannelTemplate.tsx +++ b/frontends/main/src/app-pages/ChannelPage/UnitChannelTemplate.tsx @@ -8,7 +8,9 @@ import { BannerBackground, Typography, VisuallyHidden, + UnitLogo, } from "ol-components" +import { OfferedByEnum, SourceTypeEnum } from "api" import { SearchSubscriptionToggle } from "@/page-components/SearchSubscriptionToggle/SearchSubscriptionToggle" import { ChannelDetails } from "@/page-components/ChannelDetails/ChannelDetails" import { useChannelDetail } from "api/hooks/channels" @@ -16,7 +18,6 @@ import ChannelMenu from "@/components/ChannelMenu/ChannelMenu" import ResourceCarousel, { ResourceCarouselProps, } from "@/page-components/ResourceCarousel/ResourceCarousel" -import { SourceTypeEnum } from "api" import { getSearchParamMap } from "@/common/utils" import { HOME as HOME_URL, UNITS as UNITS_URL } from "../../common/urls" import { ChannelTypeEnum } from "api/v0" @@ -38,13 +39,13 @@ const FeaturedCoursesCarousel = styled(ResourceCarousel)(({ theme }) => ({ }, })) -const UnitLogo = styled.img(({ theme }) => ({ +const UnitLogoInverted = styled(UnitLogo)(({ theme }) => ({ filter: "saturate(0%) invert(100%)", + height: 50, maxWidth: "100%", - width: "auto", - height: "50px", [theme.breakpoints.down("md")]: { - height: "40px", + height: 40, + width: "auto", }, })) @@ -126,7 +127,10 @@ const UnitChannelTemplate: React.FC = ({ {channel.data?.title} {channel.data ? ( - + ) : null} diff --git a/frontends/main/src/app-pages/DepartmentListingPage/DepartmentListingPage.tsx b/frontends/main/src/app-pages/DepartmentListingPage/DepartmentListingPage.tsx index 32e308a1ea..8399d741db 100644 --- a/frontends/main/src/app-pages/DepartmentListingPage/DepartmentListingPage.tsx +++ b/frontends/main/src/app-pages/DepartmentListingPage/DepartmentListingPage.tsx @@ -13,7 +13,7 @@ import { Banner, Breadcrumbs, } from "ol-components" -import { pluralize } from "ol-utilities" +import { pluralize, backgroundSrcSetCSS } from "ol-utilities" import type { LearningResourceSchool } from "api" import { useSchoolsList } from "api/hooks/learningResources" import { @@ -27,7 +27,7 @@ import { RiTerminalBoxLine, } from "@remixicon/react" import { HOME } from "@/common/urls" - +import backgroundSteps from "@/public/images/backgrounds/background_steps.jpg" import { aggregateProgramCounts, aggregateCourseCounts } from "@/common/utils" import { useChannelCounts } from "api/hooks/channels" @@ -201,7 +201,6 @@ const DepartmentListingPage: React.FC = () => { return ( <> { current="Departments" /> } + backgroundUrl={backgroundSrcSetCSS(backgroundSteps)} /> diff --git a/frontends/main/src/app-pages/HomePage/PersonalizeSection.tsx b/frontends/main/src/app-pages/HomePage/PersonalizeSection.tsx index 93bd620af7..e20e788483 100644 --- a/frontends/main/src/app-pages/HomePage/PersonalizeSection.tsx +++ b/frontends/main/src/app-pages/HomePage/PersonalizeSection.tsx @@ -1,11 +1,14 @@ import React from "react" import { Typography, styled, Container, ButtonLink } from "ol-components" +import { backgroundSrcSetCSS } from "ol-utilities" import { useUserMe } from "api/hooks/user" import * as urls from "@/common/urls" +import personalizeImage from "@/public/images/homepage/personalize-image.png" +import personalizeBgImage from "@/public/images/homepage/personalize-bg.png" const FullWidthBackground = styled.div(({ theme }) => ({ padding: "80px 0", - background: 'url("/images/homepage/personalize-bg.png") center top no-repeat', + background: `${backgroundSrcSetCSS(personalizeBgImage)} center top no-repeat`, backgroundSize: "cover", [theme.breakpoints.down("md")]: { padding: "40px 0", @@ -108,7 +111,7 @@ const PersonalizeSection = () => { return ( - + diff --git a/frontends/main/src/app-pages/LearningPathListingPage/LearningPathListingPage.tsx b/frontends/main/src/app-pages/LearningPathListingPage/LearningPathListingPage.tsx index 94502ce32a..8c0066287d 100644 --- a/frontends/main/src/app-pages/LearningPathListingPage/LearningPathListingPage.tsx +++ b/frontends/main/src/app-pages/LearningPathListingPage/LearningPathListingPage.tsx @@ -84,11 +84,6 @@ const LearningPathListingPage: React.FC = () => { src="/images/backgrounds/course_search_banner.png" className="learningpaths-page" > - {/* TODO - - - - */} diff --git a/frontends/main/src/app-pages/ProgramLetterPage/[id]/view/ProgramLetter.test.tsx b/frontends/main/src/app-pages/ProgramLetterPage/ProgramLetter.test.tsx similarity index 100% rename from frontends/main/src/app-pages/ProgramLetterPage/[id]/view/ProgramLetter.test.tsx rename to frontends/main/src/app-pages/ProgramLetterPage/ProgramLetter.test.tsx diff --git a/frontends/main/src/app-pages/ProgramLetterPage/[id]/view/ProgramLetterPage.tsx b/frontends/main/src/app-pages/ProgramLetterPage/ProgramLetterPage.tsx similarity index 100% rename from frontends/main/src/app-pages/ProgramLetterPage/[id]/view/ProgramLetterPage.tsx rename to frontends/main/src/app-pages/ProgramLetterPage/ProgramLetterPage.tsx diff --git a/frontends/main/src/app-pages/TermsPage/TermsPage.tsx b/frontends/main/src/app-pages/TermsPage/TermsPage.tsx index 73e2bc2b21..d44e161d91 100644 --- a/frontends/main/src/app-pages/TermsPage/TermsPage.tsx +++ b/frontends/main/src/app-pages/TermsPage/TermsPage.tsx @@ -1,7 +1,7 @@ "use client" import React from "react" -// Not urrently linked to. See https://github.com/mitodl/hq/issues/4639 +// Not currently linked to. See https://github.com/mitodl/hq/issues/4639 import { Breadcrumbs, Container, diff --git a/frontends/main/src/app-pages/TopicsListingPage/TopicsListingPage.tsx b/frontends/main/src/app-pages/TopicsListingPage/TopicsListingPage.tsx index 922cb42f3f..3177059a4f 100644 --- a/frontends/main/src/app-pages/TopicsListingPage/TopicsListingPage.tsx +++ b/frontends/main/src/app-pages/TopicsListingPage/TopicsListingPage.tsx @@ -14,14 +14,14 @@ import { Breadcrumbs, } from "ol-components" import Link from "next/link" -import { propsNotNil } from "ol-utilities" - +import { propsNotNil, backgroundSrcSetCSS } from "ol-utilities" import { useLearningResourceTopics } from "api/hooks/learningResources" import { LearningResourceTopic } from "api" import RootTopicIcon from "@/components/RootTopicIcon/RootTopicIcon" import { HOME } from "@/common/urls" import { aggregateProgramCounts, aggregateCourseCounts } from "@/common/utils" import { useChannelCounts } from "api/hooks/channels" +import backgroundSteps from "@/public/images/backgrounds/background_steps.jpg" type ChannelSummary = { id: number | string @@ -275,6 +275,7 @@ const TopicsListingPage: React.FC = () => { } title="Browse by Topic" header="Select a topic below to explore relevant learning resources across all Academic and Professional units." + backgroundUrl={backgroundSrcSetCSS(backgroundSteps)} /> diff --git a/frontends/main/src/app-pages/UnitsListingPage/UnitCard.tsx b/frontends/main/src/app-pages/UnitsListingPage/UnitCard.tsx index 224323731c..39e5e5ee2a 100644 --- a/frontends/main/src/app-pages/UnitsListingPage/UnitCard.tsx +++ b/frontends/main/src/app-pages/UnitsListingPage/UnitCard.tsx @@ -1,6 +1,13 @@ import React from "react" import { LearningResourceOfferorDetail, OfferedByEnum } from "api" -import { Card, Skeleton, Typography, styled, theme } from "ol-components" +import { + Card, + Skeleton, + Typography, + styled, + theme, + UnitLogo, +} from "ol-components" import { useChannelDetail } from "api/hooks/channels" const CardStyled = styled(Card)({ @@ -34,14 +41,12 @@ const LogoContainer = styled.div({ margin: "0 auto", }, }, -}) - -const UnitLogo = styled.img({ - height: "50px", - display: "block", - [theme.breakpoints.down("md")]: { - height: "40px", - margin: "0 auto", + img: { + display: "block", + [theme.breakpoints.down("md")]: { + height: "40px", + margin: "0 auto", + }, }, }) @@ -108,15 +113,6 @@ const CountsText = styled(Typography)(({ theme }) => ({ }, })) -const unitLogos = { - [OfferedByEnum.Mitx]: "/images/unit_logos/mitx.svg", - [OfferedByEnum.Ocw]: "/images/unit_logos/ocw.svg", - [OfferedByEnum.Bootcamps]: "/images/unit_logos/bootcamps.svg", - [OfferedByEnum.Xpro]: "/images/unit_logos/xpro.svg", - [OfferedByEnum.Mitpe]: "/images/unit_logos/mitpe.svg", - [OfferedByEnum.See]: "/images/unit_logos/see.svg", -} - interface UnitCardsProps { units: LearningResourceOfferorDetail[] | undefined courseCounts: Record @@ -125,13 +121,12 @@ interface UnitCardsProps { interface UnitCardProps { unit: LearningResourceOfferorDetail - logo: string courseCount: number programCount: number } const UnitCard: React.FC = (props) => { - const { unit, logo, courseCount, programCount } = props + const { unit, courseCount, programCount } = props const channelDetailQuery = useChannelDetail("unit", unit.code) const channelDetail = channelDetailQuery.data const unitUrl = channelDetail?.channel_url @@ -144,7 +139,7 @@ const UnitCard: React.FC = (props) => { - + @@ -197,14 +192,10 @@ export const UnitCards: React.FC = (props) => { {units?.map((unit) => { const courseCount = courseCounts[unit.code] || 0 const programCount = programCounts[unit.code] || 0 - const logo = - unitLogos[unit.code as OfferedByEnum] || - `/images/unit_logos/${unit.code}.svg` return unit.value_prop ? ( diff --git a/frontends/main/src/app-pages/UnitsListingPage/UnitsListingPage.tsx b/frontends/main/src/app-pages/UnitsListingPage/UnitsListingPage.tsx index 574ccb3b42..112de6ea7e 100644 --- a/frontends/main/src/app-pages/UnitsListingPage/UnitsListingPage.tsx +++ b/frontends/main/src/app-pages/UnitsListingPage/UnitsListingPage.tsx @@ -11,7 +11,8 @@ import { theme, Breadcrumbs, } from "ol-components" - +import { backgroundSrcSetCSS } from "ol-utilities" +import backgroundSteps from "@/public/images/backgrounds/background_steps.jpg" import { RiBookOpenLine, RiSuitcaseLine } from "@remixicon/react" import { LearningResourceOfferorDetail } from "api" import { HOME } from "@/common/urls" @@ -248,6 +249,7 @@ const UnitsListingPage: React.FC = () => { } title="Academic & Professional Learning" header="Non-degree learning resources tailored to the needs of students and working professionals." + backgroundUrl={backgroundSrcSetCSS(backgroundSteps)} /> diff --git a/frontends/main/src/app/program_letter/[id]/view/page.tsx b/frontends/main/src/app/program_letter/[id]/view/page.tsx index faa058688c..849a6050e5 100644 --- a/frontends/main/src/app/program_letter/[id]/view/page.tsx +++ b/frontends/main/src/app/program_letter/[id]/view/page.tsx @@ -1,5 +1,5 @@ import React from "react" -import ProgramLetterPage from "@/app-pages/ProgramLetterPage/[id]/view/ProgramLetterPage" +import ProgramLetterPage from "@/app-pages/ProgramLetterPage/ProgramLetterPage" const Page: React.FC = () => { return diff --git a/frontends/main/src/page-components/Header/Header.tsx b/frontends/main/src/page-components/Header/Header.tsx index e5cb4b7584..93ef5bcdb9 100644 --- a/frontends/main/src/page-components/Header/Header.tsx +++ b/frontends/main/src/page-components/Header/Header.tsx @@ -295,7 +295,7 @@ const Header: FunctionComponent = () => { desktopTrigger.current, mobileTrigger.current, ]} - navdata={navData} + navData={navData} open={drawerOpen} onClose={toggleDrawer.off} /> diff --git a/frontends/ol-components/src/components/Banner/Banner.tsx b/frontends/ol-components/src/components/Banner/Banner.tsx index a908fb5a74..22dd0bdaa6 100644 --- a/frontends/ol-components/src/components/Banner/Banner.tsx +++ b/frontends/ol-components/src/components/Banner/Banner.tsx @@ -27,26 +27,32 @@ const BannerBackground = styled.div( backgroundUrl = DEFAULT_BACKGROUND_IMAGE_URL, backgroundSize = "cover", backgroundDim = 0, - }) => ({ - backgroundAttachment: "fixed", - backgroundImage: backgroundDim - ? `linear-gradient(rgba(0 0 0 / ${backgroundDim}%), rgba(0 0 0 / ${backgroundDim}%)), url('${backgroundUrl}')` - : `url(${backgroundUrl})`, - backgroundSize: backgroundSize, - backgroundPosition: "center top", - backgroundRepeat: "no-repeat", - color: theme.custom.colors.white, - padding: "48px 0 48px 0", - [theme.breakpoints.up("lg")]: { - backgroundSize: - backgroundUrl === DEFAULT_BACKGROUND_IMAGE_URL - ? "140%" - : backgroundSize, - }, - [theme.breakpoints.down("sm")]: { - padding: "32px 0 32px 0", - }, - }), + }) => { + const backgroundUrlFn = backgroundUrl.startsWith("image-set(") + ? backgroundUrl + : `url('${backgroundUrl}')` + + return { + backgroundAttachment: "fixed", + backgroundImage: backgroundDim + ? `linear-gradient(rgba(0 0 0 / ${backgroundDim}%), rgba(0 0 0 / ${backgroundDim}%)), ${backgroundUrlFn}` + : backgroundUrlFn, + backgroundSize: backgroundSize, + backgroundPosition: "center top", + backgroundRepeat: "no-repeat", + color: theme.custom.colors.white, + padding: "48px 0 48px 0", + [theme.breakpoints.up("lg")]: { + backgroundSize: + backgroundUrl === DEFAULT_BACKGROUND_IMAGE_URL + ? "140%" + : backgroundSize, + }, + [theme.breakpoints.down("sm")]: { + padding: "32px 0 32px 0", + }, + } + }, ) const InnerContainer = styled.div(({ theme }) => ({ diff --git a/frontends/ol-components/src/components/EmbedlyCard/EmbedlyCard.tsx b/frontends/ol-components/src/components/EmbedlyCard/EmbedlyCard.tsx index 94c36354b7..6064544f4c 100644 --- a/frontends/ol-components/src/components/EmbedlyCard/EmbedlyCard.tsx +++ b/frontends/ol-components/src/components/EmbedlyCard/EmbedlyCard.tsx @@ -6,9 +6,10 @@ import { embedlyCardHtml, EmbedlyEventTypes, ensureEmbedlyPlatform, - getEmbedlyKey, } from "./util" +const EMBEDLY_KEY = process.env.NEXT_PUBLIC_EMBEDLY_KEY as string + type EmbedlyCardProps = { url: string className?: string @@ -51,7 +52,6 @@ const Container = styled.div<{ aspectRatio?: number }>` const EmbedlyCard: React.FC = ({ className, url, - embedlyKey, aspectRatio, }) => { const [container, setContainer] = useState(null) @@ -85,12 +85,12 @@ const EmbedlyCard: React.FC = ({ const a = document.createElement("a") a.dataset.cardChrome = "0" a.dataset.cardControls = "0" - a.dataset.cardKey = embedlyKey ?? getEmbedlyKey() ?? "" + a.dataset.cardKey = EMBEDLY_KEY a.href = url a.classList.add("embedly-card") a.dataset["testid"] = "embedly-card" container.appendChild(a) - }, [embedlyKey, container, url]) + }, [container, url]) return ( { head.appendChild(style) } -const getEmbedlyKey = (): string | null => { - const key = process.env.NEXT_PUBLIC_EMBEDLY_KEY - if (typeof key === "string") return key - console.warn("process.env.NEXT_PUBLIC_EMBEDLY_KEY should be a string.") - return null -} - const embedlyCardHtml = (url: string) => { - const embedlyKey = getEmbedlyKey() return `` } @@ -101,7 +95,6 @@ const embedlyCardHtml = (url: string) => { export { createStylesheet, ensureEmbedlyPlatform, - getEmbedlyKey, EmbedlyEventTypes, dispatchCardCreated, embedlyCardHtml, diff --git a/frontends/ol-components/src/components/LearningResourceCard/LearningResourceListCard.tsx b/frontends/ol-components/src/components/LearningResourceCard/LearningResourceListCard.tsx index 1757cffa63..d80859d49a 100644 --- a/frontends/ol-components/src/components/LearningResourceCard/LearningResourceListCard.tsx +++ b/frontends/ol-components/src/components/LearningResourceCard/LearningResourceListCard.tsx @@ -11,7 +11,6 @@ import { ResourceTypeEnum, LearningResource } from "api" import { formatDate, getReadableResourceType, - // embedlyCroppedImage, DEFAULT_RESOURCE_IMG, pluralize, getLearningResourcePrices, @@ -106,14 +105,6 @@ type ResourceIdCallback = ( resourceId: number, ) => void -// TODO confirm use of Next.js image optimizer in place of Embedly -// const getEmbedlyUrl = (url: string, isMobile: boolean) => { -// return embedlyCroppedImage(url, { -// key: process.env.NEXT_PUBLIC_EMBEDLY_KEY!, -// ...IMAGE_SIZES[isMobile ? "mobile" : "desktop"], -// }) -// } - /* This displays a single price for courses with no free option * (price includes the certificate). For free courses with the * option of a paid certificate, the certificate price displayed diff --git a/frontends/ol-components/src/components/LearningResourceExpanded/LearningResourceExpanded.stories.tsx b/frontends/ol-components/src/components/LearningResourceExpanded/LearningResourceExpanded.stories.tsx index c6af08ffb4..27d7e2524c 100644 --- a/frontends/ol-components/src/components/LearningResourceExpanded/LearningResourceExpanded.stories.tsx +++ b/frontends/ol-components/src/components/LearningResourceExpanded/LearningResourceExpanded.stories.tsx @@ -21,7 +21,6 @@ const meta: Meta = { component: LearningResourceExpandedV1, args: { imgConfig: { - key: "", width: 385, height: 200, }, diff --git a/frontends/ol-components/src/components/LearningResourceExpanded/LearningResourceExpandedV1.test.tsx b/frontends/ol-components/src/components/LearningResourceExpanded/LearningResourceExpandedV1.test.tsx index 1e38706ff4..8a1a54df9d 100644 --- a/frontends/ol-components/src/components/LearningResourceExpanded/LearningResourceExpandedV1.test.tsx +++ b/frontends/ol-components/src/components/LearningResourceExpanded/LearningResourceExpandedV1.test.tsx @@ -11,10 +11,9 @@ import { ThemeProvider } from "../ThemeProvider/ThemeProvider" import invariant from "tiny-invariant" import type { LearningResource } from "api" import { faker } from "@faker-js/faker/locale/en" -import { PLATFORMS } from "../Logo/Logo" +import { PLATFORM_LOGOS } from "../Logo/Logo" const IMG_CONFIG: LearningResourceExpandedV1Props["imgConfig"] = { - key: "fake-key", width: 385, height: 200, } @@ -151,7 +150,7 @@ describe("Learning Resource Expanded", () => { .find((img) => img.getAttribute("alt")?.includes("xPRO")) expect(xproImage).toBeInTheDocument() - expect(xproImage).toHaveAttribute("alt", PLATFORMS["xpro"].name) + expect(xproImage).toHaveAttribute("alt", PLATFORM_LOGOS["xpro"].name) }, ) diff --git a/frontends/ol-components/src/components/LearningResourceExpanded/LearningResourceExpandedV1.tsx b/frontends/ol-components/src/components/LearningResourceExpanded/LearningResourceExpandedV1.tsx index 5af9ac6066..a0155829db 100644 --- a/frontends/ol-components/src/components/LearningResourceExpanded/LearningResourceExpandedV1.tsx +++ b/frontends/ol-components/src/components/LearningResourceExpanded/LearningResourceExpandedV1.tsx @@ -2,26 +2,26 @@ import React, { useEffect, useState } from "react" import styled from "@emotion/styled" import Skeleton from "@mui/material/Skeleton" import Typography from "@mui/material/Typography" +import { default as NextImage } from "next/image" import { ButtonLink } from "../Button/Button" import type { LearningResource, LearningResourceRun } from "api" import { ResourceTypeEnum, PlatformEnum } from "api" import { formatDate, capitalize, - resourceThumbnailSrc, DEFAULT_RESOURCE_IMG, showStartAnytime, } from "ol-utilities" import { RiExternalLinkLine } from "@remixicon/react" -import type { EmbedlyConfig } from "ol-utilities" import { theme } from "../ThemeProvider/ThemeProvider" import { SimpleSelect } from "../SimpleSelect/SimpleSelect" import type { SimpleSelectProps } from "../SimpleSelect/SimpleSelect" -import { EmbedlyCard } from "../EmbedlyCard/EmbedlyCard" -import { PlatformLogo, PLATFORMS } from "../Logo/Logo" +import { PlatformLogo, PLATFORM_LOGOS } from "../Logo/Logo" import InfoSectionV1 from "./InfoSectionV1" import type { User } from "api/hooks/user" import { LearningResourceCardProps } from "../LearningResourceCard/LearningResourceCard" +import type { ImageConfig } from "../../constants/imgConfigs" +import VideoFrame from "./VideoFrame" const Container = styled.div<{ padTop?: boolean }>` display: flex; @@ -76,8 +76,13 @@ const DateLabel = styled.span` margin-right: 16px; ` -const Image = styled.img<{ aspect: number }>` - aspect-ratio: ${({ aspect }) => aspect}; +const ImageContainer = styled.div<{ aspect: number }>` + position: relative; + width: 100%; + padding-bottom: ${({ aspect }) => 100 / aspect}%; +` + +const Image = styled(NextImage)` border-radius: 8px; width: 100%; object-fit: cover; @@ -141,38 +146,39 @@ const OnPlatform = styled.span` type LearningResourceExpandedV1Props = { resource?: LearningResource user?: User - imgConfig: EmbedlyConfig + imgConfig: ImageConfig onAddToLearningPathClick?: LearningResourceCardProps["onAddToLearningPathClick"] onAddToUserListClick?: LearningResourceCardProps["onAddToUserListClick"] } const ImageSection: React.FC<{ resource?: LearningResource - config: EmbedlyConfig + config: ImageConfig }> = ({ resource, config }) => { + const aspect = config.width / config.height if (resource?.resource_type === "video" && resource?.url) { return ( - + ) } else if (resource?.image) { return ( - {resource?.image.alt + + {resource?.image.alt + ) } else if (resource) { return ( - {resource.image?.alt + + {resource.image?.alt + ) } else { return ( @@ -218,7 +224,7 @@ const CallToActionSection = ({ (offeredBy?.code as PlatformEnum) === PlatformEnum.Xpro ? (offeredBy?.code as PlatformEnum) : (platform?.code as PlatformEnum) - const platformImage = PLATFORMS[platformCode]?.image + const platformImage = PLATFORM_LOGOS[platformCode]?.image const getCallToActionText = (resource: LearningResource): string => { if (resource?.platform?.code === PlatformEnum.Ocw) { diff --git a/frontends/ol-components/src/components/LearningResourceExpanded/LearningResourceExpandedV2.test.tsx b/frontends/ol-components/src/components/LearningResourceExpanded/LearningResourceExpandedV2.test.tsx index 1d40a84d27..0f242e754e 100644 --- a/frontends/ol-components/src/components/LearningResourceExpanded/LearningResourceExpandedV2.test.tsx +++ b/frontends/ol-components/src/components/LearningResourceExpanded/LearningResourceExpandedV2.test.tsx @@ -12,11 +12,10 @@ import { factories } from "api/test-utils" import { ThemeProvider } from "../ThemeProvider/ThemeProvider" import invariant from "tiny-invariant" import type { LearningResource } from "api" -import { PLATFORMS } from "../Logo/Logo" +import { PLATFORM_LOGOS } from "../Logo/Logo" import _ from "lodash" const IMG_CONFIG: LearningResourceExpandedV2Props["imgConfig"] = { - key: "fake-key", width: 385, height: 200, } @@ -152,7 +151,7 @@ describe("Learning Resource Expanded", () => { .find((img) => img.getAttribute("alt")?.includes("xPRO")) expect(xproImage).toBeInTheDocument() - expect(xproImage).toHaveAttribute("alt", PLATFORMS["xpro"].name) + expect(xproImage).toHaveAttribute("alt", PLATFORM_LOGOS["xpro"].name) }, ) diff --git a/frontends/ol-components/src/components/LearningResourceExpanded/LearningResourceExpandedV2.tsx b/frontends/ol-components/src/components/LearningResourceExpanded/LearningResourceExpandedV2.tsx index 5b3506d9a8..d273f2f9cb 100644 --- a/frontends/ol-components/src/components/LearningResourceExpanded/LearningResourceExpandedV2.tsx +++ b/frontends/ol-components/src/components/LearningResourceExpanded/LearningResourceExpandedV2.tsx @@ -2,28 +2,25 @@ import React from "react" import styled from "@emotion/styled" import Skeleton from "@mui/material/Skeleton" import Typography from "@mui/material/Typography" +import { default as NextImage } from "next/image" import { ActionButton, ButtonLink } from "../Button/Button" import type { LearningResource } from "api" import { ResourceTypeEnum, PlatformEnum } from "api" -import { - resourceThumbnailSrc, - DEFAULT_RESOURCE_IMG, - getReadableResourceType, -} from "ol-utilities" +import { DEFAULT_RESOURCE_IMG, getReadableResourceType } from "ol-utilities" import { RiBookmarkLine, RiCloseLargeLine, RiExternalLinkLine, RiMenuAddLine, } from "@remixicon/react" -import type { EmbedlyConfig } from "ol-utilities" +import type { ImageConfig } from "../../constants/imgConfigs" import { theme } from "../ThemeProvider/ThemeProvider" -import { EmbedlyCard } from "../EmbedlyCard/EmbedlyCard" -import { PlatformLogo, PLATFORMS } from "../Logo/Logo" +import { PlatformLogo, PLATFORM_LOGOS } from "../Logo/Logo" import InfoSectionV2 from "./InfoSectionV2" import type { User } from "api/hooks/user" import { LearningResourceCardProps } from "../LearningResourceCard/LearningResourceCard" import { CardActionButton } from "../LearningResourceCard/LearningResourceListCard" +import VideoFrame from "./VideoFrame" const Container = styled.div({ display: "flex", @@ -81,17 +78,17 @@ const RightContainer = styled.div({ }, }) -const EmbedlyContainer = styled.div({ - width: "100%", - overflow: "hidden", -}) +const ImageContainer = styled.div<{ aspect: number }>` + position: relative; + width: 100%; + padding-bottom: ${({ aspect }) => 100 / aspect}%; +` -const Image = styled.img<{ aspect: number }>((aspect) => ({ - aspectRatio: aspect.aspect, +const Image = styled(NextImage)({ borderRadius: "8px", width: "100%", objectFit: "cover", -})) +}) const SkeletonImage = styled(Skeleton)<{ aspect: number }>((aspect) => ({ borderRadius: "8px", @@ -168,7 +165,7 @@ const ListButtonContainer = styled.div({ type LearningResourceExpandedV2Props = { resource?: LearningResource user?: User - imgConfig: EmbedlyConfig + imgConfig: ImageConfig onAddToLearningPathClick?: LearningResourceCardProps["onAddToLearningPathClick"] onAddToUserListClick?: LearningResourceCardProps["onAddToUserListClick"] closeDrawer?: () => void @@ -237,33 +234,32 @@ const TitleSection: React.FC<{ const ImageSection: React.FC<{ resource?: LearningResource - config: EmbedlyConfig + config: ImageConfig }> = ({ resource, config }) => { + const aspect = config.width / config.height if (resource?.resource_type === "video" && resource?.url) { return ( - - - + ) } else if (resource?.image) { return ( - {resource?.image.alt + + {resource?.image.alt + ) } else if (resource) { return ( - {resource.image?.alt + + {resource.image?.alt + ) } else { return ( @@ -323,7 +319,7 @@ const CallToActionSection = ({ onAddToLearningPathClick, onAddToUserListClick, }: { - imgConfig: EmbedlyConfig + imgConfig: ImageConfig resource?: LearningResource hide?: boolean user?: User @@ -350,7 +346,7 @@ const CallToActionSection = ({ (offeredBy?.code as PlatformEnum) === PlatformEnum.Xpro ? (offeredBy?.code as PlatformEnum) : (platform?.code as PlatformEnum) - const platformImage = PLATFORMS[platformCode]?.image + const platformImage = PLATFORM_LOGOS[platformCode]?.image const cta = getCallToActionText(resource) return ( diff --git a/frontends/ol-components/src/components/LearningResourceExpanded/VideoFrame.tsx b/frontends/ol-components/src/components/LearningResourceExpanded/VideoFrame.tsx new file mode 100644 index 0000000000..3f76c24609 --- /dev/null +++ b/frontends/ol-components/src/components/LearningResourceExpanded/VideoFrame.tsx @@ -0,0 +1,32 @@ +import React from "react" +import styled from "@emotion/styled" +import { EmbedlyCard } from "../EmbedlyCard/EmbedlyCard" + +const IFrame = styled.iframe` + border-radius: 8px; + border: none; + width: 100%; + aspect-ratio: 16 / 9; +` + +const VideoFrame: React.FC<{ + src: string + title: string + aspect: number +}> = ({ src, title, aspect }) => { + if (src?.startsWith("https://www.youtube.com/watch?v=")) { + const videoId = src?.split("v=")[1] + return ( +