This blog post describes how to implement tree-like hierarchy using Office UI Fabric React (OUIFR) Grouped DetailsList component. We'll be working with a hierarchy similar to the one displayed on the image below:
Office UI Fabric React (OUIFR) DetailsList ignores (doesn't render) items for the group if it (the group) has subgroups. As a result for a tree-like hierarchy - some of the items could be lost. Jump to solution.
We need to display a hierarchical grid, or grouped grid where each node (group) can have child nodes as well as leaf items. For the hierarchy from the picture above we want to see something like this:
So, we have a root level node code that contains leaf item index.ts and two subgroups with their own items: components and webparts. As we're developing SharePoint Framework solution (of course - this blog is about O365 and SharePoint =)) it would be great to use OUIFR DetailsList component to display grouped grid with a header.
Initial implementation for our component will be pretty simple. We need to define our leaf item interface:
And our node interface:
Using these two interfaces we can define our hierarchy like that:
Now, we need to process our hierarchy to construct flat array of IItem items and array of IGroup groups. This arrays will be used by DetailsList component to display grouped list. Let's assume that the hierarchy is passed in the props of our component:
Then, we can used the methods below to process the hierarchy and save results to the state:
And now we can render the DetailsList using values from the state:
If we look at the rendered result, we'll see such a list:
The problem here is we're missing index.ts leaf item in the code group. So, it's kinda data loss situation from user perspective. The reason for that, as mentioned above, is DetailsList component ignores group's items if there are subgroups.
The solution here consists of two parts. First, we'll add "fake" subgroup and place all the missing leaf items in it. For our sample we'll add fake subgroup to code group and move index.ts into it:
The fake group doesn't have data as there is no actual INode item related to it. Now our component looks like that:
The index.ts item is there, but, as expected, it is displayed as a child of fake group.
The second step of the solution is to override rendering of group's header and hide the header for any fake group. As we know, fake groups don't have data assigned, so we can use it to determine if the group is fake. And we can override group's header rendering using groupProps property of the DetailsList:
After these changes we'll see the result we actually want. Moreover, all events, including collapsing/expanding will still work as expected!