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

gitlab.com/gitlab-org/gitlab-foss.git - Unnamed repository; edit this file 'description' to name the repository.
summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorGitLab Bot <gitlab-bot@gitlab.com>2021-10-20 11:43:02 +0300
committerGitLab Bot <gitlab-bot@gitlab.com>2021-10-20 11:43:02 +0300
commitd9ab72d6080f594d0b3cae15f14b3ef2c6c638cb (patch)
tree2341ef426af70ad1e289c38036737e04b0aa5007 /app/assets/javascripts/boards
parentd6e514dd13db8947884cd58fe2a9c2a063400a9b (diff)
Add latest changes from gitlab-org/gitlab@14-4-stable-eev14.4.0-rc42
Diffstat (limited to 'app/assets/javascripts/boards')
-rw-r--r--app/assets/javascripts/boards/boards_util.js7
-rw-r--r--app/assets/javascripts/boards/components/board_add_new_column.vue4
-rw-r--r--app/assets/javascripts/boards/components/board_add_new_column_trigger.vue25
-rw-r--r--app/assets/javascripts/boards/components/board_card_inner.vue4
-rw-r--r--app/assets/javascripts/boards/components/board_content_sidebar.vue64
-rw-r--r--app/assets/javascripts/boards/components/board_list_header.vue4
-rw-r--r--app/assets/javascripts/boards/graphql.js22
-rw-r--r--app/assets/javascripts/boards/graphql/issue.fragment.graphql4
-rw-r--r--app/assets/javascripts/boards/graphql/lists_issues.query.graphql2
-rw-r--r--app/assets/javascripts/boards/index.js21
-rw-r--r--app/assets/javascripts/boards/stores/actions.js17
-rw-r--r--app/assets/javascripts/boards/stores/mutation_types.js1
-rw-r--r--app/assets/javascripts/boards/stores/mutations.js4
-rw-r--r--app/assets/javascripts/boards/stores/state.js1
14 files changed, 142 insertions, 38 deletions
diff --git a/app/assets/javascripts/boards/boards_util.js b/app/assets/javascripts/boards/boards_util.js
index d113a1d39d8..c10241d00d7 100644
--- a/app/assets/javascripts/boards/boards_util.js
+++ b/app/assets/javascripts/boards/boards_util.js
@@ -43,7 +43,9 @@ export function formatListIssues(listIssues) {
let sortedIssues = list.issues.edges.map((issueNode) => ({
...issueNode.node,
}));
- sortedIssues = sortBy(sortedIssues, 'relativePosition');
+ if (list.listType !== ListType.closed) {
+ sortedIssues = sortBy(sortedIssues, 'relativePosition');
+ }
return {
...map,
@@ -146,7 +148,8 @@ export function getMoveData(state, params) {
}
export function moveItemListHelper(item, fromList, toList) {
- const updatedItem = item;
+ const updatedItem = cloneDeep(item);
+
if (
toList.listType === ListType.label &&
!updatedItem.labels.find((label) => label.id === toList.label.id)
diff --git a/app/assets/javascripts/boards/components/board_add_new_column.vue b/app/assets/javascripts/boards/components/board_add_new_column.vue
index 22ad619e76b..c5411ec313a 100644
--- a/app/assets/javascripts/boards/components/board_add_new_column.vue
+++ b/app/assets/javascripts/boards/components/board_add_new_column.vue
@@ -52,6 +52,8 @@ export default {
},
setSelectedItem(selectedId) {
+ this.selectedId = selectedId;
+
const label = this.labels.find(({ id }) => id === selectedId);
if (!selectedId || !label) {
this.selectedLabel = null;
@@ -87,8 +89,8 @@ export default {
<template #items>
<gl-form-radio-group
v-if="labels.length > 0"
- v-model="selectedId"
class="gl-overflow-y-auto gl-px-5 gl-pt-3"
+ :checked="selectedId"
@change="setSelectedItem"
>
<label
diff --git a/app/assets/javascripts/boards/components/board_add_new_column_trigger.vue b/app/assets/javascripts/boards/components/board_add_new_column_trigger.vue
index 2aee84b805f..14c84d3c4e5 100644
--- a/app/assets/javascripts/boards/components/board_add_new_column_trigger.vue
+++ b/app/assets/javascripts/boards/components/board_add_new_column_trigger.vue
@@ -1,13 +1,23 @@
<script>
-import { GlButton } from '@gitlab/ui';
-import { mapActions } from 'vuex';
+import { GlButton, GlTooltipDirective } from '@gitlab/ui';
+import { mapActions, mapState } from 'vuex';
+import { __ } from '~/locale';
import Tracking from '~/tracking';
export default {
components: {
GlButton,
},
+ directives: {
+ GlTooltip: GlTooltipDirective,
+ },
mixins: [Tracking.mixin()],
+ computed: {
+ ...mapState({ isNewListShowing: ({ addColumnForm }) => addColumnForm.visible }),
+ tooltip() {
+ return this.isNewListShowing ? __('The list creation wizard is already open') : '';
+ },
+ },
methods: {
...mapActions(['setAddColumnFormVisibility']),
handleClick() {
@@ -19,7 +29,14 @@ export default {
</script>
<template>
- <div class="gl-ml-3 gl-display-flex gl-align-items-center" data-testid="boards-create-list">
- <gl-button variant="confirm" @click="handleClick">{{ __('Create list') }} </gl-button>
+ <div
+ v-gl-tooltip="tooltip"
+ :tabindex="isNewListShowing ? '0' : undefined"
+ class="gl-ml-3 gl-display-flex gl-align-items-center"
+ data-testid="boards-create-list"
+ >
+ <gl-button :disabled="isNewListShowing" variant="confirm" @click="handleClick"
+ >{{ __('Create list') }}
+ </gl-button>
</div>
</template>
diff --git a/app/assets/javascripts/boards/components/board_card_inner.vue b/app/assets/javascripts/boards/components/board_card_inner.vue
index db80d48239b..b6ccc6a00fe 100644
--- a/app/assets/javascripts/boards/components/board_card_inner.vue
+++ b/app/assets/javascripts/boards/components/board_card_inner.vue
@@ -316,7 +316,7 @@ export default {
</p>
</gl-tooltip>
- <span ref="countBadge" class="issue-count-badge board-card-info gl-mr-0 gl-pr-0">
+ <span ref="countBadge" class="board-card-info gl-mr-0 gl-pr-0 gl-pl-3">
<span v-if="allowSubEpics" class="gl-mr-3">
<gl-icon name="epic" />
{{ totalEpicsCount }}
@@ -334,7 +334,7 @@ export default {
<span
v-if="shouldRenderEpicProgress"
ref="progressBadge"
- class="issue-count-badge board-card-info gl-pl-0"
+ class="board-card-info gl-pl-0"
>
<span class="gl-mr-3" data-testid="epic-progress">
<gl-icon name="progress" />
diff --git a/app/assets/javascripts/boards/components/board_content_sidebar.vue b/app/assets/javascripts/boards/components/board_content_sidebar.vue
index e0105d63d99..9bbb8a1a1b2 100644
--- a/app/assets/javascripts/boards/components/board_content_sidebar.vue
+++ b/app/assets/javascripts/boards/components/board_content_sidebar.vue
@@ -3,15 +3,18 @@ import { GlDrawer } from '@gitlab/ui';
import { MountingPortal } from 'portal-vue';
import { mapState, mapActions, mapGetters } from 'vuex';
import SidebarDropdownWidget from 'ee_else_ce/sidebar/components/sidebar_dropdown_widget.vue';
+import { __, sprintf } from '~/locale';
import BoardSidebarLabelsSelect from '~/boards/components/sidebar/board_sidebar_labels_select.vue';
import BoardSidebarTimeTracker from '~/boards/components/sidebar/board_sidebar_time_tracker.vue';
import BoardSidebarTitle from '~/boards/components/sidebar/board_sidebar_title.vue';
import { ISSUABLE } from '~/boards/constants';
+import { getIdFromGraphQLId } from '~/graphql_shared/utils';
import SidebarAssigneesWidget from '~/sidebar/components/assignees/sidebar_assignees_widget.vue';
import SidebarConfidentialityWidget from '~/sidebar/components/confidential/sidebar_confidentiality_widget.vue';
import SidebarDateWidget from '~/sidebar/components/date/sidebar_date_widget.vue';
import SidebarSubscriptionsWidget from '~/sidebar/components/subscriptions/sidebar_subscriptions_widget.vue';
import SidebarTodoWidget from '~/sidebar/components/todo_toggle/sidebar_todo_widget.vue';
+import SidebarLabelsWidget from '~/vue_shared/components/sidebar/labels_select_widget/labels_select_root.vue';
import glFeatureFlagMixin from '~/vue_shared/mixins/gl_feature_flags_mixin';
export default {
@@ -23,6 +26,7 @@ export default {
SidebarConfidentialityWidget,
BoardSidebarTimeTracker,
BoardSidebarLabelsSelect,
+ SidebarLabelsWidget,
SidebarSubscriptionsWidget,
SidebarDropdownWidget,
SidebarTodoWidget,
@@ -46,16 +50,20 @@ export default {
weightFeatureAvailable: {
default: false,
},
+ allowLabelEdit: {
+ default: false,
+ },
},
inheritAttrs: false,
computed: {
...mapGetters([
+ 'isGroupBoard',
'isSidebarOpen',
'activeBoardItem',
'groupPathForActiveIssue',
'projectPathForActiveIssue',
]),
- ...mapState(['sidebarType', 'issuableType']),
+ ...mapState(['sidebarType', 'issuableType', 'isSettingLabels']),
isIssuableSidebar() {
return this.sidebarType === ISSUABLE;
},
@@ -65,17 +73,48 @@ export default {
fullPath() {
return this.activeBoardItem?.referencePath?.split('#')[0] || '';
},
+ createLabelTitle() {
+ return sprintf(__('Create %{workspace} label'), {
+ workspace: this.isGroupBoard ? 'group' : 'project',
+ });
+ },
+ manageLabelTitle() {
+ return sprintf(__('Manage %{workspace} labels'), {
+ workspace: this.isGroupBoard ? 'group' : 'project',
+ });
+ },
+ attrWorkspacePath() {
+ return this.isGroupBoard ? this.groupPathForActiveIssue : undefined;
+ },
},
methods: {
...mapActions([
'toggleBoardItem',
'setAssignees',
'setActiveItemConfidential',
+ 'setActiveBoardItemLabels',
'setActiveItemWeight',
]),
handleClose() {
this.toggleBoardItem({ boardItem: this.activeBoardItem, sidebarType: this.sidebarType });
},
+ handleUpdateSelectedLabels(input) {
+ this.setActiveBoardItemLabels({
+ iid: this.activeBoardItem.iid,
+ projectPath: this.projectPathForActiveIssue,
+ addLabelIds: input.map((label) => getIdFromGraphQLId(label.id)),
+ removeLabelIds: this.activeBoardItem.labels
+ .filter((label) => !input.find((selected) => selected.id === label.id))
+ .map((label) => label.id),
+ });
+ },
+ handleLabelRemove(input) {
+ this.setActiveBoardItemLabels({
+ iid: this.activeBoardItem.iid,
+ projectPath: this.projectPathForActiveIssue,
+ removeLabelIds: [input],
+ });
+ },
},
};
</script>
@@ -160,7 +199,28 @@ export default {
:issuable-type="issuableType"
data-testid="sidebar-due-date"
/>
- <board-sidebar-labels-select class="block labels" />
+ <sidebar-labels-widget
+ v-if="glFeatures.labelsWidget"
+ class="block labels"
+ data-testid="sidebar-labels"
+ :iid="activeBoardItem.iid"
+ :full-path="projectPathForActiveIssue"
+ :allow-label-remove="allowLabelEdit"
+ :allow-multiselect="true"
+ :selected-labels="activeBoardItem.labels"
+ :labels-select-in-progress="isSettingLabels"
+ :footer-create-label-title="createLabelTitle"
+ :footer-manage-label-title="manageLabelTitle"
+ :labels-create-title="createLabelTitle"
+ :labels-filter-base-path="projectPathForActiveIssue"
+ :attr-workspace-path="attrWorkspacePath"
+ :issuable-type="issuableType"
+ @onLabelRemove="handleLabelRemove"
+ @updateSelectedLabels="handleUpdateSelectedLabels"
+ >
+ {{ __('None') }}
+ </sidebar-labels-widget>
+ <board-sidebar-labels-select v-else class="block labels" />
<sidebar-weight-widget
v-if="weightFeatureAvailable"
:iid="activeBoardItem.iid"
diff --git a/app/assets/javascripts/boards/components/board_list_header.vue b/app/assets/javascripts/boards/components/board_list_header.vue
index dc5313b1bf6..a8d71ab7a35 100644
--- a/app/assets/javascripts/boards/components/board_list_header.vue
+++ b/app/assets/javascripts/boards/components/board_list_header.vue
@@ -365,7 +365,7 @@ export default {
>
<span class="gl-display-inline-flex">
<gl-tooltip :target="() => $refs.itemCount" :title="itemsTooltipLabel" />
- <span ref="itemCount" class="issue-count-badge-count">
+ <span ref="itemCount" class="gl-display-inline-flex gl-align-items-center">
<gl-icon class="gl-mr-2" :name="countIcon" />
<item-count :items-size="itemsCount" :max-issue-count="list.maxIssueCount" />
</span>
@@ -388,7 +388,7 @@ export default {
v-gl-tooltip.hover
:aria-label="$options.i18n.newIssue"
:title="$options.i18n.newIssue"
- class="issue-count-badge-add-button no-drag"
+ class="no-drag"
icon="plus"
@click="showNewIssueForm"
/>
diff --git a/app/assets/javascripts/boards/graphql.js b/app/assets/javascripts/boards/graphql.js
new file mode 100644
index 00000000000..d8d16184936
--- /dev/null
+++ b/app/assets/javascripts/boards/graphql.js
@@ -0,0 +1,22 @@
+import { IntrospectionFragmentMatcher, defaultDataIdFromObject } from 'apollo-cache-inmemory';
+import createDefaultClient from '~/lib/graphql';
+import introspectionQueryResultData from '~/sidebar/fragmentTypes.json';
+
+const fragmentMatcher = new IntrospectionFragmentMatcher({
+ introspectionQueryResultData,
+});
+
+export const gqlClient = createDefaultClient(
+ {},
+ {
+ cacheConfig: {
+ dataIdFromObject: (object) => {
+ // eslint-disable-next-line no-underscore-dangle
+ return object.__typename === 'BoardList' ? object.iid : defaultDataIdFromObject(object);
+ },
+
+ fragmentMatcher,
+ },
+ assumeImmutableResults: true,
+ },
+);
diff --git a/app/assets/javascripts/boards/graphql/issue.fragment.graphql b/app/assets/javascripts/boards/graphql/issue.fragment.graphql
index 1b14396fb5c..314faae89f8 100644
--- a/app/assets/javascripts/boards/graphql/issue.fragment.graphql
+++ b/app/assets/javascripts/boards/graphql/issue.fragment.graphql
@@ -1,3 +1,4 @@
+#import "~/graphql_shared/fragments/milestone.fragment.graphql"
#import "~/graphql_shared/fragments/user.fragment.graphql"
fragment IssueNode on Issue {
@@ -15,6 +16,9 @@ fragment IssueNode on Issue {
hidden
webUrl
relativePosition
+ milestone {
+ ...MilestoneFragment
+ }
assignees {
nodes {
...User
diff --git a/app/assets/javascripts/boards/graphql/lists_issues.query.graphql b/app/assets/javascripts/boards/graphql/lists_issues.query.graphql
index d1cb1ecf834..787dd77b901 100644
--- a/app/assets/javascripts/boards/graphql/lists_issues.query.graphql
+++ b/app/assets/javascripts/boards/graphql/lists_issues.query.graphql
@@ -16,6 +16,7 @@ query ListIssues(
nodes {
id
issuesCount
+ listType
issues(first: $first, filters: $filters, after: $after) {
edges {
node {
@@ -37,6 +38,7 @@ query ListIssues(
nodes {
id
issuesCount
+ listType
issues(first: $first, filters: $filters, after: $after) {
edges {
node {
diff --git a/app/assets/javascripts/boards/index.js b/app/assets/javascripts/boards/index.js
index 21c1bb23dc6..b6b1094fb3a 100644
--- a/app/assets/javascripts/boards/index.js
+++ b/app/assets/javascripts/boards/index.js
@@ -1,4 +1,3 @@
-import { IntrospectionFragmentMatcher } from 'apollo-cache-inmemory';
import PortalVue from 'portal-vue';
import Vue from 'vue';
import VueApollo from 'vue-apollo';
@@ -14,30 +13,17 @@ import FilteredSearchBoards from '~/boards/filtered_search_boards';
import initBoardsFilteredSearch from '~/boards/mount_filtered_search_issue_boards';
import store from '~/boards/stores';
import toggleFocusMode from '~/boards/toggle_focus';
-import createDefaultClient from '~/lib/graphql';
import { NavigationType, parseBoolean } from '~/lib/utils/common_utils';
-import introspectionQueryResultData from '~/sidebar/fragmentTypes.json';
import { fullBoardId } from './boards_util';
import boardConfigToggle from './config_toggle';
+import { gqlClient } from './graphql';
import mountMultipleBoardsSwitcher from './mount_multiple_boards_switcher';
Vue.use(VueApollo);
Vue.use(PortalVue);
-const fragmentMatcher = new IntrospectionFragmentMatcher({
- introspectionQueryResultData,
-});
-
const apolloProvider = new VueApollo({
- defaultClient: createDefaultClient(
- {},
- {
- cacheConfig: {
- fragmentMatcher,
- },
- assumeImmutableResults: true,
- },
- ),
+ defaultClient: gqlClient,
});
function mountBoardApp(el) {
@@ -101,6 +87,9 @@ function mountBoardApp(el) {
iterationListsAvailable: parseBoolean(el.dataset.iterationListsAvailable),
issuableType: issuableTypes.issue,
emailsDisabled: parseBoolean(el.dataset.emailsDisabled),
+ allowLabelCreate: parseBoolean(el.dataset.canUpdate),
+ allowLabelEdit: parseBoolean(el.dataset.canUpdate),
+ allowScopedLabels: parseBoolean(el.dataset.scopedLabels),
},
render: (createComponent) => createComponent(BoardApp),
});
diff --git a/app/assets/javascripts/boards/stores/actions.js b/app/assets/javascripts/boards/stores/actions.js
index dc06b62cebb..ca993e75cf9 100644
--- a/app/assets/javascripts/boards/stores/actions.js
+++ b/app/assets/javascripts/boards/stores/actions.js
@@ -19,7 +19,6 @@ import {
import createBoardListMutation from 'ee_else_ce/boards/graphql/board_list_create.mutation.graphql';
import issueMoveListMutation from 'ee_else_ce/boards/graphql/issue_move_list.mutation.graphql';
import { getIdFromGraphQLId } from '~/graphql_shared/utils';
-import createGqClient, { fetchPolicies } from '~/lib/graphql';
import { convertObjectPropsToCamelCase } from '~/lib/utils/common_utils';
import { queryToObject } from '~/lib/utils/url_utility';
import { s__ } from '~/locale';
@@ -35,6 +34,7 @@ import {
FiltersInfo,
filterVariables,
} from '../boards_util';
+import { gqlClient } from '../graphql';
import boardLabelsQuery from '../graphql/board_labels.query.graphql';
import groupBoardIterationsQuery from '../graphql/group_board_iterations.query.graphql';
import groupBoardMilestonesQuery from '../graphql/group_board_milestones.query.graphql';
@@ -47,13 +47,6 @@ import projectBoardMilestonesQuery from '../graphql/project_board_milestones.que
import * as types from './mutation_types';
-export const gqlClient = createGqClient(
- {},
- {
- fetchPolicy: fetchPolicies.NO_CACHE,
- },
-);
-
export default {
setInitialBoardData: ({ commit }, data) => {
commit(types.SET_INITIAL_BOARD_DATA, data);
@@ -603,7 +596,7 @@ export default {
});
},
- addListItem: ({ commit }, { list, item, position, inProgress = false }) => {
+ addListItem: ({ commit, dispatch }, { list, item, position, inProgress = false }) => {
commit(types.ADD_BOARD_ITEM_TO_LIST, {
listId: list.id,
itemId: item.id,
@@ -611,6 +604,9 @@ export default {
inProgress,
});
commit(types.UPDATE_BOARD_ITEM, item);
+ if (!inProgress) {
+ dispatch('setActiveId', { id: item.id, sidebarType: ISSUABLE });
+ }
},
removeListItem: ({ commit }, { listId, itemId }) => {
@@ -660,6 +656,7 @@ export default {
},
setActiveIssueLabels: async ({ commit, getters }, input) => {
+ commit(types.SET_LABELS_LOADING, true);
const { activeBoardItem } = getters;
const { data } = await gqlClient.mutate({
mutation: issueSetLabelsMutation,
@@ -673,6 +670,8 @@ export default {
},
});
+ commit(types.SET_LABELS_LOADING, false);
+
if (data.updateIssue?.errors?.length > 0) {
throw new Error(data.updateIssue.errors);
}
diff --git a/app/assets/javascripts/boards/stores/mutation_types.js b/app/assets/javascripts/boards/stores/mutation_types.js
index 928cece19f7..26b785932bb 100644
--- a/app/assets/javascripts/boards/stores/mutation_types.js
+++ b/app/assets/javascripts/boards/stores/mutation_types.js
@@ -28,6 +28,7 @@ export const ADD_BOARD_ITEM_TO_LIST = 'ADD_BOARD_ITEM_TO_LIST';
export const REMOVE_BOARD_ITEM_FROM_LIST = 'REMOVE_BOARD_ITEM_FROM_LIST';
export const SET_ACTIVE_ID = 'SET_ACTIVE_ID';
export const UPDATE_BOARD_ITEM_BY_ID = 'UPDATE_BOARD_ITEM_BY_ID';
+export const SET_LABELS_LOADING = 'SET_LABELS_LOADING';
export const SET_ASSIGNEE_LOADING = 'SET_ASSIGNEE_LOADING';
export const RESET_ISSUES = 'RESET_ISSUES';
export const REQUEST_GROUP_PROJECTS = 'REQUEST_GROUP_PROJECTS';
diff --git a/app/assets/javascripts/boards/stores/mutations.js b/app/assets/javascripts/boards/stores/mutations.js
index ef5b84b4575..d381c076c19 100644
--- a/app/assets/javascripts/boards/stores/mutations.js
+++ b/app/assets/javascripts/boards/stores/mutations.js
@@ -195,6 +195,10 @@ export default {
Vue.set(state.boardItems[itemId], prop, value);
},
+ [mutationTypes.SET_LABELS_LOADING](state, isLoading) {
+ state.isSettingLabels = isLoading;
+ },
+
[mutationTypes.SET_ASSIGNEE_LOADING](state, isLoading) {
state.isSettingAssignees = isLoading;
},
diff --git a/app/assets/javascripts/boards/stores/state.js b/app/assets/javascripts/boards/stores/state.js
index 80c51c966d2..2a6605e687b 100644
--- a/app/assets/javascripts/boards/stores/state.js
+++ b/app/assets/javascripts/boards/stores/state.js
@@ -12,6 +12,7 @@ export default () => ({
listsFlags: {},
boardItemsByListId: {},
backupItemsList: [],
+ isSettingLabels: false,
isSettingAssignees: false,
pageInfoByListId: {},
boardItems: {},