@mozaic-ds/vue 1.0.0-rc.3 → 2.1.0

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 (222) hide show
  1. package/LICENSE +51 -0
  2. package/README.md +76 -77
  3. package/dist/mozaic-vue.css +1 -1
  4. package/dist/mozaic-vue.d.ts +1664 -0
  5. package/dist/mozaic-vue.js +1943 -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 +78 -51
  11. package/src/components/Contributing.mdx +118 -0
  12. package/src/components/GettingStarted.mdx +45 -0
  13. package/src/components/Introduction.mdx +100 -0
  14. package/src/components/Support.mdx +18 -0
  15. package/src/components/breadcrumb/MBreadcrumb.spec.ts +105 -0
  16. package/src/components/breadcrumb/MBreadcrumb.stories.ts +82 -0
  17. package/src/components/breadcrumb/MBreadcrumb.vue +52 -55
  18. package/src/components/button/MButton.spec.ts +191 -0
  19. package/src/components/button/MButton.stories.ts +59 -0
  20. package/src/components/button/MButton.vue +98 -154
  21. package/src/components/checkbox/MCheckbox.spec.ts +104 -0
  22. package/src/components/checkbox/MCheckbox.stories.ts +83 -0
  23. package/src/components/checkbox/MCheckbox.vue +60 -101
  24. package/src/components/checkboxgroup/MCheckboxGroup.spec.ts +78 -0
  25. package/src/components/checkboxgroup/MCheckboxGroup.stories.ts +61 -0
  26. package/src/components/checkboxgroup/MCheckboxGroup.vue +97 -0
  27. package/src/components/datepicker/MDatepicker.spec.ts +95 -0
  28. package/src/components/datepicker/MDatepicker.stories.ts +75 -0
  29. package/src/components/datepicker/MDatepicker.vue +114 -0
  30. package/src/components/divider/MDivider.spec.ts +57 -0
  31. package/src/components/divider/MDivider.stories.ts +64 -0
  32. package/src/components/divider/MDivider.vue +56 -0
  33. package/src/components/drawer/MDrawer.spec.ts +100 -0
  34. package/src/components/drawer/MDrawer.stories.ts +128 -0
  35. package/src/components/drawer/MDrawer.vue +140 -0
  36. package/src/components/field/MField.spec.ts +166 -0
  37. package/src/components/field/MField.stories.ts +369 -0
  38. package/src/components/field/MField.vue +78 -61
  39. package/src/components/fieldgroup/MFieldGroup.spec.ts +165 -0
  40. package/src/components/fieldgroup/MFieldGroup.stories.ts +416 -0
  41. package/src/components/fieldgroup/MFieldGroup.vue +79 -0
  42. package/src/components/flag/MFlag.spec.ts +46 -0
  43. package/src/components/flag/MFlag.stories.ts +46 -0
  44. package/src/components/flag/MFlag.vue +28 -39
  45. package/src/components/iconbutton/MIconButton.spec.ts +108 -0
  46. package/src/components/iconbutton/MIconButton.stories.ts +74 -0
  47. package/src/components/iconbutton/MIconButton.vue +73 -0
  48. package/src/components/link/MLink.spec.ts +154 -0
  49. package/src/components/link/MLink.stories.ts +89 -0
  50. package/src/components/link/MLink.vue +86 -120
  51. package/src/components/loader/MLoader.spec.ts +104 -0
  52. package/src/components/loader/MLoader.stories.ts +43 -0
  53. package/src/components/loader/MLoader.vue +66 -55
  54. package/src/components/loadingoverlay/MLoadingOverlay.spec.ts +37 -0
  55. package/src/components/loadingoverlay/MLoadingOverlay.stories.ts +40 -0
  56. package/src/components/loadingoverlay/MLoadingOverlay.vue +28 -0
  57. package/src/components/modal/MModal.spec.ts +103 -0
  58. package/src/components/modal/MModal.stories.ts +127 -0
  59. package/src/components/modal/MModal.vue +111 -159
  60. package/src/components/numberbadge/MNumberBadge.spec.ts +56 -0
  61. package/src/components/numberbadge/MNumberBadge.stories.ts +48 -0
  62. package/src/components/numberbadge/MNumberBadge.vue +45 -0
  63. package/src/components/overlay/MOverlay.spec.ts +51 -0
  64. package/src/components/overlay/MOverlay.stories.ts +35 -0
  65. package/src/components/overlay/MOverlay.vue +27 -19
  66. package/src/components/pagination/MPagination.spec.ts +123 -0
  67. package/src/components/pagination/MPagination.stories.ts +83 -0
  68. package/src/components/pagination/MPagination.vue +120 -140
  69. package/src/components/passwordinput/MPasswordInput.spec.ts +104 -0
  70. package/src/components/passwordinput/MPasswordInput.stories.ts +75 -0
  71. package/src/components/passwordinput/MPasswordInput.vue +126 -77
  72. package/src/components/pincode/MPincode.spec.ts +126 -0
  73. package/src/components/pincode/MPincode.stories.ts +68 -0
  74. package/src/components/pincode/MPincode.vue +148 -0
  75. package/src/components/quantityselector/MQuantitySelector.spec.ts +262 -0
  76. package/src/components/quantityselector/MQuantitySelector.stories.ts +89 -0
  77. package/src/components/quantityselector/MQuantitySelector.vue +159 -148
  78. package/src/components/radio/MRadio.spec.ts +104 -0
  79. package/src/components/radio/MRadio.stories.ts +68 -0
  80. package/src/components/radio/MRadio.vue +56 -39
  81. package/src/components/radiogroup/MRadioGroup.spec.ts +54 -0
  82. package/src/components/radiogroup/MRadioGroup.stories.ts +61 -0
  83. package/src/components/radiogroup/MRadioGroup.vue +79 -0
  84. package/src/components/select/MSelect.spec.ts +114 -0
  85. package/src/components/select/MSelect.stories.ts +101 -0
  86. package/src/components/select/MSelect.vue +77 -119
  87. package/src/components/statusbadge/MStatusBadge.stories.ts +45 -0
  88. package/src/components/statusbadge/MStatusBadge.vue +40 -0
  89. package/src/components/statusbadge/MstatusBadge.spec.ts +16 -0
  90. package/src/components/statusdot/MStatusDot.spec.ts +51 -0
  91. package/src/components/statusdot/MStatusDot.stories.ts +48 -0
  92. package/src/components/statusdot/MStatusDot.vue +36 -0
  93. package/src/components/statusnotification/MStatusNotification.spec.ts +103 -0
  94. package/src/components/statusnotification/MStatusNotification.stories.ts +89 -0
  95. package/src/components/statusnotification/MStatusNotification.vue +106 -0
  96. package/src/components/tabs/MTabs.stories.ts +104 -0
  97. package/src/components/tabs/MTabs.vue +113 -0
  98. package/src/components/tabs/Mtabs.spec.ts +149 -0
  99. package/src/components/tag/MTag.spec.ts +107 -0
  100. package/src/components/tag/MTag.stories.ts +75 -0
  101. package/src/components/tag/MTag.vue +151 -0
  102. package/src/components/textarea/MTextArea.spec.ts +112 -0
  103. package/src/components/textarea/MTextArea.stories.ts +67 -0
  104. package/src/components/textarea/MTextArea.vue +81 -43
  105. package/src/components/textinput/MTextInput.spec.ts +121 -0
  106. package/src/components/textinput/MTextInput.stories.ts +107 -0
  107. package/src/components/textinput/MTextInput.vue +127 -47
  108. package/src/components/toggle/MToggle.spec.ts +99 -0
  109. package/src/components/toggle/MToggle.stories.ts +68 -0
  110. package/src/components/toggle/MToggle.vue +63 -103
  111. package/src/components/togglegroup/MToggleGroup.spec.ts +78 -0
  112. package/src/components/togglegroup/MToggleGroup.stories.ts +61 -0
  113. package/src/components/togglegroup/MToggleGroup.vue +97 -0
  114. package/src/components/usingIcons.mdx +35 -0
  115. package/src/components/usingPresets.mdx +128 -0
  116. package/src/main.ts +32 -0
  117. package/dist/demo.html +0 -1
  118. package/dist/mozaic-vue.adeo.css +0 -47
  119. package/dist/mozaic-vue.adeo.umd.js +0 -31341
  120. package/dist/mozaic-vue.common.js +0 -31331
  121. package/dist/mozaic-vue.common.js.map +0 -1
  122. package/dist/mozaic-vue.umd.js +0 -31342
  123. package/dist/mozaic-vue.umd.js.map +0 -1
  124. package/dist/mozaic-vue.umd.min.js +0 -4
  125. package/dist/mozaic-vue.umd.min.js.map +0 -1
  126. package/postinstall.js +0 -3
  127. package/src/components/accordion/MAccordion.vue +0 -128
  128. package/src/components/accordion/index.js +0 -7
  129. package/src/components/autocomplete/MAutocomplete.vue +0 -380
  130. package/src/components/autocomplete/index.js +0 -7
  131. package/src/components/badge/MBadge.vue +0 -43
  132. package/src/components/badge/index.js +0 -7
  133. package/src/components/breadcrumb/index.js +0 -7
  134. package/src/components/button/index.js +0 -7
  135. package/src/components/card/MCard.vue +0 -78
  136. package/src/components/card/index.js +0 -7
  137. package/src/components/checkbox/MCheckboxGroup.vue +0 -163
  138. package/src/components/checkbox/index.js +0 -12
  139. package/src/components/container/MContainer.vue +0 -33
  140. package/src/components/container/index.js +0 -7
  141. package/src/components/datatable/MDataTable.vue +0 -651
  142. package/src/components/datatable/MDataTableHeader.vue +0 -55
  143. package/src/components/datatable/MDataTableTop.vue +0 -35
  144. package/src/components/datatable/helpers.js +0 -132
  145. package/src/components/datatable/index.js +0 -12
  146. package/src/components/dropdown/MDropdown.vue +0 -317
  147. package/src/components/dropdown/index.js +0 -7
  148. package/src/components/field/index.js +0 -7
  149. package/src/components/fileuploader/MFileResult.vue +0 -149
  150. package/src/components/fileuploader/MFileUploader.vue +0 -142
  151. package/src/components/fileuploader/index.js +0 -7
  152. package/src/components/flag/index.js +0 -7
  153. package/src/components/heading/MHeading.vue +0 -75
  154. package/src/components/heading/index.js +0 -7
  155. package/src/components/hero/MHero.vue +0 -93
  156. package/src/components/hero/index.js +0 -7
  157. package/src/components/icon/MIcon.vue +0 -136
  158. package/src/components/icon/index.js +0 -7
  159. package/src/components/index.js +0 -44
  160. package/src/components/layer/MLayer.vue +0 -208
  161. package/src/components/layer/index.js +0 -7
  162. package/src/components/link/index.js +0 -7
  163. package/src/components/listbox/MListBox.vue +0 -146
  164. package/src/components/listbox/MListBoxActions.vue +0 -251
  165. package/src/components/listbox/index.js +0 -12
  166. package/src/components/loader/index.js +0 -7
  167. package/src/components/modal/index.js +0 -7
  168. package/src/components/notification/MNotification.vue +0 -110
  169. package/src/components/notification/index.js +0 -7
  170. package/src/components/optionbutton/MOptionButton.vue +0 -67
  171. package/src/components/optionbutton/index.js +0 -7
  172. package/src/components/optioncard/MOptionCard.vue +0 -132
  173. package/src/components/optioncard/index.js +0 -7
  174. package/src/components/optiongroup/MOptionGroup.vue +0 -18
  175. package/src/components/optiongroup/index.js +0 -7
  176. package/src/components/overlay/MOverlayLoader.vue +0 -43
  177. package/src/components/overlay/index.js +0 -12
  178. package/src/components/pagination/index.js +0 -7
  179. package/src/components/passwordinput/index.js +0 -7
  180. package/src/components/phonenumber/MPhoneNumber.vue +0 -398
  181. package/src/components/phonenumber/index.js +0 -7
  182. package/src/components/progressbar/MProgress.vue +0 -102
  183. package/src/components/progressbar/index.js +0 -7
  184. package/src/components/quantityselector/index.js +0 -7
  185. package/src/components/radio/MRadioGroup.vue +0 -111
  186. package/src/components/radio/index.js +0 -12
  187. package/src/components/ratingstars/MStarsInput.vue +0 -119
  188. package/src/components/ratingstars/MStarsResult.vue +0 -89
  189. package/src/components/ratingstars/index.js +0 -12
  190. package/src/components/select/index.js +0 -7
  191. package/src/components/stepper/MStepper.vue +0 -111
  192. package/src/components/stepper/index.js +0 -7
  193. package/src/components/tabs/MTab.vue +0 -204
  194. package/src/components/tabs/index.js +0 -7
  195. package/src/components/tags/MTag.vue +0 -175
  196. package/src/components/tags/index.js +0 -7
  197. package/src/components/textarea/index.js +0 -7
  198. package/src/components/textinput/MTextInputField.vue +0 -105
  199. package/src/components/textinput/MTextInputIcon.vue +0 -42
  200. package/src/components/textinput/index.js +0 -7
  201. package/src/components/toggle/index.js +0 -7
  202. package/src/components/tooltip/MTooltip.vue +0 -42
  203. package/src/components/tooltip/index.js +0 -7
  204. package/src/index.js +0 -63
  205. package/src/shims-tsx.d.ts +0 -13
  206. package/src/shims.vue.d.ts +0 -4
  207. package/src/tokens/adeo/android/colors.xml +0 -452
  208. package/src/tokens/adeo/android/font_dimens.xml +0 -18
  209. package/src/tokens/adeo/css/_variables.scss +0 -446
  210. package/src/tokens/adeo/css/root.scss +0 -448
  211. package/src/tokens/adeo/ios/StyleDictionaryColor.h +0 -460
  212. package/src/tokens/adeo/ios/StyleDictionaryColor.m +0 -472
  213. package/src/tokens/adeo/ios/StyleDictionaryColor.swift +0 -455
  214. package/src/tokens/adeo/ios/StyleDictionarySize.h +0 -69
  215. package/src/tokens/adeo/ios/StyleDictionarySize.m +0 -70
  216. package/src/tokens/adeo/ios/StyleDictionarySize.swift +0 -71
  217. package/src/tokens/adeo/js/tokens.js +0 -544
  218. package/src/tokens/adeo/js/tokensObject.js +0 -11733
  219. package/src/tokens/adeo/scss/_tokens.scss +0 -1522
  220. package/src/utils/mozaicClasses.js +0 -16
  221. package/src/utils/theme.validator.js +0 -19
  222. package/types/index.d.ts +0 -104
