diff options
author | GitLab Bot <gitlab-bot@gitlab.com> | 2020-12-15 00:09:47 +0300 |
---|---|---|
committer | GitLab Bot <gitlab-bot@gitlab.com> | 2020-12-15 00:09:47 +0300 |
commit | 3a51d1d11d8282ec011f1a79fa10b1ce370e9933 (patch) | |
tree | 2730c6078b7641d9d25eb64df53ac0e44bd1f3c5 /app/assets/javascripts/boards | |
parent | 319ac09313e73485b47b8da7a67fb27e74f05721 (diff) |
Add latest changes from gitlab-org/gitlab@master
Diffstat (limited to 'app/assets/javascripts/boards')
10 files changed, 118 insertions, 90 deletions
diff --git a/app/assets/javascripts/boards/boards_util.js b/app/assets/javascripts/boards/boards_util.js index 4961017b6af..634313f666b 100644 --- a/app/assets/javascripts/boards/boards_util.js +++ b/app/assets/javascripts/boards/boards_util.js @@ -2,20 +2,28 @@ import { sortBy } from 'lodash'; import axios from '~/lib/utils/axios_utils'; import { ListType } from './constants'; import { getIdFromGraphQLId } from '~/graphql_shared/utils'; -import boardsStore from '~/boards/stores/boards_store'; export function getMilestone() { 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) { - const formattedLists = lists.nodes.map(list => - boardsStore.updateListPosition({ ...list, doNotFetchIssues: true }), - ); - return formattedLists.reduce((map, list) => { + return lists.nodes.reduce((map, list) => { return { ...map, - [list.id]: list, + [list.id]: updateListPosition(list), }; }, {}); } @@ -85,22 +93,22 @@ export function fullLabelId(label) { export function moveIssueListHelper(issue, fromList, toList) { const updatedIssue = issue; if ( - toList.type === ListType.label && + toList.listType === ListType.label && !updatedIssue.labels.find(label => label.id === toList.label.id) ) { 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); } if ( - toList.type === ListType.assignee && + toList.listType === ListType.assignee && !updatedIssue.assignees.find(assignee => assignee.id === toList.assignee.id) ) { updatedIssue.assignees.push(toList.assignee); } - if (fromList?.assignee && fromList.type === ListType.assignee) { + if (fromList?.assignee && fromList.listType === ListType.assignee) { updatedIssue.assignees = updatedIssue.assignees.filter( assignee => assignee.id !== fromList.assignee.id, ); @@ -118,6 +126,10 @@ export function getBoardsPath(endpoint, board) { return axios.post(path, { board }); } +export function isListDraggable(list) { + return list.listType !== ListType.backlog && list.listType !== ListType.closed; +} + export default { getMilestone, formatIssue, @@ -125,4 +137,5 @@ export default { fullBoardId, fullLabelId, getBoardsPath, + isListDraggable, }; diff --git a/app/assets/javascripts/boards/components/board_column_new.vue b/app/assets/javascripts/boards/components/board_column_new.vue index 9ae9d4697d9..7839f45c48b 100644 --- a/app/assets/javascripts/boards/components/board_column_new.vue +++ b/app/assets/javascripts/boards/components/board_column_new.vue @@ -2,6 +2,7 @@ import { mapGetters, mapActions, mapState } from 'vuex'; import BoardListHeader from 'ee_else_ce/boards/components/board_list_header_new.vue'; import BoardList from './board_list_new.vue'; +import { isListDraggable } from '../boards_util'; export default { components: { @@ -35,6 +36,9 @@ export default { listIssues() { return this.getIssuesByList(this.list.id); }, + isListDraggable() { + return isListDraggable(this.list); + }, }, watch: { filterParams: { @@ -47,7 +51,6 @@ export default { }, methods: { ...mapActions(['fetchIssuesForList']), - // TODO: Reordering of lists https://gitlab.com/gitlab-org/gitlab/-/issues/280515 }, }; </script> @@ -55,13 +58,12 @@ export default { <template> <div :class="{ - 'is-draggable': !list.preset, - 'is-expandable': list.isExpandable, - 'is-collapsed': !list.isExpanded, - 'board-type-assignee': list.type === 'assignee', + 'is-draggable': isListDraggable, + 'is-collapsed': list.collapsed, + 'board-type-assignee': list.listType === 'assignee', }" :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" > <div diff --git a/app/assets/javascripts/boards/components/board_content.vue b/app/assets/javascripts/boards/components/board_content.vue index fb8f3014869..b366aa6fdb3 100644 --- a/app/assets/javascripts/boards/components/board_content.vue +++ b/app/assets/javascripts/boards/components/board_content.vue @@ -103,9 +103,6 @@ export default { :key="list.id" ref="board" :can-admin-list="canAdminList" - :class="{ - 'is-draggable': !list.preset, - }" :list="list" :disabled="disabled" /> diff --git a/app/assets/javascripts/boards/components/board_list_header_new.vue b/app/assets/javascripts/boards/components/board_list_header_new.vue index b7c892e3b52..44eb2aa34c2 100644 --- a/app/assets/javascripts/boards/components/board_list_header_new.vue +++ b/app/assets/javascripts/boards/components/board_list_header_new.vue @@ -9,15 +9,22 @@ import { GlSprintf, GlTooltipDirective, } from '@gitlab/ui'; -import { n__, s__ } from '~/locale'; +import { n__, s__, __ } from '~/locale'; import AccessorUtilities from '../../lib/utils/accessor'; import IssueCount from './issue_count.vue'; import eventHub from '../eventhub'; import sidebarEventHub from '~/sidebar/event_hub'; import { inactiveId, LIST, ListType } from '../constants'; import { isScopedLabel } from '~/lib/utils/common_utils'; +import { isListDraggable } from '~/boards/boards_util'; export default { + i18n: { + newIssue: __('New issue'), + listSettings: __('List settings'), + expand: s__('Boards|Expand'), + collapse: s__('Boards|Collapse'), + }, components: { GlButtonGroup, GlButton, @@ -66,47 +73,47 @@ export default { return Boolean(this.currentUserId); }, listType() { - return this.list.type; + return this.list.listType; }, listAssignee() { return this.list?.assignee?.username || ''; }, listTitle() { - return this.list?.label?.description || this.list.title || ''; + return this.list?.label?.description || this.list?.assignee?.name || this.list.title || ''; }, showListHeaderButton() { return !this.disabled && this.listType !== ListType.closed; }, showMilestoneListDetails() { return ( - this.list.type === ListType.milestone && + this.listType === ListType.milestone && this.list.milestone && - (this.list.isExpanded || !this.isSwimlanesHeader) + (!this.list.collapsed || !this.isSwimlanesHeader) ); }, showAssigneeListDetails() { return ( - this.list.type === ListType.assignee && (this.list.isExpanded || !this.isSwimlanesHeader) + this.listType === ListType.assignee && (!this.list.collapsed || !this.isSwimlanesHeader) ); }, issuesCount() { - return this.list.issuesSize; + return this.list.issuesCount; }, issuesTooltipLabel() { return n__(`%d issue`, `%d issues`, this.issuesCount); }, chevronTooltip() { - return this.list.isExpanded ? s__('Boards|Collapse') : s__('Boards|Expand'); + return this.list.collapsed ? this.$options.i18n.expand : this.$options.i18n.collapse; }, chevronIcon() { - return this.list.isExpanded ? 'chevron-right' : 'chevron-down'; + return this.list.collapsed ? 'chevron-down' : 'chevron-right'; }, isNewIssueShown() { return this.listType === ListType.backlog || this.showListHeaderButton; }, isSettingsShown() { return ( - this.listType !== ListType.backlog && this.showListHeaderButton && this.list.isExpanded + this.listType !== ListType.backlog && this.showListHeaderButton && !this.list.collapsed ); }, uniqueKey() { @@ -119,6 +126,9 @@ export default { headerStyle() { return { borderTopColor: this.list?.label?.color }; }, + userCanDrag() { + return !this.disabled && isListDraggable(this.list); + }, }, methods: { ...mapActions(['updateList', 'setActiveId']), @@ -137,7 +147,7 @@ export default { eventHub.$emit(`toggle-issue-form-${this.list.id}`); }, toggleExpanded() { - this.list.isExpanded = !this.list.isExpanded; + this.list.collapsed = !this.list.collapsed; if (!this.isLoggedIn) { this.addToLocalStorage(); @@ -151,11 +161,11 @@ export default { }, addToLocalStorage() { if (AccessorUtilities.isLocalStorageAccessSafe()) { - localStorage.setItem(`${this.uniqueKey}.expanded`, this.list.isExpanded); + localStorage.setItem(`${this.uniqueKey}.expanded`, !this.list.collapsed); } }, 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 { <header :class="{ '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, }" :style="headerStyle" @@ -175,16 +185,15 @@ export default { > <h3 :class="{ - 'user-can-drag': !disabled && !list.preset, - 'gl-py-3 gl-h-full': !list.isExpanded && !isSwimlanesHeader, - 'gl-border-b-0': !list.isExpanded || isSwimlanesHeader, - 'gl-py-2': !list.isExpanded && isSwimlanesHeader, - 'gl-flex-direction-column': !list.isExpanded, + 'user-can-drag': userCanDrag, + 'gl-py-3 gl-h-full': list.collapsed && !isSwimlanesHeader, + 'gl-border-b-0': list.collapsed || isSwimlanesHeader, + 'gl-py-2': list.collapsed && isSwimlanesHeader, + '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" > <gl-button - v-if="list.isExpandable" v-gl-tooltip.hover :aria-label="chevronTooltip" :title="chevronTooltip" @@ -200,8 +209,8 @@ export default { aria-hidden="true" class="milestone-icon" :class="{ - 'gl-mt-3 gl-rotate-90': !list.isExpanded, - 'gl-mr-2': list.isExpanded, + 'gl-mt-3 gl-rotate-90': list.collapsed, + 'gl-mr-2': !list.collapsed, }" > <gl-icon name="timer" /> @@ -209,17 +218,17 @@ export default { <a v-if="showAssigneeListDetails" - :href="list.assignee.path" + :href="list.assignee.webUrl" class="user-avatar-link js-no-trigger" :class="{ - 'gl-mt-3 gl-rotate-90': !list.isExpanded, + 'gl-mt-3 gl-rotate-90': list.collapsed, }" > <img v-gl-tooltip.hover.bottom :title="listAssignee" :alt="list.assignee.name" - :src="list.assignee.avatar" + :src="list.assignee.avatarUrl" class="avatar s20" height="20" width="20" @@ -229,9 +238,9 @@ export default { <div class="board-title-text" :class="{ - 'gl-display-none': !list.isExpanded && isSwimlanesHeader, - 'gl-flex-grow-0 gl-my-3 gl-mx-0': !list.isExpanded, - 'gl-flex-grow-1': list.isExpanded, + 'gl-display-none': list.collapsed && isSwimlanesHeader, + 'gl-flex-grow-0 gl-my-3 gl-mx-0': list.collapsed, + 'gl-flex-grow-1': !list.collapsed, }" > <!-- EE start --> @@ -239,16 +248,16 @@ export default { v-if="listType !== 'label'" v-gl-tooltip.hover :class="{ - 'gl-display-block': !list.isExpanded || listType === 'milestone', + 'gl-display-block': list.collapsed || listType === 'milestone', }" :title="listTitle" class="board-title-main-text gl-text-truncate" > - {{ list.title }} + {{ listTitle }} </span> <span v-if="listType === 'assignee'" - v-show="list.isExpanded" + v-show="!list.collapsed" class="gl-ml-2 gl-font-weight-normal gl-text-gray-500" > @{{ listAssignee }} @@ -260,21 +269,21 @@ export default { :background-color="list.label.color" :description="list.label.description" :scoped="showScopedLabels(list.label)" - :size="!list.isExpanded ? 'sm' : ''" + :size="list.collapsed ? 'sm' : ''" :title="list.label.title" /> </div> <!-- EE start --> <span - v-if="isSwimlanesHeader && !list.isExpanded" + v-if="isSwimlanesHeader && list.collapsed" ref="collapsedInfo" aria-hidden="true" class="board-header-collapsed-info-icon gl-cursor-pointer gl-text-gray-500" > <gl-icon name="information" /> </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 v-if="list.maxIssueCount !== 0"> • @@ -296,8 +305,8 @@ export default { <div class="issue-count-badge gl-display-inline-flex gl-pr-0 no-drag gl-text-gray-500" :class="{ - 'gl-display-none!': !list.isExpanded && isSwimlanesHeader, - 'gl-p-0': !list.isExpanded, + 'gl-display-none!': list.collapsed && isSwimlanesHeader, + 'gl-p-0': list.collapsed, }" > <span class="gl-display-inline-flex"> @@ -323,11 +332,11 @@ export default { > <gl-button v-if="isNewIssueShown" - v-show="list.isExpanded" + v-show="!list.collapsed" ref="newIssueBtn" v-gl-tooltip.hover - :aria-label="__('New issue')" - :title="__('New issue')" + :aria-label="$options.i18n.newIssue" + :title="$options.i18n.newIssue" class="issue-count-badge-add-button no-drag" icon="plus" @click="showNewIssueForm" @@ -337,13 +346,13 @@ export default { v-if="isSettingsShown" ref="settingsBtn" v-gl-tooltip.hover - :aria-label="__('List settings')" + :aria-label="$options.i18n.listSettings" class="no-drag js-board-settings-button" - :title="__('List settings')" + :title="$options.i18n.listSettings" icon="settings" @click="openSidebarSettings" /> - <gl-tooltip :target="() => $refs.settingsBtn">{{ __('List settings') }}</gl-tooltip> + <gl-tooltip :target="() => $refs.settingsBtn">{{ $options.i18n.listSettings }}</gl-tooltip> </gl-button-group> </h3> </header> diff --git a/app/assets/javascripts/boards/components/board_list_new.vue b/app/assets/javascripts/boards/components/board_list_new.vue index 291abc9849b..ada80e6913c 100644 --- a/app/assets/javascripts/boards/components/board_list_new.vue +++ b/app/assets/javascripts/boards/components/board_list_new.vue @@ -12,6 +12,11 @@ import { sprintf, __ } from '~/locale'; export default { name: 'BoardList', + i18n: { + loadingIssues: __('Loading issues'), + loadingMoreissues: __('Loading more issues'), + showingAllIssues: __('Showing all issues'), + }, components: { BoardCard, BoardNewIssue, @@ -49,11 +54,11 @@ export default { paginatedIssueText() { return sprintf(__('Showing %{pageSize} of %{total} issues'), { pageSize: this.issues.length, - total: this.list.issuesSize, + total: this.list.issuesCount, }); }, issuesSizeExceedsMax() { - return this.list.maxIssueCount > 0 && this.list.issuesSize > this.list.maxIssueCount; + return this.list.maxIssueCount > 0 && this.list.issuesCount > this.list.maxIssueCount; }, hasNextPage() { return this.pageInfoByListId[this.list.id].hasNextPage; @@ -61,10 +66,16 @@ export default { loading() { return this.listsFlags[this.list.id]?.isLoading; }, + loadingMore() { + return this.listsFlags[this.list.id]?.isLoadingMore; + }, listRef() { // When list is draggable, the reference to the list needs to be accessed differently return this.canAdminList ? this.$refs.list.$el : this.$refs.list; }, + showingAllIssues() { + return this.issues.length === this.list.issuesCount; + }, treeRootWrapper() { return this.canAdminList ? Draggable : 'ul'; }, @@ -72,7 +83,7 @@ export default { const options = { ...defaultSortableConfig, fallbackOnBody: false, - group: 'boards-list', + group: 'board-list', tag: 'ul', 'ghost-class': 'board-card-drag-active', 'data-list-id': this.list.id, @@ -85,7 +96,6 @@ export default { watch: { filters: { handler() { - this.list.loadingMore = false; this.listRef.scrollTop = 0; }, deep: true, @@ -124,13 +134,7 @@ export default { this.listRef.scrollTop = 0; }, loadNextPage() { - const loadingDone = () => { - this.list.loadingMore = false; - }; - this.list.loadingMore = true; - this.fetchIssuesForList({ listId: this.list.id, fetchNext: true }) - .then(loadingDone) - .catch(loadingDone); + this.fetchIssuesForList({ listId: this.list.id, fetchNext: true }); }, toggleForm() { this.showIssueForm = !this.showIssueForm; @@ -138,7 +142,7 @@ export default { onScroll() { window.requestAnimationFrame(() => { if ( - !this.list.loadingMore && + !this.loadingMore && this.scrollTop() > this.scrollHeight() - this.scrollOffset && this.hasNextPage ) { @@ -198,26 +202,26 @@ export default { <template> <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" data-qa-selector="board_list_cards_area" > <div v-if="loading" class="gl-mt-4 gl-text-center" - :aria-label="__('Loading issues')" + :aria-label="$options.i18n.loadingIssues" data-testid="board_list_loading" > <gl-loading-icon /> </div> - <board-new-issue v-if="list.type !== 'closed' && showIssueForm" :list="list" /> + <board-new-issue v-if="list.listType !== 'closed' && showIssueForm" :list="list" /> <component :is="treeRootWrapper" v-show="!loading" ref="list" v-bind="treeRootOptions" :data-board="list.id" - :data-board-type="list.type" + :data-board-type="list.listType" :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" data-testid="tree-root-wrapper" @@ -234,8 +238,8 @@ export default { :disabled="disabled" /> <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" /> - <span v-if="issues.length === list.issuesSize">{{ __('Showing all issues') }}</span> + <gl-loading-icon v-if="loadingMore" :label="$options.i18n.loadingMoreissues" /> + <span v-if="showingAllIssues">{{ $options.i18n.showingAllIssues }}</span> <span v-else>{{ paginatedIssueText }}</span> </li> </component> diff --git a/app/assets/javascripts/boards/components/board_settings_sidebar.vue b/app/assets/javascripts/boards/components/board_settings_sidebar.vue index 80070b25bd0..60db8fefe82 100644 --- a/app/assets/javascripts/boards/components/board_settings_sidebar.vue +++ b/app/assets/javascripts/boards/components/board_settings_sidebar.vue @@ -53,7 +53,7 @@ export default { return this.activeList.label; }, boardListType() { - return this.activeList.type || null; + return this.activeList.type || this.activeList.listType || null; }, listTypeTitle() { return this.$options.labelListText; diff --git a/app/assets/javascripts/boards/components/issue_card_inner.vue b/app/assets/javascripts/boards/components/issue_card_inner.vue index 67e8a32dbe2..ddd20ff281c 100644 --- a/app/assets/javascripts/boards/components/issue_card_inner.vue +++ b/app/assets/javascripts/boards/components/issue_card_inner.vue @@ -10,6 +10,7 @@ import IssueDueDate from './issue_due_date.vue'; import IssueTimeEstimate from './issue_time_estimate.vue'; import boardsStore from '../stores/boards_store'; import { isScopedLabel } from '~/lib/utils/common_utils'; +import { ListType } from '../constants'; export default { components: { @@ -122,7 +123,13 @@ export default { return true; }, 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) { if (!this.updateFilters) return; diff --git a/app/assets/javascripts/boards/components/project_select.vue b/app/assets/javascripts/boards/components/project_select.vue index f90fe582566..9c90938fc52 100644 --- a/app/assets/javascripts/boards/components/project_select.vue +++ b/app/assets/javascripts/boards/components/project_select.vue @@ -7,6 +7,7 @@ import eventHub from '../eventhub'; import Api from '../../api'; import { featureAccessLevel } from '~/pages/projects/shared/permissions/constants'; import initDeprecatedJQueryDropdown from '~/deprecated_jquery_dropdown'; +import { ListType } from '../constants'; export default { name: 'BoardProjectSelect', @@ -53,7 +54,7 @@ export default { this.loading = true; 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; } diff --git a/app/assets/javascripts/boards/stores/actions.js b/app/assets/javascripts/boards/stores/actions.js index 1a745eef482..b4c646b0223 100644 --- a/app/assets/javascripts/boards/stores/actions.js +++ b/app/assets/javascripts/boards/stores/actions.js @@ -12,7 +12,6 @@ import { formatListsPageInfo, formatIssue, } from '../boards_util'; -import boardStore from '~/boards/stores/boards_store'; import createFlash from '~/flash'; import { __ } from '~/locale'; import updateAssigneesMutation from '~/vue_shared/components/sidebar/queries/updateAssignees.mutation.graphql'; @@ -119,11 +118,7 @@ export default { }, addList: ({ commit }, list) => { - // Temporarily using positioning logic from boardStore - commit( - types.RECEIVE_ADD_LIST_SUCCESS, - boardStore.updateListPosition({ ...list, doNotFetchIssues: true }), - ); + commit(types.RECEIVE_ADD_LIST_SUCCESS, list); }, fetchLabels: ({ state, commit }, searchTerm) => { diff --git a/app/assets/javascripts/boards/stores/mutations.js b/app/assets/javascripts/boards/stores/mutations.js index 1860cc442a5..b58564e358e 100644 --- a/app/assets/javascripts/boards/stores/mutations.js +++ b/app/assets/javascripts/boards/stores/mutations.js @@ -13,7 +13,7 @@ const notImplemented = () => { export const removeIssueFromList = ({ state, listId, issueId }) => { Vue.set(state.issuesByListId, listId, pull(state.issuesByListId[listId], issueId)); 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 }) => { @@ -27,7 +27,7 @@ export const addIssueToList = ({ state, listId, issueId, moveBeforeId, moveAfter listIssues.splice(newIndex, 0, issueId); Vue.set(state.issuesByListId, listId, listIssues); 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 { |