Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[TreeView] Support parent / children selection relationship #12883

Closed
flaviendelangle opened this issue Apr 23, 2024 · 23 comments · Fixed by #14899
Closed

[TreeView] Support parent / children selection relationship #12883

flaviendelangle opened this issue Apr 23, 2024 · 23 comments · Fixed by #14899
Assignees
Labels
component: tree view TreeView, TreeItem. This is the name of the generic UI component, not the React module! linked in docs The issue is linked in the docs, so completely skew the upvotes

Comments

@flaviendelangle
Copy link
Member

flaviendelangle commented Apr 23, 2024

Related issues

Current behavior

In the current implementation of the Tree View component (both SimpleTreeView and RichTreeView), selecting an item does not select its children and selecting all children of an item does not select its parent.

The behavior is exactly the same with checkbox selection.

Selecting a parent (single select)

image

Model value: "A"

Selecting a leaf (single select)

image

Model value: "A1"

Selecting a parent (multi select)

Here is what the result is when you click on "A":

image

Model value: ["A"]

Selecting a leaf (multi select)

Here is what the result is when you click on "A1":

image

Model value: ["A1"]

Selecting all leaves of an item

Here is what the result is when you click on "A1" then click on "A3" while pressing Shift:

image

Model value: ["A1", "A2", "A3"]

Alternatives model

Model contains the selected leaves and the parent selection is deduced from their descendants

Based on #11452 (comment)

The following model can only work in multi selection.
The idea here is to only apply the selection on the leaves and then deduce the visual selection of the parents based on the selection status of their descendants:

  • every descendant selected: selected
  • some descendant selected: partially selected
  • no descendant selected: not selected

Selecting a parent (multi select)

Here is what the result is when you click on "A":

image

Model value: ["A1", "A2", "A3"]

Selecting a leaf (multi select)

Here is what the result is when you click on "A1":

image

Model value: ["A1"]

Selecting all leaves of an item

Here is what the result is when you click on "A1" then click on "A3" while pressing Shift:

Model value: ["A1", "A2", "A3"]

image

Keep the model as is but visually select a children if one of its ancestors is selected

Based on #11452 (comment)

The following model can work in both single and multi selection.
The idea here is to visually select an item if it is selected in the model or if one of its ancestor is selected.

It might not work well with the checkbox where people probably expect the parent checkbox to change its status when selecting the children.

Selecting a parent (single select)

image

Model value: "A"

Selecting a leaf (single select)

image

Model value: "A1"

Selecting a parent (multi select)

Here is what the result is when you click on "A":

image

Model value: ["A"]

Selecting a leaf (multi select)

Here is what the result is when you click on "A1":

image

Model value: ["A1"]

Selecting all leaves of an item

Here is what the result is when you click on "A1" then click on "A3" while pressing Shift:

image

Model value: ["A1", "A2", "A3"]

Benchmarks

Search keywords:

@flaviendelangle flaviendelangle added the component: tree view TreeView, TreeItem. This is the name of the generic UI component, not the React module! label Apr 23, 2024
@flaviendelangle
Copy link
Member Author

If you have other approaches that you saw on other Tree View (or similar component) I would be super interested.
When we will have a good understanding of what exists, we will be in a better position to decide what we want to implement (it might be several approaches and let people switch between them).

I still need to explore how the various approaches work with lazy loading (which we want to support eventually).

@LukasTy
Copy link
Member

LukasTy commented Apr 23, 2024

I'll just re-post the community members' suggested example: https://primereact.org/treeselect/#check
IMHO, it's a good role model checkbox selection behavior-wise.

Regarding our implementation, I think it should be highly flexible to cover various use cases.

@Aberkati
Copy link

Aberkati commented Apr 23, 2024

I'll just re-post the community members' suggested example: https://primereact.org/treeselect/#check IMHO, it's a good role model checkbox selection behavior-wise.

Regarding our implementation, I think it should be highly flexible to cover various use cases.

It's a very good example, hopefuly we get a similar example with MUIX

@flaviendelangle
Copy link
Member Author

I'll just re-post the community members' suggested example: https://primereact.org/treeselect/#check

In term of behaviors, this looks a lot like

If I understand correctly, with this approach you change the structure of the model to look something like "Model contains the selected leaves and the parent selection is deduced from their descendants" described above BUT with both the children and the parent represented in the model.

