@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
@@ -1,166 +1,177 @@
1
1
  <template>
2
- <div class="mc-quantity-selector" :class="cssFieldElementClass">
3
- <m-button
4
- class="mc-quantity-selector__button-left"
5
- theme="bordered"
6
- icon="ControlLess32"
7
- icon-position="left"
8
- :aria-label="decrementAriaLabel"
9
- :aria-controls="id"
10
- :disabled="disabled ? disabled : currentValue === valuemin"
11
- :size="small ? 's' : null"
12
- tabindex="-1"
13
- type="button"
14
- @click="decrement()"
15
- />
16
-
17
- <MTextInputField
2
+ <div class="mc-quantity-selector" :class="classObject">
3
+ <input
18
4
  :id="id"
19
- ref="input"
20
- :value="currentValue"
5
+ v-model="currentValue"
6
+ class="mc-quantity-selector__control"
21
7
  type="number"
22
- class="mc-quantity-selector__input"
23
- name="quantity-selector-input"
24
- :aria-label="inputAriaLabel"
25
- :aria-valuenow="currentValue"
26
- :aria-valuemin="valuemin"
27
- :aria-valuemax="valuemax"
28
- :placeholder="placeholder"
29
- :size="small ? 's' : null"
8
+ :name="name"
30
9
  :disabled="disabled"
31
- role="spinbutton"
32
- @input="handle"
33
- @focus="onFocus"
34
- @keypress="integerOnly && formatValue($event)"
35
- v-on="$listeners"
10
+ :min="min"
11
+ :max="max"
12
+ :step="step"
13
+ :readonly="readonly"
14
+ :aria-invalid="isInvalid"
15
+ :aria-valuemin="min"
16
+ :aria-valuemax="max"
17
+ :aria-valuenow="currentValue"
18
+ v-bind="$attrs"
19
+ @change="onChange(Number(($event.target as HTMLInputElement).value))"
36
20
  />
37
-
38
- <m-button
39
- class="mc-quantity-selector__button-right"
40
- theme="bordered"
41
- icon="ControlMore32"
42
- icon-position="right"
43
- :aria-label="incrementAriaLabel"
21
+ <button
22
+ v-if="!readonly"
23
+ type="button"
44
24
  :aria-controls="id"
45
- :disabled="disabled ? disabled : currentValue === valuemax"
46
- :size="small ? 's' : null"
25
+ class="mc-quantity-selector__button mc-quantity-selector__button--increase"
47
26
  tabindex="-1"
27
+ :disabled="disabled || currentValue === max"
28
+ @click="increment"
29
+ >
30
+ <span class="mc-quantity-selector__icon">
31
+ <More24 />
32
+ </span>
33
+ <span class="mc-quantity-selector__label">{{ incrementlabel }}</span>
34
+ </button>
35
+ <button
36
+ v-if="!readonly"
48
37
  type="button"
49
- @click="increment()"
50
- />
38
+ :aria-controls="id"
39
+ class="mc-quantity-selector__button mc-quantity-selector__button--decrease"
40
+ tabindex="-1"
41
+ :disabled="disabled || currentValue === min"
42
+ @click="decrement"
43
+ >
44
+ <span class="mc-quantity-selector__icon">
45
+ <Less24 />
46
+ </span>
47
+ <span class="mc-quantity-selector__label">{{ decrementLabel }}</span>
48
+ </button>
51
49
  </div>
52
50
  </template>
53
51
 
