@eturnity/eturnity_reusable_components 7.39.5-qa-elisee-7.42.1 → 7.45.0

Sign up to get free protection for your applications and to get access to all the features.
@@ -0,0 +1,33 @@
1
+ const defaultRadioButtonProps = {
2
+ dataId: 'radio_buttons',
3
+ name: 'options',
4
+ options: [
5
+ {
6
+ label: 'Option 1',
7
+ value: 'option_1',
8
+ img: 'https://staging-01-backend.eturnity.dev/static/e_templates/base/styles/solar_img_i.png',
9
+ },
10
+ {
11
+ label: 'Option 2',
12
+ value: 'option_2',
13
+ img: 'https://staging-01-backend.eturnity.dev/static/e_templates/base/styles/switcher_img_i.png',
14
+ infoText: 'Option 2 info text',
15
+ },
16
+ {
17
+ label: 'Option 3',
18
+ value: 'option_3',
19
+ img: 'https://staging-01-backend.eturnity.dev/static/e_templates/base/styles/varta_img_i.png',
20
+ infoText: 'Option 3 info text',
21
+ disabled: true,
22
+ },
23
+ {
24
+ label: 'Option 4',
25
+ value: 'option_4',
26
+ disabled: true,
27
+ infoText: 'Option 4 info text',
28
+ },
29
+ ],
30
+ selectedOption: 'option_2',
31
+ }
32
+
33
+ export default defaultRadioButtonProps
@@ -1,6 +1,10 @@
1
1
  <template>
2
2
  <ComponentWrapper :layout="layout">
3
- <RadioWrapper v-for="(item, index) in options" :key="item.value">
3
+ <RadioWrapper
4
+ v-for="(item, index) in options"
5
+ :key="item.value"
6
+ :data-test-id="'radioWrapper_' + item.value"
7
+ >
4
8
  <LabelContainer
5
9
  :checkmark-color="checkmarkColor"
6
10
  :has-label="!!item.label"
@@ -11,6 +15,7 @@
11
15
  <Radio
12
16
  :checked="selectedOption === item.value"
13
17
  :data-id="`radio_button_${dataId}_option_${item.value}`"
18
+ :data-test-id="'radioInput_' + item.value"
14
19
  :disabled="item.disabled"
15
20
  :name="'radioButtons_' + radioName"
16
21
  type="radio"
@@ -18,15 +23,32 @@
18
23
  @click="onInputHandler(item.value)"
19
24
  />
20
25
  <span class="checkmark"></span>
21
- <LabelText v-if="item.label" :is-disabled="item.disabled">
26
+ <LabelText
27
+ v-if="item.label"
28
+ :data-test-id="'radioLabel_' + item.value"
29
+ :is-disabled="item.disabled"
30
+ >
22
31
  {{ item.label }}
23
32
  </LabelText>
24
- <InfoText v-if="item.infoText" size="16px" :text="item.infoText" />
33
+ <InfoText
34
+ v-if="item.infoText"
35
+ :data-test-id="'radioInfo_' + item.value"
36
+ size="16px"
37
+ :text="item.infoText"
38
+ />
25
39
  </LabelContainer>
26
40
  <ImageContainer v-if="item.img">
27
- <RadioImage :src="item.img" />
28
- <div class="search-icn-container" @click="toggleImageModal(index)">
41
+ <RadioImage
42
+ :data-test-id="'radioImage_' + item.value"
43
+ :src="item.img"
44
+ />
45
+ <div
46
+ class="search-icn-container"
47
+ :data-test-id="'radioOpenImage_' + item.value"
48
+ @click="toggleImageModal(index)"
49
+ >
29
50
  <img
51
+ alt=""
30
52
  class="search-icn"
31
53
  :src="require('../../../assets/icons/search_icon.png')"
32
54
  />
@@ -34,6 +56,7 @@
34
56
  </ImageContainer>
35
57
  <Modal
36
58
  v-if="item.img"
59
+ :data-test-id="'radioModal_' + item.value"
37
60
  :is-open="isImageOpen(index)"
38
61
  @on-close="toggleImageModal(null)"
39
62
  >
@@ -49,18 +72,21 @@
49
72
  // import RadioButton from "@eturnity/eturnity_reusable_components/src/components/inputs/radioButton"
50
73
  // To use:
51
74
  // <radio-button
75
+ // checkmarkColor="red"
76
+ // dataId="radio_button"
77
+ // layout="horizontal"
78
+ // name="Name for Radio buttons"
52
79
  // :options="radioOptions"
