Bug 960354 - Various fixes for multi-select dropdowns plus tests. r=mbrubeck

This commit is contained in:
Jim Mathies 2014-01-16 15:48:38 -06:00
parent 08cbae02b2
commit 220404e591
5 changed files with 133 additions and 28 deletions

View File

@ -28,19 +28,20 @@ var SelectHelperUI = {
},
show: function selectHelperShow(aList, aTitle, aRect) {
if (this._list)
if (this._list) {
this.reset();
}
this._list = aList;
// The element label is used as a title to give more context
this._container.setAttribute("multiple", aList.multiple ? "true" : "false");
this._listbox.setAttribute("seltype", aList.multiple ? "multiple" : "single");
let firstSelected = null;
// Using a fragment prevent us to hang on huge list
let fragment = document.createDocumentFragment();
let choices = aList.choices;
let selectedItems = [];
for (let i = 0; i < choices.length; i++) {
let choice = choices[i];
let item = document.createElement("richlistitem");
@ -51,12 +52,10 @@ var SelectHelperUI = {
item.setAttribute("crop", "center");
label.setAttribute("value", choice.text);
item.appendChild(label);
choice.selected ? item.setAttribute("selected", "true")
: item.removeAttribute("selected");
item.setAttribute("oldstate", "false");
choice.disabled ? item.setAttribute("disabled", "true")
: item.removeAttribute("disabled");
fragment.appendChild(item);
if (choice.group) {
@ -67,18 +66,27 @@ var SelectHelperUI = {
item.optionIndex = choice.optionIndex;
item.choiceIndex = i;
if (choice.inGroup)
if (choice.inGroup) {
item.classList.add("in-optgroup");
}
if (choice.selected) {
item.classList.add("selected");
firstSelected = firstSelected || item;
selectedItems.push(item);
}
}
this._listbox.appendChild(fragment);
this._container.addEventListener("click", this, false);
window.addEventListener("MozPrecisePointer", this, false);
this._menuPopup.show(this._positionOptions(aRect));
// Setup pre-selected items. Note, this has to happen after show.
this._listbox.clearSelection();
for (let item of selectedItems) {
this._listbox.addItemToSelection(item);
item.setAttribute("oldstate", "true");
}
this._listbox.ensureElementIsVisible(firstSelected);
},
@ -94,6 +102,7 @@ var SelectHelperUI = {
return;
this._container.removeEventListener("click", this, false);
window.removeEventListener("MozPrecisePointer", this, false);
this._menuPopup.hide();
this.reset();
},
@ -111,31 +120,38 @@ var SelectHelperUI = {
};
},
_forEachOption: function _selectHelperForEachOption(aCallback) {
let children = this._listbox.childNodes;
for (let i = 0; i < children.length; i++) {
let item = children[i];
if (!item.hasOwnProperty("optionIndex"))
continue;
aCallback(item, i);
}
},
_updateControl: function _selectHelperUpdateControl() {
Browser.selectedBrowser.messageManager.sendAsyncMessage("FormAssist:ChoiceChange", { });
},
handleEvent: function selectHelperHandleEvent(aEvent) {
switch (aEvent.type) {
case "MozPrecisePointer":
this.hide();
break;
case "click":
let item = aEvent.target;
if (item && item.hasOwnProperty("optionIndex")) {
if (this._list.multiple) {
item.classList.toggle("selected");
} else {
item.classList.add("selected");
// item.selected is always true here since that's how richlistbox handles
// mouse click events. We track our own state so that we can toggle here
// on click events. Iniial 'oldstate' values are setup in show above.
if (item.getAttribute("oldstate") == "true") {
item.setAttribute("oldstate", "false");
} else {
item.setAttribute("oldstate", "true");
}
// Fix up selected items - richlistbox will clear selection on click events
// so we need to set selection on the items the user has previously chosen.
this._listbox.clearSelection();
for (let node of this._listbox.childNodes) {
if (node.getAttribute("oldstate") == "true") {
this._listbox.addItemToSelection(node);
}
}
}
this.onSelect(item.optionIndex, item.classList.contains("selected"));
// Let the form element know we've added or removed a selected item.
this.onSelect(item.optionIndex, item.selected);
}
break;
}

View File

@ -0,0 +1,21 @@
<html>
<body bgcolor=white>
<br />
<br />
<br />
<br />
<center>
<select id="selectelement" style="height:200px; width:150px;" multiple>
<option id="opt1">option 1</option>
<option id="opt2">option 2</option>
<option id="opt3">option 3</option>
<option id="opt4">option 4</option>
<option id="opt5">option 5</option>
<option id="opt6">option 6</option>
<option id="opt7">option 7</option>
<option id="opt8">option 8</option>
<option id="opt9">option 9</option>
</select>
</center>
</body>
</html>

View File

@ -0,0 +1,71 @@
// -*- Mode: js2; tab-width: 2; indent-tabs-mode: nil; js2-basic-offset: 2; js2-skip-preprocessor-directives: t; -*-
/* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
"use strict";
function test() {
runTests();
}
gTests.push({
desc: "form multi-select test 1",
setUp: function () {
},
tearDown: function () {
},
run: function () {
yield addTab(chromeRoot + "browser_form_selects.html");
yield waitForCondition(function () {
return !Browser.selectedTab.isLoading();
});
let win = Browser.selectedTab.browser.contentWindow;
let tabdoc = Browser.selectedTab.browser.contentWindow.document;
let select = tabdoc.getElementById("selectelement");
// display the touch menu
let promise = waitForEvent(tabdoc, "popupshown");
sendNativeTap(select);
yield promise;
// tap every option
for (let node of SelectHelperUI._listbox.childNodes) {
sendNativeTap(node);
}
yield waitForMs(100);
// check the menu state
for (let node of SelectHelperUI._listbox.childNodes) {
ok(node.selected, "option is selected");
}
// check the underlying form state
for (let index = 1; index < 10; index++) {
let option = tabdoc.getElementById("opt" + index);
ok(option.selected, "opt" + index + " form option selected");
}
// tap every option again
for (let node of SelectHelperUI._listbox.childNodes) {
sendNativeTap(node);
}
yield waitForMs(100);
// check the menu state
for (let node of SelectHelperUI._listbox.childNodes) {
ok(!node.selected, "option is not selected");
}
// check the underlying form state
for (let index = 1; index < 10; index++) {
let option = tabdoc.getElementById("opt" + index);
ok(!option.selected, "opt" + index + " form option not selected");
}
}
});

View File

@ -6,6 +6,7 @@ support-files =
browser_context_menu_tests_04.html
browser_findbar.html
browser_form_auto_complete.html
browser_form_selects.html
browser_link_click.html
browser_onscreen_keyboard.html
browser_progress_indicator.xul
@ -43,6 +44,7 @@ support-files =
[browser_downloads.js]
[browser_findbar.js]
[browser_form_auto_complete.js]
[browser_form_selects.js]
[browser_history.js]
[browser_inputsource.js]
[browser_link_click.js]

View File

@ -240,11 +240,6 @@ menulist {
border: 0 none;
}
.option-command.selected {
background-color: #ff8000;
color: white;
}
.option-command.optgroup {
font-weight: bold;
font-style: italic;