Commit 135d32dc authored by charlie ablett's avatar charlie ablett

Merge branch '217943-vsa-filter-bar-feature-flag' into 'master'

VSA filter bar feature flag

See merge request gitlab-org/gitlab!34169
parents e46ff054 884c8e7f
......@@ -19,6 +19,7 @@ import StageTableNav from './stage_table_nav.vue';
import CustomStageForm from './custom_stage_form.vue';
import PathNavigation from './path_navigation.vue';
import MetricCard from '../../shared/components/metric_card.vue';
import FilterBar from './filter_bar.vue';
export default {
name: 'CycleAnalytics',
......@@ -37,6 +38,7 @@ export default {
StageTableNav,
PathNavigation,
MetricCard,
FilterBar,
},
mixins: [glFeatureFlagsMixin(), UrlSyncMixin],
props: {
......@@ -56,6 +58,14 @@ export default {
type: Boolean,
required: true,
},
milestonesPath: {
type: String,
required: true,
},
labelsPath: {
type: String,
required: true,
},
},
computed: {
...mapState([
......@@ -119,12 +129,25 @@ export default {
stageCount() {
return this.activeStages.length;
},
hasProject() {
return this.selectedProjectIds.length;
},
},
mounted() {
const {
glFeatures: {
cycleAnalyticsScatterplotEnabled: hasDurationChart,
cycleAnalyticsScatterplotMedianEnabled: hasDurationChartMedian,
valueStreamAnalyticsPathNavigation: hasPathNavigation,
valueStreamAnalyticsFilterBar: hasFilterBar,
},
} = this;
this.setFeatureFlags({
hasDurationChart: this.glFeatures.cycleAnalyticsScatterplotEnabled,
hasDurationChartMedian: this.glFeatures.cycleAnalyticsScatterplotMedianEnabled,
hasPathNavigation: this.glFeatures.valueStreamAnalyticsPathNavigation,
hasDurationChart,
hasDurationChartMedian,
hasPathNavigation,
hasFilterBar,
});
},
methods: {
......@@ -232,6 +255,11 @@ export default {
:default-projects="selectedProjects"
@selected="onProjectsSelect"
/>
<filter-bar
v-if="featureFlags.hasFilterBar"
class="js-filter-bar filtered-search-box mx-2 gl-display-none"
:disabled="!hasProject"
/>
<div
v-if="shouldDisplayFilters"
class="ml-0 ml-md-auto mt-2 mt-md-0 d-flex flex-column flex-md-row align-items-md-center justify-content-md-end"
......
<script>
export default {
name: 'FilterBar',
components: {},
props: {
disabled: {
type: Boolean,
required: false,
default: false,
},
},
};
</script>
<template>
<div></div>
</template>
......@@ -6,9 +6,17 @@ import { parseBoolean } from '~/lib/utils/common_utils';
export default () => {
const el = document.querySelector('#js-cycle-analytics-app');
const { emptyStateSvgPath, noDataSvgPath, noAccessSvgPath, hideGroupDropDown } = el.dataset;
const {
emptyStateSvgPath,
noDataSvgPath,
noAccessSvgPath,
hideGroupDropDown,
milestonesPath = '',
labelsPath = '',
} = el.dataset;
const initialData = buildCycleAnalyticsInitialData(el.dataset);
const store = createStore();
store.dispatch('initializeCycleAnalytics', initialData);
......@@ -23,6 +31,8 @@ export default () => {
noDataSvgPath,
noAccessSvgPath,
hideGroupDropDown: parseBoolean(hideGroupDropDown),
milestonesPath,
labelsPath,
},
}),
});
......
......@@ -15,6 +15,7 @@ class Analytics::CycleAnalyticsController < Analytics::ApplicationController
push_frontend_feature_flag(:cycle_analytics_scatterplot_enabled, default_enabled: true)
push_frontend_feature_flag(:cycle_analytics_scatterplot_median_enabled, default_enabled: true)
push_frontend_feature_flag(:value_stream_analytics_path_navigation, @group)
push_frontend_feature_flag(:value_stream_analytics_filter_bar, @group)
end
private
......
- page_title _("Value Stream Analytics")
- data_attributes = @request_params.valid? ? @request_params.to_data_attributes : {}
- data_attributes.merge!({ empty_state_svg_path: image_path("illustrations/analytics/cycle-analytics-empty-chart.svg"), no_data_svg_path: image_path("illustrations/analytics/cycle-analytics-empty-chart.svg"), no_access_svg_path: image_path("illustrations/analytics/no-access.svg"), hide_group_drop_down: 'true' })
- api_paths = @group.present? ? { milestones_path: group_milestones_path(@group), labels_path: group_labels_path(@group) } : {}
- image_paths = { empty_state_svg_path: image_path("illustrations/analytics/cycle-analytics-empty-chart.svg"), no_data_svg_path: image_path("illustrations/analytics/cycle-analytics-empty-chart.svg"), no_access_svg_path: image_path("illustrations/analytics/no-access.svg")}
- settings = { hide_group_drop_down: 'true' }
- data_attributes.merge!(api_paths, image_paths, settings)
#js-cycle-analytics-app{ data: data_attributes }
......@@ -21,6 +21,7 @@ RSpec.describe 'Group Value Stream Analytics', :js do
stage_nav_selector = '.stage-nav'
path_nav_selector = '.js-path-navigation'
filter_bar_selector = '.js-filter-bar'
3.times do |i|
let_it_be("issue_#{i}".to_sym) { create(:issue, title: "New Issue #{i}", project: project, created_at: 2.days.ago) }
......@@ -153,19 +154,38 @@ RSpec.describe 'Group Value Stream Analytics', :js do
expect(page).to have_selector('.js-daterange-picker', visible: true)
end
it 'does not show the path navigation' do
expect(page).to have_selector(path_nav_selector, visible: false)
it 'shows the path navigation' do
expect(page).to have_selector(path_nav_selector)
end
context 'with path navigation feature flag enabled' do
before do
stub_feature_flags(value_stream_analytics_path_navigation: true)
select_group
end
it 'shows the filter bar' do
expect(page).to have_selector(filter_bar_selector, visible: false)
end
end
it 'shows the path navigation' do
expect(page).to have_selector(path_nav_selector, visible: true)
end
context 'with path navigation feature flag disabled' do
before do
stub_feature_flags(value_stream_analytics_path_navigation: false)
visit analytics_cycle_analytics_path
select_group
end
it 'shows the path navigation' do
expect(page).not_to have_selector(path_nav_selector)
end
end
context 'with filter bar feature flag disabled' do
before do
stub_feature_flags(value_stream_analytics_filter_bar: false)
visit analytics_cycle_analytics_path
select_group
end
it 'does not show the filter bar' do
expect(page).not_to have_selector(filter_bar_selector)
end
end
......
......@@ -20,7 +20,8 @@ RSpec.describe 'Group value stream analytics' do
expect(page).to have_pushed_frontend_feature_flags(
cycleAnalyticsScatterplotEnabled: true,
cycleAnalyticsScatterplotMedianEnabled: true,
valueStreamAnalyticsPathNavigation: true
valueStreamAnalyticsPathNavigation: true,
valueStreamAnalyticsFilterBar: true
)
end
......@@ -35,4 +36,16 @@ RSpec.describe 'Group value stream analytics' do
expect(page).to have_pushed_frontend_feature_flags(valueStreamAnalyticsPathNavigation: false)
end
end
context 'when `value_stream_analytics_filter_bar` is disabled for a group' do
before do
stub_feature_flags(value_stream_analytics_filter_bar: false, thing: group)
end
it 'pushes disabled feature flag to the frontend' do
visit group_analytics_cycle_analytics_path(group)
expect(page).to have_pushed_frontend_feature_flags(valueStreamAnalyticsFilterBar: false)
end
end
end
......@@ -14,6 +14,7 @@ import StageTable from 'ee/analytics/cycle_analytics/components/stage_table.vue'
import StageTableNav from 'ee/analytics/cycle_analytics/components/stage_table_nav.vue';
import StageNavItem from 'ee/analytics/cycle_analytics/components/stage_nav_item.vue';
import AddStageButton from 'ee/analytics/cycle_analytics/components/add_stage_button.vue';
import FilterBar from 'ee/analytics/cycle_analytics/components/filter_bar.vue';
import DurationChart from 'ee/analytics/cycle_analytics/components/duration_chart.vue';
import Daterange from 'ee/analytics/shared/components/daterange.vue';
import TypeOfWorkCharts from 'ee/analytics/cycle_analytics/components/type_of_work_charts.vue';
......@@ -29,6 +30,8 @@ import UrlSyncMixin from 'ee/analytics/shared/mixins/url_sync_mixin';
const noDataSvgPath = 'path/to/no/data';
const noAccessSvgPath = 'path/to/no/access';
const emptyStateSvgPath = 'path/to/empty/state';
const milestonesPath = '/some/milestones/endpoint';
const labelsPath = '/some/labels/endpoint';
const hideGroupDropDown = false;
const selectedGroup = convertObjectPropsToCamelCase(mockData.group);
......@@ -53,6 +56,7 @@ function createComponent({
withStageSelected = false,
scatterplotEnabled = true,
pathNavigationEnabled = false,
filterBarEnabled = false,
props = {},
} = {}) {
const func = shallow ? shallowMount : mount;
......@@ -64,6 +68,8 @@ function createComponent({
emptyStateSvgPath,
noDataSvgPath,
noAccessSvgPath,
milestonesPath,
labelsPath,
baseStagesEndpoint: mockData.endpoints.baseStagesEndpoint,
hideGroupDropDown,
...props,
......@@ -72,6 +78,7 @@ function createComponent({
glFeatures: {
cycleAnalyticsScatterplotEnabled: scatterplotEnabled,
valueStreamAnalyticsPathNavigation: pathNavigationEnabled,
valueStreamAnalyticsFilterBar: filterBarEnabled,
},
},
...opts,
......@@ -150,6 +157,10 @@ describe('Cycle Analytics component', () => {
expect(wrapper.find(AddStageButton).exists()).toBe(flag);
};
const displaysFilterBar = flag => {
expect(wrapper.find(FilterBar).exists()).toBe(flag);
};
beforeEach(() => {
mock = new MockAdapter(axios);
wrapper = createComponent();
......@@ -294,6 +305,10 @@ describe('Cycle Analytics component', () => {
});
});
it('displays the duration chart', () => {
displaysDurationChart(true);
});
describe('path navigation', () => {
describe('disabled', () => {
it('does not display the path navigation', () => {
......@@ -315,8 +330,25 @@ describe('Cycle Analytics component', () => {
});
});
it('displays the duration chart', () => {
displaysDurationChart(true);
describe('filter bar', () => {
describe('disabled', () => {
it('does not display the filter bar', () => {
displaysFilterBar(false);
});
});
describe('enabled', () => {
beforeEach(() => {
wrapper = createComponent({
withStageSelected: true,
filterBarEnabled: true,
});
});
it('displays the filter bar', () => {
displaysFilterBar(true);
});
});
});
describe('StageTable', () => {
......
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