From b0d616ff0a2a5567bbc8ca9844be96bdc24cfec5 Mon Sep 17 00:00:00 2001 From: Chandler Prall Date: Thu, 22 Oct 2020 10:12:16 -0600 Subject: [PATCH 01/54] in-progress virtualization --- src-docs/src/views/datagrid/datagrid.js | 76 ++- src/components/datagrid/column_selector.tsx | 47 +- src/components/datagrid/data_grid.tsx | 388 +++++++------- src/components/datagrid/data_grid_body.tsx | 474 +++++++++++++++--- src/components/datagrid/data_grid_cell.tsx | 49 +- .../datagrid/data_grid_cell_popover.tsx | 4 +- src/components/datagrid/data_grid_context.tsx | 18 +- .../data_grid_control_header_cell.tsx | 32 +- .../datagrid/data_grid_footer_row.tsx | 10 - .../datagrid/data_grid_header_cell.tsx | 51 +- .../datagrid/data_grid_header_row.tsx | 15 - src/components/datagrid/data_grid_schema.tsx | 8 +- 12 files changed, 783 insertions(+), 389 deletions(-) diff --git a/src-docs/src/views/datagrid/datagrid.js b/src-docs/src/views/datagrid/datagrid.js index 7024a855e75..4fcb28da6b7 100644 --- a/src-docs/src/views/datagrid/datagrid.js +++ b/src-docs/src/views/datagrid/datagrid.js @@ -6,6 +6,7 @@ import React, { useState, createContext, useContext, + useRef, } from 'react'; import { fake } from 'faker'; @@ -158,6 +159,70 @@ const columns = [ }, ]; +const leadingControlColumns = [ + { + id: 'actions', + width: 40, + headerCellRender: () => null, + rowCellRender: function RowCellRender() { + const [isPopoverOpen, setIsPopoverOpen] = useState(false); + return ( +
+ setIsPopoverOpen(!isPopoverOpen)} + /> + } + closePopover={() => setIsPopoverOpen(false)} + ownFocus={true}> + Actions +
+ + + +
+
+
+ ); + }, + }, +]; + const trailingControlColumns = [ { id: 'actions', @@ -183,7 +248,7 @@ const trailingControlColumns = [ ownFocus={true}> Actions
-
- - - -
-
-
+
+
+ B +
+
+
+ +
+
+
- -
- -
-
+
-

- - Row: 1, Column: 1: - -

- 0, A + class="euiDataGridRowCell__expandFlex" + > +
+

+ + Row: 1, Column: 1: + +

+
+ 0, A +
+
+
+ +
-
- -
-
-
-

- - Row: 1, Column: 2: - -

- 0, B + class="euiDataGridRowCell__expandFlex" + > +
+

+ + Row: 1, Column: 2: + +

+
+ 0, B +
+
+
+ +
-
- -
-
- -
-
-

- - Row: 2, Column: 1: - -

- 1, A + class="euiDataGridRowCell__expandFlex" + > +
+

+ + Row: 2, Column: 1: + +

+
+ 1, A +
+
+
+ +
-
- -
-
-
-

- - Row: 2, Column: 2: - -

- 1, B + class="euiDataGridRowCell__expandFlex" + > +
+

+ + Row: 2, Column: 2: + +

+
+ 1, B +
+
+
+ +
-
- -
-
-
-
-
-

- - Row: 3, Column: 1: - -

- 2, A + class="euiDataGridRowCell__expandFlex" + > +
+

+ + Row: 3, Column: 1: + +

+
+ 2, A +
+
+
+ +
-
- -
-
-
-

- - Row: 3, Column: 2: - -

- 2, B + class="euiDataGridRowCell__expandFlex" + > +
+

+ + Row: 3, Column: 2: + +

+
+ 2, B +
+
+
+ +
-
- -
@@ -1521,6 +1535,7 @@ Array [ >
-
- - leading heading - -
-
-
- -
-
-
-
-
- -
-
-
- - trailing heading - -
-
-
-
-
-
-
-
-
-
-

- - Row: 1, Column: 1: - -

+ A +
- 0 +
+ +
-
+
-
-
-
-
-
+ +
+ +
+
+
+ + trailing heading +
-
-
+
+
- Row: 1, Column: 3: + Row: 1, Column: 1:

- 0, B + 0
-
- -
+
-
-
-
-
-

- - Row: 1, Column: 4: - -

- 0 +
+
+
+

+ + Row: 1, Column: 2: + +

+
+ 0, A +
+
+
+ +
+
+
-
-
-
-
-
-
-
-

- - Row: 2, Column: 1: - -

- 1 +
+
+
+

+ + Row: 1, Column: 3: + +

+
+ 0, B +
+
+
+ +
+
+
-
-
-
+
+
- Row: 2, Column: 2: + Row: 1, Column: 4:

- 1, A + 0
-
- -
+
-
-
+
+
- Row: 2, Column: 3: + Row: 2, Column: 1:

- 1, B + 1
-
- -
+
-
-
-
-
-

- - Row: 2, Column: 4: - -

- 1 +
+
+
+

+ + Row: 2, Column: 2: + +

+
+ 1, A +
+
+
+ +
+
+
-
-
-
-
-
-
-
-

- - Row: 3, Column: 1: - -

- 2 +
+
+
+

+ + Row: 2, Column: 3: + +

+
+ 1, B +
+
+
+ +
+
+
-
-
-
+
+
- Row: 3, Column: 2: + Row: 2, Column: 4:

- 2, A + 1
-
- -
+
-
-
+
+
- Row: 3, Column: 3: + Row: 3, Column: 1:

- 2, B + 2
+
+
+
+
+
+
+
+
+
- +
+
+

+ + Row: 3, Column: 2: + +

+
+ 2, A +
+
+
+ +
+
-
-
-
-
-

- - Row: 3, Column: 4: - -

- 2 +
+
+
+

+ + Row: 3, Column: 3: + +

+
+ 2, B +
+
+
+ +
+
+
+ style="position:absolute;left:250px;top:102px;height:34px;width:50px" + > +
+
+
+
+
+
+

+ + Row: 3, Column: 4: + +

+
+ 2 +
+
+
+
+
+
+
@@ -2407,6 +2459,7 @@ Array [ >
-
-
- -
-
-
-
+
+
+
+ More Elements +
+
+
+
+ +
+
+
- -
-
-
-
+
-

- - Row: 1, Column: 1: - -

- 0, A + class="euiDataGridRowCell__expandFlex" + > +
+

+ + Row: 1, Column: 1: + +

+
+ 0, A +
+
+
+ +
-
- -
-
-
-

- - Row: 1, Column: 2: - -

- 0, B + class="euiDataGridRowCell__expandFlex" + > +
+

+ + Row: 1, Column: 2: + +

+
+ 0, B +
+
+
+ +
-
- -
-
-
-
-
-

- - Row: 2, Column: 1: - -

- 1, A + class="euiDataGridRowCell__expandFlex" + > +
+

+ + Row: 2, Column: 1: + +

+
+ 1, A +
+
+
+ +
-
- -
-
-
-

- - Row: 2, Column: 2: - -

- 1, B + class="euiDataGridRowCell__expandFlex" + > +
+

+ + Row: 2, Column: 2: + +

+
+ 1, B +
+
+
+ +
-
- -
-
-
-
-
-

- - Row: 3, Column: 1: - -

- 2, A + class="euiDataGridRowCell__expandFlex" + > +
+

+ + Row: 3, Column: 1: + +

+
+ 2, A +
+
+
+ +
-
- -
-
-
-

- - Row: 3, Column: 2: - -

- 2, B + class="euiDataGridRowCell__expandFlex" + > +
+

+ + Row: 3, Column: 2: + +

+
+ 2, B +
+
+
+ +
-
- -
@@ -2980,6 +3046,7 @@ Array [ >
-
-
- -
-
-
-
+
+
+ B +
+
+
+ +
+
+
- -
-
-
-
+
-

- - Row: 1, Column: 1: - -

- 0, A + class="euiDataGridRowCell__expandFlex" + > +
+

+ + Row: 1, Column: 1: + +

+
+ 0, A +
+
+
+ +
-
- -
-
-
-

- - Row: 1, Column: 2: - -

- 0, B + class="euiDataGridRowCell__expandFlex" + > +
+

+ + Row: 1, Column: 2: + +

+
+ 0, B +
+
+
+ +
-
- -
-
-
-
-
-

- - Row: 2, Column: 1: - -

- 1, A + class="euiDataGridRowCell__expandFlex" + > +
+

+ + Row: 2, Column: 1: + +

+
+ 1, A +
+
+
+ +
-
- -
-
-
-

- - Row: 2, Column: 2: - -

- 1, B + class="euiDataGridRowCell__expandFlex" + > +
+

+ + Row: 2, Column: 2: + +

+
+ 1, B +
+
+
+ +
-
- -
-
-
-
-
-

- - Row: 3, Column: 1: - -

- 2, A + class="euiDataGridRowCell__expandFlex" + > +
+

+ + Row: 3, Column: 1: + +

+
+ 2, A +
+
+
+ +
-
- -
-
-
-

- - Row: 3, Column: 2: - -

- 2, B + class="euiDataGridRowCell__expandFlex" + > +
+

+ + Row: 3, Column: 2: + +

+
+ 2, B +
+
+
+ +
-
- -
diff --git a/src/components/datagrid/data_grid.test.tsx b/src/components/datagrid/data_grid.test.tsx index e99075b2378..d92918a489b 100644 --- a/src/components/datagrid/data_grid.test.tsx +++ b/src/components/datagrid/data_grid.test.tsx @@ -548,61 +548,61 @@ describe('EuiDataGrid', () => { return props; }) ).toMatchInlineSnapshot(` -Array [ - Object { - "className": "euiDataGridRowCell customClass", - "data-test-subj": "dataGridRowCell", - "onBlur": [Function], - "onFocus": [Function], - "onKeyDown": [Function], - "role": "gridcell", - "style": Object { - "color": "red", - "width": "100px", - }, - "tabIndex": -1, - }, - Object { - "className": "euiDataGridRowCell customClass", - "data-test-subj": "dataGridRowCell", - "onBlur": [Function], - "onFocus": [Function], - "onKeyDown": [Function], - "role": "gridcell", - "style": Object { - "color": "blue", - "width": "100px", - }, - "tabIndex": -1, - }, - Object { - "className": "euiDataGridRowCell customClass", - "data-test-subj": "dataGridRowCell", - "onBlur": [Function], - "onFocus": [Function], - "onKeyDown": [Function], - "role": "gridcell", - "style": Object { - "color": "red", - "width": "100px", - }, - "tabIndex": -1, - }, - Object { - "className": "euiDataGridRowCell customClass", - "data-test-subj": "dataGridRowCell", - "onBlur": [Function], - "onFocus": [Function], - "onKeyDown": [Function], - "role": "gridcell", - "style": Object { - "color": "blue", - "width": "100px", - }, - "tabIndex": -1, - }, -] -`); + Array [ + Object { + "className": "euiDataGridRowCell customClass", + "data-test-subj": "dataGridRowCell", + "onBlur": [Function], + "onFocus": [Function], + "onKeyDown": [Function], + "role": "gridcell", + "style": Object { + "color": "red", + "width": "100px", + }, + "tabIndex": -1, + }, + Object { + "className": "euiDataGridRowCell customClass", + "data-test-subj": "dataGridRowCell", + "onBlur": [Function], + "onFocus": [Function], + "onKeyDown": [Function], + "role": "gridcell", + "style": Object { + "color": "blue", + "width": "100px", + }, + "tabIndex": -1, + }, + Object { + "className": "euiDataGridRowCell customClass", + "data-test-subj": "dataGridRowCell", + "onBlur": [Function], + "onFocus": [Function], + "onKeyDown": [Function], + "role": "gridcell", + "style": Object { + "color": "red", + "width": "100px", + }, + "tabIndex": -1, + }, + Object { + "className": "euiDataGridRowCell customClass", + "data-test-subj": "dataGridRowCell", + "onBlur": [Function], + "onFocus": [Function], + "onKeyDown": [Function], + "role": "gridcell", + "style": Object { + "color": "blue", + "width": "100px", + }, + "tabIndex": -1, + }, + ] + `); }); it('renders correct aria attributes on column headers', () => { @@ -791,15 +791,15 @@ Array [ .find('[className*="euiDataGridRowCell--"]') .map((x) => x.props().className); expect(gridCellClassNames).toMatchInlineSnapshot(` -Array [ - "euiDataGridRowCell euiDataGridRowCell--numeric", - "euiDataGridRowCell euiDataGridRowCell--customFormatName", - "euiDataGridRowCell euiDataGridRowCell--numeric", - "euiDataGridRowCell euiDataGridRowCell--customFormatName", - "euiDataGridRowCell euiDataGridRowCell--numeric", - "euiDataGridRowCell euiDataGridRowCell--customFormatName", -] -`); + Array [ + "euiDataGridRowCell euiDataGridRowCell--numeric", + "euiDataGridRowCell euiDataGridRowCell--customFormatName", + "euiDataGridRowCell euiDataGridRowCell--numeric", + "euiDataGridRowCell euiDataGridRowCell--customFormatName", + "euiDataGridRowCell euiDataGridRowCell--numeric", + "euiDataGridRowCell euiDataGridRowCell--customFormatName", + ] + `); }); it('automatically detects column types and applies classnames', () => { @@ -829,15 +829,15 @@ Array [ .find('[className~="euiDataGridRowCell"]') .map((x) => x.props().className); expect(gridCellClassNames).toMatchInlineSnapshot(` -Array [ - "euiDataGridRowCell euiDataGridRowCell--numeric", - "euiDataGridRowCell euiDataGridRowCell--boolean", - "euiDataGridRowCell", - "euiDataGridRowCell euiDataGridRowCell--numeric", - "euiDataGridRowCell euiDataGridRowCell--boolean", - "euiDataGridRowCell", -] -`); + Array [ + "euiDataGridRowCell euiDataGridRowCell--numeric", + "euiDataGridRowCell euiDataGridRowCell--boolean", + "euiDataGridRowCell", + "euiDataGridRowCell euiDataGridRowCell--numeric", + "euiDataGridRowCell euiDataGridRowCell--boolean", + "euiDataGridRowCell", + ] + `); }); it('overrides automatically detected column types with supplied schema', () => { @@ -861,13 +861,13 @@ Array [ .find('[className~="euiDataGridRowCell"]') .map((x) => x.props().className); expect(gridCellClassNames).toMatchInlineSnapshot(` -Array [ - "euiDataGridRowCell euiDataGridRowCell--numeric", - "euiDataGridRowCell euiDataGridRowCell--alphanumeric", - "euiDataGridRowCell euiDataGridRowCell--numeric", - "euiDataGridRowCell euiDataGridRowCell--alphanumeric", -] -`); + Array [ + "euiDataGridRowCell euiDataGridRowCell--numeric", + "euiDataGridRowCell euiDataGridRowCell--alphanumeric", + "euiDataGridRowCell euiDataGridRowCell--numeric", + "euiDataGridRowCell euiDataGridRowCell--alphanumeric", + ] + `); }); it('detects all of the supported types', () => { @@ -898,16 +898,15 @@ Array [ .find('[className~="euiDataGridRowCell"]') .map((x) => x.props().className); expect(gridCellClassNames).toMatchInlineSnapshot(` -Array [ - "euiDataGridRowCell euiDataGridRowCell--numeric", - "euiDataGridRowCell euiDataGridRowCell--boolean", - "euiDataGridRowCell euiDataGridRowCell--currency", - "euiDataGridRowCell euiDataGridRowCell--datetime", - "euiDataGridRowCell euiDataGridRowCell--datetime", - "euiDataGridRowCell euiDataGridRowCell--datetime", - "euiDataGridRowCell euiDataGridRowCell--datetime", -] -`); + Array [ + "euiDataGridRowCell euiDataGridRowCell--numeric", + "euiDataGridRowCell euiDataGridRowCell--boolean", + "euiDataGridRowCell euiDataGridRowCell--currency", + "euiDataGridRowCell euiDataGridRowCell--datetime", + "euiDataGridRowCell euiDataGridRowCell--datetime", + "euiDataGridRowCell euiDataGridRowCell--datetime", + ] + `); }); it('accepts extra detectors', () => { @@ -947,11 +946,11 @@ Array [ .find('[className~="euiDataGridRowCell"]') .map((x) => x.props().className); expect(gridCellClassNames).toMatchInlineSnapshot(` -Array [ - "euiDataGridRowCell euiDataGridRowCell--numeric", - "euiDataGridRowCell euiDataGridRowCell--ipaddress", -] -`); + Array [ + "euiDataGridRowCell euiDataGridRowCell--numeric", + "euiDataGridRowCell euiDataGridRowCell--ipaddress", + ] + `); }); }); }); @@ -974,21 +973,13 @@ Array [ /> ); expect(extractGridData(component)).toMatchInlineSnapshot(` -Array [ - Array [ - "Column 1", - "Column 2", - ], - Array [ - "Hello, Row 0-Column 1!", - "Hello, Row 0-Column 2!", - ], - Array [ - "Hello, Row 1-Column 1!", - "Hello, Row 1-Column 2!", - ], -] -`); + Array [ + Array [ + "Column 1", + "Column 2", + ], + ] + `); }); }); diff --git a/src/components/datagrid/data_grid_cell.tsx b/src/components/datagrid/data_grid_cell.tsx index ae82db297ec..fdc21e9a95b 100644 --- a/src/components/datagrid/data_grid_cell.tsx +++ b/src/components/datagrid/data_grid_cell.tsx @@ -125,6 +125,9 @@ const EuiDataGridCellContent: FunctionComponent< ); }); +const hasResizeObserver = + typeof window !== 'undefined' && typeof window.ResizeObserver !== 'undefined'; + export class EuiDataGridCell extends Component< EuiDataGridCellProps, EuiDataGridCellState @@ -147,7 +150,7 @@ export class EuiDataGridCell extends Component< // 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) { + if (ref && hasResizeObserver) { this.observer = new window.ResizeObserver(() => { const rowHeight = this.cellRef.current!.getBoundingClientRect() .height; @@ -156,7 +159,7 @@ export class EuiDataGridCell extends Component< } }); this.observer.observe(ref); - } else { + } else if (this.observer) { this.observer.disconnect(); } } From bef507301c137df69ea8830309c457c939af8939 Mon Sep 17 00:00:00 2001 From: Dave Snider Date: Wed, 2 Dec 2020 08:47:47 -0800 Subject: [PATCH 12/54] grid cells styling fixes --- src/components/datagrid/_data_grid.scss | 9 +++++---- src/components/datagrid/data_grid_body.tsx | 23 +++++++++++++--------- src/components/datagrid/data_grid_cell.tsx | 9 ++++----- 3 files changed, 23 insertions(+), 18 deletions(-) diff --git a/src/components/datagrid/_data_grid.scss b/src/components/datagrid/_data_grid.scss index ece9c84cb8f..604414ab0e2 100644 --- a/src/components/datagrid/_data_grid.scss +++ b/src/components/datagrid/_data_grid.scss @@ -28,12 +28,8 @@ } .euiDataGrid__content { - @include euiScrollBar; - height: 100%; - overflow: auto; font-feature-settings: 'tnum' 1; // Tabular numbers - scroll-padding: 0; max-width: 100%; width: 100%; z-index: 2; // Sits above the pagination below it, but below the controls above it @@ -117,3 +113,8 @@ .euiDataGrid__focusWrap { height: 100%; } + +.euiDataGrid__virtualized { + @include euiScrollBar; + scroll-padding: 0; +} diff --git a/src/components/datagrid/data_grid_body.tsx b/src/components/datagrid/data_grid_body.tsx index 90855540a55..dc52ffa4d97 100644 --- a/src/components/datagrid/data_grid_body.tsx +++ b/src/components/datagrid/data_grid_body.tsx @@ -163,6 +163,10 @@ const Cell: FunctionComponent = ({ isExpandable={false} className="euiDataGridRowCell--controlColumn" setRowHeight={setRowHeight} + style={{ + ...style, + top: `${parseFloat(style.top as string) + HEADER_ROW_HEIGHT}px`, + }} /> ); } else if (columnIndex >= leadingControlColumns.length + columns.length) { @@ -184,6 +188,10 @@ const Cell: FunctionComponent = ({ interactiveCellId={interactiveCellId} isExpandable={false} className="euiDataGridRowCell--controlColumn" + style={{ + ...style, + top: `${parseFloat(style.top as string) + HEADER_ROW_HEIGHT}px`, + }} /> ); } else { @@ -216,19 +224,15 @@ const Cell: FunctionComponent = ({ renderCellValue={renderCellValue} interactiveCellId={interactiveCellId} isExpandable={isExpandable} + style={{ + ...style, + top: `${parseFloat(style.top as string) + HEADER_ROW_HEIGHT}px`, + }} /> ); } - return ( -
- {cellContent} -
- ); + return cellContent; }; const INITIAL_ROW_HEIGHT = 34; @@ -481,6 +485,7 @@ export const EuiDataGridBody: FunctionComponent = ( | ((props: EuiDataGridCellValueElementProps) => ReactNode); setRowHeight?: (height: number) => void; + style: any; } interface EuiDataGridCellState { @@ -144,6 +145,7 @@ export class EuiDataGridCell extends Component< disableCellTabIndex: false, }; unsubscribeCell?: Function = () => {}; + style = null; setCellRef = (ref: HTMLDivElement | null) => { this.cellRef.current = ref; @@ -310,6 +312,7 @@ export class EuiDataGridCell extends Component< columnType, className, column, + style, ...rest } = this.props; const { colIndex, rowIndex } = rest; @@ -332,11 +335,7 @@ export class EuiDataGridCell extends Component< }; const widthStyle = width != null ? { width: `${width}px` } : {}; - if (cellProps.hasOwnProperty('style')) { - cellProps.style = { ...cellProps.style, ...widthStyle }; - } else { - cellProps.style = widthStyle; - } + cellProps.style = { ...style, widthStyle }; const handleCellKeyDown = (event: KeyboardEvent) => { if (isExpandable) { From 1449d0288265fab305de697e5b7112722788a298 Mon Sep 17 00:00:00 2001 From: Dave Snider Date: Thu, 3 Dec 2020 13:47:58 -0800 Subject: [PATCH 13/54] fix borders and stripes --- .../datagrid/_data_grid_data_row.scss | 8 ++--- src/components/datagrid/data_grid_body.tsx | 35 +++++++++++++++---- 2 files changed, 33 insertions(+), 10 deletions(-) diff --git a/src/components/datagrid/_data_grid_data_row.scss b/src/components/datagrid/_data_grid_data_row.scss index a30c1d904f3..a70a89f09b4 100644 --- a/src/components/datagrid/_data_grid_data_row.scss +++ b/src/components/datagrid/_data_grid_data_row.scss @@ -21,11 +21,11 @@ width: 100%; } - &:first-of-type { + &.euiDataGridRowCell--firstColumn { border-left: $euiBorderThin; } - &:last-of-type { + &.euiDataGridRowCell--lastColumn { border-right-color: $euiBorderColor; } @@ -159,8 +159,8 @@ // Stripes @include euiDataGridStyles(stripes) { - .euiDataGridRow:nth-child(odd) { - @include euiDataGridRowCell { + @include euiDataGridRowCell { + &.euiDataGridRowCell--stripe { background: $euiColorLightestShade; } } diff --git a/src/components/datagrid/data_grid_body.tsx b/src/components/datagrid/data_grid_body.tsx index dc52ffa4d97..cb091df81f3 100644 --- a/src/components/datagrid/data_grid_body.tsx +++ b/src/components/datagrid/data_grid_body.tsx @@ -27,6 +27,7 @@ import React, { useContext, useState, } from 'react'; +import classNames from 'classnames'; import { GridChildComponentProps, VariableSizeGrid as Grid, @@ -145,8 +146,30 @@ const Cell: FunctionComponent = ({ let cellContent; - if (columnIndex < leadingControlColumns.length) { - // this is a leading control column + const isFirstColumn = columnIndex === 0; + const isLastColumn = + columnIndex === + columns.length + + leadingControlColumns.length + + trailingControlColumns.length - + 1; + const isStripableRow = rowIndex % 2 !== 0; + + const isLeadingControlColumn = columnIndex < leadingControlColumns.length; + const isTrailingControlColumn = + columnIndex >= leadingControlColumns.length + columns.length; + + console.log(rowIndex, columnIndex); + + const classes = classNames({ + 'euiDataGridRowCell--stripe': isStripableRow, + 'euiDataGridRowCell--firstColumn': isFirstColumn, + 'euiDataGridRowCell--lastColumn': isLastColumn, + 'euiDataGridRowCell--controlColumn': + isLeadingControlColumn || isTrailingControlColumn, + }); + + if (isLeadingControlColumn) { const leadingColumn = leadingControlColumns[columnIndex]; const { id, rowCellRender } = leadingColumn; @@ -161,7 +184,7 @@ const Cell: FunctionComponent = ({ renderCellValue={rowCellRender} interactiveCellId={interactiveCellId} isExpandable={false} - className="euiDataGridRowCell--controlColumn" + className={classes} setRowHeight={setRowHeight} style={{ ...style, @@ -169,8 +192,7 @@ const Cell: FunctionComponent = ({ }} /> ); - } else if (columnIndex >= leadingControlColumns.length + columns.length) { - // this is a trailing control column + } else if (isTrailingControlColumn) { const columnOffset = columns.length + leadingControlColumns.length; const trailingColumnIndex = columnIndex - columnOffset; const trailingColumn = trailingControlColumns[trailingColumnIndex]; @@ -187,7 +209,7 @@ const Cell: FunctionComponent = ({ renderCellValue={rowCellRender} interactiveCellId={interactiveCellId} isExpandable={false} - className="euiDataGridRowCell--controlColumn" + className={classes} style={{ ...style, top: `${parseFloat(style.top as string) + HEADER_ROW_HEIGHT}px`, @@ -224,6 +246,7 @@ const Cell: FunctionComponent = ({ renderCellValue={renderCellValue} interactiveCellId={interactiveCellId} isExpandable={isExpandable} + className={classes} style={{ ...style, top: `${parseFloat(style.top as string) + HEADER_ROW_HEIGHT}px`, From 300801e4fb471caf16ce1b23a84a123c5be0b53d Mon Sep 17 00:00:00 2001 From: Dave Snider Date: Thu, 3 Dec 2020 13:57:22 -0800 Subject: [PATCH 14/54] remove data_row, since it's no longer in use --- .../datagrid/data_grid_data_row.tsx | 167 ------------------ src/components/datagrid/index.ts | 1 - 2 files changed, 168 deletions(-) delete mode 100644 src/components/datagrid/data_grid_data_row.tsx diff --git a/src/components/datagrid/data_grid_data_row.tsx b/src/components/datagrid/data_grid_data_row.tsx deleted file mode 100644 index a29b19fb9c3..00000000000 --- a/src/components/datagrid/data_grid_data_row.tsx +++ /dev/null @@ -1,167 +0,0 @@ -/* - * Licensed to Elasticsearch B.V. under one or more contributor - * license agreements. See the NOTICE file distributed with - * this work for additional information regarding copyright - * ownership. Elasticsearch B.V. licenses this file to you under - * the Apache License, Version 2.0 (the "License"); you may - * not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, - * software distributed under the License is distributed on an - * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY - * KIND, either express or implied. See the License for the - * specific language governing permissions and limitations - * under the License. - */ - -import React, { FunctionComponent, HTMLAttributes, memo } from 'react'; -import classnames from 'classnames'; -import { - EuiDataGridControlColumn, - EuiDataGridColumn, - EuiDataGridColumnWidths, - EuiDataGridPopoverContent, - EuiDataGridPopoverContents, -} from './data_grid_types'; -import { CommonProps } from '../common'; - -import { EuiDataGridCell, EuiDataGridCellProps } from './data_grid_cell'; -import { EuiDataGridSchema } from './data_grid_schema'; -import { EuiText } from '../text'; - -export type EuiDataGridDataRowProps = CommonProps & - HTMLAttributes & { - rowIndex: number; - leadingControlColumns: EuiDataGridControlColumn[]; - trailingControlColumns: EuiDataGridControlColumn[]; - columns: EuiDataGridColumn[]; - schema: EuiDataGridSchema; - popoverContents: EuiDataGridPopoverContents; - columnWidths: EuiDataGridColumnWidths; - defaultColumnWidth?: number | null; - focusedCellPositionInTheRow?: number | null; - renderCellValue: EuiDataGridCellProps['renderCellValue']; - onCellFocus: Function; - interactiveCellId: EuiDataGridCellProps['interactiveCellId']; - visibleRowIndex: number; - }; - -const DefaultColumnFormatter: EuiDataGridPopoverContent = ({ children }) => { - return {children}; -}; - -const EuiDataGridDataRow: FunctionComponent = memo( - (props) => { - const { - leadingControlColumns, - trailingControlColumns, - columns, - schema, - popoverContents, - columnWidths, - defaultColumnWidth, - className, - renderCellValue, - rowIndex, - focusedCellPositionInTheRow, - onCellFocus, - interactiveCellId, - 'data-test-subj': _dataTestSubj, - visibleRowIndex, - ...rest - } = props; - - const classes = classnames('euiDataGridRow', className); - const dataTestSubj = classnames('dataGridRow', _dataTestSubj); - - return ( -
- {leadingControlColumns.map((leadingColumn, i) => { - const { id, rowCellRender } = leadingColumn; - - return ( - - ); - })} - {columns.map((props, i) => { - const { id } = props; - const columnType = schema[id] ? schema[id].columnType : null; - - const isExpandable = - props.isExpandable !== undefined ? props.isExpandable : true; - const popoverContent = - popoverContents[columnType as string] || DefaultColumnFormatter; - - const width = columnWidths[id] || defaultColumnWidth; - const columnPosition = i + leadingControlColumns.length; - - return ( - - ); - })} - {trailingControlColumns.map((trailingColumn, i) => { - const { id, rowCellRender } = trailingColumn; - const colIndex = i + columns.length + leadingControlColumns.length; - - return ( - - ); - })} -
- ); - } -); - -export { EuiDataGridDataRow }; diff --git a/src/components/datagrid/index.ts b/src/components/datagrid/index.ts index 2ceffd67b49..8f09f373cd8 100644 --- a/src/components/datagrid/index.ts +++ b/src/components/datagrid/index.ts @@ -26,7 +26,6 @@ export { EuiDataGridCellValueElementProps, } from './data_grid_cell'; export { EuiDataGridColumnResizerProps } from './data_grid_column_resizer'; -export { EuiDataGridDataRowProps } from './data_grid_data_row'; export { EuiDataGridHeaderRowProps } from './data_grid_header_row'; export { EuiDataGridHeaderCellProps } from './data_grid_header_cell'; export { EuiDataGridControlHeaderRowProps } from './data_grid_control_header_cell'; From 8656fc8e3d5807b461f66a7a466c0d0d11f2314f Mon Sep 17 00:00:00 2001 From: Dave Snider Date: Thu, 3 Dec 2020 14:02:20 -0800 Subject: [PATCH 15/54] fix prop and type for style --- src/components/datagrid/data_grid_cell.tsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/components/datagrid/data_grid_cell.tsx b/src/components/datagrid/data_grid_cell.tsx index f54b987b76d..7b3cac114c0 100644 --- a/src/components/datagrid/data_grid_cell.tsx +++ b/src/components/datagrid/data_grid_cell.tsx @@ -92,7 +92,7 @@ export interface EuiDataGridCellProps { | JSXElementConstructor | ((props: EuiDataGridCellValueElementProps) => ReactNode); setRowHeight?: (height: number) => void; - style: any; + style?: React.CSSProperties; } interface EuiDataGridCellState { From 83254c157da439e5eda7ef3182d83e8a2cef0ea7 Mon Sep 17 00:00:00 2001 From: Dave Snider Date: Thu, 3 Dec 2020 16:58:14 -0800 Subject: [PATCH 16/54] remove console log --- src/components/datagrid/data_grid_body.tsx | 2 -- 1 file changed, 2 deletions(-) diff --git a/src/components/datagrid/data_grid_body.tsx b/src/components/datagrid/data_grid_body.tsx index cb091df81f3..6b864be03cd 100644 --- a/src/components/datagrid/data_grid_body.tsx +++ b/src/components/datagrid/data_grid_body.tsx @@ -159,8 +159,6 @@ const Cell: FunctionComponent = ({ const isTrailingControlColumn = columnIndex >= leadingControlColumns.length + columns.length; - console.log(rowIndex, columnIndex); - const classes = classNames({ 'euiDataGridRowCell--stripe': isStripableRow, 'euiDataGridRowCell--firstColumn': isFirstColumn, From 6281a5c7733189cf58dd243f1caac4ff56db14d0 Mon Sep 17 00:00:00 2001 From: Dave Snider Date: Tue, 8 Dec 2020 08:29:04 -0800 Subject: [PATCH 17/54] let popover in header act as a toggle --- src/components/datagrid/data_grid_header_cell.tsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/components/datagrid/data_grid_header_cell.tsx b/src/components/datagrid/data_grid_header_cell.tsx index 0d699f0a49b..ea6052d194f 100644 --- a/src/components/datagrid/data_grid_header_cell.tsx +++ b/src/components/datagrid/data_grid_header_cell.tsx @@ -318,7 +318,7 @@ export const EuiDataGridHeaderCell: FunctionComponent setIsPopoverOpen(true)}> + onClick={() => setIsPopoverOpen(!isPopoverOpen)}>
{display || displayAsText || id}
From 00ed6960c4a55a2cd6cdfcc010e450990d8fd893 Mon Sep 17 00:00:00 2001 From: Chandler Prall Date: Fri, 18 Dec 2020 10:29:00 -0700 Subject: [PATCH 18/54] recompute grid height when rowCount changes --- src/components/datagrid/data_grid.tsx | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/src/components/datagrid/data_grid.tsx b/src/components/datagrid/data_grid.tsx index 13de56184aa..eb4e2e55991 100644 --- a/src/components/datagrid/data_grid.tsx +++ b/src/components/datagrid/data_grid.tsx @@ -687,13 +687,18 @@ export const EuiDataGrid: FunctionComponent = (props) => { // this triggers a double re-render when the grid is switched from constrained to unconstrained // but in reality ... maybe don't do that? const [gridHeight, setGridHeight] = useState(IS_JEST_ENVIRONMENT ? 500 : 0); + const pageSize = pagination?.pageSize; useEffect(() => { if (height == null) { // if already 0 this update will be ignored // if not already 0, this resets the height constraint so a new value can be observed setGridHeight(0); } - }, [height, pagination?.pageSize]); + }, [ + height, + pageSize, + rowCount, // recompute height if the rowCount changes + ]); const [containerRef, _setContainerRef] = useState( null From 8b4ed63fed292db2de55f21ab19747476658e463 Mon Sep 17 00:00:00 2001 From: Chandler Prall Date: Fri, 18 Dec 2020 12:43:10 -0700 Subject: [PATCH 19/54] Dynamically detect header row height --- src/components/datagrid/data_grid_body.tsx | 74 ++++++++++--------- .../observer/mutation_observer/index.ts | 2 +- .../mutation_observer.test.tsx | 34 ++++++++- .../mutation_observer/mutation_observer.ts | 71 +++++++++++++----- 4 files changed, 125 insertions(+), 56 deletions(-) diff --git a/src/components/datagrid/data_grid_body.tsx b/src/components/datagrid/data_grid_body.tsx index 6b864be03cd..2d5e3cd03dd 100644 --- a/src/components/datagrid/data_grid_body.tsx +++ b/src/components/datagrid/data_grid_body.tsx @@ -53,9 +53,10 @@ import { EuiDataGridHeaderRow, EuiDataGridHeaderRowProps, } from './data_grid_header_row'; -import { EuiMutationObserver } from '../observer/mutation_observer'; +import { useMutationObserver } from '../observer/mutation_observer'; import { EuiText } from '../text'; import { DataGridSortingContext } from './data_grid_context'; +import { useResizeObserver } from '../observer/resize_observer'; export interface EuiDataGridBodyProps { gridHeight?: number; @@ -111,7 +112,6 @@ const providedPopoverContents: EuiDataGridPopoverContents = { }, }; -const HEADER_ROW_HEIGHT = 34; const FOOTER_ROW_HEIGHT = 34; const DefaultColumnFormatter: EuiDataGridPopoverContent = ({ children }) => { @@ -137,6 +137,7 @@ const Cell: FunctionComponent = ({ renderCellValue, interactiveCellId, setRowHeight, + headerRowDimensions, } = data; _rowIndex += rowOffset; @@ -186,7 +187,9 @@ const Cell: FunctionComponent = ({ setRowHeight={setRowHeight} style={{ ...style, - top: `${parseFloat(style.top as string) + HEADER_ROW_HEIGHT}px`, + top: `${ + parseFloat(style.top as string) + headerRowDimensions.height + }px`, }} /> ); @@ -210,7 +213,9 @@ const Cell: FunctionComponent = ({ className={classes} style={{ ...style, - top: `${parseFloat(style.top as string) + HEADER_ROW_HEIGHT}px`, + top: `${ + parseFloat(style.top as string) + headerRowDimensions.height + }px`, }} /> ); @@ -247,7 +252,9 @@ const Cell: FunctionComponent = ({ className={classes} style={{ ...style, - top: `${parseFloat(style.top as string) + HEADER_ROW_HEIGHT}px`, + top: `${ + parseFloat(style.top as string) + headerRowDimensions.height + }px`, }} /> ); @@ -287,6 +294,14 @@ export const EuiDataGridBody: FunctionComponent = ( switchColumnPos, } = props; + const [headerRowRef, setHeaderRowRef] = useState(null); + + useMutationObserver(headerRowRef, handleHeaderMutation, { + subtree: true, + childList: true, + }); + const headerRowDimensions = useResizeObserver(headerRowRef, 'height'); + const startRow = pagination ? pagination.pageIndex * pagination.pageSize : 0; let endRow = pagination ? (pagination.pageIndex + 1) * pagination.pageSize @@ -356,7 +371,7 @@ export const EuiDataGridBody: FunctionComponent = ( } return rowMap; - }, [sorting, inMemory, inMemoryValues, schema, schemaDetectors]); + }, [sorting, inMemoryValues, schema, schemaDetectors]); const mergedPopoverContents = useMemo( () => ({ @@ -368,34 +383,22 @@ export const EuiDataGridBody: FunctionComponent = ( const headerRow = useMemo(() => { return ( - <> - - {(ref) => ( - - )} - - + ); }, [ - handleHeaderMutation, switchColumnPos, setVisibleColumns, leadingControlColumns, @@ -450,7 +453,7 @@ export const EuiDataGridBody: FunctionComponent = ( ref={ref} style={{ ...style, - height: style.height + HEADER_ROW_HEIGHT, + height: style.height + headerRowDimensions.height, }} {...rest}> {headerRow} @@ -461,7 +464,7 @@ export const EuiDataGridBody: FunctionComponent = ( ); } ), - [headerRow, footerRow] + [headerRowDimensions.height, headerRow, footerRow] ); const gridRef = useRef(null); @@ -519,7 +522,7 @@ export const EuiDataGridBody: FunctionComponent = ( gridHeight || rowHeight * visibleRowIndices.length + SCROLLBAR_HEIGHT + - HEADER_ROW_HEIGHT + + headerRowDimensions.height + (footerRow ? FOOTER_ROW_HEIGHT : 0) } rowHeight={getRowHeight} @@ -536,6 +539,7 @@ export const EuiDataGridBody: FunctionComponent = ( defaultColumnWidth, renderCellValue, interactiveCellId, + headerRowDimensions, }} rowCount={visibleRowIndices.length}> {Cell} diff --git a/src/components/observer/mutation_observer/index.ts b/src/components/observer/mutation_observer/index.ts index 007d8bd2c05..9522e7bc401 100644 --- a/src/components/observer/mutation_observer/index.ts +++ b/src/components/observer/mutation_observer/index.ts @@ -17,4 +17,4 @@ * under the License. */ -export { EuiMutationObserver } from './mutation_observer'; +export { EuiMutationObserver, useMutationObserver } from './mutation_observer'; diff --git a/src/components/observer/mutation_observer/mutation_observer.test.tsx b/src/components/observer/mutation_observer/mutation_observer.test.tsx index 1805bd89ec9..4f6775ec6c1 100644 --- a/src/components/observer/mutation_observer/mutation_observer.test.tsx +++ b/src/components/observer/mutation_observer/mutation_observer.test.tsx @@ -17,9 +17,9 @@ * under the License. */ -import React, { FunctionComponent } from 'react'; +import React, { FunctionComponent, useState } from 'react'; import { mount } from 'enzyme'; -import { EuiMutationObserver } from './mutation_observer'; +import { EuiMutationObserver, useMutationObserver } from './mutation_observer'; import { sleep } from '../../../test'; export async function waitforMutationObserver(period = 30) { @@ -55,3 +55,33 @@ describe('EuiMutationObserver', () => { expect(onMutation).toHaveBeenCalledTimes(1); }); }); + +describe('useMutationObserver', () => { + it('watches changing content', async () => { + expect.assertions(2); + + const mutationCallback = jest.fn(); + const Wrapper: FunctionComponent<{}> = jest.fn(({ children }) => { + const [ref, setRef] = useState(null); + useMutationObserver(ref, mutationCallback, { childList: true, subtree: true }); + return
{children}
; + }); + + const component = mount(Hello World
} />); + + await waitforMutationObserver(); + expect(mutationCallback).toHaveBeenCalledTimes(0); + + component.setProps({ + children: ( +
+
Hello World
+
Hello Again
+
+ ), + }); + + await waitforMutationObserver(); + expect(mutationCallback).toHaveBeenCalledTimes(1); + }); +}); diff --git a/src/components/observer/mutation_observer/mutation_observer.ts b/src/components/observer/mutation_observer/mutation_observer.ts index 4db7fe6050f..c3af6083fa0 100644 --- a/src/components/observer/mutation_observer/mutation_observer.ts +++ b/src/components/observer/mutation_observer/mutation_observer.ts @@ -17,7 +17,7 @@ * under the License. */ -import { ReactNode } from 'react'; +import { ReactNode, useEffect } from 'react'; import { EuiObserver } from '../observer'; @@ -40,22 +40,57 @@ export class EuiMutationObserver extends EuiObserver { }; beginObserve = () => { - // IE11 and the MutationObserver polyfill used in Kibana (for Jest) implement - // an older spec in which specifying `attributeOldValue` or `attributeFilter` - // without specifying `attributes` results in a `SyntaxError`. - // The following logic patches the newer spec in which `attributes: true` can be - // implied when appropriate (`attributeOldValue` or `attributeFilter` is specified). - const observerOptions: MutationObserverInit = { - ...this.props.observerOptions, - }; - const needsAttributes = - observerOptions.hasOwnProperty('attributeOldValue') || - observerOptions.hasOwnProperty('attributeFilter'); - if (needsAttributes && !observerOptions.hasOwnProperty('attributes')) { - observerOptions.attributes = true; - } - - this.observer = new MutationObserver(this.onMutation); - this.observer.observe(this.childNode!, observerOptions); + const childNode = this.childNode!; + this.observer = makeMutationObserver( + childNode, + this.props.observerOptions, + this.onMutation + ); }; } + +const makeMutationObserver = ( + node: Element, + _observerOptions: MutationObserverInit | undefined, + callback: MutationCallback +) => { + // IE11 and the MutationObserver polyfill used in Kibana (for Jest) implement + // an older spec in which specifying `attributeOldValue` or `attributeFilter` + // without specifying `attributes` results in a `SyntaxError`. + // The following logic patches the newer spec in which `attributes: true` can be + // implied when appropriate (`attributeOldValue` or `attributeFilter` is specified). + const observerOptions: MutationObserverInit = { + ..._observerOptions, + }; + const needsAttributes = + observerOptions.hasOwnProperty('attributeOldValue') || + observerOptions.hasOwnProperty('attributeFilter'); + if (needsAttributes && !observerOptions.hasOwnProperty('attributes')) { + observerOptions.attributes = true; + } + + const observer = new MutationObserver(callback); + observer.observe(node, observerOptions); + + return observer; +}; + +export const useMutationObserver = ( + container: Element | null, + callback: MutationCallback, + observerOptions?: MutationObserverInit +) => { + useEffect( + () => { + if (container != null) { + const observer = makeMutationObserver(container, observerOptions, () => + console.log('SOMETHING CHANGED') + ); + return () => observer.disconnect(); + } + }, + // ignore changing observerOptions + // eslint-disable-next-line + [container, callback] + ); +}; From 99e5346a3023f647520446983a8caf0fb976baae Mon Sep 17 00:00:00 2001 From: Chandler Prall Date: Fri, 18 Dec 2020 13:48:05 -0700 Subject: [PATCH 20/54] Make useMutationObserver actually function --- .../observer/mutation_observer/mutation_observer.test.tsx | 5 ++++- .../observer/mutation_observer/mutation_observer.ts | 6 ++++-- 2 files changed, 8 insertions(+), 3 deletions(-) diff --git a/src/components/observer/mutation_observer/mutation_observer.test.tsx b/src/components/observer/mutation_observer/mutation_observer.test.tsx index 4f6775ec6c1..71f55788bb3 100644 --- a/src/components/observer/mutation_observer/mutation_observer.test.tsx +++ b/src/components/observer/mutation_observer/mutation_observer.test.tsx @@ -63,7 +63,10 @@ describe('useMutationObserver', () => { const mutationCallback = jest.fn(); const Wrapper: FunctionComponent<{}> = jest.fn(({ children }) => { const [ref, setRef] = useState(null); - useMutationObserver(ref, mutationCallback, { childList: true, subtree: true }); + useMutationObserver(ref, mutationCallback, { + childList: true, + subtree: true, + }); return
{children}
; }); diff --git a/src/components/observer/mutation_observer/mutation_observer.ts b/src/components/observer/mutation_observer/mutation_observer.ts index c3af6083fa0..458fd8ea6e9 100644 --- a/src/components/observer/mutation_observer/mutation_observer.ts +++ b/src/components/observer/mutation_observer/mutation_observer.ts @@ -83,8 +83,10 @@ export const useMutationObserver = ( useEffect( () => { if (container != null) { - const observer = makeMutationObserver(container, observerOptions, () => - console.log('SOMETHING CHANGED') + const observer = makeMutationObserver( + container, + observerOptions, + callback ); return () => observer.disconnect(); } From 532834790ba40821c95a1de68de6e0e13f43f7bd Mon Sep 17 00:00:00 2001 From: Chandler Prall Date: Fri, 18 Dec 2020 14:06:17 -0700 Subject: [PATCH 21/54] fix double-clicky and getting-stuck header cell popovers --- src/components/datagrid/data_grid_header_cell.tsx | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/src/components/datagrid/data_grid_header_cell.tsx b/src/components/datagrid/data_grid_header_cell.tsx index 9b423a51509..e0dd67903ae 100644 --- a/src/components/datagrid/data_grid_header_cell.tsx +++ b/src/components/datagrid/data_grid_header_cell.tsx @@ -274,6 +274,7 @@ export const EuiDataGridHeaderCell: FunctionComponent setIsPopoverOpen(!isPopoverOpen)}> + onClick={() => setIsPopoverOpen((isPopoverOpen) => !isPopoverOpen)}> {sortingArrow}
{display || displayAsText || id} @@ -339,6 +340,7 @@ export const EuiDataGridHeaderCell: FunctionComponent } isOpen={isPopoverOpen} - closePopover={() => setIsPopoverOpen(false)} - ownFocus={isFocused}> + closePopover={() => setIsPopoverOpen(false)}>
Date: Fri, 18 Dec 2020 15:14:29 -0700 Subject: [PATCH 22/54] small header height refactor --- src/components/datagrid/data_grid_body.tsx | 106 +++++++++--------- src/components/datagrid/data_grid_cell.tsx | 7 ++ src/components/datagrid/data_grid_context.tsx | 2 + 3 files changed, 63 insertions(+), 52 deletions(-) diff --git a/src/components/datagrid/data_grid_body.tsx b/src/components/datagrid/data_grid_body.tsx index 2d5e3cd03dd..1a46bf29747 100644 --- a/src/components/datagrid/data_grid_body.tsx +++ b/src/components/datagrid/data_grid_body.tsx @@ -55,7 +55,10 @@ import { } from './data_grid_header_row'; import { useMutationObserver } from '../observer/mutation_observer'; import { EuiText } from '../text'; -import { DataGridSortingContext } from './data_grid_context'; +import { + DataGridSortingContext, + DataGridHeaderRowHeightContext, +} from './data_grid_context'; import { useResizeObserver } from '../observer/resize_observer'; export interface EuiDataGridBodyProps { @@ -137,9 +140,10 @@ const Cell: FunctionComponent = ({ renderCellValue, interactiveCellId, setRowHeight, - headerRowDimensions, } = data; + const headerRowHeight = useContext(DataGridHeaderRowHeightContext); + _rowIndex += rowOffset; const rowIndex = rowMap.hasOwnProperty(_rowIndex) ? rowMap[_rowIndex] @@ -187,9 +191,7 @@ const Cell: FunctionComponent = ({ setRowHeight={setRowHeight} style={{ ...style, - top: `${ - parseFloat(style.top as string) + headerRowDimensions.height - }px`, + top: `${parseFloat(style.top as string) + headerRowHeight}px`, }} /> ); @@ -213,9 +215,7 @@ const Cell: FunctionComponent = ({ className={classes} style={{ ...style, - top: `${ - parseFloat(style.top as string) + headerRowDimensions.height - }px`, + top: `${parseFloat(style.top as string) + headerRowHeight}px`, }} /> ); @@ -252,9 +252,7 @@ const Cell: FunctionComponent = ({ className={classes} style={{ ...style, - top: `${ - parseFloat(style.top as string) + headerRowDimensions.height - }px`, + top: `${parseFloat(style.top as string) + headerRowHeight}px`, }} /> ); @@ -300,7 +298,7 @@ export const EuiDataGridBody: FunctionComponent = ( subtree: true, childList: true, }); - const headerRowDimensions = useResizeObserver(headerRowRef, 'height'); + const { height: headerRowHeight } = useResizeObserver(headerRowRef, 'height'); const startRow = pagination ? pagination.pageIndex * pagination.pageSize : 0; let endRow = pagination @@ -447,13 +445,14 @@ export const EuiDataGridBody: FunctionComponent = ( () => forwardRef( ({ children, style, ...rest }, ref) => { + const headerRowHeight = useContext(DataGridHeaderRowHeightContext); return ( <>
{headerRow} @@ -464,7 +463,7 @@ export const EuiDataGridBody: FunctionComponent = ( ); } ), - [headerRowDimensions.height, headerRow, footerRow] + [headerRow, footerRow] ); const gridRef = useRef(null); @@ -506,43 +505,46 @@ export const EuiDataGridBody: FunctionComponent = ( }, [getRowHeight]); return ( - - {Cell} - + + + {Cell} + + ); }; diff --git a/src/components/datagrid/data_grid_cell.tsx b/src/components/datagrid/data_grid_cell.tsx index e15e7bc5618..59854de8f1d 100644 --- a/src/components/datagrid/data_grid_cell.tsx +++ b/src/components/datagrid/data_grid_cell.tsx @@ -240,6 +240,13 @@ export class EuiDataGridCell extends Component< return true; if (nextProps.popoverContent !== this.props.popoverContent) return true; + // respond to adjusted top/left + if (nextProps.style) { + if (!this.props.style) return true; + if (nextProps.style.top !== this.props.style.top) return true; + if (nextProps.style.left !== this.props.style.left) return true; + } + if (nextState.cellProps !== this.state.cellProps) return true; if (nextState.popoverIsOpen !== this.state.popoverIsOpen) return true; if (nextState.isEntered !== this.state.isEntered) return true; diff --git a/src/components/datagrid/data_grid_context.tsx b/src/components/datagrid/data_grid_context.tsx index 1d24e825174..49f1989658d 100644 --- a/src/components/datagrid/data_grid_context.tsx +++ b/src/components/datagrid/data_grid_context.tsx @@ -35,3 +35,5 @@ export const DataGridFocusContext = React.createContext< export const DataGridSortingContext = React.createContext< EuiDataGridSorting | undefined >(undefined); + +export const DataGridHeaderRowHeightContext = React.createContext(0); From 52798b3e6ce756d0b142b2624743059eeaee9acb Mon Sep 17 00:00:00 2001 From: Chandler Prall Date: Mon, 21 Dec 2020 10:22:29 -0700 Subject: [PATCH 23/54] fix grid height detection to wait until header height is known --- src/components/datagrid/data_grid.tsx | 171 ++++++++++----------- src/components/datagrid/data_grid_body.tsx | 6 +- 2 files changed, 83 insertions(+), 94 deletions(-) diff --git a/src/components/datagrid/data_grid.tsx b/src/components/datagrid/data_grid.tsx index eb4e2e55991..0cebf489d93 100644 --- a/src/components/datagrid/data_grid.tsx +++ b/src/components/datagrid/data_grid.tsx @@ -64,10 +64,7 @@ import { useDataGridColumnSelector } from './column_selector'; import { useDataGridStyleSelector, startingStyles } from './style_selector'; import { EuiTablePagination } from '../table/table_pagination'; import { EuiFocusTrap } from '../focus_trap'; -import { - EuiResizeObserver, - useResizeObserver, -} from '../observer/resize_observer'; +import { useResizeObserver } from '../observer/resize_observer'; import { EuiDataGridInMemoryRenderer } from './data_grid_inmemory_renderer'; import { useMergedSchema, @@ -403,19 +400,6 @@ function useColumnWidths( return [columnWidths, setColumnWidth]; } -function useOnResize( - setGridWidth: (newWidth: number) => void, - setGridHeight: (newHeight: number) => void -) { - return useCallback( - ({ width, height }: { width: number; height: number }) => { - setGridWidth(width); - setGridHeight(height); - }, - [setGridWidth, setGridHeight] - ); -} - function useInMemoryValues( inMemory: EuiDataGridInMemory | undefined, rowCount: number @@ -688,6 +672,7 @@ export const EuiDataGrid: FunctionComponent = (props) => { // but in reality ... maybe don't do that? const [gridHeight, setGridHeight] = useState(IS_JEST_ENVIRONMENT ? 500 : 0); const pageSize = pagination?.pageSize; + const resetGridHeight = useCallback(() => setGridHeight(0), []); useEffect(() => { if (height == null) { // if already 0 this update will be ignored @@ -776,7 +761,14 @@ export const EuiDataGrid: FunctionComponent = (props) => { }; // enables/disables grid controls based on available width - const onResize = useOnResize(setGridWidth, setGridHeight); + const [resizeRef, setResizeRef] = useState(null); + const gridDimensions = useResizeObserver(resizeRef); + useEffect(() => { + const { height, width } = gridDimensions; + setGridWidth(width); + setGridHeight(height); + }, [gridDimensions]); + const hasRoomForGridControls = gridWidth > minSizeForControls || isFullScreen; const [columnWidths, setColumnWidth] = useColumnWidths( @@ -1054,83 +1046,76 @@ export const EuiDataGrid: FunctionComponent = (props) => { : null}
) : null} - - {(resizeRef) => ( +
+
+ {inMemory ? ( + + ) : null}
-
- {inMemory ? ( - - ) : null} -
- -
-
+ ref={setContentRef} + data-test-subj="dataGridWrapper" + className="euiDataGrid__content" + role="grid" + id={gridId} + {...wrappingDivFocusProps} + {...gridAriaProps}> +
- )} - +
+
{props.pagination && props['aria-labelledby'] && (
diff --git a/src/components/datagrid/data_grid_body.tsx b/src/components/datagrid/data_grid_body.tsx index 91f01c9fe83..b9ee10b9f96 100644 --- a/src/components/datagrid/data_grid_body.tsx +++ b/src/components/datagrid/data_grid_body.tsx @@ -85,6 +85,7 @@ export interface EuiDataGridBodyProps { setVisibleColumns: EuiDataGridHeaderRowProps['setVisibleColumns']; switchColumnPos: EuiDataGridHeaderRowProps['switchColumnPos']; resetGridHeight: () => void; + setSizeIsStable: (sizeIsStable: boolean) => void; } const defaultComparator: NonNullable< @@ -116,8 +117,6 @@ const providedPopoverContents: EuiDataGridPopoverContents = { }, }; -const FOOTER_ROW_HEIGHT = 34; - const DefaultColumnFormatter: EuiDataGridPopoverContent = ({ children }) => { return {children}; }; @@ -291,18 +290,31 @@ export const EuiDataGridBody: FunctionComponent = ( handleHeaderMutation, setVisibleColumns, switchColumnPos, - resetGridHeight + resetGridHeight, + setSizeIsStable, } = props; + const hasFooterRow = renderFooterCellValue; + const [headerRowRef, setHeaderRowRef] = useState(null); + const [footerRowRef, setFooterRowRef] = useState(null); useMutationObserver(headerRowRef, handleHeaderMutation, { subtree: true, childList: true, }); const { height: headerRowHeight } = useResizeObserver(headerRowRef, 'height'); + const { height: footerRowHeight } = useResizeObserver(footerRowRef, 'height'); - useEffect(() => resetGridHeight(), [headerRowHeight]); + useEffect(() => { + const isHeaderStable = headerRowHeight !== 0; + const isFooterStable = !hasFooterRow || footerRowHeight !== 0; + setSizeIsStable(isHeaderStable && isFooterStable); + }, [hasFooterRow, setSizeIsStable, headerRowHeight, footerRowHeight]); + + useEffect(() => { + resetGridHeight(); + }, [resetGridHeight, headerRowHeight, footerRowHeight]); const startRow = pagination ? pagination.pageIndex * pagination.pageSize : 0; let endRow = pagination @@ -419,6 +431,7 @@ export const EuiDataGridBody: FunctionComponent = ( return ( = ( if (gridRef.current) gridRef.current.resetAfterRowIndex(0); }, [getRowHeight]); + const height = + // intentionally ignoring gridHeight if it is null/undefined/0 + // and using it only if we have found the header&footer heights + (headerRowHeight && (!hasFooterRow || footerRowHeight) && gridHeight) || + // otherwise compute the height + rowHeight * visibleRowIndices.length + + SCROLLBAR_HEIGHT + + headerRowHeight + + footerRowHeight; + return ( = ( } width={gridWidth} columnWidth={getWidth} - height={ - // intentionally ignoring gridHeight if it is null/undefined/0 - (headerRowHeight && gridHeight) || - rowHeight * visibleRowIndices.length + - SCROLLBAR_HEIGHT + - headerRowHeight + - (footerRow ? FOOTER_ROW_HEIGHT : 0) - } + height={height} rowHeight={getRowHeight} itemData={{ setRowHeight, diff --git a/src/components/datagrid/data_grid_cell.tsx b/src/components/datagrid/data_grid_cell.tsx index 59854de8f1d..2f3964e7589 100644 --- a/src/components/datagrid/data_grid_cell.tsx +++ b/src/components/datagrid/data_grid_cell.tsx @@ -341,8 +341,7 @@ export class EuiDataGridCell extends Component< className: classNames(cellClasses, this.state.cellProps.className), }; - const widthStyle = width != null ? { width: `${width}px` } : {}; - cellProps.style = { ...style, widthStyle }; + cellProps.style = { ...style, width }; const handleCellKeyDown = (event: KeyboardEvent) => { if (isExpandable) { diff --git a/src/components/datagrid/data_grid_footer_row.tsx b/src/components/datagrid/data_grid_footer_row.tsx index 3ebc36a49bb..9a7b79bb8f0 100644 --- a/src/components/datagrid/data_grid_footer_row.tsx +++ b/src/components/datagrid/data_grid_footer_row.tsx @@ -17,7 +17,7 @@ * under the License. */ -import React, { FunctionComponent, HTMLAttributes, memo } from 'react'; +import React, { forwardRef, HTMLAttributes, memo } from 'react'; import classnames from 'classnames'; import { EuiDataGridControlColumn, @@ -51,86 +51,47 @@ const DefaultColumnFormatter: EuiDataGridPopoverContent = ({ children }) => { return {children}; }; -const EuiDataGridFooterRow: FunctionComponent = memo( - ({ - leadingControlColumns, - trailingControlColumns, - columns, - schema, - popoverContents, - columnWidths, - defaultColumnWidth, - className, - renderCellValue, - rowIndex, - interactiveCellId, - 'data-test-subj': _dataTestSubj, - visibleRowIndex = rowIndex, - ...rest - }) => { - const classes = classnames( - 'euiDataGridRow', - 'euiDataGridFooter', - className - ); - const dataTestSubj = classnames('dataGridRow', _dataTestSubj); +const EuiDataGridFooterRow = memo( + forwardRef( + ( + { + leadingControlColumns, + trailingControlColumns, + columns, + schema, + popoverContents, + columnWidths, + defaultColumnWidth, + className, + renderCellValue, + rowIndex, + interactiveCellId, + 'data-test-subj': _dataTestSubj, + visibleRowIndex = rowIndex, + ...rest + }, + ref + ) => { + const classes = classnames( + 'euiDataGridRow', + 'euiDataGridFooter', + className + ); + const dataTestSubj = classnames('dataGridRow', _dataTestSubj); - return ( -
- {leadingControlColumns.map(({ id, width }, i) => ( - null} - interactiveCellId={interactiveCellId} - isExpandable={true} - className="euiDataGridFooterCell euiDataGridRowCell--controlColumn" - /> - ))} - {columns.map(({ id }, i) => { - const columnType = schema[id] ? schema[id].columnType : null; - const popoverContent = - (columnType && popoverContents[columnType]) || - DefaultColumnFormatter; - - const width = columnWidths[id] || defaultColumnWidth; - const columnPosition = i + leadingControlColumns.length; - - return ( + return ( +
+ {leadingControlColumns.map(({ id, width }, i) => ( - ); - })} - {trailingControlColumns.map(({ id, width }, i) => { - const colIndex = i + columns.length + leadingControlColumns.length; - - return ( - = memo( isExpandable={true} className="euiDataGridFooterCell euiDataGridRowCell--controlColumn" /> - ); - })} -
- ); - } + ))} + {columns.map(({ id }, i) => { + const columnType = schema[id] ? schema[id].columnType : null; + const popoverContent = + (columnType && popoverContents[columnType]) || + DefaultColumnFormatter; + + const width = columnWidths[id] || defaultColumnWidth; + const columnPosition = i + leadingControlColumns.length; + + return ( + + ); + })} + {trailingControlColumns.map(({ id, width }, i) => { + const colIndex = i + columns.length + leadingControlColumns.length; + + return ( + null} + interactiveCellId={interactiveCellId} + isExpandable={true} + className="euiDataGridFooterCell euiDataGridRowCell--controlColumn" + /> + ); + })} +
+ ); + } + ) ); +EuiDataGridFooterRow.displayName = 'EuiDataGridFooterRow'; + export { EuiDataGridFooterRow }; From 037c9795ebdaeccb5969a215d79d2a6c68fa786c Mon Sep 17 00:00:00 2001 From: Chandler Prall Date: Tue, 22 Dec 2020 12:57:14 -0700 Subject: [PATCH 25/54] Down to 2 failing unit tests --- .../__snapshots__/data_grid.test.tsx.snap | 2184 ++++++++--------- src/components/datagrid/data_grid.test.tsx | 153 +- src/components/datagrid/data_grid.tsx | 12 +- src/components/datagrid/data_grid_body.tsx | 5 +- 4 files changed, 1120 insertions(+), 1234 deletions(-) diff --git a/src/components/datagrid/__snapshots__/data_grid.test.tsx.snap b/src/components/datagrid/__snapshots__/data_grid.test.tsx.snap index 9ba2b4c536f..cc2e4907ec5 100644 --- a/src/components/datagrid/__snapshots__/data_grid.test.tsx.snap +++ b/src/components/datagrid/__snapshots__/data_grid.test.tsx.snap @@ -1047,10 +1047,11 @@ Array [ tabindex="0" >
+

+ + Row: 1, Column: 1: + +

-

- - Row: 1, Column: 1: - -

-
- 0, A -
+ class="euiDataGridRowCell__truncate" + > + 0, A
-
+
+ -
+ class="euiButtonIcon__icon" + data-euiicon-type="expandMini" + /> +
@@ -1192,60 +1189,56 @@ Array [
+

+ + Row: 1, Column: 2: + +

-

- - Row: 1, Column: 2: - -

-
- 0, B -
+ class="euiDataGridRowCell__truncate" + > + 0, B
-
+
+ -
+ class="euiButtonIcon__icon" + data-euiicon-type="expandMini" + /> +
@@ -1253,60 +1246,56 @@ Array [
+

+ + Row: 2, Column: 1: + +

-

- - Row: 2, Column: 1: - -

-
- 1, A -
+ class="euiDataGridRowCell__truncate" + > + 1, A
-
+
+ -
+ class="euiButtonIcon__icon" + data-euiicon-type="expandMini" + /> +
@@ -1314,60 +1303,56 @@ Array [
+

+ + Row: 2, Column: 2: + +

-

- - Row: 2, Column: 2: - -

-
- 1, B -
+ class="euiDataGridRowCell__truncate" + > + 1, B
-
+
+ -
+ class="euiButtonIcon__icon" + data-euiicon-type="expandMini" + /> +
@@ -1375,60 +1360,56 @@ Array [
+

+ + Row: 3, Column: 1: + +

-

- - Row: 3, Column: 1: - -

-
- 2, A -
+ class="euiDataGridRowCell__truncate" + > + 2, A
-
+
+ -
+ class="euiButtonIcon__icon" + data-euiicon-type="expandMini" + /> +
@@ -1436,60 +1417,56 @@ Array [
+

+ + Row: 3, Column: 2: + +

-

- - Row: 3, Column: 2: - -

-
- 2, B -
+ class="euiDataGridRowCell__truncate" + > + 2, B
-
+
+ -
+ class="euiButtonIcon__icon" + data-euiicon-type="expandMini" + /> +
@@ -1635,10 +1612,11 @@ Array [ tabindex="0" >
+
+
-
-
+

+ + Row: 1, Column: 1: + +

-

- - Row: 1, Column: 1: - -

-
- 0 -
+ 0
-
+
+

+ + Row: 1, Column: 2: + +

-

- - Row: 1, Column: 2: - -

-
- 0, A -
+ class="euiDataGridRowCell__truncate" + > + 0, A
-
+
+ -
+ class="euiButtonIcon__icon" + data-euiicon-type="expandMini" + /> +
@@ -1861,60 +1831,56 @@ Array [
+

+ + Row: 1, Column: 3: + +

-

- - Row: 1, Column: 3: - -

-
- 0, B -
+ class="euiDataGridRowCell__truncate" + > + 0, B
-
+
+ -
+ class="euiButtonIcon__icon" + data-euiicon-type="expandMini" + /> +
@@ -1922,162 +1888,150 @@ Array [
+
+
-
-
+

+ + Row: 1, Column: 4: + +

-

- - Row: 1, Column: 4: - -

-
- 0 -
+ 0
-
+
+
+
-
-
+

+ + Row: 2, Column: 1: + +

-

- - Row: 2, Column: 1: - -

-
- 1 -
+ 1
-
+
+

+ + Row: 2, Column: 2: + +

-

- - Row: 2, Column: 2: - -

-
- 1, A -
+ class="euiDataGridRowCell__truncate" + > + 1, A
-
+
+ -
+ class="euiButtonIcon__icon" + data-euiicon-type="expandMini" + /> +
@@ -2085,60 +2039,56 @@ Array [
+

+ + Row: 2, Column: 3: + +

-

- - Row: 2, Column: 3: - -

-
- 1, B -
+ class="euiDataGridRowCell__truncate" + > + 1, B
-
+
+ -
+ class="euiButtonIcon__icon" + data-euiicon-type="expandMini" + /> +
@@ -2146,162 +2096,150 @@ Array [
+
+
-
-
+

+ + Row: 2, Column: 4: + +

-

- - Row: 2, Column: 4: - -

-
- 1 -
+ 1
-
+
+
+
-
-
+

+ + Row: 3, Column: 1: + +

-

- - Row: 3, Column: 1: - -

-
- 2 -
+ 2
-
+
+

+ + Row: 3, Column: 2: + +

-

- - Row: 3, Column: 2: - -

-
- 2, A -
+ class="euiDataGridRowCell__truncate" + > + 2, A
-
+
+ -
+ class="euiButtonIcon__icon" + data-euiicon-type="expandMini" + /> +
@@ -2309,60 +2247,56 @@ Array [
+

+ + Row: 3, Column: 3: + +

-

- - Row: 3, Column: 3: - -

-
- 2, B -
+ class="euiDataGridRowCell__truncate" + > + 2, B
-
+
+ -
+ class="euiButtonIcon__icon" + data-euiicon-type="expandMini" + /> +
@@ -2370,55 +2304,51 @@ Array [
+
+
-
-
+

+ + Row: 3, Column: 4: + +

-

- - Row: 3, Column: 4: - -

-
- 2 -
+ 2
-
+
@@ -2556,10 +2486,11 @@ Array [ tabindex="0" >
+

+ + Row: 1, Column: 1: + +

-

- - Row: 1, Column: 1: - -

-
- 0, A -
+ class="euiDataGridRowCell__truncate" + > + 0, A
-
+
+ -
+ class="euiButtonIcon__icon" + data-euiicon-type="expandMini" + /> +
@@ -2703,60 +2630,56 @@ Array [
+

+ + Row: 1, Column: 2: + +

-

- - Row: 1, Column: 2: - -

-
- 0, B -
+ class="euiDataGridRowCell__truncate" + > + 0, B
-
+
+ -
+ class="euiButtonIcon__icon" + data-euiicon-type="expandMini" + /> +
@@ -2764,60 +2687,56 @@ Array [
+

+ + Row: 2, Column: 1: + +

-

- - Row: 2, Column: 1: - -

-
- 1, A -
+ class="euiDataGridRowCell__truncate" + > + 1, A
-
+
+ -
+ class="euiButtonIcon__icon" + data-euiicon-type="expandMini" + /> +
@@ -2825,60 +2744,56 @@ Array [
+

+ + Row: 2, Column: 2: + +

-

- - Row: 2, Column: 2: - -

-
- 1, B -
+ class="euiDataGridRowCell__truncate" + > + 1, B
-
+
+ -
+ class="euiButtonIcon__icon" + data-euiicon-type="expandMini" + /> +
@@ -2886,60 +2801,56 @@ Array [
+

+ + Row: 3, Column: 1: + +

-

- - Row: 3, Column: 1: - -

-
- 2, A -
+ class="euiDataGridRowCell__truncate" + > + 2, A
-
+
+ -
+ class="euiButtonIcon__icon" + data-euiicon-type="expandMini" + /> +
@@ -2947,60 +2858,56 @@ Array [
+

+ + Row: 3, Column: 2: + +

-

- - Row: 3, Column: 2: - -

-
- 2, B -
+ class="euiDataGridRowCell__truncate" + > + 2, B
-
+
+ -
+ class="euiButtonIcon__icon" + data-euiicon-type="expandMini" + /> +
@@ -3143,10 +3050,11 @@ Array [ tabindex="0" >
+

+ + Row: 1, Column: 1: + +

-

- - Row: 1, Column: 1: - -

-
- 0, A -
+ class="euiDataGridRowCell__truncate" + > + 0, A
-
+
+ -
+ class="euiButtonIcon__icon" + data-euiicon-type="expandMini" + /> +
@@ -3288,60 +3192,56 @@ Array [
+

+ + Row: 1, Column: 2: + +

-

- - Row: 1, Column: 2: - -

-
- 0, B -
+ class="euiDataGridRowCell__truncate" + > + 0, B
-
+
+ -
+ class="euiButtonIcon__icon" + data-euiicon-type="expandMini" + /> +
@@ -3349,60 +3249,56 @@ Array [
+

+ + Row: 2, Column: 1: + +

-

- - Row: 2, Column: 1: - -

-
- 1, A -
+ class="euiDataGridRowCell__truncate" + > + 1, A
-
+
+ -
+ class="euiButtonIcon__icon" + data-euiicon-type="expandMini" + /> +
@@ -3410,60 +3306,56 @@ Array [
+

+ + Row: 2, Column: 2: + +

-

- - Row: 2, Column: 2: - -

-
- 1, B -
+ class="euiDataGridRowCell__truncate" + > + 1, B
-
+
+ -
+ class="euiButtonIcon__icon" + data-euiicon-type="expandMini" + /> +
@@ -3471,60 +3363,56 @@ Array [
+

+ + Row: 3, Column: 1: + +

-

- - Row: 3, Column: 1: - -

-
- 2, A -
+ class="euiDataGridRowCell__truncate" + > + 2, A
-
+
+ -
+ class="euiButtonIcon__icon" + data-euiicon-type="expandMini" + /> +
@@ -3532,60 +3420,56 @@ Array [
+

+ + Row: 3, Column: 2: + +

-

- - Row: 3, Column: 2: - -

-
- 2, B -
+ class="euiDataGridRowCell__truncate" + > + 2, B
-
+
+ -
+ class="euiButtonIcon__icon" + data-euiicon-type="expandMini" + /> +
diff --git a/src/components/datagrid/data_grid.test.tsx b/src/components/datagrid/data_grid.test.tsx index 3e32e6f98a7..4d15e5fdc2a 100644 --- a/src/components/datagrid/data_grid.test.tsx +++ b/src/components/datagrid/data_grid.test.tsx @@ -19,7 +19,7 @@ import React, { useEffect, useState } from 'react'; import { mount, ReactWrapper, render } from 'enzyme'; -import { EuiDataGrid } from './'; +import { EuiDataGrid, EuiDataGridProps } from './'; import { findTestSubject, requiredProps, @@ -28,13 +28,12 @@ import { import { EuiDataGridColumnResizer } from './data_grid_column_resizer'; import { keys } from '../../services'; import { act } from 'react-dom/test-utils'; -import cheerio from 'cheerio'; function getFocusableCell(component: ReactWrapper) { return findTestSubject(component, 'dataGridRowCell').find('[tabIndex=0]'); } -function extractGridData(datagrid: ReactWrapper) { +function extractGridData(datagrid: ReactWrapper) { const rows: string[][] = []; const headerCells = findTestSubject(datagrid, 'dataGridHeaderCell', '|='); @@ -46,15 +45,18 @@ function extractGridData(datagrid: ReactWrapper) { ); rows.push(headerRow); - const gridRows = findTestSubject(datagrid, 'dataGridRow'); - gridRows.forEach((row: any) => { + // reduce the virtualized grid of cells into rows + const columnCount = datagrid.prop('columnVisibility').visibleColumns.length; + const gridCells = findTestSubject(datagrid, 'dataGridRowCell'); + const visibleRowsCount = gridCells.length / columnCount; + for (let i = 0; i < visibleRowsCount; i++) { const rowContent: string[] = []; - const cells = findTestSubject(row, 'dataGridRowCell'); - cells.forEach((cell: any) => - rowContent.push(cell.find('[data-test-subj="cell-content"]').text()) - ); + for (let j = i * columnCount; j < (i + 1) * columnCount; j++) { + const cell = gridCells.at(j); + rowContent.push(cell.find('[data-test-subj="cell-content"]').text()); + } rows.push(rowContent); - }); + } return rows; } @@ -378,7 +380,7 @@ function setColumnVisibility( } function moveColumnToIndex( - datagrid: ReactWrapper, + datagrid: ReactWrapper, columnId: string, nextIndex: number ) { @@ -481,38 +483,6 @@ describe('EuiDataGrid', () => { expect(component).toMatchSnapshot(); }); - it('renders with appropriate role structure', () => { - const component = render( - {}, - }} - rowCount={3} - renderCellValue={({ rowIndex, columnId }) => - `${rowIndex}, ${columnId}` - } - /> - ); - - // purposefully not using data-test-subj attrs to test role semantics - const grid = component.find('[role="grid"]'); - const rows = grid.children('[role="row"]'); // technically, this test should also allow role=rowgroup but we don't currently use rowgroups - - expect(rows.length).not.toBe(0); - expect(grid.children().length).toBe(rows.length); - - rows.each((i, element) => { - const $element = cheerio(element); - const allCells = $element.children( - '[role="columnheader"], [role="rowheader"], [role="gridcell"]' - ); - expect($element.children().length).toBe(allCells.length); - }); - }); - it('renders and applies custom props', () => { const component = mount( { ).toMatchInlineSnapshot(` Array [ Object { - "className": "euiDataGridRowCell customClass", + "className": "euiDataGridRowCell euiDataGridRowCell--firstColumn customClass", "data-test-subj": "dataGridRowCell", "onBlur": [Function], "onFocus": [Function], "onKeyDown": [Function], "role": "gridcell", "style": Object { - "color": "red", - "width": "100px", + "height": 34, + "left": 0, + "position": "absolute", + "top": "0px", + "width": 100, }, "tabIndex": -1, }, Object { - "className": "euiDataGridRowCell customClass", + "className": "euiDataGridRowCell euiDataGridRowCell--lastColumn customClass", "data-test-subj": "dataGridRowCell", "onBlur": [Function], "onFocus": [Function], "onKeyDown": [Function], "role": "gridcell", "style": Object { - "color": "blue", - "width": "100px", + "height": 34, + "left": 100, + "position": "absolute", + "top": "0px", + "width": 100, }, "tabIndex": -1, }, Object { - "className": "euiDataGridRowCell customClass", + "className": "euiDataGridRowCell euiDataGridRowCell--stripe euiDataGridRowCell--firstColumn customClass", "data-test-subj": "dataGridRowCell", "onBlur": [Function], "onFocus": [Function], "onKeyDown": [Function], "role": "gridcell", "style": Object { - "color": "red", - "width": "100px", + "height": 34, + "left": 0, + "position": "absolute", + "top": "34px", + "width": 100, }, "tabIndex": -1, }, Object { - "className": "euiDataGridRowCell customClass", + "className": "euiDataGridRowCell euiDataGridRowCell--stripe euiDataGridRowCell--lastColumn customClass", "data-test-subj": "dataGridRowCell", "onBlur": [Function], "onFocus": [Function], "onKeyDown": [Function], "role": "gridcell", "style": Object { - "color": "blue", - "width": "100px", + "height": 34, + "left": 100, + "position": "absolute", + "top": "34px", + "width": 100, }, "tabIndex": -1, }, @@ -753,6 +735,7 @@ describe('EuiDataGrid', () => { ).toBe(0); // style selector + component.debug(); expect( findTestSubject(component, 'dataGridStyleSelectorButton').length ).toBe(1); @@ -763,7 +746,7 @@ describe('EuiDataGrid', () => { ).toBe(1); }); - describe('schema schema classnames', () => { + describe('schema classnames', () => { it('applies classnames from explicit schemas', () => { const component = mount( { .map((x) => x.props().className); expect(gridCellClassNames).toMatchInlineSnapshot(` Array [ - "euiDataGridRowCell euiDataGridRowCell--numeric", - "euiDataGridRowCell euiDataGridRowCell--customFormatName", - "euiDataGridRowCell euiDataGridRowCell--numeric", - "euiDataGridRowCell euiDataGridRowCell--customFormatName", - "euiDataGridRowCell euiDataGridRowCell--numeric", - "euiDataGridRowCell euiDataGridRowCell--customFormatName", + "euiDataGridRowCell--firstColumn", + "euiDataGridRowCell euiDataGridRowCell--numeric euiDataGridRowCell--firstColumn", + "euiDataGridRowCell--lastColumn", + "euiDataGridRowCell euiDataGridRowCell--customFormatName euiDataGridRowCell--lastColumn", + "euiDataGridRowCell--stripe euiDataGridRowCell--firstColumn", + "euiDataGridRowCell euiDataGridRowCell--numeric euiDataGridRowCell--stripe euiDataGridRowCell--firstColumn", + "euiDataGridRowCell--stripe euiDataGridRowCell--lastColumn", + "euiDataGridRowCell euiDataGridRowCell--customFormatName euiDataGridRowCell--stripe euiDataGridRowCell--lastColumn", + "euiDataGridRowCell--firstColumn", + "euiDataGridRowCell euiDataGridRowCell--numeric euiDataGridRowCell--firstColumn", + "euiDataGridRowCell--lastColumn", + "euiDataGridRowCell euiDataGridRowCell--customFormatName euiDataGridRowCell--lastColumn", ] `); }); @@ -826,12 +815,12 @@ describe('EuiDataGrid', () => { .map((x) => x.props().className); expect(gridCellClassNames).toMatchInlineSnapshot(` Array [ - "euiDataGridRowCell euiDataGridRowCell--numeric", + "euiDataGridRowCell euiDataGridRowCell--numeric euiDataGridRowCell--firstColumn", "euiDataGridRowCell euiDataGridRowCell--boolean", - "euiDataGridRowCell", - "euiDataGridRowCell euiDataGridRowCell--numeric", - "euiDataGridRowCell euiDataGridRowCell--boolean", - "euiDataGridRowCell", + "euiDataGridRowCell euiDataGridRowCell--lastColumn", + "euiDataGridRowCell euiDataGridRowCell--numeric euiDataGridRowCell--stripe euiDataGridRowCell--firstColumn", + "euiDataGridRowCell euiDataGridRowCell--boolean euiDataGridRowCell--stripe", + "euiDataGridRowCell euiDataGridRowCell--stripe euiDataGridRowCell--lastColumn", ] `); }); @@ -858,10 +847,10 @@ describe('EuiDataGrid', () => { .map((x) => x.props().className); expect(gridCellClassNames).toMatchInlineSnapshot(` Array [ - "euiDataGridRowCell euiDataGridRowCell--numeric", - "euiDataGridRowCell euiDataGridRowCell--alphanumeric", - "euiDataGridRowCell euiDataGridRowCell--numeric", - "euiDataGridRowCell euiDataGridRowCell--alphanumeric", + "euiDataGridRowCell euiDataGridRowCell--numeric euiDataGridRowCell--firstColumn", + "euiDataGridRowCell euiDataGridRowCell--alphanumeric euiDataGridRowCell--lastColumn", + "euiDataGridRowCell euiDataGridRowCell--numeric euiDataGridRowCell--stripe euiDataGridRowCell--firstColumn", + "euiDataGridRowCell euiDataGridRowCell--alphanumeric euiDataGridRowCell--stripe euiDataGridRowCell--lastColumn", ] `); }); @@ -895,7 +884,7 @@ describe('EuiDataGrid', () => { .map((x) => x.props().className); expect(gridCellClassNames).toMatchInlineSnapshot(` Array [ - "euiDataGridRowCell euiDataGridRowCell--numeric", + "euiDataGridRowCell euiDataGridRowCell--numeric euiDataGridRowCell--firstColumn", "euiDataGridRowCell euiDataGridRowCell--boolean", "euiDataGridRowCell euiDataGridRowCell--currency", "euiDataGridRowCell euiDataGridRowCell--datetime", @@ -943,8 +932,8 @@ describe('EuiDataGrid', () => { .map((x) => x.props().className); expect(gridCellClassNames).toMatchInlineSnapshot(` Array [ - "euiDataGridRowCell euiDataGridRowCell--numeric", - "euiDataGridRowCell euiDataGridRowCell--ipaddress", + "euiDataGridRowCell euiDataGridRowCell--numeric euiDataGridRowCell--firstColumn", + "euiDataGridRowCell euiDataGridRowCell--ipaddress euiDataGridRowCell--lastColumn", ] `); }); @@ -974,6 +963,14 @@ describe('EuiDataGrid', () => { "Column 1", "Column 2", ], + Array [ + "Hello, Row 0-Column 1!", + "Hello, Row 0-Column 2!", + ], + Array [ + "Hello, Row 1-Column 1!", + "Hello, Row 1-Column 2!", + ], ] `); }); @@ -2060,7 +2057,7 @@ describe('EuiDataGrid', () => { }); describe('keyboard controls', () => { - it('supports simple arrow navigation', () => { + it('supports simple arrow navigation', async () => { let pagination = { pageIndex: 0, pageSize: 3, @@ -2154,9 +2151,9 @@ describe('EuiDataGrid', () => { ).toEqual('0, A'); // move down and to the end of the row - focusableCell - .simulate('keydown', { key: keys.ARROW_DOWN }) - .simulate('keydown', { key: keys.END }); + focusableCell.simulate('keydown', { key: keys.ARROW_DOWN }); + focusableCell = getFocusableCell(component); + focusableCell.simulate('keydown', { key: keys.END }); focusableCell = getFocusableCell(component); expect( focusableCell.find('[data-test-subj="cell-content"]').text() diff --git a/src/components/datagrid/data_grid.tsx b/src/components/datagrid/data_grid.tsx index e5a95612d36..e7a26ba21fe 100644 --- a/src/components/datagrid/data_grid.tsx +++ b/src/components/datagrid/data_grid.tsx @@ -662,7 +662,7 @@ export const EuiDataGrid: FunctionComponent = (props) => { } = props; const [isFullScreen, setIsFullScreen] = useState(false); - const [gridWidth, setGridWidth] = useState(IS_JEST_ENVIRONMENT ? 500 : 0); + const [gridWidth, setGridWidth] = useState(0); // default DOM layout rules allow block content to naturally expand to the full document width // while height does not automatically expand; this gives us a little extra work when managing height: @@ -670,9 +670,11 @@ export const EuiDataGrid: FunctionComponent = (props) => { // if no `height` is set - or is unset - we need to unapply any existing `gridHeight` value // this triggers a double re-render when the grid is switched from constrained to unconstrained // but in reality ... maybe don't do that? - const [gridHeight, setGridHeight] = useState(IS_JEST_ENVIRONMENT ? 500 : 0); + const [gridHeight, setGridHeight] = useState(0); const pageSize = pagination?.pageSize; - const resetGridHeight = useCallback(() => setGridHeight(0), []); + const resetGridHeight = useCallback(() => { + setGridHeight(0); + }, []); useEffect(() => { if (height == null) { // if already 0 this update will be ignored @@ -783,7 +785,9 @@ export const EuiDataGrid: FunctionComponent = (props) => { } }, [sizeIsStable, resizeRef, gridDimensions]); - const hasRoomForGridControls = gridWidth > minSizeForControls || isFullScreen; + const hasRoomForGridControls = IS_JEST_ENVIRONMENT + ? true + : gridWidth > minSizeForControls || isFullScreen; const [columnWidths, setColumnWidth] = useColumnWidths( columns, diff --git a/src/components/datagrid/data_grid_body.tsx b/src/components/datagrid/data_grid_body.tsx index b9ee10b9f96..8451a60ea17 100644 --- a/src/components/datagrid/data_grid_body.tsx +++ b/src/components/datagrid/data_grid_body.tsx @@ -263,6 +263,7 @@ const Cell: FunctionComponent = ({ const INITIAL_ROW_HEIGHT = 34; const SCROLLBAR_HEIGHT = 15; +const IS_JEST_ENVIRONMENT = global.hasOwnProperty('_isJest'); export const EuiDataGridBody: FunctionComponent = ( props @@ -542,9 +543,9 @@ export const EuiDataGridBody: FunctionComponent = ( columns.length + trailingControlColumns.length } - width={gridWidth} + width={IS_JEST_ENVIRONMENT ? 500 : gridWidth} columnWidth={getWidth} - height={height} + height={IS_JEST_ENVIRONMENT ? 500 : height} rowHeight={getRowHeight} itemData={{ setRowHeight, From adeb437a7243ff962114128ee77bf247a1e68aad Mon Sep 17 00:00:00 2001 From: Chandler Prall Date: Wed, 23 Dec 2020 12:54:38 -0700 Subject: [PATCH 26/54] Fix unit tests --- src/components/datagrid/data_grid.tsx | 16 +--------- src/components/datagrid/data_grid_body.tsx | 32 ++++++++++--------- .../datagrid/data_grid_cell_popover.tsx | 3 +- src/components/datagrid/data_grid_context.tsx | 18 ++++++++--- .../data_grid_control_header_cell.tsx | 2 +- .../datagrid/data_grid_header_cell.tsx | 2 +- src/components/datagrid/data_grid_schema.tsx | 3 +- 7 files changed, 37 insertions(+), 39 deletions(-) diff --git a/src/components/datagrid/data_grid.tsx b/src/components/datagrid/data_grid.tsx index e7a26ba21fe..382e5e3b880 100644 --- a/src/components/datagrid/data_grid.tsx +++ b/src/components/datagrid/data_grid.tsx @@ -538,24 +538,10 @@ const useFocus = ( EuiDataGridFocusedCell | undefined, (focusedCell: EuiDataGridFocusedCell) => void ] => { - const [focusedCell, _setFocusedCell] = useState< + const [focusedCell, setFocusedCell] = useState< EuiDataGridFocusedCell | undefined >(undefined); - const setFocusedCell = useCallback((focusedCell: EuiDataGridFocusedCell) => { - _setFocusedCell((previousCell) => { - // verify that the cell has changed - if ( - previousCell != null && - previousCell[0] === focusedCell[0] && - previousCell[1] === focusedCell[1] - ) { - return previousCell; - } - return focusedCell; - }); - }, []); - const previousCell = useRef(undefined); useEffect(() => { if (previousCell.current) { diff --git a/src/components/datagrid/data_grid_body.tsx b/src/components/datagrid/data_grid_body.tsx index 8451a60ea17..2663ad84812 100644 --- a/src/components/datagrid/data_grid_body.tsx +++ b/src/components/datagrid/data_grid_body.tsx @@ -57,7 +57,7 @@ import { useMutationObserver } from '../observer/mutation_observer'; import { EuiText } from '../text'; import { DataGridSortingContext, - DataGridHeaderRowHeightContext, + DataGridWrapperRowsContext, } from './data_grid_context'; import { useResizeObserver } from '../observer/resize_observer'; @@ -123,7 +123,7 @@ const DefaultColumnFormatter: EuiDataGridPopoverContent = ({ children }) => { const Cell: FunctionComponent = ({ columnIndex, - rowIndex: _rowIndex, + rowIndex: visibleRowIndex, style, data, }) => { @@ -142,12 +142,12 @@ const Cell: FunctionComponent = ({ setRowHeight, } = data; - const headerRowHeight = useContext(DataGridHeaderRowHeightContext); + const { headerRowHeight } = useContext(DataGridWrapperRowsContext); - _rowIndex += rowOffset; - const rowIndex = rowMap.hasOwnProperty(_rowIndex) - ? rowMap[_rowIndex] - : _rowIndex; + const offsetRowIndex = visibleRowIndex + rowOffset; + const rowIndex = rowMap.hasOwnProperty(offsetRowIndex) + ? rowMap[offsetRowIndex] + : offsetRowIndex; let cellContent; @@ -179,7 +179,7 @@ const Cell: FunctionComponent = ({ cellContent = ( = ({ cellContent = ( = ({ cellContent = ( = ( if (renderFooterCellValue == null) return null; return ( = ( () => forwardRef( ({ children, style, ...rest }, ref) => { - const headerRowHeight = useContext(DataGridHeaderRowHeightContext); + const { headerRowHeight, headerRow, footerRow } = useContext( + DataGridWrapperRowsContext + ); return ( <>
= ( ); } ), - [headerRow, footerRow] + [] ); const gridRef = useRef(null); @@ -533,7 +534,8 @@ export const EuiDataGridBody: FunctionComponent = ( footerRowHeight; return ( - + = ( rowCount={visibleRowIndices.length}> {Cell} - + ); }; diff --git a/src/components/datagrid/data_grid_cell_popover.tsx b/src/components/datagrid/data_grid_cell_popover.tsx index f8e9afe8864..60724d4627e 100644 --- a/src/components/datagrid/data_grid_cell_popover.tsx +++ b/src/components/datagrid/data_grid_cell_popover.tsx @@ -78,8 +78,7 @@ export function EuiDataGridCellPopover({ event.stopPropagation(); closePopover(); } - }} - > + }}> {popoverIsOpen ? ( <> diff --git a/src/components/datagrid/data_grid_context.tsx b/src/components/datagrid/data_grid_context.tsx index 49f1989658d..07f91351286 100644 --- a/src/components/datagrid/data_grid_context.tsx +++ b/src/components/datagrid/data_grid_context.tsx @@ -17,23 +17,33 @@ * under the License. */ -import React from 'react'; +import React, { ReactElement } from 'react'; import { EuiDataGridFocusedCell, EuiDataGridSorting } from './data_grid_types'; export interface DataGridFocusContextShape { setFocusedCell: (cell: EuiDataGridFocusedCell) => void; - onFocusUpdate: (cell: EuiDataGridFocusedCell, updateFocus: Function) => void; + onFocusUpdate: ( + cell: EuiDataGridFocusedCell, + updateFocus: Function + ) => () => void; } export const DataGridFocusContext = React.createContext< DataGridFocusContextShape >({ setFocusedCell: () => {}, - onFocusUpdate: () => {}, + onFocusUpdate: () => () => {}, }); export const DataGridSortingContext = React.createContext< EuiDataGridSorting | undefined >(undefined); -export const DataGridHeaderRowHeightContext = React.createContext(0); +export interface DataGridWrapperRowsContentsShape { + headerRowHeight: number; + headerRow: ReactElement; + footerRow: ReactElement | null; +} +export const DataGridWrapperRowsContext = React.createContext< + DataGridWrapperRowsContentsShape +>({ headerRow:
, headerRowHeight: 0, footerRow: null }); diff --git a/src/components/datagrid/data_grid_control_header_cell.tsx b/src/components/datagrid/data_grid_control_header_cell.tsx index ba358ef9365..f469cb702f9 100644 --- a/src/components/datagrid/data_grid_control_header_cell.tsx +++ b/src/components/datagrid/data_grid_control_header_cell.tsx @@ -176,7 +176,7 @@ export const EuiDataGridControlHeaderCell: FunctionComponent { - onFocusUpdate([index, -1], (isFocused: boolean) => { + return onFocusUpdate([index, -1], (isFocused: boolean) => { setIsFocused(isFocused); }); }, [index, onFocusUpdate]); diff --git a/src/components/datagrid/data_grid_schema.tsx b/src/components/datagrid/data_grid_schema.tsx index 6ede9cd2c39..c25e232d879 100644 --- a/src/components/datagrid/data_grid_schema.tsx +++ b/src/components/datagrid/data_grid_schema.tsx @@ -297,7 +297,8 @@ export function useDetectSchema( autoDetectSchema: boolean ) { const inMemorySkipColumns = - inMemory?.skipColumns || (emptyArray as EuiDataGridInMemory['skipColumns']); + inMemory?.skipColumns ?? + (emptyArray as NonNullable); const schema = useMemo(() => { const schema: EuiDataGridSchema = {}; From 42a0bb1de6ef959814b8da6f392a680fa9d7d8c9 Mon Sep 17 00:00:00 2001 From: Chandler Prall Date: Wed, 23 Dec 2020 13:25:33 -0700 Subject: [PATCH 27/54] Quick react hook dependency cleanup --- src/components/datagrid/data_grid_body.tsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/components/datagrid/data_grid_body.tsx b/src/components/datagrid/data_grid_body.tsx index 2663ad84812..c4012eb781f 100644 --- a/src/components/datagrid/data_grid_body.tsx +++ b/src/components/datagrid/data_grid_body.tsx @@ -386,7 +386,7 @@ export const EuiDataGridBody: FunctionComponent = ( } return rowMap; - }, [sorting, inMemoryValues, schema, schemaDetectors]); + }, [sorting, inMemoryValues, schema, schemaDetectors, inMemory?.level]); const mergedPopoverContents = useMemo( () => ({ From 33176bc48dbf1ff824a4671abd920dd3228a11df Mon Sep 17 00:00:00 2001 From: Joe Reuter Date: Tue, 12 Jan 2021 14:29:24 +0100 Subject: [PATCH 28/54] only show buttons and popover if necessary --- src/components/datagrid/data_grid_cell.tsx | 82 +++++++++++++++------- 1 file changed, 55 insertions(+), 27 deletions(-) diff --git a/src/components/datagrid/data_grid_cell.tsx b/src/components/datagrid/data_grid_cell.tsx index 2f3964e7589..14bd07f6ac9 100644 --- a/src/components/datagrid/data_grid_cell.tsx +++ b/src/components/datagrid/data_grid_cell.tsx @@ -100,6 +100,7 @@ interface EuiDataGridCellState { popoverIsOpen: boolean; // is expansion popover open isFocused: boolean; // tracks if this cell has focus or not, used to enable tabIndex on the cell isEntered: boolean; // enables focus trap for non-expandable cells with multiple interactive elements + isHovered: boolean; disableCellTabIndex: boolean; // disables tabIndex on the wrapping cell, used for focus management of a single interactive child } @@ -142,6 +143,7 @@ export class EuiDataGridCell extends Component< popoverIsOpen: false, isFocused: false, isEntered: false, + isHovered: false, disableCellTabIndex: false, }; unsubscribeCell?: Function = () => {}; @@ -251,6 +253,7 @@ export class EuiDataGridCell extends Component< if (nextState.popoverIsOpen !== this.state.popoverIsOpen) return true; if (nextState.isEntered !== this.state.isEntered) return true; if (nextState.isFocused !== this.state.isFocused) return true; + if (nextState.isHovered !== this.state.isHovered) return true; if (nextState.disableCellTabIndex !== this.state.disableCellTabIndex) return true; @@ -457,6 +460,13 @@ export class EuiDataGridCell extends Component< ); + const showCellButtons = + this.state.isFocused || this.state.isEntered || this.state.isHovered; + + if (showCellButtons) { + console.log('showing cell buttons for ' + rowIndex + ',' + colIndex); + } + if (isExpandable || (column && column.cellActions)) { anchorContent = (
@@ -478,39 +488,49 @@ export class EuiDataGridCell extends Component< ); }} - this.setState({ popoverIsOpen: false })} - onExpandClick={() => { - this.setState(({ popoverIsOpen }) => ({ - popoverIsOpen: !popoverIsOpen, - })); - }} - /> + {showCellButtons && ( + this.setState({ popoverIsOpen: false })} + onExpandClick={() => { + this.setState(({ popoverIsOpen }) => ({ + popoverIsOpen: !popoverIsOpen, + })); + }} + /> + )}
); } let innerContent = anchorContent; if (isExpandable || (column && column.cellActions)) { - innerContent = ( -
- this.setState({ popoverIsOpen: false })} - column={column} - panelRefFn={(ref) => (this.popoverPanelRef.current = ref)} - popoverIsOpen={this.state.popoverIsOpen} - rowIndex={rowIndex} - renderCellValue={rest.renderCellValue} - popoverContent={PopoverContent} - /> -
- ); + if (this.state.popoverIsOpen) { + innerContent = ( +
+ this.setState({ popoverIsOpen: false })} + column={column} + panelRefFn={(ref) => (this.popoverPanelRef.current = ref)} + popoverIsOpen={this.state.popoverIsOpen} + rowIndex={rowIndex} + renderCellValue={rest.renderCellValue} + popoverContent={PopoverContent} + /> +
+ ); + } else { + innerContent = ( +
+
{anchorContent}
+
+ ); + } } return ( @@ -524,6 +544,14 @@ export class EuiDataGridCell extends Component< data-test-subj="dataGridRowCell" onKeyDown={handleCellKeyDown} onFocus={this.onFocus} + onMouseEnter={() => { + console.log('hovering'); + this.setState({ isHovered: true }); + }} + onMouseLeave={() => { + console.log('leaving'); + this.setState({ isHovered: false }); + }} onBlur={this.onBlur}> {innerContent}
From 1f3ba207bb6a0be39077125ef4e9ff1b2663c264 Mon Sep 17 00:00:00 2001 From: Joe Reuter Date: Tue, 12 Jan 2021 14:45:31 +0100 Subject: [PATCH 29/54] improve --- src/components/datagrid/data_grid_cell.tsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/components/datagrid/data_grid_cell.tsx b/src/components/datagrid/data_grid_cell.tsx index 14bd07f6ac9..864f44f1029 100644 --- a/src/components/datagrid/data_grid_cell.tsx +++ b/src/components/datagrid/data_grid_cell.tsx @@ -461,7 +461,7 @@ export class EuiDataGridCell extends Component< ); const showCellButtons = - this.state.isFocused || this.state.isEntered || this.state.isHovered; + this.state.isFocused || this.state.isEntered || this.state.isHovered || this.state.popoverIsOpen; if (showCellButtons) { console.log('showing cell buttons for ' + rowIndex + ',' + colIndex); From 3c47649a0cb8e0c34a3a2312fd7a8e0523752520 Mon Sep 17 00:00:00 2001 From: Joe Reuter Date: Tue, 12 Jan 2021 16:35:07 +0100 Subject: [PATCH 30/54] make sure buttons and popover are only rendered if necessary --- .../datagrid/_data_grid_data_row.scss | 33 +++++----- src/components/datagrid/data_grid_cell.tsx | 62 ++++++++++++++----- .../datagrid/data_grid_cell_buttons.tsx | 27 ++++---- 3 files changed, 79 insertions(+), 43 deletions(-) diff --git a/src/components/datagrid/_data_grid_data_row.scss b/src/components/datagrid/_data_grid_data_row.scss index a70a89f09b4..f890b14fbc1 100644 --- a/src/components/datagrid/_data_grid_data_row.scss +++ b/src/components/datagrid/_data_grid_data_row.scss @@ -34,30 +34,15 @@ margin-top: -1px; } - &:focus, - &:hover { - - .euiDataGridRowCell__expandButtonIcon { - margin-left: $euiDataGridCellPaddingM; - width: $euiSizeM; - background: $euiColorPrimary; // Needed to prevent the bg-color of .euiButtonIcon--text:focus - } - - .euiDataGridRowCell__actionButtonIcon { - margin-left: $euiDataGridCellPaddingM; - width: $euiSizeM; - } - } - // Only add the transition effect on hover, so that it is instantaneous on focus // Long delays on hover to mitigate the accordion effect &:hover { .euiDataGridRowCell__expandButtonIcon { - transition: margin $euiAnimSpeedFast 1000ms, width $euiAnimSpeedFast 1000ms; + transition: margin $euiAnimSpeedFast 500ms, width $euiAnimSpeedFast 500ms; } .euiDataGridRowCell__actionButtonIcon { - transition: margin $euiAnimSpeedFast 1000ms, width $euiAnimSpeedFast 1000ms; + transition: margin $euiAnimSpeedFast 500ms, width $euiAnimSpeedFast 500ms; } } @@ -116,6 +101,20 @@ .euiDataGridRowCell__expandButton { display: flex; flex-grow: 0; + + &.euiDataGridRowCell__expandButton-isHoveredOrFocused { + .euiDataGridRowCell__expandButtonIcon { + margin-left: $euiDataGridCellPaddingM; + width: $euiSizeM; + background: $euiColorPrimary; // Needed to prevent the bg-color of .euiButtonIcon--text:focus + } + + .euiDataGridRowCell__actionButtonIcon { + margin-left: $euiDataGridCellPaddingM; + width: $euiSizeM; + } + } + } .euiDataGridRowCell__expandButtonIcon { diff --git a/src/components/datagrid/data_grid_cell.tsx b/src/components/datagrid/data_grid_cell.tsx index 864f44f1029..899fde2c2b8 100644 --- a/src/components/datagrid/data_grid_cell.tsx +++ b/src/components/datagrid/data_grid_cell.tsx @@ -98,9 +98,11 @@ export interface EuiDataGridCellProps { interface EuiDataGridCellState { cellProps: CommonProps & HTMLAttributes; popoverIsOpen: boolean; // is expansion popover open + renderPopoverOpen: boolean; // wait one render cycle to actuall render the popover as open isFocused: boolean; // tracks if this cell has focus or not, used to enable tabIndex on the cell isEntered: boolean; // enables focus trap for non-expandable cells with multiple interactive elements isHovered: boolean; + renderIsHovered: boolean; disableCellTabIndex: boolean; // disables tabIndex on the wrapping cell, used for focus management of a single interactive child } @@ -141,9 +143,11 @@ export class EuiDataGridCell extends Component< state: EuiDataGridCellState = { cellProps: {}, popoverIsOpen: false, + renderPopoverOpen: false, isFocused: false, isEntered: false, isHovered: false, + renderIsHovered: false, disableCellTabIndex: false, }; unsubscribeCell?: Function = () => {}; @@ -251,9 +255,12 @@ export class EuiDataGridCell extends Component< if (nextState.cellProps !== this.state.cellProps) return true; if (nextState.popoverIsOpen !== this.state.popoverIsOpen) return true; + if (nextState.renderPopoverOpen !== this.state.renderPopoverOpen) + return true; if (nextState.isEntered !== this.state.isEntered) return true; if (nextState.isFocused !== this.state.isFocused) return true; if (nextState.isHovered !== this.state.isHovered) return true; + if (nextState.renderIsHovered !== this.state.renderIsHovered) return true; if (nextState.disableCellTabIndex !== this.state.disableCellTabIndex) return true; @@ -313,6 +320,14 @@ export class EuiDataGridCell extends Component< } }; + closePopover = () => { + this.setState({ popoverIsOpen: false }, () => + this.setState(() => ({ + renderPopoverOpen: false, + })) + ) + }; + render() { const { width, @@ -355,7 +370,11 @@ export class EuiDataGridCell extends Component< case keys.ENTER: case keys.F2: event.preventDefault(); - this.setState({ popoverIsOpen: true }); + this.setState({ popoverIsOpen: true }, () => + this.setState(({ popoverIsOpen }) => ({ + renderPopoverOpen: popoverIsOpen, + })) + ); break; } } else { @@ -461,11 +480,15 @@ export class EuiDataGridCell extends Component< ); const showCellButtons = - this.state.isFocused || this.state.isEntered || this.state.isHovered || this.state.popoverIsOpen; - - if (showCellButtons) { - console.log('showing cell buttons for ' + rowIndex + ',' + colIndex); - } + this.state.isFocused || + this.state.isEntered || + this.state.isHovered || + this.state.popoverIsOpen; + + if (showCellButtons) { + console.log('showing cell buttons for ' + rowIndex + ',' + colIndex); + console.log('renderIsHovered:' + this.state.renderIsHovered); + } if (isExpandable || (column && column.cellActions)) { anchorContent = ( @@ -493,11 +516,18 @@ export class EuiDataGridCell extends Component< rowIndex={rowIndex} column={column} popoverIsOpen={this.state.popoverIsOpen} - closePopover={() => this.setState({ popoverIsOpen: false })} + isHoveredOrFocused={this.state.renderIsHovered} + closePopover={this.closePopover} onExpandClick={() => { - this.setState(({ popoverIsOpen }) => ({ - popoverIsOpen: !popoverIsOpen, - })); + this.setState( + ({ popoverIsOpen }) => ({ + popoverIsOpen: !popoverIsOpen, + }), + () => + this.setState(({ popoverIsOpen }) => ({ + renderPopoverOpen: popoverIsOpen, + })) + ); }} /> )} @@ -517,7 +547,7 @@ export class EuiDataGridCell extends Component< closePopover={() => this.setState({ popoverIsOpen: false })} column={column} panelRefFn={(ref) => (this.popoverPanelRef.current = ref)} - popoverIsOpen={this.state.popoverIsOpen} + popoverIsOpen={this.state.renderPopoverOpen} rowIndex={rowIndex} renderCellValue={rest.renderCellValue} popoverContent={PopoverContent} @@ -545,12 +575,14 @@ export class EuiDataGridCell extends Component< onKeyDown={handleCellKeyDown} onFocus={this.onFocus} onMouseEnter={() => { - console.log('hovering'); - this.setState({ isHovered: true }); + this.setState({ isHovered: true, renderIsHovered: false }, () => { + setTimeout(() => { + this.setState({ renderIsHovered: true }); + }, 500); + }); }} onMouseLeave={() => { - console.log('leaving'); - this.setState({ isHovered: false }); + this.setState({ isHovered: false, renderIsHovered: false }); }} onBlur={this.onBlur}> {innerContent} diff --git a/src/components/datagrid/data_grid_cell_buttons.tsx b/src/components/datagrid/data_grid_cell_buttons.tsx index ee01e8fe6a9..86cf8d6279b 100644 --- a/src/components/datagrid/data_grid_cell_buttons.tsx +++ b/src/components/datagrid/data_grid_cell_buttons.tsx @@ -16,7 +16,7 @@ * specific language governing permissions and limitations * under the License. */ -import React, { JSXElementConstructor } from 'react'; +import React, { JSXElementConstructor, useMemo } from 'react'; import { EuiDataGridColumn, EuiDataGridColumnCellAction, @@ -28,12 +28,14 @@ import { EuiButtonIcon, EuiButtonIconProps } from '../button/button_icon'; export const EuiDataGridCellButtons = ({ popoverIsOpen, + isHoveredOrFocused, closePopover, onExpandClick, column, rowIndex, }: { popoverIsOpen: boolean; + isHoveredOrFocused: boolean; closePopover: () => void; onExpandClick: () => void; column?: EuiDataGridColumn; @@ -44,6 +46,7 @@ export const EuiDataGridCellButtons = ({ }); const buttonClasses = classNames('euiDataGridRowCell__expandButton', { 'euiDataGridRowCell__expandButton-isActive': popoverIsOpen, + 'euiDataGridRowCell__expandButton-isHoveredOrFocused': isHoveredOrFocused, }); const expandButton = ( ); - const ButtonComponent = (props: EuiButtonIconProps) => ( - - ); - const additionalButtons = - column && Array.isArray(column.cellActions) + const additionalButtons = useMemo(() => { + const ButtonComponent = (props: EuiButtonIconProps) => ( + + ); + return column && Array.isArray(column.cellActions) ? column.cellActions.map( (Action: EuiDataGridColumnCellAction, idx: number) => { // React is more permissible than the TS types indicate @@ -92,6 +95,8 @@ export const EuiDataGridCellButtons = ({ } ) : []; + }, [column, rowIndex, closePopover]); + return (
{[...additionalButtons, expandButton]}
); From f45009e1b446e35ab49f24f8faf568050e7f3722 Mon Sep 17 00:00:00 2001 From: Chandler Prall Date: Tue, 12 Jan 2021 14:33:08 -0700 Subject: [PATCH 31/54] Refactor grid container height/width detection, ignore datagrid pages from AXE automation testing --- scripts/a11y-testing.js | 8 + src-docs/src/views/datagrid/virtualization.js | 6 + .../__snapshots__/data_grid.test.tsx.snap | 2778 +++++++++-------- src/components/datagrid/data_grid.tsx | 52 +- src/components/datagrid/data_grid_body.tsx | 132 +- 5 files changed, 1488 insertions(+), 1488 deletions(-) diff --git a/scripts/a11y-testing.js b/scripts/a11y-testing.js index 39c9f5378b1..81f5c631533 100644 --- a/scripts/a11y-testing.js +++ b/scripts/a11y-testing.js @@ -40,6 +40,14 @@ const docsPages = async (root, page) => { `${root}#/forms/date-picker`, `${root}#/forms/suggest`, `${root}#/forms/super-date-picker`, + `${root}#/tabular-content/data-grid`, + `${root}#/tabular-content/data-grid-in-memory-settings`, + `${root}#/tabular-content/data-grid-schemas-and-popovers`, + `${root}#/tabular-content/data-grid-focus`, + `${root}#/tabular-content/data-grid-styling-and-control`, + `${root}#/tabular-content/data-grid-control-columns`, + `${root}#/tabular-content/data-grid-footer-row`, + `${root}#/tabular-content/data-grid-virtualization`, `${root}#/elastic-charts/creating-charts`, `${root}#/elastic-charts/part-to-whole-comparisons`, `${root}#/utilities/css-utility-classes`, diff --git a/src-docs/src/views/datagrid/virtualization.js b/src-docs/src/views/datagrid/virtualization.js index d0cfcb9bb24..0fab30860ea 100644 --- a/src-docs/src/views/datagrid/virtualization.js +++ b/src-docs/src/views/datagrid/virtualization.js @@ -174,6 +174,12 @@ export default () => { unconstrained is ignored. + // this is for example only, don't switch between controlled & uncontrolled heights + key={ + height === 'height-unconstrained' ? 'unconstrained' : 'constrained' + } aria-label="Virtualized data grid demo" height={dimensionSizes[height]} width={dimensionSizes[width]} diff --git a/src/components/datagrid/__snapshots__/data_grid.test.tsx.snap b/src/components/datagrid/__snapshots__/data_grid.test.tsx.snap index cc2e4907ec5..9a690025567 100644 --- a/src/components/datagrid/__snapshots__/data_grid.test.tsx.snap +++ b/src/components/datagrid/__snapshots__/data_grid.test.tsx.snap @@ -1047,426 +1047,430 @@ Array [ tabindex="0" >
-
- -
-
+
+
+ +
+
+ +
-
- +
+
+ +
+
+ +
-
-
-

- - Row: 1, Column: 1: - -

- 0, A + class="euiDataGridRowCell__expandContent" + > +

+ + Row: 1, Column: 1: + +

+
+ 0, A +
-
-
- + class="euiButtonIcon euiButtonIcon--ghost euiDataGridRowCell__expandButtonIcon" + tabindex="-1" + title="Click or hit enter to interact with cell content" + type="button" + > +
-
-
-

- - Row: 1, Column: 2: - -

- 0, B + class="euiDataGridRowCell__expandContent" + > +

+ + Row: 1, Column: 2: + +

+
+ 0, B +
-
-
- + class="euiButtonIcon euiButtonIcon--ghost euiDataGridRowCell__expandButtonIcon" + tabindex="-1" + title="Click or hit enter to interact with cell content" + type="button" + > +
-
-
-

- - Row: 2, Column: 1: - -

- 1, A + class="euiDataGridRowCell__expandContent" + > +

+ + Row: 2, Column: 1: + +

+
+ 1, A +
-
-
- + class="euiButtonIcon euiButtonIcon--ghost euiDataGridRowCell__expandButtonIcon" + tabindex="-1" + title="Click or hit enter to interact with cell content" + type="button" + > +
-
-
-

- - Row: 2, Column: 2: - -

- 1, B + class="euiDataGridRowCell__expandContent" + > +

+ + Row: 2, Column: 2: + +

+
+ 1, B +
-
-
- + class="euiButtonIcon euiButtonIcon--ghost euiDataGridRowCell__expandButtonIcon" + tabindex="-1" + title="Click or hit enter to interact with cell content" + type="button" + > +
-
-
-

- - Row: 3, Column: 1: - -

- 2, A + class="euiDataGridRowCell__expandContent" + > +

+ + Row: 3, Column: 1: + +

+
+ 2, A +
-
-
- + class="euiButtonIcon euiButtonIcon--ghost euiDataGridRowCell__expandButtonIcon" + tabindex="-1" + title="Click or hit enter to interact with cell content" + type="button" + > +
-
-
-

- - Row: 3, Column: 2: - -

- 2, B + class="euiDataGridRowCell__expandContent" + > +

+ + Row: 3, Column: 2: + +

+
+ 2, B +
-
-
- + class="euiButtonIcon euiButtonIcon--ghost euiDataGridRowCell__expandButtonIcon" + tabindex="-1" + title="Click or hit enter to interact with cell content" + type="button" + > +
@@ -1612,743 +1616,747 @@ Array [ tabindex="0" >
-
- - leading heading - -
-
-
-
+
+
- -
-
+
+
+ +
+
+ +
-
- -
-
+
+
+ +
+
+ +
- - trailing heading - +
+ + trailing heading + +
-
-
-
-
+
+
-

- - Row: 1, Column: 1: - -

- 0 +

+ + Row: 1, Column: 1: + +

+
+ 0 +
+
-
-
-
-

- - Row: 1, Column: 2: - -

- 0, A + class="euiDataGridRowCell__expandContent" + > +

+ + Row: 1, Column: 2: + +

+
+ 0, A +
-
-
- + class="euiButtonIcon euiButtonIcon--ghost euiDataGridRowCell__expandButtonIcon" + tabindex="-1" + title="Click or hit enter to interact with cell content" + type="button" + > +
-
-
-

- - Row: 1, Column: 3: - -

- 0, B + class="euiDataGridRowCell__expandContent" + > +

+ + Row: 1, Column: 3: + +

+
+ 0, B +
-
-
- + class="euiButtonIcon euiButtonIcon--ghost euiDataGridRowCell__expandButtonIcon" + tabindex="-1" + title="Click or hit enter to interact with cell content" + type="button" + > +
-
-
-
-
+
+
-

- - Row: 1, Column: 4: - -

- 0 +

+ + Row: 1, Column: 4: + +

+
+ 0 +
+
-
-
-
-
-
+
+
-

- - Row: 2, Column: 1: - -

- 1 +

+ + Row: 2, Column: 1: + +

+
+ 1 +
+
-
-
-
-

- - Row: 2, Column: 2: - -

- 1, A + class="euiDataGridRowCell__expandContent" + > +

+ + Row: 2, Column: 2: + +

+
+ 1, A +
-
-
- + class="euiButtonIcon euiButtonIcon--ghost euiDataGridRowCell__expandButtonIcon" + tabindex="-1" + title="Click or hit enter to interact with cell content" + type="button" + > +
-
-
-

- - Row: 2, Column: 3: - -

- 1, B + class="euiDataGridRowCell__expandContent" + > +

+ + Row: 2, Column: 3: + +

+
+ 1, B +
-
-
- + class="euiButtonIcon euiButtonIcon--ghost euiDataGridRowCell__expandButtonIcon" + tabindex="-1" + title="Click or hit enter to interact with cell content" + type="button" + > +
-
-
-
-
+
+
-

- - Row: 2, Column: 4: - -

- 1 +

+ + Row: 2, Column: 4: + +

+
+ 1 +
+
-
-
-
-
-
+
+
-

- - Row: 3, Column: 1: - -

- 2 +

+ + Row: 3, Column: 1: + +

+
+ 2 +
+
-
-
-
-

- - Row: 3, Column: 2: - -

- 2, A + class="euiDataGridRowCell__expandContent" + > +

+ + Row: 3, Column: 2: + +

+
+ 2, A +
-
-
- + class="euiButtonIcon euiButtonIcon--ghost euiDataGridRowCell__expandButtonIcon" + tabindex="-1" + title="Click or hit enter to interact with cell content" + type="button" + > +
-
-
-

- - Row: 3, Column: 3: - -

- 2, B + class="euiDataGridRowCell__expandContent" + > +

+ + Row: 3, Column: 3: + +

+
+ 2, B +
-
-
- + class="euiButtonIcon euiButtonIcon--ghost euiDataGridRowCell__expandButtonIcon" + tabindex="-1" + title="Click or hit enter to interact with cell content" + type="button" + > +
-
-
-
-
+
+
-

- - Row: 3, Column: 4: - -

- 2 +

+ + Row: 3, Column: 4: + +

+
+ 2 +
+
-
@@ -2486,428 +2494,432 @@ Array [ tabindex="0" >
-
- -
-
+
+
+ +
+
+ +
- + +
-
-
-

- - Row: 1, Column: 1: - -

- 0, A + class="euiDataGridRowCell__expandContent" + > +

+ + Row: 1, Column: 1: + +

+
+ 0, A +
-
-
- + class="euiButtonIcon euiButtonIcon--ghost euiDataGridRowCell__expandButtonIcon" + tabindex="-1" + title="Click or hit enter to interact with cell content" + type="button" + > +
-
-
-

- - Row: 1, Column: 2: - -

- 0, B + class="euiDataGridRowCell__expandContent" + > +

+ + Row: 1, Column: 2: + +

+
+ 0, B +
-
-
- + class="euiButtonIcon euiButtonIcon--ghost euiDataGridRowCell__expandButtonIcon" + tabindex="-1" + title="Click or hit enter to interact with cell content" + type="button" + > +
-
-
-

- - Row: 2, Column: 1: - -

- 1, A + class="euiDataGridRowCell__expandContent" + > +

+ + Row: 2, Column: 1: + +

+
+ 1, A +
-
-
- + class="euiButtonIcon euiButtonIcon--ghost euiDataGridRowCell__expandButtonIcon" + tabindex="-1" + title="Click or hit enter to interact with cell content" + type="button" + > +
-
-
-

- - Row: 2, Column: 2: - -

- 1, B + class="euiDataGridRowCell__expandContent" + > +

+ + Row: 2, Column: 2: + +

+
+ 1, B +
-
-
- + class="euiButtonIcon euiButtonIcon--ghost euiDataGridRowCell__expandButtonIcon" + tabindex="-1" + title="Click or hit enter to interact with cell content" + type="button" + > +
-
-
-

- - Row: 3, Column: 1: - -

- 2, A + class="euiDataGridRowCell__expandContent" + > +

+ + Row: 3, Column: 1: + +

+
+ 2, A +
-
-
- + class="euiButtonIcon euiButtonIcon--ghost euiDataGridRowCell__expandButtonIcon" + tabindex="-1" + title="Click or hit enter to interact with cell content" + type="button" + > +
-
-
-

- - Row: 3, Column: 2: - -

- 2, B + class="euiDataGridRowCell__expandContent" + > +

+ + Row: 3, Column: 2: + +

+
+ 2, B +
-
-
- + class="euiButtonIcon euiButtonIcon--ghost euiDataGridRowCell__expandButtonIcon" + tabindex="-1" + title="Click or hit enter to interact with cell content" + type="button" + > +
@@ -3050,426 +3062,430 @@ Array [ tabindex="0" >
-
- -
-
+
+
+ +
+
+ +
-
- +
+
+ +
+
+ +
-
-
-

- - Row: 1, Column: 1: - -

- 0, A + class="euiDataGridRowCell__expandContent" + > +

+ + Row: 1, Column: 1: + +

+
+ 0, A +
-
-
- + class="euiButtonIcon euiButtonIcon--ghost euiDataGridRowCell__expandButtonIcon" + tabindex="-1" + title="Click or hit enter to interact with cell content" + type="button" + > +
-
-
-

- - Row: 1, Column: 2: - -

- 0, B + class="euiDataGridRowCell__expandContent" + > +

+ + Row: 1, Column: 2: + +

+
+ 0, B +
-
-
- + class="euiButtonIcon euiButtonIcon--ghost euiDataGridRowCell__expandButtonIcon" + tabindex="-1" + title="Click or hit enter to interact with cell content" + type="button" + > +
-
-
-

- - Row: 2, Column: 1: - -

- 1, A + class="euiDataGridRowCell__expandContent" + > +

+ + Row: 2, Column: 1: + +

+
+ 1, A +
-
-
- + class="euiButtonIcon euiButtonIcon--ghost euiDataGridRowCell__expandButtonIcon" + tabindex="-1" + title="Click or hit enter to interact with cell content" + type="button" + > +
-
-
-

- - Row: 2, Column: 2: - -

- 1, B + class="euiDataGridRowCell__expandContent" + > +

+ + Row: 2, Column: 2: + +

+
+ 1, B +
-
-
- + class="euiButtonIcon euiButtonIcon--ghost euiDataGridRowCell__expandButtonIcon" + tabindex="-1" + title="Click or hit enter to interact with cell content" + type="button" + > +
-
-
-

- - Row: 3, Column: 1: - -

- 2, A + class="euiDataGridRowCell__expandContent" + > +

+ + Row: 3, Column: 1: + +

+
+ 2, A +
-
-
- + class="euiButtonIcon euiButtonIcon--ghost euiDataGridRowCell__expandButtonIcon" + tabindex="-1" + title="Click or hit enter to interact with cell content" + type="button" + > +
-
-
-

- - Row: 3, Column: 2: - -

- 2, B + class="euiDataGridRowCell__expandContent" + > +

+ + Row: 3, Column: 2: + +

+
+ 2, B +
-
-
- + class="euiButtonIcon euiButtonIcon--ghost euiDataGridRowCell__expandButtonIcon" + tabindex="-1" + title="Click or hit enter to interact with cell content" + type="button" + > +
diff --git a/src/components/datagrid/data_grid.tsx b/src/components/datagrid/data_grid.tsx index 382e5e3b880..e6ffd45478b 100644 --- a/src/components/datagrid/data_grid.tsx +++ b/src/components/datagrid/data_grid.tsx @@ -650,29 +650,6 @@ export const EuiDataGrid: FunctionComponent = (props) => { const [isFullScreen, setIsFullScreen] = useState(false); const [gridWidth, setGridWidth] = useState(0); - // default DOM layout rules allow block content to naturally expand to the full document width - // while height does not automatically expand; this gives us a little extra work when managing height: - // if a `height` prop is set, it will be applied via `style` to constrain the grid's DOM, which is reacted to with a resize observer and tracked with `gridHeight` - // if no `height` is set - or is unset - we need to unapply any existing `gridHeight` value - // this triggers a double re-render when the grid is switched from constrained to unconstrained - // but in reality ... maybe don't do that? - const [gridHeight, setGridHeight] = useState(0); - const pageSize = pagination?.pageSize; - const resetGridHeight = useCallback(() => { - setGridHeight(0); - }, []); - useEffect(() => { - if (height == null) { - // if already 0 this update will be ignored - // if not already 0, this resets the height constraint so a new value can be observed - setGridHeight(0); - } - }, [ - height, - pageSize, - rowCount, // recompute height if the rowCount changes - ]); - const [containerRef, _setContainerRef] = useState( null ); @@ -749,27 +726,16 @@ export const EuiDataGrid: FunctionComponent = (props) => { }; // enables/disables grid controls based on available width - const [sizeIsStable, setSizeIsStable] = useState(false); - const [resizeRef, setResizeRef] = useState(null); - const gridDimensions = useResizeObserver(resizeRef); + const gridDimensions = useResizeObserver(resizeRef, 'width'); useEffect(() => { if (resizeRef) { - if (sizeIsStable) { - const { height, width } = gridDimensions; - setGridWidth(width); - setGridHeight((currentHeight) => { - // because of a race condition between useResizeObserver which - // fires async outside of the React lifecycle - // and useEffect, we allow the height to grow but not shrink - return currentHeight < height ? height : currentHeight; - }); - } + const { width } = gridDimensions; + setGridWidth(width); } else { setGridWidth(0); - setGridHeight(0); } - }, [sizeIsStable, resizeRef, gridDimensions]); + }, [resizeRef, gridDimensions]); const hasRoomForGridControls = IS_JEST_ENVIRONMENT ? true @@ -1060,8 +1026,8 @@ export const EuiDataGrid: FunctionComponent = (props) => { headerIsInteractive, setFocusedCell )} - className="euiDataGrid__verticalScroll" - ref={setResizeRef}> + ref={setResizeRef} + className="euiDataGrid__verticalScroll">
{inMemory ? ( = (props) => { setColumnWidth={setColumnWidth} headerIsInteractive={headerIsInteractive} handleHeaderMutation={handleHeaderMutation} - gridHeight={gridHeight} - gridWidth={gridWidth} + // gridHeight={gridHeight} + // gridWidth={gridWidth} inMemoryValues={inMemoryValues} inMemory={inMemory} schemaDetectors={allSchemaDetectors} @@ -1115,8 +1081,6 @@ export const EuiDataGrid: FunctionComponent = (props) => { } rowCount={rowCount} interactiveCellId={interactiveCellId} - resetGridHeight={resetGridHeight} - setSizeIsStable={setSizeIsStable} />
diff --git a/src/components/datagrid/data_grid_body.tsx b/src/components/datagrid/data_grid_body.tsx index c4012eb781f..8d268e7338f 100644 --- a/src/components/datagrid/data_grid_body.tsx +++ b/src/components/datagrid/data_grid_body.tsx @@ -62,8 +62,6 @@ import { import { useResizeObserver } from '../observer/resize_observer'; export interface EuiDataGridBodyProps { - gridHeight?: number; - gridWidth: number; columnWidths: EuiDataGridColumnWidths; defaultColumnWidth?: number | null; leadingControlColumns?: EuiDataGridControlColumn[]; @@ -84,8 +82,6 @@ export interface EuiDataGridBodyProps { handleHeaderMutation: MutationCallback; setVisibleColumns: EuiDataGridHeaderRowProps['setVisibleColumns']; switchColumnPos: EuiDataGridHeaderRowProps['switchColumnPos']; - resetGridHeight: () => void; - setSizeIsStable: (sizeIsStable: boolean) => void; } const defaultComparator: NonNullable< @@ -269,8 +265,6 @@ export const EuiDataGridBody: FunctionComponent = ( props ) => { const { - gridHeight, - gridWidth, columnWidths, defaultColumnWidth, leadingControlColumns = [], @@ -291,12 +285,8 @@ export const EuiDataGridBody: FunctionComponent = ( handleHeaderMutation, setVisibleColumns, switchColumnPos, - resetGridHeight, - setSizeIsStable, } = props; - const hasFooterRow = renderFooterCellValue; - const [headerRowRef, setHeaderRowRef] = useState(null); const [footerRowRef, setFooterRowRef] = useState(null); @@ -307,16 +297,6 @@ export const EuiDataGridBody: FunctionComponent = ( const { height: headerRowHeight } = useResizeObserver(headerRowRef, 'height'); const { height: footerRowHeight } = useResizeObserver(footerRowRef, 'height'); - useEffect(() => { - const isHeaderStable = headerRowHeight !== 0; - const isFooterStable = !hasFooterRow || footerRowHeight !== 0; - setSizeIsStable(isHeaderStable && isFooterStable); - }, [hasFooterRow, setSizeIsStable, headerRowHeight, footerRowHeight]); - - useEffect(() => { - resetGridHeight(); - }, [resetGridHeight, headerRowHeight, footerRowHeight]); - const startRow = pagination ? pagination.pageIndex * pagination.pageSize : 0; let endRow = pagination ? (pagination.pageIndex + 1) * pagination.pageSize @@ -523,51 +503,77 @@ export const EuiDataGridBody: FunctionComponent = ( if (gridRef.current) gridRef.current.resetAfterRowIndex(0); }, [getRowHeight]); - const height = - // intentionally ignoring gridHeight if it is null/undefined/0 - // and using it only if we have found the header&footer heights - (headerRowHeight && (!hasFooterRow || footerRowHeight) && gridHeight) || - // otherwise compute the height + const unconstrainedHeight = rowHeight * visibleRowIndices.length + - SCROLLBAR_HEIGHT + - headerRowHeight + - footerRowHeight; + SCROLLBAR_HEIGHT + + headerRowHeight + + footerRowHeight; + + // unable to determine this until the container's size is known anyway + const unconstrainedWidth = 0; + + const [height, setHeight] = useState(undefined); + const [width, setWidth] = useState(undefined); + + // reset height constraint when rowCount changes + useEffect(() => { + setHeight(undefined); + }, [rowCount]); + + const wrapperRef = useRef(null); + const wrapperDimensions = useResizeObserver(wrapperRef.current); + + useEffect(() => { + const boundingRect = wrapperRef.current!.getBoundingClientRect(); + + if (boundingRect.height !== unconstrainedHeight) { + // unconstrained height did not change, apply the bounding height + setHeight(boundingRect.height); + } + if (boundingRect.width !== unconstrainedWidth) { + setWidth(boundingRect.width); + } + }, [unconstrainedHeight, wrapperDimensions]); return ( - - - {Cell} - - +
+ + + {Cell} + + +
); }; From 4e5111bf205e705ab00252314768fd6b4818a1c0 Mon Sep 17 00:00:00 2001 From: Chandler Prall Date: Tue, 12 Jan 2021 14:45:03 -0700 Subject: [PATCH 32/54] Fix datagrid setCellProps with a style object --- src/components/datagrid/data_grid.test.tsx | 4 ++++ src/components/datagrid/data_grid_cell.tsx | 2 +- 2 files changed, 5 insertions(+), 1 deletion(-) diff --git a/src/components/datagrid/data_grid.test.tsx b/src/components/datagrid/data_grid.test.tsx index 4d15e5fdc2a..5c0725bc122 100644 --- a/src/components/datagrid/data_grid.test.tsx +++ b/src/components/datagrid/data_grid.test.tsx @@ -523,6 +523,7 @@ describe('EuiDataGrid', () => { "onKeyDown": [Function], "role": "gridcell", "style": Object { + "color": "red", "height": 34, "left": 0, "position": "absolute", @@ -539,6 +540,7 @@ describe('EuiDataGrid', () => { "onKeyDown": [Function], "role": "gridcell", "style": Object { + "color": "blue", "height": 34, "left": 100, "position": "absolute", @@ -555,6 +557,7 @@ describe('EuiDataGrid', () => { "onKeyDown": [Function], "role": "gridcell", "style": Object { + "color": "red", "height": 34, "left": 0, "position": "absolute", @@ -571,6 +574,7 @@ describe('EuiDataGrid', () => { "onKeyDown": [Function], "role": "gridcell", "style": Object { + "color": "blue", "height": 34, "left": 100, "position": "absolute", diff --git a/src/components/datagrid/data_grid_cell.tsx b/src/components/datagrid/data_grid_cell.tsx index 2f3964e7589..6e63ed448ed 100644 --- a/src/components/datagrid/data_grid_cell.tsx +++ b/src/components/datagrid/data_grid_cell.tsx @@ -341,7 +341,7 @@ export class EuiDataGridCell extends Component< className: classNames(cellClasses, this.state.cellProps.className), }; - cellProps.style = { ...style, width }; + cellProps.style = { ...style, width, ...cellProps.style }; const handleCellKeyDown = (event: KeyboardEvent) => { if (isExpandable) { From 1b2788ba03d3da9e72820d7c1f1f8e3b083608e6 Mon Sep 17 00:00:00 2001 From: Chandler Prall Date: Tue, 12 Jan 2021 14:48:28 -0700 Subject: [PATCH 33/54] remove an inaccurate comment --- src/components/datagrid/data_grid_body.tsx | 1 - 1 file changed, 1 deletion(-) diff --git a/src/components/datagrid/data_grid_body.tsx b/src/components/datagrid/data_grid_body.tsx index 8d268e7338f..be1500a7079 100644 --- a/src/components/datagrid/data_grid_body.tsx +++ b/src/components/datagrid/data_grid_body.tsx @@ -527,7 +527,6 @@ export const EuiDataGridBody: FunctionComponent = ( const boundingRect = wrapperRef.current!.getBoundingClientRect(); if (boundingRect.height !== unconstrainedHeight) { - // unconstrained height did not change, apply the bounding height setHeight(boundingRect.height); } if (boundingRect.width !== unconstrainedWidth) { From c05d4937655fe26581a47207e923fc31773a8b8d Mon Sep 17 00:00:00 2001 From: Joe Reuter Date: Thu, 14 Jan 2021 14:39:37 +0100 Subject: [PATCH 34/54] clean up --- .../__snapshots__/data_grid.test.tsx.snap | 1032 +++++------------ .../datagrid/_data_grid_data_row.scss | 52 +- src/components/datagrid/data_grid.test.tsx | 27 +- src/components/datagrid/data_grid_cell.tsx | 19 +- .../datagrid/data_grid_cell_buttons.tsx | 3 - 5 files changed, 329 insertions(+), 804 deletions(-) diff --git a/src/components/datagrid/__snapshots__/data_grid.test.tsx.snap b/src/components/datagrid/__snapshots__/data_grid.test.tsx.snap index 9a690025567..3b560bd4ca4 100644 --- a/src/components/datagrid/__snapshots__/data_grid.test.tsx.snap +++ b/src/components/datagrid/__snapshots__/data_grid.test.tsx.snap @@ -1145,46 +1145,25 @@ Array [ class="euiDataGridRowCell__content" >
-
-

- - Row: 1, Column: 1: - -

-
- 0, A -
-
+ + Row: 1, Column: 1: + +

- + 0, A
@@ -1202,46 +1181,25 @@ Array [ class="euiDataGridRowCell__content" >
-
-

- - Row: 1, Column: 2: - -

-
- 0, B -
-
+ + Row: 1, Column: 2: + +

- + 0, B
@@ -1259,46 +1217,25 @@ Array [ class="euiDataGridRowCell__content" >
-
-

- - Row: 2, Column: 1: - -

-
- 1, A -
-
+ + Row: 2, Column: 1: + +

- + 1, A
@@ -1316,46 +1253,25 @@ Array [ class="euiDataGridRowCell__content" >
-
-

- - Row: 2, Column: 2: - -

-
- 1, B -
-
+ + Row: 2, Column: 2: + +

- + 1, B
@@ -1373,46 +1289,25 @@ Array [ class="euiDataGridRowCell__content" >
-
-

- - Row: 3, Column: 1: - -

-
- 2, A -
-
+ + Row: 3, Column: 1: + +

- + 2, A
@@ -1430,46 +1325,25 @@ Array [ class="euiDataGridRowCell__content" >
-
-

- - Row: 3, Column: 2: - -

-
- 2, B -
-
+ + Row: 3, Column: 2: + +

- + 2, B
@@ -1791,46 +1665,25 @@ Array [ class="euiDataGridRowCell__content" >
-
-

- - Row: 1, Column: 2: - -

-
- 0, A -
-
+ + Row: 1, Column: 2: + +

- + 0, A
@@ -1848,46 +1701,25 @@ Array [ class="euiDataGridRowCell__content" >
-
-

- - Row: 1, Column: 3: - -

-
- 0, B -
-
+ + Row: 1, Column: 3: + +

- + 0, B
@@ -1999,46 +1831,25 @@ Array [ class="euiDataGridRowCell__content" >
-
-

- - Row: 2, Column: 2: - -

-
- 1, A -
-
+ + Row: 2, Column: 2: + +

- + 1, A
@@ -2056,46 +1867,25 @@ Array [ class="euiDataGridRowCell__content" >
-
-

- - Row: 2, Column: 3: - -

-
- 1, B -
-
+ + Row: 2, Column: 3: + +

- + 1, B
@@ -2207,46 +1997,25 @@ Array [ class="euiDataGridRowCell__content" >
-
-

- - Row: 3, Column: 2: - -

-
- 2, A -
-
+ + Row: 3, Column: 2: + +

- + 2, A
@@ -2264,46 +2033,25 @@ Array [ class="euiDataGridRowCell__content" >
-
-

- - Row: 3, Column: 3: - -

-
- 2, B -
-
+ + Row: 3, Column: 3: + +

- + 2, B
@@ -2594,46 +2342,25 @@ Array [ class="euiDataGridRowCell__content" >
-
-

- - Row: 1, Column: 1: - -

-
- 0, A -
-
+ + Row: 1, Column: 1: + +

- + 0, A
@@ -2651,46 +2378,25 @@ Array [ class="euiDataGridRowCell__content" >
-
-

- - Row: 1, Column: 2: - -

-
- 0, B -
-
+ + Row: 1, Column: 2: + +

- + 0, B
@@ -2708,46 +2414,25 @@ Array [ class="euiDataGridRowCell__content" >
-
-

- - Row: 2, Column: 1: - -

-
- 1, A -
-
+ + Row: 2, Column: 1: + +

- + 1, A
@@ -2765,46 +2450,25 @@ Array [ class="euiDataGridRowCell__content" >
-
-

- - Row: 2, Column: 2: - -

-
- 1, B -
-
+ + Row: 2, Column: 2: + +

- + 1, B
@@ -2822,46 +2486,25 @@ Array [ class="euiDataGridRowCell__content" >
-
-

- - Row: 3, Column: 1: - -

-
- 2, A -
-
+ + Row: 3, Column: 1: + +

- + 2, A
@@ -2879,46 +2522,25 @@ Array [ class="euiDataGridRowCell__content" >
-
-

- - Row: 3, Column: 2: - -

-
- 2, B -
-
+ + Row: 3, Column: 2: + +

- + 2, B
@@ -3160,46 +2782,25 @@ Array [ class="euiDataGridRowCell__content" >
-
-

- - Row: 1, Column: 1: - -

-
- 0, A -
-
+ + Row: 1, Column: 1: + +

- + 0, A
@@ -3217,46 +2818,25 @@ Array [ class="euiDataGridRowCell__content" >
-
-

- - Row: 1, Column: 2: - -

-
- 0, B -
-
+ + Row: 1, Column: 2: + +

- + 0, B
@@ -3274,46 +2854,25 @@ Array [ class="euiDataGridRowCell__content" >
-
-

- - Row: 2, Column: 1: - -

-
- 1, A -
-
+ + Row: 2, Column: 1: + +

- + 1, A
@@ -3331,46 +2890,25 @@ Array [ class="euiDataGridRowCell__content" >
-
-

- - Row: 2, Column: 2: - -

-
- 1, B -
-
+ + Row: 2, Column: 2: + +

- + 1, B
@@ -3388,46 +2926,25 @@ Array [ class="euiDataGridRowCell__content" >
-
-

- - Row: 3, Column: 1: - -

-
- 2, A -
-
+ + Row: 3, Column: 1: + +

- + 2, A
@@ -3445,46 +2962,25 @@ Array [ class="euiDataGridRowCell__content" >
-
-

- - Row: 3, Column: 2: - -

-
- 2, B -
-
+ + Row: 3, Column: 2: + +

- + 2, B
diff --git a/src/components/datagrid/_data_grid_data_row.scss b/src/components/datagrid/_data_grid_data_row.scss index f890b14fbc1..07b45ece8c9 100644 --- a/src/components/datagrid/_data_grid_data_row.scss +++ b/src/components/datagrid/_data_grid_data_row.scss @@ -3,6 +3,18 @@ min-width: 100%; // Needed to prevent wraps. Inline flex is tricky } +@keyframes euiDataGridCellButtonSlideIn { + from { + margin-left: 0; + width: 0; + } + + to { + margin-left: $euiDataGridCellPaddingM; + width: $euiSizeM; + } +} + @include euiDataGridRowCell { @include euiFontSizeS; @@ -38,11 +50,31 @@ // Long delays on hover to mitigate the accordion effect &:hover { .euiDataGridRowCell__expandButtonIcon { - transition: margin $euiAnimSpeedFast 500ms, width $euiAnimSpeedFast 500ms; + animation-duration: $euiAnimSpeedFast; + animation-name: euiDataGridCellButtonSlideIn; + animation-iteration-count: 1; + animation-delay: 1000ms; + animation-fill-mode: forwards; + } + + .euiDataGridRowCell__actionButtonIcon { + animation-duration: $euiAnimSpeedFast; + animation-name: euiDataGridCellButtonSlideIn; + animation-iteration-count: 1; + animation-delay: 1000ms; + animation-fill-mode: forwards; + } + } + + &:not(:hover) { + .euiDataGridRowCell__expandButtonIcon { + margin-left: $euiDataGridCellPaddingM; + width: $euiSizeM; } .euiDataGridRowCell__actionButtonIcon { - transition: margin $euiAnimSpeedFast 500ms, width $euiAnimSpeedFast 500ms; + margin-left: $euiDataGridCellPaddingM; + width: $euiSizeM; } } @@ -74,6 +106,7 @@ white-space: nowrap; } } + } .euiDataGridRowCell__popover { @@ -102,21 +135,12 @@ display: flex; flex-grow: 0; - &.euiDataGridRowCell__expandButton-isHoveredOrFocused { - .euiDataGridRowCell__expandButtonIcon { - margin-left: $euiDataGridCellPaddingM; - width: $euiSizeM; - background: $euiColorPrimary; // Needed to prevent the bg-color of .euiButtonIcon--text:focus - } - - .euiDataGridRowCell__actionButtonIcon { - margin-left: $euiDataGridCellPaddingM; - width: $euiSizeM; - } + .euiDataGridRowCell__expandButtonIcon { + background: $euiColorPrimary; // Needed to prevent the bg-color of .euiButtonIcon--text:focus } - } + .euiDataGridRowCell__expandButtonIcon { height: $euiSizeM; min-height: $euiSizeM; diff --git a/src/components/datagrid/data_grid.test.tsx b/src/components/datagrid/data_grid.test.tsx index 5c0725bc122..380517d5dcf 100644 --- a/src/components/datagrid/data_grid.test.tsx +++ b/src/components/datagrid/data_grid.test.tsx @@ -28,6 +28,7 @@ import { import { EuiDataGridColumnResizer } from './data_grid_column_resizer'; import { keys } from '../../services'; import { act } from 'react-dom/test-utils'; +import { EuiDataGridCellButtons } from './data_grid_cell_buttons'; function getFocusableCell(component: ReactWrapper) { return findTestSubject(component, 'dataGridRowCell').find('[tabIndex=0]'); @@ -521,6 +522,8 @@ describe('EuiDataGrid', () => { "onBlur": [Function], "onFocus": [Function], "onKeyDown": [Function], + "onMouseEnter": [Function], + "onMouseLeave": [Function], "role": "gridcell", "style": Object { "color": "red", @@ -538,6 +541,8 @@ describe('EuiDataGrid', () => { "onBlur": [Function], "onFocus": [Function], "onKeyDown": [Function], + "onMouseEnter": [Function], + "onMouseLeave": [Function], "role": "gridcell", "style": Object { "color": "blue", @@ -555,6 +560,8 @@ describe('EuiDataGrid', () => { "onBlur": [Function], "onFocus": [Function], "onKeyDown": [Function], + "onMouseEnter": [Function], + "onMouseLeave": [Function], "role": "gridcell", "style": Object { "color": "red", @@ -572,6 +579,8 @@ describe('EuiDataGrid', () => { "onBlur": [Function], "onFocus": [Function], "onKeyDown": [Function], + "onMouseEnter": [Function], + "onMouseLeave": [Function], "role": "gridcell", "style": Object { "color": "blue", @@ -1986,7 +1995,7 @@ describe('EuiDataGrid', () => { }); describe('render column cell actions', () => { - it('renders various column cell actions configurations', () => { + it('renders various column cell actions configurations after cell gets hovered', async () => { const alertFn = jest.fn(); const happyFn = jest.fn(); const component = mount( @@ -2041,9 +2050,21 @@ describe('EuiDataGrid', () => { /> ); - findTestSubject(component, 'alertAction').at(1).simulate('click'); + // cell buttons should not get rendered for unfocused, unhovered cell + expect(findTestSubject(component, 'alertAction').exists()).toBe(false); + expect(findTestSubject(component, 'happyAction').exists()).toBe(false); + + await act(async () => { + findTestSubject(component, 'dataGridRowCell') + .at(1) + .prop('onMouseEnter')!({} as React.MouseEvent); + }); + + component.update(); + + findTestSubject(component, 'alertAction').at(0).simulate('click'); expect(alertFn).toHaveBeenCalledWith(1, 'A'); - findTestSubject(component, 'happyAction').at(1).simulate('click'); + findTestSubject(component, 'happyAction').at(0).simulate('click'); expect(happyFn).toHaveBeenCalledWith(1, 'A'); alertFn.mockReset(); happyFn.mockReset(); diff --git a/src/components/datagrid/data_grid_cell.tsx b/src/components/datagrid/data_grid_cell.tsx index 4d869f3a5fb..ecedaffdb9b 100644 --- a/src/components/datagrid/data_grid_cell.tsx +++ b/src/components/datagrid/data_grid_cell.tsx @@ -102,7 +102,6 @@ interface EuiDataGridCellState { isFocused: boolean; // tracks if this cell has focus or not, used to enable tabIndex on the cell isEntered: boolean; // enables focus trap for non-expandable cells with multiple interactive elements isHovered: boolean; - renderIsHovered: boolean; disableCellTabIndex: boolean; // disables tabIndex on the wrapping cell, used for focus management of a single interactive child } @@ -147,7 +146,6 @@ export class EuiDataGridCell extends Component< isFocused: false, isEntered: false, isHovered: false, - renderIsHovered: false, disableCellTabIndex: false, }; unsubscribeCell?: Function = () => {}; @@ -260,7 +258,6 @@ export class EuiDataGridCell extends Component< if (nextState.isEntered !== this.state.isEntered) return true; if (nextState.isFocused !== this.state.isFocused) return true; if (nextState.isHovered !== this.state.isHovered) return true; - if (nextState.renderIsHovered !== this.state.renderIsHovered) return true; if (nextState.disableCellTabIndex !== this.state.disableCellTabIndex) return true; @@ -296,7 +293,7 @@ export class EuiDataGridCell extends Component< }; onBlur = () => { - this.setState({ disableCellTabIndex: false }); + this.setState({ disableCellTabIndex: false, isFocused: false }); }; preventTabbing = () => { @@ -485,11 +482,6 @@ export class EuiDataGridCell extends Component< this.state.isHovered || this.state.popoverIsOpen; - if (showCellButtons) { - console.log('showing cell buttons for ' + rowIndex + ',' + colIndex); - console.log('renderIsHovered:' + this.state.renderIsHovered); - } - if (isExpandable || (column && column.cellActions)) { anchorContent = (
@@ -516,7 +508,6 @@ export class EuiDataGridCell extends Component< rowIndex={rowIndex} column={column} popoverIsOpen={this.state.popoverIsOpen} - isHoveredOrFocused={this.state.renderIsHovered} closePopover={this.closePopover} onExpandClick={() => { this.setState( @@ -575,14 +566,10 @@ export class EuiDataGridCell extends Component< onKeyDown={handleCellKeyDown} onFocus={this.onFocus} onMouseEnter={() => { - this.setState({ isHovered: true, renderIsHovered: false }, () => { - setTimeout(() => { - this.setState({ renderIsHovered: true }); - }, 500); - }); + this.setState({ isHovered: true }); }} onMouseLeave={() => { - this.setState({ isHovered: false, renderIsHovered: false }); + this.setState({ isHovered: false }); }} onBlur={this.onBlur}> {innerContent} diff --git a/src/components/datagrid/data_grid_cell_buttons.tsx b/src/components/datagrid/data_grid_cell_buttons.tsx index 86cf8d6279b..5e3d69844d4 100644 --- a/src/components/datagrid/data_grid_cell_buttons.tsx +++ b/src/components/datagrid/data_grid_cell_buttons.tsx @@ -28,14 +28,12 @@ import { EuiButtonIcon, EuiButtonIconProps } from '../button/button_icon'; export const EuiDataGridCellButtons = ({ popoverIsOpen, - isHoveredOrFocused, closePopover, onExpandClick, column, rowIndex, }: { popoverIsOpen: boolean; - isHoveredOrFocused: boolean; closePopover: () => void; onExpandClick: () => void; column?: EuiDataGridColumn; @@ -46,7 +44,6 @@ export const EuiDataGridCellButtons = ({ }); const buttonClasses = classNames('euiDataGridRowCell__expandButton', { 'euiDataGridRowCell__expandButton-isActive': popoverIsOpen, - 'euiDataGridRowCell__expandButton-isHoveredOrFocused': isHoveredOrFocused, }); const expandButton = ( Date: Thu, 14 Jan 2021 15:41:03 +0100 Subject: [PATCH 35/54] reduce rerenders --- src/components/datagrid/data_grid.tsx | 17 +--- src/components/datagrid/data_grid_body.tsx | 95 +++++++++++-------- .../resize_observer/resize_observer.tsx | 15 ++- 3 files changed, 75 insertions(+), 52 deletions(-) diff --git a/src/components/datagrid/data_grid.tsx b/src/components/datagrid/data_grid.tsx index e6ffd45478b..45afba38289 100644 --- a/src/components/datagrid/data_grid.tsx +++ b/src/components/datagrid/data_grid.tsx @@ -298,14 +298,11 @@ function renderPagination(props: EuiDataGridProps, controls: string) { } function useDefaultColumnWidth( - container: HTMLElement | null, + gridWidth: number, leadingControlColumns: EuiDataGridControlColumn[], trailingControlColumns: EuiDataGridControlColumn[], columns: EuiDataGridProps['columns'] ): number | null { - const containerSize = useResizeObserver(container, 'width'); - const gridWidth = containerSize.width; - const computeDefaultWidth = useCallback((): number | null => { if (IS_JEST_ENVIRONMENT) return 100; if (gridWidth === 0) return null; // we can't tell what size to compute yet @@ -624,6 +621,7 @@ function notifyCellOfFocusState( const emptyArrayDefault: EuiDataGridControlColumn[] = []; export const EuiDataGrid: FunctionComponent = (props) => { + console.log('render data grid'); const { leadingControlColumns = emptyArrayDefault, trailingControlColumns = emptyArrayDefault, @@ -650,13 +648,9 @@ export const EuiDataGrid: FunctionComponent = (props) => { const [isFullScreen, setIsFullScreen] = useState(false); const [gridWidth, setGridWidth] = useState(0); - const [containerRef, _setContainerRef] = useState( - null - ); const [interactiveCellId] = useState(htmlIdGenerator()()); const [headerIsInteractive, setHeaderIsInteractive] = useState(false); - const setContainerRef = useCallback((ref) => _setContainerRef(ref), []); const cellsUpdateFocus = useRef>(new Map()); @@ -727,7 +721,7 @@ export const EuiDataGrid: FunctionComponent = (props) => { // enables/disables grid controls based on available width const [resizeRef, setResizeRef] = useState(null); - const gridDimensions = useResizeObserver(resizeRef, 'width'); + const gridDimensions = useResizeObserver(resizeRef, 'width', 'main'); useEffect(() => { if (resizeRef) { const { width } = gridDimensions; @@ -808,7 +802,7 @@ export const EuiDataGrid: FunctionComponent = (props) => { // compute the default column width from the container's clientWidth and count of visible columns const defaultColumnWidth = useDefaultColumnWidth( - containerRef, + gridDimensions.width, leadingControlColumns, trailingControlColumns, orderedVisibleColumns @@ -999,7 +993,7 @@ export const EuiDataGrid: FunctionComponent = (props) => { className={classes} onKeyDown={handleGridKeyDown} style={{ width, height }} - ref={setContainerRef} + ref={setResizeRef} {...rest}> {(IS_JEST_ENVIRONMENT || defaultColumnWidth) && ( <> @@ -1026,7 +1020,6 @@ export const EuiDataGrid: FunctionComponent = (props) => { headerIsInteractive, setFocusedCell )} - ref={setResizeRef} className="euiDataGrid__verticalScroll">
{inMemory ? ( diff --git a/src/components/datagrid/data_grid_body.tsx b/src/components/datagrid/data_grid_body.tsx index be1500a7079..f999b4eca5a 100644 --- a/src/components/datagrid/data_grid_body.tsx +++ b/src/components/datagrid/data_grid_body.tsx @@ -264,6 +264,7 @@ const IS_JEST_ENVIRONMENT = global.hasOwnProperty('_isJest'); export const EuiDataGridBody: FunctionComponent = ( props ) => { + console.log('render data grid body'); const { columnWidths, defaultColumnWidth, @@ -467,7 +468,9 @@ export const EuiDataGridBody: FunctionComponent = ( const gridRef = useRef(null); useEffect(() => { - gridRef.current!.resetAfterColumnIndex(0); + if (gridRef.current) { + gridRef.current.resetAfterColumnIndex(0); + } }, [columns, columnWidths, defaultColumnWidth]); const getWidth = useCallback( @@ -521,15 +524,27 @@ export const EuiDataGridBody: FunctionComponent = ( }, [rowCount]); const wrapperRef = useRef(null); - const wrapperDimensions = useResizeObserver(wrapperRef.current); + const wrapperDimensions = useResizeObserver( + wrapperRef.current, + undefined, + 'wrapper' + ); useEffect(() => { const boundingRect = wrapperRef.current!.getBoundingClientRect(); - if (boundingRect.height !== unconstrainedHeight) { + if ( + boundingRect.height !== unconstrainedHeight && + height !== boundingRect.height + ) { + console.log('setting height'); setHeight(boundingRect.height); } - if (boundingRect.width !== unconstrainedWidth) { + if ( + boundingRect.width !== unconstrainedWidth && + width !== boundingRect.width + ) { + console.log('setting width'); setWidth(boundingRect.width); } }, [unconstrainedHeight, wrapperDimensions]); @@ -538,41 +553,43 @@ export const EuiDataGridBody: FunctionComponent = (
- - - {Cell} - - + {width && width > 0 && ( + + + {Cell} + + + )}
); }; diff --git a/src/components/observer/resize_observer/resize_observer.tsx b/src/components/observer/resize_observer/resize_observer.tsx index d3b598ecf86..d4cb1ad1294 100644 --- a/src/components/observer/resize_observer/resize_observer.tsx +++ b/src/components/observer/resize_observer/resize_observer.tsx @@ -102,7 +102,8 @@ const makeResizeObserver = (node: Element, callback: () => void) => { export const useResizeObserver = ( container: Element | null, - dimension?: 'width' | 'height' + dimension?: 'width' | 'height', + debug = '' ) => { const [size, _setSize] = useState({ width: 0, height: 0 }); @@ -120,6 +121,9 @@ export const useResizeObserver = ( _currentDimensions.current.height !== dimensions.height) ) { _currentDimensions.current = dimensions; + if (debug) { + console.log(`${debug}: setting size`); + } _setSize(dimensions); } }, @@ -131,12 +135,18 @@ export const useResizeObserver = ( // ResizeObserver's first call to the observation callback is scheduled in the future // so find the container's initial dimensions now const boundingRect = container.getBoundingClientRect(); + if (debug) { + console.log(`${debug}: initial size setting`); + } setSize({ width: boundingRect.width, height: boundingRect.height, }); const observer = makeResizeObserver(container, () => { + if (debug) { + console.log(`${debug}: observer fired`); + } const boundingRect = container.getBoundingClientRect(); setSize({ width: boundingRect.width, @@ -150,5 +160,8 @@ export const useResizeObserver = ( } }, [container, setSize]); + if (debug) { + console.log(`${debug}: ${JSON.stringify(size)}`); + } return size; }; From c0d6c4ecdaefc8b423bde6b48c3b2070bb756b82 Mon Sep 17 00:00:00 2001 From: Joe Reuter Date: Thu, 14 Jan 2021 16:56:14 +0100 Subject: [PATCH 36/54] remove console logs --- src/components/datagrid/data_grid.tsx | 3 +-- src/components/datagrid/data_grid_body.tsx | 9 +-------- src/components/datagrid/data_grid_cell.tsx | 7 ++++--- .../observer/resize_observer/resize_observer.tsx | 15 +-------------- 4 files changed, 7 insertions(+), 27 deletions(-) diff --git a/src/components/datagrid/data_grid.tsx b/src/components/datagrid/data_grid.tsx index 45afba38289..51497998157 100644 --- a/src/components/datagrid/data_grid.tsx +++ b/src/components/datagrid/data_grid.tsx @@ -621,7 +621,6 @@ function notifyCellOfFocusState( const emptyArrayDefault: EuiDataGridControlColumn[] = []; export const EuiDataGrid: FunctionComponent = (props) => { - console.log('render data grid'); const { leadingControlColumns = emptyArrayDefault, trailingControlColumns = emptyArrayDefault, @@ -721,7 +720,7 @@ export const EuiDataGrid: FunctionComponent = (props) => { // enables/disables grid controls based on available width const [resizeRef, setResizeRef] = useState(null); - const gridDimensions = useResizeObserver(resizeRef, 'width', 'main'); + const gridDimensions = useResizeObserver(resizeRef, 'width'); useEffect(() => { if (resizeRef) { const { width } = gridDimensions; diff --git a/src/components/datagrid/data_grid_body.tsx b/src/components/datagrid/data_grid_body.tsx index f999b4eca5a..792fafb6568 100644 --- a/src/components/datagrid/data_grid_body.tsx +++ b/src/components/datagrid/data_grid_body.tsx @@ -264,7 +264,6 @@ const IS_JEST_ENVIRONMENT = global.hasOwnProperty('_isJest'); export const EuiDataGridBody: FunctionComponent = ( props ) => { - console.log('render data grid body'); const { columnWidths, defaultColumnWidth, @@ -524,11 +523,7 @@ export const EuiDataGridBody: FunctionComponent = ( }, [rowCount]); const wrapperRef = useRef(null); - const wrapperDimensions = useResizeObserver( - wrapperRef.current, - undefined, - 'wrapper' - ); + const wrapperDimensions = useResizeObserver(wrapperRef.current, undefined); useEffect(() => { const boundingRect = wrapperRef.current!.getBoundingClientRect(); @@ -537,14 +532,12 @@ export const EuiDataGridBody: FunctionComponent = ( boundingRect.height !== unconstrainedHeight && height !== boundingRect.height ) { - console.log('setting height'); setHeight(boundingRect.height); } if ( boundingRect.width !== unconstrainedWidth && width !== boundingRect.width ) { - console.log('setting width'); setWidth(boundingRect.width); } }, [unconstrainedHeight, wrapperDimensions]); diff --git a/src/components/datagrid/data_grid_cell.tsx b/src/components/datagrid/data_grid_cell.tsx index ecedaffdb9b..6be4e85b131 100644 --- a/src/components/datagrid/data_grid_cell.tsx +++ b/src/components/datagrid/data_grid_cell.tsx @@ -469,9 +469,10 @@ export class EuiDataGridCell extends Component<
- ); - }} - + ); + + }} +
); diff --git a/src/components/observer/resize_observer/resize_observer.tsx b/src/components/observer/resize_observer/resize_observer.tsx index d4cb1ad1294..d3b598ecf86 100644 --- a/src/components/observer/resize_observer/resize_observer.tsx +++ b/src/components/observer/resize_observer/resize_observer.tsx @@ -102,8 +102,7 @@ const makeResizeObserver = (node: Element, callback: () => void) => { export const useResizeObserver = ( container: Element | null, - dimension?: 'width' | 'height', - debug = '' + dimension?: 'width' | 'height' ) => { const [size, _setSize] = useState({ width: 0, height: 0 }); @@ -121,9 +120,6 @@ export const useResizeObserver = ( _currentDimensions.current.height !== dimensions.height) ) { _currentDimensions.current = dimensions; - if (debug) { - console.log(`${debug}: setting size`); - } _setSize(dimensions); } }, @@ -135,18 +131,12 @@ export const useResizeObserver = ( // ResizeObserver's first call to the observation callback is scheduled in the future // so find the container's initial dimensions now const boundingRect = container.getBoundingClientRect(); - if (debug) { - console.log(`${debug}: initial size setting`); - } setSize({ width: boundingRect.width, height: boundingRect.height, }); const observer = makeResizeObserver(container, () => { - if (debug) { - console.log(`${debug}: observer fired`); - } const boundingRect = container.getBoundingClientRect(); setSize({ width: boundingRect.width, @@ -160,8 +150,5 @@ export const useResizeObserver = ( } }, [container, setSize]); - if (debug) { - console.log(`${debug}: ${JSON.stringify(size)}`); - } return size; }; From d2055596dc43d30ed21e0d4ef44faf9b3a3f7bd1 Mon Sep 17 00:00:00 2001 From: Joe Reuter Date: Thu, 14 Jan 2021 17:33:45 +0100 Subject: [PATCH 37/54] fix tests --- src/components/datagrid/data_grid.test.tsx | 8 ++++---- src/components/datagrid/data_grid_body.tsx | 2 +- .../observer/resize_observer/resize_observer.tsx | 7 ++++++- 3 files changed, 11 insertions(+), 6 deletions(-) diff --git a/src/components/datagrid/data_grid.test.tsx b/src/components/datagrid/data_grid.test.tsx index 380517d5dcf..65dc60e41d2 100644 --- a/src/components/datagrid/data_grid.test.tsx +++ b/src/components/datagrid/data_grid.test.tsx @@ -530,7 +530,7 @@ describe('EuiDataGrid', () => { "height": 34, "left": 0, "position": "absolute", - "top": "0px", + "top": "100px", "width": 100, }, "tabIndex": -1, @@ -549,7 +549,7 @@ describe('EuiDataGrid', () => { "height": 34, "left": 100, "position": "absolute", - "top": "0px", + "top": "100px", "width": 100, }, "tabIndex": -1, @@ -568,7 +568,7 @@ describe('EuiDataGrid', () => { "height": 34, "left": 0, "position": "absolute", - "top": "34px", + "top": "134px", "width": 100, }, "tabIndex": -1, @@ -587,7 +587,7 @@ describe('EuiDataGrid', () => { "height": 34, "left": 100, "position": "absolute", - "top": "34px", + "top": "134px", "width": 100, }, "tabIndex": -1, diff --git a/src/components/datagrid/data_grid_body.tsx b/src/components/datagrid/data_grid_body.tsx index 792fafb6568..8182a2ca44b 100644 --- a/src/components/datagrid/data_grid_body.tsx +++ b/src/components/datagrid/data_grid_body.tsx @@ -546,7 +546,7 @@ export const EuiDataGridBody: FunctionComponent = (
- {width && width > 0 && ( + {(IS_JEST_ENVIRONMENT || (width && width > 0)) && ( { - if (container != null) { + if (IS_JEST_ENVIRONMENT) { + setSize({ width: 100, height: 100 }); + } else if (container != null) { // ResizeObserver's first call to the observation callback is scheduled in the future // so find the container's initial dimensions now const boundingRect = container.getBoundingClientRect(); From 0d91454187ab0ad68430596f653e00f6623f29e2 Mon Sep 17 00:00:00 2001 From: Joe Reuter Date: Fri, 15 Jan 2021 14:49:14 +0100 Subject: [PATCH 38/54] convert to single mutation observer --- src/components/datagrid/data_grid_body.tsx | 110 +++++++++++++-------- src/components/datagrid/data_grid_cell.tsx | 56 ++++------- 2 files changed, 86 insertions(+), 80 deletions(-) diff --git a/src/components/datagrid/data_grid_body.tsx b/src/components/datagrid/data_grid_body.tsx index 8182a2ca44b..5cc716a75c5 100644 --- a/src/components/datagrid/data_grid_body.tsx +++ b/src/components/datagrid/data_grid_body.tsx @@ -28,6 +28,7 @@ import React, { useState, } from 'react'; import classNames from 'classnames'; +import tabbable from 'tabbable'; import { GridChildComponentProps, VariableSizeGrid as Grid, @@ -53,7 +54,10 @@ import { EuiDataGridHeaderRow, EuiDataGridHeaderRowProps, } from './data_grid_header_row'; -import { useMutationObserver } from '../observer/mutation_observer'; +import { + EuiMutationObserver, + useMutationObserver, +} from '../observer/mutation_observer'; import { EuiText } from '../text'; import { DataGridSortingContext, @@ -542,47 +546,71 @@ export const EuiDataGridBody: FunctionComponent = ( } }, [unconstrainedHeight, wrapperDimensions]); + const preventTabbing = useCallback(() => { + if (wrapperRef.current) { + const tabbables = tabbable(wrapperRef.current); + for (let i = 0; i < tabbables.length; i++) { + const element = tabbables[i]; + if (element.getAttribute('role') !== 'gridcell' && !element.dataset['euigrid-tab-managed']) { + element.setAttribute('tabIndex', '-1'); + element.setAttribute('data-datagrid-interactable', 'true'); + } + } + } + }, [wrapperRef]); + return ( -
- {(IS_JEST_ENVIRONMENT || (width && width > 0)) && ( - - - {Cell} - - + + {(mutationRef) => ( +
{ + wrapperRef.current = el; + mutationRef(el); + }}> + {(IS_JEST_ENVIRONMENT || (width && width > 0)) && ( + + + {Cell} + + + )} +
)} -
+ ); }; diff --git a/src/components/datagrid/data_grid_cell.tsx b/src/components/datagrid/data_grid_cell.tsx index 6be4e85b131..db00f988237 100644 --- a/src/components/datagrid/data_grid_cell.tsx +++ b/src/components/datagrid/data_grid_cell.tsx @@ -39,7 +39,6 @@ import { EuiDataGridColumn, EuiDataGridPopoverContent, } from './data_grid_types'; -import { EuiMutationObserver } from '../observer/mutation_observer'; import { DataGridFocusContext } from './data_grid_context'; import { EuiFocusTrap } from '../focus_trap'; import { keys } from '../../services'; @@ -322,7 +321,7 @@ export class EuiDataGridCell extends Component< this.setState(() => ({ renderPopoverOpen: false, })) - ) + ); }; render() { @@ -454,25 +453,14 @@ export class EuiDataGridCell extends Component< }} clickOutsideDisables={true}>
- - {(mutationRef) => { - return ( -
- {screenReaderPosition} -
- -
-
- ); - - }} -
+
+ {screenReaderPosition} +
+ +
+
); @@ -486,24 +474,14 @@ export class EuiDataGridCell extends Component< if (isExpandable || (column && column.cellActions)) { anchorContent = (
- - {(mutationRef) => { - return ( -
- {screenReaderPosition} -
- -
-
- ); - }} -
+
+ {screenReaderPosition} +
+ +
+
{showCellButtons && ( Date: Fri, 15 Jan 2021 12:58:22 -0700 Subject: [PATCH 39/54] Small PR feedback cleanup --- src/components/datagrid/data_grid.tsx | 3 --- 1 file changed, 3 deletions(-) diff --git a/src/components/datagrid/data_grid.tsx b/src/components/datagrid/data_grid.tsx index e6ffd45478b..9cf9f07def1 100644 --- a/src/components/datagrid/data_grid.tsx +++ b/src/components/datagrid/data_grid.tsx @@ -684,7 +684,6 @@ export const EuiDataGrid: FunctionComponent = (props) => { // if the focus is on the header, and the header is no longer interactive // move the focus down to the first row - // if (hasInteractives === false && focusedCell && focusedCell[1] === -1) { const focusedCell = focusedCellReference.current; if (hasInteractives === false && focusedCell && focusedCell[1] === -1) { setFocusedCell([focusedCell[0], 0]); @@ -1068,8 +1067,6 @@ export const EuiDataGrid: FunctionComponent = (props) => { setColumnWidth={setColumnWidth} headerIsInteractive={headerIsInteractive} handleHeaderMutation={handleHeaderMutation} - // gridHeight={gridHeight} - // gridWidth={gridWidth} inMemoryValues={inMemoryValues} inMemory={inMemory} schemaDetectors={allSchemaDetectors} From b1b9ae25a2f2fc83a6c2bd0500e1084d4669d2d8 Mon Sep 17 00:00:00 2001 From: Chandler Prall Date: Fri, 15 Jan 2021 13:32:18 -0700 Subject: [PATCH 40/54] Fix fullscreen experience; revert change to default example which duplicated the control column --- src-docs/src/views/datagrid/datagrid.js | 65 ---------------------- src/components/datagrid/data_grid.tsx | 3 +- src/components/datagrid/data_grid_body.tsx | 13 ++++- 3 files changed, 13 insertions(+), 68 deletions(-) diff --git a/src-docs/src/views/datagrid/datagrid.js b/src-docs/src/views/datagrid/datagrid.js index 3a39b9199f7..f496a176b42 100644 --- a/src-docs/src/views/datagrid/datagrid.js +++ b/src-docs/src/views/datagrid/datagrid.js @@ -167,70 +167,6 @@ const columns = [ }, ]; -const leadingControlColumns = [ - { - id: 'actions', - width: 40, - headerCellRender: () => null, - rowCellRender: function RowCellRender() { - const [isPopoverOpen, setIsPopoverOpen] = useState(false); - return ( -
- setIsPopoverOpen(!isPopoverOpen)} - /> - } - closePopover={() => setIsPopoverOpen(false)} - ownFocus={true}> - Actions -
- - - -
-
-
- ); - }, - }, -]; - const trailingControlColumns = [ { id: 'actions', @@ -368,7 +304,6 @@ export default () => { aria-label="Data grid demo" columns={columns} columnVisibility={{ visibleColumns, setVisibleColumns }} - leadingControlColumns={leadingControlColumns} trailingControlColumns={trailingControlColumns} rowCount={raw_data.length} renderCellValue={renderCellValue} diff --git a/src/components/datagrid/data_grid.tsx b/src/components/datagrid/data_grid.tsx index 9cf9f07def1..7f8d5f84324 100644 --- a/src/components/datagrid/data_grid.tsx +++ b/src/components/datagrid/data_grid.tsx @@ -997,7 +997,7 @@ export const EuiDataGrid: FunctionComponent = (props) => {
{(IS_JEST_ENVIRONMENT || defaultColumnWidth) && ( @@ -1052,6 +1052,7 @@ export const EuiDataGrid: FunctionComponent = (props) => { {...wrappingDivFocusProps} {...gridAriaProps}> = ( props ) => { const { + isFullScreen, columnWidths, defaultColumnWidth, leadingControlColumns = [], @@ -534,6 +536,13 @@ export const EuiDataGridBody: FunctionComponent = ( } }, [unconstrainedHeight, wrapperDimensions]); + let finalHeight = IS_JEST_ENVIRONMENT ? 500 : height || unconstrainedHeight; + let finalWidth = IS_JEST_ENVIRONMENT ? 500 : width || unconstrainedWidth; + if (isFullScreen) { + finalHeight = window.innerHeight; + finalWidth = window.innerWidth; + } + return (
= ( columns.length + trailingControlColumns.length } - width={IS_JEST_ENVIRONMENT ? 500 : width || unconstrainedWidth} + width={finalWidth} columnWidth={getWidth} - height={IS_JEST_ENVIRONMENT ? 500 : height || unconstrainedHeight} + height={finalHeight} rowHeight={getRowHeight} itemData={{ setRowHeight, From dfe0a90eb9f31b38029bb92b49038cde2292e9b6 Mon Sep 17 00:00:00 2001 From: Joe Reuter Date: Mon, 18 Jan 2021 11:32:53 +0100 Subject: [PATCH 41/54] reduce number of wrapper elements if not necessary --- .../__snapshots__/data_grid.test.tsx.snap | 840 ++++-------------- .../datagrid/_data_grid_data_row.scss | 5 + src/components/datagrid/data_grid.test.tsx | 56 +- src/components/datagrid/data_grid_body.tsx | 9 +- src/components/datagrid/data_grid_cell.tsx | 102 ++- 5 files changed, 250 insertions(+), 762 deletions(-) diff --git a/src/components/datagrid/__snapshots__/data_grid.test.tsx.snap b/src/components/datagrid/__snapshots__/data_grid.test.tsx.snap index 3b560bd4ca4..823891f428a 100644 --- a/src/components/datagrid/__snapshots__/data_grid.test.tsx.snap +++ b/src/components/datagrid/__snapshots__/data_grid.test.tsx.snap @@ -1135,220 +1135,88 @@ Array [
-
-
-
-
-

- - Row: 1, Column: 1: - -

-
- 0, A -
-
-
-
-
+ Row: 1, Column: 1: +

-
-
-
-
-

- - Row: 1, Column: 2: - -

-
- 0, B -
-
-
-
-
+ Row: 1, Column: 2: +

-
-
-
-
-

- - Row: 2, Column: 1: - -

-
- 1, A -
-
-
-
-
+ Row: 2, Column: 1: +

-
-
-
-
-

- - Row: 2, Column: 2: - -

-
- 1, B -
-
-
-
-
+ Row: 2, Column: 2: +

-
-
-
-
-

- - Row: 3, Column: 1: - -

-
- 2, A -
-
-
-
-
+ Row: 3, Column: 1: +

-
-
-
-
-

- - Row: 3, Column: 2: - -

-
- 2, B -
-
-
-
-
+ Row: 3, Column: 2: +

@@ -1636,9 +1504,7 @@ Array [

- - Row: 1, Column: 1: - + Row: 1, Column: 1:

-
-
-
-
-

- - Row: 1, Column: 2: - -

-
- 0, A -
-
-
-
-
+ Row: 1, Column: 2: +

-
-
-
-
-

- - Row: 1, Column: 3: - -

-
- 0, B -
-
-
-
-
+ Row: 1, Column: 3: +

- - Row: 1, Column: 4: - + Row: 1, Column: 4:

- - Row: 2, Column: 1: - + Row: 2, Column: 1:

-
-
-
-
-

- - Row: 2, Column: 2: - -

-
- 1, A -
-
-
-
-
+ Row: 2, Column: 2: +

-
-
-
-
-

- - Row: 2, Column: 3: - -

-
- 1, B -
-
-
-
-
+ Row: 2, Column: 3: +

- - Row: 2, Column: 4: - + Row: 2, Column: 4:

- - Row: 3, Column: 1: - + Row: 3, Column: 1:

-
-
-
-
-

- - Row: 3, Column: 2: - -

-
- 2, A -
-
-
-
-
+ Row: 3, Column: 2: +

-
-
-
-
-

- - Row: 3, Column: 3: - -

-
- 2, B -
-
-
-
-
+ Row: 3, Column: 3: +

- - Row: 3, Column: 4: - + Row: 3, Column: 4:

-
-
-
-
-

- - Row: 1, Column: 1: - -

-
- 0, A -
-
-
-
-
+ Row: 1, Column: 1: +

-
-
-
-
-

- - Row: 1, Column: 2: - -

-
- 0, B -
-
-
-
-
+ Row: 1, Column: 2: +

-
-
-
-
-

- - Row: 2, Column: 1: - -

-
- 1, A -
-
-
-
-
+ Row: 2, Column: 1: +

-
-
-
-
-

- - Row: 2, Column: 2: - -

-
- 1, B -
-
-
-
-
+ Row: 2, Column: 2: +

-
-
-
-
-

- - Row: 3, Column: 1: - -

-
- 2, A -
-
-
-
-
+ Row: 3, Column: 1: +

-
-
-
-
-

- - Row: 3, Column: 2: - -

-
- 2, B -
-
-
-
-
+ Row: 3, Column: 2: +

@@ -2772,220 +2364,88 @@ Array [
-
-
-
-
-

- - Row: 1, Column: 1: - -

-
- 0, A -
-
-
-
-
+ Row: 1, Column: 1: +

-
-
-
-
-

- - Row: 1, Column: 2: - -

-
- 0, B -
-
-
-
-
+ Row: 1, Column: 2: +

-
-
-
-
-

- - Row: 2, Column: 1: - -

-
- 1, A -
-
-
-
-
+ Row: 2, Column: 1: +

-
-
-
-
-

- - Row: 2, Column: 2: - -

-
- 1, B -
-
-
-
-
+ Row: 2, Column: 2: +

-
-
-
-
-

- - Row: 3, Column: 1: - -

-
- 2, A -
-
-
-
-
+ Row: 3, Column: 1: +

-
-
-
-
-

- - Row: 3, Column: 2: - -

-
- 2, B -
-
-
-
-
+ Row: 3, Column: 2: +

diff --git a/src/components/datagrid/_data_grid_data_row.scss b/src/components/datagrid/_data_grid_data_row.scss index 07b45ece8c9..8d1f9c32ffd 100644 --- a/src/components/datagrid/_data_grid_data_row.scss +++ b/src/components/datagrid/_data_grid_data_row.scss @@ -96,10 +96,15 @@ text-transform: capitalize; } + &.euiDataGridRowCell__truncate { + display: block; + } + // We only truncate if the cell is not a control column. &:not(.euiDataGridRowCell--controlColumn) { .euiDataGridRowCell__content, .euiDataGridRowCell__truncate, + &.euiDataGridRowCell__truncate, .euiDataGridRowCell__expandContent { @include euiTextTruncate; overflow: hidden; diff --git a/src/components/datagrid/data_grid.test.tsx b/src/components/datagrid/data_grid.test.tsx index 65dc60e41d2..2f7a1081bfb 100644 --- a/src/components/datagrid/data_grid.test.tsx +++ b/src/components/datagrid/data_grid.test.tsx @@ -517,7 +517,7 @@ describe('EuiDataGrid', () => { ).toMatchInlineSnapshot(` Array [ Object { - "className": "euiDataGridRowCell euiDataGridRowCell--firstColumn customClass", + "className": "euiDataGridRowCell euiDataGridRowCell__truncate euiDataGridRowCell--firstColumn customClass", "data-test-subj": "dataGridRowCell", "onBlur": [Function], "onFocus": [Function], @@ -536,7 +536,7 @@ describe('EuiDataGrid', () => { "tabIndex": -1, }, Object { - "className": "euiDataGridRowCell euiDataGridRowCell--lastColumn customClass", + "className": "euiDataGridRowCell euiDataGridRowCell__truncate euiDataGridRowCell--lastColumn customClass", "data-test-subj": "dataGridRowCell", "onBlur": [Function], "onFocus": [Function], @@ -555,7 +555,7 @@ describe('EuiDataGrid', () => { "tabIndex": -1, }, Object { - "className": "euiDataGridRowCell euiDataGridRowCell--stripe euiDataGridRowCell--firstColumn customClass", + "className": "euiDataGridRowCell euiDataGridRowCell__truncate euiDataGridRowCell--stripe euiDataGridRowCell--firstColumn customClass", "data-test-subj": "dataGridRowCell", "onBlur": [Function], "onFocus": [Function], @@ -574,7 +574,7 @@ describe('EuiDataGrid', () => { "tabIndex": -1, }, Object { - "className": "euiDataGridRowCell euiDataGridRowCell--stripe euiDataGridRowCell--lastColumn customClass", + "className": "euiDataGridRowCell euiDataGridRowCell__truncate euiDataGridRowCell--stripe euiDataGridRowCell--lastColumn customClass", "data-test-subj": "dataGridRowCell", "onBlur": [Function], "onFocus": [Function], @@ -785,17 +785,17 @@ describe('EuiDataGrid', () => { expect(gridCellClassNames).toMatchInlineSnapshot(` Array [ "euiDataGridRowCell--firstColumn", - "euiDataGridRowCell euiDataGridRowCell--numeric euiDataGridRowCell--firstColumn", + "euiDataGridRowCell euiDataGridRowCell--numeric euiDataGridRowCell__truncate euiDataGridRowCell--firstColumn", "euiDataGridRowCell--lastColumn", - "euiDataGridRowCell euiDataGridRowCell--customFormatName euiDataGridRowCell--lastColumn", + "euiDataGridRowCell euiDataGridRowCell--customFormatName euiDataGridRowCell__truncate euiDataGridRowCell--lastColumn", "euiDataGridRowCell--stripe euiDataGridRowCell--firstColumn", - "euiDataGridRowCell euiDataGridRowCell--numeric euiDataGridRowCell--stripe euiDataGridRowCell--firstColumn", + "euiDataGridRowCell euiDataGridRowCell--numeric euiDataGridRowCell__truncate euiDataGridRowCell--stripe euiDataGridRowCell--firstColumn", "euiDataGridRowCell--stripe euiDataGridRowCell--lastColumn", - "euiDataGridRowCell euiDataGridRowCell--customFormatName euiDataGridRowCell--stripe euiDataGridRowCell--lastColumn", + "euiDataGridRowCell euiDataGridRowCell--customFormatName euiDataGridRowCell__truncate euiDataGridRowCell--stripe euiDataGridRowCell--lastColumn", "euiDataGridRowCell--firstColumn", - "euiDataGridRowCell euiDataGridRowCell--numeric euiDataGridRowCell--firstColumn", + "euiDataGridRowCell euiDataGridRowCell--numeric euiDataGridRowCell__truncate euiDataGridRowCell--firstColumn", "euiDataGridRowCell--lastColumn", - "euiDataGridRowCell euiDataGridRowCell--customFormatName euiDataGridRowCell--lastColumn", + "euiDataGridRowCell euiDataGridRowCell--customFormatName euiDataGridRowCell__truncate euiDataGridRowCell--lastColumn", ] `); }); @@ -828,12 +828,12 @@ describe('EuiDataGrid', () => { .map((x) => x.props().className); expect(gridCellClassNames).toMatchInlineSnapshot(` Array [ - "euiDataGridRowCell euiDataGridRowCell--numeric euiDataGridRowCell--firstColumn", - "euiDataGridRowCell euiDataGridRowCell--boolean", - "euiDataGridRowCell euiDataGridRowCell--lastColumn", - "euiDataGridRowCell euiDataGridRowCell--numeric euiDataGridRowCell--stripe euiDataGridRowCell--firstColumn", - "euiDataGridRowCell euiDataGridRowCell--boolean euiDataGridRowCell--stripe", - "euiDataGridRowCell euiDataGridRowCell--stripe euiDataGridRowCell--lastColumn", + "euiDataGridRowCell euiDataGridRowCell--numeric euiDataGridRowCell__truncate euiDataGridRowCell--firstColumn", + "euiDataGridRowCell euiDataGridRowCell--boolean euiDataGridRowCell__truncate", + "euiDataGridRowCell euiDataGridRowCell__truncate euiDataGridRowCell--lastColumn", + "euiDataGridRowCell euiDataGridRowCell--numeric euiDataGridRowCell__truncate euiDataGridRowCell--stripe euiDataGridRowCell--firstColumn", + "euiDataGridRowCell euiDataGridRowCell--boolean euiDataGridRowCell__truncate euiDataGridRowCell--stripe", + "euiDataGridRowCell euiDataGridRowCell__truncate euiDataGridRowCell--stripe euiDataGridRowCell--lastColumn", ] `); }); @@ -860,10 +860,10 @@ describe('EuiDataGrid', () => { .map((x) => x.props().className); expect(gridCellClassNames).toMatchInlineSnapshot(` Array [ - "euiDataGridRowCell euiDataGridRowCell--numeric euiDataGridRowCell--firstColumn", - "euiDataGridRowCell euiDataGridRowCell--alphanumeric euiDataGridRowCell--lastColumn", - "euiDataGridRowCell euiDataGridRowCell--numeric euiDataGridRowCell--stripe euiDataGridRowCell--firstColumn", - "euiDataGridRowCell euiDataGridRowCell--alphanumeric euiDataGridRowCell--stripe euiDataGridRowCell--lastColumn", + "euiDataGridRowCell euiDataGridRowCell--numeric euiDataGridRowCell__truncate euiDataGridRowCell--firstColumn", + "euiDataGridRowCell euiDataGridRowCell--alphanumeric euiDataGridRowCell__truncate euiDataGridRowCell--lastColumn", + "euiDataGridRowCell euiDataGridRowCell--numeric euiDataGridRowCell__truncate euiDataGridRowCell--stripe euiDataGridRowCell--firstColumn", + "euiDataGridRowCell euiDataGridRowCell--alphanumeric euiDataGridRowCell__truncate euiDataGridRowCell--stripe euiDataGridRowCell--lastColumn", ] `); }); @@ -897,12 +897,12 @@ describe('EuiDataGrid', () => { .map((x) => x.props().className); expect(gridCellClassNames).toMatchInlineSnapshot(` Array [ - "euiDataGridRowCell euiDataGridRowCell--numeric euiDataGridRowCell--firstColumn", - "euiDataGridRowCell euiDataGridRowCell--boolean", - "euiDataGridRowCell euiDataGridRowCell--currency", - "euiDataGridRowCell euiDataGridRowCell--datetime", - "euiDataGridRowCell euiDataGridRowCell--datetime", - "euiDataGridRowCell euiDataGridRowCell--datetime", + "euiDataGridRowCell euiDataGridRowCell--numeric euiDataGridRowCell__truncate euiDataGridRowCell--firstColumn", + "euiDataGridRowCell euiDataGridRowCell--boolean euiDataGridRowCell__truncate", + "euiDataGridRowCell euiDataGridRowCell--currency euiDataGridRowCell__truncate", + "euiDataGridRowCell euiDataGridRowCell--datetime euiDataGridRowCell__truncate", + "euiDataGridRowCell euiDataGridRowCell--datetime euiDataGridRowCell__truncate", + "euiDataGridRowCell euiDataGridRowCell--datetime euiDataGridRowCell__truncate", ] `); }); @@ -945,8 +945,8 @@ describe('EuiDataGrid', () => { .map((x) => x.props().className); expect(gridCellClassNames).toMatchInlineSnapshot(` Array [ - "euiDataGridRowCell euiDataGridRowCell--numeric euiDataGridRowCell--firstColumn", - "euiDataGridRowCell euiDataGridRowCell--ipaddress euiDataGridRowCell--lastColumn", + "euiDataGridRowCell euiDataGridRowCell--numeric euiDataGridRowCell__truncate euiDataGridRowCell--firstColumn", + "euiDataGridRowCell euiDataGridRowCell--ipaddress euiDataGridRowCell__truncate euiDataGridRowCell--lastColumn", ] `); }); diff --git a/src/components/datagrid/data_grid_body.tsx b/src/components/datagrid/data_grid_body.tsx index a08f9ba73f6..9ae3d9a975b 100644 --- a/src/components/datagrid/data_grid_body.tsx +++ b/src/components/datagrid/data_grid_body.tsx @@ -582,7 +582,7 @@ export const EuiDataGridBody: FunctionComponent = ( wrapperRef.current = el; mutationRef(el); }}> - {(IS_JEST_ENVIRONMENT || (width && width > 0)) && ( + {(IS_JEST_ENVIRONMENT || finalWidth > 0) && ( = ( trailingControlColumns.length } width={finalWidth} - width={IS_JEST_ENVIRONMENT ? 500 : width || unconstrainedWidth} columnWidth={getWidth} height={finalHeight} rowHeight={getRowHeight} @@ -615,7 +614,11 @@ export const EuiDataGridBody: FunctionComponent = ( renderCellValue, interactiveCellId, }} - rowCount={visibleRowIndices.length}> + rowCount={ + IS_JEST_ENVIRONMENT || headerRowHeight > 0 + ? visibleRowIndices.length + : 0 + }> {Cell} diff --git a/src/components/datagrid/data_grid_cell.tsx b/src/components/datagrid/data_grid_cell.tsx index ce525b1f412..494d77d77aa 100644 --- a/src/components/datagrid/data_grid_cell.tsx +++ b/src/components/datagrid/data_grid_cell.tsx @@ -45,6 +45,7 @@ import { keys } from '../../services'; import { EuiDataGridCellButtons } from './data_grid_cell_buttons'; import { EuiDataGridCellPopover } from './data_grid_cell_popover'; +let count = 0; export interface EuiDataGridCellValueElementProps { /** * index of the row being rendered, 0 represents the first row. This index always includes @@ -232,6 +233,7 @@ export class EuiDataGridCell extends Component< nextProps: EuiDataGridCellProps, nextState: EuiDataGridCellState ) { + console.log(++count); if (nextProps.rowIndex !== this.props.rowIndex) return true; if (nextProps.visibleRowIndex !== this.props.visibleRowIndex) return true; if (nextProps.colIndex !== this.props.colIndex) return true; @@ -246,7 +248,10 @@ export class EuiDataGridCell extends Component< // respond to adjusted top/left if (nextProps.style) { if (!this.props.style) return true; - if (nextProps.style.top !== this.props.style.top) return true; + if (nextProps.style.top !== this.props.style.top) { + console.log(`${nextProps.style.top} from ${this.props.style.top}`); + return true; + } if (nextProps.style.left !== this.props.style.left) return true; } @@ -338,12 +343,19 @@ export class EuiDataGridCell extends Component< } = this.props; const { colIndex, rowIndex } = rest; + const showCellButtons = + this.state.isFocused || + this.state.isEntered || + this.state.isHovered || + this.state.popoverIsOpen; + const cellClasses = classNames( 'euiDataGridRowCell', { [`euiDataGridRowCell--${columnType}`]: columnType, + euiDataGridRowCell__truncate: (isExpandable || (column && column.cellActions)) && !showCellButtons, }, - className + className, ); const cellProps = { @@ -435,9 +447,9 @@ export class EuiDataGridCell extends Component< tokens={['euiDataGridCell.row', 'euiDataGridCell.column']} defaults={['Row', 'Column']}> {([row, column]: ReactChild[]) => ( - + <> {row}: {rowIndex + 1}, {column}: {colIndex + 1}: - + )}

@@ -465,44 +477,47 @@ export class EuiDataGridCell extends Component< ); - const showCellButtons = - this.state.isFocused || - this.state.isEntered || - this.state.isHovered || - this.state.popoverIsOpen; - if (isExpandable || (column && column.cellActions)) { - anchorContent = ( -
-
- {screenReaderPosition} -
- + if (showCellButtons) { + anchorContent = ( +
+
+
+ +
+ {screenReaderPosition}
+ {showCellButtons && ( + { + this.setState( + ({ popoverIsOpen }) => ({ + popoverIsOpen: !popoverIsOpen, + }), + () => + this.setState(({ popoverIsOpen }) => ({ + renderPopoverOpen: popoverIsOpen, + })) + ); + }} + /> + )}
- {showCellButtons && ( - { - this.setState( - ({ popoverIsOpen }) => ({ - popoverIsOpen: !popoverIsOpen, - }), - () => - this.setState(({ popoverIsOpen }) => ({ - renderPopoverOpen: popoverIsOpen, - })) - ); - }} - /> - )} -
- ); + ); + } else { + anchorContent = ( + <> + + {screenReaderPosition} + + ); + } } let innerContent = anchorContent; @@ -525,7 +540,7 @@ export class EuiDataGridCell extends Component<
); } else { - innerContent = (anchorContent); + innerContent = anchorContent; } } @@ -535,7 +550,12 @@ export class EuiDataGridCell extends Component< tabIndex={ this.state.isFocused && !this.state.disableCellTabIndex ? 0 : -1 } - ref={this.setCellRef} + ref={(el) => { + this.setCellRef(el); + if ((isExpandable || (column && column.cellActions)) && showCellButtons) { + this.setCellContentsRef(el); + } + }} {...cellProps} data-test-subj="dataGridRowCell" onKeyDown={handleCellKeyDown} From 43712b9292fd310548c35d7f903af6e84101c600 Mon Sep 17 00:00:00 2001 From: Joe Reuter Date: Mon, 18 Jan 2021 16:27:28 +0100 Subject: [PATCH 42/54] fix some bugs --- .../__snapshots__/data_grid.test.tsx.snap | 384 +++++++++++------- .../datagrid/_data_grid_data_row.scss | 6 +- src/components/datagrid/data_grid.test.tsx | 56 +-- src/components/datagrid/data_grid_cell.tsx | 17 +- 4 files changed, 276 insertions(+), 187 deletions(-) diff --git a/src/components/datagrid/__snapshots__/data_grid.test.tsx.snap b/src/components/datagrid/__snapshots__/data_grid.test.tsx.snap index 823891f428a..a89cd309577 100644 --- a/src/components/datagrid/__snapshots__/data_grid.test.tsx.snap +++ b/src/components/datagrid/__snapshots__/data_grid.test.tsx.snap @@ -1135,88 +1135,112 @@ Array [
- 0, A -

- Row: 1, Column: 1: -

+ 0, A +

+ Row: 1, Column: 1: +

+
- 0, B -

- Row: 1, Column: 2: -

+ 0, B +

+ Row: 1, Column: 2: +

+
- 1, A -

- Row: 2, Column: 1: -

+ 1, A +

+ Row: 2, Column: 1: +

+
- 1, B -

- Row: 2, Column: 2: -

+ 1, B +

+ Row: 2, Column: 2: +

+
- 2, A -

- Row: 3, Column: 1: -

+ 2, A +

+ Row: 3, Column: 1: +

+
- 2, B -

- Row: 3, Column: 2: -

+ 2, B +

+ Row: 3, Column: 2: +

+
@@ -1521,32 +1545,40 @@ Array [ />
- 0, A -

- Row: 1, Column: 2: -

+ 0, A +

+ Row: 1, Column: 2: +

+
- 0, B -

- Row: 1, Column: 3: -

+ 0, B +

+ Row: 1, Column: 3: +

+
- 1, A -

- Row: 2, Column: 2: -

+ 1, A +

+ Row: 2, Column: 2: +

+
- 1, B -

- Row: 2, Column: 3: -

+ 1, B +

+ Row: 2, Column: 3: +

+
- 2, A -

- Row: 3, Column: 2: -

+ 2, A +

+ Row: 3, Column: 2: +

+
- 2, B -

- Row: 3, Column: 3: -

+ 2, B +

+ Row: 3, Column: 3: +

+
- 0, A -

- Row: 1, Column: 1: -

+ 0, A +

+ Row: 1, Column: 1: +

+
- 0, B -

- Row: 1, Column: 2: -

+ 0, B +

+ Row: 1, Column: 2: +

+
- 1, A -

- Row: 2, Column: 1: -

+ 1, A +

+ Row: 2, Column: 1: +

+
- 1, B -

- Row: 2, Column: 2: -

+ 1, B +

+ Row: 2, Column: 2: +

+
- 2, A -

- Row: 3, Column: 1: -

+ 2, A +

+ Row: 3, Column: 1: +

+
- 2, B -

- Row: 3, Column: 2: -

+ 2, B +

+ Row: 3, Column: 2: +

+
@@ -2364,88 +2436,112 @@ Array [
- 0, A -

- Row: 1, Column: 1: -

+ 0, A +

+ Row: 1, Column: 1: +

+
- 0, B -

- Row: 1, Column: 2: -

+ 0, B +

+ Row: 1, Column: 2: +

+
- 1, A -

- Row: 2, Column: 1: -

+ 1, A +

+ Row: 2, Column: 1: +

+
- 1, B -

- Row: 2, Column: 2: -

+ 1, B +

+ Row: 2, Column: 2: +

+
- 2, A -

- Row: 3, Column: 1: -

+ 2, A +

+ Row: 3, Column: 1: +

+
- 2, B -

- Row: 3, Column: 2: -

+ 2, B +

+ Row: 3, Column: 2: +

+
diff --git a/src/components/datagrid/_data_grid_data_row.scss b/src/components/datagrid/_data_grid_data_row.scss index 8d1f9c32ffd..705af2655ea 100644 --- a/src/components/datagrid/_data_grid_data_row.scss +++ b/src/components/datagrid/_data_grid_data_row.scss @@ -66,7 +66,7 @@ } } - &:not(:hover) { + &:not(:hover):not(.euiDataGridRowCell--hovered) { .euiDataGridRowCell__expandButtonIcon { margin-left: $euiDataGridCellPaddingM; width: $euiSizeM; @@ -96,10 +96,6 @@ text-transform: capitalize; } - &.euiDataGridRowCell__truncate { - display: block; - } - // We only truncate if the cell is not a control column. &:not(.euiDataGridRowCell--controlColumn) { .euiDataGridRowCell__content, diff --git a/src/components/datagrid/data_grid.test.tsx b/src/components/datagrid/data_grid.test.tsx index 2f7a1081bfb..65dc60e41d2 100644 --- a/src/components/datagrid/data_grid.test.tsx +++ b/src/components/datagrid/data_grid.test.tsx @@ -517,7 +517,7 @@ describe('EuiDataGrid', () => { ).toMatchInlineSnapshot(` Array [ Object { - "className": "euiDataGridRowCell euiDataGridRowCell__truncate euiDataGridRowCell--firstColumn customClass", + "className": "euiDataGridRowCell euiDataGridRowCell--firstColumn customClass", "data-test-subj": "dataGridRowCell", "onBlur": [Function], "onFocus": [Function], @@ -536,7 +536,7 @@ describe('EuiDataGrid', () => { "tabIndex": -1, }, Object { - "className": "euiDataGridRowCell euiDataGridRowCell__truncate euiDataGridRowCell--lastColumn customClass", + "className": "euiDataGridRowCell euiDataGridRowCell--lastColumn customClass", "data-test-subj": "dataGridRowCell", "onBlur": [Function], "onFocus": [Function], @@ -555,7 +555,7 @@ describe('EuiDataGrid', () => { "tabIndex": -1, }, Object { - "className": "euiDataGridRowCell euiDataGridRowCell__truncate euiDataGridRowCell--stripe euiDataGridRowCell--firstColumn customClass", + "className": "euiDataGridRowCell euiDataGridRowCell--stripe euiDataGridRowCell--firstColumn customClass", "data-test-subj": "dataGridRowCell", "onBlur": [Function], "onFocus": [Function], @@ -574,7 +574,7 @@ describe('EuiDataGrid', () => { "tabIndex": -1, }, Object { - "className": "euiDataGridRowCell euiDataGridRowCell__truncate euiDataGridRowCell--stripe euiDataGridRowCell--lastColumn customClass", + "className": "euiDataGridRowCell euiDataGridRowCell--stripe euiDataGridRowCell--lastColumn customClass", "data-test-subj": "dataGridRowCell", "onBlur": [Function], "onFocus": [Function], @@ -785,17 +785,17 @@ describe('EuiDataGrid', () => { expect(gridCellClassNames).toMatchInlineSnapshot(` Array [ "euiDataGridRowCell--firstColumn", - "euiDataGridRowCell euiDataGridRowCell--numeric euiDataGridRowCell__truncate euiDataGridRowCell--firstColumn", + "euiDataGridRowCell euiDataGridRowCell--numeric euiDataGridRowCell--firstColumn", "euiDataGridRowCell--lastColumn", - "euiDataGridRowCell euiDataGridRowCell--customFormatName euiDataGridRowCell__truncate euiDataGridRowCell--lastColumn", + "euiDataGridRowCell euiDataGridRowCell--customFormatName euiDataGridRowCell--lastColumn", "euiDataGridRowCell--stripe euiDataGridRowCell--firstColumn", - "euiDataGridRowCell euiDataGridRowCell--numeric euiDataGridRowCell__truncate euiDataGridRowCell--stripe euiDataGridRowCell--firstColumn", + "euiDataGridRowCell euiDataGridRowCell--numeric euiDataGridRowCell--stripe euiDataGridRowCell--firstColumn", "euiDataGridRowCell--stripe euiDataGridRowCell--lastColumn", - "euiDataGridRowCell euiDataGridRowCell--customFormatName euiDataGridRowCell__truncate euiDataGridRowCell--stripe euiDataGridRowCell--lastColumn", + "euiDataGridRowCell euiDataGridRowCell--customFormatName euiDataGridRowCell--stripe euiDataGridRowCell--lastColumn", "euiDataGridRowCell--firstColumn", - "euiDataGridRowCell euiDataGridRowCell--numeric euiDataGridRowCell__truncate euiDataGridRowCell--firstColumn", + "euiDataGridRowCell euiDataGridRowCell--numeric euiDataGridRowCell--firstColumn", "euiDataGridRowCell--lastColumn", - "euiDataGridRowCell euiDataGridRowCell--customFormatName euiDataGridRowCell__truncate euiDataGridRowCell--lastColumn", + "euiDataGridRowCell euiDataGridRowCell--customFormatName euiDataGridRowCell--lastColumn", ] `); }); @@ -828,12 +828,12 @@ describe('EuiDataGrid', () => { .map((x) => x.props().className); expect(gridCellClassNames).toMatchInlineSnapshot(` Array [ - "euiDataGridRowCell euiDataGridRowCell--numeric euiDataGridRowCell__truncate euiDataGridRowCell--firstColumn", - "euiDataGridRowCell euiDataGridRowCell--boolean euiDataGridRowCell__truncate", - "euiDataGridRowCell euiDataGridRowCell__truncate euiDataGridRowCell--lastColumn", - "euiDataGridRowCell euiDataGridRowCell--numeric euiDataGridRowCell__truncate euiDataGridRowCell--stripe euiDataGridRowCell--firstColumn", - "euiDataGridRowCell euiDataGridRowCell--boolean euiDataGridRowCell__truncate euiDataGridRowCell--stripe", - "euiDataGridRowCell euiDataGridRowCell__truncate euiDataGridRowCell--stripe euiDataGridRowCell--lastColumn", + "euiDataGridRowCell euiDataGridRowCell--numeric euiDataGridRowCell--firstColumn", + "euiDataGridRowCell euiDataGridRowCell--boolean", + "euiDataGridRowCell euiDataGridRowCell--lastColumn", + "euiDataGridRowCell euiDataGridRowCell--numeric euiDataGridRowCell--stripe euiDataGridRowCell--firstColumn", + "euiDataGridRowCell euiDataGridRowCell--boolean euiDataGridRowCell--stripe", + "euiDataGridRowCell euiDataGridRowCell--stripe euiDataGridRowCell--lastColumn", ] `); }); @@ -860,10 +860,10 @@ describe('EuiDataGrid', () => { .map((x) => x.props().className); expect(gridCellClassNames).toMatchInlineSnapshot(` Array [ - "euiDataGridRowCell euiDataGridRowCell--numeric euiDataGridRowCell__truncate euiDataGridRowCell--firstColumn", - "euiDataGridRowCell euiDataGridRowCell--alphanumeric euiDataGridRowCell__truncate euiDataGridRowCell--lastColumn", - "euiDataGridRowCell euiDataGridRowCell--numeric euiDataGridRowCell__truncate euiDataGridRowCell--stripe euiDataGridRowCell--firstColumn", - "euiDataGridRowCell euiDataGridRowCell--alphanumeric euiDataGridRowCell__truncate euiDataGridRowCell--stripe euiDataGridRowCell--lastColumn", + "euiDataGridRowCell euiDataGridRowCell--numeric euiDataGridRowCell--firstColumn", + "euiDataGridRowCell euiDataGridRowCell--alphanumeric euiDataGridRowCell--lastColumn", + "euiDataGridRowCell euiDataGridRowCell--numeric euiDataGridRowCell--stripe euiDataGridRowCell--firstColumn", + "euiDataGridRowCell euiDataGridRowCell--alphanumeric euiDataGridRowCell--stripe euiDataGridRowCell--lastColumn", ] `); }); @@ -897,12 +897,12 @@ describe('EuiDataGrid', () => { .map((x) => x.props().className); expect(gridCellClassNames).toMatchInlineSnapshot(` Array [ - "euiDataGridRowCell euiDataGridRowCell--numeric euiDataGridRowCell__truncate euiDataGridRowCell--firstColumn", - "euiDataGridRowCell euiDataGridRowCell--boolean euiDataGridRowCell__truncate", - "euiDataGridRowCell euiDataGridRowCell--currency euiDataGridRowCell__truncate", - "euiDataGridRowCell euiDataGridRowCell--datetime euiDataGridRowCell__truncate", - "euiDataGridRowCell euiDataGridRowCell--datetime euiDataGridRowCell__truncate", - "euiDataGridRowCell euiDataGridRowCell--datetime euiDataGridRowCell__truncate", + "euiDataGridRowCell euiDataGridRowCell--numeric euiDataGridRowCell--firstColumn", + "euiDataGridRowCell euiDataGridRowCell--boolean", + "euiDataGridRowCell euiDataGridRowCell--currency", + "euiDataGridRowCell euiDataGridRowCell--datetime", + "euiDataGridRowCell euiDataGridRowCell--datetime", + "euiDataGridRowCell euiDataGridRowCell--datetime", ] `); }); @@ -945,8 +945,8 @@ describe('EuiDataGrid', () => { .map((x) => x.props().className); expect(gridCellClassNames).toMatchInlineSnapshot(` Array [ - "euiDataGridRowCell euiDataGridRowCell--numeric euiDataGridRowCell__truncate euiDataGridRowCell--firstColumn", - "euiDataGridRowCell euiDataGridRowCell--ipaddress euiDataGridRowCell__truncate euiDataGridRowCell--lastColumn", + "euiDataGridRowCell euiDataGridRowCell--numeric euiDataGridRowCell--firstColumn", + "euiDataGridRowCell euiDataGridRowCell--ipaddress euiDataGridRowCell--lastColumn", ] `); }); diff --git a/src/components/datagrid/data_grid_cell.tsx b/src/components/datagrid/data_grid_cell.tsx index 494d77d77aa..c29f8c78796 100644 --- a/src/components/datagrid/data_grid_cell.tsx +++ b/src/components/datagrid/data_grid_cell.tsx @@ -353,9 +353,9 @@ export class EuiDataGridCell extends Component< 'euiDataGridRowCell', { [`euiDataGridRowCell--${columnType}`]: columnType, - euiDataGridRowCell__truncate: (isExpandable || (column && column.cellActions)) && !showCellButtons, + [`euiDataGridRowCell--hovered`]: this.state.isHovered, }, - className, + className ); const cellProps = { @@ -512,10 +512,12 @@ export class EuiDataGridCell extends Component< ); } else { anchorContent = ( - <> +
{screenReaderPosition} - +
); } } @@ -550,12 +552,7 @@ export class EuiDataGridCell extends Component< tabIndex={ this.state.isFocused && !this.state.disableCellTabIndex ? 0 : -1 } - ref={(el) => { - this.setCellRef(el); - if ((isExpandable || (column && column.cellActions)) && showCellButtons) { - this.setCellContentsRef(el); - } - }} + ref={this.setCellRef} {...cellProps} data-test-subj="dataGridRowCell" onKeyDown={handleCellKeyDown} From b7e1685ffb18e1c211addb5d859b59de31ee143f Mon Sep 17 00:00:00 2001 From: Joe Reuter Date: Wed, 20 Jan 2021 11:13:41 +0100 Subject: [PATCH 43/54] resolve some issues --- .../datagrid/_data_grid_data_row.scss | 11 ++++++++++- src/components/datagrid/data_grid.test.tsx | 8 +++----- src/components/datagrid/data_grid_cell.tsx | 19 ++++++++++++++----- 3 files changed, 27 insertions(+), 11 deletions(-) diff --git a/src/components/datagrid/_data_grid_data_row.scss b/src/components/datagrid/_data_grid_data_row.scss index 705af2655ea..0b3f8caf765 100644 --- a/src/components/datagrid/_data_grid_data_row.scss +++ b/src/components/datagrid/_data_grid_data_row.scss @@ -66,12 +66,21 @@ } } - &:not(:hover):not(.euiDataGridRowCell--hovered) { + &:not(:hover):not(.euiDataGridRowCell--hovered), &.euiDataGridRowCell--open { .euiDataGridRowCell__expandButtonIcon { + animation: none; margin-left: $euiDataGridCellPaddingM; width: $euiSizeM; } + .euiDataGridRowCell__actionButtonIcon { + animation: none; + margin-left: $euiDataGridCellPaddingM; + width: $euiSizeM; + } + } + + &.euiDataGridRowCell--focused { .euiDataGridRowCell__actionButtonIcon { margin-left: $euiDataGridCellPaddingM; width: $euiSizeM; diff --git a/src/components/datagrid/data_grid.test.tsx b/src/components/datagrid/data_grid.test.tsx index 65dc60e41d2..fd2823e8dec 100644 --- a/src/components/datagrid/data_grid.test.tsx +++ b/src/components/datagrid/data_grid.test.tsx @@ -2054,11 +2054,9 @@ describe('EuiDataGrid', () => { expect(findTestSubject(component, 'alertAction').exists()).toBe(false); expect(findTestSubject(component, 'happyAction').exists()).toBe(false); - await act(async () => { - findTestSubject(component, 'dataGridRowCell') - .at(1) - .prop('onMouseEnter')!({} as React.MouseEvent); - }); + findTestSubject(component, 'dataGridRowCell') + .at(1) + .prop('onMouseEnter')!({} as React.MouseEvent); component.update(); diff --git a/src/components/datagrid/data_grid_cell.tsx b/src/components/datagrid/data_grid_cell.tsx index c29f8c78796..9b320a3eb37 100644 --- a/src/components/datagrid/data_grid_cell.tsx +++ b/src/components/datagrid/data_grid_cell.tsx @@ -45,7 +45,6 @@ import { keys } from '../../services'; import { EuiDataGridCellButtons } from './data_grid_cell_buttons'; import { EuiDataGridCellPopover } from './data_grid_cell_popover'; -let count = 0; export interface EuiDataGridCellValueElementProps { /** * index of the row being rendered, 0 represents the first row. This index always includes @@ -98,7 +97,7 @@ export interface EuiDataGridCellProps { interface EuiDataGridCellState { cellProps: CommonProps & HTMLAttributes; popoverIsOpen: boolean; // is expansion popover open - renderPopoverOpen: boolean; // wait one render cycle to actuall render the popover as open + renderPopoverOpen: boolean; // wait one render cycle to actually render the popover as open isFocused: boolean; // tracks if this cell has focus or not, used to enable tabIndex on the cell isEntered: boolean; // enables focus trap for non-expandable cells with multiple interactive elements isHovered: boolean; @@ -233,7 +232,6 @@ export class EuiDataGridCell extends Component< nextProps: EuiDataGridCellProps, nextState: EuiDataGridCellState ) { - console.log(++count); if (nextProps.rowIndex !== this.props.rowIndex) return true; if (nextProps.visibleRowIndex !== this.props.visibleRowIndex) return true; if (nextProps.colIndex !== this.props.colIndex) return true; @@ -249,7 +247,6 @@ export class EuiDataGridCell extends Component< if (nextProps.style) { if (!this.props.style) return true; if (nextProps.style.top !== this.props.style.top) { - console.log(`${nextProps.style.top} from ${this.props.style.top}`); return true; } if (nextProps.style.left !== this.props.style.left) return true; @@ -322,6 +319,8 @@ export class EuiDataGridCell extends Component< }; closePopover = () => { + // due to a bug, popovers don't close properly wenn unmounted in open state. + // This "double set" makes sure to unmount the component in closed state. this.setState({ popoverIsOpen: false }, () => this.setState(() => ({ renderPopoverOpen: false, @@ -353,7 +352,9 @@ export class EuiDataGridCell extends Component< 'euiDataGridRowCell', { [`euiDataGridRowCell--${columnType}`]: columnType, - [`euiDataGridRowCell--hovered`]: this.state.isHovered, + ['euiDataGridRowCell--hovered']: this.state.isHovered, + ['euiDataGridRowCell--focused']: this.state.isFocused, + ['euiDataGridRowCell--open']: this.state.popoverIsOpen, }, className ); @@ -378,6 +379,9 @@ export class EuiDataGridCell extends Component< case keys.ENTER: case keys.F2: event.preventDefault(); + // due to a bug, popovers become unclosable if they are directly rendered + // with isOpen set to true. This "double set" makes sure to mount the component + // in closed state before opening it this.setState({ popoverIsOpen: true }, () => this.setState(({ popoverIsOpen }) => ({ renderPopoverOpen: popoverIsOpen, @@ -496,6 +500,9 @@ export class EuiDataGridCell extends Component< popoverIsOpen={this.state.popoverIsOpen} closePopover={this.closePopover} onExpandClick={() => { + // due to a bug, popovers become unclosable if they are directly rendered + // with isOpen set to true. This "double set" makes sure to mount the component + // in closed state before opening it this.setState( ({ popoverIsOpen }) => ({ popoverIsOpen: !popoverIsOpen, @@ -546,6 +553,8 @@ export class EuiDataGridCell extends Component< } } + console.log(`this.state.isFocused: ${this.state.isFocused}, this.state.disableCellTabIndex: ${this.state.disableCellTabIndex}`); + return (
Date: Wed, 20 Jan 2021 11:43:14 +0100 Subject: [PATCH 44/54] resolve some issues --- src/components/datagrid/_data_grid_data_row.scss | 12 ++++++++++-- src/components/datagrid/data_grid_cell.tsx | 4 +--- 2 files changed, 11 insertions(+), 5 deletions(-) diff --git a/src/components/datagrid/_data_grid_data_row.scss b/src/components/datagrid/_data_grid_data_row.scss index 0b3f8caf765..7405754228e 100644 --- a/src/components/datagrid/_data_grid_data_row.scss +++ b/src/components/datagrid/_data_grid_data_row.scss @@ -66,7 +66,7 @@ } } - &:not(:hover):not(.euiDataGridRowCell--hovered), &.euiDataGridRowCell--open { + &:not(:hover), &.euiDataGridRowCell--open { .euiDataGridRowCell__expandButtonIcon { animation: none; margin-left: $euiDataGridCellPaddingM; @@ -80,13 +80,21 @@ } } - &.euiDataGridRowCell--focused { + // on focus, directly show action buttons (without animation), but still slide in the open popover button + &:focus { .euiDataGridRowCell__actionButtonIcon { margin-left: $euiDataGridCellPaddingM; width: $euiSizeM; } } + // if a cell is not hovered nor focused nor open via popover, don't show buttons in general + &:not(:hover):not(:focus):not(.euiDataGridRowCell--open) { + .euiDataGridRowCell__expandButtonIcon, .euiDataGridRowCell__actionButtonIcon { + display: none; + } + } + &:focus:not(:first-of-type) { // Needed because the focus state adds a border, which needs to be subtracted from padding padding-left: $euiDataGridCellPaddingM - 1px; diff --git a/src/components/datagrid/data_grid_cell.tsx b/src/components/datagrid/data_grid_cell.tsx index 9b320a3eb37..ddda4aa92c9 100644 --- a/src/components/datagrid/data_grid_cell.tsx +++ b/src/components/datagrid/data_grid_cell.tsx @@ -294,7 +294,7 @@ export class EuiDataGridCell extends Component< }; onBlur = () => { - this.setState({ disableCellTabIndex: false, isFocused: false }); + this.setState({ disableCellTabIndex: false }); }; preventTabbing = () => { @@ -352,8 +352,6 @@ export class EuiDataGridCell extends Component< 'euiDataGridRowCell', { [`euiDataGridRowCell--${columnType}`]: columnType, - ['euiDataGridRowCell--hovered']: this.state.isHovered, - ['euiDataGridRowCell--focused']: this.state.isFocused, ['euiDataGridRowCell--open']: this.state.popoverIsOpen, }, className From 8e3f6e16b1d02528ef3419b2bce6ca4bb3f2ab21 Mon Sep 17 00:00:00 2001 From: Joe Reuter Date: Wed, 20 Jan 2021 11:46:50 +0100 Subject: [PATCH 45/54] resolve some issues --- src/components/datagrid/data_grid_cell.tsx | 14 ++++++++++++-- 1 file changed, 12 insertions(+), 2 deletions(-) diff --git a/src/components/datagrid/data_grid_cell.tsx b/src/components/datagrid/data_grid_cell.tsx index ddda4aa92c9..6ee3da68a88 100644 --- a/src/components/datagrid/data_grid_cell.tsx +++ b/src/components/datagrid/data_grid_cell.tsx @@ -536,7 +536,15 @@ export class EuiDataGridCell extends Component< anchorContent={anchorContent} cellContentProps={cellContentProps} cellContentsRef={this.cellContentsRef} - closePopover={() => this.setState({ popoverIsOpen: false })} + closePopover={() => { + // due to a bug, popovers don't close properly wenn unmounted in open state. + // This "double set" makes sure to unmount the component in closed state. + this.setState({ popoverIsOpen: false }, () => + this.setState(() => ({ + renderPopoverOpen: false, + })) + ); + }} column={column} panelRefFn={(ref) => (this.popoverPanelRef.current = ref)} popoverIsOpen={this.state.renderPopoverOpen} @@ -551,7 +559,9 @@ export class EuiDataGridCell extends Component< } } - console.log(`this.state.isFocused: ${this.state.isFocused}, this.state.disableCellTabIndex: ${this.state.disableCellTabIndex}`); + console.log( + `this.state.isFocused: ${this.state.isFocused}, this.state.disableCellTabIndex: ${this.state.disableCellTabIndex}` + ); return (
Date: Wed, 20 Jan 2021 18:18:45 +0100 Subject: [PATCH 46/54] fix leftover problems --- src/components/datagrid/data_grid.test.tsx | 11 +++-------- src/components/datagrid/data_grid_cell.tsx | 17 +++++------------ 2 files changed, 8 insertions(+), 20 deletions(-) diff --git a/src/components/datagrid/data_grid.test.tsx b/src/components/datagrid/data_grid.test.tsx index fd2823e8dec..7a55333fd8a 100644 --- a/src/components/datagrid/data_grid.test.tsx +++ b/src/components/datagrid/data_grid.test.tsx @@ -28,7 +28,6 @@ import { import { EuiDataGridColumnResizer } from './data_grid_column_resizer'; import { keys } from '../../services'; import { act } from 'react-dom/test-utils'; -import { EuiDataGridCellButtons } from './data_grid_cell_buttons'; function getFocusableCell(component: ReactWrapper) { return findTestSubject(component, 'dataGridRowCell').find('[tabIndex=0]'); @@ -523,7 +522,6 @@ describe('EuiDataGrid', () => { "onFocus": [Function], "onKeyDown": [Function], "onMouseEnter": [Function], - "onMouseLeave": [Function], "role": "gridcell", "style": Object { "color": "red", @@ -542,7 +540,6 @@ describe('EuiDataGrid', () => { "onFocus": [Function], "onKeyDown": [Function], "onMouseEnter": [Function], - "onMouseLeave": [Function], "role": "gridcell", "style": Object { "color": "blue", @@ -561,7 +558,6 @@ describe('EuiDataGrid', () => { "onFocus": [Function], "onKeyDown": [Function], "onMouseEnter": [Function], - "onMouseLeave": [Function], "role": "gridcell", "style": Object { "color": "red", @@ -580,7 +576,6 @@ describe('EuiDataGrid', () => { "onFocus": [Function], "onKeyDown": [Function], "onMouseEnter": [Function], - "onMouseLeave": [Function], "role": "gridcell", "style": Object { "color": "blue", @@ -2054,9 +2049,9 @@ describe('EuiDataGrid', () => { expect(findTestSubject(component, 'alertAction').exists()).toBe(false); expect(findTestSubject(component, 'happyAction').exists()).toBe(false); - findTestSubject(component, 'dataGridRowCell') - .at(1) - .prop('onMouseEnter')!({} as React.MouseEvent); + findTestSubject(component, 'dataGridRowCell').at(1).prop('onMouseEnter')!( + {} as React.MouseEvent + ); component.update(); diff --git a/src/components/datagrid/data_grid_cell.tsx b/src/components/datagrid/data_grid_cell.tsx index 6ee3da68a88..09f45e3ee48 100644 --- a/src/components/datagrid/data_grid_cell.tsx +++ b/src/components/datagrid/data_grid_cell.tsx @@ -100,7 +100,7 @@ interface EuiDataGridCellState { renderPopoverOpen: boolean; // wait one render cycle to actually render the popover as open isFocused: boolean; // tracks if this cell has focus or not, used to enable tabIndex on the cell isEntered: boolean; // enables focus trap for non-expandable cells with multiple interactive elements - isHovered: boolean; + enableInteractions: boolean; // cell got hovered at least once, so cell button and popover interactions are rendered disableCellTabIndex: boolean; // disables tabIndex on the wrapping cell, used for focus management of a single interactive child } @@ -144,7 +144,7 @@ export class EuiDataGridCell extends Component< renderPopoverOpen: false, isFocused: false, isEntered: false, - isHovered: false, + enableInteractions: false, disableCellTabIndex: false, }; unsubscribeCell?: Function = () => {}; @@ -258,7 +258,7 @@ export class EuiDataGridCell extends Component< return true; if (nextState.isEntered !== this.state.isEntered) return true; if (nextState.isFocused !== this.state.isFocused) return true; - if (nextState.isHovered !== this.state.isHovered) return true; + if (nextState.enableInteractions !== this.state.enableInteractions) return true; if (nextState.disableCellTabIndex !== this.state.disableCellTabIndex) return true; @@ -345,7 +345,7 @@ export class EuiDataGridCell extends Component< const showCellButtons = this.state.isFocused || this.state.isEntered || - this.state.isHovered || + this.state.enableInteractions || this.state.popoverIsOpen; const cellClasses = classNames( @@ -559,10 +559,6 @@ export class EuiDataGridCell extends Component< } } - console.log( - `this.state.isFocused: ${this.state.isFocused}, this.state.disableCellTabIndex: ${this.state.disableCellTabIndex}` - ); - return (
{ - this.setState({ isHovered: true }); - }} - onMouseLeave={() => { - this.setState({ isHovered: false }); + this.setState({ enableInteractions: true }); }} onBlur={this.onBlur}> {innerContent} From 60f9bd15a8822da2766dbef40df1f75f766b94ec Mon Sep 17 00:00:00 2001 From: Dave Snider Date: Wed, 20 Jan 2021 11:19:54 -0800 Subject: [PATCH 47/54] fix prettier / ts issues --- .../datagrid/_data_grid_data_row.scss | 42 ++++++++++--------- src/components/datagrid/data_grid.tsx | 1 - src/components/datagrid/data_grid_body.tsx | 2 +- src/components/datagrid/data_grid_cell.tsx | 3 +- 4 files changed, 25 insertions(+), 23 deletions(-) diff --git a/src/components/datagrid/_data_grid_data_row.scss b/src/components/datagrid/_data_grid_data_row.scss index 7405754228e..6c3164716c6 100644 --- a/src/components/datagrid/_data_grid_data_row.scss +++ b/src/components/datagrid/_data_grid_data_row.scss @@ -3,18 +3,6 @@ min-width: 100%; // Needed to prevent wraps. Inline flex is tricky } -@keyframes euiDataGridCellButtonSlideIn { - from { - margin-left: 0; - width: 0; - } - - to { - margin-left: $euiDataGridCellPaddingM; - width: $euiSizeM; - } -} - @include euiDataGridRowCell { @include euiFontSizeS; @@ -50,23 +38,24 @@ // Long delays on hover to mitigate the accordion effect &:hover { .euiDataGridRowCell__expandButtonIcon { - animation-duration: $euiAnimSpeedFast; + animation-duration: $euiAnimSpeedExtraFast; animation-name: euiDataGridCellButtonSlideIn; animation-iteration-count: 1; - animation-delay: 1000ms; - animation-fill-mode: forwards; + animation-delay: $euiAnimSpeedNormal; + animation-fill-mode: forwards; } .euiDataGridRowCell__actionButtonIcon { - animation-duration: $euiAnimSpeedFast; + animation-duration: $euiAnimSpeedExtraFast; animation-name: euiDataGridCellButtonSlideIn; animation-iteration-count: 1; - animation-delay: 1000ms; - animation-fill-mode: forwards; + animation-delay: $euiAnimSpeedNormal; + animation-fill-mode: forwards; } } - &:not(:hover), &.euiDataGridRowCell--open { + &:not(:hover), + &.euiDataGridRowCell--open { .euiDataGridRowCell__expandButtonIcon { animation: none; margin-left: $euiDataGridCellPaddingM; @@ -90,7 +79,8 @@ // if a cell is not hovered nor focused nor open via popover, don't show buttons in general &:not(:hover):not(:focus):not(.euiDataGridRowCell--open) { - .euiDataGridRowCell__expandButtonIcon, .euiDataGridRowCell__actionButtonIcon { + .euiDataGridRowCell__expandButtonIcon, + .euiDataGridRowCell__actionButtonIcon { display: none; } } @@ -257,3 +247,15 @@ } } } + +@keyframes euiDataGridCellButtonSlideIn { + from { + margin-left: 0; + width: 0; + } + + to { + margin-left: $euiDataGridCellPaddingM; + width: $euiSizeM; + } +} diff --git a/src/components/datagrid/data_grid.tsx b/src/components/datagrid/data_grid.tsx index 0b74d19a25a..89261e070e3 100644 --- a/src/components/datagrid/data_grid.tsx +++ b/src/components/datagrid/data_grid.tsx @@ -650,7 +650,6 @@ export const EuiDataGrid: FunctionComponent = (props) => { const [interactiveCellId] = useState(htmlIdGenerator()()); const [headerIsInteractive, setHeaderIsInteractive] = useState(false); - const cellsUpdateFocus = useRef>(new Map()); const [wrappingDivFocusProps, focusedCell, setFocusedCell] = useFocus( diff --git a/src/components/datagrid/data_grid_body.tsx b/src/components/datagrid/data_grid_body.tsx index 9ae3d9a975b..3111f41287f 100644 --- a/src/components/datagrid/data_grid_body.tsx +++ b/src/components/datagrid/data_grid_body.tsx @@ -546,7 +546,7 @@ export const EuiDataGridBody: FunctionComponent = ( ) { setWidth(boundingRect.width); } - }, [unconstrainedHeight, wrapperDimensions]); + }, [unconstrainedHeight, wrapperDimensions, height, width]); const preventTabbing = useCallback(() => { if (wrapperRef.current) { diff --git a/src/components/datagrid/data_grid_cell.tsx b/src/components/datagrid/data_grid_cell.tsx index 09f45e3ee48..e5de27e8aa2 100644 --- a/src/components/datagrid/data_grid_cell.tsx +++ b/src/components/datagrid/data_grid_cell.tsx @@ -258,7 +258,8 @@ export class EuiDataGridCell extends Component< return true; if (nextState.isEntered !== this.state.isEntered) return true; if (nextState.isFocused !== this.state.isFocused) return true; - if (nextState.enableInteractions !== this.state.enableInteractions) return true; + if (nextState.enableInteractions !== this.state.enableInteractions) + return true; if (nextState.disableCellTabIndex !== this.state.disableCellTabIndex) return true; From 2b99526ca75515b712edb6b357e521d2aaed2a57 Mon Sep 17 00:00:00 2001 From: Joe Reuter Date: Thu, 21 Jan 2021 10:09:26 +0100 Subject: [PATCH 48/54] remove unnecessary checks --- src/components/datagrid/data_grid_body.tsx | 12 +++--------- 1 file changed, 3 insertions(+), 9 deletions(-) diff --git a/src/components/datagrid/data_grid_body.tsx b/src/components/datagrid/data_grid_body.tsx index 3111f41287f..54e6ad8af28 100644 --- a/src/components/datagrid/data_grid_body.tsx +++ b/src/components/datagrid/data_grid_body.tsx @@ -534,19 +534,13 @@ export const EuiDataGridBody: FunctionComponent = ( useEffect(() => { const boundingRect = wrapperRef.current!.getBoundingClientRect(); - if ( - boundingRect.height !== unconstrainedHeight && - height !== boundingRect.height - ) { + if (boundingRect.height !== unconstrainedHeight) { setHeight(boundingRect.height); } - if ( - boundingRect.width !== unconstrainedWidth && - width !== boundingRect.width - ) { + if (boundingRect.width !== unconstrainedWidth) { setWidth(boundingRect.width); } - }, [unconstrainedHeight, wrapperDimensions, height, width]); + }, [unconstrainedHeight, wrapperDimensions]); const preventTabbing = useCallback(() => { if (wrapperRef.current) { From b30faf14393ed22aa0cfff3f3464ae2c6b00cbba Mon Sep 17 00:00:00 2001 From: Joe Reuter Date: Thu, 21 Jan 2021 10:11:51 +0100 Subject: [PATCH 49/54] remove unnecessary parameter --- src/components/datagrid/data_grid_body.tsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/components/datagrid/data_grid_body.tsx b/src/components/datagrid/data_grid_body.tsx index 54e6ad8af28..d8af75bf6a4 100644 --- a/src/components/datagrid/data_grid_body.tsx +++ b/src/components/datagrid/data_grid_body.tsx @@ -529,7 +529,7 @@ export const EuiDataGridBody: FunctionComponent = ( }, [rowCount]); const wrapperRef = useRef(null); - const wrapperDimensions = useResizeObserver(wrapperRef.current, undefined); + const wrapperDimensions = useResizeObserver(wrapperRef.current); useEffect(() => { const boundingRect = wrapperRef.current!.getBoundingClientRect(); From 624446f643a22488f184a2375469d695b0cf5b69 Mon Sep 17 00:00:00 2001 From: Chandler Prall Date: Thu, 21 Jan 2021 10:33:09 -0700 Subject: [PATCH 50/54] Move datagrid's InnerElement to a stand alone component --- src/components/datagrid/data_grid_body.tsx | 53 +++++++++++----------- 1 file changed, 26 insertions(+), 27 deletions(-) diff --git a/src/components/datagrid/data_grid_body.tsx b/src/components/datagrid/data_grid_body.tsx index 277fbb6f108..f2e4c5275c7 100644 --- a/src/components/datagrid/data_grid_body.tsx +++ b/src/components/datagrid/data_grid_body.tsx @@ -31,6 +31,7 @@ import classNames from 'classnames'; import { GridChildComponentProps, VariableSizeGrid as Grid, + VariableSizeGridProps, } from 'react-window'; import { EuiCodeBlock } from '../code'; import { @@ -258,6 +259,31 @@ const Cell: FunctionComponent = ({ return cellContent; }; +const InnerElement: VariableSizeGridProps['innerElementType'] = forwardRef< + HTMLDivElement, + { style: { height: number } } +>(({ children, style, ...rest }, ref) => { + const { headerRowHeight, headerRow, footerRow } = useContext( + DataGridWrapperRowsContext + ); + return ( + <> +
+ {headerRow} + {children} +
+ {footerRow} + + ); +}); +InnerElement.displayName = 'EuiDataGridInnerElement'; + const INITIAL_ROW_HEIGHT = 34; const SCROLLBAR_HEIGHT = 15; const IS_JEST_ENVIRONMENT = global.hasOwnProperty('_isJest'); @@ -440,33 +466,6 @@ export const EuiDataGridBody: FunctionComponent = ( visibleRowIndices.length, ]); - const InnerElement = useMemo( - () => - forwardRef( - ({ children, style, ...rest }, ref) => { - const { headerRowHeight, headerRow, footerRow } = useContext( - DataGridWrapperRowsContext - ); - return ( - <> -
- {headerRow} - {children} -
- {footerRow} - - ); - } - ), - [] - ); - const gridRef = useRef(null); useEffect(() => { gridRef.current!.resetAfterColumnIndex(0); From bd0e2c675d6fc0cbc42511deb74c62d8384ca6f1 Mon Sep 17 00:00:00 2001 From: Chandler Prall Date: Thu, 21 Jan 2021 12:12:09 -0700 Subject: [PATCH 51/54] Removed devDep on @types/resize-observer-browser in anticipation of TS adding ResizeObserver --- package.json | 1 - src/components/datagrid/data_grid_cell.tsx | 12 +++++++++--- .../observer/resize_observer/resize_observer.tsx | 11 ++++++----- yarn.lock | 5 ----- 4 files changed, 15 insertions(+), 14 deletions(-) diff --git a/package.json b/package.json index 6f1c3cc1a4e..afe6d23fb0e 100644 --- a/package.json +++ b/package.json @@ -107,7 +107,6 @@ "@types/react-dom": "^16.9.6", "@types/react-is": "^16.7.1", "@types/react-router-dom": "^5.1.5", - "@types/resize-observer-browser": "^0.1.3", "@types/tabbable": "^3.1.0", "@types/url-parse": "^1.4.3", "@types/uuid": "^8.3.0", diff --git a/src/components/datagrid/data_grid_cell.tsx b/src/components/datagrid/data_grid_cell.tsx index 6e63ed448ed..d96e3aec41c 100644 --- a/src/components/datagrid/data_grid_cell.tsx +++ b/src/components/datagrid/data_grid_cell.tsx @@ -126,15 +126,21 @@ const EuiDataGridCellContent: FunctionComponent< ); }); +// TODO: TypeScript has added types for ResizeObserver but not yet released +// https://github.com/microsoft/TypeScript-DOM-lib-generator/pull/948 +// for now, marking ResizeObserver usage as `any` +// and when EUI upgrades to a version of TS with ResizeObserver +// the anys can be removed const hasResizeObserver = - typeof window !== 'undefined' && typeof window.ResizeObserver !== 'undefined'; + typeof window !== 'undefined' && + typeof (window as any).ResizeObserver !== 'undefined'; export class EuiDataGridCell extends Component< EuiDataGridCellProps, EuiDataGridCellState > { cellRef = createRef() as MutableRefObject; - observer!: ResizeObserver; + observer!: any; // ResizeObserver popoverPanelRef: MutableRefObject = createRef(); cellContentsRef: HTMLDivElement | null = null; state: EuiDataGridCellState = { @@ -153,7 +159,7 @@ export class EuiDataGridCell extends Component< // 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 = new window.ResizeObserver(() => { + this.observer = new (window as any).ResizeObserver(() => { const rowHeight = this.cellRef.current!.getBoundingClientRect() .height; if (this.props.setRowHeight) { diff --git a/src/components/observer/resize_observer/resize_observer.tsx b/src/components/observer/resize_observer/resize_observer.tsx index d3b598ecf86..f5ca3696d0c 100644 --- a/src/components/observer/resize_observer/resize_observer.tsx +++ b/src/components/observer/resize_observer/resize_observer.tsx @@ -30,7 +30,8 @@ interface Props { // IE11 and Safari don't support the `ResizeObserver` API at the time of writing const hasResizeObserver = - typeof window !== 'undefined' && typeof window.ResizeObserver !== 'undefined'; + typeof window !== 'undefined' && + typeof (window as any).ResizeObserver !== 'undefined'; const mutationObserverOptions = { // [MutationObserverInit](https://developer.mozilla.org/en-US/docs/Web/API/MutationObserverInit) @@ -69,7 +70,7 @@ export class EuiResizeObserver extends EuiObserver { // The superclass checks that childNode is not null before invoking // beginObserve() const childNode = this.childNode!; - this.observer = makeResizeObserver(childNode, this.onResize); + this.observer = makeResizeObserver(childNode, this.onResize)!; }; } @@ -91,8 +92,8 @@ const makeCompatibleObserver = (node: Element, callback: () => void) => { const makeResizeObserver = (node: Element, callback: () => void) => { let observer: Observer | undefined; if (hasResizeObserver) { - observer = new window.ResizeObserver(callback); - observer.observe(node); + observer = new (window as any).ResizeObserver(callback); + observer!.observe(node); } else { observer = makeCompatibleObserver(node, callback); requestAnimationFrame(callback); // Mimic ResizeObserver behavior of triggering a resize event on init @@ -142,7 +143,7 @@ export const useResizeObserver = ( width: boundingRect.width, height: boundingRect.height, }); - }); + })!; return () => observer.disconnect(); } else { diff --git a/yarn.lock b/yarn.lock index d41ce280d89..41aa3302e32 100755 --- a/yarn.lock +++ b/yarn.lock @@ -1802,11 +1802,6 @@ "@types/prop-types" "*" csstype "^2.2.0" -"@types/resize-observer-browser@^0.1.3": - version "0.1.3" - resolved "https://registry.yarnpkg.com/@types/resize-observer-browser/-/resize-observer-browser-0.1.3.tgz#5cca2445e6fc34a380760bd6ef8c492863469c47" - integrity sha512-3tGjLIDH8L57fWOfC7NVn/BbGQD7pXwbkk2+8Z4hK/S7kOIv1MUN4nkKjfx0qg4ctkukjzp3Bgr/Z+Hq5ZQZTQ== - "@types/responselike@*": version "1.0.0" resolved "https://registry.yarnpkg.com/@types/responselike/-/responselike-1.0.0.tgz#251f4fe7d154d2bad125abe1b429b23afd262e29" From fa04eb11d0582d054a9fb0f9d74553a775d5c9f8 Mon Sep 17 00:00:00 2001 From: Chandler Prall Date: Wed, 27 Jan 2021 13:50:48 -0700 Subject: [PATCH 52/54] Documentation --- .../datagrid_virtualization_example.js | 41 +++++++++++++++---- .../datagrid/virtualization_constrained.js | 6 ++- 2 files changed, 39 insertions(+), 8 deletions(-) diff --git a/src-docs/src/views/datagrid/datagrid_virtualization_example.js b/src-docs/src/views/datagrid/datagrid_virtualization_example.js index 4afb855368d..a5ef17e53d1 100644 --- a/src-docs/src/views/datagrid/datagrid_virtualization_example.js +++ b/src-docs/src/views/datagrid/datagrid_virtualization_example.js @@ -1,7 +1,7 @@ import React, { Fragment } from 'react'; import { renderToHtml } from '../../services'; - +import { EuiCallOut, EuiCode } from '../../../../src/components'; import { GuideSectionTypes } from '../../components'; import DataGridVirtualization from './virtualization'; @@ -30,7 +30,39 @@ export const DataGridVirtualizationExample = { ], text: ( -

Virtualization description

+

+ Creating a lot of DOM nodes is computationally expensive, and{' '} + EuiDataGrid uses a couple wrapping divs to build + each cell. To help offset the cost of larger tables, cell + virtualization can be opted into by constraining the grid's + height and/or width. There are two ways to enable this + functionality. First, height and/or{' '} + width can be passed as props, which are applied + to the grid's container style. Alternatively, if{' '} + EuiDataGrid is unable to render at the full + dimensions it needs due to screen real estate or other DOM + constraints, it will overflow within a scrollable container and only + render the visible cells. +

+ + + Never toggle the height between a value and{' '} + undefined. + + } + color="warning"> +

+ Similar to React's rule of not switching between a controlled + and uncontrolled input, EuiDataGrid does not + accommodate for its height switching to or from{' '} + undefined. For demonstration purposes, the + example below uses a key to force{' '} + EuiDataGrid to completely remount when its height + changes between constrained & constrained heights. +

+
), components: { DataGridVirtualization }, @@ -48,11 +80,6 @@ export const DataGridVirtualizationExample = { code: dataGridVirtualizationConstrainedHtml, }, ], - text: ( - -

Virtualization description

-
- ), demo: , }, ], diff --git a/src-docs/src/views/datagrid/virtualization_constrained.js b/src-docs/src/views/datagrid/virtualization_constrained.js index a459cc6a784..7ca7644bd04 100644 --- a/src-docs/src/views/datagrid/virtualization_constrained.js +++ b/src-docs/src/views/datagrid/virtualization_constrained.js @@ -157,7 +157,11 @@ export default () => { -

This is a side panel with more information.

+

+ This panel is constraining the datagrid. You can resize it + using the drag handle and EuiDataGrid{' '} + automatically detects the changes to its container size. +

From 1e345a1e54d54fa13f3c0e83bcdc404a49a665e5 Mon Sep 17 00:00:00 2001 From: Chandler Prall Date: Thu, 28 Jan 2021 09:41:33 -0700 Subject: [PATCH 53/54] Fix a unit test setup --- src/components/datagrid/data_grid.test.tsx | 10 ++++++++++ .../observer/resize_observer/resize_observer.tsx | 7 +------ 2 files changed, 11 insertions(+), 6 deletions(-) diff --git a/src/components/datagrid/data_grid.test.tsx b/src/components/datagrid/data_grid.test.tsx index 7a55333fd8a..601bfce5b8a 100644 --- a/src/components/datagrid/data_grid.test.tsx +++ b/src/components/datagrid/data_grid.test.tsx @@ -442,6 +442,16 @@ function moveColumnToIndex( describe('EuiDataGrid', () => { describe('rendering', () => { + const getBoundingClientRect = + window.Element.prototype.getBoundingClientRect; + beforeAll(() => { + window.Element.prototype.getBoundingClientRect = () => + ({ width: 100, height: 100 } as DOMRect); + }); + afterAll(() => { + window.Element.prototype.getBoundingClientRect = getBoundingClientRect; + }); + it('renders with common and div attributes', () => { const component = render( { - if (IS_JEST_ENVIRONMENT) { - setSize({ width: 100, height: 100 }); - } else if (container != null) { + if (container != null) { // ResizeObserver's first call to the observation callback is scheduled in the future // so find the container's initial dimensions now const boundingRect = container.getBoundingClientRect(); From ca0196670273f4cf7236e2406ced477f3865352e Mon Sep 17 00:00:00 2001 From: Chandler Prall Date: Thu, 28 Jan 2021 12:10:35 -0700 Subject: [PATCH 54/54] chanagelog --- CHANGELOG.md | 1 + 1 file changed, 1 insertion(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index 641a2cdd293..6b20b56d5b0 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,6 +1,7 @@ ## [`master`](https://github.com/elastic/eui/tree/master) - Added `getDefaultEuiMarkdownProcessingPlugins` method for better control over `EuiMarkdownEditor`'s toolbar UI ([#4383](https://github.com/elastic/eui/pull/4383)) +- Added virtualized cell rendering to `EuiDataGrid` ([#4170](https://github.com/elastic/eui/pull/4170)) **Bug fixes**