From 086e29c237244b7fdc96fedd26ba8c08116d425a Mon Sep 17 00:00:00 2001 From: Rajan Mistry Date: Tue, 29 Aug 2023 23:18:35 +0530 Subject: [PATCH 1/3] Create a Work items relationship widget shell Create a Work items relationship widget shell. Changelog: other --- .../shared/work_item_link_child_contents.vue | 9 ---- .../components/work_item_detail.vue | 17 ++++++ .../work_item_links/work_item_link_child.vue | 3 +- .../work_item_relationships.vue | 54 +++++++++++++++++++ .../javascripts/work_items/constants.js | 1 + .../work_item_widgets.fragment.graphql | 4 ++ app/assets/javascripts/work_items/utils.js | 4 ++ .../projects/incidents_controller.rb | 1 + app/controllers/projects/issues_controller.rb | 1 + .../projects/work_items_controller.rb | 1 + app/controllers/projects_controller.rb | 1 + .../work_item_widgets.fragment.graphql | 5 +- .../features/projects/work_items/okr_spec.rb | 8 +-- locale/gitlab.pot | 6 +++ .../work_item_link_child_spec.js | 2 - .../work_item_relationships_spec.js | 26 +++++++++ 16 files changed, 126 insertions(+), 17 deletions(-) create mode 100644 app/assets/javascripts/work_items/components/work_item_relationships/work_item_relationships.vue create mode 100644 spec/frontend/work_items/components/work_item_relationships/work_item_relationships_spec.js diff --git a/app/assets/javascripts/work_items/components/shared/work_item_link_child_contents.vue b/app/assets/javascripts/work_items/components/shared/work_item_link_child_contents.vue index 0a38dcb77f67d3..b10a3727e9f638 100644 --- a/app/assets/javascripts/work_items/components/shared/work_item_link_child_contents.vue +++ b/app/assets/javascripts/work_items/components/shared/work_item_link_child_contents.vue @@ -43,15 +43,6 @@ export default { type: Boolean, required: true, }, - parentWorkItemId: { - type: String, - required: true, - }, - workItemType: { - type: String, - required: false, - default: '', - }, childPath: { type: String, required: true, 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 8146b66dc1fd1b..133cf4ebf2e55c 100644 --- a/app/assets/javascripts/work_items/components/work_item_detail.vue +++ b/app/assets/javascripts/work_items/components/work_item_detail.vue @@ -31,6 +31,7 @@ import { WORK_ITEM_TYPE_VALUE_ISSUE, WORK_ITEM_TYPE_VALUE_OBJECTIVE, WIDGET_TYPE_NOTES, + WIDGET_TYPE_LINKED_ITEMS, } from '../constants'; import workItemUpdatedSubscription from '../graphql/work_item_updated.subscription.graphql'; @@ -50,6 +51,7 @@ import WorkItemNotes from './work_item_notes.vue'; 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'; export default { i18n, @@ -79,6 +81,7 @@ export default { AbuseCategorySelector, GlIntersectionObserver, ConfidentialityBadge, + WorkItemRelationships, }, mixins: [glFeatureFlagMixin()], inject: ['fullPath', 'reportAbusePath'], @@ -259,6 +262,15 @@ export default { showIntersectionObserver() { return !this.isModal && this.workItemsMvc2Enabled; }, + linkedWorkItems() { + return this.glFeatures.linkedWorkItems; + }, + workItemLinkedItems() { + return this.isWidgetPresent(WIDGET_TYPE_LINKED_ITEMS); + }, + showWorkItemLinkedItems() { + return this.linkedWorkItems && this.isWidgetPresent(WIDGET_TYPE_LINKED_ITEMS); + }, }, mounted() { if (this.modalWorkItemIid) { @@ -591,6 +603,11 @@ export default { @show-modal="openInModal" @addChild="$emit('addChild')" /> + +import { GlButton } from '@gitlab/ui'; + +import { s__ } from '~/locale'; + +import WidgetWrapper from '../widget_wrapper.vue'; + +export default { + components: { + WidgetWrapper, + GlButton, + }, + props: { + workItemIid: { + type: String, + required: true, + }, + workItemFullpath: { + type: String, + required: true, + }, + }, + i18n: { + title: s__('WorkItem|Linked Items'), + emptyStateMessage: s__( + "WorkItem|Link work items together to show that they're related or that one is blocking others.", + ), + addLinkedWorkItemButtonLabel: s__('WorkItem|Add'), + }, +}; + + diff --git a/app/assets/javascripts/work_items/constants.js b/app/assets/javascripts/work_items/constants.js index 715c2ef843fad1..c68f59abe00d25 100644 --- a/app/assets/javascripts/work_items/constants.js +++ b/app/assets/javascripts/work_items/constants.js @@ -26,6 +26,7 @@ export const WIDGET_TYPE_MILESTONE = 'MILESTONE'; export const WIDGET_TYPE_ITERATION = 'ITERATION'; export const WIDGET_TYPE_NOTES = 'NOTES'; export const WIDGET_TYPE_HEALTH_STATUS = 'HEALTH_STATUS'; +export const WIDGET_TYPE_LINKED_ITEMS = 'LINKED_ITEMS'; export const WORK_ITEM_TYPE_ENUM_INCIDENT = 'INCIDENT'; export const WORK_ITEM_TYPE_ENUM_ISSUE = 'ISSUE'; diff --git a/app/assets/javascripts/work_items/graphql/work_item_widgets.fragment.graphql b/app/assets/javascripts/work_items/graphql/work_item_widgets.fragment.graphql index 383d003e78ccfc..14cb6f8415c423 100644 --- a/app/assets/javascripts/work_items/graphql/work_item_widgets.fragment.graphql +++ b/app/assets/javascripts/work_items/graphql/work_item_widgets.fragment.graphql @@ -100,4 +100,8 @@ fragment WorkItemWidgets on WorkItemWidget { ... on WorkItemWidgetAwardEmoji { type } + + ... on WorkItemWidgetLinkedItems { + type + } } diff --git a/app/assets/javascripts/work_items/utils.js b/app/assets/javascripts/work_items/utils.js index ac5d8b32fad976..fdb0a58b3ae57f 100644 --- a/app/assets/javascripts/work_items/utils.js +++ b/app/assets/javascripts/work_items/utils.js @@ -42,3 +42,7 @@ export const markdownPreviewPath = (fullPath, iid) => `${ gon.relative_url_root || '' }/${fullPath}/preview_markdown?target_type=WorkItem&target_id=${iid}`; + +export const workItemPath = (fullPath, workItemIid) => { + return `${gon?.relative_url_root || ''}/${fullPath}/-/work_items/${workItemIid}`; +}; diff --git a/app/controllers/projects/incidents_controller.rb b/app/controllers/projects/incidents_controller.rb index 6109e29b16981b..69d349b1f1d53e 100644 --- a/app/controllers/projects/incidents_controller.rb +++ b/app/controllers/projects/incidents_controller.rb @@ -12,6 +12,7 @@ class Projects::IncidentsController < Projects::ApplicationController push_force_frontend_feature_flag(:work_items_mvc_2, @project&.work_items_mvc_2_feature_flag_enabled?) push_frontend_feature_flag(:moved_mr_sidebar, project) push_frontend_feature_flag(:move_close_into_dropdown, project) + push_force_frontend_feature_flag(:linked_work_items, @project&.linked_work_items_feature_flag_enabled?) end feature_category :incident_management diff --git a/app/controllers/projects/issues_controller.rb b/app/controllers/projects/issues_controller.rb index 159e839cfec83a..4d04ca635a13fe 100644 --- a/app/controllers/projects/issues_controller.rb +++ b/app/controllers/projects/issues_controller.rb @@ -72,6 +72,7 @@ class Projects::IssuesController < Projects::ApplicationController push_frontend_feature_flag(:epic_widget_edit_confirmation, project) push_frontend_feature_flag(:moved_mr_sidebar, project) push_frontend_feature_flag(:move_close_into_dropdown, project) + push_force_frontend_feature_flag(:linked_work_items, project&.linked_work_items_feature_flag_enabled?) end around_action :allow_gitaly_ref_name_caching, only: [:discussions] diff --git a/app/controllers/projects/work_items_controller.rb b/app/controllers/projects/work_items_controller.rb index 7da31c199a12b9..c3986be31b0a6d 100644 --- a/app/controllers/projects/work_items_controller.rb +++ b/app/controllers/projects/work_items_controller.rb @@ -12,6 +12,7 @@ class Projects::WorkItemsController < Projects::ApplicationController push_force_frontend_feature_flag(:work_items_mvc, project&.work_items_mvc_feature_flag_enabled?) push_force_frontend_feature_flag(:work_items_mvc_2, project&.work_items_mvc_2_feature_flag_enabled?) push_force_frontend_feature_flag(:saved_replies, current_user) + push_force_frontend_feature_flag(:linked_work_items, project&.linked_work_items_feature_flag_enabled?) end feature_category :team_planning diff --git a/app/controllers/projects_controller.rb b/app/controllers/projects_controller.rb index 2ad0f11dc91f7e..6a246219f7d394 100644 --- a/app/controllers/projects_controller.rb +++ b/app/controllers/projects_controller.rb @@ -46,6 +46,7 @@ class ProjectsController < Projects::ApplicationController push_force_frontend_feature_flag(:work_items, @project&.work_items_feature_flag_enabled?) push_force_frontend_feature_flag(:work_items_mvc, @project&.work_items_mvc_feature_flag_enabled?) push_force_frontend_feature_flag(:work_items_mvc_2, @project&.work_items_mvc_2_feature_flag_enabled?) + push_force_frontend_feature_flag(:linked_work_items, @project&.linked_work_items_feature_flag_enabled?) end layout :determine_layout diff --git a/ee/app/assets/javascripts/work_items/graphql/work_item_widgets.fragment.graphql b/ee/app/assets/javascripts/work_items/graphql/work_item_widgets.fragment.graphql index 5b7aa8e652e454..0c7e85131e9a18 100644 --- a/ee/app/assets/javascripts/work_items/graphql/work_item_widgets.fragment.graphql +++ b/ee/app/assets/javascripts/work_items/graphql/work_item_widgets.fragment.graphql @@ -1,7 +1,6 @@ #import "~/graphql_shared/fragments/label.fragment.graphql" #import "~/graphql_shared/fragments/user.fragment.graphql" #import "~/work_items/graphql/milestone.fragment.graphql" - #import "./work_item_metadata_widgets.fragment.graphql" fragment WorkItemWidgets on WorkItemWidget { @@ -126,4 +125,8 @@ fragment WorkItemWidgets on WorkItemWidget { ... on WorkItemWidgetAwardEmoji { type } + + ... on WorkItemWidgetLinkedItems { + type + } } diff --git a/ee/spec/features/projects/work_items/okr_spec.rb b/ee/spec/features/projects/work_items/okr_spec.rb index 724badfb7750c4..00f36891b8ec1e 100644 --- a/ee/spec/features/projects/work_items/okr_spec.rb +++ b/ee/spec/features/projects/work_items/okr_spec.rb @@ -134,15 +134,15 @@ it 'toggles widget body', :aggregate_failures do page.within('[data-testid="work-item-tree"]') do - expect(page).to have_selector('[data-testid="widget-body"]') + expect(page).to have_selector('[data-testid="work-item-tree"] [data-testid="widget-body"]') click_button 'Collapse' - expect(page).not_to have_selector('[data-testid="widget-body"]') + expect(page).not_to have_selector('[data-testid="work-item-tree"] [data-testid="widget-body"]') click_button 'Expand' - expect(page).to have_selector('[data-testid="widget-body"]') + expect(page).to have_selector('[data-testid="work-item-tree"] [data-testid="widget-body"]') end end @@ -249,7 +249,7 @@ close_rich_text_promo_popover_if_present wait_for_all_requests - page.within('[data-testid="widget-body"]') do + page.within('[data-testid="work-item-tree"] [data-testid="widget-body"]') do click_button 'Expand' wait_for_all_requests diff --git a/locale/gitlab.pot b/locale/gitlab.pot index f796e1933c7e8b..17c6337db0089b 100644 --- a/locale/gitlab.pot +++ b/locale/gitlab.pot @@ -53609,6 +53609,12 @@ msgstr "" msgid "WorkItem|Key result" msgstr "" +msgid "WorkItem|Link work items together to show that they're related or that one is blocking others." +msgstr "" + +msgid "WorkItem|Linked Items" +msgstr "" + msgid "WorkItem|Mark as done" msgstr "" diff --git a/spec/frontend/work_items/components/work_item_links/work_item_link_child_spec.js b/spec/frontend/work_items/components/work_item_links/work_item_link_child_spec.js index 803ff950cbe554..a624bbe85676eb 100644 --- a/spec/frontend/work_items/components/work_item_links/work_item_link_child_spec.js +++ b/spec/frontend/work_items/components/work_item_links/work_item_link_child_spec.js @@ -93,8 +93,6 @@ describe('WorkItemLinkChild', () => { expect(findWorkItemLinkChildContents().props()).toEqual({ childItem: workItemObjectiveWithChild, canUpdate: true, - parentWorkItemId: 'gid://gitlab/WorkItem/2', - workItemType: 'Objective', childPath: '/gitlab-org/gitlab-test/-/work_items/12', }); }); diff --git a/spec/frontend/work_items/components/work_item_relationships/work_item_relationships_spec.js b/spec/frontend/work_items/components/work_item_relationships/work_item_relationships_spec.js new file mode 100644 index 00000000000000..e8251e3e91b870 --- /dev/null +++ b/spec/frontend/work_items/components/work_item_relationships/work_item_relationships_spec.js @@ -0,0 +1,26 @@ +import { shallowMountExtended } from 'helpers/vue_test_utils_helper'; +import waitForPromises from 'helpers/wait_for_promises'; + +import WorkItemRelationships from '~/work_items/components/work_item_relationships/work_item_relationships.vue'; + +describe('WorkItemLinks', () => { + let wrapper; + + const createComponent = async () => { + wrapper = shallowMountExtended(WorkItemRelationships, { + propsData: { + workItem: {}, + workItemIid: '1', + workItemFullpath: 'gitlab/path', + }, + }); + + await waitForPromises(); + }; + + it('renders the component', () => { + createComponent(); + + expect(wrapper.find('.work-item-relationships')).toBeDefined(); + }); +}); -- GitLab From 837b505dd26d86f80ee36b5275693e9a6596719e Mon Sep 17 00:00:00 2001 From: Rajan Mistry Date: Fri, 1 Sep 2023 21:27:16 +0530 Subject: [PATCH 2/3] Addressed review comments for work item widget --- .../work_items/components/work_item_detail.vue | 4 ++-- .../work_item_relationships_spec.js | 6 +++--- spec/frontend/work_items/utils_spec.js | 13 ++++++++++++- 3 files changed, 17 insertions(+), 6 deletions(-) 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 133cf4ebf2e55c..f03dc320736c3c 100644 --- a/app/assets/javascripts/work_items/components/work_item_detail.vue +++ b/app/assets/javascripts/work_items/components/work_item_detail.vue @@ -262,14 +262,14 @@ export default { showIntersectionObserver() { return !this.isModal && this.workItemsMvc2Enabled; }, - linkedWorkItems() { + hasLinkedWorkItems() { return this.glFeatures.linkedWorkItems; }, workItemLinkedItems() { return this.isWidgetPresent(WIDGET_TYPE_LINKED_ITEMS); }, showWorkItemLinkedItems() { - return this.linkedWorkItems && this.isWidgetPresent(WIDGET_TYPE_LINKED_ITEMS); + return this.hasLinkedWorkItems && this.isWidgetPresent(WIDGET_TYPE_LINKED_ITEMS); }, }, mounted() { diff --git a/spec/frontend/work_items/components/work_item_relationships/work_item_relationships_spec.js b/spec/frontend/work_items/components/work_item_relationships/work_item_relationships_spec.js index e8251e3e91b870..79290ea4e78267 100644 --- a/spec/frontend/work_items/components/work_item_relationships/work_item_relationships_spec.js +++ b/spec/frontend/work_items/components/work_item_relationships/work_item_relationships_spec.js @@ -1,9 +1,9 @@ import { shallowMountExtended } from 'helpers/vue_test_utils_helper'; import waitForPromises from 'helpers/wait_for_promises'; - +import WidgetWrapper from '~/work_items/components/widget_wrapper.vue'; import WorkItemRelationships from '~/work_items/components/work_item_relationships/work_item_relationships.vue'; -describe('WorkItemLinks', () => { +describe('WorkItemRelationships', () => { let wrapper; const createComponent = async () => { @@ -21,6 +21,6 @@ describe('WorkItemLinks', () => { it('renders the component', () => { createComponent(); - expect(wrapper.find('.work-item-relationships')).toBeDefined(); + expect(wrapper.findComponent(WidgetWrapper)).toBeDefined(); }); }); diff --git a/spec/frontend/work_items/utils_spec.js b/spec/frontend/work_items/utils_spec.js index aa24b80cf08037..8a49140119d16d 100644 --- a/spec/frontend/work_items/utils_spec.js +++ b/spec/frontend/work_items/utils_spec.js @@ -1,4 +1,4 @@ -import { autocompleteDataSources, markdownPreviewPath } from '~/work_items/utils'; +import { autocompleteDataSources, markdownPreviewPath, workItemPath } from '~/work_items/utils'; describe('autocompleteDataSources', () => { beforeEach(() => { @@ -25,3 +25,14 @@ describe('markdownPreviewPath', () => { ); }); }); + +describe('workItemPath', () => { + it('returns corrrect data sources', () => { + expect(workItemPath('project/group', '2')).toEqual('/project/group/-/work_items/2'); + }); + + it('returns corrrect data sources with relative url root', () => { + gon.relative_url_root = '/foobar'; + expect(workItemPath('project/group', '2')).toEqual('/foobar/project/group/-/work_items/2'); + }); +}); -- GitLab From 9cc9b43375ce7aa0524ff900d84f29d1f65a65f1 Mon Sep 17 00:00:00 2001 From: Rajan Mistry Date: Mon, 4 Sep 2023 16:38:11 +0530 Subject: [PATCH 3/3] Addressed maintainer review comments --- .../work_items/components/work_item_detail.vue | 2 +- .../work_item_relationships/work_item_relationships.vue | 8 ++------ app/assets/javascripts/work_items/utils.js | 3 ++- app/controllers/projects/issues_controller.rb | 2 +- .../work_item_relationships_spec.js | 3 +-- 5 files changed, 7 insertions(+), 11 deletions(-) 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 f03dc320736c3c..6beca682f8f441 100644 --- a/app/assets/javascripts/work_items/components/work_item_detail.vue +++ b/app/assets/javascripts/work_items/components/work_item_detail.vue @@ -269,7 +269,7 @@ export default { return this.isWidgetPresent(WIDGET_TYPE_LINKED_ITEMS); }, showWorkItemLinkedItems() { - return this.hasLinkedWorkItems && this.isWidgetPresent(WIDGET_TYPE_LINKED_ITEMS); + return this.hasLinkedWorkItems && this.workItemLinkedItems; }, }, mounted() { 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 f04f43d3d53e03..7e58627b92fce6 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 @@ -30,14 +30,10 @@ export default { };