This is an automated email from the ASF dual-hosted git repository. erikrit pushed a commit to branch master in repository https://gitbox.apache.org/repos/asf/incubator-superset.git
The following commit(s) were added to refs/heads/master by this push: new 74423e5 deprecate tslint and configure eslint for typescript (#9172) 74423e5 is described below commit 74423e5d19e78c3195547de94df78693fdce3061 Author: ʈᵃᵢ <tdupree...@gmail.com> AuthorDate: Thu Feb 20 09:54:33 2020 -0800 deprecate tslint and configure eslint for typescript (#9172) --- superset-frontend/.eslintignore | 1 + superset-frontend/.eslintrc | 89 ------- superset-frontend/.eslintrc.js | 167 ++++++++++++ superset-frontend/package-lock.json | 285 +++++++++++++-------- superset-frontend/package.json | 13 +- .../spec/javascripts/utils/safeStringify_spec.ts | 8 +- superset-frontend/src/SqlLab/components/Link.tsx | 2 +- superset-frontend/src/SqlLab/utils/sqlKeywords.ts | 2 +- .../src/components/ConfirmStatusChange.tsx | 17 +- .../src/components/ListView/ListView.tsx | 146 ++++++----- .../src/components/ListView/TableCollection.tsx | 28 +- superset-frontend/src/components/ListView/utils.ts | 2 +- .../src/dashboard/stylesheets/popover-menu.less | 3 +- superset-frontend/src/utils/safeStringify.ts | 4 +- .../src/views/chartList/ChartList.tsx | 181 +++++++------ .../src/views/dashboardList/DashboardList.tsx | 203 +++++++++------ superset-frontend/tsconfig.json | 26 +- superset-frontend/tslint.json | 14 - 18 files changed, 707 insertions(+), 484 deletions(-) diff --git a/superset-frontend/.eslintignore b/superset-frontend/.eslintignore index a92bf58..25b08e0 100644 --- a/superset-frontend/.eslintignore +++ b/superset-frontend/.eslintignore @@ -26,3 +26,4 @@ vendor/* docs/* src/dashboard/deprecated/* **/node_modules +*.d.ts diff --git a/superset-frontend/.eslintrc b/superset-frontend/.eslintrc deleted file mode 100644 index 34cbfe9..0000000 --- a/superset-frontend/.eslintrc +++ /dev/null @@ -1,89 +0,0 @@ -/** - * Licensed to the Apache Software Foundation (ASF) under one - * or more contributor license agreements. See the NOTICE file - * distributed with this work for additional information - * regarding copyright ownership. The ASF licenses this file - * to you under the Apache License, Version 2.0 (the - * "License"); you may not use this file except in compliance - * with the License. You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, - * software distributed under the License is distributed on an - * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY - * KIND, either express or implied. See the License for the - * specific language governing permissions and limitations - * under the License. - */ -{ - "extends": ["airbnb", "prettier"], - "parser": "babel-eslint", - "parserOptions": { - "ecmaFeatures": { - "experimentalObjectRestSpread": true - } - }, - "env": { - "browser": true - }, - "plugins": ["prettier", "react"], - "rules": { - "camelcase": [ - "error", - { - "allow": ["^UNSAFE_"], - "properties": "never" - } - ], - "class-methods-use-this": 0, - "func-names": 0, - "guard-for-in": 0, - "import/extensions": [ - "error", - { - ".js": "always", - ".jsx": "always", - ".ts": "always", - ".tsx": "always", - ".json": "always" - } - ], - "import/no-named-as-default": 0, - "import/prefer-default-export": 0, - "indent": 0, - "jsx-a11y/anchor-has-content": 0, - "jsx-a11y/href-no-hash": 0, - "jsx-a11y/no-static-element-interactions": 0, - "new-cap": 0, - "no-bitwise": 0, - "no-confusing-arrow": 0, - "no-continue": 0, - "no-mixed-operators": 0, - "no-multi-assign": 0, - "no-multi-spaces": 0, - "no-plusplus": 0, - "no-prototype-builtins": 0, - "no-restricted-properties": 0, - "no-restricted-syntax": 0, - "padded-blocks": 0, - "prefer-arrow-callback": 0, - "prefer-template": 0, - "react/forbid-prop-types": 0, - "react/jsx-no-bind": 0, - "react/no-array-index-key": 0, - "react/no-string-refs": 0, - "react/no-unescaped-entities": 0, - "react/no-unused-prop-types": 0, - "react/require-default-props": 0, - "react/jsx-fragments": 1, - "react/prop-types": 0, - "prettier/prettier": "error" - }, - "settings": { - "import/resolver": "webpack", - "react": { - "version": "detect" - } - } -} diff --git a/superset-frontend/.eslintrc.js b/superset-frontend/.eslintrc.js new file mode 100644 index 0000000..f8ec002 --- /dev/null +++ b/superset-frontend/.eslintrc.js @@ -0,0 +1,167 @@ +/** + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ +module.exports = { + extends: ['airbnb', 'prettier'], + parser: 'babel-eslint', + parserOptions: { + ecmaFeatures: { + experimentalObjectRestSpread: true, + }, + }, + env: { + browser: true, + }, + plugins: ['prettier', 'react'], + overrides: [ + { + files: ['*.ts', '*.tsx'], + parser: '@typescript-eslint/parser', + extends: [ + 'airbnb', + 'plugin:@typescript-eslint/recommended', + 'prettier', + 'prettier/@typescript-eslint', + ], + plugins: ['@typescript-eslint', 'prettier', 'react'], + rules: { + '@typescript-eslint/ban-ts-ignore': 0, + '@typescript-eslint/camelcase': [ + 'error', + { + allow: ['^UNSAFE_'], + properties: 'never', + }, + ], + '@typescript-eslint/no-explicit-any': 0, + '@typescript-eslint/explicit-function-return-type': 0, + camelcase: 0, + 'class-methods-use-this': 0, + 'func-names': 0, + 'guard-for-in': 0, + 'import/extensions': [ + 'error', + { + '.ts': 'always', + '.tsx': 'always', + '.json': 'always', + }, + ], + 'import/no-named-as-default': 0, + 'import/no-named-as-default-member': 0, + 'import/prefer-default-export': 0, + indent: 0, + 'jsx-a11y/anchor-has-content': 0, + 'jsx-a11y/href-no-hash': 0, + 'jsx-a11y/no-static-element-interactions': 0, + 'new-cap': 0, + 'no-bitwise': 0, + 'no-confusing-arrow': 0, + 'no-continue': 0, + 'no-mixed-operators': 0, + 'no-multi-assign': 0, + 'no-multi-spaces': 0, + 'no-plusplus': 0, + 'no-prototype-builtins': 0, + 'no-restricted-properties': 0, + 'no-restricted-syntax': 0, + 'no-unused-vars': 0, + 'padded-blocks': 0, + 'prefer-arrow-callback': 0, + 'prefer-template': 0, + 'react/forbid-prop-types': 0, + 'react/jsx-filename-extension': [1, { extensions: ['.jsx', '.tsx'] }], + 'react/jsx-no-bind': 0, + 'react/no-array-index-key': 0, + 'react/no-string-refs': 0, + 'react/no-unescaped-entities': 0, + 'react/no-unused-prop-types': 0, + 'react/require-default-props': 0, + 'react/jsx-fragments': 1, + 'react/prop-types': 0, + 'prettier/prettier': 'error', + }, + settings: { + 'import/resolver': 'webpack', + react: { + version: 'detect', + }, + }, + }, + ], + rules: { + camelcase: [ + 'error', + { + allow: ['^UNSAFE_'], + properties: 'never', + }, + ], + 'class-methods-use-this': 0, + 'func-names': 0, + 'guard-for-in': 0, + 'import/extensions': [ + 'error', + { + '.js': 'always', + '.jsx': 'always', + '.ts': 'always', + '.tsx': 'always', + '.json': 'always', + }, + ], + 'import/no-named-as-default': 0, + 'import/prefer-default-export': 0, + indent: 0, + 'jsx-a11y/anchor-has-content': 0, + 'jsx-a11y/href-no-hash': 0, + 'jsx-a11y/no-static-element-interactions': 0, + 'new-cap': 0, + 'no-bitwise': 0, + 'no-confusing-arrow': 0, + 'no-continue': 0, + 'no-mixed-operators': 0, + 'no-multi-assign': 0, + 'no-multi-spaces': 0, + 'no-plusplus': 0, + 'no-prototype-builtins': 0, + 'no-restricted-properties': 0, + 'no-restricted-syntax': 0, + 'no-unused-vars': 0, + 'padded-blocks': 0, + 'prefer-arrow-callback': 0, + 'prefer-template': 0, + 'react/forbid-prop-types': 0, + 'react/jsx-filename-extension': [1, { extensions: ['.jsx', '.tsx'] }], + 'react/jsx-no-bind': 0, + 'react/no-array-index-key': 0, + 'react/no-string-refs': 0, + 'react/no-unescaped-entities': 0, + 'react/no-unused-prop-types': 0, + 'react/require-default-props': 0, + 'react/jsx-fragments': 1, + 'react/prop-types': 0, + 'prettier/prettier': 'error', + }, + settings: { + 'import/resolver': 'webpack', + react: { + version: 'detect', + }, + }, +}; diff --git a/superset-frontend/package-lock.json b/superset-frontend/package-lock.json index 6dd4db4..6d40634 100644 --- a/superset-frontend/package-lock.json +++ b/superset-frontend/package-lock.json @@ -4724,6 +4724,11 @@ } } }, + "@types/classnames": { + "version": "2.2.9", + "resolved": "https://registry.npmjs.org/@types/classnames/-/classnames-2.2.9.tgz", + "integrity": "sha512-MNl+rT5UmZeilaPxAVs6YaPC2m6aA8rofviZbhbxpPpl61uKodfdQVsBtgJGTqGizEf02oW3tsVe7FYB8kK14A==" + }, "@types/clone": { "version": "0.1.30", "resolved": "https://registry.npmjs.org/@types/clone/-/clone-0.1.30.tgz", @@ -4765,6 +4770,12 @@ "@types/trusted-types": "*" } }, + "@types/eslint-visitor-keys": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/@types/eslint-visitor-keys/-/eslint-visitor-keys-1.0.0.tgz", + "integrity": "sha512-OCutwjDZ4aFS6PB1UZ988C4YgwlBHJd6wCeQqaLdmadZ/7e+w79+hbMUFC1QXDNCmdyoRfAFdm0RypzwR+Qpag==", + "dev": true + }, "@types/events": { "version": "3.0.0", "resolved": "https://registry.npmjs.org/@types/events/-/events-3.0.0.tgz", @@ -4842,6 +4853,12 @@ "integrity": "sha512-DC8xTuW/6TYgvEg3HEXS7cu9OijFqprVDXXiOcdOKZCU/5PJNLZU37VVvmZHdtMiGOa8wAA/We+JzbdxFzQTRQ==", "dev": true }, + "@types/json-schema": { + "version": "7.0.4", + "resolved": "https://registry.npmjs.org/@types/json-schema/-/json-schema-7.0.4.tgz", + "integrity": "sha512-8+KAKzEvSUdeo+kmqnKrqgeE+LcA0tjYWFY7RPProVYwnqDjukzO+3b6dLD56rYX5TdWejnEOLJYOIeh4CXKuA==", + "dev": true + }, "@types/lodash": { "version": "4.14.147", "resolved": "https://registry.npmjs.org/@types/lodash/-/lodash-4.14.147.tgz", @@ -5013,6 +5030,149 @@ "integrity": "sha512-SOhuU4wNBxhhTHxYaiG5NY4HBhDIDnJF60GU+2LqHAdKKer86//e4yg69aENCtQ04n0ovz+tq2YPME5t5yp4pw==", "dev": true }, + "@typescript-eslint/eslint-plugin": { + "version": "2.20.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/eslint-plugin/-/eslint-plugin-2.20.0.tgz", + "integrity": "sha512-cimIdVDV3MakiGJqMXw51Xci6oEDEoPkvh8ggJe2IIzcc0fYqAxOXN6Vbeanahz6dLZq64W+40iUEc9g32FLDQ==", + "dev": true, + "requires": { + "@typescript-eslint/experimental-utils": "2.20.0", + "eslint-utils": "^1.4.3", + "functional-red-black-tree": "^1.0.1", + "regexpp": "^3.0.0", + "tsutils": "^3.17.1" + }, + "dependencies": { + "eslint-utils": { + "version": "1.4.3", + "resolved": "https://registry.npmjs.org/eslint-utils/-/eslint-utils-1.4.3.tgz", + "integrity": "sha512-fbBN5W2xdY45KulGXmLHZ3c3FHfVYmKg0IrAKGOkT/464PQsx2UeIzfz1RmEci+KLm1bBaAzZAh8+/E+XAeZ8Q==", + "dev": true, + "requires": { + "eslint-visitor-keys": "^1.1.0" + } + }, + "eslint-visitor-keys": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/eslint-visitor-keys/-/eslint-visitor-keys-1.1.0.tgz", + "integrity": "sha512-8y9YjtM1JBJU/A9Kc+SbaOV4y29sSWckBwMHa+FGtVj5gN/sbnKDf6xJUl+8g7FAij9LVaP8C24DUiH/f/2Z9A==", + "dev": true + }, + "regexpp": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/regexpp/-/regexpp-3.0.0.tgz", + "integrity": "sha512-Z+hNr7RAVWxznLPuA7DIh8UNX1j9CDrUQxskw9IrBE1Dxue2lyXT+shqEIeLUjrokxIP8CMy1WkjgG3rTsd5/g==", + "dev": true + }, + "tsutils": { + "version": "3.17.1", + "resolved": "https://registry.npmjs.org/tsutils/-/tsutils-3.17.1.tgz", + "integrity": "sha512-kzeQ5B8H3w60nFY2g8cJIuH7JDpsALXySGtwGJ0p2LSjLgay3NdIpqq5SoOBe46bKDW2iq25irHCr8wjomUS2g==", + "dev": true, + "requires": { + "tslib": "^1.8.1" + } + } + } + }, + "@typescript-eslint/experimental-utils": { + "version": "2.20.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/experimental-utils/-/experimental-utils-2.20.0.tgz", + "integrity": "sha512-fEBy9xYrwG9hfBLFEwGW2lKwDRTmYzH3DwTmYbT+SMycmxAoPl0eGretnBFj/s+NfYBG63w/5c3lsvqqz5mYag==", + "dev": true, + "requires": { + "@types/json-schema": "^7.0.3", + "@typescript-eslint/typescript-estree": "2.20.0", + "eslint-scope": "^5.0.0" + } + }, + "@typescript-eslint/parser": { + "version": "2.20.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/parser/-/parser-2.20.0.tgz", + "integrity": "sha512-o8qsKaosLh2qhMZiHNtaHKTHyCHc3Triq6aMnwnWj7budm3xAY9owSZzV1uon5T9cWmJRJGzTFa90aex4m77Lw==", + "dev": true, + "requires": { + "@types/eslint-visitor-keys": "^1.0.0", + "@typescript-eslint/experimental-utils": "2.20.0", + "@typescript-eslint/typescript-estree": "2.20.0", + "eslint-visitor-keys": "^1.1.0" + }, + "dependencies": { + "eslint-visitor-keys": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/eslint-visitor-keys/-/eslint-visitor-keys-1.1.0.tgz", + "integrity": "sha512-8y9YjtM1JBJU/A9Kc+SbaOV4y29sSWckBwMHa+FGtVj5gN/sbnKDf6xJUl+8g7FAij9LVaP8C24DUiH/f/2Z9A==", + "dev": true + } + } + }, + "@typescript-eslint/typescript-estree": { + "version": "2.20.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/typescript-estree/-/typescript-estree-2.20.0.tgz", + "integrity": "sha512-WlFk8QtI8pPaE7JGQGxU7nGcnk1ccKAJkhbVookv94ZcAef3m6oCE/jEDL6dGte3JcD7reKrA0o55XhBRiVT3A==", + "dev": true, + "requires": { + "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" + }, + "dependencies": { + "debug": { + "version": "4.1.1", + "resolved": "https://registry.npmjs.org/debug/-/debug-4.1.1.tgz", + "integrity": "sha512-pYAIzeRo8J6KPEaJ0VWOh5Pzkbw/RetuzehGM7QRRX5he4fPHx2rdKMB256ehJCkX+XRQm16eZLqLNS8RSZXZw==", + "dev": true, + "requires": { + "ms": "^2.1.1" + } + }, + "eslint-visitor-keys": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/eslint-visitor-keys/-/eslint-visitor-keys-1.1.0.tgz", + "integrity": "sha512-8y9YjtM1JBJU/A9Kc+SbaOV4y29sSWckBwMHa+FGtVj5gN/sbnKDf6xJUl+8g7FAij9LVaP8C24DUiH/f/2Z9A==", + "dev": true + }, + "glob": { + "version": "7.1.6", + "resolved": "https://registry.npmjs.org/glob/-/glob-7.1.6.tgz", + "integrity": "sha512-LwaxwyZ72Lk7vZINtNNrywX0ZuLyStrdDtabefZKAY5ZGJhVtgdznluResxNmPitE0SAO+O26sWTHeKSI2wMBA==", + "dev": true, + "requires": { + "fs.realpath": "^1.0.0", + "inflight": "^1.0.4", + "inherits": "2", + "minimatch": "^3.0.4", + "once": "^1.3.0", + "path-is-absolute": "^1.0.0" + } + }, + "ms": { + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.2.tgz", + "integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==", + "dev": true + }, + "semver": { + "version": "6.3.0", + "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.0.tgz", + "integrity": "sha512-b39TBaTSfV6yBrapU89p5fKekE2m/NwnDocOVruQFS1/veMgdzuPcnOM34M6CwxW8jH/lxEa5rBoDeUwu5HHTw==", + "dev": true + }, + "tsutils": { + "version": "3.17.1", + "resolved": "https://registry.npmjs.org/tsutils/-/tsutils-3.17.1.tgz", + "integrity": "sha512-kzeQ5B8H3w60nFY2g8cJIuH7JDpsALXySGtwGJ0p2LSjLgay3NdIpqq5SoOBe46bKDW2iq25irHCr8wjomUS2g==", + "dev": true, + "requires": { + "tslib": "^1.8.1" + } + } + } + }, "@vx/axis": { "version": "0.0.140", "resolved": "https://registry.npmjs.org/@vx/axis/-/axis-0.0.140.tgz", @@ -10615,12 +10775,12 @@ } }, "eslint-config-prettier": { - "version": "2.10.0", - "resolved": "https://registry.npmjs.org/eslint-config-prettier/-/eslint-config-prettier-2.10.0.tgz", - "integrity": "sha512-Mhl90VLucfBuhmcWBgbUNtgBiK955iCDK1+aHAz7QfDQF6wuzWZ6JjihZ3ejJoGlJWIuko7xLqNm8BA5uenKhA==", + "version": "6.10.0", + "resolved": "https://registry.npmjs.org/eslint-config-prettier/-/eslint-config-prettier-6.10.0.tgz", + "integrity": "sha512-AtndijGte1rPILInUdHjvKEGbIV06NuvPrqlIEaEaWtbtvJh464mDeyGMdZEQMsGvC0ZVkiex1fSNcC4HAbRGg==", "dev": true, "requires": { - "get-stdin": "^5.0.1" + "get-stdin": "^6.0.0" } }, "eslint-import-resolver-node": { @@ -10814,13 +10974,12 @@ "dev": true }, "eslint-plugin-prettier": { - "version": "2.7.0", - "resolved": "https://registry.npmjs.org/eslint-plugin-prettier/-/eslint-plugin-prettier-2.7.0.tgz", - "integrity": "sha512-CStQYJgALoQBw3FsBzH0VOVDRnJ/ZimUlpLm226U8qgqYJfPOY/CPK6wyRInMxh73HSKg5wyRwdS4BVYYHwokA==", + "version": "3.1.2", + "resolved": "https://registry.npmjs.org/eslint-plugin-prettier/-/eslint-plugin-prettier-3.1.2.tgz", + "integrity": "sha512-GlolCC9y3XZfv3RQfwGew7NnuFDKsfI4lbvRK+PIIo23SFH+LemGs4cKwzAaRa+Mdb+lQO/STaIayno8T5sJJA==", "dev": true, "requires": { - "fast-diff": "^1.1.1", - "jest-docblock": "^21.0.0" + "prettier-linter-helpers": "^1.0.0" } }, "eslint-plugin-react": { @@ -12686,9 +12845,9 @@ "dev": true }, "get-stdin": { - "version": "5.0.1", - "resolved": "https://registry.npmjs.org/get-stdin/-/get-stdin-5.0.1.tgz", - "integrity": "sha1-Ei4WFZHiH/TFJTAwVpPyDmOTo5g=", + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/get-stdin/-/get-stdin-6.0.0.tgz", + "integrity": "sha512-jp4tHawyV7+fkkSKyvjuLZswblUtz+SQKzSWnBbii16BuZksJlU1wuBYXY75r+duh/llF1ur6oNwi+2ZzjKZ7g==", "dev": true }, "get-stream": { @@ -15007,12 +15166,6 @@ } } }, - "jest-docblock": { - "version": "21.2.0", - "resolved": "https://registry.npmjs.org/jest-docblock/-/jest-docblock-21.2.0.tgz", - "integrity": "sha512-5IZ7sY9dBAYSV+YjQ0Ovb540Ku7AO9Z5o2Cg789xj167iQuZ2cG+z0f3Uct6WeYLbU6aQiM2pCs7sZ+4dotydw==", - "dev": true - }, "jest-each": { "version": "24.8.0", "resolved": "https://registry.npmjs.org/jest-each/-/jest-each-24.8.0.tgz", @@ -20562,6 +20715,15 @@ "integrity": "sha512-s7PoyDv/II1ObgQunCbB9PdLmUcBZcnWOcxDh7O0N/UwDEsHyqkW+Qh28jW+mVuCdx7gLB0BotYI1Y6uI9iyew==", "dev": true }, + "prettier-linter-helpers": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/prettier-linter-helpers/-/prettier-linter-helpers-1.0.0.tgz", + "integrity": "sha512-GbK2cP9nraSSUF9N2XwUwqfzlAFlMNYYl+ShE/V+H8a9uNl/oUqB1w2EL54Jh0OlyRSd8RfWYJ3coVS4TROP2w==", + "dev": true, + "requires": { + "fast-diff": "^1.1.2" + } + }, "pretty-format": { "version": "24.8.0", "resolved": "https://registry.npmjs.org/pretty-format/-/pretty-format-24.8.0.tgz", @@ -24496,93 +24658,6 @@ "integrity": "sha512-qOebF53frne81cf0S9B41ByenJ3/IuH8yJKngAX35CmiZySA0khhkovshKK+jGCaMnVomla7gVlIcc3EvKPbTQ==", "dev": true }, - "tslint": { - "version": "5.20.1", - "resolved": "https://registry.npmjs.org/tslint/-/tslint-5.20.1.tgz", - "integrity": "sha512-EcMxhzCFt8k+/UP5r8waCf/lzmeSyVlqxqMEDQE7rWYiQky8KpIBz1JAoYXfROHrPZ1XXd43q8yQnULOLiBRQg==", - "dev": true, - "requires": { - "@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.1", - "resolve": "^1.3.2", - "semver": "^5.3.0", - "tslib": "^1.8.0", - "tsutils": "^2.29.0" - }, - "dependencies": { - "ansi-styles": { - "version": "3.2.1", - "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-3.2.1.tgz", - "integrity": "sha512-VT0ZI6kZRdTh8YyJw3SMbYm/u+NqfsAxEpWO0Pf9sq8/e94WxxOpPKx9FR1FlyCtOVDNOQ+8ntlqFxiRc+r5qA==", - "dev": true, - "requires": { - "color-convert": "^1.9.0" - } - }, - "chalk": { - "version": "2.4.2", - "resolved": "https://registry.npmjs.org/chalk/-/chalk-2.4.2.tgz", - "integrity": "sha512-Mti+f9lpJNcwF4tWV8/OrTTtF1gZi+f8FqlyAdouralcFWFQWF2+NgCHShjkCb+IFBLq9buZwE1xckQU4peSuQ==", - "dev": true, - "requires": { - "ansi-styles": "^3.2.1", - "escape-string-regexp": "^1.0.5", - "supports-color": "^5.3.0" - } - }, - "diff": { - "version": "4.0.1", - "resolved": "https://registry.npmjs.org/diff/-/diff-4.0.1.tgz", - "integrity": "sha512-s2+XdvhPCOF01LRQBC8hf4vhbVmI2CGS5aZnxLJlT5FtdhPCDFq80q++zK2KlrVorVDdL5BOGZ/VfLrVtYNF+Q==", - "dev": true - }, - "supports-color": { - "version": "5.5.0", - "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-5.5.0.tgz", - "integrity": "sha512-QjVjwdXIt408MIiAqCX4oUKsgU2EqAGzs2Ppkm4aQYbjm+ZEWEcW4SfFNTr4uMNZma0ey4f5lgLrkB0aX0QMow==", - "dev": true, - "requires": { - "has-flag": "^3.0.0" - } - } - } - }, - "tslint-react": { - "version": "4.1.0", - "resolved": "https://registry.npmjs.org/tslint-react/-/tslint-react-4.1.0.tgz", - "integrity": "sha512-Y7CbFn09X7Mpg6rc7t/WPbmjx9xPI8p1RsQyiGCLWgDR6sh3+IBSlT+bEkc0PSZcWwClOkqq2wPsID8Vep6szQ==", - "dev": true, - "requires": { - "tsutils": "^3.9.1" - }, - "dependencies": { - "tsutils": { - "version": "3.17.1", - "resolved": "https://registry.npmjs.org/tsutils/-/tsutils-3.17.1.tgz", - "integrity": "sha512-kzeQ5B8H3w60nFY2g8cJIuH7JDpsALXySGtwGJ0p2LSjLgay3NdIpqq5SoOBe46bKDW2iq25irHCr8wjomUS2g==", - "dev": true, - "requires": { - "tslib": "^1.8.1" - } - } - } - }, - "tsutils": { - "version": "2.29.0", - "resolved": "https://registry.npmjs.org/tsutils/-/tsutils-2.29.0.tgz", - "integrity": "sha512-g5JVHCIJwzfISaXpXE1qvNalca5Jwob6FjI4AoPlqMusJ6ftFE7IkkFoMhVLRgK+4Kx3gkzb8UZK5t5yTTvEmA==", - "dev": true, - "requires": { - "tslib": "^1.8.1" - } - }, "tty-browserify": { "version": "0.0.0", "resolved": "https://registry.npmjs.org/tty-browserify/-/tty-browserify-0.0.0.tgz", diff --git a/superset-frontend/package.json b/superset-frontend/package.json index b294433..2ea7c6b 100644 --- a/superset-frontend/package.json +++ b/superset-frontend/package.json @@ -15,8 +15,8 @@ "dev-server": "node --max_old_space_size=4096 ./node_modules/webpack-dev-server/bin/webpack-dev-server.js --mode=development --progress", "prod": "node --max_old_space_size=4096 ./node_modules/webpack/bin/webpack.js --mode=production --colors --progress", "build": "cross-env NODE_OPTIONS=--max_old_space_size=8192 NODE_ENV=production webpack --mode=production --colors --progress", - "lint": "eslint --ignore-path=.eslintignore --ext .js,.jsx . && tslint -c tslint.json ./{src,spec}/**/*.ts{,x}", - "lint-fix": "eslint --fix --ignore-path=.eslintignore --ext .js,.jsx . && tslint -c tslint.json --fix ./{src,spec}/**/*.ts{,x} && npm run clean-css", + "lint": "eslint --ignore-path=.eslintignore --ext .js,.jsx,.ts,.tsx .", + "lint-fix": "eslint --fix --ignore-path=.eslintignore --ext .js,.jsx,.ts,tsx . && npm run clean-css", "clean-css": "prettier --write '{src,stylesheets}/**/*.{css,less,sass,scss}'" }, "repository": { @@ -82,6 +82,7 @@ "@superset-ui/query": "^0.12.2", "@superset-ui/time-format": "^0.12.4", "@superset-ui/translation": "^0.12.0", + "@types/classnames": "^2.2.9", "@types/react-json-tree": "^0.6.11", "@vx/responsive": "0.0.172", "abortcontroller-polyfill": "^1.1.9", @@ -163,6 +164,8 @@ "@types/react": "^16.4.18", "@types/react-dom": "^16.0.9", "@types/react-table": "^7.0.2", + "@typescript-eslint/eslint-plugin": "^2.20.0", + "@typescript-eslint/parser": "^2.20.0", "babel-core": "^7.0.0-bridge.0", "babel-eslint": "^10.0.3", "babel-jest": "^24.8.0", @@ -180,14 +183,14 @@ "enzyme-adapter-react-16": "^1.14.0", "eslint": "^6.2.2", "eslint-config-airbnb": "^15.0.1", - "eslint-config-prettier": "^2.9.0", + "eslint-config-prettier": "^6.10.0", "eslint-import-resolver-webpack": "^0.10.1", "eslint-plugin-cypress": "^2.0.1", "eslint-plugin-import": "^2.2.0", "eslint-plugin-jest": "^21.24.1", "eslint-plugin-jsx-a11y": "^5.1.1", "eslint-plugin-no-only-tests": "^2.0.1", - "eslint-plugin-prettier": "^2.6.0", + "eslint-plugin-prettier": "^3.1.2", "eslint-plugin-react": "^7.16.0", "exports-loader": "^0.7.0", "fetch-mock": "^7.0.0-alpha.6", @@ -215,8 +218,6 @@ "ts-jest": "^24.0.2", "ts-loader": "^5.4.5", "tslib": "^1.10.0", - "tslint": "^5.20.1", - "tslint-react": "^4.1.0", "typescript": "^3.5.3", "url-loader": "^1.0.1", "webpack": "^4.19.0", diff --git a/superset-frontend/spec/javascripts/utils/safeStringify_spec.ts b/superset-frontend/spec/javascripts/utils/safeStringify_spec.ts index 6a019a2..acf7748 100644 --- a/superset-frontend/spec/javascripts/utils/safeStringify_spec.ts +++ b/superset-frontend/spec/javascripts/utils/safeStringify_spec.ts @@ -40,7 +40,9 @@ describe('Stringify utility testing', () => { }; expect(safeStringify(noncircular)).toEqual(JSON.stringify(noncircular)); // Checking that it works with quick-deepish-copies as well. - expect(JSON.parse(safeStringify(noncircular))).toEqual(JSON.parse(JSON.stringify(noncircular))); + expect(JSON.parse(safeStringify(noncircular))).toEqual( + JSON.parse(JSON.stringify(noncircular)), + ); }); it('handles simple circular json as expected', () => { @@ -93,7 +95,9 @@ describe('Stringify utility testing', () => { e: {}, }, }; - expect(safeStringify(emptyObjectValues)).toEqual(JSON.stringify(emptyObjectValues)); + expect(safeStringify(emptyObjectValues)).toEqual( + JSON.stringify(emptyObjectValues), + ); }); it('does not remove nested same keys', () => { diff --git a/superset-frontend/src/SqlLab/components/Link.tsx b/superset-frontend/src/SqlLab/components/Link.tsx index be1d836..22bec92 100644 --- a/superset-frontend/src/SqlLab/components/Link.tsx +++ b/superset-frontend/src/SqlLab/components/Link.tsx @@ -52,7 +52,7 @@ const Link = ({ if (tooltip) { return ( <OverlayTrigger - overlay={<Tooltip id='tooltip'>{tooltip}</Tooltip>} + overlay={<Tooltip id="tooltip">{tooltip}</Tooltip>} placement={placement} delayShow={300} delayHide={150} diff --git a/superset-frontend/src/SqlLab/utils/sqlKeywords.ts b/superset-frontend/src/SqlLab/utils/sqlKeywords.ts index cec4222..15afdb3 100644 --- a/superset-frontend/src/SqlLab/utils/sqlKeywords.ts +++ b/superset-frontend/src/SqlLab/utils/sqlKeywords.ts @@ -95,7 +95,7 @@ const SQL_DATA_TYPES = [ const allKeywords = SQL_KEYWORDS.concat(SQL_DATA_TYPES); -const sqlKeywords = allKeywords.map((keyword) => ({ +const sqlKeywords = allKeywords.map(keyword => ({ meta: 'sql', name: keyword, score: SQL_KEYWORD_AUTOCOMPLETE_SCORE, diff --git a/superset-frontend/src/components/ConfirmStatusChange.tsx b/superset-frontend/src/components/ConfirmStatusChange.tsx index 6cce051..30d20a1 100644 --- a/superset-frontend/src/components/ConfirmStatusChange.tsx +++ b/superset-frontend/src/components/ConfirmStatusChange.tsx @@ -34,7 +34,6 @@ interface State { open: boolean; } export default class ConfirmStatusChange extends React.Component<Props, State> { - public state = { callbackArgs: [], open: false, @@ -42,33 +41,33 @@ export default class ConfirmStatusChange extends React.Component<Props, State> { public showConfirm = (...callbackArgs: any[]) => { // check if any args are DOM events, if so, call persist - callbackArgs.forEach((arg) => arg && typeof arg.persist === 'function' && arg.persist()); + callbackArgs.forEach( + arg => arg && typeof arg.persist === 'function' && arg.persist(), + ); this.setState({ callbackArgs, open: true, }); - } + }; public hide = () => this.setState({ open: false, callbackArgs: [] }); public confirm = () => { this.props.onConfirm(...this.state.callbackArgs); this.hide(); - } + }; public render() { return ( <> {this.props.children && this.props.children(this.showConfirm)} <Modal show={this.state.open} onHide={this.hide}> - <Modal.Header closeButton={true} >{this.props.title}</Modal.Header> - <Modal.Body> - {this.props.description} - </Modal.Body> + <Modal.Header closeButton>{this.props.title}</Modal.Header> + <Modal.Body>{this.props.description}</Modal.Body> <Modal.Footer> <Button onClick={this.hide}>{t('Cancel')}</Button> - <Button bsStyle='danger' onClick={this.confirm}> + <Button bsStyle="danger" onClick={this.confirm}> {t('OK')} </Button> </Modal.Footer> diff --git a/superset-frontend/src/components/ListView/ListView.tsx b/superset-frontend/src/components/ListView/ListView.tsx index 5d3168e..f7810d3 100644 --- a/superset-frontend/src/components/ListView/ListView.tsx +++ b/superset-frontend/src/components/ListView/ListView.tsx @@ -31,7 +31,13 @@ import { import IndeterminateCheckbox from '../IndeterminateCheckbox'; import './ListViewStyles.less'; import TableCollection from './TableCollection'; -import { FetchDataConfig, FilterToggle, FilterType, FilterTypeMap, SortColumn } from './types'; +import { + FetchDataConfig, + FilterToggle, + FilterType, + FilterTypeMap, + SortColumn, +} from './types'; import { convertFilters, removeFromList, useListViewState } from './utils'; interface Props { @@ -45,7 +51,11 @@ interface Props { title?: string; initialSort?: SortColumn[]; filterTypes?: FilterTypeMap; - bulkActions?: Array<{ key?: string, name: React.ReactNode, onSelect: (rows: any[]) => any }>; + bulkActions?: Array<{ + key?: string; + name: React.ReactNode; + onSelect: (rows: any[]) => any; + }>; } const bulkSelectColumnConfig = { @@ -102,7 +112,9 @@ const ListView: FunctionComponent<Props> = ({ initialPageSize, initialSort, }); - const filterableColumns = useMemo(() => columns.filter((c) => c.filterable), [columns]); + const filterableColumns = useMemo(() => columns.filter(c => c.filterable), [ + columns, + ]); const filterable = Boolean(columns.length); const removeFilterAndApply = (index: number) => { @@ -114,25 +126,26 @@ const ListView: FunctionComponent<Props> = ({ return ( <div className={`superset-list-view ${className}`}> {title && filterable && ( - <div className='header'> + <div className="header"> <Row> <Col md={10}> <h2>{t(title)}</h2> </Col> {filterable && ( <Col md={2}> - <div className='filter-dropdown'> + <div className="filter-dropdown"> <DropdownButton - id='filter-picker' - bsSize='small' + id="filter-picker" + bsSize="small" bsStyle={'default'} - noCaret={true} - title={( + noCaret + title={ <> - <i className='fa fa-filter text-primary' /> - {' '}{t('Filter List')} + <i className="fa fa-filter text-primary" /> + {' '} + {t('Filter List')} </> - )} + } > {filterableColumns .map(({ id, accessor, Header }) => ({ @@ -143,8 +156,8 @@ const ListView: FunctionComponent<Props> = ({ <MenuItem key={ft.id} eventKey={ft} - onSelect={ - (fltr: FilterToggle) => setFilterToggles([...filterToggles, fltr]) + onSelect={(fltr: FilterToggle) => + setFilterToggles([...filterToggles, fltr]) } > {ft.Header} @@ -157,51 +170,54 @@ const ListView: FunctionComponent<Props> = ({ </Row> <hr /> {filterToggles.map((ft, i) => ( - <div key={`${ft.Header}-${i}`} className='filter-inputs'> + <div key={`${ft.Header}-${i}`} className="filter-inputs"> <Row> - <Col className='text-center filter-column' md={2}> + <Col className="text-center filter-column" md={2}> <span>{ft.Header}</span> </Col> <Col md={2}> <FormControl - componentClass='select' - bsSize='small' + componentClass="select" + bsSize="small" value={ft.filterId} - placeholder={filterTypes[ft.id] ? filterTypes[ft.id][0].name : ''} + placeholder={ + filterTypes[ft.id] ? filterTypes[ft.id][0].name : '' + } onChange={(e: React.MouseEvent<HTMLInputElement>) => updateFilterToggle(i, { filterId: e.currentTarget.value }) } > - {filterTypes[ft.id] && filterTypes[ft.id].map( - ({ name, operator }: FilterType) => ( - <option key={name} value={operator}> - {name} - </option> - ), - )} + {filterTypes[ft.id] && + filterTypes[ft.id].map( + ({ name, operator }: FilterType) => ( + <option key={name} value={operator}> + {name} + </option> + ), + )} </FormControl> </Col> <Col md={1} /> <Col md={4}> <FormControl - type='text' - bsSize='small' + type="text" + bsSize="small" value={ft.value || ''} - onChange={ - (e: React.KeyboardEvent<HTMLInputElement>) => - updateFilterToggle(i, { - value: e.currentTarget.value, - }) + onChange={(e: React.KeyboardEvent<HTMLInputElement>) => + updateFilterToggle(i, { + value: e.currentTarget.value, + }) } /> </Col> <Col md={1}> <div - className='filter-close' - role='button' + className="filter-close" + role="button" + tabIndex={0} onClick={() => removeFilterAndApply(i)} > - <i className='fa fa-close text-primary' /> + <i className="fa fa-close text-primary" /> </div> </Col> </Row> @@ -214,11 +230,11 @@ const ListView: FunctionComponent<Props> = ({ <Col md={10} /> <Col md={2}> <Button - data-test='apply-filters' - disabled={filtersApplied ? true : false} - bsStyle='primary' + data-test="apply-filters" + disabled={!!filtersApplied} + bsStyle="primary" onClick={applyFilters} - bsSize='small' + bsSize="small" > {t('Apply')} </Button> @@ -228,9 +244,8 @@ const ListView: FunctionComponent<Props> = ({ </> )} </div> - ) - } - <div className='body'> + )} + <div className="body"> <TableCollection getTableProps={getTableProps} getTableBodyProps={getTableBodyProps} @@ -240,33 +255,33 @@ const ListView: FunctionComponent<Props> = ({ loading={loading} /> </div> - <div className='footer'> + <div className="footer"> <Row> <Col md={2}> - <div className='form-actions-container'> - <div className='btn-group'> + <div className="form-actions-container"> + <div className="btn-group"> {bulkActions.length > 0 && ( <DropdownButton - id='bulk-actions' - bsSize='small' - bsStyle='default' - noCaret={true} - title={( + id="bulk-actions" + bsSize="small" + bsStyle="default" + noCaret + title={ <> - {t('Actions')} <span className='caret' /> + {t('Actions')} <span className="caret" /> </> - )} + } > - {bulkActions.map((action) => ( + {bulkActions.map(action => ( <MenuItem id={action.name} key={action.key || action.name} eventKey={selectedFlatRows} - onSelect={ - (selectedRows: typeof selectedFlatRows) => { - action.onSelect(selectedRows.map((r: any) => r.original)); - } - } + onSelect={(selectedRows: typeof selectedFlatRows) => { + action.onSelect( + selectedRows.map((r: any) => r.original), + ); + }} > {action.name} </MenuItem> @@ -276,7 +291,7 @@ const ListView: FunctionComponent<Props> = ({ </div> </div> </Col> - <Col md={8} className='text-center'> + <Col md={8} className="text-center"> <Pagination prev={canPreviousPage} first={pageIndex > 1} @@ -284,23 +299,24 @@ const ListView: FunctionComponent<Props> = ({ last={pageIndex < pageCount - 2} items={pageCount} activePage={pageIndex + 1} - ellipsis={true} - boundaryLinks={true} + ellipsis + boundaryLinks maxButtons={5} onSelect={(p: number) => gotoPage(p - 1)} /> </Col> <Col md={2}> - <span className='pull-right'> + <span className="pull-right"> {t('showing')}{' '} <strong> - {pageSize * pageIndex + (rows.length && 1)}-{pageSize * pageIndex + rows.length} + {pageSize * pageIndex + (rows.length && 1)}- + {pageSize * pageIndex + rows.length} </strong>{' '} {t('of')} <strong>{count}</strong> </span> </Col> </Row> - </div > + </div> </div> ); }; diff --git a/superset-frontend/src/components/ListView/TableCollection.tsx b/superset-frontend/src/components/ListView/TableCollection.tsx index 1baae15..d3fa085 100644 --- a/superset-frontend/src/components/ListView/TableCollection.tsx +++ b/superset-frontend/src/components/ListView/TableCollection.tsx @@ -17,6 +17,7 @@ * under the License. */ import React from 'react'; +import cx from 'classnames'; import { Cell, HeaderGroup, Row } from 'react-table'; interface Props<D extends object = {}> { @@ -37,23 +38,24 @@ export default function TableCollection({ loading, }: Props<any>) { return ( - <table {...getTableProps()} className='table table-hover'> + <table {...getTableProps()} className="table table-hover"> <thead> - {headerGroups.map((headerGroup) => ( + {headerGroups.map(headerGroup => ( <tr {...headerGroup.getHeaderGroupProps()}> {headerGroup.headers.map((column: any) => ( - <th {...column.getHeaderProps(column.getSortByToggleProps())} data-test='sort-header'> + <th + {...column.getHeaderProps(column.getSortByToggleProps())} + data-test="sort-header" + > {column.render('Header')} {' '} {column.sortable && ( <i - className={`text-primary fa fa-${ - column.isSorted - ? column.isSortedDesc - ? 'sort-down' - : 'sort-up' - : 'sort' - }`} + className={cx('text-primary fa', { + 'fa-sort': !column.isSorted, + 'fa-sort-down': column.isSorted && column.isSortedDesc, + 'fa-sort-up': column.isSorted && !column.isSortedDesc, + })} /> )} </th> @@ -62,7 +64,7 @@ export default function TableCollection({ ))} </thead> <tbody {...getTableBodyProps()}> - {rows.map((row) => { + {rows.map(row => { prepareRow(row); const loadingProps = loading ? { className: 'table-row-loader' } : {}; return ( @@ -70,7 +72,9 @@ export default function TableCollection({ {...row.getRowProps()} {...loadingProps} onMouseEnter={() => row.setState && row.setState({ hover: true })} - onMouseLeave={() => row.setState && row.setState({ hover: false })} + onMouseLeave={() => + row.setState && row.setState({ hover: false }) + } > {row.cells.map((cell: Cell<any>) => { const columnCellProps = cell.column.cellProps || {}; diff --git a/superset-frontend/src/components/ListView/utils.ts b/superset-frontend/src/components/ListView/utils.ts index cb0fdc8..371bdf1 100644 --- a/superset-frontend/src/components/ListView/utils.ts +++ b/superset-frontend/src/components/ListView/utils.ts @@ -55,7 +55,7 @@ function updateInList(list: any[], index: number, update: any): any[] { export function convertFilters(fts: FilterToggle[]) { return fts .filter((ft: FilterToggle) => ft.value) - .map((ft) => ({ value: null, filterId: ft.filterId || 'sw', ...ft })); + .map(ft => ({ value: null, filterId: ft.filterId || 'sw', ...ft })); } interface UseListViewConfig { diff --git a/superset-frontend/src/dashboard/stylesheets/popover-menu.less b/superset-frontend/src/dashboard/stylesheets/popover-menu.less index c1fc157..4d14ba1 100644 --- a/superset-frontend/src/dashboard/stylesheets/popover-menu.less +++ b/superset-frontend/src/dashboard/stylesheets/popover-menu.less @@ -51,7 +51,8 @@ cursor: default; z-index: @z-index-max; - &, .menu-item { + &, + .menu-item { display: flex; flex-direction: row; align-items: center; diff --git a/superset-frontend/src/utils/safeStringify.ts b/superset-frontend/src/utils/safeStringify.ts index 230dd35..b398bde 100644 --- a/superset-frontend/src/utils/safeStringify.ts +++ b/superset-frontend/src/utils/safeStringify.ts @@ -31,10 +31,10 @@ export function safeStringify(object: any): string { // We've seen this object before try { // Quick deep copy to duplicate if this is a repeat rather than a circle. - return JSON.parse(JSON.stringify(value)); + return JSON.parse(JSON.stringify(value)); } catch (err) { // Discard key if value cannot be duplicated. - return; + return; // eslint-disable-line consistent-return } } // Store the value in our cache. diff --git a/superset-frontend/src/views/chartList/ChartList.tsx b/superset-frontend/src/views/chartList/ChartList.tsx index f90d8d7..34c260a 100644 --- a/superset-frontend/src/views/chartList/ChartList.tsx +++ b/superset-frontend/src/views/chartList/ChartList.tsx @@ -22,7 +22,7 @@ import moment from 'moment'; import PropTypes from 'prop-types'; import React from 'react'; // @ts-ignore -import { Button, Modal, Panel } from 'react-bootstrap'; +import { Panel } from 'react-bootstrap'; import ConfirmStatusChange from 'src/components/ConfirmStatusChange'; import ListView from 'src/components/ListView/ListView'; import { FetchDataConfig, FilterTypeMap } from 'src/components/ListView/types'; @@ -55,20 +55,11 @@ interface Chart { } class ChartList extends React.PureComponent<Props, State> { - - get canEdit() { - return this.hasPerm('can_edit'); - } - - get canDelete() { - return this.hasPerm('can_delete'); - } - - public static propTypes = { + static propTypes = { addDangerToast: PropTypes.func.isRequired, }; - public state: State = { + state: State = { chartCount: 0, charts: [], filterTypes: {}, @@ -78,15 +69,34 @@ class ChartList extends React.PureComponent<Props, State> { permissions: [], }; - public initialSort = [{ id: 'changed_on', desc: true }]; + componentDidMount() { + SupersetClient.get({ + endpoint: `/api/v1/chart/_info`, + }).then(({ json = {} }) => { + this.setState({ + filterTypes: json.filters, + permissions: json.permissions, + }); + }); + } + + get canEdit() { + return this.hasPerm('can_edit'); + } - public columns = [ + get canDelete() { + return this.hasPerm('can_delete'); + } + + initialSort = [{ id: 'changed_on', desc: true }]; + + columns = [ { Cell: ({ row: { - original: { url, slice_name }, + original: { url, slice_name: sliceName }, }, - }: any) => <a href={url}>{slice_name}</a>, + }: any) => <a href={url}>{sliceName}</a>, Header: t('Chart'), accessor: 'slice_name', filterable: true, @@ -95,9 +105,9 @@ class ChartList extends React.PureComponent<Props, State> { { Cell: ({ row: { - original: { viz_type }, + original: { viz_type: vizType }, }, - }: any) => viz_type, + }: any) => vizType, Header: t('Visualization Type'), accessor: 'viz_type', sortable: true, @@ -105,9 +115,12 @@ class ChartList extends React.PureComponent<Props, State> { { Cell: ({ row: { - original: { datasource_name_text, datasource_link }, + original: { + datasource_name_text: dsNameTxt, + datasource_link: dsLink, + }, }, - }: any) => <a href={datasource_link}>{datasource_name_text}</a>, + }: any) => <a href={dsLink}>{dsNameTxt}</a>, Header: t('Datasource'), accessor: 'datasource_name_text', sortable: true, @@ -115,9 +128,12 @@ class ChartList extends React.PureComponent<Props, State> { { Cell: ({ row: { - original: { changed_by_name, changed_by_url }, + original: { + changed_by_name: changedByName, + changed_by_url: changedByUrl, + }, }, - }: any) => <a href={changed_by_url}>{changed_by_name}</a>, + }: any) => <a href={changedByName}>{changedByUrl}</a>, Header: t('Creator'), accessor: 'creator', sortable: true, @@ -125,11 +141,9 @@ class ChartList extends React.PureComponent<Props, State> { { Cell: ({ row: { - original: { changed_on }, + original: { changed_on: changedOn }, }, - }: any) => ( - <span className='no-wrap'>{moment(changed_on).fromNow()}</span> - ), + }: any) => <span className="no-wrap">{moment(changedOn).fromNow()}</span>, Header: t('Last Modified'), accessor: 'changed_on', sortable: true, @@ -143,31 +157,40 @@ class ChartList extends React.PureComponent<Props, State> { } return ( - <span className={`actions ${state && state.hover ? '' : 'invisible'}`}> + <span + className={`actions ${state && state.hover ? '' : 'invisible'}`} + > {this.canDelete && ( <ConfirmStatusChange title={t('Please Confirm')} - description={<>{t('Are you sure you want to delete')} <b>{original.slice_name}</b>?</>} + description={ + <> + {t('Are you sure you want to delete')}{' '} + <b>{original.slice_name}</b>? + </> + } onConfirm={handleDelete} > - {(confirmDelete) => ( + {confirmDelete => ( <span - role='button' - className='action-button' + role="button" + tabIndex={0} + className="action-button" onClick={confirmDelete} > - <i className='fa fa-trash' /> + <i className="fa fa-trash" /> </span> )} </ConfirmStatusChange> )} {this.canEdit && ( <span - role='button' - className='action-button' + role="button" + tabIndex={0} + className="action-button" onClick={handleEdit} > - <i className='fa fa-pencil' /> + <i className="fa fa-pencil" /> </span> )} </span> @@ -178,37 +201,42 @@ class ChartList extends React.PureComponent<Props, State> { }, ]; - public hasPerm = (perm: string) => { + hasPerm = (perm: string) => { if (!this.state.permissions.length) { return false; } - return this.state.permissions.some((p) => p === perm); - } + return this.state.permissions.some(p => p === perm); + }; - public handleChartEdit = ({ id }: { id: number }) => { + handleChartEdit = ({ id }: { id: number }) => { window.location.assign(`/chart/edit/${id}`); - } + }; - public handleChartDelete = ({ id, slice_name }: Chart) => { + handleChartDelete = ({ id, slice_name: sliceName }: Chart) => { SupersetClient.delete({ endpoint: `/api/v1/chart/${id}`, }).then( - (resp) => { + () => { const { lastFetchDataConfig } = this.state; if (lastFetchDataConfig) { this.fetchData(lastFetchDataConfig); } - this.props.addSuccessToast(t('Deleted: %(slice_name)', slice_name)); + this.props.addSuccessToast(t('Deleted: %(slice_name)', sliceName)); }, - (err: any) => { - this.props.addDangerToast(t('There was an issue deleting: %(slice_name)', slice_name)); + () => { + this.props.addDangerToast( + t('There was an issue deleting: %(slice_name)', sliceName), + ); }, ); - } - public handleBulkDashboardDelete = (charts: Chart[]) => { + }; + + handleBulkDashboardDelete = (charts: Chart[]) => { SupersetClient.delete({ - endpoint: `/api/v1/dashboard/?q=!(${charts.map(({ id }) => id).join(',')})`, + endpoint: `/api/v1/dashboard/?q=!(${charts + .map(({ id }) => id) + .join(',')})`, }).then( ({ json = {} }) => { const { lastFetchDataConfig } = this.state; @@ -219,19 +247,16 @@ class ChartList extends React.PureComponent<Props, State> { }, (err: any) => { console.error(err); - this.props.addDangerToast(t('There was an issue deleting the selected dashboards')); + this.props.addDangerToast( + t('There was an issue deleting the selected dashboards'), + ); }, ); - } + }; - public fetchData = ({ - pageIndex, - pageSize, - sortBy, - filters, - }: FetchDataConfig) => { + fetchData = ({ pageIndex, pageSize, sortBy, filters }: FetchDataConfig) => { this.setState({ loading: true }); - const filterExps = Object.keys(filters).map((fk) => ({ + const filterExps = Object.keys(filters).map(fk => ({ col: fk, opr: filters[fk].filterId, value: filters[fk].filterValue, @@ -249,50 +274,48 @@ class ChartList extends React.PureComponent<Props, State> { endpoint: `/api/v1/chart/?q=${queryParams}`, }) .then(({ json = {} }) => { - this.setState({ charts: json.result, chartCount: json.count, labelColumns: json.label_columns }); + this.setState({ + charts: json.result, + chartCount: json.count, + labelColumns: json.label_columns, + }); }) .catch(() => { - this.props.addDangerToast( - t('An error occurred while fetching Charts'), - ); + this.props.addDangerToast(t('An error occurred while fetching Charts')); }) .finally(() => { this.setState({ loading: false }); }); - } - - public componentDidMount() { - SupersetClient.get({ - endpoint: `/api/v1/chart/_info`, - }) - .then(({ json = {} }) => { - this.setState({ filterTypes: json.filters, permissions: json.permissions }); - }); - } + }; - public render() { + render() { const { charts, chartCount, loading, filterTypes } = this.state; return ( - <div className='container welcome'> + <div className="container welcome"> <Panel> - <ConfirmStatusChange title={t('Please confirm')} - description={t('Are you sure you want to delete the selected charts?')} + description={t( + 'Are you sure you want to delete the selected charts?', + )} onConfirm={this.handleBulkDashboardDelete} > - {(confirmDelete) => { + {confirmDelete => { const bulkActions = []; if (this.canDelete) { bulkActions.push({ key: 'delete', - name: <><i className='fa fa-trash' /> Delete</>, + name: ( + <> + <i className="fa fa-trash" /> Delete + </> + ), onSelect: confirmDelete, }); } return ( <ListView - className='chart-list-view' + className="chart-list-view" title={'Charts'} columns={this.columns} data={charts} @@ -308,7 +331,7 @@ class ChartList extends React.PureComponent<Props, State> { }} </ConfirmStatusChange> </Panel> - </div > + </div> ); } } diff --git a/superset-frontend/src/views/dashboardList/DashboardList.tsx b/superset-frontend/src/views/dashboardList/DashboardList.tsx index 695c7f1..929f0a3 100644 --- a/superset-frontend/src/views/dashboardList/DashboardList.tsx +++ b/superset-frontend/src/views/dashboardList/DashboardList.tsx @@ -22,7 +22,7 @@ import moment from 'moment'; import PropTypes from 'prop-types'; import React from 'react'; // @ts-ignore -import { Button, Modal, Panel } from 'react-bootstrap'; +import { Panel } from 'react-bootstrap'; import ConfirmStatusChange from 'src/components/ConfirmStatusChange'; import ListView from 'src/components/ListView/ListView'; import { FetchDataConfig, FilterTypeMap } from 'src/components/ListView/types'; @@ -57,6 +57,30 @@ interface Dashboard { } class DashboardList extends React.PureComponent<Props, State> { + static propTypes = { + addDangerToast: PropTypes.func.isRequired, + }; + + state: State = { + dashboardCount: 0, + dashboards: [], + filterTypes: {}, + labelColumns: {}, + lastFetchDataConfig: null, + loading: false, + permissions: [], + }; + + componentDidMount() { + SupersetClient.get({ + endpoint: `/api/v1/dashboard/_info`, + }).then(({ json = {} }) => { + this.setState({ + filterTypes: json.filters, + permissions: json.permissions, + }); + }); + } get canEdit() { return this.hasPerm('can_edit'); @@ -70,29 +94,15 @@ class DashboardList extends React.PureComponent<Props, State> { return this.hasPerm('can_mulexport'); } - public static propTypes = { - addDangerToast: PropTypes.func.isRequired, - }; - - public state: State = { - dashboardCount: 0, - dashboards: [], - filterTypes: {}, - labelColumns: {}, - lastFetchDataConfig: null, - loading: false, - permissions: [], - }; - - public initialSort = [{ id: 'changed_on', desc: true }]; + initialSort = [{ id: 'changed_on', desc: true }]; - public columns = [ + columns = [ { Cell: ({ row: { - original: { url, dashboard_title }, + original: { url, dashboard_title: dashboardTitle }, }, - }: any) => <a href={url}>{dashboard_title}</a>, + }: any) => <a href={url}>{dashboardTitle}</a>, Header: t('Title'), accessor: 'dashboard_title', filterable: true, @@ -101,9 +111,12 @@ class DashboardList extends React.PureComponent<Props, State> { { Cell: ({ row: { - original: { changed_by_name, changed_by_url }, + original: { + changed_by_name: changedByName, + changed_by_url: changedByUrl, + }, }, - }: any) => <a href={changed_by_url}>{changed_by_name}</a>, + }: any) => <a href={changedByUrl}>{changedByName}</a>, Header: t('Changed By Name'), accessor: 'changed_by_fk', sortable: true, @@ -114,8 +127,10 @@ class DashboardList extends React.PureComponent<Props, State> { original: { published }, }, }: any) => ( - <span className='no-wrap'>{published ? <i className='fa fa-check' /> : ''}</span> - ), + <span className="no-wrap"> + {published ? <i className="fa fa-check" /> : ''} + </span> + ), Header: t('Published'), accessor: 'published', sortable: true, @@ -123,11 +138,9 @@ class DashboardList extends React.PureComponent<Props, State> { { Cell: ({ row: { - original: { changed_on }, + original: { changed_on: changedOn }, }, - }: any) => ( - <span className='no-wrap'>{moment(changed_on).fromNow()}</span> - ), + }: any) => <span className="no-wrap">{moment(changedOn).fromNow()}</span>, Header: t('Changed On'), accessor: 'changed_on', sortable: true, @@ -141,40 +154,50 @@ class DashboardList extends React.PureComponent<Props, State> { return null; } return ( - <span className={`actions ${state && state.hover ? '' : 'invisible'}`}> + <span + className={`actions ${state && state.hover ? '' : 'invisible'}`} + > {this.canDelete && ( <ConfirmStatusChange title={t('Please Confirm')} - description={<>{t('Are you sure you want to delete')} <b>{original.dashboard_title}</b>?</>} + description={ + <> + {t('Are you sure you want to delete')}{' '} + <b>{original.dashboard_title}</b>? + </> + } onConfirm={handleDelete} > - {(confirmDelete) => ( + {confirmDelete => ( <span - role='button' - className='action-button' + role="button" + tabIndex={0} + className="action-button" onClick={confirmDelete} > - <i className='fa fa-trash' /> + <i className="fa fa-trash" /> </span> )} </ConfirmStatusChange> )} {this.canExport && ( <span - role='button' - className='action-button' + role="button" + tabIndex={0} + className="action-button" onClick={handleExport} > - <i className='fa fa-database' /> + <i className="fa fa-database" /> </span> )} {this.canEdit && ( <span - role='button' - className='action-button' + role="button" + tabIndex={0} + className="action-button" onClick={handleEdit} > - <i className='fa fa-pencil' /> + <i className="fa fa-pencil" /> </span> )} </span> @@ -185,12 +208,23 @@ class DashboardList extends React.PureComponent<Props, State> { }, ]; - public handleDashboardEdit = ({ id }: { id: number }) => { + hasPerm = (perm: string) => { + if (!this.state.permissions.length) { + return false; + } + + return Boolean(this.state.permissions.find(p => p === perm)); + }; + + handleDashboardEdit = ({ id }: { id: number }) => { window.location.assign(`/dashboard/edit/${id}`); - } + }; - public handleDashboardDelete = ({ id, dashboard_title }: Dashboard) => { - return SupersetClient.delete({ + handleDashboardDelete = ({ + id, + dashboard_title: dashboardTitle, + }: Dashboard) => + SupersetClient.delete({ endpoint: `/api/v1/dashboard/${id}`, }).then( () => { @@ -198,18 +232,21 @@ class DashboardList extends React.PureComponent<Props, State> { if (lastFetchDataConfig) { this.fetchData(lastFetchDataConfig); } - this.props.addSuccessToast(t('Deleted') + ` ${dashboard_title}`); + this.props.addSuccessToast(`${t('Deleted')} ${dashboardTitle}`); }, (err: any) => { console.error(err); - this.props.addDangerToast(t('There was an issue deleting') + `${dashboard_title}`); + this.props.addDangerToast( + `${t('There was an issue deleting')}${dashboardTitle}`, + ); }, ); - } - public handleBulkDashboardDelete = (dashboards: Dashboard[]) => { + handleBulkDashboardDelete = (dashboards: Dashboard[]) => { SupersetClient.delete({ - endpoint: `/api/v1/dashboard/?q=!(${dashboards.map(({ id }) => id).join(',')})`, + endpoint: `/api/v1/dashboard/?q=!(${dashboards + .map(({ id }) => id) + .join(',')})`, }).then( ({ json = {} }) => { const { lastFetchDataConfig } = this.state; @@ -220,21 +257,20 @@ class DashboardList extends React.PureComponent<Props, State> { }, (err: any) => { console.error(err); - this.props.addDangerToast(t('There was an issue deleting the selected dashboards')); + this.props.addDangerToast( + t('There was an issue deleting the selected dashboards'), + ); }, ); - } + }; - public handleBulkDashboardExport = (dashboards: Dashboard[]) => { - return window.location.href = `/ api / v1 / dashboard /export/?q=!(${dashboards.map(({ id }) => id).join(',')})`; - } + handleBulkDashboardExport = (dashboards: Dashboard[]) => { + window.location.href = `/api/v1/dashboard/export/?q=!(${dashboards + .map(({ id }) => id) + .join(',')})`; + }; - public fetchData = ({ - pageIndex, - pageSize, - sortBy, - filters, - }: FetchDataConfig) => { + fetchData = ({ pageIndex, pageSize, sortBy, filters }: FetchDataConfig) => { // set loading state, cache the last config for fetching data in this component. this.setState({ lastFetchDataConfig: { @@ -263,7 +299,11 @@ class DashboardList extends React.PureComponent<Props, State> { endpoint: `/api/v1/dashboard/?q=${queryParams}`, }) .then(({ json = {} }) => { - this.setState({ dashboards: json.result, dashboardCount: json.count, labelColumns: json.label_columns }); + this.setState({ + dashboards: json.result, + dashboardCount: json.count, + labelColumns: json.label_columns, + }); }) .catch(() => { this.props.addDangerToast( @@ -273,46 +313,47 @@ class DashboardList extends React.PureComponent<Props, State> { .finally(() => { this.setState({ loading: false }); }); - } - - public componentDidMount() { - SupersetClient.get({ - endpoint: `/api/v1/dashboard/_info`, - }) - .then(({ json = {} }) => { - this.setState({ filterTypes: json.filters, permissions: json.permissions }); - }); - } + }; - public render() { + render() { const { dashboards, dashboardCount, loading, filterTypes } = this.state; return ( - <div className='container welcome' > + <div className="container welcome"> <Panel> <ConfirmStatusChange title={t('Please confirm')} - description={t('Are you sure you want to delete the selected dashboards?')} + description={t( + 'Are you sure you want to delete the selected dashboards?', + )} onConfirm={this.handleBulkDashboardDelete} > - {(confirmDelete) => { + {confirmDelete => { const bulkActions = []; if (this.canDelete) { bulkActions.push({ key: 'delete', - name: <><i className='fa fa-trash' /> Delete</>, + name: ( + <> + <i className="fa fa-trash" /> Delete + </> + ), onSelect: confirmDelete, }); } if (this.canExport) { bulkActions.push({ key: 'export', - name: <><i className='fa fa-database' /> Export</>, + name: ( + <> + <i className="fa fa-database" /> Export + </> + ), onSelect: this.handleBulkDashboardExport, }); } return ( <ListView - className='dashboard-list-view' + className="dashboard-list-view" title={'Dashboards'} columns={this.columns} data={dashboards} @@ -331,14 +372,6 @@ class DashboardList extends React.PureComponent<Props, State> { </div> ); } - - private hasPerm = (perm: string) => { - if (!this.state.permissions.length) { - return false; - } - - return Boolean(this.state.permissions.find((p) => p === perm)); - } } export default withToasts(DashboardList); diff --git a/superset-frontend/tsconfig.json b/superset-frontend/tsconfig.json index 6a505fa..0ab027c 100644 --- a/superset-frontend/tsconfig.json +++ b/superset-frontend/tsconfig.json @@ -1,24 +1,26 @@ { "compilerOptions": { - "baseUrl": ".", - "outDir": "./dist", - "module": "commonjs", - "target": "es5", - "lib": ["es6", "dom", "es2018.promise"], - "sourceMap": true, "allowJs": true, + "allowSyntheticDefaultImports": true, + "baseUrl": ".", + "esModuleInterop": true, + "forceConsistentCasingInFileNames": true, + "importHelpers": true, "jsx": "react", + "lib": ["dom", "esnext"], + "module": "esnext", "moduleResolution": "node", - "forceConsistentCasingInFileNames": true, + "noImplicitAny": true, "noImplicitReturns": true, "noImplicitThis": true, - "noImplicitAny": true, - "importHelpers": true, - "strictNullChecks": true, - "suppressImplicitAnyIndexErrors": true, "noUnusedLocals": true, + "outDir": "./dist", + "pretty": true, "skipLibCheck": true, - "esModuleInterop": true + "sourceMap": true, + "strictNullChecks": true, + "suppressImplicitAnyIndexErrors": true, + "target": "es5" }, "include": ["./src/**/*", "./spec/**/*"] } diff --git a/superset-frontend/tslint.json b/superset-frontend/tslint.json deleted file mode 100644 index bd9c88f..0000000 --- a/superset-frontend/tslint.json +++ /dev/null @@ -1,14 +0,0 @@ -{ - "extends": ["tslint:recommended", "tslint-react"], - "jsRules": { - "no-console": false - }, - "rules": { - "interface-name": [true, "never-prefix"], - "quotemark": [true, "single"], - "jsx-no-multiline-js": false, - "jsx-no-lambda": false, - "no-console": false - }, - "rulesDirectory": [] -}