@@ -1,133 +1,99 @@
1
1
  <template>
2
2
  <component
3
- :is="linkComponentName"
4
- v-bind="linkComponentProps"
5
- :href="router ? null : href && !disabled ? href : null"
3
+ :is="router ? 'router-link' : 'a'"
6
4
  class="mc-link"
7
- :class="setClasses"
8
- @click="onClick"
5
+ :class="classObject"
6
+ :href="href"
7
+ :target="target"
8
+ :to="router ? href : undefined"
9
9
  >
10
- <m-icon
11
- v-if="icon && iconPosition === 'left'"
12
- :class="`mc-link__icon mc-link__icon--${iconPosition}`"
13
- :name="icon"
14
- />
15
- <slot />
16
- <m-icon
17
- v-if="icon && iconPosition === 'right'"
18
- :class="`mc-link__icon mc-link__icon--${iconPosition}`"
19
- :name="icon"
20
- />
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>
21
27
  </component>
22
28
  </template>
23
29
 
24
- <script>
25
- import { checkThemeValue } from '../../utils/theme.validator';
26
- import {
27
- responsiveModifierValidators,
28
- responsiveModifiers,
29
- } from '../../utils/mozaicClasses';
30
- import MIcon from '../icon/MIcon.vue';
31
-
32
- export default {
33
- name: 'MLink',
34
-
35
- components: {
36
- 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?: '_self' | '_blank' | '_parent' | '_top';
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',
37
72
  },
73
+ );
38
74
 
