Commit 27cacbd2 authored by Kushal Pandya's avatar Kushal Pandya

Merge branch 'psi-burnup-feature-flag' into 'master'

Add burnup_chart feature flag

See merge request gitlab-org/gitlab!29391
parents 7beb37cf b1721584
...@@ -5,6 +5,9 @@ class Groups::MilestonesController < Groups::ApplicationController ...@@ -5,6 +5,9 @@ class Groups::MilestonesController < Groups::ApplicationController
before_action :milestone, only: [:edit, :show, :update, :merge_requests, :participants, :labels, :destroy] before_action :milestone, only: [:edit, :show, :update, :merge_requests, :participants, :labels, :destroy]
before_action :authorize_admin_milestones!, only: [:edit, :new, :create, :update, :destroy] before_action :authorize_admin_milestones!, only: [:edit, :new, :create, :update, :destroy]
before_action do
push_frontend_feature_flag(:burnup_charts)
end
def index def index
respond_to do |format| respond_to do |format|
......
...@@ -6,6 +6,9 @@ class Projects::MilestonesController < Projects::ApplicationController ...@@ -6,6 +6,9 @@ class Projects::MilestonesController < Projects::ApplicationController
before_action :check_issuables_available! before_action :check_issuables_available!
before_action :milestone, only: [:edit, :update, :destroy, :show, :merge_requests, :participants, :labels, :promote] before_action :milestone, only: [:edit, :update, :destroy, :show, :merge_requests, :participants, :labels, :promote]
before_action do
push_frontend_feature_flag(:burnup_charts)
end
# Allow read any milestone # Allow read any milestone
before_action :authorize_read_milestone! before_action :authorize_read_milestone!
......
<script>
import { GlButton, GlButtonGroup } from '@gitlab/ui';
import glFeatureFlagsMixin from '~/vue_shared/mixins/gl_feature_flags_mixin';
import { __ } from '~/locale';
import BurndownChart from './burndown_chart.vue';
export default {
components: {
GlButton,
GlButtonGroup,
BurndownChart,
},
mixins: [glFeatureFlagsMixin()],
props: {
startDate: {
type: String,
required: true,
},
dueDate: {
type: String,
required: true,
},
openIssuesCount: {
type: Array,
required: false,
default: () => [],
},
openIssuesWeight: {
type: Array,
required: false,
default: () => [],
},
},
data() {
return {
issuesSelected: true,
};
},
computed: {
title() {
return this.glFeatures.burnupCharts ? __('Charts') : __('Burndown chart');
},
issueButtonCategory() {
return this.issuesSelected ? 'primary' : 'secondary';
},
weightButtonCategory() {
return this.issuesSelected ? 'secondary' : 'primary';
},
},
methods: {
setIssueSelected(selected) {
this.issuesSelected = selected;
},
},
};
</script>
<template>
<div data-qa-selector="burndown_chart">
<div class="burndown-header d-flex align-items-center">
<h3 ref="chartsTitle">{{ title }}</h3>
<gl-button-group class="ml-3 js-burndown-data-selector">
<gl-button
ref="totalIssuesButton"
:category="issueButtonCategory"
variant="info"
size="small"
@click="setIssueSelected(true)"
>
{{ __('Issues') }}
</gl-button>
<gl-button
ref="totalWeightButton"
:category="weightButtonCategory"
variant="info"
size="small"
data-qa-selector="weight_button"
@click="setIssueSelected(false)"
>
{{ __('Issue weight') }}
</gl-button>
</gl-button-group>
</div>
<div v-if="glFeatures.burnupCharts" class="row">
<burndown-chart
:start-date="startDate"
:due-date="dueDate"
:open-issues-count="openIssuesCount"
:open-issues-weight="openIssuesWeight"
:issues-selected="issuesSelected"
class="col-md-6"
/>
</div>
<burndown-chart
v-else
:show-title="false"
:start-date="startDate"
:due-date="dueDate"
:open-issues-count="openIssuesCount"
:open-issues-weight="openIssuesWeight"
:issues-selected="issuesSelected"
/>
</div>
</template>
<script> <script>
import { GlDeprecatedButton, GlButtonGroup } from '@gitlab/ui';
import { GlLineChart } from '@gitlab/ui/dist/charts'; import { GlLineChart } from '@gitlab/ui/dist/charts';
import dateFormat from 'dateformat'; import dateFormat from 'dateformat';
import ResizableChartContainer from '~/vue_shared/components/resizable_chart/resizable_chart_container.vue'; import ResizableChartContainer from '~/vue_shared/components/resizable_chart/resizable_chart_container.vue';
...@@ -7,8 +6,6 @@ import { s__, __, sprintf } from '~/locale'; ...@@ -7,8 +6,6 @@ import { s__, __, sprintf } from '~/locale';
export default { export default {
components: { components: {
GlDeprecatedButton,
GlButtonGroup,
GlLineChart, GlLineChart,
ResizableChartContainer, ResizableChartContainer,
}, },
...@@ -31,10 +28,19 @@ export default { ...@@ -31,10 +28,19 @@ export default {
required: false, required: false,
default: () => [], default: () => [],
}, },
issuesSelected: {
type: Boolean,
required: false,
default: true,
},
showTitle: {
type: Boolean,
required: false,
default: true,
},
}, },
data() { data() {
return { return {
issuesSelected: true,
tooltip: { tooltip: {
title: '', title: '',
content: '', content: '',
...@@ -132,27 +138,8 @@ export default { ...@@ -132,27 +138,8 @@ export default {
<template> <template>
<div data-qa-selector="burndown_chart"> <div data-qa-selector="burndown_chart">
<div class="burndown-header d-flex align-items-center"> <div v-if="showTitle" class="burndown-header d-flex align-items-center">
<h3>{{ __('Burndown chart') }}</h3> <h3>{{ __('Burndown chart') }}</h3>
<gl-button-group class="ml-3 js-burndown-data-selector">
<gl-deprecated-button
ref="totalIssuesButton"
:variant="issuesSelected ? 'primary' : 'inverted-primary'"
size="sm"
@click="showIssueCount"
>
{{ __('Issues') }}
</gl-deprecated-button>
<gl-deprecated-button
ref="totalWeightButton"
:variant="issuesSelected ? 'inverted-primary' : 'primary'"
size="sm"
data-qa-selector="weight_button"
@click="showIssueWeight"
>
{{ __('Issue weight') }}
</gl-deprecated-button>
</gl-button-group>
</div> </div>
<resizable-chart-container class="burndown-chart"> <resizable-chart-container class="burndown-chart">
<gl-line-chart <gl-line-chart
......
import Vue from 'vue'; import Vue from 'vue';
import $ from 'jquery'; import $ from 'jquery';
import Cookies from 'js-cookie'; import Cookies from 'js-cookie';
import BurndownChart from './components/burndown_chart.vue'; import BurnCharts from './components/burn_charts.vue';
import BurndownChartData from './burndown_chart_data'; import BurndownChartData from './burndown_chart_data';
import Flash from '~/flash'; import Flash from '~/flash';
import axios from '~/lib/utils/axios_utils'; import axios from '~/lib/utils/axios_utils';
...@@ -36,10 +36,10 @@ export default () => { ...@@ -36,10 +36,10 @@ export default () => {
return new Vue({ return new Vue({
el: container, el: container,
components: { components: {
BurndownChart, BurnCharts,
}, },
render(createElement) { render(createElement) {
return createElement('burndown-chart', { return createElement('burn-charts', {
props: { props: {
startDate, startDate,
dueDate, dueDate,
......
import { shallowMount } from '@vue/test-utils';
import { GlButton } from '@gitlab/ui';
import BurnCharts from 'ee/burndown_chart/components/burn_charts.vue';
import BurndownChart from 'ee/burndown_chart/components/burndown_chart.vue';
describe('burndown_chart', () => {
let wrapper;
const findChartsTitle = () => wrapper.find({ ref: 'chartsTitle' });
const findIssuesButton = () => wrapper.find({ ref: 'totalIssuesButton' });
const findWeightButton = () => wrapper.find({ ref: 'totalWeightButton' });
const findActiveButtons = () =>
wrapper.findAll(GlButton).filter(button => button.attributes().category === 'primary');
const findBurndownChart = () => wrapper.find(BurndownChart);
const defaultProps = {
startDate: '2019-08-07T00:00:00.000Z',
dueDate: '2019-09-09T00:00:00.000Z',
openIssuesCount: [],
openIssuesWeight: [],
};
const createComponent = ({ props = {}, featureEnabled = false } = {}) => {
wrapper = shallowMount(BurnCharts, {
propsData: {
...defaultProps,
...props,
},
provide: {
glFeatures: { burnupCharts: featureEnabled },
},
});
};
it('includes Issues and Issue weight buttons', () => {
createComponent();
expect(findIssuesButton().text()).toBe('Issues');
expect(findWeightButton().text()).toBe('Issue weight');
});
it('defaults to total issues', () => {
createComponent();
expect(findActiveButtons().length).toBe(1);
expect(
findActiveButtons()
.at(0)
.text(),
).toBe('Issues');
expect(findBurndownChart().props().issuesSelected).toBe(true);
});
it('toggles Issue weight', () => {
createComponent();
findWeightButton().vm.$emit('click');
return wrapper.vm.$nextTick().then(() => {
expect(findActiveButtons().length).toBe(1);
expect(
findActiveButtons()
.at(0)
.text(),
).toBe('Issue weight');
});
});
describe('feature disabled', () => {
beforeEach(() => {
createComponent({ featureEnabled: false });
});
it('does not reduce width of burndown chart', () => {
expect(findBurndownChart().classes()).toEqual([]);
});
it('sets section title and chart title correctly', () => {
expect(findChartsTitle().text()).toBe('Burndown chart');
expect(findBurndownChart().props().showTitle).toBe(false);
});
});
describe('feature enabled', () => {
beforeEach(() => {
createComponent({ featureEnabled: true });
});
it('reduces width of burndown chart', () => {
expect(findBurndownChart().classes()).toContain('col-md-6');
});
it('sets section title and chart title correctly', () => {
expect(findChartsTitle().text()).toBe('Charts');
expect(findBurndownChart().props().showTitle).toBe(true);
});
});
});
...@@ -4,9 +4,6 @@ import BurndownChart from 'ee/burndown_chart/components/burndown_chart.vue'; ...@@ -4,9 +4,6 @@ import BurndownChart from 'ee/burndown_chart/components/burndown_chart.vue';
describe('burndown_chart', () => { describe('burndown_chart', () => {
let wrapper; let wrapper;
const issuesButton = () => wrapper.find({ ref: 'totalIssuesButton' });
const weightButton = () => wrapper.find({ ref: 'totalWeightButton' });
const defaultProps = { const defaultProps = {
startDate: '2019-08-07T00:00:00.000Z', startDate: '2019-08-07T00:00:00.000Z',
dueDate: '2019-09-09T00:00:00.000Z', dueDate: '2019-09-09T00:00:00.000Z',
...@@ -23,31 +20,6 @@ describe('burndown_chart', () => { ...@@ -23,31 +20,6 @@ describe('burndown_chart', () => {
}); });
}; };
it('inclues Issues and Issue weight buttons', () => {
createComponent();
expect(issuesButton().text()).toBe('Issues');
expect(weightButton().text()).toBe('Issue weight');
});
it('defaults to total issues', () => {
createComponent();
expect(issuesButton().attributes('variant')).toBe('primary');
expect(weightButton().attributes('variant')).toBe('inverted-primary');
});
it('toggles Issue weight', () => {
createComponent();
weightButton().vm.$emit('click');
return wrapper.vm.$nextTick().then(() => {
expect(issuesButton().attributes('variant')).toBe('inverted-primary');
expect(weightButton().attributes('variant')).toBe('primary');
});
});
describe('with single point', () => { describe('with single point', () => {
it('does not show guideline', () => { it('does not show guideline', () => {
createComponent({ createComponent({
......
...@@ -3576,6 +3576,9 @@ msgstr "" ...@@ -3576,6 +3576,9 @@ msgstr ""
msgid "Changing group path can have unintended side effects." msgid "Changing group path can have unintended side effects."
msgstr "" msgstr ""
msgid "Charts"
msgstr ""
msgid "Charts can't be displayed as the request for data has timed out. %{documentationLink}" msgid "Charts can't be displayed as the request for data has timed out. %{documentationLink}"
msgstr "" msgstr ""
......
...@@ -14,9 +14,12 @@ module QA ...@@ -14,9 +14,12 @@ module QA
element :total_issue_weight_value element :total_issue_weight_value
end end
view 'ee/app/assets/javascripts/burndown_chart/components/burn_charts.vue' do
element :weight_button
end
view 'ee/app/assets/javascripts/burndown_chart/components/burndown_chart.vue' do view 'ee/app/assets/javascripts/burndown_chart/components/burndown_chart.vue' do
element :burndown_chart element :burndown_chart
element :weight_button
end end
def click_weight_button def click_weight_button
......
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