diff options
Diffstat (limited to 'app/assets/javascripts/pages/projects')
25 files changed, 348 insertions, 310 deletions
diff --git a/app/assets/javascripts/pages/projects/branches/new/index.js b/app/assets/javascripts/pages/projects/branches/new/index.js index dbae89b5ade..f2b03468b0b 100644 --- a/app/assets/javascripts/pages/projects/branches/new/index.js +++ b/app/assets/javascripts/pages/projects/branches/new/index.js @@ -1,7 +1,6 @@ import NewBranchForm from '~/new_branch_form'; +import initNewBranchRefSelector from '~/branches/init_new_branch_ref_selector'; +initNewBranchRefSelector(); // eslint-disable-next-line no-new -new NewBranchForm( - document.querySelector('.js-create-branch-form'), - JSON.parse(document.getElementById('availableRefs').innerHTML), -); +new NewBranchForm(document.querySelector('.js-create-branch-form')); diff --git a/app/assets/javascripts/pages/projects/ci/lints/show/index.js b/app/assets/javascripts/pages/projects/ci/lints/show/index.js index 6e1cdf557b5..caac76fc6d7 100644 --- a/app/assets/javascripts/pages/projects/ci/lints/show/index.js +++ b/app/assets/javascripts/pages/projects/ci/lints/show/index.js @@ -1,3 +1,3 @@ -import initCiLint from '~/ci_lint'; +import initCiLint from '~/ci/ci_lint'; initCiLint(); diff --git a/app/assets/javascripts/pages/projects/ci/pipeline_editor/show/index.js b/app/assets/javascripts/pages/projects/ci/pipeline_editor/show/index.js index 67d32648ce8..7e91f23dd7f 100644 --- a/app/assets/javascripts/pages/projects/ci/pipeline_editor/show/index.js +++ b/app/assets/javascripts/pages/projects/ci/pipeline_editor/show/index.js @@ -1,3 +1,3 @@ -import { initPipelineEditor } from '~/pipeline_editor'; +import { initPipelineEditor } from '~/ci/pipeline_editor'; initPipelineEditor(); diff --git a/app/assets/javascripts/pages/projects/commits/show/index.js b/app/assets/javascripts/pages/projects/commits/show/index.js index ee74628a994..f5ecf9be591 100644 --- a/app/assets/javascripts/pages/projects/commits/show/index.js +++ b/app/assets/javascripts/pages/projects/commits/show/index.js @@ -1,9 +1,10 @@ import ShortcutsNavigation from '~/behaviors/shortcuts/shortcuts_navigation'; import CommitsList from '~/commits'; import GpgBadges from '~/gpg_badges'; -import mountCommits from '~/projects/commits'; +import { mountCommits, initCommitsRefSwitcher } from '~/projects/commits'; new CommitsList(document.querySelector('.js-project-commits-show').dataset.commitsLimit); // eslint-disable-line no-new new ShortcutsNavigation(); // eslint-disable-line no-new GpgBadges.fetch(); mountCommits(document.getElementById('js-author-dropdown')); +initCommitsRefSwitcher(); diff --git a/app/assets/javascripts/pages/projects/cycle_analytics/show/index.js b/app/assets/javascripts/pages/projects/cycle_analytics/show/index.js index bef21ef8fdf..05a1bbc69ed 100644 --- a/app/assets/javascripts/pages/projects/cycle_analytics/show/index.js +++ b/app/assets/javascripts/pages/projects/cycle_analytics/show/index.js @@ -1,3 +1,3 @@ -import initCycleAnalytics from '~/cycle_analytics'; +import initCycleAnalytics from '~/analytics/cycle_analytics'; initCycleAnalytics(); diff --git a/app/assets/javascripts/pages/projects/environments/show/index.js b/app/assets/javascripts/pages/projects/environments/show/index.js index 53e48ad8d86..1ce8899ac63 100644 --- a/app/assets/javascripts/pages/projects/environments/show/index.js +++ b/app/assets/javascripts/pages/projects/environments/show/index.js @@ -1,5 +1,6 @@ import initConfirmRollBackModal from '~/environments/init_confirm_rollback_modal'; -import { initHeader } from '~/environments/mount_show'; +import { initHeader, initPage } from '~/environments/mount_show'; initHeader(); +initPage(); initConfirmRollBackModal(); 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 30cefa3d717..91650003d4a 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 @@ -23,6 +23,7 @@ import { VISIBILITY_LEVEL_INTERNAL_STRING, VISIBILITY_LEVEL_PUBLIC_STRING, VISIBILITY_LEVELS_STRING_TO_INTEGER, + VISIBILITY_LEVELS_INTEGER_TO_STRING, } from '~/visibility_level/constants'; import ProjectNamespace from './project_namespace.vue'; @@ -105,39 +106,8 @@ export default { }; }, computed: { - projectVisibilityLevel() { - return VISIBILITY_LEVELS_STRING_TO_INTEGER[this.projectVisibility]; - }, - namespaceVisibilityLevel() { - const visibility = - this.form.fields.namespace.value?.visibility || VISIBILITY_LEVEL_PUBLIC_STRING; - return VISIBILITY_LEVELS_STRING_TO_INTEGER[visibility]; - }, - visibilityLevelCap() { - return Math.min(this.projectVisibilityLevel, this.namespaceVisibilityLevel); - }, - restrictedVisibilityLevelsSet() { - return new Set(this.restrictedVisibilityLevels); - }, allowedVisibilityLevels() { - const allowedLevels = Object.entries(VISIBILITY_LEVELS_STRING_TO_INTEGER).reduce( - (levels, [levelName, levelValue]) => { - if ( - !this.restrictedVisibilityLevelsSet.has(levelValue) && - levelValue <= this.visibilityLevelCap - ) { - levels.push(levelName); - } - return levels; - }, - [], - ); - - if (!allowedLevels.length) { - return [VISIBILITY_LEVEL_PRIVATE_STRING]; - } - - return allowedLevels; + return this.getAllowedVisibilityLevels(); }, visibilityLevels() { return [ @@ -178,13 +148,60 @@ export default { return !this.allowedVisibilityLevels.includes(visibility); }, getInitialVisibilityValue() { - return this.restrictedVisibilityLevels.length !== 0 ? null : this.projectVisibility; + return this.getMaximumAllowedVisibilityLevel(this.projectVisibility); }, setNamespace(namespace) { - this.form.fields.visibility.value = - this.restrictedVisibilityLevels.length !== 0 ? null : VISIBILITY_LEVEL_PRIVATE_STRING; this.form.fields.namespace.value = namespace; this.form.fields.namespace.state = true; + this.form.fields.visibility.value = this.getMaximumAllowedVisibilityLevel( + this.form.fields.visibility.value, + ); + }, + getProjectVisibilityLevel() { + return VISIBILITY_LEVELS_STRING_TO_INTEGER[this.projectVisibility]; + }, + getNamespaceVisibilityLevel() { + const visibility = + this.form?.fields?.namespace?.value?.visibility || VISIBILITY_LEVEL_PUBLIC_STRING; + return VISIBILITY_LEVELS_STRING_TO_INTEGER[visibility]; + }, + getVisibilityLevelCap() { + return Math.min(this.getProjectVisibilityLevel(), this.getNamespaceVisibilityLevel()); + }, + getRestrictedVisibilityLevelsSet() { + return new Set(this.restrictedVisibilityLevels); + }, + getAllowedVisibilityLevels() { + const allowedLevels = Object.entries(VISIBILITY_LEVELS_STRING_TO_INTEGER).reduce( + (levels, [levelName, levelValue]) => { + if ( + !this.getRestrictedVisibilityLevelsSet().has(levelValue) && + levelValue <= this.getVisibilityLevelCap() + ) { + levels.push(levelName); + } + return levels; + }, + [], + ); + + if (!allowedLevels.length) { + return [VISIBILITY_LEVEL_PRIVATE_STRING]; + } + + return allowedLevels; + }, + getMaximumAllowedVisibilityLevel(visibility) { + const allowedVisibilities = this.getAllowedVisibilityLevels().map( + (s) => VISIBILITY_LEVELS_STRING_TO_INTEGER[s], + ); + const current = VISIBILITY_LEVELS_STRING_TO_INTEGER[visibility]; + const lower = allowedVisibilities.filter((l) => l <= current); + if (lower.length) { + return VISIBILITY_LEVELS_INTEGER_TO_STRING[Math.max(...lower)]; + } + const higher = allowedVisibilities.filter((l) => l >= current); + return VISIBILITY_LEVELS_INTEGER_TO_STRING[Math.min(...higher)]; }, async onSubmit() { this.form.showValidation = true; diff --git a/app/assets/javascripts/pages/projects/graphs/components/code_coverage.vue b/app/assets/javascripts/pages/projects/graphs/components/code_coverage.vue index 08d24344ffc..10bfcdc2294 100644 --- a/app/assets/javascripts/pages/projects/graphs/components/code_coverage.vue +++ b/app/assets/javascripts/pages/projects/graphs/components/code_coverage.vue @@ -1,5 +1,5 @@ <script> -import { GlAlert, GlButton, GlDropdown, GlDropdownItem, GlSprintf } from '@gitlab/ui'; +import { GlAlert, GlButton, GlListbox, GlSprintf } from '@gitlab/ui'; import { GlAreaChart } from '@gitlab/ui/dist/charts'; import { get } from 'lodash'; import { formatDate } from '~/lib/utils/datetime_utility'; @@ -12,8 +12,7 @@ export default { GlAlert, GlAreaChart, GlButton, - GlDropdown, - GlDropdownItem, + GlListbox, GlSprintf, }, props: { @@ -96,6 +95,14 @@ export default { formattedData() { return this.sortedData.map((value) => [value.date, value.coverage]); }, + mappedCoverages() { + return this.dailyCoverageData?.map((item, index) => ({ + // A numerical index makes an item into a group header, so + // convert these to strings to get non-header GlListbox items + value: index.toString(), + text: item.group_name, + })); + }, chartData() { return [ { @@ -175,18 +182,13 @@ export default { {{ __('It seems that there is currently no available data for code coverage') }} </span> </gl-alert> - <gl-dropdown v-if="canShowData" :text="selectedDailyCoverageName"> - <gl-dropdown-item - v-for="({ group_name }, index) in dailyCoverageData" - :key="index" - :value="group_name" - is-check-item - :is-checked="index === selectedCoverageIndex" - @click="setSelectedCoverage(index)" - > - {{ group_name }} - </gl-dropdown-item> - </gl-dropdown> + <gl-listbox + v-if="canShowData" + :items="mappedCoverages" + :selected="selectedCoverageIndex.toString()" + :toggle-text="selectedDailyCoverageName" + @select="setSelectedCoverage" + /> </div> <gl-area-chart v-if="!isLoading" 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 index 2d26d3922bf..653f903c6d1 100644 --- a/app/assets/javascripts/pages/projects/merge_requests/creations/new/compare.js +++ b/app/assets/javascripts/pages/projects/merge_requests/creations/new/compare.js @@ -26,7 +26,10 @@ const updateCommitList = (url, $emptyState, $loadingIndicator, $commitList, para export default (mrNewCompareNode) => { const { sourceBranchUrl, targetBranchUrl } = mrNewCompareNode.dataset; - initTargetProjectDropdown(); + + if (!window.gon?.features?.mrCompareDropdowns) { + initTargetProjectDropdown(); + } const updateSourceBranchCommitList = () => updateCommitList( 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 9aecd154483..b3868653d6a 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,10 +1,37 @@ +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'; 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; + + // eslint-disable-next-line no-new + new Vue({ + el, + name: 'TargetProjectDropdown', + provide: { + targetProjectsPath, + currentProject: JSON.parse(currentProject), + }, + render(h) { + return h(TargetProjectDropdown, { + on: { + 'project-selected': function projectSelectedFunction(refsUrl) { + const $targetBranchDropdown = $('.js-target-branch'); + $targetBranchDropdown.data('refsUrl', refsUrl); + $targetBranchDropdown.data('deprecatedJQueryDropdown').clearMenu(); + }, + }, + }); + }, + }); } else { const mrNewSubmitNode = document.querySelector('.js-merge-request-new-submit'); // eslint-disable-next-line no-new diff --git a/app/assets/javascripts/pages/projects/merge_requests/diffs/index.js b/app/assets/javascripts/pages/projects/merge_requests/diffs/index.js new file mode 100644 index 00000000000..77294c0fb9e --- /dev/null +++ b/app/assets/javascripts/pages/projects/merge_requests/diffs/index.js @@ -0,0 +1,5 @@ +import initDiffsApp from '~/diffs'; +import { initMrPage } from '../page'; + +initMrPage(); +initDiffsApp(); 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 2399aafc9b5..b3a09cc0be3 100644 --- a/app/assets/javascripts/pages/projects/merge_requests/index/index.js +++ b/app/assets/javascripts/pages/projects/merge_requests/index/index.js @@ -1,20 +1,13 @@ import addExtraTokensForMergeRequests from 'ee_else_ce/filtered_search/add_extra_tokens_for_merge_requests'; import ShortcutsNavigation from '~/behaviors/shortcuts/shortcuts_navigation'; import IssuableFilteredSearchTokenKeys from '~/filtered_search/issuable_filtered_search_token_keys'; -import { initCsvImportExportButtons, initIssuableByEmail } from '~/issuable'; -import { - initBulkUpdateSidebar, - initStatusDropdown, - initSubscriptionsDropdown, -} from '~/issuable/bulk_update_sidebar'; 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); -initStatusDropdown(); -initSubscriptionsDropdown(); addExtraTokensForMergeRequests(IssuableFilteredSearchTokenKeys); IssuableFilteredSearchTokenKeys.removeTokensForKeys('iteration'); diff --git a/app/assets/javascripts/pages/projects/merge_requests/init_merge_request.js b/app/assets/javascripts/pages/projects/merge_requests/init_merge_request.js index 42fa306d226..a4e3ddfc506 100644 --- a/app/assets/javascripts/pages/projects/merge_requests/init_merge_request.js +++ b/app/assets/javascripts/pages/projects/merge_requests/init_merge_request.js @@ -2,6 +2,7 @@ import $ from 'jquery'; import IssuableForm from 'ee_else_ce/issuable/issuable_form'; +import IssuableLabelSelector from '~/issuable/issuable_label_selector'; import ShortcutsNavigation from '~/behaviors/shortcuts/shortcuts_navigation'; import Diff from '~/diff'; import GLForm from '~/gl_form'; @@ -14,6 +15,7 @@ export default () => { new ShortcutsNavigation(); new GLForm($('.merge-request-form')); new IssuableForm($('.merge-request-form')); + IssuableLabelSelector(); new LabelsSelect(); new IssuableTemplateSelectors({ warnTemplateOverride: true, diff --git a/app/assets/javascripts/pages/projects/merge_requests/page.js b/app/assets/javascripts/pages/projects/merge_requests/page.js new file mode 100644 index 00000000000..a8699b350f8 --- /dev/null +++ b/app/assets/javascripts/pages/projects/merge_requests/page.js @@ -0,0 +1,45 @@ +import Vue from 'vue'; +import StickyHeader from '~/merge_requests/components/sticky_header.vue'; +import { initIssuableHeaderWarnings } from '~/issuable'; +import initMrNotes from '~/mr_notes'; +import store from '~/mr_notes/stores'; +import initSidebarBundle from '~/sidebar/sidebar_bundle'; +import { apolloProvider } from '~/graphql_shared/issuable_client'; +import { parseBoolean } from '~/lib/utils/common_utils'; +import initShow from './init_merge_request_show'; +import getStateQuery from './queries/get_state.query.graphql'; + +export function initMrPage() { + initMrNotes(); + initShow(); +} + +requestIdleCallback(() => { + initSidebarBundle(store); + initIssuableHeaderWarnings(store); + + const el = document.getElementById('js-merge-sticky-header'); + + if (el) { + const { data } = el.dataset; + const { iid, projectPath, title, tabs, isFluidLayout } = JSON.parse(data); + + // eslint-disable-next-line no-new + new Vue({ + el, + store, + apolloProvider, + provide: { + query: getStateQuery, + iid, + projectPath, + title, + tabs, + isFluidLayout: parseBoolean(isFluidLayout), + }, + render(h) { + return h(StickyHeader); + }, + }); + } +}); 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 cc5c393ff8c..568bf19b55e 100644 --- a/app/assets/javascripts/pages/projects/merge_requests/show/index.js +++ b/app/assets/javascripts/pages/projects/merge_requests/show/index.js @@ -1,45 +1,5 @@ -import Vue from 'vue'; -import StickyHeader from '~/merge_requests/components/sticky_header.vue'; -import { initReviewBar } from '~/batch_comments'; -import { initIssuableHeaderWarnings } from '~/issuable'; -import initMrNotes from '~/mr_notes'; -import store from '~/mr_notes/stores'; -import initSidebarBundle from '~/sidebar/sidebar_bundle'; -import { apolloProvider } from '~/graphql_shared/issuable_client'; -import { parseBoolean } from '~/lib/utils/common_utils'; -import initShow from '../init_merge_request_show'; -import getStateQuery from '../queries/get_state.query.graphql'; +import initNotesApp from '~/mr_notes/init_notes'; +import { initMrPage } from '../page'; -initMrNotes(); -initShow(); - -requestIdleCallback(() => { - initSidebarBundle(store); - initReviewBar(); - initIssuableHeaderWarnings(store); - - const el = document.getElementById('js-merge-sticky-header'); - - if (el) { - const { data } = el.dataset; - const { iid, projectPath, title, tabs, isFluidLayout } = JSON.parse(data); - - // eslint-disable-next-line no-new - new Vue({ - el, - store, - apolloProvider, - provide: { - query: getStateQuery, - iid, - projectPath, - title, - tabs, - isFluidLayout: parseBoolean(isFluidLayout), - }, - render(h) { - return h(StickyHeader); - }, - }); - } -}); +initMrPage(); +initNotesApp(); diff --git a/app/assets/javascripts/pages/projects/ml/candidates/show/index.js b/app/assets/javascripts/pages/projects/ml/candidates/show/index.js new file mode 100644 index 00000000000..c1acef5ac13 --- /dev/null +++ b/app/assets/javascripts/pages/projects/ml/candidates/show/index.js @@ -0,0 +1,27 @@ +import Vue from 'vue'; +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(); 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 0a9d9f4c987..97e436920c7 100644 --- a/app/assets/javascripts/pages/projects/ml/experiments/show/index.js +++ b/app/assets/javascripts/pages/projects/ml/experiments/show/index.js @@ -1,5 +1,5 @@ import Vue from 'vue'; -import ShowExperiment from '~/ml/experiment_tracking/components/experiment.vue'; +import MlExperiment from '~/ml/experiment_tracking/components/ml_experiment.vue'; const initShowExperiment = () => { const element = document.querySelector('#js-show-ml-experiment'); @@ -23,7 +23,7 @@ const initShowExperiment = () => { paramNames, }, render(h) { - return h(ShowExperiment); + return h(MlExperiment); }, }); }; diff --git a/app/assets/javascripts/pages/projects/new/index.js b/app/assets/javascripts/pages/projects/new/index.js index 50733d8a145..d022428df98 100644 --- a/app/assets/javascripts/pages/projects/new/index.js +++ b/app/assets/javascripts/pages/projects/new/index.js @@ -4,10 +4,8 @@ import { initDeploymentTargetSelect, } from '~/projects/new'; import initProjectVisibilitySelector from '~/projects/project_visibility'; -import initProjectNew from '~/projects/project_new'; initProjectVisibilitySelector(); -initProjectNew.bindEvents(); initNewProjectCreation(); initNewProjectUrlSelect(); initDeploymentTargetSelect(); diff --git a/app/assets/javascripts/pages/projects/pipeline_schedules/shared/components/interval_pattern_input.vue b/app/assets/javascripts/pages/projects/pipeline_schedules/shared/components/interval_pattern_input.vue index 85443843684..fd8b1a6290f 100644 --- a/app/assets/javascripts/pages/projects/pipeline_schedules/shared/components/interval_pattern_input.vue +++ b/app/assets/javascripts/pages/projects/pipeline_schedules/shared/components/interval_pattern_input.vue @@ -39,6 +39,11 @@ export default { required: false, default: '', }, + sendNativeErrors: { + type: Boolean, + required: false, + default: true, + }, }, data() { return { @@ -114,9 +119,11 @@ export default { cronInterval() { // updates field validation state when model changes, as // glFieldError only updates on input. - this.$nextTick(() => { - gl.pipelineScheduleFieldErrors.updateFormValidityState(); - }); + if (this.sendNativeErrors) { + this.$nextTick(() => { + gl.pipelineScheduleFieldErrors.updateFormValidityState(); + }); + } }, radioValue: { immediate: true, diff --git a/app/assets/javascripts/pages/projects/pipeline_schedules/shared/components/timezone_dropdown.js b/app/assets/javascripts/pages/projects/pipeline_schedules/shared/components/timezone_dropdown.js deleted file mode 100644 index bc467952551..00000000000 --- a/app/assets/javascripts/pages/projects/pipeline_schedules/shared/components/timezone_dropdown.js +++ /dev/null @@ -1,82 +0,0 @@ -import initDeprecatedJQueryDropdown from '~/deprecated_jquery_dropdown'; -import { formatTimezone } from '~/lib/utils/datetime_utility'; - -const defaultTimezone = { identifier: 'Etc/UTC', name: 'UTC', offset: 0 }; -const defaults = { - $inputEl: null, - $dropdownEl: null, - onSelectTimezone: null, - displayFormat: (item) => item.name, -}; - -export const formatUtcOffset = (offset) => { - const parsed = parseInt(offset, 10); - if (Number.isNaN(parsed) || parsed === 0) { - return `0`; - } - const prefix = offset > 0 ? '+' : '-'; - return `${prefix} ${Math.abs(offset / 3600)}`; -}; - -export const findTimezoneByIdentifier = (tzList = [], identifier = null) => { - if (tzList && tzList.length && identifier && identifier.length) { - return tzList.find((tz) => tz.identifier === identifier) || null; - } - return null; -}; - -export default class TimezoneDropdown { - constructor({ - $dropdownEl, - $inputEl, - onSelectTimezone, - displayFormat, - allowEmpty = false, - } = defaults) { - this.$dropdown = $dropdownEl; - this.$dropdownToggle = this.$dropdown.find('.dropdown-toggle-text'); - this.$input = $inputEl; - this.timezoneData = this.$dropdown.data('data') || []; - - this.onSelectTimezone = onSelectTimezone; - this.displayFormat = displayFormat || defaults.displayFormat; - this.allowEmpty = allowEmpty; - - this.initDropdown(); - } - - initDropdown() { - initDeprecatedJQueryDropdown(this.$dropdown, { - data: this.timezoneData, - filterable: true, - selectable: true, - toggleLabel: this.displayFormat, - search: { - fields: ['name'], - }, - clicked: (cfg) => this.handleDropdownChange(cfg), - text: (item) => formatTimezone(item), - }); - - const initialTimezone = findTimezoneByIdentifier(this.timezoneData, this.$input.val()); - - if (initialTimezone !== null) { - this.setDropdownValue(initialTimezone); - } else if (!this.allowEmpty) { - this.setDropdownValue(defaultTimezone); - } - } - - setDropdownValue(timezone) { - this.$dropdownToggle.text(this.displayFormat(timezone)); - this.$input.val(timezone.identifier); - } - - handleDropdownChange({ selectedObj, e }) { - e.preventDefault(); - this.$input.val(selectedObj.identifier); - if (this.onSelectTimezone) { - this.onSelectTimezone({ selectedObj, e }); - } - } -} diff --git a/app/assets/javascripts/pages/projects/project.js b/app/assets/javascripts/pages/projects/project.js index d177c67f133..4c9eb830ff6 100644 --- a/app/assets/javascripts/pages/projects/project.js +++ b/app/assets/javascripts/pages/projects/project.js @@ -11,10 +11,14 @@ 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'; +const BRANCH_GROUP_NAME = __('Branches'); +const TAG_GROUP_NAME = __('Tags'); + export default class Project { constructor() { initClonePanel(); - // Ref switcher if (document.querySelector('.js-project-refs-dropdown')) { Project.initRefSwitcher(); @@ -62,6 +66,7 @@ export default class Project { return $('.js-project-refs-dropdown').each(function () { const $dropdown = $(this); const selected = $dropdown.data('selected'); + const refType = $dropdown.data('refType'); const fieldName = $dropdown.data('fieldName'); const shouldVisit = Boolean($dropdown.data('visit')); const $form = $dropdown.closest('form'); @@ -91,18 +96,32 @@ export default class Project { filterByText: true, inputFieldName: $dropdown.data('inputFieldName'), fieldName, - renderRow(ref) { + renderRow(ref, _, params) { const li = refListItem.cloneNode(false); const link = refLink.cloneNode(false); if (ref === selected) { - link.className = 'is-active'; + // Check group and current ref type to avoid adding a class when tags and branches share the same name + if ( + (refType === BRANCH_REF_TYPE && params.group === BRANCH_GROUP_NAME) || + (refType === TAG_REF_TYPE && params.group === TAG_GROUP_NAME) || + !refType + ) { + link.className = 'is-active'; + } } + link.textContent = ref; link.dataset.ref = ref; if (ref.length > 0 && shouldVisit) { - link.href = mergeUrlParams({ [fieldName]: ref }, linkTarget); + const urlParams = { [fieldName]: ref }; + if (params.group === BRANCH_GROUP_NAME) { + urlParams.ref_type = BRANCH_REF_TYPE; + } else { + urlParams.ref_type = TAG_REF_TYPE; + } + link.href = mergeUrlParams(urlParams, linkTarget); } li.appendChild(link); diff --git a/app/assets/javascripts/pages/projects/settings/merge_requests/index.js b/app/assets/javascripts/pages/projects/settings/merge_requests/index.js index 739e666644c..0f7ede8ed42 100644 --- a/app/assets/javascripts/pages/projects/settings/merge_requests/index.js +++ b/app/assets/javascripts/pages/projects/settings/merge_requests/index.js @@ -1,9 +1,6 @@ import groupsSelect from '~/groups_select'; import UserCallout from '~/user_callout'; -import UsersSelect from '~/users_select'; -// eslint-disable-next-line no-new -new UsersSelect(); groupsSelect(); // eslint-disable-next-line no-new 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 c37b4cc643a..5fa3288bbef 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 @@ -23,6 +23,12 @@ import ProjectSettingRow from './project_setting_row.vue'; const FEATURE_ACCESS_LEVEL_ANONYMOUS = [30, s__('ProjectSettings|Everyone')]; +const PACKAGE_REGISTRY_ACCESS_LEVEL_DEFAULT_BY_PROJECT_VISIBILITY = { + [VISIBILITY_LEVEL_PRIVATE_INTEGER]: featureAccessLevel.PROJECT_MEMBERS, + [VISIBILITY_LEVEL_INTERNAL_INTEGER]: featureAccessLevel.EVERYONE, + [VISIBILITY_LEVEL_PUBLIC_INTEGER]: FEATURE_ACCESS_LEVEL_ANONYMOUS[0], +}; + export default { i18n: { ...CVE_ID_REQUEST_BUTTON_I18N, @@ -32,7 +38,6 @@ export default { issuesLabel: s__('ProjectSettings|Issues'), lfsLabel: s__('ProjectSettings|Git Large File Storage (LFS)'), mergeRequestsLabel: s__('ProjectSettings|Merge requests'), - operationsLabel: s__('ProjectSettings|Operations'), environmentsLabel: s__('ProjectSettings|Environments'), environmentsHelpText: s__( 'ProjectSettings|Every project can make deployments to environments either via CI/CD or API calls. Non-project members have read-only access.', @@ -47,11 +52,15 @@ export default { 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|Every project can have its own space to store its packages.', + 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', + ), pagesLabel: s__('ProjectSettings|Pages'), ciCdLabel: __('CI/CD'), repositoryLabel: s__('ProjectSettings|Repository'), @@ -249,7 +258,6 @@ export default { analyticsAccessLevel: featureAccessLevel.EVERYONE, requirementsAccessLevel: featureAccessLevel.EVERYONE, securityAndComplianceAccessLevel: featureAccessLevel.PROJECT_MEMBERS, - operationsAccessLevel: featureAccessLevel.EVERYONE, environmentsAccessLevel: featureAccessLevel.EVERYONE, featureFlagsAccessLevel: featureAccessLevel.PROJECT_MEMBERS, infrastructureAccessLevel: featureAccessLevel.PROJECT_MEMBERS, @@ -287,18 +295,6 @@ export default { ); }, - packageRegistryFeatureAccessLevelOptions() { - const options = [FEATURE_ACCESS_LEVEL_ANONYMOUS]; - - if (this.visibilityLevel === VISIBILITY_LEVEL_PRIVATE_INTEGER) { - options.unshift(featureAccessLevelMembers); - } else if (this.visibilityLevel === VISIBILITY_LEVEL_INTERNAL_INTEGER) { - options.unshift(featureAccessLevelEveryone); - } - - return options; - }, - pagesFeatureAccessLevelOptions() { const options = [featureAccessLevelMembers]; @@ -318,10 +314,6 @@ export default { return options; }, - operationsEnabled() { - return this.operationsAccessLevel > featureAccessLevel.NOT_ENABLED; - }, - environmentsEnabled() { return this.environmentsAccessLevel > featureAccessLevel.NOT_ENABLED; }, @@ -351,7 +343,7 @@ export default { } return s__( - 'ProjectSettings|View and edit files in this project. Non-project members have only read access.', + 'ProjectSettings|View and edit files in this project. When set to **Everyone With Access** non-project members have only read access.', ); }, cveIdRequestIsDisabled() { @@ -366,16 +358,17 @@ export default { packageRegistryAccessLevelEnabled() { return this.glFeatures.packageRegistryAccessLevel; }, - splitOperationsEnabled() { - return this.glFeatures.splitOperationsVisibilityPermissions; + packageRegistryEnabled() { + return this.packageRegistryAccessLevel > featureAccessLevel.NOT_ENABLED; + }, + packageRegistryApiForEveryoneEnabled() { + return this.packageRegistryAccessLevel === FEATURE_ACCESS_LEVEL_ANONYMOUS[0]; + }, + packageRegistryApiForEveryoneEnabledShown() { + return this.visibilityLevel !== VISIBILITY_LEVEL_PUBLIC_INTEGER; }, monitorOperationsFeatureAccessLevelOptions() { - if (this.splitOperationsEnabled) { - return this.featureAccessLevelOptions.filter(([value]) => value <= this.monitorAccessLevel); - } - return this.featureAccessLevelOptions.filter( - ([value]) => value <= this.operationsAccessLevel, - ); + return this.featureAccessLevelOptions.filter(([value]) => value <= this.monitorAccessLevel); }, }, @@ -429,10 +422,6 @@ export default { featureAccessLevel.PROJECT_MEMBERS, this.securityAndComplianceAccessLevel, ); - this.operationsAccessLevel = Math.min( - featureAccessLevel.PROJECT_MEMBERS, - this.operationsAccessLevel, - ); this.environmentsAccessLevel = Math.min( featureAccessLevel.PROJECT_MEMBERS, this.environmentsAccessLevel, @@ -474,9 +463,8 @@ export default { this.packageRegistryAccessLevelEnabled && this.packageRegistryAccessLevel === featureAccessLevel.PROJECT_MEMBERS ) { - this.packageRegistryAccessLevel = Math.min( - ...this.packageRegistryFeatureAccessLevelOptions.map((option) => option[0]), - ); + this.packageRegistryAccessLevel = + PACKAGE_REGISTRY_ACCESS_LEVEL_DEFAULT_BY_PROJECT_VISIBILITY[value]; } if (this.buildsAccessLevel > featureAccessLevel.NOT_ENABLED) this.buildsAccessLevel = featureAccessLevel.EVERYONE; @@ -492,8 +480,6 @@ export default { this.metricsDashboardAccessLevel = featureAccessLevel.EVERYONE; if (this.requirementsAccessLevel === featureAccessLevel.PROJECT_MEMBERS) this.requirementsAccessLevel = featureAccessLevel.EVERYONE; - if (this.operationsAccessLevel === featureAccessLevel.PROJECT_MEMBERS) - this.operationsAccessLevel = featureAccessLevel.EVERYONE; if (this.environmentsAccessLevel === featureAccessLevel.PROJECT_MEMBERS) this.environmentsAccessLevel = featureAccessLevel.EVERYONE; if (this.monitorAccessLevel === featureAccessLevel.PROJECT_MEMBERS) @@ -532,10 +518,6 @@ export default { toggleHiddenClassBySelector('.merge-requests-feature', false); }, - operationsAccessLevel(value, oldValue) { - this.updateSubFeatureAccessLevel(value, oldValue); - }, - monitorAccessLevel(value, oldValue) { this.updateSubFeatureAccessLevel(value, oldValue); }, @@ -561,6 +543,22 @@ export default { visibilityAllowed(option) { return this.allowedVisibilityOptions.includes(option); }, + onPackageRegistryEnabledToggle(value) { + this.packageRegistryAccessLevel = value + ? this.packageRegistryAccessLevelDefault() + : featureAccessLevel.NOT_ENABLED; + }, + onPackageRegistryApiForEveryoneEnabledToggle(value) { + this.packageRegistryAccessLevel = value + ? FEATURE_ACCESS_LEVEL_ANONYMOUS[0] + : this.packageRegistryAccessLevelDefault(); + }, + packageRegistryAccessLevelDefault() { + return ( + PACKAGE_REGISTRY_ACCESS_LEVEL_DEFAULT_BY_PROJECT_VISIBILITY[this.visibilityLevel] ?? + featureAccessLevel.NOT_ENABLED + ); + }, }, }; </script> @@ -897,10 +895,36 @@ export default { :help-text="$options.i18n.packageRegistryHelpText" data-testid="package-registry-access-level" > - <project-feature-setting - v-model="packageRegistryAccessLevel" + <gl-toggle + class="gl-my-2" + :value="packageRegistryEnabled" :label="$options.i18n.packageRegistryLabel" - :options="packageRegistryFeatureAccessLevelOptions" + label-position="hidden" + name="package_registry_enabled" + @change="onPackageRegistryEnabledToggle" + /> + <div + v-if="packageRegistryApiForEveryoneEnabledShown" + class="project-feature-setting-group gl-pl-7 gl-sm-pl-5 gl-my-3" + > + <project-setting-row + :label="$options.i18n.packageRegistryForEveryoneLabel" + :help-text="$options.i18n.packageRegistryForEveryoneHelpText" + > + <gl-toggle + class="gl-my-2" + :value="packageRegistryApiForEveryoneEnabled" + :disabled="!packageRegistryEnabled" + :label="$options.i18n.packageRegistryForEveryoneLabel" + label-position="hidden" + name="package_registry_api_for_everyone_enabled" + @change="onPackageRegistryApiForEveryoneEnabledToggle" + /> + </project-setting-row> + </div> + <input + :value="packageRegistryAccessLevel" + type="hidden" name="project[project_feature_attributes][package_registry_access_level]" /> </project-setting-row> @@ -923,11 +947,10 @@ export default { /> </project-setting-row> <project-setting-row - v-if="splitOperationsEnabled" ref="monitor-settings" :label="$options.i18n.monitorLabel" :help-text=" - s__('ProjectSettings|Configure your project resources and monitor their health.') + s__('ProjectSettings|Monitor the health of your project and respond to incidents.') " > <project-feature-setting @@ -937,21 +960,6 @@ export default { name="project[project_feature_attributes][monitor_access_level]" /> </project-setting-row> - <project-setting-row - v-else - ref="operations-settings" - :label="$options.i18n.operationsLabel" - :help-text=" - s__('ProjectSettings|Configure your project resources and monitor their health.') - " - > - <project-feature-setting - v-model="operationsAccessLevel" - :label="$options.i18n.operationsLabel" - :options="featureAccessLevelOptions" - name="project[project_feature_attributes][operations_access_level]" - /> - </project-setting-row> <div class="project-feature-setting-group gl-pl-7 gl-sm-pl-5"> <project-setting-row ref="metrics-visibility-settings" @@ -966,47 +974,45 @@ export default { /> </project-setting-row> </div> - <template v-if="splitOperationsEnabled"> - <project-setting-row - ref="environments-settings" + <project-setting-row + ref="environments-settings" + :label="$options.i18n.environmentsLabel" + :help-text="$options.i18n.environmentsHelpText" + :help-path="environmentsHelpPath" + > + <project-feature-setting + v-model="environmentsAccessLevel" :label="$options.i18n.environmentsLabel" - :help-text="$options.i18n.environmentsHelpText" - :help-path="environmentsHelpPath" - > - <project-feature-setting - v-model="environmentsAccessLevel" - :label="$options.i18n.environmentsLabel" - :options="featureAccessLevelOptions" - name="project[project_feature_attributes][environments_access_level]" - /> - </project-setting-row> - <project-setting-row - ref="feature-flags-settings" + :options="featureAccessLevelOptions" + name="project[project_feature_attributes][environments_access_level]" + /> + </project-setting-row> + <project-setting-row + ref="feature-flags-settings" + :label="$options.i18n.featureFlagsLabel" + :help-text="$options.i18n.featureFlagsHelpText" + :help-path="featureFlagsHelpPath" + > + <project-feature-setting + v-model="featureFlagsAccessLevel" :label="$options.i18n.featureFlagsLabel" - :help-text="$options.i18n.featureFlagsHelpText" - :help-path="featureFlagsHelpPath" - > - <project-feature-setting - v-model="featureFlagsAccessLevel" - :label="$options.i18n.featureFlagsLabel" - :options="featureAccessLevelOptions" - name="project[project_feature_attributes][feature_flags_access_level]" - /> - </project-setting-row> - <project-setting-row - ref="infrastructure-settings" + :options="featureAccessLevelOptions" + name="project[project_feature_attributes][feature_flags_access_level]" + /> + </project-setting-row> + <project-setting-row + ref="infrastructure-settings" + :label="$options.i18n.infrastructureLabel" + :help-text="$options.i18n.infrastructureHelpText" + :help-path="infrastructureHelpPath" + > + <project-feature-setting + v-model="infrastructureAccessLevel" :label="$options.i18n.infrastructureLabel" - :help-text="$options.i18n.infrastructureHelpText" - :help-path="infrastructureHelpPath" - > - <project-feature-setting - v-model="infrastructureAccessLevel" - :label="$options.i18n.infrastructureLabel" - :options="featureAccessLevelOptions" - name="project[project_feature_attributes][infrastructure_access_level]" - /> - </project-setting-row> - </template> + :options="featureAccessLevelOptions" + name="project[project_feature_attributes][infrastructure_access_level]" + /> + </project-setting-row> <project-setting-row ref="releases-settings" :label="$options.i18n.releasesLabel" diff --git a/app/assets/javascripts/pages/projects/shared/web_ide_link/index.js b/app/assets/javascripts/pages/projects/shared/web_ide_link/index.js index 5f08943d211..84ff802c268 100644 --- a/app/assets/javascripts/pages/projects/shared/web_ide_link/index.js +++ b/app/assets/javascripts/pages/projects/shared/web_ide_link/index.js @@ -1,7 +1,15 @@ import Vue from 'vue'; +import VueApollo from 'vue-apollo'; import { convertObjectPropsToCamelCase } from '~/lib/utils/common_utils'; import { joinPaths, webIDEUrl } from '~/lib/utils/url_utility'; import WebIdeButton from '~/vue_shared/components/web_ide_link.vue'; +import createDefaultClient from '~/lib/graphql'; + +Vue.use(VueApollo); + +const apolloProvider = new VueApollo({ + defaultClient: createDefaultClient(), +}); export default ({ el, router }) => { if (!el) return; @@ -9,15 +17,18 @@ export default ({ el, router }) => { const { projectPath, ref, isBlob, webIdeUrl, ...options } = convertObjectPropsToCamelCase( JSON.parse(el.dataset.options), ); + const { webIdePromoPopoverImg } = el.dataset; // eslint-disable-next-line no-new new Vue({ el, router, + apolloProvider, render(h) { return h(WebIdeButton, { props: { isBlob, + webIdePromoPopoverImg, webIdeUrl: isBlob ? webIdeUrl : webIDEUrl( diff --git a/app/assets/javascripts/pages/projects/tags/new/index.js b/app/assets/javascripts/pages/projects/tags/new/index.js index 9ef1017f9f2..eb1f705eab9 100644 --- a/app/assets/javascripts/pages/projects/tags/new/index.js +++ b/app/assets/javascripts/pages/projects/tags/new/index.js @@ -1,8 +1,8 @@ import $ from 'jquery'; import GLForm from '~/gl_form'; -import RefSelectDropdown from '~/ref_select_dropdown'; import ZenMode from '~/zen_mode'; +import initNewTagRefSelector from '~/tags/init_new_tag_ref_selector'; +initNewTagRefSelector(); new ZenMode(); // eslint-disable-line no-new new GLForm($('.tag-form')); // eslint-disable-line no-new -new RefSelectDropdown($('.js-branch-select')); // eslint-disable-line no-new |