@eturnity/eturnity_reusable_components 7.45.0 → 7.45.1-EPDM-12459.0

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.0",
3
+ "version": "7.45.1-EPDM-12459.0",
4
4
  "files": [
5
5
  "dist",
6
6
  "src"
@@ -0,0 +1,70 @@
1
+ /* eslint-disable */
2
+ import { h } from 'vue'
3
+ import { mount } from '@vue/test-utils'
4
+ import InfoBanner from '@/components/banner/infoBanner'
5
+ import theme from '@/assets/theme'
6
+
7
+ jest.mock('@/components/icon/iconCache.mjs', () => ({
8
+ // need to mock this due to how jest handles import.meta
9
+ fetchIcon: jest.fn(() => Promise.resolve('')),
10
+ }))
11
+
12
+ describe('Info Banner Component', () => {
13
+ const props = {
14
+ isOpen: false,
15
+ buttonLabel: 'Gotcha',
16
+ }
17
+
18
+ const global = {
19
+ provide: {
20
+ theme,
21
+ },
22
+ }
23
+
24
+ it('info banner is shown when isOpen props is true', async () => {
25
+ const wrapper = mount(InfoBanner, {
26
+ props,
27
+ global,
28
+ })
29
+
30
+ const bannerWrapper = wrapper.find('[data-test-id="info_banner_wrapper"]')
31
+ expect(bannerWrapper.exists()).toBe(true)
32
+ expect(bannerWrapper.classes()).not.toContain('visible')
33
+ expect(bannerWrapper.classes()).toContain('hidden')
34
+ await wrapper.setProps({ isOpen: true })
35
+ expect(bannerWrapper.classes()).toContain('visible')
36
+ expect(bannerWrapper.classes()).not.toContain('hidden')
37
+ })
38
+
39
+ it('info banner slots is display when user passed slots content', async () => {
40
+ const titleText = 'Sample title'
41
+ const bodyText = 'Sample body text'
42
+
43
+ const wrapper = mount(InfoBanner, {
44
+ props,
45
+ slots: {
46
+ title: titleText,
47
+ body: bodyText,
48
+ },
49
+ global,
50
+ })
51
+
52
+ const modalTitleEl = wrapper.find('[data-test-id="modal_title"]')
53
+ expect(modalTitleEl.text()).toBe(titleText)
54
+ const modalBodyEl = wrapper.find('[data-test-id="modal_body"]')
55
+ expect(modalBodyEl.text()).toBe(bodyText)
56
+ })
57
+
58
+ it('info banner on-close event is emitted when modal close button is clicked', async () => {
59
+ const wrapper = mount(InfoBanner, {
60
+ props,
61
+ global,
62
+ })
63
+
64
+ const modalCloseButton = wrapper.find('.close')
65
+ await modalCloseButton.trigger('click')
66
+ expect(wrapper.emitted('on-close')).toBeTruthy()
67
+ const emittedEvent = wrapper.emitted('on-close')
68
+ expect(emittedEvent).toHaveLength(1)
69
+ })
70
+ })
@@ -0,0 +1,42 @@
1
+ import InfoBanner from './index.vue'
2
+ import theme from '@/assets/theme'
3
+ export default {
4
+ title: 'InfoBanner',
5
+ component: InfoBanner,
6
+ tags: ['autodocs'],
7
+ }
8
+
9
+ // <RCInfoBanner
10
+ // :isOpen="true"
11
+ // buttonLabel="Got it"
12
+ // @on-close="handleClose"
13
+ // >
14
+ // <template #title>Sample title</template>
15
+ // <template #body>Sample body</template>
16
+ // </RCInfoBanner>
17
+
18
+ export const Default = {
19
+ args: {
20
+ isOpen: false,
21
+ buttonLabel: 'Got it',
22
+ },
23
+ }
24
+
25
+ export const OpenedInfoBanner = {
26
+ args: {
27
+ isOpen: true,
28
+ buttonLabel: 'Got it',
29
+ },
30
+ render: (args) => ({
31
+ components: { InfoBanner },
32
+ setup() {
33
+ return { args }
34
+ },
35
+ template: `
36
+ <InfoBanner v-bind="args">
37
+ <template #title>Sample title</template>
38
+ <template #body>Sample body</template>
39
+ </InfoBanner>
40
+ `,
41
+ }),
42
+ }
@@ -1,45 +1,75 @@
1
1
  <template>
2
- <ActionModal :is-open="isOpen" @on-close="closeModal">
2
+ <RCModal
3
+ data-test-id="info_banner_wrapper"
4
+ :is-open="isOpen"
5
+ @on-close="closeModal"
6
+ >
3
7
  <ModalContainer>
4
- <template name="title">
8
+ <ModalTitle v-if="$slots.title" data-test-id="modal_title">
5
9
  <slot name="title"></slot>
6
- </template>
7
- <template name="body">
10
+ </ModalTitle>
11
+ <TextContainer v-if="$slots.body" data-test-id="modal_body">
8
12
  <slot name="body"></slot>
9
- </template>
10
- <template name="buttons">
11
- <ButtonContainer>
12
- <Button
13
- min-width="150px"
14
- :text="$gettext('Got it')"
15
- type="primary"
16
- @click="closeModal"
17
- />
18
- </ButtonContainer>
19
- </template>
13
+ </TextContainer>
14
+ <ButtonContainer>
15
+ <RCButton
16
+ data-test-id="modal_buttons"
17
+ min-width="150px"
18
+ :text="buttonText"
19
+ type="primary"
20
+ @click="closeModal"
21
+ />
22
+ </ButtonContainer>
20
23
  </ModalContainer>
21
- </ActionModal>
24
+ </RCModal>
22
25
  </template>
23
26
  <script>
24
27
  import styled from 'vue3-styled-components'
25
- import ActionModal from '../actionModal'
28
+ import RCModal from '@/components/modals/modal'
29
+ import RCButton from '@/components/buttons/mainButton'
26
30
 
27
31
  const ModalContainer = styled.div`
28
32
  width: 450px;
33
+ display: flex;
34
+ flex-direction: column;
29
35
  min-height: 205px;
30
36
  padding: 40px 40px 30px 40px;
31
37
  `
38
+
39
+ const ModalTitle = styled.div`
40
+ color: ${(props) => props.theme.colors.black};
41
+ font-family: inherit;
42
+ font-size: 18px;
43
+ font-style: normal;
44
+ font-weight: 700;
45
+ line-height: 120%;
46
+ text-transform: uppercase;
47
+ `
32
48
  const ButtonContainer = styled.div`
33
49
  display: inline-flex;
34
- align-items: flex-start;
50
+ flex-grow: 1;
51
+ align-items: flex-end;
35
52
  gap: 20px;
36
53
  `
54
+ const TextContainer = styled.div`
55
+ color: ${(props) => props.theme.colors.black};
56
+ font-family: inherit;
57
+ font-size: 13px;
58
+ font-style: normal;
59
+ font-weight: 400;
60
+ line-height: normal;
61
+ padding: 30px 0px;
62
+ white-space: pre-wrap;
63
+ `
37
64
  export default {
38
65
  name: 'InfoModal',
39
66
  components: {
40
67
  ModalContainer,
68
+ ModalTitle,
69
+ TextContainer,
41
70
  ButtonContainer,
42
- ActionModal,
71
+ RCModal,
72
+ RCButton,
43
73
  },
44
74
  props: {
45
75
  isOpen: {
@@ -47,6 +77,16 @@
47
77
  default: false,
48
78
  type: Boolean,
49
79
  },
80
+ buttonLabel: {
81
+ default: null,
82
+ type: String,
83
+ },
84
+ },
85
+ computed: {
86
+ buttonText() {
87
+ //work around for storybook not finding the $gettext function
88
+ return this.buttonLabel || this.$gettext('Got it')
89
+ },
50
90
  },
51
91
  methods: {
52
92
  closeModal() {
@@ -606,6 +606,10 @@
606
606
  },
607
607
  dropdownWidth: null,
608
608
  hoveredValue: null,
609
+ isDisplayedAtBottom: true,
610
+ selectTopPosition: 0,
611
+ selectAndDropdownDistance: 0,
612
+ animationFrameId: null,
609
613
  }
610
614
  },
611
615
  computed: {
@@ -676,8 +680,13 @@
676
680
  }, 10)
