Skip to content

Commit

Permalink
fix(textarea): textarea with autogrow will size to its contents (#24205)
Browse files Browse the repository at this point in the history
fix(textarea): textarea with autogrow will size to its contents (#24205)
  • Loading branch information
liamdebeasi authored Jul 27, 2022
2 parents 2cba460 + 40aba8c commit 7a342ab
Show file tree
Hide file tree
Showing 17 changed files with 56 additions and 34 deletions.
4 changes: 2 additions & 2 deletions core/src/components.d.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2803,7 +2803,7 @@ export namespace Components {
}
interface IonTextarea {
/**
* If `true`, the element height will increase based on the value.
* If `true`, the textarea container will grow and shrink based on the contents of the textarea.
*/
"autoGrow": boolean;
/**
Expand Down Expand Up @@ -6786,7 +6786,7 @@ declare namespace LocalJSX {
}
interface IonTextarea {
/**
* If `true`, the element height will increase based on the value.
* If `true`, the textarea container will grow and shrink based on the contents of the textarea.
*/
"autoGrow"?: boolean;
/**
Expand Down
4 changes: 1 addition & 3 deletions core/src/components/textarea/test/autogrow/textarea.e2e.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,11 +2,9 @@ import { expect } from '@playwright/test';
import { test } from '@utils/test/playwright';

test.describe('textarea: autogrow', () => {
test.skip('should not have visual regressions', async ({ page }) => {
test('should not have visual regressions', async ({ page }) => {
await page.goto(`/src/components/textarea/test/autogrow`);

await page.waitForChanges();

await page.setIonViewport();

expect(await page.screenshot()).toMatchSnapshot(`textarea-autogrow-diff-${page.getSnapshotSettings()}.png`);
Expand Down
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
6 changes: 0 additions & 6 deletions core/src/components/textarea/test/basic/index.html
Original file line number Diff line number Diff line change
Expand Up @@ -67,12 +67,6 @@
<ion-label color="primary">Clear on Edit</ion-label>
<ion-textarea clear-on-edit="true"></ion-textarea>
</ion-item>

<!-- TODO: Re-add auto grow with PR#24205 -->
<!-- <ion-item>
<ion-label color="primary">Autogrow</ion-label>
<ion-textarea auto-grow="true"></ion-textarea>
</ion-item> -->
</ion-list>

<div class="ion-text-center">
Expand Down
40 changes: 35 additions & 5 deletions core/src/components/textarea/textarea.scss
Original file line number Diff line number Diff line change
Expand Up @@ -26,7 +26,7 @@
--placeholder-color: initial;
--placeholder-font-style: initial;
--placeholder-font-weight: initial;
--placeholder-opacity: .5;
--placeholder-opacity: 0.5;
--padding-top: 0;
--padding-end: 0;
--padding-bottom: 0;
Expand Down Expand Up @@ -71,22 +71,41 @@
--padding-start: 0;
}


// Native Textarea
// --------------------------------------------------

.textarea-wrapper {
display: grid;

min-width: inherit;
max-width: inherit;
min-height: inherit;
max-height: inherit;

&::after {
// This technique is used for an auto-resizing textarea.
// The text contents are reflected as a pseudo-element that is visually hidden.
// This causes the textarea container to grow as needed to fit the contents.

white-space: pre-wrap;

content: attr(data-replicated-value) " ";

visibility: hidden;
}
}

.native-textarea,
.textarea-wrapper::after {
@include padding(var(--padding-top), var(--padding-end), var(--padding-bottom), var(--padding-start));
@include text-inherit();

grid-area: 1 / 1 / 2 / 2;
}

.native-textarea {
@include border-radius(var(--border-radius));
@include margin(0);
@include padding(var(--padding-top), var(--padding-end), var(--padding-bottom), var(--padding-start));
@include text-inherit();

display: block;

Expand All @@ -103,6 +122,8 @@
resize: none;
appearance: none;

overflow: hidden;

&::placeholder {
@include padding(0);

Expand All @@ -117,7 +138,7 @@
}

.native-textarea[disabled] {
opacity: .4;
opacity: 0.4;
}

// Input Cover: Unfocused
Expand All @@ -136,6 +157,15 @@
pointer-events: none;
}

:host([auto-grow]) .cloned-input {
// Workaround for webkit rendering issue with scroll assist.
// When cloning the textarea and scrolling into view,
// a white box is rendered from the difference in height
// from the auto grow container.
// This change forces the cloned input to match the true
// height of the textarea container.
height: 100%;
}

// Item Floating: Placeholder
// ----------------------------------------------------------------
Expand Down
36 changes: 18 additions & 18 deletions core/src/components/textarea/textarea.tsx
Original file line number Diff line number Diff line change
@@ -1,10 +1,10 @@
import type { ComponentInterface, EventEmitter } from '@stencil/core';
import { Build, Component, Element, Event, Host, Method, Prop, State, Watch, h, readTask } from '@stencil/core';
import { Build, Component, Element, Event, Host, Method, Prop, State, Watch, h, writeTask } from '@stencil/core';

import { getIonMode } from '../../global/ionic-global';
import type { Color, StyleEventDetail, TextareaChangeEventDetail } from '../../interface';
import type { Attributes } from '../../utils/helpers';
import { inheritAriaAttributes, debounceEvent, findItemLabel, inheritAttributes, raf } from '../../utils/helpers';
import { inheritAriaAttributes, debounceEvent, findItemLabel, inheritAttributes } from '../../utils/helpers';
import { createColorClasses } from '../../utils/theme';

/**
Expand Down Expand Up @@ -147,9 +147,10 @@ export class Textarea implements ComponentInterface {
@Prop() wrap?: 'hard' | 'soft' | 'off';

/**
* If `true`, the element height will increase based on the value.
* If `true`, the textarea container will grow and shrink based
* on the contents of the textarea.
*/
@Prop() autoGrow = false;
@Prop({ reflect: true }) autoGrow = false;

/**
* The value of the textarea.
Expand Down Expand Up @@ -227,20 +228,7 @@ export class Textarea implements ComponentInterface {
}

componentDidLoad() {
raf(() => this.runAutoGrow());
}

private runAutoGrow() {
const nativeInput = this.nativeInput;
if (nativeInput && this.autoGrow) {
readTask(() => {
nativeInput.style.height = 'auto';
nativeInput.style.height = nativeInput.scrollHeight + 'px';
if (this.textareaWrapper) {
this.textareaWrapper.style.height = nativeInput.scrollHeight + 'px';
}
});
}
this.runAutoGrow();
}

/**
Expand Down Expand Up @@ -286,6 +274,18 @@ export class Textarea implements ComponentInterface {
});
}

private runAutoGrow() {
if (this.nativeInput && this.autoGrow) {
writeTask(() => {
if (this.textareaWrapper) {
// Replicated value is an attribute to be used in the stylesheet
// to set the inner contents of a pseudo element.
this.textareaWrapper.dataset.replicatedValue = this.value ?? '';
}
});
}
}

/**
* Check if we need to clear the text input if clearOnEdit is enabled
*/
Expand Down

0 comments on commit 7a342ab

Please sign in to comment.