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

[terra-table] Allow consumers to hide table column headers #1843

Merged
merged 21 commits into from
Oct 23, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
21 commits
Select commit Hold shift + click to select a range
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions packages/terra-framework-docs/CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@
## Unreleased

* Added
* Added documentation for the new `terra-table` component.
* Added test to cover enabling zebra striping for the `terra-table` component.
* Added test for tab focus for scrollable tables for the `terra-table` component.

Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
import TableWithoutHeaders from './TableWithoutHeaders?dev-site-example';

<TableWithoutHeaders title='Table Without Column Headers' />
Original file line number Diff line number Diff line change
@@ -0,0 +1,64 @@
import React from 'react';
import Table from 'terra-table';

const tableDataJSON = {
cols: [
{ id: 'Column-0', displayName: 'Patient' },
{ id: 'Column-1', displayName: 'Location' },
{ id: 'Column-2', displayName: 'Illness Severity' },
{ id: 'Column-3', displayName: 'Visit' },
{ id: 'Column-4', displayName: 'Allergy' },
{ id: 'Column-5', displayName: 'Primary Contact' },
],
rows: [
{
id: '1',
cells: [
{ content: 'Fleck, Arthur' },
{ content: '1007-MTN' },
{ content: 'Unstable' },
{ content: 'Inpatient, 2 months' },
{ content: '' },
{ content: 'Quinzell, Harleen' },
],
},
{
id: '2',
isSelected: true,
cells: [
{ content: 'Wayne, Bruce' },
{ content: '1007-MTN-DR' },
{ content: 'Stable' },
{ content: 'Outpatient, 2 days' },
{ content: 'Phytochemicals' },
{ content: 'Grayson, Richard' },
],
},
{
id: '3',
cells: [
{ content: 'Parker, Peter' },
{ content: '1018-MTN-DR' },
{ content: 'Severe' },
{ content: 'Inpatient, 2 days' },
{ content: 'Aspirin' },
{ content: 'Grayson, Richard' },
],
},
],
};

const TableWithoutHeaders = () => {
const { cols, rows } = tableDataJSON;

return (
<Table
id="table-without-headers"
overflowColumns={cols}
rows={rows}
hasColumnHeaders={false}
/>
);
};

export default TableWithoutHeaders;
Original file line number Diff line number Diff line change
@@ -0,0 +1,84 @@
import React from 'react';
import Table from 'terra-table';

const tableDataJSON = {
cols: [
{ id: 'Column-0', displayName: 'Patient' },
{ id: 'Column-1', displayName: 'Location' },
{ id: 'Column-2', displayName: 'Illness Severity' },
{ id: 'Column-3', displayName: 'Visit' },
{ id: 'Column-4', displayName: 'Allergy' },
{ id: 'Column-5', displayName: 'Primary Contact' },
{ id: 'Column-6', displayName: 'Generic Order Counts' },
{ id: 'Column-7', displayName: 'Patient Age' },
{ id: 'Column-8', displayName: 'Medication History' },
{ id: 'Column-9', displayName: 'My Relationship' },
{ id: 'Column-10', displayName: 'Not Selectable', isSelectable: false },
],
rows: [
{
id: '1',
cells: [
{ content: 'Fleck, Arthur' },
{ content: '1007-MTN' },
{ content: 'Unstable' },
{ content: 'Inpatient, 2 months' },
{ content: '' },
{ content: 'Quinzell, Harleen' },
{ content: '' },
{ isMasked: true },
{ isMasked: true },
{ content: 'Admitting Physician' },
{ content: '', isSelectable: false },
],
},
{
id: '2',
isSelected: true,
cells: [
{ content: 'Wayne, Bruce' },
{ content: '1007-MTN-DR' },
{ content: 'Stable' },
{ content: 'Outpatient, 2 days' },
{ content: 'Phytochemicals' },
{ content: 'Grayson, Richard' },
{ content: '' },
{ content: '' },
{ isMasked: true },
{ content: 'Admitting Physician' },
{ content: '', isSelectable: false },
],
},
{
id: '3',
cells: [
{ content: 'Parker, Peter' },
{ content: '1018-MTN-DR' },
{ content: 'Severe' },
{ content: 'Inpatient, 2 days' },
{ content: 'Aspirin' },
{ content: 'Grayson, Richard' },
{ content: '' },
{ content: '' },
{ isMasked: true },
{ content: 'Primary Care Physician' },
{ content: '', isSelectable: false },
],
},
],
};

