diff --git a/CHANGELOG.md b/CHANGELOG.md
index 6768b132171..0c448a0a43f 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -10,6 +10,7 @@
- Fixed `mobileOptions.truncateText` from getting overridden by `truncateText` in `EuiTableRowCell` ([#5283](https://github.com/elastic/eui/pull/5283))
- Fixed issue with dynamic row counts in `EuiDataGrid` ([#5313](https://github.com/elastic/eui/pull/5313))
- Fixed `EuiDataGrid`'s expanded density not increasing font sizes on Amsterdam ([#5320](https://github.com/elastic/eui/pull/5320))
+- Fixed `EuiDataGrid` to dynamically update row heights when set to `auto` ([#5281](https://github.com/elastic/eui/pull/5281))
**Theme: Amsterdam**
diff --git a/src/components/datagrid/__mocks__/row_height_utils.ts b/src/components/datagrid/__mocks__/row_height_utils.ts
index dac8430c3ab..7e4ef01d812 100644
--- a/src/components/datagrid/__mocks__/row_height_utils.ts
+++ b/src/components/datagrid/__mocks__/row_height_utils.ts
@@ -12,7 +12,6 @@ import { RowHeightUtils } from '../row_height_utils';
export const mockRowHeightUtils = ({
computeStylesForGridCell: jest.fn(),
- clearHeightsCache: jest.fn(),
setGrid: jest.fn(),
getStylesForCell: jest.fn(() => ({
wordWrap: 'break-word',
@@ -22,13 +21,9 @@ export const mockRowHeightUtils = ({
isDefinedHeight: jest.fn(() => true),
isAutoHeight: jest.fn(() => false),
setRowHeight: jest.fn(),
+ pruneHiddenColumnHeights: jest.fn(),
getRowHeight: jest.fn(() => 32),
- getFont: jest.fn(() => null),
getComputedCellStyles: jest.fn(() => {}),
- compareHeights: jest.fn(
- (currentRowHeight: number, cachedRowHeight: number) =>
- currentRowHeight === cachedRowHeight
- ),
getCalculatedHeight: jest.fn(
(heightOption: EuiDataGridRowHeightOption, defaultHeight: number) => {
if (typeof heightOption === 'object') {
diff --git a/src/components/datagrid/body/__snapshots__/data_grid_cell.test.tsx.snap b/src/components/datagrid/body/__snapshots__/data_grid_cell.test.tsx.snap
index 6293227a1cb..a502481d2e4 100644
--- a/src/components/datagrid/body/__snapshots__/data_grid_cell.test.tsx.snap
+++ b/src/components/datagrid/body/__snapshots__/data_grid_cell.test.tsx.snap
@@ -10,16 +10,14 @@ exports[`EuiDataGridCell renders 1`] = `
renderCellValue={[Function]}
rowHeightUtils={
Object {
- "clearHeightsCache": [MockFunction],
- "compareHeights": [MockFunction],
"computeStylesForGridCell": [MockFunction],
"getCalculatedHeight": [MockFunction],
"getComputedCellStyles": [MockFunction],
- "getFont": [MockFunction],
"getRowHeight": [MockFunction],
"getStylesForCell": [MockFunction],
"isAutoHeight": [MockFunction],
"isDefinedHeight": [MockFunction],
+ "pruneHiddenColumnHeights": [MockFunction],
"setGrid": [MockFunction],
"setRowHeight": [MockFunction],
}
@@ -59,16 +57,14 @@ exports[`EuiDataGridCell renders 1`] = `
renderCellValue={[Function]}
rowHeightUtils={
Object {
- "clearHeightsCache": [MockFunction],
- "compareHeights": [MockFunction],
"computeStylesForGridCell": [MockFunction],
"getCalculatedHeight": [MockFunction],
"getComputedCellStyles": [MockFunction],
- "getFont": [MockFunction],
"getRowHeight": [MockFunction],
"getStylesForCell": [MockFunction],
"isAutoHeight": [MockFunction],
"isDefinedHeight": [MockFunction],
+ "pruneHiddenColumnHeights": [MockFunction],
"setGrid": [MockFunction],
"setRowHeight": [MockFunction],
}
diff --git a/src/components/datagrid/body/data_grid_cell.test.tsx b/src/components/datagrid/body/data_grid_cell.test.tsx
index 7b8e9592469..d837f1a4cfd 100644
--- a/src/components/datagrid/body/data_grid_cell.test.tsx
+++ b/src/components/datagrid/body/data_grid_cell.test.tsx
@@ -9,8 +9,8 @@
import React from 'react';
import { mount, ReactWrapper } from 'enzyme';
import { keys } from '../../../services';
-
import { mockRowHeightUtils } from '../__mocks__/row_height_utils';
+
import { EuiDataGridCell } from './data_grid_cell';
describe('EuiDataGridCell', () => {
@@ -108,6 +108,9 @@ describe('EuiDataGridCell', () => {
it('width', () => {
component.setProps({ width: 30 });
});
+ it('rowHeightsOptions', () => {
+ component.setProps({ rowHeightsOptions: { defaultHeight: 'auto' } });
+ });
it('renderCellValue', () => {
component.setProps({ renderCellValue: () =>
test
});
});
@@ -148,16 +151,6 @@ describe('EuiDataGridCell', () => {
component.setState({ disableCellTabIndex: true });
});
});
-
- it('when cell height changes', () => {
- Object.defineProperty(HTMLElement.prototype, 'offsetHeight', {
- configurable: true,
- value: 10,
- });
- const getRowHeight = jest.fn(() => 20);
-
- component.setProps({ getRowHeight });
- });
});
it('should not update for prop/state changes not specified above', () => {
@@ -167,19 +160,40 @@ describe('EuiDataGridCell', () => {
});
describe('componentDidUpdate', () => {
- it('recalculates row height on every update', () => {
- const { isAutoHeight, setRowHeight } = mockRowHeightUtils;
- (isAutoHeight as jest.Mock).mockImplementation(() => true);
+ describe('recalculateRowHeight', () => {
+ beforeEach(() => {
+ (mockRowHeightUtils.setRowHeight as jest.Mock).mockClear();
+ });
+ afterEach(() => {
+ (mockRowHeightUtils.isAutoHeight as jest.Mock).mockRestore();
+ });
- const component = mountEuiDataGridCellWithContext({
- rowHeightsOptions: { defaultHeight: 'auto' },
- getRowHeight: jest.fn(() => 50),
+ const triggerUpdate = (component: ReactWrapper) =>
+ component.setProps({ rowIndex: 2 });
+
+ it('sets the row height cache with cell heights on update', () => {
+ (mockRowHeightUtils.isAutoHeight as jest.Mock).mockReturnValue(true);
+
+ const component = mountEuiDataGridCellWithContext({
+ rowHeightsOptions: { defaultHeight: 'auto' },
+ getRowHeight: jest.fn(() => 50),
+ });
+
+ triggerUpdate(component);
+ expect(mockRowHeightUtils.setRowHeight).toHaveBeenCalled();
});
- component.setProps({ rowIndex: 2 }); // Trigger any update
- expect(setRowHeight).toHaveBeenCalled();
+ it('does not update the cache if cell height is not auto', () => {
+ (mockRowHeightUtils.isAutoHeight as jest.Mock).mockReturnValue(false);
- (isAutoHeight as jest.Mock).mockRestore();
+ const component = mountEuiDataGridCellWithContext({
+ rowHeightsOptions: { defaultHeight: 34 },
+ getRowHeight: jest.fn(() => 50),
+ });
+
+ triggerUpdate(component);
+ expect(mockRowHeightUtils.setRowHeight).not.toHaveBeenCalled();
+ });
});
it('resets cell props when the cell columnId changes', () => {
diff --git a/src/components/datagrid/body/data_grid_cell.tsx b/src/components/datagrid/body/data_grid_cell.tsx
index 0260120b12f..0af68a69181 100644
--- a/src/components/datagrid/body/data_grid_cell.tsx
+++ b/src/components/datagrid/body/data_grid_cell.tsx
@@ -194,32 +194,24 @@ export class EuiDataGridCell extends Component<
}
};
- recalculateRowHeight() {
- const cellRef = this.cellRef.current;
- const { getRowHeight, rowHeightUtils, rowHeightsOptions } = this.props;
-
- if (cellRef && getRowHeight && rowHeightUtils && rowHeightsOptions) {
- const { rowIndex, colIndex, visibleRowIndex } = this.props;
+ recalculateRowHeight = () => {
+ const { rowHeightUtils, rowHeightsOptions, rowIndex } = this.props;
+ if (
+ this.cellContentsRef &&
+ rowHeightUtils &&
+ rowHeightUtils.isAutoHeight(rowIndex, rowHeightsOptions)
+ ) {
+ const { columnId, visibleRowIndex } = this.props;
+ const rowHeight = this.cellContentsRef.offsetHeight;
- const isAutoHeight = rowHeightUtils.isAutoHeight(
+ rowHeightUtils.setRowHeight(
rowIndex,
- rowHeightsOptions
- );
- const isHeightSame = rowHeightUtils.compareHeights(
- cellRef.offsetHeight,
- getRowHeight(rowIndex)
+ columnId,
+ rowHeight,
+ visibleRowIndex
);
-
- if (isAutoHeight && !isHeightSame) {
- rowHeightUtils.setRowHeight(
- rowIndex,
- colIndex,
- this.cellContentsRef?.offsetHeight,
- visibleRowIndex
- );
- }
}
- }
+ };
componentDidMount() {
this.unsubscribeCell = this.context.onFocusUpdate(
@@ -261,6 +253,8 @@ export class EuiDataGridCell extends Component<
if (nextProps.columnId !== this.props.columnId) return true;
if (nextProps.columnType !== this.props.columnType) return true;
if (nextProps.width !== this.props.width) return true;
+ if (nextProps.rowHeightsOptions !== this.props.rowHeightsOptions)
+ return true;
if (nextProps.renderCellValue !== this.props.renderCellValue) return true;
if (nextProps.interactiveCellId !== this.props.interactiveCellId)
return true;
@@ -286,22 +280,6 @@ export class EuiDataGridCell extends Component<
if (nextState.disableCellTabIndex !== this.state.disableCellTabIndex)
return true;
- // check if we should update cell because height was changed
- if (
- this.cellRef.current &&
- nextProps.getRowHeight &&
- nextProps.rowHeightUtils
- ) {
- if (
- !nextProps.rowHeightUtils?.compareHeights(
- this.cellRef.current.offsetHeight,
- nextProps.getRowHeight(nextProps.rowIndex)
- )
- ) {
- return true;
- }
- }
-
return false;
}
@@ -311,27 +289,10 @@ export class EuiDataGridCell extends Component<
setCellContentsRef = (ref: HTMLDivElement | null) => {
this.cellContentsRef = ref;
- const { rowHeightUtils, rowHeightsOptions, rowIndex } = this.props;
- if (
- hasResizeObserver &&
- rowHeightUtils &&
- rowHeightsOptions &&
- rowHeightUtils.isAutoHeight(rowIndex, rowHeightsOptions)
- ) {
- if (ref) {
- const { colIndex, visibleRowIndex } = this.props;
-
- const setRowHeight = (rowHeight: number) =>
- rowHeightUtils.setRowHeight(
- rowIndex,
- colIndex,
- rowHeight,
- visibleRowIndex
- );
- this.contentObserver = this.observeHeight(ref, setRowHeight);
- } else if (this.contentObserver) {
- this.contentObserver.disconnect();
- }
+ if (ref && hasResizeObserver) {
+ this.contentObserver = this.observeHeight(ref, this.recalculateRowHeight);
+ } else if (this.contentObserver) {
+ this.contentObserver.disconnect();
}
this.preventTabbing();
};
diff --git a/src/components/datagrid/data_grid.tsx b/src/components/datagrid/data_grid.tsx
index 8a08000301a..3cfc517ef3c 100644
--- a/src/components/datagrid/data_grid.tsx
+++ b/src/components/datagrid/data_grid.tsx
@@ -667,8 +667,8 @@ export const EuiDataGrid: FunctionComponent = (props) => {
const rowHeightUtils = useMemo(() => new RowHeightUtils(), []);
useEffect(() => {
- rowHeightUtils.clearHeightsCache();
- }, [orderedVisibleColumns, rowHeightsOptions, rowHeightUtils]);
+ rowHeightUtils.pruneHiddenColumnHeights(orderedVisibleColumns);
+ }, [rowHeightUtils, orderedVisibleColumns]);
const [contentRef, setContentRef] = useState(null);
diff --git a/src/components/datagrid/row_height_utils.ts b/src/components/datagrid/row_height_utils.ts
index 8ade9c469b4..39344cb5efc 100644
--- a/src/components/datagrid/row_height_utils.ts
+++ b/src/components/datagrid/row_height_utils.ts
@@ -15,6 +15,7 @@ import {
EuiDataGridStyle,
EuiDataGridRowHeightOption,
EuiDataGridRowHeightsOptions,
+ EuiDataGridColumn,
} from './data_grid_types';
const cellPaddingsToClassMap: Record = {
@@ -49,28 +50,52 @@ export class RowHeightUtils {
lineHeight: 1,
};
private fakeCell = document.createElement('div');
- private heightsCache = new Map>();
+ private heightsCache = new Map>();
private timerId: any;
private grid?: Grid;
private lastUpdatedRow: number = Infinity;
setRowHeight(
rowIndex: number,
- colIndex: number,
+ colId: string,
height: number = DEFAULT_HEIGHT,
visibleRowIndex: number
) {
- const rowHeights = this.heightsCache.get(rowIndex) || {};
+ const rowHeights =
+ this.heightsCache.get(rowIndex) || new Map();
const adaptedHeight = Math.ceil(
height + this.styles.paddingTop + this.styles.paddingBottom
);
- if (rowHeights[colIndex] === adaptedHeight) {
+ if (rowHeights.get(colId) === adaptedHeight) {
return;
}
- rowHeights[colIndex] = adaptedHeight;
+ rowHeights.set(colId, adaptedHeight);
this.heightsCache.set(rowIndex, rowHeights);
+ this.resetRow(visibleRowIndex);
+ }
+
+ pruneHiddenColumnHeights(visibleColumns: EuiDataGridColumn[]) {
+ const visibleColumnIds = new Set(visibleColumns.map(({ id }) => id));
+ let didModify = false;
+
+ this.heightsCache.forEach((rowHeights) => {
+ const existingColumnIds = Array.from(rowHeights.keys());
+ existingColumnIds.forEach((existingColumnId) => {
+ if (visibleColumnIds.has(existingColumnId) === false) {
+ didModify = true;
+ rowHeights.delete(existingColumnId);
+ }
+ });
+ });
+
+ if (didModify) {
+ this.resetRow(0);
+ }
+ }
+
+ resetRow(visibleRowIndex: number) {
// save the first row index of batch, reassigning it only
// if this visible row index less than lastUpdatedRow
this.lastUpdatedRow = Math.min(this.lastUpdatedRow, visibleRowIndex);
@@ -79,8 +104,10 @@ export class RowHeightUtils {
}
getRowHeight(rowIndex: number) {
- const rowHeights = this.heightsCache.get(rowIndex) || {};
- const rowHeightValues = Object.values(rowHeights);
+ const rowHeights = this.heightsCache.get(rowIndex);
+ if (rowHeights == null) return 0;
+
+ const rowHeightValues = Array.from(rowHeights.values());
if (rowHeightValues.length) {
return Math.max(...rowHeightValues);
@@ -89,10 +116,6 @@ export class RowHeightUtils {
return 0;
}
- compareHeights(currentRowHeight: number, cachedRowHeight: number) {
- return currentRowHeight === cachedRowHeight;
- }
-
resetGrid() {
this.grid?.resetAfterRowIndex(this.lastUpdatedRow);
this.lastUpdatedRow = Infinity;
@@ -102,20 +125,15 @@ export class RowHeightUtils {
this.grid = grid;
}
- clearHeightsCache() {
- this.lastUpdatedRow = 0;
- this.heightsCache.clear();
- }
-
isAutoHeight(
rowIndex: number,
- rowHeightsOptions: EuiDataGridRowHeightsOptions
+ rowHeightsOptions?: EuiDataGridRowHeightsOptions
) {
- if (rowHeightsOptions.rowHeights?.[rowIndex] === AUTO_HEIGHT) {
- return true;
- }
-
- if (rowHeightsOptions.defaultHeight === AUTO_HEIGHT) {
+ if (rowHeightsOptions?.rowHeights?.[rowIndex] != null) {
+ if (rowHeightsOptions.rowHeights[rowIndex] === AUTO_HEIGHT) {
+ return true;
+ }
+ } else if (rowHeightsOptions?.defaultHeight === AUTO_HEIGHT) {
return true;
}
@@ -157,8 +175,6 @@ export class RowHeightUtils {
lineHeight: getNumberFromPx(allStyles.lineHeight),
};
document.body.removeChild(this.fakeCell);
- // we need clear the height cache so that it recalculates heights for new styles
- this.clearHeightsCache();
}
getComputedCellStyles() {