@mozaic-ds/vue 1.0.0-beta.3 → 1.0.0-beta.5

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.
Files changed (191) hide show
  1. package/LICENSE +51 -0
  2. package/README.md +224 -82
  3. package/dist/mozaic-vue.css +1 -1
  4. package/dist/mozaic-vue.d.ts +1202 -0
  5. package/dist/mozaic-vue.js +1220 -0
  6. package/dist/mozaic-vue.js.map +1 -0
  7. package/dist/mozaic-vue.umd.cjs +2 -0
  8. package/dist/mozaic-vue.umd.cjs.map +1 -0
  9. package/env.d.ts +1 -0
  10. package/package.json +81 -50
  11. package/src/components/Contributing.mdx +118 -0
  12. package/src/components/GettingStarted.mdx +50 -0
  13. package/src/components/Introduction.mdx +54 -0
  14. package/src/components/Support.mdx +18 -0
  15. package/src/components/badge/MBadge.spec.ts +16 -0
  16. package/src/components/badge/MBadge.stories.ts +50 -0
  17. package/src/components/badge/MBadge.vue +36 -34
  18. package/src/components/breadcrumb/MBreadcrumb.spec.ts +105 -0
  19. package/src/components/breadcrumb/MBreadcrumb.stories.ts +57 -0
  20. package/src/components/breadcrumb/MBreadcrumb.vue +52 -55
  21. package/src/components/button/MButton.spec.ts +191 -0
  22. package/src/components/button/MButton.stories.ts +66 -0
  23. package/src/components/button/MButton.vue +98 -154
  24. package/src/components/checkbox/MCheckbox.spec.ts +104 -0
  25. package/src/components/checkbox/MCheckbox.stories.ts +83 -0
  26. package/src/components/checkbox/MCheckbox.vue +60 -101
  27. package/src/components/checkboxgroup/MCheckboxGroup.spec.ts +78 -0
  28. package/src/components/checkboxgroup/MCheckboxGroup.stories.ts +61 -0
  29. package/src/components/checkboxgroup/MCheckboxGroup.vue +97 -0
  30. package/src/components/field/MField.spec.ts +166 -0
  31. package/src/components/field/MField.stories.ts +376 -0
  32. package/src/components/field/MField.vue +78 -61
  33. package/src/components/fieldgroup/MFieldGroup.spec.ts +165 -0
  34. package/src/components/fieldgroup/MFieldGroup.stories.ts +423 -0
  35. package/src/components/fieldgroup/MFieldGroup.vue +79 -0
  36. package/src/components/iconbutton/MIconButton.spec.ts +108 -0
  37. package/src/components/iconbutton/MIconButton.stories.ts +66 -0
  38. package/src/components/iconbutton/MIconButton.vue +73 -0
  39. package/src/components/link/MLink.spec.ts +154 -0
  40. package/src/components/link/MLink.stories.ts +98 -0
  41. package/src/components/link/MLink.vue +86 -109
  42. package/src/components/loader/MLoader.spec.ts +104 -0
  43. package/src/components/loader/MLoader.stories.ts +45 -0
  44. package/src/components/loader/MLoader.vue +65 -55
  45. package/src/components/overlay/MOverlay.spec.ts +51 -0
  46. package/src/components/overlay/MOverlay.stories.ts +40 -0
  47. package/src/components/overlay/MOverlay.vue +27 -19
  48. package/src/components/passwordinput/MPasswordInput.spec.ts +104 -0
  49. package/src/components/passwordinput/MPasswordInput.stories.ts +75 -0
  50. package/src/components/passwordinput/MPasswordInput.vue +129 -76
  51. package/src/components/quantityselector/MQuantitySelector.spec.ts +262 -0
  52. package/src/components/quantityselector/MQuantitySelector.stories.ts +89 -0
  53. package/src/components/quantityselector/MQuantitySelector.vue +160 -136
  54. package/src/components/radio/MRadio.spec.ts +104 -0
  55. package/src/components/radio/MRadio.stories.ts +68 -0
  56. package/src/components/radio/MRadio.vue +56 -39
  57. package/src/components/radiogroup/MRadioGroup.spec.ts +54 -0
  58. package/src/components/radiogroup/MRadioGroup.stories.ts +61 -0
  59. package/src/components/radiogroup/MRadioGroup.vue +79 -0
  60. package/src/components/select/MSelect.spec.ts +114 -0
  61. package/src/components/select/MSelect.stories.ts +101 -0
  62. package/src/components/select/MSelect.vue +77 -119
  63. package/src/components/statusbadge/MStatusBadge.stories.ts +45 -0
  64. package/src/components/statusbadge/MStatusBadge.vue +40 -0
  65. package/src/components/statusbadge/MstatusBadge.spec.ts +16 -0
  66. package/src/components/statusdot/MStatusDot.spec.ts +51 -0
  67. package/src/components/statusdot/MStatusDot.stories.ts +48 -0
  68. package/src/components/statusdot/MStatusDot.vue +36 -0
  69. package/src/components/statusnotification/MStatusNotification.spec.ts +99 -0
  70. package/src/components/statusnotification/MStatusNotification.stories.ts +96 -0
  71. package/src/components/statusnotification/MStatusNotification.vue +106 -0
  72. package/src/components/textarea/MTextArea.spec.ts +112 -0
  73. package/src/components/textarea/MTextArea.stories.ts +67 -0
  74. package/src/components/textarea/MTextArea.vue +81 -42
  75. package/src/components/textinput/MTextInput.spec.ts +121 -0
  76. package/src/components/textinput/MTextInput.stories.ts +114 -0
  77. package/src/components/textinput/MTextInput.vue +127 -47
  78. package/src/components/toggle/MToggle.spec.ts +99 -0
  79. package/src/components/toggle/MToggle.stories.ts +68 -0
  80. package/src/components/toggle/MToggle.vue +63 -103
  81. package/src/components/togglegroup/MToggleGroup.spec.ts +78 -0
  82. package/src/components/togglegroup/MToggleGroup.stories.ts +61 -0
  83. package/src/components/togglegroup/MToggleGroup.vue +97 -0
  84. package/src/components/usingIcons.mdx +43 -0
  85. package/src/components/usingPresets.mdx +125 -0
  86. package/src/main.ts +47 -0
  87. package/dist/demo.html +0 -1
  88. package/dist/mozaic-vue.adeo.css +0 -45
  89. package/dist/mozaic-vue.adeo.umd.js +0 -41775
  90. package/dist/mozaic-vue.common.js +0 -41765
  91. package/dist/mozaic-vue.common.js.map +0 -1
  92. package/dist/mozaic-vue.umd.js +0 -41776
  93. package/dist/mozaic-vue.umd.js.map +0 -1
  94. package/dist/mozaic-vue.umd.min.js +0 -4
  95. package/dist/mozaic-vue.umd.min.js.map +0 -1
  96. package/postinstall.js +0 -3
  97. package/src/components/accordion/MAccordion.vue +0 -128
  98. package/src/components/accordion/index.js +0 -7
  99. package/src/components/autocomplete/MAutocomplete.vue +0 -198
  100. package/src/components/autocomplete/index.js +0 -7
  101. package/src/components/badge/index.js +0 -7
  102. package/src/components/breadcrumb/index.js +0 -7
  103. package/src/components/button/index.js +0 -7
  104. package/src/components/card/MCard.vue +0 -78
  105. package/src/components/card/index.js +0 -7
  106. package/src/components/checkbox/MCheckboxGroup.vue +0 -155
  107. package/src/components/checkbox/index.js +0 -12
  108. package/src/components/container/MContainer.vue +0 -33
  109. package/src/components/container/index.js +0 -7
  110. package/src/components/datatable/MDataTable.vue +0 -651
  111. package/src/components/datatable/MDataTableHeader.vue +0 -55
  112. package/src/components/datatable/MDataTableTop.vue +0 -35
  113. package/src/components/datatable/helpers.js +0 -132
  114. package/src/components/datatable/index.js +0 -12
  115. package/src/components/field/index.js +0 -7
  116. package/src/components/fileuploader/MFileResult.vue +0 -149
  117. package/src/components/fileuploader/MFileUploader.vue +0 -142
  118. package/src/components/fileuploader/index.js +0 -7
  119. package/src/components/flag/MFlag.vue +0 -46
  120. package/src/components/flag/index.js +0 -7
  121. package/src/components/heading/MHeading.vue +0 -75
  122. package/src/components/heading/index.js +0 -7
  123. package/src/components/hero/MHero.vue +0 -93
  124. package/src/components/hero/index.js +0 -7
  125. package/src/components/icon/MIcon.vue +0 -120
  126. package/src/components/icon/index.js +0 -7
  127. package/src/components/index.js +0 -43
  128. package/src/components/layer/MLayer.vue +0 -208
  129. package/src/components/layer/index.js +0 -7
  130. package/src/components/link/index.js +0 -7
  131. package/src/components/listbox/MListBox.vue +0 -106
  132. package/src/components/listbox/index.js +0 -7
  133. package/src/components/loader/index.js +0 -7
  134. package/src/components/modal/MModal.vue +0 -179
  135. package/src/components/modal/index.js +0 -7
  136. package/src/components/notification/MNotification.vue +0 -110
  137. package/src/components/notification/index.js +0 -7
  138. package/src/components/optionbutton/MOptionButton.vue +0 -67
  139. package/src/components/optionbutton/index.js +0 -7
  140. package/src/components/optioncard/MOptionCard.vue +0 -132
  141. package/src/components/optioncard/index.js +0 -7
  142. package/src/components/optiongroup/MOptionGroup.vue +0 -18
  143. package/src/components/optiongroup/index.js +0 -7
  144. package/src/components/overlay/MOverlayLoader.vue +0 -43
  145. package/src/components/overlay/index.js +0 -12
  146. package/src/components/pagination/MPagination.vue +0 -162
  147. package/src/components/pagination/index.js +0 -7
  148. package/src/components/passwordinput/index.js +0 -7
  149. package/src/components/phonenumber/MPhoneNumber.vue +0 -390
  150. package/src/components/phonenumber/index.js +0 -7
  151. package/src/components/progressbar/MProgress.vue +0 -102
  152. package/src/components/progressbar/index.js +0 -7
  153. package/src/components/quantityselector/index.js +0 -7
  154. package/src/components/radio/MRadioGroup.vue +0 -111
  155. package/src/components/radio/index.js +0 -12
  156. package/src/components/ratingstars/MStarsInput.vue +0 -118
  157. package/src/components/ratingstars/MStarsResult.vue +0 -89
  158. package/src/components/ratingstars/index.js +0 -12
  159. package/src/components/select/index.js +0 -7
  160. package/src/components/stepper/MStepper.vue +0 -70
  161. package/src/components/stepper/index.js +0 -7
  162. package/src/components/tabs/MTab.vue +0 -184
  163. package/src/components/tabs/index.js +0 -7
  164. package/src/components/tags/MTag.vue +0 -173
  165. package/src/components/tags/index.js +0 -7
  166. package/src/components/textarea/index.js +0 -7
  167. package/src/components/textinput/MTextInputField.vue +0 -105
  168. package/src/components/textinput/MTextInputIcon.vue +0 -42
  169. package/src/components/textinput/index.js +0 -7
  170. package/src/components/toggle/index.js +0 -7
  171. package/src/components/tooltip/MTooltip.vue +0 -42
  172. package/src/components/tooltip/index.js +0 -7
  173. package/src/index.js +0 -62
  174. package/src/shims-tsx.d.ts +0 -13
  175. package/src/shims.vue.d.ts +0 -4
  176. package/src/tokens/adeo/android/colors.xml +0 -391
  177. package/src/tokens/adeo/android/font_dimens.xml +0 -18
  178. package/src/tokens/adeo/css/_variables.scss +0 -385
  179. package/src/tokens/adeo/css/root.scss +0 -387
  180. package/src/tokens/adeo/ios/StyleDictionaryColor.h +0 -399
  181. package/src/tokens/adeo/ios/StyleDictionaryColor.m +0 -411
  182. package/src/tokens/adeo/ios/StyleDictionaryColor.swift +0 -394
  183. package/src/tokens/adeo/ios/StyleDictionarySize.h +0 -69
  184. package/src/tokens/adeo/ios/StyleDictionarySize.m +0 -70
  185. package/src/tokens/adeo/ios/StyleDictionarySize.swift +0 -71
  186. package/src/tokens/adeo/js/tokens.js +0 -483
  187. package/src/tokens/adeo/js/tokensObject.js +0 -10354
  188. package/src/tokens/adeo/scss/_tokens.scss +0 -1300
  189. package/src/utils/mozaicClasses.js +0 -16
  190. package/src/utils/theme.validator.js +0 -19
  191. package/types/index.d.ts +0 -100
