diff --git a/README.md b/README.md
index c82d47fc..660378de 100644
--- a/README.md
+++ b/README.md
@@ -64,7 +64,7 @@ Our pull request template will request that you fill out some key fields. It'll
### Code style
-This repository uses eslint to enforce a consistent style for frontend JavaScript code.
+This repository uses eslint to enforce a consistent style for frontend Typescript code.
Before committing, run `dev/docker-lint.sh` (Mac/Linux) or `dev\docker-lint.bat` (Windows) to check for style errors and automatically fix formatting issues. (You will need to run `docker-compose up` or `docker-compose build` at least once before the docker-lint script will work.)
@@ -113,6 +113,7 @@ The build steps are defined in `cloudbuild.yaml`.
package managers offer similar performance, we were already using NPM for backend
package management, and the Yarn roadmap did not offer compelling
improvements going forward.
+- **Typescript** - To enforce build time static typing and make our code more scalable.
- **React** - Selected for popularity, simple view, and speedy virutal DOM. Code lives in the `/frontend` directory. It was built using
[Create React App](https://facebook.github.io/create-react-app/docs/folder-structure).
- **Material UI** - which we use over Bootstrap since MUI doesn't rely on jQuery. It has a
@@ -122,7 +123,7 @@ popular React framework and looks great on mobile.
- **React Hooks** - to manage interactions with state management.
- **Functional Components** - We migrated away from ES6 React Components and toward React
[Functional Components](https://reactjs.org/docs/components-and-props.html) due to the simpler component logic and the ability to use React Hooks that Functional Components offer.
-- **ESLint** - Linting set in the format of AirBNB Style.
+- **ESLint** - Linting set in the format of AirBNB Style + with Typescript additions.
- **Prettier** - Code formatter to maintain standard code format for the frontend code.
- **Husky** - Pre-commit hook to trigger Prettier auto formatting before pushing to Github.
diff --git a/frontend/.eslintrc.json b/frontend/.eslintrc.json
index c11fbca5..6787a7b0 100644
--- a/frontend/.eslintrc.json
+++ b/frontend/.eslintrc.json
@@ -1,11 +1,17 @@
{
- "extends": ["airbnb", "prettier", "prettier/react"],
+ "extends": [
+ "airbnb",
+ "prettier",
+ "prettier/react",
+ "plugin:@typescript-eslint/recommended",
+ "plugin:import/typescript"
+ ],
"plugins": ["react", "prettier"],
"env": {
"browser": true,
"jest": true
},
- "parser": "babel-eslint",
+ "parser": "@typescript-eslint/parser",
"parserOptions": {
"ecmaVersion": 9,
"sourceType": "module",
@@ -40,14 +46,35 @@
"react/destructuring-assignment": "off",
"react/jsx-filename-extension": [
1,
- { "extensions": [".js", ".jsx"] }
+ { "extensions": [".js", ".jsx", ".ts", ".tsx"] }
],
- "react/prop-types": "off"
+ "react/prop-types": "off",
+ "import/extensions": [
+ "error",
+ "ignorePackages",
+ {
+ "js": "never",
+ "jsx": "never",
+ "ts": "never",
+ "tsx": "never"
+ }
+ ],
+ //remove in future
+ "@typescript-eslint/explicit-function-return-type": "off",
+ "@typescript-eslint/no-explicit-any":"off",
+ "@typescript-eslint/ban-ts-ignore": "off",
+ "react/no-children-prop": "off"
},
"settings": {
- "react": {
- "pragma": "React",
- "version": "16.8.6"
- }
+ "react": {
+ "pragma": "React",
+ "version": "detect"
+ },
+ "import/extensions": [
+ ".js", ".jsx", ".ts", ".tsx"
+ ],
+ "import/resolver": {
+ "typescript": {}
+ }
}
}
\ No newline at end of file
diff --git a/frontend/package-lock.json b/frontend/package-lock.json
index 3adae244..9979c348 100644
--- a/frontend/package-lock.json
+++ b/frontend/package-lock.json
@@ -4476,11 +4476,26 @@
"resolved": "https://registry.npmjs.org/@types/json-schema/-/json-schema-7.0.4.tgz",
"integrity": "sha512-8+KAKzEvSUdeo+kmqnKrqgeE+LcA0tjYWFY7RPProVYwnqDjukzO+3b6dLD56rYX5TdWejnEOLJYOIeh4CXKuA=="
},
+ "@types/json5": {
+ "version": "0.0.29",
+ "resolved": "https://registry.npmjs.org/@types/json5/-/json5-0.0.29.tgz",
+ "integrity": "sha1-7ihweulOEdK4J7y+UnC86n8+ce4=",
+ "dev": true
+ },
"@types/minimatch": {
"version": "3.0.3",
"resolved": "https://registry.npmjs.org/@types/minimatch/-/minimatch-3.0.3.tgz",
"integrity": "sha512-tHq6qdbT9U1IRSGf14CL0pUlULksvY9OZ+5eEgl1N7t+OA3tGvNpxJCzuKQlsNgCVwbAs670L1vcVQi8j9HjnA=="
},
+ "@types/moment-timezone": {
+ "version": "0.5.13",
+ "resolved": "https://registry.npmjs.org/@types/moment-timezone/-/moment-timezone-0.5.13.tgz",
+ "integrity": "sha512-SWk1qM8DRssS5YR9L4eEX7WUhK/wc96aIr4nMa6p0kTk9YhGGOJjECVhIdPEj13fvJw72Xun69gScXSZ/UmcPg==",
+ "dev": true,
+ "requires": {
+ "moment": ">=2.14.0"
+ }
+ },
"@types/node": {
"version": "12.12.31",
"resolved": "https://registry.npmjs.org/@types/node/-/node-12.12.31.tgz",
@@ -4543,49 +4558,49 @@
"integrity": "sha512-FA/BWv8t8ZWJ+gEOnLLd8ygxH/2UFbAvgEonyfN6yWGLKc7zVjbpl2Y4CTjid9h2RfgPP6SEt6uHwEOply00yw=="
},
"@typescript-eslint/eslint-plugin": {
- "version": "2.25.0",
- "resolved": "https://registry.npmjs.org/@typescript-eslint/eslint-plugin/-/eslint-plugin-2.25.0.tgz",
- "integrity": "sha512-W2YyMtjmlrOjtXc+FtTelVs9OhuR6OlYc4XKIslJ8PUJOqgYYAPRJhAqkYRQo3G4sjvG8jSodsNycEn4W2gHUw==",
+ "version": "2.28.0",
+ "resolved": "https://registry.npmjs.org/@typescript-eslint/eslint-plugin/-/eslint-plugin-2.28.0.tgz",
+ "integrity": "sha512-w0Ugcq2iatloEabQP56BRWJowliXUP5Wv6f9fKzjJmDW81hOTBxRoJ4LoEOxRpz9gcY51Libytd2ba3yLmSOfg==",
"requires": {
- "@typescript-eslint/experimental-utils": "2.25.0",
+ "@typescript-eslint/experimental-utils": "2.28.0",
"functional-red-black-tree": "^1.0.1",
"regexpp": "^3.0.0",
"tsutils": "^3.17.1"
},
"dependencies": {
"regexpp": {
- "version": "3.0.0",
- "resolved": "https://registry.npmjs.org/regexpp/-/regexpp-3.0.0.tgz",
- "integrity": "sha512-Z+hNr7RAVWxznLPuA7DIh8UNX1j9CDrUQxskw9IrBE1Dxue2lyXT+shqEIeLUjrokxIP8CMy1WkjgG3rTsd5/g=="
+ "version": "3.1.0",
+ "resolved": "https://registry.npmjs.org/regexpp/-/regexpp-3.1.0.tgz",
+ "integrity": "sha512-ZOIzd8yVsQQA7j8GCSlPGXwg5PfmA1mrq0JP4nGhh54LaKN3xdai/vHUDu74pKwV8OxseMS65u2NImosQcSD0Q=="
}
}
},
"@typescript-eslint/experimental-utils": {
- "version": "2.25.0",
- "resolved": "https://registry.npmjs.org/@typescript-eslint/experimental-utils/-/experimental-utils-2.25.0.tgz",
- "integrity": "sha512-0IZ4ZR5QkFYbaJk+8eJ2kYeA+1tzOE1sBjbwwtSV85oNWYUBep+EyhlZ7DLUCyhMUGuJpcCCFL0fDtYAP1zMZw==",
+ "version": "2.28.0",
+ "resolved": "https://registry.npmjs.org/@typescript-eslint/experimental-utils/-/experimental-utils-2.28.0.tgz",
+ "integrity": "sha512-4SL9OWjvFbHumM/Zh/ZeEjUFxrYKtdCi7At4GyKTbQlrj1HcphIDXlje4Uu4cY+qzszR5NdVin4CCm6AXCjd6w==",
"requires": {
"@types/json-schema": "^7.0.3",
- "@typescript-eslint/typescript-estree": "2.25.0",
+ "@typescript-eslint/typescript-estree": "2.28.0",
"eslint-scope": "^5.0.0",
"eslint-utils": "^2.0.0"
}
},
"@typescript-eslint/parser": {
- "version": "2.25.0",
- "resolved": "https://registry.npmjs.org/@typescript-eslint/parser/-/parser-2.25.0.tgz",
- "integrity": "sha512-mccBLaBSpNVgp191CP5W+8U1crTyXsRziWliCqzj02kpxdjKMvFHGJbK33NroquH3zB/gZ8H511HEsJBa2fNEg==",
+ "version": "2.28.0",
+ "resolved": "https://registry.npmjs.org/@typescript-eslint/parser/-/parser-2.28.0.tgz",
+ "integrity": "sha512-RqPybRDquui9d+K86lL7iPqH6Dfp9461oyqvlXMNtap+PyqYbkY5dB7LawQjDzot99fqzvS0ZLZdfe+1Bt3Jgw==",
"requires": {
"@types/eslint-visitor-keys": "^1.0.0",
- "@typescript-eslint/experimental-utils": "2.25.0",
- "@typescript-eslint/typescript-estree": "2.25.0",
+ "@typescript-eslint/experimental-utils": "2.28.0",
+ "@typescript-eslint/typescript-estree": "2.28.0",
"eslint-visitor-keys": "^1.1.0"
}
},
"@typescript-eslint/typescript-estree": {
- "version": "2.25.0",
- "resolved": "https://registry.npmjs.org/@typescript-eslint/typescript-estree/-/typescript-estree-2.25.0.tgz",
- "integrity": "sha512-VUksmx5lDxSi6GfmwSK7SSoIKSw9anukWWNitQPqt58LuYrKalzsgeuignbqnB+rK/xxGlSsCy8lYnwFfB6YJg==",
+ "version": "2.28.0",
+ "resolved": "https://registry.npmjs.org/@typescript-eslint/typescript-estree/-/typescript-estree-2.28.0.tgz",
+ "integrity": "sha512-HDr8MP9wfwkiuqzRVkuM3BeDrOC4cKbO5a6BymZBHUt5y/2pL0BXD6I/C/ceq2IZoHWhcASk+5/zo+dwgu9V8Q==",
"requires": {
"debug": "^4.1.1",
"eslint-visitor-keys": "^1.1.0",
@@ -5192,6 +5207,34 @@
"postcss-value-parser": "^4.0.3"
}
},
+ "awesome-typescript-loader": {
+ "version": "5.2.1",
+ "resolved": "https://registry.npmjs.org/awesome-typescript-loader/-/awesome-typescript-loader-5.2.1.tgz",
+ "integrity": "sha512-slv66OAJB8orL+UUaTI3pKlLorwIvS4ARZzYR9iJJyGsEgOqueMfOMdKySWzZ73vIkEe3fcwFgsKMg4d8zyb1g==",
+ "requires": {
+ "chalk": "^2.4.1",
+ "enhanced-resolve": "^4.0.0",
+ "loader-utils": "^1.1.0",
+ "lodash": "^4.17.5",
+ "micromatch": "^3.1.9",
+ "mkdirp": "^0.5.1",
+ "source-map-support": "^0.5.3",
+ "webpack-log": "^1.2.0"
+ },
+ "dependencies": {
+ "webpack-log": {
+ "version": "1.2.0",
+ "resolved": "https://registry.npmjs.org/webpack-log/-/webpack-log-1.2.0.tgz",
+ "integrity": "sha512-U9AnICnu50HXtiqiDxuli5gLB5PGBo7VvcHx36jRZHwK4vzOYLbImqT4lwWwoMHdQWwEKw736fCHEekokTEKHA==",
+ "requires": {
+ "chalk": "^2.1.0",
+ "log-symbols": "^2.1.0",
+ "loglevelnext": "^1.0.1",
+ "uuid": "^3.1.0"
+ }
+ }
+ }
+ },
"aws-sign2": {
"version": "0.7.0",
"resolved": "https://registry.npmjs.org/aws-sign2/-/aws-sign2-0.7.0.tgz",
@@ -6925,9 +6968,9 @@
}
},
"core-js-pure": {
- "version": "3.6.4",
- "resolved": "https://registry.npmjs.org/core-js-pure/-/core-js-pure-3.6.4.tgz",
- "integrity": "sha512-epIhRLkXdgv32xIUFaaAry2wdxZYBi6bgM7cB136dzzXXa+dFyRLTZeLUJxnd8ShrmyVXBub63n2NHo2JAt8Cw=="
+ "version": "3.6.5",
+ "resolved": "https://registry.npmjs.org/core-js-pure/-/core-js-pure-3.6.5.tgz",
+ "integrity": "sha512-lacdXOimsiD0QyNf9BC/mxivNJ/ybBGJXQFKzRekp1WTHoVUWsUHEn+2T8GJAzzIhyOuXA+gOxCVN3l+5PLPUA=="
},
"core-util-is": {
"version": "1.0.2",
@@ -8500,6 +8543,39 @@
}
}
},
+ "eslint-import-resolver-typescript": {
+ "version": "2.0.0",
+ "resolved": "https://registry.npmjs.org/eslint-import-resolver-typescript/-/eslint-import-resolver-typescript-2.0.0.tgz",
+ "integrity": "sha512-bT5Frpl8UWoHBtY25vKUOMoVIMlJQOMefHLyQ4Tz3MQpIZ2N6yYKEEIHMo38bszBNUuMBW6M3+5JNYxeiGFH4w==",
+ "dev": true,
+ "requires": {
+ "debug": "^4.1.1",
+ "is-glob": "^4.0.1",
+ "resolve": "^1.12.0",
+ "tiny-glob": "^0.2.6",
+ "tsconfig-paths": "^3.9.0"
+ },
+ "dependencies": {
+ "debug": {
+ "version": "4.1.1",
+ "resolved": "https://registry.npmjs.org/debug/-/debug-4.1.1.tgz",
+ "integrity": "sha512-pYAIzeRo8J6KPEaJ0VWOh5Pzkbw/RetuzehGM7QRRX5he4fPHx2rdKMB256ehJCkX+XRQm16eZLqLNS8RSZXZw==",
+ "dev": true,
+ "requires": {
+ "ms": "^2.1.1"
+ }
+ },
+ "resolve": {
+ "version": "1.16.1",
+ "resolved": "https://registry.npmjs.org/resolve/-/resolve-1.16.1.tgz",
+ "integrity": "sha512-rmAglCSqWWMrrBv/XM6sW0NuRFiKViw/W4d9EbC4pt+49H8JwHy+mcGmALTEg504AUDcLTvb1T2q3E9AnmY+ig==",
+ "dev": true,
+ "requires": {
+ "path-parse": "^1.0.6"
+ }
+ }
+ }
+ },
"eslint-loader": {
"version": "2.2.1",
"resolved": "https://registry.npmjs.org/eslint-loader/-/eslint-loader-2.2.1.tgz",
@@ -8740,17 +8816,6 @@
"has": "^1.0.3"
}
},
- "object.fromentries": {
- "version": "2.0.2",
- "resolved": "https://registry.npmjs.org/object.fromentries/-/object.fromentries-2.0.2.tgz",
- "integrity": "sha512-r3ZiBH7MQppDJVLx6fhD618GKNG40CZYH9wgwdhKxBDDbQgjeWGGd4AtkZad84d291YxvWe7bJGuE65Anh0dxQ==",
- "requires": {
- "define-properties": "^1.1.3",
- "es-abstract": "^1.17.0-next.1",
- "function-bind": "^1.1.1",
- "has": "^1.0.3"
- }
- },
"object.values": {
"version": "1.1.1",
"resolved": "https://registry.npmjs.org/object.values/-/object.values-1.1.1.tgz",
@@ -8763,9 +8828,9 @@
}
},
"resolve": {
- "version": "1.15.1",
- "resolved": "https://registry.npmjs.org/resolve/-/resolve-1.15.1.tgz",
- "integrity": "sha512-84oo6ZTtoTUpjgNEr5SJyzQhzL72gaRodsSfyxC/AXRvwu0Yse9H8eF9IpGo7b8YetZhlI6v7ZQ6bKBFV/6S7w==",
+ "version": "1.16.1",
+ "resolved": "https://registry.npmjs.org/resolve/-/resolve-1.16.1.tgz",
+ "integrity": "sha512-rmAglCSqWWMrrBv/XM6sW0NuRFiKViw/W4d9EbC4pt+49H8JwHy+mcGmALTEg504AUDcLTvb1T2q3E9AnmY+ig==",
"requires": {
"path-parse": "^1.0.6"
}
@@ -8774,14 +8839,6 @@
"version": "6.3.0",
"resolved": "https://registry.npmjs.org/semver/-/semver-6.3.0.tgz",
"integrity": "sha512-b39TBaTSfV6yBrapU89p5fKekE2m/NwnDocOVruQFS1/veMgdzuPcnOM34M6CwxW8jH/lxEa5rBoDeUwu5HHTw=="
- },
- "xregexp": {
- "version": "4.3.0",
- "resolved": "https://registry.npmjs.org/xregexp/-/xregexp-4.3.0.tgz",
- "integrity": "sha512-7jXDIFXh5yJ/orPn4SXjuVrWWoi4Cr8jfV1eHv9CixKSbU+jY4mxfrBwAuDvupPNKpMUY+FeIqsVw/JLT9+B8g==",
- "requires": {
- "@babel/runtime-corejs3": "^7.8.3"
- }
}
}
},
@@ -9724,6 +9781,12 @@
"resolved": "https://registry.npmjs.org/globals/-/globals-11.12.0.tgz",
"integrity": "sha512-WOBp/EEGUiIsJSp7wcv/y6MO+lV9UoncWqxuFfm8eBwzWNgyfBd6Gz+IeKQ9jCmyhoH99g15M3T+QaVHFjizVA=="
},
+ "globalyzer": {
+ "version": "0.1.4",
+ "resolved": "https://registry.npmjs.org/globalyzer/-/globalyzer-0.1.4.tgz",
+ "integrity": "sha512-LeguVWaxgHN0MNbWC6YljNMzHkrCny9fzjmEUdnF1kQ7wATFD1RHFRqA1qxaX2tgxGENlcxjOflopBwj3YZiXA==",
+ "dev": true
+ },
"globby": {
"version": "8.0.2",
"resolved": "https://registry.npmjs.org/globby/-/globby-8.0.2.tgz",
@@ -9755,6 +9818,12 @@
}
}
},
+ "globrex": {
+ "version": "0.1.2",
+ "resolved": "https://registry.npmjs.org/globrex/-/globrex-0.1.2.tgz",
+ "integrity": "sha512-uHJgbwAMwNFf5mLst7IWLNg14x1CkeqglJb/K3doi4dw6q2IvAAmM/Y81kevy83wP+Sst+nutFTYOGg3d1lsxg==",
+ "dev": true
+ },
"graceful-fs": {
"version": "4.1.15",
"resolved": "https://registry.npmjs.org/graceful-fs/-/graceful-fs-4.1.15.tgz",
@@ -12660,7 +12729,6 @@
"version": "2.2.0",
"resolved": "https://registry.npmjs.org/log-symbols/-/log-symbols-2.2.0.tgz",
"integrity": "sha512-VeIAFslyIerEJLXHziedo2basKbMKtTw3vfn5IzG0XTjhAVEJyNHnL2p7vc+wBDSdQuUpNw3M2u6xb9QsAY5Eg==",
- "dev": true,
"requires": {
"chalk": "^2.0.1"
}
@@ -12693,6 +12761,15 @@
"resolved": "https://registry.npmjs.org/loglevel/-/loglevel-1.6.7.tgz",
"integrity": "sha512-cY2eLFrQSAfVPhCgH1s7JI73tMbg9YC3v3+ZHVW67sBS7UxWzNEk/ZBbSfLykBWHp33dqqtOv82gjhKEi81T/A=="
},
+ "loglevelnext": {
+ "version": "1.0.5",
+ "resolved": "https://registry.npmjs.org/loglevelnext/-/loglevelnext-1.0.5.tgz",
+ "integrity": "sha512-V/73qkPuJmx4BcBF19xPBr+0ZRVBhc4POxvZTZdMeXpJ4NItXSJ/MSwuFT0kQJlCbXvdlZoQQ/418bS1y9Jh6A==",
+ "requires": {
+ "es6-symbol": "^3.1.1",
+ "object.assign": "^4.1.0"
+ }
+ },
"loose-envify": {
"version": "1.4.0",
"resolved": "https://registry.npmjs.org/loose-envify/-/loose-envify-1.4.0.tgz",
@@ -13524,6 +13601,65 @@
"has": "^1.0.3"
}
},
+ "object.fromentries": {
+ "version": "2.0.2",
+ "resolved": "https://registry.npmjs.org/object.fromentries/-/object.fromentries-2.0.2.tgz",
+ "integrity": "sha512-r3ZiBH7MQppDJVLx6fhD618GKNG40CZYH9wgwdhKxBDDbQgjeWGGd4AtkZad84d291YxvWe7bJGuE65Anh0dxQ==",
+ "requires": {
+ "define-properties": "^1.1.3",
+ "es-abstract": "^1.17.0-next.1",
+ "function-bind": "^1.1.1",
+ "has": "^1.0.3"
+ },
+ "dependencies": {
+ "es-abstract": {
+ "version": "1.17.5",
+ "resolved": "https://registry.npmjs.org/es-abstract/-/es-abstract-1.17.5.tgz",
+ "integrity": "sha512-BR9auzDbySxOcfog0tLECW8l28eRGpDpU3Dm3Hp4q/N+VtLTmyj4EUN088XZWQDW/hzj6sYRDXeOFsaAODKvpg==",
+ "requires": {
+ "es-to-primitive": "^1.2.1",
+ "function-bind": "^1.1.1",
+ "has": "^1.0.3",
+ "has-symbols": "^1.0.1",
+ "is-callable": "^1.1.5",
+ "is-regex": "^1.0.5",
+ "object-inspect": "^1.7.0",
+ "object-keys": "^1.1.1",
+ "object.assign": "^4.1.0",
+ "string.prototype.trimleft": "^2.1.1",
+ "string.prototype.trimright": "^2.1.1"
+ }
+ },
+ "es-to-primitive": {
+ "version": "1.2.1",
+ "resolved": "https://registry.npmjs.org/es-to-primitive/-/es-to-primitive-1.2.1.tgz",
+ "integrity": "sha512-QCOllgZJtaUo9miYBcLChTUaHNjJF3PYs1VidD7AwiEj1kYxKeQTctLAezAOH5ZKRH0g2IgPn6KwB4IT8iRpvA==",
+ "requires": {
+ "is-callable": "^1.1.4",
+ "is-date-object": "^1.0.1",
+ "is-symbol": "^1.0.2"
+ }
+ },
+ "has-symbols": {
+ "version": "1.0.1",
+ "resolved": "https://registry.npmjs.org/has-symbols/-/has-symbols-1.0.1.tgz",
+ "integrity": "sha512-PLcsoqu++dmEIZB+6totNFKq/7Do+Z0u4oT0zKOJNl3lYK6vGwwu2hjHs+68OEZbTjiUE9bgOABXbP/GvrS0Kg=="
+ },
+ "is-callable": {
+ "version": "1.1.5",
+ "resolved": "https://registry.npmjs.org/is-callable/-/is-callable-1.1.5.tgz",
+ "integrity": "sha512-ESKv5sMCJB2jnHTWZ3O5itG+O128Hsus4K4Qh1h2/cgn2vbgnLSVqfV46AeJA9D5EeeLa9w81KUXMtn34zhX+Q=="
+ },
+ "is-regex": {
+ "version": "1.0.5",
+ "resolved": "https://registry.npmjs.org/is-regex/-/is-regex-1.0.5.tgz",
+ "integrity": "sha512-vlKW17SNq44owv5AQR3Cq0bQPEb8+kF3UKZ2fiZNOWtztYE5i0CzCZxFDwO58qAOWtxdBRVO/V5Qin1wjCqFYQ==",
+ "requires": {
+ "has": "^1.0.3"
+ }
+ }
+ }
+ },
"object.getownpropertydescriptors": {
"version": "2.1.0",
"resolved": "https://registry.npmjs.org/object.getownpropertydescriptors/-/object.getownpropertydescriptors-2.1.0.tgz",
@@ -17364,6 +17500,15 @@
"resolved": "https://registry.npmjs.org/source-map/-/source-map-0.5.7.tgz",
"integrity": "sha1-igOdLRAh0i0eoUyA2OpGi6LvP8w="
},
+ "source-map-loader": {
+ "version": "0.2.4",
+ "resolved": "https://registry.npmjs.org/source-map-loader/-/source-map-loader-0.2.4.tgz",
+ "integrity": "sha512-OU6UJUty+i2JDpTItnizPrlpOIBLmQbWMuBg9q5bVtnHACqw1tn9nNwqJLbv0/00JjnJb/Ee5g5WS5vrRv7zIQ==",
+ "requires": {
+ "async": "^2.5.0",
+ "loader-utils": "^1.1.0"
+ }
+ },
"source-map-resolve": {
"version": "0.5.2",
"resolved": "https://registry.npmjs.org/source-map-resolve/-/source-map-resolve-0.5.2.tgz",
@@ -18240,6 +18385,16 @@
"resolved": "https://registry.npmjs.org/timsort/-/timsort-0.3.0.tgz",
"integrity": "sha1-QFQRqOfmM5/mTbmiNN4R3DHgK9Q="
},
+ "tiny-glob": {
+ "version": "0.2.6",
+ "resolved": "https://registry.npmjs.org/tiny-glob/-/tiny-glob-0.2.6.tgz",
+ "integrity": "sha512-A7ewMqPu1B5PWwC3m7KVgAu96Ch5LA0w4SnEN/LbDREj/gAD0nPWboRbn8YoP9ISZXqeNAlMvKSKoEuhcfK3Pw==",
+ "dev": true,
+ "requires": {
+ "globalyzer": "^0.1.0",
+ "globrex": "^0.1.1"
+ }
+ },
"tiny-warning": {
"version": "1.0.3",
"resolved": "https://registry.npmjs.org/tiny-warning/-/tiny-warning-1.0.3.tgz",
@@ -18365,6 +18520,37 @@
"resolved": "https://registry.npmjs.org/ts-pnp/-/ts-pnp-1.1.6.tgz",
"integrity": "sha512-CrG5GqAAzMT7144Cl+UIFP7mz/iIhiy+xQ6GGcnjTezhALT02uPMRw7tgDSESgB5MsfKt55+GPWw4ir1kVtMIQ=="
},
+ "tsconfig-paths": {
+ "version": "3.9.0",
+ "resolved": "https://registry.npmjs.org/tsconfig-paths/-/tsconfig-paths-3.9.0.tgz",
+ "integrity": "sha512-dRcuzokWhajtZWkQsDVKbWyY+jgcLC5sqJhg2PSgf4ZkH2aHPvaOY8YWGhmjb68b5qqTfasSsDO9k7RUiEmZAw==",
+ "dev": true,
+ "requires": {
+ "@types/json5": "^0.0.29",
+ "json5": "^1.0.1",
+ "minimist": "^1.2.0",
+ "strip-bom": "^3.0.0"
+ },
+ "dependencies": {
+ "minimist": {
+ "version": "1.2.5",
+ "resolved": "https://registry.npmjs.org/minimist/-/minimist-1.2.5.tgz",
+ "integrity": "sha512-FM9nNUYrRBAELZQT3xeZQ7fmMOBg6nWNmJKTcgsJeaLstP/UODVpGsr5OhXhhXg6f+qtJ8uiZ+PUxkDWcgIXLw==",
+ "dev": true
+ }
+ }
+ },
+ "tsconfig-paths-webpack-plugin": {
+ "version": "3.2.0",
+ "resolved": "https://registry.npmjs.org/tsconfig-paths-webpack-plugin/-/tsconfig-paths-webpack-plugin-3.2.0.tgz",
+ "integrity": "sha512-S/gOOPOkV8rIL4LurZ1vUdYCVgo15iX9ZMJ6wx6w2OgcpT/G4wMyHB6WM+xheSqGMrWKuxFul+aXpCju3wmj/g==",
+ "dev": true,
+ "requires": {
+ "chalk": "^2.3.0",
+ "enhanced-resolve": "^4.0.0",
+ "tsconfig-paths": "^3.4.0"
+ }
+ },
"tslib": {
"version": "1.9.3",
"resolved": "https://registry.npmjs.org/tslib/-/tslib-1.9.3.tgz",
@@ -18444,6 +18630,12 @@
"resolved": "https://registry.npmjs.org/typedarray/-/typedarray-0.0.6.tgz",
"integrity": "sha1-hnrHTjhkGHsdPUfZlqeOxciDB3c="
},
+ "typescript": {
+ "version": "3.8.3",
+ "resolved": "https://registry.npmjs.org/typescript/-/typescript-3.8.3.tgz",
+ "integrity": "sha512-MYlEfn5VrLNsgudQTVJeNaQFUAI7DkhnOjdpAp4T+ku1TfQClewlbSuTVHiA+8skNBgaf02TL/kLOvig4y3G8w==",
+ "dev": true
+ },
"unicode-canonical-property-names-ecmascript": {
"version": "1.0.4",
"resolved": "https://registry.npmjs.org/unicode-canonical-property-names-ecmascript/-/unicode-canonical-property-names-ecmascript-1.0.4.tgz",
@@ -18847,6 +19039,8 @@
"integrity": "sha512-Ggd/Ktt7E7I8pxZRbGIs7vwqAPscSESMrCSkx2FtWeqmheJgCo2R74fTsZFCifr0VTPwqRpPv17+6b8Zp7th0Q==",
"optional": true,
"requires": {
+ "bindings": "^1.5.0",
+ "nan": "^2.12.1",
"node-pre-gyp": "*"
},
"dependencies": {
@@ -20814,6 +21008,14 @@
"resolved": "https://registry.npmjs.org/xmlchars/-/xmlchars-2.2.0.tgz",
"integrity": "sha512-JZnDKK8B0RCDw84FNdDAIpZK+JuJw+s7Lz8nksI7SIuU3UXJJslUthsi+uWBUYOwPFwW7W7PRLRfUKpxjtjFCw=="
},
+ "xregexp": {
+ "version": "4.3.0",
+ "resolved": "https://registry.npmjs.org/xregexp/-/xregexp-4.3.0.tgz",
+ "integrity": "sha512-7jXDIFXh5yJ/orPn4SXjuVrWWoi4Cr8jfV1eHv9CixKSbU+jY4mxfrBwAuDvupPNKpMUY+FeIqsVw/JLT9+B8g==",
+ "requires": {
+ "@babel/runtime-corejs3": "^7.8.3"
+ }
+ },
"xtend": {
"version": "4.0.2",
"resolved": "https://registry.npmjs.org/xtend/-/xtend-4.0.2.tgz",
diff --git a/frontend/package.json b/frontend/package.json
index 26ef5e58..76e65241 100644
--- a/frontend/package.json
+++ b/frontend/package.json
@@ -11,6 +11,7 @@
"@material-ui/lab": "^4.0.0-alpha.46",
"@material-ui/styles": "^4.9.0",
"@turf/turf": "^5.1.6",
+ "awesome-typescript-loader": "^5.2.1",
"axios": "0.19.0",
"clsx": "^1.1.0",
"d3": "^5.15.0",
@@ -31,14 +32,22 @@
"redux": "^4.0.5",
"redux-first-router": "^2.1.5",
"redux-first-router-link": "^2.1.1",
- "redux-thunk": "^2.3.0"
+ "redux-thunk": "^2.3.0",
+ "source-map-loader": "^0.2.4"
},
"devDependencies": {
+ "@types/moment-timezone": "^0.5.13",
+ "@types/node": "^13.13.0",
+ "@types/react": "^16.9.34",
+ "@types/react-dom": "^16.9.6",
+ "@typescript-eslint/eslint-plugin": "^2.28.0",
+ "@typescript-eslint/parser": "^2.28.0",
"babel-eslint": "10.1.0",
"eslint": "^6.8.0",
"eslint-config-airbnb": "^17.1.1",
"eslint-config-prettier": "^4.3.0",
"eslint-config-react-app": "^3.0.6",
+ "eslint-import-resolver-typescript": "^2.0.0",
"eslint-loader": "^2.2.1",
"eslint-plugin-flowtype": "^3.13.0",
"eslint-plugin-import": "^2.20.1",
@@ -47,7 +56,9 @@
"eslint-plugin-react": "^7.19.0",
"husky": "3.0.2",
"lint-staged": "^8.2.1",
- "prettier": "^1.19.1"
+ "prettier": "^1.19.1",
+ "tsconfig-paths-webpack-plugin": "^3.2.0",
+ "typescript": "^3.8.3"
},
"scripts": {
"start": "react-scripts start",
@@ -55,9 +66,9 @@
"test": "react-scripts test",
"eject": "react-scripts eject",
"heroku-postbuild": "npm build",
- "lint": "eslint src --fix --ext .jsx,.js",
- "lint:check": "eslint src --ext .jsx,.js",
- "lint:all": "eslint --fix --ext src/**/*.{js,jsx}"
+ "lint": "eslint src --fix --ext .jsx,.js,.tsx,.ts",
+ "lint:check": "eslint src --ext .jsx,.js,.tsx,.ts",
+ "lint:all": "eslint --fix --ext src/**/*.{js,jsx,ts,tsx}"
},
"eslintConfig": {
"extends": "react-app"
@@ -68,9 +79,9 @@
}
},
"lint-staged": {
- "src/**/*.{js,jsx}": [
+ "src/**/*.{js,jsx,ts,tsx}": [
"prettier --write",
- "eslint --fix src/",
+ "eslint --fix",
"git add"
]
},
diff --git a/frontend/src/App.test.js b/frontend/src/App.test.tsx
old mode 100755
new mode 100644
similarity index 100%
rename from frontend/src/App.test.js
rename to frontend/src/App.test.tsx
diff --git a/frontend/src/App.jsx b/frontend/src/App.tsx
old mode 100755
new mode 100644
similarity index 98%
rename from frontend/src/App.jsx
rename to frontend/src/App.tsx
index efef5f24..2d2929b7
--- a/frontend/src/App.jsx
+++ b/frontend/src/App.tsx
@@ -37,7 +37,11 @@ const Screens = {
const theme = createMuiTheme({
breakpoints: {
values: {
+ xs: 0,
+ sm: 600,
md: 1050,
+ lg: 1280,
+ xl: 1920,
},
},
palette: {
diff --git a/frontend/src/UIConstants.js b/frontend/src/UIConstants.ts
similarity index 100%
rename from frontend/src/UIConstants.js
rename to frontend/src/UIConstants.ts
diff --git a/frontend/src/actions/index.js b/frontend/src/actions/index.ts
similarity index 99%
rename from frontend/src/actions/index.js
rename to frontend/src/actions/index.ts
index 8a113f35..f63955e1 100644
--- a/frontend/src/actions/index.js
+++ b/frontend/src/actions/index.ts
@@ -222,7 +222,7 @@ query($agencyId:String!, $routeId:String!,
const dates = computeDates(params.firstDateRange);
return function(dispatch) {
- const variables = {
+ const variables: { [key: string]: any } = {
agencyId: params.agencyId,
routeId: params.routeId,
directionId: params.directionId,
@@ -287,7 +287,7 @@ export function resetTripMetrics() {
};
}
-export function fetchRoutes() {
+export function fetchRoutes(params) {
return function(dispatch, getState) {
const agencyId = Agencies[0].id;
@@ -365,7 +365,7 @@ query($agencyId:String!, $routeId:String!,
}
}`.replace(/\s+/g, ' ');
- const variables = {
+ const variables: { [key: string]: any } = {
agencyId: Agencies[0].id,
routeId: params.routeId,
dates,
diff --git a/frontend/src/components/AppBarLogo.jsx b/frontend/src/components/AppBarLogo.tsx
similarity index 100%
rename from frontend/src/components/AppBarLogo.jsx
rename to frontend/src/components/AppBarLogo.tsx
diff --git a/frontend/src/components/ControlPanel.jsx b/frontend/src/components/ControlPanel.tsx
similarity index 96%
rename from frontend/src/components/ControlPanel.jsx
rename to frontend/src/components/ControlPanel.tsx
index ada6933c..e5ccd39e 100644
--- a/frontend/src/components/ControlPanel.jsx
+++ b/frontend/src/components/ControlPanel.tsx
@@ -1,11 +1,10 @@
/* eslint-disable react/prop-types */
import React, { useState } from 'react';
import { connect } from 'react-redux';
-import { makeStyles } from '@material-ui/core/styles';
-import FormControl from '@material-ui/core/FormControl';
import Navlink from 'redux-first-router-link';
+import { makeStyles, FormControl } from '@material-ui/core';
import BackspaceIcon from '@material-ui/icons/Backspace';
-import { getDownstreamStopIds } from '../helpers/mapGeometry';
+import { getDownstreamStopIds } from 'helpers';
import ReactSelect from './ReactSelect';
import DateTimeRangeControls from './DateTimeRangeControls';
@@ -112,9 +111,9 @@ function ControlPanel(props) {
const hoverStop = selectedRoute ? selectedRoute.stops[stopId] : null;
- mapNode.style.setProperty(
- '--stop-name',
- `"${hoverStop ? hoverStop.title : ''}"`,
+ mapNode.setAttribute(
+ 'style',
+ `--stop-name: "${hoverStop ? hoverStop.title : ''}"`,
);
}
}
@@ -155,7 +154,7 @@ function ControlPanel(props) {
const directionStops = selectedDirection ? selectedDirection.stops : [];
- const labelStyle = { whiteSpace: 'nowrap' };
+ const labelStyle: { [key: string]: any } = { whiteSpace: 'nowrap' };
return (
diff --git a/frontend/src/components/DateRangeControl.jsx b/frontend/src/components/DateRangeControl.tsx
similarity index 94%
rename from frontend/src/components/DateRangeControl.jsx
rename to frontend/src/components/DateRangeControl.tsx
index aa79e227..2bb0c519 100644
--- a/frontend/src/components/DateRangeControl.jsx
+++ b/frontend/src/components/DateRangeControl.tsx
@@ -1,29 +1,31 @@
import React, { useState, useEffect } from 'react';
-import Moment from 'moment';
-import { makeStyles } from '@material-ui/core/styles';
-import Checkbox from '@material-ui/core/Checkbox';
-import Divider from '@material-ui/core/Divider';
-import Grid from '@material-ui/core/Grid';
-import Popover from '@material-ui/core/Popover';
-import Typography from '@material-ui/core/Typography';
-import Button from '@material-ui/core/Button';
import { connect } from 'react-redux';
+import Moment from 'moment';
+import { Theme, makeStyles } from '@material-ui/core/styles';
import ArrowDropDownIcon from '@material-ui/icons/ArrowDropDown';
-
-import FormControl from '@material-ui/core/FormControl';
-import FormGroup from '@material-ui/core/FormGroup';
-import { List, ListItem, Snackbar } from '@material-ui/core';
-import TextField from '@material-ui/core/TextField';
-import FormControlLabel from '@material-ui/core/FormControlLabel';
-import IconButton from '@material-ui/core/IconButton';
import CloseIcon from '@material-ui/icons/Close';
-
+import {
+ List,
+ ListItem,
+ Snackbar,
+ FormGroup,
+ FormControl,
+ TextField,
+ FormControlLabel,
+ IconButton,
+ Checkbox,
+ Divider,
+ Grid,
+ Popover,
+ Typography,
+ Button,
+} from '@material-ui/core';
import {
allFalse,
allTrue,
getDaysOfTheWeekLabel,
renderDateString,
-} from '../helpers/dateTime';
+} from 'helpers';
import {
DATE_RANGES,
@@ -35,7 +37,7 @@ import { initialGraphParams } from '../reducers';
import { fullQueryFromParams } from '../routesMap';
import { updateQuery } from '../actions';
-const useStyles = makeStyles(theme => ({
+const useStyles = makeStyles((theme: Theme) => ({
button: {
textTransform: 'none',
borderRadius: '0px',
@@ -58,9 +60,9 @@ const useStyles = makeStyles(theme => ({
// color: theme.palette.text.secondary,
textAlign: 'left',
},
- column: {
- flexGrow: '1',
- },
+ // column: {
+ // flexGrow: '1',
+ // },
dateTime: {
whiteSpace: 'nowrap',
display: 'flex',
@@ -253,7 +255,7 @@ function DateRangeControl(props) {
const newMoment = Moment(newDate);
const startMoment = Moment(dateRangeParams.startDate);
- const payload = {
+ const payload: { [key: string]: any } = {
date: newDate,
};
@@ -284,7 +286,7 @@ function DateRangeControl(props) {
};
const setDateRange = daysBack => {
- const date = initialGraphParams.date;
+ const date = initialGraphParams.firstDateRange.date;
const startMoment = Moment(date).subtract(daysBack - 1, 'days'); // include end date
updateLocalDateRangeParams({
@@ -391,7 +393,7 @@ function DateRangeControl(props) {
.format('YYYY-MM-DD'),
},
}}
- className={classes.textField}
+ // className={classes.textField}
InputLabelProps={{
shrink: true,
}}
@@ -412,7 +414,7 @@ function DateRangeControl(props) {
max: maxDate,
},
}}
- className={classes.textField}
+ // className={classes.textField}
InputLabelProps={{
shrink: true,
}}
diff --git a/frontend/src/components/DateTimeRangeControls.jsx b/frontend/src/components/DateTimeRangeControls.tsx
similarity index 95%
rename from frontend/src/components/DateTimeRangeControls.jsx
rename to frontend/src/components/DateTimeRangeControls.tsx
index 58d69781..9ad2338f 100644
--- a/frontend/src/components/DateTimeRangeControls.jsx
+++ b/frontend/src/components/DateTimeRangeControls.tsx
@@ -1,8 +1,6 @@
import React from 'react';
import { connect } from 'react-redux';
-import FormControl from '@material-ui/core/FormControl';
-import Tooltip from '@material-ui/core/Tooltip';
-import InputLabel from '@material-ui/core/InputLabel';
+import { FormControl, Tooltip, InputLabel } from '@material-ui/core';
import Navlink from 'redux-first-router-link';
import BackspaceIcon from '@material-ui/icons/Backspace';
import AddCircleIcon from '@material-ui/icons/AddCircle';
diff --git a/frontend/src/components/LoadingIndicator.jsx b/frontend/src/components/LoadingIndicator.tsx
similarity index 84%
rename from frontend/src/components/LoadingIndicator.jsx
rename to frontend/src/components/LoadingIndicator.tsx
index e75fe051..de557ffa 100644
--- a/frontend/src/components/LoadingIndicator.jsx
+++ b/frontend/src/components/LoadingIndicator.tsx
@@ -1,7 +1,6 @@
import React from 'react';
import { connect } from 'react-redux';
-import Box from '@material-ui/core/Box';
-import CircularProgress from '@material-ui/core/CircularProgress';
+import { Box, CircularProgress } from '@material-ui/core';
import { isLoadingRequest } from '../reducers/loadingReducer';
/*
diff --git a/frontend/src/components/MapShield.jsx b/frontend/src/components/MapShield.tsx
similarity index 100%
rename from frontend/src/components/MapShield.jsx
rename to frontend/src/components/MapShield.tsx
diff --git a/frontend/src/components/MapSpider.jsx b/frontend/src/components/MapSpider.tsx
similarity index 96%
rename from frontend/src/components/MapSpider.jsx
rename to frontend/src/components/MapSpider.tsx
index 0e5d2a08..68a140fa 100644
--- a/frontend/src/components/MapSpider.jsx
+++ b/frontend/src/components/MapSpider.tsx
@@ -1,5 +1,5 @@
import React, { Component, createRef, Fragment } from 'react';
-import Button from '@material-ui/core/Button';
+import { Button, Snackbar } from '@material-ui/core';
import GpsIcon from '@material-ui/icons/GpsFixed';
import { connect } from 'react-redux';
import {
@@ -12,22 +12,18 @@ import {
} from 'react-leaflet';
import L from 'leaflet';
import Control from 'react-leaflet-control';
-import { Snackbar } from '@material-ui/core';
import {
getDownstreamStopIds,
getTripPoints,
isInServiceArea,
-} from '../helpers/mapGeometry';
-import {
filterRoutes,
milesBetween,
getRouteColor,
-} from '../helpers/routeCalculations';
+} from 'helpers';
+import MapShield from './MapShield';
import { handleSpiderMapClick } from '../actions';
import { Agencies } from '../config';
-import MapShield from './MapShield';
-
const CLICK_RADIUS_MI = 0.5; // maximum radius for stops near a point
// Displays alert when an invalid location is set
@@ -40,8 +36,24 @@ function ValidLocationAlert(props) {
);
}
-class MapSpider extends Component {
- constructor(props) {
+interface Props {
+ [key: string]: any;
+}
+
+interface State {
+ [key: string]: any;
+}
+
+class MapSpider extends Component
{
+ agency: any;
+
+ routeLayers: any[];
+
+ mapRef: any;
+
+ boundUpdate: any;
+
+ constructor(props: Props) {
super(props);
// for now, only supports 1 agency at a time.
@@ -272,14 +284,16 @@ class MapSpider extends Component {
return true;
}}
onFocus={e => {
- this.onMouseOver(e);
+ e.target.setStyle({ opacity: 1, weight: computedWeight + 4 });
+ return true;
}}
onMouseOut={e => {
e.target.setStyle({ opacity: 0.5, weight: computedWeight });
return true;
}}
onBlur={e => {
- this.onMouseOut(e);
+ e.target.setStyle({ opacity: 0.5, weight: computedWeight });
+ return true;
}}
// when this route segment is clicked, plot only the stops for this route/dir by setting the first stop
onClick={e => {
diff --git a/frontend/src/components/MapStops.jsx b/frontend/src/components/MapStops.tsx
similarity index 98%
rename from frontend/src/components/MapStops.jsx
rename to frontend/src/components/MapStops.tsx
index 91e6c89b..33353e5e 100644
--- a/frontend/src/components/MapStops.jsx
+++ b/frontend/src/components/MapStops.tsx
@@ -1,6 +1,7 @@
import React, { Component } from 'react';
import { connect } from 'react-redux';
import { Map, TileLayer, Marker, Tooltip, Polyline } from 'react-leaflet';
+import ReactDOMServer from 'react-dom/server';
import * as d3 from 'd3';
import L from 'leaflet';
import Control from 'react-leaflet-control';
@@ -8,13 +9,24 @@ import StartStopIcon from '@material-ui/icons/DirectionsTransit';
import EndStopIcon from '@material-ui/icons/Flag';
import { withTheme } from '@material-ui/core/styles';
import { ThemeProvider } from '@material-ui/styles';
-import ReactDOMServer from 'react-dom/server';
+import { getTripPoints, getDistanceInMiles } from 'helpers';
import { handleGraphParams } from '../actions';
-import { getTripPoints, getDistanceInMiles } from '../helpers/mapGeometry';
import { Agencies } from '../config';
-class MapStops extends Component {
- constructor(props) {
+interface Props {
+ [key: string]: any;
+}
+
+interface State {
+ [key: string]: any;
+}
+
+class MapStops extends Component {
+ agency: any;
+
+ boundUpdate: any;
+
+ constructor(props: Props) {
super(props);
this.agency = Agencies[0];
diff --git a/frontend/src/components/MareyChart.jsx b/frontend/src/components/MareyChart.tsx
similarity index 95%
rename from frontend/src/components/MareyChart.jsx
rename to frontend/src/components/MareyChart.tsx
index 36284a92..842b6320 100644
--- a/frontend/src/components/MareyChart.jsx
+++ b/frontend/src/components/MareyChart.tsx
@@ -3,7 +3,7 @@
/* Note: Importing MomentTZ adds new methods to Moment. MomentTZ is not meant to be used directly. */
import React, { Fragment, useState, useEffect } from 'react';
-
+import { connect } from 'react-redux';
import {
XYPlot,
HorizontalGridLines,
@@ -16,20 +16,19 @@ import {
Borders,
} from 'react-vis';
import '../../node_modules/react-vis/dist/style.css';
-
-import { connect } from 'react-redux';
-
import { Radio, FormControl, FormControlLabel } from '@material-ui/core';
-
import Moment from 'moment';
+// eslint-disable-next-line
import MomentTZ from 'moment-timezone/builds/moment-timezone-with-data-10-year-range'; // this augments Moment
-
import * as d3 from 'd3';
+import { metersToMiles } from 'helpers';
import { fetchArrivals } from '../actions';
import { getAgency } from '../config';
import { DWELL_THRESHOLD_SECS } from '../UIConstants';
-import { metersToMiles } from '../helpers/routeCalculations';
+interface Props {
+ [key: string]: any;
+}
/**
* Within state.arrivals.data, the data is organized as follows:
@@ -56,7 +55,7 @@ import { metersToMiles } from '../helpers/routeCalculations';
*
* @param {Object} props
*/
-function MareyChart(props) {
+const MareyChart: React.FC = (props: Props) => {
const INBOUND_AND_OUTBOUND = 'Inbound_and_outbound';
const INBOUND = '1'; // same as directionInfo id
const OUTBOUND = '0'; // same as directionInfo id
@@ -64,10 +63,12 @@ function MareyChart(props) {
const { graphParams, arrivals, arrivalsErr, routes, hidden } = props;
const myFetchArrivals = props.fetchArrivals;
- const [hintValue, setHintValue] = useState();
- const [tripHighlight, setTripHighlight] = useState();
- const [processedArrivals, setProcessedArrivals] = useState(); // where the tripData gets stored
- const [selectedOption, setSelectedOption] = useState(INBOUND_AND_OUTBOUND);
+ const [hintValue, setHintValue] = useState();
+ const [tripHighlight, setTripHighlight] = useState();
+ const [processedArrivals, setProcessedArrivals] = useState(); // where the tripData gets stored
+ const [selectedOption, setSelectedOption] = useState(
+ INBOUND_AND_OUTBOUND,
+ );
const agency = getAgency(graphParams.agencyId);
const timezoneId = agency ? agency.timezoneId : 'UTC';
@@ -137,7 +138,7 @@ function MareyChart(props) {
}
distance = metersToMiles(distance);
- const arrivalMoment = Moment.unix(arrival.t).tz(timezoneId);
+ const arrivalMoment = Moment.unix(arrival.t).utcOffset(timezoneId);
const yValue = (arrival.t - startTime) / 60 / 60 + startHourOfDay; // time of arrival in fractional hours
myTripData.byTripId[tripId].series.push({
@@ -167,7 +168,7 @@ function MareyChart(props) {
// so we can see the vehicle's exit in the data series.
if (arrival.e - arrival.t > DWELL_THRESHOLD_SECS) {
- const exitMoment = Moment.unix(arrival.e).tz(timezoneId);
+ const exitMoment = Moment.unix(arrival.e).utcOffset(timezoneId);
const exitYValue = (arrival.e - startTime) / 60 / 60 + startHourOfDay; // time of arrival in fractional hours
myTripData.byTripId[tripId].series.push({
@@ -201,9 +202,8 @@ function MareyChart(props) {
const stops = myArrivals.stops;
const startTime = myArrivals.start_time;
const startHourOfDay = Moment.unix(startTime)
- .tz(timezoneId)
+ .utcOffset(timezoneId)
.hour();
-
const routeId = myArrivals.route_id;
const route = myRoutes.find(myRoute => myRoute.id === routeId);
@@ -373,7 +373,7 @@ function MareyChart(props) {
control={
@@ -388,7 +388,7 @@ function MareyChart(props) {
control={
@@ -403,7 +403,7 @@ function MareyChart(props) {
control={
@@ -477,7 +477,7 @@ function MareyChart(props) {
) : (
{arrivalsErr || 'Loading...'}
);
-}
+};
const mapStateToProps = state => ({
routes: state.routes.data,
diff --git a/frontend/src/components/OnTimePerformanceByDayChart.jsx b/frontend/src/components/OnTimePerformanceByDayChart.tsx
similarity index 97%
rename from frontend/src/components/OnTimePerformanceByDayChart.jsx
rename to frontend/src/components/OnTimePerformanceByDayChart.tsx
index 92925ad9..c72e342e 100644
--- a/frontend/src/components/OnTimePerformanceByDayChart.jsx
+++ b/frontend/src/components/OnTimePerformanceByDayChart.tsx
@@ -1,7 +1,7 @@
import React from 'react';
import { connect } from 'react-redux';
import Moment from 'moment';
-import { getOnTimePercent } from '../helpers/graphData';
+import { getOnTimePercent } from 'helpers';
import SimpleLineMarkChart from './SimpleLineMarkChart';
import { CHART_COLORS } from '../UIConstants';
diff --git a/frontend/src/components/OnTimePerformanceByTimeChart.jsx b/frontend/src/components/OnTimePerformanceByTimeChart.tsx
similarity index 95%
rename from frontend/src/components/OnTimePerformanceByTimeChart.jsx
rename to frontend/src/components/OnTimePerformanceByTimeChart.tsx
index edb8c9a9..af2ca10b 100644
--- a/frontend/src/components/OnTimePerformanceByTimeChart.jsx
+++ b/frontend/src/components/OnTimePerformanceByTimeChart.tsx
@@ -1,7 +1,10 @@
import React from 'react';
import { connect } from 'react-redux';
-import { getOnTimePercent } from '../helpers/graphData';
-import { renderDateRange, getTimeRangeShortLabel } from '../helpers/dateTime';
+import {
+ getOnTimePercent,
+ renderDateRange,
+ getTimeRangeShortLabel,
+} from 'helpers';
import SimpleVerticalBarChart from './SimpleVerticalBarChart';
import { CHART_COLORS } from '../UIConstants';
diff --git a/frontend/src/components/OnTimePerformanceHistogram.jsx b/frontend/src/components/OnTimePerformanceHistogram.tsx
similarity index 95%
rename from frontend/src/components/OnTimePerformanceHistogram.jsx
rename to frontend/src/components/OnTimePerformanceHistogram.tsx
index 67543c33..cd7dbc09 100644
--- a/frontend/src/components/OnTimePerformanceHistogram.jsx
+++ b/frontend/src/components/OnTimePerformanceHistogram.tsx
@@ -1,7 +1,6 @@
import React from 'react';
import { connect } from 'react-redux';
-import { getHistogramChartData } from '../helpers/graphData';
-import { renderDateRange } from '../helpers/dateTime';
+import { getHistogramChartData, renderDateRange } from 'helpers';
import SimpleVerticalRectChart from './SimpleVerticalRectChart';
import { CHART_COLORS } from '../UIConstants';
diff --git a/frontend/src/components/OnTimePerformanceStats.jsx b/frontend/src/components/OnTimePerformanceStats.tsx
similarity index 100%
rename from frontend/src/components/OnTimePerformanceStats.jsx
rename to frontend/src/components/OnTimePerformanceStats.tsx
diff --git a/frontend/src/components/QuadrantChart.jsx b/frontend/src/components/QuadrantChart.tsx
similarity index 99%
rename from frontend/src/components/QuadrantChart.jsx
rename to frontend/src/components/QuadrantChart.tsx
index f944ae64..42028e70 100644
--- a/frontend/src/components/QuadrantChart.jsx
+++ b/frontend/src/components/QuadrantChart.tsx
@@ -1,5 +1,4 @@
import React from 'react';
-
import { connect } from 'react-redux';
import {
XYPlot,
diff --git a/frontend/src/components/ReactSelect.jsx b/frontend/src/components/ReactSelect.tsx
similarity index 95%
rename from frontend/src/components/ReactSelect.jsx
rename to frontend/src/components/ReactSelect.tsx
index 1c5a2c26..c118bb97 100644
--- a/frontend/src/components/ReactSelect.jsx
+++ b/frontend/src/components/ReactSelect.tsx
@@ -1,11 +1,7 @@
import React, { useState, useEffect, useRef } from 'react';
import Select, { components } from 'react-select';
import { makeStyles, createMuiTheme } from '@material-ui/core/styles';
-import Paper from '@material-ui/core/Paper';
-import Fade from '@material-ui/core/Fade';
-import Grow from '@material-ui/core/Grow';
-import TextField from '@material-ui/core/TextField';
-import MenuItem from '@material-ui/core/MenuItem';
+import { Paper, Fade, Grow, TextField, MenuItem } from '@material-ui/core';
import ArrowDropDownIcon from '@material-ui/icons/ArrowDropDown';
const transitionDuration = 350;
@@ -13,7 +9,7 @@ const scrollHandlerDelay = 30;
const theme = createMuiTheme({
palette: {
background: {
- focus: 'rgba(0, 0, 0, 0.05)',
+ // focus: 'rgba(0, 0, 0, 0.05)',
},
},
});
@@ -21,7 +17,7 @@ const useStyles = makeStyles({
input: {
display: 'flex',
'&:focus': {
- backgroundColor: theme.palette.background.focus,
+ // backgroundColor: theme.palette.background.focus,
},
},
selectInput: {
@@ -94,14 +90,16 @@ function Control(props) {
ref={textRef}
fullWidth
InputProps={{
- inputComponent: 'div',
- inputProps: {
- children,
- ...innerProps,
- className: classes.input,
- tabIndex: 0,
- onKeyUp: handleTextKeyUp(props),
- },
+ inputComponent: () => (
+
+ ),
}}
label={textFieldProps.label}
InputLabelProps={inputLabelProps}
@@ -214,7 +212,7 @@ function Menu(props) {
textFieldDOMRect,
},
} = props;
- const menuStyle = {};
+ const menuStyle: { [key: string]: any } = {};
const timeout = menuTransition.current ? transitionDuration : 0;
const [menuStyleRight, setMenuStyleRight] = useState(0);
const [menuStyleBottom, setMenuStyleBottom] = useState(0);
@@ -350,7 +348,7 @@ function Option(props) {
data: { value },
selectProps: { onItemMouseOver, onItemMouseOut },
} = props;
- const focusedStyle = {};
+ const focusedStyle: { [key: string]: any } = {};
if (isFocused) {
if (onItemMouseOver) {
@@ -401,7 +399,9 @@ function handleMenuClose(
return () => {
allowTransition.current = true;
- document.activeElement.blur();
+ if (document.activeElement instanceof HTMLElement) {
+ document.activeElement.blur();
+ }
setMenuIsOpenTransition(false);
setTimeout(() => setMenuIsOpen(false), transitionDuration);
diff --git a/frontend/src/components/RouteSummary.jsx b/frontend/src/components/RouteSummary.tsx
similarity index 98%
rename from frontend/src/components/RouteSummary.jsx
rename to frontend/src/components/RouteSummary.tsx
index 7904dc78..f1983793 100644
--- a/frontend/src/components/RouteSummary.jsx
+++ b/frontend/src/components/RouteSummary.tsx
@@ -1,10 +1,9 @@
import React from 'react';
import { connect } from 'react-redux';
import { Table, TableBody, TableHead } from '@material-ui/core';
+import { metersToMiles, renderDateRange } from 'helpers';
import SummaryRow from './SummaryRow';
import SummaryHeaderRow from './SummaryHeaderRow';
-import { metersToMiles } from '../helpers/routeCalculations';
-import { renderDateRange } from '../helpers/dateTime';
/*
* Renders the Summary tab on the RouteScreen when a route and optional direction are selected,
diff --git a/frontend/src/components/RouteTable.jsx b/frontend/src/components/RouteTable.tsx
similarity index 94%
rename from frontend/src/components/RouteTable.jsx
rename to frontend/src/components/RouteTable.tsx
index 1cf3282b..e185f66b 100644
--- a/frontend/src/components/RouteTable.jsx
+++ b/frontend/src/components/RouteTable.tsx
@@ -1,19 +1,20 @@
import React, { useState, Fragment } from 'react';
+import { connect } from 'react-redux';
import PropTypes from 'prop-types';
import { lighten, makeStyles, useTheme } from '@material-ui/core/styles';
-import Popover from '@material-ui/core/Popover';
-import Table from '@material-ui/core/Table';
-import TableBody from '@material-ui/core/TableBody';
-import TableCell from '@material-ui/core/TableCell';
-import TableHead from '@material-ui/core/TableHead';
-import TableRow from '@material-ui/core/TableRow';
-import TableSortLabel from '@material-ui/core/TableSortLabel';
-import IconButton from '@material-ui/core/IconButton';
-
+import {
+ Popover,
+ Table,
+ TableBody,
+ TableCell,
+ TableHead,
+ TableRow,
+ TableSortLabel,
+ IconButton,
+} from '@material-ui/core';
import InfoIcon from '@material-ui/icons/InfoOutlined';
-import { connect } from 'react-redux';
import Navlink from 'redux-first-router-link';
-import { filterRoutes } from '../helpers/routeCalculations';
+import { filterRoutes } from 'helpers';
import DateTimeRangeControls from './DateTimeRangeControls';
function getComparisonFunction(order, orderBy) {
@@ -77,7 +78,11 @@ function stableSort(array, sortOrder, orderBy) {
return stabilizedThis.map(el => el[0]);
}
-function EnhancedTableHead(props) {
+interface Props {
+ [key: string]: any;
+}
+
+const EnhancedTableHead: React.FC = (props: Props) => {
const { order, orderBy, onRequestSort, columns } = props;
const createSortHandler = property => event => {
onRequestSort(event, property);
@@ -106,7 +111,7 @@ function EnhancedTableHead(props) {
);
-}
+};
EnhancedTableHead.propTypes = {
onRequestSort: PropTypes.func.isRequired,
@@ -367,7 +372,7 @@ function RouteTable(props) {
order={order}
orderBy={orderBy}
onRequestSort={handleRequestSort}
- rowCount={displayedRouteStats.length}
+ // rowCount={displayedRouteStats.length}
columns={columns}
/>
diff --git a/frontend/src/components/ServiceFrequencyByDayChart.jsx b/frontend/src/components/ServiceFrequencyByDayChart.tsx
similarity index 100%
rename from frontend/src/components/ServiceFrequencyByDayChart.jsx
rename to frontend/src/components/ServiceFrequencyByDayChart.tsx
index 5d84e73e..5be7f403 100644
--- a/frontend/src/components/ServiceFrequencyByDayChart.jsx
+++ b/frontend/src/components/ServiceFrequencyByDayChart.tsx
@@ -1,8 +1,8 @@
import React from 'react';
import { connect } from 'react-redux';
import Moment from 'moment';
-import { CHART_COLORS } from '../UIConstants';
import SimpleLineMarkChart from './SimpleLineMarkChart';
+import { CHART_COLORS } from '../UIConstants';
function getMedianServiceFrequencyByDayChartData(byDayData, property) {
return byDayData
diff --git a/frontend/src/components/ServiceFrequencyByTimeChart.jsx b/frontend/src/components/ServiceFrequencyByTimeChart.tsx
similarity index 97%
rename from frontend/src/components/ServiceFrequencyByTimeChart.jsx
rename to frontend/src/components/ServiceFrequencyByTimeChart.tsx
index 0a8e2451..1b29224e 100644
--- a/frontend/src/components/ServiceFrequencyByTimeChart.jsx
+++ b/frontend/src/components/ServiceFrequencyByTimeChart.tsx
@@ -1,8 +1,8 @@
import React from 'react';
import { connect } from 'react-redux';
-import { CHART_COLORS } from '../UIConstants';
-import { renderDateRange, getTimeRangeShortLabel } from '../helpers/dateTime';
+import { renderDateRange, getTimeRangeShortLabel } from 'helpers';
import SimpleVerticalBarChart from './SimpleVerticalBarChart';
+import { CHART_COLORS } from '../UIConstants';
function getMedianServiceFrequencyByTimeRangeChartData(
timeRangesData,
diff --git a/frontend/src/components/ServiceFrequencyGapHistogram.jsx b/frontend/src/components/ServiceFrequencyGapHistogram.tsx
similarity index 95%
rename from frontend/src/components/ServiceFrequencyGapHistogram.jsx
rename to frontend/src/components/ServiceFrequencyGapHistogram.tsx
index 5352f027..bc57da47 100644
--- a/frontend/src/components/ServiceFrequencyGapHistogram.jsx
+++ b/frontend/src/components/ServiceFrequencyGapHistogram.tsx
@@ -1,9 +1,8 @@
import React from 'react';
import { connect } from 'react-redux';
-import { CHART_COLORS } from '../UIConstants';
-import { renderDateRange } from '../helpers/dateTime';
-import { getHistogramChartData } from '../helpers/graphData';
+import { renderDateRange, getHistogramChartData } from 'helpers';
import SimpleVerticalRectChart from './SimpleVerticalRectChart';
+import { CHART_COLORS } from '../UIConstants';
function getGapHistogramChartData(headwayScheduleDeltas) {
return getHistogramChartData(
diff --git a/frontend/src/components/ServiceFrequencyHistogram.jsx b/frontend/src/components/ServiceFrequencyHistogram.tsx
similarity index 95%
rename from frontend/src/components/ServiceFrequencyHistogram.jsx
rename to frontend/src/components/ServiceFrequencyHistogram.tsx
index 10ccd711..a102db22 100644
--- a/frontend/src/components/ServiceFrequencyHistogram.jsx
+++ b/frontend/src/components/ServiceFrequencyHistogram.tsx
@@ -1,9 +1,8 @@
import React from 'react';
import { connect } from 'react-redux';
-import { CHART_COLORS } from '../UIConstants';
-import { renderDateRange } from '../helpers/dateTime';
-import { getHistogramChartData } from '../helpers/graphData';
+import { renderDateRange, getHistogramChartData } from 'helpers';
import SimpleVerticalRectChart from './SimpleVerticalRectChart';
+import { CHART_COLORS } from '../UIConstants';
function getHeadwaysHistogramChartData(headways) {
return getHistogramChartData(headways.histogram, headways.count);
diff --git a/frontend/src/components/ServiceFrequencyStats.jsx b/frontend/src/components/ServiceFrequencyStats.tsx
similarity index 100%
rename from frontend/src/components/ServiceFrequencyStats.jsx
rename to frontend/src/components/ServiceFrequencyStats.tsx
diff --git a/frontend/src/components/SimpleChart.jsx b/frontend/src/components/SimpleChart.tsx
similarity index 99%
rename from frontend/src/components/SimpleChart.jsx
rename to frontend/src/components/SimpleChart.tsx
index 3af3596d..091cc466 100644
--- a/frontend/src/components/SimpleChart.jsx
+++ b/frontend/src/components/SimpleChart.tsx
@@ -11,7 +11,7 @@ import {
} from 'react-vis';
import DiscreteColorLegend from 'react-vis/dist/legends/discrete-color-legend';
-function getDefaultYDomain(series) {
+function getDefaultYDomain(series, stackBy) {
let yMin = 0;
let yMax = 0;
diff --git a/frontend/src/components/SimpleLineMarkChart.jsx b/frontend/src/components/SimpleLineMarkChart.tsx
similarity index 99%
rename from frontend/src/components/SimpleLineMarkChart.jsx
rename to frontend/src/components/SimpleLineMarkChart.tsx
index ea48fd57..b960b77a 100644
--- a/frontend/src/components/SimpleLineMarkChart.jsx
+++ b/frontend/src/components/SimpleLineMarkChart.tsx
@@ -1,5 +1,4 @@
import React from 'react';
-
import { LineMarkSeries } from 'react-vis';
import SimpleChart from './SimpleChart';
diff --git a/frontend/src/components/SimpleVerticalBarChart.jsx b/frontend/src/components/SimpleVerticalBarChart.tsx
similarity index 99%
rename from frontend/src/components/SimpleVerticalBarChart.jsx
rename to frontend/src/components/SimpleVerticalBarChart.tsx
index 6f22292f..25fe622c 100644
--- a/frontend/src/components/SimpleVerticalBarChart.jsx
+++ b/frontend/src/components/SimpleVerticalBarChart.tsx
@@ -1,5 +1,4 @@
import React from 'react';
-
import { VerticalBarSeries } from 'react-vis';
import SimpleChart from './SimpleChart';
diff --git a/frontend/src/components/SimpleVerticalRectChart.jsx b/frontend/src/components/SimpleVerticalRectChart.tsx
similarity index 99%
rename from frontend/src/components/SimpleVerticalRectChart.jsx
rename to frontend/src/components/SimpleVerticalRectChart.tsx
index 8753f80f..78f478f7 100644
--- a/frontend/src/components/SimpleVerticalRectChart.jsx
+++ b/frontend/src/components/SimpleVerticalRectChart.tsx
@@ -1,5 +1,4 @@
import React from 'react';
-
import { VerticalRectSeries } from 'react-vis';
import SimpleChart from './SimpleChart';
diff --git a/frontend/src/components/SingleDateControl.jsx b/frontend/src/components/SingleDateControl.tsx
similarity index 99%
rename from frontend/src/components/SingleDateControl.jsx
rename to frontend/src/components/SingleDateControl.tsx
index 7ef354fa..1075c439 100644
--- a/frontend/src/components/SingleDateControl.jsx
+++ b/frontend/src/components/SingleDateControl.tsx
@@ -1,8 +1,6 @@
import React from 'react';
import { connect } from 'react-redux';
-
import { TextField } from '@material-ui/core';
-
import { updateQuery } from '../actions';
import { dateQueryFromDateRangeParams } from '../routesMap';
diff --git a/frontend/src/components/SummaryHeaderRow.jsx b/frontend/src/components/SummaryHeaderRow.tsx
similarity index 99%
rename from frontend/src/components/SummaryHeaderRow.jsx
rename to frontend/src/components/SummaryHeaderRow.tsx
index a69c66b1..54e45aa9 100644
--- a/frontend/src/components/SummaryHeaderRow.jsx
+++ b/frontend/src/components/SummaryHeaderRow.tsx
@@ -1,5 +1,4 @@
import React from 'react';
-
import { TableCell, TableRow } from '@material-ui/core';
/*
diff --git a/frontend/src/components/SummaryRow.jsx b/frontend/src/components/SummaryRow.tsx
similarity index 97%
rename from frontend/src/components/SummaryRow.jsx
rename to frontend/src/components/SummaryRow.tsx
index 9c27e0db..f06a4d13 100644
--- a/frontend/src/components/SummaryRow.jsx
+++ b/frontend/src/components/SummaryRow.tsx
@@ -1,10 +1,6 @@
import React, { useState } from 'react';
-
-import { TableCell, TableRow } from '@material-ui/core';
-
+import { TableCell, TableRow, IconButton, Popover } from '@material-ui/core';
import { makeStyles } from '@material-ui/core/styles';
-import IconButton from '@material-ui/core/IconButton';
-import Popover from '@material-ui/core/Popover';
import InfoIcon from '@material-ui/icons/InfoOutlined';
/*
diff --git a/frontend/src/components/SummaryStats.jsx b/frontend/src/components/SummaryStats.tsx
similarity index 99%
rename from frontend/src/components/SummaryStats.jsx
rename to frontend/src/components/SummaryStats.tsx
index a2cf9fe6..546ffa63 100644
--- a/frontend/src/components/SummaryStats.jsx
+++ b/frontend/src/components/SummaryStats.tsx
@@ -1,7 +1,5 @@
import React from 'react';
-
import { connect } from 'react-redux';
-
import RouteSummary from './RouteSummary';
import TripSummary from './TripSummary';
diff --git a/frontend/src/components/TimeRangeControl.jsx b/frontend/src/components/TimeRangeControl.tsx
similarity index 96%
rename from frontend/src/components/TimeRangeControl.jsx
rename to frontend/src/components/TimeRangeControl.tsx
index a0cf1620..8303af72 100644
--- a/frontend/src/components/TimeRangeControl.jsx
+++ b/frontend/src/components/TimeRangeControl.tsx
@@ -1,9 +1,6 @@
import React from 'react';
import { connect } from 'react-redux';
-
-import MenuItem from '@material-ui/core/MenuItem';
-import Select from '@material-ui/core/Select';
-
+import { MenuItem, Select } from '@material-ui/core';
import { TIME_RANGES, TIME_RANGE_ALL_DAY } from '../UIConstants';
import { dateQueryFromDateRangeParams } from '../routesMap';
import { updateQuery } from '../actions';
diff --git a/frontend/src/components/TravelTimeChart.jsx b/frontend/src/components/TravelTimeChart.tsx
similarity index 98%
rename from frontend/src/components/TravelTimeChart.jsx
rename to frontend/src/components/TravelTimeChart.tsx
index 0dee36e6..67428dd3 100644
--- a/frontend/src/components/TravelTimeChart.jsx
+++ b/frontend/src/components/TravelTimeChart.tsx
@@ -1,5 +1,5 @@
import React, { useState } from 'react';
-
+import { connect } from 'react-redux';
import {
XYPlot,
HorizontalGridLines,
@@ -13,11 +13,8 @@ import {
} from 'react-vis';
import DiscreteColorLegend from 'react-vis/dist/legends/discrete-color-legend';
import '../../node_modules/react-vis/dist/style.css';
-
-import { connect } from 'react-redux';
-
-import Typography from '@material-ui/core/Typography';
-import { metersToMiles } from '../helpers/routeCalculations';
+import { Typography } from '@material-ui/core';
+import { metersToMiles } from 'helpers';
/**
* Returns an array of {x: stop index, y: time} objects for
diff --git a/frontend/src/components/TravelTimesHistogram.jsx b/frontend/src/components/TravelTimesHistogram.tsx
similarity index 95%
rename from frontend/src/components/TravelTimesHistogram.jsx
rename to frontend/src/components/TravelTimesHistogram.tsx
index e2c13522..164c1eaf 100644
--- a/frontend/src/components/TravelTimesHistogram.jsx
+++ b/frontend/src/components/TravelTimesHistogram.tsx
@@ -1,9 +1,8 @@
import React from 'react';
import { connect } from 'react-redux';
-import { CHART_COLORS } from '../UIConstants';
-import { renderDateRange } from '../helpers/dateTime';
-import { getHistogramChartData } from '../helpers/graphData';
+import { renderDateRange, getHistogramChartData } from 'helpers';
import SimpleVerticalRectChart from './SimpleVerticalRectChart';
+import { CHART_COLORS } from '../UIConstants';
function getTravelTimesHistogramChartData(tripTimes) {
return getHistogramChartData(tripTimes.histogram, tripTimes.count);
diff --git a/frontend/src/components/TripSummary.jsx b/frontend/src/components/TripSummary.tsx
similarity index 98%
rename from frontend/src/components/TripSummary.jsx
rename to frontend/src/components/TripSummary.tsx
index fcbb8116..4fef700c 100644
--- a/frontend/src/components/TripSummary.jsx
+++ b/frontend/src/components/TripSummary.tsx
@@ -1,8 +1,7 @@
import React from 'react';
import { connect } from 'react-redux';
import { Table, TableBody, TableHead } from '@material-ui/core';
-import { getDistanceInMiles, getTripStops } from '../helpers/mapGeometry';
-import { renderDateRange } from '../helpers/dateTime';
+import { getDistanceInMiles, getTripStops, renderDateRange } from 'helpers';
import SummaryRow from './SummaryRow';
import SummaryHeaderRow from './SummaryHeaderRow';
diff --git a/frontend/src/components/TripTimesByDayChart.jsx b/frontend/src/components/TripTimesByDayChart.tsx
similarity index 97%
rename from frontend/src/components/TripTimesByDayChart.jsx
rename to frontend/src/components/TripTimesByDayChart.tsx
index 2d7ed4a7..6df5639a 100644
--- a/frontend/src/components/TripTimesByDayChart.jsx
+++ b/frontend/src/components/TripTimesByDayChart.tsx
@@ -1,15 +1,15 @@
import React, { useState } from 'react';
+import { connect } from 'react-redux';
import {
FormControl,
FormControlLabel,
Radio,
Typography,
} from '@material-ui/core';
-import { connect } from 'react-redux';
import Moment from 'moment';
-import { CHART_COLORS, PLANNING_PERCENTILE } from '../UIConstants';
-import { getPercentileValue } from '../helpers/graphData';
+import { getPercentileValue } from 'helpers';
import SimpleVerticalBarChart from './SimpleVerticalBarChart';
+import { CHART_COLORS, PLANNING_PERCENTILE } from '../UIConstants';
import '../../node_modules/react-vis/dist/style.css';
const AVERAGE_TIME = 'average_time';
@@ -99,7 +99,7 @@ function TripTimesByDayChart(props) {
control={
diff --git a/frontend/src/reducers/index.js b/frontend/src/reducers/index.ts
similarity index 100%
rename from frontend/src/reducers/index.js
rename to frontend/src/reducers/index.ts
diff --git a/frontend/src/reducers/loadingReducer.js b/frontend/src/reducers/loadingReducer.ts
similarity index 100%
rename from frontend/src/reducers/loadingReducer.js
rename to frontend/src/reducers/loadingReducer.ts
diff --git a/frontend/src/reducers/page.js b/frontend/src/reducers/page.ts
similarity index 91%
rename from frontend/src/reducers/page.js
rename to frontend/src/reducers/page.ts
index 0bceaa2d..ea120e08 100644
--- a/frontend/src/reducers/page.js
+++ b/frontend/src/reducers/page.ts
@@ -26,5 +26,5 @@ export function typeForPage(page) {
return currentType;
}
-export default (state = 'DASHBOARD', action = {}) =>
+export default (state = 'DASHBOARD', action: { type: string }) =>
components[action.type] || state;
diff --git a/frontend/src/reducers/titleReducer.js b/frontend/src/reducers/titleReducer.ts
similarity index 88%
rename from frontend/src/reducers/titleReducer.js
rename to frontend/src/reducers/titleReducer.ts
index 66236204..afcff206 100644
--- a/frontend/src/reducers/titleReducer.js
+++ b/frontend/src/reducers/titleReducer.ts
@@ -2,7 +2,7 @@ import { APP_NAME } from '../UIConstants';
const DEFAULT = APP_NAME;
-export default (state = DEFAULT, action = {}) => {
+export default (state = DEFAULT, action: { type: string }) => {
switch (action.type) {
case 'DASHBOARD':
return `Dashboard | ${DEFAULT}`;
diff --git a/frontend/src/routesMap.js b/frontend/src/routesMap.ts
similarity index 93%
rename from frontend/src/routesMap.js
rename to frontend/src/routesMap.ts
index 8bc7fdd4..f5fddcb8 100644
--- a/frontend/src/routesMap.js
+++ b/frontend/src/routesMap.ts
@@ -7,6 +7,13 @@ export const START_DATE = 'startDate';
export const START_TIME = 'startTime';
export const END_TIME = 'endTime';
export const DAYS_OF_THE_WEEK = 'daysOfTheWeek';
+export interface DateRangeInterface {
+ startTime: any;
+ endTime: any;
+ date: string;
+ startDate: string;
+ daysOfTheWeek: any;
+}
/**
* Gets the string values from a date range object within a query string.
@@ -14,7 +21,7 @@ export const DAYS_OF_THE_WEEK = 'daysOfTheWeek';
* @param dateRangeQuery A query subobject representing a date range.
* @returns A graphParams subobject representing a date range.
*/
-function processDateRangeQuery(dateRangeQuery) {
+function processDateRangeQuery(dateRangeQuery): DateRangeInterface {
const initialDateRangeParams = initialGraphParams.firstDateRange;
if (!dateRangeQuery) {
@@ -25,11 +32,12 @@ function processDateRangeQuery(dateRangeQuery) {
// If the values are missing, we must use the defaults, in case the user is changing
// from a nondefault to default value.
- const newDateRangeParams = {
+ const newDateRangeParams: DateRangeInterface = {
date: date || initialDateRangeParams.date,
startDate: startDate || initialDateRangeParams.startDate,
startTime: startTime || null,
endTime: endTime || null,
+ daysOfTheWeek: null,
};
if (daysOfTheWeek) {
@@ -97,7 +105,7 @@ function processQuery(getState) {
* @returns The query subobject.
*/
export function dateQueryFromDateRangeParams(params) {
- const dateQuery = {};
+ const dateQuery: { [key: string]: any } = {};
const initialDateRangeParams = initialGraphParams.firstDateRange;
if (!params) {
@@ -132,7 +140,7 @@ export function dateQueryFromDateRangeParams(params) {
* @returns The query object to dispatch.
*/
export function fullQueryFromParams(params) {
- const query = {};
+ const query: { [key: string]: any } = {};
query.firstDateRange = dateQueryFromDateRangeParams(params.firstDateRange);
query.secondDateRange = params.secondDateRange || undefined;
return query;
diff --git a/frontend/src/screens/About.jsx b/frontend/src/screens/About.tsx
similarity index 100%
rename from frontend/src/screens/About.jsx
rename to frontend/src/screens/About.tsx
diff --git a/frontend/src/screens/Dashboard.jsx b/frontend/src/screens/Dashboard.tsx
similarity index 90%
rename from frontend/src/screens/Dashboard.jsx
rename to frontend/src/screens/Dashboard.tsx
index 7b402fd7..d8e13853 100644
--- a/frontend/src/screens/Dashboard.jsx
+++ b/frontend/src/screens/Dashboard.tsx
@@ -1,10 +1,7 @@
import React, { useEffect } from 'react';
-import Grid from '@material-ui/core/Grid';
-
import { connect } from 'react-redux';
-import MapSpider from '../components/MapSpider';
-import RouteTable from '../components/RouteTable';
-
+import { Grid } from '@material-ui/core';
+import { MapSpider, RouteTable } from 'components';
import { fetchRoutes, handleGraphParams } from '../actions';
function Dashboard(props) {
diff --git a/frontend/src/screens/DataDiagnostic.jsx b/frontend/src/screens/DataDiagnostic.tsx
similarity index 94%
rename from frontend/src/screens/DataDiagnostic.jsx
rename to frontend/src/screens/DataDiagnostic.tsx
index 5ece560e..015e73e3 100644
--- a/frontend/src/screens/DataDiagnostic.jsx
+++ b/frontend/src/screens/DataDiagnostic.tsx
@@ -1,9 +1,6 @@
import React, { useEffect } from 'react';
-
import { connect } from 'react-redux';
-
-import QuadrantChart from '../components/QuadrantChart';
-
+import { QuadrantChart } from 'components';
import { fetchRoutes, handleGraphParams } from '../actions';
/**
diff --git a/frontend/src/screens/Home.jsx b/frontend/src/screens/Home.tsx
similarity index 99%
rename from frontend/src/screens/Home.jsx
rename to frontend/src/screens/Home.tsx
index 84f50fbe..08204c10 100644
--- a/frontend/src/screens/Home.jsx
+++ b/frontend/src/screens/Home.tsx
@@ -1,5 +1,4 @@
import React from 'react';
-
import Navlink from 'redux-first-router-link';
import { connect } from 'react-redux';
diff --git a/frontend/src/screens/Isochrone.jsx b/frontend/src/screens/Isochrone.tsx
similarity index 95%
rename from frontend/src/screens/Isochrone.jsx
rename to frontend/src/screens/Isochrone.tsx
index 1a907bfc..f1e5c6ab 100644
--- a/frontend/src/screens/Isochrone.jsx
+++ b/frontend/src/screens/Isochrone.tsx
@@ -15,18 +15,19 @@ import { connect } from 'react-redux';
import { Map, TileLayer } from 'react-leaflet';
import L, { DomEvent } from 'leaflet';
import Control from 'react-leaflet-control';
-import Grid from '@material-ui/core/Grid';
-import List from '@material-ui/core/List';
-import ListItem from '@material-ui/core/ListItem';
-import FormControlLabel from '@material-ui/core/FormControlLabel';
-import Checkbox from '@material-ui/core/Checkbox';
-import Select from '@material-ui/core/Select';
-import MenuItem from '@material-ui/core/MenuItem';
-import Button from '@material-ui/core/Button';
-import FormControl from '@material-ui/core/FormControl';
-import InputLabel from '@material-ui/core/InputLabel';
-import SingleDateControl from '../components/SingleDateControl';
-import TimeRangeControl from '../components/TimeRangeControl';
+import {
+ Grid,
+ List,
+ ListItem,
+ FormControlLabel,
+ Checkbox,
+ Select,
+ MenuItem,
+ Button,
+ FormControl,
+ InputLabel,
+} from '@material-ui/core';
+import { SingleDateControl, TimeRangeControl } from 'components';
import { fetchRoutes } from '../actions';
import {
@@ -78,8 +79,40 @@ function getDirectionInfo(directionId, routeInfo) {
return routeInfo.directions.find(dirInfo => dirInfo.id === directionId);
}
-class Isochrone extends React.Component {
- constructor(props) {
+interface Props {
+ [key: string]: any;
+}
+
+interface State {
+ [key: string]: any;
+}
+
+class Isochrone extends React.Component {
+ agency: any;
+
+ agencyId: any;
+
+ initialZoom: any;
+
+ initialCenter: any;
+
+ isochroneWorker: Worker;
+
+ layers: any[];
+
+ isochroneLayers: any[];
+
+ tripLayers: any[];
+
+ routeLayers: any[];
+
+ mapRef: React.RefObject;
+
+ container: any;
+
+ boundUpdate: any;
+
+ constructor(props: Props) {
super(props);
// for now, only supports 1 agency at a time.
@@ -272,13 +305,13 @@ class Isochrone extends React.Component {
latlng = this.agency.defaultIsochroneCenter;
}
if (latlng) {
- this.computeIsochrones(latlng, null);
+ this.computeIsochrones(latlng);
}
},
err => {
const defaultLatLng = this.agency.defaultIsochroneCenter;
if (defaultLatLng) {
- this.computeIsochrones(this.agency.defaultIsochroneCenter, null);
+ this.computeIsochrones(this.agency.defaultIsochroneCenter);
}
},
);
@@ -289,7 +322,7 @@ class Isochrone extends React.Component {
return;
}
this.resetMap();
- this.computeIsochrones(event.latlng, null);
+ this.computeIsochrones(event.latlng);
}
addReachableLocationsLayer(data) {
@@ -472,7 +505,7 @@ class Isochrone extends React.Component {
}
}
- computeIsochrones(latLng, endLatLng) {
+ computeIsochrones(latLng, endLatLng = '') {
if (!isInServiceArea(this.agencyId, latLng)) {
return;
}
@@ -482,16 +515,10 @@ class Isochrone extends React.Component {
const endTimeStr = this.props.endTime;
const timeStr =
startTimeStr && endTimeStr ? `${startTimeStr}-${endTimeStr}` : '';
-
const { maxTripMin, enabledRoutes } = this.state;
-
- const enabledRoutesArr = [];
-
- this.props.routes.forEach(route => {
- if (enabledRoutes[route.id] !== false) {
- enabledRoutesArr.push(route.id);
- }
- });
+ const enabledRoutesArr = this.props.routes
+ .filter(route => enabledRoutes[route.id] !== false)
+ .map(route => route.id);
const computeId = [
latLng.lat,
@@ -626,7 +653,7 @@ class Isochrone extends React.Component {
const { latLng, endLatLng } = this.state;
if (latLng) {
this.resetMap();
- this.computeIsochrones(latLng, endLatLng);
+ this.computeIsochrones(latLng);
}
}
@@ -647,7 +674,7 @@ class Isochrone extends React.Component {
this.layers = [];
this.isochroneLayers = [];
- this.clearTripLayers();
+ this.clearTripLayers(true);
}
clearTripLayers(clearTripInfo) {
@@ -757,6 +784,7 @@ class Isochrone extends React.Component {
opacity={0.6}
/>
{/* see http://maps.stamen.com for details */}
+ // @ts-ignore
diff --git a/frontend/src/screens/NotFound.jsx b/frontend/src/screens/NotFound.tsx
similarity index 100%
rename from frontend/src/screens/NotFound.jsx
rename to frontend/src/screens/NotFound.tsx
diff --git a/frontend/src/screens/RouteScreen.jsx b/frontend/src/screens/RouteScreen.tsx
similarity index 90%
rename from frontend/src/screens/RouteScreen.jsx
rename to frontend/src/screens/RouteScreen.tsx
index a9b3cd29..8408e8fd 100644
--- a/frontend/src/screens/RouteScreen.jsx
+++ b/frontend/src/screens/RouteScreen.tsx
@@ -1,15 +1,16 @@
import React, { useEffect } from 'react';
import { connect } from 'react-redux';
-
import { makeStyles } from '@material-ui/core/styles';
import { AppBar, Tab, Tabs, Box, Paper, Grid } from '@material-ui/core';
-import MapStops from '../components/MapStops';
-import ControlPanel from '../components/ControlPanel';
-import SummaryStats from '../components/SummaryStats';
-import TripTimesStats from '../components/TripTimesStats';
-import ServiceFrequencyStats from '../components/ServiceFrequencyStats';
-import OnTimePerformanceStats from '../components/OnTimePerformanceStats';
-import MareyChart from '../components/MareyChart';
+import {
+ MapStops,
+ ControlPanel,
+ SummaryStats,
+ TripTimesStats,
+ ServiceFrequencyStats,
+ OnTimePerformanceStats,
+ MareyChart,
+} from 'components';
import { fetchRoutes } from '../actions';
const useStyles = makeStyles(theme => ({
diff --git a/frontend/src/screens/index.ts b/frontend/src/screens/index.ts
new file mode 100644
index 00000000..ddb7f302
--- /dev/null
+++ b/frontend/src/screens/index.ts
@@ -0,0 +1,7 @@
+export { default as About } from './About';
+export { default as Dashboard } from './Dashboard';
+export { default as DataDiagnostic } from './DataDiagnostic';
+export { default as Home } from './Home';
+export { default as Isochrone } from './Isochrone';
+export { default as NotFound } from './NotFound';
+export { default as RouteScreen } from './RouteScreen';
diff --git a/frontend/src/serviceWorker.js b/frontend/src/serviceWorker.ts
old mode 100755
new mode 100644
similarity index 99%
rename from frontend/src/serviceWorker.js
rename to frontend/src/serviceWorker.ts
index 17dabb44..8ba0b9ca
--- a/frontend/src/serviceWorker.js
+++ b/frontend/src/serviceWorker.ts
@@ -1,3 +1,5 @@
+export default {};
+
// // This optional code is used to register a service worker.
// // register() is not called by default.
//
diff --git a/frontend/src/store.js b/frontend/src/store.ts
similarity index 71%
rename from frontend/src/store.js
rename to frontend/src/store.ts
index 81f9bbb0..45216c58 100644
--- a/frontend/src/store.js
+++ b/frontend/src/store.ts
@@ -1,27 +1,28 @@
+/* eslint-disable import/no-unresolved */
+/* eslint-disable import/extensions */
/* eslint-disable no-underscore-dangle */
import { createStore, applyMiddleware, combineReducers, compose } from 'redux';
import { connectRoutes } from 'redux-first-router';
import qs from 'qs';
import thunk from 'redux-thunk';
-
import routesMap from './routesMap';
import * as reducers from './reducers';
// import page from './reducers/page';
import * as actionCreators from './actions';
-const composeEnhancers = window.__REDUX_DEVTOOLS_EXTENSION_COMPOSE__
- ? window.__REDUX_DEVTOOLS_EXTENSION_COMPOSE__({ actionCreators })
+const composeEnhancers = (window as any).__REDUX_DEVTOOLS_EXTENSION_COMPOSE__
+ ? (window as any).__REDUX_DEVTOOLS_EXTENSION_COMPOSE__({ actionCreators })
: compose;
-export default function configureStore(preloadedState) {
+export default function configureStore(preloadedState = {}) {
const { reducer, middleware, enhancer } = connectRoutes(routesMap, {
querySerializer: qs,
});
+ // @ts-ignore
const rootReducer = combineReducers({ ...reducers, location: reducer });
const middlewares = applyMiddleware(thunk, middleware);
const enhancers = composeEnhancers(enhancer, middlewares);
-
const store = createStore(rootReducer, preloadedState, enhancers);
return { store };
diff --git a/frontend/tsconfig.json b/frontend/tsconfig.json
new file mode 100644
index 00000000..e110724c
--- /dev/null
+++ b/frontend/tsconfig.json
@@ -0,0 +1,38 @@
+{
+ "extends": "./tsconfig.paths.json",
+ "compilerOptions": {
+ "jsx": "react",
+ "outDir": "build",
+ "baseUrl": "./src/",
+ "allowJs": true,
+ "target": "es6",
+ "module": "esnext",
+ "moduleResolution": "node",
+ "lib": [
+ "esnext",
+ "dom"
+ ],
+ "allowSyntheticDefaultImports": true,
+ "esModuleInterop": true,
+ "alwaysStrict": false,
+ "strictFunctionTypes": false,
+ "strictPropertyInitialization": false,
+ "skipLibCheck": true,
+ "strict": true,
+ "forceConsistentCasingInFileNames": true,
+ "resolveJsonModule": true,
+ "isolatedModules": true,
+ "noEmit": true,
+ // remove in the future
+ "noImplicitAny": false,
+ "noImplicitThis": false,
+ "strictNullChecks": false,
+ },
+ "include": [
+ "src/**/*"
+ ],
+ "exclude": [
+ "node_modules",
+ "build"
+ ]
+}
diff --git a/frontend/tsconfig.paths.json b/frontend/tsconfig.paths.json
new file mode 100644
index 00000000..15681e47
--- /dev/null
+++ b/frontend/tsconfig.paths.json
@@ -0,0 +1,8 @@
+
+{
+ "paths": {
+ "@components": ["components"],
+ "@helpers": ["helpers"],
+ "@screens": ["screens"],
+ }
+}
\ No newline at end of file
diff --git a/frontend/webpack.config.js b/frontend/webpack.config.js
deleted file mode 100644
index 15953c96..00000000
--- a/frontend/webpack.config.js
+++ /dev/null
@@ -1,3 +0,0 @@
-module.exports = {
- resolve: {extensions: ['.js','.jsx']}
-}
\ No newline at end of file
diff --git a/frontend/webpack.config.ts b/frontend/webpack.config.ts
new file mode 100644
index 00000000..549c83fd
--- /dev/null
+++ b/frontend/webpack.config.ts
@@ -0,0 +1,21 @@
+import { TsconfigPathsPlugin } from "tsconfig-paths-webpack-plugin";
+
+module.exports = {
+ entry: './src/index.tsx',
+ output: {
+ filename: './build/bundle.js',
+ },
+ devtool: 'source-map',
+ resolve: {
+ extensions: ['', '.ts', '.tsx', '.js', '.jsx'],
+ plugins: [new TsconfigPathsPlugin()]
+ },
+ module: {
+ rules: [
+ // All files with a '.ts' or '.tsx' extension will be handled by 'awesome-typescript-loader'.
+ { test: /\.tsx?$/, loader: 'awesome-typescript-loader' },
+ // All output '.jsx' or '.js' files will have any sourcemaps re-processed by 'source-map-loader'.
+ { test: /\.jsx?$/, loader: 'source-map-loader' },
+ ],
+ },
+};
diff --git a/package-lock.json b/package-lock.json
index 958df32b..92623ebe 100644
--- a/package-lock.json
+++ b/package-lock.json
@@ -1,5 +1,45 @@
{
"name": "opentransit",
"version": "0.1.0",
- "lockfileVersion": 1
+ "lockfileVersion": 1,
+ "requires": true,
+ "dependencies": {
+ "@types/node": {
+ "version": "13.13.0",
+ "resolved": "https://registry.npmjs.org/@types/node/-/node-13.13.0.tgz",
+ "integrity": "sha512-WE4IOAC6r/yBZss1oQGM5zs2D7RuKR6Q+w+X2SouPofnWn+LbCqClRyhO3ZE7Ix8nmFgo/oVuuE01cJT2XB13A=="
+ },
+ "@types/prop-types": {
+ "version": "15.7.3",
+ "resolved": "https://registry.npmjs.org/@types/prop-types/-/prop-types-15.7.3.tgz",
+ "integrity": "sha512-KfRL3PuHmqQLOG+2tGpRO26Ctg+Cq1E01D2DMriKEATHgWLfeNDmq9e29Q9WIky0dQ3NPkd1mzYH8Lm936Z9qw=="
+ },
+ "@types/react": {
+ "version": "16.9.34",
+ "resolved": "https://registry.npmjs.org/@types/react/-/react-16.9.34.tgz",
+ "integrity": "sha512-8AJlYMOfPe1KGLKyHpflCg5z46n0b5DbRfqDksxBLBTUpB75ypDBAO9eCUcjNwE6LCUslwTz00yyG/X9gaVtow==",
+ "requires": {
+ "@types/prop-types": "*",
+ "csstype": "^2.2.0"
+ }
+ },
+ "@types/react-dom": {
+ "version": "16.9.6",
+ "resolved": "https://registry.npmjs.org/@types/react-dom/-/react-dom-16.9.6.tgz",
+ "integrity": "sha512-S6ihtlPMDotrlCJE9ST1fRmYrQNNwfgL61UB4I1W7M6kPulUKx9fXAleW5zpdIjUQ4fTaaog8uERezjsGUj9HQ==",
+ "requires": {
+ "@types/react": "*"
+ }
+ },
+ "csstype": {
+ "version": "2.6.10",
+ "resolved": "https://registry.npmjs.org/csstype/-/csstype-2.6.10.tgz",
+ "integrity": "sha512-D34BqZU4cIlMCY93rZHbrq9pjTAQJ3U8S8rfBqjwHxkGPThWFjzZDQpgMJY0QViLxth6ZKYiwFBo14RdN44U/w=="
+ },
+ "typescript": {
+ "version": "3.8.3",
+ "resolved": "https://registry.npmjs.org/typescript/-/typescript-3.8.3.tgz",
+ "integrity": "sha512-MYlEfn5VrLNsgudQTVJeNaQFUAI7DkhnOjdpAp4T+ku1TfQClewlbSuTVHiA+8skNBgaf02TL/kLOvig4y3G8w=="
+ }
+ }
}