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:
authorGitLab Bot <gitlab-bot@gitlab.com>2023-02-20 16:49:51 +0300
committerGitLab Bot <gitlab-bot@gitlab.com>2023-02-20 16:49:51 +0300
commit71786ddc8e28fbd3cb3fcc4b3ff15e5962a1c82e (patch)
tree6a2d93ef3fb2d353bb7739e4b57e6541f51cdd71 /app/assets/javascripts/pages
parenta7253423e3403b8c08f8a161e5937e1488f5f407 (diff)
Add latest changes from gitlab-org/gitlab@15-9-stable-eev15.9.0-rc42
Diffstat (limited to 'app/assets/javascripts/pages')
-rw-r--r--app/assets/javascripts/pages/abuse_reports/index.js3
-rw-r--r--app/assets/javascripts/pages/admin/application_settings/account_and_limits.js57
-rw-r--r--app/assets/javascripts/pages/admin/application_settings/general/index.js4
-rw-r--r--app/assets/javascripts/pages/admin/application_settings/index.js2
-rw-r--r--app/assets/javascripts/pages/admin/hooks/index.js3
-rw-r--r--app/assets/javascripts/pages/admin/jobs/index/components/cancel_jobs.vue37
-rw-r--r--app/assets/javascripts/pages/admin/jobs/index/components/cancel_jobs_modal.vue (renamed from app/assets/javascripts/pages/admin/jobs/index/components/stop_jobs_modal.vue)24
-rw-r--r--app/assets/javascripts/pages/admin/jobs/index/components/constants.js15
-rw-r--r--app/assets/javascripts/pages/admin/jobs/index/components/table/admin_jobs_table_app.vue19
-rw-r--r--app/assets/javascripts/pages/admin/jobs/index/index.js50
-rw-r--r--app/assets/javascripts/pages/admin/projects/components/namespace_select.vue145
-rw-r--r--app/assets/javascripts/pages/admin/projects/index.js8
-rw-r--r--app/assets/javascripts/pages/admin/runners/new/index.js3
-rw-r--r--app/assets/javascripts/pages/dashboard/issues/index.js4
-rw-r--r--app/assets/javascripts/pages/dashboard/merge_requests/index.js9
-rw-r--r--app/assets/javascripts/pages/dashboard/milestones/index/index.js13
-rw-r--r--app/assets/javascripts/pages/dashboard/todos/index/todos.js16
-rw-r--r--app/assets/javascripts/pages/groups/edit/index.js7
-rw-r--r--app/assets/javascripts/pages/groups/group_members/index.js5
-rw-r--r--app/assets/javascripts/pages/groups/merge_requests/index.js10
-rw-r--r--app/assets/javascripts/pages/groups/usage_quotas/index.js3
-rw-r--r--app/assets/javascripts/pages/profiles/saved_replies/index.js3
-rw-r--r--app/assets/javascripts/pages/projects/airflow/dags/index/index.js27
-rw-r--r--app/assets/javascripts/pages/projects/commit/show/index.js3
-rw-r--r--app/assets/javascripts/pages/projects/edit/index.js4
-rw-r--r--app/assets/javascripts/pages/projects/find_file/ref_switcher/index.js38
-rw-r--r--app/assets/javascripts/pages/projects/find_file/ref_switcher/ref_switcher_utils.js28
-rw-r--r--app/assets/javascripts/pages/projects/find_file/show/index.js2
-rw-r--r--app/assets/javascripts/pages/projects/forks/new/components/fork_form.vue33
-rw-r--r--app/assets/javascripts/pages/projects/graphs/charts/index.js38
-rw-r--r--app/assets/javascripts/pages/projects/hooks/index.js3
-rw-r--r--app/assets/javascripts/pages/projects/index.js2
-rw-r--r--app/assets/javascripts/pages/projects/learn_gitlab/components/included_in_trial_indicator.vue15
-rw-r--r--app/assets/javascripts/pages/projects/learn_gitlab/components/learn_gitlab.vue146
-rw-r--r--app/assets/javascripts/pages/projects/learn_gitlab/components/learn_gitlab_section_card.vue56
-rw-r--r--app/assets/javascripts/pages/projects/learn_gitlab/components/learn_gitlab_section_link.vue151
-rw-r--r--app/assets/javascripts/pages/projects/learn_gitlab/constants/index.js133
-rw-r--r--app/assets/javascripts/pages/projects/learn_gitlab/index/index.js31
-rw-r--r--app/assets/javascripts/pages/projects/merge_requests/creations/new/compare.js66
-rw-r--r--app/assets/javascripts/pages/projects/merge_requests/creations/new/compare_autocomplete.js91
-rw-r--r--app/assets/javascripts/pages/projects/merge_requests/creations/new/index.js81
-rw-r--r--app/assets/javascripts/pages/projects/merge_requests/creations/new/target_project_dropdown.js23
-rw-r--r--app/assets/javascripts/pages/projects/merge_requests/index/index.js2
-rw-r--r--app/assets/javascripts/pages/projects/merge_requests/show/index.js2
-rw-r--r--app/assets/javascripts/pages/projects/ml/candidates/show/index.js27
-rw-r--r--app/assets/javascripts/pages/projects/ml/experiments/index/index.js24
-rw-r--r--app/assets/javascripts/pages/projects/ml/experiments/show/index.js4
-rw-r--r--app/assets/javascripts/pages/projects/network/show/index.js32
-rw-r--r--app/assets/javascripts/pages/projects/project.js8
-rw-r--r--app/assets/javascripts/pages/projects/project_members/index.js5
-rw-r--r--app/assets/javascripts/pages/projects/settings/ci_cd/show/index.js3
-rw-r--r--app/assets/javascripts/pages/projects/shared/permissions/components/settings_panel.vue68
-rw-r--r--app/assets/javascripts/pages/projects/show/index.js2
-rw-r--r--app/assets/javascripts/pages/search/show/refresh_counts.js24
-rw-r--r--app/assets/javascripts/pages/shared/mount_runner_aws_deployments.js17
-rw-r--r--app/assets/javascripts/pages/shared/wikis/components/wiki_form.vue11
-rw-r--r--app/assets/javascripts/pages/users/show/index.js16
57 files changed, 638 insertions, 1018 deletions
diff --git a/app/assets/javascripts/pages/abuse_reports/index.js b/app/assets/javascripts/pages/abuse_reports/index.js
new file mode 100644
index 00000000000..feceeb0b10a
--- /dev/null
+++ b/app/assets/javascripts/pages/abuse_reports/index.js
@@ -0,0 +1,3 @@
+import { initLinkToSpam } from '~/abuse_reports';
+
+initLinkToSpam();
diff --git a/app/assets/javascripts/pages/admin/application_settings/account_and_limits.js b/app/assets/javascripts/pages/admin/application_settings/account_and_limits.js
index 455c637a6b3..8b8147425bc 100644
--- a/app/assets/javascripts/pages/admin/application_settings/account_and_limits.js
+++ b/app/assets/javascripts/pages/admin/application_settings/account_and_limits.js
@@ -20,10 +20,65 @@ function setUserInternalRegexPlaceholder(checkbox) {
}
}
-export default function initUserInternalRegexPlaceholder() {
+function initUserInternalRegexPlaceholder() {
const checkbox = document.getElementById('application_setting_user_default_external');
setUserInternalRegexPlaceholder(checkbox);
checkbox.addEventListener('change', () => {
setUserInternalRegexPlaceholder(checkbox);
});
}
+
+/**
+ * Sets up logic inside "Dormant users" subsection:
+ * - checkbox enables/disables additional input
+ * - shows/hides an inline error on input validation
+ */
+function initDeactivateDormantUsersPeriodInputSection() {
+ const DISPLAY_NONE_CLASS = 'gl-display-none';
+
+ /** @type {HTMLInputElement} */
+ const checkbox = document.getElementById('application_setting_deactivate_dormant_users');
+ /** @type {HTMLInputElement} */
+ const input = document.getElementById('application_setting_deactivate_dormant_users_period');
+ /** @type {HTMLDivElement} */
+ const errorLabel = document.getElementById(
+ 'application_setting_deactivate_dormant_users_period_error',
+ );
+
+ if (!checkbox || !input || !errorLabel) return;
+
+ const hideInputErrorLabel = () => {
+ if (input.checkValidity()) {
+ errorLabel.classList.add(DISPLAY_NONE_CLASS);
+ }
+ };
+
+ const handleInputInvalidState = (event) => {
+ event.preventDefault();
+ event.stopImmediatePropagation();
+ errorLabel.classList.remove(DISPLAY_NONE_CLASS);
+ return false;
+ };
+
+ const updateInputDisabledState = () => {
+ input.disabled = !checkbox.checked;
+ if (input.disabled) {
+ hideInputErrorLabel();
+ }
+ };
+
+ // Show error when input is invalid
+ input.addEventListener('invalid', handleInputInvalidState);
+ // Hide error when input changes
+ input.addEventListener('input', hideInputErrorLabel);
+ input.addEventListener('change', hideInputErrorLabel);
+
+ // Handle checkbox change and set initial state
+ checkbox.addEventListener('change', updateInputDisabledState);
+ updateInputDisabledState();
+}
+
+export default function initAccountAndLimitsSection() {
+ initUserInternalRegexPlaceholder();
+ initDeactivateDormantUsersPeriodInputSection();
+}
diff --git a/app/assets/javascripts/pages/admin/application_settings/general/index.js b/app/assets/javascripts/pages/admin/application_settings/general/index.js
index c48d99da990..8a810ca649c 100644
--- a/app/assets/javascripts/pages/admin/application_settings/general/index.js
+++ b/app/assets/javascripts/pages/admin/application_settings/general/index.js
@@ -1,9 +1,9 @@
-import initUserInternalRegexPlaceholder from '../account_and_limits';
+import initAccountAndLimitsSection from '../account_and_limits';
import initGitpod from '../gitpod';
import initSignupRestrictions from '../signup_restrictions';
(() => {
- initUserInternalRegexPlaceholder();
+ initAccountAndLimitsSection();
initGitpod();
initSignupRestrictions();
})();
diff --git a/app/assets/javascripts/pages/admin/application_settings/index.js b/app/assets/javascripts/pages/admin/application_settings/index.js
index f1e92cf195a..366be334e87 100644
--- a/app/assets/javascripts/pages/admin/application_settings/index.js
+++ b/app/assets/javascripts/pages/admin/application_settings/index.js
@@ -1,5 +1,4 @@
import initVariableList from '~/ci/ci_variable_list';
-import projectSelect from '~/project_select';
import initSearchSettings from '~/search_settings';
import selfMonitor from '~/self_monitor';
import initSettingsPanels from '~/settings_panels';
@@ -8,5 +7,4 @@ initVariableList('js-instance-variables');
selfMonitor();
// Initialize expandable settings panels
initSettingsPanels();
-projectSelect();
initSearchSettings();
diff --git a/app/assets/javascripts/pages/admin/hooks/index.js b/app/assets/javascripts/pages/admin/hooks/index.js
new file mode 100644
index 00000000000..82e601426f1
--- /dev/null
+++ b/app/assets/javascripts/pages/admin/hooks/index.js
@@ -0,0 +1,3 @@
+import { initHookTestDropdowns } from '~/webhooks';
+
+initHookTestDropdowns();
diff --git a/app/assets/javascripts/pages/admin/jobs/index/components/cancel_jobs.vue b/app/assets/javascripts/pages/admin/jobs/index/components/cancel_jobs.vue
new file mode 100644
index 00000000000..72cfc005782
--- /dev/null
+++ b/app/assets/javascripts/pages/admin/jobs/index/components/cancel_jobs.vue
@@ -0,0 +1,37 @@
+<script>
+import { GlButton, GlModalDirective, GlTooltipDirective } from '@gitlab/ui';
+import CancelJobsModal from './cancel_jobs_modal.vue';
+import { CANCEL_JOBS_MODAL_ID, CANCEL_JOBS_BUTTON_TEXT, CANCEL_BUTTON_TOOLTIP } from './constants';
+
+export default {
+ name: 'CancelJobs',
+ components: {
+ GlButton,
+ CancelJobsModal,
+ },
+ directives: {
+ GlModal: GlModalDirective,
+ GlTooltip: GlTooltipDirective,
+ },
+ props: {
+ url: {
+ type: String,
+ required: true,
+ },
+ },
+ modalId: CANCEL_JOBS_MODAL_ID,
+ buttonText: CANCEL_JOBS_BUTTON_TEXT,
+ buttonTooltip: CANCEL_BUTTON_TOOLTIP,
+};
+</script>
+<template>
+ <div>
+ <gl-button
+ v-gl-modal="$options.modalId"
+ v-gl-tooltip="$options.buttonTooltip"
+ variant="danger"
+ >{{ $options.buttonText }}</gl-button
+ >
+ <cancel-jobs-modal :modal-id="$options.modalId" :url="url" @confirm="$emit('confirm')" />
+ </div>
+</template>
diff --git a/app/assets/javascripts/pages/admin/jobs/index/components/stop_jobs_modal.vue b/app/assets/javascripts/pages/admin/jobs/index/components/cancel_jobs_modal.vue
index b608b3b9492..d5857294617 100644
--- a/app/assets/javascripts/pages/admin/jobs/index/components/stop_jobs_modal.vue
+++ b/app/assets/javascripts/pages/admin/jobs/index/components/cancel_jobs_modal.vue
@@ -5,10 +5,9 @@ import axios from '~/lib/utils/axios_utils';
import { redirectTo } from '~/lib/utils/url_utility';
import {
CANCEL_TEXT,
- STOP_JOBS_MODAL_ID,
- STOP_JOBS_FAILED_TEXT,
- STOP_JOBS_MODAL_TITLE,
- STOP_JOBS_WARNING,
+ CANCEL_JOBS_FAILED_TEXT,
+ CANCEL_JOBS_MODAL_TITLE,
+ CANCEL_JOBS_WARNING,
PRIMARY_ACTION_TEXT,
} from './constants';
@@ -21,6 +20,10 @@ export default {
type: String,
required: true,
},
+ modalId: {
+ type: String,
+ required: true,
+ },
},
methods: {
onSubmit() {
@@ -32,7 +35,7 @@ export default {
})
.catch((error) => {
createAlert({
- message: STOP_JOBS_FAILED_TEXT,
+ message: CANCEL_JOBS_FAILED_TEXT,
});
throw error;
});
@@ -45,20 +48,19 @@ export default {
cancelAction: {
text: CANCEL_TEXT,
},
- STOP_JOBS_WARNING,
- STOP_JOBS_MODAL_ID,
- STOP_JOBS_MODAL_TITLE,
+ CANCEL_JOBS_WARNING,
+ CANCEL_JOBS_MODAL_TITLE,
};
</script>
<template>
<gl-modal
- :modal-id="$options.STOP_JOBS_MODAL_ID"
+ :modal-id="modalId"
:action-primary="$options.primaryAction"
:action-cancel="$options.cancelAction"
+ :title="$options.CANCEL_JOBS_MODAL_TITLE"
@primary="onSubmit"
>
- <template #modal-title>{{ $options.STOP_JOBS_MODAL_TITLE }}</template>
- {{ $options.STOP_JOBS_WARNING }}
+ {{ $options.CANCEL_JOBS_WARNING }}
</gl-modal>
</template>
diff --git a/app/assets/javascripts/pages/admin/jobs/index/components/constants.js b/app/assets/javascripts/pages/admin/jobs/index/components/constants.js
index 9e2d464bc4d..cfde1fc0a2b 100644
--- a/app/assets/javascripts/pages/admin/jobs/index/components/constants.js
+++ b/app/assets/javascripts/pages/admin/jobs/index/components/constants.js
@@ -1,11 +1,12 @@
import { s__, __ } from '~/locale';
-export const STOP_JOBS_MODAL_ID = 'stop-jobs-modal';
-export const STOP_JOBS_MODAL_TITLE = s__('AdminArea|Stop all jobs?');
-export const STOP_JOBS_BUTTON_TEXT = s__('AdminArea|Stop all jobs');
+export const CANCEL_JOBS_MODAL_ID = 'cancel-jobs-modal';
+export const CANCEL_JOBS_MODAL_TITLE = s__('AdminArea|Are you sure?');
+export const CANCEL_JOBS_BUTTON_TEXT = s__('AdminArea|Cancel all jobs');
+export const CANCEL_BUTTON_TOOLTIP = s__('AdminArea|Cancel all running and pending jobs');
export const CANCEL_TEXT = __('Cancel');
-export const STOP_JOBS_FAILED_TEXT = s__('AdminArea|Stopping jobs failed');
-export const PRIMARY_ACTION_TEXT = s__('AdminArea|Stop jobs');
-export const STOP_JOBS_WARNING = s__(
- 'AdminArea|You’re about to stop all jobs. This will halt all current jobs that are running.',
+export const CANCEL_JOBS_FAILED_TEXT = s__('AdminArea|Canceling jobs failed');
+export const PRIMARY_ACTION_TEXT = s__('AdminArea|Yes, proceed');
+export const CANCEL_JOBS_WARNING = s__(
+ "AdminArea|You're about to cancel all running and pending jobs across this instance. Do you want to proceed?",
);
diff --git a/app/assets/javascripts/pages/admin/jobs/index/components/table/admin_jobs_table_app.vue b/app/assets/javascripts/pages/admin/jobs/index/components/table/admin_jobs_table_app.vue
new file mode 100644
index 00000000000..c5a0509b625
--- /dev/null
+++ b/app/assets/javascripts/pages/admin/jobs/index/components/table/admin_jobs_table_app.vue
@@ -0,0 +1,19 @@
+<script>
+export default {
+ inject: {
+ jobStatuses: {
+ default: null,
+ },
+ url: {
+ default: '',
+ },
+ emptyStateSvgPath: {
+ default: '',
+ },
+ },
+};
+</script>
+
+<template>
+ <div>{{ __('Jobs') }}</div>
+</template>
diff --git a/app/assets/javascripts/pages/admin/jobs/index/index.js b/app/assets/javascripts/pages/admin/jobs/index/index.js
index c82b186f671..9df52557212 100644
--- a/app/assets/javascripts/pages/admin/jobs/index/index.js
+++ b/app/assets/javascripts/pages/admin/jobs/index/index.js
@@ -1,31 +1,33 @@
import Vue from 'vue';
import { BV_SHOW_MODAL } from '~/lib/utils/constants';
import Translate from '~/vue_shared/translate';
-import { STOP_JOBS_MODAL_ID } from './components/constants';
-import StopJobsModal from './components/stop_jobs_modal.vue';
+import { CANCEL_JOBS_MODAL_ID } from './components/constants';
+import CancelJobsModal from './components/cancel_jobs_modal.vue';
+import AdminJobsTableApp from './components/table/admin_jobs_table_app.vue';
Vue.use(Translate);
function initJobs() {
const buttonId = 'js-stop-jobs-button';
- const stopJobsButton = document.getElementById(buttonId);
- if (stopJobsButton) {
+ const cancelJobsButton = document.getElementById(buttonId);
+ if (cancelJobsButton) {
// eslint-disable-next-line no-new
new Vue({
- el: `#js-${STOP_JOBS_MODAL_ID}`,
+ el: `#js-${CANCEL_JOBS_MODAL_ID}`,
components: {
- StopJobsModal,
+ CancelJobsModal,
},
mounted() {
- stopJobsButton.classList.remove('disabled');
- stopJobsButton.addEventListener('click', () => {
- this.$root.$emit(BV_SHOW_MODAL, STOP_JOBS_MODAL_ID, `#${buttonId}`);
+ cancelJobsButton.classList.remove('disabled');
+ cancelJobsButton.addEventListener('click', () => {
+ this.$root.$emit(BV_SHOW_MODAL, CANCEL_JOBS_MODAL_ID, `#${buttonId}`);
});
},
render(createElement) {
- return createElement(STOP_JOBS_MODAL_ID, {
+ return createElement(CANCEL_JOBS_MODAL_ID, {
props: {
- url: stopJobsButton.dataset.url,
+ url: cancelJobsButton.dataset.url,
+ modalId: CANCEL_JOBS_MODAL_ID,
},
});
},
@@ -33,4 +35,28 @@ function initJobs() {
}
}
-initJobs();
+export function initAdminJobsApp() {
+ const containerEl = document.getElementById('admin-jobs-app');
+
+ if (!containerEl) return false;
+
+ const { jobStatuses, emptyStateSvgPath, url } = containerEl.dataset;
+
+ return new Vue({
+ el: containerEl,
+ provide: {
+ url,
+ emptyStateSvgPath,
+ jobStatuses: JSON.parse(jobStatuses),
+ },
+ render(createElement) {
+ return createElement(AdminJobsTableApp);
+ },
+ });
+}
+
+if (gon.features.adminJobsVue) {
+ initAdminJobsApp();
+} else {
+ initJobs();
+}
diff --git a/app/assets/javascripts/pages/admin/projects/components/namespace_select.vue b/app/assets/javascripts/pages/admin/projects/components/namespace_select.vue
index c75c031b0b1..fa8f78839f3 100644
--- a/app/assets/javascripts/pages/admin/projects/components/namespace_select.vue
+++ b/app/assets/javascripts/pages/admin/projects/components/namespace_select.vue
@@ -1,34 +1,31 @@
<script>
-import {
- GlDropdown,
- GlDropdownItem,
- GlDropdownDivider,
- GlSearchBoxByType,
- GlLoadingIcon,
-} from '@gitlab/ui';
+import { debounce } from 'lodash';
+import { GlCollapsibleListbox } from '@gitlab/ui';
import Api from '~/api';
+import { DEFAULT_DEBOUNCE_AND_THROTTLE_MS } from '~/lib/utils/constants';
import { __ } from '~/locale';
export default {
i18n: {
- dropdownHeader: __('Namespaces'),
+ headerText: __('Namespaces'),
searchPlaceholder: __('Search for Namespace'),
- anyNamespace: __('Any namespace'),
+ reset: __('Clear'),
},
components: {
- GlDropdown,
- GlDropdownItem,
- GlDropdownDivider,
- GlLoadingIcon,
- GlSearchBoxByType,
+ GlCollapsibleListbox,
},
props: {
- showAny: {
- type: Boolean,
+ origSelectedId: {
+ type: String,
+ required: false,
+ default: '',
+ },
+ origSelectedText: {
+ type: String,
required: false,
- default: false,
+ default: '',
},
- placeholder: {
+ toggleTextPlaceholder: {
type: String,
required: false,
default: __('Namespace'),
@@ -42,56 +39,72 @@ export default {
data() {
return {
namespaceOptions: [],
- selectedNamespaceId: null,
- selectedNamespace: null,
+ selectedNamespaceId: this.origSelectedId,
+ selectedNamespaceText: this.origSelectedText,
searchTerm: '',
isLoading: false,
};
},
computed: {
- selectedNamespaceName() {
- if (this.selectedNamespaceId === null) {
- return this.placeholder;
- }
- return this.selectedNamespace;
+ toggleText() {
+ return this.selectedNamespaceText || this.toggleTextPlaceholder;
},
},
watch: {
- searchTerm() {
- this.fetchNamespaces(this.searchTerm);
+ selectedNamespaceId(val) {
+ if (!val) {
+ this.selectedNamespaceText = null;
+ }
+
+ this.selectedNamespaceText = this.namespaceOptions.find(({ value }) => value === val)?.text;
},
},
mounted() {
this.fetchNamespaces();
},
methods: {
- fetchNamespaces(filter) {
+ fetchNamespaces() {
this.isLoading = true;
this.namespaceOptions = [];
- return Api.namespaces(filter, (namespaces) => {
- this.namespaceOptions = namespaces;
+
+ return Api.namespaces(this.searchTerm, (namespaces) => {
+ this.namespaceOptions = this.formatNamespaceOptions(namespaces);
this.isLoading = false;
});
},
- selectNamespace(key) {
- this.selectedNamespaceId = this.namespaceOptions[key].id;
- this.selectedNamespace = this.getNamespaceString(this.namespaceOptions[key]);
- this.$emit('setNamespace', this.selectedNamespaceId);
+ formatNamespaceOptions(namespaces) {
+ if (!namespaces) {
+ return [];
+ }
+
+ return namespaces.map((namespace) => {
+ return {
+ value: String(namespace.id),
+ text: this.getNamespaceString(namespace),
+ };
+ });
},
- selectAnyNamespace() {
- this.selectedNamespaceId = null;
- this.selectedNamespace = null;
- this.$emit('setNamespace', null);
+ selectNamespace(value) {
+ this.selectedNamespaceId = value;
+ this.$emit('setNamespace', this.selectedNamespaceId);
},
getNamespaceString(namespace) {
return `${namespace.kind}: ${namespace.full_path}`;
},
+ search: debounce(function debouncedSearch(searchQuery) {
+ this.searchTerm = searchQuery?.trim();
+ this.fetchNamespaces();
+ }, DEFAULT_DEBOUNCE_AND_THROTTLE_MS),
+ onReset() {
+ this.selectedNamespaceId = null;
+ this.$emit('setNamespace', null);
+ },
},
};
</script>
<template>
- <div class="gl-display-flex">
+ <div class="gl-display-flex gl-w-full">
<input
v-if="fieldName"
:name="fieldName"
@@ -99,45 +112,19 @@ export default {
type="hidden"
data-testid="hidden-input"
/>
- <gl-dropdown
- :text="selectedNamespaceName"
- :header-text="$options.i18n.dropdownHeader"
- toggle-class="dropdown-menu-toggle large"
- data-testid="namespace-dropdown"
- :right="true"
- >
- <template #header>
- <gl-search-box-by-type
- v-model.trim="searchTerm"
- class="namespace-search-box"
- debounce="250"
- :placeholder="$options.i18n.searchPlaceholder"
- />
- </template>
-
- <template v-if="showAny">
- <gl-dropdown-item @click="selectAnyNamespace">
- {{ $options.i18n.anyNamespace }}
- </gl-dropdown-item>
- <gl-dropdown-divider />
- </template>
-
- <gl-loading-icon v-if="isLoading" />
-
- <gl-dropdown-item
- v-for="(namespace, key) in namespaceOptions"
- :key="namespace.id"
- @click="selectNamespace(key)"
- >
- {{ getNamespaceString(namespace) }}
- </gl-dropdown-item>
- </gl-dropdown>
+ <gl-collapsible-listbox
+ :items="namespaceOptions"
+ :header-text="$options.i18n.headerText"
+ :reset-button-label="$options.i18n.reset"
+ :toggle-text="toggleText"
+ :search-placeholder="$options.i18n.searchPlaceholder"
+ :searching="isLoading"
+ :selected="selectedNamespaceId"
+ toggle-class="gl-w-full gl-flex-direction-column gl-align-items-stretch!"
+ searchable
+ @reset="onReset"
+ @search="search"
+ @select="selectNamespace"
+ />
</div>
</template>
-
-<style scoped>
-/* workaround position: relative imposed by .top-area .nav-controls */
-.namespace-search-box >>> input {
- position: static;
-}
-</style>
diff --git a/app/assets/javascripts/pages/admin/projects/index.js b/app/assets/javascripts/pages/admin/projects/index.js
index 3098d06510b..49ee89de772 100644
--- a/app/assets/javascripts/pages/admin/projects/index.js
+++ b/app/assets/javascripts/pages/admin/projects/index.js
@@ -1,5 +1,4 @@
import Vue from 'vue';
-import { parseBoolean } from '~/lib/utils/common_utils';
import { mergeUrlParams } from '~/lib/utils/url_utility';
import ProjectsList from '~/projects_list';
import NamespaceSelect from './components/namespace_select.vue';
@@ -12,16 +11,17 @@ function mountNamespaceSelect() {
return false;
}
- const { showAny, fieldName, placeholder, updateLocation } = el.dataset;
+ const { fieldName, toggleTextPlaceholder, selectedId, selectedText, updateLocation } = el.dataset;
return new Vue({
el,
render(createComponent) {
return createComponent(NamespaceSelect, {
props: {
- showAny: parseBoolean(showAny),
fieldName,
- placeholder,
+ toggleTextPlaceholder,
+ origSelectedId: selectedId,
+ origSelectedText: selectedText,
},
on: {
setNamespace(newNamespace) {
diff --git a/app/assets/javascripts/pages/admin/runners/new/index.js b/app/assets/javascripts/pages/admin/runners/new/index.js
new file mode 100644
index 00000000000..5048ad7b57a
--- /dev/null
+++ b/app/assets/javascripts/pages/admin/runners/new/index.js
@@ -0,0 +1,3 @@
+import { initAdminNewRunner } from '~/ci/runner/admin_new_runner';
+
+initAdminNewRunner();
diff --git a/app/assets/javascripts/pages/dashboard/issues/index.js b/app/assets/javascripts/pages/dashboard/issues/index.js
index 08c247a498b..2ca11e96f69 100644
--- a/app/assets/javascripts/pages/dashboard/issues/index.js
+++ b/app/assets/javascripts/pages/dashboard/issues/index.js
@@ -3,7 +3,7 @@ import { mountIssuesDashboardApp } from '~/issues/dashboard';
import initManualOrdering from '~/issues/manual_ordering';
import { FILTERED_SEARCH } from '~/filtered_search/constants';
import initFilteredSearch from '~/pages/search/init_filtered_search';
-import projectSelect from '~/project_select';
+import { initNewResourceDropdown } from '~/vue_shared/components/new_resource_dropdown/init_new_resource_dropdown';
initFilteredSearch({
page: FILTERED_SEARCH.ISSUES,
@@ -11,7 +11,7 @@ initFilteredSearch({
useDefaultState: true,
});
-projectSelect();
+initNewResourceDropdown();
initManualOrdering();
mountIssuesDashboardApp();
diff --git a/app/assets/javascripts/pages/dashboard/merge_requests/index.js b/app/assets/javascripts/pages/dashboard/merge_requests/index.js
index 1350837476b..a8c59ea6f3d 100644
--- a/app/assets/javascripts/pages/dashboard/merge_requests/index.js
+++ b/app/assets/javascripts/pages/dashboard/merge_requests/index.js
@@ -2,7 +2,9 @@ import addExtraTokensForMergeRequests from 'ee_else_ce/filtered_search/add_extra
import IssuableFilteredSearchTokenKeys from '~/filtered_search/issuable_filtered_search_token_keys';
import { FILTERED_SEARCH } from '~/filtered_search/constants';
import initFilteredSearch from '~/pages/search/init_filtered_search';
-import projectSelect from '~/project_select';
+import { initNewResourceDropdown } from '~/vue_shared/components/new_resource_dropdown/init_new_resource_dropdown';
+import { RESOURCE_TYPE_MERGE_REQUEST } from '~/vue_shared/components/new_resource_dropdown/constants';
+import searchUserProjectsWithMergeRequestsEnabled from '~/vue_shared/components/new_resource_dropdown/graphql/search_user_projects_with_merge_requests_enabled.query.graphql';
addExtraTokensForMergeRequests(IssuableFilteredSearchTokenKeys, true);
@@ -12,4 +14,7 @@ initFilteredSearch({
useDefaultState: true,
});
-projectSelect();
+initNewResourceDropdown({
+ resourceType: RESOURCE_TYPE_MERGE_REQUEST,
+ query: searchUserProjectsWithMergeRequestsEnabled,
+});
diff --git a/app/assets/javascripts/pages/dashboard/milestones/index/index.js b/app/assets/javascripts/pages/dashboard/milestones/index/index.js
index b526fce6f7b..88061d9ca22 100644
--- a/app/assets/javascripts/pages/dashboard/milestones/index/index.js
+++ b/app/assets/javascripts/pages/dashboard/milestones/index/index.js
@@ -1,3 +1,12 @@
-import projectSelect from '~/project_select';
+import { initNewResourceDropdown } from '~/vue_shared/components/new_resource_dropdown/init_new_resource_dropdown';
+import { RESOURCE_TYPE_MILESTONE } from '~/vue_shared/components/new_resource_dropdown/constants';
+import searchUserGroupsAndProjects from '~/vue_shared/components/new_resource_dropdown/graphql/search_user_groups_and_projects.query.graphql';
-projectSelect();
+initNewResourceDropdown({
+ resourceType: RESOURCE_TYPE_MILESTONE,
+ query: searchUserGroupsAndProjects,
+ extractProjects: (data) => [
+ ...(data?.user?.groups?.nodes ?? []),
+ ...(data?.projects?.nodes ?? []),
+ ],
+});
diff --git a/app/assets/javascripts/pages/dashboard/todos/index/todos.js b/app/assets/javascripts/pages/dashboard/todos/index/todos.js
index c5d62ae5daf..2fdf3c42935 100644
--- a/app/assets/javascripts/pages/dashboard/todos/index/todos.js
+++ b/app/assets/javascripts/pages/dashboard/todos/index/todos.js
@@ -133,10 +133,10 @@ export default class Todos {
restoreBtn.classList.add('hidden');
doneBtn.classList.remove('hidden');
} else if (target === doneBtn) {
- row.classList.add('done-reversible', 'gl-bg-gray-50', 'gl-border-gray-100');
+ row.classList.add('done-reversible', 'gl-bg-gray-10', 'gl-border-gray-50');
restoreBtn.classList.remove('hidden');
} else if (target === restoreBtn) {
- row.classList.remove('done-reversible', 'gl-bg-gray-50', 'gl-border-gray-100');
+ row.classList.remove('done-reversible', 'gl-bg-gray-10', 'gl-border-gray-50');
doneBtn.classList.remove('hidden');
} else {
row.parentNode.removeChild(row);
@@ -147,17 +147,17 @@ export default class Todos {
e.stopPropagation();
e.preventDefault();
- const target = e.currentTarget;
- target.setAttribute('disabled', true);
- target.classList.add('disabled');
+ const { currentTarget } = e;
+ currentTarget.setAttribute('disabled', true);
+ currentTarget.classList.add('disabled');
- target.querySelector('.gl-spinner-container').classList.add('gl-mr-2');
+ currentTarget.querySelector('.gl-spinner-container').classList.add('gl-mr-2');
- axios[target.dataset.method](target.dataset.href, {
+ axios[currentTarget.dataset.method](currentTarget.href, {
ids: this.todo_ids,
})
.then(({ data }) => {
- this.updateAllState(target, data);
+ this.updateAllState(currentTarget, data);
this.updateBadges(data);
})
.catch(() =>
diff --git a/app/assets/javascripts/pages/groups/edit/index.js b/app/assets/javascripts/pages/groups/edit/index.js
index fb685247bd4..dec06fe6f4d 100644
--- a/app/assets/javascripts/pages/groups/edit/index.js
+++ b/app/assets/javascripts/pages/groups/edit/index.js
@@ -2,10 +2,10 @@ import { GROUP_BADGE } from '~/badges/constants';
import dirtySubmitFactory from '~/dirty_submit/dirty_submit_factory';
import initFilePickers from '~/file_pickers';
import initTransferGroupForm from '~/groups/init_transfer_group_form';
-import { initGroupSelects } from '~/vue_shared/components/group_select/init_group_selects';
+import { initGroupSelects } from '~/vue_shared/components/entity_select/init_group_selects';
+import { initProjectSelects } from '~/vue_shared/components/entity_select/init_project_selects';
import { initCascadingSettingsLockPopovers } from '~/namespaces/cascading_settings';
import mountBadgeSettings from '~/pages/shared/mount_badge_settings';
-import projectSelect from '~/project_select';
import initSearchSettings from '~/search_settings';
import initSettingsPanels from '~/settings_panels';
import initConfirmDanger from '~/init_confirm_danger';
@@ -22,7 +22,8 @@ mountBadgeSettings(GROUP_BADGE);
// Initialize Subgroups selector
initGroupSelects();
-projectSelect();
+// Initialize project selectors
+initProjectSelects();
initSearchSettings();
initCascadingSettingsLockPopovers();
diff --git a/app/assets/javascripts/pages/groups/group_members/index.js b/app/assets/javascripts/pages/groups/group_members/index.js
index ceda2c8fa17..1b3c7ba5a52 100644
--- a/app/assets/javascripts/pages/groups/group_members/index.js
+++ b/app/assets/javascripts/pages/groups/group_members/index.js
@@ -12,7 +12,6 @@ const SHARED_FIELDS = ['account', 'maxRole', 'expiration', 'actions'];
const APP_OPTIONS = {
[MEMBER_TYPES.user]: {
tableFields: SHARED_FIELDS.concat(['source', 'activity']),
- tableAttrs: { tr: { 'data-qa-selector': 'member_row' } },
tableSortableFields: [
'account',
'granted',
@@ -32,10 +31,6 @@ const APP_OPTIONS = {
},
[MEMBER_TYPES.group]: {
tableFields: SHARED_FIELDS.concat(['source', 'granted']),
- tableAttrs: {
- table: { 'data-qa-selector': 'groups_list' },
- tr: { 'data-qa-selector': 'group_row' },
- },
requestFormatter: groupLinkRequestFormatter,
filteredSearchBar: {
show: true,
diff --git a/app/assets/javascripts/pages/groups/merge_requests/index.js b/app/assets/javascripts/pages/groups/merge_requests/index.js
index bf0147ca885..2cf75fcf666 100644
--- a/app/assets/javascripts/pages/groups/merge_requests/index.js
+++ b/app/assets/javascripts/pages/groups/merge_requests/index.js
@@ -3,7 +3,9 @@ import IssuableFilteredSearchTokenKeys from '~/filtered_search/issuable_filtered
import { FILTERED_SEARCH } from '~/filtered_search/constants';
import { initBulkUpdateSidebar } from '~/issuable';
import initFilteredSearch from '~/pages/search/init_filtered_search';
-import projectSelect from '~/project_select';
+import { initNewResourceDropdown } from '~/vue_shared/components/new_resource_dropdown/init_new_resource_dropdown';
+import { RESOURCE_TYPE_MERGE_REQUEST } from '~/vue_shared/components/new_resource_dropdown/constants';
+import searchUserGroupProjectsWithMergeRequestsEnabled from '~/vue_shared/components/new_resource_dropdown/graphql/search_user_group_projects_with_merge_requests_enabled.query.graphql';
const ISSUABLE_BULK_UPDATE_PREFIX = 'merge_request_';
@@ -16,4 +18,8 @@ initFilteredSearch({
useDefaultState: true,
filteredSearchTokenKeys: IssuableFilteredSearchTokenKeys,
});
-projectSelect();
+initNewResourceDropdown({
+ resourceType: RESOURCE_TYPE_MERGE_REQUEST,
+ query: searchUserGroupProjectsWithMergeRequestsEnabled,
+ extractProjects: (data) => data?.group?.projects?.nodes,
+});
diff --git a/app/assets/javascripts/pages/groups/usage_quotas/index.js b/app/assets/javascripts/pages/groups/usage_quotas/index.js
new file mode 100644
index 00000000000..dab2d0b17d2
--- /dev/null
+++ b/app/assets/javascripts/pages/groups/usage_quotas/index.js
@@ -0,0 +1,3 @@
+import initUsageQuotas from '~/usage_quotas';
+
+initUsageQuotas();
diff --git a/app/assets/javascripts/pages/profiles/saved_replies/index.js b/app/assets/javascripts/pages/profiles/saved_replies/index.js
new file mode 100644
index 00000000000..ef227b82172
--- /dev/null
+++ b/app/assets/javascripts/pages/profiles/saved_replies/index.js
@@ -0,0 +1,3 @@
+import { initSavedReplies } from '~/saved_replies';
+
+initSavedReplies();
diff --git a/app/assets/javascripts/pages/projects/airflow/dags/index/index.js b/app/assets/javascripts/pages/projects/airflow/dags/index/index.js
new file mode 100644
index 00000000000..1d7cf4a5b8e
--- /dev/null
+++ b/app/assets/javascripts/pages/projects/airflow/dags/index/index.js
@@ -0,0 +1,27 @@
+import Vue from 'vue';
+import AirflowDags from '~/airflow/dags/components/dags.vue';
+import { convertObjectPropsToCamelCase } from '~/lib/utils/common_utils';
+
+const initShowDags = () => {
+ const element = document.querySelector('#js-show-airflow-dags');
+ if (!element) {
+ return null;
+ }
+
+ const dags = JSON.parse(element.dataset.dags);
+ const pagination = convertObjectPropsToCamelCase(JSON.parse(element.dataset.pagination));
+
+ return new Vue({
+ el: element,
+ render(h) {
+ return h(AirflowDags, {
+ props: {
+ dags,
+ pagination,
+ },
+ });
+ },
+ });
+};
+
+initShowDags();
diff --git a/app/assets/javascripts/pages/projects/commit/show/index.js b/app/assets/javascripts/pages/projects/commit/show/index.js
index 46704d96552..667fd89af55 100644
--- a/app/assets/javascripts/pages/projects/commit/show/index.js
+++ b/app/assets/javascripts/pages/projects/commit/show/index.js
@@ -16,6 +16,7 @@ import syntaxHighlight from '~/syntax_highlight';
import ZenMode from '~/zen_mode';
import '~/sourcegraph/load';
import DiffStats from '~/diffs/components/diff_stats.vue';
+import { initReportAbuse } from '~/projects/report_abuse';
const hasPerfBar = document.querySelector('.with-performance-bar');
const performanceHeight = hasPerfBar ? 35 : 0;
@@ -26,6 +27,7 @@ new ShortcutsNavigation();
initCommitBoxInfo();
initDeprecatedNotes();
+initReportAbuse();
const loadDiffStats = () => {
const diffStatsElements = document.querySelectorAll('#js-diff-stats');
@@ -67,6 +69,7 @@ if (filesContainer.length) {
handleLocationHash();
new Diff();
loadDiffStats();
+ initReportAbuse();
})
.catch(() => {
createAlert({ message: __('An error occurred while retrieving diff files') });
diff --git a/app/assets/javascripts/pages/projects/edit/index.js b/app/assets/javascripts/pages/projects/edit/index.js
index c0eb2a8fd77..82035008459 100644
--- a/app/assets/javascripts/pages/projects/edit/index.js
+++ b/app/assets/javascripts/pages/projects/edit/index.js
@@ -10,6 +10,8 @@ import initSearchSettings from '~/search_settings';
import initSettingsPanels from '~/settings_panels';
import UserCallout from '~/user_callout';
import initTopicsTokenSelector from '~/projects/settings/topics';
+import { initProjectSelects } from '~/vue_shared/components/entity_select/init_project_selects';
+import initPruneObjectsButton from '~/projects/prune_objects_button';
import initProjectPermissionsSettings from '../shared/permissions';
import initProjectLoadingSpinner from '../shared/save_project_loader';
@@ -17,6 +19,7 @@ initFilePickers();
initConfirmDanger();
initSettingsPanels();
initProjectDeleteButton();
+initPruneObjectsButton();
mountBadgeSettings(PROJECT_BADGE);
new UserCallout({ className: 'js-service-desk-callout' }); // eslint-disable-line no-new
@@ -30,3 +33,4 @@ dirtySubmitFactory(document.querySelectorAll('.js-general-settings-form, .js-mr-
initSearchSettings();
initTopicsTokenSelector();
+initProjectSelects();
diff --git a/app/assets/javascripts/pages/projects/find_file/ref_switcher/index.js b/app/assets/javascripts/pages/projects/find_file/ref_switcher/index.js
new file mode 100644
index 00000000000..9a3bb25de70
--- /dev/null
+++ b/app/assets/javascripts/pages/projects/find_file/ref_switcher/index.js
@@ -0,0 +1,38 @@
+import Vue from 'vue';
+import { s__ } from '~/locale';
+import Translate from '~/vue_shared/translate';
+import RefSelector from '~/ref/components/ref_selector.vue';
+import { visitUrl } from '~/lib/utils/url_utility';
+import { generateRefDestinationPath } from './ref_switcher_utils';
+
+Vue.use(Translate);
+
+const REF_SWITCH_HEADER = s__('FindFile|Switch branch/tag');
+
+export default () => {
+ const el = document.getElementById('js-blob-ref-switcher');
+ if (!el) return false;
+
+ const { projectId, ref, namespace } = el.dataset;
+
+ return new Vue({
+ el,
+ render(createElement) {
+ return createElement(RefSelector, {
+ props: {
+ projectId,
+ value: ref,
+ translations: {
+ dropdownHeader: REF_SWITCH_HEADER,
+ searchPlaceholder: REF_SWITCH_HEADER,
+ },
+ },
+ on: {
+ input(selected) {
+ visitUrl(generateRefDestinationPath(selected, namespace));
+ },
+ },
+ });
+ },
+ });
+};
diff --git a/app/assets/javascripts/pages/projects/find_file/ref_switcher/ref_switcher_utils.js b/app/assets/javascripts/pages/projects/find_file/ref_switcher/ref_switcher_utils.js
new file mode 100644
index 00000000000..5fecd024f1a
--- /dev/null
+++ b/app/assets/javascripts/pages/projects/find_file/ref_switcher/ref_switcher_utils.js
@@ -0,0 +1,28 @@
+import { joinPaths } from '~/lib/utils/url_utility';
+
+/**
+ * Generates a ref destination url based on the selected ref and current url.
+ * @param {string} selectedRef - The selected ref from the ref dropdown.
+ * @param {string} namespace - The destination namespace for the path.
+ */
+export function generateRefDestinationPath(selectedRef, namespace) {
+ if (!selectedRef || !namespace) {
+ return window.location.href;
+ }
+
+ const { pathname } = window.location;
+ const encodedHash = '%23';
+
+ const [projectRootPath] = pathname.split(namespace);
+
+ const destinationPath = joinPaths(
+ projectRootPath,
+ namespace,
+ encodeURI(selectedRef).replace(/#/g, encodedHash),
+ );
+
+ const newURL = new URL(window.location);
+ newURL.pathname = destinationPath;
+
+ return newURL.href;
+}
diff --git a/app/assets/javascripts/pages/projects/find_file/show/index.js b/app/assets/javascripts/pages/projects/find_file/show/index.js
index f47888f0cb8..e207df2434b 100644
--- a/app/assets/javascripts/pages/projects/find_file/show/index.js
+++ b/app/assets/javascripts/pages/projects/find_file/show/index.js
@@ -1,7 +1,9 @@
import $ from 'jquery';
import ShortcutsFindFile from '~/behaviors/shortcuts/shortcuts_find_file';
import ProjectFindFile from '~/projects/project_find_file';
+import InitBlobRefSwitcher from '../ref_switcher';
+InitBlobRefSwitcher();
const findElement = document.querySelector('.js-file-finder');
const projectFindFile = new ProjectFindFile($('.file-finder-holder'), {
url: findElement.dataset.fileFindUrl,
diff --git a/app/assets/javascripts/pages/projects/forks/new/components/fork_form.vue b/app/assets/javascripts/pages/projects/forks/new/components/fork_form.vue
index 2028af8b8f0..85fe3477d7c 100644
--- a/app/assets/javascripts/pages/projects/forks/new/components/fork_form.vue
+++ b/app/assets/javascripts/pages/projects/forks/new/components/fork_form.vue
@@ -16,7 +16,7 @@ import { createAlert } from '~/flash';
import axios from '~/lib/utils/axios_utils';
import csrf from '~/lib/utils/csrf';
import { redirectTo } from '~/lib/utils/url_utility';
-import { s__ } from '~/locale';
+import { s__, __ } from '~/locale';
import validation from '~/vue_shared/directives/validation';
import {
VISIBILITY_LEVEL_PRIVATE_STRING,
@@ -25,8 +25,24 @@ import {
VISIBILITY_LEVELS_STRING_TO_INTEGER,
VISIBILITY_LEVELS_INTEGER_TO_STRING,
} from '~/visibility_level/constants';
+import { START_RULE, CONTAINS_RULE } from '~/projects/project_name_rules';
import ProjectNamespace from './project_namespace.vue';
+const feedbackMap = {
+ valueMissing: {
+ isInvalid: (el) => el.validity?.valueMissing,
+ message: __('Please fill out this field.'),
+ },
+ nameStartPattern: {
+ isInvalid: (el) => el.validity?.patternMismatch && !START_RULE.reg.test(el.value),
+ message: START_RULE.msg,
+ },
+ nameContainsPattern: {
+ isInvalid: (el) => el.validity?.patternMismatch && !CONTAINS_RULE.reg.test(el.value),
+ message: CONTAINS_RULE.msg,
+ },
+};
+
const initFormField = ({ value, required = true, skipValidation = false }) => ({
value,
required,
@@ -48,7 +64,7 @@ export default {
ProjectNamespace,
},
directives: {
- validation: validation(),
+ validation: validation(feedbackMap),
},
inject: {
newGroupPath: {
@@ -109,6 +125,15 @@ export default {
};
},
computed: {
+ projectNameDescription() {
+ if (this.form.fields.name.state === false) {
+ return null;
+ }
+
+ return s__(
+ 'ProjectsNew|Must start with a lowercase or uppercase letter, digit, emoji, or underscore. Can also contain dots, pluses, dashes, or spaces.',
+ );
+ },
projectVisibilityLevel() {
return VISIBILITY_LEVELS_STRING_TO_INTEGER[this.projectVisibility];
},
@@ -248,6 +273,7 @@ export default {
},
},
csrf,
+ projectNamePattern: `(${START_RULE.reg.source})|(${CONTAINS_RULE.reg.source})`,
};
</script>
@@ -257,8 +283,10 @@ export default {
<gl-form-group
:label="__('Project name')"
+ :description="projectNameDescription"
label-for="fork-name"
:invalid-feedback="form.fields.name.feedback"
+ data-testid="fork-name-form-group"
>
<gl-form-input
id="fork-name"
@@ -268,6 +296,7 @@ export default {
data-testid="fork-name-input"
:state="form.fields.name.state"
required
+ :pattern="$options.projectNamePattern"
/>
</gl-form-group>
diff --git a/app/assets/javascripts/pages/projects/graphs/charts/index.js b/app/assets/javascripts/pages/projects/graphs/charts/index.js
index 65e7f48ed24..10c794c9ba2 100644
--- a/app/assets/javascripts/pages/projects/graphs/charts/index.js
+++ b/app/assets/javascripts/pages/projects/graphs/charts/index.js
@@ -2,6 +2,9 @@ import { GlColumnChart } from '@gitlab/ui/dist/charts';
import Vue from 'vue';
import { waitForCSSLoaded } from '~/helpers/startup_css_helper';
import { __ } from '~/locale';
+import { visitUrl } from '~/lib/utils/url_utility';
+import { REF_TYPE_BRANCHES, REF_TYPE_TAGS } from '~/ref/constants';
+import RefSelector from '~/ref/components/ref_selector.vue';
import CodeCoverage from '../components/code_coverage.vue';
import SeriesDataMixin from './series_data_mixin';
@@ -13,6 +16,7 @@ waitForCSSLoaded(() => {
const monthContainer = document.getElementById('js-month-chart');
const weekdayContainer = document.getElementById('js-weekday-chart');
const hourContainer = document.getElementById('js-hour-chart');
+ const branchSelector = document.getElementById('js-project-graph-ref-switcher');
const LANGUAGE_CHART_HEIGHT = 300;
const reorderWeekDays = (weekDays, firstDayOfWeek = 0) => {
if (firstDayOfWeek === 0) {
@@ -173,4 +177,38 @@ waitForCSSLoaded(() => {
});
},
});
+
+ const { projectId, projectBranch, graphPath } = branchSelector.dataset;
+
+ const GRAPHS_PATH_REGEX = /^(.*?)\/-\/graphs/g;
+ const graphsPathPrefix = graphPath.match(GRAPHS_PATH_REGEX)?.[0];
+ if (!graphsPathPrefix) {
+ // eslint-disable-next-line @gitlab/require-i18n-strings
+ throw new Error('Path is not correct');
+ }
+
+ // eslint-disable-next-line no-new
+ new Vue({
+ el: branchSelector,
+ name: 'RefSelector',
+ render(createComponent) {
+ return createComponent(RefSelector, {
+ props: {
+ enabledRefTypes: [REF_TYPE_BRANCHES, REF_TYPE_TAGS],
+ value: projectBranch,
+ translations: {
+ dropdownHeader: __('Switch branch/tag'),
+ searchPlaceholder: __('Search branches and tags'),
+ },
+ projectId,
+ },
+ class: 'gl-w-20',
+ on: {
+ input(selected) {
+ visitUrl(`${graphsPathPrefix}/${encodeURIComponent(selected)}/charts`);
+ },
+ },
+ });
+ },
+ });
});
diff --git a/app/assets/javascripts/pages/projects/hooks/index.js b/app/assets/javascripts/pages/projects/hooks/index.js
index 9e559354205..f25547f9982 100644
--- a/app/assets/javascripts/pages/projects/hooks/index.js
+++ b/app/assets/javascripts/pages/projects/hooks/index.js
@@ -1,7 +1,8 @@
import initSearchSettings from '~/search_settings';
-import initWebhookForm from '~/webhooks';
+import initWebhookForm, { initHookTestDropdowns } from '~/webhooks';
import { initPushEventsEditForm } from '~/webhooks/webhook';
initSearchSettings();
initWebhookForm();
initPushEventsEditForm();
+initHookTestDropdowns();
diff --git a/app/assets/javascripts/pages/projects/index.js b/app/assets/javascripts/pages/projects/index.js
index 37cf345fe77..1075241e172 100644
--- a/app/assets/javascripts/pages/projects/index.js
+++ b/app/assets/javascripts/pages/projects/index.js
@@ -1,7 +1,5 @@
import ShortcutsNavigation from '~/behaviors/shortcuts/shortcuts_navigation';
-import initTerraformNotification from '~/projects/terraform_notification';
import Project from './project';
new Project(); // eslint-disable-line no-new
new ShortcutsNavigation(); // eslint-disable-line no-new
-initTerraformNotification();
diff --git a/app/assets/javascripts/pages/projects/learn_gitlab/components/included_in_trial_indicator.vue b/app/assets/javascripts/pages/projects/learn_gitlab/components/included_in_trial_indicator.vue
deleted file mode 100644
index 693dc6a15ad..00000000000
--- a/app/assets/javascripts/pages/projects/learn_gitlab/components/included_in_trial_indicator.vue
+++ /dev/null
@@ -1,15 +0,0 @@
-<script>
-import { s__ } from '~/locale';
-
-export default {
- name: 'IncludedInTrialIndicator',
- i18n: {
- trialOnly: s__('LearnGitlab|- Included in trial'),
- },
-};
-</script>
-<template>
- <span class="gl-font-style-italic gl-text-gray-500" data-testid="trial-only">
- {{ $options.i18n.trialOnly }}
- </span>
-</template>
diff --git a/app/assets/javascripts/pages/projects/learn_gitlab/components/learn_gitlab.vue b/app/assets/javascripts/pages/projects/learn_gitlab/components/learn_gitlab.vue
deleted file mode 100644
index 54e15b6552c..00000000000
--- a/app/assets/javascripts/pages/projects/learn_gitlab/components/learn_gitlab.vue
+++ /dev/null
@@ -1,146 +0,0 @@
-<script>
-import { GlProgressBar, GlSprintf, GlAlert } from '@gitlab/ui';
-import eventHub from '~/invite_members/event_hub';
-import { s__ } from '~/locale';
-import { getCookie, removeCookie, parseBoolean } from '~/lib/utils/common_utils';
-import { ACTION_LABELS, ACTION_SECTIONS, INVITE_MODAL_OPEN_COOKIE } from '../constants';
-import LearnGitlabSectionCard from './learn_gitlab_section_card.vue';
-
-export default {
- components: { GlProgressBar, GlSprintf, GlAlert, LearnGitlabSectionCard },
- i18n: {
- title: s__('LearnGitLab|Learn GitLab'),
- description: s__(
- 'LearnGitLab|Ready to get started with GitLab? Follow these steps to set up your workspace, plan and commit changes, and deploy your project.',
- ),
- percentageCompleted: s__(`LearnGitLab|%{percentage}%{percentSymbol} completed`),
- successfulInvitations: s__(
- "LearnGitLab|Your team is growing! You've successfully invited new team members to the %{projectName} project.",
- ),
- },
- props: {
- actions: {
- required: true,
- type: Object,
- },
- sections: {
- required: true,
- type: Object,
- },
- project: {
- required: true,
- type: Object,
- },
- },
- data() {
- return {
- showSuccessfulInvitationsAlert: false,
- actionsData: this.actions,
- };
- },
- actionSections: Object.keys(ACTION_SECTIONS),
- computed: {
- maxValue() {
- return Object.keys(this.actionsData).length;
- },
- progressValue() {
- return Object.values(this.actionsData).filter((a) => a.completed).length;
- },
- progressPercentage() {
- return Math.round((this.progressValue / this.maxValue) * 100);
- },
- },
- mounted() {
- if (this.getCookieForInviteMembers()) {
- this.openInviteMembersModal('celebrate');
- }
-
- eventHub.$on('showSuccessfulInvitationsAlert', this.handleShowSuccessfulInvitationsAlert);
- },
- beforeDestroy() {
- eventHub.$off('showSuccessfulInvitationsAlert', this.handleShowSuccessfulInvitationsAlert);
- },
- methods: {
- getCookieForInviteMembers() {
- const value = parseBoolean(getCookie(INVITE_MODAL_OPEN_COOKIE));
-
- removeCookie(INVITE_MODAL_OPEN_COOKIE);
-
- return value;
- },
- openInviteMembersModal(mode) {
- eventHub.$emit('openModal', { mode, source: 'learn-gitlab' });
- },
- handleShowSuccessfulInvitationsAlert() {
- this.showSuccessfulInvitationsAlert = true;
- this.markActionAsCompleted('userAdded');
- },
- actionsFor(section) {
- const actions = Object.fromEntries(
- Object.entries(this.actionsData).filter(
- ([action]) => ACTION_LABELS[action].section === section,
- ),
- );
- return actions;
- },
- svgFor(section) {
- return this.sections[section].svg;
- },
- markActionAsCompleted(completedAction) {
- Object.keys(this.actionsData).forEach((action) => {
- if (action === completedAction) {
- this.actionsData[action].completed = true;
- this.modifySidebarPercentage();
- }
- });
- },
- modifySidebarPercentage() {
- const el = document.querySelector('.sidebar-top-level-items .active .count');
- el.textContent = `${this.progressPercentage}%`;
- },
- },
-};
-</script>
-<template>
- <div>
- <gl-alert
- v-if="showSuccessfulInvitationsAlert"
- class="gl-mt-5"
- @dismiss="showSuccessfulInvitationsAlert = false"
- >
- <gl-sprintf :message="$options.i18n.successfulInvitations">
- <template #projectName>
- <strong>{{ project.name }}</strong>
- </template>
- </gl-sprintf>
- </gl-alert>
- <div class="row">
- <div class="gl-mb-7 gl-ml-5">
- <h1 class="gl-font-size-h1">{{ $options.i18n.title }}</h1>
- <p class="gl-text-gray-700 gl-mb-0">{{ $options.i18n.description }}</p>
- </div>
- </div>
- <div class="gl-mb-3">
- <p class="gl-text-gray-500 gl-mb-2" data-testid="completion-percentage">
- <gl-sprintf :message="$options.i18n.percentageCompleted">
- <template #percentage>{{ progressPercentage }}</template>
- <template #percentSymbol>%</template>
- </gl-sprintf>
- </p>
- <gl-progress-bar :value="progressValue" :max="maxValue" />
- </div>
- <div class="row">
- <div
- v-for="section in $options.actionSections"
- :key="section"
- class="gl-mt-5 col-sm-12 col-mb-6 col-lg-4"
- >
- <learn-gitlab-section-card
- :section="section"
- :svg="svgFor(section)"
- :actions="actionsFor(section)"
- />
- </div>
- </div>
- </div>
-</template>
diff --git a/app/assets/javascripts/pages/projects/learn_gitlab/components/learn_gitlab_section_card.vue b/app/assets/javascripts/pages/projects/learn_gitlab/components/learn_gitlab_section_card.vue
deleted file mode 100644
index e8f0e6c47ee..00000000000
--- a/app/assets/javascripts/pages/projects/learn_gitlab/components/learn_gitlab_section_card.vue
+++ /dev/null
@@ -1,56 +0,0 @@
-<script>
-import { GlCard } from '@gitlab/ui';
-import { ACTION_LABELS, ACTION_SECTIONS } from '../constants';
-
-import LearnGitlabSectionLink from './learn_gitlab_section_link.vue';
-
-export default {
- name: 'LearnGitlabSectionCard',
- components: { GlCard, LearnGitlabSectionLink },
- i18n: {
- ...ACTION_SECTIONS,
- },
- props: {
- section: {
- required: true,
- type: String,
- },
- svg: {
- required: true,
- type: String,
- },
- actions: {
- required: true,
- type: Object,
- },
- },
- computed: {
- sortedActions() {
- return Object.entries(this.actions).sort(
- (a1, a2) => ACTION_LABELS[a1[0]].position - ACTION_LABELS[a2[0]].position,
- );
- },
- },
-};
-</script>
-<template>
- <gl-card
- class="gl-pt-0 h-100"
- header-class="gl-bg-white gl-border-0 gl-pb-0"
- body-class="gl-pt-0"
- >
- <template #header>
- <img :src="svg" />
- <h2 class="gl-font-lg gl-mb-3">{{ $options.i18n[section].title }}</h2>
- <p class="gl-text-gray-700 gl-mb-6">{{ $options.i18n[section].description }}</p>
- </template>
- <template #default>
- <learn-gitlab-section-link
- v-for="[action, value] in sortedActions"
- :key="action"
- :action="action"
- :value="value"
- />
- </template>
- </gl-card>
-</template>
diff --git a/app/assets/javascripts/pages/projects/learn_gitlab/components/learn_gitlab_section_link.vue b/app/assets/javascripts/pages/projects/learn_gitlab/components/learn_gitlab_section_link.vue
deleted file mode 100644
index d9b0dbbb9b0..00000000000
--- a/app/assets/javascripts/pages/projects/learn_gitlab/components/learn_gitlab_section_link.vue
+++ /dev/null
@@ -1,151 +0,0 @@
-<script>
-import { uniqueId } from 'lodash';
-import { GlLink, GlIcon, GlButton, GlPopover, GlTooltipDirective as GlTooltip } from '@gitlab/ui';
-import GitlabExperiment from '~/experimentation/components/gitlab_experiment.vue';
-import { isExperimentVariant } from '~/experimentation/utils';
-import eventHub from '~/invite_members/event_hub';
-import { s__, __ } from '~/locale';
-import { ACTION_LABELS } from '../constants';
-import IncludedInTrialIndicator from './included_in_trial_indicator.vue';
-
-export default {
- name: 'LearnGitlabSectionLink',
- components: {
- GlLink,
- GlIcon,
- GlButton,
- GlPopover,
- GitlabExperiment,
- IncludedInTrialIndicator,
- },
- directives: {
- GlTooltip,
- },
- i18n: {
- contactAdmin: s__('LearnGitlab|Contact your administrator to enable this action.'),
- viewAdminList: s__('LearnGitlab|View administrator list'),
- watchHow: __('Watch how'),
- },
- props: {
- action: {
- required: true,
- type: String,
- },
- value: {
- required: true,
- type: Object,
- },
- },
- data() {
- return {
- popoverId: uniqueId('contact-admin-'),
- };
- },
- computed: {
- showInviteModalLink() {
- return (
- this.action === 'userAdded' && isExperimentVariant('invite_for_help_continuous_onboarding')
- );
- },
- openInNewTab() {
- return ACTION_LABELS[this.action]?.openInNewTab === true || this.value.openInNewTab === true;
- },
- popoverText() {
- return this.value.message || this.$options.i18n.contactAdmin;
- },
- },
- methods: {
- openModal() {
- eventHub.$emit('openModal', { source: 'learn_gitlab' });
- },
- actionLabelValue(value) {
- return ACTION_LABELS[this.action][value];
- },
- },
-};
-</script>
-<template>
- <div class="gl-mb-4">
- <div class="flex align-items-center">
- <span v-if="value.completed" class="gl-text-green-500">
- <gl-icon name="check-circle-filled" :size="16" data-testid="completed-icon" />
- {{ actionLabelValue('title') }}
- <included-in-trial-indicator v-if="actionLabelValue('trialRequired')" />
- </span>
- <div v-else-if="showInviteModalLink">
- <gl-link
- data-track-action="click_link"
- :data-track-label="actionLabelValue('trackLabel')"
- data-track-property="Growth::Activation::Experiment::InviteForHelpContinuousOnboarding"
- data-testid="invite-for-help-continuous-onboarding-experiment-link"
- @click="openModal"
- >{{ actionLabelValue('title') }}</gl-link
- >
-
- <included-in-trial-indicator v-if="actionLabelValue('trialRequired')" />
- </div>
- <div v-else-if="value.enabled">
- <gl-link
- :target="openInNewTab ? '_blank' : '_self'"
- :href="value.url"
- data-testid="uncompleted-learn-gitlab-link"
- data-qa-selector="uncompleted_learn_gitlab_link"
- data-track-action="click_link"
- :data-track-label="actionLabelValue('trackLabel')"
- >{{ actionLabelValue('title') }}</gl-link
- >
-
- <included-in-trial-indicator v-if="actionLabelValue('trialRequired')" />
- </div>
- <template v-else>
- <div data-testid="disabled-learn-gitlab-link">{{ actionLabelValue('title') }}</div>
- <gl-button
- :id="popoverId"
- category="tertiary"
- icon="question-o"
- class="ml-auto"
- :aria-label="popoverText"
- size="small"
- data-testid="contact-admin-popover-trigger"
- />
- <gl-popover
- :target="popoverId"
- placement="top"
- triggers="hover focus"
- data-testid="contact-admin-popover"
- >
- <p>{{ popoverText }}</p>
- <gl-link
- :href="value.url"
- class="font-size-inherit"
- data-testid="view-administrator-link-text"
- >
- {{ $options.i18n.viewAdminList }}
- </gl-link>
- </gl-popover>
- </template>
- <gitlab-experiment name="video_tutorials_continuous_onboarding">
- <template #control></template>
- <template #candidate>
- <gl-button
- v-if="actionLabelValue('videoTutorial')"
- v-gl-tooltip
- category="tertiary"
- icon="live-preview"
- :title="$options.i18n.watchHow"
- :aria-label="$options.i18n.watchHow"
- :href="actionLabelValue('videoTutorial')"
- target="_blank"
- class="ml-auto"
- size="small"
- data-testid="video-tutorial-link"
- data-track-action="click_video_link"
- :data-track-label="actionLabelValue('trackLabel')"
- data-track-property="Growth::Conversion::Experiment::LearnGitLab"
- data-track-experiment="video_tutorials_continuous_onboarding"
- />
- </template>
- </gitlab-experiment>
- </div>
- </div>
-</template>
diff --git a/app/assets/javascripts/pages/projects/learn_gitlab/constants/index.js b/app/assets/javascripts/pages/projects/learn_gitlab/constants/index.js
deleted file mode 100644
index cb1a0302d91..00000000000
--- a/app/assets/javascripts/pages/projects/learn_gitlab/constants/index.js
+++ /dev/null
@@ -1,133 +0,0 @@
-import { s__ } from '~/locale';
-
-export const ACTION_LABELS = {
- gitWrite: {
- title: s__('LearnGitLab|Create a repository'),
- actionLabel: s__('LearnGitLab|Create a repository'),
- description: s__('LearnGitLab|Create or import your first repository into your new project.'),
- trackLabel: 'create_a_repository',
- section: 'workspace',
- position: 1,
- },
- userAdded: {
- title: s__('LearnGitLab|Invite your colleagues'),
- actionLabel: s__('LearnGitLab|Invite your colleagues'),
- description: s__(
- 'LearnGitLab|GitLab works best as a team. Invite your colleague to enjoy all features.',
- ),
- trackLabel: 'invite_your_colleagues',
- section: 'workspace',
- position: 0,
- },
- pipelineCreated: {
- title: s__("LearnGitLab|Set up your first project's CI/CD"),
- actionLabel: s__('LearnGitLab|Set up CI/CD'),
- description: s__('LearnGitLab|Save time by automating your integration and deployment tasks.'),
- trackLabel: 'set_up_your_first_project_s_ci_cd',
- section: 'workspace',
- position: 2,
- },
- trialStarted: {
- title: s__('LearnGitLab|Start a free trial of GitLab Ultimate'),
- actionLabel: s__('LearnGitLab|Try GitLab Ultimate for free'),
- description: s__('LearnGitLab|Try all GitLab features for 30 days, no credit card required.'),
- trackLabel: 'start_a_free_trial_of_gitlab_ultimate',
- section: 'workspace',
- position: 3,
- openInNewTab: true,
- },
- codeOwnersEnabled: {
- title: s__('LearnGitLab|Add code owners'),
- actionLabel: s__('LearnGitLab|Add code owners'),
- description: s__(
- 'LearnGitLab|Prevent unexpected changes to important assets by assigning ownership of files and paths.',
- ),
- trackLabel: 'add_code_owners',
- trialRequired: true,
- section: 'workspace',
- position: 4,
- openInNewTab: true,
- videoTutorial: 'https://vimeo.com/670896787',
- },
- requiredMrApprovalsEnabled: {
- title: s__('LearnGitLab|Enable require merge approvals'),
- actionLabel: s__('LearnGitLab|Enable require merge approvals'),
- description: s__('LearnGitLab|Route code reviews to the right reviewers, every time.'),
- trackLabel: 'enable_require_merge_approvals',
- trialRequired: true,
- section: 'workspace',
- position: 5,
- openInNewTab: true,
- videoTutorial: 'https://vimeo.com/670904904',
- },
- mergeRequestCreated: {
- title: s__('LearnGitLab|Submit a merge request (MR)'),
- actionLabel: s__('LearnGitLab|Submit a merge request (MR)'),
- description: s__('LearnGitLab|Review and edit proposed changes to source code.'),
- trackLabel: 'submit_a_merge_request_mr',
- section: 'plan',
- position: 1,
- },
- issueCreated: {
- title: s__('LearnGitLab|Create an issue'),
- actionLabel: s__('LearnGitLab|Create an issue'),
- description: s__(
- 'LearnGitLab|Create/import issues (tickets) to collaborate on ideas and plan work.',
- ),
- trackLabel: 'create_an_issue',
- section: 'plan',
- position: 0,
- },
- securityScanEnabled: {
- title: s__('LearnGitLab|Run a Security scan using CI/CD'),
- actionLabel: s__('LearnGitLab|Run a Security scan using CI/CD'),
- description: s__('LearnGitLab|Scan your code to uncover vulnerabilities before deploying.'),
- trackLabel: 'run_a_security_scan_using_ci_cd',
- section: 'deploy',
- position: 1,
- },
- licenseScanningRun: {
- title: s__('LearnGitLab|Scan dependencies for licenses'),
- trackLabel: 'scan_dependencies_for_licenses',
- trialRequired: true,
- section: 'deploy',
- position: 2,
- },
- secureDependencyScanningRun: {
- title: s__('LearnGitLab|Scan dependencies for vulnerabilities'),
- trackLabel: 'scan_dependencies_for_vulnerabilities',
- trialRequired: true,
- section: 'deploy',
- position: 3,
- },
- secureDastRun: {
- title: s__('LearnGitLab|Analyze your application for vulnerabilities with DAST'),
- trackLabel: 'analyze_your_application_for_vulnerabilities_with_dast',
- trialRequired: true,
- section: 'deploy',
- position: 4,
- },
-};
-
-export const ACTION_SECTIONS = {
- workspace: {
- title: s__('LearnGitLab|Set up your workspace'),
- description: s__(
- "LearnGitLab|Complete these tasks first so you can enjoy GitLab's features to their fullest:",
- ),
- },
- plan: {
- title: s__('LearnGitLab|Plan and execute'),
- description: s__(
- 'LearnGitLab|Create a workflow for your new workspace, and learn how GitLab features work together:',
- ),
- },
- deploy: {
- title: s__('LearnGitLab|Deploy'),
- description: s__(
- 'LearnGitLab|Use your new GitLab workflow to deploy your application, monitor its health, and keep it secure:',
- ),
- },
-};
-
-export const INVITE_MODAL_OPEN_COOKIE = 'confetti_post_signup';
diff --git a/app/assets/javascripts/pages/projects/learn_gitlab/index/index.js b/app/assets/javascripts/pages/projects/learn_gitlab/index/index.js
deleted file mode 100644
index af4a6f8a0c9..00000000000
--- a/app/assets/javascripts/pages/projects/learn_gitlab/index/index.js
+++ /dev/null
@@ -1,31 +0,0 @@
-import Vue from 'vue';
-import initInviteMembersModal from '~/invite_members/init_invite_members_modal';
-import initInviteMembersTrigger from '~/invite_members/init_invite_members_trigger';
-import { convertObjectPropsToCamelCase } from '~/lib/utils/common_utils';
-import LearnGitlab from '../components/learn_gitlab.vue';
-
-function initLearnGitlab() {
- const el = document.getElementById('js-learn-gitlab-app');
-
- if (!el) {
- return false;
- }
-
- const actions = convertObjectPropsToCamelCase(JSON.parse(el.dataset.actions));
- const sections = convertObjectPropsToCamelCase(JSON.parse(el.dataset.sections));
- const project = convertObjectPropsToCamelCase(JSON.parse(el.dataset.project));
-
- return new Vue({
- el,
- render(createElement) {
- return createElement(LearnGitlab, {
- props: { actions, sections, project },
- });
- },
- });
-}
-
-initInviteMembersModal();
-initInviteMembersTrigger();
-
-initLearnGitlab();
diff --git a/app/assets/javascripts/pages/projects/merge_requests/creations/new/compare.js b/app/assets/javascripts/pages/projects/merge_requests/creations/new/compare.js
deleted file mode 100644
index 653f903c6d1..00000000000
--- a/app/assets/javascripts/pages/projects/merge_requests/creations/new/compare.js
+++ /dev/null
@@ -1,66 +0,0 @@
-import $ from 'jquery';
-import axios from '~/lib/utils/axios_utils';
-import { localTimeAgo } from '~/lib/utils/datetime_utility';
-import initCompareAutocomplete from './compare_autocomplete';
-import initTargetProjectDropdown from './target_project_dropdown';
-
-const updateCommitList = (url, $emptyState, $loadingIndicator, $commitList, params) => {
- $emptyState.hide();
- $loadingIndicator.show();
- $commitList.empty();
-
- return axios
- .get(url, {
- params,
- })
- .then(({ data }) => {
- $loadingIndicator.hide();
- $commitList.html(data);
- localTimeAgo($commitList.get(0).querySelectorAll('.js-timeago'));
-
- if (!data) {
- $emptyState.show();
- }
- });
-};
-
-export default (mrNewCompareNode) => {
- const { sourceBranchUrl, targetBranchUrl } = mrNewCompareNode.dataset;
-
- if (!window.gon?.features?.mrCompareDropdowns) {
- initTargetProjectDropdown();
- }
-
- const updateSourceBranchCommitList = () =>
- updateCommitList(
- sourceBranchUrl,
- $(mrNewCompareNode).find('.js-source-commit-empty'),
- $(mrNewCompareNode).find('.js-source-loading'),
- $(mrNewCompareNode).find('.mr_source_commit'),
- {
- ref: $(mrNewCompareNode).find("input[name='merge_request[source_branch]']").val(),
- },
- );
- const updateTargetBranchCommitList = () =>
- updateCommitList(
- targetBranchUrl,
- $(mrNewCompareNode).find('.js-target-commit-empty'),
- $(mrNewCompareNode).find('.js-target-loading'),
- $(mrNewCompareNode).find('.mr_target_commit'),
- {
- target_project_id: $(mrNewCompareNode)
- .find("input[name='merge_request[target_project_id]']")
- .val(),
- ref: $(mrNewCompareNode).find("input[name='merge_request[target_branch]']").val(),
- },
- );
- initCompareAutocomplete('branches', ($dropdown) => {
- if ($dropdown.is('.js-target-branch')) {
- updateTargetBranchCommitList();
- } else if ($dropdown.is('.js-source-branch')) {
- updateSourceBranchCommitList();
- }
- });
- updateSourceBranchCommitList();
- updateTargetBranchCommitList();
-};
diff --git a/app/assets/javascripts/pages/projects/merge_requests/creations/new/compare_autocomplete.js b/app/assets/javascripts/pages/projects/merge_requests/creations/new/compare_autocomplete.js
deleted file mode 100644
index 65942464e2b..00000000000
--- a/app/assets/javascripts/pages/projects/merge_requests/creations/new/compare_autocomplete.js
+++ /dev/null
@@ -1,91 +0,0 @@
-/* eslint-disable func-names */
-
-import $ from 'jquery';
-import initDeprecatedJQueryDropdown from '~/deprecated_jquery_dropdown';
-import { createAlert } from '~/flash';
-import axios from '~/lib/utils/axios_utils';
-import { capitalizeFirstCharacter } from '~/lib/utils/text_utility';
-import { __ } from '~/locale';
-import { fixTitle } from '~/tooltips';
-
-export default function initCompareAutocomplete(limitTo = null, clickHandler = () => {}) {
- $('.js-compare-dropdown').each(function () {
- const $dropdown = $(this);
- const selected = $dropdown.data('selected');
- const defaultText = $dropdown.data('defaultText').trim();
- const $dropdownContainer = $dropdown.closest('.dropdown');
- const $fieldInput = $(`input[name="${$dropdown.data('fieldName')}"]`, $dropdownContainer);
- const $filterInput = $('input[type="search"]', $dropdownContainer);
- initDeprecatedJQueryDropdown($dropdown, {
- data(term, callback) {
- const params = {
- ref: $dropdown.data('ref'),
- search: term,
- };
-
- if (limitTo) {
- params.find = limitTo;
- }
-
- axios
- .get($dropdown.data('refsUrl'), {
- params,
- })
- .then(({ data }) => {
- if (limitTo) {
- callback(data[capitalizeFirstCharacter(limitTo)] || []);
- } else {
- callback(data);
- }
- })
- .catch(() =>
- createAlert({
- message: __('Error fetching refs'),
- }),
- );
- },
- selectable: true,
- filterable: true,
- filterRemote: Boolean($dropdown.data('refsUrl')),
- fieldName: $dropdown.data('fieldName'),
- filterInput: 'input[type="search"]',
- renderRow(ref) {
- const link = $('<a />')
- .attr('href', '#')
- .addClass(ref === selected ? 'is-active' : '')
- .text(ref)
- .attr('data-ref', ref);
- if (ref.header != null) {
- return $('<li />').addClass('dropdown-header').text(ref.header);
- }
- return $('<li />').append(link);
- },
- id(obj, $el) {
- return $el.attr('data-ref');
- },
- toggleLabel(obj, $el) {
- if ($el.hasClass('is-active')) {
- return $el.text().trim();
- }
-
- return defaultText;
- },
- clicked: () => clickHandler($dropdown),
- });
- $filterInput.on('keyup', (e) => {
- const keyCode = e.keyCode || e.which;
- if (keyCode !== 13) return;
- const text = $filterInput.val();
- $fieldInput.val(text);
- $('.dropdown-toggle-text', $dropdown).text(text);
- $dropdownContainer.removeClass('open');
- });
-
- $dropdownContainer.on('click', '.dropdown-content a', (e) => {
- $dropdown.prop('title', e.target.text.replace(/_+?/g, '-'));
- if ($dropdown.hasClass('has-tooltip')) {
- fixTitle($dropdown);
- }
- });
- });
-}
diff --git a/app/assets/javascripts/pages/projects/merge_requests/creations/new/index.js b/app/assets/javascripts/pages/projects/merge_requests/creations/new/index.js
index b3868653d6a..2718765ee23 100644
--- a/app/assets/javascripts/pages/projects/merge_requests/creations/new/index.js
+++ b/app/assets/javascripts/pages/projects/merge_requests/creations/new/index.js
@@ -1,35 +1,78 @@
-import $ from 'jquery';
import Vue from 'vue';
import initPipelines from '~/commit/pipelines/pipelines_bundle';
import MergeRequest from '~/merge_request';
-import TargetProjectDropdown from '~/merge_requests/components/target_project_dropdown.vue';
-import initCompare from './compare';
+import CompareApp from '~/merge_requests/components/compare_app.vue';
+import { __ } from '~/locale';
const mrNewCompareNode = document.querySelector('.js-merge-request-new-compare');
if (mrNewCompareNode) {
- initCompare(mrNewCompareNode);
-
- const el = document.getElementById('js-target-project-dropdown');
- const { targetProjectsPath, currentProject } = el.dataset;
+ const targetCompareEl = document.getElementById('js-target-project-dropdown');
+ const sourceCompareEl = document.getElementById('js-source-project-dropdown');
+ const compareEl = document.querySelector('.js-merge-request-new-compare');
// eslint-disable-next-line no-new
new Vue({
- el,
- name: 'TargetProjectDropdown',
+ el: sourceCompareEl,
+ name: 'SourceCompareApp',
provide: {
- targetProjectsPath,
- currentProject: JSON.parse(currentProject),
+ currentProject: JSON.parse(sourceCompareEl.dataset.currentProject),
+ currentBranch: JSON.parse(sourceCompareEl.dataset.currentBranch),
+ branchCommitPath: compareEl.dataset.sourceBranchUrl,
+ inputs: {
+ project: {
+ id: 'merge_request_source_project_id',
+ name: 'merge_request[source_project_id]',
+ },
+ branch: {
+ id: 'merge_request_source_branch',
+ name: 'merge_request[source_branch]',
+ },
+ },
+ i18n: {
+ projectHeaderText: __('Select source project'),
+ branchHeaderText: __('Select source branch'),
+ },
+ toggleClass: {
+ project: 'js-source-project',
+ branch: 'js-source-branch gl-font-monospace',
+ },
+ branchQaSelector: 'source_branch_dropdown',
},
render(h) {
- return h(TargetProjectDropdown, {
- on: {
- 'project-selected': function projectSelectedFunction(refsUrl) {
- const $targetBranchDropdown = $('.js-target-branch');
- $targetBranchDropdown.data('refsUrl', refsUrl);
- $targetBranchDropdown.data('deprecatedJQueryDropdown').clearMenu();
- },
+ return h(CompareApp);
+ },
+ });
+
+ // eslint-disable-next-line no-new
+ new Vue({
+ el: targetCompareEl,
+ name: 'TargetCompareApp',
+ provide: {
+ currentProject: JSON.parse(targetCompareEl.dataset.currentProject),
+ currentBranch: JSON.parse(targetCompareEl.dataset.currentBranch),
+ projectsPath: targetCompareEl.dataset.targetProjectsPath,
+ branchCommitPath: compareEl.dataset.targetBranchUrl,
+ inputs: {
+ project: {
+ id: 'merge_request_target_project_id',
+ name: 'merge_request[target_project_id]',
},
- });
+ branch: {
+ id: 'merge_request_target_branch',
+ name: 'merge_request[target_branch]',
+ },
+ },
+ i18n: {
+ projectHeaderText: __('Select target project'),
+ branchHeaderText: __('Select target branch'),
+ },
+ toggleClass: {
+ project: 'js-target-project',
+ branch: 'js-target-branch gl-font-monospace',
+ },
+ },
+ render(h) {
+ return h(CompareApp);
},
});
} else {
diff --git a/app/assets/javascripts/pages/projects/merge_requests/creations/new/target_project_dropdown.js b/app/assets/javascripts/pages/projects/merge_requests/creations/new/target_project_dropdown.js
deleted file mode 100644
index e9f0e008435..00000000000
--- a/app/assets/javascripts/pages/projects/merge_requests/creations/new/target_project_dropdown.js
+++ /dev/null
@@ -1,23 +0,0 @@
-import $ from 'jquery';
-import initDeprecatedJQueryDropdown from '~/deprecated_jquery_dropdown';
-
-export default () => {
- const $targetProjectDropdown = $('.js-target-project');
- initDeprecatedJQueryDropdown($targetProjectDropdown, {
- selectable: true,
- fieldName: $targetProjectDropdown.data('fieldName'),
- filterable: true,
- id(obj, $el) {
- return $el.data('id');
- },
- toggleLabel(obj, $el) {
- return $el.text().trim();
- },
- clicked({ $el }) {
- $('.mr_target_commit').empty();
- const $targetBranchDropdown = $('.js-target-branch');
- $targetBranchDropdown.data('refsUrl', $el.data('refsUrl'));
- $targetBranchDropdown.data('deprecatedJQueryDropdown').clearMenu();
- },
- });
-};
diff --git a/app/assets/javascripts/pages/projects/merge_requests/index/index.js b/app/assets/javascripts/pages/projects/merge_requests/index/index.js
index b3a09cc0be3..af75c05b300 100644
--- a/app/assets/javascripts/pages/projects/merge_requests/index/index.js
+++ b/app/assets/javascripts/pages/projects/merge_requests/index/index.js
@@ -5,7 +5,6 @@ import { FILTERED_SEARCH } from '~/filtered_search/constants';
import { initBulkUpdateSidebar, initCsvImportExportButtons, initIssuableByEmail } from '~/issuable';
import { ISSUABLE_INDEX } from '~/issuable/constants';
import initFilteredSearch from '~/pages/search/init_filtered_search';
-import UsersSelect from '~/users_select';
initBulkUpdateSidebar(ISSUABLE_INDEX.MERGE_REQUEST);
@@ -18,7 +17,6 @@ initFilteredSearch({
useDefaultState: true,
});
-new UsersSelect(); // eslint-disable-line no-new
new ShortcutsNavigation(); // eslint-disable-line no-new
initIssuableByEmail();
diff --git a/app/assets/javascripts/pages/projects/merge_requests/show/index.js b/app/assets/javascripts/pages/projects/merge_requests/show/index.js
index f0a955e5360..91394755367 100644
--- a/app/assets/javascripts/pages/projects/merge_requests/show/index.js
+++ b/app/assets/javascripts/pages/projects/merge_requests/show/index.js
@@ -1,5 +1,5 @@
import initNotesApp from '~/mr_notes/init_notes';
-import { initReportAbuse } from '~/projects/merge_requests';
+import { initReportAbuse } from '~/projects/report_abuse';
import { initMrPage } from '../page';
initMrPage();
diff --git a/app/assets/javascripts/pages/projects/ml/candidates/show/index.js b/app/assets/javascripts/pages/projects/ml/candidates/show/index.js
index c1acef5ac13..fee6258eddc 100644
--- a/app/assets/javascripts/pages/projects/ml/candidates/show/index.js
+++ b/app/assets/javascripts/pages/projects/ml/candidates/show/index.js
@@ -1,27 +1,4 @@
-import Vue from 'vue';
+import { initSimpleApp } from '~/helpers/init_simple_app_helper';
import MlCandidate from '~/ml/experiment_tracking/components/ml_candidate.vue';
-const initShowCandidate = () => {
- const element = document.querySelector('#js-show-ml-candidate');
- if (!element) {
- return;
- }
-
- const container = document.createElement('div');
- element.appendChild(container);
-
- const candidate = JSON.parse(element.dataset.candidate);
-
- // eslint-disable-next-line no-new
- new Vue({
- el: container,
- provide: {
- candidate,
- },
- render(h) {
- return h(MlCandidate);
- },
- });
-};
-
-initShowCandidate();
+initSimpleApp('#js-show-ml-candidate', MlCandidate);
diff --git a/app/assets/javascripts/pages/projects/ml/experiments/index/index.js b/app/assets/javascripts/pages/projects/ml/experiments/index/index.js
new file mode 100644
index 00000000000..e9ffd4b528b
--- /dev/null
+++ b/app/assets/javascripts/pages/projects/ml/experiments/index/index.js
@@ -0,0 +1,24 @@
+import Vue from 'vue';
+import MlExperimentsIndex from '~/ml/experiment_tracking/routes/experiments/index';
+import { convertObjectPropsToCamelCase } from '~/lib/utils/common_utils';
+
+const initIndexMlExperiments = () => {
+ const element = document.querySelector('#js-project-ml-experiments-index');
+ if (!element) {
+ return undefined;
+ }
+
+ const props = {
+ experiments: JSON.parse(element.dataset.experiments),
+ pageInfo: convertObjectPropsToCamelCase(JSON.parse(element.dataset.pageInfo)),
+ };
+
+ return new Vue({
+ el: element,
+ render(h) {
+ return h(MlExperimentsIndex, { props });
+ },
+ });
+};
+
+initIndexMlExperiments();
diff --git a/app/assets/javascripts/pages/projects/ml/experiments/show/index.js b/app/assets/javascripts/pages/projects/ml/experiments/show/index.js
index 6947b15dcbe..0e64d8c17db 100644
--- a/app/assets/javascripts/pages/projects/ml/experiments/show/index.js
+++ b/app/assets/javascripts/pages/projects/ml/experiments/show/index.js
@@ -14,7 +14,7 @@ const initShowExperiment = () => {
const candidates = JSON.parse(element.dataset.candidates);
const metricNames = JSON.parse(element.dataset.metrics);
const paramNames = JSON.parse(element.dataset.params);
- const pagination = convertObjectPropsToCamelCase(JSON.parse(element.dataset.pagination));
+ const pageInfo = convertObjectPropsToCamelCase(JSON.parse(element.dataset.pageInfo));
// eslint-disable-next-line no-new
new Vue({
@@ -23,7 +23,7 @@ const initShowExperiment = () => {
candidates,
metricNames,
paramNames,
- pagination,
+ pageInfo,
},
render(h) {
return h(MlExperiment);
diff --git a/app/assets/javascripts/pages/projects/network/show/index.js b/app/assets/javascripts/pages/projects/network/show/index.js
index 2dabcfadfab..414636f0a74 100644
--- a/app/assets/javascripts/pages/projects/network/show/index.js
+++ b/app/assets/javascripts/pages/projects/network/show/index.js
@@ -1,7 +1,39 @@
import $ from 'jquery';
+import Vue from 'vue';
+import { visitUrl, joinPaths } from '~/lib/utils/url_utility';
import ShortcutsNetwork from '~/behaviors/shortcuts/shortcuts_network';
+import RefSelector from '~/ref/components/ref_selector.vue';
import Network from '../network';
+const initRefSwitcher = () => {
+ const refSwitcherEl = document.getElementById('js-graph-ref-switcher');
+ const NETWORK_PATH_REGEX = /^(.*?)\/-\/network/g;
+
+ if (!refSwitcherEl) return false;
+
+ const { projectId, ref, networkPath } = refSwitcherEl.dataset;
+ const networkRootPath = networkPath.match(NETWORK_PATH_REGEX)?.[0]; // gets the network path without the ref
+
+ return new Vue({
+ el: refSwitcherEl,
+ render(createElement) {
+ return createElement(RefSelector, {
+ props: {
+ projectId,
+ value: ref,
+ },
+ on: {
+ input(selectedRef) {
+ visitUrl(joinPaths(networkRootPath, selectedRef));
+ },
+ },
+ });
+ },
+ });
+};
+
+initRefSwitcher();
+
(() => {
if (!$('.network-graph').length) return;
diff --git a/app/assets/javascripts/pages/projects/project.js b/app/assets/javascripts/pages/projects/project.js
index 4c9eb830ff6..5773737c41b 100644
--- a/app/assets/javascripts/pages/projects/project.js
+++ b/app/assets/javascripts/pages/projects/project.js
@@ -9,7 +9,6 @@ import axios from '~/lib/utils/axios_utils';
import { serializeForm } from '~/lib/utils/forms';
import { mergeUrlParams } from '~/lib/utils/url_utility';
import { __ } from '~/locale';
-import projectSelect from '~/project_select';
const BRANCH_REF_TYPE = 'heads';
const TAG_REF_TYPE = 'tags';
@@ -44,13 +43,6 @@ export default class Project {
$(this).parents('.auto-devops-implicitly-enabled-banner').remove();
return e.preventDefault();
});
-
- Project.projectSelectDropdown();
- }
-
- static projectSelectDropdown() {
- projectSelect();
- $('.project-item-select').on('click', (e) => Project.changeProject($(e.currentTarget).val()));
}
static changeProject(url) {
diff --git a/app/assets/javascripts/pages/projects/project_members/index.js b/app/assets/javascripts/pages/projects/project_members/index.js
index 2fd372a45b8..79a4ed0f9c3 100644
--- a/app/assets/javascripts/pages/projects/project_members/index.js
+++ b/app/assets/javascripts/pages/projects/project_members/index.js
@@ -21,7 +21,6 @@ const SHARED_FIELDS = ['account', 'maxRole', 'expiration', 'actions'];
initMembersApp(document.querySelector('.js-project-members-list-app'), {
[MEMBER_TYPES.user]: {
tableFields: SHARED_FIELDS.concat(['source', 'activity']),
- tableAttrs: { tr: { 'data-qa-selector': 'member_row' } },
tableSortableFields: [
'account',
'granted',
@@ -41,10 +40,6 @@ initMembersApp(document.querySelector('.js-project-members-list-app'), {
},
[MEMBER_TYPES.group]: {
tableFields: SHARED_FIELDS.concat(['source', 'granted']),
- tableAttrs: {
- table: { 'data-qa-selector': 'groups_list' },
- tr: { 'data-qa-selector': 'group_row' },
- },
requestFormatter: groupLinkRequestFormatter,
filteredSearchBar: {
show: true,
diff --git a/app/assets/javascripts/pages/projects/settings/ci_cd/show/index.js b/app/assets/javascripts/pages/projects/settings/ci_cd/show/index.js
index 895c7d0a18e..964c6ca9792 100644
--- a/app/assets/javascripts/pages/projects/settings/ci_cd/show/index.js
+++ b/app/assets/javascripts/pages/projects/settings/ci_cd/show/index.js
@@ -4,7 +4,6 @@ import initSettingsPipelinesTriggers from '~/ci_settings_pipeline_triggers';
import initVariableList from '~/ci/ci_variable_list';
import initDeployFreeze from '~/deploy_freeze';
import registrySettingsApp from '~/packages_and_registries/settings/project/registry_settings_bundle';
-import { initRunnerAwsDeployments } from '~/pages/shared/mount_runner_aws_deployments';
import { initInstallRunner } from '~/pages/shared/mount_runner_instructions';
import initSharedRunnersToggle from '~/projects/settings/mount_shared_runners_toggle';
import initSettingsPanels from '~/settings_panels';
@@ -44,7 +43,5 @@ initArtifactsSettings();
initProjectRunners();
initSharedRunnersToggle();
initInstallRunner();
-initRunnerAwsDeployments();
-
initTokenAccess();
initCiSecureFiles();
diff --git a/app/assets/javascripts/pages/projects/shared/permissions/components/settings_panel.vue b/app/assets/javascripts/pages/projects/shared/permissions/components/settings_panel.vue
index 5fa3288bbef..f2bc4796324 100644
--- a/app/assets/javascripts/pages/projects/shared/permissions/components/settings_panel.vue
+++ b/app/assets/javascripts/pages/projects/shared/permissions/components/settings_panel.vue
@@ -49,14 +49,10 @@ export default {
infrastructureLabel: s__('ProjectSettings|Infrastructure'),
infrastructureHelpText: s__('ProjectSettings|Configure your infrastructure.'),
monitorLabel: s__('ProjectSettings|Monitor'),
- packagesHelpText: s__(
- 'ProjectSettings|Every project can have its own space to store its packages. Note: The Package Registry is always visible when a project is public.',
- ),
packageRegistryHelpText: s__('ProjectSettings|Publish, store, and view packages in a project.'),
packageRegistryForEveryoneHelpText: s__(
'ProjectSettings|Anyone can pull packages with a package manager API.',
),
- packagesLabel: s__('ProjectSettings|Packages'),
packageRegistryLabel: s__('ProjectSettings|Package registry'),
packageRegistryForEveryoneLabel: s__(
'ProjectSettings|Allow anyone to pull from Package Registry',
@@ -355,9 +351,6 @@ export default {
this.visibilityLevel < this.currentSettings.visibilityLevel
);
},
- packageRegistryAccessLevelEnabled() {
- return this.glFeatures.packageRegistryAccessLevel;
- },
packageRegistryEnabled() {
return this.packageRegistryAccessLevel > featureAccessLevel.NOT_ENABLED;
},
@@ -392,14 +385,12 @@ export default {
featureAccessLevel.PROJECT_MEMBERS,
this.buildsAccessLevel,
);
- if (this.packageRegistryAccessLevelEnabled) {
- if (
- this.packageRegistryAccessLevel === featureAccessLevel.EVERYONE ||
- (this.packageRegistryAccessLevel > featureAccessLevel.EVERYONE &&
- oldValue === VISIBILITY_LEVEL_PUBLIC_INTEGER)
- ) {
- this.packageRegistryAccessLevel = featureAccessLevel.PROJECT_MEMBERS;
- }
+ if (
+ this.packageRegistryAccessLevel === featureAccessLevel.EVERYONE ||
+ (this.packageRegistryAccessLevel > featureAccessLevel.EVERYONE &&
+ oldValue === VISIBILITY_LEVEL_PUBLIC_INTEGER)
+ ) {
+ this.packageRegistryAccessLevel = featureAccessLevel.PROJECT_MEMBERS;
}
this.wikiAccessLevel = Math.min(featureAccessLevel.PROJECT_MEMBERS, this.wikiAccessLevel);
this.snippetsAccessLevel = Math.min(
@@ -459,10 +450,7 @@ export default {
this.repositoryAccessLevel = featureAccessLevel.EVERYONE;
if (this.mergeRequestsAccessLevel > featureAccessLevel.NOT_ENABLED)
this.mergeRequestsAccessLevel = featureAccessLevel.EVERYONE;
- if (
- this.packageRegistryAccessLevelEnabled &&
- this.packageRegistryAccessLevel === featureAccessLevel.PROJECT_MEMBERS
- ) {
+ if (this.packageRegistryAccessLevel === featureAccessLevel.PROJECT_MEMBERS) {
this.packageRegistryAccessLevel =
PACKAGE_REGISTRY_ACCESS_LEVEL_DEFAULT_BY_PROJECT_VISIBILITY[value];
}
@@ -488,19 +476,17 @@ export default {
this.containerRegistryAccessLevel = featureAccessLevel.EVERYONE;
this.highlightChanges();
- } else if (this.packageRegistryAccessLevelEnabled) {
- if (
- value === VISIBILITY_LEVEL_PUBLIC_INTEGER &&
- this.packageRegistryAccessLevel === featureAccessLevel.EVERYONE
- ) {
- // eslint-disable-next-line prefer-destructuring
- this.packageRegistryAccessLevel = FEATURE_ACCESS_LEVEL_ANONYMOUS[0];
- } else if (
- value === VISIBILITY_LEVEL_INTERNAL_INTEGER &&
- this.packageRegistryAccessLevel === FEATURE_ACCESS_LEVEL_ANONYMOUS[0]
- ) {
- this.packageRegistryAccessLevel = featureAccessLevel.EVERYONE;
- }
+ } else if (
+ value === VISIBILITY_LEVEL_PUBLIC_INTEGER &&
+ this.packageRegistryAccessLevel === featureAccessLevel.EVERYONE
+ ) {
+ // eslint-disable-next-line prefer-destructuring
+ this.packageRegistryAccessLevel = FEATURE_ACCESS_LEVEL_ANONYMOUS[0];
+ } else if (
+ value === VISIBILITY_LEVEL_INTERNAL_INTEGER &&
+ this.packageRegistryAccessLevel === FEATURE_ACCESS_LEVEL_ANONYMOUS[0]
+ ) {
+ this.packageRegistryAccessLevel = featureAccessLevel.EVERYONE;
}
},
@@ -770,22 +756,6 @@ export default {
</p>
</project-setting-row>
<project-setting-row
- v-if="packagesAvailable && !packageRegistryAccessLevelEnabled"
- ref="package-settings"
- :help-path="packagesHelpPath"
- :label="$options.i18n.packagesLabel"
- :help-text="$options.i18n.packagesHelpText"
- >
- <gl-toggle
- v-model="packagesEnabled"
- class="gl-my-2"
- :disabled="!repositoryEnabled"
- :label="$options.i18n.packagesLabel"
- label-position="hidden"
- name="project[packages_enabled]"
- />
- </project-setting-row>
- <project-setting-row
ref="pipeline-settings"
:label="$options.i18n.ciCdLabel"
:help-text="s__('ProjectSettings|Build, test, and deploy your changes.')"
@@ -889,7 +859,7 @@ export default {
/>
</project-setting-row>
<project-setting-row
- v-if="packageRegistryAccessLevelEnabled && packagesAvailable"
+ v-if="packagesAvailable"
:help-path="packagesHelpPath"
:label="$options.i18n.packageRegistryLabel"
:help-text="$options.i18n.packageRegistryHelpText"
diff --git a/app/assets/javascripts/pages/projects/show/index.js b/app/assets/javascripts/pages/projects/show/index.js
index 1de36f4a0fb..33d4090011f 100644
--- a/app/assets/javascripts/pages/projects/show/index.js
+++ b/app/assets/javascripts/pages/projects/show/index.js
@@ -6,6 +6,7 @@ import initClustersDeprecationAlert from '~/projects/clusters_deprecation_alert'
import leaveByUrl from '~/namespaces/leave_by_url';
import initVueNotificationsDropdown from '~/notifications';
import Star from '~/projects/star';
+import initTerraformNotification from '~/projects/terraform_notification';
import { initUploadFileTrigger } from '~/projects/upload_file';
import initReadMore from '~/read_more';
@@ -44,6 +45,7 @@ initUploadFileTrigger();
initInviteMembersModal();
initInviteMembersTrigger();
initClustersDeprecationAlert();
+initTerraformNotification();
initReadMore();
new Star(); // eslint-disable-line no-new
diff --git a/app/assets/javascripts/pages/search/show/refresh_counts.js b/app/assets/javascripts/pages/search/show/refresh_counts.js
deleted file mode 100644
index f3f6312cb7c..00000000000
--- a/app/assets/javascripts/pages/search/show/refresh_counts.js
+++ /dev/null
@@ -1,24 +0,0 @@
-import axios from '~/lib/utils/axios_utils';
-
-function showCount(el, count) {
- el.textContent = count;
- el.classList.remove('hidden');
-}
-
-function refreshCount(el) {
- const { url } = el.dataset;
-
- return axios
- .get(url)
- .then(({ data }) => showCount(el, data.count))
- .catch((e) => {
- // eslint-disable-next-line no-console
- console.error(`Failed to fetch search count from '${url}'.`, e);
- });
-}
-
-export default function refreshCounts() {
- const elements = Array.from(document.querySelectorAll('.js-search-count'));
-
- return Promise.all(elements.map(refreshCount));
-}
diff --git a/app/assets/javascripts/pages/shared/mount_runner_aws_deployments.js b/app/assets/javascripts/pages/shared/mount_runner_aws_deployments.js
deleted file mode 100644
index f3807a33a2b..00000000000
--- a/app/assets/javascripts/pages/shared/mount_runner_aws_deployments.js
+++ /dev/null
@@ -1,17 +0,0 @@
-import Vue from 'vue';
-import RunnerAwsDeployments from '~/vue_shared/components/runner_aws_deployments/runner_aws_deployments.vue';
-
-export function initRunnerAwsDeployments(componentId = 'js-runner-aws-deployments') {
- const el = document.getElementById(componentId);
-
- if (!el) {
- return null;
- }
-
- return new Vue({
- el,
- render(createElement) {
- return createElement(RunnerAwsDeployments);
- },
- });
-}
diff --git a/app/assets/javascripts/pages/shared/wikis/components/wiki_form.vue b/app/assets/javascripts/pages/shared/wikis/components/wiki_form.vue
index 8e2f542aec0..0d2bbfbbc43 100644
--- a/app/assets/javascripts/pages/shared/wikis/components/wiki_form.vue
+++ b/app/assets/javascripts/pages/shared/wikis/components/wiki_form.vue
@@ -119,6 +119,12 @@ export default {
isContentEditorActive: false,
switchEditingControlDisabled: false,
isFormDirty: getIsFormDirty(this.pageInfo),
+ formFieldProps: {
+ placeholder: this.$options.i18n.content.placeholder,
+ 'aria-label': this.$options.i18n.content.label,
+ id: 'wiki_content',
+ name: 'wiki[content]',
+ },
};
},
computed: {
@@ -338,16 +344,13 @@ export default {
<gl-form-group>
<markdown-editor
v-model="content"
+ :form-field-props="formFieldProps"
:render-markdown-path="pageInfo.markdownPreviewPath"
:markdown-docs-path="pageInfo.markdownHelpPath"
:uploads-path="pageInfo.uploadsPath"
:enable-content-editor="isMarkdownFormat"
:enable-preview="isMarkdownFormat"
:autofocus="pageInfo.persisted"
- :form-field-placeholder="$options.i18n.content.placeholder"
- :form-field-aria-label="$options.i18n.content.label"
- form-field-id="wiki_content"
- form-field-name="wiki[content]"
@contentEditor="notifyContentEditorActive"
@markdownField="notifyContentEditorInactive"
@keydown.ctrl.enter="submitFormShortcut"
diff --git a/app/assets/javascripts/pages/users/show/index.js b/app/assets/javascripts/pages/users/show/index.js
new file mode 100644
index 00000000000..f1b4e00c810
--- /dev/null
+++ b/app/assets/javascripts/pages/users/show/index.js
@@ -0,0 +1,16 @@
+import { s__ } from '~/locale';
+import { createAlert } from '~/flash';
+
+if (window.gon.features?.profileTabsVue) {
+ import('~/profile')
+ .then(({ initProfileTabs }) => {
+ initProfileTabs();
+ })
+ .catch(() => {
+ createAlert({
+ message: s__(
+ 'UserProfile|An error occurred loading the profile. Please refresh the page to try again.',
+ ),
+ });
+ });
+}