@eturnity/eturnity_reusable_components 7.45.5-qa-elisee-7.48.0 → 7.45.5-qa-16-11.13.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/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@eturnity/eturnity_reusable_components",
3
- "version": "7.45.5-qa-elisee-7.48.0",
3
+ "version": "7.45.5-qa-16-11.13.1",
4
4
  "files": [
5
5
  "dist",
6
6
  "src"
@@ -1,24 +1,14 @@
1
1
  <template>
2
- <Wrapper
3
- :cursor="cursor"
4
- data-test-id="icon_wrapper"
5
- :disabled="disabled"
6
- :size="size"
7
- >
2
+ <Wrapper :cursor="cursor" :disabled="disabled" :size="size">
8
3
  <IconImage
9
4
  :animation="animation"
10
5
  :background-color="backgroundColor"
11
6
  :color="color"
12
- data-test-id="icon_image"
13
7
  :hovered-color="hoveredColor"
14
8
  >
15
- <i data-test-id="icon_svg" v-html="icon.html"></i>
9
+ <i v-html="icon.html"></i>
16
10
  </IconImage>
17
- <StrikedLine
18
- v-if="isStriked"
19
- :color="color"
20
- data-test-id="icon_striked_line"
21
- />
11
+ <StrikedLine v-if="isStriked" :color="color" />
22
12
  </Wrapper>
23
13
  </template>
24
14
 
@@ -47,7 +47,7 @@
47
47
  :show-linear-unit-name="showLinearUnitName"
48
48
  :slot-size="slotSize"
49
49
  :text-align="textAlign"
50
- :value="formatWithCurrency(value)"
50
+ :value="formattedValue"
51
51
  @blur="onInputBlur($event)"
52
52
  @focus="focusInput()"
53
53
  @input="onInput($event)"
@@ -59,7 +59,7 @@
59
59
 
60
60
  <UnitContainer
61
61
  v-if="unitName && showLinearUnitName && !hasSlot"
62
- :has-length="!!textInput.length"
62
+ :has-length="hasLength"
63
63
  :is-error="isError"
64
64
  >{{ unitName }}</UnitContainer
65
65
  >
@@ -76,7 +76,7 @@
76
76
  :disabled="isSelectDisabled"
77
77
  :select-width="`${selectWidth}px`"
78
78
  :show-border="false"
79
- @input-change="$emit('select-change', $event)"
79
+ @input-change="handleSelectChange"
80
80
  >
81
81
  <template #selector>
82
82
  <SelectText>{{ getSelectValue }}</SelectText>
@@ -339,8 +339,18 @@
339
339
  background-color: ${({ theme }) => theme.colors.grey4};
340
340
  `
341
341
 
342
+ const EVENT_TYPES = {
343
+ INPUT_FOCUS: 'input-focus',
344
+ INPUT_CHANGE: 'input-change',
345
+ INPUT_BLUR: 'input-blur',
346
+ PRESS_ENTER: 'on-enter-click',
347
+ INPUT_DRAG: 'on-input-drag',
348
+ SELECT_CHANGE: 'select-change',
349
+ }
350
+
342
351
  export default {
343
352
  name: 'InputNumber',
353
+ emits: [...Object.values(EVENT_TYPES)],
344
354
  components: {
345
355
  Container,
346
356
  InputContainer,
@@ -519,9 +529,10 @@
519
529
  },
520
530
  data() {
521
531
  return {
522
- textInput: '',
523
532
  isFocused: false,
524
533
  warningIcon: warningIcon,
534
+ inputValue: null,
535
+ enteredValue: null,
525
536
  }
526
537
  },
527
538
  computed: {
@@ -544,6 +555,14 @@
544
555
 
545
556
  return item ? item.label : '-'
546
557
  },
558
+ formattedValue() {
559
+ return this.formatWithCurrency(
560
+ this.isFocused ? this.enteredValue : this.inputValue
561
+ )
562
+ },
563
+ hasLength() {
564
+ return this.formattedValue !== null && this.formattedValue.length > 0
565
+ },
547
566
  },
548
567
  watch: {
549
568
  focus(value) {
@@ -554,30 +573,19 @@
554
573
  clearInput: function (value) {
555
574
  if (value) {
556
575
  // If the value is typed, then we should clear the textInput on Continue
557
- this.textInput = ''
576
+ this.inputValue = ''
577
+ this.enteredValue = ''
558
578
  }
559
579
  },
560
- },
561
- created() {
562
- if (this.value) {
563
- this.textInput = numberToString({
564
- value: this.value,
565
- numberPrecision: this.numberPrecision,
566
- minDecimals: this.minDecimals,
567
- })
568
- } else if (this.defaultNumber !== null) {
569
- this.textInput = numberToString({
570
- value: this.defaultNumber,
571
- numberPrecision: this.numberPrecision,
572
- minDecimals: this.minDecimals,
573
- })
574
- } else if (this.minNumber !== null) {
575
- this.textInput = numberToString({
576
- value: this.minNumber,
577
- numberPrecision: this.numberPrecision,
578
- minDecimals: this.minDecimals,
579
- })
580
- }
580
+ value: {
581
+ immediate: true,
582
+ handler(val) {
583
+ if (this.value !== this.inputValue && !Number.isNaN(this.value)) {
584
+ this.inputValue = this.value
585
+ this.enteredValue = this.value
586
+ }
587
+ },
588
+ },
581
589
  },
582
590
  mounted() {
583
591
  if (this.focus) {
@@ -586,29 +594,28 @@
586
594
  },
587
595
  methods: {
588
596
  onEnterPress() {
589
- this.$emit('on-enter-click')
597
+ this.$emit(EVENT_TYPES.PRESS_ENTER)
590
598
  this.$refs.inputField1.$el.blur()
591
599
  },
592
- onChangeHandler(event) {
593
- if (isNaN(event) || event === '') {
594
- event = this.defaultNumber
600
+ onChangeHandler(value) {
601
+ if (isNaN(value) || value === '') {
602
+ value = this.defaultNumber
595
603
  ? this.defaultNumber
596
604
  : this.minNumber || this.minNumber === 0
597
605
  ? this.minNumber
598
- : event
606
+ : value
599
607
  }
600
608
  if (!this.allowNegative) {
601
- event = Math.abs(event)
609
+ value = Math.abs(value)
602
610
  }
603
- event = parseFloat(event)
611
+ value = parseFloat(value)
604
612
  // Need to return an integer rather than a string
605
- this.$emit('input-change', event)
613
+ return parseFloat(value)
606
614
  },
607
- onEvaluateCode(event) {
615
+ onEvaluateCode(value) {
608
616
  // function to perform math on the code
609
617
  // filter the string in case of any malicious content
610
- const val = event.target.value
611
- let filtered = val.replace('(auto)', '').replace(/[^-()\d/*+.,]/g, '')
618
+ let filtered = value.replace('(auto)', '').replace(/[^-()\d/*+.,]/g, '')
612
619
  filtered = filtered.split(/([-+*/()])/)
613
620
  let formatted = filtered.map((item) => {
614
621
  if (
@@ -666,47 +673,28 @@
666
673
  return array
667
674
  },
668
675
  onInput(event) {
669
- if (!this.isFocused) {
676
+ this.enteredValue = event.target.value
677
+ if (!this.isFocused || this.enteredValue === this.inputValue) {
670
678
  return
671
679
  }
672
- if (event.target.value === '') {
673
- this.$emit('on-input', '')
674
- }
675
680
  let evaluatedVal
676
681
  try {
677
- evaluatedVal = this.onEvaluateCode(event)
682
+ evaluatedVal = this.onEvaluateCode(String(this.enteredValue))
678
683
  } finally {
679
- if (evaluatedVal && this.value != evaluatedVal) {
680
- this.$emit('on-input', evaluatedVal)
684
+ this.inputValue = this.onChangeHandler(evaluatedVal)
685
+
686
+ if (this.isFocused && typeof this.enteredValue !== 'number') {
687
+ this.$emit(EVENT_TYPES.INPUT_CHANGE, this.inputValue)
681
688
  }
682
689
  }
683
690
  },
684
691
  onInputBlur(e) {
685
692
  this.isFocused = false
686
- let value = e.target.value
687
- let evaluatedInput = this.onEvaluateCode(e)
688
- this.onChangeHandler(evaluatedInput ? evaluatedInput : value)
689
- if ((evaluatedInput && value.length) || this.minNumber !== null) {
690
- this.textInput = numberToString({
691
- value:
692
- evaluatedInput && value.length
693
- ? evaluatedInput
694
- : this.defaultNumber
695
- ? this.defaultNumber
696
- : this.minNumber,
697
- numberPrecision: this.numberPrecision,
698
- minDecimals: this.minDecimals,
699
- })
700
- }
701
- let adjustedMinValue =
702
- evaluatedInput && evaluatedInput.length
703
- ? evaluatedInput
704
- : this.defaultNumber
705
- ? this.defaultNumber
706
- : this.minNumber || this.minNumber === 0
707
- ? this.minNumber
708
- : ''
709
- this.$emit('input-blur', adjustedMinValue)
693
+ this.enteredValue = this.inputValue
694
+ this.$emit(
695
+ EVENT_TYPES.INPUT_BLUR,
696
+ this.onEvaluateCode(String(this.inputValue))
697
+ )
710
698
  },
711
699
  focusInput() {
712
700
  if (this.disabled) {
@@ -716,7 +704,7 @@
716
704
  this.$nextTick(() => {
717
705
  this.$refs.inputField1.$el.select()
718
706
  })
719
- this.$emit('input-focus')
707
+ this.$emit(EVENT_TYPES.INPUT_FOCUS, this.inputValue)
720
708
  },
721
709
  blurInput() {
722
710
  if (this.disabled) {
@@ -749,6 +737,8 @@
749
737
  return input + ' ' + unit
750
738
  } else if (!adjustedMinValue && adjustedMinValue !== 0) {
751
739
  return ''
740
+ } else if (this.isFocused && this.numberPrecision > 0) {
741
+ return value
752
742
  } else {
753
743
  return this.numberToStringEnabled
754
744
  ? numberToString({
@@ -769,14 +759,7 @@
769
759
  e.preventDefault()
770
760
  let value = parseFloat(this.value || 0)
771
761
  value += parseFloat(this.interactionStep) * parseInt(e.movementX)
772
- this.$emit('on-input-drag', value)
773
-
774
- this.textInput = numberToString({
775
- value: value && value.length ? value : this.minNumber,
776
- numberPrecision: this.numberPrecision,
777
- minDecimals: this.minDecimals,
778
- })
779
- //this.value=value
762
+ this.$emit(EVENT_TYPES.INPUT_DRAG, this.onChangeHandler(value))
780
763
  },
781
764
  stopInteract(e) {
782
765
  e.preventDefault()
@@ -784,6 +767,9 @@
784
767
  window.removeEventListener('mouseup', this.stopInteract, false)
785
768
  this.blurInput()
786
769
  },
770
+ handleSelectChange(value) {
771
+ this.$emit(EVENT_TYPES.SELECT_CHANGE, value)
772
+ },
787
773
  },
788
774
  }
789
775
  </script>
@@ -120,6 +120,11 @@
120
120
  <SelectDropdown
121
121
  v-show="isSelectDropdownShown"
122
122
  ref="dropdown"
123
+ :style="{
124
+ transform: `translate(${dropdownPosition?.left}px, ${
125
+ noRelative ? 'auto' : `${dropdownPosition?.top}px`
126
+ })`,
127
+ }"
123
128
  :bg-color="
124
129
  dropdownBgColor || colorMode == 'dark' ? 'black' : 'white'
125
130
  "
@@ -344,9 +349,8 @@
344
349
  box-sizing: border-box;
345
350
  z-index: ${(props) => (props.isActive ? '2' : '99999')};
346
351
  position: absolute;
347
- top: ${(props) =>
348
- props.noRelative ? 'auto' : props.dropdownPosition?.top + 'px'};
349
- left: ${(props) => props.dropdownPosition?.left}px;
352
+ top: 0px;
353
+ left: 0px;
350
354
  border: ${BORDER_WIDTH} solid ${(props) => props.theme.colors.grey4};
351
355
  border-radius: 4px;
352
356
  display: flex;
@@ -606,6 +610,10 @@
606
610
  },
607
611
  dropdownWidth: null,
608
612
  hoveredValue: null,
613
+ isDisplayedAtBottom: true,
614
+ selectTopPosition: 0,
615
+ selectAndDropdownDistance: 0,
616
+ animationFrameId: null,
609
617
  }
610
618
  },
611
619
  computed: {
@@ -676,8 +684,13 @@
676
684
  }, 10)
677
685
  await this.$nextTick()
678
686
  this.handleSetDropdownOffet()
687
+ this.calculateSelectTopPosition()
679
688
  } else {
680
689
  this.dropdownPosition.left = null
690
+ if (this.animationFrameId) {
691
+ cancelAnimationFrame(this.animationFrameId)
692
+ this.animationFrameId = null
693
+ }
681
694
  setTimeout(() => {
682
695
  this.isClickOutsideActive = false
683
696
  }, 10)
@@ -690,11 +703,27 @@
690
703
  })
691
704
  }
692
705
  },
706
+ isSelectDropdownShown(isShown) {
707
+ if (!isShown) return
708
+
709
+ // Need to wait for 1ms to make sure the dropdown menu is shown in the DOM
710
+ // before getting the distance between the select and the dropdown menu
711
+ setTimeout(() => {
712
+ this.getDistanceBetweenSelectAndDropdownMenu()
713
+ }, 100)
714
+ },
715
+ selectTopPosition() {
716
+ this.dropdownPosition.top =
717
+ this.selectTopPosition +
718
+ this.$refs.select.$el.clientHeight +
719
+ this.selectAndDropdownDistance
720
+ },
693
721
  },
694
722
  mounted() {
695
723
  this.observeDropdownHeight()
696
724
  this.observeSelectWidth()
697
725
  window.addEventListener('resize', this.handleSetDropdownOffet)
726
+ document.body.addEventListener('scroll', this.calculateSelectTopPosition)
698
727
  },
699
728
  beforeMount() {
700
729
  this.selectedValue = this.value
@@ -703,6 +732,10 @@
703
732
  window.removeEventListener('resize', this.handleSetDropdownOffet)
704
733
  if (this.dropdownResizeObserver) this.dropdownResizeObserver.disconnect()
705
734
  if (this.selectResizeObserver) this.selectResizeObserver.disconnect()
735
+ document.body.removeEventListener(
736
+ 'scroll',
737
+ this.calculateSelectTopPosition
738
+ )
706
739
  },
707
740
  unmounted() {
708
741
  document.removeEventListener('click', this.clickOutside)
@@ -808,11 +841,11 @@
808
841
  return
809
842
  }
810
843
  await this.$nextTick()
811
- const isDisplayedAtBottom = await this.generateDropdownPosition()
844
+ this.isDisplayedAtBottom = await this.generateDropdownPosition()
812
845
  // If the dropdown menu is going to be displayed at the bottom,
813
846
  // we need reverify its position after a dom update (nextTick)
814
847
  await this.$nextTick()
815
- if (isDisplayedAtBottom) this.generateDropdownPosition()
848
+ if (this.isDisplayedAtBottom) this.generateDropdownPosition()
816
849
  },
817
850
  async generateDropdownPosition() {
818
851
  const isDropdownNotCompletelyVisible =
@@ -905,6 +938,25 @@
905
938
  }
906
939
  }
907
940
  },
941
+ getDistanceBetweenSelectAndDropdownMenu() {
942
+ const wholeSelectTopPosition =
943
+ this.selectTopPosition + this.$refs.select.$el.clientHeight
944
+ this.selectAndDropdownDistance =
945
+ this.dropdownPosition.top - wholeSelectTopPosition
946
+ },
947
+ calculateSelectTopPosition() {
948
+ const selectRef = this.$refs.select
949
+ if (selectRef) {
950
+ const currentTopPosition =
951
+ selectRef.$el.getBoundingClientRect().top + window.scrollY
952
+ if (this.selectTopPosition !== currentTopPosition) {
953
+ this.selectTopPosition = currentTopPosition
954
+ }
955
+ }
956
+ this.animationFrameId = requestAnimationFrame(
957
+ this.calculateSelectTopPosition
958
+ )
959
+ },
908
960
  },
909
961
  }
910
962
  </script>
@@ -35,37 +35,37 @@
35
35
  visibility: ${(props) => (props.visible ? 'inherit' : 'hidden')};
36
36
  `
37
37
 
38
- const pageAttrs = { isOpen: Boolean, backdrop: String, position: String }
38
+ const pageAttrs = { backdrop: String, position: String }
39
39
  const PageWrapper = styled('div', pageAttrs)`
40
- position: ${(props) => props.position}
41
- display: grid;
42
- top: 0;
43
- left: 0;
44
- width: 100%;
45
- height: 100%;
46
- background-color: ${(props) =>
47
- props.backdrop == 'dark'
48
- ? 'rgba(0, 0, 0, 0.4)'
49
- : 'rgba(255, 255, 255, 0.9)'};
50
- z-index: 99999;
51
- overflow: auto;
40
+ position: ${(props) => props.position}
41
+ display: grid;
42
+ top: 0;
43
+ left: 0;
44
+ width: 100%;
45
+ height: 100%;
46
+ background-color: ${(props) =>
47
+ props.backdrop == 'dark'
48
+ ? 'rgba(0, 0, 0, 0.4)'
49
+ : 'rgba(255, 255, 255, 0.9)'};
50
+ z-index: 99999;
51
+ overflow: auto;
52
52
 
53
- &.visible {
54
- visibility: visible;
55
- opacity: 1;
56
- transition: visibility 0s linear 0s, opacity 300ms;
57
- }
53
+ &.visible {
54
+ visibility: visible;
55
+ opacity: 1;
56
+ transition: visibility 0s linear 0s, opacity 300ms;
57
+ }
58
58
 
59
- &.hidden {
60
- visibility: hidden;
61
- opacity: 0;
62
- transition: visibility 0s linear 300ms, opacity 300ms;
63
- }
59
+ &.hidden {
60
+ visibility: hidden;
61
+ opacity: 0;
62
+ transition: visibility 0s linear 300ms, opacity 300ms;
63
+ }
64
64
 
65
- @media (max-width: 425px) {
66
- background: white;
67
- }
68
- `
65
+ @media (max-width: 425px) {
66
+ background: white;
67
+ }
68
+ `
69
69
 
70
70
  const ModalContainer = styled.div`
71
71
  align-self: center;
@@ -94,7 +94,7 @@ export const stringToNumber = ({
94
94
 
95
95
  export const numberToString = ({ value, numberPrecision, minDecimals }) => {
96
96
  const minimumRounding = minDecimals ? minDecimals : 0
97
- value = parseFloat(value)
97
+ value = !Number.isNaN(parseFloat(value)) ? parseFloat(value) : 0
98
98
  return value.toLocaleString(langForLocaleString(), {
99
99
  minimumFractionDigits: minimumRounding, // removing this for now. Why do we need this to be a minimum amount?
100
100
  maximumFractionDigits:
@@ -1,66 +0,0 @@
1
- import { mount } from '@vue/test-utils'
2
- import RCIcon from '@/components/icon'
3
- import theme from '@/assets/theme'
4
- import 'jest-styled-components'
5
- jest.mock('@/components/icon/iconCache.mjs', () => ({
6
- fetchIcon: jest.fn(() => Promise.resolve('mocked-icon-url.svg')),
7
- }))
8
-
9
- describe('RCIcon.vue', () => {
10
- it('renders icon with default props', async () => {
11
- const wrapper = mount(RCIcon, {
12
- props: { name: 'House' },
13
- global: {
14
- provide: {
15
- theme,
16
- },
17
- },
18
- })
19
-
20
- const iconWrapper = wrapper.find('[data-test-id="icon_wrapper"]')
21
- const iconImage = wrapper.find('[data-test-id="icon_image"]')
22
-
23
- expect(iconWrapper.exists()).toBe(true)
24
- expect(iconImage.exists()).toBe(true)
25
- expect(wrapper.props('size')).toBe('30px')
26
- })
27
-
28
- it('renders striked line when isStriked is true', async () => {
29
- const wrapper = mount(RCIcon, {
30
- props: {
31
- name: 'House',
32
- isStriked: true,
33
- color: 'red',
34
- },
35
- global: {
36
- provide: {
37
- theme,
38
- },
39
- },
40
- })
41
-
42
- const strikedLine = wrapper.find('[data-test-id="icon_striked_line"]')
43
- expect(strikedLine.exists()).toBe(true)
44
- })
45
-
46
- it('applies correct styling based on props', async () => {
47
- const wrapper = mount(RCIcon, {
48
- props: {
49
- name: 'House',
50
- size: '60px',
51
- color: 'red',
52
- hoveredColor: 'blue',
53
- backgroundColor: 'yellow',
54
- },
55
- global: {
56
- provide: {
57
- theme,
58
- },
59
- },
60
- })
61
-
62
- const iconImage = wrapper.find('[data-test-id="icon_image"]')
63
- expect(iconImage.exists()).toBe(true)
64
- // Add assertions for styling if needed
65
- })
66
- })