39
- props: {
40
- router: {
41
- type: Object,
42
- default: null,
43
- },
44
- href: {
45
- type: String,
46
- default: '#',
47
- },
48
- theme: {
49
- type: String,
50
- default: null,
51
- validator: (value) => checkThemeValue(value),
52
- },
53
- size: {
54
- type: String,
55
- default: 'm',
56
- validator: (value) => responsiveModifierValidators(value, ['s', 'm']),
57
- },
58
- // Experimental solution waiting on this issue https://github.com/adeo/mozaic-vue/issues/52
59
- responsiveSizeModifiers: {
60
- type: Array,
61
- default: null,
62
- validator: (array) =>
63
- array.every((e) =>
64
- responsiveModifierValidators(e, [
65
- 's@from-m',
66
- 's@from-l',
67
- 's@from-xl',
68
- 's@from-xxl',
69
- 'm@from-m',
70
- 'm@from-l',
71
- 'm@from-xl',
72
- 'm@from-xxl',
73
- ])
74
- ),
75
- },
76
- icon: {
77
- type: String,
78
- default: null,
79
- },
80
- iconPosition: {
81
- type: String,
82
- default: 'left',
83
- validator: (value) => ['left', 'right'].includes(value),
84
- },
85
- disabled: {
86
- type: Boolean,
87
- default: false,
88
- },
89
- },
90
-
91
- computed: {
92
- setClasses() {
93
- const classes = [];
94
-
95
- if (this.theme) {
96
- classes.push(`mc-link--${this.theme}`);
97
- }
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
+ }>();
98
85
 
99
- if (this.size) {
100
- responsiveModifiers('mc-link', this.size, classes);
101
- }
102
-
103
- if (this.disabled) {
104
- classes.push('is-disabled');
105
- }
106
-
107
- return classes;
108
- },
109
- linkComponentName() {
110
- const isLinkValid = this.href && !this.disabled;
111
- const routerComponent = this.router?.name ?? null;
112
- const defaultComponent = isLinkValid ? 'a' : 'span';
113
-
114
- return routerComponent ?? defaultComponent;
115
- },
116
- linkComponentProps() {
117
- return this.router?.props ?? {};
118
- },
119
- },
120
- methods: {
121
- onClick(event) {
122
- if (!this.disabled) {
123
- this.$emit('click', event);
124
- }
125
- },
126
- },
127
- };
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
+ });
128
95
  </script>
