@eturnity/eturnity_reusable_components 8.16.2 → 8.16.4
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 +1 -1
- package/src/components/banner/infoBanner/InfoBanner.spec.js +29 -42
- package/src/components/errorMessage/errorMessage.spec.js +34 -0
- package/src/components/errorMessage/errorMessage.stories.js +35 -0
- package/src/components/infoText/index.vue +1 -1
- package/src/components/infoText/infoText.spec.js +6 -1
- package/src/components/inputs/select/index.vue +89 -16
- package/src/components/modals/actionModal/actionModal.spec.js +52 -0
- package/src/components/modals/actionModal/actionModal.stories.js +53 -0
- package/src/components/modals/actionModal/index.vue +6 -6
- package/src/components/modals/infoModal/index.vue +49 -19
- package/src/components/modals/infoModal/infoModal.spec.js +55 -0
- package/src/components/modals/infoModal/infoModal.stories.js +47 -0
- package/src/components/modals/modal/index.vue +15 -5
- package/src/components/pageSubtitle/PageSubtitle.stories.js +0 -1
package/package.json
CHANGED
@@ -1,5 +1,4 @@
|
|
1
1
|
/* eslint-disable */
|
2
|
-
import { h } from 'vue'
|
3
2
|
import { mount } from '@vue/test-utils'
|
4
3
|
import InfoBanner from '@/components/banner/infoBanner'
|
5
4
|
import theme from '@/assets/theme'
|
@@ -10,59 +9,47 @@ jest.mock('@/components/icon/iconCache.mjs', () => ({
|
|
10
9
|
}))
|
11
10
|
|
12
11
|
describe('Info Banner Component', () => {
|
13
|
-
|
14
|
-
|
15
|
-
|
16
|
-
|
17
|
-
|
18
|
-
|
19
|
-
|
20
|
-
|
21
|
-
|
22
|
-
|
23
|
-
|
24
|
-
|
25
|
-
|
26
|
-
|
27
|
-
|
12
|
+
let wrapper
|
13
|
+
|
14
|
+
beforeEach(() => {
|
15
|
+
wrapper = mount(InfoBanner, {
|
16
|
+
props: {
|
17
|
+
isOpen: true,
|
18
|
+
buttonLabel: 'Gotcha',
|
19
|
+
},
|
20
|
+
slots: {
|
21
|
+
title: 'Sample title',
|
22
|
+
body: 'Sample body text',
|
23
|
+
},
|
24
|
+
global: {
|
25
|
+
provide: {
|
26
|
+
theme,
|
27
|
+
},
|
28
|
+
},
|
29
|
+
})
|
28
30
|
})
|
29
31
|
|
32
|
+
|
33
|
+
it('info banner is shown when isOpen props is true', async () => {
|
34
|
+
|
30
35
|
const bannerWrapper = wrapper.find('[data-test-id="info_banner_wrapper"]')
|
31
36
|
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
37
|
})
|
38
38
|
|
39
|
-
|
40
|
-
|
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
|
-
|
39
|
+
|
40
|
+
it('info banner slots is display when user passed slots content', () => {
|
52
41
|
const modalTitleEl = wrapper.find('[data-test-id="modal_title"]')
|
53
|
-
expect(modalTitleEl.text()).toBe(
|
42
|
+
expect(modalTitleEl.text()).toBe('Sample title')
|
54
43
|
const modalBodyEl = wrapper.find('[data-test-id="modal_body"]')
|
55
|
-
expect(modalBodyEl.text()).toBe(
|
44
|
+
expect(modalBodyEl.text()).toBe('Sample body text')
|
56
45
|
})
|
57
46
|
|
58
47
|
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
48
|
const modalCloseButton = wrapper.find('.close')
|
65
|
-
|
49
|
+
|
50
|
+
modalCloseButton.trigger('click')
|
51
|
+
await wrapper.vm.$nextTick()
|
52
|
+
|
66
53
|
expect(wrapper.emitted('on-close')).toBeTruthy()
|
67
54
|
const emittedEvent = wrapper.emitted('on-close')
|
68
55
|
expect(emittedEvent).toHaveLength(1)
|
@@ -0,0 +1,34 @@
|
|
1
|
+
import { mount } from '@vue/test-utils'
|
2
|
+
import ErrorMessage from '@/components/errorMessage'
|
3
|
+
import theme from '@/assets/theme'
|
4
|
+
|
5
|
+
/* eslint-disable */
|
6
|
+
|
7
|
+
describe('ErrorMessage Component', () => {
|
8
|
+
let wrapper
|
9
|
+
|
10
|
+
beforeEach(() => {
|
11
|
+
wrapper = mount(ErrorMessage, {
|
12
|
+
props: {},
|
13
|
+
slots: {
|
14
|
+
default: '<div data-test-id="fake-msg">testing</div>',
|
15
|
+
},
|
16
|
+
global: {
|
17
|
+
provide: {
|
18
|
+
theme,
|
19
|
+
},
|
20
|
+
},
|
21
|
+
})
|
22
|
+
})
|
23
|
+
|
24
|
+
test('renders ErrorMessage component with default props', () => {
|
25
|
+
expect(wrapper.findAll('[data-test-id="fake-msg"]').length).toBe(1)
|
26
|
+
})
|
27
|
+
|
28
|
+
test('applies the correct CSS class for styling', async () => {
|
29
|
+
wrapper.setProps({ marginTop: '20px' })
|
30
|
+
|
31
|
+
await wrapper.vm.$nextTick()
|
32
|
+
expect(wrapper.props().marginTop).toBe('20px')
|
33
|
+
})
|
34
|
+
})
|
@@ -0,0 +1,35 @@
|
|
1
|
+
import ErrorMessage from './index.vue'
|
2
|
+
|
3
|
+
export default {
|
4
|
+
title: 'ErrorMessage',
|
5
|
+
component: ErrorMessage,
|
6
|
+
tags: ['autodocs'],
|
7
|
+
parameters: {
|
8
|
+
layout: 'centered',
|
9
|
+
},
|
10
|
+
}
|
11
|
+
|
12
|
+
// import ErrorMessage from "@eturnity/eturnity_reusable_components/src/components/errorMessage"
|
13
|
+
//
|
14
|
+
//To use:
|
15
|
+
// <error-message>
|
16
|
+
// <span>
|
17
|
+
// testing error message
|
18
|
+
// </span>
|
19
|
+
// </error-message>
|
20
|
+
|
21
|
+
const Template = (args, { argTypes }) => ({
|
22
|
+
components: { ErrorMessage },
|
23
|
+
props: Object.keys(argTypes),
|
24
|
+
template: `
|
25
|
+
<ErrorMessage v-bind="$props">
|
26
|
+
<span>
|
27
|
+
testing error message
|
28
|
+
</span>
|
29
|
+
</ErrorMessage>`,
|
30
|
+
})
|
31
|
+
|
32
|
+
export const Default = Template.bind({})
|
33
|
+
Default.args = {
|
34
|
+
marginTop: '13px',
|
35
|
+
}
|
@@ -29,6 +29,9 @@ describe('InfoText Component', () => {
|
|
29
29
|
provide: {
|
30
30
|
theme,
|
31
31
|
},
|
32
|
+
stubs: {
|
33
|
+
teleport: true
|
34
|
+
}
|
32
35
|
},
|
33
36
|
})
|
34
37
|
})
|
@@ -38,7 +41,6 @@ describe('InfoText Component', () => {
|
|
38
41
|
expect(wrapper.vm.text).toContain('default text')
|
39
42
|
expect(wrapper.vm.size).toContain('14px')
|
40
43
|
expect(wrapper.vm.infoPosition).toContain('bottom')
|
41
|
-
expect(wrapper.vm.alignArrow).toContain('center')
|
42
44
|
})
|
43
45
|
|
44
46
|
test('openTrigger prop is set to onClick', async () => {
|
@@ -48,8 +50,11 @@ describe('InfoText Component', () => {
|
|
48
50
|
expect(wrapper.find('[data-test-id="info_text_wrapper"]').exists()).toBe(
|
49
51
|
false
|
50
52
|
)
|
53
|
+
|
51
54
|
//should see text upon click
|
52
55
|
await wrapper.find('[data-test-id="infoText_trigger"]').trigger('click')
|
56
|
+
expect(wrapper.vm.isVisible).toBe(true)
|
57
|
+
|
53
58
|
expect(wrapper.find('[data-test-id="info_text_wrapper"]').exists()).toBe(
|
54
59
|
true
|
55
60
|
)
|
@@ -109,11 +109,7 @@
|
|
109
109
|
>
|
110
110
|
<slot name="selector" :selected-value="selectedValue"></slot>
|
111
111
|
</Selector>
|
112
|
-
<Caret
|
113
|
-
class="caret_dropdown"
|
114
|
-
:color-mode="colorMode"
|
115
|
-
@click.stop="toggleCaretDropdown"
|
116
|
-
>
|
112
|
+
<Caret class="caret_dropdown" :color-mode="colorMode">
|
117
113
|
<Icon
|
118
114
|
v-if="isDropdownOpen"
|
119
115
|
:color="
|
@@ -142,12 +138,11 @@
|
|
142
138
|
v-show="isSelectDropdownShown"
|
143
139
|
ref="dropdown"
|
144
140
|
:bg-color="
|
145
|
-
|
146
|
-
colorMode == 'dark' ||
|
147
|
-
colorMode == 'transparent'
|
141
|
+
colorMode == 'dark' || colorMode == 'transparent'
|
148
142
|
? 'black'
|
149
143
|
: 'white'
|
150
144
|
"
|
145
|
+
class="rc-select-dropdown"
|
151
146
|
:dropdown-position="dropdownPosition"
|
152
147
|
:font-color="
|
153
148
|
dropdownFontColor ||
|
@@ -167,10 +162,17 @@
|
|
167
162
|
:hovered-index="hoveredIndex"
|
168
163
|
:hovered-value="hoveredValue"
|
169
164
|
:is-active="isActive"
|
165
|
+
:is-fixed-dropdown-position="isFixedDropdownPosition"
|
166
|
+
:is-parent-modal="isParentModal"
|
170
167
|
:min-width="minWidth"
|
171
168
|
:no-relative="noRelative"
|
172
169
|
:option-width="getOptionWidth"
|
173
170
|
:selected-value="selectedValue"
|
171
|
+
:style="{
|
172
|
+
transform: `translate(${dropdownPosition?.left}px, ${
|
173
|
+
noRelative ? 'auto' : `${dropdownPosition?.top}px`
|
174
|
+
})`,
|
175
|
+
}"
|
174
176
|
@mouseleave="optionLeave"
|
175
177
|
@option-hovered="optionHovered"
|
176
178
|
@option-selected="optionSelected"
|
@@ -208,7 +210,7 @@
|
|
208
210
|
// </template>
|
209
211
|
// </Select>
|
210
212
|
|
211
|
-
import { Teleport } from 'vue'
|
213
|
+
import { Teleport, inject } from 'vue'
|
212
214
|
import styled from 'vue3-styled-components'
|
213
215
|
import InfoText from '../../infoText'
|
214
216
|
import Icon from '../../icon'
|
@@ -392,14 +394,17 @@
|
|
392
394
|
selectedValue: Number | String,
|
393
395
|
noRelative: Boolean,
|
394
396
|
minWidth: String,
|
397
|
+
isParentModal: Boolean,
|
398
|
+
isFixedDropdownPosition: Boolean,
|
395
399
|
}
|
396
400
|
const SelectDropdown = styled('div', selectDropdownAttrs)`
|
397
401
|
box-sizing: border-box;
|
398
|
-
z-index: ${(props) =>
|
399
|
-
|
400
|
-
|
401
|
-
props.
|
402
|
-
|
402
|
+
z-index: ${(props) =>
|
403
|
+
props.isActive ? '2' : props.isParentModal ? '9999999' : '99999'};
|
404
|
+
position: ${(props) =>
|
405
|
+
props.isFixedDropdownPosition ? 'fixed' : 'absolute'};
|
406
|
+
top: 0px;
|
407
|
+
left: 0px;
|
403
408
|
border: ${BORDER_WIDTH} solid ${(props) => props.theme.colors.grey4};
|
404
409
|
border-radius: 4px;
|
405
410
|
display: flex;
|
@@ -647,6 +652,11 @@
|
|
647
652
|
type: String,
|
648
653
|
required: false,
|
649
654
|
},
|
655
|
+
isFixedDropdownPosition: {
|
656
|
+
type: Boolean,
|
657
|
+
required: false,
|
658
|
+
default: false,
|
659
|
+
},
|
650
660
|
},
|
651
661
|
|
652
662
|
data() {
|
@@ -664,6 +674,17 @@
|
|
664
674
|
},
|
665
675
|
dropdownWidth: null,
|
666
676
|
hoveredValue: null,
|
677
|
+
isDisplayedAtBottom: true,
|
678
|
+
selectTopPosition: 0,
|
679
|
+
selectAndDropdownDistance: 0,
|
680
|
+
animationFrameId: null,
|
681
|
+
}
|
682
|
+
},
|
683
|
+
setup() {
|
684
|
+
const modalRef = inject('modalRef')
|
685
|
+
|
686
|
+
return {
|
687
|
+
modalRef,
|
667
688
|
}
|
668
689
|
},
|
669
690
|
computed: {
|
@@ -721,6 +742,9 @@
|
|
721
742
|
/windows phone/i.test(userAgent)
|
722
743
|
)
|
723
744
|
},
|
745
|
+
isParentModal() {
|
746
|
+
return !!this.modalRef
|
747
|
+
},
|
724
748
|
},
|
725
749
|
watch: {
|
726
750
|
value(val) {
|
@@ -734,8 +758,13 @@
|
|
734
758
|
}, 10)
|
735
759
|
await this.$nextTick()
|
736
760
|
this.handleSetDropdownOffet()
|
761
|
+
if (!this.isFixedDropdownPosition) this.calculateSelectTopPosition()
|
737
762
|
} else {
|
738
763
|
this.dropdownPosition.left = null
|
764
|
+
if (this.animationFrameId) {
|
765
|
+
cancelAnimationFrame(this.animationFrameId)
|
766
|
+
this.animationFrameId = null
|
767
|
+
}
|
739
768
|
setTimeout(() => {
|
740
769
|
this.isClickOutsideActive = false
|
741
770
|
}, 10)
|
@@ -748,11 +777,30 @@
|
|
748
777
|
})
|
749
778
|
}
|
750
779
|
},
|
780
|
+
isSelectDropdownShown(isShown) {
|
781
|
+
if (!isShown) return
|
782
|
+
// Need to wait for 1ms to make sure the dropdown menu is shown in the DOM
|
783
|
+
// before getting the distance between the select and the dropdown menu
|
784
|
+
setTimeout(() => {
|
785
|
+
this.getDistanceBetweenSelectAndDropdownMenu()
|
786
|
+
}, 100)
|
787
|
+
},
|
788
|
+
selectTopPosition() {
|
789
|
+
this.dropdownPosition.top =
|
790
|
+
this.selectTopPosition +
|
791
|
+
this.$refs.select.$el.clientHeight +
|
792
|
+
this.selectAndDropdownDistance
|
793
|
+
},
|
751
794
|
},
|
752
795
|
mounted() {
|
753
796
|
this.observeDropdownHeight()
|
754
797
|
this.observeSelectWidth()
|
755
798
|
window.addEventListener('resize', this.handleSetDropdownOffet)
|
799
|
+
if (!this.isFixedDropdownPosition)
|
800
|
+
document.body.addEventListener(
|
801
|
+
'scroll',
|
802
|
+
this.calculateSelectTopPosition
|
803
|
+
)
|
756
804
|
},
|
757
805
|
beforeMount() {
|
758
806
|
this.selectedValue = this.value
|
@@ -761,6 +809,12 @@
|
|
761
809
|
window.removeEventListener('resize', this.handleSetDropdownOffet)
|
762
810
|
if (this.dropdownResizeObserver) this.dropdownResizeObserver.disconnect()
|
763
811
|
if (this.selectResizeObserver) this.selectResizeObserver.disconnect()
|
812
|
+
if (!this.isFixedDropdownPosition) {
|
813
|
+
document.body.removeEventListener(
|
814
|
+
'scroll',
|
815
|
+
this.calculateSelectTopPosition
|
816
|
+
)
|
817
|
+
}
|
764
818
|
},
|
765
819
|
unmounted() {
|
766
820
|
document.removeEventListener('click', this.clickOutside)
|
@@ -866,11 +920,11 @@
|
|
866
920
|
return
|
867
921
|
}
|
868
922
|
await this.$nextTick()
|
869
|
-
|
923
|
+
this.isDisplayedAtBottom = await this.generateDropdownPosition()
|
870
924
|
// If the dropdown menu is going to be displayed at the bottom,
|
871
925
|
// we need reverify its position after a dom update (nextTick)
|
872
926
|
await this.$nextTick()
|
873
|
-
if (isDisplayedAtBottom) this.generateDropdownPosition()
|
927
|
+
if (this.isDisplayedAtBottom) this.generateDropdownPosition()
|
874
928
|
},
|
875
929
|
async generateDropdownPosition() {
|
876
930
|
const isDropdownNotCompletelyVisible =
|
@@ -963,6 +1017,25 @@
|
|
963
1017
|
}
|
964
1018
|
}
|
965
1019
|
},
|
1020
|
+
getDistanceBetweenSelectAndDropdownMenu() {
|
1021
|
+
const wholeSelectTopPosition =
|
1022
|
+
this.selectTopPosition + this.$refs.select.$el.clientHeight
|
1023
|
+
this.selectAndDropdownDistance =
|
1024
|
+
this.dropdownPosition.top - wholeSelectTopPosition
|
1025
|
+
},
|
1026
|
+
calculateSelectTopPosition() {
|
1027
|
+
const selectRef = this.$refs.select
|
1028
|
+
if (selectRef) {
|
1029
|
+
const currentTopPosition =
|
1030
|
+
selectRef.$el.getBoundingClientRect().top + window.scrollY
|
1031
|
+
if (this.selectTopPosition !== currentTopPosition) {
|
1032
|
+
this.selectTopPosition = currentTopPosition
|
1033
|
+
}
|
1034
|
+
}
|
1035
|
+
this.animationFrameId = requestAnimationFrame(
|
1036
|
+
this.calculateSelectTopPosition
|
1037
|
+
)
|
1038
|
+
},
|
966
1039
|
},
|
967
1040
|
}
|
968
1041
|
</script>
|
@@ -0,0 +1,52 @@
|
|
1
|
+
/* eslint-disable */
|
2
|
+
import { mount } from '@vue/test-utils'
|
3
|
+
import ActionModal from '@/components/modals/actionModal'
|
4
|
+
import theme from '@/assets/theme'
|
5
|
+
|
6
|
+
describe('ActionModal Component', () => {
|
7
|
+
let wrapper
|
8
|
+
|
9
|
+
beforeEach(() => {
|
10
|
+
wrapper = mount(ActionModal, {
|
11
|
+
props: {
|
12
|
+
isOpen: true,
|
13
|
+
buttonText: 'Close',
|
14
|
+
},
|
15
|
+
slots: {
|
16
|
+
title: 'Sample title',
|
17
|
+
body: 'Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed tincidunt commodo mollis. Fusce urna felis, malesuada sed elementum et, fermentum ac massa. Integer in massa vel orci fermentum bibendum in ut ante. Donec risus risus, luctus quis ex a, pulvinar placerat lacus. Sed pharetra augue a elit volutpat, eu dignissim ex pretium. Aenean imperdiet, nulla in pharetra rutrum, mauris mauris tincidunt tellus, non tempus quam lorem laoreet lectus.',
|
18
|
+
buttons: '<button @click="closeAction">Close</button>',
|
19
|
+
},
|
20
|
+
global: {
|
21
|
+
provide: {
|
22
|
+
theme,
|
23
|
+
},
|
24
|
+
},
|
25
|
+
})
|
26
|
+
})
|
27
|
+
|
28
|
+
test('renders ActionModal component with default props', () => {
|
29
|
+
expect(wrapper.find('[data-test-id="actionModal"]').exists()).toBe(true)
|
30
|
+
|
31
|
+
expect(wrapper.vm.isOpen).toBe(true)
|
32
|
+
})
|
33
|
+
|
34
|
+
test('action modal slots is display when user passed slots content', () => {
|
35
|
+
const modalTitleEl = wrapper.find('[data-test-id="modal_title"]')
|
36
|
+
expect(modalTitleEl.text()).toBe('Sample title')
|
37
|
+
|
38
|
+
const modalBodyEl = wrapper.find('[data-test-id="modal_body"]')
|
39
|
+
expect(modalBodyEl.text()).toContain('Lorem ipsum dolor sit amet')
|
40
|
+
|
41
|
+
const modalActionButton = wrapper.find('[data-test-id="modal_buttons"]')
|
42
|
+
expect(modalActionButton.text()).toContain('Close')
|
43
|
+
})
|
44
|
+
|
45
|
+
test('action modal on-close event is emitted when modal close button is clicked', async () => {
|
46
|
+
const modalCloseButton = wrapper.find('.close')
|
47
|
+
|
48
|
+
modalCloseButton.trigger('click')
|
49
|
+
await wrapper.vm.$nextTick()
|
50
|
+
expect(wrapper.emitted('on-close')).toBeTruthy()
|
51
|
+
})
|
52
|
+
})
|
@@ -0,0 +1,53 @@
|
|
1
|
+
import ActionModal from './index.vue'
|
2
|
+
|
3
|
+
export default {
|
4
|
+
title: 'Components/ActionModal',
|
5
|
+
component: ActionModal,
|
6
|
+
tags: ['autodocs'],
|
7
|
+
parameters: {
|
8
|
+
layout: 'centered',
|
9
|
+
},
|
10
|
+
}
|
11
|
+
|
12
|
+
// import ActionModal from "@eturnity/eturnity_reusable_components/src/components/modals/ActionModal"
|
13
|
+
// This is a reusable modal component that can be used to display information to the user.
|
14
|
+
// To use:
|
15
|
+
// <ActionModal :isOpen="isOpen" @on-close="$emit('on-close-summary')" >
|
16
|
+
// <template #title>
|
17
|
+
// <h1>Header</h1>
|
18
|
+
// </template>
|
19
|
+
// <template #body>
|
20
|
+
// <p>Body</p>
|
21
|
+
// </template>
|
22
|
+
// <template #buttons>
|
23
|
+
// <button @click="closeModal">Close</button>
|
24
|
+
// </template>
|
25
|
+
// </ActionModal>
|
26
|
+
|
27
|
+
export const Default = {
|
28
|
+
args: {
|
29
|
+
isOpen: true,
|
30
|
+
},
|
31
|
+
render: (args) => ({
|
32
|
+
components: { ActionModal },
|
33
|
+
setup() {
|
34
|
+
return { args }
|
35
|
+
},
|
36
|
+
template: `
|
37
|
+
<ActionModal v-bind="args">
|
38
|
+
<template #title>Sample title</template>
|
39
|
+
<template #body>
|
40
|
+
Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed tincidunt commodo mollis. Fusce urna felis,
|
41
|
+
malesuada sed elementum et, fermentum ac massa. Integer in massa vel orci fermentum bibendum in ut ante.
|
42
|
+
Donec risus risus, luctus quis ex a, pulvinar placerat lacus. Sed pharetra augue a elit volutpat, eu dignissim
|
43
|
+
ex pretium. Aenean imperdiet, nulla in pharetra rutrum, mauris mauris tincidunt tellus, non tempus quam lorem
|
44
|
+
laoreet lectus.
|
45
|
+
</template>
|
46
|
+
<template #buttons>
|
47
|
+
<button @click="saveAction">Save</button>
|
48
|
+
<button @click="closeAction">Close</button>
|
49
|
+
</template>
|
50
|
+
</ActionModal>
|
51
|
+
`,
|
52
|
+
}),
|
53
|
+
}
|
@@ -1,13 +1,13 @@
|
|
1
1
|
<template>
|
2
|
-
<Modal :is-open="isOpen" @on-close="closeModal">
|
2
|
+
<Modal data-test-id="actionModal" :is-open="isOpen" @on-close="closeModal">
|
3
3
|
<ModalContainer>
|
4
|
-
<ModalTitle v-if="$slots.title">
|
4
|
+
<ModalTitle v-if="$slots.title" data-test-id="modal_title">
|
5
5
|
<slot name="title"></slot>
|
6
6
|
</ModalTitle>
|
7
|
-
<TextContainer v-if="$slots.body">
|
7
|
+
<TextContainer v-if="$slots.body" data-test-id="modal_body">
|
8
8
|
<slot name="body"></slot>
|
9
9
|
</TextContainer>
|
10
|
-
<ButtonContainer v-if="$slots.buttons">
|
10
|
+
<ButtonContainer v-if="$slots.buttons" data-test-id="modal_buttons">
|
11
11
|
<slot name="buttons"></slot>
|
12
12
|
</ButtonContainer>
|
13
13
|
</ModalContainer>
|
@@ -23,7 +23,7 @@
|
|
23
23
|
`
|
24
24
|
const ModalTitle = styled.div`
|
25
25
|
color: ${(props) => props.theme.colors.black};
|
26
|
-
font-family:
|
26
|
+
font-family: inherit;
|
27
27
|
font-size: 18px;
|
28
28
|
font-style: normal;
|
29
29
|
font-weight: 700;
|
@@ -37,7 +37,7 @@
|
|
37
37
|
`
|
38
38
|
const TextContainer = styled.div`
|
39
39
|
color: ${(props) => props.theme.colors.black};
|
40
|
-
font-family:
|
40
|
+
font-family: inherit;
|
41
41
|
font-size: 13px;
|
42
42
|
font-style: normal;
|
43
43
|
font-weight: 400;
|
@@ -1,48 +1,78 @@
|
|
1
1
|
<template>
|
2
|
-
<
|
2
|
+
<RCModal data-test-id="infoModal" :is-open="isOpen" @on-close="closeModal">
|
3
3
|
<ModalContainer>
|
4
|
-
<
|
4
|
+
<ModalTitle v-if="$slots.title" data-test-id="modal_title">
|
5
5
|
<slot name="title"></slot>
|
6
|
-
</
|
7
|
-
<
|
6
|
+
</ModalTitle>
|
7
|
+
<TextContainer v-if="$slots.body" data-test-id="modal_body">
|
8
8
|
<slot name="body"></slot>
|
9
|
-
</
|
10
|
-
<
|
11
|
-
<
|
12
|
-
|
13
|
-
|
14
|
-
|
15
|
-
|
16
|
-
|
17
|
-
|
18
|
-
</ButtonContainer>
|
19
|
-
</template>
|
9
|
+
</TextContainer>
|
10
|
+
<ButtonContainer>
|
11
|
+
<RCMainButton
|
12
|
+
min-width="150px"
|
13
|
+
:text="buttonText"
|
14
|
+
type="primary"
|
15
|
+
@click="closeModal"
|
16
|
+
/>
|
17
|
+
</ButtonContainer>
|
20
18
|
</ModalContainer>
|
21
|
-
</
|
19
|
+
</RCModal>
|
22
20
|
</template>
|
23
21
|
<script>
|
24
22
|
import styled from 'vue3-styled-components'
|
25
|
-
import
|
23
|
+
import RCModal from '../modal'
|
26
24
|
import RCMainButton from '../../buttons/mainButton'
|
25
|
+
|
27
26
|
const ModalContainer = styled.div`
|
28
27
|
width: 450px;
|
29
28
|
min-height: 205px;
|
30
29
|
padding: 40px 40px 30px 40px;
|
31
30
|
`
|
31
|
+
const ModalTitle = styled.div`
|
32
|
+
color: ${(props) => props.theme.colors.black};
|
33
|
+
font-family: inherit;
|
34
|
+
font-size: 18px;
|
35
|
+
font-style: normal;
|
36
|
+
font-weight: 700;
|
37
|
+
line-height: 120%;
|
38
|
+
text-transform: uppercase;
|
39
|
+
`
|
32
40
|
const ButtonContainer = styled.div`
|
33
41
|
display: inline-flex;
|
34
42
|
align-items: flex-start;
|
35
43
|
gap: 20px;
|
36
44
|
`
|
45
|
+
const TextContainer = styled.div`
|
46
|
+
color: ${(props) => props.theme.colors.black};
|
47
|
+
font-family: inherit;
|
48
|
+
font-size: 13px;
|
49
|
+
font-style: normal;
|
50
|
+
font-weight: 400;
|
51
|
+
line-height: normal;
|
52
|
+
padding: 30px 0px;
|
53
|
+
white-space: pre-wrap;
|
54
|
+
`
|
55
|
+
|
37
56
|
export default {
|
38
57
|
name: 'InfoModal',
|
39
58
|
components: {
|
59
|
+
RCModal,
|
40
60
|
ModalContainer,
|
61
|
+
ModalTitle,
|
41
62
|
ButtonContainer,
|
42
|
-
|
63
|
+
TextContainer,
|
43
64
|
RCMainButton,
|
44
65
|
},
|
45
|
-
props:
|
66
|
+
props: {
|
67
|
+
isOpen: {
|
68
|
+
type: Boolean,
|
69
|
+
required: true,
|
70
|
+
},
|
71
|
+
buttonText: {
|
72
|
+
type: String,
|
73
|
+
default: 'Got it',
|
74
|
+
},
|
75
|
+
},
|
46
76
|
methods: {
|
47
77
|
closeModal() {
|
48
78
|
this.$emit('on-close')
|
@@ -0,0 +1,55 @@
|
|
1
|
+
/* eslint-disable */
|
2
|
+
import { mount } from '@vue/test-utils'
|
3
|
+
import InfoModal from '@/components/modals/infoModal'
|
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('')),
|
9
|
+
}))
|
10
|
+
|
11
|
+
|
12
|
+
describe('InfoModal Component', () => {
|
13
|
+
let wrapper
|
14
|
+
|
15
|
+
beforeEach(() => {
|
16
|
+
wrapper = mount(InfoModal, {
|
17
|
+
props: {
|
18
|
+
isOpen: true,
|
19
|
+
buttonText: 'Close',
|
20
|
+
},
|
21
|
+
slots: {
|
22
|
+
title: 'Sample title',
|
23
|
+
body: 'Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed tincidunt commodo mollis. Fusce urna felis, malesuada sed elementum et, fermentum ac massa. Integer in massa vel orci fermentum bibendum in ut ante. Donec risus risus, luctus quis ex a, pulvinar placerat lacus. Sed pharetra augue a elit volutpat, eu dignissim ex pretium. Aenean imperdiet, nulla in pharetra rutrum, mauris mauris tincidunt tellus, non tempus quam lorem laoreet lectus.',
|
24
|
+
},
|
25
|
+
global: {
|
26
|
+
provide: {
|
27
|
+
theme,
|
28
|
+
},
|
29
|
+
},
|
30
|
+
})
|
31
|
+
})
|
32
|
+
|
33
|
+
test('renders InfoModal component with default props', () => {
|
34
|
+
expect(wrapper.find('[data-test-id="infoModal"]').exists()).toBe(true)
|
35
|
+
|
36
|
+
expect(wrapper.vm.isOpen).toBe(true)
|
37
|
+
expect(wrapper.vm.buttonText).toContain('Close')
|
38
|
+
})
|
39
|
+
|
40
|
+
test('info modal slots is display when user passed slots content', () => {
|
41
|
+
const modalTitleEl = wrapper.find('[data-test-id="modal_title"]')
|
42
|
+
expect(modalTitleEl.text()).toBe('Sample title')
|
43
|
+
|
44
|
+
const modalBodyEl = wrapper.find('[data-test-id="modal_body"]')
|
45
|
+
expect(modalBodyEl.text()).toContain('Lorem ipsum dolor sit amet')
|
46
|
+
})
|
47
|
+
|
48
|
+
test('info modal on-close event is emitted when modal close button is clicked', async () => {
|
49
|
+
const modalCloseButton = wrapper.find('.close')
|
50
|
+
|
51
|
+
modalCloseButton.trigger('click')
|
52
|
+
await wrapper.vm.$nextTick()
|
53
|
+
expect(wrapper.emitted('on-close')).toBeTruthy()
|
54
|
+
})
|
55
|
+
})
|
@@ -0,0 +1,47 @@
|
|
1
|
+
import InfoModal from './index.vue'
|
2
|
+
|
3
|
+
export default {
|
4
|
+
title: 'Components/InfoModal',
|
5
|
+
component: InfoModal,
|
6
|
+
tags: ['autodocs'],
|
7
|
+
parameters: {
|
8
|
+
layout: 'centered',
|
9
|
+
},
|
10
|
+
}
|
11
|
+
|
12
|
+
// import InfoModal from "@eturnity/eturnity_reusable_components/src/components/modals/infoModal"
|
13
|
+
// This is a reusable modal component that can be used to display information to the user.
|
14
|
+
// To use:
|
15
|
+
// <InfoModal :isOpen="isOpen" @on-close="$emit('on-close-summary')" >
|
16
|
+
// <template #title>
|
17
|
+
// <h1>Header</h1>
|
18
|
+
// </template>
|
19
|
+
// <template #body>
|
20
|
+
// <p>Body</p>
|
21
|
+
// </template>
|
22
|
+
// </InfoModal>
|
23
|
+
|
24
|
+
export const Default = {
|
25
|
+
args: {
|
26
|
+
isOpen: true,
|
27
|
+
buttonText: 'Close',
|
28
|
+
},
|
29
|
+
render: (args) => ({
|
30
|
+
components: { InfoModal },
|
31
|
+
setup() {
|
32
|
+
return { args }
|
33
|
+
},
|
34
|
+
template: `
|
35
|
+
<InfoModal v-bind="args">
|
36
|
+
<template #title>Sample title</template>
|
37
|
+
<template #body>
|
38
|
+
Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed tincidunt commodo mollis. Fusce urna felis,
|
39
|
+
malesuada sed elementum et, fermentum ac massa. Integer in massa vel orci fermentum bibendum in ut ante.
|
40
|
+
Donec risus risus, luctus quis ex a, pulvinar placerat lacus. Sed pharetra augue a elit volutpat, eu dignissim
|
41
|
+
ex pretium. Aenean imperdiet, nulla in pharetra rutrum, mauris mauris tincidunt tellus, non tempus quam lorem
|
42
|
+
laoreet lectus.
|
43
|
+
</template>
|
44
|
+
</InfoModal>
|
45
|
+
`,
|
46
|
+
}),
|
47
|
+
}
|
@@ -1,6 +1,7 @@
|
|
1
1
|
<template>
|
2
2
|
<PageWrapper
|
3
3
|
v-if="isOpen"
|
4
|
+
ref="modalRef"
|
4
5
|
:add-padding-top="addPaddingTop"
|
5
6
|
:backdrop="backdrop"
|
6
7
|
:is-open="isOpen"
|
@@ -36,6 +37,7 @@
|
|
36
37
|
// <div>Data....</div>
|
37
38
|
// </modal>
|
38
39
|
|
40
|
+
import { ref, provide } from 'vue'
|
39
41
|
import styled from 'vue3-styled-components'
|
40
42
|
import CloseButton from '../../buttons/closeButton'
|
41
43
|
import Spinner from '../../spinner'
|
@@ -58,14 +60,14 @@
|
|
58
60
|
props.backdrop == 'dark'
|
59
61
|
? 'rgba(0, 0, 0, 0.4)'
|
60
62
|
: 'rgba(255, 255, 255, 0.9)'};
|
61
|
-
z-index:
|
63
|
+
z-index: 9999999;
|
62
64
|
overflow: auto;
|
63
65
|
padding-top: ${(props) => (props.addPaddingTop ? '80px' : '0')};
|
64
66
|
|
65
|
-
|
66
|
-
|
67
|
-
|
68
|
-
`
|
67
|
+
@media (max-width: 425px) {
|
68
|
+
background: white;
|
69
|
+
}
|
70
|
+
`
|
69
71
|
|
70
72
|
const modalContainerAttrs = { overflow: String, isLoading: Boolean }
|
71
73
|
const ModalContainer = styled('div', modalContainerAttrs)`
|
@@ -163,6 +165,14 @@
|
|
163
165
|
default: false,
|
164
166
|
},
|
165
167
|
},
|
168
|
+
setup() {
|
169
|
+
const modalRef = ref(null)
|
170
|
+
provide('modalRef', modalRef)
|
171
|
+
|
172
|
+
return {
|
173
|
+
modalRef,
|
174
|
+
}
|
175
|
+
},
|
166
176
|
watch: {
|
167
177
|
isOpen: {
|
168
178
|
immediate: true,
|