Commit ce2bf106 authored by Florie Guibert's avatar Florie Guibert

Swimlanes - Filters

VueX filtering for Swimlanes and GraphQL boards
parent 2b6d2bdd
...@@ -27,6 +27,11 @@ export default class FilteredSearchBoards extends FilteredSearchManager { ...@@ -27,6 +27,11 @@ export default class FilteredSearchBoards extends FilteredSearchManager {
updateObject(path) { updateObject(path) {
this.store.path = path.substr(1); this.store.path = path.substr(1);
if (gon.features.boardsWithSwimlanes) {
boardsStore.updateFiltersUrl();
boardsStore.performSearch();
}
if (this.updateUrl) { if (this.updateUrl) {
boardsStore.updateFiltersUrl(); boardsStore.updateFiltersUrl();
} }
......
import $ from 'jquery'; import $ from 'jquery';
import Vue from 'vue'; import Vue from 'vue';
import { mapActions } from 'vuex'; import { mapActions, mapState } from 'vuex';
import 'ee_else_ce/boards/models/issue'; import 'ee_else_ce/boards/models/issue';
import 'ee_else_ce/boards/models/list'; import 'ee_else_ce/boards/models/list';
...@@ -42,6 +42,7 @@ import { ...@@ -42,6 +42,7 @@ import {
NavigationType, NavigationType,
convertObjectPropsToCamelCase, convertObjectPropsToCamelCase,
parseBoolean, parseBoolean,
urlParamsToObject,
} from '~/lib/utils/common_utils'; } from '~/lib/utils/common_utils';
import { getIdFromGraphQLId } from '~/graphql_shared/utils'; import { getIdFromGraphQLId } from '~/graphql_shared/utils';
import mountMultipleBoardsSwitcher from './mount_multiple_boards_switcher'; import mountMultipleBoardsSwitcher from './mount_multiple_boards_switcher';
...@@ -104,6 +105,7 @@ export default () => { ...@@ -104,6 +105,7 @@ export default () => {
}; };
}, },
computed: { computed: {
...mapState(['isShowingEpicsSwimlanes']),
detailIssueVisible() { detailIssueVisible() {
return Object.keys(this.detailIssue.issue).length; return Object.keys(this.detailIssue.issue).length;
}, },
...@@ -125,17 +127,21 @@ export default () => { ...@@ -125,17 +127,21 @@ export default () => {
eventHub.$on('newDetailIssue', this.updateDetailIssue); eventHub.$on('newDetailIssue', this.updateDetailIssue);
eventHub.$on('clearDetailIssue', this.clearDetailIssue); eventHub.$on('clearDetailIssue', this.clearDetailIssue);
sidebarEventHub.$on('toggleSubscription', this.toggleSubscription); sidebarEventHub.$on('toggleSubscription', this.toggleSubscription);
eventHub.$on('performSearch', this.performSearch);
}, },
beforeDestroy() { beforeDestroy() {
eventHub.$off('updateTokens', this.updateTokens); eventHub.$off('updateTokens', this.updateTokens);
eventHub.$off('newDetailIssue', this.updateDetailIssue); eventHub.$off('newDetailIssue', this.updateDetailIssue);
eventHub.$off('clearDetailIssue', this.clearDetailIssue); eventHub.$off('clearDetailIssue', this.clearDetailIssue);
sidebarEventHub.$off('toggleSubscription', this.toggleSubscription); sidebarEventHub.$off('toggleSubscription', this.toggleSubscription);
eventHub.$off('performSearch', this.performSearch);
}, },
mounted() { mounted() {
this.filterManager = new FilteredSearchBoards(boardsStore.filter, true, boardsStore.cantEdit); this.filterManager = new FilteredSearchBoards(boardsStore.filter, true, boardsStore.cantEdit);
this.filterManager.setup(); this.filterManager.setup();
this.performSearch();
boardsStore.disabled = this.disabled; boardsStore.disabled = this.disabled;
if (gon.features.graphqlBoardLists) { if (gon.features.graphqlBoardLists) {
...@@ -189,10 +195,16 @@ export default () => { ...@@ -189,10 +195,16 @@ export default () => {
} }
}, },
methods: { methods: {
...mapActions(['setInitialBoardData']), ...mapActions(['setInitialBoardData', 'setFilters', 'fetchEpicsSwimlanes']),
updateTokens() { updateTokens() {
this.filterManager.updateTokens(); this.filterManager.updateTokens();
}, },
performSearch() {
this.setFilters(convertObjectPropsToCamelCase(urlParamsToObject(window.location.search)));
if (gon.features.boardsWithSwimlanes && this.isShowingEpicsSwimlanes) {
this.fetchEpicsSwimlanes(false);
}
},
updateDetailIssue(newIssue, multiSelect = false) { updateDetailIssue(newIssue, multiSelect = false) {
const { sidebarInfoEndpoint } = newIssue; const { sidebarInfoEndpoint } = newIssue;
if (sidebarInfoEndpoint && newIssue.subscribed === undefined) { if (sidebarInfoEndpoint && newIssue.subscribed === undefined) {
......
...@@ -21,6 +21,11 @@ export default { ...@@ -21,6 +21,11 @@ export default {
commit(types.SET_ACTIVE_ID, id); commit(types.SET_ACTIVE_ID, id);
}, },
setFilters: ({ commit }, filters) => {
const { scope, utf8, state, ...filterParams } = filters;
commit(types.SET_FILTERS, filterParams);
},
fetchLists: () => { fetchLists: () => {
notImplemented(); notImplemented();
}, },
......
...@@ -513,6 +513,10 @@ const boardsStore = { ...@@ -513,6 +513,10 @@ const boardsStore = {
eventHub.$emit('updateTokens'); eventHub.$emit('updateTokens');
}, },
performSearch() {
eventHub.$emit('performSearch');
},
setListDetail(newList) { setListDetail(newList) {
this.detail.list = newList; this.detail.list = newList;
}, },
......
export const SET_INITIAL_BOARD_DATA = 'SET_INITIAL_BOARD_DATA'; export const SET_INITIAL_BOARD_DATA = 'SET_INITIAL_BOARD_DATA';
export const SET_FILTERS = 'SET_FILTERS';
export const REQUEST_ADD_LIST = 'REQUEST_ADD_LIST'; export const REQUEST_ADD_LIST = 'REQUEST_ADD_LIST';
export const RECEIVE_ADD_LIST_SUCCESS = 'RECEIVE_ADD_LIST_SUCCESS'; export const RECEIVE_ADD_LIST_SUCCESS = 'RECEIVE_ADD_LIST_SUCCESS';
export const RECEIVE_ADD_LIST_ERROR = 'RECEIVE_ADD_LIST_ERROR'; export const RECEIVE_ADD_LIST_ERROR = 'RECEIVE_ADD_LIST_ERROR';
......
...@@ -17,6 +17,10 @@ export default { ...@@ -17,6 +17,10 @@ export default {
state.activeId = id; state.activeId = id;
}, },
[mutationTypes.SET_FILTERS](state, filterParams) {
state.filterParams = filterParams;
},
[mutationTypes.REQUEST_ADD_LIST]: () => { [mutationTypes.REQUEST_ADD_LIST]: () => {
notImplemented(); notImplemented();
}, },
......
...@@ -8,6 +8,7 @@ export default () => ({ ...@@ -8,6 +8,7 @@ export default () => ({
boardLists: [], boardLists: [],
issuesByListId: {}, issuesByListId: {},
isLoadingIssues: false, isLoadingIssues: false,
filterParams: {},
error: undefined, error: undefined,
// TODO: remove after ce/ee split of board_content.vue // TODO: remove after ce/ee split of board_content.vue
isShowingEpicsSwimlanes: false, isShowingEpicsSwimlanes: false,
......
#import "ee_else_ce/boards/queries/board_list.fragment.graphql" #import "ee_else_ce/boards/queries/board_list.fragment.graphql"
#import "~/graphql_shared/fragments/epic.fragment.graphql" #import "~/graphql_shared/fragments/epic.fragment.graphql"
query GroupBoardEE($fullPath: ID!, $boardId: ID!) { query BoardEE(
group(fullPath: $fullPath) { $fullPath: ID!
$boardId: ID!
$issueFilters: BoardEpicIssueInput
$withLists: Boolean = true
$isGroup: Boolean = false
$isProject: Boolean = false
) {
group(fullPath: $fullPath) @include(if: $isGroup) {
board(id: $boardId) { board(id: $boardId) {
lists { lists @include(if: $withLists) {
nodes { nodes {
...BoardListFragment ...BoardListFragment
} }
} }
epics { epics(issueFilters: $issueFilters) {
nodes {
...EpicNode
}
}
}
}
project(fullPath: $fullPath) @include(if: $isProject) {
board(id: $boardId) {
lists @include(if: $withLists) {
nodes {
...BoardListFragment
}
}
epics(issueFilters: $issueFilters) {
nodes { nodes {
...EpicNode ...EpicNode
} }
......
#import "ee_else_ce/boards/queries/board_list.fragment.graphql"
#import "~/graphql_shared/fragments/epic.fragment.graphql"
query ProjectBoardEE($fullPath: ID!, $boardId: ID!) {
project(fullPath: $fullPath) {
board(id: $boardId) {
lists {
nodes {
...BoardListFragment
}
}
epics {
nodes {
...EpicNode
}
}
}
}
}
...@@ -8,7 +8,6 @@ import * as types from './mutation_types'; ...@@ -8,7 +8,6 @@ import * as types from './mutation_types';
import createDefaultClient from '~/lib/graphql'; import createDefaultClient from '~/lib/graphql';
import { BoardType } from '~/boards/constants'; import { BoardType } from '~/boards/constants';
import groupEpicsSwimlanesQuery from '../queries/group_epics_swimlanes.query.graphql'; import groupEpicsSwimlanesQuery from '../queries/group_epics_swimlanes.query.graphql';
import projectEpicsSwimlanesQuery from '../queries/project_epics_swimlanes.query.graphql';
const notImplemented = () => { const notImplemented = () => {
/* eslint-disable-next-line @gitlab/require-i18n-strings */ /* eslint-disable-next-line @gitlab/require-i18n-strings */
...@@ -17,42 +16,49 @@ const notImplemented = () => { ...@@ -17,42 +16,49 @@ const notImplemented = () => {
const gqlClient = createDefaultClient(); const gqlClient = createDefaultClient();
const fetchEpicsSwimlanes = ({ endpoints, boardType }) => {
const { fullPath, boardId } = endpoints;
const query =
boardType === BoardType.group ? groupEpicsSwimlanesQuery : projectEpicsSwimlanesQuery;
const variables = {
fullPath,
boardId: `gid://gitlab/Board/${boardId}`,
};
return gqlClient
.query({
query,
variables,
})
.then(({ data }) => {
const { epics, lists } = data[boardType]?.board;
const epicsFormatted = epics.nodes.map(e => ({
...e,
issues: (e?.issues?.nodes || []).map(i => ({
...i,
labels: i.labels?.nodes || [],
assignees: i.assignees?.nodes || [],
})),
}));
return {
epics: epicsFormatted,
lists: lists.nodes,
};
});
};
export default { export default {
...actionsCE, ...actionsCE,
fetchEpicsSwimlanes({ state, commit }, withLists = true) {
const { endpoints, boardType, filterParams } = state;
const { fullPath, boardId } = endpoints;
const variables = {
fullPath,
boardId: `gid://gitlab/Board/${boardId}`,
issueFilters: filterParams,
withLists,
isGroup: boardType === BoardType.group,
isProject: boardType === BoardType.project,
};
return gqlClient
.query({
query: groupEpicsSwimlanesQuery,
variables,
})
.then(({ data }) => {
const { epics, lists } = data[boardType]?.board;
const epicsFormatted = epics.nodes.map(e => ({
...e,
issues: (e?.issues?.nodes || []).map(i => ({
...i,
labels: i.labels?.nodes || [],
assignees: i.assignees?.nodes || [],
})),
}));
if (!withLists) {
commit(types.RECEIVE_EPICS_SUCCESS, epicsFormatted);
}
return {
epics: epicsFormatted,
lists: lists?.nodes,
};
});
},
setShowLabels({ commit }, val) { setShowLabels({ commit }, val) {
commit(types.SET_SHOW_LABELS, val); commit(types.SET_SHOW_LABELS, val);
}, },
...@@ -91,11 +97,11 @@ export default { ...@@ -91,11 +97,11 @@ export default {
notImplemented(); notImplemented();
}, },
toggleEpicSwimlanes: ({ state, commit }) => { toggleEpicSwimlanes: ({ state, commit, dispatch }) => {
commit(types.TOGGLE_EPICS_SWIMLANES); commit(types.TOGGLE_EPICS_SWIMLANES);
if (state.isShowingEpicsSwimlanes) { if (state.isShowingEpicsSwimlanes) {
fetchEpicsSwimlanes(state) dispatch('fetchEpicsSwimlanes')
.then(({ lists, epics }) => { .then(({ lists, epics }) => {
if (lists) { if (lists) {
let boardLists = lists.map(list => let boardLists = lists.map(list =>
......
...@@ -26,6 +26,25 @@ describe('setInitialBoardData', () => { ...@@ -26,6 +26,25 @@ describe('setInitialBoardData', () => {
}); });
}); });
describe('setFilters', () => {
it('should commit mutation SET_FILTERS', done => {
const state = {
filters: {},
};
const filters = { labelName: 'label' };
testAction(
actions.setFilters,
filters,
state,
[{ type: types.SET_FILTERS, payload: filters }],
[],
done,
);
});
});
describe('setActiveId', () => { describe('setActiveId', () => {
it('should commit mutation SET_ACTIVE_ID', done => { it('should commit mutation SET_ACTIVE_ID', done => {
const state = { const state = {
......
...@@ -44,6 +44,16 @@ describe('Board Store Mutations', () => { ...@@ -44,6 +44,16 @@ describe('Board Store Mutations', () => {
}); });
}); });
describe('SET_FILTERS', () => {
it('updates filterParams to be the value that is passed', () => {
const filterParams = { labelName: 'label' };
mutations.SET_FILTERS(state, filterParams);
expect(state.filterParams).toBe(filterParams);
});
});
describe('REQUEST_ADD_LIST', () => { describe('REQUEST_ADD_LIST', () => {
expectNotImplemented(mutations.REQUEST_ADD_LIST); expectNotImplemented(mutations.REQUEST_ADD_LIST);
}); });
......
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