Skip to content

Commit 4e86c79

Browse files
authored
Merge pull request #32 from rstudio/variable-overrides
Add .where and .default_flag args for better default behavior
2 parents 8c883fa + c20ce0c commit 4e86c79

File tree

3 files changed

+60
-11
lines changed

3 files changed

+60
-11
lines changed

R/themes.R

Lines changed: 43 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,11 @@
1616
#' @param rules Any [sass::as_sass()] `input` to place after Bootstrap's SASS imports.
1717
#' @param ... For `bs_theme_add_variables()`, these arguments define SASS variables;
1818
#' otherwise, these arguments are passed along to [sass::sass_layer()].
19+
#' @param .where whether to place the variable definitions before other
20+
#' Sass `"defaults"`, after other Sass `"declarations"`, or after other Sass `"rules"`.
21+
#' @param .default_flag whether or not to add a `!default` flag (if missing) to
22+
#' variable expressions. It's recommended to keep this as `TRUE` when
23+
#' `.where = "defaults"`.
1924
#'
2025
#' @references \url{https://getbootstrap.com/docs/4.4/getting-started/theming/}
2126
#'
@@ -57,23 +62,55 @@ bs_theme_new <- function(version = version_default(), bootswatch = NULL) {
5762

5863
#' @rdname theming
5964
#' @export
60-
bs_theme_add_variables <- function(...) {
65+
bs_theme_add_variables <- function(..., .where = "defaults",
66+
.default_flag = identical(.where, "defaults")) {
6167
vars <- rlang::list2(...)
6268
if (any(names2(vars) == "")) stop("Variables must be named.", call. = FALSE)
63-
bs_theme_add(vars)
69+
.where <- match.arg(.where, c("defaults", "declarations", "rules"))
70+
71+
# Workaround to the problem of 'blue' winning in the scenario of:
72+
# bs_theme_add_variables("body-bg" = "blue")
73+
# bs_theme_add_variables("body-bg" = "red")
74+
if (.default_flag) {
75+
vars <- ensure_default_flag(vars)
76+
}
77+
78+
do.call(bs_theme_add, setNames(list(vars), .where))
79+
}
80+
81+
# Given a named list of variable definitions,
82+
# searches each variable's expression for a !default flag,
83+
# and if missing, adds it.
84+
ensure_default_flag <- function(vars) {
85+
Map(
86+
function(key, val) {
87+
val <- paste(sass::as_sass(val), collapse = "\n")
88+
if (grepl("!default\\s*;*\\s*$", val)) {
89+
val
90+
} else {
91+
paste(sub(";+$", "", val), "!default")
92+
}
93+
}, names(vars), vars
94+
)
6495
}
6596

6697
#' @rdname theming
6798
#' @export
68-
bs_theme_add <- function(defaults = "", rules = "", ...) {
99+
bs_theme_add <- function(defaults = "", declarations = "", rules = "", ...) {
69100
old_theme <- bs_theme_get()
70101
if (is.null(old_theme)) {
71102
stop("Must call bs_theme_new() before adding to the theme.", call. = FALSE)
72103
}
73-
layer <- if (inherits(defaults, "sass_layer")) {
74-
defaults
104+
if (inherits(defaults, "sass_layer")) {
105+
if (!identical(declarations, "") || !identical(rules, "") || length(list(...)) > 0) {
106+
stop(
107+
"Not allowed to specify other arguments when the first argument, `defaults`, ",
108+
"is a sass_layer()", call. = FALSE
109+
)
110+
}
111+
layer <- defaults
75112
} else {
76-
sass_layer(defaults = defaults, rules = rules, ...)
113+
layer <- sass_layer(defaults = defaults, declarations = declarations, rules = rules, ...)
77114
}
78115
layer <- sass_layer_merge(old_theme, layer)
79116
bs_theme_set(add_class(layer, "bs_theme"))

tests/testthat/test-bs-theme.R

Lines changed: 16 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -16,7 +16,7 @@ test_that("theme api works", {
1616
expect_equal(theme_bootswatch(cosmo_theme), "cosmo")
1717

1818
# Can add new variable defaults
19-
bs_theme_add_variables(primary = "red !default;")
19+
bs_theme_add_variables("primary" = "red !default;")
2020
primary <- bootstrap_sass("body{background:$primary;}")
2121
expect_css("body{background:red;}", primary)
2222

@@ -42,5 +42,20 @@ test_that("Theme adding works as intended", {
4242
bs_theme_add_variables(primary = "#fff !default;")
4343
css <- bootstrap_sass(".foo{color:$primary;}")
4444
expect_css(".foo{color:#fff;}", css)
45+
46+
# Also works without default flags and can handle numeric values
47+
bs_theme_add_variables(primary = "blue")
48+
bs_theme_add_variables(primary = "#fff", "font-size" = 0)
49+
css <- bootstrap_sass(".foo{color:$primary;font-size:0}")
50+
expect_css(".foo{color:#fff;font-size:0;}", css)
51+
52+
# Can also override variables via declarations
53+
bs_theme_add_variables(
54+
.where = "declarations",
55+
"primary" = "$secondary",
56+
"body-color" = "color-yiq($primary)"
57+
)
58+
css <- bootstrap_sass(".foo{color:$primary;}")
59+
expect_css(".foo{color:#6c757d;}", css)
4560
})
4661

Lines changed: 1 addition & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -2,10 +2,7 @@ context("theme_bootswatch")
22

33
test_that("Can retrieve version from theme object", {
44
bs_theme_new()
5-
expect_identical(
6-
theme_bootswatch(bs_theme_get()),
7-
version_default()
8-
)
5+
expect_null(theme_bootswatch(bs_theme_get()))
96

107
bs_theme_new(version = "3", bootswatch = "paper")
118
expect_equal(

0 commit comments

Comments
 (0)