Skip to content

Commit

Permalink
feat: add text area pattern validation (CP: 14.8) (#586)
Browse files Browse the repository at this point in the history
  • Loading branch information
sissbruecker authored Nov 18, 2021
1 parent 156dfc0 commit eebe032
Show file tree
Hide file tree
Showing 2 changed files with 149 additions and 0 deletions.
39 changes: 39 additions & 0 deletions src/vaadin-text-area.html
Original file line number Diff line number Diff line change
Expand Up @@ -158,6 +158,18 @@
return '2.8.6';
}

static get properties() {
return {
/**
* A regular expression that the value is checked against.
* The pattern must match the entire value, not just some subset.
*/
pattern: {
type: String
}
};
}

static get observers() {
return [
'_textAreaValueChanged(value)'
Expand Down Expand Up @@ -233,6 +245,33 @@
this._dispatchIronResizeEventIfNeeded('InputHeight', inputHeight);
}

/**
* Returns true if the current textarea value satisfies all constraints (if any).
* @return {boolean}
*/
checkValidity() {
if (!super.checkValidity()) {
return false;
}

// Native <textarea> does not support pattern attribute, so we have a custom logic
// according to WHATWG spec for <input>, with tests inspired by web-platform-tests
// https://html.spec.whatwg.org/multipage/input.html#the-pattern-attribute

if (!this.pattern || !this.inputElement.value) {
// Mark as valid if there is no pattern, or the value is empty
return true;
}

try {
const match = this.inputElement.value.match(this.pattern);
return match ? match[0] === match.input : false;
} catch (_) {
// If the pattern can not be compiled, then report as valid
return true;
}
}

/**
* Fired when the text-area height changes.
*
Expand Down
110 changes: 110 additions & 0 deletions test/text-area.html
Original file line number Diff line number Diff line change
Expand Up @@ -161,6 +161,29 @@
});
}

describe('prevent invalid input', () => {
beforeEach(() => {
textArea.preventInvalidInput = true;
textArea.value = '1';
});

function inputText(value) {
textArea.inputElement.value = value;
textArea.inputElement.dispatchEvent(new CustomEvent('input'));
}

it('should prevent non matching input', () => {
textArea.pattern = '[0-9]*';
inputText('f');
expect(textArea.inputElement.value).to.equal('1');
});

it('should not prevent input when pattern is invalid', () => {
textArea.pattern = '[0-9])))]*';
inputText('f');
expect(textArea.inputElement.value).to.equal('f');
});
});
});

describe(`vaadin-text-area-appear${condition}`, () => {
Expand Down Expand Up @@ -394,5 +417,92 @@
});
});

describe('pattern', () => {
// eslint-disable-next-line max-len
// https://github.com/web-platform-tests/wpt/blob/7b0ebaccc62b566a1965396e5be7bb2bc06f841f/html/semantics/forms/constraints/form-validation-validity-patternMismatch.html

let element;

function userSetValue(value) {
element.value = value;
element.dispatchEvent(new CustomEvent('input'));
}

beforeEach(() => {
element = fixture(`default`);
});

it('should be valid when pattern property is not set', () => {
element.pattern = null;
userSetValue('abc');
expect(element.validate()).to.be.true;
});

it('should be valid when value property is empty', () => {
element.pattern = '[A-Z]+';
userSetValue('');
expect(element.validate()).to.be.true;
});

it('should be valid when value property matches the pattern', () => {
element.pattern = '[A-Z]{1}';
userSetValue('A');
expect(element.validate()).to.be.true;
});

it('should be valid when value property matches the pattern (multiline)', () => {
element.pattern = '[A-Z\n]{3}';
userSetValue('A\nJ');
expect(element.validate()).to.be.true;
});

it('should be valid when unicode value property matches the pattern', () => {
element.pattern = '[A-Z]+';
userSetValue('\u0041\u0042\u0043');
expect(element.validate()).to.be.true;
});

it('should be invalid when value property mismatches the pattern', () => {
element.pattern = '[a-z]{3,}';
userSetValue('ABCD');
expect(element.validate()).to.be.false;
});

it('should be invalid when value property mismatches the pattern, even if a subset matches', () => {
element.pattern = '[A-Z]+';
userSetValue('ABC123');
expect(element.validate()).to.be.false;
});

it('should be valid when pattern contains invalid regular expression', () => {
element.pattern = '(abc';
userSetValue('de');
expect(element.validate()).to.be.true;
});

it('should be valid when pattern tries to escape a group', () => {
element.pattern = 'a)(b';
userSetValue('de');
expect(element.validate()).to.be.true;
});

it('should be valid when pattern uses Unicode features', () => {
element.pattern = 'a\u{10FFFF}';
userSetValue('a\u{10FFFF}');
expect(element.validate()).to.be.true;
});

it('should be valid when value matches JavaScript-specific regular expression', () => {
element.pattern = '\\u1234\\cx[5-[]{2}';
userSetValue('\u1234\x18[6');
expect(element.validate()).to.be.true;
});

it('should be invalid when value mismatches JavaScript-specific regular expression', () => {
element.pattern = '\\u1234\\cx[5-[]{2}';
userSetValue('\u1234\x18[4');
expect(element.validate()).to.be.false;
});
});
</script>
</body>

0 comments on commit eebe032

Please sign in to comment.