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:
-rw-r--r--.rubocop_todo/layout/empty_line_after_magic_comment.yml1
-rw-r--r--app/assets/javascripts/batch_comments/components/preview_dropdown.vue22
-rw-r--r--app/assets/javascripts/layout_nav.js9
-rw-r--r--app/assets/javascripts/pages/groups/new/components/app.vue18
-rw-r--r--app/assets/javascripts/pages/groups/new/index.js2
-rw-r--r--app/assets/javascripts/projects/new/components/app.vue18
-rw-r--r--app/assets/javascripts/projects/new/index.js4
-rw-r--r--app/assets/javascripts/super_sidebar/components/help_center.vue23
-rw-r--r--app/assets/javascripts/super_sidebar/components/merge_request_menu.vue16
-rw-r--r--app/assets/javascripts/super_sidebar/components/super_sidebar.vue2
-rw-r--r--app/assets/javascripts/super_sidebar/components/user_bar.vue19
-rw-r--r--app/assets/javascripts/super_sidebar/super_sidebar_bundle.js7
-rw-r--r--app/assets/javascripts/super_sidebar/super_sidebar_collapsed_state_manager.js45
-rw-r--r--app/assets/javascripts/vue_shared/new_namespace/new_namespace_page.vue58
-rw-r--r--app/assets/stylesheets/framework/header.scss22
-rw-r--r--app/assets/stylesheets/framework/super_sidebar.scss58
-rw-r--r--app/assets/stylesheets/framework/system_messages.scss3
-rw-r--r--app/assets/stylesheets/framework/variables.scss2
-rw-r--r--app/assets/stylesheets/print.scss1
-rw-r--r--app/assets/stylesheets/startup/startup-dark.scss36
-rw-r--r--app/assets/stylesheets/startup/startup-general.scss36
-rw-r--r--app/controllers/groups_controller.rb1
-rw-r--r--app/controllers/projects/google_cloud/base_controller.rb4
-rw-r--r--app/controllers/projects_controller.rb2
-rw-r--r--app/helpers/application_helper.rb4
-rw-r--r--app/helpers/groups_helper.rb1
-rw-r--r--app/helpers/nav/new_dropdown_helper.rb29
-rw-r--r--app/helpers/nav_helper.rb4
-rw-r--r--app/views/groups/_invite_members_modal.html.haml2
-rw-r--r--app/views/groups/_invite_members_side_nav_link.html.haml1
-rw-r--r--app/views/groups/_invite_members_top_nav_link.html.haml6
-rw-r--r--app/views/groups/_new_group_fields.html.haml3
-rw-r--r--app/views/groups/group_members/index.html.haml1
-rw-r--r--app/views/groups/new.html.haml2
-rw-r--r--app/views/groups/show.html.haml1
-rw-r--r--app/views/layouts/_page.html.haml2
-rw-r--r--app/views/layouts/dashboard.html.haml2
-rw-r--r--app/views/layouts/header/_new_dropdown.html.haml20
-rw-r--r--app/views/layouts/nav/_breadcrumbs.html.haml45
-rw-r--r--app/views/layouts/nav/sidebar/_group.html.haml3
-rw-r--r--app/views/projects/_invite_members_empty_project.html.haml2
-rw-r--r--app/views/projects/_invite_members_modal.html.haml2
-rw-r--r--app/views/projects/_invite_members_side_nav_link.html.haml1
-rw-r--r--app/views/projects/_invite_members_top_nav_link.html.haml6
-rw-r--r--app/views/projects/_new_project_fields.html.haml2
-rw-r--r--app/views/projects/issues/show.html.haml1
-rw-r--r--app/views/projects/merge_requests/_page.html.haml1
-rw-r--r--app/views/projects/new.html.haml4
-rw-r--r--app/views/projects/project_members/index.html.haml1
-rw-r--r--app/views/projects/work_items/index.html.haml1
-rw-r--r--db/migrate/20230215180605_index_sbom_occurrences_on_project_id_and_id.rb15
-rw-r--r--db/schema_migrations/202302151806051
-rw-r--r--db/structure.sql2
-rw-r--r--doc/api/graphql/reference/index.md47
-rw-r--r--doc/api/group_badges.md2
-rw-r--r--doc/api/project_badges.md2
-rw-r--r--doc/api/projects.md1
-rw-r--r--doc/ci/yaml/index.md25
-rw-r--r--lib/gitlab/nav/top_nav_menu_item.rb7
-rw-r--r--lib/sidebars/groups/menus/scope_menu.rb2
-rw-r--r--locale/gitlab.pot9
-rw-r--r--package.json2
-rw-r--r--scripts/frontend/startup_css/constants.js3
-rw-r--r--spec/controllers/concerns/analytics/cycle_analytics/value_stream_actions_spec.rb3
-rw-r--r--spec/features/groups/new_group_page_spec.rb24
-rw-r--r--spec/features/projects/new_project_spec.rb21
-rw-r--r--spec/frontend/batch_comments/components/preview_dropdown_spec.js19
-rw-r--r--spec/frontend/fixtures/startup_css.rb15
-rw-r--r--spec/frontend/pages/groups/new/components/app_spec.js8
-rw-r--r--spec/frontend/vue_shared/new_namespace/new_namespace_page_spec.js8
-rw-r--r--spec/helpers/groups_helper_spec.rb6
-rw-r--r--spec/helpers/nav/new_dropdown_helper_spec.rb102
-rw-r--r--spec/lib/gitlab/nav/top_nav_menu_item_spec.rb2
-rw-r--r--spec/views/groups/group_members/index.html.haml_spec.rb1
-rw-r--r--spec/views/layouts/header/_new_dropdown.haml_spec.rb24
-rw-r--r--spec/views/projects/empty.html.haml_spec.rb2
-rw-r--r--spec/views/projects/project_members/index.html.haml_spec.rb1
-rw-r--r--yarn.lock8
78 files changed, 697 insertions, 221 deletions
diff --git a/.rubocop_todo/layout/empty_line_after_magic_comment.yml b/.rubocop_todo/layout/empty_line_after_magic_comment.yml
index 9c7b9f2c1a8..07353ea456c 100644
--- a/.rubocop_todo/layout/empty_line_after_magic_comment.yml
+++ b/.rubocop_todo/layout/empty_line_after_magic_comment.yml
@@ -555,7 +555,6 @@ Layout/EmptyLineAfterMagicComment:
- 'spec/components/previews/pajamas/card_component_preview.rb'
- 'spec/components/previews/pajamas/spinner_component_preview.rb'
- 'spec/controllers/application_controller_spec.rb'
- - 'spec/controllers/concerns/analytics/cycle_analytics/value_stream_actions_spec.rb'
- 'spec/controllers/projects/jobs_controller_spec.rb'
- 'spec/controllers/projects/merge_requests/drafts_controller_spec.rb'
- 'spec/factories/airflow/dags.rb'
diff --git a/app/assets/javascripts/batch_comments/components/preview_dropdown.vue b/app/assets/javascripts/batch_comments/components/preview_dropdown.vue
index 4ac0c8c4894..ca9cb03ca37 100644
--- a/app/assets/javascripts/batch_comments/components/preview_dropdown.vue
+++ b/app/assets/javascripts/batch_comments/components/preview_dropdown.vue
@@ -55,21 +55,25 @@ export default {
<template>
<gl-disclosure-dropdown :items="listItems" dropup data-qa-selector="review_preview_dropdown">
<template #toggle>
- <gl-button
- >{{ __('Pending comments') }} <drafts-count variant="neutral" /><gl-icon
- class="dropdown-chevron"
- name="chevron-up"
- /></gl-button>
+ <gl-button>
+ {{ __('Pending comments') }}
+ <drafts-count variant="neutral" />
+ <gl-icon class="dropdown-chevron" name="chevron-up" />
+ </gl-button>
</template>
<template #header>
- <p class="gl-dropdown-header-top">
- {{ n__('%d pending comment', '%d pending comments', draftsCount) }}
- </p>
+ <div
+ class="gl-display-flex gl-align-items-center gl-p-4! gl-min-h-8 gl-border-b-1 gl-border-b-solid gl-border-b-gray-200"
+ >
+ <span class="gl-flex-grow-1 gl-font-weight-bold gl-font-sm gl-pr-2">
+ {{ n__('%d pending comment', '%d pending comments', draftsCount) }}
+ </span>
+ </div>
</template>
<template #list-item="{ item }">
- <preview-item :draft="item" :is-last="item.last" @click="onClickDraft(item)" />
+ <preview-item :draft="item" :is-last="item.last" />
</template>
</gl-disclosure-dropdown>
</template>
diff --git a/app/assets/javascripts/layout_nav.js b/app/assets/javascripts/layout_nav.js
index b8138f34d45..f5078962b8f 100644
--- a/app/assets/javascripts/layout_nav.js
+++ b/app/assets/javascripts/layout_nav.js
@@ -70,10 +70,11 @@ function initDeferred() {
}
export default function initLayoutNav() {
- const contextualSidebar = new ContextualSidebar();
- contextualSidebar.bindEvents();
-
- initFlyOutNav();
+ if (!gon.use_new_navigation) {
+ const contextualSidebar = new ContextualSidebar();
+ contextualSidebar.bindEvents();
+ initFlyOutNav();
+ }
requestIdleCallback(initDeferred);
}
diff --git a/app/assets/javascripts/pages/groups/new/components/app.vue b/app/assets/javascripts/pages/groups/new/components/app.vue
index f01e5e595a3..68ddcf3d868 100644
--- a/app/assets/javascripts/pages/groups/new/components/app.vue
+++ b/app/assets/javascripts/pages/groups/new/components/app.vue
@@ -2,7 +2,7 @@
import importGroupIllustration from '@gitlab/svgs/dist/illustrations/group-import.svg';
import newGroupIllustration from '@gitlab/svgs/dist/illustrations/group-new.svg';
-import { __, s__ } from '~/locale';
+import { s__ } from '~/locale';
import NewNamespacePage from '~/vue_shared/new_namespace/new_namespace_page.vue';
import createGroupDescriptionDetails from './create_group_description_details.vue';
@@ -11,6 +11,11 @@ export default {
NewNamespacePage,
},
props: {
+ parentGroupUrl: {
+ type: String,
+ required: false,
+ default: null,
+ },
parentGroupName: {
type: String,
required: false,
@@ -28,8 +33,13 @@ export default {
},
},
computed: {
- initialBreadcrumb() {
- return this.parentGroupName || __('New group');
+ initialBreadcrumbs() {
+ return this.parentGroupUrl
+ ? [
+ { text: this.parentGroupName, href: this.parentGroupUrl },
+ { text: s__('GroupsNew|New subgroup'), href: '#' },
+ ]
+ : [{ text: s__('GroupsNew|New group'), href: '#' }];
},
panels() {
return [
@@ -68,7 +78,7 @@ export default {
<template>
<new-namespace-page
:jump-to-last-persisted-panel="hasErrors"
- :initial-breadcrumb="initialBreadcrumb"
+ :initial-breadcrumbs="initialBreadcrumbs"
:panels="panels"
:title="s__('GroupsNew|Create new group')"
persistence-key="new_group_last_active_tab"
diff --git a/app/assets/javascripts/pages/groups/new/index.js b/app/assets/javascripts/pages/groups/new/index.js
index a555038ed5c..acaee097dc1 100644
--- a/app/assets/javascripts/pages/groups/new/index.js
+++ b/app/assets/javascripts/pages/groups/new/index.js
@@ -22,6 +22,7 @@ initFilePickers();
function initNewGroupCreation(el) {
const {
hasErrors,
+ parentGroupUrl,
parentGroupName,
importExistingGroupPath,
verificationRequired,
@@ -30,6 +31,7 @@ function initNewGroupCreation(el) {
} = el.dataset;
const props = {
+ parentGroupUrl,
parentGroupName,
importExistingGroupPath,
hasErrors: parseBoolean(hasErrors),
diff --git a/app/assets/javascripts/projects/new/components/app.vue b/app/assets/javascripts/projects/new/components/app.vue
index 3100029eb31..4f3924b04bf 100644
--- a/app/assets/javascripts/projects/new/components/app.vue
+++ b/app/assets/javascripts/projects/new/components/app.vue
@@ -59,6 +59,16 @@ export default {
SafeHtml,
},
props: {
+ parentGroupUrl: {
+ type: String,
+ required: false,
+ default: null,
+ },
+ parentGroupName: {
+ type: String,
+ required: false,
+ default: '',
+ },
hasErrors: {
type: Boolean,
required: false,
@@ -77,6 +87,12 @@ export default {
},
computed: {
+ initialBreadcrumbs() {
+ return [
+ this.parentGroupUrl && { text: this.parentGroupName, href: this.parentGroupUrl },
+ { text: s__('ProjectsNew|New project'), href: '#' },
+ ].filter(Boolean);
+ },
availablePanels() {
return this.isCiCdAvailable ? PANELS : PANELS.filter((p) => p.name !== CI_CD_PANEL);
},
@@ -95,7 +111,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..0b190b6a88b 100644
--- a/app/assets/javascripts/projects/new/index.js
+++ b/app/assets/javascripts/projects/new/index.js
@@ -15,12 +15,16 @@ export function initNewProjectCreation() {
newProjectGuidelines,
hasErrors,
isCiCdAvailable,
+ parentGroupUrl,
+ parentGroupName,
} = el.dataset;
const props = {
hasErrors: parseBoolean(hasErrors),
isCiCdAvailable: parseBoolean(isCiCdAvailable),
newProjectGuidelines,
+ parentGroupUrl,
+ parentGroupName,
};
const provide = {
diff --git a/app/assets/javascripts/super_sidebar/components/help_center.vue b/app/assets/javascripts/super_sidebar/components/help_center.vue
index 8e7c7efa631..ee4c50e7705 100644
--- a/app/assets/javascripts/super_sidebar/components/help_center.vue
+++ b/app/assets/javascripts/super_sidebar/components/help_center.vue
@@ -96,12 +96,6 @@ export default {
return true;
},
- handleAction({ action }) {
- if (action) {
- action();
- }
- },
-
showKeyboardShortcuts() {
this.$refs.dropdown.close();
window?.toggleShortcutsHelp();
@@ -140,11 +134,7 @@ export default {
:group="itemGroups.versionCheck"
>
<template #list-item="{ item }">
- <a
- :href="item.href"
- tabindex="-1"
- class="gl-display-flex gl-flex-direction-column gl-line-height-24 gl-text-gray-900 gl-hover-text-gray-900 gl-hover-text-decoration-none"
- >
+ <span class="gl-display-flex gl-flex-direction-column gl-line-height-24">
<span class="gl-font-sm gl-font-weight-bold">
{{ item.text }}
<gl-emoji data-name="rocket" />
@@ -153,7 +143,7 @@ export default {
<span class="gl-mr-2">{{ item.version }}</span>
<gitlab-version-check-badge v-if="updateSeverity" :status="updateSeverity" size="sm" />
</span>
- </a>
+ </span>
</template>
</gl-disclosure-dropdown-group>
@@ -162,16 +152,15 @@ export default {
:bordered="sidebarData.show_version_check"
/>
- <gl-disclosure-dropdown-group :group="itemGroups.helpActions" bordered @action="handleAction">
+ <gl-disclosure-dropdown-group :group="itemGroups.helpActions" bordered>
<template #list-item="{ item }">
- <button
- tabindex="-1"
- class="gl-bg-transparent gl-w-full gl-border-none gl-display-flex gl-justify-content-space-between gl-p-0 gl-text-gray-900"
+ <span
+ class="gl-display-flex gl-justify-content-space-between gl-align-items-center gl-my-n1"
>
{{ item.text }}
<gl-badge v-if="item.count" pill size="sm" variant="info">{{ item.count }}</gl-badge>
<kbd v-else-if="item.shortcut" class="flat">?</kbd>
- </button>
+ </span>
</template>
</gl-disclosure-dropdown-group>
</gl-disclosure-dropdown>
diff --git a/app/assets/javascripts/super_sidebar/components/merge_request_menu.vue b/app/assets/javascripts/super_sidebar/components/merge_request_menu.vue
index edc13e305cf..94fc6aedcc0 100644
--- a/app/assets/javascripts/super_sidebar/components/merge_request_menu.vue
+++ b/app/assets/javascripts/super_sidebar/components/merge_request_menu.vue
@@ -12,29 +12,19 @@ export default {
required: true,
},
},
- methods: {
- navigate() {
- this.$refs.link.click();
- },
- },
};
</script>
<template>
- <gl-disclosure-dropdown :items="items" placement="center" @action="navigate">
+ <gl-disclosure-dropdown :items="items" placement="center">
<template #toggle>
<slot></slot>
</template>
<template #list-item="{ item }">
- <a
- ref="link"
- class="gl-display-flex gl-align-items-center gl-justify-content-space-between gl-hover-text-gray-900 gl-hover-text-decoration-none gl-text-gray-900"
- :href="item.href"
- tabindex="-1"
- >
+ <span class="gl-display-flex gl-align-items-center gl-justify-content-space-between">
{{ item.text }}
<gl-badge pill size="sm" variant="neutral">{{ item.count || 0 }}</gl-badge>
- </a>
+ </span>
</template>
</gl-disclosure-dropdown>
</template>
diff --git a/app/assets/javascripts/super_sidebar/components/super_sidebar.vue b/app/assets/javascripts/super_sidebar/components/super_sidebar.vue
index cff68258a35..309299855d7 100644
--- a/app/assets/javascripts/super_sidebar/components/super_sidebar.vue
+++ b/app/assets/javascripts/super_sidebar/components/super_sidebar.vue
@@ -37,7 +37,7 @@ export default {
<template>
<aside
id="super-sidebar"
- class="super-sidebar gl-fixed gl-bottom-0 gl-left-0 gl-display-flex gl-flex-direction-column gl-bg-gray-10 gl-border-r gl-border-gray-a-08"
+ class="super-sidebar gl-display-flex gl-flex-direction-column"
data-testid="super-sidebar"
>
<user-bar :sidebar-data="sidebarData" />
diff --git a/app/assets/javascripts/super_sidebar/components/user_bar.vue b/app/assets/javascripts/super_sidebar/components/user_bar.vue
index ee72e8eafb4..4a7ea5bac80 100644
--- a/app/assets/javascripts/super_sidebar/components/user_bar.vue
+++ b/app/assets/javascripts/super_sidebar/components/user_bar.vue
@@ -1,9 +1,10 @@
<script>
-import { GlAvatar, GlDropdown, GlIcon, GlTooltipDirective } from '@gitlab/ui';
+import { GlAvatar, GlButton, GlDropdown, GlIcon, GlTooltipDirective } from '@gitlab/ui';
import { __ } from '~/locale';
import SafeHtml from '~/vue_shared/directives/safe_html';
import NewNavToggle from '~/nav/components/new_nav_toggle.vue';
import logo from '../../../../views/shared/_logo.svg';
+import { toggleSuperSidebarCollapsed } from '../super_sidebar_collapsed_state_manager';
import CreateMenu from './create_menu.vue';
import Counter from './counter.vue';
import MergeRequestMenu from './merge_request_menu.vue';
@@ -12,6 +13,7 @@ export default {
logo,
components: {
GlAvatar,
+ GlButton,
GlDropdown,
GlIcon,
CreateMenu,
@@ -20,6 +22,7 @@ export default {
MergeRequestMenu,
},
i18n: {
+ collapseSidebar: __('Collapse sidebar'),
createNew: __('Create new...'),
issues: __('Issues'),
mergeRequests: __('Merge requests'),
@@ -36,15 +39,27 @@ export default {
required: true,
},
},
+ methods: {
+ collapseSidebar() {
+ toggleSuperSidebarCollapsed(true, true);
+ },
+ },
};
</script>
<template>
<div class="user-bar">
- <div class="gl-display-flex gl-align-items-center gl-px-3 gl-py-2 gl-gap-3">
+ <div class="gl-display-flex gl-align-items-center gl-px-3 gl-py-2 gl-gap-2">
<div class="gl-flex-grow-1">
<a v-safe-html="$options.logo" :href="rootPath"></a>
</div>
+ <gl-button
+ v-gl-tooltip:super-sidebar.hover.bottom="$options.i18n.collapseSidebar"
+ :aria-label="$options.i18n.collapseSidebar"
+ icon="sidebar"
+ category="tertiary"
+ @click="collapseSidebar"
+ />
<create-menu :groups="sidebarData.create_new_menu_groups" />
<button class="gl-border-none">
<gl-icon name="search" class="gl-vertical-align-middle" />
diff --git a/app/assets/javascripts/super_sidebar/super_sidebar_bundle.js b/app/assets/javascripts/super_sidebar/super_sidebar_bundle.js
index b9c7073df8c..307c920e638 100644
--- a/app/assets/javascripts/super_sidebar/super_sidebar_bundle.js
+++ b/app/assets/javascripts/super_sidebar/super_sidebar_bundle.js
@@ -1,9 +1,16 @@
import Vue from 'vue';
+import {
+ bindSuperSidebarCollapsedEvents,
+ initSuperSidebarCollapsedState,
+} from './super_sidebar_collapsed_state_manager';
import SuperSidebar from './components/super_sidebar.vue';
export const initSuperSidebar = () => {
const el = document.querySelector('.js-super-sidebar');
+ bindSuperSidebarCollapsedEvents();
+ initSuperSidebarCollapsedState();
+
if (!el) return false;
const { rootPath, sidebar, toggleNewNavEndpoint } = el.dataset;
diff --git a/app/assets/javascripts/super_sidebar/super_sidebar_collapsed_state_manager.js b/app/assets/javascripts/super_sidebar/super_sidebar_collapsed_state_manager.js
new file mode 100644
index 00000000000..1dfd132d522
--- /dev/null
+++ b/app/assets/javascripts/super_sidebar/super_sidebar_collapsed_state_manager.js
@@ -0,0 +1,45 @@
+import { GlBreakpointInstance as bp, breakpoints } from '@gitlab/ui/dist/utils';
+import { debounce } from 'lodash';
+import { setCookie, getCookie } from '~/lib/utils/common_utils';
+
+export const SIDEBAR_COLLAPSED_CLASS = 'page-with-super-sidebar-collapsed';
+export const SIDEBAR_COLLAPSED_COOKIE = 'super_sidebar_collapsed';
+export const SIDEBAR_COLLAPSED_COOKIE_EXPIRATION = 365 * 10;
+
+export const findPage = () => document.querySelector('.page-with-super-sidebar');
+export const findToggle = () => document.querySelector('.js-super-sidebar-toggle');
+
+const isCollapsed = () => findPage().classList.contains(SIDEBAR_COLLAPSED_CLASS);
+
+// See documentation: https://design.gitlab.com/patterns/navigation#left-sidebar
+// NOTE: at 1200px nav sidebar should not overlap the content
+// https://gitlab.com/gitlab-org/gitlab-foss/merge_requests/24555#note_134136110
+export const isDesktopBreakpoint = () => bp.windowWidth() >= breakpoints.xl;
+
+export const getCollapsedCookie = () => getCookie(SIDEBAR_COLLAPSED_COOKIE) === 'true';
+
+export const toggleSuperSidebarCollapsed = (collapsed, saveCookie) => {
+ findPage().classList.toggle(SIDEBAR_COLLAPSED_CLASS, collapsed);
+
+ if (saveCookie && isDesktopBreakpoint()) {
+ setCookie(SIDEBAR_COLLAPSED_COOKIE, collapsed, {
+ expires: SIDEBAR_COLLAPSED_COOKIE_EXPIRATION,
+ });
+ }
+};
+
+export const initSuperSidebarCollapsedState = () => {
+ const collapsed = isDesktopBreakpoint() ? getCollapsedCookie() : true;
+ toggleSuperSidebarCollapsed(collapsed, false);
+};
+
+export const bindSuperSidebarCollapsedEvents = () => {
+ if (findToggle()) {
+ findToggle().addEventListener('click', () => {
+ const value = !isCollapsed();
+ toggleSuperSidebarCollapsed(value, true);
+ });
+ }
+
+ window.addEventListener('resize', debounce(initSuperSidebarCollapsedState, 100));
+};
diff --git a/app/assets/javascripts/vue_shared/new_namespace/new_namespace_page.vue b/app/assets/javascripts/vue_shared/new_namespace/new_namespace_page.vue
index 318adec2319..2533b3b5489 100644
--- a/app/assets/javascripts/vue_shared/new_namespace/new_namespace_page.vue
+++ b/app/assets/javascripts/vue_shared/new_namespace/new_namespace_page.vue
@@ -29,8 +29,8 @@ export default {
type: String,
required: true,
},
- initialBreadcrumb: {
- type: String,
+ initialBreadcrumbs: {
+ type: Array,
required: true,
},
panels: {
@@ -60,6 +60,10 @@ export default {
return this.panels.find((p) => p.name === this.activePanelName);
},
+ detailProps() {
+ return this.activePanel.detailProps || {};
+ },
+
details() {
return this.activePanel.details || this.activePanel.description;
},
@@ -69,14 +73,15 @@ export default {
},
breadcrumbs() {
- if (!this.activePanel) {
- return null;
- }
-
- return [
- { text: this.initialBreadcrumb, href: '#' },
- { text: this.activePanel.title, href: `#${this.activePanel.name}` },
- ];
+ return this.activePanel
+ ? [
+ ...this.initialBreadcrumbs,
+ {
+ text: this.activePanel.title,
+ href: `#${this.activePanel.name}`,
+ },
+ ]
+ : this.initialBreadcrumbs;
},
shouldVerify() {
@@ -125,24 +130,29 @@ export default {
<template>
<credit-card-verification v-if="shouldVerify" @verified="onVerified" />
- <welcome-page v-else-if="!activePanelName" :panels="panels" :title="title">
- <template #footer>
- <slot name="welcome-footer"> </slot>
- </template>
- </welcome-page>
- <div v-else class="row">
- <div class="col-lg-3">
- <div v-safe-html="activePanel.illustration" class="gl-text-white"></div>
- <h4>{{ activePanel.title }}</h4>
-
- <p v-if="hasTextDetails">{{ details }}</p>
- <component :is="details" v-else v-bind="activePanel.detailProps || {}" />
+ <div v-else-if="!activePanelName">
+ <gl-breadcrumb :items="breadcrumbs" />
+ <welcome-page :panels="panels" :title="title">
+ <template #footer>
+ <slot name="welcome-footer"> </slot>
+ </template>
+ </welcome-page>
+ </div>
+ <div v-else>
+ <gl-breadcrumb :items="breadcrumbs" />
+ <div class="gl-display-flex gl-py-5 gl-align-items-center">
+ <div v-safe-html="activePanel.illustration" class="gl-text-white col-auto"></div>
+ <div class="col">
+ <h4>{{ activePanel.title }}</h4>
+
+ <p v-if="hasTextDetails">{{ details }}</p>
+ <component :is="details" v-else v-bind="detailProps" />
+ </div>
<slot name="extra-description"></slot>
</div>
- <div class="col-lg-9">
+ <div>
<new-top-level-group-alert v-if="showNewTopLevelGroupAlert" />
- <gl-breadcrumb v-if="breadcrumbs" :items="breadcrumbs" />
<legacy-container :key="activePanel.name" :selector="activePanel.selector" />
</div>
</div>
diff --git a/app/assets/stylesheets/framework/header.scss b/app/assets/stylesheets/framework/header.scss
index 7baf84198e4..d56337fcd60 100644
--- a/app/assets/stylesheets/framework/header.scss
+++ b/app/assets/stylesheets/framework/header.scss
@@ -321,7 +321,6 @@ $search-input-field-x-min-width: 200px;
.breadcrumbs-container {
display: flex;
width: 100%;
- position: relative;
padding-top: $gl-padding / 2;
padding-bottom: $gl-padding / 2;
align-items: center;
@@ -558,25 +557,16 @@ $search-input-field-x-min-width: 200px;
}
.toggle-mobile-nav {
- display: none;
- background-color: transparent;
- border: 0;
- padding: 6px 16px;
- margin: 0 0 0 -15px;
- height: 46px;
- color: $gl-text-color;
+ @include gl-display-none;
@include media-breakpoint-down(sm) {
- display: flex;
- align-items: center;
-
- i {
- font-size: 18px;
- }
+ @include gl-display-block;
+ .breadcrumbs-links {
- padding-left: $gl-padding;
- border-left: 1px solid $gl-text-color-quaternary;
+ @include gl-pl-4;
+ @include gl-border-l-1;
+ @include gl-border-l-solid;
+ @include gl-border-gray-100;
}
}
}
diff --git a/app/assets/stylesheets/framework/super_sidebar.scss b/app/assets/stylesheets/framework/super_sidebar.scss
index 4b55b39d6f3..6758c8b4014 100644
--- a/app/assets/stylesheets/framework/super_sidebar.scss
+++ b/app/assets/stylesheets/framework/super_sidebar.scss
@@ -1,8 +1,27 @@
.super-sidebar {
- top: 0;
- width: $contextual-sidebar-width;
+ @include gl-fixed;
+ @include gl-top-0;
+ @include gl-bottom-0;
+ @include gl-left-0;
+ @include gl-bg-gray-10;
+ @include gl-border-r;
+ @include gl-border-gray-a-08;
+ transform: translate3d(0, 0, 0);
+ width: $super-sidebar-width;
z-index: 600;
+ &.super-sidebar-loading {
+ transform: translate3d(-100%, 0, 0);
+
+ @include media-breakpoint-up(xl) {
+ transform: translate3d(0, 0, 0);
+ }
+ }
+
+ &:not(.super-sidebar-loading) {
+ transition: transform $gl-transition-duration-medium;
+ }
+
.user-bar {
background-color: $t-gray-a-04;
@@ -60,6 +79,41 @@
}
}
+.page-with-super-sidebar {
+ padding-left: 0;
+
+ @include media-breakpoint-up(xl) {
+ padding-left: $contextual-sidebar-width;
+
+ .super-sidebar-toggle {
+ display: none;
+ }
+ }
+}
+
+.page-with-super-sidebar-collapsed {
+ .super-sidebar {
+ transform: translate3d(-100%, 0, 0);
+ }
+
+ @include media-breakpoint-up(xl) {
+ padding-left: 0;
+
+ .super-sidebar-toggle {
+ display: block;
+ }
+ }
+}
+
+.container-limited .super-sidebar-toggle {
+ @media (min-width: $super-sidebar-toggle-position-breakpoint) {
+ position: absolute;
+ left: $gl-spacing-scale-3;
+ top: $gl-spacing-scale-3;
+ margin: 0;
+ }
+}
+
.with-performance-bar .super-sidebar {
top: $performance-bar-height;
}
diff --git a/app/assets/stylesheets/framework/system_messages.scss b/app/assets/stylesheets/framework/system_messages.scss
index 89585fd96ae..590a66ff28e 100644
--- a/app/assets/stylesheets/framework/system_messages.scss
+++ b/app/assets/stylesheets/framework/system_messages.scss
@@ -48,6 +48,7 @@
// left sidebar eg: project page
// right sidebar eg: MR page
.nav-sidebar,
+ .super-sidebar,
.right-sidebar {
top: calc(#{$system-header-height} + #{$header-height});
}
@@ -72,6 +73,7 @@
// left sidebar eg: project page
// right sidebar eg: MR page
.nav-sidebar,
+ .super-sidebar,
.right-sidebar {
top: calc(#{$header-height} + #{$performance-bar-height} + #{$system-header-height});
}
@@ -83,6 +85,7 @@
// left sidebar eg: project page
// right sidebar eg: mr page
.nav-sidebar,
+ .super-sidebar,
.right-sidebar,
// navless pages' footer eg: login page
// navless pages' footer border eg: login page
diff --git a/app/assets/stylesheets/framework/variables.scss b/app/assets/stylesheets/framework/variables.scss
index c616915073e..ac27f27ae2a 100644
--- a/app/assets/stylesheets/framework/variables.scss
+++ b/app/assets/stylesheets/framework/variables.scss
@@ -10,6 +10,8 @@ $default-transition-duration: 0.15s;
$contextual-sidebar-width: 256px;
$contextual-sidebar-collapsed-width: 56px;
$toggle-sidebar-height: 48px;
+$super-sidebar-width: 256px;
+$super-sidebar-toggle-position-breakpoint: 1360px;
/**
🚨 Do not use this spacing scale — it is deprecated and being removed. 🚨
diff --git a/app/assets/stylesheets/print.scss b/app/assets/stylesheets/print.scss
index ab86a2f69dd..265f27f21fa 100644
--- a/app/assets/stylesheets/print.scss
+++ b/app/assets/stylesheets/print.scss
@@ -25,6 +25,7 @@ nav,
nav.navbar-collapse,
nav.navbar-collapse.collapse,
.nav-sidebar,
+.super-sidebar,
.profiler-results,
.tree-ref-holder,
.tree-holder .breadcrumb,
diff --git a/app/assets/stylesheets/startup/startup-dark.scss b/app/assets/stylesheets/startup/startup-dark.scss
index 3b28025053b..accc50697c5 100644
--- a/app/assets/stylesheets/startup/startup-dark.scss
+++ b/app/assets/stylesheets/startup/startup-dark.scss
@@ -1493,6 +1493,42 @@ kbd {
display: none;
}
}
+.super-sidebar {
+ position: fixed;
+ top: 0;
+ bottom: 0;
+ left: 0;
+ background-color: #1f1e24;
+ border-right: solid 1px #434248;
+ border-color: rgba(251, 250, 253, 0.08);
+ transform: translate3d(0, 0, 0);
+ width: 256px;
+ z-index: 600;
+}
+.super-sidebar.super-sidebar-loading {
+ transform: translate3d(-100%, 0, 0);
+}
+@media (min-width: 1200px) {
+ .super-sidebar.super-sidebar-loading {
+ transform: translate3d(0, 0, 0);
+ }
+}
+.page-with-super-sidebar {
+ padding-left: 0;
+}
+@media (min-width: 1200px) {
+ .page-with-super-sidebar {
+ padding-left: 256px;
+ }
+}
+.page-with-super-sidebar-collapsed .super-sidebar {
+ transform: translate3d(-100%, 0, 0);
+}
+@media (min-width: 1200px) {
+ .page-with-super-sidebar-collapsed {
+ padding-left: 0;
+ }
+}
input::-moz-placeholder {
color: #737278;
opacity: 1;
diff --git a/app/assets/stylesheets/startup/startup-general.scss b/app/assets/stylesheets/startup/startup-general.scss
index adafe719892..511c270d556 100644
--- a/app/assets/stylesheets/startup/startup-general.scss
+++ b/app/assets/stylesheets/startup/startup-general.scss
@@ -1493,6 +1493,42 @@ kbd {
display: none;
}
}
+.super-sidebar {
+ position: fixed;
+ top: 0;
+ bottom: 0;
+ left: 0;
+ background-color: #fbfafd;
+ border-right: solid 1px #dcdcde;
+ border-color: rgba(31, 30, 36, 0.08);
+ transform: translate3d(0, 0, 0);
+ width: 256px;
+ z-index: 600;
+}
+.super-sidebar.super-sidebar-loading {
+ transform: translate3d(-100%, 0, 0);
+}
+@media (min-width: 1200px) {
+ .super-sidebar.super-sidebar-loading {
+ transform: translate3d(0, 0, 0);
+ }
+}
+.page-with-super-sidebar {
+ padding-left: 0;
+}
+@media (min-width: 1200px) {
+ .page-with-super-sidebar {
+ padding-left: 256px;
+ }
+}
+.page-with-super-sidebar-collapsed .super-sidebar {
+ transform: translate3d(-100%, 0, 0);
+}
+@media (min-width: 1200px) {
+ .page-with-super-sidebar-collapsed {
+ padding-left: 0;
+ }
+}
input::-moz-placeholder {
color: #89888d;
opacity: 1;
diff --git a/app/controllers/groups_controller.rb b/app/controllers/groups_controller.rb
index 76065bc3ab8..a0c82998108 100644
--- a/app/controllers/groups_controller.rb
+++ b/app/controllers/groups_controller.rb
@@ -75,6 +75,7 @@ class GroupsController < Groups::ApplicationController
end
def new
+ @parent_group = Group.find_by_id(params[:parent_id])
@group = Group.new(params.permit(:parent_id))
@group.build_namespace_settings
end
diff --git a/app/controllers/projects/google_cloud/base_controller.rb b/app/controllers/projects/google_cloud/base_controller.rb
index dfb73821b0f..7eccc0c1c77 100644
--- a/app/controllers/projects/google_cloud/base_controller.rb
+++ b/app/controllers/projects/google_cloud/base_controller.rb
@@ -45,8 +45,8 @@ class Projects::GoogleCloud::BaseController < Projects::ApplicationController
return_url = project_google_cloud_configuration_path(project)
state = generate_session_key_redirect(request.url, return_url)
@authorize_url = GoogleApi::CloudPlatform::Client.new(nil,
- callback_google_api_auth_url,
- state: state).authorize_url
+ callback_google_api_auth_url,
+ state: state).authorize_url
redirect_to @authorize_url
end
diff --git a/app/controllers/projects_controller.rb b/app/controllers/projects_controller.rb
index 71ad747b6b1..dec170cf582 100644
--- a/app/controllers/projects_controller.rb
+++ b/app/controllers/projects_controller.rb
@@ -78,6 +78,8 @@ class ProjectsController < Projects::ApplicationController
@namespace = Namespace.find_by(id: params[:namespace_id]) if params[:namespace_id]
return access_denied! if @namespace && !can?(current_user, :create_projects, @namespace)
+ @parent_group = Group.find_by(id: params[:namespace_id])
+
@current_user_group =
if current_user.manageable_groups(include_groups_with_developer_maintainer_access: true).count == 1
current_user.manageable_groups(include_groups_with_developer_maintainer_access: true).first
diff --git a/app/helpers/application_helper.rb b/app/helpers/application_helper.rb
index 93b7c8c0b94..23a21bce456 100644
--- a/app/helpers/application_helper.rb
+++ b/app/helpers/application_helper.rb
@@ -374,6 +374,10 @@ module ApplicationHelper
cookies["sidebar_collapsed"] == "true"
end
+ def collapsed_super_sidebar?
+ cookies["super_sidebar_collapsed"] == "true"
+ end
+
def locale_path
asset_path("locale/#{Gitlab::I18n.locale}/app.js")
end
diff --git a/app/helpers/groups_helper.rb b/app/helpers/groups_helper.rb
index 129871ca3fd..ce64ac1f21f 100644
--- a/app/helpers/groups_helper.rb
+++ b/app/helpers/groups_helper.rb
@@ -126,6 +126,7 @@ module GroupsHelper
def subgroup_creation_data(group)
{
+ parent_group_url: group.parent && group_url(group.parent),
parent_group_name: group.parent&.name,
import_existing_group_path: new_group_path(parent_id: group.parent_id, anchor: 'import-group-pane')
}
diff --git a/app/helpers/nav/new_dropdown_helper.rb b/app/helpers/nav/new_dropdown_helper.rb
index ddd6469a9e4..98ff48bf19b 100644
--- a/app/helpers/nav/new_dropdown_helper.rb
+++ b/app/helpers/nav/new_dropdown_helper.rb
@@ -2,23 +2,25 @@
module Nav
module NewDropdownHelper
- def new_dropdown_view_model(group:, project:)
+ def new_dropdown_view_model(group:, project:, with_context: false)
return unless current_user
menu_sections = []
+ data = { title: _('Create new...') }
- if group&.persisted?
- menu_sections.push(group_menu_section(group))
- elsif project&.persisted?
+ if project&.persisted?
menu_sections.push(project_menu_section(project))
+ data[:context] = project if with_context
+ elsif group&.persisted?
+ menu_sections.push(group_menu_section(group))
+ data[:context] = group if with_context
end
menu_sections.push(general_menu_section)
- {
- title: _("Create new..."),
- menu_sections: menu_sections.select { |x| x.fetch(:menu_items).any? }
- }
+ data[:menu_sections] = menu_sections.select { |x| x.fetch(:menu_items).any? }
+
+ data
end
private
@@ -53,7 +55,8 @@ module Nav
if can?(current_user, :admin_group_member, group)
menu_items.push(
invite_members_menu_item(
- href: group_group_members_path(group)
+ href: group_group_members_path(group),
+ partial: 'groups/invite_members_top_nav_link'
)
)
end
@@ -104,7 +107,8 @@ module Nav
if can_admin_project_member?(project)
menu_items.push(
invite_members_menu_item(
- href: project_project_members_path(project)
+ href: project_project_members_path(project),
+ partial: 'projects/invite_members_top_nav_link'
)
)
end
@@ -157,11 +161,12 @@ module Nav
}
end
- def invite_members_menu_item(href:)
+ def invite_members_menu_item(href:, partial:)
::Gitlab::Nav::TopNavMenuItem.build(
id: 'invite',
title: s_('InviteMember|Invite members'),
- emoji: 'shaking_hands',
+ icon: 'shaking_hands',
+ partial: partial,
href: href,
data: {
track_action: 'click_link_invite_members',
diff --git a/app/helpers/nav_helper.rb b/app/helpers/nav_helper.rb
index 0b0921cea8b..0f328410abc 100644
--- a/app/helpers/nav_helper.rb
+++ b/app/helpers/nav_helper.rb
@@ -12,7 +12,9 @@ module NavHelper
def page_with_sidebar_class
class_name = page_gutter_class
class_name << 'page-with-contextual-sidebar' if defined?(@left_sidebar) && @left_sidebar
- class_name << 'page-with-icon-sidebar' if collapsed_sidebar? && @left_sidebar
+ class_name << 'page-with-super-sidebar' if show_super_sidebar? && @left_sidebar
+ class_name << 'page-with-super-sidebar-collapsed' if show_super_sidebar? && collapsed_super_sidebar? && @left_sidebar
+ class_name << 'page-with-icon-sidebar' if collapsed_sidebar? && @left_sidebar && !show_super_sidebar?
class_name -= ['right-sidebar-expanded'] if defined?(@right_sidebar) && !@right_sidebar
class_name
diff --git a/app/views/groups/_invite_members_modal.html.haml b/app/views/groups/_invite_members_modal.html.haml
index f0fd9026b30..cd3327ba9ec 100644
--- a/app/views/groups/_invite_members_modal.html.haml
+++ b/app/views/groups/_invite_members_modal.html.haml
@@ -2,5 +2,5 @@
.js-invite-members-modal{ data: { is_project: 'false',
access_levels: group.access_level_roles.to_json,
- reload_page_on_submit: local_assigns.fetch(:reload_page_on_submit, false).to_s,
+ reload_page_on_submit: current_path?('group_members#index').to_s,
help_link: help_page_url('user/permissions') }.merge(common_invite_modal_dataset(group)).merge(users_filter_data(group)) }
diff --git a/app/views/groups/_invite_members_side_nav_link.html.haml b/app/views/groups/_invite_members_side_nav_link.html.haml
index 978ef01984c..ab7d4fc5041 100644
--- a/app/views/groups/_invite_members_side_nav_link.html.haml
+++ b/app/views/groups/_invite_members_side_nav_link.html.haml
@@ -5,4 +5,3 @@
qa_selector: 'invite_members_sidebar_button' } }
= render partial: 'shared/nav/sidebar_submenu', locals: { sidebar_menu: sidebar_menu }
-= render 'groups/invite_members_modal', group: group
diff --git a/app/views/groups/_invite_members_top_nav_link.html.haml b/app/views/groups/_invite_members_top_nav_link.html.haml
new file mode 100644
index 00000000000..a3a28ffd6e0
--- /dev/null
+++ b/app/views/groups/_invite_members_top_nav_link.html.haml
@@ -0,0 +1,6 @@
+= link_to local_assigns.fetch(:href), class: local_assigns.fetch(:css_class), data: local_assigns.fetch(:data) do
+ = local_assigns.fetch(:display_text)
+ - if local_assigns.fetch(:icon)
+ = " #{emoji_icon(local_assigns.fetch(:icon), 'aria-hidden': true, class: 'gl-font-base gl-vertical-align-baseline')}".html_safe
+
+= render 'groups/invite_members_modal', group: local_assigns.fetch(:context)
diff --git a/app/views/groups/_new_group_fields.html.haml b/app/views/groups/_new_group_fields.html.haml
index 95990e8937c..ddf6e52796f 100644
--- a/app/views/groups/_new_group_fields.html.haml
+++ b/app/views/groups/_new_group_fields.html.haml
@@ -31,6 +31,5 @@
.row
.col-sm-12
= f.submit submit_label, pajamas_button: true, data: { qa_selector: 'create_group_button' }
- = render Pajamas::ButtonComponent.new(href: dashboard_groups_path) do
+ = render Pajamas::ButtonComponent.new(href: @parent_group || dashboard_groups_path) do
= _('Cancel')
-
diff --git a/app/views/groups/group_members/index.html.haml b/app/views/groups/group_members/index.html.haml
index 298ed2c0806..04bf3f98a1e 100644
--- a/app/views/groups/group_members/index.html.haml
+++ b/app/views/groups/group_members/index.html.haml
@@ -16,7 +16,6 @@
trigger_source: 'group-members-page',
display_text: _('Invite members') } }
= render 'groups/invite_groups_modal', group: @group, reload_page_on_submit: true
- = render 'groups/invite_members_modal', group: @group, reload_page_on_submit: true
= render_if_exists 'groups/group_members/ldap_sync'
diff --git a/app/views/groups/new.html.haml b/app/views/groups/new.html.haml
index b75fda2f344..0878fbf9a35 100644
--- a/app/views/groups/new.html.haml
+++ b/app/views/groups/new.html.haml
@@ -4,7 +4,7 @@
- header_title _("Groups"), dashboard_groups_path
- add_page_specific_style 'page_bundles/new_namespace'
-.group-edit-container.gl-mt-5
+.group-edit-container
.js-new-group-creation{ data: { has_errors: @group.errors.any?.to_s }.merge(subgroup_creation_data(@group),
verification_for_group_creation_data) }
diff --git a/app/views/groups/show.html.haml b/app/views/groups/show.html.haml
index 72b7bec1b92..7983274f319 100644
--- a/app/views/groups/show.html.haml
+++ b/app/views/groups/show.html.haml
@@ -14,7 +14,6 @@
callouts_path: group_callouts_path,
callouts_feature_id: Users::GroupCalloutsHelper::INVITE_MEMBERS_BANNER,
group_id: @group.id } }
- = render 'groups/invite_members_modal', group: @group
= content_for :meta_tags do
= auto_discovery_link_tag(:atom, group_url(@group, rss_url_options), title: "#{@group.name} activity")
diff --git a/app/views/layouts/_page.html.haml b/app/views/layouts/_page.html.haml
index 77f0c3c636f..0fd0a97a658 100644
--- a/app/views/layouts/_page.html.haml
+++ b/app/views/layouts/_page.html.haml
@@ -4,7 +4,7 @@
- if show_super_sidebar?
- sidebar_panel = super_sidebar_nav_panel(nav: nav, user: current_user, group: @group, project: @project, current_ref: current_ref, ref_type: @ref_type)
- sidebar_data = super_sidebar_context(current_user, group: @group, project: @project, panel: sidebar_panel).to_json
- %aside.js-super-sidebar.nav-sidebar{ data: { root_path: root_path, sidebar: sidebar_data, toggle_new_nav_endpoint: profile_preferences_url } }
+ %aside.js-super-sidebar.super-sidebar.super-sidebar-loading{ data: { root_path: root_path, sidebar: sidebar_data, toggle_new_nav_endpoint: profile_preferences_url } }
- if display_whats_new?
#whats-new-app{ data: { version_digest: whats_new_version_digest } }
diff --git a/app/views/layouts/dashboard.html.haml b/app/views/layouts/dashboard.html.haml
index 89f238eb6b3..1ac5f0a8497 100644
--- a/app/views/layouts/dashboard.html.haml
+++ b/app/views/layouts/dashboard.html.haml
@@ -2,6 +2,6 @@
- header_title _("Dashboard"), root_path unless header_title
- @left_sidebar = true
-- nav "your_work"
+- nav (@parent_group ? "group" : "your_work")
= render template: "layouts/application"
diff --git a/app/views/layouts/header/_new_dropdown.html.haml b/app/views/layouts/header/_new_dropdown.html.haml
index 372babea18e..c133ce21c6c 100644
--- a/app/views/layouts/header/_new_dropdown.html.haml
+++ b/app/views/layouts/header/_new_dropdown.html.haml
@@ -1,4 +1,4 @@
-- view_model = new_dropdown_view_model(project: @project, group: @group)
+- view_model = new_dropdown_view_model(project: @project, group: @group, with_context: true)
- menu_sections = view_model.fetch(:menu_sections)
- title = view_model.fetch(:title)
- show_headers = menu_sections.length > 1
@@ -26,8 +26,16 @@
= section.fetch(:title)
- section.fetch(:menu_items).each do |menu_item|
%li<
- = link_to menu_item.fetch(:href), class: menu_item.fetch(:css_class), data: menu_item.fetch(:data) do
- = menu_item.fetch(:title)
- - if menu_item.fetch(:emoji)
- -# We need to insert a space between the title and emoji
- = " #{emoji_icon(menu_item.fetch(:emoji), 'aria-hidden': true, class: 'gl-font-base gl-vertical-align-baseline')}".html_safe
+ - if menu_item.fetch(:partial).present?
+ = render partial: menu_item.fetch(:partial),
+ locals: { context: view_model[:context],
+ display_text: menu_item.fetch(:title),
+ icon: menu_item.fetch(:icon),
+ css_class: menu_item.fetch(:css_class),
+ href: menu_item.fetch(:href),
+ data: menu_item.fetch(:data) }
+ - else
+ = link_to menu_item.fetch(:title),
+ menu_item.fetch(:href),
+ class: menu_item.fetch(:css_class),
+ data: menu_item.fetch(:data)
diff --git a/app/views/layouts/nav/_breadcrumbs.html.haml b/app/views/layouts/nav/_breadcrumbs.html.haml
index 98d6af28cf5..06dff99718c 100644
--- a/app/views/layouts/nav/_breadcrumbs.html.haml
+++ b/app/views/layouts/nav/_breadcrumbs.html.haml
@@ -3,25 +3,26 @@
- unless @skip_current_level_breadcrumb
- push_to_schema_breadcrumb(@breadcrumb_title, breadcrumb_title_link)
-%nav.breadcrumbs{ class: [container, @content_class], 'aria-label': _('Breadcrumbs') }
- .breadcrumbs-container{ class: ("border-bottom-0" if @no_breadcrumb_border) }
- - if defined?(@left_sidebar)
- = button_tag class: 'toggle-mobile-nav', data: { qa_selector: 'toggle_mobile_nav_button' }, type: 'button' do
- %span.sr-only= _("Open sidebar")
- = sprite_icon('sidebar', size: 18)
- .breadcrumbs-links{ data: { testid: 'breadcrumb-links', qa_selector: 'breadcrumb_links_content' } }
- %ul.list-unstyled.breadcrumbs-list.js-breadcrumbs-list
- - unless hide_top_links
- = header_title
- - if @breadcrumbs_extra_links
- - @breadcrumbs_extra_links.each do |extra|
- = breadcrumb_list_item link_to(extra[:text], extra[:link])
- = render "layouts/nav/breadcrumbs/collapsed_inline_list", location: :after
- - unless @skip_current_level_breadcrumb
- %li{ data: { testid: 'breadcrumb-current-link', qa_selector: 'breadcrumb_current_link' } }
- = link_to @breadcrumb_title, breadcrumb_title_link
- -# haml-lint:disable InlineJavaScript
- %script{ type: 'application/ld+json' }
- :plain
- #{schema_breadcrumb_json}
- = yield :header_content
+.gl-relative
+ .breadcrumbs{ class: [container, @content_class] }
+ .breadcrumbs-container{ class: ("border-bottom-0" if @no_breadcrumb_border) }
+ - if show_super_sidebar?
+ = render Pajamas::ButtonComponent.new(icon: 'sidebar', category: :tertiary, button_options: { class: 'js-super-sidebar-toggle super-sidebar-toggle gl-ml-n3 gl-mr-2', title: _('Expand sidebar'), aria: { label: _('Expand sidebar') }, data: {toggle: 'tooltip', placement: 'right' } })
+ - elsif defined?(@left_sidebar)
+ = render Pajamas::ButtonComponent.new(icon: 'sidebar', category: :tertiary, button_options: { class: 'toggle-mobile-nav gl-ml-n3 gl-mr-2', data: { qa_selector: 'toggle_mobile_nav_button' }, aria: { label: _('Open sidebar') } })
+ %nav.breadcrumbs-links{ 'aria-label': _('Breadcrumbs'), data: { testid: 'breadcrumb-links', qa_selector: 'breadcrumb_links_content' } }
+ %ul.list-unstyled.breadcrumbs-list.js-breadcrumbs-list
+ - unless hide_top_links
+ = header_title
+ - if @breadcrumbs_extra_links
+ - @breadcrumbs_extra_links.each do |extra|
+ = breadcrumb_list_item link_to(extra[:text], extra[:link])
+ = render "layouts/nav/breadcrumbs/collapsed_inline_list", location: :after
+ - unless @skip_current_level_breadcrumb
+ %li{ data: { testid: 'breadcrumb-current-link', qa_selector: 'breadcrumb_current_link' } }
+ = link_to @breadcrumb_title, breadcrumb_title_link
+ -# haml-lint:disable InlineJavaScript
+ %script{ type: 'application/ld+json' }
+ :plain
+ #{schema_breadcrumb_json}
+ = yield :header_content
diff --git a/app/views/layouts/nav/sidebar/_group.html.haml b/app/views/layouts/nav/sidebar/_group.html.haml
index c2b50bc0e52..fd0e47b543f 100644
--- a/app/views/layouts/nav/sidebar/_group.html.haml
+++ b/app/views/layouts/nav/sidebar/_group.html.haml
@@ -1 +1,2 @@
-= render partial: 'shared/nav/sidebar', object: Sidebars::Groups::Panel.new(group_sidebar_context(@group, current_user))
+- group = @parent_group || @group
+= render partial: 'shared/nav/sidebar', object: Sidebars::Groups::Panel.new(group_sidebar_context(group, current_user))
diff --git a/app/views/projects/_invite_members_empty_project.html.haml b/app/views/projects/_invite_members_empty_project.html.haml
index 5bc53339bf0..1ab92d56d25 100644
--- a/app/views/projects/_invite_members_empty_project.html.haml
+++ b/app/views/projects/_invite_members_empty_project.html.haml
@@ -9,5 +9,3 @@
trigger_source: 'project-empty-page',
event: 'click_button',
label: 'invite_members_empty_project' } }
-
-= render 'projects/invite_members_modal', project: @project
diff --git a/app/views/projects/_invite_members_modal.html.haml b/app/views/projects/_invite_members_modal.html.haml
index 53f74a0f270..a1b0bdd6c56 100644
--- a/app/views/projects/_invite_members_modal.html.haml
+++ b/app/views/projects/_invite_members_modal.html.haml
@@ -2,5 +2,5 @@
.js-invite-members-modal{ data: { is_project: 'true',
access_levels: ProjectMember.permissible_access_level_roles(current_user, project).to_json,
- reload_page_on_submit: local_assigns.fetch(:reload_page_on_submit, false).to_s,
+ reload_page_on_submit: current_path?('project_members#index').to_s,
help_link: help_page_url('user/permissions') }.merge(common_invite_modal_dataset(project)).merge(users_filter_data(project.group)) }
diff --git a/app/views/projects/_invite_members_side_nav_link.html.haml b/app/views/projects/_invite_members_side_nav_link.html.haml
index b96a7608ce2..771c7c650c2 100644
--- a/app/views/projects/_invite_members_side_nav_link.html.haml
+++ b/app/views/projects/_invite_members_side_nav_link.html.haml
@@ -5,4 +5,3 @@
qa_selector: 'invite_members_sidebar_button' } }
= render partial: 'shared/nav/sidebar_submenu', locals: { sidebar_menu: sidebar_menu }
-= render 'projects/invite_members_modal', project: project
diff --git a/app/views/projects/_invite_members_top_nav_link.html.haml b/app/views/projects/_invite_members_top_nav_link.html.haml
new file mode 100644
index 00000000000..e3a5c40d93c
--- /dev/null
+++ b/app/views/projects/_invite_members_top_nav_link.html.haml
@@ -0,0 +1,6 @@
+= link_to local_assigns.fetch(:href), class: local_assigns.fetch(:css_class), data: local_assigns.fetch(:data) do
+ = local_assigns.fetch(:display_text)
+ - if local_assigns.fetch(:icon)
+ = " #{emoji_icon(local_assigns.fetch(:icon), 'aria-hidden': true, class: 'gl-font-base gl-vertical-align-baseline')}".html_safe
+
+= render 'projects/invite_members_modal', project: local_assigns.fetch(:context)
diff --git a/app/views/projects/_new_project_fields.html.haml b/app/views/projects/_new_project_fields.html.haml
index 53a1abdff33..27211ffb1e5 100644
--- a/app/views/projects/_new_project_fields.html.haml
+++ b/app/views/projects/_new_project_fields.html.haml
@@ -98,4 +98,4 @@
-# this partial is from JiHu, see details in https://jihulab.com/gitlab-cn/gitlab/-/merge_requests/675
= render_if_exists 'shared/other_project_options', f: f, visibility_level: visibility_level, track_label: track_label
= f.submit _('Create project'), class: "js-create-project-button", data: { qa_selector: 'project_create_button', track_label: "#{track_label}", track_action: "click_button", track_property: "create_project", track_value: "" }, pajamas_button: true
-= link_to _('Cancel'), dashboard_projects_path, class: 'btn gl-button btn-default btn-cancel', data: { track_label: "#{track_label}", track_action: "click_button", track_property: "cancel", track_value: "" }
+= link_to _('Cancel'), @parent_group || dashboard_groups_path, class: 'btn gl-button btn-default btn-cancel', data: { track_label: "#{track_label}", track_action: "click_button", track_property: "cancel", track_value: "" }
diff --git a/app/views/projects/issues/show.html.haml b/app/views/projects/issues/show.html.haml
index a8edf87b696..7e8bf4ae57f 100644
--- a/app/views/projects/issues/show.html.haml
+++ b/app/views/projects/issues/show.html.haml
@@ -8,4 +8,3 @@
- add_page_specific_style 'page_bundles/work_items'
= render 'projects/issuable/show', issuable: @issue, api_awards_path: award_emoji_issue_api_path(@issue)
-= render 'projects/invite_members_modal', project: @project
diff --git a/app/views/projects/merge_requests/_page.html.haml b/app/views/projects/merge_requests/_page.html.haml
index 880bffc43ab..47760211bd5 100644
--- a/app/views/projects/merge_requests/_page.html.haml
+++ b/app/views/projects/merge_requests/_page.html.haml
@@ -110,5 +110,4 @@
- if current_user && Feature.enabled?(:mr_experience_survey, current_user)
#js-mr-experience-survey{ data: { account_age: current_user.account_age_in_days } }
-= render 'projects/invite_members_modal', project: @project
= render 'shared/web_ide_path'
diff --git a/app/views/projects/new.html.haml b/app/views/projects/new.html.haml
index 56581fe7b18..a089dd60e7d 100644
--- a/app/views/projects/new.html.haml
+++ b/app/views/projects/new.html.haml
@@ -4,11 +4,11 @@
- header_title _("Projects"), dashboard_projects_path
- add_page_specific_style 'page_bundles/new_namespace'
-.project-edit-container.gl-mt-5
+.project-edit-container
.project-edit-errors
= render 'projects/errors'
- .js-new-project-creation{ data: { is_ci_cd_available: (ci_cd_projects_available? if Gitlab.ee?).to_s, has_errors: @project.errors.any?.to_s, new_project_guidelines: brand_new_project_guidelines, push_to_create_project_command: push_to_create_project_command, working_with_projects_help_path: help_page_path("user/project/working_with_projects") } }
+ .js-new-project-creation{ data: { is_ci_cd_available: (ci_cd_projects_available? if Gitlab.ee?).to_s, has_errors: @project.errors.any?.to_s, new_project_guidelines: brand_new_project_guidelines, push_to_create_project_command: push_to_create_project_command, working_with_projects_help_path: help_page_path("user/project/working_with_projects"), parent_group_url: @project.parent && group_url(@project.parent), parent_group_name: @project.parent&.name } }
.row{ 'v-cloak': true }
#blank-project-pane.tab-pane.active
diff --git a/app/views/projects/project_members/index.html.haml b/app/views/projects/project_members/index.html.haml
index 4ac0e28d386..a0a90fbe204 100644
--- a/app/views/projects/project_members/index.html.haml
+++ b/app/views/projects/project_members/index.html.haml
@@ -25,7 +25,6 @@
classes: 'gl-md-w-auto gl-w-full gl-md-ml-3 gl-md-mt-0 gl-mt-3',
trigger_source: 'project-members-page',
display_text: _('Invite members') } }
- = render 'projects/invite_members_modal', project: @project, reload_page_on_submit: true
- else
- if project_can_be_shared?
%h4
diff --git a/app/views/projects/work_items/index.html.haml b/app/views/projects/work_items/index.html.haml
index 69597aab7ef..dad8bb09ff6 100644
--- a/app/views/projects/work_items/index.html.haml
+++ b/app/views/projects/work_items/index.html.haml
@@ -4,4 +4,3 @@
- @noteable_type = 'WorkItem'
#js-work-items{ data: work_items_index_data(@project) }
-= render 'projects/invite_members_modal', project: @project
diff --git a/db/migrate/20230215180605_index_sbom_occurrences_on_project_id_and_id.rb b/db/migrate/20230215180605_index_sbom_occurrences_on_project_id_and_id.rb
new file mode 100644
index 00000000000..34538a1a7ff
--- /dev/null
+++ b/db/migrate/20230215180605_index_sbom_occurrences_on_project_id_and_id.rb
@@ -0,0 +1,15 @@
+# frozen_string_literal: true
+
+class IndexSbomOccurrencesOnProjectIdAndId < Gitlab::Database::Migration[2.1]
+ disable_ddl_transaction!
+
+ INDEX_NAME = 'index_sbom_occurrences_on_project_id_and_id'
+
+ def up
+ add_concurrent_index :sbom_occurrences, [:project_id, :id], name: INDEX_NAME
+ end
+
+ def down
+ remove_concurrent_index_by_name :sbom_occurrences, INDEX_NAME
+ end
+end
diff --git a/db/schema_migrations/20230215180605 b/db/schema_migrations/20230215180605
new file mode 100644
index 00000000000..a3b222de10f
--- /dev/null
+++ b/db/schema_migrations/20230215180605
@@ -0,0 +1 @@
+879bee488f4089527de02ebfd6c9d6f6de7ab24d87361e29f998d86b62ca7461 \ No newline at end of file
diff --git a/db/structure.sql b/db/structure.sql
index 61b0e28c8ad..83e66e11098 100644
--- a/db/structure.sql
+++ b/db/structure.sql
@@ -31635,6 +31635,8 @@ CREATE INDEX index_sbom_occurrences_on_pipeline_id ON sbom_occurrences USING btr
CREATE INDEX index_sbom_occurrences_on_project_id ON sbom_occurrences USING btree (project_id);
+CREATE INDEX index_sbom_occurrences_on_project_id_and_id ON sbom_occurrences USING btree (project_id, id);
+
CREATE INDEX index_sbom_occurrences_on_source_id ON sbom_occurrences USING btree (source_id);
CREATE UNIQUE INDEX index_sbom_occurrences_on_uuid ON sbom_occurrences USING btree (uuid);
diff --git a/doc/api/graphql/reference/index.md b/doc/api/graphql/reference/index.md
index 5e0ef4f9c5d..088fa9a4cc6 100644
--- a/doc/api/graphql/reference/index.md
+++ b/doc/api/graphql/reference/index.md
@@ -7630,6 +7630,29 @@ The edge type for [`DastSiteValidation`](#dastsitevalidation).
| <a id="dastsitevalidationedgecursor"></a>`cursor` | [`String!`](#string) | A cursor for use in pagination. |
| <a id="dastsitevalidationedgenode"></a>`node` | [`DastSiteValidation`](#dastsitevalidation) | The item at the end of the edge. |
+#### `DependencyConnection`
+
+The connection type for [`Dependency`](#dependency).
+
+##### Fields
+
+| Name | Type | Description |
+| ---- | ---- | ----------- |
+| <a id="dependencyconnectionedges"></a>`edges` | [`[DependencyEdge]`](#dependencyedge) | A list of edges. |
+| <a id="dependencyconnectionnodes"></a>`nodes` | [`[Dependency]`](#dependency) | A list of nodes. |
+| <a id="dependencyconnectionpageinfo"></a>`pageInfo` | [`PageInfo!`](#pageinfo) | Information to aid in pagination. |
+
+#### `DependencyEdge`
+
+The edge type for [`Dependency`](#dependency).
+
+##### Fields
+
+| Name | Type | Description |
+| ---- | ---- | ----------- |
+| <a id="dependencyedgecursor"></a>`cursor` | [`String!`](#string) | A cursor for use in pagination. |
+| <a id="dependencyedgenode"></a>`node` | [`Dependency`](#dependency) | The item at the end of the edge. |
+
#### `DependencyProxyBlobConnection`
The connection type for [`DependencyProxyBlob`](#dependencyproxyblob).
@@ -12335,6 +12358,20 @@ The response from the AdminSidekiqQueuesDeleteJobs mutation.
| <a id="deletednoteid"></a>`id` | [`NoteID!`](#noteid) | ID of the deleted note. |
| <a id="deletednotelastdiscussionnote"></a>`lastDiscussionNote` | [`Boolean`](#boolean) | Whether deleted note is the last note in the discussion. |
+### `Dependency`
+
+A software dependency used by a project.
+
+#### Fields
+
+| Name | Type | Description |
+| ---- | ---- | ----------- |
+| <a id="dependencyid"></a>`id` | [`GlobalID!`](#globalid) | ID of the dependency. |
+| <a id="dependencylocation"></a>`location` | [`Location`](#location) | Information about where the dependency is located. |
+| <a id="dependencyname"></a>`name` | [`String!`](#string) | Name of the dependency. |
+| <a id="dependencypackager"></a>`packager` | [`String`](#string) | Description of the tool used to manage the dependency. |
+| <a id="dependencyversion"></a>`version` | [`String`](#string) | Version of the dependency. |
+
### `DependencyProxyBlob`
Dependency proxy blob.
@@ -15457,6 +15494,15 @@ Represents an entry from the Cloud License history.
| <a id="licensehistoryentrytype"></a>`type` | [`String!`](#string) | Type of the license. |
| <a id="licensehistoryentryusersinlicensecount"></a>`usersInLicenseCount` | [`Int`](#int) | Number of paid users in the license. |
+### `Location`
+
+#### Fields
+
+| Name | Type | Description |
+| ---- | ---- | ----------- |
+| <a id="locationblobpath"></a>`blobPath` | [`String`](#string) | HTTP URI path to view the input file in GitLab. |
+| <a id="locationpath"></a>`path` | [`String`](#string) | Path, relative to the root of the repository, of the filewhich was analyzed to detect the dependency. |
+
### `MavenMetadata`
Maven metadata.
@@ -17740,6 +17786,7 @@ Represents a product analytics dashboard visualization.
| <a id="projectcreatedat"></a>`createdAt` | [`Time`](#time) | Timestamp of the project creation. |
| <a id="projectdastscannerprofiles"></a>`dastScannerProfiles` | [`DastScannerProfileConnection`](#dastscannerprofileconnection) | DAST scanner profiles associated with the project. (see [Connections](#connections)) |
| <a id="projectdastsiteprofiles"></a>`dastSiteProfiles` | [`DastSiteProfileConnection`](#dastsiteprofileconnection) | DAST Site Profiles associated with the project. (see [Connections](#connections)) |
+| <a id="projectdependencies"></a>`dependencies` **{warning-solid}** | [`DependencyConnection`](#dependencyconnection) | **Introduced** in 15.9. This feature is in Alpha. It can be changed or removed at any time. Software dependencies used by the project. |
| <a id="projectdescription"></a>`description` | [`String`](#string) | Short description of the project. |
| <a id="projectdescriptionhtml"></a>`descriptionHtml` | [`String`](#string) | GitLab Flavored Markdown rendering of `description`. |
| <a id="projectdora"></a>`dora` | [`Dora`](#dora) | Project's DORA metrics. |
diff --git a/doc/api/group_badges.md b/doc/api/group_badges.md
index ede7591c6d1..0d5faae7c14 100644
--- a/doc/api/group_badges.md
+++ b/doc/api/group_badges.md
@@ -10,7 +10,7 @@ info: To determine the technical writer assigned to the Stage/Group associated w
## Placeholder tokens
-Badges support placeholders that are replaced in real time in both the link and image URL. The allowed placeholders are:
+[Badges](../user/project/badges.md) support placeholders that are replaced in real time in both the link and image URL. The allowed placeholders are:
<!-- vale gitlab.Spelling = NO -->
diff --git a/doc/api/project_badges.md b/doc/api/project_badges.md
index 3dad40a3f96..a8940a7875c 100644
--- a/doc/api/project_badges.md
+++ b/doc/api/project_badges.md
@@ -8,7 +8,7 @@ info: To determine the technical writer assigned to the Stage/Group associated w
## Placeholder tokens
-Badges support placeholders that are replaced in real-time in both the link and image URL. The allowed placeholders are:
+[Badges](../user/project/badges.md) support placeholders that are replaced in real-time in both the link and image URL. The allowed placeholders are:
<!-- vale gitlab.Spelling = NO -->
diff --git a/doc/api/projects.md b/doc/api/projects.md
index b502d547ddc..c8755e020a7 100644
--- a/doc/api/projects.md
+++ b/doc/api/projects.md
@@ -1433,6 +1433,7 @@ Supported attributes:
|-------------------------------------------------------------|----------------|------------------------|-------------|
| `id` | integer or string | **{check-circle}** Yes | The ID or [URL-encoded path of the project](rest/index.md#namespaced-path-encoding). |
| `allow_merge_on_skipped_pipeline` | boolean | **{dotted-circle}** No | Set whether or not merge requests can be merged with skipped jobs. |
+| `allow_pipeline_trigger_approve_deployment` **(PREMIUM)** | boolean | **{dotted-circle}** No | Set whether or not a pipeline triggerer is allowed to approve deployments. |
| `only_allow_merge_if_all_status_checks_passed` **(ULTIMATE)** | boolean | **{dotted-circle}** No | Indicates that merges of merge requests should be blocked unless all status checks have passed. Defaults to false.<br/><br/>[Introduced](https://gitlab.com/gitlab-org/gitlab/-/issues/369859) in GitLab 15.5 with feature flag `only_allow_merge_if_all_status_checks_passed` disabled by default. The feature flag was enabled by default in GitLab 15.9. |
| `analytics_access_level` | string | **{dotted-circle}** No | One of `disabled`, `private` or `enabled` |
| `approvals_before_merge` **(PREMIUM)** | integer | **{dotted-circle}** No | How many approvers should approve merge request by default. To configure approval rules, see [Merge request approvals API](merge_request_approvals.md). |
diff --git a/doc/ci/yaml/index.md b/doc/ci/yaml/index.md
index d5c0fe1d41d..5d5a1f9e8fe 100644
--- a/doc/ci/yaml/index.md
+++ b/doc/ci/yaml/index.md
@@ -422,23 +422,30 @@ A configuration with different pipeline names depending on the pipeline conditio
```yaml
variables:
- PIPELINE_NAME: 'Default pipeline name' # A default is not required.
+ PROJECT1_PIPELINE_NAME: 'Default pipeline name' # A default is not required.
workflow:
- name: '$PIPELINE_NAME'
+ name: '$PROJECT1_PIPELINE_NAME'
rules:
- if: '$CI_PIPELINE_SOURCE == "merge_request_event"'
variables:
- PIPELINE_NAME: 'MR pipeline: $CI_COMMIT_BRANCH'
+ PROJECT1_PIPELINE_NAME: 'MR pipeline: $CI_MERGE_REQUEST_SOURCE_BRANCH_NAME'
- if: '$CI_MERGE_REQUEST_LABELS =~ /pipeline:run-in-ruby3/'
variables:
- PIPELINE_NAME: 'Ruby 3 pipeline'
+ PROJECT1_PIPELINE_NAME: 'Ruby 3 pipeline'
```
**Additional details**:
- If the name is an empty string, the pipeline is not assigned a name. A name consisting
of only CI/CD variables could evaluate to an empty string if all the variables are also empty.
+- `workflow:rules:variables` become [global variables](#variables) available in all jobs,
+ including [`trigger`](#trigger) jobs which forward variables to downstream pipelines by default.
+ If the downstream pipeline uses the same variable, the [variable is overwritten](../variables/index.md#cicd-variable-precedence)
+ by the upstream variable value. Be sure to either:
+ - Use a unique variable name in every project's pipeline configuration, like `PROJECT1_PIPELINE_NAME`.
+ - Use [`inherit:variables`](#inheritvariables) in the trigger job to select the
+ exact variables to forward to the downstream pipeline.
#### `workflow:rules`
@@ -552,6 +559,16 @@ When the branch is something else:
- job1's `DEPLOY_VARIABLE` is `job1-default-deploy`.
- job2's `DEPLOY_VARIABLE` is `default-deploy`.
+**Additional details**:
+
+- `workflow:rules:variables` become [global variables](#variables) available in all jobs,
+ including [`trigger`](#trigger) jobs which forward variables to downstream pipelines by default.
+ If the downstream pipeline uses the same variable, the [variable is overwritten](../variables/index.md#cicd-variable-precedence)
+ by the upstream variable value. Be sure to either:
+ - Use unique variable names in every project's pipeline configuration, like `PROJECT1_VARIABLE_NAME`.
+ - Use [`inherit:variables`](#inheritvariables) in the trigger job to select the
+ exact variables to forward to the downstream pipeline.
+
## Job keywords
The following topics explain how to use keywords to configure CI/CD pipelines.
diff --git a/lib/gitlab/nav/top_nav_menu_item.rb b/lib/gitlab/nav/top_nav_menu_item.rb
index 75eb0e8a264..af88f55014c 100644
--- a/lib/gitlab/nav/top_nav_menu_item.rb
+++ b/lib/gitlab/nav/top_nav_menu_item.rb
@@ -8,7 +8,10 @@ module Gitlab
# this is already :/. We could also take a hash and manually check every
# entry, but it's much more maintainable to do rely on native Ruby.
# rubocop: disable Metrics/ParameterLists
- def self.build(id:, title:, active: false, icon: '', href: '', view: '', css_class: nil, data: nil, emoji: nil)
+ def self.build(
+ id:, title:, active: false, icon: '', href: '', view: '',
+ css_class: nil, data: nil, partial: nil
+ )
{
id: id,
type: :item,
@@ -19,7 +22,7 @@ module Gitlab
view: view.to_s,
css_class: css_class,
data: data || { qa_selector: 'menu_item_link', qa_title: title },
- emoji: emoji
+ partial: partial
}
end
# rubocop: enable Metrics/ParameterLists
diff --git a/lib/sidebars/groups/menus/scope_menu.rb b/lib/sidebars/groups/menus/scope_menu.rb
index 6ce43491343..f6e0906dac4 100644
--- a/lib/sidebars/groups/menus/scope_menu.rb
+++ b/lib/sidebars/groups/menus/scope_menu.rb
@@ -16,7 +16,7 @@ module Sidebars
override :active_routes
def active_routes
- { path: %w[groups#show groups#details] }
+ { path: %w[groups#show groups#details groups#new projects#new] }
end
override :extra_nav_link_html_options
diff --git a/locale/gitlab.pot b/locale/gitlab.pot
index da09ebd8400..ea74c2390ac 100644
--- a/locale/gitlab.pot
+++ b/locale/gitlab.pot
@@ -20441,6 +20441,12 @@ msgstr ""
msgid "GroupsNew|Importing groups by direct transfer is currently disabled."
msgstr ""
+msgid "GroupsNew|New group"
+msgstr ""
+
+msgid "GroupsNew|New subgroup"
+msgstr ""
+
msgid "GroupsNew|No import options available"
msgstr ""
@@ -34249,6 +34255,9 @@ msgstr ""
msgid "ProjectsNew|Must start with a lowercase or uppercase letter, digit, emoji, or underscore. Can also contain dots, pluses, dashes, or spaces."
msgstr ""
+msgid "ProjectsNew|New project"
+msgstr ""
+
msgid "ProjectsNew|No import options available"
msgstr ""
diff --git a/package.json b/package.json
index c9339d3c1bb..3a502402912 100644
--- a/package.json
+++ b/package.json
@@ -57,7 +57,7 @@
"@gitlab/favicon-overlay": "2.0.0",
"@gitlab/fonts": "^1.2.0",
"@gitlab/svgs": "3.20.0",
- "@gitlab/ui": "55.2.1",
+ "@gitlab/ui": "56.0.0",
"@gitlab/visual-review-tools": "1.7.3",
"@gitlab/web-ide": "0.0.1-dev-20230216131813",
"@rails/actioncable": "6.1.4-7",
diff --git a/scripts/frontend/startup_css/constants.js b/scripts/frontend/startup_css/constants.js
index 5143c04dc37..e6ca4472fe3 100644
--- a/scripts/frontend/startup_css/constants.js
+++ b/scripts/frontend/startup_css/constants.js
@@ -52,11 +52,14 @@ const createMainOutput = ({ outFile, cssKeys, type }) => ({
path.join(FIXTURES_ROOT, `startup_css/project-${type}.html`),
path.join(FIXTURES_ROOT, `startup_css/project-${type}-signed-out.html`),
path.join(FIXTURES_ROOT, `startup_css/project-${type}-search-ff-off.html`),
+ path.join(FIXTURES_ROOT, `startup_css/project-${type}-super-sidebar.html`),
],
cssKeys,
purgeOptions: {
safelist: {
standard: [
+ 'page-with-super-sidebar',
+ 'page-with-super-sidebar-collapsed',
'page-with-icon-sidebar',
'sidebar-collapsed-desktop',
// We want to include the root dropdown-menu style since it should be hidden by default
diff --git a/spec/controllers/concerns/analytics/cycle_analytics/value_stream_actions_spec.rb b/spec/controllers/concerns/analytics/cycle_analytics/value_stream_actions_spec.rb
index 246119a8118..a6a0f2c11b4 100644
--- a/spec/controllers/concerns/analytics/cycle_analytics/value_stream_actions_spec.rb
+++ b/spec/controllers/concerns/analytics/cycle_analytics/value_stream_actions_spec.rb
@@ -1,8 +1,9 @@
# frozen_string_literal: true
+
require 'spec_helper'
RSpec.describe Analytics::CycleAnalytics::ValueStreamActions, type: :controller,
-feature_category: :planning_analytics do
+ feature_category: :planning_analytics do
subject(:controller) do
Class.new(ApplicationController) do
include Analytics::CycleAnalytics::ValueStreamActions
diff --git a/spec/features/groups/new_group_page_spec.rb b/spec/features/groups/new_group_page_spec.rb
index a07c27331d9..4670df3fb5e 100644
--- a/spec/features/groups/new_group_page_spec.rb
+++ b/spec/features/groups/new_group_page_spec.rb
@@ -3,15 +3,14 @@
require 'spec_helper'
RSpec.describe 'New group page', :js, feature_category: :subgroups do
- let(:user) { create(:user) }
- let(:group) { create(:group) }
+ let_it_be(:user) { create(:user) }
+ let_it_be(:parent_group) { create(:group) }
before do
+ parent_group.add_owner(user)
sign_in(user)
end
- it_behaves_like 'a dashboard page with sidebar', :new_group_path, :groups
-
describe 'new top level group alert' do
context 'when a user visits the new group page' do
it 'shows the new top level group alert' do
@@ -22,8 +21,6 @@ RSpec.describe 'New group page', :js, feature_category: :subgroups do
end
context 'when a user visits the new sub group page' do
- let(:parent_group) { create(:group) }
-
it 'does not show the new top level group alert' do
visit new_group_path(parent_id: parent_group.id, anchor: 'create-group-pane')
@@ -31,4 +28,19 @@ RSpec.describe 'New group page', :js, feature_category: :subgroups do
end
end
end
+
+ describe 'sidebar' do
+ context 'for a new top-level group' do
+ it_behaves_like 'a dashboard page with sidebar', :new_group_path, :groups
+ end
+
+ context 'for a new subgroup' do
+ it 'shows the group sidebar of the parent group' do
+ visit new_group_path(parent_id: parent_group.id, anchor: 'create-group-pane')
+ expect(page).to have_selector(
+ ".nav-sidebar[aria-label=\"Group navigation\"] .context-header[title=\"#{parent_group.name}\"]"
+ )
+ end
+ end
+ end
end
diff --git a/spec/features/projects/new_project_spec.rb b/spec/features/projects/new_project_spec.rb
index c6a6ee68185..3cbfa14208f 100644
--- a/spec/features/projects/new_project_spec.rb
+++ b/spec/features/projects/new_project_spec.rb
@@ -578,4 +578,25 @@ RSpec.describe 'New project', :js, feature_category: :projects do
it_behaves_like 'has instructions to enable OAuth'
end
end
+
+ describe 'sidebar' do
+ let_it_be(:user) { create(:user) }
+ let_it_be(:parent_group) { create(:group) }
+
+ before do
+ parent_group.add_owner(user)
+ sign_in(user)
+ end
+
+ context 'for a new top-level project' do
+ it_behaves_like 'a dashboard page with sidebar', :new_project_path, :projects
+ end
+
+ context 'for a new group project' do
+ it 'shows the group sidebar of the parent group' do
+ visit new_project_path(namespace_id: parent_group.id)
+ expect(page).to have_selector(".nav-sidebar[aria-label=\"Group navigation\"] .context-header[title=\"#{parent_group.name}\"]")
+ end
+ end
+ end
end
diff --git a/spec/frontend/batch_comments/components/preview_dropdown_spec.js b/spec/frontend/batch_comments/components/preview_dropdown_spec.js
index f86e003ab5f..3a28bf4ade8 100644
--- a/spec/frontend/batch_comments/components/preview_dropdown_spec.js
+++ b/spec/frontend/batch_comments/components/preview_dropdown_spec.js
@@ -1,7 +1,6 @@
import Vue, { nextTick } from 'vue';
import Vuex from 'vuex';
-import { GlDisclosureDropdown } from '@gitlab/ui';
-import { shallowMount } from '@vue/test-utils';
+import { mount } from '@vue/test-utils';
import { TEST_HOST } from 'helpers/test_constants';
import { visitUrl } from '~/lib/utils/url_utility';
import PreviewDropdown from '~/batch_comments/components/preview_dropdown.vue';
@@ -46,9 +45,11 @@ function factory({ viewDiffsFileByFile = false, draftsCount = 1, sortedDrafts =
},
});
- wrapper = shallowMount(PreviewDropdown, {
+ wrapper = mount(PreviewDropdown, {
store,
- stubs: { GlDisclosureDropdown },
+ stubs: {
+ PreviewItem: true,
+ },
});
}
@@ -59,12 +60,12 @@ describe('Batch comments preview dropdown', () => {
viewDiffsFileByFile: true,
sortedDrafts: [{ id: 1, file_hash: 'hash' }],
});
-
- findPreviewItem().vm.$emit('click');
-
+ findPreviewItem().trigger('click');
await nextTick();
expect(setCurrentFileHash).toHaveBeenCalledWith(expect.anything(), 'hash');
+
+ await nextTick();
expect(scrollToDraft).toHaveBeenCalledWith(
expect.anything(),
expect.objectContaining({ id: 1, file_hash: 'hash' }),
@@ -77,7 +78,7 @@ describe('Batch comments preview dropdown', () => {
sortedDrafts: [{ id: 1 }],
});
- findPreviewItem().vm.$emit('click');
+ findPreviewItem().trigger('click');
await nextTick();
@@ -93,7 +94,7 @@ describe('Batch comments preview dropdown', () => {
sortedDrafts: [{ id: 1, position: { head_sha: '1234' } }],
});
- findPreviewItem().vm.$emit('click');
+ findPreviewItem().trigger('click');
await nextTick();
diff --git a/spec/frontend/fixtures/startup_css.rb b/spec/frontend/fixtures/startup_css.rb
index bd2d63a1827..2d766086cb7 100644
--- a/spec/frontend/fixtures/startup_css.rb
+++ b/spec/frontend/fixtures/startup_css.rb
@@ -55,6 +55,21 @@ RSpec.describe 'Startup CSS fixtures', type: :controller do
expect(response).to be_successful
end
+
+ # This Feature Flag is off by default
+ # This ensures that the correct css is generated for super sidebar
+ # When the feature flag is off, the general startup will capture it
+ it "startup_css/project-#{type}-super-sidebar.html" do
+ stub_feature_flags(super_sidebar_nav: true)
+ user.update!(use_new_navigation: true)
+
+ get :show, params: {
+ namespace_id: project.namespace.to_param,
+ id: project
+ }
+
+ expect(response).to be_successful
+ end
end
describe ProjectsController, '(Startup CSS fixtures)', type: :controller do
diff --git a/spec/frontend/pages/groups/new/components/app_spec.js b/spec/frontend/pages/groups/new/components/app_spec.js
index ab483316086..a3aa87574b5 100644
--- a/spec/frontend/pages/groups/new/components/app_spec.js
+++ b/spec/frontend/pages/groups/new/components/app_spec.js
@@ -23,7 +23,9 @@ describe('App component', () => {
it('creates correct component for group creation', () => {
createComponent();
- expect(findNewNamespacePage().props('initialBreadcrumb')).toBe('New group');
+ expect(findNewNamespacePage().props('initialBreadcrumbs')).toEqual([
+ { href: '#', text: 'New group' },
+ ]);
expect(findCreateGroupPanel().title).toBe('Create group');
});
@@ -32,7 +34,9 @@ describe('App component', () => {
createComponent(props);
- expect(findNewNamespacePage().props('initialBreadcrumb')).toBe('parent');
+ expect(findNewNamespacePage().props('initialBreadcrumbs')).toEqual([
+ { href: '#', text: 'New group' },
+ ]);
expect(findCreateGroupPanel().title).toBe('Create subgroup');
expect(findCreateGroupPanel().detailProps).toEqual(props);
});
diff --git a/spec/frontend/vue_shared/new_namespace/new_namespace_page_spec.js b/spec/frontend/vue_shared/new_namespace/new_namespace_page_spec.js
index 6115dc6e61b..40f75a0a8d6 100644
--- a/spec/frontend/vue_shared/new_namespace/new_namespace_page_spec.js
+++ b/spec/frontend/vue_shared/new_namespace/new_namespace_page_spec.js
@@ -14,7 +14,7 @@ describe('Experimental new project creation app', () => {
const DEFAULT_PROPS = {
title: 'Create something',
- initialBreadcrumb: 'Something',
+ initialBreadcrumbs: [{ text: 'Something', href: '#' }],
panels: [
{ name: 'panel1', selector: '#some-selector1' },
{ name: 'panel2', selector: '#some-selector2' },
@@ -46,8 +46,8 @@ describe('Experimental new project creation app', () => {
expect(findWelcomePage().exists()).toBe(true);
});
- it('does not render breadcrumbs', () => {
- expect(findBreadcrumb().exists()).toBe(false);
+ it('renders breadcrumbs', () => {
+ expect(findBreadcrumb().exists()).toBe(true);
});
});
@@ -75,7 +75,7 @@ describe('Experimental new project creation app', () => {
it('renders breadcrumbs', () => {
const breadcrumb = findBreadcrumb();
expect(breadcrumb.exists()).toBe(true);
- expect(breadcrumb.props().items[0].text).toBe(DEFAULT_PROPS.initialBreadcrumb);
+ expect(breadcrumb.props().items[0].text).toBe(DEFAULT_PROPS.initialBreadcrumbs[0].text);
});
});
diff --git a/spec/helpers/groups_helper_spec.rb b/spec/helpers/groups_helper_spec.rb
index 8b4ac6a7cfd..ce439e5bcdd 100644
--- a/spec/helpers/groups_helper_spec.rb
+++ b/spec/helpers/groups_helper_spec.rb
@@ -436,7 +436,8 @@ RSpec.describe GroupsHelper do
it 'returns expected hash' do
expect(subgroup_creation_data(subgroup)).to eq({
import_existing_group_path: '/groups/new#import-group-pane',
- parent_group_name: name
+ parent_group_name: name,
+ parent_group_url: group_url(group)
})
end
end
@@ -445,7 +446,8 @@ RSpec.describe GroupsHelper do
it 'returns expected hash' do
expect(subgroup_creation_data(group)).to eq({
import_existing_group_path: '/groups/new#import-group-pane',
- parent_group_name: nil
+ parent_group_name: nil,
+ parent_group_url: nil
})
end
end
diff --git a/spec/helpers/nav/new_dropdown_helper_spec.rb b/spec/helpers/nav/new_dropdown_helper_spec.rb
index 3a66fe474ab..1df0ee8c18a 100644
--- a/spec/helpers/nav/new_dropdown_helper_spec.rb
+++ b/spec/helpers/nav/new_dropdown_helper_spec.rb
@@ -11,8 +11,12 @@ RSpec.describe Nav::NewDropdownHelper, feature_category: :navigation do
let(:with_can_create_project) { false }
let(:with_can_create_group) { false }
let(:with_can_create_snippet) { false }
+ let(:with_context) { true }
+ let(:title) { 'Create new...' }
- subject(:view_model) { helper.new_dropdown_view_model(project: current_project, group: current_group) }
+ subject(:view_model) do
+ helper.new_dropdown_view_model(project: current_project, group: current_group, with_context: with_context)
+ end
before do
allow(helper).to receive(:current_user) { current_user }
@@ -22,7 +26,7 @@ RSpec.describe Nav::NewDropdownHelper, feature_category: :navigation do
allow(user).to receive(:can?).with(:create_snippet) { with_can_create_snippet }
end
- shared_examples 'invite member item' do
+ shared_examples 'invite member item' do |partial|
it 'shows invite member link with emoji' do
expect(view_model[:menu_sections]).to eq(
expected_menu_section(
@@ -30,8 +34,9 @@ RSpec.describe Nav::NewDropdownHelper, feature_category: :navigation do
menu_item: ::Gitlab::Nav::TopNavMenuItem.build(
id: 'invite',
title: 'Invite members',
- emoji: 'shaking_hands',
+ icon: 'shaking_hands',
href: expected_href,
+ partial: partial,
data: {
track_action: 'click_link_invite_members',
track_label: 'plus_menu_dropdown',
@@ -54,8 +59,13 @@ RSpec.describe Nav::NewDropdownHelper, feature_category: :navigation do
end
context 'when group and project are nil' do
- it 'has no menu sections' do
- expect(view_model[:menu_sections]).to eq([])
+ it 'has base results' do
+ results = {
+ title: title,
+ menu_sections: []
+ }
+
+ expect(view_model).to eq(results)
end
context 'when can create project' do
@@ -145,8 +155,27 @@ RSpec.describe Nav::NewDropdownHelper, feature_category: :navigation do
.to receive(:can?).with(current_user, :admin_group_member, group) { with_can_admin_in_group }
end
- it 'has no menu sections' do
- expect(view_model[:menu_sections]).to eq([])
+ it 'has base results' do
+ results = {
+ title: title,
+ menu_sections: [],
+ context: group
+ }
+
+ expect(view_model).to eq(results)
+ end
+
+ context 'without context' do
+ let(:with_context) { false }
+
+ it 'has base results' do
+ results = {
+ title: title,
+ menu_sections: []
+ }
+
+ expect(view_model).to eq(results)
+ end
end
context 'when can create projects in group' do
@@ -199,7 +228,7 @@ RSpec.describe Nav::NewDropdownHelper, feature_category: :navigation do
let(:expected_title) { 'In this group' }
let(:expected_href) { "/groups/#{group.full_path}/-/group_members" }
- it_behaves_like 'invite member item'
+ it_behaves_like 'invite member item', 'groups/invite_members_top_nav_link'
end
end
@@ -219,8 +248,27 @@ RSpec.describe Nav::NewDropdownHelper, feature_category: :navigation do
allow(helper).to receive(:can_admin_project_member?) { with_can_admin_project_member }
end
- it 'has no menu sections' do
- expect(view_model[:menu_sections]).to eq([])
+ it 'has base results with context' do
+ results = {
+ title: title,
+ menu_sections: [],
+ context: project
+ }
+
+ expect(view_model).to eq(results)
+ end
+
+ context 'without context' do
+ let(:with_context) { false }
+
+ it 'has base results without context' do
+ results = {
+ title: title,
+ menu_sections: []
+ }
+
+ expect(view_model).to eq(results)
+ end
end
context 'with show_new_issue_link?' do
@@ -296,7 +344,7 @@ RSpec.describe Nav::NewDropdownHelper, feature_category: :navigation do
let(:expected_title) { 'In this project' }
let(:expected_href) { "/#{project.path_with_namespace}/-/project_members" }
- it_behaves_like 'invite member item'
+ it_behaves_like 'invite member item', 'projects/invite_members_top_nav_link'
end
end
@@ -311,22 +359,36 @@ RSpec.describe Nav::NewDropdownHelper, feature_category: :navigation do
allow(helper).to receive(:can?).with(current_user, :create_projects, group).and_return(true)
end
- it 'gives precedence to group over project' do
- group_section = expected_menu_section(
- title: 'In this group',
+ it 'gives precedence to project over group' do
+ project_section = expected_menu_section(
+ title: 'In this project',
menu_item: ::Gitlab::Nav::TopNavMenuItem.build(
- id: 'new_project',
- title: 'New project/repository',
- href: "/projects/new?namespace_id=#{group.id}",
+ id: 'new_issue',
+ title: 'New issue',
+ href: "/#{project.path_with_namespace}/-/issues/new",
data: {
- track_action: 'click_link_new_project_group',
+ track_action: 'click_link_new_issue',
track_label: 'plus_menu_dropdown',
- track_property: 'navigation_top'
+ track_property: 'navigation_top',
+ qa_selector: 'new_issue_link'
}
)
)
+ results = {
+ title: title,
+ menu_sections: project_section,
+ context: project
+ }
+
+ expect(view_model).to eq(results)
+ end
+
+ context 'without context' do
+ let(:with_context) { false }
- expect(view_model[:menu_sections]).to eq(group_section)
+ it 'does not include context' do
+ expect(view_model.keys).to match_array([:title, :menu_sections])
+ end
end
end
diff --git a/spec/lib/gitlab/nav/top_nav_menu_item_spec.rb b/spec/lib/gitlab/nav/top_nav_menu_item_spec.rb
index 6632a8106ca..3377da5ca3a 100644
--- a/spec/lib/gitlab/nav/top_nav_menu_item_spec.rb
+++ b/spec/lib/gitlab/nav/top_nav_menu_item_spec.rb
@@ -14,7 +14,7 @@ RSpec.describe ::Gitlab::Nav::TopNavMenuItem, feature_category: :navigation do
view: 'view',
css_class: 'css_class',
data: {},
- emoji: 'smile'
+ partial: 'groups/some_view_partial_file'
}
expect(described_class.build(**item)).to eq(item.merge(type: :item))
diff --git a/spec/views/groups/group_members/index.html.haml_spec.rb b/spec/views/groups/group_members/index.html.haml_spec.rb
index 0b3b149238f..fdc6b09d32a 100644
--- a/spec/views/groups/group_members/index.html.haml_spec.rb
+++ b/spec/views/groups/group_members/index.html.haml_spec.rb
@@ -25,7 +25,6 @@ RSpec.describe 'groups/group_members/index', :aggregate_failures, feature_catego
expect(rendered).to have_selector('.js-invite-group-trigger')
expect(rendered).to have_selector('.js-invite-members-trigger')
- expect(response).to render_template(partial: 'groups/_invite_members_modal')
end
end
diff --git a/spec/views/layouts/header/_new_dropdown.haml_spec.rb b/spec/views/layouts/header/_new_dropdown.haml_spec.rb
index 178448022d1..ef8b859c9d9 100644
--- a/spec/views/layouts/header/_new_dropdown.haml_spec.rb
+++ b/spec/views/layouts/header/_new_dropdown.haml_spec.rb
@@ -8,6 +8,7 @@ RSpec.describe 'layouts/header/_new_dropdown', feature_category: :navigation do
shared_examples_for 'invite member selector' do
context 'with ability to invite members' do
it { is_expected.to have_link('Invite members', href: href) }
+ it { is_expected.to have_selector('.js-invite-members-modal') }
end
context 'without ability to invite members' do
@@ -159,6 +160,29 @@ RSpec.describe 'layouts/header/_new_dropdown', feature_category: :navigation do
expect(rendered).to have_link('New snippet', href: new_snippet_path)
end
+ context 'when partial exists in a menu item' do
+ it 'renders the menu item partial without rendering invite modal partial' do
+ view_model = {
+ title: '_title_',
+ menu_sections: [
+ {
+ title: '_section_title_',
+ menu_items: [
+ ::Gitlab::Nav::TopNavMenuItem
+ .build(id: '_id_', title: '_title_', partial: 'groups/invite_members_top_nav_link')
+ ]
+ }
+ ]
+ }
+
+ allow(view).to receive(:new_dropdown_view_model).and_return(view_model)
+
+ render
+
+ expect(response).to render_template(partial: 'groups/_invite_members_top_nav_link')
+ end
+ end
+
context 'when the user is not allowed to do anything' do
let(:user) { create(:user, :external) } # rubocop:disable RSpec/FactoryBot/AvoidCreate
diff --git a/spec/views/projects/empty.html.haml_spec.rb b/spec/views/projects/empty.html.haml_spec.rb
index 6077dda3c98..a782bab161f 100644
--- a/spec/views/projects/empty.html.haml_spec.rb
+++ b/spec/views/projects/empty.html.haml_spec.rb
@@ -73,7 +73,6 @@ RSpec.describe 'projects/empty' do
expect(rendered).to have_content('Invite your team')
expect(rendered).to have_content('Add members to this project and start collaborating with your team.')
expect(rendered).to have_selector('.js-invite-members-trigger')
- expect(rendered).to have_selector('.js-invite-members-modal')
expect(rendered).to have_selector('[data-label=invite_members_empty_project]')
expect(rendered).to have_selector('[data-event=click_button]')
expect(rendered).to have_selector('[data-trigger-source=project-empty-page]')
@@ -87,7 +86,6 @@ RSpec.describe 'projects/empty' do
expect(rendered).not_to have_content('Invite your team')
expect(rendered).not_to have_selector('.js-invite-members-trigger')
- expect(rendered).not_to have_selector('.js-invite-members-modal')
end
end
end
diff --git a/spec/views/projects/project_members/index.html.haml_spec.rb b/spec/views/projects/project_members/index.html.haml_spec.rb
index 4c4cde01cca..2fcc5c6935b 100644
--- a/spec/views/projects/project_members/index.html.haml_spec.rb
+++ b/spec/views/projects/project_members/index.html.haml_spec.rb
@@ -28,7 +28,6 @@ RSpec.describe 'projects/project_members/index', :aggregate_failures, feature_ca
expect(rendered).to have_selector('.js-invite-group-trigger')
expect(rendered).to have_selector('.js-invite-members-trigger')
expect(rendered).not_to have_content('Members can be added by project')
- expect(response).to render_template(partial: 'projects/_invite_members_modal')
end
context 'when project is not allowed to share with group' do
diff --git a/yarn.lock b/yarn.lock
index 79f1fba387d..4da96720728 100644
--- a/yarn.lock
+++ b/yarn.lock
@@ -1347,10 +1347,10 @@
resolved "https://registry.yarnpkg.com/@gitlab/svgs/-/svgs-3.20.0.tgz#4ee4f2f24304d13ccce58f82c2ecd87e556f35b4"
integrity sha512-nYTF4j5kon4XbBr/sAzuubgxjIne9+RTZLmSrSaL9FL4eyuv9aa7YMCcOrlIbYX5jlSYlcD+ck2F2M1sqXXOBA==
-"@gitlab/ui@55.2.1":
- version "55.2.1"
- resolved "https://registry.yarnpkg.com/@gitlab/ui/-/ui-55.2.1.tgz#140d8e2c7428777a745fb1cfb33c29c3f1133113"
- integrity sha512-fIvGzM4nZtHWaUWfaM9PSPJJJFcnvCLCrdAsobiiMlxFFCEwDNm99oL+uzQgbatQRjjIdHqyFKFNMo3rg6U+AQ==
+"@gitlab/ui@56.0.0":
+ version "56.0.0"
+ resolved "https://registry.yarnpkg.com/@gitlab/ui/-/ui-56.0.0.tgz#fd3b144893b01a8eae408ce8ca4477240aeb493e"
+ integrity sha512-kuoI+q5zYmmK9stpb1YJKvJNTD+NXiG6EOCw5+UQDpo5PgeDzrQ4vm7JuocYU9dNZlHMR2vVPMxi3uG0+y5JkA==
dependencies:
"@popperjs/core" "^2.11.2"
bootstrap-vue "2.20.1"