Skip to content

Commit

Permalink
feat: add exec ed modal (#276)
Browse files Browse the repository at this point in the history
  • Loading branch information
kiram15 authored Dec 16, 2022
1 parent 1405266 commit 9259b69
Show file tree
Hide file tree
Showing 8 changed files with 123 additions and 23 deletions.
8 changes: 7 additions & 1 deletion src/components/catalogInfoModal/CatalogInfoModal.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -30,7 +30,7 @@ SkillsListing.propTypes = {
};

function CourseModal({
intl, isOpen, onClose, selectedCourse,
intl, isOpen, onClose, isExecEdType, selectedCourse,
}) {
const {
courseTitle,
Expand Down Expand Up @@ -87,6 +87,7 @@ function CourseModal({
startDate={startDate}
endDate={endDate}
upcomingRuns={upcomingRuns}
execEd={isExecEdType}
/>
<p className="h3">
{intl.formatMessage(
Expand Down Expand Up @@ -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,
Expand Down Expand Up @@ -332,6 +334,7 @@ function CatalogCourseInfoModal({
intl,
isOpen,
onClose,
isExecEdType,
selectedCourse,
selectedProgram,
renderProgram,
Expand All @@ -349,6 +352,7 @@ function CatalogCourseInfoModal({
intl={intl}
isOpen={isOpen}
onClose={onClose}
isExecEdType={isExecEdType}
/>
);
}
Expand All @@ -366,6 +370,7 @@ function CatalogCourseInfoModal({
CatalogCourseInfoModal.defaultProps = {
isOpen: false,
renderProgram: false,
isExecEdType: false,
selectedCourse: {},
selectedProgram: {},
onClose: () => {},
Expand All @@ -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,
Expand Down
54 changes: 54 additions & 0 deletions src/components/catalogInfoModal/CatalogInfoModal.test.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -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(
<IntlProvider locale="en">
<CatalogInfoModal {...execEdTypeModalProps} />
</IntlProvider>,
);

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(
<IntlProvider locale="en">
<CatalogInfoModal {...execEdTypeModalProps} />
</IntlProvider>,
);
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,
Expand Down
53 changes: 38 additions & 15 deletions src/components/catalogModalBanner/CatalogCourseModalBanner.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -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,
Expand Down Expand Up @@ -41,13 +43,14 @@ function CatalogCourseModalBanner({
startDate,
endDate,
upcomingRuns,
execEd,
}) {
return (
<div className="my-4.5 banner">
<div className="banner-section mx-3">
<div className="banner h4 mb-0">
<Icon className="mr-1" src={MoneyOutline} />
{coursePrice}
{coursePrice.split('.')[0]}
</div>
<div className="banner-subtitle small">
{intl.formatMessage(
Expand All @@ -56,21 +59,39 @@ function CatalogCourseModalBanner({
</div>
</div>
<div className="banner-section slash">/</div>
{checkSubscriptions(courseAssociatedCatalogs) && (
<div className="banner-section mx-3">
<div className="banner h4 mb-0">
<Icon className="mr-1" src={BookOpen} />
{intl.formatMessage(
messages['CatalogCourseModalBanner.bannerCatalogText'],
)}
</div>
<div className="banner-subtitle small">
{checkSubscriptions(courseAssociatedCatalogs)}
{checkSubscriptions(courseAssociatedCatalogs) && !execEd && (
<>
<div className="banner-section mx-3">
<div className="banner h4 mb-0">
<Icon className="mr-1" src={BookOpen} />
{intl.formatMessage(
messages['CatalogCourseModalBanner.bannerCatalogText'],
)}
</div>
<div className="banner-subtitle small">
{checkSubscriptions(courseAssociatedCatalogs)}
</div>
</div>
</div>
<div className="banner-section slash">/</div>
</>
)}
{checkSubscriptions(courseAssociatedCatalogs) && (
<div className="banner-section slash">/</div>
{execEd && (
<>
<div className="banner-section mx-3">
<div className="banner h4 mb-0">
<Icon className="mr-1" src={Book} />
{intl.formatMessage(
messages['CatalogCourseModalBanner.bannerExecEdText'],
)}
</div>
<div className="banner-subtitle small">
{intl.formatMessage(
messages['CatalogCourseModalBanner.bannerExecEdSubtext'],
)}
</div>
</div>
<div className="banner-section slash">/</div>
</>
)}
<div className="banner-section mx-3">
<div className="banner h4 mb-0">
Expand All @@ -91,6 +112,7 @@ CatalogCourseModalBanner.defaultProps = {
startDate: '',
endDate: '',
upcomingRuns: 0,
execEd: false,
};

CatalogCourseModalBanner.propTypes = {
Expand All @@ -100,6 +122,7 @@ CatalogCourseModalBanner.propTypes = {
startDate: PropTypes.string,
endDate: PropTypes.string,
upcomingRuns: PropTypes.number,
execEd: PropTypes.bool,
};

export default injectIntl(CatalogCourseModalBanner);
Original file line number Diff line number Diff line change
Expand Up @@ -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',
Expand All @@ -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;
3 changes: 2 additions & 1 deletion src/components/catalogSearchResults/CatalogSearchResults.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -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),
},
{
Expand Down Expand Up @@ -414,6 +414,7 @@ export function BaseCatalogSearchResults({
isOpen={isCourse}
onClose={() => setSelectedCourse(null)}
selectedCourse={selectedCourse}
isExecEdType={isExecEdType}
/>
)}
{isProgramType && (
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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',
Expand Down
9 changes: 7 additions & 2 deletions src/components/courseCard/CourseCard.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand All @@ -52,7 +57,7 @@ function CourseCard({
<span className="cards-spacing" />
<Card.Section>
<p className="my-3">
{priceText}{advertised_course_run ? advertised_course_run.pacing_type?.replace('_', ' ') : 'NA'}
{priceText}{pacingType}
</p>
<div style={{ maxWidth: '400vw' }}>
{enterprise_catalog_query_titles.includes(
Expand Down
7 changes: 4 additions & 3 deletions src/components/courseCard/CourseCard.test.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -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 = {
Expand All @@ -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 () => {
Expand All @@ -80,7 +80,8 @@ describe('Course card works as expected', () => {
</IntlProvider>,
);
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();
});
});

0 comments on commit 9259b69

Please sign in to comment.