terra-table
Version:
The Terra Table component provides user a way to display data in an accessible table format.
224 lines (193 loc) • 6.26 kB
JSX
import React, { useContext, useState } from 'react';
import classNames from 'classnames/bind';
import PropTypes from 'prop-types';
import ThemeContext from 'terra-theme-context';
import Cell from './Cell';
import cellShape from '../proptypes/cellShape';
import columnShape from '../proptypes/columnShape';
import RowSelectionCell from './RowSelectionCell';
import styles from './Row.module.scss';
const cx = classNames.bind(styles);
const propTypes = {
/**
* An identifier to uniquely identify the row within the table.
*/
id: PropTypes.string.isRequired,
/**
* Unique identifier for the parent table
*/
tableId: PropTypes.string.isRequired,
/**
* The row's position in the table. This is zero based.
*/
rowIndex: PropTypes.number,
/**
* An identifier for the section.
*/
sectionId: PropTypes.string,
/**
* An identifier for the subsection.
*/
subsectionId: PropTypes.string,
/**
* String that specifies height of the row. Any valid CSS width value is accepted.
*/
height: PropTypes.string,
/**
* String that specifies the minimum height for the rows on the table. rowHeight takes precedence if valid CSS value is passed.
* With this property the height of the cell will grow to fit the cell content.
*/
rowMinimumHeight: PropTypes.string,
/**
* Data to be displayed in the cells of the row. Cells will be rendered in the row in the order given.
*/
cells: PropTypes.arrayOf(cellShape),
/**
* A boolean indicating whether or not the row should render as selected.
*/
isSelected: PropTypes.bool,
/**
* Boolean specifying whether zebra striping is enabled.
*/
isTableStriped: PropTypes.bool,
/**
* A string identifier used to describe the row contents. This value will be used to construct additional labels
* for internal controls (e.g. row selection cells).
*/
ariaLabel: PropTypes.string,
/**
* Enables row selection capabilities for the table.
* Use 'single' for single row selection and 'multiple' for multi-row selection.
*/
rowSelectionMode: PropTypes.string,
/**
* All columns currently displayed.
*/
displayedColumns: PropTypes.arrayOf(columnShape),
/**
* Callback function that will be called when a cell in the row is selected.
* @param {string} rowId rowId
* @param {string} columnId columnId
* @param {number} columnSpanIndex columnSpanIndex
* @param {object} event event
*/
onCellSelect: PropTypes.func,
/**
* A zero-based index indicating which column represents the row header.
* Index can be set to -1 if row headers are not required.
*/
rowHeaderIndex: PropTypes.number,
/**
* @private
* Id of the first row in table
*/
firstRowId: PropTypes.string,
/**
* @private
* Id of the last row in table
*/
lastRowId: PropTypes.string,
};
const defaultProps = {
rowHeaderIndex: 0,
isSelected: false,
};
function Row(props) {
const {
rowIndex,
height,
rowSelectionMode,
id,
tableId,
sectionId,
subsectionId,
isSelected,
isTableStriped,
cells,
ariaLabel,
displayedColumns,
rowHeaderIndex,
onCellSelect,
rowMinimumHeight,
firstRowId,
lastRowId,
} = props;
const theme = useContext(ThemeContext);
const [isHovered, setHovered] = useState(false);
const isMultiRowSelect = (rowSelectionMode === 'multiple');
const columnIndexOffSet = isMultiRowSelect ? 1 : 0;
// Added to check if rowHeight is defined, it will take precedence. Otherwise the minimum row height would be used.
const heightProperties = (height) ? {
height,
} : { minHeight: rowMinimumHeight };
return (
<tr
aria-rowindex={rowIndex}
data-row-id={id}
className={cx('row', {
selected: isSelected,
selectable: !!rowSelectionMode,
'striped-table-row': isTableStriped,
}, theme.className)}
// eslint-disable-next-line react/forbid-dom-props
style={{ ...heightProperties }}
onMouseEnter={rowSelectionMode ? () => { setHovered(true); } : undefined}
onMouseLeave={rowSelectionMode ? () => { setHovered(false); } : undefined}
>
{
isMultiRowSelect
&& (
<RowSelectionCell
rowId={id}
columnId={displayedColumns[0].id}
rowIndex={rowIndex}
columnIndex={0}
sectionId={sectionId}
tableId={tableId}
isSelected={isSelected}
isHighlighted={isHovered || isSelected}
ariaLabel={ariaLabel}
onCellSelect={onCellSelect}
/>
)
}
{cells.map((cellData, index) => {
const cellColumnIndex = index + columnIndexOffSet;
const columnId = displayedColumns[cellColumnIndex].id;
const columnSpanIndex = displayedColumns[cellColumnIndex].columnSpanIndex ? displayedColumns[cellColumnIndex].columnSpanIndex : undefined;
return (
<Cell
rowId={id}
columnId={columnId}
rowIndex={rowIndex}
columnIndex={cellColumnIndex}
sectionId={sectionId}
subsectionId={subsectionId}
tableId={tableId}
key={columnSpanIndex ? `${id}_${columnId}_${columnSpanIndex}` : `${id}_${columnId}`}
isSelected={!rowSelectionMode && cellData.isSelected}
isMasked={cellData.isMasked}
maskedLabel={cellData.maskedLabel}
isSelectable={cellData.isSelectable}
isRowHeader={rowHeaderIndex !== -1 && cellColumnIndex === (rowHeaderIndex + columnIndexOffSet)}
isHighlighted={isHovered || isSelected}
onCellSelect={onCellSelect}
height={height}
rowMinimumHeight={rowMinimumHeight}
rowHeaderIndex={rowHeaderIndex}
firstRowId={firstRowId}
lastRowId={lastRowId}
columnHighlightColor={displayedColumns[cellColumnIndex].columnHighlightColor}
columnSpanIndex={columnSpanIndex}
rowSelectionMode={rowSelectionMode}
>
{cellData.content}
</Cell>
);
})}
</tr>
);
}
Row.propTypes = propTypes;
Row.defaultProps = defaultProps;
export default React.memo(Row);