UNPKG

@salesforce/design-system-react

Version:

Salesforce Lightning Design System for React

298 lines (279 loc) 7.81 kB
/* Copyright (c) 2015-present, salesforce.com, inc. All rights reserved */ /* Licensed under BSD 3-Clause - see LICENSE.txt or git.io/sfdc-license */ // ### React import React, { useCallback, useContext, useMemo } from 'react'; import PropTypes from 'prop-types'; // ### classNames import classNames from 'classnames'; // ### find import find from 'lodash.find'; // ## Children import Checkbox from '../../checkbox'; import Radio from '../../radio'; // ## Constants import { DATA_TABLE_ROW, DATA_TABLE_ROW_ACTIONS, DATA_TABLE_CELL, } from '../../../utilities/constants'; import InteractiveElement from '../interactive-element'; import CellContext from '../private/cell-context'; import TableContext from '../private/table-context'; import useContextHelper from './context-helper'; const InteractiveCheckbox = InteractiveElement(Checkbox); const InteractiveRadio = InteractiveElement(Radio); const propTypes = { assistiveText: PropTypes.shape({ actionsHeader: PropTypes.string, columnSort: PropTypes.string, columnSortedAscending: PropTypes.string, columnSortedDescending: PropTypes.string, selectAllRows: PropTypes.string, selectRow: PropTypes.string, }), canSelectRows: PropTypes.oneOfType([ PropTypes.bool, PropTypes.oneOf(['checkbox', 'radio']), ]), className: PropTypes.string, columns: PropTypes.arrayOf( PropTypes.shape({ Cell: PropTypes.func, props: PropTypes.object, }) ), /** * Use this if you are creating an advanced table (selectable, sortable, or resizable rows) */ fixedLayout: PropTypes.bool, id: PropTypes.string.isRequired, item: PropTypes.object.isRequired, onToggle: PropTypes.func, rowActions: PropTypes.element, selection: PropTypes.array, tableId: PropTypes.string, disabledSelection: PropTypes.array, }; /** * Used internally, provides row rendering to the DataTable. */ const DataTableRow = (props) => { const tableContext = useContext(TableContext); const selectRowCellContext = useMemo( () => ({ rowIndex: props.rowIndex, columnIndex: 0, }), [props.rowIndex] ); const { tabIndex, hasFocus, handleFocus, handleKeyDown } = useContextHelper( tableContext, selectRowCellContext, props.fixedLayout ); const { item, onToggle } = props; const handleToggle = useCallback( (e, { checked }) => onToggle(item, checked, e), [item, onToggle] ); const isSelected = !!find(props.selection, item); const isDisabled = props.disabledSelection && !!find(props.disabledSelection, item); const ariaProps = useMemo(() => { const result = {}; if (props.canSelectRows) { result['aria-selected'] = isSelected ? 'true' : 'false'; } return result; }, [isSelected, props.canSelectRows]); const radionSelection = useMemo( () => isDisabled ? ( <Radio assistiveText={{ label: `${props.assistiveText.selectRow} ${ Number(props.index) + 1 }`, }} aria-labelledby={`${props.id}-SelectRow-label ${props.tableId}-SLDSDataTableHead-column-group-header-row-select`} checked={isSelected} className="slds-m-right_x-small" id={`${props.id}-SelectRow`} labelId={`${props.id}-SelectRow-label`} name={`${props.tableId}-SelectRow`} disabled={isDisabled} /> ) : ( <InteractiveRadio assistiveText={{ label: `${props.assistiveText.selectRow} ${ Number(props.index) + 1 }`, }} aria-labelledby={`${props.id}-SelectRow-label ${props.tableId}-SLDSDataTableHead-column-group-header-row-select`} checked={isSelected} className="slds-m-right_x-small" id={`${props.id}-SelectRow`} labelId={`${props.id}-SelectRow-label`} name={`${props.tableId}-SelectRow`} onChange={handleToggle} disabled={isDisabled} /> ), [ handleToggle, isSelected, isDisabled, props.assistiveText.selectRow, props.id, props.index, props.tableId, ] ); const checkboxSelection = useMemo( () => isDisabled ? ( <Checkbox assistiveText={{ label: `${props.assistiveText.selectRow} ${ Number(props.index) + 1 }`, }} aria-labelledby={`${props.id}-SelectRow-label ${props.tableId}-SLDSDataTableHead-column-group-header-row-select`} checked={isSelected} id={`${props.id}-SelectRow`} labelId={`${props.id}-SelectRow-label`} name={`SelectRow${props.index + 1}`} disabled={isDisabled} /> ) : ( <InteractiveCheckbox assistiveText={{ label: `${props.assistiveText.selectRow} ${ Number(props.index) + 1 }`, }} aria-labelledby={`${props.id}-SelectRow-label ${props.tableId}-SLDSDataTableHead-column-group-header-row-select`} checked={isSelected} id={`${props.id}-SelectRow`} labelId={`${props.id}-SelectRow-label`} name={`SelectRow${props.index + 1}`} onChange={handleToggle} disabled={isDisabled} /> ), [ handleToggle, isSelected, isDisabled, props.assistiveText.selectRow, props.id, props.index, props.tableId, ] ); // i18n return ( <tr {...ariaProps} className={classNames(props.className, { 'slds-hint-parent': props.rowActions, 'slds-is-selected': props.canSelectRows && isSelected, 'slds-has-focus': hasFocus, })} > {useMemo( () => ( <React.Fragment> {props.canSelectRows ? ( // eslint-disable-next-line jsx-a11y/no-noninteractive-element-interactions <td role={props.fixedLayout ? 'gridcell' : null} className="slds-text-align_right" data-label={props.stacked ? 'Select Row' : undefined} style={{ width: '3.25rem' }} onFocus={handleFocus} onKeyDown={handleKeyDown} ref={(ref) => { if (ref && hasFocus) { ref.focus(); } }} tabIndex={tabIndex} > <CellContext.Provider value={selectRowCellContext}> {props.canSelectRows === 'radio' ? radionSelection : checkboxSelection} </CellContext.Provider> </td> ) : null} {props.columns.map((column, index) => { const { Cell } = column; const cellId = `${props.id}-${DATA_TABLE_CELL}-${column.props.property}`; return ( <CellContext.Provider key={cellId} value={{ columnIndex: props.canSelectRows ? index + 1 : index, rowIndex: props.rowIndex, }} > <Cell {...column.props} className={column.props.truncate ? 'slds-truncate' : null} fixedLayout={props.fixedLayout} rowHeader={column.props.primaryColumn} id={cellId} item={item} width={column.props.width} headerId={item.headerId} columns={props.columns} > {item[column.props.property]} </Cell> </CellContext.Provider> ); })} <CellContext.Provider value={{ columnIndex: props.canSelectRows ? props.columns.length + 1 : props.columns.length, rowIndex: props.rowIndex, }} > {props.rowActions ? React.cloneElement(props.rowActions, { id: `${props.id}-${DATA_TABLE_ROW_ACTIONS}`, item, fixedLayout: props.fixedLayout, }) : null} </CellContext.Provider> </React.Fragment> ), [ handleFocus, handleKeyDown, hasFocus, item, tabIndex, props.canSelectRows, props.columns, props.fixedLayout, props.id, props.rowActions, props.rowIndex, props.stacked, selectRowCellContext, checkboxSelection, radionSelection, ] )} </tr> ); }; DataTableRow.displayName = DATA_TABLE_ROW; DataTableRow.propTypes = propTypes; export default DataTableRow;