Skip to content

Commit 458d25f

Browse files
committed
Fix footer column widths
1 parent 52798b3 commit 458d25f

4 files changed

Lines changed: 137 additions & 99 deletions

File tree

src/components/datagrid/data_grid.tsx

Lines changed: 19 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -761,13 +761,27 @@ export const EuiDataGrid: FunctionComponent<EuiDataGridProps> = (props) => {
761761
};
762762

763763
// enables/disables grid controls based on available width
764+
const [sizeIsStable, setSizeIsStable] = useState(false);
765+
764766
const [resizeRef, setResizeRef] = useState<HTMLDivElement | null>(null);
765767
const gridDimensions = useResizeObserver(resizeRef);
766768
useEffect(() => {
767-
const { height, width } = gridDimensions;
768-
setGridWidth(width);
769-
setGridHeight(height);
770-
}, [gridDimensions]);
769+
if (resizeRef) {
770+
if (sizeIsStable) {
771+
const { height, width } = gridDimensions;
772+
setGridWidth(width);
773+
setGridHeight((currentHeight) => {
774+
// because of a race condition between useResizeObserver which
775+
// fires async outside of the React lifecycle
776+
// and useEffect, we allow the height to grow but not shrink
777+
return currentHeight < height ? height : currentHeight;
778+
});
779+
}
780+
} else {
781+
setGridWidth(0);
782+
setGridHeight(0);
783+
}
784+
}, [sizeIsStable, resizeRef, gridDimensions]);
771785

772786
const hasRoomForGridControls = gridWidth > minSizeForControls || isFullScreen;
773787

@@ -1112,6 +1126,7 @@ export const EuiDataGrid: FunctionComponent<EuiDataGridProps> = (props) => {
11121126
rowCount={rowCount}
11131127
interactiveCellId={interactiveCellId}
11141128
resetGridHeight={resetGridHeight}
1129+
setSizeIsStable={setSizeIsStable}
11151130
/>
11161131
</div>
11171132
</div>

src/components/datagrid/data_grid_body.tsx

Lines changed: 28 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -85,6 +85,7 @@ export interface EuiDataGridBodyProps {
8585
setVisibleColumns: EuiDataGridHeaderRowProps['setVisibleColumns'];
8686
switchColumnPos: EuiDataGridHeaderRowProps['switchColumnPos'];
8787
resetGridHeight: () => void;
88+
setSizeIsStable: (sizeIsStable: boolean) => void;
8889
}
8990

9091
const defaultComparator: NonNullable<
@@ -116,8 +117,6 @@ const providedPopoverContents: EuiDataGridPopoverContents = {
116117
},
117118
};
118119

119-
const FOOTER_ROW_HEIGHT = 34;
120-
121120
const DefaultColumnFormatter: EuiDataGridPopoverContent = ({ children }) => {
122121
return <EuiText>{children}</EuiText>;
123122
};
@@ -291,18 +290,31 @@ export const EuiDataGridBody: FunctionComponent<EuiDataGridBodyProps> = (
291290
handleHeaderMutation,
292291
setVisibleColumns,
293292
switchColumnPos,
294-
resetGridHeight
293+
resetGridHeight,
294+
setSizeIsStable,
295295
} = props;
296296

297+
const hasFooterRow = renderFooterCellValue;
298+
297299
const [headerRowRef, setHeaderRowRef] = useState<HTMLDivElement | null>(null);
300+
const [footerRowRef, setFooterRowRef] = useState<HTMLDivElement | null>(null);
298301

299302
useMutationObserver(headerRowRef, handleHeaderMutation, {
300303
subtree: true,
301304
childList: true,
302305
});
303306
const { height: headerRowHeight } = useResizeObserver(headerRowRef, 'height');
307+
const { height: footerRowHeight } = useResizeObserver(footerRowRef, 'height');
304308

305-
useEffect(() => resetGridHeight(), [headerRowHeight]);
309+
useEffect(() => {
310+
const isHeaderStable = headerRowHeight !== 0;
311+
const isFooterStable = !hasFooterRow || footerRowHeight !== 0;
312+
setSizeIsStable(isHeaderStable && isFooterStable);
313+
}, [hasFooterRow, setSizeIsStable, headerRowHeight, footerRowHeight]);
314+
315+
useEffect(() => {
316+
resetGridHeight();
317+
}, [resetGridHeight, headerRowHeight, footerRowHeight]);
306318

