diff --git a/CHANGELOG.md b/CHANGELOG.md index 829ee3c3b19..a283dd90bf2 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,6 +1,8 @@ ## [`master`](https://github.com/elastic/eui/tree/master) -No public interface changes since `41.0.0`. +**Bug fixes** + +- Fixed an `EuiDataGrid` race condition where grid rows had incorrect heights if loaded in before CSS ([#5284](https://github.com/elastic/eui/pull/5284)) ## [`41.0.0`](https://github.com/elastic/eui/tree/v41.0.0) diff --git a/src/components/datagrid/__mocks__/row_height_utils.ts b/src/components/datagrid/__mocks__/row_height_utils.ts index 7e4ef01d812..c50c7eff0f5 100644 --- a/src/components/datagrid/__mocks__/row_height_utils.ts +++ b/src/components/datagrid/__mocks__/row_height_utils.ts @@ -6,41 +6,26 @@ * Side Public License, v 1. */ -import { EuiDataGridRowHeightOption } from '../data_grid_types'; +import { RowHeightUtils as ActualRowHeightUtils } from '../row_height_utils'; -import { RowHeightUtils } from '../row_height_utils'; +const actual = new ActualRowHeightUtils(); export const mockRowHeightUtils = ({ - computeStylesForGridCell: jest.fn(), + cacheStyles: jest.fn(), setGrid: jest.fn(), getStylesForCell: jest.fn(() => ({ wordWrap: 'break-word', wordBreak: 'break-word', flexGrow: 1, })), - isDefinedHeight: jest.fn(() => true), isAutoHeight: jest.fn(() => false), setRowHeight: jest.fn(), pruneHiddenColumnHeights: jest.fn(), getRowHeight: jest.fn(() => 32), - getComputedCellStyles: jest.fn(() => {}), - getCalculatedHeight: jest.fn( - (heightOption: EuiDataGridRowHeightOption, defaultHeight: number) => { - if (typeof heightOption === 'object') { - if (heightOption.lineCount) { - return heightOption.lineCount; - } + getRowHeightOption: jest.fn(actual.getRowHeightOption), + getCalculatedHeight: jest.fn(actual.getCalculatedHeight), + getLineCount: jest.fn(actual.getLineCount), + calculateHeightForLineCount: jest.fn(() => 50), +} as unknown) as ActualRowHeightUtils; - if (heightOption.height) { - return heightOption.height; - } - } - - if (heightOption) { - return heightOption; - } - - return defaultHeight; - } - ), -} as unknown) as RowHeightUtils; +export const RowHeightUtils = jest.fn(() => mockRowHeightUtils); diff --git a/src/components/datagrid/_data_grid_data_row.scss b/src/components/datagrid/_data_grid_data_row.scss index 0d5cc1d367f..9cdcf38dfec 100644 --- a/src/components/datagrid/_data_grid_data_row.scss +++ b/src/components/datagrid/_data_grid_data_row.scss @@ -235,20 +235,12 @@ } } -.euiDataGridRowCell--fontSizeSmall { - @include euiFontSizeXS; -} - @include euiDataGridStyles(fontSizeLarge) { @include euiDataGridRowCell { @include euiFontSize; } } -.euiDataGridRowCell--fontSizeLarge { - @include euiFontSize; -} - // Padding alternates @include euiDataGridStyles(paddingSmall) { @include euiDataGridRowCell { @@ -256,28 +248,12 @@ } } -.euiDataGridRowCell--paddingSmall { - padding: $euiDataGridCellPaddingS; - - .euiDataGridRowCell__contentByHeight + .euiDataGridRowCell__expandButton { - padding: 0; - } -} - @include euiDataGridStyles(paddingLarge) { @include euiDataGridRowCell { padding: $euiDataGridCellPaddingL; } } -.euiDataGridRowCell--paddingLarge { - padding: $euiDataGridCellPaddingL; - - .euiDataGridRowCell__contentByHeight + .euiDataGridRowCell__expandButton { - padding: $euiDataGridCellPaddingL 0; - } -} - @keyframes euiDataGridCellButtonSlideIn { from { margin-left: 0; 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 a502481d2e4..8c0bc79f63c 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,13 +10,27 @@ exports[`EuiDataGridCell renders 1`] = ` renderCellValue={[Function]} rowHeightUtils={ Object { - "computeStylesForGridCell": [MockFunction], + "cacheStyles": [MockFunction], + "calculateHeightForLineCount": [MockFunction], "getCalculatedHeight": [MockFunction], - "getComputedCellStyles": [MockFunction], + "getLineCount": [MockFunction], "getRowHeight": [MockFunction], + "getRowHeightOption": [MockFunction] { + "calls": Array [ + Array [ + 0, + undefined, + ], + ], + "results": Array [ + Object { + "type": "return", + "value": undefined, + }, + ], + }, "getStylesForCell": [MockFunction], "isAutoHeight": [MockFunction], - "isDefinedHeight": [MockFunction], "pruneHiddenColumnHeights": [MockFunction], "setGrid": [MockFunction], "setRowHeight": [MockFunction], @@ -57,13 +71,27 @@ exports[`EuiDataGridCell renders 1`] = ` renderCellValue={[Function]} rowHeightUtils={ Object { - "computeStylesForGridCell": [MockFunction], + "cacheStyles": [MockFunction], + "calculateHeightForLineCount": [MockFunction], "getCalculatedHeight": [MockFunction], - "getComputedCellStyles": [MockFunction], + "getLineCount": [MockFunction], "getRowHeight": [MockFunction], + "getRowHeightOption": [MockFunction] { + "calls": Array [ + Array [ + 0, + undefined, + ], + ], + "results": Array [ + Object { + "type": "return", + "value": undefined, + }, + ], + }, "getStylesForCell": [MockFunction], "isAutoHeight": [MockFunction], - "isDefinedHeight": [MockFunction], "pruneHiddenColumnHeights": [MockFunction], "setGrid": [MockFunction], "setRowHeight": [MockFunction], diff --git a/src/components/datagrid/body/data_grid_body.tsx b/src/components/datagrid/body/data_grid_body.tsx index 3c08e68eeb6..601e702f20f 100644 --- a/src/components/datagrid/body/data_grid_body.tsx +++ b/src/components/datagrid/body/data_grid_body.tsx @@ -28,7 +28,7 @@ import { useMutationObserver, } from '../../observer/mutation_observer'; import { useResizeObserver } from '../../observer/resize_observer'; -import { AUTO_HEIGHT } from '../row_height_utils'; +import { DEFAULT_ROW_HEIGHT } from '../row_height_utils'; import { EuiDataGridCell } from './data_grid_cell'; import { DataGridSortingContext, @@ -70,7 +70,6 @@ export const Cell: FunctionComponent = ({ setRowHeight, schemaDetectors, rowHeightsOptions, - getRowHeight, rowHeightUtils, } = data; @@ -119,30 +118,33 @@ export const Cell: FunctionComponent = ({ [`euiDataGridRowCell--${textTransform}`]: textTransform, }); + const sharedCellProps = { + rowIndex, + visibleRowIndex, + colIndex: columnIndex, + interactiveCellId, + className: classes, + style: { + ...style, + top: `${parseFloat(style.top as string) + headerRowHeight}px`, + }, + rowHeightsOptions, + rowHeightUtils, + setRowHeight: isFirstColumn ? setRowHeight : undefined, + }; + if (isLeadingControlColumn) { const leadingColumn = leadingControlColumns[columnIndex]; const { id, rowCellRender } = leadingColumn; cellContent = ( ); } else if (isTrailingControlColumn) { @@ -153,23 +155,12 @@ export const Cell: FunctionComponent = ({ cellContent = ( ); } else { @@ -188,9 +179,7 @@ export const Cell: FunctionComponent = ({ cellContent = ( = ({ renderCellValue={renderCellValue} interactiveCellId={interactiveCellId} isExpandable={isExpandable} - className={classes} - rowHeightsOptions={rowHeightsOptions} - getRowHeight={getRowHeight} - rowHeightUtils={rowHeightUtils} - style={{ - ...style, - top: `${parseFloat(style.top as string) + headerRowHeight}px`, - }} /> ); } @@ -240,7 +221,6 @@ const InnerElement: VariableSizeGridProps['innerElementType'] = forwardRef< }); InnerElement.displayName = 'EuiDataGridInnerElement'; -const INITIAL_ROW_HEIGHT = 34; const IS_JEST_ENVIRONMENT = global.hasOwnProperty('_isJest'); /** @@ -524,45 +504,32 @@ export const EuiDataGridBody: FunctionComponent = ( [rowHeightUtils] ); - const [minRowHeight, setRowHeight] = useState(INITIAL_ROW_HEIGHT); - - const computedCellStyles = rowHeightUtils.getComputedCellStyles(); + const [minRowHeight, setRowHeight] = useState(DEFAULT_ROW_HEIGHT); const defaultHeight = useMemo(() => { - // @ts-ignore we need to re-run this when computedCellStyles changes, - // but it isn't used directly; so let's make the hooks lint rule see - // that it is used, but we need to tell eslint to ignore - // eslint-disable-next-line @typescript-eslint/no-unused-vars - const _computedCellStyles = computedCellStyles; return rowHeightsOptions?.defaultHeight ? rowHeightUtils.getCalculatedHeight( rowHeightsOptions.defaultHeight, minRowHeight ) : minRowHeight; - }, [rowHeightsOptions, minRowHeight, rowHeightUtils, computedCellStyles]); + }, [rowHeightsOptions, minRowHeight, rowHeightUtils]); const getRowHeight = useCallback( (rowIndex) => { const correctRowIndex = getCorrectRowIndex(rowIndex); let height; - if (rowHeightsOptions) { - if (rowHeightsOptions.rowHeights) { - const initialHeight = rowHeightsOptions.rowHeights[correctRowIndex]; - - if (initialHeight) { - height = rowHeightUtils.getCalculatedHeight( - initialHeight, - minRowHeight, - correctRowIndex - ); - } - } - - if (!height && rowHeightsOptions.defaultHeight === AUTO_HEIGHT) { - height = rowHeightUtils.getRowHeight(correctRowIndex); - } + const rowHeightOption = rowHeightUtils.getRowHeightOption( + correctRowIndex, + rowHeightsOptions + ); + if (rowHeightOption) { + height = rowHeightUtils.getCalculatedHeight( + rowHeightOption, + minRowHeight, + correctRowIndex + ); } return height || defaultHeight; @@ -707,7 +674,6 @@ export const EuiDataGridBody: FunctionComponent = ( itemData={{ schemaDetectors, setRowHeight, - getRowHeight, getCorrectRowIndex, rowMap, rowOffset: pagination diff --git a/src/components/datagrid/body/data_grid_cell.test.tsx b/src/components/datagrid/body/data_grid_cell.test.tsx index d837f1a4cfd..ee8aa2fd257 100644 --- a/src/components/datagrid/body/data_grid_cell.test.tsx +++ b/src/components/datagrid/body/data_grid_cell.test.tsx @@ -160,7 +160,18 @@ describe('EuiDataGridCell', () => { }); describe('componentDidUpdate', () => { - describe('recalculateRowHeight', () => { + it('resets cell props when the cell columnId changes', () => { + const setState = jest.spyOn(EuiDataGridCell.prototype, 'setState'); + const component = mountEuiDataGridCellWithContext(); + + component.setProps({ columnId: 'newColumnId' }); + expect(setState).toHaveBeenCalledWith({ cellProps: {} }); + }); + }); + + // TODO: Test ResizeObserver logic in Cypress alongside Jest + describe('row height logic & resize observers', () => { + describe('recalculateAutoHeight', () => { beforeEach(() => { (mockRowHeightUtils.setRowHeight as jest.Mock).mockClear(); }); @@ -176,7 +187,6 @@ describe('EuiDataGridCell', () => { const component = mountEuiDataGridCellWithContext({ rowHeightsOptions: { defaultHeight: 'auto' }, - getRowHeight: jest.fn(() => 50), }); triggerUpdate(component); @@ -188,7 +198,6 @@ describe('EuiDataGridCell', () => { const component = mountEuiDataGridCellWithContext({ rowHeightsOptions: { defaultHeight: 34 }, - getRowHeight: jest.fn(() => 50), }); triggerUpdate(component); @@ -196,17 +205,35 @@ describe('EuiDataGridCell', () => { }); }); - it('resets cell props when the cell columnId changes', () => { - const setState = jest.spyOn(EuiDataGridCell.prototype, 'setState'); - const component = mountEuiDataGridCellWithContext(); + describe('recalculateLineCountHeight', () => { + const setRowHeight = jest.fn(); + beforeEach(() => setRowHeight.mockClear()); - component.setProps({ columnId: 'newColumnId' }); - expect(setState).toHaveBeenCalledWith({ cellProps: {} }); + const callMethod = (component: ReactWrapper) => + (component.instance() as any).recalculateLineCountHeight(); + + it('observes the first cell for size changes and calls this.props.setRowHeight on change', () => { + const component = mountEuiDataGridCellWithContext({ + rowHeightsOptions: { defaultHeight: { lineCount: 3 } }, + setRowHeight, + }); + + callMethod(component); + expect(setRowHeight).toHaveBeenCalled(); + }); + + it('does nothing if cell height is not set to lineCount', () => { + const component = mountEuiDataGridCellWithContext({ + rowHeightsOptions: { defaultHeight: 34 }, + setRowHeight, + }); + + callMethod(component); + expect(setRowHeight).not.toHaveBeenCalled(); + }); }); }); - // TODO: Test ResizeObserver logic in Cypress - // TODO: Test interacting/focus/tabbing in Cypress instead of Jest describe('interactions', () => { describe('keyboard events', () => { diff --git a/src/components/datagrid/body/data_grid_cell.tsx b/src/components/datagrid/body/data_grid_cell.tsx index 0af68a69181..e3b2fb07c5b 100644 --- a/src/components/datagrid/body/data_grid_cell.tsx +++ b/src/components/datagrid/body/data_grid_cell.tsx @@ -62,10 +62,10 @@ const EuiDataGridCellContent: FunctionComponent< { row: rowIndex + 1, col: colIndex + 1 } ); - const isDefinedHeight = - rowHeightUtils && - rowHeightsOptions && - rowHeightUtils.isDefinedHeight(rowIndex, rowHeightsOptions); + const isDefinedHeight = !!rowHeightUtils?.getRowHeightOption( + rowIndex, + rowHeightsOptions + ); return ( <> @@ -114,7 +114,6 @@ export class EuiDataGridCell extends Component< static activeFocusTimeoutId: number | undefined = undefined; cellRef = createRef() as MutableRefObject; - observer!: any; // Cell ResizeObserver contentObserver!: any; // Cell Content ResizeObserver popoverPanelRef: MutableRefObject = createRef(); cellContentsRef: HTMLDivElement | null = null; @@ -130,33 +129,6 @@ export class EuiDataGridCell extends Component< focusTimeout: number | undefined; style = null; - observeHeight = ( - component: HTMLDivElement | null, - setRowHeight?: (rowHeight: number) => void - ) => { - const observer = new (window as any).ResizeObserver(() => { - const rowHeight = component!.getBoundingClientRect().height; - if (setRowHeight) { - setRowHeight(rowHeight); - } - }); - observer.observe(component); - return observer; - }; - - setCellRef = (ref: HTMLDivElement | null) => { - this.cellRef.current = ref; - - // watch the first cell for size changes and use that to re-compute row heights - if (this.props.colIndex === 0 && this.props.visibleRowIndex === 0) { - if (ref && hasResizeObserver) { - this.observer = this.observeHeight(ref, this.props.setRowHeight); - } else if (this.observer) { - this.observer.disconnect(); - } - } - }; - static contextType = DataGridFocusContext; getInteractables = () => { @@ -194,7 +166,7 @@ export class EuiDataGridCell extends Component< } }; - recalculateRowHeight = () => { + recalculateAutoHeight = () => { const { rowHeightUtils, rowHeightsOptions, rowIndex } = this.props; if ( this.cellContentsRef && @@ -213,6 +185,27 @@ export class EuiDataGridCell extends Component< } }; + recalculateLineCountHeight = () => { + if (!this.props.setRowHeight) return; // setRowHeight is only passed by data_grid_body into one cell per row + if (!this.cellContentsRef) return; + + const { rowHeightUtils, rowHeightsOptions, rowIndex } = this.props; + const rowHeightOption = rowHeightUtils?.getRowHeightOption( + rowIndex, + rowHeightsOptions + ); + const lineCount = rowHeightUtils?.getLineCount(rowHeightOption); + + if (lineCount) { + const height = rowHeightUtils!.calculateHeightForLineCount( + this.cellContentsRef, + lineCount + ); + + this.props.setRowHeight(height); + } + }; + componentDidMount() { this.unsubscribeCell = this.context.onFocusUpdate( [this.props.colIndex, this.props.visibleRowIndex], @@ -236,7 +229,7 @@ export class EuiDataGridCell extends Component< } componentDidUpdate(prevProps: EuiDataGridCellProps) { - this.recalculateRowHeight(); + this.recalculateAutoHeight(); if (this.props.columnId !== prevProps.columnId) { this.setCellProps({}); @@ -290,7 +283,11 @@ export class EuiDataGridCell extends Component< setCellContentsRef = (ref: HTMLDivElement | null) => { this.cellContentsRef = ref; if (ref && hasResizeObserver) { - this.contentObserver = this.observeHeight(ref, this.recalculateRowHeight); + this.contentObserver = new (window as any).ResizeObserver(() => { + this.recalculateAutoHeight(); + this.recalculateLineCountHeight(); + }); + this.contentObserver.observe(ref); } else if (this.contentObserver) { this.contentObserver.disconnect(); } @@ -562,7 +559,7 @@ export class EuiDataGridCell extends Component< tabIndex={ this.state.isFocused && !this.state.disableCellTabIndex ? 0 : -1 } - ref={this.setCellRef} + ref={this.cellRef} {...cellProps} data-test-subj="dataGridRowCell" onKeyDown={handleCellKeyDown} diff --git a/src/components/datagrid/body/data_grid_footer_row.tsx b/src/components/datagrid/body/data_grid_footer_row.tsx index b35c4b1958c..71137345001 100644 --- a/src/components/datagrid/body/data_grid_footer_row.tsx +++ b/src/components/datagrid/body/data_grid_footer_row.tsx @@ -40,6 +40,13 @@ const EuiDataGridFooterRow = memo( ); const dataTestSubj = classnames('dataGridRow', _dataTestSubj); + const sharedCellProps = { + rowIndex, + visibleRowIndex, + interactiveCellId, + isExpandable: true, + }; + return (
{leadingControlColumns.map(({ id, width }, i) => ( null} - interactiveCellId={interactiveCellId} - isExpandable={true} className="euiDataGridFooterCell euiDataGridRowCell--controlColumn" /> ))} @@ -74,17 +78,14 @@ const EuiDataGridFooterRow = memo( return ( ); @@ -94,16 +95,13 @@ const EuiDataGridFooterRow = memo( return ( null} - interactiveCellId={interactiveCellId} - isExpandable={true} className="euiDataGridFooterCell euiDataGridRowCell--controlColumn" /> ); diff --git a/src/components/datagrid/data_grid.test.tsx b/src/components/datagrid/data_grid.test.tsx index 3c8e019537f..00a27d220f2 100644 --- a/src/components/datagrid/data_grid.test.tsx +++ b/src/components/datagrid/data_grid.test.tsx @@ -18,11 +18,6 @@ import { EuiDataGridColumnResizer } from './body/header/data_grid_column_resizer import { keys } from '../../services'; import { act } from 'react-dom/test-utils'; -import { mockRowHeightUtils } from './__mocks__/row_height_utils'; -jest.mock('./row_height_utils', () => ({ - RowHeightUtils: jest.fn(() => mockRowHeightUtils), -})); - function getFocusableCell(component: ReactWrapper) { return findTestSubject(component, 'dataGridRowCell').find('[tabIndex=0]'); } @@ -2221,7 +2216,7 @@ describe('EuiDataGrid', () => { const cellHeights = extractRowHeights(component); expect(cellHeights).toEqual({ 0: 70, - 1: 3, + 1: 34, 2: 50, }); }); @@ -2263,7 +2258,7 @@ describe('EuiDataGrid', () => { expect(extractRowHeights(component)).toEqual({ 0: 70, - 1: 3, + 1: 34, 2: 50, }); @@ -2281,7 +2276,7 @@ describe('EuiDataGrid', () => { expect(extractRowHeights(component)).toEqual({ 0: 70, - 1: 3, + 1: 34, 2: 50, }); }); diff --git a/src/components/datagrid/data_grid.tsx b/src/components/datagrid/data_grid.tsx index 3cfc517ef3c..ebf2a51b60c 100644 --- a/src/components/datagrid/data_grid.tsx +++ b/src/components/datagrid/data_grid.tsx @@ -694,19 +694,10 @@ export const EuiDataGrid: FunctionComponent = (props) => { }, [focusedCell, contentRef]); useEffect(() => { - rowHeightUtils.computeStylesForGridCell( - { - cellPadding: gridStyles.cellPadding, - fontSize: gridStyles.fontSize, - }, - rowHeightsOptions?.lineHeight - ); - }, [ - gridStyles.cellPadding, - gridStyles.fontSize, - rowHeightsOptions?.lineHeight, - rowHeightUtils, - ]); + rowHeightUtils.cacheStyles({ + cellPadding: gridStyles.cellPadding, + }); + }, [gridStyles.cellPadding, rowHeightUtils]); const classes = classNames( 'euiDataGrid', diff --git a/src/components/datagrid/row_height_utils.ts b/src/components/datagrid/row_height_utils.ts index 39344cb5efc..8051a23730a 100644 --- a/src/components/datagrid/row_height_utils.ts +++ b/src/components/datagrid/row_height_utils.ts @@ -11,45 +11,31 @@ import type { VariableSizeGrid as Grid } from 'react-window'; import { isObject, isNumber } from '../../services/predicate'; import { EuiDataGridStyleCellPaddings, - EuiDataGridStyleFontSizes, EuiDataGridStyle, EuiDataGridRowHeightOption, EuiDataGridRowHeightsOptions, EuiDataGridColumn, } from './data_grid_types'; -const cellPaddingsToClassMap: Record = { - s: 'euiDataGridRowCell--paddingSmall', - m: '', - l: 'euiDataGridRowCell--paddingLarge', +// TODO: Once JS variables are available, use them here instead of hard-coded maps +const cellPaddingsMap: Record = { + s: 4, + m: 6, + l: 8, }; -const fontSizesToClassMap: Record = { - s: 'euiDataGridRowCell--fontSizeSmall', - m: '', - l: 'euiDataGridRowCell--fontSizeLarge', -}; - -function getNumberFromPx(style?: string) { - return style ? parseInt(style.replace('px', ''), 10) : 0; -} - export const AUTO_HEIGHT = 'auto'; -const DEFAULT_HEIGHT = 32; +export const DEFAULT_ROW_HEIGHT = 34; -// So that we use lineCount options we should know exactly row height which allow to show defined line count. -// For this we should know paddings and line height. Because of this we should compute styles for cell with grid styles export class RowHeightUtils { private styles: { paddingTop: number; paddingBottom: number; - lineHeight: number; } = { paddingTop: 0, paddingBottom: 0, - lineHeight: 1, }; - private fakeCell = document.createElement('div'); + // Used by auto height rows only private heightsCache = new Map>(); private timerId: any; private grid?: Grid; @@ -58,7 +44,7 @@ export class RowHeightUtils { setRowHeight( rowIndex: number, colId: string, - height: number = DEFAULT_HEIGHT, + height: number = DEFAULT_ROW_HEIGHT, visibleRowIndex: number ) { const rowHeights = @@ -129,61 +115,41 @@ export class RowHeightUtils { rowIndex: number, rowHeightsOptions?: EuiDataGridRowHeightsOptions ) { - if (rowHeightsOptions?.rowHeights?.[rowIndex] != null) { - if (rowHeightsOptions.rowHeights[rowIndex] === AUTO_HEIGHT) { - return true; - } - } else if (rowHeightsOptions?.defaultHeight === AUTO_HEIGHT) { - return true; - } + const height = this.getRowHeightOption(rowIndex, rowHeightsOptions); - return false; - } - - isDefinedHeight( - rowIndex: number, - rowHeightsOptions: EuiDataGridRowHeightsOptions - ) { - if ( - rowHeightsOptions.rowHeights?.[rowIndex] || - rowHeightsOptions.defaultHeight - ) { + if (height === AUTO_HEIGHT) { return true; } - return false; } - computeStylesForGridCell( - gridStyles: EuiDataGridStyle, - lineHeight: string | undefined - ) { - this.fakeCell.className = ` - euiDataGridRowCell - ${cellPaddingsToClassMap[gridStyles.cellPadding!]} - ${fontSizesToClassMap[gridStyles.fontSize!]} - `; - - // @ts-ignore it is valid to set `lineHeight` to undefined - this.fakeCell.style.lineHeight = lineHeight; - - document.body.appendChild(this.fakeCell); - const allStyles = getComputedStyle(this.fakeCell); + cacheStyles(gridStyles: EuiDataGridStyle) { this.styles = { - paddingTop: getNumberFromPx(allStyles.paddingTop), - paddingBottom: getNumberFromPx(allStyles.paddingBottom), - lineHeight: getNumberFromPx(allStyles.lineHeight), + paddingTop: cellPaddingsMap[gridStyles.cellPadding!], + paddingBottom: cellPaddingsMap[gridStyles.cellPadding!], }; - document.body.removeChild(this.fakeCell); } - getComputedCellStyles() { - return this.styles; + getRowHeightOption( + rowIndex: number, + rowHeightsOptions?: EuiDataGridRowHeightsOptions + ): EuiDataGridRowHeightOption | undefined { + return ( + rowHeightsOptions?.rowHeights?.[rowIndex] ?? + rowHeightsOptions?.defaultHeight + ); + } + + getLineCount(option?: EuiDataGridRowHeightOption) { + return isObject(option) ? option.lineCount : undefined; } - calculateHeightForLineCount(lineCount: number) { + calculateHeightForLineCount(cellRef: HTMLElement, lineCount: number) { + const computedStyles = window.getComputedStyle(cellRef, null); + const lineHeight = parseInt(computedStyles.lineHeight, 10) || 24; + return Math.ceil( - lineCount * this.styles.lineHeight + + lineCount * lineHeight + this.styles.paddingTop + this.styles.paddingBottom ); @@ -196,9 +162,8 @@ export class RowHeightUtils { ) { if (isObject(heightOption)) { if (heightOption.lineCount) { - return this.calculateHeightForLineCount(heightOption.lineCount); + return defaultHeight; // lineCount height is set in minRowHeight state in grid_row_body } - if (heightOption.height) { return Math.max(heightOption.height, defaultHeight); } @@ -208,7 +173,7 @@ export class RowHeightUtils { return Math.max(heightOption, defaultHeight); } - if (heightOption === AUTO_HEIGHT && rowIndex) { + if (heightOption === AUTO_HEIGHT && rowIndex != null) { return this.getRowHeight(rowIndex); } @@ -219,20 +184,16 @@ export class RowHeightUtils { rowHeightsOptions: EuiDataGridRowHeightsOptions, rowIndex: number ): CSSProperties => { - if (this.isAutoHeight(rowIndex, rowHeightsOptions)) { - return {}; - } - - let initialHeight = - rowHeightsOptions.rowHeights && rowHeightsOptions.rowHeights[rowIndex]; + const height = this.getRowHeightOption(rowIndex, rowHeightsOptions); - if (!initialHeight) { - initialHeight = rowHeightsOptions.defaultHeight; + if (height === AUTO_HEIGHT) { + return {}; } - if (isObject(initialHeight) && initialHeight.lineCount) { + const lineCount = this.getLineCount(height); + if (lineCount) { return { - WebkitLineClamp: initialHeight.lineCount, + WebkitLineClamp: lineCount, display: '-webkit-box', WebkitBoxOrient: 'vertical', height: '100%',