Skip to content

Commit

Permalink
fix: Container overflow constrained to the content slot (#2864)
Browse files Browse the repository at this point in the history
  • Loading branch information
pan-kot authored Oct 18, 2024
1 parent b8f408e commit 11df6c3
Show file tree
Hide file tree
Showing 3 changed files with 94 additions and 47 deletions.
65 changes: 45 additions & 20 deletions pages/table/sticky-scrollbar-in-container.page.tsx
Original file line number Diff line number Diff line change
@@ -1,37 +1,62 @@
// Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved.
// SPDX-License-Identifier: Apache-2.0
import React from 'react';
import React, { useContext } from 'react';

import { useCollection } from '@cloudscape-design/collection-hooks';

import Header from '~components/header';
import Table from '~components/table';
import { Checkbox, Container, Header, Pagination, SpaceBetween, Table } from '~components';

import AppContext, { AppContextType } from '../app/app-context';
import ScreenshotArea from '../utils/screenshot-area';
import { generateItems, Instance } from './generate-data';
import { columnsConfig } from './shared-configs';
import { generateItems } from './generate-data';
import { columnsConfig, paginationLabels } from './shared-configs';

type PageContext = React.Context<
AppContextType<{
fitHeight?: boolean;
hasFooter?: boolean;
}>
>;

const allItems = generateItems();
const PAGE_SIZE = 50;

export default function App() {
const { items } = useCollection(allItems, {
pagination: { pageSize: PAGE_SIZE },
sorting: {},
});
const {
urlParams: { fitHeight = true, hasFooter = false },
setUrlParams,
} = useContext(AppContext as PageContext);

const { items, paginationProps } = useCollection(allItems, { pagination: { pageSize: PAGE_SIZE }, sorting: {} });

return (
<ScreenshotArea>
<div style={{ blockSize: '400px', overflow: 'scroll' }}>
<Table<Instance>
header={
<Header headingTagOverride="h1" counter={`(${allItems.length})`}>
Sticky Scrollbar Example
</Header>
}
columnDefinitions={columnsConfig}
items={items}
/>
</div>
<SpaceBetween size="m" direction="horizontal">
<div style={{ blockSize: '500px', inlineSize: '500px', overflow: 'scroll' }}>
<Container fitHeight={fitHeight}>
<Table
variant="borderless"
header={
<Header headingTagOverride="h1" counter={`(${allItems.length})`}>
Sticky Scrollbar Example
</Header>
}
footer={hasFooter ? <Pagination {...paginationProps} ariaLabels={paginationLabels} /> : undefined}
columnDefinitions={columnsConfig}
items={items}
/>
</Container>
</div>

<SpaceBetween size="s">
<Checkbox checked={fitHeight} onChange={({ detail }) => setUrlParams({ fitHeight: detail.checked })}>
fitHeight
</Checkbox>
<Checkbox checked={hasFooter} onChange={({ detail }) => setUrlParams({ hasFooter: detail.checked })}>
hasFooter
</Checkbox>
</SpaceBetween>
</SpaceBetween>
</ScreenshotArea>
);
}
22 changes: 18 additions & 4 deletions src/container/styles.scss
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@
display: flex;
flex-direction: column;
block-size: 100%;

&.with-side-media {
flex-direction: row;
}
Expand All @@ -37,13 +38,15 @@
border-end-start-radius: 0;
border-block-end-width: 0;
}

// Meld container top corners into preceding container
&-stacked + &-stacked,
&-stacked + &-stacked::before,
&-stacked + &-stacked::after {
border-start-start-radius: 0;
border-start-end-radius: 0;
}

