@volverjs/ui-vue 0.0.9-beta.17 → 0.0.9-beta.19
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/components/VvCombobox/VvCombobox.es.js +10 -7
- package/dist/components/VvCombobox/VvCombobox.umd.js +1 -1
- package/dist/components/VvInputText/VvInputText.es.js +149 -96
- package/dist/components/VvInputText/VvInputText.umd.js +1 -1
- package/dist/components/VvInputText/VvInputText.vue.d.ts +10 -37
- package/dist/components/VvInputText/index.d.ts +18 -35
- package/dist/components/VvTextarea/VvTextarea.es.js +8 -5
- package/dist/components/VvTextarea/VvTextarea.umd.js +1 -1
- package/dist/components/index.es.js +167 -108
- package/dist/components/index.umd.js +1 -1
- package/dist/icons.es.js +3 -3
- package/dist/icons.umd.js +1 -1
- package/dist/stories/InputText/InputText.settings.d.ts +4 -30
- package/dist/stories/InputText/InputText.stories.d.ts +0 -1
- package/dist/stories/InputText/InputTextMask.stories.d.ts +12 -0
- package/package.json +49 -48
- package/src/assets/icons/detailed.json +1 -1
- package/src/assets/icons/normal.json +1 -1
- package/src/assets/icons/simple.json +1 -1
- package/src/components/VvCombobox/VvCombobox.vue +11 -7
- package/src/components/VvInputText/VvInputText.vue +138 -55
- package/src/components/VvInputText/index.ts +25 -34
- package/src/components/VvTextarea/VvTextarea.vue +8 -5
- package/src/stories/InputText/InputText.settings.ts +7 -33
- package/src/stories/InputText/InputText.stories.ts +4 -12
- package/src/stories/InputText/InputText.test.ts +31 -15
- package/src/stories/InputText/InputTextMask.stories.ts +122 -0
|
@@ -12,6 +12,7 @@
|
|
|
12
12
|
|
|
13
13
|
<script setup lang="ts">
|
|
14
14
|
import type { Ref } from 'vue'
|
|
15
|
+
import { toRefs } from 'vue'
|
|
15
16
|
import { VvComboboxProps, VvComboboxEvents } from '.'
|
|
16
17
|
import VvIcon from '../VvIcon/VvIcon.vue'
|
|
17
18
|
import VvDropdown from '../VvDropdown/VvDropdown.vue'
|
|
@@ -84,7 +85,7 @@
|
|
|
84
85
|
const searchText = ref('')
|
|
85
86
|
const debouncedSearchText = refDebounced(
|
|
86
87
|
searchText,
|
|
87
|
-
Number(props.debounceSearch),
|
|
88
|
+
computed(() => Number(props.debounceSearch)),
|
|
88
89
|
)
|
|
89
90
|
watch(debouncedSearchText, () =>
|
|
90
91
|
emit('change:search', debouncedSearchText.value),
|
|
@@ -235,12 +236,15 @@
|
|
|
235
236
|
} else if (props.modelValue) {
|
|
236
237
|
selectedValues = [props.modelValue]
|
|
237
238
|
}
|
|
238
|
-
const options = props.options.reduce(
|
|
239
|
-
|
|
240
|
-
|
|
241
|
-
|
|
242
|
-
|
|
243
|
-
|
|
239
|
+
const options = props.options.reduce(
|
|
240
|
+
(acc, value) => {
|
|
241
|
+
if (isGroup(value)) {
|
|
242
|
+
return [...acc, ...getOptionGrouped(value)]
|
|
243
|
+
}
|
|
244
|
+
return [...acc, value]
|
|
245
|
+
},
|
|
246
|
+
[] as Array<Option | string>,
|
|
247
|
+
)
|
|
244
248
|
|
|
245
249
|
return options.filter((option) => {
|
|
246
250
|
if (isGroup(option)) {
|
|
@@ -6,7 +6,7 @@
|
|
|
6
6
|
|
|
7
7
|
<script setup lang="ts">
|
|
8
8
|
import type { InputHTMLAttributes } from 'vue'
|
|
9
|
-
import {
|
|
9
|
+
import { useIMask } from 'vue-imask'
|
|
10
10
|
import HintSlotFactory from '../common/HintSlot'
|
|
11
11
|
import VvIcon from '../VvIcon/VvIcon.vue'
|
|
12
12
|
import VvInputTextActionsFactory from '../VvInputText/VvInputTextActions'
|
|
@@ -29,12 +29,6 @@
|
|
|
29
29
|
props,
|
|
30
30
|
)
|
|
31
31
|
|
|
32
|
-
// template refs
|
|
33
|
-
const inputEl = ref()
|
|
34
|
-
const innerEl = ref()
|
|
35
|
-
|
|
36
|
-
defineExpose({ $inner: innerEl })
|
|
37
|
-
|
|
38
32
|
// data
|
|
39
33
|
const {
|
|
40
34
|
id,
|
|
@@ -46,6 +40,12 @@
|
|
|
46
40
|
valid,
|
|
47
41
|
invalid,
|
|
48
42
|
loading,
|
|
43
|
+
debounce,
|
|
44
|
+
maxlength,
|
|
45
|
+
minlength,
|
|
46
|
+
type,
|
|
47
|
+
iMask,
|
|
48
|
+
step,
|
|
49
49
|
} = toRefs(props)
|
|
50
50
|
const hasId = useUniqueId(id)
|
|
51
51
|
const hasHintId = computed(() => `${hasId.value}-hint`)
|
|
@@ -54,29 +54,106 @@
|
|
|
54
54
|
props.floating && isEmpty(props.placeholder) ? ' ' : props.placeholder,
|
|
55
55
|
)
|
|
56
56
|
|
|
57
|
-
//
|
|
58
|
-
const
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
57
|
+
// template refs
|
|
58
|
+
const maskReady = ref(false)
|
|
59
|
+
const { el, mask, typed, masked, unmasked } = useIMask(
|
|
60
|
+
computed(
|
|
61
|
+
() =>
|
|
62
|
+
iMask?.value ?? {
|
|
63
|
+
mask: /./,
|
|
64
|
+
},
|
|
65
|
+
),
|
|
62
66
|
{
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
67
|
+
emit,
|
|
68
|
+
onAccept: () => {
|
|
69
|
+
if (!maskReady.value) {
|
|
70
|
+
return
|
|
66
71
|
}
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
+
emit('update:masked', masked.value)
|
|
73
|
+
if (type.value === INPUT_TYPES.NUMBER) {
|
|
74
|
+
if (typeof typed.value !== 'number') {
|
|
75
|
+
localModelValue.value = Number(typed.value)
|
|
76
|
+
return
|
|
77
|
+
}
|
|
78
|
+
localModelValue.value = typed.value
|
|
79
|
+
return
|
|
80
|
+
}
|
|
81
|
+
if (type.value === INPUT_TYPES.DATE) {
|
|
82
|
+
if (
|
|
83
|
+
el.value instanceof HTMLInputElement &&
|
|
84
|
+
el.value.type === 'date'
|
|
85
|
+
) {
|
|
86
|
+
localModelValue.value = el.value.value
|
|
87
|
+
return
|
|
88
|
+
}
|
|
89
|
+
let date = typed.value
|
|
90
|
+
if (!(date instanceof Date)) {
|
|
91
|
+
date = new Date(date)
|
|
92
|
+
}
|
|
93
|
+
localModelValue.value = `${date.getFullYear()}-${(
|
|
94
|
+
'0' +
|
|
95
|
+
(date.getMonth() + 1)
|
|
96
|
+
).slice(-2)}-${('0' + date.getDate()).slice(-2)}`
|
|
97
|
+
return
|
|
72
98
|
}
|
|
73
|
-
if (
|
|
74
|
-
|
|
99
|
+
if (type.value === INPUT_TYPES.DATETIME_LOCAL) {
|
|
100
|
+
if (
|
|
101
|
+
el.value instanceof HTMLInputElement &&
|
|
102
|
+
el.value.type === 'datetime-local'
|
|
103
|
+
) {
|
|
104
|
+
localModelValue.value = el.value.value
|
|
105
|
+
return
|
|
106
|
+
}
|
|
107
|
+
let date = typed.value
|
|
108
|
+
if (!(typed.value instanceof Date)) {
|
|
109
|
+
date = new Date(date)
|
|
110
|
+
}
|
|
111
|
+
localModelValue.value = `${date.getFullYear()}-${(
|
|
112
|
+
'0' +
|
|
113
|
+
(date.getMonth() + 1)
|
|
114
|
+
).slice(-2)}-${('0' + date.getDate()).slice(-2)}T${(
|
|
115
|
+
'0' + date.getHours()
|
|
116
|
+
).slice(-2)}:${('0' + date.getMinutes()).slice(-2)}`
|
|
117
|
+
return
|
|
75
118
|
}
|
|
76
|
-
|
|
119
|
+
localModelValue.value = unmasked.value
|
|
77
120
|
},
|
|
78
121
|
},
|
|
79
122
|
)
|
|
123
|
+
onMounted(() => {
|
|
124
|
+
if (mask.value) {
|
|
125
|
+
maskReady.value = true
|
|
126
|
+
typed.value = localModelValue.value ?? ''
|
|
127
|
+
}
|
|
128
|
+
})
|
|
129
|
+
watch(
|
|
130
|
+
() => props.modelValue,
|
|
131
|
+
(newValue) => {
|
|
132
|
+
if (mask.value) {
|
|
133
|
+
typed.value =
|
|
134
|
+
newValue && iMask?.value?.mask === Date
|
|
135
|
+
? new Date(newValue)
|
|
136
|
+
: newValue ?? null
|
|
137
|
+
}
|
|
138
|
+
},
|
|
139
|
+
)
|
|
140
|
+
watch(
|
|
141
|
+
() => props.masked,
|
|
142
|
+
(newValue) => {
|
|
143
|
+
masked.value = newValue ?? ''
|
|
144
|
+
},
|
|
145
|
+
)
|
|
146
|
+
const inputEl = el as Ref<HTMLInputElement>
|
|
147
|
+
const innerEl = ref()
|
|
148
|
+
|
|
149
|
+
defineExpose({ $inner: innerEl })
|
|
150
|
+
|
|
151
|
+
// debounce
|
|
152
|
+
const localModelValue = useDebouncedInput(
|
|
153
|
+
modelValue,
|
|
154
|
+
emit,
|
|
155
|
+
debounce?.value ?? 0,
|
|
156
|
+
)
|
|
80
157
|
|
|
81
158
|
// focus
|
|
82
159
|
const { focused } = useComponentFocus(inputEl, emit)
|
|
@@ -118,12 +195,21 @@
|
|
|
118
195
|
const isNumber = computed(() => props.type === INPUT_TYPES.NUMBER)
|
|
119
196
|
const onStepUp = () => {
|
|
120
197
|
if (isClickable.value) {
|
|
198
|
+
if (iMask?.value) {
|
|
199
|
+
typed.value = typed.value + Number(step?.value ?? 1)
|
|
200
|
+
return
|
|
201
|
+
}
|
|
121
202
|
inputEl.value.stepUp()
|
|
122
203
|
localModelValue.value = unref(inputEl).value
|
|
123
204
|
}
|
|
124
205
|
}
|
|
125
206
|
const onStepDown = () => {
|
|
126
207
|
if (isClickable.value) {
|
|
208
|
+
if (iMask?.value) {
|
|
209
|
+
typed.value = typed.value - Number(step?.value ?? 1)
|
|
210
|
+
|
|
211
|
+
return
|
|
212
|
+
}
|
|
127
213
|
inputEl.value.stepDown()
|
|
128
214
|
localModelValue.value = unref(inputEl).value
|
|
129
215
|
}
|
|
@@ -132,7 +218,7 @@
|
|
|
132
218
|
// search
|
|
133
219
|
const isSearch = computed(() => props.type === INPUT_TYPES.SEARCH)
|
|
134
220
|
const onClear = () => {
|
|
135
|
-
localModelValue.value =
|
|
221
|
+
localModelValue.value = ''
|
|
136
222
|
}
|
|
137
223
|
|
|
138
224
|
// icons
|
|
@@ -158,9 +244,9 @@
|
|
|
158
244
|
|
|
159
245
|
// count
|
|
160
246
|
const { formatted: countFormatted } = useTextCount(localModelValue, {
|
|
161
|
-
mode:
|
|
162
|
-
upperLimit: Number(
|
|
163
|
-
lowerLimit: Number(
|
|
247
|
+
mode: count.value,
|
|
248
|
+
upperLimit: Number(maxlength?.value),
|
|
249
|
+
lowerLimit: Number(minlength?.value),
|
|
164
250
|
})
|
|
165
251
|
|
|
166
252
|
// tabindex
|
|
@@ -212,6 +298,9 @@
|
|
|
212
298
|
if (isDateTime.value && !isDirty.value && !focused.value) {
|
|
213
299
|
return INPUT_TYPES.TEXT
|
|
214
300
|
}
|
|
301
|
+
if (iMask?.value) {
|
|
302
|
+
return INPUT_TYPES.TEXT
|
|
303
|
+
}
|
|
215
304
|
return props.type
|
|
216
305
|
})()
|
|
217
306
|
const toReturn: InputHTMLAttributes = {
|
|
@@ -304,33 +393,6 @@
|
|
|
304
393
|
props,
|
|
305
394
|
)
|
|
306
395
|
|
|
307
|
-
// mask
|
|
308
|
-
const mask = ref()
|
|
309
|
-
watch(
|
|
310
|
-
[
|
|
311
|
-
() => props.mask,
|
|
312
|
-
() => props.type,
|
|
313
|
-
() => props.maskEager,
|
|
314
|
-
() => props.maskReversed,
|
|
315
|
-
() => props.maskTokens,
|
|
316
|
-
() => props.maskTokensReplace,
|
|
317
|
-
],
|
|
318
|
-
([newMask, newType, eager, reversed, tokens, tokensReplace]) => {
|
|
319
|
-
if (newMask && newType === INPUT_TYPES.TEXT) {
|
|
320
|
-
mask.value = new Mask({
|
|
321
|
-
mask: newMask,
|
|
322
|
-
eager,
|
|
323
|
-
reversed,
|
|
324
|
-
tokens,
|
|
325
|
-
tokensReplace,
|
|
326
|
-
})
|
|
327
|
-
return
|
|
328
|
-
}
|
|
329
|
-
mask.value = undefined
|
|
330
|
-
},
|
|
331
|
-
{ immediate: true },
|
|
332
|
-
)
|
|
333
|
-
|
|
334
396
|
// auto-width
|
|
335
397
|
const onClickInner = () => {
|
|
336
398
|
if (isClickable.value) {
|
|
@@ -348,6 +410,26 @@
|
|
|
348
410
|
: undefined,
|
|
349
411
|
}
|
|
350
412
|
})
|
|
413
|
+
|
|
414
|
+
// keydown
|
|
415
|
+
const onKeyDown = (event: KeyboardEvent) => {
|
|
416
|
+
switch (event.code) {
|
|
417
|
+
case 'ArrowUp':
|
|
418
|
+
if (isNumber.value) {
|
|
419
|
+
onStepUp()
|
|
420
|
+
event.preventDefault()
|
|
421
|
+
}
|
|
422
|
+
break
|
|
423
|
+
|
|
424
|
+
case 'ArrowDown':
|
|
425
|
+
if (isNumber.value) {
|
|
426
|
+
onStepDown()
|
|
427
|
+
event.preventDefault()
|
|
428
|
+
}
|
|
429
|
+
break
|
|
430
|
+
}
|
|
431
|
+
emit('keydown', event)
|
|
432
|
+
}
|
|
351
433
|
</script>
|
|
352
434
|
|
|
353
435
|
<template>
|
|
@@ -373,10 +455,11 @@
|
|
|
373
455
|
<input
|
|
374
456
|
:id="hasId"
|
|
375
457
|
ref="inputEl"
|
|
376
|
-
v-model="localModelValue"
|
|
377
458
|
v-bind="hasAttrs"
|
|
378
459
|
:style="hasStyle"
|
|
379
460
|
@keyup="emit('keyup', $event)"
|
|
461
|
+
@keydown="onKeyDown"
|
|
462
|
+
@keypress="emit('keypress', $event)"
|
|
380
463
|
/>
|
|
381
464
|
<div
|
|
382
465
|
v-if="(unit || $slots.unit) && isDirty"
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import type { ExtractPropTypes, PropType } from 'vue'
|
|
2
|
-
import type {
|
|
2
|
+
import type { FactoryOpts } from 'imask'
|
|
3
3
|
import { InputTextareaProps } from '../../props'
|
|
4
4
|
|
|
5
5
|
export const INPUT_TYPES = {
|
|
@@ -28,7 +28,23 @@ export const TYPES_ICON = {
|
|
|
28
28
|
SEARCH: 'close',
|
|
29
29
|
} as const
|
|
30
30
|
|
|
31
|
-
export const VvInputTextEvents = [
|
|
31
|
+
export const VvInputTextEvents = [
|
|
32
|
+
'update:modelValue',
|
|
33
|
+
'update:masked',
|
|
34
|
+
'accept',
|
|
35
|
+
'accept:typed',
|
|
36
|
+
'accept:masked',
|
|
37
|
+
'accept:unmasked',
|
|
38
|
+
'complete',
|
|
39
|
+
'complete:typed',
|
|
40
|
+
'complete:masked',
|
|
41
|
+
'complete:unmasked',
|
|
42
|
+
'focus',
|
|
43
|
+
'blur',
|
|
44
|
+
'keyup',
|
|
45
|
+
'keydown',
|
|
46
|
+
'keypress',
|
|
47
|
+
]
|
|
32
48
|
|
|
33
49
|
export const VvInputTextProps = {
|
|
34
50
|
...InputTextareaProps,
|
|
@@ -137,45 +153,20 @@ export const VvInputTextProps = {
|
|
|
137
153
|
default: 'Clear',
|
|
138
154
|
},
|
|
139
155
|
/**
|
|
140
|
-
*
|
|
141
|
-
* @see https://
|
|
156
|
+
* iMask options
|
|
157
|
+
* @see https://imask.js.org/guide.html
|
|
142
158
|
*/
|
|
143
|
-
|
|
144
|
-
type:
|
|
159
|
+
iMask: {
|
|
160
|
+
type: Object as PropType<FactoryOpts>,
|
|
145
161
|
default: undefined,
|
|
146
162
|
},
|
|
147
163
|
/**
|
|
148
|
-
*
|
|
149
|
-
* @see https://beholdr.github.io/maska/#/?id=maskinput-options
|
|
164
|
+
* Masked value
|
|
150
165
|
*/
|
|
151
|
-
|
|
152
|
-
type:
|
|
153
|
-
default: false,
|
|
154
|
-
},
|
|
155
|
-
/**
|
|
156
|
-
* Write values reverse (ex. for numbers)
|
|
157
|
-
* @see https://beholdr.github.io/maska/#/?id=maskinput-options
|
|
158
|
-
*/
|
|
159
|
-
maskReversed: {
|
|
160
|
-
type: Boolean,
|
|
161
|
-
default: false,
|
|
162
|
-
},
|
|
163
|
-
/**
|
|
164
|
-
* Add mask custom tokens
|
|
165
|
-
* @see https://beholdr.github.io/maska/#/?id=custom-tokens
|
|
166
|
-
*/
|
|
167
|
-
maskTokens: {
|
|
168
|
-
type: Object as PropType<MaskTokens>,
|
|
166
|
+
masked: {
|
|
167
|
+
type: String,
|
|
169
168
|
default: undefined,
|
|
170
169
|
},
|
|
171
|
-
/**
|
|
172
|
-
* Replace default tokens
|
|
173
|
-
* @see https://beholdr.github.io/maska/#/?id=custom-tokens
|
|
174
|
-
*/
|
|
175
|
-
maskTokensReplace: {
|
|
176
|
-
type: Boolean,
|
|
177
|
-
default: false,
|
|
178
|
-
},
|
|
179
170
|
/**
|
|
180
171
|
* Adjust input width to content
|
|
181
172
|
*/
|
|
@@ -37,6 +37,9 @@
|
|
|
37
37
|
invalid,
|
|
38
38
|
loading,
|
|
39
39
|
modifiers,
|
|
40
|
+
debounce,
|
|
41
|
+
minlength,
|
|
42
|
+
maxlength,
|
|
40
43
|
} = toRefs(props)
|
|
41
44
|
const hasId = useUniqueId(id)
|
|
42
45
|
const hasHintId = computed(() => `${hasId.value}-hint`)
|
|
@@ -46,7 +49,7 @@
|
|
|
46
49
|
)
|
|
47
50
|
|
|
48
51
|
// debounce
|
|
49
|
-
const localModelValue = useDebouncedInput(modelValue, emit,
|
|
52
|
+
const localModelValue = useDebouncedInput(modelValue, emit, debounce?.value)
|
|
50
53
|
|
|
51
54
|
// icons
|
|
52
55
|
const { hasIcon, hasIconBefore, hasIconAfter } = useComponentIcon(
|
|
@@ -67,9 +70,9 @@
|
|
|
67
70
|
|
|
68
71
|
// count
|
|
69
72
|
const { formatted: countFormatted } = useTextCount(localModelValue, {
|
|
70
|
-
mode:
|
|
71
|
-
upperLimit: Number(
|
|
72
|
-
lowerLimit: Number(
|
|
73
|
+
mode: count?.value,
|
|
74
|
+
upperLimit: Number(maxlength?.value),
|
|
75
|
+
lowerLimit: Number(minlength?.value),
|
|
73
76
|
})
|
|
74
77
|
|
|
75
78
|
// tabindex
|
|
@@ -143,7 +146,7 @@
|
|
|
143
146
|
'aria-errormessage': hasInvalidLabelOrSlot.value
|
|
144
147
|
? hasHintId.value
|
|
145
148
|
: undefined,
|
|
146
|
-
} as TextareaHTMLAttributes
|
|
149
|
+
}) as TextareaHTMLAttributes,
|
|
147
150
|
)
|
|
148
151
|
|
|
149
152
|
// slots props
|
|
@@ -174,42 +174,16 @@ export const argTypes = {
|
|
|
174
174
|
},
|
|
175
175
|
},
|
|
176
176
|
},
|
|
177
|
-
|
|
178
|
-
description: '
|
|
177
|
+
iMask: {
|
|
178
|
+
description: '[iMask](https://imask.js.org/guide.html) options',
|
|
179
179
|
control: {
|
|
180
|
-
type: '
|
|
181
|
-
},
|
|
182
|
-
},
|
|
183
|
-
maskEager: {
|
|
184
|
-
description: 'Show mask before typing',
|
|
185
|
-
table: {
|
|
186
|
-
defaultValue: {
|
|
187
|
-
summary: false,
|
|
188
|
-
},
|
|
189
|
-
},
|
|
190
|
-
},
|
|
191
|
-
maskReversed: {
|
|
192
|
-
description: 'Write typing reverse (ex. for numbers)',
|
|
193
|
-
table: {
|
|
194
|
-
defaultValue: {
|
|
195
|
-
summary: false,
|
|
196
|
-
},
|
|
197
|
-
},
|
|
198
|
-
},
|
|
199
|
-
maskTokens: {
|
|
200
|
-
description: 'Add mask custom tokens',
|
|
201
|
-
table: {
|
|
202
|
-
defaultValue: {
|
|
203
|
-
summary: 'undefined',
|
|
204
|
-
},
|
|
180
|
+
type: 'object',
|
|
205
181
|
},
|
|
206
182
|
},
|
|
207
|
-
|
|
208
|
-
description: '
|
|
209
|
-
|
|
210
|
-
|
|
211
|
-
summary: false,
|
|
212
|
-
},
|
|
183
|
+
masked: {
|
|
184
|
+
description: 'Masked input',
|
|
185
|
+
control: {
|
|
186
|
+
type: 'text',
|
|
213
187
|
},
|
|
214
188
|
},
|
|
215
189
|
autoWidth: {
|
|
@@ -25,14 +25,15 @@ export const Default: Story = {
|
|
|
25
25
|
setup() {
|
|
26
26
|
return { args }
|
|
27
27
|
},
|
|
28
|
-
data: () => ({ inputValue: undefined }),
|
|
28
|
+
data: () => ({ inputValue: undefined, maskedInputValue: undefined }),
|
|
29
29
|
template: /* html */ `
|
|
30
|
-
<vv-input-text v-bind="args" v-model="inputValue" :data-testData="inputValue" data-testId="element">
|
|
30
|
+
<vv-input-text v-bind="args" v-model="inputValue" v-model:masked="maskedInputValue" :data-testData="inputValue" data-testId="element">
|
|
31
31
|
<template #before v-if="args.before"><div class="flex" v-html="args.before"></div></template>
|
|
32
32
|
<template #after v-if="args.after"><div class="flex" v-html="args.after"></div></template>
|
|
33
33
|
<template #hint v-if="args.hint"><span v-html="args.hint"></span></template>
|
|
34
34
|
</vv-input-text>
|
|
35
|
-
<div>Value: <span data-testId="value">{{inputValue}}</span></div>
|
|
35
|
+
<div>Value: <span data-testId="value">{{ inputValue }}</span></div>
|
|
36
|
+
<div v-if="args.iMask" class="mt-sm">Masked Value: <span data-testId="masked-value">{{ maskedInputValue }}</span></div>
|
|
36
37
|
`,
|
|
37
38
|
}),
|
|
38
39
|
play: defaultTest,
|
|
@@ -82,7 +83,6 @@ export const Hint: Story = {
|
|
|
82
83
|
...defaultArgs,
|
|
83
84
|
hintLabel: 'Please enter your name.',
|
|
84
85
|
},
|
|
85
|
-
|
|
86
86
|
}
|
|
87
87
|
|
|
88
88
|
export const Loading: Story = {
|
|
@@ -102,14 +102,6 @@ export const Floating: Story = {
|
|
|
102
102
|
},
|
|
103
103
|
}
|
|
104
104
|
|
|
105
|
-
export const Mask: Story = {
|
|
106
|
-
...Default,
|
|
107
|
-
args: {
|
|
108
|
-
...defaultArgs,
|
|
109
|
-
mask: '##-###-##',
|
|
110
|
-
},
|
|
111
|
-
}
|
|
112
|
-
|
|
113
105
|
export const Unit: Story = {
|
|
114
106
|
...Default,
|
|
115
107
|
args: {
|
|
@@ -4,32 +4,48 @@ import { sleep } from '@/test/sleep'
|
|
|
4
4
|
import { within, userEvent } from '@storybook/testing-library'
|
|
5
5
|
import { INPUT_TYPES, type InputType } from '@/components/VvInputText'
|
|
6
6
|
|
|
7
|
-
const valueByType = (type: InputType, mask?: string) => {
|
|
7
|
+
const valueByType = (type: InputType, mask?: string, id?: string) => {
|
|
8
8
|
if (mask) {
|
|
9
|
-
|
|
9
|
+
switch (id) {
|
|
10
|
+
case 'phone-number':
|
|
11
|
+
return { toType: '393923847556' }
|
|
12
|
+
case 'date-placeholder':
|
|
13
|
+
return { toType: '01011970', toCheck: '1970-01-01' }
|
|
14
|
+
case 'phone-or-email':
|
|
15
|
+
return {
|
|
16
|
+
toType:
|
|
17
|
+
Math.random() < 0.5 ? '393923847556' : 'test@test.com',
|
|
18
|
+
}
|
|
19
|
+
case 'intl-number':
|
|
20
|
+
return { toType: '1234567890.22' }
|
|
21
|
+
case 'currency':
|
|
22
|
+
return { toType: '1234567890.22' }
|
|
23
|
+
default:
|
|
24
|
+
return { toType: '1234567890' }
|
|
25
|
+
}
|
|
10
26
|
}
|
|
11
27
|
switch (type) {
|
|
12
28
|
case INPUT_TYPES.TEXT:
|
|
13
29
|
case INPUT_TYPES.PASSWORD:
|
|
14
30
|
case INPUT_TYPES.SEARCH:
|
|
15
|
-
return 'Lorem ipsum'
|
|
31
|
+
return { toType: 'Lorem ipsum' }
|
|
16
32
|
case INPUT_TYPES.NUMBER:
|
|
17
|
-
return '1'
|
|
33
|
+
return { toType: '1' }
|
|
18
34
|
case INPUT_TYPES.EMAIL:
|
|
19
|
-
return 'test@test.com'
|
|
35
|
+
return { toType: 'test@test.com' }
|
|
20
36
|
case INPUT_TYPES.TEL:
|
|
21
|
-
return '+1234567890'
|
|
37
|
+
return { toType: '+1234567890' }
|
|
22
38
|
case INPUT_TYPES.URL:
|
|
23
|
-
return 'https://www.test.com'
|
|
39
|
+
return { toType: 'https://www.test.com' }
|
|
24
40
|
case INPUT_TYPES.DATE:
|
|
25
|
-
return new Date().toISOString().split('T')[0]
|
|
41
|
+
return { toType: new Date().toISOString().split('T')[0] }
|
|
26
42
|
case INPUT_TYPES.TIME:
|
|
27
|
-
return '12:00'
|
|
43
|
+
return { toType: '12:00' }
|
|
28
44
|
case INPUT_TYPES.COLOR:
|
|
29
45
|
case INPUT_TYPES.DATETIME_LOCAL:
|
|
30
46
|
case INPUT_TYPES.MONTH:
|
|
31
47
|
case INPUT_TYPES.WEEK:
|
|
32
|
-
return undefined
|
|
48
|
+
return { toType: undefined }
|
|
33
49
|
}
|
|
34
50
|
}
|
|
35
51
|
|
|
@@ -45,17 +61,17 @@ export async function defaultTest({ canvasElement, args }: PlayAttributes) {
|
|
|
45
61
|
|
|
46
62
|
// value
|
|
47
63
|
if (!args.invalid && !args.disabled && !args.readonly) {
|
|
48
|
-
const
|
|
49
|
-
if (
|
|
64
|
+
const { toType, toCheck } = valueByType(args.type, args.iMask, args.id)
|
|
65
|
+
if (toType) {
|
|
50
66
|
await expect(input).toBeClicked()
|
|
51
|
-
await userEvent.keyboard(
|
|
67
|
+
await userEvent.keyboard(toType)
|
|
52
68
|
await sleep()
|
|
53
69
|
if (args.maxlength) {
|
|
54
70
|
await expect(value.innerHTML).toEqual(
|
|
55
|
-
|
|
71
|
+
toType.slice(0, args.maxlength),
|
|
56
72
|
)
|
|
57
73
|
} else {
|
|
58
|
-
await expect(value.innerHTML).toEqual(
|
|
74
|
+
await expect(value.innerHTML).toEqual(toCheck ?? toType)
|
|
59
75
|
}
|
|
60
76
|
}
|
|
61
77
|
}
|