@@ -0,0 +1,73 @@
1
+ <template>
2
+ <button
3
+ class="mc-button mc-button--icon-button"
4
+ :class="classObject"
5
+ :disabled="disabled"
6
+ :type="type"
7
+ >
8
+ <span class="mc-button__icon">
9
+ <slot name="icon" />
10
+ </span>
11
+ </button>
12
+ </template>
13
+
14
+ <script setup lang="ts">
15
+ import { computed, type VNode } from 'vue';
16
+ /**
17
+ * Icon Buttons are used to trigger actions. Their appearance is depending on the type of action required from the user, or the context.
18
+ */
19
+ const props = withDefaults(
20
+ defineProps<{
21
+ /**
22
+ * Defines the visual style of the icon button.
23
+ */
24
+ appearance?: 'standard' | 'accent' | 'danger' | 'inverse';
25
+ /**
26
+ * Determines the size of the icon button.
27
+ */
28
+ size?: 's' | 'm' | 'l';
29
+ /**
30
+ * If `true`, disables the icon button, making it non-interactive.
31
+ */
32
+ disabled?: boolean;
33
+ /**
34
+ * If `true`, applies a "ghost" style to the icon button.
35
+ */
36
+ ghost?: boolean;
37
+ /**
38
+ * If `true`, the icon button gets an outlined style.
39
+ */
40
+ outlined?: boolean;
41
+ /**
42
+ * Specifies the button's HTML `type` attribute.
43
+ */
44
+ type?: 'button' | 'reset' | 'submit';
45
+ }>(),
46
+ {
47
+ appearance: 'standard',
48
+ size: 'm',
49
+ type: 'button',
50
+ },
51
+ );
52
+
53
+ defineSlots<{
54
+ /**
55
+ * Use this slot to insert the form element of your choice
56
+ */
57
+ icon: VNode;
58
+ }>();
59
+
60
+ const classObject = computed(() => {
61
+ return {
62
+ [`mc-button--${props.appearance}`]:
63
+ props.appearance && props.appearance != 'standard',
64
+ [`mc-button--${props.size}`]: props.size && props.size != 'm',
65
+ 'mc-button--ghost': props.ghost,
66
+ 'mc-button--outlined': props.outlined,
67
+ };
68
+ });
69
+ </script>
70
+
71
+ <style lang="scss" scoped>
72
+ @use '@mozaic-ds/styles/components/button';
73
+ </style>
@@ -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 : href && !disabled ? 'a' : 'span'"
4
- :href="router ? null : href && !disabled ? href : null"
3
+ :is="router ? 'router-link' : 'a'"
5
4
  class="mc-link"
