@eturnity/eturnity_reusable_components 7.45.5-qa-elisee-7.48.0 → 7.45.5-qa-16-11.13

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",
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
  >
@@ -339,8 +339,17 @@
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
+ }
349
+
342
350
  export default {
343
351
  name: 'InputNumber',
352
+ emits: [...Object.values(EVENT_TYPES)],
344
353
  components: {
345
354
  Container,
346
355
  InputContainer,
@@ -519,9 +528,10 @@
519
528
  },
520
529
  data() {
521
530
  return {
522
- textInput: '',
523
531
  isFocused: false,
524
532
  warningIcon: warningIcon,
533
+ inputValue: null,
534
+ enteredValue: null,
525
535
  }
526
536
  },
527
537
  computed: {
@@ -544,6 +554,14 @@
544
554
 
545
555
  return item ? item.label : '-'
546
556
  },
557
+ formattedValue() {
558
+ return this.formatWithCurrency(
559
+ this.isFocused ? this.enteredValue : this.inputValue
560
+ )
561
+ },
562
+ hasLength() {
563
+ return this.formattedValue !== null && this.formattedValue.length > 0
564
+ },
547
565
  },
548
566
  watch: {
549
567
  focus(value) {
@@ -554,30 +572,19 @@
554
572
  clearInput: function (value) {
555
573
  if (value) {
556
574
  // If the value is typed, then we should clear the textInput on Continue
557
- this.textInput = ''
575
+ this.inputValue = ''
576
+ this.enteredValue = ''
558
577
  }
559
578
  },
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
- }
579
+ value: {
580
+ immediate: true,
581
+ handler(val) {
582
+ if (this.value !== this.inputValue && !Number.isNaN(this.value)) {
583
+ this.inputValue = this.value
584
+ this.enteredValue = this.value
585
+ }
586
+ },
587
+ },
581
588
  },
582
589
  mounted() {
583
590
  if (this.focus) {
@@ -586,29 +593,28 @@
586
593
  },
587
594
  methods: {
588
595
  onEnterPress() {
589
- this.$emit('on-enter-click')
596
+ this.$emit(EVENT_TYPES.PRESS_ENTER)
590
597
  this.$refs.inputField1.$el.blur()
591
598
  },
592
- onChangeHandler(event) {
593
- if (isNaN(event) || event === '') {
594
- event = this.defaultNumber
599
+ onChangeHandler(value) {
600
+ if (isNaN(value) || value === '') {
601
+ value = this.defaultNumber
595
602
  ? this.defaultNumber
596
603
  : this.minNumber || this.minNumber === 0
597
604
  ? this.minNumber
598
- : event
605
+ : value
599
606
  }
600
607
  if (!this.allowNegative) {
601
- event = Math.abs(event)
608
+ value = Math.abs(value)
602
609
  }
603
- event = parseFloat(event)
610
+ value = parseFloat(value)
604
611
  // Need to return an integer rather than a string
605
- this.$emit('input-change', event)
612
+ return parseFloat(value)
606
613
  },
607
- onEvaluateCode(event) {
614
+ onEvaluateCode(value) {
608
615
  // function to perform math on the code
609
616
  // filter the string in case of any malicious content
610
- const val = event.target.value
611
- let filtered = val.replace('(auto)', '').replace(/[^-()\d/*+.,]/g, '')
617
+ let filtered = value.replace('(auto)', '').replace(/[^-()\d/*+.,]/g, '')
612
618
  filtered = filtered.split(/([-+*/()])/)
613
619
  let formatted = filtered.map((item) => {
614
620
  if (
@@ -666,47 +672,28 @@
666
672
  return array
667
673
  },
668
674
  onInput(event) {
669
- if (!this.isFocused) {
675
+ this.enteredValue = event.target.value
676
+ if (!this.isFocused || this.enteredValue === this.inputValue) {
670
677
  return
671
678
  }
672
- if (event.target.value === '') {
673
- this.$emit('on-input', '')
674
- }
675
679
  let evaluatedVal
676
680
  try {
677
- evaluatedVal = this.onEvaluateCode(event)
681
+ evaluatedVal = this.onEvaluateCode(String(this.enteredValue))
678
682
  } finally {
679
- if (evaluatedVal && this.value != evaluatedVal) {
680
- this.$emit('on-input', evaluatedVal)
683
+ this.inputValue = this.onChangeHandler(evaluatedVal)
684
+
685
+ if (this.isFocused && typeof this.enteredValue !== 'number') {
686
+ this.$emit(EVENT_TYPES.INPUT_CHANGE, this.inputValue)
681
687
  }
682
688
  }
683
689
  },
684
690
  onInputBlur(e) {
685
691
  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)
692
+ this.enteredValue = this.inputValue
693
+ this.$emit(
694
+ EVENT_TYPES.INPUT_BLUR,
695
+ this.onEvaluateCode(String(this.inputValue))
696
+ )
710
697
  },
711
698
  focusInput() {
712
699
  if (this.disabled) {
@@ -716,7 +703,7 @@
716
703
  this.$nextTick(() => {
717
704
  this.$refs.inputField1.$el.select()
718
705
  })
719
- this.$emit('input-focus')
706
+ this.$emit(EVENT_TYPES.INPUT_FOCUS)
720
707
  },
721
708
  blurInput() {
722
709
  if (this.disabled) {
@@ -749,6 +736,8 @@
749
736
  return input + ' ' + unit
750
737
  } else if (!adjustedMinValue && adjustedMinValue !== 0) {
751
738
  return ''
739
+ } else if (this.isFocused && this.numberPrecision > 0) {
740
+ return value
752
741
  } else {
753
742
  return this.numberToStringEnabled
754
743
  ? numberToString({
@@ -769,14 +758,7 @@
769
758
  e.preventDefault()
770
759
  let value = parseFloat(this.value || 0)
771
760
  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
761
+ this.$emit(EVENT_TYPES.INPUT_DRAG, this.onChangeHandler(value))
780
762
  },
781
763
  stopInteract(e) {
782
764
  e.preventDefault()
@@ -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
- })