@mozaic-ds/vue 1.0.0-rc.3 → 2.2.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 (224) 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 +1718 -0
  5. package/dist/mozaic-vue.js +1971 -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/tooltip/MTooltip.spec.ts +47 -0
  115. package/src/components/tooltip/MTooltip.stories.ts +59 -0
  116. package/src/components/tooltip/MTooltip.vue +49 -32
  117. package/src/components/usingIcons.mdx +35 -0
  118. package/src/components/usingPresets.mdx +128 -0
  119. package/src/main.ts +33 -0
  120. package/dist/demo.html +0 -1
  121. package/dist/mozaic-vue.adeo.css +0 -47
  122. package/dist/mozaic-vue.adeo.umd.js +0 -31341
  123. package/dist/mozaic-vue.common.js +0 -31331
  124. package/dist/mozaic-vue.common.js.map +0 -1
  125. package/dist/mozaic-vue.umd.js +0 -31342
  126. package/dist/mozaic-vue.umd.js.map +0 -1
  127. package/dist/mozaic-vue.umd.min.js +0 -4
  128. package/dist/mozaic-vue.umd.min.js.map +0 -1
  129. package/postinstall.js +0 -3
  130. package/src/components/accordion/MAccordion.vue +0 -128
  131. package/src/components/accordion/index.js +0 -7
  132. package/src/components/autocomplete/MAutocomplete.vue +0 -380
  133. package/src/components/autocomplete/index.js +0 -7
  134. package/src/components/badge/MBadge.vue +0 -43
  135. package/src/components/badge/index.js +0 -7
  136. package/src/components/breadcrumb/index.js +0 -7
  137. package/src/components/button/index.js +0 -7
  138. package/src/components/card/MCard.vue +0 -78
  139. package/src/components/card/index.js +0 -7
  140. package/src/components/checkbox/MCheckboxGroup.vue +0 -163
  141. package/src/components/checkbox/index.js +0 -12
  142. package/src/components/container/MContainer.vue +0 -33
  143. package/src/components/container/index.js +0 -7
  144. package/src/components/datatable/MDataTable.vue +0 -651
  145. package/src/components/datatable/MDataTableHeader.vue +0 -55
  146. package/src/components/datatable/MDataTableTop.vue +0 -35
  147. package/src/components/datatable/helpers.js +0 -132
  148. package/src/components/datatable/index.js +0 -12
  149. package/src/components/dropdown/MDropdown.vue +0 -317
  150. package/src/components/dropdown/index.js +0 -7
  151. package/src/components/field/index.js +0 -7
  152. package/src/components/fileuploader/MFileResult.vue +0 -149
  153. package/src/components/fileuploader/MFileUploader.vue +0 -142
  154. package/src/components/fileuploader/index.js +0 -7
  155. package/src/components/flag/index.js +0 -7
  156. package/src/components/heading/MHeading.vue +0 -75
  157. package/src/components/heading/index.js +0 -7
  158. package/src/components/hero/MHero.vue +0 -93
  159. package/src/components/hero/index.js +0 -7
  160. package/src/components/icon/MIcon.vue +0 -136
  161. package/src/components/icon/index.js +0 -7
  162. package/src/components/index.js +0 -44
  163. package/src/components/layer/MLayer.vue +0 -208
  164. package/src/components/layer/index.js +0 -7
  165. package/src/components/link/index.js +0 -7
  166. package/src/components/listbox/MListBox.vue +0 -146
  167. package/src/components/listbox/MListBoxActions.vue +0 -251
  168. package/src/components/listbox/index.js +0 -12
  169. package/src/components/loader/index.js +0 -7
  170. package/src/components/modal/index.js +0 -7
  171. package/src/components/notification/MNotification.vue +0 -110
  172. package/src/components/notification/index.js +0 -7
  173. package/src/components/optionbutton/MOptionButton.vue +0 -67
  174. package/src/components/optionbutton/index.js +0 -7
  175. package/src/components/optioncard/MOptionCard.vue +0 -132
  176. package/src/components/optioncard/index.js +0 -7
  177. package/src/components/optiongroup/MOptionGroup.vue +0 -18
  178. package/src/components/optiongroup/index.js +0 -7
  179. package/src/components/overlay/MOverlayLoader.vue +0 -43
  180. package/src/components/overlay/index.js +0 -12
  181. package/src/components/pagination/index.js +0 -7
  182. package/src/components/passwordinput/index.js +0 -7
  183. package/src/components/phonenumber/MPhoneNumber.vue +0 -398
  184. package/src/components/phonenumber/index.js +0 -7
  185. package/src/components/progressbar/MProgress.vue +0 -102
  186. package/src/components/progressbar/index.js +0 -7
  187. package/src/components/quantityselector/index.js +0 -7
  188. package/src/components/radio/MRadioGroup.vue +0 -111
  189. package/src/components/radio/index.js +0 -12
  190. package/src/components/ratingstars/MStarsInput.vue +0 -119
  191. package/src/components/ratingstars/MStarsResult.vue +0 -89
  192. package/src/components/ratingstars/index.js +0 -12
  193. package/src/components/select/index.js +0 -7
  194. package/src/components/stepper/MStepper.vue +0 -111
  195. package/src/components/stepper/index.js +0 -7
  196. package/src/components/tabs/MTab.vue +0 -204
  197. package/src/components/tabs/index.js +0 -7
  198. package/src/components/tags/MTag.vue +0 -175
  199. package/src/components/tags/index.js +0 -7
  200. package/src/components/textarea/index.js +0 -7
  201. package/src/components/textinput/MTextInputField.vue +0 -105
  202. package/src/components/textinput/MTextInputIcon.vue +0 -42
  203. package/src/components/textinput/index.js +0 -7
  204. package/src/components/toggle/index.js +0 -7
  205. package/src/components/tooltip/index.js +0 -7
  206. package/src/index.js +0 -63
  207. package/src/shims-tsx.d.ts +0 -13
  208. package/src/shims.vue.d.ts +0 -4
  209. package/src/tokens/adeo/android/colors.xml +0 -452
  210. package/src/tokens/adeo/android/font_dimens.xml +0 -18
  211. package/src/tokens/adeo/css/_variables.scss +0 -446
  212. package/src/tokens/adeo/css/root.scss +0 -448
  213. package/src/tokens/adeo/ios/StyleDictionaryColor.h +0 -460
  214. package/src/tokens/adeo/ios/StyleDictionaryColor.m +0 -472
  215. package/src/tokens/adeo/ios/StyleDictionaryColor.swift +0 -455
  216. package/src/tokens/adeo/ios/StyleDictionarySize.h +0 -69
  217. package/src/tokens/adeo/ios/StyleDictionarySize.m +0 -70
  218. package/src/tokens/adeo/ios/StyleDictionarySize.swift +0 -71
  219. package/src/tokens/adeo/js/tokens.js +0 -544
  220. package/src/tokens/adeo/js/tokensObject.js +0 -11733
  221. package/src/tokens/adeo/scss/_tokens.scss +0 -1522
  222. package/src/utils/mozaicClasses.js +0 -16
  223. package/src/utils/theme.validator.js +0 -19
  224. package/types/index.d.ts +0 -104
