Skip to content

Commit 686369b

Browse files
authored
Merge branch 'main' into integrate-test-generator
2 parents fd725ee + 0b9188b commit 686369b

File tree

7 files changed

+98
-48
lines changed

7 files changed

+98
-48
lines changed

CHANGELOG.md

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

3030
### Improvements
3131

32+
* Restricted the allowable types of the `choices` parameter of `input_select()`, `input_selectize()`, `update_select()`, and `update_selectize()` to actual set of allowable types (previously, the type was suggesting HTML-like values were supported). (#2048)
33+
3234
* Improved the styling and readability of markdown tables rendered by `ui.Chat()` and `ui.MarkdownStream()`. (#1973)
3335

3436
* `selectize`, `remove_button`, and `options` parameters of `ui.input_select()` have been deprecated; use `ui.input_selectize()` instead. (Thanks, @ErdaradunGaztea!) (#1947)
@@ -41,6 +43,12 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
4143

4244
### Bug fixes
4345

46+
* Fixed numerous issues related to programmatically updating selectize options. (#2053)
47+
* `update_selectize(options=...)` no longer gets ignored when `server=False` (the default).
48+
* `update_selectize(options=...)` now works as expected in a module.
49+
50+
* Fixed an issue with `update_selectize()` to properly display labels with HTML reserved characters like "&" (#1330)
51+
4452
* Fixed an issue with `ui.Chat()` sometimes wanting to scroll a parent element. (#1996)
4553

4654
* Explicitly call out module usage in UI input bookmark button documentation. (#1983)

shiny/api-examples/Chat/app-core.py

Lines changed: 9 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -3,21 +3,24 @@
33
app_ui = ui.page_fillable(
44
ui.panel_title("Hello Shiny Chat"),
55
ui.chat_ui("chat"),
6-
fillable_mobile=True,
7-
)
8-
9-
# Create a welcome message
10-
welcome = """
6+
ui.chat_ui(
7+
"chat",
8+
messages=[
9+
"""
1110
Hi! This is a simple Shiny `Chat` UI. Enter a message below and I will
1211
simply repeat it back to you.
1312
1413
To learn more about chatbots and how to build them with Shiny, check out
1514
[the documentation](https://shiny.posit.co/py/docs/genai-chatbots.html).
1615
"""
16+
],
17+
),
18+
fillable_mobile=True,
19+
)
1720

1821

1922
def server(input, output, session):
20-
chat = ui.Chat(id="chat", messages=[welcome])
23+
chat = ui.Chat(id="chat")
2124

2225
# Define a callback to run when the user submits a message
2326
@chat.on_user_submit

shiny/api-examples/Chat/app-express.py

Lines changed: 8 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -7,24 +7,22 @@
77
fillable_mobile=True,
88
)
99

10-
# Create a welcome message
11-
welcome = """
10+
# Create a chat instance
11+
chat = ui.Chat(id="chat")
12+
13+
# Display it, with a startup message
14+
chat.ui(
15+
messages=[
16+
"""
1217
Hi! This is a simple Shiny `Chat` UI. Enter a message below and I will
1318
simply repeat it back to you.
1419
1520
To learn more about chatbots and how to build them with Shiny, check out
1621
[the documentation](https://shiny.posit.co/py/docs/genai-chatbots.html).
1722
"""
18-
19-
# Create a chat instance
20-
chat = ui.Chat(
21-
id="chat",
22-
messages=[welcome],
23+
],
2324
)
2425

25-
# Display it
26-
chat.ui()
27-
2826

2927
# Define a callback to run when the user submits a message
3028
@chat.on_user_submit

shiny/ui/_input_select.py

Lines changed: 3 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -21,7 +21,7 @@
2121
from ._html_deps_external import selectize_deps
2222
from ._utils import JSEval, extract_js_keys, shiny_input_label
2323

24-
_Choices = Mapping[str, TagChild]
24+
_Choices = Mapping[str, str]
2525
_OptGrpChoices = Mapping[str, _Choices]
2626

2727
# Canonical format for representing select options.
@@ -75,8 +75,7 @@ def input_selectize(
7575
choices
7676
Either a list of choices or a dictionary mapping choice values to labels. Note
7777
that if a dictionary is provided, the keys are used as the (input) values and
78-
the values are labels displayed to the user. It is not recommended to use
79-
anything other than a string for these labels. A dictionary of dictionaries is
78+
the values are labels displayed to the user. A dictionary of dictionaries is
8079
also supported, and in that case, the top-level keys are treated as
8180
``<optgroup>`` labels.
8281
selected
@@ -157,8 +156,7 @@ def input_select(
157156
choices
158157
Either a list of choices or a dictionary mapping choice values to labels. Note
159158
that if a dictionary is provided, the keys are used as the (input) values and
160-
the values are labels displayed to the user. It is not recommended to use
161-
anything other than a string for these labels. A dictionary of dictionaries is
159+
the values are labels displayed to the user. A dictionary of dictionaries is
162160
also supported, and in that case, the top-level keys are treated as
163161
``<optgroup>`` labels.
164162
selected
@@ -259,12 +257,6 @@ def _input_select_impl(
259257

260258
choices_ = _normalize_choices(choices)
261259

262-
if _contains_html(choices_):
263-
warn_deprecated(
264-
"Passing anything other than a string to `choices` parameter of "
265-
"`input_select()` and `input_selectize()` is deprecated."
266-
)
267-
268260
selected = restore_input(resolved_id, selected)
269261
if selected is None and not multiple:
270262
selected = _find_first_option(choices_)

shiny/ui/_input_update.py

Lines changed: 13 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -628,8 +628,7 @@ def update_select(
628628
An input label.
629629
choices
630630
Either a list of choices or a dictionary mapping choice values to labels. Note
631-
that if a dictionary is provided, the keys are used as the (input) values so
632-
that the dictionary values can hold HTML labels. A dictionary of dictionaries is
631+
that if a dictionary is provided, the keys are used as the (input) values. A dictionary of dictionaries is
633632
also supported, and in that case, the top-level keys are treated as
634633
``<optgroup>`` labels.
635634
selected
@@ -697,8 +696,7 @@ def update_selectize(
697696
An input label.
698697
choices
699698
Either a list of choices or a dictionary mapping choice values to labels. Note
700-
that if a dictionary is provided, the keys are used as the (input) values so
701-
that the dictionary values can hold HTML labels. A dictionary of dictionaries is
699+
that if a dictionary is provided, the keys are used as the (input) values. A dictionary of dictionaries is
702700
also supported, and in that case, the top-level keys are treated as
703701
``<optgroup>`` labels.
704702
selected
@@ -724,37 +722,31 @@ def update_selectize(
724722

725723
session = require_active_session(session)
726724

725+
if options is not None:
726+
cfg = tags.script(
727+
json.dumps(options),
728+
type="application/json",
729+
data_for=resolve_id(id),
730+
data_eval=json.dumps(extract_js_keys(options)),
731+
)
732+
session.send_input_message(id, {"config": cfg.get_html_string()})
733+
727734
if not server:
728735
return update_select(
729736
id, label=label, choices=choices, selected=selected, session=session
730737
)
731738

732-
if options is not None:
733-
cfg = TagList(
734-
tags.script(
735-
json.dumps(options),
736-
type="application/json",
737-
data_for=id,
738-
data_eval=json.dumps(extract_js_keys(options)),
739-
)
740-
)
741-
session.send_input_message(id, drop_none({"config": cfg.get_html_string()}))
742-
743739
# Transform choices to a list of dicts (this is the form the client wants)
744740
# [{"label": "Foo", "value": "foo", "optgroup": "foo"}, ...]
745741
flat_choices: list[FlatSelectChoice] = []
746742
if choices is not None:
747743
for k, v in _normalize_choices(choices).items():
748744
if not isinstance(v, Mapping):
749-
flat_choices.append(
750-
FlatSelectChoice(value=k, label=session._process_ui(v)["html"])
751-
)
745+
flat_choices.append(FlatSelectChoice(value=k, label=v))
752746
else: # The optgroup case
753747
flat_choices.extend(
754748
[
755-
FlatSelectChoice(
756-
optgroup=k, value=k2, label=session._process_ui(v2)["html"]
757-
)
749+
FlatSelectChoice(optgroup=k, value=k2, label=v2)
758750
for (k2, v2) in v.items()
759751
]
760752
)
Lines changed: 34 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,34 @@
1+
from shiny import App, Inputs, Outputs, Session, module, reactive, ui
2+
3+
4+
@module.ui
5+
def reprex_selectize_ui(label: str):
6+
return ui.input_selectize("x", label, choices=[], multiple=True)
7+
8+
9+
@module.server
10+
def reprex_selectize_server(
11+
input: Inputs, output: Outputs, session: Session, server: bool = True
12+
):
13+
@reactive.effect
14+
def _():
15+
ui.update_selectize(
16+
"x",
17+
choices=[f"Foo {i}" for i in range(3)],
18+
server=server,
19+
options={"placeholder": "Search"},
20+
)
21+
22+
23+
app_ui = ui.page_fluid(
24+
reprex_selectize_ui("serverside", "Server"),
25+
reprex_selectize_ui("clientside", "Client"),
26+
)
27+
28+
29+
def server(input: Inputs, output: Outputs, session: Session):
30+
reprex_selectize_server("serverside", server=True)
31+
reprex_selectize_server("clientside", server=False)
32+
33+
34+
app = App(app_ui, server, debug=True)
Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,23 @@
1+
# import pytest
2+
from playwright.sync_api import Page, expect
3+
4+
from shiny.playwright import controller
5+
from shiny.run import ShinyAppProc
6+
7+
8+
def test_selectize(page: Page, local_app: ShinyAppProc) -> None:
9+
page.goto(local_app.url)
10+
11+
expect(page.get_by_placeholder("Search")).to_have_count(2)
12+
13+
selectize_menu = controller.InputSelectize(page, "serverside-x")
14+
selectize_menu.expect_choices(["Foo 0", "Foo 1", "Foo 2"])
15+
selectize_menu.expect_multiple(True)
16+
selectize_menu.set(["Foo 0", "Foo 1"])
17+
selectize_menu.expect_selected(["Foo 0", "Foo 1"])
18+
19+
selectize_menu2 = controller.InputSelectize(page, "clientside-x")
20+
selectize_menu2.expect_choices(["Foo 0", "Foo 1", "Foo 2"])
21+
selectize_menu2.expect_multiple(True)
22+
selectize_menu2.set(["Foo 0", "Foo 1"])
23+
selectize_menu2.expect_selected(["Foo 0", "Foo 1"])

0 commit comments

Comments
 (0)