From 44fb6a12b620965f53f246a7db689842cf6bbd55 Mon Sep 17 00:00:00 2001 From: Trevor Burnham Date: Sat, 13 Sep 2025 10:52:51 -0400 Subject: [PATCH 1/2] fix: Fix AppLayoutToolbar breadcrumbs SSR glitch --- .../visual-refresh-toolbar/skeleton/index.tsx | 4 +- .../skeleton/skeleton-parts.tsx | 22 +++---- .../visual-refresh-toolbar/skeleton/slots.tsx | 18 ++++- .../skeleton/toolbar-container.tsx | 66 +++++++++++++++++++ .../state/use-skeleton-slots-attributes.ts | 1 - .../visual-refresh-toolbar/toolbar/index.tsx | 21 +++--- 6 files changed, 105 insertions(+), 27 deletions(-) create mode 100644 src/app-layout/visual-refresh-toolbar/skeleton/toolbar-container.tsx diff --git a/src/app-layout/visual-refresh-toolbar/skeleton/index.tsx b/src/app-layout/visual-refresh-toolbar/skeleton/index.tsx index 7df2e99b24..787d7a0138 100644 --- a/src/app-layout/visual-refresh-toolbar/skeleton/index.tsx +++ b/src/app-layout/visual-refresh-toolbar/skeleton/index.tsx @@ -58,12 +58,14 @@ export const SkeletonLayout = ({ contentElAttributes, } = skeletonSlotsAttributes; + const isWidgetLoaded = !!appLayoutState.widgetizedState; + return (
} - data-awsui-app-layout-widget-loaded={false} + data-awsui-app-layout-widget-loaded={isWidgetLoaded} {...wrapperElAttributes} className={wrapperElAttributes?.className ?? clsx(styles.root, testutilStyles.root)} style={ diff --git a/src/app-layout/visual-refresh-toolbar/skeleton/skeleton-parts.tsx b/src/app-layout/visual-refresh-toolbar/skeleton/skeleton-parts.tsx index 503bb16466..ba44180e09 100644 --- a/src/app-layout/visual-refresh-toolbar/skeleton/skeleton-parts.tsx +++ b/src/app-layout/visual-refresh-toolbar/skeleton/skeleton-parts.tsx @@ -5,7 +5,8 @@ import React from 'react'; import { AppLayoutNotificationsImplementationProps } from '../notifications'; import { AppLayoutToolbarImplementationProps } from '../toolbar'; import { SkeletonPartProps } from './interfaces'; -import { BreadcrumbsSlot, NotificationsSlot, ToolbarSlot } from './slots'; +import { NotificationsSlot } from './slots'; +import { ToolbarSkeletonStructure } from './toolbar-container'; import styles from './styles.css.js'; @@ -17,11 +18,7 @@ export const BeforeMainSlotSkeleton = React.forwardRef { return ( <> - {!!toolbarProps && ( - - - - )} + {!!toolbarProps && } {toolbarProps?.navigationOpen &&
} ); @@ -34,15 +31,14 @@ export const BeforeMainSlotSkeleton = React.forwardRef( ({ appLayoutInternals }: AppLayoutToolbarImplementationProps, ref) => ( - - - + ) ); export const NotificationsSkeleton = React.forwardRef( - (props: AppLayoutNotificationsImplementationProps, ref) => + (_props: AppLayoutNotificationsImplementationProps, ref) => ); diff --git a/src/app-layout/visual-refresh-toolbar/skeleton/slots.tsx b/src/app-layout/visual-refresh-toolbar/skeleton/slots.tsx index 6c77569b59..083e010ca5 100644 --- a/src/app-layout/visual-refresh-toolbar/skeleton/slots.tsx +++ b/src/app-layout/visual-refresh-toolbar/skeleton/slots.tsx @@ -7,6 +7,8 @@ import { BreadcrumbGroupImplementation } from '../../../breadcrumb-group/impleme import { BreadcrumbGroupProps } from '../../../breadcrumb-group/interfaces'; import { BreadcrumbsSlotContext } from '../contexts'; +import testutilStyles from '../../test-classes/styles.css.js'; +import toolbarStyles from '../toolbar/styles.css.js'; import styles from './styles.css.js'; interface ToolbarSlotProps { @@ -16,7 +18,14 @@ interface ToolbarSlotProps { } export const ToolbarSlot = React.forwardRef(({ className, style, children }, ref) => ( -
} className={clsx(styles['toolbar-container'], className)} style={style}> +
} + className={clsx(styles['toolbar-container'], toolbarStyles['universal-toolbar'], testutilStyles.toolbar, className)} + style={{ + insetBlockStart: style?.insetBlockStart ?? 0, + ...style, + }} + > {children}
)); @@ -41,10 +50,13 @@ interface BreadcrumbsSlotProps { } export function BreadcrumbsSlot({ ownBreadcrumbs, discoveredBreadcrumbs }: BreadcrumbsSlotProps) { + const isSSR = typeof window === 'undefined'; + const contextValue = React.useMemo(() => ({ isInToolbar: true }), []); + return ( - +
{ownBreadcrumbs}
- {discoveredBreadcrumbs && ( + {discoveredBreadcrumbs && !isSSR && (
+ {children} +
+ ); +} + +interface ToolbarBreadcrumbsWrapperProps { + children: React.ReactNode; +} + +export function ToolbarBreadcrumbsWrapper({ children }: ToolbarBreadcrumbsWrapperProps) { + return ( +
{children}
+ ); +} + +export function ToolbarDrawersWrapper() { + return
; +} + +interface ToolbarBreadcrumbsSectionProps { + ownBreadcrumbs: React.ReactNode; + discoveredBreadcrumbs?: BreadcrumbGroupProps | null; +} + +export function ToolbarBreadcrumbsSection({ ownBreadcrumbs, discoveredBreadcrumbs }: ToolbarBreadcrumbsSectionProps) { + return ( + + + + ); +} + +interface ToolbarSkeletonStructureProps { + ownBreadcrumbs: React.ReactNode; + discoveredBreadcrumbs?: BreadcrumbGroupProps | null; +} + +export const ToolbarSkeletonStructure = React.forwardRef( + ({ ownBreadcrumbs, discoveredBreadcrumbs }, ref) => ( + + + + + + + ) +); diff --git a/src/app-layout/visual-refresh-toolbar/state/use-skeleton-slots-attributes.ts b/src/app-layout/visual-refresh-toolbar/state/use-skeleton-slots-attributes.ts index 4268a9ffab..72f4304017 100644 --- a/src/app-layout/visual-refresh-toolbar/state/use-skeleton-slots-attributes.ts +++ b/src/app-layout/visual-refresh-toolbar/state/use-skeleton-slots-attributes.ts @@ -50,7 +50,6 @@ export const useSkeletonSlotsAttributes = ( [customCssProps.navigationWidth]: `${navigationWidth}px`, [customCssProps.toolsWidth]: `${activeDrawerSize}px`, }, - 'data-awsui-app-layout-widget-loaded': true, }; const mainElAttributes = { diff --git a/src/app-layout/visual-refresh-toolbar/toolbar/index.tsx b/src/app-layout/visual-refresh-toolbar/toolbar/index.tsx index d90c9d5f39..922819e9b0 100644 --- a/src/app-layout/visual-refresh-toolbar/toolbar/index.tsx +++ b/src/app-layout/visual-refresh-toolbar/toolbar/index.tsx @@ -6,11 +6,14 @@ import clsx from 'clsx'; import { useResizeObserver } from '@cloudscape-design/component-toolkit/internal'; +import { createWidgetizedComponent } from '../../../internal/widgets'; import { AppLayoutProps } from '../../interfaces'; import { OnChangeParams } from '../../utils/use-drawers'; import { Focusable, FocusControlMultipleStates } from '../../utils/use-focus-control'; import { AppLayoutInternals } from '../interfaces'; -import { BreadcrumbsSlot, ToolbarSlot } from '../skeleton/slots'; +import { ToolbarSkeleton } from '../skeleton/skeleton-parts'; +import { ToolbarSlot } from '../skeleton/slots'; +import { ToolbarBreadcrumbsSection, ToolbarContainer } from '../skeleton/toolbar-container'; import { DrawerTriggers, SplitPanelToggleProps } from './drawer-triggers'; import TriggerButton from './trigger-button'; @@ -188,7 +191,7 @@ export function AppLayoutToolbarImplementation({
)} -
+ {hasNavigation && (
+ ); } + +export const AppLayoutToolbar = createWidgetizedComponent(AppLayoutToolbarImplementation, ToolbarSkeleton); From baf472e10e762b2e5fce48117f274486db472281 Mon Sep 17 00:00:00 2001 From: Trevor Burnham Date: Sat, 27 Sep 2025 15:33:00 -0400 Subject: [PATCH 2/2] Fix test failures --- .../visual-refresh-toolbar/skeleton/slots.tsx | 4 +--- .../skeleton/toolbar-container.tsx | 18 ++++++++++++++---- .../visual-refresh-toolbar/toolbar/index.tsx | 1 + 3 files changed, 16 insertions(+), 7 deletions(-) diff --git a/src/app-layout/visual-refresh-toolbar/skeleton/slots.tsx b/src/app-layout/visual-refresh-toolbar/skeleton/slots.tsx index 083e010ca5..1e6e0960d0 100644 --- a/src/app-layout/visual-refresh-toolbar/skeleton/slots.tsx +++ b/src/app-layout/visual-refresh-toolbar/skeleton/slots.tsx @@ -7,8 +7,6 @@ import { BreadcrumbGroupImplementation } from '../../../breadcrumb-group/impleme import { BreadcrumbGroupProps } from '../../../breadcrumb-group/interfaces'; import { BreadcrumbsSlotContext } from '../contexts'; -import testutilStyles from '../../test-classes/styles.css.js'; -import toolbarStyles from '../toolbar/styles.css.js'; import styles from './styles.css.js'; interface ToolbarSlotProps { @@ -20,7 +18,7 @@ interface ToolbarSlotProps { export const ToolbarSlot = React.forwardRef(({ className, style, children }, ref) => (
} - className={clsx(styles['toolbar-container'], toolbarStyles['universal-toolbar'], testutilStyles.toolbar, className)} + className={clsx(styles['toolbar-container'], className)} style={{ insetBlockStart: style?.insetBlockStart ?? 0, ...style, diff --git a/src/app-layout/visual-refresh-toolbar/skeleton/toolbar-container.tsx b/src/app-layout/visual-refresh-toolbar/skeleton/toolbar-container.tsx index 357ec0450d..b1dcc7d9e3 100644 --- a/src/app-layout/visual-refresh-toolbar/skeleton/toolbar-container.tsx +++ b/src/app-layout/visual-refresh-toolbar/skeleton/toolbar-container.tsx @@ -24,11 +24,16 @@ export function ToolbarContainer({ children, hasAiDrawer }: ToolbarContainerProp interface ToolbarBreadcrumbsWrapperProps { children: React.ReactNode; + includeTestUtils?: boolean; } -export function ToolbarBreadcrumbsWrapper({ children }: ToolbarBreadcrumbsWrapperProps) { +export function ToolbarBreadcrumbsWrapper({ children, includeTestUtils = false }: ToolbarBreadcrumbsWrapperProps) { return ( -
{children}
+
+ {children} +
); } @@ -39,11 +44,16 @@ export function ToolbarDrawersWrapper() { interface ToolbarBreadcrumbsSectionProps { ownBreadcrumbs: React.ReactNode; discoveredBreadcrumbs?: BreadcrumbGroupProps | null; + includeTestUtils?: boolean; } -export function ToolbarBreadcrumbsSection({ ownBreadcrumbs, discoveredBreadcrumbs }: ToolbarBreadcrumbsSectionProps) { +export function ToolbarBreadcrumbsSection({ + ownBreadcrumbs, + discoveredBreadcrumbs, + includeTestUtils = false, +}: ToolbarBreadcrumbsSectionProps) { return ( - + ); diff --git a/src/app-layout/visual-refresh-toolbar/toolbar/index.tsx b/src/app-layout/visual-refresh-toolbar/toolbar/index.tsx index 922819e9b0..ba2b669911 100644 --- a/src/app-layout/visual-refresh-toolbar/toolbar/index.tsx +++ b/src/app-layout/visual-refresh-toolbar/toolbar/index.tsx @@ -218,6 +218,7 @@ export function AppLayoutToolbarImplementation({ )} {(drawers?.length || globalDrawers?.length || (hasSplitPanel && splitPanelToggleProps?.displayed)) && (