Commit fd8e65ad authored by Martin Wortschack's avatar Martin Wortschack

Add shared metric card component

- Adds a reusable container
for displaying metrics in the
analytics space
parent 6f09f635
<script>
import { GlCard, GlSkeletonLoading } from '@gitlab/ui';
export default {
name: 'MetricCard',
components: {
GlCard,
GlSkeletonLoading,
},
props: {
title: {
type: String,
required: true,
},
metrics: {
type: Array,
required: true,
},
isLoading: {
type: Boolean,
required: false,
default: false,
},
},
};
</script>
<template>
<gl-card>
<template #header>
<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">
<div
v-for="metric in metrics"
:key="metric.key"
ref="metricItem"
class="flex-grow text-center"
>
<h3 class="my-2">
<template v-if="metric.value === null"
>-</template
>
<template v-else>{{ metric.value }}</template>
</h3>
<p class="text-secondary gl-font-size-small mb-2">{{ metric.label }}</p>
</div>
</div>
</template>
</gl-card>
</template>
import { mount } from '@vue/test-utils';
import { GlSkeletonLoading } from '@gitlab/ui';
import MetricCard from 'ee/analytics/shared/components/metric_card.vue';
const defaultProps = {
title: 'My fancy title',
metrics: [
{ key: 'first_metric', value: 10, label: 'First metric' },
{ key: 'second_metric', value: 20, label: 'Yet another metric' },
{ key: 'third_metric', value: null, label: 'Metric without value' },
],
isLoading: false,
};
describe('MetricCard', () => {
let wrapper;
const factory = (props = defaultProps) => {
wrapper = mount(MetricCard, {
propsData: {
...defaultProps,
...props,
},
});
};
afterEach(() => {
wrapper.destroy();
});
const findTitle = () => wrapper.find({ ref: 'title' });
const findLoadingIndicator = () => wrapper.find(GlSkeletonLoading);
const findMetricsWrapper = () => wrapper.find({ ref: 'metricsWrapper' });
const findMetricItem = () => wrapper.findAll({ ref: 'metricItem' });
describe('template', () => {
it('renders the title', () => {
factory();
expect(findTitle().text()).toContain('My fancy title');
});
describe('when isLoading is true', () => {
beforeEach(() => {
factory({ isLoading: true });
});
it('displays a loading indicator', () => {
expect(findLoadingIndicator().exists()).toBe(true);
});
it('does not display the metrics container', () => {
expect(findMetricsWrapper().exists()).toBe(false);
});
});
describe('when isLoading is false', () => {
beforeEach(() => {
factory({ isLoading: false });
});
it('does not display a loading indicator', () => {
expect(findLoadingIndicator().exists()).toBe(false);
});
it('displays the metrics container', () => {
expect(findMetricsWrapper().exists()).toBe(true);
});
it('renders two metrics', () => {
expect(findMetricItem()).toHaveLength(3);
});
describe.each`
columnIndex | label | value
${0} | ${'First metric'} | ${10}
${1} | ${'Yet another metric'} | ${20}
${2} | ${'Metric without value'} | ${'-'}
`('metric columns', ({ columnIndex, label, value }) => {
it(`renders "${label}" as label`, () => {
expect(
findMetricItem()
.at(columnIndex)
.text(),
).toContain(label);
});
it(`renders ${value} as value`, () => {
expect(
findMetricItem()
.at(columnIndex)
.text(),
).toContain(value);
});
});
});
});
});
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