New layouts
Learn how to lay out UI elements with bslib.
bslib layouts
page_sidebar()
card()
- with card parts
layout_columns()
- full screen cards
#| standalone: true
#| components: [editor, viewer]
## file: app.R
library(shiny)
library(bslib)
ui <- page_fluid(
)
server <- function(input, output, session) {
}
shinyApp(ui, server)
π§βπ» Your Turn
exercises/02_app.R
exercises/02_app.R
library(shiny)
library(bslib)
library(dplyr)
library(plotly)
library(collegeScorecard)
# Data --------------------------------------------------------------------
<- levels(school$control)
school_types <- levels(school$deg_predominant) |> setdiff("Graduate")
school_degrees <- levels(school$locale_type)
school_locales
# Inputs ------------------------------------------------------------------
<-
input_school_type checkboxGroupInput(
"school_type",
"Type of School",
choices = school_types,
selected = school_types,
inline = FALSE
)
<-
input_deg_predmoninant selectInput(
"deg_predominant",
"Predominant Degree Type",
choices = school_degrees,
selected = "Bachelor"
)
<-
input_group_by radioButtons(
"group_by",
"Group By",
choices = c(
"Campus Setting" = "locale_type",
"Highest Degree" = "deg_highest",
"Testing Requirements" = "adm_req_test"
)
)
# UI ----------------------------------------------------------------------
<- fluidPage(
ui # theme = bs_theme(),
titlePanel("02 - First bslib Layout"),
sidebarLayout(
sidebarPanel(
input_school_type,
input_deg_predmoninant,hr(),
input_group_by
),mainPanel(
plotlyOutput("plot_rate_admissions"),
plotlyOutput("plot_rate_completion"),
plotlyOutput("plot_cost_earnings")
)
)
)
# Server ------------------------------------------------------------------
<- function(input, output, session) {
server <- reactive({
r_scorecard <- school
school_filter if (length(input$school_type)) {
<-
school_filter |>
school_filter filter(control %in% input$school_type)
}
<-
school_filter |>
school_filter filter(deg_predominant == input$deg_predominant)
|>
scorecard semi_join(school_filter, by = "id")
})
<- function(x) {
plotly_cleaner |>
x config(displayModeBar = FALSE) |>
layout(margin = list(l = 0, r = 0, b = 0))
}
<- function(scorecard, column) {
filter_recent_complete_year <- scorecard[!is.na(scorecard[[column]]), ]$academic_year
academic_year |> filter(academic_year == !!max(academic_year))
scorecard
}
$plot_rate_admissions <- renderPlotly({
outputr_scorecard() |>
filter_recent_complete_year("rate_admissions") |>
filter(!is.na(rate_admissions)) |>
plot_ly(x = ~rate_admissions, type = "histogram") |>
layout(xaxis = list(title = "Rate")) |>
plotly_cleaner()
})
$plot_rate_completion <- renderPlotly({
outputr_scorecard() |>
filter_recent_complete_year("rate_completion") |>
filter(rate_completion > 0) |>
plot_ly(x = ~rate_completion, type = "histogram") |>
layout(xaxis = list(title = "Rate")) |>
plotly_cleaner()
})
$plot_cost_earnings <- renderPlotly({
outputr_scorecard() |>
filter_recent_complete_year("amnt_earnings_med_10y") |>
left_join(school, by = "id") |>
plot_ly(
x = ~cost_avg,
y = ~amnt_earnings_med_10y,
color = ~get(input$group_by),
hoverinfo = "text",
text = ~paste0(
"School: ", name, "<br>",
"Undergrads: ", scales::number(n_undergrads, big.mark=","), "<br>",
"Average Cost: ", scales::dollar(cost_avg), "<br>",
"Median Earnigns: ", scales::dollar(amnt_earnings_med_10y), "<br>",
"Admissions Rate: ", scales::percent(rate_admissions), "<br>",
"Completion Rate: ", scales::percent(rate_completion)
)|>
) layout(
xaxis = list(title = "Average Cost"),
yaxis = list(title = "Median Earnings")
)
})
}
shinyApp(ui, server)
Weβre in the process of refactoring an older Shiny app to use new features from bslib. Iβve started refactoring exercises/02_app.R
by pulling out the inputs so itβs easier to see the layout.
Your task is to migrate the UI section of the app from shiny::fluidPage()
and shiny::sidebarLayout()
to use page and layout functions from {bslib}
.
Value Boxes
#| standalone: true
#| components: [editor, viewer, terminal]
## file: app.R
library(shiny)
library(bslib)
library(collegeScorecard)
ui <- page_fluid(
)
server <- function(input, output, session) {
}
shinyApp(ui, server)
π§βπ» Your Turn
exercises/03_app.R
exercises/03_app.R
library(shiny)
library(bslib)
library(tidyverse)
library(collegeScorecard)
# Feel free to use either of these packages for icons!
# library(fontawesome) # usage: fontawesome::fa_i("icon-name")
# library(bsicons) # usage: bsicons::bs_icon("icon-name")
# Data --------------------------------------------------------------------
<-
school_1k |>
scorecard slice_max(academic_year, by = id, n = 1) |>
filter(n_undergrads > 1000) |>
semi_join(school, y = _, by = "id")
# UI ----------------------------------------------------------------------
<- page_sidebar(
ui title = "03 - Value Boxes",
sidebar = sidebar(
selectInput("name", "School Name", NULL),
actionButton("random_school", "Random School", fontawesome::fa_i("shuffle"))
),
layout_columns(
fill = FALSE,
# !! Put value boxes here using the following outputs !!
# textOutput("text_n_undergrads")
# textOutput("text_cost_avg")
# textOutput("text_rate_completion")
),
# pretend this card is a plot
card(class = "text-bg-light", style = "min-height: 300px")
)
# Server ------------------------------------------------------------------
<- function(input, output, session) {
server observe({
updateSelectizeInput(session, "name", choices = school_1k$name, server = TRUE)
})
observeEvent(input$random_school, {
updateSelectizeInput(session, "name", choices = school_1k$name, selected = sample(school_1k$name, 1), server = TRUE)
})
<- eventReactive(input$name, {
r_scorecard |>
scorecard semi_join(school_1k |> filter(name == input$name), by = "id") |>
slice_max(academic_year, n = 1, with_ties = FALSE)
})
$text_n_undergrads <- renderText({
output<- r_scorecard()$n_undergrads
n_undergrads validate(need(n_undergrads, "No data"))
::number(n_undergrads, big.mark = ",")
scales
})
$text_cost_avg <- renderText({
output<- r_scorecard()$cost_avg
cost_avg validate(need(cost_avg, "No data"))
::dollar(cost_avg)
scales
})
$text_rate_completion <- renderText({
output<- r_scorecard()$rate_completion
rate_completion validate(need(rate_completion, "No data"))
::percent(rate_completion)
scales
})
}
shinyApp(ui, server)
The app in exercises/03_app.R
contains the start of a dashboard where the user can pick a specific school and see a quick summary of information about the school.
Iβve set up all of the reactive server-side logic for you. Your job is to create three visually appealing value boxes to describe:
- The number of undergraduate students at the school.
- The average yearly cost of the school.
- The rate of completion at the school.
Use the Build-A-Box app from the bslib package to explore value box options and to design the three value boxes. The app is available online or locally by running
::runExample("build-a-box", package = "bslib") shiny