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>2023-06-20 13:43:29 +0300
committerGitLab Bot <gitlab-bot@gitlab.com>2023-06-20 13:43:29 +0300
commit3b1af5cc7ed2666ff18b718ce5d30fa5a2756674 (patch)
tree3bc4a40e0ee51ec27eabf917c537033c0c5b14d4 /app/assets/javascripts/issues
parent9bba14be3f2c211bf79e15769cd9b77bc73a13bc (diff)
Add latest changes from gitlab-org/gitlab@16-1-stable-eev16.1.0-rc42
Diffstat (limited to 'app/assets/javascripts/issues')
-rw-r--r--app/assets/javascripts/issues/constants.js1
-rw-r--r--app/assets/javascripts/issues/dashboard/components/issues_dashboard_app.vue19
-rw-r--r--app/assets/javascripts/issues/dashboard/index.js6
-rw-r--r--app/assets/javascripts/issues/dashboard/queries/get_issues.query.graphql5
-rw-r--r--app/assets/javascripts/issues/dashboard/queries/issue.fragment.graphql56
-rw-r--r--app/assets/javascripts/issues/list/components/empty_state_with_any_issues.vue8
-rw-r--r--app/assets/javascripts/issues/list/components/empty_state_without_any_issues.vue16
-rw-r--r--app/assets/javascripts/issues/list/components/issues_list_app.vue91
-rw-r--r--app/assets/javascripts/issues/list/constants.js6
-rw-r--r--app/assets/javascripts/issues/list/queries/search_users.query.graphql5
-rw-r--r--app/assets/javascripts/issues/show/components/app.vue29
-rw-r--r--app/assets/javascripts/issues/show/components/description.vue60
-rw-r--r--app/assets/javascripts/issues/show/components/fields/type.vue2
-rw-r--r--app/assets/javascripts/issues/show/components/form.vue2
-rw-r--r--app/assets/javascripts/issues/show/components/header_actions.vue38
-rw-r--r--app/assets/javascripts/issues/show/components/task_list_item_actions.vue6
-rw-r--r--app/assets/javascripts/issues/show/components/title.vue1
-rw-r--r--app/assets/javascripts/issues/show/index.js1
18 files changed, 202 insertions, 150 deletions
diff --git a/app/assets/javascripts/issues/constants.js b/app/assets/javascripts/issues/constants.js
index c79612ad5d0..444ee704521 100644
--- a/app/assets/javascripts/issues/constants.js
+++ b/app/assets/javascripts/issues/constants.js
@@ -14,6 +14,7 @@ export const TYPE_EPIC = 'epic';
export const TYPE_INCIDENT = 'incident';
export const TYPE_ISSUE = 'issue';
export const TYPE_MERGE_REQUEST = 'merge_request';
+export const TYPE_MILESTONE = 'milestone';
export const TYPE_TEST_CASE = 'test_case';
export const WORKSPACE_GROUP = 'group';
diff --git a/app/assets/javascripts/issues/dashboard/components/issues_dashboard_app.vue b/app/assets/javascripts/issues/dashboard/components/issues_dashboard_app.vue
index b9e4d0df3f2..14fe88b8f61 100644
--- a/app/assets/javascripts/issues/dashboard/components/issues_dashboard_app.vue
+++ b/app/assets/javascripts/issues/dashboard/components/issues_dashboard_app.vue
@@ -29,6 +29,7 @@ import {
getSortOptions,
isSortKey,
} from '~/issues/list/utils';
+import { fetchPolicies } from '~/lib/graphql';
import axios from '~/lib/utils/axios_utils';
import { scrollUp } from '~/lib/utils/scroll_utils';
import { getParameterByName } from '~/lib/utils/url_utility';
@@ -126,6 +127,10 @@ export default {
update(data) {
return data.issues.nodes ?? [];
},
+ fetchPolicy: fetchPolicies.CACHE_AND_NETWORK,
+ // We need this for handling loading state when using frontend cache
+ // See https://gitlab.com/gitlab-org/gitlab/-/merge_requests/106004#note_1217325202 for details
+ notifyOnNetworkStatusChange: true,
result({ data }) {
this.pageInfo = data?.issues.pageInfo ?? {};
},
@@ -183,6 +188,17 @@ export default {
hasSearch() {
return Boolean(this.searchQuery || Object.keys(this.urlFilterParams).length);
},
+ // due to the issues with cache-and-network, we need this hack to check if there is any data for the query in the cache.
+ // if we have cached data, we disregard the loading state
+ isLoading() {
+ return (
+ this.$apollo.queries.issues.loading &&
+ !this.$apollo.provider.clients.defaultClient.readQuery({
+ query: getIssuesQuery,
+ variables: this.queryVariables,
+ })
+ );
+ },
queryVariables() {
return {
hideUsers: this.isPublicVisibilityRestricted && !this.isSignedIn,
@@ -446,7 +462,7 @@ export default {
:initial-filter-value="filterTokens"
:initial-sort-by="sortKey"
:issuables="renderedIssues"
- :issuables-loading="$apollo.queries.issues.loading"
+ :issuables-loading="isLoading"
namespace="dashboard"
recent-searches-storage-key="issues"
:search-input-placeholder="$options.i18n.searchPlaceholder"
@@ -494,6 +510,7 @@ export default {
<gl-empty-state
:description="emptyStateDescription"
:svg-path="emptyStateSvgPath"
+ :svg-height="150"
:title="emptyStateTitle"
/>
</template>
diff --git a/app/assets/javascripts/issues/dashboard/index.js b/app/assets/javascripts/issues/dashboard/index.js
index 005ab5ce3b0..999f07781b2 100644
--- a/app/assets/javascripts/issues/dashboard/index.js
+++ b/app/assets/javascripts/issues/dashboard/index.js
@@ -1,10 +1,10 @@
import Vue from 'vue';
import VueApollo from 'vue-apollo';
-import createDefaultClient from '~/lib/graphql';
+import { gqlClient } from '~/issues/list/graphql';
import { parseBoolean } from '~/lib/utils/common_utils';
import IssuesDashboardApp from './components/issues_dashboard_app.vue';
-export function mountIssuesDashboardApp() {
+export async function mountIssuesDashboardApp() {
const el = document.querySelector('.js-issues-dashboard');
if (!el) {
@@ -34,7 +34,7 @@ export function mountIssuesDashboardApp() {
el,
name: 'IssuesDashboardRoot',
apolloProvider: new VueApollo({
- defaultClient: createDefaultClient(),
+ defaultClient: await gqlClient(),
}),
provide: {
autocompleteAwardEmojisPath,
diff --git a/app/assets/javascripts/issues/dashboard/queries/get_issues.query.graphql b/app/assets/javascripts/issues/dashboard/queries/get_issues.query.graphql
index 5625e6afad3..5c331fe95e2 100644
--- a/app/assets/javascripts/issues/dashboard/queries/get_issues.query.graphql
+++ b/app/assets/javascripts/issues/dashboard/queries/get_issues.query.graphql
@@ -1,5 +1,5 @@
#import "~/graphql_shared/fragments/page_info.fragment.graphql"
-#import "./issue.fragment.graphql"
+#import "~/issues/list/queries/issue.fragment.graphql"
query getDashboardIssues(
$hideUsers: Boolean = false
@@ -44,8 +44,9 @@ query getDashboardIssues(
before: $beforeCursor
first: $firstPageSize
last: $lastPageSize
- ) {
+ ) @persist {
nodes {
+ __persist
...IssueFragment
reference(full: true)
}
diff --git a/app/assets/javascripts/issues/dashboard/queries/issue.fragment.graphql b/app/assets/javascripts/issues/dashboard/queries/issue.fragment.graphql
deleted file mode 100644
index 040763f2ba4..00000000000
--- a/app/assets/javascripts/issues/dashboard/queries/issue.fragment.graphql
+++ /dev/null
@@ -1,56 +0,0 @@
-fragment IssueFragment on Issue {
- id
- iid
- confidential
- createdAt
- downvotes
- dueDate
- hidden
- humanTimeEstimate
- mergeRequestsCount
- moved
- state
- title
- updatedAt
- closedAt
- upvotes
- userDiscussionsCount @include(if: $isSignedIn)
- webPath
- webUrl
- type
- assignees @skip(if: $hideUsers) {
- nodes {
- id
- avatarUrl
- name
- username
- webUrl
- }
- }
- author @skip(if: $hideUsers) {
- id
- avatarUrl
- name
- username
- webUrl
- }
- labels {
- nodes {
- id
- color
- title
- description
- }
- }
- milestone {
- id
- dueDate
- startDate
- webPath
- title
- }
- taskCompletionStatus {
- completedCount
- count
- }
-}
diff --git a/app/assets/javascripts/issues/list/components/empty_state_with_any_issues.vue b/app/assets/javascripts/issues/list/components/empty_state_with_any_issues.vue
index 8aece24de0c..3c58843bcbc 100644
--- a/app/assets/javascripts/issues/list/components/empty_state_with_any_issues.vue
+++ b/app/assets/javascripts/issues/list/components/empty_state_with_any_issues.vue
@@ -28,6 +28,7 @@ export default {
:description="$options.i18n.noSearchResultsDescription"
:title="$options.i18n.noSearchResultsTitle"
:svg-path="emptyStateSvgPath"
+ :svg-height="150"
>
<template #actions>
<gl-button v-if="showNewIssueLink" :href="newIssuePath" variant="confirm">
@@ -49,5 +50,10 @@ export default {
</template>
</gl-empty-state>
- <gl-empty-state v-else :title="$options.i18n.noClosedIssuesTitle" :svg-path="emptyStateSvgPath" />
+ <gl-empty-state
+ v-else
+ :title="$options.i18n.noClosedIssuesTitle"
+ :svg-path="emptyStateSvgPath"
+ :svg-height="150"
+ />
</template>
diff --git a/app/assets/javascripts/issues/list/components/empty_state_without_any_issues.vue b/app/assets/javascripts/issues/list/components/empty_state_without_any_issues.vue
index 98429f3ffd1..3f29fc66abb 100644
--- a/app/assets/javascripts/issues/list/components/empty_state_without_any_issues.vue
+++ b/app/assets/javascripts/issues/list/components/empty_state_without_any_issues.vue
@@ -1,5 +1,5 @@
<script>
-import { GlButton, GlDropdown, GlEmptyState, GlLink, GlSprintf } from '@gitlab/ui';
+import { GlButton, GlDisclosureDropdown, GlEmptyState, GlLink, GlSprintf } from '@gitlab/ui';
import { helpPagePath } from '~/helpers/help_page_helper';
import CsvImportExportButtons from '~/issuable/components/csv_import_export_buttons.vue';
import NewResourceDropdown from '~/vue_shared/components/new_resource_dropdown/new_resource_dropdown.vue';
@@ -12,7 +12,7 @@ export default {
components: {
CsvImportExportButtons,
GlButton,
- GlDropdown,
+ GlDisclosureDropdown,
GlEmptyState,
GlLink,
GlSprintf,
@@ -56,7 +56,11 @@ export default {
<template>
<div v-if="isSignedIn">
- <gl-empty-state :title="$options.i18n.noIssuesTitle" :svg-path="emptyStateSvgPath">
+ <gl-empty-state
+ :title="$options.i18n.noIssuesTitle"
+ :svg-path="emptyStateSvgPath"
+ :svg-height="150"
+ >
<template #description>
<gl-link :href="$options.issuesHelpPagePath">
{{ $options.i18n.noIssuesDescription }}
@@ -73,17 +77,17 @@ export default {
{{ $options.i18n.newIssueLabel }}
</gl-button>
- <gl-dropdown
+ <gl-disclosure-dropdown
v-if="showCsvButtons"
class="gl-w-full gl-sm-w-auto gl-sm-mr-3"
- :text="$options.i18n.importIssues"
+ :toggle-text="$options.i18n.importIssues"
data-qa-selector="import_issues_dropdown"
>
<csv-import-export-buttons
:export-csv-path="exportCsvPathWithQuery"
:issuable-count="currentTabCount"
/>
- </gl-dropdown>
+ </gl-disclosure-dropdown>
<new-resource-dropdown
v-if="showNewIssueDropdown"
diff --git a/app/assets/javascripts/issues/list/components/issues_list_app.vue b/app/assets/javascripts/issues/list/components/issues_list_app.vue
index 5fb83dfd1ab..83b0bcebe67 100644
--- a/app/assets/javascripts/issues/list/components/issues_list_app.vue
+++ b/app/assets/javascripts/issues/list/components/issues_list_app.vue
@@ -1,11 +1,11 @@
<script>
import {
GlButton,
+ GlButtonGroup,
+ GlDisclosureDropdown,
+ GlDisclosureDropdownGroup,
GlFilteredSearchToken,
GlTooltipDirective,
- GlDropdown,
- GlDropdownItem,
- GlDropdownDivider,
} from '@gitlab/ui';
import * as Sentry from '@sentry/browser';
import fuzzaldrinPlus from 'fuzzaldrin-plus';
@@ -14,6 +14,7 @@ import IssueCardStatistics from 'ee_else_ce/issues/list/components/issue_card_st
import IssueCardTimeInfo from 'ee_else_ce/issues/list/components/issue_card_time_info.vue';
import getIssuesQuery from 'ee_else_ce/issues/list/queries/get_issues.query.graphql';
import getIssuesCountsQuery from 'ee_else_ce/issues/list/queries/get_issues_counts.query.graphql';
+import LocalStorageSync from '~/vue_shared/components/local_storage_sync.vue';
import { createAlert, VARIANT_INFO } from '~/alert';
import { TYPENAME_USER } from '~/graphql_shared/constants';
import CsvImportExportButtons from '~/issuable/components/csv_import_export_buttons.vue';
@@ -68,6 +69,9 @@ import {
defaultWorkItemTypes,
i18n,
ISSUE_REFERENCE,
+ ISSUES_GRID_VIEW_KEY,
+ ISSUES_LIST_VIEW_KEY,
+ ISSUES_VIEW_TYPE_KEY,
MAX_LIST_SIZE,
PARAM_FIRST_PAGE_SIZE,
PARAM_LAST_PAGE_SIZE,
@@ -116,19 +120,23 @@ const CrmOrganizationToken = () =>
export default {
i18n,
issuableListTabs,
+ ISSUES_VIEW_TYPE_KEY,
+ ISSUES_GRID_VIEW_KEY,
+ ISSUES_LIST_VIEW_KEY,
components: {
CsvImportExportButtons,
+ GlDisclosureDropdown,
+ GlDisclosureDropdownGroup,
EmptyStateWithAnyIssues,
EmptyStateWithoutAnyIssues,
GlButton,
- GlDropdown,
- GlDropdownDivider,
- GlDropdownItem,
+ GlButtonGroup,
IssuableByEmail,
IssuableList,
IssueCardStatistics,
IssueCardTimeInfo,
NewResourceDropdown,
+ LocalStorageSync,
},
directives: {
GlTooltip: GlTooltipDirective,
@@ -194,6 +202,21 @@ export default {
sortKey: CREATED_DESC,
state: STATUS_OPEN,
pageSize: DEFAULT_PAGE_SIZE,
+ viewType: ISSUES_LIST_VIEW_KEY,
+ subscribeDropdownOptions: {
+ items: [
+ {
+ text: i18n.rssLabel,
+ href: this.rssPath,
+ extraAttrs: { 'data-testid': 'subscribe-rss' },
+ },
+ {
+ text: i18n.calendarLabel,
+ href: this.calendarPath,
+ extraAttrs: { 'data-testid': 'subscribe-calendar' },
+ },
+ ],
+ },
};
},
apollo: {
@@ -504,6 +527,12 @@ export default {
})
);
},
+ gridViewFeatureEnabled() {
+ return Boolean(this.glFeatures?.issuesGridView);
+ },
+ isGridView() {
+ return this.viewType === ISSUES_GRID_VIEW_KEY;
+ },
},
watch: {
$route(newValue, oldValue) {
@@ -764,6 +793,15 @@ export default {
this.sortKey = sortKey;
this.state = state || STATUS_OPEN;
},
+ switchViewType(type) {
+ // Filter the wrong data from localStorage
+ if (type === ISSUES_GRID_VIEW_KEY) {
+ this.viewType = ISSUES_GRID_VIEW_KEY;
+ return;
+ }
+ // The default view is list view
+ this.viewType = ISSUES_LIST_VIEW_KEY;
+ },
},
};
</script>
@@ -798,6 +836,7 @@ export default {
:has-next-page="pageInfo.hasNextPage"
:has-previous-page="pageInfo.hasPreviousPage"
:show-filtered-search-friendly-text="hasOrFeature"
+ :is-grid-view="isGridView"
show-work-item-type-icon
@click-tab="handleClickTab"
@dismiss-alert="handleDismissAlert"
@@ -810,6 +849,30 @@ export default {
@page-size-change="handlePageSizeChange"
>
<template #nav-actions>
+ <local-storage-sync
+ v-if="gridViewFeatureEnabled"
+ :value="viewType"
+ :storage-key="$options.ISSUES_VIEW_TYPE_KEY"
+ @input="switchViewType"
+ >
+ <gl-button-group>
+ <gl-button
+ :variant="isGridView ? 'default' : 'confirm'"
+ data-testid="list-view-type"
+ @click="switchViewType($options.ISSUES_LIST_VIEW_KEY)"
+ >
+ {{ $options.i18n.listLabel }}
+ </gl-button>
+ <gl-button
+ :variant="isGridView ? 'confirm' : 'default'"
+ data-testid="grid-view-type"
+ @click="switchViewType($options.ISSUES_GRID_VIEW_KEY)"
+ >
+ {{ $options.i18n.gridLabel }}
+ </gl-button>
+ </gl-button-group>
+ </local-storage-sync>
+
<gl-button
v-if="canBulkUpdate"
:disabled="isBulkEditButtonDisabled"
@@ -831,12 +894,12 @@ export default {
:query-variables="newIssueDropdownQueryVariables"
:extract-projects="extractProjects"
/>
- <gl-dropdown
+ <gl-disclosure-dropdown
v-gl-tooltip.hover="$options.i18n.actionsLabel"
category="tertiary"
icon="ellipsis_v"
no-caret
- :text="$options.i18n.actionsLabel"
+ :toggle-text="$options.i18n.actionsLabel"
text-sr-only
data-qa-selector="issues_list_more_actions_dropdown"
>
@@ -845,16 +908,8 @@ export default {
:export-csv-path="exportCsvPathWithQuery"
:issuable-count="currentTabCount"
/>
-
- <gl-dropdown-divider v-if="showCsvButtons" />
-
- <gl-dropdown-item :href="rssPath">
- {{ $options.i18n.rssLabel }}
- </gl-dropdown-item>
- <gl-dropdown-item :href="calendarPath">
- {{ $options.i18n.calendarLabel }}
- </gl-dropdown-item>
- </gl-dropdown>
+ <gl-disclosure-dropdown-group :bordered="true" :group="subscribeDropdownOptions" />
+ </gl-disclosure-dropdown>
</template>
<template #timeframe="{ issuable = {} }">
diff --git a/app/assets/javascripts/issues/list/constants.js b/app/assets/javascripts/issues/list/constants.js
index 56d3a57457b..1a3d97277c7 100644
--- a/app/assets/javascripts/issues/list/constants.js
+++ b/app/assets/javascripts/issues/list/constants.js
@@ -75,6 +75,10 @@ export const NORMAL_FILTER = 'normalFilter';
export const SPECIAL_FILTER = 'specialFilter';
export const ALTERNATIVE_FILTER = 'alternativeFilter';
+export const ISSUES_VIEW_TYPE_KEY = 'issuesViewType';
+export const ISSUES_LIST_VIEW_KEY = 'List';
+export const ISSUES_GRID_VIEW_KEY = 'Grid';
+
export const i18n = {
actionsLabel: __('Actions'),
calendarLabel: __('Subscribe to calendar'),
@@ -116,6 +120,8 @@ export const i18n = {
upvotes: __('Upvotes'),
titles: __('Titles'),
descriptions: __('Descriptions'),
+ listLabel: __('List'),
+ gridLabel: __('Grid'),
};
export const urlSortParams = {
diff --git a/app/assets/javascripts/issues/list/queries/search_users.query.graphql b/app/assets/javascripts/issues/list/queries/search_users.query.graphql
index 46b48e4e41c..6a1967a8875 100644
--- a/app/assets/javascripts/issues/list/queries/search_users.query.graphql
+++ b/app/assets/javascripts/issues/list/queries/search_users.query.graphql
@@ -14,7 +14,10 @@ query searchUsers($fullPath: ID!, $search: String, $isProject: Boolean = false)
}
project(fullPath: $fullPath) @include(if: $isProject) {
id
- projectMembers(search: $search, relations: [DIRECT, INHERITED, INVITED_GROUPS]) {
+ projectMembers(
+ search: $search
+ relations: [DIRECT, INHERITED, INVITED_GROUPS, SHARED_INTO_ANCESTORS]
+ ) {
nodes {
id
user {
diff --git a/app/assets/javascripts/issues/show/components/app.vue b/app/assets/javascripts/issues/show/components/app.vue
index 86311b99f7c..fcdf1f7741b 100644
--- a/app/assets/javascripts/issues/show/components/app.vue
+++ b/app/assets/javascripts/issues/show/components/app.vue
@@ -188,6 +188,11 @@ export default {
required: false,
default: null,
},
+ issueIid: {
+ type: Number,
+ required: false,
+ default: null,
+ },
},
data() {
const store = new Store({
@@ -446,7 +451,10 @@ export default {
},
showStickyHeader() {
- this.isStickyHeaderShowing = true;
+ // only if scrolled under the issue's title
+ if (this.$refs.title.$el.offsetTop < window.pageYOffset) {
+ this.isStickyHeaderShowing = true;
+ }
},
handleSaveDescription(description) {
@@ -496,6 +504,7 @@ export default {
</div>
<div v-else>
<title-component
+ ref="title"
:issuable-ref="issuableRef"
:can-update="canUpdate"
:title-html="state.titleHtml"
@@ -522,7 +531,13 @@ export default {
statusText
}}</span></gl-badge
>
- <span v-if="isLocked" data-testid="locked" class="issuable-warning-icon">
+ <span
+ v-if="isLocked"
+ v-gl-tooltip.bottom
+ data-testid="locked"
+ class="issuable-warning-icon"
+ :title="__('This issue is locked. Only project members can comment.')"
+ >
<gl-icon name="lock" :aria-label="__('Locked')" />
</span>
<confidentiality-badge
@@ -533,19 +548,20 @@ export default {
/>
<span
v-if="isHidden"
- v-gl-tooltip
+ v-gl-tooltip.bottom
:title="__('This issue is hidden because its author has been banned')"
data-testid="hidden"
class="issuable-warning-icon"
>
<gl-icon name="spam" />
</span>
- <p
- class="gl-font-weight-bold gl-overflow-hidden gl-white-space-nowrap gl-text-overflow-ellipsis gl-my-0"
+ <a
+ href="#top"
+ class="gl-font-weight-bold gl-overflow-hidden gl-white-space-nowrap gl-text-overflow-ellipsis gl-my-0 gl-text-black-normal"
:title="state.titleText"
>
{{ state.titleText }}
- </p>
+ </a>
</div>
</div>
</transition>
@@ -560,6 +576,7 @@ export default {
<component
:is="descriptionComponent"
:issue-id="issueId"
+ :issue-iid="issueIid"
:can-update="canUpdate"
:description-html="state.descriptionHtml"
:description-text="state.descriptionText"
diff --git a/app/assets/javascripts/issues/show/components/description.vue b/app/assets/javascripts/issues/show/components/description.vue
index 3721f224d5e..3bf4dfc7a99 100644
--- a/app/assets/javascripts/issues/show/components/description.vue
+++ b/app/assets/javascripts/issues/show/components/description.vue
@@ -11,8 +11,7 @@ import { TYPE_ISSUE } from '~/issues/constants';
import { __, s__, sprintf } from '~/locale';
import { getSortableDefaultOptions, isDragging } from '~/sortable/utils';
import TaskList from '~/task_list';
-import addHierarchyChildMutation from '~/work_items/graphql/add_hierarchy_child.mutation.graphql';
-import removeHierarchyChildMutation from '~/work_items/graphql/remove_hierarchy_child.mutation.graphql';
+import { addHierarchyChild, removeHierarchyChild } from '~/work_items/graphql/cache_utils';
import createWorkItemMutation from '~/work_items/graphql/create_work_item.mutation.graphql';
import deleteWorkItemMutation from '~/work_items/graphql/delete_work_item.mutation.graphql';
import projectWorkItemTypesQuery from '~/work_items/graphql/project_work_item_types.query.graphql';
@@ -78,6 +77,11 @@ export default {
required: false,
default: null,
},
+ issueIid: {
+ type: Number,
+ required: false,
+ default: null,
+ },
isUpdating: {
type: Boolean,
required: false,
@@ -330,29 +334,33 @@ export default {
async createTask({ taskTitle, taskDescription, oldDescription }) {
try {
const { title, description } = extractTaskTitleAndDescription(taskTitle, taskDescription);
+
const iterationInput = {
iterationWidget: {
iterationId: this.issueDetails.iteration?.id ?? null,
},
};
- const input = {
- confidential: this.issueDetails.confidential,
- description,
- hierarchyWidget: {
- parentId: this.issueGid,
- },
- ...(this.hasIterationsFeature && iterationInput),
- milestoneWidget: {
- milestoneId: this.issueDetails.milestone?.id ?? null,
- },
- projectPath: this.fullPath,
- title,
- workItemTypeId: this.taskWorkItemTypeId,
- };
const { data } = await this.$apollo.mutate({
mutation: createWorkItemMutation,
- variables: { input },
+ variables: {
+ input: {
+ confidential: this.issueDetails.confidential,
+ description,
+ hierarchyWidget: {
+ parentId: this.issueGid,
+ },
+ ...(this.hasIterationsFeature && iterationInput),
+ milestoneWidget: {
+ milestoneId: this.issueDetails.milestone?.id ?? null,
+ },
+ projectPath: this.fullPath,
+ title,
+ workItemTypeId: this.taskWorkItemTypeId,
+ },
+ },
+ update: (cache, { data: { workItemCreate } }) =>
+ addHierarchyChild(cache, this.fullPath, String(this.issueIid), workItemCreate.workItem),
});
const { workItem, errors } = data.workItemCreate;
@@ -361,11 +369,6 @@ export default {
throw new Error(errors);
}
- await this.$apollo.mutate({
- mutation: addHierarchyChildMutation,
- variables: { id: this.issueGid, workItem },
- });
-
this.$toast.show(s__('WorkItem|Converted to task'), {
action: {
text: s__('WorkItem|Undo'),
@@ -386,19 +389,14 @@ export default {
const { data } = await this.$apollo.mutate({
mutation: deleteWorkItemMutation,
variables: { input: { id } },
+ update: (cache) =>
+ removeHierarchyChild(cache, this.fullPath, String(this.issueIid), { id }),
});
- const { errors } = data.workItemDelete;
-
- if (errors?.length) {
- throw new Error(errors);
+ if (data.workItemDelete.errors?.length) {
+ throw new Error(data.workItemDelete.errors);
}
- await this.$apollo.mutate({
- mutation: removeHierarchyChildMutation,
- variables: { id: this.issueGid, workItem: { id } },
- });
-
this.$toast.show(s__('WorkItem|Task reverted'));
} catch (error) {
this.showAlert(I18N_WORK_ITEM_ERROR_DELETING, error);
diff --git a/app/assets/javascripts/issues/show/components/fields/type.vue b/app/assets/javascripts/issues/show/components/fields/type.vue
index 775f25bdbc0..576d157e0fc 100644
--- a/app/assets/javascripts/issues/show/components/fields/type.vue
+++ b/app/assets/javascripts/issues/show/components/fields/type.vue
@@ -71,7 +71,7 @@ export default {
:label="$options.i18n.label"
label-class="sr-only"
label-for="issuable-type"
- class="mb-2 mb-md-0"
+ class="gl-mb-0"
>
<gl-collapsible-listbox
v-model="selectedIssueType"
diff --git a/app/assets/javascripts/issues/show/components/form.vue b/app/assets/javascripts/issues/show/components/form.vue
index 2e99c03d250..c9e21b296e4 100644
--- a/app/assets/javascripts/issues/show/components/form.vue
+++ b/app/assets/javascripts/issues/show/components/form.vue
@@ -222,7 +222,7 @@ export default {
<convert-description-modal
v-if="issueId && glFeatures.generateDescriptionAi"
- class="gl-pl-5 gl-sm-pl-0"
+ class="gl-pl-5 gl-md-pl-0"
:resource-id="resourceId"
:user-id="userId"
@contentGenerated="setDescription"
diff --git a/app/assets/javascripts/issues/show/components/header_actions.vue b/app/assets/javascripts/issues/show/components/header_actions.vue
index 4d9b69ddf99..a36b0c46927 100644
--- a/app/assets/javascripts/issues/show/components/header_actions.vue
+++ b/app/assets/javascripts/issues/show/components/header_actions.vue
@@ -20,7 +20,7 @@ import {
NEW_ACTIONS_POPOVER_KEY,
} from '~/issues/show/constants';
import { capitalizeFirstCharacter } from '~/lib/utils/text_utility';
-import { getCookie, parseBoolean, setCookie } from '~/lib/utils/common_utils';
+import { getCookie, parseBoolean, setCookie, isLoggedIn } from '~/lib/utils/common_utils';
import { visitUrl } from '~/lib/utils/url_utility';
import { s__, __, sprintf } from '~/locale';
import eventHub from '~/notes/event_hub';
@@ -137,6 +137,7 @@ export default {
data() {
return {
isReportAbuseDrawerOpen: false,
+ isUserSignedIn: isLoggedIn(),
};
},
apollo: {
@@ -204,7 +205,11 @@ export default {
},
hasDesktopDropdown() {
return (
- this.canCreateIssue || this.canPromoteToEpic || !this.isIssueAuthor || this.canReportSpam
+ this.canCreateIssue ||
+ this.canPromoteToEpic ||
+ !this.isIssueAuthor ||
+ this.canReportSpam ||
+ this.issuableReference
);
},
hasMobileDropdown() {
@@ -219,7 +224,10 @@ export default {
return this.glFeatures.movedMrSidebar;
},
showLockIssueOption() {
- return this.isMrSidebarMoved && this.issueType === TYPE_ISSUE;
+ return this.isMrSidebarMoved && this.issueType === TYPE_ISSUE && this.isUserSignedIn;
+ },
+ showMovedSidebarOptions() {
+ return this.isMrSidebarMoved && this.isUserSignedIn;
},
},
created() {
@@ -326,17 +334,16 @@ export default {
</script>
<template>
- <div class="detail-page-header-actions gl-display-flex gl-align-self-start gl-gap-3">
+ <div class="detail-page-header-actions gl-display-flex gl-align-self-start gl-sm-gap-3">
<gl-dropdown
v-if="hasMobileDropdown"
class="gl-sm-display-none! w-100"
block
:text="dropdownText"
- data-qa-selector="issue_actions_dropdown"
data-testid="mobile-dropdown"
:loading="isToggleStateButtonLoading"
>
- <template v-if="isMrSidebarMoved">
+ <template v-if="showMovedSidebarOptions">
<sidebar-subscriptions-widget
:iid="String(iid)"
:full-path="fullPath"
@@ -356,7 +363,7 @@ export default {
</gl-dropdown-item>
<gl-dropdown-item
v-if="showToggleIssueStateButton"
- :data-qa-selector="`mobile_${qaSelector}`"
+ :data-testid="`mobile_${qaSelector}`"
@click="toggleIssueState"
>
{{ buttonText }}
@@ -375,7 +382,7 @@ export default {
>{{ $options.i18n.copyReferenceText }}</gl-dropdown-item
>
<gl-dropdown-item
- v-if="issuableEmailAddress"
+ v-if="issuableEmailAddress && showMovedSidebarOptions"
:data-clipboard-text="issuableEmailAddress"
data-testid="copy-email"
@click="copyEmailAddress"
@@ -401,7 +408,7 @@ export default {
</gl-dropdown-item>
</template>
<gl-dropdown-item
- v-if="!isIssueAuthor"
+ v-if="!isIssueAuthor && isUserSignedIn"
data-testid="report-abuse-item"
@click="toggleReportAbuseDrawer(true)"
>
@@ -426,7 +433,7 @@ export default {
class="gl-display-none gl-sm-display-inline-flex!"
:data-qa-selector="qaSelector"
:loading="isToggleStateButtonLoading"
- data-testid="toggle-button"
+ data-testid="toggle-issue-state-button"
@click="toggleIssueState"
>
{{ buttonText }}
@@ -439,7 +446,6 @@ export default {
class="gl-display-none gl-sm-display-inline-flex!"
icon="ellipsis_v"
category="tertiary"
- data-qa-selector="issue_actions_ellipsis_dropdown"
:text="dropdownText"
:text-sr-only="true"
:title="dropdownText"
@@ -449,7 +455,7 @@ export default {
right
@shown="dismissPopover"
>
- <template v-if="isMrSidebarMoved">
+ <template v-if="showMovedSidebarOptions">
<sidebar-subscriptions-widget
:iid="String(iid)"
:full-path="fullPath"
@@ -460,7 +466,7 @@ export default {
<gl-dropdown-divider />
</template>
- <gl-dropdown-item v-if="canCreateIssue" :href="newIssuePath">
+ <gl-dropdown-item v-if="canCreateIssue && isUserSignedIn" :href="newIssuePath">
{{ newIssueTypeText }}
</gl-dropdown-item>
<gl-dropdown-item
@@ -482,7 +488,7 @@ export default {
>{{ $options.i18n.copyReferenceText }}</gl-dropdown-item
>
<gl-dropdown-item
- v-if="issuableEmailAddress"
+ v-if="issuableEmailAddress && showMovedSidebarOptions"
:data-clipboard-text="issuableEmailAddress"
data-testid="copy-email"
@click="copyEmailAddress"
@@ -502,14 +508,14 @@ export default {
<gl-dropdown-item
v-gl-modal="$options.deleteModalId"
variant="danger"
- data-qa-selector="delete_issue_button"
+ data-testid="delete_issue_button"
@click="track('click_dropdown')"
>
{{ deleteButtonText }}
</gl-dropdown-item>
</template>
<gl-dropdown-item
- v-if="!isIssueAuthor"
+ v-if="!isIssueAuthor && isUserSignedIn"
data-testid="report-abuse-item"
@click="toggleReportAbuseDrawer(true)"
>
diff --git a/app/assets/javascripts/issues/show/components/task_list_item_actions.vue b/app/assets/javascripts/issues/show/components/task_list_item_actions.vue
index 5160903c762..64b916caddb 100644
--- a/app/assets/javascripts/issues/show/components/task_list_item_actions.vue
+++ b/app/assets/javascripts/issues/show/components/task_list_item_actions.vue
@@ -17,14 +17,9 @@ export default {
methods: {
convertToTask() {
eventHub.$emit('convert-task-list-item', this.$el.closest('li').dataset.sourcepos);
- this.closeDropdown();
},
deleteTaskListItem() {
eventHub.$emit('delete-task-list-item', this.$el.closest('li').dataset.sourcepos);
- this.closeDropdown();
- },
- closeDropdown() {
- this.$refs.dropdown.close();
},
},
};
@@ -33,7 +28,6 @@ export default {
<template>
<gl-disclosure-dropdown
v-if="canUpdate"
- ref="dropdown"
class="task-list-item-actions-wrapper"
category="tertiary"
icon="ellipsis_v"
diff --git a/app/assets/javascripts/issues/show/components/title.vue b/app/assets/javascripts/issues/show/components/title.vue
index 2d2ef327018..c464f48d574 100644
--- a/app/assets/javascripts/issues/show/components/title.vue
+++ b/app/assets/javascripts/issues/show/components/title.vue
@@ -60,7 +60,6 @@ export default {
'issue-realtime-trigger-pulse': pulseAnimation,
}"
class="title gl-font-size-h-display"
- data-qa-selector="title_content"
data-testid="issue-title"
dir="auto"
></h1>
diff --git a/app/assets/javascripts/issues/show/index.js b/app/assets/javascripts/issues/show/index.js
index 5a51ac18446..bc4284457f6 100644
--- a/app/assets/javascripts/issues/show/index.js
+++ b/app/assets/javascripts/issues/show/index.js
@@ -133,6 +133,7 @@ export function initIssueApp(issueData, store) {
isLocked: this.getNoteableData?.discussion_locked,
issuableStatus: this.getNoteableData?.state,
issueId: this.getNoteableData?.id,
+ issueIid: this.getNoteableData?.iid,
},
});
},