Commit 44ea3a4c authored by Enrique Alcántara's avatar Enrique Alcántara

Merge branch 'ss/sidebar-state' into 'master'

Boards Sidebar state

See merge request gitlab-org/gitlab!40553
parents b95dbfc3 e9e76315
...@@ -6,20 +6,29 @@ export function getMilestone() { ...@@ -6,20 +6,29 @@ export function getMilestone() {
} }
export function formatListIssues(listIssues) { export function formatListIssues(listIssues) {
return listIssues.nodes.reduce((map, list) => { const issues = {};
const listData = listIssues.nodes.reduce((map, list) => {
return { return {
...map, ...map,
[list.id]: list.issues.nodes.map( [list.id]: list.issues.nodes.map(i => {
i => const id = getIdFromGraphQLId(i.id);
new ListIssue({
...i, const listIssue = new ListIssue({
id: getIdFromGraphQLId(i.id), ...i,
labels: i.labels?.nodes || [], id,
assignees: i.assignees?.nodes || [], labels: i.labels?.nodes || [],
}), assignees: i.assignees?.nodes || [],
), });
issues[id] = listIssue;
return id;
}),
}; };
}, {}); }, {});
return { listData, issues };
} }
export function fullBoardId(boardId) { export function fullBoardId(boardId) {
......
...@@ -10,4 +10,11 @@ export default { ...@@ -10,4 +10,11 @@ export default {
return state.isShowingEpicsSwimlanes; return state.isShowingEpicsSwimlanes;
}, },
getIssueById: state => id => {
return state.issues[id] || {};
},
getActiveIssue: state => {
return state.issues[state.activeId] || {};
},
}; };
...@@ -72,8 +72,9 @@ export default { ...@@ -72,8 +72,9 @@ export default {
state.isLoadingIssues = true; state.isLoadingIssues = true;
}, },
[mutationTypes.RECEIVE_ISSUES_FOR_ALL_LISTS_SUCCESS]: (state, listIssues) => { [mutationTypes.RECEIVE_ISSUES_FOR_ALL_LISTS_SUCCESS]: (state, { listData, issues }) => {
state.issuesByListId = listIssues; state.issuesByListId = listData;
state.issues = issues;
state.isLoadingIssues = false; state.isLoadingIssues = false;
}, },
......
...@@ -10,6 +10,7 @@ export default () => ({ ...@@ -10,6 +10,7 @@ export default () => ({
sidebarType: '', sidebarType: '',
boardLists: [], boardLists: [],
issuesByListId: {}, issuesByListId: {},
issues: {},
isLoadingIssues: false, isLoadingIssues: false,
filterParams: {}, filterParams: {},
error: undefined, error: undefined,
......
...@@ -10,11 +10,14 @@ export default { ...@@ -10,11 +10,14 @@ export default {
GlDrawer, GlDrawer,
}, },
computed: { computed: {
...mapGetters(['isSidebarOpen']), ...mapGetters(['isSidebarOpen', 'getActiveIssue']),
...mapState(['sidebarType']), ...mapState(['sidebarType']),
showSidebar() { showSidebar() {
return this.sidebarType === ISSUABLE; return this.sidebarType === ISSUABLE;
}, },
issueTitle() {
return this.getActiveIssue.title;
},
}, },
methods: { methods: {
...mapActions(['unsetActiveId']), ...mapActions(['unsetActiveId']),
...@@ -28,5 +31,12 @@ export default { ...@@ -28,5 +31,12 @@ export default {
:open="isSidebarOpen" :open="isSidebarOpen"
:header-height="$options.headerHeight" :header-height="$options.headerHeight"
@close="unsetActiveId" @close="unsetActiveId"
/> >
<template #header>
<div data-testid="issue-title">
<p class="gl-font-weight-bold">{{ issueTitle }}</p>
<p class="gl-mb-0">{{ getActiveIssue.referencePath }}</p>
</div>
</template>
</gl-drawer>
</template> </template>
...@@ -3,8 +3,10 @@ import gettersCE from '~/boards/stores/getters'; ...@@ -3,8 +3,10 @@ import gettersCE from '~/boards/stores/getters';
export default { export default {
...gettersCE, ...gettersCE,
getIssues: state => listId => { getIssues: (state, getters) => listId => {
return state.issuesByListId[listId] || []; const listIssueIds = state.issuesByListId[listId] || [];
return listIssueIds.map(id => getters.getIssueById(id));
}, },
getIssuesByEpic: (state, getters) => (listId, epicId) => { getIssuesByEpic: (state, getters) => (listId, epicId) => {
return getters.getIssues(listId).filter(issue => issue.epic && issue.epic.id === epicId); return getters.getIssues(listId).filter(issue => issue.epic && issue.epic.id === epicId);
......
import { shallowMount } from '@vue/test-utils'; import { mount } from '@vue/test-utils';
import { GlDrawer } from '@gitlab/ui'; import { GlDrawer } from '@gitlab/ui';
import waitForPromises from 'helpers/wait_for_promises'; import waitForPromises from 'helpers/wait_for_promises';
import BoardContentSidebar from 'ee_component/boards/components/board_content_sidebar.vue'; import BoardContentSidebar from 'ee_component/boards/components/board_content_sidebar.vue';
...@@ -10,7 +10,7 @@ describe('ee/BoardContentSidebar', () => { ...@@ -10,7 +10,7 @@ describe('ee/BoardContentSidebar', () => {
let store; let store;
const createComponent = () => { const createComponent = () => {
wrapper = shallowMount(BoardContentSidebar, { wrapper = mount(BoardContentSidebar, {
store, store,
}); });
}; };
...@@ -19,6 +19,8 @@ describe('ee/BoardContentSidebar', () => { ...@@ -19,6 +19,8 @@ describe('ee/BoardContentSidebar', () => {
store = createStore(); store = createStore();
store.state.sidebarType = ISSUABLE; store.state.sidebarType = ISSUABLE;
store.state.activeId = 1; store.state.activeId = 1;
store.state.issues = { '1': { title: 'One', referencePath: 'path' } };
store.state.activeId = '1';
createComponent(); createComponent();
}); });
...@@ -35,6 +37,14 @@ describe('ee/BoardContentSidebar', () => { ...@@ -35,6 +37,14 @@ describe('ee/BoardContentSidebar', () => {
expect(wrapper.find(GlDrawer).props('open')).toBe(true); expect(wrapper.find(GlDrawer).props('open')).toBe(true);
}); });
it('renders a title of an issue in the sidebar', () => {
expect(wrapper.find('[data-testid="issue-title"]').text()).toContain('One');
});
it('renders a referencePath of an issue in the sidebar', () => {
expect(wrapper.find('[data-testid="issue-title"]').text()).toContain('path');
});
describe('when we emit close', () => { describe('when we emit close', () => {
it('hides GlDrawer', async () => { it('hides GlDrawer', async () => {
expect(wrapper.find(GlDrawer).props('open')).toBe(true); expect(wrapper.find(GlDrawer).props('open')).toBe(true);
......
...@@ -4,7 +4,7 @@ import EpicLane from 'ee/boards/components/epic_lane.vue'; ...@@ -4,7 +4,7 @@ import EpicLane from 'ee/boards/components/epic_lane.vue';
import IssuesLaneList from 'ee/boards/components/issues_lane_list.vue'; import IssuesLaneList from 'ee/boards/components/issues_lane_list.vue';
import { GlIcon } from '@gitlab/ui'; import { GlIcon } from '@gitlab/ui';
import getters from 'ee/boards/stores/getters'; import getters from 'ee/boards/stores/getters';
import { mockEpic, mockListsWithModel, mockIssuesByListId } from '../mock_data'; import { mockEpic, mockListsWithModel, mockIssuesByListId, issues } from '../mock_data';
const localVue = createLocalVue(); const localVue = createLocalVue();
localVue.use(Vuex); localVue.use(Vuex);
...@@ -16,6 +16,7 @@ describe('EpicLane', () => { ...@@ -16,6 +16,7 @@ describe('EpicLane', () => {
return new Vuex.Store({ return new Vuex.Store({
state: { state: {
issuesByListId: mockIssuesByListId, issuesByListId: mockIssuesByListId,
issues,
}, },
getters, getters,
}); });
......
...@@ -6,7 +6,7 @@ import EpicLane from 'ee/boards/components/epic_lane.vue'; ...@@ -6,7 +6,7 @@ import EpicLane from 'ee/boards/components/epic_lane.vue';
import IssueLaneList from 'ee/boards/components/issues_lane_list.vue'; import IssueLaneList from 'ee/boards/components/issues_lane_list.vue';
import getters from 'ee/boards/stores/getters'; import getters from 'ee/boards/stores/getters';
import { GlIcon } from '@gitlab/ui'; import { GlIcon } from '@gitlab/ui';
import { mockListsWithModel, mockEpics, mockIssuesByListId } from '../mock_data'; import { mockListsWithModel, mockEpics, mockIssuesByListId, issues } from '../mock_data';
const localVue = createLocalVue(); const localVue = createLocalVue();
localVue.use(Vuex); localVue.use(Vuex);
...@@ -23,6 +23,7 @@ describe('EpicsSwimlanes', () => { ...@@ -23,6 +23,7 @@ describe('EpicsSwimlanes', () => {
epics: mockEpics, epics: mockEpics,
isLoadingIssues: false, isLoadingIssues: false,
issuesByListId: mockIssuesByListId, issuesByListId: mockIssuesByListId,
issues,
}, },
getters, getters,
}); });
......
...@@ -216,6 +216,13 @@ export const mockEpics = [ ...@@ -216,6 +216,13 @@ export const mockEpics = [
]; ];
export const mockIssuesByListId = { export const mockIssuesByListId = {
'gid://gitlab/List/1': [mockIssue, mockIssue3, mockIssue4], 'gid://gitlab/List/1': [mockIssue.id, mockIssue3.id, mockIssue4.id],
'gid://gitlab/List/2': mockIssues, 'gid://gitlab/List/2': mockIssues.map(({ id }) => id),
};
export const issues = {
[mockIssue.id]: mockIssue,
[mockIssue2.id]: mockIssue2,
[mockIssue3.id]: mockIssue3,
[mockIssue4.id]: mockIssue4,
}; };
import getters from 'ee/boards/stores/getters'; import getters from 'ee/boards/stores/getters';
import { mockIssue, mockIssue3, mockIssue4, mockIssues, mockIssuesByListId } from '../mock_data'; import {
mockIssue,
mockIssue2,
mockIssue3,
mockIssue4,
mockIssues,
mockIssuesByListId,
issues,
} from '../mock_data';
describe('EE Boards Store Getters', () => { describe('EE Boards Store Getters', () => {
const boardsState = { const boardsState = {
issuesByListId: mockIssuesByListId, issuesByListId: mockIssuesByListId,
issues,
}; };
describe('getIssues', () => { describe('getIssues', () => {
it('returns issues for a given listId', () => { it('returns issues for a given listId', () => {
expect(getters.getIssues(boardsState)('gid://gitlab/List/2')).toEqual(mockIssues); const getIssueById = issueId => [mockIssue, mockIssue2].find(({ id }) => id === issueId);
expect(getters.getIssues(boardsState, { getIssueById })('gid://gitlab/List/2')).toEqual(
mockIssues,
);
}); });
}); });
......
...@@ -91,4 +91,28 @@ describe('Boards - Getters', () => { ...@@ -91,4 +91,28 @@ describe('Boards - Getters', () => {
}); });
}); });
}); });
describe('getIssueById', () => {
const state = { issues: { '1': 'issue' } };
it.each`
id | expected
${'1'} | ${'issue'}
${''} | ${{}}
`('returns $expected when $id is passed to state', ({ id, expected }) => {
expect(getters.getIssueById(state)(id)).toEqual(expected);
});
});
describe('getActiveIssue', () => {
it.each`
id | expected
${'1'} | ${'issue'}
${''} | ${{}}
`('returns $expected when $id is passed to state', ({ id, expected }) => {
const state = { issues: { '1': 'issue' }, activeId: id };
expect(getters.getActiveIssue(state)).toEqual(expected);
});
});
}); });
...@@ -129,19 +129,24 @@ describe('Board Store Mutations', () => { ...@@ -129,19 +129,24 @@ describe('Board Store Mutations', () => {
describe('RECEIVE_ISSUES_FOR_ALL_LISTS_SUCCESS', () => { describe('RECEIVE_ISSUES_FOR_ALL_LISTS_SUCCESS', () => {
it('sets isLoadingIssues to false and updates issuesByListId object', () => { it('sets isLoadingIssues to false and updates issuesByListId object', () => {
const listIssues = { const listIssues = {
'1': [mockIssue], '1': [mockIssue.id],
};
const issues = {
'1': mockIssue,
}; };
state = { state = {
...state, ...state,
isLoadingIssues: true, isLoadingIssues: true,
issuesByListId: {}, issuesByListId: {},
issues: {},
}; };
mutations.RECEIVE_ISSUES_FOR_ALL_LISTS_SUCCESS(state, listIssues); mutations.RECEIVE_ISSUES_FOR_ALL_LISTS_SUCCESS(state, { listData: listIssues, issues });
expect(state.isLoadingIssues).toBe(false); expect(state.isLoadingIssues).toBe(false);
expect(state.issuesByListId).toEqual(listIssues); expect(state.issuesByListId).toEqual(listIssues);
expect(state.issues).toEqual(issues);
}); });
}); });
......
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