diff --git a/frontends/main/src/app-pages/HomePage/NewsEventsSection.tsx b/frontends/main/src/app-pages/HomePage/NewsEventsSection.tsx index 2b9b37abbb..a29ae1e828 100644 --- a/frontends/main/src/app-pages/HomePage/NewsEventsSection.tsx +++ b/frontends/main/src/app-pages/HomePage/NewsEventsSection.tsx @@ -192,7 +192,7 @@ const Story: React.FC<{ item: NewsFeedItem; mobile: boolean }> = ({ {item.image.url ? ( ) : null} - + {item.title} diff --git a/frontends/ol-components/src/components/Card/Card.tsx b/frontends/ol-components/src/components/Card/Card.tsx index c2b6876f83..e3b276a3ea 100644 --- a/frontends/ol-components/src/components/Card/Card.tsx +++ b/frontends/ol-components/src/components/Card/Card.tsx @@ -103,14 +103,13 @@ const Info = styled.div<{ size?: Size }>` ` const titleOpts = { - shouldForwardProp: (prop: string) => prop !== "lines" && prop !== "size", + shouldForwardProp: (prop: string) => prop !== "size", } -const Title = styled(Linkable, titleOpts)<{ lines?: number; size?: Size }>` +const Title = styled(Linkable, titleOpts)<{ size?: Size }>` text-overflow: ellipsis; - height: ${({ lines, size }) => { + height: ${({ size }) => { const lineHeightPx = size === "small" ? 18 : 20 - lines = lines ?? (size === "small" ? 2 : 3) - return theme.typography.pxToRem(lines * lineHeightPx) + return theme.typography.pxToRem(3 * lineHeightPx) }}; overflow: hidden; margin: 0; @@ -120,16 +119,16 @@ const Title = styled(Linkable, titleOpts)<{ lines?: number; size?: Size }>` ? { ...theme.typography.subtitle2 } : { ...theme.typography.subtitle1 }} - ${({ lines, size }) => { - lines = lines ?? (size === "small" ? 2 : 3) - return ` - @supports (-webkit-line-clamp: ${lines}) { - white-space: initial; - display: -webkit-box; - -webkit-line-clamp: ${lines}; - -webkit-box-orient: vertical; - }` - }} + @supports (-webkit-line-clamp: 3) { + white-space: initial; + display: -webkit-box; + -webkit-box-orient: vertical; + -webkit-line-clamp: 3; + + > * { + -webkit-line-clamp: 3; + } + } ` const Footer = styled.span` @@ -225,7 +224,6 @@ export type ImageProps = NextImageProps & { type TitleProps = { children?: ReactNode href?: string - lines?: number style?: CSSProperties } diff --git a/frontends/ol-components/src/components/LearningResourceExpanded/DifferingRunsTable.tsx b/frontends/ol-components/src/components/LearningResourceExpanded/DifferingRunsTable.tsx index ea1663f41c..9d132a9c2e 100644 --- a/frontends/ol-components/src/components/LearningResourceExpanded/DifferingRunsTable.tsx +++ b/frontends/ol-components/src/components/LearningResourceExpanded/DifferingRunsTable.tsx @@ -25,7 +25,7 @@ const DifferingRun = styled.div({ display: "flex", flexWrap: "wrap", alignItems: "center", - gap: "16px", + gap: "8px", padding: "12px", alignSelf: "stretch", borderBottom: `1px solid ${theme.custom.colors.lightGray2}`, @@ -35,7 +35,7 @@ const DifferingRunHeader = styled.div({ display: "flex", alignSelf: "stretch", alignItems: "center", - gap: "16px", + gap: "8px", padding: "12px", color: theme.custom.colors.darkGray2, backgroundColor: theme.custom.colors.lightGray1, @@ -120,7 +120,7 @@ const DifferingRunsTable: React.FC<{ resource: LearningResource }> = ({ {run.delivery.filter((d) => d.code === "in_person").length > 0 && run.location && ( - Location + Location: {run.location} )} diff --git a/frontends/ol-components/src/components/LearningResourceExpanded/LearningResourceExpandedV2.tsx b/frontends/ol-components/src/components/LearningResourceExpanded/LearningResourceExpandedV2.tsx index e51548cb45..9f580e9aaf 100644 --- a/frontends/ol-components/src/components/LearningResourceExpanded/LearningResourceExpandedV2.tsx +++ b/frontends/ol-components/src/components/LearningResourceExpanded/LearningResourceExpandedV2.tsx @@ -1,4 +1,4 @@ -import React from "react" +import React, { useCallback, useRef, useState } from "react" import styled from "@emotion/styled" import Skeleton from "@mui/material/Skeleton" import Typography from "@mui/material/Typography" @@ -21,6 +21,7 @@ import type { User } from "api/hooks/user" import { LearningResourceCardProps } from "../LearningResourceCard/LearningResourceCard" import { CardActionButton } from "../LearningResourceCard/LearningResourceListCard" import VideoFrame from "./VideoFrame" +import { Link } from "../Link/Link" const DRAWER_WIDTH = "900px" @@ -148,12 +149,39 @@ const Platform = styled.div({ gap: "16px", }) +const DescriptionContainer = styled.div({ + display: "flex", + flexDirection: "column", + gap: "4px", + width: "100%", +}) + const Description = styled.p({ ...theme.typography.body2, color: theme.custom.colors.black, margin: 0, whiteSpace: "pre-wrap", wordBreak: "break-word", + "p:first-child": { + marginTop: 0, + }, + "p:last-child": { + marginBottom: 0, + }, +}) + +const DescriptionCollapsed = styled(Description)({ + display: "-webkit-box", + overflow: "hidden", + height: `calc(${theme.typography.body2.lineHeight} * 5)`, + "@supports (-webkit-line-clamp: 5)": { + WebkitLineClamp: 5, + WebkitBoxOrient: "vertical", + }, +}) + +const DescriptionExpanded = styled(Description)({ + display: "block", }) const StyledPlatformLogo = styled(PlatformLogo)({ @@ -439,6 +467,24 @@ const CallToActionSection = ({ } const ResourceDescription = ({ resource }: { resource?: LearningResource }) => { + const firstRender = useRef(true) + const clampedOnFirstRender = useRef(false) + const [isClamped, setClamped] = useState(false) + const [isExpanded, setExpanded] = useState(false) + const descriptionRendered = useCallback((node: HTMLDivElement) => { + if (node !== null) { + const clamped = node.scrollHeight > node.clientHeight + setClamped(clamped) + if (firstRender.current) { + firstRender.current = false + clampedOnFirstRender.current = clamped + return + } + } + }, []) + const DescriptionText = isExpanded + ? DescriptionExpanded + : DescriptionCollapsed if (!resource) { return ( <> @@ -452,13 +498,22 @@ const ResourceDescription = ({ resource }: { resource?: LearningResource }) => { ) } return ( - + + + {(isClamped || clampedOnFirstRender.current) && ( + setExpanded(!isExpanded)}> + {isExpanded ? "Show less" : "Show more"} + + )} + ) }