<div className="display-flex-column flex-1"> | <div className="display-flex-column flex-1"> | ||||
<div className="overview-panel-padded display-flex-column flex-1"> | <div className="overview-panel-padded display-flex-column flex-1"> | ||||
<GraphsHeader graph={graph} metrics={metrics} updateGraph={props.onGraphChange} /> | <GraphsHeader graph={graph} metrics={metrics} updateGraph={props.onGraphChange} /> | ||||
<div | |||||
aria-label={translateWithParameters( | |||||
<GraphsHistory | |||||
analyses={[]} | |||||
ariaLabel={translateWithParameters( | |||||
'overview.activity.graph_shows_data_for_x', | 'overview.activity.graph_shows_data_for_x', | ||||
displayedMetrics.map(metricKey => localizeMetric(metricKey)).join(', ') | displayedMetrics.map(metricKey => localizeMetric(metricKey)).join(', ') | ||||
)}> | |||||
<div aria-hidden={true}> | |||||
<GraphsHistory | |||||
analyses={[]} | |||||
graph={graph} | |||||
graphs={graphs} | |||||
leakPeriodDate={shownLeakPeriodDate} | |||||
loading={Boolean(loading)} | |||||
measuresHistory={measuresHistory} | |||||
series={series} | |||||
/> | |||||
</div> | |||||
</div> | |||||
)} | |||||
graph={graph} | |||||
graphs={graphs} | |||||
leakPeriodDate={shownLeakPeriodDate} | |||||
loading={Boolean(loading)} | |||||
measuresHistory={measuresHistory} | |||||
series={series} | |||||
/> | |||||
</div> | </div> | ||||
<div className="overview-panel-padded bordered-top text-right"> | <div className="overview-panel-padded bordered-top text-right"> |
} | } | ||||
updateGraph={[MockFunction]} | updateGraph={[MockFunction]} | ||||
/> | /> | ||||
<div | |||||
aria-label="overview.activity.graph_shows_data_for_x.metric.bugs.name, metric.code_smells.name, metric.vulnerabilities.name" | |||||
> | |||||
<div | |||||
aria-hidden={true} | |||||
> | |||||
<GraphsHistory | |||||
analyses={Array []} | |||||
graph="issues" | |||||
graphs={ | |||||
Array [ | |||||
Array [ | |||||
<GraphsHistory | |||||
analyses={Array []} | |||||
ariaLabel="overview.activity.graph_shows_data_for_x.metric.bugs.name, metric.code_smells.name, metric.vulnerabilities.name" | |||||
graph="issues" | |||||
graphs={ | |||||
Array [ | |||||
Array [ | |||||
Object { | |||||
"data": Array [ | |||||
Object { | Object { | ||||
"data": Array [ | |||||
Object { | |||||
"x": 2016-10-27T14:33:50.000Z, | |||||
"y": 20, | |||||
}, | |||||
], | |||||
"name": "bugs", | |||||
"translatedName": "Bugs", | |||||
"type": "PERCENT", | |||||
"x": 2016-10-27T14:33:50.000Z, | |||||
"y": 20, | |||||
}, | }, | ||||
], | ], | ||||
] | |||||
} | |||||
loading={false} | |||||
measuresHistory={ | |||||
Array [ | |||||
"name": "bugs", | |||||
"translatedName": "Bugs", | |||||
"type": "PERCENT", | |||||
}, | |||||
], | |||||
] | |||||
} | |||||
loading={false} | |||||
measuresHistory={ | |||||
Array [ | |||||
Object { | |||||
"bestValue": true, | |||||
"history": Array [ | |||||
Object { | Object { | ||||
"bestValue": true, | |||||
"history": Array [ | |||||
Object { | |||||
"date": 2016-10-27T14:33:50.000Z, | |||||
"value": "20", | |||||
}, | |||||
], | |||||
"metric": "bugs", | |||||
"period": Object { | |||||
"bestValue": true, | |||||
"index": 1, | |||||
"value": "1.0", | |||||
}, | |||||
"value": "1.0", | |||||
"date": 2016-10-27T14:33:50.000Z, | |||||
"value": "20", | |||||
}, | }, | ||||
] | |||||
} | |||||
series={ | |||||
Array [ | |||||
], | |||||
"metric": "bugs", | |||||
"period": Object { | |||||
"bestValue": true, | |||||
"index": 1, | |||||
"value": "1.0", | |||||
}, | |||||
"value": "1.0", | |||||
}, | |||||
] | |||||
} | |||||
series={ | |||||
Array [ | |||||
Object { | |||||
"data": Array [ | |||||
Object { | Object { | ||||
"data": Array [ | |||||
Object { | |||||
"x": 2016-10-27T14:33:50.000Z, | |||||
"y": 20, | |||||
}, | |||||
], | |||||
"name": "bugs", | |||||
"translatedName": "Bugs", | |||||
"type": "PERCENT", | |||||
"x": 2016-10-27T14:33:50.000Z, | |||||
"y": 20, | |||||
}, | }, | ||||
] | |||||
} | |||||
/> | |||||
</div> | |||||
</div> | |||||
], | |||||
"name": "bugs", | |||||
"translatedName": "Bugs", | |||||
"type": "PERCENT", | |||||
}, | |||||
] | |||||
} | |||||
/> | |||||
</div> | </div> | ||||
<div | <div | ||||
className="overview-panel-padded bordered-top text-right" | className="overview-panel-padded bordered-top text-right" | ||||
} | } | ||||
updateGraph={[MockFunction]} | updateGraph={[MockFunction]} | ||||
/> | /> | ||||
<div | |||||
aria-label="overview.activity.graph_shows_data_for_x.metric.bugs.name, metric.code_smells.name, metric.vulnerabilities.name" | |||||
> | |||||
<div | |||||
aria-hidden={true} | |||||
> | |||||
<GraphsHistory | |||||
analyses={Array []} | |||||
graph="issues" | |||||
graphs={ | |||||
Array [ | |||||
Array [ | |||||
<GraphsHistory | |||||
analyses={Array []} | |||||
ariaLabel="overview.activity.graph_shows_data_for_x.metric.bugs.name, metric.code_smells.name, metric.vulnerabilities.name" | |||||
graph="issues" | |||||
graphs={ | |||||
Array [ | |||||
Array [ | |||||
Object { | |||||
"data": Array [ | |||||
Object { | Object { | ||||
"data": Array [ | |||||
Object { | |||||
"x": 2016-10-27T14:33:50.000Z, | |||||
"y": 20, | |||||
}, | |||||
], | |||||
"name": "bugs", | |||||
"translatedName": "Bugs", | |||||
"type": "PERCENT", | |||||
"x": 2016-10-27T14:33:50.000Z, | |||||
"y": 20, | |||||
}, | }, | ||||
], | ], | ||||
] | |||||
} | |||||
loading={true} | |||||
measuresHistory={ | |||||
Array [ | |||||
"name": "bugs", | |||||
"translatedName": "Bugs", | |||||
"type": "PERCENT", | |||||
}, | |||||
], | |||||
] | |||||
} | |||||
loading={true} | |||||
measuresHistory={ | |||||
Array [ | |||||
Object { | |||||
"bestValue": true, | |||||
"history": Array [ | |||||
Object { | Object { | ||||
"bestValue": true, | |||||
"history": Array [ | |||||
Object { | |||||
"date": 2016-10-27T14:33:50.000Z, | |||||
"value": "20", | |||||
}, | |||||
], | |||||
"metric": "bugs", | |||||
"period": Object { | |||||
"bestValue": true, | |||||
"index": 1, | |||||
"value": "1.0", | |||||
}, | |||||
"value": "1.0", | |||||
"date": 2016-10-27T14:33:50.000Z, | |||||
"value": "20", | |||||
}, | }, | ||||
] | |||||
} | |||||
series={ | |||||
Array [ | |||||
], | |||||
"metric": "bugs", | |||||
"period": Object { | |||||
"bestValue": true, | |||||
"index": 1, | |||||
"value": "1.0", | |||||
}, | |||||
"value": "1.0", | |||||
}, | |||||
] | |||||
} | |||||
series={ | |||||
Array [ | |||||
Object { | |||||
"data": Array [ | |||||
Object { | Object { | ||||
"data": Array [ | |||||
Object { | |||||
"x": 2016-10-27T14:33:50.000Z, | |||||
"y": 20, | |||||
}, | |||||
], | |||||
"name": "bugs", | |||||
"translatedName": "Bugs", | |||||
"type": "PERCENT", | |||||
"x": 2016-10-27T14:33:50.000Z, | |||||
"y": 20, | |||||
}, | }, | ||||
] | |||||
} | |||||
/> | |||||
</div> | |||||
</div> | |||||
], | |||||
"name": "bugs", | |||||
"translatedName": "Bugs", | |||||
"type": "PERCENT", | |||||
}, | |||||
] | |||||
} | |||||
/> | |||||
</div> | </div> | ||||
<div | <div | ||||
className="overview-panel-padded bordered-top text-right" | className="overview-panel-padded bordered-top text-right" |
showAreas: boolean; | showAreas: boolean; | ||||
series: Serie[]; | series: Serie[]; | ||||
selectedDate?: Date; | selectedDate?: Date; | ||||
graphDescription: string; | |||||
updateGraphZoom?: (from?: Date, to?: Date) => void; | updateGraphZoom?: (from?: Date, to?: Date) => void; | ||||
updateSelectedDate?: (selectedDate?: Date) => void; | updateSelectedDate?: (selectedDate?: Date) => void; | ||||
updateTooltip: (selectedDate?: Date) => void; | updateTooltip: (selectedDate?: Date) => void; | ||||
metricsType, | metricsType, | ||||
selectedDate, | selectedDate, | ||||
series, | series, | ||||
showAreas | |||||
showAreas, | |||||
graphDescription | |||||
} = this.props; | } = this.props; | ||||
const { tooltipIdx, tooltipXPos } = this.state; | const { tooltipIdx, tooltipXPos } = this.state; | ||||
series={series} | series={series} | ||||
showAreas={showAreas} | showAreas={showAreas} | ||||
startDate={graphStartDate} | startDate={graphStartDate} | ||||
graphDescription={graphDescription} | |||||
updateSelectedDate={this.props.updateSelectedDate} | updateSelectedDate={this.props.updateSelectedDate} | ||||
updateTooltip={this.updateTooltip} | updateTooltip={this.updateTooltip} | ||||
updateZoom={this.props.updateGraphZoom} | updateZoom={this.props.updateGraphZoom} |
* along with this program; if not, write to the Free Software Foundation, | * along with this program; if not, write to the Free Software Foundation, | ||||
* Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. | * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. | ||||
*/ | */ | ||||
import { isEqual } from 'lodash'; | |||||
import { isEqual, uniqBy } from 'lodash'; | |||||
import * as React from 'react'; | import * as React from 'react'; | ||||
import DeferredSpinner from '../../components/ui/DeferredSpinner'; | import DeferredSpinner from '../../components/ui/DeferredSpinner'; | ||||
import { translate } from '../../helpers/l10n'; | |||||
import { translate, translateWithParameters } from '../../helpers/l10n'; | |||||
import { getBaseUrl } from '../../helpers/system'; | import { getBaseUrl } from '../../helpers/system'; | ||||
import { GraphType, MeasureHistory, Serie } from '../../types/project-activity'; | import { GraphType, MeasureHistory, Serie } from '../../types/project-activity'; | ||||
import { ParsedAnalysis } from '../../types/types'; | import { ParsedAnalysis } from '../../types/types'; | ||||
interface Props { | interface Props { | ||||
analyses: ParsedAnalysis[]; | analyses: ParsedAnalysis[]; | ||||
ariaLabel?: string; | |||||
graph: GraphType; | graph: GraphType; | ||||
graphs: Serie[][]; | graphs: Serie[][]; | ||||
graphEndDate?: Date; | graphEndDate?: Date; | ||||
}; | }; | ||||
render() { | render() { | ||||
const { graph, loading, series } = this.props; | |||||
const { graph, loading, series, ariaLabel } = this.props; | |||||
const isCustom = isCustomGraph(graph); | const isCustom = isCustomGraph(graph); | ||||
if (loading) { | if (loading) { | ||||
const showAreas = [GraphType.coverage, GraphType.duplications].includes(graph); | const showAreas = [GraphType.coverage, GraphType.duplications].includes(graph); | ||||
return ( | return ( | ||||
<div className="display-flex-justify-center display-flex-column display-flex-stretch flex-grow"> | <div className="display-flex-justify-center display-flex-column display-flex-stretch flex-grow"> | ||||
{this.props.graphs.map((graphSeries, idx) => ( | |||||
<GraphHistory | |||||
events={events} | |||||
graph={graph} | |||||
graphEndDate={this.props.graphEndDate} | |||||
graphStartDate={this.props.graphStartDate} | |||||
isCustom={isCustom} | |||||
key={idx} | |||||
leakPeriodDate={this.props.leakPeriodDate} | |||||
measuresHistory={this.props.measuresHistory} | |||||
metricsType={getSeriesMetricType(graphSeries)} | |||||
removeCustomMetric={this.props.removeCustomMetric} | |||||
selectedDate={this.state.selectedDate} | |||||
series={graphSeries} | |||||
showAreas={showAreas} | |||||
updateGraphZoom={this.props.updateGraphZoom} | |||||
updateSelectedDate={this.props.updateSelectedDate} | |||||
updateTooltip={this.updateTooltip} | |||||
/> | |||||
))} | |||||
{this.props.graphs.map((graphSeries, idx) => { | |||||
return ( | |||||
<GraphHistory | |||||
events={events} | |||||
graph={graph} | |||||
graphEndDate={this.props.graphEndDate} | |||||
graphStartDate={this.props.graphStartDate} | |||||
isCustom={isCustom} | |||||
key={idx} | |||||
leakPeriodDate={this.props.leakPeriodDate} | |||||
measuresHistory={this.props.measuresHistory} | |||||
metricsType={getSeriesMetricType(graphSeries)} | |||||
removeCustomMetric={this.props.removeCustomMetric} | |||||
selectedDate={this.state.selectedDate} | |||||
series={graphSeries} | |||||
graphDescription={ | |||||
ariaLabel || | |||||
translateWithParameters( | |||||
'project_activity.graphs.explanation_x', | |||||
uniqBy(graphSeries, 'name') | |||||
.map(({ translatedName }) => translatedName) | |||||
.join(', ') | |||||
) | |||||
} | |||||
showAreas={showAreas} | |||||
updateGraphZoom={this.props.updateGraphZoom} | |||||
updateSelectedDate={this.props.updateSelectedDate} | |||||
updateTooltip={this.updateTooltip} | |||||
/> | |||||
); | |||||
})} | |||||
</div> | </div> | ||||
); | ); | ||||
} | } |
className="display-flex-justify-center display-flex-column display-flex-stretch flex-grow" | className="display-flex-justify-center display-flex-column display-flex-stretch flex-grow" | ||||
> | > | ||||
<GraphHistory | <GraphHistory | ||||
ariaLabel="project_activity.graphs.explanation_x.metric.bugs.name" | |||||
events={Array []} | events={Array []} | ||||
graph="issues" | graph="issues" | ||||
isCustom={false} | isCustom={false} | ||||
className="display-flex-justify-center display-flex-column display-flex-stretch flex-grow" | className="display-flex-justify-center display-flex-column display-flex-stretch flex-grow" | ||||
> | > | ||||
<GraphHistory | <GraphHistory | ||||
ariaLabel="project_activity.graphs.explanation_x.metric.bugs.name" | |||||
events={Array []} | events={Array []} | ||||
graph="issues" | graph="issues" | ||||
isCustom={false} | isCustom={false} | ||||
updateTooltip={[Function]} | updateTooltip={[Function]} | ||||
/> | /> | ||||
<GraphHistory | <GraphHistory | ||||
ariaLabel="project_activity.graphs.explanation_x.metric.bugs.name" | |||||
events={Array []} | events={Array []} | ||||
graph="issues" | graph="issues" | ||||
isCustom={false} | isCustom={false} |
import './LineChart.css'; | import './LineChart.css'; | ||||
export interface Props { | export interface Props { | ||||
graphDescription?: string; | |||||
basisCurve?: boolean; | basisCurve?: boolean; | ||||
endDate?: Date; | endDate?: Date; | ||||
disableZoom?: boolean; | disableZoom?: boolean; | ||||
}; | }; | ||||
render() { | render() { | ||||
if (!this.props.width || !this.props.height) { | |||||
const { | |||||
width, | |||||
height, | |||||
padding, | |||||
disableZoom, | |||||
startDate, | |||||
endDate, | |||||
leakPeriodDate, | |||||
hideGrid, | |||||
hideXAxis, | |||||
showAreas, | |||||
graphDescription | |||||
} = this.props; | |||||
if (!width || !height) { | |||||
return <div />; | return <div />; | ||||
} | } | ||||
const zoomEnabled = !this.props.disableZoom && this.props.updateZoom != null; | |||||
const isZoomed = Boolean(this.props.startDate || this.props.endDate); | |||||
const zoomEnabled = !disableZoom && this.props.updateZoom != null; | |||||
const isZoomed = Boolean(startDate || endDate); | |||||
return ( | return ( | ||||
<svg | <svg | ||||
aria-label={graphDescription} | |||||
className={classNames('line-chart', { 'chart-zoomed': isZoomed })} | className={classNames('line-chart', { 'chart-zoomed': isZoomed })} | ||||
height={this.props.height} | |||||
width={this.props.width}> | |||||
height={height} | |||||
width={width}> | |||||
{zoomEnabled && this.renderClipPath()} | {zoomEnabled && this.renderClipPath()} | ||||
<g transform={`translate(${this.props.padding[3]}, ${this.props.padding[0]})`}> | |||||
{this.props.leakPeriodDate != null && this.renderLeak()} | |||||
{!this.props.hideGrid && this.renderHorizontalGrid()} | |||||
{!this.props.hideXAxis && this.renderXAxisTicks()} | |||||
{this.props.showAreas && this.renderAreas()} | |||||
<g transform={`translate(${padding[3]}, ${padding[0]})`}> | |||||
{leakPeriodDate != null && this.renderLeak()} | |||||
{!hideGrid && this.renderHorizontalGrid()} | |||||
{!hideXAxis && this.renderXAxisTicks()} | |||||
{showAreas && this.renderAreas()} | |||||
{this.renderLines()} | {this.renderLines()} | ||||
{this.renderDots()} | {this.renderDots()} | ||||
{this.renderSelectedDate()} | {this.renderSelectedDate()} |
project_activity.new_code_period_start.help=The analysis before this mark is the baseline for New Code comparison | project_activity.new_code_period_start.help=The analysis before this mark is the baseline for New Code comparison | ||||
project_activity.graphs.choose_type=Choose graph type | project_activity.graphs.choose_type=Choose graph type | ||||
project_activity.graphs.explanation_x=This interactive graph shows data for the following project measures over time: {0} | |||||
project_activity.graphs.new_code=New Code | project_activity.graphs.new_code=New Code | ||||
project_activity.graphs.new_code_long=New Code is indicated in yellow on the graph. | project_activity.graphs.new_code_long=New Code is indicated in yellow on the graph. | ||||
project_activity.graphs.issues=Issues | project_activity.graphs.issues=Issues | ||||
overview.project_key.TRK=Project Key | overview.project_key.TRK=Project Key | ||||
overview.project_key.click_to_copy=Click to copy the key to your clipboard | overview.project_key.click_to_copy=Click to copy the key to your clipboard | ||||
overview.activity=Activity | overview.activity=Activity | ||||
overview.activity.graph_shows_data_for_x=This space normally shows historical data for {0}. Click on the "Activity" link below to see more information. | |||||
overview.activity.graph_shows_data_for_x=This graph shows historical data for {0}. Click on the "Activity" link below to see more information. | |||||
overview.recent_activity=Recent Activity | overview.recent_activity=Recent Activity | ||||
overview.measures=Measures | overview.measures=Measures | ||||
overview.measures.empty_explanation=Measures on New Code will appear after the second analysis of this branch. | overview.measures.empty_explanation=Measures on New Code will appear after the second analysis of this branch. |