@datametria/vue-components 1.1.3 → 2.0.1

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.
Files changed (97) hide show
  1. package/README.md +548 -590
  2. package/dist/index.es.js +2460 -1440
  3. package/dist/index.umd.js +10 -10
  4. package/dist/vue-components.css +1 -1
  5. package/package.json +102 -98
  6. package/src/components/DatametriaAlert.vue +38 -24
  7. package/src/components/DatametriaAutocomplete.vue +184 -138
  8. package/src/components/DatametriaAvatar.vue +177 -33
  9. package/src/components/DatametriaBadge.vue +31 -23
  10. package/src/components/DatametriaBreadcrumb.vue +21 -21
  11. package/src/components/DatametriaButton.vue +38 -18
  12. package/src/components/DatametriaCard.vue +12 -12
  13. package/src/components/DatametriaCheckbox.vue +8 -8
  14. package/src/components/DatametriaChip.vue +29 -33
  15. package/src/components/DatametriaContainer.vue +4 -4
  16. package/src/components/DatametriaDatePicker.vue +686 -68
  17. package/src/components/DatametriaDivider.vue +13 -13
  18. package/src/components/DatametriaFileUpload.vue +272 -140
  19. package/src/components/DatametriaGrid.vue +3 -3
  20. package/src/components/DatametriaInput.vue +15 -15
  21. package/src/components/DatametriaMenu.vue +604 -619
  22. package/src/components/DatametriaModal.vue +16 -16
  23. package/src/components/DatametriaNavbar.vue +57 -54
  24. package/src/components/DatametriaPasswordInput.vue +430 -0
  25. package/src/components/DatametriaProgress.vue +18 -18
  26. package/src/components/DatametriaRadio.vue +20 -20
  27. package/src/components/DatametriaSelect.vue +15 -15
  28. package/src/components/DatametriaSkeleton.vue +243 -239
  29. package/src/components/DatametriaSlider.vue +395 -407
  30. package/src/components/DatametriaSortableTable.vue +585 -0
  31. package/src/components/DatametriaSpinner.vue +7 -7
  32. package/src/components/DatametriaSwitch.vue +16 -16
  33. package/src/components/DatametriaTable.vue +14 -14
  34. package/src/components/DatametriaTextarea.vue +28 -28
  35. package/src/components/DatametriaTimePicker.vue +285 -285
  36. package/src/components/DatametriaToast.vue +32 -19
  37. package/src/components/DatametriaTooltip.vue +408 -408
  38. package/src/components/__tests__/DatametriaAlert.test.js +36 -0
  39. package/src/components/__tests__/DatametriaAlert.test.ts +190 -0
  40. package/src/components/__tests__/DatametriaAutocomplete.test.ts +180 -0
  41. package/src/components/__tests__/DatametriaAvatar.test.ts +152 -0
  42. package/src/components/__tests__/DatametriaBadge.test.js +30 -0
  43. package/src/components/__tests__/DatametriaBadge.test.ts +167 -0
  44. package/src/components/__tests__/DatametriaBreadcrumb.test.ts +75 -0
  45. package/src/components/__tests__/DatametriaButton.test.js +31 -0
  46. package/src/components/__tests__/DatametriaButton.test.ts +283 -0
  47. package/src/components/__tests__/DatametriaCard.test.ts +201 -0
  48. package/src/components/__tests__/DatametriaCheckbox.test.ts +47 -0
  49. package/src/components/__tests__/DatametriaChip.test.js +39 -0
  50. package/src/components/__tests__/DatametriaContainer.test.ts +52 -0
  51. package/src/components/__tests__/DatametriaDatePicker.test.ts +234 -0
  52. package/src/components/__tests__/DatametriaDivider.test.ts +54 -0
  53. package/src/components/__tests__/DatametriaFileUpload.test.ts +291 -0
  54. package/src/components/__tests__/DatametriaGrid.test.ts +31 -0
  55. package/src/components/__tests__/DatametriaInput.test.ts +72 -0
  56. package/src/components/__tests__/DatametriaMenu.test.ts +366 -0
  57. package/src/components/__tests__/DatametriaModal.test.ts +86 -0
  58. package/src/components/__tests__/DatametriaNavbar.test.js +49 -0
  59. package/src/components/__tests__/DatametriaNavbar.test.ts +203 -0
  60. package/src/components/__tests__/DatametriaPasswordInput.test.js +305 -0
  61. package/src/components/__tests__/DatametriaProgress.test.ts +90 -0
  62. package/src/components/__tests__/DatametriaRadio.test.ts +77 -0
  63. package/src/components/__tests__/DatametriaSelect.test.ts +77 -0
  64. package/src/components/__tests__/DatametriaSlider.test.ts +261 -0
  65. package/src/components/__tests__/DatametriaSortableTable.test.js +168 -0
  66. package/src/components/__tests__/DatametriaSpinner.test.ts +156 -0
  67. package/src/components/__tests__/DatametriaSwitch.test.ts +64 -0
  68. package/src/components/__tests__/DatametriaTable.test.ts +97 -0
  69. package/src/components/__tests__/DatametriaTextarea.test.ts +66 -0
  70. package/src/components/__tests__/DatametriaToast.test.js +49 -0
  71. package/src/components/__tests__/DatametriaToast.test.ts +99 -0
  72. package/src/composables/useAccessibilityScale.ts +94 -94
  73. package/src/composables/useBreakpoints.ts +82 -82
  74. package/src/composables/useHapticFeedback.ts +439 -439
  75. package/src/composables/useRipple.ts +218 -218
  76. package/src/index.ts +68 -61
  77. package/src/stories/Variants.stories.js +96 -0
  78. package/src/styles/design-tokens.css +623 -623
  79. package/src/theme/ThemeProvider.vue +96 -0
  80. package/src/theme/__tests__/ThemeProvider.test.ts +208 -0
  81. package/src/theme/__tests__/constants.test.ts +31 -0
  82. package/src/theme/__tests__/presets.test.ts +166 -0
  83. package/src/theme/__tests__/tokens.test.ts +155 -0
  84. package/src/theme/__tests__/types.test.ts +153 -0
  85. package/src/theme/__tests__/useTheme.test.ts +146 -0
  86. package/src/theme/constants.ts +14 -0
  87. package/src/theme/index.ts +12 -0
  88. package/src/theme/presets/datametria.ts +94 -0
  89. package/src/theme/presets/default.ts +94 -0
  90. package/src/theme/presets/index.ts +8 -0
  91. package/src/theme/tokens/colors.ts +28 -0
  92. package/src/theme/tokens/index.ts +47 -0
  93. package/src/theme/tokens/spacing.ts +21 -0
  94. package/src/theme/tokens/typography.ts +35 -0
  95. package/src/theme/types.ts +111 -0
  96. package/src/theme/useTheme.ts +28 -0
  97. package/src/types/index.ts +19 -0
