Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
39 changes: 4 additions & 35 deletions pages/side-navigation/collapsed.page.tsx
Original file line number Diff line number Diff line change
@@ -1,13 +1,12 @@
// Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved.
// SPDX-License-Identifier: Apache-2.0

import React, { useEffect, useRef, useState } from 'react';
import React, { useRef, useState } from 'react';

import { useReducedMotion } from '@cloudscape-design/component-toolkit/internal';

import { Box, Button, Icon, SpaceBetween, Toggle } from '~components';
import { Box, Button, Icon, SpaceBetween } from '~components';
import SideNavigation, { SideNavigationProps } from '~components/side-navigation';
import { applyTheme } from '~components/theming';
import { colorBorderDividerDefault } from '~design-tokens';

const items: SideNavigationProps.Item[] = [
Expand All @@ -24,7 +23,6 @@ const items: SideNavigationProps.Item[] = [
],
},
{ type: 'link', text: 'Settings', href: '#/settings', icon: <Icon name="settings" /> },
{ type: 'divider' },
{
type: 'section',
text: 'Resources',
Expand All @@ -34,7 +32,6 @@ const items: SideNavigationProps.Item[] = [
{ type: 'link', text: 'Networking', href: '#/networking', icon: <Icon name="share" /> },
],
},
{ type: 'divider' },
{ type: 'link', text: 'Documentation', href: '#/docs', external: true },
];

