summaryrefslogtreecommitdiff
path: root/app/assets/javascripts/vue_shared/components/sidebar
diff options
context:
space:
mode:
Diffstat (limited to 'app/assets/javascripts/vue_shared/components/sidebar')
-rw-r--r--app/assets/javascripts/vue_shared/components/sidebar/copyable_field.vue88
-rw-r--r--app/assets/javascripts/vue_shared/components/sidebar/issuable_move_dropdown.vue1
-rw-r--r--app/assets/javascripts/vue_shared/components/sidebar/labels_select_vue/labels_select_root.vue48
-rw-r--r--app/assets/javascripts/vue_shared/components/sidebar/multiselect_dropdown.vue3
-rw-r--r--app/assets/javascripts/vue_shared/components/sidebar/queries/get_issue_participants.query.graphql3
-rw-r--r--app/assets/javascripts/vue_shared/components/sidebar/queries/get_mr_participants.query.graphql3
-rw-r--r--app/assets/javascripts/vue_shared/components/sidebar/queries/update_issue_assignees.mutation.graphql3
-rw-r--r--app/assets/javascripts/vue_shared/components/sidebar/queries/update_mr_assignees.mutation.graphql3
8 files changed, 142 insertions, 10 deletions
diff --git a/app/assets/javascripts/vue_shared/components/sidebar/copyable_field.vue b/app/assets/javascripts/vue_shared/components/sidebar/copyable_field.vue
new file mode 100644
index 00000000000..bbc7e6e7a6e
--- /dev/null
+++ b/app/assets/javascripts/vue_shared/components/sidebar/copyable_field.vue
@@ -0,0 +1,88 @@
+<script>
+import { GlLoadingIcon } from '@gitlab/ui';
+import { s__, __, sprintf } from '~/locale';
+import ClipboardButton from '~/vue_shared/components/clipboard_button.vue';
+
+/**
+ * Renders an inline field, whose value can be copied to the clipboard,
+ * for use in the GitLab sidebar (issues, MRs, etc.).
+ */
+export default {
+ name: 'CopyableField',
+ components: {
+ GlLoadingIcon,
+ ClipboardButton,
+ },
+ props: {
+ value: {
+ type: String,
+ required: true,
+ },
+ name: {
+ type: String,
+ required: true,
+ },
+ isLoading: {
+ type: Boolean,
+ required: false,
+ default: false,
+ },
+ clipboardTooltipText: {
+ type: String,
+ required: false,
+ default: undefined,
+ },
+ },
+ computed: {
+ clipboardProps() {
+ return {
+ category: 'tertiary',
+ tooltipBoundary: 'viewport',
+ tooltipPlacement: 'left',
+ text: this.value,
+ title:
+ this.clipboardTooltipText ||
+ sprintf(this.$options.i18n.clipboardTooltip, { name: this.name }),
+ };
+ },
+ loadingIconLabel() {
+ return sprintf(this.$options.i18n.loadingIconLabel, { name: this.name });
+ },
+ templateText() {
+ return sprintf(this.$options.i18n.templateText, {
+ name: this.name,
+ value: this.value,
+ });
+ },
+ },
+ i18n: {
+ loadingIconLabel: __('Loading %{name}'),
+ clipboardTooltip: __('Copy %{name}'),
+ templateText: s__('Sidebar|%{name}: %{value}'),
+ },
+};
+</script>
+
+<template>
+ <div>
+ <clipboard-button
+ v-if="!isLoading"
+ css-class="sidebar-collapsed-icon dont-change-state gl-rounded-0! gl-hover-bg-transparent"
+ v-bind="clipboardProps"
+ />
+
+ <div
+ class="gl-display-flex gl-align-items-center gl-justify-content-space-between hide-collapsed"
+ >
+ <span
+ class="gl-overflow-hidden gl-text-overflow-ellipsis gl-white-space-nowrap"
+ :title="value"
+ >
+ {{ templateText }}
+ </span>
+
+ <gl-loading-icon v-if="isLoading" inline :label="loadingIconLabel" />
+ <clipboard-button v-else size="small" v-bind="clipboardProps" />
+ </div>
+ </div>
+</template>
diff --git a/app/assets/javascripts/vue_shared/components/sidebar/issuable_move_dropdown.vue b/app/assets/javascripts/vue_shared/components/sidebar/issuable_move_dropdown.vue
index 1d3bd312b09..320e2048f1c 100644
--- a/app/assets/javascripts/vue_shared/components/sidebar/issuable_move_dropdown.vue
+++ b/app/assets/javascripts/vue_shared/components/sidebar/issuable_move_dropdown.vue
@@ -164,6 +164,7 @@ export default {
variant="link"
icon="close"
class="gl-mr-2 gl-w-auto! gl-p-2!"
+ :aria-label="__('Close')"
@click.prevent="handleDropdownCloseClick"
/>
</div>
diff --git a/app/assets/javascripts/vue_shared/components/sidebar/labels_select_vue/labels_select_root.vue b/app/assets/javascripts/vue_shared/components/sidebar/labels_select_vue/labels_select_root.vue
index 426ae430ce7..f547433f322 100644
--- a/app/assets/javascripts/vue_shared/components/sidebar/labels_select_vue/labels_select_root.vue
+++ b/app/assets/javascripts/vue_shared/components/sidebar/labels_select_vue/labels_select_root.vue
@@ -172,9 +172,11 @@ export default {
after: this.handleVuexActionDispatch,
});
+ document.addEventListener('mousedown', this.handleDocumentMousedown);
document.addEventListener('click', this.handleDocumentClick);
},
beforeDestroy() {
+ document.removeEventListener('mousedown', this.handleDocumentMousedown);
document.removeEventListener('click', this.handleDocumentClick);
},
methods: {
@@ -197,11 +199,36 @@ export default {
}
},
/**
+ * This method stores a mousedown event's target.
+ * Required by the click listener because the click
+ * event itself has no reference to this element.
+ */
+ handleDocumentMousedown({ target }) {
+ this.mousedownTarget = target;
+ },
+ /**
* This method listens for document-wide click event
* and toggle dropdown if user clicks anywhere outside
* the dropdown while dropdown is visible.
*/
handleDocumentClick({ target }) {
+ // We also perform the toggle exception check for the
+ // last mousedown event's target to avoid hiding the
+ // box when the mousedown happened inside the box and
+ // only the mouseup did not.
+ if (
+ this.showDropdownContents &&
+ !this.preventDropdownToggleOnClick(target) &&
+ !this.preventDropdownToggleOnClick(this.mousedownTarget)
+ ) {
+ this.toggleDropdownContents();
+ }
+ },
+ /**
+ * This method checks whether a given click target
+ * should prevent the dropdown from being toggled.
+ */
+ preventDropdownToggleOnClick(target) {
// This approach of element detection is needed
// as the dropdown wrapper is not using `GlDropdown` as
// it will also require us to use `BDropdownForm`
@@ -216,19 +243,20 @@ export default {
target?.parentElement?.classList.contains(className),
);
- const hadExceptionParent = ['.js-btn-back', '.js-labels-list'].some(
+ const hasExceptionParent = ['.js-btn-back', '.js-labels-list'].some(
(className) => $(target).parents(className).length,
);
- if (
- this.showDropdownContents &&
- !hadExceptionParent &&
- !hasExceptionClass &&
- !this.$refs.dropdownButtonCollapsed?.$el.contains(target) &&
- !this.$refs.dropdownContents?.$el.contains(target)
- ) {
- this.toggleDropdownContents();
- }
+ const isInDropdownButtonCollapsed = this.$refs.dropdownButtonCollapsed?.$el.contains(target);
+
+ const isInDropdownContents = this.$refs.dropdownContents?.$el.contains(target);
+
+ return (
+ hasExceptionClass ||
+ hasExceptionParent ||
+ isInDropdownButtonCollapsed ||
+ isInDropdownContents
+ );
},
handleDropdownClose(labels) {
// Only emit label updates if there are any labels to update
diff --git a/app/assets/javascripts/vue_shared/components/sidebar/multiselect_dropdown.vue b/app/assets/javascripts/vue_shared/components/sidebar/multiselect_dropdown.vue
index ef5f052527b..17904f20341 100644
--- a/app/assets/javascripts/vue_shared/components/sidebar/multiselect_dropdown.vue
+++ b/app/assets/javascripts/vue_shared/components/sidebar/multiselect_dropdown.vue
@@ -30,5 +30,8 @@ export default {
<gl-dropdown-form>
<slot name="items"></slot>
</gl-dropdown-form>
+ <template #footer>
+ <slot name="footer"></slot>
+ </template>
</gl-dropdown>
</template>
diff --git a/app/assets/javascripts/vue_shared/components/sidebar/queries/get_issue_participants.query.graphql b/app/assets/javascripts/vue_shared/components/sidebar/queries/get_issue_participants.query.graphql
index 459ea27e9cd..3885127fa8e 100644
--- a/app/assets/javascripts/vue_shared/components/sidebar/queries/get_issue_participants.query.graphql
+++ b/app/assets/javascripts/vue_shared/components/sidebar/queries/get_issue_participants.query.graphql
@@ -1,4 +1,5 @@
#import "~/graphql_shared/fragments/user.fragment.graphql"
+#import "~/graphql_shared/fragments/user_availability.fragment.graphql"
query issueParticipants($fullPath: ID!, $iid: String!) {
workspace: project(fullPath: $fullPath) {
@@ -9,11 +10,13 @@ query issueParticipants($fullPath: ID!, $iid: String!) {
participants {
nodes {
...User
+ ...UserAvailability
}
}
assignees {
nodes {
...User
+ ...UserAvailability
}
}
}
diff --git a/app/assets/javascripts/vue_shared/components/sidebar/queries/get_mr_participants.query.graphql b/app/assets/javascripts/vue_shared/components/sidebar/queries/get_mr_participants.query.graphql
index 43bd9f17e9a..63482873b69 100644
--- a/app/assets/javascripts/vue_shared/components/sidebar/queries/get_mr_participants.query.graphql
+++ b/app/assets/javascripts/vue_shared/components/sidebar/queries/get_mr_participants.query.graphql
@@ -1,4 +1,5 @@
#import "~/graphql_shared/fragments/user.fragment.graphql"
+#import "~/graphql_shared/fragments/user_availability.fragment.graphql"
query getMrParticipants($fullPath: ID!, $iid: String!) {
workspace: project(fullPath: $fullPath) {
@@ -7,11 +8,13 @@ query getMrParticipants($fullPath: ID!, $iid: String!) {
participants {
nodes {
...User
+ ...UserAvailability
}
}
assignees {
nodes {
...User
+ ...UserAvailability
}
}
}
diff --git a/app/assets/javascripts/vue_shared/components/sidebar/queries/update_issue_assignees.mutation.graphql b/app/assets/javascripts/vue_shared/components/sidebar/queries/update_issue_assignees.mutation.graphql
index 8ee8de2cb5c..3f40c0368d7 100644
--- a/app/assets/javascripts/vue_shared/components/sidebar/queries/update_issue_assignees.mutation.graphql
+++ b/app/assets/javascripts/vue_shared/components/sidebar/queries/update_issue_assignees.mutation.graphql
@@ -1,4 +1,5 @@
#import "~/graphql_shared/fragments/user.fragment.graphql"
+#import "~/graphql_shared/fragments/user_availability.fragment.graphql"
mutation issueSetAssignees($iid: String!, $assigneeUsernames: [String!]!, $fullPath: ID!) {
issuableSetAssignees: issueSetAssignees(
@@ -9,11 +10,13 @@ mutation issueSetAssignees($iid: String!, $assigneeUsernames: [String!]!, $fullP
assignees {
nodes {
...User
+ ...UserAvailability
}
}
participants {
nodes {
...User
+ ...UserAvailability
}
}
}
diff --git a/app/assets/javascripts/vue_shared/components/sidebar/queries/update_mr_assignees.mutation.graphql b/app/assets/javascripts/vue_shared/components/sidebar/queries/update_mr_assignees.mutation.graphql
index a0f15a07692..77140ea36d8 100644
--- a/app/assets/javascripts/vue_shared/components/sidebar/queries/update_mr_assignees.mutation.graphql
+++ b/app/assets/javascripts/vue_shared/components/sidebar/queries/update_mr_assignees.mutation.graphql
@@ -1,4 +1,5 @@
#import "~/graphql_shared/fragments/user.fragment.graphql"
+#import "~/graphql_shared/fragments/user_availability.fragment.graphql"
mutation mergeRequestSetAssignees($iid: String!, $assigneeUsernames: [String!]!, $fullPath: ID!) {
mergeRequestSetAssignees(
@@ -9,11 +10,13 @@ mutation mergeRequestSetAssignees($iid: String!, $assigneeUsernames: [String!]!,
assignees {
nodes {
...User
+ ...UserAvailability
}
}
participants {
nodes {
...User
+ ...UserAvailability
}
}
}