Skip to content

Commit

Permalink
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Merge branch 'master' into range-position-context
Browse files Browse the repository at this point in the history
flaviendelangle committed Dec 11, 2024
2 parents 8adbee0 + e7a9530 commit 63134d0
Showing 25 changed files with 127 additions and 364 deletions.
Original file line number Diff line number Diff line change
@@ -72,6 +72,22 @@ Below are described the steps you need to make to migrate from v7 to v8.

- The Grid is more aligned with the WAI-ARIA authoring practices and sets the `role` attribute to `treegrid` if the Data Grid is used with row grouping feature.

### State

- The selectors signature has been updated due to the support of arguments in the selectors. Pass `undefined` as `arguments` if the selector doesn't use any arguments.

```diff
-mySelector(state, instanceId)
+mySelector(state, arguments, instanceId)
```

- The `useGridSelector` signature has been updated due to the introduction of arguments parameter in the selectors. Pass `undefined` as `arguments` if the selector doesn't use any arguments.

```diff
-const output = useGridSelector(apiRef, selector, equals)
+const output = useGridSelector(apiRef, selector, arguments, equals)
```

### Other exports

- `ariaV8` experimental flag is removed.
Original file line number Diff line number Diff line change
@@ -52,10 +52,7 @@ const GROUPING_COL_DEF_FORCED_PROPERTIES_DATA_SOURCE: Pick<
* TODO: Make this index comparator depth invariant, the logic should not be inverted when sorting in the "desc" direction (but the current return format of `sortComparator` does not support this behavior).
*/
const groupingFieldIndexComparator: GridComparatorFn = (v1, v2, cellParams1, cellParams2) => {
const model = gridRowGroupingSanitizedModelSelector(
cellParams1.api.state,
cellParams1.api.instanceId,
);
const model = gridRowGroupingSanitizedModelSelector({ current: cellParams1.api });

const groupingField1 = (cellParams1.rowNode as GridGroupNode).groupingField ?? null;
const groupingField2 = (cellParams2.rowNode as GridGroupNode).groupingField ?? null;
Original file line number Diff line number Diff line change
@@ -7,7 +7,6 @@ import {
GridDataSourceGroupNode,
useGridSelector,
} from '@mui/x-data-grid';
import { useGridSelectorV8 } from '@mui/x-data-grid/internals';
import CircularProgress from '@mui/material/CircularProgress';
import { useGridRootProps } from '../hooks/utils/useGridRootProps';
import { useGridPrivateApiContext } from '../hooks/utils/useGridPrivateApiContext';
@@ -54,8 +53,8 @@ function GridTreeDataGroupingCellIcon(props: GridTreeDataGroupingCellIconProps)
const classes = useUtilityClasses(rootProps);
const { rowNode, id, field, descendantCount } = props;

const isDataLoading = useGridSelectorV8(apiRef, gridDataSourceLoadingIdSelector, id);
const error = useGridSelectorV8(apiRef, gridDataSourceErrorSelector, id);
const isDataLoading = useGridSelector(apiRef, gridDataSourceLoadingIdSelector, id);
const error = useGridSelector(apiRef, gridDataSourceErrorSelector, id);

const handleClick = (event: React.MouseEvent<HTMLButtonElement>) => {
if (!rowNode.childrenExpanded) {
Original file line number Diff line number Diff line change
@@ -5,7 +5,7 @@ import {
gridPaginationModelSelector,
GridRowId,
} from '@mui/x-data-grid';
import { createSelector, createSelectorV8 } from '@mui/x-data-grid/internals';
import { createSelector } from '@mui/x-data-grid/internals';
import { GridStatePro } from '../../../models/gridStatePro';

const computeStartEnd = (paginationModel: GridPaginationModel) => {
@@ -36,7 +36,7 @@ export const gridDataSourceLoadingSelector = createSelector(
(dataSource) => dataSource.loading,
);

export const gridDataSourceLoadingIdSelector = createSelectorV8(
export const gridDataSourceLoadingIdSelector = createSelector(
gridDataSourceStateSelector,
(dataSource, id: GridRowId) => dataSource.loading[id] ?? false,
);
@@ -46,7 +46,7 @@ export const gridDataSourceErrorsSelector = createSelector(
(dataSource) => dataSource.errors,
);

export const gridDataSourceErrorSelector = createSelectorV8(
export const gridDataSourceErrorSelector = createSelector(
gridDataSourceStateSelector,
(dataSource, id: GridRowId) => dataSource.errors[id],
);
1 change: 1 addition & 0 deletions packages/x-data-grid/src/components/GridRow.tsx
Original file line number Diff line number Diff line change
@@ -224,6 +224,7 @@ const GridRow = React.forwardRef<HTMLDivElement, GridRowProps>(function GridRow(
const heightEntry = useGridSelector(
apiRef,
() => ({ ...apiRef.current.getRowHeightEntry(rowId) }),
undefined,
objectShallowCompare,
);

1 change: 1 addition & 0 deletions packages/x-data-grid/src/components/cell/GridCell.tsx
Original file line number Diff line number Diff line change
@@ -207,6 +207,7 @@ const GridCell = React.forwardRef<HTMLDivElement, GridCellProps>(function GridCe
result.api = apiRef.current;
return result;
},
undefined,
objectShallowCompare,
);

Original file line number Diff line number Diff line change
@@ -9,7 +9,7 @@ export const useGridStateInitialization = <PrivateApi extends GridPrivateApiComm
apiRef: React.MutableRefObject<PrivateApi>,
) => {
const controlStateMapRef = React.useRef<
Record<string, GridControlStateItem<PrivateApi['state'], any>>
Record<string, GridControlStateItem<PrivateApi['state'], any, any>>
>({});
const [, rawForceUpdate] = React.useState<PrivateApi['state']>();

@@ -40,10 +40,15 @@ export const useGridStateInitialization = <PrivateApi extends GridPrivateApiComm
const controlState = controlStateMapRef.current[stateId];
const oldSubState = controlState.stateSelector(
apiRef.current.state,
undefined,
apiRef.current.instanceId,
);

const newSubState = controlState.stateSelector(newState, apiRef.current.instanceId);
const newSubState = controlState.stateSelector(
newState,
undefined,
apiRef.current.instanceId,
);
if (newSubState === oldSubState) {
return;
}
@@ -82,7 +87,7 @@ export const useGridStateInitialization = <PrivateApi extends GridPrivateApiComm
if (updatedControlStateIds.length === 1) {
const { stateId, hasPropChanged } = updatedControlStateIds[0];
const controlState = controlStateMapRef.current[stateId];
const model = controlState.stateSelector(newState, apiRef.current.instanceId);
const model = controlState.stateSelector(newState, undefined, apiRef.current.instanceId);

if (controlState.propOnChange && hasPropChanged) {
controlState.propOnChange(model, {
Original file line number Diff line number Diff line change
@@ -107,7 +107,7 @@ export const useGridFilter = (

const updateFilteredRows = React.useCallback(() => {
apiRef.current.setState((state) => {
const filterModel = gridFilterModelSelector(state, apiRef.current.instanceId);
const filterModel = gridFilterModelSelector(state, undefined, apiRef.current.instanceId);
const filterState = apiRef.current.getFilterState(filterModel);

const newState = {
10 changes: 5 additions & 5 deletions packages/x-data-grid/src/hooks/features/rows/useGridRows.ts
Original file line number Diff line number Diff line change
@@ -359,7 +359,7 @@ export const useGridRows = (
}

apiRef.current.setState((state) => {
const group = gridRowTreeSelector(state, apiRef.current.instanceId)[
const group = gridRowTreeSelector(state, undefined, apiRef.current.instanceId)[
GRID_ROOT_GROUP_ID
] as GridGroupNode;
const allRows = group.children;
@@ -573,10 +573,10 @@ export const useGridRows = (
const applyHydrateRowsProcessor = React.useCallback(() => {
apiRef.current.setState((state) => {
const response = apiRef.current.unstable_applyPipeProcessors('hydrateRows', {
tree: gridRowTreeSelector(state, apiRef.current.instanceId),
treeDepths: gridRowTreeDepthsSelector(state, apiRef.current.instanceId),
dataRowIds: gridDataRowIdsSelector(state, apiRef.current.instanceId),
dataRowIdToModelLookup: gridRowsLookupSelector(state, apiRef.current.instanceId),
tree: gridRowTreeSelector(state, undefined, apiRef.current.instanceId),
treeDepths: gridRowTreeDepthsSelector(state, undefined, apiRef.current.instanceId),
dataRowIds: gridDataRowIdsSelector(state, undefined, apiRef.current.instanceId),
dataRowIdToModelLookup: gridRowsLookupSelector(state, undefined, apiRef.current.instanceId),
});

return {
Original file line number Diff line number Diff line change
@@ -154,7 +154,7 @@ export const useGridSorting = (
};
}

const sortModel = gridSortModelSelector(state, apiRef.current.instanceId);
const sortModel = gridSortModelSelector(state, undefined, apiRef.current.instanceId);
const sortRowList = buildAggregatedSortingApplier(sortModel, apiRef);
const sortedRows = apiRef.current.applyStrategyProcessor('sorting', {
sortRowList,
82 changes: 11 additions & 71 deletions packages/x-data-grid/src/hooks/utils/useGridSelector.ts
Original file line number Diff line number Diff line change
@@ -2,34 +2,22 @@ import * as React from 'react';
import { fastObjectShallowCompare } from '@mui/x-internals/fastObjectShallowCompare';
import { warnOnce } from '@mui/x-internals/warning';
import type { GridApiCommon } from '../../models/api/gridApiCommon';
import type { OutputSelector, OutputSelectorV8 } from '../../utils/createSelector';
import type { OutputSelector } from '../../utils/createSelector';
import { useLazyRef } from './useLazyRef';
import { useOnMount } from './useOnMount';
import type { GridCoreApi } from '../../models/api/gridCoreApi';

function isOutputSelector<Api extends GridApiCommon, T>(
function isOutputSelector<Api extends GridApiCommon, Args, T>(
selector: any,
): selector is OutputSelector<Api['state'], T> {
): selector is OutputSelector<Api['state'], Args, T> {
return selector.acceptsApiRef;
}

type Selector<Api extends GridApiCommon, Args, T> =
| ((state: Api['state']) => T)
| OutputSelectorV8<Api['state'], Args, T>;
| OutputSelector<Api['state'], Args, T>;

// TODO v8: Remove this function
function applySelector<Api extends GridApiCommon, T>(
apiRef: React.MutableRefObject<Api>,
selector: ((state: Api['state']) => T) | OutputSelector<Api['state'], T>,
) {
if (isOutputSelector<Api, T>(selector)) {
return selector(apiRef);
}
return selector(apiRef.current.state);
}

// TODO v8: Rename this function to `applySelector`
function applySelectorV8<Api extends GridApiCommon, Args, T>(
function applySelector<Api extends GridApiCommon, Args, T>(
apiRef: React.MutableRefObject<Api>,
selector: Selector<Api, Args, T>,
args: Args,
@@ -38,11 +26,11 @@ function applySelectorV8<Api extends GridApiCommon, Args, T>(
if (isOutputSelector(selector)) {
return selector(apiRef, args);
}
return selector(apiRef.current.state, instanceId);
return selector(apiRef.current.state, args, instanceId);
}

const defaultCompare = Object.is;
export const objectShallowCompare = fastObjectShallowCompare;
export const objectShallowCompare = fastObjectShallowCompare as (a: unknown, b: unknown) => boolean;
const arrayShallowCompare = (a: any[], b: any[]) => {
if (a === b) {
return true;
@@ -63,55 +51,7 @@ export const argsEqual = (prev: any, curr: any) => {

const createRefs = () => ({ state: null, equals: null, selector: null, args: null }) as any;

// TODO v8: Remove this function
export const useGridSelector = <Api extends GridApiCommon, T>(
apiRef: React.MutableRefObject<Api>,
selector: ((state: Api['state']) => T) | OutputSelector<Api['state'], T>,
equals: (a: T, b: T) => boolean = defaultCompare,
) => {
if (process.env.NODE_ENV !== 'production') {
if (!apiRef.current.state) {
warnOnce([
'MUI X: `useGridSelector` has been called before the initialization of the state.',
'This hook can only be used inside the context of the grid.',
]);
}
}

const refs = useLazyRef<
{
state: T;
equals: typeof equals;
selector: typeof selector;
},
never
>(createRefs);
const didInit = refs.current.selector !== null;

const [state, setState] = React.useState<T>(
// We don't use an initialization function to avoid allocations
(didInit ? null : applySelector(apiRef, selector)) as T,
);

refs.current.state = state;
refs.current.equals = equals;
refs.current.selector = selector;

useOnMount(() => {
return apiRef.current.store.subscribe(() => {
const newState = applySelector(apiRef, refs.current.selector);
if (!refs.current.equals(refs.current.state, newState)) {
refs.current.state = newState;
setState(newState);
}
});
});

return state;
};

// TODO v8: Rename this function to `useGridSelector`
export const useGridSelectorV8 = <Api extends GridApiCommon, Args, T>(
export const useGridSelector = <Api extends GridApiCommon, Args, T>(
apiRef: React.MutableRefObject<Api>,
selector: Selector<Api, Args, T>,
args: Args = undefined as Args,
@@ -139,7 +79,7 @@ export const useGridSelectorV8 = <Api extends GridApiCommon, Args, T>(

const [state, setState] = React.useState<T>(
// We don't use an initialization function to avoid allocations
(didInit ? null : applySelectorV8(apiRef, selector, args, apiRef.current.instanceId)) as T,
(didInit ? null : applySelector(apiRef, selector, args, apiRef.current.instanceId)) as T,
);

refs.current.state = state;
@@ -149,7 +89,7 @@ export const useGridSelectorV8 = <Api extends GridApiCommon, Args, T>(
refs.current.args = args;

if (didInit && !argsEqual(prevArgs, args)) {
const newState = applySelectorV8(
const newState = applySelector(
apiRef,
refs.current.selector,
refs.current.args,
@@ -163,7 +103,7 @@ export const useGridSelectorV8 = <Api extends GridApiCommon, Args, T>(

useOnMount(() => {
return apiRef.current.store.subscribe(() => {
const newState = applySelectorV8(
const newState = applySelector(
apiRef,
refs.current.selector,
refs.current.args,
8 changes: 1 addition & 7 deletions packages/x-data-grid/src/internals/index.ts
Original file line number Diff line number Diff line change
@@ -151,13 +151,7 @@ export type * from '../models/props/DataGridProps';
export type * from '../models/gridDataSource';
export { getColumnsToExport, defaultGetRowsToExport } from '../hooks/features/export/utils';
export * from '../utils/createControllablePromise';
export {
createSelector,
createSelectorV8,
createSelectorMemoized,
createSelectorMemoizedV8,
} from '../utils/createSelector';
export { useGridSelectorV8 } from '../hooks/utils/useGridSelector';
export { createSelector, createSelectorMemoized } from '../utils/createSelector';
export { gridRowGroupsToFetchSelector } from '../hooks/features/rows/gridRowsSelector';
export {
findParentElementFromClassName,
4 changes: 2 additions & 2 deletions packages/x-data-grid/src/models/api/gridStateApi.ts
Original file line number Diff line number Diff line change
@@ -42,7 +42,7 @@ export interface GridStatePrivateApi<State extends GridStateCommunity> {
* Updates a control state that binds the model, the onChange prop, and the grid state together.
* @param {GridControlStateItem>} controlState The [[GridControlStateItem]] to be registered.
*/
registerControlState: <E extends keyof GridControlledStateEventLookup>(
controlState: GridControlStateItem<State, E>,
registerControlState: <E extends keyof GridControlledStateEventLookup, Args>(
controlState: GridControlStateItem<State, Args, E>,
) => void;
}
3 changes: 2 additions & 1 deletion packages/x-data-grid/src/models/controlStateItem.ts
Original file line number Diff line number Diff line change
@@ -5,12 +5,13 @@ import { GridStateCommunity } from './gridStateCommunity';

export interface GridControlStateItem<
State extends GridStateCommunity,
Args,
E extends keyof GridControlledStateEventLookup,
> {
stateId: string;
propModel?: GridEventLookup[E]['params'];
stateSelector:
| OutputSelector<State, GridControlledStateEventLookup[E]['params']>
| OutputSelector<State, Args, GridControlledStateEventLookup[E]['params']>
| ((state: State) => GridControlledStateEventLookup[E]['params']);
propOnChange?: (
model: GridControlledStateEventLookup[E]['params'],
2 changes: 1 addition & 1 deletion packages/x-data-grid/src/utils/createSelector.spec.ts
Original file line number Diff line number Diff line change
@@ -33,7 +33,7 @@ createSelector(
createSelector(
(state: GridStateCommunity) => state.columns.orderedFields,
(fields) => fields,
)({} as GridStateCommunity, { id: 1 });
)({} as GridStateCommunity, undefined, { id: 1 });

createSelector(
// @ts-expect-error Wrong state key
Loading

0 comments on commit 63134d0

Please sign in to comment.