launchpad-reviewers team mailing list archive
-
launchpad-reviewers team
-
Mailing list archive
-
Message #03059
[Merge] lp:~allenap/launchpad/dd-initseries-bug-727105-copy-options into lp:launchpad
Gavin Panella has proposed merging lp:~allenap/launchpad/dd-initseries-bug-727105-copy-options into lp:launchpad with lp:~allenap/launchpad/dd-initseries-bug-727105-packageset-picker as a prerequisite.
Requested reviews:
Launchpad code reviewers (launchpad-reviewers)
Related bugs:
Bug #740349 in Launchpad itself: "+initseries: package copy options"
https://bugs.launchpad.net/launchpad/+bug/740349
For more details, see:
https://code.launchpad.net/~allenap/launchpad/dd-initseries-bug-727105-copy-options/+merge/54667
- Changed CheckBoxListWidget so that it can show radio-buttons
too. Renamed it to ChoiceListWidget.
- Add "choice" properties to ChoiceListWidget and SelectWidget to both
get and set the currently selected option/options.
- Add a SelectWidget.autoSize() method to choose a size for the select
control based on the number of choices, up to an optional maximum
size.
- After the packageSets field on PackagesetPickerWidget is set,
autoSize() is called.
- Adds a "copy options" widget to the page that uses ChoiceListWidget
in its radio-button mode.
--
https://code.launchpad.net/~allenap/launchpad/dd-initseries-bug-727105-copy-options/+merge/54667
Your team Launchpad code reviewers is requested to review the proposed merge of lp:~allenap/launchpad/dd-initseries-bug-727105-copy-options into lp:launchpad.
=== modified file 'lib/lp/registry/javascript/distroseries.js'
--- lib/lp/registry/javascript/distroseries.js 2011-03-24 09:48:33 +0000
+++ lib/lp/registry/javascript/distroseries.js 2011-03-24 09:48:35 +0000
@@ -133,15 +133,15 @@
* A form row matching that which LaunchpadForm presents, containing a
* list of checkboxes, and an optional label and description.
*
- * @class CheckBoxListWidget
+ * @class ChoiceListWidget
*/
-var CheckBoxListWidget = function() {
- CheckBoxListWidget.superclass.constructor.apply(this, arguments);
+var ChoiceListWidget = function() {
+ ChoiceListWidget.superclass.constructor.apply(this, arguments);
};
-Y.mix(CheckBoxListWidget, {
+Y.mix(ChoiceListWidget, {
- NAME: 'checkBoxListWidget',
+ NAME: 'choiceListWidget',
ATTRS: {
@@ -157,13 +157,14 @@
setter: function(value, name) {
var choices = Y.Array.unique(value).sort();
var field_name = this.get("name");
+ var field_type = this.get("type");
var list = Y.Node.create("<ul />");
choices.forEach(
function(choice) {
var item = Y.Node.create(
"<li><input /> <label /></li>");
item.one("input")
- .set("type", "checkbox")
+ .set("type", field_type)
.set("name", field_name)
.set("value", choice);
item.one("label")
@@ -176,30 +177,84 @@
);
this.fieldNode.empty().append(list);
}
+ },
+
+ /**
+ * The current selection.
+ *
+ * @property choice
+ */
+ choice: {
+ setter: function(value, name) {
+ if (!Y.Lang.isArray(value)) {
+ value = [value];
+ }
+ this.fieldNode.all("li > input").each(
+ function(node) {
+ node.set(
+ "checked",
+ value.indexOf(node.get("value")) >= 0);
+ }
+ );
+ },
+ getter: function() {
+ var choice = [];
+ this.fieldNode.all("li > input").each(
+ function(node) {
+ if (node.get("checked")) {
+ choice.push(node.get("value"));
+ }
+ }
+ );
+ if (this.get("type") == "radio") {
+ if (choice.length === 0) {
+ choice = null;
+ }
+ else if (choice.length == 1) {
+ choice = choice[0];
+ }
+ else {
+ choice = undefined;
+ }
+ }
+ return choice;
+ }
+ },
+
+ /**
+ * The input type to display. Choose from "checkbox" or "radio".
+ *
+ * @property type
+ */
+ type: {
+ value: "checkbox",
+ setter: function(value, name) {
+ this.fieldNode.all("li > input").set("type", value);
+ }
}
}
});
-Y.extend(CheckBoxListWidget, FormRowWidget);
+Y.extend(ChoiceListWidget, FormRowWidget);
-namespace.CheckBoxListWidget = CheckBoxListWidget;
+namespace.ChoiceListWidget = ChoiceListWidget;
/**
- * A special form of CheckBoxListWidget for choosing architecture tags.
+ * A special form of ChoiceListWidget for choosing architecture tags.
*
- * @class ArchitecturesCheckBoxListWidget
+ * @class ArchitecturesChoiceListWidget
*/
-var ArchitecturesCheckBoxListWidget = function() {
- ArchitecturesCheckBoxListWidget
+var ArchitecturesChoiceListWidget = function() {
+ ArchitecturesChoiceListWidget
.superclass.constructor.apply(this, arguments);
};
-Y.mix(ArchitecturesCheckBoxListWidget, {
+Y.mix(ArchitecturesChoiceListWidget, {
- NAME: 'architecturesCheckBoxListWidget',
+ NAME: 'architecturesChoiceListWidget',
ATTRS: {
@@ -250,7 +305,7 @@
});
-Y.extend(ArchitecturesCheckBoxListWidget, CheckBoxListWidget, {
+Y.extend(ArchitecturesChoiceListWidget, ChoiceListWidget, {
initializer: function(config) {
this.client = new Y.lp.client.Launchpad();
@@ -261,7 +316,7 @@
});
-namespace.ArchitecturesCheckBoxListWidget = ArchitecturesCheckBoxListWidget;
+namespace.ArchitecturesChoiceListWidget = ArchitecturesChoiceListWidget;
/**
@@ -330,6 +385,37 @@
},
/**
+ * The current selection.
+ *
+ * @property choice
+ */
+ choice: {
+ setter: function(value, name) {
+ if (!Y.Lang.isArray(value)) {
+ value = [value];
+ }
+ this.fieldNode.all("select > option").each(
+ function(node) {
+ node.set(
+ "selected",
+ value.indexOf(node.get("value")) >= 0);
+ }
+ );
+ },
+ getter: function() {
+ var choice = [];
+ this.fieldNode.all("select > option").each(
+ function(node) {
+ if (node.get("selected")) {
+ choice.push(node.get("value"));
+ }
+ }
+ );
+ return choice;
+ }
+ },
+
+ /**
* The number of rows to show in the select widget.
*
* @property size
@@ -349,15 +435,9 @@
multiple: {
value: false,
setter: function(value, name) {
- var select = this.fieldNode.all("select");
- if (value) {
- select.setAttribute("multiple", "multiple");
- return true;
- }
- else {
- select.removeAttribute("multiple");
- return false;
- }
+ value = value ? true : false;
+ this.fieldNode.all("select").set("multiple", value);
+ return value;
}
}
@@ -365,7 +445,32 @@
});
-Y.extend(SelectWidget, FormRowWidget);
+Y.extend(SelectWidget, FormRowWidget, {
+
+ /**
+ * Choose a size for the select control based on the number of
+ * choices, up to an optional maximum size.
+ *
+ * @method autoSize
+ */
+ autoSize: function(maxSize) {
+ var choiceCount = this.fieldNode.all("select > option").size();
+ if (choiceCount === 0) {
+ this.set("size", 1);
+ }
+ else if (maxSize === undefined) {
+ this.set("size", choiceCount);
+ }
+ else if (choiceCount < maxSize) {
+ this.set("size", choiceCount);
+ }
+ else {
+ this.set("size", maxSize);
+ }
+ return this;
+ }
+
+});
namespace.SelectWidget = SelectWidget;
@@ -438,6 +543,9 @@
"text",
"The chosen series has no package sets!"));
}
+ else {
+ this.autoSize(10);
+ }
Y.lazr.anim.green_flash({node: this.fieldNode}).run();
}
}
@@ -468,7 +576,7 @@
namespace.setup = function() {
var form_container = Y.one("#init-series-form-container");
var form_table_body = form_container.one("table.form > tbody");
- var architecture_choice = new ArchitecturesCheckBoxListWidget()
+ var architecture_choice = new ArchitecturesChoiceListWidget()
.set("name", "field.architectures")
.set("label", "Architectures")
.set("description", (
@@ -484,15 +592,26 @@
"The package sets that will be imported " +
"into the derived distroseries."))
.render(form_table_body);
- var field_derived_from_series =
+ var package_copy_options = new ChoiceListWidget()
+ .set("name", "field.package_copy_options")
+ .set("type", "radio")
+ .set("label", "Copy options")
+ .set("description", (
+ "Choose whether to rebuild all the sources you copy " +
+ "from the parent, or to copy their binaries too."))
+ .set("choices", ["Copy Source and Rebuild",
+ "Copy Source and Binaries"])
+ .set("choice", "Copy Source and Binaries")
+ .render(form_table_body);
+ var derive_from_choice =
form_table_body.one("[name=field.derived_from_series]");
// Wire up the distroseries select to the architectures widget.
function update_architecture_choice() {
architecture_choice
- .set("distroSeries", field_derived_from_series.get("value"));
+ .set("distroSeries", derive_from_choice.get("value"));
}
- field_derived_from_series.on("change", update_architecture_choice);
+ derive_from_choice.on("change", update_architecture_choice);
// Update the architectures widget for the selected distroseries.
update_architecture_choice();
@@ -500,9 +619,9 @@
// Wire up the distroseries select to the packagesets widget.
function update_packageset_choice() {
packageset_choice
- .set("distroSeries", field_derived_from_series.get("value"));
+ .set("distroSeries", derive_from_choice.get("value"));
}
- field_derived_from_series.on("change", update_packageset_choice);
+ derive_from_choice.on("change", update_packageset_choice);
// Update the packagesets widget for the selected distroseries.
update_packageset_choice();
=== modified file 'lib/lp/registry/javascript/tests/test_distroseries.js'
--- lib/lp/registry/javascript/tests/test_distroseries.js 2011-03-24 09:48:33 +0000
+++ lib/lp/registry/javascript/tests/test_distroseries.js 2011-03-24 09:48:35 +0000
@@ -123,12 +123,12 @@
suite.add(new Y.Test.Case(testFormRowWidget));
- var testCheckBoxListWidget = {
- name: 'TestCheckBoxListWidget',
+ var testChoiceListWidget = {
+ name: 'TestChoiceListWidget',
setUp: function() {
this.container = Y.Node.create("<div />");
- this.widget = new initseries.CheckBoxListWidget();
+ this.widget = new initseries.ChoiceListWidget();
},
tearDown: function() {
@@ -144,6 +144,9 @@
ArrayAssert.itemsAreEqual(
["a", "b"],
this.container.all("li > label").get("text"));
+ ArrayAssert.itemsAreEqual(
+ ["checkbox", "checkbox"],
+ this.container.all("li > input").getAttribute("type"));
},
testRenderChoicesChange: function() {
@@ -156,20 +159,90 @@
ArrayAssert.itemsAreEqual(
["c", "d", "e"],
this.container.all("li > label").get("text"));
+ },
+
+ testRenderChoicesChangeType: function() {
+ this.widget.set("choices", ["a", "b"]);
+ this.widget.render(this.container);
+ this.widget.set("type", "radio");
+ ArrayAssert.itemsAreEqual(
+ ["radio", "radio"],
+ this.container.all("li > input").getAttribute("type"));
+
+ },
+
+ testChoiceWithCheckBox: function() {
+ this.widget
+ .set("type", "checkbox")
+ .set("choices", ["a", "b"]);
+ ArrayAssert.itemsAreEqual(
+ [], this.widget.get("choice"));
+ this.widget.fieldNode.one("input[value=a]")
+ .set("checked", "checked");
+ ArrayAssert.itemsAreEqual(
+ ["a"], this.widget.get("choice"));
+ },
+
+ testChoiceWithRadio: function() {
+ this.widget
+ .set("type", "radio")
+ .set("choices", ["a", "b"]);
+ Assert.isNull(this.widget.get("choice"));
+ this.widget.fieldNode.one("input[value=a]")
+ .set("checked", "checked");
+ Assert.areEqual("a", this.widget.get("choice"));
+ /* When both radio buttons are checked (should that be
+ possible?), choice is undefined. */
+ this.widget.fieldNode.one("input[value=b]")
+ .set("checked", "checked");
+ Assert.isUndefined(this.widget.get("choice"));
+ },
+
+ testSetChoiceWithCheckBox: function() {
+ this.widget
+ .set("type", "checkbox")
+ .set("choices", ["a", "b"])
+ .set("choice", "a");
+ ArrayAssert.itemsAreEqual(
+ ["a"], this.widget.get("choice"));
+ this.widget.set("choice", ["a"]);
+ ArrayAssert.itemsAreEqual(
+ ["a"], this.widget.get("choice"));
+ this.widget.set("choice", ["a", "b"]);
+ ArrayAssert.itemsAreEqual(
+ ["a", "b"], this.widget.get("choice"));
+ this.widget.set("choice", ["b", "c"]);
+ ArrayAssert.itemsAreEqual(
+ ["b"], this.widget.get("choice"));
+ },
+
+ testSetChoiceWithRadio: function() {
+ this.widget
+ .set("type", "radio")
+ .set("choices", ["a", "b"])
+ .set("choice", "a");
+ ArrayAssert.itemsAreEqual(
+ "a", this.widget.get("choice"));
+ this.widget.set("choice", ["a"]);
+ ArrayAssert.itemsAreEqual(
+ "a", this.widget.get("choice"));
+ this.widget.set("choice", "b");
+ ArrayAssert.itemsAreEqual(
+ "b", this.widget.get("choice"));
}
};
- testCheckBoxListWidget = Y.merge(
- testFormRowWidget, testCheckBoxListWidget);
- suite.add(new Y.Test.Case(testCheckBoxListWidget));
+ testChoiceListWidget = Y.merge(
+ testFormRowWidget, testChoiceListWidget);
+ suite.add(new Y.Test.Case(testChoiceListWidget));
- var testArchitecturesCheckBoxListWidget = {
- name: 'TestArchitecturesCheckBoxListWidget',
+ var testArchitecturesChoiceListWidget = {
+ name: 'TestArchitecturesChoiceListWidget',
setUp: function() {
this.container = Y.Node.create("<div />");
- this.widget = new initseries.ArchitecturesCheckBoxListWidget();
+ this.widget = new initseries.ArchitecturesChoiceListWidget();
},
tearDown: function() {
@@ -262,9 +335,9 @@
};
- testArchitecturesCheckBoxListWidget = Y.merge(
- testCheckBoxListWidget, testArchitecturesCheckBoxListWidget);
- suite.add(new Y.Test.Case(testArchitecturesCheckBoxListWidget));
+ testArchitecturesChoiceListWidget = Y.merge(
+ testChoiceListWidget, testArchitecturesChoiceListWidget);
+ suite.add(new Y.Test.Case(testArchitecturesChoiceListWidget));
var testSelectWidget = {
name: 'TestSelectWidget',
@@ -358,6 +431,54 @@
this.container.all("select > option").get("text"));
},
+ testChoice: function() {
+ var choices = [
+ {value: "a", text: "A", data: 123},
+ {value: "b", text: "B", data: 456},
+ {value: "c", text: "C", data: 789}
+ ];
+ this.widget
+ .set("choices", choices)
+ .set("multiple", true);
+ /* It would be better to deselect all options by default,
+ but this appears impossible; the browser seems to
+ select the first option when rendering. */
+ this.widget.fieldNode.all("option").set("selected", false);
+ ArrayAssert.itemsAreEqual(
+ [], this.widget.get("choice"));
+ this.widget.fieldNode.one("option[value=a]")
+ .set("selected", true);
+ ArrayAssert.itemsAreEqual(
+ ["a"], this.widget.get("choice"));
+ this.widget.fieldNode.one("option[value=c]")
+ .set("selected", true);
+ ArrayAssert.itemsAreEqual(
+ ["a", "c"], this.widget.get("choice"));
+ },
+
+ testSetChoice: function() {
+ var choices = [
+ {value: "a", text: "A", data: 123},
+ {value: "b", text: "B", data: 456},
+ {value: "c", text: "C", data: 789}
+ ];
+ this.widget
+ .set("multiple", true)
+ .set("choices", choices)
+ .set("choice", "a");
+ ArrayAssert.itemsAreEqual(
+ ["a"], this.widget.get("choice"));
+ this.widget.set("choice", ["a"]);
+ ArrayAssert.itemsAreEqual(
+ ["a"], this.widget.get("choice"));
+ this.widget.set("choice", ["a", "b"]);
+ ArrayAssert.itemsAreEqual(
+ ["a", "b"], this.widget.get("choice"));
+ this.widget.set("choice", ["b", "z"]);
+ ArrayAssert.itemsAreEqual(
+ ["b"], this.widget.get("choice"));
+ },
+
testSize: function() {
Assert.areEqual(1, this.widget.get("size"));
},
@@ -391,6 +512,46 @@
5, this.widget.fieldNode.one("select").get("size"));
},
+ testAutoSize: function() {
+ var choices = [
+ {value: "a", text: "A", data: 123},
+ {value: "b", text: "B", data: 456},
+ {value: "c", text: "C", data: 789}
+ ];
+ this.widget.set("choices", choices);
+ /* Without argument, autoSize() sets the size to the same
+ as the number of choices. */
+ this.widget.autoSize();
+ Assert.areEqual(3, this.widget.get("size"));
+ },
+
+ testAutoSizeMoreChoicesThanMaxiumum: function() {
+ var choices = [
+ {value: "a", text: "A", data: 123},
+ {value: "b", text: "B", data: 456},
+ {value: "c", text: "C", data: 789}
+ ];
+ this.widget.set("choices", choices);
+ /* autoSize() sets the size to the same as the number of
+ choices unless there are more than the specified
+ maximum. */
+ this.widget.autoSize(2);
+ Assert.areEqual(2, this.widget.get("size"));
+ },
+
+ testAutoSizeFewerChoicesThanMaxiumum: function() {
+ var choices = [
+ {value: "a", text: "A", data: 123},
+ {value: "b", text: "B", data: 456},
+ {value: "c", text: "C", data: 789}
+ ];
+ this.widget.set("choices", choices);
+ /* autoSize() sets the size to the same as the number of
+ choices. */
+ this.widget.autoSize(5);
+ Assert.areEqual(3, this.widget.get("size"));
+ },
+
testMultiple: function() {
Assert.areEqual(false, this.widget.get("multiple"));
},
@@ -453,15 +614,28 @@
var package_sets_collection =
new Y.lp.client.Collection(
null, {entries: package_sets}, null);
- this.widget.set(
- "packageSets",
- package_sets_collection);
+ this.widget.set("packageSets", package_sets_collection);
var choices = this.widget.get("choices");
ArrayAssert.itemsAreEqual(
["foo", "bar", "baz"],
attrselect("value")(choices));
},
+ testSetPackageSetsCallsAutoSize: function() {
+ var package_sets = [
+ {name: "foo", description: "Foo"},
+ {name: "bar", description: "Bar"},
+ {name: "baz", description: "Baz"}
+ ];
+ var package_sets_collection =
+ new Y.lp.client.Collection(
+ null, {entries: package_sets}, null);
+ var autoSized = false;
+ this.widget.autoSize = function() { autoSized = true; };
+ this.widget.set("packageSets", package_sets_collection);
+ Assert.isTrue(autoSized);
+ },
+
testSetDistroSeriesInitiatesIO: function() {
var io = false;
this.widget.client = {