reactable icon indicating copy to clipboard operation
reactable copied to clipboard

Supply smarter theming defaults if a {bslib} theme is active

Open cpsievert opened this issue 3 years ago • 7 comments

This PR allows reactable() to supply smarter reactableTheme() defaults when a {bslib} theme is active at render time. For example:

library(shiny)
library(reactable)
library(bslib)

theme <- bs_theme(
  bg = "#202123", fg = "#B8BCC2", primary = "#EA80FC",
  base_font = font_google("Grandstander")
)

ui <- fluidPage(
  theme = theme,
  reactableOutput("table")
)

server <- function(input, output, session) {
  output$table <- renderReactable({
    reactable(
      mtcars, searchable = TRUE, 
      resizable = TRUE, filterable = TRUE,
      highlight = TRUE, striped = TRUE, 
      outlined = TRUE, bordered = TRUE, 
      # Can still override theme defaults, if you want 
      #theme = reactableTheme(pageButtonStyle = list(color = "orange"))
    )
  })
}

shinyApp(ui, server)
Screen Shot 2020-11-18 at 3 51 29 PM

Since the widget calls bslib::bs_current_theme() inside it's preRenderHook, it'll automatically re-render with new theming defaults whenever the {bslib} theme changes via bslib::bs_themer() (or more generally, whenever the new session$setCurrentTheme() is called). This generally just works as long the reactable() passed through renderReactable():

library(shiny)
library(reactable)
library(bslib)

ui <- fluidPage(
  theme = bs_theme(),
  reactableOutput("table")
)

x <- reactable(mtcars)

server <- function(input, output, session) {
  bslib::bs_themer()
  output$table <- renderReactable(x)
}

shinyApp(ui, server)

This approach will also generally 'just work' in {rmarkdown} (when {bslib} is being used to provide Bootstrap CSS) once https://github.com/rstudio/rmarkdown/pull/1706 lands

Outside of Shiny and R Markdown, this can also work in static HTML sites via global {bslib} themes:


library(htmltools)
theme <- bs_theme(bootswatch = "darkly")
old_theme <- bs_global_set(theme)
browsable(tags$body(
  bs_theme_dependencies(bs_global_get()),
  reactable(mtcars)
))
bs_global_set(old_theme)
Screen Shot 2020-11-18 at 4 20 39 PM

cpsievert avatar Nov 18 '20 22:11 cpsievert

Codecov Report

Merging #96 (f1b6a9a) into master (33255ab) will decrease coverage by 0.15%. The diff coverage is n/a.

Impacted file tree graph

@@            Coverage Diff             @@
##           master      #96      +/-   ##
==========================================
- Coverage   99.17%   99.02%   -0.16%     
==========================================
  Files          15        9       -6     
  Lines        1823     1125     -698     
  Branches      335      335              
==========================================
- Hits         1808     1114     -694     
+ Misses         15       11       -4     
Impacted Files Coverage Δ
R/shiny.R
R/columns.R
R/language.R
R/theme.R

Continue to review full report at Codecov.

Legend - Click here to learn more Δ = absolute <relative> (impact), ø = not affected, ? = missing data Powered by Codecov. Last update 33255ab...f1b6a9a. Read the comment docs.

codecov-io avatar Nov 20 '20 17:11 codecov-io

My only hesitation is with applying the bslib theme by default, since reactable isn't a Bootstrap themed table.

I understand the hesitation, but keep in mind that this implementation (i.e., retrieving bslib::bs_current_theme() inside preRenderHook) means that these new styles should only kick-in when {bslib} is being used with Shiny and/or R Markdown (and thus, we have some guarantee that Bootstrap will be on the page). Although, there is one exception (when a global theme is set via bslib::bs_global_set()), but hopefully those cases are few and far between (and limited to power users).

How about, as some sort of compromise, we have theme default to "auto" (i.e., the current behavior), but we'd also accept a sentinel value (maybe NULL?) to opt-out and also accommodate bslib::bs_theme() objects? This also has the benefit of being similar to how the new {DT}+{bslib} behavior will work (i.e., by default, the styles will respect the {bslib} theme when relevant, but users may also opt-out)

reactable <- function(..., theme = getOption("reactable.theme", "auto")) {}

cpsievert avatar Jan 25 '21 15:01 cpsievert

I like the idea of an opt-out, but am still on the fence about applying the {bslib} theme by default. I'm just worried there'll be enough cases to opt out that it doesn't justify the defaulting. It'll also be different from how it is now, where reactable doesn't use Bootstrap even though Shiny and R Markdown put Bootstrap on the page by default.

For now, I'd rather keep it opt-in and wait for user feedback. If the vast majority of users opt in and it becomes a pain, then we could change it to apply by default. Setting the {bslib} theme explicitly might be inconvenient, but at least it's still a huge benefit over having to do the Bootstrap styling manually.

glin avatar Feb 22 '21 05:02 glin

I like the idea of an opt-out, but am still on the fence about applying the {bslib} theme by default. I'm just worried there'll be enough cases to opt out that it doesn't justify the defaulting. It'll also be different from how it is now, where reactable doesn't use Bootstrap even though Shiny and R Markdown put Bootstrap on the page by default.

Keep in mind that existing code, as well as non-{bslib} use cases, will continue to work as before. So, this is still an 'opt-in' from the perspective that users have opt-in to a {bslib} theme; and when they do, we'd like to minimize the amount of effort required for everything on the page to reflect that theme. Of course I'll respect whatever decision you make, but just know we're pretty heavily invested in this idea by now; and in the near future, {DT}, {flexdashboard}, {gt} {shinyWidgets}, and hopefully many more packages will all be using this same approach to {bslib} integration.

cpsievert avatar Feb 22 '21 14:02 cpsievert

{bslib} integration seems like a great improvement! Will this be merged any time soon?

januz avatar Sep 09 '21 00:09 januz

Hi @glin, would you have the time and interest to resurrect this if we made this an opt-in feature (similar to what you originally proposed)?

cpsievert avatar May 11 '23 01:05 cpsievert

Hey @cpsievert - yep, still interested, and I still think it'd be safer as an opt-in. For example, I'm now using the BS5 theme in the pkgdown docs and wouldn't want to opt out in every example. A middle ground might be to provide a simple reactable() wrapper that has the Bootstrap theme enabled, like bsreactable() or something.

I don't think I'll have time to work on this any time soon though, because of general lack of time and a few other priorities to work on right now.

glin avatar May 22 '23 03:05 glin