From 3b0bba2453c8b7a052b2e434260a2f8d911f8c62 Mon Sep 17 00:00:00 2001 From: Rom Grk Date: Thu, 9 Jan 2025 04:18:25 -0500 Subject: [PATCH 1/3] [core] Update argos screenshots (#16111) From 296a499c56404f7a35e0699865a694170be0842d Mon Sep 17 00:00:00 2001 From: Bilal Shafi Date: Thu, 9 Jan 2025 17:13:03 +0500 Subject: [PATCH 2/3] [DataGridPremium] Server-side aggregation with data source (#15741) Co-authored-by: Armin Mehinovic <4390250+arminmeh@users.noreply.github.com> --- .../data/data-grid/aggregation/aggregation.md | 4 + .../ServerSideDataGridAggregation.js | 70 ++++++++ .../ServerSideDataGridAggregation.tsx | 70 ++++++++ .../ServerSideDataGridAggregation.tsx.preview | 8 + ...erverSideDataGridAggregationLazyLoading.js | 71 ++++++++ ...rverSideDataGridAggregationLazyLoading.tsx | 71 ++++++++ ...DataGridAggregationLazyLoading.tsx.preview | 7 + ...erverSideDataGridAggregationRowGrouping.js | 76 +++++++++ ...rverSideDataGridAggregationRowGrouping.tsx | 77 +++++++++ ...DataGridAggregationRowGrouping.tsx.preview | 7 + .../ServerSideDataGridAggregationTreeData.js | 75 +++++++++ .../ServerSideDataGridAggregationTreeData.tsx | 80 +++++++++ ...ideDataGridAggregationTreeData.tsx.preview | 8 + .../data-grid/server-side-data/aggregation.md | 99 ++++++++++- docs/data/pages.ts | 6 +- .../x/api/data-grid/data-grid-premium.json | 2 +- .../grid-aggregation-function-data-source.js | 26 +++ ...grid-aggregation-function-data-source.json | 19 +++ docs/pages/x/api/data-grid/index.md | 1 + docs/scripts/createXTypeScriptProjects.ts | 1 + ...grid-aggregation-function-data-source.json | 17 ++ .../data-grid/grid-aggregation-function.json | 6 +- .../src/hooks/serverUtils.ts | 118 ++++++++++++- .../src/hooks/useMockServer.ts | 17 +- .../src/DataGridPremium/DataGridPremium.tsx | 3 +- .../useDataGridPremiumComponent.tsx | 4 +- .../useDataGridPremiumProps.ts | 1 + .../GridColumnMenuAggregationItem.tsx | 6 +- .../aggregation/createAggregationLookup.ts | 55 ++++-- .../aggregation/gridAggregationInterfaces.ts | 25 ++- .../aggregation/gridAggregationUtils.ts | 29 +++- .../src/hooks/features/aggregation/index.ts | 18 +- .../aggregation/useGridAggregation.ts | 45 ++++- .../useGridAggregationPreProcessors.tsx | 22 ++- .../src/hooks/features/dataSource/cache.ts | 13 ++ .../src/hooks/features/dataSource/models.ts | 63 +++++++ .../dataSource/useGridDataSourcePremium.tsx | 102 ++++++++++++ packages/x-data-grid-premium/src/index.ts | 11 ++ .../src/models/dataGridPremiumProps.ts | 14 +- .../src/models/gridApiPremium.ts | 12 +- ...SourceAggregation.DataGridPremium.test.tsx | 157 ++++++++++++++++++ .../DataGridPro/useDataGridProComponent.tsx | 6 +- .../src/hooks/features/dataSource/cache.ts | 20 ++- .../hooks/features/dataSource/interfaces.ts | 4 +- ...DataSource.ts => useGridDataSourceBase.ts} | 68 ++++---- .../dataSource/useGridDataSourcePro.ts | 31 ++++ .../src/hooks/features/dataSource/utils.ts | 3 +- .../src/hooks/features/index.ts | 2 +- .../useGridDataSourceLazyLoader.ts | 5 + packages/x-data-grid-pro/src/index.ts | 14 ++ .../x-data-grid-pro/src/internals/index.ts | 4 +- .../x-data-grid-pro/src/models/gridApiPro.ts | 4 +- packages/x-data-grid-pro/src/models/index.ts | 6 - .../src/tests/dataSource.DataGridPro.test.tsx | 6 +- .../pipeProcessing/gridPipeProcessingApi.ts | 7 +- packages/x-data-grid/src/internals/index.ts | 5 +- scripts/x-data-grid-premium.exports.json | 1 + 57 files changed, 1579 insertions(+), 123 deletions(-) create mode 100644 docs/data/data-grid/server-side-data/ServerSideDataGridAggregation.js create mode 100644 docs/data/data-grid/server-side-data/ServerSideDataGridAggregation.tsx create mode 100644 docs/data/data-grid/server-side-data/ServerSideDataGridAggregation.tsx.preview create mode 100644 docs/data/data-grid/server-side-data/ServerSideDataGridAggregationLazyLoading.js create mode 100644 docs/data/data-grid/server-side-data/ServerSideDataGridAggregationLazyLoading.tsx create mode 100644 docs/data/data-grid/server-side-data/ServerSideDataGridAggregationLazyLoading.tsx.preview create mode 100644 docs/data/data-grid/server-side-data/ServerSideDataGridAggregationRowGrouping.js create mode 100644 docs/data/data-grid/server-side-data/ServerSideDataGridAggregationRowGrouping.tsx create mode 100644 docs/data/data-grid/server-side-data/ServerSideDataGridAggregationRowGrouping.tsx.preview create mode 100644 docs/data/data-grid/server-side-data/ServerSideDataGridAggregationTreeData.js create mode 100644 docs/data/data-grid/server-side-data/ServerSideDataGridAggregationTreeData.tsx create mode 100644 docs/data/data-grid/server-side-data/ServerSideDataGridAggregationTreeData.tsx.preview create mode 100644 docs/pages/x/api/data-grid/grid-aggregation-function-data-source.js create mode 100644 docs/pages/x/api/data-grid/grid-aggregation-function-data-source.json create mode 100644 docs/translations/api-docs/data-grid/grid-aggregation-function-data-source.json create mode 100644 packages/x-data-grid-premium/src/hooks/features/dataSource/cache.ts create mode 100644 packages/x-data-grid-premium/src/hooks/features/dataSource/models.ts create mode 100644 packages/x-data-grid-premium/src/hooks/features/dataSource/useGridDataSourcePremium.tsx create mode 100644 packages/x-data-grid-premium/src/tests/dataSourceAggregation.DataGridPremium.test.tsx rename packages/x-data-grid-pro/src/hooks/features/dataSource/{useGridDataSource.ts => useGridDataSourceBase.ts} (90%) create mode 100644 packages/x-data-grid-pro/src/hooks/features/dataSource/useGridDataSourcePro.ts diff --git a/docs/data/data-grid/aggregation/aggregation.md b/docs/data/data-grid/aggregation/aggregation.md index 0dad0c32852c2..0f9b5de14250b 100644 --- a/docs/data/data-grid/aggregation/aggregation.md +++ b/docs/data/data-grid/aggregation/aggregation.md @@ -12,6 +12,10 @@ The aggregated values are rendered in a footer row at the bottom of the Data Gri {{"demo": "AggregationInitialState.js", "bg": "inline", "defaultCodeOpen": false}} +:::info +If you're looking for aggregation on the server side, see [Server-side data—Aggregation](/x/react-data-grid/server-side-data/aggregation/). +::: + ## Pass aggregation to the Data Grid ### Structure of the model diff --git a/docs/data/data-grid/server-side-data/ServerSideDataGridAggregation.js b/docs/data/data-grid/server-side-data/ServerSideDataGridAggregation.js new file mode 100644 index 0000000000000..abeb33447b124 --- /dev/null +++ b/docs/data/data-grid/server-side-data/ServerSideDataGridAggregation.js @@ -0,0 +1,70 @@ +import * as React from 'react'; +import { DataGridPremium } from '@mui/x-data-grid-premium'; +import { useMockServer } from '@mui/x-data-grid-generator'; + +const aggregationFunctions = { + sum: { columnTypes: ['number'] }, + avg: { columnTypes: ['number'] }, + min: { columnTypes: ['number', 'date', 'dateTime'] }, + max: { columnTypes: ['number', 'date', 'dateTime'] }, + size: {}, +}; + +export default function ServerSideDataGridAggregation() { + const { columns, initialState, fetchRows } = useMockServer( + {}, + { useCursorPagination: false }, + ); + + const dataSource = React.useMemo( + () => ({ + getRows: async (params) => { + const urlParams = new URLSearchParams({ + paginationModel: JSON.stringify(params.paginationModel), + filterModel: JSON.stringify(params.filterModel), + sortModel: JSON.stringify(params.sortModel), + aggregationModel: JSON.stringify(params.aggregationModel), + }); + const getRowsResponse = await fetchRows( + `https://mui.com/x/api/data-grid?${urlParams.toString()}`, + ); + return { + rows: getRowsResponse.rows, + rowCount: getRowsResponse.rowCount, + aggregateRow: getRowsResponse.aggregateRow, + }; + }, + getAggregatedValue: (row, field) => { + return row[`${field}Aggregate`]; + }, + }), + [fetchRows], + ); + + const initialStateWithPagination = React.useMemo( + () => ({ + ...initialState, + pagination: { + paginationModel: { pageSize: 10, page: 0 }, + rowCount: 0, + }, + aggregation: { + model: { commodity: 'size', quantity: 'sum' }, + }, + }), + [initialState], + ); + + return ( +
+ +
+ ); +} diff --git a/docs/data/data-grid/server-side-data/ServerSideDataGridAggregation.tsx b/docs/data/data-grid/server-side-data/ServerSideDataGridAggregation.tsx new file mode 100644 index 0000000000000..c24b761cb58a9 --- /dev/null +++ b/docs/data/data-grid/server-side-data/ServerSideDataGridAggregation.tsx @@ -0,0 +1,70 @@ +import * as React from 'react'; +import { DataGridPremium, GridDataSource } from '@mui/x-data-grid-premium'; +import { useMockServer } from '@mui/x-data-grid-generator'; + +const aggregationFunctions = { + sum: { columnTypes: ['number'] }, + avg: { columnTypes: ['number'] }, + min: { columnTypes: ['number', 'date', 'dateTime'] }, + max: { columnTypes: ['number', 'date', 'dateTime'] }, + size: {}, +}; + +export default function ServerSideDataGridAggregation() { + const { columns, initialState, fetchRows } = useMockServer( + {}, + { useCursorPagination: false }, + ); + + const dataSource: GridDataSource = React.useMemo( + () => ({ + getRows: async (params) => { + const urlParams = new URLSearchParams({ + paginationModel: JSON.stringify(params.paginationModel), + filterModel: JSON.stringify(params.filterModel), + sortModel: JSON.stringify(params.sortModel), + aggregationModel: JSON.stringify(params.aggregationModel), + }); + const getRowsResponse = await fetchRows( + `https://mui.com/x/api/data-grid?${urlParams.toString()}`, + ); + return { + rows: getRowsResponse.rows, + rowCount: getRowsResponse.rowCount, + aggregateRow: getRowsResponse.aggregateRow, + }; + }, + getAggregatedValue: (row, field) => { + return row[`${field}Aggregate`]; + }, + }), + [fetchRows], + ); + + const initialStateWithPagination = React.useMemo( + () => ({ + ...initialState, + pagination: { + paginationModel: { pageSize: 10, page: 0 }, + rowCount: 0, + }, + aggregation: { + model: { commodity: 'size', quantity: 'sum' }, + }, + }), + [initialState], + ); + + return ( +
+ +
+ ); +} diff --git a/docs/data/data-grid/server-side-data/ServerSideDataGridAggregation.tsx.preview b/docs/data/data-grid/server-side-data/ServerSideDataGridAggregation.tsx.preview new file mode 100644 index 0000000000000..aee05887a7c35 --- /dev/null +++ b/docs/data/data-grid/server-side-data/ServerSideDataGridAggregation.tsx.preview @@ -0,0 +1,8 @@ + \ No newline at end of file diff --git a/docs/data/data-grid/server-side-data/ServerSideDataGridAggregationLazyLoading.js b/docs/data/data-grid/server-side-data/ServerSideDataGridAggregationLazyLoading.js new file mode 100644 index 0000000000000..0b7c242658a8b --- /dev/null +++ b/docs/data/data-grid/server-side-data/ServerSideDataGridAggregationLazyLoading.js @@ -0,0 +1,71 @@ +import * as React from 'react'; +import { DataGridPremium } from '@mui/x-data-grid-premium'; +import { useMockServer } from '@mui/x-data-grid-generator'; + +const aggregationFunctions = { + sum: { columnTypes: ['number'] }, + avg: { columnTypes: ['number'] }, + min: { columnTypes: ['number', 'date', 'dateTime'] }, + max: { columnTypes: ['number', 'date', 'dateTime'] }, + size: {}, +}; + +export default function ServerSideDataGridAggregationLazyLoading() { + const { + columns, + initialState: initState, + fetchRows, + } = useMockServer({}, { useCursorPagination: false }); + + const dataSource = React.useMemo( + () => ({ + getRows: async (params) => { + const urlParams = new URLSearchParams({ + filterModel: JSON.stringify(params.filterModel), + sortModel: JSON.stringify(params.sortModel), + aggregationModel: JSON.stringify(params.aggregationModel), + start: `${params.start}`, + end: `${params.end}`, + }); + const getRowsResponse = await fetchRows( + `https://mui.com/x/api/data-grid?${urlParams.toString()}`, + ); + return { + rows: getRowsResponse.rows, + rowCount: getRowsResponse.rowCount, + aggregateRow: getRowsResponse.aggregateRow, + }; + }, + getAggregatedValue: (row, field) => { + return row[`${field}Aggregate`]; + }, + }), + [fetchRows], + ); + + const initialState = React.useMemo( + () => ({ + ...initState, + pagination: { + paginationModel: { pageSize: 10, page: 0 }, + rowCount: 0, + }, + aggregation: { + model: { commodity: 'size', quantity: 'sum' }, + }, + }), + [initState], + ); + + return ( +
+ +
+ ); +} diff --git a/docs/data/data-grid/server-side-data/ServerSideDataGridAggregationLazyLoading.tsx b/docs/data/data-grid/server-side-data/ServerSideDataGridAggregationLazyLoading.tsx new file mode 100644 index 0000000000000..eaeda275e5281 --- /dev/null +++ b/docs/data/data-grid/server-side-data/ServerSideDataGridAggregationLazyLoading.tsx @@ -0,0 +1,71 @@ +import * as React from 'react'; +import { DataGridPremium, GridDataSource } from '@mui/x-data-grid-premium'; +import { useMockServer } from '@mui/x-data-grid-generator'; + +const aggregationFunctions = { + sum: { columnTypes: ['number'] }, + avg: { columnTypes: ['number'] }, + min: { columnTypes: ['number', 'date', 'dateTime'] }, + max: { columnTypes: ['number', 'date', 'dateTime'] }, + size: {}, +}; + +export default function ServerSideDataGridAggregationLazyLoading() { + const { + columns, + initialState: initState, + fetchRows, + } = useMockServer({}, { useCursorPagination: false }); + + const dataSource: GridDataSource = React.useMemo( + () => ({ + getRows: async (params) => { + const urlParams = new URLSearchParams({ + filterModel: JSON.stringify(params.filterModel), + sortModel: JSON.stringify(params.sortModel), + aggregationModel: JSON.stringify(params.aggregationModel), + start: `${params.start}`, + end: `${params.end}`, + }); + const getRowsResponse = await fetchRows( + `https://mui.com/x/api/data-grid?${urlParams.toString()}`, + ); + return { + rows: getRowsResponse.rows, + rowCount: getRowsResponse.rowCount, + aggregateRow: getRowsResponse.aggregateRow, + }; + }, + getAggregatedValue: (row, field) => { + return row[`${field}Aggregate`]; + }, + }), + [fetchRows], + ); + + const initialState = React.useMemo( + () => ({ + ...initState, + pagination: { + paginationModel: { pageSize: 10, page: 0 }, + rowCount: 0, + }, + aggregation: { + model: { commodity: 'size', quantity: 'sum' }, + }, + }), + [initState], + ); + + return ( +
+ +
+ ); +} diff --git a/docs/data/data-grid/server-side-data/ServerSideDataGridAggregationLazyLoading.tsx.preview b/docs/data/data-grid/server-side-data/ServerSideDataGridAggregationLazyLoading.tsx.preview new file mode 100644 index 0000000000000..1856db25a89aa --- /dev/null +++ b/docs/data/data-grid/server-side-data/ServerSideDataGridAggregationLazyLoading.tsx.preview @@ -0,0 +1,7 @@ + \ No newline at end of file diff --git a/docs/data/data-grid/server-side-data/ServerSideDataGridAggregationRowGrouping.js b/docs/data/data-grid/server-side-data/ServerSideDataGridAggregationRowGrouping.js new file mode 100644 index 0000000000000..a2467cefcc01a --- /dev/null +++ b/docs/data/data-grid/server-side-data/ServerSideDataGridAggregationRowGrouping.js @@ -0,0 +1,76 @@ +import * as React from 'react'; +import { + DataGridPremium, + useKeepGroupedColumnsHidden, + useGridApiRef, +} from '@mui/x-data-grid-premium'; +import { useMockServer } from '@mui/x-data-grid-generator'; + +const aggregationFunctions = { + sum: { columnTypes: ['number'] }, + avg: { columnTypes: ['number'] }, + min: { columnTypes: ['number', 'date', 'dateTime'] }, + max: { columnTypes: ['number', 'date', 'dateTime'] }, + size: {}, +}; + +export default function ServerSideDataGridAggregationRowGrouping() { + const apiRef = useGridApiRef(); + const { + columns, + initialState: initState, + fetchRows, + } = useMockServer({ rowGrouping: true }); + + const dataSource = React.useMemo( + () => ({ + getRows: async (params) => { + const urlParams = new URLSearchParams({ + paginationModel: JSON.stringify(params.paginationModel), + filterModel: JSON.stringify(params.filterModel), + sortModel: JSON.stringify(params.sortModel), + groupKeys: JSON.stringify(params.groupKeys), + groupFields: JSON.stringify(params.groupFields), + aggregationModel: JSON.stringify(params.aggregationModel), + }); + const getRowsResponse = await fetchRows( + `https://mui.com/x/api/data-grid?${urlParams.toString()}`, + ); + return { + rows: getRowsResponse.rows, + rowCount: getRowsResponse.rowCount, + aggregateRow: getRowsResponse.aggregateRow, + }; + }, + getGroupKey: (row) => row.group, + getChildrenCount: (row) => row.descendantCount, + getAggregatedValue: (row, field) => row[`${field}Aggregate`], + }), + [fetchRows], + ); + + const initialState = useKeepGroupedColumnsHidden({ + apiRef, + initialState: { + ...initState, + rowGrouping: { + model: ['company', 'director'], + }, + aggregation: { + model: { title: 'size', gross: 'sum', year: 'max' }, + }, + }, + }); + + return ( +
+ +
+ ); +} diff --git a/docs/data/data-grid/server-side-data/ServerSideDataGridAggregationRowGrouping.tsx b/docs/data/data-grid/server-side-data/ServerSideDataGridAggregationRowGrouping.tsx new file mode 100644 index 0000000000000..afca284bd7259 --- /dev/null +++ b/docs/data/data-grid/server-side-data/ServerSideDataGridAggregationRowGrouping.tsx @@ -0,0 +1,77 @@ +import * as React from 'react'; +import { + DataGridPremium, + useKeepGroupedColumnsHidden, + useGridApiRef, + GridDataSource, +} from '@mui/x-data-grid-premium'; +import { useMockServer } from '@mui/x-data-grid-generator'; + +const aggregationFunctions = { + sum: { columnTypes: ['number'] }, + avg: { columnTypes: ['number'] }, + min: { columnTypes: ['number', 'date', 'dateTime'] }, + max: { columnTypes: ['number', 'date', 'dateTime'] }, + size: {}, +}; + +export default function ServerSideDataGridAggregationRowGrouping() { + const apiRef = useGridApiRef(); + const { + columns, + initialState: initState, + fetchRows, + } = useMockServer({ rowGrouping: true }); + + const dataSource: GridDataSource = React.useMemo( + () => ({ + getRows: async (params) => { + const urlParams = new URLSearchParams({ + paginationModel: JSON.stringify(params.paginationModel), + filterModel: JSON.stringify(params.filterModel), + sortModel: JSON.stringify(params.sortModel), + groupKeys: JSON.stringify(params.groupKeys), + groupFields: JSON.stringify(params.groupFields), + aggregationModel: JSON.stringify(params.aggregationModel), + }); + const getRowsResponse = await fetchRows( + `https://mui.com/x/api/data-grid?${urlParams.toString()}`, + ); + return { + rows: getRowsResponse.rows, + rowCount: getRowsResponse.rowCount, + aggregateRow: getRowsResponse.aggregateRow, + }; + }, + getGroupKey: (row) => row.group, + getChildrenCount: (row) => row.descendantCount, + getAggregatedValue: (row, field) => row[`${field}Aggregate`], + }), + [fetchRows], + ); + + const initialState = useKeepGroupedColumnsHidden({ + apiRef, + initialState: { + ...initState, + rowGrouping: { + model: ['company', 'director'], + }, + aggregation: { + model: { title: 'size', gross: 'sum', year: 'max' }, + }, + }, + }); + + return ( +
+ +
+ ); +} diff --git a/docs/data/data-grid/server-side-data/ServerSideDataGridAggregationRowGrouping.tsx.preview b/docs/data/data-grid/server-side-data/ServerSideDataGridAggregationRowGrouping.tsx.preview new file mode 100644 index 0000000000000..b2d4bf7135a06 --- /dev/null +++ b/docs/data/data-grid/server-side-data/ServerSideDataGridAggregationRowGrouping.tsx.preview @@ -0,0 +1,7 @@ + \ No newline at end of file diff --git a/docs/data/data-grid/server-side-data/ServerSideDataGridAggregationTreeData.js b/docs/data/data-grid/server-side-data/ServerSideDataGridAggregationTreeData.js new file mode 100644 index 0000000000000..3834980e55020 --- /dev/null +++ b/docs/data/data-grid/server-side-data/ServerSideDataGridAggregationTreeData.js @@ -0,0 +1,75 @@ +import * as React from 'react'; +import { DataGridPremium, useGridApiRef } from '@mui/x-data-grid-premium'; +import { useMockServer } from '@mui/x-data-grid-generator'; + +const dataSetOptions = { + dataSet: 'Employee', + rowLength: 1000, + treeData: { maxDepth: 3, groupingField: 'name', averageChildren: 5 }, +}; + +const aggregationFunctions = { + sum: { columnTypes: ['number'] }, + avg: { columnTypes: ['number'] }, + min: { columnTypes: ['number', 'date', 'dateTime'] }, + max: { columnTypes: ['number', 'date', 'dateTime'] }, + size: {}, +}; + +export default function ServerSideDataGridAggregationTreeData() { + const apiRef = useGridApiRef(); + const { + fetchRows, + columns, + initialState: initState, + } = useMockServer(dataSetOptions); + + const initialState = React.useMemo( + () => ({ + ...initState, + aggregation: { + model: { rating: 'avg', website: 'size' }, + }, + }), + [initState], + ); + + const dataSource = React.useMemo( + () => ({ + getRows: async (params) => { + const urlParams = new URLSearchParams({ + paginationModel: JSON.stringify(params.paginationModel), + filterModel: JSON.stringify(params.filterModel), + sortModel: JSON.stringify(params.sortModel), + groupKeys: JSON.stringify(params.groupKeys), + aggregationModel: JSON.stringify(params.aggregationModel), + }); + const getRowsResponse = await fetchRows( + `https://mui.com/x/api/data-grid?${urlParams.toString()}`, + ); + return { + rows: getRowsResponse.rows, + rowCount: getRowsResponse.rowCount, + aggregateRow: getRowsResponse.aggregateRow, + }; + }, + getGroupKey: (row) => row[dataSetOptions.treeData.groupingField], + getChildrenCount: (row) => row.descendantCount, + getAggregatedValue: (row, field) => row[`${field}Aggregate`], + }), + [fetchRows], + ); + + return ( +
+ +
+ ); +} diff --git a/docs/data/data-grid/server-side-data/ServerSideDataGridAggregationTreeData.tsx b/docs/data/data-grid/server-side-data/ServerSideDataGridAggregationTreeData.tsx new file mode 100644 index 0000000000000..cf55fabc541d5 --- /dev/null +++ b/docs/data/data-grid/server-side-data/ServerSideDataGridAggregationTreeData.tsx @@ -0,0 +1,80 @@ +import * as React from 'react'; +import { + DataGridPremium, + useGridApiRef, + GridInitialState, + GridDataSource, +} from '@mui/x-data-grid-premium'; +import { useMockServer } from '@mui/x-data-grid-generator'; + +const dataSetOptions = { + dataSet: 'Employee' as const, + rowLength: 1000, + treeData: { maxDepth: 3, groupingField: 'name', averageChildren: 5 }, +}; + +const aggregationFunctions = { + sum: { columnTypes: ['number'] }, + avg: { columnTypes: ['number'] }, + min: { columnTypes: ['number', 'date', 'dateTime'] }, + max: { columnTypes: ['number', 'date', 'dateTime'] }, + size: {}, +}; + +export default function ServerSideDataGridAggregationTreeData() { + const apiRef = useGridApiRef(); + const { + fetchRows, + columns, + initialState: initState, + } = useMockServer(dataSetOptions); + + const initialState: GridInitialState = React.useMemo( + () => ({ + ...initState, + aggregation: { + model: { rating: 'avg', website: 'size' }, + }, + }), + [initState], + ); + + const dataSource: GridDataSource = React.useMemo( + () => ({ + getRows: async (params) => { + const urlParams = new URLSearchParams({ + paginationModel: JSON.stringify(params.paginationModel), + filterModel: JSON.stringify(params.filterModel), + sortModel: JSON.stringify(params.sortModel), + groupKeys: JSON.stringify(params.groupKeys), + aggregationModel: JSON.stringify(params.aggregationModel), + }); + const getRowsResponse = await fetchRows( + `https://mui.com/x/api/data-grid?${urlParams.toString()}`, + ); + return { + rows: getRowsResponse.rows, + rowCount: getRowsResponse.rowCount, + aggregateRow: getRowsResponse.aggregateRow, + }; + }, + getGroupKey: (row) => row[dataSetOptions.treeData.groupingField], + getChildrenCount: (row) => row.descendantCount, + getAggregatedValue: (row, field) => row[`${field}Aggregate`], + }), + [fetchRows], + ); + + return ( +
+ +
+ ); +} diff --git a/docs/data/data-grid/server-side-data/ServerSideDataGridAggregationTreeData.tsx.preview b/docs/data/data-grid/server-side-data/ServerSideDataGridAggregationTreeData.tsx.preview new file mode 100644 index 0000000000000..6eee2299964e8 --- /dev/null +++ b/docs/data/data-grid/server-side-data/ServerSideDataGridAggregationTreeData.tsx.preview @@ -0,0 +1,8 @@ + \ No newline at end of file diff --git a/docs/data/data-grid/server-side-data/aggregation.md b/docs/data/data-grid/server-side-data/aggregation.md index e11485b7d80ce..7b94fbaf0e76d 100644 --- a/docs/data/data-grid/server-side-data/aggregation.md +++ b/docs/data/data-grid/server-side-data/aggregation.md @@ -2,14 +2,103 @@ title: React Data Grid - Server-side aggregation --- -# Data Grid - Server-side aggregation [](/x/introduction/licensing/#premium-plan 'Premium plan')🚧 +# Data Grid - Server-side aggregation [](/x/introduction/licensing/#premium-plan 'Premium plan')🧪

