diff options
Diffstat (limited to 'server/sonar-web/src')
17 files changed, 303 insertions, 207 deletions
diff --git a/server/sonar-web/src/main/js/apps/component-measures/app.js b/server/sonar-web/src/main/js/apps/component-measures/app.js index 4077b2f777d..babee01e50d 100644 --- a/server/sonar-web/src/main/js/apps/component-measures/app.js +++ b/server/sonar-web/src/main/js/apps/component-measures/app.js @@ -23,10 +23,10 @@ import { Router, Route, IndexRoute, Redirect, IndexRedirect, useRouterHistory } import { createHistory } from 'history'; import ComponentMeasuresApp from './components/ComponentMeasuresApp'; -import AllMeasuresList from './components/AllMeasuresList'; +import AllMeasuresList from './components/AllMeasures'; import MeasureDetails from './components/MeasureDetails'; -import MeasureTree from './components/MeasureTree'; -import MeasureList from './components/MeasureList'; +import MeasureDrilldownTree from './components/MeasureDrilldownTree'; +import MeasureDrilldownList from './components/MeasureDrilldownList'; import './styles.css'; @@ -53,8 +53,8 @@ window.sonarqube.appStarted.then(options => { <IndexRoute component={AllMeasuresList}/> <Route path=":metricKey" component={MeasureDetails}> <IndexRedirect to="tree"/> - <Route path="tree" component={MeasureTree}/> - <Route path="list" component={MeasureList}/> + <Route path="tree" component={MeasureDrilldownTree}/> + <Route path="list" component={MeasureDrilldownList}/> </Route> </Route> diff --git a/server/sonar-web/src/main/js/apps/component-measures/components/AllMeasuresList.js b/server/sonar-web/src/main/js/apps/component-measures/components/AllMeasures.js index 12a43009b1b..26c6ca254d2 100644 --- a/server/sonar-web/src/main/js/apps/component-measures/components/AllMeasuresList.js +++ b/server/sonar-web/src/main/js/apps/component-measures/components/AllMeasures.js @@ -19,17 +19,14 @@ */ import _ from 'underscore'; import React from 'react'; -import { Link } from 'react-router'; import Spinner from './Spinner'; -import { getLeakValue, formatLeak } from '../utils'; +import AllMeasuresDomain from './AllMeasuresDomain'; +import { getLeakValue } from '../utils'; import { getMeasuresAndMeta } from '../../../api/measures'; -import { formatMeasure } from '../../../helpers/measures'; -import { translateWithParameters } from '../../../helpers/l10n'; +import { getLeakPeriodLabel } from '../../../helpers/periods'; -import { getPeriodLabel } from '../../overview/helpers/periods'; - -export default class ComponentMeasuresApp extends React.Component { +export default class AllMeasures extends React.Component { state = { fetching: true, measures: [] @@ -86,51 +83,23 @@ export default class ComponentMeasuresApp extends React.Component { return { name, measures: sortedMeasures }; }), 'name'); - const leakLabel = getPeriodLabel(periods, 1); + const leakPeriodLabel = getLeakPeriodLabel(periods); return ( - <ul className="component-measures-domains"> + <ul className="measures-domains"> {domains.map((domain, index) => ( - <li key={domain.name}> - <header className="page-header"> - <h3 className="page-title">{domain.name}</h3> - {index === 0 && ( - <div className="component-measures-domains-leak-header"> - {translateWithParameters('overview.leak_period_x', leakLabel)} - </div> - )} - </header> - - <ul className="component-measures-domain-measures"> - {domain.measures.map(measure => ( - <li key={measure.metric.key}> - <div className="component-measures-domain-measures-name"> - {measure.metric.name} - </div> - <div className="component-measures-domain-measures-value"> - {measure.value != null && ( - <Link to={{ pathname: measure.metric.key, query: { id: component.key } }}> - {formatMeasure(measure.value, measure.metric.type)} - </Link> - )} - </div> - <div className="component-measures-domain-measures-value component-measures-leak-cell"> - {measure.leak != null && ( - <Link to={{ pathname: measure.metric.key, query: { id: component.key } }}> - {formatLeak(measure.leak, measure.metric)} - </Link> - )} - </div> - </li> - ))} - </ul> - </li> + <AllMeasuresDomain + key={domain.name} + domain={domain} + component={component} + displayLeakHeader={index === 0} + leakPeriodLabel={leakPeriodLabel}/> ))} </ul> ); } } -ComponentMeasuresApp.contextTypes = { +AllMeasures.contextTypes = { component: React.PropTypes.object }; diff --git a/server/sonar-web/src/main/js/apps/component-measures/components/AllMeasuresDomain.js b/server/sonar-web/src/main/js/apps/component-measures/components/AllMeasuresDomain.js new file mode 100644 index 00000000000..ba5c86459bf --- /dev/null +++ b/server/sonar-web/src/main/js/apps/component-measures/components/AllMeasuresDomain.js @@ -0,0 +1,64 @@ +/* + * SonarQube + * Copyright (C) 2009-2016 SonarSource SA + * mailto:contact AT sonarsource DOT com + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 3 of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with this program; if not, write to the Free Software Foundation, + * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ +import React from 'react'; +import { Link } from 'react-router'; + +import { formatLeak } from '../utils'; +import { formatMeasure } from '../../../helpers/measures'; +import { translateWithParameters } from '../../../helpers/l10n'; + +export default function AllMeasuresDomain ({ domain, component, displayLeakHeader, leakPeriodLabel }) { + return ( + <li> + <header className="page-header"> + <h3 className="page-title">{domain.name}</h3> + {displayLeakHeader && ( + <div className="measures-domains-leak-header"> + {translateWithParameters('overview.leak_period_x', leakPeriodLabel)} + </div> + )} + </header> + + <ul className="domain-measures"> + {domain.measures.map(measure => ( + <li key={measure.metric.key}> + <div className="domain-measures-name"> + {measure.metric.name} + </div> + <div className="domain-measures-value"> + {measure.value != null && ( + <Link to={{ pathname: measure.metric.key, query: { id: component.key } }}> + {formatMeasure(measure.value, measure.metric.type)} + </Link> + )} + </div> + <div className="domain-measures-value domain-measures-leak"> + {measure.leak != null && ( + <Link to={{ pathname: measure.metric.key, query: { id: component.key } }}> + {formatLeak(measure.leak, measure.metric)} + </Link> + )} + </div> + </li> + ))} + </ul> + </li> + ); +} diff --git a/server/sonar-web/src/main/js/apps/component-measures/components/ListIcon.js b/server/sonar-web/src/main/js/apps/component-measures/components/IconList.js index 6725685c36b..6725685c36b 100644 --- a/server/sonar-web/src/main/js/apps/component-measures/components/ListIcon.js +++ b/server/sonar-web/src/main/js/apps/component-measures/components/IconList.js diff --git a/server/sonar-web/src/main/js/apps/component-measures/components/TreeIcon.js b/server/sonar-web/src/main/js/apps/component-measures/components/IconTree.js index cd8d28d0313..1b9cb2ca79d 100644 --- a/server/sonar-web/src/main/js/apps/component-measures/components/TreeIcon.js +++ b/server/sonar-web/src/main/js/apps/component-measures/components/IconTree.js @@ -19,7 +19,7 @@ */ import React from 'react'; -export default function TreeIcon () { +export default function IconTree () { /* eslint max-len: 0 */ return ( <svg className="measure-tab-icon" diff --git a/server/sonar-web/src/main/js/apps/component-measures/components/UpIcon.js b/server/sonar-web/src/main/js/apps/component-measures/components/IconUp.js index 69c2dd828a1..462d07bb17b 100644 --- a/server/sonar-web/src/main/js/apps/component-measures/components/UpIcon.js +++ b/server/sonar-web/src/main/js/apps/component-measures/components/IconUp.js @@ -19,7 +19,7 @@ */ import React from 'react'; -export default function UpIcon () { +export default function IconUp () { /* eslint max-len: 0 */ return ( <svg className="measure-details-components-up-icon" diff --git a/server/sonar-web/src/main/js/apps/component-measures/components/MeasureDetails.js b/server/sonar-web/src/main/js/apps/component-measures/components/MeasureDetails.js index 20e3cf6c53e..adeef588454 100644 --- a/server/sonar-web/src/main/js/apps/component-measures/components/MeasureDetails.js +++ b/server/sonar-web/src/main/js/apps/component-measures/components/MeasureDetails.js @@ -20,14 +20,12 @@ import React from 'react'; import Spinner from './Spinner'; +import MeasureDetailsHeader from './MeasureDetailsHeader'; import MeasureDrilldown from './MeasureDrilldown'; -import { getLeakValue, formatLeak } from '../utils'; -import { getMeasuresAndMeta } from '../../../api/measures'; -import { formatMeasure } from '../../../helpers/measures'; -import { translateWithParameters } from '../../../helpers/l10n'; -import { TooltipsContainer } from '../../../components/mixins/tooltips-mixin'; -import { getPeriodLabel } from '../../overview/helpers/periods'; +import { enhanceWithLeak } from '../utils'; +import { getMeasuresAndMeta } from '../../../api/measures'; +import { getLeakPeriodLabel } from '../../../helpers/periods'; export default class MeasureDetails extends React.Component { state = {}; @@ -35,10 +33,12 @@ export default class MeasureDetails extends React.Component { componentWillMount () { const { metrics } = this.props; const { metricKey } = this.props.params; - const { router, component } = this.context; - const metric = metrics.find(metric => metric.key === metricKey); - if (!metric) { + this.metric = metrics.find(metric => metric.key === metricKey); + + if (!this.metric) { + const { router, component } = this.context; + router.replace({ pathname: '/', query: { id: component.key } @@ -66,14 +66,16 @@ export default class MeasureDetails extends React.Component { const { metricKey } = this.props.params; const { component } = this.context; - getMeasuresAndMeta(component.key, [metricKey], { additionalFields: 'periods' }).then(r => { + getMeasuresAndMeta( + component.key, + [metricKey], + { additionalFields: 'periods' } + ).then(r => { const measures = r.component.measures; if (this.mounted && measures.length === 1) { - const measure = { - ...measures[0], - leak: getLeakValue(measures[0]) - }; + const measure = enhanceWithLeak(measures[0]); + this.setState({ measure, periods: r.periods @@ -83,48 +85,25 @@ export default class MeasureDetails extends React.Component { } render () { - const { metrics, children } = this.props; - const { metricKey, tab } = this.props.params; - const metric = metrics.find(metric => metric.key === metricKey); const { measure, periods } = this.state; if (!measure) { return <Spinner/>; } - const finalTab = tab || 'tree'; - const leakLabel = getPeriodLabel(periods, 1); + const { tab } = this.props.params; + const leakPeriodLabel = getLeakPeriodLabel(periods); return ( <div className="measure-details"> - <h2 className="measure-details-metric"> - {metric.name} - </h2> - - {measure && ( - <TooltipsContainer> - <div className="measure-details-value"> - {measure.value != null && ( - <span className="measure-details-value-absolute"> - {formatMeasure(measure.value, metric.type)} - </span> - )} - - {measure.leak != null && ( - <span - className="measure-details-value-leak" - title={translateWithParameters('overview.leak_period_x', leakLabel)} - data-toggle="tooltip"> - {formatLeak(measure.leak, metric)} - </span> - )} - </div> - </TooltipsContainer> - )} + <MeasureDetailsHeader + measure={measure} + metric={this.metric} + leakPeriodLabel={leakPeriodLabel}/> {measure && ( - <MeasureDrilldown metric={metric} tab={finalTab}> - {children} + <MeasureDrilldown metric={this.metric} tab={tab}> + {this.props.children} </MeasureDrilldown> )} </div> diff --git a/server/sonar-web/src/main/js/apps/component-measures/components/MeasureDetailsHeader.js b/server/sonar-web/src/main/js/apps/component-measures/components/MeasureDetailsHeader.js new file mode 100644 index 00000000000..93d0e8b58f3 --- /dev/null +++ b/server/sonar-web/src/main/js/apps/component-measures/components/MeasureDetailsHeader.js @@ -0,0 +1,56 @@ +/* + * SonarQube + * Copyright (C) 2009-2016 SonarSource SA + * mailto:contact AT sonarsource DOT com + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 3 of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with this program; if not, write to the Free Software Foundation, + * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ +import React from 'react'; + +import { formatLeak } from '../utils'; +import { formatMeasure } from '../../../helpers/measures'; +import { translateWithParameters } from '../../../helpers/l10n'; +import { TooltipsContainer } from '../../../components/mixins/tooltips-mixin'; + +export default function MeasureDetailsHeader ({ measure, metric, leakPeriodLabel }) { + const leakPeriodTooltip = translateWithParameters('overview.leak_period_x', leakPeriodLabel); + + return ( + <header className="measure-details-header"> + <h2 className="measure-details-metric"> + {metric.name} + </h2> + + <TooltipsContainer> + <div className="measure-details-value"> + {measure.value != null && ( + <div className="measure-details-value-absolute"> + {formatMeasure(measure.value, metric.type)} + </div> + )} + + {measure.leak != null && ( + <div + className="measure-details-value-leak" + title={leakPeriodTooltip} + data-toggle="tooltip"> + {formatLeak(measure.leak, metric)} + </div> + )} + </div> + </TooltipsContainer> + </header> + ); +} diff --git a/server/sonar-web/src/main/js/apps/component-measures/components/MeasureDrilldown.js b/server/sonar-web/src/main/js/apps/component-measures/components/MeasureDrilldown.js index 7d1ebec1867..a66d23ebcda 100644 --- a/server/sonar-web/src/main/js/apps/component-measures/components/MeasureDrilldown.js +++ b/server/sonar-web/src/main/js/apps/component-measures/components/MeasureDrilldown.js @@ -20,8 +20,8 @@ import React from 'react'; import { Link } from 'react-router'; -import ListIcon from './ListIcon'; -import TreeIcon from './TreeIcon'; +import IconList from './IconList'; +import IconTree from './IconTree'; import { translate } from '../../../helpers/l10n'; export default class MeasureDrilldown extends React.Component { @@ -29,19 +29,16 @@ export default class MeasureDrilldown extends React.Component { const { metric, children } = this.props; const { component } = this.context; - const child = React.cloneElement(children, { - component, - metric - }); + const child = React.cloneElement(children, { component, metric }); return ( <div className="measure-details-drilldown"> - <ul className="measure-details-mode"> + <ul className="measure-details-drilldown-mode"> <li> <Link activeClassName="active" to={{ pathname: `${metric.key}/tree`, query: { id: component.key } }}> - <TreeIcon/> + <IconTree/> {translate('component_measures.tab.tree')} </Link> </li> @@ -49,7 +46,7 @@ export default class MeasureDrilldown extends React.Component { <Link activeClassName="active" to={{ pathname: `${metric.key}/list`, query: { id: component.key } }}> - <ListIcon/> + <IconList/> {translate('component_measures.tab.list')} </Link> </li> diff --git a/server/sonar-web/src/main/js/apps/component-measures/components/ComponentsList.js b/server/sonar-web/src/main/js/apps/component-measures/components/MeasureDrilldownComponents.js index 9f0ebd78cbf..a020e83f0db 100644 --- a/server/sonar-web/src/main/js/apps/component-measures/components/ComponentsList.js +++ b/server/sonar-web/src/main/js/apps/component-measures/components/MeasureDrilldownComponents.js @@ -19,12 +19,13 @@ */ import React from 'react'; -import UpIcon from './UpIcon'; +import MeasureDrilldownEmpty from './MeasureDrilldownEmpty'; +import IconUp from './IconUp'; import QualifierIcon from '../../../components/shared/qualifier-icon'; import { formatMeasure } from '../../../helpers/measures'; import { formatLeak } from '../utils'; -export default function ComponentsList ({ components, selected, parent, metric, onClick }) { +export default function MeasureDrilldownComponents ({ components, selected, parent, metric, onClick }) { const handleClick = (component, e) => { e.preventDefault(); e.target.blur(); @@ -32,18 +33,19 @@ export default function ComponentsList ({ components, selected, parent, metric, }; return ( - <ul> + <ul className="measure-details-components"> {parent && ( <li key={parent.id} className="measure-details-components-parent"> <a href="#" onClick={handleClick.bind(this, parent)}> <div className="measure-details-component-name"> - <UpIcon/> - - .. + <IconUp/> .. </div> </a> </li> )} + + {!components.length && <MeasureDrilldownEmpty/>} + {components.map(component => ( <li key={component.id}> <a diff --git a/server/sonar-web/src/main/js/apps/component-measures/components/NoResults.js b/server/sonar-web/src/main/js/apps/component-measures/components/MeasureDrilldownEmpty.js index db8afedd0be..ede861f8382 100644 --- a/server/sonar-web/src/main/js/apps/component-measures/components/NoResults.js +++ b/server/sonar-web/src/main/js/apps/component-measures/components/MeasureDrilldownEmpty.js @@ -21,7 +21,7 @@ import React from 'react'; import { translate } from '../../../helpers/l10n'; -export default function NoResults () { +export default function MeasureDrilldownEmpty () { return ( <div className="measures-details-components-empty note"> {translate('no_results')} diff --git a/server/sonar-web/src/main/js/apps/component-measures/components/MeasureList.js b/server/sonar-web/src/main/js/apps/component-measures/components/MeasureDrilldownList.js index 322d7b5eb13..0a0e49601aa 100644 --- a/server/sonar-web/src/main/js/apps/component-measures/components/MeasureList.js +++ b/server/sonar-web/src/main/js/apps/component-measures/components/MeasureDrilldownList.js @@ -20,13 +20,13 @@ import React from 'react'; import Spinner from './Spinner'; -import ComponentsList from './ComponentsList'; +import MeasureDrilldownComponents from './MeasureDrilldownComponents'; import SourceViewer from '../../code/components/SourceViewer'; -import NoResults from './NoResults'; -import { getSingleMeasureValue, getSingleLeakValue } from '../utils'; + +import { enhanceWithSingleMeasure } from '../utils'; import { getFiles } from '../../../api/components'; -export default class MeasurePlainList extends React.Component { +export default class MeasureDrilldownList extends React.Component { state = { components: [], selected: null, @@ -61,20 +61,12 @@ export default class MeasurePlainList extends React.Component { this.setState({ fetching: true }); - getFiles(baseComponent.key, [metric.key], options).then(children => { + getFiles(baseComponent.key, [metric.key], options).then(files => { if (this.mounted) { - const componentsWithMappedMeasure = children - .map(component => { - return { - ...component, - value: getSingleMeasureValue(component.measures), - leak: getSingleLeakValue(component.measures) - }; - }) - .filter(component => component.value != null || component.leak != null); + const components = enhanceWithSingleMeasure(files); this.setState({ - components: componentsWithMappedMeasure, + components, selected: null, fetching: false }); @@ -94,19 +86,13 @@ export default class MeasurePlainList extends React.Component { return <Spinner/>; } - if (!components.length) { - return <NoResults/>; - } - return ( <div className="measure-details-plain-list"> - <div className="measure-details-components"> - <ComponentsList - components={components} - selected={selected} - metric={metric} - onClick={this.handleFileClick.bind(this)}/> - </div> + <MeasureDrilldownComponents + components={components} + selected={selected} + metric={metric} + onClick={this.handleFileClick.bind(this)}/> {selected && ( <div className="measure-details-viewer"> @@ -118,6 +104,6 @@ export default class MeasurePlainList extends React.Component { } } -MeasurePlainList.contextTypes = { +MeasureDrilldownList.contextTypes = { component: React.PropTypes.object }; diff --git a/server/sonar-web/src/main/js/apps/component-measures/components/MeasureTree.js b/server/sonar-web/src/main/js/apps/component-measures/components/MeasureDrilldownTree.js index d39f0eda0bc..47ae38b67cb 100644 --- a/server/sonar-web/src/main/js/apps/component-measures/components/MeasureTree.js +++ b/server/sonar-web/src/main/js/apps/component-measures/components/MeasureDrilldownTree.js @@ -20,13 +20,13 @@ import React from 'react'; import Spinner from './Spinner'; -import ComponentsList from './ComponentsList'; -import NoResults from './NoResults'; +import MeasureDrilldownComponents from './MeasureDrilldownComponents'; import SourceViewer from '../../code/components/SourceViewer'; -import { getSingleMeasureValue, getSingleLeakValue } from '../utils'; + +import { enhanceWithSingleMeasure } from '../utils'; import { getChildren } from '../../../api/components'; -export default class MeasureTree extends React.Component { +export default class MeasureDrilldownTree extends React.Component { state = { components: [], breadcrumbs: [], @@ -64,17 +64,11 @@ export default class MeasureTree extends React.Component { getChildren(baseComponent.key, [metric.key], options).then(children => { if (this.mounted) { - const componentsWithMappedMeasure = children - .map(component => { - return { - ...component, - value: getSingleMeasureValue(component.measures), - leak: getSingleLeakValue(component.measures) - }; - }) - .filter(component => component.value != null || component.leak != null); - - const indexInBreadcrumbs = this.state.breadcrumbs.findIndex(component => component === baseComponent); + const components = enhanceWithSingleMeasure(children); + + const indexInBreadcrumbs = this.state.breadcrumbs + .findIndex(component => component === baseComponent); + const breadcrumbs = indexInBreadcrumbs !== -1 ? this.state.breadcrumbs.slice(0, indexInBreadcrumbs + 1) : [...this.state.breadcrumbs, baseComponent]; @@ -82,7 +76,7 @@ export default class MeasureTree extends React.Component { this.setState({ baseComponent, breadcrumbs, - components: componentsWithMappedMeasure, + components, selected: null, fetching: false }); @@ -111,20 +105,14 @@ export default class MeasureTree extends React.Component { return <Spinner/>; } - if (!components.length) { - return <NoResults/>; - } - return ( <div className="measure-details-tree"> - <div className="measure-details-components"> - <ComponentsList - components={components} - selected={selected} - parent={parent} - metric={metric} - onClick={this.handleFileClick.bind(this)}/> - </div> + <MeasureDrilldownComponents + components={components} + selected={selected} + parent={parent} + metric={metric} + onClick={this.handleFileClick.bind(this)}/> {selected && ( <div className="measure-details-viewer"> @@ -136,6 +124,6 @@ export default class MeasureTree extends React.Component { } } -MeasureTree.contextTypes = { +MeasureDrilldownTree.contextTypes = { component: React.PropTypes.object }; diff --git a/server/sonar-web/src/main/js/apps/component-measures/styles.css b/server/sonar-web/src/main/js/apps/component-measures/styles.css index 5e2a065cd2d..09e238d3e02 100644 --- a/server/sonar-web/src/main/js/apps/component-measures/styles.css +++ b/server/sonar-web/src/main/js/apps/component-measures/styles.css @@ -1,16 +1,13 @@ -.component-measures-domains { +.measures-domains { width: 600px; margin: 20px auto; } -.component-measures-domains > li { -} - -.component-measures-domains > li + li { +.measures-domains > li + li { margin-top: 40px; } -.component-measures-domains-leak-header { +.measures-domains-leak-header { float: right; line-height: 22px; padding: 0 10px; @@ -18,43 +15,37 @@ background-color: #fbf3d5; } -.component-measures-domain-measures { -} - -.component-measures-domain-measures > li { +.domain-measures > li { display: flex; } -.component-measures-domain-measures > li:nth-child(odd) { +.domain-measures > li:nth-child(odd) { background-color: #f8f8f8; } -.component-measures-domain-measures-name, -.component-measures-domain-measures-value { +.domain-measures-name, +.domain-measures-value { padding: 7px 10px; box-sizing: border-box; } -.component-measures-domain-measures-name { +.domain-measures-name { width: calc(100% - 160px); } -.component-measures-domain-measures-value { +.domain-measures-value { width: 80px; text-align: right; } -.component-measures-leak-cell { +.domain-measures-leak { background-color: #fbf3d5; } -.component-measures-domain-measures > li:nth-child(odd) .component-measures-leak-cell { +.domain-measures > li:nth-child(odd) .domain-measures-leak { background-color: #f5eed0; } -.component-measures-domain-name { - margin-bottom: 8px; -} .measure-details { margin-top: 10px; @@ -95,34 +86,34 @@ margin-top: 20px; } -.measure-details-mode { +.measure-details-drilldown-mode { display: flex; padding-left: 10px; padding-right: 10px; border-bottom: 1px solid #e6e6e6; } -.measure-details-mode > li { +.measure-details-drilldown-mode > li { margin-bottom: -1px; } -.measure-details-mode > li + li { +.measure-details-drilldown-mode > li + li { margin-left: 2px; } -.measure-details-mode > li > a { +.measure-details-drilldown-mode > li > a { display: inline-block; padding: 5px 10px; border-bottom: 2px solid transparent; color: #444; } -.measure-details-mode > li > a:hover, -.measure-details-mode > li > a.active { +.measure-details-drilldown-mode > li > a:hover, +.measure-details-drilldown-mode > li > a.active { border-bottom-color: #4b9fd5; } -.measure-details-mode > li > a.active .measure-tab-icon path { +.measure-details-drilldown-mode > li > a.active .measure-tab-icon path { fill: #4b9fd5; } @@ -140,7 +131,7 @@ padding-bottom: 6px; } -.measure-details-components > ul > li > a { +.measure-details-components > li > a { display: flex; padding-top: 5px; padding-bottom: 5px; @@ -148,8 +139,8 @@ color: #444; } -.measure-details-components > ul > li > a:hover, -.measure-details-components > ul > li > a.selected { +.measure-details-components > li > a:hover, +.measure-details-components > li > a.selected { background-color: #cae3f2 !important; } @@ -207,5 +198,5 @@ } .measures-details-components-empty { - padding: 20px; + padding: 10px; } diff --git a/server/sonar-web/src/main/js/apps/component-measures/utils.js b/server/sonar-web/src/main/js/apps/component-measures/utils.js index 39d0f5ec2cc..b3eef46c626 100644 --- a/server/sonar-web/src/main/js/apps/component-measures/utils.js +++ b/server/sonar-web/src/main/js/apps/component-measures/utils.js @@ -58,3 +58,27 @@ export function formatLeak (value, metric) { return formatMeasureVariation(value, metric.type); } } + +export function enhanceWithLeak (measures) { + function enhanceSingle (measure) { + return { ...measure, leak: getLeakValue(measure) }; + } + + if (Array.isArray(measures)) { + return measures.map(enhanceSingle); + } else { + return enhanceSingle(measures); + } +} + +export function enhanceWithSingleMeasure (components) { + return components + .map(component => { + return { + ...component, + value: getSingleMeasureValue(component.measures), + leak: getSingleLeakValue(component.measures) + }; + }) + .filter(component => component.value != null || component.leak != null); +} diff --git a/server/sonar-web/src/main/js/apps/overview/helpers/periods.js b/server/sonar-web/src/main/js/apps/overview/helpers/periods.js index 3e1e1d5d89f..80bb9b7a308 100644 --- a/server/sonar-web/src/main/js/apps/overview/helpers/periods.js +++ b/server/sonar-web/src/main/js/apps/overview/helpers/periods.js @@ -19,8 +19,8 @@ */ import _ from 'underscore'; import moment from 'moment'; -import { translate, translateWithParameters } from '../../../helpers/l10n'; +import { getPeriodLabel as getLabel } from '../../../helpers/periods'; export function getPeriodLabel (periods, periodIndex) { const period = _.findWhere(periods, { index: periodIndex }); @@ -29,13 +29,7 @@ export function getPeriodLabel (periods, periodIndex) { return null; } - const parameter = period.modeParam || period.parameter; - - if (period.mode === 'previous_version' && !parameter) { - return translate('overview.period.previous_version_only_date'); - } - - return translateWithParameters(`overview.period.${period.mode}`, parameter); + return getLabel(period); } diff --git a/server/sonar-web/src/main/js/helpers/periods.js b/server/sonar-web/src/main/js/helpers/periods.js new file mode 100644 index 00000000000..48006199d2a --- /dev/null +++ b/server/sonar-web/src/main/js/helpers/periods.js @@ -0,0 +1,46 @@ +/* + * SonarQube + * Copyright (C) 2009-2016 SonarSource SA + * mailto:contact AT sonarsource DOT com + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 3 of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with this program; if not, write to the Free Software Foundation, + * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ +import { translate, translateWithParameters } from './l10n'; + +export function getLeakPeriod (periods) { + if (!Array.isArray(periods)) { + return null; + } + + return periods.find(period => period.index === 1); +} + +export function getPeriodLabel (period) { + if (!period) { + return null; + } + + const parameter = period.modeParam || period.parameter; + + if (period.mode === 'previous_version' && !parameter) { + return translate('overview.period.previous_version_only_date'); + } + + return translateWithParameters(`overview.period.${period.mode}`, parameter); +} + +export function getLeakPeriodLabel (periods) { + return getPeriodLabel(getLeakPeriod(periods)); +} |