Skip to content

Commit 0110900

Browse files
authored
tests: Add 317-nav-insert (#279)
* tests: Add 317-nav-insert * Generate apps deps (GitHub Actions) * add web component test * Generate apps deps (GitHub Actions)
1 parent 62c7a20 commit 0110900

File tree

6 files changed

+531
-0
lines changed

6 files changed

+531
-0
lines changed

R/data-apps-deps.R

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -174,6 +174,7 @@ apps_deps_map <- list(
174174
`314-bslib-tooltips` = c("bsicons", "bslib", "plotly", "shiny", "shinycoreci", "shinytest2", "withr"),
175175
`315-bslib-input-switch` = c("bslib", "shiny", "shinytest2", "withr"),
176176
`316-bslib-popovers` = c("bsicons", "bslib", "plotly", "rversions", "shiny", "shinycoreci", "shinytest2", "testthat", "withr"),
177+
`317-nav-insert` = c("bslib", "htmltools", "leaflet", "rversions", "shiny", "shinycoreci", "shinytest2", "testthat", "withr"),
177178
`900-text-jster` = c("shiny", "shinyjster", "shinytest2"),
178179
`901-button-jster` = c("shiny", "shinyjster", "shinytest2")
179180
)

inst/apps/317-nav-insert/app.R

Lines changed: 279 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,279 @@
1+
library(shiny)
2+
library(bslib)
3+
4+
DO_ALERT <- FALSE
5+
6+
action_choices <- c(
7+
"Singleton script" = "singleton",
8+
"Scripts with singleton" = "scripts",
9+
"HTML Widget" = "htmlwidgets",
10+
"Input/Output (content)" = "input_output_content",
11+
"Input/Output (nav)" = "input_output_nav",
12+
"Shiny sub-app" = "subapp",
13+
"Web Component" = "init_component"
14+
)
15+
16+
ui <- page_navbar(
17+
title = "Reprex for #4179",
18+
id = "main",
19+
lang = "en",
20+
navbar_options = navbar_options(collapsible = FALSE),
21+
footer = absolutePanel(
22+
card(
23+
selectInput("insert_type", "Insert nav type", choices = action_choices),
24+
actionButton("do_insert", "Insert Nav"),
25+
HTML(
26+
'<p>Scripts: <span id="script-count">0</span> evaluated (<span id="script-count-expected">0</span> expected).'
27+
),
28+
tags$script(
29+
HTML(
30+
"Shiny.addCustomMessageHandler('script-count-expected', function(value) {
31+
const exp = document.getElementById('script-count-expected')
32+
exp.textContent = +exp.textContent + value;
33+
})"
34+
)
35+
)
36+
),
37+
bottom = "1rem",
38+
right = "1rem",
39+
draggable = TRUE
40+
)
41+
)
42+
43+
# https://github.com/rstudio/shiny/pull/1794#issuecomment-318722200
44+
# We need these test cases for anywhere we insert dynamic UI:
45+
46+
# 1. `<script>` blocks should run
47+
# 2. `<script>` blocks should only run once
48+
# 3. `head()`/`singleton()` should be respected
49+
# 4. HTML widgets should work
50+
# a. Even when the dependencies are not part of the initial page load
51+
# 5. Shiny inputs/outputs should work
52+
# 6. Subapps should work (include a `shinyApp` object right in the UI)
53+
54+
action_link <- shiny::actionLink("refresh", "Refresh")
55+
56+
script_hello_world <- local({
57+
i <- 0
58+
59+
function() {
60+
i <<- i + 1
61+
62+
shiny::HTML(
63+
"<script>(function() {
64+
const el = document.getElementById('script-count')
65+
el.textContent = +el.textContent + 1
66+
})()</script>"
67+
)
68+
}
69+
})
70+
71+
script_singleton <- shiny::singleton(script_hello_world())
72+
73+
init_component <- function(init = NULL) {
74+
tag(
75+
"init-component",
76+
list(
77+
init = init,
78+
htmltools::htmlDependency(
79+
"init-component",
80+
"0.0.1",
81+
src = ".",
82+
script = "wc-init.js",
83+
all_files = FALSE
84+
)
85+
)
86+
)
87+
}
88+
89+
singleton_has_run <- FALSE
90+
91+
nav_insert_singleton <- function(session) {
92+
if (!singleton_has_run) {
93+
session$sendCustomMessage('script-count-expected', 1L)
94+
singleton_has_run <<- TRUE
95+
}
96+
97+
nav_insert(
98+
id = "main",
99+
select = TRUE,
100+
nav_panel(
101+
"One",
102+
p("Script should only run the first time this nav is inserted."),
103+
# 1. script blocks should run
104+
script_singleton,
105+
# 3. head() should be respected
106+
tags$head(tags$meta(content = "shiny-test-head"))
107+
),
108+
)
109+
}
110+
111+
nav_insert_scripts <- function(session) {
112+
session$sendCustomMessage('script-count-expected', 2L)
113+
114+
nav_insert(
115+
id = "main",
116+
select = TRUE,
117+
nav_panel(
118+
value = "Two",
119+
tagList(
120+
"Two",
121+
script_hello_world(),
122+
),
123+
p(
124+
"Two scripts should run every time this nav is inserted."
125+
),
126+
# 2. script blocks should only run once
127+
script_hello_world()
128+
),
129+
)
130+
}
131+
132+
nav_insert_htmlwidget <- local({
133+
widget_count <- 0
134+
function() {
135+
widget_count <<- widget_count + 1
136+
# 4. htmlwidgets work even if not part of initial page load
137+
nav_insert(
138+
id = "main",
139+
select = TRUE,
140+
nav_panel(
141+
"Map",
142+
leaflet::addTiles(
143+
leaflet::leaflet(
144+
elementId = sprintf("leaflet-%d", widget_count)
145+
)
146+
)
147+
),
148+
)
149+
}
150+
})
151+
152+
nav_insert_input_output_content <- function(input, output) {
153+
# 5. Input/outputs should work (in content)
154+
nav_insert(
155+
id = "main",
156+
select = TRUE,
157+
nav_panel(
158+
"Inputs/outputs",
159+
layout_columns(
160+
actionButton("btn", "Click me"),
161+
sliderInput("slider", "Slide me", min = 0, max = 10, value = 2),
162+
),
163+
verbatimTextOutput("debug")
164+
)
165+
)
166+
167+
output$debug <- renderPrint({
168+
list(
169+
btn = input$btn,
170+
slider = input$slider,
171+
nav_link = input$nav_link
172+
)
173+
})
174+
}
175+
176+
nav_insert_input_output_nav <- function(input, output) {
177+
# 5. Inputs/outputs work (in navbar)
178+
nav_insert(
179+
id = "main",
180+
nav_item(
181+
actionLink("nav_link", "Click me too", class = "nav-link")
182+
)
183+
)
184+
185+
nav_insert(
186+
id = "main",
187+
nav_item(textOutput("nav_output"))
188+
)
189+
190+
output$nav_output <- renderText({
191+
sprintf("Clicked %d times", input$nav_link)
192+
})
193+
}
194+
195+
nav_insert_subapp <- function() {
196+
# 6. Shiny subapps
197+
nav_insert(
198+
id = "main",
199+
select = TRUE,
200+
nav_panel(
201+
"Shiny app",
202+
p("There should be another shiny app in here."),
203+
shinyApp(
204+
ui = page_fluid(
205+
theme = bs_theme(preset = "darkly"),
206+
titlePanel("Hello from in here!"),
207+
p("This is a sub-app. Notice we're re-using the btn id."),
208+
actionButton("btn", "Click me"),
209+
verbatimTextOutput("debug")
210+
),
211+
server = function(input, output, session) {
212+
output$debug <- renderPrint(list(btn = input$btn))
213+
}
214+
)
215+
)
216+
)
217+
}
218+
219+
nav_insert_init_component <- function() {
220+
# `init_component()` renders differently if it goes through the cycle html ->
221+
# rendered -> html -> rendered, because the HTML of the element *after* being
222+
# attached to the DOM is different than it's initial HTML. In short, this
223+
# tests that web components are handled in a way that the connected callback
224+
# is only ever called once.
225+
226+
nav_insert(
227+
id = "main",
228+
select = TRUE,
229+
nav_panel(
230+
value = "Web Component",
231+
tagList(
232+
"Web",
233+
init_component("Component")
234+
),
235+
p(init_component()),
236+
p(init_component("custom init text"))
237+
)
238+
)
239+
}
240+
241+
server <- function(input, output, session) {
242+
choices <- reactiveVal(action_choices)
243+
244+
observe({
245+
updateSelectInput(
246+
session,
247+
"insert_type",
248+
choices = choices(),
249+
selected = input$insert_type
250+
)
251+
})
252+
253+
observeEvent(input$do_insert, {
254+
one_time_choice <- FALSE
255+
256+
switch(
257+
input$insert_type,
258+
"singleton" = nav_insert_singleton(session),
259+
"scripts" = nav_insert_scripts(session),
260+
"htmlwidgets" = nav_insert_htmlwidget(),
261+
"input_output_content" = {
262+
one_time_choice <- TRUE
263+
nav_insert_input_output_content(input, output)
264+
},
265+
"input_output_nav" = {
266+
one_time_choice <- TRUE
267+
nav_insert_input_output_nav(input, output)
268+
},
269+
"subapp" = nav_insert_subapp(),
270+
"init_component" = nav_insert_init_component()
271+
)
272+
273+
if (one_time_choice) {
274+
choices(choices()[choices() != input$insert_type])
275+
}
276+
})
277+
}
278+
279+
shinyApp(ui, server)
Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
shinytest2::test_app()
Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
# Load application support files into testing environment
2+
shinytest2::load_app_env()
3+

0 commit comments

Comments
 (0)