{
    '0-0': {
        partialChecked: false,
        checked: true
    }
}

With this approach, if I understand correctly, the model describes what is selected (fully selected, partially selected or not selected) item by item without any parent / children relationship.
And the UI actions (clicking on an item, clicking on a checkbox, etc...) updates the model and selects children and/or parent based on the selection rule we want (might have some parent / children relationship, might not).


That could be an interesting approach.
I'm not a huge fan of the shape of the model (having a boolean when not using checkbox and this two-properties object when using them) but this could be adapter.

One could say that this model structure is super flexible since it lets the model remain the same with different selection behaviors.
You always have the same model with every item selected and based on some prop you can change which item are added to the model when selecting an item.

The only weakness I can see is that if someone control the model, there is no guarantee that he passes a model value that is coherent with the selection mechanism in place in his app.
For instance, he configures the Tree View to automatically selects the children when selecting a parent. So when you click A, it calls the onSelectedItemsChange with ["A", "A1", "A2", "A3"].
But based on some external UI he updates props.selectedItems and passes ["A1", "A2", "A3"] to the Tree View.
This would visually select "A1", "A2" and "A3" which is a selection state you can never achieve through interactions with the Tree View.

As long as we agree that this is not a problem, this looks like a very flexible approach.

@flaviendelangle
Copy link
Member Author

flaviendelangle commented Apr 23, 2024

