Modernize build and fix the anonymous header (#26)

* refactor: rename SiteHeader to Header

* build: use frontend-build

* fix: ensure that we can handle anonymous users

* feat: anonymous header works now

Up until now we haven’t had any microfrontends that allowed anonymous usage.  The Header was encountering errors rendering in such situations, though it was close.  Now it properly shows the Login and Sign Up buttons.

* build: bump version of frontend-build
This commit is contained in:
David Joy
2019-11-04 09:58:36 -05:00
committed by GitHub
parent 5d77fab8f3
commit 57e05c4d88
25 changed files with 9635 additions and 8221 deletions

33
.env
View File

@@ -1,33 +0,0 @@
BASE_URL=localhost:1995
SITE_NAME=Open edX
LMS_BASE_URL=http://localhost:18000
CREDENTIALS_BASE_URL=http://localhost:18150
ECOMMERCE_BASE_URL=http://localhost:18130
LOGIN_URL=http://localhost:18000/login
LOGOUT_URL=http://localhost:18000/login
CSRF_TOKEN_API_PATH=/csrf/api/v1/token
REFRESH_ACCESS_TOKEN_ENDPOINT=http://localhost:18000/login_refresh
SEGMENT_KEY=ul
ACCESS_TOKEN_COOKIE_NAME=edx-jwt-cookie-header-payload
USER_INFO_COOKIE_NAME=edx-user-info
CSRF_COOKIE_NAME=csrftoken
LANGUAGE_PREFERENCE_COOKIE_NAME=openedx-language-preference
SITE_NAME=edX
MARKETING_SITE_BASE_URL=http://localhost:18000
ENTERPRISE_MARKETING_URL=http://example.com
ENTERPRISE_MARKETING_UTM_CAMPAIGN=my_campaign
ENTERPRISE_MARKETING_UTM_SOURCE=edX profile
ENTERPRISE_MARKETING_FOOTER_UTM_MEDIUM=Footer
SUPPORT_URL=http://localhost:18000/support
CONTACT_URL=http://localhost:18000/contact
OPEN_SOURCE_URL=http://localhost:18000/openedx
TERMS_OF_SERVICE_URL=http://localhost:18000/terms-of-service
PRIVACY_POLICY_URL=http://localhost:18000/privacy-policy
FACEBOOK_URL=https://www.facebook.com
TWITTER_URL=https://twitter.com
YOU_TUBE_URL=https://www.youtube.com
LINKED_IN_URL=https://www.linkedin.com
REDDIT_URL=https://www.reddit.com
APPLE_APP_STORE_URL=https://www.apple.com/ios/app-store/
GOOGLE_PLAY_URL=https://play.google.com/store
ORDER_HISTORY_URL=localhost:1996/orders

15
.env.development Normal file
View File

@@ -0,0 +1,15 @@
ACCESS_TOKEN_COOKIE_NAME=edx-jwt-cookie-header-payload
BASE_URL=localhost:8080
CREDENTIALS_BASE_URL=http://localhost:18150
CSRF_TOKEN_API_PATH=/csrf/api/v1/token
ECOMMERCE_BASE_URL=http://localhost:18130
LANGUAGE_PREFERENCE_COOKIE_NAME=openedx-language-preference
LMS_BASE_URL=http://localhost:18000
LOGIN_URL=http://localhost:18000/login
LOGOUT_URL=http://localhost:18000/login
MARKETING_SITE_BASE_URL=http://localhost:18000
ORDER_HISTORY_URL=localhost:1996/orders
REFRESH_ACCESS_TOKEN_ENDPOINT=http://localhost:18000/login_refresh
SEGMENT_KEY=null
SITE_NAME=Open edX
USER_INFO_COOKIE_NAME=edx-user-info

3
.eslintrc.js Normal file
View File

@@ -0,0 +1,3 @@
const { createConfig } = require('@edx/frontend-build');
module.exports = createConfig('eslint');

View File

@@ -1,19 +0,0 @@
{
"extends": "eslint-config-edx",
"parser": "babel-eslint",
"rules": {
"import/no-extraneous-dependencies": [
"error",
{
"devDependencies": [
"webpack.config.js",
"**/*.test.jsx",
"**/*.test.js"
]
}
]
},
"env": {
"jest": true
}
}

View File

@@ -12,7 +12,7 @@ transifex_temp = ./temp/babel-plugin-react-intl
build:
rm -rf ./dist
./node_modules/.bin/babel src --out-dir dist --source-maps --ignore **/*.test.jsx,**/__mocks__,**/__snapshots__,**/setupTest.js --copy-files
./node_modules/.bin/fedx-scripts babel src --out-dir dist --source-maps --ignore **/*.test.jsx,**/__mocks__,**/__snapshots__,**/setupTest.js --copy-files
@# --copy-files will bring in everything else that wasn't processed by babel. Remove what we don't want.
@rm -rf dist/**/*.test.jsx
@rm -rf dist/**/__snapshots__

View File

@@ -1,33 +1,3 @@
module.exports = {
presets: [
[
'@babel/preset-env',
{
modules: false,
},
],
'@babel/preset-react',
],
plugins: [
'@babel/plugin-proposal-object-rest-spread',
'@babel/plugin-proposal-class-properties',
],
env: {
i18n: {
plugins: [
[
'react-intl',
{
messagesDir: './temp/babel-plugin-react-intl',
moduleSourceName: '@edx/frontend-i18n',
},
],
],
},
test: {
presets: [
'@babel/preset-env',
],
},
},
};
const { createConfig } = require('@edx/frontend-build');
module.exports = createConfig('babel-preserve-modules');

View File

@@ -1,10 +0,0 @@
const path = require('path');
// Resolve ~tilda paths used in paragon to
// node_modules. This is used to reference
// bootstrap specifically.
module.exports = {
includePaths: [
path.resolve(__dirname, '../node_modules'),
],
};

View File

@@ -1,12 +0,0 @@
<!doctype html>
<html lang="en-us">
<head>
<title>Example</title>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
</head>
<body>
<div id="root"></div>
<script src="./index.js"></script>
</body>
</html>

View File

@@ -5,36 +5,31 @@ import ReactDOM from 'react-dom';
import { App, AppContext, APP_READY, AppProvider } from '@edx/frontend-base';
import { NewRelicLoggingService } from '@edx/frontend-logging';
import './index.scss';
import SiteHeader from '../src/';
import Header from '../src/';
App.subscribe(APP_READY, () => {
ReactDOM.render(
<AppProvider>
{/* We can fake out authentication by including another provider here with the data we want */}
<AppContext.Provider value={{
authenticatedUser: {
userId: null,
username: null,
roles: [],
administrator: false,
},
authenticatedUser: null,
config: App.config
}}>
<SiteHeader />
<Header />
</AppContext.Provider>
<h5 className="mt-2 mb-5">Logged out state</h5>
{/* We can fake out authentication by including another provider here with the data we want */}
<AppContext.Provider value={{
authenticatedUser: {
userId: null,
userId: '123abc',
username: 'testuser',
roles: [],
administrator: false,
},
config: App.config
}}>
<SiteHeader />
<Header />
</AppContext.Provider>
<h5 className="mt-2">Logged in state</h5>
</AppProvider>,
@@ -42,4 +37,6 @@ App.subscribe(APP_READY, () => {
);
});
App.initialize({ messages: [], loggingService: NewRelicLoggingService });
App.initialize({
messages: []
});

7
jest.config.js Normal file
View File

@@ -0,0 +1,7 @@
const { createConfig } = require('@edx/frontend-build');
module.exports = createConfig('jest', {
setupFiles: [
'<rootDir>/src/setupTest.js',
],
});

16773
package-lock.json generated

File diff suppressed because it is too large Load Diff

View File

@@ -2,22 +2,27 @@
"name": "@edx/frontend-component-header",
"version": "1.0.0-semantically-released",
"description": "The standard header for Open edX",
"main": "dist/index.js",
"publishConfig": {
"access": "public"
},
"main": "dist/index.js",
"scripts": {
"build": "make build",
"i18n_extract": "BABEL_ENV=i18n babel src --quiet > /dev/null",
"lint": "eslint --ext .js --ext .jsx .",
"semantic-release": "semantic-release",
"start": "parcel ./example/index.html --no-source-maps --out-dir example/dist",
"test": "jest --coverage --passWithNoTests"
"i18n_extract": "BABEL_ENV=i18n fedx-scripts babel src --quiet > /dev/null",
"lint": "fedx-scripts eslint --ext .js --ext .jsx .",
"snapshot": "fedx-scripts jest --updateSnapshot",
"start": "fedx-scripts webpack-dev-server --progress",
"test": "fedx-scripts jest --coverage"
},
"files": [
"/dist",
"/src"
"/dist"
],
"husky": {
"hooks": {
"pre-commit": "npm run lint",
"commit-msg": "commitlint -e $GIT_PARAMS"
}
},
"repository": {
"type": "git",
"url": "git+https://github.com/edx/frontend-component-header.git"
@@ -29,38 +34,30 @@
},
"homepage": "https://github.com/edx/frontend-component-header#readme",
"devDependencies": {
"@babel/cli": "7.6.0",
"@babel/core": "7.6.0",
"@babel/plugin-proposal-class-properties": "7.5.5",
"@babel/plugin-proposal-object-rest-spread": "7.5.5",
"@babel/preset-env": "7.6.0",
"@babel/preset-react": "7.0.0",
"@commitlint/cli": "8.2.0",
"@commitlint/config-angular": "8.2.0",
"@commitlint/prompt": "8.2.0",
"@commitlint/prompt-cli": "8.2.0",
"@edx/frontend-analytics": "3.0.0",
"@edx/frontend-auth": "7.0.1",
"@edx/frontend-base": "4.0.0",
"@edx/frontend-base": "^5.0.0",
"@edx/frontend-build": "^1.3.1",
"@edx/frontend-i18n": "3.0.3",
"@edx/frontend-logging": "3.0.1",
"@edx/paragon": "7.1.4",
"babel-eslint": "10.0.3",
"babel-plugin-react-intl": "4.1.18",
"dotenv": "8.1.0",
"codecov": "3.6.1",
"enzyme": "3.10.0",
"enzyme-adapter-react-16": "1.14.0",
"eslint": "6.3.0",
"eslint-config-edx": "4.0.4",
"jest": "24.9.0",
"parcel-bundler": "1.12.3",
"husky": "3.0.8",
"prop-types": "15.7.2",
"react": "16.9.0",
"react-dom": "16.9.0",
"react-redux": "7.1.1",
"react-router-dom": "5.0.1",
"react-redux": "^7.1.1",
"react-router-dom": "^5.1.2",
"react-test-renderer": "16.9.0",
"reactifex": "1.1.1",
"redux": "4.0.4",
"redux-saga": "1.0.5",
"sass": "1.22.12",
"semantic-release": "15.13.24"
"redux": "^4.0.4",
"redux-saga": "^1.1.1"
},
"dependencies": {
"babel-polyfill": "6.26.0",
@@ -71,27 +68,10 @@
"@edx/frontend-analytics": "^3.0.0",
"@edx/frontend-base": "^4.0.0",
"@edx/frontend-i18n": "^3.0.3",
"prop-types": "^15.7.2",
"react": "^16.9.0"
},
"jest": {
"transform": {
"^.+\\.jsx?$": "babel-jest"
},
"collectCoverageFrom": [
"src/**/*.{js,jsx}",
"!**/node_modules/**",
"!**/dist/**"
],
"moduleNameMapper": {
"\\.(jpg|jpeg|png|gif|eot|otf|webp|svg|ttf|woff|woff2|mp4|webm|wav|mp3|m4a|aac|oga)$": "<rootDir>/src/__mocks__/fileMock.js"
},
"transformIgnorePatterns": [
"/node_modules/(?!@edx)"
],
"setupFiles": [
"./src/setupTest.js",
"dotenv/config"
]
"@edx/frontend-logging": "^3.0.0",
"@edx/paragon": "^7.0.0",
"prop-types": "^15.5.10",
"react": "^16.9.0",
"react-dom": "^16.9.0"
}
}

11
public/index.html Executable file
View File

@@ -0,0 +1,11 @@
<!doctype html>
<html lang="en-us" dir="ltr">
<head>
<title>Header</title>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
</head>
<body>
<div id="root"></div>
</body>
</html>

View File

@@ -8,7 +8,7 @@ import Avatar from './Avatar';
import { LinkedLogo, Logo } from './Logo';
// i18n
import messages from './SiteHeader.messages';
import messages from './Header.messages';
// Assets
import { CaretIcon } from './Icons';

View File

@@ -8,7 +8,7 @@ import MobileHeader from './MobileHeader';
import LogoSVG from './logo.svg';
import messages from './SiteHeader.messages';
import messages from './Header.messages';
App.ensureConfig([
'LMS_BASE_URL',
@@ -17,7 +17,7 @@ App.ensureConfig([
'SITE_NAME',
], 'Header component');
function SiteHeader({ intl }) {
function Header({ intl }) {
const { authenticatedUser, config } = useContext(AppContext);
const mainMenu = [
@@ -28,7 +28,7 @@ function SiteHeader({ intl }) {
},
];
const userMenu = [
const userMenu = authenticatedUser === null ? [] : [
{
type: 'item',
href: `${config.LMS_BASE_URL}/dashboard`,
@@ -69,9 +69,9 @@ function SiteHeader({ intl }) {
logoAltText: config.SITE_NAME,
siteName: config.SITE_NAME,
logoDestination: `${config.LMS_BASE_URL}/dashboard`,
loggedIn: !!authenticatedUser.username,
username: authenticatedUser.username,
avatar: authenticatedUser.avatar,
loggedIn: authenticatedUser !== null,
username: authenticatedUser !== null ? authenticatedUser.username : null,
avatar: authenticatedUser !== null ? authenticatedUser.avatar : null,
mainMenu,
userMenu,
loggedOutItems,
@@ -89,8 +89,8 @@ function SiteHeader({ intl }) {
);
}
SiteHeader.propTypes = {
Header.propTypes = {
intl: intlShape.isRequired,
};
export default injectIntl(SiteHeader);
export default injectIntl(Header);

120
src/Header.test.jsx Normal file
View File

@@ -0,0 +1,120 @@
import React from 'react';
import { IntlProvider } from '@edx/frontend-i18n';
import TestRenderer from 'react-test-renderer';
import { AppContext } from '@edx/frontend-base';
import { Context as ResponsiveContext } from 'react-responsive';
import Header from './index';
describe('<Header />', () => {
it('renders correctly for anonymous desktop', () => {
const component = (
<ResponsiveContext.Provider value={{ width: 1280 }}>
<IntlProvider locale="en" messages={{}}>
<AppContext.Provider value={{
authenticatedUser: null,
config: {
LMS_BASE_URL: process.env.LMS_BASE_URL,
SITE_NAME: process.env.SITE_NAME,
LOGIN_URL: process.env.LOGIN_URL,
LOGOUT_URL: process.env.LOGOUT_URL,
},
}}
>
<Header />
</AppContext.Provider>
</IntlProvider>
</ResponsiveContext.Provider>
);
const wrapper = TestRenderer.create(component);
expect(wrapper.toJSON()).toMatchSnapshot();
});
it('renders correctly for authenticated desktop', () => {
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,
SITE_NAME: process.env.SITE_NAME,
LOGIN_URL: process.env.LOGIN_URL,
LOGOUT_URL: process.env.LOGOUT_URL,
},
}}
>
<Header />
</AppContext.Provider>
</IntlProvider>
</ResponsiveContext.Provider>
);
const wrapper = TestRenderer.create(component);
expect(wrapper.toJSON()).toMatchSnapshot();
});
it('renders correctly for anonymous mobile', () => {
const component = (
<ResponsiveContext.Provider value={{ width: 500 }}>
<IntlProvider locale="en" messages={{}}>
<AppContext.Provider value={{
authenticatedUser: null,
config: {
LMS_BASE_URL: process.env.LMS_BASE_URL,
SITE_NAME: process.env.SITE_NAME,
LOGIN_URL: process.env.LOGIN_URL,
LOGOUT_URL: process.env.LOGOUT_URL,
},
}}
>
<Header />
</AppContext.Provider>
</IntlProvider>
</ResponsiveContext.Provider>
);
const wrapper = TestRenderer.create(component);
expect(wrapper.toJSON()).toMatchSnapshot();
});
it('renders correctly for authenticated mobile', () => {
const component = (
<ResponsiveContext.Provider value={{ width: 500 }}>
<IntlProvider locale="en" messages={{}}>
<AppContext.Provider value={{
authenticatedUser: {
userId: 'abc123',
username: 'edX',
roles: [],
administrator: false,
},
config: {
LMS_BASE_URL: process.env.LMS_BASE_URL,
SITE_NAME: process.env.SITE_NAME,
LOGIN_URL: process.env.LOGIN_URL,
LOGOUT_URL: process.env.LOGOUT_URL,
},
}}
>
<Header />
</AppContext.Provider>
</IntlProvider>
</ResponsiveContext.Provider>
);
const wrapper = TestRenderer.create(component);
expect(wrapper.toJSON()).toMatchSnapshot();
});
});

View File

@@ -8,7 +8,7 @@ import Avatar from './Avatar';
import { LinkedLogo, Logo } from './Logo';
// i18n
import messages from './SiteHeader.messages';
import messages from './Header.messages';
// Assets
import { MenuIcon } from './Icons';

View File

@@ -1,70 +0,0 @@
import React from 'react';
import { IntlProvider } from '@edx/frontend-i18n';
import TestRenderer from 'react-test-renderer';
import { AppContext } from '@edx/frontend-base';
import { Context as ResponsiveContext } from 'react-responsive';
import SiteHeader from './index';
describe('<SiteHeader />', () => {
it('renders correctly for desktop', () => {
const component = (
<ResponsiveContext.Provider value={{ width: 1280 }}>
<IntlProvider locale="en" messages={{}}>
<AppContext.Provider value={{
authenticatedUser: {
userId: null,
username: null,
roles: [],
administrator: false,
},
config: {
LMS_BASE_URL: process.env.LMS_BASE_URL,
SITE_NAME: process.env.SITE_NAME,
LOGIN_URL: process.env.LOGIN_URL,
LOGOUT_URL: process.env.LOGOUT_URL,
},
}}
>
<SiteHeader />
</AppContext.Provider>
</IntlProvider>
</ResponsiveContext.Provider>
);
const wrapper = TestRenderer.create(component);
expect(wrapper.toJSON()).toMatchSnapshot();
});
it('renders correctly for mobile', () => {
const component = (
<ResponsiveContext.Provider value={{ width: 500 }}>
<IntlProvider locale="en" messages={{}}>
<AppContext.Provider value={{
authenticatedUser: {
userId: null,
username: null,
roles: [],
administrator: false,
},
config: {
LMS_BASE_URL: process.env.LMS_BASE_URL,
SITE_NAME: process.env.SITE_NAME,
LOGIN_URL: process.env.LOGIN_URL,
LOGOUT_URL: process.env.LOGOUT_URL,
},
}}
>
<SiteHeader />
</AppContext.Provider>
</IntlProvider>
</ResponsiveContext.Provider>
);
const wrapper = TestRenderer.create(component);
expect(wrapper.toJSON()).toMatchSnapshot();
});
});

View File

@@ -1,3 +0,0 @@
// __mocks__/fileMock.js
module.exports = 'test-file-stub';

View File

@@ -0,0 +1,422 @@
// Jest Snapshot v1, https://goo.gl/fbAQLP
exports[`<Header /> renders correctly for anonymous desktop 1`] = `
<header
className="site-header-desktop"
>
<div
className="container-fluid"
>
<div
className="nav-container position-relative d-flex align-items-center"
>
<a
className="logo"
href="http://localhost:18000/dashboard"
>
<img
alt="edX"
className="d-block"
src="icon/mock/path"
/>
</a>
<nav
aria-label="Main"
className="nav main-nav"
>
<a
className="nav-link"
href="http://localhost:18000/dashboard"
>
Courses
</a>
</nav>
<nav
aria-label="Secondary"
className="nav secondary-menu-container align-items-center ml-auto"
>
<a
className="btn mr-2 btn-link"
href="http://localhost:18000/login"
>
Login
</a>
<a
className="btn mr-2 btn-outline-primary"
href="http://localhost:18000/register"
>
Sign Up
</a>
</nav>
</div>
</div>
</header>
`;
exports[`<Header /> renders correctly for anonymous mobile 1`] = `
<header
aria-label="Main"
className="site-header-mobile d-flex justify-content-between align-items-center shadow sticky-top"
>
<div
className="w-100 d-flex justify-content-start"
>
<div
className="menu position-static"
onKeyDown={[Function]}
onMouseEnter={[Function]}
onMouseLeave={[Function]}
>
<button
aria-expanded={false}
aria-haspopup="menu"
aria-label="Main Menu"
className="menu-trigger icon-button"
onClick={[Function]}
title="Main Menu"
>
<svg
aria-hidden={true}
focusable="false"
height="24px"
role="img"
style={
Object {
"height": "1.5rem",
"width": "1.5rem",
}
}
version="1.1"
viewBox="0 0 24 24"
width="24px"
>
<rect
fill="currentColor"
height="2"
width="20"
x="2"
y="5"
/>
<rect
fill="currentColor"
height="2"
width="20"
x="2"
y="11"
/>
<rect
fill="currentColor"
height="2"
width="20"
x="2"
y="17"
/>
</svg>
</button>
</div>
</div>
<div
className="w-100 d-flex justify-content-center"
>
<a
className="logo"
href="http://localhost:18000/dashboard"
itemType="http://schema.org/Organization"
>
<img
alt="edX"
className="d-block"
src="icon/mock/path"
/>
</a>
</div>
<div
className="w-100 d-flex justify-content-end align-items-center"
>
<nav
aria-label="Secondary"
className="menu position-static"
onKeyDown={[Function]}
onMouseEnter={[Function]}
onMouseLeave={[Function]}
>
<button
aria-expanded={false}
aria-haspopup="menu"
aria-label="Account Menu"
className="menu-trigger icon-button"
onClick={[Function]}
title="Account Menu"
>
<span
className="avatar overflow-hidden d-inline-flex rounded-circle null"
style={
Object {
"height": "1.5rem",
"width": "1.5rem",
}
}
>
<svg
aria-hidden={true}
className="text-muted"
focusable="false"
height="24px"
role="img"
style={
Object {
"height": "1.5rem",
"width": "1.5rem",
}
}
version="1.1"
viewBox="0 0 24 24"
width="24px"
>
<path
d="M4.10255106,18.1351061 C4.7170266,16.0581859 8.01891846,14.4720277 12,14.4720277 C15.9810815,14.4720277 19.2829734,16.0581859 19.8974489,18.1351061 C21.215206,16.4412566 22,14.3122775 22,12 C22,6.4771525 17.5228475,2 12,2 C6.4771525,2 2,6.4771525 2,12 C2,14.3122775 2.78479405,16.4412566 4.10255106,18.1351061 Z M12,24 C5.372583,24 0,18.627417 0,12 C0,5.372583 5.372583,0 12,0 C18.627417,0 24,5.372583 24,12 C24,18.627417 18.627417,24 12,24 Z M12,13 C9.790861,13 8,11.209139 8,9 C8,6.790861 9.790861,5 12,5 C14.209139,5 16,6.790861 16,9 C16,11.209139 14.209139,13 12,13 Z"
fill="currentColor"
/>
</svg>
</span>
</button>
</nav>
</div>
</header>
`;
exports[`<Header /> renders correctly for authenticated desktop 1`] = `
<header
className="site-header-desktop"
>
<div
className="container-fluid"
>
<div
className="nav-container position-relative d-flex align-items-center"
>
<a
className="logo"
href="http://localhost:18000/dashboard"
>
<img
alt="edX"
className="d-block"
src="icon/mock/path"
/>
</a>
<nav
aria-label="Main"
className="nav main-nav"
>
<a
className="nav-link"
href="http://localhost:18000/dashboard"
>
Courses
</a>
</nav>
<nav
aria-label="Secondary"
className="nav secondary-menu-container align-items-center ml-auto"
>
<div
className="menu null"
onKeyDown={[Function]}
onMouseEnter={[Function]}
onMouseLeave={[Function]}
>
<button
aria-expanded={false}
aria-haspopup="menu"
aria-label="Account menu for edX"
className="menu-trigger btn btn-light d-inline-flex align-items-center pl-2 pr-3"
onClick={[Function]}
>
<span
className="avatar overflow-hidden d-inline-flex rounded-circle mr-2"
style={
Object {
"height": "1.5em",
"width": "1.5em",
}
}
>
<svg
aria-hidden={true}
className="text-muted"
focusable="false"
height="24px"
role="img"
style={
Object {
"height": "1.5em",
"width": "1.5em",
}
}
version="1.1"
viewBox="0 0 24 24"
width="24px"
>
<path
d="M4.10255106,18.1351061 C4.7170266,16.0581859 8.01891846,14.4720277 12,14.4720277 C15.9810815,14.4720277 19.2829734,16.0581859 19.8974489,18.1351061 C21.215206,16.4412566 22,14.3122775 22,12 C22,6.4771525 17.5228475,2 12,2 C6.4771525,2 2,6.4771525 2,12 C2,14.3122775 2.78479405,16.4412566 4.10255106,18.1351061 Z M12,24 C5.372583,24 0,18.627417 0,12 C0,5.372583 5.372583,0 12,0 C18.627417,0 24,5.372583 24,12 C24,18.627417 18.627417,24 12,24 Z M12,13 C9.790861,13 8,11.209139 8,9 C8,6.790861 9.790861,5 12,5 C14.209139,5 16,6.790861 16,9 C16,11.209139 14.209139,13 12,13 Z"
fill="currentColor"
/>
</svg>
</span>
edX
<svg
aria-hidden={true}
focusable="false"
height="16px"
role="img"
version="1.1"
viewBox="0 0 16 16"
width="16px"
>
<path
d="M7,4 L7,8 L11,8 L11,10 L5,10 L5,4 L7,4 Z"
fill="currentColor"
transform="translate(8.000000, 7.000000) rotate(-45.000000) translate(-8.000000, -7.000000) "
/>
</svg>
</button>
</div>
</nav>
</div>
</div>
</header>
`;
exports[`<Header /> renders correctly for authenticated mobile 1`] = `
<header
aria-label="Main"
className="site-header-mobile d-flex justify-content-between align-items-center shadow sticky-top"
>
<div
className="w-100 d-flex justify-content-start"
>
<div
className="menu position-static"
onKeyDown={[Function]}
onMouseEnter={[Function]}
onMouseLeave={[Function]}
>
<button
aria-expanded={false}
aria-haspopup="menu"
aria-label="Main Menu"
className="menu-trigger icon-button"
onClick={[Function]}
title="Main Menu"
>
<svg
aria-hidden={true}
focusable="false"
height="24px"
role="img"
style={
Object {
"height": "1.5rem",
"width": "1.5rem",
}
}
version="1.1"
viewBox="0 0 24 24"
width="24px"
>
<rect
fill="currentColor"
height="2"
width="20"
x="2"
y="5"
/>
<rect
fill="currentColor"
height="2"
width="20"
x="2"
y="11"
/>
<rect
fill="currentColor"
height="2"
width="20"
x="2"
y="17"
/>
</svg>
</button>
</div>
</div>
<div
className="w-100 d-flex justify-content-center"
>
<a
className="logo"
href="http://localhost:18000/dashboard"
itemType="http://schema.org/Organization"
>
<img
alt="edX"
className="d-block"
src="icon/mock/path"
/>
</a>
</div>
<div
className="w-100 d-flex justify-content-end align-items-center"
>
<nav
aria-label="Secondary"
className="menu position-static"
onKeyDown={[Function]}
onMouseEnter={[Function]}
onMouseLeave={[Function]}
>
<button
aria-expanded={false}
aria-haspopup="menu"
aria-label="Account Menu"
className="menu-trigger icon-button"
onClick={[Function]}
title="Account Menu"
>
<span
className="avatar overflow-hidden d-inline-flex rounded-circle null"
style={
Object {
"height": "1.5rem",
"width": "1.5rem",
}
}
>
<svg
aria-hidden={true}
className="text-muted"
focusable="false"
height="24px"
role="img"
style={
Object {
"height": "1.5rem",
"width": "1.5rem",
}
}
version="1.1"
viewBox="0 0 24 24"
width="24px"
>
<path
d="M4.10255106,18.1351061 C4.7170266,16.0581859 8.01891846,14.4720277 12,14.4720277 C15.9810815,14.4720277 19.2829734,16.0581859 19.8974489,18.1351061 C21.215206,16.4412566 22,14.3122775 22,12 C22,6.4771525 17.5228475,2 12,2 C6.4771525,2 2,6.4771525 2,12 C2,14.3122775 2.78479405,16.4412566 4.10255106,18.1351061 Z M12,24 C5.372583,24 0,18.627417 0,12 C0,5.372583 5.372583,0 12,0 C18.627417,0 24,5.372583 24,12 C24,18.627417 18.627417,24 12,24 Z M12,13 C9.790861,13 8,11.209139 8,9 C8,6.790861 9.790861,5 12,5 C14.209139,5 16,6.790861 16,9 C16,11.209139 14.209139,13 12,13 Z"
fill="currentColor"
/>
</svg>
</span>
</button>
</nav>
</div>
</header>
`;

View File

@@ -1,186 +0,0 @@
// Jest Snapshot v1, https://goo.gl/fbAQLP
exports[`<SiteHeader /> renders correctly for desktop 1`] = `
<header
className="site-header-desktop"
>
<div
className="container-fluid"
>
<div
className="nav-container position-relative d-flex align-items-center"
>
<a
className="logo"
href="http://localhost:18000/dashboard"
>
<img
alt="edX"
className="d-block"
src="test-file-stub"
/>
</a>
<nav
aria-label="Main"
className="nav main-nav"
>
<a
className="nav-link"
href="http://localhost:18000/dashboard"
>
Courses
</a>
</nav>
<nav
aria-label="Secondary"
className="nav secondary-menu-container align-items-center ml-auto"
>
<a
className="btn mr-2 btn-link"
href="http://localhost:18000/login"
>
Login
</a>
<a
className="btn mr-2 btn-outline-primary"
href="http://localhost:18000/register"
>
Sign Up
</a>
</nav>
</div>
</div>
</header>
`;
exports[`<SiteHeader /> renders correctly for mobile 1`] = `
<header
aria-label="Main"
className="site-header-mobile d-flex justify-content-between align-items-center shadow sticky-top"
>
<div
className="w-100 d-flex justify-content-start"
>
<div
className="menu position-static"
onKeyDown={[Function]}
onMouseEnter={[Function]}
onMouseLeave={[Function]}
>
<button
aria-expanded={false}
aria-haspopup="menu"
aria-label="Main Menu"
className="menu-trigger icon-button"
onClick={[Function]}
title="Main Menu"
>
<svg
aria-hidden={true}
focusable="false"
height="24px"
role="img"
style={
Object {
"height": "1.5rem",
"width": "1.5rem",
}
}
version="1.1"
viewBox="0 0 24 24"
width="24px"
>
<rect
fill="currentColor"
height="2"
width="20"
x="2"
y="5"
/>
<rect
fill="currentColor"
height="2"
width="20"
x="2"
y="11"
/>
<rect
fill="currentColor"
height="2"
width="20"
x="2"
y="17"
/>
</svg>
</button>
</div>
</div>
<div
className="w-100 d-flex justify-content-center"
>
<a
className="logo"
href="http://localhost:18000/dashboard"
itemType="http://schema.org/Organization"
>
<img
alt="edX"
className="d-block"
src="test-file-stub"
/>
</a>
</div>
<div
className="w-100 d-flex justify-content-end align-items-center"
>
<nav
aria-label="Secondary"
className="menu position-static"
onKeyDown={[Function]}
onMouseEnter={[Function]}
onMouseLeave={[Function]}
>
<button
aria-expanded={false}
aria-haspopup="menu"
aria-label="Account Menu"
className="menu-trigger icon-button"
onClick={[Function]}
title="Account Menu"
>
<span
className="avatar overflow-hidden d-inline-flex rounded-circle null"
style={
Object {
"height": "1.5rem",
"width": "1.5rem",
}
}
>
<svg
aria-hidden={true}
className="text-muted"
focusable="false"
height="24px"
role="img"
style={
Object {
"height": "1.5rem",
"width": "1.5rem",
}
}
version="1.1"
viewBox="0 0 24 24"
width="24px"
>
<path
d="M4.10255106,18.1351061 C4.7170266,16.0581859 8.01891846,14.4720277 12,14.4720277 C15.9810815,14.4720277 19.2829734,16.0581859 19.8974489,18.1351061 C21.215206,16.4412566 22,14.3122775 22,12 C22,6.4771525 17.5228475,2 12,2 C6.4771525,2 2,6.4771525 2,12 C2,14.3122775 2.78479405,16.4412566 4.10255106,18.1351061 Z M12,24 C5.372583,24 0,18.627417 0,12 C0,5.372583 5.372583,0 12,0 C18.627417,0 24,5.372583 24,12 C24,18.627417 18.627417,24 12,24 Z M12,13 C9.790861,13 8,11.209139 8,9 C8,6.790861 9.790861,5 12,5 C14.209139,5 16,6.790861 16,9 C16,11.209139 14.209139,13 12,13 Z"
fill="currentColor"
/>
</svg>
</span>
</button>
</nav>
</div>
</header>
`;

View File

@@ -1,6 +1,6 @@
import SiteHeader from './SiteHeader';
import Header from './Header';
import messages from './i18n/index';
export { messages };
export default SiteHeader;
export default Header;

View File

@@ -11,7 +11,6 @@ Enzyme.configure({ adapter: new Adapter() });
process.env.ACCESS_TOKEN_COOKIE_NAME = 'edx-jwt-cookie-header-payload';
process.env.BASE_URL = 'localhost:1995';
process.env.CREDENTIALS_BASE_URL = 'http://localhost:18150';
process.env.CSRF_COOKIE_NAME = 'csrftoken';
process.env.CSRF_TOKEN_API_PATH = '/csrf/api/v1/token';
process.env.ECOMMERCE_BASE_URL = 'http://localhost:18130';
process.env.LANGUAGE_PREFERENCE_COOKIE_NAME = 'openedx-language-preference';

10
webpack.dev.config.js Normal file
View File

@@ -0,0 +1,10 @@
const path = require('path');
const { createConfig } = require('@edx/frontend-build');
module.exports = createConfig('webpack-dev', {
entry: path.resolve(__dirname, 'example'),
output: {
path: path.resolve(__dirname, 'example/dist'),
publicPath: '/',
},
});