@datametria/vue-components 1.2.0 → 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.
- package/README.md +548 -657
- package/dist/index.es.js +2353 -1364
- package/dist/index.umd.js +10 -10
- package/dist/vue-components.css +1 -1
- package/package.json +102 -98
- package/src/components/DatametriaAlert.vue +137 -137
- package/src/components/DatametriaAutocomplete.vue +184 -138
- package/src/components/DatametriaAvatar.vue +177 -33
- package/src/components/DatametriaBadge.vue +98 -98
- package/src/components/DatametriaBreadcrumb.vue +21 -21
- package/src/components/DatametriaButton.vue +177 -165
- package/src/components/DatametriaCard.vue +12 -12
- package/src/components/DatametriaCheckbox.vue +8 -8
- package/src/components/DatametriaChip.vue +145 -149
- package/src/components/DatametriaContainer.vue +4 -4
- package/src/components/DatametriaDatePicker.vue +686 -68
- package/src/components/DatametriaDivider.vue +13 -13
- package/src/components/DatametriaFileUpload.vue +272 -140
- package/src/components/DatametriaGrid.vue +3 -3
- package/src/components/DatametriaInput.vue +15 -15
- package/src/components/DatametriaMenu.vue +604 -619
- package/src/components/DatametriaModal.vue +16 -16
- package/src/components/DatametriaNavbar.vue +230 -252
- package/src/components/DatametriaPasswordInput.vue +430 -0
- package/src/components/DatametriaProgress.vue +18 -18
- package/src/components/DatametriaRadio.vue +20 -20
- package/src/components/DatametriaSelect.vue +15 -15
- package/src/components/DatametriaSkeleton.vue +243 -239
- package/src/components/DatametriaSlider.vue +395 -407
- package/src/components/DatametriaSortableTable.vue +585 -0
- package/src/components/DatametriaSpinner.vue +7 -7
- package/src/components/DatametriaSwitch.vue +16 -16
- package/src/components/DatametriaTable.vue +14 -14
- package/src/components/DatametriaTextarea.vue +28 -28
- package/src/components/DatametriaTimePicker.vue +285 -285
- package/src/components/DatametriaToast.vue +176 -176
- package/src/components/DatametriaTooltip.vue +408 -408
- package/src/components/__tests__/DatametriaAlert.test.js +35 -35
- package/src/components/__tests__/DatametriaAlert.test.ts +190 -0
- package/src/components/__tests__/DatametriaAutocomplete.test.ts +180 -0
- package/src/components/__tests__/DatametriaAvatar.test.ts +152 -0
- package/src/components/__tests__/DatametriaBadge.test.js +29 -29
- package/src/components/__tests__/DatametriaBadge.test.ts +167 -0
- package/src/components/__tests__/DatametriaBreadcrumb.test.ts +75 -0
- package/src/components/__tests__/DatametriaButton.test.js +30 -30
- package/src/components/__tests__/DatametriaButton.test.ts +283 -0
- package/src/components/__tests__/DatametriaCard.test.ts +201 -0
- package/src/components/__tests__/DatametriaCheckbox.test.ts +47 -0
- package/src/components/__tests__/DatametriaChip.test.js +38 -38
- package/src/components/__tests__/DatametriaContainer.test.ts +52 -0
- package/src/components/__tests__/DatametriaDatePicker.test.ts +234 -0
- package/src/components/__tests__/DatametriaDivider.test.ts +54 -0
- package/src/components/__tests__/DatametriaFileUpload.test.ts +291 -0
- package/src/components/__tests__/DatametriaGrid.test.ts +31 -0
- package/src/components/__tests__/DatametriaInput.test.ts +72 -0
- package/src/components/__tests__/DatametriaMenu.test.ts +366 -0
- package/src/components/__tests__/DatametriaModal.test.ts +86 -0
- package/src/components/__tests__/DatametriaNavbar.test.js +48 -48
- package/src/components/__tests__/DatametriaNavbar.test.ts +203 -0
- package/src/components/__tests__/DatametriaPasswordInput.test.js +305 -0
- package/src/components/__tests__/DatametriaProgress.test.ts +90 -0
- package/src/components/__tests__/DatametriaRadio.test.ts +77 -0
- package/src/components/__tests__/DatametriaSelect.test.ts +77 -0
- package/src/components/__tests__/DatametriaSlider.test.ts +261 -0
- package/src/components/__tests__/DatametriaSortableTable.test.js +168 -0
- package/src/components/__tests__/DatametriaSpinner.test.ts +156 -0
- package/src/components/__tests__/DatametriaSwitch.test.ts +64 -0
- package/src/components/__tests__/DatametriaTable.test.ts +97 -0
- package/src/components/__tests__/DatametriaTextarea.test.ts +66 -0
- package/src/components/__tests__/DatametriaToast.test.js +48 -48
- package/src/components/__tests__/DatametriaToast.test.ts +99 -0
- package/src/composables/useAccessibilityScale.ts +94 -94
- package/src/composables/useBreakpoints.ts +82 -82
- package/src/composables/useHapticFeedback.ts +439 -439
- package/src/composables/useRipple.ts +218 -218
- package/src/index.ts +68 -61
- package/src/stories/Variants.stories.js +95 -95
- package/src/styles/design-tokens.css +623 -623
- package/src/theme/ThemeProvider.vue +96 -0
- package/src/theme/__tests__/ThemeProvider.test.ts +208 -0
- package/src/theme/__tests__/constants.test.ts +31 -0
- package/src/theme/__tests__/presets.test.ts +166 -0
- package/src/theme/__tests__/tokens.test.ts +155 -0
- package/src/theme/__tests__/types.test.ts +153 -0
- package/src/theme/__tests__/useTheme.test.ts +146 -0
- package/src/theme/constants.ts +14 -0
- package/src/theme/index.ts +12 -0
- package/src/theme/presets/datametria.ts +94 -0
- package/src/theme/presets/default.ts +94 -0
- package/src/theme/presets/index.ts +8 -0
- package/src/theme/tokens/colors.ts +28 -0
- package/src/theme/tokens/index.ts +47 -0
- package/src/theme/tokens/spacing.ts +21 -0
- package/src/theme/tokens/typography.ts +35 -0
- package/src/theme/types.ts +111 -0
- package/src/theme/useTheme.ts +28 -0
- package/src/types/index.ts +19 -0
|
@@ -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
|
+
})
|
|
@@ -0,0 +1,261 @@
|
|
|
1
|
+
import { describe, it, expect } from 'vitest'
|
|
2
|
+
import { mount } from '@vue/test-utils'
|
|
3
|
+
import DatametriaSlider from '../DatametriaSlider.vue'
|
|
4
|
+
|
|
5
|
+
describe('DatametriaSlider', () => {
|
|
6
|
+
describe('Renderização', () => {
|
|
7
|
+
it('renderiza corretamente', () => {
|
|
8
|
+
const wrapper = mount(DatametriaSlider, {
|
|
9
|
+
props: { modelValue: 50 }
|
|
10
|
+
})
|
|
11
|
+
expect(wrapper.find('.dm-slider').exists()).toBe(true)
|
|
12
|
+
})
|
|
13
|
+
|
|
14
|
+
it('renderiza label', () => {
|
|
15
|
+
const wrapper = mount(DatametriaSlider, {
|
|
16
|
+
props: { modelValue: 50, label: 'Volume' }
|
|
17
|
+
})
|
|
18
|
+
expect(wrapper.text()).toContain('Volume')
|
|
19
|
+
})
|
|
20
|
+
|
|
21
|
+
it('renderiza valor quando showValue=true', () => {
|
|
22
|
+
const wrapper = mount(DatametriaSlider, {
|
|
23
|
+
props: { modelValue: 75, showValue: true }
|
|
24
|
+
})
|
|
25
|
+
expect(wrapper.find('.dm-slider__value').text()).toBe('75')
|
|
26
|
+
})
|
|
27
|
+
|
|
28
|
+
it('não renderiza valor quando showValue=false', () => {
|
|
29
|
+
const wrapper = mount(DatametriaSlider, {
|
|
30
|
+
props: { modelValue: 75, showValue: false }
|
|
31
|
+
})
|
|
32
|
+
expect(wrapper.find('.dm-slider__value').exists()).toBe(false)
|
|
33
|
+
})
|
|
34
|
+
|
|
35
|
+
it('renderiza min/max quando showMinMax=true', () => {
|
|
36
|
+
const wrapper = mount(DatametriaSlider, {
|
|
37
|
+
props: { modelValue: 50, min: 0, max: 100, showMinMax: true }
|
|
38
|
+
})
|
|
39
|
+
expect(wrapper.find('.dm-slider__min').text()).toBe('0')
|
|
40
|
+
expect(wrapper.find('.dm-slider__max').text()).toBe('100')
|
|
41
|
+
})
|
|
42
|
+
|
|
43
|
+
it('renderiza required indicator', () => {
|
|
44
|
+
const wrapper = mount(DatametriaSlider, {
|
|
45
|
+
props: { modelValue: 50, label: 'Volume', required: true }
|
|
46
|
+
})
|
|
47
|
+
expect(wrapper.find('.dm-slider__required').exists()).toBe(true)
|
|
48
|
+
})
|
|
49
|
+
})
|
|
50
|
+
|
|
51
|
+
describe('Props', () => {
|
|
52
|
+
it('aceita modelValue', () => {
|
|
53
|
+
const wrapper = mount(DatametriaSlider, {
|
|
54
|
+
props: { modelValue: 60 }
|
|
55
|
+
})
|
|
56
|
+
expect(wrapper.props('modelValue')).toBe(60)
|
|
57
|
+
})
|
|
58
|
+
|
|
59
|
+
it('aceita min e max', () => {
|
|
60
|
+
const wrapper = mount(DatametriaSlider, {
|
|
61
|
+
props: { modelValue: 50, min: 10, max: 90 }
|
|
62
|
+
})
|
|
63
|
+
const input = wrapper.find('input')
|
|
64
|
+
expect(input.attributes('min')).toBe('10')
|
|
65
|
+
expect(input.attributes('max')).toBe('90')
|
|
66
|
+
})
|
|
67
|
+
|
|
68
|
+
it('aceita step', () => {
|
|
69
|
+
const wrapper = mount(DatametriaSlider, {
|
|
70
|
+
props: { modelValue: 50, step: 5 }
|
|
71
|
+
})
|
|
72
|
+
expect(wrapper.find('input').attributes('step')).toBe('5')
|
|
73
|
+
})
|
|
74
|
+
|
|
75
|
+
it('aceita disabled', () => {
|
|
76
|
+
const wrapper = mount(DatametriaSlider, {
|
|
77
|
+
props: { modelValue: 50, disabled: true }
|
|
78
|
+
})
|
|
79
|
+
expect(wrapper.find('.dm-slider').classes()).toContain('dm-slider--disabled')
|
|
80
|
+
expect(wrapper.find('input').element.disabled).toBe(true)
|
|
81
|
+
})
|
|
82
|
+
|
|
83
|
+
it('aceita errorMessage', () => {
|
|
84
|
+
const wrapper = mount(DatametriaSlider, {
|
|
85
|
+
props: { modelValue: 50, errorMessage: 'Valor inválido' }
|
|
86
|
+
})
|
|
87
|
+
expect(wrapper.find('.dm-slider__error').text()).toBe('Valor inválido')
|
|
88
|
+
})
|
|
89
|
+
|
|
90
|
+
it('aceita helperText', () => {
|
|
91
|
+
const wrapper = mount(DatametriaSlider, {
|
|
92
|
+
props: { modelValue: 50, helperText: 'Ajuste o volume' }
|
|
93
|
+
})
|
|
94
|
+
expect(wrapper.find('.dm-slider__helper').text()).toBe('Ajuste o volume')
|
|
95
|
+
})
|
|
96
|
+
|
|
97
|
+
it('aceita formatter customizado', () => {
|
|
98
|
+
const wrapper = mount(DatametriaSlider, {
|
|
99
|
+
props: {
|
|
100
|
+
modelValue: 50,
|
|
101
|
+
showValue: true,
|
|
102
|
+
formatter: (v: number) => `${v}%`
|
|
103
|
+
}
|
|
104
|
+
})
|
|
105
|
+
expect(wrapper.find('.dm-slider__value').text()).toBe('50%')
|
|
106
|
+
})
|
|
107
|
+
})
|
|
108
|
+
|
|
109
|
+
describe('Interação', () => {
|
|
110
|
+
it('emite update:modelValue ao mudar input', async () => {
|
|
111
|
+
const wrapper = mount(DatametriaSlider, {
|
|
112
|
+
props: { modelValue: 50 }
|
|
113
|
+
})
|
|
114
|
+
|
|
115
|
+
const input = wrapper.find('input')
|
|
116
|
+
await input.setValue(75)
|
|
117
|
+
|
|
118
|
+
expect(wrapper.emitted('update:modelValue')).toBeTruthy()
|
|
119
|
+
expect(wrapper.emitted('update:modelValue')?.[0]).toEqual([75])
|
|
120
|
+
})
|
|
121
|
+
|
|
122
|
+
it('emite change ao mudar valor', async () => {
|
|
123
|
+
const wrapper = mount(DatametriaSlider, {
|
|
124
|
+
props: { modelValue: 50 }
|
|
125
|
+
})
|
|
126
|
+
|
|
127
|
+
const input = wrapper.find('input')
|
|
128
|
+
await input.trigger('change')
|
|
129
|
+
|
|
130
|
+
expect(wrapper.emitted('change')).toBeTruthy()
|
|
131
|
+
})
|
|
132
|
+
|
|
133
|
+
it('emite focus ao focar', async () => {
|
|
134
|
+
const wrapper = mount(DatametriaSlider, {
|
|
135
|
+
props: { modelValue: 50 }
|
|
136
|
+
})
|
|
137
|
+
|
|
138
|
+
await wrapper.find('input').trigger('focus')
|
|
139
|
+
expect(wrapper.emitted('focus')).toBeTruthy()
|
|
140
|
+
})
|
|
141
|
+
|
|
142
|
+
it('emite blur ao desfocar', async () => {
|
|
143
|
+
const wrapper = mount(DatametriaSlider, {
|
|
144
|
+
props: { modelValue: 50 }
|
|
145
|
+
})
|
|
146
|
+
|
|
147
|
+
await wrapper.find('input').trigger('blur')
|
|
148
|
+
expect(wrapper.emitted('blur')).toBeTruthy()
|
|
149
|
+
})
|
|
150
|
+
})
|
|
151
|
+
|
|
152
|
+
describe('Progress', () => {
|
|
153
|
+
it('calcula progressPercentage corretamente', () => {
|
|
154
|
+
const wrapper = mount(DatametriaSlider, {
|
|
155
|
+
props: { modelValue: 50, min: 0, max: 100 }
|
|
156
|
+
})
|
|
157
|
+
|
|
158
|
+
const progress = wrapper.find('.dm-slider__progress')
|
|
159
|
+
expect(progress.attributes('style')).toContain('width: 50%')
|
|
160
|
+
})
|
|
161
|
+
|
|
162
|
+
it('calcula progressPercentage com min diferente de 0', () => {
|
|
163
|
+
const wrapper = mount(DatametriaSlider, {
|
|
164
|
+
props: { modelValue: 50, min: 25, max: 75 }
|
|
165
|
+
})
|
|
166
|
+
|
|
167
|
+
const progress = wrapper.find('.dm-slider__progress')
|
|
168
|
+
expect(progress.attributes('style')).toContain('width: 50%')
|
|
169
|
+
})
|
|
170
|
+
})
|
|
171
|
+
|
|
172
|
+
describe('Acessibilidade', () => {
|
|
173
|
+
it('input tem type range', () => {
|
|
174
|
+
const wrapper = mount(DatametriaSlider, {
|
|
175
|
+
props: { modelValue: 50 }
|
|
176
|
+
})
|
|
177
|
+
expect(wrapper.find('input').attributes('type')).toBe('range')
|
|
178
|
+
})
|
|
179
|
+
|
|
180
|
+
it('input tem aria-valuemin', () => {
|
|
181
|
+
const wrapper = mount(DatametriaSlider, {
|
|
182
|
+
props: { modelValue: 50, min: 10 }
|
|
183
|
+
})
|
|
184
|
+
expect(wrapper.find('input').attributes('aria-valuemin')).toBe('10')
|
|
185
|
+
})
|
|
186
|
+
|
|
187
|
+
it('input tem aria-valuemax', () => {
|
|
188
|
+
const wrapper = mount(DatametriaSlider, {
|
|
189
|
+
props: { modelValue: 50, max: 90 }
|
|
190
|
+
})
|
|
191
|
+
expect(wrapper.find('input').attributes('aria-valuemax')).toBe('90')
|
|
192
|
+
})
|
|
193
|
+
|
|
194
|
+
it('input tem aria-valuenow', () => {
|
|
195
|
+
const wrapper = mount(DatametriaSlider, {
|
|
196
|
+
props: { modelValue: 60 }
|
|
197
|
+
})
|
|
198
|
+
expect(wrapper.find('input').attributes('aria-valuenow')).toBe('60')
|
|
199
|
+
})
|
|
200
|
+
|
|
201
|
+
it('input tem aria-valuetext com formatter', () => {
|
|
202
|
+
const wrapper = mount(DatametriaSlider, {
|
|
203
|
+
props: {
|
|
204
|
+
modelValue: 50,
|
|
205
|
+
formatter: (v: number) => `${v}%`
|
|
206
|
+
}
|
|
207
|
+
})
|
|
208
|
+
expect(wrapper.find('input').attributes('aria-valuetext')).toBe('50%')
|
|
209
|
+
})
|
|
210
|
+
|
|
211
|
+
it('error tem role alert', () => {
|
|
212
|
+
const wrapper = mount(DatametriaSlider, {
|
|
213
|
+
props: { modelValue: 50, errorMessage: 'Erro' }
|
|
214
|
+
})
|
|
215
|
+
expect(wrapper.find('.dm-slider__error').attributes('role')).toBe('alert')
|
|
216
|
+
})
|
|
217
|
+
|
|
218
|
+
it('input tem aria-describedby quando há error', () => {
|
|
219
|
+
const wrapper = mount(DatametriaSlider, {
|
|
220
|
+
props: { modelValue: 50, errorMessage: 'Erro' }
|
|
221
|
+
})
|
|
222
|
+
const input = wrapper.find('input')
|
|
223
|
+
expect(input.attributes('aria-describedby')).toBeDefined()
|
|
224
|
+
})
|
|
225
|
+
})
|
|
226
|
+
|
|
227
|
+
describe('Métodos Expostos', () => {
|
|
228
|
+
it('expõe método focus', () => {
|
|
229
|
+
const wrapper = mount(DatametriaSlider, {
|
|
230
|
+
props: { modelValue: 50 }
|
|
231
|
+
})
|
|
232
|
+
const exposed = wrapper.vm as any
|
|
233
|
+
expect(typeof exposed.focus).toBe('function')
|
|
234
|
+
})
|
|
235
|
+
|
|
236
|
+
it('expõe método blur', () => {
|
|
237
|
+
const wrapper = mount(DatametriaSlider, {
|
|
238
|
+
props: { modelValue: 50 }
|
|
239
|
+
})
|
|
240
|
+
const exposed = wrapper.vm as any
|
|
241
|
+
expect(typeof exposed.blur).toBe('function')
|
|
242
|
+
})
|
|
243
|
+
|
|
244
|
+
it('expõe inputRef', () => {
|
|
245
|
+
const wrapper = mount(DatametriaSlider, {
|
|
246
|
+
props: { modelValue: 50 }
|
|
247
|
+
})
|
|
248
|
+
const exposed = wrapper.vm as any
|
|
249
|
+
expect(exposed.inputRef).toBeDefined()
|
|
250
|
+
})
|
|
251
|
+
})
|
|
252
|
+
|
|
253
|
+
describe('CSS Variables', () => {
|
|
254
|
+
it('usa CSS Variables padronizadas', () => {
|
|
255
|
+
const wrapper = mount(DatametriaSlider, {
|
|
256
|
+
props: { modelValue: 50 }
|
|
257
|
+
})
|
|
258
|
+
expect(wrapper.find('.dm-slider').exists()).toBe(true)
|
|
259
|
+
})
|
|
260
|
+
})
|
|
261
|
+
})
|
|
@@ -0,0 +1,168 @@
|
|
|
1
|
+
import { describe, it, expect } from 'vitest'
|
|
2
|
+
import { mount } from '@vue/test-utils'
|
|
3
|
+
import DatametriaSortableTable from '../DatametriaSortableTable.vue'
|
|
4
|
+
|
|
5
|
+
describe('DatametriaSortableTable', () => {
|
|
6
|
+
const columns = [
|
|
7
|
+
{ key: 'id', label: 'ID', width: '80px' },
|
|
8
|
+
{ key: 'name', label: 'Nome' },
|
|
9
|
+
{ key: 'email', label: 'Email' },
|
|
10
|
+
{ key: 'status', label: 'Status' }
|
|
11
|
+
]
|
|
12
|
+
|
|
13
|
+
const data = [
|
|
14
|
+
{ id: 1, name: 'João Silva', email: 'joao@example.com', status: 'Ativo' },
|
|
15
|
+
{ id: 2, name: 'Maria Santos', email: 'maria@example.com', status: 'Inativo' },
|
|
16
|
+
{ id: 3, name: 'Pedro Costa', email: 'pedro@example.com', status: 'Ativo' },
|
|
17
|
+
{ id: 4, name: 'Ana Oliveira', email: 'ana@example.com', status: 'Ativo' }
|
|
18
|
+
]
|
|
19
|
+
|
|
20
|
+
it('renders table with data', () => {
|
|
21
|
+
const wrapper = mount(DatametriaSortableTable, {
|
|
22
|
+
props: { columns, data }
|
|
23
|
+
})
|
|
24
|
+
|
|
25
|
+
expect(wrapper.find('.datametria-sortable-table').exists()).toBe(true)
|
|
26
|
+
expect(wrapper.findAll('.datametria-sortable-table__tr')).toHaveLength(4)
|
|
27
|
+
})
|
|
28
|
+
|
|
29
|
+
it('renders search input when searchable', () => {
|
|
30
|
+
const wrapper = mount(DatametriaSortableTable, {
|
|
31
|
+
props: { columns, data, searchable: true }
|
|
32
|
+
})
|
|
33
|
+
|
|
34
|
+
expect(wrapper.find('.datametria-sortable-table__search-input').exists()).toBe(true)
|
|
35
|
+
})
|
|
36
|
+
|
|
37
|
+
it('filters data by search query', async () => {
|
|
38
|
+
const wrapper = mount(DatametriaSortableTable, {
|
|
39
|
+
props: { columns, data, searchable: true }
|
|
40
|
+
})
|
|
41
|
+
|
|
42
|
+
const searchInput = wrapper.find('.datametria-sortable-table__search-input')
|
|
43
|
+
await searchInput.setValue('João')
|
|
44
|
+
|
|
45
|
+
expect(wrapper.findAll('.datametria-sortable-table__tr')).toHaveLength(1)
|
|
46
|
+
expect(wrapper.text()).toContain('João Silva')
|
|
47
|
+
})
|
|
48
|
+
|
|
49
|
+
it('sorts data when clicking column header', async () => {
|
|
50
|
+
const wrapper = mount(DatametriaSortableTable, {
|
|
51
|
+
props: { columns, data, paginated: false }
|
|
52
|
+
})
|
|
53
|
+
|
|
54
|
+
const nameHeader = wrapper.findAll('.datametria-sortable-table__th')[1]
|
|
55
|
+
await nameHeader.trigger('click')
|
|
56
|
+
|
|
57
|
+
const rows = wrapper.findAll('.datametria-sortable-table__tr')
|
|
58
|
+
expect(rows[0].text()).toContain('Ana Oliveira')
|
|
59
|
+
expect(rows[3].text()).toContain('Pedro Costa')
|
|
60
|
+
})
|
|
61
|
+
|
|
62
|
+
it('toggles sort order on second click', async () => {
|
|
63
|
+
const wrapper = mount(DatametriaSortableTable, {
|
|
64
|
+
props: { columns, data, paginated: false }
|
|
65
|
+
})
|
|
66
|
+
|
|
67
|
+
const nameHeader = wrapper.findAll('.datametria-sortable-table__th')[1]
|
|
68
|
+
await nameHeader.trigger('click') // Ascending
|
|
69
|
+
await nameHeader.trigger('click') // Descending
|
|
70
|
+
|
|
71
|
+
const rows = wrapper.findAll('.datametria-sortable-table__tr')
|
|
72
|
+
expect(rows[0].text()).toContain('Pedro Costa')
|
|
73
|
+
expect(rows[3].text()).toContain('Ana Oliveira')
|
|
74
|
+
})
|
|
75
|
+
|
|
76
|
+
it('renders checkboxes when selectable', () => {
|
|
77
|
+
const wrapper = mount(DatametriaSortableTable, {
|
|
78
|
+
props: { columns, data, selectable: true }
|
|
79
|
+
})
|
|
80
|
+
|
|
81
|
+
expect(wrapper.findAll('input[type="checkbox"]').length).toBeGreaterThan(0)
|
|
82
|
+
})
|
|
83
|
+
|
|
84
|
+
it('selects row when checkbox clicked', async () => {
|
|
85
|
+
const wrapper = mount(DatametriaSortableTable, {
|
|
86
|
+
props: { columns, data, selectable: true }
|
|
87
|
+
})
|
|
88
|
+
|
|
89
|
+
const firstCheckbox = wrapper.findAll('input[type="checkbox"]')[1]
|
|
90
|
+
await firstCheckbox.setValue(true)
|
|
91
|
+
|
|
92
|
+
expect(wrapper.emitted('selection-change')).toBeTruthy()
|
|
93
|
+
})
|
|
94
|
+
|
|
95
|
+
it('renders pagination controls when paginated', () => {
|
|
96
|
+
const wrapper = mount(DatametriaSortableTable, {
|
|
97
|
+
props: { columns, data, paginated: true, pageSize: 2 }
|
|
98
|
+
})
|
|
99
|
+
|
|
100
|
+
expect(wrapper.find('.datametria-sortable-table__pagination').exists()).toBe(true)
|
|
101
|
+
expect(wrapper.text()).toContain('Página 1 de 2')
|
|
102
|
+
})
|
|
103
|
+
|
|
104
|
+
it('changes page when pagination button clicked', async () => {
|
|
105
|
+
const wrapper = mount(DatametriaSortableTable, {
|
|
106
|
+
props: { columns, data, paginated: true, pageSize: 2 }
|
|
107
|
+
})
|
|
108
|
+
|
|
109
|
+
const nextButton = wrapper.findAll('.datametria-sortable-table__pagination-btn')[2]
|
|
110
|
+
await nextButton.trigger('click')
|
|
111
|
+
|
|
112
|
+
expect(wrapper.text()).toContain('Página 2 de 2')
|
|
113
|
+
})
|
|
114
|
+
|
|
115
|
+
it('renders column filters when filterable', () => {
|
|
116
|
+
const wrapper = mount(DatametriaSortableTable, {
|
|
117
|
+
props: { columns, data, filterable: true }
|
|
118
|
+
})
|
|
119
|
+
|
|
120
|
+
expect(wrapper.findAll('.datametria-sortable-table__filter-input').length).toBeGreaterThan(0)
|
|
121
|
+
})
|
|
122
|
+
|
|
123
|
+
it('filters by column filter', async () => {
|
|
124
|
+
const wrapper = mount(DatametriaSortableTable, {
|
|
125
|
+
props: { columns, data, filterable: true }
|
|
126
|
+
})
|
|
127
|
+
|
|
128
|
+
const statusFilter = wrapper.findAll('.datametria-sortable-table__filter-input')[3]
|
|
129
|
+
await statusFilter.setValue('Inativo')
|
|
130
|
+
|
|
131
|
+
expect(wrapper.findAll('.datametria-sortable-table__tr')).toHaveLength(1)
|
|
132
|
+
expect(wrapper.text()).toContain('Maria Santos')
|
|
133
|
+
})
|
|
134
|
+
|
|
135
|
+
it('shows empty state when no data', () => {
|
|
136
|
+
const wrapper = mount(DatametriaSortableTable, {
|
|
137
|
+
props: { columns, data: [] }
|
|
138
|
+
})
|
|
139
|
+
|
|
140
|
+
expect(wrapper.find('.datametria-sortable-table__empty').exists()).toBe(true)
|
|
141
|
+
expect(wrapper.text()).toContain('Nenhum dado encontrado')
|
|
142
|
+
})
|
|
143
|
+
|
|
144
|
+
it('disables sorting for specific columns', async () => {
|
|
145
|
+
const columnsWithNonSortable = [
|
|
146
|
+
{ key: 'id', label: 'ID', sortable: false },
|
|
147
|
+
{ key: 'name', label: 'Nome' }
|
|
148
|
+
]
|
|
149
|
+
|
|
150
|
+
const wrapper = mount(DatametriaSortableTable, {
|
|
151
|
+
props: { columns: columnsWithNonSortable, data }
|
|
152
|
+
})
|
|
153
|
+
|
|
154
|
+
const idHeader = wrapper.findAll('.datametria-sortable-table__th')[0]
|
|
155
|
+
expect(idHeader.classes()).not.toContain('datametria-sortable-table__th--sortable')
|
|
156
|
+
})
|
|
157
|
+
|
|
158
|
+
it('changes page size', async () => {
|
|
159
|
+
const wrapper = mount(DatametriaSortableTable, {
|
|
160
|
+
props: { columns, data, paginated: true, pageSize: 2, pageSizeOptions: [2, 5, 10] }
|
|
161
|
+
})
|
|
162
|
+
|
|
163
|
+
const pageSizeSelect = wrapper.find('.datametria-sortable-table__page-size')
|
|
164
|
+
await pageSizeSelect.setValue(5)
|
|
165
|
+
|
|
166
|
+
expect(wrapper.findAll('.datametria-sortable-table__tr')).toHaveLength(4)
|
|
167
|
+
})
|
|
168
|
+
})
|
|
@@ -0,0 +1,156 @@
|
|
|
1
|
+
import { describe, it, expect } from 'vitest'
|
|
2
|
+
import { mount } from '@vue/test-utils'
|
|
3
|
+
import DatametriaSpinner from '../DatametriaSpinner.vue'
|
|
4
|
+
|
|
5
|
+
describe('DatametriaSpinner', () => {
|
|
6
|
+
// Rendering tests
|
|
7
|
+
it('renders with default props', () => {
|
|
8
|
+
const wrapper = mount(DatametriaSpinner)
|
|
9
|
+
expect(wrapper.find('.dm-spinner').exists()).toBe(true)
|
|
10
|
+
expect(wrapper.find('.dm-spinner--md').exists()).toBe(true)
|
|
11
|
+
expect(wrapper.find('.dm-spinner--primary').exists()).toBe(true)
|
|
12
|
+
})
|
|
13
|
+
|
|
14
|
+
it('renders with label', () => {
|
|
15
|
+
const wrapper = mount(DatametriaSpinner, {
|
|
16
|
+
props: { label: 'Loading...' }
|
|
17
|
+
})
|
|
18
|
+
expect(wrapper.find('.dm-spinner__label').text()).toBe('Loading...')
|
|
19
|
+
})
|
|
20
|
+
|
|
21
|
+
it('renders without label', () => {
|
|
22
|
+
const wrapper = mount(DatametriaSpinner)
|
|
23
|
+
expect(wrapper.find('.dm-spinner__label').exists()).toBe(false)
|
|
24
|
+
})
|
|
25
|
+
|
|
26
|
+
it('renders with custom aria-label', () => {
|
|
27
|
+
const wrapper = mount(DatametriaSpinner, {
|
|
28
|
+
props: { ariaLabel: 'Custom loading' }
|
|
29
|
+
})
|
|
30
|
+
expect(wrapper.attributes('aria-label')).toBe('Custom loading')
|
|
31
|
+
})
|
|
32
|
+
|
|
33
|
+
it('has role status', () => {
|
|
34
|
+
const wrapper = mount(DatametriaSpinner)
|
|
35
|
+
expect(wrapper.attributes('role')).toBe('status')
|
|
36
|
+
})
|
|
37
|
+
|
|
38
|
+
// Size tests
|
|
39
|
+
it('renders small size', () => {
|
|
40
|
+
const wrapper = mount(DatametriaSpinner, {
|
|
41
|
+
props: { size: 'sm' }
|
|
42
|
+
})
|
|
43
|
+
expect(wrapper.find('.dm-spinner--sm').exists()).toBe(true)
|
|
44
|
+
})
|
|
45
|
+
|
|
46
|
+
it('renders medium size', () => {
|
|
47
|
+
const wrapper = mount(DatametriaSpinner, {
|
|
48
|
+
props: { size: 'md' }
|
|
49
|
+
})
|
|
50
|
+
expect(wrapper.find('.dm-spinner--md').exists()).toBe(true)
|
|
51
|
+
})
|
|
52
|
+
|
|
53
|
+
it('renders large size', () => {
|
|
54
|
+
const wrapper = mount(DatametriaSpinner, {
|
|
55
|
+
props: { size: 'lg' }
|
|
56
|
+
})
|
|
57
|
+
expect(wrapper.find('.dm-spinner--lg').exists()).toBe(true)
|
|
58
|
+
})
|
|
59
|
+
|
|
60
|
+
// Variant tests
|
|
61
|
+
it('renders primary variant', () => {
|
|
62
|
+
const wrapper = mount(DatametriaSpinner, {
|
|
63
|
+
props: { variant: 'primary' }
|
|
64
|
+
})
|
|
65
|
+
expect(wrapper.find('.dm-spinner--primary').exists()).toBe(true)
|
|
66
|
+
})
|
|
67
|
+
|
|
68
|
+
it('renders secondary variant', () => {
|
|
69
|
+
const wrapper = mount(DatametriaSpinner, {
|
|
70
|
+
props: { variant: 'secondary' }
|
|
71
|
+
})
|
|
72
|
+
expect(wrapper.find('.dm-spinner--secondary').exists()).toBe(true)
|
|
73
|
+
})
|
|
74
|
+
|
|
75
|
+
it('renders white variant', () => {
|
|
76
|
+
const wrapper = mount(DatametriaSpinner, {
|
|
77
|
+
props: { variant: 'white' }
|
|
78
|
+
})
|
|
79
|
+
expect(wrapper.find('.dm-spinner--white').exists()).toBe(true)
|
|
80
|
+
})
|
|
81
|
+
|
|
82
|
+
// SVG structure tests
|
|
83
|
+
it('renders SVG element', () => {
|
|
84
|
+
const wrapper = mount(DatametriaSpinner)
|
|
85
|
+
expect(wrapper.find('.dm-spinner__svg').exists()).toBe(true)
|
|
86
|
+
expect(wrapper.find('svg').exists()).toBe(true)
|
|
87
|
+
})
|
|
88
|
+
|
|
89
|
+
it('renders circle element', () => {
|
|
90
|
+
const wrapper = mount(DatametriaSpinner)
|
|
91
|
+
expect(wrapper.find('.dm-spinner__circle').exists()).toBe(true)
|
|
92
|
+
expect(wrapper.find('circle').exists()).toBe(true)
|
|
93
|
+
})
|
|
94
|
+
|
|
95
|
+
it('circle has correct attributes', () => {
|
|
96
|
+
const wrapper = mount(DatametriaSpinner)
|
|
97
|
+
const circle = wrapper.find('circle')
|
|
98
|
+
expect(circle.attributes('cx')).toBe('25')
|
|
99
|
+
expect(circle.attributes('cy')).toBe('25')
|
|
100
|
+
expect(circle.attributes('r')).toBe('20')
|
|
101
|
+
expect(circle.attributes('fill')).toBe('none')
|
|
102
|
+
expect(circle.attributes('stroke-width')).toBe('4')
|
|
103
|
+
})
|
|
104
|
+
|
|
105
|
+
// CSS Variables tests
|
|
106
|
+
it('uses CSS variables for colors', () => {
|
|
107
|
+
const variants: Array<'primary' | 'secondary' | 'white'> = ['primary', 'secondary', 'white']
|
|
108
|
+
|
|
109
|
+
variants.forEach(variant => {
|
|
110
|
+
const wrapper = mount(DatametriaSpinner, {
|
|
111
|
+
props: { variant }
|
|
112
|
+
})
|
|
113
|
+
expect(wrapper.find(`.dm-spinner--${variant}`).exists()).toBe(true)
|
|
114
|
+
})
|
|
115
|
+
})
|
|
116
|
+
|
|
117
|
+
it('uses CSS variables for spacing', () => {
|
|
118
|
+
const wrapper = mount(DatametriaSpinner, {
|
|
119
|
+
props: { label: 'Test' }
|
|
120
|
+
})
|
|
121
|
+
|
|
122
|
+
// Verifica estrutura
|
|
123
|
+
expect(wrapper.find('.dm-spinner').exists()).toBe(true)
|
|
124
|
+
expect(wrapper.find('.dm-spinner__label').exists()).toBe(true)
|
|
125
|
+
})
|
|
126
|
+
|
|
127
|
+
// Backward compatibility tests
|
|
128
|
+
it('works without ThemeProvider (fallback values)', () => {
|
|
129
|
+
const wrapper = mount(DatametriaSpinner, {
|
|
130
|
+
props: {
|
|
131
|
+
variant: 'primary',
|
|
132
|
+
size: 'md',
|
|
133
|
+
label: 'Loading'
|
|
134
|
+
}
|
|
135
|
+
})
|
|
136
|
+
|
|
137
|
+
expect(wrapper.find('.dm-spinner--primary').exists()).toBe(true)
|
|
138
|
+
expect(wrapper.find('.dm-spinner--md').exists()).toBe(true)
|
|
139
|
+
expect(wrapper.text()).toContain('Loading')
|
|
140
|
+
})
|
|
141
|
+
|
|
142
|
+
it('maintains visual consistency across variants and sizes', () => {
|
|
143
|
+
const variants: Array<'primary' | 'secondary' | 'white'> = ['primary', 'secondary', 'white']
|
|
144
|
+
const sizes: Array<'sm' | 'md' | 'lg'> = ['sm', 'md', 'lg']
|
|
145
|
+
|
|
146
|
+
variants.forEach(variant => {
|
|
147
|
+
sizes.forEach(size => {
|
|
148
|
+
const wrapper = mount(DatametriaSpinner, {
|
|
149
|
+
props: { variant, size }
|
|
150
|
+
})
|
|
151
|
+
expect(wrapper.find(`.dm-spinner--${variant}`).exists()).toBe(true)
|
|
152
|
+
expect(wrapper.find(`.dm-spinner--${size}`).exists()).toBe(true)
|
|
153
|
+
})
|
|
154
|
+
})
|
|
155
|
+
})
|
|
156
|
+
})
|