129
96
 
130
- <style lang="scss">
131
- @import 'settings-tools/all-settings';
132
- @import 'components/c.links';
97
+ <style lang="scss" scoped>
98
+ @use '@mozaic-ds/styles/components/link';
133
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,43 @@
1
+ import type { Meta, StoryObj } from '@storybook/vue3-vite';
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
+ globals: {
36
+ backgrounds: { value: 'inverse' },
37
+ },
38
+ args: { appearance: 'inverse' },
39
+ };
40
+
41
+ export const Size: Story = {
42
+ args: { size: 's' },
43
+ };
@@ -1,84 +1,95 @@
1
1
  <template>
2
- <div class="mc-loader" :class="setClasses">
2
+ <div class="mc-loader" :class="classObject">
3
3
  <span class="mc-loader__spinner">
4
4
  <svg
5
5
  class="mc-loader__icon"
6
6
  xmlns="http://www.w3.org/2000/svg"
7
- :viewBox="viewBox"
7
+ :viewBox="setViewBox"
8
+ aria-hidden="true"
8
9
  >
9
- <circle class="mc-loader__path" cx="50%" cy="50%" :r="pathR" />
10
+ <circle
11
+ class="mc-loader__path"
12
+ cx="50%"
13
+ cy="50%"
14
+ :r="setCircleRadius"
15
+ ></circle>
10
16
  </svg>
