Skip to content

Commit

Permalink
feat: Implement DataGridBody powered by react-window (#26081)
Browse files Browse the repository at this point in the history
* feat: Implement DataGridBody powered by react-window

Uses hooks to recompose `DataGridBody` that uses react-window
internally. This component should be a straight replacement for the
standard `DataGridBody` that supports virtualization.

* remove useless import

* update datagrid

* refactor: render function children called in component render function

Components like `DataGridBody` and `DataGridRow` only accept render
functions as children. These render functions were always called during
the `useComponent` hook. This was unnecessary, and makes recomposition
harder because data is always processed even if the recomposed component
should render children differently.

* changefile

* updates

* remove useless story

* changefile

* update md

* changefile

* useScrollbarWidth

* update changefile entry

* remove unnecessary dependencies

* remove TODO

* expose width as prop

* fix comment typo

* fix datagrid virtualization story import

* use unknown in

* change  to

* add incrementing index to virtualization examples

* update api md
  • Loading branch information
ling1726 authored Jan 11, 2023
1 parent ac636e8 commit 10942a5
Show file tree
Hide file tree
Showing 28 changed files with 475 additions and 12 deletions.
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
{
"type": "minor",
"comment": "feat: Export RowIdContextProvider and useRowIdContext from react-table",
"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": "feat: Export RowIdProvider",
"packageName": "@fluentui/react-table",
"email": "[email protected]",
"dependentChangeType": "patch"
}
Original file line number Diff line number Diff line change
Expand Up @@ -178,6 +178,7 @@ import { renderTableSelectionCell_unstable } from '@fluentui/react-table';
import { renderToolbar_unstable } from '@fluentui/react-toolbar';
import { renderToolbarGroup_unstable } from '@fluentui/react-toolbar';
import { RowId } from '@fluentui/react-table';
import { RowIdContextProvider } from '@fluentui/react-table';
import { RowState } from '@fluentui/react-table';
import { Select } from '@fluentui/react-select';
import { selectClassNames } from '@fluentui/react-select';
Expand Down Expand Up @@ -325,6 +326,7 @@ import { useOptionStyles_unstable } from '@fluentui/react-combobox';
import { useOverflowMenu } from '@fluentui/react-overflow';
import { useProgressBar_unstable } from '@fluentui/react-progress';
import { useProgressBarStyles_unstable } from '@fluentui/react-progress';
import { useRowIdContext } from '@fluentui/react-table';
import { useSelect_unstable } from '@fluentui/react-select';
import { useSelectStyles_unstable } from '@fluentui/react-select';
import { useTable_unstable } from '@fluentui/react-table';
Expand Down Expand Up @@ -711,6 +713,8 @@ export { renderToolbarGroup_unstable }

export { RowId }

export { RowIdContextProvider }

export { RowState }

export { Select }
Expand Down Expand Up @@ -1006,6 +1010,8 @@ export { useProgressBar_unstable }

export { useProgressBarStyles_unstable }

export { useRowIdContext }

export { useSelect_unstable }

export { useSelectStyles_unstable }
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -270,6 +270,8 @@ export {
useDataGridSelectionCell_unstable,
renderDataGridSelectionCell_unstable,
dataGridSelectionCellClassNames,
RowIdContextProvider,
useRowIdContext,
} from '@fluentui/react-table';

export type {
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
const rootMain = require('../../../../.storybook/main');

module.exports = /** @type {Omit<import('../../../../.storybook/main'), 'typescript'|'babel'>} */ ({
...rootMain,
stories: [...rootMain.stories, '../stories/**/*.stories.mdx', '../stories/**/index.stories.@(ts|tsx)'],
addons: [...rootMain.addons],
webpackFinal: (config, options) => {
const localConfig = { ...rootMain.webpackFinal(config, options) };

// add your own webpack tweaks if needed

return localConfig;
},
});
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
import * as rootPreview from '../../../../.storybook/preview';

/** @type {typeof rootPreview.decorators} */
export const decorators = [...rootPreview.decorators];

/** @type {typeof rootPreview.parameters} */
export const parameters = { ...rootPreview.parameters };
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
{
"extends": "../tsconfig.json",
"compilerOptions": {
"outDir": "",
"allowJs": true,
"checkJs": true,
"types": ["static-assets", "environment", "storybook__addons"]
},
"include": ["../stories/**/*.stories.ts", "../stories/**/*.stories.tsx", "*.js"]
}
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,40 @@
```ts

/// <reference types="react" />

import type { DataGridBodyProps as DataGridBodyProps_2 } from '@fluentui/react-components/unstable';
import type { DataGridBodySlots as DataGridBodySlots_2 } from '@fluentui/react-components/unstable';
import type { DataGridBodyState as DataGridBodyState_2 } from '@fluentui/react-components/unstable';
import type { ForwardRefComponent } from '@fluentui/react-components';
import * as React_2 from 'react';
import type { RowState } from '@fluentui/react-components/unstable';

// @public
export const DataGridBody: ForwardRefComponent<DataGridBodyProps>;

// @public
export type DataGridBodyProps = Omit<DataGridBodyProps_2, 'children'> & {
itemSize: number;
height: number;
width?: string | number;
children: RowRenderFunction;
};

// @public (undocumented)
export type DataGridBodySlots = DataGridBodySlots_2;

// @public
export type DataGridBodyState = Omit<DataGridBodyState_2, 'renderRow'> & Pick<DataGridBodyProps, 'itemSize' | 'height'> & Pick<Required<DataGridBodyProps>, 'width'> & {
renderRow: RowRenderFunction;
};

// @public
export const renderDataGridBody_unstable: (state: DataGridBodyState) => JSX.Element;

// @public
export const useDataGridBody_unstable: (props: DataGridBodyProps, ref: React_2.Ref<HTMLElement>) => DataGridBodyState;

// (No @packageDocumentation comment for this package)

```
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@
"name": "@fluentui/react-data-grid-react-window",
"version": "9.0.0-alpha.0",
"private": true,
"description": "Components and utilities to virtualize the Fluent UI DataGrid component",
"description": "Virtualized DataGrid components and utilities powered by react-window",
"main": "lib-commonjs/index.js",
"module": "lib/index.js",
"typings": "./dist/index.d.ts",
Expand All @@ -20,7 +20,9 @@
"lint": "just-scripts lint",
"test": "jest --passWithNoTests",
"type-check": "tsc -b tsconfig.json",
"generate-api": "tsc -p ./tsconfig.lib.json --emitDeclarationOnly && just-scripts api-extractor"
"generate-api": "tsc -p ./tsconfig.lib.json --emitDeclarationOnly && just-scripts api-extractor",
"storybook": "start-storybook",
"start": "yarn storybook"
},
"devDependencies": {
"@fluentui/eslint-plugin": "*",
Expand All @@ -30,12 +32,11 @@
"@fluentui/scripts-tasks": "*"
},
"dependencies": {
"@fluentui/react-theme": "^9.1.5",
"@fluentui/react-utilities": "^9.4.0",
"@griffel/react": "^1.5.2",
"react-window": "^1.8.6",
"tslib": "^2.1.0"
},
"peerDependencies": {
"@fluentui/react-components": "^9.7.4",
"@types/react": ">=16.8.0 <19.0.0",
"@types/react-dom": ">=16.8.0 <19.0.0",
"react": ">=16.8.0 <19.0.0",
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
export * from './components/DataGridBody/index';
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
import { DataGridBody } from './DataGridBody';
import { DataGridBodyProps } from './DataGridBody.types';
import { isConformant } from '../../testing/isConformant';

describe('DataGridBody', () => {
isConformant<DataGridBodyProps>({
Component: DataGridBody,
displayName: 'DataGridBody',
disabledTests: ['component-has-static-classnames-object'],
requiredProps: {
height: 50,
itemSize: 1000,
},
});
});
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
import * as React from 'react';
import type { ForwardRefComponent } from '@fluentui/react-components';
import { useDataGridBodyStyles_unstable } from './useDataGridBodyStyles';
import { useDataGridBody_unstable } from './useDataGridBody';
import { renderDataGridBody_unstable } from './renderDataGridBody';
import type { DataGridBodyProps } from './DataGridBody.types';

/**
* DataGridBody component
*/
export const DataGridBody: ForwardRefComponent<DataGridBodyProps> = React.forwardRef((props, ref) => {
const state = useDataGridBody_unstable(props, ref);

useDataGridBodyStyles_unstable(state);
return renderDataGridBody_unstable(state);
});

DataGridBody.displayName = 'DataGridBody';
Original file line number Diff line number Diff line change
@@ -0,0 +1,46 @@
import * as React from 'react';
import type {
RowState,
DataGridBodyProps as DataGridBodyPropsBase,
DataGridBodySlots as DataGridBodySlotsBase,
DataGridBodyState as DataGridBodyStateBase,
} from '@fluentui/react-components/unstable';

export type DataGridBodySlots = DataGridBodySlotsBase;

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

/**
* DataGridBody Props
*/
export type DataGridBodyProps = Omit<DataGridBodyPropsBase, 'children'> & {
/**
* The size of each row
*/
itemSize: number;
/**
* The height of the virtualized container
*/
height: number;
/**
* The width of the virtualized container
* @default 100%
*/
width?: string | number;
/**
* Children render function for rows
*/
children: RowRenderFunction;
};

/**
* State used in rendering DataGridBody
*/
export type DataGridBodyState = Omit<DataGridBodyStateBase, 'renderRow'> &
Pick<DataGridBodyProps, 'itemSize' | 'height'> &
Pick<Required<DataGridBodyProps>, 'width'> & {
renderRow: RowRenderFunction;
};
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
export * from './DataGridBody';
export * from './DataGridBody.types';
export * from './renderDataGridBody';
export * from './useDataGridBody';
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
import * as React from 'react';
import type { DataGridBodyState, DataGridBodySlots } from './DataGridBody.types';
import { FixedSizeList as List, ListChildComponentProps } from 'react-window';
import { getSlots } from '@fluentui/react-components';
import { RowState, RowIdContextProvider } from '@fluentui/react-components/unstable';

/**
* Render the final JSX of DataGridVirtualizedBody
*/
export const renderDataGridBody_unstable = (state: DataGridBodyState) => {
const { slots, slotProps } = getSlots<DataGridBodySlots>(state);

return (
<slots.root {...slotProps.root}>
<List
itemSize={state.itemSize}
width={state.width}
itemData={state.rows}
height={state.height}
itemCount={state.rows.length}
>
{({ data, index, style }: ListChildComponentProps) => {
const row: RowState<unknown> = data[index];
return <RowIdContextProvider value={row.rowId}>{state.renderRow(row, style)}</RowIdContextProvider>;
}}
</List>
</slots.root>
);
};
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
import * as React from 'react';
import type { DataGridBodyProps, DataGridBodyState } from './DataGridBody.types';
import { useDataGridBody_unstable as useDataGridBodyBase_unstable } from '@fluentui/react-components/unstable';

/**
* Create the state required to render DataGridBody.
*
* The returned state can be modified with hooks such as useDataGridBodyStyles_unstable,
* before being passed to renderDataGridBody_unstable.
*
* @param props - props from this instance of DataGridBody
* @param ref - reference to root HTMLElement of DataGridBody
*/
export const useDataGridBody_unstable = (props: DataGridBodyProps, ref: React.Ref<HTMLElement>): DataGridBodyState => {
const { height, itemSize, width = '100%' } = props;
const baseState = useDataGridBodyBase_unstable(props, ref);

return {
...baseState,
itemSize,
height,
renderRow: props.children,
width,
};
};
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
import type { DataGridBodyState } from './DataGridBody.types';
import { useDataGridBodyStyles_unstable as useDataGridBodyStylesBase_unstable } from '@fluentui/react-components/unstable';

/**
* Apply styling to the DataGridBody slots based on the state
*/
export const useDataGridBodyStyles_unstable = (state: DataGridBodyState): DataGridBodyState => {
useDataGridBodyStylesBase_unstable({ ...state, renderRow: () => null });
return state;
};
Original file line number Diff line number Diff line change
@@ -1,2 +1,3 @@
// TODO: replace with real exports
export {};
export { DataGridBody, useDataGridBody_unstable, renderDataGridBody_unstable } from './DataGridBody';

export type { DataGridBodyProps, DataGridBodyState, DataGridBodySlots } from './DataGridBody';
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
import { isConformant as baseIsConformant } from '@fluentui/react-conformance';
import type { IsConformantOptions, TestObject } from '@fluentui/react-conformance';
import griffelTests from '@fluentui/react-conformance-griffel';

export function isConformant<TProps = {}>(
testInfo: Omit<IsConformantOptions<TProps>, 'componentPath'> & { componentPath?: string },
) {
const defaultOptions: Partial<IsConformantOptions<TProps>> = {
componentPath: require.main?.filename.replace('.test', ''),
extraTests: griffelTests as TestObject<TProps>,
};

baseIsConformant(defaultOptions, testInfo);
}
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,9 @@
},
{
"path": "./tsconfig.spec.json"
},
{
"path": "./.storybook/tsconfig.json"
}
]
}
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,14 @@
"inlineSources": true,
"types": ["static-assets", "environment"]
},
"exclude": ["**/*.spec.ts", "**/*.spec.tsx", "**/*.test.ts", "**/*.test.tsx"],
"exclude": [
"./src/testing/**",
"**/*.spec.ts",
"**/*.spec.tsx",
"**/*.test.ts",
"**/*.test.tsx",
"**/*.stories.ts",
"**/*.stories.tsx"
],
"include": ["./src/**/*.ts", "./src/**/*.tsx"]
}
Original file line number Diff line number Diff line change
Expand Up @@ -5,5 +5,13 @@
"outDir": "dist",
"types": ["jest", "node"]
},
"include": ["**/*.spec.ts", "**/*.spec.tsx", "**/*.test.ts", "**/*.test.tsx", "**/*.d.ts"]
"include": [
"**/*.spec.ts",
"**/*.spec.tsx",
"**/*.test.ts",
"**/*.test.tsx",
"**/*.d.ts",
"./src/testing/**/*.ts",
"./src/testing/**/*.tsx"
]
}
Loading

0 comments on commit 10942a5

Please sign in to comment.