Skip to content
Snippets Groups Projects
Commit cc7fd2ed authored by Deepika Guliani's avatar Deepika Guliani :two:
Browse files

User role badges for work item notes

Changelog: added
parent 503e9f96
No related branches found
No related tags found
1 merge request!123469User role badges for work item notes
......@@ -154,6 +154,9 @@ export default {
isWorkItemAuthor() {
return getIdFromGraphQLId(this.workItem?.author?.id) === getIdFromGraphQLId(this.author.id);
},
projectName() {
return this.workItem?.project?.name;
},
},
apollo: {
workItem: {
......@@ -329,6 +332,9 @@ export default {
:can-report-abuse="!isCurrentUserAuthorOfNote"
:is-work-item-author="isWorkItemAuthor"
:work-item-type="workItemType"
:is-author-contributor="note.authorIsContributor"
:max-access-level-of-author="note.maxAccessLevelOfAuthor"
:project-name="projectName"
@startReplying="showReplyForm"
@startEditing="startEditing"
@error="($event) => $emit('error', $event)"
......
......@@ -84,6 +84,21 @@ export default {
required: false,
default: false,
},
isAuthorContributor: {
type: Boolean,
required: false,
default: false,
},
maxAccessLevelOfAuthor: {
type: String,
required: false,
default: '',
},
projectName: {
type: String,
required: false,
default: '',
},
},
computed: {
assignUserActionText() {
......@@ -96,6 +111,17 @@ export default {
workItemType: this.workItemType.toLowerCase(),
});
},
displayMemberBadgeText() {
return sprintf(__('This user has the %{access} role in the %{name} project.'), {
access: this.maxAccessLevelOfAuthor.toLowerCase(),
name: this.projectName,
});
},
displayContributorBadgeText() {
return sprintf(__('This user has previously committed to the %{name} project.'), {
name: this.projectName,
});
},
},
methods: {
......@@ -140,6 +166,24 @@ export default {
>
{{ __('Author') }}
</user-access-role-badge>
<user-access-role-badge
v-if="maxAccessLevelOfAuthor"
v-gl-tooltip
class="gl-mr-3 gl-display-none gl-sm-display-block"
:title="displayMemberBadgeText"
data-testid="max-access-level-badge"
>
{{ maxAccessLevelOfAuthor }}
</user-access-role-badge>
<user-access-role-badge
v-else-if="isAuthorContributor"
v-gl-tooltip
class="gl-mr-3 gl-display-none gl-sm-display-block"
:title="displayContributorBadgeText"
data-testid="contributor-badge"
>
{{ __('Contributor') }}
</user-access-role-badge>
<emoji-picker
v-if="showAwardEmoji && glFeatures.workItemsMvc2"
toggle-class="note-action-button note-emoji-button btn-icon btn-default-tertiary"
......
......@@ -10,6 +10,8 @@ fragment WorkItemNote on Note {
createdAt
lastEditedAt
url
authorIsContributor
maxAccessLevelOfAuthor
lastEditedBy {
...User
webPath
......
......@@ -15,6 +15,7 @@ fragment WorkItem on WorkItem {
id
fullPath
archived
name
}
author {
...Author
......
......@@ -10,6 +10,8 @@ fragment WorkItemNote on Note {
createdAt
lastEditedAt
url
authorIsContributor
maxAccessLevelOfAuthor
lastEditedBy {
...User
webPath
......
......@@ -25,6 +25,8 @@ describe('Work Item Note Actions', () => {
const findAssignUnassignButton = () => wrapper.find('[data-testid="assign-note-action"]');
const findReportAbuseToAdminButton = () => wrapper.find('[data-testid="abuse-note-action"]');
const findAuthorBadge = () => wrapper.find('[data-testid="author-badge"]');
const findMaxAccessLevelBadge = () => wrapper.find('[data-testid="max-access-level-badge"]');
const findContributorBadge = () => wrapper.find('[data-testid="contributor-badge"]');
const addEmojiMutationResolver = jest.fn().mockResolvedValue({
data: {
......@@ -45,6 +47,9 @@ describe('Work Item Note Actions', () => {
canReportAbuse = false,
workItemType = 'Task',
isWorkItemAuthor = false,
isAuthorContributor = false,
maxAccessLevelOfAuthor = '',
projectName = 'Project name',
} = {}) => {
wrapper = shallowMount(WorkItemNoteActions, {
propsData: {
......@@ -56,6 +61,9 @@ describe('Work Item Note Actions', () => {
canReportAbuse,
workItemType,
isWorkItemAuthor,
isAuthorContributor,
maxAccessLevelOfAuthor,
projectName,
},
provide: {
glFeatures: {
......@@ -251,5 +259,41 @@ describe('Work Item Note Actions', () => {
expect(findAuthorBadge().attributes('title')).toBe('This user is the author of this task.');
});
});
describe('Max access level badge', () => {
it('does not show the access level badge by default', () => {
createComponent();
expect(findMaxAccessLevelBadge().exists()).toBe(false);
});
it('shows the access badge when we have a valid value', () => {
createComponent({ maxAccessLevelOfAuthor: 'Owner' });
expect(findMaxAccessLevelBadge().exists()).toBe(true);
expect(findMaxAccessLevelBadge().text()).toBe('Owner');
expect(findMaxAccessLevelBadge().attributes('title')).toBe(
'This user has the owner role in the Project name project.',
);
});
});
describe('Contributor badge', () => {
it('does not show the contributor badge by default', () => {
createComponent();
expect(findContributorBadge().exists()).toBe(false);
});
it('shows the contributor badge the note author is a contributor', () => {
createComponent({ isAuthorContributor: true });
expect(findContributorBadge().exists()).toBe(true);
expect(findContributorBadge().text()).toBe('Contributor');
expect(findContributorBadge().attributes('title')).toBe(
'This user has previously committed to the Project name project.',
);
});
});
});
});
......@@ -20,6 +20,8 @@ import {
updateWorkItemMutationResponse,
workItemByIidResponseFactory,
workItemQueryResponse,
mockWorkItemCommentNoteByContributor,
mockWorkItemCommentByMaintainer,
} from 'jest/work_items/mock_data';
import { i18n, TRACKING_CATEGORY_SHOW } from '~/work_items/constants';
import { mockTracking } from 'helpers/tracking_helper';
......@@ -236,8 +238,9 @@ describe('Work Item Note', () => {
});
describe('main comment', () => {
beforeEach(() => {
beforeEach(async () => {
createComponent({ isFirstNote: true });
await waitForPromises();
});
it('should have the note header, actions and body', () => {
......@@ -250,6 +253,10 @@ describe('Work Item Note', () => {
it('should have the reply button props', () => {
expect(findNoteActions().props('showReply')).toBe(true);
});
it('should have the project name', () => {
expect(findNoteActions().props('projectName')).toBe('Project name');
});
});
describe('comment threads', () => {
......@@ -374,6 +381,28 @@ describe('Work Item Note', () => {
},
);
});
describe('Max access level badge', () => {
it('should pass the max access badge props', async () => {
createComponent({ note: mockWorkItemCommentByMaintainer });
await waitForPromises();
expect(findNoteActions().props('maxAccessLevelOfAuthor')).toBe(
mockWorkItemCommentByMaintainer.maxAccessLevelOfAuthor,
);
});
});
describe('Contributor badge', () => {
it('should pass the contributor props', async () => {
createComponent({ note: mockWorkItemCommentNoteByContributor });
await waitForPromises();
expect(findNoteActions().props('isAuthorContributor')).toBe(
mockWorkItemCommentNoteByContributor.authorIsContributor,
);
});
});
});
});
});
......@@ -97,6 +97,7 @@ export const workItemQueryResponse = {
id: '1',
fullPath: 'test-project-path',
archived: false,
name: 'Project name',
},
workItemType: {
__typename: 'WorkItemType',
......@@ -200,6 +201,7 @@ export const updateWorkItemMutationResponse = {
id: '1',
fullPath: 'test-project-path',
archived: false,
name: 'Project name',
},
workItemType: {
__typename: 'WorkItemType',
......@@ -304,6 +306,7 @@ export const convertWorkItemMutationResponse = {
id: '1',
fullPath: 'test-project-path',
archived: false,
name: 'Project name',
},
workItemType: {
__typename: 'WorkItemType',
......@@ -456,6 +459,7 @@ export const workItemResponseFactory = ({
id: '1',
fullPath: 'test-project-path',
archived: false,
name: 'Project name',
},
workItemType,
userPermissions: {
......@@ -725,6 +729,7 @@ export const createWorkItemMutationResponse = {
id: '1',
fullPath: 'test-project-path',
archived: false,
name: 'Project name',
},
workItemType: {
__typename: 'WorkItemType',
......@@ -956,6 +961,7 @@ export const workItemHierarchyEmptyResponse = {
id: '1',
fullPath: 'test-project-path',
archived: false,
name: 'Project name',
},
userPermissions: {
deleteWorkItem: false,
......@@ -1015,6 +1021,7 @@ export const workItemHierarchyNoUpdatePermissionResponse = {
id: '1',
fullPath: 'test-project-path',
archived: false,
name: 'Project name',
},
confidential: false,
widgets: [
......@@ -1167,6 +1174,7 @@ export const workItemHierarchyResponse = {
id: '1',
fullPath: 'test-project-path',
archived: false,
name: 'Project name',
},
description: 'Issue description',
state: 'OPEN',
......@@ -1244,6 +1252,7 @@ export const workItemObjectiveWithChild = {
id: '1',
fullPath: 'test-project-path',
archived: false,
name: 'Project name',
},
userPermissions: {
deleteWorkItem: true,
......@@ -1327,6 +1336,7 @@ export const workItemHierarchyTreeResponse = {
id: '1',
fullPath: 'test-project-path',
archived: false,
name: 'Project name',
},
widgets: [
{
......@@ -1417,6 +1427,7 @@ export const changeIndirectWorkItemParentMutationResponse = {
id: '1',
fullPath: 'test-project-path',
archived: false,
name: 'Project name',
},
widgets: [
{
......@@ -1480,6 +1491,7 @@ export const changeWorkItemParentMutationResponse = {
id: '1',
fullPath: 'test-project-path',
archived: false,
name: 'Project name',
},
widgets: [
{
......@@ -1953,6 +1965,8 @@ export const mockWorkItemNotesResponse = {
lastEditedBy: null,
system: true,
internal: false,
maxAccessLevelOfAuthor: 'Owner',
authorIsContributor: false,
discussion: {
id: 'gid://gitlab/Discussion/9c17769ca29798eddaed539d010da12723561234',
},
......@@ -2002,6 +2016,8 @@ export const mockWorkItemNotesResponse = {
lastEditedBy: null,
system: true,
internal: false,
maxAccessLevelOfAuthor: 'Owner',
authorIsContributor: false,
discussion: {
id: 'gid://gitlab/Discussion/9c17769ca29798eddaed539d010da12723565678',
},
......@@ -2050,6 +2066,8 @@ export const mockWorkItemNotesResponse = {
lastEditedBy: null,
system: true,
internal: false,
maxAccessLevelOfAuthor: 'Owner',
authorIsContributor: false,
discussion: {
id: 'gid://gitlab/Discussion/9c17769ca29798eddaed539d010da12723560987',
},
......@@ -2158,6 +2176,8 @@ export const mockWorkItemNotesByIidResponse = {
lastEditedBy: null,
system: true,
internal: false,
maxAccessLevelOfAuthor: null,
authorIsContributor: false,
discussion: {
id:
'gid://gitlab/Discussion/9c17769ca29798eddaed539d010da12723561234',
......@@ -2209,6 +2229,8 @@ export const mockWorkItemNotesByIidResponse = {
lastEditedBy: null,
system: true,
internal: false,
maxAccessLevelOfAuthor: null,
authorIsContributor: false,
discussion: {
id:
'gid://gitlab/Discussion/9c17769ca29798eddaed539d010da12723568765',
......@@ -2261,6 +2283,8 @@ export const mockWorkItemNotesByIidResponse = {
lastEditedBy: null,
system: true,
internal: false,
maxAccessLevelOfAuthor: null,
authorIsContributor: false,
discussion: {
id:
'gid://gitlab/Discussion/9c17769ca29798eddaed539d010da12723569876',
......@@ -2371,6 +2395,8 @@ export const mockMoreWorkItemNotesResponse = {
lastEditedBy: null,
system: true,
internal: false,
maxAccessLevelOfAuthor: 'Owner',
authorIsContributor: false,
discussion: {
id:
'gid://gitlab/Discussion/9c17769ca29798eddaed539d010da1112356a59e',
......@@ -2422,6 +2448,8 @@ export const mockMoreWorkItemNotesResponse = {
lastEditedBy: null,
system: true,
internal: false,
maxAccessLevelOfAuthor: 'Owner',
authorIsContributor: false,
discussion: {
id:
'gid://gitlab/Discussion/9c17769ca29798eddaed539d010da1272356a59e',
......@@ -2471,6 +2499,8 @@ export const mockMoreWorkItemNotesResponse = {
lastEditedBy: null,
system: true,
internal: false,
maxAccessLevelOfAuthor: 'Owner',
authorIsContributor: false,
discussion: {
id:
'gid://gitlab/Discussion/9c17769ca29798eddaed539d010da12723569876',
......@@ -2539,6 +2569,8 @@ export const createWorkItemNoteResponse = {
lastEditedAt: null,
url: 'http://127.0.0.1:3000/flightjs/Flight/-/work_items/37?iid_path=true#note_191',
lastEditedBy: null,
maxAccessLevelOfAuthor: 'Owner',
authorIsContributor: false,
discussion: {
id: 'gid://gitlab/Discussion/c872ba2d7d3eb780d2255138d67ca8b04f65b122',
__typename: 'Discussion',
......@@ -2590,6 +2622,8 @@ export const mockWorkItemCommentNote = {
lastEditedBy: null,
system: false,
internal: false,
maxAccessLevelOfAuthor: 'Owner',
authorIsContributor: false,
discussion: {
id: 'gid://gitlab/Discussion/9c17769ca29798eddaed539d010da12723569876',
},
......@@ -2613,6 +2647,16 @@ export const mockWorkItemCommentNote = {
},
};
export const mockWorkItemCommentNoteByContributor = {
...mockWorkItemCommentNote,
authorIsContributor: true,
};
export const mockWorkItemCommentByMaintainer = {
...mockWorkItemCommentNote,
maxAccessLevelOfAuthor: 'Maintainer',
};
export const mockWorkItemNotesResponseWithComments = {
data: {
workspace: {
......@@ -2674,6 +2718,8 @@ export const mockWorkItemNotesResponseWithComments = {
url:
'http://127.0.0.1:3000/flightjs/Flight/-/work_items/37?iid_path=true#note_191',
lastEditedBy: null,
maxAccessLevelOfAuthor: 'Owner',
authorIsContributor: false,
discussion: {
id:
'gid://gitlab/Discussion/2bb1162fd0d39297d1a68fdd7d4083d3780af0f3',
......@@ -2712,6 +2758,8 @@ export const mockWorkItemNotesResponseWithComments = {
url:
'http://127.0.0.1:3000/flightjs/Flight/-/work_items/37?iid_path=true#note_191',
lastEditedBy: null,
maxAccessLevelOfAuthor: 'Owner',
authorIsContributor: false,
discussion: {
id:
'gid://gitlab/Discussion/2bb1162fd0d39297d1a68fdd7d4083d3780af0f3',
......@@ -2759,6 +2807,8 @@ export const mockWorkItemNotesResponseWithComments = {
lastEditedBy: null,
system: false,
internal: false,
maxAccessLevelOfAuthor: 'Owner',
authorIsContributor: false,
discussion: {
id:
'gid://gitlab/Discussion/9c17769ca29798eddaed539d010da12723560987',
......@@ -2831,6 +2881,8 @@ export const workItemNotesCreateSubscriptionResponse = {
lastEditedBy: null,
system: true,
internal: false,
maxAccessLevelOfAuthor: 'Owner',
authorIsContributor: false,
discussion: {
id: 'gid://gitlab/Discussion/9c17769ca29798eddaed539d010da12723560987',
},
......@@ -2901,6 +2953,8 @@ export const workItemNotesUpdateSubscriptionResponse = {
lastEditedBy: null,
system: true,
internal: false,
maxAccessLevelOfAuthor: 'Owner',
authorIsContributor: false,
discussion: {
id: 'gid://gitlab/Discussion/9c17769ca29798eddaed539d010da12723560987',
},
......@@ -2952,6 +3006,8 @@ export const workItemSystemNoteWithMetadata = {
lastEditedAt: '2023-05-05T07:19:37Z',
url: 'https://gdk.test:3443/flightjs/Flight/-/work_items/46#note_1651',
lastEditedBy: null,
maxAccessLevelOfAuthor: 'Owner',
authorIsContributor: false,
discussion: {
id: 'gid://gitlab/Discussion/7d4a46ea0525e2eeed451f7b718b0ebe73205374',
__typename: 'Discussion',
......@@ -3044,6 +3100,8 @@ export const workItemNotesWithSystemNotesWithChangedDescription = {
lastEditedAt: '2023-05-10T05:21:01Z',
url: 'https://gdk.test:3443/gnuwget/Wget2/-/work_items/79#note_1687',
lastEditedBy: null,
maxAccessLevelOfAuthor: 'Owner',
authorIsContributor: false,
discussion: {
id:
'gid://gitlab/Discussion/aa72f4c2f3eef66afa6d79a805178801ce4bd89f',
......@@ -3104,6 +3162,8 @@ export const workItemNotesWithSystemNotesWithChangedDescription = {
lastEditedAt: '2023-05-10T05:21:05Z',
url: 'https://gdk.test:3443/gnuwget/Wget2/-/work_items/79#note_1688',
lastEditedBy: null,
maxAccessLevelOfAuthor: 'Owner',
authorIsContributor: false,
discussion: {
id:
'gid://gitlab/Discussion/a7d3cf7bd72f7a98f802845f538af65cb11a02cc',
......@@ -3165,6 +3225,8 @@ export const workItemNotesWithSystemNotesWithChangedDescription = {
lastEditedAt: '2023-05-10T05:21:08Z',
url: 'https://gdk.test:3443/gnuwget/Wget2/-/work_items/79#note_1689',
lastEditedBy: null,
maxAccessLevelOfAuthor: 'Owner',
authorIsContributor: false,
discussion: {
id:
'gid://gitlab/Discussion/391eed1ee0a258cc966a51dde900424f3b51b95d',
......
0% Loading or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment