Skip to content

Commit

Permalink
Improve Bokeh Layout (bokeh#4343)
Browse files Browse the repository at this point in the history
* Have Box and Widget recompute height on render if mode is responsive

* Add example with lots of widgets laid out.

* Fix layout tests

* Get layout working on page load

Would love to find a more elegant way to do this, but it works for now.

* Add a space to top for aesthetics.

* Remove coloring from slider, and tweak slider template.

* Start tweaking css for widgets

* plainer styling
* try and make sizes play nicely with layout

* Start tweaking styling of widgets to give better consistency

* Small tweak back for layout box

* Tweak constraints on LayoutDOM & Widget

* Fix up tests after changing layout constraints.

* Tweak button display

It may be that all widgets need a template so that they can live under a widget
div and behave nicely with layout.

* Set the text_input height

* Continue small styling tweaks

* Add grow concept

Widgets don't grow, layouts do.

* Add ToolBar widget to python side

* Add ToolBar object to Plot

Deprecate plot.tools and plot.logo

* tools is also deprecated on Plot.

* Move ToolManager to Toolbar model

* first step to making it a widget

* Be resilient to setting logo via Plot declaration

Add tests for deprecated properties

* Toolbar becomes a Widget

* Add a simple example for laying out plots with rows & columns

* Massive simplification of responsive width_height example

Note aspect ratio maintenance does not currently exist - added to bokeh#4164 ToDo.

* Update responsive tests

1) add a new simple box responsive test
2) update the aspect ratio box responsive test (and skip for now)
3) update all the tests to use Plot not figure as part of ongoing effort to
isolate integration tests as much as possible - bokeh#4314

* Fix-up bokehjs tests and remove resize tests

Some bokehjs tests were broken after the toolbar change.
Remove resizing tests as about to use new resizing function on layout.

* Move location of toolbar definition

Allows defaults test to pass.

* Remove old resize code

Remove old resize code from plot_canvas.

Breaks all responsiveness.

* Also add protection onto plot.tools

* Remove plot_template (keep it for grid_plot)

We're no longer going to have the gorpy table layout for plot. But grid_plot
still needs it. We'll probably need to refactor grid_plot later.

* Trigger a resize event from document not a layout_update event

We need the distinction for the new plot resizing

* Remove the update_dimensions method

Inconsistently used, and adds to the confusion.

* Remove the plot template

We're just going to have a wrapper div for plot now.

* Don't repeate create Variables

@_width and @_height were already created by LayoutDOM

* Update responsive box test

* Remove min_size constraint from plot_canvas, and use parent constraints

We may need to add min_size constraint back in at a later date

* Remove all title related code

Will be handled in future by annotations

* Move grow onto LayoutDOM

* Make adding toolbar less flaky

* Make all variables identifiable & improve kiwi error

Add the @id to every solver Variable to try and help with debugging.
Clean-up duplicated variables on box.coffee
Improve kiwi error so "unsatisfiable constraint" gives you a hint.

* Increase information in logger statement

* Comment out WEAK_EQ whitespace constraint.

* Improve error reporting in kiwi further

* Update document resize values

Add a border for all responsive modes

* Slightly improve resize triggering

* Factor out edit_variable in responsive mode onto LayoutDOM

* Remove whitespace constraints on LayoutDOM

They just don't seem to work. Should probably have a chat with @havocp about
all of the whitespace problems. Investigate if it's a problem with WEAK_EQ.

* Give the layout engine a bit of a workout

* Factor out a render_dom method

Will be reusable by plot

* Move render_dom into LayoutDOM for use by plot

* Correct layout_canvas test

Now have more specific names on all Variables

* Lots of logging when defaults don't match

* Toolbar should be in define, not in internal

* Add resize to plot_canvas

* Need to add back in that extra resize :s

* Trigger on resize if plot is root

* Toolbar can render itself!

* Update document

Resize triggers events for every kind of responsive mode as they all know how
to handle themselves and we still need layout to occur even in fixed mode -
albeit only initially. We'll probably want to do some optimization here later.

