mirror of
https://github.com/material-components/material-web.git
synced 2024-09-11 21:57:41 +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 readonly slottedAriaDescribedBy!: HTMLElement[];
|
||||||
|
|
||||||
private get counterText() {
|
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 '';
|
||||||
}
|
}
|
||||||
|
|
||||||
return `${this.count} / ${this.max}`;
|
return `${countAsNumber} / ${maxAsNumber}`;
|
||||||
}
|
}
|
||||||
|
|
||||||
private get supportingOrErrorText() {
|
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
|
// 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
|
// platform input returns. However, it will throw an error if you try to
|
||||||
// manually set it to -1.
|
// 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 {
|
} else {
|
||||||
inputOrTextArea.removeAttribute('minlength');
|
inputOrTextArea.removeAttribute('minlength');
|
||||||
}
|
}
|
||||||
|
|
||||||
if (state.maxLength > -1) {
|
if ((state.maxLength ?? -1) > -1) {
|
||||||
inputOrTextArea.maxLength = state.maxLength;
|
inputOrTextArea.setAttribute('maxlength', String(state.maxLength));
|
||||||
} else {
|
} else {
|
||||||
inputOrTextArea.removeAttribute('maxlength');
|
inputOrTextArea.removeAttribute('maxlength');
|
||||||
}
|
}
|
||||||
|
@ -89,6 +89,38 @@ describe('TextFieldValidator', () => {
|
|||||||
expect(validity.valueMissing).withContext('valueMissing').toBeFalse();
|
expect(validity.valueMissing).withContext('valueMissing').toBeFalse();
|
||||||
expect(validationMessage).withContext('validationMessage').toBe('');
|
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"', () => {
|
describe('type="email"', () => {
|
||||||
|
@ -585,6 +585,10 @@ export abstract class TextField extends textFieldBaseClass {
|
|||||||
// tslint:disable-next-line:no-any
|
// tslint:disable-next-line:no-any
|
||||||
const autocomplete = this.autocomplete as 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') {
|
if (this.type === 'textarea') {
|
||||||
return html`
|
return html`
|
||||||
<textarea
|
<textarea
|
||||||
@ -595,8 +599,8 @@ export abstract class TextField extends textFieldBaseClass {
|
|||||||
aria-label=${ariaLabel}
|
aria-label=${ariaLabel}
|
||||||
autocomplete=${autocomplete || nothing}
|
autocomplete=${autocomplete || nothing}
|
||||||
?disabled=${this.disabled}
|
?disabled=${this.disabled}
|
||||||
maxlength=${this.maxLength > -1 ? this.maxLength : nothing}
|
maxlength=${hasMaxLength ? this.maxLength : nothing}
|
||||||
minlength=${this.minLength > -1 ? this.minLength : nothing}
|
minlength=${hasMinLength ? this.minLength : nothing}
|
||||||
placeholder=${this.placeholder || nothing}
|
placeholder=${this.placeholder || nothing}
|
||||||
?readonly=${this.readOnly}
|
?readonly=${this.readOnly}
|
||||||
?required=${this.required}
|
?required=${this.required}
|
||||||
@ -631,9 +635,9 @@ export abstract class TextField extends textFieldBaseClass {
|
|||||||
?disabled=${this.disabled}
|
?disabled=${this.disabled}
|
||||||
inputmode=${inputMode || nothing}
|
inputmode=${inputMode || nothing}
|
||||||
max=${(this.max || nothing) as unknown as number}
|
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}
|
min=${(this.min || nothing) as unknown as number}
|
||||||
minlength=${this.minLength > -1 ? this.minLength : nothing}
|
minlength=${hasMinLength ? this.minLength : nothing}
|
||||||
pattern=${this.pattern || nothing}
|
pattern=${this.pattern || nothing}
|
||||||
placeholder=${this.placeholder || nothing}
|
placeholder=${this.placeholder || nothing}
|
||||||
?readonly=${this.readOnly}
|
?readonly=${this.readOnly}
|
||||||
|
Loading…
Reference in New Issue
Block a user