Skip to content

Commit

Permalink
fix(text-field): disable validation check in setRequired (#2201)
Browse files Browse the repository at this point in the history
BREAKING CHANGE: removed setRequired and isRequired from foundation.
  • Loading branch information
Matty Goo authored Mar 2, 2018
1 parent c14d244 commit 0ba7d10
Show file tree
Hide file tree
Showing 6 changed files with 354 additions and 81 deletions.
131 changes: 100 additions & 31 deletions demos/text-field.html
Original file line number Diff line number Diff line change
Expand Up @@ -137,7 +137,7 @@ <h2>Password field with validation</h2>
<h2>Outlined Text Field</h2>
<div id="demo-tf-outlined-wrapper">
<div id="tf-outlined-example" class="mdc-text-field mdc-text-field--outlined" data-demo-no-auto-js>
<input required pattern=".{8,}" type="text" id="tf-outlined-input" class="mdc-text-field__input"
<input type="text" id="tf-outlined-input" class="mdc-text-field__input"
aria-controls="name-validation-message">
<label for="tf-outlined-input" class="mdc-floating-label">Your Name</label>
<div class="mdc-text-field__outline">
Expand All @@ -147,8 +147,9 @@ <h2>Outlined Text Field</h2>
</div>
<div class="mdc-text-field__idle-outline"></div>
</div>
<p class="mdc-text-field-helper-text mdc-text-field-helper-text--validation-msg">
Must be at least 8 characters
<p class="mdc-text-field-helper-text mdc-text-field-helper-text--validation-msg"
id="name-validation-message">
Helper Text (possibly validation message)
</p>
</div>
<div>
Expand All @@ -167,19 +168,32 @@ <h2>Outlined Text Field</h2>
<input id="outlined-dense" type="checkbox">
<label for="outlined-dense">Dense</label>
</div>
<div>
<input id="outlined-required" type="checkbox">
<label for="outlined-required">Required</label>
</div>
<div>
<input id="outlined-minlength" type="checkbox">
<label for="outlined-minlength">Must be at least 8 characters</label>
</div>
<div>
<input id="outlined-maxlength" type="checkbox">
<label for="outlined-maxlength">Must not exceed 10 characters</label>
</div>
</section>

<section class="example" id="text-field-box-example">
<h2>Text Field box</h2>
<div id="demo-tf-box-wrapper">
<div id="tf-box-example" class="mdc-text-field mdc-text-field--box" data-demo-no-auto-js>
<input type="text" id="tf-box" class="mdc-text-field__input"
aria-controls="name-validation-message">
aria-controls="box-name-validation-message">
<label for="tf-box" class="mdc-floating-label">Your Name</label>
<div class="mdc-line-ripple"></div>
</div>
<p class="mdc-text-field-helper-text mdc-text-field-helper-text--validation-msg"
id="box-text-field-helper-text">
<p class="mdc-text-field-helper-text"
id="box-name-validation-message"
style="display: none;">
Helper Text (possibly validation message)
</p>
</div>
Expand All @@ -201,7 +215,11 @@ <h2>Text Field box</h2>
</div>
<div>
<input id="box-required" type="checkbox">
<label for="box-required">Required and must be at least 8 characters</label>
<label for="box-required">Required</label>
</div>
<div>
<input id="box-pattern" type="checkbox">
<label for="box-pattern">Must be at least 8 characters</label>
</div>
<div>
<input id="box-use-helper-text" type="checkbox">
Expand All @@ -211,6 +229,12 @@ <h2>Text Field box</h2>
<input id="box-persistent-helper-text" type="checkbox" disabled>
<label for="box-persistent-helper-text">Make helper text persistent</label>
</div>
<div>
<input id="box-helper-text-as-validation" type="checkbox" disabled>
<label for="box-helper-text-as-validation">
Use helper text as validation message
</label>
</div>
</section>

<section class="example" id="demo-tf-icon-container">
Expand Down Expand Up @@ -283,7 +307,11 @@ <h2>Text Field - Leading/Trailing icons</h2>
</div>
<div>
<input id="required-leading-trailing" type="checkbox">
<label for="required-leading-trailing">Required and must be at least 8 characters</label>
<label for="required-leading-trailing">Required</label>
</div>
<div>
<input id="pattern-leading-trailing" type="checkbox">
<label for="pattern-leading-trailing">Must be at least 8 characters</label>
</div>
</section>

Expand Down Expand Up @@ -357,10 +385,24 @@ <h2>Full-Width Text Field and Textarea</h2>

<script src="/assets/material-components-web.js" async></script>
<script>
function toggleRequired(textfield, isRequired) {
textfield.required = isRequired;
}

function togglePattern(textfield, checked) {
textfield.pattern = checked ? '.{8,}' : '.*';
}

demoReady(function() {
var tfEl = document.getElementById('tf-outlined-example');
var tfHelperText = document.getElementById('tf-outlined-validation-msg');
var tf = new mdc.textField.MDCTextField(tfEl);
var outlinedInputEl = tfEl.querySelector('input');
var wrapper = document.getElementById('demo-tf-outlined-wrapper');
var plainHelperText = 'Helper Text (possibly validation message)';
var minLengthError = 'Must be at least 8 characters';
var maxLengthError = 'Must not exceed 10 characters';

document.getElementById('outlined-disable').addEventListener('change', function(evt) {
tf.disabled = evt.target.checked;
});
Expand All @@ -380,13 +422,31 @@ <h2>Full-Width Text Field and Textarea</h2>
tfEl.classList[evt.target.checked ? 'add' : 'remove']('mdc-text-field--dense');
tf.layout();
});
document.getElementById('outlined-required').addEventListener('change', function(evt) {
toggleRequired(tf, evt.target.checked);
});
document.getElementById('outlined-minlength').addEventListener('change', function(evt) {
tf.minLength = evt.target.checked ? 8 : 0;
});
document.getElementById('outlined-maxlength').addEventListener('change', function(evt) {
tf.maxLength = evt.target.checked ? 10 : -1;
});
outlinedInputEl.addEventListener('blur', function() {
if (outlinedInputEl.validity.tooShort) {
tf.helperTextContent = minLengthError;
} else if (outlinedInputEl.validity.tooLong) {
tf.helperTextContent = maxLengthError;
} else {
tf.helperTextContent = plainHelperText;
}
});
});