// Replace container border with a divider
&-stacked + &-stacked:not(.refresh)::before {
@include shared.divider;
Expand All @@ -55,6 +58,7 @@
&::before {
inset-block-start: calc(-1 * #{awsui.$border-container-top-width});
}

&.variant-stacked::before {
inset-block-start: calc(-1 * #{awsui.$border-divider-section-width});
}
Expand All @@ -75,6 +79,7 @@
display: flex;
flex-direction: column;
inline-size: 100%;

&-fit-height {
block-size: 100%;
overflow: hidden;
Expand Down Expand Up @@ -103,6 +108,7 @@
border-block: 0;
border-inline: 0;
}

// stylelint-enable @cloudscape-design/no-implicit-descendant, selector-max-type

// reduce border-radius size to fill the visual gap between the parent border and image
Expand All @@ -123,12 +129,14 @@
background-color: awsui.$color-background-container-header;
border-start-start-radius: awsui.$border-radius-container;
border-start-end-radius: awsui.$border-radius-container;

&.header-full-page {
background-color: awsui.$color-background-layout-main;
}

&.header-with-media {
background: none;

&:not(:empty) {
border-block-end: none;
}
Expand Down Expand Up @@ -158,6 +166,7 @@
border-block: 0;
border-inline: 0;
}

&:not(.header-variant-cards) {
box-shadow: awsui.$shadow-sticky-embedded;
}
Expand Down Expand Up @@ -191,6 +200,7 @@
&-variant-cards {
// Border and shadows are applied with ::before and ::after elements (respectively)
@include styles.container-style;

&:not(.header-sticky-enabled) {
position: relative;
}
Expand Down Expand Up @@ -244,17 +254,21 @@

.content {
flex: 1;

&-fit-height {
overflow: auto;
}

// Using margins instead of paddings so that the content overflow works correctly.
&.with-paddings {
padding-block: awsui.$space-scaled-l;
padding-inline: awsui.$space-container-horizontal;
margin-block: awsui.$space-scaled-l;
margin-inline: awsui.$space-container-horizontal;

.header + & {
padding-block-start: awsui.$space-container-content-top;
margin-block-start: awsui.$space-container-content-top;

&.content-with-media {
padding-block-start: 0;
margin-block-start: 0;
}
}
}
Expand Down
54 changes: 31 additions & 23 deletions src/table/__integ__/sticky-scrollbar.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,23 +4,18 @@ import { BasePageObject } from '@cloudscape-design/browser-test-tools/page-objec
import useBrowser from '@cloudscape-design/browser-test-tools/use-browser';

import createWrapper from '../../../lib/components/test-utils/selectors';
const tableWrapper = createWrapper().findTable();

import scrollbarStyles from '../../../lib/components/table/sticky-scrollbar/styles.selectors.js';

const scrollbarSelector = `.${scrollbarStyles['sticky-scrollbar-visible']}`;

class StickyScrollbarPage extends BasePageObject {
findVisibleScrollbar() {
return tableWrapper.find(scrollbarSelector).toSelector();
}
findTable() {
return tableWrapper.toSelector();
}
}
const tableWrapper = createWrapper().findTable();
const containerWrapper = createWrapper().findContainer();
const scrollbarWrapper = tableWrapper.find(scrollbarSelector);

const setupTest = (testFn: (page: StickyScrollbarPage) => Promise<void>, isVisualRefresh?: boolean) => {
const setupTest = (testFn: (page: BasePageObject) => Promise<void>, isVisualRefresh?: boolean) => {
return useBrowser(async browser => {
const page = new StickyScrollbarPage(browser);
const page = new BasePageObject(browser);
await page.setWindowSize({ width: 600, height: 400 });
await browser.url(
`#/light/table/sticky-scrollbar?${isVisualRefresh ? 'visualRefresh=true' : 'visualRefresh=false'}`
Expand All @@ -34,49 +29,62 @@ describe('Sticky scrollbar', () => {
test(
`is visible, when the table is in view, but it's bottom is not`,
setupTest(async page => {
await expect(page.isExisting(await page.findVisibleScrollbar())).resolves.toEqual(true);
await expect(page.isExisting(scrollbarWrapper.toSelector())).resolves.toEqual(true);
})
);

[false, true].forEach(visualRefresh =>
describe(`visualRefresh=${visualRefresh}`, () => {
test(
`scrollbarWidth is equal to tableWidth`,
'scrollbarWidth is equal to tableWidth',
setupTest(async page => {
const { width: scrollbarWidth } = await page.getBoundingBox(await page.findVisibleScrollbar());
const { width: tableWidth } = await page.getBoundingBox(await page.findTable());
const { width: scrollbarWidth } = await page.getBoundingBox(scrollbarWrapper.toSelector());
const { width: tableWidth } = await page.getBoundingBox(tableWrapper.toSelector());
const borderOffset = visualRefresh ? 2 : 0;
expect(scrollbarWidth).toEqual(tableWidth - borderOffset);
}, visualRefresh)
);

test(
'sticky scrollbar is at the bottom when rendered inside container with fit-height',
useBrowser(async browser => {
const page = new BasePageObject(browser);
await browser.url(
`#/light/table/sticky-scrollbar-in-container?visualRefresh=${visualRefresh}&fitHeight=true`
);
const { bottom: containerBottom } = await page.getBoundingBox(containerWrapper.findContent().toSelector());
const { bottom: scrollbarBottom } = await page.getBoundingBox(scrollbarWrapper.toSelector());
expect(scrollbarBottom).toBe(containerBottom);
})
);
})
);

test(
`is hidden, when page is resized and table fits into the screen`,
'is hidden, when page is resized and table fits into the screen',
setupTest(async page => {
await page.setWindowSize({ width: 1600, height: 400 });
await expect(page.isExisting(await page.findVisibleScrollbar())).resolves.toEqual(false);
await expect(page.isExisting(scrollbarWrapper.toSelector())).resolves.toEqual(false);
})
);
test(
`appears when screen is resized`,
'appears when screen is resized',
setupTest(async page => {
await page.setWindowSize({ width: 1600, height: 400 });
await expect(page.isExisting(await page.findVisibleScrollbar())).resolves.toEqual(false);
await expect(page.isExisting(scrollbarWrapper.toSelector())).resolves.toEqual(false);
await page.setWindowSize({ width: 600, height: 400 });
await expect(page.isExisting(await page.findVisibleScrollbar())).resolves.toEqual(true);
await expect(page.isExisting(scrollbarWrapper.toSelector())).resolves.toEqual(true);
})
);
test(
`scrollbar position updates when window resizes`,
'scrollbar position updates when window resizes',
setupTest(async page => {
await page.setWindowSize({ width: 600, height: 600 });
const { bottom: bottom1 } = await page.getBoundingBox(page.findVisibleScrollbar());
const { bottom: bottom1 } = await page.getBoundingBox(scrollbarWrapper.toSelector());
expect(bottom1).toEqual((await page.getViewportSize()).height);

await page.setWindowSize({ width: 600, height: 400 });
const { bottom: bottom2 } = await page.getBoundingBox(page.findVisibleScrollbar());
const { bottom: bottom2 } = await page.getBoundingBox(scrollbarWrapper.toSelector());
expect(bottom1 - bottom2).toBe(200);
})
);
Expand Down

0 comments on commit 11df6c3

Please sign in to comment.