Browse Source

ref(ts): Refactor Sparklines to ts and use `React.Suspense` to load (#14494)

Billy Vong 5 years ago
parent
commit
49f9373632

+ 1 - 0
package.json

@@ -32,6 +32,7 @@
     "@types/react-document-title": "^2.0.3",
     "@types/react-dom": "^16.7.0",
     "@types/react-router": "^3.0.20",
+    "@types/react-sparklines": "^1.7.0",
     "@types/react-virtualized": "^9.20.1",
     "algoliasearch": "^3.32.0",
     "babel-core": "^7.0.0-bridge.0",

+ 28 - 0
src/sentry/static/sentry/app/components/sparklines/index.tsx

@@ -0,0 +1,28 @@
+import {Sparklines} from 'react-sparklines';
+import PropTypes from 'prop-types';
+
+/**
+ * This is required because:
+ *
+ * - React.Suspense only works with default exports
+ * - typescript complains that the library's `propTypes` does not
+ * have `children defined.
+ * - typescript also won't let us access `Sparklines.propTypes`
+ */
+export default class SparklinesWithCustomPropTypes extends Sparklines {
+  static propTypes = {
+    children: PropTypes.node,
+    data: PropTypes.array,
+    limit: PropTypes.number,
+    width: PropTypes.number,
+    height: PropTypes.number,
+    svgWidth: PropTypes.number,
+    svgHeight: PropTypes.number,
+    preserveAspectRatio: PropTypes.string,
+    margin: PropTypes.number,
+    style: PropTypes.object,
+    min: PropTypes.number,
+    max: PropTypes.number,
+    onMouseMove: PropTypes.func,
+  };
+}

+ 3 - 0
src/sentry/static/sentry/app/components/sparklines/line.tsx

@@ -0,0 +1,3 @@
+// Need to re-export this as default because React.Suspense does not support
+// named exports, only default
+export {SparklinesLine as default} from 'react-sparklines';

+ 0 - 75
src/sentry/static/sentry/app/views/incidents/list/sparkLine.jsx

@@ -1,75 +0,0 @@
-import React from 'react';
-import styled from 'react-emotion';
-
-import Placeholder from 'app/components/placeholder';
-import SentryTypes from 'app/sentryTypes';
-import theme from 'app/utils/theme';
-
-// Height of sparkline
-const SPARKLINE_HEIGHT = 38;
-
-class SparkLine extends React.Component {
-  static propTypes = {
-    incident: SentryTypes.Incident.isRequired,
-  };
-
-  state = {
-    loading: true,
-    error: null,
-  };
-
-  componentDidMount() {
-    this.loadLibrary();
-  }
-
-  async loadLibrary() {
-    this.setState({loading: true});
-
-    try {
-      const reactSparkLines = await import(/* webpackChunkName: "ReactSparkLines" */ 'react-sparklines');
-
-      this.setState({
-        loading: false,
-        Sparklines: reactSparkLines.Sparklines,
-        SparklinesLine: reactSparkLines.SparklinesLine,
-        error: false,
-      });
-    } catch (error) {
-      this.setState({loading: false, error});
-    }
-  }
-
-  render() {
-    const {className, incident} = this.props;
-    const {Sparklines, SparklinesLine, loading} = this.state;
-
-    if (loading) {
-      return <SparkLinePlaceholder />;
-    }
-
-    const data = incident.eventStats.data.map(([, value]) =>
-      value && value.length ? value[0].count || 0 : 0
-    );
-
-    return (
-      <div className={className}>
-        <Sparklines data={data} width={100} height={32}>
-          <SparklinesLine style={{stroke: theme.gray2, fill: 'none', strokeWidth: 2}} />
-        </Sparklines>
-      </div>
-    );
-  }
-}
-
-const StyledSparkLine = styled(SparkLine)`
-  flex-shrink: 0;
-  width: 120px;
-  height: ${SPARKLINE_HEIGHT}px;
-`;
-
-const SparkLinePlaceholder = styled(Placeholder)`
-  background-color: transparent;
-  height: ${SPARKLINE_HEIGHT}px;
-`;
-
-export default StyledSparkLine;

+ 59 - 0
src/sentry/static/sentry/app/views/incidents/list/sparkLine.tsx

@@ -0,0 +1,59 @@
+import React from 'react';
+import styled from 'react-emotion';
+
+import Placeholder from 'app/components/placeholder';
+import SentryTypes from 'app/sentryTypes';
+import theme from 'app/utils/theme';
+import {Incident} from 'app/views/incidents/types';
+
+// Height of sparkline
+const SPARKLINE_HEIGHT = 38;
+
+type Props = {
+  className?: string;
+  incident: Incident;
+};
+
+const Sparklines = React.lazy(() =>
+  import(/* webpackChunkName: "Sparklines" */ 'app/components/sparklines')
+);
+const SparklinesLine = React.lazy(() =>
+  import(/* webpackChunkName: "SparklinesLine" */ 'app/components/sparklines/line')
+);
+
+class SparkLine extends React.Component<Props> {
+  static propTypes = {
+    incident: SentryTypes.Incident.isRequired,
+  };
+
+  render() {
+    const {className, incident} = this.props;
+
+    const data = incident.eventStats.data.map(([, value]) =>
+      value && Array.isArray(value) && value.length ? value[0].count || 0 : 0
+    );
+
+    return (
+      <React.Suspense fallback={<SparkLinePlaceholder />}>
+        <div className={className}>
+          <Sparklines data={data} width={100} height={32}>
+            <SparklinesLine style={{stroke: theme.gray2, fill: 'none', strokeWidth: 2}} />
+          </Sparklines>
+        </div>
+      </React.Suspense>
+    );
+  }
+}
+
+const StyledSparkLine = styled(SparkLine)`
+  flex-shrink: 0;
+  width: 120px;
+  height: ${SPARKLINE_HEIGHT}px;
+`;
+
+const SparkLinePlaceholder = styled(Placeholder)`
+  background-color: transparent;
+  height: ${SPARKLINE_HEIGHT}px;
+`;
+
+export default StyledSparkLine;

+ 7 - 0
yarn.lock

@@ -2182,6 +2182,13 @@
     "@types/history" "^3"
     "@types/react" "*"
 
+"@types/react-sparklines@^1.7.0":
+  version "1.7.0"
+  resolved "https://registry.yarnpkg.com/@types/react-sparklines/-/react-sparklines-1.7.0.tgz#f956d0f7b0e746ad445ce1cd250fe81f8a384684"
+  integrity sha512-Vd+cME7+Yy3kFNhnid9EBIKiyCQ/at8nqDczIs0UYfIB8AtaRJPqekigv02biOsIbQCvxyvIAIjiTKOC+hHNbA==
+  dependencies:
+    "@types/react" "*"
+
 "@types/react-virtualized@^9.20.1":
   version "9.21.2"
   resolved "https://registry.yarnpkg.com/@types/react-virtualized/-/react-virtualized-9.21.2.tgz#c5e4293409593814c35466913e83fb856e2053d0"