Skip to content

Commit

Permalink
Implement HTMLSelectElement's selectedOptions
Browse files Browse the repository at this point in the history
This also pulls in webidl2js  v6.1.0, which gives us automatic [SameObject] caching, so it will also improve the semantics of other properties.
  • Loading branch information
Zirro authored and domenic committed May 1, 2017
1 parent e79e9a1 commit 8d3f747
Show file tree
Hide file tree
Showing 5 changed files with 136 additions and 2 deletions.
6 changes: 6 additions & 0 deletions lib/jsdom/living/nodes/HTMLSelectElement-impl.js
Original file line number Diff line number Diff line change
Expand Up @@ -90,6 +90,12 @@ class HTMLSelectElementImpl extends HTMLElementImpl {
return this.options.length;
}

get selectedOptions() {
return createHTMLCollection(this, () => domSymbolTree.treeToArray(this, { filter(node) {
return node._localName === "option" && node._selectedness === true;
} }));
}

get selectedIndex() {
return Array.prototype.reduceRight.call(this.options, (prev, option, i) => {
option = idlUtils.implForWrapper(option);
Expand Down
2 changes: 1 addition & 1 deletion lib/jsdom/living/nodes/HTMLSelectElement.idl
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,7 @@ interface HTMLSelectElement : HTMLElement {
void remove(long index);
// setter void (unsigned long index, HTMLOptionElement? option);

// [SameObject] readonly attribute HTMLCollection selectedOptions;
[SameObject] readonly attribute HTMLCollection selectedOptions;
attribute long selectedIndex;
attribute DOMString value;

Expand Down
2 changes: 1 addition & 1 deletion package.json
Original file line number Diff line number Diff line change
Expand Up @@ -66,7 +66,7 @@
"st": "^1.2.0",
"watchify": "^3.9.0",
"wd": "^1.1.3",
"webidl2js": "^6.0.3"
"webidl2js": "^6.1.0"
},
"browser": {
"canvas": false,
Expand Down
1 change: 1 addition & 0 deletions test/web-platform-tests/to-upstream.js
Original file line number Diff line number Diff line change
Expand Up @@ -89,6 +89,7 @@ describe("Local tests in Web Platform Test format (to-upstream)", () => {
"html/semantics/forms/the-option-element/option-index.html",
"html/semantics/forms/the-option-element/option-element-constructor.html",
"html/semantics/forms/the-select-element/select-multiple.html",
"html/semantics/forms/the-select-element/select-selectedOptions.html",
"html/semantics/forms/the-textarea-element/select.html",
"html/semantics/forms/the-textarea-element/set-value-reset-selection.html",
"html/semantics/forms/the-textarea-element/setRangeText.html",
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,127 @@
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<title>HTMLSelectElement.selectedOptions</title>
<link rel="help" href="https://html.spec.whatwg.org/multipage/forms.html#dom-select-selectedoptions">
<script src="/resources/testharness.js"></script>
<script src="/resources/testharnessreport.js"></script>
</head>
<body>
<div id="log"></div>

<select id="select-none-selected">
<option>One</option>
<option>Two</option>
<option>Three</option>
</select>

<select id="select-one-selected">
<option>One</option>
<option selected>Two</option>
<option>Three</option>
</select>

<select multiple id="multiple-select-none-selected">
<option>One</option>
<option>Two</option>
<option>Three</option>
</select>

<select multiple id="multiple-select-two-selected">
<option>One</option>
<option selected>Two</option>
<option selected>Three</option>
</select>

<select id="invalid-select">
<option selected>One</option>
<option selected>Two</option>
<option>Three</option>
</select>

<select id="select-same-object">
<option>One</option>
<option selected>Two</option>
<option>Three</option>
</select>

<select multiple id="select-same-object-change">
<option selected>One</option>
<option selected>Two</option>
<option selected>Three</option>
</select>

<script>
"use strict";

test(() => {
const select = document.getElementById("select-none-selected");

assert_array_equals(select.selectedOptions, [select.children[0]]);
assert_equals(select.selectedOptions.length, 1);

}, ".selectedOptions with no selected option");

test(() => {
const select = document.getElementById("select-one-selected");

assert_array_equals(select.selectedOptions, [select.children[1]]);
assert_equals(select.selectedOptions.length, 1);
}, ".selectedOptions with one selected option");

test(() => {
const select = document.getElementById("multiple-select-none-selected");

assert_equals(select.selectedOptions.item(0), null);
assert_equals(select.selectedOptions.length, 0);
}, ".selectedOptions using the 'multiple' attribute with no selected options");

test(() => {
const select = document.getElementById("multiple-select-two-selected");

assert_equals(select.selectedOptions.item(0), select.children[1]);
assert_equals(select.selectedOptions.item(1), select.children[2]);
assert_equals(select.selectedOptions.length, 2);
}, ".selectedOptions using the 'multiple' attribute with two selected options");

// "A select element whose multiple attribute is not specified must not have
// more than one descendant option element with its selected attribute set."
// - https://html.spec.whatwg.org/multipage/forms.html#the-option-element:the-select-element-6

// "If two or more option elements in the select element's list of options
// have their selectedness set to true, set the selectedness of all but
// the last option element with its selectedness set to true in the list of
// options in tree order to false."
// - https://html.spec.whatwg.org/multipage/forms.html#the-select-element:the-option-element-21
test(() => {
const select = document.getElementById("invalid-select");

assert_array_equals(select.selectedOptions, [select.children[1]]);
assert_equals(select.selectedOptions.length, 1);

}, ".selectedOptions without the 'multiple' attribute but " +
"more than one selected option should return the last one");

test(() => {
const select = document.getElementById("select-same-object");
const selectAgain = document.getElementById("select-same-object");

assert_equals(select.selectedOptions, selectAgain.selectedOptions);

}, ".selectedOptions should always return the same value - [SameObject]");

test(() => {
const select = document.getElementById("select-same-object-change");
const before = select.selectedOptions;

select.selectedOptions[1].selected = false;

const after = select.selectedOptions;

assert_equals(before, after);

}, ".selectedOptions should return the same object after selection changes - [SameObject]");
</script>
</body>
</html>

0 comments on commit 8d3f747

Please sign in to comment.