demoReady(function() {
var tfEl = document.getElementById('tf-box-example');
var tf = new mdc.textField.MDCTextField(tfEl);
var wrapper = document.getElementById('demo-tf-box-wrapper');
var helperText = document.getElementById('box-text-field-helper-text');
var helperText = document.getElementById('box-name-validation-message');

document.getElementById('box-disable').addEventListener('change', function(evt) {
tf.disabled = evt.target.checked;
Expand All @@ -413,19 +473,21 @@ <h2>Full-Width Text Field and Textarea</h2>
});

document.getElementById('box-required').addEventListener('change', function(evt) {
var target = evt.target;
var input = tfEl.querySelector('.mdc-text-field__input');
toggleRequired(tf, evt.target.checked);
});
document.getElementById('box-pattern').addEventListener('change', function(evt) {
var checked = evt.target.checked;
var requiredHelperText = 'Must be at least 8 characters';
var plainHelperText = 'Helper Text (possibly validation message)';
input.required = target.checked;
input.pattern = evt.target.checked ? '.{8,}' : '.*';
helperText.innerHTML = target.checked ? requiredHelperText : plainHelperText;
togglePattern(tf, checked);
tf.helperTextContent = checked ? requiredHelperText : plainHelperText;
});

document.getElementById('box-use-helper-text').addEventListener('change', function(evt) {
var target = evt.target;
helperText.style.display = target.checked ? 'block' : 'none';
document.getElementById('box-persistent-helper-text').disabled = !target.checked;
document.getElementById('box-helper-text-as-validation').disabled = !target.checked;
});

document.getElementById('box-persistent-helper-text').addEventListener('change', function(evt) {
Expand All @@ -434,6 +496,12 @@ <h2>Full-Width Text Field and Textarea</h2>
'mdc-text-field-helper-text--persistent'
);
});

document.getElementById('box-helper-text-as-validation').addEventListener('change', function(evt) {
helperText.classList[evt.target.checked ? 'add' : 'remove'](
'mdc-text-field-helper-text--validation-msg'
);
});
});

