16 Define dependencies

16.1 Introduction

The web provides a myriad of relevant open-source HTML templates like Colorlib and Creative Tim. Many of the RinteRface packages are actually built on top of those resources. However, some of them may require more efforts to work with shiny, for reasons mentioned in Chapter 3:

  • shiny is built on top of Bootstrap 3 (HTML, CSS and Javascript framework), and changing the framework will not be a trivial endeavor. However, shinymaterial and shiny.semantic are good examples that show this is possible.
  • shiny relies on jQuery. Consequently, all templates based upon React, Vue and other Javascript frameworks will not be natively supported. Again, there exist some examples for React with shiny and more generally, the reactR package developed by Kent Russell and Alan Dipert. Chapter 27 provides a general overview.

In the next chapters, we will focus on the pretty tabler.io dashboard template (Figure 16.1). We’ll describe how to create an R wrapper on top of it, thereby making it available for all Shiny users.

Tabler dashboard overview.

FIGURE 16.1: Tabler dashboard overview.

This chapter was written about two years ago, on top of the 1.0.0-alpha.7 GitHub release (https://github.com/tabler/tabler/tree/14d0c001436b85d2a4533d63680d209affdf774b). If the Tabler library significantly evolved since that date, the way to incorporate it into the Shiny ecosystem remains unchanged. Hence, the methods we describe below may be generalized to other templates. We recommend reading this chapter before the next part in Chapter 21, during which we present a more automated workflow, if you want to grasp the main concepts.

16.2 Discover the project

The first step of any template adaptation consists of exploring the underlying GitHub repository and looking for mandatory elements, like CSS/JS dependencies. This is a similar strategy if you want to incorporate an htmlwidget as well.

As depicted in Figure 16.2, the most important folders are:

  • dist, which contains CSS and JS files, as well as other libraries like Bootstrap and jQuery. It is also a good moment to look at the version of each dependency that might conflict with Shiny.
  • demo is the website folder used for demonstration purpose. This is our source to explore the template capabilities in depth.

The scss and build folder may be used to customize the tabler template directly. However, as stated above, directions on how to do so are out of the scope for this book.

GitHub project exploration.

FIGURE 16.2: GitHub project exploration.

16.3 Identify mandatory dependencies

Bootstrap 4, jQuery, tabler.min.css and tabler.min.js are key elements for the template, contrary to flag icons, which are optional (and take a lot of space). If your goal is to release your template on CRAN, be mindful of the 5 Mb maximum size limit. From personal experience, I can attest that this is quite challenging to manage.

To inspect dependencies, we proceed as follows:

  • Download or clone the GitHub repository.
  • Go to the demo folder and open the layout-dark.html file.
  • Open the HTML inspector.

As shown in Figure 16.3 left-hand side, we need to include the tabler.min.css from the header. If you are not convinced, try to remove it from the DOM and see what happens. jqvmap is actually related to an external visualization plugin used in the demo. Finally, the demo.min.css file is for the demo purpose. This will not prevent the template from working, so we will skip it for now. So far so good, we only need one file thus.

Tabler dependencies overview.Tabler dependencies overview.

FIGURE 16.3: Tabler dependencies overview.

JavaScript dependencies are shown on the right-hand side and located at the end of the body tag. Because we will not need all chart-related dependencies, such as apexcharts, jquery.vmap and vmap world, we may safely ignore them. We only retain the Bootstrap 4, jQuery core and tabler.min.js, in the same order.

16.4 Bundle dependencies

With the help of the htmlDependency() function, we are going to create our main Tabler HTML dependency containing all assets to allow our template to render properly. In this example, we are going to cheat a bit: instead of handling local files, we will use a CDN (content delivery network) that hosts all necessary Tabler assets. This avoids having to include all the necessary files in the R package, as well as in a GitHub repository.

For a production template that is designed to go on CRAN, we recommend hosting files locally, as described in Chapter 21.

library(htmltools)
tabler_cdn <- "https://cdn.jsdelivr.net/npm/tabler@1.0.0-alpha.7/"
tablers_deps <- htmlDependency(
  name = "tabler",
  version = "1.0.7", # we take that of tabler,
  src = c(href = tabler_cdn),
  script = "dist/js/tabler.min.js",
  stylesheet = "dist/css/tabler.min.css"
)

We advise the reader to create one HTML dependency per element. The Bootstrap version is 4.3.1. We can also use a CDN:

bs4_cdn <- "https://stackpath.bootstrapcdn.com/bootstrap/4.3.1/"
bs4_deps <- htmlDependency(
  name = "Bootstrap",
  version = "4.3.1",
  src = c(href = bs4_cdn),
  script = "js/bootstrap.bundle.min.js"
)

We finally create our dependency manager:

# add all dependencies to a tag. Don't forget to set 
# append to TRUE to preserve any existing dependency
add_tabler_deps <- function(tag) {
  # below, the order is of critical importance!
  deps <- list(bs4_deps, tablers_deps)
  attachDependencies(tag, deps, append = TRUE)
}

Notice the dependencies order in the deps list. This will be exactly the same order in the head of the HTML page. Some libraries require being loaded at a specific place, like the Tabler dependencies, which must come after Bootstrap.

Let’s see how to use add_tabler_deps(). We consider a <div> placeholder and check for its dependencies with findDependencies(). Then, we wrap it with add_tabler_deps():

tag <- div()
findDependencies(tag)
#> NULL
#> [[1]]
#> List of 10
#>  $ name      : chr "Bootstrap"
#>  $ version   : chr "4.3.1"
#>  $ src       :List of 1
#>   ..$ href: chr "https://.../bootstrap/4.3.1/js/"
#>  $ meta      : NULL
#>  $ script    : chr "bootstrap.bundle.min.js"
#>  $ stylesheet: NULL
#>  $ head      : NULL
#>  $ attachment: NULL
#>  $ package   : NULL
#>  $ all_files : logi TRUE
#>  - attr(*, "class")= chr "html_dependency"
#> 
#> [[2]]
#> List of 10
#>  $ name      : chr "tabler"
#>  $ version   : chr "1.0.7"
#>  $ src       :List of 1
#>   ..$ href: chr "https://.../tabler@1.0.0-alpha.7/dist/"
#>  $ meta      : NULL
#>  $ script    : chr "js/tabler.min.js"
#>  $ stylesheet: chr "css/tabler.min.css"
#>  $ head      : NULL
#>  $ attachment: NULL
#>  $ package   : NULL
#>  $ all_files : logi TRUE
#>  - attr(*, "class")= chr "html_dependency"

As shown above, our dependencies are applied to the div, in the correct order. This order is set by the list list(bs4_deps, tablers_deps) and allows us to avoid potential conflicts. If we try to run this simple tag in a Shiny app, we notice that all dependencies are added to the <head> tag, whereas the original template loads JavaScript dependencies in the <body>.

Unfortunately, htmltools does not allow developers to distribute dependencies in different places yet.

Here there is no impact, but this might be no-go for templates requiring JavaScript to be placed in the body. In practice, this is challenging to guess and may only be solved by manual testing.

library(shiny)
ui <- fluidPage(tag)
server <- function(input, output, session) {}
shinyApp(ui, server)

Even though the add_tabler_deps() function may be applied to any tag, we will use it with the core HTML template, which remains to be designed.

Would you like to see if our dependency system works? Let’s meet in the next chapter to design the main dashboard layout.