diff options
13 files changed, 657 insertions, 188 deletions
diff --git a/server/sonar-web/package.json b/server/sonar-web/package.json index aecf565d6c1..4c8d13681cb 100644 --- a/server/sonar-web/package.json +++ b/server/sonar-web/package.json @@ -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", diff --git a/server/sonar-web/src/main/js/apps/component-measures/components/MeasureHeader.js b/server/sonar-web/src/main/js/apps/component-measures/components/MeasureHeader.js index de343e95337..49f039c35d1 100644 --- a/server/sonar-web/src/main/js/apps/component-measures/components/MeasureHeader.js +++ b/server/sonar-web/src/main/js/apps/component-measures/components/MeasureHeader.js @@ -140,6 +140,7 @@ export default class MeasureHeader extends React.PureComponent { <LanguageDistributionContainer alignTicks={true} distribution={secondaryMeasure.value} + width={260} /> </div> )} diff --git a/server/sonar-web/src/main/js/apps/overview/meta/MetaSize.js b/server/sonar-web/src/main/js/apps/overview/meta/MetaSize.js index a488f469da6..7cd41c45753 100644 --- a/server/sonar-web/src/main/js/apps/overview/meta/MetaSize.js +++ b/server/sonar-web/src/main/js/apps/overview/meta/MetaSize.js @@ -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; }; diff --git a/server/sonar-web/src/main/js/apps/portfolio/components/Summary.tsx b/server/sonar-web/src/main/js/apps/portfolio/components/Summary.tsx index aa229597a1e..5aecf73d70f 100644 --- a/server/sonar-web/src/main/js/apps/portfolio/components/Summary.tsx +++ b/server/sonar-web/src/main/js/apps/portfolio/components/Summary.tsx @@ -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> diff --git a/server/sonar-web/src/main/js/apps/portfolio/components/__tests__/__snapshots__/Summary-test.tsx.snap b/server/sonar-web/src/main/js/apps/portfolio/components/__tests__/__snapshots__/Summary-test.tsx.snap index d377a6a44a9..db159a6947a 100644 --- a/server/sonar-web/src/main/js/apps/portfolio/components/__tests__/__snapshots__/Summary-test.tsx.snap +++ b/server/sonar-web/src/main/js/apps/portfolio/components/__tests__/__snapshots__/Summary-test.tsx.snap @@ -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 index 00000000000..420de6946ad --- /dev/null +++ b/server/sonar-web/src/main/js/components/charts/Histogram.tsx @@ -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> + ); + } +} diff --git a/server/sonar-web/src/main/js/components/charts/LanguageDistribution.tsx b/server/sonar-web/src/main/js/components/charts/LanguageDistribution.tsx index 6766372207f..d3f769728e6 100644 --- a/server/sonar-web/src/main/js/components/charts/LanguageDistribution.tsx +++ b/server/sonar-web/src/main/js/components/charts/LanguageDistribution.tsx @@ -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 index 00000000000..9db4b13536a --- /dev/null +++ b/server/sonar-web/src/main/js/components/charts/__tests__/Histogram-test.tsx @@ -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 index 00000000000..8bed7941d2f --- /dev/null +++ b/server/sonar-web/src/main/js/components/charts/__tests__/LanguageDistribution-test.tsx @@ -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 index 00000000000..e7885c89cad --- /dev/null +++ b/server/sonar-web/src/main/js/components/charts/__tests__/__snapshots__/Histogram-test.tsx.snap @@ -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 index 00000000000..1d2a6e2c0eb --- /dev/null +++ b/server/sonar-web/src/main/js/components/charts/__tests__/__snapshots__/LanguageDistribution-test.tsx.snap @@ -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 index 2201cf025c0..00000000000 --- a/server/sonar-web/src/main/js/components/charts/histogram.js +++ /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> - ); - } -}); diff --git a/server/sonar-web/yarn.lock b/server/sonar-web/yarn.lock index 9c85f40091f..a7f70bc6202 100644 --- a/server/sonar-web/yarn.lock +++ b/server/sonar-web/yarn.lock @@ -17,6 +17,20 @@ 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" |