From 9af212cafe024b3cbc9e480db070a19481cbfe90 Mon Sep 17 00:00:00 2001 From: Georgii Lobko Date: Thu, 13 Mar 2025 14:50:17 +0100 Subject: [PATCH 01/43] Initial draft implementation --- pages/app-layout/utils/external-widget.tsx | 2 ++ src/app-layout/utils/use-drawers.ts | 3 +++ .../visual-refresh-toolbar/compute-layout.ts | 16 ++++++++++++++ .../drawer/global-drawer.tsx | 21 ++++++++++++++++++- .../visual-refresh-toolbar/drawer/styles.scss | 4 ++++ .../visual-refresh-toolbar/index.tsx | 6 ++++++ .../visual-refresh-toolbar/interfaces.ts | 4 +++- .../visual-refresh-toolbar/skeleton/index.tsx | 14 +++++++++++-- .../skeleton/styles.scss | 12 +++++++++++ src/internal/plugins/controllers/drawers.ts | 1 + 10 files changed, 79 insertions(+), 4 deletions(-) diff --git a/pages/app-layout/utils/external-widget.tsx b/pages/app-layout/utils/external-widget.tsx index 0edf5eefbd..8b6f202b31 100644 --- a/pages/app-layout/utils/external-widget.tsx +++ b/pages/app-layout/utils/external-widget.tsx @@ -210,6 +210,8 @@ awsuiPlugins.appLayout.registerDrawer({ resizable: true, defaultSize: 320, + isExpandable: true, + ariaLabels: { closeButton: 'Close button', content: 'Content', diff --git a/src/app-layout/utils/use-drawers.ts b/src/app-layout/utils/use-drawers.ts index f650b3e117..e900302a16 100644 --- a/src/app-layout/utils/use-drawers.ts +++ b/src/app-layout/utils/use-drawers.ts @@ -199,6 +199,7 @@ export function useDrawers( }); const [activeGlobalDrawersIds, setActiveGlobalDrawersIds] = useState>([]); const [drawerSizes, setDrawerSizes] = useState>({}); + const [expandedDrawerId, setExpandedDrawerId] = useState(undefined); // FIFO queue that keeps track of open drawers, where the first element is the most recently opened drawer const drawersOpenQueue = useRef>([]); @@ -335,5 +336,7 @@ export function useDrawers( onActiveDrawerChange, onActiveDrawerResize, onActiveGlobalDrawersChange, + expandedDrawerId, + setExpandedDrawerId, }; } diff --git a/src/app-layout/visual-refresh-toolbar/compute-layout.ts b/src/app-layout/visual-refresh-toolbar/compute-layout.ts index 084efdbbbb..ae279d8878 100644 --- a/src/app-layout/visual-refresh-toolbar/compute-layout.ts +++ b/src/app-layout/visual-refresh-toolbar/compute-layout.ts @@ -15,6 +15,7 @@ interface HorizontalLayoutInput { splitPanelSize: number; isMobile: boolean; activeGlobalDrawersSizes: Record; + expandedDrawerId?: string; } export const CONTENT_PADDING = 2 * 24; // space-xl @@ -30,6 +31,7 @@ export function computeHorizontalLayout({ splitPanelSize, isMobile, activeGlobalDrawersSizes, + expandedDrawerId, }: HorizontalLayoutInput) { const activeNavigationWidth = navigationOpen ? navigationWidth : 0; @@ -51,6 +53,20 @@ export function computeHorizontalLayout({ const maxDrawerSize = resizableSpaceAvailable - totalActiveGlobalDrawersSize; const maxGlobalDrawersSizes: Record = Object.keys(activeGlobalDrawersSizes).reduce( (acc, drawerId) => { + if (drawerId === expandedDrawerId) { + return { + ...acc, + [drawerId]: placement.inlineSize, + }; + } + if (expandedDrawerId && drawerId !== expandedDrawerId) { + if (drawerId === expandedDrawerId) { + return { + ...acc, + [drawerId]: resizableSpaceAvailable, + }; + } + } return { ...acc, [drawerId]: diff --git a/src/app-layout/visual-refresh-toolbar/drawer/global-drawer.tsx b/src/app-layout/visual-refresh-toolbar/drawer/global-drawer.tsx index ba6d185369..51748575ef 100644 --- a/src/app-layout/visual-refresh-toolbar/drawer/global-drawer.tsx +++ b/src/app-layout/visual-refresh-toolbar/drawer/global-drawer.tsx @@ -39,6 +39,8 @@ function AppLayoutGlobalDrawerImplementation({ activeGlobalDrawersSizes, verticalOffsets, drawersOpenQueue, + expandedDrawerId, + setExpandedDrawerId, } = appLayoutInternals; const drawerRef = useRef(null); const activeDrawerId = activeGlobalDrawer?.id ?? ''; @@ -65,6 +67,7 @@ function AppLayoutGlobalDrawerImplementation({ const lastOpenedDrawerId = drawersOpenQueue.length ? drawersOpenQueue[0] : null; const hasTriggerButton = !!activeGlobalDrawer?.trigger; const animationDisabled = activeGlobalDrawer?.defaultActive && !drawersOpenQueue.includes(activeGlobalDrawer.id); + const isExpanded = activeGlobalDrawer?.isExpandable && expandedDrawerId === activeDrawerId; return ( @@ -83,6 +86,7 @@ function AppLayoutGlobalDrawerImplementation({ [styles['drawer-hidden']]: !show, [styles['last-opened']]: lastOpenedDrawerId === activeDrawerId, [testutilStyles['active-drawer']]: show, + [styles['drawer-expanded']]: isExpanded, } )} ref={drawerRef} @@ -106,10 +110,15 @@ function AppLayoutGlobalDrawerImplementation({ ...(!isMobile && { [customCssProps.drawerSize]: `${['entering', 'entered'].includes(state) ? size : 0}px`, }), + ...(isExpanded && { + inlineSize: `calc(100% - ${Object.keys(activeGlobalDrawersSizes) + .filter(drawerId => drawerId !== activeDrawerId) + .reduce((acc, drawerId) => acc + activeGlobalDrawersSizes[drawerId], 0)}px)`, + }), }} data-testid={`awsui-app-layout-drawer-${activeDrawerId}`} > - {!isMobile && activeGlobalDrawer?.resizable && ( + {!isMobile && activeGlobalDrawer?.resizable && !isExpanded && (
+ {activeGlobalDrawer?.isExpandable && expandedDrawerId !== activeDrawerId && ( + + )} + {isExpanded && } {activeGlobalDrawer?.content}
diff --git a/src/app-layout/visual-refresh-toolbar/drawer/styles.scss b/src/app-layout/visual-refresh-toolbar/drawer/styles.scss index afbdc70801..6d9a3b8bf3 100644 --- a/src/app-layout/visual-refresh-toolbar/drawer/styles.scss +++ b/src/app-layout/visual-refresh-toolbar/drawer/styles.scss @@ -59,6 +59,10 @@ display: none; } + &.drawer-expanded { + order: -1; + } + > .drawer-content-container { grid-column: 1 / span 2; grid-row: 1; diff --git a/src/app-layout/visual-refresh-toolbar/index.tsx b/src/app-layout/visual-refresh-toolbar/index.tsx index 0bf8253873..b541692759 100644 --- a/src/app-layout/visual-refresh-toolbar/index.tsx +++ b/src/app-layout/visual-refresh-toolbar/index.tsx @@ -151,6 +151,8 @@ const AppLayoutVisualRefreshToolbar = React.forwardRef void; onActiveGlobalDrawersChange: (newDrawerId: string, params: OnChangeParams) => void; splitPanelAnimationDisabled?: boolean; + expandedDrawerId: string | undefined; + setExpandedDrawerId: (value: string | undefined) => void; } diff --git a/src/app-layout/visual-refresh-toolbar/skeleton/index.tsx b/src/app-layout/visual-refresh-toolbar/skeleton/index.tsx index 05deec3818..958d3e1361 100644 --- a/src/app-layout/visual-refresh-toolbar/skeleton/index.tsx +++ b/src/app-layout/visual-refresh-toolbar/skeleton/index.tsx @@ -41,6 +41,7 @@ interface SkeletonLayoutProps globalToolsOpen?: boolean; navigationAnimationDisabled?: boolean; isNested?: boolean; + drawerFocusMode: boolean; } export const SkeletonLayout = React.forwardRef( @@ -69,6 +70,7 @@ export const SkeletonLayout = React.forwardRef { @@ -81,6 +83,7 @@ export const SkeletonLayout = React.forwardRef {navigation} )} -
+
{notifications && (
Date: Tue, 25 Mar 2025 14:48:26 +0100 Subject: [PATCH 02/43] feat: Focus mode button --- src/app-layout/__tests__/utils.tsx | 6 +++++ src/app-layout/interfaces.ts | 1 + .../drawer/global-drawer.tsx | 23 +++++++++++-------- .../visual-refresh-toolbar/drawer/styles.scss | 9 ++++++++ .../visual-refresh-toolbar/skeleton/index.tsx | 3 ++- 5 files changed, 31 insertions(+), 11 deletions(-) diff --git a/src/app-layout/__tests__/utils.tsx b/src/app-layout/__tests__/utils.tsx index 24963823ad..063e4b6397 100644 --- a/src/app-layout/__tests__/utils.tsx +++ b/src/app-layout/__tests__/utils.tsx @@ -195,5 +195,11 @@ export const getGlobalDrawersTestUtils = (wrapper: AppLayoutWrapper) => { `.${testutilStyles['active-drawer']}[data-testid="awsui-app-layout-drawer-${id}"] .${testutilStyles['active-drawer-close-button']}` ); }, + + findFocusModeButtonByActiveDrawerId(id: string): ElementWrapper | null { + return wrapper.find( + `.${testutilStyles['active-drawer']}[data-testid="awsui-app-layout-drawer-${id}"] .${testutilStyles['active-drawer-focus-mode-button']}` + ); + }, }; }; diff --git a/src/app-layout/interfaces.ts b/src/app-layout/interfaces.ts index 58c4b446ae..55825e2e3b 100644 --- a/src/app-layout/interfaces.ts +++ b/src/app-layout/interfaces.ts @@ -317,6 +317,7 @@ export namespace AppLayoutProps { closeButton?: string; triggerButton?: string; resizeHandle?: string; + focusModeButton?: string; } export interface Labels { diff --git a/src/app-layout/visual-refresh-toolbar/drawer/global-drawer.tsx b/src/app-layout/visual-refresh-toolbar/drawer/global-drawer.tsx index 51748575ef..d16d164f71 100644 --- a/src/app-layout/visual-refresh-toolbar/drawer/global-drawer.tsx +++ b/src/app-layout/visual-refresh-toolbar/drawer/global-drawer.tsx @@ -135,6 +135,19 @@ function AppLayoutGlobalDrawerImplementation({ className={clsx(styles['drawer-content-container'], sharedStyles['with-motion-horizontal'])} data-testid={`awsui-app-layout-drawer-content-${activeDrawerId}`} > + {activeGlobalDrawer?.isExpandable && ( +
+ setExpandedDrawerId(isExpanded ? undefined : activeDrawerId)} + variant="icon" + /> +
+ )}
- {activeGlobalDrawer?.isExpandable && expandedDrawerId !== activeDrawerId && ( - - )} - {isExpanded && } {activeGlobalDrawer?.content}
diff --git a/src/app-layout/visual-refresh-toolbar/drawer/styles.scss b/src/app-layout/visual-refresh-toolbar/drawer/styles.scss index 6d9a3b8bf3..6589aa47e6 100644 --- a/src/app-layout/visual-refresh-toolbar/drawer/styles.scss +++ b/src/app-layout/visual-refresh-toolbar/drawer/styles.scss @@ -78,6 +78,15 @@ align-self: start; } + > .drawer-focus-mode-button { + grid-column: 2; + grid-row: 2; + z-index: 1; + align-self: start; + display: flex; + justify-content: end; + } + > .drawer-content { grid-column: 1 / span 4; grid-row: 1 / span 2; diff --git a/src/app-layout/visual-refresh-toolbar/skeleton/index.tsx b/src/app-layout/visual-refresh-toolbar/skeleton/index.tsx index 958d3e1361..223144c8db 100644 --- a/src/app-layout/visual-refresh-toolbar/skeleton/index.tsx +++ b/src/app-layout/visual-refresh-toolbar/skeleton/index.tsx @@ -146,7 +146,8 @@ export const SkeletonLayout = React.forwardRef {tools} From 3335806061683c3baa2ffabb847758e106323517 Mon Sep 17 00:00:00 2001 From: Georgii Lobko Date: Tue, 25 Mar 2025 14:52:33 +0100 Subject: [PATCH 03/43] chore: Make one more drawer expandable --- pages/app-layout/utils/external-widget.tsx | 2 ++ 1 file changed, 2 insertions(+) diff --git a/pages/app-layout/utils/external-widget.tsx b/pages/app-layout/utils/external-widget.tsx index 8b6f202b31..d24c394856 100644 --- a/pages/app-layout/utils/external-widget.tsx +++ b/pages/app-layout/utils/external-widget.tsx @@ -130,6 +130,8 @@ awsuiPlugins.appLayout.registerDrawer({ defaultSize: 350, preserveInactiveContent: true, + isExpandable: true, + ariaLabels: { closeButton: 'Close button', content: 'Content', From b6956ccf324dd78c0efbf69bda2fb37c5dc31675 Mon Sep 17 00:00:00 2001 From: Georgii Lobko Date: Wed, 26 Mar 2025 13:28:09 +0100 Subject: [PATCH 04/43] chore: Quit focus mode on closing drawer --- .../__tests__/runtime-drawers.test.tsx | 76 +++++++++++++++++++ src/app-layout/__tests__/utils.tsx | 9 +++ src/app-layout/test-classes/styles.scss | 3 +- src/app-layout/utils/use-drawers.ts | 3 + 4 files changed, 90 insertions(+), 1 deletion(-) diff --git a/src/app-layout/__tests__/runtime-drawers.test.tsx b/src/app-layout/__tests__/runtime-drawers.test.tsx index e0ab8ec457..01479efa84 100644 --- a/src/app-layout/__tests__/runtime-drawers.test.tsx +++ b/src/app-layout/__tests__/runtime-drawers.test.tsx @@ -1348,6 +1348,82 @@ describe('toolbar mode only features', () => { expect(globalDrawersWrapper.findDrawerById('global3')).toBeFalsy(); }); }); + + describe('focus mode for global drawers', () => { + test('should set a drawer to focus mode by clicking on "focus mode" button', async () => { + const drawerId = 'global-drawer'; + awsuiPlugins.appLayout.registerDrawer({ + ...drawerDefaults, + id: drawerId, + type: 'global', + isExpandable: true, + }); + const { wrapper, globalDrawersWrapper } = await renderComponent(); + + await delay(); + + wrapper.findDrawerTriggerById(drawerId)!.click(); + expect(globalDrawersWrapper.findFocusModeButtonByActiveDrawerId(drawerId)!.getElement()).toBeInTheDocument(); + expect(globalDrawersWrapper.findDrawerById(drawerId)!.isDrawerInFocusMode()).toBe(false); + globalDrawersWrapper.findFocusModeButtonByActiveDrawerId(drawerId)!.click(); + expect(globalDrawersWrapper.findDrawerById(drawerId)!.isDrawerInFocusMode()).toBe(true); + }); + + test('should switch focus mode between two global drawers', async () => { + const drawerId1 = 'global-drawer1'; + const drawerId2 = 'global-drawer2'; + awsuiPlugins.appLayout.registerDrawer({ + ...drawerDefaults, + id: drawerId1, + type: 'global', + isExpandable: true, + }); + awsuiPlugins.appLayout.registerDrawer({ + ...drawerDefaults, + id: drawerId2, + type: 'global', + isExpandable: true, + }); + const { wrapper, globalDrawersWrapper } = await renderComponent(); + + await delay(); + + wrapper.findDrawerTriggerById(drawerId1)!.click(); + wrapper.findDrawerTriggerById(drawerId2)!.click(); + expect(globalDrawersWrapper.findDrawerById(drawerId1)!.isActive()).toBe(true); + expect(globalDrawersWrapper.findDrawerById(drawerId2)!.isActive()).toBe(true); + expect(globalDrawersWrapper.isLayoutInFocusMode()).toBe(false); + expect(globalDrawersWrapper.findDrawerById(drawerId1)!.isDrawerInFocusMode()).toBe(false); + expect(globalDrawersWrapper.findDrawerById(drawerId2)!.isDrawerInFocusMode()).toBe(false); + globalDrawersWrapper.findFocusModeButtonByActiveDrawerId(drawerId1)!.click(); + expect(globalDrawersWrapper.findDrawerById(drawerId1)!.isDrawerInFocusMode()).toBe(true); + expect(globalDrawersWrapper.findDrawerById(drawerId2)!.isDrawerInFocusMode()).toBe(false); + globalDrawersWrapper.findFocusModeButtonByActiveDrawerId(drawerId2)!.click(); + expect(globalDrawersWrapper.findDrawerById(drawerId1)!.isDrawerInFocusMode()).toBe(false); + expect(globalDrawersWrapper.findDrawerById(drawerId2)!.isDrawerInFocusMode()).toBe(true); + expect(globalDrawersWrapper.isLayoutInFocusMode()).toBe(true); + }); + + test('should quit focus mode when a drawer in focus mode is closed', async () => { + const drawerId = 'global-drawer'; + awsuiPlugins.appLayout.registerDrawer({ + ...drawerDefaults, + id: drawerId, + type: 'global', + isExpandable: true, + }); + const { wrapper, globalDrawersWrapper } = await renderComponent(); + + await delay(); + + wrapper.findDrawerTriggerById(drawerId)!.click(); + globalDrawersWrapper.findFocusModeButtonByActiveDrawerId(drawerId)!.click(); + expect(globalDrawersWrapper.findDrawerById(drawerId)!.isDrawerInFocusMode()).toBe(true); + expect(globalDrawersWrapper.isLayoutInFocusMode()).toBe(true); + wrapper.findDrawerTriggerById(drawerId)!.click(); + expect(globalDrawersWrapper.isLayoutInFocusMode()).toBe(false); + }); + }); }); describeEachAppLayout({ themes: ['refresh-toolbar'], sizes: ['mobile'] }, () => { diff --git a/src/app-layout/__tests__/utils.tsx b/src/app-layout/__tests__/utils.tsx index 063e4b6397..7f2b7716db 100644 --- a/src/app-layout/__tests__/utils.tsx +++ b/src/app-layout/__tests__/utils.tsx @@ -14,6 +14,7 @@ import { forceMobileModeSymbol } from '../../internal/hooks/use-mobile'; import testutilStyles from '../../../lib/components/app-layout/test-classes/styles.css.js'; import visualRefreshStyles from '../../../lib/components/app-layout/visual-refresh/styles.css.js'; +import globalDrawerStyles from '../../../lib/components/app-layout/visual-refresh-toolbar/drawer/styles.css.js'; import visualRefreshToolbarStyles from '../../../lib/components/app-layout/visual-refresh-toolbar/skeleton/styles.css.js'; export function renderComponent(jsx: React.ReactElement) { @@ -167,6 +168,10 @@ class AppLayoutDrawerWrapper extends ComponentWrapper { isActive(): boolean { return this.element.classList.contains(testutilStyles['active-drawer']); } + + isDrawerInFocusMode(): boolean { + return this.element.classList.contains(globalDrawerStyles['drawer-expanded']); + } } export const getGlobalDrawersTestUtils = (wrapper: AppLayoutWrapper) => { @@ -201,5 +206,9 @@ export const getGlobalDrawersTestUtils = (wrapper: AppLayoutWrapper) => { `.${testutilStyles['active-drawer']}[data-testid="awsui-app-layout-drawer-${id}"] .${testutilStyles['active-drawer-focus-mode-button']}` ); }, + + isLayoutInFocusMode(): boolean { + return !!wrapper.matches(`.${visualRefreshToolbarStyles['drawer-focus-mode']}`); + }, }; }; diff --git a/src/app-layout/test-classes/styles.scss b/src/app-layout/test-classes/styles.scss index 1ad00cb742..50d4550ed3 100644 --- a/src/app-layout/test-classes/styles.scss +++ b/src/app-layout/test-classes/styles.scss @@ -25,6 +25,7 @@ .drawers-slider, .toolbar, .trigger-wrapper-tooltip-visible, -.trigger-tooltip { +.trigger-tooltip, +.active-drawer-focus-mode-button { /* used in test-utils */ } diff --git a/src/app-layout/utils/use-drawers.ts b/src/app-layout/utils/use-drawers.ts index e900302a16..f508fbeb6e 100644 --- a/src/app-layout/utils/use-drawers.ts +++ b/src/app-layout/utils/use-drawers.ts @@ -251,6 +251,9 @@ export function useDrawers( onGlobalDrawerFocus?.(drawerId, false); drawersOpenQueue.current = drawersOpenQueue.current.filter(id => id !== drawerId); fireNonCancelableEvent(drawer?.onToggle, { isOpen: false, initiatedByUserAction }); + if (drawerId === expandedDrawerId) { + setExpandedDrawerId(undefined); + } } else if (drawerId) { onAddNewActiveDrawer?.(drawerId); setActiveGlobalDrawersIds(currentState => [drawerId, ...currentState].slice(0, DRAWERS_LIMIT!)); From 1402ee4e247d3c0d7a339d4aa2149056a6dc9965 Mon Sep 17 00:00:00 2001 From: Georgii Lobko Date: Fri, 28 Mar 2025 14:46:56 +0100 Subject: [PATCH 05/43] feat: Preserve local drawers, split panel and nav state in the focus mode --- package-lock.json | 14 ++++++---- package.json | 2 +- .../toolbar/drawer-triggers.tsx | 28 +++++++++++++------ .../visual-refresh-toolbar/toolbar/index.tsx | 15 ++++++++-- 4 files changed, 42 insertions(+), 17 deletions(-) diff --git a/package-lock.json b/package-lock.json index 63e057cf52..a2a10ec8c4 100644 --- a/package-lock.json +++ b/package-lock.json @@ -16993,7 +16993,9 @@ } }, "node_modules/rxjs": { - "version": "7.8.1", + "version": "7.8.2", + "resolved": "https://registry.npmjs.org/rxjs/-/rxjs-7.8.2.tgz", + "integrity": "sha512-dhKf903U/PQZY6boNNtAGdWbG85WAbjT/1xYoZIC7FAY0yWapOBQVsVrDl58W86//e1VpMNBtRV4MaXfdMySFA==", "dev": true, "license": "Apache-2.0", "dependencies": { @@ -19662,17 +19664,17 @@ } }, "node_modules/wait-on": { - "version": "8.0.2", - "resolved": "https://registry.npmjs.org/wait-on/-/wait-on-8.0.2.tgz", - "integrity": "sha512-qHlU6AawrgAIHlueGQHQ+ETcPLAauXbnoTKl3RKq20W0T8x0DKVAo5xWIYjHSyvHxQlcYbFdR0jp4T9bDVITFA==", + "version": "8.0.3", + "resolved": "https://registry.npmjs.org/wait-on/-/wait-on-8.0.3.tgz", + "integrity": "sha512-nQFqAFzZDeRxsu7S3C7LbuxslHhk+gnJZHyethuGKAn2IVleIbTB9I3vJSQiSR+DifUqmdzfPMoMPJfLqMF2vw==", "dev": true, "license": "MIT", "dependencies": { - "axios": "^1.7.9", + "axios": "^1.8.2", "joi": "^17.13.3", "lodash": "^4.17.21", "minimist": "^1.2.8", - "rxjs": "^7.8.1" + "rxjs": "^7.8.2" }, "bin": { "wait-on": "bin/wait-on" diff --git a/package.json b/package.json index 07caf7d635..5361ac0cde 100644 --- a/package.json +++ b/package.json @@ -165,7 +165,7 @@ { "path": "lib/components/internal/widget-exports.js", "brotli": false, - "limit": "750 kB" + "limit": "751 kB" } ], "browserslist": [ diff --git a/src/app-layout/visual-refresh-toolbar/toolbar/drawer-triggers.tsx b/src/app-layout/visual-refresh-toolbar/toolbar/drawer-triggers.tsx index bc3a007728..3eba8ed080 100644 --- a/src/app-layout/visual-refresh-toolbar/toolbar/drawer-triggers.tsx +++ b/src/app-layout/visual-refresh-toolbar/toolbar/drawer-triggers.tsx @@ -37,6 +37,8 @@ interface DrawerTriggersProps { globalDrawersFocusControl?: FocusControlMultipleStates; globalDrawers: ReadonlyArray; onActiveGlobalDrawersChange?: (newDrawerId: string, params: OnChangeParams) => void; + drawerFocusMode?: boolean; + setExpandedDrawerId: (value: string | undefined) => void; splitPanelOpen?: boolean; splitPanelPosition?: AppLayoutProps.SplitPanelPreferences['position']; @@ -62,6 +64,8 @@ export function DrawerTriggers({ globalDrawers, globalDrawersFocusControl, onActiveGlobalDrawersChange, + drawerFocusMode, + setExpandedDrawerId, }: DrawerTriggersProps) { const isMobile = useMobile(); const hasMultipleTriggers = drawers.length > 1; @@ -130,15 +134,18 @@ export function DrawerTriggers({ onSplitPanelToggle?.()} - selected={splitPanelToggleProps.active} + onClick={() => { + setExpandedDrawerId(undefined); + onSplitPanelToggle?.(); + }} + selected={!drawerFocusMode && splitPanelToggleProps.active} ref={splitPanelResolvedPosition === 'side' ? splitPanelFocusRef : undefined} hasTooltip={true} isMobile={isMobile} @@ -150,10 +157,11 @@ export function DrawerTriggers({ )} {visibleItems.slice(0, globalDrawersStartIndex).map(item => { const isForPreviousActiveDrawer = previousActiveLocalDrawerId?.current === item.id; + const selected = !drawerFocusMode && item.id === activeDrawerId; return ( - onActiveDrawerChange?.(activeDrawerId !== item.id ? item.id : null, { initiatedByUserAction: true }) - } + onClick={() => { + setExpandedDrawerId(undefined); + if (activeDrawerId === item.id) { + return; + } + onActiveDrawerChange?.(activeDrawerId !== item.id ? item.id : null, { initiatedByUserAction: true }); + }} ref={item.id === previousActiveLocalDrawerId.current ? drawersFocusRef : undefined} - selected={item.id === activeDrawerId} + selected={selected} badge={item.badge} testId={`awsui-app-layout-trigger-${item.id}`} hasTooltip={true} diff --git a/src/app-layout/visual-refresh-toolbar/toolbar/index.tsx b/src/app-layout/visual-refresh-toolbar/toolbar/index.tsx index d626ecc761..0aa1b0e985 100644 --- a/src/app-layout/visual-refresh-toolbar/toolbar/index.tsx +++ b/src/app-layout/visual-refresh-toolbar/toolbar/index.tsx @@ -68,6 +68,8 @@ export function AppLayoutToolbarImplementation({ toolbarState, setToolbarState, setToolbarHeight, + expandedDrawerId, + setExpandedDrawerId, } = appLayoutInternals; const { ariaLabels, @@ -90,6 +92,7 @@ export function AppLayoutToolbarImplementation({ } = toolbarProps; // TODO: expose configuration property const pinnedToolbar = true; + const drawerFocusMode = !!expandedDrawerId; const ref = useRef(null); useResizeObserver(ref, entry => setToolbarHeight(entry.borderBoxHeight)); useEffect(() => { @@ -161,9 +164,15 @@ export function AppLayoutToolbarImplementation({ ariaExpanded={false} iconName="menu" className={testutilStyles['navigation-toggle']} - onClick={() => onNavigationToggle?.(!navigationOpen)} + onClick={() => { + setExpandedDrawerId(undefined); + if (navigationOpen) { + return; + } + onNavigationToggle?.(!navigationOpen); + }} ref={navigationFocusRef} - selected={navigationOpen} + selected={!drawerFocusMode && navigationOpen} disabled={anyPanelOpenInMobile} /> @@ -192,6 +201,8 @@ export function AppLayoutToolbarImplementation({ globalDrawers={globalDrawers?.filter(item => !!item.trigger) ?? []} activeGlobalDrawersIds={activeGlobalDrawersIds ?? []} onActiveGlobalDrawersChange={onActiveGlobalDrawersChange} + drawerFocusMode={drawerFocusMode} + setExpandedDrawerId={setExpandedDrawerId} /> )} From 4fbf9c1d3ccd4ad12b5b32ed785712152f051c6c Mon Sep 17 00:00:00 2001 From: Georgii Lobko Date: Fri, 28 Mar 2025 14:55:00 +0100 Subject: [PATCH 06/43] fix: Size limit in package.json --- package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/package.json b/package.json index 5361ac0cde..aa5bd2e830 100644 --- a/package.json +++ b/package.json @@ -165,7 +165,7 @@ { "path": "lib/components/internal/widget-exports.js", "brotli": false, - "limit": "751 kB" + "limit": "800 kB" } ], "browserslist": [ From 23eff87997ffd0309c9717263b473f9ca5f76ee3 Mon Sep 17 00:00:00 2001 From: Georgii Lobko Date: Fri, 28 Mar 2025 15:45:13 +0100 Subject: [PATCH 07/43] upd snapshots --- .../widget-contract-split-panel.test.tsx.snap | 20 ++++++++++++ .../widget-contract.test.tsx.snap | 32 +++++++++++++++++++ 2 files changed, 52 insertions(+) diff --git a/src/app-layout/__tests__/__snapshots__/widget-contract-split-panel.test.tsx.snap b/src/app-layout/__tests__/__snapshots__/widget-contract-split-panel.test.tsx.snap index ae87427ab1..17c7127f0e 100644 --- a/src/app-layout/__tests__/__snapshots__/widget-contract-split-panel.test.tsx.snap +++ b/src/app-layout/__tests__/__snapshots__/widget-contract-split-panel.test.tsx.snap @@ -56,6 +56,7 @@ Map { "setFocus": [Function], }, "drawersOpenQueue": [], + "expandedDrawerId": undefined, "globalDrawers": [], "globalDrawersFocusControl": { "loseFocus": [Function], @@ -97,6 +98,7 @@ Map { "insetInlineEnd": 0, "insetInlineStart": 0, }, + "setExpandedDrawerId": [Function], "setNotificationsHeight": [Function], "setToolbarHeight": [Function], "setToolbarState": [Function], @@ -186,6 +188,7 @@ Map { "setFocus": [Function], }, "drawersOpenQueue": [], + "expandedDrawerId": undefined, "globalDrawers": [], "globalDrawersFocusControl": { "loseFocus": [Function], @@ -227,6 +230,7 @@ Map { "insetInlineEnd": 0, "insetInlineStart": 0, }, + "setExpandedDrawerId": [Function], "setNotificationsHeight": [Function], "setToolbarHeight": [Function], "setToolbarState": [Function], @@ -368,6 +372,7 @@ Map { "setFocus": [Function], }, "drawersOpenQueue": [], + "expandedDrawerId": undefined, "globalDrawers": [], "globalDrawersFocusControl": { "loseFocus": [Function], @@ -409,6 +414,7 @@ Map { "insetInlineEnd": 0, "insetInlineStart": 0, }, + "setExpandedDrawerId": [Function], "setNotificationsHeight": [Function], "setToolbarHeight": [Function], "setToolbarState": [Function], @@ -498,6 +504,7 @@ Map { "setFocus": [Function], }, "drawersOpenQueue": [], + "expandedDrawerId": undefined, "globalDrawers": [], "globalDrawersFocusControl": { "loseFocus": [Function], @@ -539,6 +546,7 @@ Map { "insetInlineEnd": 0, "insetInlineStart": 0, }, + "setExpandedDrawerId": [Function], "setNotificationsHeight": [Function], "setToolbarHeight": [Function], "setToolbarState": [Function], @@ -628,6 +636,7 @@ Map { "setFocus": [Function], }, "drawersOpenQueue": [], + "expandedDrawerId": undefined, "globalDrawers": [], "globalDrawersFocusControl": { "loseFocus": [Function], @@ -669,6 +678,7 @@ Map { "insetInlineEnd": 0, "insetInlineStart": 0, }, + "setExpandedDrawerId": [Function], "setNotificationsHeight": [Function], "setToolbarHeight": [Function], "setToolbarState": [Function], @@ -801,6 +811,7 @@ Map { "setFocus": [Function], }, "drawersOpenQueue": [], + "expandedDrawerId": undefined, "globalDrawers": [], "globalDrawersFocusControl": { "loseFocus": [Function], @@ -844,6 +855,7 @@ Map { "insetInlineEnd": 0, "insetInlineStart": 0, }, + "setExpandedDrawerId": [Function], "setNotificationsHeight": [Function], "setToolbarHeight": [Function], "setToolbarState": [Function], @@ -935,6 +947,7 @@ Map { "setFocus": [Function], }, "drawersOpenQueue": [], + "expandedDrawerId": undefined, "globalDrawers": [], "globalDrawersFocusControl": { "loseFocus": [Function], @@ -978,6 +991,7 @@ Map { "insetInlineEnd": 0, "insetInlineStart": 0, }, + "setExpandedDrawerId": [Function], "setNotificationsHeight": [Function], "setToolbarHeight": [Function], "setToolbarState": [Function], @@ -1123,6 +1137,7 @@ Map { "setFocus": [Function], }, "drawersOpenQueue": [], + "expandedDrawerId": undefined, "globalDrawers": [], "globalDrawersFocusControl": { "loseFocus": [Function], @@ -1166,6 +1181,7 @@ Map { "insetInlineEnd": 0, "insetInlineStart": 0, }, + "setExpandedDrawerId": [Function], "setNotificationsHeight": [Function], "setToolbarHeight": [Function], "setToolbarState": [Function], @@ -1257,6 +1273,7 @@ Map { "setFocus": [Function], }, "drawersOpenQueue": [], + "expandedDrawerId": undefined, "globalDrawers": [], "globalDrawersFocusControl": { "loseFocus": [Function], @@ -1300,6 +1317,7 @@ Map { "insetInlineEnd": 0, "insetInlineStart": 0, }, + "setExpandedDrawerId": [Function], "setNotificationsHeight": [Function], "setToolbarHeight": [Function], "setToolbarState": [Function], @@ -1391,6 +1409,7 @@ Map { "setFocus": [Function], }, "drawersOpenQueue": [], + "expandedDrawerId": undefined, "globalDrawers": [], "globalDrawersFocusControl": { "loseFocus": [Function], @@ -1434,6 +1453,7 @@ Map { "insetInlineEnd": 0, "insetInlineStart": 0, }, + "setExpandedDrawerId": [Function], "setNotificationsHeight": [Function], "setToolbarHeight": [Function], "setToolbarState": [Function], diff --git a/src/app-layout/__tests__/__snapshots__/widget-contract.test.tsx.snap b/src/app-layout/__tests__/__snapshots__/widget-contract.test.tsx.snap index ce4f838b1a..fa18bda247 100644 --- a/src/app-layout/__tests__/__snapshots__/widget-contract.test.tsx.snap +++ b/src/app-layout/__tests__/__snapshots__/widget-contract.test.tsx.snap @@ -54,6 +54,7 @@ Map { "setFocus": [Function], }, "drawersOpenQueue": [], + "expandedDrawerId": undefined, "globalDrawers": [], "globalDrawersFocusControl": { "loseFocus": [Function], @@ -95,6 +96,7 @@ Map { "insetInlineEnd": 0, "insetInlineStart": 0, }, + "setExpandedDrawerId": [Function], "setNotificationsHeight": [Function], "setToolbarHeight": [Function], "setToolbarState": [Function], @@ -182,6 +184,7 @@ Map { "setFocus": [Function], }, "drawersOpenQueue": [], + "expandedDrawerId": undefined, "globalDrawers": [], "globalDrawersFocusControl": { "loseFocus": [Function], @@ -223,6 +226,7 @@ Map { "insetInlineEnd": 0, "insetInlineStart": 0, }, + "setExpandedDrawerId": [Function], "setNotificationsHeight": [Function], "setToolbarHeight": [Function], "setToolbarState": [Function], @@ -341,6 +345,7 @@ Map { "setFocus": [Function], }, "drawersOpenQueue": [], + "expandedDrawerId": undefined, "globalDrawers": [], "globalDrawersFocusControl": { "loseFocus": [Function], @@ -382,6 +387,7 @@ Map { "insetInlineEnd": 0, "insetInlineStart": 0, }, + "setExpandedDrawerId": [Function], "setNotificationsHeight": [Function], "setToolbarHeight": [Function], "setToolbarState": [Function], @@ -469,6 +475,7 @@ Map { "setFocus": [Function], }, "drawersOpenQueue": [], + "expandedDrawerId": undefined, "globalDrawers": [], "globalDrawersFocusControl": { "loseFocus": [Function], @@ -510,6 +517,7 @@ Map { "insetInlineEnd": 0, "insetInlineStart": 0, }, + "setExpandedDrawerId": [Function], "setNotificationsHeight": [Function], "setToolbarHeight": [Function], "setToolbarState": [Function], @@ -597,6 +605,7 @@ Map { "setFocus": [Function], }, "drawersOpenQueue": [], + "expandedDrawerId": undefined, "globalDrawers": [], "globalDrawersFocusControl": { "loseFocus": [Function], @@ -638,6 +647,7 @@ Map { "insetInlineEnd": 0, "insetInlineStart": 0, }, + "setExpandedDrawerId": [Function], "setNotificationsHeight": [Function], "setToolbarHeight": [Function], "setToolbarState": [Function], @@ -774,6 +784,7 @@ Map { "setFocus": [Function], }, "drawersOpenQueue": [], + "expandedDrawerId": undefined, "globalDrawers": [], "globalDrawersFocusControl": { "loseFocus": [Function], @@ -817,6 +828,7 @@ Map { "insetInlineEnd": 0, "insetInlineStart": 0, }, + "setExpandedDrawerId": [Function], "setNotificationsHeight": [Function], "setToolbarHeight": [Function], "setToolbarState": [Function], @@ -908,6 +920,7 @@ Map { "setFocus": [Function], }, "drawersOpenQueue": [], + "expandedDrawerId": undefined, "globalDrawers": [], "globalDrawersFocusControl": { "loseFocus": [Function], @@ -951,6 +964,7 @@ Map { "insetInlineEnd": 0, "insetInlineStart": 0, }, + "setExpandedDrawerId": [Function], "setNotificationsHeight": [Function], "setToolbarHeight": [Function], "setToolbarState": [Function], @@ -1045,6 +1059,7 @@ Map { "setFocus": [Function], }, "drawersOpenQueue": [], + "expandedDrawerId": undefined, "globalDrawers": [], "globalDrawersFocusControl": { "loseFocus": [Function], @@ -1088,6 +1103,7 @@ Map { "insetInlineEnd": 0, "insetInlineStart": 0, }, + "setExpandedDrawerId": [Function], "setNotificationsHeight": [Function], "setToolbarHeight": [Function], "setToolbarState": [Function], @@ -1212,6 +1228,7 @@ Map { "setFocus": [Function], }, "drawersOpenQueue": [], + "expandedDrawerId": undefined, "globalDrawers": [], "globalDrawersFocusControl": { "loseFocus": [Function], @@ -1255,6 +1272,7 @@ Map { "insetInlineEnd": 0, "insetInlineStart": 0, }, + "setExpandedDrawerId": [Function], "setNotificationsHeight": [Function], "setToolbarHeight": [Function], "setToolbarState": [Function], @@ -1346,6 +1364,7 @@ Map { "setFocus": [Function], }, "drawersOpenQueue": [], + "expandedDrawerId": undefined, "globalDrawers": [], "globalDrawersFocusControl": { "loseFocus": [Function], @@ -1389,6 +1408,7 @@ Map { "insetInlineEnd": 0, "insetInlineStart": 0, }, + "setExpandedDrawerId": [Function], "setNotificationsHeight": [Function], "setToolbarHeight": [Function], "setToolbarState": [Function], @@ -1480,6 +1500,7 @@ Map { "setFocus": [Function], }, "drawersOpenQueue": [], + "expandedDrawerId": undefined, "globalDrawers": [], "globalDrawersFocusControl": { "loseFocus": [Function], @@ -1523,6 +1544,7 @@ Map { "insetInlineEnd": 0, "insetInlineStart": 0, }, + "setExpandedDrawerId": [Function], "setNotificationsHeight": [Function], "setToolbarHeight": [Function], "setToolbarState": [Function], @@ -1674,6 +1696,7 @@ Map { "setFocus": [Function], }, "drawersOpenQueue": [], + "expandedDrawerId": undefined, "globalDrawers": [], "globalDrawersFocusControl": { "loseFocus": [Function], @@ -1715,6 +1738,7 @@ Map { "insetInlineEnd": 0, "insetInlineStart": 0, }, + "setExpandedDrawerId": [Function], "setNotificationsHeight": [Function], "setToolbarHeight": [Function], "setToolbarState": [Function], @@ -1818,6 +1842,7 @@ Map { "setFocus": [Function], }, "drawersOpenQueue": [], + "expandedDrawerId": undefined, "globalDrawers": [], "globalDrawersFocusControl": { "loseFocus": [Function], @@ -1859,6 +1884,7 @@ Map { "insetInlineEnd": 0, "insetInlineStart": 0, }, + "setExpandedDrawerId": [Function], "setNotificationsHeight": [Function], "setToolbarHeight": [Function], "setToolbarState": [Function], @@ -1993,6 +2019,7 @@ Map { "setFocus": [Function], }, "drawersOpenQueue": [], + "expandedDrawerId": undefined, "globalDrawers": [], "globalDrawersFocusControl": { "loseFocus": [Function], @@ -2034,6 +2061,7 @@ Map { "insetInlineEnd": 0, "insetInlineStart": 0, }, + "setExpandedDrawerId": [Function], "setNotificationsHeight": [Function], "setToolbarHeight": [Function], "setToolbarState": [Function], @@ -2137,6 +2165,7 @@ Map { "setFocus": [Function], }, "drawersOpenQueue": [], + "expandedDrawerId": undefined, "globalDrawers": [], "globalDrawersFocusControl": { "loseFocus": [Function], @@ -2178,6 +2207,7 @@ Map { "insetInlineEnd": 0, "insetInlineStart": 0, }, + "setExpandedDrawerId": [Function], "setNotificationsHeight": [Function], "setToolbarHeight": [Function], "setToolbarState": [Function], @@ -2281,6 +2311,7 @@ Map { "setFocus": [Function], }, "drawersOpenQueue": [], + "expandedDrawerId": undefined, "globalDrawers": [], "globalDrawersFocusControl": { "loseFocus": [Function], @@ -2322,6 +2353,7 @@ Map { "insetInlineEnd": 0, "insetInlineStart": 0, }, + "setExpandedDrawerId": [Function], "setNotificationsHeight": [Function], "setToolbarHeight": [Function], "setToolbarState": [Function], From ad99159f953407acdd62f6b7de6cc8c6078e15f4 Mon Sep 17 00:00:00 2001 From: Georgii Lobko Date: Fri, 28 Mar 2025 16:07:41 +0100 Subject: [PATCH 08/43] fix tests --- .../visual-refresh-toolbar/toolbar/drawer-triggers.tsx | 6 ++++-- src/app-layout/visual-refresh-toolbar/toolbar/index.tsx | 4 +++- 2 files changed, 7 insertions(+), 3 deletions(-) diff --git a/src/app-layout/visual-refresh-toolbar/toolbar/drawer-triggers.tsx b/src/app-layout/visual-refresh-toolbar/toolbar/drawer-triggers.tsx index 3eba8ed080..dafab5883a 100644 --- a/src/app-layout/visual-refresh-toolbar/toolbar/drawer-triggers.tsx +++ b/src/app-layout/visual-refresh-toolbar/toolbar/drawer-triggers.tsx @@ -142,7 +142,9 @@ export function DrawerTriggers({ )} iconName={splitPanelResolvedPosition === 'side' ? 'view-vertical' : 'view-horizontal'} onClick={() => { - setExpandedDrawerId(undefined); + if (setExpandedDrawerId) { + setExpandedDrawerId(undefined); + } onSplitPanelToggle?.(); }} selected={!drawerFocusMode && splitPanelToggleProps.active} @@ -173,7 +175,7 @@ export function DrawerTriggers({ key={item.id} onClick={() => { setExpandedDrawerId(undefined); - if (activeDrawerId === item.id) { + if (drawerFocusMode && activeDrawerId === item.id) { return; } onActiveDrawerChange?.(activeDrawerId !== item.id ? item.id : null, { initiatedByUserAction: true }); diff --git a/src/app-layout/visual-refresh-toolbar/toolbar/index.tsx b/src/app-layout/visual-refresh-toolbar/toolbar/index.tsx index 0aa1b0e985..ac4bafd165 100644 --- a/src/app-layout/visual-refresh-toolbar/toolbar/index.tsx +++ b/src/app-layout/visual-refresh-toolbar/toolbar/index.tsx @@ -165,7 +165,9 @@ export function AppLayoutToolbarImplementation({ iconName="menu" className={testutilStyles['navigation-toggle']} onClick={() => { - setExpandedDrawerId(undefined); + if (setExpandedDrawerId) { + setExpandedDrawerId(undefined); + } if (navigationOpen) { return; } From 84b2b965afaf54bbc6310d2793a2899680e7eec0 Mon Sep 17 00:00:00 2001 From: Georgii Lobko Date: Tue, 1 Apr 2025 14:56:05 +0200 Subject: [PATCH 09/43] chore: Border styles for global drawers (including expanded state) --- .../visual-refresh-toolbar/drawer/styles.scss | 29 ++++++++++++++++++- style-dictionary/utils/token-names.ts | 3 +- style-dictionary/visual-refresh/colors.ts | 1 + 3 files changed, 31 insertions(+), 2 deletions(-) diff --git a/src/app-layout/visual-refresh-toolbar/drawer/styles.scss b/src/app-layout/visual-refresh-toolbar/drawer/styles.scss index 6589aa47e6..e61cd76c17 100644 --- a/src/app-layout/visual-refresh-toolbar/drawer/styles.scss +++ b/src/app-layout/visual-refresh-toolbar/drawer/styles.scss @@ -19,6 +19,8 @@ } } +$global-drawer-gap-size: 8px; + .drawer { position: sticky; z-index: constants.$drawer-z-index; @@ -48,6 +50,19 @@ } &.drawer-global { + @include desktop-only { + padding-inline-start: 10px; + + &:before { + content: ''; + position: absolute; + block-size: 100%; + inline-size: $global-drawer-gap-size; + background: awsui.$color-gap-global-drawer; + border-inline-end: awsui.$border-divider-section-width solid awsui.$color-border-layout; + } + } + @include mobile-only { &:not(.last-opened) { display: none; @@ -60,7 +75,19 @@ } &.drawer-expanded { - order: -1; + @include desktop-only { + border-inline-start: none; + + &:after { + content: ''; + position: absolute; + block-size: 100%; + inline-size: $global-drawer-gap-size; + inset-inline-end: 0; + background: awsui.$color-gap-global-drawer; + border-inline-start: awsui.$border-divider-section-width solid awsui.$color-border-layout; + } + } } > .drawer-content-container { diff --git a/style-dictionary/utils/token-names.ts b/style-dictionary/utils/token-names.ts index 872d68ef7c..1f80562501 100644 --- a/style-dictionary/utils/token-names.ts +++ b/style-dictionary/utils/token-names.ts @@ -494,7 +494,8 @@ export type ColorsTokenName = | 'colorDropzoneTextDefault' | 'colorDropzoneTextHover' | 'colorDropzoneBorderDefault' - | 'colorDropzoneBorderHover'; + | 'colorDropzoneBorderHover' + | 'colorGapGlobalDrawer'; export type TypographyTokenName = | 'fontBoxValueLargeWeight' | 'fontButtonLetterSpacing' diff --git a/style-dictionary/visual-refresh/colors.ts b/style-dictionary/visual-refresh/colors.ts index 1dc58622a7..edb8c12add 100644 --- a/style-dictionary/visual-refresh/colors.ts +++ b/style-dictionary/visual-refresh/colors.ts @@ -274,6 +274,7 @@ const tokens: StyleDictionary.ColorsDictionary = { colorDropzoneTextHover: { light: '{colorGrey600}', dark: '{colorGrey350}' }, colorDropzoneBorderDefault: { light: '{colorGrey500}', dark: '{colorGrey550}' }, colorDropzoneBorderHover: { light: '{colorBlue800}', dark: '{colorBlue400}' }, + colorGapGlobalDrawer: { light: '{colorGrey150}', dark: '{colorGrey700}' }, }; const expandedTokens: StyleDictionary.ExpandedColorScopeDictionary = expandColorDictionary(tokens); From ffd3a83f2176ca10e9c3d3e0ae196fe930900814 Mon Sep 17 00:00:00 2001 From: Georgii Lobko Date: Wed, 2 Apr 2025 10:43:01 +0200 Subject: [PATCH 10/43] chore: Rename focus mode to expanded mode --- .../__tests__/runtime-drawers.test.tsx | 44 +++++++++---------- src/app-layout/__tests__/utils.tsx | 10 ++--- src/app-layout/interfaces.ts | 2 +- src/app-layout/test-classes/styles.scss | 2 +- .../drawer/global-drawer.tsx | 6 +-- .../visual-refresh-toolbar/drawer/styles.scss | 2 +- .../visual-refresh-toolbar/index.tsx | 2 +- .../visual-refresh-toolbar/skeleton/index.tsx | 12 ++--- .../skeleton/styles.scss | 2 +- .../toolbar/drawer-triggers.tsx | 12 ++--- .../visual-refresh-toolbar/toolbar/index.tsx | 6 +-- 11 files changed, 50 insertions(+), 50 deletions(-) diff --git a/src/app-layout/__tests__/runtime-drawers.test.tsx b/src/app-layout/__tests__/runtime-drawers.test.tsx index 01479efa84..a789eacbee 100644 --- a/src/app-layout/__tests__/runtime-drawers.test.tsx +++ b/src/app-layout/__tests__/runtime-drawers.test.tsx @@ -1349,8 +1349,8 @@ describe('toolbar mode only features', () => { }); }); - describe('focus mode for global drawers', () => { - test('should set a drawer to focus mode by clicking on "focus mode" button', async () => { + describe('expanded mode for global drawers', () => { + test('should set a drawer to expanded mode by clicking on "expanded mode" button', async () => { const drawerId = 'global-drawer'; awsuiPlugins.appLayout.registerDrawer({ ...drawerDefaults, @@ -1363,13 +1363,13 @@ describe('toolbar mode only features', () => { await delay(); wrapper.findDrawerTriggerById(drawerId)!.click(); - expect(globalDrawersWrapper.findFocusModeButtonByActiveDrawerId(drawerId)!.getElement()).toBeInTheDocument(); - expect(globalDrawersWrapper.findDrawerById(drawerId)!.isDrawerInFocusMode()).toBe(false); - globalDrawersWrapper.findFocusModeButtonByActiveDrawerId(drawerId)!.click(); - expect(globalDrawersWrapper.findDrawerById(drawerId)!.isDrawerInFocusMode()).toBe(true); + expect(globalDrawersWrapper.findExpandedModeButtonByActiveDrawerId(drawerId)!.getElement()).toBeInTheDocument(); + expect(globalDrawersWrapper.findDrawerById(drawerId)!.isDrawerInExpandedMode()).toBe(false); + globalDrawersWrapper.findExpandedModeButtonByActiveDrawerId(drawerId)!.click(); + expect(globalDrawersWrapper.findDrawerById(drawerId)!.isDrawerInExpandedMode()).toBe(true); }); - test('should switch focus mode between two global drawers', async () => { + test('should switch expanded mode between two global drawers', async () => { const drawerId1 = 'global-drawer1'; const drawerId2 = 'global-drawer2'; awsuiPlugins.appLayout.registerDrawer({ @@ -1392,19 +1392,19 @@ describe('toolbar mode only features', () => { wrapper.findDrawerTriggerById(drawerId2)!.click(); expect(globalDrawersWrapper.findDrawerById(drawerId1)!.isActive()).toBe(true); expect(globalDrawersWrapper.findDrawerById(drawerId2)!.isActive()).toBe(true); - expect(globalDrawersWrapper.isLayoutInFocusMode()).toBe(false); - expect(globalDrawersWrapper.findDrawerById(drawerId1)!.isDrawerInFocusMode()).toBe(false); - expect(globalDrawersWrapper.findDrawerById(drawerId2)!.isDrawerInFocusMode()).toBe(false); - globalDrawersWrapper.findFocusModeButtonByActiveDrawerId(drawerId1)!.click(); - expect(globalDrawersWrapper.findDrawerById(drawerId1)!.isDrawerInFocusMode()).toBe(true); - expect(globalDrawersWrapper.findDrawerById(drawerId2)!.isDrawerInFocusMode()).toBe(false); - globalDrawersWrapper.findFocusModeButtonByActiveDrawerId(drawerId2)!.click(); - expect(globalDrawersWrapper.findDrawerById(drawerId1)!.isDrawerInFocusMode()).toBe(false); - expect(globalDrawersWrapper.findDrawerById(drawerId2)!.isDrawerInFocusMode()).toBe(true); - expect(globalDrawersWrapper.isLayoutInFocusMode()).toBe(true); + expect(globalDrawersWrapper.isLayoutInDrawerExpandedMode()).toBe(false); + expect(globalDrawersWrapper.findDrawerById(drawerId1)!.isDrawerInExpandedMode()).toBe(false); + expect(globalDrawersWrapper.findDrawerById(drawerId2)!.isDrawerInExpandedMode()).toBe(false); + globalDrawersWrapper.findExpandedModeButtonByActiveDrawerId(drawerId1)!.click(); + expect(globalDrawersWrapper.findDrawerById(drawerId1)!.isDrawerInExpandedMode()).toBe(true); + expect(globalDrawersWrapper.findDrawerById(drawerId2)!.isDrawerInExpandedMode()).toBe(false); + globalDrawersWrapper.findExpandedModeButtonByActiveDrawerId(drawerId2)!.click(); + expect(globalDrawersWrapper.findDrawerById(drawerId1)!.isDrawerInExpandedMode()).toBe(false); + expect(globalDrawersWrapper.findDrawerById(drawerId2)!.isDrawerInExpandedMode()).toBe(true); + expect(globalDrawersWrapper.isLayoutInDrawerExpandedMode()).toBe(true); }); - test('should quit focus mode when a drawer in focus mode is closed', async () => { + test('should quit expanded mode when a drawer in focus mode is closed', async () => { const drawerId = 'global-drawer'; awsuiPlugins.appLayout.registerDrawer({ ...drawerDefaults, @@ -1417,11 +1417,11 @@ describe('toolbar mode only features', () => { await delay(); wrapper.findDrawerTriggerById(drawerId)!.click(); - globalDrawersWrapper.findFocusModeButtonByActiveDrawerId(drawerId)!.click(); - expect(globalDrawersWrapper.findDrawerById(drawerId)!.isDrawerInFocusMode()).toBe(true); - expect(globalDrawersWrapper.isLayoutInFocusMode()).toBe(true); + globalDrawersWrapper.findExpandedModeButtonByActiveDrawerId(drawerId)!.click(); + expect(globalDrawersWrapper.findDrawerById(drawerId)!.isDrawerInExpandedMode()).toBe(true); + expect(globalDrawersWrapper.isLayoutInDrawerExpandedMode()).toBe(true); wrapper.findDrawerTriggerById(drawerId)!.click(); - expect(globalDrawersWrapper.isLayoutInFocusMode()).toBe(false); + expect(globalDrawersWrapper.isLayoutInDrawerExpandedMode()).toBe(false); }); }); }); diff --git a/src/app-layout/__tests__/utils.tsx b/src/app-layout/__tests__/utils.tsx index 7f2b7716db..2039028b3e 100644 --- a/src/app-layout/__tests__/utils.tsx +++ b/src/app-layout/__tests__/utils.tsx @@ -169,7 +169,7 @@ class AppLayoutDrawerWrapper extends ComponentWrapper { return this.element.classList.contains(testutilStyles['active-drawer']); } - isDrawerInFocusMode(): boolean { + isDrawerInExpandedMode(): boolean { return this.element.classList.contains(globalDrawerStyles['drawer-expanded']); } } @@ -201,14 +201,14 @@ export const getGlobalDrawersTestUtils = (wrapper: AppLayoutWrapper) => { ); }, - findFocusModeButtonByActiveDrawerId(id: string): ElementWrapper | null { + findExpandedModeButtonByActiveDrawerId(id: string): ElementWrapper | null { return wrapper.find( - `.${testutilStyles['active-drawer']}[data-testid="awsui-app-layout-drawer-${id}"] .${testutilStyles['active-drawer-focus-mode-button']}` + `.${testutilStyles['active-drawer']}[data-testid="awsui-app-layout-drawer-${id}"] .${testutilStyles['active-drawer-expanded-mode-button']}` ); }, - isLayoutInFocusMode(): boolean { - return !!wrapper.matches(`.${visualRefreshToolbarStyles['drawer-focus-mode']}`); + isLayoutInDrawerExpandedMode(): boolean { + return !!wrapper.matches(`.${visualRefreshToolbarStyles['drawer-expanded-mode']}`); }, }; }; diff --git a/src/app-layout/interfaces.ts b/src/app-layout/interfaces.ts index 55825e2e3b..e348e5274b 100644 --- a/src/app-layout/interfaces.ts +++ b/src/app-layout/interfaces.ts @@ -317,7 +317,7 @@ export namespace AppLayoutProps { closeButton?: string; triggerButton?: string; resizeHandle?: string; - focusModeButton?: string; + expandedModeButton?: string; } export interface Labels { diff --git a/src/app-layout/test-classes/styles.scss b/src/app-layout/test-classes/styles.scss index 50d4550ed3..00733f4d57 100644 --- a/src/app-layout/test-classes/styles.scss +++ b/src/app-layout/test-classes/styles.scss @@ -26,6 +26,6 @@ .toolbar, .trigger-wrapper-tooltip-visible, .trigger-tooltip, -.active-drawer-focus-mode-button { +.active-drawer-expanded-mode-button { /* used in test-utils */ } diff --git a/src/app-layout/visual-refresh-toolbar/drawer/global-drawer.tsx b/src/app-layout/visual-refresh-toolbar/drawer/global-drawer.tsx index d16d164f71..7133eda5b2 100644 --- a/src/app-layout/visual-refresh-toolbar/drawer/global-drawer.tsx +++ b/src/app-layout/visual-refresh-toolbar/drawer/global-drawer.tsx @@ -136,10 +136,10 @@ function AppLayoutGlobalDrawerImplementation({ data-testid={`awsui-app-layout-drawer-content-${activeDrawerId}`} > {activeGlobalDrawer?.isExpandable && ( -
+
.drawer-focus-mode-button { + > .drawer-expanded-mode-button { grid-column: 2; grid-row: 2; z-index: 1; diff --git a/src/app-layout/visual-refresh-toolbar/index.tsx b/src/app-layout/visual-refresh-toolbar/index.tsx index b541692759..6dccbe40a6 100644 --- a/src/app-layout/visual-refresh-toolbar/index.tsx +++ b/src/app-layout/visual-refresh-toolbar/index.tsx @@ -489,7 +489,7 @@ const AppLayoutVisualRefreshToolbar = React.forwardRef( @@ -70,7 +70,7 @@ export const SkeletonLayout = React.forwardRef { @@ -83,7 +83,7 @@ export const SkeletonLayout = React.forwardRef {navigation} @@ -110,7 +110,7 @@ export const SkeletonLayout = React.forwardRef {notifications && ( @@ -147,7 +147,7 @@ export const SkeletonLayout = React.forwardRef {tools} diff --git a/src/app-layout/visual-refresh-toolbar/skeleton/styles.scss b/src/app-layout/visual-refresh-toolbar/skeleton/styles.scss index 8e56c1b4d0..7338179316 100644 --- a/src/app-layout/visual-refresh-toolbar/skeleton/styles.scss +++ b/src/app-layout/visual-refresh-toolbar/skeleton/styles.scss @@ -73,7 +73,7 @@ } } - &.drawer-focus-mode { + &.drawer-expanded-mode { grid-template-areas: 'toolbar toolbar toolbar toolbar toolbar ' 'global-tools global-tools global-tools global-tools global-tools' diff --git a/src/app-layout/visual-refresh-toolbar/toolbar/drawer-triggers.tsx b/src/app-layout/visual-refresh-toolbar/toolbar/drawer-triggers.tsx index dafab5883a..bb8d812988 100644 --- a/src/app-layout/visual-refresh-toolbar/toolbar/drawer-triggers.tsx +++ b/src/app-layout/visual-refresh-toolbar/toolbar/drawer-triggers.tsx @@ -37,7 +37,7 @@ interface DrawerTriggersProps { globalDrawersFocusControl?: FocusControlMultipleStates; globalDrawers: ReadonlyArray; onActiveGlobalDrawersChange?: (newDrawerId: string, params: OnChangeParams) => void; - drawerFocusMode?: boolean; + drawerExpandedMode?: boolean; setExpandedDrawerId: (value: string | undefined) => void; splitPanelOpen?: boolean; @@ -64,7 +64,7 @@ export function DrawerTriggers({ globalDrawers, globalDrawersFocusControl, onActiveGlobalDrawersChange, - drawerFocusMode, + drawerExpandedMode, setExpandedDrawerId, }: DrawerTriggersProps) { const isMobile = useMobile(); @@ -134,7 +134,7 @@ export function DrawerTriggers({ { const isForPreviousActiveDrawer = previousActiveLocalDrawerId?.current === item.id; - const selected = !drawerFocusMode && item.id === activeDrawerId; + const selected = !drawerExpandedMode && item.id === activeDrawerId; return ( { setExpandedDrawerId(undefined); - if (drawerFocusMode && activeDrawerId === item.id) { + if (drawerExpandedMode && activeDrawerId === item.id) { return; } onActiveDrawerChange?.(activeDrawerId !== item.id ? item.id : null, { initiatedByUserAction: true }); diff --git a/src/app-layout/visual-refresh-toolbar/toolbar/index.tsx b/src/app-layout/visual-refresh-toolbar/toolbar/index.tsx index ac4bafd165..8f0bdc3974 100644 --- a/src/app-layout/visual-refresh-toolbar/toolbar/index.tsx +++ b/src/app-layout/visual-refresh-toolbar/toolbar/index.tsx @@ -92,7 +92,7 @@ export function AppLayoutToolbarImplementation({ } = toolbarProps; // TODO: expose configuration property const pinnedToolbar = true; - const drawerFocusMode = !!expandedDrawerId; + const drawerExpandedMode = !!expandedDrawerId; const ref = useRef(null); useResizeObserver(ref, entry => setToolbarHeight(entry.borderBoxHeight)); useEffect(() => { @@ -174,7 +174,7 @@ export function AppLayoutToolbarImplementation({ onNavigationToggle?.(!navigationOpen); }} ref={navigationFocusRef} - selected={!drawerFocusMode && navigationOpen} + selected={!drawerExpandedMode && navigationOpen} disabled={anyPanelOpenInMobile} /> @@ -203,7 +203,7 @@ export function AppLayoutToolbarImplementation({ globalDrawers={globalDrawers?.filter(item => !!item.trigger) ?? []} activeGlobalDrawersIds={activeGlobalDrawersIds ?? []} onActiveGlobalDrawersChange={onActiveGlobalDrawersChange} - drawerFocusMode={drawerFocusMode} + drawerExpandedMode={drawerExpandedMode} setExpandedDrawerId={setExpandedDrawerId} />
From 680b1c28ae474d8fd5d95958d2388a182aef2750 Mon Sep 17 00:00:00 2001 From: Georgii Lobko Date: Wed, 2 Apr 2025 14:58:55 +0200 Subject: [PATCH 11/43] feat: Allow only 1 drawer to be in expanded mode --- .../__tests__/runtime-drawers.test.tsx | 108 ++++++++++++++++-- .../visual-refresh-toolbar/compute-layout.ts | 16 --- .../drawer/global-drawer.tsx | 5 - .../drawer/global-drawers.tsx | 4 +- .../visual-refresh-toolbar/drawer/styles.scss | 2 + .../visual-refresh-toolbar/index.tsx | 1 - .../toolbar/drawer-triggers.tsx | 24 ++-- .../visual-refresh-toolbar/toolbar/index.tsx | 2 +- 8 files changed, 117 insertions(+), 45 deletions(-) diff --git a/src/app-layout/__tests__/runtime-drawers.test.tsx b/src/app-layout/__tests__/runtime-drawers.test.tsx index a789eacbee..33ea4de51c 100644 --- a/src/app-layout/__tests__/runtime-drawers.test.tsx +++ b/src/app-layout/__tests__/runtime-drawers.test.tsx @@ -1369,9 +1369,10 @@ describe('toolbar mode only features', () => { expect(globalDrawersWrapper.findDrawerById(drawerId)!.isDrawerInExpandedMode()).toBe(true); }); - test('should switch expanded mode between two global drawers', async () => { + test('only one drawer could be in expanded mode. all other panels should be closed', async () => { const drawerId1 = 'global-drawer1'; const drawerId2 = 'global-drawer2'; + const drawerId3Local = 'local-drawer'; awsuiPlugins.appLayout.registerDrawer({ ...drawerDefaults, id: drawerId1, @@ -1384,27 +1385,112 @@ describe('toolbar mode only features', () => { type: 'global', isExpandable: true, }); - const { wrapper, globalDrawersWrapper } = await renderComponent(); + awsuiPlugins.appLayout.registerDrawer({ + ...drawerDefaults, + id: drawerId3Local, + }); + const { wrapper, globalDrawersWrapper } = await renderComponent( + nav
} /> + ); await delay(); wrapper.findDrawerTriggerById(drawerId1)!.click(); wrapper.findDrawerTriggerById(drawerId2)!.click(); - expect(globalDrawersWrapper.findDrawerById(drawerId1)!.isActive()).toBe(true); - expect(globalDrawersWrapper.findDrawerById(drawerId2)!.isActive()).toBe(true); + wrapper.findDrawerTriggerById(drawerId3Local)!.click(); + + expect(wrapper.findDrawerTriggerById(drawerId1)!.getElement()).toHaveClass(toolbarTriggerStyles.selected); + expect(wrapper.findDrawerTriggerById(drawerId2)!.getElement()).toHaveClass(toolbarTriggerStyles.selected); + expect(wrapper.findDrawerTriggerById(drawerId3Local)!.getElement()).toHaveClass(toolbarTriggerStyles.selected); + expect(wrapper.findNavigationToggle()!.getElement()).toHaveClass(toolbarTriggerStyles.selected); expect(globalDrawersWrapper.isLayoutInDrawerExpandedMode()).toBe(false); expect(globalDrawersWrapper.findDrawerById(drawerId1)!.isDrawerInExpandedMode()).toBe(false); - expect(globalDrawersWrapper.findDrawerById(drawerId2)!.isDrawerInExpandedMode()).toBe(false); + globalDrawersWrapper.findExpandedModeButtonByActiveDrawerId(drawerId1)!.click(); + expect(globalDrawersWrapper.findDrawerById(drawerId1)!.isDrawerInExpandedMode()).toBe(true); - expect(globalDrawersWrapper.findDrawerById(drawerId2)!.isDrawerInExpandedMode()).toBe(false); - globalDrawersWrapper.findExpandedModeButtonByActiveDrawerId(drawerId2)!.click(); - expect(globalDrawersWrapper.findDrawerById(drawerId1)!.isDrawerInExpandedMode()).toBe(false); - expect(globalDrawersWrapper.findDrawerById(drawerId2)!.isDrawerInExpandedMode()).toBe(true); - expect(globalDrawersWrapper.isLayoutInDrawerExpandedMode()).toBe(true); + expect(wrapper.findDrawerTriggerById(drawerId1)!.getElement()).toHaveClass(toolbarTriggerStyles.selected); + expect(wrapper.findDrawerTriggerById(drawerId2)!.getElement()).not.toHaveClass(toolbarTriggerStyles.selected); + expect(wrapper.findDrawerTriggerById(drawerId3Local)!.getElement()).not.toHaveClass( + toolbarTriggerStyles.selected + ); + expect(wrapper.findNavigationToggle()!.getElement()).not.toHaveClass(toolbarTriggerStyles.selected); }); - test('should quit expanded mode when a drawer in focus mode is closed', async () => { + // test.each(['expanded', 'global-drawer', 'local-drawer', 'nav'] as const)('%s', theme => {}); + test.each(['expanded', 'global-drawer', 'local-drawer', 'nav'] as const)( + 'should return panels to their initial state after leaving expanded mode by clicking on %s button', + async triggerName => { + const drawerId1 = 'global-drawer1'; + const drawerId2 = 'global-drawer2'; + const drawerId3Local = 'local-drawer'; + awsuiPlugins.appLayout.registerDrawer({ + ...drawerDefaults, + id: drawerId1, + type: 'global', + isExpandable: true, + }); + awsuiPlugins.appLayout.registerDrawer({ + ...drawerDefaults, + id: drawerId2, + type: 'global', + isExpandable: true, + }); + awsuiPlugins.appLayout.registerDrawer({ + ...drawerDefaults, + id: drawerId3Local, + }); + const { wrapper, globalDrawersWrapper } = await renderComponent( + nav} /> + ); + + await delay(); + + wrapper.findDrawerTriggerById(drawerId1)!.click(); + wrapper.findDrawerTriggerById(drawerId2)!.click(); + wrapper.findDrawerTriggerById(drawerId3Local)!.click(); + + expect(wrapper.findDrawerTriggerById(drawerId1)!.getElement()).toHaveClass(toolbarTriggerStyles.selected); + expect(wrapper.findDrawerTriggerById(drawerId2)!.getElement()).toHaveClass(toolbarTriggerStyles.selected); + expect(wrapper.findDrawerTriggerById(drawerId3Local)!.getElement()).toHaveClass( + toolbarTriggerStyles.selected + ); + expect(wrapper.findNavigationToggle()!.getElement()).toHaveClass(toolbarTriggerStyles.selected); + expect(globalDrawersWrapper.isLayoutInDrawerExpandedMode()).toBe(false); + expect(globalDrawersWrapper.findDrawerById(drawerId1)!.isDrawerInExpandedMode()).toBe(false); + + globalDrawersWrapper.findExpandedModeButtonByActiveDrawerId(drawerId1)!.click(); + + expect(globalDrawersWrapper.findDrawerById(drawerId1)!.isDrawerInExpandedMode()).toBe(true); + expect(wrapper.findDrawerTriggerById(drawerId1)!.getElement()).toHaveClass(toolbarTriggerStyles.selected); + expect(wrapper.findDrawerTriggerById(drawerId2)!.getElement()).not.toHaveClass(toolbarTriggerStyles.selected); + expect(wrapper.findDrawerTriggerById(drawerId3Local)!.getElement()).not.toHaveClass( + toolbarTriggerStyles.selected + ); + expect(wrapper.findNavigationToggle()!.getElement()).not.toHaveClass(toolbarTriggerStyles.selected); + + if (triggerName === 'expanded') { + globalDrawersWrapper.findExpandedModeButtonByActiveDrawerId(drawerId1)!.click(); + } else if (triggerName === 'global-drawer') { + wrapper.findDrawerTriggerById(drawerId2)!.click(); + } else if (triggerName === 'local-drawer') { + wrapper.findDrawerTriggerById(drawerId3Local)!.click(); + } else if (triggerName === 'nav') { + wrapper.findNavigationToggle()!.click(); + } + + expect(wrapper.findDrawerTriggerById(drawerId1)!.getElement()).toHaveClass(toolbarTriggerStyles.selected); + expect(wrapper.findDrawerTriggerById(drawerId2)!.getElement()).toHaveClass(toolbarTriggerStyles.selected); + expect(wrapper.findDrawerTriggerById(drawerId3Local)!.getElement()).toHaveClass( + toolbarTriggerStyles.selected + ); + expect(wrapper.findNavigationToggle()!.getElement()).toHaveClass(toolbarTriggerStyles.selected); + expect(globalDrawersWrapper.isLayoutInDrawerExpandedMode()).toBe(false); + expect(globalDrawersWrapper.findDrawerById(drawerId1)!.isDrawerInExpandedMode()).toBe(false); + } + ); + + test('should quit expanded mode when a drawer in expanded mode is closed', async () => { const drawerId = 'global-drawer'; awsuiPlugins.appLayout.registerDrawer({ ...drawerDefaults, diff --git a/src/app-layout/visual-refresh-toolbar/compute-layout.ts b/src/app-layout/visual-refresh-toolbar/compute-layout.ts index ae279d8878..084efdbbbb 100644 --- a/src/app-layout/visual-refresh-toolbar/compute-layout.ts +++ b/src/app-layout/visual-refresh-toolbar/compute-layout.ts @@ -15,7 +15,6 @@ interface HorizontalLayoutInput { splitPanelSize: number; isMobile: boolean; activeGlobalDrawersSizes: Record; - expandedDrawerId?: string; } export const CONTENT_PADDING = 2 * 24; // space-xl @@ -31,7 +30,6 @@ export function computeHorizontalLayout({ splitPanelSize, isMobile, activeGlobalDrawersSizes, - expandedDrawerId, }: HorizontalLayoutInput) { const activeNavigationWidth = navigationOpen ? navigationWidth : 0; @@ -53,20 +51,6 @@ export function computeHorizontalLayout({ const maxDrawerSize = resizableSpaceAvailable - totalActiveGlobalDrawersSize; const maxGlobalDrawersSizes: Record = Object.keys(activeGlobalDrawersSizes).reduce( (acc, drawerId) => { - if (drawerId === expandedDrawerId) { - return { - ...acc, - [drawerId]: placement.inlineSize, - }; - } - if (expandedDrawerId && drawerId !== expandedDrawerId) { - if (drawerId === expandedDrawerId) { - return { - ...acc, - [drawerId]: resizableSpaceAvailable, - }; - } - } return { ...acc, [drawerId]: diff --git a/src/app-layout/visual-refresh-toolbar/drawer/global-drawer.tsx b/src/app-layout/visual-refresh-toolbar/drawer/global-drawer.tsx index 7133eda5b2..91ad3a72aa 100644 --- a/src/app-layout/visual-refresh-toolbar/drawer/global-drawer.tsx +++ b/src/app-layout/visual-refresh-toolbar/drawer/global-drawer.tsx @@ -110,11 +110,6 @@ function AppLayoutGlobalDrawerImplementation({ ...(!isMobile && { [customCssProps.drawerSize]: `${['entering', 'entered'].includes(state) ? size : 0}px`, }), - ...(isExpanded && { - inlineSize: `calc(100% - ${Object.keys(activeGlobalDrawersSizes) - .filter(drawerId => drawerId !== activeDrawerId) - .reduce((acc, drawerId) => acc + activeGlobalDrawersSizes[drawerId], 0)}px)`, - }), }} data-testid={`awsui-app-layout-drawer-${activeDrawerId}`} > diff --git a/src/app-layout/visual-refresh-toolbar/drawer/global-drawers.tsx b/src/app-layout/visual-refresh-toolbar/drawer/global-drawers.tsx index b702ee12c4..f6fbdf91f9 100644 --- a/src/app-layout/visual-refresh-toolbar/drawer/global-drawers.tsx +++ b/src/app-layout/visual-refresh-toolbar/drawer/global-drawers.tsx @@ -13,7 +13,7 @@ interface AppLayoutGlobalDrawersImplementationProps { export function AppLayoutGlobalDrawersImplementation({ appLayoutInternals, }: AppLayoutGlobalDrawersImplementationProps) { - const { globalDrawers, activeGlobalDrawersIds } = appLayoutInternals; + const { globalDrawers, activeGlobalDrawersIds, expandedDrawerId } = appLayoutInternals; const openDrawersHistory = useRef>(new Set()); if (!globalDrawers.length) { @@ -33,7 +33,7 @@ export function AppLayoutGlobalDrawersImplementation({ return ( diff --git a/src/app-layout/visual-refresh-toolbar/drawer/styles.scss b/src/app-layout/visual-refresh-toolbar/drawer/styles.scss index 182e36a6f0..6b28aab7df 100644 --- a/src/app-layout/visual-refresh-toolbar/drawer/styles.scss +++ b/src/app-layout/visual-refresh-toolbar/drawer/styles.scss @@ -75,6 +75,8 @@ $global-drawer-gap-size: 8px; } &.drawer-expanded { + inline-size: 100%; + @include desktop-only { border-inline-start: none; diff --git a/src/app-layout/visual-refresh-toolbar/index.tsx b/src/app-layout/visual-refresh-toolbar/index.tsx index 6dccbe40a6..1c8bc07156 100644 --- a/src/app-layout/visual-refresh-toolbar/index.tsx +++ b/src/app-layout/visual-refresh-toolbar/index.tsx @@ -268,7 +268,6 @@ const AppLayoutVisualRefreshToolbar = React.forwardRef; onActiveGlobalDrawersChange?: (newDrawerId: string, params: OnChangeParams) => void; - drawerExpandedMode?: boolean; + expandedDrawerId?: string; setExpandedDrawerId: (value: string | undefined) => void; splitPanelOpen?: boolean; @@ -64,7 +64,7 @@ export function DrawerTriggers({ globalDrawers, globalDrawersFocusControl, onActiveGlobalDrawersChange, - drawerExpandedMode, + expandedDrawerId, setExpandedDrawerId, }: DrawerTriggersProps) { const isMobile = useMobile(); @@ -134,7 +134,7 @@ export function DrawerTriggers({ { const isForPreviousActiveDrawer = previousActiveLocalDrawerId?.current === item.id; - const selected = !drawerExpandedMode && item.id === activeDrawerId; + const selected = !expandedDrawerId && item.id === activeDrawerId; return ( { setExpandedDrawerId(undefined); - if (drawerExpandedMode && activeDrawerId === item.id) { + if (!!expandedDrawerId && activeDrawerId === item.id) { return; } onActiveDrawerChange?.(activeDrawerId !== item.id ? item.id : null, { initiatedByUserAction: true }); @@ -198,11 +198,13 @@ export function DrawerTriggers({ )} {visibleItems.slice(globalDrawersStartIndex).map(item => { const isForPreviousActiveDrawer = previousActiveGlobalDrawersIds?.current.includes(item.id); + const selected = + activeGlobalDrawersIds.includes(item.id) && (!expandedDrawerId || item.id === expandedDrawerId); return ( { + setExpandedDrawerId(undefined); + if (!!expandedDrawerId && item.id !== expandedDrawerId && activeGlobalDrawersIds.includes(item.id)) { + return; + } onActiveGlobalDrawersChange?.(item.id, { initiatedByUserAction: true }); }} ref={globalDrawersFocusControl?.refs[item.id]?.toggle} - selected={activeGlobalDrawersIds.includes(item.id)} + selected={selected} badge={item.badge} testId={`awsui-app-layout-trigger-${item.id}`} hasTooltip={true} diff --git a/src/app-layout/visual-refresh-toolbar/toolbar/index.tsx b/src/app-layout/visual-refresh-toolbar/toolbar/index.tsx index 8f0bdc3974..e56f06dc79 100644 --- a/src/app-layout/visual-refresh-toolbar/toolbar/index.tsx +++ b/src/app-layout/visual-refresh-toolbar/toolbar/index.tsx @@ -203,7 +203,7 @@ export function AppLayoutToolbarImplementation({ globalDrawers={globalDrawers?.filter(item => !!item.trigger) ?? []} activeGlobalDrawersIds={activeGlobalDrawersIds ?? []} onActiveGlobalDrawersChange={onActiveGlobalDrawersChange} - drawerExpandedMode={drawerExpandedMode} + expandedDrawerId={expandedDrawerId} setExpandedDrawerId={setExpandedDrawerId} /> From f79ee97743a76769e4335f1aa70e33836051ef15 Mon Sep 17 00:00:00 2001 From: Georgii Lobko Date: Wed, 2 Apr 2025 15:20:59 +0200 Subject: [PATCH 12/43] chore: Prevent rendering expanded mode button in mobile view --- .../__tests__/runtime-drawers.test.tsx | 16 ++++++++++++++++ .../drawer/global-drawer.tsx | 4 ++-- .../visual-refresh-toolbar/drawer/styles.scss | 2 +- 3 files changed, 19 insertions(+), 3 deletions(-) diff --git a/src/app-layout/__tests__/runtime-drawers.test.tsx b/src/app-layout/__tests__/runtime-drawers.test.tsx index 33ea4de51c..b223448715 100644 --- a/src/app-layout/__tests__/runtime-drawers.test.tsx +++ b/src/app-layout/__tests__/runtime-drawers.test.tsx @@ -1548,5 +1548,21 @@ describe('toolbar mode only features', () => { globalDrawersWrapper.findCloseButtonByActiveDrawerId(drawerIdWithToggle)!.click(); expect(onToggle).toHaveBeenCalledWith({ isOpen: false, initiatedByUserAction: true }); }); + + test('should not render drawer expanded mode button in mobile view', async () => { + const drawerId = 'global-drawer'; + awsuiPlugins.appLayout.registerDrawer({ + ...drawerDefaults, + id: drawerId, + type: 'global', + isExpandable: true, + }); + const { wrapper, globalDrawersWrapper } = await renderComponent(); + + await delay(); + + wrapper.findDrawerTriggerById(drawerId)!.click(); + expect(globalDrawersWrapper.findExpandedModeButtonByActiveDrawerId(drawerId)).toBeFalsy(); + }); }); }); diff --git a/src/app-layout/visual-refresh-toolbar/drawer/global-drawer.tsx b/src/app-layout/visual-refresh-toolbar/drawer/global-drawer.tsx index 91ad3a72aa..0aaeb22b0d 100644 --- a/src/app-layout/visual-refresh-toolbar/drawer/global-drawer.tsx +++ b/src/app-layout/visual-refresh-toolbar/drawer/global-drawer.tsx @@ -84,7 +84,7 @@ function AppLayoutGlobalDrawerImplementation({ !animationDisabled && sharedStyles['with-motion-horizontal'], { [styles['drawer-hidden']]: !show, - [styles['last-opened']]: lastOpenedDrawerId === activeDrawerId, + [styles['last-opened']]: lastOpenedDrawerId === activeDrawerId || isExpanded, [testutilStyles['active-drawer']]: show, [styles['drawer-expanded']]: isExpanded, } @@ -130,7 +130,7 @@ function AppLayoutGlobalDrawerImplementation({ className={clsx(styles['drawer-content-container'], sharedStyles['with-motion-horizontal'])} data-testid={`awsui-app-layout-drawer-content-${activeDrawerId}`} > - {activeGlobalDrawer?.isExpandable && ( + {!isMobile && activeGlobalDrawer?.isExpandable && (
Date: Wed, 2 Apr 2025 15:34:54 +0200 Subject: [PATCH 13/43] chore: Update snapshots --- src/__integ__/__snapshots__/themes.test.ts.snap | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/src/__integ__/__snapshots__/themes.test.ts.snap b/src/__integ__/__snapshots__/themes.test.ts.snap index 6a4497519b..c04b9935a9 100644 --- a/src/__integ__/__snapshots__/themes.test.ts.snap +++ b/src/__integ__/__snapshots__/themes.test.ts.snap @@ -355,6 +355,7 @@ exports[`CSS Custom Properties match previous snapshot for mode "compact" 1`] = "color-foreground-control-default": "#ffffff", "color-foreground-control-disabled": "#ffffff", "color-foreground-control-read-only": "#687078", + "color-gap-global-drawer": "#f2f3f3", "color-shadow-default": "rgba(0, 28, 36, 0.5)", "color-stroke-chart-line": "#879596", "color-text-accent": "#0073bb", @@ -1020,6 +1021,7 @@ exports[`CSS Custom Properties match previous snapshot for mode "dark" 1`] = ` "color-foreground-control-default": "#ffffff", "color-foreground-control-disabled": "#687078", "color-foreground-control-read-only": "#95a5a6", + "color-gap-global-drawer": "#2a2e33", "color-shadow-default": "rgba(0, 0, 0, 0.5)", "color-stroke-chart-line": "#879596", "color-text-accent": "#44b9d6", @@ -1685,6 +1687,7 @@ exports[`CSS Custom Properties match previous snapshot for mode "light" 1`] = ` "color-foreground-control-default": "#ffffff", "color-foreground-control-disabled": "#ffffff", "color-foreground-control-read-only": "#687078", + "color-gap-global-drawer": "#f2f3f3", "color-shadow-default": "rgba(0, 28, 36, 0.5)", "color-stroke-chart-line": "#879596", "color-text-accent": "#0073bb", @@ -2350,6 +2353,7 @@ exports[`CSS Custom Properties match previous snapshot for mode "reduced-motion" "color-foreground-control-default": "#ffffff", "color-foreground-control-disabled": "#ffffff", "color-foreground-control-read-only": "#687078", + "color-gap-global-drawer": "#f2f3f3", "color-shadow-default": "rgba(0, 28, 36, 0.5)", "color-stroke-chart-line": "#879596", "color-text-accent": "#0073bb", @@ -3015,6 +3019,7 @@ exports[`CSS Custom Properties match previous snapshot for mode "visual-refresh" "color-foreground-control-default": "#ffffff", "color-foreground-control-disabled": "#ffffff", "color-foreground-control-read-only": "#656871", + "color-gap-global-drawer": "#f3f3f7", "color-shadow-default": "rgba(15, 20, 26, 0.12)", "color-stroke-chart-line": "#8c8c94", "color-text-accent": "#006ce0", @@ -3680,6 +3685,7 @@ exports[`CSS Custom Properties match previous snapshot for mode "visual-refresh- "color-foreground-control-default": "#ffffff", "color-foreground-control-disabled": "#ffffff", "color-foreground-control-read-only": "#656871", + "color-gap-global-drawer": "#f3f3f7", "color-shadow-default": "rgba(15, 20, 26, 0.12)", "color-stroke-chart-line": "#8c8c94", "color-text-accent": "#006ce0", @@ -4345,6 +4351,7 @@ exports[`CSS Custom Properties match previous snapshot for mode "visual-refresh- "color-foreground-control-default": "#0f141a", "color-foreground-control-disabled": "#161d26", "color-foreground-control-read-only": "#a4a4ad", + "color-gap-global-drawer": "#232b37", "color-shadow-default": "rgba(15, 20, 26, 1)", "color-stroke-chart-line": "#8c8c94", "color-text-accent": "#42b4ff", @@ -5010,6 +5017,7 @@ exports[`CSS Custom Properties match previous snapshot for mode "visual-refresh- "color-foreground-control-default": "#0f141a", "color-foreground-control-disabled": "#161d26", "color-foreground-control-read-only": "#a4a4ad", + "color-gap-global-drawer": "#232b37", "color-shadow-default": "rgba(15, 20, 26, 1)", "color-stroke-chart-line": "#8c8c94", "color-text-accent": "#42b4ff", From 84e2fbc7f96af892b3ae9b9ad62fc0d6acc491f6 Mon Sep 17 00:00:00 2001 From: Georgii Lobko Date: Wed, 2 Apr 2025 16:24:11 +0200 Subject: [PATCH 14/43] feat: Split panel behaviour --- .../__tests__/runtime-drawers.test.tsx | 16 +++++++++++++--- .../toolbar/drawer-triggers.tsx | 3 +++ 2 files changed, 16 insertions(+), 3 deletions(-) diff --git a/src/app-layout/__tests__/runtime-drawers.test.tsx b/src/app-layout/__tests__/runtime-drawers.test.tsx index b223448715..7d7f4ea2da 100644 --- a/src/app-layout/__tests__/runtime-drawers.test.tsx +++ b/src/app-layout/__tests__/runtime-drawers.test.tsx @@ -8,6 +8,7 @@ import AppLayout, { AppLayoutProps } from '../../../lib/components/app-layout'; import { TOOLS_DRAWER_ID } from '../../../lib/components/app-layout/utils/use-drawers'; import { awsuiPlugins, awsuiPluginsInternal } from '../../../lib/components/internal/plugins/api'; import { DrawerConfig } from '../../../lib/components/internal/plugins/controllers/drawers'; +import SplitPanel from '../../../lib/components/split-panel'; import createWrapper from '../../../lib/components/test-utils/dom'; import { describeEachAppLayout, @@ -1417,8 +1418,7 @@ describe('toolbar mode only features', () => { expect(wrapper.findNavigationToggle()!.getElement()).not.toHaveClass(toolbarTriggerStyles.selected); }); - // test.each(['expanded', 'global-drawer', 'local-drawer', 'nav'] as const)('%s', theme => {}); - test.each(['expanded', 'global-drawer', 'local-drawer', 'nav'] as const)( + test.each(['expanded', 'split-panel', 'global-drawer', 'local-drawer', 'nav'] as const)( 'should return panels to their initial state after leaving expanded mode by clicking on %s button', async triggerName => { const drawerId1 = 'global-drawer1'; @@ -1441,7 +1441,11 @@ describe('toolbar mode only features', () => { id: drawerId3Local, }); const { wrapper, globalDrawersWrapper } = await renderComponent( - nav
} /> + nav} + splitPanel={test content} + /> ); await delay(); @@ -1449,12 +1453,14 @@ describe('toolbar mode only features', () => { wrapper.findDrawerTriggerById(drawerId1)!.click(); wrapper.findDrawerTriggerById(drawerId2)!.click(); wrapper.findDrawerTriggerById(drawerId3Local)!.click(); + wrapper.findSplitPanelOpenButton()!.click(); expect(wrapper.findDrawerTriggerById(drawerId1)!.getElement()).toHaveClass(toolbarTriggerStyles.selected); expect(wrapper.findDrawerTriggerById(drawerId2)!.getElement()).toHaveClass(toolbarTriggerStyles.selected); expect(wrapper.findDrawerTriggerById(drawerId3Local)!.getElement()).toHaveClass( toolbarTriggerStyles.selected ); + expect(wrapper.findSplitPanelOpenButton()!.getElement()).toHaveClass(toolbarTriggerStyles.selected); expect(wrapper.findNavigationToggle()!.getElement()).toHaveClass(toolbarTriggerStyles.selected); expect(globalDrawersWrapper.isLayoutInDrawerExpandedMode()).toBe(false); expect(globalDrawersWrapper.findDrawerById(drawerId1)!.isDrawerInExpandedMode()).toBe(false); @@ -1468,6 +1474,7 @@ describe('toolbar mode only features', () => { toolbarTriggerStyles.selected ); expect(wrapper.findNavigationToggle()!.getElement()).not.toHaveClass(toolbarTriggerStyles.selected); + expect(wrapper.findSplitPanelOpenButton()!.getElement()).not.toHaveClass(toolbarTriggerStyles.selected); if (triggerName === 'expanded') { globalDrawersWrapper.findExpandedModeButtonByActiveDrawerId(drawerId1)!.click(); @@ -1477,6 +1484,8 @@ describe('toolbar mode only features', () => { wrapper.findDrawerTriggerById(drawerId3Local)!.click(); } else if (triggerName === 'nav') { wrapper.findNavigationToggle()!.click(); + } else if (triggerName === 'split-panel') { + wrapper.findSplitPanelOpenButton()!.click(); } expect(wrapper.findDrawerTriggerById(drawerId1)!.getElement()).toHaveClass(toolbarTriggerStyles.selected); @@ -1485,6 +1494,7 @@ describe('toolbar mode only features', () => { toolbarTriggerStyles.selected ); expect(wrapper.findNavigationToggle()!.getElement()).toHaveClass(toolbarTriggerStyles.selected); + expect(wrapper.findSplitPanelOpenButton()!.getElement()).toHaveClass(toolbarTriggerStyles.selected); expect(globalDrawersWrapper.isLayoutInDrawerExpandedMode()).toBe(false); expect(globalDrawersWrapper.findDrawerById(drawerId1)!.isDrawerInExpandedMode()).toBe(false); } diff --git a/src/app-layout/visual-refresh-toolbar/toolbar/drawer-triggers.tsx b/src/app-layout/visual-refresh-toolbar/toolbar/drawer-triggers.tsx index ff8f1aa9dd..24c2ef7392 100644 --- a/src/app-layout/visual-refresh-toolbar/toolbar/drawer-triggers.tsx +++ b/src/app-layout/visual-refresh-toolbar/toolbar/drawer-triggers.tsx @@ -145,6 +145,9 @@ export function DrawerTriggers({ if (setExpandedDrawerId) { setExpandedDrawerId(undefined); } + if (!!expandedDrawerId && splitPanelToggleProps.active) { + return; + } onSplitPanelToggle?.(); }} selected={!expandedDrawerId && splitPanelToggleProps.active} From faa35359a2886ce0bed40c4ae96f7b2257fc3e08 Mon Sep 17 00:00:00 2001 From: Georgii Lobko Date: Thu, 3 Apr 2025 13:01:28 +0200 Subject: [PATCH 15/43] feat: Overflown triggers behavior in expanded mode --- .../__tests__/runtime-drawers.test.tsx | 63 +++++++++++++++++++ .../toolbar/drawer-triggers.tsx | 9 ++- 2 files changed, 71 insertions(+), 1 deletion(-) diff --git a/src/app-layout/__tests__/runtime-drawers.test.tsx b/src/app-layout/__tests__/runtime-drawers.test.tsx index 7d7f4ea2da..5ea61bdfda 100644 --- a/src/app-layout/__tests__/runtime-drawers.test.tsx +++ b/src/app-layout/__tests__/runtime-drawers.test.tsx @@ -15,6 +15,7 @@ import { findActiveDrawerLandmark, getActiveDrawerWidth, getGlobalDrawersTestUtils, + manyDrawers, testDrawer, } from './utils'; @@ -1500,6 +1501,68 @@ describe('toolbar mode only features', () => { } ); + test('should return panels to their initial state after leaving expanded mode by clicking on a button in the overflow menu', async () => { + const drawerId1 = 'global-drawer1'; + const drawerId2 = 'global-drawer2'; + awsuiPlugins.appLayout.registerDrawer({ + ...drawerDefaults, + id: drawerId1, + type: 'global', + isExpandable: true, + }); + awsuiPlugins.appLayout.registerDrawer({ + ...drawerDefaults, + id: drawerId2, + type: 'global', + isExpandable: true, + }); + const { wrapper, globalDrawersWrapper } = await renderComponent(); + + await delay(); + + const buttonDropdown = wrapper.findDrawersOverflowTrigger(); + + buttonDropdown!.openDropdown(); + buttonDropdown!.findItemById(drawerId1)!.click(); + buttonDropdown!.openDropdown(); + buttonDropdown!.findItemById(drawerId2)!.click(); + + buttonDropdown!.openDropdown(); + expect(buttonDropdown!.findItemById(drawerId1)!.getElement().firstElementChild).toHaveAttribute( + 'aria-checked', + 'true' + ); + expect(buttonDropdown!.findItemById(drawerId2)!.getElement().firstElementChild).toHaveAttribute( + 'aria-checked', + 'true' + ); + + globalDrawersWrapper.findExpandedModeButtonByActiveDrawerId(drawerId1)!.click(); + + expect(globalDrawersWrapper.findDrawerById(drawerId1)!.isDrawerInExpandedMode()).toBe(true); + buttonDropdown!.openDropdown(); + expect(buttonDropdown!.findItemById(drawerId1)!.getElement().firstElementChild).toHaveAttribute( + 'aria-checked', + 'true' + ); + expect(buttonDropdown!.findItemById(drawerId2)!.getElement().firstElementChild).toHaveAttribute( + 'aria-checked', + 'false' + ); + // leave expanded mode + buttonDropdown!.findItemById(drawerId2)!.click(); + buttonDropdown!.openDropdown(); + expect(buttonDropdown!.findItemById(drawerId1)!.getElement().firstElementChild).toHaveAttribute( + 'aria-checked', + 'true' + ); + expect(buttonDropdown!.findItemById(drawerId2)!.getElement().firstElementChild).toHaveAttribute( + 'aria-checked', + 'true' + ); + expect(globalDrawersWrapper.findDrawerById(drawerId1)!.isDrawerInExpandedMode()).toBe(false); + }); + test('should quit expanded mode when a drawer in expanded mode is closed', async () => { const drawerId = 'global-drawer'; awsuiPlugins.appLayout.registerDrawer({ diff --git a/src/app-layout/visual-refresh-toolbar/toolbar/drawer-triggers.tsx b/src/app-layout/visual-refresh-toolbar/toolbar/drawer-triggers.tsx index 24c2ef7392..741d4874ed 100644 --- a/src/app-layout/visual-refresh-toolbar/toolbar/drawer-triggers.tsx +++ b/src/app-layout/visual-refresh-toolbar/toolbar/drawer-triggers.tsx @@ -240,7 +240,7 @@ export function DrawerTriggers({ ({ ...item, - active: activeGlobalDrawersIds.includes(item.id), + active: activeGlobalDrawersIds.includes(item.id) && (!expandedDrawerId || item.id === expandedDrawerId), }))} ariaLabel={overflowMenuHasBadge ? ariaLabels?.drawersOverflowWithBadge : ariaLabels?.drawersOverflow} customTriggerBuilder={({ onClick, triggerRef, ariaLabel, ariaExpanded, testUtilsClass }) => { @@ -264,9 +264,16 @@ export function DrawerTriggers({ }} onItemClick={event => { const id = event.detail.id; + setExpandedDrawerId(undefined); if (globalDrawers.find(drawer => drawer.id === id)) { + if (!!expandedDrawerId && id !== expandedDrawerId && activeGlobalDrawersIds.includes(id)) { + return; + } onActiveGlobalDrawersChange?.(id, { initiatedByUserAction: true }); } else { + if (!!expandedDrawerId && activeDrawerId === id) { + return; + } onActiveDrawerChange?.(event.detail.id, { initiatedByUserAction: true }); } }} From 7c64515e2eb5a9129f88ce3263960d176aab2bb7 Mon Sep 17 00:00:00 2001 From: Georgii Lobko Date: Thu, 3 Apr 2025 16:02:54 +0200 Subject: [PATCH 16/43] chore: Overflow menu condition for local drawers --- .../visual-refresh-toolbar/toolbar/drawer-triggers.tsx | 3 --- 1 file changed, 3 deletions(-) diff --git a/src/app-layout/visual-refresh-toolbar/toolbar/drawer-triggers.tsx b/src/app-layout/visual-refresh-toolbar/toolbar/drawer-triggers.tsx index 741d4874ed..62e99c44e1 100644 --- a/src/app-layout/visual-refresh-toolbar/toolbar/drawer-triggers.tsx +++ b/src/app-layout/visual-refresh-toolbar/toolbar/drawer-triggers.tsx @@ -271,9 +271,6 @@ export function DrawerTriggers({ } onActiveGlobalDrawersChange?.(id, { initiatedByUserAction: true }); } else { - if (!!expandedDrawerId && activeDrawerId === id) { - return; - } onActiveDrawerChange?.(event.detail.id, { initiatedByUserAction: true }); } }} From be428d5609e36adb8762f2bc9938afe925ab4c3f Mon Sep 17 00:00:00 2001 From: Georgii Lobko Date: Thu, 3 Apr 2025 17:55:31 +0200 Subject: [PATCH 17/43] chore: Integ test to check page elements' visibility in focus mode --- .../__integ__/runtime-drawers.test.ts | 29 +++++++++++++++++++ 1 file changed, 29 insertions(+) diff --git a/src/app-layout/__integ__/runtime-drawers.test.ts b/src/app-layout/__integ__/runtime-drawers.test.ts index 3890d5d530..b886628b75 100644 --- a/src/app-layout/__integ__/runtime-drawers.test.ts +++ b/src/app-layout/__integ__/runtime-drawers.test.ts @@ -8,6 +8,7 @@ import { Theme } from '../../__integ__/utils.js'; import { viewports } from './constants'; import { getUrlParams } from './utils'; +import testUtilsStyles from '../../../lib/components/app-layout/test-classes/styles.selectors.js'; import vrDrawerStyles from '../../../lib/components/app-layout/visual-refresh/styles.selectors.js'; import vrToolbarDrawerStyles from '../../../lib/components/app-layout/visual-refresh-toolbar/drawer/styles.selectors.js'; @@ -19,6 +20,12 @@ const findDrawerContentById = (wrapper: AppLayoutWrapper, id: string) => { return wrapper.find(`[data-testid="awsui-app-layout-drawer-content-${id}"]`); }; +const findExpandedModeButtonByActiveDrawerId = (wrapper: AppLayoutWrapper, id: string) => { + return wrapper.find( + `[data-testid="awsui-app-layout-drawer-${id}"] .${testUtilsStyles['active-drawer-expanded-mode-button']}` + ); +}; + describe.each(['classic', 'refresh', 'refresh-toolbar'] as Theme[])('%s', theme => { function setupTest( { hasDrawers = 'false', url = 'runtime-drawers', size = viewports.desktop }, @@ -333,4 +340,26 @@ describe('Visual refresh toolbar only', () => { await expect(page.isDisplayed('[data-testid="drawer-sticky-header"]')).resolves.toBe(true); }) ); + + test( + 'should show only expanded drawer and hide all other panels if expanded mode for a drawer is active', + setupTest(async page => { + await page.setWindowSize({ ...viewports.desktop, width: 1600 }); + await page.click(wrapper.findDrawerTriggerById('circle-global').toSelector()); + await page.click(wrapper.findDrawerTriggerById('circle3-global').toSelector()); + await page.click(wrapper.findDrawerTriggerById('circle').toSelector()); + + await expect(page.isClickable(findDrawerById(wrapper, 'circle-global')!.toSelector())).resolves.toBe(true); + await expect(page.isClickable(findDrawerById(wrapper, 'circle3-global')!.toSelector())).resolves.toBe(true); + await expect(page.isClickable(findDrawerById(wrapper, 'circle')!.toSelector())).resolves.toBe(true); + await expect(page.isClickable(wrapper.findOpenNavigationPanel().toSelector())).resolves.toBe(true); + + await page.click(findExpandedModeButtonByActiveDrawerId(wrapper, 'circle-global').toSelector()); + + await expect(page.isClickable(findDrawerById(wrapper, 'circle-global')!.toSelector())).resolves.toBe(true); + await expect(page.isClickable(findDrawerById(wrapper, 'circle3-global')!.toSelector())).resolves.toBe(false); + await expect(page.isClickable(findDrawerById(wrapper, 'circle')!.toSelector())).resolves.toBe(false); + await expect(page.isClickable(wrapper.findOpenNavigationPanel().toSelector())).resolves.toBe(false); + }) + ); }); From 7045cd21f542288319a765a3c723fcc82694554e Mon Sep 17 00:00:00 2001 From: Georgii Lobko Date: Fri, 4 Apr 2025 15:39:36 +0200 Subject: [PATCH 18/43] feat: Expanded mode for nested app layouts --- .../__tests__/multi-layout-props.test.tsx | 2 + .../__tests__/runtime-drawers.test.tsx | 47 ++++++++++++++++++- .../visual-refresh-toolbar/index.tsx | 6 +++ .../visual-refresh-toolbar/multi-layout.ts | 6 +++ .../visual-refresh-toolbar/skeleton/index.tsx | 4 +- .../toolbar/drawer-triggers.tsx | 12 +++-- .../visual-refresh-toolbar/toolbar/index.tsx | 9 ++-- 7 files changed, 78 insertions(+), 8 deletions(-) diff --git a/src/app-layout/__tests__/multi-layout-props.test.tsx b/src/app-layout/__tests__/multi-layout-props.test.tsx index 06cc4228f3..2ebf951069 100644 --- a/src/app-layout/__tests__/multi-layout-props.test.tsx +++ b/src/app-layout/__tests__/multi-layout-props.test.tsx @@ -9,6 +9,7 @@ describe('mergeMultiAppLayoutProps', () => { const mockParentActiveDrawerChange = jest.fn(); const mockParentActiveGlobalDrawerChange = jest.fn(); const mockParentSplitPanelToggle = jest.fn(); + const mockSetExpandedDrawerId = jest.fn(); const ownProps: SharedProps = { forceDeduplicationType: 'primary', ariaLabels: { @@ -43,6 +44,7 @@ describe('mergeMultiAppLayoutProps', () => { }, splitPanelFocusRef: React.createRef(), onSplitPanelToggle: mockParentSplitPanelToggle, + setExpandedDrawerId: mockSetExpandedDrawerId, }; const additionalPropsBase: Partial[] = [ diff --git a/src/app-layout/__tests__/runtime-drawers.test.tsx b/src/app-layout/__tests__/runtime-drawers.test.tsx index 5ea61bdfda..2a4ce8b783 100644 --- a/src/app-layout/__tests__/runtime-drawers.test.tsx +++ b/src/app-layout/__tests__/runtime-drawers.test.tsx @@ -1,7 +1,7 @@ // Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. // SPDX-License-Identifier: Apache-2.0 import React, { useState } from 'react'; -import { act, fireEvent, render } from '@testing-library/react'; +import { act, fireEvent, render, waitFor } from '@testing-library/react'; import { Button } from '../../../lib/components'; import AppLayout, { AppLayoutProps } from '../../../lib/components/app-layout'; @@ -20,6 +20,7 @@ import { } from './utils'; import triggerStyles from '../../../lib/components/app-layout/visual-refresh/styles.selectors.js'; +import skeletonStyles from '../../../lib/components/app-layout/visual-refresh-toolbar/skeleton/styles.selectors.js'; import toolbarStyles from '../../../lib/components/app-layout/visual-refresh-toolbar/toolbar/styles.selectors.js'; import toolbarTriggerStyles from '../../../lib/components/app-layout/visual-refresh-toolbar/toolbar/trigger-button/styles.selectors.js'; import iconStyles from '../../../lib/components/icon/styles.selectors.js'; @@ -1582,6 +1583,50 @@ describe('toolbar mode only features', () => { wrapper.findDrawerTriggerById(drawerId)!.click(); expect(globalDrawersWrapper.isLayoutInDrawerExpandedMode()).toBe(false); }); + + describe('nested app layouts', () => { + test('should apply expanded drawer mode only for inner AppLayout and hide nav for the outer AppLayout', async () => { + const drawerId = 'global-drawer'; + awsuiPlugins.appLayout.registerDrawer({ + ...drawerDefaults, + id: drawerId, + type: 'global', + isExpandable: true, + }); + await renderComponent( + } + /> + ); + + const outerLayout = createWrapper().find('[data-testid="first"]')!.findAppLayout()!; + const innerLayout = createWrapper().find('[data-testid="second"]')!.findAppLayout()!; + const innerGlobalDrawers = getGlobalDrawersTestUtils(outerLayout); + const outerGlobalDrawers = getGlobalDrawersTestUtils(innerLayout); + + await delay(); + + outerLayout.findDrawerTriggerById(drawerId)!.click(); + + await waitFor(() => + expect(outerLayout.findDrawerTriggerById(drawerId)!.getElement()).toHaveClass(toolbarTriggerStyles.selected) + ); + expect(outerLayout.findNavigationToggle()!.getElement()).toHaveClass(toolbarTriggerStyles.selected); + expect(innerGlobalDrawers.isLayoutInDrawerExpandedMode()).toBe(false); + expect(innerGlobalDrawers.findDrawerById(drawerId)!.isDrawerInExpandedMode()).toBe(false); + + innerGlobalDrawers.findExpandedModeButtonByActiveDrawerId(drawerId)!.click(); + + expect(innerGlobalDrawers.isLayoutInDrawerExpandedMode()).toBe(false); + expect(outerGlobalDrawers.isLayoutInDrawerExpandedMode()).toBe(true); + + await waitFor(() => + expect(outerLayout.find(`.${skeletonStyles.navigation}`)!.getElement()).toHaveClass(skeletonStyles.hidden) + ); + }); + }); }); }); diff --git a/src/app-layout/visual-refresh-toolbar/index.tsx b/src/app-layout/visual-refresh-toolbar/index.tsx index 1c8bc07156..35b8c45f85 100644 --- a/src/app-layout/visual-refresh-toolbar/index.tsx +++ b/src/app-layout/visual-refresh-toolbar/index.tsx @@ -298,6 +298,8 @@ const AppLayoutVisualRefreshToolbar = React.forwardRef | undefined; onSplitPanelToggle: () => void; + expandedDrawerId?: string; + setExpandedDrawerId: (value: string | undefined) => void; } function checkAlreadyExists(value: boolean, propName: string) { @@ -87,6 +89,10 @@ export function mergeProps( if (props.breadcrumbs && !checkAlreadyExists(!!toolbar.hasBreadcrumbsPortal, 'hasBreadcrumbsPortal')) { toolbar.hasBreadcrumbsPortal = true; } + if (props.expandedDrawerId && !checkAlreadyExists(!!toolbar.expandedDrawerId, 'expandedDrawerId')) { + toolbar.expandedDrawerId = props.expandedDrawerId; + toolbar.setExpandedDrawerId = props.setExpandedDrawerId; + } } // do not render toolbar if no fields are defined, except ariaLabels, which are always there return Object.keys(toolbar).filter(key => key !== 'ariaLabels').length > 0 ? toolbar : null; diff --git a/src/app-layout/visual-refresh-toolbar/skeleton/index.tsx b/src/app-layout/visual-refresh-toolbar/skeleton/index.tsx index f18ec26378..d9dbba1b9d 100644 --- a/src/app-layout/visual-refresh-toolbar/skeleton/index.tsx +++ b/src/app-layout/visual-refresh-toolbar/skeleton/index.tsx @@ -42,6 +42,7 @@ interface SkeletonLayoutProps navigationAnimationDisabled?: boolean; isNested?: boolean; drawerExpandedMode: boolean; + drawerExpandedModeInChildLayout: boolean; } export const SkeletonLayout = React.forwardRef( @@ -71,6 +72,7 @@ export const SkeletonLayout = React.forwardRef { @@ -100,7 +102,7 @@ export const SkeletonLayout = React.forwardRef {navigation} diff --git a/src/app-layout/visual-refresh-toolbar/toolbar/drawer-triggers.tsx b/src/app-layout/visual-refresh-toolbar/toolbar/drawer-triggers.tsx index 62e99c44e1..e0a72b1324 100644 --- a/src/app-layout/visual-refresh-toolbar/toolbar/drawer-triggers.tsx +++ b/src/app-layout/visual-refresh-toolbar/toolbar/drawer-triggers.tsx @@ -177,7 +177,9 @@ export function DrawerTriggers({ iconSvg={item.trigger!.iconSvg} key={item.id} onClick={() => { - setExpandedDrawerId(undefined); + if (setExpandedDrawerId) { + setExpandedDrawerId(undefined); + } if (!!expandedDrawerId && activeDrawerId === item.id) { return; } @@ -217,7 +219,9 @@ export function DrawerTriggers({ iconSvg={item.trigger!.iconSvg} key={item.id} onClick={() => { - setExpandedDrawerId(undefined); + if (setExpandedDrawerId) { + setExpandedDrawerId(undefined); + } if (!!expandedDrawerId && item.id !== expandedDrawerId && activeGlobalDrawersIds.includes(item.id)) { return; } @@ -264,7 +268,9 @@ export function DrawerTriggers({ }} onItemClick={event => { const id = event.detail.id; - setExpandedDrawerId(undefined); + if (setExpandedDrawerId) { + setExpandedDrawerId(undefined); + } if (globalDrawers.find(drawer => drawer.id === id)) { if (!!expandedDrawerId && id !== expandedDrawerId && activeGlobalDrawersIds.includes(id)) { return; diff --git a/src/app-layout/visual-refresh-toolbar/toolbar/index.tsx b/src/app-layout/visual-refresh-toolbar/toolbar/index.tsx index e56f06dc79..c9b51cde4f 100644 --- a/src/app-layout/visual-refresh-toolbar/toolbar/index.tsx +++ b/src/app-layout/visual-refresh-toolbar/toolbar/index.tsx @@ -47,6 +47,9 @@ export interface ToolbarProps { globalDrawers?: ReadonlyArray | undefined; activeGlobalDrawersIds?: ReadonlyArray; onActiveGlobalDrawersChange?: ((drawerId: string, params: OnChangeParams) => void) | undefined; + + expandedDrawerId?: string; + setExpandedDrawerId?: (value: string | undefined) => void; } export interface AppLayoutToolbarImplementationProps { @@ -68,8 +71,6 @@ export function AppLayoutToolbarImplementation({ toolbarState, setToolbarState, setToolbarHeight, - expandedDrawerId, - setExpandedDrawerId, } = appLayoutInternals; const { ariaLabels, @@ -89,6 +90,8 @@ export function AppLayoutToolbarImplementation({ splitPanelFocusRef, splitPanelToggleProps, onSplitPanelToggle, + expandedDrawerId, + setExpandedDrawerId, } = toolbarProps; // TODO: expose configuration property const pinnedToolbar = true; @@ -204,7 +207,7 @@ export function AppLayoutToolbarImplementation({ activeGlobalDrawersIds={activeGlobalDrawersIds ?? []} onActiveGlobalDrawersChange={onActiveGlobalDrawersChange} expandedDrawerId={expandedDrawerId} - setExpandedDrawerId={setExpandedDrawerId} + setExpandedDrawerId={setExpandedDrawerId!} /> )} From ec0260826a83488c410954cd39fcfa81e17c1afb Mon Sep 17 00:00:00 2001 From: Georgii Lobko Date: Mon, 14 Apr 2025 16:03:17 +0200 Subject: [PATCH 19/43] chore: Increase size limit --- package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/package.json b/package.json index f9a75bf7db..f7d6b1072e 100644 --- a/package.json +++ b/package.json @@ -164,7 +164,7 @@ { "path": "lib/components/internal/widget-exports.js", "brotli": false, - "limit": "753 kB", + "limit": "770 kB", "ignore": "react-dom" } ], From dfabb702db30e5f19494f48dc0c4db4af92c75c6 Mon Sep 17 00:00:00 2001 From: Georgii Lobko Date: Mon, 5 May 2025 13:20:39 +0200 Subject: [PATCH 20/43] fix: Hide side split panel when focus mode is on --- src/app-layout/visual-refresh-toolbar/skeleton/index.tsx | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/src/app-layout/visual-refresh-toolbar/skeleton/index.tsx b/src/app-layout/visual-refresh-toolbar/skeleton/index.tsx index 99cf176f41..c11e14d3d6 100644 --- a/src/app-layout/visual-refresh-toolbar/skeleton/index.tsx +++ b/src/app-layout/visual-refresh-toolbar/skeleton/index.tsx @@ -140,7 +140,13 @@ export const SkeletonLayout = React.forwardRef {sideSplitPanel && ( -
+
{sideSplitPanel}
)} From 028a2e44b7cc458c43452df7d1a013d6e7ffc8e2 Mon Sep 17 00:00:00 2001 From: Georgii Lobko Date: Tue, 6 May 2025 12:13:05 +0200 Subject: [PATCH 21/43] chore: Adjust expanded drawer styles --- src/app-layout/visual-refresh-toolbar/drawer/styles.scss | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/app-layout/visual-refresh-toolbar/drawer/styles.scss b/src/app-layout/visual-refresh-toolbar/drawer/styles.scss index d84d437e9a..6d6bc57e2d 100644 --- a/src/app-layout/visual-refresh-toolbar/drawer/styles.scss +++ b/src/app-layout/visual-refresh-toolbar/drawer/styles.scss @@ -74,7 +74,7 @@ $global-drawer-gap-size: 8px; display: none; } - &.drawer-expanded { + &.drawer-expanded:not(:last-child) { inline-size: 100%; @include desktop-only { From 8500e9e935b2c4e54410728500f75adc56cd7849 Mon Sep 17 00:00:00 2001 From: Georgii Lobko Date: Tue, 6 May 2025 12:36:38 +0200 Subject: [PATCH 22/43] chore: Adjust expanded drawer styles --- .../visual-refresh-toolbar/drawer/styles.scss | 20 ++++++++++--------- 1 file changed, 11 insertions(+), 9 deletions(-) diff --git a/src/app-layout/visual-refresh-toolbar/drawer/styles.scss b/src/app-layout/visual-refresh-toolbar/drawer/styles.scss index 6d6bc57e2d..e9479aa189 100644 --- a/src/app-layout/visual-refresh-toolbar/drawer/styles.scss +++ b/src/app-layout/visual-refresh-toolbar/drawer/styles.scss @@ -74,20 +74,22 @@ $global-drawer-gap-size: 8px; display: none; } - &.drawer-expanded:not(:last-child) { + &.drawer-expanded { inline-size: 100%; @include desktop-only { border-inline-start: none; - &:after { - content: ''; - position: absolute; - block-size: 100%; - inline-size: $global-drawer-gap-size; - inset-inline-end: 0; - background: awsui.$color-gap-global-drawer; - border-inline-start: awsui.$border-divider-section-width solid awsui.$color-border-layout; + &:not(:last-child) { + &:after { + content: ''; + position: absolute; + block-size: 100%; + inline-size: $global-drawer-gap-size; + inset-inline-end: 0; + background: awsui.$color-gap-global-drawer; + border-inline-start: awsui.$border-divider-section-width solid awsui.$color-border-layout; + } } } } From ee1abe427703583f732f8151ad10c562d1e2a8f4 Mon Sep 17 00:00:00 2001 From: at-susie Date: Tue, 6 May 2025 15:21:16 +0200 Subject: [PATCH 23/43] chore: Explore drawer animation (#3472) --- pages/app-layout/sidecar-demo.page.tsx | 213 ++++++++++++++++ .../app-layout/utils/external-widget-demo.tsx | 228 ++++++++++++++++++ .../drawer/global-drawer.tsx | 5 +- .../visual-refresh-toolbar/drawer/styles.scss | 6 + .../skeleton/styles.scss | 22 +- .../components/panel-resize-handle/icon.tsx | 3 +- 6 files changed, 463 insertions(+), 14 deletions(-) create mode 100644 pages/app-layout/sidecar-demo.page.tsx create mode 100644 pages/app-layout/utils/external-widget-demo.tsx diff --git a/pages/app-layout/sidecar-demo.page.tsx b/pages/app-layout/sidecar-demo.page.tsx new file mode 100644 index 0000000000..8535deb906 --- /dev/null +++ b/pages/app-layout/sidecar-demo.page.tsx @@ -0,0 +1,213 @@ +// Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. +// SPDX-License-Identifier: Apache-2.0 +import React, { useContext, useRef, useState } from 'react'; + +import { + AppLayout, + Box, + Button, + CopyToClipboard, + Header, + HelpPanel, + KeyValuePairs, + Link, + ProgressBar, + SideNavigation, + SpaceBetween, + SplitPanel, + StatusIndicator, + Table, +} from '~components'; +import { AppLayoutProps } from '~components/app-layout'; + +import './utils/external-widget-demo'; +import AppContext, { AppContextType } from '../app/app-context'; +import { generateItems, Instance } from '../table/generate-data'; +import { columnsConfig } from '../table/shared-configs'; +import { Breadcrumbs } from './utils/content-blocks'; +import { drawerLabels } from './utils/drawers'; +import appLayoutLabels from './utils/labels'; + +type DemoContext = React.Context< + AppContextType<{ + hasTools: boolean | undefined; + hasDrawers: boolean | undefined; + splitPanelPosition: AppLayoutProps.SplitPanelPreferences['position']; + }> +>; + +export default function WithDrawers() { + const { urlParams, setUrlParams } = useContext(AppContext as DemoContext); + const hasTools = urlParams.hasTools ?? true; + const appLayoutRef = useRef(null); + + const [activeHref, setActiveHref] = useState('#/'); + const [navigationOpen, setNavigationOpen] = useState(true); + const [toolsOpen, setToolsOpen] = useState(false); + + const items = generateItems(20); + + function openHelp() { + setToolsOpen(true); + } + + return ( + } + ref={appLayoutRef} + navigation={ + { + e.preventDefault(); + setActiveHref(e.detail.href); + }} + items={[ + { type: 'link', text: 'Side nav menu A', href: '#/menu-a' }, + { type: 'link', text: 'Side nav menu B', href: '#/menu-b' }, + { type: 'link', text: 'Side nav menu C', href: '#/menu-c' }, + ]} + /> + } + navigationOpen={navigationOpen} + onNavigationChange={({ detail }) => setNavigationOpen(detail.open)} + content={ + + header={ +
openHelp()}> + Info + + } + actions={} + > + Sticky Scrollbar Example +
+ } + stickyHeader={true} + variant="full-page" + columnDefinitions={columnsConfig} + items={items} + selectionType="multi" + ariaLabels={{ + selectionGroupLabel: 'Item selection', + allItemsSelectionLabel: () => 'Select all items', + itemSelectionLabel: (_, item) => `Select ${item.id}`, + }} + /> + } + splitPanel={ + + + + Receive real-time data insights to build process improvements, track key performance indicators, and + predict future business outcomes. Create a new Cloud Data Solution account to receive a 30 day free trial + of all Cloud Data Solution services. + + + ), + }, + { + label: 'Status', + value: Available, + }, + ], + }, + + { + type: 'group', + items: [ + { + label: 'SSL Certificate', + id: 'ssl-certificate-id', + value: ( + + ), + }, + { + label: 'Price class', + value: 'Use only US, Canada, Europe', + }, + { + label: 'CNAMEs', + value: ( + + abc.service23G24.xyz + + ), + }, + ], + }, + ]} + /> + + + } + splitPanelPreferences={{ + position: urlParams.splitPanelPosition, + }} + onSplitPanelPreferencesChange={event => { + const { position } = event.detail; + setUrlParams({ splitPanelPosition: position === 'side' ? position : undefined }); + }} + toolsOpen={toolsOpen} + toolsHide={!hasTools} + onToolsChange={({ detail }) => setToolsOpen(detail.open)} + tools={ + Help}> +

This is a demo page showcasing the AppLayout component with a sticky header and scrollable content.

+

Features:

+
    +
  • Responsive navigation sidebar
  • +
  • Sticky header with actions
  • +
  • Full-page table with sticky header
  • +
  • Split panel support
  • +
+
+ } + /> + ); +} diff --git a/pages/app-layout/utils/external-widget-demo.tsx b/pages/app-layout/utils/external-widget-demo.tsx new file mode 100644 index 0000000000..9489baebe6 --- /dev/null +++ b/pages/app-layout/utils/external-widget-demo.tsx @@ -0,0 +1,228 @@ +// Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. +// SPDX-License-Identifier: Apache-2.0 +import React from 'react'; +import ReactDOM, { unmountComponentAtNode } from 'react-dom'; + +import { Alert, BarChart, Box, ColumnLayout, FormField, Input, SpaceBetween, Textarea, Tiles } from '~components'; +import awsuiPlugins from '~components/internal/plugins'; + +function Details() { + const [value, setValue] = React.useState('item1'); + const [inputValue, setInputValue] = React.useState(''); + const [valueText, setValueText] = React.useState(''); + return ( + + setValue(detail.value)} + value={value} + items={[ + { + label: 'Start a new investigation', + value: 'item1', + }, + { + label: 'Add to an existing investigation', + value: 'item2', + }, + ]} + /> + + + Investigation details + + + setInputValue(event.detail.value)} + placeholder="Enter name of investigation" + ariaLabel="Investigation name input" + /> + + + + + New finding details + + '$' + + e.toLocaleString('en-US', { + minimumFractionDigits: 2, + maximumFractionDigits: 2, + }), + }, + { + title: 'Average revenue', + type: 'threshold', + y: 19104, + valueFormatter: e => + '$' + + e.toLocaleString('en-US', { + minimumFractionDigits: 2, + maximumFractionDigits: 2, + }), + }, + ]} + xDomain={[ + new Date(1601071200000), + new Date(1601078400000), + new Date(1601085600000), + new Date(1601092800000), + new Date(1601100000000), + ]} + yDomain={[-10000, 40000]} + i18nStrings={{ + xTickFormatter: e => + e + .toLocaleDateString('en-US', { + month: 'short', + day: 'numeric', + hour: 'numeric', + minute: 'numeric', + hour12: !1, + }) + .split(',') + .join('\n'), + yTickFormatter: function o(e) { + return Math.abs(e) >= 1e9 + ? (e / 1e9).toFixed(1).replace(/\.0$/, '') + 'G' + : Math.abs(e) >= 1e6 + ? (e / 1e6).toFixed(1).replace(/\.0$/, '') + 'M' + : Math.abs(e) >= 1e3 + ? (e / 1e3).toFixed(1).replace(/\.0$/, '') + 'K' + : e.toFixed(2); + }, + }} + ariaLabel="Revenue chart showing Site 1 performance and average revenue threshold" + height={300} + hideFilter={true} + hideLegend={true} + xTitle="Time (UTC)" + yTitle="Revenue (USD)" + /> + + +