Expand All @@ -44,36 +41,11 @@ const EXPANDED_WIDTH = 220;
export default function SideNavigationCollapsedPage() {
const [activeHref, setActiveHref] = useState('#/calendar');
const [collapsed, setCollapsed] = useState(false);
const [highlighted, setHighlighted] = useState(true);

useEffect(() => {
handleHighlightedChange(true);
}, []);
const [panelWidth, setPanelWidth] = useState(EXPANDED_WIDTH);
const navRef = useRef<HTMLElement>(null);
const toggleRef = useRef<HTMLButtonElement>(null);
const reducedMotion = useReducedMotion(navRef);

const resetThemeRef = useRef<(() => void) | null>(null);

function handleHighlightedChange(value: boolean) {
setHighlighted(value);
if (value) {
const { reset } = applyTheme({
theme: {

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Even though this is just a dev page, we don't want to see the themed version but the actual version of the side nav in one theme. Hence, removed this part.

tokens: {
colorBackgroundSideNavigationItemActive: { light: '{colorPrimary50}', dark: '#0099FF20' },
colorTextSideNavigationItemActive: { light: '{colorPrimary600}', dark: '{colorPrimary300}' },
},
},
});
resetThemeRef.current = reset;
} else {
resetThemeRef.current?.();
resetThemeRef.current = null;
}
}

function handleToggle() {
const willCollapse = !collapsed;
// Focus management: move focus to toggle if focused item will be hidden
Expand Down Expand Up @@ -107,7 +79,7 @@ export default function SideNavigationCollapsedPage() {
{/* Toggle button at top */}
<div
style={{
padding: collapsed ? '12px' : `12px 12px 4px 24px`,
padding: '12px',
display: 'flex',
alignItems: 'center',
justifyContent: collapsed ? 'center' : 'space-between',
Expand Down Expand Up @@ -141,12 +113,9 @@ export default function SideNavigationCollapsedPage() {
<SpaceBetween size="m">
<Box variant="h1">Collapsed state demo</Box>
<Box>Active: {activeHref}</Box>
<Toggle checked={highlighted} onChange={({ detail }) => handleHighlightedChange(detail.checked)}>
Background highlight
</Toggle>
<Box color="text-status-inactive">
Toggle the navigation panel using the button. Items without icons are hidden in collapsed mode. Sections
show their icon-bearing children as a flat list.
show their icon-bearing children as a flat list, and the section title becomes a divider in its place.
</Box>
</SpaceBetween>
</div>
Expand Down
24 changes: 24 additions & 0 deletions src/__integ__/__snapshots__/themes.test.ts.snap
Original file line number Diff line number Diff line change
Expand Up @@ -167,6 +167,7 @@ exports[`CSS Custom Properties match previous snapshot for mode "compact" 1`] =
"color-background-segment-hover": "#fafafa",
"color-background-segment-wrapper": "#ffffff",
"color-background-side-navigation-item-active": "transparent",
"color-background-side-navigation-item-active-collapsed": "transparent",
"color-background-skeleton": "#eaeded",
"color-background-skeleton-wave": "#f6f6f9",
"color-background-slider-handle-active": "#0a4a74",
Expand Down Expand Up @@ -639,6 +640,8 @@ exports[`CSS Custom Properties match previous snapshot for mode "compact" 1`] =
"color-text-segment-default": "#545b64",
"color-text-segment-hover": "#16191f",
"color-text-side-navigation-item-active": "#0073bb",
"color-text-side-navigation-item-active-collapsed": "#0073bb",
"color-text-side-navigation-item-default": "#545b64",
"color-text-small": "#687078",
"color-text-status-error": "#d13212",
"color-text-status-inactive": "#687078",
Expand Down Expand Up @@ -1075,6 +1078,7 @@ exports[`CSS Custom Properties match previous snapshot for mode "dark" 1`] = `
"color-background-segment-hover": "#21252c",
"color-background-segment-wrapper": "#2a2e33",
"color-background-side-navigation-item-active": "transparent",
"color-background-side-navigation-item-active-collapsed": "transparent",
"color-background-skeleton": "#2a2e33",
"color-background-skeleton-wave": "#414750",
"color-background-slider-handle-active": "#44b9d6",
Expand Down Expand Up @@ -1547,6 +1551,8 @@ exports[`CSS Custom Properties match previous snapshot for mode "dark" 1`] = `
"color-text-segment-default": "#d5dbdb",
"color-text-segment-hover": "#fafafa",
"color-text-side-navigation-item-active": "#44b9d6",
"color-text-side-navigation-item-active-collapsed": "#44b9d6",
"color-text-side-navigation-item-default": "#d5dbdb",
"color-text-small": "#95a5a6",
"color-text-status-error": "#ff5d64",
"color-text-status-inactive": "#95a5a6",
Expand Down Expand Up @@ -1983,6 +1989,7 @@ exports[`CSS Custom Properties match previous snapshot for mode "light" 1`] = `
"color-background-segment-hover": "#fafafa",
"color-background-segment-wrapper": "#ffffff",
"color-background-side-navigation-item-active": "transparent",
"color-background-side-navigation-item-active-collapsed": "transparent",
"color-background-skeleton": "#eaeded",
"color-background-skeleton-wave": "#f6f6f9",
"color-background-slider-handle-active": "#0a4a74",
Expand Down Expand Up @@ -2455,6 +2462,8 @@ exports[`CSS Custom Properties match previous snapshot for mode "light" 1`] = `
"color-text-segment-default": "#545b64",
"color-text-segment-hover": "#16191f",
"color-text-side-navigation-item-active": "#0073bb",
"color-text-side-navigation-item-active-collapsed": "#0073bb",
"color-text-side-navigation-item-default": "#545b64",
"color-text-small": "#687078",
"color-text-status-error": "#d13212",
"color-text-status-inactive": "#687078",
Expand Down Expand Up @@ -2891,6 +2900,7 @@ exports[`CSS Custom Properties match previous snapshot for mode "reduced-motion"
"color-background-segment-hover": "#fafafa",
"color-background-segment-wrapper": "#ffffff",
"color-background-side-navigation-item-active": "transparent",
"color-background-side-navigation-item-active-collapsed": "transparent",
"color-background-skeleton": "#eaeded",
"color-background-skeleton-wave": "#f6f6f9",
"color-background-slider-handle-active": "#0a4a74",
Expand Down Expand Up @@ -3363,6 +3373,8 @@ exports[`CSS Custom Properties match previous snapshot for mode "reduced-motion"
"color-text-segment-default": "#545b64",
"color-text-segment-hover": "#16191f",
"color-text-side-navigation-item-active": "#0073bb",
"color-text-side-navigation-item-active-collapsed": "#0073bb",
"color-text-side-navigation-item-default": "#545b64",
"color-text-small": "#687078",
"color-text-status-error": "#d13212",
"color-text-status-inactive": "#687078",
Expand Down Expand Up @@ -3799,6 +3811,7 @@ exports[`CSS Custom Properties match previous snapshot for mode "visual-refresh"
"color-background-segment-hover": "#f0fbff",
"color-background-segment-wrapper": "#ffffff",
"color-background-side-navigation-item-active": "transparent",
"color-background-side-navigation-item-active-collapsed": "transparent",
"color-background-skeleton": "#ebebf0",
"color-background-skeleton-wave": "#f6f6f9",
"color-background-slider-handle-active": "#004a9e",
Expand Down Expand Up @@ -4271,6 +4284,8 @@ exports[`CSS Custom Properties match previous snapshot for mode "visual-refresh"
"color-text-segment-default": "#424650",
"color-text-segment-hover": "#002b66",
"color-text-side-navigation-item-active": "#006ce0",
"color-text-side-navigation-item-active-collapsed": "#006ce0",
"color-text-side-navigation-item-default": "#424650",
"color-text-small": "#656871",
"color-text-status-error": "#db0000",
"color-text-status-inactive": "#656871",
Expand Down Expand Up @@ -4707,6 +4722,7 @@ exports[`CSS Custom Properties match previous snapshot for mode "visual-refresh-
"color-background-segment-hover": "#f0fbff",
"color-background-segment-wrapper": "#ffffff",
"color-background-side-navigation-item-active": "transparent",
"color-background-side-navigation-item-active-collapsed": "transparent",
"color-background-skeleton": "#ebebf0",
"color-background-skeleton-wave": "#f6f6f9",
"color-background-slider-handle-active": "#004a9e",
Expand Down Expand Up @@ -5179,6 +5195,8 @@ exports[`CSS Custom Properties match previous snapshot for mode "visual-refresh-
"color-text-segment-default": "#424650",
"color-text-segment-hover": "#002b66",
"color-text-side-navigation-item-active": "#006ce0",
"color-text-side-navigation-item-active-collapsed": "#006ce0",
"color-text-side-navigation-item-default": "#424650",
"color-text-small": "#656871",
"color-text-status-error": "#db0000",
"color-text-status-inactive": "#656871",
Expand Down Expand Up @@ -5615,6 +5633,7 @@ exports[`CSS Custom Properties match previous snapshot for mode "visual-refresh-
"color-background-segment-hover": "#1b232d",
"color-background-segment-wrapper": "#0f141a",
"color-background-side-navigation-item-active": "transparent",
"color-background-side-navigation-item-active-collapsed": "transparent",
"color-background-skeleton": "#232b37",
"color-background-skeleton-wave": "#333843",
"color-background-slider-handle-active": "#75cfff",
Expand Down Expand Up @@ -6087,6 +6106,8 @@ exports[`CSS Custom Properties match previous snapshot for mode "visual-refresh-
"color-text-segment-default": "#dedee3",
"color-text-segment-hover": "#75cfff",
"color-text-side-navigation-item-active": "#42b4ff",
"color-text-side-navigation-item-active-collapsed": "#42b4ff",
"color-text-side-navigation-item-default": "#c6c6cd",
"color-text-small": "#a4a4ad",
"color-text-status-error": "#ff7a7a",
"color-text-status-inactive": "#a4a4ad",
Expand Down Expand Up @@ -6523,6 +6544,7 @@ exports[`CSS Custom Properties match previous snapshot for mode "visual-refresh-
"color-background-segment-hover": "#1b232d",
"color-background-segment-wrapper": "#161d26",
"color-background-side-navigation-item-active": "transparent",
"color-background-side-navigation-item-active-collapsed": "transparent",
"color-background-skeleton": "#232b37",
"color-background-skeleton-wave": "#333843",
"color-background-slider-handle-active": "#75cfff",
Expand Down Expand Up @@ -6995,6 +7017,8 @@ exports[`CSS Custom Properties match previous snapshot for mode "visual-refresh-
"color-text-segment-default": "#dedee3",
"color-text-segment-hover": "#75cfff",
"color-text-side-navigation-item-active": "#42b4ff",
"color-text-side-navigation-item-active-collapsed": "#42b4ff",
"color-text-side-navigation-item-default": "#c6c6cd",
"color-text-small": "#a4a4ad",
"color-text-status-error": "#ff7a7a",
"color-text-status-inactive": "#a4a4ad",
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -137,6 +137,30 @@ describe('SideNavigation collapsed mode', () => {
const wrapper = renderSideNavigation({ collapsed: true, items });
expect(wrapper.findAll('a')).toHaveLength(0);
});

it('renders a divider in place of the section title when collapsed', () => {
const wrapper = renderSideNavigation({
collapsed: true,
items: [
iconLink('Top', '#/top'),
{
type: 'section',
text: 'Resources',
items: [iconLink('Compute', '#/compute'), iconLink('Storage', '#/storage')],
},
],
});
expect(wrapper.findAll('hr')).toHaveLength(1);
expect(wrapper.find('ul[aria-label="Resources"]')).not.toBeNull();
});

it('does not render a section divider when the section has no icon-bearing children', () => {
const wrapper = renderSideNavigation({
collapsed: true,
items: [iconLink('Top', '#/top'), { type: 'section', text: 'Empty', items: [plainLink('A', '#/a')] }],
});
expect(wrapper.findAll('hr')).toHaveLength(0);
});
});

describe('expandable link groups', () => {
Expand Down
46 changes: 29 additions & 17 deletions src/side-navigation/parts.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -135,14 +135,39 @@ export function NavigationItemsList({
const itemid = index + 1;
const itemPosition = `${position ? `${position},` : ''}${itemid}`;

// Emits a divider as its own list segment (dividers break the <ul> grouping).
function pushDivider() {
lists[lists.length] = {
listVariant: variant,
element: (
<div data-itemid={`item-${itemid}`}>
<Divider variant="default" collapsed={collapsed} />
</div>
),
};
currentListIndex = lists.length;
lists[currentListIndex] = {
listVariant: variant,
items: [],
};
}

// Renders icon-bearing children of a container item as a collapsed group.
// The inner <ul> carries the group label so list semantics are preserved
// for screen readers even when the visual header is hidden.
function pushCollapsedGroup(children: ReadonlyArray<SideNavigationProps.Item>, label: string) {
function pushCollapsedGroup(
children: ReadonlyArray<SideNavigationProps.Item>,
label: string,
{ leadingDivider = false }: { leadingDivider?: boolean } = {}
) {
const iconChildren = children.filter(child => (child as SideNavigationProps.Link).icon);
if (iconChildren.length === 0) {
return;
}
// A section's title is hidden when collapsed; render a divider in its place
if (leadingDivider) {
pushDivider();
}
const groupElements = iconChildren.map((child, childIndex) => {
const childPosition = `${position ? `${position},` : ''}${itemid},${childIndex + 1}`;
return (
Expand All @@ -166,7 +191,7 @@ export function NavigationItemsList({
key={`group-${itemid}`}
className={clsx(
styles['list-item--group'],
prevItem?.type === 'divider' && styles['list-item--group-no-padding-start'],
(leadingDivider || prevItem?.type === 'divider') && styles['list-item--group-no-padding-start'],
nextItem?.type === 'divider' && styles['list-item--group-no-padding-end']
)}
>
Expand Down Expand Up @@ -196,28 +221,15 @@ export function NavigationItemsList({
: (item as SideNavigationProps.SectionGroup).items.flatMap(child =>
child.type === 'section' ? (child as SideNavigationProps.Section).items : [child]
);
pushCollapsedGroup(childItems, sectionLabel);
pushCollapsedGroup(childItems, sectionLabel, { leadingDivider: true });
return;
}
if (collapsed && item.type !== 'divider' && !(item as SideNavigationProps.Link).icon) {
return;
}
switch (item.type) {
case 'divider': {
const dividerIndex = lists.length;
lists[dividerIndex] = {
listVariant: variant,
element: (
<div data-itemid={`item-${itemid}`}>
<Divider variant="default" collapsed={collapsed} />
</div>
),
};
currentListIndex = lists.length;
lists[currentListIndex] = {
listVariant: variant,
items: [],
};
pushDivider();
return;
}
case 'link': {
Expand Down
23 changes: 10 additions & 13 deletions src/side-navigation/styles.scss
Original file line number Diff line number Diff line change
Expand Up @@ -154,9 +154,6 @@ $expandable-icon-negative-margin: awsui.$space-l;
padding-block: 0;
padding-inline: $item-padding-inline;
list-style: none;
@include styles.with-motion {
transition: margin awsui.$motion-duration-expressive awsui.$motion-easing-responsive;
}
// Remove margin from first item in side nav, outer block margins are covered by list-container
.list-variant-root--first > &:first-child {
margin-block-start: 0px;
Expand All @@ -168,9 +165,6 @@ $expandable-icon-negative-margin: awsui.$space-l;
justify-content: center;
padding-block: 0;
padding-inline: 0;
@include styles.with-motion {
transition: margin awsui.$motion-duration-expressive awsui.$motion-easing-responsive;
}
}

&--group {
Expand Down Expand Up @@ -284,8 +278,8 @@ $expandable-icon-negative-margin: awsui.$space-l;
// ==========================================================================
.link {
@include styles.font-body-m;
color: awsui.$color-text-body-secondary;
display: inline-flex;
color: awsui.$color-text-side-navigation-item-default;
display: flex;
padding-block: $item-padding-block;
min-inline-size: awsui.$size-side-navigation-item-height;
padding-inline: $item-padding-inline;
Expand All @@ -295,17 +289,20 @@ $expandable-icon-negative-margin: awsui.$space-l;
font-weight: styles.$font-weight-normal;
-webkit-font-smoothing: auto;
-moz-osx-font-smoothing: auto;
@include styles.with-motion {
transition:
background-color awsui.$motion-duration-expressive awsui.$motion-easing-responsive,
color awsui.$motion-duration-expressive awsui.$motion-easing-expressive;
}

&-active {
font-weight: awsui.$font-wayfinding-link-active-weight;
@include styles.font-smoothing;
color: awsui.$color-text-side-navigation-item-active;
background-color: awsui.$color-background-side-navigation-item-active;

&.link--collapsed {
color: awsui.$color-text-side-navigation-item-active-collapsed;
background-color: awsui.$color-background-side-navigation-item-active-collapsed;
&:hover {
color: awsui.$color-text-side-navigation-item-active-collapsed;
}
}
}

&--collapsed {
Expand Down
Loading
Loading