Skip to content
Open
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
1 change: 1 addition & 0 deletions build-tools/utils/custom-css-properties.js
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@ const customCssPropertiesList = [
'defaultMinContentWidth',
'drawerSize',
'drawerMinSize',
'bottomDrawerSize',
'footerHeight',
'headerGap',
'headerHeight',
Expand Down
2 changes: 1 addition & 1 deletion package.json
Original file line number Diff line number Diff line change
Expand Up @@ -176,7 +176,7 @@
{
"path": "lib/components/internal/widget-exports.js",
"brotli": false,
"limit": "964 kB",
"limit": "976 kB",
"ignore": "react-dom"
}
],
Expand Down
4 changes: 4 additions & 0 deletions pages/app-layout/runtime-drawers.page.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,7 @@ import './utils/external-widget';
import AppContext, { AppContextType } from '../app/app-context';
import { Breadcrumbs, Containers, CustomDrawerContent } from './utils/content-blocks';
import { drawerLabels } from './utils/drawers';
import { registerRuntimeBottomDrawer } from './utils/external-widget';
import appLayoutLabels from './utils/labels';
import { splitPaneli18nStrings } from './utils/strings';

Expand Down Expand Up @@ -126,6 +127,9 @@ export default function WithDrawers() {
>
Resize circle3-global drawer to 500px
</Button>
<Button onClick={() => registerRuntimeBottomDrawer()} data-testid="button-register-bottom-drawer">
Register a bottom drawer
</Button>
</SpaceBetween>
</SpaceBetween>
}
Expand Down
63 changes: 63 additions & 0 deletions pages/app-layout/utils/external-widget.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ import React, { useEffect, useImperativeHandle, useRef, useState } from 'react';
import Box from '~components/box';
import Drawer from '~components/drawer';
import awsuiPlugins from '~components/internal/plugins';
import { registerBottomDrawer } from '~components/internal/plugins/widget/index';
import { mount, unmount } from '~mount';

import { IframeWrapper } from '../../utils/iframe-wrapper';
Expand Down Expand Up @@ -294,3 +295,65 @@ awsuiPlugins.appLayout.registerDrawer({
},
unmountContent: container => unmount(container),
});

export const registerRuntimeBottomDrawer = () => {
registerBottomDrawer({
id: 'circle5-global-bottom',
position: 'bottom',
defaultActive: false,
resizable: true,
defaultSize: 350,
preserveInactiveContent: true,

isExpandable: true,

ariaLabels: {
closeButton: 'Close button',
content: 'Content bottom',
triggerButton: 'Trigger button',
resizeHandle: 'Resize handle',
expandedModeButton: 'Expanded mode button',
},
onToggle: event => {
console.log('circle-global drawer on toggle', event.detail);
},

trigger: {
iconSvg: `<svg viewBox="0 0 16 16" focusable="false">
<circle stroke-width="2" stroke="currentColor" fill="none" cx="8" cy="8" r="7" />
<circle stroke-width="2" stroke="currentColor" fill="none" cx="8" cy="8" r="3" />
</svg>`,
},

onResize: event => {
console.log('resize', event.detail);
},

mountContent: (container, mountContext) => {
mount(
<Drawer header={<Box variant="h2">Global drawer</Box>}>
<AutoIncrementCounter onVisibilityChange={mountContext?.onVisibilityChange}>
global bottom panel
{new Array(100).fill(null).map((_, index) => (
<div key={index}>{index}</div>
))}
<div data-testid="circle-global-bottom-content">circle-global bottom content</div>
</AutoIncrementCounter>
</Drawer>,
container
);
},
unmountContent: container => unmount(container),
headerActions: [
{
type: 'icon-button',
id: 'add',
iconName: 'add-plus',
text: 'Add',
},
],
onHeaderActionClick: ({ detail }) => {
console.log('onHeaderActionClick: ', detail);
},
});
};
94 changes: 82 additions & 12 deletions src/app-layout/__integ__/runtime-drawers.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@ import { Theme } from '../../__integ__/utils.js';
import { viewports } from './constants';
import { getUrlParams } from './utils';

