@eturnity/eturnity_reusable_components 8.22.23 → 8.26.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.
@@ -0,0 +1,178 @@
1
+ // Mock SVG components
2
+ const SvgMock = {
3
+ name: 'SvgMock',
4
+ template: '<div class="mock-svg"></div>',
5
+ }
6
+
7
+ // Mock Slider component
8
+ const Slider = {
9
+ name: 'Slider',
10
+ template: '<div class="mock-slider"></div>',
11
+ props: [
12
+ 'color',
13
+ 'draggable',
14
+ 'max',
15
+ 'min',
16
+ 'minWidth',
17
+ 'resizable',
18
+ 'step',
19
+ 'stepCount',
20
+ 'subStepCount',
21
+ 'z',
22
+ ],
23
+ emits: ['drag-stop', 'resize-stop', 'activated', 'deactivated'],
24
+ }
25
+
26
+ jest.mock('../../assets/svgIcons/handle.svg', () => SvgMock)
27
+ jest.mock('../../assets/svgIcons/add_icon.svg', () => SvgMock)
28
+ jest.mock('./Slider', () => Slider)
29
+
30
+ import { mount } from '@vue/test-utils'
31
+ import RangeSlider from './index.vue'
32
+
33
+ const baseProps = {
34
+ dataLocation: 'test',
35
+ tariffItems: [
36
+ { id: 1, name: 'Tariff A', color: '#4CAF50' },
37
+ { id: 2, name: 'Tariff B', color: '#2196F3' },
38
+ ],
39
+ labels: [
40
+ {
41
+ placement: 'top',
42
+ value: ['00:00', '12:00', '24:00'],
43
+ },
44
+ {
45
+ placement: 'left',
46
+ value: [
47
+ {
48
+ id: 1,
49
+ name: 'Group 1',
50
+ selectedTariffs: [
51
+ {
52
+ id: 1,
53
+ name: 'Tariff A',
54
+ min: 0,
55
+ max: 12,
56
+ color: '#4CAF50',
57
+ parentId: 1,
58
+ },
59
+ ],
60
+ },
61
+ ],
62
+ },
63
+ ],
64
+ rangeMargin: 1,
65
+ step: 1,
66
+ minWidth: 1,
67
+ canOverlap: true,
68
+ disabled: false,
69
+ }
70
+
71
+ describe('RangeSlider', () => {
72
+ it('renders top and left labels', () => {
73
+ const wrapper = mount(RangeSlider, { props: baseProps })
74
+ expect(wrapper.text()).toContain('00:00')
75
+ expect(wrapper.text()).toContain('Group 1')
76
+ })
77
+
78
+ it('renders sliders for selected tariffs', () => {
79
+ const wrapper = mount(RangeSlider, { props: baseProps })
80
+ // Should render one slider for Tariff A
81
+ expect(wrapper.findAll('[data-id^="slider_bar_item_"]').length).toBe(1)
82
+ })
83
+
84
+ it('emits onChange when slider is dragged or resized', async () => {
85
+ const wrapper = mount(RangeSlider, { props: baseProps })
86
+ // Simulate drag-stop event on Slider
87
+ await wrapper
88
+ .findComponent({ name: 'Slider' })
89
+ .vm.$emit('drag-stop', { min: 2, max: 10 })
90
+ expect(wrapper.emitted().drag).toBeTruthy()
91
+ })
92
+
93
+ it('shows error message when overlap is detected and canOverlap is false', async () => {
94
+ const overlappingLabels = [
95
+ baseProps.labels[0],
96
+ {
97
+ placement: 'left',
98
+ value: [
99
+ {
100
+ id: 1,
101
+ name: 'Group 1',
102
+ selectedTariffs: [
103
+ {
104
+ id: 1,
105
+ name: 'Tariff A',
106
+ min: 0,
107
+ max: 12,
108
+ color: '#4CAF50',
109
+ parentId: 1,
110
+ },
111
+ {
112
+ id: 2,
113
+ name: 'Tariff B',
114
+ min: 10,
115
+ max: 20,
116
+ color: '#2196F3',
117
+ parentId: 1,
118
+ },
119
+ ],
120
+ },
121
+ ],
122
+ },
123
+ ]
124
+
125
+ // First mount with non-overlapping labels
126
+ const wrapper = mount(RangeSlider, {
127
+ props: {
128
+ ...baseProps,
129
+ canOverlap: false,
130
+ labels: baseProps.labels, // Start with non-overlapping labels
131
+ },
132
+ global: {
133
+ mocks: {
134
+ $gettext: (msg) => msg,
135
+ },
136
+ },
137
+ })
138
+
139
+ // Wait for initial mount
140
+ await wrapper.vm.$nextTick()
141
+
142
+ // Now update with overlapping labels
143
+ await wrapper.setProps({ labels: overlappingLabels })
144
+
145
+ // Wait for all updates to complete
146
+ await wrapper.vm.$nextTick()
147
+ await new Promise((resolve) => setTimeout(resolve, 0))
148
+
149
+ // Verify the overlap state
150
+ expect(wrapper.vm.hasOverlap).toBe(true)
151
+ expect(wrapper.vm.OverlapId).toContain(1)
152
+
153
+ // Check for error message
154
+ expect(wrapper.text()).toContain('overlap_error_message')
155
+ })
156
+
157
+ it('does not allow interaction when disabled', async () => {
158
+ const wrapper = mount(RangeSlider, {
159
+ props: { ...baseProps, disabled: true },
160
+ })
161
+ // Try to click on a bar
162
+ await wrapper.find('[data-id^="slider_bar_"]').trigger('click')
163
+ // Should not emit any events
164
+ expect(wrapper.emitted()['on-bar-tariff-click']).toBeFalsy()
165
+ })
166
+
167
+ it('emits on-bar-tariff-click when a bar is clicked', async () => {
168
+ const wrapper = mount(RangeSlider, { props: baseProps })
169
+ await wrapper.find('[data-id^="slider_bar_item_"]').trigger('click')
170
+ expect(wrapper.emitted()['on-bar-tariff-click']).toBeTruthy()
171
+ })
172
+
173
+ it('renders nothing if labels are empty', () => {
174
+ const wrapper = mount(RangeSlider, { props: { ...baseProps, labels: [] } })
175
+ expect(wrapper.html()).toContain('<div') // Should still render container
176
+ expect(wrapper.text()).not.toContain('00:00')
177
+ })
178
+ })
@@ -0,0 +1,159 @@
1
+ import RangeSlider from './index.vue'
2
+
3
+ export default {
4
+ title: 'Components/RangeSlider',
5
+ component: RangeSlider,
6
+ argTypes: {
7
+ dataLocation: { control: 'text' },
8
+ tariffItems: { control: 'array' },
9
+ labels: { control: 'array' },
10
+ rangeMargin: { control: 'number' },
11
+ step: { control: 'number' },
12
+ minWidth: { control: 'number' },
13
+ canOverlap: { control: 'boolean' },
14
+ disabled: { control: 'boolean' },
15
+ onChange: { action: 'changed' },
16
+ onBarTariffClick: { action: 'barTariffClicked' },
17
+ activate: { action: 'activated' },
18
+ deactivate: { action: 'deactivated' },
19
+ hasOverlap: { action: 'overlapDetected' },
20
+ },
21
+ }
22
+
23
+ const Template = (args) => ({
24
+ components: { RangeSlider },
25
+ setup() {
26
+ return { args }
27
+ },
28
+ template: '<RangeSlider v-bind="args" />',
29
+ })
30
+
31
+ export const Default = Template.bind({})
32
+ Default.args = {
33
+ dataLocation: 'default',
34
+ tariffItems: [
35
+ { id: 1, name: 'Tariff A', color: '#4CAF50' },
36
+ { id: 2, name: 'Tariff B', color: '#2196F3' },
37
+ { id: 3, name: 'Tariff C', color: '#FFC107' },
38
+ ],
39
+ labels: [
40
+ {
41
+ placement: 'top',
42
+ value: ['00:00', '04:00', '08:00', '12:00', '16:00', '20:00', '24:00'],
43
+ },
44
+ {
45
+ placement: 'left',
46
+ value: [
47
+ {
48
+ id: 1,
49
+ name: 'Group 1',
50
+ selectedTariffs: [
51
+ {
52
+ id: 1,
53
+ name: 'Tariff A',
54
+ min: 0,
55
+ max: 8,
56
+ color: '#4CAF50',
57
+ parentId: 1,
58
+ },
59
+ ],
60
+ },
61
+ {
62
+ id: 2,
63
+ name: 'Group 2',
64
+ selectedTariffs: [
65
+ {
66
+ id: 2,
67
+ name: 'Tariff B',
68
+ min: 8,
69
+ max: 16,
70
+ color: '#2196F3',
71
+ parentId: 2,
72
+ },
73
+ ],
74
+ },
75
+ ],
76
+ },
77
+ ],
78
+ rangeMargin: 1,
79
+ step: 1,
80
+ minWidth: 1,
81
+ canOverlap: true,
82
+ disabled: false,
83
+ }
84
+
85
+ export const WithOverlap = Template.bind({})
86
+ WithOverlap.args = {
87
+ ...Default.args,
88
+ labels: [
89
+ {
90
+ placement: 'top',
91
+ value: ['00:00', '04:00', '08:00', '12:00', '16:00', '20:00', '24:00'],
92
+ },
93
+ {
94
+ placement: 'left',
95
+ value: [
96
+ {
97
+ id: 1,
98
+ name: 'Group 1',
99
+ selectedTariffs: [
100
+ {
101
+ id: 1,
102
+ name: 'Tariff A',
103
+ min: 0,
104
+ max: 8,
105
+ color: '#4CAF50',
106
+ parentId: 1,
107
+ },
108
+ {
109
+ id: 2,
110
+ name: 'Tariff B',
111
+ min: 6,
112
+ max: 14,
113
+ color: '#2196F3',
114
+ parentId: 1,
115
+ },
116
+ ],
117
+ },
118
+ ],
119
+ },
120
+ ],
121
+ canOverlap: false,
122
+ }
123
+
124
+ export const Disabled = Template.bind({})
125
+ Disabled.args = {
126
+ ...Default.args,
127
+ disabled: true,
128
+ }
129
+
130
+ export const CustomSteps = Template.bind({})
131
+ CustomSteps.args = {
132
+ ...Default.args,
133
+ step: 2,
134
+ labels: [
135
+ {
136
+ placement: 'top',
137
+ value: ['00:00', '06:00', '12:00', '18:00', '24:00'],
138
+ },
139
+ {
140
+ placement: 'left',
141
+ value: [
142
+ {
143
+ id: 1,
144
+ name: 'Group 1',
145
+ selectedTariffs: [
146
+ {
147
+ id: 1,
148
+ name: 'Tariff A',
149
+ min: 0,
150
+ max: 12,
151
+ color: '#4CAF50',
152
+ parentId: 1,
153
+ },
154
+ ],
155
+ },
156
+ ],
157
+ },
158
+ ],
159
+ }
@@ -322,42 +322,37 @@
322
322
  },
