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 ( -
-
- logo -

- Edit src/App.js and save to reload. -

- - Learn React - -
-
- ); -} - -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 ( + +
{inputListSection}
+ + + ) + } +} + +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 @@ - - - - - - -