@weni/unnnic-system 3.28.2-alpha.3 → 3.29.0
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/index.d.ts +12 -324
- package/dist/style.css +1 -1
- package/dist/unnnic.mjs +7345 -7474
- package/dist/unnnic.umd.js +29 -29
- package/package.json +2 -2
- package/src/components/Button/__tests__/Button.spec.js +81 -0
- package/src/components/Button/__tests__/ButtonIcon.spec.js +56 -0
- package/src/components/Carousel/TagCarousel.vue +8 -2
- package/src/components/Carousel/__tests__/TagCarousel.spec.js +258 -0
- package/src/components/ChartLine/__tests__/ChartLine.spec.js +170 -0
- package/src/components/Checkbox/__tests__/Checkbox.spec.js +14 -0
- package/src/components/CheckboxGroup/__tests__/CheckboxGroup.spec.js +62 -0
- package/src/components/DatePicker/DatePicker.vue +1 -0
- package/src/components/DatePicker/__tests__/DatePicker.spec.js +364 -0
- package/src/components/Disclaimer/__tests__/Disclaimer.spec.js +16 -0
- package/src/components/DropArea/DropArea.vue +1 -2
- package/src/components/DropArea/__tests__/DropArea.spec.js +333 -0
- package/src/components/MultiSelect/__tests__/MultiSelect.spec.js +109 -2
- package/src/components/MultiSelect/index.vue +1 -1
- package/src/components/PageHeader/__tests__/PageHeader.spec.js +97 -0
- package/src/components/Pagination/__tests__/Pagination.spec.js +99 -0
- package/src/components/Radio/__test__/Radio.spec.js +67 -0
- package/src/components/RadioGroup/__tests__/RadioGroup.spec.js +100 -0
- package/src/components/Slider/__tests__/Slider.spec.js +321 -0
- package/src/components/Table/Table.vue +2 -21
- package/src/components/Table/TableRow.vue +3 -28
- package/src/components/Tag/DefaultTag.vue +4 -3
- package/src/components/Tag/__tests__/DefaultTag.spec.js +51 -0
- package/src/components/Tag/__tests__/Tag.spec.js +82 -0
- package/src/components/TextArea/__test__/TextArea.spec.js +19 -0
- package/src/components/ToolTip/__tests__/ToolTip.spec.js +82 -1
- package/src/components/index.ts +0 -13
- package/src/stories/Table.stories.js +2 -75
- package/src/components/ui/table/Table.vue +0 -29
- package/src/components/ui/table/TableBody.vue +0 -14
- package/src/components/ui/table/TableCell.vue +0 -28
- package/src/components/ui/table/TableHead.vue +0 -27
- package/src/components/ui/table/TableHeader.vue +0 -24
- package/src/components/ui/table/TableRow.vue +0 -47
- package/src/components/ui/table/index.ts +0 -6
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
import { mount } from '@vue/test-utils';
|
|
2
2
|
import { describe, test, expect, beforeEach, afterEach } from 'vitest';
|
|
3
3
|
import Radio from '@/components/Radio/Radio.vue';
|
|
4
|
+
import RadioGroup from '@/components/RadioGroup/RadioGroup.vue';
|
|
4
5
|
import UnnnicIcon from '@/components/Icon.vue';
|
|
5
6
|
|
|
6
7
|
describe('Radio.vue', () => {
|
|
@@ -75,4 +76,70 @@ describe('Radio.vue', () => {
|
|
|
75
76
|
test('matches the snapshot', () => {
|
|
76
77
|
expect(wrapper.html()).toMatchSnapshot();
|
|
77
78
|
});
|
|
79
|
+
|
|
80
|
+
test('renders helper text when provided', async () => {
|
|
81
|
+
await wrapper.setProps({ helper: 'Radio helper' });
|
|
82
|
+
expect(wrapper.find('.unnnic-radio__helper').text()).toBe('Radio helper');
|
|
83
|
+
});
|
|
84
|
+
|
|
85
|
+
test('renders label prop', async () => {
|
|
86
|
+
await wrapper.setProps({ label: 'Radio label' });
|
|
87
|
+
expect(wrapper.find('.unnnic-radio__label').text()).toContain(
|
|
88
|
+
'Radio label',
|
|
89
|
+
);
|
|
90
|
+
});
|
|
91
|
+
|
|
92
|
+
test('uses name prop when radio is standalone', () => {
|
|
93
|
+
const standalone = mount(Radio, {
|
|
94
|
+
props: {
|
|
95
|
+
modelValue: '',
|
|
96
|
+
value: 'option1',
|
|
97
|
+
name: 'standalone-radio',
|
|
98
|
+
},
|
|
99
|
+
global: { components: { UnnnicIcon } },
|
|
100
|
+
});
|
|
101
|
+
|
|
102
|
+
expect(standalone.find('input').attributes('name')).toBe(
|
|
103
|
+
'standalone-radio',
|
|
104
|
+
);
|
|
105
|
+
standalone.unmount();
|
|
106
|
+
});
|
|
107
|
+
|
|
108
|
+
test('uses injected name when inside RadioGroup without name prop', () => {
|
|
109
|
+
const groupWrapper = mount(RadioGroup, {
|
|
110
|
+
props: { modelValue: '', name: 'group-name' },
|
|
111
|
+
slots: {
|
|
112
|
+
default: '<Radio value="option1" label="Option 1" />',
|
|
113
|
+
},
|
|
114
|
+
global: {
|
|
115
|
+
components: { Radio },
|
|
116
|
+
stubs: { UnnnicLabel: true },
|
|
117
|
+
},
|
|
118
|
+
});
|
|
119
|
+
|
|
120
|
+
expect(groupWrapper.find('input[type="radio"]').attributes('name')).toBe(
|
|
121
|
+
'group-name',
|
|
122
|
+
);
|
|
123
|
+
groupWrapper.unmount();
|
|
124
|
+
});
|
|
125
|
+
|
|
126
|
+
test('updates group context value when used inside RadioGroup', async () => {
|
|
127
|
+
const groupWrapper = mount(RadioGroup, {
|
|
128
|
+
props: { modelValue: '' },
|
|
129
|
+
slots: {
|
|
130
|
+
default: '<Radio value="option1" label="Option 1" />',
|
|
131
|
+
},
|
|
132
|
+
global: {
|
|
133
|
+
components: { Radio },
|
|
134
|
+
stubs: {
|
|
135
|
+
UnnnicLabel: true,
|
|
136
|
+
},
|
|
137
|
+
},
|
|
138
|
+
});
|
|
139
|
+
|
|
140
|
+
await groupWrapper.find('input[type="radio"]').trigger('change');
|
|
141
|
+
|
|
142
|
+
expect(groupWrapper.emitted('update:modelValue')[0]).toEqual(['option1']);
|
|
143
|
+
groupWrapper.unmount();
|
|
144
|
+
});
|
|
78
145
|
});
|
|
@@ -0,0 +1,100 @@
|
|
|
1
|
+
import { describe, expect, it } from 'vitest';
|
|
2
|
+
import { mount } from '@vue/test-utils';
|
|
3
|
+
|
|
4
|
+
import RadioGroup from '../RadioGroup.vue';
|
|
5
|
+
import Radio from '../../Radio/Radio.vue';
|
|
6
|
+
|
|
7
|
+
const mountRadioGroup = (props = {}, slots = {}) =>
|
|
8
|
+
mount(RadioGroup, {
|
|
9
|
+
props: {
|
|
10
|
+
modelValue: '',
|
|
11
|
+
...props,
|
|
12
|
+
},
|
|
13
|
+
slots: {
|
|
14
|
+
default: `
|
|
15
|
+
<Radio value="a" label="Option A" />
|
|
16
|
+
<Radio value="b" label="Option B" />
|
|
17
|
+
`,
|
|
18
|
+
...slots,
|
|
19
|
+
},
|
|
20
|
+
global: {
|
|
21
|
+
components: { Radio },
|
|
22
|
+
stubs: {
|
|
23
|
+
UnnnicLabel: {
|
|
24
|
+
props: ['label', 'tooltip'],
|
|
25
|
+
template:
|
|
26
|
+
'<label data-testid="radio-group-label">{{ label }}</label>',
|
|
27
|
+
},
|
|
28
|
+
},
|
|
29
|
+
},
|
|
30
|
+
});
|
|
31
|
+
|
|
32
|
+
describe('RadioGroup', () => {
|
|
33
|
+
it('renders radios in slot', () => {
|
|
34
|
+
const wrapper = mountRadioGroup();
|
|
35
|
+
|
|
36
|
+
expect(wrapper.findAllComponents(Radio).length).toBe(2);
|
|
37
|
+
});
|
|
38
|
+
|
|
39
|
+
it('renders label and helper when provided', () => {
|
|
40
|
+
const wrapper = mountRadioGroup({
|
|
41
|
+
label: 'Choose one',
|
|
42
|
+
helper: 'Select an option',
|
|
43
|
+
});
|
|
44
|
+
|
|
45
|
+
expect(wrapper.find('[data-testid="radio-group-label"]').text()).toBe(
|
|
46
|
+
'Choose one',
|
|
47
|
+
);
|
|
48
|
+
expect(wrapper.find('.unnnic-radio-group__helper').text()).toBe(
|
|
49
|
+
'Select an option',
|
|
50
|
+
);
|
|
51
|
+
});
|
|
52
|
+
|
|
53
|
+
it('emits update:modelValue when a radio is selected', async () => {
|
|
54
|
+
const wrapper = mountRadioGroup();
|
|
55
|
+
|
|
56
|
+
await wrapper.findAll('input[type="radio"]')[0].trigger('change');
|
|
57
|
+
|
|
58
|
+
expect(wrapper.emitted('update:modelValue')).toBeTruthy();
|
|
59
|
+
expect(wrapper.emitted('update:modelValue')[0]).toEqual(['a']);
|
|
60
|
+
});
|
|
61
|
+
|
|
62
|
+
it('syncs modelValue from parent', async () => {
|
|
63
|
+
const wrapper = mountRadioGroup({ modelValue: 'b' });
|
|
64
|
+
|
|
65
|
+
await wrapper.setProps({ modelValue: 'a' });
|
|
66
|
+
|
|
67
|
+
const inputs = wrapper.findAll('input[type="radio"]');
|
|
68
|
+
expect(inputs[0].element.checked).toBe(true);
|
|
69
|
+
});
|
|
70
|
+
|
|
71
|
+
it('does not emit when modelValue is already in sync', async () => {
|
|
72
|
+
const wrapper = mountRadioGroup({ modelValue: 'a' });
|
|
73
|
+
|
|
74
|
+
await wrapper.setProps({ modelValue: 'a' });
|
|
75
|
+
|
|
76
|
+
expect(wrapper.emitted('update:modelValue') ?? []).toHaveLength(0);
|
|
77
|
+
});
|
|
78
|
+
|
|
79
|
+
it('uses custom name when provided', () => {
|
|
80
|
+
const wrapper = mountRadioGroup({ name: 'custom-group' });
|
|
81
|
+
|
|
82
|
+
expect(wrapper.find('input[type="radio"]').attributes('name')).toBe(
|
|
83
|
+
'custom-group',
|
|
84
|
+
);
|
|
85
|
+
});
|
|
86
|
+
|
|
87
|
+
it('generates a random group name when name is not provided', () => {
|
|
88
|
+
const wrapper = mountRadioGroup();
|
|
89
|
+
|
|
90
|
+
expect(wrapper.find('input[type="radio"]').attributes('name')).toMatch(
|
|
91
|
+
/^unnnic-radio-group-/,
|
|
92
|
+
);
|
|
93
|
+
});
|
|
94
|
+
|
|
95
|
+
it('applies vertical state class', () => {
|
|
96
|
+
const wrapper = mountRadioGroup({ state: 'vertical' });
|
|
97
|
+
|
|
98
|
+
expect(wrapper.classes()).toContain('unnnic-radio-group--state-vertical');
|
|
99
|
+
});
|
|
100
|
+
});
|
|
@@ -0,0 +1,321 @@
|
|
|
1
|
+
import { afterEach, beforeEach, describe, expect, it, vi } from 'vitest';
|
|
2
|
+
import { mount } from '@vue/test-utils';
|
|
3
|
+
|
|
4
|
+
import Slider from '../Slider.vue';
|
|
5
|
+
|
|
6
|
+
const tooltipStub = {
|
|
7
|
+
name: 'UnnnicTooltip',
|
|
8
|
+
template: '<div><div ref="label" class="tooltip-label"><slot /></div></div>',
|
|
9
|
+
mounted() {
|
|
10
|
+
Object.defineProperty(this.$refs.label, 'clientWidth', {
|
|
11
|
+
configurable: true,
|
|
12
|
+
value: 32,
|
|
13
|
+
});
|
|
14
|
+
},
|
|
15
|
+
};
|
|
16
|
+
|
|
17
|
+
const noLabelTooltipStub = {
|
|
18
|
+
name: 'UnnnicTooltip',
|
|
19
|
+
template: '<div><slot /></div>',
|
|
20
|
+
};
|
|
21
|
+
|
|
22
|
+
const mockInputDimensions = (wrapper, width = 200) => {
|
|
23
|
+
const input = wrapper.find('input[type="range"]').element;
|
|
24
|
+
|
|
25
|
+
Object.defineProperty(input, 'clientWidth', {
|
|
26
|
+
configurable: true,
|
|
27
|
+
value: width,
|
|
28
|
+
});
|
|
29
|
+
Object.defineProperty(input, 'offsetWidth', {
|
|
30
|
+
configurable: true,
|
|
31
|
+
value: width,
|
|
32
|
+
});
|
|
33
|
+
};
|
|
34
|
+
|
|
35
|
+
const createWrapper = async (props = {}, options = {}) => {
|
|
36
|
+
const wrapper = mount(Slider, {
|
|
37
|
+
props: {
|
|
38
|
+
initialValue: 2,
|
|
39
|
+
minValue: 0,
|
|
40
|
+
maxValue: 10,
|
|
41
|
+
...props,
|
|
42
|
+
},
|
|
43
|
+
attachTo: document.body,
|
|
44
|
+
global: {
|
|
45
|
+
stubs: {
|
|
46
|
+
UnnnicTooltip: tooltipStub,
|
|
47
|
+
UnnnicIcon: true,
|
|
48
|
+
...options.stubs,
|
|
49
|
+
},
|
|
50
|
+
},
|
|
51
|
+
});
|
|
52
|
+
|
|
53
|
+
mockInputDimensions(wrapper);
|
|
54
|
+
await wrapper.vm.$nextTick();
|
|
55
|
+
|
|
56
|
+
return wrapper;
|
|
57
|
+
};
|
|
58
|
+
|
|
59
|
+
describe('Slider', () => {
|
|
60
|
+
beforeEach(() => {
|
|
61
|
+
vi.useFakeTimers();
|
|
62
|
+
});
|
|
63
|
+
|
|
64
|
+
afterEach(() => {
|
|
65
|
+
vi.useRealTimers();
|
|
66
|
+
document.body.innerHTML = '';
|
|
67
|
+
});
|
|
68
|
+
|
|
69
|
+
it('renders label, info tooltip, and min/max labels', async () => {
|
|
70
|
+
const wrapper = await createWrapper({
|
|
71
|
+
label: 'Volume',
|
|
72
|
+
labelInfo: 'Adjust volume',
|
|
73
|
+
minLabel: 'Low',
|
|
74
|
+
maxLabel: 'High',
|
|
75
|
+
});
|
|
76
|
+
|
|
77
|
+
expect(wrapper.find('.unnnic-slider__label').text()).toBe('Volume');
|
|
78
|
+
expect(wrapper.find('.unnnic-label__tooltip').exists()).toBe(true);
|
|
79
|
+
expect(wrapper.find('.unnnic-slider__content__labels__min').text()).toBe(
|
|
80
|
+
'Low',
|
|
81
|
+
);
|
|
82
|
+
expect(wrapper.find('.unnnic-slider__content__labels__max').text()).toBe(
|
|
83
|
+
'High',
|
|
84
|
+
);
|
|
85
|
+
});
|
|
86
|
+
|
|
87
|
+
it('hides value input when showInputValue is false', async () => {
|
|
88
|
+
const wrapper = await createWrapper({ showInputValue: false });
|
|
89
|
+
|
|
90
|
+
expect(wrapper.find('.value-input').exists()).toBe(false);
|
|
91
|
+
});
|
|
92
|
+
|
|
93
|
+
it('emits valueChange when range input changes', async () => {
|
|
94
|
+
const wrapper = await createWrapper();
|
|
95
|
+
|
|
96
|
+
const rangeInput = wrapper.find('input[type="range"]');
|
|
97
|
+
await rangeInput.setValue(5);
|
|
98
|
+
await rangeInput.trigger('change');
|
|
99
|
+
|
|
100
|
+
expect(wrapper.emitted('valueChange')).toBeTruthy();
|
|
101
|
+
expect(wrapper.emitted('valueChange').at(-1)).toEqual(['5']);
|
|
102
|
+
});
|
|
103
|
+
|
|
104
|
+
it('updates value from the text input', async () => {
|
|
105
|
+
const wrapper = await createWrapper();
|
|
106
|
+
|
|
107
|
+
const valueInput = wrapper.find('.value-input');
|
|
108
|
+
await valueInput.setValue('7');
|
|
109
|
+
|
|
110
|
+
expect(wrapper.vm.sliderVal).toBe('7');
|
|
111
|
+
expect(wrapper.emitted('valueChange')).toBeTruthy();
|
|
112
|
+
});
|
|
113
|
+
|
|
114
|
+
it('shows tooltip on mouseover and hides on mouseleave', async () => {
|
|
115
|
+
const wrapper = await createWrapper();
|
|
116
|
+
|
|
117
|
+
const rangeInput = wrapper.find('input[type="range"]');
|
|
118
|
+
await rangeInput.trigger('mouseover');
|
|
119
|
+
expect(wrapper.vm.showTooltip).toBe(true);
|
|
120
|
+
|
|
121
|
+
await rangeInput.trigger('mouseleave');
|
|
122
|
+
expect(wrapper.vm.showTooltip).toBe(false);
|
|
123
|
+
});
|
|
124
|
+
|
|
125
|
+
it('computes css variables from current state', async () => {
|
|
126
|
+
const wrapper = await createWrapper({
|
|
127
|
+
initialValue: 4,
|
|
128
|
+
minValue: 0,
|
|
129
|
+
maxValue: 8,
|
|
130
|
+
});
|
|
131
|
+
|
|
132
|
+
expect(wrapper.vm.cssVars).toEqual({
|
|
133
|
+
'--val': 4,
|
|
134
|
+
'--tooltip-offset': expect.any(Number),
|
|
135
|
+
'--min': 0,
|
|
136
|
+
'--max': 8,
|
|
137
|
+
});
|
|
138
|
+
});
|
|
139
|
+
|
|
140
|
+
it('returns zero tooltip offset when dimensions are unavailable', async () => {
|
|
141
|
+
const wrapper = await createWrapper();
|
|
142
|
+
wrapper.vm.sliderWidth = 0;
|
|
143
|
+
wrapper.vm.labelWidth = 0;
|
|
144
|
+
|
|
145
|
+
expect(wrapper.vm.getNewTooltipPosition()).toBe(0);
|
|
146
|
+
});
|
|
147
|
+
|
|
148
|
+
it('handles NaN range when min and max are equal', async () => {
|
|
149
|
+
const wrapper = await createWrapper({
|
|
150
|
+
initialValue: 5,
|
|
151
|
+
minValue: 5,
|
|
152
|
+
maxValue: 5,
|
|
153
|
+
});
|
|
154
|
+
wrapper.vm.sliderWidth = 200;
|
|
155
|
+
wrapper.vm.labelWidth = 32;
|
|
156
|
+
|
|
157
|
+
expect(wrapper.vm.getNewTooltipPosition()).toBeTypeOf('number');
|
|
158
|
+
});
|
|
159
|
+
|
|
160
|
+
it('uses fallback label width when label width is zero', async () => {
|
|
161
|
+
const wrapper = await createWrapper();
|
|
162
|
+
wrapper.vm.sliderWidth = 200;
|
|
163
|
+
wrapper.vm.labelWidth = 0;
|
|
164
|
+
|
|
165
|
+
expect(wrapper.vm.getNewTooltipPosition()).toBe(0);
|
|
166
|
+
});
|
|
167
|
+
|
|
168
|
+
it('reconfigures tooltip on window resize', async () => {
|
|
169
|
+
const wrapper = await createWrapper();
|
|
170
|
+
const configureSpy = vi.spyOn(wrapper.vm, 'configureTooltip');
|
|
171
|
+
|
|
172
|
+
window.dispatchEvent(new Event('resize'));
|
|
173
|
+
|
|
174
|
+
expect(configureSpy).toHaveBeenCalled();
|
|
175
|
+
});
|
|
176
|
+
|
|
177
|
+
it('removes resize listener on unmount', async () => {
|
|
178
|
+
const wrapper = await createWrapper();
|
|
179
|
+
const removeSpy = vi.spyOn(window, 'removeEventListener');
|
|
180
|
+
|
|
181
|
+
wrapper.unmount();
|
|
182
|
+
|
|
183
|
+
expect(removeSpy).toHaveBeenCalledWith('resize', wrapper.vm.handleResize);
|
|
184
|
+
});
|
|
185
|
+
|
|
186
|
+
it('initializes dimensions from polling intervals', async () => {
|
|
187
|
+
const wrapper = await createWrapper();
|
|
188
|
+
mockInputDimensions(wrapper, 0);
|
|
189
|
+
|
|
190
|
+
vi.advanceTimersByTime(100);
|
|
191
|
+
await wrapper.vm.$nextTick();
|
|
192
|
+
|
|
193
|
+
mockInputDimensions(wrapper, 200);
|
|
194
|
+
vi.advanceTimersByTime(100);
|
|
195
|
+
await wrapper.vm.$nextTick();
|
|
196
|
+
|
|
197
|
+
expect(wrapper.vm.sliderWidth).toBe(200);
|
|
198
|
+
expect(wrapper.vm.labelWidth).toBe(32);
|
|
199
|
+
});
|
|
200
|
+
|
|
201
|
+
it('skips tooltip label polling when label ref is missing', async () => {
|
|
202
|
+
vi.spyOn(Slider.methods, 'configureTooltip').mockImplementation(() => {});
|
|
203
|
+
|
|
204
|
+
const wrapper = await createWrapper(
|
|
205
|
+
{},
|
|
206
|
+
{ stubs: { UnnnicTooltip: noLabelTooltipStub } },
|
|
207
|
+
);
|
|
208
|
+
mockInputDimensions(wrapper);
|
|
209
|
+
|
|
210
|
+
vi.advanceTimersByTime(100);
|
|
211
|
+
await wrapper.vm.$nextTick();
|
|
212
|
+
|
|
213
|
+
expect(wrapper.vm.labelWidth).toBe(0);
|
|
214
|
+
|
|
215
|
+
Slider.methods.configureTooltip.mockRestore();
|
|
216
|
+
});
|
|
217
|
+
|
|
218
|
+
it('syncs value input text when slider value changes', async () => {
|
|
219
|
+
const wrapper = await createWrapper({ initialValue: 1 });
|
|
220
|
+
|
|
221
|
+
const valueInput = wrapper.find('.value-input').element;
|
|
222
|
+
valueInput.textContent = 'old';
|
|
223
|
+
|
|
224
|
+
wrapper.vm.sliderVal = 9;
|
|
225
|
+
await wrapper.vm.$nextTick();
|
|
226
|
+
await wrapper.vm.$nextTick();
|
|
227
|
+
|
|
228
|
+
expect(valueInput.textContent).toBe('9');
|
|
229
|
+
});
|
|
230
|
+
|
|
231
|
+
it('skips value input sync when text already matches slider value', async () => {
|
|
232
|
+
const wrapper = await createWrapper({ initialValue: 4 });
|
|
233
|
+
const valueInput = wrapper.find('.value-input').element;
|
|
234
|
+
valueInput.textContent = '4';
|
|
235
|
+
|
|
236
|
+
wrapper.vm.sliderVal = 4;
|
|
237
|
+
await wrapper.vm.$nextTick();
|
|
238
|
+
await wrapper.vm.$nextTick();
|
|
239
|
+
|
|
240
|
+
expect(valueInput.textContent).toBe('4');
|
|
241
|
+
});
|
|
242
|
+
|
|
243
|
+
it('computes tooltip position with non-zero dimensions', async () => {
|
|
244
|
+
const wrapper = await createWrapper({
|
|
245
|
+
initialValue: 5,
|
|
246
|
+
minValue: 0,
|
|
247
|
+
maxValue: 10,
|
|
248
|
+
});
|
|
249
|
+
wrapper.vm.sliderWidth = 200;
|
|
250
|
+
wrapper.vm.labelWidth = 32;
|
|
251
|
+
|
|
252
|
+
expect(wrapper.vm.getNewTooltipPosition()).toBeGreaterThan(0);
|
|
253
|
+
});
|
|
254
|
+
|
|
255
|
+
it('uses fallback label width when label width is NaN', async () => {
|
|
256
|
+
const wrapper = await createWrapper();
|
|
257
|
+
wrapper.vm.sliderWidth = 200;
|
|
258
|
+
wrapper.vm.labelWidth = Number.NaN;
|
|
259
|
+
|
|
260
|
+
expect(wrapper.vm.getNewTooltipPosition()).toBeTypeOf('number');
|
|
261
|
+
});
|
|
262
|
+
|
|
263
|
+
it('ignores input polling after unmount', async () => {
|
|
264
|
+
const wrapper = await createWrapper();
|
|
265
|
+
mockInputDimensions(wrapper, 0);
|
|
266
|
+
|
|
267
|
+
wrapper.unmount();
|
|
268
|
+
vi.advanceTimersByTime(200);
|
|
269
|
+
|
|
270
|
+
expect(true).toBe(true);
|
|
271
|
+
});
|
|
272
|
+
|
|
273
|
+
it('waits until tooltip label width is greater than zero', async () => {
|
|
274
|
+
const delayedLabelTooltipStub = {
|
|
275
|
+
name: 'UnnnicTooltip',
|
|
276
|
+
template:
|
|
277
|
+
'<div><div ref="label" class="tooltip-label"><slot /></div></div>',
|
|
278
|
+
mounted() {
|
|
279
|
+
Object.defineProperty(this.$refs.label, 'clientWidth', {
|
|
280
|
+
configurable: true,
|
|
281
|
+
value: 0,
|
|
282
|
+
});
|
|
283
|
+
},
|
|
284
|
+
};
|
|
285
|
+
|
|
286
|
+
vi.spyOn(Slider.methods, 'configureTooltip').mockImplementation(
|
|
287
|
+
function mockConfigure() {
|
|
288
|
+
if (!this.$refs.input || !this.$refs.tooltip?.$refs?.label) {
|
|
289
|
+
return;
|
|
290
|
+
}
|
|
291
|
+
|
|
292
|
+
this.sliderWidth = this.$refs.input.clientWidth;
|
|
293
|
+
this.labelWidth = this.$refs.tooltip.$refs.label.clientWidth;
|
|
294
|
+
this.tooltipOffset = this.getNewTooltipPosition();
|
|
295
|
+
},
|
|
296
|
+
);
|
|
297
|
+
|
|
298
|
+
const wrapper = await createWrapper(
|
|
299
|
+
{},
|
|
300
|
+
{
|
|
301
|
+
stubs: { UnnnicTooltip: delayedLabelTooltipStub },
|
|
302
|
+
},
|
|
303
|
+
);
|
|
304
|
+
mockInputDimensions(wrapper);
|
|
305
|
+
|
|
306
|
+
vi.advanceTimersByTime(100);
|
|
307
|
+
await wrapper.vm.$nextTick();
|
|
308
|
+
expect(wrapper.vm.labelWidth).toBe(0);
|
|
309
|
+
|
|
310
|
+
Object.defineProperty(wrapper.vm.$refs.tooltip.$refs.label, 'clientWidth', {
|
|
311
|
+
configurable: true,
|
|
312
|
+
value: 32,
|
|
313
|
+
});
|
|
314
|
+
vi.advanceTimersByTime(100);
|
|
315
|
+
await wrapper.vm.$nextTick();
|
|
316
|
+
|
|
317
|
+
expect(wrapper.vm.labelWidth).toBe(32);
|
|
318
|
+
|
|
319
|
+
Slider.methods.configureTooltip.mockRestore();
|
|
320
|
+
});
|
|
321
|
+
});
|
|
@@ -1,10 +1,6 @@
|
|
|
1
1
|
<template>
|
|
2
|
-
<UiTable v-if="version === '2'">
|
|
3
|
-
<slot />
|
|
4
|
-
</UiTable>
|
|
5
|
-
|
|
6
2
|
<div
|
|
7
|
-
v-
|
|
3
|
+
v-show="true"
|
|
8
4
|
class="unnnic-table"
|
|
9
5
|
>
|
|
10
6
|
<div class="header">
|
|
@@ -32,30 +28,15 @@
|
|
|
32
28
|
</template>
|
|
33
29
|
|
|
34
30
|
<script>
|
|
35
|
-
import { computed } from 'vue';
|
|
36
|
-
import { Table as UiTable } from '@/components/ui/table';
|
|
37
|
-
|
|
38
31
|
export default {
|
|
39
32
|
name: 'UnnnicTable',
|
|
40
33
|
|
|
41
|
-
components: {
|
|
42
|
-
UiTable,
|
|
43
|
-
},
|
|
44
|
-
|
|
45
|
-
provide() {
|
|
46
|
-
return {
|
|
47
|
-
unnnicTableVersion: computed(() => this.version),
|
|
48
|
-
};
|
|
49
|
-
},
|
|
34
|
+
components: {},
|
|
50
35
|
|
|
51
36
|
props: {
|
|
52
37
|
items: {
|
|
53
38
|
type: Array,
|
|
54
39
|
},
|
|
55
|
-
version: {
|
|
56
|
-
type: String,
|
|
57
|
-
default: '1',
|
|
58
|
-
},
|
|
59
40
|
},
|
|
60
41
|
|
|
61
42
|
data() {
|
|
@@ -1,16 +1,5 @@
|
|
|
1
1
|
<template>
|
|
2
|
-
<
|
|
3
|
-
v-if="isVersion2"
|
|
4
|
-
v-bind="$attrs"
|
|
5
|
-
>
|
|
6
|
-
<slot />
|
|
7
|
-
</UiTableRow>
|
|
8
|
-
|
|
9
|
-
<div
|
|
10
|
-
v-else
|
|
11
|
-
class="table-row"
|
|
12
|
-
v-bind="$attrs"
|
|
13
|
-
>
|
|
2
|
+
<div class="table-row">
|
|
14
3
|
<template
|
|
15
4
|
v-for="(header, index) in headers"
|
|
16
5
|
:key="header.id"
|
|
@@ -37,31 +26,17 @@
|
|
|
37
26
|
</template>
|
|
38
27
|
|
|
39
28
|
<script>
|
|
40
|
-
import { computed, inject, unref } from 'vue';
|
|
41
|
-
import UiTableRow from '@/components/ui/table/TableRow.vue';
|
|
42
|
-
|
|
43
29
|
export default {
|
|
44
30
|
name: 'UnnnicTableRow',
|
|
45
31
|
|
|
46
|
-
components: {
|
|
47
|
-
UiTableRow,
|
|
48
|
-
},
|
|
49
|
-
|
|
50
|
-
inheritAttrs: false,
|
|
51
|
-
|
|
52
32
|
props: {
|
|
53
33
|
headers: {
|
|
54
34
|
type: Array,
|
|
55
35
|
},
|
|
56
36
|
},
|
|
57
37
|
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
const isVersion2 = computed(() => unref(tableVersion) === '2');
|
|
61
|
-
|
|
62
|
-
return {
|
|
63
|
-
isVersion2,
|
|
64
|
-
};
|
|
38
|
+
data() {
|
|
39
|
+
return {};
|
|
65
40
|
},
|
|
66
41
|
};
|
|
67
42
|
</script>
|
|
@@ -1,5 +1,8 @@
|
|
|
1
1
|
<template>
|
|
2
|
-
<section
|
|
2
|
+
<section
|
|
3
|
+
:class="`unnnic-tag unnnic-tag--${size}`"
|
|
4
|
+
:style="{ backgroundColor: color }"
|
|
5
|
+
>
|
|
3
6
|
<section
|
|
4
7
|
v-if="leftIcon"
|
|
5
8
|
class="unnnic-tag__icon"
|
|
@@ -62,8 +65,6 @@ const color = computed(() => {
|
|
|
62
65
|
padding: calc($unnnic-space-1 * 1.5) $unnnic-space-3;
|
|
63
66
|
width: fit-content;
|
|
64
67
|
|
|
65
|
-
background-color: v-bind(color);
|
|
66
|
-
|
|
67
68
|
&--small {
|
|
68
69
|
padding: calc($unnnic-space-1 * 0.5) $unnnic-space-3;
|
|
69
70
|
}
|
|
@@ -0,0 +1,51 @@
|
|
|
1
|
+
import { describe, expect, it } from 'vitest';
|
|
2
|
+
import { mount } from '@vue/test-utils';
|
|
3
|
+
|
|
4
|
+
import DefaultTag from '../DefaultTag.vue';
|
|
5
|
+
|
|
6
|
+
const schemes = [
|
|
7
|
+
'aux-green',
|
|
8
|
+
'aux-blue',
|
|
9
|
+
'aux-purple',
|
|
10
|
+
'aux-red',
|
|
11
|
+
'aux-pink',
|
|
12
|
+
'aux-orange',
|
|
13
|
+
'aux-yellow',
|
|
14
|
+
'aux-gray',
|
|
15
|
+
'aux-teal',
|
|
16
|
+
'aux-weni',
|
|
17
|
+
'unknown-scheme',
|
|
18
|
+
];
|
|
19
|
+
|
|
20
|
+
describe('DefaultTag', () => {
|
|
21
|
+
schemes.forEach((scheme) => {
|
|
22
|
+
it(`renders with scheme ${scheme}`, () => {
|
|
23
|
+
const wrapper = mount(DefaultTag, {
|
|
24
|
+
props: { text: `Tag ${scheme}`, scheme },
|
|
25
|
+
global: { stubs: { UnnnicIcon: true } },
|
|
26
|
+
});
|
|
27
|
+
|
|
28
|
+
expect(wrapper.find('.unnnic-tag__label').text()).toBe(`Tag ${scheme}`);
|
|
29
|
+
expect(wrapper.element.style.backgroundColor).toBeTruthy();
|
|
30
|
+
wrapper.unmount();
|
|
31
|
+
});
|
|
32
|
+
});
|
|
33
|
+
|
|
34
|
+
it('renders left icon when provided', () => {
|
|
35
|
+
const wrapper = mount(DefaultTag, {
|
|
36
|
+
props: { text: 'Label', leftIcon: 'info' },
|
|
37
|
+
global: { stubs: { UnnnicIcon: true } },
|
|
38
|
+
});
|
|
39
|
+
|
|
40
|
+
expect(wrapper.find('.unnnic-tag__icon').exists()).toBe(true);
|
|
41
|
+
});
|
|
42
|
+
|
|
43
|
+
it('applies small size class', () => {
|
|
44
|
+
const wrapper = mount(DefaultTag, {
|
|
45
|
+
props: { text: 'Label', size: 'small' },
|
|
46
|
+
global: { stubs: { UnnnicIcon: true } },
|
|
47
|
+
});
|
|
48
|
+
|
|
49
|
+
expect(wrapper.classes()).toContain('unnnic-tag--small');
|
|
50
|
+
});
|
|
51
|
+
});
|