* Add some protection into canvas so it doesn't try and set to zero (which it can't)

* Update Box render to handle width and fixed mode

* Update layout_dom to handle fixed mode properly

* Update box to work with fixed layout

Clean-up tests a little

* Update widgets to handle fixed view

Tweak box test

* Get Toolbar playing nicely with layout engine

* Make default responsive mode on PlotCanvas box

* PlotCanvas handles fixed responsive mode now.

* Tweak toolbar tooltips

Small alignment things - make sure tooltips always go inwards - continue work
on defaulting styling. Try and make things a little cleaner and more
consistent.

* Add custom dom layout for toolbar

* Plot is a subclass of Box and creates a PlotCanvas

Move plot_height, plot_width, toolbar, toolbar_location properties to Plot.
Also remove unnecessary render code from PlotCanvas.

* Make Plot into a Box layout that holds Toolbar and PlotCanvas

* Get GMapPlot working again

* Clean-up duplicated or no-longer needed css

* Remove no-longer needed property from document

* Improve defaults mismatch reporting

* Tweak properties to get defaults test passing

* Add new base screenshots

* Add other failing screenshot test and small tweaks

* Deprecate old layouts from models interface.

Start updating example custom.py

* Fix-up erroring layout tests

Stub out initialize to by-pass a lot of set-up and only focus on what we need.

* Remove throttled resize for now

Seems to be making initialization worse. Definitely want to fix this up though.

* Try slightly more aggressive handling of minimum sizes on canvas

* Do not render margins on plots

Seems to help with laying out grids of plots with toolbars

* We can't mess with dom_left.

Not surprisingly, ends up hiding a portion of the toolbar on
grid-type layouts

* Small tweak to set a default location.