@@ -0,0 +1,191 @@
1
+ import { mount } from '@vue/test-utils';
2
+ import { describe, it, expect } from 'vitest';
3
+ import MButton from './MButton.vue';
4
+ import ChevronRight24 from '@mozaic-ds/icons-vue/src/components/ChevronRight24/ChevronRight24.vue';
5
+ import MLoader from '../loader/MLoader.vue';
6
+
7
+ describe('MButton component', () => {
8
+ it('renders with a label', () => {
9
+ const wrapper = mount(MButton, {
10
+ slots: {
11
+ default: 'Click Me',
12
+ },
13
+ });
14
+ expect(wrapper.text()).toContain('Click Me');
15
+ });
16
+
17
+ it('applies the correct appearance class based on the appearance prop', () => {
18
+ const wrapper = mount(MButton, {
19
+ props: {
20
+ appearance: 'accent',
21
+ },
22
+ slots: {
23
+ default: 'Styled Button',
24
+ },
25
+ });
26
+
27
+ expect(wrapper.classes()).toContain('mc-button--accent');
28
+ });
29
+
30
+ it('applies the correct size class based on the size prop', () => {
31
+ const wrapper = mount(MButton, {
32
+ props: {
33
+ label: 'Sized Button',
34
+ size: 'l',
35
+ },
36
+ slots: {
37
+ default: 'Sized Button',
38
+ },
39
+ });
40
+
41
+ expect(wrapper.classes()).toContain('mc-button--l');
42
+ });
43
+
44
+ it('disables the button when the disabled prop is true', async () => {
45
+ const wrapper = mount(MButton, {
46
+ props: {
47
+ disabled: true,
48
+ },
49
+ slots: {
50
+ default: 'Disabled Button',
51
+ },
52
+ });
53
+
54
+ const button = wrapper.find('button');
55
+ expect(button.attributes('disabled')).toBeDefined();
56
+ });
57
+
58
+ it('applies the correct ghost class when ghost prop is true', () => {
59
+ const wrapper = mount(MButton, {
60
+ props: {
61
+ ghost: true,
62
+ },
63
+ slots: {
64
+ default: 'Ghost Button',
65
+ },
66
+ });
67
+
68
+ expect(wrapper.classes()).toContain('mc-button--ghost');
69
+ });
70
+
71
+ it('applies the outlined class when outlined prop is true', () => {
72
+ const wrapper = mount(MButton, {
73
+ props: {
74
+ outlined: true,
75
+ },
76
+ slots: {
77
+ default: 'Outlined Button',
78
+ },
79
+ });
80
+
81
+ expect(wrapper.classes()).toContain('mc-button--outlined');
82
+ });
83
+
84
+ it('has type="button" by default', () => {
85
+ const wrapper = mount(MButton, {
86
+ slots: {
87
+ default: 'Default Button',
88
+ },
89
+ });
90
+
91
+ const button = wrapper.find('button');
92
+ expect(button.attributes('type')).toBe('button');
93
+ });
94
+
95
+ it('can have type="submit" when the type prop is "submit"', () => {
96
+ const wrapper = mount(MButton, {
97
+ props: {
98
+ type: 'submit',
99
+ },
100
+ slots: {
101
+ default: 'Submit Button',
102
+ },
103
+ });
104
+
105
+ const button = wrapper.find('button');
106
+ expect(button.attributes('type')).toBe('submit');
107
+ });
108
+
109
+ it('renders with an icon in the left position', () => {
110
+ const wrapper = mount(MButton, {
111
+ props: {
112
+ iconPosition: 'left',
113
+ },
114
+ slots: {
115
+ default: 'Button with Icon',
116
+ icon: ChevronRight24,
117
+ },
118
+ });
119
+
120
+ const icon = wrapper.findComponent(ChevronRight24);
121
+ expect(icon.exists()).toBe(true);
122
+ });
123
+
124
+ it('renders with an icon in the right position', () => {
125
+ const wrapper = mount(MButton, {
126
+ props: {
127
+ iconPosition: 'right',
128
+ },
129
+ slots: {
130
+ default: 'Button with Icon',
131
+ icon: ChevronRight24,
132
+ },
133
+ });
134
+
135
+ const icon = wrapper.findComponent(ChevronRight24);
136
+ expect(icon.exists()).toBe(true);
137
+ });
138
+
139
+ it('renders with only an icon when iconPosition is "only"', () => {
140
+ const wrapper = mount(MButton, {
141
+ props: {
142
+ iconPosition: 'only',
143
+ },
144
+ slots: {
145
+ default: 'Icon Only Button',
146
+ icon: ChevronRight24,
147
+ },
148
+ });
149
+
150
+ const icon = wrapper.findComponent(ChevronRight24);
151
+ expect(icon.exists()).toBe(true);
152
+
153
+ const label = wrapper.find('.mc-button__label');
154
+ expect(label.exists()).toBe(false);
155
+ });
156
+
157
+ it('renders loader when isLoading prop is true', () => {
158
+ const wrapper = mount(MButton, {
159
+ props: {
160
+ isLoading: true,
161
+ },
162
+ slots: {
163
+ default: 'Loading Button',
164
+ },
165
+ });
166
+
167
+ const loader = wrapper.findComponent(MLoader);
168
+ expect(loader.exists()).toBe(true);
169
+
170
+ const label = wrapper.find('.mc-button__label');
171
+ expect(label.attributes('style')).toContain('visibility: hidden');
172
+ });
173
+
174
+ it('does not render loader when isLoading prop is false', () => {
175
+ const wrapper = mount(MButton, {
176
+ props: {
177
+ isLoading: false,
178
+ },
179
+ slots: {
180
+ default: 'Normal Button',
181
+ },
182
+ });
183
+
184
+ const loader = wrapper.findComponent(MLoader);
185
+ expect(loader.exists()).toBe(false);
186
+
187
+ const label = wrapper.find('.mc-button__label');
188
+ expect(label.exists()).toBe(true);
189
+ expect(label.text()).toBe('Normal Button');
190
+ });
191
+ });
@@ -0,0 +1,59 @@
1
+ import type { Meta, StoryObj } from '@storybook/vue3-vite';
2
+
3
+ import MButton from './MButton.vue';
4
+ import Download24 from '@mozaic-ds/icons-vue/src/components/Download24/Download24.vue';
5
+ import ChevronRight24 from '@mozaic-ds/icons-vue/src/components/ChevronRight24/ChevronRight24.vue';
6
+
7
+ const meta: Meta<typeof MButton> = {
8
+ title: 'Action/Button',
9
+ component: MButton,
10
+ parameters: {
11
+ docs: {
12
+ description: {
13
+ component:
14
+ 'Buttons are key interactive elements used to perform actions and can be used as standalone element, or as part of another component. Their appearance depends on the type of action required from the user and the context in which they are used.',
15
+ },
16
+ },
17
+ },
18
+ args: { default: 'Button Label' },
19
+ render: (args) => ({
20
+ components: { MButton, Download24, ChevronRight24 },
21
+ setup() {
22
+ return { args };
23
+ },
24
+ template: `
25
+ <MButton
26
+ v-bind="args"
27
+ >
28
+ <template v-if="${'icon' in args}" v-slot:icon>${args.icon}</template>
29
+ <template v-if="${'default' in args}" v-slot>${args.default}</template>
30
+ </MButton>
31
+ `,
32
+ }),
33
+ };
34
+ export default meta;
35
+ type Story = StoryObj<typeof MButton>;
36
+
37
+ export const Filled: Story = {};
38
+
39
+ export const Outline: Story = {
40
+ args: { outlined: true },
41
+ };
42
+
43
+ export const Ghost: Story = {
44
+ args: { ghost: true },
45
+ };
46
+
47
+ export const Icon: Story = {
48
+ args: {
49
+ iconPosition: 'left',
50
+ icon: '<ChevronRight24/>',
51
+ },
52
+ };
53
+
54
+ export const Loading: Story = {
55
+ args: {
56
+ isLoading: true,
57
+ ariaLabel: 'Loading button',
58
+ },
59
+ };
@@ -1,169 +1,113 @@
1
1
  <template>
