Commit 72aa021f authored by Kushal Pandya's avatar Kushal Pandya

Merge branch '332917-boards-adding-new-list-should-update-issue-from-other-columns' into 'master'

Boards - Update Open column when adding list [RUN AS-IF-FOSS]

See merge request gitlab-org/gitlab!63481
parents de759c5e ad5a8b2d
...@@ -36,6 +36,7 @@ export const ListTypeTitles = { ...@@ -36,6 +36,7 @@ export const ListTypeTitles = {
milestone: __('Milestone'), milestone: __('Milestone'),
iteration: __('Iteration'), iteration: __('Iteration'),
label: __('Label'), label: __('Label'),
backlog: __('Open'),
}; };
export const formType = { export const formType = {
......
...@@ -12,6 +12,7 @@ import { ...@@ -12,6 +12,7 @@ import {
updateListQueries, updateListQueries,
issuableTypes, issuableTypes,
FilterFields, FilterFields,
ListTypeTitles,
} from 'ee_else_ce/boards/constants'; } from 'ee_else_ce/boards/constants';
import createBoardListMutation from 'ee_else_ce/boards/graphql/board_list_create.mutation.graphql'; import createBoardListMutation from 'ee_else_ce/boards/graphql/board_list_create.mutation.graphql';
import issueMoveListMutation from 'ee_else_ce/boards/graphql/issue_move_list.mutation.graphql'; import issueMoveListMutation from 'ee_else_ce/boards/graphql/issue_move_list.mutation.graphql';
...@@ -169,8 +170,11 @@ export default { ...@@ -169,8 +170,11 @@ export default {
}); });
}, },
addList: ({ commit }, list) => { addList: ({ commit, dispatch, getters }, list) => {
commit(types.RECEIVE_ADD_LIST_SUCCESS, updateListPosition(list)); commit(types.RECEIVE_ADD_LIST_SUCCESS, updateListPosition(list));
dispatch('fetchItemsForList', {
listId: getters.getListByTitle(ListTypeTitles.backlog).id,
});
}, },
fetchLabels: ({ state, commit, getters }, searchTerm) => { fetchLabels: ({ state, commit, getters }, searchTerm) => {
...@@ -261,7 +265,7 @@ export default { ...@@ -261,7 +265,7 @@ export default {
commit(types.TOGGLE_LIST_COLLAPSED, { listId, collapsed }); commit(types.TOGGLE_LIST_COLLAPSED, { listId, collapsed });
}, },
removeList: ({ state: { issuableType, boardLists }, commit }, listId) => { removeList: ({ state: { issuableType, boardLists }, commit, dispatch, getters }, listId) => {
const listsBackup = { ...boardLists }; const listsBackup = { ...boardLists };
commit(types.REMOVE_LIST, listId); commit(types.REMOVE_LIST, listId);
...@@ -281,6 +285,10 @@ export default { ...@@ -281,6 +285,10 @@ export default {
}) => { }) => {
if (errors.length > 0) { if (errors.length > 0) {
commit(types.REMOVE_LIST_FAILURE, listsBackup); commit(types.REMOVE_LIST_FAILURE, listsBackup);
} else {
dispatch('fetchItemsForList', {
listId: getters.getListByTitle(ListTypeTitles.backlog).id,
});
} }
}, },
) )
...@@ -290,6 +298,9 @@ export default { ...@@ -290,6 +298,9 @@ export default {
}, },
fetchItemsForList: ({ state, commit }, { listId, fetchNext = false }) => { fetchItemsForList: ({ state, commit }, { listId, fetchNext = false }) => {
if (!fetchNext) {
commit(types.RESET_ITEMS_FOR_LIST, listId);
}
commit(types.REQUEST_ITEMS_FOR_LIST, { listId, fetchNext }); commit(types.REQUEST_ITEMS_FOR_LIST, { listId, fetchNext });
const { fullPath, fullBoardId, boardType, filterParams } = state; const { fullPath, fullBoardId, boardType, filterParams } = state;
......
...@@ -15,6 +15,7 @@ export const UPDATE_LIST_FAILURE = 'UPDATE_LIST_FAILURE'; ...@@ -15,6 +15,7 @@ export const UPDATE_LIST_FAILURE = 'UPDATE_LIST_FAILURE';
export const TOGGLE_LIST_COLLAPSED = 'TOGGLE_LIST_COLLAPSED'; export const TOGGLE_LIST_COLLAPSED = 'TOGGLE_LIST_COLLAPSED';
export const REMOVE_LIST = 'REMOVE_LIST'; export const REMOVE_LIST = 'REMOVE_LIST';
export const REMOVE_LIST_FAILURE = 'REMOVE_LIST_FAILURE'; export const REMOVE_LIST_FAILURE = 'REMOVE_LIST_FAILURE';
export const RESET_ITEMS_FOR_LIST = 'RESET_ITEMS_FOR_LIST';
export const REQUEST_ITEMS_FOR_LIST = 'REQUEST_ITEMS_FOR_LIST'; export const REQUEST_ITEMS_FOR_LIST = 'REQUEST_ITEMS_FOR_LIST';
export const RECEIVE_ITEMS_FOR_LIST_FAILURE = 'RECEIVE_ITEMS_FOR_LIST_FAILURE'; export const RECEIVE_ITEMS_FOR_LIST_FAILURE = 'RECEIVE_ITEMS_FOR_LIST_FAILURE';
export const RECEIVE_ITEMS_FOR_LIST_SUCCESS = 'RECEIVE_ITEMS_FOR_LIST_SUCCESS'; export const RECEIVE_ITEMS_FOR_LIST_SUCCESS = 'RECEIVE_ITEMS_FOR_LIST_SUCCESS';
......
...@@ -117,6 +117,11 @@ export default { ...@@ -117,6 +117,11 @@ export default {
state.boardLists = listsBackup; state.boardLists = listsBackup;
}, },
[mutationTypes.RESET_ITEMS_FOR_LIST]: (state, listId) => {
Vue.set(state, 'backupItemsList', state.boardItemsByListId[listId]);
Vue.set(state.boardItemsByListId, listId, []);
},
[mutationTypes.REQUEST_ITEMS_FOR_LIST]: (state, { listId, fetchNext }) => { [mutationTypes.REQUEST_ITEMS_FOR_LIST]: (state, { listId, fetchNext }) => {
Vue.set(state.listsFlags, listId, { [fetchNext ? 'isLoadingMore' : 'isLoading']: true }); Vue.set(state.listsFlags, listId, { [fetchNext ? 'isLoadingMore' : 'isLoading']: true });
}, },
...@@ -138,6 +143,7 @@ export default { ...@@ -138,6 +143,7 @@ export default {
'Boards|An error occurred while fetching the board issues. Please reload the page.', 'Boards|An error occurred while fetching the board issues. Please reload the page.',
); );
Vue.set(state.listsFlags, listId, { isLoading: false, isLoadingMore: false }); Vue.set(state.listsFlags, listId, { isLoading: false, isLoadingMore: false });
Vue.set(state.boardItemsByListId, listId, state.backupItemsList);
}, },
[mutationTypes.RESET_ISSUES]: (state) => { [mutationTypes.RESET_ISSUES]: (state) => {
......
...@@ -11,6 +11,7 @@ export default () => ({ ...@@ -11,6 +11,7 @@ export default () => ({
boardLists: {}, boardLists: {},
listsFlags: {}, listsFlags: {},
boardItemsByListId: {}, boardItemsByListId: {},
backupItemsList: [],
isSettingAssignees: false, isSettingAssignees: false,
pageInfoByListId: {}, pageInfoByListId: {},
boardItems: {}, boardItems: {},
......
...@@ -245,6 +245,9 @@ export default { ...@@ -245,6 +245,9 @@ export default {
{ state, commit, getters }, { state, commit, getters },
{ listId, fetchNext = false, noEpicIssues = false, forSwimlanes = false }, { listId, fetchNext = false, noEpicIssues = false, forSwimlanes = false },
) => { ) => {
if (!fetchNext && !state.isShowingEpicsSwimlanes) {
commit(types.RESET_ITEMS_FOR_LIST, listId);
}
commit(types.REQUEST_ITEMS_FOR_LIST, { listId, fetchNext }); commit(types.REQUEST_ITEMS_FOR_LIST, { listId, fetchNext });
const { epicId, ...filterParams } = state.filterParams; const { epicId, ...filterParams } = state.filterParams;
......
...@@ -46,6 +46,7 @@ export default { ...@@ -46,6 +46,7 @@ export default {
? ErrorMessages.fetchEpicsError ? ErrorMessages.fetchEpicsError
: ErrorMessages.fetchIssueError; : ErrorMessages.fetchIssueError;
Vue.set(state.listsFlags, listId, { isLoading: false, isLoadingMore: false }); Vue.set(state.listsFlags, listId, { isLoading: false, isLoadingMore: false });
Vue.set(state.boardItemsByListId, listId, state.backupItemsList);
}, },
[mutationTypes.TOGGLE_EPICS_SWIMLANES]: (state) => { [mutationTypes.TOGGLE_EPICS_SWIMLANES]: (state) => {
......
...@@ -288,7 +288,7 @@ describe('fetchEpicsSwimlanes', () => { ...@@ -288,7 +288,7 @@ describe('fetchEpicsSwimlanes', () => {
describe('fetchItemsForList', () => { describe('fetchItemsForList', () => {
const listId = mockLists[0].id; const listId = mockLists[0].id;
const state = { let state = {
fullPath: 'gitlab-org', fullPath: 'gitlab-org',
boardId: '1', boardId: '1',
filterParams: {}, filterParams: {},
...@@ -329,6 +329,10 @@ describe('fetchItemsForList', () => { ...@@ -329,6 +329,10 @@ describe('fetchItemsForList', () => {
}; };
it('add epicWildcardId with ANY as value when forSwimlanes is true', () => { it('add epicWildcardId with ANY as value when forSwimlanes is true', () => {
state = {
...state,
isShowingEpicsSwimlanes: true,
};
jest.spyOn(gqlClient, 'query').mockResolvedValue(queryResponse); jest.spyOn(gqlClient, 'query').mockResolvedValue(queryResponse);
testAction( testAction(
......
...@@ -15,6 +15,7 @@ import { ...@@ -15,6 +15,7 @@ import {
formatIssueInput, formatIssueInput,
formatIssue, formatIssue,
getMoveData, getMoveData,
updateListPosition,
} from '~/boards/boards_util'; } from '~/boards/boards_util';
import destroyBoardListMutation from '~/boards/graphql/board_list_destroy.mutation.graphql'; import destroyBoardListMutation from '~/boards/graphql/board_list_destroy.mutation.graphql';
import issueCreateMutation from '~/boards/graphql/issue_create.mutation.graphql'; import issueCreateMutation from '~/boards/graphql/issue_create.mutation.graphql';
...@@ -36,6 +37,7 @@ import { ...@@ -36,6 +37,7 @@ import {
mockMoveIssueParams, mockMoveIssueParams,
mockMoveState, mockMoveState,
mockMoveData, mockMoveData,
mockList,
} from '../mock_data'; } from '../mock_data';
jest.mock('~/flash'); jest.mock('~/flash');
...@@ -374,6 +376,24 @@ describe('createIssueList', () => { ...@@ -374,6 +376,24 @@ describe('createIssueList', () => {
}); });
}); });
describe('addList', () => {
const getters = {
getListByTitle: jest.fn().mockReturnValue(mockList),
};
it('should commit RECEIVE_ADD_LIST_SUCCESS mutation and dispatch fetchItemsForList action', () => {
testAction({
action: actions.addList,
payload: mockLists[1],
state: { ...getters },
expectedMutations: [
{ type: types.RECEIVE_ADD_LIST_SUCCESS, payload: updateListPosition(mockLists[1]) },
],
expectedActions: [{ type: 'fetchItemsForList', payload: { listId: mockList.id } }],
});
});
});
describe('fetchLabels', () => { describe('fetchLabels', () => {
it('should commit mutation RECEIVE_LABELS_SUCCESS on success', async () => { it('should commit mutation RECEIVE_LABELS_SUCCESS on success', async () => {
const queryResponse = { const queryResponse = {
...@@ -521,7 +541,8 @@ describe('toggleListCollapsed', () => { ...@@ -521,7 +541,8 @@ describe('toggleListCollapsed', () => {
describe('removeList', () => { describe('removeList', () => {
let state; let state;
const list = mockLists[0]; let getters;
const list = mockLists[1];
const listId = list.id; const listId = list.id;
const mutationVariables = { const mutationVariables = {
mutation: destroyBoardListMutation, mutation: destroyBoardListMutation,
...@@ -535,6 +556,9 @@ describe('removeList', () => { ...@@ -535,6 +556,9 @@ describe('removeList', () => {
boardLists: mockListsById, boardLists: mockListsById,
issuableType: issuableTypes.issue, issuableType: issuableTypes.issue,
}; };
getters = {
getListByTitle: jest.fn().mockReturnValue(mockList),
};
}); });
afterEach(() => { afterEach(() => {
...@@ -544,13 +568,15 @@ describe('removeList', () => { ...@@ -544,13 +568,15 @@ describe('removeList', () => {
it('optimistically deletes the list', () => { it('optimistically deletes the list', () => {
const commit = jest.fn(); const commit = jest.fn();
actions.removeList({ commit, state }, listId); actions.removeList({ commit, state, getters, dispatch: () => {} }, listId);
expect(commit.mock.calls).toEqual([[types.REMOVE_LIST, listId]]); expect(commit.mock.calls).toEqual([[types.REMOVE_LIST, listId]]);
}); });
it('keeps the updated list if remove succeeds', async () => { it('keeps the updated list if remove succeeds', async () => {
const commit = jest.fn(); const commit = jest.fn();
const dispatch = jest.fn();
jest.spyOn(gqlClient, 'mutate').mockResolvedValue({ jest.spyOn(gqlClient, 'mutate').mockResolvedValue({
data: { data: {
destroyBoardList: { destroyBoardList: {
...@@ -559,17 +585,18 @@ describe('removeList', () => { ...@@ -559,17 +585,18 @@ describe('removeList', () => {
}, },
}); });
await actions.removeList({ commit, state }, listId); await actions.removeList({ commit, state, getters, dispatch }, listId);
expect(gqlClient.mutate).toHaveBeenCalledWith(mutationVariables); expect(gqlClient.mutate).toHaveBeenCalledWith(mutationVariables);
expect(commit.mock.calls).toEqual([[types.REMOVE_LIST, listId]]); expect(commit.mock.calls).toEqual([[types.REMOVE_LIST, listId]]);
expect(dispatch.mock.calls).toEqual([['fetchItemsForList', { listId: mockList.id }]]);
}); });
it('restores the list if update fails', async () => { it('restores the list if update fails', async () => {
const commit = jest.fn(); const commit = jest.fn();
jest.spyOn(gqlClient, 'mutate').mockResolvedValue(Promise.reject()); jest.spyOn(gqlClient, 'mutate').mockResolvedValue(Promise.reject());
await actions.removeList({ commit, state }, listId); await actions.removeList({ commit, state, getters, dispatch: () => {} }, listId);
expect(gqlClient.mutate).toHaveBeenCalledWith(mutationVariables); expect(gqlClient.mutate).toHaveBeenCalledWith(mutationVariables);
expect(commit.mock.calls).toEqual([ expect(commit.mock.calls).toEqual([
...@@ -588,7 +615,7 @@ describe('removeList', () => { ...@@ -588,7 +615,7 @@ describe('removeList', () => {
}, },
}); });
await actions.removeList({ commit, state }, listId); await actions.removeList({ commit, state, getters, dispatch: () => {} }, listId);
expect(gqlClient.mutate).toHaveBeenCalledWith(mutationVariables); expect(gqlClient.mutate).toHaveBeenCalledWith(mutationVariables);
expect(commit.mock.calls).toEqual([ expect(commit.mock.calls).toEqual([
...@@ -649,6 +676,10 @@ describe('fetchItemsForList', () => { ...@@ -649,6 +676,10 @@ describe('fetchItemsForList', () => {
{ listId }, { listId },
state, state,
[ [
{
type: types.RESET_ITEMS_FOR_LIST,
payload: listId,
},
{ {
type: types.REQUEST_ITEMS_FOR_LIST, type: types.REQUEST_ITEMS_FOR_LIST,
payload: { listId, fetchNext: false }, payload: { listId, fetchNext: false },
...@@ -671,6 +702,10 @@ describe('fetchItemsForList', () => { ...@@ -671,6 +702,10 @@ describe('fetchItemsForList', () => {
{ listId }, { listId },
state, state,
[ [
{
type: types.RESET_ITEMS_FOR_LIST,
payload: listId,
},
{ {
type: types.REQUEST_ITEMS_FOR_LIST, type: types.REQUEST_ITEMS_FOR_LIST,
payload: { listId, fetchNext: false }, payload: { listId, fetchNext: false },
......
...@@ -273,6 +273,53 @@ describe('Board Store Mutations', () => { ...@@ -273,6 +273,53 @@ describe('Board Store Mutations', () => {
}); });
}); });
describe('RESET_ITEMS_FOR_LIST', () => {
it('should remove issues from boardItemsByListId state', () => {
const listId = 'gid://gitlab/List/1';
const boardItemsByListId = {
[listId]: [mockIssue.id],
};
state = {
...state,
boardItemsByListId,
};
mutations[types.RESET_ITEMS_FOR_LIST](state, listId);
expect(state.boardItemsByListId[listId]).toEqual([]);
});
});
describe('REQUEST_ITEMS_FOR_LIST', () => {
const listId = 'gid://gitlab/List/1';
const boardItemsByListId = {
[listId]: [mockIssue.id],
};
it.each`
fetchNext | isLoading | isLoadingMore
${true} | ${undefined} | ${true}
${false} | ${true} | ${undefined}
`(
'sets isLoading to $isLoading and isLoadingMore to $isLoadingMore when fetchNext is $fetchNext',
({ fetchNext, isLoading, isLoadingMore }) => {
state = {
...state,
boardItemsByListId,
listsFlags: {
[listId]: {},
},
};
mutations[types.REQUEST_ITEMS_FOR_LIST](state, { listId, fetchNext });
expect(state.listsFlags[listId].isLoading).toBe(isLoading);
expect(state.listsFlags[listId].isLoadingMore).toBe(isLoadingMore);
},
);
});
describe('RECEIVE_ITEMS_FOR_LIST_SUCCESS', () => { describe('RECEIVE_ITEMS_FOR_LIST_SUCCESS', () => {
it('updates boardItemsByListId and issues on state', () => { it('updates boardItemsByListId and issues on state', () => {
const listIssues = { const listIssues = {
......
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