54
- <script>
55
- import MButton from '../button/MButton.vue';
56
- import MTextInputField from '../textinput/MTextInputField.vue';
57
-
58
- export default {
59
- name: 'MQuantitySelector',
52
+ <script setup lang="ts">
53
+ import { computed, ref, watch } from 'vue';
54
+ import More24 from '@mozaic-ds/icons-vue/src/components/More24/More24.vue';
55
+ import Less24 from '@mozaic-ds/icons-vue/src/components/Less24/Less24.vue';
60
56
 
61
- components: {
62
- MButton,
63
- MTextInputField,
64
- },
65
- inject: {
66
- cssFieldElementClass: {
67
- default: '',
68
- },
69
- },
70
- props: {
71
- id: {
72
- type: String,
73
- default: 'qty-selector',
74
- },
75
- value: {
76
- type: Number,
77
- default: 0,
78
- },
79
- inputAriaLabel: {
80
- type: String,
81
- default: 'Quantity Selector',
82
- },
83
- decrementAriaLabel: {
84
- type: String,
85
- default: 'Decrement',
86
- },
87
- incrementAriaLabel: {
88
- type: String,
89
- default: 'Increment',
90
- },
91
- valuemin: {
92
- type: Number,
93
- default: 1,
94
- },
95
- valuemax: {
96
- type: Number,
97
- default: 100,
98
- },
99
- placeholder: {
100
- type: String,
101
- default: null,
102
- },
103
- small: {
104
- type: Boolean,
105
- default: false,
106
- },
107
- integerOnly: {
108
- type: Boolean,
109
- default: false,
110
- },
111
- disabled: {
112
- type: Boolean,
113
- default: false,
114
- },
57
+ /**
58
+ * The quantity selector is a form element used to enter or select a number. This type of input is best used when the user needs to choose the quantity of a selected item, like a product before adding to cart for example.
59
+ */
60
+ const props = withDefaults(
61
+ defineProps<{
62
+ /**
63
+ * A unique identifier for the quantity selector element, used to associate the label with the form element.
64
+ */
65
+ id: string;
66
+ /**
67
+ * The name attribute for the quantity selector element, typically used for form submission.
68
+ */
69
+ name?: string;
70
+ /**
71
+ * The current value of the quantity selector field.
72
+ */
73
+ modelValue?: number;
74
+ /**
75
+ * If `true`, applies an invalid state to the quantity selector.
76
+ */
77
+ isInvalid?: boolean;
78
+ /**
79
+ * If `true`, disables the quantity selector, making it non-interactive.
80
+ */
81
+ disabled?: boolean;
82
+ /**
83
+ * Determines the size of the quantity selector
84
+ */
85
+ size?: 's' | 'm';
86
+ /**
87
+ * Minimum acceptable value for the quantity selector.
88
+ */
89
+ min?: number;
90
+ /**
91
+ * Maximum acceptable value for the quantity selector.
92
+ */
93
+ max?: number;
94
+ /**
95
+ * Determines how much the value will change per click when the quantity is increased or decreased.
96
+ */
97
+ step?: number;
98
+ /**
99
+ * If `true`, the quantity selector is read-only (cannot be edited).
100
+ */
101
+ readonly?: boolean;
102
+ /**
103
+ * The label text for the increment button.
104
+ */
105
+ incrementlabel?: string;
106
+ /**
107
+ * The label text for the decrement button.
108
+ */
109
+ decrementLabel?: string;
110
+ }>(),
111
+ {
112
+ modelValue: 1,
113
+ min: 1,
114
+ max: 100,
115
+ step: 1,
116
+ size: 'm',
117
+ name: 'quantity-selector-input',
118
+ incrementlabel: 'Increment',
119
+ decrementLabel: 'Decrement',
115
120
  },
121
+ );
116
122
 
117
- data() {
118
- return {
119
- currentValue: this.value || this.valuemin,
120
- };
121
- },
122
- watch: {
123
- value() {
124
- this.currentValue = this.value || this.valuemin;
125
- },
126
- },
127
- methods: {
128
- handle(value) {
129
- this.currentValue = value;
130
- if (this.currentValue > this.valuemax) {
131
- this.currentValue = this.valuemax;
132
- }
133
- if (this.currentValue < this.valuemin) {
134
- this.currentValue = this.valuemin;
135
- }
136
- this.$emit('input', this.currentValue);
137
- },
138
- increment() {
139
- if (this.currentValue < this.valuemax) {
140
- this.currentValue++;
141
- this.$emit('increment', this.currentValue);
142
- }
143
- },
144
- decrement() {
145
- if (this.currentValue > this.valuemin) {
146
- this.currentValue--;
147
- this.$emit('decrement', this.currentValue);
148
- }
149
- },
150
- formatValue(e) {
151
- const INTEGER_ONLY_REGEX = /[0-9/]+/;
152
- if (!INTEGER_ONLY_REGEX.test(e.key)) {
153
- e.preventDefault();
154
- }
155
- },
156
- onFocus(e) {
157
- e.target.select();
158
- },
159
- },
123
+ const currentValue = ref(props.modelValue);
124
+
125
+ watch(currentValue, (newVal) => {
126
+ if (newVal !== props.modelValue) {
127
+ emit('update:modelValue', newVal);
128
+ }
129
+ });
130
+
131
+ const classObject = computed(() => {
132
+ return {
133
+ [`mc-quantity-selector--${props.size}`]: props.size && props.size != 'm',
134
+ 'is-invalid': props.isInvalid,
135
+ };
136
+ });
137
+
138
+ const increment = () => {
139
+ if (currentValue.value + props.step <= props.max) {
140
+ currentValue.value += props.step;
141
+ } else {
142
+ currentValue.value = props.max;
143
+ }
160
144
  };
145
+
146
+ const decrement = () => {
147
+ if (currentValue.value - props.step > props.min) {
148
+ currentValue.value -= props.step;
149
+ } else {
150
+ currentValue.value = props.min;
151
+ }
152
+ };
153
+
154
+ const onChange = (value: number) => {
155
+ currentValue.value = value;
156
+
157
+ if (currentValue.value > props.max) {
158
+ currentValue.value = props.max;
159
+ }
160
+ if (currentValue.value <= props.min) {
161
+ currentValue.value = props.min;
162
+ }
163
+
164
+ emit('update:modelValue', currentValue.value);
165
+ };
166
+
167
+ const emit = defineEmits<{
168
+ /**
169
+ * Emits when the quantity selector value changes, updating the `modelValue` prop.
170
+ */
171
+ (on: 'update:modelValue', value: number): void;
172
+ }>();
161
173
  </script>
162
174
 
163
- <style lang="scss">
164
- @import 'settings-tools/_all-settings';
165
- @import 'components/_c.quantity-selector';
175
+ <style lang="scss" scoped>
176
+ @use '@mozaic-ds/styles/components/quantity-selector';
166
177
  </style>
@@ -0,0 +1,104 @@
1
+ import { mount } from '@vue/test-utils';
2
+ import MRadio from './MRadio.vue';
3
+ import { describe, it, expect } from 'vitest';
4
+
5
+ describe('MRadio component', () => {
6
+ it('renders a radio button with a label', () => {
7
+ const wrapper = mount(MRadio, {
8
+ props: {
9
+ id: 'radio1',
10
+ label: 'Option 1',
11
+ },
12
+ });
13
+
14
+ const radioInput = wrapper.find('input[type="radio"]');
15
+ expect(radioInput.exists()).toBe(true);
16
+
17
+ const label = wrapper.find('label');
18
+ expect(label.text()).toBe('Option 1');
19
+ });
20
+
21
+ it('binds modelValue correctly when checked', async () => {
22
+ const wrapper = mount(MRadio, {
23
+ props: {
24
+ id: 'radio1',
25
+ modelValue: false,
26
+ },
27
+ });
28
+
29
+ const radioInput = wrapper.find('input');
30
+
31
+ expect(radioInput.element.checked).toBe(false);
32
+
33
+ await radioInput.setChecked();
34
+
35
+ expect(wrapper.emitted()['update:modelValue'][0]).toEqual([true]);
36
+ });
37
+
38
+ it('sets the disabled attribute when disabled prop is true', () => {
39
+ const wrapper = mount(MRadio, {
40
+ props: {
41
+ id: 'radio1',
42
+ disabled: true,
43
+ },
44
+ });
45
+
46
+ const radioInput = wrapper.find('input[type="radio"]');
47
+ expect(radioInput.attributes('disabled')).toBeDefined();
48
+ });
49
+
50
+ it('applies the is-invalid class when isInvalid prop is true', () => {
51
+ const wrapper = mount(MRadio, {
52
+ props: {
53
+ id: 'radio1',
54
+ isInvalid: true,
55
+ },
56
+ });
57
+
58
+ const radioInput = wrapper.find('input[type="radio"]');
59
+ expect(radioInput.classes()).toContain('is-invalid');
60
+ });
61
+
62
+ it('does not apply the is-invalid class when isInvalid prop is false', () => {
63
+ const wrapper = mount(MRadio, {
64
+ props: {
65
+ id: 'radio1',
66
+ isInvalid: false,
67
+ },
68
+ });
69
+
70
+ const radioInput = wrapper.find('input[type="radio"]');
71
+ expect(radioInput.classes()).not.toContain('is-invalid');
72
+ });
73
+
74
+ it('emits update:modelValue on change', async () => {
75
+ const wrapper = mount(MRadio, {
76
+ props: {
77
+ id: 'radio1',
78
+ modelValue: false,
79
+ },
80
+ });
81
+
82
+ const radioInput = wrapper.find('input[type="radio"]');
83
+
84
+ await radioInput.setChecked();
85
+
86
+ expect(wrapper.emitted()['update:modelValue'][0]).toEqual([true]);
87
+ });
88
+
89
+ it('does not emit change when disabled', async () => {
90
+ const wrapper = mount(MRadio, {
91
+ props: {
92
+ id: 'radio1',
93
+ disabled: true,
94
+ modelValue: false,
95
+ },
96
+ });
97
+
98
+ const radioInput = wrapper.find('input[type="radio"]');
99
+
100
+ await radioInput.setChecked();
101
+
102
+ expect(wrapper.emitted()['update:modelValue']).toBeUndefined();
103
+ });
104
+ });
@@ -0,0 +1,68 @@
1
+ import type { Meta, StoryObj } from '@storybook/vue3-vite';
2
+ import { action } from 'storybook/actions';
3
+
4
+ import MRadio from './MRadio.vue';
5
+
6
+ const meta: Meta<typeof MRadio> = {
7
+ title: 'Form Elements/Radio',
8
+ component: MRadio,
9
+ parameters: {
10
+ docs: {
11
+ description: {
12
+ component:
13
+ 'A radio button is a selection control that allows users to choose a single option from a list of mutually exclusive choices. Unlike checkboxes, only one option can be selected at a time within the same group. Radio Buttons are commonly used in forms, surveys, and settings where a single choice must be made.',
14
+ },
15
+ },
16
+ },
17
+ args: {
18
+ id: 'RadioId',
19
+ label: 'Label',
20
+ },
21
+ render: (args) => ({
22
+ components: { MRadio },
23
+ setup() {
24
+ const handleUpdate = action('update:modelValue');
25
+
26
+ return { args, handleUpdate };
27
+ },
28
+ template: `
29
+ <MRadio
30
+ v-bind="args"
31
+ @update:modelValue="handleUpdate"
32
+ />
33
+ `,
34
+ }),
35
+ };
36
+ export default meta;
37
+ type Story = StoryObj<typeof MRadio>;
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 Invalid: Story = {
49
+ args: {
50
+ isInvalid: true,
51
+ id: 'invalidId',
52
+ },
53
+ };
54
+
55
+ export const Disabled: Story = {
56
+ args: {
57
+ disabled: true,
58
+ id: 'disabledId',
59
+ },
60
+ };
61
+
62
+ export const CheckedDisabled: Story = {
63
+ args: {
64
+ modelValue: true,
65
+ disabled: true,
66
+ id: 'checkedDisabledId',
67
+ },
68
+ };
@@ -1,55 +1,72 @@
1
1
  <template>
2
- <div class="mc-radio" :class="rootClass">
2
+ <div class="mc-radio">
3
3
  <input
4
4
  :id="id"
5
5
  type="radio"
6
6
  class="mc-radio__input"
7
- :class="{ 'is-invalid': isInvalid }"
7
+ :class="classObject"
8
+ :name="name"
9
+ :checked="modelValue"
10
+ :disabled="disabled"
11
+ :aria-invalid="isInvalid"
8
12
  v-bind="$attrs"
9
- :checked="checked"
10
- @change="$emit('change', $event.target.checked)"
13
+ @change="
14
+ emit('update:modelValue', ($event.target as HTMLInputElement).checked)
15
+ "
11
16
  />
12
- <label :for="id" class="mc-radio__label">{{ label }}</label>
17
+ <label v-if="label" :for="id" class="mc-radio__label">
18
+ {{ label }}
19
+ </label>
13
20
  </div>
14
21
  </template>
15
22
 
16
- <script>
17
- export default {
18
- name: 'MRadio',
23
+ <script setup lang="ts">
24
+ import { computed } from 'vue';
19
25
 
20
- inheritAttrs: false,
26
+ /**
27
+ * A radio button is used to offer a unique choice to your user in a form. Unlike checkboxes, it can not be used alone.
28
+ */
29
+ const props = defineProps<{
30
+ /**
31
+ * A unique identifier for the radio, used to associate the label with the form element.
32
+ */
33
+ id: string;
34
+ /**
35
+ * The name attribute for the radio element, typically used for form submission.
36
+ */
37
+ name?: string;
38
+ /**
39
+ * The text label displayed next to the radio.
40
+ */
41
+ label?: string;
42
+ /**
43
+ * The radio's checked state, bound via v-model.
44
+ */
45
+ modelValue?: boolean;
46
+ /**
47
+ * If `true`, applies an invalid state to the radio.
48
+ */
49
+ isInvalid?: boolean;
50
+ /**
51
+ * If `true`, disables the radio, making it non-interactive.
52
+ */
53
+ disabled?: boolean;
54
+ }>();
21
55
 
22
- model: {
23
- prop: 'checked',
24
- event: 'change',
25
- },
56
+ const classObject = computed(() => {
57
+ return {
58
+ 'is-invalid': props.isInvalid,
59
+ };
60
+ });
26
61
 
27
- props: {
28
- id: {
29
- type: String,
30
- required: true,
31
- },
32
- label: {
33
- type: String,
34
- required: true,
35
- },
36
- checked: {
37
- type: Boolean,
38
- default: false,
39
- },
40
- isInvalid: {
41
- type: Boolean,
42
- default: false,
43
- },
44
- rootClass: {
45
- type: String,
46
- default: '',
47
- },
48
- },
49
- };
62
+ const emit = defineEmits<{
63
+ /**
64
+ * Emits when the radio value changes, updating the modelValue prop.
65
+ */
66
+ (on: 'update:modelValue', value: boolean): void;
67
+ }>();
50
68
  </script>
51
69
 
52
- <style lang="scss">
53
- @import 'settings-tools/_all-settings';
54
- @import 'components/_c.radio';
70
+ <style lang="scss" scoped>
71
+ @use '@mozaic-ds/styles/components/radio';
55
72
  </style>
@@ -0,0 +1,54 @@
1
+ import { mount } from '@vue/test-utils';
2
+ import { describe, it, expect } from 'vitest';
3
+ import MRadioGroup from './MRadioGroup.vue';
4
+ import MField from '@/components/field/MField.vue';
5
+ import MRadio from '@/components/radio/MRadio.vue';
6
+
7
+ describe('MRadioGroup component', () => {
8
+ const options = [
9
+ { id: 'option-1', label: 'Option 1', value: 'option1' },
10
+ { id: 'option-2', label: 'Option 2', value: 'option2', disabled: true },
11
+ { id: 'option-3', label: 'Option 3', value: 'option3' },
12
+ ];
13
+
14
+ // eslint-disable-next-line
15
+ const mountComponent = (props: Record<string, any>) => {
16
+ return mount(MRadioGroup, {
17
+ global: {
18
+ components: { MField, MRadio },
19
+ },
20
+ props: {
21
+ name: 'radio-group',
22
+ options,
23
+ ...props,
24
+ },
25
+ });
26
+ };
27
+
28
+ it('renders the radio buttons correctly', () => {
29
+ const wrapper = mountComponent({});
30
+ expect(wrapper.findAll('input[type="radio"]')).toHaveLength(3);
31
+ });
32
+
33
+ it('binds the modelValue prop correctly', async () => {
34
+ const wrapper = mountComponent({ modelValue: 'option1' });
35
+
36
+ const radio1 = wrapper.find('#option-1');
37
+ const radio2 = wrapper.find('#option-2');
38
+ const radio3 = wrapper.find('#option-3');
39
+
40
+ expect(radio1.element.checked).toBe(true);
41
+ expect(radio2.element.checked).toBe(false);
42
+ expect(radio3.element.checked).toBe(false);
43
+ });
44
+
45
+ it('does not allow disabled options to be selected', async () => {
46
+ const wrapper = mountComponent({ modelValue: 'option1' });
47
+ const radio2 = wrapper.find('#option-2');
48
+
49
+ expect(radio2.attributes('disabled')).toBeDefined();
50
+ await radio2.setChecked();
51
+
52
+ expect(wrapper.emitted()['update:modelValue']).toBeUndefined();
53
+ });
54
+ });