307319
const startRow = pagination ? pagination.pageIndex * pagination.pageSize : 0;
308320
let endRow = pagination
@@ -419,6 +431,7 @@ export const EuiDataGridBody: FunctionComponent<EuiDataGridBodyProps> = (
419431
return (
420432
<EuiDataGridFooterRow
421433
key="footerRow"
434+
ref={setFooterRowRef}
422435
leadingControlColumns={leadingControlColumns}
423436
trailingControlColumns={trailingControlColumns}
424437
columns={columns}
@@ -508,6 +521,16 @@ export const EuiDataGridBody: FunctionComponent<EuiDataGridBodyProps> = (
508521
if (gridRef.current) gridRef.current.resetAfterRowIndex(0);
509522
}, [getRowHeight]);
510523

524+
const height =
525+
// intentionally ignoring gridHeight if it is null/undefined/0
526+
// and using it only if we have found the header&footer heights
527+
(headerRowHeight && (!hasFooterRow || footerRowHeight) && gridHeight) ||
528+
// otherwise compute the height
529+
rowHeight * visibleRowIndices.length +
530+
SCROLLBAR_HEIGHT +
531+
headerRowHeight +
532+
footerRowHeight;
533+
511534
return (
512535
<DataGridHeaderRowHeightContext.Provider value={headerRowHeight}>
513536
<Grid
@@ -521,14 +544,7 @@ export const EuiDataGridBody: FunctionComponent<EuiDataGridBodyProps> = (
521544
}
522545
width={gridWidth}
523546
columnWidth={getWidth}
524-
height={
525-
// intentionally ignoring gridHeight if it is null/undefined/0
526-
(headerRowHeight && gridHeight) ||
527-
rowHeight * visibleRowIndices.length +
528-
SCROLLBAR_HEIGHT +
529-
headerRowHeight +
530-
(footerRow ? FOOTER_ROW_HEIGHT : 0)
531-
}
547+
height={height}
532548
rowHeight={getRowHeight}
533549
itemData={{
534550
setRowHeight,

src/components/datagrid/data_grid_cell.tsx

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -341,8 +341,7 @@ export class EuiDataGridCell extends Component<
341341
className: classNames(cellClasses, this.state.cellProps.className),
342342
};
343343

344-
const widthStyle = width != null ? { width: `${width}px` } : {};
345-
cellProps.style = { ...style, widthStyle };
344+
cellProps.style = { ...style, width };
346345

347346
const handleCellKeyDown = (event: KeyboardEvent<HTMLDivElement>) => {
348347
if (isExpandable) {

src/components/datagrid/data_grid_footer_row.tsx

Lines changed: 89 additions & 81 deletions
Original file line numberDiff line numberDiff line change
@@ -17,7 +17,7 @@
1717
* under the License.
1818
*/
1919

20-
import React, { FunctionComponent, HTMLAttributes, memo } from 'react';
20+
import React, { forwardRef, HTMLAttributes, memo } from 'react';
2121
import classnames from 'classnames';
2222
import {
2323
EuiDataGridControlColumn,
@@ -51,86 +51,47 @@ const DefaultColumnFormatter: EuiDataGridPopoverContent = ({ children }) => {
5151
return <EuiText>{children}</EuiText>;
5252
};
5353

54-
const EuiDataGridFooterRow: FunctionComponent<EuiDataGridFooterRowProps> = memo(
55-
({
56-
leadingControlColumns,
57-
trailingControlColumns,
58-
columns,
59-
schema,
60-
popoverContents,
61-
columnWidths,
62-
defaultColumnWidth,
63-
className,
64-
renderCellValue,
65-
rowIndex,
66-
interactiveCellId,
67-
'data-test-subj': _dataTestSubj,
68-
visibleRowIndex = rowIndex,
69-
...rest
70-
}) => {
71-
const classes = classnames(
72-
'euiDataGridRow',
73-
'euiDataGridFooter',
74-
className
75-
);
76-
const dataTestSubj = classnames('dataGridRow', _dataTestSubj);
54+
const EuiDataGridFooterRow = memo(
55+
forwardRef<HTMLDivElement, EuiDataGridFooterRowProps>(
56+
(
57+
{
58+
leadingControlColumns,
59+
trailingControlColumns,
60+
columns,
61+
schema,
62+
popoverContents,
63+
columnWidths,
64+
defaultColumnWidth,
65+
className,
66+
renderCellValue,
67+
rowIndex,
68+
interactiveCellId,
69+
'data-test-subj': _dataTestSubj,
70+
visibleRowIndex = rowIndex,
71+
...rest
72+
},
73+
ref
74+
) => {
75+
const classes = classnames(
76+
'euiDataGridRow',
77+
'euiDataGridFooter',
78+
className
79+
);
80+
const dataTestSubj = classnames('dataGridRow', _dataTestSubj);
7781

78-
return (
79-
<div
80-
role="row"
81-
className={classes}
82-
data-test-subj={dataTestSubj}
83-
{...rest}>
84-
{leadingControlColumns.map(({ id, width }, i) => (
85-
<EuiDataGridCell
86-
key={`${id}-${rowIndex}`}
87-
rowIndex={rowIndex}
88-
visibleRowIndex={visibleRowIndex}
89-
colIndex={i}
90-
columnId={id}
91-
popoverContent={DefaultColumnFormatter}
92-
width={width}
93-
renderCellValue={() => null}
94-
interactiveCellId={interactiveCellId}
95-
isExpandable={true}
96-
className="euiDataGridFooterCell euiDataGridRowCell--controlColumn"
97-
/>
98-
))}
99-
{columns.map(({ id }, i) => {
100-
const columnType = schema[id] ? schema[id].columnType : null;
101-
const popoverContent =
102-
(columnType && popoverContents[columnType]) ||
103-
DefaultColumnFormatter;
104-
105-
const width = columnWidths[id] || defaultColumnWidth;
106-
const columnPosition = i + leadingControlColumns.length;
107-
108-
return (
82+
return (
83+
<div
84+
ref={ref}
85+
role="row"
86+
className={classes}
87+
data-test-subj={dataTestSubj}
88+
{...rest}>
89+
{leadingControlColumns.map(({ id, width }, i) => (
10990
<EuiDataGridCell
11091
key={`${id}-${rowIndex}`}
11192
rowIndex={rowIndex}
11293
visibleRowIndex={visibleRowIndex}
113-
colIndex={columnPosition}
114-
columnId={id}
115-
columnType={columnType}
116-
popoverContent={popoverContent}
117-
width={width || undefined}
118-
renderCellValue={renderCellValue}
119-
interactiveCellId={interactiveCellId}
120-
isExpandable={true}
121-
className="euiDataGridFooterCell"
122-
/>
123-
);
124-
})}
125-
{trailingControlColumns.map(({ id, width }, i) => {
126-
const colIndex = i + columns.length + leadingControlColumns.length;
127-
128-
return (
129-
<EuiDataGridCell
130-
key={`${id}-${rowIndex}`}
131-
rowIndex={rowIndex}
132-
visibleRowIndex={visibleRowIndex}
133-
colIndex={colIndex}
94+
colIndex={i}
13495
columnId={id}
13596
popoverContent={DefaultColumnFormatter}
13697
width={width}
@@ -139,11 +100,58 @@ const EuiDataGridFooterRow: FunctionComponent<EuiDataGridFooterRowProps> = memo(
139100
isExpandable={true}
140101
className="euiDataGridFooterCell euiDataGridRowCell--controlColumn"
141102
/>
142-
);
143-
})}
144-
</div>
145-
);
146-
}
103+
))}
104+
{columns.map(({ id }, i) => {
105+
const columnType = schema[id] ? schema[id].columnType : null;
106+
const popoverContent =
107+
(columnType && popoverContents[columnType]) ||
108+
DefaultColumnFormatter;
109+
110+
const width = columnWidths[id] || defaultColumnWidth;
111+
const columnPosition = i + leadingControlColumns.length;
112+
113+
return (
114+
<EuiDataGridCell
115+
key={`${id}-${rowIndex}`}
116+
rowIndex={rowIndex}
117+
visibleRowIndex={visibleRowIndex}
118+
colIndex={columnPosition}
119+
columnId={id}
120+
columnType={columnType}
121+
popoverContent={popoverContent}
122+
width={width || undefined}
123+
renderCellValue={renderCellValue}
124+
interactiveCellId={interactiveCellId}
125+
isExpandable={true}
126+
className="euiDataGridFooterCell"
127+
/>
128+
);
129+
})}
130+
{trailingControlColumns.map(({ id, width }, i) => {
131+
const colIndex = i + columns.length + leadingControlColumns.length;
132+
133+
return (
134+
<EuiDataGridCell
135+
key={`${id}-${rowIndex}`}
136+
rowIndex={rowIndex}
137+
visibleRowIndex={visibleRowIndex}
138+
colIndex={colIndex}
139+
columnId={id}
140+
popoverContent={DefaultColumnFormatter}
141+
width={width}
142+
renderCellValue={() => null}
143+
interactiveCellId={interactiveCellId}
144+
isExpandable={true}
145+
className="euiDataGridFooterCell euiDataGridRowCell--controlColumn"
146+
/>
147+
);
148+
})}
149+
</div>
150+
);
151+
}
152+
)
147153
);
148154

155+
EuiDataGridFooterRow.displayName = 'EuiDataGridFooterRow';
156+
149157
export { EuiDataGridFooterRow };

0 commit comments

Comments
 (0)