Skip to content

Commit 5ee8c97

Browse files
authored
Changes to accomodate plotly v6 (#182)
1 parent 61215a5 commit 5ee8c97

File tree

5 files changed

+50
-25
lines changed

5 files changed

+50
-25
lines changed

CHANGELOG.md

+1
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
77

88
## [UNRELEASED] - 2025-01-23
99

10+
* Updates to accomodate the new plotly v6.0.0 release. (#182)
1011
* Fixed an issue with plotly graphs sometimes not getting fully removed from the DOM. (#178)
1112
* Fixed an issue with ipyleaflet erroring out when attempting to read the `.model_id` property of a closed widget object. (#179)
1213
* Fixed an issue where altair charts would sometimes render to a 0 height after being shown, hidden, and then shown again. (#180)

js/src/output.ts

+12-9
Original file line numberDiff line numberDiff line change
@@ -27,17 +27,20 @@ class OutputManager extends HTMLManager {
2727
// Define our own custom module loader for Shiny
2828
const shinyRequireLoader = async function(moduleName: string, moduleVersion: string): Promise<any> {
2929

30-
// shiny provides require.js and also sets `define.amd=false` to prevent <script>s
31-
// with UMD loaders from triggering anonymous define() errors. shinywidgets should
32-
// generally be able to avoid anonymous define errors though since there should only
33-
// be one 'main' anonymous define() for the widget's module (located in a JS file that
34-
// we've already require.config({paths: {...}})ed; and in that case, requirejs adds a
35-
// data-requiremodule attribute to the <script> tag that shiny's custom define will
36-
// recognize and use as the name).)
30+
// shiny provides a shim of require.js which allows <script>s with anonymous
31+
// define()s to be loaded without error. When an anonymous define() occurs,
32+
// the shim uses the data-requiremodule attribute (set by require.js) on the script
33+
// to determine the module name.
34+
// https://github.com/posit-dev/py-shiny/blob/230940c/scripts/define-shims.js#L10-L16
35+
// In the context of shinywidgets, when a widget gets rendered, it should
36+
// come with another <script> tag that does `require.config({paths: {...}})`
37+
// which maps the module name to a URL of the widget's JS file.
3738
const oldAmd = (window as any).define.amd;
3839

39-
// The is the original value for define.amd that require.js sets
40-
(window as any).define.amd = {jQuery: true};
40+
// This is probably not necessary, but just in case -- especially now in a
41+
// anywidget/ES6 world, we probably don't want to load AMD modules
42+
// (plotly is one example of a widget that will fail to load if AMD is enabled)
43+
(window as any).define.amd = false;
4144

4245
// Store jQuery global since loading we load a module, it may overwrite it
4346
// (qgrid is one good example)

shinywidgets/_render_widget_base.py

+3
Original file line numberDiff line numberDiff line change
@@ -178,6 +178,9 @@ def set_layout_defaults(widget: Widget) -> Tuple[Widget, bool]:
178178
# so change that 60px default to 32px
179179
if layout.margin["t"] == 60: # pyright: ignore
180180
layout.margin["t"] = 32 # pyright: ignore
181+
# In plotly >=v6.0, the plot won't actually fill unless it's responsive
182+
if fill:
183+
widget._config = {"responsive": True, **widget._config} # type: ignore
181184

182185
widget.layout = layout
183186

shinywidgets/_shinywidgets.py

+33-15
Original file line numberDiff line numberDiff line change
@@ -96,9 +96,6 @@ def _cleanup_session_state():
9696

9797
session.on_ended(_cleanup_session_state)
9898

99-
# Get the initial state of the widget
100-
state, buffer_paths, buffers = _remove_buffers(w.get_state())
101-
10299
# Make sure window.require() calls made by 3rd party widgets
103100
# (via handle_comm_open() -> new_model() -> loadClass() -> requireLoader())
104101
# actually point to directories served locally by shiny
@@ -114,18 +111,39 @@ def _cleanup_session_state():
114111

115112
id = cast(str, w._model_id)
116113

117-
# Initialize the comm...this will also send the initial state of the widget
118-
with widget_comm_patch():
119-
w.comm = ShinyComm(
120-
comm_id=id,
121-
comm_manager=COMM_MANAGER,
122-
target_name="jupyter.widgets",
123-
data={"state": state, "buffer_paths": buffer_paths},
124-
buffers=cast(BufferType, buffers),
125-
# TODO: should this be hard-coded?
126-
metadata={"version": __protocol_version__},
127-
html_deps=session._process_ui(TagList(widget_dep))["deps"],
128-
)
114+
# Since the actual ShinyComm() is initialized _after_ the Widget is initialized,
115+
# and Widget.__init__() includes a call to Widget.open() which opens an unnecessary
116+
# comm, we just set the comm to a dummy comm for now (to avoid unnecessary work)
117+
w.comm = OrphanedShinyComm(id)
118+
119+
# Schedule the opening of the comm to happen sometime after this init function.
120+
# This is important for widgets like plotly that do additional initialization that
121+
# is required to get a valid widget state.
122+
@reactive.effect(priority=99999)
123+
def _open_shiny_comm():
124+
125+
# Call _repr_mimebundle_() before get_state() since it may modify the widget
126+
# in an important way (unfortunately, it does for plotly)
127+
# # https://github.com/plotly/plotly.py/blob/0089f32/packages/python/plotly/plotly/basewidget.py#L734-L738
128+
w._repr_mimebundle_()
129+
130+
# Now, get the state
131+
state, buffer_paths, buffers = _remove_buffers(w.get_state())
132+
133+
# Initialize the comm -- this sends widget state to the frontend
134+
with widget_comm_patch():
135+
w.comm = ShinyComm(
136+
comm_id=id,
137+
comm_manager=COMM_MANAGER,
138+
target_name="jupyter.widgets",
139+
data={"state": state, "buffer_paths": buffer_paths},
140+
buffers=cast(BufferType, buffers),
141+
# TODO: should this be hard-coded?
142+
metadata={"version": __protocol_version__},
143+
html_deps=session._process_ui(TagList(widget_dep))["deps"],
144+
)
145+
146+
_open_shiny_comm.destroy()
129147

130148
# If we're in a reactive context, close this widget when the context is invalidated
131149
# TODO: this should probably only be done in an output context, but I'm pretty sure

shinywidgets/static/output.js

+1-1
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

0 commit comments

Comments
 (0)