6
- :class="setClasses"
7
- @click="onClick"
5
+ :class="classObject"
6
+ :href="href"
7
+ :target="target"
8
+ :to="router ? href : undefined"
8
9
  >
9
- <m-icon
10
- v-if="icon && iconPosition === 'left'"
11
- :class="`mc-link__icon mc-link__icon--${iconPosition}`"
12
- :name="icon"
13
- />
14
- <slot />
15
- <m-icon
16
- v-if="icon && iconPosition === 'right'"
17
- :class="`mc-link__icon mc-link__icon--${iconPosition}`"
18
- :name="icon"
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 { checkThemeValue } from '../../utils/theme.validator';
25
- import {
26
- responsiveModifierValidators,
27
- responsiveModifiers,
28
- } from '../../utils/mozaicClasses';
29
- import MIcon from '../icon/MIcon.vue';
30
-
31
- export default {
32
- name: 'MLink',
33
-
34
- components: {
35
- MIcon,
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
- props: {
39
- router: {
40
- type: [String, Object],
41
- default: null,
42
- },
43
- href: {
44
- type: String,
45
- default: '#',
46
- },
47
- theme: {
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
- if (this.size) {
99
- responsiveModifiers('mc-link', this.size, classes);
100
- }
101
-
102
- if (this.disabled) {
103
- classes.push('is-disabled');
104
- }
105
-
106
- return classes;
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
- @import 'settings-tools/all-settings';
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 is a visual indicator used to inform users that a process is in progress, typically during data fetching, page loading, or background operations. It provides feedback that the system is working, helping to manage user expectations and reduce perceived wait time.',
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
+ };