677
681
  await this.$nextTick()
678
682
  this.handleSetDropdownOffet()
683
+ this.calculateSelectTopPosition()
679
684
  } else {
680
685
  this.dropdownPosition.left = null
686
+ if (this.animationFrameId) {
687
+ cancelAnimationFrame(this.animationFrameId)
688
+ this.animationFrameId = null
689
+ }
681
690
  setTimeout(() => {
682
691
  this.isClickOutsideActive = false
683
692
  }, 10)
@@ -690,11 +699,27 @@
690
699
  })
691
700
  }
692
701
  },
702
+ isSelectDropdownShown(isShown) {
703
+ if (!isShown) return
704
+
705
+ // Need to wait for 1ms to make sure the dropdown menu is shown in the DOM
706
+ // before getting the distance between the select and the dropdown menu
707
+ setTimeout(() => {
708
+ this.getDistanceBetweenSelectAndDropdownMenu()
709
+ }, 100)
710
+ },
711
+ selectTopPosition() {
712
+ this.dropdownPosition.top =
713
+ this.selectTopPosition +
714
+ this.$refs.select.$el.clientHeight +
715
+ this.selectAndDropdownDistance
716
+ },
693
717
  },
694
718
  mounted() {
695
719
  this.observeDropdownHeight()
696
720
  this.observeSelectWidth()
697
721
  window.addEventListener('resize', this.handleSetDropdownOffet)
722
+ document.body.addEventListener('scroll', this.calculateSelectTopPosition)
698
723
  },
