diff --git a/packages/terra-data-grid/CHANGELOG.md b/packages/terra-data-grid/CHANGELOG.md index da97e340cca..2efd8cfdf67 100644 --- a/packages/terra-data-grid/CHANGELOG.md +++ b/packages/terra-data-grid/CHANGELOG.md @@ -2,6 +2,9 @@ ## Unreleased +* Added + * Added support for orange and green column highlighting. + ## 1.18.0 - (March 5, 2024) * Changed diff --git a/packages/terra-data-grid/src/DataGrid.jsx b/packages/terra-data-grid/src/DataGrid.jsx index 7e8062dccf6..b01dc41875f 100644 --- a/packages/terra-data-grid/src/DataGrid.jsx +++ b/packages/terra-data-grid/src/DataGrid.jsx @@ -5,7 +5,7 @@ import PropTypes from 'prop-types'; import classNames from 'classnames/bind'; import * as KeyCode from 'keycode-js'; import Table, { - GridConstants, GridContext, sectionShape, rowShape, columnShape, validateRowHeaderIndex, hasColumnActions, + GridConstants, GridContext, sectionShape, rowShape, columnShape, validateRowHeaderIndex, hasColumnActions, ColumnHighlightColor, } from 'terra-table'; import VisuallyHiddenText from 'terra-visually-hidden-text'; import WorklistDataGridUtils from './utils/WorklistDataGridUtils'; @@ -619,3 +619,4 @@ DataGrid.defaultProps = defaultProps; DataGrid.propTypes = propTypes; export default DataGrid; +export { ColumnHighlightColor }; diff --git a/packages/terra-data-grid/src/FlowsheetDataGrid.jsx b/packages/terra-data-grid/src/FlowsheetDataGrid.jsx index 52aaf10617c..3433a37f9ec 100644 --- a/packages/terra-data-grid/src/FlowsheetDataGrid.jsx +++ b/packages/terra-data-grid/src/FlowsheetDataGrid.jsx @@ -8,7 +8,7 @@ import * as KeyCode from 'keycode-js'; import VisuallyHiddenText from 'terra-visually-hidden-text'; import { sectionShape, rowShape, columnShape } from 'terra-table'; -import DataGrid from './DataGrid'; +import DataGrid, { ColumnHighlightColor } from './DataGrid'; import styles from './FlowsheetDataGrid.module.scss'; const cx = classNames.bind(styles); @@ -439,3 +439,4 @@ FlowsheetDataGrid.propTypes = propTypes; FlowsheetDataGrid.defaultProps = defaultProps; export default injectIntl(FlowsheetDataGrid); +export { ColumnHighlightColor }; diff --git a/packages/terra-data-grid/src/index.js b/packages/terra-data-grid/src/index.js index a4e4babf7a8..97e40b4660b 100644 --- a/packages/terra-data-grid/src/index.js +++ b/packages/terra-data-grid/src/index.js @@ -1,4 +1,4 @@ import WorklistDataGrid from './WorklistDataGrid'; -import FlowsheetDataGrid from './FlowsheetDataGrid'; +import FlowsheetDataGrid, { ColumnHighlightColor } from './FlowsheetDataGrid'; -export { WorklistDataGrid, FlowsheetDataGrid }; +export { WorklistDataGrid, FlowsheetDataGrid, ColumnHighlightColor }; diff --git a/packages/terra-data-grid/tests/jest/__snapshots__/DataGrid.test.jsx.snap b/packages/terra-data-grid/tests/jest/__snapshots__/DataGrid.test.jsx.snap index 148efe2595c..12ee21d2152 100644 --- a/packages/terra-data-grid/tests/jest/__snapshots__/DataGrid.test.jsx.snap +++ b/packages/terra-data-grid/tests/jest/__snapshots__/DataGrid.test.jsx.snap @@ -1183,10 +1183,12 @@ exports[`DataGrid verifies onCellSelect callback is triggered when space is pres }, ] } + firstRowId="1" id="00000000-0000-0000-0000-000000000000" isHidden={true} isTableStriped={true} key="00000000-0000-0000-0000-000000000000" + lastRowId="4" onCellSelect={[Function]} rowHeaderIndex={0} rowMinimumHeight="auto" @@ -1313,9 +1315,11 @@ exports[`DataGrid verifies onCellSelect callback is triggered when space is pres }, ] } + firstRowId="1" id="1" isTableStriped={true} key="1" + lastRowId="4" onCellSelect={[Function]} rowHeaderIndex={0} rowIndex={2} @@ -1335,10 +1339,12 @@ exports[`DataGrid verifies onCellSelect callback is triggered when space is pres { expect(browser.$('//*[@id="terra-flowsheet-data-grid-no-column-headers-table-rowheader-1"]').isFocused()).toBe(true); }); }); + + describe('flowsheet data grid with column highlight', () => { + it('renders a flowsheet with default column highlight', () => { + browser.url('/raw/tests/cerner-terra-framework-docs/data-grid/flowsheet-data-grid/column-highlight-orange'); + browser.keys(['Tab']); // Cell 0,0 gets focus + expect(browser.$('[class*="column-header-row"] th:nth-child(1)').isFocused()).toBe(true); + Terra.validates.element('flowsheet-data-grid-column-highlight', { selector: '#terra-flowsheet-data-grid-column-highlight-orange' }); + }); + + it('renders a flowsheet data grid with green column highlight', () => { + browser.url('/raw/tests/cerner-terra-framework-docs/data-grid/flowsheet-data-grid/column-highlight-green'); + browser.keys(['Tab']); // Cell 0,0 gets focus + expect(browser.$('[class*="column-header-row"] th:nth-child(1)').isFocused()).toBe(true); + Terra.validates.element('flowsheet-data-grid-column-highlight-green', { selector: '#terra-flowsheet-data-grid-column-highlight-green' }); + }); + + it('renders a flowsheet data grid with orange and green column highlight', () => { + browser.url('/raw/tests/cerner-terra-framework-docs/data-grid/flowsheet-data-grid/multi-column-highlight'); + browser.keys(['Tab']); // Cell 0,0 gets focus + expect(browser.$('[class*="column-header-row"] th:nth-child(1)').isFocused()).toBe(true); + Terra.validates.element('flowsheet-data-grid-multi-column-highlight', { selector: '#terra-flowsheet-data-grid-multi-column-highlight' }); + }); + + it('renders a flowsheet data grid sections along with orange column highlight', () => { + browser.url('/raw/tests/cerner-terra-framework-docs/data-grid/flowsheet-data-grid/flowsheet-sections-with-col-highlight'); + Terra.validates.element('flowsheet-data-grid-sections-with-orange-column-highlight', { selector: '#flowsheet-sections-with-col-highlight' }); + }); + }); }); describe('Cell selection', () => { diff --git a/packages/terra-framework-docs/CHANGELOG.md b/packages/terra-framework-docs/CHANGELOG.md index fc6791609b9..5d62b2b8e8c 100644 --- a/packages/terra-framework-docs/CHANGELOG.md +++ b/packages/terra-framework-docs/CHANGELOG.md @@ -2,6 +2,9 @@ ## Unreleased +* Added + * Added orange and green column highlighting examples for `flowsheet-data-grid` + ## 1.72.0 - (March 5, 2024) * Changed @@ -49,7 +52,6 @@ * Added * Added shouldTrapFocus to be set to 'true'. - * Changed * Updated `terra-navigation-side-menu` example to use more meaningful labels. * Updated the `terra-date-time-picker` example for field label. diff --git a/packages/terra-framework-docs/src/terra-dev-site/doc/data-grid/FlowsheetDataGrid.4/About.5.doc.mdx b/packages/terra-framework-docs/src/terra-dev-site/doc/data-grid/FlowsheetDataGrid.4/About.5.doc.mdx index 88253f31fc8..b4914085003 100644 --- a/packages/terra-framework-docs/src/terra-dev-site/doc/data-grid/FlowsheetDataGrid.4/About.5.doc.mdx +++ b/packages/terra-framework-docs/src/terra-dev-site/doc/data-grid/FlowsheetDataGrid.4/About.5.doc.mdx @@ -44,6 +44,8 @@ See [keyboard interactions](/components/cerner-terra-framework-docs/data-grid/fl |[ColumnHeadersHidden](/components/cerner-terra-framework-docs/data-grid/flowsheet-data-grid/examples/column-headers-hidden)|An example containing visually hidden column headers.| |[RowSelection](/components/cerner-terra-framework-docs/data-grid/flowsheet-data-grid/examples/row-selection)|An example demonstrating the ability to select a row of cells.| |[Flowsheet Data Grid with Sections](/components/cerner-terra-framework-docs/data-grid/flowsheet-data-grid/examples/flowsheet-with-sections)|An example demonstrating how to create a Flowsheet Data Grid with sections.| +|[ColumnHighlightOrangeGreen](/components/cerner-terra-framework-docs/data-grid/flowsheet-data-grid/examples/column-highlight-orange-green)|Examples of the Flowsheet Data Grid with orange and green column highlighting.| +|[MultiColumnHighlight](/components/cerner-terra-framework-docs/data-grid/flowsheet-data-grid/examples/multi-column-highlight)|An example of the Flowsheet Data Grid with multi-column highlighting.| ## Accessibility ### Keyboard Interactions @@ -105,13 +107,22 @@ import { FlowsheetDataGrid } from 'terra-data-grid'; ## Properties -When making changes to the columns, section, rows, or cells, the Flowsheet Data Grid component requires you to pass new objects so that React can detect the changes. +When making changes to the columns, section, rows, or cells, the Flowsheet Data Grid component requires you to pass new objects so that React can detect the changes. In addition, ensure that you memoize callback functions using methods such as the useCallback hook to prevent unnecessary rerenders of components. ### Flowsheet Data Grid Property Table +### Flowsheet Data Grid Constants +Enumeration: ColumnHighlightColor + +|Constant|Type|Description| +|---|---|---| +|**ORANGE**|string|Orange column highlight.| +|**GREEN**|string|Green column highlight.| + + ### Column A column specifies the data to render a cell in the header row of the Flowsheet Data Grid. @@ -122,6 +133,8 @@ A column specifies the data to render a cell in the header row of the Flowsheet |**action**|object|optional|none|An object containing label and onClick properties for column action button, which will be displayed in an additional row below the column header row.| |**hasError**|bool|optional|none|Boolean value indicating whether the column has an error in the data.| |**width**|number|optional|none|A number (in px) specifying the width of the column. If not provided, the Data Grid's default column width is used.| +|**columnHighlightColor**|`enum: ["green", "orange"]`|optional|none|The color to be used for highlighting a column.| +|**columnHighlightDescription**|string|optional|none|The information to be conveyed to screen readers about the highlighted column.| ### Rows Rows define the cells rendered in the row and the row's selection properties. diff --git a/packages/terra-framework-docs/src/terra-dev-site/doc/data-grid/FlowsheetDataGrid.4/Examples.6/ColumnHighlightGreen.jsx b/packages/terra-framework-docs/src/terra-dev-site/doc/data-grid/FlowsheetDataGrid.4/Examples.6/ColumnHighlightGreen.jsx new file mode 100644 index 00000000000..95bf99ab969 --- /dev/null +++ b/packages/terra-framework-docs/src/terra-dev-site/doc/data-grid/FlowsheetDataGrid.4/Examples.6/ColumnHighlightGreen.jsx @@ -0,0 +1,78 @@ +import React from 'react'; +import { FlowsheetDataGrid, ColumnHighlightColor } from 'terra-data-grid'; + +const ColumnHighlightGreen = () => { + const gridDataJSON = { + cols: [ + { id: 'Column-0', displayName: 'Vitals' }, + { id: 'Column-1', displayName: 'March 16' }, + { + id: 'Column-2', + displayName: 'March 17', + columnHighlightColor: ColumnHighlightColor.GREEN, + columnHighlightDescription: 'Selected time', + }, + { id: 'Column-3', displayName: 'March 18' }, + ], + rows: [ + { + id: '1', + cells: [ + { content: 'Heart Rate Monitored (bpm)' }, + { content: '68' }, + { content: '66' }, + { content: '67' }, + ], + }, + { + id: '2', + cells: [ + { content: 'Temperature Oral (degC)' }, + { content: '36.7' }, + { content: '36.9' }, + { content: '37' }, + ], + }, + { + id: '3', + cells: [ + { content: 'Cardiac Index (L/min/m2)' }, + { content: '2.25' }, + { content: '2.28' }, + { content: '2.8' }, + ], + }, + { + id: '4', + cells: [ + { content: 'ICP (mmHg)' }, + { content: '11' }, + { content: '11' }, + { content: '12' }, + ], + }, + { + id: '5', + cells: [ + { content: 'CPP (mmHg)' }, + { content: '63' }, + { content: '47' }, + { content: '48' }, + ], + }, + ], + }; + + const { cols, rows } = gridDataJSON; + + return ( + + ); +}; + +export default ColumnHighlightGreen; diff --git a/packages/terra-framework-docs/src/terra-dev-site/doc/data-grid/FlowsheetDataGrid.4/Examples.6/ColumnHighlightOrange.jsx b/packages/terra-framework-docs/src/terra-dev-site/doc/data-grid/FlowsheetDataGrid.4/Examples.6/ColumnHighlightOrange.jsx new file mode 100644 index 00000000000..bda133a56b1 --- /dev/null +++ b/packages/terra-framework-docs/src/terra-dev-site/doc/data-grid/FlowsheetDataGrid.4/Examples.6/ColumnHighlightOrange.jsx @@ -0,0 +1,78 @@ +import React from 'react'; +import { FlowsheetDataGrid, ColumnHighlightColor } from 'terra-data-grid'; + +const ColumnHighlightOrange = () => { + const gridDataJSON = { + cols: [ + { id: 'Column-0', displayName: 'Vitals' }, + { id: 'Column-1', displayName: 'March 16' }, + { + id: 'Column-2', + displayName: 'March 17', + columnHighlightColor: ColumnHighlightColor.ORANGE, + columnHighlightDescription: 'Most recent time', + }, + { id: 'Column-3', displayName: 'March 18' }, + ], + rows: [ + { + id: '1', + cells: [ + { content: 'Heart Rate Monitored (bpm)' }, + { content: '68' }, + { content: '66' }, + { content: '67' }, + ], + }, + { + id: '2', + cells: [ + { content: 'Temperature Oral (degC)' }, + { content: '36.7' }, + { content: '36.9' }, + { content: '37' }, + ], + }, + { + id: '3', + cells: [ + { content: 'Cardiac Index (L/min/m2)' }, + { content: '2.25' }, + { content: '2.28' }, + { content: '2.8' }, + ], + }, + { + id: '4', + cells: [ + { content: 'ICP (mmHg)' }, + { content: '11' }, + { content: '11' }, + { content: '12' }, + ], + }, + { + id: '5', + cells: [ + { content: 'CPP (mmHg)' }, + { content: '63' }, + { content: '47' }, + { content: '48' }, + ], + }, + ], + }; + + const { cols, rows } = gridDataJSON; + + return ( + + ); +}; + +export default ColumnHighlightOrange; diff --git a/packages/terra-framework-docs/src/terra-dev-site/doc/data-grid/FlowsheetDataGrid.4/Examples.6/ColumnHighlightOrangeGreen.6.doc.mdx b/packages/terra-framework-docs/src/terra-dev-site/doc/data-grid/FlowsheetDataGrid.4/Examples.6/ColumnHighlightOrangeGreen.6.doc.mdx new file mode 100644 index 00000000000..d28157a02e0 --- /dev/null +++ b/packages/terra-framework-docs/src/terra-dev-site/doc/data-grid/FlowsheetDataGrid.4/Examples.6/ColumnHighlightOrangeGreen.6.doc.mdx @@ -0,0 +1,20 @@ +import ColumnHighlightOrange from "./ColumnHighlightOrange?dev-site-example"; +import ColumnHighlightGreen from "./ColumnHighlightGreen?dev-site-example"; + +# Flowsheet Data Grid with orange column highlighting + +### Description + +This example demonstrates a basic [Flowsheet Data Grid](/components/cerner-terra-framework-docs/data-grid/flowsheet-data-grid/about) with orange column highlighting. + +Below are the attributes used for column highlighting. +- `columnHighlightColor` can be used to set highlight colour to either Orange or Green. +- `columnHighlightDescription` can be used to provide context regarding the highlighted column to screen readers. + + + +# Flowsheet Data Grid with green column highlighting + +This example demonstrates a basic [Flowsheet Data Grid](/components/cerner-terra-framework-docs/data-grid/flowsheet-data-grid/about) with green column highlighting. + + diff --git a/packages/terra-framework-docs/src/terra-dev-site/doc/data-grid/FlowsheetDataGrid.4/Examples.6/FlowsheetSectionsWithColHighlight.doc.mdx b/packages/terra-framework-docs/src/terra-dev-site/doc/data-grid/FlowsheetDataGrid.4/Examples.6/FlowsheetSectionsWithColHighlight.doc.mdx new file mode 100644 index 00000000000..b1b47508f7b --- /dev/null +++ b/packages/terra-framework-docs/src/terra-dev-site/doc/data-grid/FlowsheetDataGrid.4/Examples.6/FlowsheetSectionsWithColHighlight.doc.mdx @@ -0,0 +1,18 @@ +import FlowsheetSectionsWithColHighlight from "./FlowsheetSectionsWithColHighlight?dev-site-example"; + +# Flowsheet Data Grid Sections with column highlight + +### Description + +This example demonstrates a [Flowsheet Data Grid](/components/cerner-terra-framework-docs/data-grid/flowsheet-data-grid/about) component with information grouped by sections along with column highlighting. + +### Required Properties +* **sections**: The tabular information for the component. +* **isCollapsible**: This property determines whether a section is collapsible. Collapsible sections contain a button in the section header. +* **isCollapsed**: When users select the section header of a collapsible section, this property is toggled to control whether the section is expanded or collapsed. + +### Below are the attributes used for column highlighting. +- `columnHighlightColor` can be used to set highlight colour to either Orange or Green. +- `columnHighlightDescription` can be used to provide context regarding the highlighted column to screen readers. + + diff --git a/packages/terra-framework-docs/src/terra-dev-site/doc/data-grid/FlowsheetDataGrid.4/Examples.6/FlowsheetSectionsWithColHighlight.jsx b/packages/terra-framework-docs/src/terra-dev-site/doc/data-grid/FlowsheetDataGrid.4/Examples.6/FlowsheetSectionsWithColHighlight.jsx new file mode 100644 index 00000000000..ccfe6bf68cd --- /dev/null +++ b/packages/terra-framework-docs/src/terra-dev-site/doc/data-grid/FlowsheetDataGrid.4/Examples.6/FlowsheetSectionsWithColHighlight.jsx @@ -0,0 +1,172 @@ +import React, { useCallback, useState } from 'react'; +import { ColumnHighlightColor, FlowsheetDataGrid } from 'terra-data-grid'; + +const gridDataJSON = { + cols: [ + { + id: 'Column-0', displayName: 'Patient', isSelectable: true, + }, + { + id: 'Column-1', displayName: 'Location', isSelectable: true, + }, + { + id: 'Column-2', + displayName: 'Illness Severity', + isSelectable: true, + columnHighlightColor: ColumnHighlightColor.ORANGE, + columnHighlightDescription: 'Most recent time', + }, + { 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 FlowsheetSectionsWithColHighlight = () => { + 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((isMetaPressed) => tableSections.map(section => ({ + ...section, + rows: section.rows.map(row => ({ + ...row, + cells: row.cells.map(cell => ({ + ...cell, + isSelected: isMetaPressed ? 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(selectedCell.isMetaPressed); + + // // 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]); + + const onClearSelectedCells = () => { + setTableSections(getClearedSections()); + }; + + return ( + + ); +}; + +export default FlowsheetSectionsWithColHighlight; diff --git a/packages/terra-framework-docs/src/terra-dev-site/doc/data-grid/FlowsheetDataGrid.4/Examples.6/MultiColumnHighlight.8.doc.mdx b/packages/terra-framework-docs/src/terra-dev-site/doc/data-grid/FlowsheetDataGrid.4/Examples.6/MultiColumnHighlight.8.doc.mdx new file mode 100644 index 00000000000..7f3253351e7 --- /dev/null +++ b/packages/terra-framework-docs/src/terra-dev-site/doc/data-grid/FlowsheetDataGrid.4/Examples.6/MultiColumnHighlight.8.doc.mdx @@ -0,0 +1,14 @@ +import MultiColumnHighlight from "./MultiColumnHighlight?dev-site-example"; + +# Flowsheet Data Grid with multi-column highlighting + +### Description + +This example demonstrates a basic [Flowsheet Data Grid](/components/cerner-terra-framework-docs/data-grid/flowsheet-data-grid/about) with multi-column highlighting. + +Below are the attributes used for column highlighting. +- `columnHighlightColor` can be used to set highlight colour to either Orange or Green. +- `columnHighlightDescription` can be used to provide context regarding the highlighted column to screen readers. + + + diff --git a/packages/terra-framework-docs/src/terra-dev-site/doc/data-grid/FlowsheetDataGrid.4/Examples.6/MultiColumnHighlight.jsx b/packages/terra-framework-docs/src/terra-dev-site/doc/data-grid/FlowsheetDataGrid.4/Examples.6/MultiColumnHighlight.jsx new file mode 100644 index 00000000000..03e866be190 --- /dev/null +++ b/packages/terra-framework-docs/src/terra-dev-site/doc/data-grid/FlowsheetDataGrid.4/Examples.6/MultiColumnHighlight.jsx @@ -0,0 +1,83 @@ +import React from 'react'; +import { ColumnHighlightColor, FlowsheetDataGrid } from 'terra-data-grid'; + +const MultiColumnHighlight = () => { + const gridDataJSON = { + cols: [ + { id: 'Column-0', displayName: 'Vitals' }, + { id: 'Column-1', displayName: 'March 16' }, + { + id: 'Column-2', + displayName: 'March 17', + columnHighlightColor: ColumnHighlightColor.ORANGE, + columnHighlightDescription: 'Most recent time', + }, + { + id: 'Column-3', + displayName: 'March 18', + columnHighlightColor: ColumnHighlightColor.GREEN, + columnHighlightDescription: 'Selected time', + }, + ], + rows: [ + { + id: '1', + cells: [ + { content: 'Heart Rate Monitored (bpm)' }, + { content: '68' }, + { content: '66' }, + { content: '67' }, + ], + }, + { + id: '2', + cells: [ + { content: 'Temperature Oral (degC)' }, + { content: '36.7' }, + { content: '36.9' }, + { content: '37' }, + ], + }, + { + id: '3', + cells: [ + { content: 'Cardiac Index (L/min/m2)' }, + { content: '2.25' }, + { content: '2.28' }, + { content: '2.8' }, + ], + }, + { + id: '4', + cells: [ + { content: 'ICP (mmHg)' }, + { content: '11' }, + { content: '11' }, + { content: '12' }, + ], + }, + { + id: '5', + cells: [ + { content: 'CPP (mmHg)' }, + { content: '63' }, + { content: '47' }, + { content: '48' }, + ], + }, + ], + }; + + const { cols, rows } = gridDataJSON; + + return ( + + ); +}; + +export default MultiColumnHighlight; diff --git a/packages/terra-framework-docs/src/terra-dev-site/test/data-grid/flowsheet-data-grid/ColumnHighlightGreen.test.jsx b/packages/terra-framework-docs/src/terra-dev-site/test/data-grid/flowsheet-data-grid/ColumnHighlightGreen.test.jsx new file mode 100644 index 00000000000..08298f80a75 --- /dev/null +++ b/packages/terra-framework-docs/src/terra-dev-site/test/data-grid/flowsheet-data-grid/ColumnHighlightGreen.test.jsx @@ -0,0 +1,78 @@ +import React from 'react'; +import { ColumnHighlightColor, FlowsheetDataGrid } from 'terra-data-grid'; + +const ColumnHighlightGreen = () => { + const gridDataJSON = { + cols: [ + { id: 'Column-0', displayName: 'Vitals' }, + { id: 'Column-1', displayName: 'March 16' }, + { + id: 'Column-2', + displayName: 'March 17', + columnHighlightColor: ColumnHighlightColor.GREEN, + columnHighlightDescription: 'Selected time', + }, + { id: 'Column-3', displayName: 'March 18' }, + ], + rows: [ + { + id: '1', + cells: [ + { content: 'Heart Rate Monitored (bpm)' }, + { content: '68' }, + { content: '66' }, + { content: '67' }, + ], + }, + { + id: '2', + cells: [ + { content: 'Temperature Oral (degC)' }, + { content: '36.7' }, + { content: '36.9' }, + { content: '37' }, + ], + }, + { + id: '3', + cells: [ + { content: 'Cardiac Index (L/min/m2)' }, + { content: '2.25' }, + { content: '2.28' }, + { content: '2.8' }, + ], + }, + { + id: '4', + cells: [ + { content: 'ICP (mmHg)' }, + { content: '11' }, + { content: '11' }, + { content: '12' }, + ], + }, + { + id: '5', + cells: [ + { content: 'CPP (mmHg)' }, + { content: '63' }, + { content: '47' }, + { content: '48' }, + ], + }, + ], + }; + + const { cols, rows } = gridDataJSON; + + return ( + + ); +}; + +export default ColumnHighlightGreen; diff --git a/packages/terra-framework-docs/src/terra-dev-site/test/data-grid/flowsheet-data-grid/ColumnHighlightOrange.test.jsx b/packages/terra-framework-docs/src/terra-dev-site/test/data-grid/flowsheet-data-grid/ColumnHighlightOrange.test.jsx new file mode 100644 index 00000000000..6d97fa357f2 --- /dev/null +++ b/packages/terra-framework-docs/src/terra-dev-site/test/data-grid/flowsheet-data-grid/ColumnHighlightOrange.test.jsx @@ -0,0 +1,78 @@ +import React from 'react'; +import { ColumnHighlightColor, FlowsheetDataGrid } from 'terra-data-grid'; + +const ColumnHighlightOrange = () => { + const gridDataJSON = { + cols: [ + { id: 'Column-0', displayName: 'Vitals' }, + { id: 'Column-1', displayName: 'March 16' }, + { + id: 'Column-2', + displayName: 'March 17', + columnHighlightColor: ColumnHighlightColor.ORANGE, + columnHighlightDescription: 'Most recent time', + }, + { id: 'Column-3', displayName: 'March 18' }, + ], + rows: [ + { + id: '1', + cells: [ + { content: 'Heart Rate Monitored (bpm)' }, + { content: '68' }, + { content: '66' }, + { content: '67' }, + ], + }, + { + id: '2', + cells: [ + { content: 'Temperature Oral (degC)' }, + { content: '36.7' }, + { content: '36.9' }, + { content: '37' }, + ], + }, + { + id: '3', + cells: [ + { content: 'Cardiac Index (L/min/m2)' }, + { content: '2.25' }, + { content: '2.28' }, + { content: '2.8' }, + ], + }, + { + id: '4', + cells: [ + { content: 'ICP (mmHg)' }, + { content: '11' }, + { content: '11' }, + { content: '12' }, + ], + }, + { + id: '5', + cells: [ + { content: 'CPP (mmHg)' }, + { content: '63' }, + { content: '47' }, + { content: '48' }, + ], + }, + ], + }; + + const { cols, rows } = gridDataJSON; + + return ( + + ); +}; + +export default ColumnHighlightOrange; diff --git a/packages/terra-framework-docs/src/terra-dev-site/test/data-grid/flowsheet-data-grid/FlowsheetSectionsWithColHighlight.test.jsx b/packages/terra-framework-docs/src/terra-dev-site/test/data-grid/flowsheet-data-grid/FlowsheetSectionsWithColHighlight.test.jsx new file mode 100644 index 00000000000..ccfe6bf68cd --- /dev/null +++ b/packages/terra-framework-docs/src/terra-dev-site/test/data-grid/flowsheet-data-grid/FlowsheetSectionsWithColHighlight.test.jsx @@ -0,0 +1,172 @@ +import React, { useCallback, useState } from 'react'; +import { ColumnHighlightColor, FlowsheetDataGrid } from 'terra-data-grid'; + +const gridDataJSON = { + cols: [ + { + id: 'Column-0', displayName: 'Patient', isSelectable: true, + }, + { + id: 'Column-1', displayName: 'Location', isSelectable: true, + }, + { + id: 'Column-2', + displayName: 'Illness Severity', + isSelectable: true, + columnHighlightColor: ColumnHighlightColor.ORANGE, + columnHighlightDescription: 'Most recent time', + }, + { 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 FlowsheetSectionsWithColHighlight = () => { + 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((isMetaPressed) => tableSections.map(section => ({ + ...section, + rows: section.rows.map(row => ({ + ...row, + cells: row.cells.map(cell => ({ + ...cell, + isSelected: isMetaPressed ? 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(selectedCell.isMetaPressed); + + // // 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]); + + const onClearSelectedCells = () => { + setTableSections(getClearedSections()); + }; + + return ( + + ); +}; + +export default FlowsheetSectionsWithColHighlight; diff --git a/packages/terra-framework-docs/src/terra-dev-site/test/data-grid/flowsheet-data-grid/MultiColumnHighlight.test.jsx b/packages/terra-framework-docs/src/terra-dev-site/test/data-grid/flowsheet-data-grid/MultiColumnHighlight.test.jsx new file mode 100644 index 00000000000..03e866be190 --- /dev/null +++ b/packages/terra-framework-docs/src/terra-dev-site/test/data-grid/flowsheet-data-grid/MultiColumnHighlight.test.jsx @@ -0,0 +1,83 @@ +import React from 'react'; +import { ColumnHighlightColor, FlowsheetDataGrid } from 'terra-data-grid'; + +const MultiColumnHighlight = () => { + const gridDataJSON = { + cols: [ + { id: 'Column-0', displayName: 'Vitals' }, + { id: 'Column-1', displayName: 'March 16' }, + { + id: 'Column-2', + displayName: 'March 17', + columnHighlightColor: ColumnHighlightColor.ORANGE, + columnHighlightDescription: 'Most recent time', + }, + { + id: 'Column-3', + displayName: 'March 18', + columnHighlightColor: ColumnHighlightColor.GREEN, + columnHighlightDescription: 'Selected time', + }, + ], + rows: [ + { + id: '1', + cells: [ + { content: 'Heart Rate Monitored (bpm)' }, + { content: '68' }, + { content: '66' }, + { content: '67' }, + ], + }, + { + id: '2', + cells: [ + { content: 'Temperature Oral (degC)' }, + { content: '36.7' }, + { content: '36.9' }, + { content: '37' }, + ], + }, + { + id: '3', + cells: [ + { content: 'Cardiac Index (L/min/m2)' }, + { content: '2.25' }, + { content: '2.28' }, + { content: '2.8' }, + ], + }, + { + id: '4', + cells: [ + { content: 'ICP (mmHg)' }, + { content: '11' }, + { content: '11' }, + { content: '12' }, + ], + }, + { + id: '5', + cells: [ + { content: 'CPP (mmHg)' }, + { content: '63' }, + { content: '47' }, + { content: '48' }, + ], + }, + ], + }; + + const { cols, rows } = gridDataJSON; + + return ( + + ); +}; + +export default MultiColumnHighlight; diff --git a/packages/terra-table/CHANGELOG.md b/packages/terra-table/CHANGELOG.md index 63d0e9cc840..79e2f88f51d 100644 --- a/packages/terra-table/CHANGELOG.md +++ b/packages/terra-table/CHANGELOG.md @@ -34,6 +34,10 @@ * Changed * Changed the default rowMinimumHeight value to auto to fit the row contents. + * Updated lowlight light zebra strap color to darker grey to improve colour contrast. + +* Added + * Added support for orange and green column highlighting. ## 5.5.0 - (February 1, 2024) diff --git a/packages/terra-table/src/Table.jsx b/packages/terra-table/src/Table.jsx index 7987515f5ce..0d59499b11c 100644 --- a/packages/terra-table/src/Table.jsx +++ b/packages/terra-table/src/Table.jsx @@ -21,6 +21,7 @@ import styles from './Table.module.scss'; import sectionShape from './proptypes/sectionShape'; import getFocusableElements from './utils/focusManagement'; import hasColumnActions from './utils/actionsUtils'; +import tableUtils from './utils/tableUtils'; const cx = classNames.bind(styles); @@ -580,6 +581,19 @@ function Table(props) { marginRight: hasResizableCol ? `${TableConstants.TABLE_MARGIN_RIGHT}px` : '0', }; + // Set first and last row Ids + let firstRowId; + let lastRowId; + + if (rows && rows.length) { + firstRowId = rows[0].id; + lastRowId = rows[rows.length - 1].id; + } else if (sections) { + const rowData = tableUtils.getFirstAndLastVisibleRowData(sections); + firstRowId = rowData.firstRowId; + lastRowId = rowData.lastRowId; + } + // ------------------------------------- return ( @@ -648,6 +662,8 @@ function Table(props) { onSectionSelect={onSectionSelect} rowMinimumHeight={rowMinimumHeight} boundingRef={boundingRef} + firstRowId={firstRowId} + lastRowId={lastRowId} /> ))} diff --git a/packages/terra-table/src/clinical-lowlight-theme/Cell.module.scss b/packages/terra-table/src/clinical-lowlight-theme/Cell.module.scss index 87484e91adf..ad4c4bb7a7c 100644 --- a/packages/terra-table/src/clinical-lowlight-theme/Cell.module.scss +++ b/packages/terra-table/src/clinical-lowlight-theme/Cell.module.scss @@ -5,11 +5,20 @@ --terra-table-cell-border-right: 1px solid #181b1d; --terra-table-cell-focus-outline: 3px dashed #fff; --terra-table-cell-selected-background-color: #1e3a49; + --terra-table-cell-highlighted-not-hover-background-color: rgba(255, 177, 102, 0.1); --terra-table-last-pinned-cell-border-right: 2px solid #181b1d; --terra-table-mask-background: repeating-linear-gradient(-45deg, #434647, #434647 3px, transparent 3px, transparent 6px); --terra-table-highlight-mask-background: repeating-linear-gradient(-45deg, #434647, #434647 3px, transparent 3px, transparent 6px); --terra-table-odd-row-hover-background-color: #25282a; --terra-table-even-row-hover-background-color: #1d2225; --terra-table-pinned-columns-cell-border: 1px solid #181b1d; + --terra-table-cell-highlighted-orange-background-color: rgba(199, 115, 39, 0.2); + --terra-table-cell-highlighted-orange-box-shadow: inset 1px 0 0 0 #c77327, inset -1px 0 0 0 #c77327; + --terra-table-cell-highlighted-orange-first-box-shadow: inset 1px 0 0 0 #c77327, inset -1px 0 0 0 #c77327, inset 0 1px 0 0 #c77327; + --terra-table-cell-highlighted-orange-last-box-shadow: inset 1px 0 0 0 #c77327, inset -1px 0 0 0 #c77327, inset 0 -1px 0 0 #c77327; + --terra-table-cell-highlighted-green-background-color: rgba(17, 109, 14, 0.2); + --terra-table-cell-highlighted-green-box-shadow: inset 1px 0 0 0 #116d0e, inset -1px 0 0 0 #116d0e; + --terra-table-cell-highlighted-green-first-box-shadow: inset 1px 0 0 0 #116d0e, inset -1px 0 0 0 #116d0e, inset 0 1px 0 0 #116d0e; + --terra-table-cell-highlighted-green-last-box-shadow: inset 1px 0 0 0 #116d0e, inset -1px 0 0 0 #116d0e, inset 0 -1px 0 0 #116d0e; } } diff --git a/packages/terra-table/src/clinical-lowlight-theme/ColumnHeaderCell.module.scss b/packages/terra-table/src/clinical-lowlight-theme/ColumnHeaderCell.module.scss index 82b539acdf7..d5ae324d11b 100644 --- a/packages/terra-table/src/clinical-lowlight-theme/ColumnHeaderCell.module.scss +++ b/packages/terra-table/src/clinical-lowlight-theme/ColumnHeaderCell.module.scss @@ -8,6 +8,8 @@ --terra-table-column-header-border-right: 1px solid #181b1d; --terra-table-column-header-border-top: 1px solid #181b1d; --terra-table-column-header-focus-outline: 3px dashed #fff; + --terra-table-column-header-highlighted-orange-square-fill: #c77327; + --terra-table-column-header-highlighted-green-circle-fill: #116d0e; // Pinned Columns Divider --terra-table-last-pinned-header-border-right: 2px solid #181b1d; diff --git a/packages/terra-table/src/clinical-lowlight-theme/Row.module.scss b/packages/terra-table/src/clinical-lowlight-theme/Row.module.scss index 0af749ac483..793e09daf08 100644 --- a/packages/terra-table/src/clinical-lowlight-theme/Row.module.scss +++ b/packages/terra-table/src/clinical-lowlight-theme/Row.module.scss @@ -1,7 +1,7 @@ :local { .clinical-lowlight-theme { --terra-table-row-striped-background-color: #2d3539; - --terra-table-row-background-color: #6e6f71; + --terra-table-row-background-color: #232a2f; --terra-table-row-selected-background-color: #1e3a49; --terra-table-row-selected-box-shadow: inset 0 1px 0 0 #004c76, inset 0 -1px 0 0 #004c76; --terra-table-row-hover-background-color: #232a2d; diff --git a/packages/terra-table/src/index.js b/packages/terra-table/src/index.js index 58dc1990dce..41ecedf8237 100644 --- a/packages/terra-table/src/index.js +++ b/packages/terra-table/src/index.js @@ -1,7 +1,7 @@ import Table, { TableConstants, RowSelectionModes } from './Table'; import GridContext, { GridConstants } from './utils/GridContext'; import cellShape from './proptypes/cellShape'; -import columnShape from './proptypes/columnShape'; +import columnShape, { ColumnHighlightColor } from './proptypes/columnShape'; import rowShape from './proptypes/rowShape'; import sectionShape from './proptypes/sectionShape'; import { validateRowHeaderIndex, validateAction } from './proptypes/validators'; @@ -9,5 +9,5 @@ import hasColumnActions from './utils/actionsUtils'; export default Table; export { - GridContext, GridConstants, TableConstants, RowSelectionModes, cellShape, columnShape, rowShape, sectionShape, validateRowHeaderIndex, validateAction, hasColumnActions, + GridContext, GridConstants, TableConstants, RowSelectionModes, cellShape, columnShape, rowShape, sectionShape, validateRowHeaderIndex, validateAction, hasColumnActions, ColumnHighlightColor, }; diff --git a/packages/terra-table/src/proptypes/columnShape.js b/packages/terra-table/src/proptypes/columnShape.js index 2b25d971666..e2285e18a77 100644 --- a/packages/terra-table/src/proptypes/columnShape.js +++ b/packages/terra-table/src/proptypes/columnShape.js @@ -6,6 +6,11 @@ const SortIndicators = { DESCENDING: 'descending', }; +const ColumnHighlightColor = { + ORANGE: 'orange', + GREEN: 'green', +}; + const columnShape = PropTypes.shape({ /** * Required string representing a unique identifier for the column. @@ -47,7 +52,17 @@ const columnShape = PropTypes.shape({ * One of `ascending`, `descending`. */ sortIndicator: PropTypes.oneOf(Object.values(SortIndicators)), + + /** + * The color to be used for highlighting a column. + */ + columnHighlightColor: PropTypes.oneOf(Object.values(ColumnHighlightColor)), + + /** + * The information to be conveyed to screen readers about the highlighted column. + */ + columnHighlightDescription: PropTypes.string, }); export default columnShape; -export { SortIndicators }; +export { SortIndicators, ColumnHighlightColor }; diff --git a/packages/terra-table/src/subcomponents/Cell.jsx b/packages/terra-table/src/subcomponents/Cell.jsx index 4525cf4ff42..967c2482aa6 100644 --- a/packages/terra-table/src/subcomponents/Cell.jsx +++ b/packages/terra-table/src/subcomponents/Cell.jsx @@ -16,6 +16,7 @@ import VisuallyHiddenText from 'terra-visually-hidden-text'; import ColumnContext from '../utils/ColumnContext'; import GridContext, { GridConstants } from '../utils/GridContext'; import getFocusableElements from '../utils/focusManagement'; +import { ColumnHighlightColor } from '../proptypes/columnShape'; import styles from './Cell.module.scss'; const cx = classNames.bind(styles); @@ -118,6 +119,23 @@ const propTypes = { * The intl object containing translations. This is retrieved from the context automatically by injectIntl. */ intl: PropTypes.shape({ formatMessage: PropTypes.func }).isRequired, + /** + * @private + * Id of the first row in table + */ + firstRowId: PropTypes.string, + + /** + * @private + * Id of the last row in table + */ + lastRowId: PropTypes.string, + + /** + * @private + * The color to be used for highlighting a column. + */ + columnHighlightColor: PropTypes.oneOf(Object.values(ColumnHighlightColor)), }; const defaultProps = { @@ -148,6 +166,9 @@ function Cell(props) { rowMinimumHeight, sectionId, tableId, + firstRowId, + lastRowId, + columnHighlightColor, } = props; const cellRef = useRef(); @@ -327,6 +348,15 @@ function Cell(props) { const rowHeaderId = !isRowHeader && rowHeaderIndex !== -1 ? `${tableId}-rowheader-${rowId} ` : ''; const sectionHeaderId = sectionId ? `${tableId}-${sectionId} ` : ''; + let columnHighlight = {}; + if (columnHighlightColor) { + columnHighlight = { + [`column-highlight-${columnHighlightColor.toLowerCase()}`]: true, + [`first-highlight-${columnHighlightColor.toLowerCase()}`]: rowId === firstRowId, + [`last-highlight-${columnHighlightColor.toLowerCase()}`]: rowId === lastRowId, + }; + } + const className = cx('cell', { masked: isMasked, pinned: columnIndex < columnContext.pinnedColumnOffsets.length, @@ -335,6 +365,7 @@ function Cell(props) { selected: isSelected && !isMasked, highlighted: isHighlighted, blank: !children, + ...columnHighlight, }, theme.className); return ( diff --git a/packages/terra-table/src/subcomponents/Cell.module.scss b/packages/terra-table/src/subcomponents/Cell.module.scss index 5abb1a8b588..c5164514665 100644 --- a/packages/terra-table/src/subcomponents/Cell.module.scss +++ b/packages/terra-table/src/subcomponents/Cell.module.scss @@ -36,6 +36,36 @@ background: var(--terra-table-highlight-mask-background, repeating-linear-gradient(-45deg, #cfd2d3, #cfd2d3 3px, #cbe7fa 3px, #cbe7fa 6px)); } + .column-highlight-orange { + background-color: var(--terra-table-cell-highlighted-orange-background-color, rgba(250, 203, 170, 0.38)); + box-shadow: var(--terra-table-cell-highlighted-box-shadow, inset 1px 0 0 0 #c97318, inset -1px 0 0 0 #c97318); + } + + .column-highlight-green { + background-color: var(--terra-table-cell-highlighted-green-background-color, rgba(87, 146, 47, 0.15)); + box-shadow: var(--terra-table-cell-highlighted-green-box-shadow, inset 1px 0 0 0 #57922f, inset -1px 0 0 0 #57922f); + } + + .first-highlight-orange { + background-color: var(--terra-table-cell-highlighted-orange-background-color, rgba(250, 203, 170, 0.38)); + box-shadow: var(--terra-table-cell-highlighted-orange-first-box-shadow, inset 1px 0 0 0 #c97318, inset -1px 0 0 0 #c97318, inset 0 1px 0 0 #c97318); + } + + .first-highlight-green { + background-color: var(--terra-table-cell-highlighted-green-background-color, rgba(87, 146, 47, 0.15)); + box-shadow: var(--terra-table-cell-highlighted-green-first-box-shadow, inset 1px 0 0 0 #57922f, inset -1px 0 0 0 #57922f, inset 0 1px 0 0 #57922f); + } + + .last-highlight-orange { + background-color: var(--terra-table-cell-highlighted-orange-background-color, rgba(250, 203, 170, 0.38)); + box-shadow: var(--terra-table-cell-highlighted-orange-last-box-shadow, inset 1px 0 0 0 #c97318, inset -1px 0 0 0 #c97318, inset 0 -1px 0 0 #c97318); + } + + .last-highlight-green { + background-color: var(--terra-table-cell-highlighted-green-background-color, rgba(87, 146, 47, 0.15)); + box-shadow: var(--terra-table-cell-highlighted-green-last-box-shadow, inset 1px 0 0 0 #57922f, inset -1px 0 0 0 #57922f, inset 0 -1px 0 0 #57922f); + } + .cell-content { overflow: hidden; overflow-wrap: break-word; diff --git a/packages/terra-table/src/subcomponents/ColumnHeader.jsx b/packages/terra-table/src/subcomponents/ColumnHeader.jsx index 39c0f4b8786..a2b74a98cae 100644 --- a/packages/terra-table/src/subcomponents/ColumnHeader.jsx +++ b/packages/terra-table/src/subcomponents/ColumnHeader.jsx @@ -169,6 +169,8 @@ const ColumnHeader = (props) => { onColumnSelect={onColumnSelect} onResizeMouseDown={onResizeMouseDown} onResizeHandleChange={onResizeHandleChange} + columnHighlightColor={column.columnHighlightColor} + columnHighlightDescription={column.columnHighlightDescription} /> ))} diff --git a/packages/terra-table/src/subcomponents/ColumnHeaderCell.jsx b/packages/terra-table/src/subcomponents/ColumnHeaderCell.jsx index 7f6f022848b..a0ec2da7562 100644 --- a/packages/terra-table/src/subcomponents/ColumnHeaderCell.jsx +++ b/packages/terra-table/src/subcomponents/ColumnHeaderCell.jsx @@ -16,7 +16,7 @@ import { IconUp, IconDown, IconError } from 'terra-icon'; import { validateAction } from '../proptypes/validators'; import ColumnResizeHandle from './ColumnResizeHandle'; import GridContext, { GridConstants } from '../utils/GridContext'; -import { SortIndicators } from '../proptypes/columnShape'; +import { ColumnHighlightColor, SortIndicators } from '../proptypes/columnShape'; import ColumnContext from '../utils/ColumnContext'; import styles from './ColumnHeaderCell.module.scss'; @@ -144,7 +144,7 @@ const propTypes = { /** * String that specifies the column height. Any valid CSS height value accepted. - */ + */ headerHeight: PropTypes.string.isRequired, /** @@ -174,6 +174,18 @@ const propTypes = { * Object containing intl APIs */ intl: PropTypes.shape({ formatMessage: PropTypes.func }), + + /** + * @private + * The information to be conveyed to screen readers about the highlighted column. + */ + columnHighlightDescription: PropTypes.string, + + /** + * @private + * The color to be used for highlighting a column. + */ + columnHighlightColor: PropTypes.oneOf(Object.values(ColumnHighlightColor)), }; const defaultProps = { @@ -216,6 +228,8 @@ const ColumnHeaderCell = (props) => { onResizeMouseDown, onResizeHandleChange, ownsResizeHandle, + columnHighlightDescription, + columnHighlightColor, } = props; const columnContext = useContext(ColumnContext); @@ -314,6 +328,14 @@ const ColumnHeaderCell = (props) => { sortDescription = intl.formatMessage({ id: 'Terra.table.sort-descending' }); } + // Add column highlight indicator based on color + let columnHighlightIcon; + if (columnHighlightColor === ColumnHighlightColor.GREEN) { + columnHighlightIcon = ; + } else if (columnHighlightColor === ColumnHighlightColor.ORANGE) { + columnHighlightIcon = ; + } + // Retrieve current theme from context const theme = useContext(ThemeContext); @@ -334,6 +356,7 @@ const ColumnHeaderCell = (props) => { let headerDescription = displayName; headerDescription += errorIcon ? `, ${intl.formatMessage({ id: 'Terra.table.columnError' })}` : ''; headerDescription += sortDescription ? `, ${sortDescription}` : ''; + headerDescription += columnHighlightDescription ? `, ${columnHighlightDescription}` : ''; const isPinnedColumn = columnIndex < columnContext.pinnedColumnOffsets.length; const CellTag = !isActionCell ? 'th' : 'td'; @@ -373,6 +396,7 @@ const ColumnHeaderCell = (props) => { {displayName} {sortIndicatorIcon} + {columnHighlightIcon} ); } @@ -394,32 +418,32 @@ const ColumnHeaderCell = (props) => { tabIndex={isGridContext && !hasButtonElement ? -1 : undefined} role={!isActionCell ? 'columnheader' : undefined} scope={!isActionCell ? 'col' : undefined} - // action Cell has to own a corresponding resize handle to avoid a double announcement on handle focus + // action Cell has to own a corresponding resize handle to avoid a double announcement on handle focus aria-owns={ownsResizeHandle ? resizeHandleId : undefined} title={!isActionCell ? displayName : action?.label} onMouseDown={isSelectable && onColumnSelect ? handleMouseDown : undefined} onKeyDown={(isSelectable || isResizable) ? handleKeyDown : undefined} - // eslint-disable-next-line react/forbid-component-props + // eslint-disable-next-line react/forbid-component-props style={{ width: `${width}px`, height: isActionCell ? 'auto' : headerHeight, left: cellLeftEdge }} > {cellContent} { isResizable && !isActionCell && ( - + )} ); diff --git a/packages/terra-table/src/subcomponents/ColumnHeaderCell.module.scss b/packages/terra-table/src/subcomponents/ColumnHeaderCell.module.scss index b467645abad..fdfa5c951e0 100644 --- a/packages/terra-table/src/subcomponents/ColumnHeaderCell.module.scss +++ b/packages/terra-table/src/subcomponents/ColumnHeaderCell.module.scss @@ -101,4 +101,22 @@ background-color: var(--terra-table-header-hover-background-color, #daecf6); cursor: pointer; } + + // Style column header highlight icon + .highlight-icon-svg { + height: 20px; + width: 20px; + } + + // Style column header highlight circle icon + .highlight-icon-circle { + fill: var(--terra-table-column-header-highlighted-green-circle-fill, #57922f); + } + + // Style column header highlight square icon + .highlight-icon-square { + fill: var(--terra-table-column-header-highlighted-orange-square-fill, #c97318); + height: 6px; + width: 6px; + } } diff --git a/packages/terra-table/src/subcomponents/Row.jsx b/packages/terra-table/src/subcomponents/Row.jsx index 150684a4c74..1461e46f7ff 100644 --- a/packages/terra-table/src/subcomponents/Row.jsx +++ b/packages/terra-table/src/subcomponents/Row.jsx @@ -88,6 +88,18 @@ const propTypes = { * 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 = { @@ -111,6 +123,8 @@ function Row(props) { rowHeaderIndex, onCellSelect, rowMinimumHeight, + firstRowId, + lastRowId, } = props; const theme = useContext(ThemeContext); @@ -178,6 +192,9 @@ function Row(props) { height={height} rowMinimumHeight={rowMinimumHeight} rowHeaderIndex={rowHeaderIndex} + firstRowId={firstRowId} + lastRowId={lastRowId} + columnHighlightColor={displayedColumns[cellColumnIndex].columnHighlightColor} > {cellData.content} diff --git a/packages/terra-table/src/subcomponents/Section.jsx b/packages/terra-table/src/subcomponents/Section.jsx index b47ab277fb7..27d31627158 100644 --- a/packages/terra-table/src/subcomponents/Section.jsx +++ b/packages/terra-table/src/subcomponents/Section.jsx @@ -106,6 +106,17 @@ const propTypes = { * Bounding container for the table, will use window if no value provided. */ boundingRef: PropTypes.func, + /** + * @private + * Id of the first row in table + */ + firstRowId: PropTypes.string, + + /** + * @private + * Id of the last row in table + */ + lastRowId: PropTypes.string, }; const defaultProps = { @@ -134,6 +145,8 @@ function Section(props) { onSectionSelect, rowMinimumHeight, boundingRef, + firstRowId, + lastRowId, } = props; const theme = useContext(ThemeContext); @@ -203,6 +216,8 @@ function Section(props) { isSelected={row.isSelected} isTableStriped={isTableStriped} rowMinimumHeight={rowMinimumHeight} + firstRowId={firstRowId} + lastRowId={lastRowId} /> ))} diff --git a/packages/terra-table/src/utils/tableUtils.js b/packages/terra-table/src/utils/tableUtils.js new file mode 100644 index 00000000000..9104818e45d --- /dev/null +++ b/packages/terra-table/src/utils/tableUtils.js @@ -0,0 +1,60 @@ +/** + * Returns an object with the section ids and the row ids of the first row and last row in non-collapsed and non-empty sections. + * @param {Object} Object conforming to the state.columnHighlightRowData shape. + */ +const getFirstAndLastVisibleRowData = (sections) => { + const rowData = { + firstRowId: null, + lastRowId: null, + }; + + if (sections.length < 1) { + /** + * If the sections prop is empty, there is no work to do here. + */ + return rowData; + } + + const findNotEmptyOrCollapsed = section => section.rows.length > 0 && !section.isCollapsed; + const visibleSections = sections.filter(findNotEmptyOrCollapsed); + + if (visibleSections.length < 1) { + /** + * If the filtered list is empty after removing sections that contain no rows, + * plus removing sections that are collapsed, there is no more work to do here. + */ + return rowData; + } + + for (let i = 0; i < visibleSections.length; i += 1) { + const { rows } = visibleSections[i]; + + if (rows && rows.length) { + rowData.firstRowId = rows[0].id; + break; + } + } + + if (!rowData.firstRowId) { + /** + * If no first row is found after filtering out rows marked as decorative, + * no last row will be found either. There is no more work to do here. + */ + return rowData; + } + + for (let i = visibleSections.length - 1; i >= 0; i -= 1) { + const { rows } = visibleSections[i]; + + if (rows && rows.length) { + rowData.lastRowId = rows[rows.length - 1].id; + break; + } + } + + return rowData; +}; + +const tableUtils = { getFirstAndLastVisibleRowData }; + +export default tableUtils; diff --git a/packages/terra-table/tests/jest/__snapshots__/Table.test.jsx.snap b/packages/terra-table/tests/jest/__snapshots__/Table.test.jsx.snap index edf402e82fa..4ab012cb13b 100644 --- a/packages/terra-table/tests/jest/__snapshots__/Table.test.jsx.snap +++ b/packages/terra-table/tests/jest/__snapshots__/Table.test.jsx.snap @@ -814,9 +814,11 @@ exports[`Table verifies row selection column header selection 1`] = ` }, ] } + firstRowId="1" id="00000000-0000-0000-0000-000000000000" isHidden={true} key="00000000-0000-0000-0000-000000000000" + lastRowId="4" onCellSelect={[Function]} rowHeaderIndex={0} rowMinimumHeight="auto" @@ -935,8 +937,10 @@ exports[`Table verifies row selection column header selection 1`] = ` }, ] } + firstRowId="1" id="1" key="1" + lastRowId="4" onCellSelect={[Function]} rowHeaderIndex={0} rowIndex={2} @@ -1089,11 +1093,13 @@ exports[`Table verifies row selection column header selection 1`] = `