Commit 969eb2dc authored by Rajat Jain's avatar Rajat Jain

Persist collapsed state to DB

For swimlanes, save the collapsed state to the DB so that
the view remains consistent.
parent b58d35dd
......@@ -2,6 +2,7 @@
import { GlButton, GlIcon, GlLink, GlLoadingIcon, GlPopover, GlTooltipDirective } from '@gitlab/ui';
import { mapActions, mapGetters, mapState } from 'vuex';
import { __, n__, sprintf } from '~/locale';
import createFlash from '~/flash';
import timeagoMixin from '~/vue_shared/mixins/timeago';
import { formatDate } from '~/lib/utils/datetime_utility';
import { statusType } from '../../epic/constants';
......@@ -40,8 +41,12 @@ export default {
},
},
data() {
const { userPreferences } = this.epic;
const { collapsed = false } = userPreferences || {};
return {
isExpanded: true,
isCollapsed: collapsed,
};
},
computed: {
......@@ -51,10 +56,10 @@ export default {
return this.epic.state === statusType.open;
},
chevronTooltip() {
return this.isExpanded ? __('Collapse') : __('Expand');
return this.isCollapsed ? __('Expand') : __('Collapse');
},
chevronIcon() {
return this.isExpanded ? 'chevron-down' : 'chevron-right';
return this.isCollapsed ? 'chevron-right' : 'chevron-down';
},
issuesCount() {
return this.lists.reduce(
......@@ -95,9 +100,16 @@ export default {
this.fetchIssuesForEpic(this.epic.id);
},
methods: {
...mapActions(['fetchIssuesForEpic']),
toggleExpanded() {
this.isExpanded = !this.isExpanded;
...mapActions(['fetchIssuesForEpic', 'updateBoardEpicUserPreferences']),
toggleCollapsed() {
this.isCollapsed = !this.isCollapsed;
this.updateBoardEpicUserPreferences({
collapsed: this.isCollapsed,
epicId: this.epic.id,
}).catch(() => {
createFlash({ message: __('Unable to save your preference'), captureError: true });
});
},
},
};
......@@ -115,7 +127,7 @@ export default {
class="gl-mr-2 gl-cursor-pointer"
variant="link"
data-testid="epic-lane-chevron"
@click="toggleExpanded"
@click="toggleCollapsed"
/>
<h4
ref="epicTitle"
......@@ -146,7 +158,7 @@ export default {
<gl-loading-icon v-if="isLoading" class="gl-p-2" />
</div>
</div>
<div v-if="isExpanded" class="gl-display-flex" data-testid="board-epic-lane-issues">
<div v-if="!isCollapsed" class="gl-display-flex" data-testid="board-epic-lane-issues">
<issues-lane-list
v-for="list in lists"
:key="`${list.id}-issues`"
......
......@@ -21,6 +21,9 @@ query BoardEE(
edges {
node {
...BoardEpicNode
userPreferences {
collapsed
}
}
}
pageInfo {
......@@ -41,6 +44,9 @@ query BoardEE(
edges {
node {
...BoardEpicNode
userPreferences {
collapsed
}
}
}
pageInfo {
......
mutation updateBoardEpicUserPreferences(
$boardId: BoardID!
$epicId: EpicID!
$collapsed: Boolean!
) {
updateBoardEpicUserPreferences(
input: { boardId: $boardId, epicId: $epicId, collapsed: $collapsed }
) {
errors
epicUserPreferences {
collapsed
}
}
}
......@@ -24,6 +24,7 @@ import epicsSwimlanesQuery from '../queries/epics_swimlanes.query.graphql';
import issueSetEpic from '../queries/issue_set_epic.mutation.graphql';
import listsIssuesQuery from '~/boards/queries/lists_issues.query.graphql';
import issueMoveListMutation from '../queries/issue_move_list.mutation.graphql';
import updateBoardEpicUserPreferencesMutation from '../queries/updateBoardEpicUserPreferences.mutation.graphql';
const notImplemented = () => {
/* eslint-disable-next-line @gitlab/require-i18n-strings */
......@@ -138,6 +139,40 @@ export default {
.catch(() => commit(types.RECEIVE_SWIMLANES_FAILURE));
},
updateBoardEpicUserPreferences({ commit, state }, { epicId, collapsed }) {
const {
endpoints: { boardId },
} = state;
const variables = {
boardId: fullBoardId(boardId),
epicId,
collapsed,
};
return gqlClient
.mutate({
mutation: updateBoardEpicUserPreferencesMutation,
variables,
})
.then(({ data }) => {
if (data?.updateBoardEpicUserPreferences?.errors.length) {
throw new Error();
}
const { epicUserPreferences: userPreferences } = data?.updateBoardEpicUserPreferences;
commit(types.SET_BOARD_EPIC_USER_PREFERENCES, { epicId, userPreferences });
})
.catch(() => {
commit(types.SET_BOARD_EPIC_USER_PREFERENCES, {
epicId,
userPreferences: {
collapsed: !collapsed,
},
});
});
},
setShowLabels({ commit }, val) {
commit(types.SET_SHOW_LABELS, val);
},
......
......@@ -28,3 +28,4 @@ export const SET_FILTERS = 'SET_FILTERS';
export const MOVE_ISSUE = 'MOVE_ISSUE';
export const MOVE_ISSUE_SUCCESS = 'MOVE_ISSUE_SUCCESS';
export const MOVE_ISSUE_FAILURE = 'MOVE_ISSUE_FAILURE';
export const SET_BOARD_EPIC_USER_PREFERENCES = 'SET_BOARD_EPIC_USER_PREFERENCES';
......@@ -155,4 +155,14 @@ export default {
removeIssueFromList({ state, listId: fromListId, issueId: issue.id });
addIssueToList({ state, listId: toListId, issueId: issue.id, moveBeforeId, moveAfterId });
},
[mutationTypes.SET_BOARD_EPIC_USER_PREFERENCES]: (state, val) => {
const { userPreferences, epicId } = val;
const epic = state.epics.filter(currentEpic => currentEpic.id === epicId)[0];
if (epic) {
Vue.set(epic, 'userPreferences', userPreferences);
}
},
};
---
title: Persist collapsed state to DB
merge_request: 43681
author:
type: added
......@@ -14,10 +14,13 @@ describe('EpicLane', () => {
const findByTestId = testId => wrapper.find(`[data-testid="${testId}"]`);
const updateBoardEpicUserPreferencesSpy = jest.fn();
const createStore = isLoading => {
return new Vuex.Store({
actions: {
fetchIssuesForEpic: jest.fn(),
updateBoardEpicUserPreferences: updateBoardEpicUserPreferencesSpy,
},
state: {
issuesByListId: mockIssuesByListId,
......@@ -76,13 +79,13 @@ describe('EpicLane', () => {
it('hides issues when collapsing', () => {
expect(wrapper.findAll(IssuesLaneList)).toHaveLength(wrapper.props('lists').length);
expect(wrapper.vm.isExpanded).toBe(true);
expect(wrapper.vm.isCollapsed).toBe(false);
findByTestId('epic-lane-chevron').vm.$emit('click');
return wrapper.vm.$nextTick().then(() => {
expect(wrapper.findAll(IssuesLaneList)).toHaveLength(0);
expect(wrapper.vm.isExpanded).toBe(false);
expect(wrapper.vm.isCollapsed).toBe(true);
});
});
......@@ -95,5 +98,26 @@ describe('EpicLane', () => {
expect(wrapper.find(GlLoadingIcon).exists()).toBe(true);
expect(findByTestId('epic-lane-issue-count').exists()).toBe(false);
});
it('invokes `updateBoardEpicUserPreferences` method on collapse', () => {
const collapsedValue = false;
expect(wrapper.vm.isCollapsed).toBe(collapsedValue);
findByTestId('epic-lane-chevron').vm.$emit('click');
return wrapper.vm.$nextTick().then(() => {
expect(updateBoardEpicUserPreferencesSpy).toHaveBeenCalled();
const payload = updateBoardEpicUserPreferencesSpy.mock.calls[0][1];
expect(payload).toEqual({
collapsed: !collapsedValue,
epicId: mockEpic.id,
});
expect(wrapper.vm.isCollapsed).toBe(true);
});
});
});
});
......@@ -164,6 +164,47 @@ describe('fetchEpicsSwimlanes', () => {
});
});
describe('updateBoardEpicUserPreferences', () => {
const state = {
endpoints: {
boardId: 1,
},
};
const queryResponse = (collapsed = false) => ({
data: {
updateBoardEpicUserPreferences: {
errors: [],
epicUserPreferences: { collapsed },
},
},
});
it('should send mutation', done => {
const collapsed = true;
jest.spyOn(gqlClient, 'mutate').mockResolvedValue(queryResponse(collapsed));
testAction(
actions.updateBoardEpicUserPreferences,
{ epicId: mockEpic.id, collapsed },
state,
[
{
payload: {
epicId: mockEpic.id,
userPreferences: {
collapsed,
},
},
type: types.SET_BOARD_EPIC_USER_PREFERENCES,
},
],
[],
done,
);
});
});
describe('setShowLabels', () => {
it('should commit mutation SET_SHOW_LABELS', done => {
const state = {
......
......@@ -319,3 +319,19 @@ describe('MOVE_ISSUE', () => {
expect(state.issues['437'].epic).toEqual(null);
});
});
describe('SET_BOARD_EPIC_USER_PREFERENCES', () => {
it('should replace userPreferences on the given epic', () => {
state = {
...state,
epics: mockEpics,
};
const epic = mockEpics[0];
const userPreferences = { collapsed: false };
mutations.SET_BOARD_EPIC_USER_PREFERENCES(state, { epicId: epic.id, userPreferences });
expect(state.epics[0].userPreferences).toEqual(userPreferences);
});
});
......@@ -27853,6 +27853,9 @@ msgstr ""
msgid "Unable to save your changes. Please try again."
msgstr ""
msgid "Unable to save your preference"
msgstr ""
msgid "Unable to schedule a pipeline to run immediately"
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