From 0318e8b0a7325f3c79c645b78400968bd1fb75c2 Mon Sep 17 00:00:00 2001 From: Sascha Eggenberger Date: Wed, 10 Sep 2025 16:21:27 +0200 Subject: [PATCH 1/5] Update super sidebar alignments --- .../super_sidebar/components/help_center.vue | 11 ++++++++--- .../super_sidebar/components/menu_section.vue | 6 +++--- .../javascripts/super_sidebar/components/nav_item.vue | 4 ++-- .../super_sidebar/components/super_sidebar.vue | 2 +- .../super_sidebar/components/super_topbar.vue | 2 +- .../stylesheets/framework/application-chrome.scss | 2 +- 6 files changed, 16 insertions(+), 11 deletions(-) diff --git a/app/assets/javascripts/super_sidebar/components/help_center.vue b/app/assets/javascripts/super_sidebar/components/help_center.vue index 61919a7bdd0ba8..c0d46939a51421 100644 --- a/app/assets/javascripts/super_sidebar/components/help_center.vue +++ b/app/assets/javascripts/super_sidebar/components/help_center.vue @@ -236,7 +236,7 @@ export default {
{{ $options.i18n.whatsnew }} -
diff --git a/app/assets/javascripts/super_sidebar/components/super_topbar.vue b/app/assets/javascripts/super_sidebar/components/super_topbar.vue index 79ddcc921d35bf..cf8c558366dee9 100644 --- a/app/assets/javascripts/super_sidebar/components/super_topbar.vue +++ b/app/assets/javascripts/super_sidebar/components/super_topbar.vue @@ -66,7 +66,7 @@ export default { class="super-topbar gl-grid gl-w-full gl-grid-cols-[1fr_auto_1fr] gl-items-center gl-gap-4" >
-
+
Date: Thu, 11 Sep 2025 13:13:49 +0200 Subject: [PATCH 2/5] Rebase and adjust spacings --- .../javascripts/super_sidebar/components/help_center.vue | 5 +++-- .../super_sidebar/components/icon_only_toggle.vue | 8 ++++++-- .../javascripts/super_sidebar/components/menu_section.vue | 4 ++-- .../javascripts/super_sidebar/components/nav_item.vue | 4 ++-- app/assets/stylesheets/framework/application-chrome.scss | 5 ----- app/assets/stylesheets/framework/super_sidebar.scss | 6 +++++- 6 files changed, 18 insertions(+), 14 deletions(-) diff --git a/app/assets/javascripts/super_sidebar/components/help_center.vue b/app/assets/javascripts/super_sidebar/components/help_center.vue index c0d46939a51421..1a2ddefbc00002 100644 --- a/app/assets/javascripts/super_sidebar/components/help_center.vue +++ b/app/assets/javascripts/super_sidebar/components/help_center.vue @@ -236,7 +236,7 @@ export default {
@@ -274,7 +275,7 @@ export default { diff --git a/app/assets/javascripts/super_sidebar/components/icon_only_toggle.vue b/app/assets/javascripts/super_sidebar/components/icon_only_toggle.vue index 43bc28c608efde..4fbb3e8dfd916b 100644 --- a/app/assets/javascripts/super_sidebar/components/icon_only_toggle.vue +++ b/app/assets/javascripts/super_sidebar/components/icon_only_toggle.vue @@ -35,11 +35,15 @@ export default { > {{ isIconOnly ? '' : text }}{{ text }} diff --git a/app/assets/javascripts/super_sidebar/components/menu_section.vue b/app/assets/javascripts/super_sidebar/components/menu_section.vue index 640828a8ef2fbc..131cf6be26b9ac 100644 --- a/app/assets/javascripts/super_sidebar/components/menu_section.vue +++ b/app/assets/javascripts/super_sidebar/components/menu_section.vue @@ -141,7 +141,7 @@ export default {
diff --git a/app/assets/stylesheets/framework/application-chrome.scss b/app/assets/stylesheets/framework/application-chrome.scss index 0ffc562c6ba15a..e73fef3d269d89 100644 --- a/app/assets/stylesheets/framework/application-chrome.scss +++ b/app/assets/stylesheets/framework/application-chrome.scss @@ -70,11 +70,6 @@ // Super sidebar active item .super-sidebar-nav-item { @apply gl-rounded-lg #{!important}; - - // Hide indicator - .active-indicator { - display: none; - } } // Search button diff --git a/app/assets/stylesheets/framework/super_sidebar.scss b/app/assets/stylesheets/framework/super_sidebar.scss index b34126c14cab80..4655e48a1c81b6 100644 --- a/app/assets/stylesheets/framework/super_sidebar.scss +++ b/app/assets/stylesheets/framework/super_sidebar.scss @@ -347,6 +347,10 @@ $command-palette-spacing: px-to-rem(14px); background-color: var(--super-sidebar-nav-item-hover-bg); } + &:has(.gl-button-text.gl-hidden) .gl-button-icon { + @apply gl-mr-0 #{!important}; + } + .badge { font-size: 10px; @apply gl-font-semibold; @@ -393,7 +397,7 @@ $command-palette-spacing: px-to-rem(14px); } .active-indicator { - background-color: var(--super-sidebar-accent-color-fg); + display: none; } .super-sidebar-help-center-toggle[aria-expanded='true'] { -- GitLab From fa3fe95d03d5513b76d1c8b7c85fa347fd8f1fee Mon Sep 17 00:00:00 2001 From: Sascha Eggenberger Date: Thu, 11 Sep 2025 13:36:41 +0200 Subject: [PATCH 3/5] Remove active indicator --- .../super_sidebar/components/menu_section.vue | 6 -- .../super_sidebar/components/nav_item.vue | 26 +------ .../stylesheets/framework/super_sidebar.scss | 4 -- .../duo_agents_platform/router/dom_utils.js | 10 --- .../router/dom_utils_spec.js | 70 ++----------------- .../super_sidebar/components/nav_item_spec.js | 40 ++--------- 6 files changed, 13 insertions(+), 143 deletions(-) diff --git a/app/assets/javascripts/super_sidebar/components/menu_section.vue b/app/assets/javascripts/super_sidebar/components/menu_section.vue index 131cf6be26b9ac..97b157a5c909f3 100644 --- a/app/assets/javascripts/super_sidebar/components/menu_section.vue +++ b/app/assets/javascripts/super_sidebar/components/menu_section.vue @@ -150,12 +150,6 @@ export default { @pointerover="handlePointerover" @pointerleave="handlePointerleave" > - -
{ } const activeClass = 'super-sidebar-nav-item-current'; - const activeIndicatorClass = 'active-indicator'; const currentActiveNavItems = el.querySelectorAll(`.${activeClass}`); - const currentActiveIndicators = el.querySelectorAll(`.${activeIndicatorClass}`); if (currentActiveNavItems.length) { currentActiveNavItems.forEach((foundEl) => foundEl.classList.remove(activeClass)); } - if (currentActiveIndicators.length) { - currentActiveIndicators.forEach((foundEl) => foundEl.classList.add('gl-hidden')); - } - const newActiveNavItems = el.querySelectorAll(`[href*="${href}"]`); if (newActiveNavItems) { newActiveNavItems.forEach((foundEl) => { foundEl.classList.add(activeClass); - const newIndicator = foundEl.querySelector(`.${activeIndicatorClass}`); - if (newIndicator) { - newIndicator.classList.remove('gl-hidden'); - } }); } }; diff --git a/ee/spec/frontend/ai/duo_agents_platform/router/dom_utils_spec.js b/ee/spec/frontend/ai/duo_agents_platform/router/dom_utils_spec.js index c0560d7ca497f4..fe38cf0fb24468 100644 --- a/ee/spec/frontend/ai/duo_agents_platform/router/dom_utils_spec.js +++ b/ee/spec/frontend/ai/duo_agents_platform/router/dom_utils_spec.js @@ -3,7 +3,6 @@ import { updateActiveNavigation } from 'ee/ai/duo_agents_platform/router/dom_uti // Test constants const CSS_CLASSES = { activeClass: 'super-sidebar-nav-item-current', - activeIndicatorClass: 'active-indicator', hiddenClass: 'gl-hidden', }; @@ -22,11 +21,6 @@ const createMockElement = (methods = {}) => ({ ...methods, }); -const createMockNavItem = (indicator = null) => ({ - ...createMockElement(), - querySelector: jest.fn().mockReturnValue(indicator), -}); - const createMockSuperSidebar = (queryResults = {}) => ({ querySelectorAll: jest.fn().mockImplementation((selector) => { return queryResults[selector] || []; @@ -38,11 +32,10 @@ describe('updateActiveNavigation', () => { let mockElements; const setupMockSuperSidebar = (config = {}) => { - const { activeNavItems = [], activeIndicators = [], newNavItems = [] } = config; + const { activeNavItems = [], newNavItems = [] } = config; const queryResults = { [`.${CSS_CLASSES.activeClass}`]: activeNavItems, - [`.${CSS_CLASSES.activeIndicatorClass}`]: activeIndicators, }; // Add dynamic href-based queries @@ -72,14 +65,9 @@ describe('updateActiveNavigation', () => { // Create reusable mock elements mockElements = { activeNavItems: [createMockElement(), createMockElement()], - activeIndicators: [createMockElement(), createMockElement()], - newIndicators: [createMockElement(), createMockElement()], }; - mockElements.newNavItems = [ - createMockNavItem(mockElements.newIndicators[0]), - createMockNavItem(mockElements.newIndicators[1]), - ]; + mockElements.newNavItems = [createMockElement()]; mockSuperSidebar = createMockSuperSidebar(); setupDocumentMock(); @@ -93,7 +81,6 @@ describe('updateActiveNavigation', () => { beforeEach(() => { setupMockSuperSidebar({ activeNavItems: mockElements.activeNavItems, - activeIndicators: mockElements.activeIndicators, newNavItems: mockElements.newNavItems, }); }); @@ -106,14 +93,6 @@ describe('updateActiveNavigation', () => { }); }); - it('hides current active indicators', () => { - updateActiveNavigation('/test-href'); - - mockElements.activeIndicators.forEach((indicator) => { - expect(indicator.classList.add).toHaveBeenCalledWith(CSS_CLASSES.hiddenClass); - }); - }); - it('adds active class to new nav items matching href', () => { updateActiveNavigation('/test-href'); @@ -123,17 +102,6 @@ describe('updateActiveNavigation', () => { }); }); - it('shows indicators for new active nav items', () => { - updateActiveNavigation('/test-href'); - - mockElements.newNavItems.forEach((item, index) => { - expect(item.querySelector).toHaveBeenCalledWith(`.${CSS_CLASSES.activeIndicatorClass}`); - expect(mockElements.newIndicators[index].classList.remove).toHaveBeenCalledWith( - CSS_CLASSES.hiddenClass, - ); - }); - }); - it('handles href with special characters', () => { const specialHref = '/agents/test-agent-123'; updateActiveNavigation(specialHref); @@ -145,7 +113,6 @@ describe('updateActiveNavigation', () => { beforeEach(() => { setupMockSuperSidebar({ activeNavItems: [], - activeIndicators: [], newNavItems: [mockElements.newNavItems[0]], }); }); @@ -167,42 +134,20 @@ describe('updateActiveNavigation', () => { }); }); - describe('when no current active indicators exist', () => { - beforeEach(() => { - setupMockSuperSidebar({ - activeNavItems: [mockElements.activeNavItems[0]], - activeIndicators: [], - newNavItems: [mockElements.newNavItems[0]], - }); - }); - - it('does not attempt to hide non-existent indicators', () => { - updateActiveNavigation('/test-href'); - - mockElements.activeIndicators.forEach((indicator) => { - expect(indicator.classList.add).not.toHaveBeenCalled(); - }); - }); - }); - describe('when no new nav items match the href', () => { beforeEach(() => { setupMockSuperSidebar({ activeNavItems: [mockElements.activeNavItems[0]], - activeIndicators: [mockElements.activeIndicators[0]], newNavItems: [], }); }); - it('still removes current active classes and hides indicators', () => { + it('still removes current active classes', () => { updateActiveNavigation('/non-matching-href'); expect(mockElements.activeNavItems[0].classList.remove).toHaveBeenCalledWith( CSS_CLASSES.activeClass, ); - expect(mockElements.activeIndicators[0].classList.add).toHaveBeenCalledWith( - CSS_CLASSES.hiddenClass, - ); }); it('does not attempt to add classes to non-existent new nav items', () => { @@ -214,17 +159,16 @@ describe('updateActiveNavigation', () => { }); }); - describe('when new nav items exist but have no indicators', () => { + describe('when new nav items exist', () => { beforeEach(() => { - const navItemWithoutIndicator = createMockNavItem(null); + const navItems = createMockElement(); setupMockSuperSidebar({ activeNavItems: [], - activeIndicators: [], - newNavItems: [navItemWithoutIndicator], + newNavItems: [navItems], }); }); - it('adds active class to nav items but does not attempt to show indicators', () => { + it('adds active class to nav items', () => { expect(() => updateActiveNavigation('/test-href')).not.toThrow(); }); }); diff --git a/spec/frontend/super_sidebar/components/nav_item_spec.js b/spec/frontend/super_sidebar/components/nav_item_spec.js index 40580f0fbad708..449bd31bb4cdc2 100644 --- a/spec/frontend/super_sidebar/components/nav_item_spec.js +++ b/spec/frontend/super_sidebar/components/nav_item_spec.js @@ -3,9 +3,9 @@ import { GlBadge, GlButton, GlAvatar } from '@gitlab/ui'; import { RouterLinkStub } from '@vue/test-utils'; import { mountExtended, extendedWrapper } from 'helpers/vue_test_utils_helper'; import NavItem from '~/super_sidebar/components/nav_item.vue'; -import NavItemRouterLink from '~/super_sidebar/components/nav_item_router_link.vue'; import NavItemLink from '~/super_sidebar/components/nav_item_link.vue'; import { + NAV_ITEM_LINK_ACTIVE_CLASS, CLICK_MENU_ITEM_ACTION, TRACKING_UNKNOWN_ID, TRACKING_UNKNOWN_PANEL, @@ -19,7 +19,6 @@ describe('NavItem component', () => { const findLink = () => wrapper.findByTestId('nav-item-link'); const findPill = () => wrapper.findComponent(GlBadge); const findPinButton = () => wrapper.findComponent(GlButton); - const findNavItemRouterLink = () => extendedWrapper(wrapper.findComponent(NavItemRouterLink)); const findNavItemLink = () => extendedWrapper(wrapper.findComponent(NavItemLink)); const createWrapper = ({ item, props = {}, provide = {}, routerLinkSlotProps = {} }) => { @@ -298,49 +297,20 @@ describe('NavItem component', () => { ); }); - describe('when `item` prop has `to` attribute', () => { - describe('when `RouterLink` is not active', () => { - it('renders `NavItemRouterLink` with active indicator hidden', () => { - createWrapper({ item: { title: 'Foo', to: { name: 'foo' } } }); - - expect(findNavItemRouterLink().findByTestId('active-indicator').classes()).toContain( - 'gl-opacity-0', - ); - }); - }); - - describe('when `RouterLink` is active', () => { - it('renders `NavItemRouterLink` with active indicator shown', () => { - createWrapper({ - item: { title: 'Foo', to: { name: 'foo' } }, - routerLinkSlotProps: { isActive: true }, - }); - - expect(findNavItemRouterLink().findByTestId('active-indicator').classes()).toContain( - 'gl-opacity-10', - ); - }); - }); - }); - describe('when `item` prop has `link` attribute', () => { describe('when `item` has `is_active` set to `false`', () => { - it('renders `NavItemLink` with active indicator hidden', () => { + it('renders `NavItemLink` with no active class', () => { createWrapper({ item: { title: 'Foo', link: '/foo', is_active: false } }); - expect(findNavItemLink().findByTestId('active-indicator').classes()).toContain( - 'gl-opacity-0', - ); + expect(findNavItemLink().classes()).not.toContain(NAV_ITEM_LINK_ACTIVE_CLASS); }); }); describe('when `item` has `is_active` set to `true`', () => { - it('renders `NavItemLink` with active indicator shown', () => { + it('renders `NavItemLink` with active class', () => { createWrapper({ item: { title: 'Foo', link: '/foo', is_active: true } }); - expect(findNavItemLink().findByTestId('active-indicator').classes()).toContain( - 'gl-opacity-10', - ); + expect(findNavItemLink().classes()).toContain(NAV_ITEM_LINK_ACTIVE_CLASS); }); }); }); -- GitLab From 208527a1faa7ccb85729140d206856992c4cb6d6 Mon Sep 17 00:00:00 2001 From: Sascha Eggenberger Date: Thu, 11 Sep 2025 15:51:17 +0200 Subject: [PATCH 4/5] Fix spec --- .../frontend/super_sidebar/components/icon_only_toggle_spec.js | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/spec/frontend/super_sidebar/components/icon_only_toggle_spec.js b/spec/frontend/super_sidebar/components/icon_only_toggle_spec.js index 4f2e3b6d1713e7..9a2f4bb8aacdc2 100644 --- a/spec/frontend/super_sidebar/components/icon_only_toggle_spec.js +++ b/spec/frontend/super_sidebar/components/icon_only_toggle_spec.js @@ -48,7 +48,8 @@ describe('IconOnlyToggle', () => { }); it('does not display text content', () => { - expect(findButton().text()).toBe(''); + expect(findButton().text()).toBe('Expand sidebar'); + expect(findButton().props('buttonTextClasses')).toBe('gl-hidden'); }); it('shows tooltip with expand text', () => { -- GitLab From 22b110128374c581ff74ac0dcef9f4e8cfcdc906 Mon Sep 17 00:00:00 2001 From: Sascha Eggenberger Date: Fri, 12 Sep 2025 13:18:51 +0200 Subject: [PATCH 5/5] Set overflow-x to hidden, custom scrollbar style --- .../javascripts/super_sidebar/components/scroll_scrim.vue | 5 ++++- app/assets/stylesheets/framework/application-chrome.scss | 8 +++++++- 2 files changed, 11 insertions(+), 2 deletions(-) diff --git a/app/assets/javascripts/super_sidebar/components/scroll_scrim.vue b/app/assets/javascripts/super_sidebar/components/scroll_scrim.vue index db45edd4f5e1df..723314ebd029e5 100644 --- a/app/assets/javascripts/super_sidebar/components/scroll_scrim.vue +++ b/app/assets/javascripts/super_sidebar/components/scroll_scrim.vue @@ -56,7 +56,10 @@ export default {