Commit 0b2e5048 authored by Clement Ho's avatar Clement Ho

Merge branch '13076-add-stage-button-ee' into 'master'

Customizable Cycle Analytics - Add stage button

Closes #13076

See merge request gitlab-org/gitlab-ee!15122
parents 1eb24b98 8216a617
export default {
data() {
return {
isCustomStageForm: false,
};
},
methods: {
showAddStageForm: () => {},
hideAddStageForm: () => {},
},
};
......@@ -23,7 +23,10 @@ export default {
</script>
<template>
<div :class="{ active: isActive }" class="stage-nav-item d-flex pl-4 pr-4 m-0 mb-1 ml-2 rounded">
<div
:class="{ active: isActive }"
class="stage-nav-item d-flex pl-4 pr-4 m-0 mb-1 ml-2 rounded border-color-default border-style-solid border-width-1px"
>
<slot></slot>
<div v-if="canEdit" class="dropdown">
<gl-button
......
......@@ -3,6 +3,7 @@ import Vue from 'vue';
import Cookies from 'js-cookie';
import { GlEmptyState } from '@gitlab/ui';
import filterMixins from 'ee_else_ce/analytics/cycle_analytics/mixins/filter_mixins';
import addStageMixin from 'ee_else_ce/analytics/cycle_analytics/mixins/add_stage_mixin';
import Flash from '../flash';
import { __ } from '~/locale';
import Translate from '../vue_shared/translate';
......@@ -43,8 +44,12 @@ export default () => {
DateRangeDropdown: () =>
import('ee_component/analytics/shared/components/date_range_dropdown.vue'),
'stage-nav-item': stageNavItem,
CustomStageForm: () =>
import('ee_component/analytics/cycle_analytics/components/custom_stage_form.vue'),
AddStageButton: () =>
import('ee_component/analytics/cycle_analytics/components/add_stage_button.vue'),
},
mixins: [filterMixins],
mixins: [filterMixins, addStageMixin],
data() {
return {
store: CycleAnalyticsStore,
......@@ -124,6 +129,7 @@ export default () => {
return;
}
this.hideAddStageForm();
this.isLoadingStage = true;
this.store.setStageEvents([], stage);
this.store.setActiveStage(stage);
......
......@@ -41,7 +41,6 @@
width: 20%;
}
.fa {
color: $cycle-analytics-light-gray;
......@@ -146,7 +145,6 @@
.stage-nav-item {
line-height: 65px;
border: 1px solid $border-color;
&.active {
background: $blue-50;
......
......@@ -15,3 +15,9 @@
font-size: $size;
}
}
.border-width-1px { border-width: 1px; }
.border-style-dashed { border-style: dashed; }
.border-style-solid { border-style: solid; }
.border-color-blue-300 { border-color: $blue-300; }
.border-color-default { border-color: $border-color; }
<script>
export default {
props: {
active: {
type: Boolean,
required: true,
},
},
computed: {
activeClass() {
return 'active font-weight-bold border-style-solid border-color-blue-300';
},
inactiveClass() {
return 'bg-transparent border-style-dashed border-color-default';
},
},
};
</script>
<template>
<li
:class="[active ? activeClass : inactiveClass]"
class="js-add-stage-button stage-nav-item ml-2 mb-1 rounded d-flex justify-content-center border-width-1px"
@click="$emit('showform')"
>
{{ s__('CustomCycleAnalytics|Add a stage') }}
</li>
</template>
<template>
<div class="ml-3">
<h2>{{ s__('New stage') }}</h2>
</div>
</template>
export default {
data() {
return {
isCustomStageForm: false,
};
},
methods: {
showAddStageForm() {
if (this.store) {
this.store.deactivateAllStages();
}
this.isCustomStageForm = true;
},
hideAddStageForm() {
this.isCustomStageForm = false;
},
},
};
- page_title _('Cycle Analytics')
- customizable_cycle_analytics = Feature.enabled?(:customizable_cycle_analytics)
- if cookies[:cycle_analytics_app] == 'true'
#js-cycle-analytics-app
......@@ -63,13 +64,17 @@
%nav.stage-nav
%ul
%stage-nav-item{ "v-for" => "stage in state.stages", ":key" => '`ca-stage-title-${stage.title}`', '@select' => 'selectStage(stage)', ":title" => "stage.title", ":is-user-allowed" => "stage.isUserAllowed", ":value" => "stage.value", ":is-active" => "stage.active" }
- if customizable_cycle_analytics
%add-stage-button{ '@showform' => 'showAddStageForm', ":active" => 'isCustomStageForm' }
.section.stage-events
%template{ "v-if" => "isLoadingStage" }
= icon("spinner spin")
%template{ "v-if" => "currentStage && !currentStage.isUserAllowed" }
= render partial: "projects/cycle_analytics/no_access"
%template{ "v-else" => true }
%template{ "v-if" => "isEmptyStage && !isLoadingStage" }
%template{ "v-if" => "isEmptyStage && !isLoadingStage && !isCustomStageForm" }
= render partial: "projects/cycle_analytics/empty_stage"
%template{ "v-if" => "state.events.length && !isLoadingStage && !isEmptyStage" }
%template{ "v-if" => "state.events.length && !isLoadingStage && !isEmptyStage && !isCustomStageForm" }
%component{ ":is" => "currentStage.component", ":stage" => "currentStage", ":items" => "state.events" }
- if customizable_cycle_analytics
%custom-stage-form{ "v-if" => "isCustomStageForm" }
......@@ -41,4 +41,55 @@ describe 'Group Cycle Analytics', :js do
expect(page).to have_selector('.js-timeframe-filter', visible: true)
end
end
describe 'Customizable cycle analytics', :js do
context 'enabled' do
before do
dropdown = page.find('.dropdown-groups')
dropdown.click
dropdown.find('a').click
end
context 'Add a stage button' do
it 'is visible' do
expect(page).to have_selector('.js-add-stage-button', visible: true)
expect(page).to have_text('Add a stage')
end
it 'becomes active when clicked ' do
btn = page.find('.js-add-stage-button')
expect(btn[:class]).not_to include('active')
btn.click
expect(btn[:class]).to include('active')
end
it 'displays the custom stage form when clicked' do
expect(page).not_to have_text('New stage')
page.find('.js-add-stage-button').click
expect(page).to have_text('New stage')
end
end
end
context 'not enabled' do
before do
stub_feature_flags(customizable_cycle_analytics: false)
dropdown = page.find('.dropdown-groups')
dropdown.click
dropdown.find('a').click
end
context 'Add a stage button' do
it 'is not visible' do
expect(page).to have_selector('.js-add-stage-button', visible: false)
end
end
end
end
end
import { shallowMount } from '@vue/test-utils';
import AddStageButton from 'ee/analytics/cycle_analytics/components/add_stage_button.vue';
describe('AddStageButton', () => {
const active = false;
function createComponent(props) {
return shallowMount(AddStageButton, {
propsData: {
active,
...props,
},
});
}
let wrapper = null;
afterEach(() => {
wrapper.destroy();
});
describe('is not active', () => {
beforeEach(() => {
wrapper = createComponent();
});
it('emits the `showform` event when clicked', () => {
wrapper = createComponent();
expect(wrapper.emitted().showform).toBeUndefined();
wrapper.trigger('click');
expect(wrapper.emitted().showform.length).toBe(1);
});
it('does not have the active class', () => {
expect(wrapper.classes('active')).toBe(false);
});
});
describe('is active', () => {
it('has the active class when active=true', () => {
wrapper = createComponent({ active: true });
expect(wrapper.classes('active')).toBe(true);
});
});
});
......@@ -4469,6 +4469,9 @@ msgstr ""
msgid "Custom project templates have not been set up for groups that you are a member of. They are enabled from a group’s settings page. Contact your group’s Owner or Maintainer to setup custom project templates."
msgstr ""
msgid "CustomCycleAnalytics|Add a stage"
msgstr ""
msgid "Customize colors"
msgstr ""
......@@ -10067,6 +10070,9 @@ msgstr ""
msgid "New snippet"
msgstr ""
msgid "New stage"
msgstr ""
msgid "New subgroup"
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