Skip to content

Commit

Permalink
feat: Implement render function for DataGridRow (microsoft#25495)
Browse files Browse the repository at this point in the history
* feat: Implement render function for DataGridRow

Fixes microsoft#25458

* changefile

* feat: createColumn implementation

* update md

* changefile

* make column definitions visible in

* fix data grid story
  • Loading branch information
ling1726 authored and Hotell committed Feb 9, 2023
1 parent f64c0fe commit 8371543
Show file tree
Hide file tree
Showing 25 changed files with 513 additions and 248 deletions.
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
{
"type": "patch",
"comment": "feat: Exports `createColumn` utility from `@fluentui/react-table` as unstable",
"packageName": "@fluentui/react-components",
"email": "[email protected]",
"dependentChangeType": "patch"
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
{
"type": "prerelease",
"comment": "BREAKING: ColumnDefinition type is stricter, use createColumn to create column definition. Implments render function for DataGridRow.",
"packageName": "@fluentui/react-table",
"email": "[email protected]",
"dependentChangeType": "patch"
}
Original file line number Diff line number Diff line change
Expand Up @@ -63,6 +63,8 @@ import { ComboboxOpenEvents } from '@fluentui/react-combobox';
import { ComboboxProps } from '@fluentui/react-combobox';
import { ComboboxSlots } from '@fluentui/react-combobox';
import { ComboboxState } from '@fluentui/react-combobox';
import { createColumn } from '@fluentui/react-table';
import { CreateColumnOptions } from '@fluentui/react-table';
import { DATA_OVERFLOW_ITEM } from '@fluentui/react-overflow';
import { DATA_OVERFLOW_MENU } from '@fluentui/react-overflow';
import { DATA_OVERFLOWING } from '@fluentui/react-overflow';
Expand Down Expand Up @@ -421,6 +423,10 @@ export { ComboboxSlots }

export { ComboboxState }

export { createColumn }

export { CreateColumnOptions }

export { DATA_OVERFLOW_ITEM }

export { DATA_OVERFLOW_MENU }
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -235,6 +235,7 @@ export {
useTable,
useSelection,
useSort,
createColumn,
} from '@fluentui/react-table';

export type {
Expand Down Expand Up @@ -274,6 +275,7 @@ export type {
RowId,
ColumnDefinition,
ColumnId,
CreateColumnOptions,
} from '@fluentui/react-table';

export {
Expand Down
28 changes: 26 additions & 2 deletions packages/react-components/react-table/etc/react-table.api.md
Original file line number Diff line number Diff line change
Expand Up @@ -15,20 +15,42 @@ import type { ComponentState } from '@fluentui/react-utilities';
import type { ForwardRefComponent } from '@fluentui/react-utilities';
import type { Radio } from '@fluentui/react-radio';
import * as React_2 from 'react';
import { ReactNode } from 'react';
import type { Slot } from '@fluentui/react-utilities';
import type { SlotClassNames } from '@fluentui/react-utilities';

// @public (undocumented)
export type CellRenderFunction = (column: ColumnDefinition<any>) => React_2.ReactNode;

// @public (undocumented)
export interface ColumnDefinition<TItem> {
// (undocumented)
columnId: ColumnId;
// (undocumented)
compare?: (a: TItem, b: TItem) => number;
compare: (a: TItem, b: TItem) => number;
// (undocumented)
renderCell: (item: TItem) => React_2.ReactNode;
// (undocumented)
renderHeaderCell: () => React_2.ReactNode;
}

// @public (undocumented)
export type ColumnId = string | number;

// @public
export function createColumn<TItem>(options: CreateColumnOptions<TItem>): {
columnId: ColumnId;
renderCell: (item: TItem) => ReactNode;
renderHeaderCell: () => ReactNode;
compare: (a: TItem, b: TItem) => number;
};

// @public (undocumented)
export interface CreateColumnOptions<TItem> extends Partial<ColumnDefinition<TItem>> {
// (undocumented)
columnId: ColumnId;
}

// @public
export const DataGrid: ForwardRefComponent<DataGridProps>;

Expand Down Expand Up @@ -115,7 +137,9 @@ export const DataGridRow: ForwardRefComponent<DataGridRowProps>;
export const dataGridRowClassNames: SlotClassNames<DataGridRowSlots>;

// @public
export type DataGridRowProps = TableRowProps;
export type DataGridRowProps = Omit<TableRowProps, 'children'> & {
children: CellRenderFunction;
};

// @public (undocumented)
export type DataGridRowSlots = TableRowSlots;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -3,10 +3,11 @@ import { render } from '@testing-library/react';
import { DataGrid } from './DataGrid';
import { isConformant } from '../../testing/isConformant';
import { DataGridProps } from './DataGrid.types';
import { ColumnDefinition, RowState } from '../../hooks';
import { ColumnDefinition, createColumn, RowState } from '../../hooks';
import { DataGridBody } from '../DataGridBody/DataGridBody';
import { DataGridRow } from '../DataGridRow/DataGridRow';
import { DataGridCell } from '../DataGridCell/DataGridCell';
import { DataGridHeader } from '../DataGridHeader/DataGridHeader';

describe('DataGrid', () => {
isConformant<DataGridProps>({
Expand All @@ -20,7 +21,11 @@ describe('DataGrid', () => {
third: string;
}

const testColumns: ColumnDefinition<Item>[] = [{ columnId: 'first' }, { columnId: 'second' }, { columnId: 'third' }];
const testColumns: ColumnDefinition<Item>[] = [
createColumn({ columnId: 'first', renderHeaderCell: () => 'first', renderCell: item => item.first }),
createColumn({ columnId: 'second', renderHeaderCell: () => 'second', renderCell: item => item.second }),
createColumn({ columnId: 'third', renderHeaderCell: () => 'third', renderCell: item => item.third }),
];
const testItems: Item[] = [
{ first: 'first', second: 'second', third: 'third' },
{ first: 'first', second: 'second', third: 'third' },
Expand All @@ -30,12 +35,15 @@ describe('DataGrid', () => {
it('renders a default state', () => {
const result = render(
<DataGrid items={testItems} columns={testColumns}>
<DataGridHeader>
<DataGridRow>
{({ renderHeaderCell, columnId }) => <DataGridCell key={columnId}>{renderHeaderCell()}</DataGridCell>}
</DataGridRow>
</DataGridHeader>
<DataGridBody>
{({ item, rowId }: RowState<Item>) => (
<DataGridRow key={rowId}>
<DataGridCell>{item.first}</DataGridCell>
<DataGridCell>{item.second}</DataGridCell>
<DataGridCell>{item.third}</DataGridCell>
{({ renderCell, columnId }) => <DataGridCell key={columnId}>{renderCell(item)}</DataGridCell>}
</DataGridRow>
)}
</DataGridBody>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,34 @@ exports[`DataGrid renders a default state 1`] = `
class="fui-DataGrid fui-Table"
role="table"
>
<div
class="fui-DataGridHeader fui-TableHeader"
role="rowgroup"
>
<div
class="fui-DataGridRow fui-TableRow"
role="row"
>
<div
class="fui-DataGridCell fui-TableCell"
role="cell"
>
first
</div>
<div
class="fui-DataGridCell fui-TableCell"
role="cell"
>
second
</div>
<div
class="fui-DataGridCell fui-TableCell"
role="cell"
>
third
</div>
</div>
</div>
<div
class="fui-DataGridBody fui-TableBody"
role="rowgroup"
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@ describe('DataGridRow', () => {
// TODO add more tests here, and create visual regression tests in /apps/vr-tests

it('renders a default state', () => {
const result = render(<DataGridRow>Default DataGridRow</DataGridRow>);
const result = render(<DataGridRow>{() => 'foo'}</DataGridRow>);
expect(result.container).toMatchSnapshot();
});
});
Original file line number Diff line number Diff line change
@@ -1,11 +1,20 @@
import * as React from 'react';
import { ColumnDefinition } from '../../hooks';
import { TableRowProps, TableRowSlots, TableRowState } from '../TableRow/TableRow.types';

export type DataGridRowSlots = TableRowSlots;

// Use any here since we can't know the user types
// The user is responsible for narrowing the type downstream
// eslint-disable-next-line @typescript-eslint/no-explicit-any
export type CellRenderFunction = (column: ColumnDefinition<any>) => React.ReactNode;

/**
* DataGridRow Props
*/
export type DataGridRowProps = TableRowProps;
export type DataGridRowProps = Omit<TableRowProps, 'children'> & {
children: CellRenderFunction;
};

/**
* State used in rendering DataGridRow
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -5,8 +5,6 @@ exports[`DataGridRow renders a default state 1`] = `
<div
class="fui-DataGridRow fui-TableRow"
role="row"
>
Default DataGridRow
</div>
/>
</div>
`;
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
import * as React from 'react';
import type { DataGridRowProps, DataGridRowState } from './DataGridRow.types';
import { useTableRow_unstable } from '../TableRow/useTableRow';
import { useDataGridContext_unstable } from '../../contexts/dataGridContext';

/**
* Create the state required to render DataGridRow.
Expand All @@ -12,5 +13,12 @@ import { useTableRow_unstable } from '../TableRow/useTableRow';
* @param ref - reference to root HTMLElement of DataGridRow
*/
export const useDataGridRow_unstable = (props: DataGridRowProps, ref: React.Ref<HTMLElement>): DataGridRowState => {
return useTableRow_unstable({ ...props, as: 'div' }, ref);
const columnDefs = useDataGridContext_unstable(ctx => ctx.columns);

const cellRenderFunction = props.children;
const children = columnDefs.map(columnDef => {
return cellRenderFunction(columnDef);
});

return useTableRow_unstable({ ...props, children, as: 'div' }, ref);
};
42 changes: 42 additions & 0 deletions packages/react-components/react-table/src/hooks/createColumn.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,42 @@
import { CreateColumnOptions } from './types';

const defaultCompare = () => 0;

const defaultRenderCell = () => {
if (process.env.NODE_ENV !== 'production') {
// eslint-disable-next-line no-console
console.warn('@fluentui/react-table: You are using the default column renderCell function that renders null');
}

return null;
};

const defaultRenderHeaderCell = () => {
if (process.env.NODE_ENV !== 'production') {
// eslint-disable-next-line no-console
console.warn('@fluentui/react-table: You are using the default column renderHeaderCell function that renders null');
}

return null;
};

/**
* Helper function to create column definition with defaults
* @param options - column definition options
* @returns - column definition with defaults
*/
export function createColumn<TItem>(options: CreateColumnOptions<TItem>) {
const {
columnId,
renderCell = defaultRenderCell,
renderHeaderCell = defaultRenderHeaderCell,
compare = defaultCompare,
} = options;

return {
columnId,
renderCell,
renderHeaderCell,
compare,
};
}
1 change: 1 addition & 0 deletions packages/react-components/react-table/src/hooks/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,3 +2,4 @@ export * from './types';
export * from './useTable';
export * from './useSort';
export * from './useSelection';
export * from './createColumn';
9 changes: 8 additions & 1 deletion packages/react-components/react-table/src/hooks/types.ts
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
import * as React from 'react';
import { SortDirection } from '../components/Table/Table.types';

export type RowId = string | number;
Expand All @@ -9,9 +10,15 @@ export interface SortState {
sortDirection: SortDirection;
}

export interface CreateColumnOptions<TItem> extends Partial<ColumnDefinition<TItem>> {
columnId: ColumnId;
}

export interface ColumnDefinition<TItem> {
columnId: ColumnId;
compare?: (a: TItem, b: TItem) => number;
compare: (a: TItem, b: TItem) => number;
renderHeaderCell: () => React.ReactNode;
renderCell: (item: TItem) => React.ReactNode;
}

export type RowEnhancer<TItem, TRowState extends RowState<TItem> = RowState<TItem>> = (
Expand Down
Loading

0 comments on commit 8371543

Please sign in to comment.