This commit is contained in:
darenhsu
2022-07-17 13:16:16 +08:00
parent 84759556ff
commit befd344ab0
28070 changed files with 4008428 additions and 1 deletions
+91
View File
@@ -0,0 +1,91 @@
@import './_variables.scss'
.v-textarea
textarea
align-self: stretch
flex: 1 1 auto
line-height: $textarea-line-height
max-width: 100%
min-height: $textarea-min-height
outline: none
padding: $textarea-padding
width: 100%
.v-text-field__prefix,
.v-text-field__suffix
padding-top: $textarea-prefix-padding-top
align-self: start
&.v-text-field--box,
&.v-text-field--enclosed
.v-text-field__prefix,
textarea
margin-top: $textarea-box-enclosed-prefix-margin-top
&.v-text-field--single-line,
&.v-text-field--outlined
&:not(.v-input--dense)
.v-text-field__prefix,
.v-text-field__suffix,
textarea
margin-top: $textarea-box-enclosed-single-outlined-margin-top
.v-label
top: $textarea-box-enclosed-single-outlined-label-top
&.v-input--dense
.v-text-field__prefix,
.v-text-field__suffix,
textarea
margin-top: $textarea-dense-box-enclosed-single-outlined-margin-top
.v-input__prepend-inner,
.v-input__prepend-outer,
.v-input__append-inner,
.v-input__append-outer
align-self: flex-start
margin-top: $textarea-dense-append-prepend-margin-top
&.v-text-field--solo
align-items: flex-start
// Essentially revert styles
// applied by v-text-field
.v-input__prepend-inner,
.v-input__prepend-outer,
.v-input__append-inner,
.v-input__append-outer
align-self: flex-start
margin-top: $textarea-solo-append-prepend-margin-top
.v-input__append-inner
+ltr()
padding-left: $textarea-solo-append-padding
+rtl()
padding-right: $textarea-solo-append-padding
&--auto-grow
textarea
overflow: hidden
&--no-resize
textarea
resize: none
&.v-text-field--enclosed
.v-text-field__slot
align-self: stretch
+ltr()
margin-right: $textarea-enclosed-text-slot-margin
+rtl()
margin-left: $textarea-enclosed-text-slot-margin
textarea
+ltr()
padding-right: $textarea-enclosed-text-slot-padding
+rtl()
padding-left: $textarea-enclosed-text-slot-padding
+109
View File
@@ -0,0 +1,109 @@
// Styles
import './VTextarea.sass'
// Extensions
import VTextField from '../VTextField/VTextField'
// Utilities
import mixins from '../../util/mixins'
// Types
import Vue from 'vue'
interface options extends Vue {
$refs: {
input: HTMLTextAreaElement
}
}
const baseMixins = mixins<options &
InstanceType<typeof VTextField>
>(
VTextField
)
/* @vue/component */
export default baseMixins.extend({
name: 'v-textarea',
props: {
autoGrow: Boolean,
noResize: Boolean,
rowHeight: {
type: [Number, String],
default: 24,
validator: (v: any) => !isNaN(parseFloat(v)),
},
rows: {
type: [Number, String],
default: 5,
validator: (v: any) => !isNaN(parseInt(v, 10)),
},
},
computed: {
classes (): object {
return {
'v-textarea': true,
'v-textarea--auto-grow': this.autoGrow,
'v-textarea--no-resize': this.noResizeHandle,
...VTextField.options.computed.classes.call(this),
}
},
noResizeHandle (): boolean {
return this.noResize || this.autoGrow
},
},
watch: {
lazyValue () {
this.autoGrow && this.$nextTick(this.calculateInputHeight)
},
rowHeight () {
this.autoGrow && this.$nextTick(this.calculateInputHeight)
},
},
mounted () {
setTimeout(() => {
this.autoGrow && this.calculateInputHeight()
}, 0)
},
methods: {
calculateInputHeight () {
const input = this.$refs.input
if (!input) return
input.style.height = '0'
const height = input.scrollHeight
const minHeight = parseInt(this.rows, 10) * parseFloat(this.rowHeight)
// This has to be done ASAP, waiting for Vue
// to update the DOM causes ugly layout jumping
input.style.height = Math.max(minHeight, height) + 'px'
},
genInput () {
const input = VTextField.options.methods.genInput.call(this)
input.tag = 'textarea'
delete input.data!.attrs!.type
input.data!.attrs!.rows = this.rows
return input
},
onInput (e: Event) {
VTextField.options.methods.onInput.call(this, e)
this.autoGrow && this.calculateInputHeight()
},
onKeyDown (e: KeyboardEvent) {
// Prevents closing of a
// dialog when pressing
// enter
if (this.isFocused && e.keyCode === 13) {
e.stopPropagation()
}
this.$emit('keydown', e)
},
},
})
@@ -0,0 +1,147 @@
import { keyCodes } from '../../../util/helpers'
import VTextarea from '../VTextarea'
import {
mount,
MountOptions,
Wrapper,
} from '@vue/test-utils'
import { wait } from '../../../../test'
describe('VTextarea.ts', () => {
type Instance = InstanceType<typeof VTextarea>
let mountFunction: (options?: MountOptions<Instance>) => Wrapper<Instance>
beforeEach(() => {
mountFunction = (options?: MountOptions<Instance>) => {
return mount(VTextarea, options)
}
})
it('should calculate element height when using auto-grow prop', async () => {
const wrapper = mountFunction({
attachToDocument: true,
propsData: {
value: '',
autoGrow: true,
},
})
const input = jest.fn(value => wrapper.setProps({ value }))
wrapper.vm.$on('input', input)
const el = wrapper.findAll('textarea').at(0)
el.trigger('focus')
await wrapper.vm.$nextTick()
el.element.value = 'this is a really long text that should hopefully make auto-grow kick in. maybe?'.replace(/\s/g, '\n')
el.trigger('input')
await wrapper.vm.$nextTick()
// TODO: switch to e2e, jest doesn't do inline styles
expect(wrapper.html()).toMatchSnapshot()
expect(el.element.style.getPropertyValue('height').length).not.toBe(0)
})
it('should watch lazy value', async () => {
const wrapper = mountFunction()
const calculateInputHeight = jest.fn()
wrapper.setMethods({ calculateInputHeight })
wrapper.vm.lazyValue = 'foo'
expect(calculateInputHeight).not.toHaveBeenCalled()
wrapper.setProps({ autoGrow: true })
wrapper.vm.lazyValue = 'bar'
// wait for watcher
await wrapper.vm.$nextTick()
expect(calculateInputHeight).toHaveBeenCalled()
})
it('should calculate height on mounted', async () => {
const calculateInputHeight = jest.fn()
mountFunction({
attachToDocument: true,
propsData: {
autoGrow: true,
},
methods: { calculateInputHeight },
})
await wait()
expect(calculateInputHeight).toHaveBeenCalled()
})
it('should stop propagation', async () => {
const wrapper = mountFunction()
const stopPropagation = jest.fn()
const onKeyDown = {
keyCode: keyCodes.enter,
stopPropagation,
}
wrapper.vm.onKeyDown(onKeyDown)
expect(stopPropagation).not.toHaveBeenCalled()
wrapper.setData({ isFocused: true })
wrapper.vm.onKeyDown(onKeyDown)
expect(stopPropagation).toHaveBeenCalled()
})
it('should render no-resize the same if already auto-grow', () => {
const wrappers = [
{ autoGrow: true, outlined: false },
{ autoGrow: true, outlined: true },
].map(propsData => mountFunction({ propsData }))
wrappers.forEach(async wrapper => {
await wrapper.vm.$nextTick()
const html1 = wrapper.html()
wrapper.setProps({ noResize: true })
// will still pass without this, do not remove
await wrapper.vm.$nextTick()
const html2 = wrapper.html()
expect(html2).toBe(html1)
})
})
it('should emit keydown event', () => {
const wrapper = mountFunction()
const keydown = jest.fn()
const textarea = wrapper.find('textarea')
wrapper.vm.$on('keydown', keydown)
textarea.trigger('focus')
textarea.element.value = 'foobar'
textarea.trigger('input')
textarea.trigger('keydown.enter')
expect(keydown).toHaveBeenCalled()
})
it('should dynamically adjust row-height', async () => {
const wrapper = mountFunction({
propsData: {
autoGrow: true,
},
})
await wait()
expect(wrapper.vm.$refs.input.style.height).toBe('120px')
wrapper.setProps({ rowHeight: 30 })
await wrapper.vm.$nextTick()
expect(wrapper.vm.$refs.input.style.height).toBe('150px')
})
})
@@ -0,0 +1,26 @@
// Jest Snapshot v1, https://goo.gl/fbAQLP
exports[`VTextarea.ts should calculate element height when using auto-grow prop 1`] = `
<div class="v-input v-textarea v-textarea--auto-grow v-textarea--no-resize v-input--is-label-active v-input--is-dirty v-input--is-focused theme--light v-text-field primary--text">
<div class="v-input__control">
<div class="v-input__slot">
<div class="v-text-field__slot">
<textarea id="input-1"
rows="5"
style="height: 120px;"
>
</textarea>
</div>
</div>
<div class="v-text-field__details">
<div class="v-messages theme--light primary--text">
<span name="message-transition"
tag="div"
class="v-messages__wrapper"
>
</span>
</div>
</div>
</div>
</div>
`;
+15
View File
@@ -0,0 +1,15 @@
@import '../../styles/styles.sass';
$textarea-box-enclosed-prefix-margin-top: 24px !default;
$textarea-box-enclosed-single-outlined-label-top: 18px !default;
$textarea-box-enclosed-single-outlined-margin-top: 10px !default;
$textarea-dense-box-enclosed-single-outlined-margin-top: 6px !default;
$textarea-dense-append-prepend-margin-top: 8px !default;
$textarea-enclosed-text-slot-margin: -12px !default;
$textarea-enclosed-text-slot-padding: 12px !default;
$textarea-line-height: 1.75rem !default;
$textarea-min-height: 32px !default;
$textarea-padding: 0 !default;
$textarea-prefix-padding-top: 2px !default;
$textarea-solo-append-padding: 12px !default;
$textarea-solo-append-prepend-margin-top: 12px !default;
+4
View File
@@ -0,0 +1,4 @@
import VTextarea from './VTextarea'
export { VTextarea }
export default VTextarea