@mozaic-ds/vue 1.0.0-rc.2 → 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 +106 -154
  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 -31109
  120. package/dist/mozaic-vue.common.js +0 -31099
  121. package/dist/mozaic-vue.common.js.map +0 -1
  122. package/dist/mozaic-vue.umd.js +0 -31110
  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 -381
  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 -198
  194. package/src/components/tabs/index.js +0 -7
  195. package/src/components/tags/MTag.vue +0 -173
  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 -448
  208. package/src/tokens/adeo/android/font_dimens.xml +0 -18
  209. package/src/tokens/adeo/css/_variables.scss +0 -442
  210. package/src/tokens/adeo/css/root.scss +0 -444
  211. package/src/tokens/adeo/ios/StyleDictionaryColor.h +0 -456
  212. package/src/tokens/adeo/ios/StyleDictionaryColor.m +0 -468
  213. package/src/tokens/adeo/ios/StyleDictionaryColor.swift +0 -451
  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 -540
  218. package/src/tokens/adeo/js/tokensObject.js +0 -11641
  219. package/src/tokens/adeo/scss/_tokens.scss +0 -1514
  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
@@ -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
+ };