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

Sign up to get free protection for your applications and to get access to all the features.
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@eturnity/eturnity_reusable_components",
3
- "version": "7.39.4",
3
+ "version": "7.39.5-qa-elisee-7.42.1",
4
4
  "files": [
5
5
  "dist",
6
6
  "src"
@@ -0,0 +1 @@
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>
@@ -0,0 +1,6 @@
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>
@@ -35,6 +35,9 @@ const theme = {
35
35
  tablet: '768px',
36
36
  tabletLarge: '950px',
37
37
  },
38
+ fonts: {
39
+ mainFont: '"Figtree", sans-serif',
40
+ },
38
41
  borderRadius: '4px',
39
42
  }
40
43
 
@@ -111,10 +111,10 @@
111
111
  props.backgroundColor ? props.backgroundColor : 'transparent'};
112
112
  padding: ${(props) => (props.backgroundColor ? '3px' : '0')};
113
113
  }
114
- svg path {
114
+ svg path:not(.fix) {
115
115
  ${({ theme, color }) => color && `fill: ${theme.colors[color] || color};`}
116
116
  }
117
- &:hover svg path {
117
+ &:hover svg path:not(.fix) {
118
118
  ${({ theme, hoveredColor }) =>
119
119
  hoveredColor && `fill: ${theme.colors[hoveredColor] || hoveredColor};`}
120
120
  }
@@ -1,6 +1,10 @@
1
1
  <template>
2
- <InfoContainer>
3
- <RCIcon name="info" size="24px" :color="color" />
2
+ <InfoContainer
3
+ background-color="backgrounColor"
4
+ :border-color="borderColor"
5
+ :has-dashed-border="hasDashedBorder"
6
+ >
7
+ <RCIcon :color="color" name="info" size="24px" />
4
8
  <TextContainer>
5
9
  <slot></slot>
6
10
  </TextContainer>
@@ -10,12 +14,24 @@
10
14
  <script>
11
15
  import styled from 'vue3-styled-components'
12
16
  import RCIcon from '../icon'
13
-
14
- const InfoContainer = styled.div`
17
+ const propsContainer = {
18
+ backgroundColor: String,
19
+ hasDashedBorder: Boolean,
20
+ borderColor: String,
21
+ }
22
+ const InfoContainer = styled('div', propsContainer)`
15
23
  display: flex;
16
24
  gap: 15px;
17
- padding: 20px;
18
- border: 1px dashed ${(props) => props.theme.colors.grey4};
25
+ padding: 15px;
26
+ border: 1px ${(props) => (props.hasDashedBorder ? 'dashed' : 'solid')}
27
+ ${(props) =>
28
+ props.theme.colors[props.borderColor]
29
+ ? props.theme.colors[props.borderColor]
30
+ : props.borderColor};
31
+ background-color: ${(props) =>
32
+ props.theme.colors[props.backgroundColor]
33
+ ? props.theme.colors[props.backgroundColor]
34
+ : props.backgroundColor};
19
35
  border-radius: 4px;
20
36
  `
21
37
 
@@ -33,8 +49,19 @@
33
49
  },
34
50
  props: {
35
51
  color: {
36
- required: false
37
- }
38
- }
52
+ required: false,
53
+ },
54
+ backgrounColor: {
55
+ required: false,
56
+ },
57
+ hasDashedBorder: {
58
+ required: false,
59
+ default: true,
60
+ },
61
+ borderColor: {
62
+ required: false,
63
+ default: 'grey4',
64
+ },
65
+ },
39
66
  }
40
67
  </script>
@@ -18,10 +18,11 @@
18
18
  :align-arrow="alignArrow"
19
19
  :half-computed-text-info-width="halfComputedTextInfoWidth"
20
20
  :icon-size="size"
21
+ :info-position="infoPosition"
21
22
  :max-width="maxWidth"
22
23
  :width="width"
23
24
  ><slot></slot>
24
- <span v-html="text"></span>
25
+ <span v-if="text.length > 0" v-html="text"></span>
25
26
  </TextOverlay>
26
27
  </IconWrapper>
27
28
  </ComponentWrapper>
@@ -44,10 +45,14 @@
44
45
  alignArrow: String,
45
46
  width: String,
46
47
  halfComputedTextInfoWidth: Number,
48
+ infoPosition: String,
47
49
  }
48
50
  const TextOverlay = styled('div', textAttrs)`
49
51
  position: absolute;
50
- top: ${(props) => 'calc(' + props.iconSize + ' + 15px)'};
52
+ ${(props) =>
53
+ props.infoPosition == 'top'
54
+ ? 'bottom : calc(' + props.iconSize + ' + 15px)'
55
+ : 'top : calc(' + props.iconSize + ' + 15px)'};
51
56
  ${(props) =>
52
57
  props.alignArrow === 'left'
53
58
  ? 'left: calc(' + props.iconSize + ' /2 - 18px)'
@@ -63,14 +68,17 @@
63
68
  font-weight: 400;
64
69
  line-height: normal;
65
70
  border-radius: 4px;
66
- z-index: 99;
71
+ z-index: 999;
67
72
  color: ${(props) => props.theme.colors.white};
68
73
 
69
74
  :before {
70
75
  content: '';
71
76
  background-color: ${(props) => props.theme.colors.black};
72
77
  position: absolute;
73
- top: 2px;
78
+
79
+ ${(props) =>
80
+ props.infoPosition == 'top' ? 'bottom : -10px' : 'top: 2px'};
81
+
74
82
  ${(props) =>
75
83
  props.alignArrow === 'left'
76
84
  ? 'left:40px;'
@@ -114,26 +122,37 @@
114
122
  props: {
115
123
  text: {
116
124
  required: false,
125
+ default: '',
126
+ type: String,
117
127
  },
118
128
  size: {
119
129
  required: false,
120
130
  default: '14px',
131
+ type: String,
132
+ },
133
+ infoPosition: {
134
+ required: false,
135
+ default: 'bottom',
136
+ type: String,
121
137
  },
122
138
  alignArrow: {
123
139
  required: false,
124
140
  default: 'center',
141
+ type: String,
125
142
  },
126
143
  openTrigger: {
127
144
  required: false,
128
145
  default: 'onHover', // onHover, onClick
146
+ type: String,
129
147
  },
130
148
  width: {
131
149
  required: false,
132
150
  default: '200px',
151
+ type: String,
133
152
  },
134
153
  maxWidth: {
135
- type: String,
136
154
  default: '400px',
155
+ type: String,
137
156
  },
138
157
  },
139
158
  data() {
@@ -3,73 +3,68 @@ import InputText from './index.vue'
3
3
  export default {
4
4
  title: 'InputText',
5
5
  component: InputText,
6
- // argTypes: {},
6
+ tags: ['autodocs'],
7
7
  }
8
8
 
9
- const Template = (args, { argTypes }) => ({
10
- // Components used in your story `template` are defined in the `components` object
11
- components: { InputText },
12
- // The story's `args` need to be mapped into the template through the `setup()` method
13
- props: Object.keys(argTypes),
14
- template: '<input-text v-bind="$props" />',
9
+ // import InputText from "@eturnity/eturnity_reusable_components/src/components/inputs/inputText"
10
+ // To use:
11
+ // <input-text
12
+ // placeholder="Company name"
13
+ // :value="companyName"
14
+ // @input-change="onInputChange({ value: $event, type: 'companyName' })"
15
+ // :isError="checkErrors()"
16
+ // :errorMessage="This is my error message"
17
+ // infoTextAlign="right" // left by default
18
+ // infoTextMessage="My info message"
19
+ // label="Question 5"
20
+ // alignItems="horizontal" // horizontal, vertical
21
+ // inputWidth="250px"
22
+ // minWidth="100px"
23
+ // />
15
24
 
16
- // import InputText from "@eturnity/eturnity_reusable_components/src/components/inputs/inputText"
17
- // To use:
18
- // <input-text
19
- // placeholder="Company name"
20
- // :value="companyName"
21
- // @input-change="onInputChange({ value: $event, type: 'companyName' })"
22
- // :isError="checkErrors()"
23
- // :errorMessage="This is my error message"
24
- // infoTextAlign="right" // left by default
25
- // infoTextMessage="My info message"
26
- // label="Question 5"
27
- // alignItems="horizontal" // horizontal, vertical
28
- // inputWidth="250px"
29
- // minWidth="100px"
30
- // />
31
- })
32
-
33
- export const Default = Template.bind({})
34
- Default.args = {
35
- placeholder: 'Company name',
36
- disabled: false,
37
- value: '',
38
- inputWidth: '200px',
39
- minWidth: '10ch',
40
- unitName: 'pc',
41
- isError: false,
42
- textAlign: 'left',
25
+ export const Default = {
26
+ args: {
27
+ placeholder: 'Company name',
28
+ disabled: false,
29
+ value: '',
30
+ inputWidth: '200px',
31
+ minWidth: '10ch',
32
+ isError: false,
33
+ label: 'label test',
34
+ labelOptional: true,
35
+ },
43
36
  }
44
37
 
45
- export const hasError = Template.bind({})
46
- hasError.args = {
47
- placeholder: 'Enter Value',
48
- errorMessage: 'This field is required',
49
- isError: true,
50
- disabled: false,
51
- inputWidth: '200px',
38
+ export const HasError = {
39
+ args: {
40
+ placeholder: 'Enter Value',
41
+ errorMessage: 'This field is required',
42
+ isError: true,
43
+ disabled: false,
44
+ inputWidth: '200px',
45
+ },
52
46
  }
53
47
 
54
- export const Disabled = Template.bind({})
55
- Disabled.args = {
56
- placeholder: 'Enter Value',
57
- disabled: true,
58
- value: '',
59
- inputWidth: '200px',
60
- isError: false,
48
+ export const Disabled = {
49
+ args: {
50
+ placeholder: 'Enter Value',
51
+ disabled: true,
52
+ value: '',
53
+ inputWidth: '200px',
54
+ isError: false,
55
+ },
61
56
  }
62
57
 
63
- export const WithLabel = Template.bind({})
64
- WithLabel.args = {
65
- placeholder: 'Company name',
66
- disabled: false,
67
- label: 'What is the best company in Switzerland?',
68
- value: 'Eturnity',
69
- inputWidth: '200px',
70
- unitName: 'pc',
71
- isError: false,
72
- errorMessage: 'Maximum 5 characters',
73
- textAlign: 'left',
74
- alignItems: 'vertical',
58
+ export const WithLabel = {
59
+ args: {
60
+ placeholder: 'Company name',
61
+ disabled: false,
62
+ label: 'What is the best company in Switzerland?',
63
+ value: 'Eturnity',
64
+ inputWidth: '200px',
65
+ unitName: 'pc',
66
+ isError: false,
67
+ errorMessage: 'Maximum 5 characters',
68
+ alignItems: 'vertical',
69
+ },
75
70
  }
@@ -2,22 +2,26 @@
2
2
  <Container>
3
3
  <InputWrapper
4
4
  :align-items="alignItems"
5
+ data-test-id="input_wrapper"
5
6
  :has-label="!!label && label.length > 0"
6
7
  >
7
8
  <LabelWrapper v-if="label">
8
9
  <InputLabel
10
+ ref="label"
9
11
  :data-id="labelDataId"
12
+ data-test-id="label_wrapper"
10
13
  :font-size="fontSize"
11
14
  :label-font-color="labelFontColor"
12
15
  >
13
16
  {{ label }}
14
- <OptionalLabel v-if="labelOptional">
17
+ <OptionalLabel v-if="labelOptional" data-test-id="label_optional">
15
18
  ({{ $gettext('Optional') }})
16
19
  </OptionalLabel>
17
20
  </InputLabel>
18
21
  <InfoText
19
- v-if="infoTextMessage"
22
+ v-if="infoTextMessage != ''"
20
23
  :align-arrow="infoTextAlign"
24
+ data-test-id="info_text_message"
21
25
  :size="fontSize ? fontSize : '16px'"
22
26
  :text="infoTextMessage"
23
27
  />
@@ -28,6 +32,7 @@
28
32
  ref="inputElement"
29
33
  :background-color="backgroundColor"
30
34
  :data-id="inputDataId"
35
+ data-test-id="input"
31
36
  :disabled="disabled"
32
37
  :disabled-background-color="disabledBackgroundColor"
33
38
  :font-color="fontColor"
@@ -44,21 +49,26 @@
44
49
  :type="inputTypeData"
45
50
  :value="value"
46
51
  @blur="onInputBlur"
52
+ @focus="onInputFocus"
47
53
  @input="onChangeHandler"
48
54
  @keyup.enter="onEnterPress"
49
55
  />
50
56
  <IconWrapper
51
57
  v-if="inputType === 'password' && !isError"
58
+ data-test-id="password_visiblity_change_icon"
52
59
  size="20px"
53
60
  @click="toggleShowPassword()"
54
61
  >
55
62
  <Icon name="current_variant" size="20px" />
56
63
  </IconWrapper>
57
- <IconWrapper v-if="hasError" size="16px">
64
+ <IconWrapper v-if="hasError" data-test-id="error_wrapper" size="16px">
58
65
  <Icon cursor="default" name="warning" size="16px" />
59
66
  </IconWrapper>
60
67
  </IconContainer>
61
- <ErrorMessage v-if="hasError && hasErrorMessage">
68
+ <ErrorMessage
69
+ v-if="hasError && hasErrorMessage"
70
+ data-test-id="error_message_wrapper"
71
+ >
62
72
  {{ dynamicErrorMessage }}
63
73
  </ErrorMessage>
64
74
  </InputErrorWrapper>
@@ -236,93 +246,123 @@
236
246
  placeholder: {
237
247
  required: false,
238
248
  default: '',
249
+ type: String,
239
250
  },
240
251
  alignItems: {
241
252
  required: false,
242
253
  default: 'horizontal',
254
+ type: String,
255
+ validator(value) {
256
+ return ['horizontal', 'vertical'].includes(value)
257
+ },
243
258
  },
244
259
  isError: {
245
260
  required: false,
246
261
  default: false,
262
+ type: Boolean,
247
263
  },
248
264
  inputWidth: {
249
265
  required: false,
250
266
  default: null,
267
+ type: String,
251
268
  },
252
269
  inputHeight: {
253
270
  required: false,
254
271
  default: null,
272
+ type: String,
255
273
  },
256
274
  minWidth: {
257
275
  required: false,
258
276
  default: null,
277
+ type: String,
259
278
  },
260
279
  value: {
261
280
  required: true,
262
281
  default: null,
282
+ type: String,
263
283
  },
264
284
  errorMessage: {
265
285
  required: false,
266
286
  default: '',
287
+ type: String,
267
288
  },
268
289
  infoTextMessage: {
269
290
  required: false,
291
+ default: '',
292
+ type: String,
270
293
  },
271
294
  infoTextAlign: {
272
295
  required: false,
296
+ default: 'left',
297
+ type: String,
273
298
  },
274
299
  label: {
275
300
  required: false,
301
+ default: '',
302
+ type: String,
276
303
  },
277
304
  labelOptional: {
278
305
  required: false,
279
306
  default: false,
307
+ type: Boolean,
280
308
  },
281
309
  noBorder: {
282
310
  required: false,
283
311
  default: false,
312
+ type: Boolean,
284
313
  },
285
314
  disabled: {
286
315
  required: false,
287
316
  default: false,
317
+ type: Boolean,
288
318
  },
289
319
  fontSize: {
290
320
  required: false,
291
321
  default: null,
322
+ type: String,
292
323
  },
293
324
  inputType: {
294
325
  required: false,
295
326
  default: 'text',
327
+ type: String,
296
328
  },
297
329
  labelFontColor: {
298
330
  required: false,
299
331
  default: 'black',
332
+ type: String,
300
333
  },
301
334
  backgroundColor: {
302
335
  required: false,
336
+ type: String,
303
337
  },
304
338
  disabledBackgroundColor: {
305
339
  required: false,
306
340
  default: null,
341
+ type: String,
307
342
  },
308
343
  fontColor: {
309
344
  required: false,
310
345
  default: 'black',
346
+ type: String,
311
347
  },
312
348
  hasFocus: {
313
349
  required: false,
314
350
  default: false,
351
+ type: Boolean,
315
352
  },
316
353
  borderColor: {
317
354
  required: false,
355
+ type: String,
318
356
  },
319
357
  labelDataId: {
320
358
  required: false,
321
359
  default: '',
360
+ type: String,
322
361
  },
323
362
  inputDataId: {
324
363
  required: false,
325
364
  default: '',
365
+ type: String,
326
366
  },
327
367
  },
328
368
  data() {
@@ -335,12 +375,10 @@
335
375
  return this.isError || this.error
336
376
  },
337
377
  hasErrorMessage() {
338
- return (
339
- (this.errorMessage && this.errorMessage.length > 0) || this.errMessage
340
- )
378
+ return this.errorMessage && this.errorMessage.length > 0
341
379
  },
342
380
  dynamicErrorMessage() {
343
- return this.errMessage || this.errorMessage
381
+ return this.errorMessage
344
382
  },
345
383
  },
346
384
  watch: {
@@ -367,6 +405,9 @@
367
405
  this.validateInput($event.target.value)
368
406
  this.$emit('input-blur', $event.target.value)
369
407
  },
408
+ onInputFocus($event) {
409
+ this.$emit('input-focus', $event.target.value)
410
+ },
370
411
  toggleShowPassword() {
371
412
  this.inputTypeData =
372
413
  this.inputTypeData === 'password' ? 'text' : 'password'
@@ -0,0 +1,588 @@
1
+ /* eslint-disable */
2
+ import { mount, Wrapper } from '@vue/test-utils'
3
+ import RCInputText from '@/components/inputs/inputText/index'
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
+
11
+ const wrapper1 = mount(RCInputText, {
12
+ props: {
13
+ placeholder: 'placeholderText',
14
+ value: 'Test value',
15
+ label: 'Test label',
16
+ },
17
+ global: {
18
+ provide: {
19
+ theme,
20
+ },
21
+ },
22
+ })
23
+ const wrapperPassword = mount(RCInputText, {
24
+ props: {
25
+ placeholder: 'placeholderText',
26
+ value: 'Test value',
27
+ label: 'Test label',
28
+ inputType: 'password',
29
+ },
30
+ global: {
31
+ provide: {
32
+ theme,
33
+ },
34
+ },
35
+ })
36
+ describe('RCInputText.vue', () => {
37
+ let consoleWarnSpy
38
+ beforeEach(() => {
39
+ consoleWarnSpy = jest.spyOn(console, 'warn').mockImplementation(() => {})
40
+ })
41
+ afterEach(() => {
42
+ consoleWarnSpy.mockRestore()
43
+ })
44
+ // initial rendering
45
+ it('input is rendered', async () => {
46
+ const inputTextWrapper = wrapper1.find('[data-test-id="input_wrapper"]')
47
+ expect(inputTextWrapper.exists()).toBe(true)
48
+ })
49
+ // event management
50
+ it('input is rendered and emits inputChange when input event is triggered.', async () => {
51
+ const inputElement = wrapper1.find('input')
52
+ await inputElement.setValue('Test Input Value')
53
+ const emittedInputChangeEvent = wrapper1.emitted('input-change')
54
+ expect(emittedInputChangeEvent).toBeTruthy()
55
+ expect(emittedInputChangeEvent).toHaveLength(1) // Check if the event was emitted exactly once
56
+ expect(emittedInputChangeEvent[0]).toEqual(['Test Input Value'])
57
+ })
58
+ it('input emits on-enter-click', async () => {
59
+ const inputElement = wrapper1.find('input')
60
+ await inputElement.trigger('keyup.enter')
61
+ const emittedOnEnterClickEvent = wrapper1.emitted('on-enter-click')
62
+ expect(emittedOnEnterClickEvent).toBeTruthy()
63
+ expect(emittedOnEnterClickEvent).toHaveLength(1) // Check if the event was emitted exactly once
64
+ expect(emittedOnEnterClickEvent[0]).toEqual([]) // Check if the event was emitted exactly once
65
+ })
66
+ it('input emits on-blur', async () => {
67
+ const inputElement = wrapper1.find('input')
68
+ await inputElement.setValue('Test Input Value')
69
+ await inputElement.trigger('blur')
70
+ const emittedOnBlurEvent = wrapper1.emitted('input-blur')
71
+ expect(emittedOnBlurEvent).toBeTruthy()
72
+ expect(emittedOnBlurEvent).toHaveLength(1) // Check if the event was emitted exactly once
73
+ expect(emittedOnBlurEvent[0]).toEqual(['Test Input Value']) // Check if the event was emitted exactly once
74
+ })
75
+ it('input password visibility click trigger input type change', async () => {
76
+ const inputElement = wrapperPassword.find('input')
77
+ const passwordVisibilityIconElement = wrapperPassword.find(
78
+ '[data-test-id="password_visiblity_change_icon"]'
79
+ )
80
+ expect(passwordVisibilityIconElement.exists()).toBe(true)
81
+ await passwordVisibilityIconElement.trigger('click')
82
+ let inputType = inputElement.attributes().type
83
+ expect(inputType).toBe('text')
84
+ await passwordVisibilityIconElement.trigger('click')
85
+ inputType = inputElement.attributes().type
86
+ expect(inputType).toBe('password')
87
+ await passwordVisibilityIconElement.trigger('click')
88
+ inputType = inputElement.attributes().type
89
+ expect(inputType).toBe('text')
90
+ })
91
+ //Test conditional visibility
92
+ it('label visibility check', async () => {
93
+ const wrapper = mount(RCInputText, {
94
+ props: {
95
+ value: 'Test value',
96
+ label: 'Test label',
97
+ },
98
+ global: {
99
+ provide: {
100
+ theme,
101
+ },
102
+ },
103
+ })
104
+ const label = wrapper.find('[data-test-id="label_wrapper"]')
105
+ expect(label.exists()).toBe(true)
106
+ expect(label.text()).toContain('Test label')
107
+ })
108
+ it('label invisibility check', async () => {
109
+ const wrapper = mount(RCInputText, {
110
+ props: {
111
+ value: 'Test value',
112
+ },
113
+ global: {
114
+ provide: {
115
+ theme,
116
+ },
117
+ },
118
+ })
119
+ const label = wrapper.find('[data-test-id="label_wrapper"]')
120
+ expect(label.exists()).toBe(false)
121
+ })
122
+ it('label optional visibility check', async () => {
123
+ const wrapper = mount(RCInputText, {
124
+ props: {
125
+ value: 'Test value',
126
+ label: 'Test label',
127
+ labelOptional: true,
128
+ },
129
+ global: {
130
+ provide: {
131
+ theme,
132
+ },
133
+ },
134
+ })
135
+ const labelOptional = wrapper.find('[data-test-id="label_optional"]')
136
+ expect(labelOptional.exists()).toBe(true)
137
+ })
138
+ it('label optional invisibility check', async () => {
139
+ const wrapper = mount(RCInputText, {
140
+ props: {
141
+ value: 'Test value',
142
+ },
143
+ global: {
144
+ provide: {
145
+ theme,
146
+ },
147
+ },
148
+ })
149
+ const labelOptional = wrapper.find('[data-test-id="label_optional"]')
150
+ expect(labelOptional.exists()).toBe(false)
151
+ })
152
+ it('infoTextMessage visibility check', async () => {
153
+ const wrapper = mount(RCInputText, {
154
+ props: {
155
+ value: 'Test value',
156
+ infoTextMessage: 'infoTextMessage test',
157
+ label: 'Test label',
158
+ },
159
+ global: {
160
+ provide: {
161
+ theme,
162
+ },
163
+ },
164
+ })
165
+ const InfoTextMessage = wrapper.find('[data-test-id="info_text_message"]')
166
+ expect(InfoTextMessage.exists()).toBe(true)
167
+ })
168
+ it('infoTextMessage invibility check', async () => {
169
+ const wrapper = mount(RCInputText, {
170
+ props: {
171
+ value: 'Test value',
172
+ label: 'Test label',
173
+ },
174
+ global: {
175
+ provide: {
176
+ theme,
177
+ },
178
+ },
179
+ })
180
+ const InfoTextMessage = wrapper.find('[data-test-id="info_text_message"]')
181
+ expect(InfoTextMessage.exists()).toBe(false)
182
+ })
183
+ it('error visibility check', async () => {
184
+ const wrapper = mount(RCInputText, {
185
+ props: {
186
+ value: 'Test value',
187
+ isError: true,
188
+ errorMessage: 'Error message test',
189
+ label: 'Test label',
190
+ },
191
+ global: {
192
+ provide: {
193
+ theme,
194
+ },
195
+ },
196
+ })
197
+ const errorWrapper = wrapper.find('[data-test-id="error_wrapper"]')
198
+ const errorMessageWrapper = wrapper.find(
199
+ '[data-test-id="error_message_wrapper"]'
200
+ )
201
+ expect(errorWrapper.exists()).toBe(true)
202
+ expect(errorMessageWrapper.exists()).toBe(true)
203
+ expect(errorMessageWrapper.text()).toContain('Error message test')
204
+ })
205
+ it('error invibility check', async () => {
206
+ const wrapper = mount(RCInputText, {
207
+ props: {
208
+ value: 'Test value',
209
+ label: 'Test label',
210
+ },
211
+ global: {
212
+ provide: {
213
+ theme,
214
+ },
215
+ },
216
+ })
217
+ const errorWrapper = wrapper.find('[data-test-id="error_wrapper"]')
218
+ const errorMessageWrapper = wrapper.find(
219
+ '[data-test-id="error_message_wrapper"]'
220
+ )
221
+ expect(errorWrapper.exists()).toBe(false)
222
+ expect(errorMessageWrapper.exists()).toBe(false)
223
+ })
224
+ //Test that the component handles different prop types correctly and throws appropriate warnings for invalid props.
225
+ it('validation of prop type - placeholder - not String', async () => {
226
+ mount(RCInputText, {
227
+ props: {
228
+ placeholder: 42,
229
+ value: 'value test',
230
+ },
231
+ global: {
232
+ provide: {
233
+ theme,
234
+ },
235
+ },
236
+ })
237
+ expect(consoleWarnSpy).toHaveBeenCalled()
238
+ })
239
+ it('validation of prop type - alignItems - not horizontal or vertical', async () => {
240
+ mount(RCInputText, {
241
+ props: {
242
+ value: 'value test',
243
+ alignItems: 'top',
244
+ },
245
+ global: {
246
+ provide: {
247
+ theme,
248
+ },
249
+ },
250
+ })
251
+ expect(consoleWarnSpy).toHaveBeenCalled()
252
+ })
253
+ it('validation of prop type - isError - not Boolean', async () => {
254
+ mount(RCInputText, {
255
+ props: {
256
+ value: 'value test',
257
+ isError: 'true',
258
+ },
259
+ global: {
260
+ provide: {
261
+ theme,
262
+ },
263
+ },
264
+ })
265
+ expect(consoleWarnSpy).toHaveBeenCalled()
266
+ })
267
+ it('validation of prop type - inputWidth - not String', async () => {
268
+ mount(RCInputText, {
269
+ props: {
270
+ value: 'value test',
271
+ inputWidth: 200,
272
+ },
273
+ global: {
274
+ provide: {
275
+ theme,
276
+ },
277
+ },
278
+ })
279
+ expect(consoleWarnSpy).toHaveBeenCalled()
280
+ })
281
+ it('validation of prop type - inputHeight - not String', async () => {
282
+ mount(RCInputText, {
283
+ props: {
284
+ value: 'value test',
285
+ inputHeight: 200,
286
+ },
287
+ global: {
288
+ provide: {
289
+ theme,
290
+ },
291
+ },
292
+ })
293
+ expect(consoleWarnSpy).toHaveBeenCalled()
294
+ })
295
+ it('validation of prop type - minWidth - not String', async () => {
296
+ mount(RCInputText, {
297
+ props: {
298
+ value: 'value test',
299
+ minWidth: 200,
300
+ },
301
+ global: {
302
+ provide: {
303
+ theme,
304
+ },
305
+ },
306
+ })
307
+ expect(consoleWarnSpy).toHaveBeenCalled()
308
+ })
309
+ it('validation of prop type - value - not String', async () => {
310
+ mount(RCInputText, {
311
+ props: {
312
+ value: 42,
313
+ },
314
+ global: {
315
+ provide: {
316
+ theme,
317
+ },
318
+ },
319
+ })
320
+ expect(consoleWarnSpy).toHaveBeenCalled()
321
+ })
322
+ it('validation of prop type - errorMessage - not String', async () => {
323
+ mount(RCInputText, {
324
+ props: {
325
+ value: 'test value',
326
+ errorMessage: true,
327
+ },
328
+ global: {
329
+ provide: {
330
+ theme,
331
+ },
332
+ },
333
+ })
334
+ expect(consoleWarnSpy).toHaveBeenCalled()
335
+ })
336
+ it('validation of prop type - infoTextMessage - not String', async () => {
337
+ mount(RCInputText, {
338
+ props: {
339
+ value: 'test value',
340
+ infoTextMessage: true,
341
+ },
342
+ global: {
343
+ provide: {
344
+ theme,
345
+ },
346
+ },
347
+ })
348
+ expect(consoleWarnSpy).toHaveBeenCalled()
349
+ })
350
+ it('validation of prop type - infoTextAlign - not String', async () => {
351
+ mount(RCInputText, {
352
+ props: {
353
+ value: 'test value',
354
+ infoTextAlign: true,
355
+ },
356
+ global: {
357
+ provide: {
358
+ theme,
359
+ },
360
+ },
361
+ })
362
+ expect(consoleWarnSpy).toHaveBeenCalled()
363
+ })
364
+ it('validation of prop type - label - not String', async () => {
365
+ mount(RCInputText, {
366
+ props: {
367
+ value: 'test value',
368
+ label: true,
369
+ },
370
+ global: {
371
+ provide: {
372
+ theme,
373
+ },
374
+ },
375
+ })
376
+ expect(consoleWarnSpy).toHaveBeenCalled()
377
+ })
378
+ it('validation of prop type - labelOptional - not Boolean', async () => {
379
+ const wrapper = mount(RCInputText, {
380
+ props: {
381
+ value: 'test value',
382
+ labelOptional: 12,
383
+ label: 'label test',
384
+ },
385
+ global: {
386
+ provide: {
387
+ theme,
388
+ },
389
+ },
390
+ })
391
+ wrapper.vm.$gettext.mockReturnValue('Translated Message')
392
+ expect(consoleWarnSpy).toHaveBeenCalled()
393
+ })
394
+ it('validation of prop type - noBorder - not Boolean', async () => {
395
+ mount(RCInputText, {
396
+ props: {
397
+ value: 'test value',
398
+ noBorder: 1,
399
+ },
400
+ global: {
401
+ provide: {
402
+ theme,
403
+ },
404
+ },
405
+ })
406
+ expect(consoleWarnSpy).toHaveBeenCalled()
407
+ })
408
+ it('validation of prop type - disabled - not Boolean', async () => {
409
+ mount(RCInputText, {
410
+ props: {
411
+ disabled: 'false',
412
+ value: 'value test',
413
+ },
414
+ global: {
415
+ provide: {
416
+ theme,
417
+ },
418
+ },
419
+ })
420
+ expect(consoleWarnSpy).toHaveBeenCalled()
421
+ })
422
+ it('validation of prop type - fontSize - not String', async () => {
423
+ mount(RCInputText, {
424
+ props: {
425
+ fontSize: true,
426
+ value: 'value test',
427
+ },
428
+ global: {
429
+ provide: {
430
+ theme,
431
+ },
432
+ },
433
+ })
434
+ expect(consoleWarnSpy).toHaveBeenCalled()
435
+ })
436
+ it('validation of prop type - inputType - not String', async () => {
437
+ mount(RCInputText, {
438
+ props: {
439
+ inputType: true,
440
+ value: 'value test',
441
+ },
442
+ global: {
443
+ provide: {
444
+ theme,
445
+ },
446
+ },
447
+ })
448
+ expect(consoleWarnSpy).toHaveBeenCalled()
449
+ })
450
+ it('validation of prop type - labelFontColor - not String', async () => {
451
+ mount(RCInputText, {
452
+ props: {
453
+ labelFontColor: true,
454
+ value: 'value test',
455
+ label: 'label text',
456
+ },
457
+ global: {
458
+ provide: {
459
+ theme,
460
+ },
461
+ },
462
+ })
463
+ expect(consoleWarnSpy).toHaveBeenCalled()
464
+ })
465
+ it('validation of prop type - backgroundColor - not String', async () => {
466
+ mount(RCInputText, {
467
+ props: {
468
+ backgroundColor: true,
469
+ value: 'value test',
470
+ },
471
+ global: {
472
+ provide: {
473
+ theme,
474
+ },
475
+ },
476
+ })
477
+ expect(consoleWarnSpy).toHaveBeenCalled()
478
+ })
479
+ it('validation of prop type - disabledBackgroundColor - not String', async () => {
480
+ mount(RCInputText, {
481
+ props: {
482
+ disabledBackgroundColor: true,
483
+ value: 'value test',
484
+ },
485
+ global: {
486
+ provide: {
487
+ theme,
488
+ },
489
+ },
490
+ })
491
+ expect(consoleWarnSpy).toHaveBeenCalled()
492
+ })
493
+ it('validation of prop type - fontColor - not String', async () => {
494
+ mount(RCInputText, {
495
+ props: {
496
+ fontColor: true,
497
+ value: 'value test',
498
+ },
499
+ global: {
500
+ provide: {
501
+ theme,
502
+ },
503
+ },
504
+ })
505
+ expect(consoleWarnSpy).toHaveBeenCalled()
506
+ })
507
+ it('validation of prop type - hasFocus - not Boolean', async () => {
508
+ mount(RCInputText, {
509
+ props: {
510
+ hasFocus: 'true',
511
+ value: 'value test',
512
+ },
513
+ global: {
514
+ provide: {
515
+ theme,
516
+ },
517
+ },
518
+ })
519
+ expect(consoleWarnSpy).toHaveBeenCalled()
520
+ })
521
+ it('validation of prop type - borderColor - not String', async () => {
522
+ mount(RCInputText, {
523
+ props: {
524
+ borderColor: 42,
525
+ value: 'value test',
526
+ },
527
+ global: {
528
+ provide: {
529
+ theme,
530
+ },
531
+ },
532
+ })
533
+ expect(consoleWarnSpy).toHaveBeenCalled()
534
+ })
535
+ it('validation of prop type - labelDataId - not String', async () => {
536
+ mount(RCInputText, {
537
+ props: {
538
+ labelDataId: 42,
539
+ value: 'value test',
540
+ },
541
+ global: {
542
+ provide: {
543
+ theme,
544
+ },
545
+ },
546
+ })
547
+ expect(consoleWarnSpy).toHaveBeenCalled()
548
+ })
549
+ it('validation of prop type - inputDataId - not String', async () => {
550
+ mount(RCInputText, {
551
+ props: {
552
+ inputDataId: 42,
553
+ value: 'value test',
554
+ },
555
+ global: {
556
+ provide: {
557
+ theme,
558
+ },
559
+ },
560
+ })
561
+ expect(consoleWarnSpy).toHaveBeenCalled()
562
+ })
563
+ //Prop Updates: Verify that the component updates correctly when props change, reflecting new values in the rendered output.
564
+ it('update of prop hasFocus', async () => {
565
+ const wrapper = mount(RCInputText, {
566
+ props: {
567
+ value: 'value test',
568
+ hasFocus: false,
569
+ },
570
+ global: {
571
+ provide: {
572
+ theme,
573
+ },
574
+ },
575
+ })
576
+ const inputElement = wrapper.find('input').element
577
+ jest.spyOn(inputElement, 'focus').mockImplementation(() => {
578
+ // Simulate that the input element is now the focused element
579
+ Object.defineProperty(document, 'activeElement', {
580
+ value: inputElement,
581
+ configurable: true,
582
+ })
583
+ })
584
+ await wrapper.setProps({ hasFocus: true })
585
+ await wrapper.vm.$nextTick()
586
+ expect(document.activeElement).toBe(wrapper.find('input').element)
587
+ })
588
+ })
@@ -0,0 +1,107 @@
1
+ <template>
2
+ <ContainerComponent>
3
+ <RoundedTabLeft
4
+ :background-color="backgroundColor"
5
+ :border-radius="borderRadius"
6
+ />
7
+ <TabComponent
8
+ :background-color="backgroundColor"
9
+ :border-radius="borderRadius"
10
+ :height="height"
11
+ :width="width"
12
+ >
13
+ <slot></slot>
14
+ </TabComponent>
15
+ <RoundedTabRight
16
+ :background-color="backgroundColor"
17
+ :border-radius="borderRadius"
18
+ />
19
+ </ContainerComponent>
20
+ </template>
21
+ <script>
22
+ import styled from 'vue3-styled-components'
23
+
24
+ const ContainerComponent = styled.div`
25
+ position: relative;
26
+ display: flex;
27
+ align-items: center;
28
+ justify-content: center;
29
+ `
30
+ const RoundedTabLeft = styled('div', {
31
+ backgroundColor: String,
32
+ borderRadius: String,
33
+ })`
34
+ position:absolute;
35
+ bottom:0;
36
+ left:-${(prop) => prop.borderRadius}
37
+ background-color: ${(prop) => prop.backgroundColor};
38
+ width: ${(prop) => prop.borderRadius};
39
+ height: ${(prop) => prop.borderRadius};
40
+ -webkit-mask-image: radial-gradient(
41
+ circle at top left,
42
+ transparent 71%,
43
+ black 71%
44
+ );
45
+ `
46
+ const RoundedTabRight = styled('div', {
47
+ backgroundColor: String,
48
+ borderRadius: String,
49
+ })`
50
+ position:absolute;
51
+ bottom:0;
52
+ right:-${(prop) => prop.borderRadius}
53
+ background-color: ${(prop) => prop.backgroundColor};
54
+ width: ${(prop) => prop.borderRadius};
55
+ height: ${(prop) => prop.borderRadius};
56
+ -webkit-mask-image: radial-gradient(
57
+ circle at top right,
58
+ transparent 71%,
59
+ black 71%
60
+ );`
61
+ const TabComponent = styled('div', {
62
+ width: String,
63
+ height: String,
64
+ backgroundColor: String,
65
+ borderRadius: String,
66
+ })`
67
+ display: flex;
68
+ align-items: center;
69
+ justify-content: center;
70
+ background-color: ${(prop) => prop.backgroundColor};
71
+ width: ${(prop) => prop.width};
72
+ height: ${(prop) => prop.height};
73
+ border-radius: ${(prop) => prop.borderRadius} ${(prop) => prop.borderRadius}
74
+ 0 0;
75
+ `
76
+ export default {
77
+ name: 'RoundTabs',
78
+ components: {
79
+ ContainerComponent,
80
+ TabComponent,
81
+ RoundedTabLeft,
82
+ RoundedTabRight,
83
+ },
84
+ props: {
85
+ height: {
86
+ required: false,
87
+ default: '40px',
88
+ type: String,
89
+ },
90
+ width: {
91
+ required: false,
92
+ default: '200px',
93
+ type: String,
94
+ },
95
+ backgroundColor: {
96
+ required: false,
97
+ default: 'white',
98
+ type: String,
99
+ },
100
+ borderRadius: {
101
+ required: false,
102
+ default: '20px',
103
+ type: String,
104
+ },
105
+ },
106
+ }
107
+ </script>