Commit 493e0e17 authored by Jose Ivan Vargas's avatar Jose Ivan Vargas

Merge branch '285073-boards-remove-the-use-of-list-model-from-graphql-boards' into 'master'

Boards - Remove List model from GraphQL boards [RUN AS-IF-FOSS]

See merge request gitlab-org/gitlab!48854
parents d8d942c1 a2edabc8
...@@ -2,20 +2,28 @@ import { sortBy } from 'lodash'; ...@@ -2,20 +2,28 @@ import { sortBy } from 'lodash';
import axios from '~/lib/utils/axios_utils'; import axios from '~/lib/utils/axios_utils';
import { ListType } from './constants'; import { ListType } from './constants';
import { getIdFromGraphQLId } from '~/graphql_shared/utils'; import { getIdFromGraphQLId } from '~/graphql_shared/utils';
import boardsStore from '~/boards/stores/boards_store';
export function getMilestone() { export function getMilestone() {
return null; return null;
} }
export function updateListPosition(listObj) {
const { listType } = listObj;
let { position } = listObj;
if (listType === ListType.closed) {
position = Infinity;
} else if (listType === ListType.backlog) {
position = -Infinity;
}
return { ...listObj, position };
}
export function formatBoardLists(lists) { export function formatBoardLists(lists) {
const formattedLists = lists.nodes.map(list => return lists.nodes.reduce((map, list) => {
boardsStore.updateListPosition({ ...list, doNotFetchIssues: true }),
);
return formattedLists.reduce((map, list) => {
return { return {
...map, ...map,
[list.id]: list, [list.id]: updateListPosition(list),
}; };
}, {}); }, {});
} }
...@@ -85,22 +93,22 @@ export function fullLabelId(label) { ...@@ -85,22 +93,22 @@ export function fullLabelId(label) {
export function moveIssueListHelper(issue, fromList, toList) { export function moveIssueListHelper(issue, fromList, toList) {
const updatedIssue = issue; const updatedIssue = issue;
if ( if (
toList.type === ListType.label && toList.listType === ListType.label &&
!updatedIssue.labels.find(label => label.id === toList.label.id) !updatedIssue.labels.find(label => label.id === toList.label.id)
) { ) {
updatedIssue.labels.push(toList.label); updatedIssue.labels.push(toList.label);
} }
if (fromList?.label && fromList.type === ListType.label) { if (fromList?.label && fromList.listType === ListType.label) {
updatedIssue.labels = updatedIssue.labels.filter(label => fromList.label.id !== label.id); updatedIssue.labels = updatedIssue.labels.filter(label => fromList.label.id !== label.id);
} }
if ( if (
toList.type === ListType.assignee && toList.listType === ListType.assignee &&
!updatedIssue.assignees.find(assignee => assignee.id === toList.assignee.id) !updatedIssue.assignees.find(assignee => assignee.id === toList.assignee.id)
) { ) {
updatedIssue.assignees.push(toList.assignee); updatedIssue.assignees.push(toList.assignee);
} }
if (fromList?.assignee && fromList.type === ListType.assignee) { if (fromList?.assignee && fromList.listType === ListType.assignee) {
updatedIssue.assignees = updatedIssue.assignees.filter( updatedIssue.assignees = updatedIssue.assignees.filter(
assignee => assignee.id !== fromList.assignee.id, assignee => assignee.id !== fromList.assignee.id,
); );
...@@ -118,6 +126,10 @@ export function getBoardsPath(endpoint, board) { ...@@ -118,6 +126,10 @@ export function getBoardsPath(endpoint, board) {
return axios.post(path, { board }); return axios.post(path, { board });
} }
export function isListDraggable(list) {
return list.listType !== ListType.backlog && list.listType !== ListType.closed;
}
export default { export default {
getMilestone, getMilestone,
formatIssue, formatIssue,
...@@ -125,4 +137,5 @@ export default { ...@@ -125,4 +137,5 @@ export default {
fullBoardId, fullBoardId,
fullLabelId, fullLabelId,
getBoardsPath, getBoardsPath,
isListDraggable,
}; };
...@@ -2,6 +2,7 @@ ...@@ -2,6 +2,7 @@
import { mapGetters, mapActions, mapState } from 'vuex'; import { mapGetters, mapActions, mapState } from 'vuex';
import BoardListHeader from 'ee_else_ce/boards/components/board_list_header_new.vue'; import BoardListHeader from 'ee_else_ce/boards/components/board_list_header_new.vue';
import BoardList from './board_list_new.vue'; import BoardList from './board_list_new.vue';
import { isListDraggable } from '../boards_util';
export default { export default {
components: { components: {
...@@ -35,6 +36,9 @@ export default { ...@@ -35,6 +36,9 @@ export default {
listIssues() { listIssues() {
return this.getIssuesByList(this.list.id); return this.getIssuesByList(this.list.id);
}, },
isListDraggable() {
return isListDraggable(this.list);
},
}, },
watch: { watch: {
filterParams: { filterParams: {
...@@ -47,7 +51,6 @@ export default { ...@@ -47,7 +51,6 @@ export default {
}, },
methods: { methods: {
...mapActions(['fetchIssuesForList']), ...mapActions(['fetchIssuesForList']),
// TODO: Reordering of lists https://gitlab.com/gitlab-org/gitlab/-/issues/280515
}, },
}; };
</script> </script>
...@@ -55,13 +58,12 @@ export default { ...@@ -55,13 +58,12 @@ export default {
<template> <template>
<div <div
:class="{ :class="{
'is-draggable': !list.preset, 'is-draggable': isListDraggable,
'is-expandable': list.isExpandable, 'is-collapsed': list.collapsed,
'is-collapsed': !list.isExpanded, 'board-type-assignee': list.listType === 'assignee',
'board-type-assignee': list.type === 'assignee',
}" }"
:data-id="list.id" :data-id="list.id"
class="board gl-display-inline-block gl-h-full gl-px-3 gl-vertical-align-top gl-white-space-normal" class="board gl-display-inline-block gl-h-full gl-px-3 gl-vertical-align-top gl-white-space-normal is-expandable"
data-qa-selector="board_list" data-qa-selector="board_list"
> >
<div <div
......
...@@ -103,9 +103,6 @@ export default { ...@@ -103,9 +103,6 @@ export default {
:key="list.id" :key="list.id"
ref="board" ref="board"
:can-admin-list="canAdminList" :can-admin-list="canAdminList"
:class="{
'is-draggable': !list.preset,
}"
:list="list" :list="list"
:disabled="disabled" :disabled="disabled"
/> />
......
...@@ -9,15 +9,22 @@ import { ...@@ -9,15 +9,22 @@ import {
GlSprintf, GlSprintf,
GlTooltipDirective, GlTooltipDirective,
} from '@gitlab/ui'; } from '@gitlab/ui';
import { n__, s__ } from '~/locale'; import { n__, s__, __ } from '~/locale';
import AccessorUtilities from '../../lib/utils/accessor'; import AccessorUtilities from '../../lib/utils/accessor';
import IssueCount from './issue_count.vue'; import IssueCount from './issue_count.vue';
import eventHub from '../eventhub'; import eventHub from '../eventhub';
import sidebarEventHub from '~/sidebar/event_hub'; import sidebarEventHub from '~/sidebar/event_hub';
import { inactiveId, LIST, ListType } from '../constants'; import { inactiveId, LIST, ListType } from '../constants';
import { isScopedLabel } from '~/lib/utils/common_utils'; import { isScopedLabel } from '~/lib/utils/common_utils';
import { isListDraggable } from '~/boards/boards_util';
export default { export default {
i18n: {
newIssue: __('New issue'),
listSettings: __('List settings'),
expand: s__('Boards|Expand'),
collapse: s__('Boards|Collapse'),
},
components: { components: {
GlButtonGroup, GlButtonGroup,
GlButton, GlButton,
...@@ -66,47 +73,47 @@ export default { ...@@ -66,47 +73,47 @@ export default {
return Boolean(this.currentUserId); return Boolean(this.currentUserId);
}, },
listType() { listType() {
return this.list.type; return this.list.listType;
}, },
listAssignee() { listAssignee() {
return this.list?.assignee?.username || ''; return this.list?.assignee?.username || '';
}, },
listTitle() { listTitle() {
return this.list?.label?.description || this.list.title || ''; return this.list?.label?.description || this.list?.assignee?.name || this.list.title || '';
}, },
showListHeaderButton() { showListHeaderButton() {
return !this.disabled && this.listType !== ListType.closed; return !this.disabled && this.listType !== ListType.closed;
}, },
showMilestoneListDetails() { showMilestoneListDetails() {
return ( return (
this.list.type === ListType.milestone && this.listType === ListType.milestone &&
this.list.milestone && this.list.milestone &&
(this.list.isExpanded || !this.isSwimlanesHeader) (!this.list.collapsed || !this.isSwimlanesHeader)
); );
}, },
showAssigneeListDetails() { showAssigneeListDetails() {
return ( return (
this.list.type === ListType.assignee && (this.list.isExpanded || !this.isSwimlanesHeader) this.listType === ListType.assignee && (!this.list.collapsed || !this.isSwimlanesHeader)
); );
}, },
issuesCount() { issuesCount() {
return this.list.issuesSize; return this.list.issuesCount;
}, },
issuesTooltipLabel() { issuesTooltipLabel() {
return n__(`%d issue`, `%d issues`, this.issuesCount); return n__(`%d issue`, `%d issues`, this.issuesCount);
}, },
chevronTooltip() { chevronTooltip() {
return this.list.isExpanded ? s__('Boards|Collapse') : s__('Boards|Expand'); return this.list.collapsed ? this.$options.i18n.expand : this.$options.i18n.collapse;
}, },
chevronIcon() { chevronIcon() {
return this.list.isExpanded ? 'chevron-right' : 'chevron-down'; return this.list.collapsed ? 'chevron-down' : 'chevron-right';
}, },
isNewIssueShown() { isNewIssueShown() {
return this.listType === ListType.backlog || this.showListHeaderButton; return this.listType === ListType.backlog || this.showListHeaderButton;
}, },
isSettingsShown() { isSettingsShown() {
return ( return (
this.listType !== ListType.backlog && this.showListHeaderButton && this.list.isExpanded this.listType !== ListType.backlog && this.showListHeaderButton && !this.list.collapsed
); );
}, },
uniqueKey() { uniqueKey() {
...@@ -119,6 +126,9 @@ export default { ...@@ -119,6 +126,9 @@ export default {
headerStyle() { headerStyle() {
return { borderTopColor: this.list?.label?.color }; return { borderTopColor: this.list?.label?.color };
}, },
userCanDrag() {
return !this.disabled && isListDraggable(this.list);
},
}, },
methods: { methods: {
...mapActions(['updateList', 'setActiveId']), ...mapActions(['updateList', 'setActiveId']),
...@@ -137,7 +147,7 @@ export default { ...@@ -137,7 +147,7 @@ export default {
eventHub.$emit(`toggle-issue-form-${this.list.id}`); eventHub.$emit(`toggle-issue-form-${this.list.id}`);
}, },
toggleExpanded() { toggleExpanded() {
this.list.isExpanded = !this.list.isExpanded; this.list.collapsed = !this.list.collapsed;
if (!this.isLoggedIn) { if (!this.isLoggedIn) {
this.addToLocalStorage(); this.addToLocalStorage();
...@@ -151,11 +161,11 @@ export default { ...@@ -151,11 +161,11 @@ export default {
}, },
addToLocalStorage() { addToLocalStorage() {
if (AccessorUtilities.isLocalStorageAccessSafe()) { if (AccessorUtilities.isLocalStorageAccessSafe()) {
localStorage.setItem(`${this.uniqueKey}.expanded`, this.list.isExpanded); localStorage.setItem(`${this.uniqueKey}.expanded`, !this.list.collapsed);
} }
}, },
updateListFunction() { updateListFunction() {
this.updateList({ listId: this.list.id, collapsed: !this.list.isExpanded }); this.updateList({ listId: this.list.id, collapsed: this.list.collapsed });
}, },
}, },
}; };
...@@ -165,7 +175,7 @@ export default { ...@@ -165,7 +175,7 @@ export default {
<header <header
:class="{ :class="{
'has-border': list.label && list.label.color, 'has-border': list.label && list.label.color,
'gl-h-full': !list.isExpanded, 'gl-h-full': list.collapsed,
'board-inner gl-rounded-top-left-base gl-rounded-top-right-base': isSwimlanesHeader, 'board-inner gl-rounded-top-left-base gl-rounded-top-right-base': isSwimlanesHeader,
}" }"
:style="headerStyle" :style="headerStyle"
...@@ -175,16 +185,15 @@ export default { ...@@ -175,16 +185,15 @@ export default {
> >
<h3 <h3
:class="{ :class="{
'user-can-drag': !disabled && !list.preset, 'user-can-drag': userCanDrag,
'gl-py-3 gl-h-full': !list.isExpanded && !isSwimlanesHeader, 'gl-py-3 gl-h-full': list.collapsed && !isSwimlanesHeader,
'gl-border-b-0': !list.isExpanded || isSwimlanesHeader, 'gl-border-b-0': list.collapsed || isSwimlanesHeader,
'gl-py-2': !list.isExpanded && isSwimlanesHeader, 'gl-py-2': list.collapsed && isSwimlanesHeader,
'gl-flex-direction-column': !list.isExpanded, 'gl-flex-direction-column': list.collapsed,
}" }"
class="board-title gl-m-0 gl-display-flex gl-align-items-center gl-font-base gl-px-3 js-board-handle" class="board-title gl-m-0 gl-display-flex gl-align-items-center gl-font-base gl-px-3 js-board-handle"
> >
<gl-button <gl-button
v-if="list.isExpandable"
v-gl-tooltip.hover v-gl-tooltip.hover
:aria-label="chevronTooltip" :aria-label="chevronTooltip"
:title="chevronTooltip" :title="chevronTooltip"
...@@ -200,8 +209,8 @@ export default { ...@@ -200,8 +209,8 @@ export default {
aria-hidden="true" aria-hidden="true"
class="milestone-icon" class="milestone-icon"
:class="{ :class="{
'gl-mt-3 gl-rotate-90': !list.isExpanded, 'gl-mt-3 gl-rotate-90': list.collapsed,
'gl-mr-2': list.isExpanded, 'gl-mr-2': !list.collapsed,
}" }"
> >
<gl-icon name="timer" /> <gl-icon name="timer" />
...@@ -209,17 +218,17 @@ export default { ...@@ -209,17 +218,17 @@ export default {
<a <a
v-if="showAssigneeListDetails" v-if="showAssigneeListDetails"
:href="list.assignee.path" :href="list.assignee.webUrl"
class="user-avatar-link js-no-trigger" class="user-avatar-link js-no-trigger"
:class="{ :class="{
'gl-mt-3 gl-rotate-90': !list.isExpanded, 'gl-mt-3 gl-rotate-90': list.collapsed,
}" }"
> >
<img <img
v-gl-tooltip.hover.bottom v-gl-tooltip.hover.bottom
:title="listAssignee" :title="listAssignee"
:alt="list.assignee.name" :alt="list.assignee.name"
:src="list.assignee.avatar" :src="list.assignee.avatarUrl"
class="avatar s20" class="avatar s20"
height="20" height="20"
width="20" width="20"
...@@ -229,9 +238,9 @@ export default { ...@@ -229,9 +238,9 @@ export default {
<div <div
class="board-title-text" class="board-title-text"
:class="{ :class="{
'gl-display-none': !list.isExpanded && isSwimlanesHeader, 'gl-display-none': list.collapsed && isSwimlanesHeader,
'gl-flex-grow-0 gl-my-3 gl-mx-0': !list.isExpanded, 'gl-flex-grow-0 gl-my-3 gl-mx-0': list.collapsed,
'gl-flex-grow-1': list.isExpanded, 'gl-flex-grow-1': !list.collapsed,
}" }"
> >
<!-- EE start --> <!-- EE start -->
...@@ -239,16 +248,16 @@ export default { ...@@ -239,16 +248,16 @@ export default {
v-if="listType !== 'label'" v-if="listType !== 'label'"
v-gl-tooltip.hover v-gl-tooltip.hover
:class="{ :class="{
'gl-display-block': !list.isExpanded || listType === 'milestone', 'gl-display-block': list.collapsed || listType === 'milestone',
}" }"
:title="listTitle" :title="listTitle"
class="board-title-main-text gl-text-truncate" class="board-title-main-text gl-text-truncate"
> >
{{ list.title }} {{ listTitle }}
</span> </span>
<span <span
v-if="listType === 'assignee'" v-if="listType === 'assignee'"
v-show="list.isExpanded" v-show="!list.collapsed"
class="gl-ml-2 gl-font-weight-normal gl-text-gray-500" class="gl-ml-2 gl-font-weight-normal gl-text-gray-500"
> >
@{{ listAssignee }} @{{ listAssignee }}
...@@ -260,21 +269,21 @@ export default { ...@@ -260,21 +269,21 @@ export default {
:background-color="list.label.color" :background-color="list.label.color"
:description="list.label.description" :description="list.label.description"
:scoped="showScopedLabels(list.label)" :scoped="showScopedLabels(list.label)"
:size="!list.isExpanded ? 'sm' : ''" :size="list.collapsed ? 'sm' : ''"
:title="list.label.title" :title="list.label.title"
/> />
</div> </div>
<!-- EE start --> <!-- EE start -->
<span <span
v-if="isSwimlanesHeader && !list.isExpanded" v-if="isSwimlanesHeader && list.collapsed"
ref="collapsedInfo" ref="collapsedInfo"
aria-hidden="true" aria-hidden="true"
class="board-header-collapsed-info-icon gl-cursor-pointer gl-text-gray-500" class="board-header-collapsed-info-icon gl-cursor-pointer gl-text-gray-500"
> >
<gl-icon name="information" /> <gl-icon name="information" />
</span> </span>
<gl-tooltip v-if="isSwimlanesHeader && !list.isExpanded" :target="() => $refs.collapsedInfo"> <gl-tooltip v-if="isSwimlanesHeader && list.collapsed" :target="() => $refs.collapsedInfo">
<div class="gl-font-weight-bold gl-pb-2">{{ collapsedTooltipTitle }}</div> <div class="gl-font-weight-bold gl-pb-2">{{ collapsedTooltipTitle }}</div>
<div v-if="list.maxIssueCount !== 0"> <div v-if="list.maxIssueCount !== 0">
...@@ -296,8 +305,8 @@ export default { ...@@ -296,8 +305,8 @@ export default {
<div <div
class="issue-count-badge gl-display-inline-flex gl-pr-0 no-drag gl-text-gray-500" class="issue-count-badge gl-display-inline-flex gl-pr-0 no-drag gl-text-gray-500"
:class="{ :class="{
'gl-display-none!': !list.isExpanded && isSwimlanesHeader, 'gl-display-none!': list.collapsed && isSwimlanesHeader,
'gl-p-0': !list.isExpanded, 'gl-p-0': list.collapsed,
}" }"
> >
<span class="gl-display-inline-flex"> <span class="gl-display-inline-flex">
...@@ -323,11 +332,11 @@ export default { ...@@ -323,11 +332,11 @@ export default {
> >
<gl-button <gl-button
v-if="isNewIssueShown" v-if="isNewIssueShown"
v-show="list.isExpanded" v-show="!list.collapsed"
ref="newIssueBtn" ref="newIssueBtn"
v-gl-tooltip.hover v-gl-tooltip.hover
:aria-label="__('New issue')" :aria-label="$options.i18n.newIssue"
:title="__('New issue')" :title="$options.i18n.newIssue"
class="issue-count-badge-add-button no-drag" class="issue-count-badge-add-button no-drag"
icon="plus" icon="plus"
@click="showNewIssueForm" @click="showNewIssueForm"
...@@ -337,13 +346,13 @@ export default { ...@@ -337,13 +346,13 @@ export default {
v-if="isSettingsShown" v-if="isSettingsShown"
ref="settingsBtn" ref="settingsBtn"
v-gl-tooltip.hover v-gl-tooltip.hover
:aria-label="__('List settings')" :aria-label="$options.i18n.listSettings"
class="no-drag js-board-settings-button" class="no-drag js-board-settings-button"
:title="__('List settings')" :title="$options.i18n.listSettings"
icon="settings" icon="settings"
@click="openSidebarSettings" @click="openSidebarSettings"
/> />
<gl-tooltip :target="() => $refs.settingsBtn">{{ __('List settings') }}</gl-tooltip> <gl-tooltip :target="() => $refs.settingsBtn">{{ $options.i18n.listSettings }}</gl-tooltip>
</gl-button-group> </gl-button-group>
</h3> </h3>
</header> </header>
......
...@@ -12,6 +12,11 @@ import { sprintf, __ } from '~/locale'; ...@@ -12,6 +12,11 @@ import { sprintf, __ } from '~/locale';
export default { export default {
name: 'BoardList', name: 'BoardList',
i18n: {
loadingIssues: __('Loading issues'),
loadingMoreissues: __('Loading more issues'),
showingAllIssues: __('Showing all issues'),
},
components: { components: {
BoardCard, BoardCard,
BoardNewIssue, BoardNewIssue,
...@@ -49,11 +54,11 @@ export default { ...@@ -49,11 +54,11 @@ export default {
paginatedIssueText() { paginatedIssueText() {
return sprintf(__('Showing %{pageSize} of %{total} issues'), { return sprintf(__('Showing %{pageSize} of %{total} issues'), {
pageSize: this.issues.length, pageSize: this.issues.length,
total: this.list.issuesSize, total: this.list.issuesCount,
}); });
}, },
issuesSizeExceedsMax() { issuesSizeExceedsMax() {
return this.list.maxIssueCount > 0 && this.list.issuesSize > this.list.maxIssueCount; return this.list.maxIssueCount > 0 && this.list.issuesCount > this.list.maxIssueCount;
}, },
hasNextPage() { hasNextPage() {
return this.pageInfoByListId[this.list.id].hasNextPage; return this.pageInfoByListId[this.list.id].hasNextPage;
...@@ -61,10 +66,16 @@ export default { ...@@ -61,10 +66,16 @@ export default {
loading() { loading() {
return this.listsFlags[this.list.id]?.isLoading; return this.listsFlags[this.list.id]?.isLoading;
}, },
loadingMore() {
return this.listsFlags[this.list.id]?.isLoadingMore;
},
listRef() { listRef() {
// When list is draggable, the reference to the list needs to be accessed differently // When list is draggable, the reference to the list needs to be accessed differently
return this.canAdminList ? this.$refs.list.$el : this.$refs.list; return this.canAdminList ? this.$refs.list.$el : this.$refs.list;
}, },
showingAllIssues() {
return this.issues.length === this.list.issuesCount;
},
treeRootWrapper() { treeRootWrapper() {
return this.canAdminList ? Draggable : 'ul'; return this.canAdminList ? Draggable : 'ul';
}, },
...@@ -72,7 +83,7 @@ export default { ...@@ -72,7 +83,7 @@ export default {
const options = { const options = {
...defaultSortableConfig, ...defaultSortableConfig,
fallbackOnBody: false, fallbackOnBody: false,
group: 'boards-list', group: 'board-list',
tag: 'ul', tag: 'ul',
'ghost-class': 'board-card-drag-active', 'ghost-class': 'board-card-drag-active',
'data-list-id': this.list.id, 'data-list-id': this.list.id,
...@@ -85,7 +96,6 @@ export default { ...@@ -85,7 +96,6 @@ export default {
watch: { watch: {
filters: { filters: {
handler() { handler() {
this.list.loadingMore = false;
this.listRef.scrollTop = 0; this.listRef.scrollTop = 0;
}, },
deep: true, deep: true,
...@@ -124,13 +134,7 @@ export default { ...@@ -124,13 +134,7 @@ export default {
this.listRef.scrollTop = 0; this.listRef.scrollTop = 0;
}, },
loadNextPage() { loadNextPage() {
const loadingDone = () => { this.fetchIssuesForList({ listId: this.list.id, fetchNext: true });
this.list.loadingMore = false;
};
this.list.loadingMore = true;
this.fetchIssuesForList({ listId: this.list.id, fetchNext: true })
.then(loadingDone)
.catch(loadingDone);
}, },
toggleForm() { toggleForm() {
this.showIssueForm = !this.showIssueForm; this.showIssueForm = !this.showIssueForm;
...@@ -138,7 +142,7 @@ export default { ...@@ -138,7 +142,7 @@ export default {
onScroll() { onScroll() {
window.requestAnimationFrame(() => { window.requestAnimationFrame(() => {
if ( if (
!this.list.loadingMore && !this.loadingMore &&
this.scrollTop() > this.scrollHeight() - this.scrollOffset && this.scrollTop() > this.scrollHeight() - this.scrollOffset &&
this.hasNextPage this.hasNextPage
) { ) {
...@@ -198,26 +202,26 @@ export default { ...@@ -198,26 +202,26 @@ export default {
<template> <template>
<div <div
v-show="list.isExpanded" v-show="!list.collapsed"
class="board-list-component gl-relative gl-h-full gl-display-flex gl-flex-direction-column" class="board-list-component gl-relative gl-h-full gl-display-flex gl-flex-direction-column"
data-qa-selector="board_list_cards_area" data-qa-selector="board_list_cards_area"
> >
<div <div
v-if="loading" v-if="loading"
class="gl-mt-4 gl-text-center" class="gl-mt-4 gl-text-center"
:aria-label="__('Loading issues')" :aria-label="$options.i18n.loadingIssues"
data-testid="board_list_loading" data-testid="board_list_loading"
> >
<gl-loading-icon /> <gl-loading-icon />
</div> </div>
<board-new-issue v-if="list.type !== 'closed' && showIssueForm" :list="list" /> <board-new-issue v-if="list.listType !== 'closed' && showIssueForm" :list="list" />
<component <component
:is="treeRootWrapper" :is="treeRootWrapper"
v-show="!loading" v-show="!loading"
ref="list" ref="list"
v-bind="treeRootOptions" v-bind="treeRootOptions"
:data-board="list.id" :data-board="list.id"
:data-board-type="list.type" :data-board-type="list.listType"
:class="{ 'bg-danger-100': issuesSizeExceedsMax }" :class="{ 'bg-danger-100': issuesSizeExceedsMax }"
class="board-list gl-w-full gl-h-full gl-list-style-none gl-mb-0 gl-p-2 js-board-list" class="board-list gl-w-full gl-h-full gl-list-style-none gl-mb-0 gl-p-2 js-board-list"
data-testid="tree-root-wrapper" data-testid="tree-root-wrapper"
...@@ -234,8 +238,8 @@ export default { ...@@ -234,8 +238,8 @@ export default {
:disabled="disabled" :disabled="disabled"
/> />
<li v-if="showCount" class="board-list-count gl-text-center" data-issue-id="-1"> <li v-if="showCount" class="board-list-count gl-text-center" data-issue-id="-1">
<gl-loading-icon v-show="list.loadingMore" label="Loading more issues" /> <gl-loading-icon v-if="loadingMore" :label="$options.i18n.loadingMoreissues" />
<span v-if="issues.length === list.issuesSize">{{ __('Showing all issues') }}</span> <span v-if="showingAllIssues">{{ $options.i18n.showingAllIssues }}</span>
<span v-else>{{ paginatedIssueText }}</span> <span v-else>{{ paginatedIssueText }}</span>
</li> </li>
</component> </component>
......
...@@ -53,7 +53,7 @@ export default { ...@@ -53,7 +53,7 @@ export default {
return this.activeList.label; return this.activeList.label;
}, },
boardListType() { boardListType() {
return this.activeList.type || null; return this.activeList.type || this.activeList.listType || null;
}, },
listTypeTitle() { listTypeTitle() {
return this.$options.labelListText; return this.$options.labelListText;
......
...@@ -10,6 +10,7 @@ import IssueDueDate from './issue_due_date.vue'; ...@@ -10,6 +10,7 @@ import IssueDueDate from './issue_due_date.vue';
import IssueTimeEstimate from './issue_time_estimate.vue'; import IssueTimeEstimate from './issue_time_estimate.vue';
import boardsStore from '../stores/boards_store'; import boardsStore from '../stores/boards_store';
import { isScopedLabel } from '~/lib/utils/common_utils'; import { isScopedLabel } from '~/lib/utils/common_utils';
import { ListType } from '../constants';
export default { export default {
components: { components: {
...@@ -122,7 +123,13 @@ export default { ...@@ -122,7 +123,13 @@ export default {
return true; return true;
}, },
isNonListLabel(label) { isNonListLabel(label) {
return label.id && !(this.list.type === 'label' && this.list.title === label.title); return (
label.id &&
!(
(this.list.type || this.list.listType) === ListType.label &&
this.list.title === label.title
)
);
}, },
filterByLabel(label) { filterByLabel(label) {
if (!this.updateFilters) return; if (!this.updateFilters) return;
......
...@@ -7,6 +7,7 @@ import eventHub from '../eventhub'; ...@@ -7,6 +7,7 @@ import eventHub from '../eventhub';
import Api from '../../api'; import Api from '../../api';
import { featureAccessLevel } from '~/pages/projects/shared/permissions/constants'; import { featureAccessLevel } from '~/pages/projects/shared/permissions/constants';
import initDeprecatedJQueryDropdown from '~/deprecated_jquery_dropdown'; import initDeprecatedJQueryDropdown from '~/deprecated_jquery_dropdown';
import { ListType } from '../constants';
export default { export default {
name: 'BoardProjectSelect', name: 'BoardProjectSelect',
...@@ -53,7 +54,7 @@ export default { ...@@ -53,7 +54,7 @@ export default {
this.loading = true; this.loading = true;
const additionalAttrs = {}; const additionalAttrs = {};
if (this.list.type && this.list.type !== 'backlog') { if ((this.list.type || this.list.listType) !== ListType.backlog) {
additionalAttrs.min_access_level = featureAccessLevel.EVERYONE; additionalAttrs.min_access_level = featureAccessLevel.EVERYONE;
} }
......
...@@ -12,7 +12,6 @@ import { ...@@ -12,7 +12,6 @@ import {
formatListsPageInfo, formatListsPageInfo,
formatIssue, formatIssue,
} from '../boards_util'; } from '../boards_util';
import boardStore from '~/boards/stores/boards_store';
import createFlash from '~/flash'; import createFlash from '~/flash';
import { __ } from '~/locale'; import { __ } from '~/locale';
import updateAssigneesMutation from '~/vue_shared/components/sidebar/queries/updateAssignees.mutation.graphql'; import updateAssigneesMutation from '~/vue_shared/components/sidebar/queries/updateAssignees.mutation.graphql';
...@@ -119,11 +118,7 @@ export default { ...@@ -119,11 +118,7 @@ export default {
}, },
addList: ({ commit }, list) => { addList: ({ commit }, list) => {
// Temporarily using positioning logic from boardStore commit(types.RECEIVE_ADD_LIST_SUCCESS, list);
commit(
types.RECEIVE_ADD_LIST_SUCCESS,
boardStore.updateListPosition({ ...list, doNotFetchIssues: true }),
);
}, },
fetchLabels: ({ state, commit }, searchTerm) => { fetchLabels: ({ state, commit }, searchTerm) => {
......
...@@ -13,7 +13,7 @@ const notImplemented = () => { ...@@ -13,7 +13,7 @@ const notImplemented = () => {
export const removeIssueFromList = ({ state, listId, issueId }) => { export const removeIssueFromList = ({ state, listId, issueId }) => {
Vue.set(state.issuesByListId, listId, pull(state.issuesByListId[listId], issueId)); Vue.set(state.issuesByListId, listId, pull(state.issuesByListId[listId], issueId));
const list = state.boardLists[listId]; const list = state.boardLists[listId];
Vue.set(state.boardLists, listId, { ...list, issuesSize: list.issuesSize - 1 }); Vue.set(state.boardLists, listId, { ...list, issuesCount: list.issuesCount - 1 });
}; };
export const addIssueToList = ({ state, listId, issueId, moveBeforeId, moveAfterId, atIndex }) => { export const addIssueToList = ({ state, listId, issueId, moveBeforeId, moveAfterId, atIndex }) => {
...@@ -27,7 +27,7 @@ export const addIssueToList = ({ state, listId, issueId, moveBeforeId, moveAfter ...@@ -27,7 +27,7 @@ export const addIssueToList = ({ state, listId, issueId, moveBeforeId, moveAfter
listIssues.splice(newIndex, 0, issueId); listIssues.splice(newIndex, 0, issueId);
Vue.set(state.issuesByListId, listId, listIssues); Vue.set(state.issuesByListId, listId, listIssues);
const list = state.boardLists[listId]; const list = state.boardLists[listId];
Vue.set(state.boardLists, listId, { ...list, issuesSize: list.issuesSize + 1 }); Vue.set(state.boardLists, listId, { ...list, issuesCount: list.issuesCount + 1 });
}; };
export default { export default {
......
...@@ -8,6 +8,7 @@ import defaultSortableConfig from '~/sortable/sortable_config'; ...@@ -8,6 +8,7 @@ import defaultSortableConfig from '~/sortable/sortable_config';
import { n__ } from '~/locale'; import { n__ } from '~/locale';
import EpicLane from './epic_lane.vue'; import EpicLane from './epic_lane.vue';
import IssuesLaneList from './issues_lane_list.vue'; import IssuesLaneList from './issues_lane_list.vue';
import { isListDraggable } from '~/boards/boards_util';
export default { export default {
components: { components: {
...@@ -94,6 +95,9 @@ export default { ...@@ -94,6 +95,9 @@ export default {
} }
}); });
}, },
isListDraggable(list) {
return isListDraggable(list);
},
}, },
}; };
</script> </script>
...@@ -115,8 +119,8 @@ export default { ...@@ -115,8 +119,8 @@ export default {
v-for="list in lists" v-for="list in lists"
:key="list.id" :key="list.id"
:class="{ :class="{
'is-collapsed': !list.isExpanded, 'is-collapsed': list.collapsed,
'is-draggable': !list.preset, 'is-draggable': isListDraggable(list),
}" }"
class="board gl-display-inline-block gl-px-3 gl-vertical-align-top gl-white-space-normal" class="board gl-display-inline-block gl-px-3 gl-vertical-align-top gl-white-space-normal"
:data-list-id="list.id" :data-list-id="list.id"
......
...@@ -155,7 +155,7 @@ export default { ...@@ -155,7 +155,7 @@ export default {
<template> <template>
<div <div
class="board gl-px-3 gl-vertical-align-top gl-white-space-normal gl-display-flex gl-flex-shrink-0" class="board gl-px-3 gl-vertical-align-top gl-white-space-normal gl-display-flex gl-flex-shrink-0"
:class="{ 'is-collapsed': !list.isExpanded }" :class="{ 'is-collapsed': list.collapsed }"
> >
<div class="board-inner gl-rounded-base gl-relative gl-w-full"> <div class="board-inner gl-rounded-base gl-relative gl-w-full">
<board-new-issue <board-new-issue
...@@ -164,7 +164,7 @@ export default { ...@@ -164,7 +164,7 @@ export default {
/> />
<component <component
:is="treeRootWrapper" :is="treeRootWrapper"
v-if="list.isExpanded" v-if="!list.collapsed"
v-bind="treeRootOptions" v-bind="treeRootOptions"
class="board-cell gl-p-2 gl-m-0 gl-h-full" class="board-cell gl-p-2 gl-m-0 gl-h-full"
data-testid="tree-root-wrapper" data-testid="tree-root-wrapper"
......
...@@ -212,7 +212,7 @@ export default { ...@@ -212,7 +212,7 @@ export default {
const list = data.boardListUpdateLimitMetrics?.list; const list = data.boardListUpdateLimitMetrics?.list;
commit(types.UPDATE_LIST_SUCCESS, { commit(types.UPDATE_LIST_SUCCESS, {
listId, listId,
list: boardsStore.updateListPosition({ ...list, doNotFetchIssues: true }), list,
}); });
} }
}) })
......
...@@ -3,9 +3,8 @@ import Vuex from 'vuex'; ...@@ -3,9 +3,8 @@ import Vuex from 'vuex';
import BoardListHeader from 'ee/boards/components/board_list_header_new.vue'; import BoardListHeader from 'ee/boards/components/board_list_header_new.vue';
import getters from 'ee/boards/stores/getters'; import getters from 'ee/boards/stores/getters';
import { listObj } from 'jest/boards/mock_data'; import { mockLabelList } from 'jest/boards/mock_data';
import { ListType, inactiveId } from '~/boards/constants'; import { ListType, inactiveId } from '~/boards/constants';
import List from '~/boards/models/list';
import sidebarEventHub from '~/sidebar/event_hub'; import sidebarEventHub from '~/sidebar/event_hub';
const localVue = createLocalVue(); const localVue = createLocalVue();
...@@ -38,21 +37,19 @@ describe('Board List Header Component', () => { ...@@ -38,21 +37,19 @@ describe('Board List Header Component', () => {
const boardId = '1'; const boardId = '1';
const listMock = { const listMock = {
...listObj, ...mockLabelList,
list_type: listType, listType,
collapsed, collapsed,
}; };
if (listType === ListType.assignee) { if (listType === ListType.assignee) {
delete listMock.label; delete listMock.label;
listMock.user = {}; listMock.assignee = {};
} }
const list = new List({ ...listMock, doNotFetchIssues: true });
if (withLocalStorage) { if (withLocalStorage) {
localStorage.setItem( localStorage.setItem(
`boards.${boardId}.${list.type}.${list.id}.expanded`, `boards.${boardId}.${listMock.listType}.${listMock.id}.expanded`,
(!collapsed).toString(), (!collapsed).toString(),
); );
} }
...@@ -62,7 +59,7 @@ describe('Board List Header Component', () => { ...@@ -62,7 +59,7 @@ describe('Board List Header Component', () => {
localVue, localVue,
propsData: { propsData: {
disabled: false, disabled: false,
list, list: listMock,
isSwimlanesHeader, isSwimlanesHeader,
}, },
provide: { provide: {
...@@ -112,7 +109,7 @@ describe('Board List Header Component', () => { ...@@ -112,7 +109,7 @@ describe('Board List Header Component', () => {
}); });
it('does not emit event when there is an active List', () => { it('does not emit event when there is an active List', () => {
store.state.activeId = listObj.id; store.state.activeId = mockLabelList.id;
createComponent({ listType: hasSettings[0] }); createComponent({ listType: hasSettings[0] });
wrapper.vm.openSidebarSettings(); wrapper.vm.openSidebarSettings();
......
...@@ -4,7 +4,7 @@ import Vuex from 'vuex'; ...@@ -4,7 +4,7 @@ import Vuex from 'vuex';
import EpicLane from 'ee/boards/components/epic_lane.vue'; import EpicLane from 'ee/boards/components/epic_lane.vue';
import IssuesLaneList from 'ee/boards/components/issues_lane_list.vue'; import IssuesLaneList from 'ee/boards/components/issues_lane_list.vue';
import getters from 'ee/boards/stores/getters'; import getters from 'ee/boards/stores/getters';
import { mockEpic, mockListsWithModel, mockIssuesByListId, issues } from '../mock_data'; import { mockEpic, mockLists, mockIssuesByListId, issues } from '../mock_data';
const localVue = createLocalVue(); const localVue = createLocalVue();
localVue.use(Vuex); localVue.use(Vuex);
...@@ -42,7 +42,7 @@ describe('EpicLane', () => { ...@@ -42,7 +42,7 @@ describe('EpicLane', () => {
const defaultProps = { const defaultProps = {
epic: mockEpic, epic: mockEpic,
lists: mockListsWithModel, lists: mockLists,
disabled: false, disabled: false,
}; };
......
...@@ -7,7 +7,7 @@ import EpicsSwimlanes from 'ee/boards/components/epics_swimlanes.vue'; ...@@ -7,7 +7,7 @@ import EpicsSwimlanes from 'ee/boards/components/epics_swimlanes.vue';
import IssueLaneList from 'ee/boards/components/issues_lane_list.vue'; import IssueLaneList from 'ee/boards/components/issues_lane_list.vue';
import getters from 'ee/boards/stores/getters'; import getters from 'ee/boards/stores/getters';
import BoardListHeader from 'ee_else_ce/boards/components/board_list_header_new.vue'; import BoardListHeader from 'ee_else_ce/boards/components/board_list_header_new.vue';
import { mockListsWithModel, mockEpics, mockIssuesByListId, issues } from '../mock_data'; import { mockLists, mockEpics, mockIssuesByListId, issues } from '../mock_data';
const localVue = createLocalVue(); const localVue = createLocalVue();
localVue.use(Vuex); localVue.use(Vuex);
...@@ -41,7 +41,7 @@ describe('EpicsSwimlanes', () => { ...@@ -41,7 +41,7 @@ describe('EpicsSwimlanes', () => {
const createComponent = (props = {}) => { const createComponent = (props = {}) => {
const store = createStore(); const store = createStore();
const defaultProps = { const defaultProps = {
lists: mockListsWithModel, lists: mockLists,
disabled: false, disabled: false,
}; };
......
import { shallowMount } from '@vue/test-utils'; import { shallowMount } from '@vue/test-utils';
import IssuesLaneList from 'ee/boards/components/issues_lane_list.vue'; import IssuesLaneList from 'ee/boards/components/issues_lane_list.vue';
import { listObj } from 'jest/boards/mock_data'; import { mockList } from 'jest/boards/mock_data';
import BoardCard from '~/boards/components/board_card_layout.vue'; import BoardCard from '~/boards/components/board_card_layout.vue';
import { ListType } from '~/boards/constants'; import { ListType } from '~/boards/constants';
import List from '~/boards/models/list';
import { createStore } from '~/boards/stores'; import { createStore } from '~/boards/stores';
import { mockIssues } from '../mock_data'; import { mockIssues } from '../mock_data';
...@@ -13,8 +12,8 @@ describe('IssuesLaneList', () => { ...@@ -13,8 +12,8 @@ describe('IssuesLaneList', () => {
const createComponent = ({ listType = ListType.backlog, collapsed = false } = {}) => { const createComponent = ({ listType = ListType.backlog, collapsed = false } = {}) => {
const listMock = { const listMock = {
...listObj, ...mockList,
list_type: listType, listType,
collapsed, collapsed,
}; };
...@@ -23,12 +22,10 @@ describe('IssuesLaneList', () => { ...@@ -23,12 +22,10 @@ describe('IssuesLaneList', () => {
listMock.user = {}; listMock.user = {};
} }
const list = new List({ ...listMock, doNotFetchIssues: true });
wrapper = shallowMount(IssuesLaneList, { wrapper = shallowMount(IssuesLaneList, {
store, store,
propsData: { propsData: {
list, list: listMock,
issues: mockIssues, issues: mockIssues,
disabled: false, disabled: false,
canAdminList: true, canAdminList: true,
......
...@@ -10,14 +10,7 @@ import { formatListIssues } from '~/boards/boards_util'; ...@@ -10,14 +10,7 @@ import { formatListIssues } from '~/boards/boards_util';
import * as typesCE from '~/boards/stores/mutation_types'; import * as typesCE from '~/boards/stores/mutation_types';
import * as commonUtils from '~/lib/utils/common_utils'; import * as commonUtils from '~/lib/utils/common_utils';
import { mergeUrlParams, removeParams } from '~/lib/utils/url_utility'; import { mergeUrlParams, removeParams } from '~/lib/utils/url_utility';
import { import { mockLists, mockIssue, mockIssue2, mockEpic, rawIssue } from '../mock_data';
mockLists,
mockIssue,
mockIssue2,
mockEpic,
rawIssue,
mockListsWithModel,
} from '../mock_data';
const expectNotImplemented = action => { const expectNotImplemented = action => {
it('is not implemented', () => { it('is not implemented', () => {
...@@ -605,7 +598,7 @@ describe('moveIssue', () => { ...@@ -605,7 +598,7 @@ describe('moveIssue', () => {
endpoints: { fullPath: 'gitlab-org', boardId: '1' }, endpoints: { fullPath: 'gitlab-org', boardId: '1' },
boardType: 'group', boardType: 'group',
disabled: false, disabled: false,
boardLists: mockListsWithModel, boardLists: mockLists,
issuesByListId: listIssues, issuesByListId: listIssues,
issues, issues,
}; };
......
import mutations from 'ee/boards/stores/mutations'; import mutations from 'ee/boards/stores/mutations';
import { mockIssue, mockIssue2, mockEpics, mockEpic, mockListsWithModel } from '../mock_data'; import { mockIssue, mockIssue2, mockEpics, mockEpic, mockLists } from '../mock_data';
const expectNotImplemented = action => { const expectNotImplemented = action => {
it('is not implemented', () => { it('is not implemented', () => {
...@@ -10,8 +10,8 @@ const expectNotImplemented = action => { ...@@ -10,8 +10,8 @@ const expectNotImplemented = action => {
const epicId = mockEpic.id; const epicId = mockEpic.id;
const initialBoardListsState = { const initialBoardListsState = {
'gid://gitlab/List/1': mockListsWithModel[0], 'gid://gitlab/List/1': mockLists[0],
'gid://gitlab/List/2': mockListsWithModel[1], 'gid://gitlab/List/2': mockLists[1],
}; };
let state = { let state = {
......
...@@ -16504,6 +16504,9 @@ msgstr "" ...@@ -16504,6 +16504,9 @@ msgstr ""
msgid "Loading issues" msgid "Loading issues"
msgstr "" msgstr ""
msgid "Loading more issues"
msgstr ""
msgid "Loading snippet" msgid "Loading snippet"
msgstr "" msgstr ""
......
/* global List */
import Vuex from 'vuex'; import Vuex from 'vuex';
import { useFakeRequestAnimationFrame } from 'helpers/fake_request_animation_frame'; import { useFakeRequestAnimationFrame } from 'helpers/fake_request_animation_frame';
import { createLocalVue, mount } from '@vue/test-utils'; import { createLocalVue, mount } from '@vue/test-utils';
...@@ -7,7 +5,7 @@ import eventHub from '~/boards/eventhub'; ...@@ -7,7 +5,7 @@ import eventHub from '~/boards/eventhub';
import BoardList from '~/boards/components/board_list_new.vue'; import BoardList from '~/boards/components/board_list_new.vue';
import BoardCard from '~/boards/components/board_card.vue'; import BoardCard from '~/boards/components/board_card.vue';
import '~/boards/models/list'; import '~/boards/models/list';
import { listObj, mockIssuesByListId, issues, mockIssues } from './mock_data'; import { mockList, mockIssuesByListId, issues, mockIssues } from './mock_data';
import defaultState from '~/boards/stores/state'; import defaultState from '~/boards/stores/state';
const localVue = createLocalVue(); const localVue = createLocalVue();
...@@ -44,12 +42,10 @@ const createComponent = ({ ...@@ -44,12 +42,10 @@ const createComponent = ({
...state, ...state,
}); });
const list = new List({ const list = {
...listObj, ...mockList,
id: 'gid://gitlab/List/1',
...listProps, ...listProps,
doNotFetchIssues: true, };
});
const issue = { const issue = {
title: 'Testing', title: 'Testing',
id: 1, id: 1,
...@@ -59,8 +55,8 @@ const createComponent = ({ ...@@ -59,8 +55,8 @@ const createComponent = ({
assignees: [], assignees: [],
...listIssueProps, ...listIssueProps,
}; };
if (!Object.prototype.hasOwnProperty.call(listProps, 'issuesSize')) { if (!Object.prototype.hasOwnProperty.call(listProps, 'issuesCount')) {
list.issuesSize = 1; list.issuesCount = 1;
} }
const component = mount(BoardList, { const component = mount(BoardList, {
...@@ -158,7 +154,7 @@ describe('Board list component', () => { ...@@ -158,7 +154,7 @@ describe('Board list component', () => {
it('shows how many more issues to load', async () => { it('shows how many more issues to load', async () => {
wrapper.vm.showCount = true; wrapper.vm.showCount = true;
wrapper.setProps({ list: { issuesSize: 20 } }); wrapper.setProps({ list: { issuesCount: 20 } });
await wrapper.vm.$nextTick(); await wrapper.vm.$nextTick();
expect(wrapper.find('.board-list-count').text()).toBe('Showing 1 of 20 issues'); expect(wrapper.find('.board-list-count').text()).toBe('Showing 1 of 20 issues');
...@@ -168,7 +164,7 @@ describe('Board list component', () => { ...@@ -168,7 +164,7 @@ describe('Board list component', () => {
describe('load more issues', () => { describe('load more issues', () => {
beforeEach(() => { beforeEach(() => {
wrapper = createComponent({ wrapper = createComponent({
listProps: { issuesSize: 25 }, listProps: { issuesCount: 25 },
}); });
}); });
...@@ -179,15 +175,19 @@ describe('Board list component', () => { ...@@ -179,15 +175,19 @@ describe('Board list component', () => {
}); });
it('does not load issues if already loading', () => { it('does not load issues if already loading', () => {
wrapper.vm.listRef.dispatchEvent(new Event('scroll')); wrapper = createComponent({
state: { listsFlags: { 'gid://gitlab/List/1': { isLoadingMore: true } } },
});
wrapper.vm.listRef.dispatchEvent(new Event('scroll')); wrapper.vm.listRef.dispatchEvent(new Event('scroll'));
expect(actions.fetchIssuesForList).toHaveBeenCalledTimes(1); expect(actions.fetchIssuesForList).not.toHaveBeenCalled();
}); });
it('shows loading more spinner', async () => { it('shows loading more spinner', async () => {
wrapper = createComponent({
state: { listsFlags: { 'gid://gitlab/List/1': { isLoadingMore: true } } },
});
wrapper.vm.showCount = true; wrapper.vm.showCount = true;
wrapper.vm.list.loadingMore = true;
await wrapper.vm.$nextTick(); await wrapper.vm.$nextTick();
expect(wrapper.find('.board-list-count .gl-spinner').exists()).toBe(true); expect(wrapper.find('.board-list-count .gl-spinner').exists()).toBe(true);
...@@ -197,13 +197,13 @@ describe('Board list component', () => { ...@@ -197,13 +197,13 @@ describe('Board list component', () => {
describe('max issue count warning', () => { describe('max issue count warning', () => {
beforeEach(() => { beforeEach(() => {
wrapper = createComponent({ wrapper = createComponent({
listProps: { issuesSize: 50 }, listProps: { issuesCount: 50 },
}); });
}); });
describe('when issue count exceeds max issue count', () => { describe('when issue count exceeds max issue count', () => {
it('sets background to bg-danger-100', async () => { it('sets background to bg-danger-100', async () => {
wrapper.setProps({ list: { issuesSize: 4, maxIssueCount: 3 } }); wrapper.setProps({ list: { issuesCount: 4, maxIssueCount: 3 } });
await wrapper.vm.$nextTick(); await wrapper.vm.$nextTick();
expect(wrapper.find('.bg-danger-100').exists()).toBe(true); expect(wrapper.find('.bg-danger-100').exists()).toBe(true);
...@@ -212,7 +212,7 @@ describe('Board list component', () => { ...@@ -212,7 +212,7 @@ describe('Board list component', () => {
describe('when list issue count does NOT exceed list max issue count', () => { describe('when list issue count does NOT exceed list max issue count', () => {
it('does not sets background to bg-danger-100', () => { it('does not sets background to bg-danger-100', () => {
wrapper.setProps({ list: { issuesSize: 2, maxIssueCount: 3 } }); wrapper.setProps({ list: { issuesCount: 2, maxIssueCount: 3 } });
expect(wrapper.find('.bg-danger-100').exists()).toBe(false); expect(wrapper.find('.bg-danger-100').exists()).toBe(false);
}); });
......
...@@ -2,7 +2,6 @@ import { shallowMount } from '@vue/test-utils'; ...@@ -2,7 +2,6 @@ import { shallowMount } from '@vue/test-utils';
import { listObj } from 'jest/boards/mock_data'; import { listObj } from 'jest/boards/mock_data';
import BoardColumn from '~/boards/components/board_column_new.vue'; import BoardColumn from '~/boards/components/board_column_new.vue';
import List from '~/boards/models/list';
import { ListType } from '~/boards/constants'; import { ListType } from '~/boards/constants';
import { createStore } from '~/boards/stores'; import { createStore } from '~/boards/stores';
...@@ -20,24 +19,22 @@ describe('Board Column Component', () => { ...@@ -20,24 +19,22 @@ describe('Board Column Component', () => {
const listMock = { const listMock = {
...listObj, ...listObj,
list_type: listType, listType,
collapsed, collapsed,
}; };
if (listType === ListType.assignee) { if (listType === ListType.assignee) {
delete listMock.label; delete listMock.label;
listMock.user = {}; listMock.assignee = {};
} }
const list = new List({ ...listMock, doNotFetchIssues: true });
store = createStore(); store = createStore();
wrapper = shallowMount(BoardColumn, { wrapper = shallowMount(BoardColumn, {
store, store,
propsData: { propsData: {
disabled: false, disabled: false,
list, list: listMock,
}, },
provide: { provide: {
boardId, boardId,
...@@ -60,7 +57,7 @@ describe('Board Column Component', () => { ...@@ -60,7 +57,7 @@ describe('Board Column Component', () => {
it('has class is-collapsed when list is collapsed', () => { it('has class is-collapsed when list is collapsed', () => {
createComponent({ collapsed: false }); createComponent({ collapsed: false });
expect(wrapper.vm.list.isExpanded).toBe(true); expect(isCollapsed()).toBe(false);
}); });
it('does not have class is-collapsed when list is expanded', () => { it('does not have class is-collapsed when list is expanded', () => {
......
...@@ -5,7 +5,7 @@ import Draggable from 'vuedraggable'; ...@@ -5,7 +5,7 @@ import Draggable from 'vuedraggable';
import EpicsSwimlanes from 'ee_component/boards/components/epics_swimlanes.vue'; import EpicsSwimlanes from 'ee_component/boards/components/epics_swimlanes.vue';
import getters from 'ee_else_ce/boards/stores/getters'; import getters from 'ee_else_ce/boards/stores/getters';
import BoardColumn from '~/boards/components/board_column.vue'; import BoardColumn from '~/boards/components/board_column.vue';
import { mockListsWithModel } from '../mock_data'; import { mockLists, mockListsWithModel } from '../mock_data';
import BoardContent from '~/boards/components/board_content.vue'; import BoardContent from '~/boards/components/board_content.vue';
const localVue = createLocalVue(); const localVue = createLocalVue();
...@@ -20,7 +20,7 @@ describe('BoardContent', () => { ...@@ -20,7 +20,7 @@ describe('BoardContent', () => {
const defaultState = { const defaultState = {
isShowingEpicsSwimlanes: false, isShowingEpicsSwimlanes: false,
boardLists: mockListsWithModel, boardLists: mockLists,
error: undefined, error: undefined,
}; };
...@@ -59,7 +59,7 @@ describe('BoardContent', () => { ...@@ -59,7 +59,7 @@ describe('BoardContent', () => {
it('renders a BoardColumn component per list', () => { it('renders a BoardColumn component per list', () => {
createComponent(); createComponent();
expect(wrapper.findAll(BoardColumn)).toHaveLength(mockListsWithModel.length); expect(wrapper.findAll(BoardColumn)).toHaveLength(mockLists.length);
}); });
it('does not display EpicsSwimlanes component', () => { it('does not display EpicsSwimlanes component', () => {
......
import Vuex from 'vuex'; import Vuex from 'vuex';
import { shallowMount, createLocalVue } from '@vue/test-utils'; import { shallowMount, createLocalVue } from '@vue/test-utils';
import { listObj } from 'jest/boards/mock_data'; import { mockLabelList } from 'jest/boards/mock_data';
import BoardListHeader from '~/boards/components/board_list_header_new.vue'; import BoardListHeader from '~/boards/components/board_list_header_new.vue';
import List from '~/boards/models/list';
import { ListType } from '~/boards/constants'; import { ListType } from '~/boards/constants';
const localVue = createLocalVue(); const localVue = createLocalVue();
...@@ -32,21 +31,19 @@ describe('Board List Header Component', () => { ...@@ -32,21 +31,19 @@ describe('Board List Header Component', () => {
const boardId = '1'; const boardId = '1';
const listMock = { const listMock = {
...listObj, ...mockLabelList,
list_type: listType, listType,
collapsed, collapsed,
}; };
if (listType === ListType.assignee) { if (listType === ListType.assignee) {
delete listMock.label; delete listMock.label;
listMock.user = {}; listMock.assignee = {};
} }
const list = new List({ ...listMock, doNotFetchIssues: true });
if (withLocalStorage) { if (withLocalStorage) {
localStorage.setItem( localStorage.setItem(
`boards.${boardId}.${list.type}.${list.id}.expanded`, `boards.${boardId}.${listMock.listType}.${listMock.id}.expanded`,
(!collapsed).toString(), (!collapsed).toString(),
); );
} }
...@@ -62,7 +59,7 @@ describe('Board List Header Component', () => { ...@@ -62,7 +59,7 @@ describe('Board List Header Component', () => {
localVue, localVue,
propsData: { propsData: {
disabled: false, disabled: false,
list, list: listMock,
}, },
provide: { provide: {
boardId, boardId,
...@@ -72,10 +69,11 @@ describe('Board List Header Component', () => { ...@@ -72,10 +69,11 @@ describe('Board List Header Component', () => {
}); });
}; };
const isExpanded = () => wrapper.vm.list.isExpanded; const isCollapsed = () => wrapper.vm.list.collapsed;
const isCollapsed = () => !isExpanded(); const isExpanded = () => !isCollapsed;
const findAddIssueButton = () => wrapper.find({ ref: 'newIssueBtn' }); const findAddIssueButton = () => wrapper.find({ ref: 'newIssueBtn' });
const findTitle = () => wrapper.find('.board-title');
const findCaret = () => wrapper.find('.board-title-caret'); const findCaret = () => wrapper.find('.board-title-caret');
describe('Add issue button', () => { describe('Add issue button', () => {
...@@ -125,7 +123,7 @@ describe('Board List Header Component', () => { ...@@ -125,7 +123,7 @@ describe('Board List Header Component', () => {
it('collapses expanded Column when clicking the collapse icon', async () => { it('collapses expanded Column when clicking the collapse icon', async () => {
createComponent(); createComponent();
expect(isExpanded()).toBe(true); expect(isCollapsed()).toBe(false);
findCaret().vm.$emit('click'); findCaret().vm.$emit('click');
...@@ -166,4 +164,24 @@ describe('Board List Header Component', () => { ...@@ -166,4 +164,24 @@ describe('Board List Header Component', () => {
expect(localStorage.getItem(`${wrapper.vm.uniqueKey}.expanded`)).toBe(String(isExpanded())); expect(localStorage.getItem(`${wrapper.vm.uniqueKey}.expanded`)).toBe(String(isExpanded()));
}); });
}); });
describe('user can drag', () => {
const cannotDragList = [ListType.backlog, ListType.closed];
const canDragList = [ListType.label, ListType.milestone, ListType.assignee];
it.each(cannotDragList)(
'does not have user-can-drag-class so user cannot drag list',
listType => {
createComponent({ listType });
expect(findTitle().classes()).not.toContain('user-can-drag');
},
);
it.each(canDragList)('has user-can-drag-class so user can drag list', listType => {
createComponent({ listType });
expect(findTitle().classes()).toContain('user-can-drag');
});
});
}); });
...@@ -3,7 +3,7 @@ import { shallowMount, createLocalVue } from '@vue/test-utils'; ...@@ -3,7 +3,7 @@ import { shallowMount, createLocalVue } from '@vue/test-utils';
import BoardNewIssue from '~/boards/components/board_new_issue_new.vue'; import BoardNewIssue from '~/boards/components/board_new_issue_new.vue';
import '~/boards/models/list'; import '~/boards/models/list';
import { mockListsWithModel } from '../mock_data'; import { mockList } from '../mock_data';
const localVue = createLocalVue(); const localVue = createLocalVue();
...@@ -37,7 +37,7 @@ describe('Issue boards new issue form', () => { ...@@ -37,7 +37,7 @@ describe('Issue boards new issue form', () => {
wrapper = shallowMount(BoardNewIssue, { wrapper = shallowMount(BoardNewIssue, {
propsData: { propsData: {
disabled: false, disabled: false,
list: mockListsWithModel[0], list: mockList,
}, },
store, store,
localVue, localVue,
......
...@@ -97,7 +97,7 @@ export const mockMilestone = { ...@@ -97,7 +97,7 @@ export const mockMilestone = {
due_date: '2019-12-31', due_date: '2019-12-31',
}; };
const assignees = [ export const assignees = [
{ {
id: 'gid://gitlab/User/2', id: 'gid://gitlab/User/2',
username: 'angelina.herman', username: 'angelina.herman',
...@@ -282,38 +282,39 @@ export const setMockEndpoints = (opts = {}) => { ...@@ -282,38 +282,39 @@ export const setMockEndpoints = (opts = {}) => {
}); });
}; };
export const mockLists = [ export const mockList = {
{ id: 'gid://gitlab/List/1',
id: 'gid://gitlab/List/1', title: 'Backlog',
title: 'Backlog', position: null,
position: null, listType: 'backlog',
listType: 'backlog', collapsed: false,
collapsed: false, label: null,
label: null, assignee: null,
assignee: null, milestone: null,
milestone: null, loading: false,
loading: false, issuesCount: 1,
issuesSize: 1, };
},
{ export const mockLabelList = {
id: 'gid://gitlab/List/2', id: 'gid://gitlab/List/2',
title: 'To Do',
position: 0,
listType: 'label',
collapsed: false,
label: {
id: 'gid://gitlab/GroupLabel/121',
title: 'To Do', title: 'To Do',
position: 0, color: '#F0AD4E',
listType: 'label', textColor: '#FFFFFF',
collapsed: false, description: null,
label: {
id: 'gid://gitlab/GroupLabel/121',
title: 'To Do',
color: '#F0AD4E',
textColor: '#FFFFFF',
description: null,
},
assignee: null,
milestone: null,
loading: false,
issuesSize: 0,
}, },
]; assignee: null,
milestone: null,
loading: false,
issuesCount: 0,
};
export const mockLists = [mockList, mockLabelList];
export const mockListsById = keyBy(mockLists, 'id'); export const mockListsById = keyBy(mockLists, 'id');
......
import testAction from 'helpers/vuex_action_helper'; import testAction from 'helpers/vuex_action_helper';
import { import {
mockListsWithModel,
mockLists, mockLists,
mockListsById, mockListsById,
mockIssue, mockIssue,
...@@ -229,8 +228,8 @@ describe('createList', () => { ...@@ -229,8 +228,8 @@ describe('createList', () => {
describe('moveList', () => { describe('moveList', () => {
it('should commit MOVE_LIST mutation and dispatch updateList action', done => { it('should commit MOVE_LIST mutation and dispatch updateList action', done => {
const initialBoardListsState = { const initialBoardListsState = {
'gid://gitlab/List/1': mockListsWithModel[0], 'gid://gitlab/List/1': mockLists[0],
'gid://gitlab/List/2': mockListsWithModel[1], 'gid://gitlab/List/2': mockLists[1],
}; };
const state = { const state = {
...@@ -252,7 +251,7 @@ describe('moveList', () => { ...@@ -252,7 +251,7 @@ describe('moveList', () => {
[ [
{ {
type: types.MOVE_LIST, type: types.MOVE_LIST,
payload: { movedList: mockListsWithModel[0], listAtNewIndex: mockListsWithModel[1] }, payload: { movedList: mockLists[0], listAtNewIndex: mockLists[1] },
}, },
], ],
[ [
...@@ -271,8 +270,8 @@ describe('moveList', () => { ...@@ -271,8 +270,8 @@ describe('moveList', () => {
it('should not commit MOVE_LIST or dispatch updateList if listId and replacedListId are the same', () => { it('should not commit MOVE_LIST or dispatch updateList if listId and replacedListId are the same', () => {
const initialBoardListsState = { const initialBoardListsState = {
'gid://gitlab/List/1': mockListsWithModel[0], 'gid://gitlab/List/1': mockLists[0],
'gid://gitlab/List/2': mockListsWithModel[1], 'gid://gitlab/List/2': mockLists[1],
}; };
const state = { const state = {
...@@ -512,7 +511,7 @@ describe('moveIssue', () => { ...@@ -512,7 +511,7 @@ describe('moveIssue', () => {
endpoints: { fullPath: 'gitlab-org', boardId: '1' }, endpoints: { fullPath: 'gitlab-org', boardId: '1' },
boardType: 'group', boardType: 'group',
disabled: false, disabled: false,
boardLists: mockListsWithModel, boardLists: mockLists,
issuesByListId: listIssues, issuesByListId: listIssues,
issues, issues,
}; };
......
...@@ -6,7 +6,7 @@ import { ...@@ -6,7 +6,7 @@ import {
mockIssues, mockIssues,
mockIssuesByListId, mockIssuesByListId,
issues, issues,
mockListsWithModel, mockLists,
} from '../mock_data'; } from '../mock_data';
describe('Boards - Getters', () => { describe('Boards - Getters', () => {
...@@ -94,22 +94,22 @@ describe('Boards - Getters', () => { ...@@ -94,22 +94,22 @@ describe('Boards - Getters', () => {
const boardsState = { const boardsState = {
boardLists: { boardLists: {
'gid://gitlab/List/1': mockListsWithModel[0], 'gid://gitlab/List/1': mockLists[0],
'gid://gitlab/List/2': mockListsWithModel[1], 'gid://gitlab/List/2': mockLists[1],
}, },
}; };
describe('getListByLabelId', () => { describe('getListByLabelId', () => {
it('returns list for a given label id', () => { it('returns list for a given label id', () => {
expect(getters.getListByLabelId(boardsState)('gid://gitlab/GroupLabel/121')).toEqual( expect(getters.getListByLabelId(boardsState)('gid://gitlab/GroupLabel/121')).toEqual(
mockListsWithModel[1], mockLists[1],
); );
}); });
}); });
describe('getListByTitle', () => { describe('getListByTitle', () => {
it('returns list for a given list title', () => { it('returns list for a given list title', () => {
expect(getters.getListByTitle(boardsState)('To Do')).toEqual(mockListsWithModel[1]); expect(getters.getListByTitle(boardsState)('To Do')).toEqual(mockLists[1]);
}); });
}); });
}); });
import mutations from '~/boards/stores/mutations'; import mutations from '~/boards/stores/mutations';
import * as types from '~/boards/stores/mutation_types'; import * as types from '~/boards/stores/mutation_types';
import defaultState from '~/boards/stores/state'; import defaultState from '~/boards/stores/state';
import { mockListsWithModel, mockLists, rawIssue, mockIssue, mockIssue2 } from '../mock_data'; import { mockLists, rawIssue, mockIssue, mockIssue2 } from '../mock_data';
const expectNotImplemented = action => { const expectNotImplemented = action => {
it('is not implemented', () => { it('is not implemented', () => {
...@@ -13,8 +13,8 @@ describe('Board Store Mutations', () => { ...@@ -13,8 +13,8 @@ describe('Board Store Mutations', () => {
let state; let state;
const initialBoardListsState = { const initialBoardListsState = {
'gid://gitlab/List/1': mockListsWithModel[0], 'gid://gitlab/List/1': mockLists[0],
'gid://gitlab/List/2': mockListsWithModel[1], 'gid://gitlab/List/2': mockLists[1],
}; };
beforeEach(() => { beforeEach(() => {
...@@ -124,10 +124,10 @@ describe('Board Store Mutations', () => { ...@@ -124,10 +124,10 @@ describe('Board Store Mutations', () => {
describe('RECEIVE_ADD_LIST_SUCCESS', () => { describe('RECEIVE_ADD_LIST_SUCCESS', () => {
it('adds list to boardLists state', () => { it('adds list to boardLists state', () => {
mutations.RECEIVE_ADD_LIST_SUCCESS(state, mockListsWithModel[0]); mutations.RECEIVE_ADD_LIST_SUCCESS(state, mockLists[0]);
expect(state.boardLists).toEqual({ expect(state.boardLists).toEqual({
[mockListsWithModel[0].id]: mockListsWithModel[0], [mockLists[0].id]: mockLists[0],
}); });
}); });
}); });
...@@ -144,13 +144,13 @@ describe('Board Store Mutations', () => { ...@@ -144,13 +144,13 @@ describe('Board Store Mutations', () => {
}; };
mutations.MOVE_LIST(state, { mutations.MOVE_LIST(state, {
movedList: mockListsWithModel[0], movedList: mockLists[0],
listAtNewIndex: mockListsWithModel[1], listAtNewIndex: mockLists[1],
}); });
expect(state.boardLists).toEqual({ expect(state.boardLists).toEqual({
'gid://gitlab/List/2': mockListsWithModel[1], 'gid://gitlab/List/2': mockLists[1],
'gid://gitlab/List/1': mockListsWithModel[0], 'gid://gitlab/List/1': mockLists[0],
}); });
}); });
}); });
...@@ -160,8 +160,8 @@ describe('Board Store Mutations', () => { ...@@ -160,8 +160,8 @@ describe('Board Store Mutations', () => {
state = { state = {
...state, ...state,
boardLists: { boardLists: {
'gid://gitlab/List/2': mockListsWithModel[1], 'gid://gitlab/List/2': mockLists[1],
'gid://gitlab/List/1': mockListsWithModel[0], 'gid://gitlab/List/1': mockLists[0],
}, },
error: undefined, error: undefined,
}; };
...@@ -175,7 +175,7 @@ describe('Board Store Mutations', () => { ...@@ -175,7 +175,7 @@ describe('Board Store Mutations', () => {
describe('REMOVE_LIST', () => { describe('REMOVE_LIST', () => {
it('removes list from boardLists', () => { it('removes list from boardLists', () => {
const [list, secondList] = mockListsWithModel; const [list, secondList] = mockLists;
const expected = { const expected = {
[secondList.id]: secondList, [secondList.id]: secondList,
}; };
...@@ -455,13 +455,13 @@ describe('Board Store Mutations', () => { ...@@ -455,13 +455,13 @@ describe('Board Store Mutations', () => {
boardLists: initialBoardListsState, boardLists: initialBoardListsState,
}; };
expect(state.boardLists['gid://gitlab/List/1'].issuesSize).toBe(1); expect(state.boardLists['gid://gitlab/List/1'].issuesCount).toBe(1);
mutations.ADD_ISSUE_TO_LIST(state, { list: mockListsWithModel[0], issue: mockIssue2 }); mutations.ADD_ISSUE_TO_LIST(state, { list: mockLists[0], issue: mockIssue2 });
expect(state.issuesByListId['gid://gitlab/List/1']).toContain(mockIssue2.id); expect(state.issuesByListId['gid://gitlab/List/1']).toContain(mockIssue2.id);
expect(state.issues[mockIssue2.id]).toEqual(mockIssue2); expect(state.issues[mockIssue2.id]).toEqual(mockIssue2);
expect(state.boardLists['gid://gitlab/List/1'].issuesSize).toBe(2); expect(state.boardLists['gid://gitlab/List/1'].issuesCount).toBe(2);
}); });
}); });
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment