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

[terra-flowsheet-data-grid] Allow flowsheet sections to have sticky title #2043

Merged
merged 6 commits into from
Feb 28, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
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
2 changes: 2 additions & 0 deletions packages/terra-data-grid/CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,8 @@

## Unreleased

* Added `boundingRef` prop for bounded flowsheet data grids.

## 1.14.0 - (February 20, 2024)

* Changed
Expand Down
6 changes: 6 additions & 0 deletions packages/terra-data-grid/src/DataGrid.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -145,6 +145,10 @@ const propTypes = {
* With this property the height of the cell will grow to fit the cell content.
*/
rowMinimumHeight: PropTypes.string,
/**
* Bounding container for the grid, will use window if no value provided.
*/
boundingRef: PropTypes.func,
};

const defaultProps = {
Expand Down Expand Up @@ -182,6 +186,7 @@ const DataGrid = forwardRef((props, ref) => {
rows,
sections,
rowMinimumHeight,
boundingRef,
} = props;

const displayedColumns = (hasSelectableRows ? [WorklistDataGridUtils.ROW_SELECTION_COLUMN] : []).concat(pinnedColumns).concat(overflowColumns);
Expand Down Expand Up @@ -586,6 +591,7 @@ const DataGrid = forwardRef((props, ref) => {
hasVisibleColumnHeaders={hasVisibleColumnHeaders}
isStriped
rowMinimumHeight={rowMinimumHeight}
boundingRef={boundingRef}
/>
</GridContext.Provider>
<VisuallyHiddenText aria-live="polite" aria-atomic="true" text={cellAriaLiveMessage} />
Expand Down
6 changes: 6 additions & 0 deletions packages/terra-data-grid/src/FlowsheetDataGrid.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -108,6 +108,10 @@ const propTypes = {
* Boolean to show/hide column headers. By default, it is set to `true` and column headers are visible.
*/
hasVisibleColumnHeaders: PropTypes.bool,
/**
* Bounding container for the flowsheet grid, will use window if no value provided.
*/
boundingRef: PropTypes.func,
};

const defaultProps = {
Expand Down Expand Up @@ -137,6 +141,7 @@ function FlowsheetDataGrid(props) {
intl,
hasVisibleColumnHeaders,
rowMinimumHeight,
boundingRef,
} = props;

const anchorCell = useRef(null);
Expand Down Expand Up @@ -423,6 +428,7 @@ function FlowsheetDataGrid(props) {
hasVisibleColumnHeaders={hasVisibleColumnHeaders}
ref={dataGridFuncRef}
rowMinimumHeight={rowMinimumHeight}
boundingRef={boundingRef}
/>
<VisuallyHiddenText aria-live="polite" text={cellSelectionAriaLiveMessage} />
</div>
Expand Down
3 changes: 3 additions & 0 deletions packages/terra-framework-docs/CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,9 @@
* Changed
* Updated `terra-menu` test for selectable menu to retain selection state when using `isToggled` prop after closing and re-opening the menu.

* Added
* Added example for bounded `terra-flowsheet-data-grid` with sections.

## 1.67.0 - (February 22, 2024)

* Changed
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,165 @@
import React, { useCallback, useState, useRef } from 'react';
import { FlowsheetDataGrid } from 'terra-data-grid';

const gridDataJSON = {
cols: [
{
id: 'Column-0', displayName: 'Patient', sortIndicator: 'ascending', isSelectable: true,
},
{
id: 'Column-1', displayName: 'Location', isSelectable: true,
},
{ id: 'Column-2', displayName: 'Illness Severity', isSelectable: true },
{ id: 'Column-3', displayName: 'Visit' },
{ id: 'Column-4', displayName: 'Allergy' },
{ id: 'Column-5', displayName: 'Primary Contact' },

],
sections: [{
id: 'section-0',
isCollapsible: true,
text: 'Test Section',
rows: [
{
id: '1',
cells: [
{ content: 'Fleck, Arthur' },
{ content: '1007-MTN' },
{ content: 'Unstable' },
{ content: 'Inpatient, 2 months' },
{ content: '' },
{ content: 'Quinzell, Harleen' },
],
},
{
id: '2',
cells: [
{ content: 'Wayne, Bruce' },
{ content: '1007-MTN-DR' },
{ content: 'Stable' },
{ content: 'Outpatient, 2 days' },
{ content: 'Phytochemicals' },
{ content: 'Grayson, Richard' },
],
},
],
},
{
id: 'section-1',
text: 'Test Section #2',
rows: [
{
id: '3',
cells: [
{ content: 'Parker, Peter' },
{ content: '1007-MTN' },
{ content: 'Unstable' },
{ content: 'Inpatient, 2 months' },
{ content: '' },
{ content: 'Octopus, Doctor' },
],
},
{
id: '4',
cells: [
{ content: 'Stark, Tony' },
{ content: '1007-MTN-DR' },
{ content: 'Stable' },
{ content: 'Outpatient, 2 days' },
{ content: 'Phytochemicals' },
{ content: 'America, Captain' },
],
},
],
}],
};

const BoundedFlowsheetWithSections = () => {
const boundingRef = useRef(null);
const [tableSections, setTableSections] = useState(gridDataJSON.sections);

const handleSectionSelect = (sectionId) => {
const newSections = [...tableSections];
const sectionIndex = newSections.findIndex(section => section.id === sectionId);

const sectionToClear = newSections.find(section => section.id === sectionId);

sectionToClear.rows = sectionToClear.rows.map(row => ({
...row,
cells: row.cells.map(cell => ({
...cell,
isSelected: false,
})),
}));

if (sectionIndex > -1) {
newSections[sectionIndex].isCollapsed = !newSections[sectionIndex].isCollapsed;
}

setTableSections(newSections);
};

const getClearedSections = useCallback(() => tableSections.map(section => ({
...section,
rows: section.rows.map(row => ({
...row,
cells: row.cells.map(cell => ({
...cell,
isSelected: false,
})),
})),
})), [tableSections]);

const onCellSelect = useCallback((selectedCell) => {
let selectedSection = tableSections.find(section => section.id === selectedCell.sectionId);

const columnIndex = gridDataJSON.cols.findIndex(col => col.id === selectedCell.columnId);
const rowIndex = selectedSection.rows.findIndex(row => row.id === selectedCell.rowId);
const previousCell = selectedSection.rows[rowIndex].cells[columnIndex];

const newSections = getClearedSections();

// // If the current cell is the only selected cell, toggle it to unselected. Otherwise, set it to selected.
selectedSection = newSections.find(section => section.id === selectedCell.sectionId);
selectedSection.rows[rowIndex].cells[columnIndex].isSelected = !previousCell.isSelected;
setTableSections(newSections);
}, [tableSections, getClearedSections]);

const handleCellRangeSelection = useCallback((cells) => {
const columnIndexesToUpdate = new Set(cells
.map(cell => cell.columnId)
.map(id => gridDataJSON.cols.findIndex(col => col.id === id)));

const rowsToUpdate = new Set(cells.map(cell => cell.rowId));

const newSections = getClearedSections();
const selectedSection = newSections.find(section => section.id === cells[0].sectionId);

selectedSection.rows = selectedSection.rows.map(row => ({
...row,
cells: row.cells.map((cell, cellIndex) => ({
...cell,
isSelected: columnIndexesToUpdate.has(cellIndex) && rowsToUpdate.has(row.id),
})),
}));

setTableSections(newSections);
}, [getClearedSections]);

return (
// eslint-disable-next-line react/forbid-dom-props
<div style={{ height: '150px', width: '700px' }} ref={boundingRef}>
<FlowsheetDataGrid
id="flowsheet-with-sections-bounded"
columns={gridDataJSON.cols}
sections={tableSections}
onCellSelect={onCellSelect}
onSectionSelect={handleSectionSelect}
onCellRangeSelect={handleCellRangeSelection}
boundingRef={boundingRef}
/>
</div>
);
};

export default BoundedFlowsheetWithSections;
6 changes: 6 additions & 0 deletions packages/terra-table/CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,12 @@

## Unreleased

* Added
* Added `boundingRef` prop for bounded table.

* Changed
* Changed flowsheet data grid sections to have sticky title.

## 5.7.0 - (February 20, 2024)

* Added
Expand Down
6 changes: 6 additions & 0 deletions packages/terra-table/src/Table.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -176,6 +176,10 @@ const propTypes = {
* The intl object containing translations. This is retrieved from the context automatically by injectIntl.
*/
intl: PropTypes.shape({ formatMessage: PropTypes.func }).isRequired,
/**
* Bounding container for table, will use window if no value provided.
*/
boundingRef: PropTypes.func,
};

const defaultProps = {
Expand Down Expand Up @@ -219,6 +223,7 @@ function Table(props) {
rowHeaderIndex,
intl,
rowMinimumHeight,
boundingRef,
} = props;

// Manage column resize
Expand Down Expand Up @@ -617,6 +622,7 @@ function Table(props) {
onCellSelect={isGridContext || rowSelectionMode ? handleCellSelection : undefined}
onSectionSelect={onSectionSelect}
rowMinimumHeight={rowMinimumHeight}
boundingRef={boundingRef}
/>
))}
</ColumnContext.Provider>
Expand Down
10 changes: 9 additions & 1 deletion packages/terra-table/src/subcomponents/Section.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -101,6 +101,10 @@ const propTypes = {
* Function that is called when a collapsible section is selected. Parameters: `onSectionSelect(sectionId)`
*/
onSectionSelect: PropTypes.func,
/**
* Bounding container for the table, will use window if no value provided.
*/
boundingRef: PropTypes.func,
};

const defaultProps = {
Expand Down Expand Up @@ -128,6 +132,7 @@ function Section(props) {
rows,
onSectionSelect,
rowMinimumHeight,
boundingRef,
} = props;

const theme = useContext(ThemeContext);
Expand All @@ -136,6 +141,8 @@ function Section(props) {
const isGridContext = gridContext.role === GridConstants.GRID;

const hasSectionButton = isCollapsible && onSectionSelect;
const boundedWidth = isCollapsible && boundingRef && boundingRef.current ? boundingRef.current.clientWidth - 50 : null;
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The 50px offset used here is to reduce button width (under header) further from the bounding width to maintain sticky header. It fails to stick when its exactly set to container width.

const titlePosition = boundingRef ? { isTitleSticky: true } : { isTitleFixed: true };

const handleClick = useCallback(() => {
onSectionSelect(id);
Expand Down Expand Up @@ -164,8 +171,9 @@ function Section(props) {
className={cx('section-header')}
text={text}
isOpen={hasSectionButton ? !isCollapsed : undefined}
isTitleFixed
onClick={hasSectionButton ? handleClick : undefined}
boundedWidth={boundedWidth}
{...titlePosition}
/>
</th>
</tr>
Expand Down
Loading