This is an automated email from the ASF dual-hosted git repository.

villebro pushed a commit to branch 0.37
in repository https://gitbox.apache.org/repos/asf/incubator-superset.git

commit 2631b3882e58e56f2f4b163b351d2ec5f5043ce1
Author: Erik Ritter <erik.rit...@airbnb.com>
AuthorDate: Tue Jul 14 16:39:37 2020 -0700

    chore: add typing to profile (#10282)
---
 superset-frontend/package-lock.json                | 82 ++++++++++++----------
 superset-frontend/package.json                     |  9 ++-
 .../profile/{App_spec.jsx => App_spec.tsx}         |  0
 ...tedContent_spec.jsx => CreatedContent_spec.tsx} |  0
 ...itableTitle_spec.jsx => EditableTitle_spec.tsx} |  0
 .../{Favorites_spec.jsx => Favorites_spec.tsx}     |  0
 ...ntActivity_spec.jsx => RecentActivity_spec.tsx} |  0
 .../{Security_spec.jsx => Security_spec.tsx}       |  0
 .../{UserInfo_spec.jsx => UserInfo_spec.tsx}       |  0
 .../profile/{fixtures.jsx => fixtures.tsx}         |  0
 .../src/SqlLab/components/ResultSet.tsx            |  2 +
 .../src/messageToasts/enhancers/withToasts.tsx     |  4 +-
 superset-frontend/src/profile/{App.jsx => App.tsx} |  2 +-
 .../src/profile/components/{App.jsx => App.tsx}    | 21 +++---
 .../{CreatedContent.jsx => CreatedContent.tsx}     | 24 ++-----
 .../components/{Favorites.jsx => Favorites.tsx}    | 24 ++-----
 .../{RecentActivity.jsx => RecentActivity.tsx}     | 56 +++++++--------
 .../components/{Security.jsx => Security.tsx}      | 12 ++--
 .../src/profile/components/UserInfo.jsx            | 75 --------------------
 .../src/profile/components/UserInfo.tsx            | 76 ++++++++++++++++++++
 .../src/profile/{index.jsx => index.tsx}           |  0
 .../profile/types.ts}                              | 56 ++++++++++-----
 superset-frontend/webpack.config.js                |  2 +-
 23 files changed, 225 insertions(+), 220 deletions(-)

diff --git a/superset-frontend/package-lock.json 
b/superset-frontend/package-lock.json
index b5c4dad..135f6b4 100644
--- a/superset-frontend/package-lock.json
+++ b/superset-frontend/package-lock.json
@@ -7678,22 +7678,6 @@
         "@types/node": "*"
       }
     },
-    "@types/hoist-non-react-statics": {
-      "version": "3.3.1",
-      "resolved": 
"https://registry.npmjs.org/@types/hoist-non-react-statics/-/hoist-non-react-statics-3.3.1.tgz";,
-      "integrity": 
"sha512-iMIqiko6ooLrTh1joXodJK5X9xeEALT1kM5G3ZLhD3hszxBdIEd5C75U834D9mLcINgD4OyZf5uQXjkuYydWvA==",
-      "dev": true,
-      "requires": {
-        "@types/react": "*",
-        "hoist-non-react-statics": "^3.3.0"
-      },
-      "dependencies": {
-        "react-is": {
-          "version": 
"https://registry.npmjs.org/react-is/-/react-is-16.13.0.tgz";,
-          "integrity": 
"sha512-GFMtL0vHkiBv9HluwNZTggSn/sCyEt9n02aM0dSAjGGyqyNlAyftYm4phPxdvCigG15JreC5biwxCgTAJZ7yAA=="
-        }
-      }
-    },
     "@types/istanbul-lib-coverage": {
       "version": "2.0.3",
       "resolved": 
"https://registry.npmjs.org/@types/istanbul-lib-coverage/-/istanbul-lib-coverage-2.0.3.tgz";,
@@ -7818,6 +7802,14 @@
         "@types/react": "*"
       }
     },
