Skip to content
This repository has been archived by the owner on May 24, 2024. It is now read-only.

Commit

Permalink
Update package refs. Attempt to useImperativeHandle to reduce fragile…
Browse files Browse the repository at this point in the history
… ref code in datagrid. Move table props to separate files to avoid dev props table bug.
  • Loading branch information
kenk2 committed Oct 26, 2023
1 parent f74684a commit f9f7448
Show file tree
Hide file tree
Showing 15 changed files with 40,415 additions and 38,802 deletions.
2 changes: 1 addition & 1 deletion packages/terra-data-grid/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -34,7 +34,7 @@
"keycode-js": "^3.1.0",
"prop-types": "^15.5.8",
"terra-icon": "^3.54.0",
"terra-table": "^5.0.0-alpha",
"terra-table": "^5.1.1-alpha.0",
"terra-theme-context": "^1.8.0",
"terra-visually-hidden-text": "^2.36.0"
},
Expand Down
72 changes: 33 additions & 39 deletions packages/terra-data-grid/src/DataGrid.jsx
Original file line number Diff line number Diff line change
@@ -1,19 +1,18 @@
import React, {
useState, useRef, useCallback, useEffect, forwardRef, useImperativeHandle,
useState, useRef, useCallback, forwardRef, useImperativeHandle,
} from 'react';
import PropTypes from 'prop-types';
import { injectIntl } from 'react-intl';
import classNames from 'classnames/bind';
import * as KeyCode from 'keycode-js';
import Table, { GridContext } from 'terra-table';
import Table, { GridConstants, GridContext } from 'terra-table';
import VisuallyHiddenText from 'terra-visually-hidden-text';
import rowShape from './proptypes/rowShape';
import { columnShape } from './proptypes/columnShape';
import WorklistDataGridUtils from './utils/WorklistDataGridUtils';
import validateRowHeaderIndex from './proptypes/validators';
import styles from './DataGrid.module.scss';
import './_elementPolyfill';
import { mapDataGridColumn, mapDataGridRow } from './utils/dataGridMappers';

const cx = classNames.bind(styles);

