Commit 1a84547b authored by Stan Hu's avatar Stan Hu

Merge branch '277380-fe-vsa-switch-to-table-layout-for-individual-stages' into 'master'

[FE] VSA - Switch to table layout for individual stages

See merge request gitlab-org/gitlab!57792
parents 73773204 cb05fa54
......@@ -14,6 +14,7 @@ import Metrics from './metrics.vue';
import PathNavigation from './path_navigation.vue';
import StageTable from './stage_table.vue';
import StageTableNav from './stage_table_nav.vue';
import StageTableNew from './stage_table_new.vue';
import TypeOfWorkCharts from './type_of_work_charts.vue';
import ValueStreamSelect from './value_stream_select.vue';
......@@ -28,6 +29,7 @@ export default {
TypeOfWorkCharts,
CustomStageForm,
StageTableNav,
StageTableNew,
PathNavigation,
FilterBar,
ValueStreamSelect,
......@@ -53,6 +55,7 @@ export default {
'featureFlags',
'isLoading',
'isLoadingStage',
// NOTE: we can remove the `isEmptyStage` field when we remove the existing stage table
'isEmptyStage',
'currentGroup',
'selectedProjects',
......@@ -263,14 +266,24 @@ export default {
)
"
/>
<div v-else>
<template v-else>
<metrics
v-if="!featureFlags.hasPathNavigation || isOverviewStageSelected"
:group-path="currentGroupPath"
:request-params="cycleAnalyticsRequestParams"
/>
<template v-if="featureFlags.hasPathNavigation">
<stage-table-new
v-if="!isLoading && !isOverviewStageSelected"
:is-loading="isLoading || isLoadingStage"
:stage-events="currentStageEvents"
:current-stage="selectedStage"
:empty-state-message="selectedStageError"
:no-data-svg-path="noDataSvgPath"
/>
</template>
<stage-table
v-if="!featureFlags.hasPathNavigation || !isOverviewStageSelected"
v-else
:key="stageCount"
class="js-stage-table"
:current-stage="selectedStage"
......@@ -308,7 +321,7 @@ export default {
</template>
</stage-table>
<url-sync :query="query" />
</div>
</template>
<duration-chart v-if="shouldDisplayDurationChart" class="gl-mt-3" :stages="activeStages" />
<type-of-work-charts v-if="shouldDisplayTypeOfWorkCharts" />
</div>
......
---
title: Updated VSA stage table with simpler layout
merge_request: 57792
author:
type: changed
......@@ -33,6 +33,7 @@ RSpec.describe 'Value stream analytics charts', :js do
context 'Duration chart' do
duration_stage_selector = '.js-dropdown-stages'
stage_nav_selector = '.stage-nav'
stage_table_selector = '.js-stage-table'
let(:duration_chart_dropdown) { page.find(duration_stage_selector) }
let(:first_default_stage) { page.find('.stage-nav-item-cell', text: 'Issue').ancestor('.stage-nav-item') }
......@@ -55,7 +56,7 @@ RSpec.describe 'Value stream analytics charts', :js do
before do
stub_feature_flags(value_stream_analytics_path_navigation: false)
select_group(group)
select_group(group, stage_table_selector)
end
it 'has all the default stages' do
......
......@@ -41,6 +41,7 @@ RSpec.describe 'Customizable Group Value Stream Analytics', :js do
start_field_label = 'custom-stage-start-event-label-0'
end_field_label = 'custom-stage-end-event-label-0'
name_field = 'custom-stage-name-0'
stage_table_selector = '.js-stage-table'
let(:add_stage_button) { '.js-add-stage-button' }
let(:params) { { name: custom_stage_name, start_event_identifier: start_event_identifier, end_event_identifier: end_event_identifier } }
......@@ -86,7 +87,7 @@ RSpec.describe 'Customizable Group Value Stream Analytics', :js do
context 'Manual ordering' do
before do
stub_feature_flags(value_stream_analytics_path_navigation: false)
select_group(group)
select_group(group, stage_table_selector)
end
let(:default_stage_order) { %w[Issue Plan Code Test Review Staging].freeze }
......@@ -299,7 +300,7 @@ RSpec.describe 'Customizable Group Value Stream Analytics', :js do
context 'with a group' do
context 'selected' do
before do
select_group(group)
select_group(group, stage_table_selector)
end
it_behaves_like 'can create custom stages' do
......@@ -312,7 +313,7 @@ RSpec.describe 'Customizable Group Value Stream Analytics', :js do
context 'with a custom stage created', quarantine: 'https://gitlab.com/gitlab-org/gitlab/-/issues/273045' do
before do
create_custom_stage
select_group(group)
select_group(group, stage_table_selector)
expect(page).to have_text custom_stage_name
end
......@@ -324,7 +325,7 @@ RSpec.describe 'Customizable Group Value Stream Analytics', :js do
context 'with a sub group' do
context 'selected' do
before do
select_group(sub_group)
select_group(sub_group, stage_table_selector)
end
it_behaves_like 'can create custom stages' do
......@@ -337,7 +338,7 @@ RSpec.describe 'Customizable Group Value Stream Analytics', :js do
context 'with a custom stage created' do
before do
create_custom_stage(sub_group)
select_group(sub_group)
select_group(sub_group, stage_table_selector)
expect(page).to have_text custom_stage_name
end
......@@ -349,7 +350,7 @@ RSpec.describe 'Customizable Group Value Stream Analytics', :js do
context 'Add a stage button' do
before do
stub_feature_flags(value_stream_analytics_path_navigation: false)
select_group(group)
select_group(group, stage_table_selector)
end
it 'displays the custom stage form when clicked' do
......@@ -378,7 +379,7 @@ RSpec.describe 'Customizable Group Value Stream Analytics', :js do
before do
stub_feature_flags(value_stream_analytics_path_navigation: false)
select_group(group)
select_group(group, stage_table_selector)
toggle_more_options(first_default_stage)
end
......@@ -431,7 +432,7 @@ RSpec.describe 'Customizable Group Value Stream Analytics', :js do
before do
stub_feature_flags(value_stream_analytics_path_navigation: false)
create_custom_stage
select_group(group)
select_group(group, stage_table_selector)
expect(page).to have_text custom_stage_name
......@@ -487,7 +488,7 @@ RSpec.describe 'Customizable Group Value Stream Analytics', :js do
context 'hidden stage' do
before do
stub_feature_flags(value_stream_analytics_path_navigation: false)
select_group(group)
select_group(group, stage_table_selector)
toggle_more_options(first_default_stage)
click_button(_('Hide stage'))
......
......@@ -121,14 +121,13 @@ RSpec.describe 'Group value stream analytics filters and data', :js do
end
shared_examples 'group value stream analytics' do
context 'stage panel' do
context 'stage table' do
before do
select_stage("Issue")
end
it 'displays the stage table headers' do
expect(page).to have_selector('.event-header', visible: true)
expect(page).to have_selector('.total-time-header', visible: true)
it 'displays the stage table' do
expect(page).to have_selector('[data-testid="vsa-stage-table"]')
end
end
......@@ -176,7 +175,7 @@ RSpec.describe 'Group value stream analytics filters and data', :js do
before do
stub_feature_flags(value_stream_analytics_path_navigation: false)
select_group(group)
select_group(group, '.js-stage-table')
end
it 'does not show the path navigation' do
......@@ -323,28 +322,16 @@ RSpec.describe 'Group value stream analytics filters and data', :js do
{ title: 'Test', description: 'Total test time for all commits/merges', events_count: 0, time: "-" }
]
it 'each stage will display the events description when selected', :sidekiq_might_not_need_inline do
stages_without_data.each do |stage|
select_stage(stage[:title])
expect(page).not_to have_selector('.stage-events .events-description')
end
stages_with_data.each do |stage|
select_stage(stage[:title])
expect(page.find('.stage-events .events-description').text).to have_text(_(stage[:description]))
end
end
it 'each stage with events will display the stage events list when selected', :sidekiq_might_not_need_inline do
stages_without_data.each do |stage|
select_stage(stage[:title])
expect(page).not_to have_selector('.stage-events .stage-event-item')
expect(page).not_to have_selector('[data-testid="vsa-stage-event"]')
end
stages_with_data.each do |stage|
select_stage(stage[:title])
expect(page).to have_selector('.stage-events .stage-event-list')
expect(page.all('.stage-events .stage-event-item').length).to eq(stage[:events_count])
expect(page).to have_selector('[data-testid="vsa-stage-table"]')
expect(page.all('[data-testid="vsa-stage-event"]').length).to eq(stage[:events_count])
end
end
......
......@@ -13,6 +13,7 @@ import PathNavigation from 'ee/analytics/cycle_analytics/components/path_navigat
import StageNavItem from 'ee/analytics/cycle_analytics/components/stage_nav_item.vue';
import StageTable from 'ee/analytics/cycle_analytics/components/stage_table.vue';
import StageTableNav from 'ee/analytics/cycle_analytics/components/stage_table_nav.vue';
import StageTableNew from 'ee/analytics/cycle_analytics/components/stage_table_new.vue';
import TypeOfWorkCharts from 'ee/analytics/cycle_analytics/components/type_of_work_charts.vue';
import ValueStreamSelect from 'ee/analytics/cycle_analytics/components/value_stream_select.vue';
import createStore from 'ee/analytics/cycle_analytics/store';
......@@ -166,8 +167,8 @@ describe('Value Stream Analytics component', () => {
expect(wrapper.find(Metrics).exists()).toBe(flag);
};
const displaysStageTable = (flag) => {
expect(wrapper.find(StageTable).exists()).toBe(flag);
const displaysStageTable = (flag, component = StageTable) => {
expect(wrapper.find(component).exists()).toBe(flag);
};
const displaysDurationChart = (flag) => {
......@@ -233,6 +234,7 @@ describe('Value Stream Analytics component', () => {
it('does not display the stage table', () => {
displaysStageTable(false);
displaysStageTable(false, StageTableNew);
});
it('does not display the duration chart', () => {
......@@ -289,6 +291,7 @@ describe('Value Stream Analytics component', () => {
it('does not display the stage table', () => {
displaysStageTable(false);
displaysStageTable(false, StageTableNew);
});
it('does not display the add stage button', () => {
......@@ -394,6 +397,7 @@ describe('Value Stream Analytics component', () => {
it('hides the stage table', () => {
displaysStageTable(false);
displaysStageTable(false, StageTableNew);
});
it('hides the add stage button', () => {
......@@ -402,28 +406,25 @@ describe('Value Stream Analytics component', () => {
describe('Without the overview stage selected', () => {
beforeEach(async () => {
mock = new MockAdapter(axios);
mockRequiredRoutes(mock);
wrapper = await createComponent({
withStageSelected: true,
featureFlags: {
hasPathNavigation: true,
},
});
await store.dispatch('setSelectedStage', mockData.issueStage);
await wrapper.vm.$nextTick();
});
it('displays the stage table', () => {
displaysStageTable(true);
displaysStageTable(true, StageTableNew);
});
it('displays the add stage button', async () => {
wrapper = await createComponent({
opts: {
stubs: {
StageTable,
StageTableNav,
AddStageButton,
},
},
withStageSelected: true,
});
await wrapper.vm.$nextTick();
displaysAddStageButton(true);
it('does not display the add stage button', () => {
displaysAddStageButton(false);
});
});
......@@ -441,79 +442,79 @@ describe('Value Stream Analytics component', () => {
it('does not display the path navigation', () => {
displaysPathNavigation(false);
});
});
describe('enabled', () => {
beforeEach(async () => {
wrapper = await createComponent({
withStageSelected: true,
featureFlags: {
hasPathNavigation: true,
},
describe('StageTable', () => {
beforeEach(async () => {
mock = new MockAdapter(axios);
mockRequiredRoutes(mock);
wrapper = await createComponent({
opts: {
stubs: {
StageTable,
StageTableNav,
StageNavItem,
},
},
withStageSelected: true,
});
});
});
it('displays the path navigation', () => {
displaysPathNavigation(true);
});
});
});
describe('StageTable', () => {
beforeEach(async () => {
mock = new MockAdapter(axios);
mockRequiredRoutes(mock);
wrapper = await createComponent({
opts: {
stubs: {
StageTable,
StageTableNav,
StageNavItem,
},
},
withStageSelected: true,
});
});
it('has the first stage selected by default', () => {
const first = findStageNavItemAtIndex(0);
const second = findStageNavItemAtIndex(1);
it('has the first stage selected by default', () => {
const first = findStageNavItemAtIndex(0);
const second = findStageNavItemAtIndex(1);
expect(first.props('isActive')).toBe(true);
expect(second.props('isActive')).toBe(false);
});
expect(first.props('isActive')).toBe(true);
expect(second.props('isActive')).toBe(false);
});
it('can navigate to different stages', async () => {
findStageNavItemAtIndex(2).trigger('click');
it('can navigate to different stages', async () => {
findStageNavItemAtIndex(2).trigger('click');
await wrapper.vm.$nextTick();
const first = findStageNavItemAtIndex(0);
const third = findStageNavItemAtIndex(2);
expect(third.props('isActive')).toBe(true);
expect(first.props('isActive')).toBe(false);
});
await wrapper.vm.$nextTick();
const first = findStageNavItemAtIndex(0);
const third = findStageNavItemAtIndex(2);
expect(third.props('isActive')).toBe(true);
expect(first.props('isActive')).toBe(false);
describe('Add stage button', () => {
beforeEach(async () => {
wrapper = await createComponent({
opts: {
stubs: {
StageTable,
StageTableNav,
AddStageButton,
},
},
withStageSelected: true,
});
});
it('can navigate to the custom stage form', async () => {
expect(wrapper.find(CustomStageForm).exists()).toBe(false);
findAddStageButton().trigger('click');
await wrapper.vm.$nextTick();
expect(wrapper.find(CustomStageForm).exists()).toBe(true);
});
});
});
});
describe('Add stage button', () => {
describe('enabled', () => {
beforeEach(async () => {
wrapper = await createComponent({
opts: {
stubs: {
StageTable,
StageTableNav,
AddStageButton,
},
},
withStageSelected: true,
featureFlags: {
hasPathNavigation: true,
},
});
});
it('can navigate to the custom stage form', async () => {
expect(wrapper.find(CustomStageForm).exists()).toBe(false);
findAddStageButton().trigger('click');
await wrapper.vm.$nextTick();
expect(wrapper.find(CustomStageForm).exists()).toBe(true);
it('displays the path navigation', () => {
displaysPathNavigation(true);
});
});
});
......
......@@ -3,15 +3,15 @@
module CycleAnalyticsHelpers
include GitHelpers
def wait_for_stages_to_load
expect(page).to have_selector '.js-stage-table'
def wait_for_stages_to_load(selector = '.js-path-navigation')
expect(page).to have_selector selector
wait_for_requests
end
def select_group(target_group)
def select_group(target_group, ready_selector = '.js-path-navigation')
visit group_analytics_cycle_analytics_path(target_group)
wait_for_stages_to_load
wait_for_stages_to_load(ready_selector)
end
def toggle_dropdown(field)
......
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