tests
test_links.py
try:
import holoviews as hv
except ImportError:
hv = None
import pytest
from bokeh.plotting import figure
from panel.layout import Row
from panel.links import Link
from panel.pane import Bokeh, HoloViews
from panel.widgets import FloatSlider, RangeSlider, ColorPicker, TextInput, DatetimeInput
from panel.tests.util import hv_available
def test_widget_link_bidirectional():
t1 = TextInput()
t2 = TextInput()
t1.link(t2, value='value', bidirectional=True)
t1.value = 'ABC'
assert t1.value == 'ABC'
assert t2.value == 'ABC'
t2.value = 'DEF'
assert t1.value == 'DEF'
assert t2.value == 'DEF'
def test_widget_jslink_bidirectional(document, comm):
t1 = TextInput()
t2 = TextInput()
t1.jslink(t2, value='value', bidirectional=True)
row = Row(t1, t2)
model = row.get_root(document, comm)
tm1, tm2 = model.children
link1_customjs = tm1.js_property_callbacks['change:value'][-1]
link2_customjs = tm2.js_property_callbacks['change:value'][-1]
assert link1_customjs.args['source'] is tm1
assert link2_customjs.args['source'] is tm2
assert link1_customjs.args['target'] is tm2
assert link2_customjs.args['target'] is tm1
def test_widget_link_source_param_not_found():
t1 = TextInput()
t2 = TextInput()
with pytest.raises(ValueError) as excinfo:
t1.jslink(t2, value1='value')
assert "Could not jslink \'value1\' parameter" in str(excinfo)
def test_widget_link_target_param_not_found():
t1 = TextInput()
t2 = TextInput()
with pytest.raises(ValueError) as excinfo:
t1.jslink(t2, value='value1')
assert "Could not jslink \'value1\' parameter" in str(excinfo)
def test_widget_link_no_transform_error():
t1 = DatetimeInput()
t2 = TextInput()
with pytest.raises(ValueError) as excinfo:
t1.jslink(t2, value='value')
assert "Cannot jslink \'value\' parameter on DatetimeInput object" in str(excinfo)
def test_widget_link_no_target_transform_error():
t1 = DatetimeInput()
t2 = TextInput()
with pytest.raises(ValueError) as excinfo:
t2.jslink(t1, value='value')
assert ("Cannot jslink \'value\' parameter on TextInput object "
"to \'value\' parameter on DatetimeInput object") in str(excinfo)
@hv_available
def test_pnwidget_hvplot_links(document, comm):
size_widget = FloatSlider(value=5, start=1, end=10)
points1 = hv.Points([1, 2, 3])
size_widget.jslink(points1, value='glyph.size')
row = Row(points1, size_widget)
model = row.get_root(document, comm=comm)
hv_views = row.select(HoloViews)
widg_views = row.select(FloatSlider)
assert len(hv_views) == 1
assert len(widg_views) == 1
slider = widg_views[0]._models[model.ref['id']][0]
scatter = hv_views[0]._plots[model.ref['id']][0].handles['glyph']
link_customjs = slider.js_property_callbacks['change:value'][-1]
assert link_customjs.args['source'] is slider
assert link_customjs.args['target'] is scatter
code = """
var value = source['value'];
value = value;
value = value;
try {
var property = target.properties['size'];
if (property !== undefined) { property.validate(value); }
} catch(err) {
console.log('WARNING: Could not set size on target, raised error: ' + err);
return;
}
try {
target['size'] = value;
} catch(err) {
console.log(err)
}
"""
assert link_customjs.code == code
@hv_available
def test_bkwidget_hvplot_links(document, comm):
from bokeh.models import Slider
bokeh_widget = Slider(value=5, start=1, end=10, step=1e-1)
points1 = hv.Points([1, 2, 3])
Link(bokeh_widget, points1, properties={'value': 'glyph.size'})
row = Row(points1, bokeh_widget)
model = row.get_root(document, comm=comm)
hv_views = row.select(HoloViews)
assert len(hv_views) == 1
slider = bokeh_widget
scatter = hv_views[0]._plots[model.ref['id']][0].handles['glyph']
link_customjs = slider.js_property_callbacks['change:value'][-1]
assert link_customjs.args['source'] is slider
assert link_customjs.args['target'] is scatter
code = """
var value = source['value'];
value = value;
value = value;
try {
var property = target.properties['size'];
if (property !== undefined) { property.validate(value); }
} catch(err) {
console.log('WARNING: Could not set size on target, raised error: ' + err);
return;
}
try {
target['size'] = value;
} catch(err) {
console.log(err)
}
"""
assert link_customjs.code == code
def test_bkwidget_bkplot_links(document, comm):
from bokeh.models import Slider
bokeh_widget = Slider(value=5, start=1, end=10, step=1e-1)
bokeh_fig = figure()
scatter = bokeh_fig.scatter([1, 2, 3], [1, 2, 3])
Link(bokeh_widget, scatter, properties={'value': 'glyph.size'})
row = Row(bokeh_fig, bokeh_widget)
row.get_root(document, comm=comm)
slider = bokeh_widget
link_customjs = slider.js_property_callbacks['change:value'][-1]
assert link_customjs.args['source'] is slider
assert link_customjs.args['target'] is scatter.glyph
code = """
var value = source['value'];
value = value;
value = value;
try {
var property = target.properties['size'];
if (property !== undefined) { property.validate(value); }
} catch(err) {
console.log('WARNING: Could not set size on target, raised error: ' + err);
return;
}
try {
target['size'] = value;
} catch(err) {
console.log(err)
}
"""
assert link_customjs.code == code
def test_widget_bkplot_link(document, comm):
widget = ColorPicker(value='#ff00ff')
bokeh_fig = figure()
scatter = bokeh_fig.scatter([1, 2, 3], [1, 2, 3])
widget.jslink(scatter.glyph, value='fill_color')
row = Row(bokeh_fig, widget)
model = row.get_root(document, comm=comm)
link_customjs = model.children[1].js_property_callbacks['change:color'][-1]
assert link_customjs.args['source'] is model.children[1]
assert link_customjs.args['target'] is scatter.glyph
assert scatter.glyph.fill_color == '#ff00ff'
code = """
var value = source['color'];
value = value;
value = value;
try {
var property = target.properties['fill_color'];
if (property !== undefined) { property.validate(value); }
} catch(err) {
console.log('WARNING: Could not set fill_color on target, raised error: ' + err);
return;
}
try {
target['fill_color'] = value;
} catch(err) {
console.log(err)
}
"""
assert link_customjs.code == code
def test_bokeh_figure_jslink(document, comm):
fig = figure()
pane = Bokeh(fig)
t1 = TextInput()
pane.jslink(t1, **{'x_range.start': 'value'})
row = Row(pane, t1)
model = row.get_root(document, comm)
link_customjs = fig.x_range.js_property_callbacks['change:start'][-1]
assert link_customjs.args['source'] == fig.x_range
assert link_customjs.args['target'] == model.children[1]
assert link_customjs.code == """
var value = source['start'];
value = value;
value = value;
try {
var property = target.properties['value'];
if (property !== undefined) { property.validate(value); }
} catch(err) {
console.log('WARNING: Could not set value on target, raised error: ' + err);
return;
}
try {
target['value'] = value;
} catch(err) {
console.log(err)
}
"""
def test_widget_jscallback(document, comm):
widget = ColorPicker(value='#ff00ff')
widget.jscallback(value='some_code')
model = widget.get_root(document, comm=comm)
customjs = model.js_property_callbacks['change:color'][-1]
assert customjs.args['source'] is model
assert customjs.code == "try { some_code } catch(err) { console.log(err) }"
def test_widget_jscallback_args_scalar(document, comm):
widget = ColorPicker(value='#ff00ff')
widget.jscallback(value='some_code', args={'scalar': 1})
model = widget.get_root(document, comm=comm)
customjs = model.js_property_callbacks['change:color'][-1]
assert customjs.args['scalar'] == 1
def test_widget_jscallback_args_model(document, comm):
widget = ColorPicker(value='#ff00ff')
widget2 = ColorPicker(value='#ff00ff')
widget.jscallback(value='some_code', args={'widget': widget2})
model = Row(widget, widget2).get_root(document, comm=comm)
customjs = model.children[0].js_property_callbacks['change:color'][-1]
assert customjs.args['source'] is model.children[0]
assert customjs.args['widget'] is model.children[1]
assert customjs.code == "try { some_code } catch(err) { console.log(err) }"
@hv_available
def test_hvplot_jscallback(document, comm):
points1 = hv.Points([1, 2, 3])
hvplot = HoloViews(points1)
hvplot.jscallback(**{'x_range.start': "some_code"})
model = hvplot.get_root(document, comm=comm)
x_range = hvplot._plots[model.ref['id']][0].handles['x_range']
customjs = x_range.js_property_callbacks['change:start'][-1]
assert customjs.args['source'] is x_range
assert customjs.code == "try { some_code } catch(err) { console.log(err) }"
@hv_available
def test_link_with_customcode(document, comm):
range_widget = RangeSlider(start=0., end=1.)
curve = hv.Curve([])
code = """
x_range.start = source.value[0]
x_range.end = source.value[1]
"""
range_widget.jslink(curve, code={'value': code})
row = Row(curve, range_widget)
range_widget.value = (0.5, 0.7)
model = row.get_root(document, comm=comm)
hv_views = row.select(HoloViews)
widg_views = row.select(RangeSlider)
assert len(hv_views) == 1
assert len(widg_views) == 1
range_slider = widg_views[0]._models[model.ref['id']][0]
x_range = hv_views[0]._plots[model.ref['id']][0].handles['x_range']
link_customjs = range_slider.js_property_callbacks['change:value'][-1]
assert link_customjs.args['source'] is range_slider
assert link_customjs.args['x_range'] is x_range
assert link_customjs.code == "try { %s } catch(err) { console.log(err) }" % code