Skip to content
Snippets Groups Projects
Verified Commit 95f6ba5a authored by Phil Hughes's avatar Phil Hughes
Browse files

Added reviewers list to reviewer drawer component

parent add2b639
No related branches found
No related tags found
No related merge requests found
mutation setReviewers($projectPath: ID!, $iid: String!, $reviewerUsernames: [String!]!) {
mergeRequestSetReviewers(
input: { projectPath: $projectPath, iid: $iid, reviewerUsernames: $reviewerUsernames }
) {
errors
}
}
query addReviewerPermissions($fullPath: ID!, $iid: String!) {
project(fullPath: $fullPath) {
id
mergeRequest(iid: $iid) {
id
userPermissions {
adminMergeRequest
}
}
}
}
......@@ -2,26 +2,53 @@
import { GlDrawer } from '@gitlab/ui';
import { DRAWER_Z_INDEX } from '~/lib/utils/constants';
import { getContentWrapperHeight } from '~/lib/utils/dom_utils';
import getMergeRequestReviewers from '~/sidebar/queries/get_merge_request_reviewers.query.graphql';
import ReviewersContainer from './reviewers_container.vue';
export default {
apollo: {
reviewers: {
query: getMergeRequestReviewers,
variables() {
return {
fullPath: this.projectPath,
iid: this.issuableIid,
};
},
update: (data) => data.workspace?.issuable?.reviewers?.nodes || [],
skip() {
return !this.open;
},
},
},
components: {
GlDrawer,
ReviewersContainer,
ApprovalSummary: () =>
import('ee_component/merge_requests/components/reviewers/approval_summary.vue'),
ApprovalRulesWrapper: () =>
import('ee_component/merge_requests/components/reviewers/approval_rules_wrapper.vue'),
},
inject: ['projectPath', 'issuableIid'],
props: {
open: {
type: Boolean,
required: true,
},
},
data() {
return {
reviewers: [],
};
},
computed: {
getDrawerHeaderHeight() {
if (!this.open) return '0';
return getContentWrapperHeight();
},
loadingReviewers() {
return this.$apollo.queries.reviewers.loading;
},
},
DRAWER_Z_INDEX,
};
......@@ -40,6 +67,7 @@ export default {
<template #header>
<approval-summary />
</template>
<reviewers-container :reviewers="reviewers" :loading-reviewers="loadingReviewers" />
<approval-rules-wrapper />
</gl-drawer>
</template>
<script>
import { GlEmptyState, GlButton } from '@gitlab/ui';
import noReviewersAssignedSvg from '@gitlab/svgs/dist/illustrations/add-user-sm.svg?url';
import UncollapsedReviewerList from '~/sidebar/components/reviewers/uncollapsed_reviewer_list.vue';
import { sprintf, __, n__ } from '~/locale';
import UpdateReviewers from './update_reviewers.vue';
import userPermissionsQuery from './queries/user_permissions.query.graphql';
export default {
apollo: {
userPermissions: {
query: userPermissionsQuery,
variables() {
return {
fullPath: this.projectPath,
iid: this.issuableIid,
};
},
update: (data) => data.project?.mergeRequest?.userPermissions || {},
},
},
components: {
GlEmptyState,
GlButton,
UncollapsedReviewerList,
UpdateReviewers,
},
inject: ['projectPath', 'issuableIid'],
props: {
reviewers: {
type: Array,
required: true,
},
loadingReviewers: {
type: Boolean,
required: true,
},
},
data() {
return {
userPermissions: {},
};
},
computed: {
currentUser() {
return [gon.current_username];
},
relativeUrlRoot() {
return gon.relative_url_root ?? '';
},
reviewersTitle() {
if (this.reviewers.length === 0) {
return sprintf(__('%{count} Reviewers'), { count: this.reviewers.length });
}
return sprintf(n__('%{count} Reviewer', '%{count} Reviewers', this.reviewers.length), {
count: this.reviewers.length,
});
},
},
noReviewersAssignedSvg,
};
</script>
<template>
<div>
<div class="gl-display-flex gl-mb-3">
<template v-if="loadingReviewers">
<div class="gl-animate-skeleton-loader gl-h-4 gl-rounded-base gl-w-20"></div>
<div class="gl-animate-skeleton-loader gl-h-4 gl-rounded-base gl-w-2 gl-ml-auto"></div>
</template>
<div v-else class="gl-line-height-20 gl-text-gray-900 gl-font-weight-bold">
{{ reviewersTitle }}
</div>
</div>
<div v-if="loadingReviewers">
<div class="gl-animate-skeleton-loader gl-h-4 gl-rounded-base gl-mb-3 gl-max-w-20!"></div>
<div class="gl-animate-skeleton-loader gl-h-4 gl-rounded-base gl-mb-3 gl-max-w-20!"></div>
<div class="gl-animate-skeleton-loader gl-h-4 gl-rounded-base gl-max-w-20!"></div>
</div>
<uncollapsed-reviewer-list
v-else-if="reviewers.length"
:root-path="relativeUrlRoot"
:users="reviewers"
/>
<gl-empty-state v-else :svg-path="$options.noReviewersAssignedSvg" :svg-height="70">
<template #description>
<p class="gl-font-weight-normal gl-mb-3">{{ __('No reviewers assigned') }}</p>
<update-reviewers
v-if="userPermissions.adminMergeRequest"
:selected-reviewers="currentUser"
>
<template #default="{ loading, updateReviewers }">
<gl-button category="tertiary" size="small" :loading="loading" @click="updateReviewers">
{{ __('Assign yourself') }}
</gl-button>
</template>
</update-reviewers>
</template>
</gl-empty-state>
</div>
</template>
<script>
import setReviewersMutation from './queries/set_reviewers.mutation.graphql';
export default {
inject: ['projectPath', 'issuableIid'],
props: {
selectedReviewers: {
type: Array,
required: true,
},
},
data() {
return {
loading: false,
};
},
methods: {
updateReviewers() {
this.loading = true;
return this.$apollo
.mutate({
mutation: setReviewersMutation,
variables: {
reviewerUsernames: this.selectedReviewers,
projectPath: this.projectPath,
iid: this.issuableIid,
},
})
.finally(() => {
this.loading = false;
});
},
},
render() {
return this.$scopedSlots.default({
loading: this.loading,
updateReviewers: this.updateReviewers,
});
},
};
</script>
......@@ -53,7 +53,7 @@ export default {
:data-username="user.username"
:data-cannot-merge="cannotMerge"
data-placement="left"
class="gl-display-inline-block js-user-link"
class="gl-display-inline-block js-user-link gl-reset-color! gl-hover-text-blue-800!"
>
<!-- use d-flex so that slot can be appropriately styled -->
<span class="gl-display-flex">
......
......@@ -648,6 +648,14 @@ msgid_plural "%{count} Participants"
msgstr[0] ""
msgstr[1] ""
 
msgid "%{count} Reviewer"
msgid_plural "%{count} Reviewers"
msgstr[0] ""
msgstr[1] ""
msgid "%{count} Reviewers"
msgstr ""
msgid "%{count} approval required from %{name}"
msgid_plural "%{count} approvals required from %{name}"
msgstr[0] ""
......@@ -7040,6 +7048,9 @@ msgstr ""
msgid "Assign to me"
msgstr ""
 
msgid "Assign yourself"
msgstr ""
msgid "Assigned"
msgstr ""
 
......@@ -34244,6 +34255,9 @@ msgstr ""
msgid "No results found."
msgstr ""
 
msgid "No reviewers assigned"
msgstr ""
msgid "No runner executable"
msgstr ""
 
import Vue from 'vue';
import VueApollo from 'vue-apollo';
import { GlLoadingIcon } from '@gitlab/ui';
import { mountExtended } from 'helpers/vue_test_utils_helper';
import { setHTMLFixture, resetHTMLFixture } from 'helpers/fixtures';
import { mockTracking, triggerEvent } from 'helpers/tracking_helper';
import waitForPromises from 'helpers/wait_for_promises';
import createMockApollo from 'helpers/mock_apollo_helper';
import Component from '~/sidebar/components/reviewers/reviewer_title.vue';
import getMergeRequestReviewers from '~/sidebar/queries/get_merge_request_reviewers.query.graphql';
import userPermissionsQuery from '~/merge_requests/components/reviewers/queries/user_permissions.query.graphql';
Vue.use(VueApollo);
describe('ReviewerTitle component', () => {
let wrapper;
......@@ -11,7 +18,13 @@ describe('ReviewerTitle component', () => {
const findEditButton = () => wrapper.findByTestId('reviewers-edit-button');
const createComponent = (props, { reviewerAssignDrawer = false } = {}) => {
const apolloMock = createMockApollo([
[getMergeRequestReviewers, jest.fn().mockResolvedValue({ data: { workspace: null } })],
[userPermissionsQuery, jest.fn().mockResolvedValue({ data: { project: null } })],
]);
return mountExtended(Component, {
apolloProvider: apolloMock,
propsData: {
numberOfReviewers: 0,
editable: false,
......@@ -26,6 +39,7 @@ describe('ReviewerTitle component', () => {
reviewerAssignDrawer,
},
},
stubs: ['approval-summary'],
});
};
......@@ -121,7 +135,7 @@ describe('ReviewerTitle component', () => {
resetHTMLFixture();
});
it('sets title for dropdown toggle as `Quick assign`', () => {
it('sets title for dropdown toggle as `Quick assign`', async () => {
wrapper = createComponent(
{
editable: true,
......@@ -129,6 +143,8 @@ describe('ReviewerTitle component', () => {
{ reviewerAssignDrawer: true },
);
await waitForPromises();
expect(findEditButton().attributes('title')).toBe('Quick assign');
});
......
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