diff --git a/src/containers/LearnerDashboardHeader/AuthenticatedUserDropdown.jsx b/src/containers/LearnerDashboardHeader/AuthenticatedUserDropdown.jsx index 397dcce..004595d 100644 --- a/src/containers/LearnerDashboardHeader/AuthenticatedUserDropdown.jsx +++ b/src/containers/LearnerDashboardHeader/AuthenticatedUserDropdown.jsx @@ -2,50 +2,51 @@ import React from 'react'; import PropTypes from 'prop-types'; import { getConfig } from '@edx/frontend-platform'; -import { injectIntl, intlShape } from '@edx/frontend-platform/i18n'; +import { useIntl } from '@edx/frontend-platform/i18n'; import { Dropdown, Icon } from '@edx/paragon'; import { Person } from '@edx/paragon/icons'; import messages from './messages'; +import EnterpriseDashboard from './EnterpriseDashboard'; -export const AuthenticatedUserDropdown = ({ intl, username }) => ( - <> - - - - - {username} - - - - - {intl.formatMessage(messages.dashboard)} - {' '} - - {intl.formatMessage(messages.profile)} - - - {intl.formatMessage(messages.account)} - - {getConfig().ORDER_HISTORY_URL && ( - - {intl.formatMessage(messages.orderHistory)} +export const AuthenticatedUserDropdown = ({ username }) => { + const { formatMessage } = useIntl(); + return ( + <> + + + + + {username} + + + + + + {formatMessage(messages.profile)} - )} - - {intl.formatMessage(messages.help)} - - - {intl.formatMessage(messages.signOut)} - - - - -); + + {formatMessage(messages.account)} + + {getConfig().ORDER_HISTORY_URL && ( + + {formatMessage(messages.orderHistory)} + + )} + + {formatMessage(messages.help)} + + + {formatMessage(messages.signOut)} + + + + + ); +}; AuthenticatedUserDropdown.propTypes = { - intl: intlShape.isRequired, username: PropTypes.string.isRequired, }; -export default injectIntl(AuthenticatedUserDropdown); +export default AuthenticatedUserDropdown; diff --git a/src/containers/LearnerDashboardHeader/EnterpriseDashboard/__snapshots__/index.test.jsx.snap b/src/containers/LearnerDashboardHeader/EnterpriseDashboard/__snapshots__/index.test.jsx.snap new file mode 100644 index 0000000..66150b6 --- /dev/null +++ b/src/containers/LearnerDashboardHeader/EnterpriseDashboard/__snapshots__/index.test.jsx.snap @@ -0,0 +1,130 @@ +// 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 new file mode 100644 index 0000000..465c343 --- /dev/null +++ b/src/containers/LearnerDashboardHeader/EnterpriseDashboard/hooks.js @@ -0,0 +1,37 @@ +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 new file mode 100644 index 0000000..744d97e --- /dev/null +++ b/src/containers/LearnerDashboardHeader/EnterpriseDashboard/hooks.test.js @@ -0,0 +1,60 @@ +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 new file mode 100644 index 0000000..5252db0 --- /dev/null +++ b/src/containers/LearnerDashboardHeader/EnterpriseDashboard/index.jsx @@ -0,0 +1,73 @@ +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 new file mode 100644 index 0000000..74b9205 --- /dev/null +++ b/src/containers/LearnerDashboardHeader/EnterpriseDashboard/index.test.jsx @@ -0,0 +1,49 @@ +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/EnterpriseDashboard/messages.js b/src/containers/LearnerDashboardHeader/EnterpriseDashboard/messages.js new file mode 100644 index 0000000..8b9c118 --- /dev/null +++ b/src/containers/LearnerDashboardHeader/EnterpriseDashboard/messages.js @@ -0,0 +1,31 @@ +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', + description: 'title for enterpise dashboard dialog', + }, + enterpriseDialogBody: { + id: 'leanerDashboard.enterpriseDialogBody', + defaultMessage: 'To access the coureses available to you through {label}, visit the {label} dashboard now.', + description: 'Body text for enterpise dashboard dialog', + }, + enterpriseDialogDismissButton: { + id: 'leanerDashboard.enterpriseDialogDismissButton', + defaultMessage: 'Dismiss', + description: 'Dismiss button to cancel visiting dashboard', + }, + enterpriseDialogConfirmButton: { + id: 'leanerDashboard.enterpriseDialogConfirmButton', + defaultMessage: 'Go To Dashboard', + description: 'Confirm button to go to the dashboard url', + }, +}); + +export default messages; diff --git a/src/containers/LearnerDashboardHeader/messages.js b/src/containers/LearnerDashboardHeader/messages.js index 94d1c53..02e2700 100644 --- a/src/containers/LearnerDashboardHeader/messages.js +++ b/src/containers/LearnerDashboardHeader/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.', - }, help: { id: 'leanerDashboard.help.label', defaultMessage: 'Help', diff --git a/src/data/services/lms/fakeData/courses.js b/src/data/services/lms/fakeData/courses.js index 23d2ad4..63c79b6 100644 --- a/src/data/services/lms/fakeData/courses.js +++ b/src/data/services/lms/fakeData/courses.js @@ -59,10 +59,11 @@ const globalData = { }, enterpriseDashboards: { availableDashboards: [ - { label: 'edX', url: 'edx.org/edx-dashboard' }, - { label: 'harvard', url: 'edx.org/harvard-dashboard' }, + { label: 'Personal', url: '/dashboard' }, + { label: 'edX, Inc.', url: '/edx-dashboard' }, + { label: 'Harvard', url: '/harvard-dashboard' }, ], - mostRecentDashboard: { label: 'edX', url: 'edx.org/edx-dashboard' }, + mostRecentDashboard: { label: 'edX, Inc.', url: '/edx-dashboard' }, }, platformSettings: { supportEmail: 'support@example.com', diff --git a/src/testUtils.js b/src/testUtils.js index a076398..078c245 100644 --- a/src/testUtils.js +++ b/src/testUtils.js @@ -17,7 +17,7 @@ export const formatMessage = (msg, values) => { } Object.keys(values).forEach((key) => { // eslint-disable-next-line - message = message.replace(`{${key}}`, values[key]); + message = message.replaceAll(`{${key}}`, values[key]); }); return message; };