From: Grégoire Aubert Date: Thu, 22 Feb 2018 13:29:30 +0000 (+0100) Subject: SONAR-10440 Remove useless tooltips on project overview measures X-Git-Tag: 7.5~1632 X-Git-Url: https://source.dussan.org/?a=commitdiff_plain;h=aa0ced5bc4a11dc28a5f8e298b03e7011bcb62d3;p=sonarqube.git SONAR-10440 Remove useless tooltips on project overview measures --- diff --git a/server/sonar-web/src/main/js/apps/overview/components/ApplicationLeakPeriodLegend.js b/server/sonar-web/src/main/js/apps/overview/components/ApplicationLeakPeriodLegend.js deleted file mode 100644 index bc0704b005a..00000000000 --- a/server/sonar-web/src/main/js/apps/overview/components/ApplicationLeakPeriodLegend.js +++ /dev/null @@ -1,99 +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 Tooltip from '../../../components/controls/Tooltip'; -import DateTooltipFormatter from '../../../components/intl/DateTooltipFormatter'; -import { getApplicationLeak } from '../../../api/application'; -import { translate } from '../../../helpers/l10n'; - -/*:: -type Props = { - component: { key: string } -}; -*/ - -/*:: -type State = { - leaks: ?Array<{ date: string, project: string, projectName: string }> -}; -*/ - -export default class ApplicationLeakPeriodLegend extends React.Component { - /*:: mounted: boolean; */ - /*:: props: Props; */ - state /*: State */ = { - leaks: null - }; - - componentDidMount() { - this.mounted = true; - } - - componentWillReceiveProps(nextProps /*: Props */) { - if (nextProps.component.key !== this.props.component.key) { - this.setState({ leaks: null }); - } - } - - componentWillUnmount() { - this.mounted = false; - } - - fetchLeaks = (visible /*: boolean */) => { - if (visible && this.state.leaks == null) { - getApplicationLeak(this.props.component.key).then( - leaks => { - if (this.mounted) { - this.setState({ leaks }); - } - }, - () => { - if (this.mounted) { - this.setState({ leaks: [] }); - } - } - ); - } - }; - - renderOverlay = () => - this.state.leaks != null ? ( - - ) : ( - - ); - - render() { - return ( - -
- {translate('issues.leak_period')} -
-
- ); - } -} diff --git a/server/sonar-web/src/main/js/apps/overview/components/ApplicationLeakPeriodLegend.tsx b/server/sonar-web/src/main/js/apps/overview/components/ApplicationLeakPeriodLegend.tsx new file mode 100644 index 00000000000..55d8c5082ce --- /dev/null +++ b/server/sonar-web/src/main/js/apps/overview/components/ApplicationLeakPeriodLegend.tsx @@ -0,0 +1,91 @@ +/* + * 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 Tooltip from '../../../components/controls/Tooltip'; +import DateTooltipFormatter from '../../../components/intl/DateTooltipFormatter'; +import { getApplicationLeak } from '../../../api/application'; +import { translate } from '../../../helpers/l10n'; + +interface Props { + component: string; +} + +interface State { + leaks?: Array<{ date: string; project: string; projectName: string }>; +} + +export default class ApplicationLeakPeriodLegend extends React.Component { + mounted = false; + state: State = {}; + + componentDidMount() { + this.mounted = true; + } + + componentWillReceiveProps(nextProps: Props) { + if (nextProps.component !== this.props.component) { + this.setState({ leaks: undefined }); + } + } + + componentWillUnmount() { + this.mounted = false; + } + + fetchLeaks = (visible: boolean) => { + if (visible && this.state.leaks) { + getApplicationLeak(this.props.component).then( + leaks => { + if (this.mounted) { + this.setState({ leaks }); + } + }, + () => { + if (this.mounted) { + this.setState({ leaks: [] }); + } + } + ); + } + }; + + renderOverlay = () => + this.state.leaks != null ? ( +
    + {this.state.leaks.map(leak => ( +
  • + {leak.projectName}: +
  • + ))} +
+ ) : ( + + ); + + render() { + return ( + +
+ {translate('issues.leak_period')} +
+
+ ); + } +} diff --git a/server/sonar-web/src/main/js/apps/overview/components/OverviewApp.tsx b/server/sonar-web/src/main/js/apps/overview/components/OverviewApp.tsx index a9bba4a81bc..5bc3679f9df 100644 --- a/server/sonar-web/src/main/js/apps/overview/components/OverviewApp.tsx +++ b/server/sonar-web/src/main/js/apps/overview/components/OverviewApp.tsx @@ -143,7 +143,9 @@ export class OverviewApp extends React.PureComponent { }; getApplicationLeakPeriod = () => - this.state.measures.find(measure => measure.metric.key === 'new_bugs') ? { index: 1 } : null; + this.state.measures.find(measure => measure.metric.key === 'new_bugs') + ? { index: 1 } + : undefined; renderLoading() { return ( diff --git a/server/sonar-web/src/main/js/apps/overview/components/__tests__/ApplicationLeakPeriodLegend-test.js b/server/sonar-web/src/main/js/apps/overview/components/__tests__/ApplicationLeakPeriodLegend-test.js deleted file mode 100644 index cd504227511..00000000000 --- a/server/sonar-web/src/main/js/apps/overview/components/__tests__/ApplicationLeakPeriodLegend-test.js +++ /dev/null @@ -1,35 +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 { shallow } from 'enzyme'; -import ApplicationLeakPeriodLegend from '../ApplicationLeakPeriodLegend'; - -it('renders', () => { - const wrapper = shallow(); - expect(wrapper).toMatchSnapshot(); - wrapper.setState({ - leaks: [ - { date: '2017-01-01T11:39:03+0100', project: 'foo', projectName: 'Foo' }, - { date: '2017-02-01T11:39:03+0100', project: 'bar', projectName: 'Bar' } - ] - }); - expect(wrapper).toMatchSnapshot(); -}); diff --git a/server/sonar-web/src/main/js/apps/overview/components/__tests__/ApplicationLeakPeriodLegend-test.tsx b/server/sonar-web/src/main/js/apps/overview/components/__tests__/ApplicationLeakPeriodLegend-test.tsx new file mode 100644 index 00000000000..ee95828e890 --- /dev/null +++ b/server/sonar-web/src/main/js/apps/overview/components/__tests__/ApplicationLeakPeriodLegend-test.tsx @@ -0,0 +1,34 @@ +/* + * 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 { shallow } from 'enzyme'; +import ApplicationLeakPeriodLegend from '../ApplicationLeakPeriodLegend'; + +it('renders', () => { + const wrapper = shallow(); + expect(wrapper).toMatchSnapshot(); + wrapper.setState({ + leaks: [ + { date: '2017-01-01T11:39:03+0100', project: 'foo', projectName: 'Foo' }, + { date: '2017-02-01T11:39:03+0100', project: 'bar', projectName: 'Bar' } + ] + }); + expect(wrapper).toMatchSnapshot(); +}); diff --git a/server/sonar-web/src/main/js/apps/overview/components/__tests__/__snapshots__/ApplicationLeakPeriodLegend-test.js.snap b/server/sonar-web/src/main/js/apps/overview/components/__tests__/__snapshots__/ApplicationLeakPeriodLegend-test.js.snap deleted file mode 100644 index cb842597822..00000000000 --- a/server/sonar-web/src/main/js/apps/overview/components/__tests__/__snapshots__/ApplicationLeakPeriodLegend-test.js.snap +++ /dev/null @@ -1,52 +0,0 @@ -// Jest Snapshot v1, https://goo.gl/fbAQLP - -exports[`renders 1`] = ` - - } - placement="bottom" -> -
- issues.leak_period -
-
-`; - -exports[`renders 2`] = ` - -
  • - Foo - : - -
  • -
  • - Bar - : - -
  • - - } - placement="bottom" -> -
    - issues.leak_period -
    -
    -`; diff --git a/server/sonar-web/src/main/js/apps/overview/components/__tests__/__snapshots__/ApplicationLeakPeriodLegend-test.tsx.snap b/server/sonar-web/src/main/js/apps/overview/components/__tests__/__snapshots__/ApplicationLeakPeriodLegend-test.tsx.snap new file mode 100644 index 00000000000..cb842597822 --- /dev/null +++ b/server/sonar-web/src/main/js/apps/overview/components/__tests__/__snapshots__/ApplicationLeakPeriodLegend-test.tsx.snap @@ -0,0 +1,52 @@ +// Jest Snapshot v1, https://goo.gl/fbAQLP + +exports[`renders 1`] = ` + + } + placement="bottom" +> +
    + issues.leak_period +
    +
    +`; + +exports[`renders 2`] = ` + +
  • + Foo + : + +
  • +
  • + Bar + : + +
  • + + } + placement="bottom" +> +
    + issues.leak_period +
    +
    +`; diff --git a/server/sonar-web/src/main/js/apps/overview/main/BugsAndVulnerabilities.js b/server/sonar-web/src/main/js/apps/overview/main/BugsAndVulnerabilities.js deleted file mode 100644 index d7a1db69ffa..00000000000 --- a/server/sonar-web/src/main/js/apps/overview/main/BugsAndVulnerabilities.js +++ /dev/null @@ -1,149 +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 { Link } from 'react-router'; -import enhance from './enhance'; -import ApplicationLeakPeriodLegend from '../components/ApplicationLeakPeriodLegend'; -import BubblesIcon from '../../../components/icons-components/BubblesIcon'; -import BugIcon from '../../../components/icons-components/BugIcon'; -import LeakPeriodLegend from '../components/LeakPeriodLegend'; -import VulnerabilityIcon from '../../../components/icons-components/VulnerabilityIcon'; -import { getMetricName } from '../helpers/metrics'; -import { getComponentDrilldownUrl } from '../../../helpers/urls'; -import { translate } from '../../../helpers/l10n'; - -class BugsAndVulnerabilities extends React.PureComponent { - renderHeader() { - const { branch, component } = this.props; - - return ( -
    -
    - {translate('metric.bugs.name')} - - - - {translate('metric.vulnerabilities.name')} - - - -
    -
    - ); - } - - renderLeak() { - const { component, leakPeriod } = this.props; - - if (leakPeriod == null) { - return null; - } - - return ( -
    - {component.qualifier === 'APP' ? ( - - ) : ( - - )} - -
    -
    -
    - {this.props.renderIssues('new_bugs', 'BUG')} - {this.props.renderRating('new_reliability_rating')} -
    -
    - - {getMetricName('new_bugs')} -
    -
    -
    -
    - - {this.props.renderIssues('new_vulnerabilities', 'VULNERABILITY')} - - {this.props.renderRating('new_security_rating')} -
    -
    - - {getMetricName('new_vulnerabilities')} -
    -
    -
    -
    - ); - } - - renderNutshell() { - return ( -
    -
    -
    -
    - {this.props.renderIssues('bugs', 'BUG')} - {this.props.renderRating('reliability_rating')} -
    -
    - - {getMetricName('bugs')} - {this.props.renderHistoryLink('bugs')} -
    -
    -
    -
    - {this.props.renderIssues('vulnerabilities', 'VULNERABILITY')} - {this.props.renderRating('security_rating')} -
    -
    - - {getMetricName('vulnerabilities')} - {this.props.renderHistoryLink('vulnerabilities')} -
    -
    -
    -
    - ); - } - - render() { - const { measures } = this.props; - const bugsMeasure = measures.find(measure => measure.metric.key === 'bugs'); - if (bugsMeasure == null) { - return null; - } - return ( -
    - {this.renderHeader()} - -
    - {this.renderNutshell()} - {this.renderLeak()} -
    -
    - ); - } -} - -export default enhance(BugsAndVulnerabilities); diff --git a/server/sonar-web/src/main/js/apps/overview/main/BugsAndVulnerabilities.tsx b/server/sonar-web/src/main/js/apps/overview/main/BugsAndVulnerabilities.tsx new file mode 100644 index 00000000000..d6433a10e08 --- /dev/null +++ b/server/sonar-web/src/main/js/apps/overview/main/BugsAndVulnerabilities.tsx @@ -0,0 +1,148 @@ +/* + * 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 { Link } from 'react-router'; +import enhance, { ComposedProps } from './enhance'; +import ApplicationLeakPeriodLegend from '../components/ApplicationLeakPeriodLegend'; +import BubblesIcon from '../../../components/icons-components/BubblesIcon'; +import BugIcon from '../../../components/icons-components/BugIcon'; +import LeakPeriodLegend from '../components/LeakPeriodLegend'; +import VulnerabilityIcon from '../../../components/icons-components/VulnerabilityIcon'; +import { getMetricName } from '../helpers/metrics'; +import { getComponentDrilldownUrl } from '../../../helpers/urls'; +import { translate } from '../../../helpers/l10n'; + +export class BugsAndVulnerabilities extends React.PureComponent { + renderHeader() { + const { branch, component } = this.props; + + return ( +
    +
    + {translate('metric.bugs.name')} + + + + {translate('metric.vulnerabilities.name')} + + + +
    +
    + ); + } + + renderLeak() { + const { component, leakPeriod } = this.props; + if (!leakPeriod) { + return null; + } + + return ( +
    + {component.qualifier === 'APP' ? ( + + ) : ( + + )} + +
    +
    +
    + {this.props.renderIssues('new_bugs', 'BUG')} + {this.props.renderRating('new_reliability_rating')} +
    +
    + + {getMetricName('new_bugs')} +
    +
    +
    +
    + + {this.props.renderIssues('new_vulnerabilities', 'VULNERABILITY')} + + {this.props.renderRating('new_security_rating')} +
    +
    + + {getMetricName('new_vulnerabilities')} +
    +
    +
    +
    + ); + } + + renderNutshell() { + return ( +
    +
    +
    +
    + {this.props.renderIssues('bugs', 'BUG')} + {this.props.renderRating('reliability_rating')} +
    +
    + + {getMetricName('bugs')} + {this.props.renderHistoryLink('bugs')} +
    +
    +
    +
    + {this.props.renderIssues('vulnerabilities', 'VULNERABILITY')} + {this.props.renderRating('security_rating')} +
    +
    + + {getMetricName('vulnerabilities')} + {this.props.renderHistoryLink('vulnerabilities')} +
    +
    +
    +
    + ); + } + + render() { + const { measures } = this.props; + const bugsMeasure = measures.find(measure => measure.metric.key === 'bugs'); + if (!bugsMeasure) { + return null; + } + return ( +
    + {this.renderHeader()} + +
    + {this.renderNutshell()} + {this.renderLeak()} +
    +
    + ); + } +} + +export default enhance(BugsAndVulnerabilities); diff --git a/server/sonar-web/src/main/js/apps/overview/main/CodeSmells.js b/server/sonar-web/src/main/js/apps/overview/main/CodeSmells.js deleted file mode 100644 index 94786fea4f8..00000000000 --- a/server/sonar-web/src/main/js/apps/overview/main/CodeSmells.js +++ /dev/null @@ -1,171 +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 { Link } from 'react-router'; -import enhance from './enhance'; -import Tooltip from '../../../components/controls/Tooltip'; -import DateFromNow from '../../../components/intl/DateFromNow'; -import DateTimeFormatter from '../../../components/intl/DateTimeFormatter'; -import { getMetricName } from '../helpers/metrics'; -import { translate, translateWithParameters } from '../../../helpers/l10n'; -import { formatMeasure, isDiffMetric } from '../../../helpers/measures'; -import { getComponentIssuesUrl } from '../../../helpers/urls'; -import CodeSmellIcon from '../../../components/icons-components/CodeSmellIcon'; - -class CodeSmells extends React.PureComponent { - renderHeader() { - return this.props.renderHeader('Maintainability', translate('metric.code_smells.name')); - } - - renderDebt(metric, type) { - const { branch, measures, component } = this.props; - const measure = measures.find(measure => measure.metric.key === metric); - const value = this.props.getValue(measure); - const params = { branch, resolved: 'false', facetMode: 'effort', types: type }; - - if (isDiffMetric(metric)) { - Object.assign(params, { sinceLeakPeriod: 'true' }); - } - - const tooltip = ( - - {formattedAnalysisDate => ( - {translateWithParameters('widget.as_calculated_on_x', formattedAnalysisDate)} - )} - - ); - return ( - - - {formatMeasure(value, 'SHORT_WORK_DUR')} - - - ); - } - - renderTimelineStartDate() { - if (!this.props.historyStartDate) { - return null; - } - return ( - - {fromNow => ( - - {translateWithParameters('overview.started_x', fromNow)} - - )} - - ); - } - - renderTimeline(range, displayDate) { - return this.props.renderTimeline( - 'sqale_index', - range, - displayDate ? this.renderTimelineStartDate() : null - ); - } - - renderLeak() { - const { leakPeriod } = this.props; - - if (leakPeriod == null) { - return null; - } - - return ( -
    -
    -
    -
    - - {this.renderDebt('new_technical_debt', 'CODE_SMELL')} - - {this.props.renderRating('new_maintainability_rating')} -
    -
    {getMetricName('new_effort')}
    -
    -
    -
    - {this.props.renderIssues('new_code_smells', 'CODE_SMELL')} -
    -
    - - {getMetricName('new_code_smells')} -
    -
    -
    - - {this.renderTimeline('after')} -
    - ); - } - - renderNutshell() { - return ( -
    -
    -
    -
    - {this.renderDebt('sqale_index', 'CODE_SMELL')} - {this.props.renderRating('sqale_rating')} -
    -
    - {getMetricName('effort')} - {this.props.renderHistoryLink('sqale_index')} -
    -
    -
    -
    - {this.props.renderIssues('code_smells', 'CODE_SMELL')} -
    -
    - - {getMetricName('code_smells')} - {this.props.renderHistoryLink('code_smells')} -
    -
    -
    - - {this.renderTimeline('before', true)} -
    - ); - } - - render() { - const { measures } = this.props; - const codeSmellsMeasure = measures.find(measure => measure.metric.key === 'code_smells'); - if (codeSmellsMeasure == null) { - return null; - } - return ( -
    - {this.renderHeader()} - -
    - {this.renderNutshell()} - {this.renderLeak()} -
    -
    - ); - } -} - -export default enhance(CodeSmells); diff --git a/server/sonar-web/src/main/js/apps/overview/main/CodeSmells.tsx b/server/sonar-web/src/main/js/apps/overview/main/CodeSmells.tsx new file mode 100644 index 00000000000..ac7fcd0c94c --- /dev/null +++ b/server/sonar-web/src/main/js/apps/overview/main/CodeSmells.tsx @@ -0,0 +1,159 @@ +/* + * 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 { Link } from 'react-router'; +import enhance, { ComposedProps } from './enhance'; +import DateFromNow from '../../../components/intl/DateFromNow'; +import { getMetricName } from '../helpers/metrics'; +import { translate, translateWithParameters } from '../../../helpers/l10n'; +import { formatMeasure, isDiffMetric } from '../../../helpers/measures'; +import { getComponentIssuesUrl } from '../../../helpers/urls'; +import CodeSmellIcon from '../../../components/icons-components/CodeSmellIcon'; + +export class CodeSmells extends React.PureComponent { + renderHeader() { + return this.props.renderHeader('Maintainability', translate('metric.code_smells.name')); + } + + renderDebt(metric: string, type: string) { + const { branch, measures, component } = this.props; + const measure = measures.find(measure => measure.metric.key === metric); + const value = measure ? this.props.getValue(measure) : undefined; + const params = { branch, resolved: 'false', facetMode: 'effort', types: type }; + + if (isDiffMetric(metric)) { + Object.assign(params, { sinceLeakPeriod: 'true' }); + } + + return ( + + {formatMeasure(value, 'SHORT_WORK_DUR')} + + ); + } + + renderTimelineStartDate() { + if (!this.props.historyStartDate) { + return null; + } + return ( + + {fromNow => ( + + {translateWithParameters('overview.started_x', fromNow)} + + )} + + ); + } + + renderTimeline(range: string, displayDate?: boolean) { + return this.props.renderTimeline( + 'sqale_index', + range, + displayDate ? this.renderTimelineStartDate() : null + ); + } + + renderLeak() { + const { leakPeriod } = this.props; + if (!leakPeriod) { + return null; + } + + return ( +
    +
    +
    +
    + + {this.renderDebt('new_technical_debt', 'CODE_SMELL')} + + {this.props.renderRating('new_maintainability_rating')} +
    +
    {getMetricName('new_effort')}
    +
    +
    +
    + {this.props.renderIssues('new_code_smells', 'CODE_SMELL')} +
    +
    + + {getMetricName('new_code_smells')} +
    +
    +
    + + {this.renderTimeline('after')} +
    + ); + } + + renderNutshell() { + return ( +
    +
    +
    +
    + {this.renderDebt('sqale_index', 'CODE_SMELL')} + {this.props.renderRating('sqale_rating')} +
    +
    + {getMetricName('effort')} + {this.props.renderHistoryLink('sqale_index')} +
    +
    +
    +
    + {this.props.renderIssues('code_smells', 'CODE_SMELL')} +
    +
    + + {getMetricName('code_smells')} + {this.props.renderHistoryLink('code_smells')} +
    +
    +
    + + {this.renderTimeline('before', true)} +
    + ); + } + + render() { + const { measures } = this.props; + const codeSmellsMeasure = measures.find(measure => measure.metric.key === 'code_smells'); + if (!codeSmellsMeasure) { + return null; + } + return ( +
    + {this.renderHeader()} + +
    + {this.renderNutshell()} + {this.renderLeak()} +
    +
    + ); + } +} + +export default enhance(CodeSmells); diff --git a/server/sonar-web/src/main/js/apps/overview/main/Coverage.js b/server/sonar-web/src/main/js/apps/overview/main/Coverage.js deleted file mode 100644 index 6d800e2a8f6..00000000000 --- a/server/sonar-web/src/main/js/apps/overview/main/Coverage.js +++ /dev/null @@ -1,186 +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 enhance from './enhance'; -import DrilldownLink from '../../../components/shared/DrilldownLink'; -import { getMetricName } from '../helpers/metrics'; -import { formatMeasure, getPeriodValue } from '../../../helpers/measures'; -import { translate } from '../../../helpers/l10n'; -import CoverageRating from '../../../components/ui/CoverageRating'; - -class Coverage extends React.PureComponent { - getCoverage() { - const { measures } = this.props; - const { value } = measures.find(measure => measure.metric.key === 'coverage'); - return Number(value); - } - - getNewCoverageMeasure() { - const { measures } = this.props; - return measures.find(measure => measure.metric.key === 'new_coverage'); - } - - getNewLinesToCover() { - const { measures } = this.props; - return measures.find(measure => measure.metric.key === 'new_lines_to_cover'); - } - - renderHeader() { - return this.props.renderHeader('Coverage', translate('metric.coverage.name')); - } - - renderTimeline(range) { - return this.props.renderTimeline('coverage', range); - } - - renderTests() { - return this.props.renderMeasure('tests'); - } - - renderCoverage() { - const { branch, component } = this.props; - const metric = 'coverage'; - const coverage = this.getCoverage(); - - return ( -
    -
    - -
    - -
    -
    - - - {formatMeasure(coverage, 'PERCENT')} - - -
    - -
    - {getMetricName('coverage')} - {this.props.renderHistoryLink('coverage')} -
    -
    -
    - ); - } - - renderNewCoverage() { - const { branch, component, leakPeriod } = this.props; - const newCoverageMeasure = this.getNewCoverageMeasure(); - const newLinesToCover = this.getNewLinesToCover(); - - const newCoverageValue = newCoverageMeasure - ? getPeriodValue(newCoverageMeasure, leakPeriod.index) - : null; - const newLinesToCoverValue = newLinesToCover - ? getPeriodValue(newLinesToCover, leakPeriod.index) - : null; - - const formattedValue = - newCoverageValue != null ? ( -
    - - - {formatMeasure(newCoverageValue, 'PERCENT')} - - -
    - ) : ( - — - ); - const label = - newLinesToCoverValue != null && newLinesToCoverValue > 0 ? ( -
    - {translate('overview.coverage_on')} -
    - - - {formatMeasure(newLinesToCoverValue, 'SHORT_INT')} - - - {getMetricName('new_lines_to_cover')} -
    - ) : ( -
    {getMetricName('new_coverage')}
    - ); - return ( -
    -
    {formattedValue}
    - {label} -
    - ); - } - - renderNutshell() { - return ( -
    -
    - {this.renderCoverage()} - {this.renderTests()} -
    - - {this.renderTimeline('before')} -
    - ); - } - - renderLeak() { - const { leakPeriod } = this.props; - if (leakPeriod == null) { - return null; - } - return ( -
    -
    {this.renderNewCoverage()}
    - - {this.renderTimeline('after')} -
    - ); - } - - render() { - const { measures } = this.props; - const coverageMeasure = measures.find(measure => measure.metric.key === 'coverage'); - if (coverageMeasure == null) { - return null; - } - return ( -
    - {this.renderHeader()} - -
    - {this.renderNutshell()} - {this.renderLeak()} -
    -
    - ); - } -} - -export default enhance(Coverage); diff --git a/server/sonar-web/src/main/js/apps/overview/main/Coverage.tsx b/server/sonar-web/src/main/js/apps/overview/main/Coverage.tsx new file mode 100644 index 00000000000..cd1abc807ee --- /dev/null +++ b/server/sonar-web/src/main/js/apps/overview/main/Coverage.tsx @@ -0,0 +1,177 @@ +/* + * 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 enhance, { ComposedProps } from './enhance'; +import DrilldownLink from '../../../components/shared/DrilldownLink'; +import { getMetricName } from '../helpers/metrics'; +import { formatMeasure, getPeriodValue } from '../../../helpers/measures'; +import { translate } from '../../../helpers/l10n'; +import CoverageRating from '../../../components/ui/CoverageRating'; + +export class Coverage extends React.PureComponent { + getCoverage() { + const measure = this.props.measures.find(measure => measure.metric.key === 'coverage'); + return Number(measure ? measure.value : undefined); + } + + renderHeader() { + return this.props.renderHeader('Coverage', translate('metric.coverage.name')); + } + + renderTimeline(range: string) { + return this.props.renderTimeline('coverage', range); + } + + renderTests() { + return this.props.renderMeasure('tests'); + } + + renderCoverage() { + const { branch, component } = this.props; + const metric = 'coverage'; + const coverage = this.getCoverage(); + + return ( +
    +
    + +
    + +
    +
    + + + {formatMeasure(coverage, 'PERCENT')} + + +
    + +
    + {getMetricName('coverage')} + {this.props.renderHistoryLink('coverage')} +
    +
    +
    + ); + } + + renderNewCoverage() { + const { branch, component, leakPeriod, measures } = this.props; + if (!leakPeriod) { + return null; + } + + const newCoverageMeasure = measures.find(measure => measure.metric.key === 'new_coverage'); + const newCoverageValue = + newCoverageMeasure && getPeriodValue(newCoverageMeasure, leakPeriod.index); + const formattedValue = + newCoverageMeasure && newCoverageValue !== undefined ? ( +
    + + + {formatMeasure(newCoverageValue, 'PERCENT')} + + +
    + ) : ( + — + ); + + const newLinesToCover = measures.find(measure => measure.metric.key === 'new_lines_to_cover'); + const newLinesToCoverValue = + newLinesToCover && getPeriodValue(newLinesToCover, leakPeriod.index); + const label = + newLinesToCover && newLinesToCoverValue !== undefined && Number(newLinesToCoverValue) > 0 ? ( +
    + {translate('overview.coverage_on')} +
    + + + {formatMeasure(newLinesToCoverValue, 'SHORT_INT')} + + + {getMetricName('new_lines_to_cover')} +
    + ) : ( +
    {getMetricName('new_coverage')}
    + ); + + return ( +
    +
    {formattedValue}
    + {label} +
    + ); + } + + renderNutshell() { + return ( +
    +
    + {this.renderCoverage()} + {this.renderTests()} +
    + + {this.renderTimeline('before')} +
    + ); + } + + renderLeak() { + const { leakPeriod } = this.props; + if (!leakPeriod) { + return null; + } + return ( +
    +
    {this.renderNewCoverage()}
    + + {this.renderTimeline('after')} +
    + ); + } + + render() { + const { measures } = this.props; + const coverageMeasure = measures.find(measure => measure.metric.key === 'coverage'); + if (!coverageMeasure) { + return null; + } + return ( +
    + {this.renderHeader()} + +
    + {this.renderNutshell()} + {this.renderLeak()} +
    +
    + ); + } +} + +export default enhance(Coverage); diff --git a/server/sonar-web/src/main/js/apps/overview/main/Duplications.js b/server/sonar-web/src/main/js/apps/overview/main/Duplications.js deleted file mode 100644 index 3f94296f6b3..00000000000 --- a/server/sonar-web/src/main/js/apps/overview/main/Duplications.js +++ /dev/null @@ -1,175 +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 enhance from './enhance'; -import DrilldownLink from '../../../components/shared/DrilldownLink'; -import { getMetricName } from '../helpers/metrics'; -import { formatMeasure, getPeriodValue } from '../../../helpers/measures'; -import { translate } from '../../../helpers/l10n'; -import DuplicationsRating from '../../../components/ui/DuplicationsRating'; - -class Duplications extends React.PureComponent { - renderHeader() { - return this.props.renderHeader('Duplications', translate('overview.domain.duplications')); - } - - renderTimeline(range) { - return this.props.renderTimeline('duplicated_lines_density', range); - } - - renderDuplicatedBlocks() { - return this.props.renderMeasure('duplicated_blocks'); - } - - renderDuplications() { - const { branch, component, measures } = this.props; - const measure = measures.find(measure => measure.metric.key === 'duplicated_lines_density'); - const duplications = Number(measure.value); - - return ( -
    -
    - -
    - -
    -
    - - {formatMeasure(duplications, 'PERCENT')} - -
    - -
    - {getMetricName('duplications')} - {this.props.renderHistoryLink('duplicated_lines_density')} -
    -
    -
    - ); - } - - renderNewDuplications() { - const { branch, component, measures, leakPeriod } = this.props; - const newDuplicationsMeasure = measures.find( - measure => measure.metric.key === 'new_duplicated_lines_density' - ); - const newLinesMeasure = measures.find(measure => measure.metric.key === 'new_lines'); - - const newDuplicationsValue = newDuplicationsMeasure - ? getPeriodValue(newDuplicationsMeasure, leakPeriod.index) - : null; - const newLinesValue = newLinesMeasure - ? getPeriodValue(newLinesMeasure, leakPeriod.index) - : null; - - const formattedValue = - newDuplicationsValue != null ? ( -
    - - - {formatMeasure(newDuplicationsValue, 'PERCENT')} - - -
    - ) : ( - — - ); - const label = - newLinesValue != null && newLinesValue > 0 ? ( -
    - {translate('overview.duplications_on')} -
    - - - {formatMeasure(newLinesValue, 'SHORT_INT')} - - - {getMetricName('new_lines')} -
    - ) : ( -
    {getMetricName('new_duplications')}
    - ); - return ( -
    -
    {formattedValue}
    - {label} -
    - ); - } - - renderNutshell() { - return ( -
    -
    - {this.renderDuplications()} - {this.renderDuplicatedBlocks()} -
    - - {this.renderTimeline('before')} -
    - ); - } - - renderLeak() { - const { leakPeriod } = this.props; - if (leakPeriod == null) { - return null; - } - return ( -
    -
    {this.renderNewDuplications()}
    - - {this.renderTimeline('after')} -
    - ); - } - - render() { - const { measures } = this.props; - const duplications = measures.find( - measure => measure.metric.key === 'duplicated_lines_density' - ); - if (duplications == null) { - return null; - } - return ( -
    - {this.renderHeader()} - -
    - {this.renderNutshell()} - {this.renderLeak()} -
    -
    - ); - } -} - -export default enhance(Duplications); diff --git a/server/sonar-web/src/main/js/apps/overview/main/Duplications.tsx b/server/sonar-web/src/main/js/apps/overview/main/Duplications.tsx new file mode 100644 index 00000000000..d8a09caee40 --- /dev/null +++ b/server/sonar-web/src/main/js/apps/overview/main/Duplications.tsx @@ -0,0 +1,181 @@ +/* + * 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 enhance, { ComposedProps } from './enhance'; +import DrilldownLink from '../../../components/shared/DrilldownLink'; +import { getMetricName } from '../helpers/metrics'; +import { formatMeasure, getPeriodValue } from '../../../helpers/measures'; +import { translate } from '../../../helpers/l10n'; +import DuplicationsRating from '../../../components/ui/DuplicationsRating'; + +export class Duplications extends React.PureComponent { + renderHeader() { + return this.props.renderHeader('Duplications', translate('overview.domain.duplications')); + } + + renderTimeline(range: string) { + return this.props.renderTimeline('duplicated_lines_density', range); + } + + renderDuplicatedBlocks() { + return this.props.renderMeasure('duplicated_blocks'); + } + + renderDuplications() { + const { branch, component, measures } = this.props; + + const measure = measures.find(measure => measure.metric.key === 'duplicated_lines_density'); + if (!measure) { + return null; + } + + const duplications = Number(measure.value); + + return ( +
    +
    + +
    + +
    +
    + + {formatMeasure(duplications, 'PERCENT')} + +
    + +
    + {getMetricName('duplications')} + {this.props.renderHistoryLink('duplicated_lines_density')} +
    +
    +
    + ); + } + + renderNewDuplications() { + const { branch, component, measures, leakPeriod } = this.props; + if (!leakPeriod) { + return null; + } + + const newDuplicationsMeasure = measures.find( + measure => measure.metric.key === 'new_duplicated_lines_density' + ); + const newDuplicationsValue = + newDuplicationsMeasure && getPeriodValue(newDuplicationsMeasure, leakPeriod.index); + const formattedValue = + newDuplicationsMeasure && newDuplicationsValue ? ( +
    + + + {formatMeasure(newDuplicationsValue, 'PERCENT')} + + +
    + ) : ( + — + ); + + const newLinesMeasure = measures.find(measure => measure.metric.key === 'new_lines'); + const newLinesValue = newLinesMeasure && getPeriodValue(newLinesMeasure, leakPeriod.index); + const label = + newLinesMeasure && newLinesValue !== undefined && Number(newLinesValue) > 0 ? ( +
    + {translate('overview.duplications_on')} +
    + + + {formatMeasure(newLinesValue, 'SHORT_INT')} + + + {getMetricName('new_lines')} +
    + ) : ( +
    {getMetricName('new_duplications')}
    + ); + + return ( +
    +
    {formattedValue}
    + {label} +
    + ); + } + + renderNutshell() { + return ( +
    +
    + {this.renderDuplications()} + {this.renderDuplicatedBlocks()} +
    + + {this.renderTimeline('before')} +
    + ); + } + + renderLeak() { + const { leakPeriod } = this.props; + if (leakPeriod == null) { + return null; + } + return ( +
    +
    {this.renderNewDuplications()}
    + + {this.renderTimeline('after')} +
    + ); + } + + render() { + const { measures } = this.props; + const duplications = measures.find( + measure => measure.metric.key === 'duplicated_lines_density' + ); + if (duplications == null) { + return null; + } + return ( +
    + {this.renderHeader()} + +
    + {this.renderNutshell()} + {this.renderLeak()} +
    +
    + ); + } +} + +export default enhance(Duplications); diff --git a/server/sonar-web/src/main/js/apps/overview/main/enhance.tsx b/server/sonar-web/src/main/js/apps/overview/main/enhance.tsx index 43e23c1715d..2b07c3c9239 100644 --- a/server/sonar-web/src/main/js/apps/overview/main/enhance.tsx +++ b/server/sonar-web/src/main/js/apps/overview/main/enhance.tsx @@ -21,7 +21,6 @@ import * as React from 'react'; import { Link } from 'react-router'; import DrilldownLink from '../../../components/shared/DrilldownLink'; import BubblesIcon from '../../../components/icons-components/BubblesIcon'; -import DateTimeFormatter from '../../../components/intl/DateTimeFormatter'; import HistoryIcon from '../../../components/icons-components/HistoryIcon'; import Rating from '../../../components/ui/Rating'; import Timeline from '../components/Timeline'; @@ -34,7 +33,7 @@ import { getRatingTooltip, MeasureEnhanced } from '../../../helpers/measures'; -import { translateWithParameters, getLocalizedMetricName } from '../../../helpers/l10n'; +import { getLocalizedMetricName } from '../../../helpers/l10n'; import { getPeriodDate } from '../../../helpers/periods'; import { getComponentDrilldownUrl, @@ -155,22 +154,10 @@ export default function enhance(ComposedComponent: React.ComponentType - {formattedAnalysisDate => ( - - {translateWithParameters('widget.as_calculated_on_x', formattedAnalysisDate)} - - )} - - ); - return ( - - - {formatMeasure(value, 'SHORT_INT')} - - + + {formatMeasure(value, 'SHORT_INT')} + ); }; @@ -209,9 +196,9 @@ export default function enhance(ComposedComponent: React.ComponentType ); diff --git a/sonar-core/src/main/resources/org/sonar/l10n/core.properties b/sonar-core/src/main/resources/org/sonar/l10n/core.properties index 8144c5c5190..ac534cbda9e 100644 --- a/sonar-core/src/main/resources/org/sonar/l10n/core.properties +++ b/sonar-core/src/main/resources/org/sonar/l10n/core.properties @@ -2364,8 +2364,6 @@ overview.badges.marketing.description=This badge lets you advertise that you're overview.badges.quality_gate.alt=SonarCloud Quality Gate badge overview.badges.quality_gate.description=This badge dynamically displays the current quality gate status of your project. -widget.as_calculated_on_x=As calculated on {0} - #------------------------------------------------------------------------------ #