diff --git a/src/components/datagrid/body/cell/data_grid_cell.tsx b/src/components/datagrid/body/cell/data_grid_cell.tsx index e124684e111..1c05480ebb3 100644 --- a/src/components/datagrid/body/cell/data_grid_cell.tsx +++ b/src/components/datagrid/body/cell/data_grid_cell.tsx @@ -15,8 +15,9 @@ import React, { JSXElementConstructor, KeyboardEvent, memo, + useMemo, MutableRefObject, - ReactNode, + ReactElement, } from 'react'; import { createPortal } from 'react-dom'; @@ -48,13 +49,15 @@ import { HandleInteractiveChildren } from './focus_utils'; const EuiDataGridCellContent: FunctionComponent< EuiDataGridCellValueProps & { setCellProps: EuiDataGridCellValueElementProps['setCellProps']; - setCellContentsRef: EuiDataGridCell['setCellContentsRef']; + setCellContentsRef: (ref: HTMLDivElement | null) => void; + showCellActions: boolean; isExpanded: boolean; + onExpandClick: () => void; + popoverAnchorRef: MutableRefObject; isControlColumn: boolean; isFocused: boolean; ariaRowIndex: number; rowHeight?: EuiDataGridRowHeightOption; - cellActions?: ReactNode; } > = memo( ({ @@ -69,93 +72,97 @@ const EuiDataGridCellContent: FunctionComponent< rowHeightUtils, isControlColumn, isFocused, - cellActions, + showCellActions, + onExpandClick, + popoverAnchorRef, ...rest }) => { - // React is more permissible than the TS types indicate + // React is more permissive than the TS types indicate const CellElement = renderCellValue as JSXElementConstructor; - const cellHeightType = - rowHeightUtils?.getHeightType(rowHeight) || 'default'; - - const classes = classNames( - 'euiDataGridRowCell__content', - `euiDataGridRowCell__content--${cellHeightType}Height`, - !isControlColumn && { - 'eui-textBreakWord': cellHeightType !== 'default', - 'eui-textTruncate': cellHeightType === 'default', - } + const cellHeightType = useMemo( + () => rowHeightUtils?.getHeightType(rowHeight) || 'default', + [rowHeightUtils, rowHeight] ); - let cellContent = ( -
- -
- ); - if (cellHeightType === 'lineCount' && !isControlColumn) { - const lines = rowHeightUtils!.getLineCount(rowHeight)!; - cellContent = ( - - {cellContent} - - ); - } - - const screenReaderText = ( - - - + const classes = useMemo( + () => + classNames( + 'euiDataGridRowCell__content', + `euiDataGridRowCell__content--${cellHeightType}Height`, + !isControlColumn && { + 'eui-textBreakWord': cellHeightType !== 'default', + 'eui-textTruncate': cellHeightType === 'default', + } + ), + [cellHeightType, isControlColumn] ); return ( <> - {cellContent} - {screenReaderText} - {cellActions} + +
+ +
+
+ + + + + + {showCellActions && ( + + )} ); } ); EuiDataGridCellContent.displayName = 'EuiDataGridCellContent'; -const RenderCellInRow: FunctionComponent<{ - children: ReactNode; - row?: HTMLElement; -}> = ({ row, children }) => <>{row ? createPortal(children, row) : children}; - export class EuiDataGridCell extends Component< EuiDataGridCellProps, EuiDataGridCellState @@ -613,24 +620,6 @@ export class EuiDataGridCell extends Component< rowHeightsOptions ); - const cellContentProps = { - ...rest, - setCellProps: this.setCellProps, - column, - columnType, - isExpandable, - isExpanded: popoverIsOpen, - isDetails: false, - isFocused: this.state.isFocused, - setCellContentsRef: this.setCellContentsRef, - rowHeight, - rowHeightUtils, - isControlColumn: cellClasses.includes( - 'euiDataGridRowCell--controlColumn' - ), - ariaRowIndex, - }; - const row = rowManager && !IS_JEST_ENVIRONMENT ? rowManager.getRow({ @@ -665,26 +654,25 @@ export class EuiDataGridCell extends Component< renderFocusTrap={!isExpandable} > - - {/* Give the cell expansion popover a separate div/ref - otherwise the - extra popover wrappers mess up the absolute positioning and cause - animation stuttering */} -
- - ) - } + {...rest} + setCellProps={this.setCellProps} + column={column} + columnType={columnType} + isExpandable={isExpandable} + isExpanded={popoverIsOpen} + onExpandClick={this.handleCellExpansionClick} + popoverAnchorRef={this.popoverAnchorRef} + showCellActions={showCellActions} + isFocused={this.state.isFocused} + setCellContentsRef={this.setCellContentsRef} + rowHeight={rowHeight} + rowHeightUtils={rowHeightUtils} + isControlColumn={cellClasses.includes( + 'euiDataGridRowCell--controlColumn' + )} + ariaRowIndex={ariaRowIndex} + rowIndex={rowIndex} + colIndex={colIndex} />
@@ -692,3 +680,36 @@ export class EuiDataGridCell extends Component< ); } } + +/** + * Function component utilities for conditional rendering. + * Used for DRYness and performance + */ + +const RenderCellInRow: FunctionComponent<{ + children: ReactElement; + row?: HTMLElement; +}> = memo(({ row, children }) => + row ? createPortal(children, row) : children +); +RenderCellInRow.displayName = 'RenderCellInRow'; + +const RenderTruncatedCellContent: FunctionComponent<{ + children: ReactElement; + hasLineCountTruncation: boolean; + rowHeight?: EuiDataGridRowHeightOption; +}> = memo(({ children, hasLineCountTruncation, rowHeight }) => { + // If `hasLineCountTruncation` is true, we can rely on rowHeight being the correct type + const lines = hasLineCountTruncation + ? (rowHeight as { lineCount: number }).lineCount + : undefined; + + return lines ? ( + + {children} + + ) : ( + children + ); +}); +RenderTruncatedCellContent.displayName = 'RenderTruncatedCellContent'; diff --git a/src/components/datagrid/body/cell/data_grid_cell_actions.test.tsx b/src/components/datagrid/body/cell/data_grid_cell_actions.test.tsx index fe16e085dd9..f17711d7d3e 100644 --- a/src/components/datagrid/body/cell/data_grid_cell_actions.test.tsx +++ b/src/components/datagrid/body/cell/data_grid_cell_actions.test.tsx @@ -22,6 +22,7 @@ const MockAction: EuiDataGridColumnCellAction = ({ Component }) => ( describe('EuiDataGridCellActions', () => { const requiredProps = { onExpandClick: jest.fn(), + popoverAnchorRef: () => {}, rowIndex: 0, colIndex: 0, cellHeightType: 'default', @@ -31,17 +32,22 @@ describe('EuiDataGridCellActions', () => { const component = shallow(); expect(component).toMatchInlineSnapshot(` -
- +
- - -
+ + + +
+
+ `); const button: Function = component.find('EuiI18n').renderProp('children'); @@ -68,7 +74,7 @@ describe('EuiDataGridCellActions', () => { /> ); - const button = component.childAt(0).renderProp('Component'); + const button = component.childAt(0).childAt(0).renderProp('Component'); expect(button({ iconType: 'eye' })).toMatchInlineSnapshot(` { ); expect(component).toMatchInlineSnapshot(` -
- - +
- - -
+ + + + +
+
+ `); }); diff --git a/src/components/datagrid/body/cell/data_grid_cell_actions.tsx b/src/components/datagrid/body/cell/data_grid_cell_actions.tsx index f21b4ddc618..3f278e1f9a0 100644 --- a/src/components/datagrid/body/cell/data_grid_cell_actions.tsx +++ b/src/components/datagrid/body/cell/data_grid_cell_actions.tsx @@ -6,7 +6,7 @@ * Side Public License, v 1. */ -import React, { JSXElementConstructor, useMemo, useCallback } from 'react'; +import React, { JSXElementConstructor, Ref, useMemo, useCallback } from 'react'; import { EuiDataGridColumn, EuiDataGridColumnCellAction, @@ -24,11 +24,13 @@ import { EuiPopoverFooter } from '../../../popover'; export const EuiDataGridCellActions = ({ onExpandClick, + popoverAnchorRef, column, rowIndex, colIndex, }: { onExpandClick: () => void; + popoverAnchorRef: Ref; column?: EuiDataGridColumn; rowIndex: number; colIndex: number; @@ -102,9 +104,15 @@ export const EuiDataGridCellActions = ({ }, [column, colIndex, rowIndex]); return ( -
- {[...additionalButtons, expandButton]} -
+ <> +
+ {[...additionalButtons, expandButton]} +
+ {/* The cell expansion popover needs a separate div/ref - otherwise the + extra popover wrappers mess up the absolute positioning and cause + animation stuttering on the cell actions */} +
+ ); };