Skip to content

Commit a96032f

Browse files
committed
Refactor accordion app to use shiny.express and simplify logic
Refactors the accordion test app to use the new shiny.express API.
1 parent 47ccb7c commit a96032f

File tree

2 files changed

+141
-136
lines changed

2 files changed

+141
-136
lines changed
Lines changed: 140 additions & 135 deletions
Original file line numberDiff line numberDiff line change
@@ -1,142 +1,147 @@
11
from __future__ import annotations
22

3-
from shiny import App, Inputs, Outputs, Session, reactive, render, req, ui
4-
5-
6-
def make_panel(letter: str) -> ui.AccordionPanel:
7-
return ui.accordion_panel(
8-
f"Section {letter}",
9-
f"Some narrative for section {letter}",
10-
)
11-
12-
13-
items = [make_panel(letter) for letter in "ABCD"]
14-
15-
accordion = ui.accordion(*items, id="acc")
16-
app_ui = ui.page_fluid(
17-
ui.tags.div(
18-
ui.input_action_button("toggle_b", "Open/Close B"),
19-
ui.input_action_button("open_all", "Open All"),
20-
ui.input_action_button("close_all", "Close All"),
21-
ui.input_action_button("alternate", "Alternate"),
22-
ui.input_action_button("toggle_efg", "Add/Remove EFG"),
23-
ui.input_action_button("toggle_updates", "Add/Remove Updates"),
24-
class_="d-flex",
25-
),
26-
ui.output_text_verbatim("acc_txt", placeholder=True),
27-
accordion,
28-
)
29-
30-
31-
def server(input: Inputs, output: Outputs, session: Session) -> None:
32-
@reactive.calc
33-
def acc() -> list[str]:
34-
acc_val: list[str] | None = input.acc()
35-
if acc_val is None:
36-
acc_val = []
37-
return acc_val
38-
39-
@reactive.effect
40-
def _():
41-
req(input.toggle_b())
3+
from shiny import reactive, render
4+
from shiny.express import input, ui
425

43-
with reactive.isolate():
44-
if "Section B" in acc():
45-
ui.update_accordion_panel("acc", "Section B", show=False)
46-
else:
47-
ui.update_accordion_panel("acc", "Section B", show=True)
48-
49-
@reactive.effect
50-
def _():
51-
req(input.open_all())
52-
ui.update_accordion("acc", show=True)
53-
54-
@reactive.effect
55-
def _():
56-
req(input.close_all())
57-
ui.update_accordion("acc", show=False)
58-
59-
has_efg = False
60-
has_alternate = True
61-
has_updates = False
62-
63-
@reactive.effect
64-
def _():
65-
req(input.alternate())
66-
67-
sections = [
68-
"updated_section_a" if has_updates else "Section A",
69-
"Section B",
70-
"Section C",
71-
"Section D",
72-
]
73-
if has_efg:
74-
sections.extend(["Section E", "Section F", "Section G"])
75-
76-
nonlocal has_alternate
77-
val = int(has_alternate)
78-
sections = [section for i, section in enumerate(sections) if i % 2 == val]
79-
ui.update_accordion("acc", show=sections)
80-
has_alternate = not has_alternate
81-
82-
@reactive.effect
83-
def _():
84-
req(input.toggle_efg())
85-
86-
nonlocal has_efg
87-
if has_efg:
88-
ui.remove_accordion_panel("acc", ["Section E", "Section F", "Section G"])
89-
else:
90-
ui.insert_accordion_panel("acc", make_panel("E"), "Section D")
91-
ui.insert_accordion_panel("acc", make_panel("F"), "Section E")
92-
ui.insert_accordion_panel("acc", make_panel("G"), "Section F")
93-
94-
has_efg = not has_efg
95-
96-
@reactive.effect
97-
def _():
98-
req(input.toggle_updates())
99-
100-
nonlocal has_updates
101-
if has_updates:
102-
ui.update_accordion_panel(
103-
"acc",
104-
"updated_section_a",
105-
"Some narrative for section A",
106-
title="Section A",
107-
value="Section A",
108-
icon="",
109-
)
110-
else:
111-
with reactive.isolate():
112-
# print(acc())
113-
if "Section A" not in acc():
114-
ui.notification_show("Opening Section A", duration=2)
115-
ui.update_accordion_panel("acc", "Section A", show=True)
116-
ui.update_accordion_panel(
117-
"acc",
118-
"Section A",
119-
"Updated body",
120-
value="updated_section_a",
121-
title=ui.tags.h3("Updated title"),
122-
icon=ui.tags.div(
123-
"Look! An icon! -->",
124-
ui.HTML(
125-
"""\
126-
<svg xmlns="http://www.w3.org/2000/svg" width="16" height="16" fill="currentColor" class="bi bi-chat-right-quote" viewBox="0 0 16 16">
127-
<path d="M2 1a1 1 0 0 0-1 1v8a1 1 0 0 0 1 1h9.586a2 2 0 0 1 1.414.586l2 2V2a1 1 0 0 0-1-1H2zm12-1a2 2 0 0 1 2 2v12.793a.5.5 0 0 1-.854.353l-2.853-2.853a1 1 0 0 0-.707-.293H2a2 2 0 0 1-2-2V2a2 2 0 0 1 2-2h12z"/>
128-
<path d="M7.066 4.76A1.665 1.665 0 0 0 4 5.668a1.667 1.667 0 0 0 2.561 1.406c-.131.389-.375.804-.777 1.22a.417.417 0 1 0 .6.58c1.486-1.54 1.293-3.214.682-4.112zm4 0A1.665 1.665 0 0 0 8 5.668a1.667 1.667 0 0 0 2.561 1.406c-.131.389-.375.804-.777 1.22a.417.417 0 1 0 .6.58c1.486-1.54 1.293-3.214.682-4.112z"/>
129-
</svg>
130-
"""
131-
),
132-
),
133-
)
1346

