@eturnity/eturnity_reusable_components 8.22.1 → 8.22.2
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/infoText/defaultProps.js +16 -0
- package/src/components/infoText/index.vue +9 -1
- package/src/components/infoText/infoText.spec.js +39 -20
- package/src/components/infoText/infoText.stories.js +28 -0
- package/src/components/infoText/templates/iconTextContent.vue +105 -0
- package/src/components/spinner/index.vue +4 -0
- package/src/components/spinnerGif/index.vue +8 -1
- package/src/components/tabsHeader/TabsHeader.spec.js +101 -0
- package/src/components/tabsHeader/index.vue +20 -7
package/package.json
CHANGED
@@ -0,0 +1,16 @@
|
|
1
|
+
export default {
|
2
|
+
text: 'default text',
|
3
|
+
size: '14px',
|
4
|
+
infoPosition: 'bottom',
|
5
|
+
alignArrow: 'center',
|
6
|
+
openTrigger: 'onHover',
|
7
|
+
width: '165px',
|
8
|
+
maxWidth: '165px',
|
9
|
+
shouldUseTeleport: false,
|
10
|
+
dotColor: '#00009f',
|
11
|
+
type: 'info',
|
12
|
+
iconTextContent: {
|
13
|
+
iconName: 'error',
|
14
|
+
text: 'Sample Text',
|
15
|
+
},
|
16
|
+
}
|
@@ -1,6 +1,7 @@
|
|
1
1
|
<template>
|
2
2
|
<PageContainer
|
3
3
|
ref="container"
|
4
|
+
data-test-id="infoText_container"
|
4
5
|
:type="type"
|
5
6
|
@click=";(isMobile || openTrigger === 'onClick') && toggleInfo()"
|
6
7
|
@mouseenter="!isMobile && openTrigger === 'onHover' && showInfo()"
|
@@ -287,6 +288,11 @@
|
|
287
288
|
default: '',
|
288
289
|
required: false,
|
289
290
|
},
|
291
|
+
contentBackgroundColor: {
|
292
|
+
type: String,
|
293
|
+
default: '',
|
294
|
+
required: false,
|
295
|
+
},
|
290
296
|
borderRadius: {
|
291
297
|
type: String,
|
292
298
|
default: '',
|
@@ -466,7 +472,9 @@
|
|
466
472
|
width: '100%',
|
467
473
|
maxWidth: props.maxWidth,
|
468
474
|
overflowY: 'auto',
|
469
|
-
backgroundColor: props.
|
475
|
+
backgroundColor: props.contentBackgroundColor
|
476
|
+
? props.contentBackgroundColor
|
477
|
+
: props.image
|
470
478
|
? theme.colors.white
|
471
479
|
: props.appTheme === 'light'
|
472
480
|
? theme.colors.black
|
@@ -1,7 +1,8 @@
|
|
1
1
|
/* eslint-disable */
|
2
2
|
import { mount } from '@vue/test-utils'
|
3
3
|
import InfoText from '@/components/infoText'
|
4
|
-
import
|
4
|
+
import defaultProps from './defaultProps'
|
5
|
+
import IconTextContent from './templates/iconTextContent.vue'
|
5
6
|
|
6
7
|
jest.mock('@/components/icon/iconCache.mjs', () => ({
|
7
8
|
// need to mock this due to how jest handles import.meta
|
@@ -13,31 +14,19 @@ describe('InfoText Component', () => {
|
|
13
14
|
|
14
15
|
beforeEach(() => {
|
15
16
|
wrapper = mount(InfoText, {
|
16
|
-
props:
|
17
|
-
text: 'default text',
|
18
|
-
size: '14px',
|
19
|
-
infoPosition: 'bottom',
|
20
|
-
alignArrow: 'center',
|
21
|
-
openTrigger: 'onHover',
|
22
|
-
width: '165px',
|
23
|
-
maxWidth: '165px',
|
24
|
-
shouldUseTeleport: false,
|
25
|
-
dotColor: '#00009f',
|
26
|
-
type: 'info',
|
27
|
-
},
|
17
|
+
props: defaultProps,
|
28
18
|
global: {
|
29
|
-
provide: {
|
30
|
-
theme,
|
31
|
-
},
|
32
19
|
stubs: {
|
33
|
-
teleport: true
|
34
|
-
}
|
20
|
+
teleport: true,
|
21
|
+
},
|
35
22
|
},
|
36
23
|
})
|
37
24
|
})
|
38
25
|
|
39
26
|
test('renders InfoText component with default props', () => {
|
40
|
-
expect(wrapper.find('[data-test-id="
|
27
|
+
expect(wrapper.find('[data-test-id="infoText_container"]').exists()).toBe(
|
28
|
+
true
|
29
|
+
)
|
41
30
|
expect(wrapper.vm.text).toContain('default text')
|
42
31
|
expect(wrapper.vm.size).toContain('14px')
|
43
32
|
expect(wrapper.vm.infoPosition).toContain('bottom')
|
@@ -52,7 +41,7 @@ describe('InfoText Component', () => {
|
|
52
41
|
)
|
53
42
|
|
54
43
|
//should see text upon click
|
55
|
-
await wrapper.
|
44
|
+
await wrapper.trigger('click')
|
56
45
|
expect(wrapper.vm.isVisible).toBe(true)
|
57
46
|
|
58
47
|
expect(wrapper.find('[data-test-id="info_text_wrapper"]').exists()).toBe(
|
@@ -64,4 +53,34 @@ describe('InfoText Component', () => {
|
|
64
53
|
await wrapper.setProps({ type: 'dot' })
|
65
54
|
expect(wrapper.find('[data-test-id="infoText_dot"]').exists()).toBe(true)
|
66
55
|
})
|
56
|
+
|
57
|
+
test('iconTextContent slot is correctly rendered', async () => {
|
58
|
+
// override
|
59
|
+
const props = defaultProps.iconTextContent
|
60
|
+
wrapper = mount(InfoText, {
|
61
|
+
props: defaultProps,
|
62
|
+
slots: {
|
63
|
+
default: `<IconTextContent icon-name="${props.iconName}" text="${props.text}" />`,
|
64
|
+
},
|
65
|
+
global: {
|
66
|
+
stubs: {
|
67
|
+
teleport: true,
|
68
|
+
},
|
69
|
+
components: {
|
70
|
+
IconTextContent,
|
71
|
+
},
|
72
|
+
},
|
73
|
+
})
|
74
|
+
|
75
|
+
await wrapper.trigger('mouseenter')
|
76
|
+
expect(wrapper.vm.isVisible).toBe(true)
|
77
|
+
|
78
|
+
const container = wrapper.find('[data-test-id="iconTextContent_container"]')
|
79
|
+
const icon = container.find('[data-test-id="iconTextContent_icon"]')
|
80
|
+
const text = container.find('[data-test-id="iconTextContent_text"]')
|
81
|
+
expect(container.exists()).toBe(true)
|
82
|
+
expect(icon.exists()).toBe(true)
|
83
|
+
expect(text.exists()).toBe(true)
|
84
|
+
expect(text.text()).toBe(props.text)
|
85
|
+
})
|
67
86
|
})
|
@@ -1,4 +1,6 @@
|
|
1
|
+
import theme from '../../assets/theme'
|
1
2
|
import InfoText from './index.vue'
|
3
|
+
import IconTextContent from './templates/iconTextContent.vue'
|
2
4
|
|
3
5
|
export default {
|
4
6
|
title: 'infoText',
|
@@ -46,3 +48,29 @@ export const OnClick = {
|
|
46
48
|
openTrigger: 'onClick',
|
47
49
|
},
|
48
50
|
}
|
51
|
+
|
52
|
+
export const IconTextContentTemplate = {
|
53
|
+
args: {
|
54
|
+
...Default.args,
|
55
|
+
contentBackgroundColor: 'black',
|
56
|
+
iconName: 'warning_triangle',
|
57
|
+
iconColor: theme.colors.yellow,
|
58
|
+
size: '18px',
|
59
|
+
},
|
60
|
+
render: (args) => ({
|
61
|
+
components: { InfoText, IconTextContent },
|
62
|
+
setup() {
|
63
|
+
return { args }
|
64
|
+
},
|
65
|
+
template: `
|
66
|
+
<InfoText v-bind="args">
|
67
|
+
<IconTextContent
|
68
|
+
icon-name="warning_triangle"
|
69
|
+
text="This is a sample text with an icon"
|
70
|
+
icon-size="${args.size}"
|
71
|
+
icon-color="${args.iconColor}"
|
72
|
+
/>
|
73
|
+
</InfoText>
|
74
|
+
`,
|
75
|
+
}),
|
76
|
+
}
|
@@ -0,0 +1,105 @@
|
|
1
|
+
<template>
|
2
|
+
<Container data-test-id="iconTextContent_container">
|
3
|
+
<RCIcon
|
4
|
+
:color="iconColor"
|
5
|
+
data-test-id="iconTextContent_icon"
|
6
|
+
:name="iconName"
|
7
|
+
:size="iconSize"
|
8
|
+
/>
|
9
|
+
<TextWrapper
|
10
|
+
data-test-id="iconTextContent_text"
|
11
|
+
:font-color="fontColor"
|
12
|
+
:font-size="fontSize"
|
13
|
+
>
|
14
|
+
{{ text }}
|
15
|
+
<slot data-test-id="iconTextContent_slot"></slot>
|
16
|
+
</TextWrapper>
|
17
|
+
</Container>
|
18
|
+
</template>
|
19
|
+
|
20
|
+
<script>
|
21
|
+
// import InfoText from "@eturnity/eturnity_reusable_components/src/components/infoText"
|
22
|
+
// import IconTextContent from "@eturnity/eturnity_reusable_components/src/components/infoText/templates/iconTextContent"
|
23
|
+
//To use:
|
24
|
+
// <InfoText
|
25
|
+
// icon-color="red"
|
26
|
+
// icon-name="error"
|
27
|
+
// size="20px"
|
28
|
+
// open-trigger="onClick"
|
29
|
+
// >
|
30
|
+
// <IconTextContent
|
31
|
+
// icon-name="error"
|
32
|
+
// text="Text"
|
33
|
+
// icon-size="18px"
|
34
|
+
// icon-color="red"
|
35
|
+
// />
|
36
|
+
// </InfoText>
|
37
|
+
|
38
|
+
import styled from 'vue3-styled-components'
|
39
|
+
import theme from '../../../assets/theme.js'
|
40
|
+
import RCIcon from '../../icon'
|
41
|
+
|
42
|
+
const Container = styled('div')`
|
43
|
+
display: flex;
|
44
|
+
flex-direction: row;
|
45
|
+
align-items: flex-start;
|
46
|
+
gap: 8px;
|
47
|
+
`
|
48
|
+
|
49
|
+
const TextAttrs = {
|
50
|
+
fontSize: String,
|
51
|
+
fontColor: String,
|
52
|
+
}
|
53
|
+
const TextWrapper = styled('div', TextAttrs)`
|
54
|
+
font-size: ${(props) => props.fontSize};
|
55
|
+
color: ${(props) => props.fontColor};
|
56
|
+
overflow: hidden;
|
57
|
+
display: flex;
|
58
|
+
flex-direction: column;
|
59
|
+
align-items: center;
|
60
|
+
gap: 8px;
|
61
|
+
font-weight: 400;
|
62
|
+
line-height: 150%;
|
63
|
+
letter-spacing: 0%;
|
64
|
+
vertical-align: middle;
|
65
|
+
`
|
66
|
+
|
67
|
+
export default {
|
68
|
+
name: 'IconTextContent',
|
69
|
+
components: {
|
70
|
+
Container,
|
71
|
+
TextWrapper,
|
72
|
+
RCIcon,
|
73
|
+
},
|
74
|
+
props: {
|
75
|
+
iconName: {
|
76
|
+
type: String,
|
77
|
+
required: true,
|
78
|
+
},
|
79
|
+
text: {
|
80
|
+
type: String,
|
81
|
+
required: true,
|
82
|
+
},
|
83
|
+
iconSize: {
|
84
|
+
type: String,
|
85
|
+
required: false,
|
86
|
+
default: '18px',
|
87
|
+
},
|
88
|
+
iconColor: {
|
89
|
+
type: String,
|
90
|
+
required: false,
|
91
|
+
default: '',
|
92
|
+
},
|
93
|
+
fontSize: {
|
94
|
+
type: String,
|
95
|
+
required: false,
|
96
|
+
default: '11px',
|
97
|
+
},
|
98
|
+
fontColor: {
|
99
|
+
type: String,
|
100
|
+
required: false,
|
101
|
+
default: theme.colors.white,
|
102
|
+
},
|
103
|
+
},
|
104
|
+
}
|
105
|
+
</script>
|
@@ -1,6 +1,8 @@
|
|
1
1
|
<template>
|
2
2
|
<SpinnerContainer
|
3
3
|
v-if="fullWidth"
|
4
|
+
data-id="spinner_full_container"
|
5
|
+
data-qa-id="spinner_full_container"
|
4
6
|
data-test-id="spinner_full_container"
|
5
7
|
:size="size"
|
6
8
|
>
|
@@ -12,6 +14,8 @@
|
|
12
14
|
</SpinnerContainer>
|
13
15
|
<Container
|
14
16
|
v-else
|
17
|
+
data-id="spinner_container"
|
18
|
+
data-qa-id="spinner_container"
|
15
19
|
data-test-id="spinner_container"
|
16
20
|
:limited-to-modal="limitedToModal"
|
17
21
|
>
|
@@ -1,5 +1,10 @@
|
|
1
1
|
<template>
|
2
|
-
<SpinnerContainer
|
2
|
+
<SpinnerContainer
|
3
|
+
v-if="fullWidth"
|
4
|
+
data-id="spinner_full_container"
|
5
|
+
data-qa-id="spinner_full_container"
|
6
|
+
data-test-id="spinner_full_container"
|
7
|
+
>
|
3
8
|
<Container>
|
4
9
|
<SpinnerWrapper data-test-id="spinner_full_wrapper" :size="size">
|
5
10
|
<img
|
@@ -13,6 +18,8 @@
|
|
13
18
|
</SpinnerContainer>
|
14
19
|
<Container
|
15
20
|
v-else
|
21
|
+
data-id="spinner_container"
|
22
|
+
data-qa-id="spinner_container"
|
16
23
|
data-test-id="spinner_container"
|
17
24
|
:limited-to-modal="limitedToModal"
|
18
25
|
>
|
@@ -0,0 +1,101 @@
|
|
1
|
+
/* eslint-disable */
|
2
|
+
import { mount } from '@vue/test-utils'
|
3
|
+
import TabsHeader from './index.vue'
|
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
|
+
describe('TabsHeader', () => {
|
12
|
+
const mockTabsData = [
|
13
|
+
{
|
14
|
+
text: 'Tab 1',
|
15
|
+
id: 0,
|
16
|
+
hasError: true,
|
17
|
+
icon: 'home',
|
18
|
+
subText: 'Subtext 1',
|
19
|
+
},
|
20
|
+
{
|
21
|
+
text: 'Tab 2',
|
22
|
+
id: 1,
|
23
|
+
hasError: false,
|
24
|
+
icon: 'settings',
|
25
|
+
subText: 'Subtext 2',
|
26
|
+
},
|
27
|
+
]
|
28
|
+
|
29
|
+
const createWrapper = (props = {}) => {
|
30
|
+
return mount(TabsHeader, {
|
31
|
+
props: {
|
32
|
+
tabsData: mockTabsData,
|
33
|
+
activeTab: 0,
|
34
|
+
fullSize: true,
|
35
|
+
...props,
|
36
|
+
},
|
37
|
+
global: {
|
38
|
+
mocks: {
|
39
|
+
theme,
|
40
|
+
},
|
41
|
+
},
|
42
|
+
})
|
43
|
+
}
|
44
|
+
|
45
|
+
it('renders all tabs correctly', () => {
|
46
|
+
const wrapper = createWrapper()
|
47
|
+
const tabs = wrapper.findAll('[data-test-id="tab-item"]')
|
48
|
+
expect(tabs).toHaveLength(mockTabsData.length)
|
49
|
+
})
|
50
|
+
|
51
|
+
it('displays correct tab text', () => {
|
52
|
+
const wrapper = createWrapper()
|
53
|
+
const firstTab = wrapper.find('[data-test-id="tab-text"]')
|
54
|
+
expect(firstTab.text()).toBe('Tab 1')
|
55
|
+
})
|
56
|
+
|
57
|
+
it('emits on-tab-change event when clicking a tab', async () => {
|
58
|
+
const wrapper = createWrapper()
|
59
|
+
const tabs = wrapper.findAll('[data-test-id="tab-item"]')
|
60
|
+
await tabs[1].trigger('click')
|
61
|
+
expect(wrapper.emitted('on-tab-change')).toBeTruthy()
|
62
|
+
expect(wrapper.emitted('on-tab-change')[0]).toEqual([1])
|
63
|
+
})
|
64
|
+
|
65
|
+
it('does not emit on-tab-change when clicking active tab', async () => {
|
66
|
+
const wrapper = createWrapper()
|
67
|
+
const activeTab = wrapper.find('[data-test-id="tab-item"]')
|
68
|
+
await activeTab.trigger('click')
|
69
|
+
expect(wrapper.emitted('on-tab-change')).toBeFalsy()
|
70
|
+
})
|
71
|
+
|
72
|
+
it('applies correct styles for active tab', () => {
|
73
|
+
const wrapper = createWrapper()
|
74
|
+
const activeTab = wrapper.find('[data-test-active="true"]')
|
75
|
+
expect(activeTab.exists()).toBe(true)
|
76
|
+
})
|
77
|
+
|
78
|
+
it('renders error icon when tab has error', () => {
|
79
|
+
const wrapper = createWrapper()
|
80
|
+
const warningIcon = wrapper.find('[data-test-id="warning-icon"]')
|
81
|
+
expect(warningIcon.exists()).toBe(true)
|
82
|
+
})
|
83
|
+
|
84
|
+
it('renders subtext when provided', () => {
|
85
|
+
const wrapper = createWrapper()
|
86
|
+
const subtext = wrapper.find('[data-test-id="tab-subtext"]')
|
87
|
+
expect(subtext.text()).toBe('Subtext 1')
|
88
|
+
})
|
89
|
+
|
90
|
+
it('renders icon when provided', () => {
|
91
|
+
const wrapper = createWrapper()
|
92
|
+
const icon = wrapper.find('[data-test-id="tab-icon"]')
|
93
|
+
expect(icon.exists()).toBe(true)
|
94
|
+
})
|
95
|
+
|
96
|
+
it('applies fullSize prop correctly', () => {
|
97
|
+
const wrapper = createWrapper({ fullSize: true })
|
98
|
+
const tabs = wrapper.findAll('[data-test-id="tab-item"]')
|
99
|
+
expect(tabs.length).toBeGreaterThan(0)
|
100
|
+
})
|
101
|
+
})
|
@@ -1,10 +1,12 @@
|
|
1
1
|
<template>
|
2
|
-
<PageContainer>
|
3
|
-
<TabsContainer>
|
2
|
+
<PageContainer data-test-id="tabs-header-container">
|
3
|
+
<TabsContainer data-test-id="tabs-container">
|
4
4
|
<TabItem
|
5
5
|
v-for="item in tabsData"
|
6
6
|
:key="item.id"
|
7
7
|
:data-id="item.dataId"
|
8
|
+
:data-test-active="activeTab === item.id"
|
9
|
+
data-test-id="tab-item"
|
8
10
|
:full-size="fullSize"
|
9
11
|
:is-active="activeTab === item.id"
|
10
12
|
@click="onTabClick({ id: item.id })"
|
@@ -16,6 +18,7 @@
|
|
16
18
|
? theme.semanticColors.purple[500]
|
17
19
|
: theme.semanticColors.teal[800]
|
18
20
|
"
|
21
|
+
data-test-id="tab-icon"
|
19
22
|
:hovered-color="
|
20
23
|
activeTab === item.id
|
21
24
|
? theme.semanticColors.purple[500]
|
@@ -24,10 +27,21 @@
|
|
24
27
|
:name="item.icon"
|
25
28
|
size="14px"
|
26
29
|
/>
|
27
|
-
<div>{{ item.text }}</div>
|
28
|
-
<DotIcon
|
29
|
-
|
30
|
-
|
30
|
+
<div data-test-id="tab-text">{{ item.text }}</div>
|
31
|
+
<DotIcon
|
32
|
+
v-if="item.subText"
|
33
|
+
data-test-id="dot-icon"
|
34
|
+
:is-active="activeTab === item.id"
|
35
|
+
/>
|
36
|
+
<SubText v-if="item.subText" data-test-id="tab-subtext">{{
|
37
|
+
item.subText
|
38
|
+
}}</SubText>
|
39
|
+
<RCIcon
|
40
|
+
v-if="item.hasError"
|
41
|
+
data-test-id="warning-icon"
|
42
|
+
name="warning"
|
43
|
+
size="14px"
|
44
|
+
/>
|
31
45
|
</TabItem>
|
32
46
|
</TabsContainer>
|
33
47
|
</PageContainer>
|
@@ -112,7 +126,6 @@
|
|
112
126
|
components: {
|
113
127
|
PageContainer,
|
114
128
|
TabsContainer,
|
115
|
-
NameContainer,
|
116
129
|
TabItem,
|
117
130
|
SubText,
|
118
131
|
DotIcon,
|