import testutilStyles 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';

Expand All @@ -24,6 +25,9 @@ const findExpandedModeButtonByActiveDrawerId = (wrapper: AppLayoutWrapper, id: s
.findComponent(`[data-testid="awsui-app-layout-drawer-${id}"]`, ButtonGroupWrapper)!
.findButtonById('expand');
};
const findResizeHandlerByDrawerId = (wrapper: AppLayoutWrapper, id: string) => {
return wrapper.find(`[data-testid="awsui-app-layout-drawer-${id}"] .${testutilStyles['drawers-slider']}`);
};

describe.each(['classic', 'refresh', 'refresh-toolbar'] as Theme[])('%s', theme => {
function setupTest(
Expand Down Expand Up @@ -144,27 +148,35 @@ describe.each(['classic', 'refresh', 'refresh-toolbar'] as Theme[])('%s', theme
});
});

interface SetupTestOptions {
splitPanelPosition?: string;
}

describe('Visual refresh toolbar only', () => {
class PageObject extends BasePageObject {
hasHorizontalScroll() {
return this.browser.execute(() => document.body.scrollWidth - document.body.clientWidth > 0);
}

getVerticalScroll() {
return this.browser.execute(() => window.scrollY);
}

async getDrawerWidth(drawerId: string) {
const { width } = await this.getBoundingBox(findDrawerById(wrapper, drawerId)!.toSelector());

return width;
}
}
function setupTest(testFn: (page: PageObject) => Promise<void>) {
function setupTest({ splitPanelPosition = 'side' }: SetupTestOptions, testFn: (page: PageObject) => Promise<void>) {
return useBrowser(async browser => {
const page = new PageObject(browser);

await browser.url(
`#/light/app-layout/runtime-drawers?${getUrlParams('refresh-toolbar', {
hasDrawers: 'false',
hasTools: 'true',
splitPanelPosition: 'side',
splitPanelPosition,
})}`
);
await page.waitForVisible(wrapper.findDrawerTriggerById('security').toSelector(), true);
Expand All @@ -174,7 +186,7 @@ describe('Visual refresh toolbar only', () => {

test(
'displays only the most recently opened drawer in a full-width popup on mobile view (global drawer on top of the local one)',
setupTest(async page => {
setupTest({}, async page => {
await page.click(wrapper.findDrawerTriggerById('security').toSelector());
await page.click(wrapper.findDrawerTriggerById('circle-global').toSelector());

Expand All @@ -187,7 +199,7 @@ describe('Visual refresh toolbar only', () => {

test(
'displays only the most recently opened drawer in a full-width popup on mobile view (local drawer on top of the global one)',
setupTest(async page => {
setupTest({}, async page => {
await page.click(wrapper.findDrawerTriggerById('circle-global').toSelector());
await page.click(wrapper.findDrawerTriggerById('security').toSelector());

Expand All @@ -200,7 +212,7 @@ describe('Visual refresh toolbar only', () => {

test(
'should open 3 drawers (1 local and 2 global) if the screen size permits',
setupTest(async page => {
setupTest({}, async page => {
await page.setWindowSize({ ...viewports.desktop, width: 1700 });
await page.click(wrapper.findDrawerTriggerById('security').toSelector());
await page.click(wrapper.findDrawerTriggerById('circle-global').toSelector());
Expand All @@ -217,7 +229,7 @@ describe('Visual refresh toolbar only', () => {
describe('active drawers take up all available space on the page and a third drawer is opened', () => {
test(
'active drawers can be shrunk to accommodate a third drawer',
setupTest(async page => {
setupTest({}, async page => {
await page.setWindowSize(viewports.desktopWide);
await page.click(wrapper.findDrawerTriggerById('circle-global').toSelector());
await page.click(wrapper.findDrawerTriggerById('global-with-stored-state').toSelector());
Expand All @@ -237,7 +249,7 @@ describe('Visual refresh toolbar only', () => {

test(
'first opened drawer should be closed when active drawers can not be shrunk to accommodate it',
setupTest(async page => {
setupTest({}, async page => {
// Give the toolbar enough horizontal space to make sure the triggers are not collapsed into a dropdown
await page.setWindowSize({ ...viewports.desktop, width: 1400 });
await page.click(wrapper.findDrawerTriggerById('circle').toSelector());
Expand All @@ -257,7 +269,7 @@ describe('Visual refresh toolbar only', () => {

test(
'should prevent the horizontal page scroll from appearing during resize',
setupTest(async page => {
setupTest({}, async page => {
await page.setWindowSize(viewports.desktopWide);
await page.click(wrapper.findDrawerTriggerById('circle').toSelector());
await page.click(wrapper.findDrawerTriggerById('global-with-stored-state').toSelector());
Expand Down Expand Up @@ -296,7 +308,7 @@ describe('Visual refresh toolbar only', () => {
for (const viewport of ['mobile', 'desktop']) {
test(
`the content inside drawers should be scrollable on ${viewport} view`,
setupTest(async page => {
setupTest({}, async page => {
await page.click(wrapper.findDrawerTriggerById('circle-global').toSelector());
if (viewport === 'mobile') {
await page.setWindowSize(viewports.mobile);
Expand All @@ -314,7 +326,7 @@ describe('Visual refresh toolbar only', () => {

test(
'should show sticky elements on scroll in custom global drawer',
setupTest(async page => {
setupTest({}, async page => {
await page.setWindowSize(viewports.desktop);
await expect(page.isDisplayed('[data-testid="drawer-sticky-footer"]')).resolves.toBe(false);

Expand All @@ -334,7 +346,7 @@ describe('Visual refresh toolbar only', () => {

test(
'should show only expanded drawer and hide all other panels if expanded mode for a drawer is active',
setupTest(async page => {
setupTest({}, async page => {
await page.setWindowSize(viewports.desktopWide);
await page.click(wrapper.findDrawerTriggerById('circle-global').toSelector());
await page.click(wrapper.findDrawerTriggerById('circle3-global').toSelector());
Expand All @@ -356,7 +368,7 @@ describe('Visual refresh toolbar only', () => {

test(
'should programmatically resize drawers',
setupTest(async page => {
setupTest({}, async page => {
await page.setWindowSize(viewports.desktopWide);
const drawerId1 = 'circle-global';
const drawerId2 = 'circle3-global';
Expand All @@ -381,4 +393,62 @@ describe('Visual refresh toolbar only', () => {
});
})
);

test(
'bottom drawer should not overlap with toolbar when resized to its max height',
setupTest({ splitPanelPosition: 'bottom' }, async page => {
await page.setWindowSize(viewports.desktopWide);

await page.click(createWrapper().findButton('[data-testid="button-register-bottom-drawer"]').toSelector());

const drawerId = 'circle5-global-bottom';
await page.click(wrapper.findDrawerTriggerById(drawerId).toSelector());

await expect(page.isClickable(findDrawerById(wrapper, drawerId)!.toSelector())).resolves.toBe(true);
const { top: toolbarTop, bottom: toolbarBottom } = await page.getBoundingBox(
wrapper.findToolsToggle().toSelector()
);
const { top: drawerTopInitial } = await page.getBoundingBox(findDrawerById(wrapper, drawerId)!.toSelector());
// resize bottom drawer to its max height
await page.dragAndDrop(
findResizeHandlerByDrawerId(wrapper, drawerId).toSelector(),
0,
toolbarTop - drawerTopInitial
);
// make sure it does not overlap with toolbar after resizing
const { top: drawerTopAfterResizing } = await page.getBoundingBox(
findDrawerById(wrapper, drawerId)!.toSelector()
);
expect(drawerTopAfterResizing).toBeGreaterThan(toolbarBottom);

await page.click(wrapper.findSplitPanel().findOpenButton().toSelector());

// make sure it does not overlap with toolbar after opening a bottom split panel
const { top: drawerTopAfterOpeningSplitPanel } = await page.getBoundingBox(
findDrawerById(wrapper, drawerId)!.toSelector()
);
const { top: splitPanelTop } = await page.getBoundingBox(wrapper.findSplitPanel().toSelector());
expect(drawerTopAfterResizing).toBeGreaterThan(toolbarBottom);

expect(drawerTopAfterOpeningSplitPanel).toBeGreaterThan(toolbarBottom);
expect(splitPanelTop).toBeGreaterThan(toolbarBottom);
})
);

test(
'bottom drawer opening does not cause the page to scroll',
setupTest({}, async page => {
await page.setWindowSize(viewports.desktopWide);

await page.click(createWrapper().findButton('[data-testid="button-register-bottom-drawer"]').toSelector());

await expect(page.getVerticalScroll()).resolves.toBe(0);

const drawerId = 'circle5-global-bottom';
await page.click(wrapper.findDrawerTriggerById(drawerId).toSelector());

await expect(page.isClickable(findDrawerById(wrapper, drawerId)!.toSelector())).resolves.toBe(true);
await expect(page.getVerticalScroll()).resolves.toBe(0);
})
);
});
Original file line number Diff line number Diff line change
Expand Up @@ -347,6 +347,7 @@ Map {
"toolbar": 0,
},
},
"bottomDrawerReportedSize": 0,
},
"AppLayoutSplitPanelDrawerBottomImplementation" => {
"appLayoutInternals": {
Expand Down Expand Up @@ -684,6 +685,7 @@ Map {
"toolbar": 0,
},
},
"bottomDrawerReportedSize": 0,
},
"AppLayoutGlobalDrawersImplementation" => {
"appLayoutInternals": {
Expand Down Expand Up @@ -1203,6 +1205,7 @@ Map {
"toolbar": 0,
},
},
"bottomDrawerReportedSize": 0,
},
"AppLayoutNotificationsImplementation" => {
"appLayoutInternals": {
Expand Down Expand Up @@ -1716,6 +1719,7 @@ Map {
"toolbar": 0,
},
},
"bottomDrawerReportedSize": 0,
},
"AppLayoutGlobalDrawersImplementation" => {
"appLayoutInternals": {
Expand Down Expand Up @@ -2260,6 +2264,7 @@ Map {
"toolbar": 0,
},
},
"bottomDrawerReportedSize": 0,
},
"AppLayoutSplitPanelDrawerBottomImplementation" => {
"appLayoutInternals": {
Expand Down Expand Up @@ -2629,6 +2634,7 @@ Map {
"toolbar": 0,
},
},
"bottomDrawerReportedSize": 0,
},
"AppLayoutGlobalDrawersImplementation" => {
"appLayoutInternals": {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -347,6 +347,7 @@ Map {
"toolbar": 0,
},
},
"bottomDrawerReportedSize": 0,
},
"AppLayoutSplitPanelDrawerBottomImplementation" => {
"appLayoutInternals": {
Expand Down Expand Up @@ -691,6 +692,7 @@ Map {
"toolbar": 0,
},
},
"bottomDrawerReportedSize": 0,
},
"AppLayoutGlobalDrawersImplementation" => {
"appLayoutInternals": {
Expand Down Expand Up @@ -1195,6 +1197,7 @@ Map {
"toolbar": 0,
},
},
"bottomDrawerReportedSize": 0,
},
"AppLayoutSplitPanelDrawerBottomImplementation" => {
"appLayoutInternals": {
Expand Down Expand Up @@ -1538,6 +1541,7 @@ Map {
"toolbar": 0,
},
},
"bottomDrawerReportedSize": 0,
},
"AppLayoutGlobalDrawersImplementation" => {
"appLayoutInternals": {
Expand Down
Loading
Loading