diff --git a/package-lock.json b/package-lock.json index 23596da52a0..e98e52e6599 100644 --- a/package-lock.json +++ b/package-lock.json @@ -57,6 +57,7 @@ "terra-application": "^1.48.0", "terra-disclosure-manager": "^4.9.0", "terra-enzyme-intl": "^3.4.0", + "terra-icon": "^3.58.0", "webpack": "^5.28.0", "webpack-cli": "^4.6.0", "webpack-dev-server": "^4.11.1", @@ -43254,6 +43255,7 @@ "classnames": "^2.2.5", "keycode-js": "^3.1.0", "prop-types": "^15.5.8", + "terra-icon": "^3.58.0", "terra-theme-context": "^1.0.0" }, "peerDependencies": { @@ -43516,6 +43518,7 @@ "terra-form-select": "^6.8.0", "terra-grid": "^6.0.0", "terra-hookshot": "^5.41.0", + "terra-icon": "^3.58.0", "terra-infinite-list": "^3.42.0", "terra-layout": "^4.38.0", "terra-menu": "^6.80.0", diff --git a/package.json b/package.json index 2d3c3e0910e..8947629b919 100644 --- a/package.json +++ b/package.json @@ -131,6 +131,7 @@ "terra-application": "^1.48.0", "terra-disclosure-manager": "^4.9.0", "terra-enzyme-intl": "^3.4.0", + "terra-icon": "^3.58.0", "webpack": "^5.28.0", "webpack-cli": "^4.6.0", "webpack-dev-server": "^4.11.1", diff --git a/packages/terra-compact-interactive-list/package.json b/packages/terra-compact-interactive-list/package.json index 91208b9e8b2..244088fc280 100644 --- a/packages/terra-compact-interactive-list/package.json +++ b/packages/terra-compact-interactive-list/package.json @@ -1,6 +1,6 @@ { "name": "terra-compact-interactive-list", - "main": "lib/CompactInteractiveList.js", + "main": "lib/index.js", "version": "0.1.0", "description": "The Terra Compact Interactive List component provides users a way to render a collection of interactive data in a list format into a single tab stop.", "repository": { @@ -32,6 +32,7 @@ "classnames": "^2.2.5", "keycode-js": "^3.1.0", "prop-types": "^15.5.8", + "terra-icon": "^3.58.0", "terra-theme-context": "^1.0.0" }, "scripts": { diff --git a/packages/terra-compact-interactive-list/src/CompactInteractiveList.jsx b/packages/terra-compact-interactive-list/src/CompactInteractiveList.jsx index b290afc88e4..97dab5dda9e 100644 --- a/packages/terra-compact-interactive-list/src/CompactInteractiveList.jsx +++ b/packages/terra-compact-interactive-list/src/CompactInteractiveList.jsx @@ -4,6 +4,17 @@ import { injectIntl } from 'react-intl'; import classNames from 'classnames/bind'; import ThemeContext from 'terra-theme-context'; import styles from './CompactInteractiveList.module.scss'; +import rowShape from './proptypes/rowShape'; +import Row from './subcomponents/Row'; +import columnShape from './proptypes/columnShape'; +import { widthUnitTypes, DefaultListValues, WARNINGS } from './utils/constants'; +import { + getRowMaximumWidth, + getRowMinimumWidth, + checkIfRowHasResponsiveColumns, + getValueUnitTypePair, + converseColumnTypes, +} from './utils/utils'; const cx = classNames.bind(styles); @@ -23,23 +34,153 @@ const propTypes = { * String that labels the table for accessibility. If ariaLabelledBy is specified, ariaLabel will not be used. */ ariaLabel: PropTypes.string, + + /** + * Data for columns. + */ + columns: PropTypes.arrayOf(columnShape), + + /** + * Data for rows (list items) content. + */ + rows: PropTypes.arrayOf(rowShape), + + /** + * Row height should be a valid css string with height in px, em, or rem units. + */ + rowHeight: PropTypes.string, + + /** + * A number of visual columns. Defaults to 1. + */ + numberOfColumns: PropTypes.number, + + /** + * By default the items go from top to bottom, then break to the next column. + * If flowHorizontally prop is set to true, items will flow left to right, then break to the next row. + */ + flowHorizontally: PropTypes.bool, + + /** + * A string for container's width. Any valid css string. Defaults to '100%'. + * In case when all the columns has their widths explicitly set to a number, the width of the list will be determined by the width of it's columns unless the flexGrow prop would allow them grow. + */ + width: PropTypes.string, + + /** + * Columns minimum width should be a valid css string in value in px, em, or rem units.. + */ + columnMinimumWidth: PropTypes.string, + + /** + * Columns maximum width should be a valid css string in value in px, em, or rem units.. + */ + columnMaximumWidth: PropTypes.string, }; -// const defaultColumnMinimumWidth = 60; -// const defaultColumnMaximumWidth = 300; +const defaultProps = { + rows: [], + numberOfColumns: 1, + width: '100%', +}; const CompactInteractiveList = (props) => { const { id, ariaLabelledBy, ariaLabel, + columns, + rows, + rowHeight, + numberOfColumns, + flowHorizontally, + width, + columnMinimumWidth, + columnMaximumWidth, } = props; const theme = useContext(ThemeContext); + const defaultUnitType = widthUnitTypes.PX; + // map the columns to ensure that width, maximumWidth and minimumWidth use same units (px, em, or rem) across all columns. + // if a width prop uses different units, it will be disregarded. + const [conversionedColumns, widthUnit] = converseColumnTypes(columns, defaultUnitType); + // ensure columnMinimumWidth is in the same width units + let columnMinWidth = getValueUnitTypePair(columnMinimumWidth); + if (!columnMinWidth) { + columnMinWidth = getValueUnitTypePair(DefaultListValues.minimumWidth[widthUnit]); + } else if (columnMinWidth.unitType !== widthUnit) { + // eslint-disable-next-line no-console + console.warn(WARNINGS.COLUMN_MIN_WIDTH_UNIT_TYPE); + columnMinWidth = getValueUnitTypePair(DefaultListValues.minimumWidth[widthUnit]); + } + // ensure columnMinimumWidth has proper width units + let columnMaxWidth = getValueUnitTypePair(columnMaximumWidth); + if (columnMaxWidth && columnMaxWidth.unitType !== widthUnit) { + // eslint-disable-next-line no-console + console.warn(WARNINGS.COLUMN_MAX_WIDTH_UNIT_TYPE); + columnMaxWidth = null; + } + + // check if list has responsive columns + const isResponsive = checkIfRowHasResponsiveColumns(conversionedColumns); + // if there are responsive columns, we need maxWidth and minWidth for semantic rows specifically. + + const rowMaxWidth = isResponsive ? getRowMaximumWidth(conversionedColumns, columnMaxWidth?.value) : null; + const rowMinWidth = isResponsive ? getRowMinimumWidth(conversionedColumns, columnMinWidth?.value) : null; + + // calculate row width based on the width of its columns + const getRowWidthSum = (total, column) => total + column.width; + const rowWidth = isResponsive ? null : conversionedColumns.reduce(getRowWidthSum, 0); + const rowsPerColumn = Math.ceil(rows.length / numberOfColumns); + + const calculatedRowHeight = flowHorizontally ? null : getValueUnitTypePair(rowHeight || DefaultListValues.rowDefaultHeight[widthUnit]); + // calculate list width based on the semantic row width and number of columns + const listWidth = `${rowWidth * numberOfColumns}${widthUnit}`; + // calculate list min width or use default + const defaultListMinWidth = getValueUnitTypePair(DefaultListValues.listMinimumWidth[widthUnit])?.value; + const listMinWidth = isResponsive ? Math.max(rowMinWidth * numberOfColumns, defaultListMinWidth) : null; + // defining styles to apply to the list + const style = { + width: isResponsive ? width : listWidth, + height: flowHorizontally ? null : `${calculatedRowHeight?.value * rowsPerColumn}${calculatedRowHeight?.unitType}`, + minWidth: isResponsive ? `${listMinWidth}${widthUnit}` : null, + flexDirection: flowHorizontally ? 'row' : 'column', + }; + if (rowMaxWidth) { + style.maxWidth = `${rowMaxWidth * numberOfColumns}${widthUnit}`; + } + + // number of rows including placeholder rows + const numberOfRows = Math.ceil(rows.length / numberOfColumns); + // map rows differently depending on vertical or horizontal orientation + const mapRows = () => { + const placeholdersNumber = isResponsive ? (numberOfRows * numberOfColumns) - rows.length : 0; + let result = []; + result = [...rows]; + if (flowHorizontally) { + // all placeholder rows go in the end. + for (let i = rows.length; i < rows.length + placeholdersNumber; i += 1) { + result.push({ id: `placeholder-row-${i - rows.length + 1}` }); + } + } else { + // inject placeholders to specific positions so that they all appear in the last row. + let position = rows.length; + for (let i = placeholdersNumber; i > 0; i -= 1) { + result.splice(position, 0, { id: `placeholder-row-${i}` }); + position -= rowsPerColumn - 1; + } + } + return result; + }; + + const mappedRows = mapRows(); + const checkIfRowIsLeftMost = (index) => (flowHorizontally ? index % numberOfColumns === 0 : index < rowsPerColumn); + const checkIfRowIsTopMost = (index) => (flowHorizontally ? index < numberOfColumns : index % rowsPerColumn === 0); + return (
@@ -48,16 +189,37 @@ const CompactInteractiveList = (props) => { role="grid" aria-labelledby={ariaLabelledBy} aria-label={ariaLabel} - className={cx('compact-interactive-list', theme.className)} + className={cx('compact-interactive-list')} + // eslint-disable-next-line react/forbid-dom-props + style={style} > -
-
This is the placeholder div
-
+ {mappedRows.map((row, index) => ( + + ))}
); }; CompactInteractiveList.propTypes = propTypes; +CompactInteractiveList.defaultProps = defaultProps; export default injectIntl(CompactInteractiveList); diff --git a/packages/terra-compact-interactive-list/src/CompactInteractiveList.module.scss b/packages/terra-compact-interactive-list/src/CompactInteractiveList.module.scss index 8260dba4b42..73e295742a8 100644 --- a/packages/terra-compact-interactive-list/src/CompactInteractiveList.module.scss +++ b/packages/terra-compact-interactive-list/src/CompactInteractiveList.module.scss @@ -1,14 +1,21 @@ +// Themes @import './clinical-lowlight-theme/CompactInteractiveList.module'; @import './orion-fusion-theme/CompactInteractiveList.module'; :local { .compact-interactive-list-container { - height: 100%; + height: auto; overflow: auto; - width: 100%; + + > * { + box-sizing: border-box; + } } .compact-interactive-list { + display: flex; + flex-wrap: wrap; + :focus { outline: var(--terra-compact-interactive-list-cell-focus-outline, 3px dashed #000); outline-offset: var(--terra-compact-interactive-list-cell-focus-outline-offset, -2px); diff --git a/packages/terra-compact-interactive-list/src/clinical-lowlight-theme/Cell.module.scss b/packages/terra-compact-interactive-list/src/clinical-lowlight-theme/Cell.module.scss new file mode 100644 index 00000000000..8a0344d0161 --- /dev/null +++ b/packages/terra-compact-interactive-list/src/clinical-lowlight-theme/Cell.module.scss @@ -0,0 +1,5 @@ +:local { + .clinical-lowlight-theme { + --terra-compact-interactive-list-hover-background-color: #25282a; + } +} diff --git a/packages/terra-compact-interactive-list/src/clinical-lowlight-theme/CompactInteractiveList.module.scss b/packages/terra-compact-interactive-list/src/clinical-lowlight-theme/CompactInteractiveList.module.scss index da5807badee..dedd608fc80 100644 --- a/packages/terra-compact-interactive-list/src/clinical-lowlight-theme/CompactInteractiveList.module.scss +++ b/packages/terra-compact-interactive-list/src/clinical-lowlight-theme/CompactInteractiveList.module.scss @@ -2,6 +2,8 @@ .clinical-lowlight-theme { --terra-compact-interactive-list-cell-focus-outline: 3px dashed #fff; --terra-compact-interactive-list-cell-focus-outline-offset: -2px; - --terra-compact-interactive-list-headerless-first-row-border-top: 1px solid #181b1d; + + --terra-compact-interactive-list-row-border: 1px solid #181b1d; + --terra-compact-interactive-list-row-background-color: #6e6f71; } } diff --git a/packages/terra-compact-interactive-list/src/index.js b/packages/terra-compact-interactive-list/src/index.js new file mode 100644 index 00000000000..0a56bbb18ff --- /dev/null +++ b/packages/terra-compact-interactive-list/src/index.js @@ -0,0 +1,5 @@ +import CompactInteractiveList from './CompactInteractiveList'; +import { widthUnitTypes, alignTypes } from './utils/constants'; + +export default CompactInteractiveList; +export { widthUnitTypes, alignTypes }; diff --git a/packages/terra-compact-interactive-list/src/orion-fusion-theme/Cell.module.scss b/packages/terra-compact-interactive-list/src/orion-fusion-theme/Cell.module.scss new file mode 100644 index 00000000000..30e91d759db --- /dev/null +++ b/packages/terra-compact-interactive-list/src/orion-fusion-theme/Cell.module.scss @@ -0,0 +1,5 @@ +:local { + .orion-fusion-theme { + --terra-compact-interactive-list-hover-background-color: #e0f2fb; + } +} diff --git a/packages/terra-compact-interactive-list/src/orion-fusion-theme/CompactInteractiveList.module.scss b/packages/terra-compact-interactive-list/src/orion-fusion-theme/CompactInteractiveList.module.scss index a51dbcff2f7..301c055329e 100644 --- a/packages/terra-compact-interactive-list/src/orion-fusion-theme/CompactInteractiveList.module.scss +++ b/packages/terra-compact-interactive-list/src/orion-fusion-theme/CompactInteractiveList.module.scss @@ -2,6 +2,11 @@ .orion-fusion-theme { --terra-compact-interactive-list-cell-focus-outline: 3px dashed #000; --terra-compact-interactive-list-cell-focus-outline-offset: -2px; - --terra-compact-interactive-list-headerless-first-row-border-top: 1px solid #c8cacb; + + --terra-compact-interactive-list-row-background-color: #fefffe; + --terra-compact-interactive-list-row-border-top: 1px solid #c8cacb; + --terra-compact-interactive-list-row-border-bottom: 1px solid #c8cacb; + --terra-compact-interactive-list-row-border-left: 1px solid #c8cacb; + --terra-compact-interactive-list-row-border-right: 1px solid #c8cacb; } } diff --git a/packages/terra-compact-interactive-list/src/proptypes/cellShape.js b/packages/terra-compact-interactive-list/src/proptypes/cellShape.js index de65f5aba22..33ece8de167 100644 --- a/packages/terra-compact-interactive-list/src/proptypes/cellShape.js +++ b/packages/terra-compact-interactive-list/src/proptypes/cellShape.js @@ -5,16 +5,6 @@ const cellShape = PropTypes.shape({ * Content that will be rendered within the Cell. */ content: PropTypes.node, - /** - * @private - * Boolean value indicating whether or not the column header is selectable. - */ - isSelectable: PropTypes.bool, - /** - * @private - * Boolean value indicating whether or not the cell should render as selected. - */ - isSelected: PropTypes.bool, }); export default cellShape; diff --git a/packages/terra-compact-interactive-list/src/proptypes/columnShape.js b/packages/terra-compact-interactive-list/src/proptypes/columnShape.js index c98b73e1b97..cb29b36f9b4 100644 --- a/packages/terra-compact-interactive-list/src/proptypes/columnShape.js +++ b/packages/terra-compact-interactive-list/src/proptypes/columnShape.js @@ -1,26 +1,53 @@ import PropTypes from 'prop-types'; +import { alignTypes } from '../utils/constants'; const columnShape = PropTypes.shape({ /** * Required string representing a unique identifier for the column. */ id: PropTypes.string.isRequired, + /** - * String of text to render within the column header cell. + * String of text which will be used to provide a column context and to serve as a column header if needed. */ displayName: PropTypes.string, + /** - * Number that specifies the minimum column width in pixels. + * A valid css string, px, em, or rem supported (should be the same across all width units). + * If not set, the column will be considered a flex growing column. + * If width is set, but flexGrow prop is also set to true, the width will be disregarded. */ - minimumWidth: PropTypes.number, + width: PropTypes.string, + /** - * Number that specifies the maximum column width in pixels. + * A valid css string, px, em, or rem supported (should be the same across all width units). + * Will be disregarded if the width is set and flexGrow is omitted or false. + * Is reccomended in cases the column is flex growing column. */ - maximumWidth: PropTypes.number, + minimumWidth: PropTypes.string, + /** - * A number (in px) specifying the width of the column. If not provided, the Table's default column width will be used. + * A valid css string, px, em, or rem supported (should be the same across all width units). + * Will be disregarded if the width is set and flexGrow is omitted or false. + * Is reccomended in cases the column is flex growing column. */ - width: PropTypes.number, + maximumWidth: PropTypes.string, + + /** + * Whether the column width can grow (a flex growing column). + * Defaults to false if the width is set and true if the width is not set. + */ + flexGrow: PropTypes.bool, + + /** + * Aligns the cell content to center, righ, or left. Defaults to the left. + */ + align: PropTypes.oneOf([ + alignTypes.CENTER, + alignTypes.RIGHT, + alignTypes.LEFT, + ]), + }); export default columnShape; diff --git a/packages/terra-compact-interactive-list/src/proptypes/formattedColumnShape.js b/packages/terra-compact-interactive-list/src/proptypes/formattedColumnShape.js new file mode 100644 index 00000000000..76793892e92 --- /dev/null +++ b/packages/terra-compact-interactive-list/src/proptypes/formattedColumnShape.js @@ -0,0 +1,53 @@ +import PropTypes from 'prop-types'; +import { alignTypes } from '../utils/constants'; + +const columnShape = PropTypes.shape({ + /** + * Required string representing a unique identifier for the column. + */ + id: PropTypes.string.isRequired, + + /** + * String of text which will be used to provide a column context and to serve as a column header if needed. + */ + displayName: PropTypes.string, + + /** + * Width of the column is a number value in px, em, or rem units. + * If not set, the column will be considered a flex growing column. + * If width is set, but flexGrow prop is also set to true, the width will be disregarded. + */ + width: PropTypes.number, + + /** + * Minimum width of the column is a number value in px, em, or rem units. + * Will be disregarded if the width is set and flexGrow is omitted or false. + * Is reccomended in cases the column is flex growing column. + */ + minimumWidth: PropTypes.number, + + /** + * Maximum width of the column is a number value in px, em, or rem units. + * Will be disregarded if the width is set and flexGrow is omitted or false. + * Is reccomended in cases the column is flex growing column. + */ + maximumWidth: PropTypes.number, + + /** + * Whether the column width can grow (a flex growing column). + * Defaults to false if the width is set and true if the width is not set. + */ + flexGrow: PropTypes.bool, + + /** + * Aligns the cell content to center, righ, or left. Defaults to the left. + */ + align: PropTypes.oneOf([ + alignTypes.CENTER, + alignTypes.RIGHT, + alignTypes.LEFT, + ]), + +}); + +export default columnShape; diff --git a/packages/terra-compact-interactive-list/src/proptypes/unitShape.js b/packages/terra-compact-interactive-list/src/proptypes/unitShape.js new file mode 100644 index 00000000000..c13472a15d7 --- /dev/null +++ b/packages/terra-compact-interactive-list/src/proptypes/unitShape.js @@ -0,0 +1,8 @@ +import PropTypes from 'prop-types'; + +const unitShape = PropTypes.shape({ + value: PropTypes.number, + unitTupe: PropTypes.string, +}); + +export default unitShape; diff --git a/packages/terra-compact-interactive-list/src/subcomponents/Cell.jsx b/packages/terra-compact-interactive-list/src/subcomponents/Cell.jsx new file mode 100644 index 00000000000..b48ba2ab5ab --- /dev/null +++ b/packages/terra-compact-interactive-list/src/subcomponents/Cell.jsx @@ -0,0 +1,89 @@ +import React, { useContext } from 'react'; +import PropTypes from 'prop-types'; +import classNames from 'classnames/bind'; +import ThemeContext from 'terra-theme-context'; +import { checkIfColumnIsResponsive } from '../utils/utils'; +import formattedColumnShape from '../proptypes/formattedColumnShape'; +import { widthUnitTypes, alignTypes } from '../utils/constants'; +import styles from './Cell.module.scss'; + +const cx = classNames.bind(styles); + +const propTypes = { + /** + * Content that will be rendered within the Cell. + */ + children: PropTypes.node, + + /** + * A column data. + */ + column: formattedColumnShape, + + /** + * Columns minimum width in units set by widthUnit prop. + */ + columnMinimumWidth: PropTypes.number, + + /** + * Columns maximum width in units set by widthUnit prop. + */ + columnMaximumWidth: PropTypes.number, + + /** + * The type of width value. Defaults to px. + */ + widthUnit: PropTypes.oneOf([ + widthUnitTypes.PX, + widthUnitTypes.EM, + widthUnitTypes.REM, + ]).isRequired, +}; + +const Cell = (props) => { + const { + children, + column, + columnMinimumWidth, + columnMaximumWidth, + widthUnit, + } = props; + + const theme = useContext(ThemeContext); + const className = cx('cell', theme.className); + + const { + width, + flexGrow, + maximumWidth, + minimumWidth, + align, + } = column; + + const isResponsive = checkIfColumnIsResponsive(flexGrow, width); + + const style = { + flex: isResponsive ? `1 1 ${width || minimumWidth || columnMinimumWidth}${widthUnit}` : null, + width: isResponsive ? null : `${width}${widthUnit}`, + justifyContent: align || alignTypes.LEFT, + textAlign: align || alignTypes.LEFT, + maxWidth: isResponsive && (maximumWidth || columnMaximumWidth) ? `${(maximumWidth || columnMaximumWidth)}${widthUnit}` : null, + minWidth: isResponsive && (minimumWidth, columnMinimumWidth) ? `${(minimumWidth || columnMinimumWidth)}${widthUnit}` : null, + }; + + return ( +
+ {children} +
+ ); +}; + +Cell.propTypes = propTypes; + +export default React.memo(Cell); diff --git a/packages/terra-compact-interactive-list/src/subcomponents/Cell.module.scss b/packages/terra-compact-interactive-list/src/subcomponents/Cell.module.scss new file mode 100644 index 00000000000..3f41dbf4924 --- /dev/null +++ b/packages/terra-compact-interactive-list/src/subcomponents/Cell.module.scss @@ -0,0 +1,17 @@ +// Themes +@import '../orion-fusion-theme/Cell.module'; +@import '../clinical-lowlight-theme/Cell.module'; + +:local { + .cell { + align-items: center; + display: flex; + overflow: hidden; + padding: var(--terra-compact-interactive-list-cell-content-padding, 7px); + position: relative; + + &:hover { + background-color: var(--terra-compact-interactive-list-hover-background-color, #e0f2fb); + } + } +} diff --git a/packages/terra-compact-interactive-list/src/subcomponents/Row.jsx b/packages/terra-compact-interactive-list/src/subcomponents/Row.jsx new file mode 100644 index 00000000000..5e9de9a0308 --- /dev/null +++ b/packages/terra-compact-interactive-list/src/subcomponents/Row.jsx @@ -0,0 +1,158 @@ +import React, { useContext } from 'react'; +import PropTypes from 'prop-types'; +import ThemeContext from 'terra-theme-context'; +import classNames from 'classnames/bind'; +import styles from './Row.module.scss'; +import Cell from './Cell'; +import cellShape from '../proptypes/cellShape'; +import formattedColumnShape from '../proptypes/formattedColumnShape'; +import { widthUnitTypes } from '../utils/constants'; +import unitShape from '../proptypes/unitShape'; + +const cx = classNames.bind(styles); + +const propTypes = { + /** + * An identifier to uniquely identify the row. + */ + id: PropTypes.string.isRequired, + + /** + * 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), + + /** + * Data for columns. + */ + columns: PropTypes.arrayOf(formattedColumnShape).isRequired, + + /** + * A number for column minimum width. + */ + columnMinimumWidth: PropTypes.number, + + /** + * A number for column maximum width. + */ + columnMaximumWidth: PropTypes.number, + + /** + * Number of visual columns. + */ + numberOfColumns: PropTypes.number.isRequired, + + /** + * Indicates if the column is flex growing column (can grow wider or shrink). + */ + isResponsive: PropTypes.bool.isRequired, + + /** + * Row's width in units set by widthUnit prop, such as `px`, `em`, or `rem`. + * Disregarded if flexGrow is set to true. + */ + rowWidth: PropTypes.number, + + /** + * Row's maximum width for flex growing rows in units set by widthUnit prop, such as `px`, `em`, or `rem`. + * Will have no effect if row width is set and flexGrow is omitted or false. + */ + rowMaximumWidth: PropTypes.number, + + /** + * Row's minimum width for flex growing rows in units set by widthUnit prop, such as `px`, `em`, or `rem`. + * Will have no effect if row width is set and flexGrow is omitted or false. + */ + rowMinimumWidth: PropTypes.number, + + /** + * The type of width value. One of `px`, `em`, `rem`. Defaults to 'px'. + */ + widthUnit: PropTypes.oneOf([ + widthUnitTypes.PX, + widthUnitTypes.EM, + widthUnitTypes.REM, + ]).isRequired, + + /** + * By default the items go from top to bottom, then break to the next column. + * If flowHorizontally prop is set to true, items will flow left to right, then break to the next row. + */ + flowHorizontally: PropTypes.bool, + + /** + * A valid css string, px, em, or rem supported. + */ + rowHeight: unitShape, + + /** + * Indicates if the row is located in the top visual row and needs a top border. + */ + isTopmost: PropTypes.bool, + + /** + * Indicates if the row is located in the left visual column and needs a left border. + */ + isLeftmost: PropTypes.bool, +}; + +const Row = (props) => { + const { + id, + cells, + columns, + columnMinimumWidth, + columnMaximumWidth, + numberOfColumns, + isResponsive, + rowMaximumWidth, + rowMinimumWidth, + rowWidth, + widthUnit, + flowHorizontally, + rowHeight, + isTopmost, + isLeftmost, + } = props; + + const theme = useContext(ThemeContext); + + const style = isResponsive ? { + width: flowHorizontally ? null : `${Math.min(100 / numberOfColumns)}%`, + flex: flowHorizontally ? `1 1 ${Math.min(100 / numberOfColumns)}%` : null, + maxWidth: rowMaximumWidth ? `${rowMaximumWidth}${widthUnit}` : null, + minWidth: rowMinimumWidth ? `${rowMinimumWidth}${widthUnit}` : null, + } : { width: `${rowWidth}${widthUnit}` }; + if (rowHeight) { + style.height = `${rowHeight?.value}${rowHeight?.unitType}`; + } + + const activeRow = cells && cells.length > 0; + + return ( +
+ {activeRow && cells.map((cellData, index) => ( + + {cellData.content} + + ))} +
+ ); +}; + +Row.propTypes = propTypes; + +export default React.memo(Row); diff --git a/packages/terra-compact-interactive-list/src/subcomponents/Row.module.scss b/packages/terra-compact-interactive-list/src/subcomponents/Row.module.scss new file mode 100644 index 00000000000..42f7d4fd539 --- /dev/null +++ b/packages/terra-compact-interactive-list/src/subcomponents/Row.module.scss @@ -0,0 +1,25 @@ +// Themes +@import '../orion-fusion-theme/CompactInteractiveList.module'; +@import '../clinical-lowlight-theme/CompactInteractiveList.module'; + +:local { + .row { + box-sizing: border-box; + display: flex; + position: relative; + + &:not(.row-placeholder) { + background-color: var(--terra-compact-interactive-list-row-background-color, #fefffe); + border-bottom: var(--terra-compact-interactive-list-row-border-bottom, 1px solid #dedfe0); + border-right: var(--terra-compact-interactive-list-row-border-right, 1px solid #dedfe0); + } + } + + .row-topmost { + border-top: var(--terra-compact-interactive-list-row-border-top, 1px solid #dedfe0); + } + + .row-leftmost { + border-left: var(--terra-compact-interactive-list-row-border-left, 1px solid #dedfe0); + } +} diff --git a/packages/terra-compact-interactive-list/src/utils/constants.js b/packages/terra-compact-interactive-list/src/utils/constants.js new file mode 100644 index 00000000000..044c5b29d2b --- /dev/null +++ b/packages/terra-compact-interactive-list/src/utils/constants.js @@ -0,0 +1,36 @@ +export const widthUnitTypes = { + PX: 'px', + EM: 'em', + REM: 'rem', +}; + +export const DefaultListValues = { + // minimumWidth and maximumWidth names are reserved for columns + minimumWidth: { + [widthUnitTypes.PX]: '60px', + [widthUnitTypes.EM]: '3em', + [widthUnitTypes.REM]: '3rem', + }, + listMinimumWidth: { + [widthUnitTypes.PX]: '500px', + [widthUnitTypes.EM]: '10em', + [widthUnitTypes.REM]: '10rem', + }, + rowDefaultHeight: { + [widthUnitTypes.PX]: '40px', + [widthUnitTypes.EM]: '3.5em', + [widthUnitTypes.REM]: '3.5rem', + }, +}; + +export const alignTypes = { + CENTER: 'center', + LEFT: 'left', + RIGHT: 'right', +}; + +export const WARNINGS = { + COLUMN_MIN_WIDTH_UNIT_TYPE: 'columnMinWidth prop has different unitType than the one used in columns. It will be disregarded and the default value will be used instead.', + COLUMN_MAX_WIDTH_UNIT_TYPE: 'columnMaxWidth prop has different unitType than the one used in columns. It will be disregarded.', + COLUMN_WIDTH_INCONSISTENT_TYPE: 'width, minWidth, and maxWidth should be of the same unit type across all the columns. px, em and rem are supported types.', +}; diff --git a/packages/terra-compact-interactive-list/src/utils/utils.js b/packages/terra-compact-interactive-list/src/utils/utils.js new file mode 100644 index 00000000000..dd1ecd4214f --- /dev/null +++ b/packages/terra-compact-interactive-list/src/utils/utils.js @@ -0,0 +1,123 @@ +import { widthUnitTypes, WARNINGS } from './constants'; + +/** + * A method that splits the css width, mimWidth or Maxwidth string into number value and string type. + */ +export const getValueUnitTypePair = (valueString) => { + if (!valueString || valueString.length === 0 || typeof valueString !== 'string') { + return null; + } + const regex = new RegExp('([0-9.]+)|([a-zA-Z]+)', 'g'); + const parsedValueString = valueString.match(regex); + if (!parsedValueString || parsedValueString.length < 2) { + return null; + } + const unitType = parsedValueString[1] || widthUnitTypes.PX; + const value = unitType === widthUnitTypes.PX ? parseInt(parsedValueString[0], 10) : parseFloat(parsedValueString[0]); + // If the first element is number and the second is in widthUnitTypes, return the value/type pair + if (typeof value === 'number' && Object.values(widthUnitTypes).includes(unitType)) { + return { value, unitType }; + } + return null; +}; + +/** + * A column is responsive in following cases: + * 1) flexGrow is set to true. + * 2) there is no width prop set to a number. + */ +export const checkIfColumnIsResponsive = (flexGrow, width) => flexGrow || !(width && typeof width === 'number'); + +// A row is considered being responsive if it has at least one column that can grow or shrink (a flex column). +const checkIfAnyColumnIsResponsive = (total, column) => total || checkIfColumnIsResponsive(column.flexGrow, column.width); +export const checkIfRowHasResponsiveColumns = (columns) => columns.reduce(checkIfAnyColumnIsResponsive, false); + +/** + * The method checks if width, maxWidth, and minWidth props use the same unit (px, em or rem). + * The first unit type is considered the width unit, all props with types different tham that will be disregarded. + */ +export const converseColumnTypes = (columns, defaultType) => { + let unitType; + // get unitType and check it's consistant across columns + + let i = 0; + while (!unitType && i < columns.length) { + const widthProp = columns[i].width || columns[i].minimumWidth || columns[i].maximumWidth; + if (widthProp) { + unitType = getValueUnitTypePair(widthProp)?.unitType; + } + i += 1; + } + + if (!unitType) { + return [[columns], defaultType]; + } + + const newColumns = columns.map(column => { + const width = getValueUnitTypePair(column.width); + const minimumWidth = getValueUnitTypePair(column.minimumWidth); + const maximumWidth = getValueUnitTypePair(column.maximumWidth); + if ( + (width && width?.unitType !== unitType) + || (minimumWidth && maximumWidth?.unitType !== unitType) + || (minimumWidth && minimumWidth?.unitType !== unitType)) { + // eslint-disable-next-line no-console + console.warn(WARNINGS.COLUMN_WIDTH_INCONSISTENT_TYPE); + } + const newColumn = { ...column }; + if (width?.unitType === unitType) { + newColumn.width = width?.value; + } + if (maximumWidth?.unitType === unitType) { + newColumn.maximumWidth = maximumWidth?.value; + } + if (minimumWidth) { + newColumn.minimumWidth = minimumWidth?.value; + } + return newColumn; + }); + return [newColumns, unitType]; +}; + +/** + * A semantic row needs a maxWidth prop if: + * 1) There is at least one flex column in the list. + * 2) All flex columns have maximumWidth property set, or columnMaximumWidth was set for the whole list component. + */ +export const getRowMaximumWidth = (columns, columnMaximumWidth) => { + let flexColumnWasFound = false; + let maxWidth = 0; + for (let i = 0; i < columns.length; i += 1) { + if (checkIfColumnIsResponsive(columns[i].flexGrow, columns[i].width)) { + flexColumnWasFound = true; + if (columns[i].maximumWidth || columnMaximumWidth) { + const colMaxWidth = columns[i].maximumWidth || columnMaximumWidth; + maxWidth += colMaxWidth; + } else { return null; } + } else if (columns[i].width) { + maxWidth += columns[i].width; + } else { return null; } + } + return flexColumnWasFound ? maxWidth : null; +}; + +/** + * A semantic row needs a minimumWidth prop if: + * 1) There is at least one flex column in the list. + * 2) All flex columns have minimumWidth property set, or columnMinimumWidth was set for the whole list component. + */ +export const getRowMinimumWidth = (columns, columnMinimumWidth) => { + let flexColumnWasFound = false; + let minWidth = 0; + for (let i = 0; i < columns.length; i += 1) { + if (checkIfColumnIsResponsive(columns[i].flexGrow, columns[i].width)) { + flexColumnWasFound = true; + if (columns[i].minimumWidth || columnMinimumWidth) { + minWidth += columns[i].minimumWidth || columnMinimumWidth; + } else { return null; } + } else if (columns[i].width) { + minWidth += columns[i].width; + } else { return null; } + } + return flexColumnWasFound ? minWidth : null; +}; diff --git a/packages/terra-compact-interactive-list/tests/jest/CompactInteractiveList.test.jsx b/packages/terra-compact-interactive-list/tests/jest/CompactInteractiveList.test.jsx index bf639e1aedc..f7d1b2c086c 100644 --- a/packages/terra-compact-interactive-list/tests/jest/CompactInteractiveList.test.jsx +++ b/packages/terra-compact-interactive-list/tests/jest/CompactInteractiveList.test.jsx @@ -1,7 +1,8 @@ import React from 'react'; /* eslint-disable-next-line import/no-extraneous-dependencies */ -import { shallowWithIntl } from 'terra-enzyme-intl'; +import { mountWithIntl } from 'terra-enzyme-intl'; import CompactInteractiveList from '../../src/CompactInteractiveList'; +import rows from './rowsData'; beforeAll(() => { jest.spyOn(console, 'error').mockImplementation(); @@ -13,12 +14,220 @@ afterAll(() => { console.warn.mockRestore(); // eslint-disable-line no-console }); -describe('placeholder for tests', () => { - it('placeholder for a test', () => { - const wrapper = shallowWithIntl( - , - ).dive(); +describe('Compact Interactive List', () => { + describe('fixed width list', () => { + const cols = [ + { + id: 'Column-0', + displayName: 'Col_1', + width: '40px', + }, + { + id: 'Column-1', + displayName: 'Col_2', + width: '200px', + }, + { + id: 'Column-2', + displayName: 'Col_3', + width: '40px', + }, + ]; - expect(wrapper).toMatchSnapshot(); + it('should not apply default minimumWidth to fixed width list', () => { + const wrapper = mountWithIntl( + , + ); + const list = wrapper.find('.compact-interactive-list'); + expect(list.props().style.minWidth).toBeFalsy(); + }); + + it('should not apply columnMaximumWidth and columnMinimumWidth props to fixed columns width', () => { + const numberOfColumns = 2; + const wrapper = mountWithIntl( + , + ); + const list = wrapper.find('.compact-interactive-list'); + // columnMinimumWidth and columnMaximumWidth should NOT be applied to fixed width lists + expect(list.props().style.minWidth).toBeFalsy(); + expect(list.props().style.maxWidth).toBeFalsy(); + // the width should be equal to the sum of all columns width multiplied by numberOfColumns + expect(list.props().style.width).toEqual('560px'); + }); + }); + + describe('responsive columns', () => { + it('should calculate list minWidth based on default columnMinWidth and apply default minListWIdth if it is bigger', () => { + const respCols1 = [ + { + id: 'Column-0', + displayName: 'Col_1', + width: '20px', + }, + { + id: 'Column-1', + displayName: 'Col_2', + width: '200px', + flexGrow: true, + }, + { + id: 'Column-2', + displayName: 'Col_3', + width: '20px', + }, + ]; + const numberOfColumns = 2; + const wrapper = mountWithIntl( + , + ); + const list = wrapper.find('.compact-interactive-list'); + expect(list.props().style.minWidth).toEqual('500px'); + }); + + it('should calculate list minWidth based on column level minimumWidth, compare to default and apply the bigger one', () => { + const respCols1 = [ + { + id: 'Column-0', + displayName: 'Col_1', + width: '6em', + }, + { + id: 'Column-1', + displayName: 'Col_2', + width: '20em', + flexGrow: true, + minimumWidth: '10em', + maximumWidth: '30em', + }, + { + id: 'Column-2', + displayName: 'Col_3', + width: '7em', + }, + ]; + const numberOfColumns = 2; + const wrapper = mountWithIntl( + , + ); + const list = wrapper.find('.compact-interactive-list'); + // compare to default value of 10em and choose 46em as it's bigger + expect(list.props().style.minWidth).toEqual('46em'); + // maxWidth should be calculated based on maximumWidth set on column level + expect(list.props().style.maxWidth).toEqual('86em'); + }); + + it('should flow rows vertically by default', () => { + const respCols = [ + { + id: 'Column-0', + displayName: 'Col_1', + width: '60px', + }, + { + id: 'Column-1', + displayName: 'Col_2', + width: '200px', + flexGrow: true, + }, + { + id: 'Column-2', + displayName: 'Col_3', + width: '60px', + }, + ]; + const numberOfColumns = 4; + const wrapper = mountWithIntl( + , + ); + const rowElements = wrapper.find('.row'); + expect(rowElements.length).toEqual(8); + expect(rowElements.at(0).props().id).toEqual(rows[0].id); + expect(rowElements.at(0).props().role).toEqual('row'); + expect(rowElements.at(0).props()['aria-hidden']).toBeFalsy(); + + expect(rowElements.at(1).props().id).toEqual(rows[1].id); + expect(rowElements.at(2).props().id).toEqual(rows[2].id); + + expect(rowElements.at(3).props().id).toEqual(`placeholder-row-${1}`); + expect(rowElements.at(3).props().role).toBeFalsy(); + expect(rowElements.at(3).props()['aria-hidden']).toEqual(true); + + expect(rowElements.at(4).props().id).toEqual(rows[3].id); + expect(rowElements.at(5).props().id).toEqual(`placeholder-row-${2}`); + expect(rowElements.at(6).props().id).toEqual(rows[4].id); + expect(rowElements.at(7).props().id).toEqual(`placeholder-row-${3}`); + }); + + it('should flow horizontally if flowHorizontally prop is set', () => { + const respCols = [ + { + id: 'Column-0', + displayName: 'Col_1', + width: '60px', + }, + { + id: 'Column-1', + displayName: 'Col_2', + width: '200px', + flexGrow: true, + }, + { + id: 'Column-2', + displayName: 'Col_3', + width: '60px', + }, + ]; + const numberOfColumns = 4; + const wrapper = mountWithIntl( + , + ); + const rowElements = wrapper.find('.row'); + expect(rowElements.at(0).props().id).toEqual(rows[0].id); + expect(rowElements.at(0).props().role).toEqual('row'); + expect(rowElements.at(0).props()['aria-hidden']).toBeFalsy(); + + expect(rowElements.at(1).props().id).toEqual(rows[1].id); + expect(rowElements.at(2).props().id).toEqual(rows[2].id); + expect(rowElements.at(3).props().id).toEqual(rows[3].id); + expect(rowElements.at(4).props().id).toEqual(rows[4].id); + expect(rowElements.at(5).props().id).toEqual(`placeholder-row-${1}`); + expect(rowElements.at(6).props().id).toEqual(`placeholder-row-${2}`); + + expect(rowElements.at(7).props().id).toEqual(`placeholder-row-${3}`); + expect(rowElements.at(7).props().role).toBeFalsy(); + expect(rowElements.at(7).props()['aria-hidden']).toEqual(true); + }); }); }); diff --git a/packages/terra-compact-interactive-list/tests/jest/__snapshots__/CompactInteractiveList.test.jsx.snap b/packages/terra-compact-interactive-list/tests/jest/__snapshots__/CompactInteractiveList.test.jsx.snap deleted file mode 100644 index 62b19761c40..00000000000 --- a/packages/terra-compact-interactive-list/tests/jest/__snapshots__/CompactInteractiveList.test.jsx.snap +++ /dev/null @@ -1,23 +0,0 @@ -// Jest Snapshot v1, https://goo.gl/fbAQLP - -exports[`placeholder for tests placeholder for a test 1`] = ` -
-
-
-
- This is the placeholder div -
-
-
-
-`; diff --git a/packages/terra-compact-interactive-list/tests/jest/rowsData.jsx b/packages/terra-compact-interactive-list/tests/jest/rowsData.jsx new file mode 100644 index 00000000000..be6111d4e40 --- /dev/null +++ b/packages/terra-compact-interactive-list/tests/jest/rowsData.jsx @@ -0,0 +1,47 @@ +import React from 'react'; +import { IconDocuments, IconFeaturedOutline, IconImage } from 'terra-icon'; + +const rows = [ + { + id: 'row_1', + cells: [ + { content: }, + { content: 'Discern Care Set (1)' }, + { content: }, + ], + }, + { + id: 'row_2', + cells: [ + { content: }, + { content: 'Initial observation Care/Day High Severity 99220 (2)' }, + { content: }, + ], + }, + { + id: 'row_3', + cells: [ + { content: }, + { content: 'Arterial Sheath Care (3)' }, + { content: }, + ], + }, + { + id: 'row_4', + cells: [ + { content: ' ' }, + { content: 'Sbsq Observation Care/Day High Severity 99226 (4)' }, + { content: ' ' }, + ], + }, + { + id: 'row_5', + cells: [ + { content: }, + { content: 'Arterial Sheath Care (5)' }, + { content: }, + ], + }, +]; + +export default rows; diff --git a/packages/terra-compact-interactive-list/tests/wdio/__snapshots__/reference/clinical-lowlight-theme/en/chrome_large/compact-interactive-list-spec/fixed_width_columns.png b/packages/terra-compact-interactive-list/tests/wdio/__snapshots__/reference/clinical-lowlight-theme/en/chrome_large/compact-interactive-list-spec/fixed_width_columns.png new file mode 100644 index 00000000000..419a3e8a874 Binary files /dev/null and b/packages/terra-compact-interactive-list/tests/wdio/__snapshots__/reference/clinical-lowlight-theme/en/chrome_large/compact-interactive-list-spec/fixed_width_columns.png differ diff --git a/packages/terra-compact-interactive-list/tests/wdio/__snapshots__/reference/clinical-lowlight-theme/en/chrome_large/compact-interactive-list-spec/responsive_columns.png b/packages/terra-compact-interactive-list/tests/wdio/__snapshots__/reference/clinical-lowlight-theme/en/chrome_large/compact-interactive-list-spec/responsive_columns.png new file mode 100644 index 00000000000..6d235e9834b Binary files /dev/null and b/packages/terra-compact-interactive-list/tests/wdio/__snapshots__/reference/clinical-lowlight-theme/en/chrome_large/compact-interactive-list-spec/responsive_columns.png differ diff --git a/packages/terra-compact-interactive-list/tests/wdio/__snapshots__/reference/clinical-lowlight-theme/en/chrome_large/compact-interactive-list-spec/responsive_columns_with_max_width.png b/packages/terra-compact-interactive-list/tests/wdio/__snapshots__/reference/clinical-lowlight-theme/en/chrome_large/compact-interactive-list-spec/responsive_columns_with_max_width.png new file mode 100644 index 00000000000..2b61e606a6d Binary files /dev/null and b/packages/terra-compact-interactive-list/tests/wdio/__snapshots__/reference/clinical-lowlight-theme/en/chrome_large/compact-interactive-list-spec/responsive_columns_with_max_width.png differ diff --git a/packages/terra-compact-interactive-list/tests/wdio/__snapshots__/reference/clinical-lowlight-theme/en/chrome_large/compact-interactive-list-spec/scalable_units.png b/packages/terra-compact-interactive-list/tests/wdio/__snapshots__/reference/clinical-lowlight-theme/en/chrome_large/compact-interactive-list-spec/scalable_units.png new file mode 100644 index 00000000000..c36d49c29b4 Binary files /dev/null and b/packages/terra-compact-interactive-list/tests/wdio/__snapshots__/reference/clinical-lowlight-theme/en/chrome_large/compact-interactive-list-spec/scalable_units.png differ diff --git a/packages/terra-compact-interactive-list/tests/wdio/__snapshots__/reference/clinical-lowlight-theme/en/chrome_large/compact-interactive-list-spec/width_breakpoints.png b/packages/terra-compact-interactive-list/tests/wdio/__snapshots__/reference/clinical-lowlight-theme/en/chrome_large/compact-interactive-list-spec/width_breakpoints.png new file mode 100644 index 00000000000..2f1d587a1b7 Binary files /dev/null and b/packages/terra-compact-interactive-list/tests/wdio/__snapshots__/reference/clinical-lowlight-theme/en/chrome_large/compact-interactive-list-spec/width_breakpoints.png differ diff --git a/packages/terra-compact-interactive-list/tests/wdio/__snapshots__/reference/clinical-lowlight-theme/en/chrome_large/compact-interactive-list-spec/width_breakpoints_horizontal_flow.png b/packages/terra-compact-interactive-list/tests/wdio/__snapshots__/reference/clinical-lowlight-theme/en/chrome_large/compact-interactive-list-spec/width_breakpoints_horizontal_flow.png new file mode 100644 index 00000000000..711e65f1e08 Binary files /dev/null and b/packages/terra-compact-interactive-list/tests/wdio/__snapshots__/reference/clinical-lowlight-theme/en/chrome_large/compact-interactive-list-spec/width_breakpoints_horizontal_flow.png differ diff --git a/packages/terra-compact-interactive-list/tests/wdio/__snapshots__/reference/clinical-lowlight-theme/en/chrome_medium/compact-interactive-list-spec/fixed_width_columns.png b/packages/terra-compact-interactive-list/tests/wdio/__snapshots__/reference/clinical-lowlight-theme/en/chrome_medium/compact-interactive-list-spec/fixed_width_columns.png new file mode 100644 index 00000000000..a5ae5ece589 Binary files /dev/null and b/packages/terra-compact-interactive-list/tests/wdio/__snapshots__/reference/clinical-lowlight-theme/en/chrome_medium/compact-interactive-list-spec/fixed_width_columns.png differ diff --git a/packages/terra-compact-interactive-list/tests/wdio/__snapshots__/reference/clinical-lowlight-theme/en/chrome_medium/compact-interactive-list-spec/responsive_columns.png b/packages/terra-compact-interactive-list/tests/wdio/__snapshots__/reference/clinical-lowlight-theme/en/chrome_medium/compact-interactive-list-spec/responsive_columns.png new file mode 100644 index 00000000000..bef4713e0c4 Binary files /dev/null and b/packages/terra-compact-interactive-list/tests/wdio/__snapshots__/reference/clinical-lowlight-theme/en/chrome_medium/compact-interactive-list-spec/responsive_columns.png differ diff --git a/packages/terra-compact-interactive-list/tests/wdio/__snapshots__/reference/clinical-lowlight-theme/en/chrome_medium/compact-interactive-list-spec/responsive_columns_with_max_width.png b/packages/terra-compact-interactive-list/tests/wdio/__snapshots__/reference/clinical-lowlight-theme/en/chrome_medium/compact-interactive-list-spec/responsive_columns_with_max_width.png new file mode 100644 index 00000000000..a69d10f686a Binary files /dev/null and b/packages/terra-compact-interactive-list/tests/wdio/__snapshots__/reference/clinical-lowlight-theme/en/chrome_medium/compact-interactive-list-spec/responsive_columns_with_max_width.png differ diff --git a/packages/terra-compact-interactive-list/tests/wdio/__snapshots__/reference/clinical-lowlight-theme/en/chrome_medium/compact-interactive-list-spec/scalable_units.png b/packages/terra-compact-interactive-list/tests/wdio/__snapshots__/reference/clinical-lowlight-theme/en/chrome_medium/compact-interactive-list-spec/scalable_units.png new file mode 100644 index 00000000000..4b961349878 Binary files /dev/null and b/packages/terra-compact-interactive-list/tests/wdio/__snapshots__/reference/clinical-lowlight-theme/en/chrome_medium/compact-interactive-list-spec/scalable_units.png differ diff --git a/packages/terra-compact-interactive-list/tests/wdio/__snapshots__/reference/clinical-lowlight-theme/en/chrome_medium/compact-interactive-list-spec/width_breakpoints.png b/packages/terra-compact-interactive-list/tests/wdio/__snapshots__/reference/clinical-lowlight-theme/en/chrome_medium/compact-interactive-list-spec/width_breakpoints.png new file mode 100644 index 00000000000..bef4713e0c4 Binary files /dev/null and b/packages/terra-compact-interactive-list/tests/wdio/__snapshots__/reference/clinical-lowlight-theme/en/chrome_medium/compact-interactive-list-spec/width_breakpoints.png differ diff --git a/packages/terra-compact-interactive-list/tests/wdio/__snapshots__/reference/clinical-lowlight-theme/en/chrome_medium/compact-interactive-list-spec/width_breakpoints_horizontal_flow.png b/packages/terra-compact-interactive-list/tests/wdio/__snapshots__/reference/clinical-lowlight-theme/en/chrome_medium/compact-interactive-list-spec/width_breakpoints_horizontal_flow.png new file mode 100644 index 00000000000..a0d1d8e42c2 Binary files /dev/null and b/packages/terra-compact-interactive-list/tests/wdio/__snapshots__/reference/clinical-lowlight-theme/en/chrome_medium/compact-interactive-list-spec/width_breakpoints_horizontal_flow.png differ diff --git a/packages/terra-compact-interactive-list/tests/wdio/__snapshots__/reference/orion-fusion-theme/en/chrome_large/compact-interactive-list-spec/fixed_width_columns.png b/packages/terra-compact-interactive-list/tests/wdio/__snapshots__/reference/orion-fusion-theme/en/chrome_large/compact-interactive-list-spec/fixed_width_columns.png new file mode 100644 index 00000000000..09a8377355d Binary files /dev/null and b/packages/terra-compact-interactive-list/tests/wdio/__snapshots__/reference/orion-fusion-theme/en/chrome_large/compact-interactive-list-spec/fixed_width_columns.png differ diff --git a/packages/terra-compact-interactive-list/tests/wdio/__snapshots__/reference/orion-fusion-theme/en/chrome_large/compact-interactive-list-spec/responsive_columns.png b/packages/terra-compact-interactive-list/tests/wdio/__snapshots__/reference/orion-fusion-theme/en/chrome_large/compact-interactive-list-spec/responsive_columns.png new file mode 100644 index 00000000000..eb51ca624b0 Binary files /dev/null and b/packages/terra-compact-interactive-list/tests/wdio/__snapshots__/reference/orion-fusion-theme/en/chrome_large/compact-interactive-list-spec/responsive_columns.png differ diff --git a/packages/terra-compact-interactive-list/tests/wdio/__snapshots__/reference/orion-fusion-theme/en/chrome_large/compact-interactive-list-spec/responsive_columns_with_max_width.png b/packages/terra-compact-interactive-list/tests/wdio/__snapshots__/reference/orion-fusion-theme/en/chrome_large/compact-interactive-list-spec/responsive_columns_with_max_width.png new file mode 100644 index 00000000000..8f947765ceb Binary files /dev/null and b/packages/terra-compact-interactive-list/tests/wdio/__snapshots__/reference/orion-fusion-theme/en/chrome_large/compact-interactive-list-spec/responsive_columns_with_max_width.png differ diff --git a/packages/terra-compact-interactive-list/tests/wdio/__snapshots__/reference/orion-fusion-theme/en/chrome_large/compact-interactive-list-spec/scalable_units.png b/packages/terra-compact-interactive-list/tests/wdio/__snapshots__/reference/orion-fusion-theme/en/chrome_large/compact-interactive-list-spec/scalable_units.png new file mode 100644 index 00000000000..3929a663c27 Binary files /dev/null and b/packages/terra-compact-interactive-list/tests/wdio/__snapshots__/reference/orion-fusion-theme/en/chrome_large/compact-interactive-list-spec/scalable_units.png differ diff --git a/packages/terra-compact-interactive-list/tests/wdio/__snapshots__/reference/orion-fusion-theme/en/chrome_large/compact-interactive-list-spec/width_breakpoints.png b/packages/terra-compact-interactive-list/tests/wdio/__snapshots__/reference/orion-fusion-theme/en/chrome_large/compact-interactive-list-spec/width_breakpoints.png new file mode 100644 index 00000000000..0a3fa5a1571 Binary files /dev/null and b/packages/terra-compact-interactive-list/tests/wdio/__snapshots__/reference/orion-fusion-theme/en/chrome_large/compact-interactive-list-spec/width_breakpoints.png differ diff --git a/packages/terra-compact-interactive-list/tests/wdio/__snapshots__/reference/orion-fusion-theme/en/chrome_large/compact-interactive-list-spec/width_breakpoints_horizontal_flow.png b/packages/terra-compact-interactive-list/tests/wdio/__snapshots__/reference/orion-fusion-theme/en/chrome_large/compact-interactive-list-spec/width_breakpoints_horizontal_flow.png new file mode 100644 index 00000000000..f3145e53149 Binary files /dev/null and b/packages/terra-compact-interactive-list/tests/wdio/__snapshots__/reference/orion-fusion-theme/en/chrome_large/compact-interactive-list-spec/width_breakpoints_horizontal_flow.png differ diff --git a/packages/terra-compact-interactive-list/tests/wdio/__snapshots__/reference/orion-fusion-theme/en/chrome_medium/compact-interactive-list-spec/fixed_width_columns.png b/packages/terra-compact-interactive-list/tests/wdio/__snapshots__/reference/orion-fusion-theme/en/chrome_medium/compact-interactive-list-spec/fixed_width_columns.png new file mode 100644 index 00000000000..45610e8196e Binary files /dev/null and b/packages/terra-compact-interactive-list/tests/wdio/__snapshots__/reference/orion-fusion-theme/en/chrome_medium/compact-interactive-list-spec/fixed_width_columns.png differ diff --git a/packages/terra-compact-interactive-list/tests/wdio/__snapshots__/reference/orion-fusion-theme/en/chrome_medium/compact-interactive-list-spec/responsive_columns.png b/packages/terra-compact-interactive-list/tests/wdio/__snapshots__/reference/orion-fusion-theme/en/chrome_medium/compact-interactive-list-spec/responsive_columns.png new file mode 100644 index 00000000000..fa9f626e609 Binary files /dev/null and b/packages/terra-compact-interactive-list/tests/wdio/__snapshots__/reference/orion-fusion-theme/en/chrome_medium/compact-interactive-list-spec/responsive_columns.png differ diff --git a/packages/terra-compact-interactive-list/tests/wdio/__snapshots__/reference/orion-fusion-theme/en/chrome_medium/compact-interactive-list-spec/responsive_columns_with_max_width.png b/packages/terra-compact-interactive-list/tests/wdio/__snapshots__/reference/orion-fusion-theme/en/chrome_medium/compact-interactive-list-spec/responsive_columns_with_max_width.png new file mode 100644 index 00000000000..fbcc645607b Binary files /dev/null and b/packages/terra-compact-interactive-list/tests/wdio/__snapshots__/reference/orion-fusion-theme/en/chrome_medium/compact-interactive-list-spec/responsive_columns_with_max_width.png differ diff --git a/packages/terra-compact-interactive-list/tests/wdio/__snapshots__/reference/orion-fusion-theme/en/chrome_medium/compact-interactive-list-spec/scalable_units.png b/packages/terra-compact-interactive-list/tests/wdio/__snapshots__/reference/orion-fusion-theme/en/chrome_medium/compact-interactive-list-spec/scalable_units.png new file mode 100644 index 00000000000..e8582a740c8 Binary files /dev/null and b/packages/terra-compact-interactive-list/tests/wdio/__snapshots__/reference/orion-fusion-theme/en/chrome_medium/compact-interactive-list-spec/scalable_units.png differ diff --git a/packages/terra-compact-interactive-list/tests/wdio/__snapshots__/reference/orion-fusion-theme/en/chrome_medium/compact-interactive-list-spec/width_breakpoints.png b/packages/terra-compact-interactive-list/tests/wdio/__snapshots__/reference/orion-fusion-theme/en/chrome_medium/compact-interactive-list-spec/width_breakpoints.png new file mode 100644 index 00000000000..fa9f626e609 Binary files /dev/null and b/packages/terra-compact-interactive-list/tests/wdio/__snapshots__/reference/orion-fusion-theme/en/chrome_medium/compact-interactive-list-spec/width_breakpoints.png differ diff --git a/packages/terra-compact-interactive-list/tests/wdio/__snapshots__/reference/orion-fusion-theme/en/chrome_medium/compact-interactive-list-spec/width_breakpoints_horizontal_flow.png b/packages/terra-compact-interactive-list/tests/wdio/__snapshots__/reference/orion-fusion-theme/en/chrome_medium/compact-interactive-list-spec/width_breakpoints_horizontal_flow.png new file mode 100644 index 00000000000..e17525e72ee Binary files /dev/null and b/packages/terra-compact-interactive-list/tests/wdio/__snapshots__/reference/orion-fusion-theme/en/chrome_medium/compact-interactive-list-spec/width_breakpoints_horizontal_flow.png differ diff --git a/packages/terra-compact-interactive-list/tests/wdio/__snapshots__/reference/terra-default-theme/en/chrome_large/compact-interactive-list-spec/default_compact_interactive_list.png b/packages/terra-compact-interactive-list/tests/wdio/__snapshots__/reference/terra-default-theme/en/chrome_large/compact-interactive-list-spec/default_compact_interactive_list.png deleted file mode 100644 index 886015bb6bc..00000000000 Binary files a/packages/terra-compact-interactive-list/tests/wdio/__snapshots__/reference/terra-default-theme/en/chrome_large/compact-interactive-list-spec/default_compact_interactive_list.png and /dev/null differ diff --git a/packages/terra-compact-interactive-list/tests/wdio/__snapshots__/reference/terra-default-theme/en/chrome_large/compact-interactive-list-spec/fixed_width_columns.png b/packages/terra-compact-interactive-list/tests/wdio/__snapshots__/reference/terra-default-theme/en/chrome_large/compact-interactive-list-spec/fixed_width_columns.png new file mode 100644 index 00000000000..33555845c4b Binary files /dev/null and b/packages/terra-compact-interactive-list/tests/wdio/__snapshots__/reference/terra-default-theme/en/chrome_large/compact-interactive-list-spec/fixed_width_columns.png differ diff --git a/packages/terra-compact-interactive-list/tests/wdio/__snapshots__/reference/terra-default-theme/en/chrome_large/compact-interactive-list-spec/responsive_columns.png b/packages/terra-compact-interactive-list/tests/wdio/__snapshots__/reference/terra-default-theme/en/chrome_large/compact-interactive-list-spec/responsive_columns.png new file mode 100644 index 00000000000..c37fe56eb1f Binary files /dev/null and b/packages/terra-compact-interactive-list/tests/wdio/__snapshots__/reference/terra-default-theme/en/chrome_large/compact-interactive-list-spec/responsive_columns.png differ diff --git a/packages/terra-compact-interactive-list/tests/wdio/__snapshots__/reference/terra-default-theme/en/chrome_large/compact-interactive-list-spec/responsive_columns_with_max_width.png b/packages/terra-compact-interactive-list/tests/wdio/__snapshots__/reference/terra-default-theme/en/chrome_large/compact-interactive-list-spec/responsive_columns_with_max_width.png new file mode 100644 index 00000000000..a42a926fbf6 Binary files /dev/null and b/packages/terra-compact-interactive-list/tests/wdio/__snapshots__/reference/terra-default-theme/en/chrome_large/compact-interactive-list-spec/responsive_columns_with_max_width.png differ diff --git a/packages/terra-compact-interactive-list/tests/wdio/__snapshots__/reference/terra-default-theme/en/chrome_large/compact-interactive-list-spec/scalable_units.png b/packages/terra-compact-interactive-list/tests/wdio/__snapshots__/reference/terra-default-theme/en/chrome_large/compact-interactive-list-spec/scalable_units.png new file mode 100644 index 00000000000..fce8929ffca Binary files /dev/null and b/packages/terra-compact-interactive-list/tests/wdio/__snapshots__/reference/terra-default-theme/en/chrome_large/compact-interactive-list-spec/scalable_units.png differ diff --git a/packages/terra-compact-interactive-list/tests/wdio/__snapshots__/reference/terra-default-theme/en/chrome_large/compact-interactive-list-spec/width_breakpoints.png b/packages/terra-compact-interactive-list/tests/wdio/__snapshots__/reference/terra-default-theme/en/chrome_large/compact-interactive-list-spec/width_breakpoints.png new file mode 100644 index 00000000000..5762d826491 Binary files /dev/null and b/packages/terra-compact-interactive-list/tests/wdio/__snapshots__/reference/terra-default-theme/en/chrome_large/compact-interactive-list-spec/width_breakpoints.png differ diff --git a/packages/terra-compact-interactive-list/tests/wdio/__snapshots__/reference/terra-default-theme/en/chrome_large/compact-interactive-list-spec/width_breakpoints_horizontal_flow.png b/packages/terra-compact-interactive-list/tests/wdio/__snapshots__/reference/terra-default-theme/en/chrome_large/compact-interactive-list-spec/width_breakpoints_horizontal_flow.png new file mode 100644 index 00000000000..244daeeb72b Binary files /dev/null and b/packages/terra-compact-interactive-list/tests/wdio/__snapshots__/reference/terra-default-theme/en/chrome_large/compact-interactive-list-spec/width_breakpoints_horizontal_flow.png differ diff --git a/packages/terra-compact-interactive-list/tests/wdio/__snapshots__/reference/terra-default-theme/en/chrome_medium/compact-interactive-list-spec/default_compact_interactive_list.png b/packages/terra-compact-interactive-list/tests/wdio/__snapshots__/reference/terra-default-theme/en/chrome_medium/compact-interactive-list-spec/default_compact_interactive_list.png deleted file mode 100644 index 9a58b6aea87..00000000000 Binary files a/packages/terra-compact-interactive-list/tests/wdio/__snapshots__/reference/terra-default-theme/en/chrome_medium/compact-interactive-list-spec/default_compact_interactive_list.png and /dev/null differ diff --git a/packages/terra-compact-interactive-list/tests/wdio/__snapshots__/reference/terra-default-theme/en/chrome_medium/compact-interactive-list-spec/fixed_width_columns.png b/packages/terra-compact-interactive-list/tests/wdio/__snapshots__/reference/terra-default-theme/en/chrome_medium/compact-interactive-list-spec/fixed_width_columns.png new file mode 100644 index 00000000000..70ff36e5d6d Binary files /dev/null and b/packages/terra-compact-interactive-list/tests/wdio/__snapshots__/reference/terra-default-theme/en/chrome_medium/compact-interactive-list-spec/fixed_width_columns.png differ diff --git a/packages/terra-compact-interactive-list/tests/wdio/__snapshots__/reference/terra-default-theme/en/chrome_medium/compact-interactive-list-spec/responsive_columns.png b/packages/terra-compact-interactive-list/tests/wdio/__snapshots__/reference/terra-default-theme/en/chrome_medium/compact-interactive-list-spec/responsive_columns.png new file mode 100644 index 00000000000..10475ce02dc Binary files /dev/null and b/packages/terra-compact-interactive-list/tests/wdio/__snapshots__/reference/terra-default-theme/en/chrome_medium/compact-interactive-list-spec/responsive_columns.png differ diff --git a/packages/terra-compact-interactive-list/tests/wdio/__snapshots__/reference/terra-default-theme/en/chrome_medium/compact-interactive-list-spec/responsive_columns_with_max_width.png b/packages/terra-compact-interactive-list/tests/wdio/__snapshots__/reference/terra-default-theme/en/chrome_medium/compact-interactive-list-spec/responsive_columns_with_max_width.png new file mode 100644 index 00000000000..47db70162dd Binary files /dev/null and b/packages/terra-compact-interactive-list/tests/wdio/__snapshots__/reference/terra-default-theme/en/chrome_medium/compact-interactive-list-spec/responsive_columns_with_max_width.png differ diff --git a/packages/terra-compact-interactive-list/tests/wdio/__snapshots__/reference/terra-default-theme/en/chrome_medium/compact-interactive-list-spec/scalable_units.png b/packages/terra-compact-interactive-list/tests/wdio/__snapshots__/reference/terra-default-theme/en/chrome_medium/compact-interactive-list-spec/scalable_units.png new file mode 100644 index 00000000000..3e2c9f6acf5 Binary files /dev/null and b/packages/terra-compact-interactive-list/tests/wdio/__snapshots__/reference/terra-default-theme/en/chrome_medium/compact-interactive-list-spec/scalable_units.png differ diff --git a/packages/terra-compact-interactive-list/tests/wdio/__snapshots__/reference/terra-default-theme/en/chrome_medium/compact-interactive-list-spec/width_breakpoints.png b/packages/terra-compact-interactive-list/tests/wdio/__snapshots__/reference/terra-default-theme/en/chrome_medium/compact-interactive-list-spec/width_breakpoints.png new file mode 100644 index 00000000000..10475ce02dc Binary files /dev/null and b/packages/terra-compact-interactive-list/tests/wdio/__snapshots__/reference/terra-default-theme/en/chrome_medium/compact-interactive-list-spec/width_breakpoints.png differ diff --git a/packages/terra-compact-interactive-list/tests/wdio/__snapshots__/reference/terra-default-theme/en/chrome_medium/compact-interactive-list-spec/width_breakpoints_horizontal_flow.png b/packages/terra-compact-interactive-list/tests/wdio/__snapshots__/reference/terra-default-theme/en/chrome_medium/compact-interactive-list-spec/width_breakpoints_horizontal_flow.png new file mode 100644 index 00000000000..348b31d6499 Binary files /dev/null and b/packages/terra-compact-interactive-list/tests/wdio/__snapshots__/reference/terra-default-theme/en/chrome_medium/compact-interactive-list-spec/width_breakpoints_horizontal_flow.png differ diff --git a/packages/terra-compact-interactive-list/tests/wdio/compact-interactive-list-spec.js b/packages/terra-compact-interactive-list/tests/wdio/compact-interactive-list-spec.js index 41816680299..5f6e1e3c6f4 100644 --- a/packages/terra-compact-interactive-list/tests/wdio/compact-interactive-list-spec.js +++ b/packages/terra-compact-interactive-list/tests/wdio/compact-interactive-list-spec.js @@ -1,11 +1,61 @@ Terra.describeViewports('CompactInteractiveList', ['medium', 'large'], () => { - describe('placeholder', () => { + describe('fixed width columns', () => { beforeEach(() => { - browser.url('/raw/tests/cerner-terra-framework-docs/compact-interactive-list/default-compact-interactive-list'); + browser.url('/raw/tests/cerner-terra-framework-docs/compact-interactive-list/fixed-width-columns'); }); - it('placeholder', () => { - Terra.validates.element('default compact interactive list'); + it('should render correctly', () => { + Terra.validates.element('fixed width columns'); + }); + }); + + describe('responsive columns', () => { + beforeEach(() => { + browser.url('/raw/tests/cerner-terra-framework-docs/compact-interactive-list/responsive-columns'); + }); + + it('should render correctly', () => { + Terra.validates.element('responsive columns'); + }); + }); + + describe('responsive columns with max width', () => { + beforeEach(() => { + browser.url('/raw/tests/cerner-terra-framework-docs/compact-interactive-list/responsive-columns-max-width'); + }); + + it('should render correctly', () => { + Terra.validates.element('responsive columns with max width'); + }); + }); + + describe('scalable units', () => { + beforeEach(() => { + browser.url('/raw/tests/cerner-terra-framework-docs/compact-interactive-list/scalable-units'); + }); + + it('should render correctly', () => { + Terra.validates.element('scalable units'); + }); + }); + + describe('width breakpoints', () => { + beforeEach(() => { + browser.url('/raw/tests/cerner-terra-framework-docs/compact-interactive-list/width-breakpoints'); + }); + + it('should render correctly', () => { + Terra.validates.element('width breakpoints'); + }); + }); + + describe('horizontal flow', () => { + beforeEach(() => { + browser.url('/raw/tests/cerner-terra-framework-docs/compact-interactive-list/width-breakpoints-horizontal-flow'); + }); + + it('should render correctly', () => { + Terra.validates.element('width breakpoints horizontal flow'); }); }); }); diff --git a/packages/terra-data-grid/CHANGELOG.md b/packages/terra-data-grid/CHANGELOG.md index 227022b5e39..f9998f994fd 100644 --- a/packages/terra-data-grid/CHANGELOG.md +++ b/packages/terra-data-grid/CHANGELOG.md @@ -10,6 +10,7 @@ Changed * Fixed * Fixed issue where focus was given to the column header instead of its button element. * Fixed issue where row selection was being announced twice in Worklist Data Grid. + * Fixed an issue when the datagrid is updated and the column/row indices no longer exist. * Added * Added `hasVisibleColumnHeaders` prop for FlowsheetDataGrid to toggle visibility of column headers. diff --git a/packages/terra-data-grid/src/DataGrid.jsx b/packages/terra-data-grid/src/DataGrid.jsx index 1518a45d4bf..523fa3975b7 100644 --- a/packages/terra-data-grid/src/DataGrid.jsx +++ b/packages/terra-data-grid/src/DataGrid.jsx @@ -195,6 +195,8 @@ const DataGrid = forwardRef((props, ref) => { const tableContainerRef = useRef(); const handleFocus = useRef(true); + const focusedCellRef = useRef({ rowId: '', columnId: '' }); + const [checkResizable, setCheckResizable] = useState(false); // if columns are not visible then set the first selectable row index to 1 @@ -215,15 +217,20 @@ const DataGrid = forwardRef((props, ref) => { // ------------------------------------- // functions - const isRowSelectionCell = (columnIndex) => ( + const isRowSelectionCell = useCallback((columnIndex) => ( hasSelectableRows && columnIndex < displayedColumns.length && displayedColumns[columnIndex].id === WorklistDataGridUtils.ROW_SELECTION_COLUMN.id - ); + ), [displayedColumns, hasSelectableRows]); - const setFocusedRowCol = (newRowIndex, newColIndex, makeActiveElement) => { + const setFocusedRowCol = useCallback((newRowIndex, newColIndex, makeActiveElement) => { setCellAriaLiveMessage(null); setFocusedRow(newRowIndex); setFocusedCol(newColIndex); + focusedCellRef.current = { + rowId: grid.current.rows[newRowIndex].getAttribute('data-row-id'), + columnId: displayedColumns[newColIndex].id, + }; + if (makeActiveElement) { // Set focus on input field (checkbox) of row selection cells. let focusedCell = grid.current.rows[newRowIndex].cells[newColIndex]; @@ -238,7 +245,7 @@ const DataGrid = forwardRef((props, ref) => { focusedCell?.focus(); } - }; + }, [isRowSelectionCell, displayedColumns]); // The focus is handled by the DataGrid. However, there are times // when the other components may want to change the currently focus @@ -295,28 +302,27 @@ const DataGrid = forwardRef((props, ref) => { const handleColumnSelect = useCallback((columnId) => { const columnIndex = displayedColumns.findIndex(column => column.id === columnId); - setFocusedCol(columnIndex); + setFocusedRowCol(0, columnIndex); if (onColumnSelect) { onColumnSelect(columnId); } - }, [onColumnSelect, displayedColumns]); + }, [onColumnSelect, displayedColumns, setFocusedRowCol]); const handleRowSelectionHeaderSelect = useCallback(() => { - setFocusedCol(0); - setFocusedRow(0); + setFocusedRowCol(0, 0); if (onRowSelectionHeaderSelect) { onRowSelectionHeaderSelect(); } - }, [onRowSelectionHeaderSelect]); + }, [onRowSelectionHeaderSelect, setFocusedRowCol]); const handleCellSelection = useCallback((selectionDetails) => { - setFocusedRow(selectionDetails.rowIndex); - setFocusedCol(selectionDetails.columnIndex); + const { columnIndex, rowIndex } = selectionDetails; + setFocusedRowCol(rowIndex, columnIndex); if (onCellSelect) { onCellSelect(selectionDetails); } - }, [onCellSelect]); + }, [onCellSelect, setFocusedRowCol]); // ------------------------------------- // event handlers @@ -478,6 +484,7 @@ const DataGrid = forwardRef((props, ref) => { event.preventDefault(); // prevent the page from moving with the arrow keys. return; } + handleMoveCellFocus(cellCoordinates, { row: nextRow, col: nextCol }); event.preventDefault(); // prevent the page from moving with the arrow keys. }; @@ -495,7 +502,26 @@ const DataGrid = forwardRef((props, ref) => { if (!event.currentTarget.contains(event.relatedTarget)) { // Not triggered when swapping focus between children if (handleFocus.current) { - setFocusedRowCol(focusedRow, focusedCol, true); + let newRowIndex = focusedRow; + let newColumnIndex = focusedCol; + + // Check for last focused row ID. If found set the index. Otherwise set it to the last focused row or last index. + if (focusedCellRef.current.rowId) { + newRowIndex = [...grid.current.rows].findIndex(row => row.getAttribute('data-row-id') === focusedCellRef.current.rowId); + newRowIndex = newRowIndex === -1 + ? Math.min(focusedRow, grid.current.rows.length - 1) + : newRowIndex; + } + + // Check for last focused column ID. If found set the index. Otherwise set it to the last focused column or last index. + if (focusedCellRef.current.columnId) { + newColumnIndex = displayedColumns.findIndex(column => column.id === focusedCellRef.current.columnId); + newColumnIndex = newColumnIndex === -1 + ? Math.min(focusedCol, displayedColumns.length - 1) + : newColumnIndex; + } + + setFocusedRowCol(newRowIndex, newColumnIndex, true); setGridHasFocus(true); } } @@ -555,7 +581,7 @@ const DataGrid = forwardRef((props, ref) => { ); }); -DataGrid.propTypes = propTypes; DataGrid.defaultProps = defaultProps; +DataGrid.propTypes = propTypes; export default DataGrid; 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 dd8846cfe10..734f1e7ba4a 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 @@ -505,7 +505,9 @@ exports[`DataGrid verifies onCellSelect callback is triggered when space is pres > { }); }); + describe('row and column deletion', () => { + const deleteRowsSelector = '#worklist-data-grid-delete-rows'; + beforeEach(() => { + browser.url('/raw/tests/cerner-terra-framework-docs/data-grid/worklist-data-grid/worklist-data-grid-delete-rows-and-columns'); + }); + + it('retains the last selected row index when filtering rows', () => { + clickCell(2, 1, deleteRowsSelector); + clickCell(8, 1, deleteRowsSelector); + clickCell(5, 1, deleteRowsSelector); + + browser.$('#filter-rows-button').click(); + browser.keys(['Tab']); + + expect(browser.$('tbody tr:nth-child(2) th').isFocused()).toBe(true); + }); + + it('uses the last focused row when the row is deleted and the index is not out of bounds after deletion', () => { + clickCell(1, 1, deleteRowsSelector); + holdDownShiftKey(); + clickCell(3, 1, deleteRowsSelector); + + browser.$('#delete-rows-button').click(); + browser.keys(['Tab', 'Tab']); + + expect(browser.$('tbody tr:nth-child(3) th').isFocused()).toBe(true); + }); + + it('focuses the last row when selected and the index is out of bounds after deletion', () => { + clickCell(5, 1, deleteRowsSelector); + holdDownShiftKey(); + clickCell(10, 1, deleteRowsSelector); + + browser.$('#delete-rows-button').click(); + browser.keys(['Tab', 'Tab']); + expect(browser.$('tbody tr:last-child th').isFocused()).toBe(true); + }); + + it('focuses on the last column when selected and the index is out of bounds', () => { + clickCell(0, 2, deleteRowsSelector); + clickCell(0, 2, deleteRowsSelector); + clickCell(0, 2, deleteRowsSelector); + + browser.keys(['Shift', 'Tab']); + expect(browser.$('thead th:nth-child(2)').isFocused()).toBe(true); + }); + }); + describe('with pinned columns', () => { const pinnedColumnsSelector = '#pinned-columns-table'; const pinnedColumnsWithRowSelectionSelector = '#pinned-columns-with-row-selection-table'; diff --git a/packages/terra-framework-docs/CHANGELOG.md b/packages/terra-framework-docs/CHANGELOG.md index 782e87a48a5..fa9a776bf7b 100644 --- a/packages/terra-framework-docs/CHANGELOG.md +++ b/packages/terra-framework-docs/CHANGELOG.md @@ -2,6 +2,9 @@ ## Unreleased +* Added + * Added examples and tests to `terra-table` for single row selection mode. + ## 1.45.0 - (November 20, 2023) * Added @@ -9,12 +12,13 @@ * Added new test for Terra Slider component for long field labels. * Added `hasVisibleColumnHeaders` example for FlowsheetDataGrid. * Added examples and tests to `terra-data-grid` for sections in FlowsheetDataGrid. - * Added examples and tests to `terra-table` for single row selection mode. + * Added documentation and examples for `terra-compact-interactive-list`. * Updated * Updated About page of `terra-table` to provide accessibility documentation for Home and End keys. * Removed alpha notice for FlowsheetDataGrid. * Removed unsupported props `isMasked` and `isSelectable` from FlowsheetDataGrid subcomponent documentation. + * Updated examples and tests in `terra-table` to validate sections in a table with pinned columns. ## 1.44.0 - (November 9, 2023) diff --git a/packages/terra-framework-docs/package.json b/packages/terra-framework-docs/package.json index 41176212c52..b8cf9371f33 100644 --- a/packages/terra-framework-docs/package.json +++ b/packages/terra-framework-docs/package.json @@ -59,6 +59,7 @@ "terra-form-select": "^6.8.0", "terra-grid": "^6.0.0", "terra-hookshot": "^5.41.0", + "terra-icon": "^3.58.0", "terra-infinite-list": "^3.42.0", "terra-layout": "^4.38.0", "terra-menu": "^6.80.0", diff --git a/packages/terra-framework-docs/src/terra-dev-site/doc/compact-interactive-list/About.1.doc.mdx b/packages/terra-framework-docs/src/terra-dev-site/doc/compact-interactive-list/About.1.doc.mdx index c5bcadda86f..2efecf9d89a 100644 --- a/packages/terra-framework-docs/src/terra-dev-site/doc/compact-interactive-list/About.1.doc.mdx +++ b/packages/terra-framework-docs/src/terra-dev-site/doc/compact-interactive-list/About.1.doc.mdx @@ -3,8 +3,6 @@ import { Badge } from 'terra-file-path/package.json?dev-site-package'; import CompactInteractiveListPropsTable from 'terra-compact-interactive-list/src/CompactInteractiveList?dev-site-props-table'; -import DefaultCompactInteractiveList from './Examples.2/DefaultCompactInteractiveList?dev-site-example'; - # Terra CompactInteractiveList @@ -38,8 +36,5 @@ This component requires the following peer dependencies be installed in your app * [Internationalization Support](https://engineering.cerner.com/terra-ui/about/terra-ui/component-standards#internationalization-i18n) * [LTR/RTL Support](https://engineering.cerner.com/terra-ui/about/terra-ui/component-standards#ltr--rtl) -## Examples - - ## Compact Interactive List Props Table diff --git a/packages/terra-framework-docs/src/terra-dev-site/doc/compact-interactive-list/Examples.2.doc.mdx b/packages/terra-framework-docs/src/terra-dev-site/doc/compact-interactive-list/Examples.2.doc.mdx new file mode 100644 index 00000000000..476ab8be578 --- /dev/null +++ b/packages/terra-framework-docs/src/terra-dev-site/doc/compact-interactive-list/Examples.2.doc.mdx @@ -0,0 +1,64 @@ +import FixedWidthColumns from './Examples/FixedWidthColumns?dev-site-examples'; +import ScalableUnits from './Examples/ScalableUnits?dev-site-examples'; +import WidthBreakpoints from './Examples/WidthBreakpoints?dev-site-examples'; +import ResponsiveColumns from './Examples/ResponsiveColumns?dev-site-examples'; +import ResponsiveColumnsMaxWidth from './Examples/ResponsiveColumnsMaxWidth?dev-site-examples'; +import HorizontalFlow from './Examples/HorizontalFlow?dev-site-examples'; +import VerticalFlow from './Examples/VerticalFlow?dev-site-examples'; + +# Compact Interactive List examples + +## Fix width columns + +In this example all semantic columns have `width` set to a number. + +* The semantic columns (and hence the visual columns) will always stay the same width and would not grow bigger or shrink to fill up the container width. +* If the screen width is less than the list container, the horizontal scroll will be added. + + + +## Responsive columns + +There are two ways to make a sematic column grow: + +* Omitting the `width` in column props will make the semantic column grow to take all available space. +* Setting the `flexGrow` prop to true with the `width` set to a number of width units (such as `px`, `em`, or `rem`) will also make the column size responsive. In this case the width will be used as a flex basis. + +In the following example the middle column has `flexGrow: true` and grows wide to take all width: + + + +To control the minimum and maximum width of flexible width columns, `columnMaximumWidth` and `columnMinimumWidth` props can be determined on a column level. +The whole list has a defaul minimumWidth. Alternatively `minimumWidth` can be passed as a prop. + +In following example the middle column has `maximumWidth: 300px` and `minimumWidth: 100px`. + +* Once the minimum width has been reached, the horisontal scroll would show up. +* Once the maximum width has been reached, the row stops growing. + + + +## Use width breakpoints to dynamically adjust the number of columns to the screen width + +Another way to control the Compact Interactive List column width is to make its parent component take charge of the number of list columns. +Change the width of the screen to see the number of columns change and the width adjust. + + + +## Horizontal Flow + +By default the items in a list go from top to bottom before breaking into a new visual column: + + + +Use `flowHorizontally` prop to change the direction. Items will go from left to right before breaking into a new line: + + + +## Use scalable units + +The `widthUnit` prop determines the width unit type, such as `px`, `em`, or `rem`. +Check out the example below to see how the same list looks like when placed within a container with `fontSize: 1.25em` and `widthUnit` prop set to `em`: + + + diff --git a/packages/terra-framework-docs/src/terra-dev-site/doc/compact-interactive-list/Examples.2/DefaultCompactInteractiveList.1.doc.mdx b/packages/terra-framework-docs/src/terra-dev-site/doc/compact-interactive-list/Examples.2/DefaultCompactInteractiveList.1.doc.mdx deleted file mode 100644 index 75df6c0f6f9..00000000000 --- a/packages/terra-framework-docs/src/terra-dev-site/doc/compact-interactive-list/Examples.2/DefaultCompactInteractiveList.1.doc.mdx +++ /dev/null @@ -1,3 +0,0 @@ -import DefaultCompactInteractiveList from './DefaultCompactInteractiveList?dev-site-example'; - - diff --git a/packages/terra-framework-docs/src/terra-dev-site/doc/compact-interactive-list/Examples.2/DefaultCompactInteractiveList.jsx b/packages/terra-framework-docs/src/terra-dev-site/doc/compact-interactive-list/Examples.2/DefaultCompactInteractiveList.jsx deleted file mode 100644 index 000e1ba3ea3..00000000000 --- a/packages/terra-framework-docs/src/terra-dev-site/doc/compact-interactive-list/Examples.2/DefaultCompactInteractiveList.jsx +++ /dev/null @@ -1,8 +0,0 @@ -import React from 'react'; -import CompactInteractiveList from 'terra-compact-interactive-list'; - -const DefaultCompactInteractiveList = () => ( - -); - -export default DefaultCompactInteractiveList; diff --git a/packages/terra-framework-docs/src/terra-dev-site/doc/compact-interactive-list/Examples/FixedWidthColumns.jsx b/packages/terra-framework-docs/src/terra-dev-site/doc/compact-interactive-list/Examples/FixedWidthColumns.jsx new file mode 100644 index 00000000000..c84062ba963 --- /dev/null +++ b/packages/terra-framework-docs/src/terra-dev-site/doc/compact-interactive-list/Examples/FixedWidthColumns.jsx @@ -0,0 +1,78 @@ +import React from 'react'; +import CompactInteractiveList, { alignTypes } from 'terra-compact-interactive-list'; +import { IconDocuments, IconFeaturedOutline, IconImage } from 'terra-icon'; + +// Source rows data for tests +const rows = [ + { + id: 'row_1', + cells: [ + { content: }, + { content: 'Discern Care Set (1)' }, + { content: }, + ], + }, + { + id: 'row_2', + cells: [ + { content: }, + { content: 'Initial observation Care/Day High Severity 99220 (2)' }, + { content: }, + ], + }, + { + id: 'row_3', + cells: [ + { content: }, + { content: 'Arterial Sheath Care (3)' }, + { content: }, + ], + }, + { + id: 'row_4', + cells: [ + { content: ' ' }, + { content: 'Sbsq Observation Care/Day High Severity 99226 (4)' }, + { content: ' ' }, + ], + }, + { + id: 'row_5', + cells: [ + { content: }, + { content: 'Arterial Sheath Care (5)' }, + { content: }, + ], + }, +]; + +const cols = [ + { + id: 'Column-0', + displayName: 'Col_1', + width: '5em', + align: alignTypes.CENTER, + }, + { + id: 'Column-1', + displayName: 'Col_2', + width: '20em', + }, + { + id: 'Column-2', + displayName: 'Col_3', + width: '5em', + align: alignTypes.CENTER, + }, +]; + +const FixedWidthColumns = () => ( + +); + +export default FixedWidthColumns; diff --git a/packages/terra-framework-docs/src/terra-dev-site/doc/compact-interactive-list/Examples/HorizontalFlow.jsx b/packages/terra-framework-docs/src/terra-dev-site/doc/compact-interactive-list/Examples/HorizontalFlow.jsx new file mode 100644 index 00000000000..d19c7adf5c4 --- /dev/null +++ b/packages/terra-framework-docs/src/terra-dev-site/doc/compact-interactive-list/Examples/HorizontalFlow.jsx @@ -0,0 +1,95 @@ +import React from 'react'; +import CompactInteractiveList, { alignTypes } from 'terra-compact-interactive-list'; +import { ActiveBreakpointContext } from 'terra-breakpoints'; +import { IconDocuments, IconFeaturedOutline, IconImage } from 'terra-icon'; + +const getNumberOfColumns = (activeBreakpoint) => { + switch (activeBreakpoint) { + case 'enormous': return 4; + case 'huge': return 3; + case 'large': return 2; + case 'medium': return 2; + case 'small': return 1; + case 'tiny': return 1; + default: return 1; + } +}; + +const rows = [ + { + id: 'row_1', + cells: [ + { content: }, + { content: 'Discern Care Set (1)' }, + { content: }, + ], + }, + { + id: 'row_2', + cells: [ + { content: }, + { content: 'Initial observation Care/Day High Severity 99220 (2)' }, + { content: }, + ], + }, + { + id: 'row_3', + cells: [ + { content: }, + { content: 'Arterial Sheath Care (3)' }, + { content: }, + ], + }, + { + id: 'row_4', + cells: [ + { content: ' ' }, + { content: 'Sbsq Observation Care/Day High Severity 99226 (4)' }, + { content: ' ' }, + ], + }, + { + id: 'row_5', + cells: [ + { content: }, + { content: 'Arterial Sheath Care (5)' }, + { content: }, + ], + }, +]; + +const cols = [ + { + id: 'Column-0', + displayName: 'Col_1', + width: '60px', + align: alignTypes.CENTER, + }, + { + id: 'Column-1', + displayName: 'Col_2', + width: '200px', // will be disregarded as flexGrow prop set to true will make it flex growing column + flexGrow: true, + }, + { + id: 'Column-2', + displayName: 'Col_3', + width: '60px', + align: alignTypes.CENTER, + }, +]; + +const HorizontalFlow = () => { + const activeBreakpoint = React.useContext(ActiveBreakpointContext); + return ( + + ); +}; + +export default HorizontalFlow; diff --git a/packages/terra-framework-docs/src/terra-dev-site/doc/compact-interactive-list/Examples/ResponsiveColumns.jsx b/packages/terra-framework-docs/src/terra-dev-site/doc/compact-interactive-list/Examples/ResponsiveColumns.jsx new file mode 100644 index 00000000000..aa415082aa1 --- /dev/null +++ b/packages/terra-framework-docs/src/terra-dev-site/doc/compact-interactive-list/Examples/ResponsiveColumns.jsx @@ -0,0 +1,78 @@ +import React from 'react'; +import CompactInteractiveList, { alignTypes } from 'terra-compact-interactive-list'; +import { IconDocuments, IconFeaturedOutline, IconImage } from 'terra-icon'; + +const rows = [ + { + id: 'row_1', + cells: [ + { content: }, + { content: 'Discern Care Set (1)' }, + { content: }, + ], + }, + { + id: 'row_2', + cells: [ + { content: }, + { content: 'Initial observation Care/Day High Severity 99220 (2)' }, + { content: }, + ], + }, + { + id: 'row_3', + cells: [ + { content: }, + { content: 'Arterial Sheath Care (3)' }, + { content: }, + ], + }, + { + id: 'row_4', + cells: [ + { content: ' ' }, + { content: 'Sbsq Observation Care/Day High Severity 99226 (4)' }, + { content: ' ' }, + ], + }, + { + id: 'row_5', + cells: [ + { content: }, + { content: 'Arterial Sheath Care (5)' }, + { content: }, + ], + }, +]; + +const cols = [ + { + id: 'Column-0', + displayName: 'Col_1', + width: '60px', + align: alignTypes.CENTER, + }, + { + id: 'Column-1', + displayName: 'Col_2', + width: '200px', // will be disregarded because of the flexGrow prop + flexGrow: true, // flexGrow prop has to be set here, as with width set, without that prop the column would not grow. + }, + { + id: 'Column-2', + displayName: 'Col_3', + width: '60px', + align: alignTypes.CENTER, + }, +]; + +const ResponsiveColumns = () => ( + +); + +export default ResponsiveColumns; diff --git a/packages/terra-framework-docs/src/terra-dev-site/doc/compact-interactive-list/Examples/ResponsiveColumnsMaxWidth.jsx b/packages/terra-framework-docs/src/terra-dev-site/doc/compact-interactive-list/Examples/ResponsiveColumnsMaxWidth.jsx new file mode 100644 index 00000000000..adf4a048dea --- /dev/null +++ b/packages/terra-framework-docs/src/terra-dev-site/doc/compact-interactive-list/Examples/ResponsiveColumnsMaxWidth.jsx @@ -0,0 +1,81 @@ +import React from 'react'; +import CompactInteractiveList, { alignTypes } from 'terra-compact-interactive-list'; +import { IconDocuments, IconFeaturedOutline, IconImage } from 'terra-icon'; + +const rows = [ + { + id: 'row_1', + cells: [ + { content: }, + { content: 'Discern Care Set (1)' }, + { content: }, + ], + }, + { + id: 'row_2', + cells: [ + { content: }, + { content: 'Initial observation Care/Day High Severity 99220 (2)' }, + { content: }, + ], + }, + { + id: 'row_3', + cells: [ + { content: }, + { content: 'Arterial Sheath Care (3)' }, + { content: }, + ], + }, + { + id: 'row_4', + cells: [ + { content: ' ' }, + { content: 'Sbsq Observation Care/Day High Severity 99226 (4)' }, + { content: ' ' }, + ], + }, + { + id: 'row_5', + cells: [ + { content: }, + { content: 'Arterial Sheath Care (5)' }, + { content: }, + ], + }, +]; + +const cols = [ + { + id: 'Column-0', + displayName: 'Col_1', + width: '60px', // there is no widthUnit prop set in the component, so it will default to px + align: alignTypes.CENTER, + }, + { + id: 'Column-1', + displayName: 'Col_2', + width: '200px', // will be disregarded because of flexGrow prop + flexGrow: true, // flexGrow prop has to be set here, as with width set, without that prop the column would not grow. + maximumWidth: '300px', + minimumWidth: '100px', + }, + { + id: 'Column-2', + displayName: 'Col_3', + width: '60px', // there is no widthUnit prop set in the component, so it will default to px + align: alignTypes.CENTER, + }, +]; + +const ResponsiveColumnsMaxWidth = () => ( + +); + +export default ResponsiveColumnsMaxWidth; diff --git a/packages/terra-framework-docs/src/terra-dev-site/doc/compact-interactive-list/Examples/ScalableUnits.jsx b/packages/terra-framework-docs/src/terra-dev-site/doc/compact-interactive-list/Examples/ScalableUnits.jsx new file mode 100644 index 00000000000..0a93c6c9e98 --- /dev/null +++ b/packages/terra-framework-docs/src/terra-dev-site/doc/compact-interactive-list/Examples/ScalableUnits.jsx @@ -0,0 +1,82 @@ +/* eslint-disable react/forbid-dom-props */ +import React from 'react'; +import CompactInteractiveList, { alignTypes } from 'terra-compact-interactive-list'; +import { IconDocuments, IconFeaturedOutline, IconImage } from 'terra-icon'; + +const rows = [ + { + id: 'row_1', + cells: [ + { content: }, + { content: 'Discern Care Set (1)' }, + { content: }, + ], + }, + { + id: 'row_2', + cells: [ + { content: }, + { content: 'Initial observation Care/Day High Severity 99220 (2)' }, + { content: }, + ], + }, + { + id: 'row_3', + cells: [ + { content: }, + { content: 'Arterial Sheath Care (3)' }, + { content: }, + ], + }, + { + id: 'row_4', + cells: [ + { content: ' ' }, + { content: 'Sbsq Observation Care/Day High Severity 99226 (4)' }, + { content: ' ' }, + ], + }, + { + id: 'row_5', + cells: [ + { content: }, + { content: 'Arterial Sheath Care (5)' }, + { content: }, + ], + }, +]; + +const cols = [ + { + id: 'Column-0', + displayName: 'Col_1', + width: '5em', + align: alignTypes.CENTER, + }, + { + id: 'Column-1', + displayName: 'Col_2', + // flexGrow prop is not set here, but as there is no width set, the column will be flex growing anyway + maximumWidth: '25em', // maximumWidth is reccomended for flex growing columns so that they don't grow beyond reasonable. + minimumWidth: '7em', // minimumWidth is reccomended for flex growing columns so that they don't squish beyond reasonable. + }, + { + id: 'Column-2', + displayName: 'Col_3', + width: '5em', + align: alignTypes.CENTER, + }, +]; + +const ScalableUnits = () => ( +
+ +
+); + +export default ScalableUnits; diff --git a/packages/terra-framework-docs/src/terra-dev-site/doc/compact-interactive-list/Examples/VerticalFlow.jsx b/packages/terra-framework-docs/src/terra-dev-site/doc/compact-interactive-list/Examples/VerticalFlow.jsx new file mode 100644 index 00000000000..5d817fb4208 --- /dev/null +++ b/packages/terra-framework-docs/src/terra-dev-site/doc/compact-interactive-list/Examples/VerticalFlow.jsx @@ -0,0 +1,107 @@ +import React from 'react'; +import CompactInteractiveList, { alignTypes } from 'terra-compact-interactive-list'; +import { ActiveBreakpointContext } from 'terra-breakpoints'; +import { IconDocuments, IconFeaturedOutline, IconImage } from 'terra-icon'; + +const rows = [ + { + id: 'row_1', + cells: [ + { content: }, + { content: 'Discern Care Set (1)' }, + { content: }, + ], + }, + { + id: 'row_2', + cells: [ + { content: }, + { content: 'Initial observation Care/Day High Severity 99220 (2)' }, + { content: }, + ], + }, + { + id: 'row_3', + cells: [ + { content: }, + { content: 'Arterial Sheath Care (3)' }, + { content: }, + ], + }, + { + id: 'row_4', + cells: [ + { content: ' ' }, + { content: 'Sbsq Observation Care/Day High Severity 99226 (4)' }, + { content: ' ' }, + ], + }, + { + id: 'row_5', + cells: [ + { content: }, + { content: 'Arterial Sheath Care (5)' }, + { content: }, + ], + }, +]; + +const getNumberOfColumns = (activeBreakpoint) => { + switch (activeBreakpoint) { + case 'enormous': return 4; + case 'huge': return 3; + case 'large': return 2; + case 'medium': return 2; + case 'small': return 1; + case 'tiny': return 1; + default: return 1; + } +}; + +const getRowHeight = (activeBreakpoint) => { + switch (activeBreakpoint) { + case 'enormous': return '60px'; + case 'huge': return '60px'; + case 'large': return '60px'; + case 'medium': return '60px'; + case 'small': return '40px'; + case 'tiny': return '40px'; + default: return '40px'; + } +}; + +const cols = [ + { + id: 'Column-0', + displayName: 'Col_1', + width: '60px', + align: alignTypes.CENTER, + }, + { + id: 'Column-1', + displayName: 'Col_2', + width: '200px', // will be disregarded bacause of flexGrow prop. + flexGrow: true, // flexGrow prop has to be set here, as with width set, without that prop the column would not grow. + }, + { + id: 'Column-2', + displayName: 'Col_3', + width: '60px', // there is no widthUnit prop set in the component, so it will default to px + align: alignTypes.CENTER, + }, +]; + +const VerticalFlow = () => { + const activeBreakpoint = React.useContext(ActiveBreakpointContext); + return ( + + ); +}; + +export default VerticalFlow; diff --git a/packages/terra-framework-docs/src/terra-dev-site/doc/compact-interactive-list/Examples/WidthBreakpoints.jsx b/packages/terra-framework-docs/src/terra-dev-site/doc/compact-interactive-list/Examples/WidthBreakpoints.jsx new file mode 100644 index 00000000000..164ec157372 --- /dev/null +++ b/packages/terra-framework-docs/src/terra-dev-site/doc/compact-interactive-list/Examples/WidthBreakpoints.jsx @@ -0,0 +1,95 @@ +import React from 'react'; +import CompactInteractiveList, { alignTypes } from 'terra-compact-interactive-list'; +import { ActiveBreakpointContext } from 'terra-breakpoints'; +import { IconDocuments, IconFeaturedOutline, IconImage } from 'terra-icon'; + +const rows = [ + { + id: 'row_1', + cells: [ + { content: }, + { content: 'Discern Care Set (1)' }, + { content: }, + ], + }, + { + id: 'row_2', + cells: [ + { content: }, + { content: 'Initial observation Care/Day High Severity 99220 (2)' }, + { content: }, + ], + }, + { + id: 'row_3', + cells: [ + { content: }, + { content: 'Arterial Sheath Care (3)' }, + { content: }, + ], + }, + { + id: 'row_4', + cells: [ + { content: ' ' }, + { content: 'Sbsq Observation Care/Day High Severity 99226 (4)' }, + { content: ' ' }, + ], + }, + { + id: 'row_5', + cells: [ + { content: }, + { content: 'Arterial Sheath Care (5)' }, + { content: }, + ], + }, +]; + +const getNumberOfColumns = (activeBreakpoint) => { + switch (activeBreakpoint) { + case 'enormous': return 5; + case 'huge': return 4; + case 'large': return 3; + case 'medium': return 2; + case 'small': return 1; + case 'tiny': return 1; + default: return 1; + } +}; + +const cols = [ + { + id: 'Column-0', + displayName: 'Col_1', + width: '60px', + align: alignTypes.CENTER, + }, + { + id: 'Column-1', + displayName: 'Col_2', + width: '200px', // will be disregarded because of flexGrow prop. + flexGrow: true, // flexGrow prop has to be set here, as with width set, without that prop the column would not grow. + }, + { + id: 'Column-2', + displayName: 'Col_3', + width: '60px', + align: alignTypes.CENTER, + }, +]; + +const WidthBreakpoints = () => { + const activeBreakpoint = React.useContext(ActiveBreakpointContext); + return ( + + ); +}; + +export default WidthBreakpoints; diff --git a/packages/terra-framework-docs/src/terra-dev-site/doc/table/Examples/TableWithSections.jsx b/packages/terra-framework-docs/src/terra-dev-site/doc/table/Examples/TableWithSections.jsx index e2b91aa7ab1..7455f3624e3 100644 --- a/packages/terra-framework-docs/src/terra-dev-site/doc/table/Examples/TableWithSections.jsx +++ b/packages/terra-framework-docs/src/terra-dev-site/doc/table/Examples/TableWithSections.jsx @@ -90,7 +90,8 @@ const TableWithSections = () => { return ( ( - -); - -export default DefaultCompactInteractiveList; diff --git a/packages/terra-framework-docs/src/terra-dev-site/test/compact-interactive-list/FixedWidthColumns.test.jsx b/packages/terra-framework-docs/src/terra-dev-site/test/compact-interactive-list/FixedWidthColumns.test.jsx new file mode 100644 index 00000000000..65696b9d594 --- /dev/null +++ b/packages/terra-framework-docs/src/terra-dev-site/test/compact-interactive-list/FixedWidthColumns.test.jsx @@ -0,0 +1,81 @@ +import React from 'react'; +import CompactInteractiveList, { alignTypes } from 'terra-compact-interactive-list'; +import { IconDocuments, IconFeaturedOutline, IconImage } from 'terra-icon'; + +// Source data for tests + +const rows = [ + { + id: 'row_1', + cells: [ + { content: }, + { content: 'Discern Care Set (1)' }, + { content: }, + ], + }, + { + id: 'row_2', + cells: [ + { content: }, + { content: 'Initial observation Care/Day High Severity 99220 (2)' }, + { content: }, + ], + }, + { + id: 'row_3', + cells: [ + { content: }, + { content: 'Arterial Sheath Care (3)' }, + { content: }, + ], + }, + { + id: 'row_4', + cells: [ + { content: ' ' }, + { content: 'Sbsq Observation Care/Day High Severity 99226 (4)' }, + { content: ' ' }, + ], + }, + { + id: 'row_5', + cells: [ + { content: }, + { content: 'Arterial Sheath Care (5)' }, + { content: }, + ], + }, +]; + +const cols = [ + { + id: 'Column-0', + displayName: 'Col_1', + width: '60px', + align: alignTypes.CENTER, + }, + { + id: 'Column-1', + displayName: 'Col_2', + width: '350px', + }, + { + id: 'Column-2', + displayName: 'Col_3', + width: '60px', + align: alignTypes.CENTER, + }, +]; + +const FixedWidthColumns = () => ( + +); + +export default FixedWidthColumns; diff --git a/packages/terra-framework-docs/src/terra-dev-site/test/compact-interactive-list/ResponsiveColumns.test.jsx b/packages/terra-framework-docs/src/terra-dev-site/test/compact-interactive-list/ResponsiveColumns.test.jsx new file mode 100644 index 00000000000..f7b84bc32dd --- /dev/null +++ b/packages/terra-framework-docs/src/terra-dev-site/test/compact-interactive-list/ResponsiveColumns.test.jsx @@ -0,0 +1,79 @@ +import React from 'react'; +import CompactInteractiveList, { alignTypes } from 'terra-compact-interactive-list'; +import { IconDocuments, IconFeaturedOutline, IconImage } from 'terra-icon'; + +// Source data for tests + +const rows = [ + { + id: 'row_1', + cells: [ + { content: }, + { content: 'Discern Care Set (1)' }, + { content: }, + ], + }, + { + id: 'row_2', + cells: [ + { content: }, + { content: 'Initial observation Care/Day High Severity 99220 (2)' }, + { content: }, + ], + }, + { + id: 'row_3', + cells: [ + { content: }, + { content: 'Arterial Sheath Care (3)' }, + { content: }, + ], + }, + { + id: 'row_4', + cells: [ + { content: ' ' }, + { content: 'Sbsq Observation Care/Day High Severity 99226 (4)' }, + { content: ' ' }, + ], + }, + { + id: 'row_5', + cells: [ + { content: }, + { content: 'Arterial Sheath Care (5)' }, + { content: }, + ], + }, +]; + +const cols = [ + { + id: 'Column-0', + displayName: 'Col_1', + width: '60px', + align: alignTypes.CENTER, + }, + { + id: 'Column-1', + displayName: 'Col_2', + flexGrow: true, + }, + { + id: 'Column-2', + displayName: 'Col_3', + width: '60px', + align: alignTypes.CENTER, + }, +]; + +const FixedWidthColumns = () => ( + +); + +export default FixedWidthColumns; diff --git a/packages/terra-framework-docs/src/terra-dev-site/test/compact-interactive-list/ResponsiveColumnsMaxWidth.test.jsx b/packages/terra-framework-docs/src/terra-dev-site/test/compact-interactive-list/ResponsiveColumnsMaxWidth.test.jsx new file mode 100644 index 00000000000..57051cead24 --- /dev/null +++ b/packages/terra-framework-docs/src/terra-dev-site/test/compact-interactive-list/ResponsiveColumnsMaxWidth.test.jsx @@ -0,0 +1,82 @@ +import React from 'react'; +import CompactInteractiveList, { alignTypes } from 'terra-compact-interactive-list'; +import { IconDocuments, IconFeaturedOutline, IconImage } from 'terra-icon'; + +// Source data for tests + +const rows = [ + { + id: 'row_1', + cells: [ + { content: }, + { content: 'Discern Care Set (1)' }, + { content: }, + ], + }, + { + id: 'row_2', + cells: [ + { content: }, + { content: 'Initial observation Care/Day High Severity 99220 (2)' }, + { content: }, + ], + }, + { + id: 'row_3', + cells: [ + { content: }, + { content: 'Arterial Sheath Care (3)' }, + { content: }, + ], + }, + { + id: 'row_4', + cells: [ + { content: ' ' }, + { content: 'Sbsq Observation Care/Day High Severity 99226 (4)' }, + { content: ' ' }, + ], + }, + { + id: 'row_5', + cells: [ + { content: }, + { content: 'Arterial Sheath Care (5)' }, + { content: }, + ], + }, +]; + +const cols = [ + { + id: 'Column-0', + displayName: 'Col_1', + width: '6em', + align: alignTypes.CENTER, + }, + { + id: 'Column-1', + displayName: 'Col_2', + width: '60em', + flexGrow: true, + minimumWidth: '10em', + maximumWidth: '30em', + }, + { + id: 'Column-2', + displayName: 'Col_3', + width: '6em', + align: alignTypes.CENTER, + }, +]; + +const ResponsiveColumnsMaxWidth = () => ( + +); + +export default ResponsiveColumnsMaxWidth; diff --git a/packages/terra-framework-docs/src/terra-dev-site/test/compact-interactive-list/ScalableUnits.test.jsx b/packages/terra-framework-docs/src/terra-dev-site/test/compact-interactive-list/ScalableUnits.test.jsx new file mode 100644 index 00000000000..f91b808aa0c --- /dev/null +++ b/packages/terra-framework-docs/src/terra-dev-site/test/compact-interactive-list/ScalableUnits.test.jsx @@ -0,0 +1,81 @@ +/* eslint-disable react/forbid-dom-props */ +import React from 'react'; +import CompactInteractiveList, { alignTypes } from 'terra-compact-interactive-list'; +import { IconDocuments, IconFeaturedOutline, IconImage } from 'terra-icon'; + +const rows = [ + { + id: 'row_1', + cells: [ + { content: }, + { content: 'Discern Care Set (1)' }, + { content: }, + ], + }, + { + id: 'row_2', + cells: [ + { content: }, + { content: 'Initial observation Care/Day High Severity 99220 (2)' }, + { content: }, + ], + }, + { + id: 'row_3', + cells: [ + { content: }, + { content: 'Arterial Sheath Care (3)' }, + { content: }, + ], + }, + { + id: 'row_4', + cells: [ + { content: ' ' }, + { content: 'Sbsq Observation Care/Day High Severity 99226 (4)' }, + { content: ' ' }, + ], + }, + { + id: 'row_5', + cells: [ + { content: }, + { content: 'Arterial Sheath Care (5)' }, + { content: }, + ], + }, +]; + +const cols = [ + { + id: 'Column-0', + displayName: 'Col_1', + maximumWidth: '4.5em', + align: alignTypes.CENTER, + }, + { + id: 'Column-1', + displayName: 'Col_2', + maximumWidth: '25em', + minimumWidth: '7em', + }, + { + id: 'Column-2', + displayName: 'Col_3', + maximumWidth: '5em', + align: alignTypes.CENTER, + }, +]; + +const ScalableUnits = () => ( +
+ +
+); + +export default ScalableUnits; diff --git a/packages/terra-framework-docs/src/terra-dev-site/test/compact-interactive-list/WidthBreakpoints.test.jsx b/packages/terra-framework-docs/src/terra-dev-site/test/compact-interactive-list/WidthBreakpoints.test.jsx new file mode 100644 index 00000000000..afe2ec194cb --- /dev/null +++ b/packages/terra-framework-docs/src/terra-dev-site/test/compact-interactive-list/WidthBreakpoints.test.jsx @@ -0,0 +1,94 @@ +import React from 'react'; +import CompactInteractiveList, { alignTypes } from 'terra-compact-interactive-list'; +import { ActiveBreakpointContext } from 'terra-breakpoints'; +import { IconDocuments, IconFeaturedOutline, IconImage } from 'terra-icon'; + +const getNumberOfColumns = (activeBreakpoint) => { + switch (activeBreakpoint) { + case 'enormous': return 5; + case 'huge': return 4; + case 'large': return 3; + case 'medium': return 2; + case 'small': return 1; + case 'tiny': return 1; + default: return 1; + } +}; + +const rows = [ + { + id: 'row_1', + cells: [ + { content: }, + { content: 'Discern Care Set (1)' }, + { content: }, + ], + }, + { + id: 'row_2', + cells: [ + { content: }, + { content: 'Initial observation Care/Day High Severity 99220 (2)' }, + { content: }, + ], + }, + { + id: 'row_3', + cells: [ + { content: }, + { content: 'Arterial Sheath Care (3)' }, + { content: }, + ], + }, + { + id: 'row_4', + cells: [ + { content: ' ' }, + { content: 'Sbsq Observation Care/Day High Severity 99226 (4)' }, + { content: ' ' }, + ], + }, + { + id: 'row_5', + cells: [ + { content: }, + { content: 'Arterial Sheath Care (5)' }, + { content: }, + ], + }, +]; + +const cols = [ + { + id: 'Column-0', + displayName: 'Col_1', + width: '60px', + align: alignTypes.CENTER, + }, + { + id: 'Column-1', + displayName: 'Col_2', + width: '200px', + flexGrow: true, + }, + { + id: 'Column-2', + displayName: 'Col_3', + width: '60px', + align: alignTypes.CENTER, + }, +]; + +const WidthBreakpoints = () => { + const activeBreakpoint = React.useContext(ActiveBreakpointContext); + return ( + + ); +}; + +export default WidthBreakpoints; diff --git a/packages/terra-framework-docs/src/terra-dev-site/test/compact-interactive-list/WidthBreakpointsHorizontalFlow.test.jsx b/packages/terra-framework-docs/src/terra-dev-site/test/compact-interactive-list/WidthBreakpointsHorizontalFlow.test.jsx new file mode 100644 index 00000000000..649e2e2217d --- /dev/null +++ b/packages/terra-framework-docs/src/terra-dev-site/test/compact-interactive-list/WidthBreakpointsHorizontalFlow.test.jsx @@ -0,0 +1,95 @@ +import React from 'react'; +import CompactInteractiveList, { alignTypes } from 'terra-compact-interactive-list'; +import { ActiveBreakpointContext } from 'terra-breakpoints'; +import { IconDocuments, IconFeaturedOutline, IconImage } from 'terra-icon'; + +const getNumberOfColumns = (activeBreakpoint) => { + switch (activeBreakpoint) { + case 'enormous': return 5; + case 'huge': return 4; + case 'large': return 3; + case 'medium': return 2; + case 'small': return 1; + case 'tiny': return 1; + default: return 1; + } +}; + +const rows = [ + { + id: 'row_1', + cells: [ + { content: }, + { content: 'Discern Care Set (1)' }, + { content: }, + ], + }, + { + id: 'row_2', + cells: [ + { content: }, + { content: 'Initial observation Care/Day High Severity 99220 (2)' }, + { content: }, + ], + }, + { + id: 'row_3', + cells: [ + { content: }, + { content: 'Arterial Sheath Care (3)' }, + { content: }, + ], + }, + { + id: 'row_4', + cells: [ + { content: ' ' }, + { content: 'Sbsq Observation Care/Day High Severity 99226 (4)' }, + { content: ' ' }, + ], + }, + { + id: 'row_5', + cells: [ + { content: }, + { content: 'Arterial Sheath Care (5)' }, + { content: }, + ], + }, +]; + +const cols = [ + { + id: 'Column-0', + displayName: 'Col_1', + width: '60px', + align: alignTypes.CENTER, + }, + { + id: 'Column-1', + displayName: 'Col_2', + width: '200px', + flexGrow: true, + }, + { + id: 'Column-2', + displayName: 'Col_3', + width: '60px', + align: alignTypes.CENTER, + }, +]; + +const WidthBreakpointsHorizontalFlow = () => { + const activeBreakpoint = React.useContext(ActiveBreakpointContext); + return ( + + ); +}; + +export default WidthBreakpointsHorizontalFlow; diff --git a/packages/terra-framework-docs/src/terra-dev-site/test/data-grid/worklist-data-grid/WorklistDataGridDeleteRowsAndColumns.test.jsx b/packages/terra-framework-docs/src/terra-dev-site/test/data-grid/worklist-data-grid/WorklistDataGridDeleteRowsAndColumns.test.jsx new file mode 100644 index 00000000000..c0284dc9ee4 --- /dev/null +++ b/packages/terra-framework-docs/src/terra-dev-site/test/data-grid/worklist-data-grid/WorklistDataGridDeleteRowsAndColumns.test.jsx @@ -0,0 +1,71 @@ +import React, { useCallback, useState } from 'react'; +import { WorklistDataGrid } from 'terra-data-grid'; +import gridDataJSON from './gridDataWithRowSelection.json'; + +const WorklistDataGridDeleteRows = () => { + const rowHeaderIndex = 0; + const { cols, rows } = gridDataJSON; + const [rowData, setRowData] = useState(rows); + const [columnData, setColumnData] = useState(cols); + + const onRowSelect = useCallback((rowsToSelectAndUnSelect) => { + // Remove current selections + const newRowData = [...rowData]; + + rowsToSelectAndUnSelect.forEach((updatedRow) => { + const dataRowToUpdate = newRowData.find(row => row.id === updatedRow.id); + if (dataRowToUpdate) { + dataRowToUpdate.isSelected = updatedRow.selected; + } + }); + + setRowData(newRowData); + }, [rowData]); + + const onRowSelectAll = useCallback(() => { + const newRowData = rowData.map(row => ({ ...row, isSelected: true })); + setRowData(newRowData); + }, [rowData]); + + const handleColumnSelect = (columnId) => { + const indexToDelete = columnData.findIndex(column => column.id === columnId); + + if (indexToDelete !== -1) { + const newColumns = [...columnData]; + newColumns.splice(indexToDelete, 1); + const newRows = [...rowData]; + + newRows.forEach(row => { + const newCells = [...row.cells]; + newCells.splice(indexToDelete, 1); + // eslint-disable-next-line no-param-reassign + row.cells = newCells; + }); + + setColumnData(newColumns); + setRowData(newRows); + } + }; + + return ( +
+
+ + +
+ +
+ ); +}; + +export default WorklistDataGridDeleteRows; diff --git a/packages/terra-framework-docs/src/terra-dev-site/test/table/TableWithSections.test.jsx b/packages/terra-framework-docs/src/terra-dev-site/test/table/TableWithSections.test.jsx index c906225df71..174a01b104d 100644 --- a/packages/terra-framework-docs/src/terra-dev-site/test/table/TableWithSections.test.jsx +++ b/packages/terra-framework-docs/src/terra-dev-site/test/table/TableWithSections.test.jsx @@ -90,7 +90,8 @@ const TableWithSections = () => { return (
diff --git a/packages/terra-table/CHANGELOG.md b/packages/terra-table/CHANGELOG.md index 740d94f66ce..ee70efeb679 100644 --- a/packages/terra-table/CHANGELOG.md +++ b/packages/terra-table/CHANGELOG.md @@ -2,6 +2,9 @@ ## Unreleased +* Fixed + * Fixed the AT messages for the row selection functionality. + ## 5.1.1-alpha.2 - (November 20, 2023) * Breaking Changes @@ -17,6 +20,7 @@ * Fixed * Fixed tab index issues in the column header cells. * Fixed screenreader issue due to space in the headers attribute. + * Fixed pinned column divider covering section headers. ## 5.1.1-alpha.1 - (November 9, 2023) diff --git a/packages/terra-table/src/Table.jsx b/packages/terra-table/src/Table.jsx index f72a4a75248..a324f1333f0 100644 --- a/packages/terra-table/src/Table.jsx +++ b/packages/terra-table/src/Table.jsx @@ -341,7 +341,7 @@ function Table(props) { } if (rowSelectionsRemoved.length === 1) { - const unselectedRowLabel = tableRef.current.querySelector(`tr[data-row-id='${rowSelectionsRemoved[0]}']`).getAttribute('data-row-id'); + const unselectedRowLabel = tableRef.current.querySelector(`tr[data-row-id='${rowSelectionsRemoved[0]}']`).getAttribute('aria-rowindex'); selectionUpdateAriaMessage += intl.formatMessage({ id: 'Terra.table.row-selection-cleared-template' }, { row: unselectedRowLabel }); } else if (rowSelectionsRemoved.length > 1) { selectionUpdateAriaMessage += intl.formatMessage({ id: 'Terra.table.multiple-rows-unselected' }, { rowCount: rowSelectionsRemoved.length }); diff --git a/packages/terra-table/src/subcomponents/ColumnHeader.jsx b/packages/terra-table/src/subcomponents/ColumnHeader.jsx index c4198bce3f6..1da208cdeb6 100644 --- a/packages/terra-table/src/subcomponents/ColumnHeader.jsx +++ b/packages/terra-table/src/subcomponents/ColumnHeader.jsx @@ -84,6 +84,8 @@ const ColumnHeader = (props) => { return ( diff --git a/packages/terra-table/src/subcomponents/RowSelectionCell.jsx b/packages/terra-table/src/subcomponents/RowSelectionCell.jsx index 0f23c8f744f..08607eb7204 100644 --- a/packages/terra-table/src/subcomponents/RowSelectionCell.jsx +++ b/packages/terra-table/src/subcomponents/RowSelectionCell.jsx @@ -89,7 +89,7 @@ function RowSelectionCell(props) { const gridContext = useContext(GridContext); const isGridContext = gridContext.role === GridConstants.GRID; - const rowLabel = intl.formatMessage({ id: 'Terra.table.row-index' }, { row: rowIndex + 1 }); + const rowLabel = intl.formatMessage({ id: 'Terra.table.row-index' }, { row: rowIndex }); const selectionCheckbox = ( { const columnHeader = wrapper.find('.column-header-row'); expect(columnHeader).toHaveLength(1); expect(columnHeader.props().height).toBe('3rem'); + expect(columnHeader.props()['data-row-id']).toBe('test-table-header-row'); + expect(columnHeader.props()['aria-rowindex']).toBe(1); // Validate ColumnHeaderCell React component const columnHeaderCells = columnHeader.find(ColumnHeaderCell); @@ -57,6 +59,8 @@ describe('ColumnHeader', () => { const columnHeader = wrapper.find('.column-header-row'); expect(columnHeader).toHaveLength(1); expect(columnHeader.props().height).toBe('3rem'); + expect(columnHeader.props()['data-row-id']).toBe('test-table-header-row'); + expect(columnHeader.props()['aria-rowindex']).toBe(1); // Validate ColumnHeaderCell React component const columnHeaderCells = columnHeader.find(ColumnHeaderCell); @@ -87,6 +91,8 @@ describe('ColumnHeader', () => { const columnHeader = wrapper.find('.column-header-row'); expect(columnHeader).toHaveLength(1); expect(columnHeader.props().height).toBe('3rem'); + expect(columnHeader.props()['data-row-id']).toBe('test-table-header-row'); + expect(columnHeader.props()['aria-rowindex']).toBe(1); // Validate ColumnHeaderCell React component const columnHeaderCells = columnHeader.find(ColumnHeaderCell); @@ -123,6 +129,8 @@ describe('ColumnHeader', () => { const columnHeader = wrapper.find('.column-header-row'); expect(columnHeader).toHaveLength(1); expect(columnHeader.props().height).toBe('3rem'); + expect(columnHeader.props()['data-row-id']).toBe('test-table-header-row'); + expect(columnHeader.props()['aria-rowindex']).toBe(1); // Validate ColumnHeaderCell React component const columnHeaderCell = columnHeader.find(ColumnHeaderCell).first(); @@ -155,6 +163,8 @@ describe('ColumnHeader', () => { // Verify that column headers are not present const columnHeader = wrapper.find('.hidden'); expect(columnHeader).toHaveLength(1); + expect(columnHeader.props()['data-row-id']).toBe('test-table-header-row'); + expect(columnHeader.props()['aria-rowindex']).toBe(1); expect(wrapper).toMatchSnapshot(); }); diff --git a/packages/terra-table/tests/jest/__snapshots__/ColumnHeader.test.jsx.snap b/packages/terra-table/tests/jest/__snapshots__/ColumnHeader.test.jsx.snap index 3c01314475b..48a5f3cd376 100644 --- a/packages/terra-table/tests/jest/__snapshots__/ColumnHeader.test.jsx.snap +++ b/packages/terra-table/tests/jest/__snapshots__/ColumnHeader.test.jsx.snap @@ -3,7 +3,9 @@ exports[`ColumnHeader renders a column header with an active resize column 1`] = `