Add `includeHTMLDocument()` to embed a complete document
Nobody has claimed this yet.
Assessment
- Difficulty
- 5/5
- Estimated time
- Over a week
- Newbie friendliness
- 25/100
Research direction
Start with the proposed includeHTMLDocument() implementation and the example Shiny app in this issue. Review the unresolved cases from #382 and the linked py-shiny prior art, then determine which inclusion methods and path semantics the API should support. Done means the behavior and API are settled and the example app works for embedded documents and external URLs.
Written by the indexing model from the issue text.
Description
App and document authors frequently want to embed a complete document in the parent app or document, often generating the subdocument with R Markdown, Quarto or a stand-alone HTML widget.
An example implementation might look like this:
includeHTMLDocument <- function(path, ..., height = NULL, fill = is.null(height)) {
stopifnot(is.character(path), length(path) == 1)
iframe_args_default <- list(
width = "100%",
height = height %||% "400px",
frameborder = "0"
)
iframe_args <- utils::modifyList(iframe_args_default, dots_list(...))
if (inherits(path, "AsIs") || grepl("^http?s://", path)) {
iframe_args$src <- as.character(path)
} else {
lines <- readLines(path, warn=FALSE, encoding='UTF-8')
iframe_args$srcdoc <- paste8(lines, collapse='\n')
}
bindFillRoll(tags$iframe(class = "html-fill-item", !!!iframe_args), fill = fill)
}
There are three reasons we haven't moved forward with that, though:
-
Using the
srcdocattribute makes the most sense when the document is rendered withself_contained = TRUEorselfcontained = TRUE, which is the default inrmarkdown::render()andhtmlwidgets::saveWidget(), repsectively.When these aren't true, the resource paths in the subdocument are relative paths. The usage of
srcdocthen implicitly makes all relative paths in the subdocument relative to the parent document, which is often not the case. -
The best choice for
self_contained = TRUEis to render the document into subfolder that is also hosted, e.g. rendering into a folder that's automatically served with the app, likewww/, or rendering into a folder that has been attached withshiny::addResourcePath(). -
Authors would also like to use
includeHTMLDocument()to embed external URLs, which would use thesrcattribute.
It's easy to detect case 3 by checking path for a full URL starting with https://. Case 1 and 2 are hard to distinguish, we won't know the right choice from path alone. The implementation above tries to solve this by making srcdoc (case 1) the default and asking authors to opt into case 2 by wrapping the path in I(). Another option might be to use absolute (starting with /) paths or relative paths to distinguish between cases 1 and 2.
Part of what stalled progress in #382 is that it's nice when everything works out but it's easy to fall into an ambiguous use case where it's not clear which option to choose and how to nudge the user toward the right choice.
Finally, another option worth exploring is to use an htmlDependency() to make the document and its surrounding files available. There's some prior art in https://github.com/rstudio/py-shiny/pull/127 and this path would also motivate improvements across the board to all include*() functions, namely to add a method argument and provide more options as to how content is included in the app or HTML document.
Example app for testing
library(shiny)
library(bslib)
library(leaflet)
tmp_www <- fs::dir_create(tempfile())
# A complete html document containing a leaflet map
tmp_map <- file.path(tmp_www, "map.html")
map <- leaflet() |>
addTiles() |>
setView(-93.65, 42.0285, zoom = 17) |>
addPopups(-93.65, 42.0285, "Here is the <b>Department of Statistics</b>, ISU")
htmlwidgets::saveWidget(map, tmp_map, selfcontained = TRUE)
# An HTML fragment, not a complete document
tmp_html <- tempfile(fileext = ".html")
writeLines(format(as.tags(lorem::ipsum(3, 3))), tmp_html)
ui <- page_fluid(
titlePanel("Hello embedded html in Shiny!"),
card(
card_header("A leaflet map, fully encapsulated"),
full_screen = TRUE,
includeHTMLDocument(tmp_map)
),
h3("Just some more HTML"),
includeHTML(tmp_html)
)
srv <- function(input, output) {}
shinyApp(ui, srv)
- Dominant language
- R
- Stars
- 225
- Forks
- 73
- PR merge metrics
- No merged PRs in 30d
Contributor guide
First steps
- Read the whole issue, then the project's contributing guide.
- Comment on the issue to say you are picking it up — it saves two people doing the same work.
- Fork the repository and make your change on a branch.
- Open a pull request that references the issue number.
More from rstudio/htmltools
-
Difficulty 2/5 1-3 hours Newbie friendliness 72/100
-
Difficulty 4/5 3-5 days Newbie friendliness 38/100
-
Consider removing empty `.shiny-html-output` containers from document flow in fillable containers Open
Difficulty 3/5 1-2 days Newbie friendliness 45/100
-
Difficulty 2/5 1-3 hours Newbie friendliness 48/100
-
Difficulty 5/5 Over a week Newbie friendliness 35/100
All issues in rstudio/htmltools
Similar issues
-
Difficulty 2/5 1-3 hours Newbie friendliness 82/100
r-lib/pkgdepends#485 · 3 comments ·
-
Difficulty 1/5 Under an hour Newbie friendliness 92/100
-
beginners blocker
Difficulty 2/5 1-3 hours Newbie friendliness 78/100
-
enviPathR OpenBuild Error Build OK Build Warning policies-accepted pre-review precheck-passed
Difficulty 1/5 Under an hour Newbie friendliness 84/100
Bioconductor/BiocContributions#207 · 6 comments ·
-
Difficulty 2/5 1-3 hours Newbie friendliness 74/100
datacarpentry/semester-biology#1255 ·