]> source.dussan.org Git - sonarqube.git/commitdiff
SONAR-9841 display tooltip on language distribution chart
authorStas Vilchik <stas.vilchik@sonarsource.com>
Tue, 19 Sep 2017 14:53:16 +0000 (16:53 +0200)
committerStas Vilchik <stas.vilchik@sonarsource.com>
Mon, 25 Sep 2017 08:17:18 +0000 (10:17 +0200)
13 files changed:
server/sonar-web/package.json
server/sonar-web/src/main/js/apps/component-measures/components/MeasureHeader.js
server/sonar-web/src/main/js/apps/overview/meta/MetaSize.js
server/sonar-web/src/main/js/apps/portfolio/components/Summary.tsx
server/sonar-web/src/main/js/apps/portfolio/components/__tests__/__snapshots__/Summary-test.tsx.snap
server/sonar-web/src/main/js/components/charts/Histogram.tsx [new file with mode: 0644]
server/sonar-web/src/main/js/components/charts/LanguageDistribution.tsx
server/sonar-web/src/main/js/components/charts/__tests__/Histogram-test.tsx [new file with mode: 0644]
server/sonar-web/src/main/js/components/charts/__tests__/LanguageDistribution-test.tsx [new file with mode: 0644]
server/sonar-web/src/main/js/components/charts/__tests__/__snapshots__/Histogram-test.tsx.snap [new file with mode: 0644]
server/sonar-web/src/main/js/components/charts/__tests__/__snapshots__/LanguageDistribution-test.tsx.snap [new file with mode: 0644]
server/sonar-web/src/main/js/components/charts/histogram.js [deleted file]
server/sonar-web/yarn.lock

index aecf565d6c172fd0b8c06e2125da2ee06e4533b8..4c8d13681cb13ed1dea1fef764b9d363669ca9c6 100644 (file)
@@ -46,6 +46,8 @@
   },
   "devDependencies": {
     "@types/classnames": "2.2.0",
+    "@types/d3-array": "1.2.1",
+    "@types/d3-scale": "1.0.10",
     "@types/date-fns": "2.6.0",
     "@types/enzyme": "2.8.6",
     "@types/escape-html": "0.0.19",
index de343e95337ea8c16ad233903a437a946c711339..49f039c35d112cc20a1ed178f6a0fbc5a7251374 100644 (file)
@@ -140,6 +140,7 @@ export default class MeasureHeader extends React.PureComponent {
             <LanguageDistributionContainer
               alignTicks={true}
               distribution={secondaryMeasure.value}
+              width={260}
             />
           </div>
         )}
index a488f469da6fae05128cd0ffdcfa5242ea43ae97..7cd41c45753f0ca39504ae1c44efd89eb1e081ed 100644 (file)
@@ -57,7 +57,7 @@ export default class MetaSize extends React.PureComponent {
 
     return languageDistribution ? (
       <div id="overview-language-distribution" className="overview-meta-size-lang-dist">
-        <LanguageDistributionContainer distribution={languageDistribution.value} />
+        <LanguageDistributionContainer distribution={languageDistribution.value} width={160} />
       </div>
     ) : null;
   };
