Skip to content

Commit

Permalink
use on_close
Browse files Browse the repository at this point in the history
  • Loading branch information
maartenbreddels committed Oct 2, 2023
1 parent 4cccec4 commit c1c0009
Show file tree
Hide file tree
Showing 3 changed files with 76 additions and 35 deletions.
49 changes: 47 additions & 2 deletions solara/lab/components/confirmation_dialog.py
Original file line number Diff line number Diff line change
@@ -1,15 +1,53 @@
from typing import Callable, List, Union
from typing import Callable, List, Union, overload

import reacton.ipyvuetify as v

import solara


@overload
def ConfirmationDialog(
open: solara.Reactive[bool],
*,
on_close: Union[None, Callable[[], None]] = None,
content: Union[str, solara.Element] = "",
title: str = "Confirm action",
ok: Union[str, solara.Element] = "OK",
on_ok: Callable[[], None] = lambda: None,
cancel: Union[str, solara.Element] = "Cancel",
on_cancel: Callable[[], None] = lambda: None,
children: List[solara.Element] = [],
max_width: Union[int, str] = 500,
persistent: bool = False,
):
...


# when open is a boolean, on_close should be given, otherwise a dialog can never be closed
# TODO: copy this pattern to many other components
@overload
def ConfirmationDialog(
open: bool,
*,
on_close: Callable[[], None],
content: Union[str, solara.Element] = "",
title: str = "Confirm action",
ok: Union[str, solara.Element] = "OK",
on_ok: Callable[[], None] = lambda: None,
cancel: Union[str, solara.Element] = "Cancel",
on_cancel: Callable[[], None] = lambda: None,
children: List[solara.Element] = [],
max_width: Union[int, str] = 500,
persistent: bool = False,
):
...


