Skip to content
This repository has been archived by the owner on Jan 13, 2025. It is now read-only.

Commit

Permalink
feat(text-field): New API to enable/disable native input validation f…
Browse files Browse the repository at this point in the history
…or custom validity (#3084)

BREAKING CHANGE: Setting the validity state using `setValid` no longer ignores native input validation. New API `useNativeValidation` is introduced to enable / disable native validation for custom validity.
  • Loading branch information
abhiomkar authored Jul 30, 2018
1 parent e4e1222 commit bd49920
Show file tree
Hide file tree
Showing 6 changed files with 68 additions and 14 deletions.
25 changes: 25 additions & 0 deletions demos/text-field.html
Original file line number Diff line number Diff line change
Expand Up @@ -386,6 +386,20 @@ <h2>Full-Width Text Field and Textarea</h2>
<label for="fullwidth-alternate-colors">Alternate Colors</label>
</div>
</section>

<section class="example">
<h2>Custom error state behaviour - remove error state as soon as invalid input is resolved</h2>
<section id="demo-text-field-wrapper">
<div class="mdc-text-field" id="demo-textfield-error-state">
<input type="text" pattern="^\d{10}$" id="demo-textfield-error-state-input" class="mdc-text-field__input">
<label class="mdc-floating-label" for="demo-textfield-error-state-input">Phone number</label>
<div class="mdc-line-ripple"></div>
</div>
<p id="username-helper-text" class="mdc-text-field-helper-text mdc-text-field-helper-text--persistent mdc-text-field-helper-text--validation-msg" aria-hidden="true">
Enter 10 digit phone number including area code
</p>
</section>
</section>
</main>

<script src="/assets/material-components-web.js" async></script>
Expand Down Expand Up @@ -723,6 +737,17 @@ <h2>Full-Width Text Field and Textarea</h2>
tfMultiRoot.classList[target.checked ? 'add' : 'remove']('demo-textarea');
});
});

demoReady(function() {
var textFieldEl = document.querySelector('#demo-textfield-error-state');
var textField = new mdc.textField.MDCTextField(textFieldEl);

textFieldEl.addEventListener('input', function(event) {
if (textField.valid) {
textField.valid = true;
}
});
});
</script>
</body>
</html>
10 changes: 6 additions & 4 deletions packages/mdc-textfield/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -9,9 +9,9 @@ path: /catalog/input-controls/text-field/
## Important - Default Style Deprecation Notice

The existing default text field style will be changed in an upcoming release. The Material spec indicates that
the default style will be the filled variant (currently referred to as the box variant). This will become the
the default style will be the filled variant (currently referred to as the box variant). This will become the
default style. Continuing to add the `mdc-text-field--box` class to the text field will
result in no change.
result in no change.

# Text Field