index aa229597a1ee7dd0c6ad93bb29e8f8d6afea5eda..5aecf73d70ff29f5f03a85fb39091ebcfb34f70a 100644 (file)
@@ -60,8 +60,8 @@ export default function Summary({ component, measures }: Props) {
       </ul>
 
       {nclocDistribution && (
-        <div className="huge-spacer-top" style={{ width: 260 }}>
-          <LanguageDistributionContainer distribution={nclocDistribution} />
+        <div className="huge-spacer-top">
+          <LanguageDistributionContainer distribution={nclocDistribution} width={260} />
         </div>
       )}
     </section>
index d377a6a44a9ca8cf1c89ee509928e16f9d8ac541..db159a6947aeeb7695a3704df9919b884752d6b7 100644 (file)
@@ -82,14 +82,10 @@ exports[`renders 1`] = `
   </ul>
   <div
     className="huge-spacer-top"
-    style={
-      Object {
-        "width": 260,
-      }
-    }
   >
     <Connect(LanguageDistribution)
       distribution="java=13;js=17"
+      width={260}
     />
   </div>
 </section>
diff --git a/server/sonar-web/src/main/js/components/charts/Histogram.tsx b/server/sonar-web/src/main/js/components/charts/Histogram.tsx
new file mode 100644 (file)
index 0000000..420de69
--- /dev/null
@@ -0,0 +1,146 @@
+/*
+ * SonarQube
+ * Copyright (C) 2009-2017 SonarSource SA
+ * mailto:info AT sonarsource DOT com
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 3 of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with this program; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.
+ */
+import * as React from 'react';
+import { max } from 'd3-array';
+import { scaleLinear, scaleBand, ScaleLinear, ScaleBand } from 'd3-scale';
+import Tooltip from '../controls/Tooltip';
+
+interface Props {
+  alignTicks?: boolean;
+  bars: number[];
+  height: number;
+  padding?: [number, number, number, number];
+  yTicks?: string[];
+  yTooltips?: string[];
+  yValues?: string[];
+  width: number;
+}
+
+const BAR_HEIGHT = 10;
+const DEFAULT_PADDING = [10, 10, 10, 10];
+
+type XScale = ScaleLinear<number, number>;
+type YScale = ScaleBand<number>;
+
+export default class Histogram extends React.PureComponent<Props> {
+  wrapWithTooltip(element: React.ReactNode, index: number) {
+    const tooltip = this.props.yTooltips && this.props.yTooltips[index];
+    return tooltip ? (
+      <Tooltip key={index} overlay={tooltip} placement="top">
+        {element}
+      </Tooltip>
+    ) : (
+      element
+    );
+  }
+
+  renderBar(d: number, index: number, xScale: XScale, yScale: YScale) {
+    const { alignTicks, padding = DEFAULT_PADDING } = this.props;
+
+    const width = Math.round(xScale(d)) + /* minimum bar width */ 1;
+    const x = xScale.range()[0] + (alignTicks ? padding[3] : 0);
+    const y = Math.round(yScale(index)! + yScale.bandwidth() / 2);
+
+    return <rect className="bar-chart-bar" x={x} y={y} width={width} height={BAR_HEIGHT} />;
+  }
+
+  renderValue(d: number, index: number, xScale: XScale, yScale: YScale) {
+    const { alignTicks, padding = DEFAULT_PADDING, yValues } = this.props;
+
+    const value = yValues && yValues[index];
+
+    if (!value) {
+      return null;
+    }
+
+    const x = xScale(d) + (alignTicks ? padding[3] : 0);
+    const y = Math.round(yScale(index)! + yScale.bandwidth() / 2 + BAR_HEIGHT / 2);
+
+    return this.wrapWithTooltip(
+      <text className="bar-chart-tick histogram-value" x={x} y={y} dx="1em" dy="0.3em">
+        {value}
+      </text>,
+      index
+    );
+  }
+
+  renderTick(index: number, xScale: XScale, yScale: YScale) {
+    const { alignTicks, yTicks } = this.props;
+
+    const tick = yTicks && yTicks[index];
+
+    if (!tick) {
+      return null;
+    }
+
+    const x = xScale.range()[0];
+    const y = Math.round(yScale(index)! + yScale.bandwidth() / 2 + BAR_HEIGHT / 2);
+    const historyTickClass = alignTicks ? 'histogram-tick-start' : 'histogram-tick';
+
+    return (
+      <text
+        className={'bar-chart-tick ' + historyTickClass}
+        x={x}
+        y={y}
+        dx={alignTicks ? 0 : '-1em'}
+        dy="0.3em">
+        {tick}
+      </text>
+    );
+  }
+
+  renderBars(xScale: XScale, yScale: YScale) {
+    return (
+      <g>
+        {this.props.bars.map((d, index) => {
+          return (
+            <g key={index}>
+              {this.renderBar(d, index, xScale, yScale)}
+              {this.renderValue(d, index, xScale, yScale)}
+              {this.renderTick(index, xScale, yScale)}
+            </g>
+          );
+        })}
+      </g>
+    );
+  }
+
+  render() {
+    const { bars, width, height, padding = DEFAULT_PADDING } = this.props;
+
+    const availableWidth = width - padding[1] - padding[3];
+    const xScale: XScale = scaleLinear()
+      .domain([0, max(bars)!])
+      .range([0, availableWidth]);
+
+    const availableHeight = height - padding[0] - padding[2];
+    const yScale: YScale = scaleBand<number>()
+      .domain(bars.map((_, index) => index))
+      .rangeRound([0, availableHeight]);
+
+    return (
+      <svg className="bar-chart" width={this.props.width} height={this.props.height}>
+        <g transform={`translate(${this.props.alignTicks ? 4 : padding[3]}, ${padding[0]})`}>
+          {this.renderBars(xScale, yScale)}
+        </g>
+      </svg>
+    );
+  }
+}
index 6766372207f8535c45bd81e8cc6575fb30526279..d3f769728e6bb9754a95adf6d3b87eb0557e970d 100644 (file)
@@ -19,7 +19,7 @@
  */
 import * as React from 'react';
 import { find, sortBy } from 'lodash';
-import { Histogram } from './histogram';
+import Histogram from './Histogram';
 import { formatMeasure } from '../../helpers/measures';
 import { Language } from '../../api/languages';
 import { translate } from '../../helpers/l10n';
@@ -28,37 +28,44 @@ interface Props {
   alignTicks?: boolean;
   distribution: string;
   languages?: Language[];
+  width: number;
 }
 
 export default function LanguageDistribution(props: Props) {
-  let data = props.distribution.split(';').map((point, index) => {
+  let distribution = props.distribution.split(';').map(point => {
     const tokens = point.split('=');
-    return { x: parseInt(tokens[1], 10), y: index, value: tokens[0] };
+    return { language: tokens[0], lines: parseInt(tokens[1], 10) };
   });
 
-  data = sortBy(data, d => -d.x);
+  distribution = sortBy(distribution, d => -d.lines);
 
-  const yTicks = data.map(point => getLanguageName(point.value)).map(cutLanguageName);
-  const yValues = data.map(point => formatMeasure(point.x, 'SHORT_INT'));
+  const data = distribution.map(d => d.lines);
+  const yTicks = distribution.map(d => getLanguageName(d.language)).map(cutLanguageName);
+  const yTooltips = distribution.map(d => (d.lines > 1000 ? formatMeasure(d.lines, 'INT') : ''));
+  const yValues = distribution.map(d => formatMeasure(d.lines, 'SHORT_INT'));
 
   return (
     <Histogram
       alignTicks={props.alignTicks}
-      data={data}
+      bars={data}
+      height={distribution.length * 25}
+      padding={[0, 60, 0, 80]}
       yTicks={yTicks}
+      yTooltips={yTooltips}
       yValues={yValues}
-      barsWidth={10}
-      height={data.length * 25}
-      padding={[0, 60, 0, 80]}
+      width={props.width}
     />
   );
 
   function getLanguageName(langKey: string) {
+    if (langKey === '<null>') {
+      return translate('unknown');
+    }
     const lang = find(props.languages, { key: langKey });
-    return lang ? lang.name : translate('unknown');
+    return lang ? lang.name : langKey;
   }
+}
 
-  function cutLanguageName(name: string) {
-    return name.length > 10 ? `${name.substr(0, 7)}...` : name;
-  }
+function cutLanguageName(name: string) {
+  return name.length > 10 ? `${name.substr(0, 7)}...` : name;
 }
diff --git a/server/sonar-web/src/main/js/components/charts/__tests__/Histogram-test.tsx b/server/sonar-web/src/main/js/components/charts/__tests__/Histogram-test.tsx
new file mode 100644 (file)
index 0000000..9db4b13
--- /dev/null
@@ -0,0 +1,68 @@
+/*
+ * SonarQube
+ * Copyright (C) 2009-2016 SonarSource SA
+ * mailto:contact AT sonarsource DOT com
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 3 of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with this program; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.
+ */
+import * as React from 'react';
+import { shallow } from 'enzyme';
+import Histogram from '../Histogram';
+
+it('renders', () => {
+  expect(shallow(<Histogram bars={[100, 75, 150]} height={75} width={100} />)).toMatchSnapshot();
+});
+
+it('renders with yValues', () => {
+  expect(
+    shallow(
+      <Histogram
+        bars={[100, 75, 150]}
+        height={75}
+        yValues={['100.0', '75.0', '150.0']}
+        width={100}
+      />
+    )
+  ).toMatchSnapshot();
+});
+
+it('renders with yValues and yTicks', () => {
+  expect(
+    shallow(
+      <Histogram
+        bars={[100, 75, 150]}
+        height={75}
+        yTicks={['a', 'b', 'c']}
+        yValues={['100.0', '75.0', '150.0']}
+        width={100}
+      />
+    )
+  ).toMatchSnapshot();
+});
+
+it('renders with yValues, yTicks and yTooltips', () => {
+  expect(
+    shallow(
+      <Histogram
+        bars={[100, 75, 150]}
+        height={75}
+        yTicks={['a', 'b', 'c']}
+        yTooltips={['a - 100', 'b - 75', 'c - 150']}
+        yValues={['100.0', '75.0', '150.0']}
+        width={100}
+      />
+    )
+  ).toMatchSnapshot();
+});
diff --git a/server/sonar-web/src/main/js/components/charts/__tests__/LanguageDistribution-test.tsx b/server/sonar-web/src/main/js/components/charts/__tests__/LanguageDistribution-test.tsx
new file mode 100644 (file)
index 0000000..8bed794
--- /dev/null
@@ -0,0 +1,34 @@
+/*
+ * SonarQube
+ * Copyright (C) 2009-2016 SonarSource SA
+ * mailto:contact AT sonarsource DOT com
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 3 of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with this program; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.
+ */
+import * as React from 'react';
+import { shallow } from 'enzyme';
+import LanguageDistribution from '../LanguageDistribution';
+
+it('renders', () => {
+  expect(
+    shallow(
+      <LanguageDistribution
+        distribution="java=1734;js=845;cpp=73;<null>=15"
+        languages={[{ key: 'java', name: 'Java' }, { key: 'js', name: 'JavaScript' }]}
+        width={100}
+      />
+    )
+  ).toMatchSnapshot();
+});
diff --git a/server/sonar-web/src/main/js/components/charts/__tests__/__snapshots__/Histogram-test.tsx.snap b/server/sonar-web/src/main/js/components/charts/__tests__/__snapshots__/Histogram-test.tsx.snap
new file mode 100644 (file)
index 0000000..e7885c8
--- /dev/null
@@ -0,0 +1,319 @@
+// Jest Snapshot v1, https://goo.gl/fbAQLP
+
+exports[`renders 1`] = `
+<svg
+  className="bar-chart"
+  height={75}
+  width={100}
+>
+  <g
+    transform="translate(10, 10)"
+  >
+    <g>
+      <g>
+        <rect
+          className="bar-chart-bar"
+          height={10}
+          width={54}
+          x={0}
+          y={10}
+        />
+      </g>
+      <g>
+        <rect
+          className="bar-chart-bar"
+          height={10}
+          width={41}
+          x={0}
+          y={28}
+        />
+      </g>
+      <g>
+        <rect
+          className="bar-chart-bar"
+          height={10}
+          width={81}
+          x={0}
+          y={46}
+        />
+      </g>
+    </g>
+  </g>
+</svg>
+`;
+
+exports[`renders with yValues 1`] = `
+<svg
+  className="bar-chart"
+  height={75}
+  width={100}
+>
+  <g
+    transform="translate(10, 10)"
+  >
+    <g>
+      <g>
+        <rect
+          className="bar-chart-bar"
+          height={10}
+          width={54}
+          x={0}
+          y={10}
+        />
+        <text
+          className="bar-chart-tick histogram-value"
+          dx="1em"
+          dy="0.3em"
+          x={53.33333333333333}
+          y={15}
+        >
+          100.0
+        </text>
+      </g>
+      <g>
+        <rect
+          className="bar-chart-bar"
+          height={10}
+          width={41}
+          x={0}
+          y={28}
+        />
+        <text
+          className="bar-chart-tick histogram-value"
+          dx="1em"
+          dy="0.3em"
+          x={40}
+          y={33}
+        >
+          75.0
+        </text>
+      </g>
+      <g>
+        <rect
+          className="bar-chart-bar"
+          height={10}
+          width={81}
+          x={0}
+          y={46}
+        />
+        <text
+          className="bar-chart-tick histogram-value"
+          dx="1em"
+          dy="0.3em"
+          x={80}
+          y={51}
+        >
+          150.0
+        </text>
+      </g>
+    </g>
+  </g>
+</svg>
+`;
+
+exports[`renders with yValues and yTicks 1`] = `
+<svg
+  className="bar-chart"
+  height={75}
+  width={100}
+>
+  <g
+    transform="translate(10, 10)"
+  >
+    <g>
+      <g>
+        <rect
+          className="bar-chart-bar"
+          height={10}
+          width={54}
+          x={0}
+          y={10}
+        />
+        <text
+          className="bar-chart-tick histogram-value"
+          dx="1em"
+          dy="0.3em"
+          x={53.33333333333333}
+          y={15}
+        >
+          100.0
+        </text>
+        <text
+          className="bar-chart-tick histogram-tick"
+          dx="-1em"
+          dy="0.3em"
+          x={0}
+          y={15}
+        >
+          a
+        </text>
+      </g>
+      <g>
+        <rect
+          className="bar-chart-bar"
+          height={10}
+          width={41}
+          x={0}
+          y={28}
+        />
+        <text
+          className="bar-chart-tick histogram-value"
+          dx="1em"
+          dy="0.3em"
+          x={40}
+          y={33}
+        >
+          75.0
+        </text>
+        <text
+          className="bar-chart-tick histogram-tick"
+          dx="-1em"
+          dy="0.3em"
+          x={0}
+          y={33}
+        >
+          b
+        </text>
+      </g>
+      <g>
+        <rect
+          className="bar-chart-bar"
+          height={10}
+          width={81}
+          x={0}
+          y={46}
+        />
+        <text
+          className="bar-chart-tick histogram-value"
+          dx="1em"
+          dy="0.3em"
+          x={80}
+          y={51}
+        >
+          150.0
+        </text>
+        <text
+          className="bar-chart-tick histogram-tick"
+          dx="-1em"
+          dy="0.3em"
+          x={0}
+          y={51}
+        >
+          c
+        </text>
+      </g>
+    </g>
+  </g>
+</svg>
+`;
+
+exports[`renders with yValues, yTicks and yTooltips 1`] = `
+<svg
+  className="bar-chart"
+  height={75}
+  width={100}
+>
+  <g
+    transform="translate(10, 10)"
+  >
+    <g>
+      <g>
+        <rect
+          className="bar-chart-bar"
+          height={10}
+          width={54}
+          x={0}
+          y={10}
+        />
+        <Tooltip
+          overlay="a - 100"
+          placement="top"
+        >
+          <text
+            className="bar-chart-tick histogram-value"
+            dx="1em"
+            dy="0.3em"
+            x={53.33333333333333}
+            y={15}
+          >
+            100.0
+          </text>
+        </Tooltip>
+        <text
+          className="bar-chart-tick histogram-tick"
+          dx="-1em"
+          dy="0.3em"
+          x={0}
+          y={15}
+        >
+          a
+        </text>
+      </g>
+      <g>
+        <rect
+          className="bar-chart-bar"
+          height={10}
+          width={41}
+          x={0}
+          y={28}
+        />
+        <Tooltip
+          overlay="b - 75"
+          placement="top"
+        >
+          <text
+            className="bar-chart-tick histogram-value"
+            dx="1em"
+            dy="0.3em"
+            x={40}
+            y={33}
+          >
+            75.0
+          </text>
+        </Tooltip>
+        <text
+          className="bar-chart-tick histogram-tick"
+          dx="-1em"
+          dy="0.3em"
+          x={0}
+          y={33}
+        >
+          b
+        </text>
+      </g>
+      <g>
+        <rect
+          className="bar-chart-bar"
+          height={10}
+          width={81}
+          x={0}
+          y={46}
+        />
+        <Tooltip
+          overlay="c - 150"
+          placement="top"
+        >
+          <text
+            className="bar-chart-tick histogram-value"
+            dx="1em"
+            dy="0.3em"
+            x={80}
+            y={51}
+          >
+            150.0
+          </text>
+        </Tooltip>
+        <text
+          className="bar-chart-tick histogram-tick"
+          dx="-1em"
+          dy="0.3em"
+          x={0}
+          y={51}
+        >
+          c
+        </text>
+      </g>
+    </g>
+  </g>
+</svg>
+`;
diff --git a/server/sonar-web/src/main/js/components/charts/__tests__/__snapshots__/LanguageDistribution-test.tsx.snap b/server/sonar-web/src/main/js/components/charts/__tests__/__snapshots__/LanguageDistribution-test.tsx.snap
new file mode 100644 (file)
index 0000000..1d2a6e2
--- /dev/null
@@ -0,0 +1,48 @@
+// Jest Snapshot v1, https://goo.gl/fbAQLP
+
+exports[`renders 1`] = `
+<Histogram
+  bars={
+    Array [
+      1734,
+      845,
+      73,
+      15,
+    ]
+  }
+  height={100}
+  padding={
+    Array [
+      0,
+      60,
+      0,
+      80,
+    ]
+  }
+  width={100}
+  yTicks={
+    Array [
+      "Java",
+      "JavaScript",
+      "cpp",
+      "unknown",
+    ]
+  }
+  yTooltips={
+    Array [
+      "1,734",
+      "",
+      "",
+      "",
+    ]
+  }
+  yValues={
+    Array [
+      "1.7k",
+      "845",
+      "73",
+      "15",
+    ]
+  }
+/>
+`;
diff --git a/server/sonar-web/src/main/js/components/charts/histogram.js b/server/sonar-web/src/main/js/components/charts/histogram.js
deleted file mode 100644 (file)
index 2201cf0..0000000
+++ /dev/null
@@ -1,166 +0,0 @@
-/*
- * SonarQube
- * Copyright (C) 2009-2017 SonarSource SA
- * mailto:info AT sonarsource DOT com
- *
- * This program is free software; you can redistribute it and/or
- * modify it under the terms of the GNU Lesser General Public
- * License as published by the Free Software Foundation; either
- * version 3 of the License, or (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
- * Lesser General Public License for more details.
- *
- * You should have received a copy of the GNU Lesser General Public License
- * along with this program; if not, write to the Free Software Foundation,
- * Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.
- */
-import React from 'react';
-import createReactClass from 'create-react-class';
-import PropTypes from 'prop-types';
-import { max } from 'd3-array';
-import { scaleLinear, scaleBand } from 'd3-scale';
-import { ResizeMixin } from './../mixins/resize-mixin';
-import { TooltipsMixin } from './../mixins/tooltips-mixin';
-
-export const Histogram = createReactClass({
-  displayName: 'Histogram',
-
-  propTypes: {
-    alignTicks: PropTypes.bool,
-    data: PropTypes.arrayOf(PropTypes.object).isRequired,
-    yTicks: PropTypes.arrayOf(PropTypes.any),
-    yValues: PropTypes.arrayOf(PropTypes.any),
-    width: PropTypes.number,
-    height: PropTypes.number,
-    padding: PropTypes.arrayOf(PropTypes.number),
-    barsHeight: PropTypes.number,
-    onBarClick: PropTypes.func
-  },
-
-  mixins: [ResizeMixin, TooltipsMixin],
-
-  getDefaultProps() {
-    return {
-      xTicks: [],
-      xValues: [],
-      padding: [10, 10, 10, 10],
-      barsHeight: 10
-    };
-  },
-
-  getInitialState() {
-    return { width: this.props.width, height: this.props.height };
-  },
-
-  handleClick(point) {
-    this.props.onBarClick(point);
-  },
-
-  renderTicks(xScale, yScale) {
-    if (!this.props.yTicks.length) {
-      return null;
-    }
-    const ticks = this.props.yTicks.map((tick, index) => {
-      const point = this.props.data[index];
-      const x = xScale.range()[0];
-      const y = Math.round(yScale(point.y) + yScale.bandwidth() / 2 + this.props.barsHeight / 2);
-      const label = tick.label ? tick.label : tick;
-      const tooltip = tick.tooltip ? tick.tooltip : null;
-      const historyTickClass = this.props.alignTicks ? 'histogram-tick-start' : 'histogram-tick';
-      return (
-        <text
-          key={index}
-          className={'bar-chart-tick ' + historyTickClass}
-          onClick={this.props.onBarClick && this.handleClick.bind(this, point)}
-          style={{ cursor: this.props.onBarClick ? 'pointer' : 'default' }}
-          data-title={tooltip}
-          data-toggle={tooltip ? 'tooltip' : null}
-          x={x}
-          y={y}
-          dx={this.props.alignTicks ? 0 : '-1em'}
-          dy="0.3em">
-          {label}
-        </text>
-      );
-    });
-    return <g>{ticks}</g>;
-  },
-
-  renderValues(xScale, yScale) {
-    if (!this.props.yValues.length) {
-      return null;
-    }
-    const ticks = this.props.yValues.map((value, index) => {
-      const point = this.props.data[index];
-      const x = xScale(point.x) + (this.props.alignTicks ? this.props.padding[3] : 0);
-      const y = Math.round(yScale(point.y) + yScale.bandwidth() / 2 + this.props.barsHeight / 2);
-      return (
-        <text
-          key={index}
-          onClick={this.props.onBarClick && this.handleClick.bind(this, point)}
-          className="bar-chart-tick histogram-value"
-          style={{ cursor: this.props.onBarClick ? 'pointer' : 'default' }}
-          x={x}
-          y={y}
-          dx="1em"
-          dy="0.3em">
-          {value}
-        </text>
-      );
-    });
-    return <g>{ticks}</g>;
-  },
-
-  renderBars(xScale, yScale) {
-    const bars = this.props.data.map((d, index) => {
-      const width = Math.round(xScale(d.x)) + /* minimum bar width */ 1;
-      const x = xScale.range()[0] + (this.props.alignTicks ? this.props.padding[3] : 0);
-      const y = Math.round(yScale(d.y) + yScale.bandwidth() / 2);
-      return (
-        <rect
-          key={index}
-          className="bar-chart-bar"
-          onClick={this.props.onBarClick && this.handleClick.bind(this, d)}
-          style={{ cursor: this.props.onBarClick ? 'pointer' : 'default' }}
-          x={x}
-          y={y}
-          width={width}
-          height={this.props.barsHeight}
-        />
-      );
-    });
-    return <g>{bars}</g>;
-  },
-
-  render() {
-    if (!this.state.width || !this.state.height) {
-      return <div />;
-    }
-
-    const availableWidth = this.state.width - this.props.padding[1] - this.props.padding[3];
-    const availableHeight = this.state.height - this.props.padding[0] - this.props.padding[2];
-
-    const maxX = max(this.props.data, d => d.x);
-    const xScale = scaleLinear()
-      .domain([0, maxX])
-      .range([0, availableWidth]);
-    const yScale = scaleBand()
-      .domain(this.props.data.map(d => d.y))
-      .rangeRound([0, availableHeight]);
-
-    return (
-      <svg className="bar-chart" width={this.state.width} height={this.state.height}>
-        <g
-          transform={`translate(${this.props.alignTicks ? 4 : this.props.padding[3]}, ${this.props
-            .padding[0]})`}>
-          {this.renderTicks(xScale, yScale)}
-          {this.renderValues(xScale, yScale)}
-          {this.renderBars(xScale, yScale)}
-        </g>
-      </svg>
-    );
-  }
-});
index 9c85f40091f38cdddc155193f4eea8394773ad32..a7f70bc62027f49f4fab4e67662b70e97f24c5a6 100644 (file)
   version "2.2.0"
   resolved "https://registry.yarnpkg.com/@types/classnames/-/classnames-2.2.0.tgz#f2312039e780bdf89d7d4102a26ec11de5ec58aa"
 
+"@types/d3-array@1.2.1":
+  version "1.2.1"
+  resolved "https://registry.yarnpkg.com/@types/d3-array/-/d3-array-1.2.1.tgz#e489605208d46a1c9d980d2e5772fa9c75d9ec65"
+
+"@types/d3-scale@1.0.10":
+  version "1.0.10"
+  resolved "https://registry.yarnpkg.com/@types/d3-scale/-/d3-scale-1.0.10.tgz#8c5c1dca54a159eed042b46719dbb3bdb7e8c842"
+  dependencies:
+    "@types/d3-time" "*"
+
+"@types/d3-time@*":
+  version "1.0.7"
+  resolved "https://registry.yarnpkg.com/@types/d3-time/-/d3-time-1.0.7.tgz#4266d7c9be15fa81256a88d1d052d61cd8dc572c"
+
 "@types/date-fns@2.6.0":
   version "2.6.0"
   resolved "https://registry.yarnpkg.com/@types/date-fns/-/date-fns-2.6.0.tgz#b062ca46562002909be0c63a6467ed173136acc1"