@nexxtmove/ui 0.1.26 → 0.1.28
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/dist/index.d.ts +9 -0
- package/dist/index.js +448 -378
- package/dist/nuxt.js +17 -16
- package/package.json +2 -2
- package/src/components/CustomerJourneyTile/CustomerJourneyTile.stories.ts +86 -0
- package/src/components/CustomerJourneyTile/CustomerJourneyTile.test.ts +178 -0
- package/src/components/CustomerJourneyTile/CustomerJourneyTile.vue +81 -0
- package/src/components/Header/Header.stories.ts +6 -0
- package/src/components/Header/Header.test.ts +15 -1
- package/src/components/Header/Header.vue +8 -4
- package/src/components.json +1 -0
- package/src/index.ts +1 -0
package/dist/nuxt.js
CHANGED
|
@@ -1,23 +1,24 @@
|
|
|
1
|
-
import { defineNuxtModule as
|
|
2
|
-
const a = "components/AnimatedNumber/AnimatedNumber.vue", m = "components/Button/Button.vue", x = "components/Calendar/Calendar.vue",
|
|
1
|
+
import { defineNuxtModule as s, addComponent as c, addTemplate as i } from "@nuxt/kit";
|
|
2
|
+
const a = "components/AnimatedNumber/AnimatedNumber.vue", m = "components/Button/Button.vue", x = "components/Calendar/Calendar.vue", u = "components/Chip/Chip.vue", p = "components/CustomerJourneyTile/CustomerJourneyTile.vue", l = "components/DatePicker/DatePicker.vue", r = "components/Header/Header.vue", d = "components/Icon/Icon.vue", v = "components/InfoBlock/InfoBlock.vue", N = "components/ProgressBar/ProgressBar.vue", T = "components/SocialIcons/SocialIcons.vue", S = "components/SocialMediaCustomTemplate/SocialMediaCustomTemplate.vue", C = "components/SocialMediaTemplate/SocialMediaTemplate.vue", f = "components/SocialMediaType/SocialMediaType.vue", h = "components/StepperHeader/StepperHeader.vue", M = "components/TimelineEvent/TimelineEvent.vue", P = "components/TimelinePhaseblock/TimelinePhaseblock.vue", b = "components/Tooltip/Tooltip.vue", k = {
|
|
3
3
|
NexxtAnimatedNumber: a,
|
|
4
4
|
NexxtButton: m,
|
|
5
5
|
NexxtCalendar: x,
|
|
6
|
-
NexxtChip:
|
|
6
|
+
NexxtChip: u,
|
|
7
|
+
NexxtCustomerJourneyTile: p,
|
|
7
8
|
NexxtDatePicker: l,
|
|
8
|
-
NexxtHeader:
|
|
9
|
+
NexxtHeader: r,
|
|
9
10
|
NexxtIcon: d,
|
|
10
|
-
NexxtInfoBlock:
|
|
11
|
-
NexxtProgressBar:
|
|
12
|
-
NexxtSocialIcons:
|
|
11
|
+
NexxtInfoBlock: v,
|
|
12
|
+
NexxtProgressBar: N,
|
|
13
|
+
NexxtSocialIcons: T,
|
|
13
14
|
NexxtSocialMediaCustomTemplate: S,
|
|
14
|
-
NexxtSocialMediaTemplate:
|
|
15
|
+
NexxtSocialMediaTemplate: C,
|
|
15
16
|
NexxtSocialMediaType: f,
|
|
16
|
-
NexxtStepperHeader:
|
|
17
|
-
NexxtTimelineEvent:
|
|
18
|
-
NexxtTimelinePhaseblock:
|
|
19
|
-
NexxtTooltip:
|
|
20
|
-
},
|
|
17
|
+
NexxtStepperHeader: h,
|
|
18
|
+
NexxtTimelineEvent: M,
|
|
19
|
+
NexxtTimelinePhaseblock: P,
|
|
20
|
+
NexxtTooltip: b
|
|
21
|
+
}, I = s({
|
|
21
22
|
meta: {
|
|
22
23
|
name: "@nexxtmove/ui",
|
|
23
24
|
configKey: "nexxtmoveUi",
|
|
@@ -29,8 +30,8 @@ const a = "components/AnimatedNumber/AnimatedNumber.vue", m = "components/Button
|
|
|
29
30
|
addCSS: !0
|
|
30
31
|
},
|
|
31
32
|
setup(e, o) {
|
|
32
|
-
for (const [t, n] of Object.entries(
|
|
33
|
-
|
|
33
|
+
for (const [t, n] of Object.entries(k))
|
|
34
|
+
c({
|
|
34
35
|
name: t,
|
|
35
36
|
export: "default",
|
|
36
37
|
filePath: `@nexxtmove/ui/${n}`
|
|
@@ -48,5 +49,5 @@ const a = "components/AnimatedNumber/AnimatedNumber.vue", m = "components/Button
|
|
|
48
49
|
}
|
|
49
50
|
});
|
|
50
51
|
export {
|
|
51
|
-
|
|
52
|
+
I as default
|
|
52
53
|
};
|
package/package.json
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@nexxtmove/ui",
|
|
3
3
|
"type": "module",
|
|
4
|
-
"version": "0.1.
|
|
4
|
+
"version": "0.1.28",
|
|
5
5
|
"exports": {
|
|
6
6
|
".": {
|
|
7
7
|
"types": "./dist/index.d.ts",
|
|
@@ -29,7 +29,7 @@
|
|
|
29
29
|
"vue": "^3.0.0"
|
|
30
30
|
},
|
|
31
31
|
"devDependencies": {
|
|
32
|
-
"@awesome.me/kit-37b149f3ef": "^1.0.
|
|
32
|
+
"@awesome.me/kit-37b149f3ef": "^1.0.8",
|
|
33
33
|
"@eslint/js": "^9.39.2",
|
|
34
34
|
"@nuxt/kit": "^4.3.1",
|
|
35
35
|
"@nuxt/schema": "^4.3.1",
|
|
@@ -0,0 +1,86 @@
|
|
|
1
|
+
import type { Meta, StoryObj } from '@storybook/vue3-vite'
|
|
2
|
+
import CustomerJourneyTile from './CustomerJourneyTile.vue'
|
|
3
|
+
|
|
4
|
+
export default {
|
|
5
|
+
title: 'Components/Atoms/Customer Journey Tile',
|
|
6
|
+
component: CustomerJourneyTile,
|
|
7
|
+
} satisfies Meta<typeof CustomerJourneyTile>
|
|
8
|
+
|
|
9
|
+
type Story = StoryObj<typeof CustomerJourneyTile>
|
|
10
|
+
|
|
11
|
+
export const Default: Story = {
|
|
12
|
+
args: {
|
|
13
|
+
title: 'Latent',
|
|
14
|
+
amount: 270,
|
|
15
|
+
percentage: 14,
|
|
16
|
+
percentageExplanaition: 'vorige maand',
|
|
17
|
+
},
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
export const CustomTitle: Story = {
|
|
21
|
+
args: {
|
|
22
|
+
title: 'Custom Title',
|
|
23
|
+
amount: 270,
|
|
24
|
+
percentage: -8,
|
|
25
|
+
percentageExplanaition: 'vorige maand',
|
|
26
|
+
},
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
export const NegativePercentage: Story = {
|
|
30
|
+
args: {
|
|
31
|
+
title: 'Latent',
|
|
32
|
+
amount: 270,
|
|
33
|
+
percentage: -8,
|
|
34
|
+
percentageExplanaition: 'vorige maand',
|
|
35
|
+
},
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
export const WithoutPercentage: Story = {
|
|
39
|
+
args: {
|
|
40
|
+
title: 'Wonen',
|
|
41
|
+
amount: 54,
|
|
42
|
+
},
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
export const WithoutAmount: Story = {
|
|
46
|
+
args: {
|
|
47
|
+
title: 'Actief zoeken',
|
|
48
|
+
percentage: 5,
|
|
49
|
+
percentageExplanaition: 'vorige maand',
|
|
50
|
+
},
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
export const Orientatie: Story = {
|
|
54
|
+
args: {
|
|
55
|
+
title: 'Oriëntatie',
|
|
56
|
+
amount: 142,
|
|
57
|
+
percentage: 3,
|
|
58
|
+
percentageExplanaition: 'vorige maand',
|
|
59
|
+
},
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
export const Selectie: Story = {
|
|
63
|
+
args: {
|
|
64
|
+
title: 'Selectie',
|
|
65
|
+
amount: 89,
|
|
66
|
+
percentage: -2,
|
|
67
|
+
percentageExplanaition: 'vorige maand',
|
|
68
|
+
},
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
export const Transactie: Story = {
|
|
72
|
+
args: {
|
|
73
|
+
title: 'Transactie',
|
|
74
|
+
amount: 31,
|
|
75
|
+
percentage: 21,
|
|
76
|
+
percentageExplanaition: 'vorige maand',
|
|
77
|
+
},
|
|
78
|
+
}
|
|
79
|
+
|
|
80
|
+
export const WithoutPhase: Story = {
|
|
81
|
+
args: {
|
|
82
|
+
amount: 270,
|
|
83
|
+
percentage: 14,
|
|
84
|
+
percentageExplanaition: 'vorige maand',
|
|
85
|
+
},
|
|
86
|
+
}
|
|
@@ -0,0 +1,178 @@
|
|
|
1
|
+
import { describe, it, expect } from 'vitest'
|
|
2
|
+
import { mount } from '@vue/test-utils'
|
|
3
|
+
import CustomerJourneyTile from './CustomerJourneyTile.vue'
|
|
4
|
+
|
|
5
|
+
const stubs = { NexxtIcon: { template: '<span :data-name="name" />', props: ['name'] } }
|
|
6
|
+
|
|
7
|
+
const mountTile = (props = {}) =>
|
|
8
|
+
mount(CustomerJourneyTile, {
|
|
9
|
+
props: { ...props },
|
|
10
|
+
global: { stubs },
|
|
11
|
+
})
|
|
12
|
+
|
|
13
|
+
describe('CustomerJourneyTile', () => {
|
|
14
|
+
describe('rendering', () => {
|
|
15
|
+
it('renders a button element', () => {
|
|
16
|
+
const wrapper = mountTile({ title: 'Latent' })
|
|
17
|
+
expect(wrapper.element.tagName).toBe('BUTTON')
|
|
18
|
+
expect(wrapper.attributes('type')).toBe('button')
|
|
19
|
+
})
|
|
20
|
+
|
|
21
|
+
it('renders the title label', () => {
|
|
22
|
+
const wrapper = mountTile({ title: 'Wonen' })
|
|
23
|
+
expect(wrapper.text()).toContain('Wonen')
|
|
24
|
+
})
|
|
25
|
+
|
|
26
|
+
it('does not render title label when title is omitted', () => {
|
|
27
|
+
const wrapper = mountTile({ amount: 100 })
|
|
28
|
+
expect(wrapper.text()).not.toContain('Latent')
|
|
29
|
+
expect(wrapper.find('span:not(.heading-1)').exists()).toBe(false)
|
|
30
|
+
})
|
|
31
|
+
|
|
32
|
+
it('does not render the icon div when title is omitted', () => {
|
|
33
|
+
const wrapper = mountTile({ amount: 100 })
|
|
34
|
+
expect(wrapper.find('.bg-purple-100').exists()).toBe(false)
|
|
35
|
+
})
|
|
36
|
+
|
|
37
|
+
it('does not render the icon div when title does not match a phase', () => {
|
|
38
|
+
const wrapper = mountTile({ title: 'Custom Title' })
|
|
39
|
+
expect(wrapper.find('.bg-purple-100').exists()).toBe(false)
|
|
40
|
+
expect(wrapper.text()).toContain('Custom Title')
|
|
41
|
+
})
|
|
42
|
+
|
|
43
|
+
it('renders the correct icon for each phase-matching title', () => {
|
|
44
|
+
const cases: Array<[string, string]> = [
|
|
45
|
+
['Latent', 'eye-closed'],
|
|
46
|
+
['Oriëntatie', 'building-magnifying-glass'],
|
|
47
|
+
['Actief zoeken', 'house-person-return'],
|
|
48
|
+
['Selectie', 'handshake'],
|
|
49
|
+
['Transactie', 'pencil-mechanical'],
|
|
50
|
+
['Wonen', 'house-heart'],
|
|
51
|
+
]
|
|
52
|
+
for (const [title, expectedIcon] of cases) {
|
|
53
|
+
const wrapper = mountTile({ title })
|
|
54
|
+
expect(wrapper.find(`[data-name="${expectedIcon}"]`).exists()).toBe(true)
|
|
55
|
+
}
|
|
56
|
+
})
|
|
57
|
+
|
|
58
|
+
it('marks the icon as aria-hidden', () => {
|
|
59
|
+
const wrapper = mountTile({ title: 'Latent' })
|
|
60
|
+
expect(wrapper.find('[aria-hidden="true"]').exists()).toBe(true)
|
|
61
|
+
})
|
|
62
|
+
})
|
|
63
|
+
|
|
64
|
+
describe('accessibility', () => {
|
|
65
|
+
it('has an aria-label with the title', () => {
|
|
66
|
+
const wrapper = mountTile({ title: 'Wonen' })
|
|
67
|
+
expect(wrapper.attributes('aria-label')).toBe('Wonen')
|
|
68
|
+
})
|
|
69
|
+
|
|
70
|
+
it('includes amount in the aria-label', () => {
|
|
71
|
+
const wrapper = mountTile({ title: 'Latent', amount: 270 })
|
|
72
|
+
expect(wrapper.attributes('aria-label')).toBe('Latent, 270 contacten')
|
|
73
|
+
})
|
|
74
|
+
|
|
75
|
+
it('includes positive percentage in the aria-label', () => {
|
|
76
|
+
const wrapper = mountTile({
|
|
77
|
+
title: 'Latent',
|
|
78
|
+
percentage: 14,
|
|
79
|
+
percentageExplanaition: 'vorige maand',
|
|
80
|
+
})
|
|
81
|
+
expect(wrapper.attributes('aria-label')).toBe('Latent, +14% vorige maand')
|
|
82
|
+
})
|
|
83
|
+
|
|
84
|
+
it('includes negative percentage in the aria-label', () => {
|
|
85
|
+
const wrapper = mountTile({ title: 'Latent', percentage: -8 })
|
|
86
|
+
expect(wrapper.attributes('aria-label')).toBe('Latent, -8%')
|
|
87
|
+
})
|
|
88
|
+
|
|
89
|
+
it('has a visible focus ring via focus-visible classes', () => {
|
|
90
|
+
const wrapper = mountTile({ title: 'Latent' })
|
|
91
|
+
expect(wrapper.classes()).toContain('focus-visible:outline-2')
|
|
92
|
+
expect(wrapper.classes()).toContain('focus-visible:outline-offset-2')
|
|
93
|
+
expect(wrapper.classes()).toContain('focus-visible:outline-cornflower-blue-500')
|
|
94
|
+
})
|
|
95
|
+
|
|
96
|
+
it('applies standard padding when no icon is present', () => {
|
|
97
|
+
const wrapper = mountTile({ amount: 100 })
|
|
98
|
+
expect(wrapper.classes()).toContain('p-4')
|
|
99
|
+
expect(wrapper.classes()).not.toContain('gap-6')
|
|
100
|
+
expect(wrapper.classes()).not.toContain('p-1.5')
|
|
101
|
+
})
|
|
102
|
+
|
|
103
|
+
it('applies layout gap and small padding when icon is present', () => {
|
|
104
|
+
const wrapper = mountTile({ title: 'Latent', amount: 100 })
|
|
105
|
+
expect(wrapper.classes()).toContain('gap-6')
|
|
106
|
+
expect(wrapper.classes()).toContain('p-1.5')
|
|
107
|
+
expect(wrapper.classes()).not.toContain('p-4')
|
|
108
|
+
})
|
|
109
|
+
})
|
|
110
|
+
|
|
111
|
+
describe('amount', () => {
|
|
112
|
+
it('renders the amount when provided', () => {
|
|
113
|
+
const wrapper = mountTile({ title: 'Latent', amount: 270 })
|
|
114
|
+
expect(wrapper.text()).toContain('270')
|
|
115
|
+
})
|
|
116
|
+
|
|
117
|
+
it('renders the amount with purple color when a phase-matching title is provided', () => {
|
|
118
|
+
const wrapper = mountTile({ title: 'Latent', amount: 270 })
|
|
119
|
+
expect(wrapper.find('.heading-1').classes()).toContain('text-purple-500')
|
|
120
|
+
})
|
|
121
|
+
|
|
122
|
+
it('renders the amount with standard text color when title is not a known phase', () => {
|
|
123
|
+
const wrapper = mountTile({ title: 'Custom Title', amount: 270 })
|
|
124
|
+
expect(wrapper.find('.heading-1').classes()).toContain('text-sapphire-900')
|
|
125
|
+
})
|
|
126
|
+
|
|
127
|
+
it('renders the amount with standard text color when title is omitted', () => {
|
|
128
|
+
const wrapper = mountTile({ amount: 270 })
|
|
129
|
+
expect(wrapper.find('.heading-1').classes()).toContain('text-sapphire-900')
|
|
130
|
+
})
|
|
131
|
+
|
|
132
|
+
it('does not render an amount element when omitted', () => {
|
|
133
|
+
const wrapper = mountTile({ title: 'Latent' })
|
|
134
|
+
expect(wrapper.find('.heading-1').exists()).toBe(false)
|
|
135
|
+
})
|
|
136
|
+
})
|
|
137
|
+
|
|
138
|
+
describe('percentage', () => {
|
|
139
|
+
it('renders a positive percentage with a + prefix', () => {
|
|
140
|
+
const wrapper = mountTile({ percentage: 14 })
|
|
141
|
+
expect(wrapper.text()).toContain('+14%')
|
|
142
|
+
})
|
|
143
|
+
|
|
144
|
+
it('renders a negative percentage without a + prefix', () => {
|
|
145
|
+
const wrapper = mountTile({ percentage: -8 })
|
|
146
|
+
expect(wrapper.text()).toContain('-8%')
|
|
147
|
+
expect(wrapper.text()).not.toContain('+-8%')
|
|
148
|
+
})
|
|
149
|
+
|
|
150
|
+
it('applies green color class for a positive percentage', () => {
|
|
151
|
+
const wrapper = mountTile({ percentage: 14 })
|
|
152
|
+
expect(wrapper.find('.text-green-500').exists()).toBe(true)
|
|
153
|
+
})
|
|
154
|
+
|
|
155
|
+
it('applies red color class for a negative percentage', () => {
|
|
156
|
+
const wrapper = mountTile({ percentage: -8 })
|
|
157
|
+
expect(wrapper.find('.text-brick-500').exists()).toBe(true)
|
|
158
|
+
})
|
|
159
|
+
|
|
160
|
+
it('does not render the percentage block when omitted', () => {
|
|
161
|
+
const wrapper = mountTile()
|
|
162
|
+
expect(wrapper.find('.text-green-500').exists()).toBe(false)
|
|
163
|
+
expect(wrapper.find('.text-brick-500').exists()).toBe(false)
|
|
164
|
+
})
|
|
165
|
+
})
|
|
166
|
+
|
|
167
|
+
describe('percentageExplanaition', () => {
|
|
168
|
+
it('renders the explanation when provided alongside percentage', () => {
|
|
169
|
+
const wrapper = mountTile({ percentage: 14, percentageExplanaition: 'vorige maand' })
|
|
170
|
+
expect(wrapper.text()).toContain('vorige maand')
|
|
171
|
+
})
|
|
172
|
+
|
|
173
|
+
it('does not render the explanation when omitted', () => {
|
|
174
|
+
const wrapper = mountTile({ percentage: 14 })
|
|
175
|
+
expect(wrapper.text()).not.toContain('vorige maand')
|
|
176
|
+
})
|
|
177
|
+
})
|
|
178
|
+
})
|
|
@@ -0,0 +1,81 @@
|
|
|
1
|
+
<script lang="ts" setup>
|
|
2
|
+
import NexxtIcon from '../Icon/Icon.vue'
|
|
3
|
+
import { computed } from 'vue'
|
|
4
|
+
|
|
5
|
+
type Phase = 'Latent' | 'Oriëntatie' | 'Actief zoeken' | 'Selectie' | 'Transactie' | 'Wonen'
|
|
6
|
+
|
|
7
|
+
interface NexxtCustomerJourneyTileProps {
|
|
8
|
+
title?: string
|
|
9
|
+
amount?: number
|
|
10
|
+
percentage?: number
|
|
11
|
+
percentageExplanaition?: string
|
|
12
|
+
}
|
|
13
|
+
|
|
14
|
+
const PHASE_ICON_MAP: Record<Phase, string> = {
|
|
15
|
+
Latent: 'eye-closed',
|
|
16
|
+
Oriëntatie: 'building-magnifying-glass',
|
|
17
|
+
'Actief zoeken': 'house-person-return',
|
|
18
|
+
Selectie: 'handshake',
|
|
19
|
+
Transactie: 'pencil-mechanical',
|
|
20
|
+
Wonen: 'house-heart',
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
defineOptions({
|
|
24
|
+
name: 'NexxtCustomerJourneyTile',
|
|
25
|
+
})
|
|
26
|
+
|
|
27
|
+
const { title, amount, percentage, percentageExplanaition } =
|
|
28
|
+
defineProps<NexxtCustomerJourneyTileProps>()
|
|
29
|
+
|
|
30
|
+
const icon = computed(() => {
|
|
31
|
+
if (!title) return null
|
|
32
|
+
return (PHASE_ICON_MAP[title as Phase] as InstanceType<typeof NexxtIcon>['name']) || null
|
|
33
|
+
})
|
|
34
|
+
|
|
35
|
+
const ariaLabel = computed(() => {
|
|
36
|
+
const parts: string[] = []
|
|
37
|
+
if (title) parts.push(title)
|
|
38
|
+
if (amount !== undefined) parts.push(`${amount} contacten`)
|
|
39
|
+
if (percentage !== undefined) {
|
|
40
|
+
const sign = percentage > 0 ? '+' : ''
|
|
41
|
+
const explanation = percentageExplanaition ? ` ${percentageExplanaition}` : ''
|
|
42
|
+
parts.push(`${sign}${percentage}%${explanation}`)
|
|
43
|
+
}
|
|
44
|
+
return parts.join(', ')
|
|
45
|
+
})
|
|
46
|
+
</script>
|
|
47
|
+
|
|
48
|
+
<template>
|
|
49
|
+
<button
|
|
50
|
+
type="button"
|
|
51
|
+
:aria-label="ariaLabel"
|
|
52
|
+
class="flex cursor-pointer rounded-xl bg-white outline outline-gray-200 focus-visible:outline-2 focus-visible:outline-offset-2 focus-visible:outline-cornflower-blue-500"
|
|
53
|
+
:class="icon ? 'gap-6 p-1.5' : 'p-4'"
|
|
54
|
+
>
|
|
55
|
+
<div
|
|
56
|
+
v-if="icon"
|
|
57
|
+
class="flex min-h-10 w-14 items-center justify-center overflow-hidden rounded-tl-md rounded-bl-md bg-purple-100"
|
|
58
|
+
>
|
|
59
|
+
<NexxtIcon :name="icon" class="text-2xl text-purple-500" aria-hidden="true" />
|
|
60
|
+
</div>
|
|
61
|
+
<div class="flex flex-col items-start gap-1" :class="icon ? 'py-6 pr-4' : ''">
|
|
62
|
+
<span v-if="title">{{ title }}</span>
|
|
63
|
+
<span
|
|
64
|
+
class="heading-1"
|
|
65
|
+
:class="icon ? 'text-purple-500' : 'text-sapphire-900'"
|
|
66
|
+
v-if="amount"
|
|
67
|
+
>{{ amount }}</span
|
|
68
|
+
>
|
|
69
|
+
<div v-if="percentage" class="flex gap-0.5">
|
|
70
|
+
<span
|
|
71
|
+
class="extra-small-semibold"
|
|
72
|
+
:class="percentage > 0 ? 'text-green-500' : 'text-brick-500'"
|
|
73
|
+
><template v-if="percentage > 0">+</template>{{ percentage }}%</span
|
|
74
|
+
>
|
|
75
|
+
<span class="extra-small-normal" v-if="percentageExplanaition">{{
|
|
76
|
+
percentageExplanaition
|
|
77
|
+
}}</span>
|
|
78
|
+
</div>
|
|
79
|
+
</div>
|
|
80
|
+
</button>
|
|
81
|
+
</template>
|
|
@@ -15,10 +15,11 @@ describe('Header', () => {
|
|
|
15
15
|
expect(wrapper.find('span.heading-4').text()).toBe('My Page Title')
|
|
16
16
|
})
|
|
17
17
|
|
|
18
|
-
it('renders the back button with
|
|
18
|
+
it('renders the back button with explicit text', () => {
|
|
19
19
|
const wrapper = mount(Header, {
|
|
20
20
|
props: {
|
|
21
21
|
title: 'Test',
|
|
22
|
+
backButtonText: 'Back',
|
|
22
23
|
},
|
|
23
24
|
})
|
|
24
25
|
|
|
@@ -28,6 +29,17 @@ describe('Header', () => {
|
|
|
28
29
|
expect(button.props('variant')).toBe('secondary')
|
|
29
30
|
})
|
|
30
31
|
|
|
32
|
+
it('does not render the back button when backButtonText is not provided', () => {
|
|
33
|
+
const wrapper = mount(Header, {
|
|
34
|
+
props: {
|
|
35
|
+
title: 'Test',
|
|
36
|
+
},
|
|
37
|
+
})
|
|
38
|
+
|
|
39
|
+
const button = wrapper.findComponent(Button)
|
|
40
|
+
expect(button.exists()).toBe(false)
|
|
41
|
+
})
|
|
42
|
+
|
|
31
43
|
it('renders the back button with custom text', () => {
|
|
32
44
|
const wrapper = mount(Header, {
|
|
33
45
|
props: {
|
|
@@ -45,6 +57,7 @@ describe('Header', () => {
|
|
|
45
57
|
const wrapper = mount(Header, {
|
|
46
58
|
props: {
|
|
47
59
|
title: 'Test',
|
|
60
|
+
backButtonText: 'Back',
|
|
48
61
|
backButtonAction,
|
|
49
62
|
},
|
|
50
63
|
})
|
|
@@ -61,6 +74,7 @@ describe('Header', () => {
|
|
|
61
74
|
const wrapper = mount(Header, {
|
|
62
75
|
props: {
|
|
63
76
|
title: 'Test',
|
|
77
|
+
backButtonText: 'Back',
|
|
64
78
|
},
|
|
65
79
|
})
|
|
66
80
|
|
|
@@ -12,7 +12,7 @@ defineOptions({
|
|
|
12
12
|
})
|
|
13
13
|
|
|
14
14
|
const {
|
|
15
|
-
backButtonText
|
|
15
|
+
backButtonText,
|
|
16
16
|
backButtonAction = () => {
|
|
17
17
|
window.history.back()
|
|
18
18
|
},
|
|
@@ -22,9 +22,13 @@ const {
|
|
|
22
22
|
<template>
|
|
23
23
|
<div class="header flex items-center" :class="$slots.center ? 'grid grid-cols-3' : ''">
|
|
24
24
|
<div class="flex max-w-full items-center justify-start gap-4">
|
|
25
|
-
<Button
|
|
26
|
-
backButtonText
|
|
27
|
-
|
|
25
|
+
<Button
|
|
26
|
+
v-if="backButtonText"
|
|
27
|
+
class="shrink-0"
|
|
28
|
+
variant="secondary"
|
|
29
|
+
@click="backButtonAction"
|
|
30
|
+
>{{ backButtonText }}</Button
|
|
31
|
+
>
|
|
28
32
|
<span class="block truncate heading-4 text-sapphire-500">
|
|
29
33
|
{{ title }}
|
|
30
34
|
</span>
|
package/src/components.json
CHANGED
|
@@ -3,6 +3,7 @@
|
|
|
3
3
|
"NexxtButton": "components/Button/Button.vue",
|
|
4
4
|
"NexxtCalendar": "components/Calendar/Calendar.vue",
|
|
5
5
|
"NexxtChip": "components/Chip/Chip.vue",
|
|
6
|
+
"NexxtCustomerJourneyTile": "components/CustomerJourneyTile/CustomerJourneyTile.vue",
|
|
6
7
|
"NexxtDatePicker": "components/DatePicker/DatePicker.vue",
|
|
7
8
|
"NexxtHeader": "components/Header/Header.vue",
|
|
8
9
|
"NexxtIcon": "components/Icon/Icon.vue",
|
package/src/index.ts
CHANGED
|
@@ -3,6 +3,7 @@ export { default as NexxtAnimatedNumber } from './components/AnimatedNumber/Anim
|
|
|
3
3
|
export { default as NexxtButton } from './components/Button/Button.vue'
|
|
4
4
|
export { default as NexxtCalendar } from './components/Calendar/Calendar.vue'
|
|
5
5
|
export { default as NexxtChip } from './components/Chip/Chip.vue'
|
|
6
|
+
export { default as NexxtCustomerJourneyTile } from './components/CustomerJourneyTile/CustomerJourneyTile.vue'
|
|
6
7
|
export { default as NexxtDatePicker } from './components/DatePicker/DatePicker.vue'
|
|
7
8
|
export { default as NexxtHeader } from './components/Header/Header.vue'
|
|
8
9
|
export { default as NexxtIcon } from './components/Icon/Icon.vue'
|