Commit 4ac42e48 authored by Mike Greiling's avatar Mike Greiling

Merge branch '41783-monitoring-graphs-popover-value-improvements' into 'master'

Resolve "Monitoring graphs - Popover value improvements"

Closes #41783 and gitlab-ee#5573

See merge request gitlab-org/gitlab-ce!18353
parents 94099f2d c93cd377
...@@ -81,9 +81,8 @@ export default { ...@@ -81,9 +81,8 @@ export default {
time: new Date(), time: new Date(),
value: 0, value: 0,
}, },
currentDataIndex: 0,
currentXCoordinate: 0, currentXCoordinate: 0,
currentFlagPosition: 0, currentCoordinates: [],
showFlag: false, showFlag: false,
showFlagContent: false, showFlagContent: false,
timeSeries: [], timeSeries: [],
...@@ -273,6 +272,9 @@ export default { ...@@ -273,6 +272,9 @@ export default {
:line-style="path.lineStyle" :line-style="path.lineStyle"
:line-color="path.lineColor" :line-color="path.lineColor"
:area-color="path.areaColor" :area-color="path.areaColor"
:current-coordinates="currentCoordinates[index]"
:current-time-series-index="index"
:show-dot="showFlagContent"
/> />
<graph-deployment <graph-deployment
:deployment-data="reducedDeploymentData" :deployment-data="reducedDeploymentData"
...@@ -298,9 +300,9 @@ export default { ...@@ -298,9 +300,9 @@ export default {
:show-flag-content="showFlagContent" :show-flag-content="showFlagContent"
:time-series="timeSeries" :time-series="timeSeries"
:unit-of-display="unitOfDisplay" :unit-of-display="unitOfDisplay"
:current-data-index="currentDataIndex"
:legend-title="legendTitle" :legend-title="legendTitle"
:deployment-flag-data="deploymentFlagData" :deployment-flag-data="deploymentFlagData"
:current-coordinates="currentCoordinates"
/> />
</div> </div>
<graph-legend <graph-legend
......
...@@ -47,14 +47,14 @@ export default { ...@@ -47,14 +47,14 @@ export default {
type: String, type: String,
required: true, required: true,
}, },
currentDataIndex: {
type: Number,
required: true,
},
legendTitle: { legendTitle: {
type: String, type: String,
required: true, required: true,
}, },
currentCoordinates: {
type: Array,
required: true,
},
}, },
computed: { computed: {
formatTime() { formatTime() {
...@@ -90,10 +90,12 @@ export default { ...@@ -90,10 +90,12 @@ export default {
}, },
}, },
methods: { methods: {
seriesMetricValue(series) { seriesMetricValue(seriesIndex, series) {
const indexFromCoordinates = this.currentCoordinates[seriesIndex]
? this.currentCoordinates[seriesIndex].currentDataIndex : 0;
const index = this.deploymentFlagData const index = this.deploymentFlagData
? this.deploymentFlagData.seriesIndex ? this.deploymentFlagData.seriesIndex
: this.currentDataIndex; : indexFromCoordinates;
const value = series.values[index] && series.values[index].value; const value = series.values[index] && series.values[index].value;
if (isNaN(value)) { if (isNaN(value)) {
return '-'; return '-';
...@@ -128,7 +130,7 @@ export default { ...@@ -128,7 +130,7 @@ export default {
<h5 v-if="deploymentFlagData"> <h5 v-if="deploymentFlagData">
Deployed Deployed
</h5> </h5>
{{ formatDate }} at {{ formatDate }}
<strong>{{ formatTime }}</strong> <strong>{{ formatTime }}</strong>
</div> </div>
<div <div
...@@ -163,9 +165,11 @@ export default { ...@@ -163,9 +165,11 @@ export default {
:key="index" :key="index"
> >
<track-line :track="series"/> <track-line :track="series"/>
<td>{{ series.track }} {{ seriesMetricLabel(index, series) }}</td>
<td> <td>
<strong>{{ seriesMetricValue(series) }}</strong> {{ series.track }} {{ seriesMetricLabel(index, series) }}
</td>
<td>
<strong>{{ seriesMetricValue(index, series) }}</strong>
</td> </td>
</tr> </tr>
</table> </table>
......
...@@ -22,6 +22,15 @@ export default { ...@@ -22,6 +22,15 @@ export default {
type: String, type: String,
required: true, required: true,
}, },
currentCoordinates: {
type: Object,
required: false,
default: () => ({ currentX: 0, currentY: 0 }),
},
showDot: {
type: Boolean,
required: true,
},
}, },
computed: { computed: {
strokeDashArray() { strokeDashArray() {
...@@ -33,12 +42,20 @@ export default { ...@@ -33,12 +42,20 @@ export default {
}; };
</script> </script>
<template> <template>
<g> <g transform="translate(-5, 20)">
<circle
class="circle-path"
:cx="currentCoordinates.currentX"
:cy="currentCoordinates.currentY"
:fill="lineColor"
:stroke="lineColor"
r="3"
v-if="showDot"
/>
<path <path
class="metric-area" class="metric-area"
:d="generatedAreaPath" :d="generatedAreaPath"
:fill="areaColor" :fill="areaColor"
transform="translate(-5, 20)"
/> />
<path <path
class="metric-line" class="metric-line"
...@@ -47,7 +64,6 @@ export default { ...@@ -47,7 +64,6 @@ export default {
fill="none" fill="none"
stroke-width="1" stroke-width="1"
:stroke-dasharray="strokeDashArray" :stroke-dasharray="strokeDashArray"
transform="translate(-5, 20)"
/> />
</g> </g>
</template> </template>
...@@ -19,16 +19,16 @@ export default { ...@@ -19,16 +19,16 @@ export default {
<template> <template>
<td> <td>
<svg <svg
width="15" width="16"
height="6"> height="8">
<line <line
:stroke-dasharray="stylizedLine" :stroke-dasharray="stylizedLine"
:stroke="track.lineColor" :stroke="track.lineColor"
stroke-width="4" stroke-width="4"
:x1="0" :x1="0"
:x2="15" :x2="16"
:y1="2" :y1="4"
:y2="2" :y2="4"
/> />
</svg> </svg>
</td> </td>
......
...@@ -52,14 +52,22 @@ const mixins = { ...@@ -52,14 +52,22 @@ const mixins = {
positionFlag() { positionFlag() {
const timeSeries = this.timeSeries[0]; const timeSeries = this.timeSeries[0];
const hoveredDataIndex = bisectDate(timeSeries.values, this.hoverData.hoveredDate, 1); const hoveredDataIndex = bisectDate(timeSeries.values, this.hoverData.hoveredDate, 1);
this.currentData = timeSeries.values[hoveredDataIndex]; this.currentData = timeSeries.values[hoveredDataIndex];
this.currentDataIndex = hoveredDataIndex;
this.currentXCoordinate = Math.floor(timeSeries.timeSeriesScaleX(this.currentData.time)); this.currentXCoordinate = Math.floor(timeSeries.timeSeriesScaleX(this.currentData.time));
if (this.currentXCoordinate > (this.graphWidth - 200)) {
this.currentFlagPosition = this.currentXCoordinate - 103; this.currentCoordinates = this.timeSeries.map((series) => {
} else { const currentDataIndex = bisectDate(series.values, this.hoverData.hoveredDate, 1);
this.currentFlagPosition = this.currentXCoordinate; const currentData = series.values[currentDataIndex];
} const currentX = Math.floor(series.timeSeriesScaleX(currentData.time));
const currentY = Math.floor(series.timeSeriesScaleY(currentData.value));
return {
currentX,
currentY,
currentDataIndex,
};
});
if (this.hoverData.currentDeployXPos) { if (this.hoverData.currentDeployXPos) {
this.showFlag = false; this.showFlag = false;
......
...@@ -14,7 +14,7 @@ const d3 = { ...@@ -14,7 +14,7 @@ const d3 = {
timeYear, timeYear,
}; };
export const dateFormat = d3.time('%a, %b %-d'); export const dateFormat = d3.time('%d %b %Y, ');
export const timeFormat = d3.time('%-I:%M%p'); export const timeFormat = d3.time('%-I:%M%p');
export const dateFormatWithName = d3.time('%a, %b %-d'); export const dateFormatWithName = d3.time('%a, %b %-d');
export const bisectDate = d3.bisector(d => d.time).left; export const bisectDate = d3.bisector(d => d.time).left;
......
...@@ -123,6 +123,7 @@ function queryTimeSeries(query, graphWidth, graphHeight, graphHeightOffset, xDom ...@@ -123,6 +123,7 @@ function queryTimeSeries(query, graphWidth, graphHeight, graphHeightOffset, xDom
linePath: lineFunction(timeSeries.values), linePath: lineFunction(timeSeries.values),
areaPath: areaFunction(timeSeries.values), areaPath: areaFunction(timeSeries.values),
timeSeriesScaleX, timeSeriesScaleX,
timeSeriesScaleY,
values: timeSeries.values, values: timeSeries.values,
max: maximumValue, max: maximumValue,
average: accum / timeSeries.values.length, average: accum / timeSeries.values.length,
......
...@@ -283,28 +283,59 @@ ...@@ -283,28 +283,59 @@
} }
&.popover { &.popover {
padding: 0;
border: 1px solid $border-color;
&.left { &.left {
left: auto; left: auto;
right: 0; right: 0;
margin-right: 10px; margin-right: 10px;
> .arrow {
right: -16px;
border-left-color: $border-color;
}
> .arrow::after {
border-left-color: $theme-gray-50;
}
} }
&.right { &.right {
left: 0; left: 0;
right: auto; right: auto;
margin-left: 10px; margin-left: 10px;
> .arrow {
left: -16px;
border-right-color: $border-color;
}
> .arrow::after {
border-right-color: $theme-gray-50;
}
} }
> .arrow { > .arrow {
top: 40px; top: 16px;
margin-top: -8px;
border-width: 8px;
} }
> .popover-title, > .popover-title,
> .popover-content { > .popover-content {
padding: 5px 8px; padding: 8px;
font-size: 12px; font-size: 12px;
white-space: nowrap; white-space: nowrap;
} }
> .popover-title {
background-color: $theme-gray-50;
}
}
strong {
font-weight: 600;
} }
} }
...@@ -317,7 +348,7 @@ ...@@ -317,7 +348,7 @@
vertical-align: middle; vertical-align: middle;
+ td { + td {
padding-left: 5px; padding-left: 8px;
vertical-align: top; vertical-align: top;
} }
} }
......
...@@ -22,15 +22,20 @@ const defaultValuesComponent = { ...@@ -22,15 +22,20 @@ const defaultValuesComponent = {
graphHeightOffset: 120, graphHeightOffset: 120,
showFlagContent: true, showFlagContent: true,
realPixelRatio: 1, realPixelRatio: 1,
timeSeries: [{ timeSeries: [
values: [{ {
values: [
{
time: new Date('2017-06-04T18:17:33.501Z'), time: new Date('2017-06-04T18:17:33.501Z'),
value: '1.49609375', value: '1.49609375',
}], },
}], ],
},
],
unitOfDisplay: 'ms', unitOfDisplay: 'ms',
currentDataIndex: 0, currentDataIndex: 0,
legendTitle: 'Average', legendTitle: 'Average',
currentCoordinates: [],
}; };
const deploymentFlagData = { const deploymentFlagData = {
...@@ -113,7 +118,7 @@ describe('GraphFlag', () => { ...@@ -113,7 +118,7 @@ describe('GraphFlag', () => {
}); });
it('formatDate', () => { it('formatDate', () => {
expect(component.formatDate).toEqual('Sun, Jun 4'); expect(component.formatDate).toEqual('04 Jun 2017, ');
}); });
it('cursorStyle', () => { it('cursorStyle', () => {
......
...@@ -39,14 +39,14 @@ describe('TrackLine component', () => { ...@@ -39,14 +39,14 @@ describe('TrackLine component', () => {
const svgEl = vm.$el.querySelector('svg'); const svgEl = vm.$el.querySelector('svg');
const lineEl = vm.$el.querySelector('svg line'); const lineEl = vm.$el.querySelector('svg line');
expect(svgEl.getAttribute('width')).toEqual('15'); expect(svgEl.getAttribute('width')).toEqual('16');
expect(svgEl.getAttribute('height')).toEqual('6'); expect(svgEl.getAttribute('height')).toEqual('8');
expect(lineEl.getAttribute('stroke-width')).toEqual('4'); expect(lineEl.getAttribute('stroke-width')).toEqual('4');
expect(lineEl.getAttribute('x1')).toEqual('0'); expect(lineEl.getAttribute('x1')).toEqual('0');
expect(lineEl.getAttribute('x2')).toEqual('15'); expect(lineEl.getAttribute('x2')).toEqual('16');
expect(lineEl.getAttribute('y1')).toEqual('2'); expect(lineEl.getAttribute('y1')).toEqual('4');
expect(lineEl.getAttribute('y2')).toEqual('2'); expect(lineEl.getAttribute('y2')).toEqual('4');
}); });
}); });
}); });
...@@ -23,6 +23,7 @@ describe('Monitoring Paths', () => { ...@@ -23,6 +23,7 @@ describe('Monitoring Paths', () => {
generatedAreaPath: firstTimeSeries.areaPath, generatedAreaPath: firstTimeSeries.areaPath,
lineColor: firstTimeSeries.lineColor, lineColor: firstTimeSeries.lineColor,
areaColor: firstTimeSeries.areaColor, areaColor: firstTimeSeries.areaColor,
showDot: false,
}); });
const metricArea = component.$el.querySelector('.metric-area'); const metricArea = component.$el.querySelector('.metric-area');
const metricLine = component.$el.querySelector('.metric-line'); const metricLine = component.$el.querySelector('.metric-line');
...@@ -40,6 +41,7 @@ describe('Monitoring Paths', () => { ...@@ -40,6 +41,7 @@ describe('Monitoring Paths', () => {
generatedAreaPath: firstTimeSeries.areaPath, generatedAreaPath: firstTimeSeries.areaPath,
lineColor: firstTimeSeries.lineColor, lineColor: firstTimeSeries.lineColor,
areaColor: firstTimeSeries.areaColor, areaColor: firstTimeSeries.areaColor,
showDot: false,
}); });
component.lineStyle = 'dashed'; component.lineStyle = 'dashed';
......
...@@ -30,7 +30,6 @@ describe('Graph', () => { ...@@ -30,7 +30,6 @@ describe('Graph', () => {
it('has a title', () => { it('has a title', () => {
const component = createComponent({ const component = createComponent({
graphData: convertedMetrics[1], graphData: convertedMetrics[1],
classType: 'col-md-6',
updateAspectRatio: false, updateAspectRatio: false,
deploymentData, deploymentData,
tagsPath, tagsPath,
...@@ -46,7 +45,6 @@ describe('Graph', () => { ...@@ -46,7 +45,6 @@ describe('Graph', () => {
it('axisTransform translates an element Y position depending of its height', () => { it('axisTransform translates an element Y position depending of its height', () => {
const component = createComponent({ const component = createComponent({
graphData: convertedMetrics[1], graphData: convertedMetrics[1],
classType: 'col-md-6',
updateAspectRatio: false, updateAspectRatio: false,
deploymentData, deploymentData,
tagsPath, tagsPath,
...@@ -62,7 +60,6 @@ describe('Graph', () => { ...@@ -62,7 +60,6 @@ describe('Graph', () => {
it('outerViewBox gets a width and height property based on the DOM size of the element', () => { it('outerViewBox gets a width and height property based on the DOM size of the element', () => {
const component = createComponent({ const component = createComponent({
graphData: convertedMetrics[1], graphData: convertedMetrics[1],
classType: 'col-md-6',
updateAspectRatio: false, updateAspectRatio: false,
deploymentData, deploymentData,
tagsPath, tagsPath,
...@@ -79,7 +76,6 @@ describe('Graph', () => { ...@@ -79,7 +76,6 @@ describe('Graph', () => {
it('sends an event to the eventhub when it has finished resizing', done => { it('sends an event to the eventhub when it has finished resizing', done => {
const component = createComponent({ const component = createComponent({
graphData: convertedMetrics[1], graphData: convertedMetrics[1],
classType: 'col-md-6',
updateAspectRatio: false, updateAspectRatio: false,
deploymentData, deploymentData,
tagsPath, tagsPath,
...@@ -97,7 +93,6 @@ describe('Graph', () => { ...@@ -97,7 +93,6 @@ describe('Graph', () => {
it('has a title for the y-axis and the chart legend that comes from the backend', () => { it('has a title for the y-axis and the chart legend that comes from the backend', () => {
const component = createComponent({ const component = createComponent({
graphData: convertedMetrics[1], graphData: convertedMetrics[1],
classType: 'col-md-6',
updateAspectRatio: false, updateAspectRatio: false,
deploymentData, deploymentData,
tagsPath, tagsPath,
...@@ -111,7 +106,6 @@ describe('Graph', () => { ...@@ -111,7 +106,6 @@ describe('Graph', () => {
it('sets the currentData object based on the hovered data index', () => { it('sets the currentData object based on the hovered data index', () => {
const component = createComponent({ const component = createComponent({
graphData: convertedMetrics[1], graphData: convertedMetrics[1],
classType: 'col-md-6',
updateAspectRatio: false, updateAspectRatio: false,
deploymentData, deploymentData,
graphIdentifier: 0, graphIdentifier: 0,
...@@ -125,6 +119,5 @@ describe('Graph', () => { ...@@ -125,6 +119,5 @@ describe('Graph', () => {
component.positionFlag(); component.positionFlag();
expect(component.currentData).toBe(component.timeSeries[0].values[10]); expect(component.currentData).toBe(component.timeSeries[0].values[10]);
expect(component.currentDataIndex).toEqual(10);
}); });
}); });
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment