@eturnity/eturnity_reusable_components 7.45.5-qa-elisee-7.48.0 → 7.45.5-qa-16-11.13.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.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
- })