Testbed for developing htmlwidgets: How to iterate quickly?

We're currently developing an htmlwidget and we've wondered how to best iterate during development.

AFAIK, whenever I change the foo_widget.js js binding (or any of the other inst/htmlwidgets dependencies), I need to run R CMD Install, so that the dependencies get copied over to the library.
Only then can I run my foo_widget() function, and check out the results.
devtools::load_all() works for foo_widget.R, but not for the dependencies.

Needless to say, this is a bit cumbersome and takes too much time.

So I've used htmlwidgets::saveWidget(widget = "foo_widget", selfcontained = FALSE) to write out a "testbed" foo_widget.html.
I then move that foo_widget.html over to the root of inst/htmlwidgets, hack-fix all its dependency paths, and can then just open foo_widget.html in a browser, or use something like atom to conveniently edit my css and js, and directly inspect the results.

This works well enough, but feels a little hacky, because all of the foo_widget() options are now hard-coded into my HTML, and I can't quickly test several of those.

So my question is: Has anyone figured out a better way?

I was also considering to raise an issue at the htmlwidgets repo and ask them to do some black magic to recognize when devtools::load_all() is being used (I think htmltools does something like this?), and change the paths accordingly.
But I wanted to make sure first, I was on the right track.

Curious how other people develop htmlwidgets.
I'm not great in JS, so I need to iterate a lot :slight_smile:

1 Like

When you run devtools::load_all(), the package's files are not in the place expected by system.file(). Normally in an installed package, system.file() looks for files starting in the package's installation directory. However in the sources that you load with load_all(), those files will be in the inst/ subdirectory.

In order to make system.file() work with a package that has been loaded with load_all(), devtools gives the loaded package a modified version of system.file() which knows to look in inst/. You can see it here: https://github.com/r-lib/devtools/blob/v1.13.5/R/shims.r#L27-L100

The reason that htmlwidgets don't play well with your package loaded via load_all() is because inside of htmlwidgets, it calls system.file() to look for files in your package. If your copy of htmlwidgets was installed normally, it doesn't have the modified version of system.file(), and it therefore doesn't know to look in the inst/ subdirectory of your loaded package.

The two solutions are to use load_all() on htmlwidgets, so it gets the modified version of system.file(), or run the code in this gist, which shims the modified system.file() into an already-loaded copy of htmlwidgets: https://gist.github.com/wch/c942335660dc6c96322f

Once you've loaded done this and loaded your package with load_all(), you can simply edit your HTML and JS in your inst/ directory, and you won't even need to reload the package for the changes to be available.

6 Likes