@fiscozen/input 3.0.1 → 3.0.3
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/CHANGELOG.md +12 -0
- package/dist/input.js +1747 -1724
- package/dist/input.umd.cjs +5 -5
- package/package.json +3 -3
- package/src/FzCurrencyInput.vue +13 -13
- package/src/FzInput.vue +36 -16
- package/src/__tests__/FzCurrencyInput.spec.ts +104 -0
- package/src/__tests__/FzInput.spec.ts +79 -0
package/package.json
CHANGED
|
@@ -1,15 +1,15 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@fiscozen/input",
|
|
3
|
-
"version": "3.0.
|
|
3
|
+
"version": "3.0.3",
|
|
4
4
|
"description": "Design System Input component",
|
|
5
5
|
"main": "src/index.ts",
|
|
6
6
|
"type": "module",
|
|
7
7
|
"keywords": [],
|
|
8
8
|
"author": "Cristian Barraco",
|
|
9
9
|
"dependencies": {
|
|
10
|
-
"@fiscozen/alert": "3.0.0",
|
|
11
10
|
"@fiscozen/button": "3.0.0",
|
|
12
|
-
"@fiscozen/composables": "1.0.3"
|
|
11
|
+
"@fiscozen/composables": "1.0.3",
|
|
12
|
+
"@fiscozen/alert": "3.0.0"
|
|
13
13
|
},
|
|
14
14
|
"peerDependencies": {
|
|
15
15
|
"tailwindcss": "^3.4.1",
|
package/src/FzCurrencyInput.vue
CHANGED
|
@@ -78,7 +78,7 @@ const getEmptyValue = (): number | null | undefined => {
|
|
|
78
78
|
*/
|
|
79
79
|
const getEmptyDisplayValue = (
|
|
80
80
|
isEmptyValueZero: boolean,
|
|
81
|
-
isCurrentlyFocused: boolean
|
|
81
|
+
isCurrentlyFocused: boolean,
|
|
82
82
|
): string => {
|
|
83
83
|
if (isEmptyValueZero) {
|
|
84
84
|
// During typing, show empty string. Formatting happens on blur
|
|
@@ -327,7 +327,7 @@ const handleBlur = () => {
|
|
|
327
327
|
// Display empty value (formatted zero if zeroOnEmpty is true, empty string otherwise)
|
|
328
328
|
fzInputModel.value = getEmptyDisplayValue(
|
|
329
329
|
expectedEmptyValue === 0,
|
|
330
|
-
false // Not focused during blur
|
|
330
|
+
false, // Not focused during blur
|
|
331
331
|
);
|
|
332
332
|
return;
|
|
333
333
|
}
|
|
@@ -395,7 +395,7 @@ const handleFocus = () => {
|
|
|
395
395
|
* @returns Normalized number value, undefined, or null
|
|
396
396
|
*/
|
|
397
397
|
const normalizeModelValue = (
|
|
398
|
-
value: number | string | undefined | null
|
|
398
|
+
value: number | string | undefined | null,
|
|
399
399
|
): number | undefined | null => {
|
|
400
400
|
if (value === undefined || value === null || value === "") {
|
|
401
401
|
return value === null ? null : undefined;
|
|
@@ -406,7 +406,7 @@ const normalizeModelValue = (
|
|
|
406
406
|
if (typeof value === "string") {
|
|
407
407
|
console.warn(
|
|
408
408
|
"[FzCurrencyInput] String values in v-model are deprecated. Please use number instead. " +
|
|
409
|
-
`Received: "${value}". This will be parsed to a number for retrocompatibility, but string support may be removed in a future version
|
|
409
|
+
`Received: "${value}". This will be parsed to a number for retrocompatibility, but string support may be removed in a future version.`,
|
|
410
410
|
);
|
|
411
411
|
const parsed = parse(value);
|
|
412
412
|
return isNaN(parsed) ? undefined : parsed;
|
|
@@ -512,7 +512,7 @@ onMounted(() => {
|
|
|
512
512
|
// Display empty value (formatted zero if zeroOnEmpty is true, empty string otherwise)
|
|
513
513
|
fzInputModel.value = getEmptyDisplayValue(
|
|
514
514
|
expectedEmptyValue === 0,
|
|
515
|
-
false // Not focused during mount
|
|
515
|
+
false, // Not focused during mount
|
|
516
516
|
);
|
|
517
517
|
return;
|
|
518
518
|
}
|
|
@@ -585,7 +585,7 @@ onMounted(() => {
|
|
|
585
585
|
// Display empty value (formatted zero if zeroOnEmpty is true, empty string otherwise)
|
|
586
586
|
fzInputModel.value = getEmptyDisplayValue(
|
|
587
587
|
emptyValue === 0,
|
|
588
|
-
false // Not focused during mount
|
|
588
|
+
false, // Not focused during mount
|
|
589
589
|
);
|
|
590
590
|
}
|
|
591
591
|
return;
|
|
@@ -621,7 +621,7 @@ watch(
|
|
|
621
621
|
// Display empty value (formatted zero if zeroOnEmpty is true, empty string otherwise)
|
|
622
622
|
fzInputModel.value = getEmptyDisplayValue(
|
|
623
623
|
expectedEmptyValue === 0,
|
|
624
|
-
isFocused.value
|
|
624
|
+
isFocused.value,
|
|
625
625
|
);
|
|
626
626
|
return;
|
|
627
627
|
}
|
|
@@ -705,12 +705,12 @@ watch(
|
|
|
705
705
|
// Display empty value (formatted zero if zeroOnEmpty is true, empty string otherwise)
|
|
706
706
|
fzInputModel.value = getEmptyDisplayValue(
|
|
707
707
|
emptyValue === 0,
|
|
708
|
-
isFocused.value
|
|
708
|
+
isFocused.value,
|
|
709
709
|
);
|
|
710
710
|
}
|
|
711
711
|
return;
|
|
712
712
|
}
|
|
713
|
-
}
|
|
713
|
+
},
|
|
714
714
|
);
|
|
715
715
|
|
|
716
716
|
defineExpose({
|
|
@@ -731,10 +731,10 @@ defineExpose({
|
|
|
731
731
|
@blur="handleBlur"
|
|
732
732
|
@paste="handlePaste"
|
|
733
733
|
>
|
|
734
|
-
<template #label>
|
|
734
|
+
<template v-if="$slots.label" #label>
|
|
735
735
|
<slot name="label"></slot>
|
|
736
736
|
</template>
|
|
737
|
-
<template #left-icon>
|
|
737
|
+
<template v-if="$slots['left-icon']" #left-icon>
|
|
738
738
|
<slot name="left-icon"></slot>
|
|
739
739
|
</template>
|
|
740
740
|
<template #right-icon>
|
|
@@ -786,10 +786,10 @@ defineExpose({
|
|
|
786
786
|
</div>
|
|
787
787
|
</div>
|
|
788
788
|
</template>
|
|
789
|
-
<template #helpText>
|
|
789
|
+
<template v-if="$slots.helpText" #helpText>
|
|
790
790
|
<slot name="helpText"></slot>
|
|
791
791
|
</template>
|
|
792
|
-
<template #errorMessage>
|
|
792
|
+
<template v-if="$slots.errorMessage" #errorMessage>
|
|
793
793
|
<slot name="errorMessage"></slot>
|
|
794
794
|
</template>
|
|
795
795
|
</FzInput>
|
package/src/FzInput.vue
CHANGED
|
@@ -11,7 +11,7 @@
|
|
|
11
11
|
* <FzInput label="Email" type="email" v-model="email" />
|
|
12
12
|
* <FzInput label="Password" type="password" rightIcon="eye" @fzinput:right-icon-click="toggleVisibility" />
|
|
13
13
|
*/
|
|
14
|
-
import { computed, toRefs, Ref, ref, watch, useSlots } from "vue";
|
|
14
|
+
import { computed, toRefs, Ref, ref, watch, useSlots, useAttrs } from "vue";
|
|
15
15
|
import { FzInputProps, type InputEnvironment } from "./types";
|
|
16
16
|
import { FzAlert } from "@fiscozen/alert";
|
|
17
17
|
import { FzIcon } from "@fiscozen/icons";
|
|
@@ -29,6 +29,26 @@ const props = withDefaults(defineProps<FzInputProps>(), {
|
|
|
29
29
|
autocomplete: false,
|
|
30
30
|
});
|
|
31
31
|
|
|
32
|
+
defineOptions({
|
|
33
|
+
inheritAttrs: false,
|
|
34
|
+
});
|
|
35
|
+
|
|
36
|
+
const attrs = useAttrs();
|
|
37
|
+
|
|
38
|
+
/**
|
|
39
|
+
* Attrs forwarded to the native input element.
|
|
40
|
+
* Excludes class which are applied to the root wrapper div
|
|
41
|
+
* so that consumers can control layout/positioning of the component.
|
|
42
|
+
*/
|
|
43
|
+
const inputAttrs = computed(() => {
|
|
44
|
+
return {
|
|
45
|
+
...attrs,
|
|
46
|
+
class: undefined,
|
|
47
|
+
};
|
|
48
|
+
});
|
|
49
|
+
|
|
50
|
+
const rootClass = computed(() => attrs.class);
|
|
51
|
+
|
|
32
52
|
/**
|
|
33
53
|
* Deprecation warning and normalization for size prop.
|
|
34
54
|
* Watches the size prop and warns once on mount if it's provided.
|
|
@@ -45,17 +65,17 @@ watch(
|
|
|
45
65
|
console.warn(
|
|
46
66
|
`[FzInput] Both "size" and "environment" props are provided. ` +
|
|
47
67
|
`"environment=${props.environment}" will be used and "size=${size}" will be ignored. ` +
|
|
48
|
-
`Please remove the deprecated "size" prop
|
|
68
|
+
`Please remove the deprecated "size" prop.`,
|
|
49
69
|
);
|
|
50
70
|
} else {
|
|
51
71
|
console.warn(
|
|
52
72
|
`[FzInput] The "size" prop is deprecated and will be removed in a future version. ` +
|
|
53
|
-
`Please use environment="${mappedEnvironment}" instead of size="${size}"
|
|
73
|
+
`Please use environment="${mappedEnvironment}" instead of size="${size}".`,
|
|
54
74
|
);
|
|
55
75
|
}
|
|
56
76
|
}
|
|
57
77
|
},
|
|
58
|
-
{ immediate: true }
|
|
78
|
+
{ immediate: true },
|
|
59
79
|
);
|
|
60
80
|
|
|
61
81
|
/**
|
|
@@ -68,11 +88,11 @@ watch(
|
|
|
68
88
|
if (rightIconSize !== undefined) {
|
|
69
89
|
console.warn(
|
|
70
90
|
`[FzInput] The "rightIconSize" prop is deprecated and will be removed in a future version. ` +
|
|
71
|
-
`Icons now have a fixed size of "md". The provided value "${rightIconSize}" will be ignored
|
|
91
|
+
`Icons now have a fixed size of "md". The provided value "${rightIconSize}" will be ignored.`,
|
|
72
92
|
);
|
|
73
93
|
}
|
|
74
94
|
},
|
|
75
|
-
{ immediate: true }
|
|
95
|
+
{ immediate: true },
|
|
76
96
|
);
|
|
77
97
|
|
|
78
98
|
/**
|
|
@@ -111,7 +131,7 @@ const {
|
|
|
111
131
|
containerRef,
|
|
112
132
|
model,
|
|
113
133
|
effectiveEnvironment,
|
|
114
|
-
isFocused
|
|
134
|
+
isFocused,
|
|
115
135
|
);
|
|
116
136
|
|
|
117
137
|
const slots = defineSlots<{
|
|
@@ -213,7 +233,7 @@ const handleIconKeydown = (
|
|
|
213
233
|
emitEvent:
|
|
214
234
|
| "fzinput:left-icon-click"
|
|
215
235
|
| "fzinput:right-icon-click"
|
|
216
|
-
| "fzinput:second-right-icon-click"
|
|
236
|
+
| "fzinput:second-right-icon-click",
|
|
217
237
|
) => {
|
|
218
238
|
if (e.key === "Enter" || e.key === " ") {
|
|
219
239
|
e.preventDefault();
|
|
@@ -273,7 +293,7 @@ const isLeftIconClickable = computed(() => !!props.leftIcon);
|
|
|
273
293
|
* Icons are only accessible via keyboard when aria-label is provided.
|
|
274
294
|
*/
|
|
275
295
|
const isLeftIconAccessible = computed(
|
|
276
|
-
() => isLeftIconClickable.value && !!props.leftIconAriaLabel
|
|
296
|
+
() => isLeftIconClickable.value && !!props.leftIconAriaLabel,
|
|
277
297
|
);
|
|
278
298
|
|
|
279
299
|
/**
|
|
@@ -282,14 +302,14 @@ const isLeftIconAccessible = computed(
|
|
|
282
302
|
* Readonly inputs have the same visual styling and behavior as disabled inputs.
|
|
283
303
|
*/
|
|
284
304
|
const isReadonlyOrDisabled = computed(
|
|
285
|
-
() => !!props.disabled || !!props.readonly
|
|
305
|
+
() => !!props.disabled || !!props.readonly,
|
|
286
306
|
);
|
|
287
307
|
|
|
288
308
|
/**
|
|
289
309
|
* Determines if right icon is clickable (not rendered as button)
|
|
290
310
|
*/
|
|
291
311
|
const isRightIconClickable = computed(
|
|
292
|
-
() => !!props.rightIcon && !props.rightIconButton
|
|
312
|
+
() => !!props.rightIcon && !props.rightIconButton,
|
|
293
313
|
);
|
|
294
314
|
|
|
295
315
|
/**
|
|
@@ -298,14 +318,14 @@ const isRightIconClickable = computed(
|
|
|
298
318
|
* Icons are only accessible via keyboard when aria-label is provided.
|
|
299
319
|
*/
|
|
300
320
|
const isRightIconAccessible = computed(
|
|
301
|
-
() => isRightIconClickable.value && !!props.rightIconAriaLabel
|
|
321
|
+
() => isRightIconClickable.value && !!props.rightIconAriaLabel,
|
|
302
322
|
);
|
|
303
323
|
|
|
304
324
|
/**
|
|
305
325
|
* Determines if second right icon is clickable (not rendered as button)
|
|
306
326
|
*/
|
|
307
327
|
const isSecondRightIconClickable = computed(
|
|
308
|
-
() => !!props.secondRightIcon && !props.secondRightIconButton
|
|
328
|
+
() => !!props.secondRightIcon && !props.secondRightIconButton,
|
|
309
329
|
);
|
|
310
330
|
|
|
311
331
|
/**
|
|
@@ -314,7 +334,7 @@ const isSecondRightIconClickable = computed(
|
|
|
314
334
|
* Icons are only accessible via keyboard when aria-label is provided.
|
|
315
335
|
*/
|
|
316
336
|
const isSecondRightIconAccessible = computed(
|
|
317
|
-
() => isSecondRightIconClickable.value && !!props.secondRightIconAriaLabel
|
|
337
|
+
() => isSecondRightIconClickable.value && !!props.secondRightIconAriaLabel,
|
|
318
338
|
);
|
|
319
339
|
|
|
320
340
|
defineExpose({
|
|
@@ -324,7 +344,7 @@ defineExpose({
|
|
|
324
344
|
</script>
|
|
325
345
|
|
|
326
346
|
<template>
|
|
327
|
-
<div class="fz-input w-full flex flex-col gap-8">
|
|
347
|
+
<div class="fz-input w-full flex flex-col gap-8" :class="rootClass">
|
|
328
348
|
<slot name="label">
|
|
329
349
|
<label
|
|
330
350
|
v-if="label"
|
|
@@ -391,7 +411,7 @@ defineExpose({
|
|
|
391
411
|
:aria-disabled="isReadonlyOrDisabled ? 'true' : 'false'"
|
|
392
412
|
:aria-labelledby="ariaLabelledBy"
|
|
393
413
|
:aria-describedby="ariaDescribedBy"
|
|
394
|
-
v-bind="
|
|
414
|
+
v-bind="inputAttrs"
|
|
395
415
|
@blur="
|
|
396
416
|
(e) => {
|
|
397
417
|
isFocused = false;
|
|
@@ -1588,5 +1588,109 @@ describe('FzCurrencyInput', () => {
|
|
|
1588
1588
|
})
|
|
1589
1589
|
})
|
|
1590
1590
|
})
|
|
1591
|
+
|
|
1592
|
+
describe('Slot forwarding', () => {
|
|
1593
|
+
it('should not render empty help text span when helpText slot is not provided', async () => {
|
|
1594
|
+
const wrapper = mount(FzCurrencyInput, {
|
|
1595
|
+
props: {
|
|
1596
|
+
label: 'Label',
|
|
1597
|
+
modelValue: 10,
|
|
1598
|
+
},
|
|
1599
|
+
})
|
|
1600
|
+
|
|
1601
|
+
await wrapper.vm.$nextTick()
|
|
1602
|
+
|
|
1603
|
+
const helpSpan = wrapper.find(`[id$="-help"]`)
|
|
1604
|
+
expect(helpSpan.exists()).toBe(false)
|
|
1605
|
+
})
|
|
1606
|
+
|
|
1607
|
+
it('should render help text span when helpText slot is provided', async () => {
|
|
1608
|
+
const wrapper = mount(FzCurrencyInput, {
|
|
1609
|
+
props: {
|
|
1610
|
+
label: 'Label',
|
|
1611
|
+
modelValue: 10,
|
|
1612
|
+
},
|
|
1613
|
+
slots: {
|
|
1614
|
+
helpText: 'Enter an amount',
|
|
1615
|
+
},
|
|
1616
|
+
})
|
|
1617
|
+
|
|
1618
|
+
await wrapper.vm.$nextTick()
|
|
1619
|
+
|
|
1620
|
+
const helpSpan = wrapper.find(`[id$="-help"]`)
|
|
1621
|
+
expect(helpSpan.exists()).toBe(true)
|
|
1622
|
+
expect(helpSpan.text()).toContain('Enter an amount')
|
|
1623
|
+
})
|
|
1624
|
+
|
|
1625
|
+
it('should render label from label prop when label slot is not provided', async () => {
|
|
1626
|
+
const wrapper = mount(FzCurrencyInput, {
|
|
1627
|
+
props: {
|
|
1628
|
+
label: 'Amount',
|
|
1629
|
+
modelValue: 10,
|
|
1630
|
+
},
|
|
1631
|
+
})
|
|
1632
|
+
|
|
1633
|
+
await wrapper.vm.$nextTick()
|
|
1634
|
+
|
|
1635
|
+
const label = wrapper.find('label')
|
|
1636
|
+
expect(label.exists()).toBe(true)
|
|
1637
|
+
expect(label.text()).toContain('Amount')
|
|
1638
|
+
})
|
|
1639
|
+
|
|
1640
|
+
it('should render custom label slot instead of label prop', async () => {
|
|
1641
|
+
const wrapper = mount(FzCurrencyInput, {
|
|
1642
|
+
props: {
|
|
1643
|
+
label: 'Amount',
|
|
1644
|
+
modelValue: 10,
|
|
1645
|
+
},
|
|
1646
|
+
slots: {
|
|
1647
|
+
label: '<strong>Custom Label</strong>',
|
|
1648
|
+
},
|
|
1649
|
+
})
|
|
1650
|
+
|
|
1651
|
+
await wrapper.vm.$nextTick()
|
|
1652
|
+
|
|
1653
|
+
const strong = wrapper.find('strong')
|
|
1654
|
+
expect(strong.exists()).toBe(true)
|
|
1655
|
+
expect(strong.text()).toBe('Custom Label')
|
|
1656
|
+
|
|
1657
|
+
const defaultLabel = wrapper.find('label')
|
|
1658
|
+
expect(defaultLabel.exists()).toBe(false)
|
|
1659
|
+
})
|
|
1660
|
+
|
|
1661
|
+
it('should not render error message container when errorMessage slot is not provided', async () => {
|
|
1662
|
+
const wrapper = mount(FzCurrencyInput, {
|
|
1663
|
+
props: {
|
|
1664
|
+
label: 'Label',
|
|
1665
|
+
modelValue: 10,
|
|
1666
|
+
error: true,
|
|
1667
|
+
},
|
|
1668
|
+
})
|
|
1669
|
+
|
|
1670
|
+
await wrapper.vm.$nextTick()
|
|
1671
|
+
|
|
1672
|
+
const errorContainer = wrapper.find('[role="alert"]')
|
|
1673
|
+
expect(errorContainer.exists()).toBe(false)
|
|
1674
|
+
})
|
|
1675
|
+
|
|
1676
|
+
it('should render error message when error is true and errorMessage slot is provided', async () => {
|
|
1677
|
+
const wrapper = mount(FzCurrencyInput, {
|
|
1678
|
+
props: {
|
|
1679
|
+
label: 'Label',
|
|
1680
|
+
modelValue: 10,
|
|
1681
|
+
error: true,
|
|
1682
|
+
},
|
|
1683
|
+
slots: {
|
|
1684
|
+
errorMessage: 'Invalid amount',
|
|
1685
|
+
},
|
|
1686
|
+
})
|
|
1687
|
+
|
|
1688
|
+
await wrapper.vm.$nextTick()
|
|
1689
|
+
|
|
1690
|
+
const errorContainer = wrapper.find('[role="alert"]')
|
|
1691
|
+
expect(errorContainer.exists()).toBe(true)
|
|
1692
|
+
expect(errorContainer.text()).toContain('Invalid amount')
|
|
1693
|
+
})
|
|
1694
|
+
})
|
|
1591
1695
|
})
|
|
1592
1696
|
|
|
@@ -1024,6 +1024,85 @@ describe('FzInput', () => {
|
|
|
1024
1024
|
})
|
|
1025
1025
|
})
|
|
1026
1026
|
|
|
1027
|
+
describe('Attribute forwarding (inheritAttrs: false)', () => {
|
|
1028
|
+
it('applies consumer class to root wrapper div', async () => {
|
|
1029
|
+
const wrapper = mount(FzInput, {
|
|
1030
|
+
props: { label: 'Label' },
|
|
1031
|
+
attrs: { class: 'max-w-xs custom-class' },
|
|
1032
|
+
})
|
|
1033
|
+
|
|
1034
|
+
await wrapper.vm.$nextTick()
|
|
1035
|
+
|
|
1036
|
+
const rootDiv = wrapper.element as HTMLElement
|
|
1037
|
+
expect(rootDiv.classList.contains('max-w-xs')).toBe(true)
|
|
1038
|
+
expect(rootDiv.classList.contains('custom-class')).toBe(true)
|
|
1039
|
+
expect(rootDiv.classList.contains('fz-input')).toBe(true)
|
|
1040
|
+
})
|
|
1041
|
+
|
|
1042
|
+
it('does not apply consumer class to native input element', async () => {
|
|
1043
|
+
const wrapper = mount(FzInput, {
|
|
1044
|
+
props: { label: 'Label' },
|
|
1045
|
+
attrs: { class: 'max-w-xs' },
|
|
1046
|
+
})
|
|
1047
|
+
|
|
1048
|
+
await wrapper.vm.$nextTick()
|
|
1049
|
+
|
|
1050
|
+
const input = wrapper.find('input').element as HTMLInputElement
|
|
1051
|
+
expect(input.classList.contains('max-w-xs')).toBe(false)
|
|
1052
|
+
})
|
|
1053
|
+
|
|
1054
|
+
it('forwards data-* attributes to native input element', async () => {
|
|
1055
|
+
const wrapper = mount(FzInput, {
|
|
1056
|
+
props: { label: 'Label' },
|
|
1057
|
+
attrs: { 'data-cy': 'my-input', 'data-testid': 'test-input' },
|
|
1058
|
+
})
|
|
1059
|
+
|
|
1060
|
+
await wrapper.vm.$nextTick()
|
|
1061
|
+
|
|
1062
|
+
const input = wrapper.find('input').element as HTMLInputElement
|
|
1063
|
+
expect(input.getAttribute('data-cy')).toBe('my-input')
|
|
1064
|
+
expect(input.getAttribute('data-testid')).toBe('test-input')
|
|
1065
|
+
|
|
1066
|
+
const rootDiv = wrapper.element as HTMLElement
|
|
1067
|
+
expect(rootDiv.getAttribute('data-cy')).toBeNull()
|
|
1068
|
+
})
|
|
1069
|
+
|
|
1070
|
+
it('forwards consumer id to native input element (overriding internal id)', async () => {
|
|
1071
|
+
const wrapper = mount(FzInput, {
|
|
1072
|
+
props: { label: 'Label' },
|
|
1073
|
+
attrs: { id: 'custom-id' },
|
|
1074
|
+
})
|
|
1075
|
+
|
|
1076
|
+
await wrapper.vm.$nextTick()
|
|
1077
|
+
|
|
1078
|
+
const input = wrapper.find('input').element as HTMLInputElement
|
|
1079
|
+
expect(input.getAttribute('id')).toBe('custom-id')
|
|
1080
|
+
|
|
1081
|
+
const rootDiv = wrapper.element as HTMLElement
|
|
1082
|
+
expect(rootDiv.getAttribute('id')).toBeNull()
|
|
1083
|
+
})
|
|
1084
|
+
|
|
1085
|
+
it('forwards consumer aria-* attributes to native input element', async () => {
|
|
1086
|
+
const wrapper = mount(FzInput, {
|
|
1087
|
+
props: { label: 'Label' },
|
|
1088
|
+
attrs: {
|
|
1089
|
+
'aria-expanded': 'true',
|
|
1090
|
+
'aria-haspopup': 'listbox',
|
|
1091
|
+
},
|
|
1092
|
+
})
|
|
1093
|
+
|
|
1094
|
+
await wrapper.vm.$nextTick()
|
|
1095
|
+
|
|
1096
|
+
const input = wrapper.find('input').element as HTMLInputElement
|
|
1097
|
+
expect(input.getAttribute('aria-expanded')).toBe('true')
|
|
1098
|
+
expect(input.getAttribute('aria-haspopup')).toBe('listbox')
|
|
1099
|
+
|
|
1100
|
+
const rootDiv = wrapper.element as HTMLElement
|
|
1101
|
+
expect(rootDiv.getAttribute('aria-expanded')).toBeNull()
|
|
1102
|
+
expect(rootDiv.getAttribute('aria-haspopup')).toBeNull()
|
|
1103
|
+
})
|
|
1104
|
+
})
|
|
1105
|
+
|
|
1027
1106
|
describe('Edge cases', () => {
|
|
1028
1107
|
it(`renders ${NUMBER_OF_INPUTS} input with different ids`, async () => {
|
|
1029
1108
|
const wrapperList = Array.from({ length: NUMBER_OF_INPUTS }).map((_, i) =>
|