Side note, I am not a fan of array model at all (for performance reasons mainly and because it looks like it ordered when in fact we don't enforce any order on the model).
So if the parent / children selection relationship is an opportunity to move to an object model I'm all in favor of doing it.

@joserodolfofreitas
Copy link
Member

DataGrid is also aiming to support this feature soon, so it'd be good for us to keep aligned on the DevEx.

@flaviendelangle flaviendelangle added the waiting for 👍 Waiting for upvotes label May 13, 2024
@flaviendelangle flaviendelangle added the linked in docs The issue is linked in the docs, so completely skew the upvotes label May 13, 2024
@oliviertassinari
Copy link
Member

oliviertassinari commented May 23, 2024

(coming from seeing the tweet https://twitter.com/MUI_hq/status/1793654965996823022, initially I wanted to raise the intermediate checkbox state missing, but I got it, it's a broader discussion)

I suspect that the "Checkbox selection" should be a separate feature from "Selection". I mean to have a separate state. I can see the use case for end-users to check items, the items they want to delete, while having another item selected to be expanded on a right panel.

If we benchmark from https://www.notion.so/mui-org/mui-x-Tree-View-component-c283f4a957474b79b293e994881b1ee8.

It seems that those behaving like it (independent). This is what I was expecting:

I see those with separate states, but where selection triggers checkbox selection and checkbox selection triggers selection:

I see those that have selection disabled when checkbox selection is enabled:

I see those that have checkbox selection meaning selection but selection still being independent.

Those that behave closely to #11452 in the sense that have checkbox selection and selection merged into a single state, however, also have the parent checkbox relationship selection correct:

I see behaving like in #11452:

I'll just re-post the community members' suggested example: https://primereact.org/treeselect/#check
IMHO, it's a good role model checkbox selection behavior-wise.

This feels more: #514

@johnnyreilly
Copy link

johnnyreilly commented May 25, 2024

Awesome to see this looks like it's getting given native support! If anyone wants to this behaviour now, it is hand rollable and I've documented it here:

https://johnnyreilly.com/mui-react-tree-view-check-children-uncheck-parents

Once implemented, behaviour looks like this:

image

Brilliant to see that this is likely to be obsolete very soon! Thanks for pointing me to this @oliviertassinari

A thing that can't be handled by the approach I'm employing, is where parent nodes have a visual state that indicates where some, but not all, nodes are selected. It would be tremendous if that could be considered for the native design. (An example of this behaviour is https://www.jstree.com/ )

It looks like you're already considering this given this image in the initial description:

@Aberkati
Copy link

Awesome to see this looks like it's getting given native support! If anyone wants to this behaviour now, it is hand rollable and I've documented it here:

https://johnnyreilly.com/mui-react-tree-view-check-children-uncheck-parents

Once implemented, behaviour looks like this:

image image

Brilliant to see that this is likely to be obsolete very soon! Thanks for pointing me to this @oliviertassinari

A thing that can't be handled by the approach I'm employing, is where parent nodes have a visual state that indicates where some, but not all, nodes are selected. It would be tremendous if that could be considered for the native design. (An example of this behaviour is https://www.jstree.com/ )

It looks like you're already considering this given this image in the initial description:

defaultSelectedItems is not working in your demo example, I cannot selectItems when component in mounted

@johnnyreilly
Copy link

I can't see your code @Aberkati, but maybe it's worth you playing with this live demo on stackblitz?

https://stackblitz.com/edit/mui-react-tree-view-check-children-uncheck-parents?file=Demo.tsx

@Aberkati
Copy link

Aberkati commented May 28, 2024

This is my code : https://stackblitz.com/edit/mui-react-tree-view-check-children-uncheck-parent-jivweq?file=Demo.tsx

I added defaultSelectedItems with an array with ids but seems not working..

@johnnyreilly
Copy link

johnnyreilly commented May 28, 2024

I should tell you that I'm on my phone in the South of France and I can't obviously tell what the issue is that you're facing - but it seems to work for me on my phone 😅

@Aberkati
Copy link

I should tell you that I'm on my phone in the South of France and I can't obviously tell what the issue is that you're facing - but it seems to work for me on my phone 😅

Oh really ? When you refresh using an id of existent ID on the array it automaticly checked when your component is mounted ?

@johnnyreilly
Copy link

defaultSelectedItems is for uncontrolled components I think? My example is using the controlled component behaviour for selection. Do you want to try setting the initial state using setSelectedIds instead?

@flaviendelangle
Copy link
Member Author

defaultSelectedItems is for un-controlled components
For controlled ones, you can use selectedItems and onSelectedItemsChange

@Skykingeagle
Copy link

defaultSelectedItems is for un-controlled components
For controlled ones, you can use selectedItems and onSelectedItemsChange

How would I limit these checkboxes to certain nodes? For example if I only wanted the checkbook selection to be true for the leaf nodes only, how would I be able to do that?

@flaviendelangle
Copy link
Member Author

flaviendelangle commented Jun 4, 2024

Right now we have no way of doing it easily.
The data grid has a isRowSelectable prop that we could replicate on the Tree View components to limit the selection to certain rows.
But it's not the topic being discussed in this issue, I would advise you to open a new issue so that we can prioritize your needs

@johnnyreilly
Copy link

Just a heads up that I found and fixed a bug in my handrolled logic last night. I've updated the blog post and the StackBlitz

If anyone is curious as to the fix, see here:

johnnyreilly/blog.johnnyreilly.com@f325762

@johnnyreilly
Copy link

johnnyreilly commented Jun 11, 2024

And one more fix! (previous code didn't walk the tree all the way to the top when reselecting from a leaf node). Again I've updated the blog post and the StackBlitz

The fix for this issue can been seen in this commit:

johnnyreilly/blog.johnnyreilly.com@908bdf0

@Aberkati
Copy link

And one more fix! (previous code didn't walk the tree all the way to the top when reselecting from a leaf node). Again I've updated the blog post and the StackBlitz

The fix for this issue can been seen in this commit:

johnnyreilly/blog.johnnyreilly.com@908bdf0

Great!! I'm trying to add a input field on top of this component to filter my nodes but seems to be hard, have you an idea how to achieve it please ? this an example demo link : https://stackblitz.com/edit/bgcued-tebqqr?file=src%2FApp.jsx,package.json
Thank you!

@Aberkati
Copy link

defaultSelectedItems is for un-controlled components For controlled ones, you can use selectedItems and onSelectedItemsChange

Yes I've tried it and it works, but the problem is when I select a parent id as selected onmount his leaf are uncheked..
Please take a look here :
image

@flaviendelangle flaviendelangle removed the waiting for 👍 Waiting for upvotes label Jul 9, 2024
@brammitch
Copy link

I stumbled across this issue while trying to implement this myself, so I thought I'd share a minimal example of how I did it for a project.

Open in StackBlitz

image

Copy link

This issue has been closed. If you have a similar problem but not exactly the same, please open a new issue.
Now, if you have additional information related to this issue or things that could help future readers, feel free to leave a comment.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
component: tree view TreeView, TreeItem. This is the name of the generic UI component, not the React module! linked in docs The issue is linked in the docs, so completely skew the upvotes
Projects
Status: Recently completed
Development

Successfully merging a pull request may close this issue.

8 participants