diff --git a/pages/app-layout/multi-layout-with-hidden-instances-iframe.page.tsx b/pages/app-layout/multi-layout-with-hidden-instances-iframe.page.tsx
index fda7d9f39e..3e848ec557 100644
--- a/pages/app-layout/multi-layout-with-hidden-instances-iframe.page.tsx
+++ b/pages/app-layout/multi-layout-with-hidden-instances-iframe.page.tsx
@@ -9,6 +9,7 @@ import Link from '~components/link';
import SideNavigation, { SideNavigationProps } from '~components/side-navigation';
import SpaceBetween from '~components/space-between';
+import './utils/external-widget';
import { IframeWrapper } from '../utils/iframe-wrapper';
import ScreenshotArea from '../utils/screenshot-area';
import { Breadcrumbs, Tools } from './utils/content-blocks';
@@ -18,7 +19,6 @@ function createView(name: string) {
return function View() {
return (
}
@@ -55,6 +55,7 @@ export default function () {
return (
{
console.log('security drawer on toggle', event.detail);
+ awsuiPlugins.appLayout.updateDrawer({ id: 'security', defaultActive: event.detail.isOpen });
},
resizable: true,
@@ -56,6 +57,7 @@ awsuiPlugins.appLayout.registerDrawer({
onResize: event => {
setSizeRef.current?.(true);
console.log('resize', event.detail);
+ awsuiPlugins.appLayout.updateDrawer({ id: 'security', defaultSize: event.detail.size });
},
mountContent: container => {
@@ -165,7 +167,7 @@ awsuiPlugins.appLayout.registerDrawer({
});
awsuiPlugins.appLayout.registerDrawer({
- id: 'circle2-global',
+ id: 'global-with-stored-state',
type: 'global',
defaultActive: false,
resizable: true,
@@ -173,7 +175,7 @@ awsuiPlugins.appLayout.registerDrawer({
ariaLabels: {
closeButton: 'Close button',
- content: 'Content',
+ content: 'Drawer with counter',
triggerButton: 'Trigger button',
resizeHandle: 'Resize handle',
},
@@ -185,10 +187,14 @@ awsuiPlugins.appLayout.registerDrawer({
`,
},
+ onToggle(event) {
+ awsuiPlugins.appLayout.updateDrawer({ id: 'global-with-stored-state', defaultActive: event.detail.isOpen });
+ },
+
mountContent: container => {
ReactDOM.render(
<>
-
+
global widget content circle 2
>,
container
diff --git a/pages/app-layout/with-drawers-scrollable.page.tsx b/pages/app-layout/with-drawers-scrollable.page.tsx
index b29765b421..d25b7417b5 100644
--- a/pages/app-layout/with-drawers-scrollable.page.tsx
+++ b/pages/app-layout/with-drawers-scrollable.page.tsx
@@ -64,7 +64,7 @@ awsuiPlugins.appLayout.registerDrawer({
unmountContent: container => unmountComponentAtNode(container),
});
awsuiPlugins.appLayout.registerDrawer({
- id: 'circle2-global',
+ id: 'global-with-stored-state',
type: 'global',
defaultActive: false,
resizable: true,
diff --git a/src/app-layout/__integ__/runtime-drawers.test.ts b/src/app-layout/__integ__/runtime-drawers.test.ts
index 27e098fb65..4c565e9e1e 100644
--- a/src/app-layout/__integ__/runtime-drawers.test.ts
+++ b/src/app-layout/__integ__/runtime-drawers.test.ts
@@ -16,28 +16,30 @@ const findDrawerContentById = (wrapper: AppLayoutWrapper, id: string) => {
};
describe.each(['classic', 'refresh', 'refresh-toolbar'] as Theme[])('%s', theme => {
- function setupTest({ hasDrawers = 'false' }, testFn: (page: BasePageObject) => Promise) {
- return useBrowser(async browser => {
+ function setupTest(
+ { hasDrawers = 'false', url = 'runtime-drawers', size = viewports.desktop },
+ testFn: (page: BasePageObject) => Promise
+ ) {
+ return useBrowser({ width: size.width, height: size.height }, async browser => {
const page = new BasePageObject(browser);
await browser.url(
- `#/light/app-layout/runtime-drawers?${getUrlParams(theme, {
+ `#/light/app-layout/${url}?${getUrlParams(theme, {
hasDrawers: hasDrawers,
hasTools: 'true',
splitPanelPosition: 'side',
})}`
);
- await page.waitForVisible(wrapper.findDrawerTriggerById('security').toSelector(), true);
+ await page.waitForVisible(wrapper.findContentRegion().toSelector());
await testFn(page);
});
}
- //drawer width assertions not neccessary for mobile
+ //drawer width assertions not necessary for mobile
describe('desktop', () => {
test(
'should resize equally with tools or drawers',
- setupTest({}, async page => {
- await page.setWindowSize({ ...viewports.desktop, width: 1800 });
+ setupTest({ size: { ...viewports.desktop, width: 1800 } }, async page => {
await page.click(wrapper.findToolsToggle().toSelector());
await page.click(wrapper.findSplitPanel().findOpenButton().toSelector());
@@ -53,7 +55,6 @@ describe.each(['classic', 'refresh', 'refresh-toolbar'] as Theme[])('%s', theme
test(
'renders according to defaultSize property',
setupTest({}, async page => {
- await page.setWindowSize(viewports.desktop);
await page.click(wrapper.findDrawerTriggerById('security').toSelector());
// using `clientWidth` to neglect possible border width set on this element
const width = await page.getElementProperty(wrapper.findActiveDrawer().toSelector(), 'clientWidth');
@@ -64,7 +65,6 @@ describe.each(['classic', 'refresh', 'refresh-toolbar'] as Theme[])('%s', theme
test(
'should call resize handler',
setupTest({}, async page => {
- await page.setWindowSize(viewports.desktop);
// close navigation panel to give drawer more room to resize
await page.click(wrapper.findNavigationClose().toSelector());
await page.click(wrapper.findDrawerTriggerById('security').toSelector());
@@ -76,10 +76,38 @@ describe.each(['classic', 'refresh', 'refresh-toolbar'] as Theme[])('%s', theme
})
);
+ test(
+ 'should persist runtime drawer preferences when switching between multiple app layouts',
+ setupTest(
+ {
+ url: 'multi-layout-with-hidden-instances-iframe',
+ },
+ async page => {
+ await page.runInsideIframe('#page1', theme !== 'refresh-toolbar', async () => {
+ await page.click(wrapper.findDrawerTriggerById('security').toSelector());
+ });
+ let newWidth: number;
+ await page.runInsideIframe('#page1', true, async () => {
+ await expect(page.getText(wrapper.findActiveDrawer().toSelector())).resolves.toContain('Security');
+ const { width: originalWidth } = await page.getBoundingBox(wrapper.findActiveDrawer().toSelector());
+ await page.dragAndDrop(wrapper.findActiveDrawerResizeHandle().toSelector(), -200);
+ ({ width: newWidth } = await page.getBoundingBox(wrapper.findActiveDrawer().toSelector()));
+ expect(newWidth).toBeGreaterThan(originalWidth);
+ });
+
+ await page.click(wrapper.findNavigation().findSideNavigation().findLinkByHref('page2').toSelector());
+ await page.runInsideIframe('#page2', true, async () => {
+ await page.waitForVisible(wrapper.findActiveDrawer().toSelector());
+ expect((await page.getBoundingBox(wrapper.findActiveDrawer().toSelector())).width).toEqual(newWidth!);
+ await expect(page.getText(wrapper.findActiveDrawer().toSelector())).resolves.toContain('Security');
+ });
+ }
+ )
+ );
+
test(
'should show sticky elements on scroll in drawer',
setupTest({ hasDrawers: 'true' }, async page => {
- await page.setWindowSize(viewports.desktop);
await page.waitForVisible(wrapper.findDrawerTriggerById('pro-help').toSelector(), true);
await expect(page.isDisplayed('[data-testid="drawer-sticky-footer"]')).resolves.toBe(false);
@@ -151,11 +179,13 @@ describe('Visual refresh toolbar only', () => {
await page.setWindowSize({ ...viewports.desktop, width: 1700 });
await page.click(wrapper.findDrawerTriggerById('security').toSelector());
await page.click(wrapper.findDrawerTriggerById('circle-global').toSelector());
- await page.click(wrapper.findDrawerTriggerById('circle2-global').toSelector());
+ await page.click(wrapper.findDrawerTriggerById('global-with-stored-state').toSelector());
await expect(page.isClickable(findDrawerById(wrapper, 'security')!.toSelector())).resolves.toBe(true);
await expect(page.isClickable(findDrawerById(wrapper, 'circle-global')!.toSelector())).resolves.toBe(true);
- await expect(page.isClickable(findDrawerById(wrapper, 'circle2-global')!.toSelector())).resolves.toBe(true);
+ await expect(page.isClickable(findDrawerById(wrapper, 'global-with-stored-state')!.toSelector())).resolves.toBe(
+ true
+ );
})
);
@@ -165,7 +195,7 @@ describe('Visual refresh toolbar only', () => {
setupTest(async page => {
await page.setWindowSize({ ...viewports.desktop, width: 1600 });
await page.click(wrapper.findDrawerTriggerById('circle-global').toSelector());
- await page.click(wrapper.findDrawerTriggerById('circle2-global').toSelector());
+ await page.click(wrapper.findDrawerTriggerById('global-with-stored-state').toSelector());
// resize an active drawer to take up all available space
await page.dragAndDrop(wrapper.findActiveDrawerResizeHandle().toSelector(), -600);
@@ -174,7 +204,9 @@ describe('Visual refresh toolbar only', () => {
await expect(page.isClickable(findDrawerById(wrapper, 'security')!.toSelector())).resolves.toBe(true);
await expect(page.isClickable(findDrawerById(wrapper, 'circle-global')!.toSelector())).resolves.toBe(true);
- await expect(page.isClickable(findDrawerById(wrapper, 'circle2-global')!.toSelector())).resolves.toBe(true);
+ await expect(page.isClickable(findDrawerById(wrapper, 'global-with-stored-state')!.toSelector())).resolves.toBe(
+ true
+ );
})
);
@@ -183,12 +215,14 @@ describe('Visual refresh toolbar only', () => {
setupTest(async page => {
await page.setWindowSize({ ...viewports.desktop, width: 1400 });
await page.click(wrapper.findDrawerTriggerById('circle-global').toSelector());
- await page.click(wrapper.findDrawerTriggerById('circle2-global').toSelector());
+ await page.click(wrapper.findDrawerTriggerById('global-with-stored-state').toSelector());
await page.click(wrapper.findDrawerTriggerById('security').toSelector());
await expect(page.isClickable(findDrawerById(wrapper, 'circle-global')!.toSelector())).resolves.toBe(false);
await expect(page.isClickable(findDrawerById(wrapper, 'security')!.toSelector())).resolves.toBe(true);
- await expect(page.isClickable(findDrawerById(wrapper, 'circle2-global')!.toSelector())).resolves.toBe(true);
+ await expect(page.isClickable(findDrawerById(wrapper, 'global-with-stored-state')!.toSelector())).resolves.toBe(
+ true
+ );
})
);
@@ -199,12 +233,14 @@ describe('Visual refresh toolbar only', () => {
await page.click(wrapper.findDrawerTriggerById('circle').toSelector());
await page.click(wrapper.findDrawerTriggerById('security').toSelector());
await page.click(wrapper.findDrawerTriggerById('circle-global').toSelector());
- await page.click(wrapper.findDrawerTriggerById('circle2-global').toSelector());
+ await page.click(wrapper.findDrawerTriggerById('global-with-stored-state').toSelector());
await expect(page.isClickable(findDrawerById(wrapper, 'circle')!.toSelector())).resolves.toBe(false);
await expect(page.isClickable(findDrawerById(wrapper, 'security')!.toSelector())).resolves.toBe(false);
await expect(page.isClickable(findDrawerById(wrapper, 'circle-global')!.toSelector())).resolves.toBe(true);
- await expect(page.isClickable(findDrawerById(wrapper, 'circle2-global')!.toSelector())).resolves.toBe(true);
+ await expect(page.isClickable(findDrawerById(wrapper, 'global-with-stored-state')!.toSelector())).resolves.toBe(
+ true
+ );
})
);
});
@@ -214,12 +250,14 @@ describe('Visual refresh toolbar only', () => {
setupTest(async page => {
await page.setWindowSize({ ...viewports.desktop, width: 1600 });
await page.click(wrapper.findDrawerTriggerById('circle').toSelector());
- await page.click(wrapper.findDrawerTriggerById('circle2-global').toSelector());
+ await page.click(wrapper.findDrawerTriggerById('global-with-stored-state').toSelector());
await page.click(wrapper.findDrawerTriggerById('circle3-global').toSelector());
await expect(page.isClickable(wrapper.findNavigation().toSelector())).resolves.toBe(true);
await expect(page.isClickable(findDrawerById(wrapper, 'circle')!.toSelector())).resolves.toBe(true);
- await expect(page.isClickable(findDrawerById(wrapper, 'circle2-global')!.toSelector())).resolves.toBe(true);
+ await expect(page.isClickable(findDrawerById(wrapper, 'global-with-stored-state')!.toSelector())).resolves.toBe(
+ true
+ );
await expect(page.isClickable(findDrawerById(wrapper, 'circle3-global')!.toSelector())).resolves.toBe(true);
await expect(page.hasHorizontalScroll()).resolves.toBe(false);
@@ -227,7 +265,9 @@ describe('Visual refresh toolbar only', () => {
// navigation panel closes first
await expect(page.isClickable(wrapper.findNavigation().toSelector())).resolves.toBe(false);
await expect(page.isClickable(findDrawerById(wrapper, 'circle')!.toSelector())).resolves.toBe(true);
- await expect(page.isClickable(findDrawerById(wrapper, 'circle2-global')!.toSelector())).resolves.toBe(true);
+ await expect(page.isClickable(findDrawerById(wrapper, 'global-with-stored-state')!.toSelector())).resolves.toBe(
+ true
+ );
await expect(page.isClickable(findDrawerById(wrapper, 'circle3-global')!.toSelector())).resolves.toBe(true);
await expect(page.hasHorizontalScroll()).resolves.toBe(false);
@@ -235,7 +275,9 @@ describe('Visual refresh toolbar only', () => {
// then the first opened drawer closes
await expect(page.isClickable(wrapper.findNavigation().toSelector())).resolves.toBe(false);
await expect(page.isClickable(findDrawerById(wrapper, 'circle')!.toSelector())).resolves.toBe(false);
- await expect(page.isClickable(findDrawerById(wrapper, 'circle2-global')!.toSelector())).resolves.toBe(true);
+ await expect(page.isClickable(findDrawerById(wrapper, 'global-with-stored-state')!.toSelector())).resolves.toBe(
+ true
+ );
await expect(page.isClickable(findDrawerById(wrapper, 'circle3-global')!.toSelector())).resolves.toBe(true);
await expect(page.hasHorizontalScroll()).resolves.toBe(false);
})
diff --git a/src/app-layout/utils/use-pointer-events.ts b/src/app-layout/utils/use-pointer-events.ts
index 8de8d1762d..f526125cb0 100644
--- a/src/app-layout/utils/use-pointer-events.ts
+++ b/src/app-layout/utils/use-pointer-events.ts
@@ -41,22 +41,31 @@ export const usePointerEvents = ({ position, panelRef, handleRef, onResize }: Si
);
const onDocumentPointerUp = useCallback(() => {
- if (!panelRef || !panelRef.current) {
+ const panelElement = panelRef?.current;
+ /* istanbul ignore if */
+ if (!panelElement) {
return;
}
+ const currentDocument = panelElement.ownerDocument;
- document.body.classList.remove(styles['resize-active']);
- document.body.classList.remove(styles[`resize-${position}`]);
- document.removeEventListener('pointerup', onDocumentPointerUp);
- document.removeEventListener('pointermove', onDocumentPointerMove);
+ currentDocument.body.classList.remove(styles['resize-active']);
+ currentDocument.body.classList.remove(styles[`resize-${position}`]);
+ currentDocument.removeEventListener('pointerup', onDocumentPointerUp);
+ currentDocument.removeEventListener('pointermove', onDocumentPointerMove);
}, [panelRef, onDocumentPointerMove, position]);
const onSliderPointerDown = useCallback(() => {
- document.body.classList.add(styles['resize-active']);
- document.body.classList.add(styles[`resize-${position}`]);
- document.addEventListener('pointerup', onDocumentPointerUp);
- document.addEventListener('pointermove', onDocumentPointerMove);
- }, [onDocumentPointerMove, onDocumentPointerUp, position]);
+ const panelElement = panelRef?.current;
+ /* istanbul ignore if */
+ if (!panelElement) {
+ return;
+ }
+ const currentDocument = panelElement.ownerDocument;
+ currentDocument.body.classList.add(styles['resize-active']);
+ currentDocument.body.classList.add(styles[`resize-${position}`]);
+ currentDocument.addEventListener('pointerup', onDocumentPointerUp);
+ currentDocument.addEventListener('pointermove', onDocumentPointerMove);
+ }, [panelRef, onDocumentPointerMove, onDocumentPointerUp, position]);
return onSliderPointerDown;
};