Skip to content

Commit

Permalink
feat: phasing out unreadable exec ed learning type (#282)
Browse files Browse the repository at this point in the history
alex-sheehan-edx authored Jan 25, 2023

Verified

This commit was created on GitHub.com and signed with GitHub’s verified signature. The key has expired.
1 parent b0f7872 commit ebc5a53
Showing 12 changed files with 80 additions and 58 deletions.
7 changes: 4 additions & 3 deletions src/components/catalogNoResultsDeck/CatalogNoResultsDeck.jsx
Original file line number Diff line number Diff line change
@@ -7,7 +7,8 @@ import PropTypes from 'prop-types';
import {
CONTENT_TYPE_COURSE,
CONTENT_TYPE_PROGRAM,
EXECUTIVE_EDUCATION_2U_COURSE_TYPE,
EXEC_ED_TITLE,
LEARNING_TYPE_REFINEMENT,
NO_RESULTS_DECK_ITEM_COUNT,
NO_RESULTS_PAGE_SIZE,
NO_RESULTS_PAGE_ITEM_COUNT,
@@ -41,7 +42,7 @@ function CatalogNoResultsDeck({
useEffect(() => {
const defaultCoursesRefinements = {
enterprise_catalog_query_titles: selectedCatalog,
learning_type: contentType,
[LEARNING_TYPE_REFINEMENT]: contentType,
};

EnterpriseCatalogApiService.fetchDefaultCoursesInCatalogWithFacets(
@@ -66,7 +67,7 @@ function CatalogNoResultsDeck({
defaultDeckTitle = intl.formatMessage(
messages['catalogSearchResults.DefaultCourseDeckTitle'],
);
} else if (contentType === EXECUTIVE_EDUCATION_2U_COURSE_TYPE) {
} else if (contentType === EXEC_ED_TITLE) {
alertText = intl.formatMessage(
messages['catalogSearchResults.NoResultsExecEdCourseBannerText'],
);
Original file line number Diff line number Diff line change
@@ -60,7 +60,7 @@ const execEdProps = {
setCardView: jest.fn(),
columns: [],
renderCardComponent: jest.fn(),
contentType: 'executive-education-2u',
contentType: 'Executive Education',
};

describe('catalog no results deck works as expected', () => {
14 changes: 6 additions & 8 deletions src/components/catalogPage/CatalogPage.jsx
Original file line number Diff line number Diff line change
@@ -17,20 +17,20 @@ import CatalogSelectionDeck from '../catalogSelectionDeck/CatalogSelectionDeck';
import {
AVAILABILITY_REFINEMENT,
AVAILABILITY_REFINEMENT_DEFAULTS,
EXECUTIVE_EDUCATION_2U_COURSE_TYPE,
EXEC_ED_TITLE,
LEARNING_TYPE_REFINEMENT,
QUERY_TITLE_REFINEMENT,
HIDE_CARDS_REFINEMENT,
TRACKING_APP_NAME,
} from '../../constants';

const LEARNING_TYPE_REFINEMENT = 'learning_type';
const learningType = {
attribute: 'learning_type',
attribute: LEARNING_TYPE_REFINEMENT,
title: 'Learning Type',
};
// Add learning_type to the search facet filters if it doesn't exist in the list yet.
if (
!SEARCH_FACET_FILTERS.some((filter) => filter.attribute === 'learning_type')
!SEARCH_FACET_FILTERS.some((filter) => filter.attribute === LEARNING_TYPE_REFINEMENT)
) {
SEARCH_FACET_FILTERS.push(learningType);
}
@@ -63,15 +63,15 @@ function CatalogPage({ intl }) {
=== config.EDX_ENTERPRISE_ALACARTE_TITLE
))
&& loadedSearchParams.get(LEARNING_TYPE_REFINEMENT)
=== EXECUTIVE_EDUCATION_2U_COURSE_TYPE
=== EXEC_ED_TITLE
) {
const loadedLearningTypes = loadedSearchParams.getAll(
LEARNING_TYPE_REFINEMENT,
);
if (loadedLearningTypes.length) {
loadedSearchParams.delete(LEARNING_TYPE_REFINEMENT);
loadedLearningTypes.forEach((type) => {
if (type !== EXECUTIVE_EDUCATION_2U_COURSE_TYPE) {
if (type !== EXEC_ED_TITLE) {
loadedSearchParams.append(LEARNING_TYPE_REFINEMENT, type);
}
});
@@ -84,7 +84,6 @@ function CatalogPage({ intl }) {
// the `a la carte` catalog
if (
config.EDX_ENTERPRISE_ALACARTE_TITLE
&& !loadedSearchParams.get(LEARNING_TYPE_REFINEMENT)
&& !loadedSearchParams.get(QUERY_TITLE_REFINEMENT)
) {
loadedSearchParams.set(
@@ -97,7 +96,6 @@ function CatalogPage({ intl }) {
// Ensure we have availability refinement(s) set by default
if (
!loadedSearchParams.get(AVAILABILITY_REFINEMENT)
&& !loadedSearchParams.get(LEARNING_TYPE_REFINEMENT)
) {
AVAILABILITY_REFINEMENT_DEFAULTS.map((a) => loadedSearchParams.append(AVAILABILITY_REFINEMENT, a));
reloadPage = true;
7 changes: 4 additions & 3 deletions src/components/catalogPage/CatalogPage.test.jsx
Original file line number Diff line number Diff line change
@@ -4,6 +4,7 @@ import React from 'react';
import { mockWindowLocations, renderWithRouter } from '../tests/testUtils';
import CatalogPage from './CatalogPage';
import selectionCardMessage from '../catalogSelectionDeck/CatalogSelectionDeck.messages';
import { LEARNING_TYPE_REFINEMENT } from '../../constants';

// all we are testing is routes, we don't need InstantSearch to work here
jest.mock('react-instantsearch-dom', () => ({
@@ -72,17 +73,17 @@ describe('CatalogPage', () => {
it('accounts for exec ed disclusion when not a la carte is selected', () => {
const location = {
...window.location,
search: '?learning_type=executive-education-2u&learning_type=ayylmao&enterprise_catalog_query_titles=foobar',
search: `?${LEARNING_TYPE_REFINEMENT}=Executive Education&${LEARNING_TYPE_REFINEMENT}=ayylmao&enterprise_catalog_query_titles=foobar`,
};
Object.defineProperty(window, 'location', {
writable: true,
value: location,
});
expect(window.location.search).toEqual('?learning_type=executive-education-2u&learning_type=ayylmao&enterprise_catalog_query_titles=foobar');
expect(window.location.search).toEqual(`?${LEARNING_TYPE_REFINEMENT}=Executive Education&${LEARNING_TYPE_REFINEMENT}=ayylmao&enterprise_catalog_query_titles=foobar`);
renderWithRouter(<CatalogPage />);
// Assert learning type: exec ed has been removed but not learning type `ayylmao`
expect(window.location.search).toEqual(
'enterprise_catalog_query_titles=foobar&learning_type=ayylmao',
`enterprise_catalog_query_titles=foobar&${LEARNING_TYPE_REFINEMENT}=ayylmao&availability=Available+Now&availability=Starting+Soon&availability=Upcoming`,
);
});
});
7 changes: 3 additions & 4 deletions src/components/catalogSearchResults/CatalogSearchResults.jsx
Original file line number Diff line number Diff line change
@@ -29,7 +29,6 @@ import {
COURSE_TITLE,
EDX_COURSE_TITLE_DESC,
EXEC_ED_TITLE,
EXECUTIVE_EDUCATION_2U_COURSE_TYPE,
HIDE_PRICE_REFINEMENT,
LEARNING_TYPE_REFINEMENT,
PROGRAM_TITLE,
@@ -91,7 +90,7 @@ export function BaseCatalogSearchResults({
useEffect(() => {
setIsProgramType(contentType === CONTENT_TYPE_PROGRAM);
setIsCourseType(contentType === CONTENT_TYPE_COURSE);
setIsExecEdType(contentType === EXECUTIVE_EDUCATION_2U_COURSE_TYPE);
setIsExecEdType(contentType === EXEC_ED_TITLE);
}, [contentType]);

const TABLE_HEADERS = useMemo(
@@ -168,7 +167,7 @@ export function BaseCatalogSearchResults({
} else if (isExecEdType) {
dispatch(
setRefinementAction(LEARNING_TYPE_REFINEMENT, [
EXECUTIVE_EDUCATION_2U_COURSE_TYPE,
EXEC_ED_TITLE,
]),
);
} else {
@@ -182,7 +181,7 @@ export function BaseCatalogSearchResults({
if (contentType === CONTENT_TYPE_COURSE) {
return <CourseCard {...props} learningType={contentType} onClick={cardClicked} />;
}
if (contentType === EXECUTIVE_EDUCATION_2U_COURSE_TYPE) {
if (contentType === EXEC_ED_TITLE) {
return <CourseCard {...props} learningType={contentType} onClick={cardClicked} />;
}
return <ProgramCard {...props} onClick={cardClicked} />;
Original file line number Diff line number Diff line change
@@ -15,7 +15,7 @@ import messages from './CatalogSearchResults.messages';
import {
CONTENT_TYPE_COURSE,
CONTENT_TYPE_PROGRAM,
EXECUTIVE_EDUCATION_2U_COURSE_TYPE,
EXEC_ED_TITLE,
HIDE_PRICE_REFINEMENT,
} from '../../constants';
import EnterpriseCatalogApiService from '../../data/services/EnterpriseCatalogAPIService';
@@ -150,7 +150,7 @@ const searchResultsExecEd = {
card_image_url: 'http://url.test2.location',
availability: ['Available Now'],
course_keys: [],
content_type: EXECUTIVE_EDUCATION_2U_COURSE_TYPE,
content_type: EXEC_ED_TITLE,
entitlements: [{ price: '100.00' }],
advertised_course_run: {
start: '2020-01-24T05:00:00Z',
@@ -214,7 +214,7 @@ const execEdProps = {
isSearchStalled: false,
searchState: { page: 1 },
error: null,
contentType: EXECUTIVE_EDUCATION_2U_COURSE_TYPE,
contentType: EXEC_ED_TITLE,
// mock i18n requirements
intl: {
formatMessage: (header) => header.defaultMessage,
36 changes: 19 additions & 17 deletions src/components/catalogs/CatalogSearch.jsx
Original file line number Diff line number Diff line change
@@ -20,7 +20,8 @@ import PageWrapper from '../PageWrapper';
import {
CONTENT_TYPE_COURSE,
CONTENT_TYPE_PROGRAM,
EXECUTIVE_EDUCATION_2U_COURSE_TYPE,
EXEC_ED_TITLE,
LEARNING_TYPE_REFINEMENT,
NUM_RESULTS_COURSE,
NUM_RESULTS_PROGRAM,
NUM_RESULTS_PER_PAGE,
@@ -32,19 +33,20 @@ import {
mapAlgoliaObjectToCourse,
mapAlgoliaObjectToExecEd,
} from '../../utils/algoliaUtils';
import { convertLearningTypesToFilters } from '../../utils/catalogUtils';
import messages from '../catalogSearchResults/CatalogSearchResults.messages';

function CatalogSearch(intl) {
const {
refinements: {
learning_type: learningType,
[LEARNING_TYPE_REFINEMENT]: learningType,
enterprise_catalog_query_titles: enterpriseCatalogQueryTitles,
},
} = useContext(SearchContext);
const { algoliaIndexName, searchClient } = useAlgoliaIndex();
const courseFilter = `learning_type:${CONTENT_TYPE_COURSE}`;
const execEdFilter = `learning_type:${EXECUTIVE_EDUCATION_2U_COURSE_TYPE}`;
const programFilter = `learning_type:${CONTENT_TYPE_PROGRAM}`;
const courseFilter = `${LEARNING_TYPE_REFINEMENT}:${CONTENT_TYPE_COURSE}`;
const execEdFilter = `${LEARNING_TYPE_REFINEMENT}:"${EXEC_ED_TITLE}"`;
const programFilter = `${LEARNING_TYPE_REFINEMENT}:${CONTENT_TYPE_PROGRAM}`;
const [noCourseResults, setNoCourseResults] = useState(false);
const [noProgramResults, setNoProgramResults] = useState(false);
const [noExecEdResults, setNoExecEdResults] = useState(false);
@@ -67,7 +69,7 @@ function CatalogSearch(intl) {
setNoResults: setNoCourseResults,
numResults: NUM_RESULTS_COURSE,
},
[EXECUTIVE_EDUCATION_2U_COURSE_TYPE]: {
[EXEC_ED_TITLE]: {
filter: execEdFilter,
noResults: noExecEdResults,
setNoResults: setNoExecEdResults,
@@ -93,7 +95,7 @@ function CatalogSearch(intl) {
useEffect(() => {
contentData[CONTENT_TYPE_COURSE].noResults = noCourseResults;
contentData[CONTENT_TYPE_PROGRAM].noResults = noProgramResults;
contentData[EXECUTIVE_EDUCATION_2U_COURSE_TYPE].noResults = noExecEdResults;
contentData[EXEC_ED_TITLE].noResults = noExecEdResults;
}, [noCourseResults, noProgramResults, noExecEdResults, contentData]);

// set specified content types & suggested search content types
@@ -103,17 +105,17 @@ function CatalogSearch(intl) {
setSpecifiedContentType(learningType);
}
setSuggestedSearchContentTypeFilter(
learningType.map((item) => `learning_type:${item}`).join(' OR '),
convertLearningTypesToFilters(learningType),
);
} else {
setSpecifiedContentType(undefined);
setSuggestedSearchContentTypeFilter(
[
CONTENT_TYPE_COURSE,
CONTENT_TYPE_PROGRAM,
EXECUTIVE_EDUCATION_2U_COURSE_TYPE,
`"${EXEC_ED_TITLE}"`,
]
.map((item) => `learning_type:${item}`)
.map((item) => `${LEARNING_TYPE_REFINEMENT}:${item}`)
.join(' OR '),
);
}
@@ -126,12 +128,12 @@ function CatalogSearch(intl) {
}, [config.ALGOLIA_INDEX_NAME, searchClient]);

const suggestedCourseOnClick = (hit) => {
if (hit.learning_type === CONTENT_TYPE_PROGRAM) {
if (hit[LEARNING_TYPE_REFINEMENT] === CONTENT_TYPE_PROGRAM) {
setSelectedSuggestedCourse(mapAlgoliaObjectToProgram(hit));
setSelectedSuggestedCourseType(CONTENT_TYPE_PROGRAM);
} else if (hit.learning_type === EXECUTIVE_EDUCATION_2U_COURSE_TYPE) {
} else if (hit[LEARNING_TYPE_REFINEMENT] === EXEC_ED_TITLE) {
setSelectedSuggestedCourse(mapAlgoliaObjectToExecEd(hit));
setSelectedSuggestedCourseType(EXECUTIVE_EDUCATION_2U_COURSE_TYPE);
setSelectedSuggestedCourseType(EXEC_ED_TITLE);
} else {
setSelectedSuggestedCourse(mapAlgoliaObjectToCourse(hit, intl, messages));
setSelectedSuggestedCourseType(CONTENT_TYPE_COURSE);
@@ -144,7 +146,7 @@ function CatalogSearch(intl) {
useEffect(() => {
const defaultTypes = [
CONTENT_TYPE_COURSE,
EXECUTIVE_EDUCATION_2U_COURSE_TYPE,
EXEC_ED_TITLE,
CONTENT_TYPE_PROGRAM,
];
// Grab content type(s) to use
@@ -159,9 +161,9 @@ function CatalogSearch(intl) {
config.EDX_ENTERPRISE_ALACARTE_TITLE,
))
) {
if (contentToDisplay.indexOf(EXECUTIVE_EDUCATION_2U_COURSE_TYPE) > 0) {
if (contentToDisplay.indexOf(EXEC_ED_TITLE) > 0) {
contentToDisplay.splice(
contentToDisplay.indexOf(EXECUTIVE_EDUCATION_2U_COURSE_TYPE),
contentToDisplay.indexOf(EXEC_ED_TITLE),
1,
);
}
@@ -215,7 +217,7 @@ function CatalogSearch(intl) {
return itemsWithResultsList;
};

const defaultInstantSearchFilter = `learning_type:${CONTENT_TYPE_COURSE} OR learning_type:${CONTENT_TYPE_PROGRAM} OR learning_type:${EXECUTIVE_EDUCATION_2U_COURSE_TYPE}`;
const defaultInstantSearchFilter = `${LEARNING_TYPE_REFINEMENT}:${CONTENT_TYPE_COURSE} OR ${LEARNING_TYPE_REFINEMENT}:${CONTENT_TYPE_PROGRAM} OR ${LEARNING_TYPE_REFINEMENT}:"${EXEC_ED_TITLE}"`;

return (
<PageWrapper className="mt-3 mb-5">
6 changes: 3 additions & 3 deletions src/components/courseCard/CourseCard.jsx
Original file line number Diff line number Diff line change
@@ -60,14 +60,14 @@ function CourseCard({
{priceText}{pacingType}
</p>
<div style={{ maxWidth: '400vw' }}>
{enterprise_catalog_query_titles.includes(
{enterprise_catalog_query_titles?.includes(
process.env.EDX_ENTERPRISE_ALACARTE_TITLE,
) && (
<Badge variant="dark" className="ml-0 padded-catalog">
{intl.formatMessage(messages['courseCard.aLaCarteBadge'])}
</Badge>
)}
{enterprise_catalog_query_titles.includes(
{enterprise_catalog_query_titles?.includes(
process.env.EDX_FOR_BUSINESS_TITLE,
) && (
<Badge
@@ -77,7 +77,7 @@ function CourseCard({
{intl.formatMessage(messages['courseCard.businessBadge'])}
</Badge>
)}
{enterprise_catalog_query_titles.includes(
{enterprise_catalog_query_titles?.includes(
process.env.EDX_FOR_ONLINE_EDU_TITLE,
) && (
<Badge variant="light" className="padded-catalog">
4 changes: 2 additions & 2 deletions src/components/courseCard/CourseCard.test.jsx
Original file line number Diff line number Diff line change
@@ -4,7 +4,7 @@ import '@testing-library/jest-dom/extend-expect';

import { IntlProvider } from '@edx/frontend-platform/i18n';
import CourseCard from './CourseCard';
import { CONTENT_TYPE_COURSE, EXECUTIVE_EDUCATION_2U_COURSE_TYPE } from '../../constants';
import { CONTENT_TYPE_COURSE, EXEC_ED_TITLE } from '../../constants';

jest.mock('@edx/frontend-platform', () => ({
...jest.requireActual('@edx/frontend-platform'),
@@ -40,7 +40,7 @@ const execEdData = {

const execEdProps = {
original: execEdData,
learningType: EXECUTIVE_EDUCATION_2U_COURSE_TYPE,
learningType: EXEC_ED_TITLE,
};

describe('Course card works as expected', () => {
25 changes: 12 additions & 13 deletions src/constants.js
Original file line number Diff line number Diff line change
@@ -18,34 +18,33 @@ export const AVAILABILITY_REFINEMENT_DEFAULTS = [
'Upcoming',
];

// Facet filters
export const CONTENT_TYPE_REFINEMENT = 'content_type';
export const COURSE_TYPE_REFINEMENT = 'course_type';
export const LEARNING_TYPE_REFINEMENT = 'learning_type';
export const LEARNING_TYPE_REFINEMENT = 'learning_type_v2';

// Page refinement settings
export const HIDE_CARDS_REFINEMENT = 'hide_cards';
export const HIDE_PRICE_REFINEMENT = 'hide_price';
export const NUM_RESULTS_PER_PAGE = 40;

// Learning types
export const CONTENT_TYPE_COURSE = 'course';
export const CONTENT_TYPE_PROGRAM = 'program';
export const EXEC_ED_TITLE = 'Executive Education';

// Page metric settings
export const NUM_RESULTS_PROGRAM = 4;
export const NUM_RESULTS_COURSE = 8;

export const COURSE_TITLE = 'Courses';
export const PROGRAM_TITLE = 'Programs';
export const EXEC_ED_TITLE = 'Executive Education';

export const NO_RESULTS_DECK_ITEM_COUNT = 4;
export const NO_RESULTS_PAGE_ITEM_COUNT = 1;
export const NO_RESULTS_PAGE_SIZE = 4;
const AUDIT_COURSE_TYPE = 'audit';
const VERIFIED_AUDIT_COURSE_TYPE = 'verified-audit';
const PROFESSIONAL_COURSE_TYPE = 'professional';
const CREDIT_VERIFIED_AUDIT_COURSE_TYPE = 'credit-verified-audit';
export const EXECUTIVE_EDUCATION_2U_COURSE_TYPE = 'executive-education-2u';
export const EDX_COURSES_COURSE_TYPES = [
AUDIT_COURSE_TYPE,
VERIFIED_AUDIT_COURSE_TYPE,
PROFESSIONAL_COURSE_TYPE,
CREDIT_VERIFIED_AUDIT_COURSE_TYPE,
];

// Descriptions
export const EDX_COURSE_TITLE_DESC = 'Self paced online learning from world-class academic institutions and corporate partners.';
export const TWOU_EXEC_ED_TITLE_DESC = 'Immersive, instructor led online short courses designed to develop interpersonal, analytical, and critical thinking skills.';
export const PROGRAM_TITLE_DESC = 'Multi-course bundled learning for skills mastery and to earn credentials such as Professional Certificates, MicroBachelors™, MicroMasters®, and Master’s Degrees.';
15 changes: 14 additions & 1 deletion src/utils/catalogUtils.js
Original file line number Diff line number Diff line change
@@ -1,3 +1,5 @@
import { EXEC_ED_TITLE } from '../constants';

/* eslint-disable import/prefer-default-export */
const nowDate = new Date(Date.now());

@@ -32,4 +34,15 @@ function checkAvailability(start, end) {
return '';
}

export { checkAvailability, checkSubscriptions };
function convertLearningTypesToFilters(types) {
return types.reduce((learningFacets, type) => {
if (type === EXEC_ED_TITLE) {
learningFacets.push(`"${type}"`);
} else {
learningFacets.push(type);
}
return learningFacets;
}, []).join(' OR ');
}

export { checkAvailability, checkSubscriptions, convertLearningTypesToFilters };
9 changes: 9 additions & 0 deletions src/utils/catalogUtils.test.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
import { EXEC_ED_TITLE } from '../constants';
import { convertLearningTypesToFilters } from './catalogUtils';

describe('catalogUtils', () => {
it('converts lists of learning types to algolia filters', () => {
const algoliaFilter = convertLearningTypesToFilters(['a', 'b', EXEC_ED_TITLE]);
expect(algoliaFilter).toEqual('a OR b OR "Executive Education"');
});
});

0 comments on commit ebc5a53

Please sign in to comment.