Skip to content
Draft
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
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ import { render } from '@testing-library/react';

import AppLayout from '../../../lib/components/app-layout';
import ErrorBoundary from '../../../lib/components/error-boundary';
import { metrics } from '../../../lib/components/internal/metrics';
import awsuiPlugins from '../../../lib/components/internal/plugins';
import { DrawerConfig } from '../../../lib/components/internal/plugins/controllers/drawers';
import * as awsuiWidgetPlugins from '../../../lib/components/internal/plugins/widget';
Expand Down Expand Up @@ -35,8 +36,10 @@ function renderComponent(jsx: React.ReactElement) {
};
}

let sendPanoramaMetricSpy: jest.SpyInstance;
beforeEach(() => {
jest.resetAllMocks();
sendPanoramaMetricSpy = jest.spyOn(metrics, 'sendOpsMetricObject').mockImplementation(() => {});
});

describe('AppLayout error boundaries: errors in different areas does not crash the entire app layout', () => {
Expand Down Expand Up @@ -66,6 +69,10 @@ describe('AppLayout error boundaries: errors in different areas does not crash t
expect(onError).toHaveBeenCalled();
}
expect(consoleSpy).toHaveBeenCalled();
expect(sendPanoramaMetricSpy).toHaveBeenCalledWith('awsui-app-layout-error-boundary-fired', {
appLayoutPart: expect.any(String),

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

can we test actual values here?

errorMessage: expect.any(String),
});
};

test('left drawer content', () => {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -131,7 +131,7 @@ export function AppLayoutGlobalAiDrawerImplementation({
}

return (
<AppLayoutBuiltInErrorBoundary>
<AppLayoutBuiltInErrorBoundary appLayoutPart="left-drawer">
<Transition nodeRef={drawerRef} in={show} appear={show} mountOnEnter={true} timeout={250}>
{drawerTransitionState => {
return (
Expand Down Expand Up @@ -192,7 +192,10 @@ export function AppLayoutGlobalAiDrawerImplementation({
<div className={styles['drawer-content']}>
<header className={styles['drawer-content-header']}>
<div className={styles['drawer-content-header-content']}>
<AppLayoutBuiltInErrorBoundary renderFallback={() => <div />}>
<AppLayoutBuiltInErrorBoundary
renderFallback={() => <div />}
appLayoutPart="left-drawer-header"
>
{activeAiDrawer?.header ?? <div />}
</AppLayoutBuiltInErrorBoundary>
<div className={styles['drawer-actions']}>
Expand Down Expand Up @@ -248,7 +251,7 @@ export function AppLayoutGlobalAiDrawerImplementation({
</div>
)}
</header>
<AppLayoutBuiltInErrorBoundary>
<AppLayoutBuiltInErrorBoundary appLayoutPart="left-drawer-content">
<div className={styles['drawer-content-content']}>{activeAiDrawer?.content}</div>
</AppLayoutBuiltInErrorBoundary>
</div>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -189,7 +189,7 @@ function AppLayoutGlobalBottomDrawerImplementation({
}, [reportBottomDrawerSize, size]);

return (
<AppLayoutBuiltInErrorBoundary>
<AppLayoutBuiltInErrorBoundary appLayoutPart="bottom-drawer">
<Transition
nodeRef={drawerRef}
in={show || isExpanded}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -110,7 +110,7 @@ function AppLayoutGlobalDrawerImplementation({
}

return (
<AppLayoutBuiltInErrorBoundary>
<AppLayoutBuiltInErrorBoundary appLayoutPart="global-drawer">
<Transition nodeRef={drawerRef} in={show || isExpanded} appear={show || isExpanded} timeout={0}>
{state => {
return (
Expand Down
2 changes: 1 addition & 1 deletion src/app-layout/visual-refresh-toolbar/index.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -105,7 +105,7 @@ function AppLayoutVisualRefreshToolbarWithI18n({
<AppLayoutVisibilityContext.Provider value={appLayoutState.isIntersecting}>
{/* Rendering a hidden copy of breadcrumbs to trigger their deduplication */}
{(embeddedViewMode || !toolbarProps) && appLayoutPropsWithI18n.breadcrumbs ? (
<AppLayoutBuiltInErrorBoundary>
<AppLayoutBuiltInErrorBoundary appLayoutPart="screenreader-only-breadcrumbs">
<ScreenreaderOnly>{appLayoutPropsWithI18n.breadcrumbs}</ScreenreaderOnly>
</AppLayoutBuiltInErrorBoundary>
) : null}
Expand Down
2 changes: 1 addition & 1 deletion src/app-layout/visual-refresh-toolbar/state/index.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -58,7 +58,7 @@ export const AppLayoutStateProviderInternal = ({ appLayoutProps, stateManager, f

export const AppLayoutStateProvider = (props: AppLayoutStateProps) => {
return (
<AppLayoutBuiltInErrorBoundary>
<AppLayoutBuiltInErrorBoundary appLayoutPart="app-layout-state">
<AppLayoutStateProviderInternal {...props} />
</AppLayoutBuiltInErrorBoundary>
);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -189,7 +189,7 @@ export function DrawerTriggers({
const selected = !expandedDrawerId && item.id === activeDrawerId;
const isFeatureNotificationsDrawer = featureNotificationsProps?.drawer?.id === item.id;
return (
<AppLayoutBuiltInErrorBoundary key={item.id}>
<AppLayoutBuiltInErrorBoundary key={item.id} appLayoutPart="toolbar-trigger-drawer">
<TriggerButton
ariaLabel={item.ariaLabels?.triggerButton}
ariaExpanded={selected}
Expand Down Expand Up @@ -243,7 +243,7 @@ export function DrawerTriggers({
}

return (
<AppLayoutBuiltInErrorBoundary key={item.id}>
<AppLayoutBuiltInErrorBoundary key={item.id} appLayoutPart="toolbar-trigger-drawer">
<TriggerButton
ariaLabel={item.ariaLabels?.triggerButton}
ariaExpanded={selected}
Expand Down Expand Up @@ -282,7 +282,7 @@ export function DrawerTriggers({
);
})}
{overflowItems.length > 0 && (
<AppLayoutBuiltInErrorBoundary>
<AppLayoutBuiltInErrorBoundary appLayoutPart="toolbar-trigger-overflow-menu">
<OverflowMenu
items={overflowItems.map(item => {
const isBottom = item?.position === 'bottom';
Expand Down
6 changes: 3 additions & 3 deletions src/app-layout/visual-refresh-toolbar/toolbar/index.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -203,7 +203,7 @@ export function AppLayoutToolbarImplementation({
<ToolbarContainer hasAiDrawer={!!activeAiDrawerId}>
{hasNavigation && (
<nav {...navLandmarkAttributes} className={clsx(styles['universal-toolbar-nav'])}>
<AppLayoutBuiltInErrorBoundary>
<AppLayoutBuiltInErrorBoundary appLayoutPart="toolbar-trigger-nav">
<TriggerButton
ariaLabel={ariaLabels?.navigationToggle ?? undefined}
ariaExpanded={!drawerExpandedMode && navigationOpen}
Expand All @@ -226,7 +226,7 @@ export function AppLayoutToolbarImplementation({
</nav>
)}
{(breadcrumbs || discoveredBreadcrumbs) && (
<AppLayoutBuiltInErrorBoundary>
<AppLayoutBuiltInErrorBoundary appLayoutPart="breadcrumbs">
<ToolbarBreadcrumbsSection
ownBreadcrumbs={appLayoutInternals.breadcrumbs}
discoveredBreadcrumbs={appLayoutInternals.discoveredBreadcrumbs}
Expand All @@ -239,7 +239,7 @@ export function AppLayoutToolbarImplementation({
bottomDrawers?.length ||
(hasSplitPanel && splitPanelToggleProps?.displayed)) && (
<div className={clsx(styles['universal-toolbar-drawers'])}>
<AppLayoutBuiltInErrorBoundary>
<AppLayoutBuiltInErrorBoundary appLayoutPart="toolbar-triggers">
<DrawerTriggers
ariaLabels={ariaLabels}
activeDrawerId={activeDrawerId ?? null}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -64,7 +64,7 @@ export const AfterMainSlotImplementationInternal = ({ appLayoutState, appLayoutP
</AppLayoutSplitPanelSide>
</div>
)}
<AppLayoutBuiltInErrorBoundary>
<AppLayoutBuiltInErrorBoundary appLayoutPart="tools">
<div
className={clsx(
styles.tools,
Expand Down Expand Up @@ -93,7 +93,7 @@ export const AfterMainSlotImplementationInternal = ({ appLayoutState, appLayoutP
};

export const AfterMainSlotImplementation = (props: SkeletonPartProps) => (
<AppLayoutBuiltInErrorBoundary>
<AppLayoutBuiltInErrorBoundary appLayoutPart="after-main-slot">
<AfterMainSlotImplementationInternal {...props} />
</AppLayoutBuiltInErrorBoundary>
);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -123,7 +123,7 @@ export const BeforeMainSlotImplementationInternal = ({
(drawerExpandedMode || drawerExpandedModeInChildLayout) && styles.hidden
)}
>
<AppLayoutBuiltInErrorBoundary>
<AppLayoutBuiltInErrorBoundary appLayoutPart="nav">
<AppLayoutNavigation
appLayoutInternals={appLayoutState.appLayoutInternals}
bottomDrawerReportedSize={bottomDrawerReportedSize}
Expand All @@ -136,7 +136,7 @@ export const BeforeMainSlotImplementationInternal = ({
};

export const BeforeMainSlotImplementation = (props: SkeletonPartProps) => (
<AppLayoutBuiltInErrorBoundary>
<AppLayoutBuiltInErrorBoundary appLayoutPart="before-main-slot">
<BeforeMainSlotImplementationInternal {...props} />
</AppLayoutBuiltInErrorBoundary>
);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -42,7 +42,7 @@ export const BottomContentSlotImplementationInternal = ({ appLayoutState, appLay

export const BottomContentSlotImplementation = (props: SkeletonPartProps) => {
return (
<AppLayoutBuiltInErrorBoundary>
<AppLayoutBuiltInErrorBoundary appLayoutPart="bottom-content-slot">
<BottomContentSlotImplementationInternal {...props} />
</AppLayoutBuiltInErrorBoundary>
);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -36,7 +36,7 @@ export const TopContentSlotImplementationInternal = ({ appLayoutProps, appLayout
};

export const TopContentSlotImplementation = (props: SkeletonPartProps) => (
<AppLayoutBuiltInErrorBoundary>
<AppLayoutBuiltInErrorBoundary appLayoutPart="top-content-slot">
<TopContentSlotImplementationInternal {...props} />
</AppLayoutBuiltInErrorBoundary>
);
Expand Down
1 change: 1 addition & 0 deletions src/error-boundary/interfaces.ts
Original file line number Diff line number Diff line change
Expand Up @@ -112,4 +112,5 @@ export interface BuiltInErrorBoundaryProps {

export interface AppLayoutBuiltInErrorBoundaryProps extends BuiltInErrorBoundaryProps {
renderFallback?: ErrorBoundaryProps['renderFallback'];
appLayoutPart?: string;
}
7 changes: 6 additions & 1 deletion src/error-boundary/internal.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ import React, { Component, createContext, useContext, useState } from 'react';
import { useMergeRefs } from '@cloudscape-design/component-toolkit/internal';

import { InternalBaseComponentProps } from '../internal/hooks/use-base-component';
import { metrics } from '../internal/metrics';
import { SomeRequired } from '../internal/types';
import { ErrorBoundaryFallback } from './fallback';
import { AppLayoutBuiltInErrorBoundaryProps, BuiltInErrorBoundaryProps, ErrorBoundaryProps } from './interfaces';
Expand Down Expand Up @@ -92,6 +93,7 @@ export function AppLayoutBuiltInErrorBoundary({
suppressNested = false,
children,
renderFallback = () => <></>,
appLayoutPart,
}: AppLayoutBuiltInErrorBoundaryProps) {
const context = useContext(ErrorBoundariesContext);
const thisSuppressed = context.suppressed === true || context.suppressed === RootSuppressed;
Expand All @@ -104,7 +106,10 @@ export function AppLayoutBuiltInErrorBoundary({
className={styles['app-layout-part-fallback']}
onError={error => {
context?.onError?.(error);
// TODO Implement cloudscape error reporting
metrics.sendOpsMetricObject('awsui-app-layout-error-boundary-fired', {
errorMessage: error?.error?.message ?? '',
appLayoutPart: appLayoutPart ?? '',
});
}}
>
<ErrorBoundariesContext.Provider value={{ ...context, suppressed: nextSuppressed, renderFallback }}>
Expand Down
Loading