Skip to content

Commit 7dea260

Browse files
authored
bslib::accordion() tests (#152)
* Add tests for bslib accordion(), accordion_select(), accordion_remove(), and accordion_insert() * Add test for accordion_replace() * Updates for code review * Generate apps deps (GitHub Actions) * Fixes and use incoming sidebar() layout * Remove outdated comment * Do a better job of syncing up server-side displayed state with the UI * Wait for idle before taking screenshot * Clean up tests * Fix shinyjster test expectation * Make sure the set_inputs() calls complete in order --------- Co-authored-by: cpsievert <[email protected]>
1 parent edafa53 commit 7dea260

File tree

9 files changed

+225
-1
lines changed

9 files changed

+225
-1
lines changed

R/data-apps-deps.R

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -58,4 +58,4 @@ apps_deps_map <- list(`001-hello` = "rsconnect", `012-datatables` = "ggplot2",
5858
"rversions", "sf", "withr"), `302-bootswatch-themes` = c("ggplot2",
5959
"progress", "rversions", "sf", "withr"), `304-bslib-card` = c("rlang",
6060
"rversions"), `305-bslib-value-box` = c("rlang", "rversions"
61-
))
61+
), `306-accordion-add-remove` = "rlang")
Lines changed: 121 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,121 @@
1+
library(shiny)
2+
library(bslib)
3+
4+
ui <- page_fill(
5+
theme = bs_theme(
6+
# Don't transition when collapsing (so screenshot timing is less of an issue)
7+
"transition-collapse" = "none",
8+
"accordion-bg" = "#1E1E1E",
9+
"accordion-color" = "white",
10+
"accordion-icon-color" = "white",
11+
"accordion-icon-active-color" = "white"
12+
),
13+
layout_sidebar(
14+
border_radius = FALSE,
15+
border = FALSE,
16+
bg = "lightgray",
17+
sidebar(
18+
bg = "#1E1E1E",
19+
accordion(
20+
open = TRUE,
21+
accordion_panel(
22+
"Selected section(s)",
23+
selectInput("selected", NULL, LETTERS, multiple = TRUE, selected = "A"),
24+
),
25+
accordion_panel(
26+
"Displayed section(s)",
27+
selectInput("displayed", NULL, LETTERS, multiple = TRUE, selected = LETTERS)
28+
),
29+
accordion_panel(
30+
"Parameters",
31+
checkboxInput("multiple", "Allow multiple panels to be open", TRUE),
32+
checkboxInput("open_on_insert", "Open on insert", FALSE)
33+
)
34+
)
35+
),
36+
uiOutput("accordion")
37+
)
38+
)
39+
40+
server <- function(input, output, session) {
41+
42+
make_panel <- function(x) {
43+
accordion_panel(
44+
paste("Section", x),
45+
paste("Some narrative for section", x),
46+
value = x
47+
)
48+
}
49+
50+
# Allows us to track which panels are entering/exiting
51+
# (when input$displayed changes)
52+
displayed <- reactiveVal(LETTERS)
53+
54+
output$accordion <- renderUI({
55+
displayed(LETTERS)
56+
57+
accordion(
58+
id = "acc", multiple = input$multiple,
59+
!!!lapply(LETTERS, make_panel)
60+
)
61+
})
62+
63+
observeEvent(input$selected, ignoreInit = TRUE, {
64+
accordion_panel_set("acc", input$selected)
65+
})
66+
67+
observeEvent(input$acc, ignoreInit = TRUE, {
68+
updateSelectInput(inputId = "selected", selected = input$acc)
69+
})
70+
71+
observeEvent(input$displayed, ignoreInit = TRUE, {
72+
exit <- setdiff(displayed(), input$displayed)
73+
enter <- setdiff(input$displayed, displayed())
74+
75+
if (length(exit)) {
76+
accordion_panel_remove("acc", target = exit)
77+
}
78+
79+
if (length(enter)) {
80+
lapply(enter, function(x) {
81+
panel <- make_panel(x)
82+
if (identical("A", x)) {
83+
84+
# Can always be inserted at the top (no target required)
85+
accordion_panel_insert("acc", panel = panel, position = "before")
86+
87+
} else {
88+
89+
# Other letters require us to find the closest _currently displayed_
90+
# letter (to insert after)
91+
idx_displayed <- which(LETTERS %in% displayed())
92+
idx_insert <- match(x, LETTERS)
93+
idx_diff <- idx_insert - idx_displayed
94+
idx_diff[idx_diff < 0] <- NA
95+
target <- LETTERS[idx_displayed[which.min(idx_diff)]]
96+
accordion_panel_insert("acc", panel = panel, target = target, position = "after")
97+
98+
}
99+
100+
displayed(c(x, displayed()))
101+
})
102+
103+
if (input$open_on_insert) {
104+
accordion_panel_open("acc", enter)
105+
}
106+
}
107+
108+
displayed(input$displayed)
109+
})
110+
111+
observeEvent(displayed(), ignoreInit = TRUE, {
112+
updateSelectInput(inputId = "displayed", selected = displayed())
113+
updateSelectInput(
114+
inputId = "selected", choices = displayed(),
115+
selected = input$selected
116+
)
117+
})
118+
119+
}
120+
121+
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: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,2 @@
1+
# Load application support files into testing environment
2+
shinytest2::load_app_env()
Lines changed: 50 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,50 @@
1+
library(shinytest2)
2+
3+
test_that("{shinytest2} recording: accordion-select", {
4+
width <- 995
5+
height <- 1336
6+
7+
app <- AppDriver$new(
8+
variant = platform_variant(), name = "accordion-select",
9+
height = height, width = width,
10+
view = interactive(),
11+
options = list(bslib.precompiled = FALSE)
12+
)
13+
14+
# Make sure the set_input() calls complete in order
15+
set_inputs <- function(...) {
16+
app$set_inputs(...)
17+
app$wait_for_idle()
18+
}
19+
20+
# Test accordion_panel_set()
21+
set_inputs(selected = c("A", "D"))
22+
set_inputs(selected = c("A", "D", "H"))
23+
app$expect_screenshot()
24+
25+
# Test accordion_panel_remove()
26+
set_inputs(displayed = c("D", "F"))
27+
# Test accordion_panel_insert()
28+
set_inputs(displayed = c("A", "D", "F"))
29+
set_inputs(displayed = c("A", "D", "F", "Z"))
30+
# Test accordion_panel_insert() + accordion_panel_open()
31+
set_inputs(open_on_insert = TRUE)
32+
set_inputs(displayed = c("A", "D", "F", "J", "Z"))
33+
set_inputs(displayed = c("A", "D", "F", "J", "K", "Z"))
34+
app$expect_screenshot()
35+
36+
# redo tests with accordion(autoclose = TRUE)
37+
set_inputs(open_on_insert = FALSE)
38+
set_inputs(multiple = FALSE)
39+
40+
# Last one (D) should be selected
41+
set_inputs(selected = "B")
42+
set_inputs(selected = c("C", "D"))
43+
app$expect_screenshot()
44+
45+
set_inputs(displayed = c("A", "D", "F", "Z"))
46+
set_inputs(open_on_insert = TRUE)
47+
set_inputs(displayed = c("A", "D", "F", "J", "Z"))
48+
set_inputs(displayed = c("A", "D", "F", "J", "K", "Z"))
49+
app$expect_screenshot()
50+
})
Lines changed: 46 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,46 @@
1+
library(shiny)
2+
library(bslib)
3+
library(bsicons)
4+
5+
ui <- page_fluid(
6+
tags$style(".accordion {--bs-accordion-active-color: #dc3545; --bs-accordion-active-bg: rgba(220, 53, 69, 0.05)}"),
7+
accordion(
8+
id = "acc",
9+
accordion_panel(
10+
title = "Test failed",
11+
icon = bs_icon("x-circle"),
12+
value = "test-message",
13+
"Try again"
14+
)
15+
),
16+
shinyjster::shinyjster_js("
17+
var jst = jster(0);
18+
jst.add(Jster.shiny.waitUntilStable);
19+
jst.add(function() {
20+
Jster.assert.isEqual(
21+
$('#acc .accordion-button').text().trim(), 'Test passed',
22+
'accordion_mutate() did not update the accordion()'
23+
);
24+
});
25+
jst.test();
26+
")
27+
)
28+
29+
server <- function(input, output, session) {
30+
31+
shinyjster::shinyjster_server(input, output)
32+
33+
observe({
34+
accordion_panel_update(
35+
id = "acc", target = "test-message",
36+
title = "Test passed",
37+
icon = bs_icon("check-circle"),
38+
"Nicely done!"
39+
)
40+
41+
insertUI("body", ui = tags$style(".accordion {--bs-accordion-active-color: #198754; --bs-accordion-active-bg: rgba(25, 135, 84, 0.05) !important}"))
42+
})
43+
44+
}
45+
46+
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: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,2 @@
1+
# Load application support files into testing environment
2+
shinytest2::load_app_env()
Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
shinyjster::testthat_shinyjster("Execute hidden plot")

0 commit comments

Comments
 (0)