Skip to content
2 changes: 2 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -1,5 +1,7 @@
## [`main`](https://github.com/elastic/eui/tree/main)

- Updated `EuiDataGrid`s with scrolling content to always have a border around the grid body and any scrollbars ([#5563](https://github.com/elastic/eui/pull/5563))

**Bug fixes**

- Fixed EuiDataGrid height issue when in full-screen mode and with scrolling content ([#5557](https://github.com/elastic/eui/pull/5557))
Expand Down
5 changes: 4 additions & 1 deletion src-docs/src/views/datagrid/container.js
Original file line number Diff line number Diff line change
Expand Up @@ -52,7 +52,10 @@ export default () => {
);

return (
<EuiPanel style={{ maxWidth: 400, height: 300 }} paddingSize="none">
<EuiPanel
style={{ maxWidth: 400, height: 300, overflow: 'hidden' }}
paddingSize="none"
>
<EuiDataGrid
aria-label="Container constrained data grid demo"
columns={columns}
Expand Down
6 changes: 5 additions & 1 deletion src-docs/src/views/datagrid/flex.js
Original file line number Diff line number Diff line change
Expand Up @@ -63,7 +63,11 @@ export default () => {
<EuiPanel color="subdued">Sidebar</EuiPanel>
</EuiFlexItem>
<EuiFlexItem style={{ minWidth: 0 }}>
<EuiPanel style={{ height: 300 }} hasBorder paddingSize="none">
<EuiPanel
style={{ height: 300, overflow: 'hidden' }}
hasBorder
paddingSize="none"
>
<EuiDataGrid
aria-label="Container constrained data grid demo"
columns={columns}
Expand Down
48 changes: 46 additions & 2 deletions src/components/datagrid/_data_grid.scss
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,7 @@
.euiDataGrid__pagination {
padding-bottom: $euiSizeXS;
background: $euiColorLightestShade;
border-top: $euiBorderThin;
box-shadow: $euiBorderWidthThin 0 0 $euiBorderWidthThin $euiBorderColor; // Use box-shadow instead of border-top to avoid duplicating the border-bottom on grid cells
}
}

Expand All @@ -29,12 +29,14 @@
max-width: 100%;
width: 100%;
overflow: hidden;
z-index: 2; // Sits above the pagination below it, but below the controls above it
z-index: 1; // Sits below the controls above it and pagination below it
position: relative;
background: $euiColorEmptyShade;
font-feature-settings: 'tnum' 1; // Tabular numbers
}

.euiDataGrid__pagination {
z-index: 2; // Sits above the content above it
padding-top: $euiSizeXS;
flex-grow: 0;
}
Expand Down Expand Up @@ -65,3 +67,45 @@
@include euiScrollBar($euiColorDarkShade, $euiColorEmptyShade);
scroll-padding: 0;
}

.euiDataGrid__scrollOverlay {
position: absolute;
top: -1 * $euiBorderWidthThin; // Overlaps the toolbar border
right: 0;
bottom: 0;
left: 0;

// Ensure the underlying grid is still interactable
pointer-events: none;

// Ensure the scrolling data grid body always has border edges
// regardless of cell position
box-shadow: inset 0 0 0 $euiBorderWidthThin $euiBorderColor;
// Note that this *must* be an inset `box-shadow` and not `border`, because
// border will affect the relative position of the child scroll bar overlays
// and cause them to be off by the width of the border

// For grids with horizontal-only borders, only render a bottom 'border'
.euiDataGrid--bordersHorizontal & {
box-shadow: inset 0 (-2 * $euiBorderWidthThin) 0 (-1 * $euiBorderWidthThin) $euiBorderColor;
}

// Ensure the horizontal scrollbar has a top border
.euiDataGrid__scrollBarOverlayBottom {
position: absolute;
width: 100%;
height: $euiBorderWidthThin;
background-color: $euiBorderColor;
}

// Ensure the vertical scrollbar has a left border
.euiDataGrid__scrollBarOverlayRight {
position: absolute;
height: 100%;
width: $euiBorderWidthThin;
background-color: $euiBorderColor;
}

// Note: Scroll bar border positions are set via JS inline style, since
// JS has access to the exact OS scrollbar width/height and CSS doesn't
}
17 changes: 14 additions & 3 deletions src/components/datagrid/body/data_grid_body.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -43,7 +43,7 @@ import {
import { useDefaultColumnWidth, useColumnWidths } from '../utils/col_widths';
import { useRowHeightUtils, useDefaultRowHeight } from '../utils/row_heights';
import { useHeaderFocusWorkaround } from '../utils/focus';
import { useScroll } from '../utils/scrolling';
import { useScrollBars, useScroll } from '../utils/scrolling';
import { DataGridSortingContext } from '../utils/sorting';
import { IS_JEST_ENVIRONMENT } from '../../../test';

Expand Down Expand Up @@ -253,6 +253,16 @@ export const EuiDataGridBody: FunctionComponent<EuiDataGridBodyProps> = (
const outerGridRef = useRef<HTMLDivElement | null>(null); // container that becomes scrollable
const innerGridRef = useRef<HTMLDivElement | null>(null); // container sized to fit all content

/**
* Scroll bars
*/
const {
scrollBarHeight,
hasVerticalScroll,
hasHorizontalScroll,
scrollBorderOverlay,
} = useScrollBars(outerGridRef, gridStyles.border);

/**
* Widths
*/
Expand Down Expand Up @@ -364,7 +374,7 @@ export const EuiDataGridBody: FunctionComponent<EuiDataGridBodyProps> = (
useScroll({
gridRef,
outerGridRef,
innerGridRef,
hasGridScrolling: hasVerticalScroll || hasHorizontalScroll,
headerRowHeight,
footerRowHeight,
visibleRowCount,
Expand Down Expand Up @@ -402,7 +412,7 @@ export const EuiDataGridBody: FunctionComponent<EuiDataGridBodyProps> = (
defaultRowHeight,
headerRowHeight,
footerRowHeight,
outerGridRef,
scrollBarHeight,
innerGridRef,
});

Expand Down Expand Up @@ -486,6 +496,7 @@ export const EuiDataGridBody: FunctionComponent<EuiDataGridBodyProps> = (
>
{Cell}
</Grid>
{scrollBorderOverlay}
</DataGridWrapperRowsContext.Provider>
) : null;
};
2 changes: 1 addition & 1 deletion src/components/datagrid/controls/_data_grid_toolbar.scss
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
.euiDataGrid__controls {
background: $euiPageBackgroundColor;
position: relative;
z-index: 3; // Needs to sit above the content blow that sits below it
z-index: 2; // Needs to sit above the content below it
border: $euiBorderThin;
padding: $euiSizeXS $euiSizeXS $euiSizeXS 0;
display: flex;
Expand Down
15 changes: 3 additions & 12 deletions src/components/datagrid/utils/grid_height_width.ts
Original file line number Diff line number Diff line change
Expand Up @@ -72,7 +72,7 @@ export const useUnconstrainedHeight = ({
defaultRowHeight,
headerRowHeight,
footerRowHeight,
outerGridRef,
scrollBarHeight,
innerGridRef,
}: {
rowHeightUtils: RowHeightUtils;
Expand All @@ -82,7 +82,7 @@ export const useUnconstrainedHeight = ({
defaultRowHeight: number;
headerRowHeight: number;
footerRowHeight: number;
outerGridRef: React.MutableRefObject<HTMLDivElement | null>;
scrollBarHeight: number;
innerGridRef: React.MutableRefObject<HTMLDivElement | null>;
}) => {
const { getCorrectRowIndex } = useContext(DataGridSortingContext);
Expand Down Expand Up @@ -127,21 +127,12 @@ export const useUnconstrainedHeight = ({
);
useUpdateEffect(forceRender, [innerWidth]);

// https://stackoverflow.com/a/5038256
const hasHorizontalScroll =
(outerGridRef.current?.scrollWidth ?? 0) >
(outerGridRef.current?.clientWidth ?? 0);
// https://stackoverflow.com/a/24797425
const scrollbarHeight = hasHorizontalScroll
? outerGridRef.current!.offsetHeight - outerGridRef.current!.clientHeight
: 0;

const unconstrainedHeight =
defaultRowHeight * (rowCountToAffordFor - knownRowCount) + // guess how much space is required for unknown rows
knownHeight + // computed pixel height of the known rows
headerRowHeight + // account for header
footerRowHeight + // account for footer
scrollbarHeight; // account for horizontal scrollbar
scrollBarHeight; // account for horizontal scrollbar

return unconstrainedHeight;
};
Expand Down
Loading