Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
46 commits
Select commit Hold shift + click to select a range
374f757
Initial toolbar container
elnelson575 Nov 3, 2025
ed1a07e
Updates
elnelson575 Nov 3, 2025
04bd68b
Minor updates
elnelson575 Nov 3, 2025
8f6d6f1
Added tests
elnelson575 Nov 3, 2025
6389307
Updated news
elnelson575 Nov 3, 2025
c7d70e6
Removing example:
elnelson575 Nov 3, 2025
3f8b630
Adding snaps
elnelson575 Nov 5, 2025
1abed04
Adding a few comments
elnelson575 Nov 5, 2025
5fd9254
Update to toolbar testing
elnelson575 Nov 5, 2025
c2289a3
Fix dots in toolbar function
elnelson575 Nov 5, 2025
b23fc17
updates to dots
elnelson575 Nov 5, 2025
d2eb240
Merge branch 'feat/toolbar-container' of https://github.com/rstudio/b…
elnelson575 Nov 5, 2025
0836374
Update R/toolbar.R
elnelson575 Nov 5, 2025
a55d03f
Update inst/components/scss/toolbar.scss
elnelson575 Nov 5, 2025
0afabf4
Update inst/components/scss/toolbar.scss
elnelson575 Nov 5, 2025
e6e9d88
Update inst/components/scss/toolbar.scss
elnelson575 Nov 5, 2025
478f5e1
Merge remote-tracking branch 'origin/main' into feat/toolbar-container
elnelson575 Nov 13, 2025
0340d21
Merge remote-tracking branch 'origin/main' into feat/toolbar-container
elnelson575 Nov 13, 2025
f898670
Updated code
elnelson575 Nov 14, 2025
899b644
minor formatting changes
elnelson575 Nov 14, 2025
6a3ccfe
Adding footer back
elnelson575 Nov 14, 2025
88f00d8
Docs changes
elnelson575 Nov 14, 2025
df309bf
Updating footer, updating tests
elnelson575 Nov 14, 2025
68b7006
chore: air format tests/testthat/test-toolbar.R
gadenbuie Nov 14, 2025
3a9b59e
Merge 'origin/main' into branch feat/toolbar-container
gadenbuie Nov 14, 2025
12db810
Spacing
elnelson575 Nov 14, 2025
10ba431
Merge branch 'feat/toolbar-container' of https://github.com/rstudio/b…
elnelson575 Nov 14, 2025
87cbf1d
Updating comments
elnelson575 Nov 14, 2025
9c04578
Adding select
elnelson575 Nov 17, 2025
6be96cb
Removed select formatting to be addressed in select input PR
elnelson575 Nov 17, 2025
6371c2f
Add dark/light mode support
elnelson575 Nov 17, 2025
d16f727
Update tests/testthat/test-toolbar.R
elnelson575 Nov 17, 2025
3d7d9f0
Update tests/testthat/test-toolbar.R
elnelson575 Nov 17, 2025
3cc95fe
Updated tests
elnelson575 Nov 17, 2025
d9a5bf0
Updating formatting
elnelson575 Nov 17, 2025
d3dd5c3
Removed size arg
elnelson575 Nov 17, 2025
e8da8d9
Formatting updates
elnelson575 Nov 17, 2025
606c7d0
Working through input tests
elnelson575 Nov 18, 2025
2e7dc65
Merging in epic branch
elnelson575 Nov 20, 2025
183d8af
Merging
elnelson575 Nov 20, 2025
2ceb201
Merging in changes and editing
elnelson575 Nov 21, 2025
8171052
Remove sample apps
elnelson575 Nov 24, 2025
03f9e8f
Updating to fix tests and fix input select spacing
elnelson575 Nov 24, 2025
eda7660
Delete testapp.R
elnelson575 Nov 24, 2025
5479ad3
Update header format in bs-theme-preset-builtin.md
elnelson575 Nov 24, 2025
1e68375
Update markdown headers for consistency
elnelson575 Nov 24, 2025
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions NAMESPACE
Original file line number Diff line number Diff line change
Expand Up @@ -163,6 +163,7 @@ export(toggle_sidebar)
export(toggle_switch)
export(toggle_tooltip)
export(toolbar)
export(toolbar_input_select)
export(tooltip)
export(update_popover)
export(update_submit_textarea)
Expand Down
1 change: 1 addition & 0 deletions NEWS.md
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@
* Added toast notifications based on [Bootstrap's Toast component](https://getbootstrap.com/docs/5.3/components/toasts/): Use `toast()` to create customizable toast objects, `show_toast()` to display a toast message, `hide_toast()` for manual dismissal, and `toast_header()` for structured headers with icons and status indicators. (#1246)

* Added a new `toolbar()` component for creating Bootstrap toolbars that can contain buttons, text, and other elements. (#1247)
* Added `toolbar_input_select()`, a select input designed for use within a `toolbar()`. (#1249)

## Improvements and bug fixes

Expand Down
54 changes: 54 additions & 0 deletions R/toolbar.R
Original file line number Diff line number Diff line change
Expand Up @@ -26,3 +26,57 @@ toolbar <- function(
tag_require(tag, version = 5, caller = "toolbar()")
as_fragment(tag)
}

#' Toolbar Input Select
#'
#' @description
#' Create a select list input control that can be used to choose a single item
#' from a list of values, suitable for use within a [toolbar()]. This is a
#' wrapper around [shiny::selectInput()] with `selectize = FALSE` and
#' appropriate styling for toolbar usage.
#'
#' @examplesIf rlang::is_interactive()
#' toolbar(
#' align = "right",
#' toolbar_input_select(
#' id = "select",
#' choices = c("Option 1", "Option 2", "Option 3"),
#' selected = "Option 2"
#' )
#' )
#'
#' @param id The input ID.
#' @param choices List of values to select from. If elements of the list are
#' named, then that name - rather than the value - is displayed to the user.
#' It's also possible to group related inputs by providing a named list whose
#' elements are (either named or unnamed) lists, vectors, or factors. In this
#' case, the outermost names will be used as the group labels (leveraging the
#' `<optgroup>` HTML tag) for the elements in the respective sublist.
#' @param selected The initially selected value. If not specified then defaults
#' to the first value.
#' @param width The width of the input, e.g. '400px', or '100%'; see
#' [validateCssUnit()].
Comment on lines +49 to +58
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is probably sufficient; it looks like this documentation was copied from selectInput() anyway.

Suggested change
#' @param choices List of values to select from. If elements of the list are
#' named, then that name - rather than the value - is displayed to the user.
#' It's also possible to group related inputs by providing a named list whose
#' elements are (either named or unnamed) lists, vectors, or factors. In this
#' case, the outermost names will be used as the group labels (leveraging the
#' `<optgroup>` HTML tag) for the elements in the respective sublist.
#' @param selected The initially selected value. If not specified then defaults
#' to the first value.
#' @param width The width of the input, e.g. '400px', or '100%'; see
#' [validateCssUnit()].
#' @inheritParams shiny::selectInput

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

If you want to keep documentation local to toolbar_input_select() -- width might be one case -- you can keep the @param docs here.

#'
#' @return Returns a select input control suitable for use in a toolbar.
#'
#' @family Toolbar components
#' @export
toolbar_input_select <- function(
id,
choices,
selected = NULL,
width = NULL
) {
htmltools::div(
class = "bslib-toolbar-input-select form-select-sm",
Comment on lines +68 to +71
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think we might want to allow ..., check that they're all named arguments (i.e. they are all attributes), and then let people add attributes to the outer container.

Suggested change
width = NULL
) {
htmltools::div(
class = "bslib-toolbar-input-select form-select-sm",
width = NULL,
...
) {
rlang::check_dots_named()
htmltools::div(
class = "bslib-toolbar-input-select form-select-sm",
...,

shiny::selectInput(
id,
label = NULL, # Removed for slimline input component
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We probably need to keep label but use it for accessibility markup

choices = choices,
selected = selected,
multiple = FALSE,
selectize = FALSE,
width = width
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

size = NULL is the default for selectInput() and usually I wouldn't include it here, but if it's set you get a very different select input so it's worth setting this to ensure it doesn't accidentally change on us later.

Suggested change
width = width
width = width,
size = NULL

)
)
}
1 change: 0 additions & 1 deletion inst/components/dist/components.css

This file was deleted.

2 changes: 2 additions & 0 deletions inst/components/scss/card.scss
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,8 @@
flex-direction: row;
align-items: center;
align-self: stretch;
min-height: 2.5rem;
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is only to match what we currently have in the buttons PR

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

note: this includes both min-height and padding-block

padding-block: 4px;
gap: 0.25rem;

// Give the nav flex: 1 so that if the card header contains a nav, it will take all the available space
Expand Down
61 changes: 55 additions & 6 deletions inst/components/scss/toolbar.scss
Original file line number Diff line number Diff line change
Expand Up @@ -2,35 +2,84 @@
.bslib-toolbar {
display: flex;
align-items: center;
gap: 0;
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Same with these changes to toolbar.scss for the .bslib-toolbar (Also just to match what's in buttons)


/* ---- Toolbar options ---- */

&[data-align="left"] {
margin-right: auto;
justify-content: start;
}

&[data-align="right"] {
margin-left: auto;
justify-content: end;
}

/* ---- Adjustments to other elements ---- */

// Ensures uniformity of font sizing in elements and sub-elements (e.g., input select)
&,
& * {
font-size: 0.8rem;
font-size: 0.9rem;
}

&>* {
margin-bottom: 0 !important;
width: auto;
align-self: center;
}
}

/* Toolbar Select Input */
.bslib-toolbar-input-select {
// Decrease this to keep header slim and leave a margin between select & border
padding-block: 2px;

.form-group.shiny-input-container {
width: auto;

select {
width: auto;
}
}

/* Remove the select label visually */
#select-label {
display: none;
}

/* Make children take width=auto to size to content */
&>* {
width: auto;
}

select {
appearance: auto;
padding-block: 3px; // Decreased to slim down select to fit in header
/* .form-controls takes away the drop-down arrow, this ensures it shows up */
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think I understand what this comment is saying, but I'm not sure. Some things to clarify:

  1. Which .form-controls? Do you mean Bootstrap's rule, Shiny's rule, or another rule in bslib?
  2. I'm pretty sure it refers to the drop-down arrow, but I'm not sure what this refers to. I think you could probably re-order this sentence a bit to avoid two adjacent implicit references.

border: none;
transition: background-color 120ms ease, color 120ms ease;
}

// Hover, active, and focus styling to highlight the select and options on interaction
// because default styles are disabled to make the input slimmer
& select:hover {
background-color: rgba(var(--bs-emphasis-color-rgb, 0, 0, 0), 0.08);
}

& select:active {
background-color: rgba(var(--bs-emphasis-color-rgb, 0, 0, 0), 0.16);
}

& select:focus,
select:focus-visible {
background-color: rgba(var(--bs-emphasis-color-rgb, 0, 0, 0), 0.12);
}

// Card header is flex by default, but card footer is not and must be in order for
// toolbar alignment to work
.card-footer:has(> &) {
display: flex;
align-items: center;
& select option:hover,
& select option:focus,
& select option:checked {
background-color: rgba(var(--bs-emphasis-color-rgb, 0, 0, 0), 0.12) !important;
}
}
46 changes: 46 additions & 0 deletions man/toolbar_input_select.Rd

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

27 changes: 27 additions & 0 deletions tests/testthat/_snaps/toolbar.md
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,16 @@
Item 2
</div>

---

Code
show_raw_html(toolbar("Item 1", "Item 2", gap = "10px"))
Output
<div class="bslib-toolbar bslib-gap-spacing" data-align="right" gap="10px">
Item 1
Item 2
</div>

# toolbar() aligns correctly

Code
Expand All @@ -28,3 +38,20 @@
Item 2
</div>

# toolbar_input_select() basic attributes and defaults

Code
show_raw_html(toolbar_input_select(id = "select", choices = c("Option 1",
"Option 2", "Option 3"), selected = "Option 2"))
Output
<div class="bslib-toolbar-input-select form-select-sm">
<div class="form-group shiny-input-container">
<label class="control-label shiny-label-null" for="select" id="select-label"></label>
<div>
<select class="shiny-input-select form-control" id="select"><option value="Option 1">Option 1</option>
<option value="Option 2" selected>Option 2</option>
<option value="Option 3">Option 3</option></select>
</div>
</div>
</div>

2 changes: 1 addition & 1 deletion tests/testthat/helper-html.R
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@ show_raw_html <- function(x) {
}

expect_snapshot_html <- function(x, .envir = parent.frame()) {
x_str <- deparse(substitute(x))
x_str <- deparse1(substitute(x))
code <- parse(text = sprintf("expect_snapshot(show_raw_html(%s))", x_str))
eval(code, envir = .envir)
}
28 changes: 28 additions & 0 deletions tests/testthat/test-toolbar.R
Original file line number Diff line number Diff line change
@@ -1,10 +1,14 @@
# Tests for toolbar container #
test_that("toolbar() basic attributes and defaults", {
tb <- as.tags(toolbar(htmltools::span("Test")))
expect_match(htmltools::tagGetAttribute(tb, "class"), "bslib-toolbar")
expect_match(htmltools::tagGetAttribute(tb, "data-align"), "right")
expect_snapshot_html(
toolbar("Item 1", "Item 2")
)
expect_snapshot_html(
toolbar("Item 1", "Item 2", gap = "10px")
)
})

test_that("toolbar() aligns correctly", {
Expand All @@ -18,3 +22,27 @@ test_that("toolbar() aligns correctly", {
)
expect_error(toolbar("x", align = "center"))
})


# Toolbar Input Select Tests #

test_that("toolbar_input_select() basic attributes and defaults", {
tis <- as.tags(
toolbar_input_select(
id = "select",
choices = c("Option 1", "Option 2", "Option 3"),
selected = "Option 2"
)
)
expect_match(
htmltools::tagGetAttribute(tis, "class"),
"bslib-toolbar-input-select"
)
expect_snapshot_html(
toolbar_input_select(
id = "select",
choices = c("Option 1", "Option 2", "Option 3"),
selected = "Option 2"
)
)
})