2
- <a v-if="href" :href="href" class="mc-button" :class="setClasses" :aria-label="ariaLabel">
3
- <m-icon
4
- v-if="icon && iconPosition === 'left'"
5
- :id="`mc-button__${iconPosition}-icon-${id}`"
6
- :class="`mc-button__icon mc-button__icon--${iconPosition}`"
7
- :name="icon"
8
- />
9
- <span v-if="label" class="mc-button__label">
10
- {{ label }}
11
- </span>
12
- <m-icon
13
- v-if="icon && iconPosition === 'right'"
14
- :id="`mc-button__${iconPosition}-icon-${id}`"
15
- :class="`mc-button__icon mc-button__icon--${iconPosition}`"
16
- :name="icon"
17
- />
18
- </a>
19
2
  <button
20
- v-else
21
3
  class="mc-button"
4
+ :class="classObject"
22
5
  :disabled="disabled"
23
- :class="setClasses"
24
- :aria-label="ariaLabel"
25
- @click="$emit('click', $event)"
6
+ :type="type"
26
7
  >
27
- <m-icon
28
- v-if="icon && iconPosition === 'left'"
29
- :id="`mc-button__${iconPosition}-icon-${id}`"
30
- :class="`mc-button__icon mc-button__icon--${iconPosition}`"
31
- :name="icon"
32
- />
33
- <span v-if="label" class="mc-button__label">
34
- {{ label }}
8
+ <span
9
+ v-if="$slots.icon && iconPosition == 'left' && !isLoading"
10
+ class="mc-button__icon"
11
+ >
12
+ <slot name="icon" />
13
+ </span>
14
+ <span
15
+ v-if="isLoading"
16
+ class="mc-button__icon"
17
+ :style="{ position: 'absolute' }"
18
+ >
19
+ <MLoader :style="{ color: 'currentColor' }" size="s" />
20
+ </span>
21
+ <span v-if="$slots.icon && iconPosition == 'only'" class="mc-button__icon">
22
+ <slot name="icon" />
23
+ </span>
24
+ <span
25
+ v-else
26
+ class="mc-button__label"
27
+ :style="{ visibility: isLoading ? 'hidden' : 'visible' }"
28
+ >
29
+ <slot>Button Label</slot>
30
+ </span>
31
+ <span
32
+ v-if="$slots.icon && iconPosition == 'right' && !isLoading"
33
+ class="mc-button__icon"
34
+ >
35
+ <slot name="icon" />
35
36
  </span>