Expand Down Expand Up @@ -255,6 +255,7 @@ Property | Value Type | Description
--- | --- | ---
`value` | String | Proxies to the foundation's `getValue`/`setValue` methods.
`disabled` | Boolean | Proxies to the foundation's `isDisabled`/`setDisabled` methods.
`useNativeValidation` | Boolean (write-only) | Proxies to the foundation's `setUseNativeValidation` method.
`valid` | Boolean | Proxies to the foundation's `isValid`/`setValid` methods.
`required` | Boolean | Proxies to the foundation's `isRequired`/`setRequired` methods.
`helperTextContent` | String | Proxies to the foundation's `setHelperTextContent` method when set.
Expand Down Expand Up @@ -303,8 +304,9 @@ Method Signature | Description
--- | ---
`getValue() => string` | Returns the input's value.
`setValue(value: string)` | Sets the input's value.
`isValid() => boolean` | If a custom validity is set, returns that value. Otherwise, returns the result of native validity checks.
`setValid(isValid: boolean)` | Sets custom validity. Once set, native validity checking is ignored.
`setUseNativeValidation(useNativeValidation: boolean)` | Sets whether to check native HTML validity state (`true`, default) or custom validity state when updating styles (`false`).
`setValid(isValid: boolean)` | Sets custom validity and updates styles accordingly. Note that native validation will still be honored subsequently unless `setUseNativeValidation(false)` is also called.
`isValid() => boolean` | Returns the component's current validity state (either native or custom, depending on how `setUseNativeValidation()` was configured).
`isDisabled() => boolean` | Returns whether or not the input is disabled.
`setDisabled(disabled: boolean) => void` | Updates the input's disabled state.
`isRequired() => boolean` | Returns whether the input is required.
Expand Down
23 changes: 17 additions & 6 deletions packages/mdc-textfield/foundation.js
Original file line number Diff line number Diff line change
Expand Up @@ -106,6 +106,10 @@ class MDCTextFieldFoundation extends MDCFoundation {
this.useCustomValidityChecking_ = false;
/** @private {boolean} */
this.isValid_ = true;

/** @private {boolean} */
this.useNativeValidation_ = true;

/** @private {function(): undefined} */
this.inputFocusHandler_ = () => this.activateFocus();
/** @private {function(): undefined} */
Expand Down Expand Up @@ -291,24 +295,31 @@ class MDCTextFieldFoundation extends MDCFoundation {
* Otherwise, returns the result of native validity checks.
*/
isValid() {
return this.useCustomValidityChecking_
? this.isValid_ : this.isNativeInputValid_();
return this.useNativeValidation_
? this.isNativeInputValid_() : this.isValid_;
}

/**
* @param {boolean} isValid Sets the validity state of the Text Field.
*/
setValid(isValid) {
this.useCustomValidityChecking_ = true;
this.isValid_ = isValid;
// Retrieve from the getter to ensure correct logic is applied.
isValid = this.isValid();
this.styleValidity_(isValid);

const shouldShake = !isValid && !this.isFocused_;
if (this.adapter_.hasLabel()) {
this.adapter_.shakeLabel(this.shouldShake);
this.adapter_.shakeLabel(shouldShake);
}
}

/**
* Enables or disables the use of native validation. Use this for custom validation.
* @param {boolean} useNativeValidation Set this to false to ignore native input validation.
*/
setUseNativeValidation(useNativeValidation) {
this.useNativeValidation_ = useNativeValidation;
}

/**
* @return {boolean} True if the Text Field is disabled.
*/
Expand Down
8 changes: 8 additions & 0 deletions packages/mdc-textfield/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -325,6 +325,14 @@ class MDCTextField extends MDCComponent {
this.foundation_.setIconContent(content);
}

/**
* Enables or disables the use of native validation. Use this for custom validation.
* @param {boolean} useNativeValidation Set this to false to ignore native input validation.
*/
set useNativeValidation(useNativeValidation) {
this.foundation_.setUseNativeValidation(useNativeValidation);
}

/**
* Recomputes the outline SVG path for the outline element.
*/
Expand Down
10 changes: 6 additions & 4 deletions test/unit/mdc-textfield/foundation.test.js
Original file line number Diff line number Diff line change
Expand Up @@ -166,8 +166,9 @@ test('#isValid for native validation', () => {
assert.isNotOk(foundation.isValid());
});

test('#setValid overrides native validation', () => {
test('#setValid overrides native validation when useNativeValidation set to false', () => {
const {foundation, nativeInput} = setupValueTest('', /* isValid */ false);
foundation.setUseNativeValidation(false);
foundation.setValid(true);
assert.isOk(foundation.isValid());

Expand Down Expand Up @@ -622,24 +623,25 @@ test('does not style label on blur if input has a value and hasLabel is false',
td.verify(mockAdapter.floatLabel(td.matchers.anything()), {times: 0});
});

test('on blur removes mdc-text-field--invalid if custom validity is false and' +
test('on blur removes mdc-text-field--invalid if useNativeValidation is true and' +
'input.checkValidity() returns true', () => {
const {mockAdapter, blur} = setupBlurTest();
blur();
td.verify(mockAdapter.removeClass(cssClasses.INVALID));
});

test('on blur adds mdc-textfied--invalid if custom validity is false and' +
test('on blur adds mdc-textfied--invalid if useNativeValidation is true and' +
'input.checkValidity() returns false', () => {
const {mockAdapter, blur, nativeInput} = setupBlurTest();
nativeInput.validity.valid = false;
blur();
td.verify(mockAdapter.addClass(cssClasses.INVALID));
});

test('on blur does not remove mdc-text-field--invalid if custom validity is true and' +
test('on blur does not remove mdc-text-field--invalid if useNativeValidation is false and' +
'input.checkValidity() returns true', () => {
const {foundation, mockAdapter, blur} = setupBlurTest();
foundation.setUseNativeValidation(false);
foundation.setValid(false);
blur();
td.verify(mockAdapter.removeClass(cssClasses.INVALID), {times: 0});
Expand Down
6 changes: 6 additions & 0 deletions test/unit/mdc-textfield/mdc-text-field.test.js
Original file line number Diff line number Diff line change
Expand Up @@ -422,6 +422,12 @@ test('get/set required', () => {
assert.isFalse(component.required);
});

test('set useNativeValidation', () => {
const {component, mockFoundation} = setupMockFoundationTest();
component.useNativeValidation = true;
td.verify(mockFoundation.setUseNativeValidation(true));
});

test('get/set pattern', () => {
const {component} = setupMockFoundationTest();
component.pattern = '.{8,}';
Expand Down

0 comments on commit bd49920

Please sign in to comment.