Commit 7bf305dc authored by Andrew Fontaine's avatar Andrew Fontaine

Merge branch...

Merge branch '229621-new-epic-button-in-epic-list-should-direct-the-user-to-a-full-epic-creation-page-2' into 'master'

New epic button in Epic Roadmap empty state should direct the user to a full epic creation page

See merge request gitlab-org/gitlab!45948
parents 3082c033 4bb08b91
......@@ -16,13 +16,15 @@ to them.
> - The New Epic form [introduced](https://gitlab.com/gitlab-org/gitlab/-/issues/211533) in [GitLab Premium](https://about.gitlab.com/pricing/) 13.2.
> - In [GitLab 13.7](https://gitlab.com/gitlab-org/gitlab/-/issues/229621) and later, the New Epic button on the Epics list opens the New Epic form.
> - In [GitLab 13.9](https://gitlab.com/gitlab-org/gitlab/-/merge_requests/45948) and later, you can create a new epic from an empty Roadmap.
To create an epic in the group you're in:
1. Get to the New Epic form:
- From the **Epics** list in your group, select the **New Epic** button.
- From an epic in your group, select the **New Epic** button.
- From the **Epics** list in your group, select **New epic**.
- From an epic in your group, select **New epic**.
- From anywhere, in the top menu, select **New...** (**{plus-square}**) **> New epic**.
- In an empty [roadmap](../roadmap/index.md), select **New epic**.
![New epic from an open epic](img/new_epic_from_groups_v13.7.png)
......@@ -39,7 +41,7 @@ To create an epic in the group you're in:
## Edit an epic
After you create an epic, you can edit change the following details:
After you create an epic, you can edit the following details:
- Title
- Description
......@@ -152,6 +154,9 @@ To make an epic confidential:
## Manage issues assigned to an epic
This section collects instructions for all the things you can do with [issues](../../project/issues/index.md)
in relation to epics.
### Add a new issue to an epic
You can add an existing issue to an epic, or create a new issue that's
......
<script>
import { mapState, mapActions } from 'vuex';
import {
GlForm,
GlFormInput,
GlFormCheckbox,
GlIcon,
GlButton,
GlTooltipDirective,
} from '@gitlab/ui';
import { __ } from '~/locale';
import autofocusonshow from '~/vue_shared/directives/autofocusonshow';
import glFeatureFlagsMixin from '~/vue_shared/mixins/gl_feature_flags_mixin';
export default {
components: {
GlFormCheckbox,
GlIcon,
GlButton,
GlForm,
GlFormInput,
},
directives: {
autofocusonshow,
GlTooltip: GlTooltipDirective,
},
mixins: [glFeatureFlagsMixin()],
props: {
alignRight: {
type: Boolean,
required: false,
default: false,
},
},
computed: {
...mapState(['newEpicTitle', 'newEpicConfidential', 'epicCreateInProgress']),
buttonLabel() {
return this.epicCreateInProgress ? __('Creating epic') : __('Create epic');
},
isEpicCreateDisabled() {
return !this.newEpicTitle.length;
},
epicTitle: {
set(value) {
this.setEpicCreateTitle({
newEpicTitle: value,
});
},
get() {
return this.newEpicTitle;
},
},
epicConfidential: {
set(value) {
this.setEpicCreateConfidential({
newEpicConfidential: value,
});
},
get() {
return this.newEpicConfidential;
},
},
},
methods: {
...mapActions(['setEpicCreateTitle', 'createEpic', 'setEpicCreateConfidential']),
},
};
</script>
<template>
<div class="dropdown epic-create-dropdown">
<gl-button
category="primary"
variant="success"
data-qa-selector="new_epic_button"
data-toggle="dropdown"
>
{{ __('New epic') }}
</gl-button>
<div :class="{ 'dropdown-menu-right': alignRight }" class="dropdown-menu">
<gl-form>
<gl-form-input
ref="epicTitleInput"
v-model="epicTitle"
v-autofocusonshow
:disabled="epicCreateInProgress"
:placeholder="__('Title')"
type="text"
class="form-control"
data-qa-selector="epic_title_field"
@keyup.enter.exact="createEpic"
/>
<gl-form-checkbox
v-model="epicConfidential"
class="mt-3 mb-3 mr-0"
data-qa-selector="confidential_epic_checkbox"
><span> {{ __('Make this epic confidential') }} </span>
<span
v-gl-tooltip.viewport.top.hover
:title="
__(
'This epic and its child elements will only be visible to team members with at minimum Reporter access.',
)
"
:aria-label="
__(
'This epic and its child elements will only be visible to team members with at minimum Reporter access.',
)
"
>
<gl-icon name="question" :size="12"
/></span>
</gl-form-checkbox>
<gl-button
:disabled="isEpicCreateDisabled"
:loading="epicCreateInProgress"
category="primary"
variant="success"
class="gl-mt-3"
data-qa-selector="create_epic_button"
@click.stop="createEpic"
>{{ buttonLabel }}</gl-button
>
</gl-form>
</div>
</div>
</template>
......@@ -9,10 +9,9 @@ import { parseIssuableData } from '~/issue_show/utils/parse_data';
import createStore from './store';
import EpicApp from './components/epic_app.vue';
import EpicCreateApp from './components/epic_create.vue';
export default (epicCreate = false) => {
const el = document.getElementById(epicCreate ? 'epic-create-root' : 'epic-app-root');
export default () => {
const el = document.getElementById('epic-app-root');
if (!el) {
return false;
......@@ -21,28 +20,6 @@ export default (epicCreate = false) => {
const store = createStore();
store.registerModule('labelsSelect', labelsSelectModule());
if (epicCreate) {
return new Vue({
el,
store,
components: { EpicCreateApp },
created() {
this.setEpicMeta({
endpoint: el.dataset.endpoint,
});
},
methods: {
...mapActions(['setEpicMeta']),
},
render: (createElement) =>
createElement('epic-create-app', {
props: {
alignRight: el.dataset.alignRight,
},
}),
});
}
const epicMeta = convertObjectPropsToCamelCase(JSON.parse(el.dataset.meta), { deep: true });
const epicData = parseIssuableData(el);
......
<script>
/* eslint-disable vue/no-v-html */
import { GlButton } from '@gitlab/ui';
import { GlButton, GlSafeHtmlDirective } from '@gitlab/ui';
import { s__, sprintf } from '~/locale';
import { dateInWords } from '~/lib/utils/datetime_utility';
import CommonMixin from '../mixins/common_mixin';
import { emptyStateDefault, emptyStateWithFilters } from '../constants';
import initEpicCreate from '../../epic/epic_bundle';
export default {
components: {
GlButton,
},
directives: {
SafeHtml: GlSafeHtmlDirective,
},
mixins: [CommonMixin],
inject: ['newEpicPath', 'listEpicsPath', 'epicsDocsPath'],
props: {
presetType: {
type: String,
......@@ -31,10 +32,6 @@ export default {
type: Boolean,
required: true,
},
newEpicEndpoint: {
type: String,
required: true,
},
emptyStateIllustrationPath: {
type: String,
required: true,
......@@ -96,8 +93,7 @@ export default {
'GroupRoadmap|To view the roadmap, add a start or due date to one of the %{linkStart}child epics%{linkEnd}.',
),
{
linkStart:
'<a href="https://docs.gitlab.com/ee/user/group/epics/#multi-level-child-epics" target="_blank" rel="noopener noreferrer nofollow">',
linkStart: `<a href="${this.epicsDocsPath}#multi-level-child-epics" target="_blank" rel="noopener noreferrer nofollow">`,
linkEnd: '</a>',
},
false,
......@@ -116,36 +112,38 @@ export default {
});
},
},
mounted() {
// If filters are not applied and yet user
// is seeing empty state, we need to show
// `New epic` button, so boot-up Epic app
// in create mode.
if (!this.hasFiltersApplied) {
initEpicCreate(true);
}
},
};
</script>
<template>
<div class="row empty-state">
<div class="col-12">
<div class="svg-content"><img :src="emptyStateIllustrationPath" /></div>
<div class="svg-content">
<img :src="emptyStateIllustrationPath" data-testid="illustration" />
</div>
</div>
<div class="col-12">
<div class="text-content">
<h4>{{ message }}</h4>
<p v-html="subMessage"></p>
<div class="text-center">
<div
<h4 data-testid="title">{{ message }}</h4>
<p v-safe-html="subMessage" data-testid="sub-title"></p>
<div class="gl-text-center">
<gl-button
v-if="!hasFiltersApplied"
id="epic-create-root"
:data-endpoint="newEpicEndpoint"
></div>
<gl-button :title="__('List')" :href="newEpicEndpoint">{{
s__('View epics list')
}}</gl-button>
:href="newEpicPath"
variant="success"
class="gl-mt-3 gl-sm-mt-0! gl-w-full gl-sm-w-auto!"
data-testid="new-epic-button"
>
{{ __('New epic') }}
</gl-button>
<gl-button
:href="listEpicsPath"
class="gl-mt-3 gl-sm-mt-0! gl-sm-ml-3 gl-w-full gl-sm-w-auto!"
data-testid="list-epics-button"
>
{{ __('View epics list') }}
</gl-button>
</div>
</div>
</div>
......
......@@ -37,10 +37,6 @@ export default {
type: String,
required: true,
},
newEpicEndpoint: {
type: String,
required: true,
},
emptyStateIllustrationPath: {
type: String,
required: true,
......@@ -179,7 +175,6 @@ export default {
:timeframe-start="timeframeStart"
:timeframe-end="timeframeEnd"
:has-filters-applied="hasFiltersApplied"
:new-epic-endpoint="newEpicEndpoint"
:empty-state-illustration-path="emptyStateIllustrationPath"
:is-child-epics="isChildEpics"
/>
......
......@@ -50,6 +50,15 @@ export default () => {
components: {
roadmapApp,
},
provide() {
const { dataset } = this.$options.el;
return {
newEpicPath: dataset.newEpicPath,
listEpicsPath: dataset.listEpicsPath,
epicsDocsPath: dataset.epicsDocsPath,
};
},
data() {
const supportedPresetTypes = Object.keys(PRESET_TYPES);
const { dataset } = this.$options.el;
......@@ -83,7 +92,6 @@ export default () => {
basePath: dataset.epicsPath,
fullPath: dataset.fullPath,
epicIid: dataset.iid,
newEpicEndpoint: dataset.newEpicEndpoint,
groupLabelsEndpoint: dataset.groupLabelsEndpoint,
groupMilestonesEndpoint: dataset.groupMilestonesEndpoint,
epicsState: dataset.epicsState,
......@@ -119,7 +127,6 @@ export default () => {
return createElement('roadmap-app', {
props: {
presetType: this.presetType,
newEpicEndpoint: this.newEpicEndpoint,
emptyStateIllustrationPath: this.emptyStateIllustrationPath,
},
});
......
......@@ -66,7 +66,9 @@
full_path: @group.full_path,
empty_state_illustration: image_path('illustrations/epics/roadmap.svg'),
has_filters_applied: 'false',
new_epic_endpoint: group_epics_path(@group),
new_epic_path: new_group_epic_path(@group),
list_epics_path: group_epics_path(@group),
epics_docs_path: help_page_path('user/group/epics/index'),
preset_type: roadmap_layout,
epics_state: 'all',
sorted_by: roadmap_sort_order,
......
......@@ -20,7 +20,9 @@
full_path: @group.full_path,
empty_state_illustration: image_path('illustrations/epics/roadmap.svg'),
has_filters_applied: "#{has_filters_applied}",
new_epic_endpoint: group_epics_path(@group),
new_epic_path: new_group_epic_path(@group),
list_epics_path: group_epics_path(@group),
epics_docs_path: help_page_path('user/group/epics/index'),
group_labels_endpoint: group_labels_path(@group, format: :json),
group_milestones_endpoint: group_milestones_path(@group, format: :json),
preset_type: roadmap_layout,
......
---
title: New epic button in Epic Roadmap empty state should direct the user to a full
epic creation page
merge_request: 45948
author:
type: changed
import Vue from 'vue';
import EpicCreate from 'ee/epic/components/epic_create.vue';
import createStore from 'ee/epic/store';
import { mountComponentWithStore } from 'helpers/vue_mount_component_helper';
import { mockEpicMeta } from '../mock_data';
describe('EpicCreateComponent', () => {
let vm;
let store;
beforeEach(() => {
const Component = Vue.extend(EpicCreate);
store = createStore();
store.dispatch('setEpicMeta', mockEpicMeta);
vm = mountComponentWithStore(Component, {
store,
});
});
afterEach(() => {
vm.$destroy();
});
describe('computed', () => {
describe('buttonLabel', () => {
it('returns string `Create epic` when `epicCreateInProgress` is false', () => {
vm.$store.state.epicCreateInProgress = false;
expect(vm.buttonLabel).toBe('Create epic');
});
it('returns string `Creating epic` when `epicCreateInProgress` is true', () => {
vm.$store.state.epicCreateInProgress = true;
expect(vm.buttonLabel).toBe('Creating epic');
});
});
describe('isEpicCreateDisabled', () => {
it('returns `true` when `newEpicTitle` is an empty string', () => {
vm.$store.state.newEpicTitle = '';
expect(vm.isEpicCreateDisabled).toBe(true);
});
it('returns `false` when `newEpicTitle` is not empty', () => {
vm.$store.state.newEpicTitle = 'foobar';
expect(vm.isEpicCreateDisabled).toBe(false);
});
});
describe('epicTitle', () => {
describe('set', () => {
it('calls `setEpicCreateTitle` with param `value`', () => {
jest.spyOn(vm, 'setEpicCreateTitle');
const newEpicTitle = 'foobar';
vm.epicTitle = newEpicTitle;
expect(vm.setEpicCreateTitle).toHaveBeenCalledWith(
expect.objectContaining({
newEpicTitle,
}),
);
});
});
describe('get', () => {
it('returns value of `newEpicTitle` from state', () => {
const newEpicTitle = 'foobar';
vm.$store.state.newEpicTitle = newEpicTitle;
expect(vm.epicTitle).toBe(newEpicTitle);
});
});
});
describe('epicConfidential', () => {
describe('set', () => {
it('calls `setEpicCreateConfidential` with param `value`', () => {
jest.spyOn(vm, 'setEpicCreateConfidential');
const newEpicConfidential = true;
vm.epicConfidential = newEpicConfidential;
expect(vm.setEpicCreateConfidential).toHaveBeenCalledWith(
expect.objectContaining({
newEpicConfidential,
}),
);
});
});
describe('get', () => {
it('returns value of `newEpicConfidential` from state', () => {
const newEpicConfidential = true;
vm.$store.state.newEpicConfidential = newEpicConfidential;
expect(vm.epicConfidential).toBe(newEpicConfidential);
});
});
});
});
describe('template', () => {
it('renders component container element with classes `dropdown` & `epic-create-dropdown`', () => {
expect(vm.$el.classList.contains('dropdown')).toBe(true);
expect(vm.$el.classList.contains('epic-create-dropdown')).toBe(true);
});
it('renders new epic button element', () => {
const newEpicButtonEl = vm.$el.querySelector('button.btn-success');
expect(newEpicButtonEl).not.toBeNull();
expect(newEpicButtonEl.innerText.trim()).toBe('New epic');
});
it('renders new epic dropdown menu element', () => {
const dropdownMenuEl = vm.$el.querySelector('.dropdown-menu');
expect(dropdownMenuEl).not.toBeNull();
});
it('renders epic input textbox element', () => {
const inputEl = vm.$el.querySelector('.dropdown-menu input.form-control');
expect(inputEl).not.toBeNull();
expect(inputEl.placeholder).toBe('Title');
});
it('renders create epic button element', () => {
const createEpicButtonEl = vm.$el.querySelector('.dropdown-menu button.btn-success');
expect(createEpicButtonEl).not.toBeNull();
expect(createEpicButtonEl.innerText.trim()).toBe('Create epic');
});
});
});
import Vue from 'vue';
import epicsListEmptyComponent from 'ee/roadmap/components/epics_list_empty.vue';
import { shallowMount } from '@vue/test-utils';
import { extendedWrapper } from 'helpers/vue_test_utils_helper';
import EpicsListEmpty from 'ee/roadmap/components/epics_list_empty.vue';
import { mockTimeframeInitialDate, mockSvgPath } from 'ee_jest/roadmap/mock_data';
import { PRESET_TYPES } from 'ee/roadmap/constants';
import { TEST_HOST } from 'helpers/test_constants';
import {
getTimeframeForQuartersView,
getTimeframeForWeeksView,
getTimeframeForMonthsView,
} from 'ee/roadmap/utils/roadmap_utils';
import {
mockTimeframeInitialDate,
mockSvgPath,
mockNewEpicEndpoint,
} from 'ee_jest/roadmap/mock_data';
import mountComponent from 'helpers/vue_mount_component_helper';
const TEST_EPICS_PATH = '/epics';
const TEST_NEW_EPIC_PATH = '/epics/new';
const mockTimeframeQuarters = getTimeframeForQuartersView(mockTimeframeInitialDate);
const mockTimeframeMonths = getTimeframeForMonthsView(mockTimeframeInitialDate);
const mockTimeframeWeeks = getTimeframeForWeeksView(mockTimeframeInitialDate);
const createComponent = ({
hasFiltersApplied = false,
presetType = PRESET_TYPES.MONTHS,
timeframeStart = mockTimeframeMonths[0],
timeframeEnd = mockTimeframeMonths[mockTimeframeMonths.length - 1],
}) => {
const Component = Vue.extend(epicsListEmptyComponent);
return mountComponent(Component, {
presetType,
timeframeStart,
timeframeEnd,
emptyStateIllustrationPath: mockSvgPath,
newEpicEndpoint: mockNewEpicEndpoint,
hasFiltersApplied,
describe('ee/roadmap/components/epics_list_empty.vue', () => {
let wrapper;
const createWrapper = ({
isChildEpics = false,
hasFiltersApplied = false,
presetType = PRESET_TYPES.MONTHS,
timeframeStart = mockTimeframeMonths[0],
timeframeEnd = mockTimeframeMonths[mockTimeframeMonths.length - 1],
} = {}) => {
wrapper = extendedWrapper(
shallowMount(EpicsListEmpty, {
propsData: {
presetType,
timeframeStart,
timeframeEnd,
emptyStateIllustrationPath: mockSvgPath,
hasFiltersApplied,
isChildEpics,
},
provide: {
newEpicPath: TEST_NEW_EPIC_PATH,
listEpicsPath: TEST_EPICS_PATH,
epicsDocsPath: TEST_HOST,
},
}),
);
};
afterEach(() => {
wrapper.destroy();
wrapper = null;
});
};
describe('EpicsListEmptyComponent', () => {
let vm;
const findTitle = () => wrapper.findByTestId('title');
const findSubTitle = () => wrapper.findByTestId('sub-title');
const findNewEpicButton = () => wrapper.findByTestId('new-epic-button');
const findListEpicsButton = () => wrapper.findByTestId('list-epics-button');
const findIllustration = () => wrapper.findByTestId('illustration');
beforeEach(() => {
vm = createComponent({});
it('renders default message', () => {
createWrapper({});
expect(findTitle().text()).toBe(wrapper.vm.message);
});
afterEach(() => {
vm.$destroy();
it('renders empty state message when `hasFiltersApplied` prop is true', () => {
createWrapper({ hasFiltersApplied: true });
expect(findTitle().text()).toBe('Sorry, no epics matched your search');
});
describe('computed', () => {
describe('message', () => {
it('returns default empty state message', () => {
expect(vm.message).toBe('The roadmap shows the progress of your epics along a timeline');
describe('with presetType `QUARTERS`', () => {
it('renders default empty state sub-title when `hasFiltersApplied` props is false', () => {
createWrapper({
presetType: PRESET_TYPES.QUARTERS,
timeframeStart: mockTimeframeQuarters[0],
timeframeEnd: mockTimeframeQuarters[mockTimeframeQuarters.length - 1],
});
it('returns empty state message when `hasFiltersApplied` prop is true', (done) => {
vm.hasFiltersApplied = true;
Vue.nextTick()
.then(() => {
expect(vm.message).toBe('Sorry, no epics matched your search');
})
.then(done)
.catch(done.fail);
});
expect(findSubTitle().text()).toBe(
'To view the roadmap, add a start or due date to one of your epics in this group or its subgroups; from Jul 1, 2017 to Mar 31, 2019.',
);
});
describe('subMessage', () => {
describe('with presetType `QUARTERS`', () => {
beforeEach(() => {
vm.presetType = PRESET_TYPES.QUARTERS;
[vm.timeframeStart] = mockTimeframeQuarters;
vm.timeframeEnd = mockTimeframeQuarters[mockTimeframeQuarters.length - 1];
});
it('returns default empty state sub-message when `hasFiltersApplied` props is false', (done) => {
Vue.nextTick()
.then(() => {
expect(vm.subMessage).toBe(
'To view the roadmap, add a start or due date to one of your epics in this group or its subgroups; from Jul 1, 2017 to Mar 31, 2019.',
);
})
.then(done)
.catch(done.fail);
});
it('returns empty state sub-message when `hasFiltersApplied` prop is true', (done) => {
vm.hasFiltersApplied = true;
Vue.nextTick()
.then(() => {
expect(vm.subMessage).toBe(
'To widen your search, change or remove filters; from Jul 1, 2017 to Mar 31, 2019.',
);
})
.then(done)
.catch(done.fail);
});
it('renders empty state sub-title when `hasFiltersApplied` prop is true', () => {
createWrapper({
presetType: PRESET_TYPES.QUARTERS,
timeframeStart: mockTimeframeQuarters[0],
timeframeEnd: mockTimeframeQuarters[mockTimeframeQuarters.length - 1],
hasFiltersApplied: true,
});
describe('with presetType `MONTHS`', () => {
beforeEach(() => {
vm.presetType = PRESET_TYPES.MONTHS;
});
it('returns default empty state sub-message when `hasFiltersApplied` props is false', (done) => {
Vue.nextTick()
.then(() => {
expect(vm.subMessage).toBe(
'To view the roadmap, add a start or due date to one of your epics in this group or its subgroups; from Nov 1, 2017 to Jun 30, 2018.',
);
})
.then(done)
.catch(done.fail);
});
it('returns empty state sub-message when `hasFiltersApplied` prop is true', (done) => {
vm.hasFiltersApplied = true;
Vue.nextTick()
.then(() => {
expect(vm.subMessage).toBe(
'To widen your search, change or remove filters; from Nov 1, 2017 to Jun 30, 2018.',
);
})
.then(done)
.catch(done.fail);
});
});
expect(findSubTitle().text()).toBe(
'To widen your search, change or remove filters; from Jul 1, 2017 to Mar 31, 2019.',
);
});
});
describe('with presetType `WEEKS`', () => {
beforeEach(() => {
const timeframeEnd = mockTimeframeWeeks[mockTimeframeWeeks.length - 1];
timeframeEnd.setDate(timeframeEnd.getDate() + 6);
vm.presetType = PRESET_TYPES.WEEKS;
[vm.timeframeStart] = mockTimeframeWeeks;
vm.timeframeEnd = timeframeEnd;
});
it('returns default empty state sub-message when `hasFiltersApplied` props is false', (done) => {
Vue.nextTick()
.then(() => {
expect(vm.subMessage).toBe(
'To view the roadmap, add a start or due date to one of your epics in this group or its subgroups; from Dec 17, 2017 to Feb 9, 2018.',
);
})
.then(done)
.catch(done.fail);
});
it('returns empty state sub-message when `hasFiltersApplied` prop is true', (done) => {
vm.hasFiltersApplied = true;
Vue.nextTick()
.then(() => {
expect(vm.subMessage).toBe(
'To widen your search, change or remove filters; from Dec 17, 2017 to Feb 15, 2018.',
);
})
.then(done)
.catch(done.fail);
});
describe('with presetType `MONTHS`', () => {
it('renders default empty state sub-title when `hasFiltersApplied` props is false', () => {
createWrapper({
presetType: PRESET_TYPES.MONTHS,
});
describe('with child epics context', () => {
it('returns empty state sub-message when `isChildEpics` is set to `true`', (done) => {
vm.isChildEpics = true;
Vue.nextTick()
.then(() => {
expect(vm.subMessage).toBe(
'To view the roadmap, add a start or due date to one of the <a href="https://docs.gitlab.com/ee/user/group/epics/#multi-level-child-epics" target="_blank" rel="noopener noreferrer nofollow">child epics</a>.',
);
})
.then(done)
.catch(done.fail);
});
});
expect(findSubTitle().text()).toBe(
'To view the roadmap, add a start or due date to one of your epics in this group or its subgroups; from Nov 1, 2017 to Jun 30, 2018.',
);
});
describe('timeframeRange', () => {
it('returns correct timeframe startDate and endDate in words', () => {
expect(vm.timeframeRange.startDate).toBe('Nov 1, 2017');
expect(vm.timeframeRange.endDate).toBe('Jun 30, 2018');
it('renders empty state sub-title when `hasFiltersApplied` prop is true', () => {
createWrapper({
presetType: PRESET_TYPES.MONTHS,
hasFiltersApplied: true,
});
});
});
describe('template', () => {
it('renders empty state illustration in image element with provided `emptyStateIllustrationPath`', () => {
expect(vm.$el.querySelector('.svg-content img').getAttribute('src')).toBe(
vm.emptyStateIllustrationPath,
expect(findSubTitle().text()).toBe(
'To widen your search, change or remove filters; from Nov 1, 2017 to Jun 30, 2018.',
);
});
});
it('renders mount point for new epic button to boot via Epic app', () => {
expect(vm.$el.querySelector('#epic-create-root')).not.toBeNull();
describe('with presetType `WEEKS`', () => {
let timeframeEnd;
beforeEach(() => {
timeframeEnd = mockTimeframeWeeks[mockTimeframeWeeks.length - 1];
timeframeEnd.setDate(timeframeEnd.getDate() + 6);
});
it('does not render new epic button element when `hasFiltersApplied` prop is true', (done) => {
vm.hasFiltersApplied = true;
Vue.nextTick()
.then(() => {
expect(vm.$el.querySelector('.epic-create-dropdown')).toBeNull();
})
.then(done)
.catch(done.fail);
it('renders default empty state sub-title when `hasFiltersApplied` props is false', () => {
createWrapper({
presetType: PRESET_TYPES.WEEKS,
timeframeStart: mockTimeframeWeeks[0],
timeframeEnd,
});
expect(findSubTitle().text()).toBe(
'To view the roadmap, add a start or due date to one of your epics in this group or its subgroups; from Dec 17, 2017 to Feb 9, 2018.',
);
});
it('renders view epics list link element', () => {
const viewEpicsListEl = vm.$el.querySelector('a.btn');
it('renders empty state sub-title when `hasFiltersApplied` prop is true', () => {
createWrapper({
presetType: PRESET_TYPES.WEEKS,
timeframeStart: mockTimeframeWeeks[0],
timeframeEnd,
hasFiltersApplied: true,
});
expect(viewEpicsListEl).not.toBeNull();
expect(viewEpicsListEl.getAttribute('href')).toBe(mockNewEpicEndpoint);
expect(viewEpicsListEl.querySelector('span').innerText.trim()).toBe('View epics list');
expect(findSubTitle().text()).toBe(
'To widen your search, change or remove filters; from Dec 17, 2017 to Feb 15, 2018.',
);
});
});
it('renders empty state sub-title when `isChildEpics` is set to `true`', () => {
createWrapper({ isChildEpics: true });
expect(findSubTitle().text()).toBe(
'To view the roadmap, add a start or due date to one of the child epics.',
);
});
it('renders empty state illustration in image element with provided `emptyStateIllustrationPath`', () => {
createWrapper({});
expect(findIllustration().attributes('src')).toBe(mockSvgPath);
});
it('renders buttons for create and list epics', () => {
createWrapper({});
expect(findNewEpicButton().attributes('href')).toBe(TEST_NEW_EPIC_PATH);
expect(findListEpicsButton().attributes('href')).toBe(TEST_EPICS_PATH);
});
it('does not render new epic button element when `hasFiltersApplied` prop is true', () => {
createWrapper({ hasFiltersApplied: true });
expect(findNewEpicButton().exists()).toBe(false);
});
});
......@@ -17,7 +17,6 @@ import {
mockFormattedEpic,
mockFormattedChildEpic2,
mockGroupId,
mockNewEpicEndpoint,
mockSortedBy,
mockSvgPath,
mockTimeframeInitialDate,
......@@ -35,7 +34,6 @@ describe('RoadmapApp', () => {
const emptyStateIllustrationPath = mockSvgPath;
const epics = [mockFormattedEpic];
const hasFiltersApplied = true;
const newEpicEndpoint = mockNewEpicEndpoint;
const presetType = PRESET_TYPES.MONTHS;
const timeframe = getTimeframeForMonthsView(mockTimeframeInitialDate);
......@@ -44,7 +42,6 @@ describe('RoadmapApp', () => {
localVue,
propsData: {
emptyStateIllustrationPath,
newEpicEndpoint,
presetType,
},
provide: {
......@@ -122,10 +119,6 @@ describe('RoadmapApp', () => {
expect(wrapper.find(EpicsListEmpty).props('isChildEpics')).toBe(false);
});
it('contains endpoint to create a new epic', () => {
expect(wrapper.find(EpicsListEmpty).props('newEpicEndpoint')).toBe(mockNewEpicEndpoint);
});
it('contains the preset type', () => {
expect(wrapper.find(EpicsListEmpty).props('presetType')).toBe(presetType);
});
......
......@@ -17223,9 +17223,6 @@ msgstr ""
msgid "Make sure you save it - you won't be able to access it again."
msgstr ""
msgid "Make this epic confidential"
msgstr ""
msgid "Makes this issue confidential."
msgstr ""
......@@ -29039,9 +29036,6 @@ msgstr ""
msgid "This epic already has the maximum number of child epics."
msgstr ""
msgid "This epic and its child elements will only be visible to team members with at minimum Reporter access."
msgstr ""
msgid "This epic does not exist or you don't have sufficient permission."
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