Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

fix(input-mixin): reset _hasInputValue on programmatic value clear #5599

Merged
merged 5 commits into from
Feb 27, 2023
Merged
Show file tree
Hide file tree
Changes from 4 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
8 changes: 7 additions & 1 deletion packages/field-base/src/input-mixin.js
Original file line number Diff line number Diff line change
Expand Up @@ -54,7 +54,11 @@ export const InputMixin = dedupingMixin(
},

/**
* When true, the input element has a non-empty value entered by the user.
* Whether the input element has user input.
*
* Note, the property indicates true only if the input has been entered by the user.
* In the case of programmatic changes, the property must be reset to false.
*
* @protected
*/
_hasInputValue: {
Expand Down Expand Up @@ -119,6 +123,8 @@ export const InputMixin = dedupingMixin(
* Clear the value of the field.
*/
clear() {
this._hasInputValue = false;

this.value = '';

// Clear the input immediately without waiting for the observer.
Expand Down
69 changes: 47 additions & 22 deletions packages/field-base/test/input-mixin.test.js
Original file line number Diff line number Diff line change
Expand Up @@ -178,7 +178,7 @@ const runTests = (defineHelper, baseMixin) => {
});

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

before(() => {
tag = defineHelper(
Expand All @@ -190,38 +190,63 @@ const runTests = (defineHelper, baseMixin) => {

beforeEach(async () => {
hasInputValueChangedSpy = sinon.spy();
valueChangedSpy = sinon.spy();
element = fixtureSync(`<${tag}></${tag}>`);
element.addEventListener('has-input-value-changed', hasInputValueChangedSpy);
element.addEventListener('value-changed', valueChangedSpy);
await nextRender();
input = document.createElement('input');
element.appendChild(input);
element._setInputElement(input);
await nextFrame();
});

it('should fire the event on input value presence change', async () => {
input.value = 'foo';
fire(input, 'input');
await nextFrame();
expect(hasInputValueChangedSpy.calledOnce).to.be.true;

hasInputValueChangedSpy.resetHistory();
input.value = '';
fire(input, 'input');
await nextFrame();
expect(hasInputValueChangedSpy.calledOnce).to.be.true;
describe('empty field', () => {
it('should fire the event once when entering input', async () => {
input.value = 'foo';
fire(input, 'input');
await nextFrame();
expect(hasInputValueChangedSpy.calledOnce).to.be.true;
vursen marked this conversation as resolved.
Show resolved Hide resolved
expect(hasInputValueChangedSpy.calledBefore(valueChangedSpy)).to.be.true;
});

it('should not fire the event on programmatic clear', async () => {
element.clear();
await nextFrame();
expect(hasInputValueChangedSpy.called).to.be.false;
});
});

it('should not fire the event on input if input value presence has not changed', async () => {
input.value = 'foo';
fire(input, 'input');
await nextFrame();
hasInputValueChangedSpy.resetHistory();

input.value = 'foobar';
fire(input, 'input');
await nextFrame();
expect(hasInputValueChangedSpy.called).to.be.false;
describe('user-originated input', () => {
beforeEach(async () => {
input.value = 'foo';
fire(input, 'input');
await nextFrame();
hasInputValueChangedSpy.resetHistory();
valueChangedSpy.resetHistory();
});

it('should not fire the event when modifying input', async () => {
input.value = 'foobar';
fire(input, 'input');
await nextFrame();
expect(hasInputValueChangedSpy.called).to.be.false;
});

it('should fire the event once when removing input', async () => {
input.value = '';
fire(input, 'input');
await nextFrame();
expect(hasInputValueChangedSpy.calledOnce).to.be.true;
expect(hasInputValueChangedSpy.calledBefore(valueChangedSpy)).to.be.true;
});

it('should fire the event once on programmatic clear', async () => {
element.clear();
await nextFrame();
expect(hasInputValueChangedSpy.calledOnce).to.be.true;
expect(hasInputValueChangedSpy.calledBefore(valueChangedSpy)).to.be.true;
});
});
});
};
Expand Down