diff --git a/package.json b/package.json
index 9343f35..fb5cb65 100644
--- a/package.json
+++ b/package.json
@@ -3,18 +3,91 @@
"version": "0.1.0",
"private": true,
"dependencies": {
+ "@fortawesome/fontawesome-svg-core": "^1.2.28",
+ "@fortawesome/free-solid-svg-icons": "^5.13.0",
+ "@fortawesome/react-fontawesome": "^0.1.9",
+ "@svgr/webpack": "4.3.3",
"@testing-library/jest-dom": "^4.2.4",
"@testing-library/react": "^9.5.0",
"@testing-library/user-event": "^7.2.1",
+ "@typescript-eslint/eslint-plugin": "^2.10.0",
+ "@typescript-eslint/parser": "^2.10.0",
+ "babel-eslint": "10.1.0",
+ "babel-jest": "^24.9.0",
+ "babel-loader": "8.1.0",
+ "babel-plugin-named-asset-import": "^0.3.6",
+ "babel-preset-react-app": "^9.1.2",
+ "bootstrap": "^4.4.1",
+ "camelcase": "^5.3.1",
+ "case-sensitive-paths-webpack-plugin": "2.3.0",
+ "css-loader": "3.4.2",
+ "dotenv": "8.2.0",
+ "dotenv-expand": "5.1.0",
+ "eslint": "^6.6.0",
+ "eslint-config-react-app": "^5.2.1",
+ "eslint-loader": "3.0.3",
+ "eslint-plugin-flowtype": "4.6.0",
+ "eslint-plugin-import": "2.20.1",
+ "eslint-plugin-jsx-a11y": "6.2.3",
+ "eslint-plugin-react": "7.19.0",
+ "eslint-plugin-react-hooks": "^1.6.1",
+ "file-loader": "4.3.0",
+ "fs-extra": "^8.1.0",
+ "gapi-script": "^1.0.2",
+ "html-webpack-plugin": "4.0.0-beta.11",
+ "identity-obj-proxy": "3.0.0",
+ "jest": "24.9.0",
+ "jest-environment-jsdom-fourteen": "1.0.1",
+ "jest-resolve": "24.9.0",
+ "jest-watch-typeahead": "0.4.2",
+ "jquery": "^3.4.1",
+ "mini-css-extract-plugin": "0.9.0",
+ "node-sass": "^4.13.1",
+ "optimize-css-assets-webpack-plugin": "5.0.3",
+ "pnp-webpack-plugin": "1.6.4",
+ "postcss-flexbugs-fixes": "4.1.0",
+ "postcss-loader": "3.0.0",
+ "postcss-normalize": "8.0.1",
+ "postcss-preset-env": "6.7.0",
+ "postcss-safe-parser": "4.0.1",
"react": "^16.13.1",
+ "react-app-polyfill": "^1.0.6",
+ "react-dev-utils": "^10.2.1",
"react-dom": "^16.13.1",
- "react-scripts": "3.4.1"
+ "react-input-range": "^1.3.0",
+ "react-loadable": "^5.5.0",
+ "react-modal": "^3.11.2",
+ "react-redux": "^7.2.0",
+ "react-router-dom": "^5.1.2",
+ "react-test-renderer": "^16.13.1",
+ "redux": "^4.0.5",
+ "redux-actions": "^2.6.5",
+ "redux-analytics": "^0.3.1",
+ "redux-thunk": "^2.3.0",
+ "reselect": "^4.0.0",
+ "reselect-immutable-helpers": "^1.2.2",
+ "resolve": "1.15.0",
+ "resolve-url-loader": "3.1.1",
+ "sass-loader": "8.0.2",
+ "semver": "6.3.0",
+ "style-loader": "0.23.1",
+ "terser-webpack-plugin": "2.3.5",
+ "ts-pnp": "1.1.6",
+ "typescript": "^3.8.3",
+ "url-loader": "2.3.0",
+ "webpack": "4.42.0",
+ "webpack-dev-server": "3.10.3",
+ "webpack-manifest-plugin": "2.2.0",
+ "workbox-webpack-plugin": "4.3.1"
},
"scripts": {
"start": "react-scripts start",
"build": "react-scripts build",
"test": "react-scripts test",
- "eject": "react-scripts eject"
+ "eject": "react-scripts eject",
+ "generate": "plop",
+ "predeploy": "npm run build",
+ "deploy": "gh-pages -d build"
},
"eslintConfig": {
"extends": "react-app"
@@ -30,5 +103,15 @@
"last 1 firefox version",
"last 1 safari version"
]
+ },
+ "devDependencies": {
+ "@babel/cli": "^7.8.4",
+ "@babel/core": "^7.9.0",
+ "@babel/plugin-transform-react-jsx": "^7.9.4",
+ "@babel/preset-env": "^7.9.0",
+ "@babel/preset-react": "^7.9.4",
+ "body-parser": "^1.19.0",
+ "gh-pages": "^2.2.0",
+ "plop": "^2.6.0"
}
}
diff --git a/plop-templates/Component/Component.component.scss.hbs b/plop-templates/Component/Component.component.scss.hbs
new file mode 100644
index 0000000..6fa764d
--- /dev/null
+++ b/plop-templates/Component/Component.component.scss.hbs
@@ -0,0 +1,3 @@
+.c-{{pascalCase name}} {
+
+}
diff --git a/plop-templates/Component/Component.jsx.hbs b/plop-templates/Component/Component.jsx.hbs
new file mode 100644
index 0000000..9f9549d
--- /dev/null
+++ b/plop-templates/Component/Component.jsx.hbs
@@ -0,0 +1,20 @@
+import React from 'react';
+import PropTypes from 'prop-types';
+import './{{pascalCase name}}.component.scss';
+
+const {{pascalCase name}} = props => {
+ return (
+
+
+ );
+};
+
+{{pascalCase name}}.defaultProps = {
+
+};
+
+{{pascalCase name}}.propTypes = {
+
+};
+
+export default {{pascalCase name}};
\ No newline at end of file
diff --git a/plop-templates/Component/Component.test.js.hbs b/plop-templates/Component/Component.test.js.hbs
new file mode 100644
index 0000000..9458a88
--- /dev/null
+++ b/plop-templates/Component/Component.test.js.hbs
@@ -0,0 +1,8 @@
+import React from 'react';
+import {{pascalCase name}} from './{{pascalCase name}}';
+
+describe('{{pascalCase name}}', () => {
+ it('renders without error', () => {
+
+ });
+});
\ No newline at end of file
diff --git a/plop-templates/Component/index.js.hbs b/plop-templates/Component/index.js.hbs
new file mode 100644
index 0000000..76e8f44
--- /dev/null
+++ b/plop-templates/Component/index.js.hbs
@@ -0,0 +1,3 @@
+import {{pascalCase name}} from './{{pascalCase name}}.jsx';
+
+export default {{pascalCase name}};
\ No newline at end of file
diff --git a/plop-templates/Page/Page.jsx.hbs b/plop-templates/Page/Page.jsx.hbs
new file mode 100644
index 0000000..95c6c94
--- /dev/null
+++ b/plop-templates/Page/Page.jsx.hbs
@@ -0,0 +1,20 @@
+import React from 'react';
+import PropTypes from 'prop-types';
+import './{{pascalCase name}}.module.scss';
+
+const {{pascalCase name}} = props => {
+ return (
+
+
+ );
+};
+
+{{pascalCase name}}.defaultProps = {
+
+};
+
+{{pascalCase name}}.propTypes = {
+
+};
+
+export default {{pascalCase name}};
\ No newline at end of file
diff --git a/plop-templates/Page/Page.module.scss.hbs b/plop-templates/Page/Page.module.scss.hbs
new file mode 100644
index 0000000..6fa764d
--- /dev/null
+++ b/plop-templates/Page/Page.module.scss.hbs
@@ -0,0 +1,3 @@
+.c-{{pascalCase name}} {
+
+}
diff --git a/plop-templates/Page/Page.test.js.hbs b/plop-templates/Page/Page.test.js.hbs
new file mode 100644
index 0000000..9458a88
--- /dev/null
+++ b/plop-templates/Page/Page.test.js.hbs
@@ -0,0 +1,8 @@
+import React from 'react';
+import {{pascalCase name}} from './{{pascalCase name}}';
+
+describe('{{pascalCase name}}', () => {
+ it('renders without error', () => {
+
+ });
+});
\ No newline at end of file
diff --git a/plop-templates/Page/index.js.hbs b/plop-templates/Page/index.js.hbs
new file mode 100644
index 0000000..76e8f44
--- /dev/null
+++ b/plop-templates/Page/index.js.hbs
@@ -0,0 +1,3 @@
+import {{pascalCase name}} from './{{pascalCase name}}.jsx';
+
+export default {{pascalCase name}};
\ No newline at end of file
diff --git a/plop-templates/hook.js.hbs b/plop-templates/hook.js.hbs
new file mode 100644
index 0000000..556a48d
--- /dev/null
+++ b/plop-templates/hook.js.hbs
@@ -0,0 +1,5 @@
+const {{camelCase name}} = () => {
+
+};
+
+export default {{camelCase name}};
\ No newline at end of file
diff --git a/plop-templates/injectable-index.js.hbs b/plop-templates/injectable-index.js.hbs
new file mode 100644
index 0000000..6c45966
--- /dev/null
+++ b/plop-templates/injectable-index.js.hbs
@@ -0,0 +1,5 @@
+/* PLOP_INJECT_IMPORT */
+
+export {
+ /* PLOP_INJECT_EXPORT */
+}
\ No newline at end of file
diff --git a/plop-templates/service.js.hbs b/plop-templates/service.js.hbs
new file mode 100644
index 0000000..c5697ba
--- /dev/null
+++ b/plop-templates/service.js.hbs
@@ -0,0 +1,17 @@
+const create{{pascalCase name}} = () => {
+ let examplePrivateVariable = 0
+
+ return {
+ getExamplePrivateVariable: () => {
+ return examplePrivateVariable
+ },
+ setExamplePrivateVariable: (n) => {
+ examplePrivateVariable = n
+ }
+ }
+};
+
+const singleton = create{{pascalCase name}}();
+Object.freeze(singleton);
+
+export default singleton;
\ No newline at end of file
diff --git a/plopfile.js b/plopfile.js
new file mode 100644
index 0000000..2ab4469
--- /dev/null
+++ b/plopfile.js
@@ -0,0 +1,192 @@
+module.exports = plop => {
+ plop.setGenerator('component', {
+ description: 'Create a component',
+ // User input prompts provided as arguments to the template
+ prompts: [
+ {
+ // Raw text input
+ type: 'input',
+ // Variable name for this input
+ name: 'name',
+ // Prompt to display on command line
+ message: 'What is your component name?'
+ },
+ ],
+ actions: [
+ {
+ type: 'add',
+ // Plop will create directories for us if they do not exist
+ // so it's okay to add files in nested locations.
+ path: 'src/app/components/molecules/{{pascalCase name}}/{{pascalCase name}}.jsx',
+ templateFile:
+ 'plop-templates/Component/Component.jsx.hbs',
+ },
+ {
+ type: 'add',
+ path: 'src/app/components/molecules/{{pascalCase name}}/{{pascalCase name}}.test.js',
+ templateFile:
+ 'plop-templates/Component/Component.test.js.hbs',
+ },
+ {
+ type: 'add',
+ path:
+ 'src/app/components/molecules/{{pascalCase name}}/{{pascalCase name}}.component.scss',
+ templateFile:
+ 'plop-templates/Component/Component.component.scss.hbs',
+ },
+ {
+ type: 'add',
+ path: 'src/app/components/molecules/{{pascalCase name}}/index.js',
+ templateFile: 'plop-templates/Component/index.js.hbs',
+ },
+ {
+ // Adds an index.js file if it does not already exist
+ type: 'add',
+ path: 'src/app/components/index.js',
+ templateFile: 'plop-templates/injectable-index.js.hbs',
+ // If index.js already exists in this location, skip this action
+ skipIfExists: true,
+ },
+ {
+ // Action type 'append' injects a template into an existing file
+ type: 'append',
+ path: 'src/app/components/index.js',
+ // Pattern tells plop where in the file to inject the template
+ pattern: `/* PLOP_INJECT_IMPORT */`,
+ template: `import {{pascalCase name}} from './molecules/{{pascalCase name}}';`,
+ },
+ {
+ type: 'append',
+ path: 'src/app/components/index.js',
+ pattern: `/* PLOP_INJECT_EXPORT */`,
+ template: `\t{{pascalCase name}},`,
+ },
+ ],
+ });
+ plop.setGenerator('page', {
+ description: 'Create a page',
+ prompts: [
+ {
+ type: 'input',
+ name: 'name',
+ message: 'What is your page name?',
+ },
+ ],
+ actions: [
+ {
+ type: 'add',
+ path: 'src/app/pages/{{pascalCase name}}/{{pascalCase name}}.jsx',
+ templateFile:
+ 'plop-templates/Page/Page.jsx.hbs',
+ },
+ {
+ type: 'add',
+ path: 'src/app/pages/{{pascalCase name}}/{{pascalCase name}}.test.js',
+ templateFile:
+ 'plop-templates/Page/Page.test.js.hbs',
+ },
+ {
+ type: 'add',
+ path:
+ 'src/app/pages/{{pascalCase name}}/{{pascalCase name}}.module.scss',
+ templateFile:
+ 'plop-templates/Page/Page.module.scss.hbs',
+ },
+ {
+ type: 'add',
+ path: 'src/app/pages/{{pascalCase name}}/index.js',
+ templateFile: 'plop-templates/Page/index.js.hbs',
+ },
+ {
+ type: 'add',
+ path: 'src/app/pages/index.js',
+ templateFile: 'plop-templates/injectable-index.js.hbs',
+ skipIfExists: true,
+ },
+ {
+ type: 'append',
+ path: 'src/app/pages/index.js',
+ pattern: `/* PLOP_INJECT_IMPORT */`,
+ template: `import {{pascalCase name}} from './{{pascalCase name}}';`,
+ },
+ {
+ type: 'append',
+ path: 'src/app/pages/index.js',
+ pattern: `/* PLOP_INJECT_EXPORT */`,
+ template: `\t{{pascalCase name}},`,
+ },
+ ],
+ })
+
+ plop.setGenerator('service', {
+ description: 'Create service',
+ prompts: [
+ {
+ type: 'input',
+ name: 'name',
+ message: 'What is your service name?',
+ },
+ ],
+ actions: [
+ {
+ type: 'add',
+ path: 'src/app/services/{{camelCase name}}.js',
+ templateFile: 'plop-templates/service.js.hbs',
+ },
+ {
+ type: 'add',
+ path: 'src/app/services/index.js',
+ templateFile: 'plop-templates/injectable-index.js.hbs',
+ skipIfExists: true,
+ },
+ {
+ type: 'append',
+ path: 'src/app/services/index.js',
+ pattern: `/* PLOP_INJECT_IMPORT */`,
+ template: `import {{camelCase name}} from './{{camelCase name}}';`,
+ },
+ {
+ type: 'append',
+ path: 'src/app/services/index.js',
+ pattern: `/* PLOP_INJECT_EXPORT */`,
+ template: `\t{{camelCase name}},`,
+ }
+ ],
+ })
+
+ plop.setGenerator('hook', {
+ description: 'Create a custom react hook',
+ prompts: [
+ {
+ type: 'input',
+ name: 'name',
+ message: 'What is your hook name?',
+ },
+ ],
+ actions: [
+ {
+ type: 'add',
+ path: 'src/app/hooks/{{camelCase name}}.js',
+ templateFile: 'plop-templates/hook.js.hbs',
+ },
+ {
+ type: 'add',
+ path: 'src/app/hooks/index.js',
+ templateFile: 'plop-templates/injectable-index.js.hbs',
+ skipIfExists: true,
+ },
+ {
+ type: 'append',
+ path: 'src/app/hooks/index.js',
+ pattern: `/* PLOP_INJECT_IMPORT */`,
+ template: `import {{camelCase name}} from './{{camelCase name}}';`,
+ },
+ {
+ type: 'append',
+ path: 'src/app/hooks/index.js',
+ pattern: `/* PLOP_INJECT_EXPORT */`,
+ template: `\t{{camelCase name}},`,
+ }
+ ],
+ })
+ };
\ No newline at end of file
diff --git a/src/App.css b/src/App.css
deleted file mode 100644
index 74b5e05..0000000
--- a/src/App.css
+++ /dev/null
@@ -1,38 +0,0 @@
-.App {
- text-align: center;
-}
-
-.App-logo {
- height: 40vmin;
- pointer-events: none;
-}
-
-@media (prefers-reduced-motion: no-preference) {
- .App-logo {
- animation: App-logo-spin infinite 20s linear;
- }
-}
-
-.App-header {
- background-color: #282c34;
- min-height: 100vh;
- display: flex;
- flex-direction: column;
- align-items: center;
- justify-content: center;
- font-size: calc(10px + 2vmin);
- color: white;
-}
-
-.App-link {
- color: #61dafb;
-}
-
-@keyframes App-logo-spin {
- from {
- transform: rotate(0deg);
- }
- to {
- transform: rotate(360deg);
- }
-}
diff --git a/src/App.js b/src/App.js
deleted file mode 100644
index ce9cbd2..0000000
--- a/src/App.js
+++ /dev/null
@@ -1,26 +0,0 @@
-import React from 'react';
-import logo from './logo.svg';
-import './App.css';
-
-function App() {
- return (
-
- );
-}
-
-export default App;
diff --git a/src/App.test.js b/src/App.test.js
deleted file mode 100644
index 4db7ebc..0000000
--- a/src/App.test.js
+++ /dev/null
@@ -1,9 +0,0 @@
-import React from 'react';
-import { render } from '@testing-library/react';
-import App from './App';
-
-test('renders learn react link', () => {
- const { getByText } = render();
- const linkElement = getByText(/learn react/i);
- expect(linkElement).toBeInTheDocument();
-});
diff --git a/src/app/actions.js b/src/app/actions.js
new file mode 100644
index 0000000..308f403
--- /dev/null
+++ b/src/app/actions.js
@@ -0,0 +1,2 @@
+export const initializeApp = () => {
+}
\ No newline at end of file
diff --git a/src/app/components/index.js b/src/app/components/index.js
new file mode 100644
index 0000000..82a577e
--- /dev/null
+++ b/src/app/components/index.js
@@ -0,0 +1,61 @@
+/* PLOP_INJECT_IMPORT */
+import SortTool from './molecules/SortTool';
+import FilterTool from './molecules/FilterTool';
+import ProductContainerWithTools from './molecules/ProductContainerWithTools';
+import Modal from './molecules/Modal';
+import QuantityControlWidget from './molecules/QuantityControlWidget';
+import ItemPrice from './molecules/ItemPrice';
+import SectionLoader from './molecules/SectionLoader';
+import CartSummary from './molecules/CartSummary';
+import CartItem from './molecules/CartItem';
+import CartList from './molecules/CartList';
+import Footer from './molecules/Footer';
+import ProductTile from './molecules/ProductTile';
+import ProductContainer from './molecules/ProductContainer';
+import FilterModal from './molecules/FilterModal';
+import SortModal from './molecules/SortModal';
+import SortAndFilterPanel from './molecules/SortAndFilterPanel';
+import CartIcon from './molecules/CartIcon';
+import Search from './molecules/Search';
+import Jumbotron from './molecules/common/Jumbotron';
+import FormFieldContainer from './molecules/common/FormFieldContainer';
+import Form from './molecules/common/Form';
+import SocialLogin from './molecules/SocialLogin';
+import LoginForm from './molecules/LoginForm';
+import PageLoader from './molecules/PageLoader';
+import Header from './molecules/Header'
+import Button from './atoms/Button';
+import InputField from './atoms/InputField';
+import SelectOption from './atoms/SelectOption';
+
+export {
+ /* PLOP_INJECT_EXPORT */
+ SortTool,
+ FilterTool,
+ ProductContainerWithTools,
+ Modal,
+ QuantityControlWidget,
+ ItemPrice,
+ SectionLoader,
+ CartSummary,
+ CartItem,
+ CartList,
+ Footer,
+ ProductTile,
+ ProductContainer,
+ FilterModal,
+ SortModal,
+ Button,
+ SortAndFilterPanel,
+ CartIcon,
+ Search,
+ Jumbotron,
+ FormFieldContainer,
+ Form,
+ Header,
+ SocialLogin,
+ LoginForm,
+ SelectOption,
+ InputField,
+ PageLoader,
+}
\ No newline at end of file
diff --git a/src/app/components/molecules/Footer/Footer.component.scss b/src/app/components/molecules/Footer/Footer.component.scss
new file mode 100644
index 0000000..fe60d51
--- /dev/null
+++ b/src/app/components/molecules/Footer/Footer.component.scss
@@ -0,0 +1,19 @@
+@import './../../../styles/variables';
+
+.c-Footer {
+ display: flex;
+ align-items: center;
+ justify-content: center;
+ position: fixed;
+ left: 0;
+ bottom: 0;
+ width: 100%;
+ background-color: $brand-color;
+ color: $font-color-light;
+ padding: 0.7em;
+
+ p {
+ margin-bottom: 0;
+ font-weight: bold;
+ }
+}
diff --git a/src/app/components/molecules/Footer/Footer.jsx b/src/app/components/molecules/Footer/Footer.jsx
new file mode 100644
index 0000000..ac0dca3
--- /dev/null
+++ b/src/app/components/molecules/Footer/Footer.jsx
@@ -0,0 +1,21 @@
+import React from 'react';
+import PropTypes from 'prop-types';
+import styles from './Footer.component.scss';
+
+const Footer = props => {
+ return (
+
+ );
+};
+
+Footer.defaultProps = {
+
+};
+
+Footer.propTypes = {
+
+};
+
+export default Footer;
\ No newline at end of file
diff --git a/src/app/components/molecules/Footer/Footer.test.js b/src/app/components/molecules/Footer/Footer.test.js
new file mode 100644
index 0000000..aaac431
--- /dev/null
+++ b/src/app/components/molecules/Footer/Footer.test.js
@@ -0,0 +1,8 @@
+import React from 'react';
+import Footer from './Footer';
+
+describe('Footer', () => {
+ it('renders without error', () => {
+
+ });
+});
\ No newline at end of file
diff --git a/src/app/components/molecules/Footer/index.js b/src/app/components/molecules/Footer/index.js
new file mode 100644
index 0000000..0959cf6
--- /dev/null
+++ b/src/app/components/molecules/Footer/index.js
@@ -0,0 +1,3 @@
+import Footer from './Footer.jsx';
+
+export default Footer;
\ No newline at end of file
diff --git a/src/app/components/molecules/Header/Header.component.scss b/src/app/components/molecules/Header/Header.component.scss
new file mode 100644
index 0000000..d626f8d
--- /dev/null
+++ b/src/app/components/molecules/Header/Header.component.scss
@@ -0,0 +1,65 @@
+@import './../../../styles/variables';
+
+.c-Header {
+ // display: flex;
+ // align-items: center;
+ // justify-content: center;
+ position: fixed;
+ height: $header-height;
+ left: 0;
+ top: 0;
+ right: 0;
+ width: 100%;
+ background-color: $brand-color;
+ color: $neutral-00;
+ padding: 1em 1em;
+ z-index: 2;
+ transition: transform 0.4s;
+
+ .row {
+ justify-content: space-between;
+ .c-Header__cartIconContainer--aligner {
+ display: flex;
+ align-items: center;
+ justify-content: flex-end;
+ }
+ }
+
+ [class*='col-'] {
+ padding: 0;
+
+ &.c-Header--margin-right {
+ margin-right: 0.8em;
+ }
+ }
+
+ .c-Header__logo-main {
+ cursor: pointer;
+ display: flex;
+ align-items: center;
+ justify-content: center;
+ height: 100%;
+ }
+
+ .c-Header__iconClass {
+ transform: rotateX(18deg) rotateZ(10deg) scale(2);
+ color: yellow;
+ // margin: 6px 0px 3px 3px;
+ }
+
+ .header-icon {
+ font-size: $bigger-font-size;
+ &:hover {
+ color: $light-accent-color;
+ }
+ }
+
+ @media only screen and (min-width: 768px) {
+ [class*='col-'] {
+ padding: 0 15px;
+ }
+ .c-Header__logo-main {
+ justify-content: normal;
+ }
+ }
+}
diff --git a/src/app/components/molecules/Header/Header.jsx b/src/app/components/molecules/Header/Header.jsx
new file mode 100644
index 0000000..d03ca3b
--- /dev/null
+++ b/src/app/components/molecules/Header/Header.jsx
@@ -0,0 +1,58 @@
+import React, {useEffect} from 'react';
+import { Link } from 'react-router-dom';
+import { FontAwesomeIcon } from '@fortawesome/react-fontawesome'
+import { faStar } from '@fortawesome/free-solid-svg-icons'
+
+import Search from './../Search'
+import CartIcon from './../CartIcon'
+
+const Header = props => {
+
+ useEffect(() => {
+ const body = document.body;
+ const scrollUp = "scroll-up";
+ const scrollDown = "scroll-down";
+ let lastScroll = 0;
+
+ window.addEventListener("scroll", () => {
+ const currentScroll = window.pageYOffset;
+ if (currentScroll === 0) {
+ body.classList.remove(scrollUp);
+ return;
+ }
+
+ if (currentScroll > lastScroll && !body.classList.contains(scrollDown)) {
+ // down
+ body.classList.remove(scrollUp);
+ body.classList.add(scrollDown);
+ } else if (currentScroll < lastScroll && body.classList.contains(scrollDown)) {
+ // up
+ body.classList.remove(scrollDown);
+ body.classList.add(scrollUp);
+ }
+ lastScroll = currentScroll;
+ });
+ })
+
+ return (
+
+
+
+
+
+
{!props.inCart && }
+
+
+
+ );
+};
+
+Header.defaultProps = {
+
+};
+
+Header.propTypes = {
+
+};
+
+export default Header;
\ No newline at end of file
diff --git a/src/app/components/molecules/Header/Header.test.js b/src/app/components/molecules/Header/Header.test.js
new file mode 100644
index 0000000..4077f50
--- /dev/null
+++ b/src/app/components/molecules/Header/Header.test.js
@@ -0,0 +1,8 @@
+import React from 'react';
+import Header from './Header';
+
+describe('Header', () => {
+ it('renders without error', () => {
+
+ });
+});
\ No newline at end of file
diff --git a/src/app/components/molecules/Header/index.js b/src/app/components/molecules/Header/index.js
new file mode 100644
index 0000000..be30249
--- /dev/null
+++ b/src/app/components/molecules/Header/index.js
@@ -0,0 +1,3 @@
+import Header from './Header.jsx';
+
+export default Header;
\ No newline at end of file
diff --git a/src/app/components/molecules/LoginForm/LoginForm.component.scss b/src/app/components/molecules/LoginForm/LoginForm.component.scss
new file mode 100644
index 0000000..3fdb21c
--- /dev/null
+++ b/src/app/components/molecules/LoginForm/LoginForm.component.scss
@@ -0,0 +1,3 @@
+.c-LoginForm {
+ padding: 1rem;
+}
diff --git a/src/app/components/molecules/LoginForm/LoginForm.js b/src/app/components/molecules/LoginForm/LoginForm.js
new file mode 100644
index 0000000..1dd90f2
--- /dev/null
+++ b/src/app/components/molecules/LoginForm/LoginForm.js
@@ -0,0 +1,29 @@
+import React from 'react';
+import PropTypes from 'prop-types';
+
+import loginFormFields from './../../../config/forms-meta/login-form.json'
+import Form from './../common/Form'
+
+const LoginForm = props => {
+ return (
+
+
+
+ );
+};
+
+LoginForm.propTypes = {
+ updateFormValues: PropTypes.func,
+ updateFormErrors: PropTypes.func,
+ formValues: PropTypes.object,
+ formErrors: PropTypes.object
+};
+
+export default LoginForm;
\ No newline at end of file
diff --git a/src/app/components/molecules/LoginForm/LoginForm.test.js b/src/app/components/molecules/LoginForm/LoginForm.test.js
new file mode 100644
index 0000000..41c0da8
--- /dev/null
+++ b/src/app/components/molecules/LoginForm/LoginForm.test.js
@@ -0,0 +1,8 @@
+import React from 'react';
+import LoginForm from './LoginForm';
+
+describe('LoginForm', () => {
+ it('renders without error', () => {
+
+ });
+});
\ No newline at end of file
diff --git a/src/app/components/molecules/LoginForm/index.js b/src/app/components/molecules/LoginForm/index.js
new file mode 100644
index 0000000..b8cfc4c
--- /dev/null
+++ b/src/app/components/molecules/LoginForm/index.js
@@ -0,0 +1,3 @@
+import LoginForm from './LoginForm';
+
+export default LoginForm;
\ No newline at end of file
diff --git a/src/app/components/molecules/PageLoader/PageLoader.component.scss b/src/app/components/molecules/PageLoader/PageLoader.component.scss
new file mode 100644
index 0000000..de29e02
--- /dev/null
+++ b/src/app/components/molecules/PageLoader/PageLoader.component.scss
@@ -0,0 +1,125 @@
+@import url('https://fonts.googleapis.com/css?family=Indie+Flower');
+
+.c-PageLoader {
+ // margin-top: 50%;
+ display: flex;
+ align-items: center;
+ justify-content: center;
+ background: rgba(0,0,0,0.3);
+ width: 100%;
+ height: 100%;
+ position: fixed;
+ top:0px;
+ left:0px;
+ z-index: 20;
+
+ // .is-animate {
+ // background: #ffb200;
+ // box-sizing: border-box;
+ // font-size: 66px;
+ // display: -webkit-inline-box;
+ // padding: 14px;
+ // border-radius: 7px;
+ // }
+ // .is-animate > div {
+ // animation-name: style;
+ // display: -webkit-inline-box;
+ // color: #fff;
+ // padding: 9px;
+ // background: #ffb200;
+ // font-family: 'Indie Flower', cursive;
+ // box-shadow: 2px 2px 9px 2px;
+ // }
+ // .l{
+ // animation: letterspacing 1s infinite alternate cubic-bezier(.2, 0, 0, 1);
+ // }
+
+ // .is-animate > div {
+ // animation-duration: 1s;
+ // animation-fill-mode: both;
+ // animation-iteration-count: infinite;
+ // }
+
+ // .is-animate > div:nth-child(1) { animation-delay: 0.0s }
+ // .is-animate > div:nth-child(2) { animation-delay: 0.1s }
+ // .is-animate > div:nth-child(3) { animation-delay: 0.2s }
+ // .is-animate > div:nth-child(4) { animation-delay: 0.3s }
+ // .is-animate > div:nth-child(5) { animation-delay: 0.4s }
+ // .is-animate > div:nth-child(6) { animation-delay: 0.5s }
+ // .is-animate > div:nth-child(7) { animation-delay: 0.6s }
+
+
+ // @keyframes style {
+ // from {
+ // transform: scale3d(1, 1, 1);
+ // }
+ // 30% {
+ // box-shadow: 0px 0px 0px 0px;
+ // transform: scale3d(1.25, 0.75, 1);
+ // }
+ // 40% {
+ // transform: scale3d(0.75, 1.25, 1);
+ // }
+ // 50% {
+ // transform: scale3d(1.15, 0.85, 1);
+ // }
+ // 65% {
+ // transform: scale3d(.95, 1.05, 1);
+ // }
+ // 75% {
+ // transform: scale3d(1.05, .95, 1);
+ // }
+ // to {
+ // transform: scale3d(1, 1, 1);
+ // }
+ // }
+
+
+ // @keyframes letterspacing {
+ // 0% {
+ // filter: blur(0.1rem);
+ // }
+ // 100% {
+ // filter: blur(0.5rem);
+ // }
+ // to {
+ // letter-spacing: none;
+ // filter: blur(0rem);
+ // }
+ // }
+
+
+ .c-PageLoader__lds-ripple {
+ display: inline-block;
+ position: relative;
+ width: 80px;
+ height: 80px;
+ div {
+ position: absolute;
+ border: 4px solid #fff;
+ opacity: 1;
+ border-radius: 50%;
+ animation: lds-ripple 1s cubic-bezier(0, 0.2, 0.8, 1) infinite;
+ &:nth-child(2) {
+ animation-delay: -0.5s;
+ }
+ }
+ }
+
+ @keyframes lds-ripple {
+ 0% {
+ top: 36px;
+ left: 36px;
+ width: 0;
+ height: 0;
+ opacity: 1;
+ }
+ 100% {
+ top: 0px;
+ left: 0px;
+ width: 72px;
+ height: 72px;
+ opacity: 0;
+ }
+ }
+}
diff --git a/src/app/components/molecules/PageLoader/PageLoader.jsx b/src/app/components/molecules/PageLoader/PageLoader.jsx
new file mode 100644
index 0000000..cd865f0
--- /dev/null
+++ b/src/app/components/molecules/PageLoader/PageLoader.jsx
@@ -0,0 +1,33 @@
+import React from 'react';
+
+const PageLoader = props => {
+ return (
+
+ )
+ //
+ //
+ //
l
+ //
o
+ //
a
+ //
d
+ //
i
+ //
n
+ //
g
+ //
+ //
+};
+
+PageLoader.defaultProps = {
+
+};
+
+PageLoader.propTypes = {
+
+};
+
+export default PageLoader;
\ No newline at end of file
diff --git a/src/app/components/molecules/PageLoader/PageLoader.test.js b/src/app/components/molecules/PageLoader/PageLoader.test.js
new file mode 100644
index 0000000..34ad0ea
--- /dev/null
+++ b/src/app/components/molecules/PageLoader/PageLoader.test.js
@@ -0,0 +1,8 @@
+import React from 'react';
+import PageLoader from './PageLoader';
+
+describe('PageLoader', () => {
+ it('renders without error', () => {
+
+ });
+});
\ No newline at end of file
diff --git a/src/app/components/molecules/PageLoader/index.js b/src/app/components/molecules/PageLoader/index.js
new file mode 100644
index 0000000..d7d9040
--- /dev/null
+++ b/src/app/components/molecules/PageLoader/index.js
@@ -0,0 +1,3 @@
+import PageLoader from './PageLoader.jsx';
+
+export default PageLoader;
\ No newline at end of file
diff --git a/src/app/components/molecules/SocialLogin/SocialLogin.component.scss b/src/app/components/molecules/SocialLogin/SocialLogin.component.scss
new file mode 100644
index 0000000..403b3f6
--- /dev/null
+++ b/src/app/components/molecules/SocialLogin/SocialLogin.component.scss
@@ -0,0 +1,10 @@
+.c-SocialLogin {
+ padding: 1rem;
+ margin-bottom: 2rem;
+ display: flex;
+ justify-content: center;
+ .abcRioButtonBlue {
+ background: white;
+ color: #585f6b;
+ }
+}
diff --git a/src/app/components/molecules/SocialLogin/SocialLogin.jsx b/src/app/components/molecules/SocialLogin/SocialLogin.jsx
new file mode 100644
index 0000000..a70f5f7
--- /dev/null
+++ b/src/app/components/molecules/SocialLogin/SocialLogin.jsx
@@ -0,0 +1,68 @@
+import React from 'react';
+import {withRouter} from 'react-router-dom'
+import {gapi} from 'gapi-script'
+
+class SocialLogin extends React.Component{
+
+ constructor(props) {
+ super(props)
+ this.width = 254
+ this.height = 50
+ this.onSuccess = this.onSuccess.bind(this)
+ this.onFailure = this.onFailure.bind(this)
+ }
+
+ componentDidMount() {
+ gapi && gapi.signin2 && gapi.signin2.render('g-signin2', {
+ 'scope': 'profile email',
+ 'width': this.width,
+ 'height': this.height,
+ 'longtitle': true,
+ 'theme': 'dark',
+ 'onsuccess': this.onSuccess,
+ 'onfailure': this.onFailure
+ });
+ }
+
+ onSuccess(googleUser) {
+ console.log('Logged in as: ' + googleUser.getBasicProfile().getName());
+ this.props.history.push('/view/plp')
+ }
+
+ onFailure(error) {
+ console.log(error);
+ }
+
+ render() {
+ return (
+
+ );
+ }
+}
+
+SocialLogin.defaultProps = {
+
+};
+
+SocialLogin.propTypes = {
+
+};
+
+export default withRouter(SocialLogin)
\ No newline at end of file
diff --git a/src/app/components/molecules/SocialLogin/SocialLogin.test.js b/src/app/components/molecules/SocialLogin/SocialLogin.test.js
new file mode 100644
index 0000000..e4a3833
--- /dev/null
+++ b/src/app/components/molecules/SocialLogin/SocialLogin.test.js
@@ -0,0 +1,8 @@
+import React from 'react';
+import SocialLogin from './SocialLogin';
+
+describe('SocialLogin', () => {
+ it('renders without error', () => {
+
+ });
+});
\ No newline at end of file
diff --git a/src/app/components/molecules/SocialLogin/index.js b/src/app/components/molecules/SocialLogin/index.js
new file mode 100644
index 0000000..3fc936b
--- /dev/null
+++ b/src/app/components/molecules/SocialLogin/index.js
@@ -0,0 +1,3 @@
+import SocialLogin from './SocialLogin.jsx';
+
+export default SocialLogin;
\ No newline at end of file
diff --git a/src/app/components/molecules/common/Form/Form.component.scss b/src/app/components/molecules/common/Form/Form.component.scss
new file mode 100644
index 0000000..c4efb1a
--- /dev/null
+++ b/src/app/components/molecules/common/Form/Form.component.scss
@@ -0,0 +1,7 @@
+.c-Form {
+ padding: 15px;
+ background: #ddd;
+ border: 1px solid #bbb;
+ border-radius: 3px;
+ margin-top: 10px;
+}
diff --git a/src/app/components/molecules/common/Form/Form.js b/src/app/components/molecules/common/Form/Form.js
new file mode 100644
index 0000000..4a4969b
--- /dev/null
+++ b/src/app/components/molecules/common/Form/Form.js
@@ -0,0 +1,91 @@
+import React from 'react'
+import PropTypes from 'prop-types'
+import FormFieldContainer from '../FormFieldContainer';
+
+class Form extends React.Component {
+ constructor(props) {
+ super(props)
+ this.state = {
+ name: '',
+ greeting: ''
+ };
+ this.handleChange = this.handleChange.bind(this);
+ this.handleSubmit = this.handleSubmit.bind(this);
+ this.onFocusHandler = this.onFocusHandler.bind(this)
+ }
+
+ handleChange(event) {
+ this.setState({ name: event.target.value });
+ }
+
+ handleSubmit(event) {
+ event.preventDefault();
+ // fetch(`/api/greeting?name=${encodeURIComponent(this.state.name)}`)
+ // .then(response => response.json())
+ // .then(state => this.setState(state));
+
+ }
+
+ onFocusHandler() {
+ console.log('Parent, on focus handler')
+ }
+
+ render() {
+ const {
+ context,
+ fieldValues,
+ formErrors,
+ formValues,
+ metaData,
+ updateFormErrors,
+ updateFormValues
+ } = this.props
+ // const jsonData = this.props && this.props.metaData
+ const inputList = metaData && context ? Object.keys(metaData[context]) : []
+
+ let inputListSection =
+ inputList &&
+ inputList.map((item, index) => {
+ const formFieldObject = metaData[context][item]
+ const formField = formFieldObject ? (
+
+ ) : null
+ return formField
+ })
+
+ inputListSection = inputListSection.filter((formFieldObject) => {
+ return formFieldObject !== null
+ })
+
+ return (
+
+ )
+ }
+}
+
+Form.propTypes = {
+ context: PropTypes.string,
+ customBlurHandler: PropTypes.func,
+ customBlurFormatter: PropTypes.func,
+ metaData: PropTypes.object.isRequired,
+ className: PropTypes.string,
+ fieldValues: PropTypes.object,
+ formErrors: PropTypes.object,
+ formValues: PropTypes.object,
+ updateFormErrors: PropTypes.func,
+ updateFormValues: PropTypes.func
+}
+
+export default Form
\ No newline at end of file
diff --git a/src/app/components/molecules/common/Form/Form.test.js b/src/app/components/molecules/common/Form/Form.test.js
new file mode 100644
index 0000000..407b698
--- /dev/null
+++ b/src/app/components/molecules/common/Form/Form.test.js
@@ -0,0 +1,8 @@
+import React from 'react';
+import Form from './Form';
+
+describe('Form', () => {
+ it('renders without error', () => {
+
+ });
+});
\ No newline at end of file
diff --git a/src/app/components/molecules/common/Form/index.js b/src/app/components/molecules/common/Form/index.js
new file mode 100644
index 0000000..a9d88bf
--- /dev/null
+++ b/src/app/components/molecules/common/Form/index.js
@@ -0,0 +1,3 @@
+import Form from './Form';
+
+export default Form;
\ No newline at end of file
diff --git a/src/app/components/molecules/common/FormFieldContainer/FormFieldContainer.component.scss b/src/app/components/molecules/common/FormFieldContainer/FormFieldContainer.component.scss
new file mode 100644
index 0000000..588c0c5
--- /dev/null
+++ b/src/app/components/molecules/common/FormFieldContainer/FormFieldContainer.component.scss
@@ -0,0 +1,3 @@
+.c-FormFieldContainer {
+
+}
diff --git a/src/app/components/molecules/common/FormFieldContainer/FormFieldContainer.js b/src/app/components/molecules/common/FormFieldContainer/FormFieldContainer.js
new file mode 100644
index 0000000..99d2eca
--- /dev/null
+++ b/src/app/components/molecules/common/FormFieldContainer/FormFieldContainer.js
@@ -0,0 +1,181 @@
+import React from 'react'
+import PropTypes from 'prop-types'
+
+import InputField from '../../../atoms/InputField'
+import SelectOption from '../../../atoms/SelectOption'
+
+/**
+ * INSERT_DESCRIPTION_HERE
+ */
+
+class FormFieldContainer extends React.Component {
+ constructor(props) {
+ super(props)
+ const componentKey = this.props.propsData && this.props.propsData.id
+ const passedValue = this.props.value || (this.props.propsData && this.props.propsData.value)
+ this.state = {
+ error: false,
+ errorMessage: null
+ }
+ this.props.updateFormValues({
+ formValues: {
+ // ...this.props.formValues,
+ [componentKey]: passedValue ? passedValue : ''
+ }
+ })
+ this.props.updateFormErrors({
+ formErrors: {
+ // ...this.props.formErrors,
+ [componentKey]: ''
+ }
+ })
+ this.onChangeHandler = this.onChangeHandler.bind(this)
+ this.onBlurHandler = this.onBlurHandler.bind(this)
+ this.onFocusHandler = this.onFocusHandler.bind(this)
+ }
+
+ /**
+ * validateFormField - validates the input field values
+ * @param {object} event js event object
+ * @param {object} validationObj fieldinput data as props
+ */
+ validateFormField(e, validationObj) {
+ const {propsData, formErrors, formValues, updateFormErrors, updateFormValues} = this.props
+ const inputValue = e.target.value && e.target.value
+ let error = false
+ let errorMessage = ''
+ const componentKey = propsData.id
+ const validRegex =
+ validationObj.dataRuleRegex &&
+ new RegExp(validationObj.dataRuleRegex.regex).test(inputValue)
+ const poValidRegex =
+ e.target.id === 'address1' &&
+ validationObj.dataRuleRegex.po_regex &&
+ new RegExp(validationObj.dataRuleRegex.po_regex, 'i').test(inputValue)
+
+ if (e.target.tagName && e.target.tagName === 'SELECT') {
+ if (
+ validationObj.required.isRequired &&
+ e.target.selectedIndex === 0 &&
+ !validationObj.required.preSelected
+ ) {
+ error = true
+ errorMessage = validationObj.required.error_message
+ }
+ } else if (e.target.tagName && e.target.tagName === 'INPUT') {
+ if (validationObj.required.isRequired) {
+ if (inputValue.trim('') === '') {
+ error = true
+ errorMessage = validationObj.required.error_message
+ } else if (validationObj.dataRuleRegex && !validRegex) {
+ error = true
+ errorMessage = validationObj.dataRuleRegex.error_message
+ } else if (validationObj.dataRuleRegex && poValidRegex) {
+ error = true
+ errorMessage = validationObj.dataRuleRegex.po_error_message
+ }
+ }
+ } else {
+ error = false
+ errorMessage = null
+ }
+
+ this.setState({
+ error
+ })
+
+ updateFormErrors({
+ formErrors: {
+ ...formErrors,
+ [componentKey]: errorMessage
+ }
+ })
+
+ updateFormValues({
+ formValues: {
+ ...formValues,
+ [componentKey]: inputValue
+ }
+ })
+
+ return error
+ }
+
+ onChangeHandler(event) {
+ const value = event.target.value
+ this.setState({
+ value
+ })
+ }
+
+ onFocusHandler(event) {
+ this.setState({
+ oldValue: event.target.value
+ })
+ }
+
+ onBlurHandler(validation, customBlurHandler, e) {
+ e.oldValue = this.state.oldValue
+ const {customBlurFormatter} = this.props
+ const isError = this.validateFormField(e, validation)
+ if (!isError) {
+ if (customBlurHandler && typeof customBlurHandler === 'function') {
+ customBlurHandler(e)
+ }
+ if (customBlurFormatter && typeof customBlurFormatter === 'function') {
+ this.setState({value: customBlurFormatter(e)})
+ }
+ }
+ }
+
+ render() {
+ const {customBlurHandler, formErrors, propsData} = this.props
+ const {elementType} = propsData
+ const errorMessage = formErrors && formErrors[propsData.id]
+ const meta = {
+ ...this.props.propsData,
+ className: this.state.error ? 'error' : '',
+ error: this.state.error || errorMessage,
+ errorMessage,
+ handlers: {
+ onBlurHandler: this.onBlurHandler,
+ customBlurHandler,
+ onChangeHandler: this.onChangeHandler,
+ onFocusHandler: this.onFocusHandler
+ },
+ fieldValues: {
+ propsValue: this.props.value,
+ stateValue: this.state.value
+ }
+ }
+
+ /**
+ * switch function - returns HTML Tag depending upon the requirement
+ * @param {string} elementType - element type e.g. input,select
+ */
+ switch (elementType) {
+ case 'input':
+ return
+ case 'select':
+ return
+ default:
+ return
+ }
+ }
+}
+
+FormFieldContainer.propTypes = {
+ propsData: PropTypes.object.isRequired,
+ className: PropTypes.string,
+ customBlurHandler: PropTypes.func,
+ customBlurFormatter: PropTypes.func,
+ onFocus: PropTypes.func,
+ value: PropTypes.string,
+ formValues: PropTypes.object,
+ formErrors: PropTypes.object,
+ selectedCreditCard: PropTypes.object,
+ updateFormValues: PropTypes.func,
+ updateFormErrors: PropTypes.func
+}
+
+export default FormFieldContainer
diff --git a/src/app/components/molecules/common/FormFieldContainer/FormFieldContainer.test.js b/src/app/components/molecules/common/FormFieldContainer/FormFieldContainer.test.js
new file mode 100644
index 0000000..1233522
--- /dev/null
+++ b/src/app/components/molecules/common/FormFieldContainer/FormFieldContainer.test.js
@@ -0,0 +1,8 @@
+import React from 'react';
+import FormFieldContainer from './FormFieldContainer';
+
+describe('FormFieldContainer', () => {
+ it('renders without error', () => {
+
+ });
+});
\ No newline at end of file
diff --git a/src/app/components/molecules/common/FormFieldContainer/index.js b/src/app/components/molecules/common/FormFieldContainer/index.js
new file mode 100644
index 0000000..99154b3
--- /dev/null
+++ b/src/app/components/molecules/common/FormFieldContainer/index.js
@@ -0,0 +1,3 @@
+import FormFieldContainer from './FormFieldContainer';
+
+export default FormFieldContainer;
\ No newline at end of file
diff --git a/src/app/components/molecules/common/Jumbotron/Jumbotron.component.scss b/src/app/components/molecules/common/Jumbotron/Jumbotron.component.scss
new file mode 100644
index 0000000..98798c9
--- /dev/null
+++ b/src/app/components/molecules/common/Jumbotron/Jumbotron.component.scss
@@ -0,0 +1,9 @@
+.c-Jumbotron {
+ padding: 1rem;
+ background: #222;
+ color: #bbbbbb;
+ margin-bottom: 0;
+ .my-4 {
+ border-color: #bbb;
+ }
+}
diff --git a/src/app/components/molecules/common/Jumbotron/Jumbotron.jsx b/src/app/components/molecules/common/Jumbotron/Jumbotron.jsx
new file mode 100644
index 0000000..32284ca
--- /dev/null
+++ b/src/app/components/molecules/common/Jumbotron/Jumbotron.jsx
@@ -0,0 +1,27 @@
+import React from 'react';
+import PropTypes from 'prop-types'
+import {Link} from 'react-router-dom'
+
+const Jumbotron = props => {
+ return (
+
+
{props.jumboText}
+
{props.subText}
+
+
{props.jumboDescription}
+
+ Go To PLP
+ {props.furtherLink}
+
+
+ );
+};
+
+Jumbotron.propTypes = {
+ furtherLink: PropTypes.string,
+ jumboDescription: PropTypes.string,
+ jumboText: PropTypes.string,
+ subText: PropTypes.string
+};
+
+export default Jumbotron;
\ No newline at end of file
diff --git a/src/app/components/molecules/common/Jumbotron/Jumbotron.test.js b/src/app/components/molecules/common/Jumbotron/Jumbotron.test.js
new file mode 100644
index 0000000..9ff25d9
--- /dev/null
+++ b/src/app/components/molecules/common/Jumbotron/Jumbotron.test.js
@@ -0,0 +1,8 @@
+import React from 'react';
+import Jumbotron from './Jumbotron.jsx';
+
+describe('Jumbotron', () => {
+ it('renders without error', () => {
+
+ });
+});
\ No newline at end of file
diff --git a/src/app/components/molecules/common/Jumbotron/index.js b/src/app/components/molecules/common/Jumbotron/index.js
new file mode 100644
index 0000000..3c0460f
--- /dev/null
+++ b/src/app/components/molecules/common/Jumbotron/index.js
@@ -0,0 +1,3 @@
+import Jumbotron from './Jumbotron.jsx';
+
+export default Jumbotron;
\ No newline at end of file
diff --git a/src/app/config/forms-meta/login-form.json b/src/app/config/forms-meta/login-form.json
new file mode 100644
index 0000000..ea47937
--- /dev/null
+++ b/src/app/config/forms-meta/login-form.json
@@ -0,0 +1,53 @@
+{
+ "login": {
+ "email": {
+ "label": "Email ID",
+ "placeholder": "Email ID",
+ "type": "text",
+ "id": "email",
+ "elementType": "input",
+ "validation": {
+ "required": {
+ "isRequired": "true",
+ "error_message": "This field is required."
+ },
+ "rules": {
+ "minlength": 6,
+ "maxlength": 50
+ }
+ }
+ },
+ "password": {
+ "label": "Password",
+ "placeholder": "Password",
+ "type": "password",
+ "id": "password",
+ "elementType": "input",
+ "validation": {
+ "required": {
+ "isRequired": "true",
+ "error_message": "This field is required."
+ },
+ "rules": {
+
+ }
+ }
+ }
+ },
+ "search": {
+ "search": {
+ "placeholder": "Search..",
+ "type": "text",
+ "id": "search",
+ "elementType": "input",
+ "validation": {
+ "required": {
+ "isRequired": "false"
+ },
+ "rules": {
+
+ }
+ }
+ }
+ }
+}
\ No newline at end of file
diff --git a/src/app/index.scss b/src/app/index.scss
new file mode 100644
index 0000000..a02628e
--- /dev/null
+++ b/src/app/index.scss
@@ -0,0 +1,21 @@
+@charset 'UTF-8';
+
+@import 'styles/variables';
+@import 'styles/base';
+@import 'styles/utilities';
+@import 'styles/pages';
+@import 'styles/components';
+
+.abcRioButtonBlue {
+ background-color: white!important;
+ color: #585f6b!important;
+ border-radius: 3px;
+}
+
+.g-signin2, .fb-login-button {
+ display: flex!important;
+ justify-content: center;
+ margin-bottom: 10px;
+ border-radius: 5px;
+}
+
diff --git a/src/app/main.js b/src/app/main.js
new file mode 100644
index 0000000..fac922d
--- /dev/null
+++ b/src/app/main.js
@@ -0,0 +1,26 @@
+import React from 'react';
+import ReactDOM from 'react-dom';
+import thunk from 'redux-thunk'
+import Immutable from 'immutable'
+import {createStore, applyMiddleware, compose} from 'redux'
+// import * as serviceWorker from '../serviceWorker';
+import Router from './router';
+import reducer from './reducer'
+import ReactModal from 'react-modal'
+import './index.scss'
+import 'bootstrap/dist/css/bootstrap.min.css'
+
+const composeEnhancers =
+ typeof window === 'object' &&
+ window.__REDUX_DEVTOOLS_EXTENSION_COMPOSE__ ?
+ window.__REDUX_DEVTOOLS_EXTENSION_COMPOSE__({
+ serialize: { // prettier-ignore
+ immutable: Immutable
+ }
+ }) : compose;
+const store = createStore(reducer, composeEnhancers(applyMiddleware(thunk)))
+
+ReactModal.setAppElement('#root');
+ReactDOM.render(, document.getElementById('root'));
+
+// serviceWorker.unregister();
diff --git a/src/app/pages/Login/Login.jsx b/src/app/pages/Login/Login.jsx
new file mode 100644
index 0000000..3965976
--- /dev/null
+++ b/src/app/pages/Login/Login.jsx
@@ -0,0 +1,84 @@
+import React from 'react'
+import PropTypes from 'prop-types'
+import styles from './Login.module.scss'
+import {connect} from 'react-redux'
+import {createPropsSelector} from 'reselect-immutable-helpers'
+
+import * as actions from './actions'
+import {getLogin, getFormErrors, getFormValues} from './selectors'
+import LoginForm from '../../components/molecules/LoginForm'
+import SocialLogin from '../../components/molecules/SocialLogin'
+import Jumbotron from '../../components/molecules/common/Jumbotron'
+
+class Login extends React.Component {
+ constructor(props) {
+ super(props)
+ this.pageType = 'login'
+ }
+
+ componentDidMount() {
+ const {initializeLogin} = this.props
+ initializeLogin()
+ }
+
+ clicked() {
+ console.log('login handler')
+ }
+
+ render() {
+ const {updateFormErrors, updateFormValues, formErrors, formValues} = this.props
+ return (
+
+
+
+
+
+
+
+
Let's get to know each other, shall we!
+
+
OR
+
+
+
+
+
+ )
+ }
+}
+
+Login.propTypes = {
+ initializeLogin: PropTypes.func,
+ dataState: PropTypes.object,
+ updateFormValues: PropTypes.func,
+ updateFormErrors: PropTypes.func,
+ formValues: PropTypes.object,
+ formErrors: PropTypes.object
+}
+
+const mapStateToProps = createPropsSelector({
+ dataState: getLogin,
+ formErrors: getFormErrors,
+ formValues: getFormValues
+})
+
+const mapDispatchToProps = {
+ initializeLogin: actions.initializeLogin,
+ updateFormErrors: actions.updateFormErrors,
+ updateFormValues: actions.updateFormValues
+}
+
+export default connect(
+ mapStateToProps,
+ mapDispatchToProps
+)(Login)
diff --git a/src/app/pages/Login/Login.module.scss b/src/app/pages/Login/Login.module.scss
new file mode 100644
index 0000000..e093fb7
--- /dev/null
+++ b/src/app/pages/Login/Login.module.scss
@@ -0,0 +1,32 @@
+.Login {
+ background: #222;
+ max-width: unset;
+ @media only screen and (max-width: 1140px) {
+ padding-left: unset;
+ padding-right: unset;
+ margin-right: unset;
+ margin-left: unset;
+ }
+ .loginContainer {
+ border: 2px solid grey;
+ border-radius: 0.3rem;
+ margin: 1rem;
+ background: #eee;
+ padding-bottom: 20px;
+ .c-login-intimation {
+ padding: 15px 5px 15px 15px;
+ background: black;
+ color: white;
+ margin-bottom: 0;
+ }
+ .loginSplitter {
+ text-align: center;
+ margin-top: 10px;
+ margin-bottom: 10px;
+ text-transform: capitalize;
+ font-size: 25px;
+ font-weight: bolder;
+ line-height: 30px;
+ }
+ }
+}
diff --git a/src/app/pages/Login/Login.test.js b/src/app/pages/Login/Login.test.js
new file mode 100644
index 0000000..35326c9
--- /dev/null
+++ b/src/app/pages/Login/Login.test.js
@@ -0,0 +1,8 @@
+import React from 'react';
+import Login from './Login';
+
+describe('Login', () => {
+ it('renders without error', () => {
+
+ });
+});
\ No newline at end of file
diff --git a/src/app/pages/Login/actions.js b/src/app/pages/Login/actions.js
new file mode 100644
index 0000000..113031a
--- /dev/null
+++ b/src/app/pages/Login/actions.js
@@ -0,0 +1,28 @@
+export const LOGIN_DATA_STATE_RECEIVED = 'LOGIN_DATA_STATE_RECEIVED'
+export const UPDATE_FORM_VALUES = 'UPDATE_BILLING_FORM_VALUES'
+export const UPDATE_FORM_ERRORS = 'UPDATE_BILLING_FORM_ERRORS'
+
+export const updateLoginDataState = (payload) => ({type: LOGIN_DATA_STATE_RECEIVED, payload})
+
+export const initializeLogin = () => (dispatch) => {
+ // return Promise.all([
+ // dispatch(initializeApp())
+ // ])
+ // .then(() => ({statusCode: 200}))
+ // .catch((err) => ({statusCode: err.statusCode || 500}))
+}
+
+export const updateFormValues = (formValues) => {
+ return {
+ type: UPDATE_FORM_VALUES,
+ payload: formValues
+ }
+}
+
+export const updateFormErrors = (formErrors) => {
+ return {
+ type: UPDATE_FORM_ERRORS,
+ payload: formErrors
+ }
+}
+
\ No newline at end of file
diff --git a/src/app/pages/Login/index.js b/src/app/pages/Login/index.js
new file mode 100644
index 0000000..afe6373
--- /dev/null
+++ b/src/app/pages/Login/index.js
@@ -0,0 +1,3 @@
+import Login from './Login.jsx';
+
+export default Login;
\ No newline at end of file
diff --git a/src/app/pages/Login/reducer.js b/src/app/pages/Login/reducer.js
new file mode 100644
index 0000000..f317a7c
--- /dev/null
+++ b/src/app/pages/Login/reducer.js
@@ -0,0 +1,18 @@
+import Immutable from 'immutable'
+
+import {LOGIN_DATA_STATE_RECEIVED, UPDATE_FORM_ERRORS, UPDATE_FORM_VALUES} from './actions'
+
+const initialState = Immutable.Map()
+
+const reducer = (state = initialState, action) => {
+ switch (action.type) {
+ case LOGIN_DATA_STATE_RECEIVED:
+ case UPDATE_FORM_ERRORS:
+ case UPDATE_FORM_VALUES:
+ return state.mergeDeep(action.payload)
+ default:
+ return state
+ }
+}
+
+export default reducer
diff --git a/src/app/pages/Login/selectors.js b/src/app/pages/Login/selectors.js
new file mode 100644
index 0000000..a30ca9c
--- /dev/null
+++ b/src/app/pages/Login/selectors.js
@@ -0,0 +1,14 @@
+import {createSelector} from 'reselect'
+import {createGetSelector} from 'reselect-immutable-helpers'
+
+const getData = ({data}) => data
+
+export const getLogin = createSelector(
+ getData,
+ (dataState) => {
+ return dataState.pages.login
+ }
+)
+
+export const getFormValues = createGetSelector(getLogin, 'formValues')
+export const getFormErrors = createGetSelector(getLogin, 'formErrors')
\ No newline at end of file
diff --git a/src/app/pages/index.js b/src/app/pages/index.js
new file mode 100644
index 0000000..d4b6201
--- /dev/null
+++ b/src/app/pages/index.js
@@ -0,0 +1,11 @@
+/* PLOP_INJECT_IMPORT */
+import Cart from './Cart';
+import Login from './Login';
+import Plp from './PLP';
+
+export {
+ /* PLOP_INJECT_EXPORT */
+ Cart,
+ Login,
+ Plp,
+}
\ No newline at end of file
diff --git a/src/app/reducer.js b/src/app/reducer.js
new file mode 100644
index 0000000..a6093b5
--- /dev/null
+++ b/src/app/reducer.js
@@ -0,0 +1,15 @@
+import {combineReducers} from 'redux'
+
+import loginReducer from './pages/Login/reducer'
+import plpReducer from './pages/PLP/reducer'
+import cartReducer from './pages/Cart/reducer'
+
+export default combineReducers({
+ data: combineReducers({
+ pages: combineReducers({
+ login: loginReducer,
+ plp: plpReducer,
+ cart: cartReducer
+ })
+ })
+})
diff --git a/src/app/router.jsx b/src/app/router.jsx
new file mode 100644
index 0000000..4088f54
--- /dev/null
+++ b/src/app/router.jsx
@@ -0,0 +1,37 @@
+import React from 'react'
+import PropTypes from 'prop-types'
+import {Provider} from 'react-redux'
+import {BrowserRouter, Route} from 'react-router-dom';
+import PageLoader from './components/molecules/PageLoader'
+import Loadable from 'react-loadable'
+
+export const LoadableLogin = Loadable({
+ loader: () => import('./pages/Login'),
+ loading: PageLoader
+})
+
+export const LoadableTCAssignmentHome = Loadable({
+ loader: () => import('./pages/TCAssignmentHome'),
+ loading: PageLoader
+})
+
+class Router extends React.Component {
+
+ render() {
+ const {store} = this.props
+ return (
+
+
+
+
+
+
+ )
+ }
+}
+
+Router.propTypes = {
+ store: PropTypes.object
+}
+
+export default Router
diff --git a/src/app/selectors.js b/src/app/selectors.js
new file mode 100644
index 0000000..e69de29
diff --git a/src/app/styles/_base.scss b/src/app/styles/_base.scss
new file mode 100644
index 0000000..71765b2
--- /dev/null
+++ b/src/app/styles/_base.scss
@@ -0,0 +1,8 @@
+@import 'base/animations';
+@import 'base/forms';
+@import 'base/general';
+@import 'base/lists';
+@import 'base/material';
+@import 'base/mixins';
+@import 'base/tables';
+@import 'base/typography';
\ No newline at end of file
diff --git a/src/app/styles/_components.scss b/src/app/styles/_components.scss
new file mode 100644
index 0000000..0aca43b
--- /dev/null
+++ b/src/app/styles/_components.scss
@@ -0,0 +1,11 @@
+// Components
+// ===
+@import '../components/atoms/InputField/InputField.component';
+@import '../components/atoms/SelectOption/SelectOption.component';
+@import '../components/molecules/common/Form/Form.component';
+@import '../components/molecules/common/FormFieldContainer/FormFieldContainer.component';
+@import '../components/molecules/common/Jumbotron/Jumbotron.component';
+@import '../components/molecules/Header/Header.component';
+@import '../components/molecules/LoginForm/LoginForm.component';
+@import '../components/molecules/PageLoader/PageLoader.component';
+@import '../components/molecules/SocialLogin/SocialLogin.component';
diff --git a/src/app/styles/_pages.scss b/src/app/styles/_pages.scss
new file mode 100644
index 0000000..8aca80b
--- /dev/null
+++ b/src/app/styles/_pages.scss
@@ -0,0 +1,4 @@
+// Pages
+// ===
+
+@import '../pages/Login/Login.module';
diff --git a/src/app/styles/_utilities.scss b/src/app/styles/_utilities.scss
new file mode 100644
index 0000000..60fa4f0
--- /dev/null
+++ b/src/app/styles/_utilities.scss
@@ -0,0 +1,25 @@
+// Utilities
+// ===
+//
+// Use wrap-at-root mixin to allow overriding existing styles with extreme
+// specificity while keeping source code clean and readable. Does not override
+// !important or inline styles; those must be dealt with separately.
+
+@import 'base/mixins';
+
+
+@include wrap-at-root("#app", 1) {
+ @import 'utilities/border';
+ @import 'utilities/box-shadow';
+ @import 'utilities/color';
+ @import 'utilities/flexbox';
+ @import 'utilities/heading';
+ @import 'utilities/layout';
+ @import 'utilities/margin';
+ @import 'utilities/padding';
+ @import 'utilities/text-content';
+ @import 'utilities/text';
+ @import 'utilities/display';
+ @import 'utilities/visibility';
+ @import 'utilities/z-index';
+}
diff --git a/src/app/styles/_variables.scss b/src/app/styles/_variables.scss
new file mode 100644
index 0000000..ef572f9
--- /dev/null
+++ b/src/app/styles/_variables.scss
@@ -0,0 +1,238 @@
+// Project-Wide Variables
+// ===
+//
+// Edit these as needed. Some guidelines:
+//
+// - Names should be lowercase and dash-separated;
+// - Qualifiers should be added to the beginning of related variables: use
+// `$small-font-size`, not `$font-size-small`;
+// - Numeric scales should use increments of 10; these numbers are arbitrary and
+// should not map to actual values. If really necessary, additional values can
+// be added in between, e.g. $neutral-15 between 10 and 20.
+//
+//
+// Table of Contents
+// ---
+//
+// [AAA] Basic Layout
+// [BBB] Responsive Layout
+// [CCC] Typography
+// [DDD] Color Palette
+// [EEE] Appearance
+// [FFF] Z-Index
+// [GGG] Shorthands
+
+
+// [AAA] Basic Layout
+// ---
+
+// Basic unit for spacing and alignment; 6 to 12px recommended. Apply in whole
+// or half multiples.
+$sub-unit: 4px;
+$unit: 8px;
+
+// Standard tap-target size
+$tap-size: 44px;
+
+// Container max-width.
+$max-width: 1280px;
+
+// Content Height Calculations
+$header-height: 64px;
+$footer-height: 173px;
+$content-height: calc(100vh - #{$header-height} - #{$footer-height});
+
+// [BBB] Responsive Layout
+// ---
+//
+// Media query breakpoints and grid setup. Please see Mobify's Responsive Best
+// Practices doc here: https://bit.ly/2tmRnEi, and our Responsive Grid
+// documentation here: http://docs.mobify.com/latest/guides/responsive-grid/
+//
+// Note: $small-breakpoint isn't needed, since it is 0px
+
+$medium-breakpoint: 600px;
+$large-breakpoint: 960px;
+$xlarge-breakpoint: $max-width;
+
+$susy: (
+ // Add color to show the columns and gutters
+ 'svg-grid-colors': hsl(0, 0%, 95%),
+ 'columns': susy-repeat(4),
+ 'gutters': 12px
+);
+
+$medium-layout: (
+ 'svg-grid-colors': hsl(0, 0%, 95%),
+ 'columns': susy-repeat(12),
+ 'gutters': 12px
+);
+
+$large-layout: (
+ 'svg-grid-colors': hsl(0, 0%, 95%),
+ 'columns': susy-repeat(12),
+ 'gutters': 24px
+);
+
+
+// [CCC] Typography
+// ---
+// $font-family: 'San Francisco', 'Roboto', 'Fira Sans', 'Segoe UI', sans-serif;
+$font-family: Verdana, Geneva, sans-serif;
+$header-font-family: 'Avenir Next Condensed', 'Roboto Condensed', 'Helvetica Neue', 'Roboto', sans-serif;
+$loaded-header-font-family: 'Roboto', $header-font-family;
+
+// Line height
+$huge-line-height: 32px;
+$bigger-line-height: 28px;
+$big-line-height: 24px;
+$line-height: 20px;
+$small-line-height: 16px;
+$smaller-line-height: 12px;
+$tiny-line-height: 8px;
+
+// Font sizes
+$huge-font-size: 28px;
+$bigger-font-size: 24px;
+$big-font-size: 20px;
+$font-size: 16px;
+$small-font-size: 14px;
+$smaller-font-size: 12px;
+$tiny-font-size: 10px;
+$smallest-font-size: 8px;
+
+// Font weight
+$thin-font-weight: 100;
+$extra-light-font-weight: 200;
+$light-font-weight: 300;
+$regular-font-weight: 400;
+$medium-font-weight: 500;
+$semi-bold-font-weight: 600;
+$bold-font-weight: 700;
+
+// [DDD] Color Palette
+// ---
+
+// Neutrals
+$neutral-00: #fff;
+$neutral-10: #f7f7f7;
+$neutral-12: #f1f3f6;
+$neutral-15: #eee;
+$neutral-17: #e1e1e1;
+$neutral-20: #d5d5d5;
+$neutral-30: #bfbfbf;
+$neutral-40: #999;
+$neutral-50: #696969;
+$neutral-60: #333;
+$neutral-70: #000;
+
+// Brand colors
+// $brand-color: #017e9b; // blue
+$brand-color: #4471ea; // blue
+// $secondary-brand-color: #005569;
+$secondary-brand-color: #fafafa;
+$tertiary-brand-color: #83bdcb;
+$quaternary-brand-color: #bfdfe6;
+
+// UI Kit colors
+$ui-brand-color: #005c83; // dark blue
+
+// Accent colors
+$accent-color: #ff852c; // orange
+$light-accent-color: lighten($accent-color, 15%);
+$dark-accent-color: darken($accent-color, 15%);
+
+// Primary Action
+$primary-action-color: #dc0a3c;
+$light-primary-action-color: lighten($primary-action-color, 15%);
+$dark-primary-action-color: darken($primary-action-color, 15%);
+
+// Secondary Action
+$secondary-action-color: #ff852c;
+$light-secondary-action-color: lighten($secondary-action-color, 15%);
+$dark-secondary-action-color: darken($secondary-action-color, 15%);
+
+// Success colors
+$success-color: #037b30;
+$light-success-color: lighten($success-color, 15%);
+$dark-success-color: darken($success-color, 15%);
+
+// Error colors
+$error-color: #c70936;
+$light-error-color: lighten($error-color, 15%);
+$feedback-error-color: #f8e7eb;
+$dark-error-color: darken($error-color, 15%);
+
+// Sale color
+$sale-color: $error-color;
+$prominent-color: $error-color;
+
+// Social colors
+$facebook-color: #3a5a93;
+$twitter-color: #55aace;
+$instagram-color: #405de6;
+$pinterest-color: #bd081c;
+$youtube-color: #e52d27;
+$google-plus-color: #dd4b39;
+$yelp-color: #af0606;
+
+
+// [EEE] Appearance
+// ---
+
+$font-color: $neutral-60;
+$font-color-light: $neutral-12;
+
+$link-color: $ui-brand-color;
+$active-link-color: $dark-accent-color;
+
+$focus-color: $brand-color;
+
+$border-color: $neutral-20;
+$border-radius: 4px;
+
+$input-background-color: $neutral-00;
+$input-border-color: $border-color;
+$focused-input-border-color: $secondary-brand-color;
+$disabled-input-color: $neutral-40;
+$disabled-input-background-color: $neutral-15;
+$disabled-button-background-color: $neutral-15;
+$add-to-cart-button-background-color: #f7ae3a;
+$horizontal-input-padding: $unit;
+$vertical-input-padding: $unit;
+
+$background-color: $neutral-12;
+$overlay-color: rgba($neutral-00, 0.85);
+
+
+// [FFF] Z-Index
+// ---
+
+// Organizes z-index usage by name. Values can be incremented/decremented
+// slightly as necessary. eg. $z1-layer + 1;
+
+$z1-depth: 1; // background
+$z2-depth: 10; // icon or other ui element
+$z3-depth: 100; // modal shade or similar
+$z4-depth: 1000; // modal dialog or similar
+
+
+// [GGG] Shorthands
+// ---
+
+$border: 1px solid $border-color;
+$light-border: 1px solid $neutral-15;
+$input-padding: $vertical-input-padding $horizontal-input-padding;
+
+$box-shadow: 0 0 2px 0 rgba(0, 0, 0, 0.3);
+$large-box-shadow: 0 2px 4px 0 rgba(0, 0, 0, 0.3);
+$inset-box-shadow: inset 0 2px 2px -2px rgba(0, 0, 0, 0.3), inset 0 -2px 2px -2px rgba(0, 0, 0, 0.3);
+$input-box-shadow: inset 0 0 5px 0 rgba(0, 0, 0, 0.3);
+$themeColor-Light: #f3e2c7;
+$themeColor-Dark:#252525;
+$theme-font: verdana, sans-serif;
+$theme-supplementer: #f39c12;
+$font-size-desktop-h5: 1.25em;
+$font-size-mobile-h5: 0.8em;
+$font-size-desktop-p: 1.1em;
+$font-size-mobile-p: 0.7em;
diff --git a/src/app/styles/base/_animations.scss b/src/app/styles/base/_animations.scss
new file mode 100644
index 0000000..a6170b8
--- /dev/null
+++ b/src/app/styles/base/_animations.scss
@@ -0,0 +1,23 @@
+// Animations
+// ===
+//
+// Background Shimmer
+// ---
+
+@keyframes background-shimmer {
+ 0% {
+ background-position: 150vw 0;
+ }
+
+ 100% {
+ background-position: -150vw 0;
+ }
+}
+
+.scroll-down .c-Header {
+ transform: translate3d(0, -100%, 0);
+}
+
+.scroll-up .c-Header {
+ transform: none;
+}
\ No newline at end of file
diff --git a/src/app/styles/base/_forms.scss b/src/app/styles/base/_forms.scss
new file mode 100644
index 0000000..e76c94b
--- /dev/null
+++ b/src/app/styles/base/_forms.scss
@@ -0,0 +1,333 @@
+// Forms
+// ===
+
+$base__radio-checkmark: 'data:image/svg+xml;utf8,';
+
+
+// General Form Elements
+// ---
+//
+// 1. Address Firefox 4+ setting `line-height` on `input` using `!important` in
+// the UA stylesheet.
+// 2. Remove padding so people aren’t caught out if they zero out fieldsets.
+// 3. Remove inner padding and border in Firefox 4+.
+// 4. Correct color not being inherited. Known issue: affects color of disabled
+// elements.
+// 5. Correct font properties not being inherited.
+// 6. Address margins set differently in Firefox 4+, Safari, and Chrome.
+// 7. Set font-size to 16px to avoid zooming in on iOS (https://stackoverflow.com/questions/11064237/prevent-iphone-from-zooming-form)
+
+fieldset {
+ min-width: 0;
+ margin: 0;
+ padding: 0;
+ border: 0;
+}
+
+input {
+ margin: 0; // 6
+
+ color: inherit; // 4
+ font: inherit; // 5
+ font-family: $font-family;
+ font-size: $font-size + 2; // 7
+ line-height: normal; // 1
+
+ &::-moz-focus-inner {
+ padding: 0; // 3
+ border: 0; // 3
+ }
+}
+
+textarea {
+ margin: 0; // 6
+
+ color: inherit; // 4
+ font: inherit; // 5
+}
+
+label,
+textarea {
+ font-family: $font-family;
+ font-size: $font-size;
+}
+
+label {
+ display: inline-block;
+ margin-bottom: $unit / 2;
+
+ font-weight: $semi-bold-font-weight;
+
+ -webkit-tap-highlight-color: rgba(0, 0, 0, 0);
+
+ &:active {
+ color: $active-link-color;
+ }
+}
+
+select,
+textarea,
+[type="text"],
+[type="search"],
+[type="password"],
+[type="tel"],
+[type="url"],
+[type="number"],
+[type="email"] {
+ width: 100%;
+ min-height: $tap-size;
+ padding: $input-padding;
+ border: $border;
+
+ border-radius: 0;
+ background-color: $input-background-color;
+
+ line-height: $line-height;
+
+ -webkit-appearance: none;
+ -webkit-tap-highlight-color: rgba(0, 0, 0, 0);
+
+ &::-webkit-input-placeholder {
+ color: $neutral-50;
+ }
+
+ &:active,
+ &:focus {
+ border-color: $focus-color;
+
+ box-shadow: $input-box-shadow;
+ }
+}
+
+legend {
+ padding: 0; // 2
+}
+
+
+// Search input
+// ---
+//
+// These properties must be set with a slightly higher specificity for search
+// inputs because Normalize's defaults are a bit too specific
+//
+// 1. Address `appearance` set to `searchfield` in Safari and Chrome.
+// 2. Remove inner padding and search cancel button in Safari and Chrome on OS X.
+// Safari (but not Chrome) clips the cancel button when the search input has
+// padding (and `textfield` appearance).
+
+input[type="search"] {
+ box-sizing: border-box;
+
+ -webkit-appearance: none; // 1
+
+ &::-webkit-search-cancel-button,
+ &::-webkit-search-decoration {
+ -webkit-appearance: none; // 2
+ }
+}
+
+// Select
+// ---
+//
+// 1. Restore browser default styling. If you’re taking full control of select
+// styling, remove both these lines.
+// 2. Simulate the position of the down-arrow as if it were a Icon component in
+// a button.
+// 3. Address inconsistent `text-transform` inheritance for `button` and `select`.
+// All other form control elements do not inherit `text-transform` values.
+// Correct `button` style inheritance in Firefox and Opera. Correct `select`
+// style inheritance in Firefox.
+// 4. Correct color not being inherited. Known issue: affects color of disabled
+// elements.
+// 5. Set font-size to 16px to avoid zooming in on iOS (https://stackoverflow.com/questions/11064237/prevent-iphone-from-zooming-form)
+// 6. Address margins set differently in Firefox 4+, Safari, and Chrome.
+
+select {
+ height: $tap-size; // 1
+ margin: 0; // 6
+
+ border-radius: 0;
+
+ color: $brand-color;
+ font-family: $font-family;
+ font-size: $font-size + 2; // 5
+ text-transform: none; // 3
+
+ &,
+ &[disabled] {
+ background-repeat: no-repeat;
+ background-position: calc(100% - 14px) center; // 2
+ background-size: 12px 6px;
+ }
+}
+
+optgroup {
+ margin: 0; // 6
+
+ color: inherit; // 4
+ font: inherit; // 5
+}
+
+
+// Checkbox and Radios
+// ---
+
+[type="checkbox"],
+[type="radio"] {
+ position: relative;
+
+ display: inline-block;
+ width: $unit*3;
+ height: $unit*3;
+ margin-right: $unit;
+ border: $border;
+
+ background: $neutral-00;
+
+ vertical-align: middle;
+
+ -webkit-appearance: none;
+ -webkit-tap-highlight-color: rgba(0, 0, 0, 0);
+
+ &:active {
+ background: $neutral-30;
+ }
+
+ &:checked {
+ border: 0;
+
+ background: $brand-color;
+
+ &::after {
+ content: '';
+
+ position: absolute;
+
+ display: block;
+ }
+ }
+
+ &:disabled {
+ border: 0;
+
+ background: rgba($neutral-20, 0.5);
+ }
+}
+
+[type="radio"] {
+ border-radius: 50%;
+
+ &:after {
+ top: $unit;
+ right: $unit;
+ bottom: $unit;
+ left: $unit;
+
+ border-radius: 50%;
+ background: $neutral-00;
+ }
+}
+
+[type="checkbox"] {
+ &:checked {
+ &:after {
+ content: '';
+
+ top: 0;
+
+ display: block;
+ width: $unit*3;
+ height: $unit*3;
+
+ background: url($base__radio-checkmark);
+
+ color: $neutral-00;
+ line-height: 0;
+
+ pointer-events: none;
+ }
+ }
+}
+
+
+// Buttons
+// ---
+//
+// 1. Address margins set differently in Firefox 4+, Safari, and Chrome.
+// 2. Correct color not being inherited. Known issue: affects color of disabled
+// elements.
+// 3. Correct font properties not being inherited.
+// 4. Address inconsistent `text-transform` inheritance for `button` and `select`.
+// All other form control elements do not inherit `text-transform` values.
+// Correct `button` style inheritance in Firefox and Opera. Correct `select`
+// style inheritance in Firefox.
+// 5. Remove inner padding and border in Firefox 4+.
+// 6. Avoid the WebKit bug in Android 4.0.* where (2) destroys native `audio`
+// and `video` controls.
+// 7. Correct inability to style clickable `input` types in iOS.
+
+button,
+[type="submit"] {
+ display: block;
+ margin: 0; // 1
+ padding: 0;
+ border: 0;
+
+ background: $neutral-15;
+
+ color: inherit; // 2
+ font: inherit; // 3
+ line-height: $line-height;
+ text-transform: none; // 4
+
+ -webkit-appearance: none;
+ -webkit-tap-highlight-color: rgba(0, 0, 0, 0);
+
+ &::-moz-focus-inner {
+ padding: 0; // 5
+ border: 0; // 5
+ }
+
+ &:active {
+ background: $neutral-30;
+ }
+}
+
+html input[type="button"],
+// 6
+input[type="reset"] {
+ -webkit-appearance: button; // 7
+}
+
+
+// Disabled
+// ---
+//
+// 1. web-kit default disabled style
+// 2. Disabled style for button, checkbox, radio, input and select
+// 3. Checkbox and Radio style
+
+[disabled] {
+ opacity: 1;
+ background: $disabled-input-background-color;
+
+ color: $disabled-input-color;
+
+ -webkit-text-fill-color: $disabled-input-color; // 1
+
+ // 2
+ &:active,
+ &:checked {
+ border-color: $disabled-input-color;
+
+ background: $disabled-input-background-color;
+
+ &::after {
+ color: $disabled-input-color; // 3
+ }
+ }
+
+ &[type="radio"]:after {
+ background-color: $disabled-input-color;
+ }
+}
diff --git a/src/app/styles/base/_general.scss b/src/app/styles/base/_general.scss
new file mode 100644
index 0000000..88cf482
--- /dev/null
+++ b/src/app/styles/base/_general.scss
@@ -0,0 +1,136 @@
+// General
+// ===
+
+// Document
+// ---
+//
+// 1. Applying styles to *::before or *::after is a performance issue on some
+// legacy Android devices (4.1.x). As such, these are disabled by default.
+// 2. Prevent iOS text size adjust after orientation change, without disabling
+// user zoom.
+// 3. Remove default margin in all browsers.
+
+
+// *::before, // 1
+// *::after, // 1
+* {
+ box-sizing: border-box;
+
+ -moz-box-sizing: border-box;
+ -webkit-box-sizing: border-box;
+}
+
+html {
+ color: $font-color;
+ background-color: $neutral-12;
+ font-family: $font-family;
+ font-size: $font-size;
+ line-height: $line-height;
+ -webkit-font-smoothing: antialiased;
+ -moz-osx-font-smoothing: grayscale;
+ -ms-text-size-adjust: 100%; // 2
+ -webkit-text-size-adjust: 100%; // 2
+}
+
+body {
+ margin: 0;
+}
+
+// Grouping content
+// ---
+//
+// 1. Address margin not present in Safari.
+// 2. Contain overflow in all browsers.
+// 3. Address odd `em`-unit font size rendering in all browsers.The duplication
+// of `monospace` is intentional
+// ([Source](http://en.wikipedia.org/wiki/User:Davidgothberg/Test59)).
+
+figure {
+ // margin: 1em 40px; // 1
+ margin: 0 0 1rem; // 1
+}
+
+pre {
+ overflow: auto; // 2
+}
+
+code,
+kbd,
+pre,
+samp {
+ font-family: source-code-pro, Menlo, Monaco, Consolas, "Courier New", monospace; // 3
+ font-size: 1em; // 3
+}
+
+
+// New HTML5 elements
+// ---
+
+// Correct `block` display not defined for `main` in mobile Safari 6 and
+// Android 4.3.
+//
+// Correct `block` display not defined for `menu`.
+
+main,
+menu {
+ display: block;
+}
+
+// Normalize vertical alignment of `progress` in Chrome and Firefox.
+
+audio,
+canvas,
+progress,
+video {
+ vertical-align: baseline;
+}
+
+// Prevent modern browsers from displaying `audio` without controls.
+// Remove excess height in iOS 5 devices.
+
+audio:not([controls]) {
+ display: none;
+ height: 0;
+}
+
+// Hide the `template` element in mobile Safari.
+
+template {
+ display: none;
+}
+
+// Prevent `sub` and `sup` affecting `line-height` in all browsers.
+
+sub,
+sup {
+ position: relative;
+
+ font-size: 75%;
+ line-height: 0;
+ vertical-align: baseline;
+}
+
+sup {
+ top: -0.5em;
+}
+
+sub {
+ bottom: -0.25em;
+}
+
+
+// Hidden
+// ---
+//
+// Hide visually and from screen readers. `u-visually-hidden` class is preferred
+// to ensure that it's working in all conditions.
+
+[hidden] {
+ display: none;
+}
+
+@media only screen and (min-width: 768px) {
+ .c-Plp .container {
+ max-width: 100%;
+ }
+}
\ No newline at end of file
diff --git a/src/app/styles/base/_lists.scss b/src/app/styles/base/_lists.scss
new file mode 100644
index 0000000..c3f69d6
--- /dev/null
+++ b/src/app/styles/base/_lists.scss
@@ -0,0 +1,40 @@
+// Lists
+// ===
+
+
+// Ordered and Unordered Lists
+// ---
+//
+// We default to unstyled lists because they seem to be a more common usecase.
+// Use the extensions to re-add the defaults back in.
+//
+// It is our recommendation that instead of styling the ul and ol directly that
+// you create a list component that can be added to lists when needed.
+
+ul,
+ol {
+ margin: 0;
+ padding: 0;
+
+ list-style-type: none;
+}
+
+
+// Definition Lists
+// ---
+
+dl {
+ margin-bottom: $unit;
+
+ line-height: $line-height;
+}
+
+dt {
+ margin-top: $unit;
+
+ font-weight: $semi-bold-font-weight;
+}
+
+dd {
+ margin: 0;
+}
diff --git a/src/app/styles/base/_material.scss b/src/app/styles/base/_material.scss
new file mode 100644
index 0000000..393c471
--- /dev/null
+++ b/src/app/styles/base/_material.scss
@@ -0,0 +1,12 @@
+
+.material-icons {
+ &.md-18 { font-size: 18px; }
+ &.md-24 { font-size: 24px; }
+ &.md-36 { font-size: 36px; }
+ &.md-48 { font-size: 48px; }
+ &.md-dark { color: rgba(0, 0, 0, 0.54); }
+ &.md-dark.md-inactive { color: rgba(0, 0, 0, 0.26); }
+ &.md-light { color: rgba(255, 255, 255, 1); }
+ &.md-light.md-inactive { color: rgba(255, 255, 255, 0.3); }
+ &.orange600 { color: #FB8C00; }
+}
\ No newline at end of file
diff --git a/src/app/styles/base/_mixins.scss b/src/app/styles/base/_mixins.scss
new file mode 100644
index 0000000..da37a8e
--- /dev/null
+++ b/src/app/styles/base/_mixins.scss
@@ -0,0 +1,81 @@
+// Mixins
+// ===
+//
+// Background Shimmer
+// ---
+
+@mixin background-shimmer() {
+ overflow: hidden;
+ background: $neutral-10;
+ background-image: linear-gradient(to right,
+ $neutral-10 0%, scale-color($neutral-15, $lightness: -5%) 50%, $neutral-10 100%);
+ background-repeat: no-repeat;
+ background-size: 100vw 100vh;
+ animation: 1.5s linear infinite background-shimmer;
+}
+
+
+// Visually Hidden
+// ---
+
+@mixin visually-hidden() {
+ position: absolute;
+
+ overflow: hidden;
+ clip: rect(1px, 1px, 1px, 1px);
+ width: 1px;
+ height: 1px;
+ padding: 0;
+ border: 0;
+}
+
+
+// Wrap At Root
+// ---
+//
+// Wraps the nested blocks in the `$selector` of your choice, and at the root
+// level. Specificity of the selector can be increased by increasing the
+// `$degree` to a higher number than the default `1`.
+
+// Examples:
+// ---
+//
+// Default:
+//
+// @include wrap-at-root("#app") {
+// .u-border-red { border: 1px solid red; }
+// }
+//
+// Output: #app .u-border-red { border: 1px solid red; }
+//
+//
+// Custom:
+//
+// @include wrap-at-root("#app", 3) {
+// .u-border-red { border: 1px solid red; }
+// }
+//
+// Output: #app#app#app .u-border-red { border: 1px solid red; }
+
+
+// Parameters
+// ---
+//
+// @param $selector [String]: ID selector.
+// @param $degree [Number]: Effectively the number of id-level selectors you
+// need to override.
+
+@mixin wrap-at-root($selector, $degree: 1) {
+ $selector-chain: '';
+
+ // Build an id selector by chaining the same id onto itself once more than
+ // the specified degree. So if degree: 3, we get #id#id#id.
+
+ @for $i from 1 through $degree {
+ $selector-chain: $selector-chain + $selector;
+ }
+
+ @at-root #{$selector-chain} {
+ @content;
+ }
+}
diff --git a/src/app/styles/base/_tables.scss b/src/app/styles/base/_tables.scss
new file mode 100644
index 0000000..577208e
--- /dev/null
+++ b/src/app/styles/base/_tables.scss
@@ -0,0 +1,26 @@
+// Tables
+// ===
+
+table {
+ border-spacing: 0;
+ border-collapse: collapse;
+ width: 100%;
+ margin: $unit 0;
+}
+
+th {
+ padding: $unit 0;
+
+ font-weight: $semi-bold-font-weight;
+ text-align: left;
+}
+
+td {
+ padding: $unit 0;
+}
+
+tr,
+td,
+th {
+ vertical-align: middle;
+}
diff --git a/src/app/styles/base/_typography.scss b/src/app/styles/base/_typography.scss
new file mode 100644
index 0000000..12bfd38
--- /dev/null
+++ b/src/app/styles/base/_typography.scss
@@ -0,0 +1,117 @@
+// Typography
+// ===
+
+// Headers
+// ---
+@import url('https://use.fontawesome.com/releases/v5.1.0/css/all.css');
+@import url('https://fonts.googleapis.com/css?family=Poppins:300,400,500,600,700');
+@import url('https://fonts.googleapis.com/css?family=Roboto:300,400,500,700&display=swap');
+@import url('https://fonts.googleapis.com/icon?family=Material+Icons');
+@import url('https://fonts.googleapis.com/css?family=McLaren&display=swap" rel="stylesheet');
+
+h1,
+h2,
+h3,
+h4,
+h5,
+h6 {
+ margin: 0;
+
+ font-family: $header-font-family;
+ font-weight: $regular-font-weight;
+ line-height: 1.25;
+
+ // Added with webfontloader. See loadFonts() in app/container.js
+ .wf-active & {
+ font-family: $loaded-header-font-family;
+ }
+}
+
+h1,
+%h1 {
+ font-weight: $light-font-weight;
+ font-size: $huge-font-size;
+ line-height: $huge-line-height;
+}
+
+h2,
+%h2 {
+ font-size: $bigger-font-size;
+ line-height: $bigger-line-height;
+}
+
+h3,
+%h3,
+h4,
+%h4 {
+ font-size: $big-font-size;
+ line-height: $big-line-height;
+}
+
+h5,
+%h5 {
+ font-size: $font-size + 2;
+ line-height: $line-height;
+}
+
+h6,
+%h6 {
+ font-size: $font-size;
+ line-height: $line-height;
+}
+
+
+// Text Elements
+// ---
+
+p {
+ margin: 0;
+}
+
+a {
+ color: $link-color;
+ text-decoration: none;
+
+ &:active,
+ &:focus {
+ color: $active-link-color;
+ }
+}
+
+b,
+strong {
+ font-weight: $semi-bold-font-weight;
+}
+
+small {
+ font-size: 80%;
+}
+
+
+// Miscellaneous Elements
+// ---
+//
+// 1. Address differences between Firefox and other browsers.
+
+hr {
+ box-sizing: content-box; // 1
+ height: 0; // 1
+ margin: ($unit * 2) 0;
+ border: $border;
+ border-width: 0 0 1px;
+
+ -moz-box-sizing: content-box; // 1
+}
+
+img {
+ max-width: 100%;
+ margin: 0;
+}
+
+blockquote {
+ margin: ($unit * 2) 0;
+ padding-left: $unit;
+ border-left: 2px solid $border-color;
+
+ color: lighten($font-color, 15);
+}
diff --git a/src/app/styles/utilities/_border.scss b/src/app/styles/utilities/_border.scss
new file mode 100644
index 0000000..1246bac
--- /dev/null
+++ b/src/app/styles/utilities/_border.scss
@@ -0,0 +1,61 @@
+// Border
+// ===
+
+// Border (Width and Color)
+// ---
+
+.u-border {
+ border: $border;
+}
+
+.u-border-0 {
+ border: 0;
+}
+
+.u-border-top {
+ border-top: $border;
+}
+
+.u-border-end {
+ border-right: $border;
+}
+
+.u-border-bottom {
+ border-bottom: $border;
+}
+
+.u-border-start {
+ border-left: $border;
+}
+
+
+// Light Border (Width and Color)
+// ---
+
+.u-border-light {
+ border: $light-border;
+}
+
+.u-border-light-top {
+ border-top: $light-border;
+}
+
+.u-border-light-end {
+ border-right: $light-border;
+}
+
+.u-border-light-bottom {
+ border-bottom: $light-border;
+}
+
+.u-border-light-start {
+ border-left: $light-border;
+}
+
+
+// Border (Other)
+// ---
+
+.u-border-radius {
+ border-radius: $border-radius;
+}
diff --git a/src/app/styles/utilities/_box-shadow.scss b/src/app/styles/utilities/_box-shadow.scss
new file mode 100644
index 0000000..18a30d8
--- /dev/null
+++ b/src/app/styles/utilities/_box-shadow.scss
@@ -0,0 +1,18 @@
+// Box Shadow
+// ===
+
+.u-box-shadow {
+ box-shadow: $box-shadow;
+}
+
+.u-box-shadow-inset {
+ box-shadow: $inset-box-shadow;
+}
+
+.u-box-shadow-lg {
+ box-shadow: $large-box-shadow;
+}
+
+.u-box-shadow-input {
+ box-shadow: $input-box-shadow;
+}
diff --git a/src/app/styles/utilities/_color.scss b/src/app/styles/utilities/_color.scss
new file mode 100644
index 0000000..b1ac0df
--- /dev/null
+++ b/src/app/styles/utilities/_color.scss
@@ -0,0 +1,177 @@
+// Color
+// ===
+
+// Neutral Colors
+// ---
+
+.u-color-neutral-00 {
+ color: $neutral-00;
+
+ fill: $neutral-00;
+}
+
+.u-color-neutral-10 {
+ color: $neutral-10;
+
+ fill: $neutral-10;
+}
+
+.u-color-neutral-15 {
+ color: $neutral-15;
+
+ fill: $neutral-15;
+}
+
+.u-color-neutral-20 {
+ color: $neutral-20;
+
+ fill: $neutral-20;
+}
+
+.u-color-neutral-30 {
+ color: $neutral-30;
+
+ fill: $neutral-30;
+}
+
+.u-color-neutral-40 {
+ color: $neutral-40;
+
+ fill: $neutral-40;
+}
+
+.u-color-neutral-50 {
+ color: $neutral-50;
+
+ fill: $neutral-50;
+}
+
+.u-color-neutral-60 {
+ color: $neutral-60;
+
+ fill: $neutral-60;
+}
+
+.u-color-neutral-70 {
+ color: $neutral-70;
+
+ fill: $neutral-70;
+}
+
+
+// Other Colors
+// ---
+
+.u-color-brand {
+ color: $brand-color;
+
+ fill: $brand-color;
+}
+
+.u-color-accent {
+ color: $accent-color;
+
+ fill: $accent-color;
+}
+
+.u-color-primary-action {
+ color: $primary-action-color;
+
+ fill: $primary-action-color;
+}
+
+.u-color-secondary-action {
+ color: $secondary-action-color;
+
+ fill: $secondary-action-color;
+}
+
+.u-color-success {
+ color: $success-color;
+
+ fill: $success-color;
+}
+
+.u-color-error {
+ color: $error-color;
+
+ fill: $error-color;
+}
+
+.u-color-sale {
+ color: $sale-color;
+
+ fill: $sale-color;
+}
+
+
+// Background Colors
+// ---
+
+.u-bg-color-neutral-00 {
+ background-color: $neutral-00;
+}
+
+.u-bg-color-neutral-10 {
+ background-color: $neutral-10;
+}
+
+.u-bg-color-neutral-15 {
+ background-color: $neutral-15;
+}
+
+.u-bg-color-neutral-20 {
+ background-color: $neutral-20;
+}
+
+.u-bg-color-neutral-30 {
+ background-color: $neutral-30;
+}
+
+.u-bg-color-neutral-40 {
+ background-color: $neutral-40;
+}
+
+.u-bg-color-neutral-50 {
+ background-color: $neutral-50;
+}
+
+.u-bg-color-neutral-60 {
+ background-color: $neutral-60;
+}
+
+.u-bg-color-neutral-70 {
+ background-color: $neutral-70;
+}
+
+
+// Other Background Colors
+// ---
+
+.u-bg-color-brand {
+ background-color: $brand-color;
+}
+
+.u-bg-color-accent {
+ background-color: $accent-color;
+}
+
+.u-bg-color-primary-action {
+ background-color: $primary-action-color;
+}
+
+.u-bg-color-secondary-action {
+ background-color: $secondary-action-color;
+}
+
+.u-bg-color-success {
+ background-color: $success-color;
+}
+
+.u-bg-color-error {
+ background-color: $error-color;
+}
+
+.u-bg-color-sale {
+ background-color: $sale-color;
+}
diff --git a/src/app/styles/utilities/_display.scss b/src/app/styles/utilities/_display.scss
new file mode 100644
index 0000000..314c85f
--- /dev/null
+++ b/src/app/styles/utilities/_display.scss
@@ -0,0 +1,42 @@
+// Display
+// ===
+
+
+// Display: Block
+// ---
+
+.u-display-block {
+ display: block;
+}
+
+
+// Display: Inline Block
+// ---
+
+.u-display-inline-block {
+ display: inline-block;
+}
+
+
+// Display: Inline
+// ---
+
+.u-display-inline {
+ display: inline;
+}
+
+
+// Dispaly: Inline Table
+// ---
+
+.u-display-inline-table {
+ display: inline-table;
+}
+
+
+// Display: None
+// ---
+
+.u-display-none {
+ display: none;
+}
diff --git a/src/app/styles/utilities/_flexbox.scss b/src/app/styles/utilities/_flexbox.scss
new file mode 100644
index 0000000..ad56fb8
--- /dev/null
+++ b/src/app/styles/utilities/_flexbox.scss
@@ -0,0 +1,158 @@
+// Flexbox
+// ===
+//
+// Flexbox and align a row of items with a lot of control. Provides a
+// declarative, robust cross-browser way of using flexbox.
+
+
+// Flex (Grow)
+// ---
+//
+// Ensures that the flex child will grow to fill the available space.
+//
+// Note! This utility class should come **BEFORE** the `u-flexbox` class in
+// order to ensure that an element with both classes inherits only the
+// `display: flex` property.
+// 1. Ensure inline elements are treated as block-level.
+// 2. Prevent width collapsing in old implementations.
+
+.u-flex {
+ display: block; // 1
+ flex: 1 1 auto;
+ width: 100%; // 2
+}
+
+
+// Flex (Shrink)
+// ---
+//
+// 1. Ensure width collapses in old implementations.
+
+
+.u-flex-shrink {
+ flex: initial;
+ width: auto; // 1
+}
+
+
+// Flex (None)
+// ---
+//
+// Neither grow nor shrink!
+
+.u-flex-none {
+ flex: 0 0 auto;
+}
+
+
+// Flexbox
+// ---
+//
+// Includes gutters, for grid-like functionality.
+//
+// Note! This should come **AFTER** the `u-flex` utility class in order to
+// ensure an element that has both classes inherits `display: flex`!
+// 1. Remove default styles present on common root elements.
+// 2. Ensure in iOS 5 and 6 that flex-direction isn't inherited from
+// any flexbox parents that has flex-direction set to row-reverse
+
+
+.u-flexbox {
+ display: flex;
+ flex-direction: row; // 2
+ margin: 0; // 1
+ padding: 0; // 1
+
+ list-style: none; // 1
+}
+
+
+// Directions
+// ---
+
+.u-direction-column {
+ flex-direction: column;
+}
+
+.u-direction-column-reverse {
+ flex-direction: column-reverse;
+}
+
+.u-direction-row {
+ flex-direction: row;
+}
+
+.u-direction-row-reverse {
+ flex-direction: row-reverse;
+}
+
+
+// Align Items
+// ---
+
+.u-align-top {
+ align-items: flex-start;
+}
+
+.u-align-center {
+ align-items: center;
+}
+
+.u-align-bottom {
+ align-items: flex-end;
+}
+
+
+// Justify Content
+// ---
+
+.u-justify-start {
+ justify-content: flex-start;
+}
+
+.u-justify-end {
+ justify-content: flex-end;
+}
+
+.u-justify-between {
+ justify-content: space-between;
+}
+
+.u-justify-center {
+ justify-content: center;
+}
+
+.u-justify-around {
+ justify-content: space-around;
+}
+
+
+// Order
+// ---
+//
+// Re-arrange the display order of up to six items. Add additional modifiers in
+// your own stylesheet to re-order more.
+
+.u-order-1 {
+ order: 1;
+}
+
+.u-order-2 {
+ order: 2;
+}
+
+.u-order-3 {
+ order: 3;
+}
+
+.u-order-4 {
+ order: 4;
+}
+
+.u-order-5 {
+ order: 5;
+}
+
+.u-order-6 {
+ order: 6;
+}
diff --git a/src/app/styles/utilities/_heading.scss b/src/app/styles/utilities/_heading.scss
new file mode 100644
index 0000000..fc43802
--- /dev/null
+++ b/src/app/styles/utilities/_heading.scss
@@ -0,0 +1,26 @@
+// Heading
+// ===
+
+.u-h1 {
+ @extend %h1;
+}
+
+.u-h2 {
+ @extend %h2;
+}
+
+.u-h3 {
+ @extend %h3;
+}
+
+.u-h4 {
+ @extend %h4;
+}
+
+.u-h5 {
+ @extend %h5;
+}
+
+.u-h6 {
+ @extend %h6;
+}
diff --git a/src/app/styles/utilities/_layout.scss b/src/app/styles/utilities/_layout.scss
new file mode 100644
index 0000000..fdb847c
--- /dev/null
+++ b/src/app/styles/utilities/_layout.scss
@@ -0,0 +1,97 @@
+// Layout utils
+// ===
+
+// Floats
+// ---
+//
+// Writing-direction independent floats. For RTL stylesheets, re-define
+// these styles with the opposite direction. Templates need not be changed.
+
+.u-float-start {
+ float: left;
+}
+
+.u-float-end {
+ float: right;
+}
+
+
+// Contain floats
+// ---
+//
+// Contain all floated children and their margins.
+//
+// 1. Avoid an Opera bug when the contenteditable attribute is included anywhere
+// else in the document.
+// 2. Contain the the first child’s top margin. Ensures rendering is consistent
+// with the other common clearfix method, `overflow: hidden`.
+
+.u-clearfix {
+ &::before,
+ &::after {
+ content: ' '; // 1
+
+ display: table; // 2
+ }
+
+ &::after {
+ clear: both;
+ }
+}
+
+
+// Positioning relative
+// ---
+//
+// Create a new positioning context.
+
+.u-position-relative {
+ position: relative;
+}
+
+
+// Positioning absolute
+// ---
+//
+// Create a new positioning context.
+
+.u-position-absolute {
+ position: absolute;
+}
+
+
+// Positioning fixed
+// ---
+//
+// Create a new positioning context.
+
+.u-position-fixed {
+ position: fixed;
+}
+
+
+// Positioning coordinate
+// ---
+
+.u-position-top {
+ top: 0;
+}
+
+.u-position-right {
+ right: 0;
+}
+
+.u-position-bottom {
+ bottom: 0;
+}
+
+.u-position-left {
+ left: 0;
+}
+
+.u-position-full {
+ top: 0;
+ right: 0;
+ bottom: 0;
+ left: 0;
+}
diff --git a/src/app/styles/utilities/_margin.scss b/src/app/styles/utilities/_margin.scss
new file mode 100644
index 0000000..5654f11
--- /dev/null
+++ b/src/app/styles/utilities/_margin.scss
@@ -0,0 +1,159 @@
+// Margin
+// ===
+
+// Miscellanous
+// ---
+
+.u-margin-0 {
+ margin: 0;
+}
+
+.u-margin-center {
+ margin: 0 auto;
+}
+
+.u-margin-all {
+ margin-top: $unit;
+ margin-right: $unit;
+ margin-bottom: $unit;
+ margin-left: $unit;
+}
+
+
+// Vertical Margins
+// ---
+//
+// Granular control over vertical spacing independent of a component. Includes
+// values based on global spacing.
+//
+// Suffixes:
+// 0: none
+// sm: small
+// md: medium
+// lg: large
+
+.u-margin-top-0 {
+ margin-top: 0;
+}
+
+.u-margin-top {
+ margin-top: $unit;
+}
+
+.u-margin-top-sm {
+ margin-top: ($unit / 2);
+}
+
+.u-margin-top-md {
+ margin-top: $unit * 1.5;
+}
+
+.u-margin-top-lg {
+ margin-top: $unit * 2;
+}
+
+.u-margin-bottom-0 {
+ margin-bottom: 0;
+}
+
+.u-margin-bottom {
+ margin-bottom: $unit;
+}
+
+.u-margin-bottom-sm {
+ margin-bottom: ($unit / 2);
+}
+
+.u-margin-bottom-md {
+ margin-bottom: $unit * 1.5;
+}
+
+.u-margin-bottom-lg {
+ margin-bottom: $unit * 2;
+}
+
+
+// Horizontal Margins
+// ---
+//
+// Granular control over horizontal spacing independent of a component. Includes
+// values based on global spacing, and adds values based on relative typographic
+// sizes.
+//
+// `start` and `end` are writing-direction independent forms for `left` and
+// `right`. For right-to-left localizations, create an alternate version of this
+// stylesheet with the property directions reversed.
+//
+// Suffixes:
+//
+// 0: none
+// th: thick space (about 1.5× a “normal” word space)
+// en: en space (half an em)
+// em: em space
+// sm: small
+// md: medium
+// lg: large
+
+.u-margin-start-0 {
+ margin-left: 0;
+}
+
+.u-margin-start {
+ margin-left: $unit;
+}
+
+.u-margin-start-th {
+ margin-left: 0.333em;
+}
+
+.u-margin-start-en {
+ margin-left: 0.5em;
+}
+
+.u-margin-start-em {
+ margin-left: 1em;
+}
+
+.u-margin-start-sm {
+ margin-left: ($unit / 2);
+}
+
+.u-margin-start-md {
+ margin-left: $unit * 1.5;
+}
+
+.u-margin-start-lg {
+ margin-left: $unit * 2;
+}
+
+.u-margin-end-0 {
+ margin-right: 0;
+}
+
+.u-margin-end {
+ margin-right: $unit;
+}
+
+.u-margin-end-th {
+ margin-right: 0.333em;
+}
+
+.u-margin-end-en {
+ margin-right: 0.5em;
+}
+
+.u-margin-end-em {
+ margin-right: 1em;
+}
+
+.u-margin-end-sm {
+ margin-right: ($unit / 2);
+}
+
+.u-margin-end-md {
+ margin-right: $unit * 1.5;
+}
+
+.u-margin-end-lg {
+ margin-right: $unit * 2;
+}
diff --git a/src/app/styles/utilities/_padding.scss b/src/app/styles/utilities/_padding.scss
new file mode 100644
index 0000000..87a7920
--- /dev/null
+++ b/src/app/styles/utilities/_padding.scss
@@ -0,0 +1,164 @@
+// Padding
+// ===
+
+// Miscellanous
+// ---
+
+.u-padding-0 {
+ padding: 0;
+}
+
+.u-padding {
+ padding: $unit;
+}
+
+.u-padding-sm {
+ padding: ($unit / 2);
+}
+
+.u-padding-md {
+ padding: $unit * 1.5;
+}
+
+.u-padding-lg {
+ padding: $unit * 2;
+}
+
+// Vertical Padding
+// ---
+//
+// Granular control over vertical spacing independent of a component. Includes
+// values based on global spacing.
+//
+// Suffixes:
+// 0: none
+// sm: small
+// md: medium
+// lg: large
+
+.u-padding-top-0 {
+ padding-top: 0;
+}
+
+.u-padding-top {
+ padding-top: $unit;
+}
+
+.u-padding-top-sm {
+ padding-top: ($unit / 2);
+}
+
+.u-padding-top-md {
+ padding-top: $unit * 1.5;
+}
+
+.u-padding-top-lg {
+ padding-top: $unit * 2;
+}
+
+.u-padding-bottom-0 {
+ padding-bottom: 0;
+}
+
+.u-padding-bottom {
+ padding-bottom: $unit;
+}
+
+.u-padding-bottom-sm {
+ padding-bottom: ($unit / 2);
+}
+
+.u-padding-bottom-md {
+ padding-bottom: $unit * 1.5;
+}
+
+.u-padding-bottom-lg {
+ padding-bottom: $unit * 2;
+}
+
+
+// Horizontal Padding
+// ---
+//
+// Granular control over horizontal spacing independent of a component. Includes
+// values based on global spacing, and adds values based on relative typographic
+// sizes.
+//
+// `start` and `end` are writing-direction independent forms for `left` and
+// `right`. For right-to-left localizations, create an alternate version of this
+// stylesheet with the property directions reversed.
+//
+// Suffixes:
+//
+// 0: none
+// th: thick space (about 1.5× a “normal” word space)
+// en: en space (half an em)
+// em: em space
+// sm: small
+// md: medium
+// lg: large
+
+.u-padding-start-0 {
+ padding-left: 0;
+}
+
+.u-padding-start {
+ padding-left: $unit;
+}
+
+.u-padding-start-th {
+ padding-left: 0.333em;
+}
+
+.u-padding-start-en {
+ padding-left: 0.5em;
+}
+
+.u-padding-start-em {
+ padding-left: 1em;
+}
+
+.u-padding-start-sm {
+ padding-left: ($unit / 2);
+}
+
+.u-padding-start-md {
+ padding-left: $unit * 1.5;
+}
+
+.u-padding-start-lg {
+ padding-left: $unit * 2;
+}
+
+.u-padding-end-0 {
+ padding-right: 0;
+}
+
+.u-padding-end {
+ padding-right: $unit;
+}
+
+.u-padding-end-th {
+ padding-right: 0.333em;
+}
+
+.u-padding-end-en {
+ padding-right: 0.5em;
+}
+
+.u-padding-end-em {
+ padding-right: 1em;
+}
+
+.u-padding-end-sm {
+ padding-right: ($unit / 2);
+}
+
+.u-padding-end-md {
+ padding-right: $unit * 1.5;
+}
+
+.u-padding-end-lg {
+ padding-right: $unit * 2;
+}
+
diff --git a/src/app/styles/utilities/_text-content.scss b/src/app/styles/utilities/_text-content.scss
new file mode 100644
index 0000000..3cd95e9
--- /dev/null
+++ b/src/app/styles/utilities/_text-content.scss
@@ -0,0 +1,27 @@
+// Text Content
+// ===
+//
+// This is used to apply text-based-content styles to a container. Think of a
+// blog post as an example of text content.
+
+.u-text-content {
+ .u-h1 {
+ text-transform: uppercase;
+ }
+
+ .u-h2 {
+ font-weight: $semi-bold-font-weight;
+ }
+
+ .u-h3 {
+ color: $brand-color;
+ }
+
+ .u-h4 {
+ text-transform: uppercase;
+ }
+
+ p + p {
+ margin-top: $unit;
+ }
+}
diff --git a/src/app/styles/utilities/_text.scss b/src/app/styles/utilities/_text.scss
new file mode 100644
index 0000000..6005d6b
--- /dev/null
+++ b/src/app/styles/utilities/_text.scss
@@ -0,0 +1,206 @@
+// Text utils
+// ===
+
+
+// Font-Family
+// ---
+
+.u-text-family {
+ font-family: $font-family;
+}
+
+.u-text-family-header {
+ font-family: $header-font-family;
+
+ // Added with webfontloader. See loadFonts() in app/container.js
+ .wf-active & {
+ font-family: $loaded-header-font-family;
+ }
+}
+
+.u-text-family-loaded {
+ font-family: $loaded-header-font-family;
+}
+
+
+// Weights & Style
+// ---
+
+.u-text-weight-thin {
+ font-weight: $thin-font-weight;
+}
+
+.u-text-weight-extra-light {
+ font-weight: $extra-light-font-weight;
+}
+
+.u-text-weight-light {
+ font-weight: $light-font-weight;
+}
+
+.u-text-weight-regular {
+ font-weight: $regular-font-weight;
+}
+
+.u-text-weight-medium {
+ font-weight: $medium-font-weight;
+}
+
+.u-text-weight-semi-bold {
+ font-weight: $semi-bold-font-weight;
+}
+
+.u-text-weight-bold {
+ font-weight: $bold-font-weight;
+}
+
+.u-text-style-italic {
+ font-style: italic;
+}
+
+.u-text-quiet {
+ color: $neutral-50;
+ font-size: $small-font-size;
+
+ fill: $neutral-50;
+}
+
+
+// Size
+// ---
+
+.u-text-size-smaller {
+ font-size: $smaller-font-size;
+}
+
+.u-text-size-small {
+ font-size: $small-font-size;
+}
+
+.u-text-size-base {
+ font-size: $font-size;
+}
+
+.u-text-size-big {
+ font-size: $big-font-size;
+}
+
+.u-text-size-bigger {
+ font-size: $bigger-font-size;
+}
+
+.u-text-size-huge {
+ font-size: $huge-font-size;
+}
+
+
+// Line Height
+// ---
+
+.u-text-height-0 {
+ line-height: 0;
+}
+
+.u-text-height-tiny {
+ line-height: $tiny-line-height;
+}
+
+.u-text-height-smaller {
+ line-height: $smaller-line-height;
+}
+
+.u-text-height-small {
+ line-height: $small-line-height;
+}
+
+.u-text-height-base {
+ line-height: $line-height;
+}
+
+.u-text-height-big {
+ line-height: $big-line-height;
+}
+
+.u-text-height-bigger {
+ line-height: $bigger-line-height;
+}
+
+.u-text-height-huge {
+ line-height: $huge-line-height;
+}
+
+
+// Letter Spacing
+// ---
+
+.u-text-letter-spacing-small {
+ letter-spacing: 1px;
+}
+
+.u-text-letter-spacing-normal {
+ letter-spacing: normal;
+}
+
+
+// Text Alignment
+// ---
+
+.u-text-align-start {
+ text-align: left;
+}
+
+.u-text-align-center {
+ text-align: center;
+}
+
+.u-text-align-end {
+ text-align: right;
+}
+
+.u-text-align-middle {
+ vertical-align: middle;
+}
+
+.u-text-align-baseline {
+ vertical-align: baseline;
+}
+
+
+// All Caps
+// ---
+//
+// Set text uppercase, letterspaced for legibility.
+
+.u-text-uppercase {
+ text-transform: uppercase;
+}
+
+
+// Capitalize Text
+// ---
+//
+// Capitalize first letter of string.
+
+.u-text-capitalize {
+ text-transform: capitalize;
+}
+
+
+// Lowercase Text
+// ---
+//
+// Lowercase all text in string.
+
+.u-text-lowercase {
+ text-transform: lowercase;
+}
+
+
+// Strikethrough Text
+// ---
+//
+// Strikethrough all text in string.
+
+.u-text-strikethrough {
+ text-decoration: line-through;
+}
diff --git a/src/app/styles/utilities/_visibility.scss b/src/app/styles/utilities/_visibility.scss
new file mode 100644
index 0000000..2435ecc
--- /dev/null
+++ b/src/app/styles/utilities/_visibility.scss
@@ -0,0 +1,8 @@
+// Visibility Utils
+// ===
+//
+// Remove from flow but leave available to screen readers.
+
+.u-visually-hidden {
+ @include visually-hidden();
+}
diff --git a/src/app/styles/utilities/_z-index.scss b/src/app/styles/utilities/_z-index.scss
new file mode 100644
index 0000000..dcbaef0
--- /dev/null
+++ b/src/app/styles/utilities/_z-index.scss
@@ -0,0 +1,18 @@
+// Z-Index
+// ===
+
+.u-z-index-1 {
+ z-index: $z1-depth;
+}
+
+.u-z-index-2 {
+ z-index: $z2-depth;
+}
+
+.u-z-index-3 {
+ z-index: $z3-depth;
+}
+
+.u-z-index-4 {
+ z-index: $z4-depth;
+}
diff --git a/src/index.css b/src/index.css
deleted file mode 100644
index ec2585e..0000000
--- a/src/index.css
+++ /dev/null
@@ -1,13 +0,0 @@
-body {
- margin: 0;
- font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', 'Roboto', 'Oxygen',
- 'Ubuntu', 'Cantarell', 'Fira Sans', 'Droid Sans', 'Helvetica Neue',
- sans-serif;
- -webkit-font-smoothing: antialiased;
- -moz-osx-font-smoothing: grayscale;
-}
-
-code {
- font-family: source-code-pro, Menlo, Monaco, Consolas, 'Courier New',
- monospace;
-}
diff --git a/src/index.js b/src/index.js
deleted file mode 100644
index f5185c1..0000000
--- a/src/index.js
+++ /dev/null
@@ -1,17 +0,0 @@
-import React from 'react';
-import ReactDOM from 'react-dom';
-import './index.css';
-import App from './App';
-import * as serviceWorker from './serviceWorker';
-
-ReactDOM.render(
-
-
- ,
- document.getElementById('root')
-);
-
-// If you want your app to work offline and load faster, you can change
-// unregister() to register() below. Note this comes with some pitfalls.
-// Learn more about service workers: https://bit.ly/CRA-PWA
-serviceWorker.unregister();
diff --git a/src/logo.svg b/src/logo.svg
deleted file mode 100644
index 6b60c10..0000000
--- a/src/logo.svg
+++ /dev/null
@@ -1,7 +0,0 @@
-