Commit 32643d14 authored by Florie Guibert's avatar Florie Guibert

Roadmap setting - Move daterange to sidebar

Behind roadmap_settings feature flag
parent f1ce3db2
...@@ -115,6 +115,7 @@ export default { ...@@ -115,6 +115,7 @@ export default {
<roadmap-settings <roadmap-settings
v-if="glFeatures.roadmapSettings" v-if="glFeatures.roadmapSettings"
:is-open="isSettingsSidebarOpen" :is-open="isSettingsSidebarOpen"
:timeframe-range-type="timeframeRangeType"
data-testid="roadmap-settings" data-testid="roadmap-settings"
@toggleSettings="toggleSettings" @toggleSettings="toggleSettings"
/> />
......
<script>
import { GlFormGroup, GlFormRadioGroup, GlDropdown, GlDropdownItem } from '@gitlab/ui';
import { mapState } from 'vuex';
import { visitUrl, mergeUrlParams } from '~/lib/utils/url_utility';
import { __, s__ } from '~/locale';
import { PRESET_TYPES, DATE_RANGES } from '../constants';
import { getPresetTypeForTimeframeRangeType } from '../utils/roadmap_utils';
export default {
availableDateRanges: [
{ text: s__('GroupRoadmap|This quarter'), value: DATE_RANGES.CURRENT_QUARTER },
{ text: s__('GroupRoadmap|This year'), value: DATE_RANGES.CURRENT_YEAR },
{ text: s__('GroupRoadmap|Within 3 years'), value: DATE_RANGES.THREE_YEARS },
],
components: {
GlFormGroup,
GlFormRadioGroup,
GlDropdown,
GlDropdownItem,
},
props: {
timeframeRangeType: {
type: String,
required: true,
},
},
data() {
return {
selectedDaterange: this.timeframeRangeType,
};
},
computed: {
...mapState(['presetType']),
daterangeDropdownText() {
switch (this.selectedDaterange) {
case DATE_RANGES.CURRENT_QUARTER:
return s__('GroupRoadmap|This quarter');
case DATE_RANGES.CURRENT_YEAR:
return s__('GroupRoadmap|This year');
case DATE_RANGES.THREE_YEARS:
return s__('GroupRoadmap|Within 3 years');
default:
return '';
}
},
availablePresets() {
const quarters = { text: __('By quarter'), value: PRESET_TYPES.QUARTERS };
const months = { text: __('By month'), value: PRESET_TYPES.MONTHS };
const weeks = { text: __('By week'), value: PRESET_TYPES.WEEKS };
if (this.selectedDaterange === DATE_RANGES.CURRENT_YEAR) {
return [months, weeks];
} else if (this.selectedDaterange === DATE_RANGES.THREE_YEARS) {
return [quarters, months, weeks];
}
return [];
},
},
methods: {
handleDaterangeSelect(value) {
this.selectedDaterange = value;
},
handleDaterangeDropdownOpen() {
this.initialSelectedDaterange = this.selectedDaterange;
},
handleDaterangeDropdownClose() {
if (this.initialSelectedDaterange !== this.selectedDaterange) {
visitUrl(
mergeUrlParams(
{
timeframe_range_type: this.selectedDaterange,
layout: getPresetTypeForTimeframeRangeType(this.selectedDaterange),
},
window.location.href,
),
);
}
},
handleRoadmapLayoutChange(presetType) {
visitUrl(
mergeUrlParams(
{ timeframe_range_type: this.selectedDaterange, layout: presetType },
window.location.href,
),
);
},
},
i18n: {
header: __('Date range'),
},
};
</script>
<template>
<div>
<label for="roadmap-daterange" class="gl-display-block">{{ $options.i18n.header }}</label>
<gl-dropdown
id="roadmap-daterange"
icon="calendar"
class="gl-mb-3 roadmap-daterange-dropdown"
toggle-class="gl-rounded-base!"
:text="daterangeDropdownText"
data-testid="daterange-dropdown"
@show="handleDaterangeDropdownOpen"
@hide="handleDaterangeDropdownClose"
>
<gl-dropdown-item
v-for="dateRange in $options.availableDateRanges"
:key="dateRange.value"
:value="dateRange.value"
@click="handleDaterangeSelect(dateRange.value)"
>
{{ dateRange.text }}
</gl-dropdown-item>
</gl-dropdown>
<gl-form-group v-if="availablePresets.length">
<gl-form-radio-group
:checked="presetType"
stacked
:options="availablePresets"
@change="handleRoadmapLayoutChange"
/>
</gl-form-group>
</div>
</template>
...@@ -176,6 +176,7 @@ export default { ...@@ -176,6 +176,7 @@ export default {
class="epics-details-filters filtered-search-block gl-display-flex gl-flex-direction-column gl-xl-flex-direction-row gl-pb-3 row-content-block second-block" class="epics-details-filters filtered-search-block gl-display-flex gl-flex-direction-column gl-xl-flex-direction-row gl-pb-3 row-content-block second-block"
> >
<gl-dropdown <gl-dropdown
v-if="!glFeatures.roadmapSettings"
icon="calendar" icon="calendar"
class="gl-mr-0 gl-lg-mr-3 mb-sm-2 roadmap-daterange-dropdown" class="gl-mr-0 gl-lg-mr-3 mb-sm-2 roadmap-daterange-dropdown"
toggle-class="gl-rounded-base!" toggle-class="gl-rounded-base!"
...@@ -192,7 +193,10 @@ export default { ...@@ -192,7 +193,10 @@ export default {
>{{ dateRange.text }}</gl-dropdown-item >{{ dateRange.text }}</gl-dropdown-item
> >
</gl-dropdown> </gl-dropdown>
<gl-form-group v-if="availablePresets.length" class="gl-mr-0 gl-lg-mr-3 mb-sm-2"> <gl-form-group
v-if="availablePresets.length && !glFeatures.roadmapSettings"
class="gl-mr-0 gl-lg-mr-3 mb-sm-2"
>
<gl-segmented-control <gl-segmented-control
:checked="presetType" :checked="presetType"
:options="availablePresets" :options="availablePresets"
......
<script> <script>
import { GlDrawer } from '@gitlab/ui'; import { GlDrawer } from '@gitlab/ui';
import RoadmapDaterange from './roadmap_daterange.vue';
export default { export default {
components: { components: {
GlDrawer, GlDrawer,
RoadmapDaterange,
}, },
props: { props: {
isOpen: { isOpen: {
type: Boolean, type: Boolean,
required: true, required: true,
}, },
timeframeRangeType: {
type: String,
required: true,
},
}, },
methods: { methods: {
getDrawerHeaderHeight() { getDrawerHeaderHeight() {
...@@ -36,5 +42,8 @@ export default { ...@@ -36,5 +42,8 @@ export default {
<template #title> <template #title>
<h2 class="gl-my-0 gl-font-size-h2 gl-line-height-24">{{ __('Roadmap settings') }}</h2> <h2 class="gl-my-0 gl-font-size-h2 gl-line-height-24">{{ __('Roadmap settings') }}</h2>
</template> </template>
<template #default>
<roadmap-daterange :timeframe-range-type="timeframeRangeType" />
</template>
</gl-drawer> </gl-drawer>
</template> </template>
...@@ -46,6 +46,57 @@ RSpec.describe 'group epic roadmap', :js do ...@@ -46,6 +46,57 @@ RSpec.describe 'group epic roadmap', :js do
sign_in(user) sign_in(user)
end end
context 'with roadmap_settings feature flag off' do
let!(:epic_with_bug) { create(:labeled_epic, group: group, start_date: 10.days.ago, end_date: 1.day.ago, labels: [bug_label]) }
let!(:epic_with_critical) { create(:labeled_epic, group: group, start_date: 20.days.ago, end_date: 2.days.ago, labels: [critical_label]) }
let!(:closed_epic) { create(:epic, :closed, group: group, start_date: 20.days.ago, end_date: 2.days.ago) }
before do
stub_feature_flags(roadmap_settings: false)
visit group_roadmap_path(group)
wait_for_requests
end
context 'roadmap daterange filtering' do
def select_date_range(range_type)
page.within('.epics-roadmap-filters') do
page.find('[data-testid="daterange-dropdown"] button.dropdown-toggle').click
click_button(range_type)
end
end
it 'renders daterange filtering dropdown with "This quarter" selected by default no layout presets available', :aggregate_failures do
page.within('.epics-roadmap-filters') do
expect(page).to have_selector('[data-testid="daterange-dropdown"]')
expect(page).not_to have_selector('.gl-segmented-control')
expect(page.find('[data-testid="daterange-dropdown"] button.dropdown-toggle')).to have_content('This quarter')
end
end
it 'selecting "This year" as daterange shows `Months` and `Weeks` layout presets', :aggregate_failures do
select_date_range('This year')
page.within('.epics-roadmap-filters') do
expect(page).to have_selector('.gl-segmented-control')
expect(page).to have_selector('input[value="MONTHS"]')
expect(page).to have_selector('input[value="WEEKS"]')
end
end
it 'selecting "Within 3 years" as daterange shows `Quarters`, `Months` and `Weeks` layout presets', :aggregate_failures do
select_date_range('Within 3 years')
page.within('.epics-roadmap-filters') do
expect(page).to have_selector('.gl-segmented-control')
expect(page).to have_selector('input[value="QUARTERS"]')
expect(page).to have_selector('input[value="MONTHS"]')
expect(page).to have_selector('input[value="WEEKS"]')
end
end
end
end
context 'when epics exist for the group' do context 'when epics exist for the group' do
available_tokens = %w[Author Label Milestone Epic My-Reaction] available_tokens = %w[Author Label Milestone Epic My-Reaction]
...@@ -60,17 +111,28 @@ RSpec.describe 'group epic roadmap', :js do ...@@ -60,17 +111,28 @@ RSpec.describe 'group epic roadmap', :js do
describe 'roadmap page' do describe 'roadmap page' do
context 'roadmap daterange filtering' do context 'roadmap daterange filtering' do
def open_settings_sidebar
click_button 'Settings'
expect(page).to have_selector('[data-testid="roadmap-settings"]')
end
def select_date_range(range_type) def select_date_range(range_type)
page.within('.epics-roadmap-filters') do open_settings_sidebar
page.within('[data-testid="roadmap-settings"]') do
page.find('[data-testid="daterange-dropdown"] button.dropdown-toggle').click page.find('[data-testid="daterange-dropdown"] button.dropdown-toggle').click
click_button(range_type) click_button(range_type)
end end
open_settings_sidebar
end end
it 'renders daterange filtering dropdown with "This quarter" selected by default no layout presets available', :aggregate_failures do it 'renders daterange filtering dropdown with "This quarter" selected by default no layout presets available', :aggregate_failures do
page.within('.epics-roadmap-filters') do open_settings_sidebar
page.within('[data-testid="roadmap-settings"]') do
expect(page).to have_selector('[data-testid="daterange-dropdown"]') expect(page).to have_selector('[data-testid="daterange-dropdown"]')
expect(page).not_to have_selector('.gl-segmented-control') expect(page).not_to have_selector('.gl-form-checkbox-group')
expect(page.find('[data-testid="daterange-dropdown"] button.dropdown-toggle')).to have_content('This quarter') expect(page.find('[data-testid="daterange-dropdown"] button.dropdown-toggle')).to have_content('This quarter')
end end
end end
...@@ -78,8 +140,8 @@ RSpec.describe 'group epic roadmap', :js do ...@@ -78,8 +140,8 @@ RSpec.describe 'group epic roadmap', :js do
it 'selecting "This year" as daterange shows `Months` and `Weeks` layout presets', :aggregate_failures do it 'selecting "This year" as daterange shows `Months` and `Weeks` layout presets', :aggregate_failures do
select_date_range('This year') select_date_range('This year')
page.within('.epics-roadmap-filters') do page.within('[data-testid="roadmap-settings"]') do
expect(page).to have_selector('.gl-segmented-control') expect(page).to have_selector('.gl-form-checkbox-group')
expect(page).to have_selector('input[value="MONTHS"]') expect(page).to have_selector('input[value="MONTHS"]')
expect(page).to have_selector('input[value="WEEKS"]') expect(page).to have_selector('input[value="WEEKS"]')
end end
...@@ -88,8 +150,8 @@ RSpec.describe 'group epic roadmap', :js do ...@@ -88,8 +150,8 @@ RSpec.describe 'group epic roadmap', :js do
it 'selecting "Within 3 years" as daterange shows `Quarters`, `Months` and `Weeks` layout presets', :aggregate_failures do it 'selecting "Within 3 years" as daterange shows `Quarters`, `Months` and `Weeks` layout presets', :aggregate_failures do
select_date_range('Within 3 years') select_date_range('Within 3 years')
page.within('.epics-roadmap-filters') do page.within('[data-testid="roadmap-settings"]') do
expect(page).to have_selector('.gl-segmented-control') expect(page).to have_selector('.gl-form-checkbox-group')
expect(page).to have_selector('input[value="QUARTERS"]') expect(page).to have_selector('input[value="QUARTERS"]')
expect(page).to have_selector('input[value="MONTHS"]') expect(page).to have_selector('input[value="MONTHS"]')
expect(page).to have_selector('input[value="WEEKS"]') expect(page).to have_selector('input[value="WEEKS"]')
......
import { GlDropdown, GlFormGroup, GlFormRadioGroup } from '@gitlab/ui';
import { nextTick } from 'vue';
import { shallowMountExtended } from 'helpers/vue_test_utils_helper';
import createStore from 'ee/roadmap/store';
import RoadmapDaterange from 'ee/roadmap/components/roadmap_daterange.vue';
import { DATE_RANGES, PRESET_TYPES } from 'ee/roadmap/constants';
describe('RoadmapDaterange', () => {
let wrapper;
const quarters = { text: 'By quarter', value: PRESET_TYPES.QUARTERS };
const months = { text: 'By month', value: PRESET_TYPES.MONTHS };
const weeks = { text: 'By week', value: PRESET_TYPES.WEEKS };
const createComponent = ({ timeframeRangeType = DATE_RANGES.CURRENT_QUARTER } = {}) => {
const store = createStore();
store.dispatch('setInitialData', {
presetType: PRESET_TYPES.MONTHS,
});
wrapper = shallowMountExtended(RoadmapDaterange, {
store,
propsData: { timeframeRangeType },
});
};
const findDropdown = () => wrapper.findComponent(GlDropdown);
const findFormGroup = () => wrapper.findComponent(GlFormGroup);
const findFormRadioGroup = () => wrapper.findComponent(GlFormRadioGroup);
beforeEach(() => {
createComponent();
});
afterEach(() => {
wrapper.destroy();
});
describe('template', () => {
it('renders labels', () => {
expect(wrapper.find('label').exists()).toBe(true);
expect(wrapper.find('label').text()).toContain('Date range');
});
it('renders dropdown', () => {
expect(findDropdown().exists()).toBe(true);
});
it.each`
timeframeRangeType | hasFormGroup | availablePresets
${DATE_RANGES.CURRENT_QUARTER} | ${false} | ${[]}
${DATE_RANGES.CURRENT_YEAR} | ${true} | ${[months, weeks]}
${DATE_RANGES.THREE_YEARS} | ${true} | ${[quarters, months, weeks]}
`(
'renders radio group depending on timeframeRangeType',
async ({ timeframeRangeType, hasFormGroup, availablePresets }) => {
createComponent({ timeframeRangeType });
await nextTick();
expect(findFormGroup().exists()).toBe(hasFormGroup);
if (hasFormGroup) {
expect(findFormRadioGroup().props('options')).toEqual(availablePresets);
}
},
);
});
});
import { GlDrawer } from '@gitlab/ui'; import { GlDrawer } from '@gitlab/ui';
import { shallowMountExtended } from 'helpers/vue_test_utils_helper'; import { shallowMountExtended } from 'helpers/vue_test_utils_helper';
import RoadmapSettings from 'ee/roadmap/components/roadmap_settings.vue'; import RoadmapSettings from 'ee/roadmap/components/roadmap_settings.vue';
import RoadmapDaterange from 'ee/roadmap/components/roadmap_daterange.vue';
describe('RoadmapSettings', () => { describe('RoadmapSettings', () => {
let wrapper; let wrapper;
const createComponent = ({ isOpen = false } = {}) => { const createComponent = ({ isOpen = false } = {}) => {
wrapper = shallowMountExtended(RoadmapSettings, { wrapper = shallowMountExtended(RoadmapSettings, {
propsData: { isOpen }, propsData: { isOpen, timeframeRangeType: 'CURRENT_QUARTER' },
}); });
}; };
const findSettingsDrawer = () => wrapper.findComponent(GlDrawer); const findSettingsDrawer = () => wrapper.findComponent(GlDrawer);
const findDaterange = () => wrapper.findComponent(RoadmapDaterange);
beforeEach(() => { beforeEach(() => {
createComponent(); createComponent();
...@@ -22,9 +24,13 @@ describe('RoadmapSettings', () => { ...@@ -22,9 +24,13 @@ describe('RoadmapSettings', () => {
}); });
describe('template', () => { describe('template', () => {
it('render drawer and title', () => { it('renders drawer and title', () => {
expect(findSettingsDrawer().exists()).toBe(true); expect(findSettingsDrawer().exists()).toBe(true);
expect(findSettingsDrawer().text()).toContain('Roadmap settings'); expect(findSettingsDrawer().text()).toContain('Roadmap settings');
}); });
it('renders roadmap daterange component', () => {
expect(findDaterange().exists()).toBe(true);
});
}); });
}); });
...@@ -6228,6 +6228,15 @@ msgstr "" ...@@ -6228,6 +6228,15 @@ msgstr ""
msgid "By default, all projects and groups will use the global notifications setting." msgid "By default, all projects and groups will use the global notifications setting."
msgstr "" msgstr ""
msgid "By month"
msgstr ""
msgid "By quarter"
msgstr ""
msgid "By week"
msgstr ""
msgid "ByAuthor|by" msgid "ByAuthor|by"
msgstr "" 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