@solara.component
def ConfirmationDialog(
open: Union[solara.Reactive[bool], bool],
*,
on_open: Callable[[bool], None] = lambda open: None,
on_close: Union[None, Callable[[], None]] = None,
content: Union[str, solara.Element] = "",
title: str = "Confirm action",
ok: Union[str, solara.Element] = "OK",
Expand All @@ -35,6 +73,7 @@ def ConfirmationDialog(
open_delete_confirmation = solara.reactive(False)
def delete_user():
# put your code to perform the action here
print("User being deleted...")
@solara.component
Expand All @@ -60,6 +99,12 @@ def Page():
* `persistent`: When False (the default), clicking outside of the element or pressing esc key will trigger cancel.
"""

def on_open(open_value):
if not open_value:
if on_close:
on_close()

open_reactive = solara.use_reactive(open, on_open)
del open

Expand Down
44 changes: 20 additions & 24 deletions solara/website/pages/api/confirmation_dialog.py
Original file line number Diff line number Diff line change
Expand Up @@ -11,45 +11,41 @@

title = "ConfirmationDialog"
users = solara.reactive("Alice Bob Cindy Dirk Eve Fred".split())
user_to_be_deleted: solara.Reactive[Union[str, None]] = solara.reactive(users.value[0])
is_open = solara.reactive(False)
user_to_be_deleted: solara.Reactive[Union[str, None]] = solara.reactive(None)


def confirm_delete():
if user_to_be_deleted.value:
is_open.set(True)
def ask_to_delete_user(user):
user_to_be_deleted.value = user


def clear_user_to_be_deleted():
user_to_be_deleted.value = None


def delete_user():
if user_to_be_deleted.value:
users.set([u for u in users.value if u != user_to_be_deleted.value])
if users.value:
user_to_be_deleted.set(users.value[0])
else:
user_to_be_deleted.set(None)
users.set([u for u in users.value if u != user_to_be_deleted.value])
clear_user_to_be_deleted()


@solara.component
def Page():
"""Create a list of users, a dropdown to select one, and a button to delete the
selected user. A confirmation dialog will pop up first before deletion."""
"""Create a list of users with a button to delete them.
A confirmation dialog will pop up first before deletion."""
solara.Markdown("#### Users:")
with solara.Column():
with solara.Column(style={"max-width": "300px"}):
for user in users.value:
bgcolor = user.lower()[0] * 3
solara.Text(user, style=f"width: 300px; background-color: #{bgcolor}; padding: 10px;")
with solara.Row(style={"align-items": "center"}):
solara.Text(user)
solara.v.Spacer()
solara.Button(icon_name="mdi-delete", on_click=lambda user=user: ask_to_delete_user(user), icon=True)
if not users.value:
solara.Text("(no users left)")
solara.Select(label="User to be deleted:", value=user_to_be_deleted, values=users.value, style="max-width: 400px;")
solara.Button(
on_click=confirm_delete,
label=(f"Delete user: {user_to_be_deleted.value}" if user_to_be_deleted.value else "Delete user"),
style="max-width: 400px;",
disabled=not users.value,
)

with ConfirmationDialog(
is_open,
user_to_be_deleted.value is not None,
on_ok=delete_user,
on_close=clear_user_to_be_deleted,
ok="Ok, Delete",
title="Delete user",
):
Expand Down
18 changes: 9 additions & 9 deletions tests/unit/confirmation_dialog_test.py
Original file line number Diff line number Diff line change
Expand Up @@ -9,28 +9,28 @@
def test_confirmation_dialog_ok():
is_open = solara.reactive(True)
on_ok = MagicMock()
on_open = MagicMock()
el = ConfirmationDialog(is_open, on_ok=on_ok, on_open=on_open, content="Hello")
on_close = MagicMock()
el = ConfirmationDialog(is_open, on_ok=on_ok, on_close=on_close, content="Hello")
_, rc = solara.render(el, handle_error=False)
buttons = rc.find(vw.Btn)
assert len(buttons) == 2
buttons[1].widget.click()
assert on_ok.call_count == 1 # was OK button clicked?
assert on_open.call_count == 1 # always triggered
assert on_close.call_count == 1 # always triggered
assert not is_open.value # is dialog closed?


def test_confirmation_dialog_cancel():
is_open = solara.reactive(True)
on_ok = MagicMock()
on_open = MagicMock()
el = ConfirmationDialog(is_open, on_ok=on_ok, on_open=on_open, content="Hello")
on_close = MagicMock()
el = ConfirmationDialog(is_open, on_ok=on_ok, on_close=on_close, content="Hello")
_, rc = solara.render(el, handle_error=False)
buttons = rc.find(vw.Btn)
assert len(buttons) == 2
buttons[0].widget.click()
assert on_ok.call_count == 0 # on_ok action should not have been executed
assert on_open.call_count == 1 # always triggered
assert on_close.call_count == 1 # always triggered
assert not is_open.value # is dialog closed?


Expand All @@ -39,16 +39,16 @@ def test_confirm_external_close():
is_open = solara.reactive(True)
on_ok = MagicMock()
on_cancel = MagicMock()
on_open = MagicMock()
el = ConfirmationDialog(is_open, on_ok=on_ok, on_cancel=on_cancel, on_open=on_open, content="Hello")
on_close = MagicMock()
el = ConfirmationDialog(is_open, on_ok=on_ok, on_cancel=on_cancel, on_close=on_close, content="Hello")
_, rc = solara.render(el, handle_error=False)
dialog = rc.find(vw.Dialog)[0].widget
assert dialog.v_model
dialog.v_model = False # trigger an external close, like escape or clicking away
assert not is_open.value # is dialog closed?
assert on_ok.call_count == 0 # on_ok action should not have been executed
assert on_cancel.call_count == 1 # on_cancel action should not have been executed
assert on_open.call_count == 1 # always triggered
assert on_close.call_count == 1 # always triggered


def test_confirmation_dialog_custom_button_no_onclick():
Expand Down

0 comments on commit c1c0009

Please sign in to comment.