135-
has_updates = not has_updates
7+
def make_panel_title(letter: str) -> str:
8+
return f"Section {letter}"
9+
10+
11+
def make_panel_content(letter: str) -> str:
12+
return f"Some narrative for section {letter}"
13+
14+
15+
with ui.tags.div(class_="d-flex"):
16+
ui.input_action_button("toggle_b", "Open/Close B")
17+
ui.input_action_button("open_all", "Open All")
18+
ui.input_action_button("close_all", "Close All")
19+
ui.input_action_button("alternate", "Alternate")
20+
ui.input_action_button("toggle_efg", "Add/Remove EFG")
21+
ui.input_action_button("toggle_updates", "Add/Remove Updates")
22+
13623

137-
@render.text
138-
def acc_txt():
139-
return f"input.acc(): {input.acc()}"
24+
@render.text
25+
def acc_txt():
26+
return f"input.acc(): {input.acc()}"
14027

14128

142-
app = App(app_ui, server)
29+
with ui.accordion(id="acc"):
30+
for letter in "ABCD":
31+
with ui.accordion_panel(f"Section {letter}"):
32+
f"Some narrative for section {letter}"
33+
34+
35+
@reactive.calc
36+
def acc() -> list[str]:
37+
acc_val: list[str] | None = input.acc()
38+
if acc_val is None:
39+
acc_val = []
40+
return acc_val
41+
42+
43+
@reactive.effect
44+
@reactive.event(input.toggle_b)
45+
def _():
46+
with reactive.isolate():
47+
if "Section B" in acc():
48+
ui.update_accordion_panel("acc", "Section B", show=False)
49+
else:
50+
ui.update_accordion_panel("acc", "Section B", show=True)
51+
52+
53+
@reactive.effect
54+
@reactive.event(input.open_all)
55+
def _():
56+
ui.update_accordion("acc", show=True)
57+
58+
59+
@reactive.effect
60+
@reactive.event(input.close_all)
61+
def _():
62+
ui.update_accordion("acc", show=False)
63+
64+
65+
has_efg = False
66+
has_alternate = True
67+
has_updates = False
68+
69+
70+
@reactive.effect
71+
@reactive.event(input.alternate)
72+
def _():
73+
sections = [
74+
"updated_section_a" if has_updates else "Section A",
75+
"Section B",
76+
"Section C",
77+
"Section D",
78+
]
79+
if has_efg:
80+
sections.extend(["Section E", "Section F", "Section G"])
81+
82+
global has_alternate
83+
val = int(has_alternate)
84+
sections = [section for i, section in enumerate(sections) if i % 2 == val]
85+
ui.update_accordion("acc", show=sections)
86+
has_alternate = not has_alternate
87+
88+
89+
@reactive.effect
90+
@reactive.event(input.toggle_efg)
91+
def _():
92+
global has_efg
93+
if has_efg:
94+
ui.remove_accordion_panel("acc", ["Section E", "Section F", "Section G"])
95+
else:
96+
ui.insert_accordion_panel(
97+
"acc", make_panel_title("E"), make_panel_content("E"), target="Section D"
98+
)
99+
ui.insert_accordion_panel(
100+
"acc", make_panel_title("F"), make_panel_content("F"), target="Section E"
101+
)
102+
ui.insert_accordion_panel(
103+
"acc", make_panel_title("G"), make_panel_content("G"), target="Section F"
104+
)
105+
106+
has_efg = not has_efg
107+
108+
109+
@reactive.effect
110+
@reactive.event(input.toggle_updates)
111+
def _():
112+
global has_updates
113+
if has_updates:
114+
ui.update_accordion_panel(
115+
"acc",
116+
"updated_section_a",
117+
"Some narrative for section A",
118+
title="Section A",
119+
value="Section A",
120+
icon="",
121+
)
122+
else:
123+
with reactive.isolate():
124+
# print(acc())
125+
if "Section A" not in acc():
126+
ui.notification_show("Opening Section A", duration=2)
127+
ui.update_accordion_panel("acc", "Section A", show=True)
128+
ui.update_accordion_panel(
129+
"acc",
130+
"Section A",
131+
"Updated body",
132+
value="updated_section_a",
133+
title=ui.tags.h3("Updated title"),
134+
icon=ui.tags.div(
135+
"Look! An icon! -->",
136+
ui.HTML(
137+
"""\
138+
<svg xmlns="http://www.w3.org/2000/svg" width="16" height="16" fill="currentColor" class="bi bi-chat-right-quote" viewBox="0 0 16 16">
139+
<path d="M2 1a1 1 0 0 0-1 1v8a1 1 0 0 0 1 1h9.586a2 2 0 0 1 1.414.586l2 2V2a1 1 0 0 0-1-1H2zm12-1a2 2 0 0 1 2 2v12.793a.5.5 0 0 1-.854.353l-2.853-2.853a1 1 0 0 0-.707-.293H2a2 2 0 0 1-2-2V2a2 2 0 0 1 2-2h12z"/>
140+
<path d="M7.066 4.76A1.665 1.665 0 0 0 4 5.668a1.667 1.667 0 0 0 2.561 1.406c-.131.389-.375.804-.777 1.22a.417.417 0 1 0 .6.58c1.486-1.54 1.293-3.214.682-4.112zm4 0A1.665 1.665 0 0 0 8 5.668a1.667 1.667 0 0 0 2.561 1.406c-.131.389-.375.804-.777 1.22a.417.417 0 1 0 .6.58c1.486-1.54 1.293-3.214.682-4.112z"/>
141+
</svg>
142+
"""
143+
),
144+
),
145+
)
146+
147+
has_updates = not has_updates

tests/playwright/shiny/components/accordion/test_accordion.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -12,7 +12,7 @@ def test_accordion(page: Page, local_app: ShinyAppProc) -> None:
1212

1313
acc = controller.Accordion(page, "acc")
1414
acc_panel_A = acc.accordion_panel("Section A")
15-
output_txt_verbatim = controller.OutputTextVerbatim(page, "acc_txt")
15+
output_txt_verbatim = controller.OutputText(page, "acc_txt")
1616
alternate_button = controller.InputActionButton(page, "alternate")
1717
open_all_button = controller.InputActionButton(page, "open_all")
1818
close_all_button = controller.InputActionButton(page, "close_all")

0 commit comments

Comments
 (0)