Skip to content

Commit

Permalink
feat: ent-3273
Browse files Browse the repository at this point in the history
Only render Order History in the header user menu if the user has no available learner portal,
and is thus not accessing content via a subscription.  Also, only display 0 or 1 learner portal
dashboard links, and give it the simple title "Dashboard".
  • Loading branch information
iloveagent57 committed Aug 7, 2020
1 parent 6266908 commit c20c618
Show file tree
Hide file tree
Showing 5 changed files with 139 additions and 35 deletions.
1 change: 1 addition & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -6,3 +6,4 @@ node_modules
temp
src/i18n/transifex_input.json
.vscode/
*~
3 changes: 3 additions & 0 deletions Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,9 @@ build:
requirements:
npm install

test:
npm run test

i18n.extract:
# Pulling display strings from .jsx files into .json files...
rm -rf $(transifex_temp)
Expand Down
60 changes: 25 additions & 35 deletions src/Header.jsx
Original file line number Diff line number Diff line change
@@ -1,9 +1,8 @@
import React, { useContext, useEffect, useState } from 'react';
import React, { useContext } from 'react';
import Responsive from 'react-responsive';
import { getLearnerPortalLinks, getSelectedEnterpriseUUID } from '@edx/frontend-enterprise';
import { injectIntl, intlShape } from '@edx/frontend-platform/i18n';
import { sendTrackEvent } from '@edx/frontend-platform/analytics';
import { getAuthenticatedHttpClient } from '@edx/frontend-platform/auth';