This helps when rendering toolbar away from plot (which necessitates setting
toolbar_location=None on the plot, so the default doesn't make it through)

* Correct model deprecation

Addition of grow property snuck in
Typescript API HBox/VBox to Row/Column

* Tweak some defaults for a more unified theme.

* Continue deprecating old style layouts

* Undo changes that added item to curdoc.

This breaks lots of things, and is not important to fix for layout.

* Revert "Undo changes that added item to curdoc."

Reverting the reversion - I'm in two minds about this.

This reverts commit bd63336.

* Make Row & Column available via bokeh.plotting

* show methods handle adding object to document if necessary

This helps maintain some backwards compatibility and provides a consistent api
across different forms of output e.g. output_file(); show(p) and
output_server(); show(p) now behave exactly the same for all kinds of Layouts.

* Fix up examples that were still not rendering.

* Add an explanatory note to side panel

* Remove responsive and grow, and give bk-root height

* Reset all new layouts

* Take logic out of LayoutDOM, Setup Plot as a container

* Remove responsive stuff from document

Add a proper error if adding something unconstraine to doc (we're going to want
to modify this later so can non-layoutables don't need to conform to this
constraint)

* Have plot pull up constraints

* Have plot initialize its children's views

* Have toolbar render itself

* Get PlotCanvas and Toolbar stuck together

Currently not shifting the canvas down, but is making space for it.

* Get toolbar laid out above plot.

* Make naming on canvas/canvas.coffee clearer

Easier to see which are variables and which is the initial height/width

* Add dom_left dom_top variables

* Delayed trigger

* Plot correctly renders with no toolbar.

min-size works correctly, plot is being pulled to edges of window.

* Add the orientation variables

* Children's order doesn't matter for the way we're constructing constraints.

Could possible clean this up later to do more like last/next, but not necessary
for now.

* Get toolbar rendering correctly above and below

* Correct weak constrainer export

* Get toolbar displaying on all four sides in a responsive box

* Have resize work against bk-root, not window.

* Remove unused responsive mode

* Add a note about how to do multiple roots.

We still have a big notebook problem - may get fixed with other modes.

* INDIVIDUAL PLOTS WITH TOOLBARS ARE RESPONSIVE AT THIS POINT!

Comment out verbose logging statement so I have something to commit

* Add whitespace constrained variables and make dom_left and dom_top edit variables

* Boxes inherit from LayoutDOM

* We're going to use two new variables

* Add hook so boxes can be found in dom

* Add some more variables needed for layout

* A single column or row will work-ish

Replace older temp version of box (I'd stuck in a really old version while
cleaning house)

* Will layout a grid if every plot is wrapped in a box.

* Tweak box algorithm for readability & testability

* used objects instead of arrays - makes it easier to read constraints
* move functions onto object and out of get_constraints for readability of
  get_constraints method and improved testability
* add optimization that havoc indicated now that we have _is_root property

* Update LayoutDOM and Plot after box tweaking

* Small layout clean-up

* Small clean-up - still need Box wrapper for plots to line up correctly

* Plots no longer need to be wrapped

* Update the test layouts

* More clean-up

Responsive-mode has been unimplemented for the moment. All layout is box.

* Document doesn't have an ide so tweak variable name

* Get deeply nested layouts working again.

Unbelievable that this was such a small change.

* Get plots axes aligning (works with no toolbars)

* Small tweak to variable name for consistency and easy of editing

* Start playing with alignment vars (toolbars have gone missing)

* Get plots w/ toolbars nested and aligning

* Get toolbars lining up awesomely.

* Add a widget box.

It's a little crude at the moment, but its functional. Provides a container for
widgets so they can be laid out by box grid. They take their size from the box,
and then overflow anything else. This can be improved, but is an ok start.

* Add a note to charts, get vplot/hplot/vform working again

Responsive attribute was temporarily removed

* Add a demo layout "mega plot"

This will have to be removed or made into a proper example

* Add back responsive property, clean-up adding layouts to document

Seperate out the _add_layoutable bit of adding a root to a document.
Fix-up document tests - skip those where we're still in flux

* Sort out tests after the epic overhaul

Some were just a few api tweaks. ~30 have been skipped for now while
responsive mode implementations are re-underway.

* XFail VBoxForm & Plot test

Both xfails need to be resolved before a release, as they indicate API
breaking changes.

Also factor out yields as they prevent the xfail working.

* fix-up xfail test

* color_sliders needs to be executable

* Use Column for VBoxForm not WidgetContainer

This allows backwards compatibility, but may lead to some ugly results.

* Clean-up document resize code

Only operate on layoutables, check for widths & heights. Iterate through
all roots. Fix-up embed example to show how to get plots layed out
successfully in the new system - user is responsible for setting their
own box sizes.

* Make box_layout more flexible about presence of width & height variables.

This will allow us to not use them in the case of fixed layouts.

* Increase trigger delay

Hopefully less floating toolbars - need a more robust way - trigger from
Bokeh finished loading.

* Only use the bits of bootstrap we need for bokeh core css.

* Clean-up rendering of canvas

Don't add non-dynamic attrs in bokehjs, use css.

* Implement fixed responsive mode.

Add edit variables height & width when in fixed mode and suggest them at
render time based on model.width and model.height. Small tweaks to box and plot
to work with these new settings.

Add some css to try and get fixed grids behaving as we want.

* Toolbar inherits from Model not LayoutDOM

Fix-up tests on LayoutDOM

* WIP Width responsive boxes

TODO - I've broken Fixed next to Box.

* Tweak deprecation, make plot fixed by default.

Still need to look carefully into VBoxForm deprecation.

* Fix deprecation warning when using charts due to new tools.

* Fix hover toolbar

Move bootstrap imports up to top level so we can see what we need.
Restore bootstrap dropdown that powers hovertool menu.
Tweak hover button css for new toolbar

* Tweak tests

* Fix missing crosshair tool.

Use add_renderers method on plot_canvas not plot.

plot.renderers wasn't failing because the property exists. Use the
add_renderers method which is on plot.plot_canvas.

* Plots are back to fixed by default, stop xfailing test

* Move canvas resize wait into utils, and use it on all tests.

The tests appear to be a bit flakier with the new rendering regime. Use
the canvas wait on all tests as part of the `has_no_console_errors` method.

* Add missing asserts to wheel zoom test

* Update screenhot tests

Add a screenshot marker for convenience
Update the base screenshots

* Small tweaks to range and responsive tests.

These needed a little love to make them more robust with the new layout
work. Mainly just more waiting for canvas resizes. Added the linear axis
onto the range test, otherwise they're pretty impenetrable to watch and
understand what's going wrong.

* Remove testing examples cruft

* Update responsive modes to `fixed`, `box`, `width_ar`, `height_ar`, `box_ar`

* Add re-run option to integration tests

* Fix-up widget display.

* Fix-up tests after new resposnive names

Also clean up wheel_zoom tests to use Plot not figure as part of trying
to keep them as simple and isolated as possible.

* Give widgets a little more room to breathe

* Plot test had wrong indentation

* Get fixed and width_ar layouts playing nicer in mixed-layouts.

Add tests for widget boxes

* Fix-up selenium tests

* Bug in stale element helper
* Selenium test was failing on chrome because of a favico 404 being
  picked up in our new console errors failing, so try and exclude that.

* Implement height_ar mode

* Small clean-up of layout_dom.coffee

* Implement box_ar on plot - must be wrapped in a box.

Re-enable selenium test for it

* set reponsive defaults, tweak warning message

* Add missing default for toolbar_location (how was this not picked up)

Very odd that default tests didn't pick this up.

* Add a toolbar box that can layout a toolbar anywhere.

* Update GridPlot responsive default

* Try and fix wierd rendering issue after merge

* Revert "Revert "Make hidpi work for webgl""

* Give ToolbarBox merge tools capability

- will replace the GridPlot tool manager
- move under tools as its moved away from a container to being a toolbar

* Massively simplified gridplot.

All set-up of rows & columns will be handled Python side.

* Add initial python implementation of GridPlot

There are some things wrong with this, but it gets the basic in place so
we can start playing with it.

* update custom model JS for random_tiles example

* Restore to height_width responsive functionality

* GridPlot is a function, clean-up to ToolbarBox

Cleaned-up ToolbarBox - it is a Box, and it creates a Toolbar on it.
Fix-up tests.

* ToolbarBase needs to be a LayoutDOM

BokehJS side ToolbarBox is a Box, but that's not what ToolbarBase should
be.

* Give clear to all row objects

* Add get_width function to box.coffee

* Have layout dom try harder to get a size in fixed mode

* Deprecate io.gridplot

* Add a spacer class and use it so gridplot can support None

* Remove BokehJS GridPlot

* Ensure rendering order and remove work-around for mixed layouts.

The non_root fixed constraint was an attempt to get mixed-mode layouts
working, but it causes lots of problems and this should be tackled
properly.

* Only render positions when not in fixed mode

* Return null from get_width

Forces width to not be specified

* Tweak styling so fixed grid right toolbar is correct

* Remove gridplot tests now its deprecated

* Add tests for toolbar tweaks, clean-up constraints tests

Clean-up tests after removing mixed-mode hack

* Make gridplot fixed by default

* Remove console.log statement from canvas

* Fix-up nasty merge conflict on plot tests

* vform and VBoxForm deprecate as WidgetBox

* Improve toolbar and widgets in layouts

* Fix-up annotations tests after merge

Image dimensions have changed slightly

* All default to fixed, auto-wrap widgets into widget boxes

* Update geojson points after API change

* Correct bad defaults - LayoutDOM is null, others are fixed

* Make sure widgets have enough room

* Small improvements to widgets css

* Turn None's into spacer, make spacers responsive

* CSS to get tabs rendering properly

* Reset should trigger a document resize.

* Fix gmap plot

* Add title back to Plot

* Fix responsive box_ar test

* Remove title prop from chart now we've changed up title

* use synthetic renderers for crosshair spans

* Clean-up Plot title properties

* Finish plot title deprecations, clean-up tests.

* Fix crosshair tool test after change to synthetic renderers

* Initial implementation of a smarter deprecation for VBoxForm.

* Add deprecated properties to deprecated attributes list

* Initial fix for tabs

* Correct title position (shouldn't have changed it)

And give title a little bit of breathing room - need to do this much
more nicely. But there's an open issue to look at this.

* Change "" to None title property

* Correct title property deprecation and add better warnings

* Add tests for tabs fix

* Update screenshot test to check outside all axes.

* Try and improve clarity of Responsive property transform

* Add layout convenience function and two examples

* Restore chart test for defaults
  • Loading branch information
birdsarah authored and bryevdv committed May 27, 2016
1 parent 560acd8 commit 79e0f83
Show file tree
Hide file tree
Showing 183 changed files with 5,715 additions and 2,754 deletions.
4 changes: 2 additions & 2 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -110,5 +110,5 @@ win-64
.cache/

# generated coffeescript
/bokehjs/test/test_common/defaults/models_defaults.coffee
/bokehjs/test/test_common/defaults/widgets_defaults.coffee
/bokehjs/test/common/generated_defaults/models_defaults.coffee
/bokehjs/test/common/generated_defaults/widgets_defaults.coffee
2 changes: 1 addition & 1 deletion .travis.yml
Original file line number Diff line number Diff line change
Expand Up @@ -73,7 +73,7 @@ script:
#
# Run integration tests only once (Py 3.4)
- if [[ -z "$TRAVIS_TAG" && "$GROUP" == integration && -n "${SAUCELABS}" ]]; then travis_start_sauce_connect; fi
- if [[ -z "$TRAVIS_TAG" && "$GROUP" == integration && -n "${SAUCELABS}" ]]; then py.test -m integration --driver SauceLabs --html=tests/pytest-report.html -n4 --upload; fi # Run the integration tests on saucelabs
- if [[ -z "$TRAVIS_TAG" && "$GROUP" == integration && -n "${SAUCELABS}" ]]; then py.test -m integration --driver SauceLabs --html=tests/pytest-report.html -n4 -r3 --upload; fi # Run the integration tests on saucelabs
- if [[ -z "$TRAVIS_TAG" && "$GROUP" == integration && -n "${SAUCELABS}" ]]; then travis_stop_sauce_connect; fi
- if [[ -z "$TRAVIS_TAG" && "$GROUP" == integration && -z "${SAUCELABS}" ]]; then py.test -m integration --driver Firefox; fi
#
Expand Down
6 changes: 1 addition & 5 deletions bokeh/charts/builder.py
Original file line number Diff line number Diff line change
Expand Up @@ -66,15 +66,11 @@ def create_and_build(builder_class, *data, **kws):
builder = builder_class(*data, **builder_kws)

# create a chart to return, since there isn't one already
chart_kws = { k:v for k,v in kws.items() if k not in builder_props}
chart_kws = {k: v for k, v in kws.items() if k not in builder_props}
chart = Chart(**chart_kws)
chart.add_builder(builder)
chart.start_plot()

curdoc()._current_plot = chart # TODO (havocp) store this on state, not doc?
if curstate().autoadd:
curdoc().add_root(chart)

return chart


Expand Down
25 changes: 11 additions & 14 deletions bokeh/charts/chart.py
Original file line number Diff line number Diff line change
Expand Up @@ -87,13 +87,14 @@ class Chart(Plot):
What kind of scale to use for the y-axis.
""")

title_text_font_size = Override(default={ 'value' : '14pt' })

responsive = Override(default=False)

_defaults = defaults

__deprecated_attributes__ = ('filename', 'server', 'notebook', 'width', 'height', 'xgrid', 'ygrid', 'legend')
__deprecated_attributes__ = (
'filename', 'server', 'notebook', 'width', 'height', 'xgrid', 'ygrid', 'legend'
'background_fill', 'border_fill', 'logo', 'tools',
'title_text_baseline', 'title_text_align', 'title_text_alpha', 'title_text_color',
'title_text_font_style', 'title_text_font_size', 'title_text_font', 'title_standoff'
)

_xgrid = True
_ygrid = True
Expand Down Expand Up @@ -133,10 +134,6 @@ def __init__(self, *args, **kwargs):
if k in kwargs:
setattr(self, k, kwargs[k])

# TODO (bev) have to force serialization of overriden defaults on subtypes for now
self.title_text_font_size = "10pt"
self.title_text_font_size = "14pt"

self._glyphs = []
self._built = False

Expand All @@ -147,7 +144,8 @@ def __init__(self, *args, **kwargs):
self._scales = defaultdict(list)
self._tooltips = []

self.create_tools(self._tools)
if hasattr(self, '_tools'):
self.create_tools(self._tools)

def add_renderers(self, builder, renderers):
self.renderers += renderers
Expand Down Expand Up @@ -198,7 +196,7 @@ def create_tools(self, tools):
# in case tools == False just exit
return

if len(self.tools) == 0:
if len(self.toolbar.tools) == 0:
# if no tools customization let's create the default tools
tool_objs = _process_tools_arg(self, tools)
self.add_tools(*tool_objs)
Expand All @@ -209,9 +207,8 @@ def start_plot(self):
self.create_axes()
self.create_grids(self._xgrid, self._ygrid)

# Add tools if supposed to
if self.tools:
self.create_tools(self.tools)
if self.toolbar.tools:
self.create_tools(self._tools)

if len(self._tooltips) > 0:
self.add_tools(HoverTool(tooltips=self._tooltips))
Expand Down
5 changes: 3 additions & 2 deletions bokeh/charts/tests/test_chart.py
Original file line number Diff line number Diff line change
Expand Up @@ -50,10 +50,10 @@ def setUp(self):

def test_title(self):
self.chart.title = "new_title"
self.assertEqual(self.chart.title, "new_title")
self.assertEqual(self.chart.title.text, "new_title")

def test_responsive(self):
self.assertEqual(self.chart.responsive, True)
self.assertEqual(self.chart.responsive, 'width_ar')

def check_chart_elements(self, expected_tools):
self.assertIsInstance(self.chart.left[0], LinearAxis)
Expand Down Expand Up @@ -162,6 +162,7 @@ def test_defaults():
assert c1.tools
assert c2.tools == c3.tools == []


def test_charts_theme_validation():
from bokeh.plotting import figure
p = figure()
Expand Down
9 changes: 7 additions & 2 deletions bokeh/client/session.py
Original file line number Diff line number Diff line change
Expand Up @@ -271,11 +271,14 @@ def push(self, document=None):
if self._document is None:
self._attach_document(doc)


def show(self, browser=None, new="tab"):
def show(self, obj=None, browser=None, new="tab"):
""" Open a browser displaying this session.
Args:
obj (LayoutDOM object, optional) : a Layout (Row/Column),
Plot or Widget object to display. The object will be added
to the session's document.
browser (str, optional) : browser to show with (default: None)
For systems that support it, the **browser** argument allows
specifying which browser to display in, e.g. "safari", "firefox",
Expand All @@ -288,6 +291,8 @@ def show(self, browser=None, new="tab"):
opens a new tab. If **new** is 'window', then opens a new window.
"""
if obj and obj not in self.document.roots:
self.document.add_root(obj)
show_session(session=self)

@classmethod
Expand Down
11 changes: 11 additions & 0 deletions bokeh/core/_templates/file.html
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,17 @@
<title>{{ title if title else "Bokeh Plot" }}</title>
{{ bokeh_css }}
{{ bokeh_js }}
<style>
html {
width: 100%;
height: 100%;
}
body {
width: 90%;
height: 100%;
margin: auto;
}
</style>
</head>
<body>
{{ plot_div|indent(8) }}
Expand Down
2 changes: 2 additions & 0 deletions bokeh/core/enums.py
Original file line number Diff line number Diff line change
Expand Up @@ -179,3 +179,5 @@ def enumeration(*values, **kwargs):
#: Specify sorting directions
SortDirection = enumeration("ascending", "descending")

#: Responsive types
Responsive = enumeration("box", "width_ar", "height_ar", "box_ar", "fixed")
37 changes: 35 additions & 2 deletions bokeh/core/properties.py
Original file line number Diff line number Diff line change
Expand Up @@ -420,8 +420,8 @@ def _get_default(self, obj):
return default

def _real_set(self, obj, old, value, hint=None):
# Currently as of Bokeh 0.11.1, all hinted events modify in place. However this may
# need refining later if this assumption changes.
# Currently as of Bokeh 0.11.1, all hinted events modify in place. However this may
# need refining later if this assumption changes.
unchanged = self.descriptor.matches(value, old) and (hint is None)
if unchanged:
return
Expand Down Expand Up @@ -1855,3 +1855,36 @@ def transform(self, value):
value = tuple(int(v) if i < 3 else v for i, v in enumerate(value))

return value

class Responsive(Either):

def __init__(self, default=None, help=None):
types = (Enum(enums.Responsive), Bool)
super(Responsive, self).__init__(*types, default=default, help=help)

def transform(self, value):
""" Transform True to width_ar mode and False to fixed
"""
if value is True:
responsive = 'width_ar'
elif value is False:
responsive = 'fixed'
else:
responsive = value
return responsive


class TitleProp(Either):

def __init__(self, default=None, help=None):
types = (Instance('bokeh.models.annotations.Title'), String)
super(TitleProp, self).__init__(*types, default=default, help=help)

def transform(self, value):
if isinstance(value, str):
if value == "":
return None
else:
from bokeh.models.annotations import Title
value = Title(text=value)
return value
37 changes: 33 additions & 4 deletions bokeh/core/tests/test_properties.py
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,10 @@
HasProps, NumberSpec, ColorSpec, Bool, Int, Float, Complex, String,
Regex, List, Dict, Tuple, Array, Instance, Any, Interval, Either,
Enum, Color, Align, DashPattern, Size, Percent, Angle, AngleSpec,
DistanceSpec, Override, Include, MinMaxBounds)
DistanceSpec, Override, Include, MinMaxBounds, Responsive, TitleProp)

from bokeh.models import Plot
from bokeh.models.annotations import Title

class Basictest(unittest.TestCase):

Expand Down Expand Up @@ -1445,13 +1448,39 @@ def test_MinMaxBounds_with_datetime(self):
# Invalid values
self.assertFalse(prop.is_valid((datetime.date(2012, 10, 1), 22)))


def test_HasProps_clone():
from bokeh.models import Plot
p1 = Plot(plot_width=1000)
c1 = p1.properties_with_values(include_defaults=False)
p2 = p1._clone()
c2 = p2.properties_with_values(include_defaults=False)
assert c1 == c2

if __name__ == "__main__":
unittest.main()

def test_responsive_transforms_true_into_width():
class Foo(HasProps):
responsive = Responsive
f = Foo(responsive=True)
assert f.responsive == 'width_ar'


def test_responsive_transforms_false_into_fixed():
class Foo(HasProps):
responsive = Responsive
f = Foo(responsive=False)
assert f.responsive == 'fixed'


def test_titleprop_transforms_string_into_title_object():
class Foo(HasProps):
title = TitleProp
f = Foo(title="hello")
assert isinstance(f.title, Title)
assert f.title.text == "hello"


def test_titleprop_transforms_empty_string_into_None():
class Foo(HasProps):
title = TitleProp
f = Foo(title="")
assert f.title is None
5 changes: 5 additions & 0 deletions bokeh/document.py
Original file line number Diff line number Diff line change
Expand Up @@ -401,6 +401,11 @@ def add_root(self, model):
if model in self._roots:
return
self._push_all_models_freeze()
# TODO(bird) Should we do some kind of reporting of how many LayoutDOM
# items are in the document roots. In vanilla bokeh cases e.g.
# output_file, output_server more than one LayoutDOM is probably not
# going to go well. But in embedded cases, you may well want more than
# one.
try:
self._roots.append(model)
finally:
Expand Down
Loading

0 comments on commit 79e0f83

Please sign in to comment.