@mozaic-ds/vue 1.0.0-beta.1 → 1.0.0-beta.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/LICENSE +51 -0
- package/README.md +218 -84
- package/dist/mozaic-vue.css +1 -1
- package/dist/mozaic-vue.d.ts +920 -0
- package/dist/mozaic-vue.js +877 -0
- package/dist/mozaic-vue.js.map +1 -0
- package/dist/mozaic-vue.umd.cjs +2 -0
- package/dist/mozaic-vue.umd.cjs.map +1 -0
- package/env.d.ts +1 -0
- package/package.json +80 -50
- package/src/components/Contributing.mdx +118 -0
- package/src/components/GettingStarted.mdx +39 -0
- package/src/components/Introduction.mdx +54 -0
- package/src/components/Support.mdx +18 -0
- package/src/components/badge/MBadge.spec.ts +16 -0
- package/src/components/badge/MBadge.stories.ts +50 -0
- package/src/components/badge/MBadge.vue +36 -34
- package/src/components/button/MButton.spec.ts +191 -0
- package/src/components/button/MButton.stories.ts +66 -0
- package/src/components/button/MButton.vue +98 -154
- package/src/components/checkbox/MCheckbox.spec.ts +104 -0
- package/src/components/checkbox/MCheckbox.stories.ts +83 -0
- package/src/components/checkbox/MCheckbox.vue +60 -101
- package/src/components/checkboxgroup/MCheckboxGroup.spec.ts +78 -0
- package/src/components/checkboxgroup/MCheckboxGroup.stories.ts +61 -0
- package/src/components/checkboxgroup/MCheckboxGroup.vue +97 -0
- package/src/components/field/MField.spec.ts +166 -0
- package/src/components/field/MField.stories.ts +376 -0
- package/src/components/field/MField.vue +78 -61
- package/src/components/fieldgroup/MFieldGroup.spec.ts +165 -0
- package/src/components/fieldgroup/MFieldGroup.stories.ts +274 -0
- package/src/components/fieldgroup/MFieldGroup.vue +79 -0
- package/src/components/iconbutton/MIconButton.spec.ts +108 -0
- package/src/components/iconbutton/MIconButton.stories.ts +66 -0
- package/src/components/iconbutton/MIconButton.vue +73 -0
- package/src/components/link/MLink.spec.ts +154 -0
- package/src/components/link/MLink.stories.ts +98 -0
- package/src/components/link/MLink.vue +86 -109
- package/src/components/loader/MLoader.spec.ts +104 -0
- package/src/components/loader/MLoader.stories.ts +45 -0
- package/src/components/loader/MLoader.vue +65 -55
- package/src/components/overlay/MOverlay.spec.ts +51 -0
- package/src/components/overlay/MOverlay.stories.ts +40 -0
- package/src/components/overlay/MOverlay.vue +27 -19
- package/src/components/quantityselector/MQuantitySelector.spec.ts +262 -0
- package/src/components/quantityselector/MQuantitySelector.stories.ts +89 -0
- package/src/components/quantityselector/MQuantitySelector.vue +160 -136
- package/src/components/radio/MRadio.spec.ts +104 -0
- package/src/components/radio/MRadio.stories.ts +68 -0
- package/src/components/radio/MRadio.vue +56 -39
- package/src/components/radiogroup/MRadioGroup.spec.ts +54 -0
- package/src/components/radiogroup/MRadioGroup.stories.ts +61 -0
- package/src/components/radiogroup/MRadioGroup.vue +79 -0
- package/src/components/select/MSelect.spec.ts +114 -0
- package/src/components/select/MSelect.stories.ts +101 -0
- package/src/components/select/MSelect.vue +77 -119
- package/src/components/statusbadge/MStatusBadge.stories.ts +45 -0
- package/src/components/statusbadge/MStatusBadge.vue +40 -0
- package/src/components/statusbadge/MStatusDot.vue +32 -0
- package/src/components/statusbadge/MstatusBadge.spec.ts +16 -0
- package/src/components/textarea/MTextArea.spec.ts +112 -0
- package/src/components/textarea/MTextArea.stories.ts +67 -0
- package/src/components/textarea/MTextArea.vue +81 -42
- package/src/components/textinput/MTextInput.spec.ts +121 -0
- package/src/components/textinput/MTextInput.stories.ts +114 -0
- package/src/components/textinput/MTextInput.vue +127 -47
- package/src/components/toggle/MToggle.spec.ts +99 -0
- package/src/components/toggle/MToggle.stories.ts +68 -0
- package/src/components/toggle/MToggle.vue +63 -103
- package/src/components/usingIcons.mdx +43 -0
- package/src/components/usingPresets.mdx +125 -0
- package/src/main.ts +39 -0
- package/dist/demo.html +0 -1
- package/dist/mozaic-vue.adeo.css +0 -45
- package/dist/mozaic-vue.adeo.umd.js +0 -41775
- package/dist/mozaic-vue.common.js +0 -41765
- package/dist/mozaic-vue.common.js.map +0 -1
- package/dist/mozaic-vue.umd.js +0 -41776
- package/dist/mozaic-vue.umd.js.map +0 -1
- package/dist/mozaic-vue.umd.min.js +0 -4
- package/dist/mozaic-vue.umd.min.js.map +0 -1
- package/postinstall.js +0 -3
- package/src/components/accordion/MAccordion.vue +0 -128
- package/src/components/accordion/index.js +0 -7
- package/src/components/autocomplete/MAutocomplete.vue +0 -198
- package/src/components/autocomplete/index.js +0 -7
- package/src/components/badge/index.js +0 -7
- package/src/components/breadcrumb/MBreadcrumb.vue +0 -73
- package/src/components/breadcrumb/index.js +0 -7
- package/src/components/button/index.js +0 -7
- package/src/components/card/MCard.vue +0 -78
- package/src/components/card/index.js +0 -7
- package/src/components/checkbox/MCheckboxGroup.vue +0 -155
- package/src/components/checkbox/index.js +0 -12
- package/src/components/container/MContainer.vue +0 -33
- package/src/components/container/index.js +0 -7
- package/src/components/datatable/MDataTable.vue +0 -651
- package/src/components/datatable/MDataTableHeader.vue +0 -55
- package/src/components/datatable/MDataTableTop.vue +0 -35
- package/src/components/datatable/helpers.js +0 -132
- package/src/components/datatable/index.js +0 -12
- package/src/components/field/index.js +0 -7
- package/src/components/fileuploader/MFileResult.vue +0 -149
- package/src/components/fileuploader/MFileUploader.vue +0 -142
- package/src/components/fileuploader/index.js +0 -7
- package/src/components/flag/MFlag.vue +0 -46
- package/src/components/flag/index.js +0 -7
- package/src/components/heading/MHeading.vue +0 -75
- package/src/components/heading/index.js +0 -7
- package/src/components/hero/MHero.vue +0 -93
- package/src/components/hero/index.js +0 -7
- package/src/components/icon/MIcon.vue +0 -120
- package/src/components/icon/index.js +0 -7
- package/src/components/index.js +0 -43
- package/src/components/layer/MLayer.vue +0 -208
- package/src/components/layer/index.js +0 -7
- package/src/components/link/index.js +0 -7
- package/src/components/listbox/MListBox.vue +0 -106
- package/src/components/listbox/index.js +0 -7
- package/src/components/loader/index.js +0 -7
- package/src/components/modal/MModal.vue +0 -179
- package/src/components/modal/index.js +0 -7
- package/src/components/notification/MNotification.vue +0 -110
- package/src/components/notification/index.js +0 -7
- package/src/components/optionbutton/MOptionButton.vue +0 -67
- package/src/components/optionbutton/index.js +0 -7
- package/src/components/optioncard/MOptionCard.vue +0 -132
- package/src/components/optioncard/index.js +0 -7
- package/src/components/optiongroup/MOptionGroup.vue +0 -18
- package/src/components/optiongroup/index.js +0 -7
- package/src/components/overlay/MOverlayLoader.vue +0 -43
- package/src/components/overlay/index.js +0 -12
- package/src/components/pagination/MPagination.vue +0 -162
- package/src/components/pagination/index.js +0 -7
- package/src/components/passwordinput/MPasswordInput.vue +0 -96
- package/src/components/passwordinput/index.js +0 -7
- package/src/components/phonenumber/MPhoneNumber.vue +0 -390
- package/src/components/phonenumber/index.js +0 -7
- package/src/components/progressbar/MProgress.vue +0 -102
- package/src/components/progressbar/index.js +0 -7
- package/src/components/quantityselector/index.js +0 -7
- package/src/components/radio/MRadioGroup.vue +0 -111
- package/src/components/radio/index.js +0 -12
- package/src/components/ratingstars/MStarsInput.vue +0 -118
- package/src/components/ratingstars/MStarsResult.vue +0 -89
- package/src/components/ratingstars/index.js +0 -12
- package/src/components/select/index.js +0 -7
- package/src/components/stepper/MStepper.vue +0 -70
- package/src/components/stepper/index.js +0 -7
- package/src/components/tabs/MTab.vue +0 -184
- package/src/components/tabs/index.js +0 -7
- package/src/components/tags/MTag.vue +0 -173
- package/src/components/tags/index.js +0 -7
- package/src/components/textarea/index.js +0 -7
- package/src/components/textinput/MTextInputField.vue +0 -105
- package/src/components/textinput/MTextInputIcon.vue +0 -42
- package/src/components/textinput/index.js +0 -7
- package/src/components/toggle/index.js +0 -7
- package/src/components/tooltip/MTooltip.vue +0 -42
- package/src/components/tooltip/index.js +0 -7
- package/src/index.js +0 -62
- package/src/shims-tsx.d.ts +0 -13
- package/src/shims.vue.d.ts +0 -4
- package/src/tokens/adeo/android/colors.xml +0 -391
- package/src/tokens/adeo/android/font_dimens.xml +0 -18
- package/src/tokens/adeo/css/_variables.scss +0 -385
- package/src/tokens/adeo/css/root.scss +0 -387
- package/src/tokens/adeo/ios/StyleDictionaryColor.h +0 -399
- package/src/tokens/adeo/ios/StyleDictionaryColor.m +0 -411
- package/src/tokens/adeo/ios/StyleDictionaryColor.swift +0 -394
- package/src/tokens/adeo/ios/StyleDictionarySize.h +0 -69
- package/src/tokens/adeo/ios/StyleDictionarySize.m +0 -70
- package/src/tokens/adeo/ios/StyleDictionarySize.swift +0 -71
- package/src/tokens/adeo/js/tokens.js +0 -483
- package/src/tokens/adeo/js/tokensObject.js +0 -10354
- package/src/tokens/adeo/scss/_tokens.scss +0 -1300
- package/src/utils/mozaicClasses.js +0 -16
- package/src/utils/theme.validator.js +0 -19
- package/types/index.d.ts +0 -100
|
@@ -0,0 +1,154 @@
|
|
|
1
|
+
import { mount } from '@vue/test-utils';
|
|
2
|
+
import MLink from './MLink.vue';
|
|
3
|
+
import { describe, it, expect } from 'vitest';
|
|
4
|
+
|
|
5
|
+
describe('MLink component', () => {
|
|
6
|
+
it('renders a link with default props', () => {
|
|
7
|
+
const wrapper = mount(MLink);
|
|
8
|
+
|
|
9
|
+
const link = wrapper.get('a');
|
|
10
|
+
expect(link.classes()).toContain('mc-link');
|
|
11
|
+
});
|
|
12
|
+
|
|
13
|
+
it('renders as router-link when the "router" prop is true', () => {
|
|
14
|
+
const wrapper = mount(MLink, {
|
|
15
|
+
props: {
|
|
16
|
+
router: true,
|
|
17
|
+
href: '/internal',
|
|
18
|
+
},
|
|
19
|
+
});
|
|
20
|
+
|
|
21
|
+
const link = wrapper.getComponent('router-link');
|
|
22
|
+
expect(link.exists()).toBe(true);
|
|
23
|
+
expect(link.attributes('to')).toBe('/internal');
|
|
24
|
+
});
|
|
25
|
+
|
|
26
|
+
it('renders as a regular anchor when the "router" prop is false or undefined', () => {
|
|
27
|
+
const wrapper = mount(MLink, {
|
|
28
|
+
props: {
|
|
29
|
+
href: 'https://example.com',
|
|
30
|
+
},
|
|
31
|
+
});
|
|
32
|
+
|
|
33
|
+
const link = wrapper.get('a');
|
|
34
|
+
expect(link.exists()).toBe(true);
|
|
35
|
+
expect(link.attributes('href')).toBe('https://example.com');
|
|
36
|
+
});
|
|
37
|
+
|
|
38
|
+
it('renders without an icon', () => {
|
|
39
|
+
const wrapper = mount(MLink, {
|
|
40
|
+
slots: {
|
|
41
|
+
default: 'Link text',
|
|
42
|
+
},
|
|
43
|
+
});
|
|
44
|
+
|
|
45
|
+
const icon = wrapper.find('.mc-link__icon');
|
|
46
|
+
const label = wrapper.find('.mc-link__label');
|
|
47
|
+
expect(icon.exists()).toBe(false);
|
|
48
|
+
expect(label.text()).toBe('Link text');
|
|
49
|
+
});
|
|
50
|
+
|
|
51
|
+
it('renders with an icon on the left when "iconPosition" is "left"', () => {
|
|
52
|
+
const wrapper = mount(MLink, {
|
|
53
|
+
props: {
|
|
54
|
+
iconPosition: 'left',
|
|
55
|
+
},
|
|
56
|
+
slots: {
|
|
57
|
+
icon: '<span>Left Icon</span>',
|
|
58
|
+
default: 'Link text',
|
|
59
|
+
},
|
|
60
|
+
});
|
|
61
|
+
|
|
62
|
+
const leftIcon = wrapper.find('.mc-link__icon');
|
|
63
|
+
const label = wrapper.find('.mc-link__label');
|
|
64
|
+
expect(leftIcon.exists()).toBe(true);
|
|
65
|
+
expect(leftIcon.text()).toBe('Left Icon');
|
|
66
|
+
expect(label.text()).toBe('Link text');
|
|
67
|
+
});
|
|
68
|
+
|
|
69
|
+
it('renders with an icon on the right when "iconPosition" is "right"', () => {
|
|
70
|
+
const wrapper = mount(MLink, {
|
|
71
|
+
props: {
|
|
72
|
+
iconPosition: 'right',
|
|
73
|
+
},
|
|
74
|
+
slots: {
|
|
75
|
+
icon: '<span>Right Icon</span>',
|
|
76
|
+
default: 'Link text',
|
|
77
|
+
},
|
|
78
|
+
});
|
|
79
|
+
|
|
80
|
+
const rightIcon = wrapper.find('.mc-link__icon');
|
|
81
|
+
const label = wrapper.find('.mc-link__label');
|
|
82
|
+
expect(rightIcon.exists()).toBe(true);
|
|
83
|
+
expect(rightIcon.text()).toBe('Right Icon');
|
|
84
|
+
expect(label.text()).toBe('Link text');
|
|
85
|
+
});
|
|
86
|
+
|
|
87
|
+
it('applies the correct appearance class', () => {
|
|
88
|
+
const wrapper = mount(MLink, {
|
|
89
|
+
props: {
|
|
90
|
+
appearance: 'accent',
|
|
91
|
+
},
|
|
92
|
+
});
|
|
93
|
+
|
|
94
|
+
const link = wrapper.get('a');
|
|
95
|
+
expect(link.classes()).toContain('mc-link--accent');
|
|
96
|
+
});
|
|
97
|
+
|
|
98
|
+
it('applies the correct size class', () => {
|
|
99
|
+
const wrapper = mount(MLink, {
|
|
100
|
+
props: {
|
|
101
|
+
size: 'm',
|
|
102
|
+
},
|
|
103
|
+
});
|
|
104
|
+
|
|
105
|
+
const link = wrapper.get('a');
|
|
106
|
+
expect(link.classes()).toContain('mc-link--m');
|
|
107
|
+
});
|
|
108
|
+
|
|
109
|
+
it('adds "mc-link--inline" class when inline', () => {
|
|
110
|
+
const wrapper = mount(MLink, {
|
|
111
|
+
props: {
|
|
112
|
+
inline: true,
|
|
113
|
+
},
|
|
114
|
+
});
|
|
115
|
+
|
|
116
|
+
const link = wrapper.get('a');
|
|
117
|
+
expect(link.classes()).toContain('mc-link--inline');
|
|
118
|
+
});
|
|
119
|
+
|
|
120
|
+
it('applies the href and target attributes correctly', () => {
|
|
121
|
+
const wrapper = mount(MLink, {
|
|
122
|
+
props: {
|
|
123
|
+
href: 'https://example.com',
|
|
124
|
+
target: '_blank',
|
|
125
|
+
},
|
|
126
|
+
});
|
|
127
|
+
|
|
128
|
+
const link = wrapper.get('a');
|
|
129
|
+
expect(link.attributes('href')).toBe('https://example.com');
|
|
130
|
+
expect(link.attributes('target')).toBe('_blank');
|
|
131
|
+
});
|
|
132
|
+
|
|
133
|
+
it('renders default slot content', () => {
|
|
134
|
+
const wrapper = mount(MLink, {
|
|
135
|
+
slots: {
|
|
136
|
+
default: 'Custom Link Text',
|
|
137
|
+
},
|
|
138
|
+
});
|
|
139
|
+
|
|
140
|
+
const linkText = wrapper.get('.mc-link__label');
|
|
141
|
+
expect(linkText.text()).toBe('Custom Link Text');
|
|
142
|
+
});
|
|
143
|
+
|
|
144
|
+
it('binds custom attributes correctly', () => {
|
|
145
|
+
const wrapper = mount(MLink, {
|
|
146
|
+
attrs: {
|
|
147
|
+
'data-test-id': 'custom-id',
|
|
148
|
+
},
|
|
149
|
+
});
|
|
150
|
+
|
|
151
|
+
const link = wrapper.get('a');
|
|
152
|
+
expect(link.attributes('data-test-id')).toBe('custom-id');
|
|
153
|
+
});
|
|
154
|
+
});
|
|
@@ -0,0 +1,98 @@
|
|
|
1
|
+
import type { Meta, StoryObj } from '@storybook/vue3';
|
|
2
|
+
import MLink from './MLink.vue';
|
|
3
|
+
import ChevronLeft24 from '@mozaic-ds/icons-vue/src/components/ChevronLeft24/ChevronLeft24.vue';
|
|
4
|
+
import ChevronRight24 from '@mozaic-ds/icons-vue/src/components/ChevronRight24/ChevronRight24.vue';
|
|
5
|
+
|
|
6
|
+
const meta: Meta<typeof MLink> = {
|
|
7
|
+
title: 'Navigation/Link (stand alone)',
|
|
8
|
+
component: MLink,
|
|
9
|
+
parameters: {
|
|
10
|
+
docs: {
|
|
11
|
+
description: {
|
|
12
|
+
component:
|
|
13
|
+
'A link is a component used exclusively to navigate to internal or external webpages or to anchors in the current page.',
|
|
14
|
+
},
|
|
15
|
+
},
|
|
16
|
+
},
|
|
17
|
+
args: {
|
|
18
|
+
default: 'Stand-alone link',
|
|
19
|
+
href: '#',
|
|
20
|
+
},
|
|
21
|
+
argTypes: {
|
|
22
|
+
$slots: {
|
|
23
|
+
table: {
|
|
24
|
+
disable: true,
|
|
25
|
+
},
|
|
26
|
+
},
|
|
27
|
+
},
|
|
28
|
+
render: (args) => ({
|
|
29
|
+
components: { MLink, ChevronRight24, ChevronLeft24 },
|
|
30
|
+
setup() {
|
|
31
|
+
return { args };
|
|
32
|
+
},
|
|
33
|
+
template: `
|
|
34
|
+
<MLink v-bind="args">
|
|
35
|
+
<template v-if="${'icon' in args}" v-slot:icon>${args.icon}</template>
|
|
36
|
+
${args.default}
|
|
37
|
+
</MLink>
|
|
38
|
+
`,
|
|
39
|
+
}),
|
|
40
|
+
};
|
|
41
|
+
export default meta;
|
|
42
|
+
type Story = StoryObj<typeof MLink>;
|
|
43
|
+
|
|
44
|
+
export const Standard: Story = {};
|
|
45
|
+
|
|
46
|
+
export const ExternalLink: Story = {
|
|
47
|
+
name: 'For External Link',
|
|
48
|
+
args: {
|
|
49
|
+
href: 'https://example.com',
|
|
50
|
+
},
|
|
51
|
+
};
|
|
52
|
+
|
|
53
|
+
export const InternalLink: Story = {
|
|
54
|
+
name: 'For Internal Link with Vue Router',
|
|
55
|
+
args: {
|
|
56
|
+
href: '/about',
|
|
57
|
+
router: true,
|
|
58
|
+
},
|
|
59
|
+
};
|
|
60
|
+
|
|
61
|
+
export const Secondary: Story = {
|
|
62
|
+
args: { appearance: 'secondary' },
|
|
63
|
+
};
|
|
64
|
+
|
|
65
|
+
export const Accent: Story = {
|
|
66
|
+
args: { appearance: 'accent' },
|
|
67
|
+
};
|
|
68
|
+
|
|
69
|
+
export const Inverse: Story = {
|
|
70
|
+
parameters: {
|
|
71
|
+
backgrounds: {
|
|
72
|
+
default: 'Inverse',
|
|
73
|
+
},
|
|
74
|
+
},
|
|
75
|
+
args: { appearance: 'inverse' },
|
|
76
|
+
};
|
|
77
|
+
|
|
78
|
+
export const Size: Story = {
|
|
79
|
+
args: { size: 'm' },
|
|
80
|
+
};
|
|
81
|
+
|
|
82
|
+
export const Inline: Story = {
|
|
83
|
+
args: { inline: true },
|
|
84
|
+
};
|
|
85
|
+
|
|
86
|
+
export const IconLeft: Story = {
|
|
87
|
+
args: {
|
|
88
|
+
iconPosition: 'left',
|
|
89
|
+
icon: '<ChevronLeft24/>',
|
|
90
|
+
},
|
|
91
|
+
};
|
|
92
|
+
|
|
93
|
+
export const IconRight: Story = {
|
|
94
|
+
args: {
|
|
95
|
+
iconPosition: 'right',
|
|
96
|
+
icon: '<ChevronRight24/>',
|
|
97
|
+
},
|
|
98
|
+
};
|
|
@@ -1,122 +1,99 @@
|
|
|
1
1
|
<template>
|
|
2
2
|
<component
|
|
3
|
-
:is="router ? router :
|
|
4
|
-
:href="router ? null : href && !disabled ? href : null"
|
|
3
|
+
:is="router ? 'router-link' : 'a'"
|
|
5
4
|
class="mc-link"
|
|
6
|
-
:class="
|
|
7
|
-
|
|
5
|
+
:class="classObject"
|
|
6
|
+
:href="href"
|
|
7
|
+
:target="target"
|
|
8
|
+
:to="router ? href : undefined"
|
|
8
9
|
>
|
|
9
|
-
<
|
|
10
|
-
v-if="icon && iconPosition
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
10
|
+
<span
|
|
11
|
+
v-if="$slots.icon && iconPosition == 'left'"
|
|
12
|
+
class="mc-link__icon"
|
|
13
|
+
aria-hidden="true"
|
|
14
|
+
>
|
|
15
|
+
<slot name="icon" />
|
|
16
|
+
</span>
|
|
17
|
+
<span class="mc-link__label">
|
|
18
|
+
<slot />
|
|
19
|
+
</span>
|
|
20
|
+
<span
|
|
21
|
+
v-if="$slots.icon && iconPosition == 'right'"
|
|
22
|
+
class="mc-link__icon"
|
|
23
|
+
aria-hidden="true"
|
|
24
|
+
>
|
|
25
|
+
<slot name="icon" />
|
|
26
|
+
</span>
|
|
20
27
|
</component>
|
|
21
28
|
</template>
|
|
22
29
|
|
|
23
|
-
<script>
|
|
24
|
-
import {
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
30
|
+
<script setup lang="ts">
|
|
31
|
+
import { computed, type VNode } from 'vue';
|
|
32
|
+
/**
|
|
33
|
+
* A link is a component used exclusively to navigate to internal or external webpages or to anchors in the current page.
|
|
34
|
+
*/
|
|
35
|
+
const props = withDefaults(
|
|
36
|
+
defineProps<{
|
|
37
|
+
/**
|
|
38
|
+
* Position of the icon relative to the text.
|
|
39
|
+
*/
|
|
40
|
+
iconPosition?: 'left' | 'right';
|
|
41
|
+
/**
|
|
42
|
+
* Allows to define the link style
|
|
43
|
+
*/
|
|
44
|
+
appearance?: 'secondary' | 'accent' | 'inverse' | 'standard';
|
|
45
|
+
/**
|
|
46
|
+
* Allows to define the link size
|
|
47
|
+
*/
|
|
48
|
+
size?: 's' | 'm';
|
|
49
|
+
/**
|
|
50
|
+
* URL for the link (for external links or the `to` prop for `router-link`).
|
|
51
|
+
*/
|
|
52
|
+
href?: string;
|
|
53
|
+
/**
|
|
54
|
+
* Where to open the link
|
|
55
|
+
*/
|
|
56
|
+
target?: string;
|
|
57
|
+
/**
|
|
58
|
+
* Specify wether the link is inline
|
|
59
|
+
*/
|
|
60
|
+
inline?: boolean;
|
|
61
|
+
/**
|
|
62
|
+
* If `true`, the link will be rendered as a `router-link` for internal navigation (Vue Router).
|
|
63
|
+
*/
|
|
64
|
+
router?: boolean;
|
|
65
|
+
}>(),
|
|
66
|
+
{
|
|
67
|
+
href: undefined,
|
|
68
|
+
target: undefined,
|
|
69
|
+
appearance: 'standard',
|
|
70
|
+
size: 's',
|
|
71
|
+
iconPosition: 'left',
|
|
36
72
|
},
|
|
73
|
+
);
|
|
37
74
|
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
type: String,
|
|
49
|
-
default: null,
|
|
50
|
-
validator: (value) => checkThemeValue(value),
|
|
51
|
-
},
|
|
52
|
-
size: {
|
|
53
|
-
type: String,
|
|
54
|
-
default: 'm',
|
|
55
|
-
validator: (value) => responsiveModifierValidators(value, ['s', 'm']),
|
|
56
|
-
},
|
|
57
|
-
// Experimental solution waiting on this issue https://github.com/adeo/mozaic-vue/issues/52
|
|
58
|
-
responsiveSizeModifiers: {
|
|
59
|
-
type: Array,
|
|
60
|
-
default: null,
|
|
61
|
-
validator: (array) =>
|
|
62
|
-
array.every((e) =>
|
|
63
|
-
responsiveModifierValidators(e, [
|
|
64
|
-
's@from-m',
|
|
65
|
-
's@from-l',
|
|
66
|
-
's@from-xl',
|
|
67
|
-
's@from-xxl',
|
|
68
|
-
'm@from-m',
|
|
69
|
-
'm@from-l',
|
|
70
|
-
'm@from-xl',
|
|
71
|
-
'm@from-xxl',
|
|
72
|
-
])
|
|
73
|
-
),
|
|
74
|
-
},
|
|
75
|
-
icon: {
|
|
76
|
-
type: String,
|
|
77
|
-
default: null,
|
|
78
|
-
},
|
|
79
|
-
iconPosition: {
|
|
80
|
-
type: String,
|
|
81
|
-
default: 'left',
|
|
82
|
-
validator: (value) => ['left', 'right'].includes(value),
|
|
83
|
-
},
|
|
84
|
-
disabled: {
|
|
85
|
-
type: Boolean,
|
|
86
|
-
default: false,
|
|
87
|
-
},
|
|
88
|
-
},
|
|
89
|
-
|
|
90
|
-
computed: {
|
|
91
|
-
setClasses() {
|
|
92
|
-
const classes = [];
|
|
93
|
-
|
|
94
|
-
if (this.theme) {
|
|
95
|
-
classes.push(`mc-link--${this.theme}`);
|
|
96
|
-
}
|
|
75
|
+
defineSlots<{
|
|
76
|
+
/**
|
|
77
|
+
* Use this slot to insert the textual content of the Link
|
|
78
|
+
*/
|
|
79
|
+
default: string;
|
|
80
|
+
/**
|
|
81
|
+
* Use this slot to insert an icon for the Link
|
|
82
|
+
*/
|
|
83
|
+
icon?: VNode;
|
|
84
|
+
}>();
|
|
97
85
|
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
},
|
|
108
|
-
},
|
|
109
|
-
methods: {
|
|
110
|
-
onClick(event) {
|
|
111
|
-
if(!this.disabled){
|
|
112
|
-
this.$emit('click', event);
|
|
113
|
-
}
|
|
114
|
-
},
|
|
115
|
-
},
|
|
116
|
-
};
|
|
86
|
+
const classObject = computed(() => {
|
|
87
|
+
return {
|
|
88
|
+
[`mc-link--${props.appearance}`]:
|
|
89
|
+
props.appearance && props.appearance != 'standard',
|
|
90
|
+
[`mc-link--${props.size}`]: props.size && props.size != 's',
|
|
91
|
+
'mc-link--inline': props.inline,
|
|
92
|
+
'mc-link--stand-alone': !props.inline,
|
|
93
|
+
};
|
|
94
|
+
});
|
|
117
95
|
</script>
|
|
118
96
|
|
|
119
|
-
<style lang="scss">
|
|
120
|
-
@
|
|
121
|
-
@import 'components/c.links';
|
|
97
|
+
<style lang="scss" scoped>
|
|
98
|
+
@use '@mozaic-ds/styles/components/link';
|
|
122
99
|
</style>
|
|
@@ -0,0 +1,104 @@
|
|
|
1
|
+
import { mount } from '@vue/test-utils';
|
|
2
|
+
import { describe, it, expect } from 'vitest';
|
|
3
|
+
import MLoader from './MLoader.vue';
|
|
4
|
+
|
|
5
|
+
describe('MLoader component', () => {
|
|
6
|
+
it('should render correctly with the given props', () => {
|
|
7
|
+
const wrapper = mount(MLoader, {
|
|
8
|
+
props: {
|
|
9
|
+
size: 's',
|
|
10
|
+
appearance: 'accent',
|
|
11
|
+
},
|
|
12
|
+
});
|
|
13
|
+
|
|
14
|
+
expect(wrapper.exists()).toBe(true);
|
|
15
|
+
|
|
16
|
+
const loaderElement = wrapper.find('.mc-loader');
|
|
17
|
+
expect(loaderElement.exists()).toBe(true);
|
|
18
|
+
expect(loaderElement.classes()).toContain('mc-loader--s');
|
|
19
|
+
expect(loaderElement.classes()).toContain('mc-loader--accent');
|
|
20
|
+
|
|
21
|
+
const svg = wrapper.find('svg.mc-loader__icon');
|
|
22
|
+
expect(svg.exists()).toBe(true);
|
|
23
|
+
|
|
24
|
+
const circle = wrapper.find('circle.mc-loader__path');
|
|
25
|
+
expect(circle.exists()).toBe(true);
|
|
26
|
+
});
|
|
27
|
+
|
|
28
|
+
it('changes class based on size prop', async () => {
|
|
29
|
+
const wrapper = mount(MLoader, {
|
|
30
|
+
props: {
|
|
31
|
+
size: 's',
|
|
32
|
+
},
|
|
33
|
+
});
|
|
34
|
+
|
|
35
|
+
let loader = wrapper.find('.mc-loader');
|
|
36
|
+
expect(loader.classes()).toContain('mc-loader--s');
|
|
37
|
+
|
|
38
|
+
await wrapper.setProps({ size: 'l' });
|
|
39
|
+
loader = wrapper.find('.mc-loader');
|
|
40
|
+
expect(loader.classes()).toContain('mc-loader--l');
|
|
41
|
+
});
|
|
42
|
+
|
|
43
|
+
it('changes class based on appearance prop', async () => {
|
|
44
|
+
const wrapper = mount(MLoader, {
|
|
45
|
+
props: {
|
|
46
|
+
appearance: 'accent',
|
|
47
|
+
},
|
|
48
|
+
});
|
|
49
|
+
|
|
50
|
+
let loader = wrapper.find('.mc-loader');
|
|
51
|
+
expect(loader.classes()).toContain('mc-loader--accent');
|
|
52
|
+
|
|
53
|
+
await wrapper.setProps({ appearance: 'inverse' });
|
|
54
|
+
loader = wrapper.find('.mc-loader');
|
|
55
|
+
expect(loader.classes()).toContain('mc-loader--inverse');
|
|
56
|
+
});
|
|
57
|
+
|
|
58
|
+
it('renders the loader text when text prop is provided', () => {
|
|
59
|
+
const wrapper = mount(MLoader, {
|
|
60
|
+
props: {
|
|
61
|
+
text: 'Loading...',
|
|
62
|
+
},
|
|
63
|
+
});
|
|
64
|
+
|
|
65
|
+
const textElement = wrapper.find('.mc-loader__text');
|
|
66
|
+
expect(textElement.exists()).toBe(true);
|
|
67
|
+
expect(textElement.text()).toBe('Loading...');
|
|
68
|
+
});
|
|
69
|
+
|
|
70
|
+
it('does not render the loader text when text prop is not provided', () => {
|
|
71
|
+
const wrapper = mount(MLoader);
|
|
72
|
+
|
|
73
|
+
const textElement = wrapper.find('.mc-loader__text');
|
|
74
|
+
expect(textElement.exists()).toBe(false);
|
|
75
|
+
});
|
|
76
|
+
|
|
77
|
+
it('sets the correct viewBox based on size prop', async () => {
|
|
78
|
+
const wrapper = mount(MLoader, {
|
|
79
|
+
props: {
|
|
80
|
+
size: 's',
|
|
81
|
+
},
|
|
82
|
+
});
|
|
83
|
+
|
|
84
|
+
const svg = wrapper.find('svg.mc-loader__icon');
|
|
85
|
+
expect(svg.attributes('viewBox')).toBe('0 0 24 24');
|
|
86
|
+
|
|
87
|
+
await wrapper.setProps({ size: 'l' });
|
|
88
|
+
expect(svg.attributes('viewBox')).toBe('0 0 64 64');
|
|
89
|
+
});
|
|
90
|
+
|
|
91
|
+
it('sets the correct circle radius based on size prop', async () => {
|
|
92
|
+
const wrapper = mount(MLoader, {
|
|
93
|
+
props: {
|
|
94
|
+
size: 's',
|
|
95
|
+
},
|
|
96
|
+
});
|
|
97
|
+
|
|
98
|
+
const circle = wrapper.find('circle.mc-loader__path');
|
|
99
|
+
expect(circle.attributes('r')).toBe('6');
|
|
100
|
+
|
|
101
|
+
await wrapper.setProps({ size: 'l' });
|
|
102
|
+
expect(circle.attributes('r')).toBe('19');
|
|
103
|
+
});
|
|
104
|
+
});
|
|
@@ -0,0 +1,45 @@
|
|
|
1
|
+
import type { Meta, StoryObj } from '@storybook/vue3';
|
|
2
|
+
import MLoader from './MLoader.vue';
|
|
3
|
+
|
|
4
|
+
const meta: Meta<typeof MLoader> = {
|
|
5
|
+
title: 'Indicators/Loader',
|
|
6
|
+
component: MLoader,
|
|
7
|
+
parameters: {
|
|
8
|
+
docs: {
|
|
9
|
+
description: {
|
|
10
|
+
component:
|
|
11
|
+
'A loader indicates that content or data is being loaded or processed, providing visual feedback to users during wait times.',
|
|
12
|
+
},
|
|
13
|
+
},
|
|
14
|
+
},
|
|
15
|
+
render: (args) => ({
|
|
16
|
+
components: { MLoader },
|
|
17
|
+
setup() {
|
|
18
|
+
return { args };
|
|
19
|
+
},
|
|
20
|
+
template: `
|
|
21
|
+
<MLoader v-bind="args"></MLoader>
|
|
22
|
+
`,
|
|
23
|
+
}),
|
|
24
|
+
};
|
|
25
|
+
export default meta;
|
|
26
|
+
type Story = StoryObj<typeof MLoader>;
|
|
27
|
+
|
|
28
|
+
export const Standard: Story = {};
|
|
29
|
+
|
|
30
|
+
export const Accent: Story = {
|
|
31
|
+
args: { appearance: 'accent' },
|
|
32
|
+
};
|
|
33
|
+
|
|
34
|
+
export const Inverse: Story = {
|
|
35
|
+
parameters: {
|
|
36
|
+
backgrounds: {
|
|
37
|
+
default: 'Inverse',
|
|
38
|
+
},
|
|
39
|
+
},
|
|
40
|
+
args: { appearance: 'inverse' },
|
|
41
|
+
};
|
|
42
|
+
|
|
43
|
+
export const Size: Story = {
|
|
44
|
+
args: { size: 's' },
|
|
45
|
+
};
|