diff --git a/textfield/lib/text-field.ts b/textfield/lib/text-field.ts
index 6c9194c9e9..f6887542b5 100644
--- a/textfield/lib/text-field.ts
+++ b/textfield/lib/text-field.ts
@@ -66,18 +66,8 @@ export abstract class TextField extends LitElement {
@property({type: Boolean, reflect: true}) required = false;
/**
* The current value of the text field. It is always a string.
- *
- * This is equal to `defaultValue` before user input.
*/
@property() value = '';
- /**
- * The default value of the text field. Before user input, changing the
- * default value will update `value` as well.
- *
- * When the text field is reset, its `value` will be set to this default
- * value.
- */
- @property() defaultValue = '';
/**
* An optional prefix to display before the input value.
*/
@@ -267,19 +257,6 @@ export abstract class TextField extends LitElement {
* to screen readers.
*/
@state() private refreshErrorAlert = false;
- /**
- * Returns true when the text field's `value` property has been changed from
- * it's initial value.
- *
- * Setting `value` should always overwrite `defaultValue`, even when `value`
- * is an empty string. This flag ensures that behavior.
- */
- @state() private valueHasChanged = false;
- /**
- * Whether or not to ignore the next `value` change when computing
- * `valueHasChanged`.
- */
- private ignoreNextValueChange = false;
/**
* Whether or not a native error has been reported via `reportValidity()`.
*/
@@ -462,27 +439,20 @@ export abstract class TextField extends LitElement {
*/
reset() {
this.dirty = false;
- this.valueHasChanged = false;
- this.ignoreNextValueChange = true;
- this.value = this.defaultValue;
+ this.value = this.getAttribute('value') ?? '';
this.nativeError = false;
this.nativeErrorText = '';
}
- protected override update(changedProperties: PropertyValues) {
- // Consider a value change anything that is not the initial empty string
- // value.
- const valueHasChanged = changedProperties.has('value') &&
- changedProperties.get('value') !== undefined;
- if (valueHasChanged && !this.ignoreNextValueChange) {
- this.valueHasChanged = true;
- }
-
- if (this.ignoreNextValueChange) {
- this.ignoreNextValueChange = false;
+ override attributeChangedCallback(
+ attribute: string, newValue: string|null, oldValue: string|null) {
+ if (attribute === 'value' && this.dirty) {
+ // After user input, changing the value attribute no longer updates the
+ // text field's value (until reset). This matches native behavior.
+ return;
}
- super.update(changedProperties);
+ super.attributeChangedCallback(attribute, newValue, oldValue);
}
protected override render() {
@@ -505,9 +475,6 @@ export abstract class TextField extends LitElement {
// value to change without dispatching an event, re-sync it.
const value = this.getInput().value;
if (this.value !== value) {
- // Don't consider these updates (such as setting `defaultValue`) as
- // the developer directly changing the `value`.
- this.ignoreNextValueChange = true;
// Note this is typically inefficient in updated() since it schedules
// another update. However, it is needed for the to fully render
// before checking its value.
@@ -536,7 +503,7 @@ export abstract class TextField extends LitElement {
?hasEnd=${this.hasTrailingIcon}
?hasStart=${this.hasLeadingIcon}
.label=${this.label}
- ?populated=${!!this.getInputValue()}
+ ?populated=${!!this.value}
?required=${this.required}
>
${this.renderLeadingIcon()}
@@ -588,22 +555,13 @@ export abstract class TextField extends LitElement {
?required=${this.required}
step=${(this.step || nothing) as unknown as number}
type=${this.type}
- .value=${live(this.getInputValue())}
+ .value=${live(this.value)}
@change=${this.redispatchEvent}
@input=${this.handleInput}
@select=${this.redispatchEvent}
>`;
}
- private getInputValue() {
- const alwaysShowValue = this.dirty || this.valueHasChanged;
- if (alwaysShowValue) {
- return this.value;
- }
-
- return this.defaultValue || this.value;
- }
-
private getAriaDescribedBy() {
const ids: string[] = [];
if (this.getSupportingText()) {
diff --git a/textfield/lib/text-field_test.ts b/textfield/lib/text-field_test.ts
index 26846026b4..0a98e8e741 100644
--- a/textfield/lib/text-field_test.ts
+++ b/textfield/lib/text-field_test.ts
@@ -147,14 +147,15 @@ describe('TextField', () => {
describe('resetting the input', () => {
it('should set value back to default value', async () => {
const {harness} = await setupTest();
- harness.element.defaultValue = 'Default';
+ harness.element.setAttribute('value', 'Default');
await env.waitForStability();
+ expect(harness.element.value).toBe('Default');
await harness.deleteValue();
await harness.inputValue('Value');
+ expect(harness.element.value).toBe('Value');
harness.element.reset();
- expect(harness.element.defaultValue).toBe('Default');
expect(harness.element.value).toBe('Default');
});
@@ -164,37 +165,15 @@ describe('TextField', () => {
await harness.inputValue('Value');
harness.element.reset();
- expect(harness.element.defaultValue).toBe('');
expect(harness.element.value).toBe('');
});
-
- it('should allow defaultValue to update value again', async () => {
- const {harness} = await setupTest();
-
- // defaultValue changes value
- harness.element.defaultValue = 'First default';
- await env.waitForStability();
- expect(harness.element.value).toBe('First default');
-
- // Setting value programmatically causes it to stick
- harness.element.value = 'Value';
- harness.element.defaultValue = 'Second default';
- await env.waitForStability();
- expect(harness.element.value).toBe('Value');
-
- // Resetting should return to original functionality
- harness.element.reset();
- harness.element.defaultValue = 'Third default';
- await env.waitForStability();
- expect(harness.element.value).toBe('Third default');
- });
});
describe('default value', () => {
it('should update `value` before user input', async () => {
const {harness} = await setupTest();
- harness.element.defaultValue = 'Default';
+ harness.element.setAttribute('value', 'Default');
await env.waitForStability();
expect(harness.element.value).toBe('Default');
@@ -203,9 +182,9 @@ describe('TextField', () => {
it('should update `value` multiple times', async () => {
const {harness} = await setupTest();
- harness.element.defaultValue = 'First default';
+ harness.element.setAttribute('value', 'First default');
await env.waitForStability();
- harness.element.defaultValue = 'Second default';
+ harness.element.setAttribute('value', 'Second default');
await env.waitForStability();
expect(harness.element.value).toBe('Second default');
@@ -214,22 +193,22 @@ describe('TextField', () => {
it('should NOT update `value` after user input', async () => {
const {harness} = await setupTest();
- harness.element.defaultValue = 'First default';
+ harness.element.setAttribute('value', 'First default');
await env.waitForStability();
await harness.deleteValue();
await harness.inputValue('Value');
- harness.element.defaultValue = 'Second default';
+ harness.element.setAttribute('value', 'Second default');
await env.waitForStability();
expect(harness.element.value).toBe('Value');
});
- it('should render `value` instead of `defaultValue` when `value` changes',
+ it('should render `value` instead of default value attribute when `value` changes',
async () => {
const {harness, input} = await setupTest();
- harness.element.defaultValue = 'Default';
+ harness.element.setAttribute('value', 'Default');
await env.waitForStability();
expect(input.value).toBe('Default');
@@ -240,7 +219,7 @@ describe('TextField', () => {
harness.element.value = '';
await env.waitForStability();
expect(input.value).toBe('');
- expect(harness.element.defaultValue).toBe('Default');
+ expect(harness.element.getAttribute('value')).toBe('Default');
});
});