Dashboard components

Build-a-Dashboard Workshop

Dr. Mine Çetinkaya-Rundel

Duke University

Posit, PBC

2024-08-12

Dashboard components

Cards

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.

A note on computing language

  • 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

Pages

dashboard-r.qmd
---
title: "Pages"
format: dashboard
---

```{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()
```

Pages

dashboard-r.qmd
---
title: "Pages"
format: dashboard
---

```{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()
```

Pages

dashboard-r.qmd
---
title: "Pages"
format: dashboard
---

```{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()
```

Logo

dashboard-r.qmd
---
title: "Logo"
format: 
  dashboard:
    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()
```

Rows

By default, cards are laid out in rows:

dashboard-r.qmd
---
title: "Rows"
format: 
  dashboard:
    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()
```

Rows

dashboard-r.qmd
---
title: "Rows"
format: 
  dashboard:
    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()
```

Rows

Default value of orientation is rows:

dashboard-r.qmd
---
title: "Rows"
format: 
  dashboard:
    orientation: rows
    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()
```

Columns

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()
```

Columns

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()
```

Rows, then columns

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()
```

Rows, then columns

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()
```

Columns, then rows

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()
```

Columns, then rows

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()
```

Heights and widths of rows and columns

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()
```

Heights and widths of rows and columns

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 filling available space or by flowing to its natural height

  • fillling 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.

    dashboard-r.qmd
    ## Row {.fill}
    
    ## Row {.flow}

scrolling

Need more space? Set scrolling: true for accommodate page overflow.

---
format: 
  dashboard:
    scrolling: true 
---

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.

Per page controls

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()
```

Tabsets

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()
```

Tabsets

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()
```

Cards

Cards

  • 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:

    ::: {.card}
    This text will be displayed within a card
    :::
  • Cards provide an expand button which appears at bottom right on hover

Card titles

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()
```

Card titles

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()
```

Pop quiz!

Which of the following cells will become a card in a dashboard?

```{r}
library(palmerpenguins)
```
```{r}
x <- 2
```
```{r}
plot(cars)
```
```{r}
#| output: false
2 + 2
```

Multiple outputs

If a cell generates multiple outputs, they get wrapped in a single cell:

dashboard-r.qmd
---
title: "Multiple outputs"
format: 
  dashboard:
    logo: images/beetle.png
---

```{r}
library(ggplot2)
```

```{r}
#| title: One cell = one card
ggplot(mpg, aes(x = cty, y = hwy)) +
  geom_point()

ggplot(mpg, aes(x = drv)) +
  geom_bar()
```

Multiple outputs

dashboard-r.qmd
---
title: "Multiple outputs"
format: 
  dashboard:
    logo: images/beetle.png
---

```{r}
library(ggplot2)
```

```{r}
#| title: One cell = one card
ggplot(mpg, aes(x = cty, y = hwy)) +
  geom_point()

ggplot(mpg, aes(x = drv)) +
  geom_bar()
```

Multiple output layout

You can control layout of multiple outputs with layout-ncol:

dashboard-r.qmd
---
title: "Multiple output layout"
format: 
  dashboard:
    logo: images/beetle.png
---

```{r}
library(ggplot2)
```

```{r}
#| title: One cell = one card
#| layout-ncol: 2
ggplot(mpg, aes(x = cty, y = hwy)) +
  geom_point()

ggplot(mpg, aes(x = drv)) +
  geom_bar()
```

Multiple output layout

dashboard-r.qmd
---
title: "Multiple output layout"
format: 
  dashboard:
    logo: images/beetle.png
---

```{r}
library(ggplot2)
```

```{r}
#| title: One cell = one card
#| layout-ncol: 2
ggplot(mpg, aes(x = cty, y = hwy)) +
  geom_point()

ggplot(mpg, aes(x = drv)) +
  geom_bar()
```

Value boxes

  • 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

Value boxes

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()
```

Value boxes

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()
```

Value boxes

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()
)
```

Value boxes

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()
)
```

Markdown text

  • 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

    ::: {.card title="Title goes here"}
    Text goes here.
    :::
  • You can place markdown text and other cell output in a single card as well

    ::: {.card title="Title goes here"}
    Text goes here.
    
    ```{r}
    # code generating output goes here
    ```
    :::    

Your turn

Start

  • 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.

Goal

Your goal is to create a dashboard that looks like the following:

Step 1

  • Add two pages - one for Summer Olympics and one for Winter Olympics.
  • Duplicate existing dashboard content for the two types of olympic games with subsets of data from the corresponding season.
05:00

Step 2

In the Summer Olympics page:

  • Make the columns 65% (first) and 35% (second) of width of the dashboard.
  • Divide the first column into rows of 60% (first) and 40% (second) of height of the dashboard.
  • In the second row of the first column, combine markdown text about cancelled olympic games with the medals by year plot in the same cell.
05:00

Step 3

In the Summer Olympics page:

  • Divide the second column into rows of 25% (first) and 75% (second) of height of the dashboard.
  • In the second row of the second column, create tables (using gt for R or great_tables for Python) of top 30 and bottom 30 total medals by team, sorted in descending order for the top 30 and ascending order for the bottom 30 total medals, and add color to the table based on data values.
  • Place these tables in tabsets with descriptive text about table content in the same card/tab.
10:00

Step 4

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

Step 5

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