Commit 46abc4e8 authored by Ezekiel Kigbo's avatar Ezekiel Kigbo Committed by Simon Knox

Add empty state with nudge to create a value stream

Adds a new empty state component to VSA, this includes
a nudge to create a custom value stream
parent 011f0a42
<script>
import { GlButton, GlEmptyState, GlLoadingIcon, GlModalDirective } from '@gitlab/ui';
import { helpPagePath } from '~/helpers/help_page_helper';
import Tracking from '~/tracking';
import {
EMPTY_STATE_TITLE,
EMPTY_STATE_DESCRIPTION,
EMPTY_STATE_ACTION_TEXT,
EMPTY_STATE_SECONDARY_TEXT,
EMPTY_STATE_FILTER_ERROR_TITLE,
EMPTY_STATE_FILTER_ERROR_DESCRIPTION,
} from '../constants';
import ValueStreamForm from './value_stream_form.vue';
export default {
name: 'ValueStreamEmptyState',
components: {
GlButton,
GlEmptyState,
GlLoadingIcon,
ValueStreamForm,
},
directives: {
GlModalDirective,
},
mixins: [Tracking.mixin()],
props: {
isLoading: {
type: Boolean,
required: true,
default: false,
},
hasDateRangeError: {
type: Boolean,
required: true,
default: false,
},
emptyStateSvgPath: {
type: String,
required: true,
},
},
computed: {
title() {
return this.hasDateRangeError
? this.$options.i18n.EMPTY_STATE_FILTER_ERROR_TITLE
: this.$options.i18n.EMPTY_STATE_TITLE;
},
description() {
return this.hasDateRangeError
? this.$options.i18n.EMPTY_STATE_FILTER_ERROR_DESCRIPTION
: this.$options.i18n.EMPTY_STATE_DESCRIPTION;
},
},
i18n: {
EMPTY_STATE_TITLE,
EMPTY_STATE_DESCRIPTION,
EMPTY_STATE_ACTION_TEXT,
EMPTY_STATE_SECONDARY_TEXT,
EMPTY_STATE_FILTER_ERROR_TITLE,
EMPTY_STATE_FILTER_ERROR_DESCRIPTION,
},
docsPath: helpPagePath('user/group/value_stream_analytics', {
anchor: 'custom-value-streams',
}),
};
</script>
<template>
<div>
<div v-if="isLoading" class="gl-p-7 gl-text-center">
<gl-loading-icon size="lg" />
</div>
<gl-empty-state
v-else
class="gl-w-half"
:svg-path="emptyStateSvgPath"
:title="title"
:description="description"
>
<template v-if="!hasDateRangeError" #actions>
<gl-button
v-gl-modal-directive="'value-stream-form-modal'"
variant="confirm"
data-testid="create-value-stream-button"
data-track-action="click_button"
data-track-label="empty_state_create_value_stream_form_open"
>{{ $options.i18n.EMPTY_STATE_ACTION_TEXT }}</gl-button
>
<gl-button data-testid="learn-more-link" :href="$options.docsPath"
>{{ $options.i18n.EMPTY_STATE_SECONDARY_TEXT }}
</gl-button>
</template>
</gl-empty-state>
<value-stream-form />
</div>
</template>
......@@ -56,3 +56,17 @@ export const DURATION_STAGE_TIME_NO_DATA = s__(
export const DURATION_STAGE_TIME_DESCRIPTION = s__(
'CycleAnalytics|The average time items spent in this stage. Data limited to items completed within this date range.',
);
export const EMPTY_STATE_TITLE = s__(
'CycleAnalytics|Custom value streams to measure your DevSecOps lifecycle',
);
export const EMPTY_STATE_DESCRIPTION = s__(
'CycleAnalytics|Create a custom value stream to view metrics about stages specific to your development process. Use your value stream to visualize your DevSecOps lifecycle, determine the velocity of your group, and identify inefficient processes.',
);
export const EMPTY_STATE_ACTION_TEXT = s__('CycleAnalytics|Create a custom value stream…');
export const EMPTY_STATE_SECONDARY_TEXT = __('Learn more');
export const EMPTY_STATE_FILTER_ERROR_TITLE = __(
'Value Stream Analytics can help you determine your team’s velocity',
);
export const EMPTY_STATE_FILTER_ERROR_DESCRIPTION = __(
'Filter parameters are not valid. Make sure that the end date is after the start date.',
);
import { shallowMount } from '@vue/test-utils';
import { GlLoadingIcon, GlEmptyState } from '@gitlab/ui';
import ValueStreamEmptyState from 'ee/analytics/cycle_analytics/components/value_stream_empty_state.vue';
import {
EMPTY_STATE_ACTION_TEXT,
EMPTY_STATE_SECONDARY_TEXT,
EMPTY_STATE_FILTER_ERROR_TITLE,
EMPTY_STATE_TITLE,
EMPTY_STATE_FILTER_ERROR_DESCRIPTION,
EMPTY_STATE_DESCRIPTION,
} from 'ee/analytics/cycle_analytics/constants';
import { extendedWrapper } from 'helpers/vue_test_utils_helper';
const emptyStateSvgPath = '/path/to/svg';
const createComponent = (props = {}) =>
extendedWrapper(
shallowMount(ValueStreamEmptyState, {
propsData: {
emptyStateSvgPath,
isLoading: false,
hasDateRangeError: false,
...props,
},
stubs: { GlEmptyState },
}),
);
describe('ValueStreamEmptyState', () => {
let wrapper = null;
const findEmptyState = () => wrapper.findComponent(GlEmptyState);
const findTitle = () => findEmptyState().props('title');
const findDescription = () => findEmptyState().props('description');
const findPrimaryAction = () => wrapper.findByTestId('create-value-stream-button');
const findSecondaryAction = () => wrapper.findByTestId('learn-more-link');
afterEach(() => {
wrapper.destroy();
});
describe('default state', () => {
beforeEach(() => {
wrapper = createComponent();
});
it('does not render the loading icon', () => {
expect(wrapper.findComponent(GlLoadingIcon).exists()).toBe(false);
});
it('renders the empty state title message', () => {
expect(findTitle()).toEqual(EMPTY_STATE_TITLE);
});
it('renders the empty state description message', () => {
expect(findDescription()).toBe(EMPTY_STATE_DESCRIPTION);
});
it('renders the create value stream button', () => {
expect(findPrimaryAction().exists()).toBe(true);
expect(findPrimaryAction().text()).toContain(EMPTY_STATE_ACTION_TEXT);
});
it('renders the learn more button', () => {
expect(findSecondaryAction().exists()).toBe(true);
expect(findSecondaryAction().text()).toBe(EMPTY_STATE_SECONDARY_TEXT);
expect(findSecondaryAction().attributes('href')).toBe(
'/help/user/group/value_stream_analytics#custom-value-streams',
);
});
});
describe('isLoading = true', () => {
beforeEach(() => {
wrapper = createComponent({ isLoading: true });
});
it('renders the loading icon', () => {
expect(wrapper.findComponent(GlLoadingIcon).exists()).toBe(true);
});
});
describe('hasDateRangeError = true', () => {
beforeEach(() => {
wrapper = createComponent({ hasDateRangeError: true });
});
it('renders the error title message', () => {
expect(findTitle()).toEqual(EMPTY_STATE_FILTER_ERROR_TITLE);
});
it('renders the error description message', () => {
expect(findDescription()).toBe(EMPTY_STATE_FILTER_ERROR_DESCRIPTION);
});
it('does not render the create value stream button', () => {
expect(findPrimaryAction().exists()).toBe(false);
});
it('does not render the learn more button', () => {
expect(findSecondaryAction().exists()).toBe(false);
});
});
});
......@@ -10917,6 +10917,15 @@ msgstr ""
msgid "CycleAnalytics|Average time to completion"
msgstr ""
msgid "CycleAnalytics|Create a custom value stream to view metrics about stages specific to your development process. Use your value stream to visualize your DevSecOps lifecycle, determine the velocity of your group, and identify inefficient processes."
msgstr ""
msgid "CycleAnalytics|Create a custom value stream…"
msgstr ""
msgid "CycleAnalytics|Custom value streams to measure your DevSecOps lifecycle"
msgstr ""
msgid "CycleAnalytics|Date"
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