fix(textarea): add intersection observer for textarea recalculate
This commit is contained in:
parent
2dc85b14d3
commit
c3a581e281
4 changed files with 55 additions and 1 deletions
5
.changeset/many-cougars-sit.md
Normal file
5
.changeset/many-cougars-sit.md
Normal file
|
|
@ -0,0 +1,5 @@
|
||||||
|
---
|
||||||
|
'@lion/textarea': patch
|
||||||
|
---
|
||||||
|
|
||||||
|
Added Intersection Observer to ensure textarea gets recalculated on visibility changes, e.g. when it is inside a dialog, tabs, or accordion when they are hidden initially.
|
||||||
|
|
@ -29,6 +29,7 @@ export const main = () => html`
|
||||||
- `max-rows` attribute to set the amount of rows it should resize to, before it will scroll
|
- `max-rows` attribute to set the amount of rows it should resize to, before it will scroll
|
||||||
- `rows` attribute to set the minimum amount of rows
|
- `rows` attribute to set the minimum amount of rows
|
||||||
- `readonly` attribute to prevent changing the content
|
- `readonly` attribute to prevent changing the content
|
||||||
|
- Uses Intersection Observer for detecting visibility change, making sure it resizes
|
||||||
|
|
||||||
## How to use
|
## How to use
|
||||||
|
|
||||||
|
|
@ -120,3 +121,33 @@ export const validation = () => {
|
||||||
`;
|
`;
|
||||||
};
|
};
|
||||||
```
|
```
|
||||||
|
|
||||||
|
### Intersection Observer
|
||||||
|
|
||||||
|
It could be that your textarea is inside a hidden container, for example for a dialog or accordion or tabs.
|
||||||
|
When it is hidden, the resizing is calculated based on the visible space of the text.
|
||||||
|
Therefore, an Intersection Observer observes visibility changes of the textarea relative to the viewport, and resizes the textarea when a visibility change happens.
|
||||||
|
|
||||||
|
> For old browsers like old Edge or IE11, a [polyfill](https://github.com/w3c/IntersectionObserver/tree/master/polyfill) is required to be added on the application level for this to work.
|
||||||
|
> For most cases, the optimized default will suffice.
|
||||||
|
|
||||||
|
In the demo below you can see that the textarea is correctly calculated to 4 maximum rows, whereas without the observer, it would be on 2 rows and only resize on user input.
|
||||||
|
|
||||||
|
```js preview-story
|
||||||
|
export const hidden = () => html`
|
||||||
|
<div style="display: none">
|
||||||
|
<lion-textarea
|
||||||
|
.modelValue="${'Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat. Duis aute irure dolor in reprehenderit in voluptate velit esse cillum dolore eu fugiat nulla pariatur. Excepteur sint occaecat cupidatat non proident, sunt in culpa qui officia deserunt mollit anim id est laborum.'}"
|
||||||
|
label="Stops growing after 4 rows"
|
||||||
|
max-rows="4"
|
||||||
|
></lion-textarea>
|
||||||
|
</div>
|
||||||
|
<button
|
||||||
|
@click=${e =>
|
||||||
|
(e.target.previousElementSibling.style.display =
|
||||||
|
e.target.previousElementSibling.style.display === 'block' ? 'none' : 'block')}
|
||||||
|
>
|
||||||
|
Toggle display
|
||||||
|
</button>
|
||||||
|
`;
|
||||||
|
```
|
||||||
|
|
|
||||||
|
|
@ -73,6 +73,8 @@ export class LionTextarea extends NativeTextFieldMixin(LionFieldWithTextArea) {
|
||||||
// eslint-disable-next-line wc/guard-super-call
|
// eslint-disable-next-line wc/guard-super-call
|
||||||
super.connectedCallback();
|
super.connectedCallback();
|
||||||
this.__initializeAutoresize();
|
this.__initializeAutoresize();
|
||||||
|
this.__intersectionObserver = new IntersectionObserver(() => this.resizeTextarea());
|
||||||
|
this.__intersectionObserver.observe(this);
|
||||||
}
|
}
|
||||||
|
|
||||||
/** @param {import('lit-element').PropertyValues } changedProperties */
|
/** @param {import('lit-element').PropertyValues } changedProperties */
|
||||||
|
|
|
||||||
|
|
@ -1,4 +1,5 @@
|
||||||
import { expect, fixture as _fixture, html } from '@open-wc/testing';
|
import { expect, fixture as _fixture, html, nextFrame } from '@open-wc/testing';
|
||||||
|
import sinon from 'sinon';
|
||||||
import '../lion-textarea.js';
|
import '../lion-textarea.js';
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|
@ -147,6 +148,21 @@ describe('<lion-textarea>', () => {
|
||||||
expect(el._inputNode.getAttribute('placeholder')).to.equal('foo');
|
expect(el._inputNode.getAttribute('placeholder')).to.equal('foo');
|
||||||
});
|
});
|
||||||
|
|
||||||
|
it('fires resize textarea when a visibility change has been detected', async () => {
|
||||||
|
const el = await fixture(`
|
||||||
|
<div style="display: none">
|
||||||
|
<lion-textarea placeholder="text"></lion-textarea>
|
||||||
|
</div>
|
||||||
|
`);
|
||||||
|
const textArea = /** @type {LionTextarea} */ (el.firstElementChild);
|
||||||
|
await textArea.updateComplete;
|
||||||
|
|
||||||
|
const resizeSpy = sinon.spy(textArea, 'resizeTextarea');
|
||||||
|
el.style.display = 'block';
|
||||||
|
await nextFrame();
|
||||||
|
expect(resizeSpy.calledOnce).to.be.true;
|
||||||
|
});
|
||||||
|
|
||||||
it('is accessible', async () => {
|
it('is accessible', async () => {
|
||||||
const el = await fixture(`<lion-textarea label="Label"></lion-textarea>`);
|
const el = await fixture(`<lion-textarea label="Label"></lion-textarea>`);
|
||||||
await expect(el).to.be.accessible();
|
await expect(el).to.be.accessible();
|
||||||
|
|
|
||||||
Loading…
Reference in a new issue