+
{
{title}}
- actions={}
+ actions={}
/>
-
+
}
+ textElement={}
>
-
+
-
-
-
+
+
+ {isEnrolled && }
);
};
CourseCard.propTypes = {
- courseNumber: PropTypes.string.isRequired,
+ cardId: PropTypes.string.isRequired,
};
CourseCard.defaultProps = {};
diff --git a/src/containers/CourseCard/index.test.jsx b/src/containers/CourseCard/index.test.jsx
index 95d77e5..e827045 100644
--- a/src/containers/CourseCard/index.test.jsx
+++ b/src/containers/CourseCard/index.test.jsx
@@ -23,14 +23,18 @@ const dataProps = {
title: 'hooks.title',
bannerUrl: 'hooks.bannerUrl',
formatMessage: jest.fn(msg => ({ formatted: msg })),
+ isEnrolled: true,
};
-const courseNumber = 'test-course-number';
+const cardId = 'test-card-id';
describe('CourseCard component', () => {
test('snapshot', () => {
hooks.mockReturnValueOnce(dataProps);
- expect(shallow(
)).toMatchSnapshot();
- expect(hooks).toHaveBeenCalledWith({ courseNumber });
+ expect(shallow(
)).toMatchSnapshot();
+ expect(hooks).toHaveBeenCalledWith({ cardId });
+ });
+ test('snapshot: not enrolled (no certificate card)', () => {
+ hooks.mockReturnValueOnce({ ...dataProps, isEnrolled: true });
});
});
diff --git a/src/containers/CourseFilterControls/ActiveCourseFilters.jsx b/src/containers/CourseFilterControls/ActiveCourseFilters.jsx
new file mode 100644
index 0000000..ce500e0
--- /dev/null
+++ b/src/containers/CourseFilterControls/ActiveCourseFilters.jsx
@@ -0,0 +1,44 @@
+import React from 'react';
+import PropTypes from 'prop-types';
+import { useIntl } from '@edx/frontend-platform/i18n';
+
+import { Button, Chip } from '@edx/paragon';
+import { CloseSmall } from '@edx/paragon/icons';
+
+import messages from './messages';
+import './index.scss';
+
+export const ActiveCourseFilters = ({
+ filters,
+ setFilters,
+ handleRemoveFilter,
+}) => {
+ const { formatMessage } = useIntl();
+ return (
+
+ {filters.map(filter => (
+
+ {formatMessage(messages[filter])}
+
+ ))}
+
+
+ );
+};
+ActiveCourseFilters.propTypes = {
+ filters: PropTypes.arrayOf(PropTypes.string).isRequired,
+ setFilters: PropTypes.shape({
+ remove: PropTypes.func,
+ clear: PropTypes.func,
+ }).isRequired,
+ handleRemoveFilter: PropTypes.func.isRequired,
+};
+
+export default ActiveCourseFilters;
diff --git a/src/containers/CourseFilterControls/CourseFilterControls.jsx b/src/containers/CourseFilterControls/CourseFilterControls.jsx
new file mode 100644
index 0000000..fe1185a
--- /dev/null
+++ b/src/containers/CourseFilterControls/CourseFilterControls.jsx
@@ -0,0 +1,82 @@
+import React from 'react';
+import PropTypes from 'prop-types';
+import { useIntl } from '@edx/frontend-platform/i18n';
+
+import {
+ Form,
+ Button,
+ ModalPopup,
+} from '@edx/paragon';
+import { Tune } from '@edx/paragon/icons';
+
+import FilterForm from './components/FilterForm';
+import SortForm from './components/SortForm';
+import useCourseFilterControlsData from './hooks';
+import messages from './messages';
+
+import './index.scss';
+
+export const CourseFilterControls = ({
+ sortBy,
+ setSortBy,
+ filters,
+ setFilters,
+}) => {
+ const { formatMessage } = useIntl();
+ const {
+ isOpen,
+ open,
+ close,
+ target,
+ setTarget,
+ handleFilterChange,
+ handleSortChange,
+ } = useCourseFilterControlsData({
+ setFilters,
+ setSortBy,
+ });
+ return (
+
+
+
+
+
+
+ );
+};
+CourseFilterControls.propTypes = {
+ sortBy: PropTypes.string.isRequired,
+ setSortBy: PropTypes.func.isRequired,
+ filters: PropTypes.arrayOf(PropTypes.string).isRequired,
+ setFilters: PropTypes.shape({
+ add: PropTypes.func.isRequired,
+ remove: PropTypes.func.isRequired,
+ }).isRequired,
+};
+
+export default CourseFilterControls;
diff --git a/src/containers/CourseFilterControls/components/Checkbox.jsx b/src/containers/CourseFilterControls/components/Checkbox.jsx
new file mode 100644
index 0000000..adfad51
--- /dev/null
+++ b/src/containers/CourseFilterControls/components/Checkbox.jsx
@@ -0,0 +1,21 @@
+import React from 'react';
+import PropTypes from 'prop-types';
+import { useIntl } from '@edx/frontend-platform/i18n';
+
+import { Form } from '@edx/paragon';
+
+import messages from '../messages';
+
+export const Checkbox = ({ filterKey }) => {
+ const { formatMessage } = useIntl();
+ return (
+
+ {formatMessage(messages[filterKey])}
+
+ );
+};
+Checkbox.propTypes = {
+ filterKey: PropTypes.string.isRequired,
+};
+
+export default Checkbox;
diff --git a/src/containers/CourseFilterControls/components/FilterForm.jsx b/src/containers/CourseFilterControls/components/FilterForm.jsx
new file mode 100644
index 0000000..f94625f
--- /dev/null
+++ b/src/containers/CourseFilterControls/components/FilterForm.jsx
@@ -0,0 +1,34 @@
+import React from 'react';
+import PropTypes from 'prop-types';
+
+import { FilterKeys } from 'data/constants/app';
+
+import { Form } from '@edx/paragon';
+
+import Checkbox from './Checkbox';
+
+export const FilterForm = ({
+ filters,
+ handleFilterChange,
+}) => (
+
+ Course Status
+
+
+
+
+
+
+
+
+);
+FilterForm.propTypes = {
+ filters: PropTypes.arrayOf(PropTypes.string).isRequired,
+ handleFilterChange: PropTypes.func.isRequired,
+};
+
+export default FilterForm;
diff --git a/src/containers/CourseFilterControls/components/SortForm.jsx b/src/containers/CourseFilterControls/components/SortForm.jsx
new file mode 100644
index 0000000..f545b1d
--- /dev/null
+++ b/src/containers/CourseFilterControls/components/SortForm.jsx
@@ -0,0 +1,39 @@
+import React from 'react';
+import PropTypes from 'prop-types';
+import { useIntl } from '@edx/frontend-platform/i18n';
+
+import { SortKeys } from 'data/constants/app';
+
+import { Form } from '@edx/paragon';
+
+import messages from '../messages';
+
+export const SortForm = ({
+ handleSortChange,
+ sortBy,
+}) => {
+ const { formatMessage } = useIntl();
+ return (
+ <>
+
{formatMessage(messages.sort)}
+
+
+ {formatMessage(messages.sortLastEnrolled)}
+
+
+ {formatMessage(messages.sortTitle)}
+
+
+ >
+ );
+};
+SortForm.propTypes = {
+ handleSortChange: PropTypes.func.isRequired,
+ sortBy: PropTypes.string.isRequired,
+};
+
+export default SortForm;
diff --git a/src/containers/CourseFilterControls/hooks.js b/src/containers/CourseFilterControls/hooks.js
new file mode 100644
index 0000000..72985ac
--- /dev/null
+++ b/src/containers/CourseFilterControls/hooks.js
@@ -0,0 +1,29 @@
+import React from 'react';
+
+import { useToggle } from '@edx/paragon';
+
+export const useCourseFilterControlsData = ({
+ setFilters,
+ setSortBy,
+}) => {
+ const [isOpen, open, close] = useToggle(false);
+ const [target, setTarget] = React.useState(null);
+ const handleFilterChange = ({ target: { checked, value } }) => {
+ const update = checked ? setFilters.add : setFilters.remove;
+ update(value);
+ };
+ const handleSortChange = ({ target: { value } }) => {
+ setSortBy(value);
+ };
+ return {
+ isOpen,
+ open,
+ close,
+ target,
+ setTarget,
+ handleFilterChange,
+ handleSortChange,
+ };
+};
+
+export default useCourseFilterControlsData;
diff --git a/src/containers/CourseFilterControls/index.jsx b/src/containers/CourseFilterControls/index.jsx
new file mode 100644
index 0000000..32ead25
--- /dev/null
+++ b/src/containers/CourseFilterControls/index.jsx
@@ -0,0 +1,6 @@
+import CourseFilterControls from './CourseFilterControls';
+
+export { default as ActiveCourseFilters } from './ActiveCourseFilters';
+
+export { CourseFilterControls };
+export default CourseFilterControls;
diff --git a/src/containers/CourseFilterControls/index.scss b/src/containers/CourseFilterControls/index.scss
new file mode 100644
index 0000000..8da93f9
--- /dev/null
+++ b/src/containers/CourseFilterControls/index.scss
@@ -0,0 +1,20 @@
+#course-filter-controls-card {
+ width: 512px;
+ height: 288px;
+ &.no-enrollments {
+ height: 172px;
+ }
+ .filter-form-heading {
+ font-weight: bold;
+ font-size: 18px;
+ }
+ hr {
+ width: 1px;
+ };
+ .filter-form-col {
+ width: 256px;
+ display: inline-block;
+ text-align: left;
+ }
+}
+
diff --git a/src/containers/CourseFilterControls/messages.js b/src/containers/CourseFilterControls/messages.js
new file mode 100644
index 0000000..fa36829
--- /dev/null
+++ b/src/containers/CourseFilterControls/messages.js
@@ -0,0 +1,55 @@
+import { StrictDict } from 'utils';
+
+export const messages = StrictDict({
+ inProgress: {
+ id: 'learner-dash.courseListFilters.inProgress',
+ description: 'in-progress filter checkbox label for course list filters',
+ defaultMessage: 'In-Progress',
+ },
+ notStarted: {
+ id: 'learner-dash.courseListFilters.notStarted',
+ description: 'Not-Started filter checkbox label for course list filters',
+ defaultMessage: 'Not Started',
+ },
+ done: {
+ id: 'learner-dash.courseListFilters.done',
+ description: 'done filter checkbox label for course list filters',
+ defaultMessage: 'Done',
+ },
+ notEnrolled: {
+ id: 'learner-dash.courseListFilters.notEnrolled',
+ description: 'not-enrolled filter checkbox label for course list filters',
+ defaultMessage: 'Not Enrolled',
+ },
+ upgraded: {
+ id: 'learner-dash.courseListFilters.upgraded',
+ description: 'upgraded filter checkbox label for course list filters',
+ defaultMessage: 'Upgraded',
+ },
+ clearAll: {
+ id: 'learner-dash.courseListFilters.clearAll',
+ description: 'clear all filters button text',
+ defaultMessage: 'Clear all',
+ },
+ sort: {
+ id: 'learner-dash.courseListFilters.sort',
+ description: 'Sort radio form heading',
+ defaultMessage: 'Sort',
+ },
+ sortLastEnrolled: {
+ id: 'learner-dash.courseListFilters.sortLastEnrolled',
+ description: 'Last enrolled sort option text',
+ defaultMessage: 'Last enrolled',
+ },
+ sortTitle: {
+ id: 'learner-dash.courseListFilters.sortTitle',
+ description: 'Title sort option text',
+ defaultMessage: 'Title (A-Z)',
+ },
+ refine: {
+ id: 'learner-dash.courseListFilters.refine',
+ description: 'Filter button container text',
+ defaultMessage: 'Refine',
+ },
+});
+export default messages;
diff --git a/src/containers/CourseList/index.jsx b/src/containers/CourseList/index.jsx
index 1215ea1..da22dde 100644
--- a/src/containers/CourseList/index.jsx
+++ b/src/containers/CourseList/index.jsx
@@ -1,20 +1,87 @@
import React from 'react';
-import PropTypes from 'prop-types';
+import { FormattedMessage } from '@edx/frontend-platform/i18n';
+import {
+ Pagination,
+ useCheckboxSetValues,
+} from '@edx/paragon';
+
+import { hooks as appHooks } from 'data/redux';
+import { ListPageSize, SortKeys } from 'data/constants/app';
+import { ActiveCourseFilters, CourseFilterControls } from 'containers/CourseFilterControls';
import CourseCard from 'containers/CourseCard';
-import SelectSession from 'containers/SelectSession';
-export const CourseList = ({ courseListData }) => (
-
- {courseListData.map((courseNumber) => (
-
- ))}
-
-
-);
+import messages from './messages';
+
+export const useCourseListData = () => {
+ const [pageNumber, setPageNumber] = React.useState(1);
+ const [sortBy, setSortBy] = React.useState(SortKeys.title);
+ const [filters, setFilters] = useCheckboxSetValues([]);
+ const { numPages, visible } = appHooks.useCurrentCourseList({
+ sortBy,
+ isAscending: true,
+ filters,
+ pageNumber,
+ pageSize: ListPageSize,
+ });
+ const handleRemoveFilter = (filter) => () => setFilters.remove(filter);
+ return {
+ numPages,
+ setPageNumber,
+ visibleList: visible,
+ filterOptions: {
+ sortBy,
+ setSortBy,
+ filters,
+ setFilters,
+ handleRemoveFilter,
+ },
+ showFilters: filters.length > 0,
+ };
+};
+
+export const CourseList = () => {
+ const {
+ filterOptions,
+ setPageNumber,
+ numPages,
+ showFilters,
+ visibleList,
+ } = useCourseListData();
+ return (
+
+
+ { showFilters && (
+
+ )}
+
+ {visibleList.map(({ cardId }) => (
+
+ ))}
+
+
+
+ );
+};
CourseList.propTypes = {
- courseListData: PropTypes.arrayOf(PropTypes.string).isRequired,
};
export default CourseList;
diff --git a/src/containers/CourseList/messages.js b/src/containers/CourseList/messages.js
new file mode 100644
index 0000000..ec73e1c
--- /dev/null
+++ b/src/containers/CourseList/messages.js
@@ -0,0 +1,11 @@
+import { defineMessages } from '@edx/frontend-platform/i18n';
+
+const messages = defineMessages({
+ myCourses: {
+ id: 'dashboard.mycourses',
+ defaultMessage: 'My Courses',
+ description: 'Course list heading',
+ },
+});
+
+export default messages;
diff --git a/src/containers/Dashboard/index.jsx b/src/containers/Dashboard/index.jsx
index ae3708e..4e59824 100644
--- a/src/containers/Dashboard/index.jsx
+++ b/src/containers/Dashboard/index.jsx
@@ -1,44 +1,41 @@
import React from 'react';
-import { useDispatch, useSelector } from 'react-redux';
+import { useDispatch } from 'react-redux';
-import { FormattedMessage } from '@edx/frontend-platform/i18n';
-
-import { selectors, thunkActions } from 'data/redux';
+import {
+ thunkActions,
+ hooks as appHooks,
+} from 'data/redux';
import CourseList from 'containers/CourseList';
import WidgetSidebar from 'containers/WidgetSidebar';
import EmptyCourse from 'containers/EmptyCourse';
+import SelectSessionModal from 'containers/SelectSessionModal';
+import EnterpriseDashboardModal from 'containers/EnterpriseDashboardModal';
-import messages from './messages';
-import * as module from '.';
+import './index.scss';
-export const useDashboardData = ({ dispatch }) => {
+export const Dashboard = () => {
+ const dispatch = useDispatch();
React.useEffect(
() => { dispatch(thunkActions.app.initialize()); },
[dispatch],
);
- return {
- enrollments: useSelector(selectors.app.enrollments),
- entitlements: useSelector(selectors.app.entitlements),
- };
-};
-export const Dashboard = () => {
- const dispatch = useDispatch();
- const {
- enrollments,
- // entitlements,
- } = module.useDashboardData({ dispatch });
+ const hasCourses = appHooks.useHasCourses();
+ const hasAvailableDashboards = appHooks.useHasAvailableDashboards();
return (
-
- {enrollments.length ? (
+
+ {hasAvailableDashboards &&
}
+ {hasCourses ? (
<>
-
-
-
-
-
-
+
>
) : (
diff --git a/src/containers/Dashboard/index.scss b/src/containers/Dashboard/index.scss
new file mode 100644
index 0000000..6dd1e29
--- /dev/null
+++ b/src/containers/Dashboard/index.scss
@@ -0,0 +1,4 @@
+#course-list-heading-container {
+ display: flex;
+ justify-content: space-between;
+}
diff --git a/src/containers/Dashboard/messages.js b/src/containers/Dashboard/messages.js
index 35a5251..ec73e1c 100644
--- a/src/containers/Dashboard/messages.js
+++ b/src/containers/Dashboard/messages.js
@@ -1,10 +1,10 @@
import { defineMessages } from '@edx/frontend-platform/i18n';
const messages = defineMessages({
- myCourse: {
- id: 'dashboard.mycourse',
+ myCourses: {
+ id: 'dashboard.mycourses',
defaultMessage: 'My Courses',
- description: 'My Courses',
+ description: 'Course list heading',
},
});
diff --git a/src/containers/EmailSettingsModal/hooks.js b/src/containers/EmailSettingsModal/hooks.js
index 24a93cc..5a15e26 100644
--- a/src/containers/EmailSettingsModal/hooks.js
+++ b/src/containers/EmailSettingsModal/hooks.js
@@ -12,10 +12,10 @@ export const state = StrictDict({
export const useEmailData = ({
closeModal,
- courseNumber,
+ cardId,
// dispatch,
}) => {
- const { isEmailEnabled } = appHooks.useCardEnrollmentData(courseNumber);
+ const { isEmailEnabled } = appHooks.useCardEnrollmentData(cardId);
const [toggleValue, setToggleValue] = module.state.toggle(isEmailEnabled);
const onToggle = React.useCallback(
() => setToggleValue(!toggleValue),
diff --git a/src/containers/EmailSettingsModal/hooks.test.js b/src/containers/EmailSettingsModal/hooks.test.js
index accee42..90aa9c5 100644
--- a/src/containers/EmailSettingsModal/hooks.test.js
+++ b/src/containers/EmailSettingsModal/hooks.test.js
@@ -9,7 +9,7 @@ jest.mock('data/redux', () => ({
},
}));
-const courseNumber = 'my-test-course-number';
+const cardId = 'my-test-course-number';
const closeModal = jest.fn();
const state = new MockUseState(hooks);
@@ -26,12 +26,12 @@ describe('EmailSettingsModal hooks', () => {
beforeEach(() => {
state.mock();
appHooks.useCardEnrollmentData.mockReturnValueOnce({ isEmailEnabled: true });
- out = hooks.useEmailData({ closeModal, courseNumber });
+ out = hooks.useEmailData({ closeModal, cardId });
});
afterEach(state.restore);
test('loads enrollment data based on course number', () => {
- expect(appHooks.useCardEnrollmentData).toHaveBeenCalledWith(courseNumber);
+ expect(appHooks.useCardEnrollmentData).toHaveBeenCalledWith(cardId);
});
test('initializes toggle value to cardData.isEmailEnabled', () => {
@@ -39,7 +39,7 @@ describe('EmailSettingsModal hooks', () => {
expect(out.toggleValue).toEqual(true);
appHooks.useCardEnrollmentData.mockReturnValueOnce({ isEmailEnabled: false });
- out = hooks.useEmailData({ closeModal, courseNumber });
+ out = hooks.useEmailData({ closeModal, cardId });
state.expectInitializedWith(state.keys.toggle, false);
expect(out.toggleValue).toEqual(false);
});
diff --git a/src/containers/EmailSettingsModal/index.jsx b/src/containers/EmailSettingsModal/index.jsx
index 261ae4c..f44ee4e 100644
--- a/src/containers/EmailSettingsModal/index.jsx
+++ b/src/containers/EmailSettingsModal/index.jsx
@@ -18,14 +18,14 @@ import messages from './messages';
export const EmailSettingsModal = ({
closeModal,
show,
- courseNumber,
+ cardId,
}) => {
const dispatch = useDispatch();
const {
toggleValue,
onToggle,
save,
- } = useEmailData({ dispatch, closeModal, courseNumber });
+ } = useEmailData({ dispatch, closeModal, cardId });
const { formatMessage } = useIntl();
return (
@@ -52,7 +52,7 @@ export const EmailSettingsModal = ({
);
};
EmailSettingsModal.propTypes = {
- courseNumber: PropTypes.string.isRequired,
+ cardId: PropTypes.string.isRequired,
closeModal: PropTypes.func.isRequired,
show: PropTypes.bool.isRequired,
};
diff --git a/src/containers/EmailSettingsModal/index.test.jsx b/src/containers/EmailSettingsModal/index.test.jsx
index 6a82846..a835772 100644
--- a/src/containers/EmailSettingsModal/index.test.jsx
+++ b/src/containers/EmailSettingsModal/index.test.jsx
@@ -19,7 +19,7 @@ const hookProps = {
const props = {
closeModal: jest.fn().mockName('closeModal'),
show: true,
- courseNumber: 'test-course-number',
+ cardId: 'test-course-number',
};
const dispatch = useDispatch();
@@ -33,11 +33,11 @@ describe('EmailSettingsModal', () => {
hooks.mockReturnValueOnce(hookProps);
shallow(
);
});
- it('calls hook w/ dispatch from redux hook, and closeModal, courseNumber from props', () => {
+ it('calls hook w/ dispatch from redux hook, and closeModal, cardId from props', () => {
expect(hooks).toHaveBeenCalledWith({
closeModal: props.closeModal,
dispatch,
- courseNumber: props.courseNumber,
+ cardId: props.cardId,
});
});
});
diff --git a/src/containers/EnterpriseDashboardModal/__snapshots__/index.test.jsx.snap b/src/containers/EnterpriseDashboardModal/__snapshots__/index.test.jsx.snap
new file mode 100644
index 0000000..068c3c3
--- /dev/null
+++ b/src/containers/EnterpriseDashboardModal/__snapshots__/index.test.jsx.snap
@@ -0,0 +1,39 @@
+// Jest Snapshot v1, https://goo.gl/fbAQLP
+
+exports[`EnterpriseDashboard snapshot 1`] = `
+
+
+
+ You have access to the edX, Inc. dashboard
+
+
+ To access the coureses available to you through edX, Inc., visit the edX, Inc. dashboard now.
+
+
+
+
+
+
+
+`;
diff --git a/src/containers/EnterpriseDashboardModal/hooks.js b/src/containers/EnterpriseDashboardModal/hooks.js
new file mode 100644
index 0000000..2812eb0
--- /dev/null
+++ b/src/containers/EnterpriseDashboardModal/hooks.js
@@ -0,0 +1,21 @@
+import React from 'react';
+import { hooks as appHooks } from 'data/redux';
+import { StrictDict } from 'utils';
+import * as module from './hooks';
+
+export const state = StrictDict({
+ showModal: (val) => React.useState(val), // eslint-disable-line
+});
+
+export const useEnterpriseDashboardHook = () => {
+ const [showModal, setShowModal] = module.state.showModal(true);
+ const { mostRecentDashboard } = appHooks.useEnterpriseDashboardData();
+ const handleClick = () => setShowModal(false);
+ return {
+ showModal,
+ handleClick,
+ mostRecentDashboard,
+ };
+};
+
+export default useEnterpriseDashboardHook;
diff --git a/src/containers/EnterpriseDashboardModal/hooks.test.js b/src/containers/EnterpriseDashboardModal/hooks.test.js
new file mode 100644
index 0000000..43183bb
--- /dev/null
+++ b/src/containers/EnterpriseDashboardModal/hooks.test.js
@@ -0,0 +1,44 @@
+import { MockUseState } from 'testUtils';
+import { hooks as appHooks } from 'data/redux';
+
+import * as hooks from './hooks';
+
+jest.mock('data/redux', () => ({
+ hooks: {
+ useEnterpriseDashboardData: jest.fn(),
+ },
+}));
+
+const state = new MockUseState(hooks);
+
+const enterpriseDashboardData = {
+ mostRecentDashboard: { label: 'edX, Inc.', url: '/edx-dashboard' },
+};
+
+describe('EnterpriseDashboard hooks', () => {
+ appHooks.useEnterpriseDashboardData.mockReturnValue({ ...enterpriseDashboardData });
+
+ describe('state values', () => {
+ state.testGetter(state.keys.showModal);
+ });
+
+ describe('behavior', () => {
+ let out;
+
+ beforeEach(() => {
+ state.mock();
+ out = hooks.useEnterpriseDashboardHook();
+ });
+ afterEach(state.restore);
+
+ test('useEnterpriseDashboardHook to return dashboard data from redux hooks', () => {
+ expect(out.mostRecentDashboard).toMatchObject(enterpriseDashboardData.mostRecentDashboard);
+ });
+
+ test('modal initializes to shown when rendered and closes on click', () => {
+ state.expectInitializedWith(state.keys.showModal, true);
+ out.handleClick();
+ expect(state.values.showModal).toEqual(false);
+ });
+ });
+});
diff --git a/src/containers/EnterpriseDashboardModal/index.jsx b/src/containers/EnterpriseDashboardModal/index.jsx
new file mode 100644
index 0000000..2908a5e
--- /dev/null
+++ b/src/containers/EnterpriseDashboardModal/index.jsx
@@ -0,0 +1,55 @@
+import React from 'react';
+// import PropTypes from 'prop-types';
+
+import { useIntl } from '@edx/frontend-platform/i18n';
+import {
+ ModalDialog, ActionRow, Button,
+} from '@edx/paragon';
+
+import messages from './messages';
+import useEnterpriseDashboardHook from './hooks';
+
+export const EnterpriseDashboardModal = () => {
+ const { formatMessage } = useIntl();
+ const {
+ showModal,
+ handleClick,
+ mostRecentDashboard,
+ } = useEnterpriseDashboardHook();
+ return (
+
+
+
+ {formatMessage(messages.enterpriseDialogHeader, {
+ label: mostRecentDashboard.label,
+ })}
+
+
+ {formatMessage(messages.enterpriseDialogBody, {
+ label: mostRecentDashboard.label,
+ })}
+
+
+
+
+
+
+
+ );
+};
+
+EnterpriseDashboardModal.propTypes = {};
+
+export default EnterpriseDashboardModal;
diff --git a/src/containers/EnterpriseDashboardModal/index.test.jsx b/src/containers/EnterpriseDashboardModal/index.test.jsx
new file mode 100644
index 0000000..d036bdd
--- /dev/null
+++ b/src/containers/EnterpriseDashboardModal/index.test.jsx
@@ -0,0 +1,22 @@
+import { shallow } from 'enzyme';
+import EnterpriseDashboard from '.';
+
+import useEnterpriseDashboardHook from './hooks';
+
+jest.mock('./hooks', () => ({
+ __esModule: true,
+ default: jest.fn(),
+}));
+
+describe('EnterpriseDashboard', () => {
+ test('snapshot', () => {
+ const hookData = {
+ mostRecentDashboard: { label: 'edX, Inc.', url: '/edx-dashboard' },
+ showDialog: false,
+ handleClick: jest.fn().mockName('useEnterpriseDashboardHook.handleClick'),
+ };
+ useEnterpriseDashboardHook.mockReturnValueOnce({ ...hookData });
+ const el = shallow(
);
+ expect(el).toMatchSnapshot();
+ });
+});
diff --git a/src/containers/LearnerDashboardHeader/EnterpriseDashboard/messages.js b/src/containers/EnterpriseDashboardModal/messages.js
similarity index 84%
rename from src/containers/LearnerDashboardHeader/EnterpriseDashboard/messages.js
rename to src/containers/EnterpriseDashboardModal/messages.js
index 8b9c118..f8a8656 100644
--- a/src/containers/LearnerDashboardHeader/EnterpriseDashboard/messages.js
+++ b/src/containers/EnterpriseDashboardModal/messages.js
@@ -1,11 +1,6 @@
import { defineMessages } from '@edx/frontend-platform/i18n';
const messages = defineMessages({
- dashboard: {
- id: 'leanerDashboard.menu.dashboard.label',
- defaultMessage: 'Dashboard',
- description: 'The text for the user menu Dashboard navigation link.',
- },
enterpriseDialogHeader: {
id: 'leanerDashboard.enterpriseDialogHeader',
defaultMessage: 'You have access to the {label} dashboard',
diff --git a/src/containers/LearnerDashboardHeader/AuthenticatedUserDropdown.jsx b/src/containers/LearnerDashboardHeader/AuthenticatedUserDropdown.jsx
index 004595d..1bfc405 100644
--- a/src/containers/LearnerDashboardHeader/AuthenticatedUserDropdown.jsx
+++ b/src/containers/LearnerDashboardHeader/AuthenticatedUserDropdown.jsx
@@ -6,11 +6,12 @@ import { useIntl } from '@edx/frontend-platform/i18n';
import { Dropdown, Icon } from '@edx/paragon';
import { Person } from '@edx/paragon/icons';
+import { hooks as appHooks } from 'data/redux';
import messages from './messages';
-import EnterpriseDashboard from './EnterpriseDashboard';
export const AuthenticatedUserDropdown = ({ username }) => {
const { formatMessage } = useIntl();
+ const { availableDashboards } = appHooks.useEnterpriseDashboardData();
return (
<>
@@ -21,7 +22,18 @@ export const AuthenticatedUserDropdown = ({ username }) => {
-
+ SWITCH DASHBOARD
+ Personal
+ {availableDashboards && availableDashboards.map((dashboard) => (
+
+ {dashboard.label} {formatMessage(messages.dashboard)}
+
+ ))}
+
{formatMessage(messages.profile)}
@@ -36,6 +48,7 @@ export const AuthenticatedUserDropdown = ({ username }) => {
{formatMessage(messages.help)}
+
{formatMessage(messages.signOut)}
diff --git a/src/containers/LearnerDashboardHeader/ConfirmEmailBanner/hooks.js b/src/containers/LearnerDashboardHeader/ConfirmEmailBanner/hooks.js
index fc8dc5d..ef29d8c 100644
--- a/src/containers/LearnerDashboardHeader/ConfirmEmailBanner/hooks.js
+++ b/src/containers/LearnerDashboardHeader/ConfirmEmailBanner/hooks.js
@@ -24,6 +24,7 @@ export const useConfirmEmailBannerData = () => {
const openConfirmModalButtonClick = () => {
dispatch(thunkActions.app.sendConfirmEmail());
openConfirmModal();
+ closePageBanner();
};
const userConfirmEmailButtonClick = () => {
diff --git a/src/containers/LearnerDashboardHeader/EnterpriseDashboard/__snapshots__/index.test.jsx.snap b/src/containers/LearnerDashboardHeader/EnterpriseDashboard/__snapshots__/index.test.jsx.snap
deleted file mode 100644
index 66150b6..0000000
--- a/src/containers/LearnerDashboardHeader/EnterpriseDashboard/__snapshots__/index.test.jsx.snap
+++ /dev/null
@@ -1,130 +0,0 @@
-// Jest Snapshot v1, https://goo.gl/fbAQLP
-
-exports[`EnterpriseDashboard snapshot initilized 1`] = `
-
-
- Personal
-
- Dashboard
-
-
- edX, Inc.
-
- Dashboard
-
-
- Harvard
-
- Dashboard
-
-
-
-
- You have access to the undefined dashboard
-
-
- To access the coureses available to you through undefined, visit the undefined dashboard now.
-
-
-
-
-
-
-
-
-`;
-
-exports[`EnterpriseDashboard snapshot select item and open modal 1`] = `
-
-
- Personal
-
- Dashboard
-
-
- edX, Inc.
-
- Dashboard
-
-
- Harvard
-
- Dashboard
-
-
-
-
- You have access to the Personal dashboard
-
-
- To access the coureses available to you through Personal, visit the Personal dashboard now.
-
-
-
-
-
-
-
-
-`;
diff --git a/src/containers/LearnerDashboardHeader/EnterpriseDashboard/hooks.js b/src/containers/LearnerDashboardHeader/EnterpriseDashboard/hooks.js
deleted file mode 100644
index 465c343..0000000
--- a/src/containers/LearnerDashboardHeader/EnterpriseDashboard/hooks.js
+++ /dev/null
@@ -1,37 +0,0 @@
-import React from 'react';
-import { hooks as appHooks } from 'data/redux';
-import { StrictDict } from 'utils';
-import * as module from './hooks';
-
-export const state = StrictDict({
- showDialog: (val) => React.useState(val), // eslint-disable-line
- selectedItem: (val) => React.useState(val), // eslint-disable-line
-});
-
-export const useEnterpriseDashboardHook = () => {
- const { availableDashboards, mostRecentDashboard } = appHooks.useEnterpriseDashboardData();
- const [showDialog, setShowDialog] = module.state.showDialog(false);
- const [selectedItem, setSelectedItem] = module.state.selectedItem({});
-
- const beginSelectDashboardItem = (val) => () => {
- setSelectedItem(val);
- setShowDialog(true);
- };
-
- const cancelSelectDashboardItem = () => {
- setSelectedItem({});
- setShowDialog(false);
- };
-
- return {
- availableDashboards,
- mostRecentDashboard,
- showDialog,
-
- selectedItem,
- beginSelectDashboardItem,
- cancelSelectDashboardItem,
- };
-};
-
-export default useEnterpriseDashboardHook;
diff --git a/src/containers/LearnerDashboardHeader/EnterpriseDashboard/hooks.test.js b/src/containers/LearnerDashboardHeader/EnterpriseDashboard/hooks.test.js
deleted file mode 100644
index 744d97e..0000000
--- a/src/containers/LearnerDashboardHeader/EnterpriseDashboard/hooks.test.js
+++ /dev/null
@@ -1,60 +0,0 @@
-import { MockUseState } from 'testUtils';
-import { hooks as appHooks } from 'data/redux';
-
-import * as hooks from './hooks';
-
-jest.mock('data/redux', () => ({
- hooks: {
- useEnterpriseDashboardData: jest.fn(),
- },
-}));
-
-const state = new MockUseState(hooks);
-
-const enterpriseDashboardData = {
- availableDashboards: [
- { label: 'Personal', url: '/dashboard' },
- { label: 'edX, Inc.', url: '/edx-dashboard' },
- { label: 'Harvard', url: '/harvard-dashboard' },
- ],
- mostRecentDashboard: { label: 'edX, Inc.', url: '/edx-dashboard' },
-};
-
-describe('EnterpriseDashboard hooks', () => {
- appHooks.useEnterpriseDashboardData.mockReturnValue({ ...enterpriseDashboardData });
-
- describe('state values', () => {
- state.testGetter(state.keys.showDialog);
- state.testGetter(state.keys.selectedItem);
- });
-
- describe('behavior', () => {
- let out;
-
- beforeEach(() => {
- state.mock();
- out = hooks.useEnterpriseDashboardHook();
- });
- afterEach(state.restore);
-
- test('useEnterpriseDashboardHook to return dashboard data from redux hooks', () => {
- expect(out.availableDashboards).toMatchObject(enterpriseDashboardData.availableDashboards);
- expect(out.mostRecentDashboard).toMatchObject(enterpriseDashboardData.mostRecentDashboard);
- });
-
- test('modal is open on begin select dashboard item', () => {
- state.expectInitializedWith('showDialog', false);
- state.expectInitializedWith('selectedItem', {});
- const selectedItem = { abitary: 'not so true' };
- out.beginSelectDashboardItem(selectedItem)();
- expect(state.values.showDialog).toEqual(true);
- expect(state.values.selectedItem).toMatchObject(selectedItem);
- });
-
- test('modal is close on cancel select dashboard item', () => {
- out.cancelSelectDashboardItem();
- expect(state.values.selectedItem).toMatchObject({});
- expect(state.values.showDialog).toEqual(false);
- });
- });
-});
diff --git a/src/containers/LearnerDashboardHeader/EnterpriseDashboard/index.jsx b/src/containers/LearnerDashboardHeader/EnterpriseDashboard/index.jsx
deleted file mode 100644
index 5252db0..0000000
--- a/src/containers/LearnerDashboardHeader/EnterpriseDashboard/index.jsx
+++ /dev/null
@@ -1,73 +0,0 @@
-import React from 'react';
-// import PropTypes from 'prop-types';
-
-import { useIntl } from '@edx/frontend-platform/i18n';
-import {
- Dropdown, ModalDialog, ActionRow, Button,
-} from '@edx/paragon';
-
-import { nullMethod } from 'hooks';
-
-import messages from './messages';
-import useEnterpriseDashboardHook from './hooks';
-
-export const EnterpriseDashboard = () => {
- const { formatMessage } = useIntl();
- const {
- availableDashboards,
- mostRecentDashboard,
- showDialog,
-
- selectedItem,
- beginSelectDashboardItem,
- cancelSelectDashboardItem,
- } = useEnterpriseDashboardHook();
-
- return (
- <>
- {availableDashboards.map((dashboard) => (
-
- {dashboard.label} {formatMessage(messages.dashboard)}
-
- ))}
-
-
-
- {formatMessage(messages.enterpriseDialogHeader, {
- label: selectedItem.label,
- })}
-
-
- {formatMessage(messages.enterpriseDialogBody, {
- label: selectedItem.label,
- })}
-
-
-
-
-
-
-
- >
- );
-};
-
-EnterpriseDashboard.propTypes = {};
-
-export default EnterpriseDashboard;
diff --git a/src/containers/LearnerDashboardHeader/EnterpriseDashboard/index.test.jsx b/src/containers/LearnerDashboardHeader/EnterpriseDashboard/index.test.jsx
deleted file mode 100644
index 74b9205..0000000
--- a/src/containers/LearnerDashboardHeader/EnterpriseDashboard/index.test.jsx
+++ /dev/null
@@ -1,49 +0,0 @@
-import { shallow } from 'enzyme';
-import EnterpriseDashboard from '.';
-
-import useEnterpriseDashboardHook from './hooks';
-
-jest.mock('./hooks', () => ({
- __esModule: true,
- default: jest.fn(),
-}));
-
-const enterpriseDashboardData = {
- availableDashboards: [
- { label: 'Personal', url: '/dashboard' },
- { label: 'edX, Inc.', url: '/edx-dashboard' },
- { label: 'Harvard', url: '/harvard-dashboard' },
- ],
- mostRecentDashboard: { label: 'edX, Inc.', url: '/edx-dashboard' },
-};
-
-describe('EnterpriseDashboard', () => {
- describe('snapshot', () => {
- const hookReturn = {
- ...enterpriseDashboardData,
- showDialog: false,
-
- selectedItem: {},
- beginSelectDashboardItem: jest.fn().mockName('beginSelectDashboardItem'),
- cancelSelectDashboardItem: jest
- .fn()
- .mockName('cancelSelectDashboardItem'),
- };
-
- test('initilized', () => {
- useEnterpriseDashboardHook.mockReturnValueOnce({ ...hookReturn });
- const el = shallow();
- expect(el).toMatchSnapshot();
- });
-
- test('select item and open modal', () => {
- useEnterpriseDashboardHook.mockReturnValueOnce({
- ...hookReturn,
- selectedItem: enterpriseDashboardData.availableDashboards[0],
- showDialog: true,
- });
- const el = shallow();
- expect(el).toMatchSnapshot();
- });
- });
-});
diff --git a/src/containers/LearnerDashboardHeader/index.jsx b/src/containers/LearnerDashboardHeader/index.jsx
index 01c6cb9..02daff0 100644
--- a/src/containers/LearnerDashboardHeader/index.jsx
+++ b/src/containers/LearnerDashboardHeader/index.jsx
@@ -6,6 +6,7 @@ import { Program } from '@edx/paragon/icons';
import { Button } from '@edx/paragon';
import AuthenticatedUserDropdown from './AuthenticatedUserDropdown';
+
import GreetingBanner from './GreetingBanner';
import messages from './messages';
import ConfirmEmailBanner from './ConfirmEmailBanner';
diff --git a/src/containers/LearnerDashboardHeader/messages.js b/src/containers/LearnerDashboardHeader/messages.js
index 02e2700..94d1c53 100644
--- a/src/containers/LearnerDashboardHeader/messages.js
+++ b/src/containers/LearnerDashboardHeader/messages.js
@@ -1,6 +1,11 @@
import { defineMessages } from '@edx/frontend-platform/i18n';
const messages = defineMessages({
+ dashboard: {
+ id: 'leanerDashboard.menu.dashboard.label',
+ defaultMessage: 'Dashboard',
+ description: 'The text for the user menu Dashboard navigation link.',
+ },
help: {
id: 'leanerDashboard.help.label',
defaultMessage: 'Help',
diff --git a/src/containers/RelatedProgramsModal/hooks.js b/src/containers/RelatedProgramsModal/hooks.js
index adcf7ff..ad134b1 100644
--- a/src/containers/RelatedProgramsModal/hooks.js
+++ b/src/containers/RelatedProgramsModal/hooks.js
@@ -1,10 +1,10 @@
import { hooks as appHooks } from 'data/redux';
export const useProgramData = ({
- courseNumber,
+ cardId,
}) => ({
- courseTitle: appHooks.useCardCourseData(courseNumber).title,
- relatedPrograms: appHooks.useCardRelatedProgramsData(courseNumber).list,
+ courseTitle: appHooks.useCardCourseData(cardId).title,
+ relatedPrograms: appHooks.useCardRelatedProgramsData(cardId).list,
});
export default useProgramData;
diff --git a/src/containers/RelatedProgramsModal/hooks.test.js b/src/containers/RelatedProgramsModal/hooks.test.js
index 8ada046..001f043 100644
--- a/src/containers/RelatedProgramsModal/hooks.test.js
+++ b/src/containers/RelatedProgramsModal/hooks.test.js
@@ -9,7 +9,7 @@ jest.mock('data/redux', () => ({
},
}));
-const courseNumber = 'test-course-number';
+const cardId = 'test-course-number';
const courseTitle = 'test-course-title';
const relatedPrograms = ['some', 'programs'];
@@ -18,9 +18,9 @@ describe('RelatedProgramsModal hooks', () => {
it('forwards course title and related programs list by course number', () => {
appHooks.useCardCourseData.mockReturnValue({ title: courseTitle });
appHooks.useCardRelatedProgramsData.mockReturnValue({ list: relatedPrograms });
- const out = hooks.useProgramData({ courseNumber });
- expect(appHooks.useCardCourseData).toHaveBeenCalledWith(courseNumber);
- expect(appHooks.useCardRelatedProgramsData).toHaveBeenCalledWith(courseNumber);
+ const out = hooks.useProgramData({ cardId });
+ expect(appHooks.useCardCourseData).toHaveBeenCalledWith(cardId);
+ expect(appHooks.useCardRelatedProgramsData).toHaveBeenCalledWith(cardId);
expect(out).toEqual({ courseTitle, relatedPrograms });
});
});
diff --git a/src/containers/RelatedProgramsModal/index.jsx b/src/containers/RelatedProgramsModal/index.jsx
index 87bc366..f25f8b9 100644
--- a/src/containers/RelatedProgramsModal/index.jsx
+++ b/src/containers/RelatedProgramsModal/index.jsx
@@ -13,10 +13,10 @@ import './index.scss';
export const RelatedProgramsModal = ({
isOpen,
closeModal,
- courseNumber,
+ cardId,
}) => {
const { formatMessage } = useIntl();
- const { courseTitle, relatedPrograms } = useProgramData({ courseNumber });
+ const { courseTitle, relatedPrograms } = useProgramData({ cardId });
return (
({
useProgramData: jest.fn(),
}));
-const courseNumber = 'test-course-number';
+const cardId = 'test-course-number';
const hookProps = {
courseTitle: 'hookProps.courseTitle',
relatedPrograms: [
@@ -31,7 +31,7 @@ const hookProps = {
const props = {
isOpen: true,
closeModal: jest.fn().mockName('props.closeModal'),
- courseNumber,
+ cardId,
};
describe('RelatedProgramsModal', () => {
diff --git a/src/containers/SelectSession/SelectSessionModal.jsx b/src/containers/SelectSession/SelectSessionModal.jsx
deleted file mode 100644
index 72ba23d..0000000
--- a/src/containers/SelectSession/SelectSessionModal.jsx
+++ /dev/null
@@ -1,82 +0,0 @@
-import React from 'react';
-import PropTypes from 'prop-types';
-
-import { useIntl } from '@edx/frontend-platform/i18n';
-import {
- ActionRow, Button, Form, ModalDialog,
-} from '@edx/paragon';
-
-import { nullMethod } from 'hooks';
-import { dateFormatter } from 'utils';
-
-import useSelectSession from './hooks';
-import messages from './messages';
-
-export const SelectSessionModal = ({ courseNumber }) => {
- const {
- entitlementSessions,
- showSessionModal,
- closeSessionModal,
- showLeaveSessionInSessionModal,
- courseTitle,
- } = useSelectSession({
- courseNumber,
- });
-
- const { formatMessage, formatDate } = useIntl();
-
- let header;
- let hint;
- if (showLeaveSessionInSessionModal) {
- header = formatMessage(messages.changeOrLeaveHeader);
- hint = formatMessage(messages.changeOrLeaveHint);
- } else {
- header = formatMessage(messages.selectSessionHeader, {
- courseTitle,
- });
- hint = formatMessage(messages.selectSessionHint);
- }
-
- return (
-
-
-
{header}
-
- {hint}
-
- {entitlementSessions?.map((entitle) => (
-
- {dateFormatter(formatDate, entitle.startDate)} - {dateFormatter(formatDate, entitle.endDate)}
-
- ))}
- {showLeaveSessionInSessionModal ? (
-
- {formatMessage(messages.leaveSessionOption)}
-
- ) : null}
-
-
-
-
-
-
-
-
- );
-};
-SelectSessionModal.propTypes = {
- courseNumber: PropTypes.string.isRequired,
-};
-
-export default SelectSessionModal;
diff --git a/src/containers/SelectSession/__snapshots__/SelectSessionModal.test.jsx.snap b/src/containers/SelectSession/__snapshots__/SelectSessionModal.test.jsx.snap
deleted file mode 100644
index 1e0f09b..0000000
--- a/src/containers/SelectSession/__snapshots__/SelectSessionModal.test.jsx.snap
+++ /dev/null
@@ -1,188 +0,0 @@
-// Jest Snapshot v1, https://goo.gl/fbAQLP
-
-exports[`SelectSessionModal snapshot empty modal with leave option 1`] = `
-
-
-
- Change or leave session?
-
-
-
- When you change to a different session any course progress or grades from your current session will be lost.
-
-
-
- Leave session
-
-
-
-
-
-
-
-
-
-`;
-
-exports[`SelectSessionModal snapshot modal with leave option 1`] = `
-
-
-
- Change or leave session?
-
-
-
- When you change to a different session any course progress or grades from your current session will be lost.
-
-
-
- 1/2/2000
- -
- 1/2/2020
-
-
- 2/3/2000
- -
- 2/3/2020
-
-
- 3/4/2000
- -
- 3/4/2020
-
-
- Leave session
-
-
-
-
-
-
-
-
-
-`;
-
-exports[`SelectSessionModal snapshot modal without leave option 1`] = `
-
-
-
- Select a session to access course-title: unit test save life
-
-
-
- Remember, if you change your mind you have 2 weeks to unenroll and reclaim your entitlement.
-
-
-
- 1/2/2000
- -
- 1/2/2020
-
-
- 2/3/2000
- -
- 2/3/2020
-
-
- 3/4/2000
- -
- 3/4/2020
-
-
-
-
-
-
-
-
-
-`;
diff --git a/src/containers/SelectSession/__snapshots__/index.test.jsx.snap b/src/containers/SelectSession/__snapshots__/index.test.jsx.snap
deleted file mode 100644
index e921de5..0000000
--- a/src/containers/SelectSession/__snapshots__/index.test.jsx.snap
+++ /dev/null
@@ -1,9 +0,0 @@
-// Jest Snapshot v1, https://goo.gl/fbAQLP
-
-exports[`SelectSession snapshot has courseNumber 1`] = `
-
-`;
-
-exports[`SelectSession snapshot no courseNumber 1`] = `""`;
diff --git a/src/containers/SelectSession/hooks.js b/src/containers/SelectSession/hooks.js
deleted file mode 100644
index e7d686d..0000000
--- a/src/containers/SelectSession/hooks.js
+++ /dev/null
@@ -1,35 +0,0 @@
-import { hooks as appHooks, actions } from 'data/redux';
-
-import { useDispatch } from 'react-redux';
-
-export const useSelectSession = ({ courseNumber }) => {
- const dispatch = useDispatch();
- const {
- showSessionModal,
- showLeaveSessionInSessionModal,
- } = appHooks.useSelectSessionsModalData();
-
- const { entitlementSessions } = appHooks.useCardEntitlementsData(courseNumber);
-
- const { title: courseTitle } = appHooks.useCardCourseData(courseNumber);
-
- const updateSessionModal = (showModal, showLeaveOption = false) => dispatch(
- actions.app.updateSelectSessionModal({
- showSessionModal: showModal,
- showLeaveSessionInSessionModal: showLeaveOption,
- courseNumber,
- }),
- );
-
- return {
- showSessionModal,
- closeSessionModal: () => updateSessionModal(false),
- openSessionModal: () => updateSessionModal(true),
- openSessionModalWithLeaveOption: () => updateSessionModal(true, true),
- showLeaveSessionInSessionModal,
- entitlementSessions,
- courseTitle,
- };
-};
-
-export default useSelectSession;
diff --git a/src/containers/SelectSession/hooks.test.js b/src/containers/SelectSession/hooks.test.js
deleted file mode 100644
index 11a7ba2..0000000
--- a/src/containers/SelectSession/hooks.test.js
+++ /dev/null
@@ -1,85 +0,0 @@
-import { hooks as appHooks, actions } from 'data/redux';
-
-import * as hooks from './hooks';
-
-jest.mock('data/redux', () => ({
- hooks: {
- useCardEntitlementsData: jest.fn(),
- useCardCourseData: jest.fn(),
- useSelectSessionsModalData: jest.fn(),
- },
- actions: {
- app: {
- updateSelectSessionModal: jest.fn(),
- },
- },
-}));
-
-const courseNumber = 'my-test-course-number';
-
-const entitlement = {
- showSessionModal: false,
- showLeaveSessionInSessionModal: false,
-};
-
-const availableSessions = [
- { startDate: '1/2/2000', endDate: '1/2/2020', courseNumber },
- { startDate: '2/3/2000', endDate: '2/3/2020', courseNumber },
- { startDate: '3/4/2000', endDate: '3/4/2020', courseNumber },
-];
-
-const cardCourseData = {
- title: 'course-title: brown fox',
-};
-
-describe('SelectSessionModal hooks', () => {
- let out;
-
- beforeEach(() => {
- jest.clearAllMocks();
- });
- describe('useSelectSession', () => {
- beforeEach(() => {
- appHooks.useSelectSessionsModalData.mockReturnValueOnce({ ...entitlement });
- appHooks.useCardEntitlementsData.mockReturnValueOnce({ entitlementSessions: availableSessions });
- appHooks.useCardCourseData.mockReturnValueOnce({ ...cardCourseData });
- out = hooks.useSelectSession({ courseNumber });
- });
-
- test('loads entitlement data based on course number', () => {
- expect(appHooks.useCardEntitlementsData).toHaveBeenCalledWith(courseNumber);
- });
-
- test('get course title based on course number', () => {
- expect(appHooks.useCardCourseData).toHaveBeenCalledWith(courseNumber);
- expect(out.courseTitle).toEqual(cardCourseData.title);
- });
-
- test('open session modal', () => {
- out.openSessionModal();
- expect(actions.app.updateSelectSessionModal).toHaveBeenCalledWith({
- showSessionModal: true,
- showLeaveSessionInSessionModal: false,
- courseNumber,
- });
- });
-
- test('open session modal with leave option', () => {
- out.openSessionModalWithLeaveOption();
- expect(actions.app.updateSelectSessionModal).toHaveBeenCalledWith({
- showSessionModal: true,
- showLeaveSessionInSessionModal: true,
- courseNumber,
- });
- });
-
- test('close session modal', () => {
- out.closeSessionModal();
- expect(actions.app.updateSelectSessionModal).toHaveBeenCalledWith({
- showSessionModal: false,
- showLeaveSessionInSessionModal: false,
- courseNumber,
- });
- });
- });
-});
diff --git a/src/containers/SelectSession/index.jsx b/src/containers/SelectSession/index.jsx
deleted file mode 100644
index f4cddca..0000000
--- a/src/containers/SelectSession/index.jsx
+++ /dev/null
@@ -1,12 +0,0 @@
-import React from 'react';
-import { hooks as appHooks } from 'data/redux';
-
-import SelectSessionModal from './SelectSessionModal';
-
-export const SelectSession = () => {
- const { courseNumber } = appHooks.useSelectSessionsModalData();
- return courseNumber ? : null;
-};
-SelectSession.propTypes = {};
-
-export default SelectSession;
diff --git a/src/containers/SelectSession/index.test.jsx b/src/containers/SelectSession/index.test.jsx
deleted file mode 100644
index 8200c0d..0000000
--- a/src/containers/SelectSession/index.test.jsx
+++ /dev/null
@@ -1,26 +0,0 @@
-import React from 'react';
-import { shallow } from 'enzyme';
-
-import { hooks as appHooks } from 'data/redux';
-
-import SelectSession from '.';
-
-jest.mock('data/redux', () => ({
- hooks: {
- useSelectSessionsModalData: jest.fn(),
- },
-}));
-
-describe('SelectSession', () => {
- describe('snapshot', () => {
- test('no courseNumber', () => {
- appHooks.useSelectSessionsModalData.mockReturnValueOnce({ courseNumber: null });
- expect(shallow()).toMatchSnapshot();
- });
-
- test('has courseNumber', () => {
- appHooks.useSelectSessionsModalData.mockReturnValueOnce({ courseNumber: 'some course' });
- expect(shallow()).toMatchSnapshot();
- });
- });
-});
diff --git a/src/containers/SelectSessionModal/__snapshots__/index.test.jsx.snap b/src/containers/SelectSessionModal/__snapshots__/index.test.jsx.snap
new file mode 100644
index 0000000..70e0308
--- /dev/null
+++ b/src/containers/SelectSessionModal/__snapshots__/index.test.jsx.snap
@@ -0,0 +1,170 @@
+// Jest Snapshot v1, https://goo.gl/fbAQLP
+
+exports[`SelectSessionModal snapshot empty modal with leave option 1`] = `
+
+
+ test-header
+
+
+
+ test-hint
+
+
+
+ Leave session
+
+
+
+
+
+
+
+
+`;
+
+exports[`SelectSessionModal snapshot modal with leave option 1`] = `
+
+
+ test-header
+
+
+
+ test-hint
+
+
+
+ 1/2/2000
+ -
+ 1/2/2020
+
+
+ 2/3/2000
+ -
+ 2/3/2020
+
+
+ 3/4/2000
+ -
+ 3/4/2020
+
+
+ Leave session
+
+
+
+
+
+
+
+
+`;
+
+exports[`SelectSessionModal snapshot modal without leave option 1`] = `
+
+
+ test-header
+
+
+
+ test-hint
+
+
+
+ 1/2/2000
+ -
+ 1/2/2020
+
+
+ 2/3/2000
+ -
+ 2/3/2020
+
+
+ 3/4/2000
+ -
+ 3/4/2020
+
+
+
+
+
+
+
+
+`;
diff --git a/src/containers/SelectSessionModal/hooks.js b/src/containers/SelectSessionModal/hooks.js
new file mode 100644
index 0000000..83482bd
--- /dev/null
+++ b/src/containers/SelectSessionModal/hooks.js
@@ -0,0 +1,46 @@
+import { useDispatch } from 'react-redux';
+
+import { useIntl } from '@edx/frontend-platform/i18n';
+
+import { hooks as appHooks } from 'data/redux';
+
+import messages from './messages';
+
+export const useSelectSessionModalData = () => {
+ const dispatch = useDispatch();
+ const selectedCardId = appHooks.useSelectSessionModalData().cardId;
+
+ const {
+ entitlementSessions,
+ isFulfilled,
+ } = appHooks.useCardEntitlementsData(selectedCardId);
+
+ const { title: courseTitle } = appHooks.useCardCourseData(selectedCardId);
+
+ const { formatMessage } = useIntl();
+
+ let header;
+ let hint;
+ if (isFulfilled) {
+ header = formatMessage(messages.changeOrLeaveHeader);
+ hint = formatMessage(messages.changeOrLeaveHint);
+ } else {
+ header = formatMessage(messages.selectSessionHeader, {
+ courseTitle,
+ });
+ hint = formatMessage(messages.selectSessionHint);
+ }
+ const updateCallback = appHooks.useUpdateSelectSessionModalCallback;
+
+ return {
+ showModal: selectedCardId != null,
+ closeSessionModal: updateCallback(dispatch, null),
+ openSessionModal: (cardId) => updateCallback(dispatch, cardId),
+ showLeaveOption: isFulfilled,
+ entitlementSessions,
+ hint,
+ header,
+ };
+};
+
+export default useSelectSessionModalData;
diff --git a/src/containers/SelectSessionModal/hooks.test.js b/src/containers/SelectSessionModal/hooks.test.js
new file mode 100644
index 0000000..9b7471c
--- /dev/null
+++ b/src/containers/SelectSessionModal/hooks.test.js
@@ -0,0 +1,110 @@
+import { useDispatch } from 'react-redux';
+import { useIntl } from '@edx/frontend-platform/i18n';
+
+import { hooks as appHooks } from 'data/redux';
+
+import messages from './messages';
+import * as hooks from './hooks';
+
+jest.mock('data/redux', () => ({
+ hooks: {
+ useCardEntitlementsData: jest.fn(),
+ useCardCourseData: jest.fn(),
+ useSelectSessionModalData: jest.fn(),
+ useUpdateSelectSessionModalCallback: jest.fn((...args) => ({
+ updateSelectSession: args,
+ })),
+ },
+ actions: {
+ app: {
+ updateSelectSessionModal: jest.fn(),
+ },
+ },
+}));
+
+const selectedCardId = 'test-selected-card-id';
+
+const selectSessionData = {
+ cardId: selectedCardId,
+};
+
+const entitlementsData = {
+ entitlementSessions: [
+ { startDate: '1/2/2000', endDate: '1/2/2020', cardId: 'session-id-1' },
+ { startDate: '2/3/2000', endDate: '2/3/2020', cardId: 'session-id-2' },
+ { startDate: '3/4/2000', endDate: '3/4/2020', cardId: 'session-id-3' },
+ ],
+ isFullfilled: false,
+};
+
+const cardCourseData = {
+ title: 'course-title: brown fox',
+};
+
+const { formatMessage } = useIntl();
+const dispatch = useDispatch();
+
+describe('SelectSessionModal hooks', () => {
+ let out;
+
+ beforeEach(() => {
+ jest.clearAllMocks();
+ });
+ describe('useSelectSession', () => {
+ const runHook = ({ selectSession = {}, entitlements = {}, course = {} }) => {
+ appHooks.useSelectSessionModalData.mockReturnValueOnce({
+ ...selectSessionData,
+ ...selectSession,
+ });
+ appHooks.useCardEntitlementsData.mockReturnValueOnce({
+ ...entitlementsData,
+ ...entitlements,
+ });
+ appHooks.useCardCourseData.mockReturnValueOnce({
+ ...cardCourseData,
+ ...course,
+ });
+ out = hooks.useSelectSessionModalData();
+ };
+ beforeEach(() => {
+ runHook({});
+ });
+
+ describe('initialization', () => {
+ test('loads entitlement data based on course number', () => {
+ expect(appHooks.useCardEntitlementsData).toHaveBeenCalledWith(selectedCardId);
+ });
+
+ test('get course title based on course number', () => {
+ expect(appHooks.useCardCourseData).toHaveBeenCalledWith(selectedCardId);
+ });
+ });
+
+ describe('output', () => {
+ test('showModal returns true if selectedCardId is not null or undefined', () => {
+ expect(out.showModal).toEqual(true);
+ runHook({ selectSession: { cardId: null } });
+ expect(out.showModal).toEqual(false);
+ runHook({ selectSession: { cardId: undefined } });
+ expect(out.showModal).toEqual(false);
+ });
+ test('displays change or leave header and hint if fulfilled', () => {
+ expect(out.header).toEqual(formatMessage(
+ messages.selectSessionHeader,
+ { courseTitle: cardCourseData.title },
+ ));
+ expect(out.hint).toEqual(formatMessage(messages.selectSessionHint));
+ });
+ test('displays select session header (w/ courseTitle) and hint if unfulfilled', () => {
+ runHook({ entitlements: { isFulfilled: true } });
+ expect(out.header).toEqual(formatMessage(messages.changeOrLeaveHeader));
+ expect(out.hint).toEqual(formatMessage(messages.changeOrLeaveHint));
+ });
+ test('closeSessionModal returns update callback wth dispatch and null card id', () => {
+ expect(out.closeSessionModal).toEqual(
+ appHooks.useUpdateSelectSessionModalCallback(dispatch, null),
+ );
+ });
+ });
+ });
+});
diff --git a/src/containers/SelectSessionModal/index.jsx b/src/containers/SelectSessionModal/index.jsx
new file mode 100644
index 0000000..edc7d59
--- /dev/null
+++ b/src/containers/SelectSessionModal/index.jsx
@@ -0,0 +1,64 @@
+import React from 'react';
+
+import { useIntl } from '@edx/frontend-platform/i18n';
+import {
+ ActionRow,
+ Button,
+ Form,
+ ModalDialog,
+} from '@edx/paragon';
+
+import { nullMethod } from 'hooks';
+import { dateFormatter } from 'utils';
+
+import useSelectSessionModalData from './hooks';
+import messages from './messages';
+
+export const SelectSessionModal = () => {
+ const {
+ entitlementSessions,
+ showModal,
+ closeSessionModal,
+ showLeaveOption,
+ header,
+ hint,
+ } = useSelectSessionModalData();
+
+ const { formatMessage, formatDate } = useIntl();
+
+ return (
+
+ {header}
+
+ {hint}
+
+ {entitlementSessions?.map((session) => (
+
+ {dateFormatter(formatDate, session.startDate)} - {dateFormatter(formatDate, session.endDate)}
+
+ ))}
+ {showLeaveOption && (
+
+ {formatMessage(messages.leaveSessionOption)}
+
+ )}
+
+
+
+
+
+
+
+ );
+};
+
+export default SelectSessionModal;
diff --git a/src/containers/SelectSession/SelectSessionModal.test.jsx b/src/containers/SelectSessionModal/index.test.jsx
similarity index 64%
rename from src/containers/SelectSession/SelectSessionModal.test.jsx
rename to src/containers/SelectSessionModal/index.test.jsx
index f8adb2d..e91da0d 100644
--- a/src/containers/SelectSession/SelectSessionModal.test.jsx
+++ b/src/containers/SelectSessionModal/index.test.jsx
@@ -2,7 +2,7 @@ import React from 'react';
import { shallow } from 'enzyme';
import hooks from './hooks';
-import SelectSessionModal from './SelectSessionModal';
+import SelectSessionModal from '.';
jest.mock('./hooks', () => ({
__esModule: true,
@@ -11,10 +11,11 @@ jest.mock('./hooks', () => ({
const hookReturn = {
entitlementSessions: [],
- showSessionModal: true,
- closeSessionModal: jest.fn().mockName('useSelectSession.closeSessionModal'),
- showLeaveSessionInSessionModal: true,
- courseTitle: 'course-title: unit test save life',
+ showModal: true,
+ closeSessionModal: jest.fn().mockName('useSelectSessionModalData.closeSessionModal'),
+ showLeaveOption: true,
+ header: 'test-header',
+ hint: 'test-hint',
};
const courseNumber = 'my-test-course-number';
@@ -31,7 +32,7 @@ describe('SelectSessionModal', () => {
hooks.mockReturnValueOnce({
...hookReturn,
});
- expect(shallow()).toMatchSnapshot();
+ expect(shallow()).toMatchSnapshot();
});
test('modal with leave option ', () => {
@@ -39,16 +40,16 @@ describe('SelectSessionModal', () => {
...hookReturn,
entitlementSessions: [...availableSessions],
});
- expect(shallow()).toMatchSnapshot();
+ expect(shallow()).toMatchSnapshot();
});
test('modal without leave option ', () => {
hooks.mockReturnValueOnce({
...hookReturn,
entitlementSessions: [...availableSessions],
- showLeaveSessionInSessionModal: false,
+ showLeaveOption: false,
});
- expect(shallow()).toMatchSnapshot();
+ expect(shallow()).toMatchSnapshot();
});
});
});
diff --git a/src/containers/SelectSession/messages.js b/src/containers/SelectSessionModal/messages.js
similarity index 95%
rename from src/containers/SelectSession/messages.js
rename to src/containers/SelectSessionModal/messages.js
index 9fa5ed3..c6861b3 100644
--- a/src/containers/SelectSession/messages.js
+++ b/src/containers/SelectSessionModal/messages.js
@@ -10,7 +10,7 @@ export const messages = StrictDict({
selectSessionHeader: {
id: 'learner-dash.selectSession.selectSessionHeader',
description: 'Header for unfulfilled entitlement',
- defaultMessage: 'Select a session to access {courseTitle}',
+ defaultMessage: 'Select a session',
},
changeOrLeaveHint: {
id: 'learner-dash.selectSession.changeOrLeaveHint',
diff --git a/src/containers/WidgetSidebar/index.scss b/src/containers/WidgetSidebar/index.scss
index 6ea7bad..71ae8fc 100644
--- a/src/containers/WidgetSidebar/index.scss
+++ b/src/containers/WidgetSidebar/index.scss
@@ -1,8 +1,8 @@
@import "@edx/paragon/scss/core/core";
-
.widget-sidebar {
+ margin-top: map-get($spacers, 5);
width: 400px;
flex-shrink: 0;
padding-left: map-get($spacers, 2);
padding-right: map-get($spacers, 2);
-}
\ No newline at end of file
+}
diff --git a/src/data/constants/app.js b/src/data/constants/app.js
index 1e12922..f053dc0 100644
--- a/src/data/constants/app.js
+++ b/src/data/constants/app.js
@@ -1,4 +1,20 @@
import { getConfig } from '@edx/frontend-platform';
+import { StrictDict } from 'utils';
export const routePath = `${getConfig().PUBLIC_PATH}:courseId`;
export const locationId = window.location.pathname.slice(1);
+
+export const SortKeys = StrictDict({
+ enrolled: 'enrolled',
+ title: 'title',
+});
+
+export const FilterKeys = StrictDict({
+ inProgress: 'inProgress',
+ notStarted: 'notStarted',
+ done: 'done',
+ notEnrolled: 'notEnrolled',
+ upgraded: 'upgraded',
+});
+
+export const ListPageSize = 50;
diff --git a/src/data/redux/app/reducer.js b/src/data/redux/app/reducer.js
index ac44d28..c0aaeb5 100644
--- a/src/data/redux/app/reducer.js
+++ b/src/data/redux/app/reducer.js
@@ -10,30 +10,25 @@ const initialState = {
platformSettings: {},
suggestedCourses: [],
filterState: {},
- selectSessionsModal: {},
+ selectSessionModal: {},
};
+export const cardId = (val) => `card-${val}`;
+
// eslint-disable-next-line no-unused-vars
const app = createSlice({
name: 'app',
initialState,
reducers: {
- loadCourses: (state, { payload: { enrollments, entitlements } }) => ({
+ loadCourses: (state, { payload: { courses } }) => ({
...state,
- enrollments: [
- ...enrollments.map(curr => curr.courseRun.courseNumber),
- ...entitlements.map(curr => curr.courseRun.courseNumber),
- ],
- courseData: {
- ...entitlements.reduce(
- (obj, curr) => ({ ...obj, [curr.courseRun.courseNumber]: curr }),
- {},
- ),
- ...enrollments.reduce(
- (obj, curr) => ({ ...obj, [curr.courseRun.courseNumber]: curr }),
- {},
- ),
- },
+ courseData: courses.reduce(
+ (obj, curr, index) => ({
+ ...obj,
+ [cardId(index)]: { ...curr, cardId: cardId(index) },
+ }),
+ {},
+ ),
}),
loadGlobalData: (state, { payload }) => ({
...state,
@@ -44,9 +39,7 @@ const app = createSlice({
}),
updateSelectSessionModal: (state, { payload }) => ({
...state,
- selectSessionsModal: {
- ...payload,
- },
+ selectSessionModal: { cardId: payload },
}),
},
});
diff --git a/src/data/redux/app/reducer.test.js b/src/data/redux/app/reducer.test.js
index 4db6537..ef70879 100644
--- a/src/data/redux/app/reducer.test.js
+++ b/src/data/redux/app/reducer.test.js
@@ -1,4 +1,6 @@
-import { initialState, reducer, actions } from './reducer';
+import {
+ cardId, initialState, reducer, actions,
+} from './reducer';
describe('app reducer', () => {
describe('reducers', () => {
@@ -12,13 +14,6 @@ describe('app reducer', () => {
},
entitlements: [],
};
- // const testValue = 'my-test-value';
- // const testAction = (action, expected) => {
- // expect(reducer(testState, action)).toEqual({
- // ...testState,
- // ...expected,
- // });
- // };
describe('action handlers', () => {
describe('loadCourses', () => {
const courseIds = [
@@ -32,29 +27,29 @@ describe('app reducer', () => {
];
const enrollmentData = [
{
- courseRun: { courseNumber: courseIds[0] },
+ courseRun: { cardId: courseIds[0] },
course: 1,
some: 'data',
},
{
- courseRun: { courseNumber: courseIds[1] },
+ courseRun: { cardId: courseIds[1] },
course: 2,
some: 'other data',
},
{
- courseRun: { courseNumber: courseIds[2] },
+ courseRun: { cardId: courseIds[2] },
course: 3,
some: 'still different data',
},
];
const entitlementData = [
{
- courseRun: { courseNumber: entitlementIds[0] },
+ courseRun: { cardId: entitlementIds[0] },
course: 4,
some: 'STILL different data',
},
{
- courseRun: { courseNumber: entitlementIds[1] },
+ courseRun: { cardId: entitlementIds[1] },
course: 5,
some: 'still DIFFERENT data',
},
@@ -62,23 +57,16 @@ describe('app reducer', () => {
let out;
beforeEach(() => {
out = reducer(testState, actions.loadCourses({
- enrollments: enrollmentData,
- entitlements: entitlementData,
+ courses: [...enrollmentData, ...entitlementData],
}));
});
- it('loads list of courseRun ids into enrollments field', () => {
- expect(out.enrollments).toEqual([
- ...courseIds,
- ...entitlementIds,
- ]);
- });
it('loads object keyed by courseRun ids into courseData field', () => {
expect(out.courseData).toEqual({
- [courseIds[0]]: enrollmentData[0],
- [courseIds[1]]: enrollmentData[1],
- [courseIds[2]]: enrollmentData[2],
- [entitlementIds[0]]: entitlementData[0],
- [entitlementIds[1]]: entitlementData[1],
+ [cardId(0)]: { ...enrollmentData[0], cardId: cardId(0) },
+ [cardId(1)]: { ...enrollmentData[1], cardId: cardId(1) },
+ [cardId(2)]: { ...enrollmentData[2], cardId: cardId(2) },
+ [cardId(3)]: { ...entitlementData[0], cardId: cardId(3) },
+ [cardId(4)]: { ...entitlementData[1], cardId: cardId(4) },
});
});
});
diff --git a/src/data/redux/app/selectors.js b/src/data/redux/app/selectors.js
index fdae538..ebcb7c5 100644
--- a/src/data/redux/app/selectors.js
+++ b/src/data/redux/app/selectors.js
@@ -1,6 +1,7 @@
import { createSelector } from 'reselect';
import { StrictDict } from 'utils';
+import { FilterKeys } from 'data/constants/app';
import * as module from './selectors';
@@ -10,24 +11,36 @@ const mkSimpleSelector = (cb) => createSelector([module.appSelector], cb);
// top-level app data selectors
export const simpleSelectors = {
- enrollments: mkSimpleSelector(app => app.enrollments),
- entitlements: mkSimpleSelector(app => app.entitlements),
courseData: mkSimpleSelector(app => app.courseData),
platformSettings: mkSimpleSelector(app => app.platformSettings),
suggestedCourses: mkSimpleSelector(app => app.suggestedCourses),
emailConfirmation: mkSimpleSelector(app => app.emailConfirmation),
enterpriseDashboards: mkSimpleSelector(app => app.enterpriseDashboards),
- selectSessionsModal: mkSimpleSelector(app => app.selectSessionsModal),
+ selectSessionModal: mkSimpleSelector(app => app.selectSessionModal),
};
-export const courseCardData = (state, courseNumber) => (
- module.simpleSelectors.courseData(state)[courseNumber]
+export const numCourses = createSelector(
+ [module.simpleSelectors.courseData],
+ (courseData) => Object.keys(courseData).length,
+);
+export const hasCourses = createSelector([module.numCourses], (num) => num > 0);
+export const hasAvailableDashboards = createSelector(
+ [module.simpleSelectors.enterpriseDashboards],
+ (data) => !!data.availableDashboards,
);
-const mkCardSelector = (sel) => (state, courseNumber) => (
- sel(courseCardData(state, courseNumber))
+export const courseCardData = (state, cardId) => (
+ module.simpleSelectors.courseData(state)[cardId]
);
+const mkCardSelector = (sel) => (state, cardId) => {
+ const cardData = module.courseCardData(state, cardId);
+ if (cardData) {
+ return sel(cardData);
+ }
+ return {};
+};
+
const dateSixMonthsFromNow = new Date();
dateSixMonthsFromNow.setDate(dateSixMonthsFromNow.getDate() + 180);
@@ -43,28 +56,41 @@ export const courseCard = StrictDict({
})),
course: mkCardSelector(({ course }) => ({
bannerUrl: course.bannerUrl,
+ courseNumber: course.courseNumber,
title: course.title,
website: course.website,
})),
- courseRun: mkCardSelector(({ courseRun }) => ({
+ courseRun: mkCardSelector(({ courseRun }) => (courseRun === null ? {} : {
endDate: courseRun?.endDate,
+ courseId: courseRun.courseId,
isArchived: courseRun.isArchived,
isStarted: courseRun.isStarted,
isFinished: courseRun.isFinished,
minPassingGrade: courseRun.minPassingGrade,
})),
- enrollment: mkCardSelector(({ enrollment }) => ({
- accessExpirationDate: enrollment.accessExpirationDate,
- canUpgrade: enrollment.canUpgrade,
- hasStarted: enrollment.hasStarted,
- isAudit: enrollment.isAudit,
- isAuditAccessExpired: enrollment.isAuditAccessExpired,
- isEmailEnabled: enrollment.isEmailEnabled,
- isVerified: enrollment.isVerified,
- lastEnrolled: enrollment.lastEnrollment,
- isEnrolled: enrollment.isEnrolled,
- })),
+ enrollment: mkCardSelector(({ enrollment }) => {
+ if (enrollment == null) {
+ return {
+ isEnrolled: false,
+ };
+ }
+ return {
+ accessExpirationDate: enrollment.accessExpirationDate,
+ canUpgrade: enrollment.canUpgrade,
+ hasStarted: enrollment.hasStarted,
+ hasFinished: enrollment.hasFinished,
+ isAudit: enrollment.isAudit,
+ isAuditAccessExpired: enrollment.isAuditAccessExpired,
+ isEmailEnabled: enrollment.isEmailEnabled,
+ isVerified: enrollment.isVerified,
+ lastEnrolled: enrollment.lastEnrollment,
+ isEnrolled: enrollment.isEnrolled,
+ };
+ }),
entitlements: mkCardSelector(({ entitlements }) => {
+ if (!entitlements) {
+ return {};
+ }
const deadline = new Date(entitlements.changeDeadline);
const showExpirationWarning = deadline > new Date() && deadline <= dateSixMonthsFromNow;
return {
@@ -96,7 +122,71 @@ export const courseCard = StrictDict({
})),
});
+export const currentList = (state, {
+ sortBy,
+ isAscending,
+ filters,
+ pageNumber,
+ pageSize,
+}) => {
+ let list = Object.values(module.simpleSelectors.courseData(state));
+ if (filters.length) {
+ list = list.filter(course => {
+ if (filters.includes(FilterKeys.notEnrolled)) {
+ if (!course.enrollment.isEnrolled) {
+ return false;
+ }
+ }
+ if (filters.includes(FilterKeys.done)) {
+ if (!course.enrollment.hasFinished) {
+ return false;
+ }
+ }
+ if (filters.includes(FilterKeys.upgraded)) {
+ if (!course.enrollment.isVerified) {
+ return false;
+ }
+ }
+ if (filters.includes(FilterKeys.inProgress)) {
+ if (!course.enrollment.hasStarted) {
+ return false;
+ }
+ }
+ if (filters.includes(FilterKeys.notStarted)) {
+ if (course.enrollment.hasStarted) {
+ return false;
+ }
+ }
+ return true;
+ });
+ }
+ if (sortBy === 'enrolled') {
+ list = list.sort((a, b) => {
+ const dateA = new Date(a.enrollment.lastEnrolled);
+ const dateB = new Date(b.enrollment.lastEnrolled);
+ if (dateA < dateB) { return isAscending ? -1 : 1; }
+ if (dateA > dateB) { return isAscending ? 1 : 1; }
+ return 0;
+ });
+ } else {
+ list = list.sort((a, b) => {
+ const titleA = a.course.title.toLowerCase();
+ const titleB = b.course.title.toLowerCase();
+ if (titleA < titleB) { return isAscending ? -1 : 1; }
+ if (titleA > titleB) { return isAscending ? 1 : 1; }
+ return 0;
+ });
+ }
+ return {
+ visible: list.slice((pageNumber - 1) * pageSize, pageNumber * pageSize),
+ numPages: Math.ceil(list.length / pageSize),
+ };
+};
+
export default StrictDict({
...simpleSelectors,
courseCard,
+ currentList,
+ hasCourses,
+ hasAvailableDashboards,
});
diff --git a/src/data/redux/hooks.js b/src/data/redux/hooks.js
index 7135a32..b1d33e0 100644
--- a/src/data/redux/hooks.js
+++ b/src/data/redux/hooks.js
@@ -1,5 +1,6 @@
import { useSelector } from 'react-redux';
+import { actions as appActions } from './app/reducer';
import appSelectors from './app/selectors';
const { courseCard } = appSelectors;
@@ -9,11 +10,17 @@ export const useEnterpriseDashboardData = () => useSelector(appSelectors.enterpr
export const usePlatformSettingsData = () => useSelector(appSelectors.platformSettings);
// suggested courses is max at 3 at the moment.
export const useSuggestedCoursesData = () => useSelector(appSelectors.suggestedCourses).slice(0, 3);
-export const useSelectSessionsModalData = () => useSelector(appSelectors.selectSessionsModal);
+export const useSelectSessionModalData = () => useSelector(appSelectors.selectSessionModal);
+
+export const useHasCourses = () => useSelector(appSelectors.hasCourses);
+export const useHasAvailableDashboards = () => useSelector(appSelectors.hasAvailableDashboards);
+export const useCurrentCourseList = (opts) => useSelector(
+ state => appSelectors.currentList(state, opts),
+);
// eslint-disable-next-line
-export const useCourseCardData = (selector) => (courseNumber) => useSelector(
- (state) => selector(state, courseNumber),
+export const useCourseCardData = (selector) => (cardId) => useSelector(
+ (state) => selector(state, cardId),
);
export const useCardCertificateData = useCourseCardData(courseCard.certificates);
@@ -24,3 +31,7 @@ export const useCardEntitlementsData = useCourseCardData(courseCard.entitlements
export const useCardGradeData = useCourseCardData(courseCard.grades);
export const useCardProviderData = useCourseCardData(courseCard.provider);
export const useCardRelatedProgramsData = useCourseCardData(courseCard.relatedPrograms);
+
+export const useUpdateSelectSessionModalCallback = (dispatch, cardId) => () => dispatch(
+ appActions.updateSelectSessionModal(cardId),
+);
diff --git a/src/data/redux/thunkActions/app.js b/src/data/redux/thunkActions/app.js
index 184d56e..2024cd2 100644
--- a/src/data/redux/thunkActions/app.js
+++ b/src/data/redux/thunkActions/app.js
@@ -14,8 +14,8 @@ import requests from './requests';
*/
export const initialize = () => (dispatch) => (
dispatch(requests.initializeList({
- onSuccess: (({ enrollments, entitlements, ...globalData }) => {
- dispatch(actions.app.loadCourses({ enrollments, entitlements }));
+ onSuccess: (({ courses, ...globalData }) => {
+ dispatch(actions.app.loadCourses({ courses }));
dispatch(actions.app.loadGlobalData(globalData));
}),
}))
@@ -23,8 +23,8 @@ export const initialize = () => (dispatch) => (
export const refreshList = () => (dispatch) => (
dispatch(requests.initializeList({
- onSuccess: (({ enrollments, entitlements, ...globalData }) => {
- dispatch(actions.app.loadCourses({ enrollments, entitlements }));
+ onSuccess: (({ courses, ...globalData }) => {
+ dispatch(actions.app.loadCourses({ courses }));
dispatch(actions.app.loadGlobalData(globalData));
}),
}))
diff --git a/src/data/services/lms/api.js b/src/data/services/lms/api.js
index e1ba3b8..3a6b0fa 100644
--- a/src/data/services/lms/api.js
+++ b/src/data/services/lms/api.js
@@ -16,8 +16,10 @@ import {
* GET Actions
*********************************************************************************/
const initializeList = () => Promise.resolve({
- enrollments: fakeData.courseRunData,
- entitlements: fakeData.entitlementData,
+ courses: [
+ ...fakeData.courseRunData,
+ ...fakeData.entitlementData,
+ ],
...fakeData.globalData,
});
diff --git a/src/data/services/lms/fakeData/courses.js b/src/data/services/lms/fakeData/courses.js
index 2f8000e..9e89fa4 100644
--- a/src/data/services/lms/fakeData/courses.js
+++ b/src/data/services/lms/fakeData/courses.js
@@ -37,7 +37,9 @@ export const relatedPrograms = [
},
];
-export const genCourseID = (index) => `course-id${index}`;
+export const genCardId = (index) => `card-id${index}`;
+export const genCourseId = (index) => `course-number${index}-course-id${index}`;
+export const genCourseNumber = (index) => `course-number${index}`;
export const genCourseTitle = (index) => `Course Name ${index}`;
const logos = {
@@ -59,7 +61,6 @@ const globalData = {
},
enterpriseDashboards: {
availableDashboards: [
- { label: 'Personal', url: '/dashboard' },
{ label: 'edX, Inc.', url: '/edx-dashboard' },
{ label: 'Harvard', url: '/harvard-dashboard' },
],
@@ -112,15 +113,15 @@ export const genCourseRunData = (data = {}) => ({
});
export const genEnrollmentData = (data = {}) => ({
- accessExpirationDate: futureDate,
- canUpgrade: data.verified ? null : true,
+ accessExpirationDate: ((data.isEnrolled === false) ? null : futureDate),
+ canUpgrade: (data.isVerified ? null : true),
+ hasFinished: false,
hasStarted: false,
- isAudit: true,
- isAuditAccessExpired: data.verified ? null : false,
+ isAudit: !data.isVerified || data.isEnrolled,
+ isAuditAccessExpired: data.isVerified ? null : false,
isEmailEnabled: false,
isEnrolled: true,
isVerified: false,
- lastEnrolled: pastDate,
...data,
});
@@ -137,150 +138,169 @@ export const genCertificateData = (data = {}) => ({
});
export const availableSessions = [
- { startDate: '1/2/2000', endDate: '1/2/2020', courseNumber: genCourseID(100) },
- { startDate: '2/3/2000', endDate: '2/3/2020', courseNumber: genCourseID(101) },
- { startDate: '3/4/2000', endDate: '3/4/2020', courseNumber: genCourseID(102) },
+ { startDate: '1/2/2000', endDate: '1/2/2020', courseId: genCourseId(1000) },
+ { startDate: '2/3/2000', endDate: '2/3/2020', courseId: genCourseId(1001) },
+ { startDate: '3/4/2000', endDate: '3/4/2020', courseId: genCourseId(1002) },
+ { startDate: '1/2/2000', endDate: '1/2/2020', courseId: genCourseId(1000) },
+ { startDate: '2/3/2000', endDate: '2/3/2020', courseId: genCourseId(1001) },
+ { startDate: '3/4/2000', endDate: '3/4/2020', courseId: genCourseId(1002) },
+ { startDate: '1/2/2000', endDate: '1/2/2020', courseId: genCourseId(1000) },
+ { startDate: '2/3/2000', endDate: '2/3/2020', courseId: genCourseId(1001) },
+ { startDate: '3/4/2000', endDate: '3/4/2020', courseId: genCourseId(1002) },
+ { startDate: '1/2/2000', endDate: '1/2/2020', courseId: genCourseId(1000) },
+ { startDate: '2/3/2000', endDate: '2/3/2020', courseId: genCourseId(1001) },
+ { startDate: '3/4/2000', endDate: '3/4/2020', courseId: genCourseId(1002) },
];
export const courseRuns = [
- // audit, pending, can upgrade
+ // audit, can upgrade, course not started,
+ {},
+ // audit, can upgrade, course started
{
- enrollment: genEnrollmentData({ isAudit: true }),
- grades: { isPassing: true },
- courseRun: { isPending: true },
- certificates: genCertificateData(),
- entitlements: { isEntitlement: false },
- },
- // audit, started, cannot upgrade, restricted
- {
- enrollment: genEnrollmentData({ isAudit: true, canUpgrade: false }),
- grades: { isPassing: true },
courseRun: { isStarted: true },
- certificates: genCertificateData({ isRestricted: true }),
- entitlements: { isEntitlement: false },
},
- // audit, started, can upgrade
+ // audit, can upgrade, course started, learner started
{
- enrollment: genEnrollmentData({ isAudit: true, canUpgrade: true }),
- grades: { isPassing: true },
courseRun: { isStarted: true },
- certificates: genCertificateData(),
- entitlements: { isEntitlement: false },
+ enrollment: { hasStarted: true },
},
- // audit, started, not passing
+ // audit, can upgrade, course started, learner started, not passing
{
- enrollment: genEnrollmentData({ isAudit: true, canUpgrade: true }),
+ courseRun: { isStarted: true },
+ enrollment: { hasStarted: true },
grades: { isPassing: false },
+ },
+ // audit, access expired, can upgrade, course started, learner started
+ {
courseRun: { isStarted: true },
- certificates: genCertificateData(),
- entitlements: { isEntitlement: false },
- },
- // audit, started, audit access expired, can upgrade
- {
- enrollment: genEnrollmentData({ isAudit: true, isAuditAccessExpired: true }),
- grades: { isPassing: true },
- courseRun: { isStarted: true, accessExpirationDate: pastDate },
- certificates: genCertificateData(),
- entitlements: { isEntitlement: false },
- },
- // audit, started, audit access expired, cannot upgrade
- {
- enrollment: genEnrollmentData({
- isAudit: true,
+ enrollment: {
+ hasStarted: true,
isAuditAccessExpired: true,
+ accessExpirationDate: pastDate,
+ },
+ },
+ // audit, access expired, cannot upgrade, course started, learner started
+ {
+ courseRun: { isStarted: true },
+ enrollment: {
+ accessExpirationDate: pastDate,
+ isAuditAccessExpired: true,
+ hasStarted: true,
canUpgrade: false,
- }),
- grades: { isPassing: true },
- courseRun: { isStarted: true, accessExpirationDate: pastDate },
- certificates: genCertificateData(),
- entitlements: { isEntitlement: false },
+ },
},
- // verified, pending, restricted
+
+ // verified, course not started
+ { enrollment: { isVerified: true } },
+ // verified, course started, learner not started
{
- enrollment: genEnrollmentData({ isAudit: false, isVerified: true }),
- grades: { isPassing: true },
- courseRun: { isPending: true },
- certificates: genCertificateData({ isRestricted: true }),
- entitlements: { isEntitlement: false },
- },
- // verified, started
- {
- enrollment: genEnrollmentData({ isAudit: false, isVerified: true }),
- grades: { isPassing: true },
courseRun: { isStarted: true },
- certificates: genCertificateData(),
- entitlements: { isEntitlement: false },
+ enrollment: { isVerified: true },
},
- // verified, not passing
+ // verified, course started, learner started, passing
{
- enrollment: genEnrollmentData({ isAudit: false, isVerified: true }),
+ courseRun: { isStarted: true },
+ enrollment: { hasStarted: true, isVerified: true },
+ },
+ // verified, course started, learner started, not passing
+ {
+ courseRun: { isStarted: true },
grades: { isPassing: false },
- courseRun: { isStarted: true },
- certificates: genCertificateData(),
- entitlements: { isEntitlement: false },
+ enrollment: { hasStarted: true, isVerified: true },
},
- // verified, finished, not passing
+ // verified, learner started, not finished, not passing
{
- enrollment: genEnrollmentData({ isAudit: false, isVerified: true }),
+ enrollment: { hasStarted: true, isVerified: true },
grades: { isPassing: false },
- courseRun: { isArchived: true, endDate: pastDate },
- certificates: genCertificateData(),
- entitlements: { isEntitlement: false },
},
- // verified, restricted
+ // verified, learner finished, passing, restricted
{
- enrollment: genEnrollmentData({ isVerified: true }),
- grades: { isPassing: true },
+ enrollment: {
+ hasFinished: true,
+ hasStarted: true,
+ isVerified: true,
+ },
courseRun: { isStarted: true },
- certificates: genCertificateData({ isRestricted: true }),
- entitlements: { isEntitlement: false },
+ certificates: { isRestricted: true },
},
- // verified, earned but not available
+ // verified, learner finished, passing, cert earned but not available
{
- enrollment: genEnrollmentData({ isVerified: true }),
- grades: { isPassing: true },
+ enrollment: {
+ hasFinished: true,
+ hasStarted: true,
+ isVerified: true,
+ },
courseRun: { isStarted: true },
- certificates: genCertificateData({
+ certificates: {
isEarned: true,
availableDate: futureDate,
- }),
- entitlements: { isEntitlement: false },
+ isAvailable: false,
+ },
},
- // verified, earned, downloadable (web + link)
+ // verified, learner finished, cert earned, downloadable (web + link)
{
- enrollment: genEnrollmentData({ isAudit: false, isVerified: true }),
- grades: { isPassing: true },
+ enrollment: {
+ hasFinished: true,
+ hasStarted: true,
+ isVerified: true,
+ },
courseRun: { isStarted: true },
- certificates: genCertificateData({
+ certificates: {
isEarned: true,
isAvailable: true,
isDownloadable: true,
availableDate: pastDate,
certDownloadUrl: logos.social,
certPreviewUrl: logos.edx,
- }),
- entitlements: { isEntitlement: false },
+ },
},
- // verified, earned, downloadable (link)
+ // verified, learner finished, cert earned, downloadable (link only)
{
- enrollment: genEnrollmentData({ isAudit: false, isVerified: true }),
- grades: { isPassing: true },
+ enrollment: {
+ hasFinished: true,
+ hasStarted: true,
+ isVerified: true,
+ },
courseRun: { isStarted: true },
- certificates: genCertificateData({
+ certificates: {
isEarned: true,
isAvailable: true,
isDownloadable: true,
availableDate: pastDate,
certDownloadUrl: logos.social,
- }),
- entitlements: { isEntitlement: false },
+ },
+ },
+ // verified, course archived, learner finished, cert earned, downloadable (web + link)
+ {
+ enrollment: {
+ hasFinished: true,
+ hasStarted: true,
+ isVerified: true,
+ },
+ courseRun: {
+ isStarted: true,
+ isArchived: true,
+ endDate: pastDate,
+ },
+ certificates: {
+ isEarned: true,
+ isAvailable: true,
+ isDownloadable: true,
+ availableDate: pastDate,
+ certDownloadUrl: logos.social,
+ certPreviewUrl: logos.edx,
+ },
+ },
+ // verified, course archived, learner started, not finished, not passing
+ {
+ enrollment: { hasStarted: true, isVerified: true },
+ grades: { isPassing: false },
+ courseRun: { isArchived: true, endDate: pastDate },
},
// Entitlement Course Run - Cannot view yet
{
- enrollment: genEnrollmentData({ isAudit: false, isVerified: true }),
- grades: { isPassing: true },
- courseRun: { isPending: true },
- certificates: genCertificateData(),
+ enrollment: { isVerified: true },
+ courseRun: { isStarted: false },
entitlements: {
isEntitlement: true,
isFulfilled: true,
@@ -294,10 +314,8 @@ export const courseRuns = [
},
// Entitlement Course Run - Can View and Change
{
- enrollment: genEnrollmentData({ isAudit: false, isVerified: true }),
- grades: { isPassing: true },
+ enrollment: { isVerified: true },
courseRun: { isStarted: true },
- certificates: genCertificateData(),
entitlements: {
isEntitlement: true,
isFulfilled: true,
@@ -311,10 +329,8 @@ export const courseRuns = [
},
// Entitlement Course Run - Can View but not Change
{
- enrollment: genEnrollmentData({ isAudit: false, isVerified: true }),
- grades: { isPassing: true },
+ enrollment: { isVerified: true },
courseRun: { isStarted: true },
- certificates: genCertificateData(),
entitlements: {
isEntitlement: true,
isFulfilled: true,
@@ -327,14 +343,8 @@ export const courseRuns = [
},
// Entitlement Course Run - Expired
{
- enrollment: genEnrollmentData({ isAudit: false, isVerified: true }),
- grades: { isPassing: true },
- courseRun: {
- isStarted: true,
- isArchived: true,
- endDate: pastDate,
- },
- certificates: genCertificateData(),
+ enrollment: { isVerified: true },
+ courseRun: { isStarted: true, isArchived: true, endDate: pastDate },
entitlements: {
isEntitlement: true,
isFulfilled: true,
@@ -406,28 +416,41 @@ export const entitlementCourses = [
export const courseRunData = courseRuns.map(
(data, index) => {
const title = genCourseTitle(index);
- const courseNumber = genCourseID(index);
+ const cardId = genCardId(index);
+ const courseId = genCourseId(index);
+ const courseNumber = genCourseNumber(index);
const providerIndex = index % 3;
+ const lastEnrolled = new Date();
+ lastEnrolled.setDate(lastEnrolled.getDate() - index);
const iteratedData = [
{
provider: providers.edx,
- course: { title, bannerUrl: logos.edx },
+ course: { title, bannerUrl: logos.edx, courseNumber },
relatedPrograms,
},
{
provider: providers.mit,
- course: { title, bannerUrl: logos.science },
+ course: { title, bannerUrl: logos.science, courseNumber },
relatedPrograms: [relatedPrograms[0]],
},
{
provider: null,
- course: { title, bannerUrl: logos.social },
+ course: { title, bannerUrl: logos.social, courseNumber },
relatedPrograms: [],
},
];
return {
+ cardId,
+ grades: { isPassing: true },
+ entitlements: null,
...data,
- courseRun: genCourseRunData({ ...data.courseRun, courseNumber }),
+ certificates: genCertificateData(data.certificates),
+ enrollment: genEnrollmentData(data.enrollment),
+ courseRun: genCourseRunData({
+ ...data.courseRun,
+ courseId,
+ lastEnrolled,
+ }),
...iteratedData[providerIndex],
};
},
@@ -436,31 +459,44 @@ export const courseRunData = courseRuns.map(
export const entitlementData = entitlementCourses.map(
(data, index) => {
const title = genCourseTitle(100 + index);
- const courseNumber = genCourseID(100 + index);
+ const cardId = genCardId(100 + index);
+ const courseNumber = genCourseNumber(100 + index);
const providerIndex = index % 3;
const iteratedData = [
{
provider: providers.edx,
- course: { title, bannerUrl: logos.edx },
+ course: { courseNumber, title, bannerUrl: logos.edx },
relatedPrograms,
},
{
provider: providers.mit,
- course: { title, bannerUrl: logos.science },
+ course: { courseNumber, title, bannerUrl: logos.science },
relatedPrograms: [relatedPrograms[0]],
},
{
provider: null,
- course: { title, bannerUrl: logos.social },
+ course: { courseNumber, title, bannerUrl: logos.social },
relatedPrograms: [],
},
];
return {
+ cardId,
+ enrollment: genEnrollmentData({
+ isEnrolled: false,
+ lastEnrolled: null,
+ accessExpirationDate: null,
+ canUpgrade: false,
+ hasFinished: false,
+ hasStarted: false,
+ isAudit: false,
+ isAuditAccessExpired: false,
+ isEmailEnabled: false,
+ isVerified: false,
+ }),
+ grades: null,
+ certificates: null,
+ courseRun: null,
...data,
- enrollment: genEnrollmentData(),
- grades: { isPassing: true },
- certificates: genCertificateData(),
- courseRun: genCourseRunData({ ...data.courseRun, courseNumber }),
...iteratedData[providerIndex],
};
},
diff --git a/src/data/services/lms/shapes.js b/src/data/services/lms/shapes.js
index 5df0455..d4d76f3 100644
--- a/src/data/services/lms/shapes.js
+++ b/src/data/services/lms/shapes.js
@@ -14,7 +14,7 @@ export const shapes = StrictDict({
}),
courseRun: PropTypes.shape({
accessExpirationDate: PropTypes.string,
- courseNumber: PropTypes.string,
+ cardId: PropTypes.string,
isArchived: PropTypes.bool,
isFinished: PropTypes.bool,
isPending: PropTypes.bool,
@@ -43,7 +43,7 @@ export const shapes = StrictDict({
availableSessions: PropTypes.shape({
startDate: PropTypes.string,
endDate: PropTypes.string,
- courseNumber: PropTypes.string,
+ cardId: PropTypes.string,
}),
canChange: PropTypes.bool,
isFulfilled: PropTypes.bool,
diff --git a/src/setupTest.jsx b/src/setupTest.jsx
index a092edf..d882fb6 100755
--- a/src/setupTest.jsx
+++ b/src/setupTest.jsx
@@ -125,6 +125,7 @@ jest.mock('@edx/paragon/icons', () => ({
}));
jest.mock('data/constants/app', () => ({
+ ...jest.requireActual('data/constants/app'),
locationId: 'fake-location-id',
}));
diff --git a/src/test/app.test.jsx b/src/test/app.test.jsx
index e12f513..4b48031 100644
--- a/src/test/app.test.jsx
+++ b/src/test/app.test.jsx
@@ -20,6 +20,7 @@ import { RequestKeys, RequestStates } from 'data/constants/requests';
import reducers from 'data/redux';
import messages from 'i18n';
import { selectors } from 'data/redux';
+import { cardId as genCardId } from 'data/redux/app/reducer';
import App from 'App';
import Inspector from './inspector';
@@ -79,8 +80,10 @@ const mockApi = () => {
(resolve, reject) => {
resolveFns.init = {
success: () => resolve({
- enrollments: fakeData.courseRunData,
- entitlements: fakeData.entitlementData,
+ courses: [
+ ...fakeData.courseRunData,
+ ...fakeData.entitlementData,
+ ],
...fakeData.globalData,
}),
};
@@ -130,27 +133,28 @@ describe('ESG app integration tests', () => {
await inspector.findByText(fakeData.courseRunData[0].course.title);
const cards = inspector.get.courseCards;
- let card = cards.at(0);
- let courseNumber;
+ let cardId;
let courseData;
let cardDetails;
await getState();
// Card 1 is Audit, pending, and can upgrade
- courseNumber = state.app.enrollments[0];
- courseData = state.app.courseData[courseNumber];
+ cardId = genCardId(0);
+ courseData = state.app.courseData[cardId];
expect(courseData.enrollment.isAudit).toEqual(true);
- expect(courseData.courseRun.isPending).toEqual(true);
+ expect(courseData.courseRun.isStarted).toEqual(false);
expect(courseData.enrollment.canUpgrade).toEqual(true);
+
+ let card = cards.at(0);
+
inspector.verifyText(
inspector.get.card.header(card),
courseData.course.title,
);
cardDetails = inspector.get.card.details(card);
-
[
courseData.provider.name,
- courseNumber,
+ courseData.course.courseNumber,
appMessages.withValues.CourseCardDetails.accessExpires({
accessExpirationDate: courseData.enrollment.accessExpirationDate,
}),
diff --git a/src/test/inspector.js b/src/test/inspector.js
index 3f68e96..c04bf5e 100644
--- a/src/test/inspector.js
+++ b/src/test/inspector.js
@@ -27,9 +27,9 @@ class Inspector {
card: {
header: (card) => within(card).getByTestId('CourseCardTitle'),
details: (card) => within(card).getByTestId('CourseCardDetails'),
- banners: (card) => within(card).getByTestId('CourseCardBanners'),
- programsBadge: (card) => within(card).getByTestId('RelatedProgramsBadge'),
- actions: (card) => within(card).getByTestId('CourseCardActions'),
+ // banners: (card) => within(card).getByTestId('CourseCardBanners'),
+ // programsBadge: (card) => within(card).getByTestId('RelatedProgramsBadge'),
+ // actions: (card) => within(card).getByTestId('CourseCardActions'),
},
};
}