diff options
author | GitLab Bot <gitlab-bot@gitlab.com> | 2023-10-04 15:17:55 +0300 |
---|---|---|
committer | GitLab Bot <gitlab-bot@gitlab.com> | 2023-10-04 15:17:55 +0300 |
commit | 49d36ce6e3484aea8131dd892a02f8304ee61aa1 (patch) | |
tree | ee7d8403c0ebacf5b5953ebbf1a838dd957eac6b /app/assets/javascripts | |
parent | b68afda3299d372ef0b3745e0603a9d51369650b (diff) |
Add latest changes from gitlab-org/gitlab@master
Diffstat (limited to 'app/assets/javascripts')
30 files changed, 213 insertions, 111 deletions
diff --git a/app/assets/javascripts/ci/job_details/components/log/line.vue b/app/assets/javascripts/ci/job_details/components/log/line.vue index eba8727c939..416f75372f9 100644 --- a/app/assets/javascripts/ci/job_details/components/log/line.vue +++ b/app/assets/javascripts/ci/job_details/components/log/line.vue @@ -56,7 +56,7 @@ export default { if (window.location.hash) { const hash = getLocationHash(); - const lineToMatch = `L${line.lineNumber + 1}`; + const lineToMatch = `L${line.lineNumber}`; if (hash === lineToMatch) { applyHashHighlight = true; diff --git a/app/assets/javascripts/ci/job_details/components/log/line_header.vue b/app/assets/javascripts/ci/job_details/components/log/line_header.vue index f2e9e09c334..658a94e6af4 100644 --- a/app/assets/javascripts/ci/job_details/components/log/line_header.vue +++ b/app/assets/javascripts/ci/job_details/components/log/line_header.vue @@ -46,7 +46,7 @@ export default { }, mounted() { const hash = getLocationHash(); - const lineToMatch = `L${this.line.lineNumber + 1}`; + const lineToMatch = `L${this.line.lineNumber}`; if (hash === lineToMatch) { this.applyHashHighlight = true; diff --git a/app/assets/javascripts/ci/job_details/components/log/line_number.vue b/app/assets/javascripts/ci/job_details/components/log/line_number.vue index 7ca9154d2fe..30b4c80f3fa 100644 --- a/app/assets/javascripts/ci/job_details/components/log/line_number.vue +++ b/app/assets/javascripts/ci/job_details/components/log/line_number.vue @@ -14,8 +14,7 @@ export default { render(h, { props }) { const { lineNumber, path } = props; - const parsedLineNumber = lineNumber + 1; - const lineId = `L${parsedLineNumber}`; + const lineId = `L${lineNumber}`; const lineHref = `${path}#${lineId}`; return h( @@ -27,7 +26,7 @@ export default { href: lineHref, }, }, - parsedLineNumber, + lineNumber, ); }, }; diff --git a/app/assets/javascripts/ci/job_details/store/utils.js b/app/assets/javascripts/ci/job_details/store/utils.js index ccd04d52e30..b18a3fa162d 100644 --- a/app/assets/javascripts/ci/job_details/store/utils.js +++ b/app/assets/javascripts/ci/job_details/store/utils.js @@ -19,20 +19,17 @@ export const parseLine = (line = {}, lineNumber) => ({ * @param Number lineNumber */ export const parseHeaderLine = (line = {}, lineNumber, hash) => { + let isClosed = parseBoolean(line.section_options?.collapsed); + // if a hash is present in the URL then we ensure // all sections are visible so we can scroll to the hash // in the DOM if (hash) { - return { - isClosed: false, - isHeader: true, - line: parseLine(line, lineNumber), - lines: [], - }; + isClosed = false; } return { - isClosed: parseBoolean(line.section_options?.collapsed), + isClosed, isHeader: true, line: parseLine(line, lineNumber), lines: [], @@ -80,27 +77,28 @@ export const isCollapsibleSection = (acc = [], last = {}, section = {}) => section.section === last.line.section; /** - * Returns the lineNumber of the last line in - * a parsed log + * Returns the next line number in the parsed log * * @param Array acc * @returns Number */ -export const getIncrementalLineNumber = (acc) => { - let lineNumberValue; - const lastIndex = acc.length - 1; - const lastElement = acc[lastIndex]; +export const getNextLineNumber = (acc) => { + if (!acc?.length) { + return 1; + } + + const lastElement = acc[acc.length - 1]; const nestedLines = lastElement.lines; if (lastElement.isHeader && !nestedLines.length && lastElement.line) { - lineNumberValue = lastElement.line.lineNumber; - } else if (lastElement.isHeader && nestedLines.length) { - lineNumberValue = nestedLines[nestedLines.length - 1].lineNumber; - } else { - lineNumberValue = lastElement.lineNumber; + return lastElement.line.lineNumber + 1; } - return lineNumberValue === 0 ? 1 : lineNumberValue + 1; + if (lastElement.isHeader && nestedLines.length) { + return nestedLines[nestedLines.length - 1].lineNumber + 1; + } + + return lastElement.lineNumber + 1; }; /** @@ -119,31 +117,28 @@ export const getIncrementalLineNumber = (acc) => { * @returns Array parsed log lines */ export const logLinesParser = (lines = [], prevLogLines = [], hash = '') => - lines.reduce( - (acc, line, index) => { - const lineNumber = acc.length > 0 ? getIncrementalLineNumber(acc) : index; - - const last = acc[acc.length - 1]; - - // If the object is an header, we parse it into another structure - if (line.section_header) { - acc.push(parseHeaderLine(line, lineNumber, hash)); - } else if (isCollapsibleSection(acc, last, line)) { - // if the object belongs to a nested section, we append it to the new `lines` array of the - // previously formatted header - last.lines.push(parseLine(line, lineNumber)); - } else if (line.section_duration) { - // if the line has section_duration, we look for the correct header to add it - addDurationToHeader(acc, line); - } else { - // otherwise it's a regular line - acc.push(parseLine(line, lineNumber)); - } + lines.reduce((acc, line) => { + const lineNumber = getNextLineNumber(acc); + + const last = acc[acc.length - 1]; + + // If the object is an header, we parse it into another structure + if (line.section_header) { + acc.push(parseHeaderLine(line, lineNumber, hash)); + } else if (isCollapsibleSection(acc, last, line)) { + // if the object belongs to a nested section, we append it to the new `lines` array of the + // previously formatted header + last.lines.push(parseLine(line, lineNumber)); + } else if (line.section_duration) { + // if the line has section_duration, we look for the correct header to add it + addDurationToHeader(acc, line); + } else { + // otherwise it's a regular line + acc.push(parseLine(line, lineNumber)); + } - return acc; - }, - [...prevLogLines], - ); + return acc; + }, prevLogLines); /** * Finds the repeated offset, removes the old one diff --git a/app/assets/javascripts/issues/show/components/description.vue b/app/assets/javascripts/issues/show/components/description.vue index acbba216601..5735a518a3e 100644 --- a/app/assets/javascripts/issues/show/components/description.vue +++ b/app/assets/javascripts/issues/show/components/description.vue @@ -362,7 +362,12 @@ export default { }, }, update: (cache, { data: { workItemCreate } }) => - addHierarchyChild(cache, this.fullPath, String(this.issueIid), workItemCreate.workItem), + addHierarchyChild({ + cache, + fullPath: this.fullPath, + iid: String(this.issueIid), + workItem: workItemCreate.workItem, + }), }); const { workItem, errors } = data.workItemCreate; @@ -392,7 +397,12 @@ export default { mutation: deleteWorkItemMutation, variables: { input: { id } }, update: (cache) => - removeHierarchyChild(cache, this.fullPath, String(this.issueIid), { id }), + removeHierarchyChild({ + cache, + fullPath: this.fullPath, + iid: String(this.issueIid), + workItem: { id }, + }), }); if (data.workItemDelete.errors?.length) { diff --git a/app/assets/javascripts/jira_connect/subscriptions/constants.js b/app/assets/javascripts/jira_connect/subscriptions/constants.js index 72fd25a6230..1a10360ed30 100644 --- a/app/assets/javascripts/jira_connect/subscriptions/constants.js +++ b/app/assets/javascripts/jira_connect/subscriptions/constants.js @@ -37,11 +37,11 @@ export const I18N_OAUTH_FAILED_MESSAGE = s__( export const INTEGRATIONS_DOC_LINK = helpPagePath('integration/jira/development_panel', { anchor: 'use-the-integration', }); -export const OAUTH_SELF_MANAGED_DOC_LINK = helpPagePath('integration/jira/connect-app', { - anchor: 'connect-the-gitlab-for-jira-cloud-app-for-self-managed-instances', +export const OAUTH_SELF_MANAGED_DOC_LINK = helpPagePath('administration/settings/jira_cloud_app', { + anchor: 'set-up-oauth-authentication', }); -export const FAILED_TO_UPDATE_DOC_LINK = helpPagePath('integration/jira/connect-app', { - anchor: 'failed-to-update-the-gitlab-instance-for-self-managed-instances', +export const FAILED_TO_UPDATE_DOC_LINK = helpPagePath('administration/settings/jira_cloud_app', { + anchor: 'failed-to-update-the-gitlab-instance', }); export const GITLAB_COM_BASE_PATH = 'https://gitlab.com'; diff --git a/app/assets/javascripts/members/components/action_dropdowns/remove_member_dropdown_item.vue b/app/assets/javascripts/members/components/action_dropdowns/remove_member_dropdown_item.vue index 920febb0e67..68bfb99a139 100644 --- a/app/assets/javascripts/members/components/action_dropdowns/remove_member_dropdown_item.vue +++ b/app/assets/javascripts/members/components/action_dropdowns/remove_member_dropdown_item.vue @@ -77,7 +77,7 @@ export default { <template> <gl-disclosure-dropdown-item - data-qa-selector="delete_member_dropdown_item" + data-testid="delete-member-dropdown-item" @action="showRemoveMemberModal(modalData)" > <template #list-item> diff --git a/app/assets/javascripts/members/components/action_dropdowns/user_action_dropdown.vue b/app/assets/javascripts/members/components/action_dropdowns/user_action_dropdown.vue index 25dc4831b11..a8c97060915 100644 --- a/app/assets/javascripts/members/components/action_dropdowns/user_action_dropdown.vue +++ b/app/assets/javascripts/members/components/action_dropdowns/user_action_dropdown.vue @@ -109,7 +109,6 @@ export default { no-caret placement="right" data-testid="user-action-dropdown" - data-qa-selector="user_action_dropdown" > <disable-two-factor-dropdown-item v-if="permissions.canDisableTwoFactor" diff --git a/app/assets/javascripts/pages/groups/work_items/show/index.js b/app/assets/javascripts/pages/groups/work_items/show/index.js new file mode 100644 index 00000000000..c091fbcc2b2 --- /dev/null +++ b/app/assets/javascripts/pages/groups/work_items/show/index.js @@ -0,0 +1,4 @@ +import { WORKSPACE_GROUP } from '~/issues/constants'; +import { initWorkItemsRoot } from '~/work_items'; + +initWorkItemsRoot(WORKSPACE_GROUP); diff --git a/app/assets/javascripts/pages/projects/work_items/index.js b/app/assets/javascripts/pages/projects/work_items/index.js index 11c257611f0..b44ca708b28 100644 --- a/app/assets/javascripts/pages/projects/work_items/index.js +++ b/app/assets/javascripts/pages/projects/work_items/index.js @@ -1,3 +1,3 @@ -import { initWorkItemsRoot } from '~/work_items/index'; +import { initWorkItemsRoot } from '~/work_items'; initWorkItemsRoot(); diff --git a/app/assets/javascripts/repository/components/table/index.vue b/app/assets/javascripts/repository/components/table/index.vue index 557e9cd168f..3da7daa3eec 100644 --- a/app/assets/javascripts/repository/components/table/index.vue +++ b/app/assets/javascripts/repository/components/table/index.vue @@ -118,7 +118,7 @@ export default { class="table tree-table" :class="{ 'gl-table-layout-fixed': !showParentRow }" aria-live="polite" - data-qa-selector="file_tree_table" + data-testid="file-tree-table" > <table-header v-once /> <tbody> diff --git a/app/assets/javascripts/repository/components/table/row.vue b/app/assets/javascripts/repository/components/table/row.vue index a76d822317a..526757e6147 100644 --- a/app/assets/javascripts/repository/components/table/row.vue +++ b/app/assets/javascripts/repository/components/table/row.vue @@ -219,7 +219,7 @@ export default { 'is-submodule': isSubmodule, }" class="tree-item-link str-truncated" - data-qa-selector="file_name_link" + data-testid="file-name-link" > <file-icon :file-name="fullPath" diff --git a/app/assets/javascripts/super_sidebar/components/user_name_group.vue b/app/assets/javascripts/super_sidebar/components/user_name_group.vue index 3c8059387fa..c9971639e74 100644 --- a/app/assets/javascripts/super_sidebar/components/user_name_group.vue +++ b/app/assets/javascripts/super_sidebar/components/user_name_group.vue @@ -41,7 +41,7 @@ export default { item.extraAttrs = { ...USER_MENU_TRACKING_DEFAULTS, 'data-track-label': 'user_profile', - 'data-testid': 'user_profile_link', + 'data-testid': 'user-profile-link', }; } diff --git a/app/assets/javascripts/work_items/components/notes/work_item_add_note.vue b/app/assets/javascripts/work_items/components/notes/work_item_add_note.vue index a17fa691404..f39a12bb6f9 100644 --- a/app/assets/javascripts/work_items/components/notes/work_item_add_note.vue +++ b/app/assets/javascripts/work_items/components/notes/work_item_add_note.vue @@ -5,6 +5,7 @@ import { ASC } from '~/notes/constants'; import { __ } from '~/locale'; import { clearDraft } from '~/lib/utils/autosave'; import createNoteMutation from '../../graphql/notes/create_work_item_note.mutation.graphql'; +import groupWorkItemByIidQuery from '../../graphql/group_work_item_by_iid.query.graphql'; import workItemByIidQuery from '../../graphql/work_item_by_iid.query.graphql'; import { TRACKING_CATEGORY_SHOW, i18n } from '../../constants'; import WorkItemNoteSignedOut from './work_item_note_signed_out.vue'; @@ -21,7 +22,7 @@ export default { WorkItemCommentForm, }, mixins: [Tracking.mixin()], - inject: ['fullPath'], + inject: ['fullPath', 'isGroup'], props: { workItemId: { type: String, @@ -90,7 +91,9 @@ export default { }, apollo: { workItem: { - query: workItemByIidQuery, + query() { + return this.isGroup ? groupWorkItemByIidQuery : workItemByIidQuery; + }, variables() { return { fullPath: this.fullPath, diff --git a/app/assets/javascripts/work_items/components/notes/work_item_note.vue b/app/assets/javascripts/work_items/components/notes/work_item_note.vue index b5e3ea68725..0c1dac60bba 100644 --- a/app/assets/javascripts/work_items/components/notes/work_item_note.vue +++ b/app/assets/javascripts/work_items/components/notes/work_item_note.vue @@ -3,7 +3,6 @@ import { GlAvatarLink, GlAvatar } from '@gitlab/ui'; import * as Sentry from '@sentry/browser'; import toast from '~/vue_shared/plugins/global_toast'; import { __ } from '~/locale'; -import { i18n, TRACKING_CATEGORY_SHOW } from '~/work_items/constants'; import Tracking from '~/tracking'; import { updateDraft, clearDraft } from '~/lib/utils/autosave'; import { renderMarkdown } from '~/notes/utils'; @@ -11,15 +10,17 @@ import { getLocationHash } from '~/lib/utils/url_utility'; import { getIdFromGraphQLId } from '~/graphql_shared/utils'; import EditedAt from '~/issues/show/components/edited.vue'; import TimelineEntryItem from '~/vue_shared/components/notes/timeline_entry_item.vue'; -import NoteBody from '~/work_items/components/notes/work_item_note_body.vue'; import NoteHeader from '~/notes/components/note_header.vue'; -import NoteActions from '~/work_items/components/notes/work_item_note_actions.vue'; -import updateWorkItemMutation from '~/work_items/graphql/update_work_item.mutation.graphql'; +import { i18n, TRACKING_CATEGORY_SHOW } from '../../constants'; +import groupWorkItemByIidQuery from '../../graphql/group_work_item_by_iid.query.graphql'; +import updateWorkItemMutation from '../../graphql/update_work_item.mutation.graphql'; import updateWorkItemNoteMutation from '../../graphql/notes/update_work_item_note.mutation.graphql'; import workItemByIidQuery from '../../graphql/work_item_by_iid.query.graphql'; import { isAssigneesWidget } from '../../utils'; import WorkItemCommentForm from './work_item_comment_form.vue'; +import NoteActions from './work_item_note_actions.vue'; import WorkItemNoteAwardsList from './work_item_note_awards_list.vue'; +import NoteBody from './work_item_note_body.vue'; export default { name: 'WorkItemNoteThread', @@ -35,7 +36,7 @@ export default { EditedAt, }, mixins: [Tracking.mixin()], - inject: ['fullPath'], + inject: ['fullPath', 'isGroup'], props: { workItemId: { type: String, @@ -169,7 +170,9 @@ export default { }, apollo: { workItem: { - query: workItemByIidQuery, + query() { + return this.isGroup ? groupWorkItemByIidQuery : workItemByIidQuery; + }, variables() { return { fullPath: this.fullPath, diff --git a/app/assets/javascripts/work_items/components/work_item_actions.vue b/app/assets/javascripts/work_items/components/work_item_actions.vue index 18aa4d55086..b19236e5961 100644 --- a/app/assets/javascripts/work_items/components/work_item_actions.vue +++ b/app/assets/javascripts/work_items/components/work_item_actions.vue @@ -15,7 +15,8 @@ import { __, s__ } from '~/locale'; import Tracking from '~/tracking'; import toast from '~/vue_shared/plugins/global_toast'; import { isLoggedIn } from '~/lib/utils/common_utils'; -import workItemByIidQuery from '~/work_items/graphql/work_item_by_iid.query.graphql'; +import groupWorkItemByIidQuery from '../graphql/group_work_item_by_iid.query.graphql'; +import workItemByIidQuery from '../graphql/work_item_by_iid.query.graphql'; import { sprintfWorkItem, @@ -70,7 +71,7 @@ export default { copyCreateNoteEmailTestId: TEST_ID_COPY_CREATE_NOTE_EMAIL_ACTION, deleteActionTestId: TEST_ID_DELETE_ACTION, promoteActionTestId: TEST_ID_PROMOTE_ACTION, - inject: ['fullPath'], + inject: ['fullPath', 'isGroup'], props: { workItemId: { type: String, @@ -256,7 +257,7 @@ export default { }, updateWorkItemNotificationsWidgetCache({ cache, issue }) { const query = { - query: workItemByIidQuery, + query: this.isGroup ? groupWorkItemByIidQuery : workItemByIidQuery, variables: { fullPath: this.fullPath, iid: this.workItemIid }, }; // Read the work item object diff --git a/app/assets/javascripts/work_items/components/work_item_created_updated.vue b/app/assets/javascripts/work_items/components/work_item_created_updated.vue index 14e55134048..ac1496cbc37 100644 --- a/app/assets/javascripts/work_items/components/work_item_created_updated.vue +++ b/app/assets/javascripts/work_items/components/work_item_created_updated.vue @@ -3,10 +3,11 @@ import { GlAvatarLink, GlSprintf, GlLoadingIcon } from '@gitlab/ui'; import { getIdFromGraphQLId } from '~/graphql_shared/utils'; import { WORKSPACE_PROJECT } from '~/issues/constants'; import TimeAgoTooltip from '~/vue_shared/components/time_ago_tooltip.vue'; -import WorkItemStateBadge from '~/work_items/components/work_item_state_badge.vue'; import ConfidentialityBadge from '~/vue_shared/components/confidentiality_badge.vue'; -import WorkItemTypeIcon from '~/work_items/components/work_item_type_icon.vue'; +import groupWorkItemByIidQuery from '../graphql/group_work_item_by_iid.query.graphql'; import workItemByIidQuery from '../graphql/work_item_by_iid.query.graphql'; +import WorkItemStateBadge from './work_item_state_badge.vue'; +import WorkItemTypeIcon from './work_item_type_icon.vue'; export default { components: { @@ -18,7 +19,7 @@ export default { ConfidentialityBadge, GlLoadingIcon, }, - inject: ['fullPath'], + inject: ['fullPath', 'isGroup'], props: { workItemIid: { type: String, @@ -59,7 +60,9 @@ export default { }, apollo: { workItem: { - query: workItemByIidQuery, + query() { + return this.isGroup ? groupWorkItemByIidQuery : workItemByIidQuery; + }, variables() { return { fullPath: this.fullPath, diff --git a/app/assets/javascripts/work_items/components/work_item_description.vue b/app/assets/javascripts/work_items/components/work_item_description.vue index 58bf524f450..b1596183f6c 100644 --- a/app/assets/javascripts/work_items/components/work_item_description.vue +++ b/app/assets/javascripts/work_items/components/work_item_description.vue @@ -10,6 +10,7 @@ import Tracking from '~/tracking'; import MarkdownEditor from '~/vue_shared/components/markdown/markdown_editor.vue'; import { autocompleteDataSources, markdownPreviewPath } from '../utils'; import updateWorkItemMutation from '../graphql/update_work_item.mutation.graphql'; +import groupWorkItemByIidQuery from '../graphql/group_work_item_by_iid.query.graphql'; import workItemByIidQuery from '../graphql/work_item_by_iid.query.graphql'; import { i18n, TRACKING_CATEGORY_SHOW, WIDGET_TYPE_DESCRIPTION } from '../constants'; import WorkItemDescriptionRendered from './work_item_description_rendered.vue'; @@ -25,7 +26,7 @@ export default { WorkItemDescriptionRendered, }, mixins: [Tracking.mixin()], - inject: ['fullPath'], + inject: ['fullPath', 'isGroup'], props: { workItemId: { type: String, @@ -55,7 +56,9 @@ export default { }, apollo: { workItem: { - query: workItemByIidQuery, + query() { + return this.isGroup ? groupWorkItemByIidQuery : workItemByIidQuery; + }, variables() { return { fullPath: this.fullPath, diff --git a/app/assets/javascripts/work_items/components/work_item_detail.vue b/app/assets/javascripts/work_items/components/work_item_detail.vue index 29fa60926c3..ff1b45c4b9e 100644 --- a/app/assets/javascripts/work_items/components/work_item_detail.vue +++ b/app/assets/javascripts/work_items/components/work_item_detail.vue @@ -16,7 +16,6 @@ import { getParameterByName, updateHistory, setUrlParams } from '~/lib/utils/url import glFeatureFlagMixin from '~/vue_shared/mixins/gl_feature_flags_mixin'; import { getIdFromGraphQLId } from '~/graphql_shared/utils'; import { isLoggedIn } from '~/lib/utils/common_utils'; -import WorkItemTypeIcon from '~/work_items/components/work_item_type_icon.vue'; import AbuseCategorySelector from '~/abuse_reports/components/abuse_category_selector.vue'; import ConfidentialityBadge from '~/vue_shared/components/confidentiality_badge.vue'; import { WORKSPACE_PROJECT } from '~/issues/constants'; @@ -37,6 +36,7 @@ import { import workItemUpdatedSubscription from '../graphql/work_item_updated.subscription.graphql'; import updateWorkItemMutation from '../graphql/update_work_item.mutation.graphql'; import updateWorkItemTaskMutation from '../graphql/update_work_item_task.mutation.graphql'; +import groupWorkItemByIidQuery from '../graphql/group_work_item_by_iid.query.graphql'; import workItemByIidQuery from '../graphql/work_item_by_iid.query.graphql'; import { findHierarchyWidgetChildren } from '../utils'; @@ -52,6 +52,7 @@ import WorkItemDetailModal from './work_item_detail_modal.vue'; import WorkItemAwardEmoji from './work_item_award_emoji.vue'; import WorkItemStateToggleButton from './work_item_state_toggle_button.vue'; import WorkItemRelationships from './work_item_relationships/work_item_relationships.vue'; +import WorkItemTypeIcon from './work_item_type_icon.vue'; export default { i18n, @@ -84,7 +85,7 @@ export default { WorkItemRelationships, }, mixins: [glFeatureFlagMixin()], - inject: ['fullPath', 'reportAbusePath'], + inject: ['fullPath', 'isGroup', 'reportAbusePath'], props: { isModal: { type: Boolean, @@ -118,7 +119,9 @@ export default { }, apollo: { workItem: { - query: workItemByIidQuery, + query() { + return this.isGroup ? groupWorkItemByIidQuery : workItemByIidQuery; + }, variables() { return { fullPath: this.fullPath, @@ -189,8 +192,8 @@ export default { canAssignUnassignUser() { return this.workItemAssignees && this.canSetWorkItemMetadata; }, - fullPath() { - return this.workItem?.project.fullPath; + projectFullPath() { + return this.workItem?.project?.fullPath; }, workItemsMvc2Enabled() { return this.glFeatures.workItemsMvc2; @@ -460,7 +463,7 @@ export default { v-if="showWorkItemCurrentUserTodos" :work-item-id="workItem.id" :work-item-iid="workItemIid" - :work-item-fullpath="workItem.project.fullPath" + :work-item-fullpath="projectFullPath" :current-user-todos="currentUserTodos" @error="updateError = $event" /> @@ -535,7 +538,7 @@ export default { v-if="showWorkItemCurrentUserTodos" :work-item-id="workItem.id" :work-item-iid="workItemIid" - :work-item-fullpath="workItem.project.fullPath" + :work-item-fullpath="projectFullPath" :current-user-todos="currentUserTodos" @error="updateError = $event" /> @@ -585,7 +588,7 @@ export default { <work-item-award-emoji v-if="workItemAwardEmoji" :work-item-id="workItem.id" - :work-item-fullpath="workItem.project.fullPath" + :work-item-fullpath="projectFullPath" :award-emoji="workItemAwardEmoji.awardEmoji" :work-item-iid="workItemIid" @error="updateError = $event" @@ -607,7 +610,7 @@ export default { v-if="showWorkItemLinkedItems" :work-item-id="workItem.id" :work-item-iid="workItemIid" - :work-item-full-path="workItem.project.fullPath" + :work-item-full-path="projectFullPath" :work-item-type="workItem.workItemType.name" @showModal="openInModal" /> diff --git a/app/assets/javascripts/work_items/components/work_item_labels.vue b/app/assets/javascripts/work_items/components/work_item_labels.vue index 1405a12a101..c42186d1c27 100644 --- a/app/assets/javascripts/work_items/components/work_item_labels.vue +++ b/app/assets/javascripts/work_items/components/work_item_labels.vue @@ -8,6 +8,7 @@ import LabelItem from '~/sidebar/components/labels/labels_select_widget/label_it import { DEFAULT_DEBOUNCE_AND_THROTTLE_MS } from '~/lib/utils/constants'; import { isScopedLabel } from '~/lib/utils/common_utils'; import updateWorkItemMutation from '../graphql/update_work_item.mutation.graphql'; +import groupWorkItemByIidQuery from '../graphql/group_work_item_by_iid.query.graphql'; import workItemByIidQuery from '../graphql/work_item_by_iid.query.graphql'; import { i18n, I18N_WORK_ITEM_ERROR_FETCHING_LABELS, TRACKING_CATEGORY_SHOW } from '../constants'; import { isLabelsWidget } from '../utils'; @@ -37,7 +38,7 @@ export default { LabelItem, }, mixins: [Tracking.mixin()], - inject: ['fullPath'], + inject: ['fullPath', 'isGroup'], props: { workItemId: { type: String, @@ -65,7 +66,9 @@ export default { }, apollo: { workItem: { - query: workItemByIidQuery, + query() { + return this.isGroup ? groupWorkItemByIidQuery : workItemByIidQuery; + }, variables() { return { fullPath: this.fullPath, diff --git a/app/assets/javascripts/work_items/components/work_item_links/work_item_children_wrapper.vue b/app/assets/javascripts/work_items/components/work_item_links/work_item_children_wrapper.vue index 9d9414b5399..b74afbde44a 100644 --- a/app/assets/javascripts/work_items/components/work_item_links/work_item_children_wrapper.vue +++ b/app/assets/javascripts/work_items/components/work_item_links/work_item_children_wrapper.vue @@ -13,6 +13,7 @@ import { findHierarchyWidgets } from '../../utils'; import { addHierarchyChild, removeHierarchyChild } from '../../graphql/cache_utils'; import reorderWorkItem from '../../graphql/reorder_work_item.mutation.graphql'; import updateWorkItemMutation from '../../graphql/update_work_item.mutation.graphql'; +import groupWorkItemByIidQuery from '../../graphql/group_work_item_by_iid.query.graphql'; import workItemByIidQuery from '../../graphql/work_item_by_iid.query.graphql'; import WorkItemLinkChild from './work_item_link_child.vue'; @@ -20,7 +21,7 @@ export default { components: { WorkItemLinkChild, }, - inject: ['fullPath'], + inject: ['fullPath', 'isGroup'], props: { workItemType: { type: String, @@ -83,7 +84,14 @@ export default { const { data } = await this.$apollo.mutate({ mutation: updateWorkItemMutation, variables: { input: { id: child.id, hierarchyWidget: { parentId: null } } }, - update: (cache) => removeHierarchyChild(cache, this.fullPath, this.workItemIid, child), + update: (cache) => + removeHierarchyChild({ + cache, + fullPath: this.fullPath, + iid: this.workItemIid, + isGroup: this.isGroup, + workItem: child, + }), }); if (data.workItemUpdate.errors.length) { @@ -109,7 +117,14 @@ export default { const { data } = await this.$apollo.mutate({ mutation: updateWorkItemMutation, variables: { input: { id: child.id, hierarchyWidget: { parentId: this.workItemId } } }, - update: (cache) => addHierarchyChild(cache, this.fullPath, this.workItemIid, child), + update: (cache) => + addHierarchyChild({ + cache, + fullPath: this.fullPath, + iid: this.workItemIid, + isGroup: this.isGroup, + workItem: child, + }), }); if (data.workItemUpdate.errors.length) { @@ -124,7 +139,7 @@ export default { }, addWorkItemQuery({ iid }) { this.$apollo.addSmartQuery('prefetchedWorkItem', { - query: workItemByIidQuery, + query: this.isGroup ? groupWorkItemByIidQuery : workItemByIidQuery, variables: { fullPath: this.fullPath, iid, @@ -206,7 +221,7 @@ export default { update: (store) => { store.updateQuery( { - query: workItemByIidQuery, + query: this.isGroup ? groupWorkItemByIidQuery : workItemByIidQuery, variables: { fullPath: this.fullPath, iid: this.workItemIid }, }, (sourceData) => diff --git a/app/assets/javascripts/work_items/components/work_item_links/work_item_links.vue b/app/assets/javascripts/work_items/components/work_item_links/work_item_links.vue index eb836007e75..c7dd7c5023d 100644 --- a/app/assets/javascripts/work_items/components/work_item_links/work_item_links.vue +++ b/app/assets/javascripts/work_items/components/work_item_links/work_item_links.vue @@ -18,6 +18,7 @@ import AbuseCategorySelector from '~/abuse_reports/components/abuse_category_sel import { FORM_TYPES, WIDGET_ICONS, WORK_ITEM_STATUS_TEXT } from '../../constants'; import { findHierarchyWidgetChildren } from '../../utils'; import { removeHierarchyChild } from '../../graphql/cache_utils'; +import groupWorkItemByIidQuery from '../../graphql/group_work_item_by_iid.query.graphql'; import workItemByIidQuery from '../../graphql/work_item_by_iid.query.graphql'; import WidgetWrapper from '../widget_wrapper.vue'; import WorkItemDetailModal from '../work_item_detail_modal.vue'; @@ -39,7 +40,7 @@ export default { directives: { GlTooltip: GlTooltipDirective, }, - inject: ['fullPath', 'reportAbusePath'], + inject: ['fullPath', 'isGroup', 'reportAbusePath'], props: { issuableId: { type: Number, @@ -52,7 +53,9 @@ export default { }, apollo: { workItem: { - query: workItemByIidQuery, + query() { + return this.isGroup ? groupWorkItemByIidQuery : workItemByIidQuery; + }, variables() { return { fullPath: this.fullPath, @@ -171,7 +174,13 @@ export default { }, handleWorkItemDeleted(child) { const { defaultClient: cache } = this.$apollo.provider.clients; - removeHierarchyChild(cache, this.fullPath, this.iid, child); + removeHierarchyChild({ + cache, + fullPath: this.fullPath, + iid: this.iid, + isGroup: this.isGroup, + workItem: child, + }); this.$toast.show(s__('WorkItem|Task deleted')); }, updateWorkItemIdUrlQuery({ iid } = {}) { diff --git a/app/assets/javascripts/work_items/components/work_item_links/work_item_links_form.vue b/app/assets/javascripts/work_items/components/work_item_links/work_item_links_form.vue index 456dee8dab1..2e5ba42c2e1 100644 --- a/app/assets/javascripts/work_items/components/work_item_links/work_item_links_form.vue +++ b/app/assets/javascripts/work_items/components/work_item_links/work_item_links_form.vue @@ -37,7 +37,7 @@ export default { GlTooltip, WorkItemTokenInput, }, - inject: ['fullPath', 'hasIterationsFeature'], + inject: ['fullPath', 'hasIterationsFeature', 'isGroup'], props: { issuableGid: { type: String, @@ -260,7 +260,13 @@ export default { input: this.workItemInput, }, update: (cache, { data }) => - addHierarchyChild(cache, this.fullPath, this.workItemIid, data.workItemCreate.workItem), + addHierarchyChild({ + cache, + fullPath: this.fullPath, + iid: this.workItemIid, + isGroup: this.isGroup, + workItem: data.workItemCreate.workItem, + }), }) .then(({ data }) => { if (data.workItemCreate?.errors?.length) { diff --git a/app/assets/javascripts/work_items/components/work_item_relationships/work_item_relationships.vue b/app/assets/javascripts/work_items/components/work_item_relationships/work_item_relationships.vue index d32e3d3a5e5..6f02ea507e5 100644 --- a/app/assets/javascripts/work_items/components/work_item_relationships/work_item_relationships.vue +++ b/app/assets/javascripts/work_items/components/work_item_relationships/work_item_relationships.vue @@ -3,6 +3,7 @@ import { GlLoadingIcon, GlIcon, GlButton } from '@gitlab/ui'; import { s__ } from '~/locale'; +import groupWorkItemByIidQuery from '../../graphql/group_work_item_by_iid.query.graphql'; import workItemByIidQuery from '../../graphql/work_item_by_iid.query.graphql'; import { WIDGET_TYPE_LINKED_ITEMS, LINKED_CATEGORIES_MAP } from '../../constants'; @@ -19,6 +20,7 @@ export default { WorkItemRelationshipList, WorkItemAddRelationshipForm, }, + inject: ['isGroup'], props: { workItemId: { type: String, @@ -41,7 +43,9 @@ export default { }, apollo: { workItem: { - query: workItemByIidQuery, + query() { + return this.isGroup ? groupWorkItemByIidQuery : workItemByIidQuery; + }, variables() { return { fullPath: this.workItemFullPath, diff --git a/app/assets/javascripts/work_items/components/work_item_todos.vue b/app/assets/javascripts/work_items/components/work_item_todos.vue index b21abf21be5..e6d7f2067ba 100644 --- a/app/assets/javascripts/work_items/components/work_item_todos.vue +++ b/app/assets/javascripts/work_items/components/work_item_todos.vue @@ -4,9 +4,10 @@ import { produce } from 'immer'; import { s__ } from '~/locale'; import { updateGlobalTodoCount } from '~/sidebar/utils'; -import workItemByIidQuery from '~/work_items/graphql/work_item_by_iid.query.graphql'; -import createWorkItemTodosMutation from '~/work_items/graphql/create_work_item_todos.mutation.graphql'; -import markDoneWorkItemTodosMutation from '~/work_items/graphql/mark_done_work_item_todos.mutation.graphql'; +import groupWorkItemByIidQuery from '../graphql/group_work_item_by_iid.query.graphql'; +import workItemByIidQuery from '../graphql/work_item_by_iid.query.graphql'; +import createWorkItemTodosMutation from '../graphql/create_work_item_todos.mutation.graphql'; +import markDoneWorkItemTodosMutation from '../graphql/mark_done_work_item_todos.mutation.graphql'; import { TODO_ADD_ICON, @@ -28,6 +29,7 @@ export default { GlIcon, GlButton, }, + inject: ['isGroup'], props: { workItemId: { type: String, @@ -148,7 +150,7 @@ export default { }, updateWorkItemCurrentTodosWidgetCache({ cache, todos }) { const query = { - query: workItemByIidQuery, + query: this.isGroup ? groupWorkItemByIidQuery : workItemByIidQuery, variables: { fullPath: this.workItemFullpath, iid: this.workItemIid }, }; diff --git a/app/assets/javascripts/work_items/components/work_item_type_icon.vue b/app/assets/javascripts/work_items/components/work_item_type_icon.vue index 5426f3965b3..76a73093206 100644 --- a/app/assets/javascripts/work_items/components/work_item_type_icon.vue +++ b/app/assets/javascripts/work_items/components/work_item_type_icon.vue @@ -36,6 +36,11 @@ export default { return this.workItemType.toUpperCase().split(' ').join('_'); }, iconName() { + // TODO Delete this conditional once we have an `issue-type-epic` icon + if (this.workItemIconName === 'issue-type-epic') { + return 'epic'; + } + return ( this.workItemIconName || WORK_ITEMS_TYPE_MAP[this.workItemTypeUppercase]?.icon || diff --git a/app/assets/javascripts/work_items/graphql/cache_utils.js b/app/assets/javascripts/work_items/graphql/cache_utils.js index 14eedf5cdd8..aeeffea24e7 100644 --- a/app/assets/javascripts/work_items/graphql/cache_utils.js +++ b/app/assets/javascripts/work_items/graphql/cache_utils.js @@ -1,5 +1,6 @@ import { produce } from 'immer'; import { WIDGET_TYPE_NOTES } from '~/work_items/constants'; +import groupWorkItemByIidQuery from '~/work_items/graphql/group_work_item_by_iid.query.graphql'; import workItemByIidQuery from '~/work_items/graphql/work_item_by_iid.query.graphql'; import { findHierarchyWidgetChildren } from '~/work_items/utils'; @@ -127,8 +128,11 @@ export const updateCacheAfterRemovingAwardEmojiFromNote = (currentNotes, note) = }); }; -export const addHierarchyChild = (cache, fullPath, iid, workItem) => { - const queryArgs = { query: workItemByIidQuery, variables: { fullPath, iid } }; +export const addHierarchyChild = ({ cache, fullPath, iid, isGroup, workItem }) => { + const queryArgs = { + query: isGroup ? groupWorkItemByIidQuery : workItemByIidQuery, + variables: { fullPath, iid }, + }; const sourceData = cache.readQuery(queryArgs); if (!sourceData) { @@ -143,8 +147,11 @@ export const addHierarchyChild = (cache, fullPath, iid, workItem) => { }); }; -export const removeHierarchyChild = (cache, fullPath, iid, workItem) => { - const queryArgs = { query: workItemByIidQuery, variables: { fullPath, iid } }; +export const removeHierarchyChild = ({ cache, fullPath, iid, isGroup, workItem }) => { + const queryArgs = { + query: isGroup ? groupWorkItemByIidQuery : workItemByIidQuery, + variables: { fullPath, iid }, + }; const sourceData = cache.readQuery(queryArgs); if (!sourceData) { diff --git a/app/assets/javascripts/work_items/graphql/group_work_item_by_iid.query.graphql b/app/assets/javascripts/work_items/graphql/group_work_item_by_iid.query.graphql new file mode 100644 index 00000000000..f23bafa20c3 --- /dev/null +++ b/app/assets/javascripts/work_items/graphql/group_work_item_by_iid.query.graphql @@ -0,0 +1,12 @@ +#import "./work_item.fragment.graphql" + +query groupWorkItemByIid($fullPath: ID!, $iid: String) { + workspace: group(fullPath: $fullPath) @persist { + id + workItems(iid: $iid) { + nodes { + ...WorkItem + } + } + } +} diff --git a/app/assets/javascripts/work_items/index.js b/app/assets/javascripts/work_items/index.js index 70bda7d3783..0b7f9290d6e 100644 --- a/app/assets/javascripts/work_items/index.js +++ b/app/assets/javascripts/work_items/index.js @@ -1,17 +1,25 @@ import Vue from 'vue'; import VueApollo from 'vue-apollo'; +import { WORKSPACE_GROUP } from '~/issues/constants'; import { parseBoolean } from '~/lib/utils/common_utils'; import { apolloProvider } from '~/graphql_shared/issuable_client'; import App from './components/app.vue'; +import WorkItemRoot from './pages/work_item_root.vue'; import { createRouter } from './router'; Vue.use(VueApollo); -export const initWorkItemsRoot = () => { +export const initWorkItemsRoot = (workspace) => { const el = document.querySelector('#js-work-items'); + + if (!el) { + return undefined; + } + const { fullPath, hasIssueWeightsFeature, + iid, issuesListPath, registerPath, signInPath, @@ -22,6 +30,8 @@ export const initWorkItemsRoot = () => { reportAbusePath, } = el.dataset; + const Component = workspace === WORKSPACE_GROUP ? WorkItemRoot : App; + return new Vue({ el, name: 'WorkItemsRoot', @@ -29,6 +39,7 @@ export const initWorkItemsRoot = () => { apolloProvider, provide: { fullPath, + isGroup: workspace === WORKSPACE_GROUP, hasIssueWeightsFeature: parseBoolean(hasIssueWeightsFeature), hasOkrsFeature: parseBoolean(hasOkrsFeature), issuesListPath, @@ -40,7 +51,11 @@ export const initWorkItemsRoot = () => { reportAbusePath, }, render(createElement) { - return createElement(App); + return createElement(Component, { + props: { + iid: workspace === WORKSPACE_GROUP ? iid : undefined, + }, + }); }, }); }; diff --git a/app/assets/javascripts/work_items/pages/create_work_item.vue b/app/assets/javascripts/work_items/pages/create_work_item.vue index b5705b21b5a..31e790254d9 100644 --- a/app/assets/javascripts/work_items/pages/create_work_item.vue +++ b/app/assets/javascripts/work_items/pages/create_work_item.vue @@ -10,6 +10,7 @@ import { } from '../constants'; import createWorkItemMutation from '../graphql/create_work_item.mutation.graphql'; import projectWorkItemTypesQuery from '../graphql/project_work_item_types.query.graphql'; +import groupWorkItemByIidQuery from '../graphql/group_work_item_by_iid.query.graphql'; import workItemByIidQuery from '../graphql/work_item_by_iid.query.graphql'; import ItemTitle from '../components/item_title.vue'; @@ -22,7 +23,7 @@ export default { ItemTitle, GlFormSelect, }, - inject: ['fullPath'], + inject: ['fullPath', 'isGroup'], props: { initialTitle: { type: String, @@ -94,7 +95,7 @@ export default { const { workItem } = workItemCreate; store.writeQuery({ - query: workItemByIidQuery, + query: this.isGroup ? groupWorkItemByIidQuery : workItemByIidQuery, variables: { fullPath: this.fullPath, iid: workItem.iid, |