Below is a step by step introduction to the {bs4Dash}
structure.
This is the template to start with {bs4Dash}
:
library(shiny) library(bs4Dash) shinyApp( ui = dashboardPage( title = "Basic Dashboard", header = dashboardHeader(), sidebar = dashboardSidebar(), controlbar = dashboardControlbar(), footer = dashboardFooter(), body = dashboardBody() ), server = function(input, output) {} )
knitr::include_graphics("figures/basicPage.png")
The dashboardPage()
is the main wrapper:
dashboardPage( header, sidebar, body, controlbar = NULL, footer = NULL, title = NULL, freshTheme = NULL, preloader = NULL, options = NULL, fullscreen = FALSE, help = FALSE, dark = FALSE, scrollToTop = FALSE )
has mandatory slots for the navbar (dashboardHeader()
), sidebar (dashboardSidebar()
) and (dashboardBody()
).
Note the dashboardControlbar()
and dashboardFooter()
are optional. The title parameter gives its name to the
web browser tab. freshTheme, when provided, expects a {fresh}
powered theme created with fresh::create_theme()
.
It allows deeper customization of colors to fit very specific needs like industry brand colors. preloader expects
a loader tag built with waiter
, see more here, for instance:
preloader <- list(html = tagList(spin_1(), "Loading ..."), color = "#343a40")
At the moment, options are not available, but the idea is to provide deeper customization of the AdminLTE3 template like changing the sidebars and cards animation speed, ...
When fullscreen is TRUE, an icon is displayed in the navbar to switch to full screen mode. help automatically enable/disable all tooltips and popover that are present in the shiny app: this is an easier approach than using the server methods addPopover()
, addTooltip()
, ... but less specific. dark allows to toggle the dark mode: if FALSE, the theme switch is hidden and the dashboard takes the light design. scrollToTop allows to toggle the scroll to top button shown in the bottom right corner.
Now, it is time to fill this template!
Below we quickly describe the dashboardSidebar()
function:
dashboardSidebar( disable = FALSE, width = NULL, skin = "dark", status = "primary", elevation = 4, collapsed = FALSE, minified = TRUE, expandOnHover = TRUE, fixed = TRUE, id = "sidebar", customArea = NULL, ... )
A lot of options are available:
menuItem()
have.
There are 20 different colors listed in getAdminLTEColors()
.updateSidebar()
function to programmatically toggle the sidebar on the server.
input$<id>
indicates the state of the sidebar: TRUE means open and FALSE means collapsed/minified.The skin switch feature allows to automatically toggle the sidebar skin.
Importantly, the sidebar contains sidebarMenu()
as well as other items like sidebarUserPanel()
,
sidebarHeader()
:
sidebarUserPanel( image = "https://image.flaticon.com/icons/svg/1149/1149168.svg", name = "Welcome Onboard!" ) sidebarMenu( id = "sidebarmenu", sidebarHeader("Header 1"), menuItem( "Item 1", tabName = "item1", icon = icon("sliders") ), menuItem( "Item 2", tabName = "item2", icon = icon("id-card") ) )
sidebarMenu()
drives the navigation within your dashboard. It has an id parameter
which allows to :
input$<id>
.updateTabItems()
, which is actually
shiny::updateTabsetPanel
. sidebarMenu()
also offers 4 cosmetic parameters:
Interestingly, menuItem()
can be more than a simple item and contain sub-items,
namely menuSubItem()
:
menuItem( text = "Item List 1", icon = icon("bars"), startExpanded = TRUE, menuSubItem( text = "Item 3", tabName = "tab3", icon = icon("circle-thin") ), menuSubItem( text = "Item 4", tabName = "tab4", icon = icon("circle-thin") ) )
startExpanded defines whether the item container has to be opened when the app
starts. When a menuItem()
contains nested items, it is not necessary to give it
a tabName. text may also contain more complex HTML tags like dashboardBadge()
.
If you want to use menuItem()
to browse to an external website, use the href parameter
as well as newTab to open a new web browser tab.
Like in {shinydashboard}
, input$sidebarItemExpanded
hosts the value of the currently
expanded sidebarItem.
The dashboardHeader()
function creates a navbar for {bs4Dash}
:
dashboardHeader( title = NULL, titleWidth = NULL, disable = FALSE, .list = NULL, skin = "light", status = "white", border = TRUE, compact = FALSE, sidebarIcon = shiny::icon("bars"), controlbarIcon = shiny::icon("th"), fixed = FALSE, leftUi = NULL, rightUi = NULL )
The title parameter can host simple text but more complex content like dashboardBrand()
:
title <- dashboardBrand( title = "My dashboard", color = "primary", href = "https://adminlte.io/themes/v3", image = "https://adminlte.io/themes/v3/dist/img/AdminLTELogo.png" )
dashboardBrand()
is an enhanced title which has a color status, points to an optional url and may contain a logo.
The title width can be controlled by titleWidth, like in {shinydashboard}
. Like dashboardSidebar()
,
dashboardHeader()
offers a lot of theming options with skin and status, but also with border and
compact. They respectively show a bottom border and smaller text. sidebarIcon and controlbarIcon control
icons for sidebar and controlbar, respectively. The fixed parameter is useful when one wants to see the
navbar even at the bottom of the dashboard, without having to scroll up.
leftUi
, ...
and rightUi
are containers that can contains content from left to right. Ideally, we put
dropdownMenu()
as well as taskItem()
, messageItem()
, notificationItem()
, dashboardUser()
...
dashboardControlbar()
provides an extra sidebar, on the right side:
dashboardControlbar( id = NULL, disable = FALSE, width = 250, collapsed = TRUE, overlay = TRUE, skin = "dark", pinned = NULL )
Like the dashboardSidebar()
, dashboardControlbar()
may be programmatically toggled on the server
with updateControlbar()
, provided that the id parameter has a value. In practice, if no id is passed by the
user, {bs4Dash}
assigns a specific id. One can control the dashboardControlbar()
state at start with collapsed.
If TRUE, the controlbar is collapsed and inversely. By default, overlay is TRUE, meaning that the controlbar
opens on top of the body content. If FALSE, it pushes and the body content to the left. pinned allows the controlbar
to remain open even after a click outside (clicking outside collapses the controlbar by default). This is useful
to keep focus on important options whenever necessary. Finally, the controlbar is entirely themable, like
dashboardSidebar()
and dashboardHeader()
.
dashboardControlbar()
contains controlbarMenu()
that hosts controlbarItem()
. This feature is built on top the
shiny::tabsetPanel
, that has been rebranded for Bootstrap 4 compatibility:
controlbarMenu( ..., id = NULL, selected = NULL, type = c("tabs", "pills"), position = NULL, vertical = FALSE, side = "left", .list = NULL )
controlbarMenu()
may be updated on the server with updateControlbarMenu()
(which is no more than
shiny::updateTabsetPanel
). If you want to have a simple container without menu, you will have to add a specific
class to account for padding, as shown below:
dashboardControlbar( div( class = "p-3", # any content ) )
We will use dashboardFooter()
:
dashboardFooter( left = a( href = "https://twitter.com/divadnojnarg", target = "_blank", "@DivadNojnarg" ), right = "2020" )
Nothing special to add here!
dashboardBody()
is the main dashboard container:
dashboardBody( tabItems( tabItem( tabName = "item1", fluidRow( lapply(1:3, FUN = function(i) { sortable( width = 4, p(class = "text-center", paste("Column", i)), lapply(1:2, FUN = function(j) { box( title = paste0("I am the ", j, "-th card of the ", i, "-th column"), width = 12, "Click on my header" ) }) ) }) ) ), tabItem( tabName = "item2", box( title = "Card with messages", width = 9, userMessages( width = 12, status = "success", userMessage( author = "Alexander Pierce", date = "20 Jan 2:00 pm", image = "https://adminlte.io/themes/AdminLTE/dist/img/user1-128x128.jpg", type = "received", "Is this template really for free? That's unbelievable!" ), userMessage( author = "Dana Pierce", date = "21 Jan 4:00 pm", image = "https://adminlte.io/themes/AdminLTE/dist/img/user5-128x128.jpg", type = "sent", "Indeed, that's unbelievable!" ) ) ) ) ) )
The principle is pretty straightforward: all dashboardBody()
elements must be embeded in a tabItems()
list containing as may elements as the number of items. Each item is a tabItem()
.
Importantly, the tabName argument must be provide and unique. Moreover, it must be identical to the
corresponding menuItem()
, so that the navigation between tabs work.
This is exactly the same principle as for {shinydashboard}
. Therefore, users should not be lost.
In practice, if the sidebar is empty (without menu), it is still possible to get rid of
tabItems()
and tabItem()
.
Below is the code for your first {bs4Dash}
application:
Code
shinyApp( ui = dashboardPage( title = "Basic Dashboard", fullscreen = TRUE, header = dashboardHeader( title = dashboardBrand( title = "bs4Dash", color = "primary", href = "https://www.google.fr", image = "https://adminlte.io/themes/AdminLTE/dist/img/user2-160x160.jpg", ), skin = "light", status = "white", border = TRUE, sidebarIcon = icon("bars"), controlbarIcon = icon("th"), fixed = FALSE, leftUi = tagList( dropdownMenu( badgeStatus = "info", type = "notifications", notificationItem( inputId = "triggerAction2", text = "Error!", status = "danger" ) ), dropdownMenu( badgeStatus = "info", type = "tasks", taskItem( inputId = "triggerAction3", text = "My progress", color = "orange", value = 10 ) ) ), rightUi = dropdownMenu( badgeStatus = "danger", type = "messages", messageItem( inputId = "triggerAction1", message = "message 1", from = "Divad Nojnarg", image = "https://adminlte.io/themes/v3/dist/img/user3-128x128.jpg", time = "today", color = "lime" ) ) ), sidebar = dashboardSidebar( skin = "light", status = "primary", elevation = 3, sidebarUserPanel( image = "https://image.flaticon.com/icons/svg/1149/1149168.svg", name = "Welcome Onboard!" ), sidebarMenu( sidebarHeader("Header 1"), menuItem( "Item 1", tabName = "item1", icon = icon("sliders") ), menuItem( "Item 2", tabName = "item2", icon = icon("id-card") ) ) ), controlbar = dashboardControlbar( skin = "light", pinned = TRUE, collapsed = FALSE, overlay = FALSE, controlbarMenu( id = "controlbarmenu", controlbarItem( title = "Item 1", sliderInput( inputId = "obs", label = "Number of observations:", min = 0, max = 1000, value = 500 ), column( width = 12, align = "center", radioButtons( inputId = "dist", label = "Distribution type:", c( "Normal" = "norm", "Uniform" = "unif", "Log-normal" = "lnorm", "Exponential" = "exp" ) ) ) ), controlbarItem( "Item 2", "Simple text" ) ) ), footer = dashboardFooter( left = a( href = "https://twitter.com/divadnojnarg", target = "_blank", "@DivadNojnarg" ), right = "2018" ), body = dashboardBody( tabItems( tabItem( tabName = "item1", fluidRow( lapply(1:3, FUN = function(i) { sortable( width = 4, p(class = "text-center", paste("Column", i)), lapply(1:2, FUN = function(j) { box( title = paste0("I am the ", j, "-th card of the ", i, "-th column"), width = 12, "Click on my header" ) }) ) }) ) ), tabItem( tabName = "item2", box( title = "Card with messages", width = 9, userMessages( width = 12, status = "success", userMessage( author = "Alexander Pierce", date = "20 Jan 2:00 pm", image = "https://adminlte.io/themes/AdminLTE/dist/img/user1-128x128.jpg", type = "received", "Is this template really for free? That's unbelievable!" ), userMessage( author = "Dana Pierce", date = "21 Jan 4:00 pm", image = "https://adminlte.io/themes/AdminLTE/dist/img/user5-128x128.jpg", type = "sent", "Indeed, that's unbelievable!" ) ) ) ) ) ) ), server = function(input, output) {} )
* All credits go to https://codyhouse.co/gem/css-jquery-image-comparison-slider/ for the nice image slider widget!
Advanced shiny user would probably design shiny modules to generate this page, which I really encourage. However, how to deal with modules is not the purpose of this article.
Add the following code to your website.
For more information on customizing the embed code, read Embedding Snippets.