From f24bc0fb1f63e7f5e38014d7191a4fe69d4179f0 Mon Sep 17 00:00:00 2001 From: Scott González Date: Thu, 15 Jul 2010 10:26:00 -0400 Subject: Widget: Deep extend options when creating a new plugin. Fixes #5830 - Widget: Using inheritance overwrites the base classes options. --- tests/unit/widget/widget.html | 3 +- tests/unit/widget/widget.js | 168 ------------------------------------ tests/unit/widget/widget_core.js | 168 ++++++++++++++++++++++++++++++++++++ tests/unit/widget/widget_tickets.js | 46 ++++++++++ ui/jquery.ui.widget.js | 2 +- 5 files changed, 217 insertions(+), 170 deletions(-) delete mode 100644 tests/unit/widget/widget.js create mode 100644 tests/unit/widget/widget_core.js create mode 100644 tests/unit/widget/widget_tickets.js diff --git a/tests/unit/widget/widget.html b/tests/unit/widget/widget.html index c3c3b0875..9e02880c4 100644 --- a/tests/unit/widget/widget.html +++ b/tests/unit/widget/widget.html @@ -13,7 +13,8 @@ - + + diff --git a/tests/unit/widget/widget.js b/tests/unit/widget/widget.js deleted file mode 100644 index 3c870f1fe..000000000 --- a/tests/unit/widget/widget.js +++ /dev/null @@ -1,168 +0,0 @@ -/* - * widget unit tests - */ -(function($) { - -module('widget factory', { - teardown: function() { - delete $.ui.testWidget; - } -}); - -test('widget creation', function() { - var myPrototype = { - _create: function() {}, - creationTest: function() {} - }; - - $.widget('ui.testWidget', myPrototype); - ok($.isFunction($.ui.testWidget), 'constructor was created'); - equals('object', typeof $.ui.testWidget.prototype, 'prototype was created'); - equals($.ui.testWidget.prototype._create, myPrototype._create, 'create function is copied over'); - equals($.ui.testWidget.prototype.creationTest, myPrototype.creationTest, 'random function is copied over'); - equals($.ui.testWidget.prototype.option, $.Widget.prototype.option, 'option method copied over from base widget'); -}); - -test('jQuery usage', function() { - expect(10); - - var shouldInit = false; - - $.widget('ui.testWidget', { - getterSetterVal: 5, - _create: function() { - ok(shouldInit, 'init called on instantiation'); - }, - methodWithParams: function(param1, param2) { - ok(true, 'method called via .pluginName(methodName)'); - equals(param1, 'value1', 'parameter passed via .pluginName(methodName, param)'); - equals(param2, 'value2', 'multiple parameter passed via .pluginName(methodName, param, param)'); - - return this; - }, - getterSetterMethod: function(val) { - if (val) { - this.getterSetterVal = val; - } else { - return this.getterSetterVal; - } - } - }); - - shouldInit = true; - var elem = $('
').testWidget(); - shouldInit = false; - - var instance = elem.data('testWidget'); - equals(typeof instance, 'object', 'instance stored in .data(pluginName)'); - equals(instance.element[0], elem[0], 'element stored on widget'); - var ret = elem.testWidget('methodWithParams', 'value1', 'value2'); - equals(ret, elem, 'jQuery object returned from method call'); - - ret = elem.testWidget('getterSetterMethod'); - equals(ret, 5, 'getter/setter can act as getter'); - ret = elem.testWidget('getterSetterMethod', 30); - equals(ret, elem, 'getter/setter method can be chainable'); - equals(instance.getterSetterVal, 30, 'getter/setter can act as setter'); -}); - -test('direct usage', function() { - expect(9); - - var shouldInit = false; - - $.widget('ui.testWidget', { - getterSetterVal: 5, - _create: function() { - ok(shouldInit, 'init called on instantiation'); - }, - methodWithParams: function(param1, param2) { - ok(true, 'method called via .pluginName(methodName)'); - equals(param1, 'value1', 'parameter passed via .pluginName(methodName, param)'); - equals(param2, 'value2', 'multiple parameter passed via .pluginName(methodName, param, param)'); - - return this; - }, - getterSetterMethod: function(val) { - if (val) { - this.getterSetterVal = val; - } else { - return this.getterSetterVal; - } - } - }); - - var elem = $('
')[0]; - - shouldInit = true; - var instance = new $.ui.testWidget({}, elem); - shouldInit = false; - - equals($(elem).data('testWidget'), instance, 'instance stored in .data(pluginName)'); - equals(instance.element[0], elem, 'element stored on widget'); - - var ret = instance.methodWithParams('value1', 'value2'); - equals(ret, instance, 'plugin returned from method call'); - - ret = instance.getterSetterMethod(); - equals(ret, 5, 'getter/setter can act as getter'); - instance.getterSetterMethod(30); - equals(instance.getterSetterVal, 30, 'getter/setter can act as setter'); -}); - -test('merge multiple option arguments', function() { - expect(1); - $.widget("ui.testWidget", { - _create: function() { - same(this.options, { - disabled: false, - option1: "value1", - option2: "value2", - option3: "value3", - option4: { - option4a: "valuea", - option4b: "valueb" - } - }); - } - }); - $("
").testWidget({ - option1: "valuex", - option2: "valuex", - option3: "value3", - option4: { - option4a: "valuex" - } - }, { - option1: "value1", - option2: "value2", - option4: { - option4b: "valueb" - } - }, { - option4: { - option4a: "valuea" - } - }); -}); - -test(".widget() - base", function() { - $.widget("ui.testWidget", { - _create: function() {} - }); - var div = $("
").testWidget() - same(div[0], div.testWidget("widget")[0]); -}); - -test(".widget() - overriden", function() { - var wrapper = $("
"); - $.widget("ui.testWidget", { - _create: function() {}, - widget: function() { - return wrapper; - } - }); - same(wrapper[0], $("
").testWidget().testWidget("widget")[0]); -}); - -})(jQuery); diff --git a/tests/unit/widget/widget_core.js b/tests/unit/widget/widget_core.js new file mode 100644 index 000000000..3c870f1fe --- /dev/null +++ b/tests/unit/widget/widget_core.js @@ -0,0 +1,168 @@ +/* + * widget unit tests + */ +(function($) { + +module('widget factory', { + teardown: function() { + delete $.ui.testWidget; + } +}); + +test('widget creation', function() { + var myPrototype = { + _create: function() {}, + creationTest: function() {} + }; + + $.widget('ui.testWidget', myPrototype); + ok($.isFunction($.ui.testWidget), 'constructor was created'); + equals('object', typeof $.ui.testWidget.prototype, 'prototype was created'); + equals($.ui.testWidget.prototype._create, myPrototype._create, 'create function is copied over'); + equals($.ui.testWidget.prototype.creationTest, myPrototype.creationTest, 'random function is copied over'); + equals($.ui.testWidget.prototype.option, $.Widget.prototype.option, 'option method copied over from base widget'); +}); + +test('jQuery usage', function() { + expect(10); + + var shouldInit = false; + + $.widget('ui.testWidget', { + getterSetterVal: 5, + _create: function() { + ok(shouldInit, 'init called on instantiation'); + }, + methodWithParams: function(param1, param2) { + ok(true, 'method called via .pluginName(methodName)'); + equals(param1, 'value1', 'parameter passed via .pluginName(methodName, param)'); + equals(param2, 'value2', 'multiple parameter passed via .pluginName(methodName, param, param)'); + + return this; + }, + getterSetterMethod: function(val) { + if (val) { + this.getterSetterVal = val; + } else { + return this.getterSetterVal; + } + } + }); + + shouldInit = true; + var elem = $('
').testWidget(); + shouldInit = false; + + var instance = elem.data('testWidget'); + equals(typeof instance, 'object', 'instance stored in .data(pluginName)'); + equals(instance.element[0], elem[0], 'element stored on widget'); + var ret = elem.testWidget('methodWithParams', 'value1', 'value2'); + equals(ret, elem, 'jQuery object returned from method call'); + + ret = elem.testWidget('getterSetterMethod'); + equals(ret, 5, 'getter/setter can act as getter'); + ret = elem.testWidget('getterSetterMethod', 30); + equals(ret, elem, 'getter/setter method can be chainable'); + equals(instance.getterSetterVal, 30, 'getter/setter can act as setter'); +}); + +test('direct usage', function() { + expect(9); + + var shouldInit = false; + + $.widget('ui.testWidget', { + getterSetterVal: 5, + _create: function() { + ok(shouldInit, 'init called on instantiation'); + }, + methodWithParams: function(param1, param2) { + ok(true, 'method called via .pluginName(methodName)'); + equals(param1, 'value1', 'parameter passed via .pluginName(methodName, param)'); + equals(param2, 'value2', 'multiple parameter passed via .pluginName(methodName, param, param)'); + + return this; + }, + getterSetterMethod: function(val) { + if (val) { + this.getterSetterVal = val; + } else { + return this.getterSetterVal; + } + } + }); + + var elem = $('
')[0]; + + shouldInit = true; + var instance = new $.ui.testWidget({}, elem); + shouldInit = false; + + equals($(elem).data('testWidget'), instance, 'instance stored in .data(pluginName)'); + equals(instance.element[0], elem, 'element stored on widget'); + + var ret = instance.methodWithParams('value1', 'value2'); + equals(ret, instance, 'plugin returned from method call'); + + ret = instance.getterSetterMethod(); + equals(ret, 5, 'getter/setter can act as getter'); + instance.getterSetterMethod(30); + equals(instance.getterSetterVal, 30, 'getter/setter can act as setter'); +}); + +test('merge multiple option arguments', function() { + expect(1); + $.widget("ui.testWidget", { + _create: function() { + same(this.options, { + disabled: false, + option1: "value1", + option2: "value2", + option3: "value3", + option4: { + option4a: "valuea", + option4b: "valueb" + } + }); + } + }); + $("
").testWidget({ + option1: "valuex", + option2: "valuex", + option3: "value3", + option4: { + option4a: "valuex" + } + }, { + option1: "value1", + option2: "value2", + option4: { + option4b: "valueb" + } + }, { + option4: { + option4a: "valuea" + } + }); +}); + +test(".widget() - base", function() { + $.widget("ui.testWidget", { + _create: function() {} + }); + var div = $("
").testWidget() + same(div[0], div.testWidget("widget")[0]); +}); + +test(".widget() - overriden", function() { + var wrapper = $("
"); + $.widget("ui.testWidget", { + _create: function() {}, + widget: function() { + return wrapper; + } + }); + same(wrapper[0], $("
").testWidget().testWidget("widget")[0]); +}); + +})(jQuery); diff --git a/tests/unit/widget/widget_tickets.js b/tests/unit/widget/widget_tickets.js new file mode 100644 index 000000000..47303dc9c --- /dev/null +++ b/tests/unit/widget/widget_tickets.js @@ -0,0 +1,46 @@ +/* + * widget unit tests + */ +(function($) { + +module('widget: tickets'); + +test('#5830 - Widget: Using inheritance overwrites the base classes options', function() { + $.widget( "ui.testWidgetBase", { + options: { + obj: { + key1: "foo", + key2: "bar" + }, + arr: [ "testing" ] + } + }); + + $.widget( "ui.testWidgetExtension", $.ui.testWidgetBase, { + options: { + obj: { + key1: "baz" + }, + arr: [ "alpha", "beta" ] + } + }); + + same( $.ui.testWidgetBase.prototype.options.obj, { + key1: "foo", + key2: "bar" + }, "base class option object not overridden"); + same( $.ui.testWidgetBase.prototype.options.arr, [ "testing" ], + "base class option array not overridden"); + + same( $.ui.testWidgetExtension.prototype.options.obj, { + key1: "baz", + key2: "bar" + }, "extension class option object extends base"); + same( $.ui.testWidgetExtension.prototype.options.arr, [ "alpha", "beta" ], + "extension class option array overwrites base"); + + delete $.ui.testWidgetBase; + delete $.ui.testWidgetExtension; +}); + +})(jQuery); diff --git a/ui/jquery.ui.widget.js b/ui/jquery.ui.widget.js index 1014c607a..265489866 100644 --- a/ui/jquery.ui.widget.js +++ b/ui/jquery.ui.widget.js @@ -57,7 +57,7 @@ $.widget = function( name, base, prototype ) { // basePrototype[ key ] = $.extend( {}, val ); // } // }); - basePrototype.options = $.extend( {}, basePrototype.options ); + basePrototype.options = $.extend( true, {}, basePrototype.options ); $[ namespace ][ name ].prototype = $.extend( true, basePrototype, { namespace: namespace, widgetName: name, -- cgit v1.2.3