summaryrefslogtreecommitdiff
path: root/app/assets/javascripts/boards
diff options
context:
space:
mode:
authorGitLab Bot <gitlab-bot@gitlab.com>2020-08-20 18:42:06 +0000
committerGitLab Bot <gitlab-bot@gitlab.com>2020-08-20 18:42:06 +0000
commit6e4e1050d9dba2b7b2523fdd1768823ab85feef4 (patch)
tree78be5963ec075d80116a932011d695dd33910b4e /app/assets/javascripts/boards
parent1ce776de4ae122aba3f349c02c17cebeaa8ecf07 (diff)
downloadgitlab-ce-6e4e1050d9dba2b7b2523fdd1768823ab85feef4.tar.gz
Add latest changes from gitlab-org/gitlab@13-3-stable-ee
Diffstat (limited to 'app/assets/javascripts/boards')
-rw-r--r--app/assets/javascripts/boards/boards_util.js21
-rw-r--r--app/assets/javascripts/boards/components/board_column.vue2
-rw-r--r--app/assets/javascripts/boards/components/board_content.vue7
-rw-r--r--app/assets/javascripts/boards/components/board_form.vue2
-rw-r--r--app/assets/javascripts/boards/components/board_list.vue2
-rw-r--r--app/assets/javascripts/boards/components/board_list_header.vue4
-rw-r--r--app/assets/javascripts/boards/components/board_new_issue.vue2
-rw-r--r--app/assets/javascripts/boards/components/board_settings_sidebar.vue92
-rw-r--r--app/assets/javascripts/boards/components/board_sidebar.js2
-rw-r--r--app/assets/javascripts/boards/components/boards_selector.vue50
-rw-r--r--app/assets/javascripts/boards/components/issue_due_date.vue6
-rw-r--r--app/assets/javascripts/boards/components/issue_time_estimate.vue7
-rw-r--r--app/assets/javascripts/boards/components/modal/footer.vue2
-rw-r--r--app/assets/javascripts/boards/components/modal/header.vue2
-rw-r--r--app/assets/javascripts/boards/components/modal/tabs.vue2
-rw-r--r--app/assets/javascripts/boards/components/new_list_dropdown.js2
-rw-r--r--app/assets/javascripts/boards/components/project_select.vue4
-rw-r--r--app/assets/javascripts/boards/components/sidebar/remove_issue.vue10
-rw-r--r--app/assets/javascripts/boards/constants.js2
-rw-r--r--app/assets/javascripts/boards/eventhub.js4
-rw-r--r--app/assets/javascripts/boards/filtered_search_boards.js3
-rw-r--r--app/assets/javascripts/boards/index.js11
-rw-r--r--app/assets/javascripts/boards/models/list.js15
-rw-r--r--app/assets/javascripts/boards/queries/board_list_shared.fragment.graphql1
-rw-r--r--app/assets/javascripts/boards/queries/group_lists_issues.query.graphql18
-rw-r--r--app/assets/javascripts/boards/queries/issue.fragment.graphql31
-rw-r--r--app/assets/javascripts/boards/queries/project_lists_issues.query.graphql18
-rw-r--r--app/assets/javascripts/boards/stores/actions.js41
-rw-r--r--app/assets/javascripts/boards/stores/boards_store.js45
-rw-r--r--app/assets/javascripts/boards/stores/mutation_types.js6
-rw-r--r--app/assets/javascripts/boards/stores/mutations.js22
-rw-r--r--app/assets/javascripts/boards/stores/state.js8
32 files changed, 350 insertions, 94 deletions
diff --git a/app/assets/javascripts/boards/boards_util.js b/app/assets/javascripts/boards/boards_util.js
index 3178bda93b8..384a386d69c 100644
--- a/app/assets/javascripts/boards/boards_util.js
+++ b/app/assets/javascripts/boards/boards_util.js
@@ -1,7 +1,28 @@
+import ListIssue from 'ee_else_ce/boards/models/issue';
+import { getIdFromGraphQLId } from '~/graphql_shared/utils';
+
export function getMilestone() {
return null;
}
+export function formatListIssues(listIssues) {
+ return listIssues.nodes.reduce((map, list) => {
+ return {
+ ...map,
+ [list.id]: list.issues.nodes.map(
+ i =>
+ new ListIssue({
+ ...i,
+ id: getIdFromGraphQLId(i.id),
+ labels: i.labels?.nodes || [],
+ assignees: i.assignees?.nodes || [],
+ }),
+ ),
+ };
+ }, {});
+}
+
export default {
getMilestone,
+ formatListIssues,
};
diff --git a/app/assets/javascripts/boards/components/board_column.vue b/app/assets/javascripts/boards/components/board_column.vue
index 0ed7579e8e1..dae24338e45 100644
--- a/app/assets/javascripts/boards/components/board_column.vue
+++ b/app/assets/javascripts/boards/components/board_column.vue
@@ -1,10 +1,10 @@
<script>
import Sortable from 'sortablejs';
import isWipLimitsOn from 'ee_else_ce/boards/mixins/is_wip_limits';
+import BoardListHeader from 'ee_else_ce/boards/components/board_list_header.vue';
import Tooltip from '~/vue_shared/directives/tooltip';
import EmptyComponent from '~/vue_shared/components/empty_component';
import BoardBlankState from './board_blank_state.vue';
-import BoardListHeader from 'ee_else_ce/boards/components/board_list_header.vue';
import BoardList from './board_list.vue';
import boardsStore from '../stores/boards_store';
import eventHub from '../eventhub';
diff --git a/app/assets/javascripts/boards/components/board_content.vue b/app/assets/javascripts/boards/components/board_content.vue
index 6ac7fdce6a7..c42295792f1 100644
--- a/app/assets/javascripts/boards/components/board_content.vue
+++ b/app/assets/javascripts/boards/components/board_content.vue
@@ -1,8 +1,8 @@
<script>
import { mapState } from 'vuex';
-import glFeatureFlagMixin from '~/vue_shared/mixins/gl_feature_flags_mixin';
import BoardColumn from 'ee_else_ce/boards/components/board_column.vue';
import EpicsSwimlanes from 'ee_component/boards/components/epics_swimlanes.vue';
+import glFeatureFlagMixin from '~/vue_shared/mixins/gl_feature_flags_mixin';
export default {
components: {
@@ -42,7 +42,7 @@ export default {
},
},
computed: {
- ...mapState(['isShowingEpicsSwimlanes']),
+ ...mapState(['isShowingEpicsSwimlanes', 'boardLists']),
isSwimlanesOn() {
return this.glFeatures.boardsWithSwimlanes && this.isShowingEpicsSwimlanes;
},
@@ -73,11 +73,12 @@ export default {
<epics-swimlanes
v-else
ref="swimlanes"
- :lists="lists"
+ :lists="boardLists"
:can-admin-list="canAdminList"
:disabled="disabled"
:board-id="boardId"
:group-id="groupId"
+ :root-path="rootPath"
/>
</div>
</template>
diff --git a/app/assets/javascripts/boards/components/board_form.vue b/app/assets/javascripts/boards/components/board_form.vue
index fbe221041c1..231059b895e 100644
--- a/app/assets/javascripts/boards/components/board_form.vue
+++ b/app/assets/javascripts/boards/components/board_form.vue
@@ -1,6 +1,6 @@
<script>
import { __ } from '~/locale';
-import Flash from '~/flash';
+import { deprecatedCreateFlash as Flash } from '~/flash';
import DeprecatedModal from '~/vue_shared/components/deprecated_modal.vue';
import { visitUrl } from '~/lib/utils/url_utility';
import boardsStore from '~/boards/stores/boards_store';
diff --git a/app/assets/javascripts/boards/components/board_list.vue b/app/assets/javascripts/boards/components/board_list.vue
index 4270ad5783d..1a26782f6f0 100644
--- a/app/assets/javascripts/boards/components/board_list.vue
+++ b/app/assets/javascripts/boards/components/board_list.vue
@@ -6,7 +6,7 @@ import boardCard from './board_card.vue';
import eventHub from '../eventhub';
import boardsStore from '../stores/boards_store';
import { sprintf, __ } from '~/locale';
-import createFlash from '~/flash';
+import { deprecatedCreateFlash as createFlash } from '~/flash';
import {
getBoardSortableDefaultOptions,
sortableStart,
diff --git a/app/assets/javascripts/boards/components/board_list_header.vue b/app/assets/javascripts/boards/components/board_list_header.vue
index 02a04cb4e46..bafe07afb48 100644
--- a/app/assets/javascripts/boards/components/board_list_header.vue
+++ b/app/assets/javascripts/boards/components/board_list_header.vue
@@ -241,7 +241,7 @@ export default {
v-if="isSwimlanesHeader && !list.isExpanded"
ref="collapsedInfo"
aria-hidden="true"
- class="board-header-collapsed-info-icon gl-mt-2 gl-cursor-pointer gl-text-gray-700"
+ class="board-header-collapsed-info-icon gl-mt-2 gl-cursor-pointer gl-text-gray-500"
>
<gl-icon name="information" />
</span>
@@ -282,7 +282,7 @@ export default {
<div
v-if="showBoardListAndBoardInfo"
class="issue-count-badge gl-display-inline-flex gl-pr-0 no-drag text-secondary"
- :class="{ 'gl-display-none': !list.isExpanded && isSwimlanesHeader }"
+ :class="{ 'gl-display-none!': !list.isExpanded && isSwimlanesHeader }"
>
<span class="gl-display-inline-flex">
<gl-tooltip :target="() => $refs.issueCount" :title="issuesTooltipLabel" />
diff --git a/app/assets/javascripts/boards/components/board_new_issue.vue b/app/assets/javascripts/boards/components/board_new_issue.vue
index 02ac45f8ef9..34e8438ba4c 100644
--- a/app/assets/javascripts/boards/components/board_new_issue.vue
+++ b/app/assets/javascripts/boards/components/board_new_issue.vue
@@ -119,7 +119,7 @@ export default {
autocomplete="off"
/>
<project-select v-if="groupId" :group-id="groupId" :list="list" />
- <div class="clearfix prepend-top-10">
+ <div class="clearfix gl-mt-3">
<gl-button
ref="submit-button"
:disabled="disabled"
diff --git a/app/assets/javascripts/boards/components/board_settings_sidebar.vue b/app/assets/javascripts/boards/components/board_settings_sidebar.vue
new file mode 100644
index 00000000000..3149762ecdf
--- /dev/null
+++ b/app/assets/javascripts/boards/components/board_settings_sidebar.vue
@@ -0,0 +1,92 @@
+<script>
+import { GlDrawer, GlLabel } from '@gitlab/ui';
+import { mapActions, mapState } from 'vuex';
+import { __ } from '~/locale';
+import boardsStore from '~/boards/stores/boards_store';
+import eventHub from '~/sidebar/event_hub';
+import { isScopedLabel } from '~/lib/utils/common_utils';
+import { inactiveId } from '~/boards/constants';
+
+// NOTE: need to revisit how we handle headerHeight, because we have so many different header and footer options.
+export default {
+ headerHeight: process.env.NODE_ENV === 'development' ? '75px' : '40px',
+ listSettingsText: __('List settings'),
+ assignee: 'assignee',
+ milestone: 'milestone',
+ label: 'label',
+ labelListText: __('Label'),
+ components: {
+ GlDrawer,
+ GlLabel,
+ BoardSettingsSidebarWipLimit: () =>
+ import('ee_component/boards/components/board_settings_wip_limit.vue'),
+ BoardSettingsListTypes: () =>
+ import('ee_component/boards/components/board_settings_list_types.vue'),
+ },
+ computed: {
+ ...mapState(['activeId']),
+ activeList() {
+ /*
+ Warning: Though a computed property it is not reactive because we are
+ referencing a List Model class. Reactivity only applies to plain JS objects
+ */
+ return boardsStore.state.lists.find(({ id }) => id === this.activeId);
+ },
+ isSidebarOpen() {
+ return this.activeId !== inactiveId;
+ },
+ activeListLabel() {
+ return this.activeList.label;
+ },
+ boardListType() {
+ return this.activeList.type || null;
+ },
+ listTypeTitle() {
+ return this.$options.labelListText;
+ },
+ },
+ created() {
+ eventHub.$on('sidebar.closeAll', this.closeSidebar);
+ },
+ beforeDestroy() {
+ eventHub.$off('sidebar.closeAll', this.closeSidebar);
+ },
+ methods: {
+ ...mapActions(['setActiveId']),
+ closeSidebar() {
+ this.setActiveId(inactiveId);
+ },
+ showScopedLabels(label) {
+ return boardsStore.scopedLabels.enabled && isScopedLabel(label);
+ },
+ },
+};
+</script>
+
+<template>
+ <gl-drawer
+ class="js-board-settings-sidebar"
+ :open="isSidebarOpen"
+ :header-height="$options.headerHeight"
+ @close="closeSidebar"
+ >
+ <template #header>{{ $options.listSettingsText }}</template>
+ <template v-if="isSidebarOpen">
+ <div v-if="boardListType === $options.label">
+ <label class="js-list-label gl-display-block">{{ listTypeTitle }}</label>
+ <gl-label
+ :title="activeListLabel.title"
+ :background-color="activeListLabel.color"
+ :scoped="showScopedLabels(activeListLabel)"
+ />
+ </div>
+
+ <board-settings-list-types
+ v-else
+ :active-list="activeList"
+ :board-list-type="boardListType"
+ />
+ <board-settings-sidebar-wip-limit :max-issue-count="activeList.maxIssueCount" />
+ </template>
+ </gl-drawer>
+</template>
diff --git a/app/assets/javascripts/boards/components/board_sidebar.js b/app/assets/javascripts/boards/components/board_sidebar.js
index 056a7b48212..3790c494085 100644
--- a/app/assets/javascripts/boards/components/board_sidebar.js
+++ b/app/assets/javascripts/boards/components/board_sidebar.js
@@ -3,7 +3,7 @@
import $ from 'jquery';
import Vue from 'vue';
import { GlLabel } from '@gitlab/ui';
-import Flash from '~/flash';
+import { deprecatedCreateFlash as Flash } from '~/flash';
import { sprintf, __ } from '~/locale';
import Sidebar from '~/right_sidebar';
import eventHub from '~/sidebar/event_hub';
diff --git a/app/assets/javascripts/boards/components/boards_selector.vue b/app/assets/javascripts/boards/components/boards_selector.vue
index dbe3e0790f6..48f6ba6cfc7 100644
--- a/app/assets/javascripts/boards/components/boards_selector.vue
+++ b/app/assets/javascripts/boards/components/boards_selector.vue
@@ -3,10 +3,10 @@ import { throttle } from 'lodash';
import {
GlLoadingIcon,
GlSearchBoxByType,
- GlDropdown,
- GlDropdownDivider,
- GlDropdownHeader,
- GlDropdownItem,
+ GlDeprecatedDropdown,
+ GlDeprecatedDropdownDivider,
+ GlDeprecatedDropdownHeader,
+ GlDeprecatedDropdownItem,
} from '@gitlab/ui';
import httpStatusCodes from '~/lib/utils/http_status';
@@ -26,10 +26,10 @@ export default {
BoardForm,
GlLoadingIcon,
GlSearchBoxByType,
- GlDropdown,
- GlDropdownDivider,
- GlDropdownHeader,
- GlDropdownItem,
+ GlDeprecatedDropdown,
+ GlDeprecatedDropdownDivider,
+ GlDeprecatedDropdownHeader,
+ GlDeprecatedDropdownItem,
},
props: {
currentBoard: {
@@ -235,7 +235,7 @@ export default {
<template>
<div class="boards-switcher js-boards-selector gl-mr-3">
<span class="boards-selector-wrapper js-boards-selector-wrapper">
- <gl-dropdown
+ <gl-deprecated-dropdown
data-qa-selector="boards_dropdown"
toggle-class="dropdown-menu-toggle js-dropdown-toggle"
menu-class="flex-column dropdown-extended-height"
@@ -248,9 +248,9 @@ export default {
</div>
</div>
- <gl-dropdown-header class="mt-0">
+ <gl-deprecated-dropdown-header class="mt-0">
<gl-search-box-by-type ref="searchBox" v-model="filterTerm" />
- </gl-dropdown-header>
+ </gl-deprecated-dropdown-header>
<div
v-if="!loading"
@@ -259,26 +259,26 @@ export default {
class="dropdown-content flex-fill"
@scroll.passive="throttledSetScrollFade"
>
- <gl-dropdown-item
+ <gl-deprecated-dropdown-item
v-show="filteredBoards.length === 0"
class="no-pointer-events text-secondary"
>
{{ s__('IssueBoards|No matching boards found') }}
- </gl-dropdown-item>
+ </gl-deprecated-dropdown-item>
<h6 v-if="showRecentSection" class="dropdown-bold-header my-0">
{{ __('Recent') }}
</h6>
<template v-if="showRecentSection">
- <gl-dropdown-item
+ <gl-deprecated-dropdown-item
v-for="recentBoard in recentBoards"
:key="`recent-${recentBoard.id}`"
class="js-dropdown-item"
:href="`${boardBaseUrl}/${recentBoard.id}`"
>
{{ recentBoard.name }}
- </gl-dropdown-item>
+ </gl-deprecated-dropdown-item>
</template>
<hr v-if="showRecentSection" class="my-1" />
@@ -287,21 +287,21 @@ export default {
{{ __('All') }}
</h6>
- <gl-dropdown-item
+ <gl-deprecated-dropdown-item
v-for="otherBoard in filteredBoards"
:key="otherBoard.id"
class="js-dropdown-item"
:href="`${boardBaseUrl}/${otherBoard.id}`"
>
{{ otherBoard.name }}
- </gl-dropdown-item>
- <gl-dropdown-item v-if="hasMissingBoards" class="small unclickable">
+ </gl-deprecated-dropdown-item>
+ <gl-deprecated-dropdown-item v-if="hasMissingBoards" class="small unclickable">
{{
s__(
'IssueBoards|Some of your boards are hidden, activate a license to see them again.',
)
}}
- </gl-dropdown-item>
+ </gl-deprecated-dropdown-item>
</div>
<div
@@ -313,25 +313,25 @@ export default {
<gl-loading-icon v-if="loading" />
<div v-if="canAdminBoard">
- <gl-dropdown-divider />
+ <gl-deprecated-dropdown-divider />
- <gl-dropdown-item
+ <gl-deprecated-dropdown-item
v-if="multipleIssueBoardsAvailable"
data-qa-selector="create_new_board_button"
@click.prevent="showPage('new')"
>
{{ s__('IssueBoards|Create new board') }}
- </gl-dropdown-item>
+ </gl-deprecated-dropdown-item>
- <gl-dropdown-item
+ <gl-deprecated-dropdown-item
v-if="showDelete"
class="text-danger js-delete-board"
@click.prevent="showPage('delete')"
>
{{ s__('IssueBoards|Delete board') }}
- </gl-dropdown-item>
+ </gl-deprecated-dropdown-item>
</div>
- </gl-dropdown>
+ </gl-deprecated-dropdown>
<board-form
v-if="currentPage"
diff --git a/app/assets/javascripts/boards/components/issue_due_date.vue b/app/assets/javascripts/boards/components/issue_due_date.vue
index 1d70c635c18..4add5ee646a 100644
--- a/app/assets/javascripts/boards/components/issue_due_date.vue
+++ b/app/assets/javascripts/boards/components/issue_due_date.vue
@@ -87,11 +87,7 @@ export default {
<template>
<span>
<span ref="issueDueDate" :class="cssClass" class="board-card-info card-number">
- <icon
- :class="{ 'text-danger': isPastDue }"
- class="board-card-info-icon align-top"
- name="calendar"
- />
+ <icon :class="{ 'text-danger': isPastDue }" class="board-card-info-icon" name="calendar" />
<time :class="{ 'text-danger': isPastDue }" datetime="date" class="board-card-info-text">{{
body
}}</time>
diff --git a/app/assets/javascripts/boards/components/issue_time_estimate.vue b/app/assets/javascripts/boards/components/issue_time_estimate.vue
index 5c33ba9461c..e8b7689da13 100644
--- a/app/assets/javascripts/boards/components/issue_time_estimate.vue
+++ b/app/assets/javascripts/boards/components/issue_time_estimate.vue
@@ -34,10 +34,9 @@ export default {
<template>
<span>
<span ref="issueTimeEstimate" class="board-card-info card-number">
- <icon name="hourglass" class="board-card-info-icon align-top" /><time
- class="board-card-info-text"
- >{{ timeEstimate }}</time
- >
+ <icon name="hourglass" class="board-card-info-icon" /><time class="board-card-info-text">{{
+ timeEstimate
+ }}</time>
</span>
<gl-tooltip
:target="() => $refs.issueTimeEstimate"
diff --git a/app/assets/javascripts/boards/components/modal/footer.vue b/app/assets/javascripts/boards/components/modal/footer.vue
index 5f100c617a0..c4953dda793 100644
--- a/app/assets/javascripts/boards/components/modal/footer.vue
+++ b/app/assets/javascripts/boards/components/modal/footer.vue
@@ -1,6 +1,6 @@
<script>
import footerEEMixin from 'ee_else_ce/boards/mixins/modal_footer';
-import Flash from '../../../flash';
+import { deprecatedCreateFlash as Flash } from '../../../flash';
import { __, n__ } from '../../../locale';
import ListsDropdown from './lists_dropdown.vue';
import ModalStore from '../../stores/modal_store';
diff --git a/app/assets/javascripts/boards/components/modal/header.vue b/app/assets/javascripts/boards/components/modal/header.vue
index 8eae8e4726f..573284d2b44 100644
--- a/app/assets/javascripts/boards/components/modal/header.vue
+++ b/app/assets/javascripts/boards/components/modal/header.vue
@@ -67,7 +67,7 @@ export default {
</h2>
</header>
<modal-tabs v-if="!loading && issuesCount > 0" />
- <div v-if="showSearch" class="d-flex append-bottom-10">
+ <div v-if="showSearch" class="d-flex gl-mb-3">
<modal-filters :store="filter" />
<button
ref="selectAllBtn"
diff --git a/app/assets/javascripts/boards/components/modal/tabs.vue b/app/assets/javascripts/boards/components/modal/tabs.vue
index ed67206218e..a71fda9d7c5 100644
--- a/app/assets/javascripts/boards/components/modal/tabs.vue
+++ b/app/assets/javascripts/boards/components/modal/tabs.vue
@@ -19,7 +19,7 @@ export default {
};
</script>
<template>
- <div class="top-area prepend-top-10 append-bottom-10">
+ <div class="top-area gl-mt-3 gl-mb-3">
<ul class="nav-links issues-state-filters">
<li :class="{ active: activeTab == 'all' }">
<a href="#" role="button" @click.prevent="changeTab('all')">
diff --git a/app/assets/javascripts/boards/components/new_list_dropdown.js b/app/assets/javascripts/boards/components/new_list_dropdown.js
index 229bb82152b..2b9fdf11b37 100644
--- a/app/assets/javascripts/boards/components/new_list_dropdown.js
+++ b/app/assets/javascripts/boards/components/new_list_dropdown.js
@@ -3,7 +3,7 @@
import $ from 'jquery';
import { __ } from '~/locale';
import axios from '~/lib/utils/axios_utils';
-import flash from '~/flash';
+import { deprecatedCreateFlash as flash } from '~/flash';
import CreateLabelDropdown from '../../create_label';
import boardsStore from '../stores/boards_store';
diff --git a/app/assets/javascripts/boards/components/project_select.vue b/app/assets/javascripts/boards/components/project_select.vue
index 8fd377938b4..598e92726c1 100644
--- a/app/assets/javascripts/boards/components/project_select.vue
+++ b/app/assets/javascripts/boards/components/project_select.vue
@@ -64,10 +64,10 @@ export default {
this.groupId,
term,
{
- search_namespaces: true,
with_issues_enabled: true,
with_shared: false,
include_subgroups: true,
+ order_by: 'similarity',
...additionalAttrs,
},
projects => {
@@ -97,7 +97,7 @@ export default {
<template>
<div>
- <label class="label-bold prepend-top-10">{{ __('Project') }}</label>
+ <label class="label-bold gl-mt-3">{{ __('Project') }}</label>
<div ref="projectsDropdown" class="dropdown dropdown-projects">
<button
class="dropdown-menu-toggle wide"
diff --git a/app/assets/javascripts/boards/components/sidebar/remove_issue.vue b/app/assets/javascripts/boards/components/sidebar/remove_issue.vue
index 71e5d8058da..4e5a6609042 100644
--- a/app/assets/javascripts/boards/components/sidebar/remove_issue.vue
+++ b/app/assets/javascripts/boards/components/sidebar/remove_issue.vue
@@ -1,10 +1,14 @@
<script>
+import { GlButton } from '@gitlab/ui';
import axios from '~/lib/utils/axios_utils';
-import Flash from '../../../flash';
+import { deprecatedCreateFlash as Flash } from '../../../flash';
import { __ } from '../../../locale';
import boardsStore from '../../stores/boards_store';
export default {
+ components: {
+ GlButton,
+ },
props: {
issue: {
type: Object,
@@ -75,8 +79,8 @@ export default {
</script>
<template>
<div class="block list">
- <button class="btn btn-default btn-block" type="button" @click="removeIssue">
+ <gl-button variant="default" category="secondary" block="block" @click="removeIssue">
{{ __('Remove from board') }}
- </button>
+ </gl-button>
</div>
</template>
diff --git a/app/assets/javascripts/boards/constants.js b/app/assets/javascripts/boards/constants.js
index f577a168e75..35c52558cac 100644
--- a/app/assets/javascripts/boards/constants.js
+++ b/app/assets/javascripts/boards/constants.js
@@ -13,7 +13,7 @@ export const ListType = {
blank: 'blank',
};
-export const inactiveListId = 0;
+export const inactiveId = 0;
export default {
BoardType,
diff --git a/app/assets/javascripts/boards/eventhub.js b/app/assets/javascripts/boards/eventhub.js
index 0948c2e5352..e31806ad199 100644
--- a/app/assets/javascripts/boards/eventhub.js
+++ b/app/assets/javascripts/boards/eventhub.js
@@ -1,3 +1,3 @@
-import Vue from 'vue';
+import createEventHub from '~/helpers/event_hub_factory';
-export default new Vue();
+export default createEventHub();
diff --git a/app/assets/javascripts/boards/filtered_search_boards.js b/app/assets/javascripts/boards/filtered_search_boards.js
index ca85e54eb89..b7966dd869d 100644
--- a/app/assets/javascripts/boards/filtered_search_boards.js
+++ b/app/assets/javascripts/boards/filtered_search_boards.js
@@ -1,6 +1,6 @@
import IssuableFilteredSearchTokenKeys from 'ee_else_ce/filtered_search/issuable_filtered_search_token_keys';
-import FilteredSearchContainer from '../filtered_search/container';
import FilteredSearchManager from 'ee_else_ce/filtered_search/filtered_search_manager';
+import FilteredSearchContainer from '../filtered_search/container';
import boardsStore from './stores/boards_store';
export default class FilteredSearchBoards extends FilteredSearchManager {
@@ -10,6 +10,7 @@ export default class FilteredSearchBoards extends FilteredSearchManager {
isGroupDecendent: true,
stateFiltersSelector: '.issues-state-filters',
isGroup: IS_EE,
+ useDefaultState: false,
filteredSearchTokenKeys: IssuableFilteredSearchTokenKeys,
});
diff --git a/app/assets/javascripts/boards/index.js b/app/assets/javascripts/boards/index.js
index 5b4a1d262dd..971edd71eec 100644
--- a/app/assets/javascripts/boards/index.js
+++ b/app/assets/javascripts/boards/index.js
@@ -4,7 +4,6 @@ import { mapActions } from 'vuex';
import 'ee_else_ce/boards/models/issue';
import 'ee_else_ce/boards/models/list';
-import BoardContent from '~/boards/components/board_content.vue';
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';
@@ -19,8 +18,9 @@ import {
} from 'ee_else_ce/boards/ee_functions';
import VueApollo from 'vue-apollo';
+import BoardContent from '~/boards/components/board_content.vue';
import createDefaultClient from '~/lib/graphql';
-import Flash from '~/flash';
+import { deprecatedCreateFlash as Flash } from '~/flash';
import { __ } from '~/locale';
import './models/label';
import './models/assignee';
@@ -83,8 +83,7 @@ export default () => {
Board: () => import('ee_else_ce/boards/components/board_column.vue'),
BoardSidebar,
BoardAddIssuesModal,
- BoardSettingsSidebar: () =>
- import('ee_component/boards/components/board_settings_sidebar.vue'),
+ BoardSettingsSidebar: () => import('~/boards/components/board_settings_sidebar.vue'),
},
store,
apolloProvider,
@@ -118,7 +117,7 @@ export default () => {
boardId: this.boardId,
fullPath: $boardApp.dataset.fullPath,
};
- this.setEndpoints(endpoints);
+ this.setInitialBoardData({ ...endpoints, boardType: this.parent });
boardsStore.setEndpoints(endpoints);
boardsStore.rootPath = this.boardsEndpoint;
@@ -190,7 +189,7 @@ export default () => {
}
},
methods: {
- ...mapActions(['setEndpoints']),
+ ...mapActions(['setInitialBoardData']),
updateTokens() {
this.filterManager.updateTokens();
},
diff --git a/app/assets/javascripts/boards/models/list.js b/app/assets/javascripts/boards/models/list.js
index 2aa92f86125..b8b30c958a9 100644
--- a/app/assets/javascripts/boards/models/list.js
+++ b/app/assets/javascripts/boards/models/list.js
@@ -1,12 +1,11 @@
/* eslint-disable no-underscore-dangle, class-methods-use-this */
-
-import ListIssue from 'ee_else_ce/boards/models/issue';
import { __ } from '~/locale';
import ListLabel from './label';
import ListAssignee from './assignee';
-import flash from '~/flash';
+import { deprecatedCreateFlash as flash } from '~/flash';
import boardsStore from '../stores/boards_store';
import ListMilestone from './milestone';
+import 'ee_else_ce/boards/models/issue';
const TYPES = {
backlog: {
@@ -61,7 +60,9 @@ class List {
this.title = this.milestone.title;
}
- if (!typeInfo.isBlank && this.id) {
+ // doNotFetchIssues is a temporary workaround until issues are fetched using GraphQL on issue boards
+ // Issue: https://gitlab.com/gitlab-org/gitlab/-/issues/229416
+ if (!typeInfo.isBlank && this.id && !obj.doNotFetchIssues) {
this.getIssues().catch(() => {
// TODO: handle request error
});
@@ -100,12 +101,6 @@ class List {
return boardsStore.newListIssue(this, issue);
}
- createIssues(data) {
- data.forEach(issueObj => {
- this.addIssue(new ListIssue(issueObj));
- });
- }
-
addMultipleIssues(issues, listFrom, newIndex) {
boardsStore.addMultipleListIssues(this, issues, listFrom, newIndex);
}
diff --git a/app/assets/javascripts/boards/queries/board_list_shared.fragment.graphql b/app/assets/javascripts/boards/queries/board_list_shared.fragment.graphql
index 5b532906f6a..8abd79332fb 100644
--- a/app/assets/javascripts/boards/queries/board_list_shared.fragment.graphql
+++ b/app/assets/javascripts/boards/queries/board_list_shared.fragment.graphql
@@ -4,6 +4,7 @@ fragment BoardListShared on BoardList {
position
listType
collapsed
+ maxIssueCount
label {
id
title
diff --git a/app/assets/javascripts/boards/queries/group_lists_issues.query.graphql b/app/assets/javascripts/boards/queries/group_lists_issues.query.graphql
new file mode 100644
index 00000000000..724c7884c58
--- /dev/null
+++ b/app/assets/javascripts/boards/queries/group_lists_issues.query.graphql
@@ -0,0 +1,18 @@
+#import "./issue.fragment.graphql"
+
+query GroupListIssues($fullPath: ID!, $boardId: ID!) {
+ group(fullPath: $fullPath) {
+ board(id: $boardId) {
+ lists {
+ nodes {
+ id
+ issues {
+ nodes {
+ ...IssueNode
+ }
+ }
+ }
+ }
+ }
+ }
+}
diff --git a/app/assets/javascripts/boards/queries/issue.fragment.graphql b/app/assets/javascripts/boards/queries/issue.fragment.graphql
new file mode 100644
index 00000000000..89d56b895a4
--- /dev/null
+++ b/app/assets/javascripts/boards/queries/issue.fragment.graphql
@@ -0,0 +1,31 @@
+#import "~/graphql_shared/fragments/user.fragment.graphql"
+
+fragment IssueNode on Issue {
+ id
+ iid
+ title
+ referencePath: reference(full: true)
+ dueDate
+ timeEstimate
+ weight
+ confidential
+ webUrl
+ subscribed
+ blocked
+ epic {
+ id
+ }
+ assignees {
+ nodes {
+ ...User
+ }
+ }
+ labels {
+ nodes {
+ id
+ title
+ color
+ description
+ }
+ }
+}
diff --git a/app/assets/javascripts/boards/queries/project_lists_issues.query.graphql b/app/assets/javascripts/boards/queries/project_lists_issues.query.graphql
new file mode 100644
index 00000000000..149b76848ef
--- /dev/null
+++ b/app/assets/javascripts/boards/queries/project_lists_issues.query.graphql
@@ -0,0 +1,18 @@
+#import "./issue.fragment.graphql"
+
+query ProjectListIssues($fullPath: ID!, $boardId: ID!) {
+ project(fullPath: $fullPath) {
+ board(id: $boardId) {
+ lists {
+ nodes {
+ id
+ issues {
+ nodes {
+ ...IssueNode
+ }
+ }
+ }
+ }
+ }
+ }
+}
diff --git a/app/assets/javascripts/boards/stores/actions.js b/app/assets/javascripts/boards/stores/actions.js
index 08fedb14dff..b4be7546252 100644
--- a/app/assets/javascripts/boards/stores/actions.js
+++ b/app/assets/javascripts/boards/stores/actions.js
@@ -1,4 +1,11 @@
import * as types from './mutation_types';
+import createDefaultClient from '~/lib/graphql';
+import { BoardType } from '~/boards/constants';
+import { formatListIssues } from '../boards_util';
+import groupListsIssuesQuery from '../queries/group_lists_issues.query.graphql';
+import projectListsIssuesQuery from '../queries/project_lists_issues.query.graphql';
+
+const gqlClient = createDefaultClient();
const notImplemented = () => {
/* eslint-disable-next-line @gitlab/require-i18n-strings */
@@ -6,8 +13,12 @@ const notImplemented = () => {
};
export default {
- setEndpoints: ({ commit }, endpoints) => {
- commit(types.SET_ENDPOINTS, endpoints);
+ setInitialBoardData: ({ commit }, data) => {
+ commit(types.SET_INITIAL_BOARD_DATA, data);
+ },
+
+ setActiveId({ commit }, id) {
+ commit(types.SET_ACTIVE_ID, id);
},
fetchLists: () => {
@@ -34,6 +45,32 @@ export default {
notImplemented();
},
+ fetchIssuesForAllLists: ({ state, commit }) => {
+ commit(types.REQUEST_ISSUES_FOR_ALL_LISTS);
+
+ const { endpoints, boardType } = state;
+ const { fullPath, boardId } = endpoints;
+
+ const query = boardType === BoardType.group ? groupListsIssuesQuery : projectListsIssuesQuery;
+
+ const variables = {
+ fullPath,
+ boardId: `gid://gitlab/Board/${boardId}`,
+ };
+
+ return gqlClient
+ .query({
+ query,
+ variables,
+ })
+ .then(({ data }) => {
+ const { lists } = data[boardType]?.board;
+ const listIssues = formatListIssues(lists);
+ commit(types.RECEIVE_ISSUES_FOR_ALL_LISTS_SUCCESS, listIssues);
+ })
+ .catch(() => commit(types.RECEIVE_ISSUES_FOR_ALL_LISTS_FAILURE));
+ },
+
moveIssue: () => {
notImplemented();
},
diff --git a/app/assets/javascripts/boards/stores/boards_store.js b/app/assets/javascripts/boards/stores/boards_store.js
index da7d2e19ec1..30c71d64085 100644
--- a/app/assets/javascripts/boards/stores/boards_store.js
+++ b/app/assets/javascripts/boards/stores/boards_store.js
@@ -1,6 +1,6 @@
/* eslint-disable no-shadow, no-param-reassign,consistent-return */
/* global List */
-
+/* global ListIssue */
import $ from 'jquery';
import { sortBy } from 'lodash';
import Vue from 'vue';
@@ -81,7 +81,7 @@ const boardsStore = {
showPage(page) {
this.state.currentPage = page;
},
- addList(listObj) {
+ updateListPosition(listObj) {
const listType = listObj.listType || listObj.list_type;
let { position } = listObj;
if (listType === ListType.closed) {
@@ -91,6 +91,10 @@ const boardsStore = {
}
const list = new List({ ...listObj, position });
+ return list;
+ },
+ addList(listObj) {
+ const list = this.updateListPosition(listObj);
this.state.lists = sortBy([...this.state.lists, list], 'position');
return list;
},
@@ -641,7 +645,9 @@ const boardsStore = {
list.issues = [];
}
- list.createIssues(data.issues);
+ data.issues.forEach(issueObj => {
+ list.addIssue(new ListIssue(issueObj));
+ });
return data;
});
@@ -848,19 +854,28 @@ const boardsStore = {
},
refreshIssueData(issue, obj) {
- issue.id = obj.id;
- issue.iid = obj.iid;
- issue.title = obj.title;
- issue.confidential = obj.confidential;
- issue.dueDate = obj.due_date;
- issue.sidebarInfoEndpoint = obj.issue_sidebar_endpoint;
- issue.referencePath = obj.reference_path;
- issue.path = obj.real_path;
- issue.toggleSubscriptionEndpoint = obj.toggle_subscription_endpoint;
+ // issue.id = obj.id;
+ // issue.iid = obj.iid;
+ // issue.title = obj.title;
+ // issue.confidential = obj.confidential;
+ // issue.dueDate = obj.due_date || obj.dueDate;
+ // issue.sidebarInfoEndpoint = obj.issue_sidebar_endpoint;
+ // issue.referencePath = obj.reference_path || obj.referencePath;
+ // issue.path = obj.real_path || obj.webUrl;
+ // issue.toggleSubscriptionEndpoint = obj.toggle_subscription_endpoint;
+ // issue.project_id = obj.project_id;
+ // issue.timeEstimate = obj.time_estimate || obj.timeEstimate;
+ // issue.assignableLabelsEndpoint = obj.assignable_labels_endpoint;
+ // issue.blocked = obj.blocked;
+ // issue.epic = obj.epic;
+
+ const convertedObj = convertObjectPropsToCamelCase(obj, {
+ dropKeys: ['issue_sidebar_endpoint', 'real_path', 'webUrl'],
+ });
+ convertedObj.sidebarInfoEndpoint = obj.issue_sidebar_endpoint;
+ issue.path = obj.real_path || obj.webUrl;
issue.project_id = obj.project_id;
- issue.timeEstimate = obj.time_estimate;
- issue.assignableLabelsEndpoint = obj.assignable_labels_endpoint;
- issue.blocked = obj.blocked;
+ Object.assign(issue, convertedObj);
if (obj.project) {
issue.project = new IssueProject(obj.project);
diff --git a/app/assets/javascripts/boards/stores/mutation_types.js b/app/assets/javascripts/boards/stores/mutation_types.js
index fcdfa6799b6..0f96dc2e287 100644
--- a/app/assets/javascripts/boards/stores/mutation_types.js
+++ b/app/assets/javascripts/boards/stores/mutation_types.js
@@ -1,4 +1,4 @@
-export const SET_ENDPOINTS = 'SET_ENDPOINTS';
+export const SET_INITIAL_BOARD_DATA = 'SET_INITIAL_BOARD_DATA';
export const REQUEST_ADD_LIST = 'REQUEST_ADD_LIST';
export const RECEIVE_ADD_LIST_SUCCESS = 'RECEIVE_ADD_LIST_SUCCESS';
export const RECEIVE_ADD_LIST_ERROR = 'RECEIVE_ADD_LIST_ERROR';
@@ -8,6 +8,9 @@ export const RECEIVE_UPDATE_LIST_ERROR = 'RECEIVE_UPDATE_LIST_ERROR';
export const REQUEST_REMOVE_LIST = 'REQUEST_REMOVE_LIST';
export const RECEIVE_REMOVE_LIST_SUCCESS = 'RECEIVE_REMOVE_LIST_SUCCESS';
export const RECEIVE_REMOVE_LIST_ERROR = 'RECEIVE_REMOVE_LIST_ERROR';
+export const REQUEST_ISSUES_FOR_ALL_LISTS = 'REQUEST_ISSUES_FOR_ALL_LISTS';
+export const RECEIVE_ISSUES_FOR_ALL_LISTS_SUCCESS = 'RECEIVE_ISSUES_FOR_ALL_LISTS_SUCCESS';
+export const RECEIVE_ISSUES_FOR_ALL_LISTS_FAILURE = 'RECEIVE_ISSUES_FOR_ALL_LISTS_FAILURE';
export const REQUEST_ADD_ISSUE = 'REQUEST_ADD_ISSUE';
export const RECEIVE_ADD_ISSUE_SUCCESS = 'RECEIVE_ADD_ISSUE_SUCCESS';
export const RECEIVE_ADD_ISSUE_ERROR = 'RECEIVE_ADD_ISSUE_ERROR';
@@ -19,3 +22,4 @@ export const RECEIVE_UPDATE_ISSUE_SUCCESS = 'RECEIVE_UPDATE_ISSUE_SUCCESS';
export const RECEIVE_UPDATE_ISSUE_ERROR = 'RECEIVE_UPDATE_ISSUE_ERROR';
export const SET_CURRENT_PAGE = 'SET_CURRENT_PAGE';
export const TOGGLE_EMPTY_STATE = 'TOGGLE_EMPTY_STATE';
+export const SET_ACTIVE_ID = 'SET_ACTIVE_ID';
diff --git a/app/assets/javascripts/boards/stores/mutations.js b/app/assets/javascripts/boards/stores/mutations.js
index e4459cdcc07..ca9b911ce5b 100644
--- a/app/assets/javascripts/boards/stores/mutations.js
+++ b/app/assets/javascripts/boards/stores/mutations.js
@@ -6,8 +6,14 @@ const notImplemented = () => {
};
export default {
- [mutationTypes.SET_ENDPOINTS]: (state, endpoints) => {
+ [mutationTypes.SET_INITIAL_BOARD_DATA]: (state, data) => {
+ const { boardType, ...endpoints } = data;
state.endpoints = endpoints;
+ state.boardType = boardType;
+ },
+
+ [mutationTypes.SET_ACTIVE_ID](state, id) {
+ state.activeId = id;
},
[mutationTypes.REQUEST_ADD_LIST]: () => {
@@ -46,6 +52,20 @@ export default {
notImplemented();
},
+ [mutationTypes.REQUEST_ISSUES_FOR_ALL_LISTS]: state => {
+ state.isLoadingIssues = true;
+ },
+
+ [mutationTypes.RECEIVE_ISSUES_FOR_ALL_LISTS_SUCCESS]: (state, listIssues) => {
+ state.issuesByListId = listIssues;
+ state.isLoadingIssues = false;
+ },
+
+ [mutationTypes.RECEIVE_ISSUES_FOR_ALL_LISTS_FAILURE]: state => {
+ state.listIssueFetchFailure = true;
+ state.isLoadingIssues = false;
+ },
+
[mutationTypes.REQUEST_ADD_ISSUE]: () => {
notImplemented();
},
diff --git a/app/assets/javascripts/boards/stores/state.js b/app/assets/javascripts/boards/stores/state.js
index aca93c4d7c6..cb6930774ed 100644
--- a/app/assets/javascripts/boards/stores/state.js
+++ b/app/assets/javascripts/boards/stores/state.js
@@ -1,7 +1,11 @@
-import { inactiveListId } from '~/boards/constants';
+import { inactiveId } from '~/boards/constants';
export default () => ({
endpoints: {},
+ boardType: null,
isShowingLabels: true,
- activeListId: inactiveListId,
+ activeId: inactiveId,
+ issuesByListId: {},
+ isLoadingIssues: false,
+ listIssueFetchFailure: false,
});