diff --git a/packages/ibm-products-styles/src/components/Datagrid/styles/_useExpandedRow.scss b/packages/ibm-products-styles/src/components/Datagrid/styles/_useExpandedRow.scss index b22707d5f2..3c566bec65 100644 --- a/packages/ibm-products-styles/src/components/Datagrid/styles/_useExpandedRow.scss +++ b/packages/ibm-products-styles/src/components/Datagrid/styles/_useExpandedRow.scss @@ -71,3 +71,24 @@ td { background: $layer-hover; } + +.#{variables.$block-class} + .#{c4p-settings.$carbon-prefix}--data-table + tbody + tr:hover + td.#{variables.$block-class}__expanded-row-cell-wrapper, +.#{variables.$block-class} + .#{c4p-settings.$carbon-prefix}--data-table + td.#{variables.$block-class}__expanded-row-cell-wrapper, +.#{variables.$block-class} + .#{c4p-settings.$carbon-prefix}--data-table + .#{variables.$block-class}__carbon-row-expanded + td.#{variables.$block-class}__expandable-row-cell { + border: none; +} + +.#{variables.$block-class} + .#{c4p-settings.$carbon-prefix}--data-table + td.#{variables.$block-class}__expanded-row-cell-wrapper { + padding: 0; +} diff --git a/packages/ibm-products/src/components/Datagrid/Datagrid/DatagridExpandedRow.js b/packages/ibm-products/src/components/Datagrid/Datagrid/DatagridExpandedRow.js index 9ea0eee798..7ff2f93a79 100644 --- a/packages/ibm-products/src/components/Datagrid/Datagrid/DatagridExpandedRow.js +++ b/packages/ibm-products/src/components/Datagrid/Datagrid/DatagridExpandedRow.js @@ -34,14 +34,16 @@ const DatagridExpandedRow = onMouseEnter={(event) => toggleParentHoverClass(event, 'enter')} onMouseLeave={(event) => toggleParentHoverClass(event)} > -
- -
+ +
+ +
+ ); }; diff --git a/packages/ibm-products/src/components/Datagrid/Datagrid/DatagridRow.js b/packages/ibm-products/src/components/Datagrid/Datagrid/DatagridRow.js index ba095bbe4d..e372f0b0b9 100644 --- a/packages/ibm-products/src/components/Datagrid/Datagrid/DatagridRow.js +++ b/packages/ibm-products/src/components/Datagrid/Datagrid/DatagridRow.js @@ -24,7 +24,15 @@ const rowHeights = { // eslint-disable-next-line react/prop-types const DatagridRow = (datagridState) => { - const { row, rowSize, withNestedRows, prepareRow, key } = datagridState; + const { + row, + rowSize, + withNestedRows, + prepareRow, + key, + tableId, + withExpandedRows, + } = datagridState; const getVisibleNestedRowCount = ({ isExpanded, subRows }) => { let size = 0; @@ -67,7 +75,7 @@ const DatagridRow = (datagridState) => { const focusRemover = () => { const elements = document.querySelectorAll( - `.${blockClass}__carbon-row-expanded` + `#${tableId} .${blockClass}__carbon-row-expanded` ); elements.forEach((el) => { el.classList.remove(`${blockClass}__carbon-row-expanded-hover-active`); @@ -107,6 +115,15 @@ const DatagridRow = (datagridState) => { [`${carbon.prefix}--data-table--selected`]: row.isSelected, }); + const setAdditionalRowProps = () => { + if (withNestedRows || withExpandedRows) { + return { + 'data-nested-row-id': row.id, + }; + } + return {}; + }; + return ( { onFocus={hoverHandler} onBlur={focusRemover} onKeyUp={handleOnKeyUp} + {...setAdditionalRowProps()} > {row.cells.map((cell, index) => { const cellProps = cell.getCellProps(); diff --git a/packages/ibm-products/src/components/Datagrid/Extensions/ExpandableRow/ExpandableRow.stories.js b/packages/ibm-products/src/components/Datagrid/Extensions/ExpandableRow/ExpandableRow.stories.js index 622a606ccc..0f2798e9e2 100644 --- a/packages/ibm-products/src/components/Datagrid/Extensions/ExpandableRow/ExpandableRow.stories.js +++ b/packages/ibm-products/src/components/Datagrid/Extensions/ExpandableRow/ExpandableRow.stories.js @@ -19,7 +19,6 @@ import { DatagridActions } from '../../utils/DatagridActions'; import { DatagridPagination } from '../../utils/DatagridPagination'; import { makeData } from '../../utils/makeData'; import { ARG_TYPES } from '../../utils/getArgTypes'; -import { pkg } from '../../../../settings'; import { DocsPage } from './ExpandableRow.docs-page'; import { usePrefix } from '../../../../global/js/hooks'; @@ -189,12 +188,6 @@ const ExpandedRows = ({ ...args }) => { useExpandedRow ); - // Warnings are ordinarily silenced in storybook, add this to test. - pkg._silenceWarnings(false); - // Enable feature flag for `useExpandedRow` hook - pkg.feature['Datagrid.useExpandedRow'] = true; - pkg._silenceWarnings(true); - return ; }; @@ -222,6 +215,5 @@ export const ExpandableRowStory = prepareStory(BasicTemplateWrapper, { }, args: { ...expandableRowControlProps, - featureFlags: ['Datagrid.useExpandedRow'], }, }); diff --git a/packages/ibm-products/src/components/Datagrid/Extensions/NestedRows/NestedRows.stories.js b/packages/ibm-products/src/components/Datagrid/Extensions/NestedRows/NestedRows.stories.js index d6d5ff0eac..21a8aa7c52 100644 --- a/packages/ibm-products/src/components/Datagrid/Extensions/NestedRows/NestedRows.stories.js +++ b/packages/ibm-products/src/components/Datagrid/Extensions/NestedRows/NestedRows.stories.js @@ -15,11 +15,9 @@ import { } from '../../../../global/js/utils/story-helper'; import { Datagrid, useDatagrid, useNestedRows } from '../../index'; import styles from '../../_storybook-styles.scss'; -// import mdx from '../../Datagrid.mdx'; import { DatagridActions } from '../../utils/DatagridActions'; import { makeData } from '../../utils/makeData'; import { ARG_TYPES } from '../../utils/getArgTypes'; -import { pkg } from '../../../../settings'; import { StoryDocsPage } from '../../../../global/js/utils/StoryDocsPage'; export default { @@ -205,12 +203,6 @@ const SingleLevelNestedRows = ({ ...args }) => { useNestedRows ); - // Warnings are ordinarily silenced in storybook, add this to test - pkg._silenceWarnings(false); - // Enable feature flag for `useNestedRows` hook - pkg.feature['Datagrid.useNestedRows'] = true; - pkg._silenceWarnings(true); - return ; }; @@ -253,12 +245,6 @@ const NestedRows = ({ ...args }) => { useNestedRows ); - // Warnings are ordinarily silenced in storybook, add this to test - pkg._silenceWarnings(false); - // Enable feature flag for `useNestedRows` hook - pkg.feature['Datagrid.useNestedRows'] = true; - pkg._silenceWarnings(true); - return ; }; @@ -281,6 +267,5 @@ export const NestedRowsUsageStory = prepareStory(BasicTemplateWrapper, { }, args: { ...nestedRowsControlProps, - featureFlags: ['Datagrid.useNestedRows'], }, }); diff --git a/packages/ibm-products/src/components/Datagrid/useExpandedRow.js b/packages/ibm-products/src/components/Datagrid/useExpandedRow.js index 57dfd7a28f..21c235ee3b 100644 --- a/packages/ibm-products/src/components/Datagrid/useExpandedRow.js +++ b/packages/ibm-products/src/components/Datagrid/useExpandedRow.js @@ -5,16 +5,11 @@ * LICENSE file in the root directory of this source tree. */ -import { useEffect, useState } from 'react'; -import { pkg } from '../../settings'; +import { useState } from 'react'; import DatagridExpandedRow from './Datagrid/DatagridExpandedRow'; import useRowExpander from './useRowExpander'; const useExpandedRow = (hooks) => { - useEffect(() => { - pkg.checkReportFeatureEnabled('Datagrid.useExpandedRow'); - }, []); - useRowExpander(hooks); const useInstance = (instance) => { const { rows, expandedContentHeight, ExpandedRowContentComponent } = @@ -29,7 +24,11 @@ const useExpandedRow = (hooks) => { expandedRowsHeight[row.index] || expandedContentHeight, RowExpansionRenderer: DatagridExpandedRow(ExpandedRowContentComponent), })); - Object.assign(instance, { rows: rowsWithExpand, setExpandedRowHeight }); + Object.assign(instance, { + rows: rowsWithExpand, + setExpandedRowHeight, + withExpandedRows: true, + }); }; hooks.useInstance.push(useInstance); }; diff --git a/packages/ibm-products/src/components/Datagrid/useFocusRowExpander.js b/packages/ibm-products/src/components/Datagrid/useFocusRowExpander.js new file mode 100644 index 0000000000..aa7cf3c68b --- /dev/null +++ b/packages/ibm-products/src/components/Datagrid/useFocusRowExpander.js @@ -0,0 +1,50 @@ +/** + * Copyright IBM Corp. 2023, 2023 + * + * This source code is licensed under the Apache-2.0 license found in the + * LICENSE file in the root directory of this source tree. + */ + +import { useEffect } from 'react'; + +// Focuses the row expander after a nested/expandable row state change. +// We have to add this workaround because react-table is re-rendering the entire row +// which removes the focus from the expander and interrupts the keyboard navigation +// flow. +export const useFocusRowExpander = ({ + instance, + lastExpandedRowIndex, + blockClass, + activeElement, +}) => { + useEffect(() => { + // We need to return at this point so that the focus is not stolen from + // other interactive elements in the Datagrid + if (!activeElement.classList.contains(`${blockClass}__row-expander`)) { + return; + } + const tableId = instance?.tableId; + const rowElements = document.querySelectorAll(`#${tableId} tbody tr`); + if (lastExpandedRowIndex) { + const rowElementsArray = Array.from(rowElements); + const activeRow = rowElementsArray.filter((r) => { + if (r.getAttribute('data-nested-row-id') === lastExpandedRowIndex) { + return r; + } + return null; + }); + if (activeRow.length) { + const rowExpander = activeRow[0].querySelector( + `.${blockClass}__row-expander` + ); + rowExpander.focus(); + } + } + }, [ + instance?.tableId, + instance?.expandedRows, + lastExpandedRowIndex, + blockClass, + activeElement, + ]); +}; diff --git a/packages/ibm-products/src/components/Datagrid/useNestedRowExpander.js b/packages/ibm-products/src/components/Datagrid/useNestedRowExpander.js index aab7e45a2f..c2ad7d6ffd 100644 --- a/packages/ibm-products/src/components/Datagrid/useNestedRowExpander.js +++ b/packages/ibm-products/src/components/Datagrid/useNestedRowExpander.js @@ -10,14 +10,24 @@ import React, { useRef } from 'react'; import { ChevronRight } from '@carbon/react/icons'; import cx from 'classnames'; import { pkg, carbon } from '../../settings'; +import { useFocusRowExpander } from './useFocusRowExpander'; const blockClass = `${pkg.prefix}--datagrid`; const useNestedRowExpander = (hooks) => { const tempState = useRef(); + const lastExpandedRowIndex = useRef(); const useInstance = (instance) => { tempState.current = instance; }; + + useFocusRowExpander({ + instance: tempState?.current, + lastExpandedRowIndex: lastExpandedRowIndex?.current, + blockClass, + activeElement: document.activeElement, + }); + const visibleColumns = (columns) => { const expanderColumn = { id: 'expander', @@ -28,6 +38,7 @@ const useNestedRowExpander = (hooks) => { // Prevents `onRowClick` from being called if `useOnRowClick` is included event.stopPropagation(); row.toggleRowExpanded(); + lastExpandedRowIndex.current = row.id; }, }; const { diff --git a/packages/ibm-products/src/components/Datagrid/useNestedRows.js b/packages/ibm-products/src/components/Datagrid/useNestedRows.js index 4bfa4a17b1..d9691fe2f1 100644 --- a/packages/ibm-products/src/components/Datagrid/useNestedRows.js +++ b/packages/ibm-products/src/components/Datagrid/useNestedRows.js @@ -5,18 +5,13 @@ * LICENSE file in the root directory of this source tree. */ -import { useEffect } from 'react'; -import { pkg } from '../../settings'; import cx from 'classnames'; +import { pkg } from '../../settings'; import useNestedRowExpander from './useNestedRowExpander'; const blockClass = `${pkg.prefix}--datagrid`; const useNestedRows = (hooks) => { - useEffect(() => { - pkg.checkReportFeatureEnabled('Datagrid.useNestedRows'); - }, []); - useNestedRowExpander(hooks); const marginLeft = 24; diff --git a/packages/ibm-products/src/components/Datagrid/useRowExpander.js b/packages/ibm-products/src/components/Datagrid/useRowExpander.js index 979bcfa02d..55eaf85c0b 100644 --- a/packages/ibm-products/src/components/Datagrid/useRowExpander.js +++ b/packages/ibm-products/src/components/Datagrid/useRowExpander.js @@ -10,14 +10,24 @@ import React, { useRef } from 'react'; import { ChevronDown, ChevronUp } from '@carbon/react/icons'; import { pkg, carbon } from '../../settings'; import cx from 'classnames'; +import { useFocusRowExpander } from './useFocusRowExpander'; const blockClass = `${pkg.prefix}--datagrid`; const useRowExpander = (hooks) => { const tempState = useRef(); + const lastExpandedRowIndex = useRef(); const useInstance = (instance) => { tempState.current = instance; }; + + useFocusRowExpander({ + instance: tempState?.current, + lastExpandedRowIndex: lastExpandedRowIndex?.current, + blockClass, + activeElement: document.activeElement, + }); + const visibleColumns = (columns) => { const expanderColumn = { id: 'expander', @@ -28,6 +38,7 @@ const useRowExpander = (hooks) => { // Prevents `onRowClick` from being called if `useOnRowClick` is included event.stopPropagation(); row.toggleRowExpanded(); + lastExpandedRowIndex.current = row.id; }, }; const { diff --git a/packages/ibm-products/src/global/js/package-settings.js b/packages/ibm-products/src/global/js/package-settings.js index a79e124b6a..cfceb27072 100644 --- a/packages/ibm-products/src/global/js/package-settings.js +++ b/packages/ibm-products/src/global/js/package-settings.js @@ -81,8 +81,6 @@ const defaults = { 'default-portal-target-body': true, 'Datagrid.useInlineEdit': false, 'Datagrid.useEditableCell': false, - 'Datagrid.useExpandedRow': false, - 'Datagrid.useNestedRows': false, 'Datagrid.useFiltering': false, 'Datagrid.useCustomizeColumns': false, 'ExampleComponent.secondaryIcon': false,