Aggregation with server-side data source.

-:::warning -This feature isn't implemented yet. It's coming. +To dynamically load tree data from the server, you must create a data source and pass the `unstable_dataSource` prop to the Data Grid, as detailed in the [Server-side data overview](/x/react-data-grid/server-side-data/). -👍 Upvote [issue #10860](https://github.com/mui/mui-x/issues/10860) if you want to see it land faster. +:::info +If you're looking for aggregation on the client side, see [Aggregation](/x/react-data-grid/aggregation/). +::: + +Server-side aggregation requires some additional steps to implement: + +1. Pass the available aggregation functions of type `GridAggregationFunctionDataSource` to the Data Grid using the `aggregationFunctions` prop. Its default value is empty when the Data Grid is used with server-side data. + + ```tsx + const aggregationFunctions: Record = { + size: { label: 'Size' }, + sum: { label: 'Sum', columnTypes: ['number'] }, + } + + + ``` + + The `GridAggregationFunctionDataSource` interface is similar to `GridAggregationFunction`, but it doesn't have `apply` or `getCellValue` properties because the computation is done on the server. + + See the [GridAggregationFunctionDataSource API page](/x/api/data-grid/grid-aggregation-function-data-source/) for more details. + +2. Use `aggregationModel` passed in the `getRows` method of `GridDataSource` to fetch the aggregated values. + For the root level footer aggregation row, pass `aggregateRow` containing the aggregated values in the `GetRowsResponse`. + + ```diff + const dataSource = { + getRows: async ({ + sortModel, + filterModel, + paginationModel, + + aggregationModel, + }) => { + const rows = await fetchRows(); + - const response = await fetchData({ sortModel, filterModel, paginationModel }); + + const response = await fetchData({ sortModel, filterModel, paginationModel, aggregationModel }); + return { + rows: response.rows, + rowCount: getRowsResponse.totalCount, + + aggregateRow: response.aggregateRow, + } + } + } + ``` + +3. Pass the getter method `getAggregatedValue` in `GridDataSource` that defines how to get the aggregated value for a parent row (including the `aggregateRow`). + + ```tsx + const dataSource = { + getRows: async ({ + ... + }) => { + ... + }, + getAggregatedValue: (row, field) => { + return row[`${field}Aggregate`]; + }, + } + ``` + +The following example demonstrates basic server-side aggregation. -Don't hesitate to leave a comment on the same issue to influence what gets built. Especially if you already have a use case for this component, or if you are facing a pain point with your current solution. +{{"demo": "ServerSideDataGridAggregation.js", "bg": "inline"}} + +:::info +The data source mock server (`useMockServer()`) mocks the built-in aggregation functions listed in the [built-in functions section](/x/react-data-grid/aggregation/#built-in-functions) of the client-side aggregation documentation. +Provide the function names and minimal configuration to demonstrate the aggregation, as shown in the demo. ::: + +## Usage with lazy loading + +Server-side aggregation can be implemented along with [server-side lazy loading](/x/react-data-grid/server-side-data/lazy-loading/) as shown in the demo below. + +{{"demo": "ServerSideDataGridAggregationLazyLoading.js", "bg": "inline"}} + +## Usage with row grouping + +Server-side aggregation works with row grouping in a similar way as described in [Aggregation—usage with row grouping](/x/react-data-grid/aggregation/#usage-with-row-grouping). +The aggregated values are acquired from the parent rows using the `getAggregatedValue` method. + +{{"demo": "ServerSideDataGridAggregationRowGrouping.js", "bg": "inline"}} + +## Usage with tree data + +Server-side aggregation can be used with tree data in a similar way as described in [Aggregation—usage with tree data](/x/react-data-grid/aggregation/#usage-with-tree-data). +The aggregated values are acquired from the parent rows using the `getAggregatedValue` method. + +{{"demo": "ServerSideDataGridAggregationTreeData.js", "bg": "inline"}} + +## API + +- [DataGrid](/x/api/data-grid/data-grid/) +- [DataGridPro](/x/api/data-grid/data-grid-pro/) +- [DataGridPremium](/x/api/data-grid/data-grid-premium/) +- [GridAggregationFunctionDataSource](/x/api/data-grid/grid-aggregation-function-data-source/) diff --git a/docs/data/pages.ts b/docs/data/pages.ts index e0d0eabaaf69a..bb8364631030c 100644 --- a/docs/data/pages.ts +++ b/docs/data/pages.ts @@ -164,7 +164,7 @@ const pages: MuiPage[] = [ { pathname: '/x/react-data-grid/server-side-data/aggregation', plan: 'premium', - planned: true, + unstable: true, }, ], }, @@ -234,6 +234,10 @@ const pages: MuiPage[] = [ pathname: '/x/api/data-grid/grid-aggregation-function', title: 'GridAggregationFunction', }, + { + pathname: '/x/api/data-grid/grid-aggregation-function-data-source', + title: 'GridAggregationFunctionDataSource', + }, { pathname: '/x/api/data-grid/grid-csv-export-options', title: 'GridCsvExportOptions', diff --git a/docs/pages/x/api/data-grid/data-grid-premium.json b/docs/pages/x/api/data-grid/data-grid-premium.json index 950b63871a3ce..a2c48c6a533db 100644 --- a/docs/pages/x/api/data-grid/data-grid-premium.json +++ b/docs/pages/x/api/data-grid/data-grid-premium.json @@ -6,7 +6,7 @@ }, "aggregationFunctions": { "type": { "name": "object" }, - "default": "GRID_AGGREGATION_FUNCTIONS" + "default": "GRID_AGGREGATION_FUNCTIONS when `unstable_dataSource` is not provided, `{}` when `unstable_dataSource` is provided" }, "aggregationModel": { "type": { "name": "object" } }, "aggregationRowsScope": { diff --git a/docs/pages/x/api/data-grid/grid-aggregation-function-data-source.js b/docs/pages/x/api/data-grid/grid-aggregation-function-data-source.js new file mode 100644 index 0000000000000..c78470ce999b9 --- /dev/null +++ b/docs/pages/x/api/data-grid/grid-aggregation-function-data-source.js @@ -0,0 +1,26 @@ +import * as React from 'react'; +import InterfaceApiPage from 'docsx/src/modules/components/InterfaceApiPage'; +import layoutConfig from 'docsx/src/modules/utils/dataGridLayoutConfig'; +import mapApiPageTranslations from 'docs/src/modules/utils/mapApiPageTranslations'; +import jsonPageContent from './grid-aggregation-function-data-source.json'; + +export default function Page(props) { + const { descriptions, pageContent } = props; + return ( + + ); +} + +Page.getInitialProps = () => { + const req = require.context( + 'docsx/translations/api-docs/data-grid/', + false, + /\.\/grid-aggregation-function-data-source.*.json$/, + ); + const descriptions = mapApiPageTranslations(req); + + return { + descriptions, + pageContent: jsonPageContent, + }; +}; diff --git a/docs/pages/x/api/data-grid/grid-aggregation-function-data-source.json b/docs/pages/x/api/data-grid/grid-aggregation-function-data-source.json new file mode 100644 index 0000000000000..75ba83bcced16 --- /dev/null +++ b/docs/pages/x/api/data-grid/grid-aggregation-function-data-source.json @@ -0,0 +1,19 @@ +{ + "name": "GridAggregationFunctionDataSource", + "imports": ["import { GridAggregationFunctionDataSource } from '@mui/x-data-grid-premium'"], + "demos": "", + "properties": { + "columnTypes": { "type": { "description": "string[]" }, "isPremiumPlan": true }, + "hasCellUnit": { + "type": { "description": "boolean" }, + "default": "true", + "isPremiumPlan": true + }, + "label": { + "type": { "description": "string" }, + "default": "apiRef.current.getLocaleText('aggregationFunctionLabel{capitalize(name)})", + "isPremiumPlan": true + }, + "valueFormatter": { "type": { "description": "GridValueFormatter" }, "isPremiumPlan": true } + } +} diff --git a/docs/pages/x/api/data-grid/index.md b/docs/pages/x/api/data-grid/index.md index 59c68e9a9b1fe..b8b5142ec3fc5 100644 --- a/docs/pages/x/api/data-grid/index.md +++ b/docs/pages/x/api/data-grid/index.md @@ -26,6 +26,7 @@ - [GridRowParams](/x/api/data-grid/grid-row-params/) - [GridRowSpacingParams](/x/api/data-grid/grid-row-spacing-params/) - [GridAggregationFunction](/x/api/data-grid/grid-aggregation-function/) +- [GridAggregationFunctionDataSource](/x/api/data-grid/grid-aggregation-function-data-source/) - [GridCsvExportOptions](/x/api/data-grid/grid-csv-export-options/) - [GridPrintExportOptions](/x/api/data-grid/grid-print-export-options/) - [GridExcelExportOptions](/x/api/data-grid/grid-excel-export-options/) diff --git a/docs/scripts/createXTypeScriptProjects.ts b/docs/scripts/createXTypeScriptProjects.ts index c0f24cb61a2d5..8fc0a872f29ca 100644 --- a/docs/scripts/createXTypeScriptProjects.ts +++ b/docs/scripts/createXTypeScriptProjects.ts @@ -152,6 +152,7 @@ export const interfacesToDocument: InterfacesToDocumentType[] = [ // Aggregation 'GridAggregationFunction', + 'GridAggregationFunctionDataSource', ], }, { diff --git a/docs/translations/api-docs/data-grid/grid-aggregation-function-data-source.json b/docs/translations/api-docs/data-grid/grid-aggregation-function-data-source.json new file mode 100644 index 0000000000000..2491dd0ed1396 --- /dev/null +++ b/docs/translations/api-docs/data-grid/grid-aggregation-function-data-source.json @@ -0,0 +1,17 @@ +{ + "interfaceDescription": "Grid aggregation function data source definition interface.", + "propertiesDescriptions": { + "columnTypes": { + "description": "Column types supported by this aggregation function.
If not defined, all types are supported (in most cases this property should be defined)." + }, + "hasCellUnit": { + "description": "Indicates if the aggregated value has the same unit as the cells used to generate it.
It can be used to apply a custom cell renderer only if the aggregated value has the same unit." + }, + "label": { + "description": "Label of the aggregation function.
Used for adding a label to the footer of the grouping column when this aggregation function is the only one being used." + }, + "valueFormatter": { + "description": "Function for applying a formatter to the aggregated value.
If not defined, the grid uses the formatter of the column." + } + } +} diff --git a/docs/translations/api-docs/data-grid/grid-aggregation-function.json b/docs/translations/api-docs/data-grid/grid-aggregation-function.json index 4a5bee820d1e9..7cb6c76d06a19 100644 --- a/docs/translations/api-docs/data-grid/grid-aggregation-function.json +++ b/docs/translations/api-docs/data-grid/grid-aggregation-function.json @@ -11,13 +11,13 @@ "description": "Function that allows to transform the value of the cell passed to the aggregation function applier.
Useful for aggregating data from multiple row fields." }, "hasCellUnit": { - "description": "Indicates if the aggregated value have the same unit as the cells used to generate it.
It can be used to apply a custom cell renderer only if the aggregated value has the same unit." + "description": "Indicates if the aggregated value has the same unit as the cells used to generate it.
It can be used to apply a custom cell renderer only if the aggregated value has the same unit." }, "label": { - "description": "Label of the aggregation function.
Will be used to add a label on the footer of the grouping column when this aggregation function is the only one being used." + "description": "Label of the aggregation function.
Used for adding a label to the footer of the grouping column when this aggregation function is the only one being used." }, "valueFormatter": { - "description": "Function that allows to apply a formatter to the aggregated value.
If not defined, the grid will use the formatter of the column." + "description": "Function for applying a formatter to the aggregated value.
If not defined, the grid uses the formatter of the column." } } } diff --git a/packages/x-data-grid-generator/src/hooks/serverUtils.ts b/packages/x-data-grid-generator/src/hooks/serverUtils.ts index c0643d608e923..b44a26a1bf7f4 100644 --- a/packages/x-data-grid-generator/src/hooks/serverUtils.ts +++ b/packages/x-data-grid-generator/src/hooks/serverUtils.ts @@ -8,12 +8,32 @@ import { GridRowId, GridPaginationModel, GridValidRowModel, -} from '@mui/x-data-grid-pro'; + GRID_AGGREGATION_FUNCTIONS, + GridAggregationModel, + GridAggregationFunction, +} from '@mui/x-data-grid-premium'; import { GridStateColDef } from '@mui/x-data-grid-pro/internals'; import { randomInt } from '../services/random-generator'; +const getAvailableAggregationFunctions = (columnType: GridColDef['type']) => { + const availableAggregationFunctions = new Map(); + Object.keys(GRID_AGGREGATION_FUNCTIONS).forEach((functionName) => { + const columnTypes = + GRID_AGGREGATION_FUNCTIONS[functionName as keyof typeof GRID_AGGREGATION_FUNCTIONS] + .columnTypes; + if (!columnTypes || columnTypes.includes(columnType ?? 'string')) { + availableAggregationFunctions.set( + functionName, + GRID_AGGREGATION_FUNCTIONS[functionName as keyof typeof GRID_AGGREGATION_FUNCTIONS], + ); + } + }); + return availableAggregationFunctions; +}; + export interface FakeServerResponse { returnedRows: GridRowModel[]; + aggregateRow?: GridValidRowModel; nextCursor?: string; hasNextPage?: boolean; totalRowCount: number; @@ -39,6 +59,7 @@ export interface QueryOptions { page?: number; pageSize?: number; filterModel?: GridFilterModel; + aggregationModel?: GridAggregationModel; sortModel?: GridSortModel; start?: number; end?: number; @@ -50,6 +71,7 @@ export interface ServerSideQueryOptions { groupKeys?: string[]; filterModel?: GridFilterModel; sortModel?: GridSortModel; + aggregationModel?: GridAggregationModel; start?: number; end?: number; groupFields?: string[]; @@ -261,6 +283,41 @@ const getFilteredRows = ( ); }; +const applyAggregation = ( + aggregationModel: GridAggregationModel, + colDefs: GridColDef[], + rows: GridRowModel[], + groupId: string = 'root', +) => { + const columnsToAggregate = Object.keys(aggregationModel); + if (columnsToAggregate.length === 0) { + return {}; + } + + const aggregateValues: GridValidRowModel = {}; + columnsToAggregate.forEach((field) => { + const type = colDefs.find(({ field: f }) => f === field)?.type; + if (!type) { + return; + } + const availableAggregationFunctions = getAvailableAggregationFunctions(type); + if (!availableAggregationFunctions.has(aggregationModel[field])) { + return; + } + const aggregationFunction = availableAggregationFunctions.get(aggregationModel[field]); + if (!aggregationFunction) { + return; + } + const values = rows.map((row) => row[field]); + aggregateValues[`${field}Aggregate`] = aggregationFunction.apply({ + values, + field, + groupId, + }); + }); + return aggregateValues; +}; + /** * Simulates server data loading */ @@ -288,6 +345,15 @@ export const loadServerRows = ( const rowComparator = getRowComparator(queryOptions.sortModel, columnsWithDefaultColDef); filteredRows = [...filteredRows].sort(rowComparator); + let aggregateRow = {}; + if (queryOptions.aggregationModel) { + aggregateRow = applyAggregation( + queryOptions.aggregationModel, + columnsWithDefaultColDef, + filteredRows, + ); + } + const totalRowCount = filteredRows.length; if (start !== undefined && end !== undefined) { firstRowIndex = start; @@ -311,6 +377,7 @@ export const loadServerRows = ( hasNextPage, nextCursor, totalRowCount, + ...(queryOptions.aggregationModel ? { aggregateRow } : {}), }; return new Promise((resolve) => { @@ -323,6 +390,7 @@ export const loadServerRows = ( interface NestedDataRowsResponse { rows: GridRowModel[]; rootRowCount: number; + aggregateRow?: GridRowModel; } const findTreeDataRowChildren = ( @@ -464,6 +532,19 @@ export const processTreeDataRows = ( let childRowsWithDescendantCounts = childRows.map((row) => { const descendants = findTreeDataRowChildren(filteredRows, row[pathKey], pathKey, -1); const descendantCount = descendants.length; + if (descendantCount > 0 && queryOptions.aggregationModel) { + // Parent row, compute aggregation + return { + ...row, + descendantCount, + ...applyAggregation( + queryOptions.aggregationModel, + columnsWithDefaultColDef, + descendants, + row.id, + ), + }; + } return { ...row, descendantCount } as GridRowModel; }); @@ -473,6 +554,15 @@ export const processTreeDataRows = ( childRowsWithDescendantCounts = [...childRowsWithDescendantCounts].sort(rowComparator); } + let aggregateRow: GridRowModel | undefined; + if (queryOptions.aggregationModel) { + aggregateRow = applyAggregation( + queryOptions.aggregationModel, + columnsWithDefaultColDef, + filteredRows, + ); + } + if (queryOptions.paginationModel && queryOptions.groupKeys.length === 0) { // Only paginate root rows, grid should refetch root rows when `paginationModel` updates const { pageSize, page } = queryOptions.paginationModel; @@ -486,7 +576,7 @@ export const processTreeDataRows = ( return new Promise((resolve) => { setTimeout(() => { - resolve({ rows: childRowsWithDescendantCounts, rootRowCount }); + resolve({ rows: childRowsWithDescendantCounts, rootRowCount, aggregateRow }); }, delay); // simulate network latency }); }; @@ -584,6 +674,19 @@ export const processRowGroupingRows = ( ({ id }) => typeof id !== 'string' || !id.startsWith('auto-generated-parent-'), ); const descendantCount = descendants.length; + if (descendantCount > 0 && queryOptions.aggregationModel) { + // Parent row, compute aggregation + return { + ...row, + descendantCount, + ...applyAggregation( + queryOptions.aggregationModel, + columnsWithDefaultColDef, + descendants, + row.id, + ), + }; + } return { ...row, descendantCount } as GridRowModel; }); @@ -594,6 +697,15 @@ export const processRowGroupingRows = ( childRowsWithDescendantCounts = [...sortedMissingGroups, ...sortedChildRows]; } + let aggregateRow: GridRowModel | undefined; + if (queryOptions.aggregationModel) { + aggregateRow = applyAggregation( + queryOptions.aggregationModel, + columnsWithDefaultColDef, + filteredRows, + ); + } + if (queryOptions.paginationModel && queryOptions.groupKeys.length === 0) { // Only paginate root rows, grid should refetch root rows when `paginationModel` updates const { pageSize, page } = queryOptions.paginationModel; @@ -607,7 +719,7 @@ export const processRowGroupingRows = ( return new Promise((resolve) => { setTimeout(() => { - resolve({ rows: childRowsWithDescendantCounts, rootRowCount }); + resolve({ rows: childRowsWithDescendantCounts, rootRowCount, aggregateRow }); }, delay); // simulate network latency }); }; diff --git a/packages/x-data-grid-generator/src/hooks/useMockServer.ts b/packages/x-data-grid-generator/src/hooks/useMockServer.ts index 9fb8aff79a8c2..a150839bb0eb4 100644 --- a/packages/x-data-grid-generator/src/hooks/useMockServer.ts +++ b/packages/x-data-grid-generator/src/hooks/useMockServer.ts @@ -7,7 +7,7 @@ import { GridColDef, GridInitialState, GridColumnVisibilityModel, -} from '@mui/x-data-grid-pro'; +} from '@mui/x-data-grid-premium'; import { extrapolateSeed, deepFreeze } from './useDemoData'; import { getCommodityColumns } from '../columns/commodities.columns'; import { getEmployeeColumns } from '../columns/employees.columns'; @@ -298,7 +298,7 @@ export const useMockServer = ( } if (isTreeData) { - const { rows, rootRowCount } = await processTreeDataRows( + const { rows, rootRowCount, aggregateRow } = await processTreeDataRows( data?.rows ?? [], params, serverOptionsWithDefault, @@ -308,9 +308,10 @@ export const useMockServer = ( getRowsResponse = { rows: rows.slice().map((row) => ({ ...row, path: undefined })), rowCount: rootRowCount, + ...(aggregateRow ? { aggregateRow } : {}), }; } else if (isRowGrouping) { - const { rows, rootRowCount } = await processRowGroupingRows( + const { rows, rootRowCount, aggregateRow } = await processRowGroupingRows( data?.rows ?? [], params, serverOptionsWithDefault, @@ -320,15 +321,21 @@ export const useMockServer = ( getRowsResponse = { rows: rows.slice().map((row) => ({ ...row, path: undefined })), rowCount: rootRowCount, + ...(aggregateRow ? { aggregateRow } : {}), }; } else { - const { returnedRows, nextCursor, totalRowCount } = await loadServerRows( + const { returnedRows, nextCursor, totalRowCount, aggregateRow } = await loadServerRows( data?.rows ?? [], { ...params, ...params.paginationModel }, serverOptionsWithDefault, columnsWithDefaultColDef, ); - getRowsResponse = { rows: returnedRows, rowCount: totalRowCount, pageInfo: { nextCursor } }; + getRowsResponse = { + rows: returnedRows, + rowCount: totalRowCount, + pageInfo: { nextCursor }, + ...(aggregateRow ? { aggregateRow } : {}), + }; } return new Promise((resolve) => { diff --git a/packages/x-data-grid-premium/src/DataGridPremium/DataGridPremium.tsx b/packages/x-data-grid-premium/src/DataGridPremium/DataGridPremium.tsx index c91e68a462c5a..6160575fb7ff9 100644 --- a/packages/x-data-grid-premium/src/DataGridPremium/DataGridPremium.tsx +++ b/packages/x-data-grid-premium/src/DataGridPremium/DataGridPremium.tsx @@ -80,7 +80,7 @@ DataGridPremiumRaw.propTypes = { // ---------------------------------------------------------------------- /** * Aggregation functions available on the grid. - * @default GRID_AGGREGATION_FUNCTIONS + * @default GRID_AGGREGATION_FUNCTIONS when `unstable_dataSource` is not provided, `{}` when `unstable_dataSource` is provided */ aggregationFunctions: PropTypes.object, /** @@ -1080,6 +1080,7 @@ DataGridPremiumRaw.propTypes = { */ treeData: PropTypes.bool, unstable_dataSource: PropTypes.shape({ + getAggregatedValue: PropTypes.func, getChildrenCount: PropTypes.func, getGroupKey: PropTypes.func, getRows: PropTypes.func.isRequired, diff --git a/packages/x-data-grid-premium/src/DataGridPremium/useDataGridPremiumComponent.tsx b/packages/x-data-grid-premium/src/DataGridPremium/useDataGridPremiumComponent.tsx index 5e3f20c6ea49d..d1b931e524a69 100644 --- a/packages/x-data-grid-premium/src/DataGridPremium/useDataGridPremiumComponent.tsx +++ b/packages/x-data-grid-premium/src/DataGridPremium/useDataGridPremiumComponent.tsx @@ -67,7 +67,6 @@ import { virtualizationStateInitializer, useGridVirtualization, useGridDataSourceTreeDataPreProcessors, - useGridDataSource, dataSourceStateInitializer, useGridRowSpanning, rowSpanningStateInitializer, @@ -76,6 +75,7 @@ import { } from '@mui/x-data-grid-pro/internals'; import { GridApiPremium, GridPrivateApiPremium } from '../models/gridApiPremium'; import { DataGridPremiumProcessedProps } from '../models/dataGridPremiumProps'; +import { useGridDataSourcePremium as useGridDataSource } from '../hooks/features/dataSource/useGridDataSourcePremium'; // Premium-only features import { useGridAggregation, @@ -153,6 +153,7 @@ export const useDataGridPremiumComponent = ( useGridRowGrouping(apiRef, props); useGridHeaderFiltering(apiRef, props); useGridTreeData(apiRef, props); + useGridDataSource(apiRef, props); useGridAggregation(apiRef, props); useGridKeyboardNavigation(apiRef, props); useGridRowSelection(apiRef, props); @@ -190,7 +191,6 @@ export const useDataGridPremiumComponent = ( useGridDimensions(apiRef, props); useGridEvents(apiRef, props); useGridStatePersistence(apiRef); - useGridDataSource(apiRef, props); useGridVirtualization(apiRef, props); useGridListView(apiRef, props); diff --git a/packages/x-data-grid-premium/src/DataGridPremium/useDataGridPremiumProps.ts b/packages/x-data-grid-premium/src/DataGridPremium/useDataGridPremiumProps.ts index c047e56429235..bb316ad4c9e3e 100644 --- a/packages/x-data-grid-premium/src/DataGridPremium/useDataGridPremiumProps.ts +++ b/packages/x-data-grid-premium/src/DataGridPremium/useDataGridPremiumProps.ts @@ -84,6 +84,7 @@ export const useDataGridPremiumProps = (inProps: DataGridPremiumProps) => { return React.useMemo( () => ({ ...DATA_GRID_PREMIUM_PROPS_DEFAULT_VALUES, + ...(themedProps.unstable_dataSource ? { aggregationFunctions: {} } : {}), ...themedProps, localeText, slots, diff --git a/packages/x-data-grid-premium/src/components/GridColumnMenuAggregationItem.tsx b/packages/x-data-grid-premium/src/components/GridColumnMenuAggregationItem.tsx index 522edfd53b492..4bbe750029925 100644 --- a/packages/x-data-grid-premium/src/components/GridColumnMenuAggregationItem.tsx +++ b/packages/x-data-grid-premium/src/components/GridColumnMenuAggregationItem.tsx @@ -27,8 +27,9 @@ function GridColumnMenuAggregationItem(props: GridColumnMenuItemProps) { getAvailableAggregationFunctions({ aggregationFunctions: rootProps.aggregationFunctions, colDef, + isDataSource: !!rootProps.unstable_dataSource, }), - [colDef, rootProps.aggregationFunctions], + [colDef, rootProps.aggregationFunctions, rootProps.unstable_dataSource], ); const { native: isBaseSelectNative = false, ...baseSelectProps } = rootProps.slotProps?.baseSelect || {}; @@ -45,13 +46,14 @@ function GridColumnMenuAggregationItem(props: GridColumnMenuItemProps) { colDef, aggregationFunctionName, aggregationFunction: rootProps.aggregationFunctions[aggregationFunctionName], + isDataSource: !!rootProps.unstable_dataSource, }) ) { return aggregationFunctionName; } return ''; - }, [rootProps.aggregationFunctions, aggregationModel, colDef]); + }, [rootProps.aggregationFunctions, rootProps.unstable_dataSource, aggregationModel, colDef]); const handleAggregationItemChange = (event: SelectChangeEvent) => { const newAggregationItem = (event.target as HTMLSelectElement | null)?.value || undefined; diff --git a/packages/x-data-grid-premium/src/hooks/features/aggregation/createAggregationLookup.ts b/packages/x-data-grid-premium/src/hooks/features/aggregation/createAggregationLookup.ts index 4294bf38db5ca..1a8b81c3585fd 100644 --- a/packages/x-data-grid-premium/src/hooks/features/aggregation/createAggregationLookup.ts +++ b/packages/x-data-grid-premium/src/hooks/features/aggregation/createAggregationLookup.ts @@ -8,10 +8,11 @@ import { gridRowTreeSelector, GRID_ROOT_GROUP_ID, } from '@mui/x-data-grid-pro'; -import { GridApiPremium } from '../../../models/gridApiPremium'; +import { GridPrivateApiPremium } from '../../../models/gridApiPremium'; import { DataGridPremiumProcessedProps } from '../../../models/dataGridPremiumProps'; import { GridAggregationFunction, + GridAggregationFunctionDataSource, GridAggregationLookup, GridAggregationPosition, GridAggregationRules, @@ -21,7 +22,7 @@ import { gridAggregationModelSelector } from './gridAggregationSelectors'; const getGroupAggregatedValue = ( groupId: GridRowId, - apiRef: React.MutableRefObject, + apiRef: React.MutableRefObject, aggregationRowsScope: DataGridPremiumProcessedProps['aggregationRowsScope'], aggregatedFields: string[], aggregationRules: GridAggregationRules, @@ -56,7 +57,8 @@ const getGroupAggregatedValue = ( const aggregatedField = aggregatedFields[j]; const columnAggregationRules = aggregationRules[aggregatedField]; - const aggregationFunction = columnAggregationRules.aggregationFunction; + const aggregationFunction = + columnAggregationRules.aggregationFunction as GridAggregationFunction; const field = aggregatedField; if (aggregatedValues[j] === undefined) { @@ -77,7 +79,8 @@ const getGroupAggregatedValue = ( for (let i = 0; i < aggregatedValues.length; i += 1) { const { aggregatedField, values } = aggregatedValues[i]; - const aggregationFunction = aggregationRules[aggregatedField].aggregationFunction; + const aggregationFunction = aggregationRules[aggregatedField] + .aggregationFunction as GridAggregationFunction; const value = aggregationFunction.apply({ values, groupId, @@ -93,21 +96,46 @@ const getGroupAggregatedValue = ( return groupAggregationLookup; }; +const getGroupAggregatedValueDataSource = ( + groupId: GridRowId, + apiRef: React.MutableRefObject, + aggregatedFields: string[], + position: GridAggregationPosition, +) => { + const groupAggregationLookup: GridAggregationLookup[GridRowId] = {}; + + for (let j = 0; j < aggregatedFields.length; j += 1) { + const aggregatedField = aggregatedFields[j]; + + groupAggregationLookup[aggregatedField] = { + position, + value: apiRef.current.resolveGroupAggregation(groupId, aggregatedField), + }; + } + + return groupAggregationLookup; +}; + export const createAggregationLookup = ({ apiRef, aggregationFunctions, aggregationRowsScope, getAggregationPosition, + isDataSource, }: { - apiRef: React.MutableRefObject; - aggregationFunctions: Record; + apiRef: React.MutableRefObject; + aggregationFunctions: + | Record + | Record; aggregationRowsScope: DataGridPremiumProcessedProps['aggregationRowsScope']; getAggregationPosition: DataGridPremiumProcessedProps['getAggregationPosition']; + isDataSource: boolean; }): GridAggregationLookup => { const aggregationRules = getAggregationRules( gridColumnLookupSelector(apiRef), gridAggregationModelSelector(apiRef), aggregationFunctions, + isDataSource, ); const aggregatedFields = Object.keys(aggregationRules); @@ -128,10 +156,17 @@ export const createAggregationLookup = ({ } } - const hasAggregableChildren = groupNode.children.length; - if (hasAggregableChildren) { - const position = getAggregationPosition(groupNode); - if (position != null) { + const position = getAggregationPosition(groupNode); + + if (position !== null) { + if (isDataSource) { + aggregationLookup[groupNode.id] = getGroupAggregatedValueDataSource( + groupNode.id, + apiRef, + aggregatedFields, + position, + ); + } else if (groupNode.children.length) { aggregationLookup[groupNode.id] = getGroupAggregatedValue( groupNode.id, apiRef, diff --git a/packages/x-data-grid-premium/src/hooks/features/aggregation/gridAggregationInterfaces.ts b/packages/x-data-grid-premium/src/hooks/features/aggregation/gridAggregationInterfaces.ts index 212bdef1a0083..96e584d233037 100644 --- a/packages/x-data-grid-premium/src/hooks/features/aggregation/gridAggregationInterfaces.ts +++ b/packages/x-data-grid-premium/src/hooks/features/aggregation/gridAggregationInterfaces.ts @@ -22,6 +22,13 @@ export interface GridAggregationApi { setAggregationModel: (model: GridAggregationModel) => void; } +export interface GridAggregationPrivateApi { + /** + * Applies the aggregation to the rows. + */ + applyAggregation: () => void; +} + export interface GridAggregationGetCellValueParams { /** * The row model of the row that the current cell belongs to. @@ -44,7 +51,7 @@ export interface GridAggregationFunction { apply: (params: GridAggregationParams) => AV | null | undefined; /** * Label of the aggregation function. - * Will be used to add a label on the footer of the grouping column when this aggregation function is the only one being used. + * Used for adding a label to the footer of the grouping column when this aggregation function is the only one being used. * @default apiRef.current.getLocaleText('aggregationFunctionLabel{capitalize(name)}) */ label?: string; @@ -54,12 +61,12 @@ export interface GridAggregationFunction { */ columnTypes?: string[]; /** - * Function that allows to apply a formatter to the aggregated value. - * If not defined, the grid will use the formatter of the column. + * Function for applying a formatter to the aggregated value. + * If not defined, the grid uses the formatter of the column. */ valueFormatter?: GridValueFormatter; /** - * Indicates if the aggregated value have the same unit as the cells used to generate it. + * Indicates if the aggregated value has the same unit as the cells used to generate it. * It can be used to apply a custom cell renderer only if the aggregated value has the same unit. * @default true */ @@ -74,6 +81,14 @@ export interface GridAggregationFunction { getCellValue?: (params: GridAggregationGetCellValueParams) => V; } +/** + * Grid aggregation function data source definition interface. + * @demos + * - [Server-side aggregation](/x/react-data-grid/server-side-data/aggregation/) + */ +export interface GridAggregationFunctionDataSource + extends Omit {} + export interface GridAggregationParams { values: (V | undefined)[]; groupId: GridRowId; @@ -115,7 +130,7 @@ export interface GridAggregationHeaderMeta { export interface GridAggregationRule { aggregationFunctionName: string; - aggregationFunction: GridAggregationFunction; + aggregationFunction: GridAggregationFunction | GridAggregationFunctionDataSource; } /** diff --git a/packages/x-data-grid-premium/src/hooks/features/aggregation/gridAggregationUtils.ts b/packages/x-data-grid-premium/src/hooks/features/aggregation/gridAggregationUtils.ts index c84aae5d5e30b..408b7e1950d6d 100644 --- a/packages/x-data-grid-premium/src/hooks/features/aggregation/gridAggregationUtils.ts +++ b/packages/x-data-grid-premium/src/hooks/features/aggregation/gridAggregationUtils.ts @@ -18,6 +18,7 @@ import { } from '@mui/x-data-grid-pro/internals'; import { GridAggregationFunction, + GridAggregationFunctionDataSource, GridAggregationModel, GridAggregationRule, GridAggregationRules, @@ -36,20 +37,29 @@ export const getAggregationFooterRowIdFromGroupId = (groupId: GridRowId | null) return `auto-generated-group-footer-${groupId}`; }; +type AggregationFunction = GridAggregationFunction | GridAggregationFunctionDataSource | undefined; + +const isClientSideAggregateFunction = ( + aggregationFunction: AggregationFunction, +): aggregationFunction is GridAggregationFunction => + !!aggregationFunction && 'apply' in aggregationFunction; + export const canColumnHaveAggregationFunction = ({ colDef, aggregationFunctionName, aggregationFunction, + isDataSource, }: { colDef: GridColDef | undefined; aggregationFunctionName: string; - aggregationFunction: GridAggregationFunction | undefined; + aggregationFunction: AggregationFunction; + isDataSource: boolean; }): boolean => { if (!colDef) { return false; } - if (!aggregationFunction) { + if (!isClientSideAggregateFunction(aggregationFunction) && !isDataSource) { return false; } @@ -57,7 +67,7 @@ export const canColumnHaveAggregationFunction = ({ return colDef.availableAggregationFunctions.includes(aggregationFunctionName); } - if (!aggregationFunction.columnTypes) { + if (!aggregationFunction?.columnTypes) { return true; } @@ -67,15 +77,20 @@ export const canColumnHaveAggregationFunction = ({ export const getAvailableAggregationFunctions = ({ aggregationFunctions, colDef, + isDataSource, }: { - aggregationFunctions: Record; + aggregationFunctions: + | Record + | Record; colDef: GridColDef; + isDataSource: boolean; }) => Object.keys(aggregationFunctions).filter((aggregationFunctionName) => canColumnHaveAggregationFunction({ colDef, aggregationFunctionName, aggregationFunction: aggregationFunctions[aggregationFunctionName], + isDataSource, }), ); @@ -89,7 +104,10 @@ export const mergeStateWithAggregationModel = export const getAggregationRules = ( columnsLookup: GridColumnRawLookup, aggregationModel: GridAggregationModel, - aggregationFunctions: Record, + aggregationFunctions: + | Record + | Record, + isDataSource: boolean, ) => { const aggregationRules: GridAggregationRules = {}; @@ -102,6 +120,7 @@ export const getAggregationRules = ( colDef: columnsLookup[field], aggregationFunctionName: columnItem, aggregationFunction: aggregationFunctions[columnItem], + isDataSource, }) ) { aggregationRules[field] = { diff --git a/packages/x-data-grid-premium/src/hooks/features/aggregation/index.ts b/packages/x-data-grid-premium/src/hooks/features/aggregation/index.ts index 0334e48bacb90..ee9155e14144c 100644 --- a/packages/x-data-grid-premium/src/hooks/features/aggregation/index.ts +++ b/packages/x-data-grid-premium/src/hooks/features/aggregation/index.ts @@ -1,4 +1,20 @@ -export * from './gridAggregationInterfaces'; +export type { + GridAggregationState, + GridAggregationInitialState, + GridAggregationInternalCache, + GridAggregationApi, + GridAggregationGetCellValueParams, + GridAggregationFunction, + GridAggregationFunctionDataSource, + GridAggregationParams, + GridAggregationModel, + GridAggregationLookup, + GridAggregationPosition, + GridAggregationCellMeta, + GridAggregationHeaderMeta, + GridAggregationRule, + GridAggregationRules, +} from './gridAggregationInterfaces'; export * from './gridAggregationSelectors'; export * from './gridAggregationFunctions'; export { diff --git a/packages/x-data-grid-premium/src/hooks/features/aggregation/useGridAggregation.ts b/packages/x-data-grid-premium/src/hooks/features/aggregation/useGridAggregation.ts index 8b41493cc2b96..345f9b718509f 100644 --- a/packages/x-data-grid-premium/src/hooks/features/aggregation/useGridAggregation.ts +++ b/packages/x-data-grid-premium/src/hooks/features/aggregation/useGridAggregation.ts @@ -4,11 +4,15 @@ import { useGridApiEventHandler, useGridApiMethod, } from '@mui/x-data-grid-pro'; -import { GridStateInitializer } from '@mui/x-data-grid-pro/internals'; +import { + useGridRegisterPipeProcessor, + GridStateInitializer, + GridPipeProcessor, +} from '@mui/x-data-grid-pro/internals'; import { DataGridPremiumProcessedProps } from '../../../models/dataGridPremiumProps'; import { GridPrivateApiPremium } from '../../../models/gridApiPremium'; import { gridAggregationModelSelector } from './gridAggregationSelectors'; -import { GridAggregationApi } from './gridAggregationInterfaces'; +import { GridAggregationApi, GridAggregationPrivateApi } from './gridAggregationInterfaces'; import { getAggregationRules, mergeStateWithAggregationModel, @@ -45,6 +49,7 @@ export const useGridAggregation = ( | 'aggregationRowsScope' | 'disableAggregation' | 'rowGroupingColumnMode' + | 'unstable_dataSource' >, ) => { apiRef.current.registerControlState({ @@ -75,6 +80,7 @@ export const useGridAggregation = ( getAggregationPosition: props.getAggregationPosition, aggregationFunctions: props.aggregationFunctions, aggregationRowsScope: props.aggregationRowsScope, + isDataSource: !!props.unstable_dataSource, }); apiRef.current.setState((state) => ({ @@ -86,13 +92,31 @@ export const useGridAggregation = ( props.getAggregationPosition, props.aggregationFunctions, props.aggregationRowsScope, + props.unstable_dataSource, ]); const aggregationApi: GridAggregationApi = { setAggregationModel, }; + const aggregationPrivateApi: GridAggregationPrivateApi = { + applyAggregation, + }; + useGridApiMethod(apiRef, aggregationApi, 'public'); + useGridApiMethod(apiRef, aggregationPrivateApi, 'private'); + + const addGetRowsParams = React.useCallback>( + (params) => { + return { + ...params, + aggregationModel: gridAggregationModelSelector(apiRef), + }; + }, + [apiRef], + ); + + useGridRegisterPipeProcessor(apiRef, 'getRowsParams', addGetRowsParams); /** * EVENTS @@ -107,12 +131,17 @@ export const useGridAggregation = ( gridColumnLookupSelector(apiRef), gridAggregationModelSelector(apiRef), props.aggregationFunctions, + !!props.unstable_dataSource, ); // Re-apply the row hydration to add / remove the aggregation footers if (!areAggregationRulesEqual(rulesOnLastRowHydration, aggregationRules)) { - apiRef.current.requestPipeProcessorsApplication('hydrateRows'); - applyAggregation(); + if (props.unstable_dataSource) { + apiRef.current.unstable_dataSource.fetchRows(); + } else { + apiRef.current.requestPipeProcessorsApplication('hydrateRows'); + applyAggregation(); + } } // Re-apply the column hydration to wrap / unwrap the aggregated columns @@ -120,7 +149,13 @@ export const useGridAggregation = ( apiRef.current.caches.aggregation.rulesOnLastColumnHydration = aggregationRules; apiRef.current.requestPipeProcessorsApplication('hydrateColumns'); } - }, [apiRef, applyAggregation, props.aggregationFunctions, props.disableAggregation]); + }, [ + apiRef, + applyAggregation, + props.aggregationFunctions, + props.disableAggregation, + props.unstable_dataSource, + ]); useGridApiEventHandler(apiRef, 'aggregationModelChange', checkAggregationRulesDiff); useGridApiEventHandler(apiRef, 'columnsChange', checkAggregationRulesDiff); diff --git a/packages/x-data-grid-premium/src/hooks/features/aggregation/useGridAggregationPreProcessors.tsx b/packages/x-data-grid-premium/src/hooks/features/aggregation/useGridAggregationPreProcessors.tsx index 5ae196fb91ce1..b40cfb1ac99b6 100644 --- a/packages/x-data-grid-premium/src/hooks/features/aggregation/useGridAggregationPreProcessors.tsx +++ b/packages/x-data-grid-premium/src/hooks/features/aggregation/useGridAggregationPreProcessors.tsx @@ -25,7 +25,12 @@ export const useGridAggregationPreProcessors = ( apiRef: React.MutableRefObject, props: Pick< DataGridPremiumProcessedProps, - 'aggregationFunctions' | 'disableAggregation' | 'getAggregationPosition' | 'slotProps' | 'slots' + | 'aggregationFunctions' + | 'disableAggregation' + | 'getAggregationPosition' + | 'slotProps' + | 'slots' + | 'unstable_dataSource' >, ) => { // apiRef.current.caches.aggregation.rulesOnLastColumnHydration is not used because by the time @@ -40,6 +45,7 @@ export const useGridAggregationPreProcessors = ( columnsState.lookup, gridAggregationModelSelector(apiRef), props.aggregationFunctions, + !!props.unstable_dataSource, ); columnsState.orderedFields.forEach((field) => { @@ -69,7 +75,7 @@ export const useGridAggregationPreProcessors = ( return columnsState; }, - [apiRef, props.aggregationFunctions, props.disableAggregation], + [apiRef, props.aggregationFunctions, props.disableAggregation, props.unstable_dataSource], ); const addGroupFooterRows = React.useCallback>( @@ -80,6 +86,7 @@ export const useGridAggregationPreProcessors = ( gridColumnLookupSelector(apiRef), gridAggregationModelSelector(apiRef), props.aggregationFunctions, + !!props.unstable_dataSource, ); const hasAggregationRule = Object.keys(aggregationRules).length > 0; @@ -102,7 +109,13 @@ export const useGridAggregationPreProcessors = ( hasAggregationRule, }); }, - [apiRef, props.disableAggregation, props.getAggregationPosition, props.aggregationFunctions], + [ + apiRef, + props.disableAggregation, + props.getAggregationPosition, + props.aggregationFunctions, + props.unstable_dataSource, + ], ); const addColumnMenuButtons = React.useCallback>( @@ -114,6 +127,7 @@ export const useGridAggregationPreProcessors = ( const availableAggregationFunctions = getAvailableAggregationFunctions({ aggregationFunctions: props.aggregationFunctions, colDef, + isDataSource: !!props.unstable_dataSource, }); if (availableAggregationFunctions.length === 0) { @@ -122,7 +136,7 @@ export const useGridAggregationPreProcessors = ( return [...columnMenuItems, 'columnMenuAggregationItem']; }, - [props.aggregationFunctions, props.disableAggregation], + [props.aggregationFunctions, props.disableAggregation, props.unstable_dataSource], ); const stateExportPreProcessing = React.useCallback>( diff --git a/packages/x-data-grid-premium/src/hooks/features/dataSource/cache.ts b/packages/x-data-grid-premium/src/hooks/features/dataSource/cache.ts new file mode 100644 index 0000000000000..db35e1db051d9 --- /dev/null +++ b/packages/x-data-grid-premium/src/hooks/features/dataSource/cache.ts @@ -0,0 +1,13 @@ +import { GridGetRowsParamsPremium } from './models'; + +export function getKeyPremium(params: GridGetRowsParamsPremium) { + return JSON.stringify([ + params.filterModel, + params.sortModel, + params.groupKeys, + params.groupFields, + params.start, + params.end, + params.aggregationModel, + ]); +} diff --git a/packages/x-data-grid-premium/src/hooks/features/dataSource/models.ts b/packages/x-data-grid-premium/src/hooks/features/dataSource/models.ts new file mode 100644 index 0000000000000..ef69f680ad459 --- /dev/null +++ b/packages/x-data-grid-premium/src/hooks/features/dataSource/models.ts @@ -0,0 +1,63 @@ +import type { + GridColDef, + GridRowId, + GridValidRowModel, + GridDataSource, + GridGetRowsResponse, + GridGetRowsParams, + GridDataSourceApiBase, + GridDataSourcePrivateApi, +} from '@mui/x-data-grid-pro'; + +import type { GridAggregationModel } from '../aggregation/gridAggregationInterfaces'; + +export interface GridGetRowsResponsePremium extends GridGetRowsResponse { + /** + * Row to be used for aggregation footer row. + * It must provide the values for the aggregated columns passed in + * `GridGetRowsParams.aggregationModel`. + */ + aggregateRow?: GridValidRowModel; +} + +export interface GridGetRowsParamsPremium extends GridGetRowsParams { + aggregationModel?: GridAggregationModel; +} + +export interface GridDataSourcePremium extends Omit { + /** + * This method will be called when the grid needs to fetch some rows. + * @param {GridGetRowsParamsPremium} params The parameters required to fetch the rows. + * @returns {Promise} A promise that resolves to the data of type [GridGetRowsResponsePremium]. + */ + getRows(params: GridGetRowsParamsPremium): Promise; + /** + * Used to get the aggregated value for a parent row. + * @param {GridValidRowModel} row The row to extract the aggregated value from. + * @param {GridColDef['field']} field The field to extract the aggregated value for. + * @returns {string} The aggregated value for a specific aggregated column. + */ + getAggregatedValue?: (row: GridValidRowModel, field: GridColDef['field']) => string; +} + +export interface GridDataSourceApiBasePremium extends Omit { + /** + * Fetches the rows from the server. + * If no `parentId` option is provided, it fetches the root rows. + * Any missing parameter from `params` will be filled from the state (sorting, filtering, etc.). + * @param {GridRowId} parentId The id of the parent node (default: `GRID_ROOT_GROUP_ID`). + * @param {Partial} params Request parameters override. + */ + fetchRows: (parentId?: GridRowId, params?: Partial) => void; +} + +export interface GridDataSourceApiPremium { + /** + * The data source API. + */ + unstable_dataSource: GridDataSourceApiBasePremium; +} + +export interface GridDataSourcePremiumPrivateApi extends GridDataSourcePrivateApi { + resolveGroupAggregation: (groupId: GridRowId, field: string) => any; +} diff --git a/packages/x-data-grid-premium/src/hooks/features/dataSource/useGridDataSourcePremium.tsx b/packages/x-data-grid-premium/src/hooks/features/dataSource/useGridDataSourcePremium.tsx new file mode 100644 index 0000000000000..09bfb1d025279 --- /dev/null +++ b/packages/x-data-grid-premium/src/hooks/features/dataSource/useGridDataSourcePremium.tsx @@ -0,0 +1,102 @@ +import * as React from 'react'; +import { + useGridApiEventHandler as addEventHandler, + useGridApiMethod, + GridEventLookup, + GRID_ROOT_GROUP_ID, + GridValidRowModel, +} from '@mui/x-data-grid-pro'; +import { + useGridDataSourceBase, + useGridRegisterStrategyProcessor, + GridPipeProcessor, + useGridRegisterPipeProcessor, +} from '@mui/x-data-grid-pro/internals'; +import { GridPrivateApiPremium } from '../../../models/gridApiPremium'; +import { DataGridPremiumProcessedProps } from '../../../models/dataGridPremiumProps'; +import { + GridDataSourcePremiumPrivateApi, + GridGetRowsParamsPremium, + GridGetRowsResponsePremium, +} from './models'; +import { getKeyPremium } from './cache'; + +const options = { + cacheOptions: { + getKey: getKeyPremium, + }, +}; + +export const useGridDataSourcePremium = ( + apiRef: React.MutableRefObject, + props: DataGridPremiumProcessedProps, +) => { + const { api, strategyProcessor, events } = useGridDataSourceBase( + apiRef, + props, + options, + ); + const aggregateRowRef = React.useRef({}); + + const processDataSourceRows = React.useCallback>( + ( + { + params, + response, + }: { + params: GridGetRowsParamsPremium; + response: GridGetRowsResponsePremium; + }, + applyRowHydration: boolean, + ) => { + if (response.aggregateRow) { + aggregateRowRef.current = response.aggregateRow; + } + if (Object.keys(params.aggregationModel || {}).length > 0) { + if (applyRowHydration) { + apiRef.current.requestPipeProcessorsApplication('hydrateRows'); + } + apiRef.current.applyAggregation(); + } + + return { + params, + response, + }; + }, + [apiRef], + ); + + const resolveGroupAggregation = React.useCallback< + GridDataSourcePremiumPrivateApi['resolveGroupAggregation'] + >( + (groupId, field) => { + if (groupId === GRID_ROOT_GROUP_ID) { + return props.unstable_dataSource?.getAggregatedValue?.(aggregateRowRef.current, field); + } + const row = apiRef.current.getRow(groupId); + return props.unstable_dataSource?.getAggregatedValue?.(row, field); + }, + [apiRef, props.unstable_dataSource], + ); + + const privateApi: GridDataSourcePremiumPrivateApi = { + ...api.private, + resolveGroupAggregation, + }; + + useGridApiMethod(apiRef, api.public, 'public'); + useGridApiMethod(apiRef, privateApi, 'private'); + + useGridRegisterStrategyProcessor( + apiRef, + strategyProcessor.strategyName, + strategyProcessor.group, + strategyProcessor.processor, + ); + useGridRegisterPipeProcessor(apiRef, 'processDataSourceRows', processDataSourceRows); + + Object.entries(events).forEach(([event, handler]) => { + addEventHandler(apiRef, event as keyof GridEventLookup, handler); + }); +}; diff --git a/packages/x-data-grid-premium/src/index.ts b/packages/x-data-grid-premium/src/index.ts index 0def4c628a755..36842cea3ac1a 100644 --- a/packages/x-data-grid-premium/src/index.ts +++ b/packages/x-data-grid-premium/src/index.ts @@ -40,3 +40,14 @@ export { GRID_COLUMN_MENU_SLOTS, GRID_COLUMN_MENU_SLOT_PROPS, } from './components/reexports'; + +export type { GridDataSourceCache } from '@mui/x-data-grid-pro'; + +export type { + GridGetRowsParamsPremium as GridGetRowsParams, + GridGetRowsResponsePremium as GridGetRowsResponse, + GridDataSourcePremium as GridDataSource, + GridDataSourceApiPremium as GridDataSourceApi, + GridDataSourceApiBasePremium as GridDataSourceApiBase, + GridDataSourcePremiumPrivateApi as GridDataSourcePrivateApi, +} from './hooks/features/dataSource/models'; diff --git a/packages/x-data-grid-premium/src/models/dataGridPremiumProps.ts b/packages/x-data-grid-premium/src/models/dataGridPremiumProps.ts index 8ed5498364dab..da879f08ed001 100644 --- a/packages/x-data-grid-premium/src/models/dataGridPremiumProps.ts +++ b/packages/x-data-grid-premium/src/models/dataGridPremiumProps.ts @@ -17,12 +17,14 @@ import type { GridRowGroupingModel } from '../hooks/features/rowGrouping'; import type { GridAggregationModel, GridAggregationFunction, + GridAggregationFunctionDataSource, GridAggregationPosition, } from '../hooks/features/aggregation'; import { GridPremiumSlotsComponent } from './gridPremiumSlotsComponent'; import { GridInitialStatePremium } from './gridStatePremium'; import { GridApiPremium } from './gridApiPremium'; import { GridCellSelectionModel } from '../hooks/features/cellSelection'; +import { GridDataSourcePremium as GridDataSource } from '../hooks/features/dataSource/models'; export interface GridExperimentalPremiumFeatures extends GridExperimentalProFeatures {} @@ -86,9 +88,11 @@ export interface DataGridPremiumPropsWithDefaultValue; + aggregationFunctions: + | Record + | Record; /** * Rows used to generate the aggregated value. * If `filtered`, the aggregated values are generated using only the rows currently passing the filtering process. @@ -118,7 +122,10 @@ export interface DataGridPremiumPropsWithDefaultValue - extends Omit, 'initialState' | 'apiRef'> { + extends Omit< + DataGridProPropsWithoutDefaultValue, + 'initialState' | 'apiRef' | 'unstable_dataSource' + > { /** * The ref object that allows grid manipulation. Can be instantiated with `useGridApiRef()`. */ @@ -188,4 +195,5 @@ export interface DataGridPremiumPropsWithoutDefaultValue; + unstable_dataSource?: GridDataSource; } diff --git a/packages/x-data-grid-premium/src/models/gridApiPremium.ts b/packages/x-data-grid-premium/src/models/gridApiPremium.ts index 98ec11cdd1dce..1ea6dd05158ce 100644 --- a/packages/x-data-grid-premium/src/models/gridApiPremium.ts +++ b/packages/x-data-grid-premium/src/models/gridApiPremium.ts @@ -8,13 +8,16 @@ import { GridRowMultiSelectionApi, GridColumnReorderApi, GridRowProApi, - GridDataSourceApi, - GridDataSourcePrivateApi, } from '@mui/x-data-grid-pro'; import { GridInitialStatePremium, GridStatePremium } from './gridStatePremium'; import type { GridRowGroupingApi, GridExcelExportApi, GridAggregationApi } from '../hooks'; import { GridCellSelectionApi } from '../hooks/features/cellSelection/gridCellSelectionInterfaces'; import type { DataGridPremiumProcessedProps } from './dataGridPremiumProps'; +import type { + GridDataSourcePremiumPrivateApi, + GridDataSourceApiPremium, +} from '../hooks/features/dataSource/models'; +import type { GridAggregationPrivateApi } from '../hooks/features/aggregation/gridAggregationInterfaces'; /** * The api of Data Grid Premium. @@ -29,7 +32,7 @@ export interface GridApiPremium GridExcelExportApi, GridAggregationApi, GridRowPinningApi, - GridDataSourceApi, + GridDataSourceApiPremium, GridCellSelectionApi, // APIs that are private in Community plan, but public in Pro and Premium plans GridRowMultiSelectionApi, @@ -38,5 +41,6 @@ export interface GridApiPremium export interface GridPrivateApiPremium extends GridApiPremium, GridPrivateOnlyApiCommon, - GridDataSourcePrivateApi, + GridDataSourcePremiumPrivateApi, + GridAggregationPrivateApi, GridDetailPanelPrivateApi {} diff --git a/packages/x-data-grid-premium/src/tests/dataSourceAggregation.DataGridPremium.test.tsx b/packages/x-data-grid-premium/src/tests/dataSourceAggregation.DataGridPremium.test.tsx new file mode 100644 index 0000000000000..f5cd1c40ee08d --- /dev/null +++ b/packages/x-data-grid-premium/src/tests/dataSourceAggregation.DataGridPremium.test.tsx @@ -0,0 +1,157 @@ +import * as React from 'react'; +import { useMockServer } from '@mui/x-data-grid-generator'; +import { createRenderer, waitFor, screen, within } from '@mui/internal-test-utils'; +import { expect } from 'chai'; +import { + DataGridPremium, + DataGridPremiumProps, + GridApi, + GridDataSource, + GridGetRowsParams, + useGridApiRef, + GRID_AGGREGATION_ROOT_FOOTER_ROW_ID, + GRID_ROOT_GROUP_ID, +} from '@mui/x-data-grid-premium'; +import { SinonSpy, spy } from 'sinon'; +import { getColumnHeaderCell } from 'test/utils/helperFn'; + +const isJSDOM = /jsdom/.test(window.navigator.userAgent); + +describe(' - Data source aggregation', () => { + const { render } = createRenderer(); + + let apiRef: React.MutableRefObject; + let getRowsSpy: SinonSpy; + let mockServer: ReturnType; + + function TestDataSourceAggregation( + props: Partial & { + getAggregatedValue?: GridDataSource['getAggregatedValue']; + }, + ) { + apiRef = useGridApiRef(); + const { getAggregatedValue: getAggregatedValueProp, ...rest } = props; + mockServer = useMockServer( + { rowLength: 10, maxColumns: 1 }, + { useCursorPagination: false, minDelay: 0, maxDelay: 0, verbose: false }, + ); + + const { fetchRows } = mockServer; + + const dataSource: GridDataSource = React.useMemo( + () => ({ + getRows: async (params: GridGetRowsParams) => { + const urlParams = new URLSearchParams({ + filterModel: JSON.stringify(params.filterModel), + sortModel: JSON.stringify(params.sortModel), + paginationModel: JSON.stringify(params.paginationModel), + aggregationModel: JSON.stringify(params.aggregationModel), + }); + + const getRowsResponse = await fetchRows( + `https://mui.com/x/api/data-grid?${urlParams.toString()}`, + ); + + return { + rows: getRowsResponse.rows, + rowCount: getRowsResponse.rowCount, + aggregateRow: getRowsResponse.aggregateRow, + }; + }, + getAggregatedValue: + getAggregatedValueProp ?? + ((row, field) => { + return row[`${field}Aggregate`]; + }), + }), + [fetchRows, getAggregatedValueProp], + ); + + getRowsSpy?.restore(); + getRowsSpy = spy(dataSource, 'getRows'); + + const baselineProps = { + unstable_dataSource: dataSource, + columns: mockServer.columns, + disableVirtualization: true, + aggregationFunctions: { + sum: { columnTypes: ['number'] }, + avg: { columnTypes: ['number'] }, + min: { columnTypes: ['number', 'date', 'dateTime'] }, + max: { columnTypes: ['number', 'date', 'dateTime'] }, + size: {}, + }, + }; + + return ( +
+ +
+ ); + } + + beforeEach(function beforeTest() { + if (isJSDOM) { + this.skip(); // Needs layout + } + }); + + it('should show aggregation option in the column menu', async () => { + const { user } = render(); + await user.click(within(getColumnHeaderCell(0)).getByLabelText('Menu')); + expect(screen.queryByLabelText('Aggregation')).not.to.equal(null); + }); + + it('should not show aggregation option in the column menu when no aggregation function is defined', async () => { + const { user } = render(); + await user.click(within(getColumnHeaderCell(0)).getByLabelText('Menu')); + expect(screen.queryByLabelText('Aggregation')).to.equal(null); + }); + + it('should provide the `aggregationModel` in the `getRows` params', async () => { + render( + , + ); + await waitFor(() => { + expect(getRowsSpy.callCount).to.be.greaterThan(0); + }); + expect(getRowsSpy.args[0][0].aggregationModel).to.deep.equal({ id: 'size' }); + }); + + it('should show the aggregation footer row when aggregation is enabled', async () => { + render( + , + ); + await waitFor(() => { + expect(Object.keys(apiRef.current.state.aggregation.lookup).length).to.be.greaterThan(0); + }); + expect(apiRef.current.state.rows.tree[GRID_AGGREGATION_ROOT_FOOTER_ROW_ID]).not.to.equal(null); + const footerRow = apiRef.current.state.aggregation.lookup[GRID_ROOT_GROUP_ID]; + expect(footerRow.id).to.deep.equal({ position: 'footer', value: 10 }); + }); + + it('should derive the aggregation values using `dataSource.getAggregatedValue`', async () => { + render( + 'Agg value'} + />, + ); + await waitFor(() => { + expect(Object.keys(apiRef.current.state.aggregation.lookup).length).to.be.greaterThan(0); + }); + expect(apiRef.current.state.aggregation.lookup[GRID_ROOT_GROUP_ID].id.value).to.equal( + 'Agg value', + ); + }); +}); diff --git a/packages/x-data-grid-pro/src/DataGridPro/useDataGridProComponent.tsx b/packages/x-data-grid-pro/src/DataGridPro/useDataGridProComponent.tsx index c423c5f58d566..58a36ea9d2b21 100644 --- a/packages/x-data-grid-pro/src/DataGridPro/useDataGridProComponent.tsx +++ b/packages/x-data-grid-pro/src/DataGridPro/useDataGridProComponent.tsx @@ -82,10 +82,8 @@ import { rowPinningStateInitializer, } from '../hooks/features/rowPinning/useGridRowPinning'; import { useGridRowPinningPreProcessors } from '../hooks/features/rowPinning/useGridRowPinningPreProcessors'; -import { - useGridDataSource, - dataSourceStateInitializer, -} from '../hooks/features/dataSource/useGridDataSource'; +import { useGridDataSourcePro as useGridDataSource } from '../hooks/features/dataSource/useGridDataSourcePro'; +import { dataSourceStateInitializer } from '../hooks/features/dataSource/useGridDataSourceBase'; import { useGridDataSourceLazyLoader } from '../hooks/features/serverSideLazyLoader/useGridDataSourceLazyLoader'; export const useDataGridProComponent = ( diff --git a/packages/x-data-grid-pro/src/hooks/features/dataSource/cache.ts b/packages/x-data-grid-pro/src/hooks/features/dataSource/cache.ts index e6e0c02e48d35..4e9757c2a4456 100644 --- a/packages/x-data-grid-pro/src/hooks/features/dataSource/cache.ts +++ b/packages/x-data-grid-pro/src/hooks/features/dataSource/cache.ts @@ -1,4 +1,4 @@ -import { GridGetRowsParams, GridGetRowsResponse } from '../../../models'; +import { GridGetRowsParams, GridGetRowsResponse } from '@mui/x-data-grid/internals'; export type GridDataSourceCacheDefaultConfig = { /** @@ -7,9 +7,16 @@ export type GridDataSourceCacheDefaultConfig = { * @default 300000 (5 minutes) */ ttl?: number; + /** + * Function to generate a cache key from the params. + * @param {GridGetRowsParams} params The params to generate the cache key from. + * @returns {string} The cache key. + * @default `getKeyDefault()` + */ + getKey?: (params: GridGetRowsParams) => string; }; -export function getKey(params: GridGetRowsParams) { +export function getKeyDefault(params: GridGetRowsParams) { return JSON.stringify([ params.filterModel, params.sortModel, @@ -25,19 +32,22 @@ export class GridDataSourceCacheDefault { private ttl: number; - constructor({ ttl = 300000 }: GridDataSourceCacheDefaultConfig) { + private getKey: (params: GridGetRowsParams) => string; + + constructor({ ttl = 300000, getKey = getKeyDefault }: GridDataSourceCacheDefaultConfig) { this.cache = {}; this.ttl = ttl; + this.getKey = getKey; } set(key: GridGetRowsParams, value: GridGetRowsResponse) { - const keyString = getKey(key); + const keyString = this.getKey(key); const expiry = Date.now() + this.ttl; this.cache[keyString] = { value, expiry }; } get(key: GridGetRowsParams): GridGetRowsResponse | undefined { - const keyString = getKey(key); + const keyString = this.getKey(key); const entry = this.cache[keyString]; if (!entry) { return undefined; diff --git a/packages/x-data-grid-pro/src/hooks/features/dataSource/interfaces.ts b/packages/x-data-grid-pro/src/hooks/features/dataSource/interfaces.ts index 08eaf7c33f42d..c83b5f8219552 100644 --- a/packages/x-data-grid-pro/src/hooks/features/dataSource/interfaces.ts +++ b/packages/x-data-grid-pro/src/hooks/features/dataSource/interfaces.ts @@ -1,5 +1,5 @@ import { GridRowId } from '@mui/x-data-grid'; -import { GridDataSourceCache, GridGetRowsParams } from '../../../models'; +import { GridDataSourceCache, GridGetRowsParams } from '@mui/x-data-grid/internals'; export interface GridDataSourceState { loading: Record; @@ -26,7 +26,7 @@ export interface GridDataSourceApiBase { * Fetches the rows from the server. * If no `parentId` option is provided, it fetches the root rows. * Any missing parameter from `params` will be filled from the state (sorting, filtering, etc.). - * @param {GridRowId} parentId The id of the parent node. + * @param {GridRowId} parentId The id of the parent node (default: `GRID_ROOT_GROUP_ID`). * @param {Partial} params Request parameters override. */ fetchRows: (parentId?: GridRowId, params?: Partial) => void; diff --git a/packages/x-data-grid-pro/src/hooks/features/dataSource/useGridDataSource.ts b/packages/x-data-grid-pro/src/hooks/features/dataSource/useGridDataSourceBase.ts similarity index 90% rename from packages/x-data-grid-pro/src/hooks/features/dataSource/useGridDataSource.ts rename to packages/x-data-grid-pro/src/hooks/features/dataSource/useGridDataSourceBase.ts index d13d64e1895d7..d98f319e7b2b1 100644 --- a/packages/x-data-grid-pro/src/hooks/features/dataSource/useGridDataSource.ts +++ b/packages/x-data-grid-pro/src/hooks/features/dataSource/useGridDataSourceBase.ts @@ -1,8 +1,6 @@ import * as React from 'react'; import useLazyRef from '@mui/utils/useLazyRef'; import { - useGridApiEventHandler, - useGridApiMethod, GridDataSourceGroupNode, useGridSelector, gridPaginationModelSelector, @@ -15,7 +13,7 @@ import { GridStateInitializer, GridStrategyGroup, GridStrategyProcessor, - useGridRegisterStrategyProcessor, + GridDataSourceCache, runIf, } from '@mui/x-data-grid/internals'; import { GridPrivateApiPro } from '../../../models/gridApiPro'; @@ -28,7 +26,6 @@ import { NestedDataManager, RequestStatus, } from './utils'; -import { GridDataSourceCache } from '../../../models'; import { GridDataSourceCacheDefault, GridDataSourceCacheDefaultConfig } from './cache'; const INITIAL_STATE = { @@ -59,8 +56,8 @@ export const dataSourceStateInitializer: GridStateInitializer = (state) => { }; }; -export const useGridDataSource = ( - apiRef: React.MutableRefObject, +export const useGridDataSourceBase = ( + apiRef: React.MutableRefObject, props: Pick< DataGridProProcessedProps, | 'unstable_dataSource' @@ -73,6 +70,9 @@ export const useGridDataSource = ( | 'treeData' | 'unstable_lazyLoading' >, + options: { + cacheOptions?: GridDataSourceCacheDefaultConfig; + } = {}, ) => { const setStrategyAvailability = React.useCallback(() => { apiRef.current.setStrategyAvailability( @@ -103,7 +103,7 @@ export const useGridDataSource = ( return new CacheChunkManager(cacheChunkSize); }).current; const [cache, setCache] = React.useState(() => - getCache(props.unstable_dataSourceCache), + getCache(props.unstable_dataSourceCache, options.cacheOptions), ); const fetchRows = React.useCallback( @@ -344,6 +344,11 @@ export const useGridDataSource = ( apiRef.current.setRowCount(response.rowCount); } apiRef.current.setRows(response.rows); + apiRef.current.unstable_applyPipeProcessors( + 'processDataSourceRows', + { params: params.fetchParams, response }, + true, + ); }, [apiRef], ); @@ -371,42 +376,18 @@ export const useGridDataSource = ( resetDataSourceState, }; - useGridApiMethod(apiRef, dataSourceApi, 'public'); - useGridApiMethod(apiRef, dataSourcePrivateApi, 'private'); - - useGridRegisterStrategyProcessor( - apiRef, - DataSourceRowsUpdateStrategy.Default, - 'dataSourceRowsUpdate', - handleDataUpdate, - ); - - useGridApiEventHandler(apiRef, 'strategyAvailabilityChange', handleStrategyActivityChange); - useGridApiEventHandler( - apiRef, - 'sortModelChange', - runIf(defaultRowsUpdateStrategyActive, () => fetchRows()), - ); - useGridApiEventHandler( - apiRef, - 'filterModelChange', - runIf(defaultRowsUpdateStrategyActive, () => fetchRows()), - ); - useGridApiEventHandler( - apiRef, - 'paginationModelChange', - runIf(defaultRowsUpdateStrategyActive, () => fetchRows()), - ); - const isFirstRender = React.useRef(true); React.useEffect(() => { if (isFirstRender.current) { isFirstRender.current = false; return; } - const newCache = getCache(props.unstable_dataSourceCache); + if (props.unstable_dataSourceCache === undefined) { + return; + } + const newCache = getCache(props.unstable_dataSourceCache, options.cacheOptions); setCache((prevCache) => (prevCache !== newCache ? newCache : prevCache)); - }, [props.unstable_dataSourceCache]); + }, [props.unstable_dataSourceCache, options.cacheOptions]); React.useEffect(() => { setStrategyAvailability(); @@ -430,4 +411,19 @@ export const useGridDataSource = ( scheduledGroups.current = groupsToAutoFetch.length; } }, [apiRef, nestedDataManager, groupsToAutoFetch]); + + return { + api: { public: dataSourceApi, private: dataSourcePrivateApi }, + strategyProcessor: { + strategyName: DataSourceRowsUpdateStrategy.Default, + group: 'dataSourceRowsUpdate' as const, + processor: handleDataUpdate, + }, + events: { + strategyAvailabilityChange: handleStrategyActivityChange, + sortModelChange: runIf(defaultRowsUpdateStrategyActive, () => fetchRows()), + filterModelChange: runIf(defaultRowsUpdateStrategyActive, () => fetchRows()), + paginationModelChange: runIf(defaultRowsUpdateStrategyActive, () => fetchRows()), + }, + }; }; diff --git a/packages/x-data-grid-pro/src/hooks/features/dataSource/useGridDataSourcePro.ts b/packages/x-data-grid-pro/src/hooks/features/dataSource/useGridDataSourcePro.ts new file mode 100644 index 0000000000000..5b3ee2b7c4c3d --- /dev/null +++ b/packages/x-data-grid-pro/src/hooks/features/dataSource/useGridDataSourcePro.ts @@ -0,0 +1,31 @@ +import * as React from 'react'; +import { + useGridApiEventHandler as addEventHandler, + useGridApiMethod, + GridEventLookup, +} from '@mui/x-data-grid'; +import { useGridRegisterStrategyProcessor } from '@mui/x-data-grid/internals'; +import { GridPrivateApiPro } from '../../../models/gridApiPro'; +import { DataGridProProcessedProps } from '../../../models/dataGridProProps'; +import { useGridDataSourceBase } from './useGridDataSourceBase'; + +export const useGridDataSourcePro = ( + apiRef: React.MutableRefObject, + props: DataGridProProcessedProps, +) => { + const { api, strategyProcessor, events } = useGridDataSourceBase(apiRef, props); + + useGridApiMethod(apiRef, api.public, 'public'); + useGridApiMethod(apiRef, api.private, 'private'); + + useGridRegisterStrategyProcessor( + apiRef, + strategyProcessor.strategyName, + strategyProcessor.group, + strategyProcessor.processor, + ); + + Object.entries(events).forEach(([event, handler]) => { + addEventHandler(apiRef, event as keyof GridEventLookup, handler); + }); +}; diff --git a/packages/x-data-grid-pro/src/hooks/features/dataSource/utils.ts b/packages/x-data-grid-pro/src/hooks/features/dataSource/utils.ts index ce2d8de133391..d542538f584da 100644 --- a/packages/x-data-grid-pro/src/hooks/features/dataSource/utils.ts +++ b/packages/x-data-grid-pro/src/hooks/features/dataSource/utils.ts @@ -1,5 +1,6 @@ import { GridRowId } from '@mui/x-data-grid'; -import { GridPrivateApiPro, GridGetRowsParams, GridGetRowsResponse } from '../../../models'; +import { GridGetRowsParams, GridGetRowsResponse } from '@mui/x-data-grid/internals'; +import { GridPrivateApiPro } from '../../../models'; const MAX_CONCURRENT_REQUESTS = Infinity; diff --git a/packages/x-data-grid-pro/src/hooks/features/index.ts b/packages/x-data-grid-pro/src/hooks/features/index.ts index 456da75b157c1..a09be50773318 100644 --- a/packages/x-data-grid-pro/src/hooks/features/index.ts +++ b/packages/x-data-grid-pro/src/hooks/features/index.ts @@ -5,5 +5,5 @@ export * from './rowReorder'; export * from './treeData'; export * from './detailPanel'; export * from './rowPinning'; -export * from './dataSource/interfaces'; +export type { GridDataSourceState } from './dataSource/interfaces'; export { GridDataSourceCacheDefault } from './dataSource/cache'; diff --git a/packages/x-data-grid-pro/src/hooks/features/serverSideLazyLoader/useGridDataSourceLazyLoader.ts b/packages/x-data-grid-pro/src/hooks/features/serverSideLazyLoader/useGridDataSourceLazyLoader.ts index c36a874f5ef82..d6f0b1d800f57 100644 --- a/packages/x-data-grid-pro/src/hooks/features/serverSideLazyLoader/useGridDataSourceLazyLoader.ts +++ b/packages/x-data-grid-pro/src/hooks/features/serverSideLazyLoader/useGridDataSourceLazyLoader.ts @@ -285,6 +285,11 @@ export const useGridDataSourceLazyLoader = ( addSkeletonRows(); privateApiRef.current.setLoading(false); + privateApiRef.current.unstable_applyPipeProcessors( + 'processDataSourceRows', + { params: params.fetchParams, response }, + false, + ); privateApiRef.current.requestPipeProcessorsApplication('hydrateRows'); }, [ diff --git a/packages/x-data-grid-pro/src/index.ts b/packages/x-data-grid-pro/src/index.ts index 0b9505fd84d70..e5910805e2ba0 100644 --- a/packages/x-data-grid-pro/src/index.ts +++ b/packages/x-data-grid-pro/src/index.ts @@ -35,3 +35,17 @@ export { export { GridColumnHeaders } from './components/GridColumnHeaders'; export type { GridColumnHeadersProps } from './components/GridColumnHeaders'; + +// Reexportable exports +export type { + GridGetRowsParams, + GridGetRowsResponse, + GridDataSource, + GridDataSourceCache, +} from '@mui/x-data-grid/internals'; + +export type { + GridDataSourceApiBase, + GridDataSourceApi, + GridDataSourcePrivateApi, +} from './hooks/features/dataSource/interfaces'; diff --git a/packages/x-data-grid-pro/src/internals/index.ts b/packages/x-data-grid-pro/src/internals/index.ts index f821f34170111..6db29e8ae55d2 100644 --- a/packages/x-data-grid-pro/src/internals/index.ts +++ b/packages/x-data-grid-pro/src/internals/index.ts @@ -45,9 +45,9 @@ export { useGridLazyLoader } from '../hooks/features/lazyLoader/useGridLazyLoade export { useGridLazyLoaderPreProcessors } from '../hooks/features/lazyLoader/useGridLazyLoaderPreProcessors'; export { useGridDataSourceLazyLoader } from '../hooks/features/serverSideLazyLoader/useGridDataSourceLazyLoader'; export { - useGridDataSource, + useGridDataSourceBase, dataSourceStateInitializer, -} from '../hooks/features/dataSource/useGridDataSource'; +} from '../hooks/features/dataSource/useGridDataSourceBase'; export type { GridExperimentalProFeatures, diff --git a/packages/x-data-grid-pro/src/models/gridApiPro.ts b/packages/x-data-grid-pro/src/models/gridApiPro.ts index 07f0d250f57a5..f0e0159eaf799 100644 --- a/packages/x-data-grid-pro/src/models/gridApiPro.ts +++ b/packages/x-data-grid-pro/src/models/gridApiPro.ts @@ -11,9 +11,11 @@ import type { GridDetailPanelApi, GridRowPinningApi, GridDetailPanelPrivateApi, +} from '../hooks'; +import type { GridDataSourceApi, GridDataSourcePrivateApi, -} from '../hooks'; +} from '../hooks/features/dataSource/interfaces'; import type { DataGridProProcessedProps } from './dataGridProProps'; /** diff --git a/packages/x-data-grid-pro/src/models/index.ts b/packages/x-data-grid-pro/src/models/index.ts index 8110b6c70a918..36deff5c944b6 100644 --- a/packages/x-data-grid-pro/src/models/index.ts +++ b/packages/x-data-grid-pro/src/models/index.ts @@ -1,9 +1,3 @@ -export type { - GridGetRowsParams, - GridGetRowsResponse, - GridDataSource, - GridDataSourceCache, -} from '@mui/x-data-grid/internals'; export * from './gridApiPro'; export * from './gridGroupingColDefOverride'; export * from './gridRowScrollEndParams'; diff --git a/packages/x-data-grid-pro/src/tests/dataSource.DataGridPro.test.tsx b/packages/x-data-grid-pro/src/tests/dataSource.DataGridPro.test.tsx index fbbc993501f36..ec14b20f1daa2 100644 --- a/packages/x-data-grid-pro/src/tests/dataSource.DataGridPro.test.tsx +++ b/packages/x-data-grid-pro/src/tests/dataSource.DataGridPro.test.tsx @@ -14,13 +14,13 @@ import { } from '@mui/x-data-grid-pro'; import { SinonSpy, spy } from 'sinon'; import { describeSkipIf, isJSDOM } from 'test/utils/skipIf'; -import { getKey } from '../hooks/features/dataSource/cache'; +import { getKeyDefault } from '../hooks/features/dataSource/cache'; const cache = new Map(); const testCache: GridDataSourceCache = { - set: (key, value) => cache.set(getKey(key), value), - get: (key) => cache.get(getKey(key)), + set: (key, value) => cache.set(getKeyDefault(key), value), + get: (key) => cache.get(getKeyDefault(key)), clear: () => cache.clear(), }; diff --git a/packages/x-data-grid/src/hooks/core/pipeProcessing/gridPipeProcessingApi.ts b/packages/x-data-grid/src/hooks/core/pipeProcessing/gridPipeProcessingApi.ts index 78300aa77e2a1..7b9eb49a37b0b 100644 --- a/packages/x-data-grid/src/hooks/core/pipeProcessing/gridPipeProcessingApi.ts +++ b/packages/x-data-grid/src/hooks/core/pipeProcessing/gridPipeProcessingApi.ts @@ -20,7 +20,7 @@ import { import { GridRowEntry, GridRowId } from '../../../models/gridRows'; import { GridHydrateRowsValue } from '../../features/rows/gridRowsInterfaces'; import { GridPreferencePanelsValue } from '../../features/preferencesPanel'; -import { GridGetRowsParams } from '../../../models/gridDataSource'; +import { GridGetRowsParams, GridGetRowsResponse } from '../../../models/gridDataSource'; import { HeightEntry } from '../../features/rows/gridRowsMetaInterfaces'; export type GridPipeProcessorGroup = keyof GridPipeProcessingLookup; @@ -68,6 +68,11 @@ export interface GridPipeProcessingLookup { context: { event: React.KeyboardEvent; cellParams: GridCellParams; editMode: GridEditMode }; }; isColumnPinned: { value: GridPinnedColumnPosition | false; context: string }; + processDataSourceRows: { + value: { params: GridGetRowsParams; response: GridGetRowsResponse }; + // `true` if the row hydration should be re-applied + context: boolean; + }; } export type GridPipeProcessor

= ( diff --git a/packages/x-data-grid/src/internals/index.ts b/packages/x-data-grid/src/internals/index.ts index 60848709e1927..6b7457bd3c945 100644 --- a/packages/x-data-grid/src/internals/index.ts +++ b/packages/x-data-grid/src/internals/index.ts @@ -21,7 +21,10 @@ export { useGridRegisterStrategyProcessor, GRID_DEFAULT_STRATEGY, } from '../hooks/core/strategyProcessing'; -export type { GridStrategyProcessor } from '../hooks/core/strategyProcessing'; +export type { + GridStrategyProcessor, + GridStrategyProcessorName, +} from '../hooks/core/strategyProcessing'; export { useGridInitialization } from '../hooks/core/useGridInitialization'; export { unwrapPrivateAPI } from '../hooks/core/useGridApiInitialization'; diff --git a/scripts/x-data-grid-premium.exports.json b/scripts/x-data-grid-premium.exports.json index b19e1c6650c81..e9d6c4df85d34 100644 --- a/scripts/x-data-grid-premium.exports.json +++ b/scripts/x-data-grid-premium.exports.json @@ -94,6 +94,7 @@ { "name": "GridAggregationApi", "kind": "Interface" }, { "name": "GridAggregationCellMeta", "kind": "Interface" }, { "name": "GridAggregationFunction", "kind": "Interface" }, + { "name": "GridAggregationFunctionDataSource", "kind": "Interface" }, { "name": "GridAggregationGetCellValueParams", "kind": "Interface" }, { "name": "GridAggregationHeaderMeta", "kind": "Interface" }, { "name": "GridAggregationInitialState", "kind": "Interface" }, From 6412b8c0a29f205a76a233ec4f07d89d179a4d8f Mon Sep 17 00:00:00 2001 From: Armin Mehinovic <4390250+arminmeh@users.noreply.github.com> Date: Thu, 9 Jan 2025 15:49:00 +0100 Subject: [PATCH 3/3] v8.0.0-alpha.7 (#16110) Signed-off-by: Armin Mehinovic <4390250+arminmeh@users.noreply.github.com> Co-authored-by: Bilal Shafi Co-authored-by: Lukas Tyla Co-authored-by: Alexandre Fauquette <45398769+alexfauquette@users.noreply.github.com> --- CHANGELOG.md | 126 ++++++++++++++++++++ package.json | 2 +- packages/x-charts-pro/package.json | 2 +- packages/x-charts-vendor/package.json | 2 +- packages/x-charts/package.json | 2 +- packages/x-codemod/package.json | 2 +- packages/x-data-grid-generator/package.json | 2 +- packages/x-data-grid-premium/package.json | 2 +- packages/x-data-grid-pro/package.json | 2 +- packages/x-data-grid/package.json | 2 +- packages/x-date-pickers-pro/package.json | 2 +- packages/x-date-pickers/package.json | 2 +- packages/x-internals/package.json | 2 +- packages/x-license/package.json | 2 +- packages/x-tree-view-pro/package.json | 2 +- packages/x-tree-view/package.json | 2 +- 16 files changed, 141 insertions(+), 15 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index d8e6af73aad21..ab86f3fc1ea91 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -5,6 +5,132 @@ All notable changes to this project will be documented in this file. See [Conventional Commits](https://conventionalcommits.org) for commit guidelines. +## 8.0.0-alpha.7 + +_Jan 9, 2025_ + +We'd like to offer a big thanks to the 13 contributors who made this release possible. Here are some highlights ✨: + +- 📊 Charts legend is now an HTML element which can be styled more easily +- 💫 Support [aggregation with server-side data](/x/react-data-grid/server-side-data/aggregation/) +- 🏎️ Improve Data Grid aggregation performance +- 🌍 Add Chinese (Taiwan) (zh-TW) locale on the Date and Time Pickers +- 🌍 Improve Norwegian (nb-NO) locale on the Date and Time Pickers +- 🐞 Bugfixes + +Special thanks go out to the community contributors who have helped make this release possible: +@derek-0000, @josteinjhauge, @k-rajat19, @nusr, @tomashauser. +Following are all team members who have contributed to this release: +@cherniavskii, @flaviendelangle, @JCQuintas, @LukasTy, @MBilalShafi, @arminmeh, @romgrk, @oliviertassinari. + + + +### Data Grid + +#### `@mui/x-data-grid@8.0.0-alpha.7` + +- [DataGrid] Improve React 19 support (#15769) @LukasTy +- [DataGrid] Add `name` attribute to the checkbox selection column (#15178) @derek-0000 +- [DataGrid] Fix number filter field formatting values while typing (#16062) @arminmeh +- [DataGrid] Fix select all checkbox state reset with server side data (#16034) @MBilalShafi +- [DataGrid] Refactor: create base button props (#15930) @romgrk +- [DataGrid] Refactor: create tooltip props (#16086) @romgrk +- [DataGrid] Fix TS error (#16046) @cherniavskii + +#### `@mui/x-data-grid-pro@8.0.0-alpha.7` [![pro](https://mui.com/r/x-pro-svg)](https://mui.com/r/x-pro-svg-link 'Pro plan') + +Same changes as in `@mui/x-data-grid@8.0.0-alpha.7`. + +#### `@mui/x-data-grid-premium@8.0.0-alpha.7` [![premium](https://mui.com/r/x-premium-svg)](https://mui.com/r/x-premium-svg-link 'Premium plan') + +Same changes as in `@mui/x-data-grid-pro@8.0.0-alpha.7`, plus: + +- [DataGridPremium] Improve aggregation performance for multiple columns (#16097) @cherniavskii +- [DataGridPremium] Make Aggregation keyboard accessible in the column menu (#15934) @k-rajat19 +- [DataGridPremium] Server-side aggregation with data source (#15741) @MBilalShafi + +### Date and Time Pickers + +#### Breaking changes + +- The `date-fns` and `date-fns-jalali` date library adapters have been renamed to better align with the current stable major versions — [Learn more](https://next.mui.com/x/migration/migration-pickers-v7/#✅-rename-date-fns-adapter-imports) +- Update default `closeOnSelect` and Action Bar `actions` values - [Learn more](https://next.mui.com/x/migration/migration-pickers-v7/#update-default-closeonselect-and-action-bar-actions-values) +- The component passed to the `layout` slot no longer receives the `value`, `onChange` and `onSelectShortcut` props — [Learn more](https://next.mui.com/x/migration/migration-pickers-v7/#slot-layout). +- The component passed to the `toolbar` slot no longer receives the `value`, `onChange` and `isLandscape` props — [Learn more](https://next.mui.com/x/migration/migration-pickers-v7/#slot-toolbar). +- The component passed to the `shortcuts` slot no longer receives the `onChange`, `isValid` and `isLandscape` props — [Learn more](https://next.mui.com/x/migration/migration-pickers-v7/#slot-shortcuts). +- The `PickerShortcutChangeImportance` type has been renamed `PickerChangeImportance` — [Learn more](https://next.mui.com/x/migration/migration-pickers-v7/#renamed-variables-and-types). +- The component passed to the `layout` slot no longer receives the `rangePosition` and `onRangePositionChange` on range pickers — [Learn more](https://next.mui.com/x/migration/migration-pickers-v7/#slot-layout). +- The component passed to the `toolbar` slot no longer receives the `rangePosition` and `onRangePositionChange` on range pickers — [Learn more](https://next.mui.com/x/migration/migration-pickers-v7/#slot-toolbar). +- The component passed to the `tabs` slot no longer receives the `rangePosition` and `onRangePositionChange` on range pickers — [Learn more](https://next.mui.com/x/migration/migration-pickers-v7/#slot-tabs). + +#### `@mui/x-date-pickers@8.0.0-alpha.7` + +- [fields] Handle focusing container with `inputRef.current.focus` on `accessibleFieldDOMStructure` (#15985) @LukasTy +- [pickers] Always use `setValue` internally to update the picker value (#16056) @flaviendelangle +- [pickers] Create a new context to pass the range position props to the layout components and to the views (#15846) @flaviendelangle +- [pickers] Introduce a new concept of `manager` (#15339) @flaviendelangle +- [pickers] Improve React 19 support (#15769) @LukasTy +- [pickers] Memoize `` (#16071) @LukasTy +- [pickers] Remove `NonEmptyDateRange` type (#16035) @flaviendelangle +- [pickers] Rename `AdapterDateFns` into `AdapterDateFnsV2` and `AdapterDateFnsV3` into `AdapterDateFns` (#16082) @LukasTy +- [pickers] Rename `ctx.onViewChange` to `ctx.setView` and add it to the actions context (#16044) @flaviendelangle +- [pickers] Support `date-fns-jalali` v4 (#16011) @LukasTy +- [pickers] Update `closeOnSelect` and `actionBar.actions` default values (#15944) @LukasTy +- [pickers] Use `usePickerContext()` and `usePickerActionsContext()` instead of passing props to the `shortcuts` and `toolbar` slots (#15948) @flaviendelangle +- [l10n] Add Chinese (Taiwan) (zh-TW) locale (#16033) @nusr +- [l10n] Improve Norwegian (nb-NO) locale (#16089) @josteinjhauge + +#### `@mui/x-date-pickers-pro@8.0.0-alpha.7` [![pro](https://mui.com/r/x-pro-svg)](https://mui.com/r/x-pro-svg-link 'Pro plan') + +Same changes as in `@mui/x-date-pickers@8.0.0-alpha.7`. + +### Charts + +#### Breaking changes + +- Removed `DefaultChartsLegend` component, since it is now easier to create custom legends — [Learn more](https://next.mui.com/x/react-charts/components/#html-components). +- The default legend is now an HTML element and can be styled more easily. +- The `width` and `height` properties of the charts now only apply to the `svg` element, and not their wrappers, this might cause some layout shifts. +- `slotProps.legend.direction` now accepts `'horizontal' | 'vertical'` instead of `'row' | 'column'` — [Learn more](https://next.mui.com/x/migration/migration-charts-v7/#legend-direction-value-change-✅). +- The `getSeriesToDisplay` function was removed in favor of the `useLegend` hook. — [Learn more](https://next.mui.com/x/migration/migration-charts-v7/#the-getseriestodisplay-function-was-removed). + +#### `@mui/x-charts@8.0.0-alpha.7` + +- [charts] New HTML legend & styles (#15733) @JCQuintas +- [charts] Improve React 19 support (#15769) @LukasTy +- [charts] Fix 301 redirection in the API documentation @oliviertassinari + +#### `@mui/x-charts-pro@8.0.0-alpha.7` [![pro](https://mui.com/r/x-pro-svg)](https://mui.com/r/x-pro-svg-link 'Pro plan') + +Same changes as in `@mui/x-charts@8.0.0-alpha.7`. + +### Tree View + +#### `@mui/x-tree-view@8.0.0-alpha.7` + +- [TreeView] Improve React 19 support (#15769) @LukasTy + +#### `@mui/x-tree-view-pro@8.0.0-alpha.7` [![pro](https://mui.com/r/x-pro-svg)](https://mui.com/r/x-pro-svg-link 'Pro plan') + +Same changes as in `@mui/x-tree-view@8.0.0-alpha.7`. + +### Docs + +- [docs] Fix `EditingWithDatePickers` demo (#15967) @k-rajat19 +- [docs] Fix inconsistent multi input range field separators (#16043) @flaviendelangle +- [docs] Fix non-existing "adapter" property of `LocalizationProvider` (#16084) @tomashauser +- [docs] Refactor Data Grid with Date Pickers example (#15992) @LukasTy +- [docs] Unify the wording of the pickers slots breaking changes (#16036) @flaviendelangle + +### Core + +- [core] Clarify the release strategy (#16014) @MBilalShafi +- [core] Small fixes on docs @oliviertassinari +- [core] Sync with other repos @oliviertassinari +- [core] Update the `release:version` docs (#16038) @cherniavskii +- [code-infra] Add `testSkipIf` and `describeSkipIf` (#16049) @JCQuintas +- [test] Stabilize flaky Data Grid tests (#16053) @LukasTy + ## 8.0.0-alpha.6 _Dec 26, 2024_ diff --git a/package.json b/package.json index c84ab7265ba46..82c549817cd7a 100644 --- a/package.json +++ b/package.json @@ -1,5 +1,5 @@ { - "version": "8.0.0-alpha.6", + "version": "8.0.0-alpha.7", "private": true, "scripts": { "preinstall": "npx only-allow pnpm", diff --git a/packages/x-charts-pro/package.json b/packages/x-charts-pro/package.json index a9c9de0ca921b..5a7e12a30e73d 100644 --- a/packages/x-charts-pro/package.json +++ b/packages/x-charts-pro/package.json @@ -1,6 +1,6 @@ { "name": "@mui/x-charts-pro", - "version": "8.0.0-alpha.6", + "version": "8.0.0-alpha.7", "description": "The Pro plan edition of the Charts components (MUI X).", "author": "MUI Team", "main": "src/index.ts", diff --git a/packages/x-charts-vendor/package.json b/packages/x-charts-vendor/package.json index 1c0b3758191bd..beb6650fb488c 100644 --- a/packages/x-charts-vendor/package.json +++ b/packages/x-charts-vendor/package.json @@ -1,6 +1,6 @@ { "name": "@mui/x-charts-vendor", - "version": "8.0.0-alpha.5", + "version": "8.0.0-alpha.7", "description": "Vendored dependencies for MUI X Charts", "author": "MUI Team", "keywords": [ diff --git a/packages/x-charts/package.json b/packages/x-charts/package.json index 03562671c5200..6fdc3011cb739 100644 --- a/packages/x-charts/package.json +++ b/packages/x-charts/package.json @@ -1,6 +1,6 @@ { "name": "@mui/x-charts", - "version": "8.0.0-alpha.6", + "version": "8.0.0-alpha.7", "description": "The community edition of the Charts components (MUI X).", "author": "MUI Team", "main": "src/index.js", diff --git a/packages/x-codemod/package.json b/packages/x-codemod/package.json index c12b47838aa14..4ba5dbdf60d34 100644 --- a/packages/x-codemod/package.json +++ b/packages/x-codemod/package.json @@ -1,6 +1,6 @@ { "name": "@mui/x-codemod", - "version": "8.0.0-alpha.6", + "version": "8.0.0-alpha.7", "bin": "./codemod.js", "private": false, "author": "MUI Team", diff --git a/packages/x-data-grid-generator/package.json b/packages/x-data-grid-generator/package.json index 09cf9d7b0b063..65778ff4a9c23 100644 --- a/packages/x-data-grid-generator/package.json +++ b/packages/x-data-grid-generator/package.json @@ -1,6 +1,6 @@ { "name": "@mui/x-data-grid-generator", - "version": "8.0.0-alpha.6", + "version": "8.0.0-alpha.7", "description": "Generate fake data for demo purposes only.", "author": "MUI Team", "main": "src/index.ts", diff --git a/packages/x-data-grid-premium/package.json b/packages/x-data-grid-premium/package.json index 29e609742bd06..0e6a91e96f627 100644 --- a/packages/x-data-grid-premium/package.json +++ b/packages/x-data-grid-premium/package.json @@ -1,6 +1,6 @@ { "name": "@mui/x-data-grid-premium", - "version": "8.0.0-alpha.6", + "version": "8.0.0-alpha.7", "description": "The Premium plan edition of the Data Grid Components (MUI X).", "author": "MUI Team", "main": "src/index.ts", diff --git a/packages/x-data-grid-pro/package.json b/packages/x-data-grid-pro/package.json index 107890045a297..65e8daee41016 100644 --- a/packages/x-data-grid-pro/package.json +++ b/packages/x-data-grid-pro/package.json @@ -1,6 +1,6 @@ { "name": "@mui/x-data-grid-pro", - "version": "8.0.0-alpha.6", + "version": "8.0.0-alpha.7", "description": "The Pro plan edition of the Data Grid components (MUI X).", "author": "MUI Team", "main": "src/index.ts", diff --git a/packages/x-data-grid/package.json b/packages/x-data-grid/package.json index 572fc9b3efff6..7f97269acefb8 100644 --- a/packages/x-data-grid/package.json +++ b/packages/x-data-grid/package.json @@ -1,6 +1,6 @@ { "name": "@mui/x-data-grid", - "version": "8.0.0-alpha.6", + "version": "8.0.0-alpha.7", "description": "The Community plan edition of the Data Grid components (MUI X).", "author": "MUI Team", "main": "src/index.ts", diff --git a/packages/x-date-pickers-pro/package.json b/packages/x-date-pickers-pro/package.json index 05c6ac1b23d79..7d1110749809e 100644 --- a/packages/x-date-pickers-pro/package.json +++ b/packages/x-date-pickers-pro/package.json @@ -1,6 +1,6 @@ { "name": "@mui/x-date-pickers-pro", - "version": "8.0.0-alpha.6", + "version": "8.0.0-alpha.7", "description": "The Pro plan edition of the Date and Time Picker components (MUI X).", "author": "MUI Team", "main": "src/index.ts", diff --git a/packages/x-date-pickers/package.json b/packages/x-date-pickers/package.json index e76bb93630d2a..56781abac6f33 100644 --- a/packages/x-date-pickers/package.json +++ b/packages/x-date-pickers/package.json @@ -1,6 +1,6 @@ { "name": "@mui/x-date-pickers", - "version": "8.0.0-alpha.6", + "version": "8.0.0-alpha.7", "description": "The community edition of the Date and Time Picker components (MUI X).", "author": "MUI Team", "main": "src/index.ts", diff --git a/packages/x-internals/package.json b/packages/x-internals/package.json index 429678f13ed90..7f6bf17b78491 100644 --- a/packages/x-internals/package.json +++ b/packages/x-internals/package.json @@ -1,6 +1,6 @@ { "name": "@mui/x-internals", - "version": "8.0.0-alpha.6", + "version": "8.0.0-alpha.7", "description": "Utility functions for the MUI X packages (internal use only).", "author": "MUI Team", "license": "MIT", diff --git a/packages/x-license/package.json b/packages/x-license/package.json index 25e1d6ea4af62..4d80321e87540 100644 --- a/packages/x-license/package.json +++ b/packages/x-license/package.json @@ -1,6 +1,6 @@ { "name": "@mui/x-license", - "version": "8.0.0-alpha.6", + "version": "8.0.0-alpha.7", "description": "MUI X License verification", "author": "MUI Team", "main": "src/index.ts", diff --git a/packages/x-tree-view-pro/package.json b/packages/x-tree-view-pro/package.json index 5b269168b652a..9476c29501938 100644 --- a/packages/x-tree-view-pro/package.json +++ b/packages/x-tree-view-pro/package.json @@ -1,6 +1,6 @@ { "name": "@mui/x-tree-view-pro", - "version": "8.0.0-alpha.6", + "version": "8.0.0-alpha.7", "description": "The Pro plan edition of the Tree View components (MUI X).", "author": "MUI Team", "main": "src/index.ts", diff --git a/packages/x-tree-view/package.json b/packages/x-tree-view/package.json index bc8cfb25a0d8e..edf0358f35f56 100644 --- a/packages/x-tree-view/package.json +++ b/packages/x-tree-view/package.json @@ -1,6 +1,6 @@ { "name": "@mui/x-tree-view", - "version": "8.0.0-alpha.6", + "version": "8.0.0-alpha.7", "description": "The community edition of the Tree View components (MUI X).", "author": "MUI Team", "main": "src/index.ts",