699
724
  beforeMount() {
700
725
  this.selectedValue = this.value
@@ -703,11 +728,34 @@
703
728
  window.removeEventListener('resize', this.handleSetDropdownOffet)
704
729
  if (this.dropdownResizeObserver) this.dropdownResizeObserver.disconnect()
705
730
  if (this.selectResizeObserver) this.selectResizeObserver.disconnect()
731
+ document.body.removeEventListener(
732
+ 'scroll',
733
+ this.calculateSelectTopPosition
734
+ )
706
735
  },
707
736
  unmounted() {
708
737
  document.removeEventListener('click', this.clickOutside)
709
738
  },
710
739
  methods: {
740
+ getDistanceBetweenSelectAndDropdownMenu() {
741
+ const wholeSelectTopPosition =
742
+ this.selectTopPosition + this.$refs.select.$el.clientHeight
743
+ this.selectAndDropdownDistance =
744
+ this.dropdownPosition.top - wholeSelectTopPosition
745
+ },
746
+ calculateSelectTopPosition() {
747
+ const selectRef = this.$refs.select
748
+ if (selectRef) {
749
+ const currentTopPosition =
750
+ selectRef.$el.getBoundingClientRect().top + window.scrollY
751
+ if (this.selectTopPosition !== currentTopPosition) {
752
+ this.selectTopPosition = currentTopPosition
753
+ }
754
+ }
755
+ this.animationFrameId = requestAnimationFrame(
756
+ this.calculateSelectTopPosition
757
+ )
758
+ },
711
759
  focus() {
712
760
  this.isActive = true
713
761
  },
@@ -808,11 +856,11 @@
808
856
  return
809
857
  }
810
858
  await this.$nextTick()
811
- const isDisplayedAtBottom = await this.generateDropdownPosition()
859
+ this.isDisplayedAtBottom = await this.generateDropdownPosition()
812
860
  // If the dropdown menu is going to be displayed at the bottom,
813
861
  // we need reverify its position after a dom update (nextTick)
814
862
  await this.$nextTick()
815
- if (isDisplayedAtBottom) this.generateDropdownPosition()
863
+ if (this.isDisplayedAtBottom) this.generateDropdownPosition()
816
864
  },
817
865
  async generateDropdownPosition() {
818
866
  const isDropdownNotCompletelyVisible =