import { AppContext } from '@edx/frontend-platform/react';
import {
APP_CONFIG_INITIALIZED,
Expand All @@ -20,6 +19,8 @@ import LogoSVG from './logo.svg';

import messages from './Header.messages';

import useEnterpriseConfig from './data/hooks/enterprise';

ensureConfig([
'LMS_BASE_URL',
'LOGOUT_URL',
Expand All @@ -36,30 +37,10 @@ subscribe(APP_CONFIG_INITIALIZED, () => {

function Header({ intl }) {
const { authenticatedUser, config } = useContext(AppContext);
const [enterpriseLearnerPortalLinks, setEnterpriseLearnerPortalLinks] = useState([]);
const [enterpriseCustomerBrandingConfig, setEnterpriseCustomerBrandingConfig] = useState(null);
useEffect(() => {
const httpClient = getAuthenticatedHttpClient();
getLearnerPortalLinks(httpClient, authenticatedUser).then((learnerPortalLinks) => {
const preferredUUID = getSelectedEnterpriseUUID(authenticatedUser);
const preferredLearnerPortalLink = learnerPortalLinks.find(learnerPortalLink =>
learnerPortalLink.uuid === preferredUUID);
if (preferredLearnerPortalLink) {
setEnterpriseCustomerBrandingConfig({
logo: preferredLearnerPortalLink.branding_configuration.logo,
logoAltText: preferredLearnerPortalLink.title,
logoDestination: preferredLearnerPortalLink.url,
});
}

const links = learnerPortalLinks.map(({ url, title }) => ({
type: 'item',
href: url,
content: `${title} Dashboard`,
}));
setEnterpriseLearnerPortalLinks(links);
});
}, []);
const {
enterpriseLearnerPortalLink,
enterpriseCustomerBrandingConfig,
} = useEnterpriseConfig(authenticatedUser);

const mainMenu = [
{
Expand Down Expand Up @@ -97,10 +78,16 @@ function Header({ intl }) {
content: intl.formatMessage(messages['header.user.menu.logout']),
};

// If there are Enterprise LP links, use those instead of the B2C Dashboard
const orderHistoryItem = {
type: 'item',
href: config.ORDER_HISTORY_URL,
content: intl.formatMessage(messages['header.user.menu.order.history']),
};

// If there is an Enterprise LP link, use that instead of the B2C Dashboard
let baseUserMenuDashboardLinks = [];
if (enterpriseLearnerPortalLinks && enterpriseLearnerPortalLinks.length > 0) {
baseUserMenuDashboardLinks = [...enterpriseLearnerPortalLinks];
if (enterpriseLearnerPortalLink) {
baseUserMenuDashboardLinks = [enterpriseLearnerPortalLink];
} else {
baseUserMenuDashboardLinks = [dashboardMenuItem];
}
Expand All @@ -117,14 +104,17 @@ function Header({ intl }) {
href: `${config.LMS_BASE_URL}/account/settings`,
content: intl.formatMessage(messages['header.user.menu.account.settings']),
},
{
type: 'item',
href: config.ORDER_HISTORY_URL,
content: intl.formatMessage(messages['header.user.menu.order.history']),
},
logoutMenuItem,
];

// Users should only see Order History if they do not have an available
// learner portal, because an available learner portal currently means
// that they access content via Subscriptions, in which context an "order"
// is not relevant.
if (!enterpriseLearnerPortalLink) {
userMenu.splice(-1, 0, orderHistoryItem);
}

if (getConfig().MINIMAL_HEADER && authenticatedUser !== null) {
userMenu = [
dashboardMenuItem,
Expand Down
73 changes: 73 additions & 0 deletions src/Header.test.jsx
Original file line number Diff line number Diff line change
@@ -1,18 +1,38 @@

import React from 'react';
import { act } from 'react-dom/test-utils';
import { IntlProvider } from '@edx/frontend-platform/i18n';
import TestRenderer from 'react-test-renderer';
import { mount } from 'enzyme';
import { getLearnerPortalLinks } from '@edx/frontend-enterprise';
import { AppContext } from '@edx/frontend-platform/react';
import { getConfig } from '@edx/frontend-platform';
import { Context as ResponsiveContext } from 'react-responsive';

import Header from './index';

jest.mock('@edx/frontend-platform');
jest.mock('@edx/frontend-enterprise');

getConfig.mockReturnValue({});

describe('<Header />', () => {
beforeEach(() => {
getLearnerPortalLinks.mockReturnValue(Promise.resolve([]));
});

const mockLearnerPortalLinks = () => {
getLearnerPortalLinks.mockReturnValue(Promise.resolve([
{
url: 'http://localhost:8000',
title: 'My Enterprise',
branding_configuration: {
logo: 'my-logo',
},
},
]));
};

it('renders correctly for unauthenticated users on desktop', () => {
const component = (
<ResponsiveContext.Provider value={{ width: 1280 }}>
Expand Down Expand Up @@ -70,6 +90,59 @@ describe('<Header />', () => {
expect(wrapper.toJSON()).toMatchSnapshot();
});


it('renders correctly for authenticated users on desktop with or without learner portal links', async () => {
const component = (
<ResponsiveContext.Provider value={{ width: 1280 }}>
<IntlProvider locale="en" messages={{}}>
<AppContext.Provider value={{
authenticatedUser: {
userId: 'abc123',
username: 'edX',
roles: [],
administrator: false,
},
config: {
LMS_BASE_URL: process.env.LMS_BASE_URL,
LOGIN_URL: process.env.LOGIN_URL,
LOGOUT_URL: process.env.LOGOUT_URL,
MARKETING_SITE_BASE_URL: process.env.MARKETING_SITE_BASE_URL,
ORDER_HISTORY_URL: process.env.ORDER_HISTORY_URL,
},
}}
>
<Header />
</AppContext.Provider>
</IntlProvider>
</ResponsiveContext.Provider>
);

// When learner portal links are not present, Order History should be a dropdown item
let wrapper = mount(component);
const flushPromises = () => new Promise(setImmediate);
await act(async () => {
await flushPromises();
});
wrapper.update();
wrapper.find('.menu-trigger').simulate('click');
expect(wrapper.find('a[children="Order History"]')).toHaveLength(1);
expect(wrapper.find('a[children="Dashboard"]')).toHaveLength(1);

// When learner portal links are present, Order History should not be a dropdown item
// We do this in the same test to avoid weird things about jest wanting you to use
// the "correct" act() function (even though it gives the same warning regardless
// of where you import it from, be it react-dom/test-utils or react-test-renderer).
mockLearnerPortalLinks();
wrapper = mount(component);
await act(async () => {
await flushPromises();
});
wrapper.update();
wrapper.find('.menu-trigger').simulate('click');
expect(wrapper.find('a[children="Order History"]')).toHaveLength(0);
expect(wrapper.find('a[children="Dashboard"]')).toHaveLength(1);
});

it('renders correctly for unauthenticated users on mobile', () => {
const component = (
<ResponsiveContext.Provider value={{ width: 500 }}>
Expand Down
37 changes: 37 additions & 0 deletions src/data/hooks/enterprise.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,37 @@
import { useEffect, useState } from 'react';
import { getLearnerPortalLinks, getSelectedEnterpriseUUID } from '@edx/frontend-enterprise';
import { getAuthenticatedHttpClient } from '@edx/frontend-platform/auth';

export default function useEnterpriseConfig(authenticatedUser) {
const [enterpriseLearnerPortalLink, setEnterpriseLearnerPortalLink] = useState();
const [enterpriseCustomerBrandingConfig, setEnterpriseCustomerBrandingConfig] = useState();
useEffect(
() => {
const httpClient = getAuthenticatedHttpClient();
getLearnerPortalLinks(httpClient, authenticatedUser).then((learnerPortalLinks) => {
const preferredUUID = getSelectedEnterpriseUUID(authenticatedUser);
const preferredLearnerPortalLink = learnerPortalLinks.find(learnerPortalLink =>
learnerPortalLink.uuid === preferredUUID);
if (preferredLearnerPortalLink) {
setEnterpriseCustomerBrandingConfig({
logo: preferredLearnerPortalLink.branding_configuration.logo,
logoAltText: preferredLearnerPortalLink.title,
logoDestination: preferredLearnerPortalLink.url,
});

setEnterpriseLearnerPortalLink({
type: 'item',
href: preferredLearnerPortalLink.url,
content: 'Dashboard',
});
}
});
},
[],
);

return {
enterpriseLearnerPortalLink,
enterpriseCustomerBrandingConfig,
};
}

0 comments on commit c20c618

Please sign in to comment.