Skip to content

Commit

Permalink
fix: update _hasInputValue on programmatic input change (#211)
Browse files Browse the repository at this point in the history
  • Loading branch information
mukherjeesudebi committed May 31, 2023
1 parent d913673 commit f8e1897
Show file tree
Hide file tree
Showing 2 changed files with 100 additions and 49 deletions.
34 changes: 11 additions & 23 deletions src/vaadin-time-picker.html
Original file line number Diff line number Diff line change
Expand Up @@ -39,7 +39,8 @@
readonly="[[readonly]]"
auto-open-disabled="[[autoOpenDisabled]]"
dir="ltr"
theme$="[[theme]]">
theme$="[[theme]]"
on-has-input-value-changed="__onComboBoxHasInputValueChanged">
<template>
[[item.label]]
</template>
Expand Down Expand Up @@ -420,7 +421,6 @@
// thus querySelector in textField.focusElement raises an undefined exception on validate
this.__dropdownElement.addEventListener('value-changed', e => this.__onInputChange(e));
this.__inputElement.addEventListener('keydown', this.__onKeyDown.bind(this));
this.__inputElement.addEventListener('input', this.__onInput.bind(this));

// Validation listeners
this.__dropdownElement.addEventListener('change', e => this.validate());
Expand All @@ -435,27 +435,6 @@
});
}

/**
* Sets the `_hasInputValue` property based on the `input` event.
*
* @param {InputEvent} event
* @protected
*/
_setHasInputValue(event) {
this._hasInputValue = event.target.value.length > 0;
}

/**
* An input event listener used to update `_hasInputValue` property.
* Do not override this method.
*
* @param {Event} event
* @private
*/
__onInput(event) {
this._setHasInputValue(event);
}

/**
* Observer to notify about the change of private property.
*
Expand Down Expand Up @@ -728,6 +707,15 @@
return this.__inputElement;
}

/**
* Synchronizes the `_hasInputValue` property with the internal combo-box's one.
*
* @private
*/
__onComboBoxHasInputValueChanged() {
this._hasInputValue = this.__dropdownElement._hasInputValue;
}

/**
* @param {boolean} invalid
* @protected
Expand Down
115 changes: 89 additions & 26 deletions test/events.html
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@
<script src='../../webcomponentsjs/webcomponents-lite.js'></script>
<link rel="import" href="../src/vaadin-time-picker.html">
<link rel="import" href="../../test-fixture/test-fixture.html">
<script src="../../iron-test-helpers/mock-interactions.js"></script>
</head>

<body>
Expand All @@ -17,37 +18,99 @@
</test-fixture>
<script>
describe('events', () => {
let timePicker, input;

function inputChar(target, char) {
target.value += char;
MockInteractions.keyDownOn(target, char.charCodeAt(0));
target.dispatchEvent(new CustomEvent('input', {bubbles: true, composed: true}));
}

function inputText(target, text) {
for (var i = 0; i < text.length; i++) {
inputChar(target, text[i]);
}
}

function arrowDown(target) {
MockInteractions.keyDownOn(target, 40);
}

function enter(target) {
MockInteractions.pressEnter(target);
}

function space(target) {
MockInteractions.pressSpace(target);
}

function esc(target) {
MockInteractions.keyDownOn(target, 27, null, 'Escape');
}

describe('has-input-value-changed event', () => {
let input, timePicker, clearButton, hasInputValueChangedSpy, valueChangedSpy;

beforeEach(async() => {
hasInputValueChangedSpy = sinon.spy();
valueChangedSpy = sinon.spy();
timePicker = fixture('time-picker');
input = timePicker.__inputElement;
});

it('should fire the event on input value presence change', async() => {
const hasInputValueChangedSpy = sinon.spy();
input = timePicker.__inputElement.inputElement;
clearButton = timePicker.__inputElement.shadowRoot.querySelector('[part=clear-button]');
timePicker.addEventListener('has-input-value-changed', hasInputValueChangedSpy);
input.value = '13:00';
input.dispatchEvent(new CustomEvent('input'));
expect(hasInputValueChangedSpy.calledOnce).to.be.true;

hasInputValueChangedSpy.reset();
input.value = '';
input.dispatchEvent(new CustomEvent('input'));
expect(hasInputValueChangedSpy.calledOnce).to.be.true;
timePicker.addEventListener('value-changed', valueChangedSpy);
});

it('should not fire the event on input if input value presence has not changed', async() => {
const hasInputValueChangedSpy = sinon.spy();
timePicker.addEventListener('has-input-value-changed', hasInputValueChangedSpy);
input.value = '13:00';
input.dispatchEvent(new CustomEvent('input'));
hasInputValueChangedSpy.reset();

input.value = '13:58';
input.dispatchEvent(new CustomEvent('input'));
expect(hasInputValueChangedSpy.called).to.be.false;

describe('without value', () => {
it('should be fired when entering user input', async() => {
inputText(input, '12:00');
expect(hasInputValueChangedSpy.calledOnce).to.be.true;
});

describe('with user input', () => {
beforeEach(async() => {
inputText(input, '12:00');
hasInputValueChangedSpy.reset();
});

it('should be fired when clearing the user input with Esc', async() => {
// Clear selection in the dropdown.
esc(input);
// Clear the user input.
esc(input);
expect(input.value).to.be.empty;
expect(hasInputValueChangedSpy.calledOnce).to.be.true;
});
});

describe('with bad user input', async() => {
beforeEach(async() => {
inputText(input, 'foo');
hasInputValueChangedSpy.reset();
});

it('should be fired when clearing bad user input with Esc', async() => {
esc(input);
expect(hasInputValueChangedSpy.calledOnce).to.be.true;
});
});
});

describe('with value', () => {
beforeEach(async() => {
timePicker.clearButtonVisible = true;
inputText(input, '10:00');
enter(input);
valueChangedSpy.reset();
hasInputValueChangedSpy.reset();
});

it('should be fired on clear button click', () => {
clearButton.dispatchEvent(new CustomEvent('click', {bubbles: true, composed: true}));
expect(input.value).to.be.empty;
expect(timePicker.value).to.be.empty;
expect(valueChangedSpy.calledOnce).to.be.true;
expect(hasInputValueChangedSpy.callCount).to.equal(1);
expect(hasInputValueChangedSpy.calledBefore(valueChangedSpy)).to.be.true;
});
});
});
});
Expand Down

0 comments on commit f8e1897

Please sign in to comment.