323
323
  computed: {
324
324
  maximum() {
325
- if (!this.labels.length) return
326
-
327
- return this.topLabels.at(-1)
325
+ if (!this.labels?.length) return 0
326
+ const labels = this.topLabels
327
+ return labels?.length ? labels[labels.length - 1] : 0
328
328
  },
329
329
  minimum() {
330
- if (!this.labels.length) return
331
-
332
- return this.topLabels[0]
330
+ if (!this.labels?.length) return 0
331
+ const labels = this.topLabels
332
+ return labels?.length ? labels[0] : 0
333
333
  },
334
334
  topLabels() {
335
- if (!this.labels.length) return
335
+ if (!this.labels?.length) return []
336
336
  const labels = this.labels.find((label) => label.placement === 'top')
337
-
338
- return labels ? labels.value : []
337
+ return labels?.value || []
339
338
  },
340
339
  leftLabels() {
341
- if (!this.labels.length) return
340
+ if (!this.labels?.length) return []
342
341
  const labels = this.labels.find((label) => label.placement === 'left')
343
- return labels ? labels.value : []
342
+ return labels?.value || []
344
343
  },
345
344
  stepCount() {
346
- let labels = this.topLabels || []
347
-
345
+ const labels = this.topLabels || []
348
346
  if (labels.length) {
349
347
  return labels.length - 1
350
348
  }
351
-
352
- return Math.floor((this.maximum - this.minimum) / this.step)
349
+ return Math.floor((this.maximum - this.minimum) / this.step) || 0
353
350
  },
354
351
  subStepCount() {
355
- let labels = this.topLabels || []
356
-
352
+ const labels = this.topLabels || []
357
353
  if (labels.length && this.step) {
358
- return (this.maximum - this.minimum) / this.step
354
+ return Math.floor((this.maximum - this.minimum) / this.step) || 0
359
355
  }
360
-
361
356
  return 0
362
357
  },
363
358
  },
@@ -395,7 +390,7 @@
395
390
  }
396
391
  },
397
392
  },
398
- beforeDestroy() {
393
+ beforeUnmount() {
399
394
  // Remove the global click event listener to prevent memory leaks
400
395
  document.removeEventListener('click', this.handleOutsideClick)
401
396
  document.removeEventListener('keydown', this.onKeyDownDelete)
@@ -5,6 +5,7 @@
5
5
  v-for="item in tabsData"
6
6
  :key="item.id"
7
7
  :data-id="item.dataId"
8
+ :data-qa-id="item.dataId"
8
9
  :data-test-active="activeTab === item.id"
9
10
  data-test-id="tab-item"
10
11
  :full-size="fullSize"