Initial commit

This commit is contained in:
stv
2020-05-05 11:36:00 -07:00
committed by stvn
parent 03fbc9c151
commit 0e4f8ed2cf
44 changed files with 20876 additions and 0 deletions

10
.dockerignore Executable file
View File

@@ -0,0 +1,10 @@
node_modules
npm-debug.log
README.rst
LICENSE
.babelrc
.eslintignore
.eslintrc.json
.gitignore
.npmignore
commitlint.config.js

16
.env Normal file
View File

@@ -0,0 +1,16 @@
NODE_ENV='production'
ACCESS_TOKEN_COOKIE_NAME=null
BASE_URL=null
CREDENTIALS_BASE_URL=null
CSRF_TOKEN_API_PATH=null
ECOMMERCE_BASE_URL=null
LANGUAGE_PREFERENCE_COOKIE_NAME=null
LMS_BASE_URL=null
LOGIN_URL=null
LOGOUT_URL=null
MARKETING_SITE_BASE_URL=null
ORDER_HISTORY_URL=null
REFRESH_ACCESS_TOKEN_ENDPOINT=null
SEGMENT_KEY=null
SITE_NAME=null
USER_INFO_COOKIE_NAME=null

17
.env.development Normal file
View File

@@ -0,0 +1,17 @@
NODE_ENV='development'
PORT=8080
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/logout'
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='edX'
USER_INFO_COOKIE_NAME='edx-user-info'

15
.env.test Normal file
View File

@@ -0,0 +1,15 @@
ACCESS_TOKEN_COOKIE_NAME='edx-jwt-cookie-header-payload'
BASE_URL='localhost:1995'
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/logout'
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='edX'
USER_INFO_COOKIE_NAME='edx-user-info'

4
.eslintignore Executable file
View File

