Welcome to mirror list, hosted at ThFree Co, Russian Federation.

gitlab.com/gitlab-org/gitlab-foss.git - Unnamed repository; edit this file 'description' to name the repository.
summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
Diffstat (limited to 'app/assets/javascripts/vue_shared')
-rw-r--r--app/assets/javascripts/vue_shared/alert_details/components/alert_details.vue12
-rw-r--r--app/assets/javascripts/vue_shared/alert_details/components/alert_status.vue2
-rw-r--r--app/assets/javascripts/vue_shared/alert_details/components/sidebar/sidebar_assignees.vue2
-rw-r--r--app/assets/javascripts/vue_shared/alert_details/components/sidebar/sidebar_header.vue2
-rw-r--r--app/assets/javascripts/vue_shared/alert_details/components/system_notes/system_note.vue5
-rw-r--r--app/assets/javascripts/vue_shared/alert_details/graphql/mutations/alert_set_assignees.mutation.graphql1
-rw-r--r--app/assets/javascripts/vue_shared/components/actions_button.vue112
-rw-r--r--app/assets/javascripts/vue_shared/components/awards_list.vue10
-rw-r--r--app/assets/javascripts/vue_shared/components/blob_viewers/rich_viewer.vue2
-rw-r--r--app/assets/javascripts/vue_shared/components/blob_viewers/simple_viewer.vue5
-rw-r--r--app/assets/javascripts/vue_shared/components/code_block_highlighted.vue4
-rw-r--r--app/assets/javascripts/vue_shared/components/confirm_danger/confirm_danger_modal.vue10
-rw-r--r--app/assets/javascripts/vue_shared/components/confirm_modal.vue3
-rw-r--r--app/assets/javascripts/vue_shared/components/content_viewer/viewers/markdown_viewer.vue8
-rw-r--r--app/assets/javascripts/vue_shared/components/date_time_picker/date_time_picker.vue1
-rw-r--r--app/assets/javascripts/vue_shared/components/dismissible_alert.vue3
-rw-r--r--app/assets/javascripts/vue_shared/components/filtered_search_bar/constants.js57
-rw-r--r--app/assets/javascripts/vue_shared/components/filtered_search_bar/filtered_search_bar_root.vue14
-rw-r--r--app/assets/javascripts/vue_shared/components/filtered_search_bar/tokens/base_token.vue6
-rw-r--r--app/assets/javascripts/vue_shared/components/filtered_search_bar/tokens/crm_contact_token.vue4
-rw-r--r--app/assets/javascripts/vue_shared/components/filtered_search_bar/tokens/crm_organization_token.vue4
-rw-r--r--app/assets/javascripts/vue_shared/components/filtered_search_bar/tokens/emoji_token.vue4
-rw-r--r--app/assets/javascripts/vue_shared/components/filtered_search_bar/tokens/label_token.vue4
-rw-r--r--app/assets/javascripts/vue_shared/components/filtered_search_bar/tokens/release_token.vue4
-rw-r--r--app/assets/javascripts/vue_shared/components/filtered_search_bar/tokens/user_token.vue (renamed from app/assets/javascripts/vue_shared/components/filtered_search_bar/tokens/author_token.vue)50
-rw-r--r--app/assets/javascripts/vue_shared/components/group_select/group_select.vue8
-rw-r--r--app/assets/javascripts/vue_shared/components/header_ci_component.vue12
-rw-r--r--app/assets/javascripts/vue_shared/components/help_popover.vue5
-rw-r--r--app/assets/javascripts/vue_shared/components/listbox_input/listbox_input.stories.js26
-rw-r--r--app/assets/javascripts/vue_shared/components/listbox_input/listbox_input.vue110
-rw-r--r--app/assets/javascripts/vue_shared/components/markdown/apply_suggestion.vue2
-rw-r--r--app/assets/javascripts/vue_shared/components/markdown/field.vue11
-rw-r--r--app/assets/javascripts/vue_shared/components/markdown/field_view.vue10
-rw-r--r--app/assets/javascripts/vue_shared/components/markdown/markdown_editor.vue6
-rw-r--r--app/assets/javascripts/vue_shared/components/markdown/suggestion_diff_row.vue2
-rw-r--r--app/assets/javascripts/vue_shared/components/markdown/suggestions.vue2
-rw-r--r--app/assets/javascripts/vue_shared/components/markdown_drawer/markdown_drawer.stories.js (renamed from app/assets/javascripts/vue_shared/components/markdown_drawer/makrdown_drawer.stories.js)0
-rw-r--r--app/assets/javascripts/vue_shared/components/markdown_drawer/markdown_drawer.vue21
-rw-r--r--app/assets/javascripts/vue_shared/components/markdown_drawer/utils/fetch.js2
-rw-r--r--app/assets/javascripts/vue_shared/components/metric_images/metric_images_tab.vue7
-rw-r--r--app/assets/javascripts/vue_shared/components/notes/placeholder_note.vue3
-rw-r--r--app/assets/javascripts/vue_shared/components/notes/system_note.vue17
-rw-r--r--app/assets/javascripts/vue_shared/components/paginated_table_with_search_and_tabs/paginated_table_with_search_and_tabs.vue45
-rw-r--r--app/assets/javascripts/vue_shared/components/project_selector/project_list_item.vue3
-rw-r--r--app/assets/javascripts/vue_shared/components/registry/registry_search.vue2
-rw-r--r--app/assets/javascripts/vue_shared/components/sidebar/copyable_field.vue86
-rw-r--r--app/assets/javascripts/vue_shared/components/sidebar/issuable_move_dropdown.vue217
-rw-r--r--app/assets/javascripts/vue_shared/components/sidebar/labels_select_vue/constants.js5
-rw-r--r--app/assets/javascripts/vue_shared/components/sidebar/labels_select_vue/dropdown_button.vue45
-rw-r--r--app/assets/javascripts/vue_shared/components/sidebar/labels_select_vue/dropdown_contents.vue48
-rw-r--r--app/assets/javascripts/vue_shared/components/sidebar/labels_select_vue/dropdown_contents_create_view.vue122
-rw-r--r--app/assets/javascripts/vue_shared/components/sidebar/labels_select_vue/dropdown_contents_labels_view.vue230
-rw-r--r--app/assets/javascripts/vue_shared/components/sidebar/labels_select_vue/dropdown_title.vue46
-rw-r--r--app/assets/javascripts/vue_shared/components/sidebar/labels_select_vue/dropdown_value.vue74
-rw-r--r--app/assets/javascripts/vue_shared/components/sidebar/labels_select_vue/dropdown_value_collapsed.vue53
-rw-r--r--app/assets/javascripts/vue_shared/components/sidebar/labels_select_vue/label_item.vue109
-rw-r--r--app/assets/javascripts/vue_shared/components/sidebar/labels_select_vue/labels_select_root.vue345
-rw-r--r--app/assets/javascripts/vue_shared/components/sidebar/labels_select_vue/store/actions.js69
-rw-r--r--app/assets/javascripts/vue_shared/components/sidebar/labels_select_vue/store/getters.js53
-rw-r--r--app/assets/javascripts/vue_shared/components/sidebar/labels_select_vue/store/index.js12
-rw-r--r--app/assets/javascripts/vue_shared/components/sidebar/labels_select_vue/store/mutation_types.js22
-rw-r--r--app/assets/javascripts/vue_shared/components/sidebar/labels_select_vue/store/mutations.js113
-rw-r--r--app/assets/javascripts/vue_shared/components/sidebar/labels_select_vue/store/state.js30
-rw-r--r--app/assets/javascripts/vue_shared/components/sidebar/labels_select_widget/constants.js13
-rw-r--r--app/assets/javascripts/vue_shared/components/sidebar/labels_select_widget/dropdown_contents.vue241
-rw-r--r--app/assets/javascripts/vue_shared/components/sidebar/labels_select_widget/dropdown_contents_create_view.vue200
-rw-r--r--app/assets/javascripts/vue_shared/components/sidebar/labels_select_widget/dropdown_contents_labels_view.vue177
-rw-r--r--app/assets/javascripts/vue_shared/components/sidebar/labels_select_widget/dropdown_footer.vue35
-rw-r--r--app/assets/javascripts/vue_shared/components/sidebar/labels_select_widget/dropdown_header.vue91
-rw-r--r--app/assets/javascripts/vue_shared/components/sidebar/labels_select_widget/dropdown_value.vue125
-rw-r--r--app/assets/javascripts/vue_shared/components/sidebar/labels_select_widget/graphql/create_label.mutation.graphql12
-rw-r--r--app/assets/javascripts/vue_shared/components/sidebar/labels_select_widget/graphql/epic_labels.query.graphql15
-rw-r--r--app/assets/javascripts/vue_shared/components/sidebar/labels_select_widget/graphql/epic_update_labels.mutation.graphql15
-rw-r--r--app/assets/javascripts/vue_shared/components/sidebar/labels_select_widget/graphql/group_labels.query.graphql12
-rw-r--r--app/assets/javascripts/vue_shared/components/sidebar/labels_select_widget/graphql/issue_labels.query.graphql15
-rw-r--r--app/assets/javascripts/vue_shared/components/sidebar/labels_select_widget/graphql/merge_request_labels.query.graphql15
-rw-r--r--app/assets/javascripts/vue_shared/components/sidebar/labels_select_widget/graphql/project_labels.query.graphql12
-rw-r--r--app/assets/javascripts/vue_shared/components/sidebar/labels_select_widget/label_item.vue21
-rw-r--r--app/assets/javascripts/vue_shared/components/sidebar/labels_select_widget/labels_select_root.vue406
-rw-r--r--app/assets/javascripts/vue_shared/components/sidebar/labels_select_widget/utils.js22
-rw-r--r--app/assets/javascripts/vue_shared/components/sidebar/queries/get_alert_assignees.query.graphql21
-rw-r--r--app/assets/javascripts/vue_shared/components/sidebar/queries/get_issue_assignees.query.graphql21
-rw-r--r--app/assets/javascripts/vue_shared/components/sidebar/queries/get_issue_participants.query.graphql17
-rw-r--r--app/assets/javascripts/vue_shared/components/sidebar/queries/get_issue_timelogs.query.graphql13
-rw-r--r--app/assets/javascripts/vue_shared/components/sidebar/queries/get_merge_request_reviewers.query.graphql26
-rw-r--r--app/assets/javascripts/vue_shared/components/sidebar/queries/get_mr_assignees.query.graphql30
-rw-r--r--app/assets/javascripts/vue_shared/components/sidebar/queries/get_mr_participants.query.graphql17
-rw-r--r--app/assets/javascripts/vue_shared/components/sidebar/queries/get_mr_timelogs.query.graphql13
-rw-r--r--app/assets/javascripts/vue_shared/components/sidebar/queries/merge_request_reviewers.subscription.graphql22
-rw-r--r--app/assets/javascripts/vue_shared/components/sidebar/queries/update_issue_assignees.mutation.graphql18
-rw-r--r--app/assets/javascripts/vue_shared/components/sidebar/queries/update_mr_assignees.mutation.graphql21
-rw-r--r--app/assets/javascripts/vue_shared/components/sidebar/todo_toggle/todo_button.stories.js21
-rw-r--r--app/assets/javascripts/vue_shared/components/sidebar/todo_toggle/todo_button.vue44
-rw-r--r--app/assets/javascripts/vue_shared/components/sidebar/todo_toggle/utils.js21
-rw-r--r--app/assets/javascripts/vue_shared/components/sidebar/toggle_sidebar.vue55
-rw-r--r--app/assets/javascripts/vue_shared/components/source_viewer/components/chunk.vue13
-rw-r--r--app/assets/javascripts/vue_shared/components/source_viewer/components/chunk_line.vue4
-rw-r--r--app/assets/javascripts/vue_shared/components/source_viewer/plugins/link_dependencies.js2
-rw-r--r--app/assets/javascripts/vue_shared/components/source_viewer/plugins/utils/go_sum_linker.js34
-rw-r--r--app/assets/javascripts/vue_shared/components/source_viewer/source_viewer.vue5
-rw-r--r--app/assets/javascripts/vue_shared/components/user_popover/user_popover.vue4
-rw-r--r--app/assets/javascripts/vue_shared/components/web_ide_link.vue161
-rw-r--r--app/assets/javascripts/vue_shared/constants.js4
-rw-r--r--app/assets/javascripts/vue_shared/issuable/create/components/issuable_form.vue4
-rw-r--r--app/assets/javascripts/vue_shared/issuable/create/components/issuable_label_selector.vue92
-rw-r--r--app/assets/javascripts/vue_shared/issuable/list/components/issuable_item.vue4
-rw-r--r--app/assets/javascripts/vue_shared/issuable/list/components/issuable_list_root.vue1
-rw-r--r--app/assets/javascripts/vue_shared/issuable/show/components/issuable_description.vue12
-rw-r--r--app/assets/javascripts/vue_shared/issuable/show/components/issuable_title.vue10
-rw-r--r--app/assets/javascripts/vue_shared/new_namespace/components/legacy_container.vue3
-rw-r--r--app/assets/javascripts/vue_shared/new_namespace/components/welcome.vue2
-rw-r--r--app/assets/javascripts/vue_shared/new_namespace/new_namespace_page.vue3
-rw-r--r--app/assets/javascripts/vue_shared/security_reports/security_reports_app.vue4
-rw-r--r--app/assets/javascripts/vue_shared/security_reports/store/getters.js2
-rw-r--r--app/assets/javascripts/vue_shared/security_reports/store/utils.js8
115 files changed, 670 insertions, 3791 deletions
diff --git a/app/assets/javascripts/vue_shared/alert_details/components/alert_details.vue b/app/assets/javascripts/vue_shared/alert_details/components/alert_details.vue
index 96c2ffa929c..6803d609dbc 100644
--- a/app/assets/javascripts/vue_shared/alert_details/components/alert_details.vue
+++ b/app/assets/javascripts/vue_shared/alert_details/components/alert_details.vue
@@ -9,9 +9,9 @@ import {
GlTabs,
GlTab,
GlButton,
- GlSafeHtmlDirective,
} from '@gitlab/ui';
import * as Sentry from '@sentry/browser';
+import SafeHtml from '~/vue_shared/directives/safe_html';
import highlightCurrentUser from '~/behaviors/markdown/highlight_current_user';
import { fetchPolicies } from '~/lib/graphql';
import { toggleContainerClasses } from '~/lib/utils/dom_utils';
@@ -41,7 +41,7 @@ export default {
reportedAtWithTool: s__('AlertManagement|Reported %{when} by %{tool}'),
},
directives: {
- SafeHtml: GlSafeHtmlDirective,
+ SafeHtml,
},
severityLabels: SEVERITY_LEVELS,
tabsConfig: [
@@ -369,10 +369,10 @@ export default {
<alert-details-table :alert="alert" :loading="loading" :statuses="statuses" />
</gl-tab>
- <metric-images-tab
- :data-testid="$options.tabsConfig[1].id"
- :title="$options.tabsConfig[1].title"
- />
+ <gl-tab :title="$options.tabsConfig[1].title">
+ <metric-images-tab :data-testid="$options.tabsConfig[1].id" />
+ </gl-tab>
+
<gl-tab :data-testid="$options.tabsConfig[2].id" :title="$options.tabsConfig[2].title">
<div v-if="alert.notes.nodes.length > 0" class="issuable-discussion">
<ul class="notes main-notes-list timeline">
diff --git a/app/assets/javascripts/vue_shared/alert_details/components/alert_status.vue b/app/assets/javascripts/vue_shared/alert_details/components/alert_status.vue
index 672761af1cf..8d2ef20b381 100644
--- a/app/assets/javascripts/vue_shared/alert_details/components/alert_status.vue
+++ b/app/assets/javascripts/vue_shared/alert_details/components/alert_status.vue
@@ -106,7 +106,7 @@ export default {
@keydown.esc.native="$emit('hide-dropdown')"
@hide="$emit('hide-dropdown')"
>
- <p v-if="isSidebar" class="gl-new-dropdown-header-top" data-testid="dropdown-header">
+ <p v-if="isSidebar" class="gl-dropdown-header-top" data-testid="dropdown-header">
{{ s__('AlertManagement|Assign status') }}
</p>
<div class="dropdown-content dropdown-body">
diff --git a/app/assets/javascripts/vue_shared/alert_details/components/sidebar/sidebar_assignees.vue b/app/assets/javascripts/vue_shared/alert_details/components/sidebar/sidebar_assignees.vue
index 72dcc16b57a..4ec301b946b 100644
--- a/app/assets/javascripts/vue_shared/alert_details/components/sidebar/sidebar_assignees.vue
+++ b/app/assets/javascripts/vue_shared/alert_details/components/sidebar/sidebar_assignees.vue
@@ -242,7 +242,7 @@ export default {
@keydown.esc.native="hideDropdown"
@hide="hideDropdown"
>
- <p class="gl-new-dropdown-header-top">
+ <p class="gl-dropdown-header-top">
{{ __('Assign To') }}
</p>
<gl-search-box-by-type v-model.trim="search" :placeholder="__('Search users')" />
diff --git a/app/assets/javascripts/vue_shared/alert_details/components/sidebar/sidebar_header.vue b/app/assets/javascripts/vue_shared/alert_details/components/sidebar/sidebar_header.vue
index 832b154b312..b3ee01f3a24 100644
--- a/app/assets/javascripts/vue_shared/alert_details/components/sidebar/sidebar_header.vue
+++ b/app/assets/javascripts/vue_shared/alert_details/components/sidebar/sidebar_header.vue
@@ -1,5 +1,5 @@
<script>
-import ToggleSidebar from '~/vue_shared/components/sidebar/toggle_sidebar.vue';
+import ToggleSidebar from '~/sidebar/components/toggle/toggle_sidebar.vue';
import SidebarTodo from './sidebar_todo.vue';
export default {
diff --git a/app/assets/javascripts/vue_shared/alert_details/components/system_notes/system_note.vue b/app/assets/javascripts/vue_shared/alert_details/components/system_notes/system_note.vue
index 6b774b2a734..3c73f42b6b1 100644
--- a/app/assets/javascripts/vue_shared/alert_details/components/system_notes/system_note.vue
+++ b/app/assets/javascripts/vue_shared/alert_details/components/system_notes/system_note.vue
@@ -1,5 +1,6 @@
<script>
-import { GlIcon, GlSafeHtmlDirective } from '@gitlab/ui';
+import { GlIcon } from '@gitlab/ui';
+import SafeHtml from '~/vue_shared/directives/safe_html';
import NoteHeader from '~/notes/components/note_header.vue';
export default {
@@ -8,7 +9,7 @@ export default {
GlIcon,
},
directives: {
- SafeHtml: GlSafeHtmlDirective,
+ SafeHtml,
},
props: {
note: {
diff --git a/app/assets/javascripts/vue_shared/alert_details/graphql/mutations/alert_set_assignees.mutation.graphql b/app/assets/javascripts/vue_shared/alert_details/graphql/mutations/alert_set_assignees.mutation.graphql
index 33091f1ba5e..b04d5773a37 100644
--- a/app/assets/javascripts/vue_shared/alert_details/graphql/mutations/alert_set_assignees.mutation.graphql
+++ b/app/assets/javascripts/vue_shared/alert_details/graphql/mutations/alert_set_assignees.mutation.graphql
@@ -8,6 +8,7 @@ mutation alertSetAssignees($fullPath: ID!, $assigneeUsernames: [String!]!, $iid:
) {
errors
issuable: alert {
+ id
iid
assignees {
nodes {
diff --git a/app/assets/javascripts/vue_shared/components/actions_button.vue b/app/assets/javascripts/vue_shared/components/actions_button.vue
index c6c22f9c61f..175aef59ae5 100644
--- a/app/assets/javascripts/vue_shared/components/actions_button.vue
+++ b/app/assets/javascripts/vue_shared/components/actions_button.vue
@@ -1,11 +1,5 @@
<script>
-import {
- GlDropdown,
- GlDropdownItem,
- GlDropdownDivider,
- GlButton,
- GlTooltipDirective,
-} from '@gitlab/ui';
+import { GlDropdown, GlDropdownItem, GlDropdownDivider, GlButton, GlTooltip } from '@gitlab/ui';
export default {
components: {
@@ -13,11 +7,14 @@ export default {
GlDropdownItem,
GlDropdownDivider,
GlButton,
- },
- directives: {
- GlTooltip: GlTooltipDirective,
+ GlTooltip,
},
props: {
+ id: {
+ type: String,
+ required: false,
+ default: '',
+ },
actions: {
type: Array,
required: true,
@@ -37,6 +34,11 @@ export default {
required: false,
default: 'default',
},
+ showActionTooltip: {
+ type: Boolean,
+ required: false,
+ default: true,
+ },
},
computed: {
hasMultipleActions() {
@@ -51,6 +53,7 @@ export default {
this.$emit('select', action.key);
},
handleClick(action, evt) {
+ this.$emit('actionClicked', { action });
return action.handle?.(evt);
},
},
@@ -58,46 +61,51 @@ export default {
</script>
<template>
- <gl-dropdown
- v-if="hasMultipleActions"
- v-gl-tooltip="selectedAction.tooltip"
- :text="selectedAction.text"
- :split-href="selectedAction.href"
- :variant="variant"
- :category="category"
- split
- data-qa-selector="action_dropdown"
- @click="handleClick(selectedAction, $event)"
- >
- <template #button-content>
- <span class="gl-new-dropdown-button-text" v-bind="selectedAction.attrs">
- {{ selectedAction.text }}
- </span>
- </template>
- <template v-for="(action, index) in actions">
- <gl-dropdown-item
- :key="action.key"
- is-check-item
- :is-checked="action.key === selectedAction.key"
- :secondary-text="action.secondaryText"
- :data-qa-selector="`${action.key}_menu_item`"
- :data-testid="`action_${action.key}`"
- @click="handleItemClick(action)"
- >
- <span class="gl-font-weight-bold">{{ action.text }}</span>
- </gl-dropdown-item>
- <gl-dropdown-divider v-if="index != actions.length - 1" :key="action.key + '_divider'" />
- </template>
- </gl-dropdown>
- <gl-button
- v-else-if="selectedAction"
- v-gl-tooltip="selectedAction.tooltip"
- v-bind="selectedAction.attrs"
- :variant="variant"
- :category="category"
- :href="selectedAction.href"
- @click="handleClick(selectedAction, $event)"
- >
- {{ selectedAction.text }}
- </gl-button>
+ <span>
+ <gl-dropdown
+ v-if="hasMultipleActions"
+ :id="id"
+ :text="selectedAction.text"
+ :split-href="selectedAction.href"
+ :variant="variant"
+ :category="category"
+ split
+ data-qa-selector="action_dropdown"
+ @click="handleClick(selectedAction, $event)"
+ >
+ <template #button-content>
+ <span class="gl-dropdown-button-text" v-bind="selectedAction.attrs">
+ {{ selectedAction.text }}
+ </span>
+ </template>
+ <template v-for="(action, index) in actions">
+ <gl-dropdown-item
+ :key="action.key"
+ is-check-item
+ :is-checked="action.key === selectedAction.key"
+ :secondary-text="action.secondaryText"
+ :data-qa-selector="`${action.key}_menu_item`"
+ :data-testid="`action_${action.key}`"
+ @click="handleItemClick(action)"
+ >
+ <span class="gl-font-weight-bold">{{ action.text }}</span>
+ </gl-dropdown-item>
+ <gl-dropdown-divider v-if="index != actions.length - 1" :key="action.key + '_divider'" />
+ </template>
+ </gl-dropdown>
+ <gl-button
+ v-else-if="selectedAction"
+ :id="id"
+ v-bind="selectedAction.attrs"
+ :variant="variant"
+ :category="category"
+ :href="selectedAction.href"
+ @click="handleClick(selectedAction, $event)"
+ >
+ {{ selectedAction.text }}
+ </gl-button>
+ <gl-tooltip v-if="selectedAction.tooltip && showActionTooltip" :target="id">
+ {{ selectedAction.tooltip }}
+ </gl-tooltip>
+ </span>
</template>
diff --git a/app/assets/javascripts/vue_shared/components/awards_list.vue b/app/assets/javascripts/vue_shared/components/awards_list.vue
index f5d8811e83c..cb38b3e13bb 100644
--- a/app/assets/javascripts/vue_shared/components/awards_list.vue
+++ b/app/assets/javascripts/vue_shared/components/awards_list.vue
@@ -1,6 +1,7 @@
<script>
-import { GlIcon, GlButton, GlTooltipDirective, GlSafeHtmlDirective } from '@gitlab/ui';
+import { GlIcon, GlButton, GlTooltipDirective } from '@gitlab/ui';
import { groupBy } from 'lodash';
+import SafeHtml from '~/vue_shared/directives/safe_html';
import EmojiPicker from '~/emoji/components/picker.vue';
import { __, sprintf } from '~/locale';
import glFeatureFlagsMixin from '~/vue_shared/mixins/gl_feature_flags_mixin';
@@ -17,7 +18,7 @@ export default {
},
directives: {
GlTooltip: GlTooltipDirective,
- SafeHtml: GlSafeHtmlDirective,
+ SafeHtml,
},
mixins: [glFeatureFlagsMixin()],
props: {
@@ -158,10 +159,7 @@ export default {
return;
}
- // 100 and 1234 emoji are a number. Callback for v-for click sends it as a string
- const parsedName = /^[0-9]+$/.test(awardName) ? Number(awardName) : awardName;
-
- this.$emit('award', parsedName);
+ this.$emit('award', awardName);
if (document.activeElement) document.activeElement.blur();
},
diff --git a/app/assets/javascripts/vue_shared/components/blob_viewers/rich_viewer.vue b/app/assets/javascripts/vue_shared/components/blob_viewers/rich_viewer.vue
index ed0eb9cc0b8..49181bb847d 100644
--- a/app/assets/javascripts/vue_shared/components/blob_viewers/rich_viewer.vue
+++ b/app/assets/javascripts/vue_shared/components/blob_viewers/rich_viewer.vue
@@ -1,5 +1,5 @@
<script>
-import { GlSafeHtmlDirective as SafeHtml } from '@gitlab/ui';
+import SafeHtml from '~/vue_shared/directives/safe_html';
import { handleBlobRichViewer } from '~/blob/viewer';
import MarkdownFieldView from '~/vue_shared/components/markdown/field_view.vue';
import ViewerMixin from './mixins';
diff --git a/app/assets/javascripts/vue_shared/components/blob_viewers/simple_viewer.vue b/app/assets/javascripts/vue_shared/components/blob_viewers/simple_viewer.vue
index 0117c06c3d5..c7a76af7f74 100644
--- a/app/assets/javascripts/vue_shared/components/blob_viewers/simple_viewer.vue
+++ b/app/assets/javascripts/vue_shared/components/blob_viewers/simple_viewer.vue
@@ -1,5 +1,6 @@
<script>
-import { GlIcon, GlSafeHtmlDirective } from '@gitlab/ui';
+import { GlIcon } from '@gitlab/ui';
+import SafeHtml from '~/vue_shared/directives/safe_html';
import { HIGHLIGHT_CLASS_NAME } from './constants';
import ViewerMixin from './mixins';
@@ -9,7 +10,7 @@ export default {
GlIcon,
},
directives: {
- SafeHtml: GlSafeHtmlDirective,
+ SafeHtml,
},
mixins: [ViewerMixin],
inject: ['blobHash'],
diff --git a/app/assets/javascripts/vue_shared/components/code_block_highlighted.vue b/app/assets/javascripts/vue_shared/components/code_block_highlighted.vue
index 65b08b608e8..352d03befc3 100644
--- a/app/assets/javascripts/vue_shared/components/code_block_highlighted.vue
+++ b/app/assets/javascripts/vue_shared/components/code_block_highlighted.vue
@@ -1,5 +1,5 @@
<script>
-import { GlSafeHtmlDirective } from '@gitlab/ui';
+import SafeHtml from '~/vue_shared/directives/safe_html';
import languageLoader from '~/content_editor/services/highlight_js_language_loader';
import CodeBlock from './code_block.vue';
@@ -7,7 +7,7 @@ import CodeBlock from './code_block.vue';
export default {
name: 'CodeBlockHighlighted',
directives: {
- SafeHtml: GlSafeHtmlDirective,
+ SafeHtml,
},
components: {
CodeBlock,
diff --git a/app/assets/javascripts/vue_shared/components/confirm_danger/confirm_danger_modal.vue b/app/assets/javascripts/vue_shared/components/confirm_danger/confirm_danger_modal.vue
index 7a982bc035a..d0a634d8e54 100644
--- a/app/assets/javascripts/vue_shared/components/confirm_danger/confirm_danger_modal.vue
+++ b/app/assets/javascripts/vue_shared/components/confirm_danger/confirm_danger_modal.vue
@@ -1,12 +1,6 @@
<script>
-import {
- GlAlert,
- GlModal,
- GlFormGroup,
- GlFormInput,
- GlSafeHtmlDirective as SafeHtml,
- GlSprintf,
-} from '@gitlab/ui';
+import { GlAlert, GlModal, GlFormGroup, GlFormInput, GlSprintf } from '@gitlab/ui';
+import SafeHtml from '~/vue_shared/directives/safe_html';
import {
CONFIRM_DANGER_MODAL_BUTTON,
CONFIRM_DANGER_MODAL_TITLE,
diff --git a/app/assets/javascripts/vue_shared/components/confirm_modal.vue b/app/assets/javascripts/vue_shared/components/confirm_modal.vue
index 72504e5bc50..664c3578785 100644
--- a/app/assets/javascripts/vue_shared/components/confirm_modal.vue
+++ b/app/assets/javascripts/vue_shared/components/confirm_modal.vue
@@ -1,6 +1,7 @@
<script>
-import { GlModal, GlSafeHtmlDirective as SafeHtml } from '@gitlab/ui';
+import { GlModal } from '@gitlab/ui';
import { uniqueId } from 'lodash';
+import SafeHtml from '~/vue_shared/directives/safe_html';
import csrf from '~/lib/utils/csrf';
import eventHub, { EVENT_OPEN_CONFIRM_MODAL } from './confirm_modal_eventhub';
import DomElementListener from './dom_element_listener.vue';
diff --git a/app/assets/javascripts/vue_shared/components/content_viewer/viewers/markdown_viewer.vue b/app/assets/javascripts/vue_shared/components/content_viewer/viewers/markdown_viewer.vue
index 3ecfac10f9c..00d12654ee3 100644
--- a/app/assets/javascripts/vue_shared/components/content_viewer/viewers/markdown_viewer.vue
+++ b/app/assets/javascripts/vue_shared/components/content_viewer/viewers/markdown_viewer.vue
@@ -1,10 +1,10 @@
<script>
-import { GlSkeletonLoader, GlSafeHtmlDirective as SafeHtml } from '@gitlab/ui';
-import $ from 'jquery';
-import '~/behaviors/markdown/render_gfm';
+import { GlSkeletonLoader } from '@gitlab/ui';
import { forEach, escape } from 'lodash';
+import SafeHtml from '~/vue_shared/directives/safe_html';
import axios from '~/lib/utils/axios_utils';
import { __ } from '~/locale';
+import { renderGFM } from '~/behaviors/markdown/render_gfm';
const { CancelToken } = axios;
let axiosSource;
@@ -96,7 +96,7 @@ export default {
this.isLoading = false;
this.$nextTick(() => {
- $(this.$refs.markdownPreview).renderGFM();
+ renderGFM(this.$refs.markdownPreview);
});
})
.catch(() => {
diff --git a/app/assets/javascripts/vue_shared/components/date_time_picker/date_time_picker.vue b/app/assets/javascripts/vue_shared/components/date_time_picker/date_time_picker.vue
index 181c1b89e31..d8a2789a419 100644
--- a/app/assets/javascripts/vue_shared/components/date_time_picker/date_time_picker.vue
+++ b/app/assets/javascripts/vue_shared/components/date_time_picker/date_time_picker.vue
@@ -265,7 +265,6 @@ export default {
<gl-dropdown-item
v-for="(option, index) in options"
:key="index"
- data-qa-selector="quick_range_item"
:active="isOptionActive(option)"
active-class="active"
@click="setQuickRange(option)"
diff --git a/app/assets/javascripts/vue_shared/components/dismissible_alert.vue b/app/assets/javascripts/vue_shared/components/dismissible_alert.vue
index 0621ec14c6c..8395bc89790 100644
--- a/app/assets/javascripts/vue_shared/components/dismissible_alert.vue
+++ b/app/assets/javascripts/vue_shared/components/dismissible_alert.vue
@@ -1,5 +1,6 @@
<script>
-import { GlAlert, GlSafeHtmlDirective as SafeHtml } from '@gitlab/ui';
+import { GlAlert } from '@gitlab/ui';
+import SafeHtml from '~/vue_shared/directives/safe_html';
export default {
name: 'DismissibleAlert',
diff --git a/app/assets/javascripts/vue_shared/components/filtered_search_bar/constants.js b/app/assets/javascripts/vue_shared/components/filtered_search_bar/constants.js
index 755ce004aa9..993b4c11c0e 100644
--- a/app/assets/javascripts/vue_shared/components/filtered_search_bar/constants.js
+++ b/app/assets/javascripts/vue_shared/components/filtered_search_bar/constants.js
@@ -8,52 +8,44 @@ export const FILTER_ANY = 'Any';
export const FILTER_CURRENT = 'Current';
export const FILTER_UPCOMING = 'Upcoming';
export const FILTER_STARTED = 'Started';
-export const FILTER_NONE_ANY = [FILTER_NONE, FILTER_ANY];
+
+export const FILTERS_NONE_ANY = [FILTER_NONE, FILTER_ANY];
export const OPERATOR_IS = '=';
export const OPERATOR_IS_TEXT = __('is');
-export const OPERATOR_IS_NOT = '!=';
-export const OPERATOR_IS_NOT_TEXT = __('is not one of');
+export const OPERATOR_NOT = '!=';
+export const OPERATOR_NOT_TEXT = __('is not one of');
export const OPERATOR_OR = '||';
export const OPERATOR_OR_TEXT = __('is one of');
-export const OPERATOR_IS_ONLY = [{ value: OPERATOR_IS, description: OPERATOR_IS_TEXT }];
-export const OPERATOR_IS_NOT_ONLY = [{ value: OPERATOR_IS_NOT, description: OPERATOR_IS_NOT_TEXT }];
-export const OPERATOR_OR_ONLY = [{ value: OPERATOR_OR, description: OPERATOR_OR_TEXT }];
-export const OPERATOR_IS_AND_IS_NOT = [...OPERATOR_IS_ONLY, ...OPERATOR_IS_NOT_ONLY];
-export const OPERATOR_IS_NOT_OR = [
- ...OPERATOR_IS_ONLY,
- ...OPERATOR_IS_NOT_ONLY,
- ...OPERATOR_OR_ONLY,
-];
-
-export const DEFAULT_LABEL_NONE = { value: FILTER_NONE, text: __('None'), title: __('None') };
-export const DEFAULT_LABEL_ANY = { value: FILTER_ANY, text: __('Any'), title: __('Any') };
-export const DEFAULT_NONE_ANY = [DEFAULT_LABEL_NONE, DEFAULT_LABEL_ANY];
+export const OPERATORS_IS = [{ value: OPERATOR_IS, description: OPERATOR_IS_TEXT }];
+export const OPERATORS_NOT = [{ value: OPERATOR_NOT, description: OPERATOR_NOT_TEXT }];
+export const OPERATORS_OR = [{ value: OPERATOR_OR, description: OPERATOR_OR_TEXT }];
+export const OPERATORS_IS_NOT = [...OPERATORS_IS, ...OPERATORS_NOT];
+export const OPERATORS_IS_NOT_OR = [...OPERATORS_IS, ...OPERATORS_NOT, ...OPERATORS_OR];
-export const DEFAULT_MILESTONE_UPCOMING = {
+export const OPTION_NONE = { value: FILTER_NONE, text: __('None'), title: __('None') };
+export const OPTION_ANY = { value: FILTER_ANY, text: __('Any'), title: __('Any') };
+export const OPTION_CURRENT = { value: FILTER_CURRENT, text: __('Current') };
+export const OPTION_STARTED = { value: FILTER_STARTED, text: __('Started'), title: __('Started') };
+export const OPTION_UPCOMING = {
value: FILTER_UPCOMING,
text: __('Upcoming'),
title: __('Upcoming'),
};
-export const DEFAULT_MILESTONE_STARTED = {
- value: FILTER_STARTED,
- text: __('Started'),
- title: __('Started'),
-};
-export const DEFAULT_MILESTONES = DEFAULT_NONE_ANY.concat([
- DEFAULT_MILESTONE_UPCOMING,
- DEFAULT_MILESTONE_STARTED,
-]);
-export const SortDirection = {
+export const OPTIONS_NONE_ANY = [OPTION_NONE, OPTION_ANY];
+
+export const DEFAULT_MILESTONES = OPTIONS_NONE_ANY.concat([OPTION_UPCOMING, OPTION_STARTED]);
+
+export const SORT_DIRECTION = {
descending: 'descending',
ascending: 'ascending',
};
-export const FILTERED_SEARCH_LABELS = 'labels';
export const FILTERED_SEARCH_TERM = 'filtered-search-term';
+export const TOKEN_TITLE_APPROVED_BY = __('Approved-By');
export const TOKEN_TITLE_ASSIGNEE = s__('SearchToken|Assignee');
export const TOKEN_TITLE_AUTHOR = __('Author');
export const TOKEN_TITLE_CONFIDENTIAL = __('Confidential');
@@ -63,11 +55,14 @@ export const TOKEN_TITLE_MILESTONE = __('Milestone');
export const TOKEN_TITLE_MY_REACTION = __('My-Reaction');
export const TOKEN_TITLE_ORGANIZATION = s__('Crm|Organization');
export const TOKEN_TITLE_RELEASE = __('Release');
+export const TOKEN_TITLE_REVIEWER = s__('SearchToken|Reviewer');
export const TOKEN_TITLE_SOURCE_BRANCH = __('Source Branch');
export const TOKEN_TITLE_STATUS = __('Status');
export const TOKEN_TITLE_TARGET_BRANCH = __('Target Branch');
export const TOKEN_TITLE_TYPE = __('Type');
+export const TOKEN_TITLE_SEARCH_WITHIN = __('Search Within');
+export const TOKEN_TYPE_APPROVED_BY = 'approved-by';
export const TOKEN_TYPE_ASSIGNEE = 'assignee';
export const TOKEN_TYPE_AUTHOR = 'author';
export const TOKEN_TYPE_CONFIDENTIAL = 'confidential';
@@ -84,5 +79,11 @@ export const TOKEN_TYPE_MILESTONE = 'milestone';
export const TOKEN_TYPE_MY_REACTION = 'my-reaction';
export const TOKEN_TYPE_ORGANIZATION = 'organization';
export const TOKEN_TYPE_RELEASE = 'release';
+export const TOKEN_TYPE_REVIEWER = 'reviewer';
+export const TOKEN_TYPE_SOURCE_BRANCH = 'source-branch';
+export const TOKEN_TYPE_STATUS = 'status';
+export const TOKEN_TYPE_TARGET_BRANCH = 'target-branch';
export const TOKEN_TYPE_TYPE = 'type';
export const TOKEN_TYPE_WEIGHT = 'weight';
+
+export const TOKEN_TYPE_SEARCH_WITHIN = 'in';
diff --git a/app/assets/javascripts/vue_shared/components/filtered_search_bar/filtered_search_bar_root.vue b/app/assets/javascripts/vue_shared/components/filtered_search_bar/filtered_search_bar_root.vue
index 0d0787e7033..34f64dddc41 100644
--- a/app/assets/javascripts/vue_shared/components/filtered_search_bar/filtered_search_bar_root.vue
+++ b/app/assets/javascripts/vue_shared/components/filtered_search_bar/filtered_search_bar_root.vue
@@ -15,7 +15,7 @@ import RecentSearchesStore from '~/filtered_search/stores/recent_searches_store'
import { createAlert } from '~/flash';
import { __ } from '~/locale';
-import { SortDirection } from './constants';
+import { SORT_DIRECTION } from './constants';
import { filterEmptySearchTerm, stripQuotes, uniqueTokens } from './filtered_search_utils';
export default {
@@ -107,7 +107,7 @@ export default {
recentSearches: [],
filterValue: this.initialFilterValue,
selectedSortOption: this.sortOptions[0],
- selectedSortDirection: SortDirection.descending,
+ selectedSortDirection: SORT_DIRECTION.descending,
};
},
computed: {
@@ -130,12 +130,12 @@ export default {
);
},
sortDirectionIcon() {
- return this.selectedSortDirection === SortDirection.ascending
+ return this.selectedSortDirection === SORT_DIRECTION.ascending
? 'sort-lowest'
: 'sort-highest';
},
sortDirectionTooltip() {
- return this.selectedSortDirection === SortDirection.ascending
+ return this.selectedSortDirection === SORT_DIRECTION.ascending
? __('Sort direction: Ascending')
: __('Sort direction: Descending');
},
@@ -267,9 +267,9 @@ export default {
},
handleSortDirectionClick() {
this.selectedSortDirection =
- this.selectedSortDirection === SortDirection.ascending
- ? SortDirection.descending
- : SortDirection.ascending;
+ this.selectedSortDirection === SORT_DIRECTION.ascending
+ ? SORT_DIRECTION.descending
+ : SORT_DIRECTION.ascending;
this.$emit('onSort', this.selectedSortOption.sortDirection[this.selectedSortDirection]);
},
handleHistoryItemSelected(filters) {
diff --git a/app/assets/javascripts/vue_shared/components/filtered_search_bar/tokens/base_token.vue b/app/assets/javascripts/vue_shared/components/filtered_search_bar/tokens/base_token.vue
index 6a4ff07c999..b0fa3e4c27e 100644
--- a/app/assets/javascripts/vue_shared/components/filtered_search_bar/tokens/base_token.vue
+++ b/app/assets/javascripts/vue_shared/components/filtered_search_bar/tokens/base_token.vue
@@ -9,7 +9,7 @@ import {
} from '@gitlab/ui';
import { debounce } from 'lodash';
-import { DEBOUNCE_DELAY, FILTER_NONE_ANY, OPERATOR_IS_NOT } from '../constants';
+import { DEBOUNCE_DELAY, FILTERS_NONE_ANY, OPERATOR_NOT } from '../constants';
import {
getRecentlyUsedSuggestions,
setTokenValueToRecentlyUsed,
@@ -100,9 +100,9 @@ export default {
return this.getActiveTokenValue(this.suggestions, this.value.data);
},
availableDefaultSuggestions() {
- if (this.value.operator === OPERATOR_IS_NOT) {
+ if (this.value.operator === OPERATOR_NOT) {
return this.defaultSuggestions.filter(
- (suggestion) => !FILTER_NONE_ANY.includes(suggestion.value),
+ (suggestion) => !FILTERS_NONE_ANY.includes(suggestion.value),
);
}
return this.defaultSuggestions;
diff --git a/app/assets/javascripts/vue_shared/components/filtered_search_bar/tokens/crm_contact_token.vue b/app/assets/javascripts/vue_shared/components/filtered_search_bar/tokens/crm_contact_token.vue
index d34cfb922a9..e0fa06c159e 100644
--- a/app/assets/javascripts/vue_shared/components/filtered_search_bar/tokens/crm_contact_token.vue
+++ b/app/assets/javascripts/vue_shared/components/filtered_search_bar/tokens/crm_contact_token.vue
@@ -8,7 +8,7 @@ import { isPositiveInteger } from '~/lib/utils/number_utils';
import { __ } from '~/locale';
import searchCrmContactsQuery from '../queries/search_crm_contacts.query.graphql';
-import { DEFAULT_NONE_ANY } from '../constants';
+import { OPTIONS_NONE_ANY } from '../constants';
import BaseToken from './base_token.vue';
@@ -39,7 +39,7 @@ export default {
},
computed: {
defaultContacts() {
- return this.config.defaultContacts || DEFAULT_NONE_ANY;
+ return this.config.defaultContacts || OPTIONS_NONE_ANY;
},
namespace() {
return this.config.isProject ? ITEM_TYPE.PROJECT : ITEM_TYPE.GROUP;
diff --git a/app/assets/javascripts/vue_shared/components/filtered_search_bar/tokens/crm_organization_token.vue b/app/assets/javascripts/vue_shared/components/filtered_search_bar/tokens/crm_organization_token.vue
index c7c9350ee93..3f030c8698c 100644
--- a/app/assets/javascripts/vue_shared/components/filtered_search_bar/tokens/crm_organization_token.vue
+++ b/app/assets/javascripts/vue_shared/components/filtered_search_bar/tokens/crm_organization_token.vue
@@ -8,7 +8,7 @@ import { isPositiveInteger } from '~/lib/utils/number_utils';
import { __ } from '~/locale';
import searchCrmOrganizationsQuery from '../queries/search_crm_organizations.query.graphql';
-import { DEFAULT_NONE_ANY } from '../constants';
+import { OPTIONS_NONE_ANY } from '../constants';
import BaseToken from './base_token.vue';
@@ -39,7 +39,7 @@ export default {
},
computed: {
defaultOrganizations() {
- return this.config.defaultOrganizations || DEFAULT_NONE_ANY;
+ return this.config.defaultOrganizations || OPTIONS_NONE_ANY;
},
namespace() {
return this.config.isProject ? ITEM_TYPE.PROJECT : ITEM_TYPE.GROUP;
diff --git a/app/assets/javascripts/vue_shared/components/filtered_search_bar/tokens/emoji_token.vue b/app/assets/javascripts/vue_shared/components/filtered_search_bar/tokens/emoji_token.vue
index 929823f7308..74905dc2ae0 100644
--- a/app/assets/javascripts/vue_shared/components/filtered_search_bar/tokens/emoji_token.vue
+++ b/app/assets/javascripts/vue_shared/components/filtered_search_bar/tokens/emoji_token.vue
@@ -3,7 +3,7 @@ import { GlFilteredSearchSuggestion } from '@gitlab/ui';
import { createAlert } from '~/flash';
import { __ } from '~/locale';
import BaseToken from '~/vue_shared/components/filtered_search_bar/tokens/base_token.vue';
-import { DEFAULT_NONE_ANY } from '../constants';
+import { OPTIONS_NONE_ANY } from '../constants';
import { stripQuotes } from '../filtered_search_utils';
export default {
@@ -33,7 +33,7 @@ export default {
},
computed: {
defaultEmojis() {
- return this.config.defaultEmojis || DEFAULT_NONE_ANY;
+ return this.config.defaultEmojis || OPTIONS_NONE_ANY;
},
},
methods: {
diff --git a/app/assets/javascripts/vue_shared/components/filtered_search_bar/tokens/label_token.vue b/app/assets/javascripts/vue_shared/components/filtered_search_bar/tokens/label_token.vue
index bce0c11aafd..71c50ef292a 100644
--- a/app/assets/javascripts/vue_shared/components/filtered_search_bar/tokens/label_token.vue
+++ b/app/assets/javascripts/vue_shared/components/filtered_search_bar/tokens/label_token.vue
@@ -5,7 +5,7 @@ import { createAlert } from '~/flash';
import { convertObjectPropsToCamelCase } from '~/lib/utils/common_utils';
import { __ } from '~/locale';
-import { DEFAULT_NONE_ANY } from '../constants';
+import { OPTIONS_NONE_ANY } from '../constants';
import { stripQuotes } from '../filtered_search_utils';
import BaseToken from './base_token.vue';
@@ -38,7 +38,7 @@ export default {
},
computed: {
defaultLabels() {
- return this.config.defaultLabels || DEFAULT_NONE_ANY;
+ return this.config.defaultLabels || OPTIONS_NONE_ANY;
},
},
methods: {
diff --git a/app/assets/javascripts/vue_shared/components/filtered_search_bar/tokens/release_token.vue b/app/assets/javascripts/vue_shared/components/filtered_search_bar/tokens/release_token.vue
index 59701b4959e..6d681aab3ca 100644
--- a/app/assets/javascripts/vue_shared/components/filtered_search_bar/tokens/release_token.vue
+++ b/app/assets/javascripts/vue_shared/components/filtered_search_bar/tokens/release_token.vue
@@ -3,7 +3,7 @@ import { GlFilteredSearchSuggestion } from '@gitlab/ui';
import { createAlert } from '~/flash';
import { __ } from '~/locale';
import BaseToken from '~/vue_shared/components/filtered_search_bar/tokens/base_token.vue';
-import { DEFAULT_NONE_ANY } from '../constants';
+import { OPTIONS_NONE_ANY } from '../constants';
export default {
components: {
@@ -32,7 +32,7 @@ export default {
},
computed: {
defaultReleases() {
- return this.config.defaultReleases || DEFAULT_NONE_ANY;
+ return this.config.defaultReleases || OPTIONS_NONE_ANY;
},
},
methods: {
diff --git a/app/assets/javascripts/vue_shared/components/filtered_search_bar/tokens/author_token.vue b/app/assets/javascripts/vue_shared/components/filtered_search_bar/tokens/user_token.vue
index 7c184a3c391..28e65c1185f 100644
--- a/app/assets/javascripts/vue_shared/components/filtered_search_bar/tokens/author_token.vue
+++ b/app/assets/javascripts/vue_shared/components/filtered_search_bar/tokens/user_token.vue
@@ -4,7 +4,7 @@ import { compact } from 'lodash';
import { createAlert } from '~/flash';
import { __ } from '~/locale';
-import { DEFAULT_NONE_ANY } from '../constants';
+import { OPTIONS_NONE_ANY } from '../constants';
import BaseToken from './base_token.vue';
@@ -30,30 +30,30 @@ export default {
},
data() {
return {
- authors: this.config.initialAuthors || [],
+ users: this.config.initialUsers || [],
loading: false,
};
},
computed: {
- defaultAuthors() {
- return this.config.defaultAuthors || DEFAULT_NONE_ANY;
+ defaultUsers() {
+ return this.config.defaultUsers || OPTIONS_NONE_ANY;
},
- preloadedAuthors() {
- return this.config.preloadedAuthors || [];
+ preloadedUsers() {
+ return this.config.preloadedUsers || [];
},
},
methods: {
- getActiveAuthor(authors, data) {
- return authors.find((author) => author.username.toLowerCase() === data.toLowerCase());
+ getActiveUser(users, data) {
+ return users.find((user) => user.username.toLowerCase() === data.toLowerCase());
},
- getAvatarUrl(author) {
- return author.avatarUrl || author.avatar_url;
+ getAvatarUrl(user) {
+ return user.avatarUrl || user.avatar_url;
},
- fetchAuthors(searchTerm) {
+ fetchUsers(searchTerm) {
this.loading = true;
const fetchPromise = this.config.fetchPath
- ? this.config.fetchAuthors(this.config.fetchPath, searchTerm)
- : this.config.fetchAuthors(searchTerm);
+ ? this.config.fetchUsers(this.config.fetchPath, searchTerm)
+ : this.config.fetchUsers(searchTerm);
fetchPromise
.then((res) => {
@@ -62,7 +62,7 @@ export default {
// return response differently
// TODO: rm when completed https://gitlab.com/gitlab-org/gitlab/-/issues/345756
- this.authors = Array.isArray(res) ? compact(res) : compact(res.data);
+ this.users = Array.isArray(res) ? compact(res) : compact(res.data);
})
.catch(() =>
createAlert({
@@ -83,12 +83,12 @@ export default {
:value="value"
:active="active"
:suggestions-loading="loading"
- :suggestions="authors"
- :get-active-token-value="getActiveAuthor"
- :default-suggestions="defaultAuthors"
- :preloaded-suggestions="preloadedAuthors"
+ :suggestions="users"
+ :get-active-token-value="getActiveUser"
+ :default-suggestions="defaultUsers"
+ :preloaded-suggestions="preloadedUsers"
v-bind="$attrs"
- @fetch-suggestions="fetchAuthors"
+ @fetch-suggestions="fetchUsers"
v-on="$listeners"
>
<template #view="{ viewTokenProps: { inputValue, activeTokenValue } }">
@@ -102,15 +102,15 @@ export default {
</template>
<template #suggestions-list="{ suggestions }">
<gl-filtered-search-suggestion
- v-for="author in suggestions"
- :key="author.username"
- :value="author.username"
+ v-for="user in suggestions"
+ :key="user.username"
+ :value="user.username"
>
<div class="gl-display-flex">
- <gl-avatar :size="32" :src="getAvatarUrl(author)" />
+ <gl-avatar :size="32" :src="getAvatarUrl(user)" />
<div>
- <div>{{ author.name }}</div>
- <div>@{{ author.username }}</div>
+ <div>{{ user.name }}</div>
+ <div>@{{ user.username }}</div>
</div>
</div>
</gl-filtered-search-suggestion>
diff --git a/app/assets/javascripts/vue_shared/components/group_select/group_select.vue b/app/assets/javascripts/vue_shared/components/group_select/group_select.vue
index 1de6c0121bc..5db723e1e5a 100644
--- a/app/assets/javascripts/vue_shared/components/group_select/group_select.vue
+++ b/app/assets/javascripts/vue_shared/components/group_select/group_select.vue
@@ -1,6 +1,6 @@
<script>
import { debounce } from 'lodash';
-import { GlListbox } from '@gitlab/ui';
+import { GlCollapsibleListbox } from '@gitlab/ui';
import axios from '~/lib/utils/axios_utils';
import Api from '~/api';
import { __ } from '~/locale';
@@ -18,7 +18,7 @@ const MINIMUM_QUERY_LENGTH = 3;
export default {
components: {
- GlListbox,
+ GlCollapsibleListbox,
},
props: {
inputName: {
@@ -167,7 +167,7 @@ export default {
<template>
<div>
- <gl-listbox
+ <gl-collapsible-listbox
ref="listbox"
v-model="selected"
:header-text="$options.i18n.selectGroup"
@@ -188,7 +188,7 @@ export default {
</div>
<div class="gl-text-gray-300">{{ item.full_path }}</div>
</template>
- </gl-listbox>
+ </gl-collapsible-listbox>
<div class="flash-container"></div>
<input :id="inputId" data-testid="input" type="hidden" :name="inputName" :value="inputValue" />
</div>
diff --git a/app/assets/javascripts/vue_shared/components/header_ci_component.vue b/app/assets/javascripts/vue_shared/components/header_ci_component.vue
index 96f7427dda1..3c4ae08d2f7 100644
--- a/app/assets/javascripts/vue_shared/components/header_ci_component.vue
+++ b/app/assets/javascripts/vue_shared/components/header_ci_component.vue
@@ -1,12 +1,6 @@
<script>
-import {
- GlTooltipDirective,
- GlButton,
- GlSafeHtmlDirective,
- GlAvatarLink,
- GlAvatarLabeled,
- GlTooltip,
-} from '@gitlab/ui';
+import { GlTooltipDirective, GlButton, GlAvatarLink, GlAvatarLabeled, GlTooltip } from '@gitlab/ui';
+import SafeHtml from '~/vue_shared/directives/safe_html';
import { isGid, getIdFromGraphQLId } from '~/graphql_shared/utils';
import { glEmojiTag } from '~/emoji';
import { __, sprintf } from '~/locale';
@@ -31,7 +25,7 @@ export default {
},
directives: {
GlTooltip: GlTooltipDirective,
- SafeHtml: GlSafeHtmlDirective,
+ SafeHtml,
},
EMOJI_REF: 'EMOJI_REF',
props: {
diff --git a/app/assets/javascripts/vue_shared/components/help_popover.vue b/app/assets/javascripts/vue_shared/components/help_popover.vue
index f349aa78bac..92d468cf970 100644
--- a/app/assets/javascripts/vue_shared/components/help_popover.vue
+++ b/app/assets/javascripts/vue_shared/components/help_popover.vue
@@ -1,5 +1,6 @@
<script>
-import { GlButton, GlPopover, GlSafeHtmlDirective } from '@gitlab/ui';
+import { GlButton, GlPopover } from '@gitlab/ui';
+import SafeHtml from '~/vue_shared/directives/safe_html';
/**
* Render a button with a question mark icon
@@ -12,7 +13,7 @@ export default {
GlPopover,
},
directives: {
- SafeHtml: GlSafeHtmlDirective,
+ SafeHtml,
},
props: {
options: {
diff --git a/app/assets/javascripts/vue_shared/components/listbox_input/listbox_input.stories.js b/app/assets/javascripts/vue_shared/components/listbox_input/listbox_input.stories.js
new file mode 100644
index 00000000000..4106de371cb
--- /dev/null
+++ b/app/assets/javascripts/vue_shared/components/listbox_input/listbox_input.stories.js
@@ -0,0 +1,26 @@
+import ListboxInput from './listbox_input.vue';
+
+export default {
+ component: ListboxInput,
+ title: 'vue_shared/listbox_input',
+};
+
+const Template = (args, { argTypes }) => ({
+ components: { ListboxInput },
+ data() {
+ return { selected: null };
+ },
+ props: Object.keys(argTypes),
+ template: '<listbox-input v-model="selected" v-bind="$props" />',
+});
+
+export const Default = Template.bind({});
+Default.args = {
+ name: 'input_name',
+ defaultToggleText: 'Select an option',
+ items: [
+ { text: 'Option 1', value: '1' },
+ { text: 'Option 2', value: '2' },
+ { text: 'Option 3', value: '3' },
+ ],
+};
diff --git a/app/assets/javascripts/vue_shared/components/listbox_input/listbox_input.vue b/app/assets/javascripts/vue_shared/components/listbox_input/listbox_input.vue
new file mode 100644
index 00000000000..b1809e6a9f3
--- /dev/null
+++ b/app/assets/javascripts/vue_shared/components/listbox_input/listbox_input.vue
@@ -0,0 +1,110 @@
+<script>
+import { GlListbox } from '@gitlab/ui';
+import { __ } from '~/locale';
+
+const MIN_ITEMS_COUNT_FOR_SEARCHING = 20;
+
+export default {
+ i18n: {
+ noResultsText: __('No results found'),
+ },
+ components: {
+ GlListbox,
+ },
+ model: GlListbox.model,
+ props: {
+ name: {
+ type: String,
+ required: true,
+ },
+ defaultToggleText: {
+ type: String,
+ required: true,
+ },
+ selected: {
+ type: String,
+ required: false,
+ default: null,
+ },
+ items: {
+ type: GlListbox.props.items.type,
+ required: true,
+ },
+ },
+ data() {
+ return {
+ searchString: '',
+ };
+ },
+ computed: {
+ allOptions() {
+ const allOptions = [];
+
+ const getOptions = (options) => {
+ for (let i = 0; i < options.length; i += 1) {
+ const option = options[i];
+ if (option.options) {
+ getOptions(option.options);
+ } else {
+ allOptions.push(option);
+ }
+ }
+ };
+ getOptions(this.items);
+
+ return allOptions;
+ },
+ isGrouped() {
+ return this.items.some((item) => item.options !== undefined);
+ },
+ isSearchable() {
+ return this.allOptions.length > MIN_ITEMS_COUNT_FOR_SEARCHING;
+ },
+ filteredItems() {
+ const searchString = this.searchString.toLowerCase();
+
+ if (!searchString) {
+ return this.items;
+ }
+
+ if (this.isGrouped) {
+ return this.items
+ .map(({ text, options }) => {
+ return {
+ text,
+ options: options.filter((option) => option.text.toLowerCase().includes(searchString)),
+ };
+ })
+ .filter(({ options }) => options.length);
+ }
+
+ return this.items.filter((item) => item.text.toLowerCase().includes(searchString));
+ },
+ toggleText() {
+ return this.selected
+ ? this.allOptions.find((option) => option.value === this.selected).text
+ : this.defaultToggleText;
+ },
+ },
+ methods: {
+ search(searchString) {
+ this.searchString = searchString;
+ },
+ },
+};
+</script>
+
+<template>
+ <div>
+ <gl-listbox
+ :selected="selected"
+ :toggle-text="toggleText"
+ :items="filteredItems"
+ :searchable="isSearchable"
+ :no-results-text="$options.i18n.noResultsText"
+ @search="search"
+ @select="$emit($options.model.event, $event)"
+ />
+ <input ref="input" type="hidden" :name="name" :value="selected" />
+ </div>
+</template>
diff --git a/app/assets/javascripts/vue_shared/components/markdown/apply_suggestion.vue b/app/assets/javascripts/vue_shared/components/markdown/apply_suggestion.vue
index caec49c557a..f51ec715678 100644
--- a/app/assets/javascripts/vue_shared/components/markdown/apply_suggestion.vue
+++ b/app/assets/javascripts/vue_shared/components/markdown/apply_suggestion.vue
@@ -74,7 +74,7 @@ export default {
@submit="onApply"
/>
<gl-button
- class="gl-w-auto! gl-mt-3 gl-text-center! gl-hover-text-white! gl-transition-medium! float-right"
+ class="gl-w-auto! gl-mt-3 gl-text-center! gl-transition-medium! float-right"
category="primary"
variant="confirm"
data-qa-selector="commit_with_custom_message_button"
diff --git a/app/assets/javascripts/vue_shared/components/markdown/field.vue b/app/assets/javascripts/vue_shared/components/markdown/field.vue
index 657e4498b53..b5f2602af5e 100644
--- a/app/assets/javascripts/vue_shared/components/markdown/field.vue
+++ b/app/assets/javascripts/vue_shared/components/markdown/field.vue
@@ -1,15 +1,16 @@
<script>
-import { GlIcon, GlSafeHtmlDirective } from '@gitlab/ui';
+import { GlIcon } from '@gitlab/ui';
import $ from 'jquery';
-import '~/behaviors/markdown/render_gfm';
import { debounce, unescape } from 'lodash';
import { createAlert } from '~/flash';
import GLForm from '~/gl_form';
+import SafeHtml from '~/vue_shared/directives/safe_html';
import axios from '~/lib/utils/axios_utils';
import { stripHtml } from '~/lib/utils/text_utility';
import { __, sprintf } from '~/locale';
import Suggestions from '~/vue_shared/components/markdown/suggestions.vue';
import glFeatureFlagsMixin from '~/vue_shared/mixins/gl_feature_flags_mixin';
+import { renderGFM } from '~/behaviors/markdown/render_gfm';
import MarkdownHeader from './header.vue';
import MarkdownToolbar from './toolbar.vue';
@@ -25,7 +26,7 @@ export default {
Suggestions,
},
directives: {
- SafeHtml: GlSafeHtmlDirective,
+ SafeHtml,
},
mixins: [glFeatureFlagsMixin()],
props: {
@@ -313,7 +314,9 @@ export default {
this.markdownPreview = data.body || __('Nothing to preview.');
this.$nextTick()
- .then(() => $(this.$refs['markdown-preview']).renderGFM())
+ .then(() => {
+ renderGFM(this.$refs['markdown-preview']);
+ })
.catch(() =>
createAlert({
message: __('Error rendering Markdown preview'),
diff --git a/app/assets/javascripts/vue_shared/components/markdown/field_view.vue b/app/assets/javascripts/vue_shared/components/markdown/field_view.vue
index d77123371f2..84d40db07bb 100644
--- a/app/assets/javascripts/vue_shared/components/markdown/field_view.vue
+++ b/app/assets/javascripts/vue_shared/components/markdown/field_view.vue
@@ -1,15 +1,9 @@
<script>
-import $ from 'jquery';
-import '~/behaviors/markdown/render_gfm';
+import { renderGFM } from '~/behaviors/markdown/render_gfm';
export default {
mounted() {
- this.renderGFM();
- },
- methods: {
- renderGFM() {
- $(this.$el).renderGFM();
- },
+ renderGFM(this.$el);
},
};
</script>
diff --git a/app/assets/javascripts/vue_shared/components/markdown/markdown_editor.vue b/app/assets/javascripts/vue_shared/components/markdown/markdown_editor.vue
index c0712e46613..d01eae0308f 100644
--- a/app/assets/javascripts/vue_shared/components/markdown/markdown_editor.vue
+++ b/app/assets/javascripts/vue_shared/components/markdown/markdown_editor.vue
@@ -82,6 +82,11 @@ export default {
required: false,
default: false,
},
+ useBottomToolbar: {
+ type: Boolean,
+ required: false,
+ default: false,
+ },
},
data() {
return {
@@ -197,6 +202,7 @@ export default {
:uploads-path="uploadsPath"
:markdown="value"
:autofocus="contentEditorAutofocused"
+ :use-bottom-toolbar="useBottomToolbar"
@initialized="setEditorAsAutofocused"
@change="updateMarkdownFromContentEditor"
@loading="disableSwitchEditingControl"
diff --git a/app/assets/javascripts/vue_shared/components/markdown/suggestion_diff_row.vue b/app/assets/javascripts/vue_shared/components/markdown/suggestion_diff_row.vue
index a04f8616acb..0b598d3acaf 100644
--- a/app/assets/javascripts/vue_shared/components/markdown/suggestion_diff_row.vue
+++ b/app/assets/javascripts/vue_shared/components/markdown/suggestion_diff_row.vue
@@ -1,5 +1,5 @@
<script>
-import { GlSafeHtmlDirective as SafeHtml } from '@gitlab/ui';
+import SafeHtml from '~/vue_shared/directives/safe_html';
export default {
name: 'SuggestionDiffRow',
diff --git a/app/assets/javascripts/vue_shared/components/markdown/suggestions.vue b/app/assets/javascripts/vue_shared/components/markdown/suggestions.vue
index 30d72332c90..c307601e670 100644
--- a/app/assets/javascripts/vue_shared/components/markdown/suggestions.vue
+++ b/app/assets/javascripts/vue_shared/components/markdown/suggestions.vue
@@ -1,6 +1,6 @@
<script>
-import { GlSafeHtmlDirective as SafeHtml } from '@gitlab/ui';
import Vue from 'vue';
+import SafeHtml from '~/vue_shared/directives/safe_html';
import { createAlert } from '~/flash';
import { __ } from '~/locale';
import SuggestionDiff from './suggestion_diff.vue';
diff --git a/app/assets/javascripts/vue_shared/components/markdown_drawer/makrdown_drawer.stories.js b/app/assets/javascripts/vue_shared/components/markdown_drawer/markdown_drawer.stories.js
index 03bd64e2a57..03bd64e2a57 100644
--- a/app/assets/javascripts/vue_shared/components/markdown_drawer/makrdown_drawer.stories.js
+++ b/app/assets/javascripts/vue_shared/components/markdown_drawer/markdown_drawer.stories.js
diff --git a/app/assets/javascripts/vue_shared/components/markdown_drawer/markdown_drawer.vue b/app/assets/javascripts/vue_shared/components/markdown_drawer/markdown_drawer.vue
index a4b509f8656..379f22fdc6f 100644
--- a/app/assets/javascripts/vue_shared/components/markdown_drawer/markdown_drawer.vue
+++ b/app/assets/javascripts/vue_shared/components/markdown_drawer/markdown_drawer.vue
@@ -1,9 +1,9 @@
<script>
-import { GlSafeHtmlDirective as SafeHtml, GlDrawer, GlAlert, GlSkeletonLoader } from '@gitlab/ui';
-import $ from 'jquery';
-import '~/behaviors/markdown/render_gfm';
+import { GlDrawer, GlAlert, GlSkeletonLoader } from '@gitlab/ui';
+import SafeHtml from '~/vue_shared/directives/safe_html';
import { s__ } from '~/locale';
import { contentTop } from '~/lib/utils/common_utils';
+import { renderGFM } from '~/behaviors/markdown/render_gfm';
import { getRenderedMarkdown } from './utils/fetch';
export const cache = {};
@@ -34,13 +34,9 @@ export default {
title: '',
body: null,
open: false,
+ drawerTop: '0px',
};
},
- computed: {
- drawerOffsetTop() {
- return `${contentTop()}px`;
- },
- },
watch: {
documentPath: {
immediate: true,
@@ -76,18 +72,23 @@ export default {
cache[this.documentPath] = { title, body };
}
},
+ getDrawerTop() {
+ this.drawerTop = `${contentTop()}px`;
+ },
renderGLFM() {
this.$nextTick(() => {
- $(this.$refs['content-element']).renderGFM();
+ renderGFM(this.$refs['content-element']);
});
},
closeDrawer() {
this.open = false;
},
toggleDrawer() {
+ this.getDrawerTop();
this.open = !this.open;
},
openDrawer() {
+ this.getDrawerTop();
this.open = true;
},
},
@@ -97,7 +98,7 @@ export default {
};
</script>
<template>
- <gl-drawer :header-height="drawerOffsetTop" :open="open" header-sticky @close="closeDrawer">
+ <gl-drawer :header-height="drawerTop" :open="open" header-sticky @close="closeDrawer">
<template #title>
<h4 data-testid="title-element" class="gl-m-0">{{ title }}</h4>
</template>
diff --git a/app/assets/javascripts/vue_shared/components/markdown_drawer/utils/fetch.js b/app/assets/javascripts/vue_shared/components/markdown_drawer/utils/fetch.js
index 7c8e1bc160a..27237f2f16b 100644
--- a/app/assets/javascripts/vue_shared/components/markdown_drawer/utils/fetch.js
+++ b/app/assets/javascripts/vue_shared/components/markdown_drawer/utils/fetch.js
@@ -16,7 +16,7 @@ export const getRenderedMarkdown = (documentPath) => {
return axios
.get(helpPagePath(documentPath))
.then(({ data }) => {
- const { body, title } = splitDocument(data.html);
+ const { body, title } = splitDocument(data);
return {
body,
title,
diff --git a/app/assets/javascripts/vue_shared/components/metric_images/metric_images_tab.vue b/app/assets/javascripts/vue_shared/components/metric_images/metric_images_tab.vue
index e23721da223..2cadc87eca3 100644
--- a/app/assets/javascripts/vue_shared/components/metric_images/metric_images_tab.vue
+++ b/app/assets/javascripts/vue_shared/components/metric_images/metric_images_tab.vue
@@ -1,5 +1,5 @@
<script>
-import { GlFormGroup, GlFormInput, GlLoadingIcon, GlModal, GlTab } from '@gitlab/ui';
+import { GlFormGroup, GlFormInput, GlLoadingIcon, GlModal } from '@gitlab/ui';
import { mapState, mapActions } from 'vuex';
import { __, s__ } from '~/locale';
import UploadDropzone from '~/vue_shared/components/upload_dropzone/upload_dropzone.vue';
@@ -11,7 +11,6 @@ export default {
GlFormInput,
GlLoadingIcon,
GlModal,
- GlTab,
MetricImagesTable,
UploadDropzone,
},
@@ -82,7 +81,7 @@ export default {
</script>
<template>
- <gl-tab :title="s__('Incident|Metrics')" data-testid="metrics-tab">
+ <div>
<div v-if="isLoadingMetricImages">
<gl-loading-icon class="gl-p-5" size="sm" />
</div>
@@ -117,5 +116,5 @@ export default {
:drop-description-message="$options.i18n.dropDescription"
@change="openMetricDialog"
/>
- </gl-tab>
+ </div>
</template>
diff --git a/app/assets/javascripts/vue_shared/components/notes/placeholder_note.vue b/app/assets/javascripts/vue_shared/components/notes/placeholder_note.vue
index cf34a60c363..748d6082abd 100644
--- a/app/assets/javascripts/vue_shared/components/notes/placeholder_note.vue
+++ b/app/assets/javascripts/vue_shared/components/notes/placeholder_note.vue
@@ -16,8 +16,9 @@
* :note="{body: 'This is a note'}"
* />
*/
-import { GlSafeHtmlDirective as SafeHtml, GlAvatarLink, GlAvatar } from '@gitlab/ui';
+import { GlAvatarLink, GlAvatar } from '@gitlab/ui';
import { mapGetters } from 'vuex';
+import SafeHtml from '~/vue_shared/directives/safe_html';
import { renderMarkdown } from '~/notes/utils';
import TimelineEntryItem from '~/vue_shared/components/notes/timeline_entry_item.vue';
diff --git a/app/assets/javascripts/vue_shared/components/notes/system_note.vue b/app/assets/javascripts/vue_shared/components/notes/system_note.vue
index 1ae5045b34f..1cbbdf0deb0 100644
--- a/app/assets/javascripts/vue_shared/components/notes/system_note.vue
+++ b/app/assets/javascripts/vue_shared/components/notes/system_note.vue
@@ -16,22 +16,17 @@
* }"
* />
*/
-import {
- GlButton,
- GlSkeletonLoader,
- GlTooltipDirective,
- GlIcon,
- GlSafeHtmlDirective as SafeHtml,
-} from '@gitlab/ui';
+import { GlButton, GlSkeletonLoader, GlTooltipDirective, GlIcon } from '@gitlab/ui';
import $ from 'jquery';
import { mapGetters, mapActions, mapState } from 'vuex';
+import SafeHtml from '~/vue_shared/directives/safe_html';
import descriptionVersionHistoryMixin from 'ee_else_ce/notes/mixins/description_version_history';
-import '~/behaviors/markdown/render_gfm';
import axios from '~/lib/utils/axios_utils';
import { __ } from '~/locale';
import NoteHeader from '~/notes/components/note_header.vue';
import glFeatureFlagsMixin from '~/vue_shared/mixins/gl_feature_flags_mixin';
import { spriteIcon } from '~/lib/utils/common_utils';
+import { renderGFM } from '~/behaviors/markdown/render_gfm';
import TimelineEntryItem from './timeline_entry_item.vue';
const MAX_VISIBLE_COMMIT_LIST_COUNT = 3;
@@ -94,7 +89,7 @@ export default {
},
},
mounted() {
- $(this.$refs['gfm-content']).renderGFM();
+ renderGFM(this.$refs['gfm-content']);
},
methods: {
...mapActions(['fetchDescriptionVersion', 'softDeleteDescriptionVersion']),
@@ -205,7 +200,7 @@ export default {
<tr v-for="line in lines" v-once :key="line.line_code" class="line_holder">
<td
:class="line.type"
- class="diff-line-num old_line gl-border-bottom-0! gl-border-top-0!"
+ class="diff-line-num old_line gl-border-bottom-0! gl-border-top-0! gl-border-0! gl-rounded-0!"
>
{{ line.old_line }}
</td>
@@ -217,7 +212,7 @@ export default {
</td>
<td
:class="line.type"
- class="line_content gl-display-table-cell!"
+ class="line_content gl-display-table-cell! gl-border-0! gl-rounded-0!"
v-html="line.rich_text /* eslint-disable-line vue/no-v-html */"
></td>
</tr>
diff --git a/app/assets/javascripts/vue_shared/components/paginated_table_with_search_and_tabs/paginated_table_with_search_and_tabs.vue b/app/assets/javascripts/vue_shared/components/paginated_table_with_search_and_tabs/paginated_table_with_search_and_tabs.vue
index 867222279b2..57e3a97244e 100644
--- a/app/assets/javascripts/vue_shared/components/paginated_table_with_search_and_tabs/paginated_table_with_search_and_tabs.vue
+++ b/app/assets/javascripts/vue_shared/components/paginated_table_with_search_and_tabs/paginated_table_with_search_and_tabs.vue
@@ -1,22 +1,19 @@
<script>
-import {
- GlAlert,
- GlBadge,
- GlPagination,
- GlTab,
- GlTabs,
- GlSafeHtmlDirective as SafeHtml,
-} from '@gitlab/ui';
+import { GlAlert, GlBadge, GlPagination, GlTab, GlTabs } from '@gitlab/ui';
+import SafeHtml from '~/vue_shared/directives/safe_html';
import Api from '~/api';
import { updateHistory, setUrlParams } from '~/lib/utils/url_utility';
import Tracking from '~/tracking';
import {
- OPERATOR_IS_ONLY,
+ FILTERED_SEARCH_TERM,
+ OPERATORS_IS,
TOKEN_TITLE_ASSIGNEE,
TOKEN_TITLE_AUTHOR,
+ TOKEN_TYPE_ASSIGNEE,
+ TOKEN_TYPE_AUTHOR,
} from '~/vue_shared/components/filtered_search_bar/constants';
import FilteredSearchBar from '~/vue_shared/components/filtered_search_bar/filtered_search_bar_root.vue';
-import AuthorToken from '~/vue_shared/components/filtered_search_bar/tokens/author_token.vue';
+import UserToken from '~/vue_shared/components/filtered_search_bar/tokens/user_token.vue';
import { initialPaginationState, defaultI18n, defaultPageSize } from './constants';
import { isAny } from './utils';
@@ -95,7 +92,7 @@ export default {
filterSearchTokens: {
type: Array,
required: false,
- default: () => ['author_username', 'assignee_username'],
+ default: () => [TOKEN_TYPE_AUTHOR, TOKEN_TYPE_ASSIGNEE],
},
},
data() {
@@ -113,26 +110,26 @@ export default {
defaultTokens() {
return [
{
- type: 'author_username',
+ type: TOKEN_TYPE_AUTHOR,
icon: 'user',
title: TOKEN_TITLE_AUTHOR,
unique: true,
symbol: '@',
- token: AuthorToken,
- operators: OPERATOR_IS_ONLY,
+ token: UserToken,
+ operators: OPERATORS_IS,
fetchPath: this.projectPath,
- fetchAuthors: Api.projectUsers.bind(Api),
+ fetchUsers: Api.projectUsers.bind(Api),
},
{
- type: 'assignee_username',
+ type: TOKEN_TYPE_ASSIGNEE,
icon: 'user',
title: TOKEN_TITLE_ASSIGNEE,
unique: true,
symbol: '@',
- token: AuthorToken,
- operators: OPERATOR_IS_ONLY,
+ token: UserToken,
+ operators: OPERATORS_IS,
fetchPath: this.projectPath,
- fetchAuthors: Api.projectUsers.bind(Api),
+ fetchUsers: Api.projectUsers.bind(Api),
},
];
},
@@ -144,14 +141,14 @@ export default {
if (this.authorUsername) {
value.push({
- type: 'author_username',
+ type: TOKEN_TYPE_AUTHOR,
value: { data: this.authorUsername },
});
}
if (this.assigneeUsername) {
value.push({
- type: 'assignee_username',
+ type: TOKEN_TYPE_ASSIGNEE,
value: { data: this.assigneeUsername },
});
}
@@ -226,13 +223,13 @@ export default {
filters.forEach((filter) => {
if (typeof filter === 'object') {
switch (filter.type) {
- case 'author_username':
+ case TOKEN_TYPE_AUTHOR:
filterParams.authorUsername = isAny(filter.value.data);
break;
- case 'assignee_username':
+ case TOKEN_TYPE_ASSIGNEE:
filterParams.assigneeUsername = isAny(filter.value.data);
break;
- case 'filtered-search-term':
+ case FILTERED_SEARCH_TERM:
if (filter.value.data !== '') filterParams.search = filter.value.data;
break;
default:
diff --git a/app/assets/javascripts/vue_shared/components/project_selector/project_list_item.vue b/app/assets/javascripts/vue_shared/components/project_selector/project_list_item.vue
index 66643ff4026..16bc8070dc1 100644
--- a/app/assets/javascripts/vue_shared/components/project_selector/project_list_item.vue
+++ b/app/assets/javascripts/vue_shared/components/project_selector/project_list_item.vue
@@ -1,9 +1,10 @@
<script>
-import { GlButton, GlIcon, GlSafeHtmlDirective as SafeHtml } from '@gitlab/ui';
+import { GlButton, GlIcon } from '@gitlab/ui';
import { isString } from 'lodash';
import highlight from '~/lib/utils/highlight';
import { truncateNamespace } from '~/lib/utils/text_utility';
import ProjectAvatar from '~/vue_shared/components/project_avatar.vue';
+import SafeHtml from '~/vue_shared/directives/safe_html';
export default {
name: 'ProjectListItem',
diff --git a/app/assets/javascripts/vue_shared/components/registry/registry_search.vue b/app/assets/javascripts/vue_shared/components/registry/registry_search.vue
index 8c9c7c63db1..c990baaa2f3 100644
--- a/app/assets/javascripts/vue_shared/components/registry/registry_search.vue
+++ b/app/assets/javascripts/vue_shared/components/registry/registry_search.vue
@@ -1,7 +1,7 @@
<script>
import { GlSorting, GlSortingItem, GlFilteredSearch } from '@gitlab/ui';
-import { FILTERED_SEARCH_TERM } from '~/packages_and_registries/shared/constants';
import { SORT_DIRECTION_UI } from '~/search/sort/constants';
+import { FILTERED_SEARCH_TERM } from '~/vue_shared/components/filtered_search_bar/constants';
const ASCENDING_ORDER = 'asc';
const DESCENDING_ORDER = 'desc';
diff --git a/app/assets/javascripts/vue_shared/components/sidebar/copyable_field.vue b/app/assets/javascripts/vue_shared/components/sidebar/copyable_field.vue
deleted file mode 100644
index 6538de085b0..00000000000
--- a/app/assets/javascripts/vue_shared/components/sidebar/copyable_field.vue
+++ /dev/null
@@ -1,86 +0,0 @@
-<script>
-import { GlLoadingIcon, GlSprintf } 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: {
- ClipboardButton,
- GlLoadingIcon,
- GlSprintf,
- },
- 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 });
- },
- },
- 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 js-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"
- >
- <gl-sprintf :message="$options.i18n.templateText">
- <template #name>{{ name }}</template>
- <template #value>{{ value }}</template>
- </gl-sprintf>
- </span>
-
- <gl-loading-icon v-if="isLoading" size="sm" 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
deleted file mode 100644
index 02323e5a0c6..00000000000
--- a/app/assets/javascripts/vue_shared/components/sidebar/issuable_move_dropdown.vue
+++ /dev/null
@@ -1,217 +0,0 @@
-<script>
-import {
- GlIcon,
- GlLoadingIcon,
- GlDropdown,
- GlDropdownForm,
- GlDropdownItem,
- GlSearchBoxByType,
- GlButton,
- GlTooltipDirective as GlTooltip,
-} from '@gitlab/ui';
-
-import axios from '~/lib/utils/axios_utils';
-
-export default {
- components: {
- GlIcon,
- GlLoadingIcon,
- GlDropdown,
- GlDropdownForm,
- GlDropdownItem,
- GlSearchBoxByType,
- GlButton,
- },
- directives: {
- GlTooltip,
- },
- props: {
- projectsFetchPath: {
- type: String,
- required: true,
- },
- dropdownButtonTitle: {
- type: String,
- required: true,
- },
- dropdownHeaderTitle: {
- type: String,
- required: true,
- },
- moveInProgress: {
- type: Boolean,
- required: false,
- default: false,
- },
- disabled: {
- type: Boolean,
- required: false,
- default: false,
- },
- },
- data() {
- return {
- projectsListLoading: false,
- projectsListLoadFailed: false,
- searchKey: '',
- projects: [],
- selectedProject: null,
- projectItemClick: false,
- };
- },
- computed: {
- hasNoSearchResults() {
- return Boolean(
- !this.projectsListLoading &&
- !this.projectsListLoadFailed &&
- this.searchKey &&
- !this.projects.length,
- );
- },
- failedToLoadResults() {
- return !this.projectsListLoading && this.projectsListLoadFailed;
- },
- },
- watch: {
- searchKey(value = '') {
- this.fetchProjects(value);
- },
- },
- methods: {
- fetchProjects(search = '') {
- this.projectsListLoading = true;
- this.projectsListLoadFailed = false;
- return axios
- .get(this.projectsFetchPath, {
- params: {
- search,
- },
- })
- .then(({ data }) => {
- this.projects = data;
- this.$refs.searchInput.focusInput();
- })
- .catch(() => {
- this.projectsListLoadFailed = true;
- })
- .finally(() => {
- this.projectsListLoading = false;
- });
- },
- isSelectedProject(project) {
- if (this.selectedProject) {
- return this.selectedProject.id === project.id;
- }
- return false;
- },
- /**
- * This handler is to prevent dropdown
- * from closing when an item is selected
- * and emit an event only when dropdown closes.
- */
- handleDropdownHide(e) {
- if (this.projectItemClick) {
- e.preventDefault();
- this.projectItemClick = false;
- } else {
- this.$emit('dropdown-close');
- }
- },
- handleDropdownCloseClick() {
- this.$refs.dropdown.hide();
- },
- handleProjectSelect(project) {
- this.selectedProject = project.id === this.selectedProject?.id ? null : project;
- this.projectItemClick = true;
- },
- handleMoveClick() {
- this.$refs.dropdown.hide();
- this.$emit('move-issuable', this.selectedProject);
- },
- },
-};
-</script>
-
-<template>
- <div class="js-issuable-move-block issuable-move-dropdown sidebar-move-issue-dropdown">
- <div
- v-gl-tooltip.left.viewport
- data-testid="move-collapsed"
- :title="dropdownButtonTitle"
- class="sidebar-collapsed-icon"
- @click="$emit('toggle-collapse')"
- >
- <gl-icon name="arrow-right" />
- </div>
- <gl-dropdown
- ref="dropdown"
- :block="true"
- :disabled="moveInProgress || disabled"
- class="hide-collapsed"
- toggle-class="js-sidebar-dropdown-toggle"
- @shown="fetchProjects"
- @hide="handleDropdownHide"
- >
- <template #button-content
- ><gl-loading-icon v-if="moveInProgress" size="sm" class="gl-mr-3" />{{
- dropdownButtonTitle
- }}</template
- >
- <gl-dropdown-form class="gl-pt-0">
- <div
- data-testid="header"
- class="gl-display-flex gl-pb-3 gl-border-1 gl-border-b-solid gl-border-gray-100"
- >
- <span class="gl-flex-grow-1 gl-text-center gl-font-weight-bold gl-py-1">{{
- dropdownHeaderTitle
- }}</span>
- <gl-button
- variant="link"
- icon="close"
- class="gl-mr-2 gl-w-auto! gl-p-2!"
- :aria-label="__('Close')"
- @click.prevent="handleDropdownCloseClick"
- />
- </div>
- <gl-search-box-by-type
- ref="searchInput"
- v-model.trim="searchKey"
- :placeholder="__('Search project')"
- :debounce="300"
- />
- <div data-testid="content" class="dropdown-content">
- <gl-loading-icon v-if="projectsListLoading" size="lg" class="gl-p-5" />
- <ul v-else>
- <gl-dropdown-item
- v-for="project in projects"
- :key="project.id"
- is-check-item
- :is-checked="isSelectedProject(project)"
- @click.stop.prevent="handleProjectSelect(project)"
- >{{ project.name_with_namespace }}</gl-dropdown-item
- >
- </ul>
- <div v-if="hasNoSearchResults" class="gl-text-center gl-p-3">
- {{ __('No matching results') }}
- </div>
- <div v-if="failedToLoadResults" class="gl-text-center gl-p-3">
- {{ __('Failed to load projects') }}
- </div>
- </div>
- <div
- data-testid="footer"
- class="gl-pt-3 gl-px-3 gl-border-1 gl-border-t-solid gl-border-gray-100"
- >
- <gl-button
- category="primary"
- variant="confirm"
- :disabled="!Boolean(selectedProject)"
- class="gl-text-center! issuable-move-button"
- @click="handleMoveClick"
- >{{ __('Move') }}</gl-button
- >
- </div>
- </gl-dropdown-form>
- </gl-dropdown>
- </div>
-</template>
diff --git a/app/assets/javascripts/vue_shared/components/sidebar/labels_select_vue/constants.js b/app/assets/javascripts/vue_shared/components/sidebar/labels_select_vue/constants.js
deleted file mode 100644
index 00c54313292..00000000000
--- a/app/assets/javascripts/vue_shared/components/sidebar/labels_select_vue/constants.js
+++ /dev/null
@@ -1,5 +0,0 @@
-export const DropdownVariant = {
- Sidebar: 'sidebar',
- Standalone: 'standalone',
- Embedded: 'embedded',
-};
diff --git a/app/assets/javascripts/vue_shared/components/sidebar/labels_select_vue/dropdown_button.vue b/app/assets/javascripts/vue_shared/components/sidebar/labels_select_vue/dropdown_button.vue
deleted file mode 100644
index 9388ef4ba45..00000000000
--- a/app/assets/javascripts/vue_shared/components/sidebar/labels_select_vue/dropdown_button.vue
+++ /dev/null
@@ -1,45 +0,0 @@
-<script>
-import { GlButton, GlIcon } from '@gitlab/ui';
-import { mapActions, mapGetters } from 'vuex';
-
-// @deprecated This component should only be used when there is no GraphQL API.
-// In most cases you should use
-// `app/assets/javascripts/vue_shared/components/sidebar/labels_select_widget` instead.
-export default {
- components: {
- GlButton,
- GlIcon,
- },
- computed: {
- ...mapGetters([
- 'dropdownButtonText',
- 'isDropdownVariantStandalone',
- 'isDropdownVariantEmbedded',
- ]),
- },
- methods: {
- ...mapActions(['toggleDropdownContents']),
- handleButtonClick(e) {
- if (this.isDropdownVariantStandalone || this.isDropdownVariantEmbedded) {
- this.toggleDropdownContents();
- }
-
- if (this.isDropdownVariantStandalone) {
- e.stopPropagation();
- }
- },
- },
-};
-</script>
-
-<template>
- <gl-button
- class="labels-select-dropdown-button js-dropdown-button w-100 text-left"
- @click="handleButtonClick"
- >
- <span class="dropdown-toggle-text gl-pointer-events-none flex-fill">
- {{ dropdownButtonText }}
- </span>
- <gl-icon name="chevron-down" class="gl-pointer-events-none float-right" />
- </gl-button>
-</template>
diff --git a/app/assets/javascripts/vue_shared/components/sidebar/labels_select_vue/dropdown_contents.vue b/app/assets/javascripts/vue_shared/components/sidebar/labels_select_vue/dropdown_contents.vue
deleted file mode 100644
index 1064cbc26e3..00000000000
--- a/app/assets/javascripts/vue_shared/components/sidebar/labels_select_vue/dropdown_contents.vue
+++ /dev/null
@@ -1,48 +0,0 @@
-<script>
-import { mapGetters, mapState } from 'vuex';
-
-import DropdownContentsCreateView from './dropdown_contents_create_view.vue';
-import DropdownContentsLabelsView from './dropdown_contents_labels_view.vue';
-
-// @deprecated This component should only be used when there is no GraphQL API.
-// In most cases you should use
-// `app/assets/javascripts/vue_shared/components/sidebar/labels_select_widget/dropdown_contents.vue` instead.
-export default {
- components: {
- DropdownContentsLabelsView,
- DropdownContentsCreateView,
- },
- props: {
- renderOnTop: {
- type: Boolean,
- required: false,
- default: false,
- },
- },
- computed: {
- ...mapState(['showDropdownContentsCreateView']),
- ...mapGetters(['isDropdownVariantSidebar']),
- dropdownContentsView() {
- if (this.showDropdownContentsCreateView) {
- return 'dropdown-contents-create-view';
- }
- return 'dropdown-contents-labels-view';
- },
- directionStyle() {
- const bottom = this.isDropdownVariantSidebar ? '3rem' : '2rem';
- return this.renderOnTop ? { bottom } : {};
- },
- },
-};
-</script>
-
-<template>
- <div
- class="labels-select-dropdown-contents gl-w-full gl-my-2 gl-py-3 gl-rounded-base gl-absolute"
- data-testid="labels-select-dropdown-contents"
- data-qa-selector="labels_dropdown_content"
- :style="directionStyle"
- >
- <component :is="dropdownContentsView" />
- </div>
-</template>
diff --git a/app/assets/javascripts/vue_shared/components/sidebar/labels_select_vue/dropdown_contents_create_view.vue b/app/assets/javascripts/vue_shared/components/sidebar/labels_select_vue/dropdown_contents_create_view.vue
deleted file mode 100644
index 3ff3755de46..00000000000
--- a/app/assets/javascripts/vue_shared/components/sidebar/labels_select_vue/dropdown_contents_create_view.vue
+++ /dev/null
@@ -1,122 +0,0 @@
-<script>
-import { GlTooltipDirective, GlButton, GlFormInput, GlLink, GlLoadingIcon } from '@gitlab/ui';
-import { mapState, mapActions } from 'vuex';
-
-// @deprecated This component should only be used when there is no GraphQL API.
-// In most cases you should use
-// `app/assets/javascripts/vue_shared/components/sidebar/labels_select_widget/dropdown_contents_create_view.vue` instead.
-export default {
- components: {
- GlButton,
- GlFormInput,
- GlLink,
- GlLoadingIcon,
- },
- directives: {
- GlTooltip: GlTooltipDirective,
- },
- data() {
- return {
- labelTitle: '',
- selectedColor: '',
- };
- },
- computed: {
- ...mapState(['labelsCreateTitle', 'labelCreateInProgress']),
- disableCreate() {
- return !this.labelTitle.length || !this.selectedColor.length || this.labelCreateInProgress;
- },
- suggestedColors() {
- const colorsMap = gon.suggested_label_colors;
- return Object.keys(colorsMap).map((color) => ({ [color]: colorsMap[color] }));
- },
- },
- methods: {
- ...mapActions(['toggleDropdownContents', 'toggleDropdownContentsCreateView', 'createLabel']),
- getColorCode(color) {
- return Object.keys(color).pop();
- },
- getColorName(color) {
- return Object.values(color).pop();
- },
- handleColorClick(color) {
- this.selectedColor = this.getColorCode(color);
- },
- handleCreateClick() {
- this.createLabel({
- title: this.labelTitle,
- color: this.selectedColor,
- });
- },
- },
-};
-</script>
-
-<template>
- <div class="labels-select-contents-create js-labels-create">
- <div class="dropdown-title d-flex align-items-center pt-0 pb-2 gl-mb-0">
- <gl-button
- :aria-label="__('Go back')"
- category="tertiary"
- size="small"
- class="js-btn-back dropdown-header-button p-0"
- icon="arrow-left"
- @click="toggleDropdownContentsCreateView"
- />
- <span class="flex-grow-1">{{ labelsCreateTitle }}</span>
- <gl-button
- :aria-label="__('Close')"
- category="tertiary"
- size="small"
- class="dropdown-header-button p-0"
- icon="close"
- @click="toggleDropdownContents"
- />
- </div>
- <div class="dropdown-input">
- <gl-form-input
- v-model.trim="labelTitle"
- :placeholder="__('Name new label')"
- :autofocus="true"
- />
- </div>
- <div class="dropdown-content px-2">
- <div class="suggest-colors suggest-colors-dropdown mt-0 mb-2">
- <gl-link
- v-for="(color, index) in suggestedColors"
- :key="index"
- v-gl-tooltip:tooltipcontainer
- :style="{ backgroundColor: getColorCode(color) }"
- :title="getColorName(color)"
- @click.prevent="handleColorClick(color)"
- />
- </div>
- <div class="color-input-container gl-display-flex">
- <span
- class="dropdown-label-color-preview position-relative position-relative d-inline-block"
- :style="{ backgroundColor: selectedColor }"
- ></span>
- <gl-form-input
- v-model.trim="selectedColor"
- class="gl-rounded-top-left-none gl-rounded-bottom-left-none gl-mb-2"
- :placeholder="__('Use custom color #FF0000')"
- />
- </div>
- </div>
- <div class="dropdown-actions clearfix pt-2 px-2">
- <gl-button
- :disabled="disableCreate"
- category="primary"
- variant="confirm"
- class="float-left d-flex align-items-center"
- @click="handleCreateClick"
- >
- <gl-loading-icon v-show="labelCreateInProgress" size="sm" :inline="true" class="mr-1" />
- {{ __('Create') }}
- </gl-button>
- <gl-button class="float-right js-btn-cancel-create" @click="toggleDropdownContentsCreateView">
- {{ __('Cancel') }}
- </gl-button>
- </div>
- </div>
-</template>
diff --git a/app/assets/javascripts/vue_shared/components/sidebar/labels_select_vue/dropdown_contents_labels_view.vue b/app/assets/javascripts/vue_shared/components/sidebar/labels_select_vue/dropdown_contents_labels_view.vue
deleted file mode 100644
index e235bfde394..00000000000
--- a/app/assets/javascripts/vue_shared/components/sidebar/labels_select_vue/dropdown_contents_labels_view.vue
+++ /dev/null
@@ -1,230 +0,0 @@
-<script>
-import {
- GlIntersectionObserver,
- GlLoadingIcon,
- GlButton,
- GlSearchBoxByType,
- GlLink,
-} from '@gitlab/ui';
-import fuzzaldrinPlus from 'fuzzaldrin-plus';
-import { mapState, mapGetters, mapActions } from 'vuex';
-
-import { UP_KEY_CODE, DOWN_KEY_CODE, ENTER_KEY_CODE, ESC_KEY_CODE } from '~/lib/utils/keycodes';
-
-import LabelItem from './label_item.vue';
-
-// @deprecated This component should only be used when there is no GraphQL API.
-// In most cases you should use
-// `app/assets/javascripts/vue_shared/components/sidebar/labels_select_widget/dropdown_contents_labels_view.vue` instead.
-export default {
- components: {
- GlIntersectionObserver,
- GlLoadingIcon,
- GlButton,
- GlSearchBoxByType,
- GlLink,
- LabelItem,
- },
- data() {
- return {
- searchKey: '',
- currentHighlightItem: -1,
- };
- },
- computed: {
- ...mapState([
- 'allowLabelCreate',
- 'allowMultiselect',
- 'labelsManagePath',
- 'labels',
- 'labelsFetchInProgress',
- 'labelsListTitle',
- 'footerCreateLabelTitle',
- 'footerManageLabelTitle',
- ]),
- ...mapGetters(['selectedLabelsList', 'isDropdownVariantSidebar', 'isDropdownVariantEmbedded']),
- visibleLabels() {
- if (this.searchKey) {
- return fuzzaldrinPlus.filter(this.labels, this.searchKey, {
- key: ['title'],
- });
- }
- return this.labels;
- },
- showDropdownFooter() {
- return (
- (this.isDropdownVariantSidebar || this.isDropdownVariantEmbedded) &&
- (this.allowLabelCreate || this.labelsManagePath)
- );
- },
- showNoMatchingResultsMessage() {
- return Boolean(this.searchKey) && this.visibleLabels.length === 0;
- },
- },
- watch: {
- searchKey(value) {
- // When there is search string present
- // and there are matching results,
- // highlight first item by default.
- if (value && this.visibleLabels.length) {
- this.currentHighlightItem = 0;
- }
- },
- },
- methods: {
- ...mapActions([
- 'toggleDropdownContents',
- 'toggleDropdownContentsCreateView',
- 'fetchLabels',
- 'receiveLabelsSuccess',
- 'updateSelectedLabels',
- 'toggleDropdownContents',
- ]),
- isLabelSelected(label) {
- return this.selectedLabelsList.includes(label.id);
- },
- /**
- * This method scrolls item from dropdown into
- * the view if it is off the viewable area of the
- * container.
- */
- scrollIntoViewIfNeeded() {
- const highlightedLabel = this.$refs.labelsListContainer.querySelector('.is-focused');
-
- if (highlightedLabel) {
- const container = this.$refs.labelsListContainer.getBoundingClientRect();
- const label = highlightedLabel.getBoundingClientRect();
-
- if (label.bottom > container.bottom) {
- this.$refs.labelsListContainer.scrollTop += label.bottom - container.bottom;
- } else if (label.top < container.top) {
- this.$refs.labelsListContainer.scrollTop -= container.top - label.top;
- }
- }
- },
- handleComponentAppear() {
- // We can avoid putting `catch` block here
- // as failure is handled within actions.js already.
- return this.fetchLabels().then(() => {
- this.$refs.searchInput.focusInput();
- });
- },
- /**
- * We want to remove loaded labels to ensure component
- * fetches fresh set of labels every time when shown.
- */
- handleComponentDisappear() {
- this.receiveLabelsSuccess([]);
- },
- handleCreateLabelClick() {
- this.receiveLabelsSuccess([]);
- this.toggleDropdownContentsCreateView();
- },
- /**
- * This method enables keyboard navigation support for
- * the dropdown.
- */
- handleKeyDown(e) {
- if (e.keyCode === UP_KEY_CODE && this.currentHighlightItem > 0) {
- this.currentHighlightItem -= 1;
- } else if (
- e.keyCode === DOWN_KEY_CODE &&
- this.currentHighlightItem < this.visibleLabels.length - 1
- ) {
- this.currentHighlightItem += 1;
- } else if (e.keyCode === ENTER_KEY_CODE && this.currentHighlightItem > -1) {
- this.updateSelectedLabels([this.visibleLabels[this.currentHighlightItem]]);
- this.searchKey = '';
-
- // Prevent parent form submission upon hitting enter.
- e.preventDefault();
- } else if (e.keyCode === ESC_KEY_CODE) {
- this.toggleDropdownContents();
- }
-
- if (e.keyCode !== ESC_KEY_CODE) {
- // Scroll the list only after highlighting
- // styles are rendered completely.
- this.$nextTick(() => {
- this.scrollIntoViewIfNeeded();
- });
- }
- },
- handleLabelClick(label) {
- this.updateSelectedLabels([label]);
- if (!this.allowMultiselect) this.toggleDropdownContents();
- },
- },
-};
-</script>
-
-<template>
- <gl-intersection-observer @appear="handleComponentAppear" @disappear="handleComponentDisappear">
- <div class="labels-select-contents-list js-labels-list" @keydown="handleKeyDown">
- <div
- v-if="isDropdownVariantSidebar || isDropdownVariantEmbedded"
- class="dropdown-title gl-display-flex gl-align-items-center gl-pt-0 gl-pb-3!"
- data-testid="dropdown-title"
- >
- <span class="flex-grow-1">{{ labelsListTitle }}</span>
- <gl-button
- :aria-label="__('Close')"
- category="tertiary"
- size="small"
- class="dropdown-header-button gl-p-0!"
- icon="close"
- @click="toggleDropdownContents"
- />
- </div>
- <div class="dropdown-input" @click.stop="() => {}">
- <gl-search-box-by-type
- ref="searchInput"
- v-model="searchKey"
- :disabled="labelsFetchInProgress"
- data-qa-selector="dropdown_input_field"
- />
- </div>
- <div ref="labelsListContainer" class="dropdown-content" data-testid="dropdown-content">
- <gl-loading-icon
- v-if="labelsFetchInProgress"
- class="labels-fetch-loading gl-align-items-center w-100 h-100"
- size="lg"
- />
- <ul v-else class="list-unstyled gl-mb-0 gl-word-break-word">
- <label-item
- v-for="(label, index) in visibleLabels"
- :key="label.id"
- :label="label"
- :is-label-set="label.set"
- :is-label-indeterminate="label.indeterminate"
- :highlight="index === currentHighlightItem"
- @clickLabel="handleLabelClick(label)"
- />
- <li v-show="showNoMatchingResultsMessage" class="gl-p-3 gl-text-center">
- {{ __('No matching results') }}
- </li>
- </ul>
- </div>
- <div v-if="showDropdownFooter" class="dropdown-footer" data-testid="dropdown-footer">
- <ul class="list-unstyled">
- <li v-if="allowLabelCreate">
- <gl-link
- class="gl-display-flex w-100 flex-row text-break-word label-item"
- @click="handleCreateLabelClick"
- >
- {{ footerCreateLabelTitle }}
- </gl-link>
- </li>
- <li v-if="labelsManagePath">
- <gl-link
- :href="labelsManagePath"
- class="gl-display-flex flex-row text-break-word label-item"
- >
- {{ footerManageLabelTitle }}
- </gl-link>
- </li>
- </ul>
- </div>
- </div>
- </gl-intersection-observer>
-</template>
diff --git a/app/assets/javascripts/vue_shared/components/sidebar/labels_select_vue/dropdown_title.vue b/app/assets/javascripts/vue_shared/components/sidebar/labels_select_vue/dropdown_title.vue
deleted file mode 100644
index e4325492334..00000000000
--- a/app/assets/javascripts/vue_shared/components/sidebar/labels_select_vue/dropdown_title.vue
+++ /dev/null
@@ -1,46 +0,0 @@
-<script>
-import { GlButton, GlLoadingIcon } from '@gitlab/ui';
-import { mapState, mapActions } from 'vuex';
-
-// @deprecated This component should only be used when there is no GraphQL API.
-// In most cases you should use
-// `app/assets/javascripts/vue_shared/components/sidebar/labels_select_widget/dropdown_header.vue` instead.
-export default {
- components: {
- GlButton,
- GlLoadingIcon,
- },
- props: {
- labelsSelectInProgress: {
- type: Boolean,
- required: true,
- },
- },
- computed: {
- ...mapState(['allowLabelEdit', 'labelsFetchInProgress']),
- },
- methods: {
- ...mapActions(['toggleDropdownContents']),
- },
-};
-</script>
-
-<template>
- <div
- class="hide-collapsed gl-line-height-20 gl-mb-2 gl-text-gray-900 gl-font-weight-bold gl-mb-0"
- >
- {{ __('Labels') }}
- <template v-if="allowLabelEdit">
- <gl-loading-icon v-show="labelsSelectInProgress" size="sm" inline />
- <gl-button
- category="tertiary"
- size="small"
- class="float-right js-sidebar-dropdown-toggle gl-mr-n2"
- data-qa-selector="labels_edit_button"
- @click="toggleDropdownContents"
- >
- {{ __('Edit') }}
- </gl-button>
- </template>
- </div>
-</template>
diff --git a/app/assets/javascripts/vue_shared/components/sidebar/labels_select_vue/dropdown_value.vue b/app/assets/javascripts/vue_shared/components/sidebar/labels_select_vue/dropdown_value.vue
deleted file mode 100644
index e59d150dd43..00000000000
--- a/app/assets/javascripts/vue_shared/components/sidebar/labels_select_vue/dropdown_value.vue
+++ /dev/null
@@ -1,74 +0,0 @@
-<script>
-import { GlLabel } from '@gitlab/ui';
-import { sortBy } from 'lodash';
-import { mapState } from 'vuex';
-
-import { isScopedLabel } from '~/lib/utils/common_utils';
-
-// @deprecated This component should only be used when there is no GraphQL API.
-// In most cases you should use
-// `app/assets/javascripts/vue_shared/components/sidebar/labels_select_widget/dropdown_value.vue` instead.
-export default {
- components: {
- GlLabel,
- },
- props: {
- disableLabels: {
- type: Boolean,
- required: false,
- default: false,
- },
- },
- computed: {
- ...mapState([
- 'selectedLabels',
- 'allowLabelRemove',
- 'allowScopedLabels',
- 'labelsFilterBasePath',
- 'labelsFilterParam',
- ]),
- sortedSelectedLabels() {
- return sortBy(this.selectedLabels, (label) => (isScopedLabel(label) ? 0 : 1));
- },
- },
- methods: {
- labelFilterUrl(label) {
- return `${this.labelsFilterBasePath}?${this.labelsFilterParam}[]=${encodeURIComponent(
- label.title,
- )}`;
- },
- scopedLabel(label) {
- return this.allowScopedLabels && isScopedLabel(label);
- },
- },
-};
-</script>
-
-<template>
- <div
- :class="{
- 'has-labels': selectedLabels.length,
- }"
- class="hide-collapsed value issuable-show-labels js-value"
- >
- <span v-if="!selectedLabels.length" class="text-secondary">
- <slot></slot>
- </span>
- <template v-for="label in sortedSelectedLabels" v-else>
- <gl-label
- :key="label.id"
- data-qa-selector="selected_label_content"
- :data-qa-label-name="label.title"
- :title="label.title"
- :description="label.description"
- :background-color="label.color"
- :target="labelFilterUrl(label)"
- :scoped="scopedLabel(label)"
- :show-close-button="allowLabelRemove"
- :disabled="disableLabels"
- tooltip-placement="top"
- @close="$emit('onLabelRemove', label.id)"
- />
- </template>
- </div>
-</template>
diff --git a/app/assets/javascripts/vue_shared/components/sidebar/labels_select_vue/dropdown_value_collapsed.vue b/app/assets/javascripts/vue_shared/components/sidebar/labels_select_vue/dropdown_value_collapsed.vue
deleted file mode 100644
index 5966c78aa51..00000000000
--- a/app/assets/javascripts/vue_shared/components/sidebar/labels_select_vue/dropdown_value_collapsed.vue
+++ /dev/null
@@ -1,53 +0,0 @@
-<script>
-import { GlIcon, GlTooltipDirective } from '@gitlab/ui';
-import { s__, sprintf } from '~/locale';
-
-// @deprecated This component should only be used when there is no GraphQL API.
-// In most cases you should use
-// `app/assets/javascripts/vue_shared/components/sidebar/labels_select_widget` instead.
-export default {
- directives: {
- GlTooltip: GlTooltipDirective,
- },
- components: {
- GlIcon,
- },
- props: {
- labels: {
- type: Array,
- required: true,
- },
- },
- computed: {
- labelsList() {
- const labelsString = this.labels.length
- ? this.labels
- .slice(0, 5)
- .map((label) => label.title)
- .join(', ')
- : s__('LabelSelect|Labels');
-
- if (this.labels.length > 5) {
- return sprintf(s__('LabelSelect|%{labelsString}, and %{remainingLabelCount} more'), {
- labelsString,
- remainingLabelCount: this.labels.length - 5,
- });
- }
-
- return labelsString;
- },
- },
- methods: {
- handleClick() {
- this.$emit('onValueClick');
- },
- },
-};
-</script>
-
-<template>
- <div v-gl-tooltip.left.viewport="labelsList" class="sidebar-collapsed-icon" @click="handleClick">
- <gl-icon name="labels" />
- <span>{{ labels.length }}</span>
- </div>
-</template>
diff --git a/app/assets/javascripts/vue_shared/components/sidebar/labels_select_vue/label_item.vue b/app/assets/javascripts/vue_shared/components/sidebar/labels_select_vue/label_item.vue
deleted file mode 100644
index 154e3013acd..00000000000
--- a/app/assets/javascripts/vue_shared/components/sidebar/labels_select_vue/label_item.vue
+++ /dev/null
@@ -1,109 +0,0 @@
-<script>
-import { GlLink, GlIcon } from '@gitlab/ui';
-import { __ } from '~/locale';
-
-// @deprecated This component should only be used when there is no GraphQL API.
-// In most cases you should use
-// `app/assets/javascripts/vue_shared/components/sidebar/labels_select_widget/label_item.vue` instead.
-export default {
- functional: true,
- props: {
- label: {
- type: Object,
- required: true,
- },
- isLabelSet: {
- type: Boolean,
- required: true,
- },
- isLabelIndeterminate: {
- type: Boolean,
- required: false,
- default: false,
- },
- highlight: {
- type: Boolean,
- required: false,
- default: false,
- },
- },
- render(h, { props, listeners }) {
- const { label, highlight, isLabelSet, isLabelIndeterminate } = props;
-
- const labelColorBox = h('span', {
- class: 'dropdown-label-box gl-flex-shrink-0 gl-top-0 gl-mr-3',
- style: {
- backgroundColor: label.color,
- },
- attrs: {
- 'data-testid': 'label-color-box',
- },
- });
-
- const checkedIcon = h(GlIcon, {
- class: {
- 'gl-mr-3 gl-flex-shrink-0 has-tooltip': true,
- hidden: !isLabelSet,
- },
- attrs: {
- title: __('Selected for all items.'),
- 'data-testid': 'checked-icon',
- },
- props: {
- name: 'mobile-issue-close',
- },
- });
-
- const indeterminateIcon = h(GlIcon, {
- class: {
- 'gl-mr-3 gl-flex-shrink-0 has-tooltip': true,
- hidden: !isLabelIndeterminate,
- },
- attrs: {
- title: __('Selected for some items.'),
- 'data-testid': 'indeterminate-icon',
- },
- props: {
- name: 'dash',
- },
- });
-
- const noIcon = h('span', {
- class: {
- 'gl-mr-5 gl-pr-3': true,
- hidden: isLabelSet || isLabelIndeterminate,
- },
- attrs: {
- 'data-testid': 'no-icon',
- },
- });
-
- const labelTitle = h('span', label.title);
-
- const labelLink = h(
- GlLink,
- {
- class: 'gl-display-flex gl-align-items-center label-item gl-text-body',
- on: {
- click: () => {
- listeners.clickLabel(label);
- },
- },
- },
- [noIcon, checkedIcon, indeterminateIcon, labelColorBox, labelTitle],
- );
-
- return h(
- 'li',
- {
- class: {
- 'gl-display-block': true,
- 'gl-text-left': true,
- 'is-focused': highlight,
- },
- },
- [labelLink],
- );
- },
-};
-</script>
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
deleted file mode 100644
index e6c29e24f0c..00000000000
--- a/app/assets/javascripts/vue_shared/components/sidebar/labels_select_vue/labels_select_root.vue
+++ /dev/null
@@ -1,345 +0,0 @@
-<script>
-import $ from 'jquery';
-import Vue from 'vue';
-import Vuex, { mapState, mapActions, mapGetters } from 'vuex';
-import { isInViewport } from '~/lib/utils/common_utils';
-import { __ } from '~/locale';
-
-import { DropdownVariant } from './constants';
-import DropdownButton from './dropdown_button.vue';
-import DropdownContents from './dropdown_contents.vue';
-import DropdownTitle from './dropdown_title.vue';
-import DropdownValue from './dropdown_value.vue';
-import DropdownValueCollapsed from './dropdown_value_collapsed.vue';
-import labelsSelectModule from './store';
-
-Vue.use(Vuex);
-
-// @deprecated This component should only be used when there is no GraphQL API.
-// In most cases you should use
-// `app/assets/javascripts/vue_shared/components/sidebar/labels_select_widget/labels_select_root.vue` instead.
-export default {
- store: new Vuex.Store(labelsSelectModule()),
- components: {
- DropdownTitle,
- DropdownValue,
- DropdownButton,
- DropdownContents,
- DropdownValueCollapsed,
- },
- props: {
- allowLabelRemove: {
- type: Boolean,
- required: false,
- default: false,
- },
- allowLabelEdit: {
- type: Boolean,
- required: false,
- default: false,
- },
- allowLabelCreate: {
- type: Boolean,
- required: false,
- default: false,
- },
- allowMultiselect: {
- type: Boolean,
- required: false,
- default: false,
- },
- allowScopedLabels: {
- type: Boolean,
- required: false,
- default: false,
- },
- allowMultipleScopedLabels: {
- type: Boolean,
- required: false,
- default: false,
- },
- variant: {
- type: String,
- required: false,
- default: DropdownVariant.Sidebar,
- },
- selectedLabels: {
- type: Array,
- required: false,
- default: () => [],
- },
- hideCollapsedView: {
- type: Boolean,
- required: false,
- default: false,
- },
- labelsSelectInProgress: {
- type: Boolean,
- required: false,
- default: false,
- },
- labelsFetchPath: {
- type: String,
- required: false,
- default: '',
- },
- labelsManagePath: {
- type: String,
- required: false,
- default: '',
- },
- labelsFilterBasePath: {
- type: String,
- required: false,
- default: '',
- },
- labelsFilterParam: {
- type: String,
- required: false,
- default: 'label_name',
- },
- dropdownButtonText: {
- type: String,
- required: false,
- default: __('Label'),
- },
- labelsListTitle: {
- type: String,
- required: false,
- default: __('Assign labels'),
- },
- labelsCreateTitle: {
- type: String,
- required: false,
- default: __('Create group label'),
- },
- footerCreateLabelTitle: {
- type: String,
- required: false,
- default: __('Create group label'),
- },
- footerManageLabelTitle: {
- type: String,
- required: false,
- default: __('Manage group labels'),
- },
- isEditing: {
- type: Boolean,
- required: false,
- default: false,
- },
- },
- data() {
- return {
- contentIsOnViewport: true,
- };
- },
- computed: {
- ...mapState(['showDropdownButton', 'showDropdownContents']),
- ...mapGetters([
- 'isDropdownVariantSidebar',
- 'isDropdownVariantStandalone',
- 'isDropdownVariantEmbedded',
- ]),
- dropdownButtonVisible() {
- return this.isDropdownVariantSidebar ? this.showDropdownButton : true;
- },
- },
- watch: {
- selectedLabels(selectedLabels) {
- this.setInitialState({
- selectedLabels,
- });
- setTimeout(() => this.updateLabelsSetState(), 100);
- },
- showDropdownContents(showDropdownContents) {
- this.setContentIsOnViewport(showDropdownContents);
- },
- isEditing(newVal) {
- if (newVal) {
- this.toggleDropdownContents();
- }
- },
- },
- mounted() {
- this.setInitialState({
- variant: this.variant,
- allowLabelRemove: this.allowLabelRemove,
- allowLabelEdit: this.allowLabelEdit,
- allowLabelCreate: this.allowLabelCreate,
- allowMultiselect: this.allowMultiselect,
- allowScopedLabels: this.allowScopedLabels,
- allowMultipleScopedLabels: this.allowMultipleScopedLabels,
- dropdownButtonText: this.dropdownButtonText,
- selectedLabels: this.selectedLabels,
- labelsFetchPath: this.labelsFetchPath,
- labelsManagePath: this.labelsManagePath,
- labelsFilterBasePath: this.labelsFilterBasePath,
- labelsFilterParam: this.labelsFilterParam,
- labelsListTitle: this.labelsListTitle,
- labelsCreateTitle: this.labelsCreateTitle,
- footerCreateLabelTitle: this.footerCreateLabelTitle,
- footerManageLabelTitle: this.footerManageLabelTitle,
- });
-
- this.$store.subscribeAction({
- after: this.handleVuexActionDispatch,
- });
-
- document.addEventListener('mousedown', this.handleDocumentMousedown);
- document.addEventListener('click', this.handleDocumentClick);
-
- this.updateLabelsSetState();
- },
- beforeDestroy() {
- document.removeEventListener('mousedown', this.handleDocumentMousedown);
- document.removeEventListener('click', this.handleDocumentClick);
- },
- methods: {
- ...mapActions(['setInitialState', 'toggleDropdownContents', 'updateLabelsSetState']),
- /**
- * This method differentiates between
- * dispatched actions and calls necessary method.
- */
- handleVuexActionDispatch(action, state) {
- if (
- action.type === 'toggleDropdownContents' &&
- !state.showDropdownButton &&
- !state.showDropdownContents
- ) {
- const filterTouchedLabelsFn = (label) => label.touched;
- const filterSetLabelsFn = (label) => label.set;
- const labels = this.isDropdownVariantEmbedded
- ? state.labels.filter(filterSetLabelsFn)
- : state.labels.filter(filterTouchedLabelsFn);
- this.handleDropdownClose(labels, state.labels.filter(filterTouchedLabelsFn));
- }
- },
- /**
- * 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`
- // which is yet to be implemented in GitLab UI.
- const hasExceptionClass = [
- 'js-dropdown-button',
- 'js-btn-cancel-create',
- 'js-sidebar-dropdown-toggle',
- ].some(
- (className) =>
- target?.classList.contains(className) ||
- target?.parentElement?.classList.contains(className),
- );
-
- const hasExceptionParent = ['.js-btn-back', '.js-labels-list'].some(
- (className) => $(target).parents(className).length,
- );
-
- const isInDropdownButtonCollapsed = this.$refs.dropdownButtonCollapsed?.$el.contains(target);
-
- const isInDropdownContents = this.$refs.dropdownContents?.$el.contains(target);
-
- return (
- hasExceptionClass ||
- hasExceptionParent ||
- isInDropdownButtonCollapsed ||
- isInDropdownContents
- );
- },
- handleDropdownClose(labels, touchedLabels) {
- // Only emit label updates if there are any
- // labels to update on UI.
- if (labels.length) this.$emit('updateSelectedLabels', labels);
- this.$emit('onDropdownClose', touchedLabels);
- },
- handleCollapsedValueClick() {
- this.$emit('toggleCollapse');
- },
- setContentIsOnViewport(showDropdownContents) {
- if (!showDropdownContents) {
- this.contentIsOnViewport = true;
-
- return;
- }
-
- this.$nextTick(() => {
- if (this.$refs.dropdownContents) {
- this.contentIsOnViewport = isInViewport(this.$refs.dropdownContents.$el);
- }
- });
- },
- },
-};
-</script>
-
-<template>
- <div
- class="labels-select-wrapper position-relative"
- :class="{
- 'is-standalone': isDropdownVariantStandalone,
- 'is-embedded': isDropdownVariantEmbedded,
- }"
- >
- <template v-if="isDropdownVariantSidebar">
- <dropdown-value-collapsed
- v-if="!hideCollapsedView"
- ref="dropdownButtonCollapsed"
- :labels="selectedLabels"
- @onValueClick="handleCollapsedValueClick"
- />
- <dropdown-title
- :allow-label-edit="allowLabelEdit"
- :labels-select-in-progress="labelsSelectInProgress"
- />
- <dropdown-value
- :disable-labels="labelsSelectInProgress"
- @onLabelRemove="$emit('onLabelRemove', $event)"
- >
- <slot></slot>
- </dropdown-value>
- <dropdown-button v-show="dropdownButtonVisible" class="gl-mt-2" />
- <dropdown-contents
- v-if="dropdownButtonVisible && showDropdownContents"
- ref="dropdownContents"
- :render-on-top="!contentIsOnViewport"
- />
- </template>
- <template v-if="isDropdownVariantStandalone || isDropdownVariantEmbedded">
- <dropdown-button v-show="dropdownButtonVisible" />
- <dropdown-contents
- v-if="dropdownButtonVisible && showDropdownContents"
- ref="dropdownContents"
- :render-on-top="!contentIsOnViewport"
- />
- </template>
- </div>
-</template>
diff --git a/app/assets/javascripts/vue_shared/components/sidebar/labels_select_vue/store/actions.js b/app/assets/javascripts/vue_shared/components/sidebar/labels_select_vue/store/actions.js
deleted file mode 100644
index 2dab97826b9..00000000000
--- a/app/assets/javascripts/vue_shared/components/sidebar/labels_select_vue/store/actions.js
+++ /dev/null
@@ -1,69 +0,0 @@
-import { createAlert } from '~/flash';
-import axios from '~/lib/utils/axios_utils';
-import { __ } from '~/locale';
-import * as types from './mutation_types';
-
-export const setInitialState = ({ commit }, props) => commit(types.SET_INITIAL_STATE, props);
-
-export const toggleDropdownButton = ({ commit }) => commit(types.TOGGLE_DROPDOWN_BUTTON);
-export const toggleDropdownContents = ({ commit }) => commit(types.TOGGLE_DROPDOWN_CONTENTS);
-
-export const toggleDropdownContentsCreateView = ({ commit }) =>
- commit(types.TOGGLE_DROPDOWN_CONTENTS_CREATE_VIEW);
-
-export const requestLabels = ({ commit }) => commit(types.REQUEST_LABELS);
-export const receiveLabelsSuccess = ({ commit }, labels) =>
- commit(types.RECEIVE_SET_LABELS_SUCCESS, labels);
-export const receiveLabelsFailure = ({ commit }) => {
- commit(types.RECEIVE_SET_LABELS_FAILURE);
- createAlert({
- message: __('Error fetching labels.'),
- });
-};
-export const fetchLabels = ({ state, dispatch }, options) => {
- if (state.labelsFetched && (!options || !options.refetch)) {
- return Promise.resolve();
- }
-
- dispatch('requestLabels');
- return axios
- .get(state.labelsFetchPath)
- .then(({ data }) => {
- dispatch('receiveLabelsSuccess', data);
- })
- .catch(() => dispatch('receiveLabelsFailure'));
-};
-
-export const requestCreateLabel = ({ commit }) => commit(types.REQUEST_CREATE_LABEL);
-export const receiveCreateLabelSuccess = ({ commit }) => commit(types.RECEIVE_CREATE_LABEL_SUCCESS);
-export const receiveCreateLabelFailure = ({ commit }) => {
- commit(types.RECEIVE_CREATE_LABEL_FAILURE);
- createAlert({
- message: __('Error creating label.'),
- });
-};
-export const createLabel = ({ state, dispatch }, label) => {
- dispatch('requestCreateLabel');
- axios
- .post(state.labelsManagePath, {
- label,
- })
- .then(({ data }) => {
- if (data.id) {
- dispatch('fetchLabels', { refetch: true });
- dispatch('receiveCreateLabelSuccess');
- dispatch('toggleDropdownContentsCreateView');
- } else {
- // eslint-disable-next-line @gitlab/require-i18n-strings
- throw new Error('Error Creating Label');
- }
- })
- .catch(() => {
- dispatch('receiveCreateLabelFailure');
- });
-};
-
-export const updateSelectedLabels = ({ commit }, labels) =>
- commit(types.UPDATE_SELECTED_LABELS, { labels });
-
-export const updateLabelsSetState = ({ commit }) => commit(types.UPDATE_LABELS_SET_STATE);
diff --git a/app/assets/javascripts/vue_shared/components/sidebar/labels_select_vue/store/getters.js b/app/assets/javascripts/vue_shared/components/sidebar/labels_select_vue/store/getters.js
deleted file mode 100644
index ef3eedd9bb2..00000000000
--- a/app/assets/javascripts/vue_shared/components/sidebar/labels_select_vue/store/getters.js
+++ /dev/null
@@ -1,53 +0,0 @@
-import { __, s__, sprintf } from '~/locale';
-import { DropdownVariant } from '../constants';
-
-/**
- * Returns string representing current labels
- * selection on dropdown button.
- *
- * @param {object} state
- */
-export const dropdownButtonText = (state, getters) => {
- const selectedLabels =
- getters.isDropdownVariantSidebar || getters.isDropdownVariantEmbedded
- ? state.labels.filter((label) => label.set || label.indeterminate)
- : state.selectedLabels;
-
- if (!selectedLabels.length) {
- return state.dropdownButtonText || __('Label');
- } else if (selectedLabels.length > 1) {
- return sprintf(s__('LabelSelect|%{firstLabelName} +%{remainingLabelCount} more'), {
- firstLabelName: selectedLabels[0].title,
- remainingLabelCount: selectedLabels.length - 1,
- });
- }
- return selectedLabels[0].title;
-};
-
-/**
- * Returns array containing only label IDs from
- * selectedLabels array.
- * @param {object} state
- */
-export const selectedLabelsList = (state) => state.selectedLabels.map((label) => label.id);
-
-/**
- * Returns boolean representing whether dropdown variant
- * is `sidebar`
- * @param {object} state
- */
-export const isDropdownVariantSidebar = (state) => state.variant === DropdownVariant.Sidebar;
-
-/**
- * Returns boolean representing whether dropdown variant
- * is `standalone`
- * @param {object} state
- */
-export const isDropdownVariantStandalone = (state) => state.variant === DropdownVariant.Standalone;
-
-/**
- * Returns boolean representing whether dropdown variant
- * is `embedded`
- * @param {object} state
- */
-export const isDropdownVariantEmbedded = (state) => state.variant === DropdownVariant.Embedded;
diff --git a/app/assets/javascripts/vue_shared/components/sidebar/labels_select_vue/store/index.js b/app/assets/javascripts/vue_shared/components/sidebar/labels_select_vue/store/index.js
deleted file mode 100644
index 5f61cb732c8..00000000000
--- a/app/assets/javascripts/vue_shared/components/sidebar/labels_select_vue/store/index.js
+++ /dev/null
@@ -1,12 +0,0 @@
-import * as actions from './actions';
-import * as getters from './getters';
-import mutations from './mutations';
-import state from './state';
-
-export default () => ({
- namespaced: true,
- state: state(),
- actions,
- getters,
- mutations,
-});
diff --git a/app/assets/javascripts/vue_shared/components/sidebar/labels_select_vue/store/mutation_types.js b/app/assets/javascripts/vue_shared/components/sidebar/labels_select_vue/store/mutation_types.js
deleted file mode 100644
index f26e36031f4..00000000000
--- a/app/assets/javascripts/vue_shared/components/sidebar/labels_select_vue/store/mutation_types.js
+++ /dev/null
@@ -1,22 +0,0 @@
-export const SET_INITIAL_STATE = 'SET_INITIAL_STATE';
-
-export const REQUEST_LABELS = 'REQUEST_LABELS';
-export const RECEIVE_LABELS_SUCCESS = 'RECEIVE_LABELS_SUCCESS';
-export const RECEIVE_LABELS_FAILURE = 'RECEIVE_LABELS_FAILURE';
-
-export const REQUEST_SET_LABELS = 'REQUEST_SET_LABELS';
-export const RECEIVE_SET_LABELS_SUCCESS = 'RECEIVE_SET_LABELS_SUCCESS';
-export const RECEIVE_SET_LABELS_FAILURE = 'RECEIVE_SET_LABELS_FAILURE';
-
-export const REQUEST_CREATE_LABEL = 'REQUEST_CREATE_LABEL';
-export const RECEIVE_CREATE_LABEL_SUCCESS = 'RECEIVE_CREATE_LABEL_SUCCESS';
-export const RECEIVE_CREATE_LABEL_FAILURE = 'RECEIVE_CREATE_LABEL_FAILURE';
-
-export const TOGGLE_DROPDOWN_BUTTON = 'TOGGLE_DROPDOWN_VISIBILITY';
-export const TOGGLE_DROPDOWN_CONTENTS = 'TOGGLE_DROPDOWN_CONTENTS';
-
-export const UPDATE_SELECTED_LABELS = 'UPDATE_SELECTED_LABELS';
-
-export const TOGGLE_DROPDOWN_CONTENTS_CREATE_VIEW = 'TOGGLE_DROPDOWN_CONTENTS_CREATE_VIEW';
-
-export const UPDATE_LABELS_SET_STATE = 'UPDATE_LABELS_SET_STATE';
diff --git a/app/assets/javascripts/vue_shared/components/sidebar/labels_select_vue/store/mutations.js b/app/assets/javascripts/vue_shared/components/sidebar/labels_select_vue/store/mutations.js
deleted file mode 100644
index c85d9befcbb..00000000000
--- a/app/assets/javascripts/vue_shared/components/sidebar/labels_select_vue/store/mutations.js
+++ /dev/null
@@ -1,113 +0,0 @@
-import { isScopedLabel, scopedLabelKey } from '~/lib/utils/common_utils';
-import { DropdownVariant } from '../constants';
-import * as types from './mutation_types';
-
-const transformLabels = (labels, selectedLabels) =>
- labels.map((label) => {
- const selectedLabel = selectedLabels.find(({ id }) => id === label.id);
-
- return {
- ...label,
- set: Boolean(selectedLabel?.set),
- indeterminate: Boolean(selectedLabel?.indeterminate),
- };
- });
-
-export default {
- [types.SET_INITIAL_STATE](state, props) {
- // We need to ensure that selectedLabels have
- // `set` & `indeterminate` properties defined.
- if (props.selectedLabels?.length) {
- props.selectedLabels.forEach((label) => {
- /* eslint-disable no-param-reassign */
- if (label.set === undefined && label.indeterminate === undefined) {
- label.set = true;
- label.indeterminate = false;
- } else if (label.set === undefined && label.indeterminate !== undefined) {
- label.set = false;
- } else if (label.set !== undefined && label.indeterminate === undefined) {
- label.indeterminate = false;
- } else {
- label.set = false;
- label.indeterminate = false;
- }
- /* eslint-enable no-param-reassign */
- });
- }
-
- Object.assign(state, { ...props });
- },
-
- [types.TOGGLE_DROPDOWN_BUTTON](state) {
- state.showDropdownButton = !state.showDropdownButton;
- },
-
- [types.TOGGLE_DROPDOWN_CONTENTS](state) {
- if (state.variant === DropdownVariant.Sidebar) {
- state.showDropdownButton = !state.showDropdownButton;
- }
- state.showDropdownContents = !state.showDropdownContents;
- // Ensure that Create View is hidden by default
- // when dropdown contents are revealed.
- if (state.showDropdownContents) {
- state.showDropdownContentsCreateView = false;
- }
- },
-
- [types.TOGGLE_DROPDOWN_CONTENTS_CREATE_VIEW](state) {
- state.showDropdownContentsCreateView = !state.showDropdownContentsCreateView;
- },
-
- [types.REQUEST_LABELS](state) {
- state.labelsFetchInProgress = true;
- },
- [types.RECEIVE_SET_LABELS_SUCCESS](state, labels) {
- // Iterate over every label and add a `set` prop
- // to determine whether it is already a part of
- // selectedLabels array.
- state.labelsFetchInProgress = false;
- state.labelsFetched = true;
- state.labels = transformLabels(labels, state.selectedLabels);
- },
- [types.RECEIVE_SET_LABELS_FAILURE](state) {
- state.labelsFetchInProgress = false;
- },
-
- [types.REQUEST_CREATE_LABEL](state) {
- state.labelCreateInProgress = true;
- },
- [types.RECEIVE_CREATE_LABEL_SUCCESS](state) {
- state.labelCreateInProgress = false;
- },
- [types.RECEIVE_CREATE_LABEL_FAILURE](state) {
- state.labelCreateInProgress = false;
- },
-
- [types.UPDATE_SELECTED_LABELS](state, { labels }) {
- // Find the label to update from all the labels
- // and change `set` prop value to represent their current state.
- const labelId = labels.pop()?.id;
- const candidateLabel = state.labels.find((label) => labelId === label.id);
- if (candidateLabel) {
- candidateLabel.touched = true;
- candidateLabel.set = candidateLabel.indeterminate ? true : !candidateLabel.set;
- candidateLabel.indeterminate = false;
- }
-
- if (isScopedLabel(candidateLabel) && !state.allowMultipleScopedLabels) {
- const currentActiveScopedLabel = state.labels.find(
- ({ set, title }) =>
- set &&
- title !== candidateLabel.title &&
- scopedLabelKey({ title }) === scopedLabelKey(candidateLabel),
- );
- if (currentActiveScopedLabel) {
- currentActiveScopedLabel.set = false;
- }
- }
- },
-
- [types.UPDATE_LABELS_SET_STATE](state) {
- state.labels = transformLabels(state.labels, state.selectedLabels);
- },
-};
diff --git a/app/assets/javascripts/vue_shared/components/sidebar/labels_select_vue/store/state.js b/app/assets/javascripts/vue_shared/components/sidebar/labels_select_vue/store/state.js
deleted file mode 100644
index 0185d5f88e1..00000000000
--- a/app/assets/javascripts/vue_shared/components/sidebar/labels_select_vue/store/state.js
+++ /dev/null
@@ -1,30 +0,0 @@
-export default () => ({
- // Initial Data
- labels: [],
- labelsFetched: false,
- selectedLabels: [],
- labelsListTitle: '',
- labelsCreateTitle: '',
- footerCreateLabelTitle: '',
- footerManageLabelTitle: '',
- dropdownButtonText: '',
-
- // Paths
- namespace: '',
- labelsFetchPath: '',
- labelsFilterBasePath: '',
-
- // UI Flags
- variant: '',
- allowLabelRemove: false,
- allowLabelCreate: false,
- allowLabelEdit: false,
- allowScopedLabels: false,
- allowMultiselect: false,
- showDropdownButton: false,
- showDropdownContents: false,
- showDropdownContentsCreateView: false,
- labelsFetchInProgress: false,
- labelCreateInProgress: false,
- selectedLabelsUpdated: false,
-});
diff --git a/app/assets/javascripts/vue_shared/components/sidebar/labels_select_widget/constants.js b/app/assets/javascripts/vue_shared/components/sidebar/labels_select_widget/constants.js
deleted file mode 100644
index cd671b4d8f5..00000000000
--- a/app/assets/javascripts/vue_shared/components/sidebar/labels_select_widget/constants.js
+++ /dev/null
@@ -1,13 +0,0 @@
-export const SCOPED_LABEL_DELIMITER = '::';
-export const DEBOUNCE_DROPDOWN_DELAY = 200;
-
-export const DropdownVariant = {
- Sidebar: 'sidebar',
- Standalone: 'standalone',
- Embedded: 'embedded',
-};
-
-export const LabelType = {
- group: 'group',
- project: 'project',
-};
diff --git a/app/assets/javascripts/vue_shared/components/sidebar/labels_select_widget/dropdown_contents.vue b/app/assets/javascripts/vue_shared/components/sidebar/labels_select_widget/dropdown_contents.vue
deleted file mode 100644
index 27186281c42..00000000000
--- a/app/assets/javascripts/vue_shared/components/sidebar/labels_select_widget/dropdown_contents.vue
+++ /dev/null
@@ -1,241 +0,0 @@
-<script>
-import { GlButton, GlDropdown, GlDropdownItem, GlLink } from '@gitlab/ui';
-import { debounce } from 'lodash';
-import { DEFAULT_DEBOUNCE_AND_THROTTLE_MS } from '~/lib/utils/constants';
-import { __, s__, sprintf } from '~/locale';
-import DropdownContentsCreateView from './dropdown_contents_create_view.vue';
-import DropdownContentsLabelsView from './dropdown_contents_labels_view.vue';
-import DropdownFooter from './dropdown_footer.vue';
-import DropdownHeader from './dropdown_header.vue';
-import { isDropdownVariantStandalone, isDropdownVariantSidebar } from './utils';
-
-export default {
- components: {
- DropdownContentsLabelsView,
- DropdownContentsCreateView,
- DropdownHeader,
- DropdownFooter,
- GlButton,
- GlDropdown,
- GlDropdownItem,
- GlLink,
- },
- props: {
- labelsCreateTitle: {
- type: String,
- required: true,
- },
- selectedLabels: {
- type: Array,
- required: true,
- },
- allowMultiselect: {
- type: Boolean,
- required: true,
- },
- labelsListTitle: {
- type: String,
- required: true,
- },
- dropdownButtonText: {
- type: String,
- required: true,
- },
- footerCreateLabelTitle: {
- type: String,
- required: true,
- },
- footerManageLabelTitle: {
- type: String,
- required: true,
- },
- variant: {
- type: String,
- required: true,
- },
- isVisible: {
- type: Boolean,
- required: false,
- default: false,
- },
- fullPath: {
- type: String,
- required: true,
- },
- workspaceType: {
- type: String,
- required: true,
- },
- attrWorkspacePath: {
- type: String,
- required: true,
- },
- labelCreateType: {
- type: String,
- required: true,
- },
- },
- data() {
- return {
- showDropdownContentsCreateView: false,
- localSelectedLabels: [...this.selectedLabels],
- isDirty: false,
- searchKey: '',
- };
- },
- computed: {
- dropdownContentsView() {
- if (this.showDropdownContentsCreateView) {
- return 'dropdown-contents-create-view';
- }
- return 'dropdown-contents-labels-view';
- },
- dropdownTitle() {
- return this.showDropdownContentsCreateView ? this.labelsCreateTitle : this.labelsListTitle;
- },
- buttonText() {
- if (!this.localSelectedLabels.length) {
- return this.dropdownButtonText || __('Label');
- } else if (this.localSelectedLabels.length > 1) {
- return sprintf(s__('LabelSelect|%{firstLabelName} +%{remainingLabelCount} more'), {
- firstLabelName: this.localSelectedLabels[0].title,
- remainingLabelCount: this.localSelectedLabels.length - 1,
- });
- }
- return this.localSelectedLabels[0].title;
- },
- showDropdownFooter() {
- return !this.showDropdownContentsCreateView && !this.isStandalone;
- },
- isStandalone() {
- return isDropdownVariantStandalone(this.variant);
- },
- },
- watch: {
- localSelectedLabels: {
- handler() {
- this.isDirty = true;
- },
- deep: true,
- },
- isVisible(newVal) {
- if (newVal) {
- this.$refs.dropdown.show();
- this.isDirty = false;
- this.localSelectedLabels = this.selectedLabels;
- } else {
- this.$refs.dropdown.hide();
- this.setLabels();
- }
- },
- selectedLabels(newVal) {
- if (!this.isDirty) {
- this.localSelectedLabels = newVal;
- }
- },
- },
- created() {
- this.debouncedSearchKeyUpdate = debounce(this.setSearchKey, DEFAULT_DEBOUNCE_AND_THROTTLE_MS);
- },
- beforeDestroy() {
- this.debouncedSearchKeyUpdate.cancel();
- },
- methods: {
- toggleDropdownContentsCreateView() {
- this.showDropdownContentsCreateView = !this.showDropdownContentsCreateView;
- },
- toggleDropdownContent() {
- this.toggleDropdownContentsCreateView();
- // Required to recalculate dropdown position as its size changes
- if (this.$refs.dropdown?.$refs.dropdown) {
- this.$refs.dropdown.$refs.dropdown.$_popper.scheduleUpdate();
- }
- },
- setLabels() {
- if (!this.isDirty) {
- return;
- }
- this.$emit('setLabels', this.localSelectedLabels);
- },
- handleDropdownHide() {
- this.$emit('closeDropdown');
- if (!isDropdownVariantSidebar(this.variant)) {
- this.setLabels();
- }
- },
- setSearchKey(value) {
- this.searchKey = value;
- },
- setFocus() {
- this.$refs.header.focusInput();
- },
- hideDropdown() {
- this.$refs.dropdown.hide();
- },
- showDropdown() {
- this.$refs.dropdown.show();
- },
- clearSearch() {
- if (!this.allowMultiselect || this.isStandalone) {
- return;
- }
- this.searchKey = '';
- this.setFocus();
- },
- selectFirstItem() {
- this.$refs.dropdownContentsView.selectFirstItem();
- },
- },
-};
-</script>
-
-<template>
- <gl-dropdown
- ref="dropdown"
- :text="buttonText"
- class="gl-w-full"
- block
- data-testid="labels-select-dropdown-contents"
- data-qa-selector="labels_dropdown_content"
- @hide="handleDropdownHide"
- @shown="setFocus"
- >
- <template #header>
- <dropdown-header
- ref="header"
- :search-key="searchKey"
- :labels-create-title="labelsCreateTitle"
- :labels-list-title="labelsListTitle"
- :show-dropdown-contents-create-view="showDropdownContentsCreateView"
- :is-standalone="isStandalone"
- @toggleDropdownContentsCreateView="toggleDropdownContent"
- @closeDropdown="hideDropdown"
- @input="debouncedSearchKeyUpdate"
- @searchEnter="selectFirstItem"
- />
- </template>
- <template #default>
- <component
- :is="dropdownContentsView"
- ref="dropdownContentsView"
- v-model="localSelectedLabels"
- :search-key="searchKey"
- :allow-multiselect="allowMultiselect"
- :full-path="fullPath"
- :workspace-type="workspaceType"
- :attr-workspace-path="attrWorkspacePath"
- :label-create-type="labelCreateType"
- @hideCreateView="toggleDropdownContent"
- @input="clearSearch"
- />
- </template>
- <template #footer>
- <dropdown-footer
- v-if="showDropdownFooter"
- :footer-create-label-title="footerCreateLabelTitle"
- :footer-manage-label-title="footerManageLabelTitle"
- @toggleDropdownContentsCreateView="toggleDropdownContent"
- />
- </template>
- </gl-dropdown>
-</template>
diff --git a/app/assets/javascripts/vue_shared/components/sidebar/labels_select_widget/dropdown_contents_create_view.vue b/app/assets/javascripts/vue_shared/components/sidebar/labels_select_widget/dropdown_contents_create_view.vue
deleted file mode 100644
index ce93ad216ec..00000000000
--- a/app/assets/javascripts/vue_shared/components/sidebar/labels_select_widget/dropdown_contents_create_view.vue
+++ /dev/null
@@ -1,200 +0,0 @@
-<script>
-import {
- GlAlert,
- GlTooltipDirective,
- GlButton,
- GlFormInput,
- GlLink,
- GlLoadingIcon,
-} from '@gitlab/ui';
-import produce from 'immer';
-import { createAlert } from '~/flash';
-import { __ } from '~/locale';
-import { workspaceLabelsQueries } from '~/sidebar/constants';
-import createLabelMutation from './graphql/create_label.mutation.graphql';
-import { LabelType } from './constants';
-
-const errorMessage = __('Error creating label.');
-
-export default {
- components: {
- GlAlert,
- GlButton,
- GlFormInput,
- GlLink,
- GlLoadingIcon,
- },
- directives: {
- GlTooltip: GlTooltipDirective,
- },
- props: {
- fullPath: {
- type: String,
- required: true,
- },
- attrWorkspacePath: {
- type: String,
- required: true,
- },
- labelCreateType: {
- type: String,
- required: true,
- },
- workspaceType: {
- type: String,
- required: true,
- },
- },
- data() {
- return {
- labelTitle: '',
- selectedColor: '',
- labelCreateInProgress: false,
- error: undefined,
- };
- },
- computed: {
- disableCreate() {
- return !this.labelTitle.length || !this.selectedColor.length || this.labelCreateInProgress;
- },
- suggestedColors() {
- const colorsMap = gon.suggested_label_colors;
- return Object.keys(colorsMap).map((color) => ({ [color]: colorsMap[color] }));
- },
- mutationVariables() {
- const attributePath = this.labelCreateType === LabelType.group ? 'groupPath' : 'projectPath';
-
- return {
- title: this.labelTitle,
- color: this.selectedColor,
- [attributePath]: this.attrWorkspacePath,
- };
- },
- },
- methods: {
- getColorCode(color) {
- return Object.keys(color).pop();
- },
- getColorName(color) {
- return Object.values(color).pop();
- },
- handleColorClick(color) {
- this.selectedColor = this.getColorCode(color);
- },
- updateLabelsInCache(store, label) {
- const { query } = workspaceLabelsQueries[this.workspaceType];
-
- const sourceData = store.readQuery({
- query,
- variables: { fullPath: this.fullPath, searchTerm: '' },
- });
-
- const collator = new Intl.Collator('en');
- const data = produce(sourceData, (draftData) => {
- const { nodes } = draftData.workspace.labels;
- nodes.push(label);
- nodes.sort((a, b) => collator.compare(a.title, b.title));
- });
-
- store.writeQuery({
- query,
- variables: { fullPath: this.fullPath, searchTerm: '' },
- data,
- });
- },
- async createLabel() {
- this.labelCreateInProgress = true;
- try {
- const {
- data: { labelCreate },
- } = await this.$apollo.mutate({
- mutation: createLabelMutation,
- variables: this.mutationVariables,
- update: (
- store,
- {
- data: {
- labelCreate: { label },
- },
- },
- ) => {
- if (label) {
- this.updateLabelsInCache(store, label);
- }
- },
- });
- if (labelCreate.errors.length) {
- [this.error] = labelCreate.errors;
- } else {
- this.$emit('hideCreateView');
- }
- } catch {
- createAlert({ message: errorMessage });
- }
- this.labelCreateInProgress = false;
- },
- },
-};
-</script>
-
-<template>
- <div class="labels-select-contents-create js-labels-create">
- <div class="dropdown-input">
- <gl-alert v-if="error" variant="danger" :dismissible="false" class="gl-mt-3">
- {{ error }}
- </gl-alert>
- <gl-form-input
- v-model.trim="labelTitle"
- class="gl-mt-3"
- :placeholder="__('Name new label')"
- :autofocus="true"
- data-testid="label-title-input"
- />
- </div>
- <div class="dropdown-content gl-px-3">
- <div class="suggest-colors suggest-colors-dropdown gl-mt-0! gl-mb-3! gl-mb-0">
- <gl-link
- v-for="(color, index) in suggestedColors"
- :key="index"
- v-gl-tooltip:tooltipcontainer
- :style="{ backgroundColor: getColorCode(color) }"
- :title="getColorName(color)"
- @click.prevent="handleColorClick(color)"
- />
- </div>
- <div class="color-input-container gl-display-flex">
- <span
- class="dropdown-label-color-preview gl-relative gl-display-inline-block"
- data-testid="selected-color"
- :style="{ backgroundColor: selectedColor }"
- ></span>
- <gl-form-input
- v-model.trim="selectedColor"
- class="gl-rounded-top-left-none gl-rounded-bottom-left-none gl-mb-2"
- :placeholder="__('Use custom color #FF0000')"
- data-testid="selected-color-text"
- />
- </div>
- </div>
- <div class="dropdown-actions gl-display-flex gl-justify-content-space-between gl-pt-3 gl-px-3">
- <gl-button
- :disabled="disableCreate"
- category="primary"
- variant="confirm"
- class="gl-display-flex gl-align-items-center"
- data-testid="create-button"
- @click="createLabel"
- >
- <gl-loading-icon v-if="labelCreateInProgress" size="sm" :inline="true" class="mr-1" />
- {{ __('Create') }}
- </gl-button>
- <gl-button
- class="js-btn-cancel-create"
- data-testid="cancel-button"
- @click.stop="$emit('hideCreateView')"
- >
- {{ __('Cancel') }}
- </gl-button>
- </div>
- </div>
-</template>
diff --git a/app/assets/javascripts/vue_shared/components/sidebar/labels_select_widget/dropdown_contents_labels_view.vue b/app/assets/javascripts/vue_shared/components/sidebar/labels_select_widget/dropdown_contents_labels_view.vue
deleted file mode 100644
index 1d854505d11..00000000000
--- a/app/assets/javascripts/vue_shared/components/sidebar/labels_select_widget/dropdown_contents_labels_view.vue
+++ /dev/null
@@ -1,177 +0,0 @@
-<script>
-import { GlDropdownForm, GlDropdownItem, GlLoadingIcon, GlIntersectionObserver } from '@gitlab/ui';
-import fuzzaldrinPlus from 'fuzzaldrin-plus';
-import { createAlert } from '~/flash';
-import { getIdFromGraphQLId } from '~/graphql_shared/utils';
-import { __ } from '~/locale';
-import { workspaceLabelsQueries } from '~/sidebar/constants';
-import LabelItem from './label_item.vue';
-
-export default {
- components: {
- GlDropdownForm,
- GlDropdownItem,
- GlLoadingIcon,
- GlIntersectionObserver,
- LabelItem,
- },
- model: {
- prop: 'localSelectedLabels',
- },
- props: {
- allowMultiselect: {
- type: Boolean,
- required: true,
- },
- localSelectedLabels: {
- type: Array,
- required: true,
- },
- fullPath: {
- type: String,
- required: true,
- },
- searchKey: {
- type: String,
- required: true,
- },
- workspaceType: {
- type: String,
- required: true,
- },
- },
- data() {
- return {
- labels: [],
- isVisible: false,
- };
- },
- apollo: {
- labels: {
- query() {
- return workspaceLabelsQueries[this.workspaceType].query;
- },
- variables() {
- return {
- fullPath: this.fullPath,
- searchTerm: this.searchKey,
- };
- },
- skip() {
- return this.searchKey.length === 1 || !this.isVisible;
- },
- update: (data) => data.workspace?.labels?.nodes || [],
- error() {
- createAlert({ message: __('Error fetching labels.') });
- },
- },
- },
- computed: {
- labelsFetchInProgress() {
- return this.$apollo.queries.labels.loading;
- },
- localSelectedLabelsIds() {
- return this.localSelectedLabels.map((label) => getIdFromGraphQLId(label.id));
- },
- visibleLabels() {
- if (this.searchKey) {
- return fuzzaldrinPlus.filter(this.labels, this.searchKey, {
- key: ['title'],
- });
- }
- return this.labels;
- },
- showNoMatchingResultsMessage() {
- return Boolean(this.searchKey) && this.visibleLabels.length === 0;
- },
- shouldHighlightFirstItem() {
- return this.searchKey !== '' && this.visibleLabels.length > 0;
- },
- },
- methods: {
- isLabelSelected(label) {
- return this.localSelectedLabelsIds.includes(getIdFromGraphQLId(label.id));
- },
- /**
- * This method scrolls item from dropdown into
- * the view if it is off the viewable area of the
- * container.
- */
- scrollIntoViewIfNeeded() {
- const highlightedLabel = this.$refs.labelsListContainer.querySelector('.is-focused');
-
- if (highlightedLabel) {
- const container = this.$refs.labelsListContainer.getBoundingClientRect();
- const label = highlightedLabel.getBoundingClientRect();
-
- if (label.bottom > container.bottom) {
- this.$refs.labelsListContainer.scrollTop += label.bottom - container.bottom;
- } else if (label.top < container.top) {
- this.$refs.labelsListContainer.scrollTop -= container.top - label.top;
- }
- }
- },
- updateSelectedLabels(label) {
- let labels;
- if (this.isLabelSelected(label)) {
- labels = this.localSelectedLabels.filter(
- ({ id }) => id !== getIdFromGraphQLId(label.id) && id !== label.id,
- );
- } else {
- labels = [...this.localSelectedLabels, label];
- }
- this.$emit('input', labels);
- },
- handleLabelClick(label) {
- this.updateSelectedLabels(label);
- if (!this.allowMultiselect) {
- this.$emit('closeDropdown', this.localSelectedLabels);
- }
- },
- onDropdownAppear() {
- this.isVisible = true;
- },
- selectFirstItem() {
- if (this.shouldHighlightFirstItem) {
- this.handleLabelClick(this.visibleLabels[0]);
- }
- },
- },
-};
-</script>
-
-<template>
- <gl-intersection-observer @appear="onDropdownAppear">
- <gl-dropdown-form class="labels-select-contents-list js-labels-list">
- <div ref="labelsListContainer" data-testid="dropdown-content">
- <gl-loading-icon
- v-if="labelsFetchInProgress"
- class="labels-fetch-loading gl-align-items-center gl-w-full gl-h-full gl-mb-3"
- size="lg"
- />
- <template v-else>
- <gl-dropdown-item
- v-for="(label, index) in visibleLabels"
- :key="label.id"
- :is-checked="isLabelSelected(label)"
- is-check-centered
- is-check-item
- :active="shouldHighlightFirstItem && index === 0"
- active-class="is-focused"
- data-testid="labels-list"
- @click.native.capture.stop="handleLabelClick(label)"
- >
- <label-item :label="label" />
- </gl-dropdown-item>
- <gl-dropdown-item
- v-show="showNoMatchingResultsMessage"
- class="gl-p-3 gl-text-center"
- data-testid="no-results"
- >
- {{ __('No matching results') }}
- </gl-dropdown-item>
- </template>
- </div>
- </gl-dropdown-form>
- </gl-intersection-observer>
-</template>
diff --git a/app/assets/javascripts/vue_shared/components/sidebar/labels_select_widget/dropdown_footer.vue b/app/assets/javascripts/vue_shared/components/sidebar/labels_select_widget/dropdown_footer.vue
deleted file mode 100644
index e67e704ffb8..00000000000
--- a/app/assets/javascripts/vue_shared/components/sidebar/labels_select_widget/dropdown_footer.vue
+++ /dev/null
@@ -1,35 +0,0 @@
-<script>
-import { GlDropdownItem } from '@gitlab/ui';
-
-export default {
- components: {
- GlDropdownItem,
- },
- inject: ['allowLabelCreate', 'labelsManagePath'],
- props: {
- footerCreateLabelTitle: {
- type: String,
- required: true,
- },
- footerManageLabelTitle: {
- type: String,
- required: true,
- },
- },
-};
-</script>
-
-<template>
- <div data-testid="dropdown-footer">
- <gl-dropdown-item
- v-if="allowLabelCreate"
- data-testid="create-label-button"
- @click.capture.native.stop="$emit('toggleDropdownContentsCreateView')"
- >
- {{ footerCreateLabelTitle }}
- </gl-dropdown-item>
- <gl-dropdown-item :href="labelsManagePath" @click.capture.native.stop>
- {{ footerManageLabelTitle }}
- </gl-dropdown-item>
- </div>
-</template>
diff --git a/app/assets/javascripts/vue_shared/components/sidebar/labels_select_widget/dropdown_header.vue b/app/assets/javascripts/vue_shared/components/sidebar/labels_select_widget/dropdown_header.vue
deleted file mode 100644
index 154a8e866d0..00000000000
--- a/app/assets/javascripts/vue_shared/components/sidebar/labels_select_widget/dropdown_header.vue
+++ /dev/null
@@ -1,91 +0,0 @@
-<script>
-import { GlButton, GlSearchBoxByType } from '@gitlab/ui';
-
-export default {
- components: {
- GlButton,
- GlSearchBoxByType,
- },
- props: {
- labelsCreateTitle: {
- type: String,
- required: true,
- },
- labelsListTitle: {
- type: String,
- required: true,
- },
- showDropdownContentsCreateView: {
- type: Boolean,
- required: true,
- },
- labelsFetchInProgress: {
- type: Boolean,
- required: false,
- default: false,
- },
- searchKey: {
- type: String,
- required: true,
- },
- isStandalone: {
- type: Boolean,
- required: false,
- default: false,
- },
- },
- computed: {
- dropdownTitle() {
- return this.showDropdownContentsCreateView ? this.labelsCreateTitle : this.labelsListTitle;
- },
- },
- methods: {
- focusInput() {
- this.$refs.searchInput?.focusInput();
- },
- },
-};
-</script>
-
-<template>
- <div data-testid="dropdown-header">
- <div
- v-if="!isStandalone"
- class="dropdown-title gl-display-flex gl-align-items-center gl-pt-0 gl-pb-3! gl-mb-0"
- data-testid="dropdown-header-title"
- >
- <gl-button
- v-if="showDropdownContentsCreateView"
- :aria-label="__('Go back')"
- variant="link"
- size="small"
- class="js-btn-back dropdown-header-button gl-p-0"
- icon="arrow-left"
- data-testid="go-back-button"
- @click.stop="$emit('toggleDropdownContentsCreateView')"
- />
- <span class="gl-flex-grow-1">{{ dropdownTitle }}</span>
- <gl-button
- :aria-label="__('Close')"
- variant="link"
- size="small"
- class="dropdown-header-button gl-p-0!"
- icon="close"
- data-testid="close-button"
- data-qa-selector="close_labels_dropdown_button"
- @click="$emit('closeDropdown')"
- />
- </div>
- <gl-search-box-by-type
- v-if="!showDropdownContentsCreateView"
- ref="searchInput"
- :value="searchKey"
- :placeholder="__('Search labels')"
- :disabled="labelsFetchInProgress"
- data-qa-selector="dropdown_input_field"
- data-testid="dropdown-input-field"
- @input="$emit('input', $event)"
- @keydown.enter="$emit('searchEnter', $event)"
- />
- </div>
-</template>
diff --git a/app/assets/javascripts/vue_shared/components/sidebar/labels_select_widget/dropdown_value.vue b/app/assets/javascripts/vue_shared/components/sidebar/labels_select_widget/dropdown_value.vue
deleted file mode 100644
index 57e3ee4aaa5..00000000000
--- a/app/assets/javascripts/vue_shared/components/sidebar/labels_select_widget/dropdown_value.vue
+++ /dev/null
@@ -1,125 +0,0 @@
-<script>
-import { GlIcon, GlLabel, GlTooltipDirective } from '@gitlab/ui';
-import { sortBy } from 'lodash';
-import { isScopedLabel } from '~/lib/utils/common_utils';
-import { s__, sprintf } from '~/locale';
-
-export default {
- directives: {
- GlTooltip: GlTooltipDirective,
- },
- components: {
- GlIcon,
- GlLabel,
- },
- inject: ['allowScopedLabels'],
- props: {
- disableLabels: {
- type: Boolean,
- required: false,
- default: false,
- },
- selectedLabels: {
- type: Array,
- required: true,
- },
- allowLabelRemove: {
- type: Boolean,
- required: true,
- },
- labelsFilterBasePath: {
- type: String,
- required: true,
- },
- labelsFilterParam: {
- type: String,
- required: true,
- },
- },
- computed: {
- sortedSelectedLabels() {
- return sortBy(this.selectedLabels, (label) => (isScopedLabel(label) ? 0 : 1));
- },
- labelsList() {
- const labelsString = this.selectedLabels.length
- ? this.selectedLabels
- .slice(0, 5)
- .map((label) => label.title)
- .join(', ')
- : s__('LabelSelect|Labels');
-
- if (this.selectedLabels.length > 5) {
- return sprintf(s__('LabelSelect|%{labelsString}, and %{remainingLabelCount} more'), {
- labelsString,
- remainingLabelCount: this.selectedLabels.length - 5,
- });
- }
-
- return labelsString;
- },
- },
- methods: {
- labelFilterUrl(label) {
- return `${this.labelsFilterBasePath}?${this.labelsFilterParam}[]=${encodeURIComponent(
- label.title,
- )}`;
- },
- scopedLabel(label) {
- return this.allowScopedLabels && isScopedLabel(label);
- },
- removeLabel(labelId) {
- this.$emit('onLabelRemove', labelId);
- },
- handleCollapsedClick() {
- this.$emit('onCollapsedValueClick');
- },
- },
-};
-</script>
-
-<template>
- <div
- :class="{
- 'has-labels': selectedLabels.length,
- }"
- class="value issuable-show-labels js-value"
- data-testid="value-wrapper"
- >
- <div
- v-gl-tooltip.left.viewport
- :title="labelsList"
- class="sidebar-collapsed-icon"
- @click="handleCollapsedClick"
- >
- <gl-icon name="labels" />
- <span class="collapse-truncated-title gl-pt-2 gl-px-3 gl-font-sm">{{
- selectedLabels.length
- }}</span>
- </div>
- <span
- v-if="!selectedLabels.length"
- class="text-secondary hide-collapsed"
- data-testid="empty-placeholder"
- >
- <slot></slot>
- </span>
- <template v-else>
- <gl-label
- v-for="label in sortedSelectedLabels"
- :key="label.id"
- class="hide-collapsed"
- data-qa-selector="selected_label_content"
- :data-qa-label-name="label.title"
- :title="label.title"
- :description="label.description"
- :background-color="label.color"
- :target="labelFilterUrl(label)"
- :scoped="scopedLabel(label)"
- :show-close-button="allowLabelRemove"
- :disabled="disableLabels"
- tooltip-placement="top"
- @close="removeLabel(label.id)"
- />
- </template>
- </div>
-</template>
diff --git a/app/assets/javascripts/vue_shared/components/sidebar/labels_select_widget/graphql/create_label.mutation.graphql b/app/assets/javascripts/vue_shared/components/sidebar/labels_select_widget/graphql/create_label.mutation.graphql
deleted file mode 100644
index a9c791091fc..00000000000
--- a/app/assets/javascripts/vue_shared/components/sidebar/labels_select_widget/graphql/create_label.mutation.graphql
+++ /dev/null
@@ -1,12 +0,0 @@
-#import "~/graphql_shared/fragments/label.fragment.graphql"
-
-mutation createLabel($title: String!, $color: String, $projectPath: ID, $groupPath: ID) {
- labelCreate(
- input: { title: $title, color: $color, projectPath: $projectPath, groupPath: $groupPath }
- ) {
- label {
- ...Label
- }
- errors
- }
-}
diff --git a/app/assets/javascripts/vue_shared/components/sidebar/labels_select_widget/graphql/epic_labels.query.graphql b/app/assets/javascripts/vue_shared/components/sidebar/labels_select_widget/graphql/epic_labels.query.graphql
deleted file mode 100644
index c442c17eb88..00000000000
--- a/app/assets/javascripts/vue_shared/components/sidebar/labels_select_widget/graphql/epic_labels.query.graphql
+++ /dev/null
@@ -1,15 +0,0 @@
-#import "~/graphql_shared/fragments/label.fragment.graphql"
-
-query epicLabels($fullPath: ID!, $iid: ID) {
- workspace: group(fullPath: $fullPath) {
- id
- issuable: epic(iid: $iid) {
- id
- labels {
- nodes {
- ...Label
- }
- }
- }
- }
-}
diff --git a/app/assets/javascripts/vue_shared/components/sidebar/labels_select_widget/graphql/epic_update_labels.mutation.graphql b/app/assets/javascripts/vue_shared/components/sidebar/labels_select_widget/graphql/epic_update_labels.mutation.graphql
deleted file mode 100644
index cb054e2968f..00000000000
--- a/app/assets/javascripts/vue_shared/components/sidebar/labels_select_widget/graphql/epic_update_labels.mutation.graphql
+++ /dev/null
@@ -1,15 +0,0 @@
-#import "~/graphql_shared/fragments/label.fragment.graphql"
-
-mutation updateEpicLabels($input: UpdateEpicInput!) {
- updateIssuableLabels: updateEpic(input: $input) {
- issuable: epic {
- id
- labels {
- nodes {
- ...Label
- }
- }
- }
- errors
- }
-}
diff --git a/app/assets/javascripts/vue_shared/components/sidebar/labels_select_widget/graphql/group_labels.query.graphql b/app/assets/javascripts/vue_shared/components/sidebar/labels_select_widget/graphql/group_labels.query.graphql
deleted file mode 100644
index ce1a69f84c0..00000000000
--- a/app/assets/javascripts/vue_shared/components/sidebar/labels_select_widget/graphql/group_labels.query.graphql
+++ /dev/null
@@ -1,12 +0,0 @@
-#import "~/graphql_shared/fragments/label.fragment.graphql"
-
-query groupLabels($fullPath: ID!, $searchTerm: String) {
- workspace: group(fullPath: $fullPath) {
- id
- labels(searchTerm: $searchTerm, onlyGroupLabels: true, includeAncestorGroups: true) {
- nodes {
- ...Label
- }
- }
- }
-}
diff --git a/app/assets/javascripts/vue_shared/components/sidebar/labels_select_widget/graphql/issue_labels.query.graphql b/app/assets/javascripts/vue_shared/components/sidebar/labels_select_widget/graphql/issue_labels.query.graphql
deleted file mode 100644
index 2904857270e..00000000000
--- a/app/assets/javascripts/vue_shared/components/sidebar/labels_select_widget/graphql/issue_labels.query.graphql
+++ /dev/null
@@ -1,15 +0,0 @@
-#import "~/graphql_shared/fragments/label.fragment.graphql"
-
-query issueLabels($fullPath: ID!, $iid: String) {
- workspace: project(fullPath: $fullPath) {
- id
- issuable: issue(iid: $iid) {
- id
- labels {
- nodes {
- ...Label
- }
- }
- }
- }
-}
diff --git a/app/assets/javascripts/vue_shared/components/sidebar/labels_select_widget/graphql/merge_request_labels.query.graphql b/app/assets/javascripts/vue_shared/components/sidebar/labels_select_widget/graphql/merge_request_labels.query.graphql
deleted file mode 100644
index e0cdfd91658..00000000000
--- a/app/assets/javascripts/vue_shared/components/sidebar/labels_select_widget/graphql/merge_request_labels.query.graphql
+++ /dev/null
@@ -1,15 +0,0 @@
-#import "~/graphql_shared/fragments/label.fragment.graphql"
-
-query mergeRequestLabels($fullPath: ID!, $iid: String!) {
- workspace: project(fullPath: $fullPath) {
- id
- issuable: mergeRequest(iid: $iid) {
- id
- labels {
- nodes {
- ...Label
- }
- }
- }
- }
-}
diff --git a/app/assets/javascripts/vue_shared/components/sidebar/labels_select_widget/graphql/project_labels.query.graphql b/app/assets/javascripts/vue_shared/components/sidebar/labels_select_widget/graphql/project_labels.query.graphql
deleted file mode 100644
index a7c24620aad..00000000000
--- a/app/assets/javascripts/vue_shared/components/sidebar/labels_select_widget/graphql/project_labels.query.graphql
+++ /dev/null
@@ -1,12 +0,0 @@
-#import "~/graphql_shared/fragments/label.fragment.graphql"
-
-query projectLabels($fullPath: ID!, $searchTerm: String) {
- workspace: project(fullPath: $fullPath) {
- id
- labels(searchTerm: $searchTerm, includeAncestorGroups: true) {
- nodes {
- ...Label
- }
- }
- }
-}
diff --git a/app/assets/javascripts/vue_shared/components/sidebar/labels_select_widget/label_item.vue b/app/assets/javascripts/vue_shared/components/sidebar/labels_select_widget/label_item.vue
deleted file mode 100644
index 314ffbaf84c..00000000000
--- a/app/assets/javascripts/vue_shared/components/sidebar/labels_select_widget/label_item.vue
+++ /dev/null
@@ -1,21 +0,0 @@
-<script>
-export default {
- props: {
- label: {
- type: Object,
- required: true,
- },
- },
-};
-</script>
-
-<template>
- <div class="gl-display-flex gl-align-items-center gl-word-break-word">
- <span
- class="dropdown-label-box gl-flex-shrink-0 gl-top-0 gl-mr-3"
- :style="{ 'background-color': label.color }"
- data-testid="label-color-box"
- ></span>
- <span>{{ label.title }}</span>
- </div>
-</template>
diff --git a/app/assets/javascripts/vue_shared/components/sidebar/labels_select_widget/labels_select_root.vue b/app/assets/javascripts/vue_shared/components/sidebar/labels_select_widget/labels_select_root.vue
deleted file mode 100644
index 2c27a69d587..00000000000
--- a/app/assets/javascripts/vue_shared/components/sidebar/labels_select_widget/labels_select_root.vue
+++ /dev/null
@@ -1,406 +0,0 @@
-<script>
-import { debounce } from 'lodash';
-import issuableLabelsSubscription from 'ee_else_ce/sidebar/queries/issuable_labels.subscription.graphql';
-import { MutationOperationMode, getIdFromGraphQLId } from '~/graphql_shared/utils';
-import { createAlert } from '~/flash';
-import glFeatureFlagsMixin from '~/vue_shared/mixins/gl_feature_flags_mixin';
-import { IssuableType } from '~/issues/constants';
-
-import { __ } from '~/locale';
-import SidebarEditableItem from '~/sidebar/components/sidebar_editable_item.vue';
-import { issuableLabelsQueries } from '~/sidebar/constants';
-import { DEBOUNCE_DROPDOWN_DELAY, DropdownVariant } from './constants';
-import DropdownContents from './dropdown_contents.vue';
-import DropdownValue from './dropdown_value.vue';
-import {
- isDropdownVariantSidebar,
- isDropdownVariantStandalone,
- isDropdownVariantEmbedded,
-} from './utils';
-
-export default {
- components: {
- DropdownValue,
- DropdownContents,
- SidebarEditableItem,
- },
- mixins: [glFeatureFlagsMixin()],
- inject: {
- allowLabelEdit: {
- default: false,
- },
- },
- props: {
- iid: {
- type: String,
- required: false,
- default: '',
- },
- fullPath: {
- type: String,
- required: true,
- },
- allowLabelRemove: {
- type: Boolean,
- required: false,
- default: false,
- },
- allowMultiselect: {
- type: Boolean,
- required: false,
- default: false,
- },
- variant: {
- type: String,
- required: false,
- default: DropdownVariant.Sidebar,
- },
- labelsFilterBasePath: {
- type: String,
- required: false,
- default: '',
- },
- labelsFilterParam: {
- type: String,
- required: false,
- default: 'label_name',
- },
- dropdownButtonText: {
- type: String,
- required: false,
- default: __('Label'),
- },
- labelsListTitle: {
- type: String,
- required: false,
- default: __('Assign labels'),
- },
- labelsCreateTitle: {
- type: String,
- required: false,
- default: __('Create group label'),
- },
- footerCreateLabelTitle: {
- type: String,
- required: false,
- default: __('Create group label'),
- },
- footerManageLabelTitle: {
- type: String,
- required: false,
- default: __('Manage group labels'),
- },
- issuableType: {
- type: String,
- required: true,
- },
- workspaceType: {
- type: String,
- required: true,
- },
- attrWorkspacePath: {
- type: String,
- required: true,
- },
- labelCreateType: {
- type: String,
- required: true,
- },
- },
- data() {
- return {
- contentIsOnViewport: true,
- issuable: null,
- labelsSelectInProgress: false,
- oldIid: null,
- sidebarExpandedOnClick: false,
- };
- },
- computed: {
- isLoading() {
- return this.labelsSelectInProgress || this.$apollo.queries.issuable.loading;
- },
- issuableLabelIds() {
- return this.issuableLabels.map((label) => label.id);
- },
- issuableLabels() {
- return this.issuable?.labels.nodes || [];
- },
- issuableId() {
- return this.issuable?.id;
- },
- },
- apollo: {
- issuable: {
- query() {
- return issuableLabelsQueries[this.issuableType].issuableQuery;
- },
- skip() {
- return !isDropdownVariantSidebar(this.variant);
- },
- variables() {
- return {
- iid: this.iid,
- fullPath: this.fullPath,
- };
- },
- update(data) {
- return data.workspace?.issuable;
- },
- error() {
- createAlert({ message: __('Error fetching labels.') });
- },
- subscribeToMore: {
- document() {
- return issuableLabelsSubscription;
- },
- variables() {
- return {
- issuableId: this.issuableId,
- };
- },
- skip() {
- return !this.issuableId || !this.isDropdownVariantSidebar;
- },
- updateQuery(
- _,
- {
- subscriptionData: {
- data: { issuableLabelsUpdated },
- },
- },
- ) {
- if (issuableLabelsUpdated) {
- const {
- id,
- labels: { nodes },
- } = issuableLabelsUpdated;
- this.$emit('updateSelectedLabels', { id, labels: nodes });
- }
- },
- },
- },
- },
- watch: {
- iid(_, oldVal) {
- this.oldIid = oldVal;
- },
- },
- mounted() {
- document.addEventListener('toggleSidebarRevealLabelsDropdown', this.handleCollapsedValueClick);
- },
- beforeDestroy() {
- document.removeEventListener(
- 'toggleSidebarRevealLabelsDropdown',
- this.handleCollapsedValueClick,
- );
- },
- methods: {
- handleDropdownClose(labels) {
- if (this.iid !== '') {
- this.updateSelectedLabels(this.getUpdateVariables(labels));
- } else {
- this.$emit('updateSelectedLabels', { labels });
- }
-
- this.collapseEditableItem();
- },
- collapseEditableItem() {
- this.$refs.editable?.collapse();
- if (this.sidebarExpandedOnClick) {
- this.sidebarExpandedOnClick = false;
- this.$emit('toggleCollapse');
- }
- },
- handleCollapsedValueClick() {
- this.sidebarExpandedOnClick = true;
- this.$emit('toggleCollapse');
- debounce(() => {
- this.$refs.editable.toggle();
- this.$refs.dropdownContents.showDropdown();
- }, DEBOUNCE_DROPDOWN_DELAY)();
- },
- getUpdateVariables(labels) {
- let labelIds = [];
-
- labelIds = labels.map(({ id }) => id);
- const currentIid = this.oldIid || this.iid;
-
- const updateVariables = {
- iid: currentIid,
- projectPath: this.fullPath,
- labelIds,
- };
-
- switch (this.issuableType) {
- case IssuableType.Issue:
- return updateVariables;
- case IssuableType.MergeRequest:
- return {
- ...updateVariables,
- operationMode: MutationOperationMode.Replace,
- };
- case IssuableType.Epic:
- return {
- iid: currentIid,
- groupPath: this.fullPath,
- addLabelIds: labelIds.map((id) => getIdFromGraphQLId(id)),
- removeLabelIds: this.issuableLabelIds
- .filter((id) => !labelIds.includes(id))
- .map((id) => getIdFromGraphQLId(id)),
- };
- default:
- return {};
- }
- },
- updateSelectedLabels(inputVariables) {
- this.labelsSelectInProgress = true;
-
- this.$apollo
- .mutate({
- mutation: issuableLabelsQueries[this.issuableType].mutation,
- variables: { input: inputVariables },
- })
- .then(({ data }) => {
- if (data.updateIssuableLabels?.errors?.length) {
- throw new Error();
- }
-
- this.$emit('updateSelectedLabels', {
- id: data.updateIssuableLabels?.issuable?.id,
- labels: data.updateIssuableLabels?.issuable?.labels?.nodes,
- });
- })
- .catch((error) =>
- createAlert({
- message: __('An error occurred while updating labels.'),
- captureError: true,
- error,
- }),
- )
- .finally(() => {
- this.labelsSelectInProgress = false;
- });
- },
- getRemoveVariables(labelId) {
- const removeVariables = {
- iid: this.iid,
- projectPath: this.fullPath,
- };
-
- switch (this.issuableType) {
- case IssuableType.Issue:
- return {
- ...removeVariables,
- removeLabelIds: [labelId],
- };
- case IssuableType.MergeRequest:
- return {
- ...removeVariables,
- labelIds: [labelId],
- operationMode: MutationOperationMode.Remove,
- };
- case IssuableType.Epic:
- return {
- iid: this.iid,
- removeLabelIds: [getIdFromGraphQLId(labelId)],
- groupPath: this.fullPath,
- };
- default:
- return {};
- }
- },
- handleLabelRemove(labelId) {
- this.updateSelectedLabels(this.getRemoveVariables(labelId));
- this.$emit('onLabelRemove', labelId);
- },
- isDropdownVariantSidebar,
- isDropdownVariantStandalone,
- isDropdownVariantEmbedded,
- },
-};
-</script>
-
-<template>
- <div
- class="labels-select-wrapper gl-relative"
- :class="{
- 'is-standalone': isDropdownVariantStandalone(variant),
- 'is-embedded': isDropdownVariantEmbedded(variant),
- }"
- data-testid="sidebar-labels"
- data-qa-selector="labels_block"
- >
- <template v-if="isDropdownVariantSidebar(variant)">
- <sidebar-editable-item
- ref="editable"
- :title="__('Labels')"
- :loading="isLoading"
- :can-edit="allowLabelEdit"
- @open="oldIid = null"
- >
- <template #collapsed>
- <dropdown-value
- :disable-labels="labelsSelectInProgress"
- :selected-labels="issuableLabels"
- :allow-label-remove="allowLabelRemove"
- :labels-filter-base-path="labelsFilterBasePath"
- :labels-filter-param="labelsFilterParam"
- @onLabelRemove="handleLabelRemove"
- @onCollapsedValueClick="handleCollapsedValueClick"
- >
- <slot></slot>
- </dropdown-value>
- </template>
- <template #default="{ edit }">
- <dropdown-value
- :disable-labels="labelsSelectInProgress"
- :selected-labels="issuableLabels"
- :allow-label-remove="allowLabelRemove"
- :labels-filter-base-path="labelsFilterBasePath"
- :labels-filter-param="labelsFilterParam"
- class="gl-mb-2"
- @onLabelRemove="handleLabelRemove"
- >
- <slot></slot>
- </dropdown-value>
- <dropdown-contents
- ref="dropdownContents"
- :dropdown-button-text="dropdownButtonText"
- :allow-multiselect="allowMultiselect"
- :labels-list-title="labelsListTitle"
- :footer-create-label-title="footerCreateLabelTitle"
- :footer-manage-label-title="footerManageLabelTitle"
- :labels-create-title="labelsCreateTitle"
- :selected-labels="issuableLabels"
- :variant="variant"
- :is-visible="edit"
- :full-path="fullPath"
- :workspace-type="workspaceType"
- :attr-workspace-path="attrWorkspacePath"
- :label-create-type="labelCreateType"
- @setLabels="handleDropdownClose"
- @closeDropdown="collapseEditableItem"
- />
- </template>
- </sidebar-editable-item>
- </template>
- <dropdown-contents
- v-else
- ref="dropdownContents"
- :allow-multiselect="allowMultiselect"
- :dropdown-button-text="dropdownButtonText"
- :labels-list-title="labelsListTitle"
- :footer-create-label-title="footerCreateLabelTitle"
- :footer-manage-label-title="footerManageLabelTitle"
- :labels-create-title="labelsCreateTitle"
- :selected-labels="issuableLabels"
- :variant="variant"
- :full-path="fullPath"
- :workspace-type="workspaceType"
- :attr-workspace-path="attrWorkspacePath"
- :label-create-type="labelCreateType"
- @setLabels="handleDropdownClose"
- />
- </div>
-</template>
diff --git a/app/assets/javascripts/vue_shared/components/sidebar/labels_select_widget/utils.js b/app/assets/javascripts/vue_shared/components/sidebar/labels_select_widget/utils.js
deleted file mode 100644
index b5cd946a189..00000000000
--- a/app/assets/javascripts/vue_shared/components/sidebar/labels_select_widget/utils.js
+++ /dev/null
@@ -1,22 +0,0 @@
-import { DropdownVariant } from './constants';
-
-/**
- * Returns boolean representing whether dropdown variant
- * is `sidebar`
- * @param {string} variant
- */
-export const isDropdownVariantSidebar = (variant) => variant === DropdownVariant.Sidebar;
-
-/**
- * Returns boolean representing whether dropdown variant
- * is `standalone`
- * @param {string} variant
- */
-export const isDropdownVariantStandalone = (variant) => variant === DropdownVariant.Standalone;
-
-/**
- * Returns boolean representing whether dropdown variant
- * is `embedded`
- * @param {string} variant
- */
-export const isDropdownVariantEmbedded = (variant) => variant === DropdownVariant.Embedded;
diff --git a/app/assets/javascripts/vue_shared/components/sidebar/queries/get_alert_assignees.query.graphql b/app/assets/javascripts/vue_shared/components/sidebar/queries/get_alert_assignees.query.graphql
deleted file mode 100644
index bb6c7181e5c..00000000000
--- a/app/assets/javascripts/vue_shared/components/sidebar/queries/get_alert_assignees.query.graphql
+++ /dev/null
@@ -1,21 +0,0 @@
-#import "~/graphql_shared/fragments/user.fragment.graphql"
-#import "~/graphql_shared/fragments/user_availability.fragment.graphql"
-
-query alertAssignees(
- $domain: AlertManagementDomainFilter = threat_monitoring
- $fullPath: ID!
- $iid: String!
-) {
- workspace: project(fullPath: $fullPath) {
- id
- issuable: alertManagementAlert(domain: $domain, iid: $iid) {
- iid
- assignees {
- nodes {
- ...User
- ...UserAvailability
- }
- }
- }
- }
-}
diff --git a/app/assets/javascripts/vue_shared/components/sidebar/queries/get_issue_assignees.query.graphql b/app/assets/javascripts/vue_shared/components/sidebar/queries/get_issue_assignees.query.graphql
deleted file mode 100644
index 4af07366a6d..00000000000
--- a/app/assets/javascripts/vue_shared/components/sidebar/queries/get_issue_assignees.query.graphql
+++ /dev/null
@@ -1,21 +0,0 @@
-#import "~/graphql_shared/fragments/user.fragment.graphql"
-#import "~/graphql_shared/fragments/user_availability.fragment.graphql"
-
-query issueAssignees($fullPath: ID!, $iid: String!) {
- workspace: project(fullPath: $fullPath) {
- id
- issuable: issue(iid: $iid) {
- id
- author {
- ...User
- ...UserAvailability
- }
- assignees {
- nodes {
- ...User
- ...UserAvailability
- }
- }
- }
- }
-}
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
deleted file mode 100644
index eae5e96ac46..00000000000
--- a/app/assets/javascripts/vue_shared/components/sidebar/queries/get_issue_participants.query.graphql
+++ /dev/null
@@ -1,17 +0,0 @@
-#import "~/graphql_shared/fragments/user.fragment.graphql"
-#import "~/graphql_shared/fragments/user_availability.fragment.graphql"
-
-query issueParticipants($fullPath: ID!, $iid: String!, $getStatus: Boolean = false) {
- workspace: project(fullPath: $fullPath) {
- id
- issuable: issue(iid: $iid) {
- id
- participants {
- nodes {
- ...User
- ...UserAvailability @include(if: $getStatus)
- }
- }
- }
- }
-}
diff --git a/app/assets/javascripts/vue_shared/components/sidebar/queries/get_issue_timelogs.query.graphql b/app/assets/javascripts/vue_shared/components/sidebar/queries/get_issue_timelogs.query.graphql
deleted file mode 100644
index b127b8ec5a9..00000000000
--- a/app/assets/javascripts/vue_shared/components/sidebar/queries/get_issue_timelogs.query.graphql
+++ /dev/null
@@ -1,13 +0,0 @@
-#import "~/graphql_shared/fragments/issuable_timelogs.fragment.graphql"
-
-query issueTimeTrackingReport($id: IssueID!) {
- issuable: issue(id: $id) {
- id
- title
- timelogs {
- nodes {
- ...TimelogFragment
- }
- }
- }
-}
diff --git a/app/assets/javascripts/vue_shared/components/sidebar/queries/get_merge_request_reviewers.query.graphql b/app/assets/javascripts/vue_shared/components/sidebar/queries/get_merge_request_reviewers.query.graphql
deleted file mode 100644
index f087ca6c982..00000000000
--- a/app/assets/javascripts/vue_shared/components/sidebar/queries/get_merge_request_reviewers.query.graphql
+++ /dev/null
@@ -1,26 +0,0 @@
-#import "~/graphql_shared/fragments/user.fragment.graphql"
-#import "~/graphql_shared/fragments/user_availability.fragment.graphql"
-
-query mergeRequestReviewers($fullPath: ID!, $iid: String!) {
- workspace: project(fullPath: $fullPath) {
- id
- issuable: mergeRequest(iid: $iid) {
- id
- reviewers {
- nodes {
- ...User
- ...UserAvailability
- mergeRequestInteraction {
- canMerge
- canUpdate
- approved
- reviewed
- }
- }
- }
- userPermissions {
- adminMergeRequest
- }
- }
- }
-}
diff --git a/app/assets/javascripts/vue_shared/components/sidebar/queries/get_mr_assignees.query.graphql b/app/assets/javascripts/vue_shared/components/sidebar/queries/get_mr_assignees.query.graphql
deleted file mode 100644
index f70cd723f2e..00000000000
--- a/app/assets/javascripts/vue_shared/components/sidebar/queries/get_mr_assignees.query.graphql
+++ /dev/null
@@ -1,30 +0,0 @@
-#import "~/graphql_shared/fragments/user.fragment.graphql"
-#import "~/graphql_shared/fragments/user_availability.fragment.graphql"
-
-query getMrAssignees($fullPath: ID!, $iid: String!) {
- workspace: project(fullPath: $fullPath) {
- id
- issuable: mergeRequest(iid: $iid) {
- id
- author {
- ...User
- ...UserAvailability
- mergeRequestInteraction {
- canMerge
- }
- }
- assignees {
- nodes {
- ...User
- ...UserAvailability
- mergeRequestInteraction {
- canMerge
- }
- }
- }
- userPermissions {
- canMerge
- }
- }
- }
-}
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
deleted file mode 100644
index 2781ac71f31..00000000000
--- a/app/assets/javascripts/vue_shared/components/sidebar/queries/get_mr_participants.query.graphql
+++ /dev/null
@@ -1,17 +0,0 @@
-#import "~/graphql_shared/fragments/user.fragment.graphql"
-#import "~/graphql_shared/fragments/user_availability.fragment.graphql"
-
-query getMrParticipants($fullPath: ID!, $iid: String!, $getStatus: Boolean = false) {
- workspace: project(fullPath: $fullPath) {
- id
- issuable: mergeRequest(iid: $iid) {
- id
- participants {
- nodes {
- ...User
- ...UserAvailability @include(if: $getStatus)
- }
- }
- }
- }
-}
diff --git a/app/assets/javascripts/vue_shared/components/sidebar/queries/get_mr_timelogs.query.graphql b/app/assets/javascripts/vue_shared/components/sidebar/queries/get_mr_timelogs.query.graphql
deleted file mode 100644
index 17f548b44b5..00000000000
--- a/app/assets/javascripts/vue_shared/components/sidebar/queries/get_mr_timelogs.query.graphql
+++ /dev/null
@@ -1,13 +0,0 @@
-#import "~/graphql_shared/fragments/issuable_timelogs.fragment.graphql"
-
-query mrTimeTrackingReport($id: MergeRequestID!) {
- issuable: mergeRequest(id: $id) {
- id
- title
- timelogs {
- nodes {
- ...TimelogFragment
- }
- }
- }
-}
diff --git a/app/assets/javascripts/vue_shared/components/sidebar/queries/merge_request_reviewers.subscription.graphql b/app/assets/javascripts/vue_shared/components/sidebar/queries/merge_request_reviewers.subscription.graphql
deleted file mode 100644
index a1b16b378b3..00000000000
--- a/app/assets/javascripts/vue_shared/components/sidebar/queries/merge_request_reviewers.subscription.graphql
+++ /dev/null
@@ -1,22 +0,0 @@
-#import "~/graphql_shared/fragments/user.fragment.graphql"
-#import "~/graphql_shared/fragments/user_availability.fragment.graphql"
-
-subscription mergeRequestReviewersUpdated($issuableId: IssuableID!) {
- mergeRequestReviewersUpdated(issuableId: $issuableId) {
- ... on MergeRequest {
- id
- reviewers {
- nodes {
- ...User
- ...UserAvailability
- mergeRequestInteraction {
- canMerge
- canUpdate
- approved
- reviewed
- }
- }
- }
- }
- }
-}
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
deleted file mode 100644
index 24de5ea4fe3..00000000000
--- a/app/assets/javascripts/vue_shared/components/sidebar/queries/update_issue_assignees.mutation.graphql
+++ /dev/null
@@ -1,18 +0,0 @@
-#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(
- input: { iid: $iid, assigneeUsernames: $assigneeUsernames, projectPath: $fullPath }
- ) {
- issuable: issue {
- id
- assignees {
- 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
deleted file mode 100644
index 5fec2ccbdfb..00000000000
--- a/app/assets/javascripts/vue_shared/components/sidebar/queries/update_mr_assignees.mutation.graphql
+++ /dev/null
@@ -1,21 +0,0 @@
-#import "~/graphql_shared/fragments/user.fragment.graphql"
-#import "~/graphql_shared/fragments/user_availability.fragment.graphql"
-
-mutation mergeRequestSetAssignees($iid: String!, $assigneeUsernames: [String!]!, $fullPath: ID!) {
- issuableSetAssignees: mergeRequestSetAssignees(
- input: { iid: $iid, assigneeUsernames: $assigneeUsernames, projectPath: $fullPath }
- ) {
- issuable: mergeRequest {
- id
- assignees {
- nodes {
- ...User
- ...UserAvailability
- mergeRequestInteraction {
- canMerge
- }
- }
- }
- }
- }
-}
diff --git a/app/assets/javascripts/vue_shared/components/sidebar/todo_toggle/todo_button.stories.js b/app/assets/javascripts/vue_shared/components/sidebar/todo_toggle/todo_button.stories.js
deleted file mode 100644
index 465ee9aa0d4..00000000000
--- a/app/assets/javascripts/vue_shared/components/sidebar/todo_toggle/todo_button.stories.js
+++ /dev/null
@@ -1,21 +0,0 @@
-import TodoButton from './todo_button.vue';
-
-export default {
- component: TodoButton,
- title: 'vue_shared/sidebar/todo_toggle/todo_button',
-};
-
-const Template = (args, { argTypes }) => ({
- components: { TodoButton },
- props: Object.keys(argTypes),
- template: '<todo-button v-bind="$props" v-on="$props" />',
-});
-
-export const Default = Template.bind({});
-Default.argTypes = {
- isTodo: {
- description: 'True if to-do is unresolved (i.e. not "done")',
- control: { type: 'boolean' },
- },
- click: { action: 'clicked' },
-};
diff --git a/app/assets/javascripts/vue_shared/components/sidebar/todo_toggle/todo_button.vue b/app/assets/javascripts/vue_shared/components/sidebar/todo_toggle/todo_button.vue
deleted file mode 100644
index cdc7422c7df..00000000000
--- a/app/assets/javascripts/vue_shared/components/sidebar/todo_toggle/todo_button.vue
+++ /dev/null
@@ -1,44 +0,0 @@
-<script>
-import { GlButton } from '@gitlab/ui';
-import { todoLabel, updateGlobalTodoCount } from './utils';
-
-export default {
- components: {
- GlButton,
- },
- props: {
- isTodo: {
- type: Boolean,
- required: false,
- default: true,
- },
- },
- computed: {
- buttonLabel() {
- return todoLabel(this.isTodo);
- },
- },
- methods: {
- incrementGlobalTodoCount() {
- updateGlobalTodoCount(1);
- },
- decrementGlobalTodoCount() {
- updateGlobalTodoCount(-1);
- },
- onToggle(event) {
- if (this.isTodo) {
- this.decrementGlobalTodoCount();
- } else {
- this.incrementGlobalTodoCount();
- }
- this.$emit('click', event);
- },
- },
-};
-</script>
-
-<template>
- <gl-button v-bind="$attrs" :aria-label="buttonLabel" @click="onToggle($event)">
- {{ buttonLabel }}
- </gl-button>
-</template>
diff --git a/app/assets/javascripts/vue_shared/components/sidebar/todo_toggle/utils.js b/app/assets/javascripts/vue_shared/components/sidebar/todo_toggle/utils.js
deleted file mode 100644
index 098ab72dfb5..00000000000
--- a/app/assets/javascripts/vue_shared/components/sidebar/todo_toggle/utils.js
+++ /dev/null
@@ -1,21 +0,0 @@
-import { __ } from '~/locale';
-
-export const todoLabel = (hasTodo) => {
- return hasTodo ? __('Mark as done') : __('Add a to do');
-};
-
-export const updateGlobalTodoCount = (additionalTodoCount) => {
- const countContainer = document.querySelector('.js-todos-count');
-
- if (countContainer === null) return;
-
- const currentCount = parseInt(countContainer.innerText, 10);
-
- const todoToggleEvent = new CustomEvent('todo:toggle', {
- detail: {
- count: Math.max(currentCount + additionalTodoCount, 0),
- },
- });
-
- document.dispatchEvent(todoToggleEvent);
-};
diff --git a/app/assets/javascripts/vue_shared/components/sidebar/toggle_sidebar.vue b/app/assets/javascripts/vue_shared/components/sidebar/toggle_sidebar.vue
deleted file mode 100644
index 6dacf4e10d3..00000000000
--- a/app/assets/javascripts/vue_shared/components/sidebar/toggle_sidebar.vue
+++ /dev/null
@@ -1,55 +0,0 @@
-<script>
-import { GlButton, GlTooltipDirective } from '@gitlab/ui';
-import { __ } from '~/locale';
-
-export default {
- name: 'ToggleSidebar',
- components: {
- GlButton,
- },
- directives: {
- GlTooltip: GlTooltipDirective,
- },
- props: {
- collapsed: {
- type: Boolean,
- required: true,
- },
- cssClasses: {
- type: String,
- required: false,
- default: '',
- },
- },
- computed: {
- tooltipLabel() {
- return this.collapsed ? __('Expand sidebar') : __('Collapse sidebar');
- },
- buttonIcon() {
- return this.collapsed ? 'chevron-double-lg-left' : 'chevron-double-lg-right';
- },
- allCssClasses() {
- return [this.cssClasses, { 'js-sidebar-collapsed': this.collapsed }];
- },
- },
- methods: {
- toggle() {
- this.$emit('toggle');
- },
- },
-};
-</script>
-
-<template>
- <gl-button
- v-gl-tooltip:body.viewport.left
- :title="tooltipLabel"
- :class="allCssClasses"
- class="gutter-toggle btn-sidebar-action js-sidebar-vue-toggle"
- :icon="buttonIcon"
- category="tertiary"
- size="small"
- :aria-label="__('toggle collapse')"
- @click="toggle"
- />
-</template>
diff --git a/app/assets/javascripts/vue_shared/components/source_viewer/components/chunk.vue b/app/assets/javascripts/vue_shared/components/source_viewer/components/chunk.vue
index a2d8b7cbd15..28a16cd846a 100644
--- a/app/assets/javascripts/vue_shared/components/source_viewer/components/chunk.vue
+++ b/app/assets/javascripts/vue_shared/components/source_viewer/components/chunk.vue
@@ -1,6 +1,6 @@
<script>
-import { GlIntersectionObserver, GlSafeHtmlDirective } from '@gitlab/ui';
-import { scrollToElement } from '~/lib/utils/common_utils';
+import { GlIntersectionObserver } from '@gitlab/ui';
+import LineHighlighter from '~/blob/line_highlighter';
import ChunkLine from './chunk_line.vue';
/*
@@ -20,9 +20,6 @@ export default {
ChunkLine,
GlIntersectionObserver,
},
- directives: {
- SafeHtml: GlSafeHtmlDirective,
- },
props: {
isFirstChunk: {
type: Boolean,
@@ -84,12 +81,14 @@ export default {
return;
}
- window.requestIdleCallback(() => {
+ window.requestIdleCallback(async () => {
this.isLoading = false;
const { hash } = this.$route;
if (hash && this.totalChunks > 0 && this.totalChunks === this.chunkIndex + 1) {
// when the last chunk is loaded scroll to the hash
- scrollToElement(hash, { behavior: 'auto' });
+ await this.$nextTick();
+ const lineHighlighter = new LineHighlighter({ scrollBehavior: 'auto' });
+ lineHighlighter.highlightHash(hash);
}
});
},
diff --git a/app/assets/javascripts/vue_shared/components/source_viewer/components/chunk_line.vue b/app/assets/javascripts/vue_shared/components/source_viewer/components/chunk_line.vue
index 0bf19f83d86..ce6741f33b1 100644
--- a/app/assets/javascripts/vue_shared/components/source_viewer/components/chunk_line.vue
+++ b/app/assets/javascripts/vue_shared/components/source_viewer/components/chunk_line.vue
@@ -1,11 +1,11 @@
<script>
-import { GlSafeHtmlDirective } from '@gitlab/ui';
+import SafeHtml from '~/vue_shared/directives/safe_html';
import glFeatureFlagMixin from '~/vue_shared/mixins/gl_feature_flags_mixin';
import { getPageParamValue, getPageSearchString } from '~/blob/utils';
export default {
directives: {
- SafeHtml: GlSafeHtmlDirective,
+ SafeHtml,
},
mixins: [glFeatureFlagMixin()],
props: {
diff --git a/app/assets/javascripts/vue_shared/components/source_viewer/plugins/link_dependencies.js b/app/assets/javascripts/vue_shared/components/source_viewer/plugins/link_dependencies.js
index fca2616f069..cd15916851c 100644
--- a/app/assets/javascripts/vue_shared/components/source_viewer/plugins/link_dependencies.js
+++ b/app/assets/javascripts/vue_shared/components/source_viewer/plugins/link_dependencies.js
@@ -4,6 +4,7 @@ import godepsJsonLinker from './utils/godeps_json_linker';
import gemfileLinker from './utils/gemfile_linker';
import podspecJsonLinker from './utils/podspec_json_linker';
import composerJsonLinker from './utils/composer_json_linker';
+import goSumLinker from './utils/go_sum_linker';
const DEPENDENCY_LINKERS = {
package_json: packageJsonLinker,
@@ -12,6 +13,7 @@ const DEPENDENCY_LINKERS = {
gemfile: gemfileLinker,
podspec_json: podspecJsonLinker,
composer_json: composerJsonLinker,
+ go_sum: goSumLinker,
};
/**
diff --git a/app/assets/javascripts/vue_shared/components/source_viewer/plugins/utils/go_sum_linker.js b/app/assets/javascripts/vue_shared/components/source_viewer/plugins/utils/go_sum_linker.js
new file mode 100644
index 00000000000..b290dfa78b9
--- /dev/null
+++ b/app/assets/javascripts/vue_shared/components/source_viewer/plugins/utils/go_sum_linker.js
@@ -0,0 +1,34 @@
+import { createLink } from './dependency_linker_util';
+
+const openTag = '<span class="">';
+const closeTag = '</span>';
+const TAG_URL = 'https://sum.golang.org/lookup/';
+const GO_PACKAGE_URL = 'https://pkg.go.dev/';
+
+const DEPENDENCY_REGEX = new RegExp(
+ /*
+ * Detects dependencies inside of content that is highlighted by Highlight.js
+ * Example: '<span class="">cloud.google.com/Go/bigquery v1.0.1/go.mod h1:i/xbL2UlR5RvWAURpBYZTtm/cXjCha9lbfbpx4poX+o=</span>'
+ * Group 1 (packagePath): 'cloud.google.com/Go/bigquery'
+ * Group 2 (version): 'v1.0.1/go.mod'
+ * Group 3 (base64url): 'i/xbL2UlR5RvWAURpBYZTtm/cXjCha9lbfbpx4poX+o='
+ */
+ `${openTag}(.*) (v.*) h1:(.*)${closeTag}`,
+ 'gm',
+);
+
+const handleReplace = (packagePath, version, tag) => {
+ const lowercasePath = packagePath.toLowerCase();
+ const packageHref = `${GO_PACKAGE_URL}${lowercasePath}`;
+ const packageLink = createLink(packageHref, packagePath);
+ const tagHref = `${TAG_URL}${lowercasePath}@${version.split('/go.mod')[0]}`;
+ const tagLink = createLink(tagHref, tag);
+
+ return `${openTag}${packageLink} ${version} h1:${tagLink}${closeTag}`;
+};
+
+export default (result) => {
+ return result.value.replace(DEPENDENCY_REGEX, (_, packagePath, version, tag) =>
+ handleReplace(packagePath, version, tag),
+ );
+};
diff --git a/app/assets/javascripts/vue_shared/components/source_viewer/source_viewer.vue b/app/assets/javascripts/vue_shared/components/source_viewer/source_viewer.vue
index f621a23734a..0cfee93ce5d 100644
--- a/app/assets/javascripts/vue_shared/components/source_viewer/source_viewer.vue
+++ b/app/assets/javascripts/vue_shared/components/source_viewer/source_viewer.vue
@@ -1,5 +1,5 @@
<script>
-import { GlSafeHtmlDirective, GlLoadingIcon } from '@gitlab/ui';
+import { GlLoadingIcon } from '@gitlab/ui';
import LineHighlighter from '~/blob/line_highlighter';
import eventHub from '~/notes/event_hub';
import languageLoader from '~/content_editor/services/highlight_js_language_loader';
@@ -28,9 +28,6 @@ export default {
GlLoadingIcon,
Chunk,
},
- directives: {
- SafeHtml: GlSafeHtmlDirective,
- },
mixins: [Tracking.mixin()],
props: {
blob: {
diff --git a/app/assets/javascripts/vue_shared/components/user_popover/user_popover.vue b/app/assets/javascripts/vue_shared/components/user_popover/user_popover.vue
index 80c1fcbacfa..d06bc7b8f98 100644
--- a/app/assets/javascripts/vue_shared/components/user_popover/user_popover.vue
+++ b/app/assets/javascripts/vue_shared/components/user_popover/user_popover.vue
@@ -4,11 +4,11 @@ import {
GlLink,
GlSkeletonLoader,
GlIcon,
- GlSafeHtmlDirective,
GlSprintf,
GlButton,
GlAvatarLabeled,
} from '@gitlab/ui';
+import SafeHtml from '~/vue_shared/directives/safe_html';
import { glEmojiTag } from '~/emoji';
import { createAlert } from '~/flash';
import { followUser, unfollowUser } from '~/rest_api';
@@ -44,7 +44,7 @@ export default {
GlAvatarLabeled,
},
directives: {
- SafeHtml: GlSafeHtmlDirective,
+ SafeHtml,
},
mixins: [Tracking.mixin()],
props: {
diff --git a/app/assets/javascripts/vue_shared/components/web_ide_link.vue b/app/assets/javascripts/vue_shared/components/web_ide_link.vue
index 6d179b3dc92..383dc27ea5e 100644
--- a/app/assets/javascripts/vue_shared/components/web_ide_link.vue
+++ b/app/assets/javascripts/vue_shared/components/web_ide_link.vue
@@ -1,14 +1,16 @@
<script>
-import { GlModal, GlSprintf, GlLink } from '@gitlab/ui';
+import { GlModal, GlSprintf, GlLink, GlPopover } from '@gitlab/ui';
import { s__, __ } from '~/locale';
+import UserCalloutDismisser from '~/vue_shared/components/user_callout_dismisser.vue';
import ActionsButton from '~/vue_shared/components/actions_button.vue';
import LocalStorageSync from '~/vue_shared/components/local_storage_sync.vue';
import ConfirmForkModal from '~/vue_shared/components/confirm_fork_modal.vue';
+import glFeatureFlagsMixin from '~/vue_shared/mixins/gl_feature_flags_mixin';
-const KEY_EDIT = 'edit';
-const KEY_WEB_IDE = 'webide';
-const KEY_GITPOD = 'gitpod';
-const KEY_PIPELINE_EDITOR = 'pipeline_editor';
+export const KEY_EDIT = 'edit';
+export const KEY_WEB_IDE = 'webide';
+export const KEY_GITPOD = 'gitpod';
+export const KEY_PIPELINE_EDITOR = 'pipeline_editor';
export const i18n = {
modal: {
@@ -25,6 +27,9 @@ export const i18n = {
),
};
+export const PREFERRED_EDITOR_KEY = 'gl-web-ide-button-selected';
+export const PREFERRED_EDITOR_RESET_KEY = 'gl-web-ide-button-selected-reset';
+
export default {
components: {
ActionsButton,
@@ -32,9 +37,12 @@ export default {
GlModal,
GlSprintf,
GlLink,
+ GlPopover,
ConfirmForkModal,
+ UserCalloutDismisser,
},
i18n,
+ mixins: [glFeatureFlagsMixin()],
props: {
isFork: {
type: Boolean,
@@ -131,6 +139,11 @@ export default {
required: false,
default: '',
},
+ webIdePromoPopoverImg: {
+ type: String,
+ required: false,
+ default: '',
+ },
},
data() {
return {
@@ -296,6 +309,12 @@ export default {
},
};
},
+ displayVscodeWebIdeCallout() {
+ return this.glFeatures.vscodeWebIde && !this.showEditButton;
+ },
+ },
+ mounted() {
+ this.resetPreferredEditor();
},
methods: {
select(key) {
@@ -304,41 +323,109 @@ export default {
showModal(dataKey) {
this[dataKey] = true;
},
+ resetPreferredEditor() {
+ if (!this.glFeatures.vscodeWebIde || this.showEditButton) {
+ return;
+ }
+
+ if (localStorage.getItem(PREFERRED_EDITOR_RESET_KEY) === 'true') {
+ return;
+ }
+
+ localStorage.setItem(PREFERRED_EDITOR_KEY, KEY_WEB_IDE);
+ localStorage.setItem(PREFERRED_EDITOR_RESET_KEY, true);
+
+ this.select(KEY_WEB_IDE);
+ },
+ dismissCalloutOnActionClicked(dismiss) {
+ if (this.displayVscodeWebIdeCallout) {
+ dismiss();
+ }
+ },
},
+ webIdeButtonId: 'web-ide-link',
+ PREFERRED_EDITOR_KEY,
};
</script>
<template>
- <div class="gl-sm-ml-3">
- <actions-button
- :actions="actions"
- :selected-key="selection"
- :variant="isBlob ? 'confirm' : 'default'"
- :category="isBlob ? 'primary' : 'secondary'"
- @select="select"
- />
- <local-storage-sync
- storage-key="gl-web-ide-button-selected"
- :value="selection"
- as-string
- @input="select"
- />
- <gl-modal
- v-if="computedShowGitpodButton && !gitpodEnabled"
- v-model="showEnableGitpodModal"
- v-bind="enableGitpodModalProps"
- >
- <gl-sprintf :message="$options.i18n.modal.content">
- <template #link="{ content }">
- <gl-link :href="userPreferencesGitpodPath">{{ content }}</gl-link>
- </template>
- </gl-sprintf>
- </gl-modal>
- <confirm-fork-modal
- v-if="showWebIdeButton || showEditButton"
- v-model="showForkModal"
- :modal-id="forkModalId"
- :fork-path="forkPath"
- />
- </div>
+ <user-callout-dismisser
+ :skip-query="!displayVscodeWebIdeCallout"
+ feature-name="vscode_web_ide_callout"
+ >
+ <template #default="{ dismiss, shouldShowCallout }">
+ <div class="gl-sm-ml-3">
+ <actions-button
+ :id="$options.webIdeButtonId"
+ :actions="actions"
+ :selected-key="selection"
+ :variant="isBlob ? 'confirm' : 'default'"
+ :category="isBlob ? 'primary' : 'secondary'"
+ :show-action-tooltip="!displayVscodeWebIdeCallout || !shouldShowCallout"
+ @select="select"
+ @actionClicked="dismissCalloutOnActionClicked(dismiss)"
+ />
+ <local-storage-sync
+ :storage-key="$options.PREFERRED_EDITOR_KEY"
+ :value="selection"
+ as-string
+ @input="select"
+ />
+ <gl-modal
+ v-if="computedShowGitpodButton && !gitpodEnabled"
+ v-model="showEnableGitpodModal"
+ v-bind="enableGitpodModalProps"
+ >
+ <gl-sprintf :message="$options.i18n.modal.content">
+ <template #link="{ content }">
+ <gl-link :href="userPreferencesGitpodPath">{{ content }}</gl-link>
+ </template>
+ </gl-sprintf>
+ </gl-modal>
+ <confirm-fork-modal
+ v-if="showWebIdeButton || showEditButton"
+ v-model="showForkModal"
+ :modal-id="forkModalId"
+ :fork-path="forkPath"
+ />
+ <gl-popover
+ v-if="displayVscodeWebIdeCallout"
+ :target="$options.webIdeButtonId"
+ :show="shouldShowCallout"
+ :css-classes="['web-ide-promo-popover']"
+ :boundary-padding="80"
+ show-close-button
+ triggers="manual"
+ @close-button-clicked="dismiss"
+ >
+ <img
+ :src="webIdePromoPopoverImg"
+ class="web-ide-promo-popover-illustration"
+ width="280"
+ height="140"
+ />
+ <div class="gl-mx-2">
+ <h5 class="gl-mt-3 gl-mb-3">{{ __('The new Web IDE') }}</h5>
+ <p>
+ {{
+ __(
+ 'VS Code in your browser. View code and make changes from the same UI as in your local IDE.',
+ )
+ }}
+ </p>
+ <gl-link
+ class="gl-button btn btn-confirm block gl-mb-4 gl-mt-5"
+ variant="confirm"
+ category="primary"
+ target="_blank"
+ :href="webIdeUrl"
+ block
+ >
+ {{ __('Try it out now') }}
+ </gl-link>
+ </div>
+ </gl-popover>
+ </div>
+ </template>
+ </user-callout-dismisser>
</template>
diff --git a/app/assets/javascripts/vue_shared/constants.js b/app/assets/javascripts/vue_shared/constants.js
index a851f84ed2f..2f85a29fb84 100644
--- a/app/assets/javascripts/vue_shared/constants.js
+++ b/app/assets/javascripts/vue_shared/constants.js
@@ -13,7 +13,9 @@ export const SHORT_DATE_FORMAT = 'd mmm, yyyy';
export const ISO_SHORT_FORMAT = 'yyyy-mm-dd';
-export const DATE_FORMATS = [SHORT_DATE_FORMAT, ISO_SHORT_FORMAT];
+export const LONG_DATE_FORMAT_WITH_TZ = 'yyyy-mm-dd HH:MM:ss Z';
+
+export const DATE_FORMATS = [SHORT_DATE_FORMAT, ISO_SHORT_FORMAT, LONG_DATE_FORMAT_WITH_TZ];
const getTimeLabel = (days) => n__('1 day', '%d days', days);
diff --git a/app/assets/javascripts/vue_shared/issuable/create/components/issuable_form.vue b/app/assets/javascripts/vue_shared/issuable/create/components/issuable_form.vue
index 25799171905..2644befc902 100644
--- a/app/assets/javascripts/vue_shared/issuable/create/components/issuable_form.vue
+++ b/app/assets/javascripts/vue_shared/issuable/create/components/issuable_form.vue
@@ -1,8 +1,8 @@
<script>
import { GlForm, GlFormInput, GlFormGroup } from '@gitlab/ui';
import MarkdownField from '~/vue_shared/components/markdown/field.vue';
-import { DropdownVariant } from '~/vue_shared/components/sidebar/labels_select_vue/constants';
-import LabelsSelect from '~/vue_shared/components/sidebar/labels_select_vue/labels_select_root.vue';
+import { DropdownVariant } from '~/sidebar/components/labels/labels_select_vue/constants';
+import LabelsSelect from '~/sidebar/components/labels/labels_select_vue/labels_select_root.vue';
export default {
LabelSelectVariant: DropdownVariant,
diff --git a/app/assets/javascripts/vue_shared/issuable/create/components/issuable_label_selector.vue b/app/assets/javascripts/vue_shared/issuable/create/components/issuable_label_selector.vue
new file mode 100644
index 00000000000..b3f9c8d9fcd
--- /dev/null
+++ b/app/assets/javascripts/vue_shared/issuable/create/components/issuable_label_selector.vue
@@ -0,0 +1,92 @@
+<script>
+import { GlFormGroup, GlIcon } from '@gitlab/ui';
+import { getIdFromGraphQLId } from '~/graphql_shared/utils';
+import LabelsSelect from '~/sidebar/components/labels/labels_select_widget/labels_select_root.vue';
+import { __ } from '~/locale';
+
+export default {
+ components: {
+ GlFormGroup,
+ GlIcon,
+ LabelsSelect,
+ },
+ inject: [
+ 'allowLabelRemove',
+ 'attrWorkspacePath',
+ 'fieldName',
+ 'fullPath',
+ 'labelsFilterBasePath',
+ 'initialLabels',
+ 'issuableType',
+ 'labelType',
+ 'variant',
+ 'workspaceType',
+ ],
+ data() {
+ return {
+ selectedLabels: this.initialLabels || [],
+ };
+ },
+ methods: {
+ handleUpdateSelectedLabels({ labels }) {
+ this.selectedLabels = labels.map((label) => ({ ...label, id: getIdFromGraphQLId(label.id) }));
+ },
+ handleLabelRemove(labelId) {
+ this.selectedLabels = this.selectedLabels.filter((label) => label.id !== labelId);
+ },
+ },
+ i18n: {
+ fieldLabel: __('Labels'),
+ dropdownButtonText: __('Select label'),
+ listTitle: __('Select label'),
+ createTitle: __('Create project label'),
+ manageTitle: __('Manage project labels'),
+ emptySelection: __('None'),
+ },
+};
+</script>
+
+<template>
+ <gl-form-group class="row" label-class="gl-display-none">
+ <label class="col-12 gl-display-flex gl-align-center gl-mb-1">
+ {{ $options.i18n.fieldLabel }}
+ <div class="gl-ml-3">
+ <gl-icon name="labels" />
+ <span class="gl-font-base gl-line-height-24">{{ selectedLabels.length }}</span>
+ </div>
+ </label>
+ <div class="col-12">
+ <div class="issuable-form-select-holder">
+ <input
+ v-for="selectedLabel in selectedLabels"
+ :key="selectedLabel.id"
+ :value="selectedLabel.id"
+ :name="fieldName"
+ type="hidden"
+ />
+ <labels-select
+ class="block labels"
+ :allow-label-remove="allowLabelRemove"
+ :allow-multiselect="true"
+ :show-embedded-labels-list="true"
+ :full-path="fullPath"
+ :attr-workspace-path="attrWorkspacePath"
+ :labels-filter-base-path="labelsFilterBasePath"
+ :dropdown-button-text="$options.i18n.dropdownButtonText"
+ :labels-list-title="$options.i18n.listTitle"
+ :footer-create-label-title="$options.i18n.createTitle"
+ :footer-manage-label-title="$options.i18n.manageTitle"
+ :variant="variant"
+ :workspace-type="workspaceType"
+ :issuable-type="issuableType"
+ :label-create-type="labelType"
+ :selected-labels="selectedLabels"
+ @updateSelectedLabels="handleUpdateSelectedLabels"
+ @onLabelRemove="handleLabelRemove"
+ >
+ {{ $options.i18n.emptySelection }}
+ </labels-select>
+ </div>
+ </div>
+ </gl-form-group>
+</template>
diff --git a/app/assets/javascripts/vue_shared/issuable/list/components/issuable_item.vue b/app/assets/javascripts/vue_shared/issuable/list/components/issuable_item.vue
index 30b7b073ac3..5b303b9a314 100644
--- a/app/assets/javascripts/vue_shared/issuable/list/components/issuable_item.vue
+++ b/app/assets/javascripts/vue_shared/issuable/list/components/issuable_item.vue
@@ -318,8 +318,8 @@ export default {
<slot name="statistics"></slot>
<li
v-if="showDiscussions"
- data-testid="issuable-discussions"
- class="issuable-comments gl-display-none gl-sm-display-block"
+ class="gl-display-none gl-sm-display-block"
+ data-testid="issuable-comments"
>
<gl-link
v-gl-tooltip.top
diff --git a/app/assets/javascripts/vue_shared/issuable/list/components/issuable_list_root.vue b/app/assets/javascripts/vue_shared/issuable/list/components/issuable_list_root.vue
index dd3d7c8f4d6..5b6c5bf6e03 100644
--- a/app/assets/javascripts/vue_shared/issuable/list/components/issuable_list_root.vue
+++ b/app/assets/javascripts/vue_shared/issuable/list/components/issuable_list_root.vue
@@ -331,6 +331,7 @@ export default {
<slot name="sidebar-items" :checked-issuables="bulkEditIssuables"></slot>
</template>
</issuable-bulk-edit-sidebar>
+ <slot name="list-body"></slot>
<ul v-if="issuablesLoading" class="content-list">
<li v-for="n in skeletonItemCount" :key="n" class="issue gl-px-5! gl-py-5!">
<gl-skeleton-loader />
diff --git a/app/assets/javascripts/vue_shared/issuable/show/components/issuable_description.vue b/app/assets/javascripts/vue_shared/issuable/show/components/issuable_description.vue
index d4e9120ff17..ce1851ab873 100644
--- a/app/assets/javascripts/vue_shared/issuable/show/components/issuable_description.vue
+++ b/app/assets/javascripts/vue_shared/issuable/show/components/issuable_description.vue
@@ -1,7 +1,6 @@
<script>
-import { GlSafeHtmlDirective as SafeHtml } from '@gitlab/ui';
-import $ from 'jquery';
-import '~/behaviors/markdown/render_gfm';
+import SafeHtml from '~/vue_shared/directives/safe_html';
+import { renderGFM } from '~/behaviors/markdown/render_gfm';
export default {
directives: {
@@ -26,12 +25,7 @@ export default {
},
},
mounted() {
- this.renderGFM();
- },
- methods: {
- renderGFM() {
- $(this.$refs.gfmContainer).renderGFM();
- },
+ renderGFM(this.$refs.gfmContainer);
},
};
</script>
diff --git a/app/assets/javascripts/vue_shared/issuable/show/components/issuable_title.vue b/app/assets/javascripts/vue_shared/issuable/show/components/issuable_title.vue
index 35124bd15d2..fd94245b7c9 100644
--- a/app/assets/javascripts/vue_shared/issuable/show/components/issuable_title.vue
+++ b/app/assets/javascripts/vue_shared/issuable/show/components/issuable_title.vue
@@ -1,12 +1,6 @@
<script>
-import {
- GlIcon,
- GlBadge,
- GlButton,
- GlIntersectionObserver,
- GlTooltipDirective,
- GlSafeHtmlDirective as SafeHtml,
-} from '@gitlab/ui';
+import { GlIcon, GlBadge, GlButton, GlIntersectionObserver, GlTooltipDirective } from '@gitlab/ui';
+import SafeHtml from '~/vue_shared/directives/safe_html';
import { __ } from '~/locale';
import { IssuableStates } from '~/vue_shared/issuable/list/constants';
diff --git a/app/assets/javascripts/vue_shared/new_namespace/components/legacy_container.vue b/app/assets/javascripts/vue_shared/new_namespace/components/legacy_container.vue
index e42720bf1db..ae40076ca96 100644
--- a/app/assets/javascripts/vue_shared/new_namespace/components/legacy_container.vue
+++ b/app/assets/javascripts/vue_shared/new_namespace/components/legacy_container.vue
@@ -1,4 +1,6 @@
<script>
+import projectNew from '~/projects/project_new';
+
export default {
inheritAttrs: false,
props: {
@@ -16,6 +18,7 @@ export default {
this.source = legacyEntry.parentNode;
this.$el.appendChild(legacyEntry);
legacyEntry.classList.add('active');
+ projectNew.bindEvents();
}
},
diff --git a/app/assets/javascripts/vue_shared/new_namespace/components/welcome.vue b/app/assets/javascripts/vue_shared/new_namespace/components/welcome.vue
index 5cd2018bb8c..b6a459f21e0 100644
--- a/app/assets/javascripts/vue_shared/new_namespace/components/welcome.vue
+++ b/app/assets/javascripts/vue_shared/new_namespace/components/welcome.vue
@@ -1,5 +1,5 @@
<script>
-import { GlSafeHtmlDirective as SafeHtml } from '@gitlab/ui';
+import SafeHtml from '~/vue_shared/directives/safe_html';
import Tracking from '~/tracking';
export default {
diff --git a/app/assets/javascripts/vue_shared/new_namespace/new_namespace_page.vue b/app/assets/javascripts/vue_shared/new_namespace/new_namespace_page.vue
index 624ae7027d5..318adec2319 100644
--- a/app/assets/javascripts/vue_shared/new_namespace/new_namespace_page.vue
+++ b/app/assets/javascripts/vue_shared/new_namespace/new_namespace_page.vue
@@ -1,5 +1,6 @@
<script>
-import { GlBreadcrumb, GlIcon, GlSafeHtmlDirective as SafeHtml } from '@gitlab/ui';
+import { GlBreadcrumb, GlIcon } from '@gitlab/ui';
+import SafeHtml from '~/vue_shared/directives/safe_html';
import NewTopLevelGroupAlert from '~/groups/components/new_top_level_group_alert.vue';
import LegacyContainer from './components/legacy_container.vue';
diff --git a/app/assets/javascripts/vue_shared/security_reports/security_reports_app.vue b/app/assets/javascripts/vue_shared/security_reports/security_reports_app.vue
index 0e1975e1c09..b739baad5d7 100644
--- a/app/assets/javascripts/vue_shared/security_reports/security_reports_app.vue
+++ b/app/assets/javascripts/vue_shared/security_reports/security_reports_app.vue
@@ -2,8 +2,8 @@
import { mapActions, mapGetters } from 'vuex';
import { createAlert } from '~/flash';
import { s__ } from '~/locale';
-import ReportSection from '~/reports/components/report_section.vue';
-import { ERROR, SLOT_SUCCESS, SLOT_LOADING, SLOT_ERROR } from '~/reports/constants';
+import ReportSection from '~/ci/reports/components/report_section.vue';
+import { ERROR, SLOT_SUCCESS, SLOT_LOADING, SLOT_ERROR } from '~/ci/reports/constants';
import glFeatureFlagsMixin from '~/vue_shared/mixins/gl_feature_flags_mixin';
import HelpIcon from './components/help_icon.vue';
import SecurityReportDownloadDropdown from './components/security_report_download_dropdown.vue';
diff --git a/app/assets/javascripts/vue_shared/security_reports/store/getters.js b/app/assets/javascripts/vue_shared/security_reports/store/getters.js
index 08f6bcca15b..c274f531139 100644
--- a/app/assets/javascripts/vue_shared/security_reports/store/getters.js
+++ b/app/assets/javascripts/vue_shared/security_reports/store/getters.js
@@ -1,5 +1,5 @@
import { s__, sprintf } from '~/locale';
-import { LOADING, ERROR, SUCCESS } from '~/reports/constants';
+import { LOADING, ERROR, SUCCESS } from '~/ci/reports/constants';
import { TRANSLATION_IS_LOADING } from './messages';
import { countVulnerabilities, groupedTextBuilder } from './utils';
diff --git a/app/assets/javascripts/vue_shared/security_reports/store/utils.js b/app/assets/javascripts/vue_shared/security_reports/store/utils.js
index a6628fa0f9f..f3cb5fc16f0 100644
--- a/app/assets/javascripts/vue_shared/security_reports/store/utils.js
+++ b/app/assets/javascripts/vue_shared/security_reports/store/utils.js
@@ -29,7 +29,13 @@ export const fetchDiffData = (state, endpoint, category) => {
*/
export const enrichVulnerabilityWithFeedback = (vulnerability, feedback = []) =>
feedback
- .filter((fb) => fb.project_fingerprint === vulnerability.project_fingerprint)
+ .filter((fb) =>
+ // Some records still have a `finding_uuid` with null, we need to fallback to using `project_fingerprint` in those cases. Once all entries have been fixed, we can remove the fallback.
+ // related epic: https://gitlab.com/groups/gitlab-org/-/epics/2791
+ fb.finding_uuid !== null
+ ? fb.finding_uuid === vulnerability.finding_uuid
+ : fb.project_fingerprint === vulnerability.project_fingerprint,
+ )
.reduce((vuln, fb) => {
if (fb.feedback_type === FEEDBACK_TYPE_DISMISSAL) {
return {