Skip to content

Commit

Permalink
feat(text-field): add min, max, and step
Browse files Browse the repository at this point in the history
PiperOrigin-RevId: 470006182
  • Loading branch information
asyncLiz authored and copybara-github committed Aug 25, 2022
1 parent ace7fc6 commit c73b59c
Show file tree
Hide file tree
Showing 2 changed files with 131 additions and 0 deletions.
56 changes: 56 additions & 0 deletions textfield/lib/text-field.ts
Original file line number Diff line number Diff line change
Expand Up @@ -145,13 +145,25 @@ 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.
*
* 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.
Expand Down Expand Up @@ -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'|
Expand Down Expand Up @@ -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.
*/
Expand Down Expand Up @@ -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)}
Expand All @@ -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}
Expand Down
75 changes: 75 additions & 0 deletions textfield/lib/text-field_test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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.
Expand Down

0 comments on commit c73b59c

Please sign in to comment.