+    "@types/react-gravatar": {
+      "version": "2.6.8",
+      "resolved": 
"https://registry.npmjs.org/@types/react-gravatar/-/react-gravatar-2.6.8.tgz";,
+      "integrity": 
"sha512-VMk0bF0w72l+opBm+EqLs0JqUG+hPowMBWCVGrbTwUWm/oDncvwNrf7P/ImwYwkTCKiLnU8Rc+/lyhehaIE/Rw==",
+      "requires": {
+        "@types/react": "*"
+      }
+    },
     "@types/react-json-tree": {
       "version": "0.6.11",
       "resolved": 
"https://registry.npmjs.org/@types/react-json-tree/-/react-json-tree-0.6.11.tgz";,
@@ -7836,31 +7828,13 @@
       }
     },
     "@types/react-redux": {
-      "version": "7.1.7",
-      "resolved": 
"https://registry.npmjs.org/@types/react-redux/-/react-redux-7.1.7.tgz";,
-      "integrity": 
"sha512-U+WrzeFfI83+evZE2dkZ/oF/1vjIYgqrb5dGgedkqVV8HEfDFujNgWCwHL89TDuWKb47U0nTBT6PLGq4IIogWg==",
+      "version": "5.0.21",
+      "resolved": 
"https://registry.npmjs.org/@types/react-redux/-/react-redux-5.0.21.tgz";,
+      "integrity": 
"sha512-ewkOW4GjnyXq5L++T31utI8yRmwj8iCIahZohYi1Ef7Xkrw0V/q92ao7x20rm38FKgImDaCCsaRGWfCJmF/Ukg==",
       "dev": true,
       "requires": {
-        "@types/hoist-non-react-statics": "^3.3.0",
         "@types/react": "*",
-        "hoist-non-react-statics": "^3.3.0",
-        "redux": "^4.0.0"
-      },
-      "dependencies": {
-        "react-is": {
-          "version": 
"https://registry.npmjs.org/react-is/-/react-is-16.13.0.tgz";,
-          "integrity": 
"sha512-GFMtL0vHkiBv9HluwNZTggSn/sCyEt9n02aM0dSAjGGyqyNlAyftYm4phPxdvCigG15JreC5biwxCgTAJZ7yAA=="
-        },
-        "redux": {
-          "version": "4.0.5",
-          "resolved": "https://registry.npmjs.org/redux/-/redux-4.0.5.tgz";,
-          "integrity": 
"sha512-VSz1uMAH24DM6MF72vcojpYPtrTUu3ByVWfPL1nPfVRb5mZVTve5GnNCUV53QM/BZ66xfWrm0CTWoM+Xlz8V1w==",
-          "dev": true,
-          "requires": {
-            "loose-envify": "^1.4.0",
-            "symbol-observable": "^1.2.0"
-          }
-        }
+        "redux": "^3.6.0"
       }
     },
     "@types/react-select": {
@@ -7923,6 +7897,25 @@
         "redux": "^3.6.0"
       }
     },
+    "@types/redux-mock-store": {
+      "version": "1.0.2",
+      "resolved": 
"https://registry.npmjs.org/@types/redux-mock-store/-/redux-mock-store-1.0.2.tgz";,
+      "integrity": 
"sha512-6LBtAQBN34i7SI5X+Qs4zpTEZO1tTDZ6sZ9fzFjYwTl3nLQXaBtwYdoV44CzNnyKu438xJ1lSIYyw0YMvunESw==",
+      "requires": {
+        "redux": "^4.0.5"
+      },
+      "dependencies": {
+        "redux": {
+          "version": "4.0.5",
+          "resolved": "https://registry.npmjs.org/redux/-/redux-4.0.5.tgz";,
+          "integrity": 
"sha512-VSz1uMAH24DM6MF72vcojpYPtrTUu3ByVWfPL1nPfVRb5mZVTve5GnNCUV53QM/BZ66xfWrm0CTWoM+Xlz8V1w==",
+          "requires": {
+            "loose-envify": "^1.4.0",
+            "symbol-observable": "^1.2.0"
+          }
+        }
+      }
+    },
     "@types/rison": {
       "version": "0.0.6",
       "resolved": "https://registry.npmjs.org/@types/rison/-/rison-0.0.6.tgz";,
@@ -7933,6 +7926,19 @@
       "resolved": 
"https://registry.npmjs.org/@types/shortid/-/shortid-0.0.29.tgz";,
       "integrity": "sha1-gJPuBBam4r8qpjOBCRFLP7/6Dps="
     },
