Commit a6f5aa3a authored by Axel García's avatar Axel García

Hide child epics icon on Roadmap if not available

This checks the group features for sub epics support, if available the
allowSubEpics flag is propagated with the store and used in the epic
details component to hide/show the child epics icon.

This also adds the store to parent components specs that use
@vue/test-utils/mount.
parent 15c7001e
<script> <script>
import { mapState } from 'vuex';
import { GlButton, GlIcon, GlLoadingIcon, GlTooltip } from '@gitlab/ui'; import { GlButton, GlIcon, GlLoadingIcon, GlTooltip } from '@gitlab/ui';
import { __, n__ } from '~/locale'; import { __, n__ } from '~/locale';
import eventHub from '../event_hub'; import eventHub from '../event_hub';
...@@ -43,6 +44,7 @@ export default { ...@@ -43,6 +44,7 @@ export default {
}, },
}, },
computed: { computed: {
...mapState(['allowSubEpics']),
itemId() { itemId() {
return this.epic.id; return this.epic.id;
}, },
...@@ -154,6 +156,7 @@ export default { ...@@ -154,6 +156,7 @@ export default {
<p class="epic-timeframe" :title="timeframeString">{{ timeframeString }}</p> <p class="epic-timeframe" :title="timeframeString">{{ timeframeString }}</p>
</div> </div>
</div> </div>
<template v-if="allowSubEpics">
<div ref="childEpicsCount" class="d-flex text-secondary text-nowrap"> <div ref="childEpicsCount" class="d-flex text-secondary text-nowrap">
<gl-icon name="epic" class="align-text-bottom mr-1" aria-hidden="true" /> <gl-icon name="epic" class="align-text-bottom mr-1" aria-hidden="true" />
<p class="m-0" :aria-label="childEpicsCountText">{{ childEpicsCount }}</p> <p class="m-0" :aria-label="childEpicsCountText">{{ childEpicsCount }}</p>
...@@ -162,6 +165,7 @@ export default { ...@@ -162,6 +165,7 @@ export default {
<span :class="{ bold: hasFiltersApplied }">{{ childEpicsCountText }}</span> <span :class="{ bold: hasFiltersApplied }">{{ childEpicsCountText }}</span>
<span v-if="hasFiltersApplied" class="d-block">{{ childEpicsSearchText }}</span> <span v-if="hasFiltersApplied" class="d-block">{{ childEpicsSearchText }}</span>
</gl-tooltip> </gl-tooltip>
</template>
</div> </div>
</div> </div>
</template> </template>
...@@ -78,6 +78,7 @@ export default () => { ...@@ -78,6 +78,7 @@ export default () => {
return { return {
emptyStateIllustrationPath: dataset.emptyStateIllustration, emptyStateIllustrationPath: dataset.emptyStateIllustration,
hasFiltersApplied: parseBoolean(dataset.hasFiltersApplied), hasFiltersApplied: parseBoolean(dataset.hasFiltersApplied),
allowSubEpics: parseBoolean(dataset.allowSubEpics),
defaultInnerHeight: Number(dataset.innerHeight), defaultInnerHeight: Number(dataset.innerHeight),
isChildEpics: parseBoolean(dataset.childEpics), isChildEpics: parseBoolean(dataset.childEpics),
currentGroupId: parseInt(dataset.groupId, 0), currentGroupId: parseInt(dataset.groupId, 0),
...@@ -109,6 +110,7 @@ export default () => { ...@@ -109,6 +110,7 @@ export default () => {
initialEpicsPath: this.initialEpicsPath, initialEpicsPath: this.initialEpicsPath,
defaultInnerHeight: this.defaultInnerHeight, defaultInnerHeight: this.defaultInnerHeight,
isChildEpics: this.isChildEpics, isChildEpics: this.isChildEpics,
allowSubEpics: this.allowSubEpics,
}); });
}, },
methods: { methods: {
......
...@@ -33,4 +33,5 @@ export default () => ({ ...@@ -33,4 +33,5 @@ export default () => ({
milestonesFetchInProgress: false, milestonesFetchInProgress: false,
milestonesFetchFailure: false, milestonesFetchFailure: false,
milestonesFetchResultEmpty: false, milestonesFetchResultEmpty: false,
allowSubEpics: false,
}); });
...@@ -6,6 +6,8 @@ ...@@ -6,6 +6,8 @@
- @content_class = "group-epics-roadmap" - @content_class = "group-epics-roadmap"
- breadcrumb_title _("Epics Roadmap") - breadcrumb_title _("Epics Roadmap")
- epics_limit_feature = 'epics_limit_warning_dismissed' - epics_limit_feature = 'epics_limit_warning_dismissed'
- sub_epics_feature_available = @group.feature_available?(:subepics)
- allow_sub_epics = sub_epics_feature_available ? 'true' : 'false'
- has_filters_applied = params[:label_name].present? || params[:author_username].present? || params[:search].present? - has_filters_applied = params[:label_name].present? || params[:author_username].present? || params[:search].present?
...@@ -21,7 +23,7 @@ ...@@ -21,7 +23,7 @@
%a.btn.btn-outline-warning#js-learn-more{ "href" => "https://docs.gitlab.com/ee/user/group/roadmap/" } %a.btn.btn-outline-warning#js-learn-more{ "href" => "https://docs.gitlab.com/ee/user/group/roadmap/" }
= _("Learn more") = _("Learn more")
#js-roadmap{ data: { epics_path: group_epics_path(@group, format: :json), group_id: @group.id, 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), preset_type: roadmap_layout, epics_state: @epics_state, sorted_by: @sort } } #js-roadmap{ data: { epics_path: group_epics_path(@group, format: :json), group_id: @group.id, 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), preset_type: roadmap_layout, epics_state: @epics_state, sorted_by: @sort, allow_sub_epics: allow_sub_epics } }
- else - else
= render 'shared/empty_states/roadmap' = render 'shared/empty_states/roadmap'
---
title: Hide child epic icon on Roadmap for accounts without child epics support
merge_request: 31250
author:
type: fixed
import { mount } from '@vue/test-utils'; import { mount } from '@vue/test-utils';
import createStore from 'ee/roadmap/store';
import EpicItem from 'ee/roadmap/components/epic_item.vue'; import EpicItem from 'ee/roadmap/components/epic_item.vue';
import EpicItemContainer from 'ee/roadmap/components/epic_item_container.vue'; import EpicItemContainer from 'ee/roadmap/components/epic_item_container.vue';
...@@ -13,6 +14,8 @@ import { ...@@ -13,6 +14,8 @@ import {
mockFormattedChildEpic1, mockFormattedChildEpic1,
} from 'ee_jest/roadmap/mock_data'; } from 'ee_jest/roadmap/mock_data';
let store;
const mockTimeframeMonths = getTimeframeForMonthsView(mockTimeframeInitialDate); const mockTimeframeMonths = getTimeframeForMonthsView(mockTimeframeInitialDate);
const createComponent = ({ const createComponent = ({
...@@ -26,6 +29,7 @@ const createComponent = ({ ...@@ -26,6 +29,7 @@ const createComponent = ({
hasFiltersApplied = false, hasFiltersApplied = false,
} = {}) => { } = {}) => {
return mount(EpicItemContainer, { return mount(EpicItemContainer, {
store,
stubs: { stubs: {
'epic-item': EpicItem, 'epic-item': EpicItem,
}, },
...@@ -46,6 +50,7 @@ describe('EpicItemContainer', () => { ...@@ -46,6 +50,7 @@ describe('EpicItemContainer', () => {
let wrapper; let wrapper;
beforeEach(() => { beforeEach(() => {
store = createStore();
wrapper = createComponent(); wrapper = createComponent();
}); });
......
import { GlButton, GlIcon, GlTooltip } from '@gitlab/ui'; import { GlButton, GlIcon, GlTooltip } from '@gitlab/ui';
import { shallowMount } from '@vue/test-utils'; import { shallowMount } from '@vue/test-utils';
import createStore from 'ee/roadmap/store';
import EpicItemDetails from 'ee/roadmap/components/epic_item_details.vue'; import EpicItemDetails from 'ee/roadmap/components/epic_item_details.vue';
import eventHub from 'ee/roadmap/event_hub'; import eventHub from 'ee/roadmap/event_hub';
import { import {
...@@ -9,6 +10,8 @@ import { ...@@ -9,6 +10,8 @@ import {
mockFormattedChildEpic1, mockFormattedChildEpic1,
} from 'ee_jest/roadmap/mock_data'; } from 'ee_jest/roadmap/mock_data';
let store;
const createComponent = ({ const createComponent = ({
epic = mockFormattedEpic, epic = mockFormattedEpic,
currentGroupId = mockGroupId, currentGroupId = mockGroupId,
...@@ -19,6 +22,7 @@ const createComponent = ({ ...@@ -19,6 +22,7 @@ const createComponent = ({
isChildrenEmpty = false, isChildrenEmpty = false,
} = {}) => { } = {}) => {
return shallowMount(EpicItemDetails, { return shallowMount(EpicItemDetails, {
store,
propsData: { propsData: {
epic, epic,
currentGroupId, currentGroupId,
...@@ -43,6 +47,7 @@ describe('EpicItemDetails', () => { ...@@ -43,6 +47,7 @@ describe('EpicItemDetails', () => {
let wrapper; let wrapper;
beforeEach(() => { beforeEach(() => {
store = createStore();
wrapper = createComponent(); wrapper = createComponent();
}); });
...@@ -131,6 +136,10 @@ describe('EpicItemDetails', () => { ...@@ -131,6 +136,10 @@ describe('EpicItemDetails', () => {
}); });
describe('epic', () => { describe('epic', () => {
beforeEach(() => {
store.state.allowSubEpics = true;
});
describe('expand icon', () => { describe('expand icon', () => {
it('is hidden when epic has no child epics', () => { it('is hidden when epic has no child epics', () => {
const epic = { const epic = {
...@@ -325,6 +334,12 @@ describe('EpicItemDetails', () => { ...@@ -325,6 +334,12 @@ describe('EpicItemDetails', () => {
'1 child epic Some child epics may be hidden due to applied filters', '1 child epic Some child epics may be hidden due to applied filters',
); );
}); });
it('does not render if the user license does not support child epics', () => {
store.state.allowSubEpics = false;
wrapper = createComponent();
expect(getChildEpicsCount(wrapper).exists()).toBe(false);
});
}); });
}); });
}); });
...@@ -2,6 +2,7 @@ import { mount } from '@vue/test-utils'; ...@@ -2,6 +2,7 @@ import { mount } from '@vue/test-utils';
import { delay } from 'lodash'; import { delay } from 'lodash';
import createStore from 'ee/roadmap/store';
import EpicItemComponent from 'ee/roadmap/components/epic_item.vue'; import EpicItemComponent from 'ee/roadmap/components/epic_item.vue';
import EpicItemContainer from 'ee/roadmap/components/epic_item_container.vue'; import EpicItemContainer from 'ee/roadmap/components/epic_item_container.vue';
...@@ -24,6 +25,8 @@ jest.mock('lodash/delay', () => ...@@ -24,6 +25,8 @@ jest.mock('lodash/delay', () =>
}), }),
); );
let store;
const mockTimeframeMonths = getTimeframeForMonthsView(mockTimeframeInitialDate); const mockTimeframeMonths = getTimeframeForMonthsView(mockTimeframeInitialDate);
const createComponent = ({ const createComponent = ({
...@@ -37,6 +40,7 @@ const createComponent = ({ ...@@ -37,6 +40,7 @@ const createComponent = ({
hasFiltersApplied = false, hasFiltersApplied = false,
}) => { }) => {
return mount(EpicItemComponent, { return mount(EpicItemComponent, {
store,
stubs: { stubs: {
'epic-item-container': EpicItemContainer, 'epic-item-container': EpicItemContainer,
'epic-item': EpicItemComponent, 'epic-item': EpicItemComponent,
...@@ -58,6 +62,7 @@ describe('EpicItemComponent', () => { ...@@ -58,6 +62,7 @@ describe('EpicItemComponent', () => {
let wrapper; let wrapper;
beforeEach(() => { beforeEach(() => {
store = createStore();
wrapper = createComponent({}); wrapper = createComponent({});
}); });
......
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