Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
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
6 changes: 6 additions & 0 deletions packages/vuetify/src/components/VTextarea/VTextarea.sass
Original file line number Diff line number Diff line change
Expand Up @@ -33,6 +33,12 @@
&--no-resize
.v-field__input
resize: none
scrollbar-width: none

&.v-textarea--height-capped
.v-field__input
scrollbar-gutter: stable
scrollbar-width: thin
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

it makes it slightly inconsistent when textarea with auto-grow appears next to the regular one (without this prop heightCapped is always false and the *--height-capped is never present even though it has scrollbar, see playground).

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I would drop thin and change none to:

&:not(.v-textarea--height-capped)
  .v-field__input
    scrollbar-width: none


.v-field--no-label,
.v-field--active
Expand Down
10 changes: 8 additions & 2 deletions packages/vuetify/src/components/VTextarea/VTextarea.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -98,6 +98,7 @@ export const VTextarea = genericComponent<VTextareaSlots>()({
const vInputRef = ref<VInput>()
const vFieldRef = ref<VInput>()
const controlHeight = shallowRef('')
const heightCapped = shallowRef(false)
const textareaRef = ref<HTMLInputElement>()
const isActive = computed(() => (
props.persistentPlaceholder ||
Expand Down Expand Up @@ -150,7 +151,10 @@ export const VTextarea = genericComponent<VTextareaSlots>()({
if (!props.autoGrow) rows.value = Number(props.rows)
})
function calculateInputHeight () {
if (!props.autoGrow) return
if (!props.autoGrow) {
heightCapped.value = false
return
}

nextTick(() => {
if (!sizerRef.value || !vFieldRef.value) return
Expand All @@ -170,9 +174,10 @@ export const VTextarea = genericComponent<VTextareaSlots>()({
)
const maxHeight = parseFloat(props.maxRows!) * lineHeight + padding || Infinity
const newHeight = clamp(height ?? 0, minHeight, maxHeight)
rows.value = Math.floor((newHeight - padding) / lineHeight)

rows.value = Math.floor((newHeight - padding) / lineHeight)
controlHeight.value = convertToUnit(newHeight)
heightCapped.value = height > maxHeight
})
}

Expand Down Expand Up @@ -221,6 +226,7 @@ export const VTextarea = genericComponent<VTextareaSlots>()({
'v-text-field--suffixed': props.suffix,
'v-textarea--auto-grow': props.autoGrow,
'v-textarea--no-resize': props.noResize || props.autoGrow,
'v-textarea--height-capped': heightCapped.value,
'v-input--plain-underlined': isPlainOrUnderlined.value,
},
props.class,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -24,16 +24,16 @@ describe('VTextarea', () => {
expect(el.offsetHeight).toBe(56)

await userEvent.tab()
await userEvent.keyboard('sed d')
await userEvent.keyboard(' sed do ')
await expect.poll(() => el.offsetHeight).toBe(56)

await userEvent.keyboard('o')
await userEvent.keyboard('e')
await expect.poll(() => el.offsetHeight).toBe(80)
})

it('should respect max-rows', async () => {
await page.viewport(500, 500)
const model = ref('Lorem ipsum dolor sit amet, consectetur adipiscing elit')
const model = ref('Lorem ipsum dolor sit amet, consectetur adipiscing elit. ')

render(() => (
<Application>
Expand All @@ -57,7 +57,7 @@ describe('VTextarea', () => {

it('should emit update rows', async () => {
await page.viewport(500, 500)
const model = ref('Lorem ipsum dolor sit amet, consectetur adipiscing elit')
const model = ref('Lorem ipsum dolor sit amet, consectetur adipiscing elit. ')
const rows = ref(1)
render(() => (
<Application>
Expand Down