const TableWithoutHeaders = () => {
const { cols, rows } = tableDataJSON;

return (
<Table
id="table-without-headers"
overflowColumns={cols}
rows={rows}
hasColumnHeaders={false}
/>
);
};

export default TableWithoutHeaders;
1 change: 1 addition & 0 deletions packages/terra-table/CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@

* Changed
* Updated the table component so that the cell dive-in logic would not execute when not in the grid context.
* Updated the table component to allow consumers to control the visibility of column headers in the table.
* Modified the table component so that it can receive focus when scrollable.

## 5.0.0-alpha.0 - (October 17, 2023)
Expand Down
18 changes: 17 additions & 1 deletion packages/terra-table/src/Table.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -103,6 +103,12 @@ const propTypes = {
hasSelectableRows: PropTypes.bool,

/**
* Boolean indicating whether or not the table columns should be displayed. Setting the value to false will hide the columns,
* but the voice reader will use the column header values for a11y.
*/
hasColumnHeaders: PropTypes.bool,

/*
* Boolean specifying whether or not the table should have zebra striping for rows.
*/
isStriped: PropTypes.bool,
Expand All @@ -116,6 +122,7 @@ const defaultProps = {
pinnedColumns: [],
overflowColumns: [],
rows: [],
hasColumnHeaders: true,
};

const defaultColumnMinimumWidth = 60;
Expand All @@ -136,6 +143,7 @@ function Table(props) {
onColumnSelect,
onCellSelect,
hasSelectableRows,
hasColumnHeaders,
isStriped,
rowHeaderIndex,
} = props;
Expand Down Expand Up @@ -301,14 +309,22 @@ function Table(props) {
role={gridContext.role}
aria-labelledby={ariaLabelledBy}
aria-label={ariaLabel}
className={cx('table', theme.className)}
className={cx('table', theme.className, { headerless: !hasColumnHeaders })}
{...(activeIndex != null && { onMouseUp, onMouseMove, onMouseLeave: onMouseUp })}
>
<ColumnContext.Provider
value={columnContextValue}
>
<colgroup>
{tableColumns.map((column) => (
// eslint-disable-next-line react/forbid-dom-props
<col key={column.id} style={{ width: `${column.width}px` }} />
))}
</colgroup>

<ColumnHeader
columns={tableColumns}
hasColumnHeaders={hasColumnHeaders}
headerHeight={columnHeaderHeight}
tableHeight={tableHeight}
onResizeMouseDown={onResizeMouseDown}
Expand Down
11 changes: 11 additions & 0 deletions packages/terra-table/src/Table.module.scss
Original file line number Diff line number Diff line change
Expand Up @@ -19,4 +19,15 @@
outline-offset: var(--terra-table-cell-focus-outline-offset, -2px);
}
}

/* stylelint-disable selector-max-compound-selectors */
.headerless {
tr:first-child {
td,
th {
border-top: var(--terra-table-headerless-first-row-border-top, 1px solid #dedfe0);
}
}
}
/* stylelint-enable selector-max-compound-selectors */
}
Original file line number Diff line number Diff line change
Expand Up @@ -2,5 +2,6 @@
.clinical-lowlight-theme {
--terra-table-cell-focus-outline: 3px dashed #fff;
--terra-table-cell-focus-outline-offset: -2px;
--terra-table-headerless-first-row-border-top: 1px solid #181b1d;
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -2,5 +2,6 @@
.orion-fusion-theme {
--terra-table-cell-focus-outline: 3px dashed #000;
--terra-table-cell-focus-outline-offset: -2px;
--terra-table-headerless-first-row-border-top: 1px solid #c8cacb;
}
}
25 changes: 21 additions & 4 deletions packages/terra-table/src/subcomponents/ColumnHeader.jsx
Original file line number Diff line number Diff line change
@@ -1,8 +1,11 @@
import React from 'react';
import PropTypes from 'prop-types';

