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>2022-03-18 23:02:30 +0300
committerGitLab Bot <gitlab-bot@gitlab.com>2022-03-18 23:02:30 +0300
commit41fe97390ceddf945f3d967b8fdb3de4c66b7dea (patch)
tree9c8d89a8624828992f06d892cd2f43818ff5dcc8 /app/assets/javascripts/issues
parent0804d2dc31052fb45a1efecedc8e06ce9bc32862 (diff)
Add latest changes from gitlab-org/gitlab@14-9-stable-eev14.9.0-rc42
Diffstat (limited to 'app/assets/javascripts/issues')
-rw-r--r--app/assets/javascripts/issues/create_merge_request_dropdown.js12
-rw-r--r--app/assets/javascripts/issues/list/components/issues_list_app.vue20
-rw-r--r--app/assets/javascripts/issues/list/constants.js11
-rw-r--r--app/assets/javascripts/issues/list/queries/get_issues.query.graphql3
-rw-r--r--app/assets/javascripts/issues/list/queries/get_issues_counts.query.graphql7
-rw-r--r--app/assets/javascripts/issues/list/queries/search_users.query.graphql4
-rw-r--r--app/assets/javascripts/issues/list/utils.js11
-rw-r--r--app/assets/javascripts/issues/show/components/delete_issue_modal.vue5
-rw-r--r--app/assets/javascripts/issues/show/components/description.vue41
-rw-r--r--app/assets/javascripts/issues/show/components/fields/description_template.vue5
-rw-r--r--app/assets/javascripts/issues/show/components/header_actions.vue16
-rw-r--r--app/assets/javascripts/issues/show/components/incidents/incident_tabs.vue8
-rw-r--r--app/assets/javascripts/issues/show/components/title.vue4
-rw-r--r--app/assets/javascripts/issues/show/index.js4
14 files changed, 122 insertions, 29 deletions
diff --git a/app/assets/javascripts/issues/create_merge_request_dropdown.js b/app/assets/javascripts/issues/create_merge_request_dropdown.js
index a3752c7043c..247f8dd0bd6 100644
--- a/app/assets/javascripts/issues/create_merge_request_dropdown.js
+++ b/app/assets/javascripts/issues/create_merge_request_dropdown.js
@@ -10,6 +10,7 @@ import ISetter from '~/filtered_search/droplab/plugins/input_setter';
import createFlash from '~/flash';
import axios from '~/lib/utils/axios_utils';
import { __, sprintf } from '~/locale';
+import { mergeUrlParams } from '~/lib/utils/url_utility';
// Todo: Remove this when fixing issue in input_setter plugin
const InputSetter = { ...ISetter };
@@ -171,12 +172,21 @@ export default class CreateMergeRequestDropdown {
this.isCreatingMergeRequest = true;
return this.createBranch().then(() => {
- window.location.href = canCreateConfidentialMergeRequest()
+ let path = canCreateConfidentialMergeRequest()
? this.createMrPath.replace(
this.projectPath,
confidentialMergeRequestState.selectedProject.pathWithNamespace,
)
: this.createMrPath;
+ path = mergeUrlParams(
+ {
+ 'merge_request[target_branch]': this.refInput.value,
+ 'merge_request[source_branch]': this.branchInput.value,
+ },
+ path,
+ );
+
+ window.location.href = path;
});
});
}
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 3866a7b3305..a532fa5b771 100644
--- a/app/assets/javascripts/issues/list/components/issues_list_app.vue
+++ b/app/assets/javascripts/issues/list/components/issues_list_app.vue
@@ -39,8 +39,11 @@ import { IssuableListTabs, IssuableStates } from '~/vue_shared/issuable/list/con
import {
CREATED_DESC,
i18n,
+ ISSUE_REFERENCE,
MAX_LIST_SIZE,
PAGE_SIZE,
+ PARAM_PAGE_AFTER,
+ PARAM_PAGE_BEFORE,
PARAM_STATE,
RELATIVE_POSITION_ASC,
TOKEN_TYPE_ASSIGNEE,
@@ -134,6 +137,8 @@ export default {
},
},
data() {
+ const pageAfter = getParameterByName(PARAM_PAGE_AFTER);
+ const pageBefore = getParameterByName(PARAM_PAGE_BEFORE);
const state = getParameterByName(PARAM_STATE);
const defaultSortKey = state === IssuableStates.Closed ? UPDATED_DESC : CREATED_DESC;
const dashboardSortKey = getSortKey(this.initialSort);
@@ -165,7 +170,7 @@ export default {
issuesCounts: {},
issuesError: null,
pageInfo: {},
- pageParams: getInitialPageParams(sortKey),
+ pageParams: getInitialPageParams(sortKey, pageAfter, pageBefore),
showBulkEditSidebar: false,
sortKey,
state: state || IssuableStates.Opened,
@@ -219,11 +224,13 @@ export default {
},
computed: {
queryVariables() {
+ const isIidSearch = ISSUE_REFERENCE.test(this.searchQuery);
return {
fullPath: this.fullPath,
+ iid: isIidSearch ? this.searchQuery.slice(1) : undefined,
isProject: this.isProject,
isSignedIn: this.isSignedIn,
- search: this.searchQuery,
+ search: isIidSearch ? undefined : this.searchQuery,
sort: this.sortKey,
state: this.state,
...this.pageParams,
@@ -234,7 +241,12 @@ export default {
return this.isProject ? ITEM_TYPE.PROJECT : ITEM_TYPE.GROUP;
},
hasSearch() {
- return this.searchQuery || Object.keys(this.urlFilterParams).length;
+ return (
+ this.searchQuery ||
+ Object.keys(this.urlFilterParams).length ||
+ this.pageParams.afterCursor ||
+ this.pageParams.beforeCursor
+ );
},
isBulkEditButtonDisabled() {
return this.showBulkEditSidebar || !this.issues.length;
@@ -391,6 +403,8 @@ export default {
},
urlParams() {
return {
+ page_after: this.pageParams.afterCursor,
+ page_before: this.pageParams.beforeCursor,
search: this.searchQuery,
sort: urlSortParams[this.sortKey],
state: this.state,
diff --git a/app/assets/javascripts/issues/list/constants.js b/app/assets/javascripts/issues/list/constants.js
index 284167a933f..4b07a078512 100644
--- a/app/assets/javascripts/issues/list/constants.js
+++ b/app/assets/javascripts/issues/list/constants.js
@@ -52,20 +52,15 @@ export const i18n = {
upvotes: __('Upvotes'),
};
+export const ISSUE_REFERENCE = /^#\d+$/;
export const MAX_LIST_SIZE = 10;
export const PAGE_SIZE = 20;
export const PAGE_SIZE_MANUAL = 100;
+export const PARAM_PAGE_AFTER = 'page_after';
+export const PARAM_PAGE_BEFORE = 'page_before';
export const PARAM_STATE = 'state';
export const RELATIVE_POSITION = 'relative_position';
-export const defaultPageSizeParams = {
- firstPageSize: PAGE_SIZE,
-};
-
-export const largePageSizeParams = {
- firstPageSize: PAGE_SIZE_MANUAL,
-};
-
export const BLOCKING_ISSUES_ASC = 'BLOCKING_ISSUES_ASC';
export const BLOCKING_ISSUES_DESC = 'BLOCKING_ISSUES_DESC';
export const CREATED_ASC = 'CREATED_ASC';
diff --git a/app/assets/javascripts/issues/list/queries/get_issues.query.graphql b/app/assets/javascripts/issues/list/queries/get_issues.query.graphql
index be8deb3fe97..529262d2162 100644
--- a/app/assets/javascripts/issues/list/queries/get_issues.query.graphql
+++ b/app/assets/javascripts/issues/list/queries/get_issues.query.graphql
@@ -5,6 +5,7 @@ query getIssues(
$isProject: Boolean = false
$isSignedIn: Boolean = false
$fullPath: ID!
+ $iid: String
$search: String
$sort: IssueSort
$state: IssuableState
@@ -29,6 +30,7 @@ query getIssues(
id
issues(
includeSubgroups: true
+ iid: $iid
search: $search
sort: $sort
state: $state
@@ -59,6 +61,7 @@ query getIssues(
project(fullPath: $fullPath) @include(if: $isProject) {
id
issues(
+ iid: $iid
search: $search
sort: $sort
state: $state
diff --git a/app/assets/javascripts/issues/list/queries/get_issues_counts.query.graphql b/app/assets/javascripts/issues/list/queries/get_issues_counts.query.graphql
index 1a345fd2877..58e7ce32e7c 100644
--- a/app/assets/javascripts/issues/list/queries/get_issues_counts.query.graphql
+++ b/app/assets/javascripts/issues/list/queries/get_issues_counts.query.graphql
@@ -1,6 +1,7 @@
query getIssuesCount(
$isProject: Boolean = false
$fullPath: ID!
+ $iid: String
$search: String
$assigneeId: String
$assigneeUsernames: [String!]
@@ -20,6 +21,7 @@ query getIssuesCount(
openedIssues: issues(
includeSubgroups: true
state: opened
+ iid: $iid
search: $search
assigneeId: $assigneeId
assigneeUsernames: $assigneeUsernames
@@ -37,6 +39,7 @@ query getIssuesCount(
closedIssues: issues(
includeSubgroups: true
state: closed
+ iid: $iid
search: $search
assigneeId: $assigneeId
assigneeUsernames: $assigneeUsernames
@@ -54,6 +57,7 @@ query getIssuesCount(
allIssues: issues(
includeSubgroups: true
state: all
+ iid: $iid
search: $search
assigneeId: $assigneeId
assigneeUsernames: $assigneeUsernames
@@ -73,6 +77,7 @@ query getIssuesCount(
id
openedIssues: issues(
state: opened
+ iid: $iid
search: $search
assigneeId: $assigneeId
assigneeUsernames: $assigneeUsernames
@@ -91,6 +96,7 @@ query getIssuesCount(
}
closedIssues: issues(
state: closed
+ iid: $iid
search: $search
assigneeId: $assigneeId
assigneeUsernames: $assigneeUsernames
@@ -109,6 +115,7 @@ query getIssuesCount(
}
allIssues: issues(
state: all
+ iid: $iid
search: $search
assigneeId: $assigneeId
assigneeUsernames: $assigneeUsernames
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 92517ad35d0..46b48e4e41c 100644
--- a/app/assets/javascripts/issues/list/queries/search_users.query.graphql
+++ b/app/assets/javascripts/issues/list/queries/search_users.query.graphql
@@ -3,7 +3,7 @@
query searchUsers($fullPath: ID!, $search: String, $isProject: Boolean = false) {
group(fullPath: $fullPath) @skip(if: $isProject) {
id
- groupMembers(search: $search) {
+ groupMembers(search: $search, relations: [DIRECT, INHERITED, SHARED_FROM_GROUPS]) {
nodes {
id
user {
@@ -14,7 +14,7 @@ query searchUsers($fullPath: ID!, $search: String, $isProject: Boolean = false)
}
project(fullPath: $fullPath) @include(if: $isProject) {
id
- projectMembers(search: $search) {
+ projectMembers(search: $search, relations: [DIRECT, INHERITED, INVITED_GROUPS]) {
nodes {
id
user {
diff --git a/app/assets/javascripts/issues/list/utils.js b/app/assets/javascripts/issues/list/utils.js
index 6322968b3f0..4b77bd9bc5f 100644
--- a/app/assets/javascripts/issues/list/utils.js
+++ b/app/assets/javascripts/issues/list/utils.js
@@ -10,16 +10,16 @@ import {
BLOCKING_ISSUES_DESC,
CREATED_ASC,
CREATED_DESC,
- defaultPageSizeParams,
DUE_DATE_ASC,
DUE_DATE_DESC,
filters,
LABEL_PRIORITY_ASC,
LABEL_PRIORITY_DESC,
- largePageSizeParams,
MILESTONE_DUE_ASC,
MILESTONE_DUE_DESC,
NORMAL_FILTER,
+ PAGE_SIZE,
+ PAGE_SIZE_MANUAL,
POPULARITY_ASC,
POPULARITY_DESC,
PRIORITY_ASC,
@@ -43,8 +43,11 @@ import {
WEIGHT_DESC,
} from './constants';
-export const getInitialPageParams = (sortKey) =>
- sortKey === RELATIVE_POSITION_ASC ? largePageSizeParams : defaultPageSizeParams;
+export const getInitialPageParams = (sortKey, afterCursor, beforeCursor) => ({
+ firstPageSize: sortKey === RELATIVE_POSITION_ASC ? PAGE_SIZE_MANUAL : PAGE_SIZE,
+ afterCursor,
+ beforeCursor,
+});
export const getSortKey = (sort) =>
Object.keys(urlSortParams).find((key) => urlSortParams[key] === sort);
diff --git a/app/assets/javascripts/issues/show/components/delete_issue_modal.vue b/app/assets/javascripts/issues/show/components/delete_issue_modal.vue
index 26862346b86..47b09bd6aa0 100644
--- a/app/assets/javascripts/issues/show/components/delete_issue_modal.vue
+++ b/app/assets/javascripts/issues/show/components/delete_issue_modal.vue
@@ -31,7 +31,10 @@ export default {
computed: {
actionPrimary() {
return {
- attributes: { variant: 'danger' },
+ attributes: {
+ variant: 'danger',
+ 'data-qa-selector': 'confirm_delete_issue_button',
+ },
text: this.title,
};
},
diff --git a/app/assets/javascripts/issues/show/components/description.vue b/app/assets/javascripts/issues/show/components/description.vue
index eeccf886b65..68ed7bb4062 100644
--- a/app/assets/javascripts/issues/show/components/description.vue
+++ b/app/assets/javascripts/issues/show/components/description.vue
@@ -10,7 +10,9 @@ import $ from 'jquery';
import createFlash from '~/flash';
import { __, sprintf } from '~/locale';
import TaskList from '~/task_list';
+import Tracking from '~/tracking';
import glFeatureFlagMixin from '~/vue_shared/mixins/gl_feature_flags_mixin';
+import WorkItemDetailModal from '~/work_items/components/work_item_detail_modal.vue';
import CreateWorkItem from '~/work_items/pages/create_work_item.vue';
import animateMixin from '../mixins/animate';
@@ -24,8 +26,9 @@ export default {
GlPopover,
CreateWorkItem,
GlButton,
+ WorkItemDetailModal,
},
- mixins: [animateMixin, glFeatureFlagMixin()],
+ mixins: [animateMixin, glFeatureFlagMixin(), Tracking.mixin()],
props: {
canUpdate: {
type: Boolean,
@@ -68,9 +71,13 @@ export default {
initialUpdate: true,
taskButtons: [],
activeTask: {},
+ workItemId: null,
};
},
computed: {
+ showWorkItemDetailModal() {
+ return Boolean(this.workItemId);
+ },
workItemsEnabled() {
return this.glFeatures.workItems;
},
@@ -194,7 +201,13 @@ export default {
closeCreateTaskModal() {
this.$refs.modal.hide();
},
- handleCreateTask(title) {
+ closeWorkItemDetailModal() {
+ this.workItemId = null;
+ },
+ handleWorkItemDetailModalError(message) {
+ createFlash({ message });
+ },
+ handleCreateTask({ id, title, type }) {
const listItem = this.$el.querySelector(`#${this.activeTask.id}`).parentElement;
const taskBadge = document.createElement('span');
taskBadge.innerHTML = `
@@ -204,12 +217,28 @@ export default {
<span class="badge badge-info badge-pill gl-badge sm gl-mr-1">
${__('Task')}
</span>
- <a href="#">${title}</a>
`;
+ const button = this.createWorkItemDetailButton(id, title, type);
+ taskBadge.append(button);
+
listItem.insertBefore(taskBadge, listItem.lastChild);
listItem.removeChild(listItem.lastChild);
this.closeCreateTaskModal();
},
+ createWorkItemDetailButton(id, title, type) {
+ const button = document.createElement('button');
+ button.addEventListener('click', () => {
+ this.workItemId = id;
+ this.track('viewed_work_item_from_modal', {
+ category: 'workItems:show',
+ label: 'work_item_view',
+ property: `type_${type}`,
+ });
+ });
+ button.classList.add('btn-link');
+ button.innerText = title;
+ return button;
+ },
focusButton() {
this.$refs.convertButton[0].$el.focus();
},
@@ -262,6 +291,12 @@ export default {
@onCreate="handleCreateTask"
/>
</gl-modal>
+ <work-item-detail-modal
+ :visible="showWorkItemDetailModal"
+ :work-item-id="workItemId"
+ @close="closeWorkItemDetailModal"
+ @error="handleWorkItemDetailModalError"
+ />
<template v-if="workItemsEnabled">
<gl-popover
v-for="item in taskButtons"
diff --git a/app/assets/javascripts/issues/show/components/fields/description_template.vue b/app/assets/javascripts/issues/show/components/fields/description_template.vue
index 9ce49b65a1a..d528641dcb6 100644
--- a/app/assets/javascripts/issues/show/components/fields/description_template.vue
+++ b/app/assets/javascripts/issues/show/components/fields/description_template.vue
@@ -68,7 +68,10 @@ export default {
data-toggle="dropdown"
>
<span class="dropdown-toggle-text">{{ __('Choose a template') }}</span>
- <gl-icon name="chevron-down" class="gl-absolute gl-top-3 gl-right-3 gl-text-gray-500" />
+ <gl-icon
+ name="chevron-down"
+ class="gl-absolute gl-top-3 gl-right-3 gl-text-gray-500 dropdown-menu-toggle-icon"
+ />
</button>
<div class="dropdown-menu dropdown-select">
<div class="dropdown-title gl-display-flex gl-justify-content-center">
diff --git a/app/assets/javascripts/issues/show/components/header_actions.vue b/app/assets/javascripts/issues/show/components/header_actions.vue
index 8ba08472ea0..adf449aca7b 100644
--- a/app/assets/javascripts/issues/show/components/header_actions.vue
+++ b/app/assets/javascripts/issues/show/components/header_actions.vue
@@ -128,13 +128,21 @@ export default {
});
},
newIssueTypeText() {
- return sprintf(__('New %{issueType}'), { issueType: this.issueType });
+ return sprintf(__('New related %{issueType}'), { issueType: this.issueType });
},
showToggleIssueStateButton() {
const canClose = !this.isClosed && this.canUpdateIssue;
const canReopen = this.isClosed && this.canReopenIssue;
return canClose || canReopen;
},
+ hasDesktopDropdown() {
+ return (
+ this.canCreateIssue || this.canPromoteToEpic || !this.isIssueAuthor || this.canReportSpam
+ );
+ },
+ hasMobileDropdown() {
+ return this.hasDesktopDropdown || this.showToggleIssueStateButton;
+ },
},
created() {
eventHub.$on('toggle.issuable.state', this.toggleIssueState);
@@ -223,10 +231,12 @@ export default {
<template>
<div class="detail-page-header-actions gl-display-flex">
<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"
>
<gl-dropdown-item
@@ -276,11 +286,14 @@ export default {
</gl-button>
<gl-dropdown
+ v-if="hasDesktopDropdown"
class="gl-display-none gl-sm-display-inline-flex! gl-ml-3"
icon="ellipsis_v"
category="tertiary"
+ data-qa-selector="issue_actions_ellipsis_dropdown"
:text="dropdownText"
:text-sr-only="true"
+ data-testid="desktop-dropdown"
no-caret
right
>
@@ -311,6 +324,7 @@ export default {
<gl-dropdown-item
v-gl-modal="$options.deleteModalId"
variant="danger"
+ data-qa-selector="delete_issue_button"
@click="track('click_dropdown')"
>
{{ deleteButtonText }}
diff --git a/app/assets/javascripts/issues/show/components/incidents/incident_tabs.vue b/app/assets/javascripts/issues/show/components/incidents/incident_tabs.vue
index 4790062ab7d..04ddc7f3501 100644
--- a/app/assets/javascripts/issues/show/components/incidents/incident_tabs.vue
+++ b/app/assets/javascripts/issues/show/components/incidents/incident_tabs.vue
@@ -5,6 +5,7 @@ import { trackIncidentDetailsViewsOptions } from '~/incidents/constants';
import { s__ } from '~/locale';
import Tracking from '~/tracking';
import AlertDetailsTable from '~/vue_shared/components/alert_details_table.vue';
+import glFeatureFlagsMixin from '~/vue_shared/mixins/gl_feature_flags_mixin';
import DescriptionComponent from '../description.vue';
import getAlert from './graphql/queries/get_alert.graphql';
import HighlightBar from './highlight_bar.vue';
@@ -17,7 +18,10 @@ export default {
GlTabs,
HighlightBar,
MetricsTab: () => import('ee_component/issues/show/components/incidents/metrics_tab.vue'),
+ TimelineTab: () =>
+ import('ee_component/issues/show/components/incidents/timeline_events_tab.vue'),
},
+ mixins: [glFeatureFlagsMixin()],
inject: ['fullPath', 'iid', 'uploadMetricsFeatureAvailable'],
apollo: {
alert: {
@@ -47,6 +51,9 @@ export default {
loading() {
return this.$apollo.queries.alert.loading;
},
+ incidentTabEnabled() {
+ return this.glFeatures.incidentTimelineEvents && this.glFeatures.incidentTimelineEventTab;
+ },
},
mounted() {
this.trackPageViews();
@@ -76,6 +83,7 @@ export default {
>
<alert-details-table :alert="alert" :loading="loading" />
</gl-tab>
+ <timeline-tab v-if="incidentTabEnabled" data-testid="timeline-events-tab" />
</gl-tabs>
</div>
</template>
diff --git a/app/assets/javascripts/issues/show/components/title.vue b/app/assets/javascripts/issues/show/components/title.vue
index 5e92211685a..1982147e454 100644
--- a/app/assets/javascripts/issues/show/components/title.vue
+++ b/app/assets/javascripts/issues/show/components/title.vue
@@ -68,7 +68,7 @@ export default {
<template>
<div class="title-container">
- <h2
+ <h1
v-safe-html="titleHtml"
:class="{
'issue-realtime-pre-pulse': preAnimation,
@@ -76,7 +76,7 @@ export default {
}"
class="title qa-title"
dir="auto"
- ></h2>
+ ></h1>
<gl-button
v-if="showInlineEditButton && canUpdate"
v-gl-tooltip.bottom
diff --git a/app/assets/javascripts/issues/show/index.js b/app/assets/javascripts/issues/show/index.js
index f5c71f9691f..c9af5d9b4a7 100644
--- a/app/assets/javascripts/issues/show/index.js
+++ b/app/assets/javascripts/issues/show/index.js
@@ -77,9 +77,7 @@ export function initIssueApp(issueData, store) {
const { fullPath } = el.dataset;
- if (gon?.features?.fixCommentScroll) {
- scrollToTargetOnResize();
- }
+ scrollToTargetOnResize();
bootstrapApollo({ ...issueState, issueType: el.dataset.issueType });