Skip to content

Commit a3f32d2

Browse files
committed
Merge branch 'main' into fix-issue-2013
2 parents 38a0984 + 3055517 commit a3f32d2

File tree

73 files changed

+2861
-2659
lines changed

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

73 files changed

+2861
-2659
lines changed

CHANGELOG.md

Lines changed: 11 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -9,20 +9,28 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
99

1010
### New features
1111

12+
* `ui.sidebar()` is now interactively resizable. (#2020)
13+
14+
* `ui.update_*()` functions now accept `ui.TagChild` (i.e., HTML) as input to the `label` and `icon` arguments. (#2020)
15+
16+
* `playwright.controller.InputActionButton` gains a `expect_icon()` method. As a result, the already existing `expect_label()` no longer includes the icon. (#2020)
17+
1218
### Improvements
1319

20+
* Improved the styling and readability of markdown tables rendered by `ui.Chat()` and `ui.MarkdownStream()`. (#1973)
21+
1422
* `selectize`, `remove_button`, and `options` parameters of `ui.input_select()` have been deprecated; use `ui.input_selectize()` instead. (Thanks, @ErdaradunGaztea!) (#1947)
1523

16-
* Improved the styling and readability of markdown tables rendered by `ui.Chat()` and `ui.MarkdownStream()`. (#1973)
24+
* Added `timeout_secs` parameter to `create_app_fixture` to allow testing apps with longer startup times. (#2033)
1725

1826
### Bug fixes
1927

28+
* Fixed an issue with `ui.Chat()` sometimes wanting to scroll a parent element. (#1996)
29+
2030
* Explicitly call out module usage in UI input bookmark button documentation. (#1983)
2131

2232
* Fix missing session when trying to display an error duing bookmarking. (#1984)
2333

24-
* Fixed an issue with `ui.Chat()` sometimes wanting to scroll a parent element. (#1996)
25-
2634
* Fixed `set()` method of `InputSelectize` controller so it clears existing selections before applying new values. (#2024)
2735

2836

shiny/_versions.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
shiny_html_deps = "1.10.0.9001"
1+
shiny_html_deps = "1.11.1.9000"
22
bslib = "0.9.0.9000"
33
htmltools = "0.5.8.9000"
44
bootstrap = "5.3.1"

shiny/playwright/controller/_base.py

Lines changed: 22 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -451,8 +451,6 @@ def expect_label(
451451
"""
452452
Expect the label of the input button to have a specific value.
453453
454-
Note: This must include the icon if it is present!
455-
456454
Parameters
457455
----------
458456
value
@@ -461,7 +459,28 @@ def expect_label(
461459
The maximum time to wait for the expectation to be fulfilled. Defaults to `None`.
462460
"""
463461

464-
self.expect.to_have_text(value, timeout=timeout)
462+
loc_label = self.loc.locator(".action-label")
463+
playwright_expect(loc_label).to_have_text(value, timeout=timeout)
464+
465+
def expect_icon(
466+
self,
467+
value: PatternOrStr,
468+
*,
469+
timeout: Timeout = None,
470+
) -> None:
471+
"""
472+
Expect the icon of the input button to have a specific value.
473+
474+
Parameters
475+
----------
476+
value
477+
The expected value of the icon.
478+
timeout
479+
The maximum time to wait for the expectation to be fulfilled. Defaults to `None`.
480+
"""
481+
482+
loc_icon = self.loc.locator(".action-icon")
483+
playwright_expect(loc_icon).to_have_text(value, timeout=timeout)
465484

466485
def click(self, *, timeout: Timeout = None, **kwargs: object) -> None:
467486
"""

shiny/pytest/_fixture.py

Lines changed: 5 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -36,6 +36,7 @@
3636
def create_app_fixture(
3737
app: PurePath | str | list[PurePath | str],
3838
scope: ScopeName = "module",
39+
timeout_secs: float = 30,
3940
):
4041
"""
4142
Create a fixture for a local Shiny app directory.
@@ -70,6 +71,8 @@ def create_app_fixture(
7071
will be created once per module. See [Pytest fixture
7172
scopes](https://docs.pytest.org/en/stable/how-to/fixtures.html#fixture-scopes)
7273
for more details.
74+
timeout_secs
75+
The maximum number of seconds to wait for the app to become ready.
7376
7477
Returns
7578
-------
@@ -133,7 +136,7 @@ def get_app_path(request: pytest.FixtureRequest, app: PurePath | str):
133136
@pytest.fixture(scope=scope, params=app)
134137
def fixture_func(request: pytest.FixtureRequest):
135138
app_path = get_app_path(request, request.param)
136-
sa_gen = shiny_app_gen(app_path)
139+
sa_gen = shiny_app_gen(app_path, timeout_secs=timeout_secs)
137140
yield next(sa_gen)
138141

139142
else:
@@ -142,7 +145,7 @@ def fixture_func(request: pytest.FixtureRequest):
142145
@pytest.fixture(scope=scope)
143146
def fixture_func(request: pytest.FixtureRequest):
144147
app_path = get_app_path(request, app)
145-
sa_gen = shiny_app_gen(app_path)
148+
sa_gen = shiny_app_gen(app_path, timeout_secs=timeout_secs)
146149
yield next(sa_gen)
147150

148151
return fixture_func

shiny/ui/_input_action_button.py

Lines changed: 13 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -61,10 +61,15 @@ def input_action_button(
6161
if "_add_ws" not in kwargs:
6262
kwargs["_add_ws"] = True
6363

64+
if icon is not None:
65+
icon = tags.span(icon, class_="action-icon")
66+
67+
if label is not None:
68+
label = tags.span(label, class_="action-label")
69+
6470
return tags.button(
6571
{"class": "btn btn-default action-button", "style": css(width=width)},
6672
icon,
67-
None if icon is None else " ",
6873
label,
6974
id=resolve_id(id),
7075
type="button",
@@ -114,8 +119,14 @@ def input_action_link(
114119
* :func:`~shiny.reactive.event`
115120
"""
116121

122+
if icon is not None:
123+
icon = tags.span(icon, class_="action-icon")
124+
125+
if label is not None:
126+
label = tags.span(label, class_="action-label")
127+
117128
return tags.a(
118-
{"class": "action-button"},
129+
{"class": "action-button action-link"},
119130
icon,
120131
label,
121132
id=resolve_id(id),

shiny/ui/_input_update.py

Lines changed: 42 additions & 34 deletions
Original file line numberDiff line numberDiff line change
@@ -71,7 +71,7 @@
7171
def update_action_button(
7272
id: str,
7373
*,
74-
label: Optional[str] = None,
74+
label: Optional[TagChild] = None,
7575
icon: TagChild = None,
7676
disabled: Optional[bool] = None,
7777
session: Optional[Session] = None,
@@ -104,11 +104,9 @@ def update_action_button(
104104
"""
105105

106106
session = require_active_session(session)
107-
# TODO: supporting a TagChild for label would require changes to shiny.js
108-
# https://github.com/rstudio/shiny/issues/1140
109107
msg = {
110-
"label": label,
111-
"icon": session._process_ui(icon)["html"] if icon else None,
108+
"label": session._process_ui(label) if label is not None else None,
109+
"icon": session._process_ui(icon) if icon is not None else None,
112110
"disabled": disabled,
113111
}
114112
session.send_input_message(id, drop_none(msg))
@@ -119,7 +117,7 @@ def update_action_button(
119117
def update_action_link(
120118
id: str,
121119
*,
122-
label: Optional[str] = None,
120+
label: Optional[TagChild] = None,
123121
icon: TagChild = None,
124122
session: Optional[Session] = None,
125123
) -> None:
@@ -148,11 +146,9 @@ def update_action_link(
148146
"""
149147

150148
session = require_active_session(session)
151-
# TODO: supporting a TagChild for label would require changes to shiny.js
152-
# https://github.com/rstudio/shiny/issues/1140
153149
msg = {
154-
"label": label,
155-
"icon": session._process_ui(icon)["html"] if icon else None,
150+
"label": session._process_ui(label) if label is not None else None,
151+
"icon": session._process_ui(icon) if icon is not None else None,
156152
}
157153
session.send_input_message(id, drop_none(msg))
158154

@@ -229,7 +225,7 @@ def callback() -> None:
229225
def update_checkbox(
230226
id: str,
231227
*,
232-
label: Optional[str] = None,
228+
label: Optional[TagChild] = None,
233229
value: Optional[bool] = None,
234230
session: Optional[Session] = None,
235231
) -> None:
@@ -258,7 +254,10 @@ def update_checkbox(
258254
"""
259255

260256
session = require_active_session(session)
261-
msg = {"label": label, "value": value}
257+
msg = {
258+
"label": session._process_ui(label) if label is not None else None,
259+
"value": value,
260+
}
262261
session.send_input_message(id, drop_none(msg))
263262

264263

@@ -267,7 +266,7 @@ def update_checkbox(
267266
def update_switch(
268267
id: str,
269268
*,
270-
label: Optional[str] = None,
269+
label: Optional[TagChild] = None,
271270
value: Optional[bool] = None,
272271
session: Optional[Session] = None,
273272
) -> None:
@@ -296,7 +295,10 @@ def update_switch(
296295
"""
297296

298297
session = require_active_session(session)
299-
msg = {"label": label, "value": value}
298+
msg = {
299+
"label": session._process_ui(label) if label is not None else None,
300+
"value": value,
301+
}
300302
session.send_input_message(id, drop_none(msg))
301303

302304

@@ -305,7 +307,7 @@ def update_switch(
305307
def update_checkbox_group(
306308
id: str,
307309
*,
308-
label: Optional[str] = None,
310+
label: Optional[TagChild] = None,
309311
choices: Optional[ChoicesArg] = None,
310312
selected: Optional[str | list[str] | tuple[str, ...]] = None,
311313
inline: bool = False,
@@ -357,7 +359,7 @@ def update_checkbox_group(
357359
def update_radio_buttons(
358360
id: str,
359361
*,
360-
label: Optional[str] = None,
362+
label: Optional[TagChild] = None,
361363
choices: Optional[ChoicesArg] = None,
362364
selected: Optional[str] = None,
363365
inline: bool = False,
@@ -408,7 +410,7 @@ def _update_choice_input(
408410
id: str,
409411
*,
410412
type: Literal["checkbox", "radio"],
411-
label: Optional[str] = None,
413+
label: Optional[TagChild] = None,
412414
choices: Optional[ChoicesArg] = None,
413415
selected: Optional[str | list[str] | tuple[str, ...]] = None,
414416
inline: bool = False,
@@ -429,7 +431,11 @@ def _update_choice_input(
429431
inline=inline,
430432
)
431433
options = session._process_ui(opts)["html"]
432-
msg = {"label": label, "options": options, "value": selected}
434+
msg = {
435+
"label": session._process_ui(label) if label is not None else None,
436+
"options": options,
437+
"value": selected,
438+
}
433439
session.send_input_message(id, drop_none(msg))
434440

435441

@@ -441,7 +447,7 @@ def _update_choice_input(
441447
def update_date(
442448
id: str,
443449
*,
444-
label: Optional[str] = None,
450+
label: Optional[TagChild] = None,
445451
value: Optional[date | str] = None,
446452
min: Optional[date | str] = None,
447453
max: Optional[date | str] = None,
@@ -478,7 +484,7 @@ def update_date(
478484

479485
session = require_active_session(session)
480486
msg = {
481-
"label": label,
487+
"label": session._process_ui(label) if label is not None else None,
482488
"value": _as_date_attr(value),
483489
"min": _as_date_attr(min),
484490
"max": _as_date_attr(max),
@@ -491,7 +497,7 @@ def update_date(
491497
def update_date_range(
492498
id: str,
493499
*,
494-
label: Optional[str] = None,
500+
label: Optional[TagChild] = None,
495501
start: Optional[date | str] = None,
496502
end: Optional[date | str] = None,
497503
min: Optional[date | str] = None,
@@ -535,7 +541,7 @@ def update_date_range(
535541
session = require_active_session(session)
536542
value = {"start": _as_date_attr(start), "end": _as_date_attr(end)}
537543
msg = {
538-
"label": label,
544+
"label": session._process_ui(label) if label is not None else None,
539545
"value": drop_none(value),
540546
"min": _as_date_attr(min),
541547
"max": _as_date_attr(max),
@@ -551,7 +557,7 @@ def update_date_range(
551557
def update_numeric(
552558
id: str,
553559
*,
554-
label: Optional[str] = None,
560+
label: Optional[TagChild] = None,
555561
value: Optional[float] = None,
556562
min: Optional[float] = None,
557563
max: Optional[float] = None,
@@ -589,7 +595,7 @@ def update_numeric(
589595

590596
session = require_active_session(session)
591597
msg = {
592-
"label": label,
598+
"label": session._process_ui(label) if label is not None else None,
593599
"value": value,
594600
"min": min,
595601
"max": max,
@@ -606,7 +612,7 @@ def update_numeric(
606612
def update_select(
607613
id: str,
608614
*,
609-
label: Optional[str] = None,
615+
label: Optional[TagChild] = None,
610616
choices: Optional[SelectChoicesArg] = None,
611617
selected: Optional[str | list[str]] = None,
612618
session: Optional[Session] = None,
@@ -652,12 +658,10 @@ def update_select(
652658
options = None
653659
else:
654660
option_tags = _render_choices(_normalize_choices(choices), selected)
655-
# Typing problem due to a bug in pylance:
656-
# https://github.com/microsoft/pylance-release/issues/2377
657-
options = session._process_ui(option_tags)["html"] # type: ignore
661+
options = session._process_ui(option_tags)["html"]
658662

659663
msg = {
660-
"label": label,
664+
"label": session._process_ui(label) if label is not None else None,
661665
"options": options,
662666
"value": selected_values,
663667
}
@@ -675,7 +679,7 @@ class FlatSelectChoice(TypedDict):
675679
def update_selectize(
676680
id: str,
677681
*,
678-
label: Optional[str] = None,
682+
label: Optional[TagChild] = None,
679683
choices: Optional[SelectChoicesArg] = None,
680684
selected: Optional[str | list[str]] = None,
681685
options: Optional[dict[str, str | float | JSEval]] = None,
@@ -854,7 +858,7 @@ def selectize_choices_json(request: Request) -> Response:
854858
def update_slider(
855859
id: str,
856860
*,
857-
label: Optional[str] = None,
861+
label: Optional[TagChild] = None,
858862
value: Optional[SliderValueArg | tuple[SliderValueArg, SliderValueArg]] = None,
859863
min: Optional[SliderValueArg] = None,
860864
max: Optional[SliderValueArg] = None,
@@ -928,7 +932,7 @@ def update_slider(
928932
value_num = None
929933

930934
msg = {
931-
"label": label,
935+
"label": session._process_ui(label) if label is not None else None,
932936
"value": value_num,
933937
"min": min_num,
934938
"max": max_num,
@@ -948,7 +952,7 @@ def update_slider(
948952
def update_text(
949953
id: str,
950954
*,
951-
label: Optional[str] = None,
955+
label: Optional[TagChild] = None,
952956
value: Optional[str] = None,
953957
placeholder: Optional[str] = None,
954958
session: Optional[Session] = None,
@@ -980,7 +984,11 @@ def update_text(
980984
"""
981985

982986
session = require_active_session(session)
983-
msg = {"label": label, "value": value, "placeholder": placeholder}
987+
msg = {
988+
"label": session._process_ui(label) if label is not None else None,
989+
"value": value,
990+
"placeholder": placeholder,
991+
}
984992
session.send_input_message(id, drop_none(msg))
985993

986994

0 commit comments

Comments
 (0)