import classNames from 'classnames/bind';
import ColumnHeaderCell from './ColumnHeaderCell';
import { columnShape } from '../proptypes/columnShape';
import styles from './ColumnHeader.module.scss';

const cx = classNames.bind(styles);

const propTypes = {
/**
Expand Down Expand Up @@ -30,6 +33,15 @@ const propTypes = {
* Function that is called when the mouse down event is triggered on the column resize handle.
*/
onResizeMouseDown: PropTypes.func,

/**
* Boolean indicating whether or not the table columns should be displayed.
*/
hasColumnHeaders: PropTypes.bool,
};

const defaultProps = {
hasColumnHeaders: true,
};

const ColumnHeader = (props) => {
Expand All @@ -39,11 +51,15 @@ const ColumnHeader = (props) => {
tableHeight,
onColumnSelect,
onResizeMouseDown,
hasColumnHeaders,
} = props;

return (
<thead>
<tr className="column-header-row" height={headerHeight}>
<tr
className={cx('column-header-row', { hidden: !hasColumnHeaders })}
height={hasColumnHeaders ? headerHeight : undefined}
>
{columns.map((column, columnIndex) => (
<ColumnHeaderCell
key={column.id}
Expand All @@ -55,8 +71,8 @@ const ColumnHeader = (props) => {
minimumWidth={column.minimumWidth}
maximumWidth={column.maximumWidth}
headerHeight={headerHeight}
isResizable={column.isResizable}
isSelectable={column.isSelectable}
isResizable={hasColumnHeaders && column.isResizable}
isSelectable={hasColumnHeaders && column.isSelectable}
tableHeight={tableHeight}
hasError={column.hasError}
sortIndicator={column.sortIndicator}
Expand All @@ -70,4 +86,5 @@ const ColumnHeader = (props) => {
};

ColumnHeader.propTypes = propTypes;
ColumnHeader.defaultProps = defaultProps;
export default React.memo(ColumnHeader);
13 changes: 13 additions & 0 deletions packages/terra-table/src/subcomponents/ColumnHeader.module.scss
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
:local {
.hidden {
border: 0;
clip: rect(0 0 0 0);
height: 1px;
margin: -1px;
overflow: hidden;
padding: 0;
position: absolute;
white-space: nowrap; /* Ensures words are not read one at a time on screen readers*/
width: 1px;
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -202,7 +202,7 @@ const ColumnHeaderCell = (props) => {
onMouseDown={isSelectable && onColumnSelect ? handleMouseDown : undefined}
onKeyDown={isSelectable && onColumnSelect ? handleKeyDown : undefined}
// eslint-disable-next-line react/forbid-dom-props
style={{ width: `${width}px`, height: headerHeight, left: cellLeftEdge }}
style={{ height: headerHeight, left: cellLeftEdge }}
>
<div className={cx('header-container')} role={displayName && 'button'}>
{errorIcon}
Expand Down
48 changes: 48 additions & 0 deletions packages/terra-table/tests/jest/ColumnHeader.test.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -128,4 +128,52 @@ describe('ColumnHeader', () => {

expect(wrapper).toMatchSnapshot();
});

it('verifies that the hasColumnHeaders prop hides the table column headers when set to false', () => {
const columns = [{
id: 'Column-0',
displayName: ' Vitals',
}, {
id: 'Column-1',
displayName: ' Patient',
}];

const wrapper = shallow(
<ColumnHeader
columns={columns}
hasColumnHeaders={false}
headerHeight="3rem"
/>,
);

// Verify that column headers are not present
const columnHeader = wrapper.find('.hidden');
expect(columnHeader).toHaveLength(1);

expect(wrapper).toMatchSnapshot();
});

it('verifies that the column headers are not hidden when the hasColumnHeader is true', () => {
const columns = [{
id: 'Column-0',
displayName: ' Vitals',
}, {
id: 'Column-1',
displayName: ' Patient',
}];

const wrapper = shallow(
<ColumnHeader
columns={columns}
headerHeight="3rem"
hasColumnHeaders
/>,
);

// Verify that column headers are present
const columnHeader = wrapper.find('.hidden');
expect(columnHeader).toHaveLength(0);

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