Skip to content

Commit

Permalink
mandatory attrib. for ToggleButtons and typing.
Browse files Browse the repository at this point in the history
  • Loading branch information
iisakkirotko committed Sep 7, 2023
1 parent 6bdd2bf commit 944d7ca
Show file tree
Hide file tree
Showing 2 changed files with 120 additions and 15 deletions.
89 changes: 74 additions & 15 deletions solara/components/togglebuttons.py
Original file line number Diff line number Diff line change
@@ -1,7 +1,8 @@
from typing import Callable, Dict, List, Optional, TypeVar, Union, cast
from typing import Callable, Dict, List, Optional, TypeVar, Union, cast, overload

import ipyvuetify as v
import reacton
from typing_extensions import Literal

import solara
from solara.alias import rv
Expand All @@ -19,13 +20,44 @@ def _get_button_value(button: reacton.core.Element):
return value


@overload
@solara.value_component(None)
def ToggleButtonsSingle(
value: Union[None, T, solara.Reactive[T]] = None,
value: Union[T, solara.Reactive[T]],
values: List[T] = ...,
children: List[reacton.core.Element] = ...,
on_value: Optional[Callable[[T], None]] = ...,
dense: bool = ...,
mandatory: Literal[True] = ...,
classes: List[str] = ...,
style: Union[str, Dict[str, str], None] = ...,
) -> reacton.core.ValueElement[v.BtnToggle, T]:
...


@overload
@solara.value_component(None)
def ToggleButtonsSingle(
value: Union[Optional[T], solara.Reactive[Optional[T]]] = None,
values: List[T] = ...,
children: List[reacton.core.Element] = ...,
on_value: Optional[Callable[[T], None]] = ...,
dense: bool = ...,
mandatory: Literal[False] = ...,
classes: List[str] = ...,
style: Union[str, Dict[str, str], None] = ...,
) -> reacton.core.ValueElement[v.BtnToggle, T]:
...


@solara.value_component(None)
def ToggleButtonsSingle(
value: Union[None, T, solara.Reactive[T], solara.Reactive[Optional[T]]],
values: List[T] = [],
children: List[reacton.core.Element] = [],
on_value: Optional[Callable[[T], None]] = None,
dense: bool = False,
mandatory: bool = True,
classes: List[str] = [],
style: Union[str, Dict[str, str], None] = None,
) -> reacton.core.ValueElement[v.BtnToggle, T]:
Expand Down Expand Up @@ -87,20 +119,17 @@ def Page():
reactive_value = solara.use_reactive(value, on_value) # type: ignore
children = [solara.Button(label=str(value)) for value in values] + children
values = values + [_get_button_value(button) for button in children] # type: ignore

if mandatory:
index, set_index = solara.use_state_or_update(values.index(reactive_value.value) if reactive_value.value is not None else 0, key="index")

def on_index(index):
set_index(index)
# When mandatory = True, index should not be None, but we are letting the front-end take care of setting index to 0 because of a bug
# (see https://github.com/widgetti/solara/issues/282)
# TODO: set index to 0 on python side (after #282 is resolved)
index, set_index = solara.use_state_or_update(values.index(reactive_value.value) if reactive_value.value is not None else None, key="index")

def on_index(index):
set_index(index)
if mandatory:
value = values[index]
reactive_value.set(value)

elif not mandatory:
index, set_index = solara.use_state_or_update(values.index(reactive_value.value) if reactive_value.value is not None else None, key="index")

def on_index(index):
set_index(index)
else:
if index is not None:
value = values[index]
reactive_value.set(value)
Expand All @@ -109,13 +138,43 @@ def on_index(index):

return cast(
reacton.core.ValueElement[v.BtnToggle, T],
rv.BtnToggle(children=children, multiple=False, mandatory=True, v_model=index, on_v_model=on_index, dense=dense, class_=class_, style_=style_flat),
rv.BtnToggle(children=children, multiple=False, mandatory=mandatory, v_model=index, on_v_model=on_index, dense=dense, class_=class_, style_=style_flat),
)


@overload
@solara.value_component(None)
def ToggleButtonsMultiple(
value: Union[List[T], solara.Reactive[List[T]]],
values: List[T] = ...,
children: List[reacton.core.Element] = ...,
on_value: Optional[Callable[[T], None]] = ...,
dense: bool = ...,
mandatory: Literal[True] = ...,
classes: List[str] = ...,
style: Union[str, Dict[str, str], None] = ...,
) -> reacton.core.ValueElement[v.BtnToggle, T]:
...


@overload
@solara.value_component(None)
def ToggleButtonsMultiple(
value: Union[Optional[List[T]], solara.Reactive[Optional[List[T]]]] = [],
values: List[T] = ...,
children: List[reacton.core.Element] = ...,
on_value: Callable[[List[T]], None] = ...,
dense: bool = ...,
mandatory: Literal[False] = ...,
classes: List[str] = ...,
style: Union[str, Dict[str, str], None] = ...,
) -> reacton.core.ValueElement[v.BtnToggle, List[T]]:
...


@solara.value_component(None)
def ToggleButtonsMultiple(
value: Union[List[T], Optional[List[T]], solara.Reactive[List[T]], solara.Reactive[Optional[List[T]]]] = [],
values: List[T] = [],
children: List[reacton.core.Element] = [],
on_value: Callable[[List[T]], None] = None,
Expand Down
46 changes: 46 additions & 0 deletions tests/unit/toggle_button_test.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,46 @@
from typing import Optional

import solara


def test_toggle_buttons_single():
value: Optional[str] = None

def set(value_):
nonlocal value
value = value_

@solara.component
def Test():
with solara.ToggleButtonsSingle("noot", on_value=set) as main:
solara.Button("Aap", value="aap")
solara.Button("Noot", value="noot")
solara.Button("Mies", value="mies")
return main

group, rc = solara.render_fixed(Test())
assert group.v_model == 1
group.v_model = 2
assert value == "mies"


def test_toggle_buttons_multiple():
value: Optional[str] = None

def set(value_):
nonlocal value
value = value_

@solara.component
def Test():
with solara.ToggleButtonsMultiple(["noot"], on_value=set) as main:
solara.Button("Aap", value="aap")
solara.Button("Noot", value="noot")
solara.Button("Mies", value="mies")
return main

group, rc = solara.render_fixed(Test())
assert group.v_model == [1]
group.v_model = [0, 2]
assert value is not None
assert value == ["aap", "mies"]

0 comments on commit 944d7ca

Please sign in to comment.