Build-a-Dashboard Workshop
Duke University
Posit, PBC
2024-08-12
Icon, title, and author along with links to sub-pages if more than one page is defined.
Rows and columns using markdown heading, with optional attributes to control height, width, etc. Sidebars, mostly used for for interactive inputs. Tabsets to further divide content.
Cards are containers for code cell outputs (e.g., plots, tables, value boxes) and free form markdown text. The content of cards typically maps to cells in your notebook or source document.
The examples in this module will primarily feature R code in code cells
All code in code cells can be replaced with Python, Julia, Observable, etc. and the layout would look exactly the same
When/if different behavior in layout is expected, this will be explicitly called out and examples in other computing languages will also be provided
dashboard-r.qmd
---
title: "Navigation buttons"
format:
dashboard:
logo: images/beetle.png
nav-buttons:
- icon: github
href: https://github.com/quarto-dev/quarto-cli
aria-label: GitHub
- icon: linkedin
href: https://www.linkedin.com/company/posit-software/
aria-label: LinkedIn
- icon: youtube
href: https://youtube.com/
aria-label: YouTube
---
```{r}
library(ggplot2)
```
# Scatter
```{r}
#| title: Highway vs. city mileage
ggplot(mpg, aes(x = cty, y = hwy)) +
geom_point()
```
# Bar
```{r}
#| title: Drive types
ggplot(mpg, aes(x = drv)) +
geom_bar()
```
dashboard-r.qmd
---
title: "Navigation buttons"
format:
dashboard:
logo: images/beetle.png
nav-buttons:
- icon: github
href: https://github.com/quarto-dev/quarto-cli
aria-label: GitHub
- icon: linkedin
href: https://www.linkedin.com/company/posit-software/
aria-label: LinkedIn
- icon: youtube
href: https://youtube.com/
aria-label: YouTube
---
```{r}
library(ggplot2)
```
# Scatter
```{r}
#| title: Highway vs. city mileage
ggplot(mpg, aes(x = cty, y = hwy)) +
geom_point()
```
# Bar
```{r}
#| title: Drive types
ggplot(mpg, aes(x = drv)) +
geom_bar()
```
Special aliases are recognized for certain navigation buttons: linkedin
, facebook
, reddit
, twitter
, and github
.
dashboard-r.qmd
---
title: "Navigation buttons - alternative"
format:
dashboard:
logo: images/beetle.png
nav-buttons: [linkedin]
linkedin: https://www.linkedin.com/company/posit-software
---
```{r}
library(ggplot2)
```
# Scatter
```{r}
#| title: Highway vs. city mileage
ggplot(mpg, aes(x = cty, y = hwy)) +
geom_point()
```
# Bar
```{r}
#| title: Drive types
ggplot(mpg, aes(x = drv)) +
geom_bar()
```
For information about the dashboard, data, etc. and/or for user inputs in interactive dashboards
Can be at the global or page level
dashboard-r.qmd
---
title: "Sidebars - Global"
format:
dashboard:
logo: images/beetle.png
nav-buttons:
- icon: github
href: https://github.com/quarto-dev/quarto-cli
aria-label: GitHub
- icon: linkedin
href: https://www.linkedin.com/company/posit-software/
aria-label: LinkedIn
- icon: youtube
href: https://youtube.com/
aria-label: YouTube
---
```{r}
library(ggplot2)
```
# {.sidebar}
Information and/or interactivity tools that you want on every page go here.
# Scatter
```{r}
#| title: Highway vs. city mileage
ggplot(mpg, aes(x = cty, y = hwy)) +
geom_point()
```
# Bar
```{r}
#| title: Drive types
ggplot(mpg, aes(x = drv)) +
geom_bar()
```
dashboard-r.qmd
---
title: "Sidebars - Global"
format:
dashboard:
logo: images/beetle.png
nav-buttons:
- icon: github
href: https://github.com/quarto-dev/quarto-cli
aria-label: GitHub
- icon: linkedin
href: https://www.linkedin.com/company/posit-software/
aria-label: LinkedIn
- icon: youtube
href: https://youtube.com/
aria-label: YouTube
---
```{r}
library(ggplot2)
```
# {.sidebar}
Information and/or interactivity tools that you want on every page go here.
# Scatter
```{r}
#| title: Highway vs. city mileage
ggplot(mpg, aes(x = cty, y = hwy)) +
geom_point()
```
# Bar
```{r}
#| title: Drive types
ggplot(mpg, aes(x = drv)) +
geom_bar()
```
dashboard-r.qmd
---
title: "Sidebars - Global"
format:
dashboard:
logo: images/beetle.png
nav-buttons:
- icon: github
href: https://github.com/quarto-dev/quarto-cli
aria-label: GitHub
- icon: linkedin
href: https://www.linkedin.com/company/posit-software/
aria-label: LinkedIn
- icon: youtube
href: https://youtube.com/
aria-label: YouTube
---
```{r}
library(ggplot2)
```
# {.sidebar}
Information and/or interactivity tools that you want on every page go here.
# Scatter
```{r}
#| title: Highway vs. city mileage
ggplot(mpg, aes(x = cty, y = hwy)) +
geom_point()
```
# Bar
```{r}
#| title: Drive types
ggplot(mpg, aes(x = drv)) +
geom_bar()
```
dashboard-r.qmd
---
title: "Sidebars - Per page"
format:
dashboard:
logo: images/beetle.png
nav-buttons:
- icon: github
href: https://github.com/quarto-dev/quarto-cli
aria-label: GitHub
- icon: linkedin
href: https://www.linkedin.com/company/posit-software/
aria-label: LinkedIn
- icon: youtube
href: https://youtube.com/
aria-label: YouTube
---
```{r}
library(ggplot2)
```
# Scatter
## {.sidebar}
Information and/or interactivity tools that you want on a single page go here.
##
```{r}
#| title: Highway vs. city mileage
ggplot(mpg, aes(x = cty, y = hwy)) +
geom_point()
```
# Bar
```{r}
#| title: Drive types
ggplot(mpg, aes(x = drv)) +
geom_bar()
```
dashboard-r.qmd
---
title: "Sidebars - Per page"
format:
dashboard:
logo: images/beetle.png
nav-buttons:
- icon: github
href: https://github.com/quarto-dev/quarto-cli
aria-label: GitHub
- icon: linkedin
href: https://www.linkedin.com/company/posit-software/
aria-label: LinkedIn
- icon: youtube
href: https://youtube.com/
aria-label: YouTube
---
```{r}
library(ggplot2)
```
# Scatter
## {.sidebar}
Information and/or interactivity tools that you want on a single page go here.
##
```{r}
#| title: Highway vs. city mileage
ggplot(mpg, aes(x = cty, y = hwy)) +
geom_point()
```
# Bar
```{r}
#| title: Drive types
ggplot(mpg, aes(x = drv)) +
geom_bar()
```
dashboard-r.qmd
---
title: "Sidebars - Per page"
format:
dashboard:
logo: images/beetle.png
nav-buttons:
- icon: github
href: https://github.com/quarto-dev/quarto-cli
aria-label: GitHub
- icon: linkedin
href: https://www.linkedin.com/company/posit-software/
aria-label: LinkedIn
- icon: youtube
href: https://youtube.com/
aria-label: YouTube
---
```{r}
library(ggplot2)
```
# Scatter
## {.sidebar}
Information and/or interactivity tools that you want on a single page go here.
##
```{r}
#| title: Highway vs. city mileage
ggplot(mpg, aes(x = cty, y = hwy)) +
geom_point()
```
# Bar
```{r}
#| title: Drive types
ggplot(mpg, aes(x = drv)) +
geom_bar()
```
By default, cards are laid out in rows:
Default value of orientation
is rows
:
dashboard-r.qmd
Setting orientation
to columns
makes each ##
indicate a column instead of a row:
dashboard-r.qmd
---
title: "Columns"
format:
dashboard:
orientation: columns
logo: images/beetle.png
---
```{r}
library(ggplot2)
```
## Scatter
```{r}
#| title: Highway vs. city mileage
ggplot(mpg, aes(x = cty, y = hwy)) +
geom_point()
```
## Bar
```{r}
#| title: Drive types
ggplot(mpg, aes(x = drv)) +
geom_bar()
```
dashboard-r.qmd
---
title: "Columns"
format:
dashboard:
orientation: columns
logo: images/beetle.png
---
```{r}
library(ggplot2)
```
## Scatter
```{r}
#| title: Highway vs. city mileage
ggplot(mpg, aes(x = cty, y = hwy)) +
geom_point()
```
## Bar
```{r}
#| title: Drive types
ggplot(mpg, aes(x = drv)) +
geom_bar()
```
dashboard-r.qmd
---
title: "Rows, then columns"
format:
dashboard:
logo: images/beetle.png
---
```{r}
library(ggplot2)
```
## Overview
###
This dashboard summarizes an illuminating analysis of fuel economy of cars.
###
This is a car.
![](images/car.png){fig-alt="Illustration of a teal color car."}
## Plots
### Scatter
```{r}
#| title: Highway vs. city mileage
ggplot(mpg, aes(x = cty, y = hwy)) +
geom_point()
```
### Bar
```{r}
#| title: Drive types
ggplot(mpg, aes(x = drv)) +
geom_bar()
```
dashboard-r.qmd
---
title: "Rows, then columns"
format:
dashboard:
logo: images/beetle.png
---
```{r}
library(ggplot2)
```
## Overview
###
This dashboard summarizes an illuminating analysis of fuel economy of cars.
###
This is a car.
![](images/car.png){fig-alt="Illustration of a teal color car."}
## Plots
### Scatter
```{r}
#| title: Highway vs. city mileage
ggplot(mpg, aes(x = cty, y = hwy)) +
geom_point()
```
### Bar
```{r}
#| title: Drive types
ggplot(mpg, aes(x = drv)) +
geom_bar()
```
dashboard-r.qmd
---
title: "Rows, then columns"
format:
dashboard:
orientation: columns
logo: images/beetle.png
---
```{r}
library(ggplot2)
```
## Overview
###
This dashboard summarizes an illuminating analysis of fuel economy of cars.
###
This is a car.
![](images/car.png){fig-alt="Illustration of a teal color car."}
## Plots
### Scatter
```{r}
#| title: Highway vs. city mileage
ggplot(mpg, aes(x = cty, y = hwy)) +
geom_point()
```
### Bar
```{r}
#| title: Drive types
ggplot(mpg, aes(x = drv)) +
geom_bar()
```
dashboard-r.qmd
---
title: "Rows, then columns"
format:
dashboard:
orientation: columns
logo: images/beetle.png
---
```{r}
library(ggplot2)
```
## Overview
###
This dashboard summarizes an illuminating analysis of fuel economy of cars.
###
This is a car.
![](images/car.png){fig-alt="Illustration of a teal color car."}
## Plots
### Scatter
```{r}
#| title: Highway vs. city mileage
ggplot(mpg, aes(x = cty, y = hwy)) +
geom_point()
```
### Bar
```{r}
#| title: Drive types
ggplot(mpg, aes(x = drv)) +
geom_bar()
```
dashboard-r.qmd
---
title: "Rows, then columns"
format:
dashboard:
logo: images/beetle.png
---
```{r}
library(ggplot2)
```
## Overview {height="30%"}
### {width="20%"}
This dashboard summarizes an illuminating analysis of fuel economy of cars.
### {width="80%"}
This is a car.
![](images/car.png){fig-alt="Illustration of a teal color car." width="300"}
## Plots {height="70%"}
### Scatter {width="75%"}
```{r}
#| title: Highway vs. city mileage
ggplot(mpg, aes(x = cty, y = hwy)) +
geom_point()
```
### Bar {width="25%"}
```{r}
#| title: Drive types
ggplot(mpg, aes(x = drv)) +
geom_bar()
```
dashboard-r.qmd
---
title: "Rows, then columns"
format:
dashboard:
logo: images/beetle.png
---
```{r}
library(ggplot2)
```
## Overview {height="30%"}
### {width="20%"}
This dashboard summarizes an illuminating analysis of fuel economy of cars.
### {width="80%"}
This is a car.
![](images/car.png){fig-alt="Illustration of a teal color car." width="300"}
## Plots {height="70%"}
### Scatter {width="75%"}
```{r}
#| title: Highway vs. city mileage
ggplot(mpg, aes(x = cty, y = hwy)) +
geom_point()
```
### Bar {width="25%"}
```{r}
#| title: Drive types
ggplot(mpg, aes(x = drv)) +
geom_bar()
```
fill
vs. flow
Each row in a dashboard that is not given an explicit height will determine its size by either fill
ing available space or by flow
ing to its natural height
fill
ling layout is generally the default, however for certain types of content (e.g., markdown text) flow
layout is the default.
If the default behavior is not what you want, you can explicitly specify filling or flowing behavior using the .fill
and .flow
classes applied to a row.
scrolling
Need more space? Set scrolling: true
for accommodate page overflow.
Note
This option is handy for adding more rows of cards to a dashboard, however spreading content across pages and tabsets can be a better way to present information.
orientation
and scrolling
can be provided at the dashboard or page level:
dashboard-r.qmd
---
title: "Rows, then columns"
format:
dashboard:
logo: images/beetle.png
---
```{r}
library(ggplot2)
```
## Overview {orientation="rows"}
###
This dashboard summarizes an illuminating analysis of fuel economy of cars.
###
This is a car.
![](images/car.png){fig-alt="Illustration of a teal color car."}
## Plots {orientation="columns" scrolling="true"}
### Scatter
```{r}
#| title: Highway vs. city mileage
ggplot(mpg, aes(x = cty, y = hwy)) +
geom_point()
```
### Bar
```{r}
#| title: Drive types
ggplot(mpg, aes(x = drv)) +
geom_bar()
```
dashboard-r.qmd
---
title: "Tabsets"
format:
dashboard:
logo: images/beetle.png
---
```{r}
library(ggplot2)
```
## Overview {.tabset}
### Objective
This dashboard summarizes an illuminating analysis of fuel economy of cars.
### Car
This is a car.
![](images/car.png){fig-alt="Illustration of a teal color car." width="299"}
## Plots {.tabset}
### Scatter
```{r}
#| title: Highway vs. city mileage
ggplot(mpg, aes(x = cty, y = hwy)) +
geom_point()
```
### Bar
```{r}
#| title: Drive types
ggplot(mpg, aes(x = drv)) +
geom_bar()
```
Each card within a row/column or each row/column within another becomes a tab:
dashboard-r.qmd
---
title: "Tabsets"
format:
dashboard:
logo: images/beetle.png
---
```{r}
library(ggplot2)
```
## Overview {.tabset}
### Objective
This dashboard summarizes an illuminating analysis of fuel economy of cars.
### Car
This is a car.
![](images/car.png){fig-alt="Illustration of a teal color car." width="299"}
## Plots {.tabset}
### Scatter
```{r}
#| title: Highway vs. city mileage
ggplot(mpg, aes(x = cty, y = hwy)) +
geom_point()
```
### Bar
```{r}
#| title: Drive types
ggplot(mpg, aes(x = drv)) +
geom_bar()
```
Fundamental unit of display within dashboards
Created automatically for cells and markdown content that are within rows and columns
Can be created manually with a card
div:
Cards provide an expand button which appears at bottom right on hover
Card titles can be provided as a cell option or within a cat()
(R) or print()
(Python) statement:
dashboard-r.qmd
---
title: "Card titles"
format:
dashboard:
logo: images/beetle.png
---
```{r}
library(ggplot2)
```
```{r}
#| title: Highway vs. city mileage
ggplot(mpg, aes(x = cty, y = hwy)) +
geom_point()
```
```{r}
n_cars <- nrow(mpg)
cat("title=", "Drive types of", n_cars, "cars")
ggplot(mpg, aes(x = drv)) +
geom_bar()
```
dashboard-r.qmd
---
title: "Card titles"
format:
dashboard:
logo: images/beetle.png
---
```{r}
library(ggplot2)
```
```{r}
#| title: Highway vs. city mileage
ggplot(mpg, aes(x = cty, y = hwy)) +
geom_point()
```
```{r}
n_cars <- nrow(mpg)
cat("title=", "Drive types of", n_cars, "cars")
ggplot(mpg, aes(x = drv)) +
geom_bar()
```
Which of the following cells will become a card in a dashboard?
If a cell generates multiple outputs, they get wrapped in a single cell:
You can control layout of multiple outputs with layout-ncol
:
Value boxes are a great way to prominently display simple values within a dashboard
You can create value boxes in executable cells or plain markdown in divs
Value boxes use Bootstrap icons (https://icons.getbootstrap.com) and can be set to any color (e.g., with a HEX code) or color alias defined by the theme (we’ll get to themes later!)
Color alias | Default theme color(s) |
---|---|
primary |
Blue |
secondary |
Gray |
success |
Green |
info |
Bright Blue |
warning |
Yellow/Orange |
danger |
Red |
light |
Light Gray |
dark |
Black |
dashboard-r.qmd
---
title: "Valueboxes"
format: dashboard
---
```{r}
library(ggplot2)
library(dplyr)
```
## Value boxes {height="25%"}
```{r}
#| label: calculate-values
lowest_mileage_cty <- mpg |>
filter(cty == min(cty)) |>
distinct(cty) |>
pull(cty)
highest_mileage_cty <- mpg |>
filter(cty == max(cty)) |>
distinct(cty) |>
pull(cty)
rounded_mean_city_mileage <- mpg |>
summarize(round(mean(cty), 2)) |>
pull()
```
```{r}
#| content: valuebox
#| title: "Least efficient"
#| icon: fuel-pump-fill
#| color: danger
list(
value = paste(lowest_mileage_cty, "mpg")
)
```
```{r}
#| content: valuebox
#| title: "Most efficient"
list(
icon = "fuel-pump",
color = "success",
value = paste(highest_mileage_cty, "mpg")
)
```
::: {.valuebox icon="fuel-pump" color="secondary"}
Average city mileage
`{r} rounded_mean_city_mileage` mpg
:::
## Plots {height="75%"}
```{r}
#| title: Highway vs. city mileage
ggplot(mpg, aes(x = cty, y = hwy)) +
geom_point()
```
```{r}
#| title: Drive types
ggplot(mpg, aes(x = drv)) +
geom_bar()
```
dashboard-r.qmd
---
title: "Valueboxes"
format: dashboard
---
```{r}
library(ggplot2)
library(dplyr)
```
## Value boxes {height="25%"}
```{r}
#| label: calculate-values
lowest_mileage_cty <- mpg |>
filter(cty == min(cty)) |>
distinct(cty) |>
pull(cty)
highest_mileage_cty <- mpg |>
filter(cty == max(cty)) |>
distinct(cty) |>
pull(cty)
rounded_mean_city_mileage <- mpg |>
summarize(round(mean(cty), 2)) |>
pull()
```
```{r}
#| content: valuebox
#| title: "Least efficient"
#| icon: fuel-pump-fill
#| color: danger
list(
value = paste(lowest_mileage_cty, "mpg")
)
```
```{r}
#| content: valuebox
#| title: "Most efficient"
list(
icon = "fuel-pump",
color = "success",
value = paste(highest_mileage_cty, "mpg")
)
```
::: {.valuebox icon="fuel-pump" color="secondary"}
Average city mileage
`{r} rounded_mean_city_mileage` mpg
:::
## Plots {height="75%"}
```{r}
#| title: Highway vs. city mileage
ggplot(mpg, aes(x = cty, y = hwy)) +
geom_point()
```
```{r}
#| title: Drive types
ggplot(mpg, aes(x = drv)) +
geom_bar()
```
dashboard-py.qmd
---
title: "Valueboxes"
format: dashboard
---
```{python}
from plotnine import ggplot, aes, geom_point, geom_bar
from plotnine.data import mpg
```
## Value boxes {height="25%"}
```{python}
#| label: calculate-values
lowest_mileage_index = mpg['cty'].idxmin()
lowest_mileage_car = mpg.iloc[lowest_mileage_index]
lowest_mileage_cty = mpg.loc[lowest_mileage_index, 'cty']
highest_mileage_index = mpg['cty'].idxmax()
highest_mileage_car = mpg.iloc[highest_mileage_index]
highest_mileage_cty = mpg.loc[highest_mileage_index, 'cty']
mean_city_mileage = mpg['cty'].mean()
rounded_mean_city_mileage = round(mean_city_mileage, 2)
```
```{python}
#| content: valuebox
#| title: "Least efficient"
#| icon: fuel-pump-fill
#| color: danger
dict(
value = str(f"{lowest_mileage_cty} mpg")
)
```
```{python}
#| content: valuebox
#| title: "Most efficient"
dict(
icon = "fuel-pump",
color = "success",
value = str(f"{highest_mileage_cty} mpg")
)
```
::: {.valuebox icon="fuel-pump" color="secondary"}
Average city mileage
`{python} str(rounded_mean_city_mileage)` mpg
:::
## Plots {height="75%"}
```{python}
#| title: Highway vs. city mileage
(
ggplot(mpg, aes(x = "cty", y = "hwy"))
+ geom_point()
)
```
```{python}
#| title: Drive types
(
ggplot(mpg, aes(x = "drv"))
+ geom_bar()
)
```
dashboard-py.qmd
---
title: "Valueboxes"
format: dashboard
---
```{python}
from plotnine import ggplot, aes, geom_point, geom_bar
from plotnine.data import mpg
```
## Value boxes {height="25%"}
```{python}
#| label: calculate-values
lowest_mileage_index = mpg['cty'].idxmin()
lowest_mileage_car = mpg.iloc[lowest_mileage_index]
lowest_mileage_cty = mpg.loc[lowest_mileage_index, 'cty']
highest_mileage_index = mpg['cty'].idxmax()
highest_mileage_car = mpg.iloc[highest_mileage_index]
highest_mileage_cty = mpg.loc[highest_mileage_index, 'cty']
mean_city_mileage = mpg['cty'].mean()
rounded_mean_city_mileage = round(mean_city_mileage, 2)
```
```{python}
#| content: valuebox
#| title: "Least efficient"
#| icon: fuel-pump-fill
#| color: danger
dict(
value = str(f"{lowest_mileage_cty} mpg")
)
```
```{python}
#| content: valuebox
#| title: "Most efficient"
dict(
icon = "fuel-pump",
color = "success",
value = str(f"{highest_mileage_cty} mpg")
)
```
::: {.valuebox icon="fuel-pump" color="secondary"}
Average city mileage
`{python} str(rounded_mean_city_mileage)` mpg
:::
## Plots {height="75%"}
```{python}
#| title: Highway vs. city mileage
(
ggplot(mpg, aes(x = "cty", y = "hwy"))
+ geom_point()
)
```
```{python}
#| title: Drive types
(
ggplot(mpg, aes(x = "drv"))
+ geom_bar()
)
```
You can include markdown text anywhere within a dashboard
Markdown text is automatically placed in cards, but you can also explicitly put them in cards with fenced divs, and add titles to cards
You can place markdown text and other cell output in a single card as well
Option 1 - Cloud: Go to Posit Cloud for the workshop, open project titled 2 - Dashboards components
.
Option 2 - Local: Clone the GitHub repo posit-conf-2024/olympicdash
(https://github.com/posit-conf-2024/olympicdash) and work on olympicdash-r-2.qmd.
Python - Local: Clone the GitHub repo posit-conf-2024/olympicdash
(https://github.com/posit-conf-2024/olympicdash) and work on olympicdash-py-2.qmd.
Your goal is to create a dashboard that looks like the following:
05:00
In the Summer Olympics page:
05:00
In the Summer Olympics page:
10:00
In the first row of the second column of the Summer Olympics page, add value boxes for highest numbers of gold, silver, and bronze medals with appropriate color for each medal and using the award-fill
icon.
10:00
Duplicate dashboard content for the Winter Olympics page, share your results with your neighbor, and discuss approaches for not repeating yourself in your code.
05:00