53
80
  // :selectedOption="checkedOption"
54
- // @on-radio-change="onInputChange($event)"
55
- // layout="vertical"
56
81
  // size="large"
82
+ // @on-radio-change="onInputChange($event)"
57
83
  // />
58
84
  // Data being passed should look like:
59
85
  // radioOptions: [
60
- // { label: 'Button 1', value: 'button_1', img: 'www.imagesrc.com', infoText: 'my info text' },
61
- // { label: 'Button 2', value: 'button_2', img: 'www.imagesrc.com', infoText: 'my info text 2' },
62
- // { label: 'Button 3', value: 'button_3', img: 'www.imagesrc.com', disabled: true },
63
- // { label: 'Button 4', value: 'button_4', disabled: true }
86
+ // { label: 'Option 1', value: 'option_1', img: 'www.imagesrc.com', infoText: 'my info text' },
87
+ // { label: 'Option 2', value: 'option_2', img: 'www.imagesrc.com', infoText: 'my info text 2' },
88
+ // { label: 'Option 3', value: 'option_3', img: 'www.imagesrc.com', disabled: true },
89
+ // { label: 'Option 4', value: 'option_4', disabled: true }
64
90
  // ]
65
91
 
66
92
  import styled from 'vue3-styled-components'
@@ -82,7 +108,6 @@
82
108
  cursor: pointer;
83
109
  position: absolute;
84
110
  opacity: 0;
85
- cursor: pointer;
86
111
  height: 0;
87
112
  width: 0;
