Checkbox tree - hierarchical selection¶
Source: examples/panels/wunderbaum/checkbox_tree.py
Test: tests/panels/wunderbaum/examples/test_checkbox_tree.py
Checkboxes with tri-state propagation via selectMode: "hier": checking a parent checks every descendant, and a partial child selection shows the parent as indeterminate.
The code¶
import panel as pn
from panelini import Panelini
from panelini.panels.wunderbaum import Wunderbaum
source = [
{
"title": "Fruits",
"key": "fruits",
"expanded": True,
"children": [
{
"title": "Citrus",
"key": "citrus",
"expanded": True,
"children": [
{"title": "Orange", "key": "orange"},
{"title": "Lemon", "key": "lemon"},
{"title": "Lime", "key": "lime"},
],
},
# ...
],
},
# ...
]
tree = Wunderbaum(
source=source,
options={"checkbox": True, "selectMode": "hier"},
)
checked_display = pn.pane.Markdown("**Checked:** (none)")
def _get_checked_keys(src: list[dict]) -> list[str]:
"""Walk *src* and return keys of nodes with ``selected: True``."""
keys: list[str] = []
def walk(nodes: list[dict]) -> None:
for n in nodes:
if n.get("selected"):
keys.append(n["key"])
walk(n.get("children", []))
walk(src)
return keys
def _on_source_change(*args: object) -> None:
keys = _get_checked_keys(tree.source)
checked_display.object = f"**Checked:** {', '.join(keys)}" if keys else "**Checked:** (none)"
tree.param.watch(_on_source_change, ["source"])
Key points:
Two options drive the behaviour:
checkbox: Truerenders the boxes,selectMode: "hier"enables parent/child propagation and the indeterminate (tri-state) parent.Selection state syncs back into
tree.sourceas aselected: Trueflag on each node - no separate event wiring needed.tree.param.watch(..., ["source"])fires whenever the client toggles a box, so_get_checked_keyscan walk the tree and update the live display.
How the test exercises it¶
The test file is the most thorough in the suite (8 tests). It clicks the .wb-checkbox of specific rows and verifies both the backend (_get_checked_keys(tree.source)) and the client-side wunderbaum state (via a findInShadowRoots helper reading node.selected / node._partsel):
checking a leaf selects only that node;
checking a parent selects all descendants, unchecking deselects them;
a single checked child leaves the parent indeterminate, not fully selected;
unchecking a grandchild propagates indeterminate state up to grandparent;
sibling subtrees stay independent.
An autouse fixture resets tree.source before each test to prevent state leakage between cases.
Run it live¶
This example runs entirely in your browser via Pyodide. The first load downloads packages, so give it a few seconds.
See also¶
Virtual filesystem - the full demo - checkboxes combined with a “Delete Checked” batch action
Wunderbaum tree inside panelini - the minimal tree this builds on