feat(input, textarea): dir is inherited to native form control (#30102)

Issue number: resolves #30193 resolves #29577

Co-authored-by: Maria Hutt <thetaPC@users.noreply.github.com>
This commit is contained in:
Mehran Poursadeghi
2025-03-11 15:50:07 -07:00
committed by GitHub
parent 18e26acb01
commit 504fb6a25f
4 changed files with 70 additions and 2 deletions

View File

@ -338,10 +338,26 @@ export class Input implements ComponentInterface {
} }
} }
/**
* dir is a globally enumerated attribute.
* As a result, creating these as properties
* can have unintended side effects. Instead, we
* listen for attribute changes and inherit them
* to the inner `<input>` element.
*/
@Watch('dir')
onDirChanged(newValue: string) {
this.inheritedAttributes = {
...this.inheritedAttributes,
dir: newValue,
};
forceUpdate(this);
}
componentWillLoad() { componentWillLoad() {
this.inheritedAttributes = { this.inheritedAttributes = {
...inheritAriaAttributes(this.el), ...inheritAriaAttributes(this.el),
...inheritAttributes(this.el, ['tabindex', 'title', 'data-form-type']), ...inheritAttributes(this.el, ['tabindex', 'title', 'data-form-type', 'dir']),
}; };
} }

View File

@ -44,6 +44,24 @@ describe('input: rendering', () => {
const bottomContent = page.body.querySelector('ion-input .input-bottom'); const bottomContent = page.body.querySelector('ion-input .input-bottom');
expect(bottomContent).toBe(null); expect(bottomContent).toBe(null);
}); });
it('should inherit watched attributes', async () => {
const page = await newSpecPage({
components: [Input],
html: '<ion-input label="Input" dir="ltr"></ion-input>',
});
const inputEl = page.body.querySelector('ion-input')!;
const nativeEl = inputEl.querySelector('input')!;
expect(nativeEl.getAttribute('dir')).toBe('ltr');
inputEl.setAttribute('dir', 'rtl');
await page.waitForChanges();
expect(nativeEl.getAttribute('dir')).toBe('rtl');
});
}); });
/** /**

View File

@ -14,6 +14,24 @@ it('should inherit attributes', async () => {
expect(nativeEl.getAttribute('data-form-type')).toBe('password'); expect(nativeEl.getAttribute('data-form-type')).toBe('password');
}); });
it('should inherit watched attributes', async () => {
const page = await newSpecPage({
components: [Textarea],
html: '<ion-textarea dir="ltr"></ion-textarea>',
});
const textareaEl = page.body.querySelector('ion-textarea')!;
const nativeEl = textareaEl.querySelector('textarea')!;
expect(nativeEl.getAttribute('dir')).toBe('ltr');
textareaEl.setAttribute('dir', 'rtl');
await page.waitForChanges();
expect(nativeEl.getAttribute('dir')).toBe('rtl');
});
/** /**
* Textarea uses emulated slots, so the internal * Textarea uses emulated slots, so the internal
* behavior will not exactly match IonSelect's slots. * behavior will not exactly match IonSelect's slots.

View File

@ -261,6 +261,22 @@ export class Textarea implements ComponentInterface {
this.runAutoGrow(); this.runAutoGrow();
} }
/**
* dir is a globally enumerated attribute.
* As a result, creating these as properties
* can have unintended side effects. Instead, we
* listen for attribute changes and inherit them
* to the inner `<textarea>` element.
*/
@Watch('dir')
onDirChanged(newValue: string) {
this.inheritedAttributes = {
...this.inheritedAttributes,
dir: newValue,
};
forceUpdate(this);
}
/** /**
* The `ionChange` event is fired when the user modifies the textarea's value. * The `ionChange` event is fired when the user modifies the textarea's value.
* Unlike the `ionInput` event, the `ionChange` event is fired when * Unlike the `ionInput` event, the `ionChange` event is fired when
@ -331,7 +347,7 @@ export class Textarea implements ComponentInterface {
componentWillLoad() { componentWillLoad() {
this.inheritedAttributes = { this.inheritedAttributes = {
...inheritAriaAttributes(this.el), ...inheritAriaAttributes(this.el),
...inheritAttributes(this.el, ['data-form-type', 'title', 'tabindex']), ...inheritAttributes(this.el, ['data-form-type', 'title', 'tabindex', 'dir']),
}; };
} }