Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
14 changes: 12 additions & 2 deletions shiny/bookmark/_restore_state.py
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@
from contextlib import contextmanager
from contextvars import ContextVar, Token
from pathlib import Path
from typing import TYPE_CHECKING, Any, Literal, Optional
from typing import TYPE_CHECKING, Any, Literal, Optional, TypeVar, overload
from urllib.parse import parse_qs, parse_qsl

from .._docstring import add_example
Expand Down Expand Up @@ -387,8 +387,15 @@ def get_current_restore_context() -> RestoreContext | None:
return ctx


T = TypeVar("T")


@overload
def restore_input(resolved_id: ResolvedId, default: Any) -> Any: ...
@overload
def restore_input(resolved_id: None, default: T) -> T: ...
@add_example()
def restore_input(resolved_id: ResolvedId, default: Any) -> Any:
def restore_input(resolved_id: ResolvedId | None, default: Any) -> Any:
"""
Restore an input value

Expand All @@ -402,6 +409,9 @@ def restore_input(resolved_id: ResolvedId, default: Any) -> Any:
default
A default value to use, if there's no value to restore.
"""
if resolved_id is None:
return default

if not isinstance(resolved_id, ResolvedId):
raise TypeError(
"Expected `resolved_id` to be of type `ResolvedId` which is returned from `shiny.module.resolve_id(id)`."
Expand Down
2 changes: 2 additions & 0 deletions shiny/playwright/controller/__init__.py
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
from ._input_buttons import (
InputActionButton,
InputActionLink,
InputBookmarkButton,
InputDarkMode,
InputFile,
InputTaskButton,
Expand Down Expand Up @@ -77,6 +78,7 @@
__all__ = [
"InputActionButton",
"InputActionLink",
"InputBookmarkButton",
"InputCheckbox",
"InputCheckboxGroup",
"InputDarkMode",
Expand Down
41 changes: 40 additions & 1 deletion shiny/playwright/controller/_input_buttons.py
Original file line number Diff line number Diff line change
Expand Up @@ -47,7 +47,7 @@ def __init__(
super().__init__(
page,
id=id,
loc=f"button#{id}.action-button.shiny-bound-input",
loc=f'button[id="{id}"].action-button.shiny-bound-input',
)

def expect_disabled(self, value: bool, *, timeout: Timeout = None):
Expand All @@ -66,6 +66,45 @@ def expect_disabled(self, value: bool, *, timeout: Timeout = None):
)


class InputBookmarkButton(
InputActionButton,
):
"""Controller for :func:`shiny.ui.input_bookmark_button`."""

def __init__(
self,
page: Page,
id: str = "._bookmark_",
) -> None:
"""
Initializes the input bookmark button.

Parameters
----------
page
The page where the input bookmark button is located.
id
The id of the input bookmark button. Defaults to "._bookmark_".
"""
super().__init__(
page,
id=id,
)

def expect_disabled(self, value: bool, *, timeout: Timeout = None):
"""
Expect the input bookmark button to be disabled.

Parameters
----------
value
The expected value of the `disabled` attribute.
timeout
The maximum time to wait for the expectation to be fulfilled. Defaults to `None`.
"""
super().expect_disabled(value, timeout=timeout)


class InputDarkMode(UiBase):
"""Controller for :func:`shiny.ui.input_dark_mode`."""

Expand Down
14 changes: 14 additions & 0 deletions shiny/playwright/controller/_input_fields.py
Original file line number Diff line number Diff line change
Expand Up @@ -647,6 +647,20 @@ def expect_daysofweekdisabled(
timeout=timeout,
)

def set(self, value: str, *, timeout: Timeout = None) -> None:
"""
Sets the text value

Parameters
----------
value
The text to set.
timeout
The maximum time to wait for the text to be set. Defaults to `None`.
"""
set_text(self.loc, value, timeout=timeout)
self.loc.press("Enter", timeout=timeout)


class InputDate(_DateBase):
def __init__(self, page: Page, id: str) -> None:
Expand Down
14 changes: 9 additions & 5 deletions shiny/ui/_input_check_radio.py
Original file line number Diff line number Diff line change
Expand Up @@ -66,12 +66,13 @@ def input_checkbox(
* :func:`~shiny.ui.input_checkbox_group`
* :func:`~shiny.ui.input_radio_buttons`
"""

resolved_id = resolve_id(id)
value = restore_input(resolved_id, value)
return div(
div(
tags.label(
tags.input(
id=resolve_id(id),
id=resolved_id,
type="checkbox",
checked="checked" if value else None,
class_="shiny-input-checkbox",
Expand Down Expand Up @@ -142,11 +143,13 @@ def _bslib_input_checkbox(
*,
width: Optional[str] = None,
) -> Tag:
resolved_id = resolve_id(id)
value = restore_input(resolved_id, value)
return div(
div(
{"class": "form-check"},
tags.input(
id=resolve_id(id),
id=resolved_id,
class_="form-check-input",
type="checkbox",
role="switch",
Expand All @@ -157,7 +160,7 @@ def _bslib_input_checkbox(
# Must be wrapped in `span` for update_switch(label=) method to work
tags.span(label),
class_="form-check-label",
for_=resolve_id(id),
for_=resolved_id,
),
class_=class_,
),
Expand Down Expand Up @@ -218,11 +221,12 @@ def input_checkbox_group(

resolved_id = resolve_id(id)
input_label = shiny_input_label(resolved_id, label)

options = _generate_options(
id=resolved_id,
type="checkbox",
choices=choices,
selected=selected,
selected=restore_input(resolved_id, selected),
inline=inline,
)
return div(
Expand Down
15 changes: 7 additions & 8 deletions shiny/ui/_input_dark_mode.py
Original file line number Diff line number Diff line change
@@ -1,16 +1,17 @@
from __future__ import annotations

__all__ = ("input_dark_mode", "update_dark_mode")

from typing import Literal, Optional

from htmltools import Tag, TagAttrValue, css

from .._docstring import add_example, no_example
from ..module import resolve_id
from .._namespaces import resolve_id_or_none
from ..bookmark import restore_input
from ..session import Session, require_active_session
from ._web_component import web_component

__all__ = ("input_dark_mode", "update_dark_mode")

BootstrapColorMode = Literal["light", "dark"]


Expand Down Expand Up @@ -46,13 +47,11 @@ def input_dark_mode(
----------
* <https://getbootstrap.com/docs/5.3/customize/color-modes>
"""
resolved_id = resolve_id_or_none(id)

if mode is not None:
mode = validate_dark_mode_option(mode)

if id is not None:
id = resolve_id(id)

return web_component(
"bslib-input-dark-mode",
{
Expand All @@ -65,9 +64,9 @@ def input_dark_mode(
},
)
},
id=id,
id=resolved_id,
attribute="data-bs-theme",
mode=mode,
mode=restore_input(resolved_id, mode),
**kwargs,
)

Expand Down
16 changes: 11 additions & 5 deletions shiny/ui/_input_date.py
Original file line number Diff line number Diff line change
@@ -1,18 +1,19 @@
from __future__ import annotations

__all__ = ("input_date", "input_date_range")

import json
from datetime import date
from typing import Optional

from htmltools import Tag, TagAttrValue, TagChild, css, div, span, tags

from .._docstring import add_example
from ..bookmark import restore_input
from ..module import resolve_id
from ._html_deps_external import datepicker_deps
from ._utils import shiny_input_label

__all__ = ("input_date", "input_date_range")


@add_example()
def input_date(
Expand Down Expand Up @@ -111,11 +112,13 @@ def input_date(
"""

resolved_id = resolve_id(id)
default_value = value if value is not None else date.today()

return div(
shiny_input_label(resolved_id, label),
_date_input_tag(
id=resolved_id,
value=value,
value=restore_input(resolved_id, default_value),
min=min,
max=max,
format=format,
Expand Down Expand Up @@ -230,12 +233,15 @@ def input_date_range(
"""

resolved_id = resolve_id(id)
default_start = start if start is not None else date.today()
default_end = end if end is not None else date.today()
restored_date_range = restore_input(resolved_id, [default_start, default_end])
return div(
shiny_input_label(resolved_id, label),
div(
_date_input_tag(
id=resolved_id,
value=start,
value=restored_date_range[0],
min=min,
max=max,
format=format,
Expand All @@ -251,7 +257,7 @@ def input_date_range(
),
_date_input_tag(
id=resolved_id,
value=end,
value=restored_date_range[1],
min=min,
max=max,
format=format,
Expand Down
3 changes: 2 additions & 1 deletion shiny/ui/_input_numeric.py
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@
from htmltools import Tag, TagChild, css, div, tags

from .._docstring import add_example
from ..bookmark import restore_input
from ..module import resolve_id
from ._utils import shiny_input_label

Expand Down Expand Up @@ -69,7 +70,7 @@ def input_numeric(
id=resolved_id,
type="number",
class_="shiny-input-number form-control",
value=value,
value=restore_input(resolved_id, value),
min=min,
max=max,
step=step,
Expand Down
15 changes: 9 additions & 6 deletions shiny/ui/_input_select.py
Original file line number Diff line number Diff line change
Expand Up @@ -8,14 +8,14 @@
"input_select",
"input_selectize",
)

import copy
from json import dumps
from typing import Any, Mapping, Optional, Union, cast

from htmltools import Tag, TagChild, TagList, css, div, tags

from .._docstring import add_example
from ..bookmark import restore_input
from ..module import resolve_id
from ._html_deps_external import selectize_deps
from ._utils import JSEval, extract_js_keys, shiny_input_label
Expand Down Expand Up @@ -111,11 +111,12 @@ def input_selectize(
* :func:`~shiny.ui.input_radio_buttons`
* :func:`~shiny.ui.input_checkbox_group`
"""
resolved_id = resolve_id(id)

x = input_select(
id,
label,
choices,
id=resolved_id,
label=label,
choices=restore_input(resolved_id, choices),
selected=selected,
multiple=multiple,
selectize=True,
Expand Down Expand Up @@ -196,7 +197,11 @@ def input_select(

remove_button = _resolve_remove_button(remove_button, multiple)

resolved_id = resolve_id(id)

choices_ = _normalize_choices(choices)

selected = restore_input(resolved_id, selected)
if selected is None and not multiple:
selected = _find_first_option(choices_)

Expand All @@ -207,8 +212,6 @@ def input_select(

choices_tags = _render_choices(choices_, selected)

resolved_id = resolve_id(id)

return div(
shiny_input_label(resolved_id, label),
div(
Expand Down
6 changes: 4 additions & 2 deletions shiny/ui/_input_slider.py
Original file line number Diff line number Diff line change
@@ -1,5 +1,7 @@
from __future__ import annotations

from ..bookmark import restore_input

__all__ = (
"input_slider",
"SliderValueArg",
Expand Down Expand Up @@ -140,9 +142,10 @@ def input_slider(
* :func:`~shiny.ui.update_slider`
"""

resolved_id = resolve_id(id)
value = restore_input(resolved_id, value)
# Thanks to generic typing, max, value, etc. should be of the same type
data_type = _slider_type(min)

# Make sure min, max, value, and step are all numeric
# (converts dates/datetimes to milliseconds since epoch...this is the value JS wants)
min_num = _as_numeric(min)
Expand Down Expand Up @@ -201,7 +204,6 @@ def input_slider(
# ionRangeSlider wants attr = 'true'/'false'
props = {k: str(v).lower() if isinstance(v, bool) else v for k, v in props.items()}

resolved_id = resolve_id(id)
slider_tag = div(
shiny_input_label(resolved_id, label),
tags.input(**props),
Expand Down
Loading
Loading