Commit 146954d0 authored by Jose Ivan Vargas's avatar Jose Ivan Vargas

UX Adjustments for the prometheus dashboard

parent f4ff62fa
...@@ -7,7 +7,8 @@ ...@@ -7,7 +7,8 @@
* @param {String} text * @param {String} text
* @returns {String} * @returns {String}
*/ */
export const addDelimiter = text => (text ? text.toString().replace(/\B(?=(\d{3})+(?!\d))/g, ',') : text); export const addDelimiter = text =>
(text ? text.toString().replace(/\B(?=(\d{3})+(?!\d))/g, ',') : text);
/** /**
* Returns '99+' for numbers bigger than 99. * Returns '99+' for numbers bigger than 99.
...@@ -22,7 +23,8 @@ export const highCountTrim = count => (count > 99 ? '99+' : count); ...@@ -22,7 +23,8 @@ export const highCountTrim = count => (count > 99 ? '99+' : count);
* @param {String} string * @param {String} string
* @requires {String} * @requires {String}
*/ */
export const humanize = string => string.charAt(0).toUpperCase() + string.replace(/_/g, ' ').slice(1); export const humanize = string =>
string.charAt(0).toUpperCase() + string.replace(/_/g, ' ').slice(1);
/** /**
* Adds an 's' to the end of the string when count is bigger than 0 * Adds an 's' to the end of the string when count is bigger than 0
...@@ -53,7 +55,7 @@ export const slugify = str => str.trim().toLowerCase(); ...@@ -53,7 +55,7 @@ export const slugify = str => str.trim().toLowerCase();
* @param {Number} maxLength * @param {Number} maxLength
* @returns {String} * @returns {String}
*/ */
export const truncate = (string, maxLength) => `${string.substr(0, (maxLength - 3))}...`; export const truncate = (string, maxLength) => `${string.substr(0, maxLength - 3)}...`;
/** /**
* Capitalizes first character * Capitalizes first character
...@@ -80,3 +82,15 @@ export const stripHtml = (string, replace = '') => string.replace(/<[^>]*>/g, re ...@@ -80,3 +82,15 @@ export const stripHtml = (string, replace = '') => string.replace(/<[^>]*>/g, re
* @param {*} string * @param {*} string
*/ */
export const convertToCamelCase = string => string.replace(/(_\w)/g, s => s[1].toUpperCase()); export const convertToCamelCase = string => string.replace(/(_\w)/g, s => s[1].toUpperCase());
/**
* Converts a sentence to lower case from the second word onwards
* e.g. Hello World => Hello world
*
* @param {*} string
*/
export const convertToSentenceCase = string => {
const splitWord = string.split(' ').map((word, index) => (index > 0 ? word.toLowerCase() : word));
return splitWord.join(' ');
};
...@@ -255,6 +255,7 @@ export default { ...@@ -255,6 +255,7 @@ export default {
:margin="margin" :margin="margin"
:measurements="measurements" :measurements="measurements"
:y-axis-label="yAxisLabel" :y-axis-label="yAxisLabel"
:unit-of-display="unitOfDisplay"
/> />
<svg <svg
class="graph-data" class="graph-data"
...@@ -304,7 +305,6 @@ export default { ...@@ -304,7 +305,6 @@ export default {
:legend-title="legendTitle" :legend-title="legendTitle"
:time-series="timeSeries" :time-series="timeSeries"
:current-data-index="currentDataIndex" :current-data-index="currentDataIndex"
:unit-of-display="unitOfDisplay"
/> />
</div> </div>
</template> </template>
<script> <script>
import { convertToSentenceCase } from '~/lib/utils/text_utility';
export default { export default {
props: { props: {
graphWidth: { graphWidth: {
...@@ -21,6 +23,10 @@ export default { ...@@ -21,6 +23,10 @@ export default {
type: String, type: String,
required: true, required: true,
}, },
unitOfDisplay: {
type: String,
required: true,
},
}, },
data() { data() {
return { return {
...@@ -64,6 +70,10 @@ export default { ...@@ -64,6 +70,10 @@ export default {
this.measurements.axisLabelLineOffset || 0 this.measurements.axisLabelLineOffset || 0
); );
}, },
yAxisLabelSentenceCase() {
return `${convertToSentenceCase(this.yAxisLabel)} (${this.unitOfDisplay})`;
},
}, },
mounted() { mounted() {
this.$nextTick(() => { this.$nextTick(() => {
...@@ -106,7 +116,7 @@ export default { ...@@ -106,7 +116,7 @@ export default {
:transform="textTransform" :transform="textTransform"
ref="ylabel" ref="ylabel"
> >
{{ yAxisLabel }} {{ yAxisLabelSentenceCase }}
</text> </text>
<rect <rect
class="rect-axis-text" class="rect-axis-text"
......
...@@ -181,7 +181,7 @@ export default { ...@@ -181,7 +181,7 @@ export default {
/> />
</svg> </svg>
</td> </td>
<td>{{ seriesMetricLabel(index, series) }}</td> <td>{{ series.track }} {{ seriesMetricLabel(index, series) }}</td>
<td> <td>
<strong>{{ seriesMetricValue(series) }}</strong> <strong>{{ seriesMetricValue(series) }}</strong>
</td> </td>
......
...@@ -15,21 +15,8 @@ export default { ...@@ -15,21 +15,8 @@ export default {
type: Number, type: Number,
required: true, required: true,
}, },
unitOfDisplay: {
type: String,
required: true,
},
}, },
methods: { methods: {
formatMetricUsage(series) {
const value =
series.values[this.currentDataIndex] && series.values[this.currentDataIndex].value;
if (isNaN(value)) {
return '-';
}
return `${formatRelevantDigits(value)} ${this.unitOfDisplay}`;
},
summaryMetrics(series) { summaryMetrics(series) {
return `Avg: ${formatRelevantDigits(series.average)} · Max: ${formatRelevantDigits(series.max)}`; return `Avg: ${formatRelevantDigits(series.average)} · Max: ${formatRelevantDigits(series.max)}`;
}, },
...@@ -49,6 +36,9 @@ export default { ...@@ -49,6 +36,9 @@ export default {
v-for="(series, index) in timeSeries" v-for="(series, index) in timeSeries"
:key="index" :key="index"
> >
<td>
<strong>{{ series.track }}</strong>
</td>
<td> <td>
<svg <svg
width="15" width="15"
...@@ -70,17 +60,15 @@ export default { ...@@ -70,17 +60,15 @@ export default {
v-if="timeSeries.length > 1" v-if="timeSeries.length > 1"
> >
<template v-if="series.metricTag"> <template v-if="series.metricTag">
<strong>{{ series.metricTag }}</strong> <strong>{{ series.metricTag }}</strong> {{ summaryMetrics(series) }}
{{ formatMetricUsage(series) }} {{ summaryMetrics(series) }}
</template> </template>
<template v-else> <template v-else>
<strong>{{ legendTitle }}</strong> <strong>{{ legendTitle }}</strong>
series {{ index + 1 }} {{ formatMetricUsage(series) }} {{ summaryMetrics(series) }} series {{ index + 1 }} {{ summaryMetrics(series) }}
</template> </template>
</td> </td>
<td v-else> <td v-else>
<strong>{{ legendTitle }}</strong> <strong>{{ legendTitle }}</strong> {{ summaryMetrics(series) }}
{{ formatMetricUsage(series) }} {{ summaryMetrics(series) }}
</td> </td>
</tr> </tr>
</table> </table>
......
...@@ -3,6 +3,7 @@ import { scaleLinear, scaleTime } from 'd3-scale'; ...@@ -3,6 +3,7 @@ import { scaleLinear, scaleTime } from 'd3-scale';
import { line, area, curveLinear } from 'd3-shape'; import { line, area, curveLinear } from 'd3-shape';
import { extent, max, sum } from 'd3-array'; import { extent, max, sum } from 'd3-array';
import { timeMinute } from 'd3-time'; import { timeMinute } from 'd3-time';
import { capitalizeFirstCharacter } from '~/lib/utils/text_utility';
const d3 = { const d3 = {
scaleLinear, scaleLinear,
...@@ -28,15 +29,7 @@ const defaultColorOrder = ['blue', 'orange', 'red', 'green', 'purple']; ...@@ -28,15 +29,7 @@ const defaultColorOrder = ['blue', 'orange', 'red', 'green', 'purple'];
const defaultStyleOrder = ['solid', 'dashed', 'dotted']; const defaultStyleOrder = ['solid', 'dashed', 'dotted'];
function queryTimeSeries( function queryTimeSeries(query, graphWidth, graphHeight, graphHeightOffset, xDom, yDom, lineStyle) {
query,
graphWidth,
graphHeight,
graphHeightOffset,
xDom,
yDom,
lineStyle,
) {
let usedColors = []; let usedColors = [];
function pickColor(name) { function pickColor(name) {
...@@ -63,12 +56,11 @@ function queryTimeSeries( ...@@ -63,12 +56,11 @@ function queryTimeSeries(
const timeSeriesValues = timeSeries.values.map(d => d.value); const timeSeriesValues = timeSeries.values.map(d => d.value);
const maximumValue = d3.max(timeSeriesValues); const maximumValue = d3.max(timeSeriesValues);
const accum = d3.sum(timeSeriesValues); const accum = d3.sum(timeSeriesValues);
const track = capitalizeFirstCharacter(query.track ? query.track : 'Stable');
const timeSeriesScaleX = d3.scaleTime().range([0, graphWidth - 70]); const timeSeriesScaleX = d3.scaleTime().range([0, graphWidth - 70]);
const timeSeriesScaleY = d3 const timeSeriesScaleY = d3.scaleLinear().range([graphHeight - graphHeightOffset, 0]);
.scaleLinear()
.range([graphHeight - graphHeightOffset, 0]);
timeSeriesScaleX.domain(xDom); timeSeriesScaleX.domain(xDom);
timeSeriesScaleX.ticks(d3.timeMinute, 60); timeSeriesScaleX.ticks(d3.timeMinute, 60);
...@@ -91,27 +83,18 @@ function queryTimeSeries( ...@@ -91,27 +83,18 @@ function queryTimeSeries(
.y0(graphHeight - graphHeightOffset) .y0(graphHeight - graphHeightOffset)
.y1(d => timeSeriesScaleY(d.value)); .y1(d => timeSeriesScaleY(d.value));
const timeSeriesMetricLabel = const timeSeriesMetricLabel = timeSeries.metric[Object.keys(timeSeries.metric)[0]];
timeSeries.metric[Object.keys(timeSeries.metric)[0]];
const seriesCustomizationData = const seriesCustomizationData =
query.series != null && query.series != null && _.findWhere(query.series[0].when, { value: timeSeriesMetricLabel });
_.findWhere(query.series[0].when, { value: timeSeriesMetricLabel });
if (seriesCustomizationData) { if (seriesCustomizationData) {
metricTag = seriesCustomizationData.value || timeSeriesMetricLabel; metricTag = seriesCustomizationData.value || timeSeriesMetricLabel;
[lineColor, areaColor] = pickColor(seriesCustomizationData.color); [lineColor, areaColor] = pickColor(seriesCustomizationData.color);
} else { } else {
metricTag = metricTag = timeSeriesMetricLabel || query.label || `series ${timeSeriesNumber + 1}`;
timeSeriesMetricLabel ||
query.label ||
`series ${timeSeriesNumber + 1}`;
[lineColor, areaColor] = pickColor(); [lineColor, areaColor] = pickColor();
} }
if (query.track) {
metricTag += ` - ${query.track}`;
}
return { return {
linePath: lineFunction(timeSeries.values), linePath: lineFunction(timeSeries.values),
areaPath: areaFunction(timeSeries.values), areaPath: areaFunction(timeSeries.values),
...@@ -123,23 +106,16 @@ function queryTimeSeries( ...@@ -123,23 +106,16 @@ function queryTimeSeries(
lineColor, lineColor,
areaColor, areaColor,
metricTag, metricTag,
track,
}; };
}); });
} }
export default function createTimeSeries( export default function createTimeSeries(queries, graphWidth, graphHeight, graphHeightOffset) {
queries,
graphWidth,
graphHeight,
graphHeightOffset,
) {
const allValues = queries.reduce( const allValues = queries.reduce(
(allQueryResults, query) => (allQueryResults, query) =>
allQueryResults.concat( allQueryResults.concat(
query.result.reduce( query.result.reduce((allResults, result) => allResults.concat(result.values), []),
(allResults, result) => allResults.concat(result.values),
[],
),
), ),
[], [],
); );
...@@ -150,15 +126,7 @@ export default function createTimeSeries( ...@@ -150,15 +126,7 @@ export default function createTimeSeries(
return queries.reduce((series, query, index) => { return queries.reduce((series, query, index) => {
const lineStyle = defaultStyleOrder[index % defaultStyleOrder.length]; const lineStyle = defaultStyleOrder[index % defaultStyleOrder.length];
return series.concat( return series.concat(
queryTimeSeries( queryTimeSeries(query, graphWidth, graphHeight, graphHeightOffset, xDom, yDom, lineStyle),
query,
graphWidth,
graphHeight,
graphHeightOffset,
xDom,
yDom,
lineStyle,
),
); );
}, []); }, []);
} }
...@@ -65,11 +65,15 @@ describe('text_utility', () => { ...@@ -65,11 +65,15 @@ describe('text_utility', () => {
describe('stripHtml', () => { describe('stripHtml', () => {
it('replaces html tag with the default replacement', () => { it('replaces html tag with the default replacement', () => {
expect(textUtils.stripHtml('This is a text with <p>html</p>.')).toEqual('This is a text with html.'); expect(textUtils.stripHtml('This is a text with <p>html</p>.')).toEqual(
'This is a text with html.',
);
}); });
it('replaces html tags with the provided replacement', () => { it('replaces html tags with the provided replacement', () => {
expect(textUtils.stripHtml('This is a text with <p>html</p>.', ' ')).toEqual('This is a text with html .'); expect(textUtils.stripHtml('This is a text with <p>html</p>.', ' ')).toEqual(
'This is a text with html .',
);
}); });
}); });
...@@ -78,4 +82,10 @@ describe('text_utility', () => { ...@@ -78,4 +82,10 @@ describe('text_utility', () => {
expect(textUtils.convertToCamelCase('snake_case')).toBe('snakeCase'); expect(textUtils.convertToCamelCase('snake_case')).toBe('snakeCase');
}); });
}); });
describe('convertToSentenceCase', () => {
it('converts Sentence Case to Sentence case', () => {
expect(textUtils.convertToSentenceCase('Hello World')).toBe('Hello world');
});
});
}); });
...@@ -17,6 +17,7 @@ const defaultValuesComponent = { ...@@ -17,6 +17,7 @@ const defaultValuesComponent = {
margin: measurements.large.margin, margin: measurements.large.margin,
measurements: measurements.large, measurements: measurements.large,
yAxisLabel: 'Values', yAxisLabel: 'Values',
unitOfDisplay: 'MB',
}; };
function getTextFromNode(component, selector) { function getTextFromNode(component, selector) {
...@@ -28,9 +29,7 @@ describe('Axis', () => { ...@@ -28,9 +29,7 @@ describe('Axis', () => {
it('textTransform', () => { it('textTransform', () => {
const component = createComponent(defaultValuesComponent); const component = createComponent(defaultValuesComponent);
expect(component.textTransform).toContain( expect(component.textTransform).toContain('translate(15, 120) rotate(-90)');
'translate(15, 120) rotate(-90)',
);
}); });
it('xPosition', () => { it('xPosition', () => {
...@@ -48,9 +47,7 @@ describe('Axis', () => { ...@@ -48,9 +47,7 @@ describe('Axis', () => {
it('rectTransform', () => { it('rectTransform', () => {
const component = createComponent(defaultValuesComponent); const component = createComponent(defaultValuesComponent);
expect(component.rectTransform).toContain( expect(component.rectTransform).toContain('translate(0, 120) rotate(-90)');
'translate(0, 120) rotate(-90)',
);
}); });
}); });
...@@ -63,8 +60,6 @@ describe('Axis', () => { ...@@ -63,8 +60,6 @@ describe('Axis', () => {
it('contains text to signal the usage, title and time with multiple time series', () => { it('contains text to signal the usage, title and time with multiple time series', () => {
const component = createComponent(defaultValuesComponent); const component = createComponent(defaultValuesComponent);
expect(getTextFromNode(component, '.y-label-text')).toEqual( expect(getTextFromNode(component, '.y-label-text')).toEqual('Values (MB)');
component.yAxisLabel,
);
}); });
}); });
...@@ -30,14 +30,6 @@ describe('Legend Component', () => { ...@@ -30,14 +30,6 @@ describe('Legend Component', () => {
}); });
}); });
it('formatMetricUsage should contain the unit of display and the current value selected via "currentDataIndex"', () => {
const formattedMetricUsage = vm.formatMetricUsage(timeSeries[0]);
const valueFromSeries = timeSeries[0].values[vm.currentDataIndex].value;
expect(formattedMetricUsage.indexOf(vm.unitOfDisplay)).not.toEqual(-1);
expect(formattedMetricUsage.indexOf(valueFromSeries)).not.toEqual(-1);
});
it('strokeDashArray', () => { it('strokeDashArray', () => {
const dashedArray = vm.strokeDashArray('dashed'); const dashedArray = vm.strokeDashArray('dashed');
const dottedArray = vm.strokeDashArray('dotted'); const dottedArray = vm.strokeDashArray('dotted');
......
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