From b357ffe282d4801400059447391f264b2400ad3f Mon Sep 17 00:00:00 2001
From: Hans Christian Reinl
Date: Tue, 5 May 2020 20:52:48 +0200
Subject: [PATCH] Move to typescript
---
.babelrc | 8 -
.editorconfig | 6 +-
.eslintrc.js | 49 ++
.prettierrc.js | 7 +
package.json | 36 +-
src/AddButton/AddButton.jsx | 34 --
src/AddButton/AddButton.test.jsx | 10 -
src/AddButton/AddButton.test.tsx | 10 +
src/AddButton/AddButton.tsx | 30 ++
src/AddButton/index.jsx | 3 -
src/AddButton/index.tsx | 3 +
src/AppContainer/AppContainer.jsx | 54 --
src/AppContainer/AppContainer.test.jsx | 16 -
src/AppContainer/AppContainer.test.tsx | 12 +
src/AppContainer/AppContainer.tsx | 56 +++
src/AppContainer/index.js | 3 -
src/AppContainer/index.ts | 3 +
src/AppContainer/withRoot.jsx | 44 --
src/BackButton/BackButton.jsx | 37 --
src/BackButton/BackButton.test.jsx | 22 -
src/BackButton/BackButton.test.tsx | 30 ++
src/BackButton/BackButton.tsx | 29 ++
src/BackButton/BackButtonBranch.jsx | 31 --
src/BackButton/BackButtonBranch.tsx | 24 +
src/BackButton/index.js | 3 -
src/BackButton/index.ts | 3 +
src/Base/Base.jsx | 112 -----
src/Base/Base.test.jsx | 151 ------
src/Base/Base.test.tsx | 134 +++++
src/Base/Base.tsx | 92 ++++
src/Base/{BaseBranch.jsx => BaseBranch.tsx} | 111 +++--
src/Base/index.js | 3 -
src/Base/index.ts | 3 +
src/Confirm/Confirm.jsx | 80 ---
src/Confirm/Confirm.test.jsx | 79 ---
src/Confirm/Confirm.test.tsx | 70 +++
src/Confirm/Confirm.tsx | 67 +++
.../{ConfirmBranch.jsx => ConfirmBranch.tsx} | 50 +-
src/Confirm/index.js | 3 -
src/Confirm/index.ts | 3 +
src/CookieInfo/Cookie.js | 34 --
src/CookieInfo/Cookie.test.js | 31 --
src/CookieInfo/Cookie.test.ts | 31 ++
src/CookieInfo/Cookie.tsx | 29 ++
src/CookieInfo/CookieInfo.jsx | 100 ----
src/CookieInfo/CookieInfo.test.jsx | 34 --
src/CookieInfo/CookieInfo.test.tsx | 30 ++
src/CookieInfo/CookieInfo.tsx | 77 +++
src/CookieInfo/index.js | 3 -
src/CookieInfo/index.ts | 3 +
src/Dashboard/Dashboard.jsx | 34 --
src/Dashboard/Dashboard.test.jsx | 27 -
src/Dashboard/Dashboard.test.tsx | 31 ++
src/Dashboard/Dashboard.tsx | 24 +
src/Dashboard/DashboardBranch.jsx | 43 --
src/Dashboard/DashboardBranch.tsx | 56 +++
src/Dashboard/DashboardCard.test.jsx | 29 --
src/Dashboard/DashboardCard.test.tsx | 25 +
.../{DashboardCard.jsx => DashboardCard.tsx} | 64 ++-
src/Dashboard/DashboardGroup.jsx | 57 ---
src/Dashboard/DashboardGroup.test.jsx | 17 -
src/Dashboard/DashboardGroup.test.tsx | 21 +
src/Dashboard/DashboardGroup.tsx | 48 ++
src/Dashboard/{index.js => index.ts} | 0
src/Drawer/Drawer.test.jsx | 17 -
src/Drawer/Drawer.test.tsx | 14 +
src/Drawer/{Drawer.jsx => Drawer.tsx} | 61 ++-
src/Drawer/index.jsx | 3 -
src/Drawer/index.tsx | 3 +
src/Form/Form.jsx | 169 -------
src/Form/Form.test.jsx | 52 --
src/Form/Form.test.tsx | 52 ++
src/Form/Form.tsx | 154 ++++++
src/Form/FormBranch.jsx | 150 ------
src/Form/FormBranch.tsx | 148 ++++++
src/Form/FormField.jsx | 222 ---------
src/Form/FormField.test.jsx | 249 ----------
src/Form/FormField.test.tsx | 242 +++++++++
src/Form/FormField.tsx | 197 ++++++++
src/Form/FormFieldBranch.jsx | 174 -------
src/Form/FormFieldBranch.tsx | 154 ++++++
src/Form/FormFieldDate.jsx | 96 ----
src/Form/FormFieldDate.tsx | 83 ++++
src/Form/FormFieldHidden.jsx | 29 --
src/Form/FormFieldHidden.tsx | 16 +
src/Form/FormFieldInput.jsx | 135 -----
src/Form/FormFieldInput.test.jsx | 23 -
src/Form/FormFieldInput.test.tsx | 16 +
src/Form/FormFieldInput.tsx | 113 +++++
src/Form/FormFieldList.jsx | 148 ------
src/Form/FormFieldList.tsx | 152 ++++++
src/Form/FormFieldListBranch.jsx | 153 ------
src/Form/FormFieldListBranch.tsx | 139 ++++++
src/Form/FormFieldSwitch.jsx | 87 ----
src/Form/FormFieldSwitch.test.jsx | 42 --
src/Form/FormFieldSwitch.test.tsx | 38 ++
src/Form/FormFieldSwitch.tsx | 68 +++
...mGroupWrapper.jsx => FormGroupWrapper.tsx} | 50 +-
...mSubmitButton.jsx => FormSubmitButton.tsx} | 60 ++-
src/Form/constants.js | 20 -
src/Form/constants.ts | 20 +
src/Form/index.js | 3 -
src/Form/index.ts | 3 +
src/Form/isValid.js | 61 ---
src/Form/isValid.ts | 74 +++
src/Form/validators.js | 35 --
src/Form/validators.ts | 38 ++
src/Header/Header.test.jsx | 33 --
src/Header/Header.test.tsx | 33 ++
src/Header/{Header.jsx => Header.tsx} | 69 ++-
src/Header/index.js | 3 -
src/Header/index.ts | 3 +
src/Listing/Listing.jsx | 401 ---------------
src/Listing/Listing.test.jsx | 243 ---------
src/Listing/Listing.test.tsx | 244 +++++++++
src/Listing/Listing.tsx | 372 ++++++++++++++
src/Listing/ListingBranch.jsx | 153 ------
src/Listing/ListingBranch.tsx | 146 ++++++
src/Listing/ListingHeader.jsx | 80 ---
src/Listing/ListingHeader.test.jsx | 27 -
src/Listing/ListingHeader.test.tsx | 29 ++
src/Listing/ListingHeader.tsx | 89 ++++
src/Listing/ListingLine.jsx | 108 ----
src/Listing/ListingLine.test.jsx | 78 ---
src/Listing/ListingLine.test.tsx | 78 +++
src/Listing/ListingLine.tsx | 94 ++++
src/Listing/ListingLoader.jsx | 41 --
src/Listing/ListingLoader.test.jsx | 15 -
src/Listing/ListingLoader.test.tsx | 15 +
src/Listing/ListingLoader.tsx | 41 ++
src/Listing/ListingSearch.jsx | 68 ---
src/Listing/ListingSearch.test.jsx | 103 ----
src/Listing/ListingSearch.test.tsx | 81 +++
src/Listing/ListingSearch.tsx | 71 +++
src/Listing/ListingSearchBranch.jsx | 71 ---
src/Listing/ListingSearchBranch.tsx | 68 +++
src/Listing/ListingToolbar.jsx | 83 ----
src/Listing/ListingToolbar.test.jsx | 16 -
src/Listing/ListingToolbar.test.tsx | 12 +
src/Listing/ListingToolbar.tsx | 71 +++
src/Listing/index.js | 3 -
src/Listing/index.ts | 3 +
src/Menu/Menu.jsx | 50 --
src/Menu/Menu.test.jsx | 21 -
src/Menu/Menu.test.tsx | 23 +
src/Menu/Menu.tsx | 59 +++
src/Menu/MenuItem.jsx | 57 ---
src/Menu/MenuItem.test.jsx | 38 --
src/Menu/MenuItem.test.tsx | 30 ++
src/Menu/MenuItem.tsx | 57 +++
src/Menu/index.jsx | 3 -
src/Menu/index.ts | 3 +
src/NoMatch/NoMatch.jsx | 53 --
src/NoMatch/NoMatch.test.jsx | 28 --
src/NoMatch/NoMatch.test.tsx | 21 +
src/NoMatch/NoMatch.tsx | 51 ++
src/NoMatch/index.js | 3 -
src/NoMatch/index.ts | 3 +
src/Snackbar/Snackbar.jsx | 33 --
src/Snackbar/Snackbar.test.jsx | 10 -
src/Snackbar/Snackbar.test.tsx | 10 +
src/Snackbar/Snackbar.tsx | 28 ++
src/Snackbar/index.d.ts | 17 -
src/Snackbar/index.js | 3 -
src/Snackbar/index.ts | 3 +
src/Tabs/Tabs.test.jsx | 57 ---
src/Tabs/Tabs.test.tsx | 65 +++
src/Tabs/{Tabs.jsx => Tabs.tsx} | 91 ++--
src/Tabs/index.js | 3 -
src/Tabs/index.ts | 3 +
src/app.js | 16 -
src/app.ts | 16 +
src/index.d.ts | 29 --
src/react-app-env.d.ts | 2 +
src/tests/Container.jsx | 78 ---
src/tests/Container.tsx | 59 +++
src/tests/General.jsx | 50 --
src/tests/General.tsx | 45 ++
src/tests/{Page.jsx => Page.tsx} | 110 ++---
src/tests/data/dashboard.js | 22 -
src/tests/data/dashboard.ts | 27 +
src/tests/data/form.jsx | 196 --------
src/tests/data/form.tsx | 228 +++++++++
src/tests/data/listing_data.js | 253 ----------
src/tests/data/listing_data.ts | 255 ++++++++++
src/tests/data/listing_headers.jsx | 44 --
src/tests/data/listing_headers.tsx | 48 ++
src/tests/data/menu.js | 20 -
src/tests/data/menu.ts | 25 +
src/tests/data/tabs.jsx | 45 --
src/tests/data/tabs.tsx | 39 ++
.../{easeInOutQuad.js => easeInOutQuad.ts} | 4 +-
src/utils/replace.js | 18 -
src/utils/replace.ts | 25 +
tsconfig.build.json | 32 ++
tslint.json | 12 -
yarn.lock | 465 +++++++++---------
197 files changed, 5910 insertions(+), 6253 deletions(-)
delete mode 100644 .babelrc
create mode 100644 .eslintrc.js
create mode 100644 .prettierrc.js
delete mode 100644 src/AddButton/AddButton.jsx
delete mode 100644 src/AddButton/AddButton.test.jsx
create mode 100644 src/AddButton/AddButton.test.tsx
create mode 100644 src/AddButton/AddButton.tsx
delete mode 100644 src/AddButton/index.jsx
create mode 100644 src/AddButton/index.tsx
delete mode 100644 src/AppContainer/AppContainer.jsx
delete mode 100644 src/AppContainer/AppContainer.test.jsx
create mode 100644 src/AppContainer/AppContainer.test.tsx
create mode 100644 src/AppContainer/AppContainer.tsx
delete mode 100644 src/AppContainer/index.js
create mode 100644 src/AppContainer/index.ts
delete mode 100644 src/AppContainer/withRoot.jsx
delete mode 100644 src/BackButton/BackButton.jsx
delete mode 100644 src/BackButton/BackButton.test.jsx
create mode 100644 src/BackButton/BackButton.test.tsx
create mode 100644 src/BackButton/BackButton.tsx
delete mode 100644 src/BackButton/BackButtonBranch.jsx
create mode 100644 src/BackButton/BackButtonBranch.tsx
delete mode 100644 src/BackButton/index.js
create mode 100644 src/BackButton/index.ts
delete mode 100644 src/Base/Base.jsx
delete mode 100644 src/Base/Base.test.jsx
create mode 100644 src/Base/Base.test.tsx
create mode 100644 src/Base/Base.tsx
rename src/Base/{BaseBranch.jsx => BaseBranch.tsx} (50%)
delete mode 100644 src/Base/index.js
create mode 100644 src/Base/index.ts
delete mode 100644 src/Confirm/Confirm.jsx
delete mode 100644 src/Confirm/Confirm.test.jsx
create mode 100644 src/Confirm/Confirm.test.tsx
create mode 100644 src/Confirm/Confirm.tsx
rename src/Confirm/{ConfirmBranch.jsx => ConfirmBranch.tsx} (57%)
delete mode 100644 src/Confirm/index.js
create mode 100644 src/Confirm/index.ts
delete mode 100644 src/CookieInfo/Cookie.js
delete mode 100644 src/CookieInfo/Cookie.test.js
create mode 100644 src/CookieInfo/Cookie.test.ts
create mode 100644 src/CookieInfo/Cookie.tsx
delete mode 100644 src/CookieInfo/CookieInfo.jsx
delete mode 100644 src/CookieInfo/CookieInfo.test.jsx
create mode 100644 src/CookieInfo/CookieInfo.test.tsx
create mode 100644 src/CookieInfo/CookieInfo.tsx
delete mode 100644 src/CookieInfo/index.js
create mode 100644 src/CookieInfo/index.ts
delete mode 100644 src/Dashboard/Dashboard.jsx
delete mode 100644 src/Dashboard/Dashboard.test.jsx
create mode 100644 src/Dashboard/Dashboard.test.tsx
create mode 100644 src/Dashboard/Dashboard.tsx
delete mode 100644 src/Dashboard/DashboardBranch.jsx
create mode 100644 src/Dashboard/DashboardBranch.tsx
delete mode 100644 src/Dashboard/DashboardCard.test.jsx
create mode 100644 src/Dashboard/DashboardCard.test.tsx
rename src/Dashboard/{DashboardCard.jsx => DashboardCard.tsx} (58%)
delete mode 100644 src/Dashboard/DashboardGroup.jsx
delete mode 100644 src/Dashboard/DashboardGroup.test.jsx
create mode 100644 src/Dashboard/DashboardGroup.test.tsx
create mode 100644 src/Dashboard/DashboardGroup.tsx
rename src/Dashboard/{index.js => index.ts} (100%)
delete mode 100644 src/Drawer/Drawer.test.jsx
create mode 100644 src/Drawer/Drawer.test.tsx
rename src/Drawer/{Drawer.jsx => Drawer.tsx} (50%)
delete mode 100644 src/Drawer/index.jsx
create mode 100644 src/Drawer/index.tsx
delete mode 100644 src/Form/Form.jsx
delete mode 100644 src/Form/Form.test.jsx
create mode 100644 src/Form/Form.test.tsx
create mode 100644 src/Form/Form.tsx
delete mode 100644 src/Form/FormBranch.jsx
create mode 100644 src/Form/FormBranch.tsx
delete mode 100644 src/Form/FormField.jsx
delete mode 100644 src/Form/FormField.test.jsx
create mode 100644 src/Form/FormField.test.tsx
create mode 100644 src/Form/FormField.tsx
delete mode 100644 src/Form/FormFieldBranch.jsx
create mode 100644 src/Form/FormFieldBranch.tsx
delete mode 100644 src/Form/FormFieldDate.jsx
create mode 100644 src/Form/FormFieldDate.tsx
delete mode 100644 src/Form/FormFieldHidden.jsx
create mode 100644 src/Form/FormFieldHidden.tsx
delete mode 100644 src/Form/FormFieldInput.jsx
delete mode 100644 src/Form/FormFieldInput.test.jsx
create mode 100644 src/Form/FormFieldInput.test.tsx
create mode 100644 src/Form/FormFieldInput.tsx
delete mode 100644 src/Form/FormFieldList.jsx
create mode 100644 src/Form/FormFieldList.tsx
delete mode 100644 src/Form/FormFieldListBranch.jsx
create mode 100644 src/Form/FormFieldListBranch.tsx
delete mode 100644 src/Form/FormFieldSwitch.jsx
delete mode 100644 src/Form/FormFieldSwitch.test.jsx
create mode 100644 src/Form/FormFieldSwitch.test.tsx
create mode 100644 src/Form/FormFieldSwitch.tsx
rename src/Form/{FormGroupWrapper.jsx => FormGroupWrapper.tsx} (56%)
rename src/Form/{FormSubmitButton.jsx => FormSubmitButton.tsx} (55%)
delete mode 100644 src/Form/constants.js
create mode 100644 src/Form/constants.ts
delete mode 100644 src/Form/index.js
create mode 100644 src/Form/index.ts
delete mode 100644 src/Form/isValid.js
create mode 100644 src/Form/isValid.ts
delete mode 100644 src/Form/validators.js
create mode 100644 src/Form/validators.ts
delete mode 100644 src/Header/Header.test.jsx
create mode 100644 src/Header/Header.test.tsx
rename src/Header/{Header.jsx => Header.tsx} (59%)
delete mode 100644 src/Header/index.js
create mode 100644 src/Header/index.ts
delete mode 100644 src/Listing/Listing.jsx
delete mode 100644 src/Listing/Listing.test.jsx
create mode 100644 src/Listing/Listing.test.tsx
create mode 100644 src/Listing/Listing.tsx
delete mode 100644 src/Listing/ListingBranch.jsx
create mode 100644 src/Listing/ListingBranch.tsx
delete mode 100644 src/Listing/ListingHeader.jsx
delete mode 100644 src/Listing/ListingHeader.test.jsx
create mode 100644 src/Listing/ListingHeader.test.tsx
create mode 100644 src/Listing/ListingHeader.tsx
delete mode 100644 src/Listing/ListingLine.jsx
delete mode 100644 src/Listing/ListingLine.test.jsx
create mode 100644 src/Listing/ListingLine.test.tsx
create mode 100644 src/Listing/ListingLine.tsx
delete mode 100644 src/Listing/ListingLoader.jsx
delete mode 100644 src/Listing/ListingLoader.test.jsx
create mode 100644 src/Listing/ListingLoader.test.tsx
create mode 100644 src/Listing/ListingLoader.tsx
delete mode 100644 src/Listing/ListingSearch.jsx
delete mode 100644 src/Listing/ListingSearch.test.jsx
create mode 100644 src/Listing/ListingSearch.test.tsx
create mode 100644 src/Listing/ListingSearch.tsx
delete mode 100644 src/Listing/ListingSearchBranch.jsx
create mode 100644 src/Listing/ListingSearchBranch.tsx
delete mode 100644 src/Listing/ListingToolbar.jsx
delete mode 100644 src/Listing/ListingToolbar.test.jsx
create mode 100644 src/Listing/ListingToolbar.test.tsx
create mode 100644 src/Listing/ListingToolbar.tsx
delete mode 100644 src/Listing/index.js
create mode 100644 src/Listing/index.ts
delete mode 100644 src/Menu/Menu.jsx
delete mode 100644 src/Menu/Menu.test.jsx
create mode 100644 src/Menu/Menu.test.tsx
create mode 100644 src/Menu/Menu.tsx
delete mode 100644 src/Menu/MenuItem.jsx
delete mode 100644 src/Menu/MenuItem.test.jsx
create mode 100644 src/Menu/MenuItem.test.tsx
create mode 100644 src/Menu/MenuItem.tsx
delete mode 100644 src/Menu/index.jsx
create mode 100644 src/Menu/index.ts
delete mode 100644 src/NoMatch/NoMatch.jsx
delete mode 100644 src/NoMatch/NoMatch.test.jsx
create mode 100644 src/NoMatch/NoMatch.test.tsx
create mode 100644 src/NoMatch/NoMatch.tsx
delete mode 100644 src/NoMatch/index.js
create mode 100644 src/NoMatch/index.ts
delete mode 100644 src/Snackbar/Snackbar.jsx
delete mode 100644 src/Snackbar/Snackbar.test.jsx
create mode 100644 src/Snackbar/Snackbar.test.tsx
create mode 100644 src/Snackbar/Snackbar.tsx
delete mode 100644 src/Snackbar/index.d.ts
delete mode 100644 src/Snackbar/index.js
create mode 100644 src/Snackbar/index.ts
delete mode 100644 src/Tabs/Tabs.test.jsx
create mode 100644 src/Tabs/Tabs.test.tsx
rename src/Tabs/{Tabs.jsx => Tabs.tsx} (53%)
delete mode 100644 src/Tabs/index.js
create mode 100644 src/Tabs/index.ts
delete mode 100644 src/app.js
create mode 100644 src/app.ts
delete mode 100644 src/index.d.ts
delete mode 100644 src/tests/Container.jsx
create mode 100644 src/tests/Container.tsx
delete mode 100644 src/tests/General.jsx
create mode 100644 src/tests/General.tsx
rename src/tests/{Page.jsx => Page.tsx} (56%)
delete mode 100644 src/tests/data/dashboard.js
create mode 100644 src/tests/data/dashboard.ts
delete mode 100644 src/tests/data/form.jsx
create mode 100644 src/tests/data/form.tsx
delete mode 100644 src/tests/data/listing_data.js
create mode 100644 src/tests/data/listing_data.ts
delete mode 100644 src/tests/data/listing_headers.jsx
create mode 100644 src/tests/data/listing_headers.tsx
delete mode 100644 src/tests/data/menu.js
create mode 100644 src/tests/data/menu.ts
delete mode 100644 src/tests/data/tabs.jsx
create mode 100644 src/tests/data/tabs.tsx
rename src/utils/{easeInOutQuad.js => easeInOutQuad.ts} (58%)
delete mode 100644 src/utils/replace.js
create mode 100644 src/utils/replace.ts
create mode 100644 tsconfig.build.json
delete mode 100644 tslint.json
diff --git a/.babelrc b/.babelrc
deleted file mode 100644
index bb29451..0000000
--- a/.babelrc
+++ /dev/null
@@ -1,8 +0,0 @@
-{
- "presets": [
- "@babel/react"
- ],
- "plugins": [
- "@babel/plugin-proposal-class-properties"
- ]
-}
diff --git a/.editorconfig b/.editorconfig
index b3dfee7..c6c8b36 100644
--- a/.editorconfig
+++ b/.editorconfig
@@ -1,7 +1,9 @@
root = true
[*]
-end_of_line = lf
-insert_final_newline = true
indent_style = space
indent_size = 2
+end_of_line = lf
+charset = utf-8
+trim_trailing_whitespace = true
+insert_final_newline = true
diff --git a/.eslintrc.js b/.eslintrc.js
new file mode 100644
index 0000000..64373b2
--- /dev/null
+++ b/.eslintrc.js
@@ -0,0 +1,49 @@
+module.exports = {
+ parser: "@typescript-eslint/parser",
+
+ extends: [
+ "plugin:react/recommended",
+ "plugin:@typescript-eslint/recommended",
+ "prettier/@typescript-eslint",
+ "plugin:prettier/recommended",
+ ],
+
+ parserOptions: {
+ ecmaVersion: 2020,
+ sourceType: "module",
+ project: "./tsconfig.build.json",
+ tsconfigRootDir: "./",
+ ecmaFeatures: {
+ jsx: true,
+ },
+ },
+
+ rules: {
+ "react/display-name": "off",
+ "react/prop-types": "off",
+
+ "@typescript-eslint/no-explicit-any": "off",
+ "@typescript-eslint/explicit-function-return-type": "off",
+ "@typescript-eslint/no-non-null-assertion": "off",
+ "@typescript-eslint/no-use-before-define": "off",
+
+ // For the next couple of months ;)
+ "@typescript-eslint/no-empty-function": "off",
+ },
+
+ settings: {
+ react: {
+ version: "detect",
+ },
+ "import/resolver": {
+ node: {
+ extensions: [
+ ".js",
+ ".jsx",
+ ".ts",
+ ".tsx",
+ ],
+ },
+ },
+ },
+};
diff --git a/.prettierrc.js b/.prettierrc.js
new file mode 100644
index 0000000..1c49b44
--- /dev/null
+++ b/.prettierrc.js
@@ -0,0 +1,7 @@
+module.exports = {
+ semi: true,
+ trailingComma: 'all',
+ singleQuote: false,
+ printWidth: 80,
+ arrowParens: 'always',
+};
diff --git a/package.json b/package.json
index fb75637..5263e5f 100644
--- a/package.json
+++ b/package.json
@@ -6,19 +6,18 @@
"license": "MIT",
"scripts": {
"start": "react-scripts start",
- "js:lint": "eslint .",
"js:test": "react-scripts test",
"js:test:coverage": "yarn js:test --coverage",
"js:build": "react-scripts build",
- "ts:lint": "tslint {,src/**/}*.{ts,tsx} -p tsconfig.json",
- "ts:build": "tsc -p tsconfig.json",
+ "ts:lint": "eslint ./src/ --ext ts,tsx",
+ "ts:build": "tsc -p tsconfig.build.json",
"test": "yarn lint && yarn js:test",
"test:coverage": "CI=true yarn js:test:coverage && yarn test:codecov",
"test:codecov": "codecov",
- "lint": "yarn js:lint && yarn ts:lint",
+ "lint": "yarn ts:lint",
"build": "yarn js:build",
- "build:babel": "NODE_ENV=production babel src --out-dir dist",
- "prepublishOnly": "rm -rf ./dist && yarn build:babel"
+ "build:ts": "NODE_ENV=production yarn ts:build",
+ "prepublishOnly": "rm -rf ./dist && yarn build:ts"
},
"dependencies": {
"@date-io/moment": "^2.6.0",
@@ -38,7 +37,6 @@
"react-dom": "^16.13.1",
"react-jss": "^10.1.1",
"react-router-dom": "^5.1.2",
- "recompose": "^0.30.0",
"typescript": "^3.8.3",
"vanilla-store": "^0.4.0"
},
@@ -48,22 +46,32 @@
"react-dom": "^16.13.1"
},
"devDependencies": {
- "@babel/cli": "^7.8.4",
- "@babel/core": "^7.9.6",
- "@babel/plugin-proposal-class-properties": "^7.8.3",
- "@babel/preset-react": "^7.9.4",
+ "@types/classnames": "^2.2.10",
+ "@types/enzyme": "^3.10.5",
+ "@types/enzyme-adapter-react-16": "^1.0.6",
+ "@types/is-url": "^1.2.28",
+ "@types/jest": "^25.2.1",
+ "@types/react-router-dom": "^5.1.5",
+ "@typescript-eslint/eslint-plugin": "^2.30.0",
+ "@typescript-eslint/eslint-plugin-tslint": "^2.30.0",
+ "@typescript-eslint/parser": "^2.30.0",
"babel-polyfill": "^6.26.0",
"codecov": "^3.6.5",
"concurrently": "^5.2.0",
"enzyme": "^3.11.0",
"enzyme-adapter-react-16": "^1.15.2",
"enzyme-to-json": "^3.4.4",
+ "eslint": "^6.8.0",
+ "eslint-config-airbnb": "^18.1.0",
+ "eslint-config-prettier": "^6.11.0",
+ "eslint-config-react": "^1.1.7",
+ "eslint-plugin-prettier": "^3.1.3",
+ "eslint-plugin-react": "^7.19.0",
"express": "^4.17.1",
"jest-enzyme": "^7.1.2",
+ "prettier": "^2.0.5",
"react-mock-router": "^1.0.15",
- "react-scripts": "^3.4.1",
- "tslint": "^6.1.2",
- "tslint-react": "^5.0.0"
+ "react-scripts": "^3.4.1"
},
"keywords": [
"react",
diff --git a/src/AddButton/AddButton.jsx b/src/AddButton/AddButton.jsx
deleted file mode 100644
index c652cec..0000000
--- a/src/AddButton/AddButton.jsx
+++ /dev/null
@@ -1,34 +0,0 @@
-import React from 'react'
-import PropTypes from 'prop-types'
-
-import {
- withStyles,
- Fab,
-} from '@material-ui/core'
-import AddIcon from '@material-ui/icons/Add'
-
-const styles = theme => ({
- button: {
- position: 'fixed',
- right: theme.spacing(3),
- bottom: theme.spacing(3),
- },
-})
-
-const AddButton = ({ onClick, classes }) => (
-
-
-
-)
-
-AddButton.propTypes = {
- onClick: PropTypes.func.isRequired,
- classes: PropTypes.objectOf(PropTypes.string).isRequired,
-}
-
-export default withStyles(styles)(AddButton)
diff --git a/src/AddButton/AddButton.test.jsx b/src/AddButton/AddButton.test.jsx
deleted file mode 100644
index 0572cc0..0000000
--- a/src/AddButton/AddButton.test.jsx
+++ /dev/null
@@ -1,10 +0,0 @@
-import React from 'react'
-import { shallow } from 'enzyme'
-
-import AddButton from '.'
-
-it('renders correctly', () => {
- const tree = shallow( {}} />)
-
- expect(tree).toMatchSnapshot()
-})
diff --git a/src/AddButton/AddButton.test.tsx b/src/AddButton/AddButton.test.tsx
new file mode 100644
index 0000000..4bfa40f
--- /dev/null
+++ b/src/AddButton/AddButton.test.tsx
@@ -0,0 +1,10 @@
+import React from "react";
+import { shallow } from "enzyme";
+
+import AddButton from ".";
+
+it("renders correctly", () => {
+ const tree = shallow( {}} />);
+
+ expect(tree).toMatchSnapshot();
+});
diff --git a/src/AddButton/AddButton.tsx b/src/AddButton/AddButton.tsx
new file mode 100644
index 0000000..a662c1f
--- /dev/null
+++ b/src/AddButton/AddButton.tsx
@@ -0,0 +1,30 @@
+import React from "react";
+import { withStyles, Fab } from "@material-ui/core";
+import AddIcon from "@material-ui/icons/Add";
+
+const styles = (theme: any) => ({
+ button: {
+ position: "fixed",
+ right: theme.spacing(3),
+ bottom: theme.spacing(3),
+ },
+});
+
+type AddButtonProps = {
+ onClick: (...args: any[]) => any;
+ classes: {
+ [key: string]: string;
+ };
+};
+
+const AddButton: React.SFC = ({ onClick, classes }) => (
+
+
+
+);
+export default withStyles(styles as any)(AddButton);
diff --git a/src/AddButton/index.jsx b/src/AddButton/index.jsx
deleted file mode 100644
index 53689c2..0000000
--- a/src/AddButton/index.jsx
+++ /dev/null
@@ -1,3 +0,0 @@
-import AddButton from './AddButton'
-
-export default AddButton
diff --git a/src/AddButton/index.tsx b/src/AddButton/index.tsx
new file mode 100644
index 0000000..b6647e5
--- /dev/null
+++ b/src/AddButton/index.tsx
@@ -0,0 +1,3 @@
+import AddButton from "./AddButton";
+
+export default AddButton;
diff --git a/src/AppContainer/AppContainer.jsx b/src/AppContainer/AppContainer.jsx
deleted file mode 100644
index fead79e..0000000
--- a/src/AppContainer/AppContainer.jsx
+++ /dev/null
@@ -1,54 +0,0 @@
-import React, { Fragment } from 'react'
-import PropTypes from 'prop-types'
-import { createMuiTheme } from '@material-ui/core'
-import {
- blueGrey,
- orange,
-} from '@material-ui/core/colors'
-
-import withRoot from './withRoot'
-
-const defaultTheme = {
- typography: {
- useNextVariants: true,
- },
- palette: {
- primary: {
- light: blueGrey[300],
- main: blueGrey[500],
- dark: blueGrey[700],
- },
- secondary: {
- light: orange[300],
- main: orange[500],
- dark: orange[700],
- },
- },
-}
-
-const AppContainer = ({ theme, children }) => {
- const BaseComponent = () => (
-
- {children}
-
- )
-
- const Root = withRoot(BaseComponent, {
- theme: createMuiTheme(theme),
- })
-
- return (
-
- )
-}
-
-AppContainer.propTypes = {
- theme: PropTypes.objectOf(PropTypes.object),
- children: PropTypes.node.isRequired,
-}
-
-AppContainer.defaultProps = {
- theme: defaultTheme,
-}
-
-export default AppContainer
diff --git a/src/AppContainer/AppContainer.test.jsx b/src/AppContainer/AppContainer.test.jsx
deleted file mode 100644
index 3a67727..0000000
--- a/src/AppContainer/AppContainer.test.jsx
+++ /dev/null
@@ -1,16 +0,0 @@
-import React from 'react'
-import { shallow } from 'enzyme'
-
-import AppContainer from '.'
-
-describe('Component Info', () => {
- it('renders correctly', () => {
- const tree = shallow((
-
- Foo
-
- ))
-
- expect(tree).toMatchSnapshot()
- })
-})
diff --git a/src/AppContainer/AppContainer.test.tsx b/src/AppContainer/AppContainer.test.tsx
new file mode 100644
index 0000000..26bb75c
--- /dev/null
+++ b/src/AppContainer/AppContainer.test.tsx
@@ -0,0 +1,12 @@
+import React from "react";
+import { shallow } from "enzyme";
+
+import AppContainer from ".";
+
+describe("Component Info", () => {
+ it("renders correctly", () => {
+ const tree = shallow(Foo);
+
+ expect(tree).toMatchSnapshot();
+ });
+});
diff --git a/src/AppContainer/AppContainer.tsx b/src/AppContainer/AppContainer.tsx
new file mode 100644
index 0000000..b617de4
--- /dev/null
+++ b/src/AppContainer/AppContainer.tsx
@@ -0,0 +1,56 @@
+import React, { ReactNode } from "react";
+import { blueGrey, orange } from "@material-ui/core/colors";
+import { withStyles, ThemeProvider } from "@material-ui/styles";
+import { createMuiTheme } from "@material-ui/core";
+
+const defaultTheme = {
+ typography: {
+ useNextVariants: true,
+ },
+ palette: {
+ primary: {
+ light: blueGrey[300],
+ main: blueGrey[500],
+ dark: blueGrey[700],
+ },
+ secondary: {
+ light: orange[300],
+ main: orange[500],
+ dark: orange[700],
+ },
+ },
+};
+
+type AppContainerProps = {
+ theme?: {
+ [key: string]: any;
+ };
+};
+
+// Apply some reset
+const styles = (theme: any) => ({
+ "@global": {
+ html: {
+ background: theme.palette.background.default,
+ WebkitFontSmoothing: "antialiased", // Antialiasing.
+ MozOsxFontSmoothing: "grayscale", // Antialiasing.
+ },
+ body: {
+ margin: 0,
+ },
+ },
+});
+
+const BaseComponent = withStyles(styles)((props) => props.children as any);
+const AppContainer: React.SFC = ({
+ theme = defaultTheme,
+ children,
+}) => {
+ return (
+
+ {children}
+
+ );
+};
+
+export default AppContainer;
diff --git a/src/AppContainer/index.js b/src/AppContainer/index.js
deleted file mode 100644
index 79ee5d8..0000000
--- a/src/AppContainer/index.js
+++ /dev/null
@@ -1,3 +0,0 @@
-import AppContainer from './AppContainer'
-
-export default AppContainer
diff --git a/src/AppContainer/index.ts b/src/AppContainer/index.ts
new file mode 100644
index 0000000..4814015
--- /dev/null
+++ b/src/AppContainer/index.ts
@@ -0,0 +1,3 @@
+import AppContainer from "./AppContainer";
+
+export default AppContainer;
diff --git a/src/AppContainer/withRoot.jsx b/src/AppContainer/withRoot.jsx
deleted file mode 100644
index 57bedbc..0000000
--- a/src/AppContainer/withRoot.jsx
+++ /dev/null
@@ -1,44 +0,0 @@
-import React, { Component } from 'react'
-import wrapDisplayName from 'recompose/wrapDisplayName'
-
-import { withStyles, MuiThemeProvider } from '@material-ui/core/styles'
-
-// Apply some reset
-const styles = theme => ({
- '@global': {
- html: {
- background: theme.palette.background.default,
- WebkitFontSmoothing: 'antialiased', // Antialiasing.
- MozOsxFontSmoothing: 'grayscale', // Antialiasing.
- },
- body: {
- margin: 0,
- },
- },
-})
-
-let AppWrapper = props => props.children
-
-AppWrapper = withStyles(styles)(AppWrapper)
-
-const withRoot = (BaseComponent, props) => {
- class WithRoot extends Component {
- render() {
- return (
-
-
-
-
-
- )
- }
- }
-
- if (process.env.NODE_ENV !== 'production') {
- WithRoot.displayName = wrapDisplayName(BaseComponent, 'withRoot')
- }
-
- return WithRoot
-}
-
-export default withRoot
diff --git a/src/BackButton/BackButton.jsx b/src/BackButton/BackButton.jsx
deleted file mode 100644
index 2f1788c..0000000
--- a/src/BackButton/BackButton.jsx
+++ /dev/null
@@ -1,37 +0,0 @@
-import React from 'react'
-import PropTypes from 'prop-types'
-import { withRouter } from 'react-router-dom'
-
-import BackButtonBranch from './BackButtonBranch'
-
-const withBackButton = Component => class extends React.Component {
- static propTypes = {
- url: PropTypes.string.isRequired,
- history: PropTypes.objectOf(PropTypes.any).isRequired,
- }
-
- constructor() {
- super()
-
- this.handleBack = this.handleBack.bind(this)
- }
-
- handleBack() {
- const { url, history } = this.props
-
- history.push(url)
- }
-
- render() {
- return (
-
- )
- }
-}
-
-const BackButton = withBackButton(BackButtonBranch)
-
-export default withRouter(BackButton)
diff --git a/src/BackButton/BackButton.test.jsx b/src/BackButton/BackButton.test.jsx
deleted file mode 100644
index 5b5ae03..0000000
--- a/src/BackButton/BackButton.test.jsx
+++ /dev/null
@@ -1,22 +0,0 @@
-import React from 'react'
-import MockRouter from 'react-mock-router'
-import { shallow } from 'enzyme'
-
-import BackButton from '.'
-
-describe('Back Button', () => {
- it('renders correctly', () => {
- const tree = shallow(())
-
- expect(tree).toMatchSnapshot()
- })
-
- it('updates history on click', () => {
- const push = jest.fn()
- const button = shallow()
-
- button.find(BackButton).simulate('click')
-
- expect(push).toHaveBeenLastCalledWith('/')
- })
-})
diff --git a/src/BackButton/BackButton.test.tsx b/src/BackButton/BackButton.test.tsx
new file mode 100644
index 0000000..ca27235
--- /dev/null
+++ b/src/BackButton/BackButton.test.tsx
@@ -0,0 +1,30 @@
+import React from "react";
+import MockRouter from "react-mock-router";
+import { shallow } from "enzyme";
+
+import BackButton from ".";
+
+describe("Back Button", () => {
+ it("renders correctly", () => {
+ const tree = shallow(
+
+
+ ,
+ );
+
+ expect(tree).toMatchSnapshot();
+ });
+
+ it("updates history on click", () => {
+ const push = jest.fn();
+ const button = shallow(
+
+
+ ,
+ );
+
+ button.find(BackButton).simulate("click");
+
+ expect(push).toHaveBeenLastCalledWith("/");
+ });
+});
diff --git a/src/BackButton/BackButton.tsx b/src/BackButton/BackButton.tsx
new file mode 100644
index 0000000..aab8331
--- /dev/null
+++ b/src/BackButton/BackButton.tsx
@@ -0,0 +1,29 @@
+import React from "react";
+import { withRouter } from "react-router-dom";
+import BackButtonBranch from "./BackButtonBranch";
+
+type WithBackButtonProps = {
+ url: string;
+ history: {
+ [key: string]: any;
+ };
+};
+
+const withBackButton = (Component: any) =>
+ class WithBackButton extends React.Component {
+ constructor(props: WithBackButtonProps) {
+ super(props);
+ this.handleBack = this.handleBack.bind(this);
+ }
+ handleBack() {
+ const { url, history } = this.props;
+ history.push(url);
+ }
+ render() {
+ return ;
+ }
+ };
+
+const BackButton = withBackButton(BackButtonBranch);
+
+export default withRouter(BackButton as any) as any;
diff --git a/src/BackButton/BackButtonBranch.jsx b/src/BackButton/BackButtonBranch.jsx
deleted file mode 100644
index 97cf71d..0000000
--- a/src/BackButton/BackButtonBranch.jsx
+++ /dev/null
@@ -1,31 +0,0 @@
-import React from 'react'
-import PropTypes from 'prop-types'
-
-import {
- Button,
- withStyles,
-} from '@material-ui/core'
-import BackIcon from '@material-ui/icons/ArrowBackIos'
-
-const styles = theme => ({
- leftIcon: {
- marginRight: theme.spacing(),
- },
-})
-
-const BackButtonBranch = ({
- onNavigateBack,
- classes,
-}) => (
-
-)
-
-BackButtonBranch.propTypes = {
- onNavigateBack: PropTypes.func.isRequired,
- classes: PropTypes.objectOf(PropTypes.string).isRequired,
-}
-
-export default withStyles(styles)(BackButtonBranch)
diff --git a/src/BackButton/BackButtonBranch.tsx b/src/BackButton/BackButtonBranch.tsx
new file mode 100644
index 0000000..91bdea0
--- /dev/null
+++ b/src/BackButton/BackButtonBranch.tsx
@@ -0,0 +1,24 @@
+import React from "react";
+import { Button, withStyles } from "@material-ui/core";
+import BackIcon from "@material-ui/icons/ArrowBackIos";
+const styles = (theme: any) => ({
+ leftIcon: {
+ marginRight: theme.spacing(),
+ },
+});
+type BackButtonBranchProps = {
+ onNavigateBack: (...args: any[]) => any;
+ classes: {
+ [key: string]: string;
+ };
+};
+const BackButtonBranch: React.SFC = ({
+ onNavigateBack,
+ classes,
+}) => (
+
+);
+export default withStyles(styles)(BackButtonBranch);
diff --git a/src/BackButton/index.js b/src/BackButton/index.js
deleted file mode 100644
index 96118fd..0000000
--- a/src/BackButton/index.js
+++ /dev/null
@@ -1,3 +0,0 @@
-import BackButton from './BackButton'
-
-export default BackButton
diff --git a/src/BackButton/index.ts b/src/BackButton/index.ts
new file mode 100644
index 0000000..0a54c1e
--- /dev/null
+++ b/src/BackButton/index.ts
@@ -0,0 +1,3 @@
+import BackButton from "./BackButton";
+
+export default BackButton;
diff --git a/src/Base/Base.jsx b/src/Base/Base.jsx
deleted file mode 100644
index e43167d..0000000
--- a/src/Base/Base.jsx
+++ /dev/null
@@ -1,112 +0,0 @@
-import React from 'react'
-import PropTypes from 'prop-types'
-
-import Cookie from '../CookieInfo/Cookie'
-import BaseBranch from './BaseBranch'
-
-const withBase = Component => class extends React.Component {
- static propTypes = {
- title: PropTypes.string.isRequired,
- menuOpen: PropTypes.bool,
- menuData: PropTypes.arrayOf(PropTypes.object).isRequired,
- rightContent: PropTypes.node,
- isHeaderFixed: PropTypes.bool,
- hasHeader: PropTypes.bool,
- hasCookieInfo: PropTypes.bool,
- history: PropTypes.objectOf(PropTypes.any),
- }
-
- static defaultProps = {
- isHeaderFixed: true,
- hasHeader: true,
- hasCookieInfo: false,
- menuOpen: false,
- rightContent: null,
- history: undefined,
- }
-
- constructor(props) {
- super(props)
-
- this.state = {
- open: false,
- cookieInfoOpen: false,
- }
-
- if (props.hasCookieInfo && Cookie.getCookie() === undefined) {
- Cookie.setCookie(false)
- }
-
- this.handleCookieInfoAccept = this.handleCookieInfoAccept.bind(this)
- this.handleDrawerOpen = this.handleDrawerOpen.bind(this)
- this.handleDrawerClose = this.handleDrawerClose.bind(this)
- this.redirectTo = this.redirectTo.bind(this)
- this.onClick = this.onClick.bind(this)
- }
-
- componentDidMount() {
- const { hasCookieInfo, menuOpen } = this.props
-
- this.setState({
- cookieInfoOpen: hasCookieInfo && Cookie.getCookie() === false,
- open: menuOpen,
- })
- }
-
- componentWillReceiveProps({ menuOpen }) {
- this.setState({
- open: menuOpen,
- })
- }
-
- onClick() {
- this.redirectTo('/')
- }
-
- handleDrawerOpen() {
- this.setState({
- open: true,
- })
- }
-
- handleDrawerClose() {
- this.setState({
- open: false,
- })
- }
-
- redirectTo(link) {
- const { history } = this.props
-
- if (history) {
- history.push(link)
- }
- }
-
- handleCookieInfoAccept() {
- this.setState({
- cookieInfoOpen: false,
- })
- }
-
- render() {
- const { rightContent } = this.props
-
- return (
-
- )
- }
-}
-
-const WithBase = withBase(BaseBranch)
-
-export default WithBase
diff --git a/src/Base/Base.test.jsx b/src/Base/Base.test.jsx
deleted file mode 100644
index 808afdc..0000000
--- a/src/Base/Base.test.jsx
+++ /dev/null
@@ -1,151 +0,0 @@
-import React from 'react'
-import MockRouter from 'react-mock-router'
-import Enzyme, { shallow } from 'enzyme'
-import Adapter from 'enzyme-adapter-react-16'
-import { Typography, IconButton, Button } from '@material-ui/core'
-
-import Base from '.'
-
-import menuData from '../tests/data/menu'
-import Header from '../Header'
-import Drawer from '../Drawer'
-import CookieInfo from '../CookieInfo'
-
-Enzyme.configure({ adapter: new Adapter() })
-
-describe('Base', () => {
- it('renders correctly', () => {
- const tree = shallow((
-
-
- Foo
-
-
- ))
-
- expect(tree).toMatchSnapshot()
- })
-
- it('renders correctly without header', () => {
- const tree = shallow((
-
-
- Foo
-
-
- ))
-
- expect(tree).toMatchSnapshot()
- })
-
- it('renders with drawer open', () => {
- const tree = shallow((
-
-
- Foo
-
-
- ))
-
- expect(tree).toMatchSnapshot()
- })
-
- it('click on title', () => {
- const tree = shallow((
-
-
- Foo
-
-
- ))
-
- tree.find(Header).find(Typography).simulate('click')
-
- expect(tree).toMatchSnapshot()
- })
-
- it('click on menu icon if open changes state', () => {
- const tree = shallow((
-
-
- Foo
-
-
- ))
-
- tree.find(Header).find(IconButton).simulate('click')
-
- expect(tree).toMatchSnapshot()
- })
-
- it('click on menu icon if closed changes state', () => {
- const tree = shallow((
-
-
- Foo
-
-
- ))
-
- tree.find(Drawer).find(IconButton).simulate('click')
-
- expect(tree).toMatchSnapshot()
- })
-
- it('call function when cookie is accepted', () => {
- const tree = shallow((
-
-
-
- Foo
-
-
- ))
-
- tree.find(CookieInfo).find(Button).simulate('click')
-
- expect(tree).toMatchSnapshot()
- })
-})
diff --git a/src/Base/Base.test.tsx b/src/Base/Base.test.tsx
new file mode 100644
index 0000000..7a87153
--- /dev/null
+++ b/src/Base/Base.test.tsx
@@ -0,0 +1,134 @@
+import React from "react";
+import MockRouter from "react-mock-router";
+import Enzyme, { shallow } from "enzyme";
+import Adapter from "enzyme-adapter-react-16";
+import { Typography, IconButton, Button } from "@material-ui/core";
+
+import Base from ".";
+
+import menuData from "../tests/data/menu";
+import Header from "../Header";
+import Drawer from "../Drawer";
+import CookieInfo from "../CookieInfo";
+
+Enzyme.configure({ adapter: new Adapter() });
+
+describe("Base", () => {
+ it("renders correctly", () => {
+ const tree = shallow(
+
+
+ Foo
+
+ ,
+ );
+
+ expect(tree).toMatchSnapshot();
+ });
+
+ it("renders correctly without header", () => {
+ const tree = shallow(
+
+
+ Foo
+
+ ,
+ );
+
+ expect(tree).toMatchSnapshot();
+ });
+
+ it("renders with drawer open", () => {
+ const tree = shallow(
+
+
+ Foo
+
+ ,
+ );
+
+ expect(tree).toMatchSnapshot();
+ });
+
+ it("click on title", () => {
+ const tree = shallow(
+
+
+ Foo
+
+ ,
+ );
+
+ tree.find(Header).find(Typography).simulate("click");
+
+ expect(tree).toMatchSnapshot();
+ });
+
+ it("click on menu icon if open changes state", () => {
+ const tree = shallow(
+
+
+ Foo
+
+ ,
+ );
+
+ tree.find(Header).find(IconButton).simulate("click");
+
+ expect(tree).toMatchSnapshot();
+ });
+
+ it("click on menu icon if closed changes state", () => {
+ const tree = shallow(
+
+
+ Foo
+
+ ,
+ );
+
+ tree.find(Drawer).find(IconButton).simulate("click");
+
+ expect(tree).toMatchSnapshot();
+ });
+
+ it("call function when cookie is accepted", () => {
+ const tree = shallow(
+
+
+
+ Foo
+
+ ,
+ );
+
+ tree.find(CookieInfo).find(Button).simulate("click");
+
+ expect(tree).toMatchSnapshot();
+ });
+});
diff --git a/src/Base/Base.tsx b/src/Base/Base.tsx
new file mode 100644
index 0000000..742b587
--- /dev/null
+++ b/src/Base/Base.tsx
@@ -0,0 +1,92 @@
+import React from "react";
+import Cookie from "../CookieInfo/Cookie";
+import BaseBranch from "./BaseBranch";
+type WithBaseProps = {
+ title: string;
+ menuOpen?: boolean;
+ menuData: object[];
+ rightContent?: React.ReactNode;
+ isHeaderFixed?: boolean;
+ hasHeader?: boolean;
+ hasCookieInfo?: boolean;
+ history?: {
+ [key: string]: any;
+ };
+};
+
+type WithBaseState = {
+ cookieInfoOpen: boolean | undefined;
+ open: boolean | undefined;
+};
+
+const withBase = (Component: any) =>
+ class WithBase extends React.Component {
+ constructor(props: WithBaseProps) {
+ super(props);
+ this.state = {
+ open: false,
+ cookieInfoOpen: false,
+ };
+ if (props.hasCookieInfo && Cookie.getCookie() === undefined) {
+ Cookie.setCookie(false);
+ }
+ this.handleCookieInfoAccept = this.handleCookieInfoAccept.bind(this);
+ this.handleDrawerOpen = this.handleDrawerOpen.bind(this);
+ this.handleDrawerClose = this.handleDrawerClose.bind(this);
+ this.redirectTo = this.redirectTo.bind(this);
+ this.onClick = this.onClick.bind(this);
+ }
+ componentDidMount() {
+ const { hasCookieInfo, menuOpen } = this.props;
+ this.setState({
+ cookieInfoOpen: hasCookieInfo && Cookie.getCookie() === false,
+ open: menuOpen,
+ });
+ }
+ UNSAFE_componentWillReceiveProps({ menuOpen }: WithBaseProps) {
+ this.setState({
+ open: menuOpen,
+ });
+ }
+ onClick() {
+ this.redirectTo("/");
+ }
+ handleDrawerOpen() {
+ this.setState({
+ open: true,
+ });
+ }
+ handleDrawerClose() {
+ this.setState({
+ open: false,
+ });
+ }
+ redirectTo(link: string) {
+ const { history } = this.props;
+ if (history) {
+ history.push(link);
+ }
+ }
+ handleCookieInfoAccept() {
+ this.setState({
+ cookieInfoOpen: false,
+ });
+ }
+ render() {
+ const { rightContent } = this.props;
+ return (
+
+ );
+ }
+ };
+const WithBase = withBase(BaseBranch);
+export default WithBase;
diff --git a/src/Base/BaseBranch.jsx b/src/Base/BaseBranch.tsx
similarity index 50%
rename from src/Base/BaseBranch.jsx
rename to src/Base/BaseBranch.tsx
index cee527d..0e40867 100644
--- a/src/Base/BaseBranch.jsx
+++ b/src/Base/BaseBranch.tsx
@@ -1,22 +1,20 @@
-import React, { Fragment } from 'react'
-import PropTypes from 'prop-types'
-import classNames from 'classnames'
+import React from "react";
+import classNames from "classnames";
+import { withStyles } from "@material-ui/core";
+import Drawer from "../Drawer";
+import Header from "../Header";
+import { MenuDataItem } from "../Menu/Menu";
-import { withStyles } from '@material-ui/core'
+const drawerWidth = 280;
-import Drawer from '../Drawer'
-import Header from '../Header'
-
-const drawerWidth = 280
-
-const styles = theme => ({
+const styles = (theme: any) => ({
appFrame: {
- position: 'relative',
- display: 'flex',
- width: '100%',
- height: '100%',
- minHeight: '100vh',
- transition: '0.25s',
+ position: "relative",
+ display: "flex",
+ width: "100%",
+ height: "100%",
+ minHeight: "100vh",
+ transition: "0.25s",
},
appFrameWithCookieInfo: {
marginTop: theme.spacing(6),
@@ -27,24 +25,42 @@ const styles = theme => ({
flexGrow: 1,
backgroundColor: theme.palette.background.default,
padding: theme.spacing(3),
- transition: theme.transitions.create('margin', {
+ transition: theme.transitions.create("margin", {
easing: theme.transitions.easing.sharp,
duration: theme.transitions.duration.leavingScreen,
}),
- height: 'calc(100% - 56px)',
+ height: "calc(100% - 56px)",
marginTop: theme.spacing(8),
marginLeft: -drawerWidth,
},
contentShift: {
- transition: theme.transitions.create('margin', {
+ transition: theme.transitions.create("margin", {
easing: theme.transitions.easing.easeOut,
duration: theme.transitions.duration.enteringScreen,
}),
marginLeft: 0,
},
-})
+});
-const BaseBranch = ({
+type BaseBranchProps = {
+ open?: boolean;
+ title: string;
+ menuData: MenuDataItem[];
+ isHeaderFixed?: boolean;
+ cookieInfoOpen?: boolean;
+ onClick: (...args: any[]) => any;
+ handleDrawerOpen: (...args: any[]) => any;
+ handleDrawerClose: (...args: any[]) => any;
+ onCookieInfoAccept: (...args: any[]) => any;
+ redirectTo: (...args: any[]) => any;
+ rightContent?: JSX.Element;
+ hasHeader: boolean;
+ classes: {
+ [key: string]: string;
+ };
+};
+
+const BaseBranch: React.SFC = ({
open,
title,
menuData,
@@ -56,13 +72,13 @@ const BaseBranch = ({
onCookieInfoAccept,
redirectTo,
rightContent,
- hasHeader,
+ hasHeader = true,
classes,
children,
...rest
}) => (
-
- {hasHeader ? (
+ <>
+ {hasHeader && (
- ) : null}
+ )}
-
- {hasHeader ? (
+ {hasHeader && (
- ) : null}
+ )}
- {React.Children.map(children, child => (
- React.cloneElement(child, {
+ {React.Children.map(children, (child) =>
+ React.cloneElement(child as any, {
isOpen: cookieInfoOpen,
onAccept: onCookieInfoAccept,
...rest,
- })
- ))}
+ }),
+ )}
-
-)
-
-BaseBranch.propTypes = {
- open: PropTypes.bool,
- title: PropTypes.string.isRequired,
- menuData: PropTypes.arrayOf(PropTypes.object).isRequired,
- isHeaderFixed: PropTypes.bool,
- cookieInfoOpen: PropTypes.bool,
- onClick: PropTypes.func.isRequired,
- handleDrawerOpen: PropTypes.func.isRequired,
- handleDrawerClose: PropTypes.func.isRequired,
- onCookieInfoAccept: PropTypes.func.isRequired,
- redirectTo: PropTypes.func.isRequired,
- rightContent: PropTypes.element,
- hasHeader: PropTypes.bool.isRequired,
- classes: PropTypes.objectOf(PropTypes.string).isRequired,
- children: PropTypes.node.isRequired,
-}
+ >
+);
BaseBranch.defaultProps = {
open: false,
isHeaderFixed: false,
cookieInfoOpen: false,
- rightContent: (),
-}
+};
-export default withStyles(styles)(BaseBranch)
+export default withStyles(styles as any)(BaseBranch);
diff --git a/src/Base/index.js b/src/Base/index.js
deleted file mode 100644
index d11f2ba..0000000
--- a/src/Base/index.js
+++ /dev/null
@@ -1,3 +0,0 @@
-import Base from './Base'
-
-export default Base
diff --git a/src/Base/index.ts b/src/Base/index.ts
new file mode 100644
index 0000000..2e06f6b
--- /dev/null
+++ b/src/Base/index.ts
@@ -0,0 +1,3 @@
+import Base from "./Base";
+
+export default Base;
diff --git a/src/Confirm/Confirm.jsx b/src/Confirm/Confirm.jsx
deleted file mode 100644
index 7011248..0000000
--- a/src/Confirm/Confirm.jsx
+++ /dev/null
@@ -1,80 +0,0 @@
-import React from 'react'
-import PropTypes from 'prop-types'
-
-import ConfirmBranch from './ConfirmBranch'
-
-const withConfirm = Component => class extends React.Component {
- static propTypes = {
- isOpen: PropTypes.bool,
- title: PropTypes.string,
- description: PropTypes.string.isRequired,
- agreeText: PropTypes.string,
- disagreeText: PropTypes.string,
- onConfirm: PropTypes.func.isRequired,
- onClose: PropTypes.func,
- hasCloseButton: PropTypes.bool,
- }
-
- static defaultProps = {
- title: null,
- isOpen: false,
- agreeText: 'Agree',
- disagreeText: 'Disagree',
- onClose: () => {},
- hasCloseButton: true,
- }
-
- constructor(props) {
- super(props)
-
- this.state = {
- isOpen: false,
- }
-
- this.handleClose = this.handleClose.bind(this)
- this.handleConfirm = this.handleConfirm.bind(this)
- }
-
- componentDidMount() {
- this.setState({
- isOpen: this.props.isOpen,
- })
- }
-
- componentWillReceiveProps(nextProps) {
- if (this.state.isOpen !== nextProps.isOpen) {
- this.setState({
- isOpen: nextProps.isOpen,
- })
- }
- }
-
- handleClose() {
- this.setState({
- isOpen: false,
- })
-
- this.props.onClose()
- }
-
- handleConfirm() {
- this.setState({
- isOpen: false,
- })
-
- this.props.onConfirm()
- }
-
- render() {
- return (
-
- )
- }
-}
-
-export default withConfirm(ConfirmBranch)
diff --git a/src/Confirm/Confirm.test.jsx b/src/Confirm/Confirm.test.jsx
deleted file mode 100644
index 7a74f74..0000000
--- a/src/Confirm/Confirm.test.jsx
+++ /dev/null
@@ -1,79 +0,0 @@
-import React from 'react'
-import Enzyme, { shallow } from 'enzyme'
-import Adapter from 'enzyme-adapter-react-16'
-
-import Confirm from '.'
-
-Enzyme.configure({ adapter: new Adapter() })
-
-describe('Confirm', () => {
- it('renders correctly', () => {
- const tree = shallow( { }}
- />)
-
- expect(tree).toMatchSnapshot()
- })
-
- it('renders correctly without close button', () => {
- const tree = shallow( { }}
- hasCloseButton={false}
- />)
-
- expect(tree).toMatchSnapshot()
- })
-
- it('confirm is handled', () => {
- const onConfirm = jest.fn()
- const confirm = shallow((
-
- ))
-
- expect(confirm.state().isOpen).toBe(true)
- confirm.instance().handleConfirm()
-
- expect(onConfirm).toHaveBeenCalled()
- })
-
- it('close is handled', () => {
- const onClose = jest.fn()
- const confirm = shallow((
- {}}
- onClose={onClose}
- />
- ))
-
- expect(confirm.state().isOpen).toBe(true)
- confirm.instance().handleClose()
-
- expect(onClose).toHaveBeenCalled()
- })
-
- it('changes open', () => {
- const confirm = shallow((
- { }}
- />
- ))
-
- confirm.setProps({
- isOpen: false,
- })
- })
-})
diff --git a/src/Confirm/Confirm.test.tsx b/src/Confirm/Confirm.test.tsx
new file mode 100644
index 0000000..608ae4b
--- /dev/null
+++ b/src/Confirm/Confirm.test.tsx
@@ -0,0 +1,70 @@
+import React from "react";
+import Enzyme, { shallow } from "enzyme";
+import Adapter from "enzyme-adapter-react-16";
+
+import Confirm from ".";
+
+Enzyme.configure({ adapter: new Adapter() });
+
+describe("Confirm", () => {
+ it("renders correctly", () => {
+ const tree = shallow(
+ {}} />,
+ );
+
+ expect(tree).toMatchSnapshot();
+ });
+
+ it("renders correctly without close button", () => {
+ const tree = shallow(
+ {}}
+ hasCloseButton={false}
+ />,
+ );
+
+ expect(tree).toMatchSnapshot();
+ });
+
+ it("confirm is handled", () => {
+ const onConfirm = jest.fn();
+ const confirm = shallow(
+ ,
+ );
+
+ expect(confirm.state().isOpen).toBe(true);
+ confirm.instance().handleConfirm();
+
+ expect(onConfirm).toHaveBeenCalled();
+ });
+
+ it("close is handled", () => {
+ const onClose = jest.fn();
+ const confirm = shallow(
+ {}}
+ onClose={onClose}
+ />,
+ );
+
+ expect(confirm.state().isOpen).toBe(true);
+ confirm.instance().handleClose();
+
+ expect(onClose).toHaveBeenCalled();
+ });
+
+ it("changes open", () => {
+ const confirm = shallow(
+ {}} />,
+ );
+
+ confirm.setProps({
+ isOpen: false,
+ });
+ });
+});
diff --git a/src/Confirm/Confirm.tsx b/src/Confirm/Confirm.tsx
new file mode 100644
index 0000000..99d5a97
--- /dev/null
+++ b/src/Confirm/Confirm.tsx
@@ -0,0 +1,67 @@
+import React from "react";
+import ConfirmBranch from "./ConfirmBranch";
+type WithConfirmProps = {
+ isOpen?: boolean;
+ title?: string;
+ description: string;
+ agreeText?: string;
+ disagreeText?: string;
+ onConfirm: (...args: any[]) => any;
+ onClose?: (...args: any[]) => any;
+ hasCloseButton?: boolean;
+};
+
+type WithConfirmState = {
+ isOpen: boolean | undefined;
+};
+
+const withConfirm = (Component: any) =>
+ class WithConfirm extends React.Component<
+ WithConfirmProps,
+ WithConfirmState
+ > {
+ constructor(props: WithConfirmProps) {
+ super(props);
+ this.state = {
+ isOpen: false,
+ };
+ this.handleClose = this.handleClose.bind(this);
+ this.handleConfirm = this.handleConfirm.bind(this);
+ }
+ componentDidMount() {
+ this.setState({
+ isOpen: this.props.isOpen,
+ });
+ }
+ UNSAFE_componentWillReceiveProps(nextProps: WithConfirmProps) {
+ if (this.state.isOpen !== nextProps.isOpen) {
+ this.setState({
+ isOpen: nextProps.isOpen,
+ });
+ }
+ }
+ handleClose() {
+ this.setState({
+ isOpen: false,
+ });
+ this.props.onClose && this.props.onClose();
+ }
+ handleConfirm() {
+ this.setState({
+ isOpen: false,
+ });
+ this.props.onConfirm();
+ }
+ render() {
+ return (
+
+ );
+ }
+ };
+
+export default withConfirm(ConfirmBranch);
diff --git a/src/Confirm/ConfirmBranch.jsx b/src/Confirm/ConfirmBranch.tsx
similarity index 57%
rename from src/Confirm/ConfirmBranch.jsx
rename to src/Confirm/ConfirmBranch.tsx
index 9ddf6be..87783a2 100644
--- a/src/Confirm/ConfirmBranch.jsx
+++ b/src/Confirm/ConfirmBranch.tsx
@@ -1,5 +1,4 @@
-import React from 'react'
-import PropTypes from 'prop-types'
+import React from "react";
import {
Button,
Dialog,
@@ -8,15 +7,26 @@ import {
DialogContentText,
DialogTitle,
withStyles,
-} from '@material-ui/core'
-
-const styles = theme => ({
+} from "@material-ui/core";
+const styles = (theme: any) => ({
primaryButton: {
color: theme.palette.primary.dark,
},
-})
-
-const ConfirmBranch = ({
+});
+type ConfirmBranchProps = {
+ isOpen: boolean;
+ title?: string;
+ description: string;
+ agreeText: string;
+ disagreeText: string;
+ onClose: (...args: any[]) => any;
+ onConfirm: (...args: any[]) => any;
+ hasCloseButton: boolean;
+ classes: {
+ [key: string]: string;
+ };
+};
+const ConfirmBranch: React.SFC = ({
title,
description,
isOpen,
@@ -33,9 +43,7 @@ const ConfirmBranch = ({
aria-labelledby="alert-dialog-title"
aria-describedby="alert-dialog-description"
>
- {title ? (
- {title}
- ) : null}
+ {title ? {title} : null}
@@ -59,22 +67,10 @@ const ConfirmBranch = ({
-)
-
-ConfirmBranch.propTypes = {
- isOpen: PropTypes.bool.isRequired,
- title: PropTypes.string,
- description: PropTypes.string.isRequired,
- agreeText: PropTypes.string.isRequired,
- disagreeText: PropTypes.string.isRequired,
- onClose: PropTypes.func.isRequired,
- onConfirm: PropTypes.func.isRequired,
- hasCloseButton: PropTypes.bool.isRequired,
- classes: PropTypes.objectOf(PropTypes.string).isRequired,
-}
+);
ConfirmBranch.defaultProps = {
- title: null,
-}
+ title: undefined,
+};
-export default withStyles(styles)(ConfirmBranch)
+export default withStyles(styles)(ConfirmBranch);
diff --git a/src/Confirm/index.js b/src/Confirm/index.js
deleted file mode 100644
index d62fa91..0000000
--- a/src/Confirm/index.js
+++ /dev/null
@@ -1,3 +0,0 @@
-import Confirm from './Confirm'
-
-export default Confirm
diff --git a/src/Confirm/index.ts b/src/Confirm/index.ts
new file mode 100644
index 0000000..cbbbe24
--- /dev/null
+++ b/src/Confirm/index.ts
@@ -0,0 +1,3 @@
+import Confirm from "./Confirm";
+
+export default Confirm;
diff --git a/src/CookieInfo/Cookie.js b/src/CookieInfo/Cookie.js
deleted file mode 100644
index 465f9ab..0000000
--- a/src/CookieInfo/Cookie.js
+++ /dev/null
@@ -1,34 +0,0 @@
-class Cookie {
- constructor() {
- this.value = undefined
- }
-
- getCookie() {
- if (this.value !== undefined) {
- return this.value
- }
-
- const value = `; ${document.cookie}`
- const parts = value.split('; cookie_concent=')
-
- if (parts.length < 2) {
- return undefined
- }
-
- const concentValue = parts.pop().split(';').shift() !== 'false'
- this.value = concentValue
-
- return concentValue
- }
-
- setCookie(value) {
- this.value = value
-
- const exdate = new Date()
- exdate.setDate(exdate.getDate() + 365)
-
- document.cookie = `cookie_concent=${value};expires=${exdate.toUTCString()};path=/`
- }
-}
-
-export default new Cookie()
diff --git a/src/CookieInfo/Cookie.test.js b/src/CookieInfo/Cookie.test.js
deleted file mode 100644
index cb5e784..0000000
--- a/src/CookieInfo/Cookie.test.js
+++ /dev/null
@@ -1,31 +0,0 @@
-import Cookie from './Cookie'
-
-describe('CookieInfo/Cookie', () => {
- const COOKIE_VALUE = 'foocookiefoo'
-
- it('gets undefined cookie', () => {
- const returnValue = Cookie.getCookie()
-
- expect(returnValue).toBe(undefined)
- })
-
- it('gets predefined cookie', () => {
- document.cookie = `cookie_concent=${COOKIE_VALUE}`
-
- const returnValue = Cookie.getCookie()
-
- expect(returnValue).toBe(true)
- })
-
- it('sets cookie', () => {
- Cookie.setCookie(COOKIE_VALUE)
-
- expect(document.cookie).toBe(`cookie_concent=${COOKIE_VALUE}`)
- })
-
- it('gets cookie value correctly', () => {
- const returnValue = Cookie.getCookie()
-
- expect(returnValue).toBe(COOKIE_VALUE)
- })
-})
diff --git a/src/CookieInfo/Cookie.test.ts b/src/CookieInfo/Cookie.test.ts
new file mode 100644
index 0000000..78ddbb7
--- /dev/null
+++ b/src/CookieInfo/Cookie.test.ts
@@ -0,0 +1,31 @@
+import Cookie from "./Cookie";
+
+describe("CookieInfo/Cookie", () => {
+ const COOKIE_VALUE = "foocookiefoo";
+
+ it("gets undefined cookie", () => {
+ const returnValue = Cookie.getCookie();
+
+ expect(returnValue).toBe(undefined);
+ });
+
+ it("gets predefined cookie", () => {
+ document.cookie = `cookie_concent=${COOKIE_VALUE}`;
+
+ const returnValue = Cookie.getCookie();
+
+ expect(returnValue).toBe(true);
+ });
+
+ it("sets cookie", () => {
+ Cookie.setCookie(COOKIE_VALUE);
+
+ expect(document.cookie).toBe(`cookie_concent=${COOKIE_VALUE}`);
+ });
+
+ it("gets cookie value correctly", () => {
+ const returnValue = Cookie.getCookie();
+
+ expect(returnValue).toBe(COOKIE_VALUE);
+ });
+});
diff --git a/src/CookieInfo/Cookie.tsx b/src/CookieInfo/Cookie.tsx
new file mode 100644
index 0000000..fc58c18
--- /dev/null
+++ b/src/CookieInfo/Cookie.tsx
@@ -0,0 +1,29 @@
+class Cookie {
+ private value: any;
+
+ constructor() {
+ this.value = undefined;
+ }
+ getCookie() {
+ if (this.value !== undefined) {
+ return this.value;
+ }
+ const value = `; ${document.cookie}`;
+ const parts = value.split("; cookie_concent=");
+ if (parts.length < 2) {
+ return undefined;
+ }
+ const concentValue = parts.pop()?.split(";").shift() !== "false";
+
+ this.value = concentValue;
+ return concentValue;
+ }
+
+ setCookie(value: any) {
+ this.value = value;
+ const exdate = new Date();
+ exdate.setDate(exdate.getDate() + 365);
+ document.cookie = `cookie_concent=${value};expires=${exdate.toUTCString()};path=/`;
+ }
+}
+export default new Cookie();
diff --git a/src/CookieInfo/CookieInfo.jsx b/src/CookieInfo/CookieInfo.jsx
deleted file mode 100644
index c64a54c..0000000
--- a/src/CookieInfo/CookieInfo.jsx
+++ /dev/null
@@ -1,100 +0,0 @@
-import React from 'react'
-import PropTypes from 'prop-types'
-import classNames from 'classnames'
-
-import {
- Button,
- withStyles,
-} from '@material-ui/core'
-
-import Cookie from './Cookie'
-
-const styles = theme => ({
- root: {
- position: 'fixed',
- top: 0,
- left: 0,
- right: 0,
- zIndex: 3000,
- display: 'flex',
- backgroundColor: theme.palette.secondary.main,
- paddingTop: theme.spacing(),
- paddingBottom: theme.spacing(),
- paddingLeft: theme.spacing(3),
- paddingRight: theme.spacing(3),
- transform: 'translate(0, -100%)',
- opacity: 0,
- transition: 'transform 0.25s, opacity 0s 0.25s',
- },
- rootActive: {
- transform: 'translate(0, 0)',
- opacity: 1,
- },
- content: {
- display: 'flex',
- flex: 1,
- opacity: 1,
- alignItems: 'center',
- },
- button: {
- float: 'right',
- },
-})
-
-class CookieInfo extends React.Component {
- static propTypes = {
- isOpen: PropTypes.bool,
- onAccept: PropTypes.func,
- buttonText: PropTypes.string,
- children: PropTypes.node.isRequired,
- classes: PropTypes.objectOf(PropTypes.string).isRequired,
- }
-
- static defaultProps = {
- isOpen: false,
- onAccept: null,
- buttonText: 'Accept',
- }
-
- constructor(props) {
- super(props)
-
- this.handleClick = this.handleClick.bind(this)
- }
-
- handleClick() {
- const { onAccept } = this.props
- if (onAccept) {
- onAccept()
- }
-
- Cookie.setCookie(true)
- }
-
- render() {
- const {
- isOpen,
- buttonText,
- classes,
- children,
- } = this.props
- const className = classNames({
- [classes.root]: true,
- [classes.rootActive]: isOpen,
- })
-
- return (
-
-
- {children}
-
-
-
-
- )
- }
-}
-
-export default withStyles(styles)(CookieInfo)
diff --git a/src/CookieInfo/CookieInfo.test.jsx b/src/CookieInfo/CookieInfo.test.jsx
deleted file mode 100644
index 7a32b6c..0000000
--- a/src/CookieInfo/CookieInfo.test.jsx
+++ /dev/null
@@ -1,34 +0,0 @@
-import React from 'react'
-import Enzyme, { shallow } from 'enzyme'
-import Adapter from 'enzyme-adapter-react-16'
-import { Button } from '@material-ui/core'
-
-import CookieInfo from '.'
-
-Enzyme.configure({ adapter: new Adapter() })
-
-
-describe('Component Info', () => {
- it('renders correctly', () => {
- const tree = shallow(Foo)
-
- expect(tree).toMatchSnapshot()
- })
-
- it('click away', () => {
- const mockCallBack = jest.fn()
-
- const cookieInfo = shallow((
-
- Foo
-
- ))
-
- cookieInfo.find(Button).simulate('click')
-
- expect(mockCallBack).toHaveBeenCalled()
- })
-})
diff --git a/src/CookieInfo/CookieInfo.test.tsx b/src/CookieInfo/CookieInfo.test.tsx
new file mode 100644
index 0000000..2a901ba
--- /dev/null
+++ b/src/CookieInfo/CookieInfo.test.tsx
@@ -0,0 +1,30 @@
+import React from "react";
+import Enzyme, { shallow } from "enzyme";
+import Adapter from "enzyme-adapter-react-16";
+import { Button } from "@material-ui/core";
+
+import CookieInfo from ".";
+
+Enzyme.configure({ adapter: new Adapter() });
+
+describe("Component Info", () => {
+ it("renders correctly", () => {
+ const tree = shallow(Foo);
+
+ expect(tree).toMatchSnapshot();
+ });
+
+ it("click away", () => {
+ const mockCallBack = jest.fn();
+
+ const cookieInfo = shallow(
+
+ Foo
+ ,
+ );
+
+ cookieInfo.find(Button).simulate("click");
+
+ expect(mockCallBack).toHaveBeenCalled();
+ });
+});
diff --git a/src/CookieInfo/CookieInfo.tsx b/src/CookieInfo/CookieInfo.tsx
new file mode 100644
index 0000000..3fe4668
--- /dev/null
+++ b/src/CookieInfo/CookieInfo.tsx
@@ -0,0 +1,77 @@
+import React from "react";
+import classNames from "classnames";
+import { Button, withStyles } from "@material-ui/core";
+import Cookie from "./Cookie";
+
+const styles = (theme: any) => ({
+ root: {
+ position: "fixed",
+ top: 0,
+ left: 0,
+ right: 0,
+ zIndex: 3000,
+ display: "flex",
+ backgroundColor: theme.palette.secondary.main,
+ paddingTop: theme.spacing(),
+ paddingBottom: theme.spacing(),
+ paddingLeft: theme.spacing(3),
+ paddingRight: theme.spacing(3),
+ transform: "translate(0, -100%)",
+ opacity: 0,
+ transition: "transform 0.25s, opacity 0s 0.25s",
+ },
+ rootActive: {
+ transform: "translate(0, 0)",
+ opacity: 1,
+ },
+ content: {
+ display: "flex",
+ flex: 1,
+ opacity: 1,
+ alignItems: "center",
+ },
+ button: {
+ float: "right",
+ },
+});
+
+type CookieInfoProps = {
+ isOpen?: boolean;
+ onAccept?: (...args: any[]) => any;
+ buttonText?: string;
+ classes: {
+ [key: string]: string;
+ };
+};
+
+class CookieInfo extends React.Component {
+ constructor(props: CookieInfoProps) {
+ super(props);
+ this.handleClick = this.handleClick.bind(this);
+ }
+ handleClick() {
+ const { onAccept } = this.props;
+ if (onAccept) {
+ onAccept();
+ }
+ Cookie.setCookie(true);
+ }
+ render() {
+ const { isOpen, buttonText, classes, children } = this.props;
+ const className = classNames({
+ [classes.root]: true,
+ [classes.rootActive]: isOpen,
+ });
+ return (
+
+
{children}
+
+
+
+ );
+ }
+}
+
+export default withStyles(styles as any)(CookieInfo);
diff --git a/src/CookieInfo/index.js b/src/CookieInfo/index.js
deleted file mode 100644
index 6798f52..0000000
--- a/src/CookieInfo/index.js
+++ /dev/null
@@ -1,3 +0,0 @@
-import CookieInfo from './CookieInfo'
-
-export default CookieInfo
diff --git a/src/CookieInfo/index.ts b/src/CookieInfo/index.ts
new file mode 100644
index 0000000..947e0f2
--- /dev/null
+++ b/src/CookieInfo/index.ts
@@ -0,0 +1,3 @@
+import CookieInfo from "./CookieInfo";
+
+export default CookieInfo;
diff --git a/src/Dashboard/Dashboard.jsx b/src/Dashboard/Dashboard.jsx
deleted file mode 100644
index b35ea53..0000000
--- a/src/Dashboard/Dashboard.jsx
+++ /dev/null
@@ -1,34 +0,0 @@
-import React from 'react'
-import PropTypes from 'prop-types'
-import { withRouter } from 'react-router-dom'
-
-import DashboardBranch from './DashboardBranch'
-
-const withDashboard = Component => class extends React.Component {
- static propTypes = {
- history: PropTypes.objectOf(PropTypes.any).isRequired,
- }
-
- constructor() {
- super()
-
- this.handleClick = this.handleClick.bind(this)
- }
-
- handleClick(url) {
- this.props.history.push(url)
- }
-
- render() {
- return (
-
- )
- }
-}
-
-const Dashboard = withDashboard(DashboardBranch)
-
-export default withRouter(Dashboard)
diff --git a/src/Dashboard/Dashboard.test.jsx b/src/Dashboard/Dashboard.test.jsx
deleted file mode 100644
index 923285f..0000000
--- a/src/Dashboard/Dashboard.test.jsx
+++ /dev/null
@@ -1,27 +0,0 @@
-import React from 'react'
-import { BrowserRouter as Router } from 'react-router-dom'
-import { shallow } from 'enzyme'
-
-import Dashboard from '.'
-
-it('renders correctly', () => {
- const tree = shallow((
-
-
-
- ))
-
- expect(tree).toMatchSnapshot()
-})
diff --git a/src/Dashboard/Dashboard.test.tsx b/src/Dashboard/Dashboard.test.tsx
new file mode 100644
index 0000000..4d00258
--- /dev/null
+++ b/src/Dashboard/Dashboard.test.tsx
@@ -0,0 +1,31 @@
+import React from "react";
+import { BrowserRouter as Router } from "react-router-dom";
+import { shallow } from "enzyme";
+
+import Dashboard from ".";
+
+it("renders correctly", () => {
+ const tree = shallow(
+
+
+ ,
+ );
+
+ expect(tree).toMatchSnapshot();
+});
diff --git a/src/Dashboard/Dashboard.tsx b/src/Dashboard/Dashboard.tsx
new file mode 100644
index 0000000..d8baa8b
--- /dev/null
+++ b/src/Dashboard/Dashboard.tsx
@@ -0,0 +1,24 @@
+import React from "react";
+import { withRouter } from "react-router-dom";
+import DashboardBranch from "./DashboardBranch";
+type WithDashboardProps = {
+ history: {
+ [key: string]: any;
+ };
+};
+const withDashboard = (Component: any) =>
+ class WithDashboard extends React.Component {
+ constructor(props: WithDashboardProps) {
+ super(props);
+
+ this.handleClick = this.handleClick.bind(this);
+ }
+ handleClick(url: string) {
+ this.props.history.push(url);
+ }
+ render() {
+ return ;
+ }
+ };
+const Dashboard = withDashboard(DashboardBranch);
+export default withRouter(Dashboard as any) as any;
diff --git a/src/Dashboard/DashboardBranch.jsx b/src/Dashboard/DashboardBranch.jsx
deleted file mode 100644
index 3729714..0000000
--- a/src/Dashboard/DashboardBranch.jsx
+++ /dev/null
@@ -1,43 +0,0 @@
-import React from 'react'
-import PropTypes from 'prop-types'
-import { Typography, withStyles } from '@material-ui/core'
-
-import DashboardGroup from './DashboardGroup'
-
-const styles = theme => ({
- root: {
- flexGrow: 1,
- maxWidth: theme.spacing(140),
- marginLeft: 'auto',
- marginRight: 'auto',
- },
- headline: {
- marginTop: theme.spacing(3),
- marginBottom: theme.spacing(2),
- },
-})
-
-const DashboardBranch = ({ data, onClick, classes }) => (
-
-
- {data.title}
- {data.description}
-
-
- {data.groups ? data.groups.map(group => (
-
- )) : null}
-
-)
-
-DashboardBranch.propTypes = {
- data: PropTypes.objectOf(PropTypes.any).isRequired,
- onClick: PropTypes.func.isRequired,
- classes: PropTypes.objectOf(PropTypes.string).isRequired,
-}
-
-export default withStyles(styles)(DashboardBranch)
diff --git a/src/Dashboard/DashboardBranch.tsx b/src/Dashboard/DashboardBranch.tsx
new file mode 100644
index 0000000..cc467f7
--- /dev/null
+++ b/src/Dashboard/DashboardBranch.tsx
@@ -0,0 +1,56 @@
+import React from "react";
+import { Typography, withStyles } from "@material-ui/core";
+import DashboardGroup from "./DashboardGroup";
+const styles = (theme: any) => ({
+ root: {
+ flexGrow: 1,
+ maxWidth: theme.spacing(140),
+ marginLeft: "auto",
+ marginRight: "auto",
+ },
+ headline: {
+ marginTop: theme.spacing(3),
+ marginBottom: theme.spacing(2),
+ },
+});
+
+interface Groups {
+ id: string;
+ title: string;
+ cards: any;
+}
+
+type DashboardBranchProps = {
+ data: {
+ groups: Groups[];
+ [key: string]: any;
+ };
+ onClick: (...args: any[]) => any;
+ classes: {
+ [key: string]: string;
+ };
+};
+
+const DashboardBranch: React.SFC = ({
+ data,
+ onClick,
+ classes,
+}) => (
+
+
+ {data.title}
+ {data.description}
+
+
+ {data.groups
+ ? data.groups.map((group) => (
+
+ ))
+ : null}
+
+);
+export default withStyles(styles)(DashboardBranch);
diff --git a/src/Dashboard/DashboardCard.test.jsx b/src/Dashboard/DashboardCard.test.jsx
deleted file mode 100644
index 8437ebb..0000000
--- a/src/Dashboard/DashboardCard.test.jsx
+++ /dev/null
@@ -1,29 +0,0 @@
-import React from 'react'
-import { shallow } from 'enzyme'
-
-import DashboardCard from './DashboardCard'
-
-it('renders correctly', () => {
- const tree = shallow( { }}
- />)
-
- expect(tree).toMatchSnapshot()
-})
-
-
-it('renders disabled', () => {
- const tree = shallow((
- { }}
- isDisabled
- />
- ))
-
-
- expect(tree).toMatchSnapshot()
-})
diff --git a/src/Dashboard/DashboardCard.test.tsx b/src/Dashboard/DashboardCard.test.tsx
new file mode 100644
index 0000000..493582f
--- /dev/null
+++ b/src/Dashboard/DashboardCard.test.tsx
@@ -0,0 +1,25 @@
+import React from "react";
+import { shallow } from "enzyme";
+
+import DashboardCard from "./DashboardCard";
+
+it("renders correctly", () => {
+ const tree = shallow(
+ {}} />,
+ );
+
+ expect(tree).toMatchSnapshot();
+});
+
+it("renders disabled", () => {
+ const tree = shallow(
+ {}}
+ isDisabled
+ />,
+ );
+
+ expect(tree).toMatchSnapshot();
+});
diff --git a/src/Dashboard/DashboardCard.jsx b/src/Dashboard/DashboardCard.tsx
similarity index 58%
rename from src/Dashboard/DashboardCard.jsx
rename to src/Dashboard/DashboardCard.tsx
index b9936d3..bdb9f3a 100644
--- a/src/Dashboard/DashboardCard.jsx
+++ b/src/Dashboard/DashboardCard.tsx
@@ -1,26 +1,23 @@
-import React from 'react'
-import PropTypes from 'prop-types'
-import classNames from 'classnames'
-
+import React from "react";
+import classNames from "classnames";
import {
Card,
CardContent,
Typography,
withStyles,
Avatar,
-} from '@material-ui/core'
-
-import DisabledIcon from '@material-ui/icons/Lock'
+} from "@material-ui/core";
+import DisabledIcon from "@material-ui/icons/Lock";
-const styles = theme => ({
+const styles = (theme: any) => ({
root: {
- cursor: 'pointer',
+ cursor: "pointer",
},
content: {
paddingBottom: `${theme.spacing(2)}px !important`,
},
avatar: {
- float: 'left',
+ float: "left",
marginRight: theme.spacing(2),
backgroundColor: theme.palette.secondary.dark,
},
@@ -29,16 +26,27 @@ const styles = theme => ({
},
disabled: {
opacity: 0.75,
- pointerEvents: 'none',
- filter: 'grayscale(30%)',
+ pointerEvents: "none",
+ filter: "grayscale(30%)",
},
icon: {
- float: 'right',
+ float: "right",
opacity: 0.5,
},
-})
+});
+
+type DashboardCardProps = {
+ title: string;
+ description?: string;
+ handleClick: (...args: any[]) => any;
+ icon?: (...args: any[]) => any;
+ isDisabled?: boolean;
+ classes: {
+ [key: string]: string;
+ };
+};
-const DashboardCard = ({
+const DashboardCard: React.SFC = ({
title,
description,
handleClick,
@@ -49,14 +57,12 @@ const DashboardCard = ({
const rootClasses = classNames({
[classes.root]: true,
[classes.disabled]: isDisabled,
- })
+ });
return (
- {isDisabled ? (
-
- ) : null}
+ {isDisabled ? : null}
{Icon ? (
@@ -73,22 +79,10 @@ const DashboardCard = ({
) : null}
- )
-}
-
-DashboardCard.propTypes = {
- title: PropTypes.string.isRequired,
- description: PropTypes.string,
- handleClick: PropTypes.func.isRequired,
- icon: PropTypes.func,
- isDisabled: PropTypes.bool,
- classes: PropTypes.objectOf(PropTypes.string).isRequired,
-}
-
+ );
+};
DashboardCard.defaultProps = {
- description: null,
- icon: null,
isDisabled: false,
-}
+};
-export default withStyles(styles)(DashboardCard)
+export default withStyles(styles as any)(DashboardCard);
diff --git a/src/Dashboard/DashboardGroup.jsx b/src/Dashboard/DashboardGroup.jsx
deleted file mode 100644
index 86e0722..0000000
--- a/src/Dashboard/DashboardGroup.jsx
+++ /dev/null
@@ -1,57 +0,0 @@
-import React from 'react'
-import PropTypes from 'prop-types'
-
-import {
- Grid,
- Typography,
- withStyles,
-} from '@material-ui/core'
-
-import DashboardCard from './DashboardCard'
-
-const styles = theme => ({
- headline: {
- marginTop: theme.spacing(3),
- marginBottom: theme.spacing(2),
- },
-})
-
-const DashboardGroup = ({
- title,
- cards,
- onClick,
- classes,
-}) => (
-
-
-
- {title}
-
-
-
-
-
- {cards ? cards.map(card => (
-
- onClick(card.link)}
- icon={card.icon}
- isDisabled={card.isDisabled}
- />
-
- )) : null}
-
-
-
-)
-
-DashboardGroup.propTypes = {
- title: PropTypes.string.isRequired,
- cards: PropTypes.arrayOf(PropTypes.object).isRequired,
- onClick: PropTypes.func.isRequired,
- classes: PropTypes.objectOf(PropTypes.string).isRequired,
-}
-
-export default withStyles(styles)(DashboardGroup)
diff --git a/src/Dashboard/DashboardGroup.test.jsx b/src/Dashboard/DashboardGroup.test.jsx
deleted file mode 100644
index a152deb..0000000
--- a/src/Dashboard/DashboardGroup.test.jsx
+++ /dev/null
@@ -1,17 +0,0 @@
-import React from 'react'
-import { shallow } from 'enzyme'
-
-import DashboardGroup from './DashboardGroup'
-
-it('renders correctly', () => {
- const tree = shallow( {}}
- />)
-
- expect(tree).toMatchSnapshot()
-})
diff --git a/src/Dashboard/DashboardGroup.test.tsx b/src/Dashboard/DashboardGroup.test.tsx
new file mode 100644
index 0000000..6e77345
--- /dev/null
+++ b/src/Dashboard/DashboardGroup.test.tsx
@@ -0,0 +1,21 @@
+import React from "react";
+import { shallow } from "enzyme";
+
+import DashboardGroup from "./DashboardGroup";
+
+it("renders correctly", () => {
+ const tree = shallow(
+ {}}
+ />,
+ );
+
+ expect(tree).toMatchSnapshot();
+});
diff --git a/src/Dashboard/DashboardGroup.tsx b/src/Dashboard/DashboardGroup.tsx
new file mode 100644
index 0000000..d0b0289
--- /dev/null
+++ b/src/Dashboard/DashboardGroup.tsx
@@ -0,0 +1,48 @@
+import React from "react";
+import { Grid, Typography, withStyles } from "@material-ui/core";
+import DashboardCard from "./DashboardCard";
+const styles = (theme: any) => ({
+ headline: {
+ marginTop: theme.spacing(3),
+ marginBottom: theme.spacing(2),
+ },
+});
+
+type DashboardGroupProps = {
+ title: string;
+ cards: Record[];
+ onClick: (...args: any[]) => any;
+ classes: {
+ [key: string]: string;
+ };
+};
+const DashboardGroup: React.SFC = ({
+ title,
+ cards,
+ onClick,
+ classes,
+}) => (
+
+
+ {title}
+
+
+
+ {cards
+ ? cards.map((card) => (
+
+ onClick(card.link)}
+ icon={card.icon}
+ isDisabled={card.isDisabled}
+ />
+
+ ))
+ : null}
+
+
+);
+
+export default withStyles(styles)(DashboardGroup);
diff --git a/src/Dashboard/index.js b/src/Dashboard/index.ts
similarity index 100%
rename from src/Dashboard/index.js
rename to src/Dashboard/index.ts
diff --git a/src/Drawer/Drawer.test.jsx b/src/Drawer/Drawer.test.jsx
deleted file mode 100644
index 33a933f..0000000
--- a/src/Drawer/Drawer.test.jsx
+++ /dev/null
@@ -1,17 +0,0 @@
-import React from 'react'
-import { shallow } from 'enzyme'
-
-import menuData from '../tests/data/menu'
-
-import Drawer from '.'
-
-it('renders correctly', () => {
- const tree = shallow( {}}
- redirectTo={() => {}}
- />)
-
- expect(tree).toMatchSnapshot()
-})
diff --git a/src/Drawer/Drawer.test.tsx b/src/Drawer/Drawer.test.tsx
new file mode 100644
index 0000000..18b143f
--- /dev/null
+++ b/src/Drawer/Drawer.test.tsx
@@ -0,0 +1,14 @@
+import React from "react";
+import { shallow } from "enzyme";
+
+import menuData from "../tests/data/menu";
+
+import Drawer from ".";
+
+it("renders correctly", () => {
+ const tree = shallow(
+ {}} redirectTo={() => {}} />,
+ );
+
+ expect(tree).toMatchSnapshot();
+});
diff --git a/src/Drawer/Drawer.jsx b/src/Drawer/Drawer.tsx
similarity index 50%
rename from src/Drawer/Drawer.jsx
rename to src/Drawer/Drawer.tsx
index e078703..539c7c1 100644
--- a/src/Drawer/Drawer.jsx
+++ b/src/Drawer/Drawer.tsx
@@ -1,34 +1,40 @@
-import React from 'react'
-import PropTypes from 'prop-types'
-
+import React from "react";
import {
Drawer as MaterialDrawer,
Divider,
IconButton,
withStyles,
-} from '@material-ui/core'
-import ChevronLeftIcon from '@material-ui/icons/ChevronLeft'
-
-import Menu from '../Menu'
-
-const drawerWidth = 280
-
-const styles = theme => ({
+} from "@material-ui/core";
+import ChevronLeftIcon from "@material-ui/icons/ChevronLeft";
+import Menu from "../Menu";
+import { MenuDataItem } from "../Menu/Menu";
+const drawerWidth = 280;
+const styles = (theme: any) => ({
drawerPaper: {
- position: 'relative',
- height: '100%',
+ position: "relative",
+ height: "100%",
width: drawerWidth,
},
drawerHeader: {
- display: 'flex',
- alignItems: 'center',
- justifyContent: 'flex-end',
- padding: '0 8px',
+ display: "flex",
+ alignItems: "center",
+ justifyContent: "flex-end",
+ padding: "0 8px",
height: theme.spacing(8),
},
-})
+});
+
+type DrawerProps = {
+ data: MenuDataItem[];
+ isOpen?: boolean;
+ onClose: (...args: any[]) => any;
+ redirectTo: (...args: any[]) => any;
+ classes: {
+ [key: string]: string;
+ };
+};
-const Drawer = ({
+const Drawer: React.SFC = ({
data,
isOpen,
onClose,
@@ -55,19 +61,8 @@ const Drawer = ({
-)
-
-
-Drawer.propTypes = {
- data: PropTypes.arrayOf(PropTypes.object).isRequired,
- isOpen: PropTypes.bool,
- onClose: PropTypes.func.isRequired,
- redirectTo: PropTypes.func.isRequired,
- classes: PropTypes.objectOf(PropTypes.string).isRequired,
-}
-
+);
Drawer.defaultProps = {
isOpen: false,
-}
-
-export default withStyles(styles)(Drawer)
+};
+export default withStyles(styles as any)(Drawer);
diff --git a/src/Drawer/index.jsx b/src/Drawer/index.jsx
deleted file mode 100644
index 0451052..0000000
--- a/src/Drawer/index.jsx
+++ /dev/null
@@ -1,3 +0,0 @@
-import Drawer from './Drawer'
-
-export default Drawer
diff --git a/src/Drawer/index.tsx b/src/Drawer/index.tsx
new file mode 100644
index 0000000..e720d38
--- /dev/null
+++ b/src/Drawer/index.tsx
@@ -0,0 +1,3 @@
+import Drawer from "./Drawer";
+
+export default Drawer;
diff --git a/src/Form/Form.jsx b/src/Form/Form.jsx
deleted file mode 100644
index 35f9e7f..0000000
--- a/src/Form/Form.jsx
+++ /dev/null
@@ -1,169 +0,0 @@
-import React from 'react'
-import PropTypes from 'prop-types'
-
-import FormBranch from './FormBranch'
-import isValid from './isValid'
-
-const withForm = Component => class Form extends React.Component {
- static propTypes = {
- form: PropTypes.arrayOf(PropTypes.object).isRequired,
- data: PropTypes.objectOf(PropTypes.any).isRequired,
- onDataChanged: PropTypes.func,
- onSubmit: PropTypes.func.isRequired,
- submitText: PropTypes.string,
- isFixedSubmitButton: PropTypes.bool,
- }
-
- static defaultProps = {
- onDataChanged: () => {},
- submitText: 'Save',
- isFixedSubmitButton: false,
- }
-
- constructor(props, defaultProps) {
- super(props, defaultProps)
-
- this.state = {
- data: {},
- loading: false,
- }
-
- this.fields = {}
- this.timer = undefined
-
- this.handleSubmit = this.handleSubmit.bind(this)
- this.updateFieldData = this.updateFieldData.bind(this)
- }
-
- componentDidMount() {
- const { form, data } = this.props
- this.generateFields(form, data)
- this.generateMissingData(data)
-
- this.setState({
- data: this.fields,
- })
- }
-
- componentWillReceiveProps({ form, data }) {
- this.generateFields(form, data)
- this.generateMissingData(data)
-
- this.setState({
- data: this.fields,
- })
- }
-
- componentWillUnmount() {
- clearTimeout(this.timer)
- }
-
- static getInitialField(field, data) {
- let valueName = data[field.id] && data[field.id].value
- let submitValue = valueName
-
- if (!valueName && field.value !== undefined) {
- valueName = field.value
- }
-
- if (typeof field.beforeSubmit === 'function') {
- submitValue = field.beforeSubmit(submitValue)
- }
-
- return {
- value: valueName,
- submitValue,
- error: !isValid(field.type, field.isRequired, field.validators, submitValue),
- }
- }
-
- generateFields(fieldset, data) {
- fieldset.forEach((field) => {
- if (field.group) {
- this.generateFields(field.data, data)
- return
- }
-
- this.fields[field.id] = Form.getInitialField(field, data)
- })
- }
-
- generateMissingData(data) {
- Object.keys(data).forEach((key) => {
- if (this.fields[key]) {
- return
- }
-
- this.fields[key] = data[key]
- })
- }
-
- handleSubmit() {
- const {
- data,
- loading,
- } = this.state
- const { onSubmit } = this.props
-
- const errors = Object.values(data).map(field => field.error)
-
- if (errors.indexOf(true) > -1) {
- this.setState({
- error: true,
- })
-
- return
- }
-
- if (!loading) {
- this.setState(
- {
- loading: true,
- error: false,
- },
- () => {
- this.timer = setTimeout(() => {
- this.setState({
- loading: false,
- })
- }, 1000)
- },
- )
- }
-
- onSubmit(data)
- }
-
- updateFieldData(fieldId, value, submitValue, error) {
- const { onDataChanged } = this.props
- const { data: stateData } = this.state
- const data = {
- ...stateData,
- }
-
- data[fieldId] = {
- value,
- submitValue,
- error,
- }
-
- onDataChanged(data)
-
- this.setState({
- data,
- })
- }
-
- render() {
- return (
-
- )
- }
-}
-
-export default withForm(FormBranch)
diff --git a/src/Form/Form.test.jsx b/src/Form/Form.test.jsx
deleted file mode 100644
index 0e9655e..0000000
--- a/src/Form/Form.test.jsx
+++ /dev/null
@@ -1,52 +0,0 @@
-import React from 'react'
-import { MemoryRouter } from 'react-router-dom'
-import { shallow } from 'enzyme'
-
-import Form from '.'
-
-import formData from '../tests/data/form'
-
-it('renders correctly', () => {
- const tree = shallow((
-
-
-
- ))
-
- expect(tree).toMatchSnapshot()
-})
-
-it('renders correctly with content', () => {
- const tree = shallow((
-
-
-
- ))
-
- expect(tree).toMatchSnapshot()
-})
diff --git a/src/Form/Form.test.tsx b/src/Form/Form.test.tsx
new file mode 100644
index 0000000..266fa62
--- /dev/null
+++ b/src/Form/Form.test.tsx
@@ -0,0 +1,52 @@
+import React from "react";
+import { MemoryRouter } from "react-router-dom";
+import { shallow } from "enzyme";
+
+import Form from ".";
+
+import formData from "../tests/data/form";
+
+it("renders correctly", () => {
+ const tree = shallow(
+
+
+ ,
+ );
+
+ expect(tree).toMatchSnapshot();
+});
+
+it("renders correctly with content", () => {
+ const tree = shallow(
+
+
+ ,
+ );
+
+ expect(tree).toMatchSnapshot();
+});
diff --git a/src/Form/Form.tsx b/src/Form/Form.tsx
new file mode 100644
index 0000000..4b3cfcf
--- /dev/null
+++ b/src/Form/Form.tsx
@@ -0,0 +1,154 @@
+import React from "react";
+import FormBranch from "./FormBranch";
+import isValid from "./isValid";
+
+type FormProps = {
+ form: object[];
+ data: {
+ [key: string]: any;
+ };
+ onDataChanged?: (...args: any[]) => any;
+ onSubmit: (...args: any[]) => any;
+ submitText?: string;
+ isFixedSubmitButton?: boolean;
+};
+
+type FormState = {
+ loading: boolean;
+ error?: boolean;
+ data: {} | any;
+};
+
+const withForm = (Component: any) =>
+ class Form extends React.Component {
+ private fields: any;
+ private timer: any;
+
+ constructor(props: FormProps, defaultProps: any) {
+ super(props, defaultProps);
+
+ this.state = {
+ data: {},
+ loading: false,
+ };
+ this.fields = {};
+ this.timer = undefined;
+ this.handleSubmit = this.handleSubmit.bind(this);
+ this.updateFieldData = this.updateFieldData.bind(this);
+ }
+ componentDidMount() {
+ const { form, data } = this.props;
+ this.generateFields(form, data);
+ this.generateMissingData(data);
+ this.setState({
+ data: this.fields,
+ });
+ }
+ UNSAFE_componentWillReceiveProps({ form, data }: FormProps) {
+ this.generateFields(form, data);
+ this.generateMissingData(data);
+ this.setState({
+ data: this.fields,
+ });
+ }
+ componentWillUnmount() {
+ clearTimeout(this.timer);
+ }
+
+ static getInitialField(field: any, data: any) {
+ let valueName = data[field.id] && data[field.id].value;
+ let submitValue = valueName;
+ if (!valueName && field.value !== undefined) {
+ valueName = field.value;
+ }
+ if (typeof field.beforeSubmit === "function") {
+ submitValue = field.beforeSubmit(submitValue);
+ }
+ return {
+ value: valueName,
+ submitValue,
+ error: !isValid(
+ field.type,
+ field.isRequired,
+ field.validators,
+ submitValue,
+ ),
+ };
+ }
+ generateFields(fieldset: any[], data: any) {
+ fieldset.forEach((field) => {
+ if (field.group) {
+ this.generateFields(field.data, data);
+ return;
+ }
+ this.fields[field.id] = Form.getInitialField(field, data);
+ });
+ }
+ generateMissingData(data: any) {
+ Object.keys(data).forEach((key) => {
+ if (this.fields[key]) {
+ return;
+ }
+ this.fields[key] = data[key];
+ });
+ }
+ handleSubmit() {
+ const { data, loading } = this.state;
+ const { onSubmit } = this.props;
+ const errors = Object.values(data).map((field: any) => field.error);
+ if (errors.indexOf(true) > -1) {
+ this.setState({
+ error: true,
+ });
+ return;
+ }
+ if (!loading) {
+ this.setState(
+ {
+ loading: true,
+ error: false,
+ },
+ () => {
+ this.timer = setTimeout(() => {
+ this.setState({
+ loading: false,
+ });
+ }, 1000);
+ },
+ );
+ }
+ onSubmit(data);
+ }
+
+ updateFieldData(fieldId: string, value: any, submitValue: any, error: any) {
+ const { onDataChanged } = this.props;
+ const { data: stateData } = this.state;
+ const data = {
+ ...stateData,
+ };
+ data[fieldId] = {
+ value,
+ submitValue,
+ error,
+ };
+
+ onDataChanged && onDataChanged(data);
+
+ this.setState({
+ data,
+ });
+ }
+
+ render() {
+ return (
+
+ );
+ }
+ };
+
+export default withForm(FormBranch);
diff --git a/src/Form/FormBranch.jsx b/src/Form/FormBranch.jsx
deleted file mode 100644
index a865951..0000000
--- a/src/Form/FormBranch.jsx
+++ /dev/null
@@ -1,150 +0,0 @@
-import React from 'react'
-import PropTypes from 'prop-types'
-import {
- Typography,
- withStyles,
-} from '@material-ui/core'
-
-import FormGroupWrapper from './FormGroupWrapper'
-import FormField from './FormField'
-import FormSubmitButton from './FormSubmitButton'
-
-const styles = theme => ({
- title: {
- marginLeft: theme.spacing(),
- marginRight: theme.spacing(),
- },
- errorMessage: {
- marginTop: theme.spacing(2),
- marginBottom: theme.spacing(2),
- color: theme.palette.error.main,
- },
-})
-
-const Element = ({ useFormElement, ...props }) => {
- if (useFormElement) {
- return (
-
- )
- }
-
- return (
-
- )
-}
-
-Element.propTypes = {
- useFormElement: PropTypes.bool.isRequired,
-}
-
-const FormBranch = ({
- form,
- data,
- loading,
- useFormElement,
- error,
- errorMessage,
- isFixedSubmitButton,
- updateFieldData,
- handleSubmit,
- submitText,
- children,
- classes,
-}) => {
- const renderField = (field, index) => {
- let valueName = data[field.id] && data[field.id].value
-
- if (!valueName) {
- valueName = field.value
- }
-
- return (
-
- )
- }
-
- const generateFields = formData => formData.map((field, index) => {
- if (field.group) {
- return (
-
- {field.title ? (
-
- {field.title}
-
- ) : null}
-
- {generateFields(field.data, field)}
-
- )
- }
-
- return renderField(field, index)
- })
-
- const elements = generateFields(form)
-
- return (
-
- {elements}
-
- {children}
-
- {error && (
-
- {errorMessage}
-
- )}
-
-
- {submitText}
-
-
- )
-}
-
-FormBranch.propTypes = {
- form: PropTypes.arrayOf(PropTypes.object).isRequired,
- data: PropTypes.objectOf(PropTypes.object),
- loading: PropTypes.bool.isRequired,
- useFormElement: PropTypes.bool,
- isFixedSubmitButton: PropTypes.bool,
- error: PropTypes.bool,
- errorMessage: PropTypes.string,
- updateFieldData: PropTypes.func,
- handleSubmit: PropTypes.func,
- submitText: PropTypes.string,
- children: PropTypes.node,
- classes: PropTypes.objectOf(PropTypes.string).isRequired,
-}
-
-FormBranch.defaultProps = {
- data: {},
- useFormElement: true,
- isFixedSubmitButton: false,
- error: false,
- errorMessage: 'An error occured. Please fill out all required fields correctly.',
- updateFieldData: () => {},
- handleSubmit: () => {},
- submitText: 'Save',
- children: null,
-}
-
-export default withStyles(styles)(FormBranch)
diff --git a/src/Form/FormBranch.tsx b/src/Form/FormBranch.tsx
new file mode 100644
index 0000000..61dc896
--- /dev/null
+++ b/src/Form/FormBranch.tsx
@@ -0,0 +1,148 @@
+import React from "react";
+import { Typography, withStyles } from "@material-ui/core";
+import FormGroupWrapper from "./FormGroupWrapper";
+import FormField from "./FormField";
+import FormSubmitButton from "./FormSubmitButton";
+
+const styles = (theme: any) => ({
+ title: {
+ marginLeft: theme.spacing(),
+ marginRight: theme.spacing(),
+ },
+ errorMessage: {
+ marginTop: theme.spacing(2),
+ marginBottom: theme.spacing(2),
+ color: theme.palette.error.main,
+ },
+});
+
+type FormBranchProps = {
+ form: object[];
+ data?: {
+ [key: string]: Record;
+ };
+ loading: boolean;
+ useFormElement?: boolean;
+ isFixedSubmitButton?: boolean;
+ error?: boolean;
+ errorMessage?: string;
+ updateFieldData?: (...args: any[]) => any;
+ handleSubmit?: (...args: any[]) => any;
+ submitText?: string;
+ classes: {
+ [key: string]: string;
+ };
+};
+
+type ElementProps = {
+ useFormElement?: boolean;
+ noValidate: boolean;
+ autoComplete: string;
+};
+
+const Element: React.SFC = ({ useFormElement, ...props }) => {
+ if (useFormElement) {
+ return ;
+ }
+
+ return ;
+};
+
+const FormBranch: React.SFC = ({
+ form,
+ data,
+ loading,
+ useFormElement,
+ error,
+ errorMessage,
+ isFixedSubmitButton,
+ updateFieldData,
+ handleSubmit,
+ submitText,
+ children,
+ classes,
+}) => {
+ if (!data) {
+ return null;
+ }
+
+ const renderField = (field: any, index: number) => {
+ let valueName = data[field.id] && data[field.id].value;
+ if (!valueName) {
+ valueName = field.value;
+ }
+ return (
+
+ );
+ };
+
+ const generateFields = (formData: any) =>
+ formData.map((field: any, index: number) => {
+ if (field.group) {
+ return (
+
+ {field.title ? (
+
+ {field.title}
+
+ ) : null}
+
+ {generateFields(field.data)}
+
+ );
+ }
+ return renderField(field, index);
+ });
+
+ const elements = generateFields(form);
+
+ return (
+
+ {elements}
+
+ {children}
+
+ {error && (
+
+ {errorMessage}
+
+ )}
+
+
+ {submitText}
+
+
+ );
+};
+
+FormBranch.defaultProps = {
+ data: {},
+ useFormElement: true,
+ isFixedSubmitButton: false,
+ error: false,
+ errorMessage:
+ "An error occured. Please fill out all required fields correctly.",
+ updateFieldData: () => {},
+ handleSubmit: () => {},
+ submitText: "Save",
+};
+
+export default withStyles(styles)(FormBranch);
diff --git a/src/Form/FormField.jsx b/src/Form/FormField.jsx
deleted file mode 100644
index a1d230e..0000000
--- a/src/Form/FormField.jsx
+++ /dev/null
@@ -1,222 +0,0 @@
-import React from 'react'
-import PropTypes from 'prop-types'
-
-import { TYPES } from './constants'
-import FormFieldBranch from './FormFieldBranch'
-import isValid from './isValid'
-
-const withFormField = Component => class FormField extends React.Component {
- static propTypes = {
- id: PropTypes.string.isRequired,
- type: PropTypes.string,
- helperText: PropTypes.string,
- validators: PropTypes.arrayOf(PropTypes.oneOfType([
- PropTypes.string,
- PropTypes.func,
- PropTypes.shape({
- validator: PropTypes.oneOfType([
- PropTypes.string,
- PropTypes.func,
- ]),
- message: PropTypes.string,
- }),
- ])),
- isRequired: PropTypes.bool,
- handleChange: PropTypes.func.isRequired,
- getAdditionalValue: PropTypes.func,
- beforeSubmit: PropTypes.func,
- }
-
- static defaultProps = {
- type: 'text',
- validators: [],
- isRequired: false,
- helperText: '',
- getAdditionalValue: data => data,
- beforeSubmit: null,
- }
-
- static getCompleteFrom(completeFrom = []) {
- return completeFrom.map((option) => {
- let { title } = option
-
- if (!option.title) {
- title = option
- }
-
- return {
- title,
- tooltip: option.tooltip,
- }
- })
- }
-
- constructor(props) {
- super(props)
-
- this.handleChange = this.handleChange.bind(this)
- this.initialize = this.initialize.bind(this)
- this.handleAddListItem = this.handleAddListItem.bind(this)
- this.handleRemoveListItem = this.handleRemoveListItem.bind(this)
- }
-
- state = {
- listItems: [],
- value: '',
- isDirty: false,
- completeFrom: [],
- messages: [],
- }
-
- componentWillMount() {
- this.initialize(this.props)
- }
-
- componentWillReceiveProps(nextProps) {
- this.initialize(nextProps)
- }
-
- getAdditionalValue(value) {
- if (typeof this.props.getAdditionalValue === 'function') {
- return this.props.getAdditionalValue(value)
- }
-
- return value
- }
-
- getList({ value, completeFrom }) {
- let listItems = this.state.listItems.map(item => item.title)
-
- if (this.state.isDirty === false) {
- if (value && value.constructor === Array) {
- listItems = [
- ...value,
- ]
- } else {
- listItems = []
- }
- }
-
- const allCompleteFrom = FormField.getCompleteFrom(completeFrom)
- const transformedListItems = this.getAdditionalValue(listItems)
-
- listItems = transformedListItems.map((selectedValue) => {
- if (allCompleteFrom.length > 0) {
- return allCompleteFrom.filter(option => option.title === selectedValue)[0]
- }
-
- return {
- title: selectedValue,
- }
- })
-
- return {
- completeFrom: allCompleteFrom,
- value: '',
- isDirty: true,
- listItems,
- }
- }
-
- initialize(props) {
- const isList = props.type === 'list'
- let state = {}
-
- if (isList) {
- state = this.getList(props)
- } else {
- state = {
- value: this.getAdditionalValue(props.value),
- }
- }
-
- this.setState(state)
- }
-
- isValid(value) {
- const {
- type,
- isRequired,
- validators,
- } = this.props
-
- return isValid(type, isRequired, validators, value)
- }
-
- handleChange(fieldId) {
- return ((event) => {
- let newValue
-
- if (event.target) {
- newValue = event.target.value
-
- // eslint-disable-next-line no-underscore-dangle
- } else if (event._isAMomentObject) {
- newValue = event.valueOf()
- }
-
- let submitValue = newValue
-
- if (typeof this.props.beforeSubmit === 'function') {
- submitValue = this.props.beforeSubmit(submitValue)
- }
- const isValidWithMessages = this.isValid(submitValue)
- const error = !isValidWithMessages.isValid
-
- if (this.props.type === TYPES.NUMBER) {
- newValue = parseFloat(newValue, 10)
- }
-
- this.setState({
- value: newValue,
- messages: isValidWithMessages.messages || [],
- error,
- })
-
- this.props.handleChange(fieldId, newValue, submitValue, error)
- })
- }
-
- handleAddListItem(option) {
- this.setState({
- listItems: [...this.state.listItems, option],
- })
- }
-
- handleRemoveListItem(option) {
- const listItems = [...this.state.listItems]
- const flatListItems = listItems.map(item => item.title)
- const index = flatListItems.indexOf(option.title)
-
- if (index > -1) {
- listItems.splice(index, 1)
- }
-
- this.setState({
- listItems,
- })
- }
-
- render() {
- const { helperText, ...props } = this.props
- const { messages, ...state } = this.state
- let helperMessages = []
-
- if (helperText) {
- helperMessages = [helperText, ...messages]
- }
-
- return (
-
- )
- }
-}
-
-export default withFormField(FormFieldBranch)
diff --git a/src/Form/FormField.test.jsx b/src/Form/FormField.test.jsx
deleted file mode 100644
index 8377768..0000000
--- a/src/Form/FormField.test.jsx
+++ /dev/null
@@ -1,249 +0,0 @@
-import React from 'react'
-import Enzyme, { shallow } from 'enzyme'
-import Adapter from 'enzyme-adapter-react-16'
-import { ListItem, Chip } from '@material-ui/core'
-
-import FormField from './FormField'
-import FormFieldInput from './FormFieldInput'
-
-Enzyme.configure({ adapter: new Adapter() })
-
-describe('Form Field', () => {
- it('handles change', () => {
- const handleChange = jest.fn()
- const field = shallow((
-
- ))
-
- field.find('input').simulate('change', { value: 'a' })
-
- expect(handleChange).toHaveBeenCalled()
- })
-
- it('renders non-visible', () => {
- const field = shallow((
- { }}
- isVisible={false}
- width="small"
- />
- ))
- expect(field.find(FormFieldInput).props().classNames.join(' '))
- .toEqual(expect.stringMatching(/hidden/))
- })
-
- it('renders calls beforeSubmit function, handles number', () => {
- const beforeSubmit = jest.fn()
- const field = shallow((
- { }}
- beforeSubmit={beforeSubmit}
- width="mid"
- />
- ))
-
- field.find('input').simulate('change', { value: 1 })
-
- expect(beforeSubmit).toHaveBeenCalled()
- })
-
- it('handles list', () => {
- const field = shallow((
- { }}
- />
- ))
-
- expect(field.state().listItems).toEqual([])
-
- field.find('input').simulate('change', { target: { value: 'foo' } })
- field.find('input').simulate('keyPress', { which: 13 })
-
- expect(field.state().listItems).toEqual([{ title: 'foo' }])
- })
-
- it('handles list with allowed values', () => {
- const field = shallow((
- { }}
- completeFrom={['foo']}
- />
- ))
-
- expect(field.state().listItems).toEqual([])
-
- field.find('input').simulate('change', { target: { value: '_' } })
- field.find('input').simulate('keyPress', { which: 13 })
-
- expect(field.state().listItems).toEqual([])
-
- field.find('input').simulate('change', { target: { value: 'foo' } })
- field.find(ListItem).at(0).simulate('click')
-
- expect(field.state().listItems[0].title).toEqual('foo')
- })
-
- it('handles list delete', () => {
- const field = shallow((
- { }}
- completeFrom={[{
- title: 'foo',
- tooltip: 'bar',
- }]}
- />
- ))
-
- field.find('input').simulate('change', { target: { value: 'foo' } })
- field.find(ListItem).at(0).simulate('click')
-
- expect(field.state().listItems[0].title).toEqual('foo')
- field.find(Chip).at(0).find('svg').simulate('click')
- })
-
- it('renders list item via function', () => {
- const renderElement = jest.fn()
- const field = shallow((
- { }}
- renderElement={renderElement}
- />
- ))
-
- field.find('input').simulate('change', { target: { value: 'foo' } })
- field.find('input').simulate('keyPress', { which: 13 })
-
- expect(renderElement).toHaveBeenCalled()
- })
-
- it('does not render if there is no option', () => {
- const field = shallow((
- { }}
- />
- ))
-
- field.setState({
- listItems: [null],
- })
-
- expect(field.find(Chip).length).toEqual(0)
- })
-
- it('handles datetime field', () => {
- const field = shallow((
- { }}
- />
- ))
- const newDate = +new Date()
-
- field.instance().handleChange('6')({
- _isAMomentObject: true,
- valueOf: () => newDate,
- })
-
- expect(field.state().value).toEqual(newDate)
- })
-
- it('handles date field', () => {
- const field = shallow((
- { }}
- />
- ))
- const newDate = +new Date()
-
- field.instance().handleChange('6')({
- _isAMomentObject: true,
- valueOf: () => newDate,
- })
-
- expect(field.state().value).toEqual(newDate)
- })
-
- it('handles time field', () => {
- const field = shallow((
- { }}
- />
- ))
- const newDate = +new Date()
-
- field.instance().handleChange('6')({
- _isAMomentObject: true,
- valueOf: () => newDate,
- })
-
- expect(field.state().value).toEqual(newDate)
- })
-
- it('handle validator with message', () => {
- const handleChange = jest.fn()
- const validatorWithMessage = {
- validator: value => value === 'test',
- message: 'Value should equal `test`',
- }
-
- const field = shallow((
-
- ))
-
- field.find('input').simulate('change', { value: 'a' })
-
- const { messages } = field.instance().state
-
- expect(messages).toEqual([validatorWithMessage.message])
- })
-})
diff --git a/src/Form/FormField.test.tsx b/src/Form/FormField.test.tsx
new file mode 100644
index 0000000..969b4f3
--- /dev/null
+++ b/src/Form/FormField.test.tsx
@@ -0,0 +1,242 @@
+import React from "react";
+import Enzyme, { shallow } from "enzyme";
+import Adapter from "enzyme-adapter-react-16";
+import { ListItem, Chip } from "@material-ui/core";
+
+import FormField from "./FormField";
+import FormFieldInput from "./FormFieldInput";
+
+Enzyme.configure({ adapter: new Adapter() });
+
+describe("Form Field", () => {
+ it("handles change", () => {
+ const handleChange = jest.fn();
+ const field = shallow(
+ ,
+ );
+
+ field.find("input").simulate("change", { value: "a" });
+
+ expect(handleChange).toHaveBeenCalled();
+ });
+
+ it("renders non-visible", () => {
+ const field = shallow(
+ {}}
+ isVisible={false}
+ width="small"
+ />,
+ );
+ expect(field.find(FormFieldInput).props().classNames.join(" ")).toEqual(
+ expect.stringMatching(/hidden/),
+ );
+ });
+
+ it("renders calls beforeSubmit function, handles number", () => {
+ const beforeSubmit = jest.fn();
+ const field = shallow(
+ {}}
+ beforeSubmit={beforeSubmit}
+ width="mid"
+ />,
+ );
+
+ field.find("input").simulate("change", { value: 1 });
+
+ expect(beforeSubmit).toHaveBeenCalled();
+ });
+
+ it("handles list", () => {
+ const field = shallow(
+ {}} />,
+ );
+
+ expect(field.state().listItems).toEqual([]);
+
+ field.find("input").simulate("change", { target: { value: "foo" } });
+ field.find("input").simulate("keyPress", { which: 13 });
+
+ expect(field.state().listItems).toEqual([{ title: "foo" }]);
+ });
+
+ it("handles list with allowed values", () => {
+ const field = shallow(
+ {}}
+ completeFrom={["foo"]}
+ />,
+ );
+
+ expect(field.state().listItems).toEqual([]);
+
+ field.find("input").simulate("change", { target: { value: "_" } });
+ field.find("input").simulate("keyPress", { which: 13 });
+
+ expect(field.state().listItems).toEqual([]);
+
+ field.find("input").simulate("change", { target: { value: "foo" } });
+ field.find(ListItem).at(0).simulate("click");
+
+ expect(field.state().listItems[0].title).toEqual("foo");
+ });
+
+ it("handles list delete", () => {
+ const field = shallow(
+ {}}
+ completeFrom={[
+ {
+ title: "foo",
+ tooltip: "bar",
+ },
+ ]}
+ />,
+ );
+
+ field.find("input").simulate("change", { target: { value: "foo" } });
+ field.find(ListItem).at(0).simulate("click");
+
+ expect(field.state().listItems[0].title).toEqual("foo");
+ field.find(Chip).at(0).find("svg").simulate("click");
+ });
+
+ it("renders list item via function", () => {
+ const renderElement = jest.fn();
+ const field = shallow(
+ {}}
+ renderElement={renderElement}
+ />,
+ );
+
+ field.find("input").simulate("change", { target: { value: "foo" } });
+ field.find("input").simulate("keyPress", { which: 13 });
+
+ expect(renderElement).toHaveBeenCalled();
+ });
+
+ it("does not render if there is no option", () => {
+ const field = shallow(
+ {}} />,
+ );
+
+ field.setState({
+ listItems: [null],
+ });
+
+ expect(field.find(Chip).length).toEqual(0);
+ });
+
+ it("handles datetime field", () => {
+ const field = shallow(
+ {}}
+ />,
+ );
+ const newDate = +new Date();
+
+ field.instance().handleChange("6")({
+ _isAMomentObject: true,
+ valueOf: () => newDate,
+ });
+
+ expect(field.state().value).toEqual(newDate);
+ });
+
+ it("handles date field", () => {
+ const field = shallow(
+ {}}
+ />,
+ );
+ const newDate = +new Date();
+
+ field.instance().handleChange("6")({
+ _isAMomentObject: true,
+ valueOf: () => newDate,
+ });
+
+ expect(field.state().value).toEqual(newDate);
+ });
+
+ it("handles time field", () => {
+ const field = shallow(
+ {}}
+ />,
+ );
+ const newDate = +new Date();
+
+ field.instance().handleChange("6")({
+ _isAMomentObject: true,
+ valueOf: () => newDate,
+ });
+
+ expect(field.state().value).toEqual(newDate);
+ });
+
+ it("handle validator with message", () => {
+ const handleChange = jest.fn();
+ const validatorWithMessage = {
+ validator: (value) => value === "test",
+ message: "Value should equal `test`",
+ };
+
+ const field = shallow(
+ ,
+ );
+
+ field.find("input").simulate("change", { value: "a" });
+
+ const { messages } = field.instance().state;
+
+ expect(messages).toEqual([validatorWithMessage.message]);
+ });
+});
diff --git a/src/Form/FormField.tsx b/src/Form/FormField.tsx
new file mode 100644
index 0000000..7944498
--- /dev/null
+++ b/src/Form/FormField.tsx
@@ -0,0 +1,197 @@
+import React from "react";
+import { TYPES } from "./constants";
+import FormFieldBranch from "./FormFieldBranch";
+import isValid from "./isValid";
+
+type FormFieldProps = {
+ id: string;
+ type?: string;
+ helperText?: string;
+ validators?: (
+ | string
+ | ((...args: any[]) => any)
+ | {
+ validator?: string | ((...args: any[]) => any);
+ message?: string;
+ }
+ )[];
+ isRequired?: boolean;
+ handleChange: (...args: any[]) => any;
+ getAdditionalValue?: (...args: any[]) => any;
+ beforeSubmit?: (...args: any[]) => any;
+ value: any;
+};
+
+type FormFieldState = {
+ value: any;
+ messages: any;
+ error?: boolean;
+ listItems?: any[];
+ isDirty: boolean;
+ completeFrom: any[];
+};
+
+const withFormField = (Component: any) =>
+ class FormField extends React.Component {
+ static getCompleteFrom(completeFrom = []) {
+ return completeFrom.map((option: any) => {
+ let { title } = option;
+ if (!option.title) {
+ title = option;
+ }
+ return {
+ title,
+ tooltip: option.tooltip,
+ };
+ });
+ }
+
+ constructor(props: FormFieldProps) {
+ super(props);
+ this.handleChange = this.handleChange.bind(this);
+ this.initialize = this.initialize.bind(this);
+ this.handleAddListItem = this.handleAddListItem.bind(this);
+ this.handleRemoveListItem = this.handleRemoveListItem.bind(this);
+ }
+
+ state = {
+ listItems: [],
+ value: "",
+ isDirty: false,
+ completeFrom: [],
+ messages: [],
+ };
+
+ UNSAFE_componentWillMount() {
+ this.initialize(this.props);
+ }
+ UNSAFE_componentWillReceiveProps(nextProps: FormFieldProps) {
+ this.initialize(nextProps);
+ }
+ getAdditionalValue(value: any) {
+ if (typeof this.props.getAdditionalValue === "function") {
+ return this.props.getAdditionalValue(value);
+ }
+ return value;
+ }
+
+ getList({ value, completeFrom }: any) {
+ let listItems = this.state.listItems.map((item: any) => item.title);
+ if (this.state.isDirty === false) {
+ if (value && value.constructor === Array) {
+ listItems = [...value];
+ } else {
+ listItems = [];
+ }
+ }
+ const allCompleteFrom = FormField.getCompleteFrom(completeFrom);
+ const transformedListItems = this.getAdditionalValue(listItems);
+ listItems = transformedListItems.map((selectedValue: any) => {
+ if (allCompleteFrom.length > 0) {
+ return allCompleteFrom.filter(
+ (option) => option.title === selectedValue,
+ )[0];
+ }
+ return {
+ title: selectedValue,
+ };
+ });
+ return {
+ completeFrom: allCompleteFrom,
+ value: "",
+ isDirty: true,
+ listItems,
+ };
+ }
+
+ initialize(props: FormFieldProps) {
+ const isList = props.type === "list";
+ let state = {};
+ if (isList) {
+ state = this.getList(props);
+ } else {
+ state = {
+ value: this.getAdditionalValue(props.value),
+ };
+ }
+ this.setState(state);
+ }
+
+ isValid(value: any) {
+ const { type, isRequired, validators } = this.props;
+ return isValid(type, isRequired, validators, value);
+ }
+
+ handleChange(fieldId: string) {
+ return (event: any) => {
+ let newValue;
+
+ if (event.target) {
+ newValue = event.target.value;
+ // eslint-disable-next-line no-underscore-dangle
+ } else if (event._isAMomentObject) {
+ newValue = event.valueOf();
+ }
+
+ let submitValue = newValue;
+
+ if (typeof this.props.beforeSubmit === "function") {
+ submitValue = this.props.beforeSubmit(submitValue);
+ }
+
+ const isValidWithMessages = this.isValid(submitValue);
+ const error = !isValidWithMessages.isValid;
+
+ if (this.props.type === TYPES.NUMBER) {
+ newValue = parseFloat(newValue);
+ }
+
+ this.setState({
+ value: newValue,
+ messages: isValidWithMessages.messages || [],
+ error,
+ });
+ this.props.handleChange(fieldId, newValue, submitValue, error);
+ };
+ }
+
+ handleAddListItem(option: any) {
+ this.setState({
+ listItems: [...this.state.listItems, option],
+ });
+ }
+
+ handleRemoveListItem(option: any) {
+ const listItems = [...this.state.listItems];
+ const flatListItems = listItems.map((item: any) => item.title);
+ const index = flatListItems.indexOf(option.title);
+ if (index > -1) {
+ listItems.splice(index, 1);
+ }
+ this.setState({
+ listItems,
+ });
+ }
+
+ render() {
+ const { helperText, ...props } = this.props;
+ const { messages, ...state } = this.state;
+ let helperMessages: any[] = [];
+
+ if (helperText) {
+ helperMessages = [helperText, ...messages];
+ }
+ return (
+
+ );
+ }
+ };
+
+export default withFormField(FormFieldBranch);
diff --git a/src/Form/FormFieldBranch.jsx b/src/Form/FormFieldBranch.jsx
deleted file mode 100644
index 912d7a8..0000000
--- a/src/Form/FormFieldBranch.jsx
+++ /dev/null
@@ -1,174 +0,0 @@
-import React from 'react'
-import PropTypes from 'prop-types'
-import classnames from 'classnames'
-import { withStyles } from '@material-ui/core'
-
-import { TYPES } from './constants'
-import FormFieldInput from './FormFieldInput'
-import FormFieldDate from './FormFieldDate'
-import FormFieldList from './FormFieldList'
-import FormFieldSwitch from './FormFieldSwitch'
-import FormFieldHidden from './FormFieldHidden'
-
-const styles = theme => ({
- hidden: {
- display: 'none',
- },
-
- headline: {
- marginTop: theme.spacing(3),
- },
-
- field: {
- marginLeft: theme.spacing(),
- marginRight: theme.spacing(),
- verticalAlign: 'top',
- },
-
- fieldInline: {
- display: 'inline-block',
- },
-
- fieldDate: {
- marginTop: theme.spacing(2),
- marginBottom: theme.spacing(),
- },
-
- widthSmall: {
- width: `calc(25% - ${theme.spacing(2)}px)`,
- },
-
- widthMid: {
- width: `calc(50% - ${theme.spacing(2)}px)`,
- },
-
- widthFull: {
- width: `calc(100% - ${theme.spacing(2)}px)`,
- },
-
- divider: {
- marginTop: theme.spacing(3),
- marginBottom: theme.spacing(3),
- },
-})
-
-const FormFieldBranch = ({
- type,
- width,
- classes,
- isVisible,
- ...props
-}) => {
- const getClasses = () => {
- const classNames = [classes.field]
-
- if (width === 'small') {
- classNames.push(classes.widthSmall)
- } else if (width === 'mid') {
- classNames.push(classes.widthMid)
- } else {
- classNames.push(classes.widthFull)
- }
-
- if (!isVisible) {
- classNames.push(classes.hidden)
- }
-
- return classnames(...classNames)
- }
-
- const classNames = getClasses()
-
- switch (type) {
- case TYPES.SELECT:
- return (
-
- )
- case TYPES.LIST:
- return (
-
- )
- case TYPES.MULTILINE:
- return (
-
- )
- case TYPES.DATE:
- case TYPES.TIME:
- case TYPES.DATETIME:
- return (
-
- )
- case TYPES.SWITCH:
- return (
-
- )
- case TYPES.HIDDEN:
- return (
-
- )
- case TYPES.DIVIDER:
- return (
-
- )
- case TYPES.EMPTY:
- return (
-
- )
- case TYPES.CONTENT:
- return (
-
- {props.content}
-
- )
- default:
- return (
-
- )
- }
-}
-
-FormFieldBranch.propTypes = {
- type: PropTypes.string,
- width: PropTypes.string,
- listItems: PropTypes.arrayOf(PropTypes.object),
- selectOptions: PropTypes.arrayOf(PropTypes.string),
- content: PropTypes.node,
- isVisible: PropTypes.bool,
- classes: PropTypes.objectOf(PropTypes.string).isRequired,
- handleChange: PropTypes.func,
-}
-
-FormFieldBranch.defaultProps = {
- type: 'text',
- width: 'full',
- listItems: [],
- selectOptions: [],
- content: null,
- isVisible: true,
- handleChange: () => { },
-}
-
-export default withStyles(styles)(FormFieldBranch)
diff --git a/src/Form/FormFieldBranch.tsx b/src/Form/FormFieldBranch.tsx
new file mode 100644
index 0000000..a2eba2f
--- /dev/null
+++ b/src/Form/FormFieldBranch.tsx
@@ -0,0 +1,154 @@
+import React from "react";
+import classnames from "classnames";
+import { withStyles } from "@material-ui/core";
+import { TYPES } from "./constants";
+import FormFieldInput from "./FormFieldInput";
+import FormFieldDate from "./FormFieldDate";
+import FormFieldList from "./FormFieldList";
+import FormFieldSwitch from "./FormFieldSwitch";
+import FormFieldHidden from "./FormFieldHidden";
+
+const styles = (theme: any) => ({
+ hidden: {
+ display: "none",
+ },
+ headline: {
+ marginTop: theme.spacing(3),
+ },
+ field: {
+ marginLeft: theme.spacing(),
+ marginRight: theme.spacing(),
+ verticalAlign: "top",
+ },
+ fieldInline: {
+ display: "inline-block",
+ },
+ fieldDate: {
+ marginTop: theme.spacing(2),
+ marginBottom: theme.spacing(),
+ },
+ widthSmall: {
+ width: `calc(25% - ${theme.spacing(2)}px)`,
+ },
+ widthMid: {
+ width: `calc(50% - ${theme.spacing(2)}px)`,
+ },
+ widthFull: {
+ width: `calc(100% - ${theme.spacing(2)}px)`,
+ },
+ divider: {
+ marginTop: theme.spacing(3),
+ marginBottom: theme.spacing(3),
+ },
+});
+
+type FormFieldBranchProps = {
+ type?: string;
+ width?: string;
+ listItems?: object[];
+ selectOptions?: string[];
+ content?: React.ReactNode;
+ isVisible?: boolean;
+ classes: {
+ [key: string]: string;
+ };
+ handleChange: (...args: any[]) => any;
+ id: string;
+ title: string;
+ onAddListItem: () => void;
+ onRemoveListItem: () => void;
+};
+
+const FormFieldBranch: React.SFC = ({
+ type,
+ width,
+ classes,
+ isVisible,
+ ...props
+}) => {
+ const getClasses = () => {
+ const classNames = [classes.field];
+
+ if (width === "small") {
+ classNames.push(classes.widthSmall);
+ } else if (width === "mid") {
+ classNames.push(classes.widthMid);
+ } else {
+ classNames.push(classes.widthFull);
+ }
+ if (!isVisible) {
+ classNames.push(classes.hidden);
+ }
+ return classnames(...classNames);
+ };
+
+ const classNames = getClasses();
+
+ switch (type) {
+ case TYPES.SELECT:
+ return (
+
+ );
+
+ case TYPES.LIST:
+ return ;
+
+ case TYPES.MULTILINE:
+ return ;
+
+ case TYPES.DATE:
+ case TYPES.TIME:
+ case TYPES.DATETIME:
+ return (
+
+ );
+
+ case TYPES.SWITCH:
+ return (
+
+ );
+
+ case TYPES.HIDDEN:
+ return ;
+
+ case TYPES.DIVIDER:
+ return
;
+
+ case TYPES.EMPTY:
+ return ;
+
+ case TYPES.CONTENT:
+ return (
+
+ {props.content}
+
+ );
+
+ default:
+ return ;
+ }
+};
+
+FormFieldBranch.defaultProps = {
+ type: "text",
+ width: "full",
+ listItems: [],
+ selectOptions: [],
+ content: null,
+ isVisible: true,
+ handleChange: () => {},
+};
+
+export default withStyles(styles)(FormFieldBranch);
diff --git a/src/Form/FormFieldDate.jsx b/src/Form/FormFieldDate.jsx
deleted file mode 100644
index abc94a9..0000000
--- a/src/Form/FormFieldDate.jsx
+++ /dev/null
@@ -1,96 +0,0 @@
-import React from 'react'
-import PropTypes from 'prop-types'
-
-import KeyboardArrowLeftIcon from '@material-ui/icons/KeyboardArrowLeft'
-import KeyboardArrowRightIcon from '@material-ui/icons/KeyboardArrowRight'
-import DateRangeIcon from '@material-ui/icons/DateRange'
-import AccessTimeIcon from '@material-ui/icons/AccessTime'
-import MomentUtils from '@date-io/moment'
-import {
- MuiPickersUtilsProvider,
- DateTimePicker,
- DatePicker,
- TimePicker,
-} from '@material-ui/pickers'
-
-import { TYPES } from './constants'
-
-const FormFieldDate = ({
- type,
- id,
- title,
- value,
- format,
- isRequired,
- handleChange,
- helperText,
- className,
-}) => {
- let additionalAttributes = {}
- let Component
-
- switch (type) {
- case TYPES.TIME:
- Component = TimePicker
- break
- case TYPES.DATE:
- Component = DatePicker
- additionalAttributes = {
- leftArrowIcon: (),
- rightArrowIcon: (),
- }
- break
- default:
- Component = DateTimePicker
- additionalAttributes = {
- leftArrowIcon: (),
- rightArrowIcon: (),
- timeIcon: (),
- dateRangeIcon: (),
- }
- break
- }
-
- return (
-
-
-
- )
-}
-
-FormFieldDate.propTypes = {
- type: PropTypes.oneOf([
- TYPES.TIME,
- TYPES.DATE,
- TYPES.DATETIME,
- ]).isRequired,
- id: PropTypes.string.isRequired,
- title: PropTypes.string.isRequired,
- value: PropTypes.oneOfType([
- PropTypes.string,
- PropTypes.number,
- ]).isRequired,
- format: PropTypes.string,
- helperText: PropTypes.string,
- isRequired: PropTypes.bool.isRequired,
- className: PropTypes.string.isRequired,
- handleChange: PropTypes.func.isRequired,
-}
-
-FormFieldDate.defaultProps = {
- helperText: null,
- format: null,
-}
-
-export default FormFieldDate
diff --git a/src/Form/FormFieldDate.tsx b/src/Form/FormFieldDate.tsx
new file mode 100644
index 0000000..351effb
--- /dev/null
+++ b/src/Form/FormFieldDate.tsx
@@ -0,0 +1,83 @@
+import React from "react";
+import KeyboardArrowLeftIcon from "@material-ui/icons/KeyboardArrowLeft";
+import KeyboardArrowRightIcon from "@material-ui/icons/KeyboardArrowRight";
+import DateRangeIcon from "@material-ui/icons/DateRange";
+import AccessTimeIcon from "@material-ui/icons/AccessTime";
+import MomentUtils from "@date-io/moment";
+import {
+ MuiPickersUtilsProvider,
+ DateTimePicker,
+ DatePicker,
+ TimePicker,
+} from "@material-ui/pickers";
+import { TYPES } from "./constants";
+
+type FormFieldDateProps = {
+ type: any;
+ id: string;
+ title: string;
+ value?: string | number;
+ format?: string;
+ helperText?: string;
+ isRequired?: boolean;
+ className: string;
+ handleChange: (...args: any[]) => any;
+};
+
+const FormFieldDate: React.SFC = ({
+ type,
+ id,
+ title,
+ value,
+ format,
+ isRequired = false,
+ handleChange,
+ helperText,
+ className,
+}) => {
+ let additionalAttributes = {};
+ let Component;
+ switch (type) {
+ case TYPES.TIME:
+ Component = TimePicker;
+ break;
+ case TYPES.DATE:
+ Component = DatePicker;
+ additionalAttributes = {
+ leftArrowIcon: ,
+ rightArrowIcon: ,
+ };
+ break;
+ default:
+ Component = DateTimePicker;
+ additionalAttributes = {
+ leftArrowIcon: ,
+ rightArrowIcon: ,
+ timeIcon: ,
+ dateRangeIcon: ,
+ };
+ break;
+ }
+ return (
+
+
+
+ );
+};
+
+FormFieldDate.defaultProps = {
+ helperText: undefined,
+ format: undefined,
+};
+export default FormFieldDate;
diff --git a/src/Form/FormFieldHidden.jsx b/src/Form/FormFieldHidden.jsx
deleted file mode 100644
index fae6c97..0000000
--- a/src/Form/FormFieldHidden.jsx
+++ /dev/null
@@ -1,29 +0,0 @@
-import React from 'react'
-import PropTypes from 'prop-types'
-
-const FormFieldHidden = ({
- id,
- value,
-}) => (
-
-)
-
-FormFieldHidden.propTypes = {
- id: PropTypes.string.isRequired,
- value: PropTypes.oneOfType([
- PropTypes.string,
- PropTypes.array,
- PropTypes.number,
- ]),
-}
-
-FormFieldHidden.defaultProps = {
- value: '',
-}
-
-export default FormFieldHidden
diff --git a/src/Form/FormFieldHidden.tsx b/src/Form/FormFieldHidden.tsx
new file mode 100644
index 0000000..5b8ca69
--- /dev/null
+++ b/src/Form/FormFieldHidden.tsx
@@ -0,0 +1,16 @@
+import React from "react";
+
+type FormFieldHiddenProps = {
+ id: string;
+ value?: string | any[] | number;
+};
+
+const FormFieldHidden: React.SFC = ({ id, value }) => (
+
+);
+
+FormFieldHidden.defaultProps = {
+ value: "",
+};
+
+export default FormFieldHidden;
diff --git a/src/Form/FormFieldInput.jsx b/src/Form/FormFieldInput.jsx
deleted file mode 100644
index 9f0dd36..0000000
--- a/src/Form/FormFieldInput.jsx
+++ /dev/null
@@ -1,135 +0,0 @@
-import React, { Fragment } from 'react'
-import PropTypes from 'prop-types'
-
-import {
- TextField,
- InputAdornment,
- MenuItem,
-} from '@material-ui/core'
-
-const InputEnd = ({ icon }) => (
-
- {icon ? (
-
- {icon}
-
- ) : null}
-
-)
-
-InputEnd.propTypes = {
- icon: PropTypes.element,
-}
-
-InputEnd.defaultProps = {
- icon: null,
-}
-
-const FormFieldInput = ({
- id,
- type,
- title,
- value,
- isMultiline,
- handleChange,
- helperText,
- defaultValue,
- select,
- options,
- rows,
- isRequired,
- error,
- iconEnd,
- isDisabled,
- className,
- onFocus,
- onBlur,
- onKeyPress,
-}) => (
- ),
- }}
- className={className}
- >
- {options
- ? options.map(option => (
-
- ))
- : null
- }
-
-)
-
-FormFieldInput.propTypes = {
- id: PropTypes.string.isRequired,
- type: PropTypes.string,
- title: PropTypes.string,
- value: PropTypes.oneOfType([
- PropTypes.string,
- PropTypes.array,
- PropTypes.number,
- ]),
- defaultValue: PropTypes.oneOfType([
- PropTypes.string,
- PropTypes.array,
- PropTypes.number,
- ]),
- rows: PropTypes.number,
- isMultiline: PropTypes.bool,
- helperText: PropTypes.string,
- isRequired: PropTypes.bool,
- error: PropTypes.bool,
- className: PropTypes.string,
- handleChange: PropTypes.func,
- select: PropTypes.bool,
- options: PropTypes.arrayOf(PropTypes.string),
- isDisabled: PropTypes.bool,
- iconEnd: PropTypes.element,
- onFocus: PropTypes.func,
- onBlur: PropTypes.func,
- onKeyPress: PropTypes.func,
-}
-
-FormFieldInput.defaultProps = {
- type: 'text',
- title: undefined,
- value: '',
- defaultValue: undefined,
- isMultiline: false,
- helperText: '',
- className: '',
- isRequired: false,
- error: false,
- rows: 1,
- select: false,
- options: [],
- iconEnd: null,
- isDisabled: false,
- handleChange: () => { },
- onFocus: () => { },
- onBlur: () => { },
- onKeyPress: () => { },
-}
-
-export default FormFieldInput
diff --git a/src/Form/FormFieldInput.test.jsx b/src/Form/FormFieldInput.test.jsx
deleted file mode 100644
index 6541114..0000000
--- a/src/Form/FormFieldInput.test.jsx
+++ /dev/null
@@ -1,23 +0,0 @@
-import React from 'react'
-import { shallow } from 'enzyme'
-
-import FormFieldInput from './FormFieldInput'
-
-it('renders correctly', () => {
- const tree = shallow((
-
- ))
-
- expect(tree).toMatchSnapshot()
-})
-
-
-it('renders disabled', () => {
- const tree = shallow((
-
- ))
-
- expect(tree).toMatchSnapshot()
-})
diff --git a/src/Form/FormFieldInput.test.tsx b/src/Form/FormFieldInput.test.tsx
new file mode 100644
index 0000000..5ab3103
--- /dev/null
+++ b/src/Form/FormFieldInput.test.tsx
@@ -0,0 +1,16 @@
+import React from "react";
+import { shallow } from "enzyme";
+
+import FormFieldInput from "./FormFieldInput";
+
+it("renders correctly", () => {
+ const tree = shallow();
+
+ expect(tree).toMatchSnapshot();
+});
+
+it("renders disabled", () => {
+ const tree = shallow();
+
+ expect(tree).toMatchSnapshot();
+});
diff --git a/src/Form/FormFieldInput.tsx b/src/Form/FormFieldInput.tsx
new file mode 100644
index 0000000..6e4fe01
--- /dev/null
+++ b/src/Form/FormFieldInput.tsx
@@ -0,0 +1,113 @@
+import React, { Fragment } from "react";
+import { TextField, InputAdornment, MenuItem } from "@material-ui/core";
+
+type InputEndProps = {
+ icon?: JSX.Element;
+};
+
+const InputEnd: React.SFC = ({ icon }) => (
+
+ {icon && {icon}}
+
+);
+
+InputEnd.defaultProps = {
+ icon: undefined,
+};
+
+type FormFieldInputProps = {
+ id: string;
+ type?: string;
+ title?: string;
+ value?: string | any[] | number;
+ defaultValue?: string | any[] | number;
+ rows?: number;
+ isMultiline?: boolean;
+ helperText?: string;
+ isRequired?: boolean;
+ error?: boolean;
+ className?: string;
+ handleChange?: (...args: any[]) => any;
+ select?: boolean;
+ options?: string[];
+ isDisabled?: boolean;
+ iconEnd?: JSX.Element;
+ onFocus?: (...args: any[]) => any;
+ onBlur?: (...args: any[]) => any;
+ onKeyPress?: (...args: any[]) => any;
+};
+
+const FormFieldInput: React.SFC = ({
+ id,
+ type,
+ title,
+ value,
+ isMultiline,
+ handleChange,
+ helperText,
+ defaultValue,
+ select,
+ options,
+ rows,
+ isRequired,
+ error,
+ iconEnd,
+ isDisabled,
+ className,
+ onFocus,
+ onBlur,
+ onKeyPress,
+}) => (
+ ,
+ }}
+ className={className}
+ >
+ {options
+ ? options.map((option) => (
+
+ ))
+ : null}
+
+);
+
+FormFieldInput.defaultProps = {
+ type: "text",
+ value: "",
+ isMultiline: false,
+ helperText: "",
+ className: "",
+ isRequired: false,
+ error: false,
+ rows: 1,
+ select: false,
+ options: [],
+ isDisabled: false,
+ handleChange: () => {},
+ onFocus: () => {},
+ onBlur: () => {},
+ onKeyPress: () => {},
+};
+
+export default FormFieldInput;
diff --git a/src/Form/FormFieldList.jsx b/src/Form/FormFieldList.jsx
deleted file mode 100644
index 21822d5..0000000
--- a/src/Form/FormFieldList.jsx
+++ /dev/null
@@ -1,148 +0,0 @@
-import React from 'react'
-import PropTypes from 'prop-types'
-
-import replace from '../utils/replace'
-
-import FormFieldListBranch from './FormFieldListBranch'
-
-const withFormFieldList = Component => class extends React.Component {
- static propTypes = {
- listItems: PropTypes.arrayOf(PropTypes.object),
- completeFrom: PropTypes.arrayOf(PropTypes.object),
- handleChange: PropTypes.func.isRequired,
- onAddListItem: PropTypes.func.isRequired,
- onRemoveListItem: PropTypes.func.isRequired,
- }
-
- static defaultProps = {
- listItems: [],
- completeFrom: [],
- }
-
- constructor(props) {
- super(props)
-
- this.handleChange = this.handleChange.bind(this)
- this.handleClick = this.handleClick.bind(this)
- this.handleBlur = this.handleBlur.bind(this)
- this.handleDelete = this.handleDelete.bind(this)
- this.handleKeyPress = this.handleKeyPress.bind(this)
- this.getAvailableOptions = this.getAvailableOptions.bind(this)
- }
-
- state = {
- availableOptions: [],
- showMenu: false,
- }
-
- componentWillUnmount() {
- clearTimeout(this.timer)
- }
-
- getAvailableOptions(value) {
- const { completeFrom, listItems } = this.props
- const lowerValue = value.toLowerCase()
- const flatListItems = listItems.map(item => item.title)
-
- // Remove selected items
- let availableOptions = completeFrom.filter(option => (
- flatListItems.indexOf(option.title) === -1
- ))
-
- // Overload data
- availableOptions = availableOptions.map((option) => {
- let { title } = option
-
- if (!option.title) {
- title = option
- }
-
- return {
- title,
- tooltip: option.tooltip,
- text: replace(title, lowerValue, searchResult => `${searchResult}`),
- }
- })
-
- // Filter for real
- availableOptions = availableOptions.filter(option => (
- option.title.toLowerCase().indexOf(lowerValue) > -1
- ))
-
- return availableOptions
- }
-
- timer = undefined
-
- handleChange(id) {
- return (event) => {
- const parentFunction = this.props.handleChange(id)
- const { value } = event.target
-
- this.setState({
- value,
- availableOptions: this.getAvailableOptions(value),
- showMenu: value.length > 0,
- })
-
- return parentFunction(event)
- }
- }
-
- handleClick(option) {
- return () => {
- this.props.onAddListItem(option)
-
- this.setState({
- value: '',
- })
- }
- }
-
- handleBlur() {
- this.timer = setTimeout(() => {
- this.setState({
- showMenu: false,
- })
- }, 100)
- }
-
- handleDelete(option) {
- return () => {
- this.props.onRemoveListItem(option)
- }
- }
-
- handleKeyPress(event) {
- const { value } = this.state
- const { completeFrom } = this.props
-
- if (event.which === 13 && completeFrom.length === 0) {
- this.props.onAddListItem({
- title: value,
- })
-
- this.setState({
- value: '',
- })
- }
- }
-
- render() {
- return (
-
- )
- }
-}
-
-const FormFieldList = withFormFieldList(FormFieldListBranch)
-
-export default FormFieldList
diff --git a/src/Form/FormFieldList.tsx b/src/Form/FormFieldList.tsx
new file mode 100644
index 0000000..b275d54
--- /dev/null
+++ b/src/Form/FormFieldList.tsx
@@ -0,0 +1,152 @@
+import React from "react";
+import replace from "../utils/replace";
+import FormFieldListBranch from "./FormFieldListBranch";
+
+type WithFormFieldListProps = {
+ listItems?: Record[];
+ completeFrom?: Record[];
+ handleChange: (...args: any[]) => any;
+ onAddListItem: (...args: any[]) => any;
+ onRemoveListItem: (...args: any[]) => any;
+ className: string;
+};
+
+type WithFormFieldListState = {
+ value?: string;
+ availableOptions: any[];
+ showMenu: boolean;
+};
+
+const withFormFieldList = (Component: any) =>
+ class WithFormFieldList extends React.Component<
+ WithFormFieldListProps,
+ WithFormFieldListState
+ > {
+ constructor(props: WithFormFieldListProps) {
+ super(props);
+ this.handleChange = this.handleChange.bind(this);
+ this.handleClick = this.handleClick.bind(this);
+ this.handleBlur = this.handleBlur.bind(this);
+ this.handleDelete = this.handleDelete.bind(this);
+ this.handleKeyPress = this.handleKeyPress.bind(this);
+ this.getAvailableOptions = this.getAvailableOptions.bind(this);
+ }
+
+ state = {
+ availableOptions: [],
+ showMenu: false,
+ value: undefined,
+ };
+
+ componentWillUnmount() {
+ clearTimeout(this.timer);
+ }
+
+ getAvailableOptions(value: any) {
+ const { completeFrom, listItems } = this.props;
+ const lowerValue = value.toLowerCase();
+ const flatListItems =
+ listItems && listItems.map((item: any) => item.title);
+ // Remove selected items
+ let availableOptions =
+ completeFrom &&
+ completeFrom.filter(
+ (option) =>
+ flatListItems && flatListItems.indexOf(option.title) === -1,
+ );
+ // Overload data
+ availableOptions =
+ availableOptions &&
+ availableOptions.map((option) => {
+ let { title } = option;
+ if (!option.title) {
+ title = option;
+ }
+ return {
+ title,
+ tooltip: option.tooltip,
+ text: replace(
+ title,
+ lowerValue,
+ (searchResult: string) => `${searchResult}`,
+ ),
+ };
+ });
+
+ // Filter for real
+ availableOptions =
+ availableOptions &&
+ availableOptions.filter(
+ (option) => option.title.toLowerCase().indexOf(lowerValue) > -1,
+ );
+
+ return availableOptions as Record[];
+ }
+
+ timer: any = undefined;
+
+ handleChange(id: string) {
+ return (event: any) => {
+ const parentFunction = this.props.handleChange(id);
+ const { value } = event.target;
+ this.setState({
+ value,
+ availableOptions: this.getAvailableOptions(value),
+ showMenu: value.length > 0,
+ });
+ return parentFunction(event);
+ };
+ }
+
+ handleClick(option: any) {
+ return () => {
+ this.props.onAddListItem(option);
+ this.setState({
+ value: "",
+ });
+ };
+ }
+
+ handleBlur() {
+ this.timer = setTimeout(() => {
+ this.setState({
+ showMenu: false,
+ });
+ }, 100);
+ }
+
+ handleDelete(option: any) {
+ return () => {
+ this.props.onRemoveListItem(option);
+ };
+ }
+ handleKeyPress(event: any) {
+ const { value } = this.state;
+ const { completeFrom } = this.props;
+ if (event.which === 13 && completeFrom && completeFrom.length === 0) {
+ this.props.onAddListItem({
+ title: value,
+ });
+ this.setState({
+ value: "",
+ });
+ }
+ }
+
+ render() {
+ return (
+
+ );
+ }
+ };
+
+const FormFieldList = withFormFieldList(FormFieldListBranch);
+export default FormFieldList;
diff --git a/src/Form/FormFieldListBranch.jsx b/src/Form/FormFieldListBranch.jsx
deleted file mode 100644
index 4a1c554..0000000
--- a/src/Form/FormFieldListBranch.jsx
+++ /dev/null
@@ -1,153 +0,0 @@
-import React from 'react'
-import PropTypes from 'prop-types'
-import classnames from 'classnames'
-
-import {
- Chip,
- List,
- ListItem,
- ListItemText,
- Paper,
- Tooltip,
- withStyles,
-} from '@material-ui/core'
-
-import FormFieldInput from './FormFieldInput'
-
-const styles = theme => ({
- root: {
- position: 'relative',
- zIndex: 1,
- display: 'inline-flex',
- flexDirection: 'column',
- },
- list: {
- position: 'absolute',
- top: theme.spacing(9),
- width: '100%',
- height: 0,
- opacity: 0,
- overflow: 'hidden',
- transition: '0.25s',
- },
- listActive: {
- height: 'auto',
- opacity: 1,
- },
- selectedItem: {
- display: 'inline-flex',
- margin: '0.125rem',
- },
-})
-
-const FormFieldListBranch = ({
- availableOptions,
- listItems,
- showMenu,
- renderElement,
- className,
- onClick,
- onChange,
- onBlur,
- onDelete,
- classes,
- ...rest
-}) => {
- const renderListItem = (option, index) => {
- if (renderElement) {
- return renderElement(option, index)
- }
-
- if (!option) {
- return null
- }
-
- const chipProp = {
- label: option.title,
- onDelete: onDelete(option),
- }
-
- if (option.tooltip) {
- return (
-
-
-
- )
- }
-
- return (
-
- )
- }
-
- return (
-
-
-
-
0,
- })}
- >
-
- {availableOptions.slice(0, 10).map((option) => {
- const key = Math.floor((1 + Math.random()) * 0x10000)
- .toString(16)
- .substring(1)
-
- const title = (
-
- )
-
- return (
-
-
-
- )
- })}
-
-
-
-
- {listItems ? listItems.map(renderListItem) : null}
-
-
- )
-}
-
-FormFieldListBranch.propTypes = {
- availableOptions: PropTypes.arrayOf(PropTypes.object).isRequired,
- listItems: PropTypes.arrayOf(PropTypes.oneOfType([
- PropTypes.string,
- PropTypes.object,
- ])).isRequired,
- showMenu: PropTypes.bool.isRequired,
- renderElement: PropTypes.func,
- className: PropTypes.string.isRequired,
- onClick: PropTypes.func.isRequired,
- onChange: PropTypes.func.isRequired,
- onBlur: PropTypes.func.isRequired,
- onDelete: PropTypes.func.isRequired,
- classes: PropTypes.objectOf(PropTypes.string).isRequired,
-}
-
-FormFieldListBranch.defaultProps = {
- renderElement: null,
-}
-
-export default withStyles(styles)(FormFieldListBranch)
diff --git a/src/Form/FormFieldListBranch.tsx b/src/Form/FormFieldListBranch.tsx
new file mode 100644
index 0000000..7b4ce0d
--- /dev/null
+++ b/src/Form/FormFieldListBranch.tsx
@@ -0,0 +1,139 @@
+import React from "react";
+import classnames from "classnames";
+import {
+ Chip,
+ List,
+ ListItem,
+ ListItemText,
+ Paper,
+ Tooltip,
+ withStyles,
+} from "@material-ui/core";
+import FormFieldInput from "./FormFieldInput";
+
+const styles = (theme: any) => ({
+ root: {
+ position: "relative",
+ zIndex: 1,
+ display: "inline-flex",
+ flexDirection: "column",
+ },
+ list: {
+ position: "absolute",
+ top: theme.spacing(9),
+ width: "100%",
+ height: 0,
+ opacity: 0,
+ overflow: "hidden",
+ transition: "0.25s",
+ },
+ listActive: {
+ height: "auto",
+ opacity: 1,
+ },
+ selectedItem: {
+ display: "inline-flex",
+ margin: "0.125rem",
+ },
+});
+
+type FormFieldListBranchProps = {
+ availableOptions: object[];
+ listItems: (string | object)[];
+ showMenu: boolean;
+ renderElement?: (...args: any[]) => any;
+ className: string;
+ onClick: (...args: any[]) => any;
+ onChange: (...args: any[]) => any;
+ onBlur: (...args: any[]) => any;
+ onDelete: (...args: any[]) => any;
+ classes: {
+ [key: string]: string;
+ };
+ id: string;
+};
+
+const FormFieldListBranch: React.SFC = ({
+ availableOptions,
+ listItems,
+ showMenu,
+ renderElement,
+ className,
+ onClick,
+ onChange,
+ onBlur,
+ onDelete,
+ classes,
+ ...rest
+}) => {
+ const renderListItem = (option: any, index: number) => {
+ if (renderElement) {
+ return renderElement(option, index);
+ }
+
+ if (!option) {
+ return null;
+ }
+
+ const chipProp = {
+ label: option.title,
+ onDelete: onDelete(option),
+ };
+
+ if (option.tooltip) {
+ return (
+
+
+
+ );
+ }
+
+ return (
+
+ );
+ };
+
+ return (
+
+
+
+
0,
+ })}
+ >
+
+ {availableOptions.slice(0, 10).map((option: any) => {
+ const key = Math.floor((1 + Math.random()) * 0x10000)
+ .toString(16)
+ .substring(1);
+ const title = (
+
+ );
+ return (
+
+
+
+ );
+ })}
+
+
+
+
{listItems ? listItems.map(renderListItem) : null}
+
+ );
+};
+
+export default withStyles(styles as any)(FormFieldListBranch);
diff --git a/src/Form/FormFieldSwitch.jsx b/src/Form/FormFieldSwitch.jsx
deleted file mode 100644
index 2434c61..0000000
--- a/src/Form/FormFieldSwitch.jsx
+++ /dev/null
@@ -1,87 +0,0 @@
-import React from 'react'
-import PropTypes from 'prop-types'
-
-import {
- FormControlLabel,
- FormHelperText,
- Switch,
-} from '@material-ui/core'
-
-class FormFieldSwitch extends React.Component {
- static propTypes = {
- id: PropTypes.string.isRequired,
- title: PropTypes.string.isRequired,
- handleChange: PropTypes.func.isRequired,
- helperText: PropTypes.string,
- className: PropTypes.string,
- isDisabled: PropTypes.bool,
- }
-
- static defaultProps = {
- helperText: null,
- className: '',
- isDisabled: false,
- }
-
- state = {
- value: false,
- }
-
- handleChange(id) {
- const { handleChange } = this.props
- const { value } = this.state
- const func = handleChange(id)
-
- return () => {
- const newValue = !value
-
- this.setState({
- value: newValue,
- })
-
- return func({
- target: {
- value: newValue,
- },
- })
- }
- }
-
- render() {
- const {
- id,
- title,
- helperText,
- isDisabled,
- className,
- } = this.props
- const { value } = this.state
-
- return (
-
-
- )}
- label={title}
- />
-
- {helperText ? (
-
- {helperText}
-
- ) : null}
-
- )
- }
-}
-
-export default FormFieldSwitch
diff --git a/src/Form/FormFieldSwitch.test.jsx b/src/Form/FormFieldSwitch.test.jsx
deleted file mode 100644
index 1760c67..0000000
--- a/src/Form/FormFieldSwitch.test.jsx
+++ /dev/null
@@ -1,42 +0,0 @@
-import React from 'react'
-import { shallow } from 'enzyme'
-
-import FormFieldSwitch from './FormFieldSwitch'
-
-it('renders correctly', () => {
- const tree = shallow((
- () => {}}
- />
- ))
-
- expect(tree).toMatchSnapshot()
-})
-
-it('renders disabled', () => {
- const tree = shallow((
- () => { }}
- isDisabled
- />
- ))
-
- expect(tree).toMatchSnapshot()
-})
-
-it('renders with helper text', () => {
- const tree = shallow((
- () => { }}
- helperText="test"
- />
- ))
-
- expect(tree).toMatchSnapshot()
-})
diff --git a/src/Form/FormFieldSwitch.test.tsx b/src/Form/FormFieldSwitch.test.tsx
new file mode 100644
index 0000000..0d272db
--- /dev/null
+++ b/src/Form/FormFieldSwitch.test.tsx
@@ -0,0 +1,38 @@
+import React from "react";
+import { shallow } from "enzyme";
+
+import FormFieldSwitch from "./FormFieldSwitch";
+
+it("renders correctly", () => {
+ const tree = shallow(
+ () => {}} />,
+ );
+
+ expect(tree).toMatchSnapshot();
+});
+
+it("renders disabled", () => {
+ const tree = shallow(
+ () => {}}
+ isDisabled
+ />,
+ );
+
+ expect(tree).toMatchSnapshot();
+});
+
+it("renders with helper text", () => {
+ const tree = shallow(
+ () => {}}
+ helperText="test"
+ />,
+ );
+
+ expect(tree).toMatchSnapshot();
+});
diff --git a/src/Form/FormFieldSwitch.tsx b/src/Form/FormFieldSwitch.tsx
new file mode 100644
index 0000000..dce7c91
--- /dev/null
+++ b/src/Form/FormFieldSwitch.tsx
@@ -0,0 +1,68 @@
+import React from "react";
+import { FormControlLabel, FormHelperText, Switch } from "@material-ui/core";
+
+type FormFieldSwitchProps = {
+ id: string;
+ title: string;
+ handleChange: (...args: any[]) => any;
+ helperText?: string;
+ className?: string;
+ isDisabled?: boolean;
+};
+
+type FormFieldSwitchState = {
+ value: boolean;
+};
+
+class FormFieldSwitch extends React.Component<
+ FormFieldSwitchProps,
+ FormFieldSwitchState
+> {
+ state = {
+ value: false,
+ };
+
+ handleChange(id: string) {
+ const { handleChange } = this.props;
+ const { value } = this.state;
+ const func = handleChange(id);
+ return () => {
+ const newValue = !value;
+ this.setState({
+ value: newValue,
+ });
+ return func({
+ target: {
+ value: newValue,
+ },
+ });
+ };
+ }
+
+ render() {
+ const { id, title, helperText, isDisabled, className } = this.props;
+ const { value } = this.state;
+ return (
+
+
+ }
+ label={title}
+ />
+
+ {helperText ? (
+ {helperText}
+ ) : null}
+
+ );
+ }
+}
+
+export default FormFieldSwitch;
diff --git a/src/Form/FormGroupWrapper.jsx b/src/Form/FormGroupWrapper.tsx
similarity index 56%
rename from src/Form/FormGroupWrapper.jsx
rename to src/Form/FormGroupWrapper.tsx
index 723bb73..5565625 100644
--- a/src/Form/FormGroupWrapper.jsx
+++ b/src/Form/FormGroupWrapper.tsx
@@ -1,10 +1,8 @@
-import React from 'react'
-import PropTypes from 'prop-types'
-import classNames from 'classnames'
+import React from "react";
+import classNames from "classnames";
+import { Paper, withStyles } from "@material-ui/core";
-import { Paper, withStyles } from '@material-ui/core'
-
-const styles = theme => ({
+const styles = (theme: any) => ({
group: {
marginTop: theme.spacing(),
marginBottom: theme.spacing(),
@@ -15,11 +13,19 @@ const styles = theme => ({
paddingTop: theme.spacing(3),
},
hidden: {
- display: 'none',
+ display: "none",
},
-})
+});
+
+type FormGroupWrapperProps = {
+ isPaper?: boolean;
+ isVisible?: boolean;
+ classes: {
+ [key: string]: string;
+ };
+};
-const FormGroupWrapper = ({
+const FormGroupWrapper: React.SFC = ({
isPaper,
isVisible,
classes,
@@ -30,38 +36,30 @@ const FormGroupWrapper = ({
return (
{children}
- )
+ );
}
return (
{children}
- )
-}
-
-FormGroupWrapper.propTypes = {
- isPaper: PropTypes.bool,
- isVisible: PropTypes.bool,
- children: PropTypes.node,
- classes: PropTypes.objectOf(PropTypes.string).isRequired,
-}
+ );
+};
FormGroupWrapper.defaultProps = {
isPaper: true,
isVisible: true,
- children: null,
-}
+};
-export default withStyles(styles)(FormGroupWrapper)
+export default withStyles(styles)(FormGroupWrapper);
diff --git a/src/Form/FormSubmitButton.jsx b/src/Form/FormSubmitButton.tsx
similarity index 55%
rename from src/Form/FormSubmitButton.jsx
rename to src/Form/FormSubmitButton.tsx
index 6d7e80e..8ac2662 100644
--- a/src/Form/FormSubmitButton.jsx
+++ b/src/Form/FormSubmitButton.tsx
@@ -1,17 +1,15 @@
-import React from 'react'
-import PropTypes from 'prop-types'
-import classNames from 'classnames'
+import React from "react";
+import classNames from "classnames";
+import { Button, CircularProgress, withStyles } from "@material-ui/core";
+import { green } from "@material-ui/core/colors";
-import { Button, CircularProgress, withStyles } from '@material-ui/core'
-import { green } from '@material-ui/core/colors'
-
-const styles = theme => ({
+const styles = (theme: any) => ({
root: {
- position: 'relative',
- display: 'inline-block',
+ position: "relative",
+ display: "inline-block",
},
fixed: {
- position: 'fixed',
+ position: "fixed",
right: theme.spacing(5),
bottom: theme.spacing(3),
marginBottom: theme.spacing(),
@@ -22,15 +20,25 @@ const styles = theme => ({
},
progress: {
color: green[500],
- position: 'absolute',
- top: '50%',
- left: '50%',
+ position: "absolute",
+ top: "50%",
+ left: "50%",
marginTop: -12,
marginLeft: -12,
},
-})
+});
+
+type FormSubmitButtonProps = {
+ onSubmit: (...args: any[]) => any;
+ disabled?: boolean;
+ loading?: boolean;
+ fixed?: boolean;
+ classes: {
+ [key: string]: string;
+ };
+};
-const FormSubmitButton = ({
+const FormSubmitButton: React.SFC = ({
onSubmit,
disabled,
loading,
@@ -40,8 +48,7 @@ const FormSubmitButton = ({
}) => {
const wrapperClasses = classNames(classes.root, {
[classes.fixed]: fixed,
- })
-
+ });
return (
) : null}
- )
-}
-
-FormSubmitButton.propTypes = {
- onSubmit: PropTypes.func.isRequired,
- disabled: PropTypes.bool,
- loading: PropTypes.bool,
- fixed: PropTypes.bool,
- children: PropTypes.node,
- classes: PropTypes.objectOf(PropTypes.string).isRequired,
-}
-
+ );
+};
FormSubmitButton.defaultProps = {
disabled: false,
loading: false,
fixed: false,
- children: null,
-}
+};
-export default withStyles(styles)(FormSubmitButton)
+export default withStyles(styles as any)(FormSubmitButton);
diff --git a/src/Form/constants.js b/src/Form/constants.js
deleted file mode 100644
index 6c55c7b..0000000
--- a/src/Form/constants.js
+++ /dev/null
@@ -1,20 +0,0 @@
-export const TYPES = {
- SELECT: 'select',
- LIST: 'list',
- MULTILINE: 'multiline',
- TEXT: 'text',
- DATE: 'date',
- TIME: 'time',
- DATETIME: 'datetime',
- NUMBER: 'number',
- SWITCH: 'switch',
- EMAIL: 'email',
- PASSWORD: 'password',
- URL: 'url',
- CONTENT: 'content',
- DIVIDER: 'divider',
- EMPTY: 'empty',
- HIDDEN: 'hidden',
-}
-
-export default {}
diff --git a/src/Form/constants.ts b/src/Form/constants.ts
new file mode 100644
index 0000000..b4de2ce
--- /dev/null
+++ b/src/Form/constants.ts
@@ -0,0 +1,20 @@
+export const TYPES = {
+ SELECT: "select",
+ LIST: "list",
+ MULTILINE: "multiline",
+ TEXT: "text",
+ DATE: "date",
+ TIME: "time",
+ DATETIME: "datetime",
+ NUMBER: "number",
+ SWITCH: "switch",
+ EMAIL: "email",
+ PASSWORD: "password",
+ URL: "url",
+ CONTENT: "content",
+ DIVIDER: "divider",
+ EMPTY: "empty",
+ HIDDEN: "hidden",
+};
+
+export default {};
diff --git a/src/Form/index.js b/src/Form/index.js
deleted file mode 100644
index ce98af0..0000000
--- a/src/Form/index.js
+++ /dev/null
@@ -1,3 +0,0 @@
-import Form from './Form'
-
-export default Form
diff --git a/src/Form/index.ts b/src/Form/index.ts
new file mode 100644
index 0000000..5e2f96a
--- /dev/null
+++ b/src/Form/index.ts
@@ -0,0 +1,3 @@
+import Form from "./Form";
+
+export default Form;
diff --git a/src/Form/isValid.js b/src/Form/isValid.js
deleted file mode 100644
index 993354e..0000000
--- a/src/Form/isValid.js
+++ /dev/null
@@ -1,61 +0,0 @@
-import * as Validators from './validators'
-import { TYPES } from './constants'
-
-export const getValidator = (validator) => {
- if (typeof validator === 'function') {
- return {
- validator,
- }
- }
-
- if (typeof validator === 'string') {
- return {
- validator: Validators[validator],
- }
- }
-
- return validator
-}
-
-/**
- * Returns an object
- */
-export default (type, isRequired, validators = [], value) => {
- const allValidators = [...validators]
- const messages = []
-
- if (isRequired) {
- allValidators.push('required')
- }
-
- if (type === TYPES.EMAIL) {
- allValidators.push('email')
- }
-
- if (type === TYPES.URL) {
- allValidators.push('url')
- }
-
- if (allValidators.length === 0) {
- return {
- valid: true,
- }
- }
-
- const validatorFunctions = allValidators.map(getValidator)
-
- const validState = validatorFunctions.map((validator) => {
- if (validator.message) {
- messages.push(validator.message)
- }
-
- return validator.validator(value)
- })
-
- const isValid = validState.indexOf(false) === -1
-
- return {
- isValid,
- messages: !isValid ? messages : [],
- }
-}
diff --git a/src/Form/isValid.ts b/src/Form/isValid.ts
new file mode 100644
index 0000000..cdff56c
--- /dev/null
+++ b/src/Form/isValid.ts
@@ -0,0 +1,74 @@
+import * as Validators from "./validators";
+import { TYPES } from "./constants";
+
+type Validator =
+ | string
+ | ((...args: any[]) => any)
+ | {
+ validator?: string | ((...args: any[]) => any) | undefined;
+ message?: string | undefined;
+ };
+
+export const getValidator = (validator: Validator): any => {
+ if (typeof validator === "function") {
+ return {
+ validator,
+ };
+ }
+
+ if (typeof validator === "string") {
+ return {
+ validator: Validators[validator],
+ };
+ }
+
+ return validator;
+};
+
+/**
+ * Returns an object
+ */
+export default (
+ type: any,
+ isRequired?: boolean,
+ validators: Validator[] = [],
+ value?: any,
+) => {
+ const allValidators = [...validators];
+ const messages: Validator[] = [];
+
+ if (isRequired) {
+ allValidators.push("required");
+ }
+
+ if (type === TYPES.EMAIL) {
+ allValidators.push("email");
+ }
+
+ if (type === TYPES.URL) {
+ allValidators.push("url");
+ }
+
+ if (allValidators.length === 0) {
+ return {
+ valid: true,
+ };
+ }
+
+ const validatorFunctions = allValidators.map(getValidator);
+
+ const validState = validatorFunctions.map((validator) => {
+ if (validator.message) {
+ messages.push(validator.message);
+ }
+
+ return validator.validator(value);
+ });
+
+ const isValid = validState.indexOf(false) === -1;
+
+ return {
+ isValid,
+ messages: !isValid ? messages : [],
+ };
+};
diff --git a/src/Form/validators.js b/src/Form/validators.js
deleted file mode 100644
index 916675d..0000000
--- a/src/Form/validators.js
+++ /dev/null
@@ -1,35 +0,0 @@
-import Isemail from 'isemail'
-import isUrl from 'is-url'
-
-/**
- * Check if a field has any value at all
- */
-export const required = (value) => {
- let checkValue = value
-
- if (typeof value === 'string') {
- checkValue = value.trim()
- }
-
- return !!(checkValue && checkValue.length > 0)
-}
-
-/**
- * Check if a value is readable for machines (e.g. as identifier)
- */
-export const machinereadable = value => (value && value === encodeURIComponent(value)) || !value
-
-/**
- * Check if value is a date
- */
-export const date = value => (value && !Number.isNaN(Date.parse(value))) || !value
-
-/**
- * Check if value is email
- */
-export const email = value => (value && Isemail.validate(value)) || !value
-
-/**
- * Check if value is url
- */
-export const url = value => (value && isUrl(value)) || !value
diff --git a/src/Form/validators.ts b/src/Form/validators.ts
new file mode 100644
index 0000000..534d1d7
--- /dev/null
+++ b/src/Form/validators.ts
@@ -0,0 +1,38 @@
+import Isemail from "isemail";
+import isUrl from "is-url";
+
+/**
+ * Check if a field has any value at all
+ */
+export const required = (value: any) => {
+ let checkValue = value;
+
+ if (typeof value === "string") {
+ checkValue = value.trim();
+ }
+
+ return !!(checkValue && checkValue.length > 0);
+};
+
+/**
+ * Check if a value is readable for machines (e.g. as identifier)
+ */
+export const machinereadable = (value: any) =>
+ (value && value === encodeURIComponent(value)) || !value;
+
+/**
+ * Check if value is a date
+ */
+export const date = (value: any) =>
+ (value && !Number.isNaN(Date.parse(value))) || !value;
+
+/**
+ * Check if value is email
+ */
+export const email = (value: any) =>
+ (value && Isemail.validate(value)) || !value;
+
+/**
+ * Check if value is url
+ */
+export const url = (value: any) => (value && isUrl(value)) || !value;
diff --git a/src/Header/Header.test.jsx b/src/Header/Header.test.jsx
deleted file mode 100644
index c68f46a..0000000
--- a/src/Header/Header.test.jsx
+++ /dev/null
@@ -1,33 +0,0 @@
-import React from 'react'
-import { shallow } from 'enzyme'
-
-import Header from '.'
-
-it('renders correctly', () => {
- const tree = shallow((
- {}}
- onClick={() => { }}
- />
- ))
-
- expect(tree).toMatchSnapshot()
-})
-
-it('renders correctly with closed', () => {
- const tree = shallow((
- { }}
- onClick={() => { }}
- />
- ))
-
- expect(tree).toMatchSnapshot()
-})
diff --git a/src/Header/Header.test.tsx b/src/Header/Header.test.tsx
new file mode 100644
index 0000000..750f193
--- /dev/null
+++ b/src/Header/Header.test.tsx
@@ -0,0 +1,33 @@
+import React from "react";
+import { shallow } from "enzyme";
+
+import Header from ".";
+
+it("renders correctly", () => {
+ const tree = shallow(
+ {}}
+ onClick={() => {}}
+ />,
+ );
+
+ expect(tree).toMatchSnapshot();
+});
+
+it("renders correctly with closed", () => {
+ const tree = shallow(
+ {}}
+ onClick={() => {}}
+ />,
+ );
+
+ expect(tree).toMatchSnapshot();
+});
diff --git a/src/Header/Header.jsx b/src/Header/Header.tsx
similarity index 59%
rename from src/Header/Header.jsx
rename to src/Header/Header.tsx
index 2fe5e7a..8606b4b 100644
--- a/src/Header/Header.jsx
+++ b/src/Header/Header.tsx
@@ -1,34 +1,33 @@
-import React from 'react'
-import PropTypes from 'prop-types'
-import classNames from 'classnames'
+import React from "react";
+import classNames from "classnames";
import {
AppBar,
IconButton,
Toolbar,
Typography,
withStyles,
-} from '@material-ui/core'
-import MenuIcon from '@material-ui/icons/Menu'
+} from "@material-ui/core";
+import MenuIcon from "@material-ui/icons/Menu";
-const drawerWidth = 280
+const drawerWidth = 280;
-const styles = theme => ({
+const styles = (theme: any) => ({
root: {
- width: '100%',
+ width: "100%",
},
appBar: {
- transition: theme.transitions.create(['margin', 'width'], {
+ transition: theme.transitions.create(["margin", "width"], {
easing: theme.transitions.easing.sharp,
duration: theme.transitions.duration.leavingScreen,
}),
- transform: 'translate(0, 0)',
+ transform: "translate(0, 0)",
},
appBarWithCookieInfo: {
transform: `translate(0, ${theme.spacing(6)}px)`,
},
appBarShift: {
width: `calc(100% - ${drawerWidth}px)`,
- transition: theme.transitions.create(['margin', 'width'], {
+ transition: theme.transitions.create(["margin", "width"], {
easing: theme.transitions.easing.easeOut,
duration: theme.transitions.duration.enteringScreen,
}),
@@ -39,25 +38,37 @@ const styles = theme => ({
marginRight: 20,
},
hide: {
- display: 'none',
+ display: "none",
},
flex: {
flex: 1,
},
title: {
- cursor: 'pointer',
- '&:hover': {
- textDecoration: 'underline',
+ cursor: "pointer",
+ "&:hover": {
+ textDecoration: "underline",
},
},
-})
+});
-const Header = ({
+type HeaderProps = {
+ isOpen?: boolean;
+ title: string;
+ isFixed?: boolean;
+ isCookieInfoOpen?: boolean;
+ onDrawerOpen: (...args: any[]) => any;
+ onClick: (...args: any[]) => any;
+ classes: {
+ [key: string]: string;
+ };
+};
+
+const Header: React.SFC = ({
title,
isOpen,
- isFixed,
+ isFixed = false,
onDrawerOpen,
- isCookieInfoOpen,
+ isCookieInfoOpen = false,
onClick,
children,
classes,
@@ -68,7 +79,7 @@ const Header = ({
[classes.appBarShift]: isOpen,
[classes.appBarWithCookieInfo]: isCookieInfoOpen,
})}
- position={isFixed ? 'fixed' : 'static'}
+ position={isFixed ? "fixed" : "static"}
>
-)
-
-Header.propTypes = {
- isOpen: PropTypes.bool,
- title: PropTypes.string.isRequired,
- isFixed: PropTypes.bool.isRequired,
- isCookieInfoOpen: PropTypes.bool.isRequired,
- onDrawerOpen: PropTypes.func.isRequired,
- onClick: PropTypes.func.isRequired,
- children: PropTypes.node,
- classes: PropTypes.objectOf(PropTypes.string).isRequired,
-}
+);
Header.defaultProps = {
isOpen: false,
- children: null,
-}
+};
-export default withStyles(styles)(Header)
+export default withStyles(styles)(Header);
diff --git a/src/Header/index.js b/src/Header/index.js
deleted file mode 100644
index 7cd29d7..0000000
--- a/src/Header/index.js
+++ /dev/null
@@ -1,3 +0,0 @@
-import Header from './Header'
-
-export default Header
diff --git a/src/Header/index.ts b/src/Header/index.ts
new file mode 100644
index 0000000..0d87fe1
--- /dev/null
+++ b/src/Header/index.ts
@@ -0,0 +1,3 @@
+import Header from "./Header";
+
+export default Header;
diff --git a/src/Listing/Listing.jsx b/src/Listing/Listing.jsx
deleted file mode 100644
index ba40426..0000000
--- a/src/Listing/Listing.jsx
+++ /dev/null
@@ -1,401 +0,0 @@
-import React, { Fragment } from 'react'
-import PropTypes from 'prop-types'
-import keycode from 'keycode'
-import Uuid from 'init-uuid'
-import Store from 'vanilla-store'
-
-import easeInOutQuad from '../utils/easeInOutQuad'
-import ListingBranch from './ListingBranch'
-
-const getStringContent = (content) => {
- let matchContent = content
-
- if (content.constructor === Array) {
- matchContent = content.join(' ')
- } else if (typeof content === 'object') {
- matchContent = Object.values(content).join(' ')
- }
-
- return matchContent.toLowerCase()
-}
-
-const tryToMatch = (value, content) => {
- let initialContent = content
-
- if (!content) {
- return false
- }
-
- if (content.highlight) {
- initialContent = content.value
- }
-
- const contentToSearch = getStringContent(initialContent)
-
- if (contentToSearch.indexOf(value) > -1) {
- return true
- }
-
- return false
-}
-
-const getSearchableHeaders = headers => headers
- .filter(header => header.isSearchable)
- .map(header => header.id)
-
-
-const filterElement = (element, value, searchables) => {
- const searchValue = value.toLowerCase()
- let newElement
-
- Object.keys(element).forEach((key) => {
- if (searchables.indexOf(key) > -1) {
- const matched = tryToMatch(searchValue, element[key])
-
- if (matched) {
- newElement = element
- }
- }
- })
-
- if (newElement) {
- Object.keys(newElement).forEach((key) => {
- if (newElement[key]) {
- newElement[key] = {
- highlight: searchValue,
- value: newElement[key].highlight ? newElement[key].value : newElement[key],
- }
- }
- })
- }
-
- return newElement
-}
-const withListing = Component => class Listing extends React.Component {
- static propTypes = {
- orderBy: PropTypes.string.isRequired,
- order: PropTypes.oneOf([
- 'asc',
- 'desc',
- ]),
- hasLoader: PropTypes.bool,
- data: PropTypes.arrayOf(PropTypes.object).isRequired,
- headers: PropTypes.arrayOf(PropTypes.object).isRequired,
- toolbarContent: PropTypes.node,
- onUpdateSelection: PropTypes.func,
- id: PropTypes.string,
- }
-
- static defaultProps = {
- id: new Uuid().get(),
- order: 'asc',
- hasLoader: false,
- toolbarContent: (),
- onUpdateSelection: () => { },
- }
-
- constructor(props, context) {
- super(props, context)
-
- this.state = {
- order: 'asc',
- orderBy: 'id',
- selected: [],
- data: [],
- page: 0,
- rowsPerPage: 10,
- searchable: [],
- origData: null,
- id: null,
- }
-
- this.node = React.createRef()
-
- this.handleRequestSort = this.handleRequestSort.bind(this)
- this.handleSelectAllClick = this.handleSelectAllClick.bind(this)
- this.handleKeyDown = this.handleKeyDown.bind(this)
- this.handleCheckClick = this.handleCheckClick.bind(this)
- this.handleChangeRowsPerPage = this.handleChangeRowsPerPage.bind(this)
- this.handleChangePage = this.handleChangePage.bind(this)
- this.isSelected = this.isSelected.bind(this)
- this.handleFilter = this.handleFilter.bind(this)
- }
-
- componentDidMount() {
- const {
- id,
- data,
- headers,
- order,
- orderBy,
- } = this.props
-
- const storedData = Store.get('Listing', id)
- const newState = {
- data: this.sortData(data, orderBy, order),
- searchable: getSearchableHeaders(headers),
- }
-
- if (storedData) {
- if (storedData.page) {
- newState.page = storedData.page
- }
-
- if (storedData.rowsPerPage) {
- newState.rowsPerPage = storedData.rowsPerPage
- }
- }
-
- this.setState({
- ...newState,
- order,
- orderBy,
- })
- }
-
- componentWillReceiveProps({
- data,
- headers,
- orderBy,
- order,
- }) {
- this.setState({
- data: this.sortData(data, orderBy, order),
- orderBy,
- order,
- searchable: getSearchableHeaders(headers),
- })
- }
-
- sortData(data, orderBy, order) {
- const { headers } = this.props
- const orderByHeader = headers.filter(header => header.id === orderBy)[0]
- let { transformData } = orderByHeader
-
- if (!data) {
- return data
- }
-
- if (typeof transformData !== 'function') {
- transformData = values => values
- }
-
- const transformedData = data.map((element) => {
- const newElement = element
- newElement[orderBy] = transformData(element[orderBy])
-
- return newElement
- })
- const sortedData = transformedData.sort((a, b) => (a[orderBy] < b[orderBy] ? -1 : 1))
-
- if (order === 'asc') {
- return sortedData
- }
-
- return sortedData.reverse()
- }
-
- handleRequestSort(event, property) {
- const {
- orderBy: orderByState,
- order: orderState,
- data: dataState,
- } = this.state
- const orderBy = property
- let order = 'desc'
-
- if (orderByState === property && orderState === 'desc') {
- order = 'asc'
- }
-
- let data
-
- if (order === 'desc') {
- data = dataState.sort((a, b) => (b[orderBy] < a[orderBy] ? -1 : 1))
- } else {
- data = dataState.sort((a, b) => (a[orderBy] < b[orderBy] ? -1 : 1))
- }
-
- this.setState({
- data,
- order,
- orderBy,
- })
- }
-
- handleSelectAllClick(event, checked) {
- const { data } = this.state
-
- if (checked) {
- this.setState({
- selected: data.map(n => n.id),
- })
-
- return
- }
-
- this.setState({
- selected: [],
- })
- }
-
- handleKeyDown(event, id) {
- if (keycode(event) === 'space') {
- this.handleCheckClick(event, id)
- }
- }
-
- handleCheckClick(id) {
- const { onUpdateSelection } = this.props
- const { selected } = this.state
- const selectedIndex = selected.indexOf(id)
- let newSelected = []
-
- if (selectedIndex === -1) {
- newSelected = newSelected.concat(selected, id)
- } else if (selectedIndex === 0) {
- newSelected = newSelected.concat(selected.slice(1))
- } else if (selectedIndex === selected.length - 1) {
- newSelected = newSelected.concat(selected.slice(0, -1))
- } else if (selectedIndex > 0) {
- newSelected = newSelected.concat(
- selected.slice(0, selectedIndex),
- selected.slice(selectedIndex + 1),
- )
- }
-
- onUpdateSelection(newSelected)
-
- this.setState({
- selected: newSelected,
- })
- }
-
- scrollToElement() {
- if (!this.node.current) {
- return
- }
-
- const { offsetTop } = this.node.current
- const start = window.scrollY
- const change = offsetTop - start
- const increment = 20
- const duration = 250
- let currentTime = 0
-
- const animateScroll = () => {
- currentTime += increment
- const val = easeInOutQuad(currentTime, start, change, duration)
- window.scrollTo(0, val)
-
- if (currentTime < duration) {
- setTimeout(animateScroll, increment)
- }
- }
-
- animateScroll()
- }
-
- handleChangePage(event, page) {
- const { id } = this.props
- const { rowsPerPage } = this.state
-
- Store.create('Listing', {
- id,
- rowsPerPage,
- page,
- })
-
- this.scrollToElement()
-
- this.setState({
- page,
- })
- }
-
- handleChangeRowsPerPage(event) {
- const rowsPerPage = event.target.value
- const { id, page } = this.state
-
- Store.create('Listing', {
- id,
- page,
- rowsPerPage,
- })
-
- this.setState({
- rowsPerPage,
- })
- }
-
- handleFilter(value) {
- const { searchable, origData, data } = this.state
- let searchableData
-
- if (origData && origData.constructor === Array) {
- searchableData = [...origData]
- } else {
- searchableData = [...data]
- }
-
- if (!value) {
- searchableData = searchableData.map((item) => {
- const newItem = item
-
- Object.keys(item).forEach((key) => {
- if (item[key]) {
- if (item[key].value) {
- newItem[key] = item[key].value
- } else {
- newItem[key] = item[key]
- }
- }
- })
-
- return newItem
- })
-
- this.setState({
- data: searchableData,
- })
-
- return
- }
-
- const newData = searchableData
- .map(element => filterElement(element, value, searchable))
- .filter(item => item !== undefined)
-
- this.setState({
- data: newData,
- origData: searchableData,
- })
- }
-
- isSelected(id) {
- const { selected } = this.state
-
- return selected.indexOf(id) !== -1
- }
-
- render() {
- return (
-
-
-
- )
- }
-}
-
-export default withListing(ListingBranch)
diff --git a/src/Listing/Listing.test.jsx b/src/Listing/Listing.test.jsx
deleted file mode 100644
index 346e98e..0000000
--- a/src/Listing/Listing.test.jsx
+++ /dev/null
@@ -1,243 +0,0 @@
-import React from 'react'
-import Enzyme, { shallow } from 'enzyme'
-import Adapter from 'enzyme-adapter-react-16'
-import {
- Checkbox,
- TablePagination,
- IconButton,
-} from '@material-ui/core'
-
-import Listing from '.'
-
-import headers from '../tests/data/listing_headers'
-import data from '../tests/data/listing_data'
-import ListingHeader from './ListingHeader'
-
-Enzyme.configure({ adapter: new Adapter() })
-
-describe('Listing', () => {
- it('renders correctly', () => {
- const tree = shallow((
- { }}
- onUpdateSelection={() => { }}
- />
- ))
-
- expect(tree).toMatchSnapshot()
- })
-
- it('renders correctly with Loader', () => {
- const tree = shallow((
- { }}
- hasLoader
- onUpdateSelection={() => { }}
- />
- ))
-
- expect(tree).toMatchSnapshot()
- })
-
- it('changes props', () => {
- const origSortData = Listing.prototype.sortData
- Listing.prototype.sortData = jest.fn()
-
- const listing = shallow((
- { }}
- onUpdateSelection={() => { }}
- />
- ))
-
- listing.setProps({
- orderBy: 'name',
- data: null,
- })
-
- expect(Listing.prototype.sortData).toHaveBeenCalled()
- Listing.prototype.sortData = origSortData
- })
-
- it('does a new sorting', () => {
- const listing = shallow((
- { }}
- onUpdateSelection={() => { }}
- />
- ))
-
- expect(listing.state().data[0].username).toBe('Antonette')
-
- listing.instance().handleRequestSort({}, 'name')
- expect(listing.state().data[0].username).toBe('Karianne')
-
- listing.instance().handleRequestSort({}, 'name')
- expect(listing.state().data[0].username).toBe('Kamren')
- })
-
- it('handles click on a checkbox', () => {
- const onUpdateSelection = jest.fn()
- const listing = shallow((
- { }}
- onUpdateSelection={onUpdateSelection}
- />
- ))
-
- listing.instance().handleCheckClick('foo')
- expect(listing.state().selected).toEqual(['foo'])
- expect(onUpdateSelection).toHaveBeenCalled()
-
- listing.instance().handleCheckClick('bar')
- expect(listing.state().selected).toEqual(['foo', 'bar'])
-
- listing.instance().handleCheckClick('baz')
- expect(listing.state().selected).toEqual(['foo', 'bar', 'baz'])
-
- listing.instance().handleCheckClick('bar')
- expect(listing.state().selected).toEqual(['foo', 'baz'])
-
- listing.instance().handleCheckClick('baz')
- expect(listing.state().selected).toEqual(['foo'])
-
- listing.instance().handleCheckClick('foo')
- expect(listing.state().selected).toEqual([])
- })
-
- it('allows to filter', () => {
- const listing = shallow((
- { }}
- />
- ))
-
- const { length } = listing.state().data
-
- listing.instance().handleFilter('')
-
- expect(listing.state().data.length).toEqual(length)
-
- listing.instance().handleFilter('f0ooasdnajsbhdhuq2871dasd')
-
- expect(listing.state().data.length).toEqual(0)
- })
-
- it('allows to filter even if there are empty filters', () => {
- data[0].name = null
-
- const listing = shallow((
- { }}
- />
- ))
-
- const { length } = listing.state().data
-
- listing.instance().handleFilter('')
-
- expect(listing.state().data.length).toEqual(length)
-
- listing.instance().handleFilter('f0ooasdnajsbhdhuq2871dasd')
-
- expect(listing.state().data.length).toEqual(0)
- })
-
- it('selects all elements when checkbox is clicked', () => {
- const listing = shallow((
- { }}
- />
- ))
-
- listing.find(ListingHeader).find(Checkbox).props().onChange({}, true)
-
- expect(listing.state().selected.length).toBe(data.length)
- })
-
- it('deselects all elements when checkbox is unset', () => {
- const listing = shallow((
- { }}
- />
- ))
-
- listing.find(ListingHeader).find(Checkbox).props().onChange({}, false)
-
- expect(listing.state().selected.length).toBe(0)
- })
-
- it('handles changing the no of rows per age', () => {
- const listing = shallow((
- { }}
- />
- ))
-
- listing.find(TablePagination).props().onChangeRowsPerPage({
- target: {
- value: 100,
- },
- })
-
- expect(listing.state().rowsPerPage).toBe(100)
- })
-
- it('handles changing the page', () => {
- window.scrollTo = () => {}
-
- const listing = shallow((
- { }}
- />
- ))
-
- listing.find(TablePagination).find(IconButton).last().simulate('click')
-
- expect(listing.state().page).toBe(1)
- })
-})
diff --git a/src/Listing/Listing.test.tsx b/src/Listing/Listing.test.tsx
new file mode 100644
index 0000000..7a7f429
--- /dev/null
+++ b/src/Listing/Listing.test.tsx
@@ -0,0 +1,244 @@
+import React from "react";
+import Enzyme, { shallow } from "enzyme";
+import Adapter from "enzyme-adapter-react-16";
+import { Checkbox, TablePagination, IconButton } from "@material-ui/core";
+
+import Listing from ".";
+
+import headers from "../tests/data/listing_headers";
+import data from "../tests/data/listing_data";
+import ListingHeader from "./ListingHeader";
+
+Enzyme.configure({ adapter: new Adapter() });
+
+describe("Listing", () => {
+ it("renders correctly", () => {
+ const tree = shallow(
+ {}}
+ onUpdateSelection={() => {}}
+ />,
+ );
+
+ expect(tree).toMatchSnapshot();
+ });
+
+ it("renders correctly with Loader", () => {
+ const tree = shallow(
+ {}}
+ hasLoader
+ onUpdateSelection={() => {}}
+ />,
+ );
+
+ expect(tree).toMatchSnapshot();
+ });
+
+ it("changes props", () => {
+ const origSortData = Listing.prototype.sortData;
+ Listing.prototype.sortData = jest.fn();
+
+ const listing = shallow(
+ {}}
+ onUpdateSelection={() => {}}
+ />,
+ );
+
+ listing.setProps({
+ orderBy: "name",
+ data: null,
+ });
+
+ expect(Listing.prototype.sortData).toHaveBeenCalled();
+ Listing.prototype.sortData = origSortData;
+ });
+
+ it("does a new sorting", () => {
+ const listing = shallow(
+ {}}
+ onUpdateSelection={() => {}}
+ />,
+ );
+
+ expect(listing.state().data[0].username).toBe("Antonette");
+
+ listing.instance().handleRequestSort({}, "name");
+ expect(listing.state().data[0].username).toBe("Karianne");
+
+ listing.instance().handleRequestSort({}, "name");
+ expect(listing.state().data[0].username).toBe("Kamren");
+ });
+
+ it("handles click on a checkbox", () => {
+ const onUpdateSelection = jest.fn();
+ const listing = shallow(
+ {}}
+ onUpdateSelection={onUpdateSelection}
+ />,
+ );
+
+ listing.instance().handleCheckClick("foo");
+ expect(listing.state().selected).toEqual(["foo"]);
+ expect(onUpdateSelection).toHaveBeenCalled();
+
+ listing.instance().handleCheckClick("bar");
+ expect(listing.state().selected).toEqual(["foo", "bar"]);
+
+ listing.instance().handleCheckClick("baz");
+ expect(listing.state().selected).toEqual(["foo", "bar", "baz"]);
+
+ listing.instance().handleCheckClick("bar");
+ expect(listing.state().selected).toEqual(["foo", "baz"]);
+
+ listing.instance().handleCheckClick("baz");
+ expect(listing.state().selected).toEqual(["foo"]);
+
+ listing.instance().handleCheckClick("foo");
+ expect(listing.state().selected).toEqual([]);
+ });
+
+ it("allows to filter", () => {
+ const listing = shallow(
+ {}}
+ />,
+ );
+
+ const { length } = listing.state().data;
+
+ listing.instance().handleFilter("");
+
+ expect(listing.state().data.length).toEqual(length);
+
+ listing.instance().handleFilter("f0ooasdnajsbhdhuq2871dasd");
+
+ expect(listing.state().data.length).toEqual(0);
+ });
+
+ it("allows to filter even if there are empty filters", () => {
+ data[0].name = null;
+
+ const listing = shallow(
+ {}}
+ />,
+ );
+
+ const { length } = (listing.state() as any).data;
+
+ (listing.instance() as any).handleFilter("");
+
+ expect((listing.state() as any).data.length).toEqual(length);
+
+ (listing.instance() as any).handleFilter("f0ooasdnajsbhdhuq2871dasd");
+
+ expect((listing.state() as any).data.length).toEqual(0);
+ });
+
+ it("selects all elements when checkbox is clicked", () => {
+ const listing = shallow(
+ {}}
+ />,
+ );
+
+ const { onChange } = listing.find(ListingHeader).find(Checkbox).props();
+ onChange && onChange({}, true);
+
+ expect((listing.state() as any).selected.length).toBe(data.length);
+ });
+
+ it("deselects all elements when checkbox is unset", () => {
+ const listing = shallow(
+ {}}
+ />,
+ );
+
+ const { onChange } = listing.find(ListingHeader).find(Checkbox).props();
+
+ onChange && onChange({}, false);
+
+ expect((listing.state() as any).selected.length).toBe(0);
+ });
+
+ it("handles changing the no of rows per age", () => {
+ const listing = shallow(
+ {}}
+ />,
+ );
+
+ const { onChangeRowsPerPage } = listing.find(TablePagination).props();
+ onChangeRowsPerPage &&
+ onChangeRowsPerPage({
+ target: {
+ value: 100,
+ },
+ });
+
+ expect(listing.state().rowsPerPage).toBe(100);
+ });
+
+ it("handles changing the page", () => {
+ window.scrollTo = () => {};
+
+ const listing = shallow(
+ {}}
+ />,
+ );
+
+ listing.find(TablePagination).find(IconButton).last().simulate("click");
+
+ expect(listing.state().page).toBe(1);
+ });
+});
diff --git a/src/Listing/Listing.tsx b/src/Listing/Listing.tsx
new file mode 100644
index 0000000..33bc178
--- /dev/null
+++ b/src/Listing/Listing.tsx
@@ -0,0 +1,372 @@
+import React from "react";
+import keycode from "keycode";
+import Store from "vanilla-store";
+import easeInOutQuad from "../utils/easeInOutQuad";
+import ListingBranch from "./ListingBranch";
+
+const getStringContent = (content: any) => {
+ let matchContent = content;
+ if (content.constructor === Array) {
+ matchContent = content.join(" ");
+ } else if (typeof content === "object") {
+ matchContent = Object.values(content).join(" ");
+ }
+ return matchContent.toLowerCase();
+};
+
+const tryToMatch = (value: any, content: any) => {
+ let initialContent = content;
+ if (!content) {
+ return false;
+ }
+ if (content.highlight) {
+ initialContent = content.value;
+ }
+ const contentToSearch = getStringContent(initialContent);
+ if (contentToSearch.indexOf(value) > -1) {
+ return true;
+ }
+ return false;
+};
+
+const getSearchableHeaders = (headers: Record[]) =>
+ headers.filter((header) => header.isSearchable).map((header) => header.id);
+
+const filterElement = (
+ element: Record,
+ value: any,
+ searchables: any,
+) => {
+ const searchValue = value.toLowerCase();
+ let newElement: Record | undefined = undefined;
+
+ Object.keys(element).forEach((key) => {
+ if (searchables.indexOf(key) > -1) {
+ const matched = tryToMatch(searchValue, element[key]);
+ if (matched) {
+ newElement = element;
+ }
+ }
+ });
+
+ if (newElement) {
+ Object.keys(newElement).forEach((key: any) => {
+ if (newElement && newElement[key]) {
+ newElement[key] = {
+ highlight: searchValue,
+ value: newElement[key].highlight
+ ? newElement[key].value
+ : newElement[key],
+ };
+ }
+ });
+ }
+
+ return newElement;
+};
+
+type ListingProps = {
+ orderBy: string;
+ order?: "asc" | "desc";
+ hasLoader?: boolean;
+ data: object[];
+ headers: object[];
+ toolbarContent?: React.ReactNode;
+ onUpdateSelection?: (...args: any[]) => any;
+ id?: string;
+ onClick?: (...args: any[]) => any;
+ title?: string;
+ isIntegated?: boolean;
+};
+
+type ListingState = {
+ order?: string;
+ orderBy?: string;
+ data: any[];
+ searchable: any[];
+ page: number;
+ rowsPerPage?: number;
+ origData: any;
+ selected: any[];
+ id: null;
+};
+
+const withListing = (Component: any) =>
+ class Listing extends React.Component {
+ private node: any;
+
+ constructor(props: ListingProps, context: any) {
+ super(props, context);
+ this.state = {
+ order: "asc",
+ orderBy: "id",
+ selected: [],
+ data: [],
+ page: 0,
+ rowsPerPage: 10,
+ searchable: [],
+ origData: null,
+ id: null,
+ };
+ this.node = React.createRef();
+ this.handleRequestSort = this.handleRequestSort.bind(this);
+ this.handleSelectAllClick = this.handleSelectAllClick.bind(this);
+ this.handleKeyDown = this.handleKeyDown.bind(this);
+ this.handleCheckClick = this.handleCheckClick.bind(this);
+ this.handleChangeRowsPerPage = this.handleChangeRowsPerPage.bind(this);
+ this.handleChangePage = this.handleChangePage.bind(this);
+ this.isSelected = this.isSelected.bind(this);
+ this.handleFilter = this.handleFilter.bind(this);
+ }
+
+ componentDidMount() {
+ const { id, data, headers, order, orderBy } = this.props;
+ const storedData = Store.get("Listing", id);
+ const newState: any = {
+ data: this.sortData(data, orderBy, order),
+ searchable: getSearchableHeaders(headers),
+ };
+ if (storedData) {
+ if (storedData.page) {
+ newState.page = storedData.page;
+ }
+ if (storedData.rowsPerPage) {
+ newState.rowsPerPage = storedData.rowsPerPage;
+ }
+ }
+ this.setState({
+ ...newState,
+ order,
+ orderBy,
+ });
+ }
+
+ UNSAFE_componentWillReceiveProps({
+ data,
+ headers,
+ orderBy,
+ order,
+ }: ListingProps) {
+ this.setState({
+ data: this.sortData(data, orderBy, order),
+ orderBy,
+ order,
+ searchable: getSearchableHeaders(headers),
+ });
+ }
+
+ sortData(data: any, orderBy: any, order: any) {
+ const { headers } = this.props;
+ const orderByHeader: any = headers.filter(
+ (header: any) => header.id === orderBy,
+ )[0];
+
+ let { transformData } = orderByHeader;
+
+ if (!data) {
+ return data;
+ }
+
+ if (typeof transformData !== "function") {
+ transformData = (values: any) => values;
+ }
+
+ const transformedData = data.map((element: any) => {
+ const newElement = element;
+ newElement[orderBy] = transformData(element[orderBy]);
+ return newElement;
+ });
+
+ const sortedData = transformedData.sort((a: any, b: any) =>
+ a[orderBy] < b[orderBy] ? -1 : 1,
+ );
+
+ if (order === "asc") {
+ return sortedData;
+ }
+
+ return sortedData.reverse();
+ }
+
+ handleRequestSort(event: any, property: any) {
+ const {
+ orderBy: orderByState,
+ order: orderState,
+ data: dataState,
+ } = this.state;
+ const orderBy = property;
+ let order = "desc";
+ if (orderByState === property && orderState === "desc") {
+ order = "asc";
+ }
+ let data;
+ if (order === "desc") {
+ data = dataState.sort((a, b) => (b[orderBy] < a[orderBy] ? -1 : 1));
+ } else {
+ data = dataState.sort((a, b) => (a[orderBy] < b[orderBy] ? -1 : 1));
+ }
+ this.setState({
+ data,
+ order,
+ orderBy,
+ });
+ }
+
+ handleSelectAllClick(event: any, checked: any) {
+ const { data } = this.state;
+
+ if (checked) {
+ this.setState({
+ selected: data.map((n: any) => n.id),
+ });
+ return;
+ }
+
+ this.setState({
+ selected: [],
+ });
+ }
+
+ handleKeyDown(event: any, id: string) {
+ if (keycode(event) === "space") {
+ this.handleCheckClick(id);
+ }
+ }
+
+ handleCheckClick(id: string) {
+ const { onUpdateSelection } = this.props;
+ const { selected } = this.state;
+ const selectedIndex = selected.indexOf(id);
+
+ let newSelected: string[] = [];
+
+ if (selectedIndex === -1) {
+ newSelected = newSelected.concat(selected, id);
+ } else if (selectedIndex === 0) {
+ newSelected = newSelected.concat(selected.slice(1));
+ } else if (selectedIndex === selected.length - 1) {
+ newSelected = newSelected.concat(selected.slice(0, -1));
+ } else if (selectedIndex > 0) {
+ newSelected = newSelected.concat(
+ selected.slice(0, selectedIndex),
+ selected.slice(selectedIndex + 1),
+ );
+ }
+
+ onUpdateSelection && onUpdateSelection(newSelected);
+
+ this.setState({
+ selected: newSelected,
+ });
+ }
+
+ scrollToElement() {
+ if (!this.node.current) {
+ return;
+ }
+ const { offsetTop } = this.node.current;
+ const start = window.scrollY;
+ const change = offsetTop - start;
+ const increment = 20;
+ const duration = 250;
+ let currentTime = 0;
+ const animateScroll = () => {
+ currentTime += increment;
+ const val = easeInOutQuad(currentTime, start, change, duration);
+ window.scrollTo(0, val);
+ if (currentTime < duration) {
+ setTimeout(animateScroll, increment);
+ }
+ };
+ animateScroll();
+ }
+
+ handleChangePage(event: any, page: any) {
+ const { id } = this.props;
+ const { rowsPerPage } = this.state;
+ Store.create("Listing", {
+ id,
+ rowsPerPage,
+ page,
+ });
+ this.scrollToElement();
+ this.setState({
+ page,
+ });
+ }
+
+ handleChangeRowsPerPage(event: any) {
+ const rowsPerPage = event.target.value;
+ const { id, page } = this.state;
+ Store.create("Listing", {
+ id,
+ page,
+ rowsPerPage,
+ });
+ this.setState({
+ rowsPerPage,
+ });
+ }
+
+ handleFilter(value: any) {
+ const { searchable, origData, data } = this.state;
+ let searchableData;
+ if (origData && origData.constructor === Array) {
+ searchableData = [...origData];
+ } else {
+ searchableData = [...data];
+ }
+ if (!value) {
+ searchableData = searchableData.map((item) => {
+ const newItem = item;
+ Object.keys(item).forEach((key) => {
+ if (item[key]) {
+ if (item[key].value) {
+ newItem[key] = item[key].value;
+ } else {
+ newItem[key] = item[key];
+ }
+ }
+ });
+ return newItem;
+ });
+
+ this.setState({
+ data: searchableData,
+ });
+
+ return;
+ }
+ const newData = searchableData
+ .map((element) => filterElement(element, value, searchable))
+ .filter((item) => item !== undefined);
+ this.setState({
+ data: newData,
+ origData: searchableData,
+ });
+ }
+ isSelected(id: string) {
+ const { selected } = this.state;
+ return selected.indexOf(id) !== -1;
+ }
+ render() {
+ return (
+
+
+
+ );
+ }
+ };
+
+export default withListing(ListingBranch) as any;
diff --git a/src/Listing/ListingBranch.jsx b/src/Listing/ListingBranch.jsx
deleted file mode 100644
index f958ecb..0000000
--- a/src/Listing/ListingBranch.jsx
+++ /dev/null
@@ -1,153 +0,0 @@
-import React from 'react'
-import PropTypes from 'prop-types'
-
-import {
- Table,
- TableBody,
- TableFooter,
- TablePagination,
- TableRow,
- Paper,
- withStyles,
-} from '@material-ui/core'
-
-import ListingHeader from './ListingHeader'
-import ListingToolbar from './ListingToolbar'
-import ListingLine from './ListingLine'
-import ListingLoader from './ListingLoader'
-
-const styles = theme => ({
- root: {
- width: '100%',
- marginTop: theme.spacing(3),
- },
- table: {
- minWidth: 800,
- },
- tableWrapper: {
- overflowX: 'auto',
- },
-})
-
-const ListingBranch = ({
- title,
- headers,
- classes,
- data,
- order,
- orderBy,
- selected,
- rowsPerPage,
- rowsPerPageOptions,
- page,
- toolbarContent,
- handleSelectAllClick,
- handleRequestSort,
- handleCheckClick,
- onClick,
- handleKeyDown,
- handleChangePage,
- handleChangeRowsPerPage,
- isSelected,
- onFilter,
- hasLoader,
- isIntegrated,
-}) => (
-
-
- {toolbarContent}
-
-
-
-
-
-
-
-
- {hasLoader ? (
-
- ) : null}
-
- {data.slice(page * rowsPerPage, (page * rowsPerPage) + rowsPerPage).map(n => (
-
- ))}
-
-
-
-
-
-
-
-
-
-)
-
-ListingBranch.propTypes = {
- title: PropTypes.string,
- data: PropTypes.arrayOf(PropTypes.object),
- headers: PropTypes.arrayOf(PropTypes.object).isRequired,
- order: PropTypes.string,
- orderBy: PropTypes.string.isRequired,
- selected: PropTypes.arrayOf(PropTypes.string),
- rowsPerPage: PropTypes.number,
- rowsPerPageOptions: PropTypes.arrayOf(PropTypes.number),
- page: PropTypes.number,
- hasLoader: PropTypes.bool.isRequired,
- toolbarContent: PropTypes.node.isRequired,
- handleSelectAllClick: PropTypes.func.isRequired,
- handleRequestSort: PropTypes.func.isRequired,
- handleCheckClick: PropTypes.func.isRequired,
- onClick: PropTypes.func.isRequired,
- handleKeyDown: PropTypes.func.isRequired,
- handleChangePage: PropTypes.func.isRequired,
- handleChangeRowsPerPage: PropTypes.func.isRequired,
- isSelected: PropTypes.func,
- onFilter: PropTypes.func,
- isIntegrated: PropTypes.bool,
- classes: PropTypes.objectOf(PropTypes.string).isRequired,
-}
-
-ListingBranch.defaultProps = {
- title: '',
- data: [],
- order: 'asc',
- selected: [],
- rowsPerPage: 10,
- rowsPerPageOptions: [10, 25, 50, 100],
- page: 0,
- onFilter: () => {},
- isSelected: () => {},
- isIntegrated: false,
-}
-
-export default withStyles(styles)(ListingBranch)
diff --git a/src/Listing/ListingBranch.tsx b/src/Listing/ListingBranch.tsx
new file mode 100644
index 0000000..267bbfa
--- /dev/null
+++ b/src/Listing/ListingBranch.tsx
@@ -0,0 +1,146 @@
+import React from "react";
+import {
+ Table,
+ TableBody,
+ TableFooter,
+ TablePagination,
+ TableRow,
+ Paper,
+ withStyles,
+} from "@material-ui/core";
+import ListingHeader from "./ListingHeader";
+import ListingToolbar from "./ListingToolbar";
+import ListingLine from "./ListingLine";
+import ListingLoader from "./ListingLoader";
+const styles = (theme: any) => ({
+ root: {
+ width: "100%",
+ marginTop: theme.spacing(3),
+ },
+ table: {
+ minWidth: 800,
+ },
+ tableWrapper: {
+ overflowX: "auto",
+ },
+});
+
+type ListingBranchProps = {
+ title?: string;
+ data?: object[];
+ headers: object[];
+ order?: "asc" | "desc";
+ orderBy: string;
+ selected?: string[];
+ rowsPerPage?: number;
+ rowsPerPageOptions?: number[];
+ page?: number;
+ hasLoader: boolean;
+ toolbarContent: React.ReactNode;
+ handleSelectAllClick: (...args: any[]) => any;
+ handleRequestSort: (...args: any[]) => any;
+ handleCheckClick: (...args: any[]) => any;
+ onClick: (...args: any[]) => any;
+ handleKeyDown: (...args: any[]) => any;
+ handleChangePage: (...args: any[]) => any;
+ handleChangeRowsPerPage: (...args: any[]) => any;
+ isSelected?: (...args: any[]) => any;
+ onFilter?: (...args: any[]) => any;
+ isIntegrated?: boolean;
+ classes: {
+ [key: string]: string;
+ };
+};
+
+const ListingBranch: React.SFC = ({
+ title,
+ headers,
+ classes,
+ data,
+ order = "asc",
+ orderBy,
+ selected,
+ rowsPerPage = 10,
+ rowsPerPageOptions = [10, 25, 50, 100],
+ page = 0,
+ toolbarContent,
+ handleSelectAllClick,
+ handleRequestSort,
+ handleCheckClick,
+ onClick,
+ handleKeyDown,
+ handleChangePage,
+ handleChangeRowsPerPage,
+ isSelected,
+ onFilter,
+ hasLoader,
+ isIntegrated,
+}) => (
+
+
+ {toolbarContent}
+
+
+
+
+
+
+
+ {hasLoader ? : null}
+
+ {data
+ ?.slice(page * rowsPerPage, page * rowsPerPage + rowsPerPage)
+ .map((item: any) => (
+
+ ))}
+
+
+
+
+
+
+
+
+
+);
+
+ListingBranch.defaultProps = {
+ title: "",
+ data: [],
+ selected: [],
+ onFilter: () => {},
+ isSelected: () => {},
+ isIntegrated: false,
+};
+
+export default withStyles(styles as any)(ListingBranch);
diff --git a/src/Listing/ListingHeader.jsx b/src/Listing/ListingHeader.jsx
deleted file mode 100644
index ff5a578..0000000
--- a/src/Listing/ListingHeader.jsx
+++ /dev/null
@@ -1,80 +0,0 @@
-import React from 'react'
-import PropTypes from 'prop-types'
-import {
- Checkbox,
- TableCell,
- TableHead,
- TableRow,
- TableSortLabel,
- Tooltip,
-} from '@material-ui/core'
-
-class ListingHeader extends React.Component {
- createSortHandler(property) {
- const { onRequestSort } = this.props
-
- return (event) => {
- onRequestSort(event, property)
- }
- }
-
- render() {
- const {
- onSelectAllClick,
- order,
- orderBy,
- numSelected,
- rowCount,
- headers,
- } = this.props
-
- return (
-
-
-
- 0 && numSelected < rowCount}
- checked={numSelected === rowCount}
- onChange={onSelectAllClick}
- />
-
-
- {headers.map(column => (
-
-
-
- {column.label}
-
-
-
- ), this)}
-
-
- )
- }
-}
-
-ListingHeader.propTypes = {
- headers: PropTypes.arrayOf(PropTypes.object).isRequired,
- numSelected: PropTypes.number.isRequired,
- onRequestSort: PropTypes.func.isRequired,
- onSelectAllClick: PropTypes.func.isRequired,
- order: PropTypes.string.isRequired,
- orderBy: PropTypes.string.isRequired,
- rowCount: PropTypes.number.isRequired,
-}
-
-export default ListingHeader
diff --git a/src/Listing/ListingHeader.test.jsx b/src/Listing/ListingHeader.test.jsx
deleted file mode 100644
index ca166fe..0000000
--- a/src/Listing/ListingHeader.test.jsx
+++ /dev/null
@@ -1,27 +0,0 @@
-import React from 'react'
-import { shallow } from 'enzyme'
-
-import { Table } from '@material-ui/core'
-
-import ListingHeader from './ListingHeader'
-
-it('renders correctly', () => {
- const tree = shallow((
-
- {}}
- onSelectAllClick={() => { }}
- order="asc"
- orderBy="name"
- rowCount={0}
- />
-
- ))
-
- expect(tree).toMatchSnapshot()
-})
diff --git a/src/Listing/ListingHeader.test.tsx b/src/Listing/ListingHeader.test.tsx
new file mode 100644
index 0000000..a529549
--- /dev/null
+++ b/src/Listing/ListingHeader.test.tsx
@@ -0,0 +1,29 @@
+import React from "react";
+import { shallow } from "enzyme";
+
+import { Table } from "@material-ui/core";
+
+import ListingHeader from "./ListingHeader";
+
+it("renders correctly", () => {
+ const tree = shallow(
+
+ {}}
+ onSelectAllClick={() => {}}
+ order="asc"
+ orderBy="name"
+ rowCount={0}
+ />
+
,
+ );
+
+ expect(tree).toMatchSnapshot();
+});
diff --git a/src/Listing/ListingHeader.tsx b/src/Listing/ListingHeader.tsx
new file mode 100644
index 0000000..7a3e17a
--- /dev/null
+++ b/src/Listing/ListingHeader.tsx
@@ -0,0 +1,89 @@
+import React from "react";
+import {
+ Checkbox,
+ TableCell,
+ TableHead,
+ TableRow,
+ TableSortLabel,
+ Tooltip,
+} from "@material-ui/core";
+
+type ListingHeaderProps = {
+ headers: object[];
+ numSelected?: number;
+ onRequestSort: (...args: any[]) => any;
+ onSelectAllClick: (...args: any[]) => any;
+ order?: "asc" | "desc";
+ orderBy: string;
+ rowCount?: number;
+};
+
+class ListingHeader extends React.Component {
+ static defaultProps = {
+ numSelected: 0,
+ rowCount: 0,
+ };
+
+ createSortHandler(property: any) {
+ const { onRequestSort } = this.props;
+
+ return (event: any) => {
+ onRequestSort(event, property);
+ };
+ }
+
+ render() {
+ const {
+ onSelectAllClick,
+ order,
+ orderBy,
+ numSelected,
+ rowCount,
+ headers,
+ } = this.props;
+
+ return (
+
+
+
+ 0 && (numSelected || 0) < (rowCount || 0)
+ }
+ checked={numSelected === rowCount}
+ onChange={onSelectAllClick}
+ />
+
+
+ {headers.map(
+ (column: any) => (
+
+
+
+ {column.label}
+
+
+
+ ),
+ this,
+ )}
+
+
+ );
+ }
+}
+
+export default ListingHeader;
diff --git a/src/Listing/ListingLine.jsx b/src/Listing/ListingLine.jsx
deleted file mode 100644
index 82a560e..0000000
--- a/src/Listing/ListingLine.jsx
+++ /dev/null
@@ -1,108 +0,0 @@
-import React from 'react'
-import PropTypes from 'prop-types'
-import {
- Checkbox,
- TableCell,
- TableRow,
-} from '@material-ui/core'
-
-import replace from '../utils/replace'
-
-
-const getCellContent = (content, transformContent, data) => {
- let printableContent = content
-
- if (content && content.highlight) {
- printableContent = content.value
- }
-
- if (typeof transformContent === 'function') {
- printableContent = transformContent(printableContent, data)
- }
-
- const props = {}
-
- if (typeof printableContent === 'string' && content.highlight) {
- printableContent = replace(
- printableContent,
- content.highlight,
- key => `${key}`,
- )
-
- props.dangerouslySetInnerHTML = {
- __html: printableContent,
- }
-
- printableContent = undefined
- }
-
- return {
- content: printableContent,
- props,
- }
-}
-
-class ListingLine extends React.Component {
- static propTypes = {
- headers: PropTypes.arrayOf(PropTypes.object).isRequired,
- data: PropTypes.objectOf(PropTypes.any).isRequired,
- onClick: PropTypes.func.isRequired,
- isSelected: PropTypes.bool.isRequired,
- handleKeyDown: PropTypes.func.isRequired,
- handleCheckClick: PropTypes.func.isRequired,
- }
-
- renderCells() {
- const { headers, data, onClick } = this.props
-
- return headers.map((header) => {
- const {
- content,
- props,
- } = getCellContent(data[header.id], header.transformContent, data)
-
- return (
- onClick(data.id)}
- {...props}
- >
- {content}
-
- )
- })
- }
-
- render() {
- const {
- data,
- isSelected,
- handleKeyDown,
- handleCheckClick,
- } = this.props
-
- return (
- handleKeyDown(event, data.id)}
- role="checkbox"
- aria-checked={isSelected}
- tabIndex={-1}
- selected={isSelected}
- >
- handleCheckClick(data.id)}
- >
-
-
-
- {this.renderCells()}
-
- )
- }
-}
-
-export default ListingLine
diff --git a/src/Listing/ListingLine.test.jsx b/src/Listing/ListingLine.test.jsx
deleted file mode 100644
index be35ac4..0000000
--- a/src/Listing/ListingLine.test.jsx
+++ /dev/null
@@ -1,78 +0,0 @@
-import React from 'react'
-import { shallow } from 'enzyme'
-import { Table } from '@material-ui/core'
-
-import ListingLine from './ListingLine'
-
-import headers from '../tests/data/listing_headers'
-
-const twoHeaders = headers.slice(0, 2)
-
-it('renders correctly', () => {
- const tree = shallow((
-
- { }}
- handleKeyDown={() => { }}
- handleCheckClick={() => { }}
- isSelected={false}
- />
-
- ))
-
- expect(tree).toMatchSnapshot()
-})
-
-it('renders selected', () => {
- const tree = shallow((
-
- { }}
- handleKeyDown={() => { }}
- handleCheckClick={() => { }}
- isSelected
- />
-
- ))
-
- expect(tree).toMatchSnapshot()
-})
-
-it('renders highlighted', () => {
- const tree = shallow((
-
- { }}
- handleKeyDown={() => { }}
- handleCheckClick={() => { }}
- isSelected
- />
-
- ))
-
- expect(tree).toMatchSnapshot()
-})
diff --git a/src/Listing/ListingLine.test.tsx b/src/Listing/ListingLine.test.tsx
new file mode 100644
index 0000000..4352daa
--- /dev/null
+++ b/src/Listing/ListingLine.test.tsx
@@ -0,0 +1,78 @@
+import React from "react";
+import { shallow } from "enzyme";
+import { Table } from "@material-ui/core";
+
+import ListingLine from "./ListingLine";
+
+import headers from "../tests/data/listing_headers";
+
+const twoHeaders = headers.slice(0, 2);
+
+it("renders correctly", () => {
+ const tree = shallow(
+
+ {}}
+ handleKeyDown={() => {}}
+ handleCheckClick={() => {}}
+ isSelected={false}
+ />
+
,
+ );
+
+ expect(tree).toMatchSnapshot();
+});
+
+it("renders selected", () => {
+ const tree = shallow(
+
+ {}}
+ handleKeyDown={() => {}}
+ handleCheckClick={() => {}}
+ isSelected
+ />
+
,
+ );
+
+ expect(tree).toMatchSnapshot();
+});
+
+it("renders highlighted", () => {
+ const tree = shallow(
+
+ {}}
+ handleKeyDown={() => {}}
+ handleCheckClick={() => {}}
+ isSelected
+ />
+
,
+ );
+
+ expect(tree).toMatchSnapshot();
+});
diff --git a/src/Listing/ListingLine.tsx b/src/Listing/ListingLine.tsx
new file mode 100644
index 0000000..a7ee099
--- /dev/null
+++ b/src/Listing/ListingLine.tsx
@@ -0,0 +1,94 @@
+import React from "react";
+import { Checkbox, TableCell, TableRow } from "@material-ui/core";
+import replace from "../utils/replace";
+
+const getCellContent = (content: any, transformContent: any, data: any) => {
+ let printableContent = content;
+
+ if (content && content.highlight) {
+ printableContent = content.value;
+ }
+
+ if (typeof transformContent === "function") {
+ printableContent = transformContent(printableContent, data);
+ }
+
+ const props: Record = {};
+
+ if (typeof printableContent === "string" && content.highlight) {
+ printableContent = replace(
+ printableContent,
+ content.highlight,
+ (key: any) => `${key}`,
+ );
+
+ props.dangerouslySetInnerHTML = {
+ __html: printableContent,
+ };
+
+ printableContent = undefined;
+ }
+
+ return {
+ content: printableContent,
+ props,
+ };
+};
+
+type ListingLineProps = {
+ headers: object[];
+ data: {
+ [key: string]: any;
+ };
+ onClick: (...args: any[]) => any;
+ isSelected: boolean;
+ handleKeyDown: (...args: any[]) => any;
+ handleCheckClick: (...args: any[]) => any;
+};
+
+class ListingLine extends React.Component {
+ renderCells() {
+ const { headers, data, onClick } = this.props;
+ return headers.map((header: any) => {
+ const { content, props } = getCellContent(
+ data[header.id],
+ header.transformContent,
+ data,
+ );
+
+ return (
+ onClick(data.id)}
+ {...props}
+ >
+ {content}
+
+ );
+ });
+ }
+
+ render() {
+ const { data, isSelected, handleKeyDown, handleCheckClick } = this.props;
+ return (
+ handleKeyDown(event, data.id)}
+ role="checkbox"
+ aria-checked={isSelected}
+ tabIndex={-1}
+ selected={isSelected}
+ >
+ handleCheckClick(data.id)}>
+
+
+
+ {this.renderCells()}
+
+ );
+ }
+}
+
+export default ListingLine;
diff --git a/src/Listing/ListingLoader.jsx b/src/Listing/ListingLoader.jsx
deleted file mode 100644
index 778b771..0000000
--- a/src/Listing/ListingLoader.jsx
+++ /dev/null
@@ -1,41 +0,0 @@
-import React from 'react'
-import PropTypes from 'prop-types'
-
-import {
- TableCell,
- TableRow,
- LinearProgress,
- withStyles,
-} from '@material-ui/core'
-
-const styles = {
- row: {
- height: 'auto',
- },
- cell: {
- border: 0,
- padding: 0,
- },
- progress: {
- width: '100%',
- },
-}
-
-const ListingLoader = ({ cols, classes }) => (
-
-
-
-
-
-)
-
-ListingLoader.propTypes = {
- cols: PropTypes.number,
- classes: PropTypes.objectOf(PropTypes.string).isRequired,
-}
-
-ListingLoader.defaultProps = {
- cols: 1,
-}
-
-export default withStyles(styles)(ListingLoader)
diff --git a/src/Listing/ListingLoader.test.jsx b/src/Listing/ListingLoader.test.jsx
deleted file mode 100644
index 897a694..0000000
--- a/src/Listing/ListingLoader.test.jsx
+++ /dev/null
@@ -1,15 +0,0 @@
-import React from 'react'
-import { shallow } from 'enzyme'
-import { Table } from '@material-ui/core'
-
-import ListingLoader from './ListingLoader'
-
-it('renders correctly', () => {
- const tree = shallow((
-
- ))
-
- expect(tree).toMatchSnapshot()
-})
diff --git a/src/Listing/ListingLoader.test.tsx b/src/Listing/ListingLoader.test.tsx
new file mode 100644
index 0000000..1e6764f
--- /dev/null
+++ b/src/Listing/ListingLoader.test.tsx
@@ -0,0 +1,15 @@
+import React from "react";
+import { shallow } from "enzyme";
+import { Table } from "@material-ui/core";
+
+import ListingLoader from "./ListingLoader";
+
+it("renders correctly", () => {
+ const tree = shallow(
+ ,
+ );
+
+ expect(tree).toMatchSnapshot();
+});
diff --git a/src/Listing/ListingLoader.tsx b/src/Listing/ListingLoader.tsx
new file mode 100644
index 0000000..71ded4e
--- /dev/null
+++ b/src/Listing/ListingLoader.tsx
@@ -0,0 +1,41 @@
+import React from "react";
+import {
+ TableCell,
+ TableRow,
+ LinearProgress,
+ withStyles,
+} from "@material-ui/core";
+
+const styles = {
+ row: {
+ height: "auto",
+ },
+ cell: {
+ border: 0,
+ padding: 0,
+ },
+ progress: {
+ width: "100%",
+ },
+};
+
+type ListingLoaderProps = {
+ cols?: number;
+ classes: {
+ [key: string]: string;
+ };
+};
+
+const ListingLoader: React.SFC = ({ cols, classes }) => (
+
+
+
+
+
+);
+
+ListingLoader.defaultProps = {
+ cols: 1,
+};
+
+export default withStyles(styles)(ListingLoader);
diff --git a/src/Listing/ListingSearch.jsx b/src/Listing/ListingSearch.jsx
deleted file mode 100644
index c642f2e..0000000
--- a/src/Listing/ListingSearch.jsx
+++ /dev/null
@@ -1,68 +0,0 @@
-import React from 'react'
-import PropTypes from 'prop-types'
-
-import ListingSearchBranch from './ListingSearchBranch'
-
-const withListing = Component => class extends React.Component {
- static propTypes = {
- onFilter: PropTypes.func.isRequired,
- }
-
- constructor() {
- super()
-
- this.handleClick = this.handleClick.bind(this)
- this.handleFilter = this.handleFilter.bind(this)
- this.getSearchRef = this.getSearchRef.bind(this)
- }
-
- state = {
- open: false,
- }
-
- getSearchRef(node) {
- this.searchRef = node
- }
-
- handleClick() {
- const { open } = this.state
- const { onFilter } = this.props
- const newOpen = !open
-
- if (this.searchRef && newOpen) {
- this.searchRef.focus()
- }
-
- this.setState({
- open: newOpen,
- })
-
- onFilter(undefined)
- }
-
- handleFilter(event) {
- const { open } = this.state
- const { onFilter } = this.props
- const { value } = event.target
-
- if (open) {
- onFilter(value)
- } else {
- onFilter(undefined)
- }
- }
-
- render() {
- return (
-
- )
- }
-}
-
-export default withListing(ListingSearchBranch)
diff --git a/src/Listing/ListingSearch.test.jsx b/src/Listing/ListingSearch.test.jsx
deleted file mode 100644
index 1d277e0..0000000
--- a/src/Listing/ListingSearch.test.jsx
+++ /dev/null
@@ -1,103 +0,0 @@
-import React from 'react'
-import Enzyme, { shallow } from 'enzyme'
-import Adapter from 'enzyme-adapter-react-16'
-
-import { IconButton } from '@material-ui/core'
-import ListingSearch from './ListingSearch'
-
-Enzyme.configure({ adapter: new Adapter() })
-
-it('renders correctly', () => {
- const tree = shallow((
- { }}
- onFilter={() => { }}
- />
- ))
-
- expect(tree).toMatchSnapshot()
-})
-
-it('renders correctly when open', () => {
- const tree = shallow((
- { }}
- onFilter={() => { }}
- />
- ))
-
- expect(tree).toMatchSnapshot()
-})
-
-it('opens if click is triggered', () => {
- const listingSearch = shallow((
- { }}
- onFilter={() => { }}
- />
- ))
-
- expect(listingSearch.instance().state.open).toEqual(false)
-
- listingSearch.find(IconButton).simulate('click')
-
- expect(listingSearch.instance().state.open).toEqual(true)
-})
-
-it('filters if open and value changes', () => {
- const onFilter = jest.fn()
- const listingSearch = shallow((
- { }}
- onFilter={onFilter}
- />
- ))
-
- listingSearch.find('input').simulate('change', { value: 'a' })
-
- expect(onFilter).toHaveBeenCalled()
-})
-
-it('does not filter if not open and value changes', () => {
- const onFilter = jest.fn()
- const listingSearch = shallow((
- { }}
- onFilter={onFilter}
- />
- ))
-
- expect(listingSearch.instance().state.open).toEqual(false)
- listingSearch.find(IconButton).simulate('click')
-
- expect(listingSearch.instance().state.open).toEqual(true)
-
- listingSearch.find('input').simulate('change', { value: 'a' })
-
- expect(onFilter).toHaveBeenCalled()
-})
-
-it('focuses search field on click', () => {
- const mockObject = {
- focus: jest.fn(),
- }
-
- const listingSearch = shallow((
- { }}
- onFilter={() => {}}
- />
- ))
-
- listingSearch.instance().searchRef = mockObject
-
- listingSearch.find(IconButton).simulate('click')
-
- expect(listingSearch.state().open).toBe(true)
-
- expect(mockObject.focus).toHaveBeenCalled()
-})
diff --git a/src/Listing/ListingSearch.test.tsx b/src/Listing/ListingSearch.test.tsx
new file mode 100644
index 0000000..4439081
--- /dev/null
+++ b/src/Listing/ListingSearch.test.tsx
@@ -0,0 +1,81 @@
+import React from "react";
+import Enzyme, { shallow } from "enzyme";
+import Adapter from "enzyme-adapter-react-16";
+
+import { IconButton } from "@material-ui/core";
+import ListingSearch from "./ListingSearch";
+
+Enzyme.configure({ adapter: new Adapter() });
+
+it("renders correctly", () => {
+ const tree = shallow(
+ {}} onFilter={() => {}} />,
+ );
+
+ expect(tree).toMatchSnapshot();
+});
+
+it("renders correctly when open", () => {
+ const tree = shallow(
+ {}} onFilter={() => {}} />,
+ );
+
+ expect(tree).toMatchSnapshot();
+});
+
+it("opens if click is triggered", () => {
+ const listingSearch = shallow(
+ {}} onFilter={() => {}} />,
+ );
+
+ expect(listingSearch.instance().state.open).toEqual(false);
+
+ listingSearch.find(IconButton).simulate("click");
+
+ expect(listingSearch.instance().state.open).toEqual(true);
+});
+
+it("filters if open and value changes", () => {
+ const onFilter = jest.fn();
+ const listingSearch = shallow(
+ {}} onFilter={onFilter} />,
+ );
+
+ listingSearch.find("input").simulate("change", { value: "a" });
+
+ expect(onFilter).toHaveBeenCalled();
+});
+
+it("does not filter if not open and value changes", () => {
+ const onFilter = jest.fn();
+ const listingSearch = shallow(
+ {}} onFilter={onFilter} />,
+ );
+
+ expect(listingSearch.instance().state.open).toEqual(false);
+ listingSearch.find(IconButton).simulate("click");
+
+ expect(listingSearch.instance().state.open).toEqual(true);
+
+ listingSearch.find("input").simulate("change", { value: "a" });
+
+ expect(onFilter).toHaveBeenCalled();
+});
+
+it("focuses search field on click", () => {
+ const mockObject = {
+ focus: jest.fn(),
+ };
+
+ const listingSearch = shallow(
+ {}} onFilter={() => {}} />,
+ );
+
+ (listingSearch.instance() as any).searchRef = mockObject;
+
+ listingSearch.find(IconButton).simulate("click");
+
+ expect((listingSearch.state() as any).open).toBe(true);
+
+ expect(mockObject.focus).toHaveBeenCalled();
+});
diff --git a/src/Listing/ListingSearch.tsx b/src/Listing/ListingSearch.tsx
new file mode 100644
index 0000000..e61a8bf
--- /dev/null
+++ b/src/Listing/ListingSearch.tsx
@@ -0,0 +1,71 @@
+import React from "react";
+import ListingSearchBranch from "./ListingSearchBranch";
+
+type WithListingSearchProps = {
+ onFilter: (...args: any[]) => any;
+};
+
+type WithListingSearchState = {
+ open: boolean;
+};
+
+const withListing = (Component: any) =>
+ class WithListingSearch extends React.Component<
+ WithListingSearchProps,
+ WithListingSearchState
+ > {
+ private searchRef: any;
+
+ constructor(props: WithListingSearchProps) {
+ super(props);
+
+ this.handleClick = this.handleClick.bind(this);
+ this.handleFilter = this.handleFilter.bind(this);
+ this.getSearchRef = this.getSearchRef.bind(this);
+ }
+ state = {
+ open: false,
+ };
+
+ getSearchRef(node: any) {
+ this.searchRef = node;
+ }
+
+ handleClick() {
+ const { open } = this.state;
+ const { onFilter } = this.props;
+ const newOpen = !open;
+ if (this.searchRef && newOpen) {
+ this.searchRef.focus();
+ }
+ this.setState({
+ open: newOpen,
+ });
+ onFilter(undefined);
+ }
+
+ handleFilter(event: any) {
+ const { open } = this.state;
+ const { onFilter } = this.props;
+ const { value } = event.target;
+ if (open) {
+ onFilter(value);
+ } else {
+ onFilter(undefined);
+ }
+ }
+
+ render() {
+ return (
+
+ );
+ }
+ };
+
+export default withListing(ListingSearchBranch) as any;
diff --git a/src/Listing/ListingSearchBranch.jsx b/src/Listing/ListingSearchBranch.jsx
deleted file mode 100644
index 656ed1d..0000000
--- a/src/Listing/ListingSearchBranch.jsx
+++ /dev/null
@@ -1,71 +0,0 @@
-import React from 'react'
-import PropTypes from 'prop-types'
-import classNames from 'classnames'
-
-import {
- TextField,
- Tooltip,
- IconButton,
- withStyles,
-} from '@material-ui/core'
-import SearchIcon from '@material-ui/icons/Search'
-
-const styles = theme => ({
- root: {
- position: 'relative',
- marginRight: theme.spacing(),
- },
- field: {
- position: 'absolute',
- right: '100%',
- width: 0,
- marginTop: theme.spacing(),
- transition: 'width 0.25s',
- },
- fieldActive: {
- width: theme.spacing(30),
- },
-})
-
-const ListingSearchBranch = ({
- open,
- placeholder,
- onClick,
- onFilter,
- getSearchRef,
- classes,
-}) => (
-
- { getSearchRef(node) },
- }}
- />
-
-
-
-
-
-
-)
-
-ListingSearchBranch.propTypes = {
- open: PropTypes.bool.isRequired,
- placeholder: PropTypes.string,
- onClick: PropTypes.func.isRequired,
- onFilter: PropTypes.func.isRequired,
- getSearchRef: PropTypes.func.isRequired,
- classes: PropTypes.objectOf(PropTypes.string).isRequired,
-}
-
-ListingSearchBranch.defaultProps = {
- placeholder: 'Filter',
-}
-
-export default withStyles(styles)(ListingSearchBranch)
diff --git a/src/Listing/ListingSearchBranch.tsx b/src/Listing/ListingSearchBranch.tsx
new file mode 100644
index 0000000..832f69d
--- /dev/null
+++ b/src/Listing/ListingSearchBranch.tsx
@@ -0,0 +1,68 @@
+import React from "react";
+import classNames from "classnames";
+import { TextField, Tooltip, IconButton, withStyles } from "@material-ui/core";
+import SearchIcon from "@material-ui/icons/Search";
+
+const styles = (theme: any) => ({
+ root: {
+ position: "relative",
+ marginRight: theme.spacing(),
+ },
+ field: {
+ position: "absolute",
+ right: "100%",
+ width: 0,
+ marginTop: theme.spacing(),
+ transition: "width 0.25s",
+ },
+ fieldActive: {
+ width: theme.spacing(30),
+ },
+});
+
+type ListingSearchBranchProps = {
+ open: boolean;
+ placeholder?: string;
+ onClick: (...args: any[]) => any;
+ onFilter: (...args: any[]) => any;
+ getSearchRef: (...args: any[]) => any;
+ classes: {
+ [key: string]: string;
+ };
+};
+
+const ListingSearchBranch: React.SFC = ({
+ open,
+ placeholder,
+ onClick,
+ onFilter,
+ getSearchRef,
+ classes,
+}) => (
+
+ {
+ getSearchRef(node);
+ },
+ }}
+ />
+
+
+
+
+
+
+);
+
+ListingSearchBranch.defaultProps = {
+ placeholder: "Filter",
+};
+
+export default withStyles(styles as any)(ListingSearchBranch);
diff --git a/src/Listing/ListingToolbar.jsx b/src/Listing/ListingToolbar.jsx
deleted file mode 100644
index bb4976b..0000000
--- a/src/Listing/ListingToolbar.jsx
+++ /dev/null
@@ -1,83 +0,0 @@
-import React, { Fragment } from 'react'
-import PropTypes from 'prop-types'
-import classNames from 'classnames'
-
-import {
- Toolbar,
- Typography,
- withStyles,
-} from '@material-ui/core'
-
-import ListingSearch from './ListingSearch'
-
-const toolbarStyles = theme => ({
- root: {
- paddingRight: 2,
- },
- highlight:
- theme.palette.type === 'light'
- ? {
- color: theme.palette.secondary.dark,
- backgroundColor: theme.palette.secondary.light,
- }
- : {
- color: theme.palette.secondary.light,
- backgroundColor: theme.palette.secondary.dark,
- },
- spacer: {
- flex: '1 1 100%',
- },
- actions: {
- color: theme.palette.text.secondary,
- },
- title: {
- flex: '0 0 auto',
- },
-})
-
-const ListingToolbar = ({
- title,
- numSelected,
- onFilter,
- classes,
- children,
-}) => (
- 0,
- })}
- >
-
- {numSelected > 0 ? (
-
- {numSelected}
- selected
-
- ) : (
- {title}
- )}
-
-
-
-
-
- {numSelected > 0 ? children : (
-
- )}
-
-
-)
-
-ListingToolbar.propTypes = {
- title: PropTypes.string.isRequired,
- numSelected: PropTypes.number.isRequired,
- onFilter: PropTypes.func.isRequired,
- classes: PropTypes.objectOf(PropTypes.string).isRequired,
- children: PropTypes.node,
-}
-
-ListingToolbar.defaultProps = {
- children: (),
-}
-
-export default withStyles(toolbarStyles)(ListingToolbar)
diff --git a/src/Listing/ListingToolbar.test.jsx b/src/Listing/ListingToolbar.test.jsx
deleted file mode 100644
index eb8923c..0000000
--- a/src/Listing/ListingToolbar.test.jsx
+++ /dev/null
@@ -1,16 +0,0 @@
-import React from 'react'
-import { shallow } from 'enzyme'
-
-import ListingToolbar from './ListingToolbar'
-
-it('renders correctly', () => {
- const tree = shallow((
- { }}
- />
- ))
-
- expect(tree).toMatchSnapshot()
-})
diff --git a/src/Listing/ListingToolbar.test.tsx b/src/Listing/ListingToolbar.test.tsx
new file mode 100644
index 0000000..290890b
--- /dev/null
+++ b/src/Listing/ListingToolbar.test.tsx
@@ -0,0 +1,12 @@
+import React from "react";
+import { shallow } from "enzyme";
+
+import ListingToolbar from "./ListingToolbar";
+
+it("renders correctly", () => {
+ const tree = shallow(
+ {}} />,
+ );
+
+ expect(tree).toMatchSnapshot();
+});
diff --git a/src/Listing/ListingToolbar.tsx b/src/Listing/ListingToolbar.tsx
new file mode 100644
index 0000000..ab0b829
--- /dev/null
+++ b/src/Listing/ListingToolbar.tsx
@@ -0,0 +1,71 @@
+import React from "react";
+import classNames from "classnames";
+import { Toolbar, Typography, withStyles } from "@material-ui/core";
+import ListingSearch from "./ListingSearch";
+
+const toolbarStyles = (theme: any) => ({
+ root: {
+ paddingRight: 2,
+ },
+ highlight:
+ theme.palette.type === "light"
+ ? {
+ color: theme.palette.secondary.dark,
+ backgroundColor: theme.palette.secondary.light,
+ }
+ : {
+ color: theme.palette.secondary.light,
+ backgroundColor: theme.palette.secondary.dark,
+ },
+ spacer: {
+ flex: "1 1 100%",
+ },
+ actions: {
+ color: theme.palette.text.secondary,
+ },
+ title: {
+ flex: "0 0 auto",
+ },
+});
+
+type ListingToolbarProps = {
+ title: string;
+ numSelected: number;
+ onFilter: (...args: any[]) => any;
+ classes: {
+ [key: string]: string;
+ };
+};
+
+const ListingToolbar: React.SFC = ({
+ title,
+ numSelected,
+ onFilter,
+ classes,
+ children,
+}) => (
+ 0,
+ })}
+ >
+
+ {numSelected > 0 ? (
+
+ {numSelected}
+ selected
+
+ ) : (
+ {title}
+ )}
+
+
+
+
+
+ {numSelected > 0 ? children : }
+
+
+);
+
+export default withStyles(toolbarStyles as any)(ListingToolbar) as any;
diff --git a/src/Listing/index.js b/src/Listing/index.js
deleted file mode 100644
index 549251b..0000000
--- a/src/Listing/index.js
+++ /dev/null
@@ -1,3 +0,0 @@
-import Listing from './Listing'
-
-export default Listing
diff --git a/src/Listing/index.ts b/src/Listing/index.ts
new file mode 100644
index 0000000..07d8f28
--- /dev/null
+++ b/src/Listing/index.ts
@@ -0,0 +1,3 @@
+import Listing from "./Listing";
+
+export default Listing;
diff --git a/src/Menu/Menu.jsx b/src/Menu/Menu.jsx
deleted file mode 100644
index c332347..0000000
--- a/src/Menu/Menu.jsx
+++ /dev/null
@@ -1,50 +0,0 @@
-import React from 'react'
-import PropTypes from 'prop-types'
-import {
- Divider,
- List,
- withStyles,
-} from '@material-ui/core'
-
-import MenuItem from './MenuItem'
-
-const styles = theme => ({
- root: {
- width: '100%',
- maxWidth: 360,
- background: theme.palette.background.paper,
- },
-})
-
-const Menu = ({ data, redirectTo, classes }) => (
-
-
- {data.map((item) => {
- switch (item.type) {
- case 'divider':
- return
-
- default:
- return (
-
- )
- }
- })}
-
-
-)
-
-Menu.propTypes = {
- redirectTo: PropTypes.func.isRequired,
- data: PropTypes.arrayOf(PropTypes.object).isRequired,
- classes: PropTypes.objectOf(PropTypes.string).isRequired,
-}
-
-export default withStyles(styles)(Menu)
diff --git a/src/Menu/Menu.test.jsx b/src/Menu/Menu.test.jsx
deleted file mode 100644
index dc52515..0000000
--- a/src/Menu/Menu.test.jsx
+++ /dev/null
@@ -1,21 +0,0 @@
-import React from 'react'
-import { mount } from 'enzyme'
-
-import Icon from '@material-ui/icons/AccessAlarm'
-
-import Menu from './'
-
-it('renders correctly', () => {
- const tree = mount((
-
- )}
- />)
-
- expect(tree).toMatchSnapshot()
-})
diff --git a/src/NoMatch/NoMatch.test.tsx b/src/NoMatch/NoMatch.test.tsx
new file mode 100644
index 0000000..aede1ef
--- /dev/null
+++ b/src/NoMatch/NoMatch.test.tsx
@@ -0,0 +1,21 @@
+import React from "react";
+import { BrowserRouter as Router } from "react-router-dom";
+import { shallow } from "enzyme";
+
+import NoMatch from ".";
+
+it("renders correctly", () => {
+ const tree = shallow(
+
+
+ ,
+ );
+
+ expect(tree).toMatchSnapshot();
+});
+
+it("renders correctly with description", () => {
+ const tree = shallow(Desc} />);
+
+ expect(tree).toMatchSnapshot();
+});
diff --git a/src/NoMatch/NoMatch.tsx b/src/NoMatch/NoMatch.tsx
new file mode 100644
index 0000000..bad22d5
--- /dev/null
+++ b/src/NoMatch/NoMatch.tsx
@@ -0,0 +1,51 @@
+import React, { Fragment } from "react";
+import { Link } from "react-router-dom";
+import { Typography, withStyles } from "@material-ui/core";
+
+const styles = (theme: any) => ({
+ root: {
+ display: "flex",
+ justifyContent: "center",
+ alignItems: "center",
+ maxWidth: "60rem",
+ paddingLeft: theme.spacing(3),
+ paddingRight: theme.spacing(3),
+ marginLeft: "auto",
+ marginRight: "auto",
+ height: "calc(100vh - 104px)",
+ },
+});
+
+type NoMatchProps = {
+ title?: string;
+ description?: React.ReactNode;
+ classes: {
+ [key: string]: string;
+ };
+};
+
+const NoMatch: React.SFC = ({ title, description, classes }) => (
+
+
+ {title}
+
+ {description}
+
+
+
+);
+
+NoMatch.defaultProps = {
+ title: "404! Sorry, not found.",
+ description: (
+
+ This URL does not exist, sorry. Please start over from the{" "}
+
+ Dashboard
+
+ .
+
+ ),
+};
+
+export default withStyles(styles)(NoMatch);
diff --git a/src/NoMatch/index.js b/src/NoMatch/index.js
deleted file mode 100644
index c8760ca..0000000
--- a/src/NoMatch/index.js
+++ /dev/null
@@ -1,3 +0,0 @@
-import NoMatch from './NoMatch'
-
-export default NoMatch
diff --git a/src/NoMatch/index.ts b/src/NoMatch/index.ts
new file mode 100644
index 0000000..71fa195
--- /dev/null
+++ b/src/NoMatch/index.ts
@@ -0,0 +1,3 @@
+import NoMatch from "./NoMatch";
+
+export default NoMatch;
diff --git a/src/Snackbar/Snackbar.jsx b/src/Snackbar/Snackbar.jsx
deleted file mode 100644
index 7757260..0000000
--- a/src/Snackbar/Snackbar.jsx
+++ /dev/null
@@ -1,33 +0,0 @@
-import React from 'react'
-import PropTypes from 'prop-types'
-
-import { Snackbar as MaterialSnackbar } from '@material-ui/core'
-
-const Snackbar = ({
- isOpen,
- message,
-}) => (
- {message}}
- />
-)
-
-Snackbar.propTypes = {
- isOpen: PropTypes.bool,
- message: PropTypes.string.isRequired,
-}
-
-Snackbar.defaultProps = {
- isOpen: false,
-}
-
-export default Snackbar
diff --git a/src/Snackbar/Snackbar.test.jsx b/src/Snackbar/Snackbar.test.jsx
deleted file mode 100644
index 389a7fe..0000000
--- a/src/Snackbar/Snackbar.test.jsx
+++ /dev/null
@@ -1,10 +0,0 @@
-import React from 'react'
-import { shallow } from 'enzyme'
-
-import Snackbar from '.'
-
-it('renders correctly', () => {
- const tree = shallow()
-
- expect(tree).toMatchSnapshot()
-})
diff --git a/src/Snackbar/Snackbar.test.tsx b/src/Snackbar/Snackbar.test.tsx
new file mode 100644
index 0000000..548fa83
--- /dev/null
+++ b/src/Snackbar/Snackbar.test.tsx
@@ -0,0 +1,10 @@
+import React from "react";
+import { shallow } from "enzyme";
+
+import Snackbar from ".";
+
+it("renders correctly", () => {
+ const tree = shallow();
+
+ expect(tree).toMatchSnapshot();
+});
diff --git a/src/Snackbar/Snackbar.tsx b/src/Snackbar/Snackbar.tsx
new file mode 100644
index 0000000..3495f5c
--- /dev/null
+++ b/src/Snackbar/Snackbar.tsx
@@ -0,0 +1,28 @@
+import React from "react";
+import { Snackbar as MaterialSnackbar } from "@material-ui/core";
+
+type SnackbarProps = {
+ isOpen?: boolean;
+ message: string;
+};
+
+const Snackbar: React.SFC = ({ isOpen, message }) => (
+ {message}}
+ />
+);
+
+Snackbar.defaultProps = {
+ isOpen: false,
+};
+
+export default Snackbar;
diff --git a/src/Snackbar/index.d.ts b/src/Snackbar/index.d.ts
deleted file mode 100644
index 426ec13..0000000
--- a/src/Snackbar/index.d.ts
+++ /dev/null
@@ -1,17 +0,0 @@
-import * as React from "react"
-import { StandardProps } from ".."
-
-export interface ISnackbarProps
- extends StandardProps<
- React.HTMLAttributes,
- SnackbarClassKey
- > {
- isOpen: boolean
- message: React.ReactElement | string
-}
-
-export type SnackbarClassKey = "root"
-
-declare const Snackbar: React.ComponentType
-
-export default Snackbar
diff --git a/src/Snackbar/index.js b/src/Snackbar/index.js
deleted file mode 100644
index 60953ff..0000000
--- a/src/Snackbar/index.js
+++ /dev/null
@@ -1,3 +0,0 @@
-import Snackbar from './Snackbar'
-
-export default Snackbar
diff --git a/src/Snackbar/index.ts b/src/Snackbar/index.ts
new file mode 100644
index 0000000..6422c63
--- /dev/null
+++ b/src/Snackbar/index.ts
@@ -0,0 +1,3 @@
+import Snackbar from "./Snackbar";
+
+export default Snackbar;
diff --git a/src/Tabs/Tabs.test.jsx b/src/Tabs/Tabs.test.jsx
deleted file mode 100644
index d03d093..0000000
--- a/src/Tabs/Tabs.test.jsx
+++ /dev/null
@@ -1,57 +0,0 @@
-import React from 'react'
-import MockRouter from 'react-mock-router'
-import Enzyme, { shallow } from 'enzyme'
-import Adapter from 'enzyme-adapter-react-16'
-import { Tab } from '@material-ui/core'
-
-import Tabs from '.'
-import tabsContent from '../tests/data/tabs'
-
-Enzyme.configure({ adapter: new Adapter() })
-
-describe('Tabs', () => {
- it('renders correctly', () => {
- const tree = shallow((
-
- Content
- ),
- }]}
- />
-
- ))
-
- expect(tree).toMatchSnapshot()
- })
-
- it('renders correctly if no data', () => {
- const tree = shallow((
-
-
-
- ))
-
- expect(tree).toMatchSnapshot()
- })
-
- it('click second tab', () => {
- const tabAt = 1
- const tabs = shallow()
-
- expect(window.location.hash).toBe('')
- tabs.find(Tab).at(tabAt).simulate('click')
- expect(window.location.hash).toBe(`#/${tabsContent[tabAt].id}`)
- })
-
- it('test if correct element is active', () => {
- const tabAt = 1
- window.location.hash = `#/${tabsContent[tabAt].id}`
-
- shallow()
-
- expect(window.location.hash).toBe(`#/${tabsContent[tabAt].id}`)
- })
-})
diff --git a/src/Tabs/Tabs.test.tsx b/src/Tabs/Tabs.test.tsx
new file mode 100644
index 0000000..3f8d382
--- /dev/null
+++ b/src/Tabs/Tabs.test.tsx
@@ -0,0 +1,65 @@
+import React from "react";
+import MockRouter from "react-mock-router";
+import Enzyme, { shallow } from "enzyme";
+import Adapter from "enzyme-adapter-react-16";
+import { Tab } from "@material-ui/core";
+
+import Tabs from ".";
+import tabsContent from "../tests/data/tabs";
+
+Enzyme.configure({ adapter: new Adapter() });
+
+describe("Tabs", () => {
+ it("renders correctly", () => {
+ const tree = shallow(
+
+ Content,
+ },
+ ]}
+ />
+ ,
+ );
+
+ expect(tree).toMatchSnapshot();
+ });
+
+ it("renders correctly if no data", () => {
+ const tree = shallow(
+
+
+ ,
+ );
+
+ expect(tree).toMatchSnapshot();
+ });
+
+ it("click second tab", () => {
+ const tabAt = 1;
+ const tabs = shallow(
+
+
+ ,
+ );
+
+ expect(window.location.hash).toBe("");
+ tabs.find(Tab).at(tabAt).simulate("click");
+ expect(window.location.hash).toBe(`#/${tabsContent[tabAt].id}`);
+ });
+
+ it("test if correct element is active", () => {
+ const tabAt = 1;
+ window.location.hash = `#/${tabsContent[tabAt].id}`;
+
+ shallow(
+
+
+ ,
+ );
+
+ expect(window.location.hash).toBe(`#/${tabsContent[tabAt].id}`);
+ });
+});
diff --git a/src/Tabs/Tabs.jsx b/src/Tabs/Tabs.tsx
similarity index 53%
rename from src/Tabs/Tabs.jsx
rename to src/Tabs/Tabs.tsx
index 186606a..bff16c2 100644
--- a/src/Tabs/Tabs.jsx
+++ b/src/Tabs/Tabs.tsx
@@ -1,86 +1,80 @@
-import React from 'react'
-import PropTypes from 'prop-types'
-import { withRouter } from 'react-router-dom'
+import React from "react";
+import { withRouter } from "react-router-dom";
import {
AppBar,
withStyles,
Tabs as MaterialTabs,
Tab,
Typography,
-} from '@material-ui/core'
+} from "@material-ui/core";
-function TabContainer(props) {
+const TabContainer: React.SFC<{}> = (props) => {
return (
{props.children}
- )
-}
-
-TabContainer.propTypes = {
- children: PropTypes.node.isRequired,
-}
+ );
+};
-const styles = theme => ({
+const styles = (theme: any) => ({
root: {
flexGrow: 1,
- width: '100%',
+ width: "100%",
marginTop: theme.spacing() * 3,
backgroundColor: theme.palette.background.paper,
},
-})
+});
-class Tabs extends React.Component {
- static propTypes = {
- isScrollable: PropTypes.bool,
- data: PropTypes.arrayOf(PropTypes.object).isRequired,
- classes: PropTypes.objectOf(PropTypes.string).isRequired,
- }
+type TabsProps = {
+ isScrollable?: boolean;
+ data: Record[];
+ classes: {
+ [key: string]: string;
+ };
+};
- static defaultProps = {
- isScrollable: false,
- }
+type TabsState = {
+ value?: number;
+};
- constructor() {
- super()
+class Tabs extends React.Component {
+ constructor(props: TabsProps) {
+ super(props);
- this.handleChange = this.handleChange.bind(this)
+ this.handleChange = this.handleChange.bind(this);
}
state = {
value: 0,
- }
+ };
componentDidMount() {
- const { data } = this.props
- const hash = window.location.hash.replace('#/', '')
- let value = 0
+ const { data } = this.props;
+ const hash = window.location.hash.replace("#/", "");
+ let value = 0;
if (data && data.constructor === Array) {
data.forEach((item, index) => {
if (item.id === hash) {
- value = index
+ value = index;
}
- })
+ });
}
this.setState({
value,
- })
+ });
}
- handleChange(event, value) {
- const hash = this.props.data[value].id || value
-
- window.location.hash = `#/${hash}`
-
- this.setState({ value })
+ handleChange(event: any, value: any) {
+ const hash = this.props.data[value].id || value;
+ window.location.hash = `#/${hash}`;
+ this.setState({ value });
}
render() {
- const { isScrollable, data, classes } = this.props
- const { value } = this.state
-
+ const { isScrollable, data, classes } = this.props;
+ const { value } = this.state;
return (
@@ -91,7 +85,7 @@ class Tabs extends React.Component {
variant={isScrollable ? "scrollable" : undefined}
scrollButtons="auto"
>
- {data.map(item => (
+ {data.map((item) => (
{item.content}
- )
+ );
}
-
- return null
+ return null;
})}
- )
+ );
}
}
+const tabsWithStyles = withStyles(styles)(Tabs);
-const tabsWithStyles = withStyles(styles)(Tabs)
-export default withRouter(tabsWithStyles)
+export default withRouter(tabsWithStyles as any) as any;
diff --git a/src/Tabs/index.js b/src/Tabs/index.js
deleted file mode 100644
index d094ea3..0000000
--- a/src/Tabs/index.js
+++ /dev/null
@@ -1,3 +0,0 @@
-import Tabs from './Tabs'
-
-export default Tabs
diff --git a/src/Tabs/index.ts b/src/Tabs/index.ts
new file mode 100644
index 0000000..a5881f1
--- /dev/null
+++ b/src/Tabs/index.ts
@@ -0,0 +1,3 @@
+import Tabs from "./Tabs";
+
+export default Tabs;
diff --git a/src/app.js b/src/app.js
deleted file mode 100644
index 09e2720..0000000
--- a/src/app.js
+++ /dev/null
@@ -1,16 +0,0 @@
-export { default as AddButton } from './AddButton'
-export { default as AppContainer } from './AppContainer'
-export { default as BackButton } from './BackButton'
-export { default as Base } from './Base'
-export { default as Confirm } from './Confirm'
-export { default as CookieInfo } from './CookieInfo'
-export { default as Drawer } from './Drawer'
-export { default as Snackbar } from './Snackbar'
-export { default as Form } from './Form'
-export { default as Header } from './Header'
-export { default as Dashboard } from './Dashboard'
-export { default as Listing } from './Listing'
-export { default as Menu } from './Menu'
-export { default as NoMatch } from './NoMatch'
-export { default as Tabs } from './Tabs'
-export { TYPES } from './Form/constants'
diff --git a/src/app.ts b/src/app.ts
new file mode 100644
index 0000000..3dd9a91
--- /dev/null
+++ b/src/app.ts
@@ -0,0 +1,16 @@
+export { default as AddButton } from "./AddButton";
+export { default as AppContainer } from "./AppContainer";
+export { default as BackButton } from "./BackButton";
+export { default as Base } from "./Base";
+export { default as Confirm } from "./Confirm";
+export { default as CookieInfo } from "./CookieInfo";
+export { default as Drawer } from "./Drawer";
+export { default as Snackbar } from "./Snackbar";
+export { default as Form } from "./Form";
+export { default as Header } from "./Header";
+export { default as Dashboard } from "./Dashboard";
+export { default as Listing } from "./Listing";
+export { default as Menu } from "./Menu";
+export { default as NoMatch } from "./NoMatch";
+export { default as Tabs } from "./Tabs";
+export { TYPES } from "./Form/constants";
diff --git a/src/index.d.ts b/src/index.d.ts
deleted file mode 100644
index 9f1618f..0000000
--- a/src/index.d.ts
+++ /dev/null
@@ -1,29 +0,0 @@
-import * as React from "react"
-
-/**
- * All standard components exposed by `backoffice` are `StyledComponents` with
- * certain `classes`, on which one can also set a top-level `className` and inline
- * `style`.
- */
-export type StandardProps = Omit<
- C & { classes: any },
- "classes" | Removals
- > &
- IStyledComponentProps & {
- className?: string
- style?: Partial,
- }
-
-export type ClassNameMap = Record
-
-export interface IStyledComponentProps {
- classes?: Partial>
- innerRef?: React.Ref
-}
-
-/** @internal */
-type Diff = ({[P in T]: P } &
- {[P in U]: never } & { [x: string]: never })[T]
-
-/** @internal */
-export type Omit = Pick>
diff --git a/src/react-app-env.d.ts b/src/react-app-env.d.ts
index 6431bc5..6d3309e 100644
--- a/src/react-app-env.d.ts
+++ b/src/react-app-env.d.ts
@@ -1 +1,3 @@
///
+
+declare module "vanilla-store";
diff --git a/src/tests/Container.jsx b/src/tests/Container.jsx
deleted file mode 100644
index 14b43cf..0000000
--- a/src/tests/Container.jsx
+++ /dev/null
@@ -1,78 +0,0 @@
-import React from 'react'
-import {
- BrowserRouter as Router,
- Route,
- Switch,
-} from 'react-router-dom'
-import Typography from '@material-ui/core/Typography'
-
-import indigo from '@material-ui/core/colors/indigo'
-import amber from '@material-ui/core/colors/amber'
-
-import menuData from './data/menu'
-
-import AppContainer from '../AppContainer'
-import Base from '../Base'
-import NoMatch from '../NoMatch'
-import CookieInfo from '../CookieInfo'
-
-import Page from './Page'
-import General from './General'
-
-const theme = {
- palette: {
- primary: {
- light: indigo[300],
- main: indigo[500],
- dark: indigo[700],
- },
- secondary: {
- light: amber[300],
- main: amber[500],
- dark: amber[700],
- },
- },
-}
-
-const Container = () => (
-
-
-
- (
-
-
-
-
- )}
- />
-
- (
-
-
-
-
-
- This is the cookie info
-
-
-
- )}
- />
-
-
-
-)
-
-export default Container
diff --git a/src/tests/Container.tsx b/src/tests/Container.tsx
new file mode 100644
index 0000000..2feccd2
--- /dev/null
+++ b/src/tests/Container.tsx
@@ -0,0 +1,59 @@
+import React from "react";
+import { BrowserRouter as Router, Route, Switch } from "react-router-dom";
+import Typography from "@material-ui/core/Typography";
+import indigo from "@material-ui/core/colors/indigo";
+import amber from "@material-ui/core/colors/amber";
+import menuData from "./data/menu";
+import AppContainer from "../AppContainer";
+import Base from "../Base";
+import NoMatch from "../NoMatch";
+import CookieInfo from "../CookieInfo";
+import Page from "./Page";
+import General from "./General";
+
+const theme = {
+ palette: {
+ primary: {
+ light: indigo[300],
+ main: indigo[500],
+ dark: indigo[700],
+ },
+ secondary: {
+ light: amber[300],
+ main: amber[500],
+ dark: amber[700],
+ },
+ },
+};
+
+const Container = () => (
+
+
+
+ (
+
+
+
+
+ )}
+ />
+
+ (
+
+
+
+
+ This is the cookie info
+
+
+ )}
+ />
+
+
+
+);
+export default Container;
diff --git a/src/tests/General.jsx b/src/tests/General.jsx
deleted file mode 100644
index 43db5d1..0000000
--- a/src/tests/General.jsx
+++ /dev/null
@@ -1,50 +0,0 @@
-import React, { Fragment } from 'react'
-import Typography from '@material-ui/core/Typography'
-import Button from '@material-ui/core/Button/Button'
-
-import CookieInfo from '../CookieInfo'
-import Confirm from '../Confirm'
-
-class General extends React.Component {
- constructor() {
- super()
-
- this.state = {
- dialogOpen: false,
- }
-
- this.handleOpenDialoge = this.handleOpenDialoge.bind(this)
- }
-
- handleOpenDialoge() {
- this.setState({
- dialogOpen: true,
- })
- }
-
- render() {
- const { dialogOpen } = this.state
-
- return (
-
-
-
- This is the cookie info
-
-
-
-
- Confirm
-
-
- {}}
- />
-
- )
- }
-}
-
-export default General
diff --git a/src/tests/General.tsx b/src/tests/General.tsx
new file mode 100644
index 0000000..40d3878
--- /dev/null
+++ b/src/tests/General.tsx
@@ -0,0 +1,45 @@
+import React, { Fragment } from "react";
+import { Typography, Button } from "@material-ui/core";
+import CookieInfo from "../CookieInfo";
+import Confirm from "../Confirm";
+
+type GeneralState = {
+ dialogOpen: boolean;
+};
+
+class General extends React.Component<{}, GeneralState> {
+ constructor(props: any) {
+ super(props);
+
+ this.state = {
+ dialogOpen: false,
+ };
+ this.handleOpenDialoge = this.handleOpenDialoge.bind(this);
+ }
+ handleOpenDialoge() {
+ this.setState({
+ dialogOpen: true,
+ });
+ }
+
+ render() {
+ const { dialogOpen } = this.state;
+ return (
+
+
+ This is the cookie info
+
+
+ Confirm
+
+ {}}
+ />
+
+ );
+ }
+}
+
+export default General;
diff --git a/src/tests/Page.jsx b/src/tests/Page.tsx
similarity index 56%
rename from src/tests/Page.jsx
rename to src/tests/Page.tsx
index 27fd8ee..fc7cb9a 100644
--- a/src/tests/Page.jsx
+++ b/src/tests/Page.tsx
@@ -1,82 +1,79 @@
-import React, { Fragment } from 'react'
-import PropTypes from 'prop-types'
-import Typography from '@material-ui/core/Typography'
-import withStyles from '@material-ui/core/styles/withStyles'
-import Tooltip from '@material-ui/core/Tooltip'
-import IconButton from '@material-ui/core/IconButton'
-import Button from '@material-ui/core/Button'
-import DeleteIcon from '@material-ui/icons/Delete'
+import React, { Fragment } from "react";
+import Typography from "@material-ui/core/Typography";
+import withStyles from "@material-ui/core/styles/withStyles";
+import Tooltip from "@material-ui/core/Tooltip";
+import IconButton from "@material-ui/core/IconButton";
+import Button from "@material-ui/core/Button";
+import DeleteIcon from "@material-ui/icons/Delete";
-import Dashboard from '../Dashboard'
-import dashboardData from './data/dashboard'
+import Dashboard from "../Dashboard";
+import dashboardData from "./data/dashboard";
-import Menu from '../Menu'
-import menuData from './data/menu'
+import Menu from "../Menu";
+import menuData from "./data/menu";
-import Listing from '../Listing'
-import listingData from './data/listing_data'
-import listingHeaders from './data/listing_headers'
+import Listing from "../Listing";
+import listingData from "./data/listing_data";
+import listingHeaders from "./data/listing_headers";
-import Form from '../Form'
-import formData from './data/form'
+import Form from "../Form";
+import formData from "./data/form";
-import AddButton from '../AddButton'
-import BackButton from '../BackButton'
+import AddButton from "../AddButton";
+import BackButton from "../BackButton";
-import Tabs from '../Tabs'
-import tabData from './data/tabs'
+import Tabs from "../Tabs";
+import tabData from "./data/tabs";
-const noop = () => {}
+const noop = () => {};
-const styles = theme => ({
+const styles = (theme: any) => ({
headline: {
marginTop: theme.spacing(4),
},
-})
+});
-class Page extends React.Component {
- static propTypes = {
- classes: PropTypes.objectOf(PropTypes.string).isRequired,
- }
+interface Props {
+ classes: {
+ [key: string]: string;
+ };
+}
- constructor() {
- super()
+class Page extends React.Component {
+ constructor(props: Props) {
+ super(props);
- this.handleFormButtonClick = this.handleFormButtonClick.bind(this)
+ this.handleFormButtonClick = this.handleFormButtonClick.bind(this);
}
state = {
formData,
- additionalValue: null,
- }
+ additionalValue: undefined,
+ };
componentDidMount() {
- const { formData: newFormData, additionalValue } = this.state
+ const { formData: newFormData, additionalValue } = this.state;
- newFormData[0].data[0].getAdditionalValue = value => (
- additionalValue || value
- )
+ (newFormData[0].data[0] as any).getAdditionalValue = (value: any) =>
+ additionalValue || value;
this.setState({
formData: newFormData,
- })
+ });
}
handleFormButtonClick() {
this.setState({
- additionalValue: 'New value',
- })
+ additionalValue: "New value",
+ });
}
render() {
- const { classes, ...props } = this.props
+ const { classes, ...props } = this.props;
return (
-
+
Listing
@@ -89,14 +86,16 @@ class Page extends React.Component {
orderBy="username"
onClick={noop}
hasLoader
- onUpdateSelection={(selection) => { console.log(selection) }}
- toolbarContent={(
+ onUpdateSelection={(selection: any) => {
+ console.log(selection);
+ }}
+ toolbarContent={
- )}
+ }
/>
@@ -119,10 +118,10 @@ class Page extends React.Component {
- )
+ );
}
}
-export default withStyles(styles)(Page)
+export default withStyles(styles)(Page);
diff --git a/src/tests/data/dashboard.js b/src/tests/data/dashboard.js
deleted file mode 100644
index f185a95..0000000
--- a/src/tests/data/dashboard.js
+++ /dev/null
@@ -1,22 +0,0 @@
-export default {
- title: 'Backoffice',
- description: 'Visual Testing for Backoffice Framework',
- groups: [{
- id: 'list',
- title: 'List',
- cards: [{
- id: 'card-1',
- title: 'Card 1',
- description: 'Manage this card’s page',
- link: '/some-link',
- icon: null,
- }, {
- id: 'card-2',
- title: 'Card 2',
- description: 'Manage this card’s page',
- link: '/some-link',
- icon: null,
- isDisabled: true,
- }],
- }],
-}
diff --git a/src/tests/data/dashboard.ts b/src/tests/data/dashboard.ts
new file mode 100644
index 0000000..d22ac1a
--- /dev/null
+++ b/src/tests/data/dashboard.ts
@@ -0,0 +1,27 @@
+export default {
+ title: "Backoffice",
+ description: "Visual Testing for Backoffice Framework",
+ groups: [
+ {
+ id: "list",
+ title: "List",
+ cards: [
+ {
+ id: "card-1",
+ title: "Card 1",
+ description: "Manage this card’s page",
+ link: "/some-link",
+ icon: null,
+ },
+ {
+ id: "card-2",
+ title: "Card 2",
+ description: "Manage this card’s page",
+ link: "/some-link",
+ icon: null,
+ isDisabled: true,
+ },
+ ],
+ },
+ ],
+};
diff --git a/src/tests/data/form.jsx b/src/tests/data/form.jsx
deleted file mode 100644
index 3334bb8..0000000
--- a/src/tests/data/form.jsx
+++ /dev/null
@@ -1,196 +0,0 @@
-import React, { Fragment } from 'react'
-
-import Icon from '@material-ui/icons/Visibility'
-import {
- Divider,
- Typography,
-} from '@material-ui/core'
-
-import { TYPES } from '../../Form/constants'
-
-export default [{
- group: true,
- title: 'Form',
- id: 'base',
- data: [{
- id: 'text',
- title: 'Text Field',
- type: TYPES.TEXT,
- width: 'small',
- isRequired: true,
- }, {
- id: 'select',
- title: 'Select',
- type: TYPES.SELECT,
- options: ['Foo', 'Bar', 'Baz'],
- width: 'small',
- isRequired: true,
- }, {
- id: 'number',
- title: 'Number',
- type: TYPES.NUMBER,
- value: 10,
- width: 'small',
- }, {
- id: 'multiline',
- title: 'Multiline',
- type: 'multiline',
- rows: 5,
- }, {
- id: 'list',
- title: 'List',
- type: TYPES.LIST,
- width: 'mid',
- value: ['Foo', 'Bar', 'Baz'],
- completeFrom: [{
- title: 'Foo',
- tooltip: 'Foo-Baz',
- },
- 'Bar',
- {
- title: 'Baz',
- tooltip: 'Foo-Baz',
- },
- {
- title: 'Froot',
- },
- {
- title: 'Foobar',
- tooltip: 'Foo-Baz',
- },
- {
- title: 'Barbaz',
- tooltip: 'Foo-Baz',
- },
- {
- title: 'Foobaz',
- }],
- }, {
- id: 'free-list',
- title: 'Free List',
- type: TYPES.LIST,
- width: 'mid',
- }, {
- id: 'nested',
- group: true,
- title: 'Nested Form',
- integrated: true,
- isVisible: true,
- data: [{
- id: 'nested-text',
- title: 'Nested Text',
- type: TYPES.TEXT,
- iconEnd: (),
- width: 'mid',
- }, {
- id: 'empty',
- type: TYPES.EMPTY,
- width: 'mid',
- }],
- }, {
- id: 'content',
- type: TYPES.CONTENT,
- content: (
-
- Content
-
-
- ),
- }, {
- id: 'email',
- title: 'Email',
- type: TYPES.EMAIL,
- width: 'mid',
- beforeSubmit: (url) => {
- if (url) {
- return `${url}#top`
- }
-
- return url
- },
- }, {
- id: 'password',
- title: 'Password',
- type: TYPES.PASSWORD,
- width: 'mid',
- }, {
- id: 'url',
- title: 'URL',
- type: TYPES.URL,
- width: 'mid',
- helperText: 'Should be URL with trailing slash',
- validators: [{
- validator: value => value && value.substr(-1, 1) === '/',
- message: 'Please add a trailing slash to your URL',
- }],
- }, {
- id: 'empty',
- type: TYPES.EMAIL,
- width: 'mid',
- }],
-}, {
- group: true,
- title: 'Form - Date',
- id: 'date',
- data: [{
- id: 'date',
- title: 'Date',
- type: TYPES.DATE,
- format: 'DD.MM.YYYY',
- value: 1514989682669,
- width: 'small',
- validators: ['date'],
- }, {
- id: 'time',
- title: 'Time',
- type: TYPES.TIME,
- format: 'hh:mm a',
- value: 1514989682669,
- width: 'small',
- validators: ['date'],
- }, {
- id: 'datetime',
- title: 'Datetime',
- type: TYPES.DATETIME,
- format: 'DD.MM.YYYY, hh:mm a',
- value: 1514989682669,
- width: 'small',
- validators: ['date'],
- }],
-}, {
- group: true,
- title: 'Form - Disabled fields',
- id: 'disabled',
- data: [{
- id: 'disabled',
- title: 'Disabled Text',
- type: TYPES.TEXT,
- width: 'mid',
- isDisabled: true,
- }, {
- id: 'disabled-select',
- title: 'Disabled Select',
- type: TYPES.SELECT,
- width: 'mid',
- options: ['Foo', 'Bar', 'Baz'],
- isDisabled: true,
- }],
-}, {
- group: true,
- title: 'Form - Switches, Radio Buttons and Checkboxes',
- id: 'switches',
- data: [{
- id: 'switch',
- title: 'Switch',
- type: TYPES.SWITCH,
- helperText: 'Display Helper Text',
- width: 'small',
- }, {
- id: 'switch-1',
- title: 'Switch - Disabled',
- type: TYPES.SWITCH,
- helperText: 'Display Helper Text',
- width: 'small',
- isDisabled: true,
- }],
-}]
diff --git a/src/tests/data/form.tsx b/src/tests/data/form.tsx
new file mode 100644
index 0000000..fb6567b
--- /dev/null
+++ b/src/tests/data/form.tsx
@@ -0,0 +1,228 @@
+import React, { Fragment } from "react";
+
+import Icon from "@material-ui/icons/Visibility";
+import { Divider, Typography } from "@material-ui/core";
+
+import { TYPES } from "../../Form/constants";
+
+export default [
+ {
+ group: true,
+ title: "Form",
+ id: "base",
+ data: [
+ {
+ id: "text",
+ title: "Text Field",
+ type: TYPES.TEXT,
+ width: "small",
+ isRequired: true,
+ },
+ {
+ id: "select",
+ title: "Select",
+ type: TYPES.SELECT,
+ options: ["Foo", "Bar", "Baz"],
+ width: "small",
+ isRequired: true,
+ },
+ {
+ id: "number",
+ title: "Number",
+ type: TYPES.NUMBER,
+ value: 10,
+ width: "small",
+ },
+ {
+ id: "multiline",
+ title: "Multiline",
+ type: "multiline",
+ rows: 5,
+ },
+ {
+ id: "list",
+ title: "List",
+ type: TYPES.LIST,
+ width: "mid",
+ value: ["Foo", "Bar", "Baz"],
+ completeFrom: [
+ {
+ title: "Foo",
+ tooltip: "Foo-Baz",
+ },
+ "Bar",
+ {
+ title: "Baz",
+ tooltip: "Foo-Baz",
+ },
+ {
+ title: "Froot",
+ },
+ {
+ title: "Foobar",
+ tooltip: "Foo-Baz",
+ },
+ {
+ title: "Barbaz",
+ tooltip: "Foo-Baz",
+ },
+ {
+ title: "Foobaz",
+ },
+ ],
+ },
+ {
+ id: "free-list",
+ title: "Free List",
+ type: TYPES.LIST,
+ width: "mid",
+ },
+ {
+ id: "nested",
+ group: true,
+ title: "Nested Form",
+ integrated: true,
+ isVisible: true,
+ data: [
+ {
+ id: "nested-text",
+ title: "Nested Text",
+ type: TYPES.TEXT,
+ iconEnd: ,
+ width: "mid",
+ },
+ {
+ id: "empty",
+ type: TYPES.EMPTY,
+ width: "mid",
+ },
+ ],
+ },
+ {
+ id: "content",
+ type: TYPES.CONTENT,
+ content: (
+
+ Content
+
+
+ ),
+ },
+ {
+ id: "email",
+ title: "Email",
+ type: TYPES.EMAIL,
+ width: "mid",
+ beforeSubmit: (url?: string) => {
+ if (url) {
+ return `${url}#top`;
+ }
+
+ return url;
+ },
+ },
+ {
+ id: "password",
+ title: "Password",
+ type: TYPES.PASSWORD,
+ width: "mid",
+ },
+ {
+ id: "url",
+ title: "URL",
+ type: TYPES.URL,
+ width: "mid",
+ helperText: "Should be URL with trailing slash",
+ validators: [
+ {
+ validator: (value?: string) => value && value.substr(-1, 1) === "/",
+ message: "Please add a trailing slash to your URL",
+ },
+ ],
+ },
+ {
+ id: "empty",
+ type: TYPES.EMAIL,
+ width: "mid",
+ },
+ ],
+ },
+ {
+ group: true,
+ title: "Form - Date",
+ id: "date",
+ data: [
+ {
+ id: "date",
+ title: "Date",
+ type: TYPES.DATE,
+ format: "DD.MM.YYYY",
+ value: 1514989682669,
+ width: "small",
+ validators: ["date"],
+ },
+ {
+ id: "time",
+ title: "Time",
+ type: TYPES.TIME,
+ format: "hh:mm a",
+ value: 1514989682669,
+ width: "small",
+ validators: ["date"],
+ },
+ {
+ id: "datetime",
+ title: "Datetime",
+ type: TYPES.DATETIME,
+ format: "DD.MM.YYYY, hh:mm a",
+ value: 1514989682669,
+ width: "small",
+ validators: ["date"],
+ },
+ ],
+ },
+ {
+ group: true,
+ title: "Form - Disabled fields",
+ id: "disabled",
+ data: [
+ {
+ id: "disabled",
+ title: "Disabled Text",
+ type: TYPES.TEXT,
+ width: "mid",
+ isDisabled: true,
+ },
+ {
+ id: "disabled-select",
+ title: "Disabled Select",
+ type: TYPES.SELECT,
+ width: "mid",
+ options: ["Foo", "Bar", "Baz"],
+ isDisabled: true,
+ },
+ ],
+ },
+ {
+ group: true,
+ title: "Form - Switches, Radio Buttons and Checkboxes",
+ id: "switches",
+ data: [
+ {
+ id: "switch",
+ title: "Switch",
+ type: TYPES.SWITCH,
+ helperText: "Display Helper Text",
+ width: "small",
+ },
+ {
+ id: "switch-1",
+ title: "Switch - Disabled",
+ type: TYPES.SWITCH,
+ helperText: "Display Helper Text",
+ width: "small",
+ isDisabled: true,
+ },
+ ],
+ },
+];
diff --git a/src/tests/data/listing_data.js b/src/tests/data/listing_data.js
deleted file mode 100644
index 0c45621..0000000
--- a/src/tests/data/listing_data.js
+++ /dev/null
@@ -1,253 +0,0 @@
-export default [{
- id: 1,
- name: 'Leanne Graham',
- username: 'Bret',
- email: 'Sincere@april.biz',
- address: {
- street: 'Kulas Light',
- suite: 'Apt. 556',
- city: 'Gwenborough',
- zipcode: '92998-3874',
- geo: {
- lat: '-37.3159',
- lng: '81.1496',
- },
- },
- phone: '1-770-736-8031 x56442',
- website: 'hildegard.org',
- company: {
- name: 'Romaguera-Crona',
- catchPhrase: 'Multi-layered client-server neural-net',
- bs: 'harness real-time e-markets',
- },
-},
-{
- id: 2,
- name: 'Ervin Howell',
- username: 'Antonette',
- email: 'Shanna@melissa.tv',
- address: {
- street: 'Victor Plains',
- suite: 'Suite 879',
- city: 'Wisokyburgh',
- zipcode: '90566-7771',
- geo: {
- lat: '-43.9509',
- lng: '-34.4618',
- },
- },
- phone: '010-692-6593 x09125',
- website: 'anastasia.net',
- company: {
- name: 'Deckow-Crist',
- catchPhrase: 'Proactive didactic contingency',
- bs: 'synergize scalable supply-chains',
- },
-},
-{
- id: 3,
- name: 'Clementine Bauch',
- username: 'Samantha',
- email: 'Nathan@yesenia.net',
- address: {
- street: 'Douglas Extension',
- suite: 'Suite 847',
- city: 'McKenziehaven',
- zipcode: '59590-4157',
- geo: {
- lat: '-68.6102',
- lng: '-47.0653',
- },
- },
- phone: '1-463-123-4447',
- website: 'ramiro.info',
- company: {
- name: 'Romaguera-Jacobson',
- catchPhrase: 'Face to face bifurcated interface',
- bs: 'e-enable strategic applications',
- },
-},
-{
- id: 4,
- name: 'Patricia Lebsack',
- username: 'Karianne',
- email: 'Julianne.OConner@kory.org',
- address: {
- street: 'Hoeger Mall',
- suite: 'Apt. 692',
- city: 'South Elvis',
- zipcode: '53919-4257',
- geo: {
- lat: '29.4572',
- lng: '-164.2990',
- },
- },
- phone: '493-170-9623 x156',
- website: 'kale.biz',
- company: {
- name: 'Robel-Corkery',
- catchPhrase: 'Multi-tiered zero tolerance productivity',
- bs: 'transition cutting-edge web services',
- },
-},
-{
- id: 5,
- name: 'Chelsey Dietrich',
- username: 'Kamren',
- email: 'Lucio_Hettinger@annie.ca',
- address: {
- street: 'Skiles Walks',
- suite: 'Suite 351',
- city: 'Roscoeview',
- zipcode: '33263',
- geo: {
- lat: '-31.8129',
- lng: '62.5342',
- },
- },
- phone: '(254)954-1289',
- website: 'demarco.info',
- company: {
- name: 'Keebler LLC',
- catchPhrase: 'User-centric fault-tolerant solution',
- bs: 'revolutionize end-to-end systems',
- },
-},
-{
- id: 6,
- name: 'Mrs. Dennis Schulist',
- username: 'Leopoldo_Corkery',
- email: 'Karley_Dach@jasper.info',
- address: {
- street: 'Norberto Crossing',
- suite: 'Apt. 950',
- city: 'South Christy',
- zipcode: '23505-1337',
- geo: {
- lat: '-71.4197',
- lng: '71.7478',
- },
- },
- phone: '1-477-935-8478 x6430',
- website: 'ola.org',
- company: {
- name: 'Considine-Lockman',
- catchPhrase: 'Synchronised bottom-line interface',
- bs: 'e-enable innovative applications',
- },
-},
-{
- id: 7,
- name: 'Kurtis Weissnat',
- username: 'Elwyn.Skiles',
- email: 'Telly.Hoeger@billy.biz',
- address: {
- street: 'Rex Trail',
- suite: 'Suite 280',
- city: 'Howemouth',
- zipcode: '58804-1099',
- geo: {
- lat: '24.8918',
- lng: '21.8984',
- },
- },
- phone: '210.067.6132',
- website: 'elvis.io',
- company: {
- name: 'Johns Group',
- catchPhrase: 'Configurable multimedia task-force',
- bs: 'generate enterprise e-tailers',
- },
-},
-{
- id: 8,
- name: 'Nicholas Runolfsdottir V',
- username: 'Maxime_Nienow',
- email: 'Sherwood@rosamond.me',
- address: {
- street: 'Ellsworth Summit',
- suite: 'Suite 729',
- city: 'Aliyaview',
- zipcode: '45169',
- geo: {
- lat: '-14.3990',
- lng: '-120.7677',
- },
- },
- phone: '586.493.6943 x140',
- website: 'jacynthe.com',
- company: {
- name: 'Abernathy Group',
- catchPhrase: 'Implemented secondary concept',
- bs: 'e-enable extensible e-tailers',
- },
-},
-{
- id: 9,
- name: 'Glenna Reichert',
- username: 'Delphine',
- email: 'Chaim_McDermott@dana.io',
- address: {
- street: 'Dayna Park',
- suite: 'Suite 449',
- city: 'Bartholomebury',
- zipcode: '76495-3109',
- geo: {
- lat: '24.6463',
- lng: '-168.8889',
- },
- },
- phone: '(775)976-6794 x41206',
- website: 'conrad.com',
- company: {
- name: 'Yost and Sons',
- catchPhrase: 'Switchable contextually-based project',
- bs: 'aggregate real-time technologies',
- },
-},
-{
- id: 10,
- name: 'Clementina DuBuque',
- username: 'Moriah.Stanton',
- email: 'Rey.Padberg@karina.biz',
- address: {
- street: 'Kattie Turnpike',
- suite: 'Suite 198',
- city: 'Lebsackbury',
- zipcode: '31428-2261',
- geo: {
- lat: '-38.2386',
- lng: '57.2232',
- },
- },
- phone: '024-648-3804',
- website: 'ambrose.net',
- company: {
- name: 'Hoeger LLC',
- catchPhrase: 'Centralized empowering task-force',
- bs: 'target end-to-end models',
- },
-},
-{
- id: 11,
- name: 'Mr. Dan Schulist',
- username: 'Dan.Schulist',
- email: 'Dan.Schulist@karina.biz',
- address: {
- street: 'Kattie Turnpike',
- suite: 'Suite 198',
- city: 'Lebsackbury',
- zipcode: '31428-2261',
- geo: {
- lat: '-38.2386',
- lng: '57.2232',
- },
- },
- phone: '024-648-3804',
- website: 'ambrose.net',
- company: {
- name: 'Hoeger LLC',
- catchPhrase: 'Centralized empowering task-force',
- bs: 'target end-to-end models',
- },
-}]
diff --git a/src/tests/data/listing_data.ts b/src/tests/data/listing_data.ts
new file mode 100644
index 0000000..89d8ffa
--- /dev/null
+++ b/src/tests/data/listing_data.ts
@@ -0,0 +1,255 @@
+export default [
+ {
+ id: 1,
+ name: "Leanne Graham",
+ username: "Bret",
+ email: "Sincere@april.biz",
+ address: {
+ street: "Kulas Light",
+ suite: "Apt. 556",
+ city: "Gwenborough",
+ zipcode: "92998-3874",
+ geo: {
+ lat: "-37.3159",
+ lng: "81.1496",
+ },
+ },
+ phone: "1-770-736-8031 x56442",
+ website: "hildegard.org",
+ company: {
+ name: "Romaguera-Crona",
+ catchPhrase: "Multi-layered client-server neural-net",
+ bs: "harness real-time e-markets",
+ },
+ },
+ {
+ id: 2,
+ name: "Ervin Howell",
+ username: "Antonette",
+ email: "Shanna@melissa.tv",
+ address: {
+ street: "Victor Plains",
+ suite: "Suite 879",
+ city: "Wisokyburgh",
+ zipcode: "90566-7771",
+ geo: {
+ lat: "-43.9509",
+ lng: "-34.4618",
+ },
+ },
+ phone: "010-692-6593 x09125",
+ website: "anastasia.net",
+ company: {
+ name: "Deckow-Crist",
+ catchPhrase: "Proactive didactic contingency",
+ bs: "synergize scalable supply-chains",
+ },
+ },
+ {
+ id: 3,
+ name: "Clementine Bauch",
+ username: "Samantha",
+ email: "Nathan@yesenia.net",
+ address: {
+ street: "Douglas Extension",
+ suite: "Suite 847",
+ city: "McKenziehaven",
+ zipcode: "59590-4157",
+ geo: {
+ lat: "-68.6102",
+ lng: "-47.0653",
+ },
+ },
+ phone: "1-463-123-4447",
+ website: "ramiro.info",
+ company: {
+ name: "Romaguera-Jacobson",
+ catchPhrase: "Face to face bifurcated interface",
+ bs: "e-enable strategic applications",
+ },
+ },
+ {
+ id: 4,
+ name: "Patricia Lebsack",
+ username: "Karianne",
+ email: "Julianne.OConner@kory.org",
+ address: {
+ street: "Hoeger Mall",
+ suite: "Apt. 692",
+ city: "South Elvis",
+ zipcode: "53919-4257",
+ geo: {
+ lat: "29.4572",
+ lng: "-164.2990",
+ },
+ },
+ phone: "493-170-9623 x156",
+ website: "kale.biz",
+ company: {
+ name: "Robel-Corkery",
+ catchPhrase: "Multi-tiered zero tolerance productivity",
+ bs: "transition cutting-edge web services",
+ },
+ },
+ {
+ id: 5,
+ name: "Chelsey Dietrich",
+ username: "Kamren",
+ email: "Lucio_Hettinger@annie.ca",
+ address: {
+ street: "Skiles Walks",
+ suite: "Suite 351",
+ city: "Roscoeview",
+ zipcode: "33263",
+ geo: {
+ lat: "-31.8129",
+ lng: "62.5342",
+ },
+ },
+ phone: "(254)954-1289",
+ website: "demarco.info",
+ company: {
+ name: "Keebler LLC",
+ catchPhrase: "User-centric fault-tolerant solution",
+ bs: "revolutionize end-to-end systems",
+ },
+ },
+ {
+ id: 6,
+ name: "Mrs. Dennis Schulist",
+ username: "Leopoldo_Corkery",
+ email: "Karley_Dach@jasper.info",
+ address: {
+ street: "Norberto Crossing",
+ suite: "Apt. 950",
+ city: "South Christy",
+ zipcode: "23505-1337",
+ geo: {
+ lat: "-71.4197",
+ lng: "71.7478",
+ },
+ },
+ phone: "1-477-935-8478 x6430",
+ website: "ola.org",
+ company: {
+ name: "Considine-Lockman",
+ catchPhrase: "Synchronised bottom-line interface",
+ bs: "e-enable innovative applications",
+ },
+ },
+ {
+ id: 7,
+ name: "Kurtis Weissnat",
+ username: "Elwyn.Skiles",
+ email: "Telly.Hoeger@billy.biz",
+ address: {
+ street: "Rex Trail",
+ suite: "Suite 280",
+ city: "Howemouth",
+ zipcode: "58804-1099",
+ geo: {
+ lat: "24.8918",
+ lng: "21.8984",
+ },
+ },
+ phone: "210.067.6132",
+ website: "elvis.io",
+ company: {
+ name: "Johns Group",
+ catchPhrase: "Configurable multimedia task-force",
+ bs: "generate enterprise e-tailers",
+ },
+ },
+ {
+ id: 8,
+ name: "Nicholas Runolfsdottir V",
+ username: "Maxime_Nienow",
+ email: "Sherwood@rosamond.me",
+ address: {
+ street: "Ellsworth Summit",
+ suite: "Suite 729",
+ city: "Aliyaview",
+ zipcode: "45169",
+ geo: {
+ lat: "-14.3990",
+ lng: "-120.7677",
+ },
+ },
+ phone: "586.493.6943 x140",
+ website: "jacynthe.com",
+ company: {
+ name: "Abernathy Group",
+ catchPhrase: "Implemented secondary concept",
+ bs: "e-enable extensible e-tailers",
+ },
+ },
+ {
+ id: 9,
+ name: "Glenna Reichert",
+ username: "Delphine",
+ email: "Chaim_McDermott@dana.io",
+ address: {
+ street: "Dayna Park",
+ suite: "Suite 449",
+ city: "Bartholomebury",
+ zipcode: "76495-3109",
+ geo: {
+ lat: "24.6463",
+ lng: "-168.8889",
+ },
+ },
+ phone: "(775)976-6794 x41206",
+ website: "conrad.com",
+ company: {
+ name: "Yost and Sons",
+ catchPhrase: "Switchable contextually-based project",
+ bs: "aggregate real-time technologies",
+ },
+ },
+ {
+ id: 10,
+ name: "Clementina DuBuque",
+ username: "Moriah.Stanton",
+ email: "Rey.Padberg@karina.biz",
+ address: {
+ street: "Kattie Turnpike",
+ suite: "Suite 198",
+ city: "Lebsackbury",
+ zipcode: "31428-2261",
+ geo: {
+ lat: "-38.2386",
+ lng: "57.2232",
+ },
+ },
+ phone: "024-648-3804",
+ website: "ambrose.net",
+ company: {
+ name: "Hoeger LLC",
+ catchPhrase: "Centralized empowering task-force",
+ bs: "target end-to-end models",
+ },
+ },
+ {
+ id: 11,
+ name: "Mr. Dan Schulist",
+ username: "Dan.Schulist",
+ email: "Dan.Schulist@karina.biz",
+ address: {
+ street: "Kattie Turnpike",
+ suite: "Suite 198",
+ city: "Lebsackbury",
+ zipcode: "31428-2261",
+ geo: {
+ lat: "-38.2386",
+ lng: "57.2232",
+ },
+ },
+ phone: "024-648-3804",
+ website: "ambrose.net",
+ company: {
+ name: "Hoeger LLC",
+ catchPhrase: "Centralized empowering task-force",
+ bs: "target end-to-end models",
+ },
+ },
+];
diff --git a/src/tests/data/listing_headers.jsx b/src/tests/data/listing_headers.jsx
deleted file mode 100644
index ba805f2..0000000
--- a/src/tests/data/listing_headers.jsx
+++ /dev/null
@@ -1,44 +0,0 @@
-import React from 'react'
-
-export default [{
- id: 'name',
- isPaddingDisabled: true,
- label: 'Name',
- isSearchable: true,
-}, {
- id: 'username',
- isPaddingDisabled: true,
- label: 'Username',
- isSearchable: true,
-}, {
- id: 'phone',
- isPaddingDisabled: false,
- label: 'Phone',
- isSearchable: true,
- isNumeric: true,
-}, {
- id: 'website',
- isPaddingDisabled: true,
- label: 'Website',
- transformContent: (website) => {
- if (website) {
- return (
- {website}
- )
- }
-
- return website
- },
-}, {
- id: 'company',
- isPaddingDisabled: true,
- label: 'Company',
- transformContent: (company) => {
- if (company.name) {
- return company.name
- }
-
- return ''
- },
- isSearchable: true,
-}]
diff --git a/src/tests/data/listing_headers.tsx b/src/tests/data/listing_headers.tsx
new file mode 100644
index 0000000..4ff7c65
--- /dev/null
+++ b/src/tests/data/listing_headers.tsx
@@ -0,0 +1,48 @@
+import React from "react";
+
+export default [
+ {
+ id: "name",
+ isPaddingDisabled: true,
+ label: "Name",
+ isSearchable: true,
+ },
+ {
+ id: "username",
+ isPaddingDisabled: true,
+ label: "Username",
+ isSearchable: true,
+ },
+ {
+ id: "phone",
+ isPaddingDisabled: false,
+ label: "Phone",
+ isSearchable: true,
+ isNumeric: true,
+ },
+ {
+ id: "website",
+ isPaddingDisabled: true,
+ label: "Website",
+ transformContent: (website?: string) => {
+ if (website) {
+ return {website};
+ }
+
+ return website;
+ },
+ },
+ {
+ id: "company",
+ isPaddingDisabled: true,
+ label: "Company",
+ transformContent: (company: Record) => {
+ if (company.name) {
+ return company.name;
+ }
+
+ return "";
+ },
+ isSearchable: true,
+ },
+];
diff --git a/src/tests/data/menu.js b/src/tests/data/menu.js
deleted file mode 100644
index 80ecddf..0000000
--- a/src/tests/data/menu.js
+++ /dev/null
@@ -1,20 +0,0 @@
-import BugIcon from '@material-ui/icons/BugReport'
-
-export default [{
- type: 'link',
- url: '/',
- title: 'Dashboard',
- icon: null,
-}, {
- type: 'link',
- url: '/portfolio',
- title: 'New portfolio',
- isDisabled: true,
-}, {
- type: 'divider',
-}, {
- type: 'link',
- url: '/bug',
- title: 'Report a bug',
- icon: BugIcon,
-}]
diff --git a/src/tests/data/menu.ts b/src/tests/data/menu.ts
new file mode 100644
index 0000000..0f773cd
--- /dev/null
+++ b/src/tests/data/menu.ts
@@ -0,0 +1,25 @@
+import BugIcon from "@material-ui/icons/BugReport";
+
+export default [
+ {
+ type: "link",
+ url: "/",
+ title: "Dashboard",
+ icon: null,
+ },
+ {
+ type: "link",
+ url: "/portfolio",
+ title: "New portfolio",
+ isDisabled: true,
+ },
+ {
+ type: "divider",
+ },
+ {
+ type: "link",
+ url: "/bug",
+ title: "Report a bug",
+ icon: BugIcon,
+ },
+];
diff --git a/src/tests/data/tabs.jsx b/src/tests/data/tabs.jsx
deleted file mode 100644
index 332f2fe..0000000
--- a/src/tests/data/tabs.jsx
+++ /dev/null
@@ -1,45 +0,0 @@
-import React from 'react'
-
-export default [{
- id: 'item-1',
- title: 'Item One',
- content: (
- Tab One
- ),
-}, {
- id: 'item-2',
- title: 'Item Two',
- content: (
- Tab Two
- ),
-}, {
- id: 'item-3',
- title: 'Item Three',
- content: (
- Tab Three
- ),
-}, {
- id: 'item-4',
- title: 'Item Four',
- content: (
- Tab Four
- ),
-}, {
- id: 'item-5',
- title: 'Item Five',
- content: (
- Tab Five
- ),
-}, {
- id: 'item-6',
- title: 'Item Six',
- content: (
- Tab Six
- ),
-}, {
- id: 'item-7',
- title: 'Item Seven',
- content: (
- Tab Seven
- ),
-}]
diff --git a/src/tests/data/tabs.tsx b/src/tests/data/tabs.tsx
new file mode 100644
index 0000000..ea32898
--- /dev/null
+++ b/src/tests/data/tabs.tsx
@@ -0,0 +1,39 @@
+import React from "react";
+
+export default [
+ {
+ id: "item-1",
+ title: "Item One",
+ content: Tab One
,
+ },
+ {
+ id: "item-2",
+ title: "Item Two",
+ content: Tab Two
,
+ },
+ {
+ id: "item-3",
+ title: "Item Three",
+ content: Tab Three
,
+ },
+ {
+ id: "item-4",
+ title: "Item Four",
+ content: Tab Four
,
+ },
+ {
+ id: "item-5",
+ title: "Item Five",
+ content: Tab Five
,
+ },
+ {
+ id: "item-6",
+ title: "Item Six",
+ content: Tab Six
,
+ },
+ {
+ id: "item-7",
+ title: "Item Seven",
+ content: Tab Seven
,
+ },
+];
diff --git a/src/utils/easeInOutQuad.js b/src/utils/easeInOutQuad.ts
similarity index 58%
rename from src/utils/easeInOutQuad.js
rename to src/utils/easeInOutQuad.ts
index aa2bbfa..7753c14 100644
--- a/src/utils/easeInOutQuad.js
+++ b/src/utils/easeInOutQuad.ts
@@ -1,5 +1,5 @@
/* eslint-disable */
-const easeInOutQuad = (t, b, c, d) => {
+const easeInOutQuad = (t: number, b: number, c: number, d: number) => {
t /= d / 2
if (t < 1) return c / 2 * t * t + b
t--
@@ -7,4 +7,4 @@ const easeInOutQuad = (t, b, c, d) => {
};
/* eslint-enable */
-export default easeInOutQuad
+export default easeInOutQuad;
diff --git a/src/utils/replace.js b/src/utils/replace.js
deleted file mode 100644
index 0c199c9..0000000
--- a/src/utils/replace.js
+++ /dev/null
@@ -1,18 +0,0 @@
-export default (string, searchValue, replaceValue) => {
- if (!searchValue) {
- return string
- }
-
- const search = searchValue.replace(/[-/\\^$*+?.()|[\]{}]/g, '\\$&')
-
- if (typeof replaceValue === 'function') {
- const index = string.search(new RegExp(search, 'gi'))
- const replaceString = string
- .substring(index)
- .substring(0, searchValue.length)
-
- return string.replace(new RegExp(search, 'gi'), replaceValue(replaceString))
- }
-
- return string.replace(new RegExp(search, 'gi'), replaceValue)
-}
diff --git a/src/utils/replace.ts b/src/utils/replace.ts
new file mode 100644
index 0000000..3989b2a
--- /dev/null
+++ b/src/utils/replace.ts
@@ -0,0 +1,25 @@
+export default (
+ string: string,
+ searchValue: string,
+ replaceValue: string | ((replace: string) => string),
+) => {
+ if (!searchValue) {
+ return string;
+ }
+
+ const search = searchValue.replace(/[-/\\^$*+?.()|[\]{}]/g, "\\$&");
+
+ if (typeof replaceValue === "function") {
+ const index = string.search(new RegExp(search, "gi"));
+ const replaceString = string
+ .substring(index)
+ .substring(0, searchValue.length);
+
+ return string.replace(
+ new RegExp(search, "gi"),
+ replaceValue(replaceString),
+ );
+ }
+
+ return string.replace(new RegExp(search, "gi"), replaceValue);
+};
diff --git a/tsconfig.build.json b/tsconfig.build.json
new file mode 100644
index 0000000..c8a4b5d
--- /dev/null
+++ b/tsconfig.build.json
@@ -0,0 +1,32 @@
+{
+ "compilerOptions": {
+ "esModuleInterop": true,
+ "forceConsistentCasingInFileNames": true,
+ "allowSyntheticDefaultImports": true,
+ "jsx": "react",
+ "module": "commonjs",
+ "moduleResolution": "node",
+ "resolveJsonModule": true,
+ "sourceMap": true,
+ "strict": true,
+ "target": "es5",
+ "declaration": true,
+ "noImplicitAny": false,
+ "lib": [
+ "dom",
+ "dom.iterable",
+ "esnext"
+ ],
+ "skipLibCheck": true,
+ "outDir": "./dist/"
+ },
+ "include": [
+ "src/**/*"
+ ],
+ "exclude": [
+ "node_modules",
+ "build",
+ "dist",
+ "src/**/*test*"
+ ]
+}
diff --git a/tslint.json b/tslint.json
deleted file mode 100644
index 3aa73bd..0000000
--- a/tslint.json
+++ /dev/null
@@ -1,12 +0,0 @@
-{
- "extends": [
- "tslint:latest",
- "tslint-react"
- ],
- "rules": {
- "semicolon": [
- true,
- "never"
- ]
- }
-}
diff --git a/yarn.lock b/yarn.lock
index 823f7bb..ef26724 100644
--- a/yarn.lock
+++ b/yarn.lock
@@ -2,22 +2,6 @@
# yarn lockfile v1
-"@babel/cli@^7.8.4":
- version "7.8.4"
- resolved "https://registry.yarnpkg.com/@babel/cli/-/cli-7.8.4.tgz#505fb053721a98777b2b175323ea4f090b7d3c1c"
- integrity sha512-XXLgAm6LBbaNxaGhMAznXXaxtCWfuv6PIDJ9Alsy9JYTOh+j2jJz+L/162kkfU1j/pTSxK1xGmlwI4pdIMkoag==
- dependencies:
- commander "^4.0.1"
- convert-source-map "^1.1.0"
- fs-readdir-recursive "^1.1.0"
- glob "^7.0.0"
- lodash "^4.17.13"
- make-dir "^2.1.0"
- slash "^2.0.0"
- source-map "^0.5.0"
- optionalDependencies:
- chokidar "^2.1.8"
-
"@babel/code-frame@7.8.3", "@babel/code-frame@^7.0.0", "@babel/code-frame@^7.8.3":
version "7.8.3"
resolved "https://registry.yarnpkg.com/@babel/code-frame/-/code-frame-7.8.3.tgz#33e25903d7481181534e12ec0a25f16b6fcf419e"
@@ -56,28 +40,6 @@
semver "^5.4.1"
source-map "^0.5.0"
-"@babel/core@^7.9.6":
- version "7.9.6"
- resolved "https://registry.yarnpkg.com/@babel/core/-/core-7.9.6.tgz#d9aa1f580abf3b2286ef40b6904d390904c63376"
- integrity sha512-nD3deLvbsApbHAHttzIssYqgb883yU/d9roe4RZymBCDaZryMJDbptVpEpeQuRh4BJ+SYI8le9YGxKvFEvl1Wg==
- dependencies:
- "@babel/code-frame" "^7.8.3"
- "@babel/generator" "^7.9.6"
- "@babel/helper-module-transforms" "^7.9.0"
- "@babel/helpers" "^7.9.6"
- "@babel/parser" "^7.9.6"
- "@babel/template" "^7.8.6"
- "@babel/traverse" "^7.9.6"
- "@babel/types" "^7.9.6"
- convert-source-map "^1.7.0"
- debug "^4.1.0"
- gensync "^1.0.0-beta.1"
- json5 "^2.1.2"
- lodash "^4.17.13"
- resolve "^1.3.2"
- semver "^5.4.1"
- source-map "^0.5.0"
-
"@babel/generator@^7.4.0", "@babel/generator@^7.9.0", "@babel/generator@^7.9.5":
version "7.9.5"
resolved "https://registry.yarnpkg.com/@babel/generator/-/generator-7.9.5.tgz#27f0917741acc41e6eaaced6d68f96c3fa9afaf9"
@@ -88,16 +50,6 @@
lodash "^4.17.13"
source-map "^0.5.0"
-"@babel/generator@^7.9.6":
- version "7.9.6"
- resolved "https://registry.yarnpkg.com/@babel/generator/-/generator-7.9.6.tgz#5408c82ac5de98cda0d77d8124e99fa1f2170a43"
- integrity sha512-+htwWKJbH2bL72HRluF8zumBxzuX0ZZUFl3JLNyoUjM/Ho8wnVpPXM6aUz8cfKDqQ/h7zHqKt4xzJteUosckqQ==
- dependencies:
- "@babel/types" "^7.9.6"
- jsesc "^2.5.1"
- lodash "^4.17.13"
- source-map "^0.5.0"
-
"@babel/helper-annotate-as-pure@^7.8.3":
version "7.8.3"
resolved "https://registry.yarnpkg.com/@babel/helper-annotate-as-pure/-/helper-annotate-as-pure-7.8.3.tgz#60bc0bc657f63a0924ff9a4b4a0b24a13cf4deee"
@@ -308,15 +260,6 @@
"@babel/traverse" "^7.9.0"
"@babel/types" "^7.9.0"
-"@babel/helpers@^7.9.6":
- version "7.9.6"
- resolved "https://registry.yarnpkg.com/@babel/helpers/-/helpers-7.9.6.tgz#092c774743471d0bb6c7de3ad465ab3d3486d580"
- integrity sha512-tI4bUbldloLcHWoRUMAj4g1bF313M/o6fBKhIsb3QnGVPwRm9JsNf/gqMkQ7zjqReABiffPV6RWj7hEglID5Iw==
- dependencies:
- "@babel/template" "^7.8.3"
- "@babel/traverse" "^7.9.6"
- "@babel/types" "^7.9.6"
-
"@babel/highlight@^7.8.3":
version "7.9.0"
resolved "https://registry.yarnpkg.com/@babel/highlight/-/highlight-7.9.0.tgz#4e9b45ccb82b79607271b2979ad82c7b68163079"
@@ -331,11 +274,6 @@
resolved "https://registry.yarnpkg.com/@babel/parser/-/parser-7.9.4.tgz#68a35e6b0319bbc014465be43828300113f2f2e8"
integrity sha512-bC49otXX6N0/VYhgOMh4gnP26E9xnDZK3TmbNpxYzzz9BQLBosQwfyOe9/cXUU3txYhTzLCbcqd5c8y/OmCjHA==
-"@babel/parser@^7.9.6":
- version "7.9.6"
- resolved "https://registry.yarnpkg.com/@babel/parser/-/parser-7.9.6.tgz#3b1bbb30dabe600cd72db58720998376ff653bc7"
- integrity sha512-AoeIEJn8vt+d/6+PXDRPaksYhnlbMIiejioBZvvMQsOjW/JYK6k/0dKnvvP3EhK5GfMBWDPtrxRtegWdAcdq9Q==
-
"@babel/plugin-proposal-async-generator-functions@^7.8.3":
version "7.8.3"
resolved "https://registry.yarnpkg.com/@babel/plugin-proposal-async-generator-functions/-/plugin-proposal-async-generator-functions-7.8.3.tgz#bad329c670b382589721b27540c7d288601c6e6f"
@@ -345,7 +283,7 @@
"@babel/helper-remap-async-to-generator" "^7.8.3"
"@babel/plugin-syntax-async-generators" "^7.8.0"
-"@babel/plugin-proposal-class-properties@7.8.3", "@babel/plugin-proposal-class-properties@^7.8.3":
+"@babel/plugin-proposal-class-properties@7.8.3":
version "7.8.3"
resolved "https://registry.yarnpkg.com/@babel/plugin-proposal-class-properties/-/plugin-proposal-class-properties-7.8.3.tgz#5e06654af5cd04b608915aada9b2a6788004464e"
integrity sha512-EqFhbo7IosdgPgZggHaNObkmO1kNUe3slaKu54d5OWvy+p9QIKOzK1GAEpAIsZtWVtPXUHSMcT4smvDrCfY4AA==
@@ -993,7 +931,7 @@
"@babel/plugin-transform-react-jsx-self" "^7.9.0"
"@babel/plugin-transform-react-jsx-source" "^7.9.0"
-"@babel/preset-react@^7.0.0", "@babel/preset-react@^7.9.4":
+"@babel/preset-react@^7.0.0":
version "7.9.4"
resolved "https://registry.yarnpkg.com/@babel/preset-react/-/preset-react-7.9.4.tgz#c6c97693ac65b6b9c0b4f25b948a8f665463014d"
integrity sha512-AxylVB3FXeOTQXNXyiuAQJSvss62FEotbX2Pzx3K/7c+MKJMdSg6Ose6QYllkdCFA8EInCJVw7M/o5QbLuA4ZQ==
@@ -1059,21 +997,6 @@
globals "^11.1.0"
lodash "^4.17.13"
-"@babel/traverse@^7.9.6":
- version "7.9.6"
- resolved "https://registry.yarnpkg.com/@babel/traverse/-/traverse-7.9.6.tgz#5540d7577697bf619cc57b92aa0f1c231a94f442"
- integrity sha512-b3rAHSjbxy6VEAvlxM8OV/0X4XrG72zoxme6q1MOoe2vd0bEc+TwayhuC1+Dfgqh1QEG+pj7atQqvUprHIccsg==
- dependencies:
- "@babel/code-frame" "^7.8.3"
- "@babel/generator" "^7.9.6"
- "@babel/helper-function-name" "^7.9.5"
- "@babel/helper-split-export-declaration" "^7.8.3"
- "@babel/parser" "^7.9.6"
- "@babel/types" "^7.9.6"
- debug "^4.1.0"
- globals "^11.1.0"
- lodash "^4.17.13"
-
"@babel/types@^7.0.0", "@babel/types@^7.3.0", "@babel/types@^7.4.0", "@babel/types@^7.4.4", "@babel/types@^7.7.0", "@babel/types@^7.8.3", "@babel/types@^7.8.6", "@babel/types@^7.9.0", "@babel/types@^7.9.5":
version "7.9.5"
resolved "https://registry.yarnpkg.com/@babel/types/-/types-7.9.5.tgz#89231f82915a8a566a703b3b20133f73da6b9444"
@@ -1083,15 +1006,6 @@
lodash "^4.17.13"
to-fast-properties "^2.0.0"
-"@babel/types@^7.9.6":
- version "7.9.6"
- resolved "https://registry.yarnpkg.com/@babel/types/-/types-7.9.6.tgz#2c5502b427251e9de1bd2dff95add646d95cc9f7"
- integrity sha512-qxXzvBO//jO9ZnoasKF1uJzHd2+M6Q2ZPIVfnFps8JJvXy0ZBbwbNOmE6SGIY5XOY6d1Bo5lb9d9RJ8nv3WSeA==
- dependencies:
- "@babel/helper-validator-identifier" "^7.9.5"
- lodash "^4.17.13"
- to-fast-properties "^2.0.0"
-
"@cnakazawa/watch@^1.0.3":
version "1.0.4"
resolved "https://registry.yarnpkg.com/@cnakazawa/watch/-/watch-1.0.4.tgz#f864ae85004d0fcab6f50be9141c4da368d1656a"
@@ -1324,6 +1238,16 @@
"@types/istanbul-reports" "^1.1.1"
"@types/yargs" "^13.0.0"
+"@jest/types@^25.5.0":
+ version "25.5.0"
+ resolved "https://registry.yarnpkg.com/@jest/types/-/types-25.5.0.tgz#4d6a4793f7b9599fc3680877b856a97dbccf2a9d"
+ integrity sha512-OXD0RgQ86Tu3MazKo8bnrkDRaDXXMGUqd+kTtLtK1Zb7CRzQcaSRPPPV37SvYTdevXEBVxe0HXylEjs8ibkmCw==
+ dependencies:
+ "@types/istanbul-lib-coverage" "^2.0.0"
+ "@types/istanbul-reports" "^1.1.1"
+ "@types/yargs" "^15.0.0"
+ chalk "^3.0.0"
+
"@material-ui/core@^4.9.13":
version "4.9.13"
resolved "https://registry.yarnpkg.com/@material-ui/core/-/core-4.9.13.tgz#024962bcdda05139e1bad17a1815bf4088702b15"
@@ -1580,11 +1504,38 @@
dependencies:
"@babel/types" "^7.3.0"
+"@types/cheerio@*":
+ version "0.22.18"
+ resolved "https://registry.yarnpkg.com/@types/cheerio/-/cheerio-0.22.18.tgz#19018dceae691509901e339d63edf1e935978fe6"
+ integrity sha512-Fq7R3fINAPSdUEhOyjG4iVxgHrOnqDJbY0/BUuiN0pvD/rfmZWekVZnv+vcs8TtpA2XF50uv50LaE4EnpEL/Hw==
+ dependencies:
+ "@types/node" "*"
+
+"@types/classnames@^2.2.10":
+ version "2.2.10"
+ resolved "https://registry.yarnpkg.com/@types/classnames/-/classnames-2.2.10.tgz#cc658ca319b6355399efc1f5b9e818f1a24bf999"
+ integrity sha512-1UzDldn9GfYYEsWWnn/P4wkTlkZDH7lDb0wBMGbtIQc9zXEQq7FlKBdZUn6OBqD8sKZZ2RQO2mAjGpXiDGoRmQ==
+
"@types/color-name@^1.1.1":
version "1.1.1"
resolved "https://registry.yarnpkg.com/@types/color-name/-/color-name-1.1.1.tgz#1c1261bbeaa10a8055bbc5d8ab84b7b2afc846a0"
integrity sha512-rr+OQyAjxze7GgWrSaJwydHStIhHq2lvY3BOC2Mj7KnzI7XK0Uw1TOOdI9lDoajEbSWLiYgoo4f1R51erQfhPQ==
+"@types/enzyme-adapter-react-16@^1.0.6":
+ version "1.0.6"
+ resolved "https://registry.yarnpkg.com/@types/enzyme-adapter-react-16/-/enzyme-adapter-react-16-1.0.6.tgz#8aca7ae2fd6c7137d869b6616e696d21bb8b0cec"
+ integrity sha512-VonDkZ15jzqDWL8mPFIQnnLtjwebuL9YnDkqeCDYnB4IVgwUm0mwKkqhrxLL6mb05xm7qqa3IE95m8CZE9imCg==
+ dependencies:
+ "@types/enzyme" "*"
+
+"@types/enzyme@*", "@types/enzyme@^3.10.5":
+ version "3.10.5"
+ resolved "https://registry.yarnpkg.com/@types/enzyme/-/enzyme-3.10.5.tgz#fe7eeba3550369eed20e7fb565bfb74eec44f1f0"
+ integrity sha512-R+phe509UuUYy9Tk0YlSbipRpfVtIzb/9BHn5pTEtjJTF5LXvUjrIQcZvNyANNEyFrd2YGs196PniNT1fgvOQA==
+ dependencies:
+ "@types/cheerio" "*"
+ "@types/react" "*"
+
"@types/eslint-visitor-keys@^1.0.0":
version "1.0.0"
resolved "https://registry.yarnpkg.com/@types/eslint-visitor-keys/-/eslint-visitor-keys-1.0.0.tgz#1ee30d79544ca84d68d4b3cdb0af4f205663dd2d"
@@ -1604,6 +1555,16 @@
"@types/minimatch" "*"
"@types/node" "*"
+"@types/history@*":
+ version "4.7.5"
+ resolved "https://registry.yarnpkg.com/@types/history/-/history-4.7.5.tgz#527d20ef68571a4af02ed74350164e7a67544860"
+ integrity sha512-wLD/Aq2VggCJXSjxEwrMafIP51Z+13H78nXIX0ABEuIGhmB5sNGbR113MOKo+yfw+RDo1ZU3DM6yfnnRF/+ouw==
+
+"@types/is-url@^1.2.28":
+ version "1.2.28"
+ resolved "https://registry.yarnpkg.com/@types/is-url/-/is-url-1.2.28.tgz#914dabd50546d9b0142806e42c72bc7c2b7e0787"
+ integrity sha1-kU2r1QVG2bAUKAbkLHK8fCt+B4c=
+
"@types/istanbul-lib-coverage@*", "@types/istanbul-lib-coverage@^2.0.0":
version "2.0.1"
resolved "https://registry.yarnpkg.com/@types/istanbul-lib-coverage/-/istanbul-lib-coverage-2.0.1.tgz#42995b446db9a48a11a07ec083499a860e9138ff"
@@ -1624,6 +1585,14 @@
"@types/istanbul-lib-coverage" "*"
"@types/istanbul-lib-report" "*"
+"@types/jest@^25.2.1":
+ version "25.2.1"
+ resolved "https://registry.yarnpkg.com/@types/jest/-/jest-25.2.1.tgz#9544cd438607955381c1bdbdb97767a249297db5"
+ integrity sha512-msra1bCaAeEdkSyA0CZ6gW1ukMIvZ5YoJkdXw/qhQdsuuDlFTcEUrUw8CLCPt2rVRUfXlClVvK2gvPs9IokZaA==
+ dependencies:
+ jest-diff "^25.2.1"
+ pretty-format "^25.2.1"
+
"@types/json-schema@^7.0.3":
version "7.0.4"
resolved "https://registry.yarnpkg.com/@types/json-schema/-/json-schema-7.0.4.tgz#38fd73ddfd9b55abb1e1b2ed578cb55bd7b7d339"
@@ -1654,6 +1623,23 @@
resolved "https://registry.yarnpkg.com/@types/q/-/q-1.5.2.tgz#690a1475b84f2a884fd07cd797c00f5f31356ea8"
integrity sha512-ce5d3q03Ex0sy4R14722Rmt6MT07Ua+k4FwDfdcToYJcMKNtRVQvJ6JCAPdAmAnbRb6CsX6aYb9m96NGod9uTw==
+"@types/react-router-dom@^5.1.5":
+ version "5.1.5"
+ resolved "https://registry.yarnpkg.com/@types/react-router-dom/-/react-router-dom-5.1.5.tgz#7c334a2ea785dbad2b2dcdd83d2cf3d9973da090"
+ integrity sha512-ArBM4B1g3BWLGbaGvwBGO75GNFbLDUthrDojV2vHLih/Tq8M+tgvY1DSwkuNrPSwdp/GUL93WSEpTZs8nVyJLw==
+ dependencies:
+ "@types/history" "*"
+ "@types/react" "*"
+ "@types/react-router" "*"
+
+"@types/react-router@*":
+ version "5.1.7"
+ resolved "https://registry.yarnpkg.com/@types/react-router/-/react-router-5.1.7.tgz#e9d12ed7dcfc79187e4d36667745b69a5aa11556"
+ integrity sha512-2ouP76VQafKjtuc0ShpwUebhHwJo0G6rhahW9Pb8au3tQTjYXd2jta4wv6U2tGLR/I42yuG00+UXjNYY0dTzbg==
+ dependencies:
+ "@types/history" "*"
+ "@types/react" "*"
+
"@types/react-transition-group@^4.2.0":
version "4.2.4"
resolved "https://registry.yarnpkg.com/@types/react-transition-group/-/react-transition-group-4.2.4.tgz#c7416225987ccdb719262766c1483da8f826838d"
@@ -1693,6 +1679,21 @@
dependencies:
"@types/yargs-parser" "*"
+"@types/yargs@^15.0.0":
+ version "15.0.4"
+ resolved "https://registry.yarnpkg.com/@types/yargs/-/yargs-15.0.4.tgz#7e5d0f8ca25e9d5849f2ea443cf7c402decd8299"
+ integrity sha512-9T1auFmbPZoxHz0enUFlUuKRy3it01R+hlggyVUMtnCTQRunsQYifnSGb8hET4Xo8yiC0o0r1paW3ud5+rbURg==
+ dependencies:
+ "@types/yargs-parser" "*"
+
+"@typescript-eslint/eslint-plugin-tslint@^2.30.0":
+ version "2.30.0"
+ resolved "https://registry.yarnpkg.com/@typescript-eslint/eslint-plugin-tslint/-/eslint-plugin-tslint-2.30.0.tgz#408e356066add27af23dbeebdb67459a67d9799a"
+ integrity sha512-phARGRY1SyAkG9uVhF7o0yjK1eqmGYCwM7JpNkOo/50d68ZG0V/P9VyYMSKAj+IbRlZ/k2rKFibKZvfLJPcFGw==
+ dependencies:
+ "@typescript-eslint/experimental-utils" "2.30.0"
+ lodash "^4.17.15"
+
"@typescript-eslint/eslint-plugin@^2.10.0":
version "2.27.0"
resolved "https://registry.yarnpkg.com/@typescript-eslint/eslint-plugin/-/eslint-plugin-2.27.0.tgz#e479cdc4c9cf46f96b4c287755733311b0d0ba4b"
@@ -1703,6 +1704,16 @@
regexpp "^3.0.0"
tsutils "^3.17.1"
+"@typescript-eslint/eslint-plugin@^2.30.0":
+ version "2.30.0"
+ resolved "https://registry.yarnpkg.com/@typescript-eslint/eslint-plugin/-/eslint-plugin-2.30.0.tgz#312a37e80542a764d96e8ad88a105316cdcd7b05"
+ integrity sha512-PGejii0qIZ9Q40RB2jIHyUpRWs1GJuHP1pkoCiaeicfwO9z7Fx03NQzupuyzAmv+q9/gFNHu7lo1ByMXe8PNyg==
+ dependencies:
+ "@typescript-eslint/experimental-utils" "2.30.0"
+ functional-red-black-tree "^1.0.1"
+ regexpp "^3.0.0"
+ tsutils "^3.17.1"
+
"@typescript-eslint/experimental-utils@2.27.0":
version "2.27.0"
resolved "https://registry.yarnpkg.com/@typescript-eslint/experimental-utils/-/experimental-utils-2.27.0.tgz#801a952c10b58e486c9a0b36cf21e2aab1e9e01a"
@@ -1713,6 +1724,16 @@
eslint-scope "^5.0.0"
eslint-utils "^2.0.0"
+"@typescript-eslint/experimental-utils@2.30.0":
+ version "2.30.0"
+ resolved "https://registry.yarnpkg.com/@typescript-eslint/experimental-utils/-/experimental-utils-2.30.0.tgz#9845e868c01f3aed66472c561d4b6bac44809dd0"
+ integrity sha512-L3/tS9t+hAHksy8xuorhOzhdefN0ERPDWmR9CclsIGOUqGKy6tqc/P+SoXeJRye5gazkuPO0cK9MQRnolykzkA==
+ dependencies:
+ "@types/json-schema" "^7.0.3"
+ "@typescript-eslint/typescript-estree" "2.30.0"
+ eslint-scope "^5.0.0"
+ eslint-utils "^2.0.0"
+
"@typescript-eslint/parser@^2.10.0":
version "2.27.0"
resolved "https://registry.yarnpkg.com/@typescript-eslint/parser/-/parser-2.27.0.tgz#d91664335b2c46584294e42eb4ff35838c427287"
@@ -1723,6 +1744,16 @@
"@typescript-eslint/typescript-estree" "2.27.0"
eslint-visitor-keys "^1.1.0"
+"@typescript-eslint/parser@^2.30.0":
+ version "2.30.0"
+ resolved "https://registry.yarnpkg.com/@typescript-eslint/parser/-/parser-2.30.0.tgz#7681c305a6f4341ae2579f5e3a75846c29eee9ce"
+ integrity sha512-9kDOxzp0K85UnpmPJqUzdWaCNorYYgk1yZmf4IKzpeTlSAclnFsrLjfwD9mQExctLoLoGAUXq1co+fbr+3HeFw==
+ dependencies:
+ "@types/eslint-visitor-keys" "^1.0.0"
+ "@typescript-eslint/experimental-utils" "2.30.0"
+ "@typescript-eslint/typescript-estree" "2.30.0"
+ eslint-visitor-keys "^1.1.0"
+
"@typescript-eslint/typescript-estree@2.27.0":
version "2.27.0"
resolved "https://registry.yarnpkg.com/@typescript-eslint/typescript-estree/-/typescript-estree-2.27.0.tgz#a288e54605412da8b81f1660b56c8b2e42966ce8"
@@ -1736,6 +1767,19 @@
semver "^6.3.0"
tsutils "^3.17.1"
+"@typescript-eslint/typescript-estree@2.30.0":
+ version "2.30.0"
+ resolved "https://registry.yarnpkg.com/@typescript-eslint/typescript-estree/-/typescript-estree-2.30.0.tgz#1b8e848b55144270255ffbfe4c63291f8f766615"
+ integrity sha512-nI5WOechrA0qAhnr+DzqwmqHsx7Ulr/+0H7bWCcClDhhWkSyZR5BmTvnBEyONwJCTWHfc5PAQExX24VD26IAVw==
+ dependencies:
+ debug "^4.1.1"
+ eslint-visitor-keys "^1.1.0"
+ glob "^7.1.6"
+ is-glob "^4.0.1"
+ lodash "^4.17.15"
+ semver "^6.3.0"
+ tsutils "^3.17.1"
+
"@webassemblyjs/ast@1.8.5":
version "1.8.5"
resolved "https://registry.yarnpkg.com/@webassemblyjs/ast/-/ast-1.8.5.tgz#51b1c5fe6576a34953bf4b253df9f0d490d9e359"
@@ -2069,7 +2113,7 @@ ansi-styles@^3.2.0, ansi-styles@^3.2.1:
dependencies:
color-convert "^1.9.0"
-ansi-styles@^4.1.0:
+ansi-styles@^4.0.0, ansi-styles@^4.1.0:
version "4.2.1"
resolved "https://registry.yarnpkg.com/ansi-styles/-/ansi-styles-4.2.1.tgz#90ae75c424d008d2624c5bf29ead3177ebfcf359"
integrity sha512-9VGjrMsG1vePxcSweQsN20KY/c4zN0h9fLjqAbwbPfahM3t+NL+M9HC8xeXG2I8pX5NoamTGNuomEUFI7fcUjA==
@@ -2205,7 +2249,7 @@ arrify@^1.0.1:
resolved "https://registry.yarnpkg.com/arrify/-/arrify-1.0.1.tgz#898508da2226f380df904728456849c1501a4b0d"
integrity sha1-iYUI2iIm84DfkEcoRWhJwVAaSw0=
-asap@~2.0.3, asap@~2.0.6:
+asap@~2.0.6:
version "2.0.6"
resolved "https://registry.yarnpkg.com/asap/-/asap-2.0.6.tgz#e50347611d7e690943208bbdafebcbc2fb866d46"
integrity sha1-5QNHYR1+aQlDIIu9r+vLwvuGbUY=
@@ -2733,11 +2777,6 @@ buffer@^4.3.0:
ieee754 "^1.1.4"
isarray "^1.0.0"
-builtin-modules@^1.1.1:
- version "1.1.1"
- resolved "https://registry.yarnpkg.com/builtin-modules/-/builtin-modules-1.1.1.tgz#270f076c5a72c02f5b65a47df94c5fe3a278892f"
- integrity sha1-Jw8HbFpywC9bZaR9+Uxf46J4iS8=
-
builtin-status-codes@^3.0.0:
version "3.0.0"
resolved "https://registry.yarnpkg.com/builtin-status-codes/-/builtin-status-codes-3.0.0.tgz#85982878e21b98e1c66425e03d0174788f569ee8"
@@ -2892,7 +2931,7 @@ caseless@~0.12.0:
resolved "https://registry.yarnpkg.com/caseless/-/caseless-0.12.0.tgz#1b681c21ff84033c826543090689420d187151dc"
integrity sha1-G2gcIf+EAzyCZUMJBolCDRhxUdw=
-chalk@2.4.2, chalk@^2.0.0, chalk@^2.0.1, chalk@^2.1.0, chalk@^2.3.0, chalk@^2.4.1, chalk@^2.4.2:
+chalk@2.4.2, chalk@^2.0.0, chalk@^2.0.1, chalk@^2.1.0, chalk@^2.4.1, chalk@^2.4.2:
version "2.4.2"
resolved "https://registry.yarnpkg.com/chalk/-/chalk-2.4.2.tgz#cd42541677a54333cf541a49108c1432b44c9424"
integrity sha512-Mti+f9lpJNcwF4tWV8/OrTTtF1gZi+f8FqlyAdouralcFWFQWF2+NgCHShjkCb+IFBLq9buZwE1xckQU4peSuQ==
@@ -2920,11 +2959,6 @@ chalk@^3.0.0:
ansi-styles "^4.1.0"
supports-color "^7.1.0"
-change-emitter@^0.1.2:
- version "0.1.6"
- resolved "https://registry.yarnpkg.com/change-emitter/-/change-emitter-0.1.6.tgz#e8b2fe3d7f1ab7d69a32199aff91ea6931409515"
- integrity sha1-6LL+PX8at9aaMhma/5HqaTFAlRU=
-
chardet@^0.7.0:
version "0.7.0"
resolved "https://registry.yarnpkg.com/chardet/-/chardet-0.7.0.tgz#90094849f0937f2eedc2425d0d28a9e5f0cbad9e"
@@ -3173,12 +3207,12 @@ combined-stream@^1.0.6, combined-stream@~1.0.6:
dependencies:
delayed-stream "~1.0.0"
-commander@^2.11.0, commander@^2.12.1, commander@^2.19.0, commander@^2.20.0:
+commander@^2.11.0, commander@^2.19.0, commander@^2.20.0:
version "2.20.3"
resolved "https://registry.yarnpkg.com/commander/-/commander-2.20.3.tgz#fd485e84c03eb4881c20722ba48035e8531aeb33"
integrity sha512-GpVkmM8vF2vQUkj2LvZmD35JxeJOLCwJ9cUkugyk2nuhbv3+mJvpLYYt+0+USMxE+oj+ey/lJEnhZw75x/OMcQ==
-commander@^4.0.1, commander@^4.1.1:
+commander@^4.1.1:
version "4.1.1"
resolved "https://registry.yarnpkg.com/commander/-/commander-4.1.1.tgz#9fd602bd936294e9e9ef46a3f4d6964044b18068"
integrity sha512-NOKm8xhkzAjzFx8B2v5OAHT+u5pRQc2UCa2Vq9jYL/31o2wi9mxBA7LIFs3sV5VSC49z6pEhfbMULvShKj26WA==
@@ -3292,7 +3326,7 @@ content-type@~1.0.4:
resolved "https://registry.yarnpkg.com/content-type/-/content-type-1.0.4.tgz#e138cc75e040c727b1966fe5e5f8c9aee256fe3b"
integrity sha512-hIP3EEPs8tB9AT1L+NUqtwOAps4mk2Zob89MWXMHjHWg9milF/j4osnnQLXBCBFBk/tvIG/tUc9mOUJiPBhPXA==
-convert-source-map@1.7.0, convert-source-map@^1.1.0, convert-source-map@^1.4.0, convert-source-map@^1.7.0:
+convert-source-map@1.7.0, convert-source-map@^1.4.0, convert-source-map@^1.7.0:
version "1.7.0"
resolved "https://registry.yarnpkg.com/convert-source-map/-/convert-source-map-1.7.0.tgz#17a2cb882d7f77d3490585e2ce6c524424a3a442"
integrity sha512-4FJkXzKXEDB1snCFZlLP4gpC3JILicCpGbzG9f9G7tGqGCzETQ2hWPrcinA9oU4wtf2biUaEH5065UnMeR33oA==
@@ -3344,11 +3378,6 @@ core-js-pure@^3.0.0:
resolved "https://registry.yarnpkg.com/core-js-pure/-/core-js-pure-3.6.5.tgz#c79e75f5e38dbc85a662d91eea52b8256d53b813"
integrity sha512-lacdXOimsiD0QyNf9BC/mxivNJ/ybBGJXQFKzRekp1WTHoVUWsUHEn+2T8GJAzzIhyOuXA+gOxCVN3l+5PLPUA==
-core-js@^1.0.0:
- version "1.2.7"
- resolved "https://registry.yarnpkg.com/core-js/-/core-js-1.2.7.tgz#652294c14651db28fa93bd2d5ff2983a4f08c636"
- integrity sha1-ZSKUwUZR2yj6k70tX/KYOk8IxjY=
-
core-js@^2.4.0:
version "2.6.11"
resolved "https://registry.yarnpkg.com/core-js/-/core-js-2.6.11.tgz#38831469f9922bded8ee21c9dc46985e0399308c"
@@ -3886,10 +3915,10 @@ diff-sequences@^24.9.0:
resolved "https://registry.yarnpkg.com/diff-sequences/-/diff-sequences-24.9.0.tgz#5715d6244e2aa65f48bba0bc972db0b0b11e95b5"
integrity sha512-Dj6Wk3tWyTE+Fo1rW8v0Xhwk80um6yFYKbuAxc9c3EZxIHFDYwbi34Uk42u1CdnIiVorvt4RmlSDjIPyzGC2ew==
-diff@^4.0.1:
- version "4.0.2"
- resolved "https://registry.yarnpkg.com/diff/-/diff-4.0.2.tgz#60f3aecb89d5fae520c11aa19efc2bb982aade7d"
- integrity sha512-58lmxKSA4BNyLz+HHMUzlOEpg09FV+ev6ZMe3vJihgdxzgcwZ8VoEEPmALCZG9LmqfVoNMMKpttIYTVG6uDY7A==
+diff-sequences@^25.2.6:
+ version "25.2.6"
+ resolved "https://registry.yarnpkg.com/diff-sequences/-/diff-sequences-25.2.6.tgz#5f467c00edd35352b7bca46d7927d60e687a76dd"
+ integrity sha512-Hq8o7+6GaZeoFjtpgvRBUknSXNeJiCx7V9Fr94ZMljNiCr9n9L8H8aJqgWOQiDDGdyn29fRNcDdRVJ5fdyihfg==
diffie-hellman@^5.0.0:
version "5.0.3"
@@ -4127,13 +4156,6 @@ encodeurl@~1.0.2:
resolved "https://registry.yarnpkg.com/encodeurl/-/encodeurl-1.0.2.tgz#ad3ff4c86ec2d029322f5a02c3a9a606c95b3f59"
integrity sha1-rT/0yG7C0CkyL1oCw6mmBslbP1k=
-encoding@^0.1.11:
- version "0.1.12"
- resolved "https://registry.yarnpkg.com/encoding/-/encoding-0.1.12.tgz#538b66f3ee62cd1ab51ec323829d1f9480c74beb"
- integrity sha1-U4tm8+5izRq1HsMjgp0flIDHS+s=
- dependencies:
- iconv-lite "~0.4.13"
-
end-of-stream@^1.0.0, end-of-stream@^1.1.0:
version "1.4.4"
resolved "https://registry.yarnpkg.com/end-of-stream/-/end-of-stream-1.4.4.tgz#5ae64a5f45057baf3626ec14da0ca5e4b2431eb0"
@@ -4332,6 +4354,31 @@ escodegen@^1.11.0, escodegen@^1.9.1:
optionalDependencies:
source-map "~0.6.1"
+eslint-config-airbnb-base@^14.1.0:
+ version "14.1.0"
+ resolved "https://registry.yarnpkg.com/eslint-config-airbnb-base/-/eslint-config-airbnb-base-14.1.0.tgz#2ba4592dd6843258221d9bff2b6831bd77c874e4"
+ integrity sha512-+XCcfGyCnbzOnktDVhwsCAx+9DmrzEmuwxyHUJpw+kqBVT744OUBrB09khgFKlK1lshVww6qXGsYPZpavoNjJw==
+ dependencies:
+ confusing-browser-globals "^1.0.9"
+ object.assign "^4.1.0"
+ object.entries "^1.1.1"
+
+eslint-config-airbnb@^18.1.0:
+ version "18.1.0"
+ resolved "https://registry.yarnpkg.com/eslint-config-airbnb/-/eslint-config-airbnb-18.1.0.tgz#724d7e93dadd2169492ff5363c5aaa779e01257d"
+ integrity sha512-kZFuQC/MPnH7KJp6v95xsLBf63G/w7YqdPfQ0MUanxQ7zcKUNG8j+sSY860g3NwCBOa62apw16J6pRN+AOgXzw==
+ dependencies:
+ eslint-config-airbnb-base "^14.1.0"
+ object.assign "^4.1.0"
+ object.entries "^1.1.1"
+
+eslint-config-prettier@^6.11.0:
+ version "6.11.0"
+ resolved "https://registry.yarnpkg.com/eslint-config-prettier/-/eslint-config-prettier-6.11.0.tgz#f6d2238c1290d01c859a8b5c1f7d352a0b0da8b1"
+ integrity sha512-oB8cpLWSAjOVFEJhhyMZh6NOEOtBVziaqdDQ86+qhDHFbZXoRTM7pNSvFRfW/W/L/LrQ38C99J5CGuRBBzBsdA==
+ dependencies:
+ get-stdin "^6.0.0"
+
eslint-config-react-app@^5.2.1:
version "5.2.1"
resolved "https://registry.yarnpkg.com/eslint-config-react-app/-/eslint-config-react-app-5.2.1.tgz#698bf7aeee27f0cea0139eaef261c7bf7dd623df"
@@ -4339,6 +4386,11 @@ eslint-config-react-app@^5.2.1:
dependencies:
confusing-browser-globals "^1.0.9"
+eslint-config-react@^1.1.7:
+ version "1.1.7"
+ resolved "https://registry.yarnpkg.com/eslint-config-react/-/eslint-config-react-1.1.7.tgz#a0918d0fc47d0e9bd161a47308021da85d2585b3"
+ integrity sha1-oJGND8R9DpvRYaRzCAIdqF0lhbM=
+
eslint-import-resolver-node@^0.3.2:
version "0.3.3"
resolved "https://registry.yarnpkg.com/eslint-import-resolver-node/-/eslint-import-resolver-node-0.3.3.tgz#dbaa52b6b2816b50bc6711af75422de808e98404"
@@ -4406,12 +4458,19 @@ eslint-plugin-jsx-a11y@6.2.3:
has "^1.0.3"
jsx-ast-utils "^2.2.1"
+eslint-plugin-prettier@^3.1.3:
+ version "3.1.3"
+ resolved "https://registry.yarnpkg.com/eslint-plugin-prettier/-/eslint-plugin-prettier-3.1.3.tgz#ae116a0fc0e598fdae48743a4430903de5b4e6ca"
+ integrity sha512-+HG5jmu/dN3ZV3T6eCD7a4BlAySdN7mLIbJYo0z1cFQuI+r2DiTJEFeF68ots93PsnrMxbzIZ2S/ieX+mkrBeQ==
+ dependencies:
+ prettier-linter-helpers "^1.0.0"
+
eslint-plugin-react-hooks@^1.6.1:
version "1.7.0"
resolved "https://registry.yarnpkg.com/eslint-plugin-react-hooks/-/eslint-plugin-react-hooks-1.7.0.tgz#6210b6d5a37205f0b92858f895a4e827020a7d04"
integrity sha512-iXTCFcOmlWvw4+TOE8CLWj6yX1GwzT0Y6cUfHHZqWnSk144VmVIRcVGtUAzrLES7C798lmvnt02C7rxaOX1HNA==
-eslint-plugin-react@7.19.0:
+eslint-plugin-react@7.19.0, eslint-plugin-react@^7.19.0:
version "7.19.0"
resolved "https://registry.yarnpkg.com/eslint-plugin-react/-/eslint-plugin-react-7.19.0.tgz#6d08f9673628aa69c5559d33489e855d83551666"
integrity sha512-SPT8j72CGuAP+JFbT0sJHOB80TX/pu44gQ4vXH/cq+hQTiY2PuZ6IHkqXJV6x1b28GDdo1lbInjKUrrdUf0LOQ==
@@ -4464,7 +4523,7 @@ eslint-visitor-keys@^1.0.0, eslint-visitor-keys@^1.1.0:
resolved "https://registry.yarnpkg.com/eslint-visitor-keys/-/eslint-visitor-keys-1.1.0.tgz#e2a82cea84ff246ad6fb57f9bde5b46621459ec2"
integrity sha512-8y9YjtM1JBJU/A9Kc+SbaOV4y29sSWckBwMHa+FGtVj5gN/sbnKDf6xJUl+8g7FAij9LVaP8C24DUiH/f/2Z9A==
-eslint@^6.6.0:
+eslint@^6.6.0, eslint@^6.8.0:
version "6.8.0"
resolved "https://registry.yarnpkg.com/eslint/-/eslint-6.8.0.tgz#62262d6729739f9275723824302fb227c8c93ffb"
integrity sha512-K+Iayyo2LtyYhDSYwz5D5QdWw0hCacNzyq1Y821Xna2xSJj7cijoLLYmLxTQgcgZ9mC61nryMy9S7GRbYpI5Ig==
@@ -4729,6 +4788,11 @@ fast-deep-equal@^3.1.1:
resolved "https://registry.yarnpkg.com/fast-deep-equal/-/fast-deep-equal-3.1.1.tgz#545145077c501491e33b15ec408c294376e94ae4"
integrity sha512-8UEa58QDLauDNfpbrX55Q9jrGHThw2ZMdOky5Gl1CDtVeJDPVrG4Jxx1N8jw2gkWaff5UUuX1KJd+9zGe2B+ZA==
+fast-diff@^1.1.2:
+ version "1.2.0"
+ resolved "https://registry.yarnpkg.com/fast-diff/-/fast-diff-1.2.0.tgz#73ee11982d86caaf7959828d519cfe927fac5f03"
+ integrity sha512-xJuoT5+L99XlZ8twedaRf6Ax2TgQVxvgZOYoPKqZufmJib0tL2tegPBOZb1pVNgIhlqDlA0eO0c3wBvQcmzx4w==
+
fast-glob@^2.0.2:
version "2.2.7"
resolved "https://registry.yarnpkg.com/fast-glob/-/fast-glob-2.2.7.tgz#6953857c3afa475fff92ee6015d52da70a4cd39d"
@@ -4772,19 +4836,6 @@ fb-watchman@^2.0.0:
dependencies:
bser "2.1.1"
-fbjs@^0.8.1:
- version "0.8.17"
- resolved "https://registry.yarnpkg.com/fbjs/-/fbjs-0.8.17.tgz#c4d598ead6949112653d6588b01a5cdcd9f90fdd"
- integrity sha1-xNWY6taUkRJlPWWIsBpc3Nn5D90=
- dependencies:
- core-js "^1.0.0"
- isomorphic-fetch "^2.1.1"
- loose-envify "^1.0.0"
- object-assign "^4.1.0"
- promise "^7.1.1"
- setimmediate "^1.0.5"
- ua-parser-js "^0.7.18"
-
figgy-pudding@^3.5.1:
version "3.5.2"
resolved "https://registry.yarnpkg.com/figgy-pudding/-/figgy-pudding-3.5.2.tgz#b4eee8148abb01dcf1d1ac34367d59e12fa61d6e"
@@ -5047,11 +5098,6 @@ fs-minipass@^2.0.0:
dependencies:
minipass "^3.0.0"
-fs-readdir-recursive@^1.1.0:
- version "1.1.0"
- resolved "https://registry.yarnpkg.com/fs-readdir-recursive/-/fs-readdir-recursive-1.1.0.tgz#e32fc030a2ccee44a6b5371308da54be0b397d27"
- integrity sha512-GNanXlVr2pf02+sPN40XN8HG+ePaNcvM0q5mZBd668Obwb0yD5GiUbZOFgwn8kGMY6I3mdyDJzieUy3PTYyTRA==
-
fs-write-stream-atomic@^1.0.8:
version "1.0.10"
resolved "https://registry.yarnpkg.com/fs-write-stream-atomic/-/fs-write-stream-atomic-1.0.10.tgz#b47df53493ef911df75731e70a9ded0189db40c9"
@@ -5124,6 +5170,11 @@ get-own-enumerable-property-symbols@^3.0.0:
resolved "https://registry.yarnpkg.com/get-own-enumerable-property-symbols/-/get-own-enumerable-property-symbols-3.0.2.tgz#b5fde77f22cbe35f390b4e089922c50bce6ef664"
integrity sha512-I0UBV/XOz1XkIJHEUDMZAbzCThU/H8DxmSfmdGcKPnVhu2VfFqr34jr9777IyaTYvxjedWhqVIilEDsCdP5G6g==
+get-stdin@^6.0.0:
+ version "6.0.0"
+ resolved "https://registry.yarnpkg.com/get-stdin/-/get-stdin-6.0.0.tgz#9e09bf712b360ab9225e812048f71fde9c89657b"
+ integrity sha512-jp4tHawyV7+fkkSKyvjuLZswblUtz+SQKzSWnBbii16BuZksJlU1wuBYXY75r+duh/llF1ur6oNwi+2ZzjKZ7g==
+
get-stream@^4.0.0:
version "4.1.0"
resolved "https://registry.yarnpkg.com/get-stream/-/get-stream-4.1.0.tgz#c1b255575f3dc21d59bfc79cd3d2b46b1c3a54b5"
@@ -5163,7 +5214,7 @@ glob-to-regexp@^0.3.0:
resolved "https://registry.yarnpkg.com/glob-to-regexp/-/glob-to-regexp-0.3.0.tgz#8c5a1494d2066c570cc3bfe4496175acc4d502ab"
integrity sha1-jFoUlNIGbFcMw7/kSWF1rMTVAqs=
-glob@^7.0.0, glob@^7.0.3, glob@^7.1.1, glob@^7.1.2, glob@^7.1.3, glob@^7.1.4, glob@^7.1.6:
+glob@^7.0.3, glob@^7.1.1, glob@^7.1.2, glob@^7.1.3, glob@^7.1.4, glob@^7.1.6:
version "7.1.6"
resolved "https://registry.yarnpkg.com/glob/-/glob-7.1.6.tgz#141f33b81a7c2492e125594307480c46679278a6"
integrity sha512-LwaxwyZ72Lk7vZINtNNrywX0ZuLyStrdDtabefZKAY5ZGJhVtgdznluResxNmPitE0SAO+O26sWTHeKSI2wMBA==
@@ -5380,11 +5431,6 @@ hmac-drbg@^1.0.0:
minimalistic-assert "^1.0.0"
minimalistic-crypto-utils "^1.0.1"
-hoist-non-react-statics@^2.3.1:
- version "2.5.5"
- resolved "https://registry.yarnpkg.com/hoist-non-react-statics/-/hoist-non-react-statics-2.5.5.tgz#c5903cf409c0dfd908f388e619d86b9c1174cb47"
- integrity sha512-rqcy4pJo55FTTLWt+bU8ukscqHeE/e9KWvsOW2b/a3afxQZhwkQdT1rPPCJ0rYXdj4vNcasY8zHTH+jF/qStxw==
-
hoist-non-react-statics@^3.1.0, hoist-non-react-statics@^3.2.0, hoist-non-react-statics@^3.3.0, hoist-non-react-statics@^3.3.2:
version "3.3.2"
resolved "https://registry.yarnpkg.com/hoist-non-react-statics/-/hoist-non-react-statics-3.3.2.tgz#ece0acaf71d62c2969c2ec59feff42a4b1a85b45"
@@ -5569,7 +5615,7 @@ hyphenate-style-name@^1.0.3:
resolved "https://registry.yarnpkg.com/hyphenate-style-name/-/hyphenate-style-name-1.0.3.tgz#097bb7fa0b8f1a9cf0bd5c734cf95899981a9b48"
integrity sha512-EcuixamT82oplpoJ2XU4pDtKGWQ7b00CD9f1ug9IaQ3p1bkHMiKCZ9ut9QDI6qsa6cpUuB+A/I+zLtdNK4n2DQ==
-iconv-lite@0.4.24, iconv-lite@^0.4.24, iconv-lite@~0.4.13:
+iconv-lite@0.4.24, iconv-lite@^0.4.24:
version "0.4.24"
resolved "https://registry.yarnpkg.com/iconv-lite/-/iconv-lite-0.4.24.tgz#2022b4b25fbddc21d2f524974a474aafe733908b"
integrity sha512-v3MXnZAcvnywkTUEZomIActle7RXXeedOR31wwl7VlyoXO4Qi9arvSenNQWne1TcRwhCL1HwLI21bEqdpj8/rA==
@@ -6077,7 +6123,7 @@ is-root@2.1.0:
resolved "https://registry.yarnpkg.com/is-root/-/is-root-2.1.0.tgz#809e18129cf1129644302a4f8544035d51984a9c"
integrity sha512-AGOriNp96vNBd3HtU+RzFEc75FfR5ymiYv8E553I71SCeXBiMsVDUtdio1OEFvrPyLIQ9tVR5RxXIFe5PUFjMg==
-is-stream@^1.0.1, is-stream@^1.1.0:
+is-stream@^1.1.0:
version "1.1.0"
resolved "https://registry.yarnpkg.com/is-stream/-/is-stream-1.1.0.tgz#12d4a3dd4e68e0b79ceb8dbc84173ae80d91ca44"
integrity sha1-EtSj3U5o4Lec6428hBc66A2RykQ=
@@ -6165,14 +6211,6 @@ isobject@^3.0.0, isobject@^3.0.1:
resolved "https://registry.yarnpkg.com/isobject/-/isobject-3.0.1.tgz#4e431e92b11a9731636aa1f9c8d1ccbcfdab78df"
integrity sha1-TkMekrEalzFjaqH5yNHMvP2reN8=
-isomorphic-fetch@^2.1.1:
- version "2.2.1"
- resolved "https://registry.yarnpkg.com/isomorphic-fetch/-/isomorphic-fetch-2.2.1.tgz#611ae1acf14f5e81f729507472819fe9733558a9"
- integrity sha1-YRrhrPFPXoH3KVB0coGf6XM1WKk=
- dependencies:
- node-fetch "^1.0.1"
- whatwg-fetch ">=0.10.0"
-
isstream@~0.1.2:
version "0.1.2"
resolved "https://registry.yarnpkg.com/isstream/-/isstream-0.1.2.tgz#47e63f7af55afa6f92e1500e690eb8b8529c099a"
@@ -6284,6 +6322,16 @@ jest-diff@^24.9.0:
jest-get-type "^24.9.0"
pretty-format "^24.9.0"
+jest-diff@^25.2.1:
+ version "25.5.0"
+ resolved "https://registry.yarnpkg.com/jest-diff/-/jest-diff-25.5.0.tgz#1dd26ed64f96667c068cef026b677dfa01afcfa9"
+ integrity sha512-z1kygetuPiREYdNIumRpAHY6RXiGmp70YHptjdaxTWGmA085W3iCnXNx0DhflK3vwrKmrRWyY1wUpkPMVxMK7A==
+ dependencies:
+ chalk "^3.0.0"
+ diff-sequences "^25.2.6"
+ jest-get-type "^25.2.6"
+ pretty-format "^25.5.0"
+
jest-docblock@^24.3.0:
version "24.9.0"
resolved "https://registry.yarnpkg.com/jest-docblock/-/jest-docblock-24.9.0.tgz#7970201802ba560e1c4092cc25cbedf5af5a8ce2"
@@ -6358,6 +6406,11 @@ jest-get-type@^24.9.0:
resolved "https://registry.yarnpkg.com/jest-get-type/-/jest-get-type-24.9.0.tgz#1684a0c8a50f2e4901b6644ae861f579eed2ef0e"
integrity sha512-lUseMzAley4LhIcpSP9Jf+fTrQ4a1yHQwLNeeVa2cEmbCGeoZAtYPOIv8JaxLD/sUpKxetKGP+gsHl8f8TSj8Q==
+jest-get-type@^25.2.6:
+ version "25.2.6"
+ resolved "https://registry.yarnpkg.com/jest-get-type/-/jest-get-type-25.2.6.tgz#0b0a32fab8908b44d508be81681487dbabb8d877"
+ integrity sha512-DxjtyzOHjObRM+sM1knti6or+eOgcGU4xVSb2HNP1TqO4ahsT+rqZg+nyqHWJSvWgKC5cG3QjGFBqxLghiF/Ig==
+
jest-haste-map@^24.9.0:
version "24.9.0"
resolved "https://registry.yarnpkg.com/jest-haste-map/-/jest-haste-map-24.9.0.tgz#b38a5d64274934e21fa417ae9a9fbeb77ceaac7d"
@@ -7656,14 +7709,6 @@ no-case@^3.0.3:
lower-case "^2.0.1"
tslib "^1.10.0"
-node-fetch@^1.0.1:
- version "1.7.3"
- resolved "https://registry.yarnpkg.com/node-fetch/-/node-fetch-1.7.3.tgz#980f6f72d85211a5347c6b2bc18c5b84c3eb47ef"
- integrity sha512-NhZ4CsKx7cYm2vSrBAr2PvFOe6sWDf0UYLRqA6svUYg7+/TSfVAu49jYC4BvQ4Sms9SZgdqGBgroqfDhJdTyKQ==
- dependencies:
- encoding "^0.1.11"
- is-stream "^1.0.1"
-
node-fetch@^2.2.0:
version "2.3.0"
resolved "https://registry.yarnpkg.com/node-fetch/-/node-fetch-2.3.0.tgz#1a1d940bbfb916a1d3e0219f037e89e71f8c5fa5"
@@ -9072,6 +9117,18 @@ prepend-http@^1.0.0:
resolved "https://registry.yarnpkg.com/prepend-http/-/prepend-http-1.0.4.tgz#d4f4562b0ce3696e41ac52d0e002e57a635dc6dc"
integrity sha1-1PRWKwzjaW5BrFLQ4ALlemNdxtw=
+prettier-linter-helpers@^1.0.0:
+ version "1.0.0"
+ resolved "https://registry.yarnpkg.com/prettier-linter-helpers/-/prettier-linter-helpers-1.0.0.tgz#d23d41fe1375646de2d0104d3454a3008802cf7b"
+ integrity sha512-GbK2cP9nraSSUF9N2XwUwqfzlAFlMNYYl+ShE/V+H8a9uNl/oUqB1w2EL54Jh0OlyRSd8RfWYJ3coVS4TROP2w==
+ dependencies:
+ fast-diff "^1.1.2"
+
+prettier@^2.0.5:
+ version "2.0.5"
+ resolved "https://registry.yarnpkg.com/prettier/-/prettier-2.0.5.tgz#d6d56282455243f2f92cc1716692c08aa31522d4"
+ integrity sha512-7PtVymN48hGcO4fGjybyBSIWDsLU4H4XlvOHfq91pz9kkGlonzwTfYkaIEwiRg/dAJF9YlbsduBAgtYLi+8cFg==
+
pretty-bytes@^5.1.0:
version "5.3.0"
resolved "https://registry.yarnpkg.com/pretty-bytes/-/pretty-bytes-5.3.0.tgz#f2849e27db79fb4d6cfe24764fc4134f165989f2"
@@ -9095,6 +9152,16 @@ pretty-format@^24.9.0:
ansi-styles "^3.2.0"
react-is "^16.8.4"
+pretty-format@^25.2.1, pretty-format@^25.5.0:
+ version "25.5.0"
+ resolved "https://registry.yarnpkg.com/pretty-format/-/pretty-format-25.5.0.tgz#7873c1d774f682c34b8d48b6743a2bf2ac55791a"
+ integrity sha512-kbo/kq2LQ/A/is0PQwsEHM7Ca6//bGPPvU6UnsdDRSKTWxT/ru/xb88v4BJf6a69H+uTytOEsTusT9ksd/1iWQ==
+ dependencies:
+ "@jest/types" "^25.5.0"
+ ansi-regex "^5.0.0"
+ ansi-styles "^4.0.0"
+ react-is "^16.12.0"
+
private@^0.1.8:
version "0.1.8"
resolved "https://registry.yarnpkg.com/private/-/private-0.1.8.tgz#2381edb3689f7a53d653190060fcf822d2f368ff"
@@ -9120,13 +9187,6 @@ promise-inflight@^1.0.1:
resolved "https://registry.yarnpkg.com/promise-inflight/-/promise-inflight-1.0.1.tgz#98472870bf228132fcbdd868129bad12c3c029e3"
integrity sha1-mEcocL8igTL8vdhoEputEsPAKeM=
-promise@^7.1.1:
- version "7.3.1"
- resolved "https://registry.yarnpkg.com/promise/-/promise-7.3.1.tgz#064b72602b18f90f29192b8b1bc418ffd1ebd3bf"
- integrity sha512-nolQXZ/4L+bP/UGlkfaIujX9BKxGwmQ9OT4mOt5yvy8iK1h3wqTEJCijzGANTCCl9nWjY41juyAn2K3Q1hLLTg==
- dependencies:
- asap "~2.0.3"
-
promise@^8.0.3:
version "8.1.0"
resolved "https://registry.yarnpkg.com/promise/-/promise-8.1.0.tgz#697c25c3dfe7435dd79fcd58c38a135888eaf05e"
@@ -9402,11 +9462,6 @@ react-jss@^10.1.1:
theming "3.2.0"
tiny-warning "^1.0.2"
-react-lifecycles-compat@^3.0.2:
- version "3.0.4"
- resolved "https://registry.yarnpkg.com/react-lifecycles-compat/-/react-lifecycles-compat-3.0.4.tgz#4f1a273afdfc8f3488a8c516bfda78f872352362"
- integrity sha512-fBASbA6LnOU9dOU2eW7aQ8xmYBSXUIWr+UmF9b1efZBazGNO+rcXT/icdKnYm2pTwcRylVUYwW7H1PHfLekVzA==
-
react-mock-router@^1.0.15:
version "1.0.15"
resolved "https://registry.yarnpkg.com/react-mock-router/-/react-mock-router-1.0.15.tgz#01c823a7ed366e7e834565e879bc72443131b4e1"
@@ -9618,18 +9673,6 @@ realpath-native@^1.1.0:
dependencies:
util.promisify "^1.0.0"
-recompose@^0.30.0:
- version "0.30.0"
- resolved "https://registry.yarnpkg.com/recompose/-/recompose-0.30.0.tgz#82773641b3927e8c7d24a0d87d65aeeba18aabd0"
- integrity sha512-ZTrzzUDa9AqUIhRk4KmVFihH0rapdCSMFXjhHbNrjAWxBuUD/guYlyysMnuHjlZC/KRiOKRtB4jf96yYSkKE8w==
- dependencies:
- "@babel/runtime" "^7.0.0"
- change-emitter "^0.1.2"
- fbjs "^0.8.1"
- hoist-non-react-statics "^2.3.1"
- react-lifecycles-compat "^3.0.2"
- symbol-observable "^1.0.4"
-
recursive-readdir@2.2.2:
version "2.2.2"
resolved "https://registry.yarnpkg.com/recursive-readdir/-/recursive-readdir-2.2.2.tgz#9946fb3274e1628de6e36b2f6714953b4845094f"
@@ -10093,7 +10136,7 @@ selfsigned@^1.10.7:
dependencies:
node-forge "0.9.0"
-"semver@2 || 3 || 4 || 5", semver@^5.3.0, semver@^5.4.1, semver@^5.5.0, semver@^5.5.1, semver@^5.6.0, semver@^5.7.0, semver@^5.7.1:
+"semver@2 || 3 || 4 || 5", semver@^5.4.1, semver@^5.5.0, semver@^5.5.1, semver@^5.6.0, semver@^5.7.0, semver@^5.7.1:
version "5.7.1"
resolved "https://registry.yarnpkg.com/semver/-/semver-5.7.1.tgz#a954f931aeba508d307bbf069eff0c01c96116f7"
integrity sha512-sauaDf/PZdVgrLTNYHRtpXa1iRiKcaebiKQ1BJdpQlWH2lCvexQdX55snPFyK7QzpudqbCI0qXFfOasHdyNDGQ==
@@ -10170,7 +10213,7 @@ set-value@^2.0.0, set-value@^2.0.1:
is-plain-object "^2.0.3"
split-string "^3.0.1"
-setimmediate@^1.0.4, setimmediate@^1.0.5:
+setimmediate@^1.0.4:
version "1.0.5"
resolved "https://registry.yarnpkg.com/setimmediate/-/setimmediate-1.0.5.tgz#290cbb232e306942d7d7ea9b83732ab7856f8285"
integrity sha1-KQy7Iy4waULX1+qbg3Mqt4VvgoU=
@@ -10814,7 +10857,7 @@ svgo@^1.0.0, svgo@^1.2.2:
unquote "~1.1.1"
util.promisify "~1.0.0"
-symbol-observable@^1.0.4, symbol-observable@^1.2.0:
+symbol-observable@^1.2.0:
version "1.2.0"
resolved "https://registry.yarnpkg.com/symbol-observable/-/symbol-observable-1.2.0.tgz#c22688aed4eab3cdc2dfeacbb561660560a00804"
integrity sha512-e900nM8RRtGhlV36KGEU9k65K3mPb1WV70OdjfxlG2EAuM1noi/E/BaW/uMhL7bPEssK8QV57vN3esixjUvcXQ==
@@ -11053,39 +11096,6 @@ tslib@^1.10.0, tslib@^1.8.1, tslib@^1.9.0:
resolved "https://registry.yarnpkg.com/tslib/-/tslib-1.11.1.tgz#eb15d128827fbee2841549e171f45ed338ac7e35"
integrity sha512-aZW88SY8kQbU7gpV19lN24LtXh/yD4ZZg6qieAJDDg+YBsJcSmLGK9QpnUjAKVG/xefmvJGd1WUmfpT/g6AJGA==
-tslint-react@^5.0.0:
- version "5.0.0"
- resolved "https://registry.yarnpkg.com/tslint-react/-/tslint-react-5.0.0.tgz#d0ae644e8163bdd3e134012e9353094904e8dd44"
- integrity sha512-/IbcSmoBPlFic8kQaRfQ4knTY4mivwo5LVzvozvX6Dyu2ynEnrh1dIcR2ujjyp/IodXqY/H5GbxFxSMo/Kf2Hg==
- dependencies:
- tsutils "^3.17.1"
-
-tslint@^6.1.2:
- version "6.1.2"
- resolved "https://registry.yarnpkg.com/tslint/-/tslint-6.1.2.tgz#2433c248512cc5a7b2ab88ad44a6b1b34c6911cf"
- integrity sha512-UyNrLdK3E0fQG/xWNqAFAC5ugtFyPO4JJR1KyyfQAyzR8W0fTRrC91A8Wej4BntFzcvETdCSDa/4PnNYJQLYiA==
- dependencies:
- "@babel/code-frame" "^7.0.0"
- builtin-modules "^1.1.1"
- chalk "^2.3.0"
- commander "^2.12.1"
- diff "^4.0.1"
- glob "^7.1.1"
- js-yaml "^3.13.1"
- minimatch "^3.0.4"
- mkdirp "^0.5.3"
- resolve "^1.3.2"
- semver "^5.3.0"
- tslib "^1.10.0"
- tsutils "^2.29.0"
-
-tsutils@^2.29.0:
- version "2.29.0"
- resolved "https://registry.yarnpkg.com/tsutils/-/tsutils-2.29.0.tgz#32b488501467acbedd4b85498673a0812aca0b99"
- integrity sha512-g5JVHCIJwzfISaXpXE1qvNalca5Jwob6FjI4AoPlqMusJ6ftFE7IkkFoMhVLRgK+4Kx3gkzb8UZK5t5yTTvEmA==
- dependencies:
- tslib "^1.8.1"
-
tsutils@^3.17.1:
version "3.17.1"
resolved "https://registry.yarnpkg.com/tsutils/-/tsutils-3.17.1.tgz#ed719917f11ca0dee586272b2ac49e015a2dd759"
@@ -11155,11 +11165,6 @@ typescript@^3.8.3:
resolved "https://registry.yarnpkg.com/typescript/-/typescript-3.8.3.tgz#409eb8544ea0335711205869ec458ab109ee1061"
integrity sha512-MYlEfn5VrLNsgudQTVJeNaQFUAI7DkhnOjdpAp4T+ku1TfQClewlbSuTVHiA+8skNBgaf02TL/kLOvig4y3G8w==
-ua-parser-js@^0.7.18:
- version "0.7.19"
- resolved "https://registry.yarnpkg.com/ua-parser-js/-/ua-parser-js-0.7.19.tgz#94151be4c0a7fb1d001af7022fdaca4642659e4b"
- integrity sha512-T3PVJ6uz8i0HzPxOF9SWzWAlfN/DavlpQqepn22xgve/5QecC+XMCAtmUNnY7C9StehaV6exjUCI801lOI7QlQ==
-
unicode-canonical-property-names-ecmascript@^1.0.4:
version "1.0.4"
resolved "https://registry.yarnpkg.com/unicode-canonical-property-names-ecmascript/-/unicode-canonical-property-names-ecmascript-1.0.4.tgz#2619800c4c825800efdd8343af7dd9933cbe2818"
@@ -11568,7 +11573,7 @@ whatwg-encoding@^1.0.1, whatwg-encoding@^1.0.3, whatwg-encoding@^1.0.5:
dependencies:
iconv-lite "0.4.24"
-whatwg-fetch@>=0.10.0, whatwg-fetch@^3.0.0:
+whatwg-fetch@^3.0.0:
version "3.0.0"
resolved "https://registry.yarnpkg.com/whatwg-fetch/-/whatwg-fetch-3.0.0.tgz#fc804e458cc460009b1a2b966bc8817d2578aefb"
integrity sha512-9GSJUgz1D4MfyKU7KRqwOjXCXTqWdFNvEr7eUBYchQiVc744mqK/MzXPNR2WsPkmkOa4ywfg8C2n8h+13Bey1Q==