Skip to content

Commit

Permalink
feat: Implement child render function for DataGrid rows (#25476)
Browse files Browse the repository at this point in the history
* feat: Implement child render function for DataGrid rows

* changefile

* update package.json

* update unit tests
  • Loading branch information
ling1726 authored Nov 2, 2022
1 parent 072f041 commit 48c8520
Show file tree
Hide file tree
Showing 18 changed files with 364 additions and 28 deletions.
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
{
"type": "prerelease",
"comment": "feat: Implement child render function for DataGrid rows",
"packageName": "@fluentui/react-table",
"email": "[email protected]",
"dependentChangeType": "patch"
}
20 changes: 16 additions & 4 deletions packages/react-components/react-table/etc/react-table.api.md
Original file line number Diff line number Diff line change
Expand Up @@ -39,7 +39,9 @@ export const DataGridBody: ForwardRefComponent<DataGridBodyProps>;
export const dataGridBodyClassNames: SlotClassNames<DataGridBodySlots>;

// @public
export type DataGridBodyProps = TableBodyProps;
export type DataGridBodyProps = Omit<TableBodyProps, 'children'> & {
children: RowRenderFunction;
};

// @public (undocumented)
export type DataGridBodySlots = TableBodySlots;
Expand All @@ -66,7 +68,12 @@ export type DataGridCellState = TableCellState;
export const dataGridClassNames: SlotClassNames<DataGridSlots>;

// @public (undocumented)
export type DataGridContextValues = TableContextValues;
export type DataGridContextValue = HeadlessTableState<any>;

// @public (undocumented)
export type DataGridContextValues = TableContextValues & {
dataGrid: DataGridContextValue;
};

// @public
export const DataGridHeader: ForwardRefComponent<DataGridHeaderProps>;
Expand Down Expand Up @@ -99,7 +106,7 @@ export type DataGridHeaderSlots = TableHeaderSlots;
export type DataGridHeaderState = TableHeaderState;

// @public
export type DataGridProps = TableProps;
export type DataGridProps = TableProps & Pick<DataGridContextValue, 'items' | 'columns'>;

// @public
export const DataGridRow: ForwardRefComponent<DataGridRowProps>;
Expand Down Expand Up @@ -135,7 +142,9 @@ export type DataGridSelectionCellState = TableSelectionCellState;
export type DataGridSlots = TableSlots;

// @public
export type DataGridState = TableState;
export type DataGridState = TableState & {
tableState: HeadlessTableState<unknown>;
};

// @public (undocumented)
export interface HeadlessTableState<TItem> extends Pick<UseTableOptions<TItem>, 'items' | 'getRowId'> {
Expand Down Expand Up @@ -196,6 +205,9 @@ export const renderTableSelectionCell_unstable: (state: TableSelectionCellState)
// @public (undocumented)
export type RowId = string | number;

// @public (undocumented)
export type RowRenderFunction<TItem = any> = (row: RowState<TItem>) => React_2.ReactNode;

// @public (undocumented)
export interface RowState<TItem> {
item: TItem;
Expand Down
1 change: 1 addition & 0 deletions packages/react-components/react-table/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -33,6 +33,7 @@
"@fluentui/react-aria": "^9.3.0",
"@fluentui/react-avatar": "^9.2.5",
"@fluentui/react-checkbox": "^9.0.11",
"@fluentui/react-context-selector": "^9.1.0",
"@fluentui/react-icons": "^2.0.175",
"@fluentui/react-radio": "^9.0.10",
"@fluentui/react-tabster": "^9.2.1",
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -3,17 +3,44 @@ 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 { DataGridBody } from '../DataGridBody/DataGridBody';
import { DataGridRow } from '../DataGridRow/DataGridRow';
import { DataGridCell } from '../DataGridCell/DataGridCell';

describe('DataGrid', () => {
isConformant<DataGridProps>({
Component: DataGrid,
displayName: 'DataGrid',
});

// TODO add more tests here, and create visual regression tests in /apps/vr-tests
interface Item {
first: string;
second: string;
third: string;
}

const testColumns: ColumnDefinition<Item>[] = [{ columnId: 'first' }, { columnId: 'second' }, { columnId: 'third' }];
const testItems: Item[] = [
{ first: 'first', second: 'second', third: 'third' },
{ first: 'first', second: 'second', third: 'third' },
{ first: 'first', second: 'second', third: 'third' },
];

it('renders a default state', () => {
const result = render(<DataGrid>Default DataGrid</DataGrid>);
const result = render(
<DataGrid items={testItems} columns={testColumns}>
<DataGridBody>
{({ item, rowId }: RowState<Item>) => (
<DataGridRow key={rowId}>
<DataGridCell>{item.first}</DataGridCell>
<DataGridCell>{item.second}</DataGridCell>
<DataGridCell>{item.third}</DataGridCell>
</DataGridRow>
)}
</DataGridBody>
</DataGrid>,
);
expect(result.container).toMatchSnapshot();
});
});
Original file line number Diff line number Diff line change
@@ -1,15 +1,23 @@
import { TableContextValues, TableProps, TableSlots, TableState } from '../Table/Table.types';
import { TableState as HeadlessTableState } from '../../hooks';

export type DataGridSlots = TableSlots;

export type DataGridContextValues = TableContextValues;
export type DataGridContextValues = TableContextValues & {
dataGrid: DataGridContextValue;
};

// 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 DataGridContextValue = HeadlessTableState<any>;

/**
* DataGrid Props
*/
export type DataGridProps = TableProps;
export type DataGridProps = TableProps & Pick<DataGridContextValue, 'items' | 'columns'>;

/**
* State used in rendering DataGrid
*/
export type DataGridState = TableState;
export type DataGridState = TableState & { tableState: HeadlessTableState<unknown> };
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,80 @@ exports[`DataGrid renders a default state 1`] = `
class="fui-DataGrid fui-Table"
role="table"
>
Default DataGrid
<div
class="fui-DataGridBody fui-TableBody"
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
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
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>
</div>
`;
Original file line number Diff line number Diff line change
@@ -1,9 +1,15 @@
import * as React from 'react';
import type { DataGridContextValues, DataGridState } from './DataGrid.types';
import { renderTable_unstable } from '../Table/renderTable';
import { DataGridContextProvider } from '../../contexts/dataGridContext';

/**
* Render the final JSX of DataGrid
*/
export const renderDataGrid_unstable = (state: DataGridState, contextValues: DataGridContextValues) => {
return renderTable_unstable(state, contextValues);
return (
<DataGridContextProvider value={contextValues.dataGrid}>
{renderTable_unstable(state, contextValues)}
</DataGridContextProvider>
);
};
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
import * as React from 'react';
import type { DataGridProps, DataGridState } from './DataGrid.types';
import { useTable_unstable } from '../Table/useTable';
import { useTable } from '../../hooks/useTable';

/**
* Create the state required to render DataGrid.
Expand All @@ -12,5 +13,12 @@ import { useTable_unstable } from '../Table/useTable';
* @param ref - reference to root HTMLElement of DataGrid
*/
export const useDataGrid_unstable = (props: DataGridProps, ref: React.Ref<HTMLElement>): DataGridState => {
return useTable_unstable({ ...props, as: 'div' }, ref);
const { items, columns } = props;
const tableState = useTable({ items, columns }, []);
const baseTableState = useTable_unstable({ ...props, as: 'div' }, ref);

return {
...baseTableState,
tableState,
};
};
Original file line number Diff line number Diff line change
@@ -1,6 +1,12 @@
import { useTableContextValues_unstable } from '../Table/useTableContextValues';
import { DataGridState } from './DataGrid.types';
import { DataGridContextValues, DataGridState } from './DataGrid.types';

export function useDataGridContextValues_unstable(state: DataGridState) {
return useTableContextValues_unstable(state);
export function useDataGridContextValues_unstable(state: DataGridState): DataGridContextValues {
const tableContextValues = useTableContextValues_unstable(state);
return {
...tableContextValues,
dataGrid: {
...state.tableState,
},
};
}
Original file line number Diff line number Diff line change
Expand Up @@ -10,10 +10,8 @@ describe('DataGridBody', () => {
displayName: 'DataGridBody',
});

// TODO add more tests here, and create visual regression tests in /apps/vr-tests

it('renders a default state', () => {
const result = render(<DataGridBody>Default DataGridBody</DataGridBody>);
it('renders items from render function', () => {
const result = render(<DataGridBody>{() => 'foo'}</DataGridBody>);
expect(result.container).toMatchSnapshot();
});
});
Original file line number Diff line number Diff line change
@@ -1,11 +1,23 @@
import { TableBodySlots, TableBodyProps, TableBodyState } from '../TableBody/TableBody.types';
import * as React from 'react';
import type { RowState } from '../../hooks';
import type { TableBodySlots, TableBodyProps, TableBodyState } from '../TableBody/TableBody.types';

export type DataGridBodySlots = TableBodySlots;

// 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 RowRenderFunction<TItem = any> = (row: RowState<TItem>) => React.ReactNode;

/**
* DataGridBody Props
*/
export type DataGridBodyProps = TableBodyProps;
export type DataGridBodyProps = Omit<TableBodyProps, 'children'> & {
/**
* Render function for rows
*/
children: RowRenderFunction;
};

/**
* State used in rendering DataGridBody
Expand Down
Original file line number Diff line number Diff line change
@@ -1,12 +1,10 @@
// Jest Snapshot v1, https://goo.gl/fbAQLP

exports[`DataGridBody renders a default state 1`] = `
exports[`DataGridBody renders items from render function 1`] = `
<div>
<div
class="fui-DataGridBody fui-TableBody"
role="rowgroup"
>
Default DataGridBody
</div>
/>
</div>
`;
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
import * as React from 'react';
import type { DataGridBodyProps, DataGridBodyState } from './DataGridBody.types';
import { useTableBody_unstable } from '../TableBody/useTableBody';
import { useDataGridContext_unstable } from '../../contexts/dataGridContext';

/**
* Create the state required to render DataGridBody.
Expand All @@ -12,5 +13,10 @@ import { useTableBody_unstable } from '../TableBody/useTableBody';
* @param ref - reference to root HTMLElement of DataGridBody
*/
export const useDataGridBody_unstable = (props: DataGridBodyProps, ref: React.Ref<HTMLElement>): DataGridBodyState => {
return useTableBody_unstable({ ...props, as: 'div' }, ref);
const getRows = useDataGridContext_unstable(ctx => ctx.getRows);
const rows = getRows();

const { children: renderRow } = props;
const children = rows.map(row => renderRow(row));
return useTableBody_unstable({ ...props, children, as: 'div' }, ref);
};
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
import { createContext, useContextSelector } from '@fluentui/react-context-selector';
import type { ContextSelector } from '@fluentui/react-context-selector';
import { DataGridContextValue } from '../components/DataGrid/DataGrid.types';
import { defaultTableState } from '../hooks';

const dataGridContext = createContext<DataGridContextValue | undefined>(undefined);

const dataGridContextDefaultValue: DataGridContextValue = {
...defaultTableState,
};

export const DataGridContextProvider = dataGridContext.Provider;

export const useDataGridContext_unstable = <T>(selector: ContextSelector<DataGridContextValue, T>) =>
useContextSelector(dataGridContext, (ctx = dataGridContextDefaultValue) => selector(ctx));
9 changes: 9 additions & 0 deletions packages/react-components/react-table/src/hooks/useTable.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,15 @@ import { defaultTableSortState } from './useSort';

const defaultRowEnhancer: RowEnhancer<unknown, RowState<unknown>> = row => row;

export const defaultTableState: TableState<unknown> = {
selection: defaultTableSelectionState,
sort: defaultTableSortState,
getRows: () => [],
getRowId: () => '',
items: [],
columns: [],
};

export function useTable<TItem>(options: UseTableOptions<TItem>, plugins: TableStatePlugin[] = []): TableState<TItem> {
const { items, getRowId, columns } = options;

Expand Down
Loading

0 comments on commit 48c8520

Please sign in to comment.