diff --git a/core/src/components/textarea/test/fill/textarea.e2e.ts b/core/src/components/textarea/test/fill/textarea.e2e.ts index 933806b9482..708b257d769 100644 --- a/core/src/components/textarea/test/fill/textarea.e2e.ts +++ b/core/src/components/textarea/test/fill/textarea.e2e.ts @@ -17,7 +17,7 @@ configs({ modes: ['md'] }).forEach(({ title, screenshot, config }) => { helper-text="Enter your email" maxlength="20" counter="true" - > + > `, config ); @@ -196,3 +196,58 @@ configs({ modes: ['md'], directions: ['ltr'] }).forEach(({ title, config }) => { }); }); }); + +configs({ modes: ['md'], directions: ['ltr'] }).forEach(({ title, screenshot, config }) => { + test.describe(title('textarea: label slot'), () => { + test('should render the notch correctly with a slotted label', async ({ page }) => { + await page.setContent( + ` + + +
My Label Content
+
+ `, + config + ); + + const textarea = page.locator('ion-textarea'); + expect(await textarea.screenshot()).toMatchSnapshot(screenshot(`textarea-fill-outline-slotted-label`)); + }); + test('should render the notch correctly with a slotted label after the textarea was originally hidden', async ({ + page, + }) => { + await page.setContent( + ` + + +
My Label Content
+
+ `, + config + ); + + const textarea = page.locator('ion-textarea'); + + await textarea.evaluate((el: HTMLIonSelectElement) => el.style.removeProperty('display')); + + expect(await textarea.screenshot()).toMatchSnapshot(screenshot(`textarea-fill-outline-hidden-slotted-label`)); + }); + }); +}); diff --git a/core/src/components/textarea/test/fill/textarea.e2e.ts-snapshots/textarea-fill-outline-hidden-slotted-label-md-ltr-Mobile-Chrome-linux.png b/core/src/components/textarea/test/fill/textarea.e2e.ts-snapshots/textarea-fill-outline-hidden-slotted-label-md-ltr-Mobile-Chrome-linux.png new file mode 100644 index 00000000000..a2f6303f5c4 Binary files /dev/null and b/core/src/components/textarea/test/fill/textarea.e2e.ts-snapshots/textarea-fill-outline-hidden-slotted-label-md-ltr-Mobile-Chrome-linux.png differ diff --git a/core/src/components/textarea/test/fill/textarea.e2e.ts-snapshots/textarea-fill-outline-hidden-slotted-label-md-ltr-Mobile-Firefox-linux.png b/core/src/components/textarea/test/fill/textarea.e2e.ts-snapshots/textarea-fill-outline-hidden-slotted-label-md-ltr-Mobile-Firefox-linux.png new file mode 100644 index 00000000000..0a625b60199 Binary files /dev/null and b/core/src/components/textarea/test/fill/textarea.e2e.ts-snapshots/textarea-fill-outline-hidden-slotted-label-md-ltr-Mobile-Firefox-linux.png differ diff --git a/core/src/components/textarea/test/fill/textarea.e2e.ts-snapshots/textarea-fill-outline-hidden-slotted-label-md-ltr-Mobile-Safari-linux.png b/core/src/components/textarea/test/fill/textarea.e2e.ts-snapshots/textarea-fill-outline-hidden-slotted-label-md-ltr-Mobile-Safari-linux.png new file mode 100644 index 00000000000..92dfc995c1a Binary files /dev/null and b/core/src/components/textarea/test/fill/textarea.e2e.ts-snapshots/textarea-fill-outline-hidden-slotted-label-md-ltr-Mobile-Safari-linux.png differ diff --git a/core/src/components/textarea/test/fill/textarea.e2e.ts-snapshots/textarea-fill-outline-slotted-label-md-ltr-Mobile-Chrome-linux.png b/core/src/components/textarea/test/fill/textarea.e2e.ts-snapshots/textarea-fill-outline-slotted-label-md-ltr-Mobile-Chrome-linux.png new file mode 100644 index 00000000000..a2f6303f5c4 Binary files /dev/null and b/core/src/components/textarea/test/fill/textarea.e2e.ts-snapshots/textarea-fill-outline-slotted-label-md-ltr-Mobile-Chrome-linux.png differ diff --git a/core/src/components/textarea/test/fill/textarea.e2e.ts-snapshots/textarea-fill-outline-slotted-label-md-ltr-Mobile-Firefox-linux.png b/core/src/components/textarea/test/fill/textarea.e2e.ts-snapshots/textarea-fill-outline-slotted-label-md-ltr-Mobile-Firefox-linux.png new file mode 100644 index 00000000000..0a625b60199 Binary files /dev/null and b/core/src/components/textarea/test/fill/textarea.e2e.ts-snapshots/textarea-fill-outline-slotted-label-md-ltr-Mobile-Firefox-linux.png differ diff --git a/core/src/components/textarea/test/fill/textarea.e2e.ts-snapshots/textarea-fill-outline-slotted-label-md-ltr-Mobile-Safari-linux.png b/core/src/components/textarea/test/fill/textarea.e2e.ts-snapshots/textarea-fill-outline-slotted-label-md-ltr-Mobile-Safari-linux.png new file mode 100644 index 00000000000..92dfc995c1a Binary files /dev/null and b/core/src/components/textarea/test/fill/textarea.e2e.ts-snapshots/textarea-fill-outline-slotted-label-md-ltr-Mobile-Safari-linux.png differ diff --git a/core/src/components/textarea/textarea.md.outline.scss b/core/src/components/textarea/textarea.md.outline.scss index 0493cbc5bf1..76f20279ae4 100644 --- a/core/src/components/textarea/textarea.md.outline.scss +++ b/core/src/components/textarea/textarea.md.outline.scss @@ -176,6 +176,16 @@ opacity: 0; pointer-events: none; + + /** + * The spacer currently inherits + * border-box sizing from the Ionic reset styles. + * However, we do not want to include padding in + * the calculation of the element dimensions. + * This code can be removed if textarea is updated + * to use the Shadow DOM. + */ + box-sizing: content-box; } :host(.textarea-fill-outline) .textarea-outline-start { diff --git a/core/src/components/textarea/textarea.tsx b/core/src/components/textarea/textarea.tsx index 3948b2daf7b..939bca3b9ea 100644 --- a/core/src/components/textarea/textarea.tsx +++ b/core/src/components/textarea/textarea.tsx @@ -1,7 +1,7 @@ import type { ComponentInterface, EventEmitter } from '@stencil/core'; import { Build, Component, Element, Event, Host, Method, Prop, State, Watch, h, writeTask } from '@stencil/core'; -import type { LegacyFormController } from '@utils/forms'; -import { createLegacyFormController } from '@utils/forms'; +import type { LegacyFormController, NotchController } from '@utils/forms'; +import { createLegacyFormController, createNotchController } from '@utils/forms'; import type { Attributes } from '@utils/helpers'; import { inheritAriaAttributes, debounceEvent, findItemLabel, inheritAttributes } from '@utils/helpers'; import { printIonWarning } from '@utils/logging'; @@ -40,6 +40,9 @@ export class Textarea implements ComponentInterface { private inheritedAttributes: Attributes = {}; private originalIonInput?: EventEmitter; private legacyFormController!: LegacyFormController; + private notchSpacerEl: HTMLElement | undefined; + + private notchController?: NotchController; // This flag ensures we log the deprecation warning at most once. private hasLoggedDeprecationWarning = false; @@ -292,6 +295,11 @@ export class Textarea implements ComponentInterface { connectedCallback() { const { el } = this; this.legacyFormController = createLegacyFormController(el); + this.notchController = createNotchController( + el, + () => this.notchSpacerEl, + () => this.labelSlot + ); this.emitStyle(); this.debounceChanged(); if (Build.isBrowser) { @@ -311,6 +319,11 @@ export class Textarea implements ComponentInterface { }) ); } + + if (this.notchController) { + this.notchController.destroy(); + this.notchController = undefined; + } } componentWillLoad() { @@ -325,6 +338,10 @@ export class Textarea implements ComponentInterface { this.runAutoGrow(); } + componentDidRender() { + this.notchController?.calculateNotchWidth(); + } + /** * Sets focus on the native `textarea` in `ion-textarea`. Use this method instead of the global * `textarea.focus()`. @@ -591,7 +608,7 @@ Developers can use the "legacy" property to continue using the legacy form marku 'textarea-outline-notch-hidden': !this.hasLabel, }} > - diff --git a/core/src/utils/forms/notch-controller.ts b/core/src/utils/forms/notch-controller.ts index 1507f1f3763..0ee2a2805e1 100644 --- a/core/src/utils/forms/notch-controller.ts +++ b/core/src/utils/forms/notch-controller.ts @@ -1,7 +1,7 @@ import { win } from '@utils/browser'; import { raf } from '@utils/helpers'; -type NotchElement = HTMLIonInputElement | HTMLIonSelectElement; +type NotchElement = HTMLIonInputElement | HTMLIonSelectElement | HTMLIonTextareaElement; /** * A utility to calculate the size of an outline notch