Commit 9c7433e9 authored by Ezekiel Kigbo's avatar Ezekiel Kigbo Committed by Brandon Labuschagne

Move duration chart to component

Moves the duration chart out of the base
component into a separate component
parent befd7a23
...@@ -6,11 +6,10 @@ import glFeatureFlagsMixin from '~/vue_shared/mixins/gl_feature_flags_mixin'; ...@@ -6,11 +6,10 @@ import glFeatureFlagsMixin from '~/vue_shared/mixins/gl_feature_flags_mixin';
import { PROJECTS_PER_PAGE } from '../constants'; import { PROJECTS_PER_PAGE } from '../constants';
import GroupsDropdownFilter from '../../shared/components/groups_dropdown_filter.vue'; import GroupsDropdownFilter from '../../shared/components/groups_dropdown_filter.vue';
import ProjectsDropdownFilter from '../../shared/components/projects_dropdown_filter.vue'; import ProjectsDropdownFilter from '../../shared/components/projects_dropdown_filter.vue';
import Scatterplot from '../../shared/components/scatterplot.vue'; import { LAST_ACTIVITY_AT, DATE_RANGE_LIMIT } from '../../shared/constants';
import { LAST_ACTIVITY_AT, dateFormats, DATE_RANGE_LIMIT } from '../../shared/constants';
import DateRange from '../../shared/components/daterange.vue'; import DateRange from '../../shared/components/daterange.vue';
import StageDropdownFilter from './stage_dropdown_filter.vue';
import StageTable from './stage_table.vue'; import StageTable from './stage_table.vue';
import DurationChart from './duration_chart.vue';
import TasksByTypeChart from './tasks_by_type_chart.vue'; import TasksByTypeChart from './tasks_by_type_chart.vue';
import UrlSyncMixin from '../../shared/mixins/url_sync_mixin'; import UrlSyncMixin from '../../shared/mixins/url_sync_mixin';
import { toYmd } from '../../shared/utils'; import { toYmd } from '../../shared/utils';
...@@ -20,13 +19,12 @@ export default { ...@@ -20,13 +19,12 @@ export default {
name: 'CycleAnalytics', name: 'CycleAnalytics',
components: { components: {
DateRange, DateRange,
DurationChart,
GlLoadingIcon, GlLoadingIcon,
GlEmptyState, GlEmptyState,
GroupsDropdownFilter, GroupsDropdownFilter,
ProjectsDropdownFilter, ProjectsDropdownFilter,
StageTable, StageTable,
StageDropdownFilter,
Scatterplot,
TasksByTypeChart, TasksByTypeChart,
RecentActivityCard, RecentActivityCard,
}, },
...@@ -214,7 +212,6 @@ export default { ...@@ -214,7 +212,6 @@ export default {
order_by: LAST_ACTIVITY_AT, order_by: LAST_ACTIVITY_AT,
include_subgroups: true, include_subgroups: true,
}, },
durationChartTooltipDateFormat: dateFormats.defaultDate,
maxDateRange: DATE_RANGE_LIMIT, maxDateRange: DATE_RANGE_LIMIT,
}; };
</script> </script>
...@@ -323,31 +320,15 @@ export default { ...@@ -323,31 +320,15 @@ export default {
/> />
</div> </div>
</div> </div>
<template v-if="shouldDisplayDurationChart"> <div v-if="shouldDisplayDurationChart" class="mt-3">
<template v-if="isDurationChartLoaded"> <duration-chart
<div class="mt-3 d-flex"> :is-loading="isLoading"
<h4 class="mt-0">{{ s__('CycleAnalytics|Days to completion') }}</h4> :stages="activeStages"
<stage-dropdown-filter :scatter-data="durationChartPlottableData"
v-if="activeStages.length" :median-line-data="durationChartMedianData"
class="ml-auto" @stageSelected="onDurationStageSelect"
:stages="activeStages" />
@selected="onDurationStageSelect" </div>
/>
</div>
<scatterplot
v-if="durationChartPlottableData"
:x-axis-title="s__('CycleAnalytics|Date')"
:y-axis-title="s__('CycleAnalytics|Total days to completion')"
:tooltip-date-format="$options.durationChartTooltipDateFormat"
:scatter-data="durationChartPlottableData"
:median-line-data="durationChartMedianData"
/>
<div v-else ref="duration-chart-no-data" class="bs-callout bs-callout-info">
{{ __('There is no data available. Please change your selection.') }}
</div>
</template>
<gl-loading-icon v-else-if="!isLoading" size="md" class="my-4 py-4" />
</template>
<template v-if="shouldDisplayTasksByTypeChart"> <template v-if="shouldDisplayTasksByTypeChart">
<div class="js-tasks-by-type-chart"> <div class="js-tasks-by-type-chart">
<div v-if="isTasksByTypeChartLoaded"> <div v-if="isTasksByTypeChartLoaded">
......
<script>
import { GlLoadingIcon } from '@gitlab/ui';
import { dateFormats } from '../../shared/constants';
import Scatterplot from '../../shared/components/scatterplot.vue';
import StageDropdownFilter from './stage_dropdown_filter.vue';
export default {
name: 'DurationChart',
components: {
GlLoadingIcon,
Scatterplot,
StageDropdownFilter,
},
props: {
isLoading: {
type: Boolean,
required: false,
default: false,
},
stages: {
type: Array,
required: true,
},
scatterData: {
type: Array,
required: true,
},
medianLineData: {
type: Array,
required: true,
},
},
computed: {
hasData() {
// TODO: check if we want to display when only the median data is available
return Boolean(this.scatterData.length || this.medianLineData.length);
},
},
methods: {},
durationChartTooltipDateFormat: dateFormats.defaultDate,
};
</script>
<template>
<gl-loading-icon v-if="isLoading" size="md" class="my-4 py-4" />
<div v-else>
<div class="d-flex">
<h4 class="mt-0">{{ s__('CycleAnalytics|Days to completion') }}</h4>
<stage-dropdown-filter
v-if="stages.length"
class="ml-auto"
:stages="stages"
@selected="$emit('stageSelected')"
/>
</div>
<scatterplot
v-if="hasData"
:x-axis-title="s__('CycleAnalytics|Date')"
:y-axis-title="s__('CycleAnalytics|Total days to completion')"
:tooltip-date-format="$options.durationChartTooltipDateFormat"
:scatter-data="scatterData"
:median-line-data="medianLineData"
/>
<div v-else ref="duration-chart-no-data" class="bs-callout bs-callout-info">
{{ __('There is no data available. Please change your selection.') }}
</div>
</div>
</template>
...@@ -23,7 +23,7 @@ export const durationChartPlottableData = state => { ...@@ -23,7 +23,7 @@ export const durationChartPlottableData = state => {
const selectedStagesDurationData = durationData.filter(stage => stage.selected); const selectedStagesDurationData = durationData.filter(stage => stage.selected);
const plottableData = getDurationChartData(selectedStagesDurationData, startDate, endDate); const plottableData = getDurationChartData(selectedStagesDurationData, startDate, endDate);
return plottableData.length ? plottableData : null; return plottableData.length ? plottableData : [];
}; };
export const durationChartMedianData = state => { export const durationChartMedianData = state => {
......
...@@ -12,6 +12,7 @@ import RecentActivityCard from 'ee/analytics/cycle_analytics/components/recent_a ...@@ -12,6 +12,7 @@ import RecentActivityCard from 'ee/analytics/cycle_analytics/components/recent_a
import StageTable from 'ee/analytics/cycle_analytics/components/stage_table.vue'; import StageTable from 'ee/analytics/cycle_analytics/components/stage_table.vue';
import 'bootstrap'; import 'bootstrap';
import '~/gl_dropdown'; import '~/gl_dropdown';
import DurationChart from 'ee/analytics/cycle_analytics/components/duration_chart.vue';
import Scatterplot from 'ee/analytics/shared/components/scatterplot.vue'; import Scatterplot from 'ee/analytics/shared/components/scatterplot.vue';
import Daterange from 'ee/analytics/shared/components/daterange.vue'; import Daterange from 'ee/analytics/shared/components/daterange.vue';
import TasksByTypeChart from 'ee/analytics/cycle_analytics/components/tasks_by_type_chart.vue'; import TasksByTypeChart from 'ee/analytics/cycle_analytics/components/tasks_by_type_chart.vue';
...@@ -39,6 +40,9 @@ const defaultStubs = { ...@@ -39,6 +40,9 @@ const defaultStubs = {
'stage-nav-item': true, 'stage-nav-item': true,
'tasks-by-type-chart': true, 'tasks-by-type-chart': true,
'labels-selector': true, 'labels-selector': true,
'stage-dropdown-filter': true,
Scatterplot: true,
DurationChart: true,
}; };
function createComponent({ function createComponent({
...@@ -206,7 +210,17 @@ describe('Cycle Analytics component', () => { ...@@ -206,7 +210,17 @@ describe('Cycle Analytics component', () => {
describe('after a filter has been selected', () => { describe('after a filter has been selected', () => {
describe('the user has access to the group', () => { describe('the user has access to the group', () => {
beforeEach(() => { beforeEach(() => {
wrapper = createComponent({ withStageSelected: true, tasksByTypeChartEnabled: false }); wrapper = createComponent({
withStageSelected: true,
tasksByTypeChartEnabled: false,
opts: {
stubs: {
...defaultStubs,
'duration-chart': false,
},
},
});
}); });
it('hides the empty state', () => { it('hides the empty state', () => {
...@@ -271,7 +285,7 @@ describe('Cycle Analytics component', () => { ...@@ -271,7 +285,7 @@ describe('Cycle Analytics component', () => {
}); });
it('displays the duration chart', () => { it('displays the duration chart', () => {
expect(wrapper.find(Scatterplot).exists()).toBe(true); expect(wrapper.find(DurationChart).exists()).toBe(true);
}); });
}); });
......
...@@ -118,7 +118,7 @@ describe('Cycle analytics getters', () => { ...@@ -118,7 +118,7 @@ describe('Cycle analytics getters', () => {
durationData: [], durationData: [],
}; };
expect(getters.durationChartPlottableData(stateWithDurationData)).toBeNull(); expect(getters.durationChartPlottableData(stateWithDurationData)).toEqual([]);
}); });
}); });
......
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