summaryrefslogtreecommitdiff
path: root/app/assets/javascripts/vue_shared
diff options
context:
space:
mode:
authorGitLab Bot <gitlab-bot@gitlab.com>2022-05-19 07:33:21 +0000
committerGitLab Bot <gitlab-bot@gitlab.com>2022-05-19 07:33:21 +0000
commit36a59d088eca61b834191dacea009677a96c052f (patch)
treee4f33972dab5d8ef79e3944a9f403035fceea43f /app/assets/javascripts/vue_shared
parenta1761f15ec2cae7c7f7bbda39a75494add0dfd6f (diff)
downloadgitlab-ce-36a59d088eca61b834191dacea009677a96c052f.tar.gz
Add latest changes from gitlab-org/gitlab@15-0-stable-eev15.0.0-rc42
Diffstat (limited to 'app/assets/javascripts/vue_shared')
-rw-r--r--app/assets/javascripts/vue_shared/alert_details/components/alert_details.vue4
-rw-r--r--app/assets/javascripts/vue_shared/components/ci_cd_analytics/ci_cd_analytics_charts.vue10
-rw-r--r--app/assets/javascripts/vue_shared/components/ci_icon.vue33
-rw-r--r--app/assets/javascripts/vue_shared/components/color_picker/color_picker.vue11
-rw-r--r--app/assets/javascripts/vue_shared/components/commit.vue2
-rw-r--r--app/assets/javascripts/vue_shared/components/confidentiality_badge.vue39
-rw-r--r--app/assets/javascripts/vue_shared/components/confirm_danger/confirm_danger_modal.stories.js2
-rw-r--r--app/assets/javascripts/vue_shared/components/confirm_danger/confirm_danger_modal.vue14
-rw-r--r--app/assets/javascripts/vue_shared/components/confirm_danger/constants.js2
-rw-r--r--app/assets/javascripts/vue_shared/components/date_time_picker/date_time_picker.vue2
-rw-r--r--app/assets/javascripts/vue_shared/components/deployment_instance.vue7
-rw-r--r--app/assets/javascripts/vue_shared/components/dropdown/dropdown_hidden_input.vue18
-rw-r--r--app/assets/javascripts/vue_shared/components/dropdown/dropdown_search_input.vue49
-rw-r--r--app/assets/javascripts/vue_shared/components/dropdown/dropdown_widget/dropdown_widget.vue52
-rw-r--r--app/assets/javascripts/vue_shared/components/file_finder/index.vue10
-rw-r--r--app/assets/javascripts/vue_shared/components/filtered_search_bar/constants.js2
-rw-r--r--app/assets/javascripts/vue_shared/components/filtered_search_bar/filtered_search_bar_root.vue54
-rw-r--r--app/assets/javascripts/vue_shared/components/filtered_search_bar/tokens/author_token.vue1
-rw-r--r--app/assets/javascripts/vue_shared/components/filtered_search_bar/tokens/base_token.vue16
-rw-r--r--app/assets/javascripts/vue_shared/components/filtered_search_bar/tokens/branch_token.vue1
-rw-r--r--app/assets/javascripts/vue_shared/components/filtered_search_bar/tokens/emoji_token.vue1
-rw-r--r--app/assets/javascripts/vue_shared/components/filtered_search_bar/tokens/label_token.vue1
-rw-r--r--app/assets/javascripts/vue_shared/components/filtered_search_bar/tokens/milestone_token.vue1
-rw-r--r--app/assets/javascripts/vue_shared/components/filtered_search_bar/tokens/release_token.vue1
-rw-r--r--app/assets/javascripts/vue_shared/components/markdown/field.vue15
-rw-r--r--app/assets/javascripts/vue_shared/components/markdown/header.vue31
-rw-r--r--app/assets/javascripts/vue_shared/components/markdown/toolbar.vue26
-rw-r--r--app/assets/javascripts/vue_shared/components/metric_images/metric_images_tab.vue4
-rw-r--r--app/assets/javascripts/vue_shared/components/metric_images/metric_images_table.vue8
-rw-r--r--app/assets/javascripts/vue_shared/components/navigation_tabs.vue4
-rw-r--r--app/assets/javascripts/vue_shared/components/paginated_list.vue2
-rw-r--r--app/assets/javascripts/vue_shared/components/registry/list_item.vue1
-rw-r--r--app/assets/javascripts/vue_shared/components/registry/registry_search.vue1
-rw-r--r--app/assets/javascripts/vue_shared/components/runner_instructions/runner_instructions_modal.vue23
-rw-r--r--app/assets/javascripts/vue_shared/components/segmented_control_button_group.vue35
-rw-r--r--app/assets/javascripts/vue_shared/components/sidebar/issuable_move_dropdown.vue4
-rw-r--r--app/assets/javascripts/vue_shared/components/sidebar/labels_select_vue/dropdown_contents_create_view.vue2
-rw-r--r--app/assets/javascripts/vue_shared/components/sidebar/labels_select_vue/dropdown_contents_labels_view.vue2
-rw-r--r--app/assets/javascripts/vue_shared/components/sidebar/labels_select_vue/dropdown_title.vue2
-rw-r--r--app/assets/javascripts/vue_shared/components/sidebar/labels_select_widget/dropdown_contents_create_view.vue2
-rw-r--r--app/assets/javascripts/vue_shared/components/sidebar/labels_select_widget/dropdown_contents_labels_view.vue2
-rw-r--r--app/assets/javascripts/vue_shared/components/source_viewer/components/chunk_line.vue11
-rw-r--r--app/assets/javascripts/vue_shared/components/source_viewer/source_viewer.vue3
-rw-r--r--app/assets/javascripts/vue_shared/components/time_ago_tooltip.vue2
-rw-r--r--app/assets/javascripts/vue_shared/components/upload_dropzone/upload_dropzone.vue2
-rw-r--r--app/assets/javascripts/vue_shared/components/usage_quotas/usage_banner.vue68
-rw-r--r--app/assets/javascripts/vue_shared/components/user_popover/user_popover.vue90
-rw-r--r--app/assets/javascripts/vue_shared/components/user_select/user_select.vue2
-rw-r--r--app/assets/javascripts/vue_shared/constants.js14
-rw-r--r--app/assets/javascripts/vue_shared/issuable/create/components/issuable_form.vue19
-rw-r--r--app/assets/javascripts/vue_shared/issuable/list/components/issuable_item.vue15
-rw-r--r--app/assets/javascripts/vue_shared/issuable/list/components/issuable_list_root.vue6
-rw-r--r--app/assets/javascripts/vue_shared/issuable/show/components/issuable_header.vue42
-rw-r--r--app/assets/javascripts/vue_shared/issuable/show/components/issuable_show_root.vue8
-rw-r--r--app/assets/javascripts/vue_shared/issuable/show/components/issuable_title.vue2
-rw-r--r--app/assets/javascripts/vue_shared/mixins/timeago.js4
-rw-r--r--app/assets/javascripts/vue_shared/security_configuration/components/section_layout.vue34
-rw-r--r--app/assets/javascripts/vue_shared/security_configuration/components/section_loader.vue35
-rw-r--r--app/assets/javascripts/vue_shared/security_reports/store/utils.js2
59 files changed, 634 insertions, 222 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 948d2505966..c93f620995f 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
@@ -278,7 +278,7 @@ export default {
data-testid="viewIncidentBtn"
:href="incidentPath(alert.issue.iid)"
category="primary"
- variant="success"
+ variant="confirm"
>
{{ s__('AlertManagement|View incident') }}
</gl-button>
@@ -288,7 +288,7 @@ export default {
data-testid="createIncidentBtn"
:loading="incidentCreationInProgress"
category="primary"
- variant="success"
+ variant="confirm"
@click="createIncident()"
>
{{ s__('AlertManagement|Create incident') }}
diff --git a/app/assets/javascripts/vue_shared/components/ci_cd_analytics/ci_cd_analytics_charts.vue b/app/assets/javascripts/vue_shared/components/ci_cd_analytics/ci_cd_analytics_charts.vue
index 8b76af05ffe..6a03e38a31d 100644
--- a/app/assets/javascripts/vue_shared/components/ci_cd_analytics/ci_cd_analytics_charts.vue
+++ b/app/assets/javascripts/vue_shared/components/ci_cd_analytics/ci_cd_analytics_charts.vue
@@ -1,12 +1,12 @@
<script>
-import { GlSegmentedControl } from '@gitlab/ui';
import { s__, sprintf } from '~/locale';
+import SegmentedControlButtonGroup from '~/vue_shared/components/segmented_control_button_group.vue';
import CiCdAnalyticsAreaChart from './ci_cd_analytics_area_chart.vue';
export default {
components: {
- GlSegmentedControl,
CiCdAnalyticsAreaChart,
+ SegmentedControlButtonGroup,
},
props: {
charts: {
@@ -38,7 +38,11 @@ export default {
</script>
<template>
<div>
- <gl-segmented-control v-model="selectedChart" :options="chartRanges" class="gl-mb-4" />
+ <segmented-control-button-group
+ v-model="selectedChart"
+ :options="chartRanges"
+ class="gl-mb-4"
+ />
<ci-cd-analytics-area-chart
v-if="chart"
v-bind="$attrs"
diff --git a/app/assets/javascripts/vue_shared/components/ci_icon.vue b/app/assets/javascripts/vue_shared/components/ci_icon.vue
index 07bd6019b80..9bccc49e894 100644
--- a/app/assets/javascripts/vue_shared/components/ci_icon.vue
+++ b/app/assets/javascripts/vue_shared/components/ci_icon.vue
@@ -11,17 +11,22 @@ import { GlIcon } from '@gitlab/ui';
* }
*
* Used in:
- * - Pipelines table Badge
- * - Pipelines table mini graph
- * - Pipeline graph
- * - Pipeline show view badge
- * - Jobs table
+ * - Extended MR Popover
* - Jobs show view header
* - Jobs show view sidebar
+ * - Jobs table
* - Linked pipelines
- * - Extended MR Popover
+ * - Pipeline graph
+ * - Pipeline mini graph
+ * - Pipeline show view badge
+ * - Pipelines table Badge
+ */
+
+/*
+ * These sizes are defined in gitlab-ui/src/scss/variables.scss
+ * under '$gl-icon-sizes'
*/
-const validSizes = [8, 12, 16, 18, 24, 32, 48, 72];
+const validSizes = [8, 12, 14, 16, 24, 32, 48, 72];
export default {
components: {
@@ -45,6 +50,11 @@ export default {
required: false,
default: false,
},
+ isInteractive: {
+ type: Boolean,
+ required: false,
+ default: false,
+ },
cssClasses: {
type: String,
required: false,
@@ -52,9 +62,9 @@ export default {
},
},
computed: {
- cssClass() {
+ wrapperStyleClasses() {
const status = this.status.group;
- return `ci-status-icon ci-status-icon-${status} js-ci-status-icon-${status}`;
+ return `ci-status-icon ci-status-icon-${status} js-ci-status-icon-${status} gl-rounded-full gl-justify-content-center`;
},
icon() {
return this.borderless ? `${this.status.icon}_borderless` : this.status.icon;
@@ -63,7 +73,10 @@ export default {
};
</script>
<template>
- <span :class="cssClass">
+ <span
+ :class="[wrapperStyleClasses, { interactive: isInteractive }]"
+ :style="{ height: `${size}px`, width: `${size}px` }"
+ >
<gl-icon :name="icon" :size="size" :class="cssClasses" :aria-label="status.icon" />
</span>
</template>
diff --git a/app/assets/javascripts/vue_shared/components/color_picker/color_picker.vue b/app/assets/javascripts/vue_shared/components/color_picker/color_picker.vue
index 7a166f9a3e4..78db2bf15b0 100644
--- a/app/assets/javascripts/vue_shared/components/color_picker/color_picker.vue
+++ b/app/assets/javascripts/vue_shared/components/color_picker/color_picker.vue
@@ -4,6 +4,7 @@
*
* @example
* <color-picker
+ :id="example-id"
:invalid-feedback="__('Please enter a valid hex (#RRGGBB or #RGB) color value')"
:label="__('Background color')"
:value="#FF0000"
@@ -12,6 +13,7 @@
/>
*/
import { GlFormGroup, GlFormInput, GlFormInputGroup, GlLink, GlTooltipDirective } from '@gitlab/ui';
+import { uniqueId } from 'lodash';
import { __ } from '~/locale';
const PREVIEW_COLOR_DEFAULT_CLASSES =
@@ -29,6 +31,11 @@ export default {
GlTooltip: GlTooltipDirective,
},
props: {
+ id: {
+ type: String,
+ required: false,
+ default: () => uniqueId('color-picker-'),
+ },
invalidFeedback: {
type: String,
required: false,
@@ -94,14 +101,13 @@ export default {
<div>
<gl-form-group
:label="label"
- label-for="color-picker"
+ :label-for="id"
:description="description"
:invalid-feedback="invalidFeedback"
:state="state"
:class="{ 'gl-mb-3!': hasSuggestedColors }"
>
<gl-form-input-group
- id="color-picker"
max-length="7"
type="text"
class="gl-align-center gl-rounded-0 gl-rounded-top-right-base gl-rounded-bottom-right-base"
@@ -112,6 +118,7 @@ export default {
<template #prepend>
<div :class="previewColorClasses" :style="previewColor" data-testid="color-preview">
<gl-form-input
+ :id="id"
type="color"
class="gl-absolute gl-top-0 gl-left-0 gl-h-full! gl-p-0! gl-m-0! gl-opacity-0"
tabindex="-1"
diff --git a/app/assets/javascripts/vue_shared/components/commit.vue b/app/assets/javascripts/vue_shared/components/commit.vue
index ebbc1bfb037..388353bc35b 100644
--- a/app/assets/javascripts/vue_shared/components/commit.vue
+++ b/app/assets/javascripts/vue_shared/components/commit.vue
@@ -172,7 +172,9 @@ export default {
:img-src="author.avatar_url"
:img-alt="userImageAltDescription"
:tooltip-text="author.username"
+ :img-size="16"
class="avatar-image-container text-decoration-none"
+ img-css-classes="gl-mr-3"
/>
<tooltip-on-truncate :title="title" class="flex-truncate-child">
<gl-link :href="commitUrl" class="commit-row-message cgray">{{ title }}</gl-link>
diff --git a/app/assets/javascripts/vue_shared/components/confidentiality_badge.vue b/app/assets/javascripts/vue_shared/components/confidentiality_badge.vue
new file mode 100644
index 00000000000..298c7bc50cc
--- /dev/null
+++ b/app/assets/javascripts/vue_shared/components/confidentiality_badge.vue
@@ -0,0 +1,39 @@
+<script>
+import { GlBadge, GlTooltipDirective } from '@gitlab/ui';
+import { confidentialityInfoText } from '../constants';
+
+export default {
+ components: {
+ GlBadge,
+ },
+ directives: {
+ GlTooltip: GlTooltipDirective,
+ },
+ props: {
+ workspaceType: {
+ type: String,
+ required: true,
+ },
+ issuableType: {
+ type: String,
+ required: true,
+ },
+ },
+ computed: {
+ confidentialTooltip() {
+ return confidentialityInfoText(this.workspaceType, this.issuableType);
+ },
+ },
+};
+</script>
+
+<template>
+ <gl-badge
+ v-gl-tooltip.bottom
+ :title="confidentialTooltip"
+ icon="eye-slash"
+ variant="warning"
+ class="gl-display-inline gl-mr-2"
+ >{{ __('Confidential') }}</gl-badge
+ >
+</template>
diff --git a/app/assets/javascripts/vue_shared/components/confirm_danger/confirm_danger_modal.stories.js b/app/assets/javascripts/vue_shared/components/confirm_danger/confirm_danger_modal.stories.js
index 6629b293eb9..8481280f25f 100644
--- a/app/assets/javascripts/vue_shared/components/confirm_danger/confirm_danger_modal.stories.js
+++ b/app/assets/javascripts/vue_shared/components/confirm_danger/confirm_danger_modal.stories.js
@@ -14,6 +14,8 @@ const Template = (args, { argTypes }) => ({
additionalInformation: args.additionalInformation || null,
confirmDangerMessage: args.confirmDangerMessage || 'You require more Vespene Gas',
htmlConfirmationMessage: args.confirmDangerMessage || false,
+ confirmButtonText: args.confirmButtonText || 'Cancel',
+ cancelButtonText: args.cancelButtonText || 'Confirm',
},
});
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 88890b3332d..37e480f7e41 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
@@ -12,7 +12,7 @@ import {
CONFIRM_DANGER_MODAL_TITLE,
CONFIRM_DANGER_PHRASE_TEXT,
CONFIRM_DANGER_WARNING,
- CONFIRM_DANGER_MODAL_ERROR,
+ CONFIRM_DANGER_MODAL_CANCEL,
} from './constants';
export default {
@@ -40,6 +40,9 @@ export default {
additionalInformation: {
default: CONFIRM_DANGER_WARNING,
},
+ cancelButtonText: {
+ default: CONFIRM_DANGER_MODAL_CANCEL,
+ },
},
props: {
modalId: {
@@ -66,6 +69,11 @@ export default {
attributes: [{ variant: 'danger', disabled: !this.isValid, class: 'qa-confirm-button' }],
};
},
+ actionCancel() {
+ return {
+ text: this.cancelButtonText,
+ };
+ },
},
methods: {
equalString(a, b) {
@@ -77,7 +85,6 @@ export default {
CONFIRM_DANGER_MODAL_TITLE,
CONFIRM_DANGER_WARNING,
CONFIRM_DANGER_PHRASE_TEXT,
- CONFIRM_DANGER_MODAL_ERROR,
},
};
</script>
@@ -88,6 +95,7 @@ export default {
:data-testid="modalId"
:title="$options.i18n.CONFIRM_DANGER_MODAL_TITLE"
:action-primary="actionPrimary"
+ :action-cancel="actionCancel"
@primary="$emit('confirm')"
>
<gl-alert
@@ -110,7 +118,7 @@ export default {
</template>
</gl-sprintf>
</p>
- <gl-form-group :state="isValid" :invalid-feedback="$options.i18n.CONFIRM_DANGER_MODAL_ERROR">
+ <gl-form-group :state="isValid">
<gl-form-input
id="confirm_name_input"
v-model="confirmationPhrase"
diff --git a/app/assets/javascripts/vue_shared/components/confirm_danger/constants.js b/app/assets/javascripts/vue_shared/components/confirm_danger/constants.js
index fa44a9be411..90d55d0f93f 100644
--- a/app/assets/javascripts/vue_shared/components/confirm_danger/constants.js
+++ b/app/assets/javascripts/vue_shared/components/confirm_danger/constants.js
@@ -2,7 +2,6 @@ import { __ } from '~/locale';
export const CONFIRM_DANGER_MODAL_ID = 'confirm-danger-modal';
export const CONFIRM_DANGER_MODAL_TITLE = __('Confirmation required');
-export const CONFIRM_DANGER_MODAL_ERROR = __('Confirmation required');
export const CONFIRM_DANGER_MODAL_BUTTON = __('Confirm');
export const CONFIRM_DANGER_WARNING = __(
'This action can lead to data loss. To prevent accidental actions we ask you to confirm your intention.',
@@ -10,3 +9,4 @@ export const CONFIRM_DANGER_WARNING = __(
export const CONFIRM_DANGER_PHRASE_TEXT = __(
'Please type %{phrase_code} to proceed or close this modal to cancel.',
);
+export const CONFIRM_DANGER_MODAL_CANCEL = __('Cancel');
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 e546ca57c5e..181c1b89e31 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
@@ -248,7 +248,7 @@ export default {
__('Cancel')
}}</gl-button>
<gl-button
- variant="success"
+ variant="confirm"
category="primary"
:disabled="!isValid"
@click="setFixedRange()"
diff --git a/app/assets/javascripts/vue_shared/components/deployment_instance.vue b/app/assets/javascripts/vue_shared/components/deployment_instance.vue
index 41b783aa011..4aae86fc82b 100644
--- a/app/assets/javascripts/vue_shared/components/deployment_instance.vue
+++ b/app/assets/javascripts/vue_shared/components/deployment_instance.vue
@@ -14,6 +14,7 @@
*/
import { GlLink, GlTooltipDirective } from '@gitlab/ui';
import { mergeUrlParams } from '~/lib/utils/url_utility';
+import glFeatureFlagsMixin from '~/vue_shared/mixins/gl_feature_flags_mixin';
export default {
components: {
@@ -22,7 +23,7 @@ export default {
directives: {
GlTooltip: GlTooltipDirective,
},
-
+ mixins: [glFeatureFlagsMixin()],
props: {
/**
* Represents the status of the pod. Each state is represented with a different
@@ -75,7 +76,9 @@ export default {
},
computedLogPath() {
- return this.isLink ? mergeUrlParams({ pod_name: this.podName }, this.logsPath) : null;
+ return this.isLink && this.glFeatures.monitorLogging
+ ? mergeUrlParams({ pod_name: this.podName }, this.logsPath)
+ : null;
},
},
};
diff --git a/app/assets/javascripts/vue_shared/components/dropdown/dropdown_hidden_input.vue b/app/assets/javascripts/vue_shared/components/dropdown/dropdown_hidden_input.vue
deleted file mode 100644
index afde0c81580..00000000000
--- a/app/assets/javascripts/vue_shared/components/dropdown/dropdown_hidden_input.vue
+++ /dev/null
@@ -1,18 +0,0 @@
-<script>
-export default {
- props: {
- name: {
- type: String,
- required: true,
- },
- value: {
- type: [Number, String],
- required: true,
- },
- },
-};
-</script>
-
-<template>
- <input :name="name" :value="value" type="hidden" />
-</template>
diff --git a/app/assets/javascripts/vue_shared/components/dropdown/dropdown_search_input.vue b/app/assets/javascripts/vue_shared/components/dropdown/dropdown_search_input.vue
deleted file mode 100644
index edb5ffdc39c..00000000000
--- a/app/assets/javascripts/vue_shared/components/dropdown/dropdown_search_input.vue
+++ /dev/null
@@ -1,49 +0,0 @@
-<script>
-import { GlIcon } from '@gitlab/ui';
-import { __ } from '~/locale';
-
-export default {
- components: {
- GlIcon,
- },
- props: {
- placeholderText: {
- type: String,
- required: true,
- default: __('Search'),
- },
- focused: {
- type: Boolean,
- required: false,
- default: false,
- },
- },
- data() {
- return { searchQuery: this.value };
- },
- watch: {
- searchQuery(query) {
- this.$emit('input', query);
- },
- focused(val) {
- if (val) {
- this.$refs.searchInput.focus();
- }
- },
- },
-};
-</script>
-
-<template>
- <div class="dropdown-input">
- <input
- ref="searchInput"
- v-model="searchQuery"
- :placeholder="placeholderText"
- class="dropdown-input-field"
- type="search"
- autocomplete="off"
- />
- <gl-icon name="search" class="dropdown-input-search" data-hidden="true" />
- </div>
-</template>
diff --git a/app/assets/javascripts/vue_shared/components/dropdown/dropdown_widget/dropdown_widget.vue b/app/assets/javascripts/vue_shared/components/dropdown/dropdown_widget/dropdown_widget.vue
index 2a79ccc2648..840911dc99c 100644
--- a/app/assets/javascripts/vue_shared/components/dropdown/dropdown_widget/dropdown_widget.vue
+++ b/app/assets/javascripts/vue_shared/components/dropdown/dropdown_widget/dropdown_widget.vue
@@ -1,28 +1,22 @@
<script>
import {
- GlIcon,
GlLoadingIcon,
GlDropdown,
GlDropdownForm,
GlDropdownDivider,
GlDropdownItem,
- GlDropdownSectionHeader,
GlSearchBoxByType,
} from '@gitlab/ui';
import { __ } from '~/locale';
-import TooltipOnTruncate from '~/vue_shared/components/tooltip_on_truncate/tooltip_on_truncate.vue';
export default {
components: {
- GlIcon,
GlLoadingIcon,
GlDropdown,
GlDropdownForm,
GlDropdownDivider,
GlDropdownItem,
- GlDropdownSectionHeader,
GlSearchBoxByType,
- TooltipOnTruncate,
},
props: {
selectText: {
@@ -45,11 +39,6 @@ export default {
required: false,
default: () => [],
},
- groupedOptions: {
- type: Array,
- required: false,
- default: () => [],
- },
isLoading: {
type: Boolean,
required: false,
@@ -70,6 +59,11 @@ export default {
required: false,
default: false,
},
+ customIsSelectedOption: {
+ type: Function,
+ required: false,
+ default: undefined,
+ },
},
computed: {
isSearchEmpty() {
@@ -87,6 +81,9 @@ export default {
}
},
isSelected(option) {
+ if (this.customIsSelectedOption !== undefined) {
+ return this.customIsSelectedOption(option);
+ }
if (Array.isArray(this.selected)) {
return this.selected.some((label) => label.title === option.title);
}
@@ -143,7 +140,7 @@ export default {
<gl-dropdown-form class="gl-relative gl-min-h-7" data-qa-selector="labels_dropdown_content">
<gl-loading-icon
v-if="isLoading"
- size="md"
+ size="lg"
class="gl-absolute gl-left-0 gl-top-0 gl-right-0"
/>
<template v-else>
@@ -177,36 +174,7 @@ export default {
{{ option.title }}
</slot>
</gl-dropdown-item>
- <template v-for="(optionGroup, index) in groupedOptions">
- <gl-dropdown-divider v-if="index !== 0" :key="index" />
- <gl-dropdown-section-header :key="optionGroup.id">
- <div class="gl-display-flex gl-max-w-full">
- <tooltip-on-truncate
- :title="optionGroup.title"
- class="gl-text-truncate gl-flex-grow-1"
- >
- {{ optionGroup.title }}
- </tooltip-on-truncate>
- <span v-if="optionGroup.secondaryText" class="gl-float-right gl-font-weight-normal">
- <gl-icon name="clock" class="gl-mr-2" />
- {{ optionGroup.secondaryText }}
- </span>
- </div>
- </gl-dropdown-section-header>
- <gl-dropdown-item
- v-for="option in optionGroup.options"
- :key="optionKey(option)"
- :is-checked="isSelected(option)"
- is-check-centered
- is-check-item
- data-testid="unselected-option"
- @click="selectOption(option)"
- >
- <slot name="item" :item="option">
- {{ option.title }}
- </slot>
- </gl-dropdown-item>
- </template>
+ <slot v-bind="{ isSelected }" name="grouped-options"></slot>
<gl-dropdown-item v-if="noOptionsFound" class="gl-pl-6!">
{{ $options.i18n.noMatchingResults }}
</gl-dropdown-item>
diff --git a/app/assets/javascripts/vue_shared/components/file_finder/index.vue b/app/assets/javascripts/vue_shared/components/file_finder/index.vue
index b0c1c1531aa..680f229d5e8 100644
--- a/app/assets/javascripts/vue_shared/components/file_finder/index.vue
+++ b/app/assets/javascripts/vue_shared/components/file_finder/index.vue
@@ -1,5 +1,5 @@
<script>
-import { GlIcon } from '@gitlab/ui';
+import { GlIcon, GlLoadingIcon } from '@gitlab/ui';
import fuzzaldrinPlus from 'fuzzaldrin-plus';
import Mousetrap from 'mousetrap';
import VirtualList from 'vue-virtual-scroll-list';
@@ -9,13 +9,13 @@ import Item from './item.vue';
export const MAX_FILE_FINDER_RESULTS = 40;
export const FILE_FINDER_ROW_HEIGHT = 55;
-export const FILE_FINDER_EMPTY_ROW_HEIGHT = 33;
const originalStopCallback = Mousetrap.prototype.stopCallback;
export default {
components: {
GlIcon,
+ GlLoadingIcon,
Item,
VirtualList,
},
@@ -71,7 +71,7 @@ export default {
return this.filteredBlobsLength ? Math.min(this.filteredBlobsLength, 5) : 1;
},
listHeight() {
- return this.filteredBlobsLength ? FILE_FINDER_ROW_HEIGHT : FILE_FINDER_EMPTY_ROW_HEIGHT;
+ return FILE_FINDER_ROW_HEIGHT;
},
showClearInputButton() {
return this.searchText.trim() !== '';
@@ -265,9 +265,9 @@ export default {
</li>
</template>
<li v-else class="dropdown-menu-empty-item">
- <div class="gl-mr-3 gl-ml-3 gl-mt-3 gl-mb-3">
+ <div class="gl-mr-3 gl-ml-3 gl-mt-5 gl-mb-3">
<template v-if="loading">
- {{ __('Loading...') }}
+ <gl-loading-icon />
</template>
<template v-else>
{{ __('No files found.') }}
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 3d48c74b40b..d7a84798e47 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
@@ -1,6 +1,6 @@
import { __ } from '~/locale';
-export const DEBOUNCE_DELAY = 200;
+export const DEBOUNCE_DELAY = 500;
export const MAX_RECENT_TOKENS_SIZE = 3;
export const FILTER_NONE = 'None';
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 6638a5de62f..33d507dad57 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
@@ -89,32 +89,20 @@ export default {
required: false,
default: () => ({}),
},
+ syncFilterAndSort: {
+ type: Boolean,
+ required: false,
+ default: false,
+ },
},
data() {
- let selectedSortOption = this.sortOptions[0]?.sortDirection?.descending;
- let selectedSortDirection = SortDirection.descending;
-
- // Extract correct sortBy value based on initialSortBy
- if (this.initialSortBy) {
- selectedSortOption = this.sortOptions
- .filter(
- (sortBy) =>
- sortBy.sortDirection.ascending === this.initialSortBy ||
- sortBy.sortDirection.descending === this.initialSortBy,
- )
- .pop();
- selectedSortDirection = Object.keys(selectedSortOption.sortDirection).find(
- (key) => selectedSortOption.sortDirection[key] === this.initialSortBy,
- );
- }
-
return {
initialRender: true,
recentSearchesPromise: null,
recentSearches: [],
filterValue: this.initialFilterValue,
- selectedSortOption,
- selectedSortDirection,
+ selectedSortOption: this.sortOptions[0],
+ selectedSortDirection: SortDirection.descending,
};
},
computed: {
@@ -173,7 +161,20 @@ export default {
return undefined;
},
},
+ watch: {
+ initialFilterValue(newValue) {
+ if (this.syncFilterAndSort) {
+ this.filterValue = newValue;
+ }
+ },
+ initialSortBy(newValue) {
+ if (this.syncFilterAndSort) {
+ this.updateSelectedSortValues(newValue);
+ }
+ },
+ },
created() {
+ this.updateSelectedSortValues(this.initialSortBy);
if (this.recentSearchesStorageKey) this.setupRecentSearch();
},
methods: {
@@ -309,12 +310,25 @@ export default {
const cleared = true;
this.$emit('onFilter', [], cleared);
},
+ updateSelectedSortValues(sort) {
+ if (!sort) {
+ return;
+ }
+
+ this.selectedSortOption = this.sortOptions.find(
+ (sortBy) =>
+ sortBy.sortDirection.ascending === sort || sortBy.sortDirection.descending === sort,
+ );
+ this.selectedSortDirection = Object.keys(this.selectedSortOption.sortDirection).find(
+ (key) => this.selectedSortOption.sortDirection[key] === sort,
+ );
+ },
},
};
</script>
<template>
- <div class="vue-filtered-search-bar-container d-md-flex">
+ <div class="vue-filtered-search-bar-container gl-md-display-flex">
<gl-form-checkbox
v-if="showCheckbox"
class="gl-align-self-center"
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/author_token.vue
index 696456be990..848c49c48c7 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/author_token.vue
@@ -87,6 +87,7 @@ export default {
:get-active-token-value="getActiveAuthor"
:default-suggestions="defaultAuthors"
:preloaded-suggestions="preloadedAuthors"
+ v-bind="$attrs"
@fetch-suggestions="fetchAuthors"
v-on="$listeners"
>
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 e7923e0b55e..c3a0a97a7ba 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
@@ -211,10 +211,22 @@ export default {
@select="handleTokenValueSelected"
>
<template #view-token="viewTokenProps">
- <slot name="view-token" :view-token-props="{ ...viewTokenProps, activeTokenValue }"></slot>
+ <slot
+ name="view-token"
+ :view-token-props="/* eslint-disable @gitlab/vue-no-new-non-primitive-in-template */ {
+ ...viewTokenProps,
+ activeTokenValue,
+ } /* eslint-enable @gitlab/vue-no-new-non-primitive-in-template */"
+ ></slot>
</template>
<template #view="viewTokenProps">
- <slot name="view" :view-token-props="{ ...viewTokenProps, activeTokenValue }"></slot>
+ <slot
+ name="view"
+ :view-token-props="/* eslint-disable @gitlab/vue-no-new-non-primitive-in-template */ {
+ ...viewTokenProps,
+ activeTokenValue,
+ } /* eslint-enable @gitlab/vue-no-new-non-primitive-in-template */"
+ ></slot>
</template>
<template v-if="suggestionsEnabled" #suggestions>
<template v-if="showDefaultSuggestions">
diff --git a/app/assets/javascripts/vue_shared/components/filtered_search_bar/tokens/branch_token.vue b/app/assets/javascripts/vue_shared/components/filtered_search_bar/tokens/branch_token.vue
index 4ecfc1cf40c..aa5161ca93c 100644
--- a/app/assets/javascripts/vue_shared/components/filtered_search_bar/tokens/branch_token.vue
+++ b/app/assets/javascripts/vue_shared/components/filtered_search_bar/tokens/branch_token.vue
@@ -65,6 +65,7 @@ export default {
:suggestions="branches"
:suggestions-loading="loading"
:get-active-token-value="getActiveBranch"
+ v-bind="$attrs"
@fetch-suggestions="fetchBranches"
v-on="$listeners"
>
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 5a69751a2cc..210d814d22a 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
@@ -67,6 +67,7 @@ export default {
:suggestions="emojis"
:suggestions-loading="loading"
:get-active-token-value="getActiveEmoji"
+ v-bind="$attrs"
@fetch-suggestions="fetchEmojis"
v-on="$listeners"
>
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 3f7a8920f48..6f24955814c 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
@@ -104,6 +104,7 @@ export default {
:suggestions="labels"
:get-active-token-value="getActiveLabel"
:default-suggestions="defaultLabels"
+ v-bind="$attrs"
@fetch-suggestions="fetchLabels"
v-on="$listeners"
>
diff --git a/app/assets/javascripts/vue_shared/components/filtered_search_bar/tokens/milestone_token.vue b/app/assets/javascripts/vue_shared/components/filtered_search_bar/tokens/milestone_token.vue
index 11c081ab4f8..69265d0fdc9 100644
--- a/app/assets/javascripts/vue_shared/components/filtered_search_bar/tokens/milestone_token.vue
+++ b/app/assets/javascripts/vue_shared/components/filtered_search_bar/tokens/milestone_token.vue
@@ -84,6 +84,7 @@ export default {
:suggestions="milestones"
:suggestions-loading="loading"
:get-active-token-value="getActiveMilestone"
+ v-bind="$attrs"
@fetch-suggestions="fetchMilestones"
v-on="$listeners"
>
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 f353cc3a765..9e68c92af5d 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
@@ -66,6 +66,7 @@ export default {
:suggestions="releases"
:suggestions-loading="loading"
:get-active-token-value="getActiveRelease"
+ v-bind="$attrs"
@fetch-suggestions="fetchReleases"
v-on="$listeners"
>
diff --git a/app/assets/javascripts/vue_shared/components/markdown/field.vue b/app/assets/javascripts/vue_shared/components/markdown/field.vue
index 722df3cc58b..1f309a19b14 100644
--- a/app/assets/javascripts/vue_shared/components/markdown/field.vue
+++ b/app/assets/javascripts/vue_shared/components/markdown/field.vue
@@ -111,6 +111,16 @@ export default {
required: false,
default: false,
},
+ showCommentToolBar: {
+ type: Boolean,
+ required: false,
+ default: true,
+ },
+ restrictedToolBarItems: {
+ type: Array,
+ required: false,
+ default: () => [],
+ },
},
data() {
return {
@@ -331,7 +341,7 @@ export default {
:enable-preview="enablePreview"
:show-suggest-popover="showSuggestPopover"
:suggestion-start-index="suggestionsStartIndex"
- data-testid="markdownHeader"
+ :restricted-tool-bar-items="restrictedToolBarItems"
@preview-markdown="showPreviewTab"
@write-markdown="showWriteTab"
@handleSuggestDismissed="() => $emit('handleSuggestDismissed')"
@@ -350,6 +360,7 @@ export default {
:markdown-docs-path="markdownDocsPath"
:quick-actions-docs-path="quickActionsDocsPath"
:can-attach-file="canAttachFile"
+ :show-comment-tool-bar="showCommentToolBar"
/>
</div>
</div>
@@ -362,8 +373,6 @@ export default {
<suggestions
v-if="hasSuggestion"
:note-html="markdownPreview"
- :from-line="lineNumber"
- :from-content="lineContent"
:line-type="lineType"
:disabled="true"
:suggestions="suggestions"
diff --git a/app/assets/javascripts/vue_shared/components/markdown/header.vue b/app/assets/javascripts/vue_shared/components/markdown/header.vue
index d0bd5046bf0..ba2b5eaa4f9 100644
--- a/app/assets/javascripts/vue_shared/components/markdown/header.vue
+++ b/app/assets/javascripts/vue_shared/components/markdown/header.vue
@@ -54,6 +54,11 @@ export default {
required: false,
default: true,
},
+ restrictedToolBarItems: {
+ type: Array,
+ required: false,
+ default: () => [],
+ },
},
data() {
return {
@@ -193,7 +198,10 @@ export default {
<toolbar-button
tag="**"
:button-title="
- sprintf(s__('MarkdownEditor|Add bold text (%{modifierKey}B)'), { modifierKey })
+ /* eslint-disable @gitlab/vue-no-new-non-primitive-in-template */
+ sprintf(s__('MarkdownEditor|Add bold text (%{modifierKey}B)'), {
+ modifierKey,
+ }) /* eslint-enable @gitlab/vue-no-new-non-primitive-in-template */
"
:shortcuts="$options.shortcuts.bold"
icon="bold"
@@ -201,22 +209,28 @@ export default {
<toolbar-button
tag="_"
:button-title="
- sprintf(s__('MarkdownEditor|Add italic text (%{modifierKey}I)'), { modifierKey })
+ /* eslint-disable @gitlab/vue-no-new-non-primitive-in-template */
+ sprintf(s__('MarkdownEditor|Add italic text (%{modifierKey}I)'), {
+ modifierKey,
+ }) /* eslint-enable @gitlab/vue-no-new-non-primitive-in-template */
"
:shortcuts="$options.shortcuts.italic"
icon="italic"
/>
<toolbar-button
+ v-if="!restrictedToolBarItems.includes('strikethrough')"
tag="~~"
:button-title="
+ /* eslint-disable @gitlab/vue-no-new-non-primitive-in-template */
sprintf(s__('MarkdownEditor|Add strikethrough text (%{modifierKey}⇧X)'), {
- modifierKey,
+ modifierKey /* eslint-enable @gitlab/vue-no-new-non-primitive-in-template */,
})
"
:shortcuts="$options.shortcuts.strikethrough"
icon="strikethrough"
/>
<toolbar-button
+ v-if="!restrictedToolBarItems.includes('quote')"
:prepend="true"
:tag="tag"
:button-title="__('Insert a quote')"
@@ -266,30 +280,37 @@ export default {
tag="[{text}](url)"
tag-select="url"
:button-title="
- sprintf(s__('MarkdownEditor|Add a link (%{modifierKey}K)'), { modifierKey })
+ /* eslint-disable @gitlab/vue-no-new-non-primitive-in-template */
+ sprintf(s__('MarkdownEditor|Add a link (%{modifierKey}K)'), {
+ modifierKey,
+ }) /* eslint-enable @gitlab/vue-no-new-non-primitive-in-template */
"
:shortcuts="$options.shortcuts.link"
icon="link"
/>
<toolbar-button
+ v-if="!restrictedToolBarItems.includes('bullet-list')"
:prepend="true"
tag="- "
:button-title="__('Add a bullet list')"
icon="list-bulleted"
/>
<toolbar-button
+ v-if="!restrictedToolBarItems.includes('numbered-list')"
:prepend="true"
tag="1. "
:button-title="__('Add a numbered list')"
icon="list-numbered"
/>
<toolbar-button
+ v-if="!restrictedToolBarItems.includes('task-list')"
:prepend="true"
tag="- [ ] "
:button-title="__('Add a task list')"
icon="list-task"
/>
<toolbar-button
+ v-if="!restrictedToolBarItems.includes('collapsible-section')"
:tag="mdCollapsibleSection"
:prepend="true"
tag-select="Click to expand"
@@ -297,12 +318,14 @@ export default {
icon="details-block"
/>
<toolbar-button
+ v-if="!restrictedToolBarItems.includes('table')"
:tag="mdTable"
:prepend="true"
:button-title="__('Add a table')"
icon="table"
/>
<toolbar-button
+ v-if="!restrictedToolBarItems.includes('full-screen')"
class="js-zen-enter"
:prepend="true"
:button-title="__('Go full screen')"
diff --git a/app/assets/javascripts/vue_shared/components/markdown/toolbar.vue b/app/assets/javascripts/vue_shared/components/markdown/toolbar.vue
index f1c293c87f4..6c99a749edc 100644
--- a/app/assets/javascripts/vue_shared/components/markdown/toolbar.vue
+++ b/app/assets/javascripts/vue_shared/components/markdown/toolbar.vue
@@ -24,6 +24,11 @@ export default {
required: false,
default: true,
},
+ showCommentToolBar: {
+ type: Boolean,
+ required: false,
+ default: true,
+ },
},
computed: {
hasQuickActionsDocsPath() {
@@ -34,24 +39,33 @@ export default {
</script>
<template>
- <div class="comment-toolbar clearfix">
+ <div v-if="showCommentToolBar" class="comment-toolbar clearfix">
<div class="toolbar-text">
<template v-if="!hasQuickActionsDocsPath && markdownDocsPath">
- <gl-link :href="markdownDocsPath" target="_blank">
- {{ __('Markdown is supported') }}
- </gl-link>
+ <gl-sprintf
+ :message="
+ s__('MarkdownToolbar|Supports %{markdownDocsLinkStart}Markdown%{markdownDocsLinkEnd}')
+ "
+ >
+ <template #markdownDocsLink="{ content }">
+ <gl-link :href="markdownDocsPath" target="_blank">{{ content }}</gl-link>
+ </template>
+ </gl-sprintf>
</template>
<template v-if="hasQuickActionsDocsPath && markdownDocsPath">
<gl-sprintf
:message="
- __(
- '%{markdownDocsLinkStart}Markdown%{markdownDocsLinkEnd} and %{quickActionsDocsLinkStart}quick actions%{quickActionsDocsLinkEnd} are supported',
+ s__(
+ 'NoteToolbar|Supports %{markdownDocsLinkStart}Markdown%{markdownDocsLinkEnd}. For %{quickActionsDocsLinkStart}quick actions%{quickActionsDocsLinkEnd}, type %{keyboardStart}/%{keyboardEnd}.',
)
"
>
<template #markdownDocsLink="{ content }">
<gl-link :href="markdownDocsPath" target="_blank">{{ content }}</gl-link>
</template>
+ <template #keyboard="{ content }">
+ <kbd>{{ content }}</kbd>
+ </template>
<template #quickActionsDocsLink="{ content }">
<gl-link :href="quickActionsDocsPath" target="_blank">{{ content }}</gl-link>
</template>
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 3e796a73f72..e23721da223 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
@@ -90,7 +90,9 @@ export default {
modal-id="upload-metric-modal"
size="sm"
:action-primary="actionPrimaryProps"
- :action-cancel="{ text: $options.i18n.modalCancel }"
+ :action-cancel="/* eslint-disable @gitlab/vue-no-new-non-primitive-in-template */ {
+ text: $options.i18n.modalCancel,
+ } /* eslint-enable @gitlab/vue-no-new-non-primitive-in-template */"
:title="$options.i18n.modalTitle"
:visible="modalVisible"
@hidden="clearInputs"
diff --git a/app/assets/javascripts/vue_shared/components/metric_images/metric_images_table.vue b/app/assets/javascripts/vue_shared/components/metric_images/metric_images_table.vue
index 8eb8e52728d..bbbaaeb8a9e 100644
--- a/app/assets/javascripts/vue_shared/components/metric_images/metric_images_table.vue
+++ b/app/assets/javascripts/vue_shared/components/metric_images/metric_images_table.vue
@@ -159,7 +159,9 @@ export default {
size="sm"
:visible="modalVisible"
:action-primary="deleteActionPrimaryProps"
- :action-cancel="{ text: $options.i18n.modalCancel }"
+ :action-cancel="/* eslint-disable @gitlab/vue-no-new-non-primitive-in-template */ {
+ text: $options.i18n.modalCancel,
+ } /* eslint-enable @gitlab/vue-no-new-non-primitive-in-template */"
@primary.prevent="onDelete"
@hidden="resetEditFields"
>
@@ -177,7 +179,9 @@ export default {
modal-id="edit-metric-modal"
size="sm"
:action-primary="updateActionPrimaryProps"
- :action-cancel="{ text: $options.i18n.modalCancel }"
+ :action-cancel="/* eslint-disable @gitlab/vue-no-new-non-primitive-in-template */ {
+ text: $options.i18n.modalCancel,
+ } /* eslint-enable @gitlab/vue-no-new-non-primitive-in-template */"
:visible="editModalVisible"
data-testid="metric-image-edit-modal"
@hidden="resetEditFields"
diff --git a/app/assets/javascripts/vue_shared/components/navigation_tabs.vue b/app/assets/javascripts/vue_shared/components/navigation_tabs.vue
index a069d1cd756..21212e82de4 100644
--- a/app/assets/javascripts/vue_shared/components/navigation_tabs.vue
+++ b/app/assets/javascripts/vue_shared/components/navigation_tabs.vue
@@ -61,7 +61,9 @@ export default {
v-for="(tab, i) in tabs"
:key="i"
:title-link-class="`js-${scope}-tab-${tab.scope} gl-display-inline-flex`"
- :title-link-attributes="{ 'data-testid': `${scope}-tab-${tab.scope}` }"
+ :title-link-attributes="/* eslint-disable @gitlab/vue-no-new-non-primitive-in-template */ {
+ 'data-testid': `${scope}-tab-${tab.scope}`,
+ } /* eslint-enable @gitlab/vue-no-new-non-primitive-in-template */"
:active="tab.isActive"
@click="onTabClick(tab)"
>
diff --git a/app/assets/javascripts/vue_shared/components/paginated_list.vue b/app/assets/javascripts/vue_shared/components/paginated_list.vue
index e19b8510399..ddc7a457b98 100644
--- a/app/assets/javascripts/vue_shared/components/paginated_list.vue
+++ b/app/assets/javascripts/vue_shared/components/paginated_list.vue
@@ -29,7 +29,7 @@ export default {
</template>
<template #default="{ listItem, query }">
- <slot :listItem="listItem" :query="query"></slot>
+ <slot :list-item="listItem" :query="query"></slot>
</template>
</gl-paginated-list>
</template>
diff --git a/app/assets/javascripts/vue_shared/components/registry/list_item.vue b/app/assets/javascripts/vue_shared/components/registry/list_item.vue
index 6bb321713d5..a8b250f2041 100644
--- a/app/assets/javascripts/vue_shared/components/registry/list_item.vue
+++ b/app/assets/javascripts/vue_shared/components/registry/list_item.vue
@@ -32,7 +32,6 @@ export default {
return {
'gl-border-t-transparent': !this.first && !this.selected,
'gl-border-t-gray-100': this.first && !this.selected,
- 'gl-opacity-5': this.disabled,
'gl-border-b-gray-100': !this.selected,
'gl-bg-blue-50 gl-border-blue-200': this.selected,
};
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 767a108dde5..da68fe961a6 100644
--- a/app/assets/javascripts/vue_shared/components/registry/registry_search.vue
+++ b/app/assets/javascripts/vue_shared/components/registry/registry_search.vue
@@ -116,6 +116,7 @@ export default {
@clear="clearSearch"
/>
<gl-sorting
+ data-testid="registry-sort-dropdown"
:text="sortText"
:is-ascending="isSortAscending"
@sortDirectionChange="onDirectionChange"
diff --git a/app/assets/javascripts/vue_shared/components/runner_instructions/runner_instructions_modal.vue b/app/assets/javascripts/vue_shared/components/runner_instructions/runner_instructions_modal.vue
index d5493aa5a66..9eaaf7d1c18 100644
--- a/app/assets/javascripts/vue_shared/components/runner_instructions/runner_instructions_modal.vue
+++ b/app/assets/javascripts/vue_shared/components/runner_instructions/runner_instructions_modal.vue
@@ -50,6 +50,11 @@ export default {
required: false,
default: null,
},
+ defaultPlatformName: {
+ type: String,
+ required: false,
+ default: null,
+ },
},
apollo: {
platforms: {
@@ -64,9 +69,10 @@ export default {
});
},
result() {
- // Select first platform by default
- if (this.platforms?.[0]) {
- this.selectPlatform(this.platforms[0]);
+ if (this.platforms.length) {
+ // If it is set and available, select the defaultSelectedPlatform.
+ // Otherwise, select the first available platform
+ this.selectPlatform(this.defaultPlatform() || this.platforms[0]);
}
},
error() {
@@ -138,6 +144,14 @@ export default {
show() {
this.$refs.modal.show();
},
+ focusSelected() {
+ // By default the first platform always gets the focus, but when the `defaultPlatformName`
+ // property is present, any other platform might actually be selected.
+ this.$refs[this.selectedPlatformName]?.[0].$el.focus();
+ },
+ defaultPlatform() {
+ return this.platforms.find((platform) => platform.name === this.defaultPlatformName);
+ },
selectPlatform(platform) {
this.selectedPlatform = platform;
@@ -182,6 +196,8 @@ export default {
:title="$options.i18n.installARunner"
:action-secondary="$options.closeButton"
v-bind="$attrs"
+ v-on="$listeners"
+ @shown="focusSelected"
>
<gl-alert v-if="showAlert" variant="danger" @dismiss="toggleAlert(false)">
{{ $options.i18n.fetchError }}
@@ -203,6 +219,7 @@ export default {
<gl-button
v-for="platform in platforms"
:key="platform.name"
+ :ref="platform.name"
:selected="selectedPlatform && selectedPlatform.name === platform.name"
@click="selectPlatform(platform)"
>
diff --git a/app/assets/javascripts/vue_shared/components/segmented_control_button_group.vue b/app/assets/javascripts/vue_shared/components/segmented_control_button_group.vue
new file mode 100644
index 00000000000..f50706b6de8
--- /dev/null
+++ b/app/assets/javascripts/vue_shared/components/segmented_control_button_group.vue
@@ -0,0 +1,35 @@
+<script>
+import { GlButtonGroup, GlButton } from '@gitlab/ui';
+
+// TODO: We're planning to move this component to GitLab UI
+// https://gitlab.com/gitlab-org/gitlab-ui/-/issues/1787
+export default {
+ components: {
+ GlButtonGroup,
+ GlButton,
+ },
+ props: {
+ options: {
+ type: Array,
+ required: true,
+ },
+ value: {
+ type: [String, Number, Boolean],
+ required: true,
+ },
+ },
+};
+</script>
+<template>
+ <gl-button-group>
+ <gl-button
+ v-for="opt in options"
+ :key="opt.value"
+ :disabled="!!opt.disabled"
+ :selected="value === opt.value"
+ @click="$emit('input', opt.value)"
+ >
+ <slot name="button-content" v-bind="opt">{{ opt.text }}</slot>
+ </gl-button>
+ </gl-button-group>
+</template>
diff --git a/app/assets/javascripts/vue_shared/components/sidebar/issuable_move_dropdown.vue b/app/assets/javascripts/vue_shared/components/sidebar/issuable_move_dropdown.vue
index 12daaea8758..dfa2ca2d20c 100644
--- a/app/assets/javascripts/vue_shared/components/sidebar/issuable_move_dropdown.vue
+++ b/app/assets/javascripts/vue_shared/components/sidebar/issuable_move_dropdown.vue
@@ -175,7 +175,7 @@ export default {
:debounce="300"
/>
<div data-testid="content" class="dropdown-content">
- <gl-loading-icon v-if="projectsListLoading" size="md" class="gl-p-5" />
+ <gl-loading-icon v-if="projectsListLoading" size="lg" class="gl-p-5" />
<ul v-else>
<gl-dropdown-item
v-for="project in projects"
@@ -199,7 +199,7 @@ export default {
>
<gl-button
category="primary"
- variant="success"
+ variant="confirm"
:disabled="!Boolean(selectedProject)"
class="gl-text-center! issuable-move-button"
@click="handleMoveClick"
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
index 3ec33a653b8..2cccb8325f4 100644
--- 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
@@ -104,7 +104,7 @@ export default {
<gl-button
:disabled="disableCreate"
category="primary"
- variant="success"
+ variant="confirm"
class="float-left d-flex align-items-center"
@click="handleCreateClick"
>
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
index 623e7799493..134575b7a27 100644
--- 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
@@ -185,7 +185,7 @@ export default {
<gl-loading-icon
v-if="labelsFetchInProgress"
class="labels-fetch-loading gl-align-items-center w-100 h-100"
- size="md"
+ size="lg"
/>
<ul v-else class="list-unstyled gl-mb-0 gl-word-break-word">
<label-item
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
index 7989ad40b5a..e91a0489ef1 100644
--- 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
@@ -23,7 +23,7 @@ export default {
</script>
<template>
- <div class="hide-collapsed gl-line-height-20 gl-mb-2 gl-text-gray-900">
+ <div class="hide-collapsed gl-line-height-20 gl-mb-2 gl-text-gray-900 gl-font-weight-bold">
{{ __('Labels') }}
<template v-if="allowLabelEdit">
<gl-loading-icon v-show="labelsSelectInProgress" size="sm" inline />
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
index 88977652556..090bf9493bf 100644
--- 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
@@ -179,7 +179,7 @@ export default {
<gl-button
:disabled="disableCreate"
category="primary"
- variant="success"
+ variant="confirm"
class="gl-display-flex gl-align-items-center"
data-testid="create-button"
@click="createLabel"
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
index ae179ef93c7..f595e635f2c 100644
--- 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
@@ -147,7 +147,7 @@ export default {
<gl-loading-icon
v-if="labelsFetchInProgress"
class="labels-fetch-loading gl-align-items-center gl-w-full gl-h-full gl-mb-3"
- size="md"
+ size="lg"
/>
<template v-else>
<gl-dropdown-item
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 1b8e4bcfec6..c30ca5369ee 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
@@ -36,6 +36,9 @@ export default {
return content;
},
+ firstLineClass() {
+ return { 'gl-mt-3!': this.number === 1 };
+ },
},
methods: {
wrapBidiChar(bidiChar) {
@@ -56,10 +59,11 @@ export default {
</script>
<template>
<div class="gl-display-flex">
- <div class="line-numbers gl-pt-0! gl-pb-0! gl-absolute gl-z-index-3">
+ <div class="gl-p-0! gl-absolute gl-z-index-3 gl-border-r diff-line-num line-numbers">
<gl-link
:id="`L${number}`"
- class="file-line-num diff-line-num gl-user-select-none"
+ class="gl-user-select-none gl-ml-5 gl-pr-3 gl-shadow-none! file-line-num diff-line-num"
+ :class="firstLineClass"
:to="`#L${number}`"
:data-line-number="number"
>
@@ -68,7 +72,8 @@ export default {
</div>
<pre
- class="code highlight gl-p-0! gl-w-full gl-overflow-visible! gl-ml-11!"
+ class="gl-p-0! gl-w-full gl-overflow-visible! gl-ml-11! gl-border-none! code highlight"
+ :class="firstLineClass"
><code><span :id="`LC${number}`" v-safe-html="formattedContent" :lang="language" class="line" data-testid="content"></span></code></pre>
</div>
</template>
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 edf2229a9a1..ed87a202b15 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
@@ -2,6 +2,7 @@
import { GlSafeHtmlDirective, 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';
import { ROUGE_TO_HLJS_LANGUAGE_MAP, LINES_PER_CHUNK } from './constants';
import Chunk from './components/chunk.vue';
@@ -129,7 +130,7 @@ export default {
let languageDefinition;
try {
- languageDefinition = await import(`highlight.js/lib/languages/${this.language}`);
+ languageDefinition = await languageLoader[this.language]();
this.hljs.registerLanguage(this.language, languageDefinition.default);
} catch (message) {
this.$emit('error', message);
diff --git a/app/assets/javascripts/vue_shared/components/time_ago_tooltip.vue b/app/assets/javascripts/vue_shared/components/time_ago_tooltip.vue
index e784bba6698..0d466df1b7f 100644
--- a/app/assets/javascripts/vue_shared/components/time_ago_tooltip.vue
+++ b/app/assets/javascripts/vue_shared/components/time_ago_tooltip.vue
@@ -42,6 +42,6 @@ export default {
:class="cssClass"
:title="tooltipTitle(time)"
:datetime="time"
- ><slot :timeAgo="timeAgo">{{ timeAgo }}</slot></time
+ ><slot :time-ago="timeAgo">{{ timeAgo }}</slot></time
>
</template>
diff --git a/app/assets/javascripts/vue_shared/components/upload_dropzone/upload_dropzone.vue b/app/assets/javascripts/vue_shared/components/upload_dropzone/upload_dropzone.vue
index 62de76e46b5..f62bf686f85 100644
--- a/app/assets/javascripts/vue_shared/components/upload_dropzone/upload_dropzone.vue
+++ b/app/assets/javascripts/vue_shared/components/upload_dropzone/upload_dropzone.vue
@@ -160,7 +160,7 @@ export default {
>
<gl-icon name="upload" :size="iconStyles.size" :class="iconStyles.class" />
<p class="gl-mb-0" data-testid="upload-text">
- <slot name="upload-text" :openFileUpload="openFileUpload">
+ <slot name="upload-text" :open-file-upload="openFileUpload">
<gl-sprintf
:message="
singleFileSelection
diff --git a/app/assets/javascripts/vue_shared/components/usage_quotas/usage_banner.vue b/app/assets/javascripts/vue_shared/components/usage_quotas/usage_banner.vue
new file mode 100644
index 00000000000..bc5e0cf10dd
--- /dev/null
+++ b/app/assets/javascripts/vue_shared/components/usage_quotas/usage_banner.vue
@@ -0,0 +1,68 @@
+<script>
+import { GlSkeletonLoader } from '@gitlab/ui';
+import { helpPagePath } from '~/helpers/help_page_helper';
+import { s__ } from '~/locale';
+
+export default {
+ name: 'UsageBanner',
+ components: {
+ GlSkeletonLoader,
+ },
+ props: {
+ loading: {
+ type: Boolean,
+ required: false,
+ default: false,
+ },
+ },
+ i18n: {
+ dependencyProxy: s__('UsageQuota|Dependency proxy'),
+ storageUsed: s__('UsageQuota|Storage used'),
+ dependencyProxyMessage: s__(
+ 'UsageQuota|Local proxy used for frequently-accessed upstream Docker images. %{linkStart}More information%{linkEnd}',
+ ),
+ },
+ storageUsageQuotaHelpPage: helpPagePath('user/usage_quotas'),
+};
+</script>
+<template>
+ <div class="gl-display-flex gl-flex-direction-column">
+ <div class="gl-display-flex gl-align-items-center gl-py-3">
+ <div
+ class="gl-display-flex gl-xs-flex-direction-column gl-justify-content-space-between gl-align-items-stretch gl-flex-grow-1"
+ >
+ <div class="gl-display-flex gl-flex-direction-column gl-xs-mb-3 gl-min-w-0 gl-flex-grow-1">
+ <div
+ v-if="$slots['left-primary-text']"
+ class="gl-display-flex gl-align-items-center gl-text-body gl-font-weight-bold gl-min-h-6 gl-min-w-0 gl-mb-4"
+ >
+ <slot name="left-primary-text"></slot>
+ </div>
+ <div
+ v-if="$slots['left-secondary-text']"
+ class="gl-display-flex gl-align-items-center gl-text-gray-500 gl-min-h-6 gl-min-w-0 gl-flex-grow-1 gl-w-70p gl-md-max-w-70p"
+ >
+ <slot name="left-secondary-text"></slot>
+ </div>
+ </div>
+ <div
+ class="gl-display-flex gl-flex-direction-column gl-sm-align-items-flex-end gl-justify-content-space-between gl-text-gray-500 gl-flex-shrink-0"
+ >
+ <div
+ v-if="$slots['right-primary-text']"
+ class="gl-display-flex gl-align-items-center gl-sm-text-body gl-sm-font-weight-bold gl-min-h-6"
+ >
+ <slot name="right-primary-text"></slot>
+ </div>
+ <div
+ v-if="$slots['right-secondary-text']"
+ class="gl-display-flex gl-align-items-center gl-min-h-6"
+ >
+ <slot v-if="!loading" name="right-secondary-text"></slot>
+ <gl-skeleton-loader v-else :width="60" :lines="1" />
+ </div>
+ </div>
+ </div>
+ </div>
+ </div>
+</template>
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 cac8f0a9aa5..ec7a7cd72ae 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
@@ -6,9 +6,13 @@ import {
GlIcon,
GlSafeHtmlDirective,
GlSprintf,
+ GlButton,
} from '@gitlab/ui';
+import { __ } from '~/locale';
import UserNameWithStatus from '~/sidebar/components/assignees/user_name_with_status.vue';
import { glEmojiTag } from '~/emoji';
+import createFlash from '~/flash';
+import { followUser, unfollowUser } from '~/rest_api';
import UserAvatarImage from '../user_avatar/user_avatar_image.vue';
const MAX_SKELETON_LINES = 4;
@@ -24,6 +28,7 @@ export default {
UserAvatarImage,
UserNameWithStatus,
GlSprintf,
+ GlButton,
},
directives: {
SafeHtml: GlSafeHtmlDirective,
@@ -38,6 +43,16 @@ export default {
required: true,
default: null,
},
+ placement: {
+ type: String,
+ required: false,
+ default: 'top',
+ },
+ },
+ data() {
+ return {
+ toggleFollowLoading: false,
+ };
},
computed: {
statusHtml() {
@@ -59,6 +74,59 @@ export default {
availabilityStatus() {
return this.user?.status?.availability || '';
},
+ isNotCurrentUser() {
+ return !this.userIsLoading && this.user.username !== gon.current_username;
+ },
+ shouldRenderToggleFollowButton() {
+ return this.isNotCurrentUser && typeof this.user?.isFollowed !== 'undefined';
+ },
+ toggleFollowButtonText() {
+ if (this.toggleFollowLoading) return null;
+
+ return this.user?.isFollowed ? __('Unfollow') : __('Follow');
+ },
+ toggleFollowButtonVariant() {
+ return this.user?.isFollowed ? 'default' : 'confirm';
+ },
+ },
+ methods: {
+ async toggleFollow() {
+ if (this.user.isFollowed) {
+ this.unfollow();
+ } else {
+ this.follow();
+ }
+ },
+ async follow() {
+ this.toggleFollowLoading = true;
+ try {
+ await followUser(this.user.id);
+ this.$emit('follow');
+ } catch (error) {
+ createFlash({
+ message: __('An error occurred while trying to follow this user, please try again.'),
+ error,
+ captureError: true,
+ });
+ } finally {
+ this.toggleFollowLoading = false;
+ }
+ },
+ async unfollow() {
+ this.toggleFollowLoading = true;
+ try {
+ await unfollowUser(this.user.id);
+ this.$emit('unfollow');
+ } catch (error) {
+ createFlash({
+ message: __('An error occurred while trying to unfollow this user, please try again.'),
+ error,
+ captureError: true,
+ });
+ } finally {
+ this.toggleFollowLoading = false;
+ }
+ },
},
safeHtmlConfig: { ADD_TAGS: ['gl-emoji'] },
};
@@ -66,12 +134,24 @@ export default {
<template>
<!-- 200ms delay so not every mouseover triggers Popover -->
- <gl-popover :target="target" :delay="200" boundary="viewport" placement="top">
+ <gl-popover :target="target" :delay="200" :placement="placement" boundary="viewport">
<div class="gl-p-3 gl-line-height-normal gl-display-flex" data-testid="user-popover">
- <div class="gl-p-2 flex-shrink-1">
- <user-avatar-image :img-src="user.avatarUrl" :size="64" css-classes="gl-mr-3!" />
+ <div
+ class="gl-p-2 flex-shrink-1 gl-display-flex gl-flex-direction-column align-items-center gl-w-70p"
+ >
+ <user-avatar-image :img-src="user.avatarUrl" :size="64" css-classes="gl-m-0!" />
+ <div v-if="shouldRenderToggleFollowButton" class="gl-mt-3">
+ <gl-button
+ :variant="toggleFollowButtonVariant"
+ :loading="toggleFollowLoading"
+ size="small"
+ data-testid="toggle-follow-button"
+ @click="toggleFollow"
+ >{{ toggleFollowButtonText }}</gl-button
+ >
+ </div>
</div>
- <div class="gl-p-2 gl-w-full gl-min-w-0">
+ <div class="gl-w-full gl-min-w-0 gl-word-break-word">
<template v-if="userIsLoading">
<gl-skeleton-loader
:lines="$options.maxSkeletonLines"
@@ -94,7 +174,7 @@ export default {
<div class="gl-text-gray-500">
<div v-if="user.bio" class="gl-display-flex gl-mb-2">
<gl-icon name="profile" class="gl-flex-shrink-0" />
- <span ref="bio" class="gl-ml-2 gl-overflow-hidden">{{ user.bio }}</span>
+ <span ref="bio" class="gl-ml-2">{{ user.bio }}</span>
</div>
<div v-if="user.workInformation" class="gl-display-flex gl-mb-2">
<gl-icon name="work" class="gl-flex-shrink-0" />
diff --git a/app/assets/javascripts/vue_shared/components/user_select/user_select.vue b/app/assets/javascripts/vue_shared/components/user_select/user_select.vue
index 9df5254155e..91f20863089 100644
--- a/app/assets/javascripts/vue_shared/components/user_select/user_select.vue
+++ b/app/assets/javascripts/vue_shared/components/user_select/user_select.vue
@@ -298,7 +298,7 @@ export default {
<gl-loading-icon
v-if="isLoading"
data-testid="loading-participants"
- size="md"
+ size="lg"
class="gl-absolute gl-left-0 gl-top-0 gl-right-0"
/>
<template v-else>
diff --git a/app/assets/javascripts/vue_shared/constants.js b/app/assets/javascripts/vue_shared/constants.js
index 9cb66f6e65f..3ebeec4a50b 100644
--- a/app/assets/javascripts/vue_shared/constants.js
+++ b/app/assets/javascripts/vue_shared/constants.js
@@ -1,4 +1,5 @@
-import { __ } from '~/locale';
+import { __, sprintf } from '~/locale';
+import { IssuableType, WorkspaceType } from '~/issues/constants';
const INTERVALS = {
minute: 'minute',
@@ -66,3 +67,14 @@ export const getTimeWindow = (timeWindowName) =>
export const AVATAR_SHAPE_OPTION_CIRCLE = 'circle';
export const AVATAR_SHAPE_OPTION_RECT = 'rect';
+
+export const confidentialityInfoText = (workspaceType, issuableType) =>
+ sprintf(
+ __(
+ 'Only %{workspaceType} members with at least Reporter role can view or be notified about this %{issuableType}.',
+ ),
+ {
+ workspaceType: workspaceType === WorkspaceType.project ? __('project') : __('group'),
+ issuableType: issuableType === IssuableType.Issue ? __('issue') : __('epic'),
+ },
+ );
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 c216a05bdb0..0758cb507e9 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,5 +1,5 @@
<script>
-import { GlForm, GlFormInput } from '@gitlab/ui';
+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';
@@ -9,6 +9,7 @@ export default {
components: {
GlForm,
GlFormInput,
+ GlFormGroup,
MarkdownField,
LabelsSelect,
},
@@ -37,6 +38,7 @@ export default {
selectedLabels: [],
};
},
+ computed: {},
methods: {
handleUpdateSelectedLabels(labels) {
if (labels.length) {
@@ -52,12 +54,15 @@ export default {
<div data-testid="issuable-title" class="form-group row">
<label for="issuable-title" class="col-form-label col-sm-2">{{ __('Title') }}</label>
<div class="col-sm-10">
- <gl-form-input
- id="issuable-title"
- v-model="issuableTitle"
- :autofocus="true"
- :placeholder="__('Title')"
- />
+ <gl-form-group :description="__('Maximum of 255 characters')">
+ <gl-form-input
+ id="issuable-title"
+ v-model="issuableTitle"
+ maxlength="255"
+ :autofocus="true"
+ :placeholder="__('Title')"
+ />
+ </gl-form-group>
</div>
</div>
<div data-testid="issuable-description" class="form-group row">
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 8008b85bbdb..6453290f6ea 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
@@ -55,7 +55,7 @@ export default {
return createdSecondsAgo < SECONDS_IN_DAY;
},
author() {
- return this.issuable.author;
+ return this.issuable.author || {};
},
webUrl() {
return this.issuable.gitlabWebUrl || this.issuable.webUrl;
@@ -215,7 +215,7 @@ export default {
<span class="gl-display-none gl-sm-display-inline">
<span aria-hidden="true">&middot;</span>
<span class="issuable-authored gl-mr-3">
- <gl-sprintf :message="__('created %{timeAgo} by %{author}')">
+ <gl-sprintf v-if="author.name" :message="__('created %{timeAgo} by %{author}')">
<template #timeAgo>
<span
v-gl-tooltip.bottom
@@ -241,6 +241,17 @@ export default {
</gl-link>
</template>
</gl-sprintf>
+ <gl-sprintf v-else :message="__('created %{timeAgo}')">
+ <template #timeAgo>
+ <span
+ v-gl-tooltip.bottom
+ :title="tooltipTitle(issuable.createdAt)"
+ data-testid="issuable-created-at"
+ >
+ {{ createdAt }}
+ </span>
+ </template>
+ </gl-sprintf>
</span>
<slot name="timeframe"></slot>
</span>
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 20f178dfb7d..8b293b2e9f6 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
@@ -168,6 +168,11 @@ export default {
required: false,
default: '',
},
+ syncFilterAndSort: {
+ type: Boolean,
+ required: false,
+ default: false,
+ },
},
data() {
return {
@@ -282,6 +287,7 @@ export default {
:sort-options="sortOptions"
:initial-filter-value="initialFilterValue"
:initial-sort-by="initialSortBy"
+ :sync-filter-and-sort="syncFilterAndSort"
:show-checkbox="showBulkEditSidebar"
:checkbox-checked="allIssuablesChecked"
class="gl-flex-grow-1 gl-border-t-none row-content-block"
diff --git a/app/assets/javascripts/vue_shared/issuable/show/components/issuable_header.vue b/app/assets/javascripts/vue_shared/issuable/show/components/issuable_header.vue
index ee7e113af72..649dbd6576b 100644
--- a/app/assets/javascripts/vue_shared/issuable/show/components/issuable_header.vue
+++ b/app/assets/javascripts/vue_shared/issuable/show/components/issuable_header.vue
@@ -1,14 +1,23 @@
<script>
-import { GlIcon, GlButton, GlTooltipDirective, GlAvatarLink, GlAvatarLabeled } from '@gitlab/ui';
+import {
+ GlIcon,
+ GlBadge,
+ GlButton,
+ GlTooltipDirective,
+ GlAvatarLink,
+ GlAvatarLabeled,
+} from '@gitlab/ui';
import { getIdFromGraphQLId } from '~/graphql_shared/utils';
import { isExternal } from '~/lib/utils/url_utility';
import { n__, sprintf } from '~/locale';
import TimeAgoTooltip from '~/vue_shared/components/time_ago_tooltip.vue';
+import { IssuableStates } from '~/vue_shared/issuable/list/constants';
export default {
components: {
GlIcon,
+ GlBadge,
GlButton,
GlAvatarLink,
GlAvatarLabeled,
@@ -26,6 +35,11 @@ export default {
type: Object,
required: true,
},
+ issuableState: {
+ type: String,
+ required: false,
+ default: '',
+ },
statusBadgeClass: {
type: String,
required: false,
@@ -36,6 +50,11 @@ export default {
required: false,
default: '',
},
+ statusIconClass: {
+ type: String,
+ required: false,
+ default: '',
+ },
blocked: {
type: Boolean,
required: false,
@@ -53,6 +72,9 @@ export default {
},
},
computed: {
+ badgeVariant() {
+ return this.issuableState === IssuableStates.Opened ? 'success' : 'info';
+ },
authorId() {
return getIdFromGraphQLId(`${this.author.id}`);
},
@@ -71,6 +93,9 @@ export default {
{ completedCount, count },
);
},
+ hasTasks() {
+ return this.taskCompletionStatus.count > 0;
+ },
},
mounted() {
this.toggleSidebarButtonEl = document.querySelector('.js-toggle-right-sidebar-button');
@@ -88,10 +113,15 @@ export default {
<template>
<div class="detail-page-header">
<div class="detail-page-header-body">
- <div data-testid="status" class="issuable-status-box status-box" :class="statusBadgeClass">
- <gl-icon v-if="statusIcon" :name="statusIcon" class="d-block d-sm-none" />
- <span class="d-none d-sm-block"><slot name="status-badge"></slot></span>
- </div>
+ <gl-badge
+ data-testid="status"
+ class="issuable-status-badge gl-mr-3"
+ :class="statusBadgeClass"
+ :variant="badgeVariant"
+ >
+ <gl-icon v-if="statusIcon" :name="statusIcon" :class="statusIconClass" />
+ <span class="gl-display-none gl-sm-display-block"><slot name="status-badge"></slot></span>
+ </gl-badge>
<div class="issuable-meta gl-display-flex gl-align-items-center d-md-inline-block">
<div v-if="blocked || confidential" class="gl-display-inline-block">
<div v-if="blocked" data-testid="blocked" class="issuable-warning-icon inline">
@@ -128,7 +158,7 @@ export default {
<strong class="author d-sm-none d-inline">@{{ author.username }}</strong>
</gl-avatar-link>
<span
- v-if="taskCompletionStatus"
+ v-if="taskCompletionStatus && hasTasks"
data-testid="task-status"
class="gl-display-none gl-md-display-block gl-lg-display-inline-block"
>{{ taskStatusString }}</span
diff --git a/app/assets/javascripts/vue_shared/issuable/show/components/issuable_show_root.vue b/app/assets/javascripts/vue_shared/issuable/show/components/issuable_show_root.vue
index 8849af2a52e..c165ee91c59 100644
--- a/app/assets/javascripts/vue_shared/issuable/show/components/issuable_show_root.vue
+++ b/app/assets/javascripts/vue_shared/issuable/show/components/issuable_show_root.vue
@@ -27,6 +27,11 @@ export default {
required: false,
default: '',
},
+ statusIconClass: {
+ type: String,
+ required: false,
+ default: '',
+ },
enableEdit: {
type: Boolean,
required: false,
@@ -102,8 +107,10 @@ export default {
<template>
<div class="issuable-show-container" data-qa-selector="issuable_show_container">
<issuable-header
+ :issuable-state="issuable.state"
:status-badge-class="statusBadgeClass"
:status-icon="statusIcon"
+ :status-icon-class="statusIconClass"
:blocked="issuable.blocked"
:confidential="issuable.confidential"
:created-at="issuable.createdAt"
@@ -122,6 +129,7 @@ export default {
:issuable="issuable"
:status-badge-class="statusBadgeClass"
:status-icon="statusIcon"
+ :status-icon-class="statusIconClass"
:enable-edit="enableEdit"
:enable-autocomplete="enableAutocomplete"
:enable-autosave="enableAutosave"
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 45941174a62..47f05a2cee2 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
@@ -86,7 +86,7 @@ export default {
>
<p
data-testid="status"
- class="issuable-status-box status-box gl-my-0"
+ class="issuable-status-box status-box gl-white-space-nowrap gl-my-0"
:class="statusBadgeClass"
>
<gl-icon :name="statusIcon" class="gl-display-block d-sm-none gl-h-6!" />
diff --git a/app/assets/javascripts/vue_shared/mixins/timeago.js b/app/assets/javascripts/vue_shared/mixins/timeago.js
index c5f41d81167..2a0256548a8 100644
--- a/app/assets/javascripts/vue_shared/mixins/timeago.js
+++ b/app/assets/javascripts/vue_shared/mixins/timeago.js
@@ -1,4 +1,4 @@
-import { formatDate, getTimeago } from '~/lib/utils/datetime_utility';
+import { formatDate, getTimeago, timeagoLanguageCode } from '~/lib/utils/datetime_utility';
/**
* Mixin with time ago methods used in some vue components
@@ -8,7 +8,7 @@ export default {
timeFormatted(time) {
const timeago = getTimeago();
- return timeago.format(time);
+ return timeago.format(time, timeagoLanguageCode);
},
tooltipTitle(time) {
diff --git a/app/assets/javascripts/vue_shared/security_configuration/components/section_layout.vue b/app/assets/javascripts/vue_shared/security_configuration/components/section_layout.vue
new file mode 100644
index 00000000000..6045d75ac11
--- /dev/null
+++ b/app/assets/javascripts/vue_shared/security_configuration/components/section_layout.vue
@@ -0,0 +1,34 @@
+<script>
+import SectionLoader from './section_loader.vue';
+
+export default {
+ name: 'SectionLayout',
+ components: {
+ SectionLoader,
+ },
+ props: {
+ heading: {
+ type: String,
+ required: true,
+ },
+ isLoading: {
+ type: Boolean,
+ required: false,
+ default: false,
+ },
+ },
+};
+</script>
+
+<template>
+ <div class="row gl-m-0 gl-border-b gl-line-height-20 gl-py-6">
+ <div class="col-lg-4 gl-pl-0 gl-pr-9">
+ <h2 class="gl-font-size-h2 gl-mt-0">{{ heading }}</h2>
+ <slot name="description"></slot>
+ </div>
+ <div class="col-lg-8 gl-pr-0 gl-pl-0">
+ <section-loader v-if="isLoading" />
+ <slot v-else name="features"></slot>
+ </div>
+ </div>
+</template>
diff --git a/app/assets/javascripts/vue_shared/security_configuration/components/section_loader.vue b/app/assets/javascripts/vue_shared/security_configuration/components/section_loader.vue
new file mode 100644
index 00000000000..b15e25b0943
--- /dev/null
+++ b/app/assets/javascripts/vue_shared/security_configuration/components/section_loader.vue
@@ -0,0 +1,35 @@
+<script>
+import { GlCard, GlSkeletonLoader } from '@gitlab/ui';
+
+export default {
+ name: 'SectionLoader',
+ components: {
+ GlCard,
+ GlSkeletonLoader,
+ },
+};
+</script>
+
+<template>
+ <div>
+ <gl-skeleton-loader :width="1248" :height="180">
+ <rect x="0" y="0" width="100" height="15" rx="4" />
+ <rect x="0" y="24" width="460" height="32" rx="4" />
+ <rect x="0" y="71" width="100" height="15" rx="4" />
+ <rect x="0" y="95" width="460" height="72" rx="4" />
+ </gl-skeleton-loader>
+ <gl-card v-for="i in 2" :key="i" class="gl-mb-5">
+ <template #header>
+ <gl-skeleton-loader :width="1248" :height="15">
+ <rect x="0" y="0" width="300" height="15" rx="4" />
+ </gl-skeleton-loader>
+ </template>
+ <gl-skeleton-loader :width="1248" :height="15">
+ <rect x="0" y="0" width="600" height="15" rx="4" />
+ </gl-skeleton-loader>
+ <gl-skeleton-loader :width="1248" :height="15">
+ <rect x="0" y="0" width="300" height="15" rx="4" />
+ </gl-skeleton-loader>
+ </gl-card>
+ </div>
+</template>
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 458bacce915..6a4f671abb9 100644
--- a/app/assets/javascripts/vue_shared/security_reports/store/utils.js
+++ b/app/assets/javascripts/vue_shared/security_reports/store/utils.js
@@ -90,7 +90,7 @@ const createStatusMessage = ({ reportType, status, total }) => {
if (status) {
message = __('%{reportType} %{status}');
} else if (!total) {
- message = __('%{reportType} detected %{totalStart}no%{totalEnd} vulnerabilities.');
+ message = __('%{reportType} detected no %{totalStart}new%{totalEnd} vulnerabilities.');
} else {
message = __(
'%{reportType} detected %{totalStart}%{total}%{totalEnd} potential %{vulnMessage}',