# Building an HTML Widget: Frappe Charts
This repository walks through the building of an
[htmlwidget](https://www.htmlwidgets.org/) around the JavaScript library
[Frappe Charts](https://frappe.io/charts).
Because there are many files and moving pieces involved in this process,
I’ve created a git repository that walks through the changes at each
step of the process. With my notes about each step, I’ve included the
SHA linked to the updates made during the step. If you’re viewing this
on GitHub, those SHA hashes should be converted to links that will take
you to a summary of which files changed at each step.
This introduction focuses on the mechanics of htmlwidgets. For a much
more detailed summary, the [HTML
Widgets](https://bookdown.org/yihui/rmarkdown/html-widgets.html) chapter
of the R Markdown book is an excellent introduction.
## Setup R Package
- [a3f6fdd986d2b98323b5be43e323df4f6a19f1f3](https://github.com/gadenbuie/js4shiny-frappeCharts/commit/a3f6fdd986d2b98323b5be43e323df4f6a19f1f3)
Create a package for this HTML widget. We’re not going to publish this,
so you can call it whatever you want
``` r
usethis::create_package("frappeCharts")
```
Add a dev script for notes
``` r
dir.create("dev")
file.create("dev/dev.R")
rstudioapi::navigateToFile("dev/dev.R")
```
## Setup npm package
- [256f0ca112b2685608f9a17a4fb4e35d279c9830](https://github.com/gadenbuie/js4shiny-frappeCharts/commit/256f0ca112b2685608f9a17a4fb4e35d279c9830)
Same process again, but this time for npm.
``` bash
npm init
# or
npm init -y
```
Open `package.json` and take a look
``` json
{
"name": "frappecharts",
"version": "0.0.1",
"description": "",
"main": "index.js",
"scripts": {
"test": "echo \"Error: no test specified\" && exit 1"
},
"author": "",
"license": "MIT"
}
```
From Frappe Charts
[docs\#installation](https://frappe.io/charts/docs#installation):
``` bash
npm install frappe-charts
```
We now have a dependency in `package.json` and there’s a
`package-lock.json` file.
``` json
"dependencies": {
"frappe-charts": "^1.3.0"
}
```
## Ignore node\_modules but add package-lock
There’s also a `node_modules/` folder with `frappe-charts/` inside. Add
`node_modules` to `.Rbuildignore` and `.gitignore`. (BTW, you can and
are supposed to commit `package-lock.json`.)
``` r
usethis::use_build_ignore("node_modules")
usethis::use_build_ignore("package.json")
usethis::use_build_ignore("package-lock.json")
usethis::use_git_ignore("node_modules")
```
## Scaffold the HTML widget
- [38bac2c65cf54816525076690310008e62ab99a1](https://github.com/gadenbuie/js4shiny-frappeCharts/commit/38bac2c65cf54816525076690310008e62ab99a1)
``` r
htmlwidgets::scaffoldWidget("frappeChart")
```
This adds files in `inst/htmlwidgets`
inst
└── htmlwidgets
├── frappeChart.js #<< R <-> JS code
└── frappeChart.yaml #<< list of dependencies
and creates a file `R/frappeChart.R` with the functions
- `frappeChart()`
- `frappeChartOutput()` (for shiny)
- `renderFrappeChart()` (for shiny)
## Use `npm` to get our dependencies in the right place
- [7abf0224345a67217c4a476f04eafe581f0ecec0](https://github.com/gadenbuie/js4shiny-frappeCharts/commit/7abf0224345a67217c4a476f04eafe581f0ecec0)
`htmlwidgets` load dependencies in a way that’s exactly the same as
using a `
We need to get our dependecy into a subfolder of `inst/htmlwidgets`.
Convention is `inst/htmlwidgets/lib/`. Rather than
creating the directoy and copying over, etc., we can have an `npm` build
script do this for us.
To avoid issues with mac/windows, we’ll add a dev dependency on
[`cpy-cli`](https://github.com/sindresorhus/cpy-cli). Dev dependencies
are node modules that are used to build a package, rather than required
for the package to work.
``` bash
npm install cpy-cli --save-dev
```
Then we create the folder `frappe-charts` under `inst/htmlwidgets/lib`
that will hold the Frappe Charts JavaScript dependency. (If the library
included other required files, we would move these too.)
``` r
dir.create("inst/htmlwidets/lib/frappe-charts", recursive = TRUE)
```
And then edit `package.json` to add a copy task. You can define scripts
that are runnable with `npm run `. For small build tasks,
this is an easy to implement build solution.
"scripts": {
"copy-js": "cpy 'node_modules/frappe-charts/dist/frappe-charts.min.iife*' inst/htmlwidgets/lib/frappe-charts/",
"build": "npm run copy-js"
}
Notice that running `npm run build` will also call `npm run copy-js`. If
we had more build tasks related to our JavaScript dependencies, like
linting or testing, we could add them as separate scripts and have them
run in the build process with `npm run && npm run `,
etc.
## Create a demo html\_document\_plain()
- [036d454f80d6036fc1ba35db92161fd19c053635](https://github.com/gadenbuie/js4shiny-frappeCharts/commit/036d454f80d6036fc1ba35db92161fd19c053635)
``` r
dir.create("dev/demo")
js4shiny::js4shiny_rmd(path = "dev/demo/demo.Rmd")
```
Use the example in the [Frappe Charts
Docs](https://frappe.io/charts/docs).
``` r
tagList(
div(id = "chart"),
htmltools::htmlDependency(
name = "frappe-charts",
version = "1.3.0",
package = "frappeCharts",
src = "htmlwidgets/lib/frappe-charts",
script = "frappe-charts.min.iife.js",
all_files = TRUE
)
)
```
And copy the JS into a javascript chunk.
⚠️ The dependencies won’t be found until you build/install.
``` r
devtools::document()
devtools::install()
```
If you get a path not found error
Error: path for html_dependency not found: inst/htmlwidgets/lib/frappe-charts
it’s most likely because
src = "inst/htmlwidgets/lib/frappe-charts"
should be relative to `inst`.
## Replace the example data with another data set and example
- [8fd703a08b021b8466171b83506f5fb0bf92f2ac](https://github.com/gadenbuie/js4shiny-frappeCharts/commit/8fd703a08b021b8466171b83506f5fb0bf92f2ac)
The first demo mixes chart types and we don’t want to do that. Use the
example from [Basic
Chart](https://frappe.io/charts/docs/basic/basic_chart#adding-more-datasets).
``` js
const data = {
labels: ["Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat", "Sun"],
datasets: [
{ name: "R", values: [18, 40, 30, 35, 8, 52, 17, -4] },
{ name: "Python", values: [30, 50, -10, 15, 18, 32, 27, 14] }
]
}
```
Then re-create this data in an R chunk
([881c12ffdbdaa017863c918f61fa6208400d6130](https://github.com/gadenbuie/js4shiny-frappeCharts/commit/881c12ffdbdaa017863c918f61fa6208400d6130)):
``` r
data <- list(
labels = c("Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat", "Sun"),
datasets = list(
list(name = "R", values = c(18, 40, 30, 35, 8, 52, 17, -4)),
list(name = "Python", values = c(30, 50, -10, 15, 18, 32, 27, 14))
)
)
```
To get the data out of R and make it available in the document,
`htmlwidgets` embeds the data in a `` element in the page. Embed the
data from the R chunk in a `