Skip to content

Commit 24b02cf

Browse files
cpsievertschloerkekarangattu
authored
feat(api): Deprecate panel_well() in favor of card() (#2038)
Co-authored-by: Barret Schloerke <[email protected]> Co-authored-by: Karan Gathani <[email protected]>
1 parent ae49e4c commit 24b02cf

File tree

10 files changed

+117
-141
lines changed

10 files changed

+117
-141
lines changed

CHANGELOG.md

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -35,6 +35,8 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
3535

3636
* Added module support for `session.clientdata` methods. This allows you to access client data values in Shiny modules without needing to namespace the keys explicitly. (#1978)
3737

38+
* Fixed false positive warning in `layout_columns()` about number of widths vs elements. (#1704)
39+
3840
### Bug fixes
3941

4042
* Fixed an issue with `ui.Chat()` sometimes wanting to scroll a parent element. (#1996)
@@ -45,6 +47,10 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
4547

4648
* Fixed `set()` method of `InputSelectize` controller so it clears existing selections before applying new values. (#2024)
4749

50+
### Deprecations
51+
52+
* `ui.panel_well()` is deprecated in favor of `ui.card()`. (#2038)
53+
4854

4955
## [1.4.0] - 2025-04-08
5056

docs/_quartodoc-core.yml

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -94,7 +94,6 @@ quartodoc:
9494
- ui.panel_fixed
9595
- ui.panel_conditional
9696
- ui.panel_title
97-
- ui.panel_well
9897
- title: Uploads & downloads
9998
desc: Allow users to upload and download files.
10099
contents:

docs/_quartodoc-express.yml

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -160,7 +160,6 @@ quartodoc:
160160
- express.ui.panel_absolute
161161
- express.ui.panel_fixed
162162
- express.ui.panel_title
163-
- express.ui.panel_well
164163
- title: Uploads & downloads
165164
desc: Allow users to upload and download files.
166165
contents:

examples/airmass/app.py

Lines changed: 20 additions & 28 deletions
Original file line numberDiff line numberDiff line change
@@ -14,45 +14,37 @@
1414

1515
from shiny import App, Inputs, Outputs, Session, reactive, render, req, ui
1616

17-
app_ui = ui.page_fixed(
18-
ui.tags.h3("Air mass calculator"),
19-
ui.div(
17+
app_ui = ui.page_fillable(
18+
ui.tags.h2("Air mass calculator", {"class": "lead display-6 text-center"}),
19+
ui.layout_columns(
2020
ui.markdown(
2121
"""This Shiny app uses [Astropy](https://www.astropy.org/) to calculate the
22-
altitude (degrees above the horizon) and airmass (the amount of atmospheric
23-
air along your line of sight to an object) of one or more astronomical
24-
objects, over a given evening, at a given geographic location.
25-
"""
22+
altitude (degrees above the horizon) and airmass (the amount of atmospheric
23+
air along your line of sight to an object) of one or more astronomical
24+
objects, over a given evening, at a given geographic location.
25+
"""
2626
),
27-
class_="mb-5",
28-
),
29-
ui.row(
30-
ui.column(
31-
8,
27+
ui.card(
3228
ui.output_ui("timeinfo"),
33-
ui.output_plot("plot", height="800px"),
29+
ui.output_plot("plot", height="600px"),
3430
# For debugging
3531
# ui.output_table("table"),
36-
class_="order-2 order-sm-1",
3732
),
38-
ui.column(
39-
4,
40-
ui.panel_well(
41-
ui.input_date("date", "Date"),
42-
class_="pb-1 mb-3",
43-
),
44-
ui.panel_well(
33+
ui.TagList(
34+
ui.card(ui.input_date("date", "Date"), fill=False),
35+
ui.card(
4536
ui.input_text_area(
46-
"objects", "Target object(s)", "M1, NGC35, PLX299", rows=3
37+
"objects",
38+
"Target object(s)",
39+
"M1, NGC35, PLX299",
40+
rows=2,
4741
),
48-
class_="pb-1 mb-3",
49-
),
50-
ui.panel_well(
51-
location_ui("location"),
52-
class_="mb-3",
42+
fill=False,
5343
),
54-
class_="order-1 order-sm-2",
44+
ui.card(location_ui("location")),
5545
),
46+
col_widths=[-2, 8, -2, -1, 7, 3, -1],
47+
min_height="600px",
5648
),
5749
)
5850

examples/inputs-update/app.py

Lines changed: 60 additions & 68 deletions
Original file line numberDiff line numberDiff line change
@@ -4,78 +4,70 @@
44

55
app_ui = ui.page_fluid(
66
ui.panel_title("Changing the values of inputs from the server"),
7-
ui.row(
8-
ui.column(
9-
4,
10-
ui.panel_well(
11-
ui.tags.h4("These inputs control the other inputs on the page"),
12-
ui.input_text(
13-
"control_label", "This controls some of the labels:", "LABEL TEXT"
14-
),
15-
ui.input_slider(
16-
"control_num", "This controls values:", min=1, max=20, value=15
17-
),
7+
ui.layout_columns(
8+
ui.card(
9+
ui.card_header("These inputs control the other inputs on the page"),
10+
ui.input_text(
11+
"control_label", "This controls some of the labels:", "LABEL TEXT"
12+
),
13+
ui.input_slider(
14+
"control_num", "This controls values:", min=1, max=20, value=15
1815
),
1916
),
20-
ui.column(
21-
4,
22-
ui.panel_well(
23-
ui.tags.h4("These inputs are controlled by the other inputs"),
24-
ui.input_text("inText", "Text input:", value="start text"),
25-
ui.input_numeric(
26-
"inNumber", "Number input:", min=1, max=20, value=5, step=0.5
27-
),
28-
ui.input_numeric(
29-
"inNumber2", "Number input 2:", min=1, max=20, value=5, step=0.5
30-
),
31-
ui.input_slider("inSlider", "Slider input:", min=1, max=20, value=15),
32-
ui.input_slider(
33-
"inSlider2", "Slider input 2:", min=1, max=20, value=(5, 15)
34-
),
35-
ui.input_slider(
36-
"inSlider3", "Slider input 3:", min=1, max=20, value=(5, 15)
37-
),
38-
ui.input_date("inDate", "Date input:"),
39-
ui.input_date_range("inDateRange", "Date range input:"),
17+
ui.card(
18+
ui.card_header("These inputs are controlled by the other inputs"),
19+
ui.input_text("inText", "Text input:", value="start text"),
20+
ui.input_numeric(
21+
"inNumber", "Number input:", min=1, max=20, value=5, step=0.5
22+
),
23+
ui.input_numeric(
24+
"inNumber2", "Number input 2:", min=1, max=20, value=5, step=0.5
25+
),
26+
ui.input_slider("inSlider", "Slider input:", min=1, max=20, value=15),
27+
ui.input_slider(
28+
"inSlider2", "Slider input 2:", min=1, max=20, value=(5, 15)
29+
),
30+
ui.input_slider(
31+
"inSlider3", "Slider input 3:", min=1, max=20, value=(5, 15)
4032
),
33+
ui.input_date("inDate", "Date input:"),
34+
ui.input_date_range("inDateRange", "Date range input:"),
4135
),
42-
ui.column(
43-
4,
44-
ui.panel_well(
45-
ui.input_checkbox("inCheckbox", "Checkbox input", value=False),
46-
ui.input_checkbox_group(
47-
"inCheckboxGroup",
48-
"Checkbox group input:",
49-
{
50-
"option1": "label 1",
51-
"option2": "label 2",
52-
},
53-
),
54-
ui.input_radio_buttons(
55-
"inRadio",
56-
"Radio buttons:",
57-
{
58-
"option1": "label 1",
59-
"option2": "label 2",
60-
},
61-
),
62-
ui.input_select(
63-
"inSelect",
64-
"Select input:",
65-
{
66-
"option1": "label 1",
67-
"option2": "label 2",
68-
},
69-
),
70-
ui.input_select(
71-
"inSelect2",
72-
"Select input 2:",
73-
{
74-
"option1": "label 1",
75-
"option2": "label 2",
76-
},
77-
multiple=True,
78-
),
36+
ui.card(
37+
ui.card_header("These inputs are updated by the server"),
38+
ui.input_checkbox("inCheckbox", "Checkbox input", value=False),
39+
ui.input_checkbox_group(
40+
"inCheckboxGroup",
41+
"Checkbox group input:",
42+
{
43+
"option1": "label 1",
44+
"option2": "label 2",
45+
},
46+
),
47+
ui.input_radio_buttons(
48+
"inRadio",
49+
"Radio buttons:",
50+
{
51+
"option1": "label 1",
52+
"option2": "label 2",
53+
},
54+
),
55+
ui.input_select(
56+
"inSelect",
57+
"Select input:",
58+
{
59+
"option1": "label 1",
60+
"option2": "label 2",
61+
},
62+
),
63+
ui.input_select(
64+
"inSelect2",
65+
"Select input 2:",
66+
{
67+
"option1": "label 1",
68+
"option2": "label 2",
69+
},
70+
multiple=True,
7971
),
8072
ui.navset_tab(
8173
ui.nav_panel("panel1", ui.h2("This is the first panel.")),

shiny/api-examples/panel_absolute/app-core.py

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -3,8 +3,9 @@
33
app_ui = ui.page_fluid(
44
ui.panel_title("A basic absolute panel example", "Demo"),
55
ui.panel_absolute(
6-
ui.panel_well(
7-
"Drag me around!", ui.input_slider("n", "N", min=0, max=100, value=20)
6+
ui.card(
7+
ui.card_header("Drag me around!"),
8+
ui.input_slider("n", "N", min=0, max=100, value=20),
89
),
910
draggable=True,
1011
width="300px",

shiny/api-examples/panel_absolute/app-express.py

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,6 @@
33
ui.h2("A basic absolute panel example")
44

55
with ui.panel_absolute(draggable=True, width="300px", right="50px", top="25%"):
6-
with ui.panel_well():
7-
"Drag me around!"
6+
with ui.card():
7+
ui.card_header("Drag me around!")
88
ui.input_slider("n", "N", min=0, max=100, value=20)

shiny/express/ui/_cm_components.py

Lines changed: 3 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@
77
from htmltools import Tag, TagAttrs, TagAttrValue, TagChild, TagFunction, TagList
88

99
from ... import ui
10+
from ..._deprecated import warn_deprecated
1011
from ..._docstring import add_example, no_example
1112
from ...types import DEPRECATED, MISSING, MISSING_TYPE
1213
from ...ui._accordion import AccordionPanel
@@ -1363,13 +1364,9 @@ def value_box(
13631364
@no_example()
13641365
def panel_well(**kwargs: TagAttrValue) -> RecallContextManager[Tag]:
13651366
"""
1366-
Context manager for a well panel
1367-
1368-
This function wraps :func:`~shiny.ui.panel_well`.
1369-
1370-
A well panel is a simple container with a border and some padding. It's useful for
1371-
grouping related content together.
1367+
Deprecated. Use :func:`~shiny.express.ui.card` instead.
13721368
"""
1369+
warn_deprecated("panel_well() is deprecated. Use shiny.express.ui.card() instead.")
13731370
return RecallContextManager(
13741371
ui.panel_well,
13751372
kwargs=dict(

shiny/ui/_bootstrap.py

Lines changed: 3 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -11,7 +11,6 @@
1111
"help_text",
1212
)
1313

14-
1514
from typing import Literal, Optional
1615

1716
from htmltools import (
@@ -27,6 +26,7 @@
2726
tags,
2827
)
2928

29+
from .._deprecated import warn_deprecated
3030
from .._docstring import add_example, no_example
3131
from ..module import current_namespace
3232
from ..types import MISSING, MISSING_TYPE
@@ -112,23 +112,9 @@ def column(
112112
@no_example()
113113
def panel_well(*args: TagChild | TagAttrs, **kwargs: TagAttrValue) -> Tag:
114114
"""
115-
Create a well panel.
116-
117-
Creates a panel with a slightly inset border and gray background. Equivalent to
118-
Bootstrap's ``well`` CSS class.
119-
120-
Parameters
121-
----------
122-
*args
123-
UI elements to include inside the panel.
124-
**kwargs
125-
Attributes to place on the panel tag.
126-
127-
Returns
128-
-------
129-
:
130-
A UI element.
115+
Deprecated. Use :func:`~shiny.ui.card` instead.
131116
"""
117+
warn_deprecated("panel_well() is deprecated. Use shiny.ui.card() instead.")
132118
return div({"class": "well"}, *args, **kwargs)
133119

134120

shiny/ui/_layout_columns.py

Lines changed: 20 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -186,31 +186,35 @@ def validate_col_width(
186186
x: Iterable[int] | int, n_kids: int, break_name: Breakpoint
187187
) -> Iterable[int]:
188188
if isinstance(x, int):
189-
y = [x]
189+
widths = [x]
190190
else:
191-
y = x
191+
widths = list(x)
192192

193-
if not all(isinstance(i, int) for i in y):
194-
raise ValueError(
195-
f"Column values at breakpoint '{break_name}' must be integers. Values greater than 0 indicate width, and negative values indicate a column offset."
196-
)
197-
198-
if any(i == 0 for i in y):
199-
raise ValueError(
200-
f"Column values at breakpoint '{break_name}' must be greater than 0 to indicate width, or negative to indicate a column offset."
201-
)
193+
positive_widths: list[int] = []
194+
for w in widths:
195+
if not isinstance(w, int):
196+
raise TypeError(
197+
f"Column widths at breakpoint '{break_name}' must be integers. Got {type(w).__name__}."
198+
)
199+
if w == 0:
200+
raise ValueError(
201+
f"Column widths at breakpoint '{break_name}' must be greater than 0 to indicate width, or negative to indicate a column offset."
202+
)
203+
if w > 0:
204+
positive_widths.append(w)
202205

203-
if not any(b > 0 for b in y):
206+
if not positive_widths:
204207
raise ValueError(
205-
f"Column values at breakpoint '{break_name}' must include at least one positive integer width."
208+
f"Column widths at breakpoint '{break_name}' must include at least one positive integer width."
206209
)
207210

208-
if len(list(y)) > n_kids:
211+
if len(positive_widths) > n_kids:
209212
warn(
210-
f"More column widths than children at breakpoint '{break_name}', extra widths will be ignored."
213+
f"More column widths than children at breakpoint '{break_name}', extra widths will be ignored.",
214+
stacklevel=2,
211215
)
212216

213-
return y
217+
return widths
214218

215219

216220
def col_widths_attrs(col_widths: BreakpointsOptional[int] | None) -> TagAttrs:

0 commit comments

Comments
 (0)