@@ -92,23 +92,19 @@ export function ActivityPanel(props: ActivityPanelProps) { | |||
<div className="display-flex-column flex-1"> | |||
<div className="overview-panel-padded display-flex-column flex-1"> | |||
<GraphsHeader graph={graph} metrics={metrics} updateGraph={props.onGraphChange} /> | |||
<div | |||
aria-label={translateWithParameters( | |||
<GraphsHistory | |||
analyses={[]} | |||
ariaLabel={translateWithParameters( | |||
'overview.activity.graph_shows_data_for_x', | |||
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 className="overview-panel-padded bordered-top text-right"> |
@@ -36,71 +36,64 @@ exports[`should render correctly 1`] = ` | |||
} | |||
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 { | |||
"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 { | |||
"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 { | |||
"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 | |||
className="overview-panel-padded bordered-top text-right" | |||
@@ -212,71 +205,64 @@ exports[`should render correctly 2`] = ` | |||
} | |||
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 { | |||
"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 { | |||
"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 { | |||
"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 | |||
className="overview-panel-padded bordered-top text-right" |
@@ -40,6 +40,7 @@ interface Props { | |||
showAreas: boolean; | |||
series: Serie[]; | |||
selectedDate?: Date; | |||
graphDescription: string; | |||
updateGraphZoom?: (from?: Date, to?: Date) => void; | |||
updateSelectedDate?: (selectedDate?: Date) => void; | |||
updateTooltip: (selectedDate?: Date) => void; | |||
@@ -78,7 +79,8 @@ export default class GraphHistory extends React.PureComponent<Props, State> { | |||
metricsType, | |||
selectedDate, | |||
series, | |||
showAreas | |||
showAreas, | |||
graphDescription | |||
} = this.props; | |||
const { tooltipIdx, tooltipXPos } = this.state; | |||
@@ -105,6 +107,7 @@ export default class GraphHistory extends React.PureComponent<Props, State> { | |||
series={series} | |||
showAreas={showAreas} | |||
startDate={graphStartDate} | |||
graphDescription={graphDescription} | |||
updateSelectedDate={this.props.updateSelectedDate} | |||
updateTooltip={this.updateTooltip} | |||
updateZoom={this.props.updateGraphZoom} |
@@ -17,10 +17,10 @@ | |||
* along with this program; if not, write to the Free Software Foundation, | |||
* 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 DeferredSpinner from '../../components/ui/DeferredSpinner'; | |||
import { translate } from '../../helpers/l10n'; | |||
import { translate, translateWithParameters } from '../../helpers/l10n'; | |||
import { getBaseUrl } from '../../helpers/system'; | |||
import { GraphType, MeasureHistory, Serie } from '../../types/project-activity'; | |||
import { ParsedAnalysis } from '../../types/types'; | |||
@@ -30,6 +30,7 @@ import { getSeriesMetricType, hasHistoryData, isCustomGraph } from './utils'; | |||
interface Props { | |||
analyses: ParsedAnalysis[]; | |||
ariaLabel?: string; | |||
graph: GraphType; | |||
graphs: Serie[][]; | |||
graphEndDate?: Date; | |||
@@ -79,7 +80,7 @@ export default class GraphsHistory extends React.PureComponent<Props, State> { | |||
}; | |||
render() { | |||
const { graph, loading, series } = this.props; | |||
const { graph, loading, series, ariaLabel } = this.props; | |||
const isCustom = isCustomGraph(graph); | |||
if (loading) { | |||
@@ -117,26 +118,37 @@ export default class GraphsHistory extends React.PureComponent<Props, State> { | |||
const showAreas = [GraphType.coverage, GraphType.duplications].includes(graph); | |||
return ( | |||
<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> | |||
); | |||
} |
@@ -5,6 +5,7 @@ exports[`should correctly render a graph 1`] = ` | |||
className="display-flex-justify-center display-flex-column display-flex-stretch flex-grow" | |||
> | |||
<GraphHistory | |||
ariaLabel="project_activity.graphs.explanation_x.metric.bugs.name" | |||
events={Array []} | |||
graph="issues" | |||
isCustom={false} | |||
@@ -49,6 +50,7 @@ exports[`should correctly render multiple graphs 1`] = ` | |||
className="display-flex-justify-center display-flex-column display-flex-stretch flex-grow" | |||
> | |||
<GraphHistory | |||
ariaLabel="project_activity.graphs.explanation_x.metric.bugs.name" | |||
events={Array []} | |||
graph="issues" | |||
isCustom={false} | |||
@@ -86,6 +88,7 @@ exports[`should correctly render multiple graphs 1`] = ` | |||
updateTooltip={[Function]} | |||
/> | |||
<GraphHistory | |||
ariaLabel="project_activity.graphs.explanation_x.metric.bugs.name" | |||
events={Array []} | |||
graph="issues" | |||
isCustom={false} |
@@ -30,6 +30,7 @@ import './AdvancedTimeline.css'; | |||
import './LineChart.css'; | |||
export interface Props { | |||
graphDescription?: string; | |||
basisCurve?: boolean; | |||
endDate?: Date; | |||
disableZoom?: boolean; | |||
@@ -595,22 +596,36 @@ export default class AdvancedTimeline extends React.PureComponent<Props, State> | |||
}; | |||
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 />; | |||
} | |||
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 ( | |||
<svg | |||
aria-label={graphDescription} | |||
className={classNames('line-chart', { 'chart-zoomed': isZoomed })} | |||
height={this.props.height} | |||
width={this.props.width}> | |||
height={height} | |||
width={width}> | |||
{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.renderDots()} | |||
{this.renderSelectedDate()} |
@@ -1535,6 +1535,7 @@ project_activity.new_code_period_start=New Code Period starts here | |||
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.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_long=New Code is indicated in yellow on the graph. | |||
project_activity.graphs.issues=Issues | |||
@@ -3220,7 +3221,7 @@ overview.project_key.APP=Application Key | |||
overview.project_key.TRK=Project Key | |||
overview.project_key.click_to_copy=Click to copy the key to your clipboard | |||
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.measures=Measures | |||
overview.measures.empty_explanation=Measures on New Code will appear after the second analysis of this branch. |