Skip to content

Commit

Permalink
Add NestedSelect dynamic levels (#6011)
Browse files Browse the repository at this point in the history
  • Loading branch information
ahuang11 committed Dec 18, 2023
1 parent 99bab46 commit 80b902e
Show file tree
Hide file tree
Showing 2 changed files with 63 additions and 4 deletions.
59 changes: 58 additions & 1 deletion panel/tests/widgets/test_select.py
Original file line number Diff line number Diff line change
Expand Up @@ -459,7 +459,7 @@ def test_nested_select_partial_options_set(document, comm):
},
}
select = NestedSelect(options=options)
select.options = {"Ben": {}}
select.options = {"Ben": []}
assert select._widgets[0].value == 'Ben'
assert select._widgets[0].visible
assert select.value == {0: 'Ben'}
Expand Down Expand Up @@ -601,6 +601,63 @@ def list_options(level, value):
assert select._max_depth == 3


def test_nested_select_dynamic_levels(document, comm):
select = NestedSelect(
options={
"Easy": {"Easy_A": {}, "Easy_B": {}},
"Medium": {
"Medium_A": {},
"Medium_B": {"Medium_B_1": []},
"Medium_C": {
"Medium_C_1": ["Medium_C_1_1"],
"Medium_C_2": ["Medium_C_2_1", "Medium_C_2_2"],
},
},
"Hard": {}
},
levels=["A", "B", "C", "D"],
)
assert select._widgets[0].visible
assert select._widgets[1].visible
assert not select._widgets[2].visible
assert not select._widgets[3].visible

assert select._widgets[0].options == ["Easy", "Medium", "Hard"]
assert select._widgets[1].options == ["Easy_A", "Easy_B"]
assert select._widgets[2].options == []
assert select._widgets[3].options == []

assert select.value == {"A": "Easy", "B": "Easy_A", "C": None, "D": None}

# now update to Medium
select.value = {"A": "Medium", "B": "Medium_C"}
assert select._widgets[0].visible
assert select._widgets[1].visible
assert select._widgets[2].visible
assert select._widgets[3].visible

assert select._widgets[0].options == ["Easy", "Medium", "Hard"]
assert select._widgets[1].options == ["Medium_A", "Medium_B", "Medium_C"]
assert select._widgets[2].options == ["Medium_C_1", "Medium_C_2"]
assert select._widgets[3].options == ["Medium_C_1_1"]

assert select.value == {"A": "Medium", "B": "Medium_C", "C": "Medium_C_1", "D": "Medium_C_1_1"}

# now update to Hard
select.value = {"A": "Hard"}
assert select._widgets[0].visible
assert not select._widgets[1].visible
assert not select._widgets[2].visible
assert not select._widgets[3].visible

assert select._widgets[0].options == ["Easy", "Medium", "Hard"]
assert select._widgets[1].options == []
assert select._widgets[2].options == []
assert select._widgets[3].options == []

assert select.value == {"A": "Hard", "B": None, "C": None, "D": None}


def test_nested_select_callable_must_have_levels(document, comm):
def list_options(level, value):
pass
Expand Down
8 changes: 5 additions & 3 deletions panel/widgets/select.py
Original file line number Diff line number Diff line change
Expand Up @@ -385,7 +385,7 @@ def _gather_values_from_widgets(self, up_to_i=None):
name = level.get("name", i)
else:
name = level
values[name] = select.value
values[name] = select.value if select.options else None

return values

Expand Down Expand Up @@ -414,7 +414,9 @@ def _find_max_depth(self, d, depth=1):
for value in d.values():
if isinstance(value, dict):
max_depth = max(max_depth, self._find_max_depth(value, depth + 1))
if len(value) == 0:
# dict means it's a level, so it's not the last level
# list means it's a leaf, so it's the last level
if isinstance(value, list) and len(value) == 0 and max_depth > 0:
max_depth -= 1
return max_depth

Expand Down Expand Up @@ -562,7 +564,7 @@ def _update_widget_options_interactively(self, event):
options = options[select.value]
else:
options = options[list(options.keys())[0]]
visible = True
visible = bool(options)

if i < start_i:
# If the select widget is before the one
Expand Down

0 comments on commit 80b902e

Please sign in to comment.