88
113
  `
@@ -221,33 +246,42 @@
221
246
  props: {
222
247
  selectedOption: {
223
248
  required: true,
224
- default: false,
249
+ type: [String, Number],
225
250
  },
226
251
  options: {
252
+ default() {
253
+ return []
254
+ },
227
255
  required: true,
228
- default: [],
256
+ type: Array,
229
257
  },
230
258
  layout: {
231
- required: false,
232
259
  default: 'horizontal', //2 options: 'vertical' (only 1 per row) & 'horizontal' (many per row)
260
+ required: false,
261
+ type: String,
233
262
  },
234
263
  size: {
235
- required: false,
236
264
  default: 'medium', // small, medium, large
265
+ required: false,
266
+ type: String,
237
267
  },
238
268
  name: {
239
- required: false,
240
269
  default: '',
270
+ required: false,
271
+ type: String,
241
272
  },
242
273
  checkmarkColor: {
243
- required: false,
244
274
  default: '',
275
+ required: false,
276
+ type: String,
245
277
  },
246
278
  dataId: {
247
- type: String,
248
279
  default: 'key',
280
+ required: false,
281
+ type: String,
249
282
  },
250
283
  },
284
+ emits: ['on-radio-change'],
251
285
  data() {
252
286
  return {
253
287
  selectedImage: null,
@@ -260,7 +294,9 @@
260
294
  },
261
295
  methods: {
262
296
  onInputHandler(value) {
263
- this.$emit('on-radio-change', value)
297
+ if (value !== this.selectedOption) {
298
+ this.$emit('on-radio-change', value)
299
+ }
264
300
  },
265
301
  isImageOpen(index) {
266
302
  return this.selectedImage === index
@@ -0,0 +1,269 @@
1
+ import { mount, DOMWrapper } from '@vue/test-utils'
2
+ import RadioButton from '@/components/inputs/radioButton'
3
+ import defaultRadioButtonProps from './defaultProps'
4
+ import theme from '@/assets/theme'
5
+
6
+ jest.mock('@/components/icon/iconCache.mjs', () => ({
7
+ // need to mock this due to how jest handles import.meta
8
+ fetchIcon: jest.fn(() => Promise.resolve('mocked-icon-url.svg')),
9
+ }))
10
+ jest.mock('../../../assets/icons/search_icon.png', () => 'search_icon.png')
11
+
12
+ describe('RadioButton.vue', () => {
13
+ const radioButtons = mount(RadioButton, {
14
+ props: defaultRadioButtonProps,
15
+ global: {
16
+ provide: {
17
+ theme,
18
+ },
19
+ },
20
+ })
21
+ function findRadioWrappersByCriteria(criteria) {
22
+ // criteria === ['active', ..., 'hasImage'], can include any number of criteria
23
+ // This function is used for getting a list of
24
+ // RadioWrapper components which matches
25
+ // all the requested criteria (conjunctive filtering).
26
+ // Make sure that criteria don't contradict each other.
27
+ // List of possible criteria =
28
+ // - active || disabled
29
+ // - checked || unchecked
30
+ // - hasInfoIcon
31
+ // - hasImage
32
+
33
+ function getParentRadioWrapper(element) {
34
+ // function is used for getting a parent RadioWrapper component
35
+ const radioWrapper = element.element.closest(
36
+ '[data-test-id^="radioWrapper_"]'
37
+ )
38
+ return radioWrapper ? new DOMWrapper(radioWrapper) : null
39
+ }
40
+
41
+ const elementsWithTestIds = radioButtons.findAll('[data-test-id]')
42
+ let resultsObject = {}
43
+
44
+ criteria.forEach((criterion) => {
45
+ resultsObject[criterion] = []
46
+ if (criterion === 'active' || criterion === 'disabled') {
47
+ const criteriaRelatedElements = elementsWithTestIds.filter((element) =>
48
+ element.attributes('data-test-id').startsWith('radioInput_')
49
+ )
50
+ criteriaRelatedElements.forEach((el) => {
51
+ const element = el.element
52
+ if (criterion === 'active' ? !element.disabled : element.disabled) {
53
+ resultsObject[criterion].push(getParentRadioWrapper(el))
54
+ }
55
+ })
56
+ }
57
+ if (criterion === 'checked' || criterion === 'unchecked') {
58
+ const criteriaRelatedElements = elementsWithTestIds.filter((element) =>
59
+ element.attributes('data-test-id').startsWith('radioInput_')
60
+ )
61
+ criteriaRelatedElements.forEach((el) => {
62
+ const element = el.element
63
+ if (criterion === 'checked' ? element.checked : !element.checked) {
64
+ resultsObject[criterion].push(getParentRadioWrapper(el))
65
+ }
66
+ })
67
+ }
68
+ if (criterion === 'hasInfoIcon') {
69
+ const criteriaRelatedElements = elementsWithTestIds.filter((element) =>
70
+ element.attributes('data-test-id').startsWith('radioInfo_')
71
+ )
72
+ criteriaRelatedElements.forEach((element) => {
73
+ resultsObject[criterion].push(getParentRadioWrapper(element))
74
+ })
75
+ }
76
+ if (criterion === 'hasImage') {
77
+ const criteriaRelatedElements = elementsWithTestIds.filter((element) =>
78
+ element.attributes('data-test-id').startsWith('radioOpenImage_')
79
+ )
80
+ criteriaRelatedElements.forEach((element) => {
81
+ resultsObject[criterion].push(getParentRadioWrapper(element))
82
+ })
83
+ }
84
+ })
85
+
86
+ let resultArray = []
87
+ Object.keys(resultsObject).forEach((criterion, index) => {
88
+ if (index === 0) {
89
+ resultArray = [...resultsObject[criterion]]
90
+ } else {
91
+ resultArray = resultArray.filter((element) =>
92
+ resultsObject[criterion]
93
+ .map((item) => item.attributes('data-test-id'))
94
+ .includes(element.attributes('data-test-id'))
95
+ )
96
+ }
97
+ })
98
+
99
+ return resultArray
100
+ }
101
+
102
+ it('Radio buttons are rendered and emit correct payload on change', async () => {
103
+ //check that all the options have been rendered
104
+ let radioBtnWrappers = radioButtons.findAll('[data-test-id]')
105
+ radioBtnWrappers = radioBtnWrappers.filter((wrapper) =>
106
+ wrapper.attributes('data-test-id').startsWith('radioWrapper_')
107
+ )
108
+ const numberOfOptions = radioButtons.props('options').length
109
+ expect(radioBtnWrappers).toHaveLength(numberOfOptions)
110
+
111
+ // check that the selected element exists and there is only a single copy of it and has correct value
112
+ const checkedWrapperArray = findRadioWrappersByCriteria(['checked'])
113
+ expect(checkedWrapperArray.length).toBe(1)
114
+ const checkedWrapper = checkedWrapperArray[0]
115
+ const checkedRadioInput = checkedWrapper.find('input[type="radio"]')
116
+ const defaultValueFromProps = radioButtons.props('selectedOption')
117
+ expect(checkedRadioInput.attributes('value')).toBe(
118
+ defaultValueFromProps
119
+ )
120
+
121
+ // Log attributes to see what is rendered (commented out just for reference)
122
+ // console.log('checkedRadioInput attributes', checkedRadioInput.attributes())
123
+
124
+ // Test the label
125
+ const labelOfDefaultValue = radioButtons
126
+ .props('options')
127
+ .find((option) => option.value === defaultValueFromProps).label
128
+ expect(checkedWrapper.text()).toContain(labelOfDefaultValue)
129
+
130
+ // Test the click on unselected active element
131
+ const uncheckedWrapperArray = findRadioWrappersByCriteria([
132
+ 'unchecked',
133
+ 'active',
134
+ ])
135
+ const expectedValueOfEmittedEvent = uncheckedWrapperArray[0]
136
+ .attributes('data-test-id')
137
+ .replace('radioWrapper_', '')
138
+ const uncheckedLabelWrapper = uncheckedWrapperArray[0].find('label')
139
+ await uncheckedLabelWrapper.trigger('click')
140
+ expect(radioButtons.emitted('on-radio-change')).toBeTruthy()
141
+ const emittedEvents = radioButtons.emitted('on-radio-change')
142
+ expect(emittedEvents).toHaveLength(1) // Check if the event was emitted exactly once
143
+ // Check the payload of the event
144
+ expect(emittedEvents[emittedEvents.length - 1]).toEqual([
145
+ expectedValueOfEmittedEvent,
146
+ ])
147
+ await radioButtons.setProps({ selectedOption: expectedValueOfEmittedEvent })
148
+ }),
149
+ it('click on disabled element (|| selected element || info icon || image) does not emit anything', async () => {
150
+ // Remember the number of emitted events before triggering clicks
151
+ const initialNumberOfEvents =
152
+ radioButtons.emitted('on-radio-change').length
153
+
154
+ // Test RadioWrapper with disabled element
155
+ const disabledWrapperArray = findRadioWrappersByCriteria(['disabled'])
156
+ const disabledLabelWrapper = disabledWrapperArray[0].find('label')
157
+ await disabledLabelWrapper.trigger('click')
158
+ // Check if we still have the same number of emitted events as at the beginning
159
+ expect(radioButtons.emitted('on-radio-change')).toHaveLength(
160
+ initialNumberOfEvents
161
+ )
162
+
163
+ // Get RadioWrapper with selected element
164
+ const checkedWrapperArray = findRadioWrappersByCriteria(['checked'])
165
+ const checkedLabelWrapper = checkedWrapperArray[0].find('label')
166
+ await checkedLabelWrapper.trigger('click')
167
+ // Check if we still have the same number of emitted events as at the beginning
168
+ expect(radioButtons.emitted('on-radio-change')).toHaveLength(
169
+ initialNumberOfEvents
170
+ )
171
+
172
+ // Get RadioWrapper with info icon
173
+ const arrayOfWrappersWithInfoIcons = findRadioWrappersByCriteria([
174
+ 'active',
175
+ 'unchecked',
176
+ 'hasInfoIcon',
177
+ ])
178
+ const infoIconForClick = arrayOfWrappersWithInfoIcons[0].find(
179
+ '[data-test-id="infoText_trigger"]'
180
+ )
181
+ await infoIconForClick.trigger('click')
182
+ // Check if we still have the same number of emitted events as at the beginning
183
+ expect(radioButtons.emitted('on-radio-change')).toHaveLength(
184
+ initialNumberOfEvents
185
+ )
186
+
187
+ // Get RadioWrapper with image
188
+ const arrayOfWrappersWithImage = findRadioWrappersByCriteria([
189
+ 'active',
190
+ 'unchecked',
191
+ 'hasImage',
192
+ ])
193
+ const testedRadioWrapperWithImage = arrayOfWrappersWithImage[0]
194
+ const openImageWrapperTestId = testedRadioWrapperWithImage
195
+ .attributes('data-test-id')
196
+ .replace('radioWrapper_', 'radioOpenImage_')
197
+ const openImageWrapper = testedRadioWrapperWithImage.find(
198
+ `[data-test-id="${openImageWrapperTestId}"]`
199
+ )
200
+ await openImageWrapper.trigger('click')
201
+ // Check if we still have the same number of emitted events as at the beginning
202
+ expect(radioButtons.emitted('on-radio-change')).toHaveLength(
203
+ initialNumberOfEvents
204
+ )
205
+
206
+ // Since we just clicked on image miniature
207
+ // lets check has the corresponding modal been opened
208
+ // and is the correct image displayed
209
+ const imageModalWrapperTestId = testedRadioWrapperWithImage
210
+ .attributes('data-test-id')
211
+ .replace('radioWrapper_', 'radioModal_')
212
+ const imageModalWrapper = testedRadioWrapperWithImage.find(
213
+ `[data-test-id="${imageModalWrapperTestId}"]`
214
+ )
215
+ expect(imageModalWrapper.attributes('class')).toContain('visible')
216
+ const imageWrapperTestId = testedRadioWrapperWithImage
217
+ .attributes('data-test-id')
218
+ .replace('radioWrapper_', 'radioImage_')
219
+ const imageWrapper = testedRadioWrapperWithImage.find(
220
+ `[data-test-id="${imageWrapperTestId}"]`
221
+ )
222
+ const expectedImageSrc = imageWrapper.attributes('src')
223
+ const modalImageSrc = imageModalWrapper.find('img').attributes('src')
224
+ expect(modalImageSrc).toBe(expectedImageSrc)
225
+ //Close the modal
226
+ radioButtons.find('.visible .close').trigger('click')
227
+ await radioButtons.vm.$nextTick()
228
+ expect(imageModalWrapper.attributes('class')).toContain('hidden')
229
+ }),
230
+ it('test hover on Info Icon', async () => {
231
+ // Get RadioWrapper with Info Icon
232
+ const arrayOfWrappersWithInfoIcon = findRadioWrappersByCriteria([
233
+ 'hasInfoIcon',
234
+ ])
235
+ //Select tested item and get expected text within the info badge
236
+ const testedRadioWrapper =
237
+ arrayOfWrappersWithInfoIcon[arrayOfWrappersWithInfoIcon.length - 1]
238
+ const valueOfTestedRadioWrapper = testedRadioWrapper
239
+ .attributes('data-test-id')
240
+ .replace('radioWrapper_', '')
241
+ const expectedText = defaultRadioButtonProps.options.find((el) => el.value === valueOfTestedRadioWrapper).infoText
242
+ const iconForHover = testedRadioWrapper.find(
243
+ '[data-test-id="infoText_trigger"]'
244
+ )
245
+ await iconForHover.trigger('mouseenter')
246
+ expect(testedRadioWrapper.text()).toContain(expectedText)
247
+ }),
248
+ it('Test the click again after all the manipulations', async () => {
249
+ const uncheckedWrapperArray = findRadioWrappersByCriteria([
250
+ 'unchecked',
251
+ 'active',
252
+ ])
253
+ const expectedValueOfEmittedEvent = uncheckedWrapperArray[0]
254
+ .attributes('data-test-id')
255
+ .replace('radioWrapper_', '')
256
+ const uncheckedLabelWrapper = uncheckedWrapperArray[0].find('label')
257
+ await uncheckedLabelWrapper.trigger('click')
258
+ expect(radioButtons.emitted('on-radio-change')).toBeTruthy()
259
+ const emittedEvents = radioButtons.emitted('on-radio-change')
260
+ expect(emittedEvents).toHaveLength(2) // Check that this is just 2nd emitted event
261
+ // Check the payload of the event
262
+ expect(emittedEvents[emittedEvents.length - 1]).toEqual([
263
+ expectedValueOfEmittedEvent,
264
+ ])
265
+ await radioButtons.setProps({
266
+ selectedOption: expectedValueOfEmittedEvent,
267
+ })
268
+ })
269
+ })
@@ -1 +0,0 @@
1
- <svg xmlns="http://www.w3.org/2000/svg" width="29" height="29" fill="#333" viewBox="0 0 29 29"><path d="m10.5 14 4-8 4 8z"/><path class='fix' fill="#ccc" d="m10.5 16 4 8 4-8z"/></svg>
@@ -1,6 +0,0 @@
1
- <svg fill="#000000" height="800px" width="800px" version="1.1" id="Capa_1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink"
2
- viewBox="0 0 512 512" xml:space="preserve">
3
- <path d="M256,0C114.617,0,0,114.615,0,256s114.617,256,256,256s256-114.615,256-256S397.383,0,256,0z M224,320
4
- c0,8.836-7.164,16-16,16h-32c-8.836,0-16-7.164-16-16V192c0-8.836,7.164-16,16-16h32c8.836,0,16,7.164,16,16V320z M352,320
5
- c0,8.836-7.164,16-16,16h-32c-8.836,0-16-7.164-16-16V192c0-8.836,7.164-16,16-16h32c8.836,0,16,7.164,16,16V320z"/>
6
- </svg>