Skip to content

Commit c53d2a1

Browse files
committed
feat: display multiple dashboards and some style fixes
1 parent dd47215 commit c53d2a1

8 files changed

Lines changed: 201 additions & 19 deletions

File tree

src/DesktopHeader.jsx

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,7 @@ import NotificationsSlot from './plugin-slots/NotificationsSlot';
99
import UserMenuGroupItemSlot from './plugin-slots/UserMenuGroupItemSlot';
1010
import UserMenuGroupSlot from './plugin-slots/UserMenuGroupSlot';
1111
import UserMenuItem from './common/UserMenuItem';
12+
import UserDashboardMenu from './common/UserDashboardMenu';
1213
import { Menu, MenuTrigger, MenuContent } from './Menu';
1314
import { LinkedLogo, Logo } from './Logo';
1415

@@ -83,6 +84,9 @@ class DesktopHeader extends React.Component {
8384
name,
8485
email,
8586
intl,
87+
hasEnterpriseAccount,
88+
logoAltText,
89+
logoDestination,
8690
} = this.props;
8791

8892
return (
@@ -108,6 +112,11 @@ class DesktopHeader extends React.Component {
108112
</Dropdown.Item>
109113
)}
110114
<UserMenuGroupSlot />
115+
<UserDashboardMenu
116+
hasEnterpriseAccount={hasEnterpriseAccount}
117+
enterpriseOrg={logoAltText}
118+
enterpriseSrc={logoDestination}
119+
/>
111120
{userMenu.map((group, index) => (
112121
// eslint-disable-next-line react/jsx-no-comment-textnodes,react/no-array-index-key
113122
<React.Fragment key={index}>
@@ -221,6 +230,7 @@ DesktopHeader.propTypes = {
221230
avatar: PropTypes.string,
222231
name: PropTypes.string,
223232
email: PropTypes.string,
233+
hasEnterpriseAccount: PropTypes.bool,
224234
loggedIn: PropTypes.bool,
225235
notificationAppData: PropTypes.shape({
226236
apps: PropTypes.objectOf(
@@ -251,6 +261,7 @@ DesktopHeader.defaultProps = {
251261
name: '',
252262
email: '',
253263
loggedIn: false,
264+
hasEnterpriseAccount: false,
254265
notificationAppData: {
255266
apps: {},
256267
tabsCount: {},

src/Header.jsx

Lines changed: 32 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -11,7 +11,8 @@ import {
1111
getConfig,
1212
subscribe,
1313
} from '@edx/frontend-platform';
14-
import { useEnterpriseConfig } from '@edx/frontend-enterprise-utils';
14+
import { useEnterpriseConfig, isEnterpriseUser } from '@edx/frontend-enterprise-utils';
15+
import './index.scss';
1516

1617
import PropTypes from 'prop-types';
1718
import DesktopHeader from './DesktopHeader';
@@ -35,6 +36,7 @@ subscribe(APP_CONFIG_INITIALIZED, () => {
3536
mergeConfig({
3637
MINIMAL_HEADER: !!process.env.MINIMAL_HEADER,
3738
ENTERPRISE_LEARNER_PORTAL_HOSTNAME: process.env.ENTERPRISE_LEARNER_PORTAL_HOSTNAME,
39+
ENABLE_EDX_PERSONAL_DASHBOARD: process.env.ENABLE_EDX_PERSONAL_DASHBOARD,
3840
AUTHN_MINIMAL_HEADER: !!process.env.AUTHN_MINIMAL_HEADER,
3941
ACCOUNT_SETTINGS_URL: process.env.ACCOUNT_SETTINGS_URL,
4042
NOTIFICATION_FEEDBACK_URL: process.env.NOTIFICATION_FEEDBACK_URL,
@@ -64,12 +66,14 @@ const Header = ({
6466
enterpriseLearnerPortalLink,
6567
enterpriseCustomerBrandingConfig,
6668
} = useEnterpriseConfig(authenticatedUser, config.ENTERPRISE_LEARNER_PORTAL_HOSTNAME, config.LMS_BASE_URL);
69+
const hasEnterpriseAccount = isEnterpriseUser(authenticatedUser);
6770

6871
const defaultMainMenu = [
6972
{
7073
type: 'item',
7174
href: `${config.LMS_BASE_URL}/dashboard`,
7275
content: intl.formatMessage(messages['header.links.courses']),
76+
isActive: document.title.includes('Learner Home'),
7377
},
7478
{
7579
type: 'item',
@@ -89,11 +93,11 @@ const Header = ({
8993
},
9094
];
9195

92-
const dashboardMenuItem = {
93-
type: 'item',
94-
href: `${config.LMS_BASE_URL}/dashboard`,
95-
content: intl.formatMessage(messages['header.user.menu.dashboard']),
96-
};
96+
// const dashboardMenuItem = {
97+
// type: 'item',
98+
// href: `${config.LMS_BASE_URL}/dashboard`,
99+
// content: intl.formatMessage(messages['header.user.menu.dashboard']),
100+
// };
97101

98102
const logoutMenuItem = {
99103
type: 'item',
@@ -102,19 +106,27 @@ const Header = ({
102106
};
103107

104108
// If there is an Enterprise LP link, use that instead of the B2C Dashboard
105-
let baseUserMenuDashboardLinks = [];
106-
if (enterpriseLearnerPortalLink) {
107-
baseUserMenuDashboardLinks = [enterpriseLearnerPortalLink];
108-
} else {
109-
baseUserMenuDashboardLinks = [dashboardMenuItem];
110-
}
109+
// let baseUserMenuDashboardLinks = [];
110+
// if (enterpriseLearnerPortalLink) {
111+
// baseUserMenuDashboardLinks = [enterpriseLearnerPortalLink];
112+
// } else {
113+
// baseUserMenuDashboardLinks = [dashboardMenuItem];
114+
// }
115+
116+
const defaultSecondaryMenu = [
117+
...(getConfig().SUPPORT_URL ? [{
118+
type: 'item',
119+
href: `${getConfig().SUPPORT_URL}`,
120+
content: intl.formatMessage(messages['header.links.help']),
121+
}] : []),
122+
];
111123

112124
const careerItemContent = <>{intl.formatMessage(messages['header.user.menu.career'])}<Badge className="px-2 mx-2" variant="warning">{intl.formatMessage(messages['header.user.menu.newAlert'])}</Badge></>;
113125
const defaultUserMenu = authenticatedUser === null ? [] : [{
114126
heading: '',
115127
items: [
116-
...baseUserMenuDashboardLinks,
117-
{
128+
// ...baseUserMenuDashboardLinks,
129+
...(!enterpriseLearnerPortalLink ? [{
118130
type: 'item',
119131
href: 'https://careers.edx.org/',
120132
content: careerItemContent,
@@ -124,16 +136,18 @@ const Header = ({
124136
{ category: 'header', label: 'header' },
125137
);
126138
},
127-
},
139+
}] : []),
128140
{
129141
type: 'item',
130142
href: `${config.ACCOUNT_PROFILE_URL}/u/${authenticatedUser.username}`,
131143
content: intl.formatMessage(messages['header.user.menu.profile']),
144+
isActive: document.title.includes('Profile'),
132145
},
133146
{
134147
type: 'item',
135148
href: config.ACCOUNT_SETTINGS_URL,
136149
content: intl.formatMessage(messages['header.user.menu.account.settings']),
150+
isActive: document.title.includes('Account'),
137151
},
138152
// Users should only see Order History if they do not have an available
139153
// learner portal and have a ORDER_HISTORY_URL define in the environment,
@@ -150,14 +164,14 @@ const Header = ({
150164
}];
151165

152166
const mainMenu = mainMenuItems || defaultMainMenu;
153-
const secondaryMenu = secondaryMenuItems || [];
167+
const secondaryMenu = secondaryMenuItems || defaultSecondaryMenu;
154168
let userMenu = authenticatedUser === null ? [] : userMenuItems || defaultUserMenu;
155169

156170
if (getConfig().MINIMAL_HEADER && authenticatedUser !== null) {
157171
userMenu = [{
158172
heading: '',
159173
items: [
160-
dashboardMenuItem,
174+
// dashboardMenuItem,
161175
logoutMenuItem,
162176
],
163177
}];
@@ -190,6 +204,7 @@ const Header = ({
190204
userMenu: getConfig().AUTHN_MINIMAL_HEADER ? [] : userMenu,
191205
loggedOutItems: getConfig().AUTHN_MINIMAL_HEADER ? [] : loggedOutItems,
192206
studioBaseUrl: config.STUDIO_BASE_URL,
207+
hasEnterpriseAccount,
193208
};
194209

195210
if (enterpriseCustomerBrandingConfig) {

src/Header.messages.jsx

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,11 @@ const messages = defineMessages({
2121
defaultMessage: 'Schools & Partners',
2222
description: 'Link to the schools and partners landing page',
2323
},
24+
'header.links.help': {
25+
id: 'header.links.help',
26+
defaultMessage: 'Help',
27+
description: 'The text for the link to the Help Center',
28+
},
2429
'header.user.menu.career': {
2530
id: 'header.user.menu.career',
2631
defaultMessage: 'Career',

src/MobileHeader.jsx

Lines changed: 19 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,7 @@ import { AvatarButton } from '@openedx/paragon';
88
import NotificationsSlot from './plugin-slots/NotificationsSlot';
99
import UserMenuGroupSlot from './plugin-slots/UserMenuGroupSlot';
1010
import UserMenuGroupItemSlot from './plugin-slots/UserMenuGroupItemSlot';
11+
import UserDashboardMenu from './common/UserDashboardMenu';
1112
import { Menu, MenuTrigger, MenuContent } from './Menu';
1213
import { LinkedLogo, Logo } from './Logo';
1314
import UserMenuItem from './common/UserMenuItem';
@@ -77,7 +78,14 @@ class MobileHeader extends React.Component {
7778
}
7879

7980
renderUserMenuItems() {
80-
const { userMenu, name, email } = this.props;
81+
const {
82+
userMenu,
83+
name,
84+
email,
85+
hasEnterpriseAccount,
86+
logoAltText,
87+
logoDestination,
88+
} = this.props;
8189
const userInfoItem = (name || email) ? (
8290
<li className="nav-item user-info__menu-item" key="user-info">
8391
<UserMenuItem name={name} email={email} />
@@ -102,9 +110,16 @@ class MobileHeader extends React.Component {
102110

103111
const userMenuGroupSlot = <UserMenuGroupSlot />;
104112
const userMenuGroupItemSlot = <UserMenuGroupItemSlot />;
113+
const userDashboardMenu = (
114+
<UserDashboardMenu
115+
hasEnterpriseAccount={hasEnterpriseAccount}
116+
enterpriseOrg={logoAltText}
117+
enterpriseSrc={logoDestination}
118+
/>
119+
);
105120

106121
return userInfoItem
107-
? [userInfoItem, userMenuGroupSlot, userMenuGroupItemSlot, ...userMenuItems]
122+
? [userInfoItem, userDashboardMenu, userMenuGroupSlot, userMenuGroupItemSlot, ...userMenuItems]
108123
: [userMenuGroupSlot, userMenuGroupItemSlot, ...userMenuItems];
109124
}
110125

@@ -227,6 +242,7 @@ MobileHeader.propTypes = {
227242
avatar: PropTypes.string,
228243
name: PropTypes.string,
229244
email: PropTypes.string,
245+
hasEnterpriseAccount: PropTypes.bool,
230246
loggedIn: PropTypes.bool,
231247
stickyOnMobile: PropTypes.bool,
232248
notificationAppData: PropTypes.shape({
@@ -257,6 +273,7 @@ MobileHeader.defaultProps = {
257273
avatar: null,
258274
name: '',
259275
email: '',
276+
hasEnterpriseAccount: false,
260277
loggedIn: false,
261278
stickyOnMobile: true,
262279
notificationAppData: {
Lines changed: 65 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,65 @@
1+
import React from 'react';
2+
import PropTypes from 'prop-types';
3+
import { getConfig } from '@edx/frontend-platform';
4+
import { useIntl } from '@edx/frontend-platform/i18n';
5+
import {
6+
breakpoints, Dropdown, useWindowSize,
7+
} from '@openedx/paragon';
8+
9+
import messages from './messages';
10+
11+
const UserDashboardMenu = ({ enterpriseOrg, enterpriseSrc, hasEnterpriseAccount }) => {
12+
const { formatMessage } = useIntl();
13+
const { width } = useWindowSize();
14+
const isMobile = width <= breakpoints.small.maxWidth;
15+
16+
if (getConfig().ENABLE_EDX_PERSONAL_DASHBOARD) {
17+
if (!isMobile) {
18+
return (
19+
<>
20+
{ hasEnterpriseAccount && <Dropdown.Header>{formatMessage(messages.dashboardSwitch)}</Dropdown.Header> }
21+
<Dropdown.Item as="a" href="/edx-dashboard" className={document.title === 'Learner Home' ? 'active' : ''} key="item-dashboard">
22+
{formatMessage(hasEnterpriseAccount ? messages.dashboardPersonal : messages.dashboard)}
23+
</Dropdown.Item>
24+
{ hasEnterpriseAccount && (
25+
<Dropdown.Item as="a" href={enterpriseSrc} key={enterpriseOrg}>
26+
{enterpriseOrg} {formatMessage(messages.dashboard)}
27+
</Dropdown.Item>
28+
)}
29+
{hasEnterpriseAccount && <Dropdown.Divider />}
30+
</>
31+
);
32+
}
33+
return (
34+
<>
35+
<li className="nav-item" key="item-dashboard">
36+
<a href="/edx-dashboard" className={`nav-link ${document.title === 'Learner Home' ? 'active' : ''})`}>
37+
{formatMessage(messages.dashboardPersonal)}
38+
</a>
39+
</li>
40+
{hasEnterpriseAccount && (
41+
<li className="nav-item" key={enterpriseOrg}>
42+
<a href={enterpriseSrc} className="nav-link">
43+
{enterpriseOrg} {formatMessage(messages.dashboard)}
44+
</a>
45+
</li>
46+
)}
47+
</>
48+
);
49+
}
50+
return null;
51+
};
52+
53+
UserDashboardMenu.defaultProps = {
54+
enterpriseOrg: null,
55+
enterpriseSrc: null,
56+
hasEnterpriseAccount: false,
57+
};
58+
59+
UserDashboardMenu.propTypes = {
60+
enterpriseOrg: PropTypes.string,
61+
enterpriseSrc: PropTypes.string,
62+
hasEnterpriseAccount: PropTypes.bool,
63+
};
64+
65+
export default UserDashboardMenu;
Lines changed: 45 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,45 @@
1+
import React from 'react';
2+
import { mergeConfig } from '@edx/frontend-platform';
3+
import { injectIntl, IntlProvider } from '@edx/frontend-platform/i18n';
4+
import {
5+
screen,
6+
render,
7+
} from '@testing-library/react';
8+
import { useEnterpriseConfig } from '@edx/frontend-enterprise-utils';
9+
10+
import UserDashboardMenu from './index'; // eslint-disable-line
11+
12+
jest.mock('@edx/frontend-enterprise-utils');
13+
mergeConfig({ ENABLE_EDX_PERSONAL_DASHBOARD: true });
14+
const IntlUserDashboardMenu = injectIntl(UserDashboardMenu);
15+
16+
describe('User Dashboard Menu', () => {
17+
const Wrapper = children => (
18+
// eslint-disable-next-line react/jsx-filename-extension
19+
<IntlProvider locale="en">
20+
{children}
21+
</IntlProvider>
22+
);
23+
24+
it('should render dashboard menu in the user menu dropdown', () => {
25+
useEnterpriseConfig.mockReturnValue({
26+
enterpriseLearnerPortalLink: {
27+
type: 'item',
28+
href: 'http://localhost:8000',
29+
content: 'Dashboard',
30+
},
31+
enterpriseCustomerBrandingConfig: {
32+
logoAltText: 'fake-enterprise-name',
33+
logoDestination: 'http://fake.url',
34+
logo: 'http://fake-logo.url',
35+
},
36+
});
37+
38+
render(Wrapper(<IntlUserDashboardMenu />));
39+
40+
const dashboardSwitchMenu = screen.getByText('SWITCH DASHBOARD');
41+
const dashboardMenu = screen.getByText('Personal');
42+
expect(dashboardMenu).toBeTruthy();
43+
expect(dashboardSwitchMenu).toBeTruthy();
44+
});
45+
});
Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,21 @@
1+
import { defineMessages } from '@edx/frontend-platform/i18n';
2+
3+
const messages = defineMessages({
4+
dashboard: {
5+
id: 'learnerVariantDashboard.menu.dashboard.label',
6+
defaultMessage: 'Dashboard',
7+
description: 'The text for the user menu Dashboard navigation link.',
8+
},
9+
dashboardPersonal: {
10+
id: 'learnerVariantDashboard.menu.dashboardPersonal.label',
11+
defaultMessage: 'Personal',
12+
description: 'Link to personal dashboard in user menu',
13+
},
14+
dashboardSwitch: {
15+
id: 'learnerVariantDashboard.menu.dashboardSwitch.label',
16+
defaultMessage: 'SWITCH DASHBOARD',
17+
description: 'Switch Dashboard header in the user menu',
18+
},
19+
});
20+
21+
export default messages;

src/index.scss

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,9 @@
11
$spacer: 1rem;
22
$blue: #007db8;
33
$white: #fff;
4+
$component-active-bg: #0A3055FF !default;
5+
$component-active-color: $white !default;
6+
$rounded-pill: 50rem !default;
47

58
@import './Menu/menu.scss';
69
@import './studio-header/StudioHeader.scss';

0 commit comments

Comments
 (0)