diff --git a/frontends/main/src/app-pages/HomePage/NewsEventsSection.tsx b/frontends/main/src/app-pages/HomePage/NewsEventsSection.tsx index be87d1d47d..bcf7d7227f 100644 --- a/frontends/main/src/app-pages/HomePage/NewsEventsSection.tsx +++ b/frontends/main/src/app-pages/HomePage/NewsEventsSection.tsx @@ -188,11 +188,11 @@ const Story: React.FC<{ item: NewsFeedItem; mobile: boolean }> = ({ mobile, }) => { return ( - + {item.image.url ? ( ) : null} - + {item.title} @@ -222,7 +222,7 @@ const NewsEventsSection: React.FC = () => { const stories = news.results.slice(0, 6) const EventCards = events.results.map((item) => ( - + @@ -238,7 +238,7 @@ const NewsEventsSection: React.FC = () => { )} - + {item.title} diff --git a/frontends/main/src/app-pages/UnitsListingPage/UnitCard.tsx b/frontends/main/src/app-pages/UnitsListingPage/UnitCard.tsx index 39e5e5ee2a..9767a828c1 100644 --- a/frontends/main/src/app-pages/UnitsListingPage/UnitCard.tsx +++ b/frontends/main/src/app-pages/UnitsListingPage/UnitCard.tsx @@ -9,6 +9,7 @@ import { UnitLogo, } from "ol-components" import { useChannelDetail } from "api/hooks/channels" +import Link from "next/link" const CardStyled = styled(Card)({ height: "100%", @@ -77,23 +78,10 @@ const LoadingContent = styled.div({ padding: "24px", }) -const HeadingText = styled(Typography)(({ theme }) => ({ +const HeadingText = styled.span(({ theme }) => ({ alignSelf: "stretch", color: theme.custom.colors.darkGray2, ...theme.typography.body2, - [theme.breakpoints.down("md")]: { - display: "none", - }, -})) - -const SubHeadingText = styled(HeadingText)(({ theme }) => ({ - alignSelf: "stretch", - color: theme.custom.colors.darkGray2, - ...theme.typography.body2, - display: "none", - [theme.breakpoints.down("md")]: { - display: "block", - }, })) const CountsTextContainer = styled.div({ @@ -131,24 +119,24 @@ const UnitCard: React.FC = (props) => { const channelDetail = channelDetailQuery.data const unitUrl = channelDetail?.channel_url - return channelDetailQuery.isLoading ? ( - - ) : ( - + if (!unitUrl) return null + const href = unitUrl && new URL(unitUrl).pathname + + return ( + - + + + {channelDetail?.configuration?.heading} - - {channelDetail?.configuration?.sub_heading} - @@ -192,6 +180,7 @@ export const UnitCards: React.FC = (props) => { {units?.map((unit) => { const courseCount = courseCounts[unit.code] || 0 const programCount = programCounts[unit.code] || 0 + return unit.value_prop ? ( , -): LearningResourcesSearchResponse => { - return { - metadata: { - suggestions: [], - aggregations: { - topic: Object.entries(aggregations).map(([key, docCount]) => ({ - key, - doc_count: docCount, - })), - }, - }, - count: 0, - results: [], - next: null, - previous: null, - } -} - describe("DepartmentListingPage", () => { const setupApis = () => { const make = factories.learningResources @@ -59,12 +38,6 @@ describe("DepartmentListingPage", () => { value_prop: "Professional Unit 2 value prop", professional: true, }) - const professionalUnit3 = make.offeror({ - code: "professionalUnit3", - name: "Professional Unit 3", - value_prop: "Professional Unit 3 value prop", - professional: true, - }) const units = [ academicUnit1, @@ -72,54 +45,42 @@ describe("DepartmentListingPage", () => { academicUnit3, professionalUnit1, professionalUnit2, - professionalUnit3, ] - const courseCounts = { + const courseCounts: Record = { academicUnit1: 10, academicUnit2: 20, academicUnit3: 1, professionalUnit1: 40, - professionalUnit2: 50, - professionalUnit3: 0, + professionalUnit2: 0, } - const programCounts = { + const programCounts: Record = { academicUnit1: 1, academicUnit2: 2, academicUnit3: 0, professionalUnit1: 4, professionalUnit2: 5, - professionalUnit3: 6, } - setMockResponse.get(urls.channels.counts("unit"), [ - { - name: academicUnit1, - counts: { - programs: 7, - courses: 10, - }, - }, - ]) - setMockResponse.get(urls.offerors.list(), units) - setMockResponse.get( - urls.search.resources({ - resource_type: ["course"], - aggregations: ["offered_by"], - }), - makeSearchResponse(courseCounts), - ) - setMockResponse.get( - urls.search.resources({ - resource_type: ["program"], - aggregations: ["offered_by"], + urls.channels.counts("unit"), + units.map((unit) => { + return { + name: unit.code, + counts: { + courses: courseCounts[unit.code], + programs: programCounts[unit.code], + }, + } }), - makeSearchResponse(programCounts), ) + setMockResponse.get(urls.offerors.list(), { + count: units.length, + results: units, + }) units.forEach((unit) => { setMockResponse.get(urls.channels.details("unit", unit.code), { - channel_url: `/units/${unit.code}`, + channel_url: `${window.location.origin}/units/${unit.code}`, }) }) @@ -138,49 +99,43 @@ describe("DepartmentListingPage", () => { it("Shows unit properties within the proper section", async () => { const { units, courseCounts, programCounts } = setupApis() + renderWithProviders() + + const academicSection = screen.getByTestId("UnitSection-academic") + const professionalSection = screen.getByTestId("UnitSection-professional") + await waitFor(() => { - const academicSection = screen.getByTestId("UnitSection-academic") - const professionalSection = screen.getByTestId("UnitSection-professional") - units.forEach(async (unit) => { - const section = unit.professional - ? professionalSection - : academicSection - const channelLink = await within(section).findByRole("link", { - name: unit.name, - }) - const logoImage = await within(section).findByAltText(unit.name) - const valuePropText = await within(section).findByText( - unit.value_prop ? unit.value_prop : "", - ) - expect(channelLink).toHaveAttribute("href", `/units/${unit.code}`) - expect(logoImage).toHaveAttribute( - "src", - `/images/units/${unit.code}.svg`, - ) - expect(valuePropText).toBeInTheDocument() - const courseCount = courseCounts[unit.code as keyof typeof courseCounts] - const programCount = - programCounts[unit.code as keyof typeof programCounts] - const courseCountText = await within(section).findByTestId( - `course-count-${unit.code}`, - ) - const programCountText = await within(section).findByTestId( - `program-count-${unit.code}`, - ) - if (courseCount > 0) { - expect(courseCountText).toHaveTextContent(`Courses: ${courseCount}`) - } else { - expect(courseCountText).toHaveTextContent("") - } - if (programCount > 0) { - expect(programCountText).toHaveTextContent( - `Programs: ${programCount}`, - ) - } else { - expect(programCountText).toHaveTextContent("") - } - }) + const links = within(academicSection).getAllByRole("link") + expect(links).toHaveLength(3) + return links + }) + await waitFor(() => { + const links = within(professionalSection).getAllByRole("link") + expect(links).toHaveLength(2) + return links + }) + + units.forEach((unit) => { + const section = unit.professional ? professionalSection : academicSection + const card = within(section).getByTestId(`unit-card-${unit.code}`) + const link = within(card).getByRole("link") + expect(link).toHaveAttribute("href", `/units/${unit.code}`) + + const courseCount = courseCounts[unit.code] + const programCount = programCounts[unit.code] + const courseCountEl = within(card).getByTestId( + `course-count-${unit.code}`, + ) + const programCountEl = within(card).getByTestId( + `program-count-${unit.code}`, + ) + expect(courseCountEl).toHaveTextContent( + courseCount > 0 ? `Courses: ${courseCount}` : "", + ) + expect(programCountEl).toHaveTextContent( + programCount > 0 ? `Programs: ${programCount}` : "", + ) }) }) diff --git a/frontends/main/src/app-pages/UnitsListingPage/UnitsListingPage.tsx b/frontends/main/src/app-pages/UnitsListingPage/UnitsListingPage.tsx index 112de6ea7e..905f2edf22 100644 --- a/frontends/main/src/app-pages/UnitsListingPage/UnitsListingPage.tsx +++ b/frontends/main/src/app-pages/UnitsListingPage/UnitsListingPage.tsx @@ -168,6 +168,7 @@ const UnitSection: React.FC = (props) => { programCounts, isLoading, } = props + return (
diff --git a/frontends/main/src/page-components/UserListCard/UserListCardCondensed.test.tsx b/frontends/main/src/page-components/UserListCard/UserListCardCondensed.test.tsx index 562600c4c3..be64937987 100644 --- a/frontends/main/src/page-components/UserListCard/UserListCardCondensed.test.tsx +++ b/frontends/main/src/page-components/UserListCard/UserListCardCondensed.test.tsx @@ -3,7 +3,8 @@ import { screen } from "@testing-library/react" import UserListCardCondensed from "./UserListCardCondensed" import * as factories from "api/test-utils/factories" import { userListView } from "@/common/urls" -import { renderWithProviders } from "@/test-utils" +import { renderWithProviders, user } from "@/test-utils" +import invariant from "tiny-invariant" const userListFactory = factories.userLists @@ -18,4 +19,16 @@ describe("UserListCard", () => { ) screen.getByText(userList.title) }) + + test("Clicking card navigates to href", async () => { + const userList = userListFactory.userList() + renderWithProviders( + , + ) + const link = screen.getByRole("link", { name: userList.title }) + expect(link).toHaveAttribute("href", "#test") + invariant(link.parentElement) + await user.click(link.parentElement) + expect(window.location.hash).toBe("#test") + }) }) diff --git a/frontends/main/src/page-components/UserListCard/UserListCardCondensed.tsx b/frontends/main/src/page-components/UserListCard/UserListCardCondensed.tsx index 976e13e696..fb6514c113 100644 --- a/frontends/main/src/page-components/UserListCard/UserListCardCondensed.tsx +++ b/frontends/main/src/page-components/UserListCard/UserListCardCondensed.tsx @@ -50,10 +50,10 @@ const UserListCardCondensed = ({ className, }: UserListCardCondensedProps) => { return ( - + - + = { +const meta: Meta = { title: "smoot-design/Cards/Card", argTypes: { size: { @@ -19,7 +20,7 @@ const meta: Meta = { alt="Provide a meaningful description or leave this blank." /> Info - + Lorem ipsum dolor sit amet, consectetur adipiscing elit @@ -51,7 +52,7 @@ const meta: Meta = { export default meta -type Story = StoryObj +type Story = StoryObj export const Medium: Story = { args: { diff --git a/frontends/ol-components/src/components/Card/Card.test.tsx b/frontends/ol-components/src/components/Card/Card.test.tsx index d0b9827580..8c1afc94cf 100644 --- a/frontends/ol-components/src/components/Card/Card.test.tsx +++ b/frontends/ol-components/src/components/Card/Card.test.tsx @@ -47,10 +47,9 @@ describe("Card", () => { ])( "The whole card is clickable as a link if forwardClicksToLink ($forwardClicksToLink)", async ({ forwardClicksToLink, finalHref }) => { - const href = "#woof" render( - - Title + + Title Info Footer @@ -75,13 +74,15 @@ describe("Card", () => { const href = "#meow" const onClick = jest.fn() render( - +
Hello!
- Link + + Link +
, { wrapper: ThemeProvider }, @@ -104,8 +105,8 @@ describe("Card", () => { const btnOnClick = jest.fn() const divOnClick = jest.fn() render( - - Title + + Title Info @@ -122,12 +123,13 @@ describe("Card", () => { { wrapper: ThemeProvider }, ) const button = screen.getByRole("button", { name: "Button" }) - const link = screen.getByRole("link", { name: "Link Two" }) + screen.getByRole("link", { name: "Title" }) + const link2 = screen.getByRole("link", { name: "Link Two" }) const div = screen.getByText("Interactive Div") await user.click(button) expect(btnOnClick).toHaveBeenCalled() expect(window.location.hash).toBe("") - await user.click(link) + await user.click(link2) expect(window.location.hash).toBe("#two") await user.click(div) expect(divOnClick).toHaveBeenCalled() diff --git a/frontends/ol-components/src/components/Card/Card.tsx b/frontends/ol-components/src/components/Card/Card.tsx index c4ed39316e..c2b6876f83 100644 --- a/frontends/ol-components/src/components/Card/Card.tsx +++ b/frontends/ol-components/src/components/Card/Card.tsx @@ -28,15 +28,25 @@ export const Linkable: React.FC = ({ href, children, className, + ...others }) => { if (href) { return ( - + {children} ) } - return {children} + return ( + + {children} + + ) } export const BaseContainer = styled.div<{ display?: CSSProperties["display"] }>( @@ -92,7 +102,10 @@ const Info = styled.div<{ size?: Size }>` margin-bottom: ${({ size }) => (size === "small" ? 4 : 8)}px; ` -const Title = styled(Linkable)<{ lines?: number; size?: Size }>` +const titleOpts = { + shouldForwardProp: (prop: string) => prop !== "lines" && prop !== "size", +} +const Title = styled(Linkable, titleOpts)<{ lines?: number; size?: Size }>` text-overflow: ellipsis; height: ${({ lines, size }) => { const lineHeightPx = size === "small" ? 18 : 20 @@ -147,8 +160,7 @@ const Actions = styled.div` * Allows making a whole region clickable as a link, even if the link is not the * direct target of the click event. */ -export const useClickChildHref = ( - href?: string, +export const useClickChildLink = ( onClick?: React.MouseEventHandler, ): React.MouseEventHandler => { return useCallback( @@ -161,7 +173,7 @@ export const useClickChildHref = ( return } const anchor = e.currentTarget.querySelector( - `a[href="${href}"]`, + 'a[data-card-link="true"]', ) const target = e.target as HTMLElement if (!anchor || target.closest("a, button, [data-card-action]")) return @@ -180,7 +192,7 @@ export const useClickChildHref = ( anchor.click() } }, - [href, onClick], + [onClick], ) } @@ -188,19 +200,14 @@ type CardProps = { children: ReactNode[] | ReactNode className?: string size?: Size - /** - * If provided, the card will render its title as a link. - * - * Clicks on the entire card can be forwarded to the link via `forwardClicksToLink`. - */ - href?: string /** * Defaults to `false`. If `true`, clicking the whole card will click the - * href link as well. + * child anchor with data-card-link="true". * * NOTES: + * - By default, Card.Title has `data-card-link="true"`. * - If using Card.Content to customize, you must ensure the content includes - * an anchor with the card's href. + * an anchor with data-card-link attribute. Its value is irrelevant. * - Clicks will NOT be forwarded if it is (or is a child of): * - an anchor or button element * - OR an element with data-card-action @@ -217,16 +224,37 @@ export type ImageProps = NextImageProps & { } type TitleProps = { children?: ReactNode + href?: string lines?: number style?: CSSProperties } type SlotProps = { children?: ReactNode; style?: CSSProperties } +/** + * Card component with slots for image, info, title, footer, and actions: + * ```tsx + * + * + * Info + * Title + * Footer + * Actions + * + * ``` + * + * **Links:** Card.Title will be a link if `href` is supplied; the entire card + * will be clickable if `forwardClicksToLink` is `true`. + * + * **Custom Layout:** Use Card.Content to create a custom layout. + */ type Card = FC & { Content: FC<{ children: ReactNode }> Image: FC Info: FC + /** + * Card title with optional `href`. + */ Title: FC Footer: FC Actions: FC @@ -236,7 +264,6 @@ const Card: Card = ({ children, className, size, - href, onClick, forwardClicksToLink = false, ...others @@ -271,9 +298,8 @@ const Card: Card = ({ else if (child.type === Actions) actions = child.props }) - const hasHref = typeof href === "string" - const handleHrefClick = useClickChildHref(href, onClick) - const handleClick = hasHref && forwardClicksToLink ? handleHrefClick : onClick + const handleHrefClick = useClickChildLink(onClick) + const handleClick = forwardClicksToLink ? handleHrefClick : onClick const allClassNames = ["MitCard-root", className ?? ""].join(" ") @@ -314,7 +340,12 @@ const Card: Card = ({ {info.children} )} - + <Title + data-card-link={!!title.href} + className="MitCard-title" + size={size} + {...title} + > {title.children} @@ -340,3 +371,4 @@ Card.Footer = Footer Card.Actions = Actions export { Card } +export type { CardProps } diff --git a/frontends/ol-components/src/components/Card/ListCard.test.tsx b/frontends/ol-components/src/components/Card/ListCard.test.tsx index bf18d6385a..ecd730ab4d 100644 --- a/frontends/ol-components/src/components/Card/ListCard.test.tsx +++ b/frontends/ol-components/src/components/Card/ListCard.test.tsx @@ -27,8 +27,8 @@ describe("ListCard", () => { async ({ forwardClicksToLink, finalHref }) => { const href = "#woof" render( - - Title + + Title Info Footer Actions @@ -50,14 +50,16 @@ describe("ListCard", () => { ])( "The whole card is clickable as a link when using Content, except buttons and links", async ({ forwardClicksToLink, finalHref }) => { - const href = "#meow" const onClick = jest.fn() + const href = "#meow" render( - +
Hello!
- Link + + Link +
, { wrapper: ThemeProvider }, @@ -80,8 +82,8 @@ describe("ListCard", () => { const btnOnClick = jest.fn() const divOnClick = jest.fn() render( - - Title + + Title Info @@ -97,12 +99,13 @@ describe("ListCard", () => { { wrapper: ThemeProvider }, ) const button = screen.getByRole("button", { name: "Button" }) - const link = screen.getByRole("link", { name: "Link Two" }) + screen.getByRole("link", { name: "Title" }) + const link2 = screen.getByRole("link", { name: "Link Two" }) const div = screen.getByText("Interactive Div") await user.click(button) expect(btnOnClick).toHaveBeenCalled() expect(window.location.hash).toBe("") - await user.click(link) + await user.click(link2) expect(window.location.hash).toBe("#two") await user.click(div) expect(divOnClick).toHaveBeenCalled() diff --git a/frontends/ol-components/src/components/Card/ListCard.tsx b/frontends/ol-components/src/components/Card/ListCard.tsx index 31f44e5079..fbb0c387ed 100644 --- a/frontends/ol-components/src/components/Card/ListCard.tsx +++ b/frontends/ol-components/src/components/Card/ListCard.tsx @@ -8,7 +8,7 @@ import React, { import styled from "@emotion/styled" import { RiDraggable } from "@remixicon/react" import { theme } from "../ThemeProvider/ThemeProvider" -import { BaseContainer, ImageProps, useClickChildHref, Linkable } from "./Card" +import { BaseContainer, ImageProps, useClickChildLink, Linkable } from "./Card" import { TruncateText } from "../TruncateText/TruncateText" import { ActionButton, ActionButtonProps } from "../Button/Button" import { default as NextImage } from "next/image" @@ -83,7 +83,11 @@ export const Info = styled.div` align-items: center; ` -export const Title = styled(Linkable)` +export type TitleProps = { + children?: ReactNode + href?: string +} +export const Title: React.FC = styled(Linkable)` flex-grow: 1; color: ${theme.custom.colors.darkGray2}; text-overflow: ellipsis; @@ -142,19 +146,14 @@ const ListCardActionButton = styled(ActionButton)<{ isMobile?: boolean }>( type CardProps = { children: ReactNode[] | ReactNode className?: string - /** - * If provided, the card will render its title as a link. - * - * Clicks on the entire card can be forwarded to the link via `forwardClicksToLink`. - */ - href?: string /** * Defaults to `false`. If `true`, clicking the whole card will click the - * href link as well. + * child anchor with data-card-link. * * NOTES: + * - By default, Card.Title has `data-card-link="true"`. * - If using Card.Content to customize, you must ensure the content includes - * an anchor with the card's href. + * an anchor with data-card-link attribute. Its value is irrelevant. * - Clicks will NOT be forwarded if: * - The click target is a child of Card.Actions OR an element with * - The click target is a child of any element with data-card-actions attribute @@ -164,14 +163,31 @@ type CardProps = { onClick?: () => void as?: React.ElementType } & AriaAttributes -type TitleProps = { - children?: ReactNode -} +/** + * Row-like card component with slots for image, info, title, footer, and actions: + * ```tsx + * + * + * Info + * Title + * Footer + * Actions + * + * ``` + * + * **Links:** Card.Title will be a link if `href` is supplied; the entire card + * will be clickable if `forwardClicksToLink` is `true`. + * + * **Custom Layout:** Use ListCard.Content to create a custom layout. + */ export type Card = FC & { Content: FC<{ children: ReactNode }> Image: FC Info: FC<{ children: ReactNode }> + /** + * Card title with optional `href`. + */ Title: FC Footer: FC<{ children: ReactNode }> Actions: FC<{ children: ReactNode }> @@ -181,7 +197,6 @@ export type Card = FC & { const ListCard: Card = ({ children, className, - href, forwardClicksToLink = false, draggable, onClick, @@ -189,9 +204,8 @@ const ListCard: Card = ({ }) => { let content, imageProps, info, footer, actions let title: TitleProps = {} - const hasHref = typeof href === "string" - const handleHrefClick = useClickChildHref(href, onClick) - const handleClick = hasHref && forwardClicksToLink ? handleHrefClick : onClick + const handleHrefClick = useClickChildLink(onClick) + const handleClick = forwardClicksToLink ? handleHrefClick : onClick Children.forEach(children, (child) => { if (!isValidElement(child)) return @@ -227,7 +241,7 @@ const ListCard: Card = ({ {info} {title && ( - + <Title data-card-link={!!title.href} {...title} href={title.href}> <TruncateText lineClamp={2}>{title.children}</TruncateText> )} diff --git a/frontends/ol-components/src/components/Card/ListCardCondensed.tsx b/frontends/ol-components/src/components/Card/ListCardCondensed.tsx index e4529fa535..a3b4ecca01 100644 --- a/frontends/ol-components/src/components/Card/ListCardCondensed.tsx +++ b/frontends/ol-components/src/components/Card/ListCardCondensed.tsx @@ -8,7 +8,7 @@ import React, { import styled from "@emotion/styled" import { RiDraggable } from "@remixicon/react" import { theme } from "../ThemeProvider/ThemeProvider" -import { BaseContainer, useClickChildHref } from "./Card" +import { BaseContainer, useClickChildLink } from "./Card" import { TruncateText } from "../TruncateText/TruncateText" import { ListCard, @@ -19,7 +19,16 @@ import { Footer, Bottom as BaseBottom, } from "./ListCard" -import type { Card as BaseCard } from "./ListCard" +import type { Card as BaseCard, TitleProps } from "./ListCard" + +const Container = styled(BaseContainer)<{ draggable?: boolean }>( + ({ draggable }) => [ + draggable && { + display: "flex", + flexDirection: "row", + }, + ], +) const DragArea = styled(BaseDragArea)` padding-right: 4px; @@ -67,19 +76,14 @@ const Content = () => <> type CardProps = { children: ReactNode[] | ReactNode className?: string - /** - * If provided, the card will render its title as a link. - * - * Clicks on the entire card can be forwarded to the link via `forwardClicksToLink`. - */ - href?: string /** * Defaults to `false`. If `true`, clicking the whole card will click the - * href link as well. + * child anchor with data-card-link. * * NOTES: + * - By default, Card.Title has `data-card-link="true"`. * - If using Card.Content to customize, you must ensure the content includes - * an anchor with the card's href. + * an anchor with data-card-link attribute. Its value is irrelevant. * - Clicks will NOT be forwarded if: * - The click target is a child of Card.Actions OR an element with * - The click target is a child of any element with data-card-actions attribute @@ -90,28 +94,44 @@ type CardProps = { as?: React.ElementType } & AriaAttributes +/** + * Condensed row-like card component with slots for info, title, footer, and actions: + * ```tsx + * + * Info + * Title + * Footer + * Actions + * + * ``` + * + * **Links:** Card.Title will be a link if `href` is supplied; the entire card + * will be clickable if `forwardClicksToLink` is `true`. + * + * **Custom Layout:** Use ListCard.Content to create a custom layout. + */ type Card = FC & Omit const ListCardCondensed: Card = ({ children, className, - href, draggable, onClick, forwardClicksToLink = false, ...others }) => { - let content, info, title, footer, actions + let content, info, footer, actions + let title: TitleProps = {} - const hasHref = typeof href === "string" - const handleHrefClick = useClickChildHref(href, onClick) - const handleClick = hasHref && forwardClicksToLink ? handleHrefClick : onClick + const handleHrefClick = useClickChildLink(onClick) + const handleClick = + forwardClicksToLink && !draggable ? handleHrefClick : onClick Children.forEach(children, (child) => { if (!isValidElement(child)) return if (child.type === Content) content = child.props.children else if (child.type === Info) info = child.props.children - else if (child.type === Title) title = child.props.children + else if (child.type === Title) title = child.props else if (child.type === Footer) footer = child.props.children else if (child.type === Actions) actions = child.props.children }) @@ -125,7 +145,12 @@ const ListCardCondensed: Card = ({ } return ( - + {draggable && ( @@ -133,15 +158,15 @@ const ListCardCondensed: Card = ({ )} {info} - - <TruncateText lineClamp={4}>{title}</TruncateText> + <Title data-card-link={!!title.href} href={title.href}> + <TruncateText lineClamp={4}>{title.children}</TruncateText>
{footer}
{actions && {actions}}
-
+ ) } diff --git a/frontends/ol-components/src/components/LearningResourceCard/LearningResourceCard.tsx b/frontends/ol-components/src/components/LearningResourceCard/LearningResourceCard.tsx index 059253ad62..8db2fffc68 100644 --- a/frontends/ol-components/src/components/LearningResourceCard/LearningResourceCard.tsx +++ b/frontends/ol-components/src/components/LearningResourceCard/LearningResourceCard.tsx @@ -234,7 +234,6 @@ const LearningResourceCard: React.FC = ({ = ({ - + {resource.title} diff --git a/frontends/ol-components/src/components/LearningResourceCard/LearningResourceListCard.tsx b/frontends/ol-components/src/components/LearningResourceCard/LearningResourceListCard.tsx index d80859d49a..f68b78baa4 100644 --- a/frontends/ol-components/src/components/LearningResourceCard/LearningResourceListCard.tsx +++ b/frontends/ol-components/src/components/LearningResourceCard/LearningResourceListCard.tsx @@ -303,7 +303,6 @@ const LearningResourceListCard: React.FC = ({ = ({ - {resource.title} + {resource.title} {onAddToLearningPathClick && ( - {resource.title} + + {resource.title} + {onAddToLearningPathClick && (