Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion frontends/api/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -29,7 +29,7 @@
"ol-test-utilities": "0.0.0"
},
"dependencies": {
"@mitodl/mitxonline-api-axios": "^2026.3.26",
"@mitodl/mitxonline-api-axios": "^2026.4.23",
"@tanstack/react-query": "^5.66.0",
"axios": "^1.12.2",
"tiny-invariant": "^1.3.3"
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -88,7 +88,11 @@ const programEnrollmentV3: PartialFactory<V3UserProgramEnrollment> = (
link: `/certificate/program/${faker.string.uuid()}/`,
}
: null,
enrollment_mode: faker.helpers.arrayElement(["audit", "verified", null]),
enrollment_mode: faker.helpers.arrayElement([
"audit",
"verified",
undefined,
]),
program: program,
}
return mergeOverrides<V3UserProgramEnrollment>(defaults, overrides)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@ const transactionLine = (
total_paid: faker.commerce.price({ min: 50, max: 500 }),
discount: "0.00",
price: faker.commerce.price({ min: 50, max: 500 }),
content_type: "",
...overrides,
})

Expand Down
52 changes: 52 additions & 0 deletions frontends/api/src/mitxonline/test-utils/factories/pages.ts
Original file line number Diff line number Diff line change
Expand Up @@ -164,6 +164,32 @@ const coursePageItem: PartialFactory<CoursePageItem> = (override) => {
],
video_url: faker.internet.url(),
what_you_learn: makeHTMLList(5),
how_youll_learn: [
{
key: "learn_by_doing",
icon: "IconComputerBulb",
title: "Learn by doing",
text: "Apply what you learn with interactive exercises, projects, and real-world case studies.",
},
{
key: "realworld_learning",
icon: "IconConnectedPeople",
title: "Real-world learning",
text: "Engage with real-world scenarios and examples to understand how concepts apply in practice.",
},
{
key: "learn_from_others",
icon: "IconConnectedPeople",
title: "Learn from others",
text: "Collaborate with peers and learn from their perspectives through group projects and discussions.",
},
{
key: "learn_on_demand",
icon: "IconComputerBulb",
title: "Learn on demand",
text: "Access course materials anytime, anywhere, and learn at your own pace.",
},
],
}
return mergeOverrides<CoursePageItem>(defaults, override)
}
Expand Down Expand Up @@ -215,6 +241,32 @@ const programPageItem: PartialFactory<ProgramPageItem> = (override) => {
faq_url: faker.internet.url(),
about: makeHTMLParagraph(3),
what_you_learn: makeHTMLList(5),
how_youll_learn: [
{
key: "learn_by_doing",
icon: "IconComputerBulb",
title: "Learn by doing",
text: "Apply what you learn with interactive exercises, projects, and real-world case studies.",
},
{
key: "realworld_learning",
icon: "IconConnectedPeople",
title: "Real-world learning",
text: "Engage with real-world scenarios and examples to understand how concepts apply in practice.",
},
{
key: "learn_from_others",
icon: "IconConnectedPeople",
title: "Learn from others",
text: "Collaborate with peers and learn from their perspectives through group projects and discussions.",
},
{
key: "learn_on_demand",
icon: "IconComputerBulb",
title: "Learn on demand",
text: "Access course materials anytime, anywhere, and learn at your own pace.",
},
],
feature_image: featureImage(),
video_url: faker.datatype.boolean() ? faker.internet.url() : null,
faculty_section_title: "Meet your instructors",
Expand Down
2 changes: 1 addition & 1 deletion frontends/main/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,7 @@
"@emotion/styled": "^11.11.0",
"@floating-ui/react": "^0.27.16",
"@mitodl/course-search-utils": "^3.5.2",
"@mitodl/mitxonline-api-axios": "^2026.3.26",
"@mitodl/mitxonline-api-axios": "^2026.4.23",
"@mitodl/smoot-design": "^6.24.0",
"@mui/material": "^6.4.5",
"@mui/material-nextjs": "^6.4.3",
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -157,7 +157,7 @@ describe("UnenrollProgramDialog", () => {

const programEnrollment =
mitxonline.factories.enrollment.programEnrollmentV3({
enrollment_mode: enrollmentMode,
enrollment_mode: enrollmentMode ? enrollmentMode : undefined,
program: { display_mode: displayMode } as never,
})

Expand Down
4 changes: 2 additions & 2 deletions frontends/main/src/app-pages/ProductPages/CoursePage.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,7 @@ import RawHTML from "./RawHTML"
import AboutSection from "./AboutSection"
import ProductPageTemplate from "./ProductPageTemplate"
import WhatYoullLearnSection from "./WhatYoullLearnSection"
import HowYoullLearnSection, { DEFAULT_HOW_DATA } from "./HowYoullLearnSection"
import HowYoullLearnSection from "./HowYoullLearnSection"
import { DEFAULT_RESOURCE_IMG } from "ol-utilities"
import { isVerifiedEnrollmentMode } from "@/common/mitxonline"
import { useFeatureFlagsLoaded } from "@/common/useFeatureFlagsLoaded"
Expand Down Expand Up @@ -98,7 +98,7 @@ const CoursePage: React.FC<CoursePageProps> = ({ readableId }) => {
{page.what_you_learn ? (
<WhatYoullLearnSection html={page.what_you_learn} />
) : null}
<HowYoullLearnSection data={DEFAULT_HOW_DATA} />
<HowYoullLearnSection page={page} />
{page.prerequisites ? (
<PrerequisitesSection aria-labelledby={HeadingIds.Prereqs}>
<Typography variant="h4" component="h2" id={HeadingIds.Prereqs}>
Expand Down
91 changes: 51 additions & 40 deletions frontends/main/src/app-pages/ProductPages/HowYoullLearnSection.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,11 @@ import IconCertificate from "@/public/images/product/icon_certificate.png"
import IconComputerBulb from "@/public/images/product/icon_computer_lightbulb.png"
import IconConnectedPeople from "@/public/images/product/icon_connected_people.png"

import type {
CoursePageItem,
ProgramPageItem,
} from "@mitodl/mitxonline-api-axios/v2"

const HowYoullLearnRoot = styled.section(({ theme }) => ({
display: "flex",
flexDirection: "column",
Expand Down Expand Up @@ -49,7 +54,7 @@ const HowYoullLearnHeader = styled.div({
alignItems: "center",
})

const HowYoullLearnIcon = styled(Image)({
const HowYoullLearnIconImage = styled(Image)({
width: "80px",
height: "50px",
flexShrink: 0,
Expand All @@ -69,64 +74,70 @@ const HowYoullLearnDescription = styled.p(({ theme }) => ({
},
}))

type HowYoullLearnItemData = {
icon: typeof IconComputerBulb
title: string
text: string
}
const HowYoullLearnIcon: React.FC<{
src: string
width: number
height: number
alt: string
}> = ({ src, width, height, alt }) => {
let iconSrc: typeof IconBrains | null = null
switch (src) {
case "IconBrains":
iconSrc = IconBrains
break
case "IconCertificate":
iconSrc = IconCertificate
break
case "IconComputerBulb":
iconSrc = IconComputerBulb
break
case "IconConnectedPeople":
iconSrc = IconConnectedPeople
break
default:
console.warn(`Unknown how_youll_learn icon: ${src}`)
}
Comment on lines +97 to +99
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We should flag in the issue or somewhere that right now we have 3 repeated icons if you enable all of them:

Image

Originally there was a fifth icon (we only had 2 duplicates) https://github.com/mitodl/mit-learn/blob/cdabbf911573599118eeb4766ffabd2035a659d0/frontends/main/public/images/product/icon_book_play.png but I removed it when we culled the hard-coded list down to 4.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The "Learn From Others" and "Learn On Demand" ones are the new ones so will ping folks about that - initially i think we'll probably just not use those two. (I need to get something in place to set the defaults for existing courses, too; we discussed this in the MITx Online PR and then I forgot to put it in..)


// Placeholder data — will be replaced by API-driven content.
const DEFAULT_HOW_DATA: HowYoullLearnItemData[] = [
{
icon: IconConnectedPeople,
title: "Real-World Learning",
text: "Learn from MIT faculty and experts who ground their teaching in real-world cases rather than mathematical models, making the material approachable for all.",
},
{
icon: IconBrains,
title: "Practical Application",
text: "Apply your new knowledge with hands-on, practical exercises drawn from healthcare, sports, finance, sustainability, and more.",
},
{
icon: IconComputerBulb,
title: "AI-Enabled Support",
text: "Deepen your understanding of the course material and get help on assignments from AskTIM, the AI assistant built by MIT researchers.",
},
{
icon: IconCertificate,
title: "Stackable Credentials",
text: "Earn an MIT Open Learning certificate at each milestone—module, course, and program—demonstrating your AI expertise. Available in paid courses only.",
},
]
return (
iconSrc && (
<HowYoullLearnIconImage
src={iconSrc}
width={width}
height={height}
alt={alt}
/>
)
)
}

const HowYoullLearnSection: React.FC<{
data: HowYoullLearnItemData[]
}> = ({ data }) => {
return (
page: CoursePageItem | ProgramPageItem
}> = ({ page }) => {
const filteredOptions = page.how_youll_learn ?? []

return filteredOptions.length > 0 ? (
<HowYoullLearnRoot aria-labelledby={HeadingIds.How}>
<Typography variant="h4" component="h2" id={HeadingIds.How}>
How you'll learn
</Typography>
<HowYoullLearnGrid>
{data.map((item, index) => (
<HowYoullLearnItem key={index}>
{filteredOptions.map((option) => (
<HowYoullLearnItem key={option.key}>
<HowYoullLearnHeader>
<HowYoullLearnIcon
src={item.icon}
src={option.icon}
width={80}
height={50}
alt=""
/>
<HowYoullLearnTitle>{item.title}</HowYoullLearnTitle>
<HowYoullLearnTitle>{option.title}</HowYoullLearnTitle>
</HowYoullLearnHeader>
<HowYoullLearnDescription>{item.text}</HowYoullLearnDescription>
<HowYoullLearnDescription>{option.text}</HowYoullLearnDescription>
</HowYoullLearnItem>
))}
</HowYoullLearnGrid>
</HowYoullLearnRoot>
)
) : null
}

export default HowYoullLearnSection
export { DEFAULT_HOW_DATA }
export type { HowYoullLearnItemData }
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,7 @@ import UnstyledRawHTML from "@/components/UnstyledRawHTML/UnstyledRawHTML"
import AboutSection from "./AboutSection"
import ProductPageTemplate from "./ProductPageTemplate"
import WhatYoullLearnSection from "./WhatYoullLearnSection"
import HowYoullLearnSection, { DEFAULT_HOW_DATA } from "./HowYoullLearnSection"
import HowYoullLearnSection from "./HowYoullLearnSection"
import { DEFAULT_RESOURCE_IMG, pluralize } from "ol-utilities"
import { useFeatureFlagsLoaded } from "@/common/useFeatureFlagsLoaded"
import ProgramAsCourseInfoBox from "./InfoBoxProgramAsCourse"
Expand Down Expand Up @@ -312,7 +312,7 @@ const ProgramAsCoursePage: React.FC<ProgramAsCoursePageProps> = ({
childPrograms={childPrograms.data?.results}
isLoading={dataLoading}
/>
<HowYoullLearnSection data={DEFAULT_HOW_DATA} />
<HowYoullLearnSection page={page} />
{page.prerequisites ? (
<PrerequisitesSection aria-labelledby={HeadingIds.Prereqs}>
<Typography variant="h4" component="h2" id={HeadingIds.Prereqs}>
Expand Down
4 changes: 2 additions & 2 deletions frontends/main/src/app-pages/ProductPages/ProgramPage.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,7 @@ import UnstyledRawHTML from "@/components/UnstyledRawHTML/UnstyledRawHTML"
import AboutSection from "./AboutSection"
import ProductPageTemplate from "./ProductPageTemplate"
import WhatYoullLearnSection from "./WhatYoullLearnSection"
import HowYoullLearnSection, { DEFAULT_HOW_DATA } from "./HowYoullLearnSection"
import HowYoullLearnSection from "./HowYoullLearnSection"
import type {
V2ProgramDetail,
CourseWithCourseRunsSerializerV2,
Expand Down Expand Up @@ -311,7 +311,7 @@ const ProgramPage: React.FC<ProgramPageProps> = ({ readableId }) => {
// Use skeleton as fallback for loading OR error
isLoading={dataLoading}
/>
<HowYoullLearnSection data={DEFAULT_HOW_DATA} />
<HowYoullLearnSection page={page} />
{page.prerequisites ? (
<PrerequisitesSection aria-labelledby={HeadingIds.Prereqs}>
<Typography variant="h4" component="h2" id={HeadingIds.Prereqs}>
Expand Down
12 changes: 6 additions & 6 deletions yarn.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

Loading