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

feat(DataTable): Update DataTable to phase 2 spec #15662

Merged
merged 17 commits into from Feb 8, 2024
Merged
Show file tree
Hide file tree
Changes from 12 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
Expand Up @@ -7,6 +7,7 @@ Array [
"aiAuraHoverEnd",
"aiAuraHoverStart",
"aiAuraStart",
"aiAuraStartTable",
"aiBorderEnd",
"aiBorderStart",
"aiBorderStrong",
Expand Down
10 changes: 0 additions & 10 deletions packages/react/__tests__/__snapshots__/PublicAPI-test.js.snap
Expand Up @@ -1938,11 +1938,6 @@ Map {
},
"TableCell": Object {
"displayName": "TableCell",
"propTypes": Object {
"className": Object {
"type": "string",
},
},
},
"TableContainer": Object {
"propTypes": Object {
Expand Down Expand Up @@ -7725,11 +7720,6 @@ Map {
},
"TableCell" => Object {
"displayName": "TableCell",
"propTypes": Object {
"className": Object {
"type": "string",
},
},
},
"TableContainer" => Object {
"propTypes": Object {
Expand Down
21 changes: 21 additions & 0 deletions packages/react/src/components/DataTable/DataTable.tsx
Expand Up @@ -91,6 +91,7 @@ export interface DataTableRow<ColTypes extends any[]> {
export interface DataTableHeader {
key: string;
header: React.ReactNode;
slug: React.ReactElement;
}

export interface DataTableRenderProps<RowType, ColTypes extends any[]> {
Expand Down Expand Up @@ -189,6 +190,10 @@ export interface DataTableRenderProps<RowType, ColTypes extends any[]> {
stickyHeader?: boolean;
useStaticWidth?: boolean;
};
getCellProps: (getCellPropsArgs: { cell: DataTableCell<ColTypes> }) => {
[key: string]: unknown;
hasSlugHeader?: boolean;
};

// Custom event handlers
onInputChange: (
Expand Down Expand Up @@ -456,6 +461,7 @@ class DataTable<RowType, ColTypes extends any[]> extends React.Component<
sortDirection,
isSortable,
isSortHeader: sortHeaderKey === header.key,
slug: header.slug,
onClick: (event) => {
const nextSortState = getNextSortState(this.props, this.state, {
key: header.key,
Expand Down Expand Up @@ -715,6 +721,20 @@ class DataTable<RowType, ColTypes extends any[]> extends React.Component<
};
};

/**
* Get the props associated with the given table cell.
*
* @param {object} config
* @param {object} config.cell the cell we want the props for
* @returns {object}
*/
getCellProps = ({ cell, ...rest }) => {
return {
...rest,
hasSlugHeader: cell.hasSlugHeader,
};
};

/**
* Helper utility to get all the currently selected rows
* @returns {Array<string>} the array of rowIds that are currently selected
Expand Down Expand Up @@ -966,6 +986,7 @@ class DataTable<RowType, ColTypes extends any[]> extends React.Component<
getBatchActionProps: this.getBatchActionProps,
getTableProps: this.getTableProps,
getTableContainerProps: this.getTableContainerProps,
getCellProps: this.getCellProps,

// Custom event handlers
onInputChange: this.handleOnInputValueChange,
Expand Down
59 changes: 52 additions & 7 deletions packages/react/src/components/DataTable/TableCell.tsx
Expand Up @@ -5,14 +5,59 @@
* LICENSE file in the root directory of this source tree.
*/

import { TdHTMLAttributes } from 'react';
import wrapComponent from '../../tools/wrapComponent';
import React from 'react';
import classNames from 'classnames';
import { usePrefix } from '../../internal/usePrefix';
import { ReactAttr } from '../../types/common';

export type TableCellProps = TdHTMLAttributes<HTMLTableCellElement>;
interface TableCellProps extends ReactAttr<HTMLTableCellElement> {
/**
* Pass in children that will be embedded in the table header label
*/
children?: React.ReactNode;

const TableCell: React.FC<TableCellProps> = wrapComponent({
name: 'TableCell',
type: 'td',
});
/**
* Specify an optional className to be applied to the container node
*/
className?: string;

/**
* The width of the expanded row's internal cell
*/
colSpan?: number;

/**
* Specify if the table cell is in an AI column
*/
hasSlugHeader?: 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
*/
headers?: string;
}

const TableCell = ({
children,
className,
hasSlugHeader,
colSpan,
...rest
}: TableCellProps) => {
const prefix = usePrefix();

const tableCellClassNames = classNames(className, {
[`${prefix}--table-cell--column-slug`]: hasSlugHeader,
});
return (
<td
className={tableCellClassNames ? tableCellClassNames : undefined}
colSpan={colSpan}
{...rest}>
{children}
</td>
);
};

TableCell.displayName = 'TableCell';
export default TableCell;
Expand Up @@ -98,7 +98,7 @@ const TableExpandRow = React.forwardRef(
[`${prefix}--parent-row`]: true,
[`${prefix}--expandable-row`]: isExpanded,
[`${prefix}--data-table--selected`]: isSelected,
[`${prefix}--parent-row--slug`]: rowHasSlug,
[`${prefix}--data-table--slug-row`]: rowHasSlug,
},
rowClassName
);
Expand Down
2 changes: 1 addition & 1 deletion packages/react/src/components/DataTable/TableHeader.tsx
Expand Up @@ -234,12 +234,12 @@ const TableHeader = React.forwardRef(function TableHeader(
{...rest}>
<span className={`${prefix}--table-sort__flex`}>
<div className={`${prefix}--table-header-label`}>{children}</div>
{normalizedSlug}
<Arrow size={20} className={`${prefix}--table-sort__icon`} />
<Arrows
size={20}
className={`${prefix}--table-sort__icon-unsorted`}
/>
{normalizedSlug}
</span>
</button>
</th>
Expand Down
13 changes: 13 additions & 0 deletions packages/react/src/components/DataTable/TableRow.tsx
Expand Up @@ -25,11 +25,24 @@ export interface TableRowProps extends ReactAttr<HTMLTableRowElement> {

const TableRow = (props: TableRowProps) => {
const prefix = usePrefix();

let rowHasSlug;
if (props?.children) {
React.Children.toArray(props.children).map((child: any) => {
if (child.type?.displayName === 'TableSlugRow') {
if (child.props.slug) {
rowHasSlug = true;
}
}
});
}
// Remove unnecessary props if provided to this component, these are
// only useful in `TableExpandRow`
const className = cx(props.className, {
[`${prefix}--data-table--selected`]: props.isSelected,
[`${prefix}--data-table--slug-row`]: rowHasSlug,
});

const cleanProps = {
...omit(props, [
'ariaLabel',
Expand Down
Expand Up @@ -51,6 +51,64 @@ export default {
},
};

const columnSlugHeaders = [
{
key: 'name',
header: 'Name',
},
{
key: 'protocol',
header: 'Protocol',
},
{
key: 'port',
header: 'Port',
},
{
key: 'rule',
header: 'Rule',
},
{
key: 'attached_groups',
header: 'Attached groups',
slug: (
<Slug className="slug-container" autoAlign={false} align="bottom-right">
<SlugContent>
<div>
<p className="secondary">AI Explained</p>
<h1>84%</h1>
<p className="secondary bold">Confidence score</p>
<p className="secondary">
Lorem ipsum dolor sit amet, di os consectetur adipiscing elit, sed
do eiusmod tempor incididunt ut fsil labore et dolore magna
aliqua.
</p>
<hr />
<p className="secondary">Model type</p>
<p className="bold">Foundation model</p>
</div>
<SlugActions>
<IconButton kind="ghost" label="View">
<View />
</IconButton>
<IconButton kind="ghost" label="Open Folder">
<FolderOpen />
</IconButton>
<IconButton kind="ghost" label="Folders">
<Folders />
</IconButton>
<Button>View details</Button>
</SlugActions>
</SlugContent>
</Slug>
),
},
{
key: 'status',
header: 'Status',
},
];

const slug = (
<Slug className="slug-container">
<SlugContent>
Expand Down Expand Up @@ -82,37 +140,6 @@ const slug = (
</Slug>
);

const columnSlug = (
<Slug className="slug-container" autoAlign={false} align="bottom-right">
<SlugContent>
<div>
<p className="secondary">AI Explained</p>
<h1>84%</h1>
<p className="secondary bold">Confidence score</p>
<p className="secondary">
Lorem ipsum dolor sit amet, di os consectetur adipiscing elit, sed do
eiusmod tempor incididunt ut fsil labore et dolore magna aliqua.
</p>
<hr />
<p className="secondary">Model type</p>
<p className="bold">Foundation model</p>
</div>
<SlugActions>
<IconButton kind="ghost" label="View">
<View />
</IconButton>
<IconButton kind="ghost" label="Open Folder">
<FolderOpen />
</IconButton>
<IconButton kind="ghost" label="Folders">
<Folders />
</IconButton>
<Button>View details</Button>
</SlugActions>
</SlugContent>
</Slug>
);

export const SlugWithSelection = () => (
<DataTable rows={rows} headers={headers}>
{({
Expand Down Expand Up @@ -325,7 +352,7 @@ export const SlugWithExpansion = () => (
);

export const ColumnSlugWithSelectionAndExpansion = () => (
<DataTable rows={rows} headers={headers}>
<DataTable rows={rows} headers={columnSlugHeaders}>
{({
rows,
headers,
Expand All @@ -336,6 +363,7 @@ export const ColumnSlugWithSelectionAndExpansion = () => (
getSelectionProps,
getTableProps,
getTableContainerProps,
getCellProps,
}) => (
<TableContainer
title="DataTable"
Expand All @@ -352,9 +380,10 @@ export const ColumnSlugWithSelectionAndExpansion = () => (
<TableSelectAll {...getSelectionProps()} />
{headers.map((header, i) => (
<TableHeader
slug={i === 1 ? columnSlug : null}
key={i}
{...getHeaderProps({ header })}>
{...getHeaderProps({
header,
})}>
{header.header}
</TableHeader>
))}
Expand All @@ -367,8 +396,11 @@ export const ColumnSlugWithSelectionAndExpansion = () => (
<TableExpandRow {...getRowProps({ row })}>
<TableSelectRow {...getSelectionProps({ row })} />
{row.cells.map((cell) => {
console.log(cell);
return <TableCell key={cell.id}>{cell.value}</TableCell>;
return (
<TableCell {...getCellProps({ cell })} key={cell.id}>
{cell.value}
</TableCell>
);
})}
</TableExpandRow>
<TableExpandedRow
Expand All @@ -389,19 +421,25 @@ export const ColumnSlugWithSelectionAndExpansion = () => (
);

export const ColumnSlugSort = () => (
<DataTable rows={rows} headers={headers}>
{({ rows, headers, getHeaderProps, getRowProps, getTableProps }) => (
<DataTable rows={rows} headers={columnSlugHeaders}>
{({
rows,
headers,
getHeaderProps,
getRowProps,
getTableProps,
getCellProps,
}) => (
<TableContainer
title="DataTable"
description="With sorting"
className="slug-column-table">
<Table {...getTableProps()} aria-label="sample table">
<TableHead>
<TableRow>
{headers.map((header, i) => (
{headers.map((header) => (
<TableHeader
key={header.key}
slug={i === 4 ? columnSlug : null}
{...getHeaderProps({ header, isSortable: true })}>
{header.header}
</TableHeader>
Expand All @@ -412,7 +450,9 @@ export const ColumnSlugSort = () => (
{rows.map((row) => (
<TableRow key={row.id} {...getRowProps({ row })}>
{row.cells.map((cell) => (
<TableCell key={cell.id}>{cell.value}</TableCell>
<TableCell {...getCellProps({ cell })} key={cell.id}>
{cell.value}
</TableCell>
))}
</TableRow>
))}
Expand Down