Commit f9043c65 authored by Ezekiel Kigbo's avatar Ezekiel Kigbo Committed by Nicolò Maria Mezzopera

Add tooltip helper text to cycle metrics

Adds a hoverable tooltip icon to the time
metrics for VSA, adding an explanation of
how each metric is calculated.

Minor refactor of the metric_card

Minor cleanup some utility css
parent 09b48cbc
<script>
import { GlTooltipDirective } from '@gitlab/ui';
import Icon from '~/vue_shared/components/icon.vue';
import { GlTooltipDirective, GlIcon } from '@gitlab/ui';
export default {
name: 'StageTableHeader',
components: {
Icon,
GlIcon,
},
directives: {
GlTooltip: GlTooltipDirective,
......@@ -31,10 +30,10 @@ export default {
<template>
<li :class="headerClasses">
<span class="stage-name align-middle font-weight-bold">{{ title }}</span>
<icon
<span class="stage-name gl-font-weight-bold">{{ title }}</span>
<gl-icon
v-gl-tooltip
class="align-middle"
class="gl-vertical-align-middle"
:size="14"
name="question"
container=".stage-name"
......
<script>
import Api from 'ee/api';
import { __ } from '~/locale';
import { __, s__ } from '~/locale';
import createFlash from '~/flash';
import { slugify } from '~/lib/utils/text_utility';
import MetricCard from '../../shared/components/metric_card.vue';
import { removeFlash } from '../utils';
const I18N_TEXT = {
'lead-time': s__('ValueStreamAnalytics|Median time from issue created to issue closed.'),
'cycle-time': s__('ValueStreamAnalytics|Median time from first commit to issue closed.'),
};
export default {
name: 'TimeMetricsCard',
components: {
......@@ -42,11 +47,15 @@ export default {
this.loading = true;
return Api.cycleAnalyticsTimeSummaryData(this.groupPath, this.additionalParams)
.then(({ data }) => {
this.data = data.map(({ title: label, ...rest }) => ({
...rest,
label,
key: slugify(label),
}));
this.data = data.map(({ title: label, ...rest }) => {
const key = slugify(label);
return {
...rest,
label,
key,
tooltipText: I18N_TEXT[key] || '',
};
});
})
.catch(() => {
createFlash(
......
<script>
import { GlCard, GlSkeletonLoading, GlLink } from '@gitlab/ui';
import { GlCard, GlSkeletonLoading, GlLink, GlIcon, GlTooltipDirective } from '@gitlab/ui';
export default {
name: 'MetricCard',
......@@ -7,6 +7,10 @@ export default {
GlCard,
GlSkeletonLoading,
GlLink,
GlIcon,
},
directives: {
GlTooltip: GlTooltipDirective,
},
props: {
title: {
......@@ -38,19 +42,29 @@ export default {
<strong ref="title">{{ title }}</strong>
</template>
<template #default>
<gl-skeleton-loading v-if="isLoading" class="h-auto py-3" />
<div v-else ref="metricsWrapper" class="d-flex">
<gl-skeleton-loading v-if="isLoading" class="gl-h-auto gl-py-3" />
<div v-else ref="metricsWrapper" class="gl-display-flex">
<div
v-for="metric in metrics"
v-for="{ tooltipText = '', ...metric } in metrics"
:key="metric.key"
ref="metricItem"
class="js-metric-card-item flex-grow text-center"
class="js-metric-card-item gl-flex-grow-1 gl-text-center"
>
<gl-link v-if="metric.link" :href="metric.link">
<h3 class="gl-my-2 gl-text-blue-700">{{ valueText(metric) }}</h3>
</gl-link>
<h3 v-else class="gl-my-2">{{ valueText(metric) }}</h3>
<p class="text-secondary gl-font-sm mb-2">{{ metric.label }}</p>
<p class="text-secondary gl-font-sm gl-mb-2">
{{ metric.label }}
<span v-if="tooltipText.length"
>&nbsp;<gl-icon
v-gl-tooltip="{ title: tooltipText }"
:size="14"
class="gl-vertical-align-middle"
name="question"
data-testid="tooltip"
/></span>
</p>
</div>
</div>
</template>
......
---
title: Add info icons to explain lead / cycle time calculations
merge_request: 37903
author:
type: added
......@@ -19,10 +19,10 @@ exports[`RecentActivityCard matches the snapshot 1`] = `
<!---->
<div
class="d-flex"
class="gl-display-flex"
>
<div
class="js-metric-card-item flex-grow text-center"
class="js-metric-card-item gl-flex-grow-1 gl-text-center"
>
<h3
class="gl-my-2"
......@@ -31,13 +31,16 @@ exports[`RecentActivityCard matches the snapshot 1`] = `
</h3>
<p
class="text-secondary gl-font-sm mb-2"
class="text-secondary gl-font-sm gl-mb-2"
>
New Issues
<!---->
</p>
</div>
<div
class="js-metric-card-item flex-grow text-center"
class="js-metric-card-item gl-flex-grow-1 gl-text-center"
>
<h3
class="gl-my-2"
......@@ -46,13 +49,16 @@ exports[`RecentActivityCard matches the snapshot 1`] = `
</h3>
<p
class="text-secondary gl-font-sm mb-2"
class="text-secondary gl-font-sm gl-mb-2"
>
Deploys
<!---->
</p>
</div>
<div
class="js-metric-card-item flex-grow text-center"
class="js-metric-card-item gl-flex-grow-1 gl-text-center"
>
<h3
class="gl-my-2"
......@@ -61,9 +67,12 @@ exports[`RecentActivityCard matches the snapshot 1`] = `
</h3>
<p
class="text-secondary gl-font-sm mb-2"
class="text-secondary gl-font-sm gl-mb-2"
>
Deployment Frequency
<!---->
</p>
</div>
</div>
......
......@@ -19,10 +19,10 @@ exports[`GroupActivity component matches the snapshot 1`] = `
<!---->
<div
class="d-flex"
class="gl-display-flex"
>
<div
class="js-metric-card-item flex-grow text-center"
class="js-metric-card-item gl-flex-grow-1 gl-text-center"
>
<h3
class="gl-my-2"
......@@ -31,13 +31,16 @@ exports[`GroupActivity component matches the snapshot 1`] = `
</h3>
<p
class="text-secondary gl-font-sm mb-2"
class="text-secondary gl-font-sm gl-mb-2"
>
Merge Requests opened
<!---->
</p>
</div>
<div
class="js-metric-card-item flex-grow text-center"
class="js-metric-card-item gl-flex-grow-1 gl-text-center"
>
<h3
class="gl-my-2"
......@@ -46,13 +49,16 @@ exports[`GroupActivity component matches the snapshot 1`] = `
</h3>
<p
class="text-secondary gl-font-sm mb-2"
class="text-secondary gl-font-sm gl-mb-2"
>
Issues opened
<!---->
</p>
</div>
<div
class="js-metric-card-item flex-grow text-center"
class="js-metric-card-item gl-flex-grow-1 gl-text-center"
>
<h3
class="gl-my-2"
......@@ -61,9 +67,12 @@ exports[`GroupActivity component matches the snapshot 1`] = `
</h3>
<p
class="text-secondary gl-font-sm mb-2"
class="text-secondary gl-font-sm gl-mb-2"
>
Members added
<!---->
</p>
</div>
</div>
......
import { mount } from '@vue/test-utils';
import { GlSkeletonLoading } from '@gitlab/ui';
import { createMockDirective, getBinding } from 'helpers/vue_mock_directive';
import MetricCard from 'ee/analytics/shared/components/metric_card.vue';
const metrics = [
......@@ -24,6 +25,9 @@ describe('MetricCard', () => {
...defaultProps,
...props,
},
directives: {
GlTooltip: createMockDirective(),
},
});
};
......@@ -35,6 +39,7 @@ describe('MetricCard', () => {
const findLoadingIndicator = () => wrapper.find(GlSkeletonLoading);
const findMetricsWrapper = () => wrapper.find({ ref: 'metricsWrapper' });
const findMetricItem = () => wrapper.findAll({ ref: 'metricItem' });
const findTooltip = () => wrapper.find('[data-testid="tooltip"]');
describe('template', () => {
it('renders the title', () => {
......@@ -74,6 +79,29 @@ describe('MetricCard', () => {
expect(findMetricItem()).toHaveLength(metrics.length);
});
describe('with tooltip text', () => {
const tooltipText = 'This is a tooltip';
const tooltipMetric = {
key: 'fifth_metric',
value: '-',
label: 'Metric with tooltip',
unit: 'parsecs',
tooltipText,
};
beforeEach(() => {
factory({
isLoading: false,
metrics: [tooltipMetric],
});
});
it('will render a tooltip', () => {
const tt = getBinding(findTooltip().element, 'gl-tooltip');
expect(tt.value.title).toEqual(tooltipText);
});
});
describe.each`
columnIndex | label | value | unit | link
${0} | ${'First metric'} | ${10} | ${' days'} | ${'some_link'}
......@@ -84,8 +112,10 @@ describe('MetricCard', () => {
it(`renders ${value}${unit} ${label} with URL ${link}`, () => {
const allMetricItems = findMetricItem();
const metricItem = allMetricItems.at(columnIndex);
const text = metricItem.text();
expect(metricItem.text()).toBe(`${value}${unit} ${label}`);
expect(text).toContain(`${value}${unit}`);
expect(text).toContain(label);
if (link) {
expect(metricItem.find('a').attributes('href')).toBe(link);
......
......@@ -26373,6 +26373,12 @@ msgstr ""
msgid "ValueStreamAnalytics|%{days}d"
msgstr ""
msgid "ValueStreamAnalytics|Median time from first commit to issue closed."
msgstr ""
msgid "ValueStreamAnalytics|Median time from issue created to issue closed."
msgstr ""
msgid "Variable"
msgstr ""
......
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