diff --git a/packages/text-area/test/visual/lumo/screenshots/text-area/baseline/clear-button.png b/packages/text-area/test/visual/lumo/screenshots/text-area/baseline/clear-button.png index eecddd9e26..6d948da869 100644 Binary files a/packages/text-area/test/visual/lumo/screenshots/text-area/baseline/clear-button.png and b/packages/text-area/test/visual/lumo/screenshots/text-area/baseline/clear-button.png differ diff --git a/packages/text-area/test/visual/lumo/screenshots/text-area/baseline/scrolled-with-prefix-suffix-clear-button.png b/packages/text-area/test/visual/lumo/screenshots/text-area/baseline/scrolled-with-prefix-suffix-clear-button.png new file mode 100644 index 0000000000..b7d31811b2 Binary files /dev/null and b/packages/text-area/test/visual/lumo/screenshots/text-area/baseline/scrolled-with-prefix-suffix-clear-button.png differ diff --git a/packages/text-area/test/visual/lumo/text-area.test.js b/packages/text-area/test/visual/lumo/text-area.test.js index c1e2c8c2bb..ab636c0761 100644 --- a/packages/text-area/test/visual/lumo/text-area.test.js +++ b/packages/text-area/test/visual/lumo/text-area.test.js @@ -66,6 +66,24 @@ describe('text-area', () => { await visualDiff(div, 'scrolled'); }); + it('scrolled with prefix, suffix, clear button', async () => { + const prefix = document.createElement('span'); + prefix.setAttribute('slot', 'prefix'); + prefix.textContent = '$'; + element.appendChild(prefix); + + const suffix = document.createElement('span'); + suffix.setAttribute('slot', 'suffix'); + suffix.textContent = '$'; + element.appendChild(suffix); + + element.clearButtonVisible = true; + element.style.height = '70px'; + element.value = 'a\nb\nc\nd\ne'; + element.focus(); + await visualDiff(div, 'scrolled-with-prefix-suffix-clear-button'); + }); + it('error message', async () => { element.label = 'Label'; element.errorMessage = 'This field is required'; diff --git a/packages/text-area/test/visual/material/screenshots/text-area/baseline/clear-button.png b/packages/text-area/test/visual/material/screenshots/text-area/baseline/clear-button.png index c971b0f769..7cac865fee 100644 Binary files a/packages/text-area/test/visual/material/screenshots/text-area/baseline/clear-button.png and b/packages/text-area/test/visual/material/screenshots/text-area/baseline/clear-button.png differ diff --git a/packages/text-area/test/visual/material/screenshots/text-area/baseline/scrolled-with-prefix-suffix-clear-button.png b/packages/text-area/test/visual/material/screenshots/text-area/baseline/scrolled-with-prefix-suffix-clear-button.png new file mode 100644 index 0000000000..931bc79e77 Binary files /dev/null and b/packages/text-area/test/visual/material/screenshots/text-area/baseline/scrolled-with-prefix-suffix-clear-button.png differ diff --git a/packages/text-area/test/visual/material/text-area.test.js b/packages/text-area/test/visual/material/text-area.test.js index 1db94534b7..10e6adfc31 100644 --- a/packages/text-area/test/visual/material/text-area.test.js +++ b/packages/text-area/test/visual/material/text-area.test.js @@ -66,6 +66,24 @@ describe('text-area', () => { await visualDiff(div, 'scrolled'); }); + it('scrolled with prefix, suffix, clear button', async () => { + const prefix = document.createElement('span'); + prefix.setAttribute('slot', 'prefix'); + prefix.textContent = '$'; + element.appendChild(prefix); + + const suffix = document.createElement('span'); + suffix.setAttribute('slot', 'suffix'); + suffix.textContent = '$'; + element.appendChild(suffix); + + element.clearButtonVisible = true; + element.style.height = '70px'; + element.value = 'a\nb\nc\nd\ne'; + element.focus(); + await visualDiff(div, 'scrolled-with-prefix-suffix-clear-button'); + }); + it('error message', async () => { element.label = 'Label'; element.errorMessage = 'This field is required'; diff --git a/packages/text-area/theme/lumo/vaadin-text-area-styles.js b/packages/text-area/theme/lumo/vaadin-text-area-styles.js index 6f537ab37c..9e98c96941 100644 --- a/packages/text-area/theme/lumo/vaadin-text-area-styles.js +++ b/packages/text-area/theme/lumo/vaadin-text-area-styles.js @@ -56,8 +56,18 @@ const textArea = css` --_lumo-text-field-overflow-mask-image: none; } - /* Vertically align icon prefix/suffix with the first line of text */ - [part='input-field'] ::slotted(vaadin-icon) { + /* Use sticky positioning to keep prefix/suffix/clear button visible when scrolling textarea container */ + [part='input-field'] ::slotted([slot$='fix']), + [part='clear-button'] { + position: sticky; + top: 0; + align-self: flex-start; + } + + /* Vertically align icon prefix/suffix/clear button with the first line of text */ + [part='input-field'] ::slotted(vaadin-icon[slot$='fix']), + [part='clear-button'] { + top: calc((var(--lumo-icon-size-m) - 1em * var(--lumo-line-height-s)) / -2); margin-top: calc((var(--lumo-icon-size-m) - 1em * var(--lumo-line-height-s)) / -2); } `; diff --git a/packages/text-area/theme/material/vaadin-text-area-styles.js b/packages/text-area/theme/material/vaadin-text-area-styles.js index 6caed8360c..acaa640bb0 100644 --- a/packages/text-area/theme/material/vaadin-text-area-styles.js +++ b/packages/text-area/theme/material/vaadin-text-area-styles.js @@ -22,6 +22,14 @@ const textArea = css` [part='input-field']::after { bottom: calc(var(--_text-area-vertical-scroll-position) * -1); } + + /* Use sticky positioning to keep prefix/suffix/clear button visible when scrolling textarea container */ + [part='input-field'] ::slotted([slot$='fix']), + [part='clear-button'] { + position: sticky; + top: 0; + align-self: flex-start; + } `; registerStyles('vaadin-text-area', [inputFieldShared, textArea], { moduleId: 'material-text-area' });