feat(text-field): add min, max, and step

PiperOrigin-RevId: 470006182
This commit is contained in:
Elizabeth Mitchell 2022-08-25 09:18:55 -07:00 committed by Copybara-Service
parent ace7fc6a69
commit c73b59cea4
2 changed files with 131 additions and 0 deletions

View File

@ -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}

View File

@ -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.