From 859a6fb938bb9ee2a317c46dfa4fcc1af49608f0 Mon Sep 17 00:00:00 2001
From: GitLab Bot
Date: Thu, 18 Feb 2021 10:34:06 +0000
Subject: Add latest changes from gitlab-org/gitlab@13-9-stable-ee
---
app/assets/javascripts/boards/boards_util.js | 14 +-
.../components/board_add_new_column_trigger.vue | 21 ++
.../boards/components/board_assignee_dropdown.vue | 196 -----------
.../javascripts/boards/components/board_card.vue | 7 +-
.../boards/components/board_card_layout.vue | 26 +-
.../components/board_card_layout_deprecated.vue | 102 ++++++
.../javascripts/boards/components/board_column.vue | 18 +-
.../boards/components/board_column_deprecated.vue | 16 +-
.../components/board_configuration_options.vue | 6 +
.../boards/components/board_content.vue | 12 +-
.../javascripts/boards/components/board_form.vue | 26 +-
.../javascripts/boards/components/board_list.vue | 10 +-
.../boards/components/board_list_deprecated.vue | 20 +-
.../boards/components/board_list_header.vue | 43 ++-
.../components/board_list_header_deprecated.vue | 41 ++-
.../boards/components/board_new_issue.vue | 6 +-
.../components/board_new_issue_deprecated.vue | 4 +-
.../boards/components/board_settings_sidebar.vue | 19 +-
.../javascripts/boards/components/board_sidebar.js | 67 +---
.../boards/components/boards_selector.vue | 38 ++-
.../components/boards_selector_deprecated.vue | 357 +++++++++++++++++++++
.../boards/components/issue_card_inner.vue | 10 +-
.../components/issue_card_inner_deprecated.vue | 6 +-
.../boards/components/issue_due_date.vue | 4 +-
.../boards/components/issue_time_estimate.vue | 2 +-
.../boards/components/modal/empty_state.vue | 2 +-
.../javascripts/boards/components/modal/filters.js | 2 +-
.../javascripts/boards/components/modal/footer.vue | 4 +-
.../javascripts/boards/components/modal/header.vue | 4 +-
.../javascripts/boards/components/modal/index.vue | 8 +-
.../javascripts/boards/components/modal/list.vue | 2 +-
.../boards/components/modal/lists_dropdown.vue | 2 +-
.../javascripts/boards/components/modal/tabs.vue | 2 +-
.../boards/components/new_list_dropdown.js | 41 ++-
.../boards/components/project_select.vue | 2 +-
.../components/project_select_deprecated.vue | 4 +-
.../components/sidebar/board_sidebar_due_date.vue | 20 +-
.../sidebar/board_sidebar_issue_title.vue | 62 ++--
.../sidebar/board_sidebar_labels_select.vue | 8 +-
.../sidebar/board_sidebar_milestone_select.vue | 86 +++--
.../sidebar/board_sidebar_subscription.vue | 2 +-
.../javascripts/boards/components/toggle_focus.vue | 52 +++
app/assets/javascripts/boards/constants.js | 20 ++
.../javascripts/boards/filtered_search_boards.js | 8 +-
.../javascripts/boards/filters/due_date_filters.js | 2 +-
.../boards/graphql/group_milestones.query.graphql | 17 -
.../graphql/project_milestones.query.graphql | 17 +
app/assets/javascripts/boards/index.js | 50 ++-
app/assets/javascripts/boards/models/issue.js | 6 +-
app/assets/javascripts/boards/models/iteration.js | 9 +
app/assets/javascripts/boards/models/list.js | 11 +-
.../boards/mount_multiple_boards_switcher.js | 20 +-
app/assets/javascripts/boards/stores/actions.js | 105 +++---
.../javascripts/boards/stores/boards_store.js | 22 +-
app/assets/javascripts/boards/stores/getters.js | 10 +-
app/assets/javascripts/boards/stores/index.js | 4 +-
.../javascripts/boards/stores/mutation_types.js | 7 +-
app/assets/javascripts/boards/stores/mutations.js | 34 +-
app/assets/javascripts/boards/stores/state.js | 4 +
app/assets/javascripts/boards/toggle_focus.js | 48 +--
60 files changed, 1128 insertions(+), 640 deletions(-)
create mode 100644 app/assets/javascripts/boards/components/board_add_new_column_trigger.vue
delete mode 100644 app/assets/javascripts/boards/components/board_assignee_dropdown.vue
create mode 100644 app/assets/javascripts/boards/components/board_card_layout_deprecated.vue
create mode 100644 app/assets/javascripts/boards/components/boards_selector_deprecated.vue
create mode 100644 app/assets/javascripts/boards/components/toggle_focus.vue
delete mode 100644 app/assets/javascripts/boards/graphql/group_milestones.query.graphql
create mode 100644 app/assets/javascripts/boards/graphql/project_milestones.query.graphql
create mode 100644 app/assets/javascripts/boards/models/iteration.js
(limited to 'app/assets/javascripts/boards')
diff --git a/app/assets/javascripts/boards/boards_util.js b/app/assets/javascripts/boards/boards_util.js
index 965d3571f42..13ad820477f 100644
--- a/app/assets/javascripts/boards/boards_util.js
+++ b/app/assets/javascripts/boards/boards_util.js
@@ -1,6 +1,6 @@
import { sortBy } from 'lodash';
-import { ListType } from './constants';
import { getIdFromGraphQLId } from '~/graphql_shared/utils';
+import { ListType, NOT_FILTER } from './constants';
export function getMilestone() {
return null;
@@ -144,6 +144,17 @@ export function isListDraggable(list) {
return list.listType !== ListType.backlog && list.listType !== ListType.closed;
}
+export function transformNotFilters(filters) {
+ return Object.keys(filters)
+ .filter((key) => key.startsWith(NOT_FILTER))
+ .reduce((obj, key) => {
+ return {
+ ...obj,
+ [key.substring(4, key.length - 1)]: filters[key],
+ };
+ }, {});
+}
+
// EE-specific feature. Find the implementation in the `ee/`-folder
export function transformBoardConfig() {
return '';
@@ -157,4 +168,5 @@ export default {
fullLabelId,
fullIterationId,
isListDraggable,
+ transformNotFilters,
};
diff --git a/app/assets/javascripts/boards/components/board_add_new_column_trigger.vue b/app/assets/javascripts/boards/components/board_add_new_column_trigger.vue
new file mode 100644
index 00000000000..85fca589279
--- /dev/null
+++ b/app/assets/javascripts/boards/components/board_add_new_column_trigger.vue
@@ -0,0 +1,21 @@
+
+
+
+
+ {{ __('Create list') }}
+
+
+
diff --git a/app/assets/javascripts/boards/components/board_assignee_dropdown.vue b/app/assets/javascripts/boards/components/board_assignee_dropdown.vue
deleted file mode 100644
index 5d381f9a570..00000000000
--- a/app/assets/javascripts/boards/components/board_assignee_dropdown.vue
+++ /dev/null
@@ -1,196 +0,0 @@
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
- {{ $options.i18n.unassigned }}
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
diff --git a/app/assets/javascripts/boards/components/board_card.vue b/app/assets/javascripts/boards/components/board_card.vue
index 31050eef83d..e6009343626 100644
--- a/app/assets/javascripts/boards/components/board_card.vue
+++ b/app/assets/javascripts/boards/components/board_card.vue
@@ -1,13 +1,14 @@
+
+
+
+
+
+
diff --git a/app/assets/javascripts/boards/components/board_column.vue b/app/assets/javascripts/boards/components/board_column.vue
index 9f0eef844f6..41b9ee795eb 100644
--- a/app/assets/javascripts/boards/components/board_column.vue
+++ b/app/assets/javascripts/boards/components/board_column.vue
@@ -1,8 +1,8 @@
@@ -28,12 +32,14 @@ export default {
{{ __('Show the Open list') }}
{{ __('Show the Closed list') }}
diff --git a/app/assets/javascripts/boards/components/board_content.vue b/app/assets/javascripts/boards/components/board_content.vue
index 19254343208..9b10e7d7db5 100644
--- a/app/assets/javascripts/boards/components/board_content.vue
+++ b/app/assets/javascripts/boards/components/board_content.vue
@@ -1,13 +1,13 @@
+
+
+
+
+
+
+
+
+
+
+ {{ s__('IssueBoards|No matching boards found') }}
+
+
+
+ {{ __('Recent') }}
+
+
+
+
+ {{ recentBoard.name }}
+
+
+
+
+
+
+ {{ __('All') }}
+
+
+
+ {{ otherBoard.name }}
+
+
+
+ {{
+ s__(
+ 'IssueBoards|Some of your boards are hidden, activate a license to see them again.',
+ )
+ }}
+
+
+
+
+
+
+
+
+
+
+
+ {{ s__('IssueBoards|Create new board') }}
+
+
+
+ {{ s__('IssueBoards|Delete board') }}
+
+
+
+
+
+
+
+
diff --git a/app/assets/javascripts/boards/components/issue_card_inner.vue b/app/assets/javascripts/boards/components/issue_card_inner.vue
index 457d0d4dcd6..e5ea30df767 100644
--- a/app/assets/javascripts/boards/components/issue_card_inner.vue
+++ b/app/assets/javascripts/boards/components/issue_card_inner.vue
@@ -1,17 +1,17 @@
+
+
+
+
diff --git a/app/assets/javascripts/boards/constants.js b/app/assets/javascripts/boards/constants.js
index 9264fac5eda..3ab89b2c9da 100644
--- a/app/assets/javascripts/boards/constants.js
+++ b/app/assets/javascripts/boards/constants.js
@@ -1,3 +1,5 @@
+import { __ } from '~/locale';
+
export const BoardType = {
project: 'project',
group: 'group',
@@ -6,16 +8,34 @@ export const BoardType = {
export const ListType = {
assignee: 'assignee',
milestone: 'milestone',
+ iteration: 'iteration',
backlog: 'backlog',
closed: 'closed',
label: 'label',
};
+export const ListTypeTitles = {
+ assignee: __('Assignee'),
+ milestone: __('Milestone'),
+ iteration: __('Iteration'),
+ label: __('Label'),
+};
+
+export const formType = {
+ new: 'new',
+ delete: 'delete',
+ edit: 'edit',
+};
+
export const inactiveId = 0;
export const ISSUABLE = 'issuable';
export const LIST = 'list';
+export const NOT_FILTER = 'not[';
+
+export const flashAnimationDuration = 2000;
+
export default {
BoardType,
ListType,
diff --git a/app/assets/javascripts/boards/filtered_search_boards.js b/app/assets/javascripts/boards/filtered_search_boards.js
index 94b35aadaf1..66580bdd30f 100644
--- a/app/assets/javascripts/boards/filtered_search_boards.js
+++ b/app/assets/javascripts/boards/filtered_search_boards.js
@@ -1,10 +1,10 @@
-import IssuableFilteredSearchTokenKeys from 'ee_else_ce/filtered_search/issuable_filtered_search_token_keys';
-import FilteredSearchManager from 'ee_else_ce/filtered_search/filtered_search_manager';
import { transformBoardConfig } from 'ee_else_ce/boards/boards_util';
+import FilteredSearchManager from 'ee_else_ce/filtered_search/filtered_search_manager';
+import IssuableFilteredSearchTokenKeys from 'ee_else_ce/filtered_search/issuable_filtered_search_token_keys';
+import { updateHistory } from '~/lib/utils/url_utility';
import FilteredSearchContainer from '../filtered_search/container';
-import boardsStore from './stores/boards_store';
import vuexstore from './stores';
-import { updateHistory } from '~/lib/utils/url_utility';
+import boardsStore from './stores/boards_store';
export default class FilteredSearchBoards extends FilteredSearchManager {
constructor(store, updateUrl = false, cantEdit = []) {
diff --git a/app/assets/javascripts/boards/filters/due_date_filters.js b/app/assets/javascripts/boards/filters/due_date_filters.js
index c35dedde71b..1745ab3bab4 100644
--- a/app/assets/javascripts/boards/filters/due_date_filters.js
+++ b/app/assets/javascripts/boards/filters/due_date_filters.js
@@ -1,5 +1,5 @@
-import Vue from 'vue';
import dateFormat from 'dateformat';
+import Vue from 'vue';
Vue.filter('due-date', (value) => {
const date = new Date(value);
diff --git a/app/assets/javascripts/boards/graphql/group_milestones.query.graphql b/app/assets/javascripts/boards/graphql/group_milestones.query.graphql
deleted file mode 100644
index f2ab12ef4a7..00000000000
--- a/app/assets/javascripts/boards/graphql/group_milestones.query.graphql
+++ /dev/null
@@ -1,17 +0,0 @@
-query groupMilestones(
- $fullPath: ID!
- $state: MilestoneStateEnum
- $includeDescendants: Boolean
- $searchTitle: String
-) {
- group(fullPath: $fullPath) {
- milestones(state: $state, includeDescendants: $includeDescendants, searchTitle: $searchTitle) {
- edges {
- node {
- id
- title
- }
- }
- }
- }
-}
diff --git a/app/assets/javascripts/boards/graphql/project_milestones.query.graphql b/app/assets/javascripts/boards/graphql/project_milestones.query.graphql
new file mode 100644
index 00000000000..776530ebb83
--- /dev/null
+++ b/app/assets/javascripts/boards/graphql/project_milestones.query.graphql
@@ -0,0 +1,17 @@
+query groupMilestones(
+ $fullPath: ID!
+ $state: MilestoneStateEnum
+ $includeAncestors: Boolean
+ $searchTitle: String
+) {
+ project(fullPath: $fullPath) {
+ milestones(state: $state, includeAncestors: $includeAncestors, searchTitle: $searchTitle) {
+ edges {
+ node {
+ id
+ title
+ }
+ }
+ }
+ }
+}
diff --git a/app/assets/javascripts/boards/index.js b/app/assets/javascripts/boards/index.js
index ef70a094f7c..859295318ed 100644
--- a/app/assets/javascripts/boards/index.js
+++ b/app/assets/javascripts/boards/index.js
@@ -1,4 +1,5 @@
import Vue from 'vue';
+import VueApollo from 'vue-apollo';
import { mapActions, mapGetters } from 'vuex';
import 'ee_else_ce/boards/models/issue';
@@ -6,41 +7,39 @@ import 'ee_else_ce/boards/models/list';
import BoardSidebar from 'ee_else_ce/boards/components/board_sidebar';
import initNewListDropdown from 'ee_else_ce/boards/components/new_list_dropdown';
import boardConfigToggle from 'ee_else_ce/boards/config_toggle';
-import toggleLabels from 'ee_else_ce/boards/toggle_labels';
-import toggleEpicsSwimlanes from 'ee_else_ce/boards/toggle_epics_swimlanes';
import {
setWeightFetchingState,
setEpicFetchingState,
getMilestoneTitle,
getBoardsModalData,
} from 'ee_else_ce/boards/ee_functions';
-
-import VueApollo from 'vue-apollo';
+import toggleEpicsSwimlanes from 'ee_else_ce/boards/toggle_epics_swimlanes';
+import toggleLabels from 'ee_else_ce/boards/toggle_labels';
+import BoardAddNewColumnTrigger from '~/boards/components/board_add_new_column_trigger.vue';
import BoardContent from '~/boards/components/board_content.vue';
import BoardExtraActions from '~/boards/components/board_extra_actions.vue';
-import createDefaultClient from '~/lib/graphql';
-import { deprecatedCreateFlash as Flash } from '~/flash';
-import { __ } from '~/locale';
import './models/label';
import './models/assignee';
-
-import toggleFocusMode from '~/boards/toggle_focus';
-import FilteredSearchBoards from '~/boards/filtered_search_boards';
-import eventHub from '~/boards/eventhub';
-import sidebarEventHub from '~/sidebar/event_hub';
import '~/boards/models/milestone';
import '~/boards/models/project';
+import '~/boards/filters/due_date_filters';
+import BoardAddIssuesModal from '~/boards/components/modal/index.vue';
+import eventHub from '~/boards/eventhub';
+import FilteredSearchBoards from '~/boards/filtered_search_boards';
+import modalMixin from '~/boards/mixins/modal_mixins';
import store from '~/boards/stores';
import boardsStore from '~/boards/stores/boards_store';
import ModalStore from '~/boards/stores/modal_store';
-import modalMixin from '~/boards/mixins/modal_mixins';
-import '~/boards/filters/due_date_filters';
-import BoardAddIssuesModal from '~/boards/components/modal/index.vue';
+import toggleFocusMode from '~/boards/toggle_focus';
+import { deprecatedCreateFlash as Flash } from '~/flash';
+import createDefaultClient from '~/lib/graphql';
import {
NavigationType,
convertObjectPropsToCamelCase,
parseBoolean,
} from '~/lib/utils/common_utils';
+import { __ } from '~/locale';
+import sidebarEventHub from '~/sidebar/event_hub';
import mountMultipleBoardsSwitcher from './mount_multiple_boards_switcher';
Vue.use(VueApollo);
@@ -73,6 +72,7 @@ export default () => {
boardsStore.setTimeTrackingLimitToHours($boardApp.dataset.timeTrackingLimitToHours);
}
+ // eslint-disable-next-line @gitlab/no-runtime-template-compiler
issueBoardsApp = new Vue({
el: $boardApp,
components: {
@@ -86,7 +86,7 @@ export default () => {
groupId: Number($boardApp.dataset.groupId),
rootPath: $boardApp.dataset.rootPath,
currentUserId: gon.current_user_id || null,
- canUpdate: $boardApp.dataset.canUpdate,
+ canUpdate: parseBoolean($boardApp.dataset.canUpdate),
labelsFetchPath: $boardApp.dataset.labelsFetchPath,
labelsManagePath: $boardApp.dataset.labelsManagePath,
labelsFilterBasePath: $boardApp.dataset.labelsFilterBasePath,
@@ -275,7 +275,7 @@ export default () => {
},
});
- // eslint-disable-next-line no-new
+ // eslint-disable-next-line no-new, @gitlab/no-runtime-template-compiler
new Vue({
el: document.getElementById('js-add-list'),
data: {
@@ -287,6 +287,21 @@ export default () => {
},
});
+ const createColumnTriggerEl = document.querySelector('.js-create-column-trigger');
+ if (createColumnTriggerEl) {
+ // eslint-disable-next-line no-new
+ new Vue({
+ el: createColumnTriggerEl,
+ components: {
+ BoardAddNewColumnTrigger,
+ },
+ store,
+ render(createElement) {
+ return createElement('board-add-new-column-trigger');
+ },
+ });
+ }
+
boardConfigToggle(boardsStore);
const issueBoardsModal = document.getElementById('js-add-issues-btn');
@@ -341,5 +356,6 @@ export default () => {
mountMultipleBoardsSwitcher({
fullPath: $boardApp.dataset.fullPath,
rootPath: $boardApp.dataset.boardsEndpoint,
+ recentBoardsEndpoint: $boardApp.dataset.recentBoardsEndpoint,
});
};
diff --git a/app/assets/javascripts/boards/models/issue.js b/app/assets/javascripts/boards/models/issue.js
index 1e77326ba9c..46d1239457d 100644
--- a/app/assets/javascripts/boards/models/issue.js
+++ b/app/assets/javascripts/boards/models/issue.js
@@ -6,8 +6,8 @@
import axios from '~/lib/utils/axios_utils';
import './label';
import { convertObjectPropsToCamelCase } from '~/lib/utils/common_utils';
-import IssueProject from './project';
import boardsStore from '../stores/boards_store';
+import IssueProject from './project';
class ListIssue {
constructor(obj) {
@@ -53,6 +53,10 @@ class ListIssue {
return boardsStore.findIssueAssignee(this, findAssignee);
}
+ setAssignees(assignees) {
+ boardsStore.setIssueAssignees(this, assignees);
+ }
+
removeAssignee(removeAssignee) {
boardsStore.removeIssueAssignee(this, removeAssignee);
}
diff --git a/app/assets/javascripts/boards/models/iteration.js b/app/assets/javascripts/boards/models/iteration.js
new file mode 100644
index 00000000000..b7bdc204f7c
--- /dev/null
+++ b/app/assets/javascripts/boards/models/iteration.js
@@ -0,0 +1,9 @@
+export default class ListIteration {
+ constructor(obj) {
+ this.id = obj.id;
+ this.title = obj.title;
+ this.state = obj.state;
+ this.webUrl = obj.web_url || obj.webUrl;
+ this.description = obj.description;
+ }
+}
diff --git a/app/assets/javascripts/boards/models/list.js b/app/assets/javascripts/boards/models/list.js
index be02ac7b889..6c6e2522d92 100644
--- a/app/assets/javascripts/boards/models/list.js
+++ b/app/assets/javascripts/boards/models/list.js
@@ -1,9 +1,10 @@
/* eslint-disable class-methods-use-this */
-import { __ } from '~/locale';
-import ListLabel from './label';
-import ListAssignee from './assignee';
import { deprecatedCreateFlash as flash } from '~/flash';
+import { __ } from '~/locale';
import boardsStore from '../stores/boards_store';
+import ListAssignee from './assignee';
+import ListIteration from './iteration';
+import ListLabel from './label';
import ListMilestone from './milestone';
import 'ee_else_ce/boards/models/issue';
@@ -43,6 +44,7 @@ class List {
this.isExpandable = Boolean(typeInfo.isExpandable);
this.isExpanded = !obj.collapsed;
this.page = 1;
+ this.highlighted = obj.highlighted;
this.loading = true;
this.loadingMore = false;
this.issues = obj.issues || [];
@@ -57,6 +59,9 @@ class List {
} else if (IS_EE && obj.milestone) {
this.milestone = new ListMilestone(obj.milestone);
this.title = this.milestone.title;
+ } else if (IS_EE && obj.iteration) {
+ this.iteration = new ListIteration(obj.iteration);
+ this.title = this.iteration.title;
}
// doNotFetchIssues is a temporary workaround until issues are fetched using GraphQL on issue boards
diff --git a/app/assets/javascripts/boards/mount_multiple_boards_switcher.js b/app/assets/javascripts/boards/mount_multiple_boards_switcher.js
index 738c8fb927e..fa58af24ba2 100644
--- a/app/assets/javascripts/boards/mount_multiple_boards_switcher.js
+++ b/app/assets/javascripts/boards/mount_multiple_boards_switcher.js
@@ -1,8 +1,12 @@
import Vue from 'vue';
import VueApollo from 'vue-apollo';
+import { mapGetters } from 'vuex';
+import BoardsSelector from '~/boards/components/boards_selector.vue';
+import BoardsSelectorDeprecated from '~/boards/components/boards_selector_deprecated.vue';
+import store from '~/boards/stores';
import createDefaultClient from '~/lib/graphql';
import { parseBoolean } from '~/lib/utils/common_utils';
-import BoardsSelector from '~/boards/components/boards_selector.vue';
+import glFeatureFlagMixin from '~/vue_shared/mixins/gl_feature_flags_mixin';
Vue.use(VueApollo);
@@ -16,11 +20,15 @@ export default (params = {}) => {
el: boardsSwitcherElement,
components: {
BoardsSelector,
+ BoardsSelectorDeprecated,
},
+ mixins: [glFeatureFlagMixin()],
apolloProvider,
+ store,
provide: {
fullPath: params.fullPath,
rootPath: params.rootPath,
+ recentBoardsEndpoint: params.recentBoardsEndpoint,
},
data() {
const { dataset } = boardsSwitcherElement;
@@ -39,8 +47,16 @@ export default (params = {}) => {
return { boardsSelectorProps };
},
+ computed: {
+ ...mapGetters(['shouldUseGraphQL']),
+ },
render(createElement) {
- return createElement(BoardsSelector, {
+ if (this.shouldUseGraphQL) {
+ return createElement(BoardsSelector, {
+ props: this.boardsSelectorProps,
+ });
+ }
+ return createElement(BoardsSelectorDeprecated, {
props: this.boardsSelectorProps,
});
},
diff --git a/app/assets/javascripts/boards/stores/actions.js b/app/assets/javascripts/boards/stores/actions.js
index 1d34f21798a..a7cf1e9e647 100644
--- a/app/assets/javascripts/boards/stores/actions.js
+++ b/app/assets/javascripts/boards/stores/actions.js
@@ -1,11 +1,9 @@
import { pick } from 'lodash';
-
import boardListsQuery from 'ee_else_ce/boards/graphql/board_lists.query.graphql';
-import createGqClient, { fetchPolicies } from '~/lib/graphql';
+import { BoardType, ListType, inactiveId, flashAnimationDuration } from '~/boards/constants';
import { getIdFromGraphQLId } from '~/graphql_shared/utils';
+import createGqClient, { fetchPolicies } from '~/lib/graphql';
import { convertObjectPropsToCamelCase, urlParamsToObject } from '~/lib/utils/common_utils';
-import { BoardType, ListType, inactiveId } from '~/boards/constants';
-import * as types from './mutation_types';
import {
formatBoardLists,
formatListIssues,
@@ -14,23 +12,22 @@ import {
formatIssue,
formatIssueInput,
updateListPosition,
+ transformNotFilters,
} from '../boards_util';
-import createFlash from '~/flash';
-import { __ } from '~/locale';
-import updateAssigneesMutation from '~/vue_shared/components/sidebar/queries/updateAssignees.mutation.graphql';
-import listsIssuesQuery from '../graphql/lists_issues.query.graphql';
import boardLabelsQuery from '../graphql/board_labels.query.graphql';
import createBoardListMutation from '../graphql/board_list_create.mutation.graphql';
-import updateBoardListMutation from '../graphql/board_list_update.mutation.graphql';
-import issueMoveListMutation from '../graphql/issue_move_list.mutation.graphql';
import destroyBoardListMutation from '../graphql/board_list_destroy.mutation.graphql';
+import updateBoardListMutation from '../graphql/board_list_update.mutation.graphql';
+import groupProjectsQuery from '../graphql/group_projects.query.graphql';
import issueCreateMutation from '../graphql/issue_create.mutation.graphql';
-import issueSetLabelsMutation from '../graphql/issue_set_labels.mutation.graphql';
+import issueMoveListMutation from '../graphql/issue_move_list.mutation.graphql';
import issueSetDueDateMutation from '../graphql/issue_set_due_date.mutation.graphql';
-import issueSetSubscriptionMutation from '../graphql/issue_set_subscription.mutation.graphql';
+import issueSetLabelsMutation from '../graphql/issue_set_labels.mutation.graphql';
import issueSetMilestoneMutation from '../graphql/issue_set_milestone.mutation.graphql';
+import issueSetSubscriptionMutation from '../graphql/issue_set_subscription.mutation.graphql';
import issueSetTitleMutation from '../graphql/issue_set_title.mutation.graphql';
-import groupProjectsQuery from '../graphql/group_projects.query.graphql';
+import listsIssuesQuery from '../graphql/lists_issues.query.graphql';
+import * as types from './mutation_types';
const notImplemented = () => {
/* eslint-disable-next-line @gitlab/require-i18n-strings */
@@ -66,6 +63,7 @@ export default {
'releaseTag',
'search',
]);
+ filterParams.not = transformNotFilters(filters);
commit(types.SET_FILTERS, filterParams);
},
@@ -108,9 +106,31 @@ export default {
.catch(() => commit(types.RECEIVE_BOARD_LISTS_FAILURE));
},
- createList: ({ state, commit, dispatch }, { backlog, labelId, milestoneId, assigneeId }) => {
+ highlightList: ({ commit, state }, listId) => {
+ if ([ListType.backlog, ListType.closed].includes(state.boardLists[listId].listType)) {
+ return;
+ }
+
+ commit(types.ADD_LIST_TO_HIGHLIGHTED_LISTS, listId);
+
+ setTimeout(() => {
+ commit(types.REMOVE_LIST_FROM_HIGHLIGHTED_LISTS, listId);
+ }, flashAnimationDuration);
+ },
+
+ createList: (
+ { state, commit, dispatch, getters },
+ { backlog, labelId, milestoneId, assigneeId },
+ ) => {
const { boardId } = state;
+ const existingList = getters.getListByLabelId(labelId);
+
+ if (existingList) {
+ dispatch('highlightList', existingList.id);
+ return;
+ }
+
gqlClient
.mutate({
mutation: createBoardListMutation,
@@ -128,6 +148,7 @@ export default {
} else {
const list = data.boardListCreate?.list;
dispatch('addList', list);
+ dispatch('highlightList', list.id);
}
})
.catch(() => commit(types.CREATE_LIST_FAILURE));
@@ -153,10 +174,10 @@ export default {
variables,
})
.then(({ data }) => {
- const labels = data[boardType]?.labels;
- return labels.nodes;
- })
- .catch(() => commit(types.RECEIVE_LABELS_FAILURE));
+ const labels = data[boardType]?.labels.nodes;
+ commit(types.RECEIVE_LABELS_SUCCESS, labels);
+ return labels;
+ });
},
moveList: (
@@ -308,34 +329,11 @@ export default {
},
setAssignees: ({ commit, getters }, assigneeUsernames) => {
- commit(types.SET_ASSIGNEE_LOADING, true);
-
- return gqlClient
- .mutate({
- mutation: updateAssigneesMutation,
- variables: {
- iid: getters.activeIssue.iid,
- projectPath: getters.activeIssue.referencePath.split('#')[0],
- assigneeUsernames,
- },
- })
- .then(({ data }) => {
- const { nodes } = data.issueSetAssignees?.issue?.assignees || [];
-
- commit('UPDATE_ISSUE_BY_ID', {
- issueId: getters.activeIssue.id,
- prop: 'assignees',
- value: nodes,
- });
-
- return nodes;
- })
- .catch(() => {
- createFlash({ message: __('An error occurred while updating assignees.') });
- })
- .finally(() => {
- commit(types.SET_ASSIGNEE_LOADING, false);
- });
+ commit('UPDATE_ISSUE_BY_ID', {
+ issueId: getters.activeIssue.id,
+ prop: 'assignees',
+ value: assigneeUsernames,
+ });
},
setActiveIssueMilestone: async ({ commit, getters }, input) => {
@@ -534,6 +532,21 @@ export default {
commit(types.SET_SELECTED_PROJECT, project);
},
+ toggleBoardItemMultiSelection: ({ commit, state }, boardItem) => {
+ const { selectedBoardItems } = state;
+ const index = selectedBoardItems.indexOf(boardItem);
+
+ if (index === -1) {
+ commit(types.ADD_BOARD_ITEM_TO_SELECTION, boardItem);
+ } else {
+ commit(types.REMOVE_BOARD_ITEM_FROM_SELECTION, boardItem);
+ }
+ },
+
+ setAddColumnFormVisibility: ({ commit }, visible) => {
+ commit(types.SET_ADD_COLUMN_FORM_VISIBLE, visible);
+ },
+
fetchBacklog: () => {
notImplemented();
},
diff --git a/app/assets/javascripts/boards/stores/boards_store.js b/app/assets/javascripts/boards/stores/boards_store.js
index f59530ddf8f..fbff736c7e1 100644
--- a/app/assets/javascripts/boards/stores/boards_store.js
+++ b/app/assets/javascripts/boards/stores/boards_store.js
@@ -4,22 +4,22 @@
import { sortBy } from 'lodash';
import Vue from 'vue';
import BoardsStoreEE from 'ee_else_ce/boards/stores/boards_store_ee';
+import { getIdFromGraphQLId } from '~/graphql_shared/utils';
+import createDefaultClient from '~/lib/graphql';
+import axios from '~/lib/utils/axios_utils';
import {
urlParamsToObject,
getUrlParamsArray,
parseBoolean,
convertObjectPropsToCamelCase,
} from '~/lib/utils/common_utils';
-import createDefaultClient from '~/lib/graphql';
-import axios from '~/lib/utils/axios_utils';
import { mergeUrlParams } from '~/lib/utils/url_utility';
-import { getIdFromGraphQLId } from '~/graphql_shared/utils';
+import { ListType, flashAnimationDuration } from '../constants';
import eventHub from '../eventhub';
-import { ListType } from '../constants';
-import IssueProject from '../models/project';
-import ListLabel from '../models/label';
import ListAssignee from '../models/assignee';
+import ListLabel from '../models/label';
import ListMilestone from '../models/milestone';
+import IssueProject from '../models/project';
const PER_PAGE = 20;
export const gqlClient = createDefaultClient();
@@ -106,6 +106,11 @@ const boardsStore = {
list
.save()
.then(() => {
+ list.highlighted = true;
+ setTimeout(() => {
+ list.highlighted = false;
+ }, flashAnimationDuration);
+
// Remove any new issues from the backlog
// as they will be visible in the new list
list.issues.forEach(backlogList.removeIssue.bind(backlogList));
@@ -117,7 +122,6 @@ const boardsStore = {
},
updateNewListDropdown(listId) {
- // eslint-disable-next-line no-unused-expressions
document
.querySelector(`.js-board-list-${getIdFromGraphQLId(listId)}`)
?.classList.remove('is-active');
@@ -720,6 +724,10 @@ const boardsStore = {
}
},
+ setIssueAssignees(issue, assignees) {
+ issue.assignees = [...assignees];
+ },
+
removeIssueLabels(issue, labels) {
labels.forEach(issue.removeLabel.bind(issue));
},
diff --git a/app/assets/javascripts/boards/stores/getters.js b/app/assets/javascripts/boards/stores/getters.js
index d72b5c6fb8e..cab97088bc6 100644
--- a/app/assets/javascripts/boards/stores/getters.js
+++ b/app/assets/javascripts/boards/stores/getters.js
@@ -17,12 +17,20 @@ export default {
return state.issues[state.activeId] || {};
},
+ groupPathForActiveIssue: (_, getters) => {
+ const { referencePath = '' } = getters.activeIssue;
+ return referencePath.slice(0, referencePath.indexOf('/'));
+ },
+
projectPathForActiveIssue: (_, getters) => {
- const referencePath = getters.activeIssue.referencePath || '';
+ const { referencePath = '' } = getters.activeIssue;
return referencePath.slice(0, referencePath.indexOf('#'));
},
getListByLabelId: (state) => (labelId) => {
+ if (!labelId) {
+ return null;
+ }
return find(state.boardLists, (l) => l.label?.id === labelId);
},
diff --git a/app/assets/javascripts/boards/stores/index.js b/app/assets/javascripts/boards/stores/index.js
index 471b952a212..0a87c6ab821 100644
--- a/app/assets/javascripts/boards/stores/index.js
+++ b/app/assets/javascripts/boards/stores/index.js
@@ -1,9 +1,9 @@
import Vue from 'vue';
import Vuex from 'vuex';
-import state from 'ee_else_ce/boards/stores/state';
-import getters from 'ee_else_ce/boards/stores/getters';
import actions from 'ee_else_ce/boards/stores/actions';
+import getters from 'ee_else_ce/boards/stores/getters';
import mutations from 'ee_else_ce/boards/stores/mutations';
+import state from 'ee_else_ce/boards/stores/state';
Vue.use(Vuex);
diff --git a/app/assets/javascripts/boards/stores/mutation_types.js b/app/assets/javascripts/boards/stores/mutation_types.js
index 4697f39498a..a89e961ae2d 100644
--- a/app/assets/javascripts/boards/stores/mutation_types.js
+++ b/app/assets/javascripts/boards/stores/mutation_types.js
@@ -2,7 +2,7 @@ export const SET_INITIAL_BOARD_DATA = 'SET_INITIAL_BOARD_DATA';
export const SET_FILTERS = 'SET_FILTERS';
export const CREATE_LIST_SUCCESS = 'CREATE_LIST_SUCCESS';
export const CREATE_LIST_FAILURE = 'CREATE_LIST_FAILURE';
-export const RECEIVE_LABELS_FAILURE = 'RECEIVE_LABELS_FAILURE';
+export const RECEIVE_LABELS_SUCCESS = 'RECEIVE_LABELS_SUCCESS';
export const GENERATE_DEFAULT_LISTS_FAILURE = 'GENERATE_DEFAULT_LISTS_FAILURE';
export const RECEIVE_BOARD_LISTS_SUCCESS = 'RECEIVE_BOARD_LISTS_SUCCESS';
export const RECEIVE_BOARD_LISTS_FAILURE = 'RECEIVE_BOARD_LISTS_FAILURE';
@@ -40,3 +40,8 @@ export const REQUEST_GROUP_PROJECTS = 'REQUEST_GROUP_PROJECTS';
export const RECEIVE_GROUP_PROJECTS_SUCCESS = 'RECEIVE_GROUP_PROJECTS_SUCCESS';
export const RECEIVE_GROUP_PROJECTS_FAILURE = 'RECEIVE_GROUP_PROJECTS_FAILURE';
export const SET_SELECTED_PROJECT = 'SET_SELECTED_PROJECT';
+export const ADD_BOARD_ITEM_TO_SELECTION = 'ADD_BOARD_ITEM_TO_SELECTION';
+export const REMOVE_BOARD_ITEM_FROM_SELECTION = 'REMOVE_BOARD_ITEM_FROM_SELECTION';
+export const SET_ADD_COLUMN_FORM_VISIBLE = 'SET_ADD_COLUMN_FORM_VISIBLE';
+export const ADD_LIST_TO_HIGHLIGHTED_LISTS = 'ADD_LIST_TO_HIGHLIGHTED_LISTS';
+export const REMOVE_LIST_FROM_HIGHLIGHTED_LISTS = 'REMOVE_LIST_FROM_HIGHLIGHTED_LISTS';
diff --git a/app/assets/javascripts/boards/stores/mutations.js b/app/assets/javascripts/boards/stores/mutations.js
index 6c79b22d308..79c98c3d90c 100644
--- a/app/assets/javascripts/boards/stores/mutations.js
+++ b/app/assets/javascripts/boards/stores/mutations.js
@@ -1,9 +1,9 @@
-import Vue from 'vue';
import { pull, union } from 'lodash';
+import Vue from 'vue';
+import { getIdFromGraphQLId } from '~/graphql_shared/utils';
+import { s__ } from '~/locale';
import { formatIssue, moveIssueListHelper } from '../boards_util';
import * as mutationTypes from './mutation_types';
-import { s__ } from '~/locale';
-import { getIdFromGraphQLId } from '~/graphql_shared/utils';
const notImplemented = () => {
/* eslint-disable-next-line @gitlab/require-i18n-strings */
@@ -63,8 +63,8 @@ export default {
state.error = s__('Boards|An error occurred while creating the list. Please try again.');
},
- [mutationTypes.RECEIVE_LABELS_FAILURE]: (state) => {
- state.error = s__('Boards|An error occurred while fetching labels. Please reload the page.');
+ [mutationTypes.RECEIVE_LABELS_SUCCESS]: (state, labels) => {
+ state.labels = labels;
},
[mutationTypes.GENERATE_DEFAULT_LISTS_FAILURE]: (state) => {
@@ -258,4 +258,28 @@ export default {
[mutationTypes.SET_SELECTED_PROJECT]: (state, project) => {
state.selectedProject = project;
},
+
+ [mutationTypes.ADD_BOARD_ITEM_TO_SELECTION]: (state, boardItem) => {
+ state.selectedBoardItems = [...state.selectedBoardItems, boardItem];
+ },
+
+ [mutationTypes.REMOVE_BOARD_ITEM_FROM_SELECTION]: (state, boardItem) => {
+ Vue.set(
+ state,
+ 'selectedBoardItems',
+ state.selectedBoardItems.filter((obj) => obj !== boardItem),
+ );
+ },
+
+ [mutationTypes.SET_ADD_COLUMN_FORM_VISIBLE]: (state, visible) => {
+ state.addColumnFormVisible = visible;
+ },
+
+ [mutationTypes.ADD_LIST_TO_HIGHLIGHTED_LISTS]: (state, listId) => {
+ state.highlightedLists.push(listId);
+ },
+
+ [mutationTypes.REMOVE_LIST_FROM_HIGHLIGHTED_LISTS]: (state, listId) => {
+ state.highlightedLists = state.highlightedLists.filter((id) => id !== listId);
+ },
};
diff --git a/app/assets/javascripts/boards/stores/state.js b/app/assets/javascripts/boards/stores/state.js
index aba7da373cf..91544d6c9c5 100644
--- a/app/assets/javascripts/boards/stores/state.js
+++ b/app/assets/javascripts/boards/stores/state.js
@@ -14,6 +14,9 @@ export default () => ({
issues: {},
filterParams: {},
boardConfig: {},
+ labels: [],
+ highlightedLists: [],
+ selectedBoardItems: [],
groupProjects: [],
groupProjectsFlags: {
isLoading: false,
@@ -22,6 +25,7 @@ export default () => ({
},
selectedProject: {},
error: undefined,
+ addColumnFormVisible: false,
// TODO: remove after ce/ee split of board_content.vue
isShowingEpicsSwimlanes: false,
});
diff --git a/app/assets/javascripts/boards/toggle_focus.js b/app/assets/javascripts/boards/toggle_focus.js
index 347deb81846..0a230f72dcc 100644
--- a/app/assets/javascripts/boards/toggle_focus.js
+++ b/app/assets/javascripts/boards/toggle_focus.js
@@ -1,45 +1,17 @@
-import $ from 'jquery';
import Vue from 'vue';
-import { GlIcon } from '@gitlab/ui';
-import { hide } from '~/tooltips';
+import ToggleFocus from './components/toggle_focus.vue';
-export default (ModalStore, boardsStore) => {
- const issueBoardsContent = document.querySelector('.content-wrapper > .js-focus-mode-board');
+export default () => {
+ const issueBoardsContentSelector = '.content-wrapper > .js-focus-mode-board';
return new Vue({
- el: document.getElementById('js-toggle-focus-btn'),
- components: {
- GlIcon,
+ el: '#js-toggle-focus-btn',
+ render(h) {
+ return h(ToggleFocus, {
+ props: {
+ issueBoardsContentSelector,
+ },
+ });
},
- data: {
- modal: ModalStore.store,
- store: boardsStore.state,
- isFullscreen: false,
- },
- methods: {
- toggleFocusMode() {
- const $el = $(this.$refs.toggleFocusModeButton);
- hide($el);
-
- issueBoardsContent.classList.toggle('is-focused');
-
- this.isFullscreen = !this.isFullscreen;
- },
- },
- template: `
-
- `,
});
};
--
cgit v1.2.3