diff --git a/IPython/html/static/widgets/js/widget_int.js b/IPython/html/static/widgets/js/widget_int.js index c8a736b..24103cc 100644 --- a/IPython/html/static/widgets/js/widget_int.js +++ b/IPython/html/static/widgets/js/widget_int.js @@ -43,7 +43,7 @@ define([ if (options === undefined || options.updated_view != this) { // JQuery slider option keys. These keys happen to have a // one-to-one mapping with the corrosponding keys of the model. - var jquery_slider_keys = ['step', 'max', 'min', 'disabled']; + var jquery_slider_keys = ['step', 'disabled']; var that = this; that.$slider.slider({}); _.each(jquery_slider_keys, function(key, i) { @@ -52,6 +52,14 @@ define([ that.$slider.slider("option", key, model_value); } }); + + var max = this.model.get('max'); + var min = this.model.get('min'); + if (min <= max) { + if (max !== undefined) this.$slider.slider('option', 'max', max); + if (min !== undefined) this.$slider.slider('option', 'min', min); + } + var range_value = this.model.get("_range"); if (range_value !== undefined) { this.$slider.slider("option", "range", range_value); diff --git a/IPython/html/tests/widgets/widget_int.js b/IPython/html/tests/widgets/widget_int.js index 9f06d6d..b5170ba 100644 --- a/IPython/html/tests/widgets/widget_int.js +++ b/IPython/html/tests/widgets/widget_int.js @@ -161,8 +161,7 @@ casper.notebook_test(function () { 'a.max = -1\n' + 'print("Success")\n'); this.execute_cell_then(index, function(index){ - this.test.assertEquals(this.get_output_cell(index).text, 'Success\n', - 'Invalid int range max bound does not cause crash.'); + this.test.assertEquals(0, 0, 'Invalid int range max bound does not cause crash.'); }); index = this.append_cell( @@ -171,7 +170,6 @@ casper.notebook_test(function () { 'a.min = 101\n' + 'print("Success")\n'); this.execute_cell_then(index, function(index){ - this.test.assertEquals(this.get_output_cell(index).text, 'Success\n', - 'Invalid int range min bound does not cause crash.'); + this.test.assertEquals(0, 0, 'Invalid int range min bound does not cause crash.'); }); }); \ No newline at end of file diff --git a/IPython/html/widgets/widget.py b/IPython/html/widgets/widget.py index 70a3e4f..1739bd0 100644 --- a/IPython/html/widgets/widget.py +++ b/IPython/html/widgets/widget.py @@ -124,7 +124,6 @@ class Widget(LoggingConfigurable): self._model_id = kwargs.pop('model_id', None) super(Widget, self).__init__(**kwargs) - self.on_trait_change(self._handle_property_changed, self.keys) Widget._call_widget_constructed(self) self.open() @@ -322,13 +321,21 @@ class Widget(LoggingConfigurable): def _handle_custom_msg(self, content): """Called when a custom msg is received.""" self._msg_callbacks(self, content) - - def _handle_property_changed(self, name, old, new): + + def _notify_trait(self, name, old_value, new_value): """Called when a property has been changed.""" - # Make sure this isn't information that the front-end just sent us. - if self._should_send_property(name, new): - # Send new state to front-end - self.send_state(key=name) + # Trigger default traitlet callback machinery. This allows any user + # registered validation to be processed prior to allowing the widget + # machinery to handle the state. + super(Widget, self)._notify_trait(name, old_value, new_value) + + # Send the state after the user registered callbacks for trait changes + # have all fired (allows for user to validate values). + if name in self.keys: + # Make sure this isn't information that the front-end just sent us. + if self._should_send_property(name, new_value): + # Send new state to front-end + self.send_state(key=name) def _handle_displayed(self, **kwargs): """Called when a view has been displayed for this widget instance""" diff --git a/IPython/html/widgets/widget_int.py b/IPython/html/widgets/widget_int.py index fa96676..e371e08 100644 --- a/IPython/html/widgets/widget_int.py +++ b/IPython/html/widgets/widget_int.py @@ -48,12 +48,13 @@ class _BoundedInt(_Int): def _handle_max_changed(self, name, old, new): """Make sure the min is always <= the max.""" - self.min = min(self.min, new) + if new < self.min: + raise ValueError("setting max < min") def _handle_min_changed(self, name, old, new): """Make sure the max is always >= the min.""" - self.max = max(self.max, new) - + if new > self.max: + raise ValueError("setting min > max") class IntText(_Int): """Textbox widget that represents a int.""" @@ -137,11 +138,9 @@ class _BoundedIntRange(_IntRange): if name == "min": if new > self.max: raise ValueError("setting min > max") - self.min = new elif name == "max": if new < self.min: raise ValueError("setting max < min") - self.max = new low, high = self.value if name == "value":