Welcome to mirror list, hosted at ThFree Co, Russian Federation.

gitlab.com/gitlab-org/gitlab-foss.git - Unnamed repository; edit this file 'description' to name the repository.
summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
Diffstat (limited to 'app/assets/javascripts/boards')
-rw-r--r--app/assets/javascripts/boards/boards_util.js56
-rw-r--r--app/assets/javascripts/boards/components/board_add_new_column.vue132
-rw-r--r--app/assets/javascripts/boards/components/board_add_new_column_form.vue10
-rw-r--r--app/assets/javascripts/boards/components/board_add_new_column_trigger.vue11
-rw-r--r--app/assets/javascripts/boards/components/board_app.vue7
-rw-r--r--app/assets/javascripts/boards/components/board_card_move_to_position.vue25
-rw-r--r--app/assets/javascripts/boards/components/board_column.vue10
-rw-r--r--app/assets/javascripts/boards/components/board_content.vue148
-rw-r--r--app/assets/javascripts/boards/components/board_list.vue210
-rw-r--r--app/assets/javascripts/boards/components/board_list_header.vue16
-rw-r--r--app/assets/javascripts/boards/components/board_settings_sidebar.vue6
-rw-r--r--app/assets/javascripts/boards/components/board_top_bar.vue12
-rw-r--r--app/assets/javascripts/boards/components/project_select.vue90
-rw-r--r--app/assets/javascripts/boards/constants.js16
-rw-r--r--app/assets/javascripts/boards/graphql/cache_updates.js118
-rw-r--r--app/assets/javascripts/boards/graphql/issue_move_list.mutation.graphql4
-rw-r--r--app/assets/javascripts/boards/stores/actions.js8
17 files changed, 741 insertions, 138 deletions
diff --git a/app/assets/javascripts/boards/boards_util.js b/app/assets/javascripts/boards/boards_util.js
index 3a22b06c72e..bf77aa4996c 100644
--- a/app/assets/javascripts/boards/boards_util.js
+++ b/app/assets/javascripts/boards/boards_util.js
@@ -1,18 +1,18 @@
-import { sortBy, cloneDeep } from 'lodash';
+import { sortBy, cloneDeep, find, inRange } from 'lodash';
import {
TYPENAME_BOARD,
TYPENAME_ITERATION,
TYPENAME_MILESTONE,
TYPENAME_USER,
} from '~/graphql_shared/constants';
-import { isGid, convertToGraphQLId } from '~/graphql_shared/utils';
+import { isGid, convertToGraphQLId, getIdFromGraphQLId } from '~/graphql_shared/utils';
import {
ListType,
MilestoneIDs,
AssigneeFilterType,
MilestoneFilterType,
boardQuery,
-} from './constants';
+} from 'ee_else_ce/boards/constants';
export function getMilestone() {
return null;
@@ -30,6 +30,17 @@ export function updateListPosition(listObj) {
return { ...listObj, position };
}
+export function calculateNewPosition(listPosition, initialPosition, targetPosition) {
+ if (
+ listPosition === null ||
+ !(inRange(listPosition, initialPosition, targetPosition) || listPosition === targetPosition)
+ ) {
+ return listPosition;
+ }
+ const offset = initialPosition < targetPosition ? -1 : 1;
+ return listPosition + offset;
+}
+
export function formatBoardLists(lists) {
return lists.nodes.reduce((map, list) => {
return {
@@ -191,6 +202,38 @@ export function moveItemListHelper(item, fromList, toList) {
return updatedItem;
}
+export function moveItemVariables({
+ iid,
+ epicId,
+ fromListId,
+ toListId,
+ moveBeforeId,
+ moveAfterId,
+ isIssue,
+ boardId,
+ itemToMove,
+}) {
+ if (isIssue) {
+ return {
+ iid,
+ boardId,
+ projectPath: itemToMove.referencePath.split(/[#]/)[0],
+ moveBeforeId: moveBeforeId ? getIdFromGraphQLId(moveBeforeId) : undefined,
+ moveAfterId: moveAfterId ? getIdFromGraphQLId(moveAfterId) : undefined,
+ fromListId: getIdFromGraphQLId(fromListId),
+ toListId: getIdFromGraphQLId(toListId),
+ };
+ }
+ return {
+ epicId,
+ boardId,
+ moveBeforeId,
+ moveAfterId,
+ fromListId,
+ toListId,
+ };
+}
+
export function isListDraggable(list) {
return list.listType !== ListType.backlog && list.listType !== ListType.closed;
}
@@ -318,6 +361,13 @@ export function getBoardQuery(boardType) {
return boardQuery[boardType].query;
}
+export function getListByTypeId(lists, type, id) {
+ // type can be assignee/label/milestone/iteration
+ if (type && id) return find(lists, (l) => l.listType === ListType[type] && l[type]?.id === id);
+
+ return null;
+}
+
export default {
getMilestone,
formatIssue,
diff --git a/app/assets/javascripts/boards/components/board_add_new_column.vue b/app/assets/javascripts/boards/components/board_add_new_column.vue
index 90f7059da86..985b9798b36 100644
--- a/app/assets/javascripts/boards/components/board_add_new_column.vue
+++ b/app/assets/javascripts/boards/components/board_add_new_column.vue
@@ -1,4 +1,6 @@
<script>
+import produce from 'immer';
+import { debounce } from 'lodash';
import {
GlTooltipDirective as GlTooltip,
GlButton,
@@ -6,8 +8,12 @@ import {
GlIcon,
} from '@gitlab/ui';
import { mapActions, mapGetters, mapState } from 'vuex';
+import { DEFAULT_DEBOUNCE_AND_THROTTLE_MS } from '~/lib/utils/constants';
import BoardAddNewColumnForm from '~/boards/components/board_add_new_column_form.vue';
import { __ } from '~/locale';
+import { createListMutations, listsQuery, BoardType, ListType } from 'ee_else_ce/boards/constants';
+import boardLabelsQuery from '../graphql/board_labels.query.graphql';
+import { getListByTypeId } from '../boards_util';
export default {
i18n: {
@@ -23,60 +29,150 @@ export default {
directives: {
GlTooltip,
},
- inject: ['scopedLabelsAvailable'],
+ inject: ['scopedLabelsAvailable', 'issuableType', 'fullPath', 'boardType', 'isApolloBoard'],
+ props: {
+ listQueryVariables: {
+ type: Object,
+ required: true,
+ },
+ boardId: {
+ type: String,
+ required: true,
+ },
+ lists: {
+ type: Object,
+ required: true,
+ },
+ },
data() {
return {
selectedId: null,
selectedLabel: null,
selectedIdValid: true,
+ labelsApollo: [],
+ searchTerm: '',
};
},
+ apollo: {
+ labelsApollo: {
+ query: boardLabelsQuery,
+ variables() {
+ return {
+ fullPath: this.fullPath,
+ searchTerm: this.searchTerm,
+ isGroup: this.boardType === BoardType.group,
+ isProject: this.boardType === BoardType.project,
+ };
+ },
+ update(data) {
+ return data[this.boardType].labels.nodes;
+ },
+ skip() {
+ return !this.isApolloBoard;
+ },
+ },
+ },
computed: {
...mapState(['labels', 'labelsLoading']),
...mapGetters(['getListByLabelId']),
+ labelsToUse() {
+ return this.isApolloBoard ? this.labelsApollo : this.labels;
+ },
+ isLabelsLoading() {
+ return this.isApolloBoard ? this.$apollo.queries.labelsApollo.loading : this.labelsLoading;
+ },
columnForSelected() {
+ if (this.isApolloBoard) {
+ return getListByTypeId(this.lists, ListType.label, this.selectedId);
+ }
return this.getListByLabelId(this.selectedId);
},
items() {
- return (
- this.labels.map((i) => ({
- ...i,
- text: i.title,
- value: i.id,
- })) || []
- );
+ return (this.labelsToUse || []).map((i) => ({
+ ...i,
+ text: i.title,
+ value: i.id,
+ }));
},
},
created() {
- this.filterItems();
+ if (!this.isApolloBoard) {
+ this.filterItems();
+ }
},
methods: {
- ...mapActions(['createList', 'fetchLabels', 'highlightList', 'setAddColumnFormVisibility']),
+ ...mapActions(['createList', 'fetchLabels', 'highlightList']),
+ createListApollo({ labelId }) {
+ return this.$apollo.mutate({
+ mutation: createListMutations[this.issuableType].mutation,
+ variables: {
+ labelId,
+ boardId: this.boardId,
+ },
+ update: (
+ store,
+ {
+ data: {
+ boardListCreate: { list },
+ },
+ },
+ ) => {
+ const sourceData = store.readQuery({
+ query: listsQuery[this.issuableType].query,
+ variables: this.listQueryVariables,
+ });
+ const data = produce(sourceData, (draftData) => {
+ draftData[this.boardType].board.lists.nodes.push(list);
+ });
+ store.writeQuery({
+ query: listsQuery[this.issuableType].query,
+ variables: this.listQueryVariables,
+ data,
+ });
+ this.$emit('highlight-list', list.id);
+ },
+ });
+ },
addList() {
if (!this.selectedLabel) {
this.selectedIdValid = false;
return;
}
- this.setAddColumnFormVisibility(false);
-
if (this.columnForSelected) {
const listId = this.columnForSelected.id;
- this.highlightList(listId);
+ if (this.isApolloBoard) {
+ this.$emit('highlight-list', listId);
+ } else {
+ this.highlightList(listId);
+ }
return;
}
- this.createList({ labelId: this.selectedId });
+ if (this.isApolloBoard) {
+ this.createListApollo({ labelId: this.selectedId });
+ } else {
+ this.createList({ labelId: this.selectedId });
+ }
+
+ this.$emit('setAddColumnFormVisibility', false);
},
filterItems(searchTerm) {
this.fetchLabels(searchTerm);
},
+ onSearch: debounce(function debouncedSearch(searchTerm) {
+ this.searchTerm = searchTerm;
+ if (!this.isApolloBoard) {
+ this.filterItems(searchTerm);
+ }
+ }, DEFAULT_DEBOUNCE_AND_THROTTLE_MS),
+
setSelectedItem(selectedId) {
this.selectedId = selectedId;
- const label = this.labels.find(({ id }) => id === selectedId);
+ const label = this.labelsToUse.find(({ id }) => id === selectedId);
if (!selectedId || !label) {
this.selectedLabel = null;
} else {
@@ -95,8 +191,8 @@ export default {
<template>
<board-add-new-column-form
:selected-id-valid="selectedIdValid"
- @filter-items="filterItems"
@add-list="addList"
+ @setAddColumnFormVisibility="$emit('setAddColumnFormVisibility', $event)"
>
<template #dropdown>
<gl-collapsible-listbox
@@ -104,11 +200,11 @@ export default {
:items="items"
searchable
:search-placeholder="__('Search labels')"
- :searching="labelsLoading"
+ :searching="isLabelsLoading"
:selected="selectedId"
:no-results-text="$options.i18n.noResults"
@select="setSelectedItem"
- @search="filterItems"
+ @search="onSearch"
@hidden="onHide"
>
<template #toggle>
diff --git a/app/assets/javascripts/boards/components/board_add_new_column_form.vue b/app/assets/javascripts/boards/components/board_add_new_column_form.vue
index 259423df07f..419d0b41d69 100644
--- a/app/assets/javascripts/boards/components/board_add_new_column_form.vue
+++ b/app/assets/javascripts/boards/components/board_add_new_column_form.vue
@@ -1,6 +1,5 @@
<script>
import { GlButton, GlFormGroup } from '@gitlab/ui';
-import { mapActions } from 'vuex';
import { __ } from '~/locale';
export default {
@@ -33,7 +32,6 @@ export default {
};
},
methods: {
- ...mapActions(['setAddColumnFormVisibility']),
onSubmit() {
this.$emit('add-list');
},
@@ -83,9 +81,11 @@ export default {
@click="onSubmit"
>{{ $options.i18n.add }}</gl-button
>
- <gl-button data-testid="cancelAddNewColumn" @click="setAddColumnFormVisibility(false)">{{
- $options.i18n.cancel
- }}</gl-button>
+ <gl-button
+ data-testid="cancelAddNewColumn"
+ @click="$emit('setAddColumnFormVisibility', false)"
+ >{{ $options.i18n.cancel }}</gl-button
+ >
</div>
</div>
</div>
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
index 14c84d3c4e5..d91c8ab4727 100644
--- a/app/assets/javascripts/boards/components/board_add_new_column_trigger.vue
+++ b/app/assets/javascripts/boards/components/board_add_new_column_trigger.vue
@@ -1,6 +1,5 @@
<script>
import { GlButton, GlTooltipDirective } from '@gitlab/ui';
-import { mapActions, mapState } from 'vuex';
import { __ } from '~/locale';
import Tracking from '~/tracking';
@@ -12,16 +11,20 @@ export default {
GlTooltip: GlTooltipDirective,
},
mixins: [Tracking.mixin()],
+ props: {
+ isNewListShowing: {
+ type: Boolean,
+ required: true,
+ },
+ },
computed: {
- ...mapState({ isNewListShowing: ({ addColumnForm }) => addColumnForm.visible }),
tooltip() {
return this.isNewListShowing ? __('The list creation wizard is already open') : '';
},
},
methods: {
- ...mapActions(['setAddColumnFormVisibility']),
handleClick() {
- this.setAddColumnFormVisibility(true);
+ this.$emit('setAddColumnFormVisibility', true);
this.track('click_button', { label: 'create_list' });
},
},
diff --git a/app/assets/javascripts/boards/components/board_app.vue b/app/assets/javascripts/boards/components/board_app.vue
index 3a247819850..0b9243c07c5 100644
--- a/app/assets/javascripts/boards/components/board_app.vue
+++ b/app/assets/javascripts/boards/components/board_app.vue
@@ -35,6 +35,7 @@ export default {
activeListId: '',
boardId: this.initialBoardId,
filterParams: { ...this.initialFilterParams },
+ addColumnFormVisible: false,
isShowingEpicsSwimlanes: Boolean(queryToObject(window.location.search).group_by),
apolloError: null,
};
@@ -79,6 +80,7 @@ export default {
computed: {
...mapGetters(['isSidebarOpen']),
listQueryVariables() {
+ if (this.filterParams.groupBy) delete this.filterParams.groupBy;
return {
...(this.isIssueBoard && {
isGroup: this.isGroupBoard,
@@ -129,19 +131,24 @@ export default {
<div class="boards-app gl-relative" :class="{ 'is-compact': isAnySidebarOpen }">
<board-top-bar
:board-id="boardId"
+ :add-column-form-visible="addColumnFormVisible"
:is-swimlanes-on="isSwimlanesOn"
@switchBoard="switchBoard"
@setFilters="setFilters"
+ @setAddColumnFormVisibility="addColumnFormVisible = $event"
@toggleSwimlanes="isShowingEpicsSwimlanes = $event"
/>
<board-content
v-if="!isApolloBoard || boardListsApollo"
:board-id="boardId"
+ :add-column-form-visible="addColumnFormVisible"
:is-swimlanes-on="isSwimlanesOn"
:filter-params="filterParams"
:board-lists-apollo="boardListsApollo"
:apollo-error="apolloError"
+ :list-query-variables="listQueryVariables"
@setActiveList="setActiveId"
+ @setAddColumnFormVisibility="addColumnFormVisible = $event"
/>
<board-settings-sidebar
v-if="!isApolloBoard || activeList"
diff --git a/app/assets/javascripts/boards/components/board_card_move_to_position.vue b/app/assets/javascripts/boards/components/board_card_move_to_position.vue
index f58f7838576..19eddbfdd68 100644
--- a/app/assets/javascripts/boards/components/board_card_move_to_position.vue
+++ b/app/assets/javascripts/boards/components/board_card_move_to_position.vue
@@ -14,6 +14,7 @@ export default {
GlDisclosureDropdown,
},
mixins: [Tracking.mixin()],
+ inject: ['isApolloBoard'],
props: {
item: {
type: Object,
@@ -83,16 +84,20 @@ export default {
});
},
moveToPosition({ positionInList }) {
- this.moveItem({
- itemId: this.item.id,
- itemIid: this.item.iid,
- itemPath: this.item.referencePath,
- fromListId: this.list.id,
- toListId: this.list.id,
- positionInList,
- atIndex: this.index,
- allItemsLoadedInList: !this.listHasNextPage,
- });
+ if (this.isApolloBoard) {
+ this.$emit('moveToPosition', positionInList);
+ } else {
+ this.moveItem({
+ itemId: this.item.id,
+ itemIid: this.item.iid,
+ itemPath: this.item.referencePath,
+ fromListId: this.list.id,
+ toListId: this.list.id,
+ positionInList,
+ atIndex: this.index,
+ allItemsLoadedInList: !this.listHasNextPage,
+ });
+ }
},
selectMoveAction({ text }) {
if (text === BOARD_CARD_MOVE_TO_POSITIONS_START_OPTION) {
diff --git a/app/assets/javascripts/boards/components/board_column.vue b/app/assets/javascripts/boards/components/board_column.vue
index b2054d76e95..2ee0b4593d6 100644
--- a/app/assets/javascripts/boards/components/board_column.vue
+++ b/app/assets/javascripts/boards/components/board_column.vue
@@ -24,12 +24,20 @@ export default {
type: Object,
required: true,
},
+ highlightedListsApollo: {
+ type: Array,
+ required: false,
+ default: () => [],
+ },
},
computed: {
...mapState(['filterParams', 'highlightedLists']),
...mapGetters(['getBoardItemsByList']),
+ highlightedListsToUse() {
+ return this.isApolloBoard ? this.highlightedListsApollo : this.highlightedLists;
+ },
highlighted() {
- return this.highlightedLists.includes(this.list.id);
+ return this.highlightedListsToUse.includes(this.list.id);
},
listItems() {
return this.isApolloBoard ? [] : this.getBoardItemsByList(this.list.id);
diff --git a/app/assets/javascripts/boards/components/board_content.vue b/app/assets/javascripts/boards/components/board_content.vue
index 8304dfef527..a51e4ddc8f8 100644
--- a/app/assets/javascripts/boards/components/board_content.vue
+++ b/app/assets/javascripts/boards/components/board_content.vue
@@ -1,12 +1,19 @@
<script>
import { GlAlert } from '@gitlab/ui';
import { sortBy } from 'lodash';
+import produce from 'immer';
import Draggable from 'vuedraggable';
import { mapState, mapActions } from 'vuex';
import eventHub from '~/boards/eventhub';
import BoardAddNewColumn from 'ee_else_ce/boards/components/board_add_new_column.vue';
import { defaultSortableOptions } from '~/sortable/constants';
-import { DraggableItemTypes } from 'ee_else_ce/boards/constants';
+import {
+ DraggableItemTypes,
+ flashAnimationDuration,
+ listsQuery,
+ updateListQueries,
+} from 'ee_else_ce/boards/constants';
+import { calculateNewPosition } from 'ee_else_ce/boards/boards_util';
import BoardColumn from './board_column.vue';
export default {
@@ -20,7 +27,15 @@ export default {
EpicsSwimlanes: () => import('ee_component/boards/components/epics_swimlanes.vue'),
GlAlert,
},
- inject: ['canAdminList', 'isIssueBoard', 'isEpicBoard', 'disabled', 'isApolloBoard'],
+ inject: [
+ 'boardType',
+ 'canAdminList',
+ 'isIssueBoard',
+ 'isEpicBoard',
+ 'disabled',
+ 'issuableType',
+ 'isApolloBoard',
+ ],
props: {
boardId: {
type: String,
@@ -44,16 +59,25 @@ export default {
required: false,
default: null,
},
+ listQueryVariables: {
+ type: Object,
+ required: true,
+ },
+ addColumnFormVisible: {
+ type: Boolean,
+ required: true,
+ },
},
data() {
return {
boardHeight: null,
+ highlightedLists: [],
};
},
computed: {
- ...mapState(['boardLists', 'error', 'addColumnForm']),
- addColumnFormVisible() {
- return this.addColumnForm?.visible;
+ ...mapState(['boardLists', 'error']),
+ boardListsById() {
+ return this.isApolloBoard ? this.boardListsApollo : this.boardLists;
},
boardListsToUse() {
const lists = this.isApolloBoard ? this.boardListsApollo : this.boardLists;
@@ -101,6 +125,90 @@ export default {
refetchLists() {
this.$apollo.queries.boardListsApollo.refetch();
},
+ highlightList(listId) {
+ this.highlightedLists.push(listId);
+
+ setTimeout(() => {
+ this.highlightedLists = this.highlightedLists.filter((id) => id !== listId);
+ }, flashAnimationDuration);
+ },
+ updateListPosition({
+ item: {
+ dataset: { listId: movedListId, draggableItemType },
+ },
+ newIndex,
+ to: { children },
+ }) {
+ if (!this.isApolloBoard) {
+ this.moveList({
+ item: {
+ dataset: { listId: movedListId, draggableItemType },
+ },
+ newIndex,
+ to: { children },
+ });
+ return;
+ }
+
+ if (draggableItemType !== DraggableItemTypes.list) {
+ return;
+ }
+
+ const displacedListId = children[newIndex].dataset.listId;
+
+ if (movedListId === displacedListId) {
+ return;
+ }
+ const initialPosition = this.boardListsById[movedListId].position;
+ const targetPosition = this.boardListsById[displacedListId].position;
+
+ try {
+ this.$apollo.mutate({
+ mutation: updateListQueries[this.issuableType].mutation,
+ variables: {
+ listId: movedListId,
+ position: targetPosition,
+ },
+ update: (store) => {
+ const sourceData = store.readQuery({
+ query: listsQuery[this.issuableType].query,
+ variables: this.listQueryVariables,
+ });
+ const data = produce(sourceData, (draftData) => {
+ // for current list, new position is already set by Apollo via automatic update
+ const affectedNodes = draftData[this.boardType].board.lists.nodes.filter(
+ (node) => node.id !== movedListId,
+ );
+ affectedNodes.forEach((node) => {
+ // eslint-disable-next-line no-param-reassign
+ node.position = calculateNewPosition(
+ node.position,
+ initialPosition,
+ targetPosition,
+ );
+ });
+ });
+ store.writeQuery({
+ query: listsQuery[this.issuableType].query,
+ variables: this.listQueryVariables,
+ data,
+ });
+ },
+ optimisticResponse: {
+ updateBoardList: {
+ __typename: 'UpdateBoardListPayload',
+ errors: [],
+ list: {
+ ...this.boardListsApollo[movedListId],
+ position: targetPosition,
+ },
+ },
+ },
+ });
+ } catch {
+ // handle error
+ }
+ },
},
};
</script>
@@ -120,7 +228,7 @@ export default {
ref="list"
v-bind="draggableOptions"
class="boards-list gl-w-full gl-py-5 gl-pr-3 gl-white-space-nowrap gl-overflow-x-auto"
- @end="moveList"
+ @end="updateListPosition"
>
<board-column
v-for="(list, index) in boardListsToUse"
@@ -129,13 +237,22 @@ export default {
:board-id="boardId"
:list="list"
:filters="filterParams"
+ :highlighted-lists-apollo="highlightedLists"
:data-draggable-item-type="$options.draggableItemTypes.list"
- :class="{ 'gl-xs-display-none!': addColumnFormVisible }"
+ :class="{ 'gl-display-none! gl-sm-display-inline-block!': addColumnFormVisible }"
@setActiveList="$emit('setActiveList', $event)"
/>
<transition name="slide" @after-enter="afterFormEnters">
- <board-add-new-column v-if="addColumnFormVisible" class="gl-xs-w-full!" />
+ <board-add-new-column
+ v-if="addColumnFormVisible"
+ class="gl-xs-w-full!"
+ :board-id="boardId"
+ :list-query-variables="listQueryVariables"
+ :lists="boardListsById"
+ @setAddColumnFormVisibility="$emit('setAddColumnFormVisibility', $event)"
+ @highlight-list="highlightList"
+ />
</transition>
</component>
@@ -146,8 +263,21 @@ export default {
:lists="boardListsToUse"
:can-admin-list="canAdminList"
:filters="filterParams"
+ :highlighted-lists="highlightedLists"
@setActiveList="$emit('setActiveList', $event)"
- />
+ @move-list="updateListPosition"
+ >
+ <board-add-new-column
+ v-if="addColumnFormVisible"
+ class="gl-sticky gl-top-5"
+ :filter-params="filterParams"
+ :list-query-variables="listQueryVariables"
+ :board-id="boardId"
+ :lists="boardListsById"
+ @setAddColumnFormVisibility="$emit('setAddColumnFormVisibility', $event)"
+ @highlight-list="highlightList"
+ />
+ </epics-swimlanes>
<board-content-sidebar v-if="isIssueBoard" data-testid="issue-boards-sidebar" />
diff --git a/app/assets/javascripts/boards/components/board_list.vue b/app/assets/javascripts/boards/components/board_list.vue
index 5f082066ad4..af309ba9912 100644
--- a/app/assets/javascripts/boards/components/board_list.vue
+++ b/app/assets/javascripts/boards/components/board_list.vue
@@ -9,6 +9,7 @@ import { sortableStart, sortableEnd } from '~/sortable/utils';
import Tracking from '~/tracking';
import listQuery from 'ee_else_ce/boards/graphql/board_lists_deferred.query.graphql';
import BoardCardMoveToPosition from '~/boards/components/board_card_move_to_position.vue';
+import glFeatureFlagMixin from '~/vue_shared/mixins/gl_feature_flags_mixin';
import {
DEFAULT_BOARD_LIST_ITEMS_SIZE,
toggleFormEventPrefix,
@@ -16,6 +17,13 @@ import {
listIssuablesQueries,
ListType,
} from 'ee_else_ce/boards/constants';
+import {
+ addItemToList,
+ removeItemFromList,
+ updateEpicsCount,
+ updateIssueCountAndWeight,
+} from '../graphql/cache_updates';
+import { shouldCloneCard, moveItemVariables } from '../boards_util';
import eventHub from '../eventhub';
import BoardCard from './board_card.vue';
import BoardNewIssue from './board_new_issue.vue';
@@ -37,7 +45,7 @@ export default {
GlIntersectionObserver,
BoardCardMoveToPosition,
},
- mixins: [Tracking.mixin()],
+ mixins: [Tracking.mixin(), glFeatureFlagMixin()],
inject: [
'isEpicBoard',
'isGroupBoard',
@@ -73,6 +81,8 @@ export default {
showEpicForm: false,
currentList: null,
isLoadingMore: false,
+ toListId: null,
+ toList: {},
};
},
apollo: {
@@ -111,6 +121,29 @@ export default {
isSingleRequest: true,
},
},
+ toList: {
+ query() {
+ return listIssuablesQueries[this.issuableType].query;
+ },
+ variables() {
+ return {
+ id: this.toListId,
+ ...this.listQueryVariables,
+ };
+ },
+ skip() {
+ return !this.toListId;
+ },
+ update(data) {
+ return data[this.boardType].board.lists.nodes[0];
+ },
+ context: {
+ isSingleRequest: true,
+ },
+ error() {
+ // handle error
+ },
+ },
},
computed: {
...mapState(['pageInfoByListId', 'listsFlags', 'isUpdateIssueOrderInProgress']),
@@ -205,6 +238,9 @@ export default {
showMoveToPosition() {
return !this.disabled && this.list.listType !== ListType.closed;
},
+ shouldCloneCard() {
+ return shouldCloneCard(this.list.listType, this.toList.listType);
+ },
},
watch: {
boardListItems() {
@@ -337,14 +373,169 @@ export default {
}
}
- this.moveItem({
- itemId,
- itemIid,
- itemPath,
- fromListId: from.dataset.listId,
- toListId: to.dataset.listId,
- moveBeforeId,
- moveAfterId,
+ if (this.isApolloBoard) {
+ this.moveBoardItem(
+ {
+ epicId: itemId,
+ iid: itemIid,
+ fromListId: from.dataset.listId,
+ toListId: to.dataset.listId,
+ moveBeforeId,
+ moveAfterId,
+ },
+ newIndex,
+ );
+ } else {
+ this.moveItem({
+ itemId,
+ itemIid,
+ itemPath,
+ fromListId: from.dataset.listId,
+ toListId: to.dataset.listId,
+ moveBeforeId,
+ moveAfterId,
+ });
+ }
+ },
+ isItemInTheList(itemIid) {
+ const items = this.toList?.[`${this.issuableType}s`]?.nodes || [];
+ return items.some((item) => item.iid === itemIid);
+ },
+ async moveBoardItem(variables, newIndex) {
+ const { fromListId, toListId, iid } = variables;
+ this.toListId = toListId;
+ await this.$nextTick(); // we need this next tick to retrieve `toList` from Apollo cache
+
+ const itemToMove = this.boardListItems.find((item) => item.iid === iid);
+
+ if (this.shouldCloneCard && this.isItemInTheList(iid)) {
+ return;
+ }
+
+ try {
+ await this.$apollo.mutate({
+ mutation: listIssuablesQueries[this.issuableType].moveMutation,
+ variables: {
+ ...moveItemVariables({
+ ...variables,
+ isIssue: !this.isEpicBoard,
+ boardId: this.boardId,
+ itemToMove,
+ }),
+ withColor: this.isEpicBoard && this.glFeatures.epicColorHighlight,
+ },
+ update: (cache, { data: { issuableMoveList } }) =>
+ this.updateCacheAfterMovingItem({
+ issuableMoveList,
+ fromListId,
+ toListId,
+ newIndex,
+ cache,
+ }),
+ optimisticResponse: {
+ issuableMoveList: {
+ issuable: itemToMove,
+ errors: [],
+ },
+ },
+ });
+ } catch {
+ // handle error
+ }
+ },
+ updateCacheAfterMovingItem({ issuableMoveList, fromListId, toListId, newIndex, cache }) {
+ const { issuable } = issuableMoveList;
+ if (!this.shouldCloneCard) {
+ removeItemFromList({
+ query: listIssuablesQueries[this.issuableType].query,
+ variables: { ...this.listQueryVariables, id: fromListId },
+ boardType: this.boardType,
+ id: issuable.id,
+ issuableType: this.issuableType,
+ cache,
+ });
+ }
+
+ addItemToList({
+ query: listIssuablesQueries[this.issuableType].query,
+ variables: { ...this.listQueryVariables, id: toListId },
+ issuable,
+ newIndex,
+ boardType: this.boardType,
+ issuableType: this.issuableType,
+ cache,
+ });
+
+ this.updateCountAndWeight({ fromListId, toListId, issuable, cache });
+ },
+ updateCountAndWeight({ fromListId, toListId, issuable, isAddingIssue, cache }) {
+ if (!this.isEpicBoard) {
+ updateIssueCountAndWeight({
+ fromListId,
+ toListId,
+ filterParams: this.filterParams,
+ issuable,
+ shouldClone: isAddingIssue || this.shouldCloneCard,
+ cache,
+ });
+ } else {
+ const { issuableType, filterParams } = this;
+ updateEpicsCount({
+ issuableType,
+ toListId,
+ fromListId,
+ filterParams,
+ issuable,
+ shouldClone: this.shouldCloneCard,
+ cache,
+ });
+ }
+ },
+ moveToPosition(positionInList, oldIndex, item) {
+ this.$apollo.mutate({
+ mutation: listIssuablesQueries[this.issuableType].moveMutation,
+ variables: {
+ ...moveItemVariables({
+ iid: item.iid,
+ epicId: item.id,
+ fromListId: this.currentList.id,
+ toListId: this.currentList.id,
+ isIssue: !this.isEpicBoard,
+ boardId: this.boardId,
+ itemToMove: item,
+ }),
+ positionInList,
+ withColor: this.isEpicBoard && this.glFeatures.epicColorHighlight,
+ },
+ optimisticResponse: {
+ issuableMoveList: {
+ issuable: item,
+ errors: [],
+ },
+ },
+ update: (cache, { data: { issuableMoveList } }) => {
+ const { issuable } = issuableMoveList;
+ removeItemFromList({
+ query: listIssuablesQueries[this.issuableType].query,
+ variables: { ...this.listQueryVariables, id: this.currentList.id },
+ boardType: this.boardType,
+ id: issuable.id,
+ issuableType: this.issuableType,
+ cache,
+ });
+ if (positionInList === 0 || this.listItemsCount <= this.boardListItems.length) {
+ const newIndex = positionInList === 0 ? 0 : this.boardListItems.length - 1;
+ addItemToList({
+ query: listIssuablesQueries[this.issuableType].query,
+ variables: { ...this.listQueryVariables, id: this.currentList.id },
+ issuable,
+ newIndex,
+ boardType: this.boardType,
+ issuableType: this.issuableType,
+ cache,
+ });
+ }
+ },
});
},
},
@@ -401,6 +592,7 @@ export default {
:index="index"
:list="list"
:list-items-length="boardListItems.length"
+ @moveToPosition="moveToPosition($event, index, item)"
/>
<gl-intersection-observer
v-if="isObservableItem(index)"
diff --git a/app/assets/javascripts/boards/components/board_list_header.vue b/app/assets/javascripts/boards/components/board_list_header.vue
index 1b711feb686..61a9b22bfc5 100644
--- a/app/assets/javascripts/boards/components/board_list_header.vue
+++ b/app/assets/javascripts/boards/components/board_list_header.vue
@@ -108,6 +108,9 @@ export default {
listType() {
return this.list.listType;
},
+ isLabelList() {
+ return this.listType === ListType.label;
+ },
itemsCount() {
return this.isEpicBoard ? this.list.metadata.epicsCount : this.boardList?.issuesCount;
},
@@ -258,9 +261,6 @@ export default {
},
methods: {
...mapActions(['updateList', 'setActiveId', 'toggleListCollapsed']),
- closeListActions() {
- this.$refs.headerListActions?.close();
- },
openSidebarSettings() {
if (this.activeId === inactiveId) {
sidebarEventHub.$emit('sidebar.closeAll');
@@ -277,8 +277,6 @@ export default {
}
this.track('click_button', { label: 'list_settings' });
-
- this.closeListActions();
},
showScopedLabels(label) {
return this.scopedLabelsAvailable && isScopedLabel(label);
@@ -292,13 +290,9 @@ export default {
} else {
eventHub.$emit(`${toggleFormEventPrefix.issue}${this.list.id}`);
}
-
- this.closeListActions();
},
showNewEpicForm() {
eventHub.$emit(`${toggleFormEventPrefix.epic}${this.list.id}`);
-
- this.closeListActions();
},
toggleExpanded() {
const collapsed = !this.list.collapsed;
@@ -382,7 +376,8 @@ export default {
<header
:class="{
'gl-h-full': list.collapsed,
- 'board-inner gl-rounded-top-left-base gl-rounded-top-right-base gl-bg-gray-50': isSwimlanesHeader,
+ 'board-inner gl-bg-gray-50': isSwimlanesHeader,
+ 'gl-border-t-solid gl-border-4 gl-rounded-top-left-base gl-rounded-top-right-base': isLabelList,
}"
:style="headerStyle"
class="board-header gl-relative"
@@ -532,7 +527,6 @@ export default {
</div>
<gl-disclosure-dropdown
v-if="showListHeaderActions"
- ref="headerListActions"
v-gl-tooltip.hover.top="{
title: $options.i18n.listActions,
boundary: 'viewport',
diff --git a/app/assets/javascripts/boards/components/board_settings_sidebar.vue b/app/assets/javascripts/boards/components/board_settings_sidebar.vue
index 23e0f2510a7..0f43aae3936 100644
--- a/app/assets/javascripts/boards/components/board_settings_sidebar.vue
+++ b/app/assets/javascripts/boards/components/board_settings_sidebar.vue
@@ -114,10 +114,10 @@ export default {
showScopedLabels(label) {
return this.scopedLabelsAvailable && isScopedLabel(label);
},
- async deleteBoardList() {
+ deleteBoardList() {
this.track('click_button', { label: 'remove_list' });
if (this.isApolloBoard) {
- await this.deleteList(this.activeListId);
+ this.deleteList(this.activeListId);
} else {
this.removeList(this.activeId);
}
@@ -157,7 +157,7 @@ export default {
<mounting-portal mount-to="#js-right-sidebar-portal" name="board-settings-sidebar" append>
<gl-drawer
v-bind="$attrs"
- class="js-board-settings-sidebar gl-absolute"
+ class="js-board-settings-sidebar gl-absolute boards-sidebar"
:open="showSidebar"
variant="sidebar"
@close="unsetActiveListId"
diff --git a/app/assets/javascripts/boards/components/board_top_bar.vue b/app/assets/javascripts/boards/components/board_top_bar.vue
index c186346b2ac..fd9043a561f 100644
--- a/app/assets/javascripts/boards/components/board_top_bar.vue
+++ b/app/assets/javascripts/boards/components/board_top_bar.vue
@@ -35,6 +35,10 @@ export default {
type: String,
required: true,
},
+ addColumnFormVisible: {
+ type: Boolean,
+ required: true,
+ },
isSwimlanesOn: {
type: Boolean,
required: true,
@@ -91,7 +95,7 @@ export default {
class="issues-details-filters filtered-search-block gl-display-flex gl-flex-direction-column gl-lg-flex-direction-row row-content-block second-block"
>
<div
- class="gl-display-flex gl-flex-direction-column gl-md-flex-direction-row gl-flex-grow-1 gl-lg-mb-0 gl-mb-3 gl-w-full"
+ class="gl-display-flex gl-flex-direction-column gl-md-flex-direction-row gl-flex-grow-1 gl-lg-mb-0 gl-mb-3 gl-w-full gl-min-w-0"
>
<boards-selector :board-apollo="board" @switchBoard="$emit('switchBoard', $event)" />
<new-board-button />
@@ -117,7 +121,11 @@ export default {
@toggleSwimlanes="$emit('toggleSwimlanes', $event)"
/>
<config-toggle :board-has-scope="hasScope" />
- <board-add-new-column-trigger v-if="canAdminList" />
+ <board-add-new-column-trigger
+ v-if="canAdminList"
+ :is-new-list-showing="addColumnFormVisible"
+ @setAddColumnFormVisibility="$emit('setAddColumnFormVisibility', $event)"
+ />
<toggle-focus />
</div>
</div>
diff --git a/app/assets/javascripts/boards/components/project_select.vue b/app/assets/javascripts/boards/components/project_select.vue
index 247910301e7..960c8e472b8 100644
--- a/app/assets/javascripts/boards/components/project_select.vue
+++ b/app/assets/javascripts/boards/components/project_select.vue
@@ -1,15 +1,10 @@
<script>
-import {
- GlDropdown,
- GlDropdownItem,
- GlDropdownText,
- GlSearchBoxByType,
- GlIntersectionObserver,
- GlLoadingIcon,
-} from '@gitlab/ui';
-import { mapActions, mapState, mapGetters } from 'vuex';
+import { GlCollapsibleListbox } from '@gitlab/ui';
+import { mapActions, mapGetters, mapState } from 'vuex';
+import { debounce } from 'lodash';
import { s__ } from '~/locale';
import { featureAccessLevel } from '~/pages/projects/shared/permissions/constants';
+import { DEFAULT_DEBOUNCE_AND_THROTTLE_MS } from '~/lib/utils/constants';
import { ListType } from '../constants';
export default {
@@ -27,12 +22,7 @@ export default {
order_by: 'similarity',
},
components: {
- GlIntersectionObserver,
- GlLoadingIcon,
- GlDropdown,
- GlDropdownItem,
- GlDropdownText,
- GlSearchBoxByType,
+ GlCollapsibleListbox,
},
inject: ['groupId'],
props: {
@@ -44,6 +34,7 @@ export default {
data() {
return {
initialLoading: true,
+ selectedProjectId: '',
selectedProject: {},
searchTerm: '',
};
@@ -51,6 +42,12 @@ export default {
computed: {
...mapState(['groupProjectsFlags']),
...mapGetters(['activeGroupProjects']),
+ projects() {
+ return this.activeGroupProjects.map((project) => ({
+ value: project.id,
+ text: project.nameWithNamespace,
+ }));
+ },
selectedProjectName() {
return this.selectedProject.name || this.$options.i18n.dropdownText;
},
@@ -73,26 +70,27 @@ export default {
},
},
watch: {
- searchTerm() {
+ searchTerm: debounce(function debouncedSearch() {
this.fetchGroupProjects({ search: this.searchTerm });
- },
+ }, DEFAULT_DEBOUNCE_AND_THROTTLE_MS),
},
mounted() {
this.fetchGroupProjects({});
-
this.initialLoading = false;
},
methods: {
...mapActions(['fetchGroupProjects', 'setSelectedProject']),
selectProject(projectId) {
+ this.selectedProjectId = projectId;
this.selectedProject = this.activeGroupProjects.find((project) => project.id === projectId);
this.setSelectedProject(this.selectedProject);
},
loadMoreProjects() {
+ if (!this.hasNextPage) return;
this.fetchGroupProjects({ search: this.searchTerm, fetchNext: true });
},
- setFocus() {
- this.$refs.search.focusInput();
+ onSearch(query) {
+ this.searchTerm = query;
},
},
};
@@ -103,45 +101,23 @@ export default {
<label class="gl-font-weight-bold gl-mt-3" data-testid="header-label">{{
$options.i18n.headerTitle
}}</label>
- <gl-dropdown
+ <gl-collapsible-listbox
+ v-model="selectedProjectId"
+ block
+ searchable
+ infinite-scroll
data-testid="project-select-dropdown"
- :text="selectedProjectName"
+ :items="projects"
+ :toggle-text="selectedProjectName"
:header-text="$options.i18n.headerTitle"
- block
- menu-class="gl-w-full!"
:loading="initialLoading"
- @shown="setFocus"
- >
- <gl-search-box-by-type
- ref="search"
- v-model.trim="searchTerm"
- debounce="250"
- :placeholder="$options.i18n.searchPlaceholder"
- />
- <gl-dropdown-item
- v-for="project in activeGroupProjects"
- v-show="!groupProjectsFlags.isLoading"
- :key="project.id"
- :name="project.name"
- @click="selectProject(project.id)"
- >
- {{ project.nameWithNamespace }}
- </gl-dropdown-item>
- <gl-dropdown-text
- v-show="groupProjectsFlags.isLoading"
- data-testid="dropdown-text-loading-icon"
- >
- <gl-loading-icon class="gl-mx-auto" size="sm" />
- </gl-dropdown-text>
- <gl-dropdown-text
- v-if="isFetchResultEmpty && !groupProjectsFlags.isLoading"
- data-testid="empty-result-message"
- >
- <span class="gl-text-gray-500">{{ $options.i18n.emptySearchResult }}</span>
- </gl-dropdown-text>
- <gl-intersection-observer v-if="hasNextPage" @appear="loadMoreProjects">
- <gl-loading-icon v-if="groupProjectsFlags.isLoadingMore" size="lg" />
- </gl-intersection-observer>
- </gl-dropdown>
+ :searching="groupProjectsFlags.isLoading"
+ :search-placeholder="$options.i18n.searchPlaceholder"
+ :no-results-text="$options.i18n.emptySearchResult"
+ :infinite-scroll-loading="groupProjectsFlags.isLoadingMore"
+ @select="selectProject"
+ @search="onSearch"
+ @bottom-reached="loadMoreProjects"
+ />
</div>
</template>
diff --git a/app/assets/javascripts/boards/constants.js b/app/assets/javascripts/boards/constants.js
index 7fe89ffbb52..d4d1bc7804e 100644
--- a/app/assets/javascripts/boards/constants.js
+++ b/app/assets/javascripts/boards/constants.js
@@ -3,15 +3,18 @@ import { TYPE_EPIC, TYPE_ISSUE, WORKSPACE_GROUP, WORKSPACE_PROJECT } from '~/iss
import { s__, __ } from '~/locale';
import updateEpicSubscriptionMutation from '~/sidebar/queries/update_epic_subscription.mutation.graphql';
import updateEpicTitleMutation from '~/sidebar/queries/update_epic_title.mutation.graphql';
+import createBoardListMutation from './graphql/board_list_create.mutation.graphql';
import destroyBoardListMutation from './graphql/board_list_destroy.mutation.graphql';
import updateBoardListMutation from './graphql/board_list_update.mutation.graphql';
import toggleListCollapsedMutation from './graphql/client/board_toggle_collapsed.mutation.graphql';
import issueSetSubscriptionMutation from './graphql/issue_set_subscription.mutation.graphql';
import issueSetTitleMutation from './graphql/issue_set_title.mutation.graphql';
+import issueMoveListMutation from './graphql/issue_move_list.mutation.graphql';
import groupBoardQuery from './graphql/group_board.query.graphql';
import projectBoardQuery from './graphql/project_board.query.graphql';
import listIssuesQuery from './graphql/lists_issues.query.graphql';
+import listDeferredQuery from './graphql/board_lists_deferred.query.graphql';
export const BoardType = {
project: 'project',
@@ -71,6 +74,18 @@ export const listsQuery = {
},
};
+export const listsDeferredQuery = {
+ [TYPE_ISSUE]: {
+ query: listDeferredQuery,
+ },
+};
+
+export const createListMutations = {
+ [TYPE_ISSUE]: {
+ mutation: createBoardListMutation,
+ },
+};
+
export const updateListQueries = {
[TYPE_ISSUE]: {
mutation: updateBoardListMutation,
@@ -110,6 +125,7 @@ export const subscriptionQueries = {
export const listIssuablesQueries = {
[TYPE_ISSUE]: {
query: listIssuesQuery,
+ moveMutation: issueMoveListMutation,
},
};
diff --git a/app/assets/javascripts/boards/graphql/cache_updates.js b/app/assets/javascripts/boards/graphql/cache_updates.js
new file mode 100644
index 00000000000..084809e4e60
--- /dev/null
+++ b/app/assets/javascripts/boards/graphql/cache_updates.js
@@ -0,0 +1,118 @@
+import produce from 'immer';
+import listQuery from 'ee_else_ce/boards/graphql/board_lists_deferred.query.graphql';
+import { listsDeferredQuery } from 'ee_else_ce/boards/constants';
+
+export function removeItemFromList({ query, variables, boardType, id, issuableType, cache }) {
+ cache.updateQuery({ query, variables }, (sourceData) =>
+ produce(sourceData, (draftData) => {
+ const { nodes: items } = draftData[boardType].board.lists.nodes[0][`${issuableType}s`];
+ items.splice(
+ items.findIndex((item) => item.id === id),
+ 1,
+ );
+ }),
+ );
+}
+
+export function addItemToList({
+ query,
+ variables,
+ boardType,
+ issuable,
+ newIndex,
+ issuableType,
+ cache,
+}) {
+ cache.updateQuery({ query, variables }, (sourceData) =>
+ produce(sourceData, (draftData) => {
+ const { nodes: items } = draftData[boardType].board.lists.nodes[0][`${issuableType}s`];
+ items.splice(newIndex, 0, issuable);
+ }),
+ );
+}
+
+export function updateIssueCountAndWeight({
+ fromListId,
+ toListId,
+ filterParams,
+ issuable: issue,
+ shouldClone,
+ cache,
+}) {
+ if (!shouldClone) {
+ cache.updateQuery(
+ {
+ query: listQuery,
+ variables: { id: fromListId, filters: filterParams },
+ },
+ ({ boardList }) => ({
+ boardList: {
+ ...boardList,
+ issuesCount: boardList.issuesCount - 1,
+ totalWeight: boardList.totalWeight - issue.weight,
+ },
+ }),
+ );
+ }
+
+ cache.updateQuery(
+ {
+ query: listQuery,
+ variables: { id: toListId, filters: filterParams },
+ },
+ ({ boardList }) => ({
+ boardList: {
+ ...boardList,
+ issuesCount: boardList.issuesCount + 1,
+ totalWeight: boardList.totalWeight + issue.weight,
+ },
+ }),
+ );
+}
+
+export function updateEpicsCount({
+ issuableType,
+ filterParams,
+ fromListId,
+ toListId,
+ issuable: epic,
+ shouldClone,
+ cache,
+}) {
+ const epicWeight = epic.descendantWeightSum.openedIssues + epic.descendantWeightSum.closedIssues;
+ if (!shouldClone) {
+ cache.updateQuery(
+ {
+ query: listsDeferredQuery[issuableType].query,
+ variables: { id: fromListId, filters: filterParams },
+ },
+ ({ epicBoardList }) => ({
+ epicBoardList: {
+ ...epicBoardList,
+ metadata: {
+ epicsCount: epicBoardList.metadata.epicsCount - 1,
+ totalWeight: epicBoardList.metadata.totalWeight - epicWeight,
+ ...epicBoardList.metadata,
+ },
+ },
+ }),
+ );
+ }
+
+ cache.updateQuery(
+ {
+ query: listsDeferredQuery[issuableType].query,
+ variables: { id: toListId, filters: filterParams },
+ },
+ ({ epicBoardList }) => ({
+ epicBoardList: {
+ ...epicBoardList,
+ metadata: {
+ epicsCount: epicBoardList.metadata.epicsCount + 1,
+ totalWeight: epicBoardList.metadata.totalWeight + epicWeight,
+ ...epicBoardList.metadata,
+ },
+ },
+ }),
+ );
+}
diff --git a/app/assets/javascripts/boards/graphql/issue_move_list.mutation.graphql b/app/assets/javascripts/boards/graphql/issue_move_list.mutation.graphql
index 89670760450..4a46d741a78 100644
--- a/app/assets/javascripts/boards/graphql/issue_move_list.mutation.graphql
+++ b/app/assets/javascripts/boards/graphql/issue_move_list.mutation.graphql
@@ -9,7 +9,7 @@ mutation issueMoveList(
$moveBeforeId: ID
$moveAfterId: ID
) {
- issueMoveList(
+ issuableMoveList: issueMoveList(
input: {
projectPath: $projectPath
iid: $iid
@@ -20,7 +20,7 @@ mutation issueMoveList(
moveAfterId: $moveAfterId
}
) {
- issue {
+ issuable: issue {
...Issue
}
errors
diff --git a/app/assets/javascripts/boards/stores/actions.js b/app/assets/javascripts/boards/stores/actions.js
index a144054d680..d96d92948be 100644
--- a/app/assets/javascripts/boards/stores/actions.js
+++ b/app/assets/javascripts/boards/stores/actions.js
@@ -602,8 +602,8 @@ export default {
cache,
{
data: {
- issueMoveList: {
- issue: { weight },
+ issuableMoveList: {
+ issuable: { weight },
},
},
},
@@ -661,11 +661,11 @@ export default {
},
});
- if (data?.issueMoveList?.errors.length || !data.issueMoveList) {
+ if (data?.issuableMoveList?.errors.length || !data.issuableMoveList) {
throw new Error('issueMoveList empty');
}
- commit(types.MUTATE_ISSUE_SUCCESS, { issue: data.issueMoveList.issue });
+ commit(types.MUTATE_ISSUE_SUCCESS, { issue: data.issuableMoveList.issuable });
commit(types.MUTATE_ISSUE_IN_PROGRESS, false);
} catch {
commit(types.MUTATE_ISSUE_IN_PROGRESS, false);