From e1d4f9f0c05a4a6d93b0cf90e944363617829775 Mon Sep 17 00:00:00 2001 From: James Kachel Date: Tue, 14 Apr 2026 11:53:12 -0500 Subject: [PATCH 01/13] Rough-in the toggleable HYL cards --- .../src/app-pages/ProductPages/CoursePage.tsx | 12 +++- .../ProductPages/HowYoullLearnSection.tsx | 65 ++++++++++++++++++- 2 files changed, 74 insertions(+), 3 deletions(-) diff --git a/frontends/main/src/app-pages/ProductPages/CoursePage.tsx b/frontends/main/src/app-pages/ProductPages/CoursePage.tsx index d814ffbb86..7996d14a0b 100644 --- a/frontends/main/src/app-pages/ProductPages/CoursePage.tsx +++ b/frontends/main/src/app-pages/ProductPages/CoursePage.tsx @@ -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, { DEFAULT_HOW_DATA, HowYoullLearnSectionWrapper } from "./HowYoullLearnSection" import { DEFAULT_RESOURCE_IMG } from "ol-utilities" import { isVerifiedEnrollmentMode } from "@/common/mitxonline" import { useFeatureFlagsLoaded } from "@/common/useFeatureFlagsLoaded" @@ -65,6 +65,14 @@ const CoursePage: React.FC = ({ readableId }) => { const imageSrc = page.course_details.page.feature_image_src || DEFAULT_RESOURCE_IMG + const hylCards = [ + page.hasOwnProperty("hyl_choice_realworld_learning") && page.hyl_choice_realworld_learning ? "hyl_choice_realworld_learning" : null, + page.hasOwnProperty("hyl_choice_learn_by_doing") && page.hyl_choice_learn_by_doing ? "hyl_choice_learn_by_doing" : null, + page.hasOwnProperty("hyl_choice_learn_from_others") && page.hyl_choice_learn_from_others ? "hyl_choice_learn_from_others" : null, + page.hasOwnProperty("hyl_choice_learn_on_demand") && page.hyl_choice_learn_on_demand ? "hyl_choice_learn_on_demand" : null, + page.hasOwnProperty("hyl_choice_ai_enabled_support") && page.hyl_choice_ai_enabled_support ? "hyl_choice_ai_enabled_support" : null, + page.hasOwnProperty("hyl_choice_stackable_credentials") && page.hyl_choice_stackable_credentials ? "hyl_choice_stackable_credentials" : null, + ] return ( = ({ readableId }) => { {page.what_you_learn ? ( ) : null} - + {page.prerequisites ? ( diff --git a/frontends/main/src/app-pages/ProductPages/HowYoullLearnSection.tsx b/frontends/main/src/app-pages/ProductPages/HowYoullLearnSection.tsx index a87adf9c72..ba952cab96 100644 --- a/frontends/main/src/app-pages/ProductPages/HowYoullLearnSection.tsx +++ b/frontends/main/src/app-pages/ProductPages/HowYoullLearnSection.tsx @@ -74,6 +74,10 @@ type HowYoullLearnItemData = { title: string text: string } +type HowYoullLearnOption = { + name: string + data: HowYoullLearnItemData +} // Placeholder data — will be replaced by API-driven content. const DEFAULT_HOW_DATA: HowYoullLearnItemData[] = [ @@ -99,6 +103,65 @@ const DEFAULT_HOW_DATA: HowYoullLearnItemData[] = [ }, ] +const HOW_YOULL_LEARN_OPTIONS: HowYoullLearnOption[] = [ + { + name: "hyl_choice_realworld_learning", + data: { + 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.", + }, + }, + { + name: "hyl_choice_learn_by_doing", + data: { + icon: IconBrains, + title: "Practical Application", + text: "Apply your new knowledge with hands-on, practical exercises drawn from healthcare, sports, finance, sustainability, and more.", + }, + }, + { + name: "hyl_choice_learn_from_others", + data: { + icon: IconBrains, + title: "Learn From Others", + text: "Connect with an international community of professionals working on real-world projects.", + }, + }, + { + name: "hyl_choice_learn_on_demand", + data: { + icon: IconBrains, + title: "Learn On Demand", + text: "Access all course content online with complete flexibility to study at your own pace.", + }, + }, + { + name: "hyl_choice_ai_enabled_support", + data: { + 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.", + }, + }, + { + name: "hyl_choice_stackable_credentials", + data: { + 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.", + }, + }, +] + +const HowYoullLearnSectionWrapper: React.FC<{ + data: Array +}> = ({data}) => { + const filteredOptions = HOW_YOULL_LEARN_OPTIONS.filter((option) => data.includes(option.name)) + const items = filteredOptions.map((option) => option.data) + return +} + const HowYoullLearnSection: React.FC<{ data: HowYoullLearnItemData[] }> = ({ data }) => { @@ -128,5 +191,5 @@ const HowYoullLearnSection: React.FC<{ } export default HowYoullLearnSection -export { DEFAULT_HOW_DATA } +export { DEFAULT_HOW_DATA, HOW_YOULL_LEARN_OPTIONS, HowYoullLearnSectionWrapper } export type { HowYoullLearnItemData } From 99ab2f069350e17c54f43d3c4838b2e869ed125a Mon Sep 17 00:00:00 2001 From: James Kachel Date: Tue, 14 Apr 2026 14:59:44 -0500 Subject: [PATCH 02/13] Refactor the HYL section to take the page object and figure out the cards from there; update where this gets called --- .../src/app-pages/ProductPages/CoursePage.tsx | 12 +---- .../ProductPages/HowYoullLearnSection.tsx | 46 ++++--------------- .../ProductPages/ProgramAsCoursePage.tsx | 4 +- .../app-pages/ProductPages/ProgramPage.tsx | 4 +- 4 files changed, 15 insertions(+), 51 deletions(-) diff --git a/frontends/main/src/app-pages/ProductPages/CoursePage.tsx b/frontends/main/src/app-pages/ProductPages/CoursePage.tsx index 7996d14a0b..0d64142ebf 100644 --- a/frontends/main/src/app-pages/ProductPages/CoursePage.tsx +++ b/frontends/main/src/app-pages/ProductPages/CoursePage.tsx @@ -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, HowYoullLearnSectionWrapper } from "./HowYoullLearnSection" +import HowYoullLearnSection from "./HowYoullLearnSection" import { DEFAULT_RESOURCE_IMG } from "ol-utilities" import { isVerifiedEnrollmentMode } from "@/common/mitxonline" import { useFeatureFlagsLoaded } from "@/common/useFeatureFlagsLoaded" @@ -65,14 +65,6 @@ const CoursePage: React.FC = ({ readableId }) => { const imageSrc = page.course_details.page.feature_image_src || DEFAULT_RESOURCE_IMG - const hylCards = [ - page.hasOwnProperty("hyl_choice_realworld_learning") && page.hyl_choice_realworld_learning ? "hyl_choice_realworld_learning" : null, - page.hasOwnProperty("hyl_choice_learn_by_doing") && page.hyl_choice_learn_by_doing ? "hyl_choice_learn_by_doing" : null, - page.hasOwnProperty("hyl_choice_learn_from_others") && page.hyl_choice_learn_from_others ? "hyl_choice_learn_from_others" : null, - page.hasOwnProperty("hyl_choice_learn_on_demand") && page.hyl_choice_learn_on_demand ? "hyl_choice_learn_on_demand" : null, - page.hasOwnProperty("hyl_choice_ai_enabled_support") && page.hyl_choice_ai_enabled_support ? "hyl_choice_ai_enabled_support" : null, - page.hasOwnProperty("hyl_choice_stackable_credentials") && page.hyl_choice_stackable_credentials ? "hyl_choice_stackable_credentials" : null, - ] return ( = ({ readableId }) => { {page.what_you_learn ? ( ) : null} - + {page.prerequisites ? ( diff --git a/frontends/main/src/app-pages/ProductPages/HowYoullLearnSection.tsx b/frontends/main/src/app-pages/ProductPages/HowYoullLearnSection.tsx index ba952cab96..40e3163362 100644 --- a/frontends/main/src/app-pages/ProductPages/HowYoullLearnSection.tsx +++ b/frontends/main/src/app-pages/ProductPages/HowYoullLearnSection.tsx @@ -9,6 +9,8 @@ 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", @@ -79,30 +81,6 @@ type HowYoullLearnOption = { data: HowYoullLearnItemData } -// 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.", - }, -] - const HOW_YOULL_LEARN_OPTIONS: HowYoullLearnOption[] = [ { name: "hyl_choice_realworld_learning", @@ -154,24 +132,19 @@ const HOW_YOULL_LEARN_OPTIONS: HowYoullLearnOption[] = [ }, ] -const HowYoullLearnSectionWrapper: React.FC<{ - data: Array -}> = ({data}) => { - const filteredOptions = HOW_YOULL_LEARN_OPTIONS.filter((option) => data.includes(option.name)) - const items = filteredOptions.map((option) => option.data) - return -} const HowYoullLearnSection: React.FC<{ - data: HowYoullLearnItemData[] -}> = ({ data }) => { - return ( + page: CoursePageItem | ProgramPageItem +}> = ({ page }) => { + const filteredOptions = HOW_YOULL_LEARN_OPTIONS.filter((option) => page.hasOwnProperty(option.name) && (page as Record)[option.name]) + const items = filteredOptions.map((option) => option.data) + return items.length > 0 ? ( How you'll learn - {data.map((item, index) => ( + {items.map((item, index) => ( - ) + ) : null } export default HowYoullLearnSection -export { DEFAULT_HOW_DATA, HOW_YOULL_LEARN_OPTIONS, HowYoullLearnSectionWrapper } export type { HowYoullLearnItemData } diff --git a/frontends/main/src/app-pages/ProductPages/ProgramAsCoursePage.tsx b/frontends/main/src/app-pages/ProductPages/ProgramAsCoursePage.tsx index 24cd3b1059..4bc11cd387 100644 --- a/frontends/main/src/app-pages/ProductPages/ProgramAsCoursePage.tsx +++ b/frontends/main/src/app-pages/ProductPages/ProgramAsCoursePage.tsx @@ -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" @@ -312,7 +312,7 @@ const ProgramAsCoursePage: React.FC = ({ childPrograms={childPrograms.data?.results} isLoading={dataLoading} /> - + {page.prerequisites ? ( diff --git a/frontends/main/src/app-pages/ProductPages/ProgramPage.tsx b/frontends/main/src/app-pages/ProductPages/ProgramPage.tsx index 4d380f7f10..946c9e4c4f 100644 --- a/frontends/main/src/app-pages/ProductPages/ProgramPage.tsx +++ b/frontends/main/src/app-pages/ProductPages/ProgramPage.tsx @@ -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, @@ -311,7 +311,7 @@ const ProgramPage: React.FC = ({ readableId }) => { // Use skeleton as fallback for loading OR error isLoading={dataLoading} /> - + {page.prerequisites ? ( From 3e3f5241578d8e09314a3657e949f85ce98f5230 Mon Sep 17 00:00:00 2001 From: James Kachel Date: Tue, 14 Apr 2026 15:16:11 -0500 Subject: [PATCH 03/13] finally figure out how to run fmt/lint/typecheck --- .../app-pages/ProductPages/HowYoullLearnSection.tsx | 12 +++++++++--- 1 file changed, 9 insertions(+), 3 deletions(-) diff --git a/frontends/main/src/app-pages/ProductPages/HowYoullLearnSection.tsx b/frontends/main/src/app-pages/ProductPages/HowYoullLearnSection.tsx index 40e3163362..184e09810a 100644 --- a/frontends/main/src/app-pages/ProductPages/HowYoullLearnSection.tsx +++ b/frontends/main/src/app-pages/ProductPages/HowYoullLearnSection.tsx @@ -9,7 +9,10 @@ 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" +import type { + CoursePageItem, + ProgramPageItem, +} from "@mitodl/mitxonline-api-axios/v2" const HowYoullLearnRoot = styled.section(({ theme }) => ({ display: "flex", @@ -132,11 +135,14 @@ const HOW_YOULL_LEARN_OPTIONS: HowYoullLearnOption[] = [ }, ] - const HowYoullLearnSection: React.FC<{ page: CoursePageItem | ProgramPageItem }> = ({ page }) => { - const filteredOptions = HOW_YOULL_LEARN_OPTIONS.filter((option) => page.hasOwnProperty(option.name) && (page as Record)[option.name]) + const filteredOptions = HOW_YOULL_LEARN_OPTIONS.filter( + (option) => + page.hasOwnProperty(option.name) && + (page as unknown as Record)[option.name], + ) const items = filteredOptions.map((option) => option.data) return items.length > 0 ? ( From 5dfdce4031956909c0bc810022336d524bf50728 Mon Sep 17 00:00:00 2001 From: James Kachel Date: Wed, 15 Apr 2026 10:30:58 -0500 Subject: [PATCH 04/13] Fixing tests --- .../api/src/mitxonline/test-utils/factories/pages.ts | 12 ++++++++++++ 1 file changed, 12 insertions(+) diff --git a/frontends/api/src/mitxonline/test-utils/factories/pages.ts b/frontends/api/src/mitxonline/test-utils/factories/pages.ts index 6cba1acc51..18b6abe61f 100644 --- a/frontends/api/src/mitxonline/test-utils/factories/pages.ts +++ b/frontends/api/src/mitxonline/test-utils/factories/pages.ts @@ -164,6 +164,12 @@ const coursePageItem: PartialFactory = (override) => { ], video_url: faker.internet.url(), what_you_learn: makeHTMLList(5), + hyl_choice_realworld_learning: true, + hyl_choice_learn_by_doing: true, + hyl_choice_learn_from_others: false, + hyl_choice_learn_on_demand: false, + hyl_choice_ai_enabled_support: true, + hyl_choice_stackable_credentials: true, } return mergeOverrides(defaults, override) } @@ -215,6 +221,12 @@ const programPageItem: PartialFactory = (override) => { faq_url: faker.internet.url(), about: makeHTMLParagraph(3), what_you_learn: makeHTMLList(5), + hyl_choice_realworld_learning: true, + hyl_choice_learn_by_doing: true, + hyl_choice_learn_from_others: false, + hyl_choice_learn_on_demand: false, + hyl_choice_ai_enabled_support: true, + hyl_choice_stackable_credentials: true, feature_image: featureImage(), video_url: faker.datatype.boolean() ? faker.internet.url() : null, faculty_section_title: "Meet your instructors", From bc3286fd75c8f30e8c9b25d72aca1006651d76fa Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Wed, 15 Apr 2026 15:55:24 +0000 Subject: [PATCH 05/13] Fix hasOwnProperty, unknown cast, and React key in HowYoullLearnSection Agent-Logs-Url: https://github.com/mitodl/mit-learn/sessions/ed6a7a47-f467-48eb-be2a-464691b2fe77 Co-authored-by: jkachel <945611+jkachel@users.noreply.github.com> --- .../ProductPages/HowYoullLearnSection.tsx | 32 ++++++++++++------- 1 file changed, 21 insertions(+), 11 deletions(-) diff --git a/frontends/main/src/app-pages/ProductPages/HowYoullLearnSection.tsx b/frontends/main/src/app-pages/ProductPages/HowYoullLearnSection.tsx index 184e09810a..deadbb918a 100644 --- a/frontends/main/src/app-pages/ProductPages/HowYoullLearnSection.tsx +++ b/frontends/main/src/app-pages/ProductPages/HowYoullLearnSection.tsx @@ -74,13 +74,24 @@ const HowYoullLearnDescription = styled.p(({ theme }) => ({ }, })) +// These fields come from the CMS page types. Once @mitodl/mitxonline-api-axios +// is published with these fields, this can be replaced with: +// Extract +type HylChoiceKey = + | "hyl_choice_realworld_learning" + | "hyl_choice_learn_by_doing" + | "hyl_choice_learn_from_others" + | "hyl_choice_learn_on_demand" + | "hyl_choice_ai_enabled_support" + | "hyl_choice_stackable_credentials" + type HowYoullLearnItemData = { icon: typeof IconComputerBulb title: string text: string } type HowYoullLearnOption = { - name: string + name: HylChoiceKey data: HowYoullLearnItemData } @@ -139,29 +150,28 @@ const HowYoullLearnSection: React.FC<{ page: CoursePageItem | ProgramPageItem }> = ({ page }) => { const filteredOptions = HOW_YOULL_LEARN_OPTIONS.filter( - (option) => - page.hasOwnProperty(option.name) && - (page as unknown as Record)[option.name], + (option) => (page as Record)[option.name], ) - const items = filteredOptions.map((option) => option.data) - return items.length > 0 ? ( + return filteredOptions.length > 0 ? ( How you'll learn - {items.map((item, index) => ( - + {filteredOptions.map((option) => ( + - {item.title} + {option.data.title} - {item.text} + + {option.data.text} + ))} From ea6b5b6a4046e4e366dcb0dc908cce6a3f6863a7 Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Wed, 15 Apr 2026 15:58:04 +0000 Subject: [PATCH 06/13] Use explicit === true check for hyl_choice field values Agent-Logs-Url: https://github.com/mitodl/mit-learn/sessions/ed6a7a47-f467-48eb-be2a-464691b2fe77 Co-authored-by: jkachel <945611+jkachel@users.noreply.github.com> --- .../main/src/app-pages/ProductPages/HowYoullLearnSection.tsx | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/frontends/main/src/app-pages/ProductPages/HowYoullLearnSection.tsx b/frontends/main/src/app-pages/ProductPages/HowYoullLearnSection.tsx index deadbb918a..f650b99a6a 100644 --- a/frontends/main/src/app-pages/ProductPages/HowYoullLearnSection.tsx +++ b/frontends/main/src/app-pages/ProductPages/HowYoullLearnSection.tsx @@ -150,7 +150,8 @@ const HowYoullLearnSection: React.FC<{ page: CoursePageItem | ProgramPageItem }> = ({ page }) => { const filteredOptions = HOW_YOULL_LEARN_OPTIONS.filter( - (option) => (page as Record)[option.name], + (option) => + (page as Record)[option.name] === true, ) return filteredOptions.length > 0 ? ( From 79a7c7985865ab8266f1587222d5308dad28c975 Mon Sep 17 00:00:00 2001 From: James Kachel Date: Thu, 16 Apr 2026 11:29:16 -0500 Subject: [PATCH 07/13] Update section component to work with the new version of the API --- .../mitxonline/test-utils/factories/pages.ts | 64 +++++++-- .../ProductPages/HowYoullLearnSection.tsx | 121 ++++++------------ 2 files changed, 92 insertions(+), 93 deletions(-) diff --git a/frontends/api/src/mitxonline/test-utils/factories/pages.ts b/frontends/api/src/mitxonline/test-utils/factories/pages.ts index 18b6abe61f..e14b61a6ec 100644 --- a/frontends/api/src/mitxonline/test-utils/factories/pages.ts +++ b/frontends/api/src/mitxonline/test-utils/factories/pages.ts @@ -164,12 +164,32 @@ const coursePageItem: PartialFactory = (override) => { ], video_url: faker.internet.url(), what_you_learn: makeHTMLList(5), - hyl_choice_realworld_learning: true, - hyl_choice_learn_by_doing: true, - hyl_choice_learn_from_others: false, - hyl_choice_learn_on_demand: false, - hyl_choice_ai_enabled_support: true, - hyl_choice_stackable_credentials: true, + 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(defaults, override) } @@ -221,12 +241,32 @@ const programPageItem: PartialFactory = (override) => { faq_url: faker.internet.url(), about: makeHTMLParagraph(3), what_you_learn: makeHTMLList(5), - hyl_choice_realworld_learning: true, - hyl_choice_learn_by_doing: true, - hyl_choice_learn_from_others: false, - hyl_choice_learn_on_demand: false, - hyl_choice_ai_enabled_support: true, - hyl_choice_stackable_credentials: true, + 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", diff --git a/frontends/main/src/app-pages/ProductPages/HowYoullLearnSection.tsx b/frontends/main/src/app-pages/ProductPages/HowYoullLearnSection.tsx index f650b99a6a..b72cb36bad 100644 --- a/frontends/main/src/app-pages/ProductPages/HowYoullLearnSection.tsx +++ b/frontends/main/src/app-pages/ProductPages/HowYoullLearnSection.tsx @@ -54,7 +54,7 @@ const HowYoullLearnHeader = styled.div({ alignItems: "center", }) -const HowYoullLearnIcon = styled(Image)({ +const HowYoullLearnIconImage = styled(Image)({ width: "80px", height: "50px", flexShrink: 0, @@ -74,85 +74,47 @@ const HowYoullLearnDescription = styled.p(({ theme }) => ({ }, })) -// These fields come from the CMS page types. Once @mitodl/mitxonline-api-axios -// is published with these fields, this can be replaced with: -// Extract -type HylChoiceKey = - | "hyl_choice_realworld_learning" - | "hyl_choice_learn_by_doing" - | "hyl_choice_learn_from_others" - | "hyl_choice_learn_on_demand" - | "hyl_choice_ai_enabled_support" - | "hyl_choice_stackable_credentials" +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}`) + } -type HowYoullLearnItemData = { - icon: typeof IconComputerBulb - title: string - text: string -} -type HowYoullLearnOption = { - name: HylChoiceKey - data: HowYoullLearnItemData + return ( + iconSrc && ( + + ) + ) } -const HOW_YOULL_LEARN_OPTIONS: HowYoullLearnOption[] = [ - { - name: "hyl_choice_realworld_learning", - data: { - 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.", - }, - }, - { - name: "hyl_choice_learn_by_doing", - data: { - icon: IconBrains, - title: "Practical Application", - text: "Apply your new knowledge with hands-on, practical exercises drawn from healthcare, sports, finance, sustainability, and more.", - }, - }, - { - name: "hyl_choice_learn_from_others", - data: { - icon: IconBrains, - title: "Learn From Others", - text: "Connect with an international community of professionals working on real-world projects.", - }, - }, - { - name: "hyl_choice_learn_on_demand", - data: { - icon: IconBrains, - title: "Learn On Demand", - text: "Access all course content online with complete flexibility to study at your own pace.", - }, - }, - { - name: "hyl_choice_ai_enabled_support", - data: { - 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.", - }, - }, - { - name: "hyl_choice_stackable_credentials", - data: { - 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.", - }, - }, -] - const HowYoullLearnSection: React.FC<{ page: CoursePageItem | ProgramPageItem }> = ({ page }) => { - const filteredOptions = HOW_YOULL_LEARN_OPTIONS.filter( - (option) => - (page as Record)[option.name] === true, - ) + const filteredOptions = page.how_youll_learn ? page.how_youll_learn : [] + return filteredOptions.length > 0 ? ( @@ -160,19 +122,17 @@ const HowYoullLearnSection: React.FC<{ {filteredOptions.map((option) => ( - + - {option.data.title} + {option.title} - - {option.data.text} - + {option.text} ))} @@ -181,4 +141,3 @@ const HowYoullLearnSection: React.FC<{ } export default HowYoullLearnSection -export type { HowYoullLearnItemData } From 8fcd872b4151c75406ea454114455ef34a30399e Mon Sep 17 00:00:00 2001 From: James Kachel Date: Thu, 23 Apr 2026 12:41:48 -0500 Subject: [PATCH 08/13] Update MITx Online API client --- frontends/api/package.json | 2 +- yarn.lock | 12 +++++++++++- 2 files changed, 12 insertions(+), 2 deletions(-) diff --git a/frontends/api/package.json b/frontends/api/package.json index 2bbe941af1..0745ec8f54 100644 --- a/frontends/api/package.json +++ b/frontends/api/package.json @@ -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" diff --git a/yarn.lock b/yarn.lock index ce95838d84..d25d0a2029 100644 --- a/yarn.lock +++ b/yarn.lock @@ -3531,6 +3531,16 @@ __metadata: languageName: node linkType: hard +"@mitodl/mitxonline-api-axios@npm:^2026.4.23": + version: 2026.4.23 + resolution: "@mitodl/mitxonline-api-axios@npm:2026.4.23" + dependencies: + "@types/node": "npm:^20.11.19" + axios: "npm:^1.6.5" + checksum: 10/9580cb496742649450c0567b8b4be6ad19082af9d3ea1c2cd1f9dcec1c87746c8370c8af984b5012660d6e9a52f36dded227115da635a8efb7634df952499a25 + languageName: node + linkType: hard + "@mitodl/smoot-design@npm:^6.24.0": version: 6.24.0 resolution: "@mitodl/smoot-design@npm:6.24.0" @@ -9141,7 +9151,7 @@ __metadata: resolution: "api@workspace:frontends/api" dependencies: "@faker-js/faker": "npm:^10.0.0" - "@mitodl/mitxonline-api-axios": "npm:^2026.3.26" + "@mitodl/mitxonline-api-axios": "npm:^2026.4.23" "@tanstack/react-query": "npm:^5.66.0" "@testing-library/react": "npm:^16.3.0" axios: "npm:^1.12.2" From bb81a1a2c511b385c5351b3380a00e579762f628 Mon Sep 17 00:00:00 2001 From: James Kachel Date: Thu, 23 Apr 2026 13:05:31 -0500 Subject: [PATCH 09/13] other place referenced the API client --- frontends/main/package.json | 2 +- yarn.lock | 12 +----------- 2 files changed, 2 insertions(+), 12 deletions(-) diff --git a/frontends/main/package.json b/frontends/main/package.json index a5c0c7c1e1..833ab6356c 100644 --- a/frontends/main/package.json +++ b/frontends/main/package.json @@ -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", diff --git a/yarn.lock b/yarn.lock index d25d0a2029..0c4cc02084 100644 --- a/yarn.lock +++ b/yarn.lock @@ -3521,16 +3521,6 @@ __metadata: languageName: node linkType: hard -"@mitodl/mitxonline-api-axios@npm:^2026.3.26": - version: 2026.3.26 - resolution: "@mitodl/mitxonline-api-axios@npm:2026.3.26" - dependencies: - "@types/node": "npm:^20.11.19" - axios: "npm:^1.6.5" - checksum: 10/66d36280b62505ea4510db2376cf1887ade17286aa17b840d120dc88a48817097e9d194cd2a54c3479535d457031883f6b420995ee8909d9afb3debb3b520ab9 - languageName: node - linkType: hard - "@mitodl/mitxonline-api-axios@npm:^2026.4.23": version: 2026.4.23 resolution: "@mitodl/mitxonline-api-axios@npm:2026.4.23" @@ -16390,7 +16380,7 @@ __metadata: "@floating-ui/react": "npm:^0.27.16" "@happy-dom/jest-environment": "npm:^20.1.0" "@mitodl/course-search-utils": "npm:^3.5.2" - "@mitodl/mitxonline-api-axios": "npm:^2026.3.26" + "@mitodl/mitxonline-api-axios": "npm:^2026.4.23" "@mitodl/smoot-design": "npm:^6.24.0" "@mui/material": "npm:^6.4.5" "@mui/material-nextjs": "npm:^6.4.3" From bc96c505bf3bdaf1bd51c037b1190224dcc5f5c1 Mon Sep 17 00:00:00 2001 From: James Kachel Date: Thu, 23 Apr 2026 15:06:22 -0500 Subject: [PATCH 10/13] fix typing issues --- frontends/api/src/mitxonline/test-utils/factories/enrollment.ts | 2 +- frontends/api/src/mitxonline/test-utils/factories/orders.ts | 1 + 2 files changed, 2 insertions(+), 1 deletion(-) diff --git a/frontends/api/src/mitxonline/test-utils/factories/enrollment.ts b/frontends/api/src/mitxonline/test-utils/factories/enrollment.ts index 1357679be5..b04737e2e7 100644 --- a/frontends/api/src/mitxonline/test-utils/factories/enrollment.ts +++ b/frontends/api/src/mitxonline/test-utils/factories/enrollment.ts @@ -88,7 +88,7 @@ const programEnrollmentV3: PartialFactory = ( 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(defaults, overrides) diff --git a/frontends/api/src/mitxonline/test-utils/factories/orders.ts b/frontends/api/src/mitxonline/test-utils/factories/orders.ts index dbab4806d3..89e5b66d9d 100644 --- a/frontends/api/src/mitxonline/test-utils/factories/orders.ts +++ b/frontends/api/src/mitxonline/test-utils/factories/orders.ts @@ -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, }) From ec293f87b89f7dfb86d2ded6ea5c46f4045a3d34 Mon Sep 17 00:00:00 2001 From: James Kachel Date: Thu, 23 Apr 2026 15:23:32 -0500 Subject: [PATCH 11/13] fmt fix --- .../api/src/mitxonline/test-utils/factories/enrollment.ts | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/frontends/api/src/mitxonline/test-utils/factories/enrollment.ts b/frontends/api/src/mitxonline/test-utils/factories/enrollment.ts index b04737e2e7..b3d9baca88 100644 --- a/frontends/api/src/mitxonline/test-utils/factories/enrollment.ts +++ b/frontends/api/src/mitxonline/test-utils/factories/enrollment.ts @@ -88,7 +88,11 @@ const programEnrollmentV3: PartialFactory = ( link: `/certificate/program/${faker.string.uuid()}/`, } : null, - enrollment_mode: faker.helpers.arrayElement(["audit", "verified", undefined]), + enrollment_mode: faker.helpers.arrayElement([ + "audit", + "verified", + undefined, + ]), program: program, } return mergeOverrides(defaults, overrides) From 5a5d11f1e14f0786030d9fd65ac8a1174738b885 Mon Sep 17 00:00:00 2001 From: James Kachel Date: Thu, 23 Apr 2026 15:31:46 -0500 Subject: [PATCH 12/13] fix other unrelated typing issue --- .../DashboardPage/CoursewareDisplay/DashboardDialogs.test.tsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/frontends/main/src/app-pages/DashboardPage/CoursewareDisplay/DashboardDialogs.test.tsx b/frontends/main/src/app-pages/DashboardPage/CoursewareDisplay/DashboardDialogs.test.tsx index 93687b1aff..db06027cb0 100644 --- a/frontends/main/src/app-pages/DashboardPage/CoursewareDisplay/DashboardDialogs.test.tsx +++ b/frontends/main/src/app-pages/DashboardPage/CoursewareDisplay/DashboardDialogs.test.tsx @@ -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, }) From 52626c5bcc5cf4aba03d1a59f4caf33eb3282fa1 Mon Sep 17 00:00:00 2001 From: James Kachel Date: Thu, 23 Apr 2026 15:44:48 -0500 Subject: [PATCH 13/13] Update frontends/main/src/app-pages/ProductPages/HowYoullLearnSection.tsx Co-authored-by: Chris Chudzicki --- .../main/src/app-pages/ProductPages/HowYoullLearnSection.tsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/frontends/main/src/app-pages/ProductPages/HowYoullLearnSection.tsx b/frontends/main/src/app-pages/ProductPages/HowYoullLearnSection.tsx index b72cb36bad..4129b916f2 100644 --- a/frontends/main/src/app-pages/ProductPages/HowYoullLearnSection.tsx +++ b/frontends/main/src/app-pages/ProductPages/HowYoullLearnSection.tsx @@ -113,7 +113,7 @@ const HowYoullLearnIcon: React.FC<{ const HowYoullLearnSection: React.FC<{ page: CoursePageItem | ProgramPageItem }> = ({ page }) => { - const filteredOptions = page.how_youll_learn ? page.how_youll_learn : [] + const filteredOptions = page.how_youll_learn ?? [] return filteredOptions.length > 0 ? (