Commit 40e29c4b authored by Martin Wortschack's avatar Martin Wortschack

Add empty state screen

- Display an empty state screen for code review analytics
parent f6e49d16
---
title: Empty state for Code Review Analytics
merge_request: 25793
author:
type: added
......@@ -5,7 +5,7 @@ import FilteredSearchCodeReviewAnalytics from './filtered_search_code_review_ana
export default () => {
const container = document.getElementById('js-code-review-analytics');
const { projectId } = container.dataset;
const { projectId, newMergeRequestUrl, emptyStateSvgPath } = container.dataset;
if (!container) return;
......@@ -21,6 +21,8 @@ export default () => {
return h(CodeAnalyticsApp, {
props: {
projectId: Number(projectId),
newMergeRequestUrl,
emptyStateSvgPath,
},
});
},
......
<script>
import { mapState, mapGetters, mapActions } from 'vuex';
import { GlBadge, GlLoadingIcon, GlPagination } from '@gitlab/ui';
import { mapState, mapActions } from 'vuex';
import { GlBadge, GlLoadingIcon, GlEmptyState, GlPagination } from '@gitlab/ui';
import MergeRequestTable from './merge_request_table.vue';
export default {
......@@ -8,6 +8,7 @@ export default {
GlBadge,
GlLoadingIcon,
GlPagination,
GlEmptyState,
MergeRequestTable,
},
props: {
......@@ -15,6 +16,14 @@ export default {
type: Number,
required: true,
},
newMergeRequestUrl: {
type: String,
required: true,
},
emptyStateSvgPath: {
type: String,
required: true,
},
},
computed: {
...mapState({
......@@ -23,7 +32,6 @@ export default {
totalItems: state => state.pageInfo.total,
page: state => state.pageInfo.page,
}),
...mapGetters(['showMrCount']),
currentPage: {
get() {
return this.page;
......@@ -46,12 +54,33 @@ export default {
<template>
<div class="mt-2">
<gl-loading-icon v-show="isLoading" size="md" class="mt-3" />
<template v-if="!isLoading">
<gl-empty-state
v-if="!totalItems"
:title="__(`You don't have any open merge requests`)"
:primary-button-text="__('New merge request')"
:primary-button-link="newMergeRequestUrl"
:svg-path="emptyStateSvgPath"
class="container-message"
>
<template #description>
<div class="text-center">
<p>
{{
__(
'Code Review Analytics displays a table of open merge requests considered to be in code review. There are currently no merge requests in review for this project and/or filters.',
)
}}
</p>
</div>
</template>
</gl-empty-state>
<template v-else>
<div>
<span class="font-weight-bold">{{ __('Merge Requests in Review') }}</span>
<gl-badge v-show="showMrCount" pill>{{ totalItems }}</gl-badge>
<gl-badge pill>{{ totalItems }}</gl-badge>
</div>
<gl-loading-icon v-show="isLoading" size="md" class="mt-3" />
<template v-if="!isLoading">
<merge-request-table />
<gl-pagination
v-model="currentPage"
......@@ -61,5 +90,6 @@ export default {
class="w-100"
/>
</template>
</template>
</div>
</template>
// eslint-disable-next-line import/prefer-default-export
export const showMrCount = state => !state.isLoading && !state.errorCode;
import Vue from 'vue';
import Vuex from 'vuex';
import * as actions from './actions';
import * as getters from './getters';
import mutations from './mutations';
import state from './state';
......@@ -11,7 +10,6 @@ const createStore = () =>
new Vuex.Store({
state: state(),
actions,
getters,
mutations,
});
......
......@@ -6,4 +6,4 @@
= _('Code Review')
%span.text-secondary= _('Review time is defined as the time it takes from first comment until merged.')
= render 'shared/issuable/search_bar', type: :issues_analytics, show_sorting_dropdown: false
#js-code-review-analytics{ data: { project_id: @project.id } }
#js-code-review-analytics{ data: { project_id: @project.id, new_merge_request_url: namespace_project_new_merge_request_path(@project.namespace), empty_state_svg_path: image_path('illustrations/merge_requests.svg') } }
import { createLocalVue, shallowMount } from '@vue/test-utils';
import Vuex from 'vuex';
import { GlLoadingIcon, GlBadge, GlPagination } from '@gitlab/ui';
import { GlLoadingIcon, GlEmptyState, GlBadge, GlPagination } from '@gitlab/ui';
import CodeReviewAnalyticsApp from 'ee/analytics/code_review_analytics/components/app.vue';
import MergeRequestTable from 'ee/analytics/code_review_analytics/components/merge_request_table.vue';
import createState from 'ee/analytics/code_review_analytics/store/state';
......@@ -44,6 +44,8 @@ describe('CodeReviewAnalyticsApp component', () => {
store,
propsData: {
projectId: 1,
newMergeRequestUrl: 'new_merge_request',
emptyStateSvgPath: 'svg',
},
});
......@@ -56,6 +58,7 @@ describe('CodeReviewAnalyticsApp component', () => {
wrapper.destroy();
});
const findEmptyState = () => wrapper.find(GlEmptyState);
const findLoadingIcon = () => wrapper.find(GlLoadingIcon);
const findBadge = () => wrapper.find(GlBadge);
const findMrTable = () => wrapper.find(MergeRequestTable);
......@@ -73,7 +76,7 @@ describe('CodeReviewAnalyticsApp component', () => {
});
it('should not show the badge containing the MR count', () => {
expect(findBadge().isVisible()).toBe(false);
expect(findBadge().exists()).toBe(false);
});
it('should not render the merge requests table', () => {
......@@ -86,6 +89,37 @@ describe('CodeReviewAnalyticsApp component', () => {
});
describe('when finished loading', () => {
describe('and there are no merge requests', () => {
beforeEach(() => {
vuexStore = createStore(
{ isLoading: false, pageInfo: { page: 0, perPage: 0, total: 0 } },
{ showMrCount: () => true },
);
wrapper = createComponent(vuexStore);
});
it('should hide the loading indicator', () => {
expect(findLoadingIcon().isVisible()).toBe(false);
});
it('should show the empty state screen', () => {
expect(findEmptyState().exists()).toBe(true);
});
it('should not show the badge containing the MR count', () => {
expect(findBadge().exists()).toBe(false);
});
it('should not render the merge requests table', () => {
expect(findMrTable().exists()).toBe(false);
});
it('should not render the pagination', () => {
expect(findPagination().exists()).toBe(false);
});
});
describe('and there are merge requests', () => {
beforeEach(() => {
vuexStore = createStore({ isLoading: false, pageInfo }, { showMrCount: () => true });
wrapper = createComponent(vuexStore);
......@@ -96,8 +130,12 @@ describe('CodeReviewAnalyticsApp component', () => {
});
it('should show the badge containing the MR count', () => {
expect(findBadge().isVisible()).toBe(true);
expect(findBadge().text()).toEqual(`${50}`);
expect(findBadge().exists()).toBe(true);
expect(findBadge().text()).toBe('50');
});
it('should not render the empty state screen', () => {
expect(findEmptyState().exists()).toBe(false);
});
it('should render the merge requests table', () => {
......@@ -109,6 +147,7 @@ describe('CodeReviewAnalyticsApp component', () => {
});
});
});
});
describe('changing the page', () => {
beforeEach(() => {
......
import createState from 'ee/analytics/code_review_analytics/store/state';
import * as getters from 'ee/analytics/code_review_analytics/store/getters';
describe('Code review analytics getteers', () => {
let state;
beforeEach(() => {
state = createState();
});
describe('showMrCount', () => {
it('returns false when is loading', () => {
state = { isLoading: true, errorCode: null };
expect(getters.showMrCount(state)).toBe(false);
});
it('returns true when not loading and no error', () => {
state = { isLoading: false, errorCode: null };
expect(getters.showMrCount(state)).toBe(true);
});
});
});
......@@ -4820,6 +4820,9 @@ msgstr ""
msgid "Code Review"
msgstr ""
msgid "Code Review Analytics displays a table of open merge requests considered to be in code review. There are currently no merge requests in review for this project and/or filters."
msgstr ""
msgid "Code owner approval is required"
msgstr ""
......@@ -22474,6 +22477,9 @@ msgstr ""
msgid "You don't have any deployments right now."
msgstr ""
msgid "You don't have any open merge requests"
msgstr ""
msgid "You don't have any projects available."
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