diff --git a/src/app-layout/__integ__/app-layout-split-panel.test.ts b/src/app-layout/__integ__/app-layout-split-panel.test.ts index de4f3fc3f3..f936d2846f 100644 --- a/src/app-layout/__integ__/app-layout-split-panel.test.ts +++ b/src/app-layout/__integ__/app-layout-split-panel.test.ts @@ -5,9 +5,12 @@ import useBrowser from '@cloudscape-design/browser-test-tools/use-browser'; import createWrapper from '../../../lib/components/test-utils/selectors'; import { scrollbarThickness } from '../../__integ__/scrollbars'; import { viewports } from './constants'; -import { AppLayoutSplitViewPage } from './utils'; +import { AppLayoutSplitViewPage, getUrlParams } from './utils'; import mobileStyles from '../../../lib/components/app-layout/mobile-toolbar/styles.selectors.js'; +import tableScrollbarStyles from '../../../lib/components/table/sticky-scrollbar/styles.selectors.js'; + +const scrollbarSelector = `.${tableScrollbarStyles['sticky-scrollbar-visible']}`; const wrapper = createWrapper().findAppLayout(); @@ -19,11 +22,7 @@ describe.each(['classic', 'refresh', 'refresh-toolbar'] as const)('%s', theme => return useBrowser(async browser => { const page = new AppLayoutSplitViewPage(browser); await page.setWindowSize(viewports.desktop); - const params = new URLSearchParams({ - visualRefresh: `${theme.startsWith('refresh')}`, - appLayoutToolbar: `${theme === 'refresh-toolbar'}`, - }); - await browser.url(`${url}?${params.toString()}`); + await browser.url(`${url}?${getUrlParams(theme)}`); await page.waitForVisible(wrapper.findContentRegion().toSelector()); await testFn(page); }); @@ -274,7 +273,7 @@ describe.each(['classic', 'refresh', 'refresh-toolbar'] as const)('%s', theme => }) ); - describe('interaction with table sticky header', () => { + describe('interaction with table sticky elements', () => { // bottom padding is included into the offset in VR but not in classic const splitPanelPadding = theme === 'refresh' ? 40 : 0; @@ -298,5 +297,19 @@ describe.each(['classic', 'refresh', 'refresh-toolbar'] as const)('%s', theme => await expect(page.getContentOffsetBottom(theme)).resolves.toEqual(windowHeight / 2 + splitPanelPadding + 'px'); }, '#/light/app-layout/with-full-page-table-and-split-panel') ); + + test( + 'bottom split panel does not block table sticky scrollbar', + setupTest(async page => { + // open tools panel to narrow the content area and display horizontal scroll on table + await page.click(wrapper.findToolsToggle().toSelector()); + await expect(page.isClickable(scrollbarSelector)).resolves.toBe(true); + await page.click(wrapper.findSplitPanelOpenButton().toSelector()); + await expect( + page.isDisplayedInViewport(wrapper.findSplitPanel().findOpenPanelBottom().toSelector()) + ).resolves.toBe(true); + await expect(page.isClickable(scrollbarSelector)).resolves.toBe(true); + }, '#/light/app-layout/with-sticky-table-and-split-panel') + ); }); }); diff --git a/src/app-layout/visual-refresh-toolbar/compute-layout.ts b/src/app-layout/visual-refresh-toolbar/compute-layout.ts index 20bd6eb00a..084efdbbbb 100644 --- a/src/app-layout/visual-refresh-toolbar/compute-layout.ts +++ b/src/app-layout/visual-refresh-toolbar/compute-layout.ts @@ -113,6 +113,36 @@ export function computeVerticalLayout({ return { toolbar, notifications, header, drawers }; } +interface SplitPanelOffsetInput { + hasSplitPanel: boolean; + placement: AppLayoutPropsWithDefaults['placement']; + splitPanelPosition: 'bottom' | 'side'; + splitPanelOpen: boolean; + splitPanelHeaderHeight: number; + splitPanelFullHeight: number; +} + +export function computeSplitPanelOffsets({ + hasSplitPanel, + splitPanelPosition, + placement, + splitPanelOpen, + splitPanelFullHeight, + splitPanelHeaderHeight, +}: SplitPanelOffsetInput) { + if (!hasSplitPanel || splitPanelPosition !== 'bottom') { + return { + stickyVerticalBottomOffset: placement.insetBlockEnd, + mainContentPaddingBlockEnd: undefined, + }; + } + const mainContentBottomOffset = splitPanelOpen ? splitPanelFullHeight : splitPanelHeaderHeight; + return { + stickyVerticalBottomOffset: mainContentBottomOffset + placement.insetBlockEnd, + mainContentPaddingBlockEnd: mainContentBottomOffset, + }; +} + export function getDrawerStyles( verticalOffsets: VerticalLayoutOutput, isMobile: boolean, diff --git a/src/app-layout/visual-refresh-toolbar/index.tsx b/src/app-layout/visual-refresh-toolbar/index.tsx index 460f783d94..906d590d18 100644 --- a/src/app-layout/visual-refresh-toolbar/index.tsx +++ b/src/app-layout/visual-refresh-toolbar/index.tsx @@ -21,7 +21,12 @@ import { MIN_DRAWER_SIZE, OnChangeParams, useDrawers } from '../utils/use-drawer import { useFocusControl, useMultipleFocusControl } from '../utils/use-focus-control'; import { useSplitPanelFocusControl } from '../utils/use-split-panel-focus-control'; import { ActiveDrawersContext } from '../utils/visibility-context'; -import { computeHorizontalLayout, computeVerticalLayout, CONTENT_PADDING } from './compute-layout'; +import { + computeHorizontalLayout, + computeSplitPanelOffsets, + computeVerticalLayout, + CONTENT_PADDING, +} from './compute-layout'; import { AppLayoutVisibilityContext } from './contexts'; import { AppLayoutInternals } from './interfaces'; import { @@ -461,6 +466,15 @@ const AppLayoutVisualRefreshToolbar = React.forwardRef {/* Rendering a hidden copy of breadcrumbs to trigger their deduplication */} @@ -469,16 +483,11 @@ const AppLayoutVisualRefreshToolbar = React.forwardRef