-
Notifications
You must be signed in to change notification settings - Fork 321
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Embed htmlwidgets in Examples #343
Conversation
Interesting idea! @jjallaire what do you think of this approach? |
@@ -113,6 +113,29 @@ replay_html.recordedplot <- function(x, name_prefix, obj_id, ...) { | |||
paste0("<img src='", escape_html(path), "' alt='' width='540' height='400' />") |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
on an unrelated note, this fixed aspect ratio can make the plot look a bit funny, although I don't see it to be a problem on ggplot2 🤔
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Why? We're setting the graphics device size above.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
You are totally right. And I cannot reproduce what I saw anymore 😨 maybe I misremembered.
This shouldn't be necessary (it's possible to fully capture all of the dependencies and include them in the pkgdown page tag). The entire model of htmlwidgets presumes multiple rendering environments and can definitely be adapted to pkgdown with a bit of extra effort. |
Note that HTML and htmlwidgets do in fact get capture by knitr/evaluate. They end up hitting the |
Thanks for the pointers @jjallaire ! Before I try your approach, can you elaborate a little on the advantage? Is it just against using iframe or is there any other reasons? It seems it will require propagating HTML dependency attributes all the way through |
iframes are okay if a bit slower due to reloading an entire set of dependencies in each iframe) when htmlwidgets have a fixed size, but many widgets have varying heights (e.g. DT) and it's really, really hard to lay these out in iframes (there are libraries that allow two-way handshaking between iframe parents and iframe content to work out sizing but I don't know how well they work). I'd argue that the easiest way to get html dependencies working is just to let Note that you can call |
@jjallaire so we'd generate an Rmd file including the examples, and then render that instead? |
I see this is a more narrow case than I was assuming it was (I thought we
were talking about the use of html/htmlwidgets within pkgdown articles, it
looks like this is just related to rendering examples. For this case you
wouldn't need to get render involved at all -- if you call knit_print and
collect the knit_meta afterwards you'll get the html as well as the
dependencies (in the knit_meta) which can then be fed through to whisker.
Here are the docs on knit_meta:
https://cran.r-project.org/web/packages/knitr/vignettes/knit_print.html
Here's another blog post where someone has unrolled enough of this to
figure out how to use htmlwidgets with jekyll:
https://brendanrocks.com/htmlwidgets-knitr-jekyll/
Here's the technique I mentioned before about using render in two steps to
capture the knit_meta then do something with it:
x <- rmarkdown::render("file.RMD", run_pandoc = FALSE, clean = FALSE)
knit_meta <- attr(x, "knit_meta")
rmarkdown::render( input = 'file.knit.md' , knit_meta = knit_meta )
Once you have the dependencies you can do a combination of copying them to
directories and building up the right stuff to put in the head tag, e.g.
https://github.com/rstudio/htmltools/blob/master/R/html_print.R#L71
If you can possibly call render directly that will make everything much
simpler to implement, but hopefully there is enough above to illustrate how
to go fully manual.
…On Fri, May 19, 2017 at 5:46 AM, Hadley Wickham ***@***.***> wrote:
@jjallaire <https://github.com/jjallaire> so we'd generate an Rmd file
including the examples, and then render that instead?
—
You are receiving this because you were mentioned.
Reply to this email directly, view it on GitHub
<#343 (comment)>, or mute
the thread
<https://github.com/notifications/unsubscribe-auth/AAGXx7JHyjU8rO6IHEpBrnjS7-t2Bgd5ks5r7WT-gaJpZM4Nf1xV>
.
|
@saurfang do you want to have a go at this? You'd need to replace |
Makes sense. Not right now. Maybe next week or so. Thanks for the help! |
This almost works. https://saurfang.github.io/pkgdown.example/reference/index.html Except that DT doesn't render style correctly I'll take further look at the second issue and polish my test package a little. Any chance you guys have insights to the first one? I banged my head for a little while on this already. |
What issue are you seeing with the DT style? This might simply be a matter
of pkgdown adding a couple extra CSS rules for DT (I do this in
flexdashboard already so it's not unheard of).
If you are doing set.seed then plotly should have no reason to keep
generating new IDs. If it is then I think you should take it up with them.
J.J.
…On Thu, May 25, 2017 at 2:36 AM, Forest Fang ***@***.***> wrote:
This almost works. https://saurfang.github.io/pkgdown.example/reference/
index.html
Except that DT doesn't render style correctly
https://saurfang.github.io/pkgdown.example/reference/datatable.html
and plotly jams in an unreproducible random id that makes pkgdown keeps
generating different HTML (when Rd changes and even if the example output
doesn't change).
https://saurfang.github.io/pkgdown.example/reference/ggplotly.html
https://github.com/ropensci/plotly/blob/2907bd40a715e203c3a4f09aa9c63b
e38cb28274/R/plotly.R#L364
https://github.com/ropensci/plotly/blob/15cd04927b8ee53b562601644e7ac6
6521c2b965/R/utils.R#L90
I'll take further look at the second issue and polish my test package a
little. Any chance you guys have insights to the first one? I banged my
head for a little while on this already.
—
You are receiving this because you were mentioned.
Reply to this email directly, view it on GitHub
<#343 (comment)>, or mute
the thread
<https://github.com/notifications/unsubscribe-auth/AAGXx_reyHpVwP9Aifx4az00TvWh-9Szks5r9SF4gaJpZM4Nf1xV>
.
|
It looks like DT is having problems because it's embedded inside a pre. |
Currently when HTML and htmlwidgets are called in RStudio, they automatically render in RStudio viewer and fail to be captured by `evaluate`. This bypasses the `print` for these two classes and instead save them to a local html file. The html is then embedded as an iframe in the reference.
Freeze the outer id to obj_id to avoid crazy diffs.
This might be benefitial in case there are nested widgets in the future.
Use `knit_print` to print examples so that HTML output can be correctly captured. The dependencies are pryed off from `knit_meta`, collected and injected in `head.html` template.
This prevents `pre` tampering style for `knit_asis` output, which is often htlmwidgets and can have strict styling requirments.
You are right. I can see if I close the The naive implementation leaves an empty |
R/rd-data.R
Outdated
expr <- evaluate::evaluate(text, code_env, new_device = TRUE) | ||
output_handler <- evaluate::new_output_handler( | ||
value = function(x) { | ||
if (isS4(x)) { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Is this branch really necessary?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Good point. It looks like the knit_print.default
calls knitr:::normal_print()
which already took care of this.
R/replay-html.r
Outdated
pieces[i] <- replay_html(parts[[i]], obj_id = i, ...) | ||
output <- replay_html(parts[[i]], obj_id = i, ...) | ||
dependencies[[i]] <- attr(output, "knit_meta") | ||
pieces[i] <- output |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Should also use [[
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I thought we generally use [
for primitive vectors?
@@ -113,6 +113,29 @@ replay_html.recordedplot <- function(x, name_prefix, obj_id, ...) { | |||
paste0("<img src='", escape_html(path), "' alt='' width='540' height='400' />") |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Why? We're setting the graphics device size above.
R/replay-html.r
Outdated
@@ -113,6 +137,14 @@ replay_html.recordedplot <- function(x, name_prefix, obj_id, ...) { | |||
paste0("<img src='", escape_html(path), "' alt='' width='540' height='400' />") | |||
} | |||
|
|||
#' @export | |||
replay_html.knit_asis <- function(x, name_prefix, obj_id, ...) { | |||
output <- paste0("</pre><div class='well knit_asis'>", x, "</div><pre class=\"example\">") |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
We need to cross-reference this with the example starting and ending tags to make sure it stays consistent.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
By "cross-referece", do you mean applying examples
css class?
inst/templates/head.html
Outdated
@@ -6,6 +6,9 @@ | |||
|
|||
<!-- jquery --> | |||
<script src="https://code.jquery.com/jquery-3.1.0.min.js" integrity="sha384-nrOSfDHtoPMzJHjVTdCopGqIqeYETSXhZDFyniQ8ZHcVy08QesyHcnOUpMpqnmWq" crossorigin="anonymous"></script> | |||
<script type="text/javascript"> | |||
var jQuery_3_1_0 = $.noConflict(false); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Can we just make this jquery
?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I think so because pkgdown.js
comes before HTML dependencies so it wouldn't have been overridden yet. I also put it inside an anonymous function to scope it better.
1. Remove import from methods 2. Correctly apply css class and styles 3. Hide empty dangling pre tag 4. Relocate jQuery noConflict call
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
All comments addressed. Everything looks good from my end: https://saurfang.github.io/pkgdown.example/reference/datatable.html
Let me know if you have any other thoughts or concerns. Otherwise, I can squash before merging.
I also filed an issue for plotly's unreproducible object: plotly/plotly.R#1048
@@ -113,6 +113,29 @@ replay_html.recordedplot <- function(x, name_prefix, obj_id, ...) { | |||
paste0("<img src='", escape_html(path), "' alt='' width='540' height='400' />") |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
You are totally right. And I cannot reproduce what I saw anymore 😨 maybe I misremembered.
R/rd-data.R
Outdated
expr <- evaluate::evaluate(text, code_env, new_device = TRUE) | ||
output_handler <- evaluate::new_output_handler( | ||
value = function(x) { | ||
if (isS4(x)) { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Good point. It looks like the knit_print.default
calls knitr:::normal_print()
which already took care of this.
R/replay-html.r
Outdated
pieces[i] <- replay_html(parts[[i]], obj_id = i, ...) | ||
output <- replay_html(parts[[i]], obj_id = i, ...) | ||
dependencies[[i]] <- attr(output, "knit_meta") | ||
pieces[i] <- output |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I thought we generally use [
for primitive vectors?
R/replay-html.r
Outdated
@@ -113,6 +137,14 @@ replay_html.recordedplot <- function(x, name_prefix, obj_id, ...) { | |||
paste0("<img src='", escape_html(path), "' alt='' width='540' height='400' />") | |||
} | |||
|
|||
#' @export | |||
replay_html.knit_asis <- function(x, name_prefix, obj_id, ...) { | |||
output <- paste0("</pre><div class='well knit_asis'>", x, "</div><pre class=\"example\">") |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
By "cross-referece", do you mean applying examples
css class?
inst/templates/head.html
Outdated
@@ -6,6 +6,9 @@ | |||
|
|||
<!-- jquery --> | |||
<script src="https://code.jquery.com/jquery-3.1.0.min.js" integrity="sha384-nrOSfDHtoPMzJHjVTdCopGqIqeYETSXhZDFyniQ8ZHcVy08QesyHcnOUpMpqnmWq" crossorigin="anonymous"></script> | |||
<script type="text/javascript"> | |||
var jQuery_3_1_0 = $.noConflict(false); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I think so because pkgdown.js
comes before HTML dependencies so it wouldn't have been overridden yet. I also put it inside an anonymous function to scope it better.
R/replay-html.r
Outdated
#' @export | ||
replay_html.knit_asis <- function(x, name_prefix, obj_id, ...) { | ||
# close and open pre tags because it sometimes breaks stylesheet of htmlwidgets | ||
output <- paste0("</pre><div class='examples'><div class='knit_asis'>", x, "</div></div><pre class='examples'>") |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
By cross-reference, I mean that we've now defined how to open and close and example block in two places. If we forget, and only update one, we'll get a weird display problem that probably won't get noticed for a while.
pieces[i] <- replay_html(parts[[i]], obj_id = i, ...) | ||
output <- replay_html(parts[[i]], obj_id = i, ...) | ||
dependencies[[i]] <- attr(output, "knit_meta") | ||
pieces[[i]] <- output |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The rule is to use [[
whenever you're extracting a single value.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Understood. Instead of pasting everything together and wrapping pre
in whisker, I pulled the pre
wrapping inside so we avoid the open/close hack for HTML pieces.
) %>% | ||
purrr::flatten() %>% | ||
purrr::map_chr(as.character) %>% | ||
unname() |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I tried the functional approach, which doesn't look great IMO. If I implement it imperatively by looping through elements and merging, the logic is probably even less readable. I can also implement this as reduce_right
by constantly checking the class of the first element in the accumulator. Let me know if you have any suggestions.
<pre class="examples">{{{ . }}}</pre> | ||
{{#bundles}} | ||
<div class="examples">{{{ . }}}</div> | ||
{{/bundles}} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Is there a way in mustache to avoid this nesting?
Right now I had to make it like so so that the anchor for Examples only shows if the examples are present:
{
"examples": {
"bundles": [
"...bundle 1...",
"...bundle 2...",
"..."
]
}
}
Hi! First, thanks for an awesome package! It takes documentation to the next level! I'm running Ubuntu 16.04 LTS with everything updated to the newest version as of today. |
This was in a working state minus the parts I mentioned before that were beyond pkgdown's control. I can rebase this if Hadley has the bandwidth to review and merge this. In your case, with this PR plotly widgets will render correctly but the source will change during every render due to unreproducible random seed used to generate widget id. |
I'm going to close this since pkgdown implementation has now changed radically since this was proposed. Sorry for not finding the time to merge it earlier! |
I just want to thank you first for such an awesome package. It adds so much flesh to the documentation.
Currently, when HTML and htmlwidgets are called in RStudio, they automatically render in RStudio viewer and fails to be captured by
evaluate
.This PR bypasses the
print
for these two classes and instead save them to a local HTML file. The HTML is then embedded as an iframe in the reference.p.s. I want to gather your thoughts first while I work on the test cases.