Skip to content
4 changes: 2 additions & 2 deletions frontends/main/src/app-pages/HomePage/HomePage.test.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -57,15 +57,15 @@ const setupAPIs = () => {
limit: 6,
sortby: "-news_date",
}),
{},
newsEvents.newsItems({ count: 0 }),
)
setMockResponse.get(
urls.newsEvents.list({
feed_type: ["events"],
limit: 5,
sortby: "event_date",
}),
{},
newsEvents.eventItems({ count: 0 }),
)

setMockResponse.get(urls.topics.list({ is_toplevel: true }), {
Expand Down
55 changes: 27 additions & 28 deletions frontends/main/src/app-pages/HomePage/NewsEventsSection.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@ import {
import type { NewsFeedItem, EventFeedItem } from "api/v0"
import { formatDate } from "ol-utilities"
import { RiArrowRightSLine } from "@remixicon/react"
import Link from "next/link"

const Section = styled.section`
background: ${theme.custom.colors.white};
Expand Down Expand Up @@ -111,10 +112,7 @@ const EventCard = styled(Card)`
align-self: stretch;
justify-content: space-between;
overflow: visible;

> a {
padding: 16px;
}
padding: 16px;
`

const EventDate = styled.div`
Expand Down Expand Up @@ -190,7 +188,7 @@ const Story: React.FC<{ item: NewsFeedItem; mobile: boolean }> = ({
mobile,
}) => {
return (
<StoryCard mobile={mobile} href={item.url}>
<StoryCard mobile={mobile} href={item.url} forwardClicksToLink>
{item.image.url ? (
<Card.Image src={item.image.url} alt={item.image.alt || ""} />
) : null}
Expand Down Expand Up @@ -221,31 +219,32 @@ const NewsEventsSection: React.FC = () => {
return null
}

const stories = news!.results?.slice(0, 6) || []
const stories = news.results.slice(0, 6)

const EventCards =
events!.results?.map((item) => (
<EventCard key={item.id} href={item.url}>
<Card.Content>
<EventDate>
<EventDay>
{formatDate(
(item as EventFeedItem).event_details?.event_datetime,
"D",
)}
</EventDay>
<EventMonth>
{formatDate(
(item as EventFeedItem).event_details?.event_datetime,
"MMM",
)}
</EventMonth>
</EventDate>
const EventCards = events.results.map((item) => (
<EventCard key={item.id} href={item.url} forwardClicksToLink>
<Card.Content>
<EventDate>
<EventDay>
{formatDate(
(item as EventFeedItem).event_details?.event_datetime,
"D",
)}
</EventDay>
<EventMonth>
{formatDate(
(item as EventFeedItem).event_details?.event_datetime,
"MMM",
)}
</EventMonth>
</EventDate>
<Link href={item.url}>
<EventTitle>{item.title}</EventTitle>
<Chevron />
</Card.Content>
</EventCard>
)) || []
</Link>
<Chevron />
</Card.Content>
</EventCard>
))

return (
<Section>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ import { UserList } from "api"
import { pluralize } from "ol-utilities"
import { RiListCheck3 } from "@remixicon/react"
import { ListCardCondensed, styled, theme, Typography } from "ol-components"
import Link from "next/link"

const StyledCard = styled(ListCardCondensed)({
display: "flex",
Expand Down Expand Up @@ -37,24 +38,29 @@ const IconContainer = styled.div(({ theme }) => ({
},
}))

type UserListCardCondensedProps<U extends UserList = UserList> = {
userList: U
href?: string
type UserListCardCondensedProps = {
userList: UserList
href: string
className?: string
}

const UserListCardCondensed = <U extends UserList>({
const UserListCardCondensed = ({
userList,
href,
className,
}: UserListCardCondensedProps<U>) => {
}: UserListCardCondensedProps) => {
return (
<StyledCard href={href} className={className}>
<ListCardCondensed.Content>
<TextContainer>
<Typography variant="subtitle1" color={theme.custom.colors.darkGray2}>
{userList.title}
</Typography>
<Link href={href}>
<Typography
variant="subtitle1"
color={theme.custom.colors.darkGray2}
>
{userList.title}
</Typography>
</Link>
<ItemsText variant="body3">
{userList.item_count} {pluralize("item", userList.item_count)}
</ItemsText>
Expand Down
98 changes: 97 additions & 1 deletion frontends/ol-components/src/components/Card/Card.test.tsx
Original file line number Diff line number Diff line change
@@ -1,8 +1,10 @@
import { render } from "@testing-library/react"
import { render, screen } from "@testing-library/react"
import user from "@testing-library/user-event"
import { Card } from "./Card"
import React from "react"
import { getOriginalSrc } from "ol-test-utilities"
import invariant from "tiny-invariant"
import { ThemeProvider } from "../ThemeProvider/ThemeProvider"

describe("Card", () => {
test("has class MitCard-root on root element", () => {
Expand All @@ -14,6 +16,7 @@ describe("Card", () => {
<Card.Footer>Footer</Card.Footer>
<Card.Actions>Actions</Card.Actions>
</Card>,
{ wrapper: ThemeProvider },
)
const card = container.firstChild as HTMLElement
const title = card.querySelector(".MitCard-title")
Expand All @@ -37,4 +40,97 @@ describe("Card", () => {
expect(footer).toHaveTextContent("Footer")
expect(actions).toHaveTextContent("Actions")
})

test.each([
{ forwardClicksToLink: true, finalHref: "#woof" },
{ forwardClicksToLink: false, finalHref: "" },
])(
"The whole card is clickable as a link if forwardClicksToLink ($forwardClicksToLink)",
async ({ forwardClicksToLink, finalHref }) => {
const href = "#woof"
render(
<Card href={href} forwardClicksToLink={forwardClicksToLink}>
<Card.Title>Title</Card.Title>
<Card.Image src="https://via.placeholder.com/150" alt="placeholder" />
<Card.Info>Info</Card.Info>
<Card.Footer>Footer</Card.Footer>
<Card.Actions>Actions</Card.Actions>
</Card>,
{ wrapper: ThemeProvider },
)
const card = document.querySelector(".MitCard-root")
invariant(card instanceof HTMLDivElement) // Sanity: Chceck it's not an anchor

await user.click(card)
expect(window.location.hash).toBe(finalHref)
},
)

test.each([
{ forwardClicksToLink: true, finalHref: "#meow" },
{ forwardClicksToLink: false, finalHref: "" },
])(
"The whole card is clickable as a link when using Content when forwardClicksToLink ($forwardClicksToLink), except buttons and links",
async ({ finalHref, forwardClicksToLink }) => {
const href = "#meow"
const onClick = jest.fn()
render(
<Card href={href} forwardClicksToLink={forwardClicksToLink}>
<Card.Content>
<div>Hello!</div>
<div data-card-actions>
<button onClick={onClick}>Button</button>
</div>
<a href={href}>Link</a>
</Card.Content>
</Card>,
{ wrapper: ThemeProvider },
)
const button = screen.getByRole("button", { name: "Button" })
await user.click(button)
expect(onClick).toHaveBeenCalled()
expect(window.location.hash).toBe("")

// outermost wrapper is not actually clickable
const card = document.querySelector(".MitCard-root")
invariant(card instanceof HTMLDivElement) // Sanity: Chceck it's not an anchor

await user.click(card)
expect(window.location.hash).toBe(finalHref)
},
)

test("Clicks on interactive elements are not forwarded", async () => {
const btnOnClick = jest.fn()
const divOnClick = jest.fn()
render(
<Card href={"#one"} forwardClicksToLink>
<Card.Title>Title</Card.Title>
<Card.Image src="https://via.placeholder.com/150" alt="placeholder" />
<Card.Info>Info</Card.Info>
<Card.Footer>
<button onClick={btnOnClick}>Button</button>
<a href="#two">Link Two</a>
{/*
eslint-disable-next-line jsx-a11y/click-events-have-key-events, jsx-a11y/no-static-element-interactions
*/}
<div data-card-action onClick={divOnClick}>
Interactive Div
</div>
</Card.Footer>
</Card>,
{ wrapper: ThemeProvider },
)
const button = screen.getByRole("button", { name: "Button" })
const link = 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)
expect(window.location.hash).toBe("#two")
await user.click(div)
expect(divOnClick).toHaveBeenCalled()
expect(window.location.hash).toBe("#two")
})
})
Loading
Loading