Leangseu edx/enterprise portal (#5)
* feat: implement enterprise dashboard * chore: update test
This commit is contained in:
@@ -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 }) => (
|
||||
<>
|
||||
<Dropdown className="user-dropdown">
|
||||
<Dropdown.Toggle id="user" variant="primary">
|
||||
<Icon src={Person} />
|
||||
<span data-hj-suppress className="d-none d-md-inline">
|
||||
{username}
|
||||
</span>
|
||||
</Dropdown.Toggle>
|
||||
<Dropdown.Menu className="dropdown-menu-right">
|
||||
<Dropdown.Item href={`${getConfig().LMS_BASE_URL}/dashboard`}>
|
||||
{intl.formatMessage(messages.dashboard)}
|
||||
</Dropdown.Item>{' '}
|
||||
<Dropdown.Item href={`${getConfig().LMS_BASE_URL}/u/${username}`}>
|
||||
{intl.formatMessage(messages.profile)}
|
||||
</Dropdown.Item>
|
||||
<Dropdown.Item href={`${getConfig().LMS_BASE_URL}/account/settings`}>
|
||||
{intl.formatMessage(messages.account)}
|
||||
</Dropdown.Item>
|
||||
{getConfig().ORDER_HISTORY_URL && (
|
||||
<Dropdown.Item href={getConfig().ORDER_HISTORY_URL}>
|
||||
{intl.formatMessage(messages.orderHistory)}
|
||||
export const AuthenticatedUserDropdown = ({ username }) => {
|
||||
const { formatMessage } = useIntl();
|
||||
return (
|
||||
<>
|
||||
<Dropdown className="user-dropdown">
|
||||
<Dropdown.Toggle id="user" variant="primary">
|
||||
<Icon src={Person} />
|
||||
<span data-hj-suppress className="d-none d-md-inline">
|
||||
{username}
|
||||
</span>
|
||||
</Dropdown.Toggle>
|
||||
<Dropdown.Menu className="dropdown-menu-right">
|
||||
<EnterpriseDashboard />
|
||||
<Dropdown.Item href={`${getConfig().LMS_BASE_URL}/u/${username}`}>
|
||||
{formatMessage(messages.profile)}
|
||||
</Dropdown.Item>
|
||||
)}
|
||||
<Dropdown.Item href={getConfig().SUPPORT_URL}>
|
||||
{intl.formatMessage(messages.help)}
|
||||
</Dropdown.Item>
|
||||
<Dropdown.Item href={getConfig().LOGOUT_URL}>
|
||||
{intl.formatMessage(messages.signOut)}
|
||||
</Dropdown.Item>
|
||||
</Dropdown.Menu>
|
||||
</Dropdown>
|
||||
</>
|
||||
);
|
||||
<Dropdown.Item href={`${getConfig().LMS_BASE_URL}/account/settings`}>
|
||||
{formatMessage(messages.account)}
|
||||
</Dropdown.Item>
|
||||
{getConfig().ORDER_HISTORY_URL && (
|
||||
<Dropdown.Item href={getConfig().ORDER_HISTORY_URL}>
|
||||
{formatMessage(messages.orderHistory)}
|
||||
</Dropdown.Item>
|
||||
)}
|
||||
<Dropdown.Item href={getConfig().SUPPORT_URL}>
|
||||
{formatMessage(messages.help)}
|
||||
</Dropdown.Item>
|
||||
<Dropdown.Item href={getConfig().LOGOUT_URL}>
|
||||
{formatMessage(messages.signOut)}
|
||||
</Dropdown.Item>
|
||||
</Dropdown.Menu>
|
||||
</Dropdown>
|
||||
</>
|
||||
);
|
||||
};
|
||||
|
||||
AuthenticatedUserDropdown.propTypes = {
|
||||
intl: intlShape.isRequired,
|
||||
username: PropTypes.string.isRequired,
|
||||
};
|
||||
|
||||
export default injectIntl(AuthenticatedUserDropdown);
|
||||
export default AuthenticatedUserDropdown;
|
||||
|
||||
@@ -0,0 +1,130 @@
|
||||
// Jest Snapshot v1, https://goo.gl/fbAQLP
|
||||
|
||||
exports[`EnterpriseDashboard snapshot initilized 1`] = `
|
||||
<Fragment>
|
||||
<Dropdown.Item
|
||||
active={false}
|
||||
key="Personal"
|
||||
>
|
||||
Personal
|
||||
|
||||
Dashboard
|
||||
</Dropdown.Item>
|
||||
<Dropdown.Item
|
||||
active={true}
|
||||
key="edX, Inc."
|
||||
>
|
||||
edX, Inc.
|
||||
|
||||
Dashboard
|
||||
</Dropdown.Item>
|
||||
<Dropdown.Item
|
||||
active={false}
|
||||
key="Harvard"
|
||||
>
|
||||
Harvard
|
||||
|
||||
Dashboard
|
||||
</Dropdown.Item>
|
||||
<ModalDialog
|
||||
hasCloseButton={false}
|
||||
isOpen={false}
|
||||
onClose={[MockFunction hooks.nullMethod]}
|
||||
title=""
|
||||
>
|
||||
<div
|
||||
className="bg-white p-3 rounded shadow"
|
||||
style={
|
||||
Object {
|
||||
"textAlign": "start",
|
||||
}
|
||||
}
|
||||
>
|
||||
<h4>
|
||||
You have access to the undefined dashboard
|
||||
</h4>
|
||||
<p>
|
||||
To access the coureses available to you through undefined, visit the undefined dashboard now.
|
||||
</p>
|
||||
<ActionRow>
|
||||
<Button
|
||||
onClick={[MockFunction cancelSelectDashboardItem]}
|
||||
variant="tertiary"
|
||||
>
|
||||
Dismiss
|
||||
</Button>
|
||||
<Button
|
||||
type="a"
|
||||
>
|
||||
Go To Dashboard
|
||||
</Button>
|
||||
</ActionRow>
|
||||
</div>
|
||||
</ModalDialog>
|
||||
</Fragment>
|
||||
`;
|
||||
|
||||
exports[`EnterpriseDashboard snapshot select item and open modal 1`] = `
|
||||
<Fragment>
|
||||
<Dropdown.Item
|
||||
active={false}
|
||||
key="Personal"
|
||||
>
|
||||
Personal
|
||||
|
||||
Dashboard
|
||||
</Dropdown.Item>
|
||||
<Dropdown.Item
|
||||
active={true}
|
||||
key="edX, Inc."
|
||||
>
|
||||
edX, Inc.
|
||||
|
||||
Dashboard
|
||||
</Dropdown.Item>
|
||||
<Dropdown.Item
|
||||
active={false}
|
||||
key="Harvard"
|
||||
>
|
||||
Harvard
|
||||
|
||||
Dashboard
|
||||
</Dropdown.Item>
|
||||
<ModalDialog
|
||||
hasCloseButton={false}
|
||||
isOpen={true}
|
||||
onClose={[MockFunction hooks.nullMethod]}
|
||||
title=""
|
||||
>
|
||||
<div
|
||||
className="bg-white p-3 rounded shadow"
|
||||
style={
|
||||
Object {
|
||||
"textAlign": "start",
|
||||
}
|
||||
}
|
||||
>
|
||||
<h4>
|
||||
You have access to the Personal dashboard
|
||||
</h4>
|
||||
<p>
|
||||
To access the coureses available to you through Personal, visit the Personal dashboard now.
|
||||
</p>
|
||||
<ActionRow>
|
||||
<Button
|
||||
onClick={[MockFunction cancelSelectDashboardItem]}
|
||||
variant="tertiary"
|
||||
>
|
||||
Dismiss
|
||||
</Button>
|
||||
<Button
|
||||
href="/dashboard"
|
||||
type="a"
|
||||
>
|
||||
Go To Dashboard
|
||||
</Button>
|
||||
</ActionRow>
|
||||
</div>
|
||||
</ModalDialog>
|
||||
</Fragment>
|
||||
`;
|
||||
@@ -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;
|
||||
@@ -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);
|
||||
});
|
||||
});
|
||||
});
|
||||
@@ -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) => (
|
||||
<Dropdown.Item
|
||||
onClick={beginSelectDashboardItem(dashboard)}
|
||||
active={dashboard.label === mostRecentDashboard.label}
|
||||
key={dashboard.label}
|
||||
>
|
||||
{dashboard.label} {formatMessage(messages.dashboard)}
|
||||
</Dropdown.Item>
|
||||
))}
|
||||
<ModalDialog
|
||||
isOpen={showDialog}
|
||||
onClose={nullMethod}
|
||||
hasCloseButton={false}
|
||||
title=""
|
||||
>
|
||||
<div
|
||||
className="bg-white p-3 rounded shadow"
|
||||
style={{ textAlign: 'start' }}
|
||||
>
|
||||
<h4>
|
||||
{formatMessage(messages.enterpriseDialogHeader, {
|
||||
label: selectedItem.label,
|
||||
})}
|
||||
</h4>
|
||||
<p>
|
||||
{formatMessage(messages.enterpriseDialogBody, {
|
||||
label: selectedItem.label,
|
||||
})}
|
||||
</p>
|
||||
<ActionRow>
|
||||
<Button variant="tertiary" onClick={cancelSelectDashboardItem}>
|
||||
{formatMessage(messages.enterpriseDialogDismissButton)}
|
||||
</Button>
|
||||
<Button type="a" href={selectedItem.url}>
|
||||
{formatMessage(messages.enterpriseDialogConfirmButton)}
|
||||
</Button>
|
||||
</ActionRow>
|
||||
</div>
|
||||
</ModalDialog>
|
||||
</>
|
||||
);
|
||||
};
|
||||
|
||||
EnterpriseDashboard.propTypes = {};
|
||||
|
||||
export default EnterpriseDashboard;
|
||||
@@ -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(<EnterpriseDashboard />);
|
||||
expect(el).toMatchSnapshot();
|
||||
});
|
||||
|
||||
test('select item and open modal', () => {
|
||||
useEnterpriseDashboardHook.mockReturnValueOnce({
|
||||
...hookReturn,
|
||||
selectedItem: enterpriseDashboardData.availableDashboards[0],
|
||||
showDialog: true,
|
||||
});
|
||||
const el = shallow(<EnterpriseDashboard />);
|
||||
expect(el).toMatchSnapshot();
|
||||
});
|
||||
});
|
||||
});
|
||||
@@ -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;
|
||||
@@ -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',
|
||||
|
||||
@@ -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',
|
||||
|
||||
@@ -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;
|
||||
};
|
||||
|
||||
Reference in New Issue
Block a user