Skip to content

Commit 57fae0b

Browse files
committed
Merge branch 'main' into pop_over
* main: feat(Session): Make Session on_flush() and on_flushed() accept async functions (#693) Make data frame selection return row numbers, not pandas index value (#677) chore(api)!: Rename `ui.navset_pill_card` -> `ui.navset_card_pill` and `ui.navset_tab_card` -> `ui.navset_card_tab` (#681) Consolidate all testing into `tests/` folder (#683) Fix pyright error (#678) Make model score app work on Connect/Shinyapps.io (#657) Suppress type check for read_csv Synchonize input_file examples More realistic file import example (#582) Make flaky dataframe test have larger timeout (#675) Wrap bare value box value in `p` tag (#668)
2 parents 81ea302 + 398738b commit 57fae0b

File tree

103 files changed

+682
-178
lines changed

Some content is hidden

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

103 files changed

+682
-178
lines changed

CHANGELOG.md

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

99
## [UNRELEASED]
1010

11-
### Experimental breaking changes
12-
13-
* `shiny.experimental.ui.sidebar_toggle()` has been renamed to `shiny.experimental.ui.toggle_sidebar()` (#680).
14-
* `shiny.experimental.ui.tooltip_toggle()` has been renamed to `shiny.experimental.ui.toggle_tooltip()` (#680).
15-
* `shiny.experimental.ui.tooltip_update()` has been renamed to `shiny.experimental.ui.update_tooltip()` (#680).
16-
17-
1811
### New features
1912

2013
* Added `shiny.render.renderer_components` decorator to help create new output renderers (#621).
2114
* 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).
2215
* Added `shiny.experimental.ui.toggle_switch()` (#680).
2316
* Added CSS classes to UI input methods (#680) .
17+
* `Session` objects can now accept an asynchronous (or synchronous) function for `.on_flush(fn=)`, `.on_flushed(fn=)`, and `.on_ended(fn=)` (#686).
18+
19+
### API changes
20+
21+
* Renamed `shiny.ui.navset_pill_card` to `shiny.ui.navset_card_pill`. `shiny.ui.navset_pill_card` will throw a deprecated warning (#492).
22+
* Renamed `shiny.ui.navset_tab_card` to `shiny.ui.navset_card_tab`. `shiny.ui.navset_tab_card` will throw a deprecated warning (#492).
23+
24+
#### Experimental API changes
25+
26+
* Renamed `shiny.experimental.ui.navset_pill_card` to `shiny.experimental.ui.navset_card_pill` (#492).
27+
* Renamed `shiny.experimental.ui.navset_tab_card` to `shiny.experimental.ui.navset_card_tab` (#492).
28+
* Renamed `shiny.experimental.ui.sidebar_toggle()` to `shiny.experimental.ui.toggle_sidebar()` (#680).
29+
* Renamed `shiny.experimental.ui.tooltip_toggle()` to `shiny.experimental.ui.toggle_tooltip()` (#680).
30+
* Renamed `shiny.experimental.ui.tooltip_update()` to `shiny.experimental.ui.update_tooltip()` (#680).
31+
2432

2533
### Bug fixes
2634

35+
* Fixed #646: Wrap bare value box value in `<p />` tags. (#668)
36+
* 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)
37+
2738
### Other changes
2839

2940

Makefile

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -77,11 +77,11 @@ check: ## check code quality with black and isort
7777
isort --check-only --diff .
7878

7979
test: ## run tests quickly with the default Python
80-
python3 tests/asyncio_prevent.py
81-
pytest tests
80+
python3 tests/pytest/asyncio_prevent.py
81+
pytest
8282

8383
# Default `FILE` to `e2e` if not specified
84-
FILE:=e2e
84+
FILE:=tests/e2e
8585

8686
e2e: ## end-to-end tests with playwright
8787
playwright install --with-deps

docs/_quartodoc.yml

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -77,9 +77,9 @@ quartodoc:
7777
- ui.nav_spacer
7878
- ui.nav_menu
7979
- ui.navset_tab
80-
- ui.navset_tab_card
80+
- ui.navset_card_tab
8181
- ui.navset_pill
82-
- ui.navset_pill_card
82+
- ui.navset_card_pill
8383
- ui.navset_pill_list
8484
- ui.navset_hidden
8585
- title: UI panels
@@ -247,8 +247,8 @@ quartodoc:
247247
- experimental.ui.layout_sidebar
248248
- experimental.ui.page_navbar
249249
- experimental.ui.navset_bar
250-
- experimental.ui.navset_tab_card
251-
- experimental.ui.navset_pill_card
250+
- experimental.ui.navset_card_tab
251+
- experimental.ui.navset_card_pill
252252
- experimental.ui.toggle_sidebar
253253
- experimental.ui.toggle_switch
254254
- experimental.ui.panel_main

e2e/experimental/value_box/test_valuebox.py

Lines changed: 0 additions & 19 deletions
This file was deleted.

examples/event/app.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -11,7 +11,7 @@
1111
print the number of clicks in the console twice.
1212
"""
1313
),
14-
ui.navset_tab_card(
14+
ui.navset_card_tab(
1515
ui.nav(
1616
"Sync",
1717
ui.input_action_button("btn", "Click me"),

examples/model-score/scoredata.py

Lines changed: 13 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -55,4 +55,16 @@ async def update_db(position):
5555

5656
def begin():
5757
position = init_db()
58-
asyncio.create_task(update_db(position))
58+
59+
# After initializing the database, we need to start a non-blocking task to update it
60+
# every second or so. If an event loop is already running, we can use an asyncio
61+
# task. (This is the case when running via `shiny run` and shinylive.) Otherwise, we
62+
# need to launch a background thread and run an asyncio event loop there. (This is
63+
# the case when running via shinyapps.io or Posit Connect.)
64+
65+
if asyncio.get_event_loop().is_running():
66+
asyncio.create_task(update_db(position))
67+
else:
68+
from threading import Thread
69+
70+
Thread(target=lambda: asyncio.run(update_db(position)), daemon=True).start()

js/dataframe/index.tsx

Lines changed: 3 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -58,12 +58,8 @@ interface ShinyDataGridProps<TIndex> {
5858

5959
const ShinyDataGrid: FC<ShinyDataGridProps<unknown>> = (props) => {
6060
const { id, data, bgcolor } = props;
61-
const { columns, index, type_hints, data: rowData } = data;
61+
const { columns, type_hints, data: rowData } = data;
6262
const { width, height, filters: withFilters } = data.options;
63-
const keyToIndex: Record<string, unknown> = {};
64-
index.forEach((value) => {
65-
keyToIndex[value + ""] = value;
66-
});
6763

6864
const containerRef = useRef<HTMLDivElement>(null);
6965
const theadRef = useRef<HTMLTableSectionElement>(null);
@@ -192,7 +188,8 @@ const ShinyDataGrid: FC<ShinyDataGridProps<unknown>> = (props) => {
192188
rowSelection
193189
.keys()
194190
.toList()
195-
.map((key) => keyToIndex[key])
191+
.map((key) => parseInt(key))
192+
.sort()
196193
);
197194
}
198195
}

js/dataframe/types.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -20,7 +20,7 @@ export interface DataGridOptions {
2020

2121
export interface PandasData<TIndex> {
2222
columns: ReadonlyArray<string>;
23-
index: ReadonlyArray<TIndex>;
23+
// index: ReadonlyArray<TIndex>;
2424
data: unknown[][];
2525
type_hints?: ReadonlyArray<TypeHint>;
2626
options: DataGridOptions;

pytest.ini

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
[pytest]
22
asyncio_mode=strict
3-
testpaths=tests
3+
testpaths=tests/pytest/
44
addopts = --strict-markers --durations=6 --durations-min=5.0 --browser webkit --browser firefox --browser chromium --numprocesses auto
55
markers =
66
examples: Suite of tests to validate that examples do not produce errors (deselect with '-m "not examples"')

shiny/api-examples/data_frame/app.py

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -63,8 +63,10 @@ def summary_data():
6363

6464
@reactive.Calc
6565
def filtered_df():
66+
# input.summary_data_selected_rows() is a tuple, so we must convert it to list,
67+
# as that's what Pandas requires for indexing.
6668
selected_idx = list(req(input.summary_data_selected_rows()))
67-
countries = summary_df["country"][selected_idx]
69+
countries = summary_df.iloc[selected_idx]["country"]
6870
# Filter data for selected countries
6971
return df[df["country"].isin(countries)]
7072

shiny/api-examples/input_file/app.py

Lines changed: 42 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -1,31 +1,56 @@
11
import pandas as pd
22

3-
from shiny import App, Inputs, Outputs, Session, render, ui
3+
from shiny import App, Inputs, Outputs, Session, reactive, render, ui
44
from shiny.types import FileInfo
55

66
app_ui = ui.page_fluid(
7-
ui.layout_sidebar(
8-
ui.panel_sidebar(
9-
ui.input_file("file1", "Choose CSV File", accept=[".csv"], multiple=False),
10-
ui.input_checkbox("header", "Header", True),
11-
width=5,
12-
),
13-
ui.panel_main(
14-
ui.output_ui("contents"),
15-
),
7+
ui.input_file("file1", "Choose CSV File", accept=[".csv"], multiple=False),
8+
ui.input_checkbox_group(
9+
"stats",
10+
"Summary Stats",
11+
choices=["Row Count", "Column Count", "Column Names"],
12+
selected=["Row Count", "Column Count", "Column Names"],
1613
),
14+
ui.output_table("summary"),
1715
)
1816

1917

2018
def server(input: Inputs, output: Outputs, session: Session):
19+
@reactive.Calc
20+
def parsed_file():
21+
file: list[FileInfo] | None = input.file1()
22+
if file is None:
23+
return pd.DataFrame()
24+
return pd.read_csv( # pyright: ignore[reportUnknownMemberType]
25+
file[0]["datapath"]
26+
)
27+
2128
@output
22-
@render.ui
23-
def contents():
24-
if input.file1() is None:
25-
return "Please upload a csv file"
26-
f: list[FileInfo] = input.file1()
27-
df = pd.read_csv(f[0]["datapath"], header=0 if input.header() else None)
28-
return ui.HTML(df.to_html(classes="table table-striped"))
29+
@render.table
30+
def summary():
31+
df = parsed_file()
32+
33+
if df.empty:
34+
return pd.DataFrame()
35+
36+
# Get the row count, column count, and column names of the DataFrame
37+
row_count = df.shape[0]
38+
column_count = df.shape[1]
39+
names = df.columns.tolist()
40+
column_names = ", ".join(str(name) for name in names)
41+
42+
# Create a new DataFrame to display the information
43+
info_df = pd.DataFrame(
44+
{
45+
"Row Count": [row_count],
46+
"Column Count": [column_count],
47+
"Column Names": [column_names],
48+
}
49+
)
50+
51+
# input.stats() is a list of strings; subset the columns based on the selected
52+
# checkboxes
53+
return info_df.loc[:, input.stats()]
2954

3055

3156
app = App(app_ui, server)

shiny/api-examples/nav/app.py

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -53,10 +53,10 @@ def nav_controls(prefix: str) -> List[NavSetArg]:
5353
ui.navset_tab(*nav_controls("navset_tab()")),
5454
ui.h4("navset_pill()"),
5555
ui.navset_pill(*nav_controls("navset_pill()")),
56-
ui.h4("navset_tab_card()"),
57-
ui.navset_tab_card(*nav_controls("navset_tab_card()")),
58-
ui.h4("navset_pill_card()"),
59-
ui.navset_pill_card(*nav_controls("navset_pill_card()")),
56+
ui.h4("navset_card_tab()"),
57+
ui.navset_card_tab(*nav_controls("navset_card_tab()")),
58+
ui.h4("navset_card_pill()"),
59+
ui.navset_card_pill(*nav_controls("navset_card_pill()")),
6060
ui.h4("navset_pill_list()"),
6161
ui.navset_pill_list(*nav_controls("navset_pill_list()")),
6262
)

shiny/api-examples/update_navs/app.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,7 @@
66
ui.input_slider("controller", "Controller", min=1, max=3, value=1)
77
),
88
ui.panel_main(
9-
ui.navset_tab_card(
9+
ui.navset_card_tab(
1010
ui.nav("Panel 1", "Panel 1 content", value="panel1"),
1111
ui.nav("Panel 2", "Panel 2 content", value="panel2"),
1212
ui.nav("Panel 3", "Panel 3 content", value="panel3"),

shiny/experimental/e2e/navbar/app.py

Lines changed: 6 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -51,14 +51,14 @@ def nav_items(prefix: str) -> list[NavSetArg]:
5151
),
5252
footer=ui.div(
5353
{"style": "width:80%; margin: 0 auto"},
54-
ui.h4("navset_tab_card()"),
55-
x.ui.navset_tab_card(
56-
*nav_items("navset_tab_card()"),
54+
ui.h4("navset_card_tab()"),
55+
x.ui.navset_card_tab(
56+
*nav_items("navset_card_tab()"),
5757
sidebar=my_sidebar,
5858
),
59-
ui.h4("navset_pill_card()"),
60-
x.ui.navset_pill_card(
61-
*nav_items("navset_pill_card()"),
59+
ui.h4("navset_card_pill()"),
60+
x.ui.navset_card_pill(
61+
*nav_items("navset_card_pill()"),
6262
sidebar=my_sidebar,
6363
),
6464
# Do not include `navset_bar()` in example. Ok for testing only

shiny/experimental/ui/__init__.py

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -35,7 +35,7 @@
3535
from ._input_switch import toggle_switch
3636
from ._input_text import input_text_area
3737
from ._layout import layout_column_wrap
38-
from ._navs import navset_bar, navset_pill_card, navset_tab_card
38+
from ._navs import navset_bar, navset_card_pill, navset_card_tab
3939
from ._output import output_image, output_plot, output_ui
4040
from ._page import page_fillable, page_navbar, page_sidebar
4141
from ._sidebar import (
@@ -72,8 +72,8 @@
7272
"page_navbar",
7373
# Navs
7474
"navset_bar",
75-
"navset_tab_card",
76-
"navset_pill_card",
75+
"navset_card_tab",
76+
"navset_card_pill",
7777
# Card
7878
"CardItem",
7979
"ImgContainer",

shiny/experimental/ui/_navs.py

Lines changed: 8 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -2,8 +2,8 @@
22

33
__all__ = (
44
"navset_bar",
5-
"navset_tab_card",
6-
"navset_pill_card",
5+
"navset_card_tab",
6+
"navset_card_pill",
77
)
88

99
import copy
@@ -194,7 +194,7 @@ def navset_card_body(content: Tag, sidebar: Optional[Sidebar] = None) -> CardIte
194194
return CardItem(content)
195195

196196

197-
def navset_tab_card(
197+
def navset_card_tab(
198198
*args: NavSetArg,
199199
id: Optional[str] = None,
200200
selected: Optional[str] = None,
@@ -229,7 +229,7 @@ def navset_tab_card(
229229
* ~shiny.experimental.ui.navset_bar
230230
* ~shiny.ui.navset_tab
231231
* ~shiny.ui.navset_pill
232-
* ~shiny.experimental.ui.navset_pill_card
232+
* ~shiny.experimental.ui.navset_card_pill
233233
* ~shiny.ui.navset_hidden
234234
235235
Example
@@ -249,7 +249,7 @@ def navset_tab_card(
249249
)
250250

251251

252-
def navset_pill_card(
252+
def navset_card_pill(
253253
*args: NavSetArg,
254254
id: Optional[str] = None,
255255
selected: Optional[str] = None,
@@ -287,7 +287,7 @@ def navset_pill_card(
287287
* ~shiny.experimental.ui.navset_bar
288288
* ~shiny.ui.navset_tab
289289
* ~shiny.ui.navset_pill
290-
* ~shiny.experimental.ui.navset_tab_card
290+
* ~shiny.experimental.ui.navset_card_tab
291291
* ~shiny.ui.navset_hidden
292292
293293
Example
@@ -533,8 +533,8 @@ def navset_bar(
533533
* ~shiny.ui.nav_spacer
534534
* ~shiny.ui.navset_tab
535535
* ~shiny.ui.navset_pill
536-
* ~shiny.experimental.ui.navset_tab_card
537-
* ~shiny.experimental.ui.navset_pill_card
536+
* ~shiny.experimental.ui.navset_card_tab
537+
* ~shiny.experimental.ui.navset_card_pill
538538
* ~shiny.ui.navset_hidden
539539
540540
Example

0 commit comments

Comments
 (0)