Skip to content

Commit 3761ab3

Browse files
authored
Merge branch 'main' into fix-app-layout-nav-label
2 parents 03b32bd + da7a8fa commit 3761ab3

File tree

12 files changed

+74
-153
lines changed

12 files changed

+74
-153
lines changed

pages/app-layout/utils/content-blocks.tsx

-1
Original file line numberDiff line numberDiff line change
@@ -37,7 +37,6 @@ export function Containers() {
3737
{range(count).map(i => (
3838
<Container
3939
key={i}
40-
data-testid={`container-${i + 1}`}
4140
header={
4241
<Header variant="h2" actions={<Button onClick={() => setCount(count - 1)}>Remove</Button>}>
4342
Demo container #{i + 1}

pages/app-layout/with-split-panel.page.tsx

+47-29
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,13 @@
11
// Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved.
22
// SPDX-License-Identifier: Apache-2.0
3+
import React, { useContext, useState } from 'react';
34

4-
import React, { useContext } from 'react';
5-
6-
import { AppLayout, AppLayoutProps, Box, Header, Popover, SpaceBetween, SplitPanel, Toggle } from '~components';
5+
import AppLayout, { AppLayoutProps } from '~components/app-layout';
6+
import Header from '~components/header';
7+
import Popover from '~components/popover';
8+
import SpaceBetween from '~components/space-between';
9+
import SplitPanel from '~components/split-panel';
10+
import Toggle from '~components/toggle';
711

812
import AppContext, { AppContextType } from '../app/app-context';
913
import ScreenshotArea from '../utils/screenshot-area';
@@ -57,23 +61,37 @@ const DEMO_CONTENT = (
5761
pellentesque id. Porta lorem mollis aliquam ut porttitor leo a. Lectus quam id leo in vitae turpis massa sed.
5862
Pharetra pharetra massa massa ultricies mi.
5963
</p>
64+
<p>
65+
Pharetra et ultrices neque ornare. Bibendum neque egestas congue quisque egestas diam in arcu cursus. Porttitor
66+
eget dolor morbi non arcu risus quis. Integer quis auctor elit sed vulputate mi sit. Mauris nunc congue nisi vitae
67+
suscipit tellus mauris a diam. Diam donec adipiscing tristique risus nec feugiat in. Arcu felis bibendum ut
68+
tristique et egestas quis. Nulla porttitor massa id neque aliquam vestibulum morbi blandit. In hac habitasse
69+
platea dictumst quisque sagittis. Sollicitudin tempor id eu nisl nunc mi ipsum. Ornare aenean euismod elementum
70+
nisi quis. Elementum curabitur vitae nunc sed velit dignissim sodales. Amet tellus cras adipiscing enim eu. Id
71+
interdum velit laoreet id donec ultrices tincidunt. Ullamcorper eget nulla facilisi etiam. Sodales neque sodales
72+
ut etiam sit amet nisl purus. Auctor urna nunc id cursus metus aliquam eleifend mi in. Urna condimentum mattis
73+
pellentesque id. Porta lorem mollis aliquam ut porttitor leo a. Lectus quam id leo in vitae turpis massa sed.
74+
Pharetra pharetra massa massa ultricies mi.
75+
</p>
6076
</div>
6177
);
6278

6379
export default function () {
64-
const {
65-
urlParams: { splitPanelEnabled = true, toolsEnabled = true, splitPanelPosition },
66-
setUrlParams,
67-
} = useContext(AppContext as SplitPanelDemoContext);
80+
const { urlParams, setUrlParams } = useContext(AppContext as SplitPanelDemoContext);
81+
const [splitPanelEnabled, setSplitPanelEnabled] = useState(urlParams.splitPanelEnabled ?? true);
82+
const [toolsPanelEnabled, setToolsPanelEnabled] = useState(urlParams.toolsEnabled ?? true);
83+
6884
return (
6985
<ScreenshotArea gutters={false}>
7086
<AppLayout
7187
ariaLabels={labels}
7288
breadcrumbs={<Breadcrumbs />}
7389
navigation={<Navigation />}
7490
tools={<Tools>{toolsContent.long}</Tools>}
75-
toolsHide={!toolsEnabled}
76-
splitPanelPreferences={{ position: splitPanelPosition }}
91+
toolsHide={!toolsPanelEnabled}
92+
splitPanelPreferences={{
93+
position: urlParams.splitPanelPosition,
94+
}}
7795
onSplitPanelPreferencesChange={event => {
7896
const { position } = event.detail;
7997
setUrlParams({ splitPanelPosition: position === 'side' ? position : undefined });
@@ -106,28 +124,28 @@ export default function () {
106124
Demo page
107125
</Header>
108126
</div>
109-
110127
<SpaceBetween size="l">
111-
<SpaceBetween size="s" direction="horizontal">
112-
<Toggle
113-
id="enable-split-panel"
114-
checked={splitPanelEnabled}
115-
onChange={e => setUrlParams({ splitPanelEnabled: e.detail.checked })}
116-
>
117-
Enable split panel
118-
</Toggle>
119-
<Toggle
120-
id="enable-tools-panel"
121-
checked={toolsEnabled}
122-
onChange={e => setUrlParams({ toolsEnabled: e.detail.checked })}
123-
>
124-
Enable tools panel
125-
</Toggle>
126-
</SpaceBetween>
127-
128+
<Toggle
129+
id="enable-split-panel"
130+
checked={splitPanelEnabled}
131+
onChange={e => {
132+
setSplitPanelEnabled(e.detail.checked);
133+
setUrlParams({ splitPanelEnabled: e.detail.checked });
134+
}}
135+
>
136+
Enable split panel
137+
</Toggle>
138+
<Toggle
139+
id="enable-tools-panel"
140+
checked={toolsPanelEnabled}
141+
onChange={e => {
142+
setToolsPanelEnabled(e.detail.checked);
143+
setUrlParams({ toolsEnabled: e.detail.checked });
144+
}}
145+
>
146+
Enable tools panel
147+
</Toggle>
128148
<Containers />
129-
130-
<Box>{DEMO_CONTENT}</Box>
131149
</SpaceBetween>
132150
</>
133151
}

src/app-layout/__integ__/app-layout-drawers.test.ts

+1-1
Original file line numberDiff line numberDiff line change
@@ -209,7 +209,7 @@ describe.each(['classic', 'refresh', 'refresh-toolbar'] as const)('%s', theme =>
209209
)
210210
);
211211

212-
test(
212+
test.skip(
213213
'updates side split panel position when using different width drawers',
214214
setupTest(
215215
{ theme, splitPanelPosition: 'side', screenSize: { ...viewports.desktop, width: 1450 } },

src/app-layout/__integ__/app-layout-split-panel.test.ts

-58
Original file line numberDiff line numberDiff line change
@@ -82,18 +82,6 @@ class AppLayoutSplitViewPage extends BasePageObject {
8282
}, contentSelector);
8383
}
8484
}
85-
86-
hasPageScrollbar() {
87-
return this.browser.execute(
88-
() => window.document.documentElement.scrollHeight > window.document.documentElement.clientHeight
89-
);
90-
}
91-
92-
verifySplitPanelPosition(targetPosition: 'side' | 'bottom') {
93-
return targetPosition === 'side'
94-
? this.isExisting(wrapper.findSplitPanel().findOpenPanelSide().toSelector())
95-
: this.isExisting(wrapper.findSplitPanel().findOpenPanelBottom().toSelector());
96-
}
9785
}
9886

9987
describe.each(['classic', 'refresh', 'refresh-toolbar'] as const)('%s', theme => {
@@ -363,50 +351,4 @@ describe.each(['classic', 'refresh', 'refresh-toolbar'] as const)('%s', theme =>
363351
}, '#/light/app-layout/with-full-page-table-and-split-panel')
364352
);
365353
});
366-
367-
test(
368-
'forces split panel to the bottom or allows on side when page scrollbar appears',
369-
useBrowser(async browser => {
370-
const page = new AppLayoutSplitViewPage(browser);
371-
const dimensions = {
372-
classic: { height: 1400, width: 945 },
373-
refresh: { height: 1200, width: 1020 },
374-
'refresh-toolbar': { height: 1400, width: 875 },
375-
}[theme];
376-
const content = wrapper.findContentRegion();
377-
378-
// Split panel is forced to the bottom when not enough horizontal space.
379-
await page.setWindowSize(dimensions);
380-
const params = new URLSearchParams({
381-
splitPanelPosition: 'side',
382-
visualRefresh: `${theme.startsWith('refresh')}`,
383-
appLayoutToolbar: `${theme === 'refresh-toolbar'}`,
384-
});
385-
await browser.url(`#/light/app-layout/with-split-panel?${params.toString()}`);
386-
await page.waitForVisible(content.toSelector());
387-
await page.click(content.findContainer('[data-testid="container-1"]').findHeader().findButton().toSelector());
388-
await page.click(content.findContainer('[data-testid="container-1"]').findHeader().findButton().toSelector());
389-
390-
// Open split panel and ensure it is forced to the bottom.
391-
await page.click(wrapper.findSplitPanel().findOpenButton().toSelector());
392-
await expect(page.verifySplitPanelPosition('bottom')).resolves.toBe(true);
393-
await expect(page.hasPageScrollbar()).resolves.toBe(true);
394-
395-
// Make split panel smaller to ensure no page-level scrollbar.
396-
const { height: screenHeight } = await page.getViewportSize();
397-
await page.dragResizerTo({ x: 0, y: screenHeight });
398-
await expect(page.verifySplitPanelPosition('bottom')).resolves.toBe(true);
399-
await expect(page.hasPageScrollbar()).resolves.toBe(false);
400-
401-
// Split panel transitions to the side when enough horizontal space.
402-
await page.setWindowSize({ ...dimensions, width: dimensions.width + 25 });
403-
await expect(page.verifySplitPanelPosition('side')).resolves.toBe(true);
404-
await expect(page.hasPageScrollbar()).resolves.toBe(true);
405-
406-
// Split panel transitions to the bottom when not enough horizontal space.
407-
await page.setWindowSize(dimensions);
408-
await expect(page.verifySplitPanelPosition('bottom')).resolves.toBe(true);
409-
await expect(page.hasPageScrollbar()).resolves.toBe(false);
410-
})
411-
);
412354
});

src/app-layout/classic.tsx

+10-4
Original file line numberDiff line numberDiff line change
@@ -23,8 +23,12 @@ import { ResizableDrawer } from './drawer/resizable-drawer';
2323
import { AppLayoutProps, AppLayoutPropsWithDefaults } from './interfaces';
2424
import { MobileToolbar } from './mobile-toolbar';
2525
import { Notifications } from './notifications';
26-
import { SideSplitPanelDrawer, SplitPanelProvider, SplitPanelProviderProps } from './split-panel';
27-
import { checkSplitPanelForcedPosition } from './split-panel/split-panel-utils';
26+
import {
27+
SideSplitPanelDrawer,
28+
SPLIT_PANEL_MIN_WIDTH,
29+
SplitPanelProvider,
30+
SplitPanelProviderProps,
31+
} from './split-panel';
2832
import { togglesConfig } from './toggles';
2933
import { getStickyOffsetVars } from './utils/sticky-offsets';
3034
import { TOOLS_DRAWER_ID, useDrawers } from './utils/use-drawers';
@@ -294,8 +298,10 @@ const ClassicAppLayout = React.forwardRef(
294298
};
295299

296300
const effectiveToolsWidth = getEffectiveToolsWidth();
297-
const splitPanelMaxWidth = resizableSpaceAvailable - effectiveToolsWidth;
298-
const isSplitPanelForcedPosition = checkSplitPanelForcedPosition({ isMobile, splitPanelMaxWidth });
301+
302+
// if there is no space to display split panel in the side, force to bottom
303+
const isSplitPanelForcedPosition =
304+
isMobile || resizableSpaceAvailable - effectiveToolsWidth < SPLIT_PANEL_MIN_WIDTH;
299305
const finalSplitPanePosition = isSplitPanelForcedPosition ? 'bottom' : splitPanelPosition;
300306

301307
const splitPaneAvailableOnTheSide = splitPanelDisplayed && finalSplitPanePosition === 'side';

src/app-layout/split-panel/constants.ts

-1
Original file line numberDiff line numberDiff line change
@@ -2,4 +2,3 @@
22
// SPDX-License-Identifier: Apache-2.0
33
export const SPLIT_PANEL_MIN_HEIGHT = 160;
44
export const SPLIT_PANEL_MIN_WIDTH = 280;
5-
export const SPLIT_PANEL_SCROLLBAR_MARGIN = 20;

src/app-layout/split-panel/split-panel-utils.ts

-36
This file was deleted.

src/app-layout/visual-refresh-toolbar/compute-layout.ts

+2-3
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@
22
// SPDX-License-Identifier: Apache-2.0
33

44
import { AppLayoutPropsWithDefaults } from '../interfaces';
5-
import { checkSplitPanelForcedPosition } from '../split-panel/split-panel-utils';
5+
import { SPLIT_PANEL_MIN_WIDTH } from '../split-panel';
66

77
interface HorizontalLayoutInput {
88
navigationOpen: boolean;
@@ -39,8 +39,7 @@ export function computeHorizontalLayout({
3939
);
4040
const totalActiveGlobalDrawersSize = Object.values(activeGlobalDrawersSizes).reduce((acc, size) => acc + size, 0);
4141

42-
const splitPanelMaxWidth = resizableSpaceAvailable - activeDrawerSize;
43-
const splitPanelForcedPosition = checkSplitPanelForcedPosition({ isMobile, splitPanelMaxWidth });
42+
const splitPanelForcedPosition = resizableSpaceAvailable - activeDrawerSize < SPLIT_PANEL_MIN_WIDTH || isMobile;
4443
const resolvedSplitPanelPosition = splitPanelForcedPosition ? 'bottom' : splitPanelPosition ?? 'bottom';
4544
const sideSplitPanelSize = resolvedSplitPanelPosition === 'side' && splitPanelOpen ? splitPanelSize ?? 0 : 0;
4645
const maxSplitPanelSize = Math.max(resizableSpaceAvailable - totalActiveGlobalDrawersSize - activeDrawerSize, 0);

src/app-layout/visual-refresh/context.tsx

+8-2
Original file line numberDiff line numberDiff line change
@@ -23,7 +23,6 @@ import { useUniqueId } from '../../internal/hooks/use-unique-id';
2323
import { getSplitPanelDefaultSize } from '../../split-panel/utils/size-utils';
2424
import { AppLayoutProps, AppLayoutPropsWithDefaults } from '../interfaces';
2525
import { SPLIT_PANEL_MIN_WIDTH } from '../split-panel';
26-
import { checkSplitPanelForcedPosition } from '../split-panel/split-panel-utils';
2726
import { useDrawers } from '../utils/use-drawers';
2827
import { FocusControlRefs, useFocusControl } from '../utils/use-focus-control';
2928
import useResize from '../utils/use-resize';
@@ -235,7 +234,14 @@ export const AppLayoutInternalsProvider = React.forwardRef(
235234
[props.onSplitPanelToggle, isSplitPanelOpen, setIsSplitPanelOpen, setSplitPanelLastInteraction]
236235
);
237236

238-
const isSplitPanelForcedPosition = checkSplitPanelForcedPosition({ isMobile, splitPanelMaxWidth });
237+
/**
238+
* The Split Panel will be in forced (bottom) position if the defined minimum width is
239+
* greater than the maximum width. In other words, the maximum width is the currently
240+
* available horizontal space based on all other components that are rendered. If the
241+
* minimum width exceeds this value then there is not enough horizontal space and we must
242+
* force it to the bottom position.
243+
*/
244+
const isSplitPanelForcedPosition = isMobile || SPLIT_PANEL_MIN_WIDTH > splitPanelMaxWidth;
239245
const splitPanelPosition = getSplitPanelPosition(isSplitPanelForcedPosition, splitPanelPreferences);
240246

241247
/**

src/attribute-editor/__tests__/attribute-editor.test.tsx

+3-10
Original file line numberDiff line numberDiff line change
@@ -167,26 +167,19 @@ describe('Attribute Editor', () => {
167167
test('enables the add button by default', () => {
168168
const wrapper = renderAttributeEditor({ ...defaultProps });
169169
const buttonElement = wrapper.findAddButton().getElement();
170-
expect(buttonElement).not.toHaveAttribute('aria-disabled');
170+
expect(buttonElement).not.toHaveAttribute('disabled');
171171
});
172172

173173
test('enables the add button when disableAddButton is false', () => {
174174
const wrapper = renderAttributeEditor({ ...defaultProps, disableAddButton: false });
175175
const buttonElement = wrapper.findAddButton().getElement();
176-
expect(buttonElement).not.toHaveAttribute('aria-disabled');
176+
expect(buttonElement).not.toHaveAttribute('disabled');
177177
});
178178

179179
test('disables the add button when disableAddButton is true', () => {
180180
const wrapper = renderAttributeEditor({ ...defaultProps, disableAddButton: true });
181181
const buttonElement = wrapper.findAddButton().getElement();
182-
expect(buttonElement).toHaveAttribute('aria-disabled');
183-
});
184-
185-
test('allows the add button to be focused manually when disableAddButton is true', () => {
186-
const ref: React.Ref<AttributeEditorProps.Ref> = React.createRef();
187-
const wrapper = renderAttributeEditor({ ...defaultProps, ref, disableAddButton: true });
188-
ref.current!.focusAddButton();
189-
expect(wrapper.findAddButton().getElement()).toHaveFocus();
182+
expect(buttonElement).toHaveAttribute('disabled');
190183
});
191184

192185
test('has no aria-describedby if there is no additional info', () => {

src/attribute-editor/internal.tsx

-5
Original file line numberDiff line numberDiff line change
@@ -103,11 +103,6 @@ const InternalAttributeEditor = React.forwardRef(
103103
<InternalButton
104104
className={styles['add-button']}
105105
disabled={disableAddButton}
106-
// Using aria-disabled="true" and tabindex="-1" instead of "disabled"
107-
// because focus can be dynamically moved to this button by calling
108-
// `focusAddButton()` on the ref.
109-
__nativeAttributes={disableAddButton ? { tabIndex: -1 } : {}}
110-
__focusable={true}
111106
onClick={onAddButtonClick}
112107
formAction="none"
113108
ref={addButtonRef}

src/tag-editor/__tests__/tag-editor.test.tsx

+3-3
Original file line numberDiff line numberDiff line change
@@ -283,7 +283,7 @@ describe('Tag Editor component', () => {
283283
test('is not disabled by default ', () => {
284284
const { wrapper } = renderTagEditor();
285285

286-
expect(wrapper.findAddButton().getElement()).not.toHaveAttribute('aria-disabled');
286+
expect(wrapper.findAddButton().getElement()).not.toHaveAttribute('disabled');
287287
});
288288

289289
test('is disabled when tag limit has been reached', () => {
@@ -292,7 +292,7 @@ describe('Tag Editor component', () => {
292292
tagLimit: 1,
293293
});
294294

295-
expect(wrapper.findAddButton().getElement()).toHaveAttribute('aria-disabled');
295+
expect(wrapper.findAddButton().getElement()).toHaveAttribute('disabled');
296296
});
297297

298298
test('is disabled when tag limit has been exceeded', () => {
@@ -304,7 +304,7 @@ describe('Tag Editor component', () => {
304304
tagLimit: 1,
305305
});
306306

307-
expect(wrapper.findAddButton().getElement()).toHaveAttribute('aria-disabled');
307+
expect(wrapper.findAddButton().getElement()).toHaveAttribute('disabled');
308308
});
309309

310310
test('adds an empty tag when there are no tags', () => {

0 commit comments

Comments
 (0)