+    "@types/sinon": {
+      "version": "9.0.4",
+      "resolved": "https://registry.npmjs.org/@types/sinon/-/sinon-9.0.4.tgz";,
+      "integrity": 
"sha512-sJmb32asJZY6Z2u09bl0G2wglSxDlROlAejCjsnor+LzBMz17gu8IU7vKC/vWDnv9zEq2wqADHVXFjf4eE8Gdw==",
+      "requires": {
+        "@types/sinonjs__fake-timers": "*"
+      }
+    },
+    "@types/sinonjs__fake-timers": {
+      "version": "6.0.1",
+      "resolved": 
"https://registry.npmjs.org/@types/sinonjs__fake-timers/-/sinonjs__fake-timers-6.0.1.tgz";,
+      "integrity": 
"sha512-yYezQwGWty8ziyYLdZjwxyMb0CZR49h8JALHGrxjQHWlqGgc8kLdHEgWrgL0uZ29DMvEVBDnHU2Wg36zKSIUtA=="
+    },
     "@types/sizzle": {
       "version": "2.3.2",
       "resolved": 
"https://registry.npmjs.org/@types/sizzle/-/sizzle-2.3.2.tgz";,
diff --git a/superset-frontend/package.json b/superset-frontend/package.json
index 9631bf8..17fab51 100644
--- a/superset-frontend/package.json
+++ b/superset-frontend/package.json
@@ -85,13 +85,13 @@
     "@superset-ui/legacy-plugin-chart-sankey": "^0.14.9",
     "@superset-ui/legacy-plugin-chart-sankey-loop": "^0.14.9",
     "@superset-ui/legacy-plugin-chart-sunburst": "^0.14.9",
-    "@superset-ui/plugin-chart-table": "^0.14.9",
     "@superset-ui/legacy-plugin-chart-treemap": "^0.14.9",
     "@superset-ui/legacy-plugin-chart-world-map": "^0.14.9",
     "@superset-ui/legacy-preset-chart-big-number": "^0.14.9",
     "@superset-ui/legacy-preset-chart-deckgl": "^0.2.4",
     "@superset-ui/legacy-preset-chart-nvd3": "^0.14.9",
     "@superset-ui/number-format": "^0.14.9",
+    "@superset-ui/plugin-chart-table": "^0.14.9",
     "@superset-ui/plugin-chart-word-cloud": "^0.14.9",
     "@superset-ui/preset-chart-xy": "^0.14.9",
     "@superset-ui/query": "^0.14.9",
@@ -103,14 +103,16 @@
     "@types/classnames": "^2.2.9",
     "@types/enzyme": "^3.10.5",
     "@types/react-bootstrap": "^0.32.21",
+    "@types/react-gravatar": "^2.6.8",
     "@types/react-json-tree": "^0.6.11",
     "@types/react-select": "^3.0.12",
     "@types/react-virtualized": "^9.21.10",
     "@types/react-window": "^1.8.2",
     "@types/redux-localstorage": "^1.0.8",
+    "@types/redux-mock-store": "^1.0.2",
     "@types/rison": "0.0.6",
+    "@types/sinon": "^9.0.4",
     "@vx/responsive": "^0.0.195",
-    "memoize-one": "^5.1.1",
     "abortcontroller-polyfill": "^1.1.9",
     "aphrodite": "^2.3.1",
     "array-move": "^2.2.1",
@@ -134,6 +136,7 @@
     "lodash": "^4.17.15",
     "lodash-es": "^4.17.14",
     "mathjs": "^3.20.2",
+    "memoize-one": "^5.1.1",
     "moment": "^2.20.1",
     "mousetrap": "^1.6.1",
     "mustache": "^2.2.1",
@@ -208,7 +211,7 @@
     "@types/react": "^16.9.38",
     "@types/react-dom": "^16.9.8",
     "@types/react-json-tree": "^0.6.11",
-    "@types/react-redux": "^7.1.7",
+    "@types/react-redux": "^5.0.2",
     "@types/react-table": "^7.0.19",
     "@types/react-ultimate-pagination": "^1.2.0",
     "@types/yargs": "12 - 15",
diff --git a/superset-frontend/spec/javascripts/profile/App_spec.jsx 
b/superset-frontend/spec/javascripts/profile/App_spec.tsx
similarity index 100%
rename from superset-frontend/spec/javascripts/profile/App_spec.jsx
rename to superset-frontend/spec/javascripts/profile/App_spec.tsx
diff --git a/superset-frontend/spec/javascripts/profile/CreatedContent_spec.jsx 
b/superset-frontend/spec/javascripts/profile/CreatedContent_spec.tsx
similarity index 100%
rename from superset-frontend/spec/javascripts/profile/CreatedContent_spec.jsx
rename to superset-frontend/spec/javascripts/profile/CreatedContent_spec.tsx
diff --git a/superset-frontend/spec/javascripts/profile/EditableTitle_spec.jsx 
b/superset-frontend/spec/javascripts/profile/EditableTitle_spec.tsx
similarity index 100%
rename from superset-frontend/spec/javascripts/profile/EditableTitle_spec.jsx
rename to superset-frontend/spec/javascripts/profile/EditableTitle_spec.tsx
diff --git a/superset-frontend/spec/javascripts/profile/Favorites_spec.jsx 
b/superset-frontend/spec/javascripts/profile/Favorites_spec.tsx
similarity index 100%
rename from superset-frontend/spec/javascripts/profile/Favorites_spec.jsx
rename to superset-frontend/spec/javascripts/profile/Favorites_spec.tsx
diff --git a/superset-frontend/spec/javascripts/profile/RecentActivity_spec.jsx 
b/superset-frontend/spec/javascripts/profile/RecentActivity_spec.tsx
similarity index 100%
copy from superset-frontend/spec/javascripts/profile/RecentActivity_spec.jsx
copy to superset-frontend/spec/javascripts/profile/RecentActivity_spec.tsx
diff --git a/superset-frontend/spec/javascripts/profile/Security_spec.jsx 
b/superset-frontend/spec/javascripts/profile/Security_spec.tsx
similarity index 100%
rename from superset-frontend/spec/javascripts/profile/Security_spec.jsx
rename to superset-frontend/spec/javascripts/profile/Security_spec.tsx
diff --git a/superset-frontend/spec/javascripts/profile/UserInfo_spec.jsx 
b/superset-frontend/spec/javascripts/profile/UserInfo_spec.tsx
similarity index 100%
rename from superset-frontend/spec/javascripts/profile/UserInfo_spec.jsx
rename to superset-frontend/spec/javascripts/profile/UserInfo_spec.tsx
diff --git a/superset-frontend/spec/javascripts/profile/fixtures.jsx 
b/superset-frontend/spec/javascripts/profile/fixtures.tsx
similarity index 100%
rename from superset-frontend/spec/javascripts/profile/fixtures.jsx
rename to superset-frontend/spec/javascripts/profile/fixtures.tsx
diff --git a/superset-frontend/src/SqlLab/components/ResultSet.tsx 
b/superset-frontend/src/SqlLab/components/ResultSet.tsx
index b074872..7de1175 100644
--- a/superset-frontend/src/SqlLab/components/ResultSet.tsx
+++ b/superset-frontend/src/SqlLab/components/ResultSet.tsx
@@ -158,6 +158,7 @@ export default class ResultSet extends React.PureComponent<
               this.props.database &&
               this.props.database.allows_virtual_table_explore && (
                 <ExploreResultsButton
+                  // @ts-ignore Redux types are difficult to work with, 
ignoring for now
                   query={this.props.query}
                   database={this.props.database}
                   actions={this.props.actions}
@@ -246,6 +247,7 @@ export default class ResultSet extends React.PureComponent<
                 {t('Query in a new tab')}
               </Button>
               <ExploreCtasResultsButton
+                // @ts-ignore Redux types are difficult to work with, ignoring 
for now
                 table={tempTable}
                 schema={tempSchema}
                 dbId={exploreDBId}
diff --git a/superset-frontend/src/messageToasts/enhancers/withToasts.tsx 
b/superset-frontend/src/messageToasts/enhancers/withToasts.tsx
index 5e41152..0a23134 100644
--- a/superset-frontend/src/messageToasts/enhancers/withToasts.tsx
+++ b/superset-frontend/src/messageToasts/enhancers/withToasts.tsx
@@ -29,7 +29,7 @@ import {
 } from '../actions';
 
 // To work properly the redux state must have a `messageToasts` subtree
-export default function withToasts(BaseComponent: ComponentType) {
+export default function withToasts(BaseComponent: ComponentType<any>) {
   return connect(null, dispatch =>
     bindActionCreators(
       {
@@ -41,6 +41,6 @@ export default function withToasts(BaseComponent: 
ComponentType) {
       dispatch,
     ),
   )(BaseComponent) as any;
-  // Rsedux has some confusing typings that cause problems for consumers of 
this function.
+  // Redux has some confusing typings that cause problems for consumers of 
this function.
   // If someone can fix the types, great, but for now it's just any.
 }
diff --git a/superset-frontend/src/profile/App.jsx 
b/superset-frontend/src/profile/App.tsx
similarity index 96%
rename from superset-frontend/src/profile/App.jsx
rename to superset-frontend/src/profile/App.tsx
index 3e870c9..ea9932b 100644
--- a/superset-frontend/src/profile/App.jsx
+++ b/superset-frontend/src/profile/App.tsx
@@ -33,7 +33,7 @@ setupApp();
 
 const profileViewContainer = document.getElementById('app');
 const bootstrap = JSON.parse(
-  profileViewContainer.getAttribute('data-bootstrap'),
+  profileViewContainer?.getAttribute('data-bootstrap') ?? '{}',
 );
 
 const store = createStore(
diff --git a/superset-frontend/src/profile/components/App.jsx 
b/superset-frontend/src/profile/components/App.tsx
similarity index 86%
rename from superset-frontend/src/profile/components/App.jsx
rename to superset-frontend/src/profile/components/App.tsx
index 457a46e..bde67ac 100644
--- a/superset-frontend/src/profile/components/App.jsx
+++ b/superset-frontend/src/profile/components/App.tsx
@@ -17,7 +17,6 @@
  * under the License.
  */
 import React from 'react';
-import PropTypes from 'prop-types';
 import { Col, Row, Tabs, Tab, Panel } from 'react-bootstrap';
 import { t } from '@superset-ui/translation';
 
@@ -26,17 +25,18 @@ import UserInfo from './UserInfo';
 import Security from './Security';
 import RecentActivity from './RecentActivity';
 import CreatedContent from './CreatedContent';
+import { User } from '../types';
 
-const propTypes = {
-  user: PropTypes.object.isRequired,
-};
+interface AppProps {
+  user: User;
+}
 
-export default function App(props) {
+export default function App({ user }: AppProps) {
   return (
     <div className="container app">
       <Row>
         <Col md={3}>
-          <UserInfo user={props.user} />
+          <UserInfo user={user} />
         </Col>
         <Col md={9}>
           <Tabs id="options">
@@ -50,7 +50,7 @@ export default function App(props) {
             >
               <Panel>
                 <Panel.Body>
-                  <Favorites user={props.user} />
+                  <Favorites user={user} />
                 </Panel.Body>
               </Panel>
             </Tab>
@@ -64,7 +64,7 @@ export default function App(props) {
             >
               <Panel>
                 <Panel.Body>
-                  <CreatedContent user={props.user} />
+                  <CreatedContent user={user} />
                 </Panel.Body>
               </Panel>
             </Tab>
@@ -78,7 +78,7 @@ export default function App(props) {
             >
               <Panel>
                 <Panel.Body>
-                  <RecentActivity user={props.user} />
+                  <RecentActivity user={user} />
                 </Panel.Body>
               </Panel>
             </Tab>
@@ -92,7 +92,7 @@ export default function App(props) {
             >
               <Panel>
                 <Panel.Body>
-                  <Security user={props.user} />
+                  <Security user={user} />
                 </Panel.Body>
               </Panel>
             </Tab>
@@ -102,4 +102,3 @@ export default function App(props) {
     </div>
   );
 }
-App.propTypes = propTypes;
diff --git a/superset-frontend/src/profile/components/CreatedContent.jsx 
b/superset-frontend/src/profile/components/CreatedContent.tsx
similarity index 84%
rename from superset-frontend/src/profile/components/CreatedContent.jsx
rename to superset-frontend/src/profile/components/CreatedContent.tsx
index 34cfdd2..9831e4d 100644
--- a/superset-frontend/src/profile/components/CreatedContent.jsx
+++ b/superset-frontend/src/profile/components/CreatedContent.tsx
@@ -17,28 +17,19 @@
  * under the License.
  */
 import React from 'react';
-import PropTypes from 'prop-types';
 import moment from 'moment';
 import { t } from '@superset-ui/translation';
 
 import TableLoader from '../../components/TableLoader';
+import { User, Dashboard, Slice } from '../types';
 
-const propTypes = {
-  user: PropTypes.object.isRequired,
-};
+interface CreatedContentProps {
+  user: User;
+}
 
-class CreatedContent extends React.PureComponent {
-  constructor(props) {
-    super(props);
-    this.state = {
-      dashboardsLoading: true,
-      slicesLoading: true,
-      dashboards: [],
-      slices: [],
-    };
-  }
+class CreatedContent extends React.PureComponent<CreatedContentProps> {
   renderSliceTable() {
-    const mutator = data =>
+    const mutator = (data: Slice[]) =>
       data.map(slice => ({
         slice: <a href={slice.url}>{slice.title}</a>,
         favorited: moment.utc(slice.dttm).fromNow(),
@@ -56,7 +47,7 @@ class CreatedContent extends React.PureComponent {
     );
   }
   renderDashboardTable() {
-    const mutator = data =>
+    const mutator = (data: Dashboard[]) =>
       data.map(dash => ({
         dashboard: <a href={dash.url}>{dash.title}</a>,
         favorited: moment.utc(dash.dttm).fromNow(),
@@ -85,6 +76,5 @@ class CreatedContent extends React.PureComponent {
     );
   }
 }
-CreatedContent.propTypes = propTypes;
 
 export default CreatedContent;
diff --git a/superset-frontend/src/profile/components/Favorites.jsx 
b/superset-frontend/src/profile/components/Favorites.tsx
similarity index 85%
rename from superset-frontend/src/profile/components/Favorites.jsx
rename to superset-frontend/src/profile/components/Favorites.tsx
index 578e763..89b4cc4 100644
--- a/superset-frontend/src/profile/components/Favorites.jsx
+++ b/superset-frontend/src/profile/components/Favorites.tsx
@@ -17,28 +17,19 @@
  * under the License.
  */
 import React from 'react';
-import PropTypes from 'prop-types';
 import moment from 'moment';
 import { t } from '@superset-ui/translation';
 
 import TableLoader from '../../components/TableLoader';
+import { User, Dashboard, Slice } from '../types';
 
-const propTypes = {
-  user: PropTypes.object.isRequired,
-};
+interface FavoritesProps {
+  user: User;
+}
 
-export default class Favorites extends React.PureComponent {
-  constructor(props) {
-    super(props);
-    this.state = {
-      dashboardsLoading: true,
-      slicesLoading: true,
-      dashboards: [],
-      slices: [],
-    };
-  }
+export default class Favorites extends React.PureComponent<FavoritesProps> {
   renderSliceTable() {
-    const mutator = data =>
+    const mutator = (data: Slice[]) =>
       data.map(slice => ({
         slice: <a href={slice.url}>{slice.title}</a>,
         creator: <a href={slice.creator_url}>{slice.creator}</a>,
@@ -57,7 +48,7 @@ export default class Favorites extends React.PureComponent {
     );
   }
   renderDashboardTable() {
-    const mutator = data =>
+    const mutator = (data: Dashboard[]) =>
       data.map(dash => ({
         dashboard: <a href={dash.url}>{dash.title}</a>,
         creator: <a href={dash.creator_url}>{dash.creator}</a>,
@@ -86,4 +77,3 @@ export default class Favorites extends React.PureComponent {
     );
   }
 }
-Favorites.propTypes = propTypes;
diff --git a/superset-frontend/src/profile/components/RecentActivity.jsx 
b/superset-frontend/src/profile/components/RecentActivity.tsx
similarity index 52%
rename from superset-frontend/src/profile/components/RecentActivity.jsx
rename to superset-frontend/src/profile/components/RecentActivity.tsx
index e58fb13..34f419c 100644
--- a/superset-frontend/src/profile/components/RecentActivity.jsx
+++ b/superset-frontend/src/profile/components/RecentActivity.tsx
@@ -17,39 +17,35 @@
  * under the License.
  */
 import React from 'react';
-import PropTypes from 'prop-types';
 import moment from 'moment';
 
 import TableLoader from '../../components/TableLoader';
+import { User, Activity } from '../types';
 
-const propTypes = {
-  user: PropTypes.object,
-};
-
-export default class RecentActivity extends React.PureComponent {
-  render() {
-    const rowLimit = 50;
-    const mutator = function (data) {
-      return data
-        .filter(row => row.action === 'dashboard' || row.action === 'explore')
-        .map(row => ({
-          name: <a href={row.item_url}>{row.item_title}</a>,
-          type: row.action,
-          time: moment.utc(row.time).fromNow(),
-          _time: row.time,
-        }));
-    };
-    return (
-      <div>
-        <TableLoader
-          className="table table-condensed"
-          mutator={mutator}
-          sortable
-          
dataEndpoint={`/superset/recent_activity/${this.props.user.userId}/?limit=${rowLimit}`}
-        />
-      </div>
-    );
-  }
+interface RecentActivityProps {
+  user: User;
 }
 
-RecentActivity.propTypes = propTypes;
+export default function RecentActivity({ user }: RecentActivityProps) {
+  const rowLimit = 50;
+  const mutator = function (data: Activity[]) {
+    return data
+      .filter(row => row.action === 'dashboard' || row.action === 'explore')
+      .map(row => ({
+        name: <a href={row.item_url}>{row.item_title}</a>,
+        type: row.action,
+        time: moment.utc(row.time).fromNow(),
+        _time: row.time,
+      }));
+  };
+  return (
+    <div>
+      <TableLoader
+        className="table table-condensed"
+        mutator={mutator}
+        sortable
+        
dataEndpoint={`/superset/recent_activity/${user.userId}/?limit=${rowLimit}`}
+      />
+    </div>
+  );
+}
diff --git a/superset-frontend/src/profile/components/Security.jsx 
b/superset-frontend/src/profile/components/Security.tsx
similarity index 92%
rename from superset-frontend/src/profile/components/Security.jsx
rename to superset-frontend/src/profile/components/Security.tsx
index 5f3d03a..91b0284 100644
--- a/superset-frontend/src/profile/components/Security.jsx
+++ b/superset-frontend/src/profile/components/Security.tsx
@@ -17,14 +17,15 @@
  * under the License.
  */
 import React from 'react';
-import PropTypes from 'prop-types';
 import { Badge, Label } from 'react-bootstrap';
 import { t } from '@superset-ui/translation';
+import { User } from '../types';
 
-const propTypes = {
-  user: PropTypes.object.isRequired,
-};
-export default function Security({ user }) {
+interface SecurityProps {
+  user: User;
+}
+
+export default function Security({ user }: SecurityProps) {
   return (
     <div>
       <div className="roles">
@@ -66,4 +67,3 @@ export default function Security({ user }) {
     </div>
   );
 }
-Security.propTypes = propTypes;
diff --git a/superset-frontend/src/profile/components/UserInfo.jsx 
b/superset-frontend/src/profile/components/UserInfo.jsx
deleted file mode 100644
index 1c6adca..0000000
--- a/superset-frontend/src/profile/components/UserInfo.jsx
+++ /dev/null
@@ -1,75 +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.
- */
-import React from 'react';
-import PropTypes from 'prop-types';
-import Gravatar from 'react-gravatar';
-import moment from 'moment';
-import { Panel } from 'react-bootstrap';
-import { t } from '@superset-ui/translation';
-
-const propTypes = {
-  user: PropTypes.object.isRequired,
-};
-const UserInfo = ({ user }) => (
-  <div>
-    <a href="https://en.gravatar.com/";>
-      <Gravatar
-        email={user.email}
-        width="100%"
-        height=""
-        size={220}
-        alt={t('Profile picture provided by Gravatar')}
-        className="img-rounded"
-        style={{ borderRadius: 15 }}
-      />
-    </a>
-    <hr />
-    <Panel>
-      <Panel.Body>
-        <h3>
-          <strong>
-            {user.firstName} {user.lastName}
-          </strong>
-        </h3>
-        <h4 className="username">
-          <i className="fa fa-user-o" /> {user.username}
-        </h4>
-        <hr />
-        <p>
-          <i className="fa fa-clock-o" /> {t('joined')}{' '}
-          {moment(user.createdOn, 'YYYYMMDD').fromNow()}
-        </p>
-        <p className="email">
-          <i className="fa fa-envelope-o" /> {user.email}
-        </p>
-        <p className="roles">
-          <i className="fa fa-lock" /> {Object.keys(user.roles).join(', ')}
-        </p>
-        <p>
-          <i className="fa fa-key" />
-          &nbsp;
-          <span className="text-muted">{t('id:')}</span>&nbsp;
-          <span className="user-id">{user.userId}</span>
-        </p>
-      </Panel.Body>
-    </Panel>
-  </div>
-);
-UserInfo.propTypes = propTypes;
-export default UserInfo;
diff --git a/superset-frontend/src/profile/components/UserInfo.tsx 
b/superset-frontend/src/profile/components/UserInfo.tsx
new file mode 100644
index 0000000..ccc1c89
--- /dev/null
+++ b/superset-frontend/src/profile/components/UserInfo.tsx
@@ -0,0 +1,76 @@
+/**
+ * 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.
+ */
+import React from 'react';
+import Gravatar from 'react-gravatar';
+import moment from 'moment';
+import { Panel } from 'react-bootstrap';
+import { t } from '@superset-ui/translation';
+import { User } from '../types';
+
+interface UserInfoProps {
+  user: User;
+}
+
+export default function UserInfo({ user }: UserInfoProps) {
+  return (
+    <div>
+      <a href="https://en.gravatar.com/";>
+        <Gravatar
+          email={user.email}
+          width="100%"
+          height=""
+          size={220}
+          alt={t('Profile picture provided by Gravatar')}
+          className="img-rounded"
+          style={{ borderRadius: 15 }}
+        />
+      </a>
+      <hr />
+      <Panel>
+        <Panel.Body>
+          <h3>
+            <strong>
+              {user.firstName} {user.lastName}
+            </strong>
+          </h3>
+          <h4 className="username">
+            <i className="fa fa-user-o" /> {user.username}
+          </h4>
+          <hr />
+          <p>
+            <i className="fa fa-clock-o" /> {t('joined')}{' '}
+            {moment(user.createdOn, 'YYYYMMDD').fromNow()}
+          </p>
+          <p className="email">
+            <i className="fa fa-envelope-o" /> {user.email}
+          </p>
+          <p className="roles">
+            <i className="fa fa-lock" /> {Object.keys(user.roles).join(', ')}
+          </p>
+          <p>
+            <i className="fa fa-key" />
+            &nbsp;
+            <span className="text-muted">{t('id:')}</span>&nbsp;
+            <span className="user-id">{user.userId}</span>
+          </p>
+        </Panel.Body>
+      </Panel>
+    </div>
+  );
+}
diff --git a/superset-frontend/src/profile/index.jsx 
b/superset-frontend/src/profile/index.tsx
similarity index 100%
rename from superset-frontend/src/profile/index.jsx
rename to superset-frontend/src/profile/index.tsx
diff --git a/superset-frontend/spec/javascripts/profile/RecentActivity_spec.jsx 
b/superset-frontend/src/profile/types.ts
similarity index 55%
rename from superset-frontend/spec/javascripts/profile/RecentActivity_spec.jsx
rename to superset-frontend/src/profile/types.ts
index 3399436..4e5a7eb 100644
--- a/superset-frontend/spec/javascripts/profile/RecentActivity_spec.jsx
+++ b/superset-frontend/src/profile/types.ts
@@ -16,25 +16,43 @@
  * specific language governing permissions and limitations
  * under the License.
  */
-import React from 'react';
-import { shallow } from 'enzyme';
-import RecentActivity from 'src/profile/components/RecentActivity';
-import TableLoader from 'src/components/TableLoader';
+export type User = {
+  createdOn: string;
+  email: string;
+  firstName: string;
+  isActive: boolean;
+  lastName: string;
+  permissions: {
+    database_access?: string[];
+    datasource_access?: string[];
+  };
+  roles: Record<string, any>;
+  userId: number;
+  username: string;
+};
 
-import { user } from './fixtures';
+export type Slice = {
+  dttm: number;
+  id: number;
+  url: string;
+  title: string;
+  creator?: string;
+  creator_url?: string;
+  viz_type: string;
+};
 
-describe('RecentActivity', () => {
-  const mockedProps = {
-    user,
-  };
-  it('is valid', () => {
-    expect(React.isValidElement(<RecentActivity {...mockedProps} />)).toBe(
-      true,
-    );
-  });
+export type Dashboard = {
+  dttm: number;
+  id: number;
+  url: string;
+  title: string;
+  creator?: string;
+  creator_url?: string;
+};
 
-  it('renders a TableLoader', () => {
-    const wrapper = shallow(<RecentActivity {...mockedProps} />);
-    expect(wrapper.find(TableLoader)).toHaveLength(1);
-  });
-});
+export type Activity = {
+  action: string;
+  item_title: string;
+  item_url: string;
+  time: number;
+};
diff --git a/superset-frontend/webpack.config.js 
b/superset-frontend/webpack.config.js
index f0c95ea..d436a3a 100644
--- a/superset-frontend/webpack.config.js
+++ b/superset-frontend/webpack.config.js
@@ -182,7 +182,7 @@ const config = {
     dashboard: addPreamble('/src/dashboard/index.jsx'),
     sqllab: addPreamble('/src/SqlLab/index.jsx'),
     welcome: addPreamble('/src/welcome/index.jsx'),
-    profile: addPreamble('/src/profile/index.jsx'),
+    profile: addPreamble('/src/profile/index.tsx'),
     showSavedQuery: [path.join(APP_DIR, '/src/showSavedQuery/index.jsx')],
   },
   output,

Reply via email to