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-05-17 19:05:49 +0300
committerGitLab Bot <gitlab-bot@gitlab.com>2023-05-17 19:05:49 +0300
commit43a25d93ebdabea52f99b05e15b06250cd8f07d7 (patch)
treedceebdc68925362117480a5d672bcff122fb625b /app/assets/javascripts/projects
parent20c84b99005abd1c82101dfeff264ac50d2df211 (diff)
Add latest changes from gitlab-org/gitlab@16-0-stable-eev16.0.0-rc42
Diffstat (limited to 'app/assets/javascripts/projects')
-rw-r--r--app/assets/javascripts/projects/commit/components/branches_dropdown.vue33
-rw-r--r--app/assets/javascripts/projects/commit/components/commit_options_dropdown.vue2
-rw-r--r--app/assets/javascripts/projects/commit/components/form_modal.vue24
-rw-r--r--app/assets/javascripts/projects/commit/components/projects_dropdown.vue32
-rw-r--r--app/assets/javascripts/projects/commit/init_revert_commit_modal.js1
-rw-r--r--app/assets/javascripts/projects/commit/store/actions.js2
-rw-r--r--app/assets/javascripts/projects/commit_box/info/components/commit_box_pipeline_mini_graph.vue2
-rw-r--r--app/assets/javascripts/projects/commit_box/info/components/commit_box_pipeline_status.vue2
-rw-r--r--app/assets/javascripts/projects/commit_box/info/index.js3
-rw-r--r--app/assets/javascripts/projects/commit_box/info/init_details_button.js18
-rw-r--r--app/assets/javascripts/projects/commit_box/info/load_branches.js3
-rw-r--r--app/assets/javascripts/projects/commits/components/author_select.vue6
-rw-r--r--app/assets/javascripts/projects/commits/index.js18
-rw-r--r--app/assets/javascripts/projects/commits/store/actions.js2
-rw-r--r--app/assets/javascripts/projects/compare/components/revision_dropdown.vue4
-rw-r--r--app/assets/javascripts/projects/compare/components/revision_dropdown_legacy.vue4
-rw-r--r--app/assets/javascripts/projects/components/shared/delete_button.vue10
-rw-r--r--app/assets/javascripts/projects/default_project_templates.js8
-rw-r--r--app/assets/javascripts/projects/new/components/app.vue54
-rw-r--r--app/assets/javascripts/projects/new/index.js10
-rw-r--r--app/assets/javascripts/projects/project_find_file.js2
-rw-r--r--app/assets/javascripts/projects/settings/access_dropdown.js33
-rw-r--r--app/assets/javascripts/projects/settings/branch_rules/components/edit/branch_dropdown.vue2
-rw-r--r--app/assets/javascripts/projects/settings/branch_rules/components/view/constants.js24
-rw-r--r--app/assets/javascripts/projects/settings/branch_rules/components/view/index.vue77
-rw-r--r--app/assets/javascripts/projects/settings/branch_rules/components/view/protection_row.vue3
-rw-r--r--app/assets/javascripts/projects/settings/branch_rules/mount_branch_rules.js7
-rw-r--r--app/assets/javascripts/projects/settings/components/access_dropdown.vue7
-rw-r--r--app/assets/javascripts/projects/settings/components/shared_runners_toggle.vue25
-rw-r--r--app/assets/javascripts/projects/settings/constants.js1
-rw-r--r--app/assets/javascripts/projects/settings/mount_ref_switcher_badges.js31
-rw-r--r--app/assets/javascripts/projects/settings/repository/branch_rules/app.vue13
-rw-r--r--app/assets/javascripts/projects/settings/repository/branch_rules/components/branch_rule.vue19
-rw-r--r--app/assets/javascripts/projects/settings/repository/branch_rules/mount_branch_rules.js12
-rw-r--r--app/assets/javascripts/projects/settings/topics/components/topics_token_selector.vue64
-rw-r--r--app/assets/javascripts/projects/settings/utils.js21
-rw-r--r--app/assets/javascripts/projects/settings_service_desk/components/service_desk_root.vue2
-rw-r--r--app/assets/javascripts/projects/settings_service_desk/components/service_desk_setting.vue6
-rw-r--r--app/assets/javascripts/projects/star.js2
-rw-r--r--app/assets/javascripts/projects/tree/components/commit_pipeline_status_component.vue2
40 files changed, 430 insertions, 161 deletions
diff --git a/app/assets/javascripts/projects/commit/components/branches_dropdown.vue b/app/assets/javascripts/projects/commit/components/branches_dropdown.vue
index 0ed154c47dd..77e809e88ce 100644
--- a/app/assets/javascripts/projects/commit/components/branches_dropdown.vue
+++ b/app/assets/javascripts/projects/commit/components/branches_dropdown.vue
@@ -1,7 +1,7 @@
<script>
import { GlCollapsibleListbox } from '@gitlab/ui';
import { mapActions, mapGetters, mapState } from 'vuex';
-import { debounce } from 'lodash';
+import { debounce, uniqBy } from 'lodash';
import {
I18N_NO_RESULTS_MESSAGE,
I18N_BRANCH_HEADER,
@@ -19,11 +19,6 @@ export default {
required: false,
default: '',
},
- blanked: {
- type: Boolean,
- required: false,
- default: false,
- },
},
i18n: {
noResultsMessage: I18N_NO_RESULTS_MESSAGE,
@@ -32,26 +27,26 @@ export default {
},
data() {
return {
- searchTerm: this.blanked ? '' : this.value,
+ searchTerm: '',
};
},
computed: {
...mapGetters(['joinedBranches']),
- ...mapState(['isFetching']),
+ ...mapState(['isFetching', 'branch']),
listboxItems() {
- return this.joinedBranches.map((value) => ({ value, text: value }));
- },
- },
- watch: {
- // Parent component can set the branch value (e.g. when the user selects a different project)
- // and we need to keep the search term in sync with the selected value
- value(val) {
- this.searchTerm = val;
- this.fetchBranches(this.searchTerm);
+ const selectedItem = { value: this.branch, text: this.branch };
+ const transformedList = this.joinedBranches.map((value) => ({ value, text: value }));
+
+ if (this.searchTerm) {
+ return transformedList;
+ }
+
+ // Add selected item to top of list if not searching
+ return uniqBy([selectedItem].concat(transformedList), 'value');
},
},
mounted() {
- this.fetchBranches(this.searchTerm);
+ this.fetchBranches();
},
methods: {
...mapActions(['fetchBranches']),
@@ -70,8 +65,10 @@ export default {
</script>
<template>
<gl-collapsible-listbox
+ class="gl-max-w-full"
:header-text="$options.i18n.branchHeaderTitle"
:toggle-text="value"
+ toggle-class="gl-w-full"
:items="listboxItems"
searchable
:search-placeholder="$options.i18n.branchSearchPlaceholder"
diff --git a/app/assets/javascripts/projects/commit/components/commit_options_dropdown.vue b/app/assets/javascripts/projects/commit/components/commit_options_dropdown.vue
index 0fd31381ba6..a4edc988d67 100644
--- a/app/assets/javascripts/projects/commit/components/commit_options_dropdown.vue
+++ b/app/assets/javascripts/projects/commit/components/commit_options_dropdown.vue
@@ -93,7 +93,7 @@ export default {
data-testid="email-patches-link"
data-qa-selector="email_patches"
>
- {{ s__('DownloadCommit|Email Patches') }}
+ {{ __('Patches') }}
</gl-dropdown-item>
<gl-dropdown-item
:href="plainDiffPath"
diff --git a/app/assets/javascripts/projects/commit/components/form_modal.vue b/app/assets/javascripts/projects/commit/components/form_modal.vue
index f78afef1c17..28bbf67c090 100644
--- a/app/assets/javascripts/projects/commit/components/form_modal.vue
+++ b/app/assets/javascripts/projects/commit/components/form_modal.vue
@@ -41,11 +41,6 @@ export default {
required: false,
default: false,
},
- isRevert: {
- type: Boolean,
- required: false,
- default: false,
- },
primaryActionEventName: {
type: String,
required: false,
@@ -57,16 +52,16 @@ export default {
checked: true,
actionPrimary: {
text: this.i18n.actionPrimaryText,
- attributes: [
- { variant: 'confirm' },
- { category: 'primary' },
- { 'data-testid': 'submit-commit' },
- { 'data-qa-selector': 'submit_commit_button' },
- ],
+ attributes: {
+ variant: 'confirm',
+ category: 'primary',
+ 'data-testid': 'submit-commit',
+ 'data-qa-selector': 'submit_commit_button',
+ },
},
actionCancel: {
text: this.i18n.actionCancelText,
- attributes: [{ 'data-testid': 'cancel-commit' }],
+ attributes: { 'data-testid': 'cancel-commit' },
},
};
},
@@ -85,7 +80,6 @@ export default {
]),
},
mounted() {
- this.setSelectedProject(this.targetProjectId);
eventHub.$on(this.openModal, this.show);
},
methods: {
@@ -141,7 +135,7 @@ export default {
:value="targetProjectId"
/>
- <projects-dropdown :value="targetProjectName" @selectProject="setSelectedProject" />
+ <projects-dropdown :value="targetProjectName" @input="setSelectedProject" />
</gl-form-group>
<gl-form-group
@@ -151,7 +145,7 @@ export default {
>
<input id="start_branch" type="hidden" name="start_branch" :value="branch" />
- <branches-dropdown :value="branch" :blanked="isRevert" @input="setBranch" />
+ <branches-dropdown :value="branch" @input="setBranch" />
</gl-form-group>
<gl-form-checkbox
diff --git a/app/assets/javascripts/projects/commit/components/projects_dropdown.vue b/app/assets/javascripts/projects/commit/components/projects_dropdown.vue
index d43f5b99e2c..fe54b62e2c8 100644
--- a/app/assets/javascripts/projects/commit/components/projects_dropdown.vue
+++ b/app/assets/javascripts/projects/commit/components/projects_dropdown.vue
@@ -1,6 +1,7 @@
<script>
import { GlCollapsibleListbox } from '@gitlab/ui';
import { mapGetters, mapState } from 'vuex';
+import { debounce, uniqBy } from 'lodash';
import {
I18N_NO_RESULTS_MESSAGE,
I18N_PROJECT_HEADER,
@@ -26,7 +27,7 @@ export default {
},
data() {
return {
- filterTerm: this.value,
+ filterTerm: '',
};
},
computed: {
@@ -39,7 +40,18 @@ export default {
);
},
listboxItems() {
- return this.filteredResults.map(({ id, name }) => ({ value: id, text: name }));
+ const selectedItem = { value: this.selectedProject.id, text: this.selectedProject.name };
+ const transformedList = this.filteredResults.map(({ id, name }) => ({
+ value: id,
+ text: name,
+ }));
+
+ if (this.filterTerm) {
+ return transformedList;
+ }
+
+ // Add selected item to top of list if not searching
+ return uniqBy([selectedItem].concat(transformedList), 'value');
},
selectedProject() {
return this.sortedProjects.find((project) => project.id === this.targetProjectId) || {};
@@ -47,28 +59,26 @@ export default {
},
methods: {
selectProject(value) {
- this.$emit('selectProject', value);
-
- // when we select a project, we want the dropdown to filter to the selected project
- const project = this.listboxItems.find((x) => x.value === value);
- this.filterTerm = project?.text || '';
- },
- filterTermChanged(value) {
- this.filterTerm = value;
+ this.$emit('input', value);
},
+ debouncedSearch: debounce(function debouncedSearch(value) {
+ this.filterTerm = value.trim();
+ }, 250),
},
};
</script>
<template>
<gl-collapsible-listbox
+ class="gl-max-w-full"
:header-text="$options.i18n.projectHeaderTitle"
:items="listboxItems"
searchable
:search-placeholder="$options.i18n.projectSearchPlaceholder"
:selected="selectedProject.id"
:toggle-text="selectedProject.name"
+ toggle-class="gl-w-full"
:no-results-text="$options.i18n.noResultsMessage"
- @search="filterTermChanged"
+ @search="debouncedSearch"
@select="selectProject"
/>
</template>
diff --git a/app/assets/javascripts/projects/commit/init_revert_commit_modal.js b/app/assets/javascripts/projects/commit/init_revert_commit_modal.js
index 41be71932e5..849b2f4858c 100644
--- a/app/assets/javascripts/projects/commit/init_revert_commit_modal.js
+++ b/app/assets/javascripts/projects/commit/init_revert_commit_modal.js
@@ -49,7 +49,6 @@ export default function initInviteMembersModal(primaryActionEventName) {
i18n: { ...I18N_REVERT_MODAL, ...I18N_MODAL },
openModal: OPEN_REVERT_MODAL,
modalId: REVERT_MODAL_ID,
- isRevert: true,
primaryActionEventName,
},
}),
diff --git a/app/assets/javascripts/projects/commit/store/actions.js b/app/assets/javascripts/projects/commit/store/actions.js
index cfff93eac5a..501006a8be5 100644
--- a/app/assets/javascripts/projects/commit/store/actions.js
+++ b/app/assets/javascripts/projects/commit/store/actions.js
@@ -1,4 +1,4 @@
-import { createAlert } from '~/flash';
+import { createAlert } from '~/alert';
import axios from '~/lib/utils/axios_utils';
import { PROJECT_BRANCHES_ERROR } from '../constants';
import * as types from './mutation_types';
diff --git a/app/assets/javascripts/projects/commit_box/info/components/commit_box_pipeline_mini_graph.vue b/app/assets/javascripts/projects/commit_box/info/components/commit_box_pipeline_mini_graph.vue
index dafc4bc5abf..54d13ecc9c8 100644
--- a/app/assets/javascripts/projects/commit_box/info/components/commit_box_pipeline_mini_graph.vue
+++ b/app/assets/javascripts/projects/commit_box/info/components/commit_box_pipeline_mini_graph.vue
@@ -1,6 +1,6 @@
<script>
import { GlLoadingIcon } from '@gitlab/ui';
-import { createAlert } from '~/flash';
+import { createAlert } from '~/alert';
import { __ } from '~/locale';
import {
getQueryHeaders,
diff --git a/app/assets/javascripts/projects/commit_box/info/components/commit_box_pipeline_status.vue b/app/assets/javascripts/projects/commit_box/info/components/commit_box_pipeline_status.vue
index 62b1209131c..71f53613a3b 100644
--- a/app/assets/javascripts/projects/commit_box/info/components/commit_box_pipeline_status.vue
+++ b/app/assets/javascripts/projects/commit_box/info/components/commit_box_pipeline_status.vue
@@ -1,7 +1,7 @@
<script>
import { GlLoadingIcon, GlLink } from '@gitlab/ui';
import CiIcon from '~/vue_shared/components/ci_icon.vue';
-import { createAlert } from '~/flash';
+import { createAlert } from '~/alert';
import {
getQueryHeaders,
toggleQueryPollingByVisibility,
diff --git a/app/assets/javascripts/projects/commit_box/info/index.js b/app/assets/javascripts/projects/commit_box/info/index.js
index 7500c152b6a..7c4b76fd62f 100644
--- a/app/assets/javascripts/projects/commit_box/info/index.js
+++ b/app/assets/javascripts/projects/commit_box/info/index.js
@@ -1,6 +1,5 @@
import { fetchCommitMergeRequests } from '~/commit_merge_requests';
import { initCommitPipelineMiniGraph } from './init_commit_pipeline_mini_graph';
-import { initDetailsButton } from './init_details_button';
import { loadBranches } from './load_branches';
import initCommitPipelineStatus from './init_commit_pipeline_status';
@@ -14,7 +13,5 @@ export const initCommitBoxInfo = () => {
// Display pipeline mini graph for this commit
initCommitPipelineMiniGraph();
- initDetailsButton();
-
initCommitPipelineStatus();
};
diff --git a/app/assets/javascripts/projects/commit_box/info/init_details_button.js b/app/assets/javascripts/projects/commit_box/info/init_details_button.js
index bc2c16b9e83..520b20fcb86 100644
--- a/app/assets/javascripts/projects/commit_box/info/init_details_button.js
+++ b/app/assets/javascripts/projects/commit_box/info/init_details_button.js
@@ -1,7 +1,17 @@
export const initDetailsButton = () => {
- document.querySelector('.commit-info').addEventListener('click', function expand(e) {
- e.preventDefault();
- this.querySelector('.js-details-content').classList.remove('hide');
- this.querySelector('.js-details-expand').classList.add('gl-display-none');
+ const expandButton = document.querySelector('.js-details-expand');
+
+ if (!expandButton) {
+ return;
+ }
+
+ expandButton.addEventListener('click', (event) => {
+ const btn = event.currentTarget;
+ const contentEl = btn.parentElement.querySelector('.js-details-content');
+
+ if (contentEl) {
+ contentEl.classList.remove('hide');
+ btn.classList.add('gl-display-none');
+ }
});
};
diff --git a/app/assets/javascripts/projects/commit_box/info/load_branches.js b/app/assets/javascripts/projects/commit_box/info/load_branches.js
index d1136817cb3..8333e70b951 100644
--- a/app/assets/javascripts/projects/commit_box/info/load_branches.js
+++ b/app/assets/javascripts/projects/commit_box/info/load_branches.js
@@ -1,6 +1,7 @@
import axios from 'axios';
import { sanitize } from '~/lib/dompurify';
import { __ } from '~/locale';
+import { initDetailsButton } from './init_details_button';
export const loadBranches = (containerSelector = '.js-commit-box-info') => {
const containerEl = document.querySelector(containerSelector);
@@ -14,6 +15,8 @@ export const loadBranches = (containerSelector = '.js-commit-box-info') => {
.get(commitPath)
.then(({ data }) => {
branchesEl.innerHTML = sanitize(data);
+
+ initDetailsButton();
})
.catch(() => {
branchesEl.textContent = __('Failed to load branches. Please try again.');
diff --git a/app/assets/javascripts/projects/commits/components/author_select.vue b/app/assets/javascripts/projects/commits/components/author_select.vue
index f85be67d4b3..2966214e051 100644
--- a/app/assets/javascripts/projects/commits/components/author_select.vue
+++ b/app/assets/javascripts/projects/commits/components/author_select.vue
@@ -9,7 +9,7 @@ import {
} from '@gitlab/ui';
import { debounce } from 'lodash';
import { mapState, mapActions } from 'vuex';
-import { redirectTo, queryToObject } from '~/lib/utils/url_utility';
+import { redirectTo, queryToObject } from '~/lib/utils/url_utility'; // eslint-disable-line import/no-deprecated
import { __ } from '~/locale';
const tooltipMessage = __('Searching by both author and message is currently not supported.');
@@ -89,10 +89,10 @@ export default {
commitListElement.style.transition = 'opacity 200ms';
if (!user) {
- return redirectTo(this.commitsPath);
+ return redirectTo(this.commitsPath); // eslint-disable-line import/no-deprecated
}
- return redirectTo(`${this.commitsPath}?author=${user}`);
+ return redirectTo(`${this.commitsPath}?author=${user}`); // eslint-disable-line import/no-deprecated
},
searchAuthors() {
this.fetchAuthors(this.authorInput);
diff --git a/app/assets/javascripts/projects/commits/index.js b/app/assets/javascripts/projects/commits/index.js
index f56884f605f..d37c1800718 100644
--- a/app/assets/javascripts/projects/commits/index.js
+++ b/app/assets/javascripts/projects/commits/index.js
@@ -35,6 +35,10 @@ export const initCommitsRefSwitcher = () => {
const { projectId, ref, commitsPath, refType } = el.dataset;
const commitsPathPrefix = commitsPath.match(COMMITS_PATH_REGEX)?.[0];
+ const generateRefDestinationUrl = (selectedRef, selectedRefType) => {
+ const commitsPathSuffix = selectedRefType ? `?ref_type=${selectedRefType}` : '';
+ return `${commitsPathPrefix}/${encodeURIComponent(selectedRef)}${commitsPathSuffix}`;
+ };
const useSymbolicRefNames = Boolean(refType);
return new Vue({
el,
@@ -42,21 +46,17 @@ export const initCommitsRefSwitcher = () => {
return createElement(RefSelector, {
props: {
projectId,
+ queryParams: { sort: 'updated_desc' },
value: useSymbolicRefNames ? `refs/${refType}/${ref}` : ref,
useSymbolicRefNames,
- refType,
},
on: {
input(selected) {
- if (useSymbolicRefNames) {
- const matches = selected.match(/refs\/(heads|tags)\/(.+)/);
- if (matches) {
- visitUrl(`${commitsPathPrefix}/${matches[2]}?ref_type=${matches[1]}`);
- } else {
- visitUrl(`${commitsPathPrefix}/${selected}`);
- }
+ const matches = selected.match(/refs\/(heads|tags)\/(.+)/);
+ if (useSymbolicRefNames && matches) {
+ visitUrl(generateRefDestinationUrl(matches[2], matches[1]));
} else {
- visitUrl(`${commitsPathPrefix}/${selected}`);
+ visitUrl(generateRefDestinationUrl(selected));
}
},
},
diff --git a/app/assets/javascripts/projects/commits/store/actions.js b/app/assets/javascripts/projects/commits/store/actions.js
index 9365066418b..5175f7f9151 100644
--- a/app/assets/javascripts/projects/commits/store/actions.js
+++ b/app/assets/javascripts/projects/commits/store/actions.js
@@ -1,5 +1,5 @@
import * as Sentry from '@sentry/browser';
-import { createAlert } from '~/flash';
+import { createAlert } from '~/alert';
import axios from '~/lib/utils/axios_utils';
import { joinPaths } from '~/lib/utils/url_utility';
import { __ } from '~/locale';
diff --git a/app/assets/javascripts/projects/compare/components/revision_dropdown.vue b/app/assets/javascripts/projects/compare/components/revision_dropdown.vue
index 10531e950f9..8af1667e26b 100644
--- a/app/assets/javascripts/projects/compare/components/revision_dropdown.vue
+++ b/app/assets/javascripts/projects/compare/components/revision_dropdown.vue
@@ -1,7 +1,7 @@
<script>
import { GlDropdown, GlDropdownItem, GlSearchBoxByType, GlDropdownSectionHeader } from '@gitlab/ui';
import { debounce } from 'lodash';
-import { createAlert } from '~/flash';
+import { createAlert } from '~/alert';
import axios from '~/lib/utils/axios_utils';
import { s__ } from '~/locale';
@@ -149,6 +149,7 @@ export default {
:key="branch"
is-check-item
:is-checked="selectedRevision === branch"
+ data-testid="branches-dropdown-item"
@click="onClick(branch)"
>
{{ branch }}
@@ -161,6 +162,7 @@ export default {
:key="tag"
is-check-item
:is-checked="selectedRevision === tag"
+ data-testid="tags-dropdown-item"
@click="onClick(tag)"
>
{{ tag }}
diff --git a/app/assets/javascripts/projects/compare/components/revision_dropdown_legacy.vue b/app/assets/javascripts/projects/compare/components/revision_dropdown_legacy.vue
index 1e1677e947c..034bae3066d 100644
--- a/app/assets/javascripts/projects/compare/components/revision_dropdown_legacy.vue
+++ b/app/assets/javascripts/projects/compare/components/revision_dropdown_legacy.vue
@@ -1,6 +1,6 @@
<script>
import { GlDropdown, GlDropdownItem, GlSearchBoxByType, GlDropdownSectionHeader } from '@gitlab/ui';
-import { createAlert } from '~/flash';
+import { createAlert } from '~/alert';
import axios from '~/lib/utils/axios_utils';
import { s__ } from '~/locale';
@@ -132,6 +132,7 @@ export default {
:key="`branch${index}`"
is-check-item
:is-checked="selectedRevision === branch"
+ data-testid="branches-dropdown-item"
@click="onClick(branch)"
>
{{ branch }}
@@ -144,6 +145,7 @@ export default {
:key="`tag${index}`"
is-check-item
:is-checked="selectedRevision === tag"
+ data-testid="tags-dropdown-item"
@click="onClick(tag)"
>
{{ tag }}
diff --git a/app/assets/javascripts/projects/components/shared/delete_button.vue b/app/assets/javascripts/projects/components/shared/delete_button.vue
index 64a16b462f5..06c0230c8e0 100644
--- a/app/assets/javascripts/projects/components/shared/delete_button.vue
+++ b/app/assets/javascripts/projects/components/shared/delete_button.vue
@@ -62,11 +62,11 @@ export default {
return {
primary: {
text: __('Yes, delete project'),
- attributes: [
- { variant: 'danger' },
- { disabled: this.confirmDisabled },
- { 'data-qa-selector': 'confirm_delete_button' },
- ],
+ attributes: {
+ variant: 'danger',
+ disabled: this.confirmDisabled,
+ 'data-qa-selector': 'confirm_delete_button',
+ },
},
cancel: {
text: __('Cancel, keep project'),
diff --git a/app/assets/javascripts/projects/default_project_templates.js b/app/assets/javascripts/projects/default_project_templates.js
index a44855c14d5..5bbc881952f 100644
--- a/app/assets/javascripts/projects/default_project_templates.js
+++ b/app/assets/javascripts/projects/default_project_templates.js
@@ -53,10 +53,6 @@ export default {
text: s__('ProjectTemplates|Pages/Plain HTML'),
icon: '.template-option .icon-plainhtml',
},
- gitbook: {
- text: s__('ProjectTemplates|Pages/GitBook'),
- icon: '.template-option .icon-gitbook',
- },
hexo: {
text: s__('ProjectTemplates|Pages/Hexo'),
icon: '.template-option .icon-hexo',
@@ -121,4 +117,8 @@ export default {
text: s__('ProjectTemplates|TYPO3 Distribution'),
icon: '.template-option .icon-typo3',
},
+ laravel: {
+ text: s__('ProjectTemplates|Laravel Framework'),
+ icon: '.template-option .icon-laravel',
+ },
};
diff --git a/app/assets/javascripts/projects/new/components/app.vue b/app/assets/javascripts/projects/new/components/app.vue
index 3100029eb31..2f58d4468be 100644
--- a/app/assets/javascripts/projects/new/components/app.vue
+++ b/app/assets/javascripts/projects/new/components/app.vue
@@ -9,6 +9,7 @@ import NewNamespacePage from '~/vue_shared/new_namespace/new_namespace_page.vue'
import NewProjectPushTipPopover from './new_project_push_tip_popover.vue';
const CI_CD_PANEL = 'cicd_for_external_repo';
+const IMPORT_PROJECT_PANEL = 'import_project';
const PANELS = [
{
key: 'blank',
@@ -32,7 +33,7 @@ const PANELS = [
},
{
key: 'import',
- name: 'import_project',
+ name: IMPORT_PROJECT_PANEL,
selector: '#import-project-pane',
title: s__('ProjectsNew|Import project'),
description: s__(
@@ -59,6 +60,24 @@ export default {
SafeHtml,
},
props: {
+ rootPath: {
+ type: String,
+ required: true,
+ },
+ projectsUrl: {
+ type: String,
+ required: true,
+ },
+ parentGroupUrl: {
+ type: String,
+ required: false,
+ default: null,
+ },
+ parentGroupName: {
+ type: String,
+ required: false,
+ default: '',
+ },
hasErrors: {
type: Boolean,
required: false,
@@ -74,11 +93,40 @@ export default {
required: false,
default: '',
},
+ canImportProjects: {
+ type: Boolean,
+ required: false,
+ default: true,
+ },
},
computed: {
+ initialBreadcrumbs() {
+ const breadcrumbs = this.parentGroupUrl
+ ? [{ text: this.parentGroupName, href: this.parentGroupUrl }]
+ : [
+ { text: s__('Navigation|Your work'), href: this.rootPath },
+ { text: s__('ProjectsNew|Projects'), href: this.projectsUrl },
+ ];
+ breadcrumbs.push({ text: s__('ProjectsNew|New project'), href: '#' });
+ return breadcrumbs;
+ },
availablePanels() {
- return this.isCiCdAvailable ? PANELS : PANELS.filter((p) => p.name !== CI_CD_PANEL);
+ if (this.isCiCdAvailable && this.canImportProjects) {
+ return PANELS;
+ }
+
+ return PANELS.filter((panel) => {
+ if (!this.canImportProjects && panel.name === IMPORT_PROJECT_PANEL) {
+ return false;
+ }
+
+ if (!this.isCiCdAvailable && panel.name === CI_CD_PANEL) {
+ return false;
+ }
+
+ return true;
+ });
},
},
@@ -95,7 +143,7 @@ export default {
<template>
<new-namespace-page
- :initial-breadcrumb="__('New project')"
+ :initial-breadcrumbs="initialBreadcrumbs"
:panels="availablePanels"
:jump-to-last-persisted-panel="hasErrors"
:title="s__('ProjectsNew|Create new project')"
diff --git a/app/assets/javascripts/projects/new/index.js b/app/assets/javascripts/projects/new/index.js
index 910244c657b..a5a833dc73b 100644
--- a/app/assets/javascripts/projects/new/index.js
+++ b/app/assets/javascripts/projects/new/index.js
@@ -15,12 +15,22 @@ export function initNewProjectCreation() {
newProjectGuidelines,
hasErrors,
isCiCdAvailable,
+ parentGroupUrl,
+ parentGroupName,
+ projectsUrl,
+ rootPath,
+ canImportProjects,
} = el.dataset;
const props = {
hasErrors: parseBoolean(hasErrors),
isCiCdAvailable: parseBoolean(isCiCdAvailable),
newProjectGuidelines,
+ parentGroupUrl,
+ parentGroupName,
+ projectsUrl,
+ rootPath,
+ canImportProjects: parseBoolean(canImportProjects),
};
const provide = {
diff --git a/app/assets/javascripts/projects/project_find_file.js b/app/assets/javascripts/projects/project_find_file.js
index 71329c4f461..a8b884a68a0 100644
--- a/app/assets/javascripts/projects/project_find_file.js
+++ b/app/assets/javascripts/projects/project_find_file.js
@@ -2,7 +2,7 @@
import fuzzaldrinPlus from 'fuzzaldrin-plus';
import $ from 'jquery';
-import { createAlert } from '~/flash';
+import { createAlert } from '~/alert';
import { sanitize } from '~/lib/dompurify';
import axios from '~/lib/utils/axios_utils';
import { spriteIcon } from '~/lib/utils/common_utils';
diff --git a/app/assets/javascripts/projects/settings/access_dropdown.js b/app/assets/javascripts/projects/settings/access_dropdown.js
index dcf7415a444..d8675a851ea 100644
--- a/app/assets/javascripts/projects/settings/access_dropdown.js
+++ b/app/assets/javascripts/projects/settings/access_dropdown.js
@@ -1,7 +1,7 @@
/* eslint-disable no-underscore-dangle, class-methods-use-this */
import { escape, find, countBy } from 'lodash';
import initDeprecatedJQueryDropdown from '~/deprecated_jquery_dropdown';
-import { createAlert } from '~/flash';
+import { createAlert } from '~/alert';
import { n__, s__, __, sprintf } from '~/locale';
import { getUsers, getGroups, getDeployKeys } from './api/access_dropdown_api';
import { LEVEL_TYPES, LEVEL_ID_PROP, ACCESS_LEVELS, ACCESS_LEVEL_NONE } from './constants';
@@ -408,14 +408,16 @@ export default class AccessDropdown {
// Has to be checked against server response
// because the selected item can be in filter results
- usersResponse.forEach((response) => {
- // Add is it has not been added
- if (map.indexOf(LEVEL_TYPES.USER + response.id) === -1) {
- const user = { ...response };
- user.type = LEVEL_TYPES.USER;
- users.push(user);
- }
- });
+ if (gon.current_project_id) {
+ usersResponse.forEach((response) => {
+ // Add is it has not been added
+ if (map.indexOf(LEVEL_TYPES.USER + response.id) === -1) {
+ const user = { ...response };
+ user.type = LEVEL_TYPES.USER;
+ users.push(user);
+ }
+ });
+ }
if (groups.length) {
if (roles.length) {
@@ -469,6 +471,14 @@ export default class AccessDropdown {
}
}
+ if (this.accessLevel === ACCESS_LEVELS.CREATE && deployKeys.length) {
+ consolidatedData = consolidatedData.concat(
+ [{ type: 'divider' }],
+ [{ type: 'header', content: s__('AccessDropdown|Deploy Keys') }],
+ deployKeys,
+ );
+ }
+
return consolidatedData;
}
@@ -506,7 +516,10 @@ export default class AccessDropdown {
break;
case LEVEL_TYPES.DEPLOY_KEY:
groupRowEl =
- this.accessLevel === ACCESS_LEVELS.PUSH ? this.deployKeyRowHtml(item, isActive) : '';
+ this.accessLevel === ACCESS_LEVELS.PUSH || this.accessLevel === ACCESS_LEVELS.CREATE
+ ? this.deployKeyRowHtml(item, isActive)
+ : '';
+
break;
case LEVEL_TYPES.GROUP:
groupRowEl = this.groupRowHtml(item, isActive);
diff --git a/app/assets/javascripts/projects/settings/branch_rules/components/edit/branch_dropdown.vue b/app/assets/javascripts/projects/settings/branch_rules/components/edit/branch_dropdown.vue
index f2b1c749abc..3dcacf9eb34 100644
--- a/app/assets/javascripts/projects/settings/branch_rules/components/edit/branch_dropdown.vue
+++ b/app/assets/javascripts/projects/settings/branch_rules/components/edit/branch_dropdown.vue
@@ -7,7 +7,7 @@ import {
GlSprintf,
GlLink,
} from '@gitlab/ui';
-import { createAlert } from '~/flash';
+import { createAlert } from '~/alert';
import { s__, sprintf } from '~/locale';
import { helpPagePath } from '~/helpers/help_page_helper';
import branchesQuery from '../../queries/branches.query.graphql';
diff --git a/app/assets/javascripts/projects/settings/branch_rules/components/view/constants.js b/app/assets/javascripts/projects/settings/branch_rules/components/view/constants.js
index a98c2439cde..b71c33d2b91 100644
--- a/app/assets/javascripts/projects/settings/branch_rules/components/view/constants.js
+++ b/app/assets/javascripts/projects/settings/branch_rules/components/view/constants.js
@@ -14,11 +14,6 @@ export const I18N = {
wildcardsHelpText: s__(
'BranchRules|%{linkStart}Wildcards%{linkEnd} such as *-stable or production/ are supported',
),
- forcePushTitle: s__('BranchRules|Force push'),
- allowForcePushDescription: s__(
- 'BranchRules|All users with push access are allowed to force push.',
- ),
- disallowForcePushDescription: s__('BranchRules|Force push is not allowed.'),
approvalsTitle: s__('BranchRules|Approvals'),
manageApprovalsLinkTitle: s__('BranchRules|Manage in merge request approvals'),
approvalsDescription: s__(
@@ -33,6 +28,19 @@ export const I18N = {
allowedToPushHeader: s__('BranchRules|Allowed to push and merge (%{total})'),
allowedToMergeHeader: s__('BranchRules|Allowed to merge (%{total})'),
approvalsHeader: s__('BranchRules|Required approvals (%{total})'),
+ allowForcePushTitle: s__('BranchRules|Allows force push'),
+ doesNotAllowForcePushTitle: s__('BranchRules|Does not allow force push'),
+ forcePushDescription: s__('BranchRules|From users with push access.'),
+ requiresCodeOwnerApprovalTitle: s__('BranchRules|Requires approval from code owners'),
+ doesNotRequireCodeOwnerApprovalTitle: s__(
+ 'BranchRules|Does not require approval from code owners',
+ ),
+ requiresCodeOwnerApprovalDescription: s__(
+ 'BranchRules|Also rejects code pushes that change files listed in CODEOWNERS file.',
+ ),
+ doesNotRequireCodeOwnerApprovalDescription: s__(
+ 'BranchRules|Also accepts code pushes that change files listed in CODEOWNERS file.',
+ ),
noData: s__('BranchRules|No data to display'),
};
@@ -48,3 +56,9 @@ export const PROTECTED_BRANCHES_HELP_PATH = 'user/project/protected_branches';
export const APPROVALS_HELP_PATH = 'user/project/merge_requests/approvals/index.md';
export const STATUS_CHECKS_HELP_PATH = 'user/project/merge_requests/status_checks.md';
+
+export const REQUIRED_ICON = 'check-circle-filled';
+export const NOT_REQUIRED_ICON = 'status-failed';
+
+export const REQUIRED_ICON_CLASS = 'gl-fill-green-500';
+export const NOT_REQUIRED_ICON_CLASS = 'gl-text-red-500';
diff --git a/app/assets/javascripts/projects/settings/branch_rules/components/view/index.vue b/app/assets/javascripts/projects/settings/branch_rules/components/view/index.vue
index 740868e1d75..dbcb77b67f3 100644
--- a/app/assets/javascripts/projects/settings/branch_rules/components/view/index.vue
+++ b/app/assets/javascripts/projects/settings/branch_rules/components/view/index.vue
@@ -1,5 +1,5 @@
<script>
-import { GlSprintf, GlLink, GlLoadingIcon } from '@gitlab/ui';
+import { GlSprintf, GlLink, GlLoadingIcon, GlIcon } from '@gitlab/ui';
import { sprintf, n__ } from '~/locale';
import { getParameterByName, mergeUrlParams } from '~/lib/utils/url_utility';
import { helpPagePath } from '~/helpers/help_page_helper';
@@ -12,6 +12,10 @@ import {
BRANCH_PARAM_NAME,
WILDCARDS_HELP_PATH,
PROTECTED_BRANCHES_HELP_PATH,
+ REQUIRED_ICON,
+ NOT_REQUIRED_ICON,
+ REQUIRED_ICON_CLASS,
+ NOT_REQUIRED_ICON_CLASS,
} from './constants';
const wildcardsHelpDocLink = helpPagePath(WILDCARDS_HELP_PATH);
@@ -22,7 +26,7 @@ export default {
i18n: I18N,
wildcardsHelpDocLink,
protectedBranchesHelpDocLink,
- components: { Protection, GlSprintf, GlLink, GlLoadingIcon },
+ components: { Protection, GlSprintf, GlLink, GlLoadingIcon, GlIcon },
inject: {
projectPath: {
default: '',
@@ -33,6 +37,9 @@ export default {
branchesPath: {
default: '',
},
+ showStatusChecks: { default: false },
+ showApprovers: { default: false },
+ showCodeOwners: { default: false },
},
apollo: {
project: {
@@ -63,10 +70,28 @@ export default {
};
},
computed: {
- forcePushDescription() {
- return this.branchProtection?.allowForcePush
- ? this.$options.i18n.allowForcePushDescription
- : this.$options.i18n.disallowForcePushDescription;
+ forcePushAttributes() {
+ const { allowForcePush } = this.branchProtection || {};
+ const icon = allowForcePush ? REQUIRED_ICON : NOT_REQUIRED_ICON;
+ const iconClass = allowForcePush ? REQUIRED_ICON_CLASS : NOT_REQUIRED_ICON_CLASS;
+ const title = allowForcePush
+ ? this.$options.i18n.allowForcePushTitle
+ : this.$options.i18n.doesNotAllowForcePushTitle;
+
+ return { icon, iconClass, title };
+ },
+ codeOwnersApprovalAttributes() {
+ const { codeOwnerApprovalRequired } = this.branchProtection || {};
+ const icon = codeOwnerApprovalRequired ? REQUIRED_ICON : NOT_REQUIRED_ICON;
+ const iconClass = codeOwnerApprovalRequired ? REQUIRED_ICON_CLASS : NOT_REQUIRED_ICON_CLASS;
+ const title = codeOwnerApprovalRequired
+ ? this.$options.i18n.requiresCodeOwnerApprovalTitle
+ : this.$options.i18n.doesNotRequireCodeOwnerApprovalTitle;
+ const description = codeOwnerApprovalRequired
+ ? this.$options.i18n.requiresCodeOwnerApprovalDescription
+ : this.$options.i18n.doesNotRequireCodeOwnerApprovalDescription;
+
+ return { icon, iconClass, title, description };
},
mergeAccessLevels() {
const { mergeAccessLevels } = this.branchProtection || {};
@@ -98,7 +123,7 @@ export default {
: this.$options.i18n.branchNameOrPattern;
},
matchingBranchesLinkHref() {
- return mergeUrlParams({ state: 'all', search: this.branch }, this.branchesPath);
+ return mergeUrlParams({ state: 'all', search: `^${this.branch}$` }, this.branchesPath);
},
matchingBranchesLinkTitle() {
const total = this.matchingBranchesCount;
@@ -162,12 +187,9 @@ export default {
:roles="pushAccessLevels.roles"
:users="pushAccessLevels.users"
:groups="pushAccessLevels.groups"
+ data-qa-selector="allowed_to_push_content"
/>
- <!-- Force push -->
- <strong>{{ $options.i18n.forcePushTitle }}</strong>
- <p>{{ forcePushDescription }}</p>
-
<!-- Allowed to merge -->
<protection
:header="allowedToMergeHeader"
@@ -176,11 +198,40 @@ export default {
:roles="mergeAccessLevels.roles"
:users="mergeAccessLevels.users"
:groups="mergeAccessLevels.groups"
+ data-qa-selector="allowed_to_merge_content"
/>
+ <!-- Force push -->
+ <div class="gl-display-flex gl-align-items-center">
+ <gl-icon
+ :size="14"
+ data-testid="force-push-icon"
+ :name="forcePushAttributes.icon"
+ :class="forcePushAttributes.iconClass"
+ />
+ <strong class="gl-ml-2">{{ forcePushAttributes.title }}</strong>
+ </div>
+
+ <div class="gl-text-gray-400 gl-mb-2">{{ $options.i18n.forcePushDescription }}</div>
+
<!-- EE start -->
+ <!-- Code Owners -->
+ <div v-if="showCodeOwners">
+ <div class="gl-display-flex gl-align-items-center">
+ <gl-icon
+ data-testid="code-owners-icon"
+ :size="14"
+ :name="codeOwnersApprovalAttributes.icon"
+ :class="codeOwnersApprovalAttributes.iconClass"
+ />
+ <strong class="gl-ml-2">{{ codeOwnersApprovalAttributes.title }}</strong>
+ </div>
+
+ <div class="gl-text-gray-400">{{ codeOwnersApprovalAttributes.description }}</div>
+ </div>
+
<!-- Approvals -->
- <template v-if="approvalsHeader">
+ <template v-if="showApprovers">
<h4 class="gl-mb-1 gl-mt-5">{{ $options.i18n.approvalsTitle }}</h4>
<gl-sprintf :message="$options.i18n.approvalsDescription">
<template #link="{ content }">
@@ -200,7 +251,7 @@ export default {
</template>
<!-- Status checks -->
- <template v-if="statusChecksHeader">
+ <template v-if="showStatusChecks">
<h4 class="gl-mb-1 gl-mt-5">{{ $options.i18n.statusChecksTitle }}</h4>
<gl-sprintf :message="$options.i18n.statusChecksDescription">
<template #link="{ content }">
diff --git a/app/assets/javascripts/projects/settings/branch_rules/components/view/protection_row.vue b/app/assets/javascripts/projects/settings/branch_rules/components/view/protection_row.vue
index 9bff2f5506c..3a5b3409596 100644
--- a/app/assets/javascripts/projects/settings/branch_rules/components/view/protection_row.vue
+++ b/app/assets/javascripts/projects/settings/branch_rules/components/view/protection_row.vue
@@ -105,7 +105,8 @@ export default {
v-for="(item, index) in accessLevels"
:key="index"
data-testid="access-level"
- class="gl-w-quarter"
+ data-qa-selector="access_level_content"
+ :data-qa-role="item.accessLevelDescription"
>
<span v-if="commaSeparateList && index > 0" data-testid="comma-separator">,</span>
{{ item.accessLevelDescription }}
diff --git a/app/assets/javascripts/projects/settings/branch_rules/mount_branch_rules.js b/app/assets/javascripts/projects/settings/branch_rules/mount_branch_rules.js
index 081d6cec958..c429c352bfa 100644
--- a/app/assets/javascripts/projects/settings/branch_rules/mount_branch_rules.js
+++ b/app/assets/javascripts/projects/settings/branch_rules/mount_branch_rules.js
@@ -1,6 +1,7 @@
import Vue from 'vue';
import VueApollo from 'vue-apollo';
import createDefaultClient from '~/lib/graphql';
+import { parseBoolean } from '~/lib/utils/common_utils';
import View from 'ee_else_ce/projects/settings/branch_rules/components/view/index.vue';
export default function mountBranchRules(el) {
@@ -20,6 +21,9 @@ export default function mountBranchRules(el) {
approvalRulesPath,
statusChecksPath,
branchesPath,
+ showStatusChecks,
+ showApprovers,
+ showCodeOwners,
} = el.dataset;
return new Vue({
@@ -31,6 +35,9 @@ export default function mountBranchRules(el) {
approvalRulesPath,
statusChecksPath,
branchesPath,
+ showStatusChecks: parseBoolean(showStatusChecks),
+ showApprovers: parseBoolean(showApprovers),
+ showCodeOwners: parseBoolean(showCodeOwners),
},
render(h) {
return h(View);
diff --git a/app/assets/javascripts/projects/settings/components/access_dropdown.vue b/app/assets/javascripts/projects/settings/components/access_dropdown.vue
index cc47496971d..08a1c586f69 100644
--- a/app/assets/javascripts/projects/settings/components/access_dropdown.vue
+++ b/app/assets/javascripts/projects/settings/components/access_dropdown.vue
@@ -9,7 +9,7 @@ import {
GlSprintf,
} from '@gitlab/ui';
import { debounce, intersectionWith, groupBy, differenceBy, intersectionBy } from 'lodash';
-import { createAlert } from '~/flash';
+import { createAlert } from '~/alert';
import { __, s__, n__ } from '~/locale';
import { getUsers, getGroups, getDeployKeys } from '../api/access_dropdown_api';
import { LEVEL_TYPES, ACCESS_LEVELS } from '../constants';
@@ -86,7 +86,10 @@ export default {
return groupBy(this.preselectedItems, 'type');
},
showDeployKeys() {
- return this.accessLevel === ACCESS_LEVELS.PUSH && this.deployKeys.length;
+ return (
+ (this.accessLevel === ACCESS_LEVELS.PUSH || this.accessLevel === ACCESS_LEVELS.CREATE) &&
+ this.deployKeys.length
+ );
},
toggleLabel() {
const counts = Object.entries(this.selected).reduce((acc, [key, value]) => {
diff --git a/app/assets/javascripts/projects/settings/components/shared_runners_toggle.vue b/app/assets/javascripts/projects/settings/components/shared_runners_toggle.vue
index aa3235b1515..b8e7e9e15db 100644
--- a/app/assets/javascripts/projects/settings/components/shared_runners_toggle.vue
+++ b/app/assets/javascripts/projects/settings/components/shared_runners_toggle.vue
@@ -1,5 +1,5 @@
<script>
-import { GlAlert, GlToggle, GlTooltip } from '@gitlab/ui';
+import { GlAlert, GlToggle } from '@gitlab/ui';
import axios from '~/lib/utils/axios_utils';
import { __, s__ } from '~/locale';
import { CC_VALIDATION_REQUIRED_ERROR } from '../constants';
@@ -16,7 +16,6 @@ export default {
components: {
GlAlert,
GlToggle,
- GlTooltip,
CcValidationRequiredAlert: () =>
import('ee_component/billings/components/cc_validation_required_alert.vue'),
},
@@ -94,10 +93,26 @@ export default {
@dismiss="ccAlertDismissed = true"
/>
- <gl-alert v-if="genericError" class="gl-mb-3" variant="danger" :dismissible="false">
+ <gl-alert
+ v-if="genericError"
+ data-testid="error-alert"
+ variant="danger"
+ :dismissible="false"
+ class="gl-mb-5"
+ >
{{ errorMessage }}
</gl-alert>
+ <gl-alert
+ v-if="isDisabledAndUnoverridable"
+ data-testid="unoverridable-alert"
+ variant="warning"
+ :dismissible="false"
+ class="gl-mb-5"
+ >
+ {{ s__('Runners|Shared runners are disabled in the group settings') }}
+ </gl-alert>
+
<gl-toggle
ref="sharedRunnersToggle"
:disabled="isDisabledAndUnoverridable"
@@ -107,10 +122,6 @@ export default {
data-testid="toggle-shared-runners"
@change="toggleSharedRunners"
/>
-
- <gl-tooltip v-if="isDisabledAndUnoverridable" :target="() => $refs.sharedRunnersToggle">
- {{ __('Shared runners are disabled on group level') }}
- </gl-tooltip>
</section>
</div>
</template>
diff --git a/app/assets/javascripts/projects/settings/constants.js b/app/assets/javascripts/projects/settings/constants.js
index 9cf1afd334f..595cbc9c991 100644
--- a/app/assets/javascripts/projects/settings/constants.js
+++ b/app/assets/javascripts/projects/settings/constants.js
@@ -17,6 +17,7 @@ export const LEVEL_ID_PROP = {
export const ACCESS_LEVELS = {
MERGE: 'merge_access_levels',
PUSH: 'push_access_levels',
+ CREATE: 'create_access_levels',
};
export const ACCESS_LEVEL_NONE = 0;
diff --git a/app/assets/javascripts/projects/settings/mount_ref_switcher_badges.js b/app/assets/javascripts/projects/settings/mount_ref_switcher_badges.js
new file mode 100644
index 00000000000..527678250fb
--- /dev/null
+++ b/app/assets/javascripts/projects/settings/mount_ref_switcher_badges.js
@@ -0,0 +1,31 @@
+import Vue from 'vue';
+import RefSelector from '~/ref/components/ref_selector.vue';
+import { visitUrl } from '~/lib/utils/url_utility';
+import { generateRefDestinationPath } from './utils';
+
+export default function initRefSwitcherBadges() {
+ const refSwitcherElements = document.getElementsByClassName('js-ref-switcher-badge');
+
+ if (refSwitcherElements.length === 0) return false;
+
+ return Array.from(refSwitcherElements).forEach((element) => {
+ const { projectId, ref } = element.dataset;
+
+ return new Vue({
+ el: element,
+ render(createElement) {
+ return createElement(RefSelector, {
+ props: {
+ projectId,
+ value: ref,
+ },
+ on: {
+ input(selectedRef) {
+ visitUrl(generateRefDestinationPath(selectedRef));
+ },
+ },
+ });
+ },
+ });
+ });
+}
diff --git a/app/assets/javascripts/projects/settings/repository/branch_rules/app.vue b/app/assets/javascripts/projects/settings/repository/branch_rules/app.vue
index f3d392a0ec4..dcf5155644d 100644
--- a/app/assets/javascripts/projects/settings/repository/branch_rules/app.vue
+++ b/app/assets/javascripts/projects/settings/repository/branch_rules/app.vue
@@ -1,6 +1,6 @@
<script>
import { GlButton, GlModal, GlModalDirective } from '@gitlab/ui';
-import { createAlert } from '~/flash';
+import { createAlert } from '~/alert';
import branchRulesQuery from 'ee_else_ce/projects/settings/repository/branch_rules/graphql/queries/branch_rules.query.graphql';
import { expandSection } from '~/settings_panels';
import { scrollToElement } from '~/lib/utils/common_utils';
@@ -69,9 +69,14 @@ export default {
<div v-if="!branchRules.length" data-testid="empty">{{ $options.i18n.emptyState }}</div>
- <gl-button v-gl-modal="$options.modalId" class="gl-mt-5" category="secondary" variant="info">{{
- $options.i18n.addBranchRule
- }}</gl-button>
+ <gl-button
+ v-gl-modal="$options.modalId"
+ class="gl-mt-5"
+ data-qa-selector="add_branch_rule_button"
+ category="secondary"
+ variant="info"
+ >{{ $options.i18n.addBranchRule }}</gl-button
+ >
<gl-modal
:ref="$options.modalId"
diff --git a/app/assets/javascripts/projects/settings/repository/branch_rules/components/branch_rule.vue b/app/assets/javascripts/projects/settings/repository/branch_rules/components/branch_rule.vue
index fa96eee5f92..a5ff478a826 100644
--- a/app/assets/javascripts/projects/settings/repository/branch_rules/components/branch_rule.vue
+++ b/app/assets/javascripts/projects/settings/repository/branch_rules/components/branch_rule.vue
@@ -27,6 +27,9 @@ export default {
branchRulesPath: {
default: '',
},
+ showCodeOwners: { default: false },
+ showStatusChecks: { default: false },
+ showApprovers: { default: false },
},
props: {
name: {
@@ -70,7 +73,7 @@ export default {
return this.approvalDetails.length;
},
detailsPath() {
- return `${this.branchRulesPath}?branch=${this.name}`;
+ return `${this.branchRulesPath}?branch=${encodeURIComponent(this.name)}`;
},
statusChecksText() {
return sprintf(this.$options.i18n.statusChecks, {
@@ -112,13 +115,13 @@ export default {
if (this.branchProtection?.allowForcePush) {
approvalDetails.push(this.$options.i18n.allowForcePush);
}
- if (this.branchProtection?.codeOwnerApprovalRequired) {
+ if (this.showCodeOwners && this.branchProtection?.codeOwnerApprovalRequired) {
approvalDetails.push(this.$options.i18n.codeOwnerApprovalRequired);
}
- if (this.statusChecksTotal) {
+ if (this.showStatusChecks && this.statusChecksTotal) {
approvalDetails.push(this.statusChecksText);
}
- if (this.approvalRulesTotal) {
+ if (this.showApprovers && this.approvalRulesTotal) {
approvalDetails.push(this.approvalRulesText);
}
if (this.mergeAccessLevels.total > 0) {
@@ -150,7 +153,11 @@ export default {
</script>
<template>
- <div class="gl-border-b gl-pt-5 gl-pb-5 gl-display-flex gl-justify-content-space-between">
+ <div
+ class="gl-border-b gl-pt-5 gl-pb-5 gl-display-flex gl-justify-content-space-between"
+ data-qa-selector="branch_content"
+ :data-qa-branch-name="name"
+ >
<div>
<strong class="gl-font-monospace">{{ name }}</strong>
@@ -166,7 +173,7 @@ export default {
<li v-for="(detail, index) in approvalDetails" :key="index">{{ detail }}</li>
</ul>
</div>
- <gl-button class="gl-align-self-start" :href="detailsPath">
+ <gl-button class="gl-align-self-start" data-qa-selector="details_button" :href="detailsPath">
{{ $options.i18n.detailsButtonLabel }}</gl-button
>
</div>
diff --git a/app/assets/javascripts/projects/settings/repository/branch_rules/mount_branch_rules.js b/app/assets/javascripts/projects/settings/repository/branch_rules/mount_branch_rules.js
index 042be089e09..a8736c87e22 100644
--- a/app/assets/javascripts/projects/settings/repository/branch_rules/mount_branch_rules.js
+++ b/app/assets/javascripts/projects/settings/repository/branch_rules/mount_branch_rules.js
@@ -2,6 +2,7 @@ import Vue from 'vue';
import VueApollo from 'vue-apollo';
import createDefaultClient from '~/lib/graphql';
import BranchRulesApp from '~/projects/settings/repository/branch_rules/app.vue';
+import { parseBoolean } from '~/lib/utils/common_utils';
Vue.use(VueApollo);
@@ -12,7 +13,13 @@ const apolloProvider = new VueApollo({
export default function mountBranchRules(el) {
if (!el) return null;
- const { projectPath, branchRulesPath } = el.dataset;
+ const {
+ projectPath,
+ branchRulesPath,
+ showCodeOwners,
+ showStatusChecks,
+ showApprovers,
+ } = el.dataset;
return new Vue({
el,
@@ -20,6 +27,9 @@ export default function mountBranchRules(el) {
provide: {
projectPath,
branchRulesPath,
+ showCodeOwners: parseBoolean(showCodeOwners),
+ showStatusChecks: parseBoolean(showStatusChecks),
+ showApprovers: parseBoolean(showApprovers),
},
render(createElement) {
return createElement(BranchRulesApp);
diff --git a/app/assets/javascripts/projects/settings/topics/components/topics_token_selector.vue b/app/assets/javascripts/projects/settings/topics/components/topics_token_selector.vue
index 3d553e71f71..47477d39b8a 100644
--- a/app/assets/javascripts/projects/settings/topics/components/topics_token_selector.vue
+++ b/app/assets/javascripts/projects/settings/topics/components/topics_token_selector.vue
@@ -1,5 +1,6 @@
<script>
-import { GlTokenSelector, GlAvatarLabeled } from '@gitlab/ui';
+import { GlTokenSelector, GlAvatarLabeled, GlFormGroup, GlLink, GlSprintf } from '@gitlab/ui';
+import { helpPagePath } from '~/helpers/help_page_helper';
import { s__ } from '~/locale';
import { AVATAR_SHAPE_OPTION_RECT } from '~/vue_shared/constants';
import searchProjectTopics from '~/graphql_shared/queries/project_topics_search.query.graphql';
@@ -8,8 +9,15 @@ export default {
components: {
GlTokenSelector,
GlAvatarLabeled,
+ GlFormGroup,
+ GlLink,
+ GlSprintf,
},
i18n: {
+ topicsTitle: s__('ProjectSettings|Topics'),
+ topicsHelpText: s__(
+ 'ProjectSettings|Topics are publicly visible even on private projects. Do not include sensitive information in topic names. %{linkStart}Learn more%{linkEnd}.',
+ ),
placeholder: s__('ProjectSettings|Search for topic'),
},
props: {
@@ -51,6 +59,11 @@ export default {
placeholderText() {
return this.selectedTokens.length ? '' : this.$options.i18n.placeholder;
},
+ topicsHelpUrl() {
+ return helpPagePath('user/admin_area/index.html', {
+ anchor: 'administering-topics',
+ });
+ },
},
methods: {
handleEnter(event) {
@@ -70,25 +83,34 @@ export default {
};
</script>
<template>
- <gl-token-selector
- ref="tokenSelector"
- v-model="selectedTokens"
- :dropdown-items="topics"
- :loading="loading"
- allow-user-defined-tokens
- :placeholder="placeholderText"
- @keydown.enter="handleEnter"
- @text-input="filterTopics"
- @input="onTokensUpdate"
- >
- <template #dropdown-item-content="{ dropdownItem }">
- <gl-avatar-labeled
- :src="dropdownItem.avatarUrl"
- :entity-name="dropdownItem.name"
- :label="dropdownItem.title"
- :size="32"
- :shape="$options.AVATAR_SHAPE_OPTION_RECT"
- />
+ <gl-form-group id="project_topics" :label="$options.i18n.topicsTitle">
+ <gl-token-selector
+ ref="tokenSelector"
+ v-model="selectedTokens"
+ :dropdown-items="topics"
+ :loading="loading"
+ allow-user-defined-tokens
+ :placeholder="placeholderText"
+ @keydown.enter="handleEnter"
+ @text-input="filterTopics"
+ @input="onTokensUpdate"
+ >
+ <template #dropdown-item-content="{ dropdownItem }">
+ <gl-avatar-labeled
+ :src="dropdownItem.avatarUrl"
+ :entity-name="dropdownItem.name"
+ :label="dropdownItem.title"
+ :size="32"
+ :shape="$options.AVATAR_SHAPE_OPTION_RECT"
+ />
+ </template>
+ </gl-token-selector>
+ <template #description>
+ <gl-sprintf :message="$options.i18n.topicsHelpText">
+ <template #link="{ content }">
+ <gl-link :href="topicsHelpUrl" target="_blank">{{ content }}</gl-link>
+ </template>
+ </gl-sprintf>
</template>
- </gl-token-selector>
+ </gl-form-group>
</template>
diff --git a/app/assets/javascripts/projects/settings/utils.js b/app/assets/javascripts/projects/settings/utils.js
index ea4574119c0..9c19657bb39 100644
--- a/app/assets/javascripts/projects/settings/utils.js
+++ b/app/assets/javascripts/projects/settings/utils.js
@@ -1,3 +1,24 @@
+import { joinPaths } from '~/lib/utils/url_utility';
+
+export const generateRefDestinationPath = (selectedRef) => {
+ const namespace = '-/settings/ci_cd';
+ const { pathname } = window.location;
+
+ if (!selectedRef || !pathname.includes(namespace)) {
+ return window.location.href;
+ }
+
+ const [projectRootPath] = pathname.split(namespace);
+
+ const destinationPath = joinPaths(projectRootPath, namespace);
+
+ const newURL = new URL(window.location);
+ newURL.pathname = destinationPath;
+ newURL.searchParams.set('ref', selectedRef);
+
+ return newURL.href;
+};
+
export const getAccessLevels = (accessLevels = {}) => {
const total = accessLevels.edges?.length;
const accessLevelTypes = { total, users: [], groups: [], roles: [] };
diff --git a/app/assets/javascripts/projects/settings_service_desk/components/service_desk_root.vue b/app/assets/javascripts/projects/settings_service_desk/components/service_desk_root.vue
index b79b3fa4573..79ece99e6ec 100644
--- a/app/assets/javascripts/projects/settings_service_desk/components/service_desk_root.vue
+++ b/app/assets/javascripts/projects/settings_service_desk/components/service_desk_root.vue
@@ -8,7 +8,7 @@ import ServiceDeskSetting from './service_desk_setting.vue';
export default {
customEmailHelpPath: helpPagePath('/user/project/service_desk.html', {
- anchor: 'using-a-custom-email-address',
+ anchor: 'use-a-custom-email-address',
}),
components: {
GlAlert,
diff --git a/app/assets/javascripts/projects/settings_service_desk/components/service_desk_setting.vue b/app/assets/javascripts/projects/settings_service_desk/components/service_desk_setting.vue
index 85550e262e6..5a3930b5df4 100644
--- a/app/assets/javascripts/projects/settings_service_desk/components/service_desk_setting.vue
+++ b/app/assets/javascripts/projects/settings_service_desk/components/service_desk_setting.vue
@@ -102,12 +102,12 @@ export default {
},
emailSuffixHelpUrl() {
return helpPagePath('user/project/service_desk.html', {
- anchor: 'configuring-a-custom-email-address-suffix',
+ anchor: 'configure-a-custom-email-address-suffix',
});
},
customEmailAddressHelpUrl() {
return helpPagePath('user/project/service_desk.html', {
- anchor: 'using-a-custom-email-address',
+ anchor: 'use-a-custom-email-address',
});
},
},
@@ -155,7 +155,7 @@ export default {
<div v-if="isEnabled" class="row mt-3">
<div class="col-md-9 mb-0">
<gl-form-group
- :label="__('Email address to use for Support Desk')"
+ :label="__('Email address to use for Service Desk')"
label-for="incoming-email"
data-testid="incoming-email-label"
>
diff --git a/app/assets/javascripts/projects/star.js b/app/assets/javascripts/projects/star.js
index 55c3d68cd11..f294811dfff 100644
--- a/app/assets/javascripts/projects/star.js
+++ b/app/assets/javascripts/projects/star.js
@@ -1,4 +1,4 @@
-import { createAlert } from '~/flash';
+import { createAlert } from '~/alert';
import axios from '~/lib/utils/axios_utils';
import { spriteIcon } from '~/lib/utils/common_utils';
import { __, s__ } from '~/locale';
diff --git a/app/assets/javascripts/projects/tree/components/commit_pipeline_status_component.vue b/app/assets/javascripts/projects/tree/components/commit_pipeline_status_component.vue
index 9f9b6424125..5b620aa2300 100644
--- a/app/assets/javascripts/projects/tree/components/commit_pipeline_status_component.vue
+++ b/app/assets/javascripts/projects/tree/components/commit_pipeline_status_component.vue
@@ -1,7 +1,7 @@
<script>
import { GlLoadingIcon, GlTooltipDirective } from '@gitlab/ui';
import Visibility from 'visibilityjs';
-import { createAlert } from '~/flash';
+import { createAlert } from '~/alert';
import Poll from '~/lib/utils/poll';
import { __, s__, sprintf } from '~/locale';
import CiIcon from '~/vue_shared/components/ci_icon.vue';