diff --git a/src/components/catalogInfoModal/CatalogInfoModal.jsx b/src/components/catalogInfoModal/CatalogInfoModal.jsx
index cb1bac53..6171946b 100644
--- a/src/components/catalogInfoModal/CatalogInfoModal.jsx
+++ b/src/components/catalogInfoModal/CatalogInfoModal.jsx
@@ -30,7 +30,7 @@ SkillsListing.propTypes = {
};
function CourseModal({
- intl, isOpen, onClose, selectedCourse,
+ intl, isOpen, onClose, isExecEdType, selectedCourse,
}) {
const {
courseTitle,
@@ -87,6 +87,7 @@ function CourseModal({
startDate={startDate}
endDate={endDate}
upcomingRuns={upcomingRuns}
+ execEd={isExecEdType}
/>
{intl.formatMessage(
@@ -138,6 +139,7 @@ CourseModal.propTypes = {
intl: intlShape.isRequired,
isOpen: PropTypes.bool.isRequired,
onClose: PropTypes.func.isRequired,
+ isExecEdType: PropTypes.bool.isRequired,
selectedCourse: PropTypes.shape({
courseTitle: PropTypes.string,
courseProvider: PropTypes.string,
@@ -332,6 +334,7 @@ function CatalogCourseInfoModal({
intl,
isOpen,
onClose,
+ isExecEdType,
selectedCourse,
selectedProgram,
renderProgram,
@@ -349,6 +352,7 @@ function CatalogCourseInfoModal({
intl={intl}
isOpen={isOpen}
onClose={onClose}
+ isExecEdType={isExecEdType}
/>
);
}
@@ -366,6 +370,7 @@ function CatalogCourseInfoModal({
CatalogCourseInfoModal.defaultProps = {
isOpen: false,
renderProgram: false,
+ isExecEdType: false,
selectedCourse: {},
selectedProgram: {},
onClose: () => {},
@@ -376,6 +381,7 @@ CatalogCourseInfoModal.propTypes = {
intl: intlShape.isRequired,
isOpen: PropTypes.bool,
onClose: PropTypes.func,
+ isExecEdType: PropTypes.bool,
selectedCourse: PropTypes.shape({
courseTitle: PropTypes.string,
courseProvider: PropTypes.string,
diff --git a/src/components/catalogInfoModal/CatalogInfoModal.test.jsx b/src/components/catalogInfoModal/CatalogInfoModal.test.jsx
index febe1207..fd9f7aef 100644
--- a/src/components/catalogInfoModal/CatalogInfoModal.test.jsx
+++ b/src/components/catalogInfoModal/CatalogInfoModal.test.jsx
@@ -163,6 +163,60 @@ describe('Course info modal works as expected', () => {
});
});
+describe('Executive Education info modal works as expected', () => {
+ const execEdTypeModalProps = {
+ isOpen: true,
+ isExecEdType: true,
+ selectedCourse: {
+ courseTitle: 'Exec Ed Title',
+ courseProvider: 'Provider',
+ coursePrice: '100.00',
+ courseAssociatedCatalogs: [],
+ courseDescription: descriptionHtml,
+ partnerLogoImageUrl: '',
+ bannerImageUrl: '',
+ startDate: '2020-09-15T16:00:00Z',
+ endDate: '2040-05-04T16:00:00Z',
+ upcomingRuns: 0,
+ marketingUrl: 'http://someurl',
+ skillNames: [],
+ },
+ };
+ const OLD_ENV = process.env;
+ beforeEach(() => {
+ jest.resetModules(); // Most important - it clears the cache
+ process.env = { ...OLD_ENV }; // Make a copy
+ });
+ afterAll(() => {
+ process.env = OLD_ENV; // Restore old environment
+ });
+ test('Executive Education info modal renders when expected', () => {
+ render(
+
+
+ ,
+ );
+
+ const { selectedCourse } = execEdTypeModalProps;
+ expect(screen.queryByText(selectedCourse.courseTitle)).toBeInTheDocument();
+ expect(screen.queryByText(selectedCourse.courseProvider)).toBeInTheDocument();
+ expect(screen.queryByText(descriptionText)).toBeInTheDocument();
+ });
+ test('Executive Education modal banner renders correctly', () => {
+ render(
+
+
+ ,
+ );
+ expect(screen.queryByText('A la carte course price')).toBeInTheDocument();
+ // price should truncate after decimal
+ expect(screen.queryByText('100')).toBeInTheDocument();
+ expect(screen.queryByText('Executive Education')).toBeInTheDocument();
+ expect(screen.queryByText('Immersive, instructor-led course')).toBeInTheDocument();
+ expect(screen.queryByText('Session ends May 4, 2040'));
+ });
+});
+
describe('Program info modal works as expected', () => {
const programTypeModalProps = {
...courseTypeModalProps,
diff --git a/src/components/catalogModalBanner/CatalogCourseModalBanner.jsx b/src/components/catalogModalBanner/CatalogCourseModalBanner.jsx
index 2d10a4cf..daf982c2 100644
--- a/src/components/catalogModalBanner/CatalogCourseModalBanner.jsx
+++ b/src/components/catalogModalBanner/CatalogCourseModalBanner.jsx
@@ -3,7 +3,9 @@ import PropTypes from 'prop-types';
import { Icon } from '@edx/paragon';
import { injectIntl, intlShape } from '@edx/frontend-platform/i18n';
-import { BookOpen, EventNote, MoneyOutline } from '@edx/paragon/icons';
+import {
+ Book, BookOpen, EventNote, MoneyOutline,
+} from '@edx/paragon/icons';
import messages from './CatalogCourseModalBanner.messages';
import {
checkAvailability,
@@ -41,13 +43,14 @@ function CatalogCourseModalBanner({
startDate,
endDate,
upcomingRuns,
+ execEd,
}) {
return (
- {coursePrice}
+ {coursePrice.split('.')[0]}
{intl.formatMessage(
@@ -56,21 +59,39 @@ function CatalogCourseModalBanner({
/
- {checkSubscriptions(courseAssociatedCatalogs) && (
-
-
-
- {intl.formatMessage(
- messages['CatalogCourseModalBanner.bannerCatalogText'],
- )}
-
-
- {checkSubscriptions(courseAssociatedCatalogs)}
+ {checkSubscriptions(courseAssociatedCatalogs) && !execEd && (
+ <>
+
+
+
+ {intl.formatMessage(
+ messages['CatalogCourseModalBanner.bannerCatalogText'],
+ )}
+
+
+ {checkSubscriptions(courseAssociatedCatalogs)}
+
-
+
/
+ >
)}
- {checkSubscriptions(courseAssociatedCatalogs) && (
-
/
+ {execEd && (
+ <>
+
+
+
+ {intl.formatMessage(
+ messages['CatalogCourseModalBanner.bannerExecEdText'],
+ )}
+
+
+ {intl.formatMessage(
+ messages['CatalogCourseModalBanner.bannerExecEdSubtext'],
+ )}
+
+
+
/
+ >
)}
@@ -91,6 +112,7 @@ CatalogCourseModalBanner.defaultProps = {
startDate: '',
endDate: '',
upcomingRuns: 0,
+ execEd: false,
};
CatalogCourseModalBanner.propTypes = {
@@ -100,6 +122,7 @@ CatalogCourseModalBanner.propTypes = {
startDate: PropTypes.string,
endDate: PropTypes.string,
upcomingRuns: PropTypes.number,
+ execEd: PropTypes.bool,
};
export default injectIntl(CatalogCourseModalBanner);
diff --git a/src/components/catalogModalBanner/CatalogCourseModalBanner.messages.js b/src/components/catalogModalBanner/CatalogCourseModalBanner.messages.js
index f9ed2d09..bdc601c0 100644
--- a/src/components/catalogModalBanner/CatalogCourseModalBanner.messages.js
+++ b/src/components/catalogModalBanner/CatalogCourseModalBanner.messages.js
@@ -6,6 +6,11 @@ const messages = defineMessages({
defaultMessage: 'Included with subscription',
description: 'Text for modal subheader banner for catalog inclusion.',
},
+ 'CatalogCourseModalBanner.bannerExecEdText': {
+ id: 'CatalogCourseModalBanner.bannerExecEdText',
+ defaultMessage: 'Executive Education',
+ description: 'Text for modal subheader banner for exec ed courses.',
+ },
'CatalogCourseModalBanner.bannerPriceText': {
id: 'CatalogCourseModalBanner.bannerPriceText',
defaultMessage: 'A la carte course price',
@@ -21,6 +26,11 @@ const messages = defineMessages({
defaultMessage: 'Skill-building selection',
description: 'Text for modal subheader banner for courses.',
},
+ 'CatalogCourseModalBanner.bannerExecEdSubtext': {
+ id: 'CatalogCourseModalBanner.bannerExecEdSubtext',
+ defaultMessage: 'Immersive, instructor-led course',
+ description: 'Text for modal subheader banner for exec ed courses.',
+ },
});
export default messages;
diff --git a/src/components/catalogSearchResults/CatalogSearchResults.jsx b/src/components/catalogSearchResults/CatalogSearchResults.jsx
index cd11d74d..42d81ffb 100644
--- a/src/components/catalogSearchResults/CatalogSearchResults.jsx
+++ b/src/components/catalogSearchResults/CatalogSearchResults.jsx
@@ -249,7 +249,7 @@ export function BaseCatalogSearchResults({
Header: TABLE_HEADERS.price,
accessor: 'entitlements',
Cell: ({ row }) => (row.values.entitlements[0].price
- ? `$${row.values.entitlements[0].price}`
+ ? `$${Math.trunc(row.values.entitlements[0].price)}`
: null),
},
{
@@ -414,6 +414,7 @@ export function BaseCatalogSearchResults({
isOpen={isCourse}
onClose={() => setSelectedCourse(null)}
selectedCourse={selectedCourse}
+ isExecEdType={isExecEdType}
/>
)}
{isProgramType && (
diff --git a/src/components/catalogSearchResults/CatalogSearchResults.test.jsx b/src/components/catalogSearchResults/CatalogSearchResults.test.jsx
index cca17ae4..25e0ed1a 100644
--- a/src/components/catalogSearchResults/CatalogSearchResults.test.jsx
+++ b/src/components/catalogSearchResults/CatalogSearchResults.test.jsx
@@ -151,7 +151,7 @@ const searchResultsExecEd = {
availability: ['Available Now'],
course_keys: [],
content_type: EXECUTIVE_EDUCATION_2U_COURSE_TYPE,
- entitlements: [{ price: '100' }],
+ entitlements: [{ price: '100.00' }],
advertised_course_run: {
start: '2020-01-24T05:00:00Z',
end: '2080-01-01T17:00:00Z',
diff --git a/src/components/courseCard/CourseCard.jsx b/src/components/courseCard/CourseCard.jsx
index f2f9d0a0..49eb3e8a 100644
--- a/src/components/courseCard/CourseCard.jsx
+++ b/src/components/courseCard/CourseCard.jsx
@@ -29,7 +29,12 @@ function CourseCard({
priceText = rowPrice != null ? `$${rowPrice.toString()}` : 'N/A';
} else {
[rowPrice] = entitlements || [null];
- priceText = rowPrice != null ? `$${rowPrice.price?.toString()}` : 'N/A';
+ priceText = rowPrice != null ? `$${Math.trunc(rowPrice.price)?.toString()}` : 'N/A';
+ }
+
+ let pacingType = 'NA';
+ if (advertised_course_run) {
+ pacingType = advertised_course_run.pacing_type === 'self_paced' ? 'Self paced' : 'Instructor led';
}
const imageSrc = card_image_url || defaultCardHeader;
@@ -52,7 +57,7 @@ function CourseCard({
- {priceText} • {advertised_course_run ? advertised_course_run.pacing_type?.replace('_', ' ') : 'NA'}
+ {priceText} • {pacingType}
{enterprise_catalog_query_titles.includes(
diff --git a/src/components/courseCard/CourseCard.test.jsx b/src/components/courseCard/CourseCard.test.jsx
index 4a382d0d..aee1b495 100644
--- a/src/components/courseCard/CourseCard.test.jsx
+++ b/src/components/courseCard/CourseCard.test.jsx
@@ -35,7 +35,7 @@ const execEdData = {
original_image_url: '',
enterprise_catalog_query_titles: TEST_CATALOG,
advertised_course_run: { pacing_type: 'instructor_paced' },
- entitlements: [{ price: '999' }],
+ entitlements: [{ price: '999.00' }],
};
const execEdProps = {
@@ -57,7 +57,7 @@ describe('Course card works as expected', () => {
expect(
screen.queryByText(defaultProps.original.partners[0].name),
).toBeInTheDocument();
- expect(screen.queryByText('$100 • self paced')).toBeInTheDocument();
+ expect(screen.queryByText('$100 • Self paced')).toBeInTheDocument();
expect(screen.queryByText('Business')).toBeInTheDocument();
});
test('test card renders default image', async () => {
@@ -80,7 +80,8 @@ describe('Course card works as expected', () => {
,
);
expect(screen.queryByText(execEdProps.original.title)).toBeInTheDocument();
- expect(screen.queryByText('$999 • instructor paced')).toBeInTheDocument();
+ // price decimal should be truncated
+ expect(screen.queryByText('$999 • Instructor led')).toBeInTheDocument();
expect(screen.queryByText('Business')).toBeInTheDocument();
});
});