@@ -0,0 +1,4 @@
coverage/*
dist/
node_modules/
jest.config.js

3
.eslintrc.js Normal file
View File

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

18
.gitignore vendored Executable file
View File

@@ -0,0 +1,18 @@
.DS_Store
.eslintcache
.idea
node_modules
npm-debug.log
coverage
dist/
src/i18n/transifex_input.json
temp/babel-plugin-react-intl
### pyenv ###
.python-version
### Emacs ###
*~
/temp
/.vscode

12
.npmignore Executable file
View File

@@ -0,0 +1,12 @@
.eslintignore
.eslintrc.json
.gitignore
.travis.yml
docker-compose.yml
Dockerfile
Makefile
npm-debug.log
coverage
node_modules
public

15
.travis.yml Executable file
View File

@@ -0,0 +1,15 @@
language: node_js
node_js: 12
before_install:
- npm install -g npm@6
install:
- npm ci
script:
- make validate-no-uncommitted-package-lock-changes
- npm run i18n_extract
- npm run lint
- npm run test
- npm run build
- npm run is-es5
after_success:
- codecov

54
Makefile Executable file
View File

@@ -0,0 +1,54 @@
transifex_resource = frontend-template-application
transifex_langs = "ar,fr,es_419,zh_CN"
transifex_utils = ./node_modules/.bin/transifex-utils.js
i18n = ./src/i18n
transifex_input = $(i18n)/transifex_input.json
tx_url1 = https://www.transifex.com/api/2/project/edx-platform/resource/$(transifex_resource)/translation/en/strings/
tx_url2 = https://www.transifex.com/api/2/project/edx-platform/resource/$(transifex_resource)/source/
# This directory must match .babelrc .
transifex_temp = ./temp/babel-plugin-react-intl
precommit:
npm run lint
npm audit
requirements:
npm install
i18n.extract:
# Pulling display strings from .jsx files into .json files...
rm -rf $(transifex_temp)
npm run-script i18n_extract
i18n.concat:
# Gathering JSON messages into one file...
$(transifex_utils) $(transifex_temp) $(transifex_input)
extract_translations: | requirements i18n.extract i18n.concat
# Despite the name, we actually need this target to detect changes in the incoming translated message files as well.
detect_changed_source_translations:
# Checking for changed translations...
git diff --exit-code $(i18n)
# Pushes translations to Transifex. You must run make extract_translations first.
push_translations:
# Pushing strings to Transifex...
tx push -s
# Fetching hashes from Transifex...
./node_modules/reactifex/bash_scripts/get_hashed_strings.sh $(tx_url1)
# Writing out comments to file...
$(transifex_utils) $(transifex_temp) --comments
# Pushing comments to Transifex...
./node_modules/reactifex/bash_scripts/put_comments.sh $(tx_url2)
# Pulls translations from Transifex.
pull_translations:
tx pull -f --mode reviewed --language=$(transifex_langs)
# This target is used by Travis.
validate-no-uncommitted-package-lock-changes:
# Checking for package-lock.json changes...
git diff --exit-code package-lock.json

62
README.rst Normal file
View File

@@ -0,0 +1,62 @@
|Build Status| |Codecov| |license|
frontend-template-application
=================================
Please tag **@edx/fedx-team** on any PRs or issues. Thanks.
Introduction
------------
This repository is a template for Open edX micro-frontend applications. It is flagged as a Template Repository, meaning it can be used as a basis for new GitHub repositories by clicking the green "Use this template" button above. The rest of this document describes how to work with your new micro-frontend after you've created a new repository from the template.
After Copying The Template
--------------------------
You'll want to do a find-and-replace to replace all instances of ``frontend-template-application`` with the name of your new repository.
**Prerequisite**
`Devstack <https://edx.readthedocs.io/projects/edx-installing-configuring-and-running/en/latest/installation/index.html>`_. If you start Devstack with ``make dev.up.ecommerce`` that should give you everything you need as a companion to this frontend.
**Installation and Startup**
In the following steps, replace "frontend-template-application' with the name of the repo you created when copying this template above.
1. Clone your new repo:
``git clone https://github.com/edx/frontend-template-application.git``
2. Install npm dependencies:
``cd frontend-template-application && npm install``
3. Start the dev server:
``npm start``
The dev server is running at `http://localhost:8080 <http://localhost:8080>`_.
Project Structure
-----------------
The source for this project is organized into nested submodules according to the ADR `Feature-based Application Organization <https://github.com/edx/frontend-template-application/blob/master/docs/decisions/0002-feature-based-application-organization.rst>`_.
Build Process Notes
-------------------
**Production Build**
The production build is created with ``npm run build``.
Internationalization
--------------------
Please see `edx/frontend-platform's i18n module <https://edx.github.io/frontend-platform/module-Internationalization.html>`_ for documentation on internationalization. The documentation explains how to use it, and the `How To <https://github.com/edx/frontend-i18n/blob/master/docs/how_tos/i18n.rst>`_ has more detail.
.. |Build Status| image:: https://api.travis-ci.org/edx/frontend-template-application.svg?branch=master
:target: https://travis-ci.org/edx/frontend-template-application
.. |Codecov| image:: https://codecov.io/gh/edx/frontend-template-application/branch/master/graph/badge.svg
:target: https://codecov.io/gh/edx/frontend-template-application
.. |license| image:: https://img.shields.io/npm/l/@edx/frontend-template-application.svg
:target: @edx/frontend-template-application

10
codecov.yml Normal file
View File

@@ -0,0 +1,10 @@
coverage:
status:
project:
default:
target: auto
threshold: 0%
patch:
default:
target: auto
threshold: 0%

View File

@@ -0,0 +1,32 @@
1. Record Architecture Decisions
--------------------------------
Status
------
Accepted
Context
-------
We would like to keep a historical record on the architectural
decisions we make with this app as it evolves over time.
Decision
--------
We will use Architecture Decision Records, as described by
Michael Nygard in `Documenting Architecture Decisions`_
.. _Documenting Architecture Decisions: http://thinkrelevance.com/blog/2011/11/15/documenting-architecture-decisions
Consequences
------------
See Michael Nygard's article, linked above.
References
----------
* https://resources.sei.cmu.edu/asset_files/Presentation/2017_017_001_497746.pdf
* https://github.com/npryce/adr-tools/tree/master/doc/adr

View File

@@ -0,0 +1,213 @@
2. Feature-based Application Organization
-----------------------------------------
Status
------
Proposed
Context
-------
The common, naive approach to organizing React/Redux applications says that code should be grouped into folders by type, putting like-types of code together. This means having directories like:
- components
- actions
- reducers
- constants
- selectors
- sagas
- services
This is often referred to as a "Ruby on Rails" approach, which organizes applications similarly.
As applications grow, it's acknowledged by the community that this organization starts to fall down and become difficult to maintain. It does nothing to help engineers keep their code modular and decoupled, as it groups code by how it looks and not how it's used. Code that functions as part of a unit is spread out over 7+ directories.
This ADR documents an approach and rules of thumb for organizing code modularly by feature, informed by articles and prior art.
Note on terminology: "feature" and "module" are used interchangeably in this ADR. In general, the feature refers to the semantically significant thing, whereas the module refers to the directory of code pertaining to that feature.
Decision
--------
**Following the spirit of these principles is more important than following them to the letter.**
These rules are guidelines. It won't always be reasonable or necessary to follow them to the letter. They provide a set of tools for dealing with complexity. It follows, then, that if you don't have complexity, then you may not need the tools.
Primary guiding principles
==========================
**1. Code is organized into feature directories.**
A feature is a logical or semantically significant grouping of code; what comprises a feature is subjective. It also may not be obvious at first - if code tends to be related or change together, then it's probably part of the same feature.
It's unlikely to be worth agonizing over your feature breakdown; time will tell what's correct moreso than overthinking it. That said, a sufficiently complex set of features will need a similarly robust taxonomy and organizational hierarchy. (This document endeavors to help inform that hierarchy.) A nice rule of thumb is that a feature should conceptually be able to be extracted into its own npm package with minimal effort.
**2. Create strict module boundaries**
A module should have a public interface exposed via an index.js file in the module directory. Consumers of a feature should limit themselves to importing only from the public exports.
::
import { MyComponent, reducer as myComponentReducer } from './submodule'; // Good
import MyComponent from './submodule/MyComponent'; // Bad
import reducer from './submodule/data/reducers'; // Bad
Modules are configured by their parent. Generally a module will expose a few things which need to be configured make use of them in the consuming code. The reason for doing this is so that the module doesn't make assumptions about it's context (effectively dependency injection).
Examples:
* For React components, this involves including them in JSX and giving them props.
* For services, this is calling their "configure" method and providing them apiClient/configuration, etc.
* For reducers, this is mounting the reducer at an agreed-upon place in the redux store's state tree.
**3. Avoid circular dependencies**
Circular dependencies are unresolvable in webpack, and will result in something being imported as 'undefined'. They're also incredibly difficult and frustrating to track down. Properly factoring your features and supporting modules should help avoid these sorts of issues. In general, a feature should never need to import from its parent or grandparents, and a more "general purpose" module should never be importing from a more specific one. If you find yourself importing from a domain-specific feature in your general utility module, then something is probably ill-factored.
File and directory naming
=========================
This section details a specific taxonomy and hierarchy to help make code modular, approachable and maintainable.
**A. Separate data management from components.**
In order to isolate our view layer (React) from the management of our data, global state, APIs, and side effects, we want to adopt the "ducks" organization (see references). This involves isolating data management into a
sub directory of a feature. We'll use the directory name "data" rather than the traditional "ducks".
**C. React components will be named semantically.**
The convention for React components is for the file to be named for what the component does, so we will preserve this. A given feature may break up its view layer into multiple sub-components without a sub-feature being present.
**B. Files in a module's data directory are named by function.**
In the data sub-directory, the file names describe what each piece of code does. Unlike React components, all of the data handlers (actions, reducers, sagas, selectors, services, etc.) are generally short functions, and so we put them all in the same file together with others of their kind.
::
/profile
/index.js // public interface
/ProfilePage.jsx // view
/ProfilePhotoUploader.jsx // supporting view
/data // Note: most files here are named with a plural, as they contain many of the things in question.
/actions.js
/constants.js
/reducers.js
/sagas.js
/selectors.js
/service.js // Note: singular - there's one 'service' here that provides many methods.
If you find yourself desiring to have multiple files of a particular type in the data directory, this is a strong sign that you actually need a sub-feature instead.
**C. Sub-features follow the same naming scheme.**
Sub-features should follow the same rules as any other module.
A module with a sub-module:
::
/profile
/index.js // public interface
/ProfilePage.jsx
/Avatar.jsx // additional components for a feature reside here at the top level, not in a "components" subdirectory.
/data
/actions.js
/reducers.js
/sagas.js
/service.js
/profile-photo
/index.js // public interface
/ProfilePhoto.jsx
/data
/actions.js
/reducers.js
/selectors.js
/education // Sparse sub-module
/index.js // public interface
/Education.jsx
/site-language // No view layer sub-module
/index.js // public interface
/data
/actions.js
/reducers.js
Note that a given feature need not contain files of all types, nor is having files of all types a prerequisite for having a feature. A feature may not contain a view (Component) layer, or in contrast to that, may not need a data directory at all!
Importing rules of thumb
========================
It can be difficult to figure out where it's okay to import from. Following these rules of thumb will help maintain a healthy code organization and should prevent the possibility of circular dependencies.
**I. A feature may not import from its parentage.**
As described above in "Avoid circular dependencies", features should not import from their parent, grandparent, etc. A feature should be agnostic to the context in which it is used. If a module is importing from its parent or grandparent, that implies something is ill-factored.
**II. A feature may import from its children, but not its grandchildren.**
The feature may only import from the exports of its child, which may include exports of the grandchildren. Importing directly from grandchildren (or great grandchildren, etc.) would violate the strict module boundary of the child.
**II. Features may import from their siblings.**
It's acceptable to import from a module's siblings, or the siblings of their parents, grandparents, etc. This is necessary to support code re-use. As an example, assume we have a sub-module with common code to support our web forms.
::
/feature1
/sub-form-1
/sub-form-2
/forms-common-code
The sub-form modules can import from forms-common-code. The latter has its own strict module boundary and could conceptually be extracted into its own repository/completely independent module as far as they're concerned. They're unaware, conceptually, that it's a child of feature1, and they don't care.
**III. Features may import from the siblings of their parentage.**
This is less intuitive, but is not really any different than the above.
If another feature (feature2) also needs forms-common-code, it should be brought up a level so it's available to feature2, as feature2 cannot "reach into" feature1:
::
/feature1
/sub-form-1
/sub-form-2
/forms-common-code
/feature2 // can now use forms-common-code
In a complex app, you could imagine that forms-common-code needs to be brought up several levels, in which case our imports might look like:
::
import { formStuff } from '../../../forms-common-code';
This is okay. Conceptually it's no different than importing from a third party npm package, we just happen to know the code we want is up a few directories nearby, rather than using the syntactic sugar of a pathless import from node_modules.
At some point, if forms-common-code is general purpose enough, we may want to extract it from this repository/set of features all together.
Consequences
------------
This organization has been implemented in several of our micro-frontends so far (frontend-app-account and frontend-app-payment most significantly) and we feel it has improved the organization and approachability of the apps. When converting frontend-app-account to use this organization, it took 2-3 days to refactor the code.
It's worth noting that to get this right, it may actually involve changing the way the modules interact with each other. It isn't as simple as just moving files around and copy/pasting code. For instance, in frontend-app-account, it became obvious very quickly that to create strict module boundaries, we had to change the way that our service layers (server requests) were configured to keep them from importing their own configuration from their parent/grandparent. Similarly, our redux store tree of reducers became more complex and deeply nested.
References
----------
Articles on react/redux application organization:
* Primary reference:
- https://jaysoo.ca/2016/02/28/organizing-redux-application/
* Ducks references:
- https://github.com/erikras/ducks-modular-redux
- https://medium.freecodecamp.org/scaling-your-redux-app-with-ducks-6115955638be
* Other reading:
- https://hackernoon.com/fractal-a-react-app-structure-for-infinite-scale-4dab943092af
- https://marmelab.com/blog/2015/12/17/react-directory-structure.html
- https://redux.js.org/faq/code-structure

5
docs/how_tos/i18n.rst Normal file
View File

@@ -0,0 +1,5 @@
####################
React App i18n HOWTO
####################
This document has moved to the frontend-platform repo: https://github.com/edx/frontend-platform/blob/master/docs/how_tos/i18n.rst

11
jest.config.js Normal file
View File

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

6
openedx.yaml Normal file
View File

@@ -0,0 +1,6 @@
# This file describes this Open edX repo, as described in OEP-2:
# http://open-edx-proposals.readthedocs.io/en/latest/oeps/oep-0002.html#specification
nick: tmpa
oeps: {}
owner: edx/arch-team

20175
package-lock.json generated Normal file

File diff suppressed because it is too large Load Diff

64
package.json Normal file
View File

@@ -0,0 +1,64 @@
{
"name": "@edx/frontend-template-application",
"version": "0.1.0",
"description": "Frontend application template",
"repository": {
"type": "git",
"url": "git+https://github.com/edx/frontend-template-application.git"
},
"browserslist": [
"last 2 versions",
"ie 11"
],
"scripts": {
"build": "fedx-scripts webpack",
"i18n_extract": "BABEL_ENV=i18n fedx-scripts babel src --quiet > /dev/null",
"is-es5": "es-check es5 ./dist/*.js",
"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 --passWithNoTests"
},
"husky": {
"hooks": {
"pre-commit": "npm run lint"
}
},
"author": "edX",
"license": "AGPL-3.0",
"homepage": "https://github.com/edx/frontend-template-application#readme",
"publishConfig": {
"access": "public"
},
"bugs": {
"url": "https://github.com/edx/frontend-template-application/issues"
},
"dependencies": {
"@edx/frontend-component-footer": "10.0.9",
"@edx/frontend-component-header": "2.0.5",
"@edx/frontend-platform": "1.1.14",
"@edx/paragon": "7.2.1",
"@fortawesome/fontawesome-svg-core": "1.2.28",
"@fortawesome/free-brands-svg-icons": "5.11.2",
"@fortawesome/free-regular-svg-icons": "5.11.2",
"@fortawesome/free-solid-svg-icons": "5.11.2",
"@fortawesome/react-fontawesome": "0.1.9",
"babel-polyfill": "6.26.0",
"prop-types": "15.7.2",
"react": "16.12.0",
"react-dom": "16.12.0",
"react-redux": "7.1.3",
"react-router": "5.1.2",
"react-router-dom": "5.1.2",
"redux": "4.0.5"
},
"devDependencies": {
"@edx/frontend-build": "^3.0.0",
"codecov": "3.6.5",
"es-check": "5.1.0",
"glob": "7.1.6",
"husky": "3.1.0",
"jest": "24.9.0",
"reactifex": "1.1.1"
}
}

12
public/index.html Normal file
View File

@@ -0,0 +1,12 @@
<!doctype html>
<html lang="en-us">
<head>
<title>Application Template | edX</title>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<link rel="shortcut icon" href="/favicon.ico" type="image/x-icon" />
</head>
<body>
<div id="root"></div>
</body>
</html>

9
renovate.json Normal file
View File

@@ -0,0 +1,9 @@
{
"extends": [
"config:base"
],
"patch": {
"automerge": true
},
"rebaseStalePrs": true
}

BIN
src/assets/favicon.ico Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 34 KiB

View File

@@ -0,0 +1,12 @@
import React from 'react';
export default function ExamplePage() {
return (
<main>
<div className="container-fluid">
<h1>Example Page</h1>
<p>Hello world!</p>
</div>
</main>
);
}

View File

@@ -0,0 +1,5 @@
describe('example', () => {
it('will pass because it is an example', () => {
});
});

View File

View File

@@ -0,0 +1,4 @@
data folder
===========
This folder is the home for non-component files, such as redux reducers, actions, selectors, API client services, etc. See `Feature-based Application Organization <https://github.com/edx/frontend-template-application/blob/master/docs/decisions/0002-feature-based-application-organization.rst>`_. for more detail.

0
src/example/index.scss Normal file
View File

32
src/i18n/index.jsx Normal file
View File

@@ -0,0 +1,32 @@
import arMessages from './messages/ar.json';
import caMessages from './messages/ca.json';
// no need to import en messages-- they are in the defaultMessage field
import es419Messages from './messages/es_419.json';
import frMessages from './messages/fr.json';
import zhcnMessages from './messages/zh_CN.json';
import heMessages from './messages/he.json';
import idMessages from './messages/id.json';
import kokrMessages from './messages/ko_kr.json';
import plMessages from './messages/pl.json';
import ptbrMessages from './messages/pt_br.json';
import ruMessages from './messages/ru.json';
import thMessages from './messages/th.json';
import ukMessages from './messages/uk.json';
const messages = {
ar: arMessages,
'es-419': es419Messages,
fr: frMessages,
'zh-cn': zhcnMessages,
ca: caMessages,
he: heMessages,
id: idMessages,
'ko-kr': kokrMessages,
pl: plMessages,
'pt-br': ptbrMessages,
ru: ruMessages,
th: thMessages,
uk: ukMessages,
};
export default messages;

View File

@@ -0,0 +1 @@
{}

View File

@@ -0,0 +1 @@
{}

View File

@@ -0,0 +1 @@
{}

View File

@@ -0,0 +1 @@
{}

View File

@@ -0,0 +1 @@
{}

View File

@@ -0,0 +1 @@
{}

View File

@@ -0,0 +1 @@
{}

View File

@@ -0,0 +1 @@
{}

View File

@@ -0,0 +1 @@
{}

View File

@@ -0,0 +1 @@
{}

View File

@@ -0,0 +1 @@
{}

View File

@@ -0,0 +1 @@
{}

View File

@@ -0,0 +1 @@
{}

40
src/index.jsx Executable file
View File

@@ -0,0 +1,40 @@
import 'babel-polyfill';
import {
APP_INIT_ERROR, APP_READY, subscribe, initialize,
} from '@edx/frontend-platform';
import { AppProvider, ErrorPage } from '@edx/frontend-platform/react';
import React from 'react';
import ReactDOM from 'react-dom';
import Header, { messages as headerMessages } from '@edx/frontend-component-header';
import Footer, { messages as footerMessages } from '@edx/frontend-component-footer';
import appMessages from './i18n';
import ExamplePage from './example/ExamplePage';
import './index.scss';
import './assets/favicon.ico';
subscribe(APP_READY, () => {
ReactDOM.render(
<AppProvider>
<Header />
<ExamplePage />
<Footer />
</AppProvider>,
document.getElementById('root'),
);
});
subscribe(APP_INIT_ERROR, (error) => {
ReactDOM.render(<ErrorPage message={error.message} />, document.getElementById('root'));
});
initialize({
messages: [
appMessages,
headerMessages,
footerMessages,
],
});

6
src/index.scss Executable file
View File

@@ -0,0 +1,6 @@
@import '~@edx/paragon/scss/edx/theme.scss';
@import './example/index.scss';
@import "~@edx/frontend-component-header/dist/index";
@import "~@edx/frontend-component-footer/dist/footer";

1
src/setupTest.js Executable file
View File

@@ -0,0 +1 @@
import 'babel-polyfill';