Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

chore: Add regression tests for runtime preferences handling in multi-app-layout setup #3045

Merged
merged 1 commit into from
Nov 25, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -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';
Expand All @@ -18,7 +19,6 @@ function createView(name: string) {
return function View() {
return (
<AppLayout
{...{ __disableRuntimeDrawers: true }}
data-testid="secondary-layout"
ariaLabels={labels}
breadcrumbs={<Breadcrumbs />}
Expand Down Expand Up @@ -55,6 +55,7 @@ export default function () {
return (
<ScreenshotArea gutters={false}>
<AppLayout
{...{ __disableRuntimeDrawers: true }}
Copy link
Member Author

Choose a reason for hiding this comment

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

Moved this here to more accurately represent real-world use-case

data-testid="main-layout"
ariaLabels={labels}
navigation={
Expand Down
12 changes: 9 additions & 3 deletions pages/app-layout/utils/external-widget.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -48,6 +48,7 @@ awsuiPlugins.appLayout.registerDrawer({
defaultActive: !!searchParams.get('force-default-active'),
onToggle: event => {
console.log('security drawer on toggle', event.detail);
awsuiPlugins.appLayout.updateDrawer({ id: 'security', defaultActive: event.detail.isOpen });
},

resizable: true,
Expand All @@ -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 => {
Expand Down Expand Up @@ -165,15 +167,15 @@ awsuiPlugins.appLayout.registerDrawer({
});

awsuiPlugins.appLayout.registerDrawer({
id: 'circle2-global',
id: 'global-with-stored-state',
type: 'global',
defaultActive: false,
resizable: true,
defaultSize: 320,

ariaLabels: {
closeButton: 'Close button',
content: 'Content',
content: 'Drawer with counter',
Copy link
Member Author

Choose a reason for hiding this comment

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

Improvement for manual testing. All drawers have the same circle icons, let's have different tooltips at least

triggerButton: 'Trigger button',
resizeHandle: 'Resize handle',
},
Expand All @@ -185,10 +187,14 @@ awsuiPlugins.appLayout.registerDrawer({
</svg>`,
},

onToggle(event) {
awsuiPlugins.appLayout.updateDrawer({ id: 'global-with-stored-state', defaultActive: event.detail.isOpen });
},

mountContent: container => {
ReactDOM.render(
<>
<Counter id="circle2-global" />
<Counter id="global-with-stored-state" />
global widget content circle 2
</>,
container
Expand Down
2 changes: 1 addition & 1 deletion pages/app-layout/with-drawers-scrollable.page.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -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,
Expand Down
86 changes: 64 additions & 22 deletions src/app-layout/__integ__/runtime-drawers.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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<void>) {
return useBrowser(async browser => {
function setupTest(
{ hasDrawers = 'false', url = 'runtime-drawers', size = viewports.desktop },
testFn: (page: BasePageObject) => Promise<void>
) {
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());

Expand All @@ -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);
Copy link
Member Author

Choose a reason for hiding this comment

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

Removed duplicated lines, this is in the shared setup

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');
Expand All @@ -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());
Expand All @@ -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',
Copy link
Member Author

@just-boris just-boris Nov 21, 2024

Choose a reason for hiding this comment

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

All changes were for the sake of writing this test

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);
Expand Down Expand Up @@ -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
);
})
);

Expand All @@ -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);
Expand All @@ -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
);
})
);

Expand All @@ -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
);
})
);

Expand All @@ -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
);
})
);
});
Expand All @@ -214,28 +250,34 @@ 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);

await page.setWindowSize({ ...viewports.desktop, width: 1185 });
// 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);

await page.setWindowSize({ ...viewports.desktop, width: 900 });
// 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);
})
Expand Down
29 changes: 19 additions & 10 deletions src/app-layout/utils/use-pointer-events.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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;
};
Loading