Skip to content

Commit 2425c2b

Browse files
committed
new design
1 parent 5814356 commit 2425c2b

4 files changed

Lines changed: 165 additions & 134 deletions

File tree

frontends/main/src/app-pages/ProductPages/ProductSummary.test.tsx

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1292,10 +1292,10 @@ describe("ProgramSummary", () => {
12921292
expect(priceRow).toHaveTextContent(
12931293
formatPrice(product.price, { avoidCents: true }),
12941294
)
1295-
expect(priceRow).toHaveTextContent("/ full program")
1295+
expect(priceRow).toHaveTextContent("full program")
12961296
expect(priceRow).not.toHaveTextContent("Free to Learn")
12971297
expect(priceRow).not.toHaveTextContent("Earn a certificate")
1298-
expect(priceRow).not.toHaveTextContent("Start for free")
1298+
expect(priceRow).not.toHaveTextContent("Audit for free")
12991299
})
13001300

13011301
test("Shows paid price and 'Start for free' callout when enrollment modes include both free and paid", () => {
@@ -1306,7 +1306,7 @@ describe("ProgramSummary", () => {
13061306
renderWithProviders(<ProgramSummary program={program} />)
13071307

13081308
const priceRow = screen.getByTestId(TestIds.PriceRow)
1309-
expect(priceRow).toHaveTextContent("Start for free")
1309+
expect(priceRow).toHaveTextContent("Audit for free")
13101310
expect(priceRow).toHaveTextContent("or upgrade to")
13111311
expect(priceRow).toHaveTextContent("certificate")
13121312
expect(priceRow).toHaveTextContent(

frontends/main/src/app-pages/ProductPages/ProductSummary.tsx

Lines changed: 146 additions & 106 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
import React, { HTMLAttributes, useState } from "react"
22
import { ActionButton, Alert, styled } from "@mitodl/smoot-design"
33
import { productQueries } from "api/mitxonline-hooks/products"
4-
import { Dialog, Link, Skeleton, Stack, Typography } from "ol-components"
4+
import { Dialog, Link, Skeleton, Stack, theme, Typography } from "ol-components"
55
import type { StackProps } from "ol-components"
66
import {
77
RiCalendarLine,
@@ -390,7 +390,7 @@ const ProgramPaySection = styled.div(({ theme }) => ({
390390
display: "flex",
391391
flexDirection: "column",
392392
alignItems: "flex-start",
393-
gap: "4px",
393+
gap: "12px",
394394
width: "346px",
395395
alignSelf: "stretch",
396396
flex: "none",
@@ -404,63 +404,84 @@ const ProgramPayLabel = styled.span(({ theme }) => ({
404404
letterSpacing: "0.04em",
405405
}))
406406

407-
const ProgramPayContent = styled.div(({ theme }) => ({
407+
/** Horizontal row: [current price block] | [vertical divider] | [list price block] */
408+
const ProgramPriceRowInner = styled.div<{}>({
408409
display: "flex",
409-
flexDirection: "column",
410-
alignItems: "flex-start",
411-
gap: "12px",
412-
width: "100%",
413-
maxWidth: "320px",
414-
[theme.breakpoints.down("sm")]: {
415-
maxWidth: "100%",
416-
},
417-
}))
410+
flexDirection: "row" as const,
411+
alignItems: "flex-end" as const,
412+
gap: "24px",
413+
})
418414

419-
const ProgramPriceLine = styled.div(({ theme }) => ({
415+
const ProgramCurrentPriceBlock = styled.div<{}>({
420416
display: "flex",
421-
alignItems: "flex-end",
422-
gap: "4px",
423-
flexWrap: "wrap",
424-
color: theme.custom.colors.darkGray2,
425-
}))
417+
flexDirection: "column" as const,
418+
justifyContent: "flex-end" as const,
419+
alignItems: "flex-start" as const,
420+
})
426421

427422
const ProgramPriceAmount = styled.span(({ theme }) => ({
428-
...theme.typography.h3,
423+
...theme.typography.subtitle2,
424+
fontFamily: "'Helvetica Neue', Helvetica, Arial, sans-serif",
429425
fontWeight: theme.typography.fontWeightBold,
430-
lineHeight: "36px",
426+
fontSize: "34px",
427+
lineHeight: "40px",
428+
color: theme.custom.colors.darkGray2,
431429
}))
432430

433431
const ProgramPriceSuffix = styled.span(({ theme }) => ({
434-
...theme.typography.subtitle1,
435-
fontSize: "18px",
436-
lineHeight: "26px",
437-
color: theme.custom.colors.silverGrayDark,
432+
...theme.typography.body3,
433+
color: "#626A73",
438434
}))
439435

440-
const ProgramDiscountBlock = styled.div(({ theme }) => ({
441-
display: "flex",
442-
flexDirection: "column",
443-
justifyContent: "center",
444-
alignItems: "flex-start",
445-
gap: "8px",
446-
color: theme.custom.colors.darkGray2,
436+
const ProgramVerticalDivider = styled.div(() => ({
437+
width: "1px",
438+
height: "48px",
439+
backgroundColor: "#CBD2D9",
440+
flexShrink: 0,
447441
}))
448442

449-
const ProgramSavingsText = styled.span(({ theme }) => ({
450-
...theme.typography.subtitle1,
451-
color: "#008000",
452-
fontWeight: theme.typography.fontWeightBold,
453-
}))
443+
const ProgramListPriceBlock = styled.div<{}>({
444+
display: "flex",
445+
flexDirection: "column" as const,
446+
justifyContent: "flex-end" as const,
447+
alignItems: "flex-start" as const,
448+
})
454449

455-
const ProgramListPriceText = styled.span(({ theme }) => ({
456-
...theme.typography.body2,
450+
const ProgramListPriceAmount = styled.span({
451+
...theme.typography.body3,
452+
fontFamily: "'Helvetica Neue', Helvetica, Arial, sans-serif",
453+
fontSize: "28px",
454+
lineHeight: "36px",
455+
display: "flex",
456+
alignItems: "flex-end" as const,
457+
textDecoration: "line-through",
458+
color: "#626A73",
459+
})
460+
461+
const ProgramListPriceSubLabel = styled.span({
462+
...theme.typography.body3,
457463
color: theme.custom.colors.silverGrayDark,
458-
}))
464+
})
459465

460-
const ProgramListPriceAmount = styled.span(({ theme }) => ({
461-
...theme.typography.subtitle2,
462-
textDecoration: "line-through",
463-
}))
466+
/** Inline row: "Save $X compared to purchasing N courses separately" */
467+
const ProgramDiscountRow = styled.div<{}>({
468+
display: "flex",
469+
flexDirection: "row" as const,
470+
alignItems: "center" as const,
471+
gap: "4px",
472+
width: "100%",
473+
})
474+
475+
const ProgramSavingsText = styled.span({
476+
...theme.typography.subtitle3,
477+
fontWeight: theme.typography.fontWeightBold,
478+
color: "#008000",
479+
})
480+
481+
const ProgramSavingsDetailText = styled.span({
482+
...theme.typography.body3,
483+
color: theme.custom.colors.silverGrayDark,
484+
})
464485

465486
const ProgramPriceDivider = styled.div(({ theme }) => ({
466487
width: "100%",
@@ -497,19 +518,25 @@ const ProgramStartForFreeTextContainer = styled.span(({ theme }) => ({
497518
...theme.typography.body2,
498519
}))
499520

500-
const ProgramStartForFreeTextStrong = styled.span(({ theme }) => ({
521+
const ProgramStartForFreeTextStrong = styled.span({
501522
...theme.typography.subtitle2,
502523
color: "#008000",
503-
}))
524+
})
504525

505-
const ProgramStartForFreeTextRegular = styled.span(({ theme }) => ({
526+
const ProgramStartForFreeTextRegular = styled.span({
506527
...theme.typography.body2,
507528
color: theme.custom.colors.darkGray2,
508-
}))
529+
})
509530

510-
const ProgramStartForFreeLink = styled(UnderlinedLink)(({ theme }) => ({
511-
...theme.typography.body2,
512-
color: theme.custom.colors.darkGray2,
531+
const ProgramStartForFreeInfoIcon = styled.span(({ theme }) => ({
532+
display: "inline-flex",
533+
alignItems: "center",
534+
flexShrink: 0,
535+
color: theme.custom.colors.silverGrayDark,
536+
"& svg": {
537+
width: "20px",
538+
height: "20px",
539+
},
513540
}))
514541

515542
const CertificateBoxRoot = styled.div(({ theme }) => ({
@@ -918,71 +945,84 @@ const ProgramPriceRow: React.FC<ProgramPriceRowProps> = ({
918945
const paidSection = currentPrice ? (
919946
<ProgramPaySection>
920947
<ProgramPayLabel>Price</ProgramPayLabel>
921-
<ProgramPayContent>
922-
<ProgramPriceLine>
948+
<ProgramPriceRowInner>
949+
<ProgramCurrentPriceBlock>
923950
<ProgramPriceAmount>
924951
{formatPrice(currentPrice, { avoidCents: true })}
925952
</ProgramPriceAmount>
926-
<ProgramPriceSuffix>/ full program</ProgramPriceSuffix>
927-
</ProgramPriceLine>
928-
{hasSavings && savingsAmount !== null && listAmount !== null ? (
929-
<ProgramDiscountBlock>
930-
<ProgramSavingsText>
931-
Save {formatPrice(savingsAmount, { avoidCents: true })}
932-
</ProgramSavingsText>
933-
<ProgramListPriceText>
953+
<ProgramPriceSuffix>full program</ProgramPriceSuffix>
954+
</ProgramCurrentPriceBlock>
955+
{hasSavings && listAmount !== null ? (
956+
<>
957+
<ProgramVerticalDivider />
958+
<ProgramListPriceBlock>
934959
<ProgramListPriceAmount>
935960
{formatPrice(listAmount, { avoidCents: true })}
936-
</ProgramListPriceAmount>{" "}
937-
total for {totalRequired} {pluralize("course", totalRequired)}{" "}
938-
purchased separately
939-
</ProgramListPriceText>
940-
</ProgramDiscountBlock>
961+
</ProgramListPriceAmount>
962+
<ProgramListPriceSubLabel>
963+
purchased separately
964+
</ProgramListPriceSubLabel>
965+
</ProgramListPriceBlock>
966+
</>
941967
) : null}
942-
{program.page.financial_assistance_form_url ? (
943-
<SecondaryUnderlinedLink
944-
color="black"
945-
href={mitxonlineLegacyUrl(
946-
program.page.financial_assistance_form_url,
947-
)}
968+
</ProgramPriceRowInner>
969+
{hasSavings && savingsAmount !== null ? (
970+
<ProgramDiscountRow>
971+
<ProgramSavingsText>
972+
Save {formatPrice(savingsAmount, { avoidCents: true })}
973+
</ProgramSavingsText>
974+
<ProgramSavingsDetailText>
975+
compared to purchasing {totalRequired}{" "}
976+
{pluralize("course", totalRequired)} separately
977+
</ProgramSavingsDetailText>
978+
</ProgramDiscountRow>
979+
) : null}
980+
{program.page.financial_assistance_form_url ? (
981+
<SecondaryUnderlinedLink
982+
color="black"
983+
href={mitxonlineLegacyUrl(
984+
program.page.financial_assistance_form_url,
985+
)}
986+
target="_blank"
987+
rel="noopener noreferrer"
988+
style={{ minWidth: "fit-content" }}
989+
>
990+
Financial assistance available
991+
</SecondaryUnderlinedLink>
992+
) : null}
993+
{enrollmentType === "both" ? (
994+
<ProgramStartForFreeBox>
995+
<ProgramStartForFreeIcon
996+
width="24"
997+
height="24"
998+
viewBox="0 0 22 19"
999+
fill="none"
1000+
xmlns="http://www.w3.org/2000/svg"
1001+
aria-hidden="true"
1002+
>
1003+
<path d="M14 0C16.2091 0 18 1.79086 18 4C18 4.72903 17.8049 5.41251 17.4642 6.00111L22 6V7.99999H20V18C20 18.5523 19.5523 19 19 19H3C2.44772 19 2 18.5523 2 18V7.99999H0V6L4.53577 6.00111C4.19504 5.41251 4 4.72903 4 4C4 1.79086 5.79086 0 8 0C9.19522 0 10.268 0.52421 11.0009 1.35526C11.732 0.52421 12.8048 0 14 0ZM10 7.99999H4V17H10V7.99999ZM18 7.99999H12V17H18V7.99999ZM8 2C6.89543 2 6 2.89543 6 4C6 5.05436 6.81588 5.91816 7.85074 5.99451L8 6H10V4C10 2.99835 9.26372 2.16869 8.30278 2.02277L8.14927 2.00548L8 2ZM14 2C12.9456 2 12.0818 2.81588 12.0055 3.85074L12 4V6H14C15.0543 6 15.9181 5.18412 15.9945 4.14926L16 4C16 2.89543 15.1046 2 14 2Z" />
1004+
</ProgramStartForFreeIcon>
1005+
<ProgramStartForFreeTextContainer>
1006+
<ProgramStartForFreeTextStrong>
1007+
Audit for free
1008+
</ProgramStartForFreeTextStrong>
1009+
<ProgramStartForFreeTextRegular>
1010+
or upgrade to certificate
1011+
</ProgramStartForFreeTextRegular>
1012+
</ProgramStartForFreeTextContainer>
1013+
<a
1014+
href={PROGRAM_CERT_INFO_HREF}
9481015
target="_blank"
9491016
rel="noopener noreferrer"
950-
style={{ minWidth: "fit-content" }}
1017+
aria-label="Learn more about program certificates"
1018+
style={{ display: "inline-flex", alignItems: "center" }}
9511019
>
952-
Financial assistance available
953-
</SecondaryUnderlinedLink>
954-
) : null}
955-
{enrollmentType === "both" ? (
956-
<ProgramStartForFreeBox>
957-
<ProgramStartForFreeIcon
958-
width="24"
959-
height="24"
960-
viewBox="0 0 22 19"
961-
fill="none"
962-
xmlns="http://www.w3.org/2000/svg"
963-
aria-hidden="true"
964-
>
965-
<path d="M14 0C16.2091 0 18 1.79086 18 4C18 4.72903 17.8049 5.41251 17.4642 6.00111L22 6V7.99999H20V18C20 18.5523 19.5523 19 19 19H3C2.44772 19 2 18.5523 2 18V7.99999H0V6L4.53577 6.00111C4.19504 5.41251 4 4.72903 4 4C4 1.79086 5.79086 0 8 0C9.19522 0 10.268 0.52421 11.0009 1.35526C11.732 0.52421 12.8048 0 14 0ZM10 7.99999H4V17H10V7.99999ZM18 7.99999H12V17H18V7.99999ZM8 2C6.89543 2 6 2.89543 6 4C6 5.05436 6.81588 5.91816 7.85074 5.99451L8 6H10V4C10 2.99835 9.26372 2.16869 8.30278 2.02277L8.14927 2.00548L8 2ZM14 2C12.9456 2 12.0818 2.81588 12.0055 3.85074L12 4V6H14C15.0543 6 15.9181 5.18412 15.9945 4.14926L16 4C16 2.89543 15.1046 2 14 2Z" />
966-
</ProgramStartForFreeIcon>
967-
<ProgramStartForFreeTextContainer>
968-
<ProgramStartForFreeTextStrong>
969-
Start for free
970-
</ProgramStartForFreeTextStrong>
971-
<ProgramStartForFreeTextRegular>
972-
or upgrade to{" "}
973-
</ProgramStartForFreeTextRegular>
974-
<ProgramStartForFreeLink
975-
color="black"
976-
href={PROGRAM_CERT_INFO_HREF}
977-
target="_blank"
978-
rel="noopener noreferrer"
979-
>
980-
certificate
981-
</ProgramStartForFreeLink>
982-
</ProgramStartForFreeTextContainer>
983-
</ProgramStartForFreeBox>
984-
) : null}
985-
</ProgramPayContent>
1020+
<ProgramStartForFreeInfoIcon>
1021+
<RiInformation2Line aria-hidden="true" />
1022+
</ProgramStartForFreeInfoIcon>
1023+
</a>
1024+
</ProgramStartForFreeBox>
1025+
) : null}
9861026
</ProgramPaySection>
9871027
) : (
9881028
<InfoLabelValue label="Price" value="Price unavailable" />

0 commit comments

Comments
 (0)