11
17
  </span>
12
-
13
- <span v-if="text" class="mc-loader__text">{{ text }}</span>
18
+ <p v-if="text" class="mc-loader__text" role="status">{{ text }}</p>
14
19
  </div>
15
20
  </template>
16
21
 
17
- <script>
18
- export default {
19
- name: 'MLoader',
20
-
21
- props: {
22
+ <script setup lang="ts">
23
+ import { computed } from 'vue';
24
+ /**
25
+ * A loader indicates that content or data is being loaded or processed, providing visual feedback to users during wait times.
26
+ */
27
+ const props = withDefaults(
28
+ defineProps<{
22
29
  /**
23
- * Loader size
24
- * @values s, m , l
30
+ * Specifies the visual appearance of the loader.
25
31
  */
26
- size: {
27
- type: String,
28
- default: 'm',
29
- validator: (value) => ['s', 'm', 'l'].includes(value),
30
- },
32
+ appearance?: 'standard' | 'accent' | 'inverse';
31
33
 
32
34
  /**
33
- * Loader theme
34
- * @values dark, light, primary
35
+ * Defines the size of the loader.
35
36
  */
36
- theme: {
37
- type: String,
38
- default: 'primary',
39
- validator: (value) => ['dark', 'light', 'primary'].includes(value),
40
- },
37
+ size?: 's' | 'm' | 'l';
41
38
 
42
39
  /**
43
- * Loader text - when using the loader inside an overlay
40
+ * Text to display alongside the loader when using the loader inside an `Overlay`.
44
41
  */
45
- text: {
46
- type: String,
47
- default: undefined,
48
- },
42
+ text?: string;
43
+ }>(),
44
+ {
45
+ appearance: 'standard',
46
+ size: 'm',
49
47
  },
48
+ );
50
49
 
51
- computed: {
52
- setClasses() {
53
- const classes = [];
50
+ const classObject = computed(() => {
51
+ return {
52
+ [`mc-loader--${props.size}`]: props.size && props.size !== 'm',
53
+ [`mc-loader--${props.appearance}`]:
54
+ props.appearance && props.appearance !== 'standard',
55
+ 'mc-loader--text-visible': props.text,
56
+ };
57
+ });
54
58
 
55
- if (this.size && this.size !== 'm') {
56
- classes.push(`mc-loader--${this.size}`);
57
- }
59
+ const setViewBox = computed(() => {
60
+ let viewBox: string;
58
61
 
59
- if (this.theme && this.theme !== 'primary') {
60
- classes.push(`mc-loader--${this.theme}`);
61
- }
62
+ switch (props.size) {
63
+ case 's':
64
+ viewBox = '0 0 24 24';
65
+ break;
66
+ case 'l':
67
+ viewBox = '0 0 64 64';
68
+ break;
69
+ default:
70
+ viewBox = '0 0 32 32';
71
+ }
72
+ return viewBox;
73
+ });
62
74
 
63
- return classes;
64
- },
75
+ const setCircleRadius = computed(() => {
76
+ let circleRadius: number;
65
77
 
66
- viewBox() {
67
- return this.size === 'm'
68
- ? '0 0 32 32'
69
- : this.size === 's'
70
- ? '0 0 24 24'
71
- : '0 0 64 64';
72
- },
78
+ switch (props.size) {
79
+ case 's':
80
+ circleRadius = 6;
81
+ break;
82
+ case 'l':
83
+ circleRadius = 19;
84
+ break;
85
+ default:
86
+ circleRadius = 9;
87
+ }
73
88
 
74
- pathR() {
75
- return this.size === 'm' ? 9 : this.size === 's' ? 6 : 19;
76
- },
77
- },
78
- };
89
+ return circleRadius;
90
+ });
79
91
  </script>
80
92
 
81
- <style lang="scss">
82
- @import 'settings-tools/all-settings';
83
- @import 'components/c.loader';
93
+ <style lang="scss" scoped>
94
+ @use '@mozaic-ds/styles/components/loader';
84
95
  </style>
@@ -0,0 +1,37 @@
1
+ import { describe, it, expect } from 'vitest';
2
+ import { mount } from '@vue/test-utils';
3
+ import MLoadingOverlay from './MLoadingOverlay.vue';
4
+
5
+ describe('MLoadingOverlay component', () => {
6
+ it('renders and applies isVisible class based on prop', () => {
7
+ const wrapper = mount(MLoadingOverlay, {
8
+ props: { isVisible: true, text: 'Loading data...' },
9
+ });
10
+
11
+ expect(wrapper.classes()).toContain('is-visible');
12
+
13
+ const dialog = wrapper.find('[role="dialog"]');
14
+ expect(dialog.attributes('aria-label')).toBe('Loading data...');
15
+ });
16
+
17
+ it('does not have is-visible class when isVisible is false or undefined', () => {
18
+ const wrapper = mount(MLoadingOverlay, {
19
+ props: { isVisible: false },
20
+ });
21
+ expect(wrapper.classes()).not.toContain('is-visible');
22
+
23
+ const wrapperNoProp = mount(MLoadingOverlay);
24
+ expect(wrapperNoProp.classes()).not.toContain('is-visible');
25
+ });
26
+
27
+ it('passes down attributes via $attrs', () => {
28
+ const wrapper = mount(MLoadingOverlay, {
29
+ props: { isVisible: true, text: 'Loading...' },
30
+ attrs: { id: 'custom-id', 'data-test': 'loading-overlay' },
31
+ });
32
+
33
+ const dialog = wrapper.find('[role="dialog"]');
34
+ expect(dialog.attributes('id')).toBe('custom-id');
35
+ expect(dialog.attributes('data-test')).toBe('loading-overlay');
36
+ });
37
+ });
@@ -0,0 +1,40 @@
1
+ import type { Meta, StoryObj } from '@storybook/vue3-vite';
2
+ import MLoadingOverlay from './MLoadingOverlay.vue';
3
+
4
+ const meta: Meta<typeof MLoadingOverlay> = {
5
+ title: 'Overlay/Loading Overlay',
6
+ component: MLoadingOverlay,
7
+ parameters: {
8
+ layout: 'fullscreen',
9
+ docs: {
10
+ story: { height: '400px' },
11
+ description: {
12
+ component:
13
+ 'An overlay component is a UI element that appears above the main content to display additional information or interactions, often blocking or dimming the background.',
14
+ },
15
+ },
16
+ },
17
+ args: {
18
+ isVisible: true,
19
+ ariaLabel: 'Page loading',
20
+ },
21
+ render: (args) => ({
22
+ components: { MLoadingOverlay },
23
+ setup() {
24
+ return { args };
25
+ },
26
+ template: `
27
+ <MLoadingOverlay v-bind="args"/>
28
+ `,
29
+ }),
30
+ };
31
+ export default meta;
32
+ type Story = StoryObj<typeof MLoadingOverlay>;
33
+
34
+ export const Default: Story = {};
35
+
36
+ export const text: Story = {
37
+ args: {
38
+ text: 'Insert your text here',
39
+ },
40
+ };
@@ -0,0 +1,28 @@
1
+ <template>
2
+ <div class="mc-loading-loader" :class="{ 'is-visible': isVisible }">
3
+ <div role="dialog" tabindex="-1" :aria-label="text" v-bind="$attrs">
4
+ <MLoader size="l" appearance="inverse" :text="text" />
5
+ </div>
6
+ </div>
7
+ </template>
8
+
9
+ <script setup lang="ts">
10
+ import MLoader from '../loader/MLoader.vue';
11
+ /**
12
+ * A loading overlay is a full-screen or container-level layer that indicates a process is in progress, preventing user interaction until the task is completed. It includes a progress indicator, and a message to inform users about the loading state. Loading Overlays are commonly used in data-heavy applications, form submissions, and page transitions to enhance user experience by managing wait times effectively.
13
+ */
14
+ defineProps<{
15
+ /**
16
+ * Controls the visibility of the loading overlay.
17
+ */
18
+ isVisible?: boolean;
19
+ /**
20
+ * Text of the loading overlay.
21
+ */
22
+ text?: string;
23
+ }>();
24
+ </script>
25
+
26
+ <style lang="scss" scoped>
27
+ @use '@mozaic-ds/styles/components/loading-overlay';
28
+ </style>