Skip to content

Commit

Permalink
Fixes start date formatting in CourseInfoBox, refactors date display …
Browse files Browse the repository at this point in the history
…for reuse (#1992)
  • Loading branch information
jkachel authored Nov 14, 2023
1 parent 3a75b8c commit 626e6a4
Show file tree
Hide file tree
Showing 5 changed files with 199 additions and 149 deletions.
19 changes: 6 additions & 13 deletions frontend/public/src/components/CourseInfoBox.js
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,8 @@ import {
formatPrettyDate,
emptyOrNil,
getFlexiblePriceForProduct,
formatLocalePrice
formatLocalePrice,
getStartDateText
} from "../lib/util"
import moment from "moment-timezone"

Expand All @@ -20,16 +21,6 @@ type CourseInfoBoxProps = {
setCurrentCourseRun: (run: EnrollmentFlaggedCourseRun) => Promise<any>
}

const getStartDateText = (run: BaseCourseRun, isArchived: boolean = false) => {
if (isArchived) {
return "Course content available anytime"
}
return run && !emptyOrNil(run.start_date) && !run.is_self_paced
? (run.start_date > moment() ? "Starts: " : "Started: ") +
formatPrettyDate(moment(new Date(run.start_date)))
: "Start Anytime"
}

export default class CourseInfoBox extends React.PureComponent<CourseInfoBoxProps> {
state = {
showMoreEnrollDates: false
Expand Down Expand Up @@ -84,7 +75,7 @@ export default class CourseInfoBox extends React.PureComponent<CourseInfoBoxProp
enrollment.run.id === courseRun.id
))
? this.renderEnrolledDateLink(courseRun)
: getStartDateText(courseRun)}
: getStartDateText(courseRun, true)}
</li>
)
}
Expand All @@ -110,7 +101,9 @@ export default class CourseInfoBox extends React.PureComponent<CourseInfoBoxProp
/>
</div>
<div className="enrollment-info-text">
{getStartDateText(run, isArchived)}
{isArchived
? "Course content available anytime"
: getStartDateText(run)}
</div>
{!isArchived && moreEnrollableCourseRuns ? (
<>
Expand Down
56 changes: 2 additions & 54 deletions frontend/public/src/containers/pages/CatalogPage.js
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
import React from "react"
import { CSSTransition, TransitionGroup } from "react-transition-group"
import moment from "moment"
import { parseDateString, formatPrettyDate } from "../../lib/util"
import { getStartDateText } from "../../lib/util"

import {
coursesSelector,
Expand Down Expand Up @@ -298,58 +298,6 @@ export class CatalogPage extends React.Component<Props> {
this.toggleMobileFilterWindowExpanded(false)
}

/**
* This is a comparison method used to sort an array of Course Runs
* from earliest start date to latest start date.
* @param {BaseCourseRun} courseRunA The first Course Run to compare.
* @param {BaseCourseRun} courseRunB The second Course Run to compare.
*/
compareCourseRunStartDates(
courseRunA: BaseCourseRun,
courseRunB: BaseCourseRun
) {
if (moment(courseRunA.start_date).isBefore(courseRunB.start_date)) {
return -1
}
if (moment(courseRunA.start_date).isAfter(courseRunB.start_date)) {
return 1
}
// CourseRunA and CourseRunB share the same start date.
return 0
}

/**
* Returns the text to be displayed on a course catalog card's tag.
* This text will either be "Start Anytime" or "Start Date: <most recent, future, start date for the course>".
* If the Course has at least one associated Course Run which is not self-paced, and
* Course Run start date is in the future, then return "Start Date: <most recent, future, start date for the course>".
* If the Course has at least one associated Course Run which is not self-paced, and
* Course Run start date is in the past, then return "Start Anytime".
* If the course only has Course Runs which are self-paced, display "Start Anytime".
* @param {CourseDetailWithRuns} course The course being evaluated.
*/
renderCatalogCardTagForCourse(course: CourseDetailWithRuns) {
const nonSelfPacedCourseRuns = course.courseruns.filter(
courseRun => !courseRun.is_self_paced
)
if (nonSelfPacedCourseRuns.length > 0) {
const futureStartDateCourseRuns = nonSelfPacedCourseRuns.filter(
courseRun => moment(courseRun.start_date).isAfter(moment())
)
if (futureStartDateCourseRuns.length > 0) {
const startDate = parseDateString(
futureStartDateCourseRuns.sort(this.compareCourseRunStartDates)[0]
.start_date
)
return `Start Date: ${formatPrettyDate(startDate)}`
} else {
return "Start Anytime"
}
} else {
return "Start Anytime"
}
}

/**
* Returns a filtered array of Course Runs which are live, define a start date,
* enrollment start date is before the current date and time, and
Expand Down Expand Up @@ -436,7 +384,7 @@ export class CatalogPage extends React.Component<Props> {
/>
<div className="catalog-item-description">
<div className="start-date-description">
{this.renderCatalogCardTagForCourse(course)}
{getStartDateText(course)}
</div>
<div className="item-title">{course.title}</div>
</div>
Expand Down
81 changes: 0 additions & 81 deletions frontend/public/src/containers/pages/CatalogPage_test.js
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,6 @@ import { expect } from "chai"
import moment from "moment-timezone"
import IntegrationTestHelper from "../../util/integration_test_helper"
import CatalogPage, { CatalogPage as InnerCatalogPage } from "./CatalogPage"
import { formatPrettyDate } from "../../lib/util"
import sinon from "sinon"

const displayedCourse = {
Expand Down Expand Up @@ -486,86 +485,6 @@ describe("CatalogPage", function() {
expect(coursesFilteredByCriteriaAndDepartment.length).equals(1)
})

it("renders catalog course card with Start Anytime label if non-self paced course runs exist and all course runs start date in the past", async () => {
const course = JSON.parse(JSON.stringify(displayedCourse))
const { inner } = await renderPage()
let catalogCardTagForCourse = inner
.instance()
.renderCatalogCardTagForCourse(course)
expect(catalogCardTagForCourse).equals("Start Anytime")
course.courseruns[0].start_date = moment().add(2, "M")
catalogCardTagForCourse = inner
.instance()
.renderCatalogCardTagForCourse(course)
expect(catalogCardTagForCourse).equals(
`Start Date: ${formatPrettyDate(course.courseruns[0].start_date)}`
)
})

it("renders catalog course card with start date label if non-self paced course runs exist and all course runs start in the future", async () => {
const course = JSON.parse(JSON.stringify(displayedCourse))
const { inner } = await renderPage()
let catalogCardTagForCourse = inner
.instance()
.renderCatalogCardTagForCourse(course)
expect(catalogCardTagForCourse).equals("Start Anytime")
course.courseruns[0].start_date = moment().add(2, "M")
catalogCardTagForCourse = inner
.instance()
.renderCatalogCardTagForCourse(course)
expect(catalogCardTagForCourse).equals(
`Start Date: ${formatPrettyDate(course.courseruns[0].start_date)}`
)
})

it("renders catalog course card with closest future start date label if non-self paced course runs exist and a course run start date is in the future", async () => {
const course = JSON.parse(JSON.stringify(displayedCourse))
// Associate a second course run with the course
course.courseruns.push(
JSON.parse(JSON.stringify(displayedCourse.courseruns[0]))
)
course.courseruns.push(
JSON.parse(JSON.stringify(displayedCourse.courseruns[0]))
)
course.courseruns.push(
JSON.parse(JSON.stringify(displayedCourse.courseruns[0]))
)
const { inner } = await renderPage()
let catalogCardTagForCourse = inner
.instance()
.renderCatalogCardTagForCourse(course)
expect(catalogCardTagForCourse).equals("Start Anytime")

// Update the start dates of each associated course run to be in the future.
course.courseruns[0].start_date = moment().add(2, "M")
course.courseruns[1].start_date = moment().add(3, "d")
course.courseruns[2].start_date = moment().add(2, "d")
course.courseruns[3].is_self_paced = true
catalogCardTagForCourse = inner
.instance()
.renderCatalogCardTagForCourse(course)
// Expect the closest future start date
expect(catalogCardTagForCourse).equals(
`Start Date: ${formatPrettyDate(course.courseruns[2].start_date)}`
)
})

it("renders catalog course card with Start Anytime if only self paced course runs exist, even if start date is in the future", async () => {
const course = JSON.parse(JSON.stringify(displayedCourse))
const { inner } = await renderPage()
let catalogCardTagForCourse = inner
.instance()
.renderCatalogCardTagForCourse(course)
expect(catalogCardTagForCourse).equals("Start Anytime")

course.courseruns[0].start_date = moment().add(2, "M")
course.courseruns[0].is_self_paced = true
catalogCardTagForCourse = inner
.instance()
.renderCatalogCardTagForCourse(course)
expect(catalogCardTagForCourse).equals("Start Anytime")
})

it("renders catalog courses based on selected department", async () => {
const course1 = JSON.parse(JSON.stringify(displayedCourse))
course1.departments = [{ name: "Math" }]
Expand Down
90 changes: 90 additions & 0 deletions frontend/public/src/lib/util.js
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,7 @@ import posthog from "posthog-js"
import type Moment from "moment"
import type { HttpRespErrorMessage, HttpResponse } from "../flow/httpTypes"
import type { Product } from "../flow/cartTypes"
import type { BaseCourseRun, CourseDetailWithRuns } from "../flow/courseTypes"

import {
DISCOUNT_TYPE_DOLLARS_OFF,
Expand Down Expand Up @@ -283,3 +284,92 @@ export const intCheckFeatureFlag = (
export const checkFeatureFlag = (flag: string, uniqueID: string | number) => {
return intCheckFeatureFlag(flag, uniqueID, document, SETTINGS)
}

/**
* This is a comparison method used to sort an array of Course Runs
* from earliest start date to latest start date.
* @param {BaseCourseRun} courseRunA The first Course Run to compare.
* @param {BaseCourseRun} courseRunB The second Course Run to compare.
*/
export const compareCourseRunStartDates = (
courseRunA: BaseCourseRun,
courseRunB: BaseCourseRun
) => {
if (moment(courseRunA.start_date).isBefore(courseRunB.start_date)) {
return -1
}
if (moment(courseRunA.start_date).isAfter(courseRunB.start_date)) {
return 1
}
// CourseRunA and CourseRunB share the same start date.
return 0
}

/**
* This is a comparison method used to sort an array of Course Runs
* from latest start date to earliest start date.
* @param {BaseCourseRun} courseRunA The first Course Run to compare.
* @param {BaseCourseRun} courseRunB The second Course Run to compare.
*/
export const reverseCompareCourseRunStartDates = (
courseRunA: BaseCourseRun,
courseRunB: BaseCourseRun
) => {
if (moment(courseRunA.start_date).isBefore(courseRunB.start_date)) {
return 1
}
if (moment(courseRunA.start_date).isAfter(courseRunB.start_date)) {
return -1
}
// CourseRunA and CourseRunB share the same start date.
return 0
}

/**
* Returns the text to be displayed on a course catalog card's tag.
* This text will either be "Start Anytime" or "Start Date: <most recent, future, start date for the course>".
* If the Course has at least one associated Course Run which is not self-paced, and
* Course Run start date is in the future, then return "Start Date: <most recent, future, start date for the course>".
* If the Course has at least one associated Course Run which is not self-paced, and
* Course Run start date is in the past, and showPast is not true, then return "Start Anytime".
* If the Course has at least one associated Course Run which is not self-paced, and
* Course Run start date is in the past, and showPast is true, then return "Start Date: <most recent start date for the course>".
* If the course only has Course Runs which are self-paced, display "Start Anytime".
* @param {CourseDetailWithRuns|BaseCourseRun} course The course being evaluated, or an individual course run to display the start text for.
* @param {showPast} boolean If the start date for the course is in the past, and showPast is true, then render the most recent start date for the course.
*/

export const getStartDateText = (
courseware: BaseCourseRun | CourseDetailWithRuns,
showPast: boolean = false
) => {
const nonSelfPacedCourseRuns = courseware.courseruns
? courseware.courseruns.filter(courseRun => !courseRun.is_self_paced)
: courseware.is_self_paced
? []
: [courseware]

if (nonSelfPacedCourseRuns.length > 0) {
const futureStartDateCourseRuns = nonSelfPacedCourseRuns.filter(courseRun =>
moment(courseRun.start_date).isAfter(moment())
)
if (futureStartDateCourseRuns.length > 0) {
const startDate = parseDateString(
futureStartDateCourseRuns.sort(compareCourseRunStartDates)[0].start_date
)
return `Start Date: ${formatPrettyDate(startDate)}`
} else {
if (showPast) {
return `Start Date: ${formatPrettyDate(
parseDateString(
nonSelfPacedCourseRuns.sort(reverseCompareCourseRunStartDates)[0]
.start_date
)
)}`
}
return "Start Anytime"
}
} else {
return "Start Anytime"
}
}
Loading

0 comments on commit 626e6a4

Please sign in to comment.