Skip to content

Commit 6ac04b8

Browse files
authored
feat(ui): Revise accordion "open" logic (#2112)
1 parent 25a0fb8 commit 6ac04b8

File tree

3 files changed

+38
-35
lines changed

3 files changed

+38
-35
lines changed

CHANGELOG.md

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,8 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
1515

1616
* Fixed `ui.tooltip()`'s `options` parameter to properly pass Bootstrap tooltip options to the underlying web component. (#2101)
1717

18+
* Revised `accordion()`'s `open` logic to close all panels when an empty list is passed. (#2109)
19+
1820
## [1.5.0] - 2025-09-11
1921

2022
### New features

shiny/express/ui/_cm_components.py

Lines changed: 8 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,7 @@
99
from ... import ui
1010
from ..._deprecated import warn_deprecated
1111
from ..._docstring import add_example, no_example
12-
from ...types import DEPRECATED, MISSING, MISSING_TYPE
12+
from ...types import DEPRECATED, MISSING, MISSING_TYPE, ListOrTuple
1313
from ...ui._accordion import AccordionPanel
1414
from ...ui._card import CardItem
1515
from ...ui._layout_columns import BreakpointsUser
@@ -649,7 +649,7 @@ def card_footer(
649649
def accordion(
650650
*,
651651
id: Optional[str] = None,
652-
open: Optional[bool | str | list[str]] = None,
652+
open: Optional[bool | str | ListOrTuple[str]] = None,
653653
multiple: bool = True,
654654
class_: Optional[str] = None,
655655
width: Optional[CssUnit] = None,
@@ -669,11 +669,12 @@ def accordion(
669669
value will correspond to the :func:`~shiny.ui.accordion_panel`'s
670670
`value` argument.
671671
open
672-
A list of :func:`~shiny.ui.accordion_panel` values to open (i.e.,
673-
show) by default. The default value of `None` will open the first
674-
:func:`~shiny.ui.accordion_panel`. Use a value of `True` to open
675-
all (or `False` to open none) of the items. It's only possible to open more than
676-
one panel when `multiple=True`.
672+
A `str` or list of `str` naming the :func:`~shiny.ui.accordion_panel`
673+
value(s) to open (i.e., show) by default. (An empty list closes all panels.)
674+
The default value of `None` will open the first
675+
:func:`~shiny.ui.accordion_panel`. Use a value of `True` to open all (or `False`
676+
to open none) of the items. It's only possible to open more than one panel when
677+
`multiple=True`.
677678
multiple
678679
Whether multiple :func:`~shiny.ui.accordion_panel` can be open at
679680
once.

shiny/ui/_accordion.py

Lines changed: 28 additions & 28 deletions
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,7 @@
99
from .._utils import drop_none, private_random_id
1010
from ..bookmark import restore_input
1111
from ..session import require_active_session
12-
from ..types import MISSING, MISSING_TYPE
12+
from ..types import MISSING, MISSING_TYPE, ListOrTuple
1313
from ._html_deps_shinyverse import components_dependencies
1414
from ._tag import consolidate_attrs
1515
from .css._css_unit import CssUnit, as_css_unit
@@ -176,7 +176,7 @@ def tagify(self) -> Tag:
176176
def accordion(
177177
*args: AccordionPanel | TagAttrs,
178178
id: Optional[str] = None,
179-
open: Optional[bool | str | list[str]] = None,
179+
open: Optional[bool | str | ListOrTuple[str]] = None,
180180
multiple: bool = True,
181181
class_: Optional[str] = None,
182182
width: Optional[CssUnit] = None,
@@ -198,11 +198,12 @@ def accordion(
198198
value will correspond to the :func:`~shiny.ui.accordion_panel`'s
199199
`value` argument.
200200
open
201-
A list of :func:`~shiny.ui.accordion_panel` values to open (i.e.,
202-
show) by default. The default value of `None` will open the first
203-
:func:`~shiny.ui.accordion_panel`. Use a value of `True` to open
204-
all (or `False` to open none) of the items. It's only possible to open more than
205-
one panel when `multiple=True`.
201+
A `str` or list of `str` naming the :func:`~shiny.ui.accordion_panel`
202+
value(s) to open (i.e., show) by default. (An empty list closes all panels.)
203+
The default value of `None` will open the first
204+
:func:`~shiny.ui.accordion_panel`. Use a value of `True` to open all (or `False`
205+
to open none) of the items. It's only possible to open more than one panel when
206+
`multiple=True`.
206207
multiple
207208
Whether multiple :func:`~shiny.ui.accordion_panel` can be open at
208209
once.
@@ -259,21 +260,19 @@ def accordion(
259260
)
260261
open = restore_input(accordion_id, open)
261262

262-
is_open: list[bool] = []
263-
if open is None:
264-
is_open = [False for _ in panels]
263+
is_open: list[bool]
264+
if has_restored_input and open is None:
265+
# None from restore_input indicates all panels closed
266+
is_open = [False] * len(panels)
267+
elif open is None:
268+
# otherwise None indicates default behavior (open first panel)
269+
is_open = [i == 0 for i in range(len(panels))]
265270
elif isinstance(open, bool):
266-
is_open = [open for _ in panels]
271+
is_open = [open] * len(panels)
267272
else:
268-
if not isinstance(open, list):
269-
open = [open]
270-
#
271-
is_open = [panel._data_value in open for panel in panels]
272-
273-
if not has_restored_input:
274-
# Open the first panel by default
275-
if open is not False and len(is_open) > 0 and not any(is_open):
276-
is_open[0] = True
273+
# str | ListOrTuple[str] -> set
274+
open_set = {open} if isinstance(open, str) else set(open)
275+
is_open = [panel._data_value in open_set for panel in panels]
277276

278277
if (not multiple) and sum(is_open) > 1:
279278
raise ValueError("Can't select more than one panel when `multiple = False`")
@@ -384,11 +383,11 @@ def _accordion_panel_action(
384383
*,
385384
id: str,
386385
method: str,
387-
values: bool | str | list[str],
386+
values: bool | str | ListOrTuple[str],
388387
session: Session | None,
389388
) -> None:
390389
if not isinstance(values, bool):
391-
if not isinstance(values, list):
390+
if not isinstance(values, (list, tuple)):
392391
values = [values]
393392
_assert_list_str(values)
394393

@@ -404,7 +403,7 @@ def _accordion_panel_action(
404403
def update_accordion(
405404
id: str,
406405
*,
407-
show: bool | str | list[str],
406+
show: bool | str | ListOrTuple[str],
408407
session: Optional[Session] = None,
409408
) -> None:
410409
"""
@@ -502,7 +501,7 @@ def insert_accordion_panel(
502501
@add_example()
503502
def remove_accordion_panel(
504503
id: str,
505-
target: str | list[str],
504+
target: str | ListOrTuple[str],
506505
session: Optional[Session] = None,
507506
) -> None:
508507
"""
@@ -513,7 +512,8 @@ def remove_accordion_panel(
513512
id
514513
A string that matches an existing :func:`~shiny.ui.accordion`'s `id`.
515514
target
516-
The `value` of an existing panel to remove.
515+
A string or list of strings used to identify particular
516+
:func:`~shiny.ui.accordion_panel`(s) by their `value`.
517517
session
518518
A Shiny session object (the default should almost always be used).
519519
@@ -529,7 +529,7 @@ def remove_accordion_panel(
529529
* :func:`~shiny.ui.insert_accordion_panel`
530530
* :func:`~shiny.ui.update_accordion_panel`
531531
"""
532-
if not isinstance(target, list):
532+
if not isinstance(target, (list, tuple)):
533533
target = [target]
534534

535535
_send_panel_message(
@@ -632,8 +632,8 @@ def _assert_str(x: str) -> str:
632632
return x
633633

634634

635-
def _assert_list_str(x: list[str]) -> list[str]:
636-
if not isinstance(x, list):
635+
def _assert_list_str(x: ListOrTuple[str]) -> ListOrTuple[str]:
636+
if not isinstance(x, (list, tuple)):
637637
raise TypeError(f"Expected list, got {type(x)}")
638638
for i, x_i in enumerate(x):
639639
if not isinstance(x_i, str):

0 commit comments

Comments
 (0)