summaryrefslogtreecommitdiff
path: root/app/assets/javascripts/sidebar/components
diff options
context:
space:
mode:
Diffstat (limited to 'app/assets/javascripts/sidebar/components')
-rw-r--r--app/assets/javascripts/sidebar/components/assignees/issuable_assignees.vue9
-rw-r--r--app/assets/javascripts/sidebar/components/assignees/sidebar_assignees_widget.vue50
-rw-r--r--app/assets/javascripts/sidebar/components/assignees/uncollapsed_assignee_list.vue2
-rw-r--r--app/assets/javascripts/sidebar/components/confidential/confidential_issue_sidebar.vue113
-rw-r--r--app/assets/javascripts/sidebar/components/confidential/edit_form.vue64
-rw-r--r--app/assets/javascripts/sidebar/components/confidential/edit_form_buttons.vue81
-rw-r--r--app/assets/javascripts/sidebar/components/confidential/mutations/update_issue_confidential.mutation.graphql8
-rw-r--r--app/assets/javascripts/sidebar/components/confidential/sidebar_confidentiality_content.vue64
-rw-r--r--app/assets/javascripts/sidebar/components/confidential/sidebar_confidentiality_form.vue136
-rw-r--r--app/assets/javascripts/sidebar/components/confidential/sidebar_confidentiality_widget.vue143
-rw-r--r--app/assets/javascripts/sidebar/components/reviewers/uncollapsed_reviewer_list.vue16
-rw-r--r--app/assets/javascripts/sidebar/components/sidebar_editable_item.vue37
-rw-r--r--app/assets/javascripts/sidebar/components/subscriptions/subscriptions.vue2
-rw-r--r--app/assets/javascripts/sidebar/components/time_tracking/help_state.vue6
14 files changed, 436 insertions, 295 deletions
diff --git a/app/assets/javascripts/sidebar/components/assignees/issuable_assignees.vue b/app/assets/javascripts/sidebar/components/assignees/issuable_assignees.vue
index e2dc37a0ac2..b53b7039018 100644
--- a/app/assets/javascripts/sidebar/components/assignees/issuable_assignees.vue
+++ b/app/assets/javascripts/sidebar/components/assignees/issuable_assignees.vue
@@ -31,13 +31,18 @@ export default {
</script>
<template>
- <div class="gl-display-flex gl-flex-direction-column">
- <div v-if="emptyUsers" data-testid="none">
+ <div class="gl-display-flex gl-flex-direction-column issuable-assignees">
+ <div
+ v-if="emptyUsers"
+ class="gl-display-flex gl-align-items-center gl-text-gray-500"
+ data-testid="none"
+ >
<span> {{ __('None') }} -</span>
<gl-button
data-testid="assign-yourself"
category="tertiary"
variant="link"
+ class="gl-ml-2"
@click="$emit('assign-self')"
>
<span class="gl-text-gray-500 gl-hover-text-blue-800">{{ __('assign yourself') }}</span>
diff --git a/app/assets/javascripts/sidebar/components/assignees/sidebar_assignees_widget.vue b/app/assets/javascripts/sidebar/components/assignees/sidebar_assignees_widget.vue
index 8f3f77cb5f0..cc2201ad359 100644
--- a/app/assets/javascripts/sidebar/components/assignees/sidebar_assignees_widget.vue
+++ b/app/assets/javascripts/sidebar/components/assignees/sidebar_assignees_widget.vue
@@ -15,13 +15,12 @@ import { IssuableType } from '~/issue_show/constants';
import { __, n__ } from '~/locale';
import IssuableAssignees from '~/sidebar/components/assignees/issuable_assignees.vue';
import SidebarEditableItem from '~/sidebar/components/sidebar_editable_item.vue';
-import { assigneesQueries } from '~/sidebar/constants';
+import { assigneesQueries, ASSIGNEES_DEBOUNCE_DELAY } from '~/sidebar/constants';
import MultiSelectDropdown from '~/vue_shared/components/sidebar/multiselect_dropdown.vue';
export const assigneesWidget = Vue.observable({
updateAssignees: null,
});
-
export default {
i18n: {
unassigned: __('Unassigned'),
@@ -88,10 +87,10 @@ export default {
return this.queryVariables;
},
update(data) {
- return data.issuable || data.project?.issuable;
+ return data.workspace?.issuable;
},
result({ data }) {
- const issuable = data.issuable || data.project?.issuable;
+ const issuable = data.workspace?.issuable;
if (issuable) {
this.selected = this.moveCurrentUserToStart(cloneDeep(issuable.assignees.nodes));
}
@@ -104,13 +103,24 @@ export default {
query: searchUsers,
variables() {
return {
+ fullPath: this.fullPath,
search: this.search,
};
},
update(data) {
- return data.users?.nodes || [];
+ const searchResults = data.workspace?.users?.nodes.map(({ user }) => user) || [];
+ const mergedSearchResults = this.participants.reduce((acc, current) => {
+ if (
+ !acc.some((user) => current.username === user.username) &&
+ (current.name.includes(this.search) || current.username.includes(this.search))
+ ) {
+ acc.push(current);
+ }
+ return acc;
+ }, searchResults);
+ return mergedSearchResults;
},
- debounce: 250,
+ debounce: ASSIGNEES_DEBOUNCE_DELAY,
skip() {
return this.isSearchEmpty;
},
@@ -185,7 +195,7 @@ export default {
return this.selected.some(isCurrentUser) || this.participants.some(isCurrentUser);
},
noUsersFound() {
- return !this.isSearchEmpty && this.unselectedFiltered.length === 0;
+ return !this.isSearchEmpty && this.searchUsers.length === 0;
},
showCurrentUser() {
return !this.isCurrentUserInParticipants && (this.isSearchEmpty || this.isSearching);
@@ -218,7 +228,7 @@ export default {
},
})
.then(({ data }) => {
- this.$emit('assignees-updated', data);
+ this.$emit('assignees-updated', data.issuableSetAssignees.issuable.assignees.nodes);
return data;
})
.catch(() => {
@@ -281,6 +291,9 @@ export default {
collapseWidget() {
this.$refs.toggle.collapse();
},
+ showDivider(list) {
+ return list.length > 0 && this.isSearchEmpty;
+ },
},
};
</script>
@@ -306,6 +319,7 @@ export default {
<issuable-assignees
:users="assignees"
:issuable-type="issuableType"
+ class="gl-mt-2"
@assign-self="assignSelf"
/>
</template>
@@ -334,12 +348,14 @@ export default {
data-testid="unassign"
@click="selectAssignee()"
>
- <span :class="selectedIsEmpty ? 'gl-pl-0' : 'gl-pl-6'">{{
- $options.i18n.unassigned
- }}</span></gl-dropdown-item
+ <span
+ :class="selectedIsEmpty ? 'gl-pl-0' : 'gl-pl-6'"
+ class="gl-font-weight-bold"
+ >{{ $options.i18n.unassigned }}</span
+ ></gl-dropdown-item
>
- <gl-dropdown-divider data-testid="unassign-divider" />
</template>
+ <gl-dropdown-divider v-if="showDivider(selectedFiltered)" />
<gl-dropdown-item
v-for="item in selectedFiltered"
:key="item.id"
@@ -358,10 +374,10 @@ export default {
/>
</gl-avatar-link>
</gl-dropdown-item>
- <gl-dropdown-divider v-if="!selectedIsEmpty" data-testid="selected-user-divider" />
<template v-if="showCurrentUser">
+ <gl-dropdown-divider />
<gl-dropdown-item
- data-testid="unselected-participant"
+ data-testid="current-user"
@click.stop="selectAssignee(currentUser)"
>
<gl-avatar-link>
@@ -370,12 +386,12 @@ export default {
:label="currentUser.name"
:sub-label="currentUser.username"
:src="currentUser.avatarUrl"
- class="gl-align-items-center"
+ class="gl-align-items-center gl-pl-6!"
/>
</gl-avatar-link>
</gl-dropdown-item>
- <gl-dropdown-divider />
</template>
+ <gl-dropdown-divider v-if="showDivider(unselectedFiltered)" />
<gl-dropdown-item
v-for="unselectedUser in unselectedFiltered"
:key="unselectedUser.id"
@@ -392,7 +408,7 @@ export default {
/>
</gl-avatar-link>
</gl-dropdown-item>
- <gl-dropdown-item v-if="noUsersFound && !isSearching">
+ <gl-dropdown-item v-if="noUsersFound && !isSearching" data-testid="empty-results">
{{ __('No matching results') }}
</gl-dropdown-item>
</template>
diff --git a/app/assets/javascripts/sidebar/components/assignees/uncollapsed_assignee_list.vue b/app/assets/javascripts/sidebar/components/assignees/uncollapsed_assignee_list.vue
index 36775648809..d0da4a9c75a 100644
--- a/app/assets/javascripts/sidebar/components/assignees/uncollapsed_assignee_list.vue
+++ b/app/assets/javascripts/sidebar/components/assignees/uncollapsed_assignee_list.vue
@@ -83,7 +83,7 @@ export default {
<assignee-avatar-link :user="user" :issuable-type="issuableType" />
</div>
</div>
- <div v-if="renderShowMoreSection" class="user-list-more">
+ <div v-if="renderShowMoreSection" class="user-list-more gl-hover-text-blue-800">
<button
type="button"
class="btn-link"
diff --git a/app/assets/javascripts/sidebar/components/confidential/confidential_issue_sidebar.vue b/app/assets/javascripts/sidebar/components/confidential/confidential_issue_sidebar.vue
deleted file mode 100644
index 57b3705e803..00000000000
--- a/app/assets/javascripts/sidebar/components/confidential/confidential_issue_sidebar.vue
+++ /dev/null
@@ -1,113 +0,0 @@
-<script>
-import { GlIcon, GlTooltipDirective } from '@gitlab/ui';
-import { mapState } from 'vuex';
-import { __, sprintf } from '~/locale';
-import eventHub from '~/sidebar/event_hub';
-import EditForm from './edit_form.vue';
-
-export default {
- components: {
- EditForm,
- GlIcon,
- },
- directives: {
- GlTooltip: GlTooltipDirective,
- },
- props: {
- fullPath: {
- required: true,
- type: String,
- },
- isEditable: {
- required: true,
- type: Boolean,
- },
- issuableType: {
- required: false,
- type: String,
- default: 'issue',
- },
- },
- data() {
- return {
- edit: false,
- };
- },
- computed: {
- ...mapState({
- confidential: ({ noteableData, confidential }) => {
- if (noteableData) {
- return noteableData.confidential;
- }
- return Boolean(confidential);
- },
- }),
- confidentialityIcon() {
- return this.confidential ? 'eye-slash' : 'eye';
- },
- tooltipLabel() {
- return this.confidential ? __('Confidential') : __('Not confidential');
- },
- confidentialText() {
- return sprintf(__('This %{issuableType} is confidential'), {
- issuableType: this.issuableType,
- });
- },
- },
- created() {
- eventHub.$on('closeConfidentialityForm', this.toggleForm);
- },
- beforeDestroy() {
- eventHub.$off('closeConfidentialityForm', this.toggleForm);
- },
- methods: {
- toggleForm() {
- this.edit = !this.edit;
- },
- },
-};
-</script>
-
-<template>
- <div class="block issuable-sidebar-item confidentiality">
- <div
- ref="collapseIcon"
- v-gl-tooltip.viewport.left
- :title="tooltipLabel"
- class="sidebar-collapsed-icon"
- @click="toggleForm"
- >
- <gl-icon :name="confidentialityIcon" />
- </div>
- <div class="title hide-collapsed">
- {{ __('Confidentiality') }}
- <a
- v-if="isEditable"
- ref="editLink"
- class="float-right confidential-edit"
- href="#"
- data-track-event="click_edit_button"
- data-track-label="right_sidebar"
- data-track-property="confidentiality"
- @click.prevent="toggleForm"
- >{{ __('Edit') }}</a
- >
- </div>
- <div class="value sidebar-item-value hide-collapsed">
- <edit-form
- v-if="edit"
- :confidential="confidential"
- :full-path="fullPath"
- :issuable-type="issuableType"
- />
- <div v-if="!confidential" class="no-value sidebar-item-value" data-testid="not-confidential">
- <gl-icon :size="16" name="eye" class="sidebar-item-icon inline" />
- {{ __('Not confidential') }}
- </div>
- <div v-else class="value sidebar-item-value hide-collapsed">
- <gl-icon :size="16" name="eye-slash" class="sidebar-item-icon inline is-active" />
- {{ confidentialText }}
- </div>
- </div>
- </div>
-</template>
diff --git a/app/assets/javascripts/sidebar/components/confidential/edit_form.vue b/app/assets/javascripts/sidebar/components/confidential/edit_form.vue
deleted file mode 100644
index 057224d5918..00000000000
--- a/app/assets/javascripts/sidebar/components/confidential/edit_form.vue
+++ /dev/null
@@ -1,64 +0,0 @@
-<script>
-import { GlSprintf } from '@gitlab/ui';
-import { __ } from '../../../locale';
-import editFormButtons from './edit_form_buttons.vue';
-
-export default {
- components: {
- editFormButtons,
- GlSprintf,
- },
- props: {
- confidential: {
- required: true,
- type: Boolean,
- },
- fullPath: {
- required: true,
- type: String,
- },
- issuableType: {
- required: true,
- type: String,
- },
- },
- computed: {
- confidentialityOnWarning() {
- return __(
- 'You are going to turn on the confidentiality. This means that only team members with %{strongStart}at least Reporter access%{strongEnd} are able to see and leave comments on the %{issuableType}.',
- );
- },
- confidentialityOffWarning() {
- return __(
- 'You are going to turn off the confidentiality. This means %{strongStart}everyone%{strongEnd} will be able to see and leave a comment on this %{issuableType}.',
- );
- },
- },
-};
-</script>
-
-<template>
- <div class="dropdown show">
- <div class="dropdown-menu sidebar-item-warning-message">
- <div>
- <p v-if="!confidential">
- <gl-sprintf :message="confidentialityOnWarning">
- <template #strong="{ content }">
- <strong>{{ content }}</strong>
- </template>
- <template #issuableType>{{ issuableType }}</template>
- </gl-sprintf>
- </p>
- <p v-else>
- <gl-sprintf :message="confidentialityOffWarning">
- <template #strong="{ content }">
- <strong>{{ content }}</strong>
- </template>
- <template #issuableType>{{ issuableType }}</template>
- </gl-sprintf>
- </p>
- <edit-form-buttons :full-path="fullPath" :confidential="confidential" />
- </div>
- </div>
- </div>
-</template>
diff --git a/app/assets/javascripts/sidebar/components/confidential/edit_form_buttons.vue b/app/assets/javascripts/sidebar/components/confidential/edit_form_buttons.vue
deleted file mode 100644
index 154a228c978..00000000000
--- a/app/assets/javascripts/sidebar/components/confidential/edit_form_buttons.vue
+++ /dev/null
@@ -1,81 +0,0 @@
-<script>
-import { GlButton } from '@gitlab/ui';
-import $ from 'jquery';
-import { mapActions } from 'vuex';
-import { deprecatedCreateFlash as Flash } from '~/flash';
-import { __ } from '~/locale';
-import eventHub from '../../event_hub';
-
-export default {
- components: {
- GlButton,
- },
- props: {
- fullPath: {
- required: true,
- type: String,
- },
- confidential: {
- required: true,
- type: Boolean,
- },
- },
- data() {
- return {
- isLoading: false,
- };
- },
- computed: {
- toggleButtonText() {
- if (this.isLoading) {
- return __('Applying');
- }
-
- return this.confidential ? __('Turn Off') : __('Turn On');
- },
- },
- methods: {
- ...mapActions(['updateConfidentialityOnIssuable']),
- closeForm() {
- eventHub.$emit('closeConfidentialityForm');
- $(this.$el).trigger('hidden.gl.dropdown');
- },
- submitForm() {
- this.isLoading = true;
- const confidential = !this.confidential;
-
- this.updateConfidentialityOnIssuable({ confidential, fullPath: this.fullPath })
- .then(() => {
- eventHub.$emit('updateIssuableConfidentiality', confidential);
- })
- .catch((err) => {
- Flash(
- err || __('Something went wrong trying to change the confidentiality of this issue'),
- );
- })
- .finally(() => {
- this.closeForm();
- this.isLoading = false;
- });
- },
- },
-};
-</script>
-
-<template>
- <div class="sidebar-item-warning-message-actions">
- <gl-button class="gl-mr-3" @click="closeForm">
- {{ __('Cancel') }}
- </gl-button>
- <gl-button
- category="secondary"
- variant="warning"
- :disabled="isLoading"
- :loading="isLoading"
- data-testid="confidential-toggle"
- @click.prevent="submitForm"
- >
- {{ toggleButtonText }}
- </gl-button>
- </div>
-</template>
diff --git a/app/assets/javascripts/sidebar/components/confidential/mutations/update_issue_confidential.mutation.graphql b/app/assets/javascripts/sidebar/components/confidential/mutations/update_issue_confidential.mutation.graphql
deleted file mode 100644
index 5caf5f6b555..00000000000
--- a/app/assets/javascripts/sidebar/components/confidential/mutations/update_issue_confidential.mutation.graphql
+++ /dev/null
@@ -1,8 +0,0 @@
-mutation updateIssueConfidential($input: IssueSetConfidentialInput!) {
- issueSetConfidential(input: $input) {
- issue {
- confidential
- }
- errors
- }
-}
diff --git a/app/assets/javascripts/sidebar/components/confidential/sidebar_confidentiality_content.vue b/app/assets/javascripts/sidebar/components/confidential/sidebar_confidentiality_content.vue
new file mode 100644
index 00000000000..37a44eb8f01
--- /dev/null
+++ b/app/assets/javascripts/sidebar/components/confidential/sidebar_confidentiality_content.vue
@@ -0,0 +1,64 @@
+<script>
+import { GlIcon, GlTooltipDirective } from '@gitlab/ui';
+import { __, sprintf } from '~/locale';
+
+export default {
+ components: {
+ GlIcon,
+ },
+ directives: {
+ GlTooltip: GlTooltipDirective,
+ },
+ props: {
+ confidential: {
+ type: Boolean,
+ required: true,
+ },
+ issuableType: {
+ type: String,
+ required: true,
+ },
+ },
+ computed: {
+ confidentialText() {
+ return this.confidential
+ ? sprintf(__('This %{issuableType} is confidential'), {
+ issuableType: this.issuableType,
+ })
+ : __('Not confidential');
+ },
+ confidentialIcon() {
+ return this.confidential ? 'eye-slash' : 'eye';
+ },
+ tooltipLabel() {
+ return this.confidential ? __('Confidential') : __('Not confidential');
+ },
+ },
+};
+</script>
+
+<template>
+ <div>
+ <div
+ v-gl-tooltip.viewport.left
+ :title="tooltipLabel"
+ class="sidebar-collapsed-icon"
+ data-testid="sidebar-collapsed-icon"
+ @click="$emit('expandSidebar')"
+ >
+ <gl-icon
+ :size="16"
+ :name="confidentialIcon"
+ class="sidebar-item-icon inline"
+ :class="{ 'is-active': confidential }"
+ />
+ </div>
+ <gl-icon
+ :size="16"
+ :name="confidentialIcon"
+ class="sidebar-item-icon inline hide-collapsed"
+ :class="{ 'is-active': confidential }"
+ />
+ <span class="hide-collapsed" data-testid="confidential-text">{{ confidentialText }}</span>
+ </div>
+</template>
diff --git a/app/assets/javascripts/sidebar/components/confidential/sidebar_confidentiality_form.vue b/app/assets/javascripts/sidebar/components/confidential/sidebar_confidentiality_form.vue
new file mode 100644
index 00000000000..a21ac73f131
--- /dev/null
+++ b/app/assets/javascripts/sidebar/components/confidential/sidebar_confidentiality_form.vue
@@ -0,0 +1,136 @@
+<script>
+import { GlSprintf, GlButton } from '@gitlab/ui';
+import createFlash from '~/flash';
+import { IssuableType } from '~/issue_show/constants';
+import { __, sprintf } from '~/locale';
+import { confidentialityQueries } from '~/sidebar/constants';
+
+export default {
+ i18n: {
+ confidentialityOnWarning: __(
+ 'You are going to turn on confidentiality. Only team members with %{strongStart}at least Reporter access%{strongEnd} will be able to see and leave comments on the %{issuableType}.',
+ ),
+ confidentialityOffWarning: __(
+ 'You are going to turn off the confidentiality. This means %{strongStart}everyone%{strongEnd} will be able to see and leave a comment on this %{issuableType}.',
+ ),
+ },
+ components: {
+ GlSprintf,
+ GlButton,
+ },
+ inject: ['fullPath', 'iid'],
+ props: {
+ confidential: {
+ required: true,
+ type: Boolean,
+ },
+ issuableType: {
+ required: true,
+ type: String,
+ },
+ },
+ data() {
+ return {
+ loading: false,
+ };
+ },
+ computed: {
+ toggleButtonText() {
+ if (this.loading) {
+ return __('Applying');
+ }
+ return this.confidential ? __('Turn off') : __('Turn on');
+ },
+ warningMessage() {
+ return this.confidential
+ ? this.$options.i18n.confidentialityOffWarning
+ : this.$options.i18n.confidentialityOnWarning;
+ },
+ workspacePath() {
+ return this.issuableType === IssuableType.Issue
+ ? {
+ projectPath: this.fullPath,
+ }
+ : {
+ groupPath: this.fullPath,
+ };
+ },
+ },
+ methods: {
+ submitForm() {
+ this.loading = true;
+ this.$apollo
+ .mutate({
+ mutation: confidentialityQueries[this.issuableType].mutation,
+ variables: {
+ input: {
+ ...this.workspacePath,
+ iid: this.iid,
+ confidential: !this.confidential,
+ },
+ },
+ })
+ .then(
+ ({
+ data: {
+ issuableSetConfidential: { errors },
+ },
+ }) => {
+ if (errors.length) {
+ createFlash({
+ message: errors[0],
+ });
+ } else {
+ this.$emit('closeForm');
+ }
+ },
+ )
+ .catch(() => {
+ createFlash({
+ message: sprintf(
+ __('Something went wrong while setting %{issuableType} confidentiality.'),
+ {
+ issuableType: this.issuableType,
+ },
+ ),
+ });
+ })
+ .finally(() => {
+ this.loading = false;
+ });
+ },
+ },
+};
+</script>
+
+<template>
+ <div class="dropdown show">
+ <div class="dropdown-menu sidebar-item-warning-message">
+ <div>
+ <p data-testid="warning-message">
+ <gl-sprintf :message="warningMessage">
+ <template #strong="{ content }">
+ <strong>{{ content }}</strong>
+ </template>
+ <template #issuableType>{{ issuableType }}</template>
+ </gl-sprintf>
+ </p>
+ <div class="sidebar-item-warning-message-actions">
+ <gl-button class="gl-mr-3" data-testid="confidential-cancel" @click="$emit('closeForm')">
+ {{ __('Cancel') }}
+ </gl-button>
+ <gl-button
+ category="secondary"
+ variant="warning"
+ :disabled="loading"
+ :loading="loading"
+ data-testid="confidential-toggle"
+ @click.prevent="submitForm"
+ >
+ {{ toggleButtonText }}
+ </gl-button>
+ </div>
+ </div>
+ </div>
+ </div>
+</template>
diff --git a/app/assets/javascripts/sidebar/components/confidential/sidebar_confidentiality_widget.vue b/app/assets/javascripts/sidebar/components/confidential/sidebar_confidentiality_widget.vue
new file mode 100644
index 00000000000..1db68d3d5b1
--- /dev/null
+++ b/app/assets/javascripts/sidebar/components/confidential/sidebar_confidentiality_widget.vue
@@ -0,0 +1,143 @@
+<script>
+import produce from 'immer';
+import Vue from 'vue';
+import createFlash from '~/flash';
+import { __, sprintf } from '~/locale';
+import SidebarEditableItem from '~/sidebar/components/sidebar_editable_item.vue';
+import { confidentialityQueries } from '~/sidebar/constants';
+import SidebarConfidentialityContent from './sidebar_confidentiality_content.vue';
+import SidebarConfidentialityForm from './sidebar_confidentiality_form.vue';
+
+export const confidentialWidget = Vue.observable({
+ setConfidentiality: null,
+});
+
+const hideDropdownEvent = new CustomEvent('hiddenGlDropdown', {
+ bubbles: true,
+});
+
+export default {
+ tracking: {
+ event: 'click_edit_button',
+ label: 'right_sidebar',
+ property: 'confidentiality',
+ },
+ components: {
+ SidebarEditableItem,
+ SidebarConfidentialityContent,
+ SidebarConfidentialityForm,
+ },
+ inject: ['fullPath', 'iid'],
+ props: {
+ issuableType: {
+ required: true,
+ type: String,
+ },
+ },
+ data() {
+ return {
+ confidential: false,
+ };
+ },
+ apollo: {
+ confidential: {
+ query() {
+ return confidentialityQueries[this.issuableType].query;
+ },
+ variables() {
+ return {
+ fullPath: this.fullPath,
+ iid: String(this.iid),
+ };
+ },
+ update(data) {
+ return data.workspace?.issuable?.confidential || false;
+ },
+ result({ data }) {
+ this.$emit('confidentialityUpdated', data.workspace?.issuable?.confidential);
+ },
+ error() {
+ createFlash({
+ message: sprintf(
+ __('Something went wrong while setting %{issuableType} confidentiality.'),
+ {
+ issuableType: this.issuableType,
+ },
+ ),
+ });
+ },
+ },
+ },
+ computed: {
+ isLoading() {
+ return this.$apollo.queries.confidential.loading;
+ },
+ },
+ mounted() {
+ confidentialWidget.setConfidentiality = this.setConfidentiality;
+ },
+ destroyed() {
+ confidentialWidget.setConfidentiality = null;
+ },
+ methods: {
+ closeForm() {
+ this.$refs.editable.collapse();
+ this.$el.dispatchEvent(hideDropdownEvent);
+ this.$emit('closeForm');
+ },
+ // synchronizing the quick action with the sidebar widget
+ // this is a temporary solution until we have confidentiality real-time updates
+ setConfidentiality() {
+ const { defaultClient: client } = this.$apollo.provider.clients;
+ const sourceData = client.readQuery({
+ query: confidentialityQueries[this.issuableType].query,
+ variables: { fullPath: this.fullPath, iid: this.iid },
+ });
+
+ const data = produce(sourceData, (draftData) => {
+ // eslint-disable-next-line no-param-reassign
+ draftData.workspace.issuable.confidential = !this.confidential;
+ });
+
+ client.writeQuery({
+ query: confidentialityQueries[this.issuableType].query,
+ variables: { fullPath: this.fullPath, iid: this.iid },
+ data,
+ });
+ },
+ expandSidebar() {
+ this.$refs.editable.expand();
+ this.$emit('expandSidebar');
+ },
+ },
+};
+</script>
+
+<template>
+ <sidebar-editable-item
+ ref="editable"
+ :title="__('Confidentiality')"
+ :tracking="$options.tracking"
+ :loading="isLoading"
+ class="block confidentiality"
+ >
+ <template #collapsed>
+ <div>
+ <sidebar-confidentiality-content
+ v-if="!isLoading"
+ :confidential="confidential"
+ :issuable-type="issuableType"
+ @expandSidebar="expandSidebar"
+ />
+ </div>
+ </template>
+ <template #default>
+ <sidebar-confidentiality-content :confidential="confidential" :issuable-type="issuableType" />
+ <sidebar-confidentiality-form
+ :confidential="confidential"
+ :issuable-type="issuableType"
+ @closeForm="closeForm"
+ />
+ </template>
+ </sidebar-editable-item>
+</template>
diff --git a/app/assets/javascripts/sidebar/components/reviewers/uncollapsed_reviewer_list.vue b/app/assets/javascripts/sidebar/components/reviewers/uncollapsed_reviewer_list.vue
index cbd68f2513a..dd1d54d67f2 100644
--- a/app/assets/javascripts/sidebar/components/reviewers/uncollapsed_reviewer_list.vue
+++ b/app/assets/javascripts/sidebar/components/reviewers/uncollapsed_reviewer_list.vue
@@ -1,5 +1,6 @@
<script>
import { GlButton, GlTooltipDirective, GlIcon } from '@gitlab/ui';
+import { sprintf, s__ } from '~/locale';
import ReviewerAvatarLink from './reviewer_avatar_link.vue';
const LOADING_STATE = 'loading';
@@ -50,6 +51,9 @@ export default {
},
},
methods: {
+ approvedByTooltipTitle(user) {
+ return sprintf(s__('MergeRequest|Approved by @%{username}'), user);
+ },
toggleShowLess() {
this.showLess = !this.showLess;
},
@@ -57,6 +61,7 @@ export default {
this.loadingStates[userId] = LOADING_STATE;
this.$emit('request-review', { userId, callback: this.requestReviewComplete });
},
+
requestReviewComplete(userId, success) {
if (success) {
this.loadingStates[userId] = SUCCESS_STATE;
@@ -86,10 +91,19 @@ export default {
<div class="gl-ml-3">@{{ user.username }}</div>
</reviewer-avatar-link>
<gl-icon
+ v-if="user.approved"
+ v-gl-tooltip.left
+ :size="16"
+ :title="approvedByTooltipTitle(user)"
+ name="status-success"
+ class="float-right gl-my-2 gl-ml-2 gl-text-green-500"
+ data-testid="re-approved"
+ />
+ <gl-icon
v-if="loadingStates[user.id] === $options.SUCCESS_STATE"
:size="24"
name="check"
- class="float-right gl-text-green-500"
+ class="float-right gl-py-2 gl-mr-2 gl-text-green-500"
data-testid="re-request-success"
/>
<gl-button
diff --git a/app/assets/javascripts/sidebar/components/sidebar_editable_item.vue b/app/assets/javascripts/sidebar/components/sidebar_editable_item.vue
index 9da839cd133..4ab4606ac1c 100644
--- a/app/assets/javascripts/sidebar/components/sidebar_editable_item.vue
+++ b/app/assets/javascripts/sidebar/components/sidebar_editable_item.vue
@@ -3,7 +3,12 @@ import { GlButton, GlLoadingIcon } from '@gitlab/ui';
export default {
components: { GlButton, GlLoadingIcon },
- inject: ['canUpdate'],
+ inject: {
+ canUpdate: {},
+ isClassicSidebar: {
+ default: false,
+ },
+ },
props: {
title: {
type: String,
@@ -15,6 +20,15 @@ export default {
required: false,
default: false,
},
+ tracking: {
+ type: Object,
+ required: false,
+ default: () => ({
+ event: null,
+ label: null,
+ property: null,
+ }),
+ },
},
data() {
return {
@@ -71,24 +85,33 @@ export default {
<template>
<div>
- <div class="gl-display-flex gl-align-items-center gl-mb-3" @click.self="collapse">
- <span data-testid="title">{{ title }}</span>
- <gl-loading-icon v-if="loading" inline class="gl-ml-2" />
+ <div class="gl-display-flex gl-align-items-center" @click.self="collapse">
+ <span class="hide-collapsed" data-testid="title">{{ title }}</span>
+ <gl-loading-icon v-if="loading" inline class="gl-ml-2 hide-collapsed" />
+ <gl-loading-icon
+ v-if="loading && isClassicSidebar"
+ inline
+ class="gl-mx-auto gl-my-0 hide-expanded"
+ />
<gl-button
v-if="canUpdate"
variant="link"
- class="gl-text-gray-900! gl-hover-text-blue-800! gl-ml-auto js-sidebar-dropdown-toggle"
+ class="gl-text-gray-900! gl-hover-text-blue-800! gl-ml-auto hide-collapsed"
data-testid="edit-button"
+ :data-track-event="tracking.event"
+ :data-track-label="tracking.label"
+ :data-track-property="tracking.property"
+ data-qa-selector="edit_link"
@keyup.esc="toggle"
@click="toggle"
>
{{ __('Edit') }}
</gl-button>
</div>
- <div v-show="!edit" class="gl-text-gray-500" data-testid="collapsed-content">
+ <div v-show="!edit" data-testid="collapsed-content">
<slot name="collapsed">{{ __('None') }}</slot>
</div>
- <div v-show="edit" data-testid="expanded-content">
+ <div v-show="edit" data-testid="expanded-content" :class="{ 'gl-mt-3': !isClassicSidebar }">
<slot :edit="edit"></slot>
</div>
</div>
diff --git a/app/assets/javascripts/sidebar/components/subscriptions/subscriptions.vue b/app/assets/javascripts/sidebar/components/subscriptions/subscriptions.vue
index 9b06c20a6f3..c0424dc2873 100644
--- a/app/assets/javascripts/sidebar/components/subscriptions/subscriptions.vue
+++ b/app/assets/javascripts/sidebar/components/subscriptions/subscriptions.vue
@@ -122,6 +122,8 @@ export default {
:value="subscribed"
class="hide-collapsed"
data-testid="subscription-toggle"
+ :label="__('Notifications')"
+ label-position="hidden"
@change="toggleSubscription"
/>
</div>
diff --git a/app/assets/javascripts/sidebar/components/time_tracking/help_state.vue b/app/assets/javascripts/sidebar/components/time_tracking/help_state.vue
index e0f60b9af08..d1a5685fdd3 100644
--- a/app/assets/javascripts/sidebar/components/time_tracking/help_state.vue
+++ b/app/assets/javascripts/sidebar/components/time_tracking/help_state.vue
@@ -1,10 +1,14 @@
<script>
/* eslint-disable vue/no-v-html */
+import { GlButton } from '@gitlab/ui';
import { joinPaths } from '~/lib/utils/url_utility';
import { sprintf, s__ } from '../../../locale';
export default {
name: 'TimeTrackingHelpState',
+ components: {
+ GlButton,
+ },
computed: {
href() {
return joinPaths(gon.relative_url_root || '', '/help/user/project/time_tracking.md');
@@ -40,7 +44,7 @@ export default {
<p>{{ __('Quick actions can be used in the issues description and comment boxes.') }}</p>
<p v-html="estimateText"></p>
<p v-html="spendText"></p>
- <a :href="href" class="btn btn-default learn-more-button"> {{ __('Learn more') }} </a>
+ <gl-button :href="href">{{ __('Learn more') }}</gl-button>
</div>
</div>
</template>