demoReady(function() {
Expand Down Expand Up @@ -510,10 +578,19 @@ <h2>Full-Width Text Field and Textarea</h2>
});

document.getElementById('required-leading-trailing').addEventListener('change', function(evt) {
[].slice.call(tfInputs).forEach(function(input) {
input.required = evt.target.checked;
input.pattern = evt.target.checked ? '.{8,}' : '.*';
});
var checked = evt.target.checked;
toggleRequired(tfBoxLeading, checked);
toggleRequired(tfBoxTrailing, checked);
toggleRequired(tfOutlinedLeading, checked);
toggleRequired(tfOutlinedTrailing, checked);
});

document.getElementById('pattern-leading-trailing').addEventListener('change', function(evt) {
var checked = evt.target.checked;
togglePattern(tfBoxLeading, checked);
togglePattern(tfBoxTrailing, checked);
togglePattern(tfOutlinedLeading, checked);
togglePattern(tfOutlinedTrailing, checked);
});

document.getElementById('leading-trailing-alternate-colors').addEventListener('change', function(evt) {
Expand Down Expand Up @@ -555,8 +632,7 @@ <h2>Full-Width Text Field and Textarea</h2>
tfRoot.classList[target.checked ? 'add' : 'remove']('mdc-text-field--dense');
});
document.getElementById('required').addEventListener('change', function(evt) {
var target = evt.target;
tfRoot.querySelector('.mdc-text-field__input').required = target.checked;
toggleRequired(tf, evt.target.checked);
});
document.getElementById('alternate-colors').addEventListener('change', function (evt) {
var target = evt.target;
Expand Down Expand Up @@ -604,13 +680,8 @@ <h2>Full-Width Text Field and Textarea</h2>
var target = evt.target;
tfRoot.classList[target.checked ? 'add' : 'remove']('demo-textarea');
});

document.getElementById('textarea-required').addEventListener('change', function(evt) {
var target = evt.target;
[].slice.call(tfRoot.querySelectorAll('.mdc-text-field__input'))
.forEach(function(input) {
input.required = target.checked;
})
toggleRequired(tf, evt.target.checked);
});
});

Expand All @@ -636,11 +707,9 @@ <h2>Full-Width Text Field and Textarea</h2>
});

document.getElementById('fullwidth-required').addEventListener('change', function(evt) {
var target = evt.target;
[].slice.call(section.querySelectorAll('.mdc-text-field__input'))
.forEach(function(input) {
input.required = target.checked;
})
var checked = evt.target.checked;
toggleRequired(tf, checked);
toggleRequired(tfMulti, checked);
});

