17 Shiny apps
Shiny apps can be submitted to Bioconductor as software packages or as documented and tested functions within packages.
17.1 Code organisation
Shiny apps are divided into two parts:
- User interface (UI)
- Server
Shiny apps submitted to Bioconductor must store the code for both the UI and server code under the R/
directory of the package.
The bulk of the package code should not be implemented directly within the shinyApp()
function call.
Instead, internal package functions should be developed to produce and return individual components of the UI and server sides of the Shiny app.
We recommend the following file naming scheme for source files:
- Place internal functions that create observers – e.g.,
shiny::observeEvent()
– in files namedobservers_*.R
. Do this both for observers reacting to the Shiny serverinput
object, and reactive values stored in lists of reactive values (e.g.,shiny::reactiveValues()
). - Place internal functions that create UI elements – e.g.,
shiny::checkboxInput()
– in files namedinterface_*.R
. - Place internal functions that update the Shiny server
output
object in files namedoutputs_*.R
. - Place internal functions that perform miscellaneous processing steps in files named
utils_*.R
.
17.2 Running app
Functions in the package should return Shiny apps, but not launch them.
In other words, the function shiny::runApp()
should not be found anywhere in the package source code.
Instead, users should be left to call that function, using Shiny app objects returned by the package functions and options that control how the app is run (e.g., launch.browser = TRUE
).
For instance, the package source code should look as follows:
build_app <- function(...) {
ui <- .build_ui(...)
server <- .build_server(...)
app <- shinyApp(ui = ui, server = server)
}
While the user’s code should look as follows:
app <- build_app(...)
shiny::runApp(app, ...)
17.3 Building the package
Coming soon: comments on build issues for packages that contain Shiny apps.
17.4 Testing
All functions in the package should be testable using standard unit testing tools (e.g., testthat).
The use of # nocov start
and # nocov end
is allowed to ignore part of the code that cannot be tested using traditional unit tests (e.g., observeEvent
).
For instance:
# nocov start
observeEvent(input$example_input, {
# <do something>
})
# nocov end
Use files setup-*.R
in the subdirectory tests/testthat/
to generate only once objects that are used repeatedly as input for the unit tests.
17.5 Documentation
Man pages documenting functions that return Shiny apps should use the interactive()
function to demonstrate usage of the app.
For instance, a typical ‘Example’ section for such a man page should look as follows:
library(MyShinyPackage)
app <- build_app(...)
if (interactive()) {
shiny::runApp(app, ...)
}
Although optional, we highly recommend documenting internal functions of packages that contain Shiny apps. We recommend doing so in a way that is visible to developers but not users:
- Create man pages named
man/INTERNAL_*.Rd
- If using roxygen2, use the tag-value
@rdname INTERNAL_name_of_function
.
- If using roxygen2, use the tag-value
- In the file
man/.gitignore
(create it if it does not exist), add the lineINTERNAL_*
For instance:
#' @rdname INTERNAL_build_app
.build_app <- function(...) {
...
}