Skip to content

Commit

Permalink
fix(ui5-input): fire change in sync with the native input (#168)
Browse files Browse the repository at this point in the history
Bind for the native input change to fire the ui5-input semantic one.
Make sure change event is fired upon suggestion item selection.
Still, for IE, a manually work is needed to detect if  change should be fired 
on Enter as the native input does not react.
Fixes: #154
  • Loading branch information
ilhan007 authored Mar 13, 2019
1 parent db88b8c commit 55fa533
Show file tree
Hide file tree
Showing 7 changed files with 115 additions and 85 deletions.
1 change: 1 addition & 0 deletions packages/main/src/Input.hbs
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@
.value="{{ctr.value}}"
placeholder="{{ctr.placeholder}}"
@input="{{ctr._input.onInput}}"
@change="{{ctr._input.change}}"
data-sap-no-tab-ref
data-sap-focus-ref
/>
Expand Down
42 changes: 16 additions & 26 deletions packages/main/src/Input.js
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
import WebComponent from "@ui5/webcomponents-base/src/sap/ui/webcomponents/base/WebComponent";
import Bootstrap from "@ui5/webcomponents-base/src/sap/ui/webcomponents/base/Bootstrap";
import { isIE } from "@ui5/webcomponents-core/dist/sap/ui/Device";
import ValueState from "@ui5/webcomponents-base/src/sap/ui/webcomponents/base/types/ValueState";
import ShadowDOM from "@ui5/webcomponents-base/src/sap/ui/webcomponents/base/compatibility/ShadowDOM";
import {
Expand Down Expand Up @@ -285,17 +286,14 @@ class Input extends WebComponent {
// Indicates if there is selected suggestionItem.
this.hasSuggestionItemSelected = false;

// Indicates if there is focused suggestionItem.
// Used to ignore the Input "focusedOut" and thus preventing firing "change" event.
this.hasSuggestionItemFocused = false;

this.previousValue = undefined;

// Represents the value before user moves selection between the suggestion items.
// Used to register and fire "input" event upon [SPACE] or [ENTER].
// Note: the property "value" is updated upon selection move and can`t be used.
this.valueBeforeItemSelection = "";

// tracks the value between focus in and focus out to detect that change event should be fired.
this.previousValue = undefined;

// Indicates, if the component is rendering for first time.
this.firstRendering = true;

Expand All @@ -307,11 +305,13 @@ class Input extends WebComponent {

// all user interactions
this.ACTION_ENTER = "enter";
this.ACTION_FOCUSOUT = "focusOut";
this.ACTION_USER_INPUT = "input";

this._input = {
onInput: this._onInput.bind(this),
change: event => {
this.fireEvent(this.EVENT_CHANGE);
},
};

this._whenShadowRootReady().then(this.attachFocusHandlers.bind(this));
Expand All @@ -327,7 +327,6 @@ class Input extends WebComponent {
if (!this.firstRendering && this.Suggestions) {
this.Suggestions.toggle(this.shouldOpenSuggestions());
}
this.checkFocusOut();
this.firstRendering = false;
}

Expand Down Expand Up @@ -392,13 +391,13 @@ class Input extends WebComponent {
}

onfocusin() {
this.previousValue = this.value;
this.hasSuggestionItemFocused = false;
this._focused = true; // invalidating property
this.previousValue = this.value;
}

onfocusout() {
this._focused = false; // invalidating property
this.previousValue = "";
}

_onInput(event) {
Expand Down Expand Up @@ -454,6 +453,7 @@ class Input extends WebComponent {
this.value = itemText;
this.valueBeforeItemSelection = itemText;
this.fireEvent(this.EVENT_INPUT);
this.fireEvent(this.EVENT_CHANGE);
}
}

Expand All @@ -469,35 +469,27 @@ class Input extends WebComponent {

const inputValue = this.getInputValue();
const isSubmit = action === this.ACTION_ENTER;
const isFocusOut = action === this.ACTION_FOCUSOUT;
const isUserInput = action === this.ACTION_USER_INPUT;

this.value = inputValue;

const valueChanged = (this.previousValue !== undefined) && (this.previousValue !== this.value);

if (isUserInput) { // input
this.fireEvent(this.EVENT_INPUT);
return;
}

if ((isSubmit || isFocusOut) && valueChanged) { // change
this.previousValue = this.value;
this.fireEvent(this.EVENT_CHANGE);
}

if (isSubmit) { // submit
this.fireEvent(this.EVENT_SUBMIT);
}
}

checkFocusOut() {
if (!this._focused && !this.hasSuggestionItemFocused) {
this.fireEventByAction(this.ACTION_FOCUSOUT);
this.previousValue = "";
// In IE, pressing the ENTER does not fire change
const valueChanged = (this.previousValue !== undefined) && (this.previousValue !== this.value);
if (isIE() && isSubmit && valueChanged) {
this.fireEvent(this.EVENT_CHANGE);
}
}


getInputValue() {
const inputDOM = this.getDomRef();
if (inputDOM) {
Expand All @@ -519,9 +511,7 @@ class Input extends WebComponent {
}

/* Suggestions interface */
onItemFocused() {
this.hasSuggestionItemFocused = true;
}
onItemFocused() {}

onItemSelected(item, keyboardUsed) {
this.selectSuggestion(item, keyboardUsed);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -32,20 +32,22 @@
<ui5-datepicker id="dp10" disabled></ui5-datepicker>
<ui5-datepicker id="dp11"></ui5-datepicker>
<ui5-datepicker id="dp12"></ui5-datepicker>
<ui5-datepicker id="dp13"></ui5-datepicker>
<label id='lbl'>0</label>
<label for="inputTimezone">Timezone:</label>
<input id="inputTimezone" type="number"/>
<button id="btnApplyTimezone" onclick="stubTimezone();">Apply</button>
<button id="btnRestoreTimezone" onclick="restore();">Restore</button>
<button id="downThere" style="position:absolute; top:3000px;"></button>

<label id='lblTomorrow'>0</label>
<label id='lblTomorrowDate'></label>
<script>
const originalGetDate = Date.prototype.getDate;
var originalGetDate = Date.prototype.getDate;

function stubTimezone() {
const timezone = parseInt(document.querySelector("#inputTimezone").value, 10);
var timezone = parseInt(document.querySelector("#inputTimezone").value, 10);
Date.prototype.getDate = function() {
const stubedDate = new Date(this.getTime() + timezone * 60 * 60 * 1000);
var stubedDate = new Date(this.getTime() + timezone * 60 * 60 * 1000);
return stubedDate.getUTCDate();
};
}
Expand All @@ -55,13 +57,18 @@
document.querySelector("#inputTimezone").value = "";
};

let counter = 0;
var counter = 0;
var counterTomorrow = 0;
function increaseCounter() {
document.querySelector('#lbl').innerHTML = ++counter;
}

document.querySelector("#dp5").addEventListener("change", increaseCounter);
document.querySelector("#dp8").addEventListener("change", increaseCounter);
document.querySelector("#dp13").addEventListener("change", function(e) {
document.querySelector('#lblTomorrow').innerHTML = ++counterTomorrow;
document.querySelector('#lblTomorrowDate').innerHTML = e.target.value;
});
</script>
</body>
</html>
36 changes: 26 additions & 10 deletions packages/main/test/sap/ui/webcomponents/main/pages/Input.html
Original file line number Diff line number Diff line change
Expand Up @@ -37,24 +37,31 @@ <h3> Input disabled</h3>
<ui5-icon data-ui5-slot="icon" src="sap-icon://appointment-2"></ui5-icon>
</ui5-input>

<h3> Input valueState Warning</h3>
<h3> Input valueState Success</h3>
<ui5-input id="input3" style="width:100%" value-state="Success" placeholder="Success state ...">
<ui5-icon data-ui5-slot="icon" src="sap-icon://message-success"></ui5-icon>
</ui5-input>

<h3> Test 'change' event</h3>
<ui5-input id="input1" style="width:100%" value-state="Warning" placeholder="Warning state ...">
<ui5-icon data-ui5-slot="icon" src="sap-icon://message-warning" functional></ui5-icon>
</ui5-input>
<h3> 'change' event result</h3>
<ui5-input id="inputResult" style="width:100%"></ui5-input>

<h3> Input valueState Error</h3>
<h3> Test 'input' event</h3>
<ui5-input id="input2" style="width:100%" value-state="Error" placeholder="Error state ...">
<ui5-icon data-ui5-slot="icon" src="sap-icon://message-error"></ui5-icon>
</ui5-input>
<h3> 'input' test result</h3>
<ui5-input id="inputLiveChangeResult" style="width:100%"></ui5-input>

<h3> Input valueState Success</h3>
<ui5-input id="input3" style="width:100%" value-state="Success" placeholder="Success state ...">
<ui5-icon data-ui5-slot="icon" src="sap-icon://message-success"></ui5-icon>
</ui5-input>


<h3> Input</h3>
<ui5-input id="inputResult" style="width:100%">
</ui5-input>
<h3> Input test change</h3>
<ui5-input id="inputChange" style="width:100%"> </ui5-input>
<h3> Input test change result</h3>
<ui5-input id="inputChangeResult" style="width:100%"></ui5-input>

<h3> Input readonly</h3>
<ui5-input style="width:100%" value="Value can`t be changed" readonly></ui5-input>
Expand Down Expand Up @@ -139,14 +146,23 @@ <h3> Input type 'URL'</h3>

input2.addEventListener("input", function (event) {
inputCounter += 1;
inputResult.value = inputCounter;
inputLiveChangeResult.value = inputCounter;
});

myInput2.addEventListener("suggestionItemSelect", function (event) {
var item = event.detail.item;
suggestionSelectedCounter += 1;
inputResult.value = suggestionSelectedCounter;
});

var inputChangeResultCounter = 0;

inputChange.addEventListener("submit", function (event) {
inputChange.value = "Changed via API";
});
inputChange.addEventListener("change", function (event) {
inputChangeResult.value = ++inputChangeResultCounter;
});
</script>
</body>
</html>
35 changes: 0 additions & 35 deletions packages/main/test/sap/ui/webcomponents/main/qunit/Input.qunit.js
Original file line number Diff line number Diff line change
Expand Up @@ -269,40 +269,5 @@ TestHelper.ready(function() {
fixture.innerHTML = "";
this.input = null;
});

QUnit.test("type in input fires change onfocusout", function(assert) {
assert.expect(1);

var input = this.getInput();
var done = assert.async();

input.addEventListener("change", function(e){
assert.ok(true, "change fired, when onfocusout");
done();
});

input.value = "abc";
input.onfocusout();
});

QUnit.test("type in input does not fire change when the initial value remains the same", function(assert) {
assert.expect(1);

var input = this.getInput();
var done = assert.async();

input.addEventListener("change", function(e){
assert.ok(false, "change fired incorreclty, when onfocusout");
});

input.value = "abc";
input.value = "";
input.onfocusout();

setTimeout(function() {
assert.ok(true, "change event not fired on onfocusout, when the value did not changed");
done();
}, 200)
});
});
});
29 changes: 27 additions & 2 deletions packages/main/test/specs/DatePicker.spec.js
Original file line number Diff line number Diff line change
Expand Up @@ -228,19 +228,44 @@ describe("Date Picker Tests", () => {

datepicker.innerInput.click();
browser.keys("\b\b\b\b\b\b\b\b\b\b\b");
datepicker.innerInput.setValue("Jan 8, 2015");
datepicker.innerInput.keys("Jan 8, 2015");
browser.findElementDeep("#dp1 >>> ui5-input >>> input").click(); //click elsewhere to focusout

assert.equal(browser.findElementDeep("#lbl").getHTML(false), "1", 'change has fired once');

datepicker.innerInput.click();
browser.keys("\b\b\b\b\b\b\b\b\b\b\b");
datepicker.innerInput.setValue("Jan 6, 2015");
datepicker.innerInput.keys("Jan 6, 2015");
browser.findElementDeep("#dp1 >>> ui5-input >>> input").click(); //click elsewhere to focusout

assert.equal(browser.findElementDeep("#lbl").getHTML(false), "2", 'change has fired once');
});

it("change fires every time tomorrow is typed and normalized", () => {
let tomorrowDate;
const lblChangeCounter = browser.$("#lblTomorrow");
const lblTomorrowDate = browser.$("#lblTomorrowDate");

datepicker.id = "#dp13";

// Type tomorrow.
datepicker.innerInput.click();
datepicker.innerInput.keys("tomorrow");

// Press Enter, store the date and delete it.
datepicker.innerInput.keys("Enter");
tomorrowDate = lblTomorrowDate.getHTML(false);
browser.keys("\b\b\b\b\b\b\b\b\b\b\b\b\b");

// Type tomorrow and press Enter for the second time.
datepicker.innerInput.keys("tomorrow");
datepicker.innerInput.keys("Enter");

// Two change events should be fired and the date should twice normalized
assert.equal(lblChangeCounter.getHTML(false), "2", 'change event is being fired twice');
assert.equal(lblTomorrowDate.getHTML(false), tomorrowDate, 'tomorrow is normalazid to date twice as well');
});

it("today value is normalized and correctly rounded to 00:00:00", () => {
datepicker.id = "#dp9";

Expand Down
Loading

0 comments on commit 55fa533

Please sign in to comment.