diff --git a/client/config-overrides.js b/client/config-overrides.js new file mode 100644 index 0000000..a9fcefe --- /dev/null +++ b/client/config-overrides.js @@ -0,0 +1,6 @@ +module.exports = { + jest: function(config) { + config.testMatch.push("/src/**/{spec,test}.{js,jsx,ts,tsx}"); + return config; + } +}; diff --git a/client/package-lock.json b/client/package-lock.json index c190260..e20baeb 100644 --- a/client/package-lock.json +++ b/client/package-lock.json @@ -932,6 +932,24 @@ "resolved": "https://registry.npmjs.org/@csstools/normalize.css/-/normalize.css-9.0.1.tgz", "integrity": "sha512-6It2EVfGskxZCQhuykrfnALg7oVeiI6KclWSmGDqB0AiInVrTGB9Jp9i4/Ad21u9Jde/voVQz6eFX/eSg/UsPA==" }, + "@emotion/is-prop-valid": { + "version": "0.7.3", + "resolved": "https://registry.npmjs.org/@emotion/is-prop-valid/-/is-prop-valid-0.7.3.tgz", + "integrity": "sha512-uxJqm/sqwXw3YPA5GXX365OBcJGFtxUVkB6WyezqFHlNe9jqUWH5ur2O2M8dGBz61kn1g3ZBlzUunFQXQIClhA==", + "requires": { + "@emotion/memoize": "0.7.1" + } + }, + "@emotion/memoize": { + "version": "0.7.1", + "resolved": "https://registry.npmjs.org/@emotion/memoize/-/memoize-0.7.1.tgz", + "integrity": "sha512-Qv4LTqO11jepd5Qmlp3M1YEjBumoTHcHFdgPTQ+sFlIL5myi/7xu/POwP7IRu6odBdmLXdtIs1D6TuW6kbwbbg==" + }, + "@emotion/unitless": { + "version": "0.7.3", + "resolved": "https://registry.npmjs.org/@emotion/unitless/-/unitless-0.7.3.tgz", + "integrity": "sha512-4zAPlpDEh2VwXswwr/t8xGNDGg8RQiPxtxZ3qQEXyQsBV39ptTdESCjuBvGze1nLMVrxmTIKmnO/nAV8Tqjjzg==" + }, "@jest/console": { "version": "24.7.1", "resolved": "https://registry.npmjs.org/@jest/console/-/console-24.7.1.tgz", @@ -2185,6 +2203,22 @@ "resolved": "https://registry.npmjs.org/babel-plugin-named-asset-import/-/babel-plugin-named-asset-import-0.3.2.tgz", "integrity": "sha512-CxwvxrZ9OirpXQ201Ec57OmGhmI8/ui/GwTDy0hSp6CmRvgRC0pSair6Z04Ck+JStA0sMPZzSJ3uE4n17EXpPQ==" }, + "babel-plugin-styled-components": { + "version": "1.10.0", + "resolved": "https://registry.npmjs.org/babel-plugin-styled-components/-/babel-plugin-styled-components-1.10.0.tgz", + "integrity": "sha512-sQVKG8irFXx14ZfaK1bBePirfkacl3j8nZwSZK+ZjsbnadRHKQTbhXbe/RB1vT6Vgkz45E+V95LBq4KqdhZUNw==", + "requires": { + "@babel/helper-annotate-as-pure": "^7.0.0", + "@babel/helper-module-imports": "^7.0.0", + "babel-plugin-syntax-jsx": "^6.18.0", + "lodash": "^4.17.10" + } + }, + "babel-plugin-syntax-jsx": { + "version": "6.18.0", + "resolved": "https://registry.npmjs.org/babel-plugin-syntax-jsx/-/babel-plugin-syntax-jsx-6.18.0.tgz", + "integrity": "sha1-CvMqmm4Tyno/1QaeYtew9Y0NiUY=" + }, "babel-plugin-syntax-object-rest-spread": { "version": "6.13.0", "resolved": "https://registry.npmjs.org/babel-plugin-syntax-object-rest-spread/-/babel-plugin-syntax-object-rest-spread-6.13.0.tgz", @@ -2728,6 +2762,11 @@ "map-obj": "^1.0.0" } }, + "camelize": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/camelize/-/camelize-1.0.0.tgz", + "integrity": "sha1-FkpUg+Yw+kMh5a8HAg5TGDGyYJs=" + }, "caniuse-api": { "version": "3.0.0", "resolved": "https://registry.npmjs.org/caniuse-api/-/caniuse-api-3.0.0.tgz", @@ -3792,6 +3831,11 @@ "postcss": "^7.0.5" } }, + "css-color-keywords": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/css-color-keywords/-/css-color-keywords-1.0.0.tgz", + "integrity": "sha1-/qJhbcZ2spYmhrOvjb2+GAskTgU=" + }, "css-color-names": { "version": "0.0.4", "resolved": "https://registry.npmjs.org/css-color-names/-/css-color-names-0.0.4.tgz", @@ -3886,6 +3930,16 @@ "resolved": "https://registry.npmjs.org/css-select-base-adapter/-/css-select-base-adapter-0.1.1.tgz", "integrity": "sha512-jQVeeRG70QI08vSTwf1jHxp74JoZsr2XSgETae8/xC8ovSnL2WF87GTLO86Sbwdt2lK4Umg4HnnwMO4YF3Ce7w==" }, + "css-to-react-native": { + "version": "2.3.1", + "resolved": "https://registry.npmjs.org/css-to-react-native/-/css-to-react-native-2.3.1.tgz", + "integrity": "sha512-yO+oEx1Lf+hDKasqQRVrAvzMCz825Huh1VMlEEDlRWyAhFb/FWb6I0KpEF1PkyKQ7NEdcx9d5M2ZEWgJAsgPvQ==", + "requires": { + "camelize": "^1.0.0", + "css-color-keywords": "^1.0.0", + "postcss-value-parser": "^3.3.0" + } + }, "css-tree": { "version": "1.0.0-alpha.28", "resolved": "https://registry.npmjs.org/css-tree/-/css-tree-1.0.0-alpha.28.tgz", @@ -9107,6 +9161,11 @@ } } }, + "memoize-one": { + "version": "5.0.4", + "resolved": "https://registry.npmjs.org/memoize-one/-/memoize-one-5.0.4.tgz", + "integrity": "sha512-P0z5IeAH6qHHGkJIXWw0xC2HNEgkx/9uWWBQw64FJj3/ol14VYdfVGWWr0fXfjhhv3TKVIqUq65os6O4GUNksA==" + }, "memory-fs": { "version": "0.4.1", "resolved": "https://registry.npmjs.org/memory-fs/-/memory-fs-0.4.1.tgz", @@ -11372,6 +11431,32 @@ "whatwg-fetch": "3.0.0" } }, + "react-app-rewired": { + "version": "2.1.3", + "resolved": "https://registry.npmjs.org/react-app-rewired/-/react-app-rewired-2.1.3.tgz", + "integrity": "sha512-NXC2EsQrnEMV7xD70rHcBq0B4PSEzjY/K2m/e+GRgit2jZO/uZApnpCZSKvIX2leLRN69Sqf2id0VXZ1F62CDw==", + "dev": true, + "requires": { + "cross-spawn": "^6.0.5", + "dotenv": "^6.2.0", + "semver": "^5.6.0" + }, + "dependencies": { + "cross-spawn": { + "version": "6.0.5", + "resolved": "https://registry.npmjs.org/cross-spawn/-/cross-spawn-6.0.5.tgz", + "integrity": "sha512-eTVLrBSt7fjbDygz805pMnstIs2VTBNkRm0qxZd+M7A5XDdxVRWO5MxGBXZhjY4cqLYLdtrGqRf8mBPmzwSpWQ==", + "dev": true, + "requires": { + "nice-try": "^1.0.4", + "path-key": "^2.0.1", + "semver": "^5.5.0", + "shebang-command": "^1.2.0", + "which": "^1.2.9" + } + } + } + }, "react-dev-utils": { "version": "9.0.0", "resolved": "https://registry.npmjs.org/react-dev-utils/-/react-dev-utils-9.0.0.tgz", @@ -12943,6 +13028,34 @@ "schema-utils": "^1.0.0" } }, + "styled-components": { + "version": "4.2.0", + "resolved": "https://registry.npmjs.org/styled-components/-/styled-components-4.2.0.tgz", + "integrity": "sha512-L/LzkL3ZbBhqIVHdR7DbYujy4tqvTNRfc+4JWDCYyhTatI+8CRRQUmdaR0+ARl03DWsfKLhjewll5uNLrqrl4A==", + "requires": { + "@babel/helper-module-imports": "^7.0.0", + "@emotion/is-prop-valid": "^0.7.3", + "@emotion/unitless": "^0.7.0", + "babel-plugin-styled-components": ">= 1", + "css-to-react-native": "^2.2.2", + "memoize-one": "^5.0.0", + "prop-types": "^15.5.4", + "react-is": "^16.6.0", + "stylis": "^3.5.0", + "stylis-rule-sheet": "^0.0.10", + "supports-color": "^5.5.0" + }, + "dependencies": { + "supports-color": { + "version": "5.5.0", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-5.5.0.tgz", + "integrity": "sha512-QjVjwdXIt408MIiAqCX4oUKsgU2EqAGzs2Ppkm4aQYbjm+ZEWEcW4SfFNTr4uMNZma0ey4f5lgLrkB0aX0QMow==", + "requires": { + "has-flag": "^3.0.0" + } + } + } + }, "stylehacks": { "version": "4.0.3", "resolved": "https://registry.npmjs.org/stylehacks/-/stylehacks-4.0.3.tgz", @@ -12965,6 +13078,16 @@ } } }, + "stylis": { + "version": "3.5.4", + "resolved": "https://registry.npmjs.org/stylis/-/stylis-3.5.4.tgz", + "integrity": "sha512-8/3pSmthWM7lsPBKv7NXkzn2Uc9W7NotcwGNpJaa3k7WMM1XDCA4MgT5k/8BIexd5ydZdboXtU90XH9Ec4Bv/Q==" + }, + "stylis-rule-sheet": { + "version": "0.0.10", + "resolved": "https://registry.npmjs.org/stylis-rule-sheet/-/stylis-rule-sheet-0.0.10.tgz", + "integrity": "sha512-nTbZoaqoBnmK+ptANthb10ZRZOGC+EmTLLUxeYIuHNkEKcmKgXX1XWKkUBT2Ac4es3NybooPe0SmvKdhKJZAuw==" + }, "supports-color": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-2.0.0.tgz", diff --git a/client/package.json b/client/package.json index ffc211e..78fc1e7 100644 --- a/client/package.json +++ b/client/package.json @@ -6,18 +6,20 @@ "bulma": "^0.7.4", "bulma-extensions": "^6.2.4", "faker": "^4.1.0", + "lodash": "^4.17.11", "node-sass": "^4.11.0", "react": "v16.8.3", "react-dom": "v16.8.3", "react-json-editor-ajrm": "^2.5.9", "react-scripts": "3.0.0", "scrollreveal": "^4.0.5", + "styled-components": "^4.2.0", "uuid": "^3.3.2" }, "scripts": { "start": "react-scripts start", "build": "react-scripts build", - "test": "REACT_APP_MOCKIT_API_URL=localhost react-scripts test", + "test": "react-app-rewired test", "eject": "react-scripts eject" }, "jest": { @@ -40,6 +42,7 @@ "dom-testing-library": "^3.19.1", "jest-dom": "^3.1.3", "nock": "^10.0.6", + "react-app-rewired": "^2.1.3", "react-testing-library": "^6.1.2" } } diff --git a/client/src/App.js b/client/src/App.js index 4cd7413..ed8e9e5 100755 --- a/client/src/App.js +++ b/client/src/App.js @@ -1,6 +1,7 @@ import React, { useState } from "react"; import useScrollReval from "./hooks/useScrollReveal"; -import RouteList from "./components/RouteList"; +import RouteListStack from "./components/RouteListStack"; +import RouteListGroup from "./components/RouteListGroup"; import Logo from "./components/Logo"; import { buildRoute, deleteRoute } from "./utils/routes-api"; @@ -9,21 +10,20 @@ import RouteModal from "./components/RouteModal"; import SettingsModal from "./components/SettingsModal"; import ConfirmationDialog from "./components/ConfirmationDialog"; -import { settings, routes } from "./config/routes.json"; +import { settings, routes as configRoutes } from "./config/routes.json"; import "./scss/index.scss"; -export default function({ settings: propSettings }) { - useScrollReval([ - { selector: ".hero .title, .card, .subtitle " }, - { selector: ".route", options: { duration: 750, distance: "40px", easing: "cubic-bezier(0.5, -0.01, 0, 1.005)", interval: 64, origin: "bottom", viewFactor: 0.32 } } - ]); +export default function({ settings: propSettings, customRoutes }) { + useScrollReval([{ selector: ".hero .title, .card, .subtitle " }]); const [selectedRoute, setSelectedRoute] = useState(); const [routeToBeRemoved, setRouteToBeRemoved] = useState(); const [settingsModalVisible, showSettingsModal] = useState(false); - const { features: { chaosMonkey = false } = {} } = propSettings || settings; + const routes = customRoutes || configRoutes; + + const { features: { chaosMonkey = false, groupedRoutes = false } = {} } = propSettings || settings; return ( <> @@ -73,8 +73,17 @@ export default function({ settings: propSettings }) {

The chaos monkey is enabled and causing havoc on all APIs...

)} + {routes.length === 0 && ( +

+ No routes found. Click "Add Route" to get started. +

+ )} - setSelectedRoute(route)} onRouteDelete={route => setRouteToBeRemoved(route)} /> + {groupedRoutes ? ( + setSelectedRoute(route)} onRouteDelete={route => setRouteToBeRemoved(route)} /> + ) : ( + setSelectedRoute(route)} onRouteDelete={route => setRouteToBeRemoved(route)} /> + )}