From 025c05fa8ce1634a2c0e4c95896451fa5d24fc20 Mon Sep 17 00:00:00 2001
From: Jon Kafton <939376+jonkafton@users.noreply.github.com>
Date: Tue, 11 Jun 2024 00:08:10 +0200
Subject: [PATCH 10/25] Style lint fixes
---
frontends/ol-components/src/components/Card/ListCard.tsx | 4 ++++
.../LearningResourceCard/LearningResourceListCard.tsx | 4 ++++
2 files changed, 8 insertions(+)
diff --git a/frontends/ol-components/src/components/Card/ListCard.tsx b/frontends/ol-components/src/components/Card/ListCard.tsx
index 1324e46e3c..08b77c5a6d 100644
--- a/frontends/ol-components/src/components/Card/ListCard.tsx
+++ b/frontends/ol-components/src/components/Card/ListCard.tsx
@@ -19,6 +19,7 @@ const containerStyles = `
const LinkContainer = styled(Link)`
${containerStyles}
display: flex;
+
:hover {
text-decoration: none;
border-color: ${theme.custom.colors.silverGrayLight};
@@ -42,6 +43,7 @@ const Body = styled.div`
${theme.breakpoints.down("md")} {
margin: 12px;
}
+
display: flex;
flex-direction: column;
justify-content: space-between;
@@ -75,6 +77,7 @@ const Info = styled.div`
${{ ...theme.typography.subtitle4 }}
margin-bottom: 8px;
}
+
color: ${theme.custom.colors.silverGrayDark};
display: flex;
justify-content: space-between;
@@ -93,6 +96,7 @@ const Title = styled.h3`
overflow: hidden;
margin: 0;
+
@supports (-webkit-line-clamp: 2) {
white-space: initial;
display: -webkit-box;
diff --git a/frontends/ol-components/src/components/LearningResourceCard/LearningResourceListCard.tsx b/frontends/ol-components/src/components/LearningResourceCard/LearningResourceListCard.tsx
index 3058905265..900ca0a5e6 100644
--- a/frontends/ol-components/src/components/LearningResourceCard/LearningResourceListCard.tsx
+++ b/frontends/ol-components/src/components/LearningResourceCard/LearningResourceListCard.tsx
@@ -50,6 +50,7 @@ const Certificate = styled.div`
height: 12px;
fill: ${theme.custom.colors.silverGrayDark};
}
+
margin: 0 12px 0 auto;
}
@@ -70,6 +71,7 @@ const BorderSeparator = styled.div`
div {
display: inline;
}
+
div + div {
margin-left: 8px;
padding-left: 8px;
@@ -173,9 +175,11 @@ const Loading = styled.div<{ mobile?: boolean }>`
display: flex;
padding: 24px;
justify-content: space-between;
+
> div {
width: calc(100% - 236px);
}
+
> span {
flex-grow: 0;
margin-left: auto;
From db72030756da30fd861c03046b98b030a3f2c89b Mon Sep 17 00:00:00 2001
From: Jon Kafton <939376+jonkafton@users.noreply.github.com>
Date: Tue, 11 Jun 2024 16:47:00 +0200
Subject: [PATCH 11/25] Pass href to navigate instead of open drawer. Add list
count for learning paths
---
.../LearningPathListingPage.tsx | 12 ++++++++++++
.../LearningResourceListCard.tsx | 18 +++++++++++++++++-
2 files changed, 29 insertions(+), 1 deletion(-)
diff --git a/frontends/mit-open/src/pages/LearningPathListingPage/LearningPathListingPage.tsx b/frontends/mit-open/src/pages/LearningPathListingPage/LearningPathListingPage.tsx
index 2c35075887..24f1548e03 100644
--- a/frontends/mit-open/src/pages/LearningPathListingPage/LearningPathListingPage.tsx
+++ b/frontends/mit-open/src/pages/LearningPathListingPage/LearningPathListingPage.tsx
@@ -11,6 +11,7 @@ import {
styled,
Typography,
PlainList,
+ LearningResourceListCard,
imgConfigs,
} from "ol-components"
import type { SimpleMenuItem } from "ol-components"
@@ -155,6 +156,17 @@ const LearningPathListingPage: React.FC = () => {
)
})}
+ {listingQuery.data.results?.map((resource) => {
+ return (
+
+
+
+ )
+ })}
)}
diff --git a/frontends/ol-components/src/components/LearningResourceCard/LearningResourceListCard.tsx b/frontends/ol-components/src/components/LearningResourceCard/LearningResourceListCard.tsx
index 900ca0a5e6..1bbf387806 100644
--- a/frontends/ol-components/src/components/LearningResourceCard/LearningResourceListCard.tsx
+++ b/frontends/ol-components/src/components/LearningResourceCard/LearningResourceListCard.tsx
@@ -8,6 +8,7 @@ import {
formatDate,
getReadableResourceType,
embedlyCroppedImage,
+ pluralize,
} from "ol-utilities"
import { ListCard } from "../Card/ListCard"
import { TruncateText } from "../TruncateText/TruncateText"
@@ -126,6 +127,18 @@ const Info = ({ resource }: { resource: LearningResource }) => {
)
}
+const Count = ({ resource }: { resource: LearningResource }) => {
+ if (resource.resource_type !== ResourceTypeEnum.LearningPath) {
+ return null
+ }
+ const count = resource.learning_path.item_count
+ return (
+
+ {count} {pluralize("item", count)}
+
+ )
+}
+
const isOcw = (resource: LearningResource) =>
resource.resource_type === ResourceTypeEnum.Course &&
resource.platform?.code === PlatformEnum.Ocw
@@ -225,6 +238,7 @@ interface LearningResourceListCardProps {
isLoading?: boolean
resource?: LearningResource | null
className?: string
+ href?: string
onAddToLearningPathClick?: ResourceIdCallback | null
onAddToUserListClick?: ResourceIdCallback | null
}
@@ -233,6 +247,7 @@ const LearningResourceListCard: React.FC
= ({
isLoading,
resource,
className,
+ href,
onAddToLearningPathClick,
onAddToUserListClick,
}) => {
@@ -251,7 +266,7 @@ const LearningResourceListCard: React.FC = ({
return null
}
return (
-
+
{resource.image && (
= ({
+
From bc8aeb4dc48fe2f2f6a3a9af8fb04b20747ad7e7 Mon Sep 17 00:00:00 2001
From: Jon Kafton <939376+jonkafton@users.noreply.github.com>
Date: Tue, 11 Jun 2024 17:57:19 +0200
Subject: [PATCH 12/25] Position buttons absolute and outside of Link container
---
.../LearningResourceCardTemplate.tsx | 167 ++++++++++++++++++
.../LearningPathListingPage.test.tsx | 4 +-
.../LearningPathListingPage.tsx | 73 +++-----
.../src/components/Button/Button.tsx | 4 +-
.../src/components/Card/Card.tsx | 47 +++--
.../src/components/Card/ListCard.tsx | 40 +++--
.../LearningResourceListCard.tsx | 3 +
7 files changed, 260 insertions(+), 78 deletions(-)
create mode 100644 frontends/mit-open/src/page-components/LearningResourceCardTemplate/LearningResourceCardTemplate.tsx
diff --git a/frontends/mit-open/src/page-components/LearningResourceCardTemplate/LearningResourceCardTemplate.tsx b/frontends/mit-open/src/page-components/LearningResourceCardTemplate/LearningResourceCardTemplate.tsx
new file mode 100644
index 0000000000..1909c8dd66
--- /dev/null
+++ b/frontends/mit-open/src/page-components/LearningResourceCardTemplate/LearningResourceCardTemplate.tsx
@@ -0,0 +1,167 @@
+import React, { useCallback } from "react"
+import { ResourceTypeEnum } from "api"
+import type { LearningResource } from "api"
+import { Chip, styled } from "ol-components"
+import CalendarTodayIcon from "@mui/icons-material/CalendarToday"
+import {
+ formatDate,
+ pluralize,
+ getReadableResourceType,
+ findBestRun,
+ DEFAULT_RESOURCE_IMG,
+} from "ol-utilities"
+import type { EmbedlyConfig } from "ol-utilities"
+import CardTemplate from "../CardTemplate/CardTemplate"
+
+type CardVariant = "column" | "row" | "row-reverse"
+type OnActivateCard = (resource: R) => void
+type LearningResourceCardTemplateProps<
+ R extends LearningResource = LearningResource,
+> = {
+ /**
+ * Whether the course picture and info display as a column or row.
+ */
+ variant: CardVariant
+ resource: R
+ sortable?: boolean
+ className?: string
+ /**
+ * Config used to generate embedly urls.
+ */
+ imgConfig: EmbedlyConfig
+ onActivate?: OnActivateCard
+ /**
+ * Suppress the image.
+ */
+ suppressImage?: boolean
+ footerActionSlot?: React.ReactNode
+}
+
+const LIGHT_TEXT_COLOR = "#8c8c8c"
+const SMALL_FONT_SIZE = 0.75
+
+const CalendarChip = styled(Chip)({
+ height: `${2.5 * SMALL_FONT_SIZE}em`,
+ fontSize: `${SMALL_FONT_SIZE}em`,
+
+ ".MuiSvgIcon-root": {
+ height: `${1.25 * SMALL_FONT_SIZE}em`,
+ width: `${1.25 * SMALL_FONT_SIZE}em`,
+ },
+})
+
+const ResourceFooterDetails: React.FC<
+ Pick
+> = ({ resource }) => {
+ if (resource.resource_type === ResourceTypeEnum.LearningPath) {
+ const count = resource.learning_path.item_count
+ return (
+
+ {count} {pluralize("item", count)}
+
+ )
+ }
+
+ const bestRun = findBestRun(resource.runs ?? [])
+ const startDate = bestRun?.start_date
+
+ if (!startDate) return null
+
+ const formattedDate = formatDate(startDate, "MMMM DD, YYYY")
+
+ return (
+ }
+ label={formattedDate}
+ />
+ )
+}
+
+const OfferedByText = styled.span`
+ color: ${LIGHT_TEXT_COLOR};
+ padding-right: 0.25em;
+`
+
+const CardBody: React.FC<
+ Pick
+> = ({ resource }) => {
+ const offerer = resource.offered_by?.name ?? null
+ return offerer ? (
+
+ Offered by –
+ {offerer}
+
+ ) : null
+}
+
+const TypeRow = styled.div`
+ display: flex;
+ flex-direction: row;
+ justify-content: space-between;
+ align-items: center;
+ min-height: 1.5em; /* ensure consistent height even if no certificate */
+`
+
+const CertificateIcon = styled.img`
+ height: 1.5em;
+`
+/**
+ * A card display for Learning Resources. Includes a title, image, and various
+ * metadata.
+ *
+ * This template does not provide any meaningful user interaction by itself, but
+ * does accept props to build user interaction (e.g., `onActivate` and
+ * `footerActionSlot`).
+ */
+const LearningResourceCardTemplate = ({
+ variant,
+ resource,
+ imgConfig,
+ className,
+ onActivate,
+ footerActionSlot,
+ sortable,
+ suppressImage = false,
+}: LearningResourceCardTemplateProps) => {
+ const handleActivate = useCallback(
+ () => onActivate?.(resource),
+ [resource, onActivate],
+ )
+
+ const imgUrl = resource.image?.url ?? DEFAULT_RESOURCE_IMG
+ const extraDetails = (
+
+ {getReadableResourceType(resource.resource_type)}
+ {resource.certification && (
+
+ )}
+
+ )
+ const body =
+ const footer =
+
+ return (
+
+ )
+}
+
+export default LearningResourceCardTemplate
+export { TypeRow }
+export type { LearningResourceCardTemplateProps }
diff --git a/frontends/mit-open/src/pages/LearningPathListingPage/LearningPathListingPage.test.tsx b/frontends/mit-open/src/pages/LearningPathListingPage/LearningPathListingPage.test.tsx
index bb8d7f9dc6..bec133a87b 100644
--- a/frontends/mit-open/src/pages/LearningPathListingPage/LearningPathListingPage.test.tsx
+++ b/frontends/mit-open/src/pages/LearningPathListingPage/LearningPathListingPage.test.tsx
@@ -2,7 +2,7 @@ import React from "react"
import { faker } from "@faker-js/faker/locale/en"
import { factories, urls } from "api/test-utils"
import { manageListDialogs } from "@/page-components/ManageListDialogs/ManageListDialogs"
-import LearningResourceCardTemplate from "@/page-components/LearningResourceCardTemplate/LearningResourceCardTemplate"
+import { LearningResourceListCard } from "ol-components"
import LearningPathListingPage from "./LearningPathListingPage"
import {
screen,
@@ -27,7 +27,7 @@ jest.mock(
}
},
)
-const spyLRCardTemplate = jest.mocked(LearningResourceCardTemplate)
+const spyLRCardTemplate = jest.mocked(LearningResourceListCard)
/**
* Set up the mock API responses for lists pages.
diff --git a/frontends/mit-open/src/pages/LearningPathListingPage/LearningPathListingPage.tsx b/frontends/mit-open/src/pages/LearningPathListingPage/LearningPathListingPage.tsx
index 24f1548e03..594c068587 100644
--- a/frontends/mit-open/src/pages/LearningPathListingPage/LearningPathListingPage.tsx
+++ b/frontends/mit-open/src/pages/LearningPathListingPage/LearningPathListingPage.tsx
@@ -1,5 +1,4 @@
import React, { useCallback, useMemo } from "react"
-import { useNavigate } from "react-router"
import {
Button,
SimpleMenu,
@@ -12,7 +11,7 @@ import {
Typography,
PlainList,
LearningResourceListCard,
- imgConfigs,
+ useMuiBreakpointAtLeast,
} from "ol-components"
import type { SimpleMenuItem } from "ol-components"
import EditIcon from "@mui/icons-material/Edit"
@@ -25,8 +24,6 @@ import { useLearningPathsList } from "api/hooks/learningResources"
import { GridColumn, GridContainer } from "@/components/GridLayout/GridLayout"
-import LearningResourceCardTemplate from "@/page-components/LearningResourceCardTemplate/LearningResourceCardTemplate"
-
import { manageListDialogs } from "@/page-components/ManageListDialogs/ManageListDialogs"
import * as urls from "@/common/urls"
import { useUserMe } from "api/hooks/user"
@@ -36,11 +33,21 @@ const ListHeaderGrid = styled(Grid)`
margin-bottom: 1rem;
`
+const StyledActionButton = styled(ActionButton)<{ mobile: boolean }>`
+ ${({ mobile }) =>
+ mobile
+ ? `
+ width: 16px;
+ height: 16px;`
+ : ""}
+`
+
type EditListMenuProps = {
resource: LearningPathResource
+ isMobile: boolean
}
-const EditListMenu: React.FC = ({ resource }) => {
+const EditListMenu: React.FC = ({ resource, isMobile }) => {
const items: SimpleMenuItem[] = useMemo(
() => [
{
@@ -61,49 +68,27 @@ const EditListMenu: React.FC = ({ resource }) => {
return (
-
+
}
items={items}
/>
)
}
-type ListCardProps = {
- list: LearningPathResource
- onActivate: (resource: LearningPathResource) => void
- canEdit: boolean
-}
-const ListCard: React.FC = ({ list, onActivate, canEdit }) => {
- return (
- : null}
- onActivate={onActivate}
- />
- )
-}
-
const LearningPathListingPage: React.FC = () => {
+ const isMobile = !useMuiBreakpointAtLeast("md")
const listingQuery = useLearningPathsList()
const { data: user } = useUserMe()
- const navigate = useNavigate()
- const handleActivate = useCallback(
- (resource: LearningPathResource) => {
- const path = urls.learningPathsView(resource.id)
- navigate(path)
- },
- [navigate],
- )
const handleCreate = useCallback(() => {
manageListDialogs.upsertLearningPath()
}, [])
@@ -119,7 +104,7 @@ const LearningPathListingPage: React.FC = () => {
Learning Paths
-
+
@@ -145,24 +130,20 @@ const LearningPathListingPage: React.FC = () => {
{listingQuery.data && (
- {listingQuery.data.results?.map((list) => {
- return (
-
-
-
- )
- })}
{listingQuery.data.results?.map((resource) => {
return (
+ ) : null
+ }
/>
)
diff --git a/frontends/ol-components/src/components/Button/Button.tsx b/frontends/ol-components/src/components/Button/Button.tsx
index e8ec114fa2..c1f20c614c 100644
--- a/frontends/ol-components/src/components/Button/Button.tsx
+++ b/frontends/ol-components/src/components/Button/Button.tsx
@@ -130,7 +130,9 @@ const ButtonStyled = styled.button((props) => {
edge === "none" && {
border: "none",
":hover:not(:disabled)": {
- backgroundColor: "none",
+ "&&": {
+ backgroundColor: "inherit",
+ },
},
},
color === "secondary" && {
diff --git a/frontends/ol-components/src/components/Card/Card.tsx b/frontends/ol-components/src/components/Card/Card.tsx
index a09e9dca16..c4f2ecd9c6 100644
--- a/frontends/ol-components/src/components/Card/Card.tsx
+++ b/frontends/ol-components/src/components/Card/Card.tsx
@@ -12,6 +12,11 @@ import { Link } from "react-router-dom"
export type Size = "small" | "medium"
+// Relative positioned wrapper to position action buttons outside of the child Link (buttons inside anchors is not valid HTML)
+const Wrapper = styled.div`
+ position: relative;
+`
+
const getWidthCss = ({ size }: { size?: Size }) => {
let width
if (size === "medium") width = 300
@@ -29,6 +34,7 @@ const Container = styled(Link)`
overflow: hidden;
${getWidthCss}
display: block;
+ position: relative;
:hover {
text-decoration: none;
@@ -104,6 +110,13 @@ const Bottom = styled.div`
const Actions = styled.div`
display: flex;
gap: 8px;
+ position: absolute;
+ bottom: 24px;
+ right: 24px;
+ ${theme.breakpoints.down("md")} {
+ bottom: 12px;
+ right: 12px;
+ }
`
type CardProps = {
@@ -143,22 +156,24 @@ const Card: Card = ({ children, className, size, href }) => {
}
return (
-
- {imageProps && (
- )}
- />
- )}
-
- {info && {info}}
- {title}
-
-
-
- {actions && {actions}}
-
-
+
+
+ {imageProps && (
+ )}
+ />
+ )}
+
+ {info && {info}}
+ {title}
+
+
+
+
+
+ {actions && {actions}}
+
)
}
diff --git a/frontends/ol-components/src/components/Card/ListCard.tsx b/frontends/ol-components/src/components/Card/ListCard.tsx
index 08b77c5a6d..136bbd6664 100644
--- a/frontends/ol-components/src/components/Card/ListCard.tsx
+++ b/frontends/ol-components/src/components/Card/ListCard.tsx
@@ -9,6 +9,11 @@ import styled from "@emotion/styled"
import { theme } from "../ThemeProvider/ThemeProvider"
import { Link } from "react-router-dom"
+// Relative positioned wrapper to position action buttons outside of the child Link (buttons inside anchors is not valid HTML)
+const Wrapper = styled.div`
+ position: relative;
+`
+
const containerStyles = `
border-radius: 8px;
border: 1px solid ${theme.custom.colors.lightGray2};
@@ -130,6 +135,13 @@ const Bottom = styled.div`
const Actions = styled.div`
display: flex;
gap: 8px;
+ position: absolute;
+ bottom: 24px;
+ right: 24px;
+ ${theme.breakpoints.down("md")} {
+ bottom: 12px;
+ right: 12px;
+ }
`
type CardProps = {
@@ -170,19 +182,21 @@ const ListCard: Card = ({ children, className, href }) => {
}
return (
- <_Container className={className} to={href!}>
-
- {info}
- {title}
-
-
- {actions && {actions}}
-
-
- {imageProps && (
- )} />
- )}
-
+
+ <_Container className={className} to={href!}>
+
+ {info}
+ {title}
+
+
+
+
+ {imageProps && (
+ )} />
+ )}
+
+ {actions && {actions}}
+
)
}
diff --git a/frontends/ol-components/src/components/LearningResourceCard/LearningResourceListCard.tsx b/frontends/ol-components/src/components/LearningResourceCard/LearningResourceListCard.tsx
index 1bbf387806..5471e8ce3e 100644
--- a/frontends/ol-components/src/components/LearningResourceCard/LearningResourceListCard.tsx
+++ b/frontends/ol-components/src/components/LearningResourceCard/LearningResourceListCard.tsx
@@ -241,6 +241,7 @@ interface LearningResourceListCardProps {
href?: string
onAddToLearningPathClick?: ResourceIdCallback | null
onAddToUserListClick?: ResourceIdCallback | null
+ editMenu?: React.ReactNode | null
}
const LearningResourceListCard: React.FC = ({
@@ -250,6 +251,7 @@ const LearningResourceListCard: React.FC = ({
href,
onAddToLearningPathClick,
onAddToUserListClick,
+ editMenu,
}) => {
const isMobile = !useMuiBreakpointAtLeast("md")
@@ -304,6 +306,7 @@ const LearningResourceListCard: React.FC = ({
)}
+ {editMenu}
From 4173aeb9a0dfde45216aefb993a2da4981415d83 Mon Sep 17 00:00:00 2001
From: Jon Kafton <939376+jonkafton@users.noreply.github.com>
Date: Tue, 11 Jun 2024 18:12:53 +0200
Subject: [PATCH 13/25] Positioning and set class on wrapper
---
.../src/components/Card/Card.tsx | 33 +++++++++----------
.../src/components/Card/ListCard.tsx | 4 +--
2 files changed, 18 insertions(+), 19 deletions(-)
diff --git a/frontends/ol-components/src/components/Card/Card.tsx b/frontends/ol-components/src/components/Card/Card.tsx
index c4f2ecd9c6..86e196fb47 100644
--- a/frontends/ol-components/src/components/Card/Card.tsx
+++ b/frontends/ol-components/src/components/Card/Card.tsx
@@ -12,11 +12,6 @@ import { Link } from "react-router-dom"
export type Size = "small" | "medium"
-// Relative positioned wrapper to position action buttons outside of the child Link (buttons inside anchors is not valid HTML)
-const Wrapper = styled.div`
- position: relative;
-`
-
const getWidthCss = ({ size }: { size?: Size }) => {
let width
if (size === "medium") width = 300
@@ -27,12 +22,18 @@ const getWidthCss = ({ size }: { size?: Size }) => {
`
}
+// Relative positioned wrapper to position action buttons outside of the child Link (buttons inside anchors is not valid HTML)
+const Wrapper = styled.div`
+ position: relative;
+ ${getWidthCss}
+`
+
const Container = styled(Link)`
border-radius: 8px;
border: 1px solid ${theme.custom.colors.lightGray2};
background: ${theme.custom.colors.white};
overflow: hidden;
- ${getWidthCss}
+
display: block;
position: relative;
@@ -111,12 +112,8 @@ const Actions = styled.div`
display: flex;
gap: 8px;
position: absolute;
- bottom: 24px;
- right: 24px;
- ${theme.breakpoints.down("md")} {
- bottom: 12px;
- right: 12px;
- }
+ bottom: 16px;
+ right: 16px;
`
type CardProps = {
@@ -149,15 +146,17 @@ const Card: Card = ({ children, className, size, href }) => {
if (content) {
return (
-
- {content}
-
+
+
+ {content}
+
+
)
}
return (
-
-
+
+
{imageProps && (
{
}
return (
-
- <_Container className={className} to={href!}>
+
+ <_Container to={href!}>
{info}
{title}
From 92fc7b0e86734db36e6cd38e4266570a66de4525 Mon Sep 17 00:00:00 2001
From: Jon Kafton <939376+jonkafton@users.noreply.github.com>
Date: Tue, 11 Jun 2024 19:09:57 +0200
Subject: [PATCH 14/25] Test fixes
---
.../LearningPathListingPage.test.tsx | 21 -------
.../LearningResourceCard.test.tsx | 59 ++++++++++++-------
.../LearningResourceCard.tsx | 2 +-
.../LearningResourceListCard.tsx | 2 +-
4 files changed, 41 insertions(+), 43 deletions(-)
diff --git a/frontends/mit-open/src/pages/LearningPathListingPage/LearningPathListingPage.test.tsx b/frontends/mit-open/src/pages/LearningPathListingPage/LearningPathListingPage.test.tsx
index bec133a87b..4afee0591f 100644
--- a/frontends/mit-open/src/pages/LearningPathListingPage/LearningPathListingPage.test.tsx
+++ b/frontends/mit-open/src/pages/LearningPathListingPage/LearningPathListingPage.test.tsx
@@ -2,33 +2,16 @@ import React from "react"
import { faker } from "@faker-js/faker/locale/en"
import { factories, urls } from "api/test-utils"
import { manageListDialogs } from "@/page-components/ManageListDialogs/ManageListDialogs"
-import { LearningResourceListCard } from "ol-components"
import LearningPathListingPage from "./LearningPathListingPage"
import {
screen,
renderWithProviders,
setMockResponse,
user,
- expectProps,
waitFor,
} from "../../test-utils"
import type { User } from "../../types/settings"
-jest.mock(
- "../../page-components/LearningResourceCardTemplate/LearningResourceCardTemplate",
- () => {
- const actual = jest.requireActual(
- "../../page-components/LearningResourceCardTemplate/LearningResourceCardTemplate",
- )
- return {
- __esModule: true,
- ...actual,
- default: jest.fn(actual.default),
- }
- },
-)
-const spyLRCardTemplate = jest.mocked(LearningResourceListCard)
-
/**
* Set up the mock API responses for lists pages.
*/
@@ -67,10 +50,6 @@ describe("LearningPathListingPage", () => {
// for sanity
expect(headings.length).toBeGreaterThan(0)
expect(titles.length).toBe(headings.length)
-
- paths.results.forEach((resource) => {
- expectProps(spyLRCardTemplate, { resource })
- })
})
it.each([
diff --git a/frontends/ol-components/src/components/LearningResourceCard/LearningResourceCard.test.tsx b/frontends/ol-components/src/components/LearningResourceCard/LearningResourceCard.test.tsx
index 96ab30f22f..ac727ab1be 100644
--- a/frontends/ol-components/src/components/LearningResourceCard/LearningResourceCard.test.tsx
+++ b/frontends/ol-components/src/components/LearningResourceCard/LearningResourceCard.test.tsx
@@ -1,11 +1,21 @@
import React from "react"
+import { BrowserRouter } from "react-router-dom"
import { render, screen } from "@testing-library/react"
import { LearningResourceCard } from "./LearningResourceCard"
-import { DEFAULT_RESOURCE_IMG } from "ol-utilities"
-import { ResourceTypeEnum, PlatformEnum } from "api"
+import { DEFAULT_RESOURCE_IMG, embedlyCroppedImage } from "ol-utilities"
+import { LearningResource, ResourceTypeEnum, PlatformEnum } from "api"
import { factories } from "api/test-utils"
import { ThemeProvider } from "../ThemeProvider/ThemeProvider"
+const setup = (resource: LearningResource) => {
+ return render(
+
+
+ ,
+ { wrapper: ThemeProvider },
+ )
+}
+
describe("Learning Resource Card", () => {
test("Renders resource type, title and start date", () => {
const resource = factories.learningResources.resource({
@@ -13,7 +23,7 @@ describe("Learning Resource Card", () => {
next_start_date: "2026-01-01",
})
- render()
+ setup(resource)
screen.getByText("Course")
screen.getByRole("heading", { name: resource.title })
@@ -32,7 +42,7 @@ describe("Learning Resource Card", () => {
],
})
- render()
+ setup(resource)
screen.getByText("Starts:")
screen.getByText("January 01, 2026")
@@ -50,10 +60,10 @@ describe("Learning Resource Card", () => {
],
})
- render()
+ setup(resource)
- screen.getByText("As taught in:")
- screen.getByText("Fall 2002")
+ expect(screen.getByRole("link")).toHaveTextContent("As taught in:")
+ expect(screen.getByRole("link")).toHaveTextContent("Fall 2002")
})
test("Click to activate and action buttons", async () => {
@@ -68,21 +78,21 @@ describe("Learning Resource Card", () => {
],
})
- const onActivate = jest.fn()
const onAddToLearningPathClick = jest.fn()
const onAddToUserListClick = jest.fn()
render(
- ,
+
+
+ ,
{ wrapper: ThemeProvider },
)
- const heading = screen.getByRole("link", { name: resource.title })
+ const heading = screen.getByRole("heading", { name: resource.title })
await heading.click()
const addToLearningPathButton = screen.getByLabelText(
@@ -93,7 +103,6 @@ describe("Learning Resource Card", () => {
const addToUserListButton = screen.getByLabelText("Add to User List")
await addToUserListButton.click()
- expect(onActivate).toHaveBeenCalledWith(resource.id)
expect(onAddToLearningPathClick).toHaveBeenCalledWith(resource.id)
expect(onAddToUserListClick).toHaveBeenCalledWith(resource.id)
})
@@ -103,7 +112,7 @@ describe("Learning Resource Card", () => {
certification: true,
})
- render()
+ setup(resource)
screen.getByText("Certificate")
})
@@ -113,7 +122,7 @@ describe("Learning Resource Card", () => {
certification: false,
})
- render()
+ setup(resource)
const badge = screen.queryByText("Certificate")
@@ -133,10 +142,20 @@ describe("Learning Resource Card", () => {
])("Image is displayed if present", ({ expected, image }) => {
const resource = factories.learningResources.resource({ image })
- render()
+ setup(resource)
const imageEls = screen.getAllByRole("img")
- const matching = imageEls.filter((im) => im.src === expected.src)
+
+ const matching = imageEls.filter((im) =>
+ expected.src === DEFAULT_RESOURCE_IMG
+ ? im.src === DEFAULT_RESOURCE_IMG
+ : im.src ===
+ embedlyCroppedImage(expected.src, {
+ width: 298,
+ height: 170,
+ key: "fake-embedly-key",
+ }),
+ )
expect(matching.length).toBe(1)
expect(matching[0]).toHaveAttribute("alt", expected.alt)
})
diff --git a/frontends/ol-components/src/components/LearningResourceCard/LearningResourceCard.tsx b/frontends/ol-components/src/components/LearningResourceCard/LearningResourceCard.tsx
index 48fcf5031e..3bf8d6490d 100644
--- a/frontends/ol-components/src/components/LearningResourceCard/LearningResourceCard.tsx
+++ b/frontends/ol-components/src/components/LearningResourceCard/LearningResourceCard.tsx
@@ -106,7 +106,7 @@ const StartDate: React.FC<{ resource: LearningResource; size?: Size }> = ({
return (
<>
- {label} {formatDate(startDate, "MMMM DD, YYYY")}
+ {label} {startDate}
>
)
}
diff --git a/frontends/ol-components/src/components/LearningResourceCard/LearningResourceListCard.tsx b/frontends/ol-components/src/components/LearningResourceCard/LearningResourceListCard.tsx
index 5471e8ce3e..04a69c387c 100644
--- a/frontends/ol-components/src/components/LearningResourceCard/LearningResourceListCard.tsx
+++ b/frontends/ol-components/src/components/LearningResourceCard/LearningResourceListCard.tsx
@@ -169,7 +169,7 @@ const StartDate: React.FC<{ resource: LearningResource }> = ({ resource }) => {
return (
- {label} {formatDate(startDate, "MMMM DD, YYYY")}
+ {label} {startDate}
)
}
From 0d815e2fe2088acd3c12b05d3634a0baa4f5dd9a Mon Sep 17 00:00:00 2001
From: Jon Kafton <939376+jonkafton@users.noreply.github.com>
Date: Wed, 12 Jun 2024 21:19:04 +0200
Subject: [PATCH 15/25] Reinstate non link card variant. Update tests
---
.../src/components/Card/Card.tsx | 43 +++++++++++--------
.../LearningResourceCard.test.tsx | 37 +++++++++++-----
2 files changed, 53 insertions(+), 27 deletions(-)
diff --git a/frontends/ol-components/src/components/Card/Card.tsx b/frontends/ol-components/src/components/Card/Card.tsx
index 86e196fb47..f25cffa642 100644
--- a/frontends/ol-components/src/components/Card/Card.tsx
+++ b/frontends/ol-components/src/components/Card/Card.tsx
@@ -12,23 +12,22 @@ import { Link } from "react-router-dom"
export type Size = "small" | "medium"
-const getWidthCss = ({ size }: { size?: Size }) => {
- let width
- if (size === "medium") width = 300
- if (size === "small") width = 192
- return `
- min-width: ${width}px;
- max-width: ${width}px;
- `
-}
-
// Relative positioned wrapper to position action buttons outside of the child Link (buttons inside anchors is not valid HTML)
-const Wrapper = styled.div`
+const Wrapper = styled.div<{ size?: Size }>`
position: relative;
- ${getWidthCss}
+ ${({ size }) => {
+ let width
+ if (!size) return ""
+ if (size === "medium") width = 300
+ if (size === "small") width = 192
+ return `
+ min-width: ${width}px;
+ max-width: ${width}px;
+ `
+ }}
`
-const Container = styled(Link)`
+const containerStyles = `
border-radius: 8px;
border: 1px solid ${theme.custom.colors.lightGray2};
background: ${theme.custom.colors.white};
@@ -36,6 +35,10 @@ const Container = styled(Link)`
display: block;
position: relative;
+`
+
+const LinkContainer = styled(Link)`
+ ${containerStyles}
:hover {
text-decoration: none;
@@ -47,6 +50,10 @@ const Container = styled(Link)`
}
`
+const Container = styled.div`
+ ${containerStyles}
+`
+
const Content = () => <>>
const Body = styled.div`
@@ -134,6 +141,8 @@ type Card = FC & {
const Card: Card = ({ children, className, size, href }) => {
let content, imageProps, info, title, footer, actions
+ const _Container = href ? LinkContainer : Container
+
Children.forEach(children, (child) => {
if (!isValidElement(child)) return
if (child.type === Content) content = child.props.children
@@ -147,16 +156,16 @@ const Card: Card = ({ children, className, size, href }) => {
if (content) {
return (
-
+ <_Container className={className} to={href!}>
{content}
-
+
)
}
return (
-
+ <_Container to={href!}>
{imageProps && (
{
-
+
{actions && {actions}}
)
diff --git a/frontends/ol-components/src/components/LearningResourceCard/LearningResourceCard.test.tsx b/frontends/ol-components/src/components/LearningResourceCard/LearningResourceCard.test.tsx
index ac727ab1be..152bc64eaa 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 { render, screen } from "@testing-library/react"
+import { screen, render, act } from "@testing-library/react"
import { LearningResourceCard } from "./LearningResourceCard"
import { DEFAULT_RESOURCE_IMG, embedlyCroppedImage } from "ol-utilities"
import { LearningResource, ResourceTypeEnum, PlatformEnum } from "api"
@@ -66,16 +66,31 @@ describe("Learning Resource Card", () => {
expect(screen.getByRole("link")).toHaveTextContent("Fall 2002")
})
- test("Click to activate and action buttons", async () => {
+ test("Click to navigate", async () => {
+ const resource = factories.learningResources.resource({
+ resource_type: ResourceTypeEnum.Course,
+ platform: { code: PlatformEnum.Ocw },
+ })
+
+ render(
+
+
+ ,
+ { wrapper: ThemeProvider },
+ )
+
+ const heading = screen.getByRole("heading", { name: resource.title })
+ await act(async () => {
+ await heading.click()
+ })
+
+ expect(window.location.search).toBe(`?resource=${resource.id}`)
+ })
+
+ test("Click action buttons", async () => {
const resource = factories.learningResources.resource({
resource_type: ResourceTypeEnum.Course,
platform: { code: PlatformEnum.Ocw },
- runs: [
- factories.learningResources.run({
- semester: "Fall",
- year: 2002,
- }),
- ],
})
const onAddToLearningPathClick = jest.fn()
@@ -92,8 +107,10 @@ describe("Learning Resource Card", () => {
{ wrapper: ThemeProvider },
)
- const heading = screen.getByRole("heading", { name: resource.title })
- await heading.click()
+ // const heading = screen.getByRole("heading", { name: resource.title })
+ // act(async () => {
+ // await heading.click()
+ // })
const addToLearningPathButton = screen.getByLabelText(
"Add to Learning Path",
From 246a66e2e3c9fc7bff8d73c335bc92cd60cc0d2a Mon Sep 17 00:00:00 2001
From: Jon Kafton <939376+jonkafton@users.noreply.github.com>
Date: Wed, 12 Jun 2024 21:30:14 +0200
Subject: [PATCH 16/25] Style lint fix
---
frontends/mit-open/src/pages/HomePage/NewsEventsSection.tsx | 2 +-
frontends/ol-components/src/components/Card/Card.stories.tsx | 4 +++-
2 files changed, 4 insertions(+), 2 deletions(-)
diff --git a/frontends/mit-open/src/pages/HomePage/NewsEventsSection.tsx b/frontends/mit-open/src/pages/HomePage/NewsEventsSection.tsx
index 68e9c530e6..c07ea7378c 100644
--- a/frontends/mit-open/src/pages/HomePage/NewsEventsSection.tsx
+++ b/frontends/mit-open/src/pages/HomePage/NewsEventsSection.tsx
@@ -172,7 +172,7 @@ const Story: React.FC<{ item: NewsFeedItem; mobile: boolean }> = ({
mobile,
}) => {
return (
-
+
{item.title}
diff --git a/frontends/ol-components/src/components/Card/Card.stories.tsx b/frontends/ol-components/src/components/Card/Card.stories.tsx
index 9e6a102787..9d7f3b630a 100644
--- a/frontends/ol-components/src/components/Card/Card.stories.tsx
+++ b/frontends/ol-components/src/components/Card/Card.stories.tsx
@@ -3,6 +3,7 @@ import type { Meta, StoryObj } from "@storybook/react"
import { Card } from "./Card"
import { ActionButton } from "../Button/Button"
import { RiMenuAddLine, RiBookmarkLine } from "@remixicon/react"
+import { withRouter } from "storybook-addon-react-router-v6"
const meta: Meta = {
title: "ol-components/Card",
@@ -42,6 +43,7 @@ const meta: Meta = {
Footer
),
+ decorators: [withRouter],
}
export default meta
@@ -66,7 +68,7 @@ export const NoSize: Story = {
export const LinkCard: Story = {
args: {
- link: true,
+ href: "#link",
size: "medium",
},
}
From 646cc432b3b1fc1ecef46fd04f35d797a0ab8e7d Mon Sep 17 00:00:00 2001
From: Jon Kafton <939376+jonkafton@users.noreply.github.com>
Date: Wed, 12 Jun 2024 22:16:48 +0200
Subject: [PATCH 17/25] Default image for list cards
---
.../LearningResourceCard.tsx | 6 +++++
.../SearchDisplay/SearchDisplay.tsx | 1 +
.../src/pages/HomePage/NewsEventsSection.tsx | 6 +++--
.../LearningPathListingPage.tsx | 2 +-
.../src/components/Card/Card.tsx | 17 +++++++-----
.../src/components/Card/ListCard.tsx | 18 ++-----------
.../LearningResourceListCard.tsx | 27 ++++++++++---------
7 files changed, 39 insertions(+), 38 deletions(-)
diff --git a/frontends/mit-open/src/page-components/LearningResourceCard/LearningResourceCard.tsx b/frontends/mit-open/src/page-components/LearningResourceCard/LearningResourceCard.tsx
index cd0686c53c..52689ea190 100644
--- a/frontends/mit-open/src/page-components/LearningResourceCard/LearningResourceCard.tsx
+++ b/frontends/mit-open/src/page-components/LearningResourceCard/LearningResourceCard.tsx
@@ -1,3 +1,9 @@
+/*
+ * TODO: This has been replaced by the ol-components LearningResourceCard
+ * It is still in use by the LearningPathDetailsPage -> ListDetails -> ItemsListing
+ * though can be removed (and adjacent LearningResourceCardTemplate) once
+ * the sorting functionality has been refactored across
+ */
import React, { useCallback } from "react"
import * as NiceModal from "@ebay/nice-modal-react"
diff --git a/frontends/mit-open/src/page-components/SearchDisplay/SearchDisplay.tsx b/frontends/mit-open/src/page-components/SearchDisplay/SearchDisplay.tsx
index 1df1fba7cd..a03c022bee 100644
--- a/frontends/mit-open/src/page-components/SearchDisplay/SearchDisplay.tsx
+++ b/frontends/mit-open/src/page-components/SearchDisplay/SearchDisplay.tsx
@@ -200,6 +200,7 @@ export const FacetsTitleContainer = styled.div`
const PaginationContainer = styled.div`
display: flex;
justify-content: end;
+ margin-top: 16px;
`
const PAGE_SIZE = 10
diff --git a/frontends/mit-open/src/pages/HomePage/NewsEventsSection.tsx b/frontends/mit-open/src/pages/HomePage/NewsEventsSection.tsx
index c07ea7378c..c1edbdea68 100644
--- a/frontends/mit-open/src/pages/HomePage/NewsEventsSection.tsx
+++ b/frontends/mit-open/src/pages/HomePage/NewsEventsSection.tsx
@@ -80,7 +80,6 @@ const StoryCard = styled(Card)<{ mobile: boolean }>`
display: flex;
flex-direction: column;
flex-shrink: 0;
- overflow: hidden;
${({ mobile }) => (mobile ? "width: 274px" : "")}
`
@@ -110,9 +109,12 @@ const EventCard = styled(Card)`
gap: 16px;
flex: 1 0 0;
align-self: stretch;
- padding: 16px;
justify-content: space-between;
overflow: visible;
+
+ > a {
+ padding: 16px;
+ }
`
const EventDate = styled.div`
diff --git a/frontends/mit-open/src/pages/LearningPathListingPage/LearningPathListingPage.tsx b/frontends/mit-open/src/pages/LearningPathListingPage/LearningPathListingPage.tsx
index 594c068587..3df4abdcf5 100644
--- a/frontends/mit-open/src/pages/LearningPathListingPage/LearningPathListingPage.tsx
+++ b/frontends/mit-open/src/pages/LearningPathListingPage/LearningPathListingPage.tsx
@@ -104,7 +104,7 @@ const LearningPathListingPage: React.FC = () => {
Learning Paths
-
+
diff --git a/frontends/ol-components/src/components/Card/Card.tsx b/frontends/ol-components/src/components/Card/Card.tsx
index f25cffa642..392c9e069a 100644
--- a/frontends/ol-components/src/components/Card/Card.tsx
+++ b/frontends/ol-components/src/components/Card/Card.tsx
@@ -12,8 +12,12 @@ import { Link } from "react-router-dom"
export type Size = "small" | "medium"
-// Relative positioned wrapper to position action buttons outside of the child Link (buttons inside anchors is not valid HTML)
-const Wrapper = styled.div<{ size?: Size }>`
+/*
+ *The relative positioned wrapper allows the action buttons to live adjacent to the
+ * Link container in the DOM structure. They cannot be a descendent of it as
+ * buttons inside anchors are not valid HTML.
+ */
+export const Wrapper = styled.div<{ size?: Size }>`
position: relative;
${({ size }) => {
let width
@@ -27,18 +31,17 @@ const Wrapper = styled.div<{ size?: Size }>`
}}
`
-const containerStyles = `
+export const containerStyles = `
border-radius: 8px;
border: 1px solid ${theme.custom.colors.lightGray2};
background: ${theme.custom.colors.white};
overflow: hidden;
-
- display: block;
- position: relative;
`
const LinkContainer = styled(Link)`
${containerStyles}
+ display: block;
+ position: relative;
:hover {
text-decoration: none;
@@ -52,6 +55,8 @@ const LinkContainer = styled(Link)`
const Container = styled.div`
${containerStyles}
+ display: block;
+ position: relative;
`
const Content = () => <>>
diff --git a/frontends/ol-components/src/components/Card/ListCard.tsx b/frontends/ol-components/src/components/Card/ListCard.tsx
index d2e2a404bb..3b615b4812 100644
--- a/frontends/ol-components/src/components/Card/ListCard.tsx
+++ b/frontends/ol-components/src/components/Card/ListCard.tsx
@@ -8,18 +8,7 @@ import React, {
import styled from "@emotion/styled"
import { theme } from "../ThemeProvider/ThemeProvider"
import { Link } from "react-router-dom"
-
-// Relative positioned wrapper to position action buttons outside of the child Link (buttons inside anchors is not valid HTML)
-const Wrapper = styled.div`
- position: relative;
-`
-
-const containerStyles = `
- border-radius: 8px;
- border: 1px solid ${theme.custom.colors.lightGray2};
- background: ${theme.custom.colors.white};
- overflow: hidden;
-`
+import { Wrapper, containerStyles } from "./Card"
const LinkContainer = styled(Link)`
${containerStyles}
@@ -56,14 +45,11 @@ const Body = styled.div`
const Image = styled.img`
display: block;
- background-size: cover;
- background-repeat: no-repeat;
- -webkit-background-position: center;
- background-position: center;
width: 236px;
height: 122px;
margin: 24px 24px 24px 0;
border-radius: 4px;
+ object-fit: cover;
${theme.breakpoints.down("md")} {
width: 111px;
height: 104px;
diff --git a/frontends/ol-components/src/components/LearningResourceCard/LearningResourceListCard.tsx b/frontends/ol-components/src/components/LearningResourceCard/LearningResourceListCard.tsx
index 04a69c387c..04ad148d57 100644
--- a/frontends/ol-components/src/components/LearningResourceCard/LearningResourceListCard.tsx
+++ b/frontends/ol-components/src/components/LearningResourceCard/LearningResourceListCard.tsx
@@ -8,6 +8,7 @@ import {
formatDate,
getReadableResourceType,
embedlyCroppedImage,
+ DEFAULT_RESOURCE_IMG,
pluralize,
} from "ol-utilities"
import { ListCard } from "../Card/ListCard"
@@ -91,13 +92,11 @@ const StyledActionButton = styled(ActionButton)<{ edge: string }>`
type ResourceIdCallback = (resourceId: number) => void
-const getEmbedlyUrl = (resource: LearningResource, isMobile: boolean) => {
- return resource?.image?.url
- ? embedlyCroppedImage(resource?.image?.url, {
- key: APP_SETTINGS.embedlyKey || process.env.EMBEDLY_KEY!,
- ...IMAGE_SIZES[isMobile ? "mobile" : "desktop"],
- })
- : null
+const getEmbedlyUrl = (url: string, isMobile: boolean) => {
+ return embedlyCroppedImage(url, {
+ key: APP_SETTINGS.embedlyKey || process.env.EMBEDLY_KEY!,
+ ...IMAGE_SIZES[isMobile ? "mobile" : "desktop"],
+ })
}
const getPrice = (resource: LearningResource) => {
@@ -269,12 +268,14 @@ const LearningResourceListCard: React.FC = ({
}
return (
- {resource.image && (
-
- )}
+
From 7645b4c67bdb1dd06204714cef06d33fb71d76c8 Mon Sep 17 00:00:00 2001
From: Jon Kafton <939376+jonkafton@users.noreply.github.com>
Date: Wed, 12 Jun 2024 23:06:35 +0200
Subject: [PATCH 18/25] Provide actions buttons to search items
---
.../SearchDisplay/SearchDisplay.tsx | 33 +++++++++++++++----
1 file changed, 27 insertions(+), 6 deletions(-)
diff --git a/frontends/mit-open/src/page-components/SearchDisplay/SearchDisplay.tsx b/frontends/mit-open/src/page-components/SearchDisplay/SearchDisplay.tsx
index a03c022bee..c1f21c0667 100644
--- a/frontends/mit-open/src/page-components/SearchDisplay/SearchDisplay.tsx
+++ b/frontends/mit-open/src/page-components/SearchDisplay/SearchDisplay.tsx
@@ -13,15 +13,13 @@ import {
css,
LearningResourceListCard,
} from "ol-components"
-
import TuneIcon from "@mui/icons-material/Tune"
-
+import * as NiceModal from "@ebay/nice-modal-react"
import {
LearningResourcesSearchApiLearningResourcesSearchRetrieveRequest as LRSearchRequest,
ResourceTypeEnum,
} from "api"
import { useLearningResourcesSearch } from "api/hooks/learningResources"
-
import { GridColumn, GridContainer } from "@/components/GridLayout/GridLayout"
import {
AvailableFacets,
@@ -34,11 +32,14 @@ import type {
FacetManifest,
} from "@mitodl/course-search-utils"
import _ from "lodash"
-
import { ResourceTypeTabs } from "./ResourceTypeTabs"
import ProfessionalToggle from "./ProfessionalToggle"
-
import type { TabConfig } from "./ResourceTypeTabs"
+import { useUserMe } from "api/hooks/user"
+import {
+ AddToLearningPathDialog,
+ AddToUserListDialog,
+} from "../Dialogs/AddToListDialog"
export const StyledDropdown = styled(SimpleSelect)`
margin: 8px;
@@ -289,6 +290,22 @@ const SearchDisplay: React.FC = ({
},
{ keepPreviousData: true },
)
+
+ const { data: user } = useUserMe()
+
+ const showAddToLearningPathDialog =
+ user?.is_authenticated && user?.is_learning_path_editor
+ ? (resourceId: number) => {
+ NiceModal.show(AddToLearningPathDialog, { resourceId })
+ }
+ : null
+
+ const showAddToUserListDialog = user?.is_authenticated
+ ? (resourceId: number) => {
+ NiceModal.show(AddToUserListDialog, { resourceId })
+ }
+ : null
+
return (
@@ -361,7 +378,11 @@ const SearchDisplay: React.FC = ({
{data.results.map((resource) => (
-
+
))}
From cb85dc2e46abc99ff546f717048db93d763529c7 Mon Sep 17 00:00:00 2001
From: Jon Kafton <939376+jonkafton@users.noreply.github.com>
Date: Wed, 12 Jun 2024 23:07:30 +0200
Subject: [PATCH 19/25] Position actions dependent on image
---
frontends/ol-components/src/components/Card/ListCard.tsx | 8 ++++----
.../LearningResourceListCard.stories.tsx | 5 +----
2 files changed, 5 insertions(+), 8 deletions(-)
diff --git a/frontends/ol-components/src/components/Card/ListCard.tsx b/frontends/ol-components/src/components/Card/ListCard.tsx
index 3b615b4812..06546ab5a5 100644
--- a/frontends/ol-components/src/components/Card/ListCard.tsx
+++ b/frontends/ol-components/src/components/Card/ListCard.tsx
@@ -118,15 +118,15 @@ const Bottom = styled.div`
}
`
-const Actions = styled.div`
+const Actions = styled.div<{ hasImage?: boolean }>`
display: flex;
gap: 8px;
position: absolute;
bottom: 24px;
- right: 24px;
+ right: ${({ hasImage }) => (hasImage ? "284px" : "24px")};
${theme.breakpoints.down("md")} {
bottom: 12px;
- right: 12px;
+ right: ${({ hasImage }) => (hasImage ? "124px" : "12px")};
}
`
@@ -181,7 +181,7 @@ const ListCard: Card = ({ children, className, href }) => {
)} />
)}
- {actions && {actions}}
+ {actions && {actions}}
)
}
diff --git a/frontends/ol-components/src/components/LearningResourceCard/LearningResourceListCard.stories.tsx b/frontends/ol-components/src/components/LearningResourceCard/LearningResourceListCard.stories.tsx
index 208c3b28bd..d8b7551205 100644
--- a/frontends/ol-components/src/components/LearningResourceCard/LearningResourceListCard.stories.tsx
+++ b/frontends/ol-components/src/components/LearningResourceCard/LearningResourceListCard.stories.tsx
@@ -2,7 +2,6 @@ import React from "react"
import type { Meta, StoryObj } from "@storybook/react"
import { LearningResourceListCard } from "./LearningResourceListCard"
import { ResourceTypeEnum } from "api"
-import styled from "@emotion/styled"
import { factories } from "api/test-utils"
import { withRouter } from "storybook-addon-react-router-v6"
@@ -15,8 +14,6 @@ const makeResource: typeof _makeResource = (overrides) => {
return resource
}
-const LearningResourceListCardStyled = styled(LearningResourceListCard)``
-
const meta: Meta = {
title: "ol-components/LearningResourceListCard",
argTypes: {
@@ -61,7 +58,7 @@ const meta: Meta = {
onAddToLearningPathClick,
onAddToUserListClick,
}) => (
-
Date: Thu, 13 Jun 2024 12:08:14 +0200
Subject: [PATCH 20/25] Test for list variant
---
.../src/components/Button/Button.stories.tsx | 10 +-
.../src/components/Button/Button.tsx | 1 +
.../src/components/Card/ListCard.tsx | 4 +
.../LearningResourceCard.test.tsx | 5 -
.../LearningResourceListCard.test.tsx | 174 ++++++++++++++++++
.../LearningResourceListCard.tsx | 2 +-
6 files changed, 187 insertions(+), 9 deletions(-)
create mode 100644 frontends/ol-components/src/components/LearningResourceCard/LearningResourceListCard.test.tsx
diff --git a/frontends/ol-components/src/components/Button/Button.stories.tsx b/frontends/ol-components/src/components/Button/Button.stories.tsx
index bd022275c9..b99dcbde45 100644
--- a/frontends/ol-components/src/components/Button/Button.stories.tsx
+++ b/frontends/ol-components/src/components/Button/Button.stories.tsx
@@ -34,7 +34,7 @@ const meta: Meta = {
control: { type: "select" },
},
edge: {
- options: ["circular", "rounded"],
+ options: ["circular", "rounded", "none"],
control: { type: "select" },
},
startIcon: {
@@ -144,6 +144,9 @@ export const EdgeStory: Story = {
+
),
}
@@ -187,7 +190,7 @@ export const IconOnlyStory: Story = {
}
const SIZES = ["small", "medium", "large"] as const
-const EDGES = ["rounded", "circular"] as const
+const EDGES = ["rounded", "circular", "none"] as const
const VARIANTS = ["primary", "secondary", "tertiary", "text"] as const
const EXTRA_PROPS = [
{},
@@ -262,7 +265,7 @@ const ICONS = [
},
]
export const ActionButtonsShowcase: Story = {
- render: () => (
+ render: (args) => (
<>
{VARIANTS.flatMap((variant) =>
EDGES.flatMap((edge) => (
@@ -281,6 +284,7 @@ export const ActionButtonsShowcase: Story = {
variant={variant}
edge={edge}
size={size}
+ {...args}
>
{icon.component}
diff --git a/frontends/ol-components/src/components/Button/Button.tsx b/frontends/ol-components/src/components/Button/Button.tsx
index c1f20c614c..d0a1603223 100644
--- a/frontends/ol-components/src/components/Button/Button.tsx
+++ b/frontends/ol-components/src/components/Button/Button.tsx
@@ -135,6 +135,7 @@ const ButtonStyled = styled.button((props) => {
},
},
},
+ // color
color === "secondary" && {
color: theme.custom.colors.silverGray,
borderColor: theme.custom.colors.silverGray,
diff --git a/frontends/ol-components/src/components/Card/ListCard.tsx b/frontends/ol-components/src/components/Card/ListCard.tsx
index 06546ab5a5..ad1bcf0f01 100644
--- a/frontends/ol-components/src/components/Card/ListCard.tsx
+++ b/frontends/ol-components/src/components/Card/ListCard.tsx
@@ -106,6 +106,8 @@ const Footer = styled.span`
span {
color: ${theme.custom.colors.black};
}
+
+ white-space: nowrap;
`
const Bottom = styled.div`
@@ -128,6 +130,8 @@ const Actions = styled.div<{ hasImage?: boolean }>`
bottom: 12px;
right: ${({ hasImage }) => (hasImage ? "124px" : "12px")};
}
+
+ background-color: ${theme.custom.colors.white};
`
type CardProps = {
diff --git a/frontends/ol-components/src/components/LearningResourceCard/LearningResourceCard.test.tsx b/frontends/ol-components/src/components/LearningResourceCard/LearningResourceCard.test.tsx
index 152bc64eaa..9a5b62a750 100644
--- a/frontends/ol-components/src/components/LearningResourceCard/LearningResourceCard.test.tsx
+++ b/frontends/ol-components/src/components/LearningResourceCard/LearningResourceCard.test.tsx
@@ -107,11 +107,6 @@ describe("Learning Resource Card", () => {
{ wrapper: ThemeProvider },
)
- // const heading = screen.getByRole("heading", { name: resource.title })
- // act(async () => {
- // await heading.click()
- // })
-
const addToLearningPathButton = screen.getByLabelText(
"Add to Learning Path",
)
diff --git a/frontends/ol-components/src/components/LearningResourceCard/LearningResourceListCard.test.tsx b/frontends/ol-components/src/components/LearningResourceCard/LearningResourceListCard.test.tsx
new file mode 100644
index 0000000000..93c3134b53
--- /dev/null
+++ b/frontends/ol-components/src/components/LearningResourceCard/LearningResourceListCard.test.tsx
@@ -0,0 +1,174 @@
+import React from "react"
+import { BrowserRouter } from "react-router-dom"
+import { screen, render, act } from "@testing-library/react"
+import { LearningResourceListCard } from "./LearningResourceListCard"
+import { DEFAULT_RESOURCE_IMG, embedlyCroppedImage } from "ol-utilities"
+import { LearningResource, ResourceTypeEnum, PlatformEnum } from "api"
+import { factories } from "api/test-utils"
+import { ThemeProvider } from "../ThemeProvider/ThemeProvider"
+
+const setup = (resource: LearningResource) => {
+ return render(
+
+
+ ,
+ { wrapper: ThemeProvider },
+ )
+}
+
+describe("Learning Resource List Card", () => {
+ test("Renders resource type, title and start date", () => {
+ const resource = factories.learningResources.resource({
+ resource_type: ResourceTypeEnum.Course,
+ next_start_date: "2026-01-01",
+ })
+
+ setup(resource)
+
+ screen.getByText("Course")
+ screen.getByRole("heading", { name: resource.title })
+ screen.getByText("Starts:")
+ screen.getByText("January 01, 2026")
+ })
+
+ test("Displays run start date", () => {
+ const resource = factories.learningResources.resource({
+ resource_type: ResourceTypeEnum.Course,
+ next_start_date: null,
+ runs: [
+ factories.learningResources.run({
+ start_date: "2026-01-01",
+ }),
+ ],
+ })
+
+ setup(resource)
+
+ screen.getByText("Starts:")
+ screen.getByText("January 01, 2026")
+ })
+
+ test("Displays taught in date for OCW", () => {
+ const resource = factories.learningResources.resource({
+ resource_type: ResourceTypeEnum.Course,
+ platform: { code: PlatformEnum.Ocw },
+ runs: [
+ factories.learningResources.run({
+ semester: "Fall",
+ year: 2002,
+ }),
+ ],
+ })
+
+ setup(resource)
+
+ expect(screen.getByRole("link")).toHaveTextContent("As taught in:")
+ expect(screen.getByRole("link")).toHaveTextContent("Fall 2002")
+ })
+
+ test("Click to navigate", async () => {
+ const resource = factories.learningResources.resource({
+ resource_type: ResourceTypeEnum.Course,
+ platform: { code: PlatformEnum.Ocw },
+ })
+
+ render(
+
+
+ ,
+ { wrapper: ThemeProvider },
+ )
+
+ const heading = screen.getByRole("heading", { name: resource.title })
+ await act(async () => {
+ await heading.click()
+ })
+
+ expect(window.location.search).toBe(`?resource=${resource.id}`)
+ })
+
+ test("Click action buttons", async () => {
+ const resource = factories.learningResources.resource({
+ resource_type: ResourceTypeEnum.Course,
+ platform: { code: PlatformEnum.Ocw },
+ })
+
+ const onAddToLearningPathClick = jest.fn()
+ const onAddToUserListClick = jest.fn()
+
+ render(
+
+
+ ,
+ { wrapper: ThemeProvider },
+ )
+
+ const addToLearningPathButton = screen.getByLabelText(
+ "Add to Learning Path",
+ )
+ await addToLearningPathButton.click()
+
+ const addToUserListButton = screen.getByLabelText("Add to User List")
+ await addToUserListButton.click()
+
+ expect(onAddToLearningPathClick).toHaveBeenCalledWith(resource.id)
+ expect(onAddToUserListClick).toHaveBeenCalledWith(resource.id)
+ })
+
+ test("Displays certificate badge", () => {
+ const resource = factories.learningResources.resource({
+ certification: true,
+ })
+
+ setup(resource)
+
+ screen.getByText("Certificate")
+ })
+
+ test("Does not display certificate badge", () => {
+ const resource = factories.learningResources.resource({
+ certification: false,
+ })
+
+ setup(resource)
+
+ const badge = screen.queryByText("Certificate")
+
+ expect(badge).not.toBeInTheDocument()
+ })
+
+ test.each([
+ { image: null, expected: { src: DEFAULT_RESOURCE_IMG, alt: "" } },
+ {
+ image: { url: "https://example.com/image.jpg", alt: "An image" },
+ expected: { src: "https://example.com/image.jpg", alt: "An image" },
+ },
+ {
+ image: { url: "https://example.com/image.jpg", alt: null },
+ expected: { src: "https://example.com/image.jpg", alt: "" },
+ },
+ ])("Image is displayed if present", ({ expected, image }) => {
+ const resource = factories.learningResources.resource({ image })
+
+ setup(resource)
+
+ const imageEls = screen.getAllByRole("img")
+
+ const matching = imageEls.filter((im) =>
+ expected.src === DEFAULT_RESOURCE_IMG
+ ? im.src === DEFAULT_RESOURCE_IMG
+ : im.src ===
+ embedlyCroppedImage(expected.src, {
+ width: 116,
+ height: 104,
+ key: "fake-embedly-key",
+ }),
+ )
+ expect(matching.length).toBe(1)
+ expect(matching[0]).toHaveAttribute("alt", expected.alt)
+ })
+})
diff --git a/frontends/ol-components/src/components/LearningResourceCard/LearningResourceListCard.tsx b/frontends/ol-components/src/components/LearningResourceCard/LearningResourceListCard.tsx
index 04ad148d57..9450867fb2 100644
--- a/frontends/ol-components/src/components/LearningResourceCard/LearningResourceListCard.tsx
+++ b/frontends/ol-components/src/components/LearningResourceCard/LearningResourceListCard.tsx
@@ -274,7 +274,7 @@ const LearningResourceListCard: React.FC = ({
? getEmbedlyUrl(resource.image.url!, isMobile)
: DEFAULT_RESOURCE_IMG
}
- alt={resource.image?.alt as string}
+ alt={resource.image?.alt ?? ""}
/>
From 41103a011d51214e608de1b25b05ec36e7159df7 Mon Sep 17 00:00:00 2001
From: Jon Kafton <939376+jonkafton@users.noreply.github.com>
Date: Fri, 14 Jun 2024 17:42:00 +0200
Subject: [PATCH 21/25] Relative API base path for local
---
frontends/api/src/clients.ts | 6 +++++-
1 file changed, 5 insertions(+), 1 deletion(-)
diff --git a/frontends/api/src/clients.ts b/frontends/api/src/clients.ts
index b0bcfc6e32..b1d4fb51a6 100644
--- a/frontends/api/src/clients.ts
+++ b/frontends/api/src/clients.ts
@@ -24,7 +24,11 @@ import {
import axiosInstance from "./axios"
-const BASE_PATH = process.env.MITOPEN_AXIOS_BASE_PATH?.replace(/\/+$/, "") ?? ""
+const BASE_PATH =
+ process.env.ENVIRONMENT === "local"
+ ? ""
+ : process.env.MITOPEN_AXIOS_BASE_PATH?.replace(/\/+$/, "") ?? ""
+
const learningResourcesApi = new LearningResourcesApi(
undefined,
BASE_PATH,
From 0da8dc8c16e2e141c473bf2c40f0788be041ef7f Mon Sep 17 00:00:00 2001
From: Jon Kafton <939376+jonkafton@users.noreply.github.com>
Date: Fri, 14 Jun 2024 17:43:12 +0200
Subject: [PATCH 22/25] Use TruncatedText for title
---
.../src/components/Card/ListCard.tsx | 15 +++++----------
.../LearningResourceListCard.tsx | 9 +--------
.../src/components/TruncateText/TruncateText.tsx | 6 ++++++
3 files changed, 12 insertions(+), 18 deletions(-)
diff --git a/frontends/ol-components/src/components/Card/ListCard.tsx b/frontends/ol-components/src/components/Card/ListCard.tsx
index ad1bcf0f01..bff62862e8 100644
--- a/frontends/ol-components/src/components/Card/ListCard.tsx
+++ b/frontends/ol-components/src/components/Card/ListCard.tsx
@@ -9,6 +9,7 @@ import styled from "@emotion/styled"
import { theme } from "../ThemeProvider/ThemeProvider"
import { Link } from "react-router-dom"
import { Wrapper, containerStyles } from "./Card"
+import { TruncateText } from "../TruncateText/TruncateText"
const LinkContainer = styled(Link)`
${containerStyles}
@@ -81,19 +82,11 @@ const Title = styled.h3`
${{ ...theme.typography.subtitle1 }}
height: ${theme.typography.pxToRem(40)};
${theme.breakpoints.down("md")} {
- ${{ ...theme.typography.body3 }}
+ ${{ ...theme.typography.subtitle3 }}
height: ${theme.typography.pxToRem(32)};
}
- overflow: hidden;
margin: 0;
-
- @supports (-webkit-line-clamp: 2) {
- white-space: initial;
- display: -webkit-box;
- -webkit-line-clamp: 2;
- -webkit-box-orient: vertical;
- }
`
const Footer = styled.span`
@@ -176,7 +169,9 @@ const ListCard: Card = ({ children, className, href }) => {
<_Container to={href!}>
{info}
- {title}
+
+ {title}
+
diff --git a/frontends/ol-components/src/components/LearningResourceCard/LearningResourceListCard.tsx b/frontends/ol-components/src/components/LearningResourceCard/LearningResourceListCard.tsx
index 9450867fb2..373618685e 100644
--- a/frontends/ol-components/src/components/LearningResourceCard/LearningResourceListCard.tsx
+++ b/frontends/ol-components/src/components/LearningResourceCard/LearningResourceListCard.tsx
@@ -12,7 +12,6 @@ import {
pluralize,
} from "ol-utilities"
import { ListCard } from "../Card/ListCard"
-import { TruncateText } from "../TruncateText/TruncateText"
import { ActionButton } from "../Button/Button"
import { theme } from "../ThemeProvider/ThemeProvider"
import { useMuiBreakpointAtLeast } from "../../hooks/useBreakpoint"
@@ -22,10 +21,6 @@ const IMAGE_SIZES = {
desktop: { width: 236, height: 122 },
}
-const EllipsisTitle = styled(TruncateText)({
- margin: 0,
-})
-
const Certificate = styled.div`
border-radius: 4px;
background-color: ${theme.custom.colors.lightGray1};
@@ -279,9 +274,7 @@ const LearningResourceListCard: React.FC = ({
-
- {resource.title}
-
+ {resource.title}
{onAddToLearningPathClick && (
overflow: "hidden",
textOverflow: "ellipsis",
WebkitLineClamp: lines,
+ [`@supports (-webkit-line-clamp: ${lines})`]: {
+ whiteSpace: "initial",
+ display: "-webkit-box",
+ "-webkit-line-clamp": `${lines}`, // cast to any to avoid typechecking error in lines,
+ "-webkit-box-orient": "vertical",
+ },
})
/**
From 08f303b21ea31c6b07441b724078e9952044ccb0 Mon Sep 17 00:00:00 2001
From: Jon Kafton <939376+jonkafton@users.noreply.github.com>
Date: Fri, 14 Jun 2024 18:24:28 +0200
Subject: [PATCH 23/25] Hook to provide open drawer href to list component
---
.../LearningResourceDrawer/LearningResourceDrawer.tsx | 11 ++++++++++-
.../page-components/SearchDisplay/SearchDisplay.tsx | 4 ++++
.../LearningResourceListCard.stories.tsx | 1 +
.../LearningResourceCard/LearningResourceListCard.tsx | 2 +-
4 files changed, 16 insertions(+), 2 deletions(-)
diff --git a/frontends/mit-open/src/page-components/LearningResourceDrawer/LearningResourceDrawer.tsx b/frontends/mit-open/src/page-components/LearningResourceDrawer/LearningResourceDrawer.tsx
index e509db5ef8..a4cd859725 100644
--- a/frontends/mit-open/src/page-components/LearningResourceDrawer/LearningResourceDrawer.tsx
+++ b/frontends/mit-open/src/page-components/LearningResourceDrawer/LearningResourceDrawer.tsx
@@ -93,5 +93,14 @@ const useOpenLearningResourceDrawer = () => {
return openLearningResourceDrawer
}
+const useResourceDrawerHref = () => {
+ const [search] = useSearchParams()
+
+ return (id: number) => {
+ search.set(RESOURCE_DRAWER_QUERY_PARAM, id.toString())
+ return `?${search.toString()}`
+ }
+}
+
export default LearningResourceDrawer
-export { useOpenLearningResourceDrawer }
+export { useOpenLearningResourceDrawer, useResourceDrawerHref }
diff --git a/frontends/mit-open/src/page-components/SearchDisplay/SearchDisplay.tsx b/frontends/mit-open/src/page-components/SearchDisplay/SearchDisplay.tsx
index c1f21c0667..5fd17cd75e 100644
--- a/frontends/mit-open/src/page-components/SearchDisplay/SearchDisplay.tsx
+++ b/frontends/mit-open/src/page-components/SearchDisplay/SearchDisplay.tsx
@@ -40,6 +40,7 @@ import {
AddToLearningPathDialog,
AddToUserListDialog,
} from "../Dialogs/AddToListDialog"
+import { useResourceDrawerHref } from "@/page-components/LearningResourceDrawer/LearningResourceDrawer"
export const StyledDropdown = styled(SimpleSelect)`
margin: 8px;
@@ -293,6 +294,8 @@ const SearchDisplay: React.FC = ({
const { data: user } = useUserMe()
+ const getDrawerHref = useResourceDrawerHref()
+
const showAddToLearningPathDialog =
user?.is_authenticated && user?.is_learning_path_editor
? (resourceId: number) => {
@@ -380,6 +383,7 @@ const SearchDisplay: React.FC = ({
diff --git a/frontends/ol-components/src/components/LearningResourceCard/LearningResourceListCard.stories.tsx b/frontends/ol-components/src/components/LearningResourceCard/LearningResourceListCard.stories.tsx
index d8b7551205..716fce9c09 100644
--- a/frontends/ol-components/src/components/LearningResourceCard/LearningResourceListCard.stories.tsx
+++ b/frontends/ol-components/src/components/LearningResourceCard/LearningResourceListCard.stories.tsx
@@ -61,6 +61,7 @@ const meta: Meta = {
diff --git a/frontends/ol-components/src/components/LearningResourceCard/LearningResourceListCard.tsx b/frontends/ol-components/src/components/LearningResourceCard/LearningResourceListCard.tsx
index 373618685e..16f5c9a843 100644
--- a/frontends/ol-components/src/components/LearningResourceCard/LearningResourceListCard.tsx
+++ b/frontends/ol-components/src/components/LearningResourceCard/LearningResourceListCard.tsx
@@ -262,7 +262,7 @@ const LearningResourceListCard: React.FC = ({
return null
}
return (
-
+
Date: Fri, 14 Jun 2024 18:35:33 +0200
Subject: [PATCH 24/25] Replace breakpoint hook with css
---
.../LearningPathListingPage.tsx | 30 +++++++------------
1 file changed, 10 insertions(+), 20 deletions(-)
diff --git a/frontends/mit-open/src/pages/LearningPathListingPage/LearningPathListingPage.tsx b/frontends/mit-open/src/pages/LearningPathListingPage/LearningPathListingPage.tsx
index 3df4abdcf5..af8adef293 100644
--- a/frontends/mit-open/src/pages/LearningPathListingPage/LearningPathListingPage.tsx
+++ b/frontends/mit-open/src/pages/LearningPathListingPage/LearningPathListingPage.tsx
@@ -11,7 +11,7 @@ import {
Typography,
PlainList,
LearningResourceListCard,
- useMuiBreakpointAtLeast,
+ theme,
} from "ol-components"
import type { SimpleMenuItem } from "ol-components"
import EditIcon from "@mui/icons-material/Edit"
@@ -33,21 +33,16 @@ const ListHeaderGrid = styled(Grid)`
margin-bottom: 1rem;
`
-const StyledActionButton = styled(ActionButton)<{ mobile: boolean }>`
- ${({ mobile }) =>
- mobile
- ? `
- width: 16px;
- height: 16px;`
- : ""}
+const StyledActionButton = styled(ActionButton)`
+ ${theme.breakpoints.down("md")} {
+ width: 16px;
+ height: 16px;
+ }
`
-type EditListMenuProps = {
- resource: LearningPathResource
- isMobile: boolean
-}
-
-const EditListMenu: React.FC = ({ resource, isMobile }) => {
+const EditListMenu: React.FC<{ resource: LearningPathResource }> = ({
+ resource,
+}) => {
const items: SimpleMenuItem[] = useMemo(
() => [
{
@@ -73,7 +68,6 @@ const EditListMenu: React.FC = ({ resource, isMobile }) => {
edge="none"
color="secondary"
size="small"
- mobile={isMobile}
aria-label={`Edit list ${resource.title}`}
>
@@ -85,7 +79,6 @@ const EditListMenu: React.FC = ({ resource, isMobile }) => {
}
const LearningPathListingPage: React.FC = () => {
- const isMobile = !useMuiBreakpointAtLeast("md")
const listingQuery = useLearningPathsList()
const { data: user } = useUserMe()
@@ -138,10 +131,7 @@ const LearningPathListingPage: React.FC = () => {
href={urls.learningPathsView(resource.id)}
editMenu={
canEdit ? (
-
+
) : null
}
/>
From dde11d3bcf9d0281eead876e51914b26da28b2f0 Mon Sep 17 00:00:00 2001
From: Jon Kafton <939376+jonkafton@users.noreply.github.com>
Date: Fri, 14 Jun 2024 20:05:17 +0200
Subject: [PATCH 25/25] Fix test (bad css). Apply drawer href hook to Cards
---
frontends/api/src/clients.ts | 5 +----
.../ResourceCarousel/ResourceCarousel.tsx | 3 +++
.../ol-components/src/components/Card/Card.tsx | 13 +++++++++++++
.../LearningResourceCard.test.tsx | 12 +++++-------
.../LearningResourceCard/LearningResourceCard.tsx | 4 +++-
.../LearningResourceListCard.test.tsx | 12 +++++-------
.../src/components/TruncateText/TruncateText.tsx | 4 ++--
7 files changed, 32 insertions(+), 21 deletions(-)
diff --git a/frontends/api/src/clients.ts b/frontends/api/src/clients.ts
index b1d4fb51a6..e5a3c8249b 100644
--- a/frontends/api/src/clients.ts
+++ b/frontends/api/src/clients.ts
@@ -24,10 +24,7 @@ import {
import axiosInstance from "./axios"
-const BASE_PATH =
- process.env.ENVIRONMENT === "local"
- ? ""
- : process.env.MITOPEN_AXIOS_BASE_PATH?.replace(/\/+$/, "") ?? ""
+const BASE_PATH = process.env.MITOPEN_AXIOS_BASE_PATH?.replace(/\/+$/, "") ?? ""
const learningResourcesApi = new LearningResourcesApi(
undefined,
diff --git a/frontends/mit-open/src/page-components/ResourceCarousel/ResourceCarousel.tsx b/frontends/mit-open/src/page-components/ResourceCarousel/ResourceCarousel.tsx
index 509cfeb4a9..9e5edb21b7 100644
--- a/frontends/mit-open/src/page-components/ResourceCarousel/ResourceCarousel.tsx
+++ b/frontends/mit-open/src/page-components/ResourceCarousel/ResourceCarousel.tsx
@@ -27,6 +27,7 @@ import {
AddToLearningPathDialog,
AddToUserListDialog,
} from "../Dialogs/AddToListDialog"
+import { useResourceDrawerHref } from "../LearningResourceDrawer/LearningResourceDrawer"
const StyledCarousel = styled(Carousel)({
/**
@@ -263,6 +264,7 @@ const ResourceCarousel: React.FC = ({
const { data: user } = useUserMe()
const [tab, setTab] = React.useState("0")
const [ref, setRef] = React.useState(null)
+ const getDrawerHref = useResourceDrawerHref()
const showAddToLearningPathDialog =
user?.is_authenticated && user?.is_learning_path_editor
@@ -315,6 +317,7 @@ const ResourceCarousel: React.FC = ({
key={resource.id}
resource={resource}
{...tabConfig.cardProps}
+ href={getDrawerHref(resource.id)}
onAddToLearningPathClick={showAddToLearningPathDialog}
onAddToUserListClick={showAddToUserListDialog}
/>
diff --git a/frontends/ol-components/src/components/Card/Card.tsx b/frontends/ol-components/src/components/Card/Card.tsx
index 392c9e069a..4d39564103 100644
--- a/frontends/ol-components/src/components/Card/Card.tsx
+++ b/frontends/ol-components/src/components/Card/Card.tsx
@@ -148,6 +148,19 @@ const Card: Card = ({ children, className, size, href }) => {
const _Container = href ? LinkContainer : Container
+ /*
+ * Allows rendering child elements to specific "slots":
+ *
+ *
+ * The Title
+ *
+ *
+ *
+ * Akin to alternative interface:
+ * The Title} image={} />.
+ *
+ * An RFC here provides rationale: https://github.com/nihgwu/rfcs/blob/neo/slots/text/0000-slots.md
+ */
Children.forEach(children, (child) => {
if (!isValidElement(child)) return
if (child.type === Content) content = child.props.children
diff --git a/frontends/ol-components/src/components/LearningResourceCard/LearningResourceCard.test.tsx b/frontends/ol-components/src/components/LearningResourceCard/LearningResourceCard.test.tsx
index 9a5b62a750..faafe7b105 100644
--- a/frontends/ol-components/src/components/LearningResourceCard/LearningResourceCard.test.tsx
+++ b/frontends/ol-components/src/components/LearningResourceCard/LearningResourceCard.test.tsx
@@ -10,7 +10,10 @@ import { ThemeProvider } from "../ThemeProvider/ThemeProvider"
const setup = (resource: LearningResource) => {
return render(
-
+
,
{ wrapper: ThemeProvider },
)
@@ -72,12 +75,7 @@ describe("Learning Resource Card", () => {
platform: { code: PlatformEnum.Ocw },
})
- render(
-
-
- ,
- { wrapper: ThemeProvider },
- )
+ setup(resource)
const heading = screen.getByRole("heading", { name: resource.title })
await act(async () => {
diff --git a/frontends/ol-components/src/components/LearningResourceCard/LearningResourceCard.tsx b/frontends/ol-components/src/components/LearningResourceCard/LearningResourceCard.tsx
index 3bf8d6490d..58712a9e77 100644
--- a/frontends/ol-components/src/components/LearningResourceCard/LearningResourceCard.tsx
+++ b/frontends/ol-components/src/components/LearningResourceCard/LearningResourceCard.tsx
@@ -116,6 +116,7 @@ interface LearningResourceCardProps {
resource?: LearningResource | null
className?: string
size?: Size
+ href?: string
onAddToLearningPathClick?: ResourceIdCallback | null
onAddToUserListClick?: ResourceIdCallback | null
}
@@ -125,6 +126,7 @@ const LearningResourceCard: React.FC = ({
resource,
className,
size = "medium",
+ href,
onAddToLearningPathClick,
onAddToUserListClick,
}) => {
@@ -145,7 +147,7 @@ const LearningResourceCard: React.FC = ({
return null
}
return (
-
+
{
return render(
-
+
,
{ wrapper: ThemeProvider },
)
@@ -72,12 +75,7 @@ describe("Learning Resource List Card", () => {
platform: { code: PlatformEnum.Ocw },
})
- render(
-
-
- ,
- { wrapper: ThemeProvider },
- )
+ setup(resource)
const heading = screen.getByRole("heading", { name: resource.title })
await act(async () => {
diff --git a/frontends/ol-components/src/components/TruncateText/TruncateText.tsx b/frontends/ol-components/src/components/TruncateText/TruncateText.tsx
index 93dbe61ae5..4f7cd33a25 100644
--- a/frontends/ol-components/src/components/TruncateText/TruncateText.tsx
+++ b/frontends/ol-components/src/components/TruncateText/TruncateText.tsx
@@ -18,8 +18,8 @@ const truncateText = (lines?: number | "none") =>
[`@supports (-webkit-line-clamp: ${lines})`]: {
whiteSpace: "initial",
display: "-webkit-box",
- "-webkit-line-clamp": `${lines}`, // cast to any to avoid typechecking error in lines,
- "-webkit-box-orient": "vertical",
+ WebkitLineClamp: `${lines}`, // cast to any to avoid typechecking error in lines,
+ WebkitBoxOrient: "vertical",
},
})