diff options
author | GitLab Bot <gitlab-bot@gitlab.com> | 2021-10-20 11:43:02 +0300 |
---|---|---|
committer | GitLab Bot <gitlab-bot@gitlab.com> | 2021-10-20 11:43:02 +0300 |
commit | d9ab72d6080f594d0b3cae15f14b3ef2c6c638cb (patch) | |
tree | 2341ef426af70ad1e289c38036737e04b0aa5007 /app/assets/javascripts/boards | |
parent | d6e514dd13db8947884cd58fe2a9c2a063400a9b (diff) |
Add latest changes from gitlab-org/gitlab@14-4-stable-eev14.4.0-rc42
Diffstat (limited to 'app/assets/javascripts/boards')
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: {}, |