Commit 7e7dac36 authored by Ezekiel Kigbo's avatar Ezekiel Kigbo

Revert back to GlDropdown

Minor cleanup tests including removing
unused refs, returning promises instead
of using a done callback etc

Move toggle label ids function to utils

Address minor review comments
parent 6ce2b93c
...@@ -2,8 +2,8 @@ ...@@ -2,8 +2,8 @@
import { import {
GlDropdownDivider, GlDropdownDivider,
GlSegmentedControl, GlSegmentedControl,
GlNewDropdown, GlDropdown,
GlNewDropdownItem, GlDropdownItem,
GlSearchBoxByType, GlSearchBoxByType,
GlIcon, GlIcon,
} from '@gitlab/ui'; } from '@gitlab/ui';
...@@ -23,8 +23,8 @@ export default { ...@@ -23,8 +23,8 @@ export default {
GlSegmentedControl, GlSegmentedControl,
GlDropdownDivider, GlDropdownDivider,
GlIcon, GlIcon,
GlNewDropdown, GlDropdown,
GlNewDropdownItem, GlDropdownItem,
GlSearchBoxByType, GlSearchBoxByType,
}, },
props: { props: {
...@@ -132,13 +132,16 @@ export default { ...@@ -132,13 +132,16 @@ export default {
<p>{{ selectedFiltersText }}</p> <p>{{ selectedFiltersText }}</p>
</div> </div>
<div class="flex-column"> <div class="flex-column">
<gl-new-dropdown <gl-dropdown
icon="settings"
aria-expanded="false" aria-expanded="false"
:aria-label="__('CycleAnalytics|Display chart filters')" :aria-label="__('CycleAnalytics|Display chart filters')"
right right
> >
<div ref="subjectFilter" class="js-tasks-by-type-chart-filters-subject mb-3 px-3"> <template #button-content>
<gl-icon class="vertical-align-top" name="settings" />
<gl-icon name="chevron-down" />
</template>
<div class="mb-3 px-3">
<p class="font-weight-bold text-left mb-2">{{ s__('CycleAnalytics|Show') }}</p> <p class="font-weight-bold text-left mb-2">{{ s__('CycleAnalytics|Show') }}</p>
<gl-segmented-control <gl-segmented-control
v-model="selectedSubjectFilter" v-model="selectedSubjectFilter"
...@@ -150,16 +153,13 @@ export default { ...@@ -150,16 +153,13 @@ export default {
/> />
</div> </div>
<gl-dropdown-divider /> <gl-dropdown-divider />
<div ref="labelsFilter" class="js-tasks-by-type-chart-filters-labels mb-3 px-3"> <div class="mb-3 px-3">
<p class="font-weight-bold text-left my-2"> <p class="font-weight-bold text-left my-2">
{{ s__('CycleAnalytics|Select labels') }} {{ s__('CycleAnalytics|Select labels') }}
<br /><small>{{ selectedLabelLimitText }}</small>
</p> </p>
<small>{{ selectedLabelLimitText }}</small> <gl-search-box-by-type v-model.trim="labelsSearchTerm" class="mb-2" />
<gl-search-box-by-type <gl-dropdown-item
v-model.trim="labelsSearchTerm"
class="js-tasks-by-type-chart-filters-subject mb-2"
/>
<gl-new-dropdown-item
v-for="label in availableLabels" v-for="label in availableLabels"
:key="label.id" :key="label.id"
:disabled="isLabelDisabled(label.id)" :disabled="isLabelDisabled(label.id)"
...@@ -179,12 +179,12 @@ export default { ...@@ -179,12 +179,12 @@ export default {
class="d-inline-block dropdown-label-box" class="d-inline-block dropdown-label-box"
></span> ></span>
{{ label.name }} {{ label.name }}
</gl-new-dropdown-item> </gl-dropdown-item>
<div v-show="!hasMatchingLabels" class="text-secondary"> <div v-show="!hasMatchingLabels" class="text-secondary">
{{ __('No matching labels') }} {{ __('No matching labels') }}
</div> </div>
</div> </div>
</gl-new-dropdown> </gl-dropdown>
</div> </div>
</div> </div>
</template> </template>
...@@ -279,9 +279,7 @@ export const fetchTopRankedGroupLabels = ({ ...@@ -279,9 +279,7 @@ export const fetchTopRankedGroupLabels = ({
}, },
}) => { }) => {
dispatch('requestTopRankedGroupLabels'); dispatch('requestTopRankedGroupLabels');
const { const { subject } = state.tasksByType;
tasksByType: { subject },
} = state;
return Api.cycleAnalyticsTopLabels({ return Api.cycleAnalyticsTopLabels({
subject, subject,
...@@ -403,22 +401,21 @@ export const fetchTasksByTypeData = ({ dispatch, state, getters }) => { ...@@ -403,22 +401,21 @@ export const fetchTasksByTypeData = ({ dispatch, state, getters }) => {
const { const {
currentGroupPath, currentGroupPath,
cycleAnalyticsRequestParams: { created_after, created_before, project_ids }, cycleAnalyticsRequestParams: { created_after, created_before, project_ids },
topRankedLabelIds,
} = getters; } = getters;
const { const {
tasksByType: { subject }, tasksByType: { subject, selectedLabelIds },
} = state; } = state;
// dont request if we have no labels selected...for now // dont request if we have no labels selected...for now
if (topRankedLabelIds.length) { if (selectedLabelIds.length) {
const params = { const params = {
group_id: currentGroupPath, group_id: currentGroupPath,
created_after, created_after,
created_before, created_before,
project_ids, project_ids,
subject, subject,
label_ids: topRankedLabelIds, label_ids: selectedLabelIds,
}; };
dispatch('requestTasksByTypeData'); dispatch('requestTasksByTypeData');
......
...@@ -11,9 +11,6 @@ export const currentGroupPath = ({ selectedGroup }) => ...@@ -11,9 +11,6 @@ export const currentGroupPath = ({ selectedGroup }) =>
export const selectedProjectIds = ({ selectedProjects }) => export const selectedProjectIds = ({ selectedProjects }) =>
selectedProjects.length ? selectedProjects.map(({ id }) => id) : []; selectedProjects.length ? selectedProjects.map(({ id }) => id) : [];
export const topRankedLabelIds = ({ topRankedLabels }) =>
topRankedLabels.length ? topRankedLabels.map(({ id }) => id) : [];
export const cycleAnalyticsRequestParams = ({ startDate = null, endDate = null }, getters) => ({ export const cycleAnalyticsRequestParams = ({ startDate = null, endDate = null }, getters) => ({
project_ids: getters.selectedProjectIds, project_ids: getters.selectedProjectIds,
created_after: startDate ? dateFormat(startDate, dateFormats.isoDate) : null, created_after: startDate ? dateFormat(startDate, dateFormats.isoDate) : null,
......
import { convertObjectPropsToCamelCase } from '~/lib/utils/common_utils'; import { convertObjectPropsToCamelCase } from '~/lib/utils/common_utils';
import * as types from './mutation_types'; import * as types from './mutation_types';
import { transformRawStages, transformRawTasksByTypeData } from '../utils'; import { transformRawStages, transformRawTasksByTypeData, toggleSelectedLabel } from '../utils';
import { TASKS_BY_TYPE_FILTERS } from '../constants'; import { TASKS_BY_TYPE_FILTERS } from '../constants';
export default { export default {
...@@ -74,10 +74,10 @@ export default { ...@@ -74,10 +74,10 @@ export default {
}, },
[types.RECEIVE_TOP_RANKED_GROUP_LABELS_SUCCESS](state, data = []) { [types.RECEIVE_TOP_RANKED_GROUP_LABELS_SUCCESS](state, data = []) {
const { tasksByType } = state; const { tasksByType } = state;
state.topRankedLabels = data.length ? data.map(convertObjectPropsToCamelCase) : []; state.topRankedLabels = data.map(convertObjectPropsToCamelCase);
state.tasksByType = { state.tasksByType = {
...tasksByType, ...tasksByType,
selectedLabelIds: data.length ? data.map(({ id }) => id) : [], selectedLabelIds: data.map(({ id }) => id),
}; };
}, },
[types.RECEIVE_TOP_RANKED_GROUP_LABELS_ERROR](state) { [types.RECEIVE_TOP_RANKED_GROUP_LABELS_ERROR](state) {
...@@ -232,9 +232,7 @@ export default { ...@@ -232,9 +232,7 @@ export default {
switch (filter) { switch (filter) {
case TASKS_BY_TYPE_FILTERS.LABEL: case TASKS_BY_TYPE_FILTERS.LABEL:
updatedFilter = { updatedFilter = {
selectedLabelIds: selectedLabelIds.includes(value) selectedLabelIds: toggleSelectedLabel({ selectedLabelIds, value }),
? selectedLabelIds.filter(v => v !== value)
: [...selectedLabelIds, value],
}; };
break; break;
case TASKS_BY_TYPE_FILTERS.SUBJECT: case TASKS_BY_TYPE_FILTERS.SUBJECT:
......
...@@ -25,6 +25,13 @@ export const removeFlash = (type = 'alert') => { ...@@ -25,6 +25,13 @@ export const removeFlash = (type = 'alert') => {
} }
}; };
export const toggleSelectedLabel = ({ selectedLabelIds = [], value = null }) => {
if (!value) return selectedLabelIds;
return selectedLabelIds.includes(value)
? selectedLabelIds.filter(v => v !== value)
: [...selectedLabelIds, value];
};
export const isStartEvent = ev => Boolean(ev) && Boolean(ev.canBeStartEvent) && ev.canBeStartEvent; export const isStartEvent = ev => Boolean(ev) && Boolean(ev.canBeStartEvent) && ev.canBeStartEvent;
export const eventToOption = (obj = null) => { export const eventToOption = (obj = null) => {
......
...@@ -419,14 +419,13 @@ describe('Cycle Analytics component', () => { ...@@ -419,14 +419,13 @@ describe('Cycle Analytics component', () => {
}); });
describe('with failed requests while loading', () => { describe('with failed requests while loading', () => {
function mockRequestCycleAnalyticsData({ const mockRequestCycleAnalyticsData = ({
overrides = {}, overrides = {},
mockFetchStageData = true, mockFetchStageData = true,
mockFetchStageMedian = true, mockFetchStageMedian = true,
mockFetchDurationData = true, mockFetchDurationData = true,
mockFetchTasksByTypeData = true, mockFetchTasksByTypeData = true,
mockFetchTasksByTypeTopLabelsData = true, }) => {
}) {
const defaultStatus = 200; const defaultStatus = 200;
const defaultRequests = { const defaultRequests = {
fetchSummaryData: { fetchSummaryData: {
...@@ -447,18 +446,16 @@ describe('Cycle Analytics component', () => { ...@@ -447,18 +446,16 @@ describe('Cycle Analytics component', () => {
...overrides, ...overrides,
}; };
mock
.onGet(mockData.endpoints.tasksByTypeTopLabelsData)
.reply(defaultStatus, mockData.groupLabels);
if (mockFetchTasksByTypeData) { if (mockFetchTasksByTypeData) {
mock mock
.onGet(mockData.endpoints.tasksByTypeData) .onGet(mockData.endpoints.tasksByTypeData)
.reply(defaultStatus, { ...mockData.tasksByTypeData }); .reply(defaultStatus, { ...mockData.tasksByTypeData });
} }
if (mockFetchTasksByTypeTopLabelsData) {
mock
.onGet(mockData.endpoints.tasksByTypeTopLabelsData)
.reply(defaultStatus, mockData.groupLabels);
}
if (mockFetchDurationData) { if (mockFetchDurationData) {
mock mock
.onGet(mockData.endpoints.durationData) .onGet(mockData.endpoints.durationData)
...@@ -476,7 +473,7 @@ describe('Cycle Analytics component', () => { ...@@ -476,7 +473,7 @@ describe('Cycle Analytics component', () => {
Object.values(defaultRequests).forEach(({ endpoint, status, response }) => { Object.values(defaultRequests).forEach(({ endpoint, status, response }) => {
mock.onGet(endpoint).replyOnce(status, response); mock.onGet(endpoint).replyOnce(status, response);
}); });
} };
beforeEach(() => { beforeEach(() => {
setFixtures('<div class="flash-container"></div>'); setFixtures('<div class="flash-container"></div>');
......
import { shallowMount, mount } from '@vue/test-utils'; import { shallowMount, mount } from '@vue/test-utils';
import { GlNewDropdownItem, GlSegmentedControl } from '@gitlab/ui'; import { GlDropdownItem, GlSegmentedControl } from '@gitlab/ui';
import TasksByTypeFilters from 'ee/analytics/cycle_analytics/components/tasks_by_type_filters.vue'; import TasksByTypeFilters from 'ee/analytics/cycle_analytics/components/tasks_by_type_filters.vue';
import { import {
TASKS_BY_TYPE_SUBJECT_ISSUE, TASKS_BY_TYPE_SUBJECT_ISSUE,
TASKS_BY_TYPE_SUBJECT_MERGE_REQUEST, TASKS_BY_TYPE_SUBJECT_MERGE_REQUEST,
TASKS_BY_TYPE_FILTERS, TASKS_BY_TYPE_FILTERS,
} from 'ee/analytics/cycle_analytics/constants'; } from 'ee/analytics/cycle_analytics/constants';
import { shouldFlashAMessage } from '../helpers';
import { groupLabels } from '../mock_data'; import { groupLabels } from '../mock_data';
const selectedLabelIds = [groupLabels[0].id]; const selectedLabelIds = [groupLabels[0].id];
const findSubjectFilters = ctx => const findSubjectFilters = ctx => ctx.find(GlSegmentedControl);
ctx.find('.js-tasks-by-type-chart-filters-subject').find(GlSegmentedControl);
const findSelectedSubjectFilters = ctx => findSubjectFilters(ctx).attributes('checked'); const findSelectedSubjectFilters = ctx => findSubjectFilters(ctx).attributes('checked');
const findDropdownLabels = ctx => const findDropdownLabels = ctx => ctx.findAll(GlDropdownItem);
ctx.find('.js-tasks-by-type-chart-filters-labels').findAll(GlNewDropdownItem);
const shouldFlashAMessage = (msg = '') =>
expect(document.querySelector('.flash-container .flash-text').innerText.trim()).toBe(msg);
const selectLabelAtIndex = (ctx, index) => { const selectLabelAtIndex = (ctx, index) => {
findDropdownLabels(ctx) findDropdownLabels(ctx)
...@@ -26,9 +22,8 @@ const selectLabelAtIndex = (ctx, index) => { ...@@ -26,9 +22,8 @@ const selectLabelAtIndex = (ctx, index) => {
return ctx.vm.$nextTick(); return ctx.vm.$nextTick();
}; };
function createComponent({ props = {}, shallow = true }) { function createComponent({ props = {}, mountFn = shallowMount }) {
const fn = shallow ? shallowMount : mount; return mountFn(TasksByTypeFilters, {
return fn(TasksByTypeFilters, {
propsData: { propsData: {
selectedLabelIds, selectedLabelIds,
labels: groupLabels, labels: groupLabels,
...@@ -37,7 +32,7 @@ function createComponent({ props = {}, shallow = true }) { ...@@ -37,7 +32,7 @@ function createComponent({ props = {}, shallow = true }) {
}, },
stubs: { stubs: {
GlNewDropdown: true, GlNewDropdown: true,
GlNewDropdownItem: true, GlDropdownItem: true,
}, },
}); });
} }
...@@ -59,11 +54,11 @@ describe('TasksByTypeFilters', () => { ...@@ -59,11 +54,11 @@ describe('TasksByTypeFilters', () => {
}); });
it('emits the `updateFilter` event when a subject label is clicked', () => { it('emits the `updateFilter` event when a subject label is clicked', () => {
expect(wrapper.emitted().updateFilter).toBeUndefined(); expect(wrapper.emitted('updateFilter')).toBeUndefined();
return selectLabelAtIndex(wrapper, 0).then(() => { return selectLabelAtIndex(wrapper, 0).then(() => {
expect(wrapper.emitted().updateFilter).toBeDefined(); expect(wrapper.emitted('updateFilter')).toBeDefined();
expect(wrapper.emitted().updateFilter[0]).toEqual([ expect(wrapper.emitted('updateFilter')[0]).toEqual([
{ filter: TASKS_BY_TYPE_FILTERS.LABEL, value: groupLabels[0].id }, { filter: TASKS_BY_TYPE_FILTERS.LABEL, value: groupLabels[0].id },
]); ]);
}); });
...@@ -107,7 +102,7 @@ describe('TasksByTypeFilters', () => { ...@@ -107,7 +102,7 @@ describe('TasksByTypeFilters', () => {
}); });
it('should not allow selecting another label', () => { it('should not allow selecting another label', () => {
expect(wrapper.emitted().updateFilter).toBeUndefined(); expect(wrapper.emitted('updateFilter')).toBeUndefined();
}); });
it('should display a message', () => { it('should display a message', () => {
...@@ -122,8 +117,8 @@ describe('TasksByTypeFilters', () => { ...@@ -122,8 +117,8 @@ describe('TasksByTypeFilters', () => {
}); });
it('emits the `updateFilter` event when a subject filter is clicked', () => { it('emits the `updateFilter` event when a subject filter is clicked', () => {
wrapper = createComponent({ shallow: false }); wrapper = createComponent({ mountFn: mount });
expect(wrapper.emitted().updateFilter).toBeUndefined(); expect(wrapper.emitted('updateFilter')).toBeUndefined();
findSubjectFilters(wrapper) findSubjectFilters(wrapper)
.findAll('label:not(.active)') .findAll('label:not(.active)')
...@@ -131,8 +126,8 @@ describe('TasksByTypeFilters', () => { ...@@ -131,8 +126,8 @@ describe('TasksByTypeFilters', () => {
.trigger('click'); .trigger('click');
return wrapper.vm.$nextTick(() => { return wrapper.vm.$nextTick(() => {
expect(wrapper.emitted().updateFilter).toBeDefined(); expect(wrapper.emitted('updateFilter')).toBeDefined();
expect(wrapper.emitted().updateFilter[0]).toEqual([ expect(wrapper.emitted('updateFilter')[0]).toEqual([
{ {
filter: TASKS_BY_TYPE_FILTERS.SUBJECT, filter: TASKS_BY_TYPE_FILTERS.SUBJECT,
value: TASKS_BY_TYPE_SUBJECT_MERGE_REQUEST, value: TASKS_BY_TYPE_SUBJECT_MERGE_REQUEST,
......
...@@ -14,6 +14,10 @@ export function renderTotalTime(selector, element, totalTime = {}) { ...@@ -14,6 +14,10 @@ export function renderTotalTime(selector, element, totalTime = {}) {
} }
} }
export const shouldFlashAMessage = (msg = '') =>
expect(document.querySelector('.flash-container .flash-text').innerText.trim()).toBe(msg);
export default { export default {
renderTotalTime, renderTotalTime,
shouldFlashAMessage,
}; };
...@@ -27,6 +27,7 @@ import { ...@@ -27,6 +27,7 @@ import {
transformedDurationMedianData, transformedDurationMedianData,
endpoints, endpoints,
} from '../mock_data'; } from '../mock_data';
import { shouldFlashAMessage } from '../helpers';
const stageData = { events: [] }; const stageData = { events: [] };
const error = new Error(`Request failed with status code ${httpStatusCodes.NOT_FOUND}`); const error = new Error(`Request failed with status code ${httpStatusCodes.NOT_FOUND}`);
...@@ -41,10 +42,6 @@ describe('Cycle analytics actions', () => { ...@@ -41,10 +42,6 @@ describe('Cycle analytics actions', () => {
let state; let state;
let mock; let mock;
function shouldFlashAMessage(msg = flashErrorMessage) {
expect(document.querySelector('.flash-container .flash-text').innerText.trim()).toBe(msg);
}
function shouldSetUrlParams({ action, payload, result }) { function shouldSetUrlParams({ action, payload, result }) {
const store = { const store = {
state, state,
...@@ -338,7 +335,7 @@ describe('Cycle analytics actions', () => { ...@@ -338,7 +335,7 @@ describe('Cycle analytics actions', () => {
describe('fetchTopRankedGroupLabels', () => { describe('fetchTopRankedGroupLabels', () => {
beforeEach(() => { beforeEach(() => {
gon.api_version = 'v4'; gon.api_version = 'v4';
state = { selectedGroup, tasksByType: { subject: TASKS_BY_TYPE_SUBJECT_ISSUE } }; state = { selectedGroup, tasksByType: { subject: TASKS_BY_TYPE_SUBJECT_ISSUE }, ...getters };
}); });
describe('succeeds', () => { describe('succeeds', () => {
...@@ -347,21 +344,16 @@ describe('Cycle analytics actions', () => { ...@@ -347,21 +344,16 @@ describe('Cycle analytics actions', () => {
}); });
it('dispatches receiveTopRankedGroupLabelsSuccess if the request succeeds', () => { it('dispatches receiveTopRankedGroupLabelsSuccess if the request succeeds', () => {
const dispatch = jest.fn(); return testAction(
actions.fetchTopRankedGroupLabels,
return actions null,
.fetchTopRankedGroupLabels({ state,
dispatch, [],
state, [
getters, { type: 'requestTopRankedGroupLabels' },
}) { type: 'receiveTopRankedGroupLabelsSuccess', payload: groupLabels },
.then(() => { ],
expect(dispatch).toHaveBeenCalledWith('requestTopRankedGroupLabels'); );
expect(dispatch).toHaveBeenCalledWith(
'receiveTopRankedGroupLabelsSuccess',
groupLabels,
);
});
}); });
}); });
...@@ -371,18 +363,16 @@ describe('Cycle analytics actions', () => { ...@@ -371,18 +363,16 @@ describe('Cycle analytics actions', () => {
}); });
it('dispatches receiveTopRankedGroupLabelsError if the request fails', () => { it('dispatches receiveTopRankedGroupLabelsError if the request fails', () => {
const dispatch = jest.fn(); return testAction(
actions.fetchTopRankedGroupLabels,
return actions null,
.fetchTopRankedGroupLabels({ state,
dispatch, [],
state, [
getters, { type: 'requestTopRankedGroupLabels' },
}) { type: 'receiveTopRankedGroupLabelsError', payload: error },
.then(() => { ],
expect(dispatch).toHaveBeenCalledWith('requestTopRankedGroupLabels'); );
expect(dispatch).toHaveBeenCalledWith('receiveTopRankedGroupLabelsError', error);
});
}); });
}); });
...@@ -628,7 +618,7 @@ describe('Cycle analytics actions', () => { ...@@ -628,7 +618,7 @@ describe('Cycle analytics actions', () => {
{}, {},
); );
shouldFlashAMessage(); shouldFlashAMessage(flashErrorMessage);
}); });
}); });
}); });
...@@ -681,7 +671,7 @@ describe('Cycle analytics actions', () => { ...@@ -681,7 +671,7 @@ describe('Cycle analytics actions', () => {
{ response }, { response },
); );
shouldFlashAMessage(); shouldFlashAMessage(flashErrorMessage);
}); });
}); });
...@@ -741,7 +731,7 @@ describe('Cycle analytics actions', () => { ...@@ -741,7 +731,7 @@ describe('Cycle analytics actions', () => {
); );
}); });
shouldFlashAMessage(); shouldFlashAMessage(flashErrorMessage);
}); });
}); });
......
...@@ -8,7 +8,6 @@ import { ...@@ -8,7 +8,6 @@ import {
durationChartPlottableMedianData, durationChartPlottableMedianData,
allowedStages, allowedStages,
selectedProjects, selectedProjects,
groupLabels,
} from '../mock_data'; } from '../mock_data';
let state = null; let state = null;
...@@ -38,7 +37,7 @@ describe('Cycle analytics getters', () => { ...@@ -38,7 +37,7 @@ describe('Cycle analytics getters', () => {
selectedProjects, selectedProjects,
}; };
expect(getters.selectedProjectIds(state)).toEqual(selectedProjects.map(({ id }) => id)); expect(getters.selectedProjectIds(state)).toEqual([1, 2]);
}); });
}); });
...@@ -50,25 +49,6 @@ describe('Cycle analytics getters', () => { ...@@ -50,25 +49,6 @@ describe('Cycle analytics getters', () => {
}); });
}); });
describe('topRankedLabelIds', () => {
describe('with topRankedLabels set', () => {
it('returns the `fullPath` value of the group', () => {
state = {
topRankedLabels: groupLabels,
};
expect(getters.topRankedLabelIds(state)).toEqual(groupLabels.map(({ id }) => id));
});
});
describe('without topRankedLabels set', () => {
it('will return an empty array', () => {
state = { topRankedLabels: [] };
expect(getters.topRankedLabelIds(state)).toEqual([]);
});
});
});
describe('currentGroupPath', () => { describe('currentGroupPath', () => {
describe('with selectedGroup set', () => { describe('with selectedGroup set', () => {
it('returns the `fullPath` value of the group', () => { it('returns the `fullPath` value of the group', () => {
......
...@@ -15,6 +15,7 @@ import { ...@@ -15,6 +15,7 @@ import {
getTasksByTypeData, getTasksByTypeData,
flattenTaskByTypeSeries, flattenTaskByTypeSeries,
orderByDate, orderByDate,
toggleSelectedLabel,
} from 'ee/analytics/cycle_analytics/utils'; } from 'ee/analytics/cycle_analytics/utils';
import { toYmd } from 'ee/analytics/shared/utils'; import { toYmd } from 'ee/analytics/shared/utils';
import { import {
...@@ -302,4 +303,19 @@ describe('Cycle analytics utils', () => { ...@@ -302,4 +303,19 @@ describe('Cycle analytics utils', () => {
}); });
}); });
}); });
describe('toggleSelectedLabel', () => {
const selectedLabelIds = [1, 2, 3];
it('will return the array if theres no value given', () => {
expect(toggleSelectedLabel({ selectedLabelIds })).toEqual([1, 2, 3]);
});
it('will remove an id that exists', () => {
expect(toggleSelectedLabel({ selectedLabelIds, value: 2 })).toEqual([1, 3]);
});
it('will add an id that does not exist', () => {
expect(toggleSelectedLabel({ selectedLabelIds, value: 4 })).toEqual([1, 2, 3, 4]);
});
});
}); });
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