shiny-plotly 0.4.2__tar.gz → 0.4.3__tar.gz
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- {shiny_plotly-0.4.2 → shiny_plotly-0.4.3}/CHANGELOG.md +7 -0
- {shiny_plotly-0.4.2 → shiny_plotly-0.4.3}/PKG-INFO +1 -1
- {shiny_plotly-0.4.2 → shiny_plotly-0.4.3}/pyproject.toml +1 -1
- {shiny_plotly-0.4.2 → shiny_plotly-0.4.3}/src/shiny_plotly/_update.py +2 -2
- {shiny_plotly-0.4.2 → shiny_plotly-0.4.3}/src/shiny_plotly/www/shiny-plotly.js +57 -0
- {shiny_plotly-0.4.2 → shiny_plotly-0.4.3}/tests/browser/apps.py +105 -0
- {shiny_plotly-0.4.2 → shiny_plotly-0.4.3}/tests/browser/conftest.py +2 -0
- shiny_plotly-0.4.3/tests/browser/test_arrays.py +92 -0
- {shiny_plotly-0.4.2 → shiny_plotly-0.4.3}/.gitignore +0 -0
- {shiny_plotly-0.4.2 → shiny_plotly-0.4.3}/LICENSE +0 -0
- {shiny_plotly-0.4.2 → shiny_plotly-0.4.3}/README.md +0 -0
- {shiny_plotly-0.4.2 → shiny_plotly-0.4.3}/examples/core_app.py +0 -0
- {shiny_plotly-0.4.2 → shiny_plotly-0.4.3}/examples/dark_app.py +0 -0
- {shiny_plotly-0.4.2 → shiny_plotly-0.4.3}/examples/events_app.py +0 -0
- {shiny_plotly-0.4.2 → shiny_plotly-0.4.3}/examples/express_app.py +0 -0
- {shiny_plotly-0.4.2 → shiny_plotly-0.4.3}/examples/shinylive/app.py +0 -0
- {shiny_plotly-0.4.2 → shiny_plotly-0.4.3}/examples/shinylive/requirements.txt +0 -0
- {shiny_plotly-0.4.2 → shiny_plotly-0.4.3}/examples/streaming_app.py +0 -0
- {shiny_plotly-0.4.2 → shiny_plotly-0.4.3}/src/shiny_plotly/__init__.py +0 -0
- {shiny_plotly-0.4.2 → shiny_plotly-0.4.3}/src/shiny_plotly/_deps.py +0 -0
- {shiny_plotly-0.4.2 → shiny_plotly-0.4.3}/src/shiny_plotly/_html.py +0 -0
- {shiny_plotly-0.4.2 → shiny_plotly-0.4.3}/src/shiny_plotly/_render.py +0 -0
- {shiny_plotly-0.4.2 → shiny_plotly-0.4.3}/src/shiny_plotly/_serve.py +0 -0
- {shiny_plotly-0.4.2 → shiny_plotly-0.4.3}/src/shiny_plotly/_validate.py +0 -0
- {shiny_plotly-0.4.2 → shiny_plotly-0.4.3}/src/shiny_plotly/py.typed +0 -0
- {shiny_plotly-0.4.2 → shiny_plotly-0.4.3}/tests/browser/__init__.py +0 -0
- {shiny_plotly-0.4.2 → shiny_plotly-0.4.3}/tests/browser/test_browser.py +0 -0
- {shiny_plotly-0.4.2 → shiny_plotly-0.4.3}/tests/browser/test_dark_mode.py +0 -0
- {shiny_plotly-0.4.2 → shiny_plotly-0.4.3}/tests/browser/test_events.py +0 -0
- {shiny_plotly-0.4.2 → shiny_plotly-0.4.3}/tests/browser/test_shinylive_demo.py +0 -0
- {shiny_plotly-0.4.2 → shiny_plotly-0.4.3}/tests/browser/test_streaming_example.py +0 -0
- {shiny_plotly-0.4.2 → shiny_plotly-0.4.3}/tests/browser/test_update.py +0 -0
- {shiny_plotly-0.4.2 → shiny_plotly-0.4.3}/tests/helpers.py +0 -0
- {shiny_plotly-0.4.2 → shiny_plotly-0.4.3}/tests/newplot.py +0 -0
- {shiny_plotly-0.4.2 → shiny_plotly-0.4.3}/tests/test_compressed_js.py +0 -0
- {shiny_plotly-0.4.2 → shiny_plotly-0.4.3}/tests/test_examples.py +0 -0
- {shiny_plotly-0.4.2 → shiny_plotly-0.4.3}/tests/test_fig_to_ui.py +0 -0
- {shiny_plotly-0.4.2 → shiny_plotly-0.4.3}/tests/test_pages_contract.py +0 -0
- {shiny_plotly-0.4.2 → shiny_plotly-0.4.3}/tests/test_plotly_js.py +0 -0
- {shiny_plotly-0.4.2 → shiny_plotly-0.4.3}/tests/test_render_plotly.py +0 -0
- {shiny_plotly-0.4.2 → shiny_plotly-0.4.3}/tests/test_update.py +0 -0
|
@@ -7,6 +7,13 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
|
|
|
7
7
|
|
|
8
8
|
## [Unreleased]
|
|
9
9
|
|
|
10
|
+
## [0.4.3](https://github.com/rvben/shiny-plotly/compare/v0.4.2...v0.4.3) - 2026-09-25
|
|
11
|
+
|
|
12
|
+
### Fixed
|
|
13
|
+
|
|
14
|
+
- `extend_traces` and `prepend_traces` work on traces built from numpy arrays or pandas columns with plotly 6. plotly 6 sends those arrays in binary form, which `Plotly.extendTraces` refuses ("cannot extend missing or non-array attribute"), so the update failed in the browser and the chart stopped updating. The new points now land as their own values, even ones the original array's type could not hold, such as 300 or 0.5 added to `np.arange(3)`. This covers held updates and 2-D arrays such as a heatmap's `z`.
|
|
15
|
+
- `extend_traces` and `prepend_traces` are typed to take numpy arrays as the per-trace values (`{"y": [np.array([...])]}`), which they always accepted at runtime.
|
|
16
|
+
|
|
10
17
|
## [0.4.2](https://github.com/rvben/shiny-plotly/compare/v0.4.1...v0.4.2) - 2026-09-25
|
|
11
18
|
|
|
12
19
|
### Fixed
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.5
|
|
2
2
|
Name: shiny-plotly
|
|
3
|
-
Version: 0.4.
|
|
3
|
+
Version: 0.4.3
|
|
4
4
|
Summary: Render plotly figures in Shiny for Python with plain plotly.js, without the shinywidgets layer.
|
|
5
5
|
Project-URL: Homepage, https://github.com/rvben/shiny-plotly
|
|
6
6
|
Project-URL: Repository, https://github.com/rvben/shiny-plotly
|
|
@@ -4,7 +4,7 @@ build-backend = "hatchling.build"
|
|
|
4
4
|
|
|
5
5
|
[project]
|
|
6
6
|
name = "shiny-plotly"
|
|
7
|
-
version = "0.4.
|
|
7
|
+
version = "0.4.3"
|
|
8
8
|
description = "Render plotly figures in Shiny for Python with plain plotly.js, without the shinywidgets layer."
|
|
9
9
|
readme = "README.md"
|
|
10
10
|
requires-python = ">=3.10"
|
|
@@ -85,7 +85,7 @@ async def _send(id: str, method: str, args: Sequence[Any], session: Session | No
|
|
|
85
85
|
|
|
86
86
|
async def extend_traces(
|
|
87
87
|
id: str,
|
|
88
|
-
data: Mapping[str, Sequence[
|
|
88
|
+
data: Mapping[str, Sequence[Any]],
|
|
89
89
|
indices: Indices = None,
|
|
90
90
|
*,
|
|
91
91
|
max_points: int | None = None,
|
|
@@ -108,7 +108,7 @@ async def extend_traces(
|
|
|
108
108
|
|
|
109
109
|
async def prepend_traces(
|
|
110
110
|
id: str,
|
|
111
|
-
data: Mapping[str, Sequence[
|
|
111
|
+
data: Mapping[str, Sequence[Any]],
|
|
112
112
|
indices: Indices = None,
|
|
113
113
|
*,
|
|
114
114
|
max_points: int | None = None,
|
|
@@ -403,12 +403,69 @@
|
|
|
403
403
|
else queue.push(held);
|
|
404
404
|
}
|
|
405
405
|
|
|
406
|
+
// plotly 6 sends a figure's numpy arrays as binary, {dtype, bdata[, shape]}, which
|
|
407
|
+
// plotly.js keeps on the trace as sent and draws from a typed array.
|
|
408
|
+
var TYPED_ARRAYS = {
|
|
409
|
+
i1: Int8Array, u1: Uint8Array, u1c: Uint8ClampedArray, i2: Int16Array, u2: Uint16Array,
|
|
410
|
+
i4: Int32Array, u4: Uint32Array, f4: Float32Array, f8: Float64Array
|
|
411
|
+
};
|
|
412
|
+
|
|
413
|
+
function isBinary(value) {
|
|
414
|
+
return value !== null && typeof value === "object" && typeof value.bdata === "string" &&
|
|
415
|
+
TYPED_ARRAYS.hasOwnProperty(value.dtype);
|
|
416
|
+
}
|
|
417
|
+
|
|
418
|
+
// The values of a 1-D binary array as a typed array, of a 2-D one as rows of them; null
|
|
419
|
+
// for more dimensions, which no update extends.
|
|
420
|
+
function decodeBinary(spec) {
|
|
421
|
+
var shape = spec.shape ? String(spec.shape).split(",").map(Number) : [];
|
|
422
|
+
if (shape.length > 2) return null;
|
|
423
|
+
var bytes = window.atob(spec.bdata);
|
|
424
|
+
var buffer = new Uint8Array(bytes.length);
|
|
425
|
+
for (var i = 0; i < bytes.length; i++) buffer[i] = bytes.charCodeAt(i);
|
|
426
|
+
var flat = new TYPED_ARRAYS[spec.dtype](buffer.buffer);
|
|
427
|
+
if (shape.length < 2) return flat;
|
|
428
|
+
var rows = [];
|
|
429
|
+
for (var r = 0; r < shape[0]; r++) rows.push(flat.subarray(r * shape[1], (r + 1) * shape[1]));
|
|
430
|
+
return rows;
|
|
431
|
+
}
|
|
432
|
+
|
|
433
|
+
// An array of values, or of rows of values, as plain arrays, whatever form it came in;
|
|
434
|
+
// anything else as it is.
|
|
435
|
+
function plainArray(value) {
|
|
436
|
+
var decoded = isBinary(value) ? decodeBinary(value) : value;
|
|
437
|
+
if (decoded === null) return value;
|
|
438
|
+
if (ArrayBuffer.isView(decoded)) return Array.prototype.slice.call(decoded);
|
|
439
|
+
if (Array.isArray(decoded)) {
|
|
440
|
+
return decoded.map(function (v) { return ArrayBuffer.isView(v) ? plainArray(v) : v; });
|
|
441
|
+
}
|
|
442
|
+
return decoded;
|
|
443
|
+
}
|
|
444
|
+
|
|
445
|
+
// extendTraces and prependTraces join plain arrays to plain arrays and a typed array only
|
|
446
|
+
// to one of its own type, so a trace built from numpy (an int8 np.arange, say) could take
|
|
447
|
+
// no new points at all. The attributes an extend or prepend reaches become plain arrays
|
|
448
|
+
// of their values first, as if the figure had been built from lists; a plain array holds
|
|
449
|
+
// a 300 or a 0.5 where an int8 array would wrap or truncate them.
|
|
450
|
+
function plainTargets(gd, data, indices) {
|
|
451
|
+
Object.keys(data).forEach(function (key) {
|
|
452
|
+
var path = key.split(".");
|
|
453
|
+
indices.forEach(function (index) {
|
|
454
|
+
var node = gd.data[index < 0 ? gd.data.length + index : index];
|
|
455
|
+
for (var i = 0; node && i < path.length - 1; i++) node = node[path[i]];
|
|
456
|
+
var last = path[path.length - 1];
|
|
457
|
+
if (node && node[last] !== undefined) node[last] = plainArray(node[last]);
|
|
458
|
+
});
|
|
459
|
+
});
|
|
460
|
+
}
|
|
461
|
+
|
|
406
462
|
function applyUpdate(gd, update) {
|
|
407
463
|
var args = update.args;
|
|
408
464
|
if (update.method === "extendTraces" || update.method === "prependTraces") {
|
|
409
465
|
// Both want an array of indices and no maxPoints rather than null.
|
|
410
466
|
if (args[1] === null) args[1] = gd.data.map(function (_, i) { return i; });
|
|
411
467
|
if (args[2] === null) args.length = 2;
|
|
468
|
+
plainTargets(gd, args[0], args[1]);
|
|
412
469
|
}
|
|
413
470
|
return window.Plotly[update.method].apply(window.Plotly, [gd].concat(args));
|
|
414
471
|
}
|
|
@@ -642,3 +642,108 @@ def make_held_app() -> App:
|
|
|
642
642
|
return f"sent {done()}"
|
|
643
643
|
|
|
644
644
|
return App(app_ui, server)
|
|
645
|
+
|
|
646
|
+
|
|
647
|
+
def make_arrays_app() -> App:
|
|
648
|
+
"""
|
|
649
|
+
Figures built from numpy, which plotly 6 sends as binary, updated in place.
|
|
650
|
+
|
|
651
|
+
Each button extends or prepends one output, with plain lists and with numpy arrays of
|
|
652
|
+
other dtypes than the figure's: the int8 a small ``np.arange`` travels as must take a
|
|
653
|
+
300 and a 0.5, and no update may reach the figure in any other form than its values.
|
|
654
|
+
"""
|
|
655
|
+
app_ui = ui.page_fluid(
|
|
656
|
+
ui.input_action_button("extend_plain", "extend plain"),
|
|
657
|
+
ui.input_action_button("extend_capped", "extend capped"),
|
|
658
|
+
ui.input_action_button("prepend", "prepend"),
|
|
659
|
+
ui.input_action_button("extend_numpy", "extend numpy"),
|
|
660
|
+
ui.input_action_button("replace", "replace"),
|
|
661
|
+
ui.input_action_button("burst", "burst"),
|
|
662
|
+
ui.input_action_button("add_row", "add row"),
|
|
663
|
+
ui.output_text("bursts"),
|
|
664
|
+
output_plotly("ints", height="200px", width="400px"),
|
|
665
|
+
output_plotly("floats", height="200px", width="400px"),
|
|
666
|
+
output_plotly("two", height="200px", width="400px"),
|
|
667
|
+
output_plotly("grid", height="200px", width="400px"),
|
|
668
|
+
ui.navset_tab(
|
|
669
|
+
ui.nav_panel("Empty", "nothing here"),
|
|
670
|
+
ui.nav_panel("Held", output_plotly("held", height="200px", width="400px")),
|
|
671
|
+
id="tab",
|
|
672
|
+
),
|
|
673
|
+
)
|
|
674
|
+
|
|
675
|
+
def server(input: Inputs, output: Outputs, session: Session):
|
|
676
|
+
@render_plotly
|
|
677
|
+
def ints():
|
|
678
|
+
return go.Figure(go.Scatter(x=np.arange(3), y=np.arange(3)))
|
|
679
|
+
|
|
680
|
+
@render_plotly
|
|
681
|
+
def floats():
|
|
682
|
+
return go.Figure(go.Scatter(x=np.linspace(0, 1, 3), y=np.array([0.5, 1.5, 2.5])))
|
|
683
|
+
|
|
684
|
+
@render_plotly
|
|
685
|
+
def two():
|
|
686
|
+
return go.Figure([go.Scatter(y=np.arange(3)), go.Scatter(y=[5, 6])])
|
|
687
|
+
|
|
688
|
+
@render_plotly
|
|
689
|
+
def held():
|
|
690
|
+
return go.Figure(go.Scatter(y=np.arange(3)))
|
|
691
|
+
|
|
692
|
+
@render_plotly
|
|
693
|
+
def grid():
|
|
694
|
+
# A 2-D array travels as one binary block with its shape.
|
|
695
|
+
return go.Figure(go.Heatmap(z=np.arange(4).reshape(2, 2)))
|
|
696
|
+
|
|
697
|
+
@reactive.effect
|
|
698
|
+
@reactive.event(input.add_row)
|
|
699
|
+
async def _add_row():
|
|
700
|
+
await extend_traces("grid", {"z": [[[40, 50.5]]]})
|
|
701
|
+
|
|
702
|
+
@reactive.effect
|
|
703
|
+
@reactive.event(input.extend_plain)
|
|
704
|
+
async def _extend_plain():
|
|
705
|
+
await extend_traces("ints", {"x": [[3, 4]], "y": [[300, 0.5]]})
|
|
706
|
+
|
|
707
|
+
@reactive.effect
|
|
708
|
+
@reactive.event(input.extend_capped)
|
|
709
|
+
async def _extend_capped():
|
|
710
|
+
await extend_traces("floats", {"x": [[2]], "y": [[3.5]]}, max_points=3)
|
|
711
|
+
|
|
712
|
+
@reactive.effect
|
|
713
|
+
@reactive.event(input.prepend)
|
|
714
|
+
async def _prepend():
|
|
715
|
+
await prepend_traces("two", {"y": [[-1], [4]]}, max_points=3)
|
|
716
|
+
|
|
717
|
+
@reactive.effect
|
|
718
|
+
@reactive.event(input.extend_numpy)
|
|
719
|
+
async def _extend_numpy():
|
|
720
|
+
await extend_traces(
|
|
721
|
+
"ints",
|
|
722
|
+
{
|
|
723
|
+
"x": [np.array([5, 6, 7], dtype=np.uint8)],
|
|
724
|
+
"y": [np.array([2**40, -1, 7], dtype=np.int64)],
|
|
725
|
+
},
|
|
726
|
+
)
|
|
727
|
+
await extend_traces("floats", {"y": [np.array([0.25], dtype=np.float32)]})
|
|
728
|
+
|
|
729
|
+
@reactive.effect
|
|
730
|
+
@reactive.event(input.replace)
|
|
731
|
+
async def _replace():
|
|
732
|
+
await restyle("floats", {"y": [np.array([9.5, 10.5])]})
|
|
733
|
+
await add_traces("two", go.Scatter(y=np.array([-3, 3], dtype=np.int16)))
|
|
734
|
+
await update("two", restyle={"marker.size": [np.array([11.0, 12.0])]}, indices=[1])
|
|
735
|
+
|
|
736
|
+
sent = reactive.value(0)
|
|
737
|
+
|
|
738
|
+
@reactive.effect
|
|
739
|
+
@reactive.event(input.burst)
|
|
740
|
+
async def _burst():
|
|
741
|
+
for i in range(5):
|
|
742
|
+
await extend_traces("held", {"y": [np.array([10.0 * i + 0.5])]}, max_points=6)
|
|
743
|
+
sent.set(sent() + 1)
|
|
744
|
+
|
|
745
|
+
@render.text
|
|
746
|
+
def bursts():
|
|
747
|
+
return f"bursts {sent()}"
|
|
748
|
+
|
|
749
|
+
return App(app_ui, server)
|
|
@@ -15,6 +15,7 @@ from starlette.routing import Mount
|
|
|
15
15
|
|
|
16
16
|
from .apps import (
|
|
17
17
|
make_app,
|
|
18
|
+
make_arrays_app,
|
|
18
19
|
make_dark_app,
|
|
19
20
|
make_events_app,
|
|
20
21
|
make_held_app,
|
|
@@ -52,6 +53,7 @@ def server_url() -> Iterator[str]:
|
|
|
52
53
|
Mount("/hidden", app=make_hidden_app()),
|
|
53
54
|
Mount("/post-script", app=make_post_script_app()),
|
|
54
55
|
Mount("/held", app=make_held_app()),
|
|
56
|
+
Mount("/arrays", app=make_arrays_app()),
|
|
55
57
|
Mount("/stream", app=load_example("streaming_app.py")),
|
|
56
58
|
Mount("/demo", app=load_example("shinylive/app.py")),
|
|
57
59
|
Mount("/", app=make_app()),
|
|
@@ -0,0 +1,92 @@
|
|
|
1
|
+
"""In-place updates to figures built from numpy arrays, which plotly 6 sends as binary."""
|
|
2
|
+
|
|
3
|
+
import json
|
|
4
|
+
from collections.abc import Iterator
|
|
5
|
+
|
|
6
|
+
import pytest
|
|
7
|
+
from playwright.sync_api import Page, expect
|
|
8
|
+
|
|
9
|
+
pytestmark = pytest.mark.browser
|
|
10
|
+
|
|
11
|
+
SVG = "svg.main-svg"
|
|
12
|
+
|
|
13
|
+
|
|
14
|
+
@pytest.fixture
|
|
15
|
+
def app(page: Page, server_url: str, errors: list[str]) -> Iterator[Page]:
|
|
16
|
+
page.goto(server_url + "/arrays/")
|
|
17
|
+
for output_id in ("ints", "floats", "two", "grid"):
|
|
18
|
+
expect(page.locator(f"#{output_id} {SVG}").first).to_be_visible()
|
|
19
|
+
yield page
|
|
20
|
+
assert errors == []
|
|
21
|
+
|
|
22
|
+
|
|
23
|
+
def drawn(output_id: str, index: int, attribute: str) -> str:
|
|
24
|
+
"""The values plotly draws for one attribute of one trace, as a plain list."""
|
|
25
|
+
trace = f"document.getElementById('{output_id}-plotly')._fullData[{index}]"
|
|
26
|
+
return f"Array.from({trace}.{attribute})"
|
|
27
|
+
|
|
28
|
+
|
|
29
|
+
def wait_for(page: Page, expression: str, expected: object) -> None:
|
|
30
|
+
want = json.dumps(json.dumps(expected, separators=(",", ":")))
|
|
31
|
+
page.wait_for_function(f"() => JSON.stringify({expression}) === {want}")
|
|
32
|
+
|
|
33
|
+
|
|
34
|
+
def points(page: Page, output_id: str, index: int) -> int:
|
|
35
|
+
return page.locator(f"#{output_id} .scatterlayer .trace").nth(index).locator(".point").count()
|
|
36
|
+
|
|
37
|
+
|
|
38
|
+
def test_a_trace_built_from_numpy_takes_points_of_any_value(app: Page):
|
|
39
|
+
"""np.arange(3) travels as int8; a 300 and a 0.5 must still land as themselves."""
|
|
40
|
+
app.click("#extend_plain")
|
|
41
|
+
|
|
42
|
+
wait_for(app, drawn("ints", 0, "y"), [0, 1, 2, 300, 0.5])
|
|
43
|
+
assert app.evaluate(f"() => {drawn('ints', 0, 'x')}") == [0, 1, 2, 3, 4]
|
|
44
|
+
assert points(app, "ints", 0) == 5, "the drawing follows the data"
|
|
45
|
+
|
|
46
|
+
|
|
47
|
+
def test_extending_a_numpy_trace_keeps_the_rolling_window(app: Page):
|
|
48
|
+
app.click("#extend_capped")
|
|
49
|
+
|
|
50
|
+
wait_for(app, drawn("floats", 0, "y"), [1.5, 2.5, 3.5])
|
|
51
|
+
assert app.evaluate(f"() => {drawn('floats', 0, 'x')}") == [0.5, 1, 2]
|
|
52
|
+
|
|
53
|
+
|
|
54
|
+
def test_prepending_reaches_numpy_and_list_traces_alike(app: Page):
|
|
55
|
+
app.click("#prepend")
|
|
56
|
+
|
|
57
|
+
wait_for(app, drawn("two", 0, "y"), [-1, 0, 1])
|
|
58
|
+
wait_for(app, drawn("two", 1, "y"), [4, 5, 6])
|
|
59
|
+
|
|
60
|
+
|
|
61
|
+
def test_numpy_points_of_any_dtype_land_as_their_values(app: Page):
|
|
62
|
+
"""uint8, an int64 past int32 and a float32, into int8 and float64 traces."""
|
|
63
|
+
app.click("#extend_numpy")
|
|
64
|
+
|
|
65
|
+
wait_for(app, drawn("ints", 0, "y"), [0, 1, 2, 2**40, -1, 7])
|
|
66
|
+
assert app.evaluate(f"() => {drawn('ints', 0, 'x')}") == [0, 1, 2, 5, 6, 7]
|
|
67
|
+
wait_for(app, drawn("floats", 0, "y"), [0.5, 1.5, 2.5, 0.25])
|
|
68
|
+
|
|
69
|
+
|
|
70
|
+
def test_numpy_arrays_in_restyle_add_traces_and_update_land_as_their_values(app: Page):
|
|
71
|
+
app.click("#replace")
|
|
72
|
+
|
|
73
|
+
wait_for(app, drawn("floats", 0, "y"), [9.5, 10.5])
|
|
74
|
+
wait_for(app, drawn("two", 2, "y"), [-3, 3])
|
|
75
|
+
wait_for(app, drawn("two", 1, "marker.size"), [11, 12])
|
|
76
|
+
|
|
77
|
+
|
|
78
|
+
def test_updates_held_for_a_hidden_numpy_figure_apply_once_it_is_drawn(app: Page):
|
|
79
|
+
app.click("#burst")
|
|
80
|
+
expect(app.locator("#bursts")).to_have_text("bursts 1")
|
|
81
|
+
|
|
82
|
+
app.get_by_role("tab", name="Held").click()
|
|
83
|
+
expect(app.locator(f"#held {SVG}").first).to_be_visible()
|
|
84
|
+
|
|
85
|
+
wait_for(app, drawn("held", 0, "y"), [2, 0.5, 10.5, 20.5, 30.5, 40.5])
|
|
86
|
+
|
|
87
|
+
|
|
88
|
+
def test_a_heatmap_built_from_a_2d_array_takes_a_new_row(app: Page):
|
|
89
|
+
app.click("#add_row")
|
|
90
|
+
|
|
91
|
+
rows = "document.getElementById('grid-plotly')._fullData[0].z.map((r) => Array.from(r))"
|
|
92
|
+
wait_for(app, rows, [[0, 1], [2, 3], [40, 50.5]])
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|