From d6077415c0716582d34e45df30cab59d93768a0b Mon Sep 17 00:00:00 2001 From: Barret Schloerke Date: Fri, 27 Oct 2023 15:03:08 -0400 Subject: [PATCH 01/12] Adjust API to have: `layout_sidebar(sidebar, *args)` --- CHANGELOG.md | 2 +- shiny/ui/_page.py | 4 +-- shiny/ui/_sidebar.py | 66 ++++++++++++++++++++---------------------- shiny/ui/fill/_fill.py | 6 ++-- 4 files changed, 37 insertions(+), 41 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 4c9cc0ed5..e0a6367df 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -37,7 +37,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 * `shiny.ui.showcase_left_center()` and `shiny.ui.showcase_top_right()` no longer take two values for the `width` argument. Instead, they now take a single value (e.g., `width = "30%"`) representing the width of the showcase are in the value box. Furthermore, they've both gained `width_full_screen` arguments that determine the width of the showcase area when the value box is expanded to fill the screen. (#772) -* TODO-barret-API; `shiny.ui.panel_main()` and `shiny.ui.panel_sidebar()` are deprecated in favor of new API for `shiny.ui.layout_sidebar()`. Please use `shiny.ui.sidebar()` to construct a sidebar and supply it (along with the main content) to `shiny.ui.layout_sidebar(*args, **kwargs)`. (#680) +* `shiny.ui.panel_main()` and `shiny.ui.panel_sidebar()` are deprecated in favor of new API for `shiny.ui.layout_sidebar()`. Please use `shiny.ui.sidebar()` to construct a `sidebar=` and supply it to `shiny.ui.layout_sidebar(sidebar, *args, **kwargs)`. (#680) #### API relocations diff --git a/shiny/ui/_page.py b/shiny/ui/_page.py index 56f9ff970..477ac8853 100644 --- a/shiny/ui/_page.py +++ b/shiny/ui/_page.py @@ -38,9 +38,7 @@ def page_sidebar( - sidebar: Sidebar - | TagChild - | TagAttrs, # TODO-barret-API; Simlar to `layout_sidebar(*args: Sidebar | TagChild)`, should `*args` include `Sidebar` and have the sidebar retrieved from within the args? + sidebar: Sidebar, *args: TagChild | TagAttrs, title: Optional[str | Tag | TagList] = None, fillable: bool = True, diff --git a/shiny/ui/_sidebar.py b/shiny/ui/_sidebar.py index c9a51ce8a..b60d0c4f9 100644 --- a/shiny/ui/_sidebar.py +++ b/shiny/ui/_sidebar.py @@ -292,8 +292,9 @@ def sidebar( @add_example() -def layout_sidebar( # TODO-barret-API; Should this be `layout_sidebar(*args, sidebar: Optional[Sidebar] = None)`? - *args: Sidebar | TagChild | TagAttrs, +def layout_sidebar( + sidebar: Sidebar, + *args: TagChild | TagAttrs, fillable: bool = True, fill: bool = True, bg: Optional[str] = None, @@ -359,7 +360,7 @@ def layout_sidebar( # TODO-barret-API; Should this be `layout_sidebar(*args, si * :func:`~shiny.ui.sidebar` """ - args, sidebar = _get_layout_sidebar_sidebar(args) + sidebar, args = _get_layout_sidebar_sidebar(sidebar, args) # TODO-future; implement # if fg is None and bg is not None: @@ -426,57 +427,57 @@ def layout_sidebar( # TODO-barret-API; Should this be `layout_sidebar(*args, si def _get_layout_sidebar_sidebar( + sidebar: Sidebar, args: tuple[Sidebar | TagChild | TagAttrs, ...], -) -> tuple[tuple[TagChild | Sidebar | TagAttrs, ...], Sidebar]: +) -> tuple[Sidebar, tuple[TagChild | Sidebar | TagAttrs, ...]]: updated_args: list[Sidebar | TagChild | TagAttrs] = [] original_args = tuple(args) - sidebar: Sidebar | None = None + # sidebar: Sidebar | None = None sidebar_orig_arg: Sidebar | DeprecatedPanelSidebar | None = None + if isinstance(sidebar, DeprecatedPanelSidebar): + sidebar = sidebar.sidebar + # Use `original_args` here so `updated_args` can be safely altered in place for i, arg in zip(range(len(original_args)), original_args): if isinstance(arg, Sidebar): - if sidebar is not None: - raise ValueError( - "`layout_sidebar()` is being supplied with multiple `sidebar()` objects. Please supply only one `sidebar()` object to `layout_sidebar()`." - ) - sidebar = arg + raise ValueError( + "`layout_sidebar()` is being supplied with multiple `sidebar()` objects. Please supply only one `sidebar()` object to `layout_sidebar()`." + ) elif isinstance(arg, DeprecatedPanelSidebar): - if i != 0: - raise ValueError( - "`panel_sidebar()` is not being used as the first argument to `layout_sidebar()`. `panel_sidebar()` has been deprecated and will go away in a future version of Shiny. Please supply `panel_sidebar()` arguments directly to `args` in `layout_sidebar(*args)` and use `sidebar()` instead of `panel_sidebar()`." - ) - sidebar_orig_arg = arg - sidebar = arg.sidebar + raise ValueError( + "`panel_sidebar()` is not being used as the first argument to `layout_sidebar(sidebar,)`. `panel_sidebar()` has been deprecated and will go away in a future version of Shiny. Please supply `panel_sidebar()` arguments directly to `args` in `layout_sidebar(sidebar)` and use `sidebar()` instead of `panel_sidebar()`." + ) elif isinstance(arg, DeprecatedPanelMain): - if i != 1: + if i != 0: raise ValueError( - "`panel_main()` is not being supplied as the second argument to `layout_sidebar()`. `panel_main()`/`panel_sidebar()` have been deprecated and will go away in a future version of Shiny. Please supply `panel_main()` arguments directly to `args` in `layout_sidebar(*args)` and use `sidebar()` instead of `panel_sidebar()`." + "`panel_main()` is not being supplied as the second argument to `layout_sidebar()`. `panel_main()`/`panel_sidebar()` have been deprecated and will go away in a future version of Shiny. Please supply `panel_main()` arguments directly to `args` in `layout_sidebar(sidebar, *args)` and use `sidebar()` instead of `panel_sidebar()`." ) if not isinstance(sidebar_orig_arg, DeprecatedPanelSidebar): raise ValueError( - "`panel_main()` is not being used with `panel_sidebar()`. `panel_main()`/`panel_sidebar()` have been deprecated and will go away in a future version of Shiny. Please supply `panel_main()` arguments directly to `args` in `layout_sidebar(*args)` and use `sidebar()` instead of `panel_sidebar()`." + "`panel_main()` is not being used with `panel_sidebar()`. `panel_main()`/`panel_sidebar()` have been deprecated and will go away in a future version of Shiny. Please supply `panel_main()` arguments directly to `args` in `layout_sidebar(sidebar, *args)` and use `sidebar()` instead of `panel_sidebar()`." ) - if len(args) > 3: + if len(args) > 2: raise ValueError( - "Unexpected extra legacy `*args` have been supplied to `layout_sidebar()` in addition to `panel_main()` or `panel_sidebar()`. `panel_main()` has been deprecated and will go away in a future version of Shiny. Please supply `panel_main()` arguments directly to `args` in `layout_sidebar(*args)` and use `sidebar()` instead of `panel_sidebar()`." + "Unexpected extra legacy `*args` have been supplied to `layout_sidebar()` in addition to `panel_main()` or `panel_sidebar()`. `panel_main()` has been deprecated and will go away in a future version of Shiny. Please supply `panel_main()` arguments directly to `args` in `layout_sidebar(sidebar, *args)` and use `sidebar()` instead of `panel_sidebar()`." ) # Notes for this point in the code: - # * We are working with args[1], a `DeprecatedPanelMain`; args[0] is `DeprecatedPanelSidebar` - # * len(args) == 2 or 3 + # * We are working with args[0], a `DeprecatedPanelMain`; sidebar was originally a `DeprecatedPanelSidebar` + # * len(args) == 1 or 2 # Handle legacy `layout_sidebar(sidebar, main, position=)` value - if len(args) == 3: - arg2 = args[2] - if not (arg2 == "left" or arg2 == "right"): + if len(args) == 2: + arg1 = args[1] + if not (arg1 == "left" or arg1 == "right"): raise ValueError( - "layout_sidebar(*args) contains non-valid legacy values. Please use `sidebar()` instead of `panel_sidebar()` and supply any `panel_main()` arguments directly to `args` in `layout_sidebar(*args)`." + "layout_sidebar(*args) contains non-valid legacy values. Please use `sidebar()` instead of `panel_sidebar()` and supply any `panel_main()` arguments directly to `args` in `layout_sidebar(sidebar, *args)`." ) # We know `sidebar_orig_arg` is a `DeprecatedPanelSidebar` here sidebar.position = cast( # pyright: ignore[reportOptionalMemberAccess] - Literal["left", "right"], arg2 + Literal["left", "right"], + arg1, ) # Only keep panel_main content @@ -490,12 +491,7 @@ def _get_layout_sidebar_sidebar( # Keep the arg! updated_args.append(arg) - if sidebar is None: - raise ValueError( - "`layout_sidebar()` did not receive a `sidebar()` object. To use `layout_sidebar()`, please supply a `sidebar()` object." - ) - - return (tuple(updated_args), sidebar) + return (sidebar, tuple(updated_args)) @add_example() @@ -618,7 +614,7 @@ def panel_main( # Deprecated 2023-06-13 -class DeprecatedPanelSidebar: +class DeprecatedPanelSidebar(Sidebar): """ [Deprecated] Sidebar panel diff --git a/shiny/ui/fill/_fill.py b/shiny/ui/fill/_fill.py index 024062d1f..080690c8d 100644 --- a/shiny/ui/fill/_fill.py +++ b/shiny/ui/fill/_fill.py @@ -31,7 +31,8 @@ @add_example() -def as_fillable_container( # TODO-barret-API; These methods mutate their input values. Should they return a new object instead? Or not return at all (like `LIST.sort()`)? +# TODO-future-API; These methods mutate their input values. Should they return a new object instead? Or not return at all (like `LIST.sort()`)? +def as_fillable_container( tag: TagT, ) -> TagT: tag_prepend_class(tag, FILL_CONTAINER_CLASS) @@ -40,7 +41,8 @@ def as_fillable_container( # TODO-barret-API; These methods mutate their input @add_example() -def as_fill_item( # TODO-barret-API; These methods mutate their input values. Should they return a new object instead? Or not return at all (like `LIST.sort()`)? +# TODO-future-API; These methods mutate their input values. Should they return a new object instead? Or not return at all (like `LIST.sort()`)? +def as_fill_item( tag: TagT, ) -> TagT: """ From bcb484e258974fbce4f17539cfecb40c548c4c64 Mon Sep 17 00:00:00 2001 From: Barret Schloerke Date: Fri, 27 Oct 2023 20:49:09 -0400 Subject: [PATCH 02/12] Require that `page_sidebar(sidebar) and `layout_sidebar(sidebar)` take a `Sidebar` class as first parameter --- CHANGELOG.md | 4 +--- shiny/experimental/ui/_deprecated.py | 6 ++++- shiny/ui/_card.py | 2 +- shiny/ui/_layout.py | 1 - shiny/ui/_navs.py | 20 ++++------------ shiny/ui/_page.py | 4 +--- shiny/ui/_sidebar.py | 36 +++++++++++++++++++--------- tests/pytest/test_sidebar.py | 17 ++----------- 8 files changed, 40 insertions(+), 50 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index e0a6367df..1ed61370c 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -35,9 +35,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 * Added `shiny.ui.navset_underline()` and `shiny.ui.navset_card_underline()` whose navigation container is similar to `shiny.ui.navset_tab()` and `shiny.ui.navset_card_tab()` respectively, but its active/focused navigation links are styled with an underline. (#772) * `shiny.ui.layout_column_wrap(width, *args)` was rearranged to `shiny.ui.layout_column_wrap(*args, width)`. Now, `width` will default to `200px` is no value is provided. (#772) * `shiny.ui.showcase_left_center()` and `shiny.ui.showcase_top_right()` no longer take two values for the `width` argument. Instead, they now take a single value (e.g., `width = "30%"`) representing the width of the showcase are in the value box. Furthermore, they've both gained `width_full_screen` arguments that determine the width of the showcase area when the value box is expanded to fill the screen. (#772) - - -* `shiny.ui.panel_main()` and `shiny.ui.panel_sidebar()` are deprecated in favor of new API for `shiny.ui.layout_sidebar()`. Please use `shiny.ui.sidebar()` to construct a `sidebar=` and supply it to `shiny.ui.layout_sidebar(sidebar, *args, **kwargs)`. (#680) +* `shiny.ui.panel_main()` and `shiny.ui.panel_sidebar()` are deprecated in favor of new API for `shiny.ui.layout_sidebar()`. Please use `shiny.ui.sidebar()` to construct a `sidebar=` and supply it to `shiny.ui.layout_sidebar(sidebar, *args, **kwargs)`. (#788) #### API relocations diff --git a/shiny/experimental/ui/_deprecated.py b/shiny/experimental/ui/_deprecated.py index 677895978..ecae08ae3 100644 --- a/shiny/experimental/ui/_deprecated.py +++ b/shiny/experimental/ui/_deprecated.py @@ -455,7 +455,7 @@ def sidebar( def layout_sidebar( - sidebar: Sidebar | TagChild | TagAttrs, + sidebar: MainSidebar | TagChild | TagAttrs, *args: TagChild | TagAttrs, fillable: bool = True, fill: bool = True, @@ -475,6 +475,8 @@ def layout_sidebar( "This method will be removed in a future version, " "please use `shiny.ui.layout_sidebar()` instead." ) + if not isinstance(sidebar, MainSidebar): + sidebar = main_sidebar(sidebar) return main_layout_sidebar( sidebar, *args, @@ -1387,6 +1389,8 @@ def page_sidebar( "This method will be removed in a future version, " "please use `shiny.ui.page_sidebar()` instead." ) + if not isinstance(sidebar, MainSidebar): + sidebar = main_sidebar(sidebar) return main_page_sidebar( sidebar, *args, diff --git a/shiny/ui/_card.py b/shiny/ui/_card.py index bfcf6aa26..aeaba298a 100644 --- a/shiny/ui/_card.py +++ b/shiny/ui/_card.py @@ -120,7 +120,7 @@ def _card_impl( Experimental implements the parameter `wrapper=`. Main does not as `card_body()` is not in `main`. """ - # TODO-barret-API: Should card_body() ever exist in main? + # TODO-question Should card_body() ever exist in main? # Barret: Yes? Need to customize fill/fillable content (ex: Fixed height for item B where content = A, B, C; fill = TRUE, fillable = TRUE). if isinstance(wrapper, MISSING_TYPE): wrapper = card_body diff --git a/shiny/ui/_layout.py b/shiny/ui/_layout.py index 73cec22d8..57f422ec4 100644 --- a/shiny/ui/_layout.py +++ b/shiny/ui/_layout.py @@ -14,7 +14,6 @@ from .fill import as_fill_item, as_fillable_container -# TODO-barret-API; Move `width` to after `args` def layout_column_wrap( *args: TagChild | TagAttrs, width: CssUnit | None | MISSING_TYPE = MISSING, diff --git a/shiny/ui/_navs.py b/shiny/ui/_navs.py index 19b282276..f57338d76 100644 --- a/shiny/ui/_navs.py +++ b/shiny/ui/_navs.py @@ -683,9 +683,7 @@ def navset_card_tab( id: Optional[str] = None, selected: Optional[str] = None, title: Optional[TagChild] = None, - sidebar: Optional[ - Sidebar - ] = None, # TODO-barret-API; Simlar to `layout_sidebar(*args: Sidebar | TagChild)`, should `*args` be of type `NavSetArg | Sidebar` and have the sidebar retrieved from within the args? + sidebar: Optional[Sidebar] = None, header: TagChild = None, footer: TagChild = None, ) -> NavSetCard: @@ -764,9 +762,7 @@ def navset_card_pill( id: Optional[str] = None, selected: Optional[str] = None, title: Optional[TagChild] = None, - sidebar: Optional[ - Sidebar - ] = None, # TODO-barret-API; Simlar to `layout_sidebar(*args: Sidebar | TagChild)`, should `*args` include `Sidebar` and have the sidebar retrieved from within the args? + sidebar: Optional[Sidebar] = None, header: TagChild = None, footer: TagChild = None, placement: Literal["above", "below"] = "above", @@ -831,9 +827,7 @@ def navset_card_underline( id: Optional[str] = None, selected: Optional[str] = None, title: Optional[TagChild] = None, - sidebar: Optional[ - Sidebar - ] = None, # TODO-barret-API; Simlar to `layout_sidebar(*args: Sidebar | TagChild)`, should `*args` include `Sidebar` and have the sidebar retrieved from within the args? + sidebar: Optional[Sidebar] = None, header: TagChild = None, footer: TagChild = None, placement: Literal["above", "below"] = "above", @@ -1009,9 +1003,7 @@ def __init__( title: TagChild, id: Optional[str], selected: Optional[str], - sidebar: Optional[ - Sidebar - ] = None, # TODO-barret-API; Simlar to `layout_sidebar(*args: Sidebar | TagChild)`, should `*args` be of type `NavSetArg | Sidebar` and have the sidebar retrieved from within the args? + sidebar: Optional[Sidebar] = None, fillable: bool | list[str] = False, gap: Optional[CssUnit], padding: Optional[CssUnit | list[CssUnit]], @@ -1166,9 +1158,7 @@ def navset_bar( title: TagChild, id: Optional[str] = None, selected: Optional[str] = None, - sidebar: Optional[ - Sidebar - ] = None, # TODO-barret-API; Simlar to `layout_sidebar(*args: Sidebar | TagChild)`, should `*args` include `Sidebar` and have the sidebar retrieved from within the args? + sidebar: Optional[Sidebar] = None, fillable: bool | list[str] = True, gap: Optional[CssUnit] = None, padding: Optional[CssUnit | list[CssUnit]] = None, diff --git a/shiny/ui/_page.py b/shiny/ui/_page.py index 477ac8853..24a16ee7b 100644 --- a/shiny/ui/_page.py +++ b/shiny/ui/_page.py @@ -108,9 +108,7 @@ def page_navbar( title: Optional[str | Tag | TagList] = None, id: Optional[str] = None, selected: Optional[str] = None, - sidebar: Optional[ - Sidebar - ] = None, # TODO-barret-API; Simlar to `layout_sidebar(*args: Sidebar | TagChild)`, should `*args` include `Sidebar` and have the sidebar retrieved from within the args? + sidebar: Optional[Sidebar] = None, # Only page_navbar gets enhanced treatement for `fillable` # If an `*args`'s `data-value` attr string is in `fillable`, then the component is fillable fillable: bool | list[str] = True, diff --git a/shiny/ui/_sidebar.py b/shiny/ui/_sidebar.py index b60d0c4f9..66f4bdbf3 100644 --- a/shiny/ui/_sidebar.py +++ b/shiny/ui/_sidebar.py @@ -35,8 +35,6 @@ # Legacy "panel_sidebar", "panel_main", - "DeprecatedPanelSidebar", - "DeprecatedPanelMain", ) @@ -428,27 +426,33 @@ def layout_sidebar( def _get_layout_sidebar_sidebar( sidebar: Sidebar, - args: tuple[Sidebar | TagChild | TagAttrs, ...], -) -> tuple[Sidebar, tuple[TagChild | Sidebar | TagAttrs, ...]]: - updated_args: list[Sidebar | TagChild | TagAttrs] = [] + args: tuple[TagChild | TagAttrs, ...], +) -> tuple[Sidebar, tuple[TagChild | TagAttrs, ...]]: + updated_args: list[TagChild | TagAttrs] = [] original_args = tuple(args) # sidebar: Sidebar | None = None - sidebar_orig_arg: Sidebar | DeprecatedPanelSidebar | None = None + sidebar_orig_arg: Sidebar | DeprecatedPanelSidebar = sidebar if isinstance(sidebar, DeprecatedPanelSidebar): sidebar = sidebar.sidebar + if not isinstance(sidebar, Sidebar): + raise ValueError( + "`layout_sidebar()` is not being supplied with a `sidebar()` object. Please supply a `sidebar()` object to `layout_sidebar(sidebar)`." + ) + # Use `original_args` here so `updated_args` can be safely altered in place for i, arg in zip(range(len(original_args)), original_args): - if isinstance(arg, Sidebar): + if isinstance(arg, DeprecatedPanelSidebar): raise ValueError( - "`layout_sidebar()` is being supplied with multiple `sidebar()` objects. Please supply only one `sidebar()` object to `layout_sidebar()`." + "`panel_sidebar()` is not being used as the first argument to `layout_sidebar(sidebar,)`. `panel_sidebar()` has been deprecated and will go away in a future version of Shiny. Please supply `panel_sidebar()` arguments directly to `args` in `layout_sidebar(sidebar)` and use `sidebar()` instead of `panel_sidebar()`." ) - elif isinstance(arg, DeprecatedPanelSidebar): + elif isinstance(arg, Sidebar): raise ValueError( - "`panel_sidebar()` is not being used as the first argument to `layout_sidebar(sidebar,)`. `panel_sidebar()` has been deprecated and will go away in a future version of Shiny. Please supply `panel_sidebar()` arguments directly to `args` in `layout_sidebar(sidebar)` and use `sidebar()` instead of `panel_sidebar()`." + "`layout_sidebar()` is being supplied with multiple `sidebar()` objects. Please supply only one `sidebar()` object to `layout_sidebar()`." ) + elif isinstance(arg, DeprecatedPanelMain): if i != 0: raise ValueError( @@ -614,7 +618,15 @@ def panel_main( # Deprecated 2023-06-13 -class DeprecatedPanelSidebar(Sidebar): + + +# This class should be removed when `panel_sidebar()` is removed +class DeprecatedPanelSidebar( + # While it doesn't seem right to inherit from `Sidebar`, it's the easiest way to + # make sure `layout_sidebar(sidebar: Sidebar)` works without mucking up the + # function signature. + Sidebar +): """ [Deprecated] Sidebar panel @@ -665,6 +677,8 @@ def tagify(self) -> Tag: return self.sidebar.tag.tagify() +# This class should be removed when `panel_main()` is removed +# Must be `Tagifiable`, so it can fit as a type `TagChild` class DeprecatedPanelMain: """ [Deprecated] Main panel diff --git a/tests/pytest/test_sidebar.py b/tests/pytest/test_sidebar.py index 1b38d31c0..0f728019f 100644 --- a/tests/pytest/test_sidebar.py +++ b/tests/pytest/test_sidebar.py @@ -16,8 +16,6 @@ def test_panel_main_and_panel_sidebar(): # OK ui.layout_sidebar(_s) ui.layout_sidebar(_s, None) - ui.layout_sidebar(None, _s) - ui.layout_sidebar(None, _s, None) try: ui.layout_sidebar(_s, _s) @@ -26,10 +24,10 @@ def test_panel_main_and_panel_sidebar(): assert "multiple `sidebar()` objects" in str(e) try: - ui.layout_sidebar(None, _ps) + ui.layout_sidebar(None, _ps) # pyright: ignore[reportGeneralTypeIssues] raise AssertionError("Should have raised ValueError") except ValueError as e: - assert "not being used as the first argument" in str(e) + assert "not being supplied with a `sidebar()` object." in str(e) try: ui.layout_sidebar(_s, _pm) @@ -37,11 +35,6 @@ def test_panel_main_and_panel_sidebar(): except ValueError as e: assert "is not being used with `panel_sidebar()`" in str(e) - try: - ui.layout_sidebar(_pm) - raise AssertionError("Should have raised ValueError") - except ValueError as e: - assert "not being supplied as the second argument" in str(e) try: ui.layout_sidebar(_ps, None, _pm) raise AssertionError("Should have raised ValueError") @@ -52,9 +45,3 @@ def test_panel_main_and_panel_sidebar(): raise AssertionError("Should have raised ValueError") except ValueError as e: assert "Unexpected extra legacy `*args`" in str(e) - - try: - ui.layout_sidebar(_m) - raise AssertionError("Should have raised ValueError") - except ValueError as e: - assert "did not receive a `sidebar()`" in str(e) From ecad6af9dff684a39b768f3b6bca688398da27c5 Mon Sep 17 00:00:00 2001 From: Barret Schloerke Date: Fri, 27 Oct 2023 20:59:35 -0400 Subject: [PATCH 03/12] Defunct `is_fill*()` methods --- CHANGELOG.md | 4 ++-- docs/_quartodoc.yml | 4 ++-- shiny/experimental/ui/_deprecated.py | 22 +++++++++++----------- shiny/ui/fill/__init__.py | 8 ++++---- shiny/ui/fill/_fill.py | 17 ++++++----------- 5 files changed, 25 insertions(+), 30 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 1ed61370c..18ad4056a 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -46,7 +46,7 @@ The following methods have been moved from `shiny.experimental.ui` and integrate * Sidebar - Sidebar layout or manipulation * `sidebar()`, `page_sidebar()`, `toggle_sidebar()`, `layout_sidebar()`, `Sidebar` * Filling layout - Allow UI components to expand into the parent container and/or allow its content to expand - * `page_fillable()`, `fill.as_fillable_container()`, `fill.as_fill_item()`, `fill.is_fillable_container()`, `fill.is_fill_item()`, `fill.remove_all_fill()` + * `page_fillable()`, `fill.as_fillable_container()`, `fill.as_fill_item()`, `fill.remove_all_fill()` * `output_plot(fill=)`, `output_image(fill=)`, `output_ui(fill=, fillable=)` * CSS units - CSS units and padding * `css.as_css_unit()`, `css.as_css_padding()`, `css.CssUnit` @@ -83,9 +83,9 @@ Methods still under consideration in `shiny.experimental.ui`: * `shiny.experimental.ui.FillingLayout` has been removed. (#481) * `shiny.experimental.ui.as_width_unit()` has been made defunct. Please remove it from your code. (#772) +* `shiny.experimental.ui`' `as_fill_carrier()`, `is_fill_carrier()`, `is_fillable_container()`, and `is_fill_item()` have been made defunct. Remove them from your code. (#680, #788) * Support for `min_height=`, `max_height=`, and `gap=` in `shiny.experimental.ui.as_fillable_container()` and `as_fill_item()` has been removed. (#481) * `shiny.experimental.ui.TagCallable` has been deprecated. Its type is equivalent to `htmltools.TagFunction`. (#680) -* `shiny.experimental.ui.as_fill_carrier()` and `shiny.experimental.ui.is_fill_carrier()` have been deprecated. Please use `shiny.ui.fill.as_fill_item()` and `shiny.ui.fill.as_fillable_container()` or `shiny.ui.fill.is_fill_item()` and `shiny.ui.fill.is_fillable_container()` respectively in combination to achieve similar behavior. (#680) ### Bug fixes diff --git a/docs/_quartodoc.yml b/docs/_quartodoc.yml index d6433cdf6..67f19f989 100644 --- a/docs/_quartodoc.yml +++ b/docs/_quartodoc.yml @@ -106,8 +106,8 @@ quartodoc: - ui.fill.as_fillable_container - ui.fill.as_fill_item - ui.fill.remove_all_fill - - ui.fill.is_fillable_container - - ui.fill.is_fill_item + # - ui.fill.is_fillable_container + # - ui.fill.is_fill_item - ui.css.as_css_unit - ui.css.as_css_padding - title: Update inputs diff --git a/shiny/experimental/ui/_deprecated.py b/shiny/experimental/ui/_deprecated.py index ecae08ae3..beeb46804 100644 --- a/shiny/experimental/ui/_deprecated.py +++ b/shiny/experimental/ui/_deprecated.py @@ -69,9 +69,9 @@ from ...ui.css._css_unit import as_css_unit as main_as_css_unit from ...ui.fill import as_fill_item as main_as_fill_item from ...ui.fill import as_fillable_container as main_as_fillable_container -from ...ui.fill import is_fill_item as main_is_fill_item -from ...ui.fill import is_fillable_container as main_is_fillable_container from ...ui.fill import remove_all_fill as main_remove_all_fill +from ...ui.fill._fill import is_fill_item as main_is_fill_item +from ...ui.fill._fill import is_fillable_container as main_is_fillable_container __all__ = ( # Input Switch @@ -978,31 +978,31 @@ def remove_all_fill(tag: TagT) -> TagT: def is_fill_carrier(tag: Tag) -> bool: - """Deprecated. Please use a combination of `shiny.ui.fill.is_fillable_container()` and `shiny.ui.fill.is_fill_item()` instead.""" + """Defunct. Please do not use method.""" warn_deprecated( - "`shiny.experimental.ui.is_fill_carrier()` is deprecated. " + "`shiny.experimental.ui.is_fill_carrier()` is defunct. " "This method will be removed in a future version, " - "please use a combination of `shiny.ui.fill.is_fillable_container()` and `shiny.ui.fill.is_fill_item()` instead." + "please update your code accordingly." ) return main_is_fill_item(main_is_fillable_container(tag)) def is_fillable_container(tag: TagChild) -> bool: - """Deprecated. Please use `shiny.ui.fill.is_fillable_container()` instead.""" + """Defunct. Please do not use method.""" warn_deprecated( - "`shiny.experimental.ui.is_fillable_container()` is deprecated. " + "`shiny.experimental.ui.is_fillable_container()` is defunct. " "This method will be removed in a future version, " - "please use `shiny.ui.fill.is_fillable_container()` instead." + "please update your code accordingly." ) return main_is_fillable_container(tag) def is_fill_item(tag: TagChild) -> bool: - """Deprecated. Please use `shiny.ui.fill.is_fill_item()` instead.""" + """Defunct. Please do not use method.""" warn_deprecated( - "`shiny.experimental.ui.is_fill_item()` is deprecated. " + "`shiny.experimental.ui.is_fill_item()` is defunct. " "This method will be removed in a future version, " - "please use `shiny.ui.fill.is_fill_item()` instead." + "please update your code accordingly." ) return main_is_fill_item(tag) diff --git a/shiny/ui/fill/__init__.py b/shiny/ui/fill/__init__.py index ad75ff274..53551d7d9 100644 --- a/shiny/ui/fill/__init__.py +++ b/shiny/ui/fill/__init__.py @@ -2,14 +2,14 @@ as_fill_item, as_fillable_container, remove_all_fill, - is_fill_item, - is_fillable_container, + # is_fill_item, + # is_fillable_container, ) __all__ = ( "as_fillable_container", "as_fill_item", "remove_all_fill", - "is_fillable_container", - "is_fill_item", + # "is_fillable_container", + # "is_fill_item", ) diff --git a/shiny/ui/fill/_fill.py b/shiny/ui/fill/_fill.py index 080690c8d..87f58bd37 100644 --- a/shiny/ui/fill/_fill.py +++ b/shiny/ui/fill/_fill.py @@ -12,8 +12,8 @@ "as_fillable_container", "as_fill_item", "remove_all_fill", - "is_fill_item", - "is_fillable_container", + # "is_fill_item", + # "is_fillable_container", ) TagT = TypeVar("TagT", bound="Tag") @@ -71,8 +71,6 @@ def as_fill_item( -------- * :func:`~shiny.ui.fill.as_fillable_container` * :func:`~shiny.ui.fill.remove_all_fill` - * :func:`~shiny.ui.fill.is_fill_item` - * :func:`~shiny.ui.fill.is_fillable_container` """ tag_prepend_class(tag, FILL_ITEM_CLASS) tag.append(fill_dependency()) @@ -109,8 +107,6 @@ def remove_all_fill( -------- * :func:`~shiny.ui.fill.as_fill_item` * :func:`~shiny.ui.fill.as_fillable_container` - * :func:`~shiny.ui.fill.is_fill_item` - * :func:`~shiny.ui.fill.is_fillable_container` """ tag_remove_class(tag, FILL_CONTAINER_CLASS) @@ -118,7 +114,8 @@ def remove_all_fill( return tag -def is_fillable_container( # TODO-barret-api; Should this be exported? +# Method currently not exposed, but implemented within bslib +def is_fillable_container( tag: object, ) -> bool: """ @@ -148,8 +145,6 @@ def is_fillable_container( # TODO-barret-api; Should this be exported? * :func:`~shiny.ui.fill.as_fill_item` * :func:`~shiny.ui.fill.as_fillable_container` * :func:`~shiny.ui.fill.remove_all_fill` - * :func:`~shiny.ui.fill.is_fill_item` - * :func:`~shiny.ui.fill.is_fillable_container` """ # TODO-future; Handle widgets # # won't actually work until (htmltools#334) gets fixed @@ -158,7 +153,8 @@ def is_fillable_container( # TODO-barret-api; Should this be exported? return isinstance(tag, Tag) and tag.has_class(FILL_CONTAINER_CLASS) -def is_fill_item(tag: object) -> bool: # TODO-barret-api; Should this be exported? +# Method currently not exposed, but implemented within bslib +def is_fill_item(tag: object) -> bool: """ Test a tag for being a fill item @@ -185,7 +181,6 @@ def is_fill_item(tag: object) -> bool: # TODO-barret-api; Should this be export * :func:`~shiny.ui.fill.as_fill_item` * :func:`~shiny.ui.fill.as_fillable_container` * :func:`~shiny.ui.fill.remove_all_fill` - * :func:`~shiny.ui.fill.is_fillable_container` """ # TODO-future; Handle widgets # # won't actually work until (htmltools#334) gets fixed From 7310fef6044a511e3ecdb1134f65d0d1f73dca78 Mon Sep 17 00:00:00 2001 From: Barret Schloerke Date: Fri, 27 Oct 2023 21:31:12 -0400 Subject: [PATCH 04/12] Defunct `X.toggle_switch()`; Rename `toggle_sidebar()` to `update_sidebar(open: bool)` --- CHANGELOG.md | 7 +-- docs/_quartodoc.yml | 3 +- shiny/api-examples/toggle_sidebar/app.py | 7 +-- shiny/api-examples/toggle_switch/app.py | 26 ----------- shiny/experimental/ui/_deprecated.py | 45 +++++++++++------- shiny/ui/__init__.py | 7 ++- shiny/ui/_input_toggle.py | 32 ------------- shiny/ui/_sidebar.py | 59 +++++++++++------------- tests/e2e/TODO/sidebar/app.py | 12 ++--- tests/e2e/bugs/0696-resolve-id/app.py | 6 +-- 10 files changed, 77 insertions(+), 127 deletions(-) delete mode 100644 shiny/api-examples/toggle_switch/app.py diff --git a/CHANGELOG.md b/CHANGELOG.md index 18ad4056a..2b0440583 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -36,6 +36,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 * `shiny.ui.layout_column_wrap(width, *args)` was rearranged to `shiny.ui.layout_column_wrap(*args, width)`. Now, `width` will default to `200px` is no value is provided. (#772) * `shiny.ui.showcase_left_center()` and `shiny.ui.showcase_top_right()` no longer take two values for the `width` argument. Instead, they now take a single value (e.g., `width = "30%"`) representing the width of the showcase are in the value box. Furthermore, they've both gained `width_full_screen` arguments that determine the width of the showcase area when the value box is expanded to fill the screen. (#772) * `shiny.ui.panel_main()` and `shiny.ui.panel_sidebar()` are deprecated in favor of new API for `shiny.ui.layout_sidebar()`. Please use `shiny.ui.sidebar()` to construct a `sidebar=` and supply it to `shiny.ui.layout_sidebar(sidebar, *args, **kwargs)`. (#788) +* `shiny.experimental.ui.toggle_sidebar()` has been renamed to `shiny.ui.update_sidebar()`. It's `open` value now only supports `bool` values. (#788) #### API relocations @@ -44,7 +45,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 The following methods have been moved from `shiny.experimental.ui` and integrated into `shiny.ui` (final locations under `shiny.ui` are displayed) (#680): * Sidebar - Sidebar layout or manipulation - * `sidebar()`, `page_sidebar()`, `toggle_sidebar()`, `layout_sidebar()`, `Sidebar` + * `sidebar()`, `page_sidebar()`, `update_sidebar()`, `layout_sidebar()`, `Sidebar` * Filling layout - Allow UI components to expand into the parent container and/or allow its content to expand * `page_fillable()`, `fill.as_fillable_container()`, `fill.as_fill_item()`, `fill.remove_all_fill()` * `output_plot(fill=)`, `output_image(fill=)`, `output_ui(fill=, fillable=)` @@ -68,7 +69,6 @@ The following methods have been moved from `shiny.experimental.ui` and integrate * Layout - Layout of UI elements * `layout_column_wrap()` * Inputs - UI elements for user input - * `toggle_switch()` * `input_text_area(autoresize=)` If a ported method is called from `shiny.experimental.ui`, a deprecation warning will be displayed. @@ -82,10 +82,11 @@ Methods still under consideration in `shiny.experimental.ui`: #### API removals * `shiny.experimental.ui.FillingLayout` has been removed. (#481) +* `shiny.experimental.ui.toggle_switch()` has been made defunct. Please remove it from your code and use `shiny.ui.update_switch()` instead. (#772) * `shiny.experimental.ui.as_width_unit()` has been made defunct. Please remove it from your code. (#772) * `shiny.experimental.ui`' `as_fill_carrier()`, `is_fill_carrier()`, `is_fillable_container()`, and `is_fill_item()` have been made defunct. Remove them from your code. (#680, #788) * Support for `min_height=`, `max_height=`, and `gap=` in `shiny.experimental.ui.as_fillable_container()` and `as_fill_item()` has been removed. (#481) -* `shiny.experimental.ui.TagCallable` has been deprecated. Its type is equivalent to `htmltools.TagFunction`. (#680) +* `shiny.experimental.ui.TagCallable` has been made defunct. Please use its type is equivalent to `htmltools.TagFunction`. (#680) ### Bug fixes diff --git a/docs/_quartodoc.yml b/docs/_quartodoc.yml index 67f19f989..93e7d03d2 100644 --- a/docs/_quartodoc.yml +++ b/docs/_quartodoc.yml @@ -140,8 +140,7 @@ quartodoc: - title: Update UI Layouts desc: "" contents: - - ui.toggle_sidebar - - ui.toggle_switch + - ui.update_sidebar - ui.toggle_tooltip - ui.toggle_popover - ui.update_tooltip diff --git a/shiny/api-examples/toggle_sidebar/app.py b/shiny/api-examples/toggle_sidebar/app.py index 9df3da115..06601b36f 100644 --- a/shiny/api-examples/toggle_sidebar/app.py +++ b/shiny/api-examples/toggle_sidebar/app.py @@ -3,7 +3,7 @@ app_ui = ui.page_sidebar( ui.sidebar("Sidebar content", id="sidebar"), ui.input_action_button( - "toggle_sidebar", + "toggle_sidebar_btn", label="Toggle sidebar", width="fit-content", ), @@ -13,9 +13,10 @@ def server(input: Inputs, output: Outputs, session: Session): @reactive.Effect - @reactive.event(input.toggle_sidebar) + @reactive.event(input.toggle_sidebar_btn) def _(): - ui.toggle_sidebar("sidebar") + sidebar_is_open = input.sidebar() + ui.update_sidebar("sidebar", open=not sidebar_is_open) @output @render.text diff --git a/shiny/api-examples/toggle_switch/app.py b/shiny/api-examples/toggle_switch/app.py deleted file mode 100644 index 754082133..000000000 --- a/shiny/api-examples/toggle_switch/app.py +++ /dev/null @@ -1,26 +0,0 @@ -from shiny import App, Inputs, Outputs, Session, reactive, render, ui - -app_ui = ui.page_fluid( - ui.input_switch("switch_value", label="Switch"), - ui.input_action_button( - "toggle_btn", - label="Toggle the switch", - width="fit-content", - ), - ui.output_text_verbatim("state"), -) - - -def server(input: Inputs, output: Outputs, session: Session): - @reactive.Effect - @reactive.event(input.toggle_btn) - def _(): - ui.toggle_switch("switch_value") - - @output - @render.text - def state(): - return f"input.switch(): {input.switch_value()}" - - -app = App(app_ui, server=server) diff --git a/shiny/experimental/ui/_deprecated.py b/shiny/experimental/ui/_deprecated.py index beeb46804..08440d6b6 100644 --- a/shiny/experimental/ui/_deprecated.py +++ b/shiny/experimental/ui/_deprecated.py @@ -13,7 +13,9 @@ ) from ..._deprecated import warn_deprecated -from ...session import Session +from ..._namespaces import resolve_id +from ..._utils import drop_none +from ...session import Session, require_active_session from ...types import MISSING, MISSING_TYPE from ...ui import AccordionPanel as MainAccordionPanel from ...ui import accordion as main_accordion @@ -27,7 +29,6 @@ from ...ui import popover as main_popover from ...ui import tags from ...ui import toggle_popover as main_toggle_popover -from ...ui import toggle_switch as main_toggle_switch from ...ui import toggle_tooltip as main_toggle_tooltip from ...ui import tooltip as main_tooltip from ...ui import update_accordion_panel as main_update_accordion_panel @@ -59,7 +60,7 @@ from ...ui._sidebar import panel_main as main_panel_main from ...ui._sidebar import panel_sidebar as main_panel_sidebar from ...ui._sidebar import sidebar as main_sidebar -from ...ui._sidebar import toggle_sidebar as main_toggle_sidebar +from ...ui._sidebar import update_sidebar as main_update_sidebar from ...ui._valuebox import ShowcaseLayout as MainShowcaseLayout from ...ui._valuebox import showcase_left_center as main_showcase_left_center from ...ui._valuebox import showcase_top_right as main_showcase_top_right @@ -176,13 +177,22 @@ def toggle_switch( value: Optional[bool] = None, session: Optional[Session] = None, ) -> None: - """Deprecated. Please use `shiny.ui.toggle_switch()` instead.""" + """Defunct. Please do not use method.""" warn_deprecated( - "`shiny.experimental.ui.toggle_switch()` is deprecated. " + "`shiny.experimental.ui.toggle_switch()` is defunct. " "This method will be removed in a future version, " - "please use `shiny.ui.toggle_switch()` instead." + "please update your code accordingly." ) - return main_toggle_switch(id, value, session=session) + + if value is not None and not isinstance(value, bool): + raise TypeError("`value` must be `None` or a single boolean value.") + msg = drop_none({"id": resolve_id(id), "value": value}) + session = require_active_session(session) + + async def callback(): + await session.send_custom_message("bslib.toggle-input-binary", msg) + + session.on_flush(callback, once=True) ###################### @@ -499,15 +509,16 @@ def toggle_sidebar( open: Literal["toggle", "open", "closed", "always"] | bool | None = None, session: Session | None = None, ) -> None: - """Deprecated. Please use `shiny.ui.toggle_sidebar()` instead.""" + """Deprecated. Please use `shiny.ui.update_sidebar()` instead.""" warn_deprecated( "`shiny.experimental.ui.toggle_sidebar()` is deprecated. " "This method will be removed in a future version, " - "please use `shiny.ui.toggle_sidebar()` instead." + "please use `shiny.ui.update_sidebar()` instead." ) - return main_toggle_sidebar( + open_val = (open is True) or (open == "open") + return main_update_sidebar( id, - open=open, + open=open_val, session=session, ) @@ -524,15 +535,17 @@ def sidebar_toggle( open: Literal["toggle", "open", "closed", "always"] | bool | None = None, session: Session | None = None, ) -> None: - """Deprecated. Please use `shiny.ui.toggle_sidebar()` instead of `sidebar_toggle()`.""" + """Deprecated. Please use `shiny.ui.update_sidebar()` instead of + `shiny.experimental.ui.sidebar_toggle()`.""" warn_deprecated( "`shiny.experimental.ui.sidebar_toggle()` is deprecated. " "This method will be removed in a future version, " - "please use `shiny.ui.toggle_sidebar()` instead." + "please use `shiny.ui.update_sidebar()` instead." ) - main_toggle_sidebar( + open_val = (open is True) or (open == "open") + main_update_sidebar( id=id, - open=open, + open=open_val, session=session, ) @@ -1095,7 +1108,7 @@ def value_box( showcase=showcase, showcase_layout=showcase_layout_val, full_screen=full_screen, - theme_color=theme_color, + theme=theme_color, height=height, max_height=max_height, fill=fill, diff --git a/shiny/ui/__init__.py b/shiny/ui/__init__.py index b32be1235..c16352f7d 100644 --- a/shiny/ui/__init__.py +++ b/shiny/ui/__init__.py @@ -17,7 +17,7 @@ Sidebar, sidebar, layout_sidebar, - toggle_sidebar, + update_sidebar, panel_sidebar, panel_main, ) @@ -126,7 +126,7 @@ from .dataframe import output_data_frame -from ._input_toggle import toggle_switch, toggle_tooltip, toggle_popover +from ._input_toggle import toggle_tooltip, toggle_popover from ._popover import popover from ._valuebox import ( value_box, @@ -183,7 +183,7 @@ "Sidebar", "sidebar", "layout_sidebar", - "toggle_sidebar", + "update_sidebar", "panel_sidebar", "panel_main", # _layout @@ -306,7 +306,6 @@ "page_fixed", "page_bootstrap", # _input_toggle - "toggle_switch", "toggle_tooltip", "toggle_popover", # _popover diff --git a/shiny/ui/_input_toggle.py b/shiny/ui/_input_toggle.py index e7318f3a7..a86b166c3 100644 --- a/shiny/ui/_input_toggle.py +++ b/shiny/ui/_input_toggle.py @@ -3,8 +3,6 @@ from typing import Literal, Optional from .._docstring import add_example -from .._utils import drop_none -from ..module import resolve_id from ..session import Session, require_active_session from ._utils import _session_on_flush_send_msg @@ -82,33 +80,3 @@ def _normalize_show_value(show: bool | None) -> Literal["toggle", "show", "hide" if show is None: return "toggle" return "show" if show else "hide" - - -@add_example() -# TODO-barret-API; Should toggle methods exist? You should know (or can retrieve) the current state and where you are wanting to go. -def toggle_switch( # TODO-barret-API; Move into `update_switch()` method - id: str, value: Optional[bool] = None, session: Optional[Session] = None -): - """ - Toggle a switch input. - - Parameters - ---------- - id - The id of the switch input. - value - The new value of the switch input. If `NULL`, the value will be toggled. - session - The session object passed to `server()`. - """ - - if value is not None and not isinstance(value, bool): - raise TypeError("`value` must be `None` or a single boolean value.") - - msg = drop_none({"id": resolve_id(id), "value": value}) - session = require_active_session(session) - - async def callback(): - await session.send_custom_message("bslib.toggle-input-binary", msg) - - session.on_flush(callback, once=True) diff --git a/shiny/ui/_sidebar.py b/shiny/ui/_sidebar.py index 66f4bdbf3..fce26616b 100644 --- a/shiny/ui/_sidebar.py +++ b/shiny/ui/_sidebar.py @@ -31,7 +31,7 @@ "Sidebar", "sidebar", "layout_sidebar", - "toggle_sidebar", + "update_sidebar", # Legacy "panel_sidebar", "panel_main", @@ -173,10 +173,8 @@ def sidebar( open), `"closed"` or `False` (the sidebar starts closed), or `"always"` or `None` (the sidebar is always open and cannot be closed). - In :func:`~shiny.ui.toggle_sidebar`, `open` indicates the desired - state of the sidebar, where the default of `open = None` will cause the sidebar - to be toggled open if closed or vice versa. Note that - :func:`~shiny.ui.toggle_sidebar` can only open or close the + In :func:`~shiny.ui.update_sidebar`, `open` indicates the desired state of the + sidebar. Note that :func:`~shiny.ui.update_sidebar` can only open or close the sidebar, so it does not support the `"desktop"` and `"always"` options. id A character string. Required if wanting to re-actively read (or update) the @@ -499,25 +497,24 @@ def _get_layout_sidebar_sidebar( @add_example() -def toggle_sidebar( # TODO-barret-API; Rename to `update_sidebar()` +def update_sidebar( id: str, - open: Literal["toggle", "open", "closed", "always"] | bool | None = None, - session: Session | None = None, + *, + open: Optional[bool] = None, + session: Optional[Session] = None, ) -> None: """ - Toggle a sidebar + Update a sidebar's visibility - Toggle a :func:`~shiny.ui.sidebar` state during an active Shiny user session. + Set a :func:`~shiny.ui.sidebar` state during an active Shiny user session. Parameters ---------- id The `id` of the :func:`~shiny.ui.sidebar` to toggle. open - The desired state of the sidebar, choosing from the following options: `None` - (toggle sidebar open and closed), `"open"` or `True` (open the sidebar), - `"closed"` or `False` (close the sidebar). Note that `toggle_sidebar()` can only - open or close the sidebar, so it does not support the `"desktop"` and `"always"` + The desired state of the sidebar, where `True` sets the sidebar state to be + open, and `False` sets the sidebar state to be closed. session A Shiny session object (the default should almost always be used). @@ -528,21 +525,22 @@ def toggle_sidebar( # TODO-barret-API; Rename to `update_sidebar()` """ session = require_active_session(session) - method: Literal["toggle", "open", "close"] - if open is None or open == "toggle": - method = "toggle" - elif open is True or open == "open": - method = "open" - elif open is False or open == "closed": - method = "close" - else: - if open == "always" or open == "desktop": - raise ValueError( - f"`open = '{open}'` is not supported by `toggle_sidebar()`" - ) - raise ValueError( - "open must be NULL (or 'toggle'), TRUE (or 'open'), or FALSE (or 'closed')" - ) + # method: Literal["toggle", "open", "close"] + # if open is None or open == "toggle": + # method = "toggle" + # elif open is True or open == "open": + # method = "open" + # elif open is False or open == "closed": + # method = "close" + # else: + # if open == "always" or open == "desktop": + # raise ValueError( + # f"`open = '{open}'` is not supported by `update_sidebar()`" + # ) + # raise ValueError( + # "open must be NULL (or 'toggle'), TRUE (or 'open'), or FALSE (or 'closed')" + # ) + method = "open" if bool(open) else "close" def callback() -> None: session.send_input_message(id, {"method": method}) @@ -550,9 +548,6 @@ def callback() -> None: session.on_flush(callback, once=True) -_sidebar_func = sidebar - - def _collapse_icon() -> TagChild: return HTML( '' diff --git a/tests/e2e/TODO/sidebar/app.py b/tests/e2e/TODO/sidebar/app.py index fc30b92d8..52e6e546c 100644 --- a/tests/e2e/TODO/sidebar/app.py +++ b/tests/e2e/TODO/sidebar/app.py @@ -65,24 +65,24 @@ def ui_content(): @reactive.Effect @reactive.event(input.open_all) def _(): - ui.toggle_sidebar("sidebar_inner", open=True) - ui.toggle_sidebar("sidebar_outer", open=True) + ui.update_sidebar("sidebar_inner", open=True) + ui.update_sidebar("sidebar_outer", open=True) @reactive.Effect @reactive.event(input.close_all) def _(): - ui.toggle_sidebar("sidebar_inner", open=False) - ui.toggle_sidebar("sidebar_outer", open=False) + ui.update_sidebar("sidebar_inner", open=False) + ui.update_sidebar("sidebar_outer", open=False) @reactive.Effect @reactive.event(input.toggle_inner) def _(): - ui.toggle_sidebar("sidebar_inner") + ui.update_sidebar("sidebar_inner", open=not input.sidebar_inner()) @reactive.Effect @reactive.event(input.toggle_outer) def _(): - ui.toggle_sidebar("sidebar_outer") + ui.update_sidebar("sidebar_outer", open=not input.sidebar_outer()) app = App(app_ui, server) diff --git a/tests/e2e/bugs/0696-resolve-id/app.py b/tests/e2e/bugs/0696-resolve-id/app.py index 8e8562b99..5479b9361 100644 --- a/tests/e2e/bugs/0696-resolve-id/app.py +++ b/tests/e2e/bugs/0696-resolve-id/app.py @@ -441,7 +441,7 @@ def update_session( # https://github.com/posit-dev/py-shiny/issues/716 # ui.update_sidebar("sidebar", value=True) if session.input.sidebar() is False: - ui.toggle_sidebar("sidebar") + ui.update_sidebar("sidebar", open=True) # Turn off switch ui.update_switch("input_switch", value=False) @@ -457,9 +457,9 @@ def update_session( ui.toggle_tooltip("tooltip") else: if session.input.sidebar() == on_off: - ui.toggle_sidebar("sidebar") + ui.update_sidebar("sidebar", open=not on_off) if session.input.input_switch() != on_off: - ui.toggle_switch("input_switch") + ui.update_switch("input_switch", value=on_off) if session.input.popover() != on_off: ui.toggle_popover("popover") if session.input.tooltip() != on_off: From 29169b853e2bd2864311464e588bec1b1fdb8b4c Mon Sep 17 00:00:00 2001 From: Barret Schloerke Date: Fri, 27 Oct 2023 22:12:55 -0400 Subject: [PATCH 05/12] Absorb `toggle_tooltip()` and `toggle_popover()` into `update_tooltip(show=)` and `update_popover(show=)` --- CHANGELOG.md | 4 +- docs/_quartodoc.yml | 2 - shiny/api-examples/toggle_sidebar/app.py | 27 ------ shiny/api-examples/toggle_tooltip/app.py | 43 ---------- .../{toggle_popover => update_popover}/app.py | 12 +-- shiny/api-examples/update_sidebar/app.py | 31 +++++++ shiny/api-examples/update_tooltip/app.py | 25 ++++-- shiny/experimental/ui/_deprecated.py | 26 +++--- shiny/ui/__init__.py | 4 - shiny/ui/_input_toggle.py | 82 ------------------- shiny/ui/_input_update.py | 73 +++++++++++++---- shiny/ui/_sidebar.py | 16 ++-- tests/e2e/TODO/sidebar/app.py | 12 +-- tests/e2e/TODO/update_popover/app.py | 10 ++- tests/e2e/bugs/0696-resolve-id/app.py | 16 ++-- tests/e2e/ui/popover/app.py | 12 +-- tests/e2e/ui/tooltip/app.py | 12 +-- 17 files changed, 157 insertions(+), 250 deletions(-) delete mode 100644 shiny/api-examples/toggle_sidebar/app.py delete mode 100644 shiny/api-examples/toggle_tooltip/app.py rename shiny/api-examples/{toggle_popover => update_popover}/app.py (72%) create mode 100644 shiny/api-examples/update_sidebar/app.py delete mode 100644 shiny/ui/_input_toggle.py diff --git a/CHANGELOG.md b/CHANGELOG.md index 2b0440583..7df2838e5 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -52,9 +52,9 @@ The following methods have been moved from `shiny.experimental.ui` and integrate * CSS units - CSS units and padding * `css.as_css_unit()`, `css.as_css_padding()`, `css.CssUnit` * Tooltip - Hover-based context UI element - * `tooltip()`, `toggle_tooltip()`, `update_tooltip()` + * `tooltip()`, `update_tooltip()` * Popover - Click-based context UI element - * `popover()`, `toggle_popover()`, `update_popover()` + * `popover()`, `update_popover()` * Accordion - Vertically collapsible UI element * `accordion()`, `accordion_panel()`, `accordion_panel_close()`, `accordion_panel_insert()`, `accordion_panel_open()`, `accordion_panel_remove()`, `accordion_panel_set()`, `update_accordion_panel()`, `Accordion`, `AccordionPanel` * Card - A general purpose container for grouping related UI elements together diff --git a/docs/_quartodoc.yml b/docs/_quartodoc.yml index 93e7d03d2..fcb5eb988 100644 --- a/docs/_quartodoc.yml +++ b/docs/_quartodoc.yml @@ -141,8 +141,6 @@ quartodoc: desc: "" contents: - ui.update_sidebar - - ui.toggle_tooltip - - ui.toggle_popover - ui.update_tooltip - ui.update_popover - ui.update_accordion_panel diff --git a/shiny/api-examples/toggle_sidebar/app.py b/shiny/api-examples/toggle_sidebar/app.py deleted file mode 100644 index 06601b36f..000000000 --- a/shiny/api-examples/toggle_sidebar/app.py +++ /dev/null @@ -1,27 +0,0 @@ -from shiny import App, Inputs, Outputs, Session, reactive, render, ui - -app_ui = ui.page_sidebar( - ui.sidebar("Sidebar content", id="sidebar"), - ui.input_action_button( - "toggle_sidebar_btn", - label="Toggle sidebar", - width="fit-content", - ), - ui.output_text_verbatim("state"), -) - - -def server(input: Inputs, output: Outputs, session: Session): - @reactive.Effect - @reactive.event(input.toggle_sidebar_btn) - def _(): - sidebar_is_open = input.sidebar() - ui.update_sidebar("sidebar", open=not sidebar_is_open) - - @output - @render.text - def state(): - return f"input.sidebar(): {input.sidebar()}" - - -app = App(app_ui, server=server) diff --git a/shiny/api-examples/toggle_tooltip/app.py b/shiny/api-examples/toggle_tooltip/app.py deleted file mode 100644 index ce4ad6885..000000000 --- a/shiny/api-examples/toggle_tooltip/app.py +++ /dev/null @@ -1,43 +0,0 @@ -from shiny import App, Inputs, Outputs, Session, reactive, req, ui - -app_ui = ui.page_fluid( - ui.input_action_button("btn_show", "Show tooltip", class_="mt-3 me-3"), - ui.input_action_button("btn_close", "Close tooltip", class_="mt-3 me-3"), - ui.br(), - ui.input_action_button("btn_toggle", "Toggle tooltip", class_="mt-3 me-3"), - ui.br(), - ui.br(), - ui.tooltip( - ui.input_action_button("btn_w_tooltip", "A button w/ a tooltip", class_="mt-3"), - "A message", - id="tooltip_id", - ), -) - - -def server(input: Inputs, output: Outputs, session: Session): - @reactive.Effect - def _(): - req(input.btn_show()) - - ui.toggle_tooltip("tooltip_id", show=True) - - @reactive.Effect - def _(): - req(input.btn_close()) - - ui.toggle_tooltip("tooltip_id", show=False) - - @reactive.Effect - def _(): - req(input.btn_toggle()) - - ui.toggle_tooltip("tooltip_id") - - @reactive.Effect - def _(): - req(input.btn_w_tooltip()) - ui.notification_show("Button clicked!", duration=3, type="message") - - -app = App(app_ui, server=server) diff --git a/shiny/api-examples/toggle_popover/app.py b/shiny/api-examples/update_popover/app.py similarity index 72% rename from shiny/api-examples/toggle_popover/app.py rename to shiny/api-examples/update_popover/app.py index 4c4141833..f54f18de9 100644 --- a/shiny/api-examples/toggle_popover/app.py +++ b/shiny/api-examples/update_popover/app.py @@ -4,8 +4,6 @@ ui.input_action_button("btn_show", "Show popover", class_="mt-3 me-3"), ui.input_action_button("btn_close", "Close popover", class_="mt-3 me-3"), ui.br(), - ui.input_action_button("btn_toggle", "Toggle popover", class_="mt-3 me-3"), - ui.br(), ui.br(), ui.popover( ui.input_action_button("btn_w_popover", "A button w/ a popover", class_="mt-3"), @@ -20,19 +18,13 @@ def server(input: Inputs, output: Outputs, session: Session): def _(): req(input.btn_show()) - ui.toggle_popover("popover_id", show=True) + ui.update_popover("popover_id", show=True) @reactive.Effect def _(): req(input.btn_close()) - ui.toggle_popover("popover_id", show=False) - - @reactive.Effect - def _(): - req(input.btn_toggle()) - - ui.toggle_popover("popover_id") + ui.update_popover("popover_id", show=False) @reactive.Effect def _(): diff --git a/shiny/api-examples/update_sidebar/app.py b/shiny/api-examples/update_sidebar/app.py new file mode 100644 index 000000000..b0849155a --- /dev/null +++ b/shiny/api-examples/update_sidebar/app.py @@ -0,0 +1,31 @@ +from shiny import App, Inputs, Outputs, Session, reactive, render, ui + +app_ui = ui.page_sidebar( + ui.sidebar("Sidebar content", id="sidebar"), + ui.input_action_button("open_sidebar", label="Open sidebar", class_="me-3"), + ui.input_action_button("close_sidebar", label="Close sidebar", class_="me-3"), + ui.br(), + ui.br(), + ui.output_text_verbatim("state"), + fillable=False, +) + + +def server(input: Inputs, output: Outputs, session: Session): + @reactive.Effect + @reactive.event(input.open_sidebar) + def _(): + ui.update_sidebar("sidebar", show=True) + + @reactive.Effect + @reactive.event(input.close_sidebar) + def _(): + ui.update_sidebar("sidebar", show=False) + + @output + @render.text + def state(): + return f"input.sidebar(): {input.sidebar()}" + + +app = App(app_ui, server=server) diff --git a/shiny/api-examples/update_tooltip/app.py b/shiny/api-examples/update_tooltip/app.py index 786c73c41..aa81eee94 100644 --- a/shiny/api-examples/update_tooltip/app.py +++ b/shiny/api-examples/update_tooltip/app.py @@ -1,7 +1,12 @@ -from shiny import App, Inputs, Outputs, Session, reactive, ui +from shiny import App, Inputs, Outputs, Session, reactive, req, ui app_ui = ui.page_fluid( - ui.input_action_button("btn_update", "Update tooltip phrase", class_="mt-3 me-3"), + ui.input_action_button("btn_show", "Show tooltip", class_="mt-3 me-3"), + ui.input_action_button("btn_close", "Close tooltip", class_="mt-3 me-3"), + ui.br(), + ui.input_action_button( + "btn_update", "Update tooltip phrase (and show tooltip)", class_="mt-3 me-3" + ), ui.br(), ui.br(), ui.tooltip( @@ -15,8 +20,15 @@ def server(input: Inputs, output: Outputs, session: Session): @reactive.Effect def _(): - # Immediately display tooltip - ui.toggle_tooltip("tooltip_id", show=True) + req(input.btn_show()) + + ui.update_tooltip("tooltip_id", show=True) + + @reactive.Effect + def _(): + req(input.btn_close()) + + ui.update_tooltip("tooltip_id", show=False) @reactive.Effect @reactive.event(input.btn_update) @@ -25,12 +37,11 @@ def _(): "A " + " ".join(["NEW" for _ in range(input.btn_update())]) + " message" ) - ui.update_tooltip("tooltip_id", content) - ui.toggle_tooltip("tooltip_id", show=True) + ui.update_tooltip("tooltip_id", content, show=True) @reactive.Effect - @reactive.event(input.btn_w_tooltip) def _(): + req(input.btn_w_tooltip()) ui.notification_show("Button clicked!", duration=3, type="message") diff --git a/shiny/experimental/ui/_deprecated.py b/shiny/experimental/ui/_deprecated.py index 08440d6b6..06ec0735a 100644 --- a/shiny/experimental/ui/_deprecated.py +++ b/shiny/experimental/ui/_deprecated.py @@ -28,8 +28,6 @@ from ...ui import input_text_area as main_input_text_area from ...ui import popover as main_popover from ...ui import tags -from ...ui import toggle_popover as main_toggle_popover -from ...ui import toggle_tooltip as main_toggle_tooltip from ...ui import tooltip as main_tooltip from ...ui import update_accordion_panel as main_update_accordion_panel from ...ui import update_popover as main_update_popover @@ -360,13 +358,13 @@ def tooltip_toggle( show: Optional[bool] = None, session: Optional[Session] = None, ) -> None: - """Deprecated. Please use `shiny.ui.toggle_tooltip()`.""" + """Deprecated. Please use `shiny.ui.update_tooltip()`.""" warn_deprecated( "`shiny.experimental.ui.tooltip_toggle()` is deprecated. " "This method will be removed in a future version, " - "please use `shiny.ui.toggle_tooltip()` instead." + "please use `shiny.ui.update_tooltip()` instead." ) - main_toggle_tooltip( + main_update_tooltip( id=id, show=show, session=session, @@ -379,13 +377,13 @@ def toggle_tooltip( show: Optional[bool] = None, session: Optional[Session] = None, ) -> None: - """Deprecated. Please use `shiny.ui.toggle_tooltip()` instead.""" + """Deprecated. Please use `shiny.ui.update_tooltip()` instead.""" warn_deprecated( "`shiny.experimental.ui.tooltip_toggle()` is deprecated. " "This method will be removed in a future version, " - "please use `shiny.ui.toggle_tooltip()` instead." + "please use `shiny.ui.update_tooltip()` instead." ) - main_toggle_tooltip( + main_update_tooltip( id=id, show=show, session=session, @@ -518,7 +516,7 @@ def toggle_sidebar( open_val = (open is True) or (open == "open") return main_update_sidebar( id, - open=open_val, + show=open_val, session=session, ) @@ -545,7 +543,7 @@ def sidebar_toggle( open_val = (open is True) or (open == "open") main_update_sidebar( id=id, - open=open_val, + show=open_val, session=session, ) @@ -687,13 +685,13 @@ def toggle_popover( show: Optional[bool] = None, session: Optional[Session] = None, ) -> None: - """Deprecated. Please use `shiny.ui.toggle_popover()` instead.""" + """Deprecated. Please use `shiny.ui.update_popover()` instead.""" warn_deprecated( - "`shiny.experimental.ui.toggle_popover()` is deprecated. " + "`shiny.experimental.ui.update_popover()` is deprecated. " "This method will be removed in a future version, " - "please use `shiny.ui.toggle_popover()` instead." + "please use `shiny.ui.update_popover()` instead." ) - return main_toggle_popover(id, show, session=session) + return main_update_popover(id, show, session=session) # Deprecated 2023-09-12 diff --git a/shiny/ui/__init__.py b/shiny/ui/__init__.py index c16352f7d..cac3da16d 100644 --- a/shiny/ui/__init__.py +++ b/shiny/ui/__init__.py @@ -126,7 +126,6 @@ from .dataframe import output_data_frame -from ._input_toggle import toggle_tooltip, toggle_popover from ._popover import popover from ._valuebox import ( value_box, @@ -305,9 +304,6 @@ "page_fluid", "page_fixed", "page_bootstrap", - # _input_toggle - "toggle_tooltip", - "toggle_popover", # _popover "popover", # _valuebox diff --git a/shiny/ui/_input_toggle.py b/shiny/ui/_input_toggle.py deleted file mode 100644 index a86b166c3..000000000 --- a/shiny/ui/_input_toggle.py +++ /dev/null @@ -1,82 +0,0 @@ -from __future__ import annotations - -from typing import Literal, Optional - -from .._docstring import add_example -from ..session import Session, require_active_session -from ._utils import _session_on_flush_send_msg - - -@add_example() -# TODO-barret-API; Should toggle methods exist? You should know (or can retrieve) the current state and where you are wanting to go. -def toggle_popover( # TODO-barret-API; Move into update popover method - id: str, - show: Optional[bool] = None, - session: Optional[Session] = None, -) -> None: - """ - Programmatically show/hide a popover. - - Parameters - ---------- - id - The id of the popover DOM element to update. - show - Whether to show (`True`) or hide (`False`) the popover. The default - (`None`) will show if currently hidden and hide if currently shown. - Note that a popover will not be shown if the trigger is not visible - (e.g., it is hidden behind a tab). - session - A Shiny session object (the default should almost always be used). - - See Also - -------- - * :func:`~shiny.ui.popover` - * :func:`~shiny.ui.update_popover` - """ - session = require_active_session(session) - - _session_on_flush_send_msg( - id, - session, - { - "method": "toggle", - "value": _normalize_show_value(show), - }, - ) - - -@add_example() -# TODO-barret-API; Should toggle methods exist? You should know (or can retrieve) the current state and where you are wanting to go. -def toggle_tooltip( # TODO-barret-API; Move into update tooltip method - id: str, show: Optional[bool] = None, session: Optional[Session] = None -) -> None: - """ - Programmatically show/hide a tooltip - - Parameters - ---------- - id - A character string that matches an existing tooltip id. - show - Whether to show (`True`) or hide (`False`) the tooltip. The default (`None`) - will show if currently hidden and hide if currently shown. Note that a tooltip - will not be shown if the trigger is not visible (e.g., it's hidden behind a - tab). - session - A Shiny session object (the default should almost always be used). - """ - _session_on_flush_send_msg( - id, - session, - { - "method": "toggle", - "value": _normalize_show_value(show), - }, - ) - - -def _normalize_show_value(show: bool | None) -> Literal["toggle", "show", "hide"]: - if show is None: - return "toggle" - return "show" if show else "hide" diff --git a/shiny/ui/_input_update.py b/shiny/ui/_input_update.py index 5351725b8..22a185d9c 100644 --- a/shiny/ui/_input_update.py +++ b/shiny/ui/_input_update.py @@ -21,7 +21,7 @@ import json import re from datetime import date -from typing import Literal, Mapping, Optional +from typing import Literal, Mapping, Optional, overload from htmltools import TagChild, TagList from starlette.requests import Request @@ -902,7 +902,12 @@ def update_navs( # tooltips.py # ----------------------------------------------------------------------------- @add_example() -def update_tooltip(id: str, *args: TagChild, session: Optional[Session] = None) -> None: +def update_tooltip( + id: str, + *args: TagChild, + show: Optional[bool] = None, + session: Optional[Session] = None, +) -> None: """ Update tooltip contents @@ -912,9 +917,12 @@ def update_tooltip(id: str, *args: TagChild, session: Optional[Session] = None) A character string that matches an existing tooltip id. *args Contents to the tooltip's body. + show + Opens (`True`) or closes (`False`) the tooltip. session A Shiny session object (the default should almost always be used). """ + _session_on_flush_send_msg( id, session, @@ -927,6 +935,15 @@ def update_tooltip(id: str, *args: TagChild, session: Optional[Session] = None) } ), ) + if show is not None: + _session_on_flush_send_msg( + id, + session, + { + "method": "toggle", + "value": _normalize_show_value(show), + }, + ) # ----------------------------------------------------------------------------- @@ -939,6 +956,7 @@ def update_popover( id: str, *args: TagChild, title: Optional[TagChild] = None, + show: Optional[bool] = None, session: Optional[Session] = None, ) -> None: """ @@ -952,26 +970,53 @@ def update_popover( The new contents of the popover. title The new title of the popover. + show + Opens (`True`) or closes (`False) the popover. session A Shiny session object (the default should almost always be used). See Also -------- * :func:`~shiny.ui.popover` - * :func:`~shiny.ui.toggle_popover` """ session = require_active_session(session) - _session_on_flush_send_msg( - id, - session, - drop_none( + if title is not None or len(args) > 0: + _session_on_flush_send_msg( + id, + session, + drop_none( + { + "method": "update", + "content": session._process_ui(TagList(*args)) + if len(args) > 0 + else None, + "header": session._process_ui(title) if title is not None else None, + }, + ), + ) + if show is not None: + _session_on_flush_send_msg( + id, + session, { - "method": "update", - "content": session._process_ui(TagList(*args)) - if len(args) > 0 - else None, - "header": session._process_ui(title) if title is not None else None, + "method": "toggle", + "value": _normalize_show_value(show), }, - ), - ) + ) + + +@overload +def _normalize_show_value(show: None) -> Literal["toggle"]: + ... + + +@overload +def _normalize_show_value(show: bool) -> Literal["show", "hide"]: + ... + + +def _normalize_show_value(show: bool | None) -> Literal["toggle", "show", "hide"]: + if show is None: + return "toggle" + return "show" if show else "hide" diff --git a/shiny/ui/_sidebar.py b/shiny/ui/_sidebar.py index fce26616b..5a3abb429 100644 --- a/shiny/ui/_sidebar.py +++ b/shiny/ui/_sidebar.py @@ -500,7 +500,7 @@ def _get_layout_sidebar_sidebar( def update_sidebar( id: str, *, - open: Optional[bool] = None, + show: Optional[bool] = None, session: Optional[Session] = None, ) -> None: """ @@ -512,9 +512,8 @@ def update_sidebar( ---------- id The `id` of the :func:`~shiny.ui.sidebar` to toggle. - open - The desired state of the sidebar, where `True` sets the sidebar state to be - open, and `False` sets the sidebar state to be closed. + show + The desired visible state of the sidebar, where `True` opens the sidebar and `False` closes the sidebar (if not already in that state). session A Shiny session object (the default should almost always be used). @@ -540,12 +539,13 @@ def update_sidebar( # raise ValueError( # "open must be NULL (or 'toggle'), TRUE (or 'open'), or FALSE (or 'closed')" # ) - method = "open" if bool(open) else "close" + if show is not None: + method = "open" if bool(show) else "close" - def callback() -> None: - session.send_input_message(id, {"method": method}) + def callback() -> None: + session.send_input_message(id, {"method": method}) - session.on_flush(callback, once=True) + session.on_flush(callback, once=True) def _collapse_icon() -> TagChild: diff --git a/tests/e2e/TODO/sidebar/app.py b/tests/e2e/TODO/sidebar/app.py index 52e6e546c..8e71a34ce 100644 --- a/tests/e2e/TODO/sidebar/app.py +++ b/tests/e2e/TODO/sidebar/app.py @@ -65,24 +65,24 @@ def ui_content(): @reactive.Effect @reactive.event(input.open_all) def _(): - ui.update_sidebar("sidebar_inner", open=True) - ui.update_sidebar("sidebar_outer", open=True) + ui.update_sidebar("sidebar_inner", show=True) + ui.update_sidebar("sidebar_outer", show=True) @reactive.Effect @reactive.event(input.close_all) def _(): - ui.update_sidebar("sidebar_inner", open=False) - ui.update_sidebar("sidebar_outer", open=False) + ui.update_sidebar("sidebar_inner", show=False) + ui.update_sidebar("sidebar_outer", show=False) @reactive.Effect @reactive.event(input.toggle_inner) def _(): - ui.update_sidebar("sidebar_inner", open=not input.sidebar_inner()) + ui.update_sidebar("sidebar_inner", show=not input.sidebar_inner()) @reactive.Effect @reactive.event(input.toggle_outer) def _(): - ui.update_sidebar("sidebar_outer", open=not input.sidebar_outer()) + ui.update_sidebar("sidebar_outer", show=not input.sidebar_outer()) app = App(app_ui, server) diff --git a/tests/e2e/TODO/update_popover/app.py b/tests/e2e/TODO/update_popover/app.py index 30a83d7eb..55b6d1c2b 100644 --- a/tests/e2e/TODO/update_popover/app.py +++ b/tests/e2e/TODO/update_popover/app.py @@ -17,7 +17,7 @@ def server(input: Inputs, output: Outputs, session: Session): @reactive.Effect def _(): # Immediately display popover - ui.toggle_popover("popover_id", show=True) + ui.update_popover("popover_id", show=True) @reactive.Effect @reactive.event(input.btn_update) @@ -26,8 +26,12 @@ def _(): "A " + " ".join(["NEW" for _ in range(input.btn_update())]) + " message" ) - ui.update_popover("popover_id", content) - # ui.toggle_popover("popover_id", show=True) + ui.update_popover( + "popover_id", + content, + # # Causes bug. Skipping for now + # show=True + ) @reactive.Effect def _(): diff --git a/tests/e2e/bugs/0696-resolve-id/app.py b/tests/e2e/bugs/0696-resolve-id/app.py index 5479b9361..ed3abd13d 100644 --- a/tests/e2e/bugs/0696-resolve-id/app.py +++ b/tests/e2e/bugs/0696-resolve-id/app.py @@ -391,7 +391,7 @@ def server(input: Inputs, output: Outputs, session: Session): # https://stackoverflow.com/a/64523765/591574 if hasattr(sys, "ps1"): # Open the explanation popover - ui.toggle_popover("explanation", show=True) + ui.update_popover("explanation", show=True) # On button clicks, hide the explanation popover @reactive.Effect(suspended=True) @@ -400,7 +400,7 @@ def _(): input.update_global() input.update_mod1() input.update_mod2() - ui.toggle_popover("explanation", show=False) + ui.update_popover("explanation", show=False) # Master function to update a module's features def update_session( @@ -441,7 +441,7 @@ def update_session( # https://github.com/posit-dev/py-shiny/issues/716 # ui.update_sidebar("sidebar", value=True) if session.input.sidebar() is False: - ui.update_sidebar("sidebar", open=True) + ui.update_sidebar("sidebar", show=True) # Turn off switch ui.update_switch("input_switch", value=False) @@ -450,20 +450,20 @@ def update_session( # https://github.com/posit-dev/py-shiny/issues/717 # ui.update_popover("popover", show=False) if session.input.popover() is True: - ui.toggle_popover("popover") + ui.update_popover("popover", show=False) # Hide tooltip # https://github.com/posit-dev/py-shiny/issues/717 if session.input.tooltip() is True: - ui.toggle_tooltip("tooltip") + ui.update_tooltip("tooltip", show=False) else: if session.input.sidebar() == on_off: - ui.update_sidebar("sidebar", open=not on_off) + ui.update_sidebar("sidebar", show=not on_off) if session.input.input_switch() != on_off: ui.update_switch("input_switch", value=on_off) if session.input.popover() != on_off: - ui.toggle_popover("popover") + ui.update_popover("popover", show=on_off) if session.input.tooltip() != on_off: - ui.toggle_tooltip("tooltip") + ui.update_tooltip("tooltip", show=on_off) ui.update_navs("navset_bar", selected=letter) ui.update_navs("navset_card_pill", selected=letter) diff --git a/tests/e2e/ui/popover/app.py b/tests/e2e/ui/popover/app.py index d19a74afc..45ea2191b 100644 --- a/tests/e2e/ui/popover/app.py +++ b/tests/e2e/ui/popover/app.py @@ -4,8 +4,6 @@ ui.input_action_button("btn_show", "Show popover", class_="mt-3 me-3"), ui.input_action_button("btn_close", "Close popover", class_="mt-3 me-3"), ui.br(), - ui.input_action_button("btn_toggle", "Toggle popover", class_="mt-3 me-3"), - ui.br(), ui.br(), ui.popover( ui.input_action_button("btn_w_popover", "A button w/ a popover", class_="mt-3"), @@ -21,19 +19,13 @@ def server(input: Inputs, output: Outputs, session: Session): def _(): req(input.btn_show()) - ui.toggle_popover("popover_id", show=True) + ui.update_popover("popover_id", show=True) @reactive.Effect def _(): req(input.btn_close()) - ui.toggle_popover("popover_id", show=False) - - @reactive.Effect - def _(): - req(input.btn_toggle()) - - ui.toggle_popover("popover_id") + ui.update_popover("popover_id", show=False) @reactive.Effect def _(): diff --git a/tests/e2e/ui/tooltip/app.py b/tests/e2e/ui/tooltip/app.py index b6fd67061..fcf6cb208 100644 --- a/tests/e2e/ui/tooltip/app.py +++ b/tests/e2e/ui/tooltip/app.py @@ -4,8 +4,6 @@ ui.input_action_button("btn_show", "Show tooltip", class_="mt-3 me-3"), ui.input_action_button("btn_close", "Close tooltip", class_="mt-3 me-3"), ui.br(), - ui.input_action_button("btn_toggle", "Toggle tooltip", class_="mt-3 me-3"), - ui.br(), ui.br(), ui.tooltip( ui.input_action_button("btn_w_tooltip", "A button w/ a tooltip", class_="mt-3"), @@ -21,19 +19,13 @@ def server(input: Inputs, output: Outputs, session: Session): def _(): req(input.btn_show()) - ui.toggle_tooltip("tooltip_id", show=True) + ui.update_tooltip("tooltip_id", show=True) @reactive.Effect def _(): req(input.btn_close()) - ui.toggle_tooltip("tooltip_id", show=False) - - @reactive.Effect - def _(): - req(input.btn_toggle()) - - ui.toggle_tooltip("tooltip_id") + ui.update_tooltip("tooltip_id", show=False) @reactive.Effect def _(): From b0561bc4e640f28dfe15893f6423775c860b6978 Mon Sep 17 00:00:00 2001 From: Barret Schloerke Date: Fri, 27 Oct 2023 22:16:12 -0400 Subject: [PATCH 06/12] Update app.py --- tests/e2e/bugs/0696-resolve-id/app.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/tests/e2e/bugs/0696-resolve-id/app.py b/tests/e2e/bugs/0696-resolve-id/app.py index ed3abd13d..d34079132 100644 --- a/tests/e2e/bugs/0696-resolve-id/app.py +++ b/tests/e2e/bugs/0696-resolve-id/app.py @@ -98,7 +98,6 @@ def ui_navs(label: str) -> list[ui._navs.Nav]: """ ), ui.layout_column_wrap( - 1 / 2, *[ ui.output_text_verbatim(f"status_x_{x_input_key}", placeholder=True) for x_input_key in x_input_keys @@ -107,6 +106,7 @@ def ui_navs(label: str) -> list[ui._navs.Nav]: ui.output_text_verbatim(f"status_{input_key}", placeholder=True) for input_key in input_keys ], + width=1 / 2, gap="2px", heights_equal="row", ), @@ -372,10 +372,10 @@ def preprocess_file(x: Any) -> str: id="explanation", ), ui.layout_column_wrap( - 1 / 3, mod_x_ui("", "Global"), # "" == Root mod_x_ui("mod1", "Module 1"), mod_x_ui("mod2", "Module 2"), + width=1 / 3, ), # ui.h3("Inputs that are not in a module:"), # ui.output_text_verbatim("not_modules", placeholder=True), From c9ce475776abbd98552a97e25b5a5831f797c2cb Mon Sep 17 00:00:00 2001 From: Barret Schloerke Date: Fri, 27 Oct 2023 22:26:19 -0400 Subject: [PATCH 07/12] `ui.accordion_panel_set()` -> `ui.update_accordion()` --- CHANGELOG.md | 2 +- docs/_quartodoc.yml | 3 ++ .../app.py | 2 +- shiny/experimental/ui/_deprecated.py | 10 +++--- shiny/ui/__init__.py | 4 +-- shiny/ui/_accordion.py | 34 ++++++++++--------- tests/e2e/bugs/0696-resolve-id/app.py | 2 +- tests/e2e/ui/accordion/app.py | 2 +- 8 files changed, 32 insertions(+), 27 deletions(-) rename shiny/api-examples/{accordion_panel_set => update_accordion}/app.py (88%) diff --git a/CHANGELOG.md b/CHANGELOG.md index 7df2838e5..2a7f2f2a7 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -56,7 +56,7 @@ The following methods have been moved from `shiny.experimental.ui` and integrate * Popover - Click-based context UI element * `popover()`, `update_popover()` * Accordion - Vertically collapsible UI element - * `accordion()`, `accordion_panel()`, `accordion_panel_close()`, `accordion_panel_insert()`, `accordion_panel_open()`, `accordion_panel_remove()`, `accordion_panel_set()`, `update_accordion_panel()`, `Accordion`, `AccordionPanel` + * `accordion()`, `accordion_panel()`, `accordion_panel_close()`, `accordion_panel_insert()`, `accordion_panel_open()`, `accordion_panel_remove()`, `update_accordion()`, `update_accordion_panel()`, `Accordion`, `AccordionPanel` * Card - A general purpose container for grouping related UI elements together * `card()`, `card_header()`, `card_footer()`, `CardItem` * Valuebox - Opinionated container for displaying a value and title diff --git a/docs/_quartodoc.yml b/docs/_quartodoc.yml index fcb5eb988..98f38e7af 100644 --- a/docs/_quartodoc.yml +++ b/docs/_quartodoc.yml @@ -143,7 +143,10 @@ quartodoc: - ui.update_sidebar - ui.update_tooltip - ui.update_popover + - ui.update_accordion - ui.update_accordion_panel + - ui.insert_accordion_panel + - ui.remove_accordion_panel - title: Rendering outputs desc: "UI (output_*()) and server (render)ing functions for generating content server-side." contents: diff --git a/shiny/api-examples/accordion_panel_set/app.py b/shiny/api-examples/update_accordion/app.py similarity index 88% rename from shiny/api-examples/accordion_panel_set/app.py rename to shiny/api-examples/update_accordion/app.py index 31ea62139..2ae14d64f 100644 --- a/shiny/api-examples/accordion_panel_set/app.py +++ b/shiny/api-examples/update_accordion/app.py @@ -16,7 +16,7 @@ def server(input: Inputs, output: Outputs, session: Session): @reactive.Effect @reactive.event(input.set_acc) def _(): - ui.accordion_panel_set("acc", ["Section A", "Section C", "Section E"]) + ui.update_accordion("acc", show=["Section A", "Section C", "Section E"]) app = App(app_ui, server) diff --git a/shiny/experimental/ui/_deprecated.py b/shiny/experimental/ui/_deprecated.py index 06ec0735a..7e43c1e7e 100644 --- a/shiny/experimental/ui/_deprecated.py +++ b/shiny/experimental/ui/_deprecated.py @@ -24,11 +24,11 @@ from ...ui import accordion_panel_insert as main_accordion_panel_insert from ...ui import accordion_panel_open as main_accordion_panel_open from ...ui import accordion_panel_remove as main_accordion_panel_remove -from ...ui import accordion_panel_set as main_accordion_panel_set from ...ui import input_text_area as main_input_text_area from ...ui import popover as main_popover from ...ui import tags from ...ui import tooltip as main_tooltip +from ...ui import update_accordion as main_update_accordion from ...ui import update_accordion_panel as main_update_accordion_panel from ...ui import update_popover as main_update_popover from ...ui import update_tooltip as main_update_tooltip @@ -782,13 +782,13 @@ def accordion_panel_set( values: bool | str | list[str], session: Optional[Session] = None, ) -> None: - """Deprecated. Please use `shiny.ui.accordion_panel_set()` instead.""" + """Deprecated. Please use `shiny.ui.update_accordion()` instead.""" warn_deprecated( - "`shiny.experimental.ui.accordion_panel_set()` is deprecated. " + "`shiny.experimental.ui.update_accordion()` is deprecated. " "This method will be removed in a future version, " - "please use `shiny.ui.accordion_panel_set()` instead." + "please use `shiny.ui.update_accordion()` instead." ) - return main_accordion_panel_set(id, values, session=session) + return main_update_accordion(id, show=values, session=session) # # Deprecated 2023-09-12 diff --git a/shiny/ui/__init__.py b/shiny/ui/__init__.py index cac3da16d..c1a646b4d 100644 --- a/shiny/ui/__init__.py +++ b/shiny/ui/__init__.py @@ -44,7 +44,7 @@ accordion_panel_close, accordion_panel_insert, accordion_panel_remove, - accordion_panel_set, + update_accordion, update_accordion_panel, ) @@ -200,7 +200,7 @@ "accordion_panel_close", "accordion_panel_insert", "accordion_panel_remove", - "accordion_panel_set", + "update_accordion", "update_accordion_panel", # _download_button "download_button", diff --git a/shiny/ui/_accordion.py b/shiny/ui/_accordion.py index 81eb35e95..507c5ab4e 100644 --- a/shiny/ui/_accordion.py +++ b/shiny/ui/_accordion.py @@ -5,6 +5,7 @@ from htmltools import Tag, TagAttrs, TagAttrValue, TagChild, css, tags +from .._docstring import add_example from .._namespaces import resolve_id_or_none from .._utils import drop_none from ..session import Session, require_active_session @@ -20,7 +21,7 @@ "accordion_panel_open", # TODO-barret-API: rename to `update_accordion_panel(open=True)`? "accordion_panel_insert", "accordion_panel_remove", - "accordion_panel_set", # TODO-barret-API: rename to `update_accordion(selected=)` + "update_accordion", # TODO-barret-API: rename to `update_accordion(selected=)` "update_accordion_panel", # TODO-barret-API: rename to `update_accordion()`? # TODO-barret-API: Add `toggle_accordion(values=list[str] | None)`? - Toggles all accordion panels if `values=None` or toggle the specified panels ) @@ -52,7 +53,7 @@ class AccordionPanel: See Also -------- * :func:`~shiny.ui.accordion` - * :func:`~shiny.ui.accordion_panel_set` + * :func:`~shiny.ui.update_accordion` * :func:`~shiny.ui.accordion_panel_open` * :func:`~shiny.ui.accordion_panel_close` * :func:`~shiny.ui.accordion_panel_insert` @@ -164,7 +165,7 @@ def tagify(self) -> Tag: return self.resolve().tagify() -# TODO-maindocs; @add_example() +@add_example() def accordion( *args: AccordionPanel | TagAttrs, id: Optional[str] = None, @@ -220,7 +221,7 @@ def accordion( See Also -------- * :func:`~shiny.ui.accordion_panel` - * :func:`~shiny.ui.accordion_panel_set` + * :func:`~shiny.ui.update_accordion` * :func:`~shiny.ui.accordion_panel_open` * :func:`~shiny.ui.accordion_panel_close` * :func:`~shiny.ui.accordion_panel_insert` @@ -287,7 +288,7 @@ def accordion( return tag -# TODO-maindocs; @add_example() +@add_example() def accordion_panel( title: TagChild, *args: TagChild | TagAttrs, @@ -326,7 +327,7 @@ def accordion_panel( See Also -------- * :func:`~shiny.ui.accordion` - * :func:`~shiny.ui.accordion_panel_set` + * :func:`~shiny.ui.update_accordion` * :func:`~shiny.ui.accordion_panel_open` * :func:`~shiny.ui.accordion_panel_close` * :func:`~shiny.ui.accordion_panel_insert` @@ -388,10 +389,11 @@ def _accordion_panel_action( ) -# TODO-maindocs; @add_example() -def accordion_panel_set( +@add_example() +def update_accordion( id: str, - values: bool | str | list[str], + *, + show: bool | str | list[str], session: Optional[Session] = None, ) -> None: """ @@ -405,7 +407,7 @@ def accordion_panel_set( ---------- id A string that matches an existing :func:`~shiny.ui.accordion`'s `id`. - values + show either a string or list of strings (used to identify particular :func:`~shiny.ui.accordion_panel`(s) by their `value`) or a `bool` to set the state of all panels. @@ -426,7 +428,7 @@ def accordion_panel_set( * :func:`~shiny.ui.accordion_panel_remove` * :func:`~shiny.ui.update_accordion_panel` """ - _accordion_panel_action(id=id, method="set", values=values, session=session) + _accordion_panel_action(id=id, method="set", values=show, session=session) # TODO-maindocs; @add_example() @@ -457,7 +459,7 @@ def accordion_panel_open( -------- * :func:`~shiny.ui.accordion` * :func:`~shiny.ui.accordion_panel` - * :func:`~shiny.ui.accordion_panel_set` + * :func:`~shiny.ui.update_accordion` * :func:`~shiny.ui.accordion_panel_close` * :func:`~shiny.ui.accordion_panel_insert` * :func:`~shiny.ui.accordion_panel_remove` @@ -494,7 +496,7 @@ def accordion_panel_close( -------- * :func:`~shiny.ui.accordion` * :func:`~shiny.ui.accordion_panel` - * :func:`~shiny.ui.accordion_panel_set` + * :func:`~shiny.ui.update_accordion` * :func:`~shiny.ui.accordion_panel_open` * :func:`~shiny.ui.accordion_panel_insert` * :func:`~shiny.ui.accordion_panel_remove` @@ -537,7 +539,7 @@ def accordion_panel_insert( -------- * :func:`~shiny.ui.accordion` * :func:`~shiny.ui.accordion_panel` - * :func:`~shiny.ui.accordion_panel_set` + * :func:`~shiny.ui.update_accordion` * :func:`~shiny.ui.accordion_panel_open` * :func:`~shiny.ui.accordion_panel_close` * :func:`~shiny.ui.accordion_panel_remove` @@ -583,7 +585,7 @@ def accordion_panel_remove( -------- * :func:`~shiny.ui.accordion` * :func:`~shiny.ui.accordion_panel` - * :func:`~shiny.ui.accordion_panel_set` + * :func:`~shiny.ui.update_accordion` * :func:`~shiny.ui.accordion_panel_open` * :func:`~shiny.ui.accordion_panel_close` * :func:`~shiny.ui.accordion_panel_insert` @@ -653,7 +655,7 @@ def update_accordion_panel( -------- * :func:`~shiny.ui.accordion` * :func:`~shiny.ui.accordion_panel` - * :func:`~shiny.ui.accordion_panel_set` + * :func:`~shiny.ui.update_accordion` * :func:`~shiny.ui.accordion_panel_open` * :func:`~shiny.ui.accordion_panel_close` * :func:`~shiny.ui.accordion_panel_insert` diff --git a/tests/e2e/bugs/0696-resolve-id/app.py b/tests/e2e/bugs/0696-resolve-id/app.py index d34079132..af9e894dd 100644 --- a/tests/e2e/bugs/0696-resolve-id/app.py +++ b/tests/e2e/bugs/0696-resolve-id/app.py @@ -411,7 +411,7 @@ def update_session( letter = letters[count % len(letters)] on_off = count % 2 == 1 with session_context(session): - ui.accordion_panel_set("accordion", letter) + ui.update_accordion("accordion", show=letter) ui.update_checkbox("input_checkbox", value=on_off) checkbox_group_letters = letters.copy() diff --git a/tests/e2e/ui/accordion/app.py b/tests/e2e/ui/accordion/app.py index e36805059..f3e3a677b 100644 --- a/tests/e2e/ui/accordion/app.py +++ b/tests/e2e/ui/accordion/app.py @@ -76,7 +76,7 @@ def _(): nonlocal has_alternate val = int(has_alternate) sections = [section for i, section in enumerate(sections) if i % 2 == val] - ui.accordion_panel_set("acc", sections) + ui.update_accordion("acc", show=sections) has_alternate = not has_alternate @reactive.Effect From bfaaa8908d3d80d1588c3f692bd9b515ed2bbeb4 Mon Sep 17 00:00:00 2001 From: Barret Schloerke Date: Fri, 27 Oct 2023 22:30:59 -0400 Subject: [PATCH 08/12] `ui.accordion_panel_insert()` -> `ui.insert_accordion_panel()` --- CHANGELOG.md | 2 +- .../app.py | 2 +- shiny/experimental/ui/_deprecated.py | 10 +++++----- shiny/ui/__init__.py | 4 ++-- shiny/ui/_accordion.py | 20 +++++++++---------- tests/e2e/ui/accordion/app.py | 6 +++--- 6 files changed, 22 insertions(+), 22 deletions(-) rename shiny/api-examples/{accordion_panel_insert => insert_accordion_panel}/app.py (90%) diff --git a/CHANGELOG.md b/CHANGELOG.md index 2a7f2f2a7..f58d1982d 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -56,7 +56,7 @@ The following methods have been moved from `shiny.experimental.ui` and integrate * Popover - Click-based context UI element * `popover()`, `update_popover()` * Accordion - Vertically collapsible UI element - * `accordion()`, `accordion_panel()`, `accordion_panel_close()`, `accordion_panel_insert()`, `accordion_panel_open()`, `accordion_panel_remove()`, `update_accordion()`, `update_accordion_panel()`, `Accordion`, `AccordionPanel` + * `accordion()`, `accordion_panel()`, `accordion_panel_close()`, `insert_accordion_panel()`, `accordion_panel_open()`, `accordion_panel_remove()`, `update_accordion()`, `update_accordion_panel()`, `Accordion`, `AccordionPanel` * Card - A general purpose container for grouping related UI elements together * `card()`, `card_header()`, `card_footer()`, `CardItem` * Valuebox - Opinionated container for displaying a value and title diff --git a/shiny/api-examples/accordion_panel_insert/app.py b/shiny/api-examples/insert_accordion_panel/app.py similarity index 90% rename from shiny/api-examples/accordion_panel_insert/app.py rename to shiny/api-examples/insert_accordion_panel/app.py index e5ac5d227..172460646 100644 --- a/shiny/api-examples/accordion_panel_insert/app.py +++ b/shiny/api-examples/insert_accordion_panel/app.py @@ -21,7 +21,7 @@ def server(input: Inputs, output: Outputs, session: Session): @reactive.Effect @reactive.event(input.add_panel) def _(): - ui.accordion_panel_insert("acc", make_panel(str(random.randint(0, 10000)))) + ui.insert_accordion_panel("acc", make_panel(str(random.randint(0, 10000)))) app = App(app_ui, server) diff --git a/shiny/experimental/ui/_deprecated.py b/shiny/experimental/ui/_deprecated.py index 7e43c1e7e..084e0480c 100644 --- a/shiny/experimental/ui/_deprecated.py +++ b/shiny/experimental/ui/_deprecated.py @@ -21,10 +21,10 @@ from ...ui import accordion as main_accordion from ...ui import accordion_panel as main_accordion_panel from ...ui import accordion_panel_close as main_accordion_panel_close -from ...ui import accordion_panel_insert as main_accordion_panel_insert from ...ui import accordion_panel_open as main_accordion_panel_open from ...ui import accordion_panel_remove as main_accordion_panel_remove from ...ui import input_text_area as main_input_text_area +from ...ui import insert_accordion_panel as main_insert_accordion_panel from ...ui import popover as main_popover from ...ui import tags from ...ui import tooltip as main_tooltip @@ -829,13 +829,13 @@ def accordion_panel_insert( position: Literal["after", "before"] = "after", session: Optional[Session] = None, ) -> None: - """Deprecated. Please use `shiny.ui.accordion_panel_insert()` instead.""" + """Deprecated. Please use `shiny.ui.insert_accordion_panel()` instead.""" warn_deprecated( - "`shiny.experimental.ui.accordion_panel_insert()` is deprecated. " + "`shiny.experimental.ui.insert_accordion_panel()` is deprecated. " "This method will be removed in a future version, " - "please use `shiny.ui.accordion_panel_insert()` instead." + "please use `shiny.ui.insert_accordion_panel()` instead." ) - return main_accordion_panel_insert( + return main_insert_accordion_panel( id, panel, target=target, diff --git a/shiny/ui/__init__.py b/shiny/ui/__init__.py index c1a646b4d..e7bb3b4ab 100644 --- a/shiny/ui/__init__.py +++ b/shiny/ui/__init__.py @@ -42,7 +42,7 @@ accordion_panel, accordion_panel_open, accordion_panel_close, - accordion_panel_insert, + insert_accordion_panel, accordion_panel_remove, update_accordion, update_accordion_panel, @@ -198,7 +198,7 @@ "accordion_panel", "accordion_panel_open", "accordion_panel_close", - "accordion_panel_insert", + "insert_accordion_panel", "accordion_panel_remove", "update_accordion", "update_accordion_panel", diff --git a/shiny/ui/_accordion.py b/shiny/ui/_accordion.py index 507c5ab4e..dd920b7e7 100644 --- a/shiny/ui/_accordion.py +++ b/shiny/ui/_accordion.py @@ -19,7 +19,7 @@ "accordion_panel", "accordion_panel_close", # TODO-barret-API: rename to `update_accordion_panel(open=False)`? "accordion_panel_open", # TODO-barret-API: rename to `update_accordion_panel(open=True)`? - "accordion_panel_insert", + "insert_accordion_panel", "accordion_panel_remove", "update_accordion", # TODO-barret-API: rename to `update_accordion(selected=)` "update_accordion_panel", # TODO-barret-API: rename to `update_accordion()`? @@ -56,7 +56,7 @@ class AccordionPanel: * :func:`~shiny.ui.update_accordion` * :func:`~shiny.ui.accordion_panel_open` * :func:`~shiny.ui.accordion_panel_close` - * :func:`~shiny.ui.accordion_panel_insert` + * :func:`~shiny.ui.insert_accordion_panel` * :func:`~shiny.ui.accordion_panel_remove` * :func:`~shiny.ui.update_accordion_panel` """ @@ -224,7 +224,7 @@ def accordion( * :func:`~shiny.ui.update_accordion` * :func:`~shiny.ui.accordion_panel_open` * :func:`~shiny.ui.accordion_panel_close` - * :func:`~shiny.ui.accordion_panel_insert` + * :func:`~shiny.ui.insert_accordion_panel` * :func:`~shiny.ui.accordion_panel_remove` * :func:`~shiny.ui.update_accordion_panel` """ @@ -330,7 +330,7 @@ def accordion_panel( * :func:`~shiny.ui.update_accordion` * :func:`~shiny.ui.accordion_panel_open` * :func:`~shiny.ui.accordion_panel_close` - * :func:`~shiny.ui.accordion_panel_insert` + * :func:`~shiny.ui.insert_accordion_panel` * :func:`~shiny.ui.accordion_panel_remove` * :func:`~shiny.ui.update_accordion_panel` """ @@ -424,7 +424,7 @@ def update_accordion( * :func:`~shiny.ui.accordion_panel` * :func:`~shiny.ui.accordion_panel_open` * :func:`~shiny.ui.accordion_panel_close` - * :func:`~shiny.ui.accordion_panel_insert` + * :func:`~shiny.ui.insert_accordion_panel` * :func:`~shiny.ui.accordion_panel_remove` * :func:`~shiny.ui.update_accordion_panel` """ @@ -461,7 +461,7 @@ def accordion_panel_open( * :func:`~shiny.ui.accordion_panel` * :func:`~shiny.ui.update_accordion` * :func:`~shiny.ui.accordion_panel_close` - * :func:`~shiny.ui.accordion_panel_insert` + * :func:`~shiny.ui.insert_accordion_panel` * :func:`~shiny.ui.accordion_panel_remove` * :func:`~shiny.ui.update_accordion_panel` """ @@ -498,7 +498,7 @@ def accordion_panel_close( * :func:`~shiny.ui.accordion_panel` * :func:`~shiny.ui.update_accordion` * :func:`~shiny.ui.accordion_panel_open` - * :func:`~shiny.ui.accordion_panel_insert` + * :func:`~shiny.ui.insert_accordion_panel` * :func:`~shiny.ui.accordion_panel_remove` * :func:`~shiny.ui.update_accordion_panel` """ @@ -506,7 +506,7 @@ def accordion_panel_close( # TODO-maindocs; @add_example() -def accordion_panel_insert( +def insert_accordion_panel( id: str, panel: AccordionPanel, target: Optional[str] = None, @@ -588,7 +588,7 @@ def accordion_panel_remove( * :func:`~shiny.ui.update_accordion` * :func:`~shiny.ui.accordion_panel_open` * :func:`~shiny.ui.accordion_panel_close` - * :func:`~shiny.ui.accordion_panel_insert` + * :func:`~shiny.ui.insert_accordion_panel` * :func:`~shiny.ui.update_accordion_panel` """ if not isinstance(target, list): @@ -658,7 +658,7 @@ def update_accordion_panel( * :func:`~shiny.ui.update_accordion` * :func:`~shiny.ui.accordion_panel_open` * :func:`~shiny.ui.accordion_panel_close` - * :func:`~shiny.ui.accordion_panel_insert` + * :func:`~shiny.ui.insert_accordion_panel` * :func:`~shiny.ui.accordion_panel_remove` """ diff --git a/tests/e2e/ui/accordion/app.py b/tests/e2e/ui/accordion/app.py index f3e3a677b..270b045d5 100644 --- a/tests/e2e/ui/accordion/app.py +++ b/tests/e2e/ui/accordion/app.py @@ -87,9 +87,9 @@ def _(): if has_efg: ui.accordion_panel_remove("acc", ["Section E", "Section F", "Section G"]) else: - ui.accordion_panel_insert("acc", make_panel("G"), "Section F") - ui.accordion_panel_insert("acc", make_panel("F"), "Section E") - ui.accordion_panel_insert("acc", make_panel("E"), "Section D") + ui.insert_accordion_panel("acc", make_panel("G"), "Section F") + ui.insert_accordion_panel("acc", make_panel("F"), "Section E") + ui.insert_accordion_panel("acc", make_panel("E"), "Section D") has_efg = not has_efg From 8e1dfb1eb549b5ad82babdc1f50567dd1146798a Mon Sep 17 00:00:00 2001 From: Barret Schloerke Date: Fri, 27 Oct 2023 22:34:09 -0400 Subject: [PATCH 09/12] `ui.accordion_panel_remove()` -> `ui.insert_accordion_panel()` --- CHANGELOG.md | 2 +- .../app.py | 2 +- shiny/experimental/ui/_deprecated.py | 12 +++++------ shiny/ui/__init__.py | 4 ++-- shiny/ui/_accordion.py | 20 +++++++++---------- tests/e2e/ui/accordion/app.py | 2 +- 6 files changed, 21 insertions(+), 21 deletions(-) rename shiny/api-examples/{accordion_panel_remove => remove_accordion_panel}/app.py (94%) diff --git a/CHANGELOG.md b/CHANGELOG.md index f58d1982d..71f235694 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -56,7 +56,7 @@ The following methods have been moved from `shiny.experimental.ui` and integrate * Popover - Click-based context UI element * `popover()`, `update_popover()` * Accordion - Vertically collapsible UI element - * `accordion()`, `accordion_panel()`, `accordion_panel_close()`, `insert_accordion_panel()`, `accordion_panel_open()`, `accordion_panel_remove()`, `update_accordion()`, `update_accordion_panel()`, `Accordion`, `AccordionPanel` + * `accordion()`, `accordion_panel()`, `accordion_panel_close()`, `insert_accordion_panel()`, `accordion_panel_open()`, `remove_accordion_panel()`, `update_accordion()`, `update_accordion_panel()`, `Accordion`, `AccordionPanel` * Card - A general purpose container for grouping related UI elements together * `card()`, `card_header()`, `card_footer()`, `CardItem` * Valuebox - Opinionated container for displaying a value and title diff --git a/shiny/api-examples/accordion_panel_remove/app.py b/shiny/api-examples/remove_accordion_panel/app.py similarity index 94% rename from shiny/api-examples/accordion_panel_remove/app.py rename to shiny/api-examples/remove_accordion_panel/app.py index cedca4f51..b04785498 100644 --- a/shiny/api-examples/accordion_panel_remove/app.py +++ b/shiny/api-examples/remove_accordion_panel/app.py @@ -37,7 +37,7 @@ def _(): return # Remove panel - ui.accordion_panel_remove("acc", f"Section { user_choices.pop() }") + ui.remove_accordion_panel("acc", f"Section { user_choices.pop() }") label = "No more panels to remove!" if len(user_choices) > 0: diff --git a/shiny/experimental/ui/_deprecated.py b/shiny/experimental/ui/_deprecated.py index 084e0480c..69453e9bb 100644 --- a/shiny/experimental/ui/_deprecated.py +++ b/shiny/experimental/ui/_deprecated.py @@ -22,10 +22,10 @@ from ...ui import accordion_panel as main_accordion_panel from ...ui import accordion_panel_close as main_accordion_panel_close from ...ui import accordion_panel_open as main_accordion_panel_open -from ...ui import accordion_panel_remove as main_accordion_panel_remove from ...ui import input_text_area as main_input_text_area from ...ui import insert_accordion_panel as main_insert_accordion_panel from ...ui import popover as main_popover +from ...ui import remove_accordion_panel as main_remove_accordion_panel from ...ui import tags from ...ui import tooltip as main_tooltip from ...ui import update_accordion as main_update_accordion @@ -784,7 +784,7 @@ def accordion_panel_set( ) -> None: """Deprecated. Please use `shiny.ui.update_accordion()` instead.""" warn_deprecated( - "`shiny.experimental.ui.update_accordion()` is deprecated. " + "`shiny.experimental.ui.accordion_panel_set()` is deprecated. " "This method will be removed in a future version, " "please use `shiny.ui.update_accordion()` instead." ) @@ -831,7 +831,7 @@ def accordion_panel_insert( ) -> None: """Deprecated. Please use `shiny.ui.insert_accordion_panel()` instead.""" warn_deprecated( - "`shiny.experimental.ui.insert_accordion_panel()` is deprecated. " + "`shiny.experimental.ui.accordion_panel_insert()` is deprecated. " "This method will be removed in a future version, " "please use `shiny.ui.insert_accordion_panel()` instead." ) @@ -850,13 +850,13 @@ def accordion_panel_remove( target: str | list[str], session: Optional[Session] = None, ) -> None: - """Deprecated. Please use `shiny.ui.accordion_panel_remove()` instead.""" + """Deprecated. Please use `shiny.ui.remove_accordion_panel()` instead.""" warn_deprecated( "`shiny.experimental.ui.accordion_panel_remove()` is deprecated. " "This method will be removed in a future version, " - "please use `shiny.ui.accordion_panel_remove()` instead." + "please use `shiny.ui.remove_accordion_panel()` instead." ) - return main_accordion_panel_remove( + return main_remove_accordion_panel( id, target=target, session=session, diff --git a/shiny/ui/__init__.py b/shiny/ui/__init__.py index e7bb3b4ab..e9a5501c2 100644 --- a/shiny/ui/__init__.py +++ b/shiny/ui/__init__.py @@ -43,7 +43,7 @@ accordion_panel_open, accordion_panel_close, insert_accordion_panel, - accordion_panel_remove, + remove_accordion_panel, update_accordion, update_accordion_panel, ) @@ -199,7 +199,7 @@ "accordion_panel_open", "accordion_panel_close", "insert_accordion_panel", - "accordion_panel_remove", + "remove_accordion_panel", "update_accordion", "update_accordion_panel", # _download_button diff --git a/shiny/ui/_accordion.py b/shiny/ui/_accordion.py index dd920b7e7..b9e750c8f 100644 --- a/shiny/ui/_accordion.py +++ b/shiny/ui/_accordion.py @@ -20,7 +20,7 @@ "accordion_panel_close", # TODO-barret-API: rename to `update_accordion_panel(open=False)`? "accordion_panel_open", # TODO-barret-API: rename to `update_accordion_panel(open=True)`? "insert_accordion_panel", - "accordion_panel_remove", + "remove_accordion_panel", "update_accordion", # TODO-barret-API: rename to `update_accordion(selected=)` "update_accordion_panel", # TODO-barret-API: rename to `update_accordion()`? # TODO-barret-API: Add `toggle_accordion(values=list[str] | None)`? - Toggles all accordion panels if `values=None` or toggle the specified panels @@ -57,7 +57,7 @@ class AccordionPanel: * :func:`~shiny.ui.accordion_panel_open` * :func:`~shiny.ui.accordion_panel_close` * :func:`~shiny.ui.insert_accordion_panel` - * :func:`~shiny.ui.accordion_panel_remove` + * :func:`~shiny.ui.remove_accordion_panel` * :func:`~shiny.ui.update_accordion_panel` """ @@ -225,7 +225,7 @@ def accordion( * :func:`~shiny.ui.accordion_panel_open` * :func:`~shiny.ui.accordion_panel_close` * :func:`~shiny.ui.insert_accordion_panel` - * :func:`~shiny.ui.accordion_panel_remove` + * :func:`~shiny.ui.remove_accordion_panel` * :func:`~shiny.ui.update_accordion_panel` """ @@ -331,7 +331,7 @@ def accordion_panel( * :func:`~shiny.ui.accordion_panel_open` * :func:`~shiny.ui.accordion_panel_close` * :func:`~shiny.ui.insert_accordion_panel` - * :func:`~shiny.ui.accordion_panel_remove` + * :func:`~shiny.ui.remove_accordion_panel` * :func:`~shiny.ui.update_accordion_panel` """ @@ -425,7 +425,7 @@ def update_accordion( * :func:`~shiny.ui.accordion_panel_open` * :func:`~shiny.ui.accordion_panel_close` * :func:`~shiny.ui.insert_accordion_panel` - * :func:`~shiny.ui.accordion_panel_remove` + * :func:`~shiny.ui.remove_accordion_panel` * :func:`~shiny.ui.update_accordion_panel` """ _accordion_panel_action(id=id, method="set", values=show, session=session) @@ -462,7 +462,7 @@ def accordion_panel_open( * :func:`~shiny.ui.update_accordion` * :func:`~shiny.ui.accordion_panel_close` * :func:`~shiny.ui.insert_accordion_panel` - * :func:`~shiny.ui.accordion_panel_remove` + * :func:`~shiny.ui.remove_accordion_panel` * :func:`~shiny.ui.update_accordion_panel` """ _accordion_panel_action(id=id, method="open", values=values, session=session) @@ -499,7 +499,7 @@ def accordion_panel_close( * :func:`~shiny.ui.update_accordion` * :func:`~shiny.ui.accordion_panel_open` * :func:`~shiny.ui.insert_accordion_panel` - * :func:`~shiny.ui.accordion_panel_remove` + * :func:`~shiny.ui.remove_accordion_panel` * :func:`~shiny.ui.update_accordion_panel` """ _accordion_panel_action(id=id, method="close", values=values, session=session) @@ -542,7 +542,7 @@ def insert_accordion_panel( * :func:`~shiny.ui.update_accordion` * :func:`~shiny.ui.accordion_panel_open` * :func:`~shiny.ui.accordion_panel_close` - * :func:`~shiny.ui.accordion_panel_remove` + * :func:`~shiny.ui.remove_accordion_panel` * :func:`~shiny.ui.update_accordion_panel` """ @@ -560,7 +560,7 @@ def insert_accordion_panel( # TODO-maindocs; @add_example() -def accordion_panel_remove( +def remove_accordion_panel( id: str, target: str | list[str], session: Optional[Session] = None, @@ -659,7 +659,7 @@ def update_accordion_panel( * :func:`~shiny.ui.accordion_panel_open` * :func:`~shiny.ui.accordion_panel_close` * :func:`~shiny.ui.insert_accordion_panel` - * :func:`~shiny.ui.accordion_panel_remove` + * :func:`~shiny.ui.remove_accordion_panel` """ session = require_active_session(session) diff --git a/tests/e2e/ui/accordion/app.py b/tests/e2e/ui/accordion/app.py index 270b045d5..43dc0299f 100644 --- a/tests/e2e/ui/accordion/app.py +++ b/tests/e2e/ui/accordion/app.py @@ -85,7 +85,7 @@ def _(): nonlocal has_efg if has_efg: - ui.accordion_panel_remove("acc", ["Section E", "Section F", "Section G"]) + ui.remove_accordion_panel("acc", ["Section E", "Section F", "Section G"]) else: ui.insert_accordion_panel("acc", make_panel("G"), "Section F") ui.insert_accordion_panel("acc", make_panel("F"), "Section E") From 0211d92219c70a75fb39f5b64e5e8928c0da9f34 Mon Sep 17 00:00:00 2001 From: Barret Schloerke Date: Fri, 27 Oct 2023 23:30:50 -0400 Subject: [PATCH 10/12] Absorb `ui.accordion_panel_open()` and `ui.accordion_panel_close()` into `update_accordion_panel(show=)` --- CHANGELOG.md | 2 +- .../api-examples/accordion_panel_close/app.py | 21 ---- .../api-examples/accordion_panel_open/app.py | 21 ---- .../app.py | 8 +- shiny/experimental/ui/_deprecated.py | 31 +++-- shiny/ui/__init__.py | 4 - shiny/ui/_accordion.py | 118 ++++-------------- shiny/ui/_input_check_radio.py | 8 +- tests/e2e/ui/accordion/app.py | 10 +- 9 files changed, 64 insertions(+), 159 deletions(-) delete mode 100644 shiny/api-examples/accordion_panel_close/app.py delete mode 100644 shiny/api-examples/accordion_panel_open/app.py rename shiny/api-examples/{accordion_panel_update => update_accordion_panel}/app.py (71%) diff --git a/CHANGELOG.md b/CHANGELOG.md index 71f235694..181665ec0 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -56,7 +56,7 @@ The following methods have been moved from `shiny.experimental.ui` and integrate * Popover - Click-based context UI element * `popover()`, `update_popover()` * Accordion - Vertically collapsible UI element - * `accordion()`, `accordion_panel()`, `accordion_panel_close()`, `insert_accordion_panel()`, `accordion_panel_open()`, `remove_accordion_panel()`, `update_accordion()`, `update_accordion_panel()`, `Accordion`, `AccordionPanel` + * `accordion()`, `accordion_panel()`, `insert_accordion_panel()`, `remove_accordion_panel()`, `update_accordion()`, `update_accordion_panel()`, `Accordion`, `AccordionPanel` * Card - A general purpose container for grouping related UI elements together * `card()`, `card_header()`, `card_footer()`, `CardItem` * Valuebox - Opinionated container for displaying a value and title diff --git a/shiny/api-examples/accordion_panel_close/app.py b/shiny/api-examples/accordion_panel_close/app.py deleted file mode 100644 index 9f396c7e0..000000000 --- a/shiny/api-examples/accordion_panel_close/app.py +++ /dev/null @@ -1,21 +0,0 @@ -from shiny import App, Inputs, Outputs, Session, reactive, ui - -items = [ - ui.accordion_panel(f"Section {letter}", f"Some narrative for section {letter}") - for letter in "ABCDE" -] - -app_ui = ui.page_fluid( - ui.input_action_button("close_acc", "Close Section C", class_="mt-3 mb-3"), - ui.accordion(*items, id="acc", multiple=True), -) - - -def server(input: Inputs, output: Outputs, session: Session): - @reactive.Effect - @reactive.event(input.close_acc) - def _(): - ui.accordion_panel_close("acc", "Section C") - - -app = App(app_ui, server) diff --git a/shiny/api-examples/accordion_panel_open/app.py b/shiny/api-examples/accordion_panel_open/app.py deleted file mode 100644 index 4d92bc6c3..000000000 --- a/shiny/api-examples/accordion_panel_open/app.py +++ /dev/null @@ -1,21 +0,0 @@ -from shiny import App, Inputs, Outputs, Session, reactive, ui - -items = [ - ui.accordion_panel(f"Section {letter}", f"Some narrative for section {letter}") - for letter in "ABCDE" -] - -app_ui = ui.page_fluid( - ui.input_action_button("open_acc", "Open Section C", class_="mt-3 mb-3"), - ui.accordion(*items, id="acc", multiple=True), -) - - -def server(input: Inputs, output: Outputs, session: Session): - @reactive.Effect - @reactive.event(input.open_acc) - def _(): - ui.accordion_panel_open("acc", "Section C") - - -app = App(app_ui, server) diff --git a/shiny/api-examples/accordion_panel_update/app.py b/shiny/api-examples/update_accordion_panel/app.py similarity index 71% rename from shiny/api-examples/accordion_panel_update/app.py rename to shiny/api-examples/update_accordion_panel/app.py index f406b8890..f96fce999 100644 --- a/shiny/api-examples/accordion_panel_update/app.py +++ b/shiny/api-examples/update_accordion_panel/app.py @@ -12,7 +12,7 @@ def make_panel(letter: str) -> ui.AccordionPanel: items = [make_panel(letter) for letter in "ABCDE"] app_ui = ui.page_fluid( - ui.input_switch("update_panel", "Update Sections"), + ui.input_switch("update_panel", "Update (and open) Sections"), ui.accordion(*items, id="acc", multiple=True), ) @@ -22,13 +22,19 @@ def server(input: Inputs, output: Outputs, session: Session): @reactive.event(input.update_panel) def _(): txt = " (updated)" if input.update_panel() else "" + show = bool(input.update_panel() % 2 == 1) for letter in "ABCDE": ui.update_accordion_panel( "acc", f"sec_{letter}", f"Some{txt} narrative for section {letter}", title=f"Section {letter}{txt}", + # Open Accordion Panel to see updated contents + show=show, ) + next_show_txt = "close" if show else "open" + + ui.update_switch("update_panel", label=f"Update (and {next_show_txt}) Sections") app = App(app_ui, server) diff --git a/shiny/experimental/ui/_deprecated.py b/shiny/experimental/ui/_deprecated.py index 69453e9bb..9fb3f1601 100644 --- a/shiny/experimental/ui/_deprecated.py +++ b/shiny/experimental/ui/_deprecated.py @@ -20,8 +20,6 @@ from ...ui import AccordionPanel as MainAccordionPanel from ...ui import accordion as main_accordion from ...ui import accordion_panel as main_accordion_panel -from ...ui import accordion_panel_close as main_accordion_panel_close -from ...ui import accordion_panel_open as main_accordion_panel_open from ...ui import input_text_area as main_input_text_area from ...ui import insert_accordion_panel as main_insert_accordion_panel from ...ui import popover as main_popover @@ -797,13 +795,22 @@ def accordion_panel_open( values: bool | str | list[str], session: Optional[Session] = None, ) -> None: - """Deprecated. Please use `shiny.ui.accordion_panel_open()` instead.""" + """Deprecated. Please use `shiny.ui.update_accordion_panel(id, value, show=True)` or `shiny.ui.update_accordion(id, show = True)` instead.""" warn_deprecated( "`shiny.experimental.ui.accordion_panel_open()` is deprecated. " "This method will be removed in a future version, " - "please use `shiny.ui.accordion_panel_open()` instead." + "please use `shiny.ui.shiny.ui.update_accordion_panel(id, value, show=True)` or `shiny.ui.update_accordion(id, show = True)` instead." ) - return main_accordion_panel_open(id, values, session=session) + + if isinstance(values, bool): + main_update_accordion(id, show=True, session=session) + return + + if not isinstance(values, list): + values = [values] + + for value in values: + main_update_accordion_panel(id, value, show=True, session=session) # # Deprecated 2023-09-12 @@ -812,13 +819,21 @@ def accordion_panel_close( values: bool | str | list[str], session: Optional[Session] = None, ) -> None: - """Deprecated. Please use `shiny.ui.accordion_panel_close()` instead.""" + """Deprecated. Please use `shiny.ui.update_accordion_panel(id, value, show=False)` or `shiny.ui.update_accordion(id, show = False)` instead.""" warn_deprecated( "`shiny.experimental.ui.accordion_panel_close()` is deprecated. " "This method will be removed in a future version, " - "please use `shiny.ui.accordion_panel_close()` instead." + "please use `shiny.ui.update_accordion_panel(id, value, show=False)` or `shiny.ui.update_accordion(id, show = False)` instead." ) - return main_accordion_panel_close(id, values, session=session) + if isinstance(values, bool): + main_update_accordion(id, show=False, session=session) + return + + if not isinstance(values, list): + values = [values] + + for value in values: + main_update_accordion_panel(id, value, show=False, session=session) # # Deprecated 2023-09-12 diff --git a/shiny/ui/__init__.py b/shiny/ui/__init__.py index e9a5501c2..28f77e2e6 100644 --- a/shiny/ui/__init__.py +++ b/shiny/ui/__init__.py @@ -40,8 +40,6 @@ AccordionPanel, accordion, accordion_panel, - accordion_panel_open, - accordion_panel_close, insert_accordion_panel, remove_accordion_panel, update_accordion, @@ -196,8 +194,6 @@ "AccordionPanel", "accordion", "accordion_panel", - "accordion_panel_open", - "accordion_panel_close", "insert_accordion_panel", "remove_accordion_panel", "update_accordion", diff --git a/shiny/ui/_accordion.py b/shiny/ui/_accordion.py index b9e750c8f..8e1ddf00b 100644 --- a/shiny/ui/_accordion.py +++ b/shiny/ui/_accordion.py @@ -17,13 +17,10 @@ __all__ = ( "accordion", "accordion_panel", - "accordion_panel_close", # TODO-barret-API: rename to `update_accordion_panel(open=False)`? - "accordion_panel_open", # TODO-barret-API: rename to `update_accordion_panel(open=True)`? "insert_accordion_panel", "remove_accordion_panel", - "update_accordion", # TODO-barret-API: rename to `update_accordion(selected=)` - "update_accordion_panel", # TODO-barret-API: rename to `update_accordion()`? - # TODO-barret-API: Add `toggle_accordion(values=list[str] | None)`? - Toggles all accordion panels if `values=None` or toggle the specified panels + "update_accordion", + "update_accordion_panel", ) @@ -54,8 +51,6 @@ class AccordionPanel: -------- * :func:`~shiny.ui.accordion` * :func:`~shiny.ui.update_accordion` - * :func:`~shiny.ui.accordion_panel_open` - * :func:`~shiny.ui.accordion_panel_close` * :func:`~shiny.ui.insert_accordion_panel` * :func:`~shiny.ui.remove_accordion_panel` * :func:`~shiny.ui.update_accordion_panel` @@ -222,8 +217,6 @@ def accordion( -------- * :func:`~shiny.ui.accordion_panel` * :func:`~shiny.ui.update_accordion` - * :func:`~shiny.ui.accordion_panel_open` - * :func:`~shiny.ui.accordion_panel_close` * :func:`~shiny.ui.insert_accordion_panel` * :func:`~shiny.ui.remove_accordion_panel` * :func:`~shiny.ui.update_accordion_panel` @@ -328,8 +321,6 @@ def accordion_panel( -------- * :func:`~shiny.ui.accordion` * :func:`~shiny.ui.update_accordion` - * :func:`~shiny.ui.accordion_panel_open` - * :func:`~shiny.ui.accordion_panel_close` * :func:`~shiny.ui.insert_accordion_panel` * :func:`~shiny.ui.remove_accordion_panel` * :func:`~shiny.ui.update_accordion_panel` @@ -422,90 +413,18 @@ def update_accordion( -------- * :func:`~shiny.ui.accordion` * :func:`~shiny.ui.accordion_panel` - * :func:`~shiny.ui.accordion_panel_open` - * :func:`~shiny.ui.accordion_panel_close` * :func:`~shiny.ui.insert_accordion_panel` * :func:`~shiny.ui.remove_accordion_panel` * :func:`~shiny.ui.update_accordion_panel` """ - _accordion_panel_action(id=id, method="set", values=show, session=session) - - -# TODO-maindocs; @add_example() -def accordion_panel_open( - id: str, - values: bool | str | list[str], - session: Optional[Session] = None, -) -> None: - """ - Open a set of :func:`~shiny.ui.accordion_panel`s. - - Parameters - ---------- - id - A string that matches an existing :func:`~shiny.ui.accordion`'s `id`. - values - either a string or list of strings (used to identify particular - :func:`~shiny.ui.accordion_panel`(s) by their `value`) or a `bool` to set the state of all - panels. - session - A shiny session object (the default should almost always be used). - - References - ---------- - [Bootstrap Accordion](https://getbootstrap.com/docs/5.3/components/accordion/) - - See Also - -------- - * :func:`~shiny.ui.accordion` - * :func:`~shiny.ui.accordion_panel` - * :func:`~shiny.ui.update_accordion` - * :func:`~shiny.ui.accordion_panel_close` - * :func:`~shiny.ui.insert_accordion_panel` - * :func:`~shiny.ui.remove_accordion_panel` - * :func:`~shiny.ui.update_accordion_panel` - """ - _accordion_panel_action(id=id, method="open", values=values, session=session) - - -# TODO-maindocs; @add_example() -def accordion_panel_close( - id: str, - values: bool | str | list[str], - session: Optional[Session] = None, -) -> None: - """ - Close a set of accordion panels in an :func:`~shiny.ui.accordion`. - - Parameters - ---------- - id - A string that matches an existing :func:`~shiny.ui.accordion`'s `id`. - values - either a string or list of strings (used to identify particular - :func:`~shiny.ui.accordion_panel`(s) by their `value`) or a `bool` to set the state of all - panels. - session - A shiny session object (the default should almost always be used). - - References - ---------- - [Bootstrap Accordion](https://getbootstrap.com/docs/5.3/components/accordion/) - - See Also - -------- - * :func:`~shiny.ui.accordion` - * :func:`~shiny.ui.accordion_panel` - * :func:`~shiny.ui.update_accordion` - * :func:`~shiny.ui.accordion_panel_open` - * :func:`~shiny.ui.insert_accordion_panel` - * :func:`~shiny.ui.remove_accordion_panel` - * :func:`~shiny.ui.update_accordion_panel` - """ - _accordion_panel_action(id=id, method="close", values=values, session=session) + if show is False: + show_val = [] + else: + show_val = show + _accordion_panel_action(id=id, method="set", values=show_val, session=session) -# TODO-maindocs; @add_example() +@add_example() def insert_accordion_panel( id: str, panel: AccordionPanel, @@ -540,8 +459,6 @@ def insert_accordion_panel( * :func:`~shiny.ui.accordion` * :func:`~shiny.ui.accordion_panel` * :func:`~shiny.ui.update_accordion` - * :func:`~shiny.ui.accordion_panel_open` - * :func:`~shiny.ui.accordion_panel_close` * :func:`~shiny.ui.remove_accordion_panel` * :func:`~shiny.ui.update_accordion_panel` """ @@ -559,7 +476,7 @@ def insert_accordion_panel( ) -# TODO-maindocs; @add_example() +@add_example() def remove_accordion_panel( id: str, target: str | list[str], @@ -586,8 +503,6 @@ def remove_accordion_panel( * :func:`~shiny.ui.accordion` * :func:`~shiny.ui.accordion_panel` * :func:`~shiny.ui.update_accordion` - * :func:`~shiny.ui.accordion_panel_open` - * :func:`~shiny.ui.accordion_panel_close` * :func:`~shiny.ui.insert_accordion_panel` * :func:`~shiny.ui.update_accordion_panel` """ @@ -613,7 +528,7 @@ def _missing_none_x(x: T | None | MISSING_TYPE) -> T | Literal[""] | None: return x -# TODO-maindocs; @add_example() +@add_example() def update_accordion_panel( id: str, target: str, @@ -621,6 +536,7 @@ def update_accordion_panel( title: TagChild | None | MISSING_TYPE = MISSING, value: str | None | MISSING_TYPE = MISSING, icon: TagChild | None | MISSING_TYPE = MISSING, + show: Optional[bool] = None, session: Optional[Session] = None, ) -> None: """ @@ -656,14 +572,22 @@ def update_accordion_panel( * :func:`~shiny.ui.accordion` * :func:`~shiny.ui.accordion_panel` * :func:`~shiny.ui.update_accordion` - * :func:`~shiny.ui.accordion_panel_open` - * :func:`~shiny.ui.accordion_panel_close` * :func:`~shiny.ui.insert_accordion_panel` * :func:`~shiny.ui.remove_accordion_panel` """ session = require_active_session(session) + # If `show` is given, then we need to open/close the targeted panel + # Perform before changing `value` at the same time. + if show is not None: + _accordion_panel_action( + id=id, + method="open" if bool(show) else "close", + values=[target], + session=session, + ) + title = _missing_none_x(title) value = _missing_none_x(value) icon = _missing_none_x(icon) diff --git a/shiny/ui/_input_check_radio.py b/shiny/ui/_input_check_radio.py index 86beed2a0..d00566364 100644 --- a/shiny/ui/_input_check_radio.py +++ b/shiny/ui/_input_check_radio.py @@ -148,10 +148,16 @@ def _bslib_input_checkbox( id=resolve_id(id), class_="form-check-input", type="checkbox", + role="switch", checked="checked" if value else None, ), " ", - tags.label(label, class_="form-check-label", for_=resolve_id(id)), + tags.label( + # Must be wrapped in `span` for update_switch(label=) method to work + tags.span(label), + class_="form-check-label", + for_=resolve_id(id), + ), class_=class_, ), components_dependency(), diff --git a/tests/e2e/ui/accordion/app.py b/tests/e2e/ui/accordion/app.py index 43dc0299f..e2b33fb25 100644 --- a/tests/e2e/ui/accordion/app.py +++ b/tests/e2e/ui/accordion/app.py @@ -42,19 +42,19 @@ def _(): with reactive.isolate(): if "Section B" in acc(): - ui.accordion_panel_close("acc", "Section B") + ui.update_accordion_panel("acc", "Section B", show=False) else: - ui.accordion_panel_open("acc", "Section B") + ui.update_accordion_panel("acc", "Section B", show=True) @reactive.Effect def _(): req(input.open_all()) - ui.accordion_panel_open("acc", True) + ui.update_accordion("acc", show=True) @reactive.Effect def _(): req(input.close_all()) - ui.accordion_panel_close("acc", True) + ui.update_accordion("acc", show=False) has_efg = False has_alternate = True @@ -112,7 +112,7 @@ def _(): # print(acc()) if "Section A" not in acc(): ui.notification_show("Opening Section A", duration=2) - ui.accordion_panel_open("acc", "Section A") + ui.update_accordion_panel("acc", "Section A", show=True) ui.update_accordion_panel( "acc", "Section A", From d64aea9687efd33f73b5b01c31f9c34557b0ab5f Mon Sep 17 00:00:00 2001 From: Barret Schloerke Date: Fri, 27 Oct 2023 23:50:28 -0400 Subject: [PATCH 11/12] Skip flaky test on webkit --- tests/e2e/bugs/0696-resolve-id/test_0696_resolve_id.py | 3 +++ 1 file changed, 3 insertions(+) diff --git a/tests/e2e/bugs/0696-resolve-id/test_0696_resolve_id.py b/tests/e2e/bugs/0696-resolve-id/test_0696_resolve_id.py index f397f58f8..94fad582c 100644 --- a/tests/e2e/bugs/0696-resolve-id/test_0696_resolve_id.py +++ b/tests/e2e/bugs/0696-resolve-id/test_0696_resolve_id.py @@ -5,6 +5,7 @@ import os from pathlib import Path +import pytest from conftest import ShinyAppProc from controls import ( DownloadButton, @@ -105,6 +106,8 @@ def expect_default_outputs(page: Page, module_id: str): expect_outputs(page, module_id, "a", 0) +# Python 3.8 and sidebars do not seem to work on webkit. Skipping test on webkit +@pytest.mark.skip_browser("webkit") def test_module_support(page: Page, local_app: ShinyAppProc) -> None: page.goto(local_app.url) From 163662d1521808f23635db9d47ba6bffaed15664 Mon Sep 17 00:00:00 2001 From: Barret Schloerke Date: Fri, 27 Oct 2023 23:52:04 -0400 Subject: [PATCH 12/12] Update test_0696_resolve_id.py --- tests/e2e/bugs/0696-resolve-id/test_0696_resolve_id.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests/e2e/bugs/0696-resolve-id/test_0696_resolve_id.py b/tests/e2e/bugs/0696-resolve-id/test_0696_resolve_id.py index 94fad582c..d04618976 100644 --- a/tests/e2e/bugs/0696-resolve-id/test_0696_resolve_id.py +++ b/tests/e2e/bugs/0696-resolve-id/test_0696_resolve_id.py @@ -106,7 +106,7 @@ def expect_default_outputs(page: Page, module_id: str): expect_outputs(page, module_id, "a", 0) -# Python 3.8 and sidebars do not seem to work on webkit. Skipping test on webkit +# Sidebars do not seem to work on webkit. Skipping test on webkit @pytest.mark.skip_browser("webkit") def test_module_support(page: Page, local_app: ShinyAppProc) -> None: page.goto(local_app.url)