Skip to content

Commit 70d37ed

Browse files
authored
Update value_box; Update to bootstrap 5.3; Update htmldeps (#772)
1 parent 10ff4b3 commit 70d37ed

File tree

133 files changed

+7439
-43482
lines changed

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

133 files changed

+7439
-43482
lines changed

CHANGELOG.md

Lines changed: 37 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -8,79 +8,101 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
88

99
## [UNRELEASED]
1010

11+
### Breaking Changes
12+
* `shiny.run` only allows positional arguments for `app`, `host`, and `port`, all other arguments must be specified with keywords.
13+
1114
### New features
1215
* `shiny run` now takes `reload-includes` and `reload-excludes` to allow you to define which files trigger a reload (#780).
1316
* `shiny.run` now passes keyword arguments to `uvicorn.run` (#780).
1417
* The `@output` decorator is no longer required for rendering functions; `@render.xxx` decorators now register themselves automatically. You can still use `@output` explicitly if you need to set specific output options (#747).
1518
* Added support for integration with Quarto (#746).
1619
* Added `shiny.render.renderer_components` decorator to help create new output renderers (#621).
17-
* Added `shiny.experimental.ui.popover()`, `update_popover()`, and `toggle_popover()` for easy creation (and server-side updating) of [Bootstrap popovers](https://getbootstrap.com/docs/5.2/components/popovers/). Popovers are similar to tooltips, but are more persistent, and should primarily be used with button-like UI elements (e.g. `input_action_button()` or icons) (#680).
20+
* Added `shiny.experimental.ui.popover()`, `update_popover()`, and `toggle_popover()` for easy creation (and server-side updating) of [Bootstrap popovers](https://getbootstrap.com/docs/5.3/components/popovers/). Popovers are similar to tooltips, but are more persistent, and should primarily be used with button-like UI elements (e.g. `input_action_button()` or icons) (#680).
1821
* Added CSS classes to UI input methods (#680) .
1922
* `Session` objects can now accept an asynchronous (or synchronous) function for `.on_flush(fn=)`, `.on_flushed(fn=)`, and `.on_ended(fn=)` (#686).
2023
* `App()` now allows `static_assets` to represent multiple paths. To do this, pass in a dictionary instead of a string (#763).
24+
* The `showcase_layout` argument of `value_box()` now accepts one of three character values: `"left center"`, `"top right"`, `"bottom"`. (#772)
25+
* `value_box()` now supports many new themes and styles, or fully customizable themes using the new `value_box_theme()` function. To reflect the new capabilities, we've replaced `theme_color` with a new `theme` argument. The previous argument will continue work as expected, but with a deprecation warning. (#772)
26+
27+
In addition to the Bootstrap theme names (`primary` ,`secondary`, etc.), you can now use the main Boostrap colors (`purple`, `blue`, `red`, etc.). You can also choose to apply the color to the background or foreground by prepending a `bg-` or `text-` prefix to the theme or color name. Finally, we've also added new gradient themes allowing you to pair any two color names as `bg-gradient-{from}-{to}` (e.g., `bg-gradient-purple-blue`).
28+
29+
These named color themes aren't limited to value boxes: because they're powered by small utility classes, you can use them anywhere within your bslib-powered UI.
30+
31+
* Added `shiny.ui.showcase_bottom()`, a new `shiny.ui.value_box()` layout that places the showcase below the value box `title` and `value`, perfect for a full-bleed plot. (#772)
2132

2233
### API changes
2334

35+
* Added `shiny.ui.navset_underline()` and `shiny.ui.navset_card_underline()` whose navigation container is similar to `shiny.ui.navset_tab()` and `shiny.ui.navset_card_tab()` respectively, but its active/focused navigation links are styled with an underline. (#772)
36+
* `shiny.ui.layout_column_wrap(width, *args)` was rearranged to `shiny.ui.layout_column_wrap(*args, width)`. Now, `width` will default to `200px` is no value is provided. (#772)
37+
* `shiny.ui.showcase_left_center()` and `shiny.ui.showcase_top_right()` no longer take two values for the `width` argument. Instead, they now take a single value (e.g., `width = "30%"`) representing the width of the showcase are in the value box. Furthermore, they've both gained `width_full_screen` arguments that determine the width of the showcase area when the value box is expanded to fill the screen. (#772)
38+
39+
2440
* TODO-barret-API; `shiny.ui.panel_main()` and `shiny.ui.panel_sidebar()` are deprecated in favor of new API for `shiny.ui.layout_sidebar()`. Please use `shiny.ui.sidebar()` to construct a sidebar and supply it (along with the main content) to `shiny.ui.layout_sidebar(*args, **kwargs)`. (#680)
2541

2642
#### API relocations
2743

28-
* `shiny.ui`'s `navset_pill_card()` and `navset_tab_card()` have been renamed to `.navset_card_pill()` and `navset_tab_card()` respectively (#492).
44+
* `shiny.ui`'s `navset_pill_card()` and `navset_tab_card()` have been renamed to `navset_card_pill()` and `navset_card_tab()` respectively (#492).
2945

3046
The following methods have been moved from `shiny.experimental.ui` and integrated into `shiny.ui` (final locations under `shiny.ui` are displayed) (#680):
3147

3248
* Sidebar - Sidebar layout or manipulation
33-
* `page_sidebar()`, `toggle_sidebar()`, `layout_sidebar()`
49+
* `sidebar()`, `page_sidebar()`, `toggle_sidebar()`, `layout_sidebar()`, `Sidebar`
3450
* Filling layout - Allow UI components to expand into the parent container and/or allow its content to expand
3551
* `page_fillable()`, `fill.as_fillable_container()`, `fill.as_fill_item()`, `fill.is_fillable_container()`, `fill.is_fill_item()`, `fill.remove_all_fill()`
3652
* `output_plot(fill=)`, `output_image(fill=)`, `output_ui(fill=, fillable=)`
3753
* CSS units - CSS units and padding
38-
* `css.as_css_unit()`, `css.as_css_padding()`, `css.as_width_unit()`, `css.CssUnit`
54+
* `css.as_css_unit()`, `css.as_css_padding()`, `css.CssUnit`
3955
* Tooltip - Hover-based context UI element
4056
* `tooltip()`, `toggle_tooltip()`, `update_tooltip()`
4157
* Popover - Click-based context UI element
4258
* `popover()`, `toggle_popover()`, `update_popover()`
4359
* Accordion - Vertically collapsible UI element
44-
* `accordion()`, `accordion_panel()`, `accordion_panel_close()`, `accordion_panel_insert()`, `accordion_panel_open()`, `accordion_panel_remove()`, `accordion_panel_set()`, `Accordion`
60+
* `accordion()`, `accordion_panel()`, `accordion_panel_close()`, `accordion_panel_insert()`, `accordion_panel_open()`, `accordion_panel_remove()`, `accordion_panel_set()`, `update_accordion_panel()`, `Accordion`, `AccordionPanel`
4561
* Card - A general purpose container for grouping related UI elements together
4662
* `card()`, `card_header()`, `card_footer()`, `CardItem`
4763
* Valuebox - Opinionated container for displaying a value and title
4864
* `valuebox()`
65+
* `showcase_left_center()`
66+
* `showcase_top_right()`
4967
* Navs - Navigation within a page
5068
* `navset_bar()`, `navset_tab_card()`, `navset_pill_card()`
51-
* `page_navbar(sidebar=, fillable=, fillable_mobile=, gap=, padding=, inverse=True)`, `navset_card_tab(sidebar=)`, `navset_card_pill(sidebar=)`, `navset_bar(sidebar=, fillable=, gap=, padding=)`
69+
* `page_navbar(sidebar=, fillable=, fillable_mobile=, gap=, padding=)`, `navset_card_tab(sidebar=)`, `navset_card_pill(sidebar=)`, `navset_bar(sidebar=, fillable=, gap=, padding=)`
70+
* Layout - Layout of UI elements
71+
* `layout_column_wrap()`
5272
* Inputs - UI elements for user input
5373
* `toggle_switch()`
5474
* `input_text_area(autoresize=)`
5575

5676
If a ported method is called from `shiny.experimental.ui`, a deprecation warning will be displayed.
5777

5878
Methods still under consideration in `shiny.experimental.ui`:
59-
* `value_box(showcase=)`
60-
* `card(wrapper=)`, `card_body()`, `card_image()`, `card_header()`
61-
62-
63-
### Bug fixes
64-
* `shiny run` now respects the user provided `reload-dir` argument (#765).
79+
* `card(wrapper=)`: A function (which returns a UI element) to call on unnamed arguments in `card(*args)` which are not already `shiny.ui.CardItem` objects.
80+
* `card_body()`: A container for grouping related UI elements together
81+
* `card_image()`: A general container for an image within a `shiny.ui.card`.
82+
* `card_title()`: A general container for the "title" of a `shiny.ui.card`.
6583

6684
#### API removals
6785

6886
* `shiny.experimental.ui.FillingLayout` has been removed. (#481)
87+
* `shiny.experimental.ui.as_width_unit()` has been made defunct. Please remove it from your code. (#772)
6988
* Support for `min_height=`, `max_height=`, and `gap=` in `shiny.experimental.ui.as_fillable_container()` and `as_fill_item()` has been removed. (#481)
7089
* `shiny.experimental.ui.TagCallable` has been deprecated. Its type is equivalent to `htmltools.TagFunction`. (#680)
71-
* `shiny.eperimental.ui.as_fill_carrier()` and `shiny.eperimental.ui.is_fill_carrier()` have been deprecated. Please use `shiny.ui.fill.as_fill_item()` and `shiny.ui.fill.as_fillable_container()` or `shiny.ui.fill.is_fill_item()` and `shiny.ui.fill.is_fillable_container()` respectively in combination to achieve similar behavior. (#680)
90+
* `shiny.experimental.ui.as_fill_carrier()` and `shiny.experimental.ui.is_fill_carrier()` have been deprecated. Please use `shiny.ui.fill.as_fill_item()` and `shiny.ui.fill.as_fillable_container()` or `shiny.ui.fill.is_fill_item()` and `shiny.ui.fill.is_fillable_container()` respectively in combination to achieve similar behavior. (#680)
7291

7392
### Bug fixes
7493

94+
* `shiny run` now respects the user provided `reload-dir` argument (#765).
7595
* Fixed #646: Wrap bare value box value in `<p />` tags. (#668)
7696
* Fixed #676: The `render.data_frame` selection feature was underdocumented and buggy (sometimes returning `None` as a row identifier if the pandas data frame's index had gaps in it). With this release, the selection is consistently a tuple of the 0-based row numbers of the selected rows--or `None` if no rows are selected. (#677)
7797
* Added tests to verify that ui input methods, ui labels, ui update (value) methods, and ui output methods work within modules (#696).
7898
* Adjusted the `@render.plot` input type to be `object` to allow for any object (if any) to be returned (#712).
99+
* In `layout_column_wrap()`, when `width` is a CSS unit -- e.g. `width = "400px"` or `width = "25%"` -- and `fixed_width = FALSE`, `layout_column_wrap()` will ensure that the columns are at least `width` wide, unless the parent container is narrower than `width`. (#772)
79100

80101
### Other changes
81102

82-
### Breaking Changes
83-
* `shiny.run` only allows positional arguments for `app`, `host`, and `port`, all other arguments must be specified with keywords.
103+
* `layout_sidebar()` now uses an `<aside>` element for the sidebar's container and a `<header>` element for the sidebar title. The classes of each element remain the same, but the semantic meaning of the elements is now better reflected in the HTML markup. (#772)
104+
* `layout_sidebar()` no longer gives the sidebar main content area the `role="main"` attribute. (#772)
105+
* Improved the style and appearance of the button to enter full screen in `card()`s and `value_box()`es to better adapt to Bootstrap's dark mode. (#772)
84106

85107

86108
## [0.5.1] - 2023-08-08

docs/_quartodoc.yml

Lines changed: 16 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -19,7 +19,7 @@ quartodoc:
1919
- ui.page_fluid
2020
- ui.page_fixed
2121
- ui.page_bootstrap
22-
- title: UI Layout
22+
- title: UI Layouts
2323
desc: Control the layout of multiple UI components.
2424
contents:
2525
- ui.sidebar
@@ -28,7 +28,6 @@ quartodoc:
2828
- ui.card
2929
- ui.card_header
3030
- ui.card_footer
31-
- ui.value_box
3231
- ui.popover
3332
- ui.tooltip
3433
- ui.accordion
@@ -53,18 +52,30 @@ quartodoc:
5352
- ui.input_password
5453
- ui.input_action_button
5554
- ui.input_action_link
55+
- title: Value boxes
56+
desc: Prominently display a value and label in a box that can be expanded to show more information.
57+
contents:
58+
- ui.value_box
59+
- ui.value_box_theme
60+
- ui.showcase_bottom
61+
- ui.showcase_left_center
62+
- ui.showcase_top_right
63+
- ui.ValueBoxTheme
64+
- ui.ShowcaseLayout
5665
- title: Navigation (tab) panels
5766
desc: Create segments of UI content.
5867
contents:
5968
- ui.nav
6069
- ui.nav_control
6170
- ui.nav_spacer
6271
- ui.nav_menu
63-
- ui.navset_tab
6472
- ui.navset_bar
65-
- ui.navset_card_tab
73+
- ui.navset_tab
6674
- ui.navset_pill
75+
- ui.navset_underline
76+
- ui.navset_card_tab
6777
- ui.navset_card_pill
78+
- ui.navset_card_underline
6879
- ui.navset_pill_list
6980
- ui.navset_hidden
7081
- title: UI panels
@@ -277,21 +288,11 @@ quartodoc:
277288
path: ExCard
278289
summary:
279290
name: "Card"
280-
desc: "Cards are a common organizing unit for modern user interfaces (UI). At their core, theyre just rectangular containers with borders and padding. However, when utilized properly to group related information, they help users better digest, engage, and navigate through content."
291+
desc: "Cards are a common organizing unit for modern user interfaces (UI). At their core, they're just rectangular containers with borders and padding. However, when utilized properly to group related information, they help users better digest, engage, and navigate through content."
281292
flatten: true
282293
contents:
283294
- experimental.ui.card_body
284295
- experimental.ui.card_title
285296
- experimental.ui.card_image
286297
- experimental.ui.ImgContainer
287298
- experimental.ui.WrapperCallable
288-
- kind: page
289-
path: ExValueBoxes
290-
summary:
291-
name: "Value boxes"
292-
desc: "Prominently display a value and label in a box that can be expanded to show more information."
293-
flatten: true
294-
contents:
295-
- experimental.ui.value_box
296-
- experimental.ui.showcase_left_center
297-
- experimental.ui.showcase_top_right

js/build.ts

Lines changed: 40 additions & 35 deletions
Original file line numberDiff line numberDiff line change
@@ -4,55 +4,60 @@ import * as fs from "node:fs/promises";
44

55
const outDir = "../shiny/www/shared/py-shiny";
66

7-
// TODO-barret-future; Map over options and wait their build to finish
8-
9-
async function bundle_dataframe() {
7+
async function bundle_helper(
8+
options: BuildOptions
9+
): Promise<ReturnType<typeof build>> {
1010
try {
11-
const options: BuildOptions = {
12-
entryPoints: { dataframe: "dataframe/index.tsx" },
11+
const result = await build({
1312
format: "esm",
1413
bundle: true,
15-
outdir: outDir + "/dataframe",
1614
minify: true,
1715
sourcemap: true,
18-
plugins: [sassPlugin({ type: "css-text", sourceMap: false })],
19-
metafile: true,
20-
};
16+
metafile: false,
17+
outdir: outDir,
18+
...options,
19+
});
2120

22-
const result = await build(options);
23-
console.log("Building dataframe completed successfully!");
24-
// console.log("Output:", result);
25-
await fs.writeFile(
26-
"esbuild-metadata.json",
27-
JSON.stringify(result.metafile)
21+
Object.entries(options.entryPoints as Record<string, string>).forEach(
22+
([output_file_stub, input_path]) => {
23+
console.log(
24+
"Building " + output_file_stub + ".js completed successfully!"
25+
);
26+
}
2827
);
28+
29+
if (options.metafile) {
30+
// Save metafile
31+
const dataframe_results = result;
32+
await fs.writeFile(
33+
"esbuild-metadata.json",
34+
JSON.stringify(dataframe_results.metafile)
35+
);
36+
console.log("Metadata file written to esbuild-metadata.json");
37+
}
38+
return result;
2939
} catch (error) {
3040
console.error("Build failed:", error);
3141
}
3242
}
3343

34-
async function bundle_textarea() {
35-
try {
36-
const options: BuildOptions = {
37-
entryPoints: {
38-
"textarea-autoresize": "text-area/textarea-autoresize.ts",
39-
},
40-
format: "esm",
41-
bundle: true,
42-
outdir: outDir + "/text-area",
43-
minify: false,
44-
sourcemap: false,
45-
metafile: false,
46-
};
47-
const result = await build(options);
48-
console.log("Building textarea-autoresize completed successfully!");
49-
} catch (error) {
50-
console.error("Build failed for textarea-autoresize:", error);
51-
}
52-
}
44+
const opts: Array<BuildOptions> = [
45+
{
46+
entryPoints: { "dataframe/dataframe": "dataframe/index.tsx" },
47+
plugins: [sassPlugin({ type: "css-text", sourceMap: false })],
48+
metafile: true,
49+
},
50+
{
51+
entryPoints: {
52+
"text-area/textarea-autoresize": "text-area/textarea-autoresize.ts",
53+
},
54+
minify: false,
55+
sourcemap: false,
56+
},
57+
];
5358

5459
// Run function to avoid top level await
5560
async function main(): Promise<void> {
56-
await Promise.all([bundle_dataframe(), bundle_textarea()]);
61+
await Promise.all(opts.map(bundle_helper));
5762
}
5863
main();

js/package.json

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,9 @@
33
"private": true,
44
"license": "MIT",
55
"scripts": {
6-
"build": "tsc -noEmit && eslint . && tsx build.ts",
6+
"lint": "tsc -noEmit && eslint .",
7+
"bundle": "tsx build.ts",
8+
"build": "npm run lint && npm run bundle",
79
"watch": "npx nodemon --exec 'npm run build' --ext '*' --ignore dist/ --ignore esbuild-metadata.json",
810
"build-fast": "tsx build.ts",
911
"watch-fast": "npx nodemon --exec 'npm run build-fast' --ext '*' --ignore dist/ --ignore esbuild-metadata.json"

scripts/htmlDependencies.R

Lines changed: 29 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -11,7 +11,7 @@ versions <- list()
1111
message("Installing GitHub packages: bslib, shiny, htmltools")
1212
withr::local_temp_libpaths()
1313
ignore <- capture.output({
14-
pak::pkg_install(c("rstudio/bslib@a076e72e78562d7f006889da4118cd781c66c84c", "rstudio/shiny@ab69d7292c51d8983174e0828496a7096f513673", "rstudio/htmltools@9338b7f3e2ed7b3fef8fd813904b9b05281344aa"))
14+
pak::pkg_install(c("rstudio/bslib@main", "rstudio/shiny@main", "rstudio/htmltools@main"))
1515
})
1616
# pak::pkg_install(c("cran::bslib", "cran::shiny", "cran::htmltools"))
1717

@@ -173,7 +173,12 @@ message("Save bootstrap bundle")
173173
deps <- bslib::bs_theme_dependencies(shiny_theme)
174174
withr::with_options(
175175
list(htmltools.dir.version = FALSE),
176-
ignore <- lapply(deps, copyDependencyToDir, www_shared)
176+
ignore <- lapply(deps, function(dep) {
177+
if (dep$name %in% c("bslib-component-css", "bslib-component-js")) {
178+
return()
179+
}
180+
copyDependencyToDir(dep, www_shared)
181+
})
177182
)
178183
bs_ver <- names(bslib::versions())[bslib::versions() == "5"]
179184
versions["bootstrap"] <- bs_ver
@@ -203,10 +208,30 @@ message("Cleanup bootstrap bundle")
203208
# This additional bs3compat HTMLDependency() only holds
204209
# the JS shim for tab panel logic, which we don't need
205210
# since we're generating BS5+ tab markup. Note, however,
206-
# we still do have bs3compat's CSS on the page, which
207-
# comes in via the bootstrap HTMLDependency()
211+
# we still do have bs3compat's bundled CSS on the page, which
212+
# comes in naturally via the bootstrap HTMLDependency()
208213
fs::dir_delete(fs::path(www_shared, "bs3compat"))
209214

215+
# Remove non-minified or unused files
216+
fs::file_delete(
217+
fs::path(www_shared, c(
218+
"datepicker/css/bootstrap-datepicker3.css",
219+
"datepicker/js/bootstrap-datepicker.js",
220+
"datepicker/scss",
221+
"ionrangeslider/js/ion.rangeSlider.js",
222+
"ionrangeslider/scss",
223+
"jquery/jquery-3.6.0.js",
224+
"jqueryui/jquery-ui.css",
225+
"jqueryui/jquery-ui.js",
226+
"jqueryui/jquery-ui.structure.css",
227+
"jqueryui/jquery-ui.structure.min.css",
228+
"jqueryui/jquery-ui.theme.css",
229+
"jqueryui/jquery-ui.theme.min.css",
230+
"selectize/accessibility/js/selectize-plugin-a11y.js",
231+
"selectize/js/selectize.js"
232+
))
233+
)
234+
210235

211236
# ------------------------------------------------------------------------------
212237
message("Save requirejs")

shiny/_versions.py

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
1-
shiny_html_deps = "1.7.4.9003"
2-
bslib = "0.5.0.9000"
3-
htmltools = "0.5.5.9000"
4-
bootstrap = "5.2.2"
1+
shiny_html_deps = "1.7.5.9000"
2+
bslib = "0.5.1.9000"
3+
htmltools = "0.5.6.9001"
4+
bootstrap = "5.3.1"
55
requirejs = "2.3.6"
66

77
__all__ = (

shiny/api-examples/accordion/app.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -34,7 +34,7 @@ def _():
3434
@output
3535
@render.text
3636
def acc_val():
37-
return input.acc()
37+
return "input.acc(): " + str(input.acc())
3838

3939

4040
app = App(app_ui, server)

shiny/api-examples/accordion_panel/app.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -21,7 +21,7 @@ def _():
2121
@output
2222
@render.text
2323
def acc_val():
24-
return input.acc()
24+
return "input.acc(): " + str(input.acc())
2525

2626

2727
app = App(app_ui, server)

shiny/api-examples/accordion_panel_remove/app.py

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,7 @@ def make_panel(letter: str) -> ui.AccordionPanel:
2020
f"Remove Section {choices[-1]}",
2121
class_="mt-3 mb-3",
2222
),
23+
" (Sections randomly picked at server start)",
2324
ui.accordion(*items, id="acc", multiple=True),
2425
)
2526

0 commit comments

Comments
 (0)