Expand Down Expand Up @@ -145,11 +144,11 @@ const DataGrid = injectIntl((props) => {

const grid = useRef();
const gridContainerRef = useRef();

const tableContainerRef = useRef();
const handleFocus = useRef(true);

const focusedRow = useRef(0);
const focusedCol = useRef(0);
const [focusedRow, setFocusedRow] = useState(0);
const [focusedCol, setFocusedCol] = useState(0);
const [cellAriaLiveMessage, setCellAriaLiveMessage] = useState(null);
// -------------------------------------
// functions
Expand All @@ -160,8 +159,8 @@ const DataGrid = injectIntl((props) => {

const setFocusedRowCol = (newRowIndex, newColIndex, makeActiveElement) => {
setCellAriaLiveMessage(null);
focusedRow.current = newRowIndex;
focusedCol.current = newColIndex;
setFocusedRow(newRowIndex);
setFocusedCol(newColIndex);
let focusedCell = grid.current.rows[newRowIndex].cells[newColIndex];
if (isRowSelectionCell(newColIndex) && focusedCell.getElementsByTagName('input').length > 0) {
[focusedCell] = focusedCell.getElementsByTagName('input');
Expand All @@ -180,40 +179,34 @@ const DataGrid = injectIntl((props) => {
props.focusFuncRef,
() => ({
setFocusedRowCol,
getFocusedCell() { return { row: focusedRow.current, col: focusedCol.current }; },
getFocusedCell() { return { row: focusedRow, col: focusedCol }; },
}),
// eslint-disable-next-line react-hooks/exhaustive-deps
[focusedCol, focusedRow],
);

// -------------------------------------
// useEffect Hooks

useEffect(() => {
// We have to search the dom for the table ref because react-intl v2 does not support forwardRef.

const [table] = gridContainerRef.current.getElementsByTagName('table');
grid.current = table;
}, [gridContainerRef]);
const handleTableRefs = useCallback((node) => {
grid.current = node.tableRef;
tableContainerRef.current = node.containerRef;
}, []);

// -------------------------------------

const handleMoveCellFocus = (fromCell, toCell) => {
// Obtain coordinate rectangles for grid container, column header, and new cell selection
const tableContainer = gridContainerRef.current.firstChild;
const gridContainerRect = tableContainer.getBoundingClientRect();
const gridContainerRect = tableContainerRef.current.getBoundingClientRect();
const columnHeaderRect = grid.current.rows[0].cells[toCell.col].getBoundingClientRect();
const nextCellRect = grid.current.rows[toCell.row].cells[toCell.col].getBoundingClientRect();

// Calculate horizontal scroll offset for right boundary
if (nextCellRect.right > gridContainerRect.right) {
tableContainer.scrollBy(nextCellRect.right - gridContainerRect.right, 0);
tableContainerRef.current.scrollBy(nextCellRect.right - gridContainerRect.right, 0);
} else {
// Calculate horizontal scroll offset for left boundary
let scrollOffsetX = 0;
if (pinnedColumns.length > 0 || hasSelectableRows) {
const pinnedColumnOffset = hasSelectableRows ? 1 : 0;
const lastPinnedColumnIndex = pinnedColumns.length - 1 + pinnedColumnOffset;
const pinnedColumnOffset = hasSelectableRows ? 1 : 0;
const lastPinnedColumnIndex = pinnedColumns.length - 1 + pinnedColumnOffset;
if (lastPinnedColumnIndex >= 0) {
if (toCell.col > lastPinnedColumnIndex) {
const lastPinnedColumnRect = grid.current.rows[toCell.row].cells[lastPinnedColumnIndex].getBoundingClientRect();
scrollOffsetX = nextCellRect.left - lastPinnedColumnRect.right;
Expand All @@ -223,32 +216,31 @@ const DataGrid = injectIntl((props) => {
}

if (scrollOffsetX < 0) {
tableContainer.scrollBy(scrollOffsetX, 0);
tableContainerRef.current.scrollBy(scrollOffsetX, 0);
}
}

// Calculate vertical scroll offset
const scrollOffsetY = nextCellRect.top - columnHeaderRect.bottom;
if (scrollOffsetY < 0) {
tableContainer.scrollBy(0, scrollOffsetY);
tableContainerRef.current.scrollBy(0, scrollOffsetY);
}

setFocusedRowCol(toCell.row, toCell.col, true);
};

const handleColumnSelect = useCallback((columnId) => {
const columnIndex = displayedColumns.findIndex(column => column.id === columnId);
focusedRow.current = 0;
focusedCol.current = columnIndex;
setFocusedCol(columnIndex === -1 ? 0 : columnId);

if (onColumnSelect) {
onColumnSelect(columnId);
onColumnSelect(columnIndex === -1 ? WorklistDataGridUtils.ROW_SELECTION_COLUMN.id : columnId);
}
}, [onColumnSelect, displayedColumns]);

const handleCellSelection = useCallback((selectionDetails) => {
focusedRow.current = selectionDetails.rowIndex;
focusedCol.current = selectionDetails.columnIndex;
setFocusedRow(selectionDetails.rowIndex);
setFocusedCol(selectionDetails.columnIndex);
if (onCellSelect) {
onCellSelect(selectionDetails);
}
Expand All @@ -272,7 +264,7 @@ const DataGrid = injectIntl((props) => {
&& !!(element.offsetWidth || element.offsetHeight || element.getClientRects().length)
&& window.getComputedStyle(element).visibility !== 'hidden'
&& element.closest('[inert]') === null
&& (element.id === id || !grid.current.contains(element)),
&& (element.id === id || !gridContainerRef.current.contains(element)),
);

// Identify index of the active element in the DOM excluding data grid children
Expand Down Expand Up @@ -312,7 +304,7 @@ const DataGrid = injectIntl((props) => {
};

const handleKeyDown = (event) => {
const cellCoordinates = { row: focusedRow.current, col: focusedCol.current };
const cellCoordinates = { row: focusedRow, col: focusedCol };
let nextRow = cellCoordinates.row;
let nextCol = cellCoordinates.col;

Expand Down Expand Up @@ -419,7 +411,7 @@ const DataGrid = injectIntl((props) => {
if (!event.currentTarget.contains(event.relatedTarget)) {
// Not triggered when swapping focus between children
if (handleFocus.current) {
setFocusedRowCol(focusedRow.current, focusedCol.current, true);
setFocusedRowCol(focusedRow, focusedCol, true);
}
}

Expand All @@ -437,16 +429,17 @@ const DataGrid = injectIntl((props) => {
onKeyDown={handleKeyDown}
onMouseDown={onMouseDown}
onFocus={onFocus}
id={id}
className={cx('data-grid-container')}
>
<GridContext.Provider value={{ role: 'grid', setCellAriaLiveMessage }}>
<GridContext.Provider value={{ role: GridConstants.GRID, setCellAriaLiveMessage }}>
<Table
id={id}
rows={rows.map(row => mapDataGridRow(row))}
id={`${id}-table`}
rows={rows}
ariaLabelledBy={ariaLabelledBy}
ariaLabel={ariaLabel}
pinnedColumns={pinnedColumns.map(column => mapDataGridColumn(column))}
overflowColumns={overflowColumns.map(column => mapDataGridColumn(column))}
pinnedColumns={pinnedColumns}
overflowColumns={overflowColumns}
defaultColumnWidth={defaultColumnWidth}
columnHeaderHeight={columnHeaderHeight}
rowHeight={rowHeight}
Expand All @@ -456,6 +449,7 @@ const DataGrid = injectIntl((props) => {
onCellSelect={handleCellSelection}
hasSelectableRows={hasSelectableRows}
isStriped
ref={handleTableRefs}
/>
</GridContext.Provider>
<VisuallyHiddenText aria-live="polite" aria-atomic="true" text={cellAriaLiveMessage} />
Expand Down
7 changes: 6 additions & 1 deletion packages/terra-data-grid/src/FlowsheetDataGrid.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -98,7 +98,11 @@ function FlowsheetDataGrid(props) {
intl,
} = props;

const flowsheetColumns = useMemo(() => columns.map(column => ({ ...column, isResizable: false })), [columns]);
const flowsheetColumns = useMemo(() => columns.map(column => ({
...column,
isSelectable: column.isSelectable !== false,
isResizable: false,
})), [columns]);
const pinnedColumns = flowsheetColumns.length ? [flowsheetColumns[0]] : [];
const overflowColumns = flowsheetColumns.length > 1 ? flowsheetColumns.slice(1) : [];

Expand All @@ -116,6 +120,7 @@ function FlowsheetDataGrid(props) {
newRows.forEach((row, rowIndex) => {
const newCells = [...row.cells];
newCells.forEach((cell, cellIndex) => {
newCells[cellIndex].isSelectable = cell.isSelectable !== false;
// Cell content has no result and is not a row header (first column), set content to "No result".
if (contentHasNoResult(cell.content) && cellIndex !== 0) {
newCells[cellIndex].content = noResultCellContent;
Expand Down
23 changes: 19 additions & 4 deletions packages/terra-data-grid/src/WorklistDataGrid.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,6 @@ import { columnShape } from './proptypes/columnShape';
import validateRowHeaderIndex from './proptypes/validators';
import styles from './WorklistDataGrid.module.scss';
import DataGrid from './DataGrid';
import { mapDataGridColumn, mapDataGridRow } from './utils/dataGridMappers';

const cx = classNames.bind(styles);

Expand Down Expand Up @@ -179,6 +178,22 @@ function WorklistDataGrid(props) {
const gridReceivedFocus = useRef(false);
const gridHasFocus = document.getElementById(`${id}-worklist-data-grid-container`)?.contains(document.activeElement);

const makeWorklistDataGridColumns = (column) => ({
...column,
isResizable: column.isResizable !== false,
isSelectable: column.isSelectable !== false,
});

const worklistDataGridPinnedColumns = pinnedColumns.map(column => makeWorklistDataGridColumns(column));
const worklistDataGridOverflowColumns = overflowColumns.map(column => makeWorklistDataGridColumns(column));
const worklistDataGridRows = rows.map((row) => ({
...row,
cells: row.cells.map((cell) => ({
...cell,
isSelectable: cell.isSelectable !== false,
})),
}));

// -------------------------------------
// useEffect Hooks

Expand Down Expand Up @@ -419,11 +434,11 @@ function WorklistDataGrid(props) {
id={id}
ariaLabel={ariaLabel}
ariaLabelledBy={ariaLabelledBy}
rows={rows.map(row => mapDataGridRow(row))}
rows={worklistDataGridRows}
rowHeight={rowHeight}
rowHeaderIndex={rowHeaderIndex}
pinnedColumns={pinnedColumns.map(column => mapDataGridColumn(column))}
overflowColumns={overflowColumns.map(column => mapDataGridColumn(column))}
pinnedColumns={worklistDataGridPinnedColumns}
overflowColumns={worklistDataGridOverflowColumns}
defaultColumnWidth={defaultColumnWidth}
columnHeaderHeight={columnHeaderHeight}
onColumnSelect={onColumnSelect}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@
* The Object representation of the row selection column.
*/
const ROW_SELECTION_COLUMN = {
id: 'table-rowSelectionColumn',
id: 'WorklistDataGrid-rowSelectionColumn',
width: 40,
isSelectable: true,
isResizable: false,
Expand Down
15 changes: 0 additions & 15 deletions packages/terra-data-grid/src/utils/dataGridMappers.js

This file was deleted.

11 changes: 6 additions & 5 deletions packages/terra-data-grid/tests/jest/DataGrid.test.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,6 @@ import { mountWithIntl, shallowWithIntl } from 'terra-enzyme-intl';
import DataGrid from '../../src/DataGrid';
import WorklistDataGridUtils from '../../src/utils/WorklistDataGridUtils';
import ERRORS from '../../src/utils/constants';
import { mapDataGridColumn, mapDataGridRow } from '../../src/utils/dataGridMappers';

// Source data for tests
const dataFile = {
Expand Down Expand Up @@ -77,7 +76,8 @@ describe('DataGrid', () => {
);

// One row used for the header.
const columnHeader = wrapper.find('Memo(ColumnHeader)');

const columnHeader = wrapper.find('ColumnHeader');
expect(columnHeader).toHaveLength(1);

// The number of rows should match the given data.
Expand Down Expand Up @@ -106,9 +106,10 @@ describe('DataGrid', () => {

const rows = wrapper.find('Row');
expect(rows).toHaveLength(dataFile.rows.length);
verifyRow(0, rows.get(0), mapDataGridRow(dataFile.rows[0]), dataFile.cols.map(columns => mapDataGridColumn(columns)));
verifyRow(1, rows.get(1), mapDataGridRow(dataFile.rows[1]), dataFile.cols.map(columns => mapDataGridColumn(columns)));
verifyRow(2, rows.get(2), mapDataGridRow(dataFile.rows[2]), dataFile.cols.map(columns => mapDataGridColumn(columns)));

verifyRow(0, rows.get(0), dataFile.rows[0], dataFile.cols);
verifyRow(1, rows.get(1), dataFile.rows[1], dataFile.cols);
verifyRow(2, rows.get(2), dataFile.rows[2], dataFile.cols);

expect(wrapper).toMatchSnapshot();
});
Expand Down
Loading

0 comments on commit f9f7448

Please sign in to comment.