mirror of
https://github.com/material-components/material-web.git
synced 2026-03-09 00:09:23 +08:00
feat(text-field): add min, max, and step
PiperOrigin-RevId: 470006182
This commit is contained in:
parent
ace7fc6a69
commit
c73b59cea4
@ -145,6 +145,12 @@ export abstract class TextField extends LitElement {
|
||||
}
|
||||
|
||||
// <input> properties
|
||||
/**
|
||||
* Defines the greatest value in the range of permitted values.
|
||||
*
|
||||
* https://developer.mozilla.org/en-US/docs/Web/HTML/Element/input#max
|
||||
*/
|
||||
@property({type: String}) max = '';
|
||||
/**
|
||||
* The maximum number of characters a user can enter into the text field. Set
|
||||
* to -1 for none.
|
||||
@ -152,6 +158,12 @@ export abstract class TextField extends LitElement {
|
||||
* https://developer.mozilla.org/en-US/docs/Web/HTML/Element/input#maxlength
|
||||
*/
|
||||
@property({type: Number}) maxLength = -1;
|
||||
/**
|
||||
* Defines the most negative value in the range of permitted values.
|
||||
*
|
||||
* https://developer.mozilla.org/en-US/docs/Web/HTML/Element/input#min
|
||||
*/
|
||||
@property({type: String}) min = '';
|
||||
/**
|
||||
* The minimum number of characters a user can enter into the text field. Set
|
||||
* to -1 for none.
|
||||
@ -193,6 +205,14 @@ export abstract class TextField extends LitElement {
|
||||
this.getInput().selectionStart = value;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns or sets the element's step attribute, which works with min and max
|
||||
* to limit the increments at which a numeric or date-time value can be set.
|
||||
*
|
||||
* https://developer.mozilla.org/en-US/docs/Web/HTML/Element/input#step
|
||||
*/
|
||||
@property({type: String}) step = '';
|
||||
|
||||
// TODO(b/237284412): replace with exported types
|
||||
@property({type: String, reflect: true})
|
||||
type: 'email'|'number'|'password'|'search'|'tel'|'text'|'url'|'color'|'date'|
|
||||
@ -379,6 +399,34 @@ export abstract class TextField extends LitElement {
|
||||
this.getInput().setSelectionRange(start, end, direction);
|
||||
}
|
||||
|
||||
/**
|
||||
* Decrements the value of a numeric type text field by `step` or `n` `step`
|
||||
* number of times.
|
||||
*
|
||||
* https://developer.mozilla.org/en-US/docs/Web/API/HTMLInputElement/stepDown
|
||||
*
|
||||
* @param stepDecrement The number of steps to decrement, defaults to 1.
|
||||
*/
|
||||
stepDown(stepDecrement?: number) {
|
||||
const input = this.getInput();
|
||||
input.stepDown(stepDecrement);
|
||||
this.value = input.value;
|
||||
}
|
||||
|
||||
/**
|
||||
* Increments the value of a numeric type text field by `step` or `n` `step`
|
||||
* number of times.
|
||||
*
|
||||
* https://developer.mozilla.org/en-US/docs/Web/API/HTMLInputElement/stepUp
|
||||
*
|
||||
* @param stepIncrement The number of steps to increment, defaults to 1.
|
||||
*/
|
||||
stepUp(stepIncrement?: number) {
|
||||
const input = this.getInput();
|
||||
input.stepUp(stepIncrement);
|
||||
this.value = input.value;
|
||||
}
|
||||
|
||||
/**
|
||||
* Reset the text field to its default value.
|
||||
*/
|
||||
@ -465,10 +513,15 @@ export abstract class TextField extends LitElement {
|
||||
const ariaExpandedValue = this.ariaExpanded || undefined;
|
||||
const ariaLabelValue = this.ariaLabel || this.label || undefined;
|
||||
const ariaLabelledByValue = this.ariaLabelledBy || undefined;
|
||||
const maxValue = this.max || undefined;
|
||||
const maxLengthValue = this.maxLength > -1 ? this.maxLength : undefined;
|
||||
const minValue = this.min || undefined;
|
||||
const minLengthValue = this.minLength > -1 ? this.minLength : undefined;
|
||||
const roleValue = this.role || undefined;
|
||||
const stepValue = this.step || undefined;
|
||||
|
||||
// TODO(b/243805848): remove `as unknown as number` once lit analyzer is
|
||||
// fixed
|
||||
return html`<input
|
||||
class="md3-text-field__input"
|
||||
aria-activedescendant=${ifDefined(ariaActiveDescendantValue)}
|
||||
@ -480,12 +533,15 @@ export abstract class TextField extends LitElement {
|
||||
aria-label=${ifDefined(ariaLabelValue)}
|
||||
aria-labelledby=${ifDefined(ariaLabelledByValue)}
|
||||
?disabled=${this.disabled}
|
||||
max=${ifDefined(maxValue as unknown as number)}
|
||||
maxlength=${ifDefined(maxLengthValue)}
|
||||
min=${ifDefined(minValue as unknown as number)}
|
||||
minlength=${ifDefined(minLengthValue)}
|
||||
placeholder=${ifDefined(placeholderValue)}
|
||||
role=${ifDefined(roleValue)}
|
||||
?readonly=${this.readonly}
|
||||
?required=${this.required}
|
||||
step=${ifDefined(stepValue as unknown as number)}
|
||||
type=${this.type}
|
||||
.value=${live(this.value)}
|
||||
@change=${this.redispatchEvent}
|
||||
|
||||
@ -396,6 +396,81 @@ describe('TextField', () => {
|
||||
.toBeFalse();
|
||||
});
|
||||
});
|
||||
|
||||
describe('min, max, and step', () => {
|
||||
it('should set attribute on input', async () => {
|
||||
const {testElement, input} = await setupTest();
|
||||
testElement.type = 'number';
|
||||
testElement.min = '2';
|
||||
testElement.max = '5';
|
||||
testElement.step = '1';
|
||||
await env.waitForStability();
|
||||
|
||||
expect(input.getAttribute('min')).withContext('min').toEqual('2');
|
||||
expect(input.getAttribute('max')).withContext('max').toEqual('5');
|
||||
expect(input.getAttribute('step')).withContext('step').toEqual('1');
|
||||
});
|
||||
|
||||
it('should not set attribute if value is empty', async () => {
|
||||
const {testElement, input} = await setupTest();
|
||||
testElement.type = 'number';
|
||||
testElement.min = '2';
|
||||
testElement.max = '5';
|
||||
testElement.step = '1';
|
||||
await env.waitForStability();
|
||||
|
||||
expect(input.hasAttribute('min'))
|
||||
.withContext('should have min')
|
||||
.toBeTrue();
|
||||
expect(input.hasAttribute('max'))
|
||||
.withContext('should have max')
|
||||
.toBeTrue();
|
||||
expect(input.hasAttribute('step'))
|
||||
.withContext('should have step')
|
||||
.toBeTrue();
|
||||
|
||||
testElement.min = '';
|
||||
testElement.max = '';
|
||||
testElement.step = '';
|
||||
await env.waitForStability();
|
||||
|
||||
expect(input.hasAttribute('min'))
|
||||
.withContext('should not have min')
|
||||
.toBeFalse();
|
||||
expect(input.hasAttribute('max'))
|
||||
.withContext('should not have max')
|
||||
.toBeFalse();
|
||||
expect(input.hasAttribute('step'))
|
||||
.withContext('should not have step')
|
||||
.toBeFalse();
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
describe('stepUp()', () => {
|
||||
it('should increment the value by `step`', async () => {
|
||||
const {testElement} = await setupTest();
|
||||
testElement.type = 'number';
|
||||
testElement.valueAsNumber = 10;
|
||||
testElement.step = '5';
|
||||
|
||||
testElement.stepUp();
|
||||
|
||||
expect(testElement.valueAsNumber).toEqual(15);
|
||||
});
|
||||
});
|
||||
|
||||
describe('stepDown()', () => {
|
||||
it('should decrement the value by `step`', async () => {
|
||||
const {testElement} = await setupTest();
|
||||
testElement.type = 'number';
|
||||
testElement.valueAsNumber = 10;
|
||||
testElement.step = '5';
|
||||
|
||||
testElement.stepDown();
|
||||
|
||||
expect(testElement.valueAsNumber).toEqual(5);
|
||||
});
|
||||
});
|
||||
|
||||
// TODO(b/235238545): Add shared FormController tests.
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user