mirror of
https://github.com/material-components/material-web.git
synced 2024-10-26 21:56:56 +03:00
fix(textfield): counter showing when max length is 0 or removed
Fixes #4998 This also fixes an error being thrown in text field's validator when minlength/maxlength change to out of bounds if they're not set in the correct order. PiperOrigin-RevId: 594013553
This commit is contained in:
parent
4ae9db6c67
commit
9973b90981
@ -47,11 +47,16 @@ export class Field extends LitElement {
|
||||
private readonly slottedAriaDescribedBy!: HTMLElement[];
|
||||
|
||||
private get counterText() {
|
||||
if (this.count < 0 || this.max < 0) {
|
||||
// Count and max are typed as number, but can be set to null when Lit removes
|
||||
// their attributes. These getters coerce back to a number for calculations.
|
||||
const countAsNumber = this.count ?? -1;
|
||||
const maxAsNumber = this.max ?? -1;
|
||||
// Counter does not show if count is negative, or max is negative or 0.
|
||||
if (countAsNumber < 0 || maxAsNumber <= 0) {
|
||||
return '';
|
||||
}
|
||||
|
||||
return `${this.count} / ${this.max}`;
|
||||
return `${countAsNumber} / ${maxAsNumber}`;
|
||||
}
|
||||
|
||||
private get supportingOrErrorText() {
|
||||
|
@ -181,14 +181,22 @@ export class TextFieldValidator extends Validator<TextFieldState> {
|
||||
// Use -1 to represent no minlength and maxlength, which is what the
|
||||
// platform input returns. However, it will throw an error if you try to
|
||||
// manually set it to -1.
|
||||
if (state.minLength > -1) {
|
||||
inputOrTextArea.minLength = state.minLength;
|
||||
//
|
||||
// While the type is `number`, it may actually be `null` at runtime.
|
||||
// `null > -1` is true since `null` coerces to `0`, so we default null and
|
||||
// undefined to -1.
|
||||
//
|
||||
// We set attributes instead of properties since setting a property may
|
||||
// throw an out of bounds error in relation to the other property.
|
||||
// Attributes will not throw errors while the state is updating.
|
||||
if ((state.minLength ?? -1) > -1) {
|
||||
inputOrTextArea.setAttribute('minlength', String(state.minLength));
|
||||
} else {
|
||||
inputOrTextArea.removeAttribute('minlength');
|
||||
}
|
||||
|
||||
if (state.maxLength > -1) {
|
||||
inputOrTextArea.maxLength = state.maxLength;
|
||||
if ((state.maxLength ?? -1) > -1) {
|
||||
inputOrTextArea.setAttribute('maxlength', String(state.maxLength));
|
||||
} else {
|
||||
inputOrTextArea.removeAttribute('maxlength');
|
||||
}
|
||||
|
@ -89,6 +89,38 @@ describe('TextFieldValidator', () => {
|
||||
expect(validity.valueMissing).withContext('valueMissing').toBeFalse();
|
||||
expect(validationMessage).withContext('validationMessage').toBe('');
|
||||
});
|
||||
|
||||
it('does not throw an error when setting minlength and maxlength out of bounds', () => {
|
||||
type WritableInputState = {
|
||||
-readonly [K in keyof InputState]: InputState[K];
|
||||
};
|
||||
|
||||
const state: WritableInputState = {
|
||||
type: 'text',
|
||||
value: '',
|
||||
required: true,
|
||||
pattern: '',
|
||||
min: '',
|
||||
max: '',
|
||||
minLength: 5,
|
||||
maxLength: 10,
|
||||
step: '',
|
||||
};
|
||||
|
||||
const validator = new TextFieldValidator(() => ({
|
||||
state,
|
||||
renderedControl: null,
|
||||
}));
|
||||
|
||||
// Compute initial validity with valid minlength of 5 and maxlength of 10
|
||||
validator.getValidity();
|
||||
// set to something that is out of bounds of current maxlength="10"
|
||||
state.minLength = 20;
|
||||
|
||||
expect(() => {
|
||||
validator.getValidity();
|
||||
}).not.toThrow();
|
||||
});
|
||||
});
|
||||
|
||||
describe('type="email"', () => {
|
||||
|
@ -585,6 +585,10 @@ export abstract class TextField extends textFieldBaseClass {
|
||||
// tslint:disable-next-line:no-any
|
||||
const autocomplete = this.autocomplete as any;
|
||||
|
||||
// These properties may be set to null if the attribute is removed, and
|
||||
// `null > -1` is incorrectly `true`.
|
||||
const hasMaxLength = (this.maxLength ?? -1) > -1;
|
||||
const hasMinLength = (this.minLength ?? -1) > -1;
|
||||
if (this.type === 'textarea') {
|
||||
return html`
|
||||
<textarea
|
||||
@ -595,8 +599,8 @@ export abstract class TextField extends textFieldBaseClass {
|
||||
aria-label=${ariaLabel}
|
||||
autocomplete=${autocomplete || nothing}
|
||||
?disabled=${this.disabled}
|
||||
maxlength=${this.maxLength > -1 ? this.maxLength : nothing}
|
||||
minlength=${this.minLength > -1 ? this.minLength : nothing}
|
||||
maxlength=${hasMaxLength ? this.maxLength : nothing}
|
||||
minlength=${hasMinLength ? this.minLength : nothing}
|
||||
placeholder=${this.placeholder || nothing}
|
||||
?readonly=${this.readOnly}
|
||||
?required=${this.required}
|
||||
@ -631,9 +635,9 @@ export abstract class TextField extends textFieldBaseClass {
|
||||
?disabled=${this.disabled}
|
||||
inputmode=${inputMode || nothing}
|
||||
max=${(this.max || nothing) as unknown as number}
|
||||
maxlength=${this.maxLength > -1 ? this.maxLength : nothing}
|
||||
maxlength=${hasMaxLength ? this.maxLength : nothing}
|
||||
min=${(this.min || nothing) as unknown as number}
|
||||
minlength=${this.minLength > -1 ? this.minLength : nothing}
|
||||
minlength=${hasMinLength ? this.minLength : nothing}
|
||||
pattern=${this.pattern || nothing}
|
||||
placeholder=${this.placeholder || nothing}
|
||||
?readonly=${this.readOnly}
|
||||
|
Loading…
Reference in New Issue
Block a user