36
- <m-icon
37
- v-if="icon && iconPosition === 'right'"
38
- :id="`mc-button__${iconPosition}-icon-${id}`"
39
- :class="`mc-button__icon mc-button__icon--${iconPosition}`"
40
- :name="icon"
41
- />
42
37
  </button>
43
38
  </template>
44
39
 
45
- <script>
46
- import {
47
- responsiveModifiers,
48
- responsiveModifierValidators,
49
- } from '../../utils/mozaicClasses';
50
- import MIcon from '../icon/MIcon.vue';
51
-
52
- export default {
53
- name: 'MButton',
54
-
55
- components: {
56
- MIcon,
57
- },
58
-
59
- props: {
60
- label: {
61
- type: String,
62
- default: null,
63
- },
64
- href: {
65
- type: String,
66
- default: null,
67
- },
68
- theme: {
69
- type: String,
70
- default: null,
71
- },
72
- size: {
73
- type: String,
74
- default: 'm',
75
- validator: (value) =>
76
- responsiveModifierValidators(value, [
77
- 's',
78
- 's@from-m',
79
- 's@from-l',
80
- 's@from-xl',
81
- 's@from-xxl',
82
- 'm',
83
- 'm@from-m',
84
- 'm@from-l',
85
- 'm@from-xl',
86
- 'm@from-xxl',
87
- 'l',
88
- 'l@from-m',
89
- 'l@from-l',
90
- 'l@from-xl',
91
- 'l@from-xxl',
92
- ]),
93
- },
94
- width: {
95
- type: String,
96
- default: null,
97
- validator: (value) =>
98
- responsiveModifierValidators(value, [
99
- 'fit',
100
- 'fit@from-m',
101
- 'fit@from-l',
102
- 'fit@from-xl',
103
- 'fit@from-xxl',
104
- 'full',
105
- 'full@from-m',
106
- 'full@from-l',
107
- 'full@from-xl',
108
- 'full@from-xxl',
109
- ]),
110
- },
111
- disabled: {
112
- type: Boolean,
113
- default: false,
114
- },
115
- icon: {
116
- type: String,
117
- default: null,
118
- },
119
- iconPosition: {
120
- type: String,
121
- default: 'left',
122
- validator: (value) => ['left', 'right'].includes(value),
123
- },
124
- ariaLabel: {
125
- type: String,
126
- default: null,
127
- },
40
+ <script setup lang="ts">
41
+ import { computed, type VNode } from 'vue';
42
+ import MLoader from '../loader/MLoader.vue';
43
+ /**
44
+ * Buttons are used to trigger actions. Their appearance is depending on the type of action required from the user, or the context.
45
+ */
46
+ const props = withDefaults(
47
+ defineProps<{
48
+ /**
49
+ * Defines the visual style of the button.
50
+ */
51
+ appearance?: 'standard' | 'accent' | 'danger' | 'inverse';
52
+ /**
53
+ * Determines the size of the button.
54
+ */
55
+ size?: 's' | 'm' | 'l';
56
+ /**
57
+ * If `true`, disables the button, making it non-interactive.
58
+ */
59
+ disabled?: boolean;
60
+ /**
61
+ * If `true`, applies a "ghost" style to the button, typically a transparent background with a border.
62
+ */
63
+ ghost?: boolean;
64
+ /**
65
+ * If `true`, the button gets an outlined style, usually with just the border and no solid background.
66
+ */
67
+ outlined?: boolean;
68
+ /**
69
+ * Controls the positioning of an icon in the button.
70
+ */
71
+ iconPosition?: 'left' | 'right' | 'only';
72
+ /**
73
+ * Specifies the button's HTML `type` attribute.
74
+ */
75
+ type?: 'button' | 'reset' | 'submit';
76
+ /**
77
+ * If `true`, a loading state is displayed.
78
+ */
79
+ isLoading?: boolean;
80
+ }>(),
81
+ {
82
+ appearance: 'standard',
83
+ size: 'm',
84
+ type: 'button',
128
85
  },
86
+ );
129
87
 
130
- data() {
131
- return {
132
- id: null,
133
- };
134
- },
135
-
136
- computed: {
137
- setClasses() {
138
- const classes = [];
139
-
140
- if (this.theme) {
141
- classes.push(`mc-button--${this.theme}`);
142
- }
143
-
144
- if (this.size) {
145
- responsiveModifiers('mc-button', this.size, classes);
146
- }
147
-
148
- if (this.width) {
149
- responsiveModifiers('mc-button', this.width, classes);
150
- }
88
+ defineSlots<{
89
+ /**
90
+ * The content displayed in the button.
91
+ */
92
+ default: string;
93
+ /**
94
+ * Use this slot to insert an icon for the Button
95
+ */
96
+ icon?: VNode;
97
+ }>();
151
98
 
152
- if (!this.label) {
153
- classes.push('mc-button--square');
154
- }
155
-
156
- return classes;
157
- },
158
- },
159
-
160
- created() {
161
- this.id = this._uid;
162
- },
163
- };
99
+ const classObject = computed(() => {
100
+ return {
101
+ [`mc-button--${props.appearance}`]:
102
+ props.appearance && props.appearance != 'standard',
103
+ [`mc-button--${props.size}`]: props.size && props.size != 'm',
104
+ 'mc-button--ghost': props.ghost,
105
+ 'mc-button--outlined': props.outlined,
106
+ 'mc-button--icon-only': props.iconPosition == 'only',
107
+ };
108
+ });
164
109
  </script>
165
110
 
166
- <style lang="scss">
167
- @import 'settings-tools/_all-settings';
168
- @import 'components/c.button';
111
+ <style lang="scss" scoped>
112
+ @use '@mozaic-ds/styles/components/button';
169
113
  </style>
@@ -0,0 +1,104 @@
1
+ import { mount } from '@vue/test-utils';
2
+ import MCheckbox from './MCheckbox.vue';
3
+ import { describe, it, expect } from 'vitest';
4
+
5
+ describe('MCheckbox', () => {
6
+ it('renders with label', () => {
7
+ const wrapper = mount(MCheckbox, {
8
+ props: {
9
+ id: 'test-checkbox',
10
+ label: 'Accept terms',
11
+ modelValue: false,
12
+ },
13
+ });
14
+
15
+ expect(wrapper.find('label').text()).toBe('Accept terms');
16
+ });
17
+
18
+ it('renders the checkbox as checked when modelValue is true', () => {
19
+ const wrapper = mount(MCheckbox, {
20
+ props: {
21
+ id: 'test-checkbox',
22
+ modelValue: true,
23
+ },
24
+ });
25
+
26
+ const checkbox = wrapper.find('input');
27
+ expect(checkbox.element.checked).toBe(true);
28
+ });
29
+
30
+ it('renders the checkbox as unchecked when modelValue is false', () => {
31
+ const wrapper = mount(MCheckbox, {
32
+ props: {
33
+ id: 'test-checkbox',
34
+ modelValue: false,
35
+ },
36
+ });
37
+
38
+ const checkbox = wrapper.find('input');
39
+ expect(checkbox.element.checked).toBe(false);
40
+ });
41
+
42
+ it('emits update:modelValue when clicked', async () => {
43
+ const wrapper = mount(MCheckbox, {
44
+ props: {
45
+ id: 'test-checkbox',
46
+ modelValue: false,
47
+ },
48
+ });
49
+
50
+ const checkbox = wrapper.find('input');
51
+ await checkbox.setChecked(true);
52
+
53
+ expect(wrapper.emitted()['update:modelValue'][0]).toEqual([true]);
54
+ });
55
+
56
+ it('emits update:modelValue when unchecked', async () => {
57
+ const wrapper = mount(MCheckbox, {
58
+ props: {
59
+ id: 'test-checkbox',
60
+ modelValue: true,
61
+ },
62
+ });
63
+
64
+ const checkbox = wrapper.find('input');
65
+ await checkbox.setChecked(false);
66
+
67
+ expect(wrapper.emitted()['update:modelValue'][0]).toEqual([false]);
68
+ });
69
+
70
+ it('is disabled when the disabled prop is true', () => {
71
+ const wrapper = mount(MCheckbox, {
72
+ props: {
73
+ id: 'test-checkbox',
74
+ disabled: true,
75
+ },
76
+ });
77
+
78
+ const checkbox = wrapper.find('input');
79
+ expect(checkbox.element.disabled).toBe(true);
80
+ });
81
+
82
+ it('sets the indeterminate state correctly', () => {
83
+ const wrapper = mount(MCheckbox, {
84
+ props: {
85
+ id: 'test-checkbox',
86
+ indeterminate: true,
87
+ },
88
+ });
89
+
90
+ const checkbox = wrapper.find('input');
91
+ expect(checkbox.element.indeterminate).toBe(true);
92
+ });
93
+
94
+ it('applies is-invalid class when isInvalid is true', () => {
95
+ const wrapper = mount(MCheckbox, {
96
+ props: {
97
+ id: 'test-checkbox',
98
+ isInvalid: true,
99
+ },
100
+ });
101
+
102
+ expect(wrapper.find('input').classes()).toContain('is-invalid');
103
+ });
104
+ });
@@ -0,0 +1,83 @@
1
+ import type { Meta, StoryObj } from '@storybook/vue3-vite';
2
+ import { action } from 'storybook/actions';
3
+
4
+ import MCheckbox from './MCheckbox.vue';
5
+
6
+ const meta: Meta<typeof MCheckbox> = {
7
+ title: 'Form Elements/Checkbox',
8
+ component: MCheckbox,
9
+ parameters: {
10
+ docs: {
11
+ description: {
12
+ component:
13
+ 'A checkbox is an interactive component used to select or deselect an option, typically within a list of choices. It allows users to make multiple selections independently and is often accompanied by a label for clarity. Checkboxes are commonly used in forms, filters, settings, and preference selections to provide a simple and intuitive way to enable or disable specific options.',
14
+ },
15
+ },
16
+ },
17
+ args: {
18
+ label: 'Label',
19
+ id: 'checkboxId',
20
+ },
21
+ render: (args) => ({
22
+ components: { MCheckbox },
23
+ setup() {
24
+ const handleUpdate = action('update:modelValue');
25
+
26
+ return { args, handleUpdate };
27
+ },
28
+ template: `
29
+ <MCheckbox
30
+ v-bind="args"
31
+ @update:modelValue="handleUpdate"
32
+ />
33
+ `,
34
+ }),
35
+ };
36
+ export default meta;
37
+ type Story = StoryObj<typeof MCheckbox>;
38
+
39
+ export const Default: Story = {};
40
+
41
+ export const Checked: Story = {
42
+ args: {
43
+ modelValue: true,
44
+ id: 'checkedId',
45
+ },
46
+ };
47
+
48
+ export const Indeterminate: Story = {
49
+ args: {
50
+ indeterminate: true,
51
+ id: 'IndeterminateId',
52
+ },
53
+ };
54
+
55
+ export const Disabled: Story = {
56
+ args: {
57
+ disabled: true,
58
+ id: 'disabledId',
59
+ },
60
+ };
61
+
62
+ export const Invalid: Story = {
63
+ args: {
64
+ isInvalid: true,
65
+ id: 'invalidId',
66
+ },
67
+ };
68
+
69
+ export const IndeterminateDisabled: Story = {
70
+ args: {
71
+ indeterminate: true,
72
+ disabled: true,
73
+ id: 'checkedIndeterminateId',
74
+ },
75
+ };
76
+
77
+ export const CheckedDisabled: Story = {
78
+ args: {
79
+ modelValue: true,
80
+ disabled: true,
81
+ id: 'checkedDisabledId',
82
+ },
83
+ };