diff --git a/core/src/components/input/input.tsx b/core/src/components/input/input.tsx
index 2a9b9ec34e7..c70b2bb4009 100644
--- a/core/src/components/input/input.tsx
+++ b/core/src/components/input/input.tsx
@@ -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 `` element.
+ */
+ @Watch('dir')
+ onDirChanged(newValue: string) {
+ this.inheritedAttributes = {
+ ...this.inheritedAttributes,
+ dir: newValue,
+ };
+ forceUpdate(this);
+ }
+
componentWillLoad() {
this.inheritedAttributes = {
...inheritAriaAttributes(this.el),
- ...inheritAttributes(this.el, ['tabindex', 'title', 'data-form-type']),
+ ...inheritAttributes(this.el, ['tabindex', 'title', 'data-form-type', 'dir']),
};
}
diff --git a/core/src/components/input/test/input.spec.ts b/core/src/components/input/test/input.spec.ts
index 245e7dd492f..af9faac9f3c 100644
--- a/core/src/components/input/test/input.spec.ts
+++ b/core/src/components/input/test/input.spec.ts
@@ -44,6 +44,24 @@ describe('input: rendering', () => {
const bottomContent = page.body.querySelector('ion-input .input-bottom');
expect(bottomContent).toBe(null);
});
+
+ it('should inherit watched attributes', async () => {
+ const page = await newSpecPage({
+ components: [Input],
+ html: '',
+ });
+
+ 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');
+ });
});
/**
diff --git a/core/src/components/textarea/test/textarea.spec.ts b/core/src/components/textarea/test/textarea.spec.ts
index 5925a19c011..f1611a3e291 100644
--- a/core/src/components/textarea/test/textarea.spec.ts
+++ b/core/src/components/textarea/test/textarea.spec.ts
@@ -14,6 +14,24 @@ it('should inherit attributes', async () => {
expect(nativeEl.getAttribute('data-form-type')).toBe('password');
});
+it('should inherit watched attributes', async () => {
+ const page = await newSpecPage({
+ components: [Textarea],
+ html: '',
+ });
+
+ 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
* behavior will not exactly match IonSelect's slots.
diff --git a/core/src/components/textarea/textarea.tsx b/core/src/components/textarea/textarea.tsx
index 3349f0c1a85..7764dfba8b5 100644
--- a/core/src/components/textarea/textarea.tsx
+++ b/core/src/components/textarea/textarea.tsx
@@ -261,6 +261,22 @@ export class Textarea implements ComponentInterface {
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 `