Commit ac6ee712 authored by Kushal Pandya's avatar Kushal Pandya

Add sub-group select dropdown for Epic create

Adds support for selecting sub-groups while creating
epic from Group Epic Boards.

Changelod: added
EE: true
parent fb1cd811
<script> <script>
import { mapActions, mapGetters } from 'vuex'; import { mapActions, mapGetters, mapState } from 'vuex';
import BoardNewItem from '~/boards/components/board_new_item.vue'; import BoardNewItem from '~/boards/components/board_new_item.vue';
import { toggleFormEventPrefix } from '~/boards/constants'; import { toggleFormEventPrefix } from '~/boards/constants';
import eventHub from '~/boards/eventhub'; import eventHub from '~/boards/eventhub';
import { fullEpicBoardId } from '../boards_util'; import { fullEpicBoardId } from '../boards_util';
import GroupSelect from './group_select.vue';
export default { export default {
components: { components: {
BoardNewItem, BoardNewItem,
GroupSelect,
}, },
inject: ['boardId'], inject: ['boardId'],
props: { props: {
...@@ -18,6 +21,7 @@ export default { ...@@ -18,6 +21,7 @@ export default {
}, },
}, },
computed: { computed: {
...mapState(['selectedGroup', 'fullPath']),
...mapGetters(['isGroupBoard']), ...mapGetters(['isGroupBoard']),
formEventPrefix() { formEventPrefix() {
return toggleFormEventPrefix.epic; return toggleFormEventPrefix.epic;
...@@ -25,6 +29,9 @@ export default { ...@@ -25,6 +29,9 @@ export default {
formEvent() { formEvent() {
return `${this.formEventPrefix}${this.list.id}`; return `${this.formEventPrefix}${this.list.id}`;
}, },
groupPath() {
return this.selectedGroup?.fullPath ?? this.fullPath;
},
}, },
methods: { methods: {
...mapActions(['addListNewEpic']), ...mapActions(['addListNewEpic']),
...@@ -34,6 +41,7 @@ export default { ...@@ -34,6 +41,7 @@ export default {
title, title,
boardId: fullEpicBoardId(this.boardId), boardId: fullEpicBoardId(this.boardId),
listId: this.list.id, listId: this.list.id,
groupPath: this.groupPath,
}, },
list: this.list, list: this.list,
}).then(() => { }).then(() => {
...@@ -54,5 +62,7 @@ export default { ...@@ -54,5 +62,7 @@ export default {
:submit-button-title="__('Create epic')" :submit-button-title="__('Create epic')"
@form-submit="submit" @form-submit="submit"
@form-cancel="cancel" @form-cancel="cancel"
/> >
<group-select :list="list" />
</board-new-item>
</template> </template>
<script>
import {
GlDropdown,
GlDropdownItem,
GlDropdownText,
GlSearchBoxByType,
GlIntersectionObserver,
GlLoadingIcon,
} from '@gitlab/ui';
import { mapActions, mapState } from 'vuex';
import { s__ } from '~/locale';
import { featureAccessLevel } from '~/pages/projects/shared/permissions/constants';
import { ListType } from '../constants';
export default {
name: 'GroupSelect',
i18n: {
headerTitle: s__(`BoardNewEpic|Groups`),
dropdownText: s__(`BoardNewEpic|Select a group`),
searchPlaceholder: s__(`BoardNewEpic|Search groups`),
emptySearchResult: s__(`BoardNewEpic|No matching results`),
},
defaultFetchOptions: {
with_issues_enabled: true,
with_shared: false,
include_subgroups: true,
order_by: 'similarity',
},
components: {
GlIntersectionObserver,
GlLoadingIcon,
GlDropdown,
GlDropdownItem,
GlDropdownText,
GlSearchBoxByType,
},
inject: ['groupId'],
props: {
list: {
type: Object,
required: true,
},
},
data() {
return {
initialLoading: true,
searchTerm: '',
};
},
computed: {
...mapState(['subGroupsFlags', 'subGroups', 'selectedGroup']),
selectedGroupName() {
return this.selectedGroup.name || s__('BoardNewEpic|Loading groups');
},
fetchOptions() {
const additionalAttrs = {};
if (this.list.type && this.list.type !== ListType.backlog) {
additionalAttrs.min_access_level = featureAccessLevel.EVERYONE;
}
return {
...this.$options.defaultFetchOptions,
...additionalAttrs,
};
},
isFetchResultEmpty() {
return this.subGroups.length === 0;
},
hasNextPage() {
return this.subGroupsFlags.pageInfo?.hasNextPage;
},
},
watch: {
searchTerm() {
this.fetchSubGroups({ search: this.searchTerm });
},
},
async mounted() {
await this.fetchSubGroups();
this.initialLoading = false;
},
methods: {
...mapActions(['fetchSubGroups', 'setSelectedGroup']),
selectGroup(groupId) {
this.setSelectedGroup(this.subGroups.find((group) => group.id === groupId));
},
loadMoreGroups() {
this.fetchSubGroups({ search: this.searchTerm, fetchNext: true });
},
},
};
</script>
<template>
<div>
<label
for="descendant-group-select"
class="gl-font-weight-bold gl-mt-3"
data-testid="header-label"
>{{ $options.i18n.headerTitle }}</label
>
<gl-dropdown
id="descendant-group-select"
data-testid="project-select-dropdown"
:text="selectedGroupName"
:header-text="$options.i18n.headerTitle"
block
menu-class="gl-w-full!"
:loading="initialLoading"
>
<gl-search-box-by-type
v-model.trim="searchTerm"
debounce="250"
:placeholder="$options.i18n.searchPlaceholder"
/>
<gl-dropdown-item
v-for="group in subGroups"
v-show="!subGroupsFlags.isLoading"
:key="group.id"
:name="group.name"
@click="selectGroup(group.id)"
>
{{ group.fullName }}
</gl-dropdown-item>
<gl-dropdown-text v-show="subGroupsFlags.isLoading" data-testid="dropdown-text-loading-icon">
<gl-loading-icon class="gl-mx-auto" size="sm" />
</gl-dropdown-text>
<gl-dropdown-text
v-if="isFetchResultEmpty && !subGroupsFlags.isLoading"
data-testid="empty-result-message"
>
<span class="gl-text-gray-500">{{ $options.i18n.emptySearchResult }}</span>
</gl-dropdown-text>
<gl-intersection-observer v-if="hasNextPage" @appear="loadMoreGroups">
<gl-loading-icon v-if="subGroupsFlags.isLoadingMore" size="md" />
</gl-intersection-observer>
</gl-dropdown>
</div>
</template>
...@@ -11,5 +11,6 @@ mutation CreateEpic($input: BoardEpicCreateInput!) { ...@@ -11,5 +11,6 @@ mutation CreateEpic($input: BoardEpicCreateInput!) {
} }
} }
} }
errors
} }
} }
#import "~/graphql_shared/fragments/pageInfo.fragment.graphql"
fragment Group on Group {
id
name
fullName
fullPath
}
query getSubGroups($fullPath: ID!, $search: String, $after: String) {
group(fullPath: $fullPath) {
...Group
descendantGroups(search: $search, after: $after, first: 100) {
nodes {
...Group
}
pageInfo {
...PageInfo
}
}
}
}
...@@ -31,6 +31,7 @@ import epicMoveListMutation from '../graphql/epic_move_list.mutation.graphql'; ...@@ -31,6 +31,7 @@ import epicMoveListMutation from '../graphql/epic_move_list.mutation.graphql';
import epicsSwimlanesQuery from '../graphql/epics_swimlanes.query.graphql'; import epicsSwimlanesQuery from '../graphql/epics_swimlanes.query.graphql';
import listUpdateLimitMetricsMutation from '../graphql/list_update_limit_metrics.mutation.graphql'; import listUpdateLimitMetricsMutation from '../graphql/list_update_limit_metrics.mutation.graphql';
import listsEpicsQuery from '../graphql/lists_epics.query.graphql'; import listsEpicsQuery from '../graphql/lists_epics.query.graphql';
import subGroupsQuery from '../graphql/sub_groups.query.graphql';
import updateBoardEpicUserPreferencesMutation from '../graphql/update_board_epic_user_preferences.mutation.graphql'; import updateBoardEpicUserPreferencesMutation from '../graphql/update_board_epic_user_preferences.mutation.graphql';
import updateEpicLabelsMutation from '../graphql/update_epic_labels.mutation.graphql'; import updateEpicLabelsMutation from '../graphql/update_epic_labels.mutation.graphql';
...@@ -449,6 +450,46 @@ export default { ...@@ -449,6 +450,46 @@ export default {
}); });
}, },
fetchSubGroups: ({ commit, state }, { search = '', fetchNext = false } = {}) => {
commit(types.REQUEST_SUB_GROUPS, fetchNext);
const { fullPath } = state;
const variables = {
fullPath,
search: search !== '' ? search : undefined,
after: fetchNext ? state.subGroupsFlags.pageInfo.endCursor : undefined,
};
return gqlClient
.query({
query: subGroupsQuery,
variables,
})
.then(({ data }) => {
const { id, name, fullName, descendantGroups, __typename } = data.group;
const currentGroup = {
__typename,
id,
name,
fullName,
fullPath: data.group.fullPath,
};
const subGroups = [currentGroup, ...descendantGroups.nodes];
commit(types.RECEIVE_SUB_GROUPS_SUCCESS, {
subGroups,
pageInfo: descendantGroups.pageInfo,
fetchNext,
});
commit(types.SET_SELECTED_GROUP, currentGroup);
})
.catch(() => commit(types.RECEIVE_SUB_GROUPS_FAILURE));
},
setSelectedGroup: ({ commit }, group) => {
commit(types.SET_SELECTED_GROUP, group);
},
createList: ( createList: (
{ getters, dispatch }, { getters, dispatch },
{ backlog, labelId, milestoneId, assigneeId, iterationId }, { backlog, labelId, milestoneId, assigneeId, iterationId },
...@@ -495,14 +536,9 @@ export default { ...@@ -495,14 +536,9 @@ export default {
}, },
addListNewEpic: ( addListNewEpic: (
{ state: { fullPath }, dispatch, commit }, { dispatch, commit },
{ epicInput, list, placeholderId = `tmp-${new Date().getTime()}` }, { epicInput, list, placeholderId = `tmp-${new Date().getTime()}` },
) => { ) => {
const input = {
...epicInput,
groupPath: fullPath,
};
const placeholderEpic = { const placeholderEpic = {
...epicInput, ...epicInput,
id: placeholderId, id: placeholderId,
...@@ -516,11 +552,11 @@ export default { ...@@ -516,11 +552,11 @@ export default {
gqlClient gqlClient
.mutate({ .mutate({
mutation: epicCreateMutation, mutation: epicCreateMutation,
variables: { input }, variables: { input: epicInput },
}) })
.then(({ data }) => { .then(({ data }) => {
if (data.boardEpicCreate.errors?.length) { if (data.boardEpicCreate.errors?.length) {
throw new Error(); throw new Error(data.boardEpicCreate.errors[0]);
} }
const rawEpic = data.boardEpicCreate?.epic; const rawEpic = data.boardEpicCreate?.epic;
......
...@@ -18,3 +18,7 @@ export const SET_BOARD_EPIC_USER_PREFERENCES = 'SET_BOARD_EPIC_USER_PREFERENCES' ...@@ -18,3 +18,7 @@ export const SET_BOARD_EPIC_USER_PREFERENCES = 'SET_BOARD_EPIC_USER_PREFERENCES'
export const RECEIVE_ASSIGNEES_REQUEST = 'RECEIVE_ASSIGNEES_REQUEST'; export const RECEIVE_ASSIGNEES_REQUEST = 'RECEIVE_ASSIGNEES_REQUEST';
export const RECEIVE_ASSIGNEES_SUCCESS = 'RECEIVE_ASSIGNEES_SUCCESS'; export const RECEIVE_ASSIGNEES_SUCCESS = 'RECEIVE_ASSIGNEES_SUCCESS';
export const RECEIVE_ASSIGNEES_FAILURE = 'RECEIVE_ASSIGNEES_FAILURE'; export const RECEIVE_ASSIGNEES_FAILURE = 'RECEIVE_ASSIGNEES_FAILURE';
export const REQUEST_SUB_GROUPS = 'REQUEST_SUB_GROUPS';
export const RECEIVE_SUB_GROUPS_SUCCESS = 'RECEIVE_SUB_GROUPS_SUCCESS';
export const RECEIVE_SUB_GROUPS_FAILURE = 'RECEIVE_SUB_GROUPS_FAILURE';
export const SET_SELECTED_GROUP = 'SET_SELECTED_GROUP';
...@@ -184,4 +184,25 @@ export default { ...@@ -184,4 +184,25 @@ export default {
state.assigneesLoading = false; state.assigneesLoading = false;
state.error = __('Failed to load assignees.'); state.error = __('Failed to load assignees.');
}, },
[mutationTypes.REQUEST_SUB_GROUPS]: (state, fetchNext) => {
Vue.set(state, 'subGroupsFlags', {
[fetchNext ? 'isLoadingMore' : 'isLoading']: true,
pageInfo: state.subGroupsFlags.pageInfo,
});
},
[mutationTypes.RECEIVE_SUB_GROUPS_SUCCESS]: (state, { subGroups, pageInfo, fetchNext }) => {
Vue.set(state, 'subGroups', fetchNext ? [...state.subGroups, ...subGroups] : subGroups);
Vue.set(state, 'subGroupsFlags', { isLoading: false, isLoadingMore: false, pageInfo });
},
[mutationTypes.RECEIVE_SUB_GROUPS_FAILURE]: (state) => {
state.error = s__('Boards|An error occurred while fetching child groups. Please try again.');
Vue.set(state, 'subGroupsFlags', { isLoading: false, isLoadingMore: false });
},
[mutationTypes.SET_SELECTED_GROUP]: (state, group) => {
state.selectedGroup = group;
},
}; };
...@@ -17,4 +17,11 @@ export default () => ({ ...@@ -17,4 +17,11 @@ export default () => ({
epicsFlags: {}, epicsFlags: {},
assignees: [], assignees: [],
assigneesLoading: false, assigneesLoading: false,
selectedGroup: {},
subGroups: [],
subGroupsFlags: {
isLoading: false,
isLoadingMore: false,
pageInfo: {},
},
}); });
...@@ -2,6 +2,7 @@ import { shallowMount, createLocalVue } from '@vue/test-utils'; ...@@ -2,6 +2,7 @@ import { shallowMount, createLocalVue } from '@vue/test-utils';
import Vuex from 'vuex'; import Vuex from 'vuex';
import BoardNewEpic from 'ee/boards/components/board_new_epic.vue'; import BoardNewEpic from 'ee/boards/components/board_new_epic.vue';
import GroupSelect from 'ee/boards/components/group_select.vue';
import { mockList } from 'jest/boards/mock_data'; import { mockList } from 'jest/boards/mock_data';
import BoardNewItem from '~/boards/components/board_new_item.vue'; import BoardNewItem from '~/boards/components/board_new_item.vue';
...@@ -65,6 +66,13 @@ describe('Epic boards new epic form', () => { ...@@ -65,6 +66,13 @@ describe('Epic boards new epic form', () => {
}); });
}); });
it('renders group-select dropdown within board-new-item', () => {
const boardNewItem = findBoardNewItem();
const groupSelect = boardNewItem.findComponent(GroupSelect);
expect(groupSelect.exists()).toBe(true);
});
it('calls action `addListNewEpic` when "Create epic" button is clicked', async () => { it('calls action `addListNewEpic` when "Create epic" button is clicked', async () => {
await submitForm(wrapper); await submitForm(wrapper);
......
import { GlDropdown, GlDropdownItem, GlSearchBoxByType, GlLoadingIcon } from '@gitlab/ui';
import { mount } from '@vue/test-utils';
import Vue from 'vue';
import Vuex from 'vuex';
import GroupSelect from 'ee/boards/components/group_select.vue';
import defaultState from 'ee/boards/stores/state';
import { extendedWrapper } from 'helpers/vue_test_utils_helper';
import { mockList } from 'jest/boards/mock_data';
import { mockGroup0, mockSubGroups } from '../mock_data';
describe('GroupSelect component', () => {
let wrapper;
let store;
const findLabel = () => wrapper.findByTestId('header-label');
const findGlDropdown = () => wrapper.findComponent(GlDropdown);
const findGlDropdownLoadingIcon = () =>
findGlDropdown().find('button:first-child').findComponent(GlLoadingIcon);
const findGlSearchBoxByType = () => wrapper.findComponent(GlSearchBoxByType);
const findGlDropdownItems = () => wrapper.findAllComponents(GlDropdownItem);
const findFirstGlDropdownItem = () => findGlDropdownItems().at(0);
const findInMenuLoadingIcon = () => wrapper.findByTestId('dropdown-text-loading-icon');
const findEmptySearchMessage = () => wrapper.findByTestId('empty-result-message');
const createStore = ({ state = {}, subGroups, selectedGroup, moreGroupsLoading = false }) => {
Vue.use(Vuex);
store = new Vuex.Store({
state: {
...state,
subGroups,
selectedGroup,
subGroupsFlags: {
isLoading: moreGroupsLoading,
pageInfo: {
hasNextPage: false,
},
},
},
actions: {
fetchSubGroups: jest.fn(),
setSelectedGroup: jest.fn(),
},
});
};
const createWrapper = ({
state = defaultState,
subGroups = [],
selectedGroup = {},
loading = false,
moreGroupsLoading = false,
} = {}) => {
createStore({
state,
subGroups,
selectedGroup,
loading,
moreGroupsLoading,
});
wrapper = extendedWrapper(
mount(GroupSelect, {
propsData: {
list: mockList,
},
data() {
return {
initialLoading: loading,
};
},
store,
provide: {
groupId: 1,
},
}),
);
};
afterEach(() => {
wrapper.destroy();
wrapper = null;
});
it('displays a header title', () => {
createWrapper();
expect(findLabel().text()).toBe('Groups');
});
it('renders a default dropdown text', () => {
createWrapper();
expect(findGlDropdown().exists()).toBe(true);
expect(findGlDropdown().text()).toContain('Loading groups');
});
describe('when mounted', () => {
it('displays a loading icon while descendant groups are being fetched', async () => {
createWrapper({ loading: true });
wrapper.setData({ initialLoading: true });
await wrapper.vm.$nextTick();
expect(findGlDropdownLoadingIcon().exists()).toBe(true);
});
});
describe('when dropdown menu is open', () => {
describe('by default', () => {
beforeEach(() => {
createWrapper({ subGroups: mockSubGroups });
});
it('shows GlSearchBoxByType with default attributes', () => {
expect(findGlSearchBoxByType().exists()).toBe(true);
expect(findGlSearchBoxByType().vm.$attrs).toMatchObject({
placeholder: 'Search groups',
debounce: '250',
});
});
it("displays the fetched groups's name", () => {
expect(findFirstGlDropdownItem().exists()).toBe(true);
expect(findFirstGlDropdownItem().text()).toContain(mockGroup0.name);
});
it("doesn't render loading icon in the menu", () => {
expect(findInMenuLoadingIcon().isVisible()).toBe(false);
});
it('does not render empty search result message', () => {
expect(findEmptySearchMessage().exists()).toBe(false);
});
});
describe('when no groups are being returned', () => {
it('renders empty search result message', () => {
createWrapper();
expect(findEmptySearchMessage().exists()).toBe(true);
});
});
describe('when a group is selected', () => {
it('renders the name of the selected group', () => {
createWrapper({ subGroups: mockSubGroups, selectedGroup: mockGroup0 });
expect(findGlDropdown().find('.gl-new-dropdown-button-text').text()).toBe(mockGroup0.name);
});
});
describe('when groups are loading', () => {
it('displays and hides gl-loading-icon while and after fetching data', () => {
createWrapper({ moreGroupsLoading: true });
expect(findInMenuLoadingIcon().isVisible()).toBe(true);
});
});
});
});
...@@ -341,3 +341,29 @@ export const issues = { ...@@ -341,3 +341,29 @@ export const issues = {
[mockIssue3.id]: mockIssue3, [mockIssue3.id]: mockIssue3,
[mockIssue4.id]: mockIssue4, [mockIssue4.id]: mockIssue4,
}; };
export const mockGroup0 = {
__typename: 'Group',
id: 'gid://gitlab/Group/22',
name: 'Gitlab Org',
fullName: 'Gitlab Org',
fullPath: 'gitlab-org',
};
export const mockGroup1 = {
__typename: 'Group',
id: 'gid://gitlab/Group/108',
name: 'Design',
fullName: 'Gitlab Org / Design',
fullPath: 'gitlab-org/design',
};
export const mockGroup2 = {
__typename: 'Group',
id: 'gid://gitlab/Group/109',
name: 'Database',
fullName: 'Gitlab Org / Database',
fullPath: 'gitlab-org/database',
};
export const mockSubGroups = [mockGroup0, mockGroup1, mockGroup2];
...@@ -25,6 +25,8 @@ import { ...@@ -25,6 +25,8 @@ import {
mockEpic, mockEpic,
mockMilestones, mockMilestones,
mockAssignees, mockAssignees,
mockSubGroups,
mockGroup0,
} from '../mock_data'; } from '../mock_data';
Vue.use(Vuex); Vue.use(Vuex);
...@@ -952,7 +954,7 @@ describe('addListNewEpic', () => { ...@@ -952,7 +954,7 @@ describe('addListNewEpic', () => {
await actions.addListNewEpic( await actions.addListNewEpic(
{ dispatch: jest.fn(), commit: jest.fn(), state }, { dispatch: jest.fn(), commit: jest.fn(), state },
{ epicInput: mockEpic, list: fakeList }, { epicInput: { ...mockEpic, groupPath: state.fullPath }, list: fakeList },
); );
expect(gqlClient.mutate).toHaveBeenCalledWith({ expect(gqlClient.mutate).toHaveBeenCalledWith({
...@@ -990,7 +992,7 @@ describe('addListNewEpic', () => { ...@@ -990,7 +992,7 @@ describe('addListNewEpic', () => {
await actions.addListNewEpic( await actions.addListNewEpic(
{ dispatch: jest.fn(), commit: jest.fn(), state }, { dispatch: jest.fn(), commit: jest.fn(), state },
{ epicInput: epic, list: fakeList }, { epicInput: { ...epic, groupPath: state.fullPath }, list: fakeList },
); );
expect(gqlClient.mutate).toHaveBeenCalledWith({ expect(gqlClient.mutate).toHaveBeenCalledWith({
...@@ -1242,6 +1244,97 @@ describe('fetchAssignees', () => { ...@@ -1242,6 +1244,97 @@ describe('fetchAssignees', () => {
}); });
}); });
describe('fetchSubGroups', () => {
const state = {
fullPath: 'gitlab-org',
};
const pageInfo = {
endCursor: '',
hasNextPage: false,
};
const queryResponse = {
data: {
group: {
descendantGroups: {
nodes: mockSubGroups.slice(1), // First group is root group, so skip it.
pageInfo: {
endCursor: '',
hasNextPage: false,
},
},
...mockGroup0, // Add root group info
},
},
};
it('should commit mutations REQUEST_SUB_GROUPS, RECEIVE_SUB_GROUPS_SUCCESS, and SET_SELECTED_GROUP on success', (done) => {
jest.spyOn(gqlClient, 'query').mockResolvedValue(queryResponse);
testAction(
actions.fetchSubGroups,
{},
state,
[
{
type: types.REQUEST_SUB_GROUPS,
payload: false,
},
{
type: types.RECEIVE_SUB_GROUPS_SUCCESS,
payload: { subGroups: mockSubGroups, pageInfo, fetchNext: false },
},
{
type: types.SET_SELECTED_GROUP,
payload: mockGroup0,
},
],
[],
done,
);
});
it('should commit mutations REQUEST_SUB_GROUPS and RECEIVE_SUB_GROUPS_FAILURE on failure', (done) => {
jest.spyOn(gqlClient, 'query').mockRejectedValue();
testAction(
actions.fetchSubGroups,
{},
state,
[
{
type: types.REQUEST_SUB_GROUPS,
payload: false,
},
{
type: types.RECEIVE_SUB_GROUPS_FAILURE,
},
],
[],
done,
);
});
});
describe('setSelectedGroup', () => {
it('should commit mutation SET_SELECTED_GROUP', (done) => {
testAction(
actions.setSelectedGroup,
mockGroup0,
{},
[
{
type: types.SET_SELECTED_GROUP,
payload: mockGroup0,
},
],
[],
done,
);
});
});
describe('setActiveEpicLabels', () => { describe('setActiveEpicLabels', () => {
const state = { boardItems: { [mockEpic.id]: mockEpic } }; const state = { boardItems: { [mockEpic.id]: mockEpic } };
const getters = { activeBoardItem: mockEpic }; const getters = { activeBoardItem: mockEpic };
......
import * as types from 'ee/boards/stores/mutation_types';
import mutations from 'ee/boards/stores/mutations'; import mutations from 'ee/boards/stores/mutations';
import { mockEpics, mockEpic, mockLists, mockIssue, mockIssue2 } from '../mock_data'; import { mockEpics, mockEpic, mockLists, mockIssue, mockIssue2, mockSubGroups } from '../mock_data';
const initialBoardListsState = { const initialBoardListsState = {
'gid://gitlab/List/1': mockLists[0], 'gid://gitlab/List/1': mockLists[0],
...@@ -15,6 +16,11 @@ let state = { ...@@ -15,6 +16,11 @@ let state = {
epicsFlags: { epicsFlags: {
[epicId]: { isLoading: true }, [epicId]: { isLoading: true },
}, },
subGroupsFlags: {
isLoading: false,
isLoadingMore: false,
pageInfo: {},
},
}; };
describe('SET_SHOW_LABELS', () => { describe('SET_SHOW_LABELS', () => {
...@@ -292,3 +298,61 @@ describe('SET_BOARD_EPIC_USER_PREFERENCES', () => { ...@@ -292,3 +298,61 @@ describe('SET_BOARD_EPIC_USER_PREFERENCES', () => {
expect(state.epics[0].userPreferences).toEqual(userPreferences); expect(state.epics[0].userPreferences).toEqual(userPreferences);
}); });
}); });
describe('REQUEST_SUB_GROUPS', () => {
it('Should set isLoading in subGroupsFlags to true in state when fetchNext is false', () => {
mutations[types.REQUEST_SUB_GROUPS](state, false);
expect(state.subGroupsFlags.isLoading).toBe(true);
});
it('Should set isLoadingMore in subGroupsFlags to true in state when fetchNext is true', () => {
mutations[types.REQUEST_SUB_GROUPS](state, true);
expect(state.subGroupsFlags.isLoadingMore).toBe(true);
});
});
describe('RECEIVE_SUB_GROUPS_SUCCESS', () => {
it('Should set subGroups and pageInfo to state and isLoading in subGroupsFlags to false', () => {
mutations[types.RECEIVE_SUB_GROUPS_SUCCESS](state, {
subGroups: mockSubGroups,
pageInfo: { hasNextPage: false },
});
expect(state.subGroups).toEqual(mockSubGroups);
expect(state.subGroupsFlags.isLoading).toBe(false);
expect(state.subGroupsFlags.pageInfo).toEqual({ hasNextPage: false });
});
it('Should merge groups in subGroups in state when fetchNext is true', () => {
state = {
...state,
subGroups: [mockSubGroups[0]],
};
mutations[types.RECEIVE_SUB_GROUPS_SUCCESS](state, {
subGroups: [mockSubGroups[1]],
fetchNext: true,
});
expect(state.subGroups).toEqual([mockSubGroups[0], mockSubGroups[1]]);
});
});
describe('RECEIVE_SUB_GROUPS_FAILURE', () => {
it('Should set error in state and isLoading in subGroupsFlags to false', () => {
mutations[types.RECEIVE_SUB_GROUPS_FAILURE](state);
expect(state.error).toEqual('An error occurred while fetching child groups. Please try again.');
expect(state.subGroupsFlags.isLoading).toBe(false);
});
});
describe('SET_SELECTED_GROUP', () => {
it('Should set selectedGroup to state', () => {
mutations[types.SET_SELECTED_GROUP](state, mockSubGroups[0]);
expect(state.selectedGroup).toEqual(mockSubGroups[0]);
});
});
...@@ -5397,6 +5397,21 @@ msgstr "" ...@@ -5397,6 +5397,21 @@ msgstr ""
msgid "Board scope affects which issues are displayed for anyone who visits this board" msgid "Board scope affects which issues are displayed for anyone who visits this board"
msgstr "" msgstr ""
msgid "BoardNewEpic|Groups"
msgstr ""
msgid "BoardNewEpic|Loading groups"
msgstr ""
msgid "BoardNewEpic|No matching results"
msgstr ""
msgid "BoardNewEpic|Search groups"
msgstr ""
msgid "BoardNewEpic|Select a group"
msgstr ""
msgid "BoardNewIssue|No matching results" msgid "BoardNewIssue|No matching results"
msgstr "" msgstr ""
...@@ -5474,6 +5489,9 @@ msgstr "" ...@@ -5474,6 +5489,9 @@ msgstr ""
msgid "Boards|An error occurred while creating the list. Please try again." msgid "Boards|An error occurred while creating the list. Please try again."
msgstr "" msgstr ""
msgid "Boards|An error occurred while fetching child groups. Please try again."
msgstr ""
msgid "Boards|An error occurred while fetching group projects. Please try again." msgid "Boards|An error occurred while fetching group projects. Please try again."
msgstr "" msgstr ""
......
...@@ -545,7 +545,7 @@ describe('Board Store Mutations', () => { ...@@ -545,7 +545,7 @@ describe('Board Store Mutations', () => {
expect(state.groupProjectsFlags.isLoading).toBe(true); expect(state.groupProjectsFlags.isLoading).toBe(true);
}); });
it('Should set isLoading in groupProjectsFlags to true in state when fetchNext is true', () => { it('Should set isLoadingMore in groupProjectsFlags to true in state when fetchNext is true', () => {
mutations[types.REQUEST_GROUP_PROJECTS](state, true); mutations[types.REQUEST_GROUP_PROJECTS](state, true);
expect(state.groupProjectsFlags.isLoadingMore).toBe(true); expect(state.groupProjectsFlags.isLoadingMore).toBe(true);
......
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