Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

chore: add decorator in dataTable #18114

Open
wants to merge 11 commits into
base: main
Choose a base branch
from
Original file line number Diff line number Diff line change
Expand Up @@ -324,6 +324,7 @@ Array [
"spacing11",
"spacing12",
"spacing13",
"statusTokens",
"styles",
"supportCautionMajor",
"supportCautionMinor",
Expand Down
22 changes: 22 additions & 0 deletions packages/react/__tests__/__snapshots__/PublicAPI-test.js.snap
Original file line number Diff line number Diff line change
Expand Up @@ -1973,6 +1973,17 @@ Map {
},
},
},
"TableDecoratorRow": Object {
"displayName": "TableDecoratorRow",
"propTypes": Object {
"className": Object {
"type": "string",
},
"decorator": Object {
"type": "node",
},
},
},
"TableExpandHeader": Object {
"propTypes": Object {
"aria-controls": Object {
Expand Down Expand Up @@ -8052,6 +8063,17 @@ Map {
},
},
},
"TableDecoratorRow" => Object {
"displayName": "TableDecoratorRow",
"propTypes": Object {
"className": Object {
"type": "string",
},
"decorator": Object {
"type": "node",
},
},
},
"TableExpandHeader" => Object {
"propTypes": Object {
"aria-controls": Object {
Expand Down
1 change: 1 addition & 0 deletions packages/react/src/__tests__/index-test.js
Original file line number Diff line number Diff line change
Expand Up @@ -205,6 +205,7 @@ describe('Carbon Components React', () => {
"TableBody",
"TableCell",
"TableContainer",
"TableDecoratorRow",
"TableExpandHeader",
"TableExpandRow",
"TableExpandedRow",
Expand Down
11 changes: 9 additions & 2 deletions packages/react/src/components/DataTable/DataTable.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,7 @@ import TableBatchActions from './TableBatchActions';
import TableBody from './TableBody';
import TableCell from './TableCell';
import TableContainer from './TableContainer';
import TableDecoratorRow from './TableDecoratorRow';
import TableExpandHeader from './TableExpandHeader';
import TableExpandRow from './TableExpandRow';
import TableExpandedRow from './TableExpandedRow';
Expand Down Expand Up @@ -99,6 +100,7 @@ export interface DataTableHeader {
key: string;
header: React.ReactNode;
slug?: React.ReactElement;
decorator?: React.ReactElement;
}

export interface DataTableRenderProps<RowType, ColTypes extends any[]> {
Expand Down Expand Up @@ -203,7 +205,8 @@ export interface DataTableRenderProps<RowType, ColTypes extends any[]> {
};
getCellProps: (getCellPropsArgs: { cell: DataTableCell<ColTypes> }) => {
[key: string]: unknown;
hasSlugHeader?: boolean;
hasAILabelHeader?: boolean;
hasDecoratorHeader?: boolean;
};

// Custom event handlers
Expand Down Expand Up @@ -390,6 +393,7 @@ class DataTable<RowType, ColTypes extends any[]> extends React.Component<
static TableBody: typeof TableBody;
static TableCell: typeof TableCell;
static TableContainer: typeof TableContainer;
static TableDecoratorRow: typeof TableDecoratorRow;
static TableExpandHeader: typeof TableExpandHeader;
static TableExpandRow: typeof TableExpandRow;
static TableExpandedRow: typeof TableExpandedRow;
Expand Down Expand Up @@ -473,6 +477,7 @@ class DataTable<RowType, ColTypes extends any[]> extends React.Component<
isSortable,
isSortHeader: sortHeaderKey === header.key,
slug: header.slug,
decorator: header.decorator,
onClick: (event) => {
const nextSortState = getNextSortState(this.props, this.state, {
key: header.key,
Expand Down Expand Up @@ -747,7 +752,8 @@ class DataTable<RowType, ColTypes extends any[]> extends React.Component<
getCellProps = ({ cell, ...rest }) => {
return {
...rest,
hasSlugHeader: cell.hasSlugHeader,
hasAILabelHeader: cell.hasAILabelHeader,
hasDecoratorHeader: cell.hasDecoratorHeader,
};
};

Expand Down Expand Up @@ -1035,6 +1041,7 @@ DataTable.TableBatchActions = TableBatchActions;
DataTable.TableBody = TableBody;
DataTable.TableCell = TableCell;
DataTable.TableContainer = TableContainer;
DataTable.TableDecoratorRow = TableDecoratorRow;
DataTable.TableExpandHeader = TableExpandHeader;
DataTable.TableExpandRow = TableExpandRow;
DataTable.TableExpandedRow = TableExpandedRow;
Expand Down
6 changes: 3 additions & 3 deletions packages/react/src/components/DataTable/TableCell.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -29,7 +29,7 @@ interface TableCellProps extends ReactAttr<HTMLTableCellElement> {
/**
* Specify if the table cell is in an AI column
*/
hasSlugHeader?: boolean;
hasAILabelHeader?: boolean;

/**
* The id of the matching th node in the table head. Addresses a11y concerns outlined here: https://www.ibm.com/able/guidelines/ci162/info_and_relationships.html and https://www.w3.org/TR/WCAG20-TECHS/H43
Expand All @@ -38,11 +38,11 @@ interface TableCellProps extends ReactAttr<HTMLTableCellElement> {
}

const TableCell = React.forwardRef<HTMLTableCellElement, TableCellProps>(
({ children, className, hasSlugHeader, colSpan, ...rest }, ref) => {
({ children, className, hasAILabelHeader, colSpan, ...rest }, ref) => {
const prefix = usePrefix();

const tableCellClassNames = classNames(className, {
[`${prefix}--table-cell--column-slug`]: hasSlugHeader,
[`${prefix}--table-cell--column-slug`]: hasAILabelHeader,
});
return (
<td
Expand Down
68 changes: 68 additions & 0 deletions packages/react/src/components/DataTable/TableDecoratorRow.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,68 @@
/**
* Copyright IBM Corp. 2016, 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 PropTypes from 'prop-types';
import React, { ReactNode } from 'react';
import classNames from 'classnames';
import { usePrefix } from '../../internal/usePrefix';
import deprecate from '../../prop-types/deprecate';

export interface TableDecoratorRowProps {
/**
* The CSS class names of the cell that wraps the underlying input control
*/
className?: string;

/**
* **Experimental**: Provide a `decorator` component to be rendered inside the `TableDecoratorRow` component
*/
decorator?: ReactNode;
}

const TableDecoratorRow = ({
className,
decorator,
}: TableDecoratorRowProps) => {
const prefix = usePrefix();
const TableDecoratorRowClasses = classNames({
...(className && { [className]: true }),
[`${prefix}--table-column-decorator`]: true,
[`${prefix}--table-column-decorator--active`]: decorator,
});

let normalizedDecorator = React.isValidElement(decorator)
? (decorator as ReactNode)
: null;
if (
normalizedDecorator &&
normalizedDecorator['type']?.displayName === 'AILabel'
) {
normalizedDecorator = React.cloneElement(
normalizedDecorator as React.ReactElement<any>,
{
size: 'mini',
}
);
}

return <td className={TableDecoratorRowClasses}>{normalizedDecorator}</td>;
};

TableDecoratorRow.displayName = 'TableDecoratorRow';
TableDecoratorRow.propTypes = {
/**
* The CSS class names of the cell that wraps the underlying input control
*/
className: PropTypes.string,

/**
* **Experimental**: Provide a `decorator` component to be rendered inside the `TableDecoratorRow` component
*/
decorator: PropTypes.node,
};

export default TableDecoratorRow;
28 changes: 19 additions & 9 deletions packages/react/src/components/DataTable/TableExpandRow.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -73,12 +73,18 @@ const TableExpandRow = React.forwardRef(
) => {
const prefix = usePrefix();

// We need to put the slug before the expansion arrow and all other table cells after the arrow.
let rowHasSlug;
const slug = React.Children.toArray(children).map((child: any) => {
if (child.type?.displayName === 'TableSlugRow') {
if (child.props.slug) {
rowHasSlug = true;
// We need to put the AILabel and Decorator before the expansion arrow and all other table cells after the arrow.
let rowHasAILabel;
const decorator = React.Children.toArray(children).map((child: any) => {
if (
child.type?.displayName === 'TableSlugRow' ||
child.type?.displayName === 'TableDecoratorRow'
) {
if (
child.props.slug ||
child.props.decorator?.type.displayName === 'AILabel'
) {
rowHasAILabel = true;
}

return child;
Expand All @@ -87,7 +93,10 @@ const TableExpandRow = React.forwardRef(

const normalizedChildren = React.Children.toArray(children).map(
(child: any) => {
if (child.type?.displayName !== 'TableSlugRow') {
if (
child.type?.displayName !== 'TableSlugRow' &&
child.type?.displayName !== 'TableDecoratorRow'
) {
return child;
}
}
Expand All @@ -98,15 +107,16 @@ const TableExpandRow = React.forwardRef(
[`${prefix}--parent-row`]: true,
[`${prefix}--expandable-row`]: isExpanded,
[`${prefix}--data-table--selected`]: isSelected,
[`${prefix}--data-table--slug-row`]: rowHasSlug,
[`${prefix}--data-table--slug-row ${prefix}--data-table--ai-label-row`]:
rowHasAILabel,
},
rowClassName
);
const previousValue = isExpanded ? 'collapsed' : undefined;

return (
<tr {...rest} ref={ref as never} className={className} data-parent-row>
{slug}
{decorator}
preetibansalui marked this conversation as resolved.
Show resolved Hide resolved
<TableCell
className={`${prefix}--table-expand`}
data-previous-value={previousValue}
Expand Down
57 changes: 43 additions & 14 deletions packages/react/src/components/DataTable/TableHeader.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -114,10 +114,16 @@ interface TableHeaderProps
scope?: string;

/**
* **Experimental**: Provide a `Slug` component to be rendered inside the `TableSlugRow` component
* @deprecated please use decorator instead.
* Provide a `Slug` component to be rendered inside the `TableSlugRow` component
*/
slug?: ReactNode;

/**
* **Experimental**: Provide a `decorator` component to be rendered inside the `TableDecoratorRow` component
*/
decorator?: ReactNode;

/**
* Specify which direction we are currently sorting by, should be one of DESC,
* NONE, or ASC.
Expand All @@ -130,6 +136,7 @@ const TableHeader = React.forwardRef(function TableHeader(
className: headerClassName,
children,
colSpan,
decorator,
isSortable = false,
isSortHeader,
onClick,
Expand All @@ -145,19 +152,32 @@ const TableHeader = React.forwardRef(function TableHeader(
const prefix = usePrefix();
const uniqueId = useId('table-sort');

// Slug is always size `mini`
const slugRef = useRef<HTMLInputElement>(null);
let normalizedSlug;
if (slug) {
normalizedSlug = React.cloneElement(slug as React.ReactElement<any>, {
size: 'mini',
ref: slugRef,
});
// AILabel is always size `mini`
const AILableRef = useRef<HTMLInputElement>(null);

let colHasAILabel;
let normalizedDecorator = React.isValidElement(slug ?? decorator)
? (slug ?? decorator)
: null;
if (
normalizedDecorator &&
normalizedDecorator['type']?.displayName === 'AILabel'
) {
colHasAILabel = true;
normalizedDecorator = React.cloneElement(
normalizedDecorator as React.ReactElement<any>,
{
size: 'mini',
ref: AILableRef,
}
);
}

const headerLabelClassNames = classNames({
[`${prefix}--table-header-label`]: true,
[`${prefix}--table-header-label--slug`]: slug,
[`${prefix}--table-header-label--slug ${prefix}--table-header-label--ai-label`]:
colHasAILabel,
[`${prefix}--table-header-label--decorator`]: decorator,
});

if (!isSortable) {
Expand All @@ -172,7 +192,9 @@ const TableHeader = React.forwardRef(function TableHeader(
{children ? (
<div className={headerLabelClassNames}>
{children}
{normalizedSlug}
<div className={`${prefix}--table-header-label--decorator-inner`}>
{normalizedDecorator}
</div>
</div>
) : null}
</th>
Expand All @@ -198,11 +220,16 @@ const TableHeader = React.forwardRef(function TableHeader(
});

const headerClasses = cx(headerClassName, `${prefix}--table-sort__header`, {
[`${prefix}--table-sort__header--slug`]: slug,
[`${prefix}--table-sort__header--ai-label`]: colHasAILabel,
[`${prefix}--table-sort__header--decorator`]: decorator,
});

const handleClick = (evt) => {
if (slug && slugRef.current && slugRef.current.contains(evt.target)) {
if (
colHasAILabel &&
AILableRef.current &&
AILableRef.current.contains(evt.target)
) {
return;
} else if (onClick) {
return onClick(evt);
Expand Down Expand Up @@ -233,7 +260,9 @@ const TableHeader = React.forwardRef(function TableHeader(
size={20}
className={`${prefix}--table-sort__icon-unsorted`}
/>
{normalizedSlug}
<div className={`${prefix}--table-header-label--decorator-inner`}>
{normalizedDecorator}
</div>
</span>
</button>
</th>
Expand Down
Loading
Loading