Skip to content

fix(textarea): textarea with autogrow will size to its contents #24205

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 22 commits into from
Jul 27, 2022
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
22 commits
Select commit Hold shift + click to select a range
ee3d8fa
feat(textarea): revised approach to auto-grow
sean-perkins Nov 11, 2021
45e0ebb
fix(textarea): remove duplicate styles
sean-perkins Nov 15, 2021
c7eb7e0
test(textarea): autogrow usage examples
sean-perkins Nov 15, 2021
a3a2d18
refactor(textarea): clean-up docs and private method ordering
sean-perkins Nov 15, 2021
76a5dca
Merge remote-tracking branch 'origin/next' into feat/textarea-auto-grow
sean-perkins Nov 15, 2021
70ef836
docs(textarea): generated textarea documentation
sean-perkins Nov 15, 2021
4b97f2f
Merge remote-tracking branch 'origin/main' into feat/textarea-auto-grow
sean-perkins Feb 16, 2022
63e88c4
Merge remote-tracking branch 'origin/main' into feat/textarea-auto-grow
sean-perkins Jun 22, 2022
954d92e
Merge remote-tracking branch 'origin/feature-6.2' into feat/textarea-…
sean-perkins Jul 7, 2022
3888b27
test(): enable textarea autogrow screenshot tests
sean-perkins Jul 7, 2022
3d30bdc
chore(): add updated snapshots
Ionitron Jul 7, 2022
ae6acea
test(): remove autogrow from basic textarea test
sean-perkins Jul 11, 2022
61b7dad
chore(): add updated snapshots
Ionitron Jul 11, 2022
1e7c43f
chore(): document usage for pseudo element
sean-perkins Jul 19, 2022
6f46f07
fix(): iOS 13 does not render grid-area correctly
sean-perkins Jul 20, 2022
0b40f23
fix(): only apply iOS 13 fix to autogrow textareas
sean-perkins Jul 20, 2022
36850d5
fix(): scroll assist rendering bug with iOS 13
sean-perkins Jul 26, 2022
656d509
chore(): update comment to not be explicit to iOS 13
sean-perkins Jul 26, 2022
f7b0103
Merge remote-tracking branch 'origin/feature-6.2' into feat/textarea-…
sean-perkins Jul 27, 2022
f07bca5
chore(): trigger rebuild
sean-perkins Jul 27, 2022
1d3c920
Merge remote-tracking branch 'origin/main' into feat/textarea-auto-grow
sean-perkins Jul 27, 2022
190b5ba
Merge remote-tracking branch 'origin/feature-6.2' into feat/textarea-…
sean-perkins Jul 27, 2022
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
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