diff --git a/src/components/Table/README.md b/src/components/Table/README.md index 9f5a1c8b63..0b5f5e8379 100644 --- a/src/components/Table/README.md +++ b/src/components/Table/README.md @@ -29,6 +29,7 @@ Additional functionality is enabled via HOCs: | data | Data | `any[]` | | | columns | Column parameters | `TableColumnConfig[]` | | | verticalAlign | Vertical alignment of contents | `"top"` `"middle"` | | +| getRowDescriptor | Row mouseleave handler | `(item: any, index: number) => DescriptorType` | | | getRowId | The row ID, used when selecting and sorting rows. If you skip a row, its ID will be the value of the field in the row data with the same name as the column ID | `string` `((item: any, index: number) => string)` | | | getRowClassNames | Row CSS classes | `(item: any, index: number) => string[]` | | | isRowDisabled | Condition for disabling columns | `(item: any, index: number) => boolean` | | @@ -41,6 +42,14 @@ Additional functionality is enabled via HOCs: | stickyHorizontalScroll | A horizontal sticky scroll in a table. NB: A table cannot have a fixed height and a sticky scroll at the same time. A sticky scroll will not work if the table has an overflow. | `boolean` | `false` | | stickyHorizontalScrollBreakpoint | The threshold that the parent block should reach before making a scroll sticky. This is useful in the console, for example, when the groupActions bar closes the scroll. | `number` | `0` | +### DescriptorType + +| Name | Description | Type | Default | +| :--------- | :----------------------------------------------- | :---------------------: | :---------: | +| id | The row ID, used when selecting and sorting rows | `string` `undefined` | `undefined` | +| disabled | Column ID | `boolean` `undefined` | `undefined` | +| classNames | Row CSS classes | ` string[]` `undefined` | `undefined` | + ### TableColumnConfig | Name | Description | Type | Default | diff --git a/src/components/Table/Table.tsx b/src/components/Table/Table.tsx index a462982e3e..d62a1944a0 100644 --- a/src/components/Table/Table.tsx +++ b/src/components/Table/Table.tsx @@ -50,6 +50,24 @@ export interface TableColumnConfig { meta?: Record; } +export interface DescriptorType { + /** + * Row ID. + * Used when selecting and sorting rows. + */ + id?: string; + + /** + * Row CSS classes. + */ + classNames?: string[]; + + /** + * Condition for disabling columns. + */ + disabled?: boolean; +} + // TODO: Replace @default in props description with defaultProps in order to work with Storybook. export interface TableProps extends QAProps { /** Data */ @@ -75,15 +93,32 @@ export interface TableProps extends QAProps { */ stickyHorizontalScrollBreakpoint?: number; /** + * @deprecated Use getRowDescriptor instead + * * Row ID. * Used when selecting and sorting rows. If you pass a row, * its ID will be the value of the field in the row data named the same as the column ID. */ getRowId?: string | ((item: I, index: number) => string); - /** Row CSS classes. */ + /** + * @deprecated Use getRowDescriptor instead + * + * Row CSS classes. + * */ getRowClassNames?: (item: I, index: number) => string[]; - /** Condition for disabling columns. */ + /** + * @deprecated Use getRowDescriptor instead + * + * Condition for disabling columns. + * */ isRowDisabled?: (item: I, index: number) => boolean; + + /** + * + * @returns {DescriptorType} {@link DescriptorType} + */ + getRowDescriptor?: (item: I, index: number) => DescriptorType | undefined; + /** Row click handler. When passed row's hover is visible. */ onRowClick?: (item: I, index: number, event: React.MouseEvent) => void; /** Row mouseenter handler. */ @@ -124,9 +159,15 @@ export class Table> extends Rea // Static methods may be used by HOCs static getRowId(props: TableProps, item: I, rowIndex?: number) { - const {data, getRowId} = props; + const {data, getRowId, getRowDescriptor} = props; const index = rowIndex ?? data.indexOf(item); + const descriptor = getRowDescriptor?.(item, index); + + if (descriptor?.id !== undefined) { + return descriptor.id; + } + if (typeof getRowId === 'function') { return getRowId(item, index); } @@ -404,12 +445,18 @@ export class Table> extends Rea verticalAlign, edgePadding, wordWrap, + getRowDescriptor, } = this.props; const {columnsStyles} = this.state; - const disabled = isRowDisabled ? isRowDisabled(item, rowIndex) : false; + const descriptor = getRowDescriptor?.(item, rowIndex); + + const disabled = descriptor?.disabled || isRowDisabled?.(item, rowIndex) || false; + + const additionalClassNames = + descriptor?.classNames || getRowClassNames?.(item, rowIndex) || []; + const interactive = Boolean(!disabled && onRowClick); - const additionalClassNames = getRowClassNames ? getRowClassNames(item, rowIndex) : []; return ( >; -const DefaultTemplate: StoryFn> = (args) => ; +const DefaultTemplate: StoryFn> = (args) => { + return
; +}; export const Default = DefaultTemplate.bind({}); const EmptyDefaultTemplate: StoryFn> = (args) =>
; diff --git a/src/components/Table/__tests__/Table.test.tsx b/src/components/Table/__tests__/Table.test.tsx index 8c524fe857..907c4b7670 100644 --- a/src/components/Table/__tests__/Table.test.tsx +++ b/src/components/Table/__tests__/Table.test.tsx @@ -3,7 +3,7 @@ import React from 'react'; import {render, screen, within} from '@testing-library/react'; import userEvent from '@testing-library/user-event'; -import {Table} from '../Table'; +import {Table, TableProps} from '../Table'; import {columns, data} from './utils'; @@ -152,4 +152,37 @@ describe('Table', () => { expect(table).toHaveStyle(style); }); + + test('getRowDescriptor property case', () => { + const EXPTECTED_CLASS_NAME = 'EXPTECTED_CLASS_NAME'; + const EXPECTED_ID = 'EXPECTED_ID'; + + const ROW_ID = 1; + + const getRowDescriptor: TableProps['getRowDescriptor'] = (_, index) => { + if (index === ROW_ID) { + return {classNames: [EXPTECTED_CLASS_NAME], disabled: true, id: EXPECTED_ID}; + } + + return undefined; + }; + + render( +
, + ); + + const table = screen.getByTestId(qaId); + const tbody = within(table).getAllByRole('rowgroup'); + const rows = within(tbody[1]).getAllByRole('row'); + + expect(rows.length).toBe(data.length); + expect(rows.length > 1).toBe(true); + + rows.forEach((row, i) => { + const expectedFlag = ROW_ID === i; + + expect(row.className.includes(EXPTECTED_CLASS_NAME)).toBe(expectedFlag); + expect(row.className.includes('yc-table__row_disabled')).toBe(expectedFlag); + }); + }); }); diff --git a/src/components/Table/hoc/withTableActions/withTableActions.tsx b/src/components/Table/hoc/withTableActions/withTableActions.tsx index 7e1f390594..3fdbdf364e 100644 --- a/src/components/Table/hoc/withTableActions/withTableActions.tsx +++ b/src/components/Table/hoc/withTableActions/withTableActions.tsx @@ -115,14 +115,15 @@ export function withTableActions( } private renderBodyCell = (item: I, index: number) => { - const {isRowDisabled, getRowActions, rowActionsSize} = this.props; + const {isRowDisabled, getRowActions, rowActionsSize, getRowDescriptor} = this.props; const actions = getRowActions(item, index); if (actions.length === 0) { return null; } - const disabled = isRowDisabled ? isRowDisabled(item, index) : false; + const disabled = + getRowDescriptor?.(item, index)?.disabled || isRowDisabled?.(item, index) || false; return (
diff --git a/src/components/Table/hoc/withTableSelection/withTableSelection.tsx b/src/components/Table/hoc/withTableSelection/withTableSelection.tsx index 2cce5bb006..6a068dc8a2 100644 --- a/src/components/Table/hoc/withTableSelection/withTableSelection.tsx +++ b/src/components/Table/hoc/withTableSelection/withTableSelection.tsx @@ -40,7 +40,7 @@ export function withTableSelection( onSelectionChange, // eslint-disable-line @typescript-eslint/no-unused-vars columns, onRowClick, - getRowClassNames, + getRowDescriptor, ...restTableProps } = this.props; @@ -49,7 +49,7 @@ export function withTableSelection( {...(restTableProps as Omit, 'columns'> & E)} columns={this.enhanceColumns(columns)} onRowClick={this.enhanceOnRowClick(onRowClick)} - getRowClassNames={this.enhanceGetRowClassNames(getRowClassNames)} + getRowDescriptor={this.enhanceGetRowDescriptor(getRowDescriptor)} /> ); } @@ -201,13 +201,15 @@ export function withTableSelection( ); // eslint-disable-next-line @typescript-eslint/member-ordering - private enhanceGetRowClassNames = _memoize( - (getRowClassNames?: (item: I, index: number) => string[]) => { + private enhanceGetRowDescriptor = _memoize( + (getRowDescriptor?: TableProps['getRowDescriptor']) => { return (item: I, index: number) => { - const {selectedIds} = this.props; - const classNames = getRowClassNames - ? getRowClassNames(item, index).slice() - : []; + const {selectedIds, getRowClassNames} = this.props; + const classNames = + getRowDescriptor?.(item, index)?.classNames?.slice() || + getRowClassNames?.(item, index) || + []; + const id = Table.getRowId(this.props, item, index); const selected = selectedIds.includes(id); @@ -219,11 +221,13 @@ export function withTableSelection( ); private isDisabled = (item: I, index: number) => { - const {isRowDisabled, isRowSelectionDisabled} = this.props; + const {isRowDisabled, isRowSelectionDisabled, getRowDescriptor} = this.props; if (isRowSelectionDisabled && isRowSelectionDisabled(item, index)) { return true; } - return isRowDisabled ? isRowDisabled(item, index) : false; + return ( + getRowDescriptor?.(item, index)?.disabled || isRowDisabled?.(item, index) || false + ); }; }; }