From 5386cb6010e7469db0c8f0fa5972973b9a2fa266 Mon Sep 17 00:00:00 2001 From: Stas Vilchik Date: Tue, 15 May 2018 17:10:00 +0200 Subject: [PATCH] rewrite some components in ts (#243) --- server/sonar-web/package.json | 2 + .../src/main/js/{typings => @types}/json.d.ts | 0 .../main/js/app/components/search/Search.js | 2 +- .../js/app/components/search/SearchResult.js | 2 +- .../js/apps/code/components/ComponentPin.tsx | 2 +- .../drilldown/TreeMapView.js | 2 +- .../apps/issues/sidebar/CreationDateFacet.tsx | 2 +- .../js/apps/overview/components/Timeline.tsx | 9 +- .../__snapshots__/Timeline-test.js.snap | 14 +- .../main/js/apps/overview/meta/MetaLink.tsx | 2 +- .../js/apps/project-admin/links/LinkRow.tsx | 2 +- .../apps/quality-profiles/components/App.tsx | 4 +- .../components/AppContainer.tsx | 4 +- .../main/js/components/charts/BarChart.tsx | 169 ++++ .../main/js/components/charts/BubbleChart.tsx | 41 +- .../{ColorBoxLegend.js => ColorBoxLegend.tsx} | 26 +- ...dientLegend.js => ColorGradientLegend.tsx} | 66 +- ...atingsLegend.js => ColorRatingsLegend.tsx} | 11 +- .../main/js/components/charts/DonutChart.tsx | 83 ++ .../charts/{line-chart.js => LineChart.tsx} | 165 +-- .../charts/{TreeMap.js => TreeMap.tsx} | 65 +- .../{TreeMapRect.js => TreeMapRect.tsx} | 51 +- .../{bar-chart-test.js => BarChart-test.tsx} | 12 +- ...bbleChart-test.js => BubbleChart-test.tsx} | 8 +- ...{line-chart-test.js => LineChart-test.tsx} | 10 +- .../{TreeMap-test.js => TreeMap-test.tsx} | 4 +- ...test.js.snap => BubbleChart-test.tsx.snap} | 0 .../main/js/components/charts/bar-chart.js | 187 ---- .../main/js/components/charts/donut-chart.js | 84 -- .../{LocationIndex.js => LocationIndex.tsx} | 32 +- ...LocationMessage.js => LocationMessage.tsx} | 17 +- ...zationHelmet.js => OrganizationHelmet.tsx} | 15 +- ...ationBox.js => UpgradeOrganizationBox.tsx} | 15 +- .../{GlobalMessages.js => GlobalMessages.tsx} | 34 +- .../controls/{Toggle.js => Toggle.tsx} | 34 +- ...ListFooter-test.js => ListFooter-test.tsx} | 6 +- .../{Toggle-test.js => Toggle-test.tsx} | 16 +- .../BugTrackerIcon.tsx} | 32 +- .../ClockIcon.tsx} | 31 +- .../components/icons-components/PinIcon.tsx | 39 + .../__snapshots__/IssueTitleBar-test.js.snap | 4 +- .../{SeverityIcon.js => SeverityIcon.tsx} | 17 +- .../{StatusHelper.js => StatusHelper.tsx} | 20 +- .../shared/{StatusIcon.js => StatusIcon.tsx} | 15 +- .../main/js/components/shared/WithStore.js | 52 - ...ierIcon-test.js => QualifierIcon-test.tsx} | 2 +- ...st.js.snap => QualifierIcon-test.tsx.snap} | 0 .../src/main/js/components/shared/pin-icon.js | 33 - .../main/js/components/ui/CoverageRating.tsx | 4 +- .../{Level-test.js => Level-test.tsx} | 2 +- .../src/main/js/components/ui/buttons.tsx | 1 + .../main/js/libs/third-party/VSS.SDK.min.js | 957 ------------------ .../src/main/js/typings/rc-tooltip.d.ts | 54 - server/sonar-web/yarn.lock | 14 + 54 files changed, 704 insertions(+), 1771 deletions(-) rename server/sonar-web/src/main/js/{typings => @types}/json.d.ts (100%) create mode 100644 server/sonar-web/src/main/js/components/charts/BarChart.tsx rename server/sonar-web/src/main/js/components/charts/{ColorBoxLegend.js => ColorBoxLegend.tsx} (76%) rename server/sonar-web/src/main/js/components/charts/{ColorGradientLegend.js => ColorGradientLegend.tsx} (68%) rename server/sonar-web/src/main/js/components/charts/{ColorRatingsLegend.js => ColorRatingsLegend.tsx} (88%) create mode 100644 server/sonar-web/src/main/js/components/charts/DonutChart.tsx rename server/sonar-web/src/main/js/components/charts/{line-chart.js => LineChart.tsx} (50%) rename server/sonar-web/src/main/js/components/charts/{TreeMap.js => TreeMap.tsx} (81%) rename server/sonar-web/src/main/js/components/charts/{TreeMapRect.js => TreeMapRect.tsx} (80%) rename server/sonar-web/src/main/js/components/charts/__tests__/{bar-chart-test.js => BarChart-test.tsx} (80%) rename server/sonar-web/src/main/js/components/charts/__tests__/{BubbleChart-test.js => BubbleChart-test.tsx} (87%) rename server/sonar-web/src/main/js/components/charts/__tests__/{line-chart-test.js => LineChart-test.tsx} (81%) rename server/sonar-web/src/main/js/components/charts/__tests__/{TreeMap-test.js => TreeMap-test.tsx} (91%) rename server/sonar-web/src/main/js/components/charts/__tests__/__snapshots__/{BubbleChart-test.js.snap => BubbleChart-test.tsx.snap} (100%) delete mode 100644 server/sonar-web/src/main/js/components/charts/bar-chart.js delete mode 100644 server/sonar-web/src/main/js/components/charts/donut-chart.js rename server/sonar-web/src/main/js/components/common/{LocationIndex.js => LocationIndex.tsx} (75%) rename server/sonar-web/src/main/js/components/common/{LocationMessage.js => LocationMessage.tsx} (82%) rename server/sonar-web/src/main/js/components/common/{OrganizationHelmet.js => OrganizationHelmet.tsx} (89%) rename server/sonar-web/src/main/js/components/common/{UpgradeOrganizationBox.js => UpgradeOrganizationBox.tsx} (86%) rename server/sonar-web/src/main/js/components/controls/{GlobalMessages.js => GlobalMessages.tsx} (68%) rename server/sonar-web/src/main/js/components/controls/{Toggle.js => Toggle.tsx} (67%) rename server/sonar-web/src/main/js/components/controls/__tests__/{ListFooter-test.js => ListFooter-test.tsx} (88%) rename server/sonar-web/src/main/js/components/controls/__tests__/{Toggle-test.js => Toggle-test.tsx} (80%) rename server/sonar-web/src/main/js/components/{ui/BugTrackerIcon.js => icons-components/BugTrackerIcon.tsx} (58%) rename server/sonar-web/src/main/js/components/{common/ClockIcon.js => icons-components/ClockIcon.tsx} (70%) create mode 100644 server/sonar-web/src/main/js/components/icons-components/PinIcon.tsx rename server/sonar-web/src/main/js/components/shared/{SeverityIcon.js => SeverityIcon.tsx} (73%) rename server/sonar-web/src/main/js/components/shared/{StatusHelper.js => StatusHelper.tsx} (80%) rename server/sonar-web/src/main/js/components/shared/{StatusIcon.js => StatusIcon.tsx} (74%) delete mode 100644 server/sonar-web/src/main/js/components/shared/WithStore.js rename server/sonar-web/src/main/js/components/shared/__tests__/{QualifierIcon-test.js => QualifierIcon-test.tsx} (97%) rename server/sonar-web/src/main/js/components/shared/__tests__/__snapshots__/{QualifierIcon-test.js.snap => QualifierIcon-test.tsx.snap} (100%) delete mode 100644 server/sonar-web/src/main/js/components/shared/pin-icon.js rename server/sonar-web/src/main/js/components/ui/__tests__/{Level-test.js => Level-test.tsx} (97%) delete mode 100644 server/sonar-web/src/main/js/libs/third-party/VSS.SDK.min.js delete mode 100644 server/sonar-web/src/main/js/typings/rc-tooltip.d.ts diff --git a/server/sonar-web/package.json b/server/sonar-web/package.json index aed2f5263af..283b02f948d 100644 --- a/server/sonar-web/package.json +++ b/server/sonar-web/package.json @@ -42,7 +42,9 @@ "@types/classnames": "2.2.3", "@types/clipboard": "2.0.0", "@types/d3-array": "1.2.1", + "@types/d3-hierarchy": "1.1.1", "@types/d3-scale": "2.0.0", + "@types/d3-shape": "1.2.2", "@types/enzyme": "3.1.10", "@types/jest": "22.2.3", "@types/keymaster": "1.6.28", diff --git a/server/sonar-web/src/main/js/typings/json.d.ts b/server/sonar-web/src/main/js/@types/json.d.ts similarity index 100% rename from server/sonar-web/src/main/js/typings/json.d.ts rename to server/sonar-web/src/main/js/@types/json.d.ts diff --git a/server/sonar-web/src/main/js/app/components/search/Search.js b/server/sonar-web/src/main/js/app/components/search/Search.js index 327844759f8..cd315cbfacf 100644 --- a/server/sonar-web/src/main/js/app/components/search/Search.js +++ b/server/sonar-web/src/main/js/app/components/search/Search.js @@ -29,7 +29,7 @@ import { sortQualifiers } from './utils'; /*:: import type { Component, More, Results } from './utils'; */ import RecentHistory from '../../components/RecentHistory'; import DeferredSpinner from '../../../components/common/DeferredSpinner'; -import ClockIcon from '../../../components/common/ClockIcon'; +import ClockIcon from '../../../components/icons-components/ClockIcon'; import OutsideClickHandler from '../../../components/controls/OutsideClickHandler'; import SearchBox from '../../../components/controls/SearchBox'; import { getSuggestions } from '../../../api/components'; diff --git a/server/sonar-web/src/main/js/app/components/search/SearchResult.js b/server/sonar-web/src/main/js/app/components/search/SearchResult.js index c4bd954c9bb..02cc378ce09 100644 --- a/server/sonar-web/src/main/js/app/components/search/SearchResult.js +++ b/server/sonar-web/src/main/js/app/components/search/SearchResult.js @@ -23,7 +23,7 @@ import { Link } from 'react-router'; /*:: import type { Component } from './utils'; */ import FavoriteIcon from '../../../components/icons-components/FavoriteIcon'; import QualifierIcon from '../../../components/shared/QualifierIcon'; -import ClockIcon from '../../../components/common/ClockIcon'; +import ClockIcon from '../../../components/icons-components/ClockIcon'; import Tooltip from '../../../components/controls/Tooltip'; import { getProjectUrl } from '../../../helpers/urls'; diff --git a/server/sonar-web/src/main/js/apps/code/components/ComponentPin.tsx b/server/sonar-web/src/main/js/apps/code/components/ComponentPin.tsx index 3eadf905cbf..18d77d03f67 100644 --- a/server/sonar-web/src/main/js/apps/code/components/ComponentPin.tsx +++ b/server/sonar-web/src/main/js/apps/code/components/ComponentPin.tsx @@ -21,7 +21,7 @@ import * as React from 'react'; import * as PropTypes from 'prop-types'; import { Component } from '../types'; import { BranchLike } from '../../../app/types'; -import PinIcon from '../../../components/shared/pin-icon'; +import PinIcon from '../../../components/icons-components/PinIcon'; import { WorkspaceContext } from '../../../components/workspace/context'; import { translate } from '../../../helpers/l10n'; diff --git a/server/sonar-web/src/main/js/apps/component-measures/drilldown/TreeMapView.js b/server/sonar-web/src/main/js/apps/component-measures/drilldown/TreeMapView.js index 62d23b385b4..6a79f5e3d56 100644 --- a/server/sonar-web/src/main/js/apps/component-measures/drilldown/TreeMapView.js +++ b/server/sonar-web/src/main/js/apps/component-measures/drilldown/TreeMapView.js @@ -205,9 +205,9 @@ export default class TreeMapView extends React.PureComponent { {({ width }) => ( )} diff --git a/server/sonar-web/src/main/js/apps/issues/sidebar/CreationDateFacet.tsx b/server/sonar-web/src/main/js/apps/issues/sidebar/CreationDateFacet.tsx index 5ba901f92e6..6096ada13a5 100644 --- a/server/sonar-web/src/main/js/apps/issues/sidebar/CreationDateFacet.tsx +++ b/server/sonar-web/src/main/js/apps/issues/sidebar/CreationDateFacet.tsx @@ -28,7 +28,7 @@ import FacetItem from '../../../components/facet/FacetItem'; import { longFormatterOption } from '../../../components/intl/DateFormatter'; import DateFromNow from '../../../components/intl/DateFromNow'; import DateTimeFormatter from '../../../components/intl/DateTimeFormatter'; -import { BarChart } from '../../../components/charts/bar-chart'; +import BarChart from '../../../components/charts/BarChart'; import DateRangeInput from '../../../components/controls/DateRangeInput'; import { isSameDay, parseDate } from '../../../helpers/dates'; import { translate } from '../../../helpers/l10n'; diff --git a/server/sonar-web/src/main/js/apps/overview/components/Timeline.tsx b/server/sonar-web/src/main/js/apps/overview/components/Timeline.tsx index 1b2bd3d04ec..2b372ce80dd 100644 --- a/server/sonar-web/src/main/js/apps/overview/components/Timeline.tsx +++ b/server/sonar-web/src/main/js/apps/overview/components/Timeline.tsx @@ -19,7 +19,7 @@ */ import * as React from 'react'; import { max } from 'd3-array'; -import { LineChart } from '../../../components/charts/line-chart'; +import LineChart from '../../../components/charts/LineChart'; import { HistoryItem } from '../../../api/time-machine'; const HEIGHT = 80; @@ -49,17 +49,16 @@ export default class Timeline extends React.PureComponent { } const data = snapshots.map((snapshot, index) => { - return { x: index, y: snapshot.value }; + return { x: index, y: Number(snapshot.value) }; }); - const domain = [0, max(this.props.history, d => parseFloat(d.value))]; + const domain = [0, max(this.props.history, d => parseFloat(d.value))] as [number, number]; return ( diff --git a/server/sonar-web/src/main/js/apps/overview/components/__tests__/__snapshots__/Timeline-test.js.snap b/server/sonar-web/src/main/js/apps/overview/components/__tests__/__snapshots__/Timeline-test.js.snap index c25e28b7e3f..3c01c85264d 100644 --- a/server/sonar-web/src/main/js/apps/overview/components/__tests__/__snapshots__/Timeline-test.js.snap +++ b/server/sonar-web/src/main/js/apps/overview/components/__tests__/__snapshots__/Timeline-test.js.snap @@ -6,11 +6,11 @@ exports[`should render correctly with a "before" range 1`] = ` Array [ Object { "x": 0, - "y": "29.6", + "y": 29.6, }, Object { "x": 1, - "y": "170.8", + "y": 170.8, }, ] } @@ -24,7 +24,6 @@ exports[`should render correctly with a "before" range 1`] = ` ] } height={80} - interpolate="basis" padding={ Array [ 0, @@ -33,8 +32,6 @@ exports[`should render correctly with a "before" range 1`] = ` 0, ] } - xTicks={Array []} - xValues={Array []} /> `; @@ -44,11 +41,11 @@ exports[`should render correctly with an "after" range 1`] = ` Array [ Object { "x": 0, - "y": "360", + "y": 360, }, Object { "x": 1, - "y": "39", + "y": 39, }, ] } @@ -62,7 +59,6 @@ exports[`should render correctly with an "after" range 1`] = ` ] } height={80} - interpolate="basis" padding={ Array [ 0, @@ -71,7 +67,5 @@ exports[`should render correctly with an "after" range 1`] = ` 0, ] } - xTicks={Array []} - xValues={Array []} /> `; diff --git a/server/sonar-web/src/main/js/apps/overview/meta/MetaLink.tsx b/server/sonar-web/src/main/js/apps/overview/meta/MetaLink.tsx index 4996bae73c0..7a3caf2ab7a 100644 --- a/server/sonar-web/src/main/js/apps/overview/meta/MetaLink.tsx +++ b/server/sonar-web/src/main/js/apps/overview/meta/MetaLink.tsx @@ -21,7 +21,7 @@ import * as React from 'react'; import { isProvided, getLinkName } from '../../project-admin/links/utils'; import { ProjectLink } from '../../../app/types'; import DetachIcon from '../../../components/icons-components/DetachIcon'; -import BugTrackerIcon from '../../../components/ui/BugTrackerIcon'; +import BugTrackerIcon from '../../../components/icons-components/BugTrackerIcon'; interface Props { link: ProjectLink; diff --git a/server/sonar-web/src/main/js/apps/project-admin/links/LinkRow.tsx b/server/sonar-web/src/main/js/apps/project-admin/links/LinkRow.tsx index 0bd5d10cbd1..a39c7cfe903 100644 --- a/server/sonar-web/src/main/js/apps/project-admin/links/LinkRow.tsx +++ b/server/sonar-web/src/main/js/apps/project-admin/links/LinkRow.tsx @@ -22,7 +22,7 @@ import { isProvided, getLinkName } from './utils'; import { ProjectLink } from '../../../app/types'; import ConfirmButton from '../../../components/controls/ConfirmButton'; import DetachIcon from '../../../components/icons-components/DetachIcon'; -import BugTrackerIcon from '../../../components/ui/BugTrackerIcon'; +import BugTrackerIcon from '../../../components/icons-components/BugTrackerIcon'; import { Button } from '../../../components/ui/buttons'; import { translate, translateWithParameters } from '../../../helpers/l10n'; diff --git a/server/sonar-web/src/main/js/apps/quality-profiles/components/App.tsx b/server/sonar-web/src/main/js/apps/quality-profiles/components/App.tsx index 3a7a834957f..35fd9354bff 100644 --- a/server/sonar-web/src/main/js/apps/quality-profiles/components/App.tsx +++ b/server/sonar-web/src/main/js/apps/quality-profiles/components/App.tsx @@ -31,7 +31,7 @@ interface Props { children: React.ReactElement; languages: Languages; onRequestFail: (reasong: any) => void; - organization: { name: string; key: string } | null; + organization: { name: string; key: string } | undefined; } interface State { @@ -113,8 +113,8 @@ export default class App extends React.PureComponent {
{this.renderChild()} diff --git a/server/sonar-web/src/main/js/apps/quality-profiles/components/AppContainer.tsx b/server/sonar-web/src/main/js/apps/quality-profiles/components/AppContainer.tsx index 1f1d6d2f4a5..1da66735a8c 100644 --- a/server/sonar-web/src/main/js/apps/quality-profiles/components/AppContainer.tsx +++ b/server/sonar-web/src/main/js/apps/quality-profiles/components/AppContainer.tsx @@ -26,7 +26,7 @@ import { Languages } from '../../../store/languages/reducer'; interface StateProps { languages: Languages; - organization: { name: string; key: string } | null; + organization: { name: string; key: string } | undefined; } interface DispatchProps { @@ -37,7 +37,7 @@ const mapStateToProps = (state: any, ownProps: any) => ({ languages: getLanguages(state), organization: ownProps.params.organizationKey ? getOrganizationByKey(state, ownProps.params.organizationKey) - : null + : undefined }); const mapDispatchToProps = (dispatch: any) => ({ diff --git a/server/sonar-web/src/main/js/components/charts/BarChart.tsx b/server/sonar-web/src/main/js/components/charts/BarChart.tsx new file mode 100644 index 00000000000..7739f1d143d --- /dev/null +++ b/server/sonar-web/src/main/js/components/charts/BarChart.tsx @@ -0,0 +1,169 @@ +/* + * SonarQube + * Copyright (C) 2009-2018 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 DataPoint { + tooltip?: React.ReactNode; + x: number; + y: number; +} + +interface Props { + barsWidth: number; + data: Array; + height: number; + onBarClick?: (point: DataPoint & T) => void; + padding?: [number, number, number, number]; + width: number; + xTicks?: string[]; + xValues?: string[]; +} + +export default class BarChart extends React.PureComponent> { + handleClick = (point: DataPoint & T) => { + if (this.props.onBarClick) { + this.props.onBarClick(point); + } + }; + + renderXTicks = (xScale: ScaleBand, yScale: ScaleLinear) => { + const { data, xTicks = [] } = this.props; + + if (!xTicks.length) { + return null; + } + + const ticks = xTicks.map((tick, index) => { + const point = data[index]; + const x = Math.round((xScale(point.x) as number) + xScale.bandwidth() / 2); + const y = yScale.range()[0]; + const d = data[index]; + const text = ( + this.handleClick(point)} + style={{ cursor: this.props.onBarClick ? 'pointer' : 'default' }} + x={x} + y={y}> + {tick} + + ); + return ( + + {text} + + ); + }); + return {ticks}; + }; + + renderXValues = (xScale: ScaleBand, yScale: ScaleLinear) => { + const { data, xValues = [] } = this.props; + + if (!xValues.length) { + return null; + } + + const ticks = xValues.map((value, index) => { + const point = data[index]; + const x = Math.round((xScale(point.x) as number) + xScale.bandwidth() / 2); + const y = yScale(point.y); + const text = ( + this.handleClick(point)} + style={{ cursor: this.props.onBarClick ? 'pointer' : 'default' }} + x={x} + y={y}> + {value} + + ); + return ( + + {text} + + ); + }); + return {ticks}; + }; + + renderBars = (xScale: ScaleBand, yScale: ScaleLinear) => { + const bars = this.props.data.map((point, index) => { + const x = Math.round(xScale(point.x) as number); + const maxY = yScale.range()[0]; + const y = Math.round(yScale(point.y)) - /* minimum bar height */ 1; + const height = maxY - y; + const rect = ( + this.handleClick(point)} + style={{ cursor: this.props.onBarClick ? 'pointer' : 'default' }} + width={this.props.barsWidth} + x={x} + y={y} + /> + ); + return ( + + {rect} + + ); + }); + return {bars}; + }; + + render() { + const { barsWidth, data, width, height, padding = [10, 10, 10, 10] } = this.props; + + const availableWidth = width - padding[1] - padding[3]; + const availableHeight = height - padding[0] - padding[2]; + + const innerPadding = (availableWidth - barsWidth * data.length) / (data.length - 1); + const relativeInnerPadding = innerPadding / (innerPadding + barsWidth); + + const maxY = max(data, d => d.y) as number; + const xScale = scaleBand() + .domain(data.map(d => d.x)) + .range([0, availableWidth]) + .paddingInner(relativeInnerPadding); + const yScale = scaleLinear() + .domain([0, maxY]) + .range([availableHeight, 0]); + + return ( + + + {this.renderXTicks(xScale, yScale)} + {this.renderXValues(xScale, yScale)} + {this.renderBars(xScale, yScale)} + + + ); + } +} diff --git a/server/sonar-web/src/main/js/components/charts/BubbleChart.tsx b/server/sonar-web/src/main/js/components/charts/BubbleChart.tsx index 40be474bb72..d280be68922 100644 --- a/server/sonar-web/src/main/js/components/charts/BubbleChart.tsx +++ b/server/sonar-web/src/main/js/components/charts/BubbleChart.tsx @@ -86,12 +86,12 @@ interface Props { displayXTicks?: boolean; displayYGrid?: boolean; displayYTicks?: boolean; - formatXTick: (tick: number) => string; - formatYTick: (tick: number) => string; + formatXTick?: (tick: number) => string; + formatYTick?: (tick: number) => string; height: number; items: Item[]; onBubbleClick?: (link?: string) => void; - padding: [number, number, number, number]; + padding?: [number, number, number, number]; sizeDomain?: [number, number]; sizeRange?: [number, number]; xDomain?: [number, number]; @@ -115,9 +115,6 @@ export default class BubbleChart extends React.Component { displayXTicks: true, displayYGrid: true, displayYTicks: true, - formatXTick: (d: number) => d, - formatYTick: (d: number) => d, - padding: [10, 10, 10, 10], sizeRange: [5, 45] }; @@ -141,6 +138,18 @@ export default class BubbleChart extends React.Component { document.removeEventListener('mousemove', this.updateZoomCenter); } + get formatXTick() { + return this.props.formatXTick || ((d: number) => String(d)); + } + + get formatYTick() { + return this.props.formatYTick || ((d: number) => String(d)); + } + + get padding() { + return this.props.padding || [10, 10, 10, 10]; + } + startMoving = (event: React.MouseEvent) => { if (this.node && this.state.zoom > 1) { const rect = this.node.getBoundingClientRect(); @@ -176,8 +185,8 @@ export default class BubbleChart extends React.Component { event.preventDefault(); const rect = this.node.getBoundingClientRect(); - const mouseX = event.clientX - rect.left - this.props.padding[1]; - const mouseY = event.clientY - rect.top - this.props.padding[0]; + const mouseX = event.clientX - rect.left - this.padding[1]; + const mouseY = event.clientY - rect.top - this.padding[0]; let delta = event.deltaY; if ((event as any).webkitDirectionInvertedFromDevice) { @@ -308,7 +317,7 @@ export default class BubbleChart extends React.Component { const ticks = xTicks.map((tick, index) => { const x = xScale(tick); const y = yScale.range()[0]; - const innerText = this.props.formatXTick(tick); + const innerText = this.formatXTick(tick); return ( { const ticks = yTicks.map((tick, index) => { const x = xScale.range()[0]; const y = yScale(tick); - const innerText = this.props.formatYTick(tick); + const innerText = this.formatYTick(tick); return ( { }; renderChart = (width: number) => { - const availableWidth = width - this.props.padding[1] - this.props.padding[3]; - const availableHeight = this.props.height - this.props.padding[0] - this.props.padding[2]; + const availableWidth = width - this.padding[1] - this.padding[3]; + const availableHeight = this.props.height - this.padding[0] - this.padding[2]; const xScale = scaleLinear() .domain(this.props.xDomain || [0, max(this.props.items, d => d.x) || 0]) @@ -389,8 +398,8 @@ export default class BubbleChart extends React.Component { ); }); - const xTicks = this.getTicks(xScale, this.props.formatXTick); - const yTicks = this.getTicks(yScale, this.props.formatYTick); + const xTicks = this.getTicks(xScale, this.formatXTick); + const yTicks = this.getTicks(yScale, this.formatYTick); return ( { onWheel={this.onWheel} ref={node => (this.node = node)} width={width}> - + {this.renderXGrid(xTicks, xScale, yScale, centerXDelta)} diff --git a/server/sonar-web/src/main/js/components/charts/ColorBoxLegend.js b/server/sonar-web/src/main/js/components/charts/ColorBoxLegend.tsx similarity index 76% rename from server/sonar-web/src/main/js/components/charts/ColorBoxLegend.js rename to server/sonar-web/src/main/js/components/charts/ColorBoxLegend.tsx index 6d21cca808f..cf2a140d2a2 100644 --- a/server/sonar-web/src/main/js/components/charts/ColorBoxLegend.js +++ b/server/sonar-web/src/main/js/components/charts/ColorBoxLegend.tsx @@ -17,22 +17,22 @@ * along with this program; if not, write to the Free Software Foundation, * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ -// @flow -import React from 'react'; -import classNames from 'classnames'; +import * as React from 'react'; +import * as classNames from 'classnames'; +import { ScaleLinear, ScaleOrdinal } from 'd3-scale'; import { formatMeasure } from '../../helpers/measures'; -/*:: type Props = { - className?: string, - colorScale: Object, - colorNA?: string, - metricType: string -}; */ +interface Props { + className?: string; + colorNA?: string; + colorScale: + | ScaleOrdinal // used for LEVEL type + | ScaleLinear; // used for RATING or PERCENT type + metricType: string; +} -export default function ColorBoxLegend( - { className, colorScale, colorNA, metricType } /*: Props */ -) { - const colorDomain = colorScale.domain(); +export default function ColorBoxLegend({ className, colorScale, colorNA, metricType }: Props) { + const colorDomain: Array = colorScale.domain(); const colorRange = colorScale.range(); return (
diff --git a/server/sonar-web/src/main/js/components/charts/ColorGradientLegend.js b/server/sonar-web/src/main/js/components/charts/ColorGradientLegend.tsx similarity index 68% rename from server/sonar-web/src/main/js/components/charts/ColorGradientLegend.js rename to server/sonar-web/src/main/js/components/charts/ColorGradientLegend.tsx index 06ca596b336..5ea00b6be59 100644 --- a/server/sonar-web/src/main/js/components/charts/ColorGradientLegend.js +++ b/server/sonar-web/src/main/js/components/charts/ColorGradientLegend.tsx @@ -17,60 +17,60 @@ * along with this program; if not, write to the Free Software Foundation, * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ -// @flow -import React from 'react'; +import * as React from 'react'; +import { ScaleLinear, ScaleOrdinal } from 'd3-scale'; -/*:: type Props = { - className?: string, - colorScale: Object, - colorNA?: string, - direction?: number, - padding?: Array, - height: number, - width: number -}; */ +interface Props { + className?: string; + colorNA?: string; + colorScale: + | ScaleOrdinal // used for LEVEL type + | ScaleLinear; // used for RATING or PERCENT type + direction?: number; + height: number; + padding?: [number, number, number, number]; + width: number; +} const NA_SPACING = 4; -export default function ColorGradientLegend( - { - className, - colorScale, - colorNA, - direction, - padding = [12, 24, 0, 0], - height, - width - } /*: Props */ -) { - const colorRange = colorScale.range(); +export default function ColorGradientLegend({ + className, + colorScale, + colorNA, + direction, + padding = [12, 24, 0, 0], + height, + width +}: Props) { + const colorRange: Array = colorScale.range(); if (direction === 1) { colorRange.reverse(); } - const colorDomain = colorScale.domain(); + const colorDomain: Array = colorScale.domain(); const lastColorIdx = colorRange.length - 1; const lastDomainIdx = colorDomain.length - 1; const widthNoPadding = width - padding[1]; const rectHeight = height - padding[0]; return ( - + {colorRange.map((color, idx) => ( - + ))} - + {colorDomain.map((d, idx) => ( + y={0}> {d} ))} @@ -79,16 +79,16 @@ export default function ColorGradientLegend( + y={0}> N/A diff --git a/server/sonar-web/src/main/js/components/charts/ColorRatingsLegend.js b/server/sonar-web/src/main/js/components/charts/ColorRatingsLegend.tsx similarity index 88% rename from server/sonar-web/src/main/js/components/charts/ColorRatingsLegend.js rename to server/sonar-web/src/main/js/components/charts/ColorRatingsLegend.tsx index 3d8a07b375c..80e44f7c11e 100644 --- a/server/sonar-web/src/main/js/components/charts/ColorRatingsLegend.js +++ b/server/sonar-web/src/main/js/components/charts/ColorRatingsLegend.tsx @@ -17,13 +17,16 @@ * along with this program; if not, write to the Free Software Foundation, * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ -// @flow -import React from 'react'; -import classNames from 'classnames'; +import * as React from 'react'; +import * as classNames from 'classnames'; import { formatMeasure } from '../../helpers/measures'; import { RATING_COLORS } from '../../helpers/constants'; -export default function ColorRatingsLegend({ className } /*: { className?: string } */) { +interface Props { + className?: string; +} + +export default function ColorRatingsLegend({ className }: Props) { return (
{[1, 2, 3, 4, 5].map(rating => ( diff --git a/server/sonar-web/src/main/js/components/charts/DonutChart.tsx b/server/sonar-web/src/main/js/components/charts/DonutChart.tsx new file mode 100644 index 00000000000..4cc5d0318f0 --- /dev/null +++ b/server/sonar-web/src/main/js/components/charts/DonutChart.tsx @@ -0,0 +1,83 @@ +/* + * SonarQube + * Copyright (C) 2009-2018 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 { arc as d3Arc, pie as d3Pie, PieArcDatum } from 'd3-shape'; + +interface DataPoint { + fill: string; + value: number; +} + +interface Props { + data: DataPoint[]; + height: number; + thickness: number; + padding?: [number, number, number, number]; + width: number; +} + +export default function DonutChart(props: Props) { + const { height, padding = [0, 0, 0, 0], width } = props; + + const availableWidth = width - padding[1] - padding[3]; + const availableHeight = height - padding[0] - padding[2]; + + const size = Math.min(availableWidth, availableHeight); + const radius = Math.floor(size / 2); + + const pie = d3Pie() + .sort(null) + .value(d => d.value); + + const sectors = pie(props.data).map((d, i) => { + return ( + + ); + }); + + return ( + + + {sectors} + + + ); +} + +interface SectorProps { + data: PieArcDatum; + fill: string; + radius: number; + thickness: number; +} + +function Sector(props: SectorProps) { + const arc = d3Arc>() + .outerRadius(props.radius) + .innerRadius(props.radius - props.thickness); + const d = arc(props.data) as string; + return ; +} diff --git a/server/sonar-web/src/main/js/components/charts/line-chart.js b/server/sonar-web/src/main/js/components/charts/LineChart.tsx similarity index 50% rename from server/sonar-web/src/main/js/components/charts/line-chart.js rename to server/sonar-web/src/main/js/components/charts/LineChart.tsx index af99622368b..2d8d5f18058 100644 --- a/server/sonar-web/src/main/js/components/charts/line-chart.js +++ b/server/sonar-web/src/main/js/components/charts/LineChart.tsx @@ -17,160 +17,161 @@ * 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 * as React from 'react'; import { extent, max } from 'd3-array'; -import { scaleLinear } from 'd3-scale'; +import { scaleLinear, ScaleLinear } from 'd3-scale'; import { area as d3Area, line as d3Line, curveBasis } from 'd3-shape'; -import { ResizeMixin } from './../mixins/resize-mixin'; - -export const LineChart = createReactClass({ - displayName: 'LineChart', - - propTypes: { - data: PropTypes.arrayOf(PropTypes.object).isRequired, - xTicks: PropTypes.arrayOf(PropTypes.any), - xValues: PropTypes.arrayOf(PropTypes.any), - padding: PropTypes.arrayOf(PropTypes.number), - backdropConstraints: PropTypes.arrayOf(PropTypes.number), - displayBackdrop: PropTypes.bool, - displayPoints: PropTypes.bool, - displayVerticalGrid: PropTypes.bool, - height: PropTypes.number - }, - - mixins: [ResizeMixin], - - getDefaultProps() { - return { - displayBackdrop: true, - displayPoints: true, - displayVerticalGrid: true, - xTicks: [], - xValues: [], - padding: [10, 10, 10, 10] - }; - }, - - getInitialState() { - return { width: this.props.width, height: this.props.height }; - }, - - renderBackdrop(xScale, yScale) { - if (!this.props.displayBackdrop) { +import { AutoSizer } from 'react-virtualized/dist/commonjs/AutoSizer'; + +interface DataPoint { + x: number; + y: number; +} + +interface Props { + backdropConstraints?: [number, number]; + data: DataPoint[]; + displayBackdrop?: boolean; + displayPoints?: boolean; + displayVerticalGrid?: boolean; + domain?: [number, number]; + height: number; + padding?: [number, number, number, number]; + width?: number; + xTicks?: {}[]; + xValues?: {}[]; +} + +export default class LineChart extends React.PureComponent { + renderBackdrop(xScale: ScaleLinear, yScale: ScaleLinear) { + const { displayBackdrop = true } = this.props; + + if (!displayBackdrop) { return null; } - const area = d3Area() + const area = d3Area() .x(d => xScale(d.x)) .y0(yScale.range()[0]) .y1(d => yScale(d.y)) .defined(d => d.y != null) .curve(curveBasis); - let data = this.props.data; + let { data } = this.props; if (this.props.backdropConstraints) { const c = this.props.backdropConstraints; data = data.filter(d => c[0] <= d.x && d.x <= c[1]); } - return ; - }, - renderPoints(xScale, yScale) { - if (!this.props.displayPoints) { + return ; + } + + renderPoints(xScale: ScaleLinear, yScale: ScaleLinear) { + const { displayPoints = true } = this.props; + + if (!displayPoints) { return null; } + const points = this.props.data.filter(point => point.y != null).map((point, index) => { const x = xScale(point.x); const y = yScale(point.y); - return ; + return ; }); return {points}; - }, + } - renderVerticalGrid(xScale, yScale) { - if (!this.props.displayVerticalGrid) { + renderVerticalGrid(xScale: ScaleLinear, yScale: ScaleLinear) { + const { displayVerticalGrid = true } = this.props; + + if (!displayVerticalGrid) { return null; } + const lines = this.props.data.map((point, index) => { const x = xScale(point.x); const y1 = yScale.range()[0]; const y2 = yScale(point.y); - return ; + return ; }); return {lines}; - }, + } - renderXTicks(xScale, yScale) { - if (!this.props.xTicks.length) { + renderXTicks(xScale: ScaleLinear, yScale: ScaleLinear) { + const { xTicks = [] } = this.props; + + if (!xTicks.length) { return null; } - const ticks = this.props.xTicks.map((tick, index) => { + + const ticks = xTicks.map((tick, index) => { const point = this.props.data[index]; const x = xScale(point.x); const y = yScale.range()[0]; return ( - + {tick} ); }); return {ticks}; - }, + } + + renderXValues(xScale: ScaleLinear, yScale: ScaleLinear) { + const { xValues = [] } = this.props; - renderXValues(xScale, yScale) { - if (!this.props.xValues.length) { + if (!xValues.length) { return null; } - const ticks = this.props.xValues.map((value, index) => { + + const ticks = xValues.map((value, index) => { const point = this.props.data[index]; const x = xScale(point.x); const y = yScale(point.y); return ( - + {value} ); }); return {ticks}; - }, + } - renderLine(xScale, yScale) { - const p = d3Line() + renderLine(xScale: ScaleLinear, yScale: ScaleLinear) { + const p = d3Line() .x(d => xScale(d.x)) .y(d => yScale(d.y)) .defined(d => d.y != null) .curve(curveBasis); - return ; - }, + return ; + } - render() { - if (!this.state.width || !this.state.height) { + renderChart = (width: number) => { + const { height, padding = [10, 10, 10, 10] } = this.props; + + if (!width || !height) { return
; } - 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 availableWidth = width - padding[1] - padding[3]; + const availableHeight = height - padding[0] - padding[2]; - let maxY; const xScale = scaleLinear() - .domain(extent(this.props.data, d => d.x)) + .domain(extent(this.props.data, d => d.x) as [number, number]) .range([0, availableWidth]); const yScale = scaleLinear().range([availableHeight, 0]); if (this.props.domain) { - maxY = this.props.domain[1]; yScale.domain(this.props.domain); } else { - maxY = max(this.props.data, d => d.y); + const maxY = max(this.props.data, d => d.y) as number; yScale.domain([0, maxY]); } return ( - - - {this.renderVerticalGrid(xScale, yScale, maxY)} + + + {this.renderVerticalGrid(xScale, yScale)} {this.renderBackdrop(xScale, yScale)} {this.renderLine(xScale, yScale)} {this.renderPoints(xScale, yScale)} @@ -179,5 +180,13 @@ export const LineChart = createReactClass({ ); + }; + + render() { + return this.props.width !== undefined ? ( + this.renderChart(this.props.width) + ) : ( + {size => this.renderChart(size.width)} + ); } -}); +} diff --git a/server/sonar-web/src/main/js/components/charts/TreeMap.js b/server/sonar-web/src/main/js/components/charts/TreeMap.tsx similarity index 81% rename from server/sonar-web/src/main/js/components/charts/TreeMap.js rename to server/sonar-web/src/main/js/components/charts/TreeMap.tsx index 2b526fd0fe2..9ee5d2320f7 100644 --- a/server/sonar-web/src/main/js/components/charts/TreeMap.js +++ b/server/sonar-web/src/main/js/components/charts/TreeMap.tsx @@ -17,33 +17,34 @@ * along with this program; if not, write to the Free Software Foundation, * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ -// @flow -import React from 'react'; +import * as React from 'react'; import { treemap as d3Treemap, hierarchy as d3Hierarchy } from 'd3-hierarchy'; import TreeMapRect from './TreeMapRect'; import { translate } from '../../helpers/l10n'; -/*:: export type TreeMapItem = { - key: string, - size: number, - color: string, - icon?: React.Element<*>, - tooltip?: string | React.Element<*>, - label: string, - link?: string -}; */ +interface TreeMapItem { + color: string; + icon?: React.ReactNode; + key: string; + label: string; + link?: string; + size: number; + tooltip?: React.ReactNode; +} -/*:: type Props = {| - items: Array, - onRectangleClick?: string => void, - height: number, - width: number -|}; */ +interface HierarchicalTreemapItem extends TreeMapItem { + children?: TreeMapItem[]; +} -export default class TreeMap extends React.PureComponent { - /*:: props: Props; */ +interface Props { + height: number; + items: TreeMapItem[]; + onRectangleClick?: (item: string) => void; + width: number; +} - mostCommitPrefix = (labels /*: Array */) => { +export default class TreeMap extends React.PureComponent { + mostCommitPrefix = (labels: string[]) => { const sortedLabels = labels.slice(0).sort(); const firstLabel = sortedLabels[0]; const firstLabelLength = firstLabel.length; @@ -76,11 +77,11 @@ export default class TreeMap extends React.PureComponent { return this.renderNoData(); } - const hierarchy = d3Hierarchy({ children: items }) + const hierarchy = d3Hierarchy({ children: items } as HierarchicalTreemapItem) .sum(d => d.size) - .sort((a, b) => b.value - a.value); + .sort((a, b) => (b.value || 0) - (a.value || 0)); - const treemap = d3Treemap() + const treemap = d3Treemap() .round(true) .size([width, height]); @@ -92,20 +93,20 @@ export default class TreeMap extends React.PureComponent {
{nodes.map(node => ( ))}
diff --git a/server/sonar-web/src/main/js/components/charts/TreeMapRect.js b/server/sonar-web/src/main/js/components/charts/TreeMapRect.tsx similarity index 80% rename from server/sonar-web/src/main/js/components/charts/TreeMapRect.js rename to server/sonar-web/src/main/js/components/charts/TreeMapRect.tsx index 9cc3c2e0738..75e8cdc8db5 100644 --- a/server/sonar-web/src/main/js/components/charts/TreeMapRect.js +++ b/server/sonar-web/src/main/js/components/charts/TreeMapRect.tsx @@ -17,42 +17,41 @@ * along with this program; if not, write to the Free Software Foundation, * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ -// @flow -import React from 'react'; +import * as React from 'react'; import { Link } from 'react-router'; -import classNames from 'classnames'; +import * as classNames from 'classnames'; import { scaleLinear } from 'd3-scale'; import LinkIcon from '../icons-components/LinkIcon'; -import Tooltip from '../controls/Tooltip'; +import Tooltip, { Placement } from '../controls/Tooltip'; const SIZE_SCALE = scaleLinear() .domain([3, 15]) .range([11, 18]) .clamp(true); -/*:: type Props = {| - x: number, - y: number, - width: number, - height: number, - fill: string, - label: string, - prefix: string, - icon?: React.Element<*>, - tooltip?: string | React.Element<*>, - itemKey: string, - link?: string, - onClick?: string => void, - placement?: string -|}; */ - -export default class TreeMapRect extends React.PureComponent { - /*:: props: Props; */ +interface Props { + fill: string; + height: number; + icon?: React.ReactNode; + itemKey: string; + label: string; + link?: string; + onClick?: (item: string) => void; + placement?: Placement; + prefix: string; + tooltip?: React.ReactNode; + width: number; + x: number; + y: number; +} - handleLinkClick = (e /*: Event */) => e.stopPropagation(); +export default class TreeMapRect extends React.PureComponent { + handleLinkClick = (event: React.MouseEvent) => { + event.stopPropagation(); + }; handleRectClick = () => { - if (this.props.onClick != null) { + if (this.props.onClick) { this.props.onClick(this.props.itemKey); } }; @@ -64,7 +63,7 @@ export default class TreeMapRect extends React.PureComponent { return null; } return ( - + ); @@ -91,9 +90,9 @@ export default class TreeMapRect extends React.PureComponent { return (
{isIconVisible && ( diff --git a/server/sonar-web/src/main/js/components/charts/__tests__/bar-chart-test.js b/server/sonar-web/src/main/js/components/charts/__tests__/BarChart-test.tsx similarity index 80% rename from server/sonar-web/src/main/js/components/charts/__tests__/bar-chart-test.js rename to server/sonar-web/src/main/js/components/charts/__tests__/BarChart-test.tsx index 933ac88ca14..0b109c12b0a 100644 --- a/server/sonar-web/src/main/js/components/charts/__tests__/bar-chart-test.js +++ b/server/sonar-web/src/main/js/components/charts/__tests__/BarChart-test.tsx @@ -17,13 +17,13 @@ * 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 * as React from 'react'; import { shallow } from 'enzyme'; -import { BarChart } from '../bar-chart'; +import BarChart from '../BarChart'; it('should display bars', () => { const data = [{ x: 1, y: 10 }, { x: 2, y: 30 }, { x: 3, y: 20 }]; - const chart = shallow(); + const chart = shallow(); expect(chart.find('.bar-chart-bar').length).toBe(3); }); @@ -31,7 +31,7 @@ it('should display ticks', () => { const data = [{ x: 1, y: 10 }, { x: 2, y: 30 }, { x: 3, y: 20 }]; const ticks = ['A', 'B', 'C']; const chart = shallow( - + ); expect(chart.find('.bar-chart-tick').length).toBe(3); }); @@ -40,7 +40,7 @@ it('should display values', () => { const data = [{ x: 1, y: 10 }, { x: 2, y: 30 }, { x: 3, y: 20 }]; const values = ['A', 'B', 'C']; const chart = shallow( - + ); expect(chart.find('.bar-chart-tick').length).toBe(3); }); @@ -50,7 +50,7 @@ it('should display bars, ticks and values', () => { const ticks = ['A', 'B', 'C']; const values = ['A', 'B', 'C']; const chart = shallow( - + ); expect(chart.find('.bar-chart-bar').length).toBe(3); expect(chart.find('.bar-chart-tick').length).toBe(6); diff --git a/server/sonar-web/src/main/js/components/charts/__tests__/BubbleChart-test.js b/server/sonar-web/src/main/js/components/charts/__tests__/BubbleChart-test.tsx similarity index 87% rename from server/sonar-web/src/main/js/components/charts/__tests__/BubbleChart-test.js rename to server/sonar-web/src/main/js/components/charts/__tests__/BubbleChart-test.tsx index 347d1df976e..07c73da2b87 100644 --- a/server/sonar-web/src/main/js/components/charts/__tests__/BubbleChart-test.js +++ b/server/sonar-web/src/main/js/components/charts/__tests__/BubbleChart-test.tsx @@ -17,25 +17,25 @@ * 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 * as React from 'react'; import { mount } from 'enzyme'; import BubbleChart, { Bubble } from '../BubbleChart'; it('should display bubbles', () => { const items = [{ x: 1, y: 10, size: 7 }, { x: 2, y: 30, size: 5 }]; - const chart = mount(); + const chart = mount(); chart.find(Bubble).forEach(bubble => expect(bubble).toMatchSnapshot()); }); it('should render bubble links', () => { const items = [{ x: 1, y: 10, size: 7, link: 'foo' }, { x: 2, y: 30, size: 5, link: 'bar' }]; - const chart = mount(); + const chart = mount(); chart.find(Bubble).forEach(bubble => expect(bubble).toMatchSnapshot()); }); it('should render bubbles with click handlers', () => { const onClick = jest.fn(); const items = [{ x: 1, y: 10, size: 7, link: 'foo' }, { x: 2, y: 30, size: 5, link: 'bar' }]; - const chart = mount(); + const chart = mount(); chart.find(Bubble).forEach(bubble => expect(bubble).toMatchSnapshot()); }); diff --git a/server/sonar-web/src/main/js/components/charts/__tests__/line-chart-test.js b/server/sonar-web/src/main/js/components/charts/__tests__/LineChart-test.tsx similarity index 81% rename from server/sonar-web/src/main/js/components/charts/__tests__/line-chart-test.js rename to server/sonar-web/src/main/js/components/charts/__tests__/LineChart-test.tsx index 9b2b0018c8a..533bd5aeddb 100644 --- a/server/sonar-web/src/main/js/components/charts/__tests__/line-chart-test.js +++ b/server/sonar-web/src/main/js/components/charts/__tests__/LineChart-test.tsx @@ -17,26 +17,26 @@ * 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 * as React from 'react'; import { shallow } from 'enzyme'; -import { LineChart } from '../line-chart'; +import LineChart from '../LineChart'; it('should display line', () => { const data = [{ x: 1, y: 10 }, { x: 2, y: 30 }, { x: 3, y: 20 }]; - const chart = shallow(); + const chart = shallow(); expect(chart.find('.line-chart-path').length).toBe(1); }); it('should display ticks', () => { const data = [{ x: 1, y: 10 }, { x: 2, y: 30 }, { x: 3, y: 20 }]; const ticks = ['A', 'B', 'C']; - const chart = shallow(); + const chart = shallow(); expect(chart.find('.line-chart-tick').length).toBe(3); }); it('should display values', () => { const data = [{ x: 1, y: 10 }, { x: 2, y: 30 }, { x: 3, y: 20 }]; const values = ['A', 'B', 'C']; - const chart = shallow(); + const chart = shallow(); expect(chart.find('.line-chart-tick').length).toBe(3); }); diff --git a/server/sonar-web/src/main/js/components/charts/__tests__/TreeMap-test.js b/server/sonar-web/src/main/js/components/charts/__tests__/TreeMap-test.tsx similarity index 91% rename from server/sonar-web/src/main/js/components/charts/__tests__/TreeMap-test.js rename to server/sonar-web/src/main/js/components/charts/__tests__/TreeMap-test.tsx index 246f3a840b8..96fbe933b41 100644 --- a/server/sonar-web/src/main/js/components/charts/__tests__/TreeMap-test.js +++ b/server/sonar-web/src/main/js/components/charts/__tests__/TreeMap-test.tsx @@ -17,7 +17,7 @@ * 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 * as React from 'react'; import { shallow } from 'enzyme'; import TreeMap from '../TreeMap'; @@ -28,7 +28,7 @@ it('should display', () => { { key: '3', size: 20, color: '#777', label: 'SonarQube :: Search' } ]; const chart = shallow( - {}} /> + {}} width={100} /> ); expect(chart.find('TreeMapRect')).toHaveLength(3); }); diff --git a/server/sonar-web/src/main/js/components/charts/__tests__/__snapshots__/BubbleChart-test.js.snap b/server/sonar-web/src/main/js/components/charts/__tests__/__snapshots__/BubbleChart-test.tsx.snap similarity index 100% rename from server/sonar-web/src/main/js/components/charts/__tests__/__snapshots__/BubbleChart-test.js.snap rename to server/sonar-web/src/main/js/components/charts/__tests__/__snapshots__/BubbleChart-test.tsx.snap diff --git a/server/sonar-web/src/main/js/components/charts/bar-chart.js b/server/sonar-web/src/main/js/components/charts/bar-chart.js deleted file mode 100644 index c137b2f74e9..00000000000 --- a/server/sonar-web/src/main/js/components/charts/bar-chart.js +++ /dev/null @@ -1,187 +0,0 @@ -/* - * SonarQube - * Copyright (C) 2009-2018 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 Tooltip from '../controls/Tooltip'; -import { ResizeMixin } from '../mixins/resize-mixin'; - -export const BarChart = createReactClass({ - displayName: 'BarChart', - - propTypes: { - data: PropTypes.arrayOf(PropTypes.object).isRequired, - xTicks: PropTypes.arrayOf(PropTypes.any), - xValues: PropTypes.arrayOf(PropTypes.any), - height: PropTypes.number, - padding: PropTypes.arrayOf(PropTypes.number), - barsWidth: PropTypes.number.isRequired, - onBarClick: PropTypes.func - }, - - mixins: [ResizeMixin], - - getDefaultProps() { - return { - xTicks: [], - xValues: [], - padding: [10, 10, 10, 10] - }; - }, - - getInitialState() { - return { width: this.props.width, height: this.props.height }; - }, - - componentDidUpdate(prevProps) { - if (this.props.width && prevProps.width !== this.props.width) { - this.setState({ width: this.props.width }); - } - if (this.props.height && prevProps.height !== this.props.height) { - this.setState({ height: this.props.height }); - } - }, - - handleClick(point) { - this.props.onBarClick(point); - }, - - renderXTicks(xScale, yScale) { - if (!this.props.xTicks.length) { - return null; - } - const ticks = this.props.xTicks.map((tick, index) => { - const point = this.props.data[index]; - const x = Math.round(xScale(point.x) + xScale.bandwidth() / 2); - const y = yScale.range()[0]; - const d = this.props.data[index]; - const text = ( - - {tick} - - ); - return ( - - {text} - - ); - }); - return {ticks}; - }, - - renderXValues(xScale, yScale) { - if (!this.props.xValues.length) { - return null; - } - const ticks = this.props.xValues.map((value, index) => { - const point = this.props.data[index]; - const x = Math.round(xScale(point.x) + xScale.bandwidth() / 2); - const y = yScale(point.y); - const d = this.props.data[index]; - const text = ( - - {value} - - ); - return ( - - {text} - - ); - }); - return {ticks}; - }, - - renderBars(xScale, yScale) { - const bars = this.props.data.map((d, index) => { - const x = Math.round(xScale(d.x)); - const maxY = yScale.range()[0]; - const y = Math.round(yScale(d.y)) - /* minimum bar height */ 1; - const height = maxY - y; - const rect = ( - - ); - return ( - - {rect} - - ); - }); - return {bars}; - }, - - render() { - if (!this.state.width || !this.state.height) { - return
; - } - - 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 innerPadding = - (availableWidth - this.props.barsWidth * this.props.data.length) / - (this.props.data.length - 1); - const relativeInnerPadding = innerPadding / (innerPadding + this.props.barsWidth); - - const maxY = max(this.props.data, d => d.y); - const xScale = scaleBand() - .domain(this.props.data.map(d => d.x)) - .range([0, availableWidth]) - .paddingInner(relativeInnerPadding); - const yScale = scaleLinear() - .domain([0, maxY]) - .range([availableHeight, 0]); - - return ( - - - {this.renderXTicks(xScale, yScale)} - {this.renderXValues(xScale, yScale)} - {this.renderBars(xScale, yScale)} - - - ); - } -}); diff --git a/server/sonar-web/src/main/js/components/charts/donut-chart.js b/server/sonar-web/src/main/js/components/charts/donut-chart.js deleted file mode 100644 index 08fd9b0bb24..00000000000 --- a/server/sonar-web/src/main/js/components/charts/donut-chart.js +++ /dev/null @@ -1,84 +0,0 @@ -/* - * SonarQube - * Copyright (C) 2009-2018 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 { arc as d3Arc, pie as d3Pie } from 'd3-shape'; -import { ResizeMixin } from './../mixins/resize-mixin'; - -function Sector(props) { - const arc = d3Arc() - .outerRadius(props.radius) - .innerRadius(props.radius - props.thickness); - return ; -} - -export const DonutChart = createReactClass({ - displayName: 'DonutChart', - - propTypes: { - data: PropTypes.arrayOf(PropTypes.object).isRequired - }, - - mixins: [ResizeMixin], - - getDefaultProps() { - return { thickness: 6, padding: [0, 0, 0, 0] }; - }, - - getInitialState() { - return { width: this.props.width, height: this.props.height }; - }, - - render() { - if (!this.state.width || !this.state.height) { - return
; - } - - 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 size = Math.min(availableWidth, availableHeight); - const radius = Math.floor(size / 2); - - const pie = d3Pie() - .sort(null) - .value(d => d.value); - const sectors = pie(this.props.data).map((d, i) => { - return ( - - ); - }); - - return ( - - - {sectors} - - - ); - } -}); diff --git a/server/sonar-web/src/main/js/components/common/LocationIndex.js b/server/sonar-web/src/main/js/components/common/LocationIndex.tsx similarity index 75% rename from server/sonar-web/src/main/js/components/common/LocationIndex.js rename to server/sonar-web/src/main/js/components/common/LocationIndex.tsx index f3ae1bf7e88..15b143e7876 100644 --- a/server/sonar-web/src/main/js/components/common/LocationIndex.js +++ b/server/sonar-web/src/main/js/components/common/LocationIndex.tsx @@ -17,35 +17,31 @@ * along with this program; if not, write to the Free Software Foundation, * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ -// @flow -import React from 'react'; -import classNames from 'classnames'; +import * as React from 'react'; +import * as classNames from 'classnames'; import './LocationIndex.css'; -/*:: -type Props = { - children?: React.Element<*>, - leading?: boolean, - onClick?: () => void, - selected?: boolean -}; -*/ +interface Props { + children?: React.ReactNode; + leading?: boolean; + onClick?: () => void; + selected?: boolean; + [x: string]: any; +} -export default function LocationIndex(props /*: Props */) { +export default function LocationIndex(props: Props) { const { children, leading, onClick, selected, ...other } = props; const clickAttributes = onClick ? { onClick, role: 'button', tabIndex: 0 } : {}; - // put {...others} because Tooltip sets some event handlers return (
{children}
); } - -LocationIndex.defaultProps = { - selected: false -}; diff --git a/server/sonar-web/src/main/js/components/common/LocationMessage.js b/server/sonar-web/src/main/js/components/common/LocationMessage.tsx similarity index 82% rename from server/sonar-web/src/main/js/components/common/LocationMessage.js rename to server/sonar-web/src/main/js/components/common/LocationMessage.tsx index 491ac9e5460..bcee4be2683 100644 --- a/server/sonar-web/src/main/js/components/common/LocationMessage.js +++ b/server/sonar-web/src/main/js/components/common/LocationMessage.tsx @@ -17,19 +17,16 @@ * along with this program; if not, write to the Free Software Foundation, * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ -// @flow -import React from 'react'; -import classNames from 'classnames'; +import * as React from 'react'; +import * as classNames from 'classnames'; import './LocationMessage.css'; -/*:: -type Props = { - children?: React.Element<*>, - selected: boolean -}; -*/ +interface Props { + children?: React.ReactNode; + selected: boolean; +} -export default function LocationMessage(props /*: Props */) { +export default function LocationMessage(props: Props) { return (
{props.children} diff --git a/server/sonar-web/src/main/js/components/common/OrganizationHelmet.js b/server/sonar-web/src/main/js/components/common/OrganizationHelmet.tsx similarity index 89% rename from server/sonar-web/src/main/js/components/common/OrganizationHelmet.js rename to server/sonar-web/src/main/js/components/common/OrganizationHelmet.tsx index d4b52885ac0..e842eaae6c7 100644 --- a/server/sonar-web/src/main/js/components/common/OrganizationHelmet.js +++ b/server/sonar-web/src/main/js/components/common/OrganizationHelmet.tsx @@ -17,18 +17,15 @@ * along with this program; if not, write to the Free Software Foundation, * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ -// @flow -import React from 'react'; +import * as React from 'react'; import Helmet from 'react-helmet'; -/*:: -type Props = { - title: string, - organization?: ?{ name: string } -}; -*/ +interface Props { + organization?: { name: string }; + title: string; +} -export default function OrganizationHelmet({ title, organization } /*: Props */) { +export default function OrganizationHelmet({ title, organization }: Props) { const defaultTitle = title + (organization ? ' - ' + organization.name : ''); return ; } diff --git a/server/sonar-web/src/main/js/components/common/UpgradeOrganizationBox.js b/server/sonar-web/src/main/js/components/common/UpgradeOrganizationBox.tsx similarity index 86% rename from server/sonar-web/src/main/js/components/common/UpgradeOrganizationBox.js rename to server/sonar-web/src/main/js/components/common/UpgradeOrganizationBox.tsx index b6934cea2c6..baeb9ad0dba 100644 --- a/server/sonar-web/src/main/js/components/common/UpgradeOrganizationBox.js +++ b/server/sonar-web/src/main/js/components/common/UpgradeOrganizationBox.tsx @@ -17,19 +17,16 @@ * along with this program; if not, write to the Free Software Foundation, * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ -// @flow -import React from 'react'; +import * as React from 'react'; import { Link } from 'react-router'; import { translate, hasMessage } from '../../helpers/l10n'; import './UpgradeOrganizationBox.css'; -/*:: -type Props = { - organization: string -}; -*/ +interface Props { + organization: string; +} -export default function UpgradeOrganizationBox(props /*: Props */) { +export default function UpgradeOrganizationBox({ organization }: Props) { return (

{translate('billing.upgrade_box.header')}

@@ -41,7 +38,7 @@ export default function UpgradeOrganizationBox(props /*: Props */) { {translate('billing.upgrade_box.button')} diff --git a/server/sonar-web/src/main/js/components/controls/GlobalMessages.js b/server/sonar-web/src/main/js/components/controls/GlobalMessages.tsx similarity index 68% rename from server/sonar-web/src/main/js/components/controls/GlobalMessages.js rename to server/sonar-web/src/main/js/components/controls/GlobalMessages.tsx index 0d0782009b7..95ee65b06e2 100644 --- a/server/sonar-web/src/main/js/components/controls/GlobalMessages.js +++ b/server/sonar-web/src/main/js/components/controls/GlobalMessages.tsx @@ -17,28 +17,26 @@ * 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 PropTypes from 'prop-types'; -import classNames from 'classnames'; -import { ERROR, SUCCESS } from '../../store/globalMessages/duck'; +import * as React from 'react'; +import * as classNames from 'classnames'; import { Button } from '../ui/buttons'; -export default class GlobalMessages extends React.PureComponent { - static propTypes = { - messages: PropTypes.arrayOf( - PropTypes.shape({ - id: PropTypes.string.isRequired, - message: PropTypes.string.isRequired, - level: PropTypes.oneOf([ERROR, SUCCESS]) - }) - ), - closeGlobalMessage: PropTypes.func.isRequired - }; +interface Message { + id: string; + level: 'ERROR' | 'SUCCESS'; + message: string; +} + +interface Props { + closeGlobalMessage: (id: string) => void; + messages: Message[]; +} - renderMessage = message => { +export default class GlobalMessages extends React.PureComponent { + renderMessage = (message: Message) => { const className = classNames('process-spinner', 'shown', { - 'process-spinner-failed': message.level === ERROR, - 'process-spinner-success': message.level === SUCCESS + 'process-spinner-failed': message.level === 'ERROR', + 'process-spinner-success': message.level === 'SUCCESS' }); return (
diff --git a/server/sonar-web/src/main/js/components/controls/Toggle.js b/server/sonar-web/src/main/js/components/controls/Toggle.tsx similarity index 67% rename from server/sonar-web/src/main/js/components/controls/Toggle.js rename to server/sonar-web/src/main/js/components/controls/Toggle.tsx index 4cbabfece09..b35d121f9e8 100644 --- a/server/sonar-web/src/main/js/components/controls/Toggle.js +++ b/server/sonar-web/src/main/js/components/controls/Toggle.tsx @@ -17,36 +17,36 @@ * 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 PropTypes from 'prop-types'; -import classNames from 'classnames'; +import * as React from 'react'; +import * as classNames from 'classnames'; import { Button } from '../ui/buttons'; import './styles.css'; -export default class Toggle extends React.PureComponent { - static propTypes = { - value: PropTypes.oneOfType([PropTypes.string, PropTypes.bool]).isRequired, - name: PropTypes.string, - onChange: PropTypes.func +export interface Props { + name?: string; + onChange?: (value: boolean) => void; + value: boolean | string; +} + +export default class Toggle extends React.PureComponent { + getValue = () => { + const { value } = this.props; + return typeof value === 'string' ? value === 'true' : value; }; - handleClick = value => { + handleClick = () => { if (this.props.onChange) { + const value = this.getValue(); this.props.onChange(!value); } }; render() { - const { value } = this.props; - const booleanValue = typeof value === 'string' ? value === 'true' : value; - - const className = classNames('boolean-toggle', { 'boolean-toggle-on': booleanValue }); + const value = this.getValue(); + const className = classNames('boolean-toggle', { 'boolean-toggle-on': value }); return ( - ); diff --git a/server/sonar-web/src/main/js/components/controls/__tests__/ListFooter-test.js b/server/sonar-web/src/main/js/components/controls/__tests__/ListFooter-test.tsx similarity index 88% rename from server/sonar-web/src/main/js/components/controls/__tests__/ListFooter-test.js rename to server/sonar-web/src/main/js/components/controls/__tests__/ListFooter-test.tsx index 5c2e7458776..b8de25e41f1 100644 --- a/server/sonar-web/src/main/js/components/controls/__tests__/ListFooter-test.js +++ b/server/sonar-web/src/main/js/components/controls/__tests__/ListFooter-test.tsx @@ -17,8 +17,8 @@ * 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 React from 'react'; import ListFooter from '../ListFooter'; import { click } from '../../../helpers/testUtils'; @@ -33,13 +33,13 @@ it('should not render "show more"', () => { }); it('should not render "show more"', () => { - const listFooter = shallow(); + const listFooter = shallow(); expect(listFooter.find('a').length).toBe(0); }); it('should "show more"', () => { const loadMore = jest.fn(); - const listFooter = shallow(); + const listFooter = shallow(); const link = listFooter.find('a'); expect(link.length).toBe(1); click(link); diff --git a/server/sonar-web/src/main/js/components/controls/__tests__/Toggle-test.js b/server/sonar-web/src/main/js/components/controls/__tests__/Toggle-test.tsx similarity index 80% rename from server/sonar-web/src/main/js/components/controls/__tests__/Toggle-test.js rename to server/sonar-web/src/main/js/components/controls/__tests__/Toggle-test.tsx index 3245074388a..7d3e8ae2d0d 100644 --- a/server/sonar-web/src/main/js/components/controls/__tests__/Toggle-test.js +++ b/server/sonar-web/src/main/js/components/controls/__tests__/Toggle-test.tsx @@ -17,23 +17,23 @@ * 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 React from 'react'; -import Toggle from '../Toggle'; +import Toggle, { Props } from '../Toggle'; import { click } from '../../../helpers/testUtils'; -function getSample(props) { - return true} value={true} {...props} />; -} - it('should render', () => { - const Toggle = shallow(getSample()); + const Toggle = shallowRender(); expect(Toggle.is('Button')).toBe(true); }); it('should call onChange', () => { const onChange = jest.fn(); - const Toggle = shallow(getSample({ onChange })); + const Toggle = shallowRender({ onChange }); click(Toggle); expect(onChange).toBeCalledWith(false); }); + +function shallowRender(props?: Partial) { + return shallow( true} value={true} {...props} />); +} diff --git a/server/sonar-web/src/main/js/components/ui/BugTrackerIcon.js b/server/sonar-web/src/main/js/components/icons-components/BugTrackerIcon.tsx similarity index 58% rename from server/sonar-web/src/main/js/components/ui/BugTrackerIcon.js rename to server/sonar-web/src/main/js/components/icons-components/BugTrackerIcon.tsx index 45b233ba806..f42e84aaa6e 100644 --- a/server/sonar-web/src/main/js/components/ui/BugTrackerIcon.js +++ b/server/sonar-web/src/main/js/components/icons-components/BugTrackerIcon.tsx @@ -17,25 +17,23 @@ * along with this program; if not, write to the Free Software Foundation, * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ -// @flow -import React from 'react'; +import * as React from 'react'; +import { IconProps } from './types'; -export default function BugTrackerIcon() { - /* eslint-disable max-len */ +export default function BugTrackerIcon({ className, fill = 'currentColor', size = 16 }: IconProps) { return ( - - - - + + ); } diff --git a/server/sonar-web/src/main/js/components/common/ClockIcon.js b/server/sonar-web/src/main/js/components/icons-components/ClockIcon.tsx similarity index 70% rename from server/sonar-web/src/main/js/components/common/ClockIcon.js rename to server/sonar-web/src/main/js/components/icons-components/ClockIcon.tsx index e0e696128aa..d5da0ec03e9 100644 --- a/server/sonar-web/src/main/js/components/common/ClockIcon.js +++ b/server/sonar-web/src/main/js/components/icons-components/ClockIcon.tsx @@ -17,33 +17,24 @@ * along with this program; if not, write to the Free Software Foundation, * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ -// @flow -import React from 'react'; -import classNames from 'classnames'; +import * as React from 'react'; +import * as classNames from 'classnames'; +import { IconProps } from './types'; -/*:: -type Props = { - className?: string, - size?: number -}; -*/ - -export default function ClockIcon(props /*: Props */) { - /* eslint max-len: 0 */ +export default function ClockIcon({ className, size = 16 }: IconProps) { return ( + width={size} + xmlSpace="preserve" + xmlnsXlink="http://www.w3.org/1999/xlink"> - + ); } - -ClockIcon.defaultProps = { - size: 16 -}; diff --git a/server/sonar-web/src/main/js/components/icons-components/PinIcon.tsx b/server/sonar-web/src/main/js/components/icons-components/PinIcon.tsx new file mode 100644 index 00000000000..3e83487feeb --- /dev/null +++ b/server/sonar-web/src/main/js/components/icons-components/PinIcon.tsx @@ -0,0 +1,39 @@ +/* + * SonarQube + * Copyright (C) 2009-2018 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 { IconProps } from './types'; + +export default function PinIcon({ className, fill = 'currentColor', size = 16 }: IconProps) { + return ( + + ; + + ); +} diff --git a/server/sonar-web/src/main/js/components/issue/components/__tests__/__snapshots__/IssueTitleBar-test.js.snap b/server/sonar-web/src/main/js/components/issue/components/__tests__/__snapshots__/IssueTitleBar-test.js.snap index 8f3694dd8d0..93d4a3f72d2 100644 --- a/server/sonar-web/src/main/js/components/issue/components/__tests__/__snapshots__/IssueTitleBar-test.js.snap +++ b/server/sonar-web/src/main/js/components/issue/components/__tests__/__snapshots__/IssueTitleBar-test.js.snap @@ -1,9 +1,7 @@ // Jest Snapshot v1, https://goo.gl/fbAQLP exports[`should count all code locations 1`] = ` - + 7 `; diff --git a/server/sonar-web/src/main/js/components/shared/SeverityIcon.js b/server/sonar-web/src/main/js/components/shared/SeverityIcon.tsx similarity index 73% rename from server/sonar-web/src/main/js/components/shared/SeverityIcon.js rename to server/sonar-web/src/main/js/components/shared/SeverityIcon.tsx index 4a7575edab2..38ee50884a4 100644 --- a/server/sonar-web/src/main/js/components/shared/SeverityIcon.js +++ b/server/sonar-web/src/main/js/components/shared/SeverityIcon.tsx @@ -17,14 +17,19 @@ * along with this program; if not, write to the Free Software Foundation, * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ -//@flow -import React from 'react'; -import classNames from 'classnames'; +import * as React from 'react'; +import * as classNames from 'classnames'; -export default function SeverityIcon(props /*: { severity: ?string, className?: string } */) { +interface Props { + className?: string; + severity: string | null | undefined; +} + +export default function SeverityIcon(props: Props) { if (!props.severity) { return null; } - const className = classNames('icon-severity-' + props.severity.toLowerCase(), props.className); - return ; + return ( + + ); } diff --git a/server/sonar-web/src/main/js/components/shared/StatusHelper.js b/server/sonar-web/src/main/js/components/shared/StatusHelper.tsx similarity index 80% rename from server/sonar-web/src/main/js/components/shared/StatusHelper.js rename to server/sonar-web/src/main/js/components/shared/StatusHelper.tsx index 2c2532605ad..b6921effdac 100644 --- a/server/sonar-web/src/main/js/components/shared/StatusHelper.js +++ b/server/sonar-web/src/main/js/components/shared/StatusHelper.tsx @@ -17,20 +17,18 @@ * along with this program; if not, write to the Free Software Foundation, * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ -//@flow -import React from 'react'; +import * as React from 'react'; import StatusIcon from './StatusIcon'; import { translate } from '../../helpers/l10n'; -export default function StatusHelper( - props /*: { - resolution?: string, - status: string, - className?: string -} */ -) { - const resolution = - props.resolution != null && ` (${translate('issue.resolution', props.resolution)})`; +interface Props { + className?: string; + resolution: string | undefined; + status: string; +} + +export default function StatusHelper(props: Props) { + const resolution = props.resolution && ` (${translate('issue.resolution', props.resolution)})`; return ( diff --git a/server/sonar-web/src/main/js/components/shared/StatusIcon.js b/server/sonar-web/src/main/js/components/shared/StatusIcon.tsx similarity index 74% rename from server/sonar-web/src/main/js/components/shared/StatusIcon.js rename to server/sonar-web/src/main/js/components/shared/StatusIcon.tsx index ed8c418ee31..d043b034406 100644 --- a/server/sonar-web/src/main/js/components/shared/StatusIcon.js +++ b/server/sonar-web/src/main/js/components/shared/StatusIcon.tsx @@ -17,11 +17,14 @@ * along with this program; if not, write to the Free Software Foundation, * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ -//@flow -import React from 'react'; -import classNames from 'classnames'; +import * as React from 'react'; +import * as classNames from 'classnames'; -export default function StatusIcon(props /*: { status: string, className?: string } */) { - const className = classNames('icon-status-' + props.status.toLowerCase(), props.className); - return ; +interface Props { + className?: string; + status: string; +} + +export default function StatusIcon({ className, status }: Props) { + return ; } diff --git a/server/sonar-web/src/main/js/components/shared/WithStore.js b/server/sonar-web/src/main/js/components/shared/WithStore.js deleted file mode 100644 index 2cdf9bb3b20..00000000000 --- a/server/sonar-web/src/main/js/components/shared/WithStore.js +++ /dev/null @@ -1,52 +0,0 @@ -/* - * SonarQube - * Copyright (C) 2009-2018 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. - */ -// @flow -import React from 'react'; -import PropTypes from 'prop-types'; -import getStore from '../../app/utils/getStore'; - -/*:: -type Props = { - children: React.Element<*> -}; -*/ - -export default class WithStore extends React.PureComponent { - /*:: props: Props; */ - /*:: store: {}; -*/ - - static childContextTypes = { - store: PropTypes.object - }; - - constructor(props /*: Props */) { - super(props); - this.store = getStore(); - } - - getChildContext() { - return { store: this.store }; - } - - render() { - return this.props.children; - } -} diff --git a/server/sonar-web/src/main/js/components/shared/__tests__/QualifierIcon-test.js b/server/sonar-web/src/main/js/components/shared/__tests__/QualifierIcon-test.tsx similarity index 97% rename from server/sonar-web/src/main/js/components/shared/__tests__/QualifierIcon-test.js rename to server/sonar-web/src/main/js/components/shared/__tests__/QualifierIcon-test.tsx index 32097c900fd..06169277552 100644 --- a/server/sonar-web/src/main/js/components/shared/__tests__/QualifierIcon-test.js +++ b/server/sonar-web/src/main/js/components/shared/__tests__/QualifierIcon-test.tsx @@ -17,7 +17,7 @@ * 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 * as React from 'react'; import { shallow } from 'enzyme'; import QualifierIcon from '../QualifierIcon'; diff --git a/server/sonar-web/src/main/js/components/shared/__tests__/__snapshots__/QualifierIcon-test.js.snap b/server/sonar-web/src/main/js/components/shared/__tests__/__snapshots__/QualifierIcon-test.tsx.snap similarity index 100% rename from server/sonar-web/src/main/js/components/shared/__tests__/__snapshots__/QualifierIcon-test.js.snap rename to server/sonar-web/src/main/js/components/shared/__tests__/__snapshots__/QualifierIcon-test.tsx.snap diff --git a/server/sonar-web/src/main/js/components/shared/pin-icon.js b/server/sonar-web/src/main/js/components/shared/pin-icon.js deleted file mode 100644 index 9435d00ee8c..00000000000 --- a/server/sonar-web/src/main/js/components/shared/pin-icon.js +++ /dev/null @@ -1,33 +0,0 @@ -/* - * SonarQube - * Copyright (C) 2009-2018 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. - */ -/* eslint max-len: 0 */ -import React from 'react'; -import * as theme from '../../app/theme'; - -export default function PinIcon() { - return ( - - - - ); -} diff --git a/server/sonar-web/src/main/js/components/ui/CoverageRating.tsx b/server/sonar-web/src/main/js/components/ui/CoverageRating.tsx index 7439b850ff3..10ffc9e849b 100644 --- a/server/sonar-web/src/main/js/components/ui/CoverageRating.tsx +++ b/server/sonar-web/src/main/js/components/ui/CoverageRating.tsx @@ -18,7 +18,7 @@ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ import * as React from 'react'; -import { DonutChart } from '../charts/donut-chart'; +import DonutChart from '../charts/DonutChart'; import * as theme from '../../app/theme'; const SIZE_TO_WIDTH_MAPPING = { small: 16, normal: 24, big: 40, huge: 60 }; @@ -45,5 +45,5 @@ export default function CoverageRating({ muted = false, size = 'normal', value } const width = SIZE_TO_WIDTH_MAPPING[size]; const thickness = SIZE_TO_THICKNESS_MAPPING[size]; - return ; + return ; } diff --git a/server/sonar-web/src/main/js/components/ui/__tests__/Level-test.js b/server/sonar-web/src/main/js/components/ui/__tests__/Level-test.tsx similarity index 97% rename from server/sonar-web/src/main/js/components/ui/__tests__/Level-test.js rename to server/sonar-web/src/main/js/components/ui/__tests__/Level-test.tsx index 3a315a07599..7706ee2414b 100644 --- a/server/sonar-web/src/main/js/components/ui/__tests__/Level-test.js +++ b/server/sonar-web/src/main/js/components/ui/__tests__/Level-test.tsx @@ -17,8 +17,8 @@ * 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 React from 'react'; import Level from '../Level'; it('should render', () => { diff --git a/server/sonar-web/src/main/js/components/ui/buttons.tsx b/server/sonar-web/src/main/js/components/ui/buttons.tsx index 82992ba0a64..1ede3bb8ce1 100644 --- a/server/sonar-web/src/main/js/components/ui/buttons.tsx +++ b/server/sonar-web/src/main/js/components/ui/buttons.tsx @@ -32,6 +32,7 @@ interface ButtonProps { disabled?: boolean; id?: string; innerRef?: (node: HTMLElement | null) => void; + name?: string; onClick?: (event: React.MouseEvent) => void; preventDefault?: boolean; stopPropagation?: boolean; diff --git a/server/sonar-web/src/main/js/libs/third-party/VSS.SDK.min.js b/server/sonar-web/src/main/js/libs/third-party/VSS.SDK.min.js deleted file mode 100644 index 5f6a76663bd..00000000000 --- a/server/sonar-web/src/main/js/libs/third-party/VSS.SDK.min.js +++ /dev/null @@ -1,957 +0,0 @@ -/* - * SonarQube - * Copyright (C) 2009-2018 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. - */ -// Copyright (C) Microsoft Corporation. All rights reserved. -var XDM, VSS; -(function(n) { - function u() { - return new o(); - } - function s() { - return ( - Math.floor(Math.random() * (f - t) + t).toString(36) + - Math.floor(Math.random() * (f - t) + t).toString(36) - ); - } - var i, r, e; - n.createDeferred = u; - var o = (function() { - function n() { - var n = this; - this._resolveCallbacks = []; - this._rejectCallbacks = []; - this._isResolved = !1; - this._isRejected = !1; - this.resolve = function(t) { - n._resolve(t); - }; - this.reject = function(t) { - n._reject(t); - }; - this.promise = {}; - this.promise.then = function(t, i) { - return n._then(t, i); - }; - } - return ( - (n.prototype._then = function(t, i) { - var u = this, - r; - return (!t && !i) || (this._isResolved && !t) || (this._isRejected && !i) - ? this.promise - : ((r = new n()), - this._resolveCallbacks.push(function(n) { - u._wrapCallback(t, n, r, !1); - }), - this._rejectCallbacks.push(function(n) { - u._wrapCallback(i, n, r, !0); - }), - this._isResolved - ? this._resolve(this._resolvedValue) - : this._isRejected && this._reject(this._rejectValue), - r.promise); - }), - (n.prototype._wrapCallback = function(n, t, i, r) { - if (!n) { - r ? i.reject(t) : i.resolve(t); - return; - } - var u; - try { - u = n(t); - } catch (f) { - i.reject(f); - return; - } - u === undefined - ? i.resolve(t) - : u && typeof u.then == 'function' - ? u.then( - function(n) { - i.resolve(n); - }, - function(n) { - i.reject(n); - } - ) - : i.resolve(u); - }), - (n.prototype._resolve = function(n) { - if ( - (this._isRejected || - this._isResolved || - ((this._isResolved = !0), (this._resolvedValue = n)), - this._isResolved && this._resolveCallbacks.length > 0) - ) { - var t = this._resolveCallbacks.splice(0); - window.setTimeout(function() { - for (var i = 0, r = t.length; i < r; i++) t[i](n); - }); - } - }), - (n.prototype._reject = function(n) { - if ( - (this._isRejected || - this._isResolved || - ((this._isRejected = !0), - (this._rejectValue = n), - this._rejectCallbacks.length === 0 && - window.console && - window.console.warn && - (console.warn('Rejected XDM promise with no reject callbacks'), - n && console.warn(n))), - this._isRejected && this._rejectCallbacks.length > 0) - ) { - var t = this._rejectCallbacks.splice(0); - window.setTimeout(function() { - for (var i = 0, r = t.length; i < r; i++) t[i](n); - }); - } - }), - n - ); - })(), - t = parseInt('10000000000', 36), - f = Number.MAX_SAFE_INTEGER || 9007199254740991; - i = (function() { - function n() { - this._registeredObjects = {}; - } - return ( - (n.prototype.register = function(n, t) { - this._registeredObjects[n] = t; - }), - (n.prototype.unregister = function(n) { - delete this._registeredObjects[n]; - }), - (n.prototype.getInstance = function(n, t) { - var i = this._registeredObjects[n]; - return i ? (typeof i == 'function' ? i(t) : i) : null; - }), - n - ); - })(); - n.XDMObjectRegistry = i; - n.globalObjectRegistry = new i(); - r = (function() { - function t(n, r) { - r === void 0 && (r = null); - this._nextMessageId = 1; - this._deferreds = {}; - this._nextProxyFunctionId = 1; - this._proxyFunctions = {}; - this._postToWindow = n; - this._targetOrigin = r; - this._channelObjectRegistry = new i(); - this._channelId = t._nextChannelId++; - this._targetOrigin || (this._handshakeToken = s()); - } - return ( - (t.prototype.getObjectRegistry = function() { - return this._channelObjectRegistry; - }), - (t.prototype.invokeRemoteMethod = function(n, t, i, r, f) { - var e = { - id: this._nextMessageId++, - methodName: n, - instanceId: t, - instanceContext: r, - params: this._customSerializeObject(i, f), - jsonrpc: '2.0', - serializationSettings: f - }, - o; - return ( - this._targetOrigin || (e.handshakeToken = this._handshakeToken), - (o = u()), - (this._deferreds[e.id] = o), - this._sendRpcMessage(e), - o.promise - ); - }), - (t.prototype.getRemoteObjectProxy = function(n, t) { - return this.invokeRemoteMethod(null, n, null, t); - }), - (t.prototype.invokeMethod = function(n, t) { - var f = this, - r, - u, - i; - if (!t.methodName) { - this._success(t, n, t.handshakeToken); - return; - } - if (((r = n[t.methodName]), typeof r != 'function')) { - this._error(t, new Error('RPC method not found: ' + t.methodName), t.handshakeToken); - return; - } - try { - u = []; - t.params && (u = this._customDeserializeObject(t.params)); - i = r.apply(n, u); - i && i.then && typeof i.then == 'function' - ? i.then( - function(n) { - f._success(t, n, t.handshakeToken); - }, - function(n) { - f._error(t, n, t.handshakeToken); - } - ) - : this._success(t, i, t.handshakeToken); - } catch (e) { - this._error(t, e, t.handshakeToken); - } - }), - (t.prototype.getRegisteredObject = function(t, i) { - if (t === '__proxyFunctions') return this._proxyFunctions; - var r = this._channelObjectRegistry.getInstance(t, i); - return r || (r = n.globalObjectRegistry.getInstance(t, i)), r; - }), - (t.prototype.onMessage = function(n) { - var u = this, - t = n, - i, - r; - if (t.instanceId) { - if (((i = this.getRegisteredObject(t.instanceId, t.instanceContext)), !i)) return !1; - typeof i.then == 'function' - ? i.then( - function(n) { - u.invokeMethod(n, t); - }, - function(n) { - u._error(t, n, t.handshakeToken); - } - ) - : this.invokeMethod(i, t); - } else { - if (((r = this._deferreds[t.id]), !r)) return !1; - t.error - ? r.reject(this._customDeserializeObject([t.error])[0]) - : r.resolve(this._customDeserializeObject([t.result])[0]); - delete this._deferreds[t.id]; - } - return !0; - }), - (t.prototype.owns = function(n, t, i) { - var r = i; - if (this._postToWindow === n) { - if (this._targetOrigin) - return t - ? t.toLowerCase() === 'null' || - this._targetOrigin.toLowerCase().indexOf(t.toLowerCase()) === 0 - : !1; - if (r.handshakeToken && r.handshakeToken === this._handshakeToken) - return (this._targetOrigin = t), !0; - } - return !1; - }), - (t.prototype.error = function(n, t) { - var i = n; - this._error(i, t, i.handshakeToken); - }), - (t.prototype._error = function(n, t, i) { - var r = { - id: n.id, - error: this._customSerializeObject([t], n.serializationSettings)[0], - jsonrpc: '2.0', - handshakeToken: i - }; - this._sendRpcMessage(r); - }), - (t.prototype._success = function(n, t, i) { - var r = { - id: n.id, - result: this._customSerializeObject([t], n.serializationSettings)[0], - jsonrpc: '2.0', - handshakeToken: i - }; - this._sendRpcMessage(r); - }), - (t.prototype._sendRpcMessage = function(n) { - var t = JSON.stringify(n); - this._postToWindow.postMessage(t, '*'); - }), - (t.prototype._shouldSkipSerialization = function(n) { - for (var r, i = 0, u = t.WINDOW_TYPES_TO_SKIP_SERIALIZATION.length; i < u; i++) - if (((r = t.WINDOW_TYPES_TO_SKIP_SERIALIZATION[i]), window[r] && n instanceof window[r])) - return !0; - if (window.jQuery) - for (i = 0, u = t.JQUERY_TYPES_TO_SKIP_SERIALIZATION.length; i < u; i++) - if ( - ((r = t.JQUERY_TYPES_TO_SKIP_SERIALIZATION[i]), - window.jQuery[r] && n instanceof window.jQuery[r]) - ) - return !0; - return !1; - }), - (t.prototype._customSerializeObject = function(n, i, r, u, f) { - var h = this, - a, - o, - l, - v, - e, - c, - s; - if ( - (r === void 0 && (r = null), - u === void 0 && (u = 1), - f === void 0 && (f = 1), - !n || f > t.MAX_XDM_DEPTH) || - this._shouldSkipSerialization(n) - ) - return null; - if ( - ((a = function(t, e, o) { - var s, c, l, a, v; - try { - s = t[o]; - } catch (y) {} - ((c = typeof s), c !== 'undefined') && - ((l = -1), - c === 'object' && (l = r.originalObjects.indexOf(s)), - l >= 0 - ? ((a = r.newObjects[l]), - a.__circularReferenceId || (a.__circularReferenceId = u++), - (e[o] = { __circularReference: a.__circularReferenceId })) - : c === 'function' - ? ((v = h._nextProxyFunctionId++), - (e[o] = { - __proxyFunctionId: h._registerProxyFunction(s, n), - __channelId: h._channelId - })) - : c === 'object' - ? (e[o] = - s && s instanceof Date - ? { __proxyDate: s.getTime() } - : h._customSerializeObject(s, i, r, u, f + 1)) - : o !== '__proxyFunctionId' && (e[o] = s)); - }), - r || (r = { newObjects: [], originalObjects: [] }), - r.originalObjects.push(n), - n instanceof Array) - ) - for (o = [], r.newObjects.push(o), e = 0, c = n.length; e < c; e++) a(n, o, e); - else { - o = {}; - r.newObjects.push(o); - l = {}; - try { - for (s in n) l[s] = !0; - for (v = Object.getOwnPropertyNames(n), e = 0, c = v.length; e < c; e++) l[v[e]] = !0; - } catch (y) {} - for (s in l) ((s && s[0] !== '_') || (i && i.includeUnderscoreProperties)) && a(n, o, s); - } - return r.originalObjects.pop(), r.newObjects.pop(), o; - }), - (t.prototype._registerProxyFunction = function(n, t) { - var i = this._nextProxyFunctionId++; - return ( - (this._proxyFunctions['proxy' + i] = function() { - return n.apply(t, Array.prototype.slice.call(arguments, 0)); - }), - i - ); - }), - (t.prototype._customDeserializeObject = function(n, t) { - var e = this, - o = this, - r, - i, - u, - f; - if (!n) return null; - if ( - (t || (t = {}), - (r = function(n, i) { - var r = n[i], - u = typeof r; - i === '__circularReferenceId' && u === 'number' - ? ((t[r] = n), delete n[i]) - : u === 'object' && - r && - (r.__proxyFunctionId - ? (n[i] = function() { - return o.invokeRemoteMethod( - 'proxy' + r.__proxyFunctionId, - '__proxyFunctions', - Array.prototype.slice.call(arguments, 0), - null, - { includeUnderscoreProperties: !0 } - ); - }) - : r.__proxyDate - ? (n[i] = new Date(r.__proxyDate)) - : r.__circularReference - ? (n[i] = t[r.__circularReference]) - : e._customDeserializeObject(r, t)); - }), - n instanceof Array) - ) - for (i = 0, u = n.length; i < u; i++) r(n, i); - else if (typeof n == 'object') for (f in n) r(n, f); - return n; - }), - (t._nextChannelId = 1), - (t.MAX_XDM_DEPTH = 100), - (t.WINDOW_TYPES_TO_SKIP_SERIALIZATION = ['Node', 'Window', 'Event']), - (t.JQUERY_TYPES_TO_SKIP_SERIALIZATION = ['jQuery']), - t - ); - })(); - n.XDMChannel = r; - e = (function() { - function n() { - this._channels = []; - this._subscribe(window); - } - return ( - (n.get = function() { - return this._default || (this._default = new n()), this._default; - }), - (n.prototype.addChannel = function(n, t) { - var i = new r(n, t); - return this._channels.push(i), i; - }), - (n.prototype.removeChannel = function(n) { - this._channels = this._channels.filter(function(t) { - return t !== n; - }); - }), - (n.prototype._handleMessageReceived = function(n) { - var i, e, r, t, u, f; - if (typeof n.data == 'string') - try { - t = JSON.parse(n.data); - } catch (o) {} - if (t) { - for (u = !1, i = 0, e = this._channels.length; i < e; i++) - (r = this._channels[i]), - r.owns(n.source, n.origin, t) && ((f = r), (u = r.onMessage(t, n.origin) || u)); - !f || - u || - (window.console && - console.error('No handler found on any channel for message: ' + JSON.stringify(t)), - t.instanceId && - f.error(t, 'The registered object ' + t.instanceId + ' could not be found.')); - } - }), - (n.prototype._subscribe = function(n) { - var t = this; - n.addEventListener - ? n.addEventListener('message', function(n) { - t._handleMessageReceived(n); - }) - : n.attachEvent('onmessage', function(n) { - t._handleMessageReceived(n); - }); - }), - n - ); - })(); - n.XDMChannelManager = e; -})(XDM || (XDM = {})), - (function(n) { - function at() { - function r() { - n || - (n = setTimeout(function() { - n = 0; - tt(); - }, 50)); - } - var n, - i = !1, - t; - try { - i = typeof document.cookie == 'string'; - } catch (f) {} - i || - Object.defineProperty(Document.prototype, 'cookie', { - get: function() { - return ''; - }, - set: function() {} - }); - t = !1; - try { - t = !!window.localStorage; - } catch (f) {} - t || - (delete window.localStorage, - (u = new g(r)), - Object.defineProperty(window, 'localStorage', { value: u }), - delete window.sessionStorage, - Object.defineProperty(window, 'sessionStorage', { value: new g() })); - } - function nt(f) { - r = f || {}; - e = r.usePlatformScripts; - a = r.usePlatformStyles; - window.setTimeout(function() { - var f = { - notifyLoadSucceeded: !r.explicitNotifyLoaded, - extensionReusedCallback: r.extensionReusedCallback, - vssSDKVersion: n.VssSDKVersion - }; - i.invokeRemoteMethod('initialHandshake', 'VSS.HostControl', [f]).then(function(n) { - var f, r, o, h, l, s, v, i; - if ( - ((t = n.pageContext), - (b = t.webContext), - (k = n.initialConfig || {}), - (d = n.contribution), - (c = n.extensionContext), - n.sandboxedStorage) - ) { - if (((f = !1), u)) - if (n.sandboxedStorage.localStorage) { - for ( - r = n.sandboxedStorage.localStorage, o = 0, h = Object.keys(u); - o < h.length; - o++ - ) - (i = h[o]), (l = u.getItem(i)), l !== r[i] && ((r[i] = l), (f = !0)); - for (s = 0, v = Object.keys(r); s < v.length; s++) (i = v[s]), u.setItem(i, r[i]); - } else u.length > 0 && (f = !0); - lt = !0; - f && tt(); - } - e || a ? ht() : w(); - }); - }, 0); - } - function tt() { - var n = { localStorage: JSON.stringify(u || {}) }; - i.invokeRemoteMethod('updateSandboxedStorage', 'VSS.HostControl', [n]); - } - function pt(n, t) { - var i; - i = typeof n == 'string' ? [n] : n; - t || (t = function() {}); - l - ? it(i, t) - : (r ? e || ((e = !0), s && ((s = !1), ht())) : nt({ usePlatformScripts: !0 }), - rt(function() { - it(i, t); - })); - } - function it(n, i) { - t.diagnostics.bundlingEnabled - ? window.require(['VSS/Bundling'], function(t) { - t.requireModules(n).spread(function() { - i.apply(this, arguments); - }); - }) - : window.require(n, i); - } - function rt(n) { - s ? window.setTimeout(n, 0) : (f || (f = []), f.push(n)); - } - function wt() { - i.invokeRemoteMethod('notifyLoadSucceeded', 'VSS.HostControl'); - } - function ut(n) { - i.invokeRemoteMethod('notifyLoadFailed', 'VSS.HostControl', [n]); - } - function ft() { - return b; - } - function bt() { - return k; - } - function et() { - return c; - } - function kt() { - return d; - } - function dt(n, t) { - return ot(n).then(function(n) { - return ( - t || (t = {}), - t.webContext || (t.webContext = ft()), - t.extensionContext || (t.extensionContext = et()), - n.getInstance(n.id, t) - ); - }); - } - function ot(t) { - var r = XDM.createDeferred(); - return ( - n.ready(function() { - i - .invokeRemoteMethod('getServiceContribution', 'vss.hostManagement', [t]) - .then(function(n) { - var t = n; - t.getInstance = function(t, i) { - return st(n, t, i); - }; - r.resolve(t); - }, r.reject); - }), - r.promise - ); - } - function gt(t) { - var r = XDM.createDeferred(); - return ( - n.ready(function() { - i - .invokeRemoteMethod('getContributionsForTarget', 'vss.hostManagement', [t]) - .then(function(n) { - var t = []; - n.forEach(function(n) { - var i = n; - i.getInstance = function(t, i) { - return st(n, t, i); - }; - t.push(i); - }); - r.resolve(t); - }, r.reject); - }), - r.promise - ); - } - function st(t, r, u) { - var f = XDM.createDeferred(); - return ( - n.ready(function() { - i - .invokeRemoteMethod('getBackgroundContributionInstance', 'vss.hostManagement', [ - t, - r, - u - ]) - .then(f.resolve, f.reject); - }), - f.promise - ); - } - function ni(n, t) { - i.getObjectRegistry().register(n, t); - } - function ti(n) { - i.getObjectRegistry().unregister(n); - } - function ii(n, t) { - return i.getObjectRegistry().getInstance(n, t); - } - function ri() { - return i.invokeRemoteMethod('getAccessToken', 'VSS.HostControl'); - } - function ui() { - return i.invokeRemoteMethod('getAppToken', 'VSS.HostControl'); - } - function fi(n, t) { - o || (o = document.getElementsByTagName('body').item(0)); - var r = typeof n == 'number' ? n : o.scrollWidth, - u = typeof t == 'number' ? t : o.scrollHeight; - i.invokeRemoteMethod('resize', 'VSS.HostControl', [r, u]); - } - function ht() { - var i = si(t.webContext), - f, - g, - n, - s, - o, - b, - k, - nt, - tt, - d, - u; - if ( - ((window.__vssPageContext = t), - (window.__cultureInfo = t.microsoftAjaxConfig.cultureInfo), - a !== !1 && - t.coreReferences.stylesheets && - t.coreReferences.stylesheets.forEach(function(n) { - if (n.isCoreStylesheet) { - var t = document.createElement('link'); - t.href = h(n.url, i); - t.rel = 'stylesheet'; - p(t, 'head'); - } - }), - !e) - ) { - l = !0; - w(); - return; - } - if ( - ((f = []), - (g = !1), - t.coreReferences.scripts && - (t.coreReferences.scripts.forEach(function(n) { - if (n.isCoreModule) { - var r = !1, - t = window; - n.identifier === 'JQuery' - ? (r = !!t.jQuery) - : n.identifier === 'JQueryUI' - ? (r = !!(t.jQuery && t.jQuery.ui && t.jQuery.ui.version)) - : n.identifier === 'AMDLoader' && - (r = typeof t.define == 'function' && !!t.define.amd); - r ? (g = !0) : f.push({ source: h(n.url, i) }); - } - }), - t.coreReferences.coreScriptsBundle && - !g && - (f = [{ source: h(t.coreReferences.coreScriptsBundle.url, i) }]), - t.coreReferences.extensionCoreReferences && - f.push({ source: h(t.coreReferences.extensionCoreReferences.url, i) })), - (n = { baseUrl: c.baseUri, contributionPaths: null, paths: {}, shim: {} }), - r.moduleLoaderConfig && - (r.moduleLoaderConfig.baseUrl && (n.baseUrl = r.moduleLoaderConfig.baseUrl), - oi(r.moduleLoaderConfig, n), - ct(r.moduleLoaderConfig, n)), - t.moduleLoaderConfig && - (ct(t.moduleLoaderConfig, n), (s = t.moduleLoaderConfig.contributionPaths), s)) - ) - for (o in s) - if ( - s.hasOwnProperty(o) && - !n.paths[o] && - ((b = s[o].value), - (n.paths[o] = b.match('^https?://') ? b : i + b), - (k = t.moduleLoaderConfig.paths), - k) - ) { - nt = o + '/'; - tt = v(i, t.moduleLoaderConfig.baseUrl); - for (d in k) - ei(d, nt) && - ((u = k[d]), - u.match('^https?://') || (u = u[0] === '/' ? v(i, u) : v(tt, u)), - (n.paths[d] = u)); - } - window.__vssModuleLoaderConfig = n; - f.push({ content: 'require.config(' + JSON.stringify(n) + ');' }); - y(f, 0, function() { - l = !0; - w(); - }); - } - function ei(n, t) { - return n && n.length >= t.length ? n.substr(0, t.length).localeCompare(t) === 0 : !1; - } - function v(n, t) { - var i = n || ''; - return i[i.length - 1] !== '/' && (i += '/'), t && (i += t[0] === '/' ? t.substr(1) : t), i; - } - function oi(n, t, i) { - var r, u; - if (n.paths) { - t.paths || (t.paths = {}); - for (r in n.paths) - n.paths.hasOwnProperty(r) && - ((u = n.paths[r]), i && (u = i(r, n.paths[r])), u && (t.paths[r] = u)); - } - } - function ct(n, t) { - if (n.shim) { - t.shim || (t.shim = {}); - for (var i in n.shim) n.shim.hasOwnProperty(i) && (t.shim[i] = n.shim[i]); - } - } - function si(n) { - var r = n.account || n.host, - t = r.uri, - i = r.relativeUri; - return ( - t && - i && - (t[t.length - 1] !== '/' && (t += '/'), - i[i.length - 1] !== '/' && (i += '/'), - (t = t.substr(0, t.length - i.length))), - t - ); - } - function y(n, t, i) { - var f = this, - r, - u; - if (t >= n.length) { - i.call(this); - return; - } - r = document.createElement('script'); - r.type = 'text/javascript'; - n[t].source - ? ((u = n[t].source), - (r.src = u), - r.addEventListener('load', function() { - y.call(f, n, t + 1, i); - }), - r.addEventListener('error', function() { - ut('Failed to load script: ' + u); - }), - p(r, 'head')) - : n[t].content && ((r.textContent = n[t].content), p(r, 'head'), y.call(this, n, t + 1, i)); - } - function p(n, t) { - var i = document.getElementsByTagName(t)[0]; - i || ((i = document.createElement(t)), document.appendChild(i)); - i.appendChild(n); - } - function h(n, t) { - var i = (n || '').toLowerCase(); - return ( - i.substr(0, 2) !== '//' && - i.substr(0, 5) !== 'http:' && - i.substr(0, 6) !== 'https:' && - (n = t + (i[0] === '/' ? '' : '/') + n), - n - ); - } - function w() { - var t = this, - n; - s = !0; - f && - ((n = f), - (f = null), - n.forEach(function(n) { - n.call(t); - })); - } - var yt; - n.VssSDKVersion = 2; - n.VssSDKRestVersion = '4.0'; - var o, - b, - t, - c, - k, - d, - r, - l = !1, - e, - a, - s = !1, - f, - i = XDM.XDMChannelManager.get().addChannel(window.parent), - u, - lt = !1, - g = (function() { - function n() { - t && t.call(this); - } - function i() {} - var t; - return ( - Object.defineProperties(i.prototype, { - getItem: { - get: function() { - return function(n) { - var t = this['' + n]; - return typeof t == 'undefined' ? null : t; - }; - } - }, - setItem: { - get: function() { - return function(t, i) { - t = '' + t; - var u = this[t], - r = '' + i; - u !== r && ((this[t] = r), n()); - }; - } - }, - removeItem: { - get: function() { - return function(t) { - t = '' + t; - typeof this[t] != 'undefined' && (delete this[t], n()); - }; - } - }, - clear: { - get: function() { - return function() { - var r = Object.keys(this), - t, - i, - u; - if (r.length > 0) { - for (t = 0, i = r; t < i.length; t++) (u = i[t]), delete this[u]; - n(); - } - }; - } - }, - key: { - get: function() { - return function(n) { - return Object.keys(this)[n]; - }; - } - }, - length: { - get: function() { - return Object.keys(this).length; - } - } - }), - i - ); - })(); - if (!window.__vssNoSandboxShim) - try { - at(); - } catch (vt) { - window.console && - window.console.warn && - window.console.warn( - 'Failed to shim support for sandboxed properties: ' + - vt.message + - '. Set "window.__vssNoSandboxShim = true" in order to bypass the shim of sandboxed properties.' - ); - } - (function(n) { - n.Dialog = 'ms.vss-web.dialog-service'; - n.Navigation = 'ms.vss-web.navigation-service'; - n.ExtensionData = 'ms.vss-web.data-service'; - })((yt = n.ServiceIds || (n.ServiceIds = {}))); - n.init = nt; - n.require = pt; - n.ready = rt; - n.notifyLoadSucceeded = wt; - n.notifyLoadFailed = ut; - n.getWebContext = ft; - n.getConfiguration = bt; - n.getExtensionContext = et; - n.getContribution = kt; - n.getService = dt; - n.getServiceContribution = ot; - n.getServiceContributions = gt; - n.register = ni; - n.unregister = ti; - n.getRegisteredObject = ii; - n.getAccessToken = ri; - n.getAppToken = ui; - n.resize = fi; - })(VSS || (VSS = {})); diff --git a/server/sonar-web/src/main/js/typings/rc-tooltip.d.ts b/server/sonar-web/src/main/js/typings/rc-tooltip.d.ts deleted file mode 100644 index 999b5b02c84..00000000000 --- a/server/sonar-web/src/main/js/typings/rc-tooltip.d.ts +++ /dev/null @@ -1,54 +0,0 @@ -/* - * SonarQube - * Copyright (C) 2009-2018 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. - */ -declare module 'rc-tooltip' { - export type Trigger = 'hover' | 'click' | 'focus'; - export type Placement = - | 'left' - | 'right' - | 'top' - | 'bottom' - | 'topLeft' - | 'topRight' - | 'bottomLeft' - | 'bottomRight'; - - export interface Props extends React.Props { - overlayClassName?: string; - trigger?: Trigger[]; - mouseEnterDelay?: number; - mouseLeaveDelay?: number; - overlayStyle?: React.CSSProperties; - prefixCls?: string; - transitionName?: string; - onVisibleChange?: () => void; - visible?: boolean; - defaultVisible?: boolean; - placement?: Placement | Object; - align?: Object; - onPopupAlign?: (popupDomNode: Element, align: Object) => void; - overlay: React.ReactNode; - arrowContent?: React.ReactNode; - getTooltipContainer?: () => Element; - destroyTooltipOnHide?: boolean; - } - - // the next line is crucial, it is absent in the original typings - export default class Tooltip extends React.Component {} -} diff --git a/server/sonar-web/yarn.lock b/server/sonar-web/yarn.lock index fed42e3c350..ca97ba90123 100644 --- a/server/sonar-web/yarn.lock +++ b/server/sonar-web/yarn.lock @@ -32,12 +32,26 @@ version "1.2.1" resolved "https://registry.yarnpkg.com/@types/d3-array/-/d3-array-1.2.1.tgz#e489605208d46a1c9d980d2e5772fa9c75d9ec65" +"@types/d3-hierarchy@1.1.1": + version "1.1.1" + resolved "https://registry.yarnpkg.com/@types/d3-hierarchy/-/d3-hierarchy-1.1.1.tgz#f1b7f4bab0a9ecfb9c372a303ded97d83db3cbbf" + +"@types/d3-path@*": + version "1.0.6" + resolved "https://registry.yarnpkg.com/@types/d3-path/-/d3-path-1.0.6.tgz#c1a7d2dc07b295fdd1c84dabe4404df991b48693" + "@types/d3-scale@2.0.0": version "2.0.0" resolved "https://registry.yarnpkg.com/@types/d3-scale/-/d3-scale-2.0.0.tgz#d40f2ff137f4fe9b13b45c5a8e14f39edd498dcf" dependencies: "@types/d3-time" "*" +"@types/d3-shape@1.2.2": + version "1.2.2" + resolved "https://registry.yarnpkg.com/@types/d3-shape/-/d3-shape-1.2.2.tgz#f8dcdff7772a7ae37858bf04abd43848a78e590e" + dependencies: + "@types/d3-path" "*" + "@types/d3-time@*": version "1.0.7" resolved "https://registry.yarnpkg.com/@types/d3-time/-/d3-time-1.0.7.tgz#4266d7c9be15fa81256a88d1d052d61cd8dc572c" -- 2.39.5