Commit 186010cf authored by Kushal Pandya's avatar Kushal Pandya

Merge branch...

Merge branch '32068-productivity-analytics-mr-table-showing-zeros-in-time-to-merge-column' into 'master'

Resolve "Productivity Analytics: MR table showing zeros in "Time to merge" column"

Closes #32068

See merge request gitlab-org/gitlab!16843
parents 8fd5bf8f 8a7dd9e9
...@@ -11,7 +11,7 @@ import { ...@@ -11,7 +11,7 @@ import {
import { GlColumnChart } from '@gitlab/ui/dist/charts'; import { GlColumnChart } from '@gitlab/ui/dist/charts';
import Icon from '~/vue_shared/components/icon.vue'; import Icon from '~/vue_shared/components/icon.vue';
import MergeRequestTable from './mr_table.vue'; import MergeRequestTable from './mr_table.vue';
import { chartKeys, metricTypes } from '../constants'; import { chartKeys } from '../constants';
export default { export default {
components: { components: {
...@@ -48,13 +48,8 @@ export default { ...@@ -48,13 +48,8 @@ export default {
}, },
computed: { computed: {
...mapState('filters', ['groupNamespace', 'projectPath']), ...mapState('filters', ['groupNamespace', 'projectPath']),
...mapState('table', [ ...mapState('table', ['isLoadingTable', 'mergeRequests', 'pageInfo', 'columnMetric']),
'isLoadingTable', ...mapGetters(['getMetricTypes']),
'mergeRequests',
'pageInfo',
'sortFields',
'columnMetric',
]),
...mapGetters('charts', [ ...mapGetters('charts', [
'chartLoading', 'chartLoading',
'getChartData', 'getChartData',
...@@ -66,7 +61,7 @@ export default { ...@@ -66,7 +61,7 @@ export default {
'sortFieldDropdownLabel', 'sortFieldDropdownLabel',
'sortIcon', 'sortIcon',
'sortTooltipTitle', 'sortTooltipTitle',
'getColumnOptions', 'tableSortOptions',
'columnMetricLabel', 'columnMetricLabel',
'isSelectedSortField', 'isSelectedSortField',
'hasNoAccessError', 'hasNoAccessError',
...@@ -92,9 +87,6 @@ export default { ...@@ -92,9 +87,6 @@ export default {
const itemValue = params.data.value[0]; const itemValue = params.data.value[0];
this.chartItemClicked({ chartKey: this.chartKeys.main, item: itemValue }); this.chartItemClicked({ chartKey: this.chartKeys.main, item: itemValue });
}, },
getMetricTypes(chartKey) {
return metricTypes.filter(m => m.chart === chartKey);
},
getColumnChartOption(chartKey) { getColumnChartOption(chartKey) {
return { return {
yAxis: { yAxis: {
...@@ -262,21 +254,21 @@ export default { ...@@ -262,21 +254,21 @@ export default {
:text="sortFieldDropdownLabel" :text="sortFieldDropdownLabel"
> >
<gl-dropdown-item <gl-dropdown-item
v-for="(value, key) in sortFields" v-for="metric in tableSortOptions"
:key="key" :key="metric.key"
active-class="is-active" active-class="is-active"
class="w-100" class="w-100"
@click="setSortField(key)" @click="setSortField(metric.key)"
> >
<span class="d-flex"> <span class="d-flex">
<icon <icon
class="flex-shrink-0 append-right-4" class="flex-shrink-0 append-right-4"
:class="{ :class="{
invisible: !isSelectedSortField(key), invisible: !isSelectedSortField(metric.key),
}" }"
name="mobile-issue-close" name="mobile-issue-close"
/> />
{{ value }} {{ metric.label }}
</span> </span>
</gl-dropdown-item> </gl-dropdown-item>
</gl-dropdown> </gl-dropdown>
...@@ -292,7 +284,7 @@ export default { ...@@ -292,7 +284,7 @@ export default {
v-else v-else
:merge-requests="mergeRequests" :merge-requests="mergeRequests"
:page-info="pageInfo" :page-info="pageInfo"
:column-options="getColumnOptions" :column-options="getMetricTypes(chartKeys.timeBasedHistogram)"
:metric-type="columnMetric" :metric-type="columnMetric"
:metric-label="columnMetricLabel" :metric-label="columnMetricLabel"
@columnMetricChange="setColumnMetric" @columnMetricChange="setColumnMetric"
......
...@@ -22,7 +22,7 @@ export default { ...@@ -22,7 +22,7 @@ export default {
required: true, required: true,
}, },
columnOptions: { columnOptions: {
type: Object, type: Array,
required: true, required: true,
}, },
metricType: { metricType: {
...@@ -36,7 +36,7 @@ export default { ...@@ -36,7 +36,7 @@ export default {
}, },
computed: { computed: {
metricDropdownLabel() { metricDropdownLabel() {
return this.columnOptions[this.metricType]; return this.columnOptions.find(option => option.key === this.metricType).label;
}, },
showPagination() { showPagination() {
return this.pageInfo && this.pageInfo.total; return this.pageInfo && this.pageInfo.total;
...@@ -72,21 +72,21 @@ export default { ...@@ -72,21 +72,21 @@ export default {
:text="metricDropdownLabel" :text="metricDropdownLabel"
> >
<gl-dropdown-item <gl-dropdown-item
v-for="(value, key) in columnOptions" v-for="option in columnOptions"
:key="key" :key="option.key"
active-class="is-active" active-class="is-active"
class="w-100" class="w-100"
@click="$emit('columnMetricChange', key)" @click="$emit('columnMetricChange', option.key)"
> >
<span class="d-flex"> <span class="d-flex">
<icon <icon
class="flex-shrink-0 append-right-4" class="flex-shrink-0 append-right-4"
:class="{ :class="{
invisible: !isSelectedMetric(key), invisible: !isSelectedMetric(option.key),
}" }"
name="mobile-issue-close" name="mobile-issue-close"
/> />
{{ value }} {{ option.label }}
</span> </span>
</gl-dropdown-item> </gl-dropdown-item>
</gl-dropdown> </gl-dropdown>
......
...@@ -71,8 +71,8 @@ export default { ...@@ -71,8 +71,8 @@ export default {
</div> </div>
<div class="table-section section-50 d-flex flex-row align-items-start qa-mr-metrics"> <div class="table-section section-50 d-flex flex-row align-items-start qa-mr-metrics">
<metric-column <metric-column
type="time_to_merge" type="days_to_merge"
:value="mergeRequest.time_to_merge" :value="mergeRequest.days_to_merge"
:label="__('Time to merge')" :label="__('Time to merge')"
/> />
<metric-column :type="metricType" :value="selectedMetric" :label="metricLabel" /> <metric-column :type="metricType" :value="selectedMetric" :label="metricLabel" />
......
...@@ -45,17 +45,6 @@ export const metricTypes = [ ...@@ -45,17 +45,6 @@ export const metricTypes = [
}, },
]; ];
export const tableSortFields = metricTypes.reduce(
(acc, curr) => {
const { key, label, chart } = curr;
if (chart === chartKeys.timeBasedHistogram) {
acc[key] = label;
}
return acc;
},
{ days_to_merge: __('Days to merge') },
);
export const tableSortOrder = { export const tableSortOrder = {
asc: { asc: {
title: s__('ProductivityAnalytics|Ascending'), title: s__('ProductivityAnalytics|Ascending'),
...@@ -69,7 +58,10 @@ export const tableSortOrder = { ...@@ -69,7 +58,10 @@ export const tableSortOrder = {
}, },
}; };
export const timeToMergeMetric = 'time_to_merge'; export const daysToMergeMetric = {
key: 'days_to_merge',
label: s__('ProductivityAnalytics|Days to merge'),
};
export const defaultMaxColumnChartItemsPerPage = 20; export const defaultMaxColumnChartItemsPerPage = 20;
......
export const getMetricTypes = state => chartKey =>
state.metricTypes.filter(m => m.chart === chartKey);
// prevent babel-plugin-rewire from generating an invalid default during karma tests
export default () => {};
import Vue from 'vue'; import Vue from 'vue';
import Vuex from 'vuex'; import Vuex from 'vuex';
import state from './state'; import state from './state';
import * as getters from './getters';
import * as actions from './actions'; import * as actions from './actions';
import mutations from './mutations'; import mutations from './mutations';
import filters from './modules/filters/index'; import filters from './modules/filters/index';
...@@ -12,6 +13,7 @@ Vue.use(Vuex); ...@@ -12,6 +13,7 @@ Vue.use(Vuex);
const createStore = () => const createStore = () =>
new Vuex.Store({ new Vuex.Store({
state: state(), state: state(),
getters,
actions, actions,
mutations, mutations,
modules: { modules: {
......
import axios from '~/lib/utils/axios_utils'; import axios from '~/lib/utils/axios_utils';
import * as types from './mutation_types'; import * as types from './mutation_types';
import { parseIntPagination, normalizeHeaders } from '~/lib/utils/common_utils'; import { parseIntPagination, normalizeHeaders } from '~/lib/utils/common_utils';
import { timeToMergeMetric } from '../../../constants'; import { daysToMergeMetric } from '../../../constants';
export const fetchMergeRequests = ({ dispatch, state, rootState, rootGetters }) => { export const fetchMergeRequests = ({ dispatch, state, rootState, rootGetters }) => {
dispatch('requestMergeRequests'); dispatch('requestMergeRequests');
...@@ -41,8 +41,8 @@ export const receiveMergeRequestsError = ({ commit }, { response }) => { ...@@ -41,8 +41,8 @@ export const receiveMergeRequestsError = ({ commit }, { response }) => {
export const setSortField = ({ commit, dispatch }, data) => { export const setSortField = ({ commit, dispatch }, data) => {
commit(types.SET_SORT_FIELD, data); commit(types.SET_SORT_FIELD, data);
// let's make sure we update the column that we sort on (except for 'time_to_merge') // let's make sure we update the column that we sort on (except for 'days_to_merge')
if (data !== timeToMergeMetric) { if (data !== daysToMergeMetric.key) {
dispatch('setColumnMetric', data); dispatch('setColumnMetric', data);
} }
......
import httpStatus from '~/lib/utils/http_status'; import httpStatus from '~/lib/utils/http_status';
import { tableSortOrder } from './../../../constants'; import { chartKeys, tableSortOrder, daysToMergeMetric } from '../../../constants';
export const sortIcon = state => tableSortOrder[state.sortOrder].icon; export const sortIcon = state => tableSortOrder[state.sortOrder].icon;
export const sortTooltipTitle = state => tableSortOrder[state.sortOrder].title; export const sortTooltipTitle = state => tableSortOrder[state.sortOrder].title;
export const sortFieldDropdownLabel = state => state.sortFields[state.sortField]; export const sortFieldDropdownLabel = (state, _, rootState) =>
rootState.metricTypes.find(metric => metric.key === state.sortField).label;
export const getColumnOptions = state => export const tableSortOptions = (_state, _getters, _rootState, rootGetters) => [
Object.keys(state.sortFields) daysToMergeMetric,
.filter(key => key !== 'time_to_merge') ...rootGetters.getMetricTypes(chartKeys.timeBasedHistogram),
.reduce((obj, key) => { ];
const result = { ...obj, [key]: state.sortFields[key] };
return result;
}, {});
export const columnMetricLabel = (state, getters) => getters.getColumnOptions[state.columnMetric]; export const columnMetricLabel = (state, _getters, _rootState, rootGetters) =>
rootGetters
.getMetricTypes(chartKeys.timeBasedHistogram)
.find(metric => metric.key === state.columnMetric).label;
export const isSelectedSortField = state => sortField => state.sortField === sortField; export const isSelectedSortField = state => sortField => state.sortField === sortField;
......
import { tableSortFields, tableSortOrder } from './../../../constants'; import { tableSortOrder } from '../../../constants';
export default () => ({ export default () => ({
isLoadingTable: false, isLoadingTable: false,
...@@ -6,7 +6,6 @@ export default () => ({ ...@@ -6,7 +6,6 @@ export default () => ({
mergeRequests: [], mergeRequests: [],
pageInfo: {}, pageInfo: {},
sortOrder: tableSortOrder.asc.value, sortOrder: tableSortOrder.asc.value,
sortFields: tableSortFields,
sortField: 'time_to_merge', sortField: 'time_to_merge',
columnMetric: 'time_to_first_comment', columnMetric: 'time_to_first_comment',
}); });
import { metricTypes } from '../constants';
export default () => ({ export default () => ({
endpoint: null, endpoint: null,
metricTypes,
}); });
...@@ -60,8 +60,8 @@ exports[`MergeRequestTableRow component on creation matches the snapshot 1`] = ` ...@@ -60,8 +60,8 @@ exports[`MergeRequestTableRow component on creation matches the snapshot 1`] = `
> >
<metriccolumn-stub <metriccolumn-stub
label="Time to merge" label="Time to merge"
type="time_to_merge" type="days_to_merge"
value="0" value="24"
/> />
<metriccolumn-stub <metriccolumn-stub
......
...@@ -65,12 +65,12 @@ describe('MergeRequestTableRow component', () => { ...@@ -65,12 +65,12 @@ describe('MergeRequestTableRow component', () => {
expect(findMetricColumns().length).toBe(2); expect(findMetricColumns().length).toBe(2);
}); });
it('renders the "Time to merge" metric column', () => { it('renders the "Time to merge" metric column with the "days_to_merge" metric', () => {
expect( expect(
findMetricColumns() findMetricColumns()
.at(0) .at(0)
.props('value'), .props('value'),
).toBe(defaultProps.mergeRequest.time_to_merge); ).toBe(defaultProps.mergeRequest.days_to_merge);
}); });
}); });
}); });
......
...@@ -9,11 +9,11 @@ describe('MergeRequestTable component', () => { ...@@ -9,11 +9,11 @@ describe('MergeRequestTable component', () => {
const defaultProps = { const defaultProps = {
mergeRequests: mockMergeRequests, mergeRequests: mockMergeRequests,
columnOptions: { columnOptions: [
time_to_first_comment: 'Time from first commit until first comment', { key: 'time_to_first_comment', label: 'Time from first commit until first comment' },
time_to_last_commit: 'Time from first comment to last commit', { key: 'time_to_last_commit', label: 'Time from first comment to last commit' },
time_to_merge: 'Time from last commit to merge', { key: 'time_to_merge', label: 'Time from last commit to merge' },
}, ],
metricType: 'time_to_last_commit', metricType: 'time_to_last_commit',
metricLabel: 'Time from first comment to last commit', metricLabel: 'Time from first comment to last commit',
pageInfo: {}, pageInfo: {},
......
...@@ -197,12 +197,12 @@ describe('Productivity analytics table actions', () => { ...@@ -197,12 +197,12 @@ describe('Productivity analytics table actions', () => {
done, done,
)); ));
it('should not dispatch setColumnMetric when metric is "time_to_merge"', done => it('should not dispatch setColumnMetric when metric is "days_to_merge"', done =>
testAction( testAction(
actions.setSortField, actions.setSortField,
'time_to_merge', 'days_to_merge',
mockedContext.state, mockedContext.state,
[{ type: types.SET_SORT_FIELD, payload: 'time_to_merge' }], [{ type: types.SET_SORT_FIELD, payload: 'days_to_merge' }],
[{ type: 'fetchMergeRequests' }], [{ type: 'fetchMergeRequests' }],
done, done,
)); ));
......
...@@ -5,6 +5,12 @@ import { tableSortOrder } from 'ee/analytics/productivity_analytics/constants'; ...@@ -5,6 +5,12 @@ import { tableSortOrder } from 'ee/analytics/productivity_analytics/constants';
describe('Productivity analytics table getters', () => { describe('Productivity analytics table getters', () => {
let state; let state;
const metricTypes = [
{ key: 'time_to_first_comment', label: 'Time from first commit until first comment' },
{ key: 'time_to_last_commit', label: 'Time from first comment to last commit' },
{ key: 'time_to_merge', label: 'Time from last commit to merge' },
];
beforeEach(() => { beforeEach(() => {
state = createState(); state = createState();
}); });
...@@ -47,26 +53,27 @@ describe('Productivity analytics table getters', () => { ...@@ -47,26 +53,27 @@ describe('Productivity analytics table getters', () => {
describe('sortFieldDropdownLabel', () => { describe('sortFieldDropdownLabel', () => {
it('returns the correct label for the current sort field', () => { it('returns the correct label for the current sort field', () => {
const rootState = {
metricTypes,
};
state.sortField = 'time_to_last_commit'; state.sortField = 'time_to_last_commit';
expect(getters.sortFieldDropdownLabel(state)).toBe('Time from first comment to last commit'); expect(getters.sortFieldDropdownLabel(state, null, rootState)).toBe(
'Time from first comment to last commit',
);
}); });
}); });
describe('getColumnOptions', () => { describe('tableSortOptions', () => {
it('returns an object of key/value pairs with the available column options', () => { it('returns the metricTypes from the timeBasedHistogram and adds "Days to merge"', () => {
state.sortFields = { const rootGetters = {
time_to_first_comment: 'Time from first commit until first comment', getMetricTypes: () => metricTypes,
time_to_last_commit: 'Time from first comment to last commit',
time_to_merge: 'Time from last commit to merge',
days_to_merge: 'Days to merge',
}; };
expect(getters.getColumnOptions(state)).toEqual({ const expected = [{ key: 'days_to_merge', label: 'Days to merge' }, ...metricTypes];
days_to_merge: 'Days to merge',
time_to_first_comment: 'Time from first commit until first comment', expect(getters.tableSortOptions(null, null, null, rootGetters)).toEqual(expected);
time_to_last_commit: 'Time from first comment to last commit',
});
}); });
}); });
......
...@@ -4656,9 +4656,6 @@ msgstr "" ...@@ -4656,9 +4656,6 @@ msgstr ""
msgid "Days" msgid "Days"
msgstr "" msgstr ""
msgid "Days to merge"
msgstr ""
msgid "Debug" msgid "Debug"
msgstr "" msgstr ""
...@@ -11467,6 +11464,9 @@ msgstr "" ...@@ -11467,6 +11464,9 @@ msgstr ""
msgid "ProductivityAnalytics|Ascending" msgid "ProductivityAnalytics|Ascending"
msgstr "" msgstr ""
msgid "ProductivityAnalytics|Days to merge"
msgstr ""
msgid "ProductivityAnalytics|Descending" msgid "ProductivityAnalytics|Descending"
msgstr "" 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