From 0fa500eae5f64fd24c636c1822111df938aa3290 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?M=C3=A1rcio?= Date: Mon, 23 Mar 2026 13:22:08 +0100 Subject: [PATCH 01/13] Update CardStyles.tsx --- src/components/card/CardStyles.tsx | 6 ------ 1 file changed, 6 deletions(-) diff --git a/src/components/card/CardStyles.tsx b/src/components/card/CardStyles.tsx index 5229ca39..2f52aae0 100644 --- a/src/components/card/CardStyles.tsx +++ b/src/components/card/CardStyles.tsx @@ -109,32 +109,26 @@ CardBodyStyled.displayName = 'CardBodyStyled'; export const CARD_HEADER_SIZE_STYLES = { [CardSize.Large]: { '--card-header-heading-height': '1.875rem', - '--card-header-min-height': '58px', '--card-header-padding': `${cssVar('dimension-space-200')} ${cssVar('dimension-space-300')}`, }, [CardSize.Medium]: { '--card-header-heading-height': '1.5rem', - '--card-header-min-height': '45px', '--card-header-padding': `${cssVar('dimension-space-150')} ${cssVar('dimension-space-200')}`, }, [CardSize.Small]: { '--card-header-heading-height': '1.25rem', - '--card-header-min-height': '36px', '--card-header-padding': `${cssVar('dimension-space-100')} ${cssVar('dimension-space-150')}`, }, }; export const CARD_SIZE_STYLES = { [CardSize.Large]: { - '--card-body-min-height': '108px', '--card-padding': cssVar('dimension-space-300'), }, [CardSize.Medium]: { - '--card-body-min-height': '92px', '--card-padding': cssVar('dimension-space-200'), }, [CardSize.Small]: { - '--card-body-min-height': '84px', '--card-padding': cssVar('dimension-space-150'), }, }; From 53e462c97f43867641abcda33b009fda23268283 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?M=C3=A1rcio?= Date: Tue, 31 Mar 2026 13:23:34 +0200 Subject: [PATCH 02/13] Changes to sidebar header structure and styling --- .../sidebar-navigation/SidebarNavigation.tsx | 1 - .../SidebarNavigationHeader.tsx | 74 ++++++++++++------- 2 files changed, 47 insertions(+), 28 deletions(-) diff --git a/src/components/layout/sidebar-navigation/SidebarNavigation.tsx b/src/components/layout/sidebar-navigation/SidebarNavigation.tsx index 3da4c224..1c54a571 100644 --- a/src/components/layout/sidebar-navigation/SidebarNavigation.tsx +++ b/src/components/layout/sidebar-navigation/SidebarNavigation.tsx @@ -89,7 +89,6 @@ const SidebarNavigationWrapper = styled.nav` box-sizing: content-box; overflow: hidden; - padding-top: ${cssVar('dimension-space-100')}; border-right: ${cssVar('border-width-default')} solid ${cssVar('color-border-weak')}; background-color: ${cssVar('color-surface-default')}; diff --git a/src/components/layout/sidebar-navigation/SidebarNavigationHeader.tsx b/src/components/layout/sidebar-navigation/SidebarNavigationHeader.tsx index 3f714db0..a83f917f 100644 --- a/src/components/layout/sidebar-navigation/SidebarNavigationHeader.tsx +++ b/src/components/layout/sidebar-navigation/SidebarNavigationHeader.tsx @@ -56,47 +56,65 @@ export const SidebarNavigationHeader = forwardRef - - - {avatar && {avatar}} - - - {name} - - {qualifier && ( - - {qualifier} - - )} - - - {isInteractive && } - - + + + + + + {avatar && {avatar}} + + + {name} + + {qualifier && ( + + {qualifier} + + )} + + + {isInteractive && } + + + + ); }, ); SidebarNavigationHeader.displayName = 'SidebarNavigationHeader'; +const HeaderWrapper = styled.div` + padding: ${cssVar('dimension-space-100')}; + border-bottom: ${cssVar('border-width-default')} solid ${cssVar('color-border-weak')}; + + [data-sidebar-docked='false'] nav:not(:hover, :focus-within) & { + padding-inline: ${cssVar('dimension-space-50')}; + } +`; +HeaderWrapper.displayName = 'HeaderWrapper'; + +const HeaderInnerRow = styled.div` + display: flex; + align-items: center; + gap: ${cssVar('dimension-space-50')}; + border-radius: ${cssVar('border-radius-400')}; + width: 100%; +`; +HeaderInnerRow.displayName = 'HeaderInnerRow'; + const HeaderContainer = styled.button` all: unset; display: flex; align-items: center; justify-content: space-between; - flex-shrink: 0; + flex: 1 0 0; gap: ${cssVar('dimension-space-100')}; padding: ${cssVar('dimension-space-100')}; - margin: ${cssVar('dimension-space-100')}; overflow: hidden; - [data-sidebar-docked='false'] nav:not(:hover, :focus-within) & { - margin: ${cssVar('dimension-space-100')} ${cssVar('dimension-space-50')}; - } - /* If it is interactive, we want mouse interactivity */ :is(button) { border-radius: ${cssVar('border-radius-400')}; @@ -129,14 +147,16 @@ const MainContent = styled.div` display: flex; align-items: center; gap: ${cssVar('dimension-space-100')}; - min-width: ${cssVar('dimension-width-300')}; + min-width: ${cssVar('dimension-width-400')}; `; MainContent.displayName = 'MainContent'; const AvatarWrapper = styled.div` flex: 1 0 auto; - width: ${cssVar('dimension-width-300')}; - height: ${cssVar('dimension-width-300')}; + width: ${cssVar('dimension-width-400')}; + height: ${cssVar('dimension-width-400')}; + border: ${cssVar('border-width-default')} solid ${cssVar('color-border-weak')}; + border-radius: 50%; `; AvatarWrapper.displayName = 'AvatarWrapper'; From 614492ea0617f340e4be48934d6f05a66fccd10b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?M=C3=A1rcio?= Date: Tue, 31 Mar 2026 13:32:43 +0200 Subject: [PATCH 03/13] Update SidebarNavigationHeader.tsx --- .../layout/sidebar-navigation/SidebarNavigationHeader.tsx | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/src/components/layout/sidebar-navigation/SidebarNavigationHeader.tsx b/src/components/layout/sidebar-navigation/SidebarNavigationHeader.tsx index a83f917f..bd117363 100644 --- a/src/components/layout/sidebar-navigation/SidebarNavigationHeader.tsx +++ b/src/components/layout/sidebar-navigation/SidebarNavigationHeader.tsx @@ -58,7 +58,9 @@ export const SidebarNavigationHeader = forwardRef - + {avatar && {avatar}} From 154b9442c0b9ba02461af3898ac7b987ebff91ad Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?M=C3=A1rcio?= Date: Tue, 31 Mar 2026 13:41:16 +0200 Subject: [PATCH 04/13] Remove border and border radius --- .../layout/sidebar-navigation/SidebarNavigationHeader.tsx | 2 -- 1 file changed, 2 deletions(-) diff --git a/src/components/layout/sidebar-navigation/SidebarNavigationHeader.tsx b/src/components/layout/sidebar-navigation/SidebarNavigationHeader.tsx index bd117363..5aee3772 100644 --- a/src/components/layout/sidebar-navigation/SidebarNavigationHeader.tsx +++ b/src/components/layout/sidebar-navigation/SidebarNavigationHeader.tsx @@ -157,8 +157,6 @@ const AvatarWrapper = styled.div` flex: 1 0 auto; width: ${cssVar('dimension-width-400')}; height: ${cssVar('dimension-width-400')}; - border: ${cssVar('border-width-default')} solid ${cssVar('color-border-weak')}; - border-radius: 50%; `; AvatarWrapper.displayName = 'AvatarWrapper'; From 2b6f5e3f643f67000760a00dbc8d8c3cf766269a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?M=C3=A1rcio?= Date: Tue, 31 Mar 2026 14:27:48 +0200 Subject: [PATCH 05/13] Add controll to the accordion --- .../sidebar-navigation/SidebarNavigationAccordionItem.tsx | 7 ++++++- .../layout/sidebar-navigation/SidebarNavigationHeader.tsx | 6 +++--- 2 files changed, 9 insertions(+), 4 deletions(-) diff --git a/src/components/layout/sidebar-navigation/SidebarNavigationAccordionItem.tsx b/src/components/layout/sidebar-navigation/SidebarNavigationAccordionItem.tsx index d43b21b2..d61100a4 100644 --- a/src/components/layout/sidebar-navigation/SidebarNavigationAccordionItem.tsx +++ b/src/components/layout/sidebar-navigation/SidebarNavigationAccordionItem.tsx @@ -66,6 +66,10 @@ export interface SidebarNavigationAccordionItemProps { * The onOpen callback is called when the accordion is opened. */ onOpen?: VoidFunction; + /** + * Whether the accordion is open by default. Defaults to true. + */ + defaultOpen?: boolean; /** * When true, scrolls the last child item into view when the accordion opens. * Useful when the accordion is near the bottom of a scrollable container. @@ -88,6 +92,7 @@ export const SidebarNavigationAccordionItem = forwardRef< >((props, ref) => { const { children, + defaultOpen = true, disableTooltip = false, Icon, label, @@ -98,7 +103,7 @@ export const SidebarNavigationAccordionItem = forwardRef< ...htmlProps } = props; - const [open, setOpen] = useState(false); + const [open, setOpen] = useState(defaultOpen); const panelRef = useRef(null); useEffect(() => { diff --git a/src/components/layout/sidebar-navigation/SidebarNavigationHeader.tsx b/src/components/layout/sidebar-navigation/SidebarNavigationHeader.tsx index 5aee3772..222c8e1f 100644 --- a/src/components/layout/sidebar-navigation/SidebarNavigationHeader.tsx +++ b/src/components/layout/sidebar-navigation/SidebarNavigationHeader.tsx @@ -98,7 +98,7 @@ HeaderWrapper.displayName = 'HeaderWrapper'; const HeaderInnerRow = styled.div` display: flex; align-items: center; - gap: ${cssVar('dimension-space-50')}; + gap: ${cssVar('dimension-space-100')}; border-radius: ${cssVar('border-radius-400')}; width: 100%; `; @@ -155,8 +155,8 @@ MainContent.displayName = 'MainContent'; const AvatarWrapper = styled.div` flex: 1 0 auto; - width: ${cssVar('dimension-width-400')}; - height: ${cssVar('dimension-width-400')}; + width: ${cssVar('dimension-width-300')}; + height: ${cssVar('dimension-width-300')}; `; AvatarWrapper.displayName = 'AvatarWrapper'; From dadf049920436c0e58726fc61c057d7b91a1da5e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?M=C3=A1rcio?= Date: Tue, 31 Mar 2026 14:38:31 +0200 Subject: [PATCH 06/13] added tests --- .../sidebar-navigation/SidebarNavigationAccordionItem.tsx | 7 ++++++- .../layout/sidebar-navigation/SidebarNavigationHeader.tsx | 6 +++--- .../__tests__/SidebarNavigationAccordionItem-test.tsx | 7 +++++-- 3 files changed, 14 insertions(+), 6 deletions(-) diff --git a/src/components/layout/sidebar-navigation/SidebarNavigationAccordionItem.tsx b/src/components/layout/sidebar-navigation/SidebarNavigationAccordionItem.tsx index d43b21b2..d61100a4 100644 --- a/src/components/layout/sidebar-navigation/SidebarNavigationAccordionItem.tsx +++ b/src/components/layout/sidebar-navigation/SidebarNavigationAccordionItem.tsx @@ -66,6 +66,10 @@ export interface SidebarNavigationAccordionItemProps { * The onOpen callback is called when the accordion is opened. */ onOpen?: VoidFunction; + /** + * Whether the accordion is open by default. Defaults to true. + */ + defaultOpen?: boolean; /** * When true, scrolls the last child item into view when the accordion opens. * Useful when the accordion is near the bottom of a scrollable container. @@ -88,6 +92,7 @@ export const SidebarNavigationAccordionItem = forwardRef< >((props, ref) => { const { children, + defaultOpen = true, disableTooltip = false, Icon, label, @@ -98,7 +103,7 @@ export const SidebarNavigationAccordionItem = forwardRef< ...htmlProps } = props; - const [open, setOpen] = useState(false); + const [open, setOpen] = useState(defaultOpen); const panelRef = useRef(null); useEffect(() => { diff --git a/src/components/layout/sidebar-navigation/SidebarNavigationHeader.tsx b/src/components/layout/sidebar-navigation/SidebarNavigationHeader.tsx index 5aee3772..222c8e1f 100644 --- a/src/components/layout/sidebar-navigation/SidebarNavigationHeader.tsx +++ b/src/components/layout/sidebar-navigation/SidebarNavigationHeader.tsx @@ -98,7 +98,7 @@ HeaderWrapper.displayName = 'HeaderWrapper'; const HeaderInnerRow = styled.div` display: flex; align-items: center; - gap: ${cssVar('dimension-space-50')}; + gap: ${cssVar('dimension-space-100')}; border-radius: ${cssVar('border-radius-400')}; width: 100%; `; @@ -155,8 +155,8 @@ MainContent.displayName = 'MainContent'; const AvatarWrapper = styled.div` flex: 1 0 auto; - width: ${cssVar('dimension-width-400')}; - height: ${cssVar('dimension-width-400')}; + width: ${cssVar('dimension-width-300')}; + height: ${cssVar('dimension-width-300')}; `; AvatarWrapper.displayName = 'AvatarWrapper'; diff --git a/src/components/layout/sidebar-navigation/__tests__/SidebarNavigationAccordionItem-test.tsx b/src/components/layout/sidebar-navigation/__tests__/SidebarNavigationAccordionItem-test.tsx index 515a5b91..6bcf3f83 100644 --- a/src/components/layout/sidebar-navigation/__tests__/SidebarNavigationAccordionItem-test.tsx +++ b/src/components/layout/sidebar-navigation/__tests__/SidebarNavigationAccordionItem-test.tsx @@ -33,7 +33,7 @@ expect.extend(matchers); it('should expand hidden elements when clicked', async () => { const onOpen = jest.fn(); const onClose = jest.fn(); - const { user } = setupSidebarNavigationAccordionItem({ onOpen, onClose }); + const { user } = setupSidebarNavigationAccordionItem({ onOpen, onClose, defaultOpen: false }); const accordionButton = screen.getByRole('button', { name: 'Accordion Item' }); expect(accordionButton).toBeInTheDocument(); @@ -83,7 +83,10 @@ it('should scroll the last child into view when opened with scrollLastChildIntoV const scrollIntoView = jest.fn(); globalThis.HTMLElement.prototype.scrollIntoView = scrollIntoView; - const { user } = setupSidebarNavigationAccordionItem({ scrollLastChildIntoViewOnOpen: true }); + const { user } = setupSidebarNavigationAccordionItem({ + defaultOpen: false, + scrollLastChildIntoViewOnOpen: true, + }); await user.click(screen.getByRole('button', { name: 'Accordion Item' })); From 67974e76acda74982475d3893be7600b3e399d46 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?M=C3=A1rcio?= Date: Thu, 2 Apr 2026 11:24:40 +0200 Subject: [PATCH 07/13] Revert "Update CardStyles.tsx" This reverts commit 0fa500eae5f64fd24c636c1822111df938aa3290. --- src/components/card/CardStyles.tsx | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/src/components/card/CardStyles.tsx b/src/components/card/CardStyles.tsx index 2f52aae0..5229ca39 100644 --- a/src/components/card/CardStyles.tsx +++ b/src/components/card/CardStyles.tsx @@ -109,26 +109,32 @@ CardBodyStyled.displayName = 'CardBodyStyled'; export const CARD_HEADER_SIZE_STYLES = { [CardSize.Large]: { '--card-header-heading-height': '1.875rem', + '--card-header-min-height': '58px', '--card-header-padding': `${cssVar('dimension-space-200')} ${cssVar('dimension-space-300')}`, }, [CardSize.Medium]: { '--card-header-heading-height': '1.5rem', + '--card-header-min-height': '45px', '--card-header-padding': `${cssVar('dimension-space-150')} ${cssVar('dimension-space-200')}`, }, [CardSize.Small]: { '--card-header-heading-height': '1.25rem', + '--card-header-min-height': '36px', '--card-header-padding': `${cssVar('dimension-space-100')} ${cssVar('dimension-space-150')}`, }, }; export const CARD_SIZE_STYLES = { [CardSize.Large]: { + '--card-body-min-height': '108px', '--card-padding': cssVar('dimension-space-300'), }, [CardSize.Medium]: { + '--card-body-min-height': '92px', '--card-padding': cssVar('dimension-space-200'), }, [CardSize.Small]: { + '--card-body-min-height': '84px', '--card-padding': cssVar('dimension-space-150'), }, }; From 01915e2b643a03f4c2e7d9832e83b71f98bfb90d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?M=C3=A1rcio?= Date: Thu, 2 Apr 2026 11:43:07 +0200 Subject: [PATCH 08/13] Update src/components/layout/sidebar-navigation/SidebarNavigationAccordionItem.tsx Co-authored-by: sonar-review-alpha[bot] <266116024+sonar-review-alpha[bot]@users.noreply.github.com> --- .../sidebar-navigation/SidebarNavigationAccordionItem.tsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/components/layout/sidebar-navigation/SidebarNavigationAccordionItem.tsx b/src/components/layout/sidebar-navigation/SidebarNavigationAccordionItem.tsx index d61100a4..402ea932 100644 --- a/src/components/layout/sidebar-navigation/SidebarNavigationAccordionItem.tsx +++ b/src/components/layout/sidebar-navigation/SidebarNavigationAccordionItem.tsx @@ -92,7 +92,7 @@ export const SidebarNavigationAccordionItem = forwardRef< >((props, ref) => { const { children, - defaultOpen = true, + defaultOpen = false, disableTooltip = false, Icon, label, From 74952327408e417634a9e80a9d9cb3b5b44023bb Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?M=C3=A1rcio?= Date: Thu, 2 Apr 2026 11:53:44 +0200 Subject: [PATCH 09/13] Update SidebarNavigationAccordionItem-test.tsx --- .../SidebarNavigationAccordionItem-test.tsx | 15 ++++++--------- 1 file changed, 6 insertions(+), 9 deletions(-) diff --git a/src/components/layout/sidebar-navigation/__tests__/SidebarNavigationAccordionItem-test.tsx b/src/components/layout/sidebar-navigation/__tests__/SidebarNavigationAccordionItem-test.tsx index 6bcf3f83..c495ac04 100644 --- a/src/components/layout/sidebar-navigation/__tests__/SidebarNavigationAccordionItem-test.tsx +++ b/src/components/layout/sidebar-navigation/__tests__/SidebarNavigationAccordionItem-test.tsx @@ -30,10 +30,14 @@ import { SidebarNavigationItem } from '../SidebarNavigationItem'; expect.extend(matchers); +jest.mock('../utils', () => ({ + TOOLTIP_DELAY_IN_MS: 0, +})); + it('should expand hidden elements when clicked', async () => { const onOpen = jest.fn(); const onClose = jest.fn(); - const { user } = setupSidebarNavigationAccordionItem({ onOpen, onClose, defaultOpen: false }); + const { user } = setupSidebarNavigationAccordionItem({ onOpen, onClose }); const accordionButton = screen.getByRole('button', { name: 'Accordion Item' }); expect(accordionButton).toBeInTheDocument(); @@ -58,10 +62,6 @@ it("shouldn't have any a11y violation", async () => { }); describe('ellipsis behavior', () => { - beforeEach(() => { - jest.clearAllMocks(); - }); - it('should show tooltip by default', async () => { const { user } = setupSidebarNavigationAccordionItem(); @@ -83,10 +83,7 @@ it('should scroll the last child into view when opened with scrollLastChildIntoV const scrollIntoView = jest.fn(); globalThis.HTMLElement.prototype.scrollIntoView = scrollIntoView; - const { user } = setupSidebarNavigationAccordionItem({ - defaultOpen: false, - scrollLastChildIntoViewOnOpen: true, - }); + const { user } = setupSidebarNavigationAccordionItem({ scrollLastChildIntoViewOnOpen: true }); await user.click(screen.getByRole('button', { name: 'Accordion Item' })); From 99f45294ac16a961922ce2823d3c005bbc49e37f Mon Sep 17 00:00:00 2001 From: David Cho-Lerat Date: Thu, 2 Apr 2026 12:29:46 +0200 Subject: [PATCH 10/13] ECHOES-1265 Update sidebar navigation (padding, header, accordion) --- .../sidebar-navigation/SidebarNavigation.tsx | 1 - .../SidebarNavigationAccordionItem.tsx | 7 +- .../SidebarNavigationHeader.tsx | 70 +++++++----- .../SidebarNavigationAccordionItem-test.tsx | 7 ++ ...SidebarNavigationAccordionItem-stories.tsx | 103 ++++++++++++++---- 5 files changed, 139 insertions(+), 49 deletions(-) diff --git a/src/components/layout/sidebar-navigation/SidebarNavigation.tsx b/src/components/layout/sidebar-navigation/SidebarNavigation.tsx index 3da4c224..1c54a571 100644 --- a/src/components/layout/sidebar-navigation/SidebarNavigation.tsx +++ b/src/components/layout/sidebar-navigation/SidebarNavigation.tsx @@ -89,7 +89,6 @@ const SidebarNavigationWrapper = styled.nav` box-sizing: content-box; overflow: hidden; - padding-top: ${cssVar('dimension-space-100')}; border-right: ${cssVar('border-width-default')} solid ${cssVar('color-border-weak')}; background-color: ${cssVar('color-surface-default')}; diff --git a/src/components/layout/sidebar-navigation/SidebarNavigationAccordionItem.tsx b/src/components/layout/sidebar-navigation/SidebarNavigationAccordionItem.tsx index d754f7c4..2093d356 100644 --- a/src/components/layout/sidebar-navigation/SidebarNavigationAccordionItem.tsx +++ b/src/components/layout/sidebar-navigation/SidebarNavigationAccordionItem.tsx @@ -65,6 +65,10 @@ export interface SidebarNavigationAccordionItemProps { * The onOpen callback is called when the accordion is opened. */ onOpen?: VoidFunction; + /** + * Whether the accordion is open by default. Defaults to false. + */ + defaultOpen?: boolean; /** * When true, scrolls the last child item into view when the accordion opens. * Useful when the accordion is near the bottom of a scrollable container. @@ -87,6 +91,7 @@ export const SidebarNavigationAccordionItem = forwardRef< >((props, ref) => { const { children, + defaultOpen = false, disableTooltip = false, Icon, label, @@ -97,7 +102,7 @@ export const SidebarNavigationAccordionItem = forwardRef< ...htmlProps } = props; - const [open, setOpen] = useState(false); + const [open, setOpen] = useState(defaultOpen); const panelRef = useRef(null); useEffect(() => { diff --git a/src/components/layout/sidebar-navigation/SidebarNavigationHeader.tsx b/src/components/layout/sidebar-navigation/SidebarNavigationHeader.tsx index 3f714db0..222c8e1f 100644 --- a/src/components/layout/sidebar-navigation/SidebarNavigationHeader.tsx +++ b/src/components/layout/sidebar-navigation/SidebarNavigationHeader.tsx @@ -56,47 +56,67 @@ export const SidebarNavigationHeader = forwardRef - - - {avatar && {avatar}} - - - {name} - - {qualifier && ( - - {qualifier} - - )} - - - {isInteractive && } - - + + + + + + {avatar && {avatar}} + + + {name} + + {qualifier && ( + + {qualifier} + + )} + + + {isInteractive && } + + + + ); }, ); SidebarNavigationHeader.displayName = 'SidebarNavigationHeader'; +const HeaderWrapper = styled.div` + padding: ${cssVar('dimension-space-100')}; + border-bottom: ${cssVar('border-width-default')} solid ${cssVar('color-border-weak')}; + + [data-sidebar-docked='false'] nav:not(:hover, :focus-within) & { + padding-inline: ${cssVar('dimension-space-50')}; + } +`; +HeaderWrapper.displayName = 'HeaderWrapper'; + +const HeaderInnerRow = styled.div` + display: flex; + align-items: center; + gap: ${cssVar('dimension-space-100')}; + border-radius: ${cssVar('border-radius-400')}; + width: 100%; +`; +HeaderInnerRow.displayName = 'HeaderInnerRow'; + const HeaderContainer = styled.button` all: unset; display: flex; align-items: center; justify-content: space-between; - flex-shrink: 0; + flex: 1 0 0; gap: ${cssVar('dimension-space-100')}; padding: ${cssVar('dimension-space-100')}; - margin: ${cssVar('dimension-space-100')}; overflow: hidden; - [data-sidebar-docked='false'] nav:not(:hover, :focus-within) & { - margin: ${cssVar('dimension-space-100')} ${cssVar('dimension-space-50')}; - } - /* If it is interactive, we want mouse interactivity */ :is(button) { border-radius: ${cssVar('border-radius-400')}; @@ -129,7 +149,7 @@ const MainContent = styled.div` display: flex; align-items: center; gap: ${cssVar('dimension-space-100')}; - min-width: ${cssVar('dimension-width-300')}; + min-width: ${cssVar('dimension-width-400')}; `; MainContent.displayName = 'MainContent'; diff --git a/src/components/layout/sidebar-navigation/__tests__/SidebarNavigationAccordionItem-test.tsx b/src/components/layout/sidebar-navigation/__tests__/SidebarNavigationAccordionItem-test.tsx index c495ac04..e6fb31c0 100644 --- a/src/components/layout/sidebar-navigation/__tests__/SidebarNavigationAccordionItem-test.tsx +++ b/src/components/layout/sidebar-navigation/__tests__/SidebarNavigationAccordionItem-test.tsx @@ -53,6 +53,13 @@ it('should expand hidden elements when clicked', async () => { expect(onClose).toHaveBeenCalled(); }); +it('should render the accordion open when defaultOpen is true', () => { + setupSidebarNavigationAccordionItem({ defaultOpen: true }); + + checkAccordionPanelVisibility(true); + expect(screen.getAllByRole('link')).toHaveLength(2); +}); + it("shouldn't have any a11y violation", async () => { const { container, user } = setupSidebarNavigationAccordionItem({ Icon: IconBranch }); await expect(container).toHaveNoA11yViolations(); diff --git a/stories/layout/sidebar-navigation/SidebarNavigationAccordionItem-stories.tsx b/stories/layout/sidebar-navigation/SidebarNavigationAccordionItem-stories.tsx index 10f556b9..1705c4c8 100644 --- a/stories/layout/sidebar-navigation/SidebarNavigationAccordionItem-stories.tsx +++ b/stories/layout/sidebar-navigation/SidebarNavigationAccordionItem-stories.tsx @@ -20,12 +20,41 @@ /* eslint-disable no-console */ import type { Meta, StoryObj } from '@storybook/react-vite'; -import { Badge, IconBranch, Layout } from '../../../src'; +import { Badge, cssVar, IconBranch, Layout } from '../../../src'; import { basicWrapperDecorator } from '../../helpers/BasicWrapper'; -const meta: Meta = { +const baseAccordionChildren = ( + <> + + Item 1 + + + + Item 2 + + +); + +const dockedSidebarAccordionChildren = ( + <> + + Icon hidden while sidebar is open + + + + Icon stays visible while sidebar is open + + +); + +const meta: Meta = { title: 'Echoes/Layout/SidebarNavigation/AccordionItem', component: Layout.SidebarNavigation.AccordionItem, + argTypes: { + defaultOpen: { + control: { type: 'boolean' }, + }, + }, decorators: [ (Story) => ( @@ -34,6 +63,13 @@ const meta: Meta = { ), basicWrapperDecorator, ], + render: ({ defaultOpen = false, ...args }) => ( + + ), }; export default meta; @@ -43,16 +79,8 @@ type Story = StoryObj; export const base: Story = { args: { Icon: IconBranch, - children: ( - <> - - Item 1 - - - Item 2 - - - ), + children: baseAccordionChildren, + defaultOpen: false, label: 'Accordion', }, }; @@ -60,16 +88,8 @@ export const base: Story = { export const suffixed: Story = { args: { Icon: IconBranch, - children: ( - <> - - Item 1 - - - Item 2 - - - ), + children: baseAccordionChildren, + defaultOpen: false, label: 'Accordion', suffix: ( @@ -79,6 +99,45 @@ export const suffixed: Story = { }, }; +export const withDefaultOpen: Story = { + args: { + Icon: IconBranch, + children: baseAccordionChildren, + defaultOpen: true, + label: 'Accordion', + }, + parameters: { + controls: { + include: ['defaultOpen'], + }, + }, +}; + +export const withDisableIconWhenSidebarOpen: Story = { + args: { + Icon: IconBranch, + defaultOpen: true, + label: 'Accordion', + }, + parameters: { + controls: { + include: ['defaultOpen'], + }, + }, + render: ({ defaultOpen = true, ...args }) => ( +
+ + {dockedSidebarAccordionChildren} + +
+ ), +}; + const fourNavItems = Array.from({ length: 4 }, (_, i) => ( Item {`${i + 1}`} From 43d3215b39a7984ddf8a7154bd90eedb603e8db2 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?M=C3=A1rcio?= Date: Tue, 7 Apr 2026 11:16:20 +0200 Subject: [PATCH 11/13] Removed Header Inner Row --- .../SidebarNavigationHeader.tsx | 54 +++++++++---------- 1 file changed, 24 insertions(+), 30 deletions(-) diff --git a/src/components/layout/sidebar-navigation/SidebarNavigationHeader.tsx b/src/components/layout/sidebar-navigation/SidebarNavigationHeader.tsx index 222c8e1f..9f037173 100644 --- a/src/components/layout/sidebar-navigation/SidebarNavigationHeader.tsx +++ b/src/components/layout/sidebar-navigation/SidebarNavigationHeader.tsx @@ -57,28 +57,26 @@ export const SidebarNavigationHeader = forwardRef - - - - - {avatar && {avatar}} - - - {name} + + + + {avatar && {avatar}} + + + {name} + + {qualifier && ( + + {qualifier} - {qualifier && ( - - {qualifier} - - )} - - - {isInteractive && } - - - + )} + + + {isInteractive && } + +
); }, @@ -87,22 +85,17 @@ SidebarNavigationHeader.displayName = 'SidebarNavigationHeader'; const HeaderWrapper = styled.div` padding: ${cssVar('dimension-space-100')}; + display: flex; border-bottom: ${cssVar('border-width-default')} solid ${cssVar('color-border-weak')}; [data-sidebar-docked='false'] nav:not(:hover, :focus-within) & { - padding-inline: ${cssVar('dimension-space-50')}; + padding-left: ${cssVar('dimension-space-50')}; + padding-right: ${cssVar('dimension-space-50')}; } `; HeaderWrapper.displayName = 'HeaderWrapper'; -const HeaderInnerRow = styled.div` - display: flex; - align-items: center; - gap: ${cssVar('dimension-space-100')}; - border-radius: ${cssVar('border-radius-400')}; - width: 100%; -`; -HeaderInnerRow.displayName = 'HeaderInnerRow'; + const HeaderContainer = styled.button` all: unset; @@ -111,6 +104,7 @@ const HeaderContainer = styled.button` align-items: center; justify-content: space-between; flex: 1 0 0; + border: 1px solid red; gap: ${cssVar('dimension-space-100')}; padding: ${cssVar('dimension-space-100')}; From 1b4be1183a7e97b6363bb7a72d86d91c0ed2603c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?M=C3=A1rcio?= Date: Tue, 7 Apr 2026 11:38:42 +0200 Subject: [PATCH 12/13] Fix Prettier formatting in SidebarNavigationHeader Co-Authored-By: Claude Sonnet 4.6 --- .../layout/sidebar-navigation/SidebarNavigationHeader.tsx | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/src/components/layout/sidebar-navigation/SidebarNavigationHeader.tsx b/src/components/layout/sidebar-navigation/SidebarNavigationHeader.tsx index bbd94af7..58c20a0a 100644 --- a/src/components/layout/sidebar-navigation/SidebarNavigationHeader.tsx +++ b/src/components/layout/sidebar-navigation/SidebarNavigationHeader.tsx @@ -57,9 +57,7 @@ export const SidebarNavigationHeader = forwardRef - + {avatar && {avatar}} From 87ef75a125b6594a3287390c52a300da10d8bc34 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?M=C3=A1rcio?= Date: Tue, 7 Apr 2026 12:02:19 +0200 Subject: [PATCH 13/13] update prop name to isDefaultOpen --- .../SidebarNavigationAccordionItem.tsx | 6 ++-- .../SidebarNavigationAccordionItem-test.tsx | 2 +- ...SidebarNavigationAccordionItem-stories.tsx | 32 +++++++------------ 3 files changed, 15 insertions(+), 25 deletions(-) diff --git a/src/components/layout/sidebar-navigation/SidebarNavigationAccordionItem.tsx b/src/components/layout/sidebar-navigation/SidebarNavigationAccordionItem.tsx index 2093d356..8290497f 100644 --- a/src/components/layout/sidebar-navigation/SidebarNavigationAccordionItem.tsx +++ b/src/components/layout/sidebar-navigation/SidebarNavigationAccordionItem.tsx @@ -68,7 +68,7 @@ export interface SidebarNavigationAccordionItemProps { /** * Whether the accordion is open by default. Defaults to false. */ - defaultOpen?: boolean; + isDefaultOpen?: boolean; /** * When true, scrolls the last child item into view when the accordion opens. * Useful when the accordion is near the bottom of a scrollable container. @@ -91,7 +91,7 @@ export const SidebarNavigationAccordionItem = forwardRef< >((props, ref) => { const { children, - defaultOpen = false, + isDefaultOpen = false, disableTooltip = false, Icon, label, @@ -102,7 +102,7 @@ export const SidebarNavigationAccordionItem = forwardRef< ...htmlProps } = props; - const [open, setOpen] = useState(defaultOpen); + const [open, setOpen] = useState(isDefaultOpen); const panelRef = useRef(null); useEffect(() => { diff --git a/src/components/layout/sidebar-navigation/__tests__/SidebarNavigationAccordionItem-test.tsx b/src/components/layout/sidebar-navigation/__tests__/SidebarNavigationAccordionItem-test.tsx index e6fb31c0..b4065c7c 100644 --- a/src/components/layout/sidebar-navigation/__tests__/SidebarNavigationAccordionItem-test.tsx +++ b/src/components/layout/sidebar-navigation/__tests__/SidebarNavigationAccordionItem-test.tsx @@ -54,7 +54,7 @@ it('should expand hidden elements when clicked', async () => { }); it('should render the accordion open when defaultOpen is true', () => { - setupSidebarNavigationAccordionItem({ defaultOpen: true }); + setupSidebarNavigationAccordionItem({ isDefaultOpen: true }); checkAccordionPanelVisibility(true); expect(screen.getAllByRole('link')).toHaveLength(2); diff --git a/stories/layout/sidebar-navigation/SidebarNavigationAccordionItem-stories.tsx b/stories/layout/sidebar-navigation/SidebarNavigationAccordionItem-stories.tsx index 1705c4c8..fe064e9b 100644 --- a/stories/layout/sidebar-navigation/SidebarNavigationAccordionItem-stories.tsx +++ b/stories/layout/sidebar-navigation/SidebarNavigationAccordionItem-stories.tsx @@ -51,7 +51,7 @@ const meta: Meta = { title: 'Echoes/Layout/SidebarNavigation/AccordionItem', component: Layout.SidebarNavigation.AccordionItem, argTypes: { - defaultOpen: { + isDefaultOpen: { control: { type: 'boolean' }, }, }, @@ -63,10 +63,10 @@ const meta: Meta = { ), basicWrapperDecorator, ], - render: ({ defaultOpen = false, ...args }) => ( + render: ({ isDefaultOpen = false, ...args }) => ( ), @@ -80,7 +80,7 @@ export const base: Story = { args: { Icon: IconBranch, children: baseAccordionChildren, - defaultOpen: false, + isDefaultOpen: false, label: 'Accordion', }, }; @@ -89,7 +89,7 @@ export const suffixed: Story = { args: { Icon: IconBranch, children: baseAccordionChildren, - defaultOpen: false, + isDefaultOpen: false, label: 'Accordion', suffix: ( @@ -103,34 +103,24 @@ export const withDefaultOpen: Story = { args: { Icon: IconBranch, children: baseAccordionChildren, - defaultOpen: true, + isDefaultOpen: true, label: 'Accordion', }, - parameters: { - controls: { - include: ['defaultOpen'], - }, - }, }; export const withDisableIconWhenSidebarOpen: Story = { args: { Icon: IconBranch, - defaultOpen: true, + isDefaultOpen: true, label: 'Accordion', }, - parameters: { - controls: { - include: ['defaultOpen'], - }, - }, - render: ({ defaultOpen = true, ...args }) => ( + render: ({ isDefaultOpen = true, ...args }) => (
{dockedSidebarAccordionChildren}