Skip to content

Commit

Permalink
fix: disable validation of internal text-field
Browse files Browse the repository at this point in the history
  • Loading branch information
vursen committed Sep 21, 2023
1 parent 903e4dc commit 947404f
Show file tree
Hide file tree
Showing 3 changed files with 111 additions and 6 deletions.
6 changes: 4 additions & 2 deletions src/vaadin-combo-box-mixin.html
Original file line number Diff line number Diff line change
Expand Up @@ -934,6 +934,7 @@
/** @private */
_detectAndDispatchChange() {
if (this.value !== this._lastCommittedValue) {
this.validate();
this.dispatchEvent(new CustomEvent('change', {bubbles: true}));
this._lastCommittedValue = this.value;
}
Expand Down Expand Up @@ -1098,6 +1099,7 @@
}
if (!this.readonly && !this._closeOnBlurIsPrevented) {
this._closeOrCommit();
this.validate();
}
}

Expand Down Expand Up @@ -1164,8 +1166,8 @@
* @return {boolean | undefined}
*/
checkValidity() {
if (this.inputElement.validate) {
return this.inputElement.validate();
if (this.inputElement.checkValidity) {
return this.inputElement.checkValidity();
}
}

Expand Down
7 changes: 7 additions & 0 deletions src/vaadin-combo-box.html
Original file line number Diff line number Diff line change
Expand Up @@ -387,6 +387,13 @@
ready() {
super.ready();

// Disable the internal text-field's validation to prevent it from overriding
// the invalid state that was propagated to the text-field from the combo-box.
this.inputElement.validate = () => {};
// Restore the internal text-field's invalid state in case
// it got overridden before the validation was disabled above.
this.inputElement.invalid = this.invalid;

this._nativeInput = this.inputElement.focusElement;
this._toggleElement = this.$.toggleButton;
this._clearElement = this.inputElement.shadowRoot.querySelector('[part="clear-button"]');
Expand Down
104 changes: 100 additions & 4 deletions test/validation.html
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@

<script src="../../web-component-tester/browser.js"></script>
<script src='../../webcomponentsjs/webcomponents-lite.js'></script>
<link rel="import" href="../../iron-test-helpers/mock-interactions.html">
<link rel="import" href="../src/vaadin-combo-box.html">
<link rel="import" href="helpers.html">
<link rel="import" href="../../test-fixture/test-fixture.html">
Expand All @@ -15,10 +16,30 @@
<body>
<test-fixture id="combo-box">
<template>
<vaadin-combo-box></vaadin-combo-box>
<vaadin-combo-box items='["foo"]'></vaadin-combo-box>
</template>
</test-fixture>
<test-fixture id="combo-box-required">
<template>
<vaadin-combo-box required items='["foo"]'></vaadin-combo-box>
</template>
</test-fixture>
<test-fixture id="combo-box-required-value-invalid">
<template>
<vaadin-combo-box required items='["foo"]' value="foo" invalid></vaadin-combo-box>
</template>
</test-fixture>

<script>
function outsideClick() {
// Move focus to body
document.body.tabIndex = 0;
document.body.focus();
document.body.tabIndex = -1;
// Outside click
document.body.click();
}

describe('initial validation', () => {
let comboBox;
let validateSpy;
Expand Down Expand Up @@ -54,11 +75,54 @@
expect(validateSpy.called).to.be.false;
});
});

describe('basic', () => {
let comboBox;
let comboBox, validateSpy, changeSpy;

beforeEach(() => {
comboBox = fixture('combo-box');
validateSpy = sinon.spy(comboBox, 'validate');
changeSpy = sinon.spy();
comboBox.addEventListener('change', changeSpy);
});

it('should disable validation of internal text-field', () => {
comboBox.required = true;
comboBox.inputElement.validate();
expect(comboBox.inputElement.invalid).to.be.false;
});

it('should validate on blur', () => {
comboBox.focus();
comboBox.blur();
expect(validateSpy.calledOnce).to.be.true;
});

it('should validate on outside click', async() => {
comboBox.focus();
comboBox.click();
await nextRender();
outsideClick();
expect(validateSpy.calledOnce).to.be.true;
});

it('should validate before change event on Enter', () => {
comboBox.focus();
comboBox.inputElement.value = 'foo';
comboBox.inputElement.dispatchEvent(new CustomEvent('input'));
MockInteractions.pressAndReleaseKeyOn(comboBox.inputElement, 13, null, 'Enter');
expect(validateSpy.calledOnce).to.be.true;
expect(changeSpy.calledOnce).to.be.true;
expect(changeSpy.calledAfter(validateSpy)).to.be.true;
});

it('should validate before change event on clear button click', () => {
comboBox.value = 'foo';
validateSpy.reset();
comboBox.inputElement.$.clearButton.click();
expect(validateSpy.calledOnce).to.be.true;
expect(changeSpy.calledOnce).to.be.true;
expect(changeSpy.calledAfter(validateSpy)).to.be.true;
});

it('should fire a validated event on validation success', () => {
Expand All @@ -82,15 +146,47 @@
expect(event.detail.valid).to.be.false;
});
});

describe('required', () => {
let comboBox;

beforeEach(async() => {
comboBox = fixture('combo-box-required');
await nextRender();
});

it('should pass validation with value', () => {
comboBox.value = 'foo';
expect(comboBox.checkValidity()).to.be.true;
});

it('should fail validation without value', () => {
expect(comboBox.checkValidity()).to.be.false;
});
});

describe('required + value + invalid', () => {
let comboBox;

beforeEach(async() => {
comboBox = fixture('combo-box-required-value-invalid');
await nextRender();
});

it('should propagate invalid state to internal text-field', () => {
expect(comboBox.inputElement.invalid).to.be.true;
});
});

describe('invalid cannot be set to false', () => {
let comboBox;

beforeEach(async() => {
comboBox = fixture('combo-box');
comboBox._shouldSetInvalid = (invalid) => invalid;
await nextRender();
});

it('should set invalid only when it is true', async() => {
comboBox.required = true;
comboBox.validate();
Expand Down

0 comments on commit 947404f

Please sign in to comment.