mui / mui-x

MUI X: Build complex and data-rich applications using a growing list of advanced React components, like the Data Grid, Date and Time Pickers, Charts, and more!
https://mui.com/x/
4.12k stars 1.27k forks source link

[DataGrid] Expand/Collapse all groups including default and "multiple" rowGroupingColumnModes #11421

Open evgeniiarts opened 9 months ago

evgeniiarts commented 9 months ago

Summary πŸ’‘

When dealing with large datasets, row grouping with it's two modes comes in very handy. Currently there is no native method to expand/collapse all grouped rows, so I have tried some things out. This issue relates somewhat to: https://github.com/mui/mui-x/issues?q=is%3Aissue+toggle+all+groups and possibly some other issues, which I am unable to find right now.

Examples 🌈

I have created a codesandbox with two approaches I could so far come up with: https://codesandbox.io/p/sandbox/expand-all-multiple-grouping-cols-hvykhr

But sadly, none of them is performant enough to provide a good user experience for more than one grouping column. The first approach where we expand groups from the rootnode works fine with very large datasets and toggles all groups nearly instantly. However, both demonstrated approaches cannot provide a performant way to toggle all groups in multiple grouping columns when we set the rowGroupingColumnMode to "multiple".

The initial dataset in the sandbox is set to 1000 rows, and when we hit the toggle expansion on the "Trader name" column, we can already feel the impact. While the "Commodity" toggles just fine with even 10000 rows, there is maybe a very slight delay. The toggle for the second column becomes unusable with 10000 rows. toggle_expansion__all

At least if we use "traderName" as the second grouping criteria. If we swap "traderName" for "status", 10000 rows are actually handled quite fast. This is because the performance of the function highly depends on the number of rows it operates on. So far I am not able to come up with a more performant approach and would be happy to hear other suggestions or even have this functionality as a native feature.

Motivation πŸ”¦

I work on very data heavy applications where our datagrids can contain more than 20k rows. Therefore features such as grouping are extremely helpful to organize the data and navigate through it. The data is deeply connected with each other, and has quite a few overlaps in row values so the grouping comes in handy especially.

Aggregation is also a highly appreciated feature and comes in amazingly with the grouping. So one of my use cases is that my users want to group their data by multiple columns and enable the aggregation, having all the rows collapsed and showing the aggregated values. Upon detecting something unusual my users want to investigate it further and click a few rows in a group open. In order to get back to the aggregated state of the table they would need to manually toggle all those opened rows back which I would like to solve with the suggested functionality.

Search keywords: toggle, expand, collapse, row grouping, row grouping multiple

evgeniiarts commented 9 months ago

I initially thought the issue was related to how I get to the grouping nodes of the nested groups, and maybe it is a little bit. But now I have come up with a solution to keep track of all IDs for any group by introducing an extendedGroupingModel in addition to the controlled rowGroupingModel. https://codesandbox.io/p/sandbox/expand-all-multiple-grouping-cols-forked-wsmdkr This helped me realize that the issue was more related to the amount of "traderNames" within the grouping column and that expanding many rows at once is causing the performance issue.

I then came up with the idea of trying a batch updateRows on the grouping rows, but... this sadly led to a hilarious result as it has blazingly fast, even on the trader column, turned all column ids to the auto-generated-column ids and removed the rest from the rows. Meaning that it does not work if we want to manipulate the behavior of auto-generated-rows. What I tried is: ` const toggleAllGroups = (event, fieldName) => { event.stopPropagation(); const shouldExpand = !isAnyGroupExpanded();

// Use the extendedGroupingModel to get the group row IDs directly
const groupRowIds = extendedGroupingModel[fieldName] || [];

console.log(groupRowIds);
// Prepare updates for batch processing
const updates = groupRowIds.map((groupId) => ({
  id: groupId,
  childrenExpanded: shouldExpand,
}));

// Perform batch update
apiRef.current.updateRows(updates);

}; ` My attempted batch update result: result

As a conclusion, it would be nice to have the possibility to perform batch updates on dynamically (auto-generated) added rows in addition to other rows.

evgeniiarts commented 9 months ago

After giving it more thinking I came up with yet another idea of how to handle the expansion in a performant way. And have now managed to do so with even 50000 rows: https://codesandbox.io/p/sandbox/expand-all-multiple-grouping-cols--2d-try-forked-l22w7n

In order to do so I utilize the isGroupExpandedByDefault={isGroupExpanded} approach. When we toggle all groups within any given columns, it's value gets set.

Yet still I must say I am deeply unsatisfied with the approach of how I am addressing the grouping column, because it is an unelegant string operation in my ExpandAllButton button:

function ExpandAllButton({
  columnField,
  extendedGroupingModel,
  toggleGroupExpansion,
}) {
  const apiRef = useGridApiContext();

  // Function to extract the actual field name from the grouping col
  const extractFieldName = (groupColumnName) => {
    const prefix = "__row_group_by_columns_group_";
    const suffix = "__";
    if (
      groupColumnName.startsWith(prefix) &&
      groupColumnName.endsWith(suffix)
    ) {
      return groupColumnName.slice(prefix.length, -suffix.length);
    }
    return null;
  };

I am wondering if there is/could be a better way of performing this operation.

michelengelen commented 9 months ago

Hey @evgeniiarts ... that looks like a solid option to perform this operation. I am not sure myself if there is any other approach that would yield the results (in a performant way) you are expecting from it.

Maybe @MBilalShafi knows of a way, but when your are ok with your solution this seems like a good option.

evgeniiarts commented 9 months ago

@michelengelen Thanks for the feedback! For me personally the solution is absolutely fine, as I work as a UX designer and utilize MUI and react for prototyping.

But when it comes to productive environment, I think it would be great to have the functionality as a native feature. Reason being is that when we use row grouping in any mode (default or multiple), being able to toggle all groups serves a whole lot of use cases as it gives users more control over the data visualization.

michelengelen commented 9 months ago

Then I would say we wait for upvotes. If we get enough of them we can have a look.