document.getElementById('fullwidth-alternate-colors').addEventListener('change', function (evt) {
Expand Down
13 changes: 13 additions & 0 deletions packages/mdc-textfield/adapter.js
Original file line number Diff line number Diff line change
Expand Up @@ -106,6 +106,19 @@ class MDCTextFieldAdapter {
*/
deregisterInputInteractionHandler(evtType, handler) {}

/**
* Registers a validation attribute change listener on the input element.
* @param {function(!Array): undefined} handler
* @return {!MutationObserver}
*/
registerValidationAttributeChangeHandler(handler) {}

/**
* Disconnects a validation attribute observer on the input element.
* @param {!MutationObserver} observer
*/
deregisterValidationAttributeChangeHandler(observer) {}

/**
* Returns an object representing the native text input element, with a
* similar API shape. The object returned should include the value, disabled
Expand Down
46 changes: 29 additions & 17 deletions packages/mdc-textfield/foundation.js
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,12 @@ import MDCTextFieldOutlineFoundation from './outline/foundation';
import {cssClasses, strings, numbers} from './constants';


// whitelist based off of https://developer.mozilla.org/en-US/docs/Web/Guide/HTML/HTML5/Constraint_validation
// under section: `Validation-related attributes`
const VALIDATION_ATTR_WHITELIST = [
'pattern', 'min', 'max', 'required', 'step', 'minlength', 'maxlength',
];

/**
* @extends {MDCFoundation<!MDCTextFieldAdapter>}
* @final
Expand Down Expand Up @@ -71,6 +77,8 @@ class MDCTextFieldFoundation extends MDCFoundation {
deregisterTextFieldInteractionHandler: () => {},
registerInputInteractionHandler: () => {},
deregisterInputInteractionHandler: () => {},
registerValidationAttributeChangeHandler: () => {},
deregisterValidationAttributeChangeHandler: () => {},
getNativeInput: () => {},
isFocused: () => {},
isRtl: () => {},
Expand Down Expand Up @@ -116,6 +124,10 @@ class MDCTextFieldFoundation extends MDCFoundation {
this.setPointerXOffset_ = (evt) => this.setTransformOrigin(evt);
/** @private {function(!Event): undefined} */
this.textFieldInteractionHandler_ = () => this.handleTextFieldInteraction();
/** @private {function(!Array): undefined} */
this.validationAttributeChangeHandler_ = (mutations) => this.handleValidationAttributeMutation_(mutations);
/** @private {!MutationObserver} */
this.validationObserver_;
}

init() {
Expand All @@ -138,6 +150,8 @@ class MDCTextFieldFoundation extends MDCFoundation {
['click', 'keydown'].forEach((evtType) => {
this.adapter_.registerTextFieldInteractionHandler(evtType, this.textFieldInteractionHandler_);
});
this.validationObserver_ = this.adapter_.registerValidationAttributeChangeHandler(
this.validationAttributeChangeHandler_);
}

destroy() {
Expand All @@ -151,6 +165,7 @@ class MDCTextFieldFoundation extends MDCFoundation {
['click', 'keydown'].forEach((evtType) => {
this.adapter_.deregisterTextFieldInteractionHandler(evtType, this.textFieldInteractionHandler_);
});
this.adapter_.deregisterValidationAttributeChangeHandler(this.validationObserver_);
}

/**
Expand All @@ -163,6 +178,20 @@ class MDCTextFieldFoundation extends MDCFoundation {
this.receivedUserInput_ = true;
}

/**
* Handles validation attribute changes
* @param {Array<MutationRecord>} mutationsList
* @private
*/
handleValidationAttributeMutation_(mutationsList) {
mutationsList.some((mutation) => {
if (VALIDATION_ATTR_WHITELIST.indexOf(mutation.attributeName) > -1) {
this.styleValidity_(true);
return true;
}
});
}

/**
* Updates the focus outline for outlined text fields.
*/
Expand Down Expand Up @@ -297,23 +326,6 @@ class MDCTextFieldFoundation extends MDCFoundation {
this.styleDisabled_(disabled);
}

/**
* @return {boolean} True if the Text Field is required.
*/
isRequired() {
return this.getNativeInput_().required;
}

/**
* @param {boolean} isRequired Sets the text-field required or not.
*/
setRequired(isRequired) {
this.getNativeInput_().required = isRequired;
// Addition of the asterisk is automatic based on CSS, but validity checking
// needs to be manually run.
this.styleValidity_(this.isValid());
}

/**
* @param {string} content Sets the content of the helper text.
*/
Expand Down
Loading

0 comments on commit 0ba7d10

Please sign in to comment.