@@ -0,0 +1,305 @@
1
+ import { describe, it, expect, vi } from 'vitest'
2
+ import { mount } from '@vue/test-utils'
3
+ import DatametriaPasswordInput from '../DatametriaPasswordInput.vue'
4
+
5
+ describe('DatametriaPasswordInput', () => {
6
+ it('renders correctly', () => {
7
+ const wrapper = mount(DatametriaPasswordInput, {
8
+ props: {
9
+ label: 'Senha'
10
+ }
11
+ })
12
+ expect(wrapper.find('.datametria-password-input').exists()).toBe(true)
13
+ expect(wrapper.find('label').text()).toContain('Senha')
14
+ })
15
+
16
+ it('toggles password visibility', async () => {
17
+ const wrapper = mount(DatametriaPasswordInput)
18
+ const input = wrapper.find('input')
19
+ const toggleButton = wrapper.find('.datametria-password-input__toggle')
20
+
21
+ expect(input.attributes('type')).toBe('password')
22
+
23
+ await toggleButton.trigger('click')
24
+ expect(input.attributes('type')).toBe('text')
25
+
26
+ await toggleButton.trigger('click')
27
+ expect(input.attributes('type')).toBe('password')
28
+ })
29
+
30
+ it('validates password strength', async () => {
31
+ const wrapper = mount(DatametriaPasswordInput, {
32
+ props: {
33
+ modelValue: '',
34
+ showStrength: true
35
+ }
36
+ })
37
+
38
+ // Weak password
39
+ await wrapper.setProps({ modelValue: 'abc' })
40
+ expect(wrapper.find('.datametria-password-input__strength-text--weak').exists()).toBe(true)
41
+
42
+ // Strong password
43
+ await wrapper.setProps({ modelValue: 'Abc123!@#' })
44
+ expect(wrapper.find('.datametria-password-input__strength-text--strong').exists()).toBe(true)
45
+ })
46
+
47
+ it('shows requirements when focused', async () => {
48
+ const wrapper = mount(DatametriaPasswordInput, {
49
+ props: {
50
+ showRequirements: true
51
+ }
52
+ })
53
+
54
+ const input = wrapper.find('input')
55
+
56
+ expect(wrapper.find('.datametria-password-input__requirements').exists()).toBe(false)
57
+
58
+ await input.trigger('focus')
59
+ expect(wrapper.find('.datametria-password-input__requirements').exists()).toBe(true)
60
+
61
+ await input.trigger('blur')
62
+ expect(wrapper.find('.datametria-password-input__requirements').exists()).toBe(false)
63
+ })
64
+
65
+ it('validates all requirements', async () => {
66
+ const wrapper = mount(DatametriaPasswordInput, {
67
+ props: {
68
+ modelValue: 'Abc123!@#',
69
+ showRequirements: true
70
+ }
71
+ })
72
+
73
+ await wrapper.find('input').trigger('focus')
74
+
75
+ const requirements = wrapper.findAll('.datametria-password-input__requirements-list li')
76
+ expect(requirements).toHaveLength(5)
77
+
78
+ // All requirements should be valid
79
+ requirements.forEach(req => {
80
+ expect(req.classes()).toContain('valid')
81
+ })
82
+ })
83
+
84
+ it('emits update:modelValue on input', async () => {
85
+ const wrapper = mount(DatametriaPasswordInput)
86
+ const input = wrapper.find('input')
87
+
88
+ await input.setValue('test123')
89
+ expect(wrapper.emitted('update:modelValue')).toBeTruthy()
90
+ expect(wrapper.emitted('update:modelValue')?.[0]).toEqual(['test123'])
91
+ })
92
+
93
+ it('emits strength-change event', async () => {
94
+ const wrapper = mount(DatametriaPasswordInput, {
95
+ props: {
96
+ modelValue: ''
97
+ }
98
+ })
99
+
100
+ await wrapper.setProps({ modelValue: 'Abc123!@#' })
101
+ await wrapper.vm.$nextTick()
102
+
103
+ expect(wrapper.emitted('strength-change')).toBeTruthy()
104
+ expect(wrapper.emitted('strength-change')?.[0]?.[0]).toBe(100)
105
+ })
106
+
107
+ it('shows error message', () => {
108
+ const wrapper = mount(DatametriaPasswordInput, {
109
+ props: {
110
+ errorMessage: 'Senha inválida'
111
+ }
112
+ })
113
+
114
+ expect(wrapper.find('.datametria-password-input__error').text()).toBe('Senha inválida')
115
+ expect(wrapper.find('input').classes()).toContain('datametria-password-input__field--error')
116
+ })
117
+
118
+ it('shows help text', () => {
119
+ const wrapper = mount(DatametriaPasswordInput, {
120
+ props: {
121
+ helpText: 'Use uma senha forte'
122
+ }
123
+ })
124
+
125
+ expect(wrapper.find('.datametria-password-input__help').text()).toBe('Use uma senha forte')
126
+ })
127
+
128
+ it('marks required field', () => {
129
+ const wrapper = mount(DatametriaPasswordInput, {
130
+ props: {
131
+ label: 'Senha',
132
+ required: true
133
+ }
134
+ })
135
+
136
+ expect(wrapper.find('.datametria-password-input__required').exists()).toBe(true)
137
+ expect(wrapper.find('input').attributes('required')).toBeDefined()
138
+ })
139
+
140
+ it('disables input when disabled prop is true', () => {
141
+ const wrapper = mount(DatametriaPasswordInput, {
142
+ props: {
143
+ disabled: true
144
+ }
145
+ })
146
+
147
+ const input = wrapper.find('input')
148
+ expect(input.attributes('disabled')).toBeDefined()
149
+ expect(input.classes()).toContain('datametria-password-input__field--disabled')
150
+ })
151
+
152
+ it('sets correct autocomplete attribute', () => {
153
+ const wrapper = mount(DatametriaPasswordInput, {
154
+ props: {
155
+ autocomplete: 'new-password'
156
+ }
157
+ })
158
+
159
+ expect(wrapper.find('input').attributes('autocomplete')).toBe('new-password')
160
+ })
161
+
162
+ it('has proper ARIA attributes', async () => {
163
+ const wrapper = mount(DatametriaPasswordInput, {
164
+ props: {
165
+ errorMessage: 'Senha inválida',
166
+ showRequirements: true
167
+ }
168
+ })
169
+
170
+ const input = wrapper.find('input')
171
+
172
+ expect(input.attributes('aria-invalid')).toBe('true')
173
+
174
+ await input.trigger('focus')
175
+ expect(input.attributes('aria-describedby')).toBeTruthy()
176
+ })
177
+
178
+ it('toggle button has proper accessibility', () => {
179
+ const wrapper = mount(DatametriaPasswordInput)
180
+ const toggleButton = wrapper.find('.datametria-password-input__toggle')
181
+
182
+ expect(toggleButton.attributes('aria-label')).toBe('Mostrar senha')
183
+ expect(toggleButton.attributes('type')).toBe('button')
184
+ expect(toggleButton.attributes('tabindex')).toBe('-1')
185
+ })
186
+
187
+ describe('CSS Variables Integration', () => {
188
+ it('uses semantic color tokens for strength indicators', async () => {
189
+ const wrapper = mount(DatametriaPasswordInput, {
190
+ props: {
191
+ modelValue: 'Abc123!@#',
192
+ showStrength: true
193
+ }
194
+ })
195
+
196
+ expect(wrapper.find('.datametria-password-input__strength-fill--strong').exists()).toBe(true)
197
+ expect(wrapper.find('.datametria-password-input__strength-text--strong').exists()).toBe(true)
198
+ })
199
+
200
+ it('uses error token for error states', () => {
201
+ const wrapper = mount(DatametriaPasswordInput, {
202
+ props: {
203
+ errorMessage: 'Error'
204
+ }
205
+ })
206
+
207
+ expect(wrapper.find('.datametria-password-input__field--error').exists()).toBe(true)
208
+ expect(wrapper.find('.datametria-password-input__error').exists()).toBe(true)
209
+ })
210
+
211
+ it('uses warning token for caps lock warning', async () => {
212
+ const wrapper = mount(DatametriaPasswordInput)
213
+ const input = wrapper.find('input')
214
+
215
+ const event = new KeyboardEvent('keyup', { key: 'A' })
216
+ Object.defineProperty(event, 'getModifierState', {
217
+ value: vi.fn(() => true)
218
+ })
219
+
220
+ await input.element.dispatchEvent(event)
221
+ await wrapper.vm.$nextTick()
222
+
223
+ expect(wrapper.find('.datametria-password-input__warning').exists()).toBe(true)
224
+ })
225
+
226
+ it('uses success token for valid requirements', async () => {
227
+ const wrapper = mount(DatametriaPasswordInput, {
228
+ props: {
229
+ modelValue: 'Abc123!@#',
230
+ showRequirements: true
231
+ }
232
+ })
233
+
234
+ await wrapper.find('input').trigger('focus')
235
+ const validItems = wrapper.findAll('.datametria-password-input__requirements-list li.valid')
236
+ expect(validItems.length).toBe(5)
237
+ })
238
+
239
+ it('uses spacing tokens consistently', () => {
240
+ const wrapper = mount(DatametriaPasswordInput, {
241
+ props: {
242
+ label: 'Password',
243
+ showRequirements: true
244
+ }
245
+ })
246
+
247
+ expect(wrapper.find('.datametria-password-input').exists()).toBe(true)
248
+ expect(wrapper.find('.datametria-password-input__field').exists()).toBe(true)
249
+ })
250
+
251
+ it('uses typography tokens for text elements', () => {
252
+ const wrapper = mount(DatametriaPasswordInput, {
253
+ props: {
254
+ label: 'Password',
255
+ helpText: 'Help text'
256
+ }
257
+ })
258
+
259
+ expect(wrapper.find('.datametria-password-input__label').exists()).toBe(true)
260
+ expect(wrapper.find('.datametria-password-input__help').exists()).toBe(true)
261
+ })
262
+
263
+ it('uses transition tokens for animations', async () => {
264
+ const wrapper = mount(DatametriaPasswordInput, {
265
+ props: {
266
+ modelValue: '',
267
+ showStrength: true
268
+ }
269
+ })
270
+
271
+ await wrapper.setProps({ modelValue: 'test' })
272
+ expect(wrapper.find('.datametria-password-input__strength-fill').exists()).toBe(true)
273
+ })
274
+ })
275
+
276
+ describe('Backward Compatibility', () => {
277
+ it('works without ThemeProvider', () => {
278
+ const wrapper = mount(DatametriaPasswordInput, {
279
+ props: {
280
+ label: 'Password',
281
+ modelValue: 'Abc123!@#',
282
+ showStrength: true,
283
+ showRequirements: true
284
+ }
285
+ })
286
+
287
+ expect(wrapper.find('.datametria-password-input').exists()).toBe(true)
288
+ expect(wrapper.find('.datametria-password-input__strength').exists()).toBe(true)
289
+ })
290
+
291
+ it('maintains original visual appearance with fallback values', async () => {
292
+ const wrapper = mount(DatametriaPasswordInput, {
293
+ props: {
294
+ modelValue: 'weak',
295
+ showStrength: true
296
+ }
297
+ })
298
+
299
+ expect(wrapper.find('.datametria-password-input__strength-fill--weak').exists()).toBe(true)
300
+
301
+ await wrapper.setProps({ modelValue: 'Abc123!@#' })
302
+ expect(wrapper.find('.datametria-password-input__strength-fill--strong').exists()).toBe(true)
303
+ })
304
+ })
305
+ })
@@ -0,0 +1,90 @@
1
+ import { describe, it, expect } from 'vitest'
2
+ import { mount } from '@vue/test-utils'
3
+ import DatametriaProgress from '../DatametriaProgress.vue'
4
+
5
+ describe('DatametriaProgress', () => {
6
+ it('renders with value', () => {
7
+ const wrapper = mount(DatametriaProgress, {
8
+ props: { value: 50 }
9
+ })
10
+ expect(wrapper.find('.dm-progress').exists()).toBe(true)
11
+ expect(wrapper.find('.dm-progress__bar').attributes('style')).toContain('width: 50%')
12
+ })
13
+
14
+ it('renders with label', () => {
15
+ const wrapper = mount(DatametriaProgress, {
16
+ props: { value: 75, label: 'Loading' }
17
+ })
18
+ expect(wrapper.find('.dm-progress__label').text()).toBe('Loading')
19
+ })
20
+
21
+ it('shows value by default', () => {
22
+ const wrapper = mount(DatametriaProgress, {
23
+ props: { value: 60, label: 'Test' }
24
+ })
25
+ expect(wrapper.find('.dm-progress__value').text()).toBe('60%')
26
+ })
27
+
28
+ it('hides value when showValue is false', () => {
29
+ const wrapper = mount(DatametriaProgress, {
30
+ props: { value: 60, label: 'Test', showValue: false }
31
+ })
32
+ expect(wrapper.find('.dm-progress__value').exists()).toBe(false)
33
+ })
34
+
35
+ it('clamps value to 0-100 range', () => {
36
+ const wrapper1 = mount(DatametriaProgress, { props: { value: -10 } })
37
+ expect(wrapper1.find('.dm-progress__bar').attributes('style')).toContain('width: 0%')
38
+
39
+ const wrapper2 = mount(DatametriaProgress, { props: { value: 150 } })
40
+ expect(wrapper2.find('.dm-progress__bar').attributes('style')).toContain('width: 100%')
41
+ })
42
+
43
+ it('renders primary variant', () => {
44
+ const wrapper = mount(DatametriaProgress, {
45
+ props: { value: 50, variant: 'primary' }
46
+ })
47
+ expect(wrapper.find('.dm-progress__bar--primary').exists()).toBe(true)
48
+ })
49
+
50
+ it('renders success variant', () => {
51
+ const wrapper = mount(DatametriaProgress, {
52
+ props: { value: 50, variant: 'success' }
53
+ })
54
+ expect(wrapper.find('.dm-progress__bar--success').exists()).toBe(true)
55
+ })
56
+
57
+ it('renders warning variant', () => {
58
+ const wrapper = mount(DatametriaProgress, {
59
+ props: { value: 50, variant: 'warning' }
60
+ })
61
+ expect(wrapper.find('.dm-progress__bar--warning').exists()).toBe(true)
62
+ })
63
+
64
+ it('renders error variant', () => {
65
+ const wrapper = mount(DatametriaProgress, {
66
+ props: { value: 50, variant: 'error' }
67
+ })
68
+ expect(wrapper.find('.dm-progress__bar--error').exists()).toBe(true)
69
+ })
70
+
71
+ it('has correct ARIA attributes', () => {
72
+ const wrapper = mount(DatametriaProgress, {
73
+ props: { value: 75, label: 'Progress' }
74
+ })
75
+ const track = wrapper.find('.dm-progress__track')
76
+ expect(track.attributes('role')).toBe('progressbar')
77
+ expect(track.attributes('aria-valuenow')).toBe('75')
78
+ expect(track.attributes('aria-valuemin')).toBe('0')
79
+ expect(track.attributes('aria-valuemax')).toBe('100')
80
+ expect(track.attributes('aria-label')).toBe('Progress')
81
+ })
82
+
83
+ it('works without ThemeProvider', () => {
84
+ const wrapper = mount(DatametriaProgress, {
85
+ props: { value: 80, label: 'Test', variant: 'success' }
86
+ })
87
+ expect(wrapper.find('.dm-progress__bar--success').exists()).toBe(true)
88
+ expect(wrapper.find('.dm-progress__bar').attributes('style')).toContain('width: 80%')
89
+ })
90
+ })
@@ -0,0 +1,77 @@
1
+ import { describe, it, expect } from 'vitest'
2
+ import { mount } from '@vue/test-utils'
3
+ import DatametriaRadio from '../DatametriaRadio.vue'
4
+
5
+ describe('DatametriaRadio', () => {
6
+ it('renders correctly', () => {
7
+ const wrapper = mount(DatametriaRadio, {
8
+ props: { value: 'option1' }
9
+ })
10
+ expect(wrapper.find('.dm-radio').exists()).toBe(true)
11
+ })
12
+
13
+ it('renders label when provided', () => {
14
+ const wrapper = mount(DatametriaRadio, {
15
+ props: { value: 'option1', label: 'Option 1' }
16
+ })
17
+ expect(wrapper.find('.dm-radio__text').text()).toBe('Option 1')
18
+ })
19
+
20
+ it('checks radio when modelValue matches value', () => {
21
+ const wrapper = mount(DatametriaRadio, {
22
+ props: { value: 'option1', modelValue: 'option1' }
23
+ })
24
+ expect((wrapper.find('input').element as HTMLInputElement).checked).toBe(true)
25
+ })
26
+
27
+ it('emits update:modelValue on change', async () => {
28
+ const wrapper = mount(DatametriaRadio, {
29
+ props: { value: 'option1' }
30
+ })
31
+ await wrapper.find('input').trigger('change')
32
+ expect(wrapper.emitted('update:modelValue')?.[0]).toEqual(['option1'])
33
+ })
34
+
35
+ it('applies disabled state', () => {
36
+ const wrapper = mount(DatametriaRadio, {
37
+ props: { value: 'option1', disabled: true }
38
+ })
39
+ expect(wrapper.find('input').attributes('disabled')).toBeDefined()
40
+ })
41
+
42
+ it('does not emit when disabled', async () => {
43
+ const wrapper = mount(DatametriaRadio, {
44
+ props: { value: 'option1', disabled: true }
45
+ })
46
+ await wrapper.find('input').trigger('change')
47
+ expect(wrapper.emitted('update:modelValue')).toBeUndefined()
48
+ })
49
+
50
+ it('shows error message when provided', () => {
51
+ const wrapper = mount(DatametriaRadio, {
52
+ props: { value: 'option1', name: 'test', error: 'Error text' }
53
+ })
54
+ expect(wrapper.find('.dm-radio__error').text()).toBe('Error text')
55
+ })
56
+
57
+ it('applies name attribute', () => {
58
+ const wrapper = mount(DatametriaRadio, {
59
+ props: { value: 'option1', name: 'radio-group' }
60
+ })
61
+ expect(wrapper.find('input').attributes('name')).toBe('radio-group')
62
+ })
63
+
64
+ it('applies aria-label', () => {
65
+ const wrapper = mount(DatametriaRadio, {
66
+ props: { value: 'option1', ariaLabel: 'Select option' }
67
+ })
68
+ expect(wrapper.find('input').attributes('aria-label')).toBe('Select option')
69
+ })
70
+
71
+ it('uses CSS variables with fallbacks', () => {
72
+ const wrapper = mount(DatametriaRadio, {
73
+ props: { value: 'option1' }
74
+ })
75
+ expect(wrapper.html()).toContain('dm-radio')
76
+ })
77
+ })
@@ -0,0 +1,77 @@
1
+ import { describe, it, expect } from 'vitest'
2
+ import { mount } from '@vue/test-utils'
3
+ import DatametriaSelect from '../DatametriaSelect.vue'
4
+
5
+ describe('DatametriaSelect', () => {
6
+ const options = [
7
+ { value: '1', label: 'Option 1' },
8
+ { value: '2', label: 'Option 2' },
9
+ { value: '3', label: 'Option 3' }
10
+ ]
11
+
12
+ it('renders correctly', () => {
13
+ const wrapper = mount(DatametriaSelect, {
14
+ props: { options }
15
+ })
16
+ expect(wrapper.find('.datametria-select').exists()).toBe(true)
17
+ })
18
+
19
+ it('renders label when provided', () => {
20
+ const wrapper = mount(DatametriaSelect, {
21
+ props: { options, label: 'Select Label' }
22
+ })
23
+ expect(wrapper.find('.datametria-select__label').text()).toContain('Select Label')
24
+ })
25
+
26
+ it('shows required indicator', () => {
27
+ const wrapper = mount(DatametriaSelect, {
28
+ props: { options, label: 'Test', required: true }
29
+ })
30
+ expect(wrapper.find('.datametria-select__required').exists()).toBe(true)
31
+ })
32
+
33
+ it('renders all options', () => {
34
+ const wrapper = mount(DatametriaSelect, {
35
+ props: { options }
36
+ })
37
+ const optionElements = wrapper.findAll('option')
38
+ expect(optionElements.length).toBeGreaterThanOrEqual(3)
39
+ })
40
+
41
+ it('renders placeholder option', () => {
42
+ const wrapper = mount(DatametriaSelect, {
43
+ props: { options, placeholder: 'Choose option' }
44
+ })
45
+ const firstOption = wrapper.find('option')
46
+ expect(firstOption.text()).toBe('Choose option')
47
+ })
48
+
49
+ it('binds modelValue correctly', () => {
50
+ const wrapper = mount(DatametriaSelect, {
51
+ props: { options, modelValue: '2' }
52
+ })
53
+ expect((wrapper.find('select').element as HTMLSelectElement).value).toBe('2')
54
+ })
55
+
56
+ it('emits update:modelValue on change', async () => {
57
+ const wrapper = mount(DatametriaSelect, {
58
+ props: { options }
59
+ })
60
+ await wrapper.find('select').setValue('2')
61
+ expect(wrapper.emitted('update:modelValue')?.[0]).toEqual(['2'])
62
+ })
63
+
64
+ it('shows error message', () => {
65
+ const wrapper = mount(DatametriaSelect, {
66
+ props: { options, errorMessage: 'Error text' }
67
+ })
68
+ expect(wrapper.find('.datametria-select__error').text()).toBe('Error text')
69
+ })
70
+
71
+ it('applies disabled state', () => {
72
+ const wrapper = mount(DatametriaSelect, {
73
+ props: { options, disabled: true }
74
+ })
75
+ expect(wrapper.find('select').attributes('disabled')).toBeDefined()
76
+ })
77
+ })