Skip to content

Commit

Permalink
FormWidget: add afterFormResetCallback() method.
Browse files Browse the repository at this point in the history
The default FormWidget and FormValueWidget afterFormResetCallback() methods
reset the widget according to the value (or checked property) of this.valueNode.
Strangely, there's no code in FormWidget and FormValueWidget to do that on initialization.
That should be added in the future.

Also, the original code in deliteful got the form reference from this.valueNode.form,
but that assumes that the widget is rendered before attachedCallback(),
which won't always be true (see #404).  So instead, I'm just tracing up the DOM tree
for the nearest <form> ancestor.

Fixes #423, refs ibm-js/deliteful#584.
  • Loading branch information
wkeese committed Aug 28, 2015
1 parent 9a488a6 commit 5b1e978
Show file tree
Hide file tree
Showing 5 changed files with 163 additions and 1 deletion.
9 changes: 8 additions & 1 deletion FormValueWidget.js
Original file line number Diff line number Diff line change
Expand Up @@ -132,6 +132,13 @@ define([
* @function
* @protected
*/
handleOnInput: genHandler("input", "_previousOnInputValue", "_onInputHandle")
handleOnInput: genHandler("input", "_previousOnInputValue", "_onInputHandle"),

afterFormResetCallback: function () {
console.log(this.id, "FormValueWidget#afterFormResetCallback");
if (this.value !== this.valueNode.value) {
this.value = this.valueNode.value;
}
}
});
});
29 changes: 29 additions & 0 deletions FormWidget.js
Original file line number Diff line number Diff line change
Expand Up @@ -239,6 +239,35 @@ define([
HTMLElement.prototype.removeAttribute.call(this, attr.name);
}
}
},

attachedCallback: function () {
// If the widget is in a form, reset the initial value of the widget when the form is reset.
for (var form = this.parentNode; form; form = form.parentNode) {
if (/^form$/i.test(form.tagName)) {
this.on("reset", function () {
this.defer(function () {
this.afterFormResetCallback();
});
}.bind(this), form);
break;
}
}
},

/**
* Callback after `<form>` containing this widget is reset.
* By the time this callback executes, `this.valueNode.value` will have already been reset according to
* the form's original value.
*
* @protected
*/
afterFormResetCallback: function () {
if (this.checked !== this.valueNode.checked) {
this.checked = this.valueNode.checked;
}
}
});
});


85 changes: 85 additions & 0 deletions tests/functional/FormValueWidget.html
Original file line number Diff line number Diff line change
@@ -0,0 +1,85 @@
<!DOCTYPE html>
<html>
<head>
<meta http-equiv="Content-type" content="text/html; charset=utf-8"/>
<meta name="viewport"
content="width=device-width,initial-scale=1,maximum-scale=1,minimum-scale=1,user-scalable=no"/>
<meta name="apple-mobile-web-app-capable" content="yes"/>
<title>FormValueWidget</title>

<!-- For testing purposes. Real applications should load the AMD loader directly -->
<script type="text/javascript" src="boilerplate.js"></script>

<style>
my-spinner {
border: solid gray 2px;
padding: 3px;
display: inline-block;
cursor: default;

-webkit-touch-callout: none;
-webkit-user-select: none;
-khtml-user-select: none;
-moz-user-select: none;
-ms-user-select: none;
user-select: none;
}
my-spinner .button {
border: solid black 1px;
padding: 3px;
margin: 2px;
display: inline-block;
}
</style>
<script type="text/javascript">
var ready = false; // set to true when the test page is ready
require(["delite/register", "delite/Container", "delite/FormValueWidget", "delite/handlebars",
"requirejs-domready/domReady!"], function (register, Container, FormValueWidget, handlebars) {
register("my-spinner", [HTMLElement, FormValueWidget, Container], {
template: handlebars.compile(
"<template>" +
"<input style='display:none'>" + // just for testing
"<span attach-point=focusNode class='value'>{{this.value}}</span>" +
"<span on-click=decrement class='button decrement'>-</span>" +
"<span on-click=increment class='button increment'>+</span>" +
"<span style='display:none' attach-point=containerNode></span>" +
"</template>"
),
postRender: function () {
// TODO: move this logic into FormValueWidget itself?
this.valueNode = this.containerNode.querySelector("input");
if (this.valueNode) {
this.value = this.valueNode.value;
} else {
this.valueNode = this.ownerDocument.createElement("input");
this.containerNode.appendChild(this.valueNode);
}
},
decrement: function () {
this.value--;
},
increment: function () {
this.value++;
}
});
register.deliver();
ready = true;
});
</script>
</head>
<body>
<h1>FormValueWidget functional test</h1>

<form id="form1">
<span id="spinner1_label">Spinner widget based on FormValueWidget:</span>
<my-spinner id="spinner1" aria-labelledby="spinner1_label"><input value="5"></my-spinner>

<br>
<input type="reset" id="resetB" name="reset">
<input type="submit" id="submitB">

<br>
Clicking the reset button should put the spinner's value back to 5
</form>

</body>
40 changes: 40 additions & 0 deletions tests/functional/FormValueWidget.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,40 @@
define([
"require",
"intern",
"intern!object",
"intern/chai!assert",
"intern/dojo/node!leadfoot/keys",
"intern/dojo/node!leadfoot/helpers/pollUntil"
], function (require, intern, registerSuite, assert, keys, pollUntil) {

registerSuite({
name: "FormValueWidget functional tests",

setup: function () {
return this.remote
.get(require.toUrl("./FormValueWidget.html"))
.then(pollUntil("return ready || null;", [],
intern.config.WAIT_TIMEOUT, intern.config.POLL_INTERVAL));
},

reset: function () {
this.timeout = intern.config.TEST_TIMEOUT;
var environmentType = this.remote.environmentType;
if (environmentType.browserName === "internet explorer") {
return this.skip("click() doesn't generate mousedown/mouseup, so popup won't open");
}
return this.remote.findByCssSelector("my-spinner .increment")
.click()
.end()
.execute("return spinner1.value").then(function (value) {
assert.equal(value, 6, "incremented value"); // use equal() to ignore string vs. number diff
})
.findByCssSelector("#resetB")
.click()
.end()
.execute("return spinner1.value").then(function (value) {
assert.equal(value, 5, "reset value"); // use equal() to ignore string vs. number diff
});
}
});
});
1 change: 1 addition & 0 deletions tests/functional/all.js
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ define([
"./activationTracker",
"./Widget",
"./DialogUnderlay",
"./FormValueWidget",
"./HasDropDown",
"./TabIndex",
"./KeyNav",
Expand Down

0 comments on commit 5b1e978

Please sign in to comment.