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

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (222) hide show
  1. package/LICENSE +51 -0
  2. package/README.md +76 -77
  3. package/dist/mozaic-vue.css +1 -1
  4. package/dist/mozaic-vue.d.ts +1664 -0
  5. package/dist/mozaic-vue.js +1943 -0
  6. package/dist/mozaic-vue.js.map +1 -0
  7. package/dist/mozaic-vue.umd.cjs +2 -0
  8. package/dist/mozaic-vue.umd.cjs.map +1 -0
  9. package/env.d.ts +1 -0
  10. package/package.json +78 -51
  11. package/src/components/Contributing.mdx +118 -0
  12. package/src/components/GettingStarted.mdx +45 -0
  13. package/src/components/Introduction.mdx +100 -0
  14. package/src/components/Support.mdx +18 -0
  15. package/src/components/breadcrumb/MBreadcrumb.spec.ts +105 -0
  16. package/src/components/breadcrumb/MBreadcrumb.stories.ts +82 -0
  17. package/src/components/breadcrumb/MBreadcrumb.vue +52 -55
  18. package/src/components/button/MButton.spec.ts +191 -0
  19. package/src/components/button/MButton.stories.ts +59 -0
  20. package/src/components/button/MButton.vue +98 -154
  21. package/src/components/checkbox/MCheckbox.spec.ts +104 -0
  22. package/src/components/checkbox/MCheckbox.stories.ts +83 -0
  23. package/src/components/checkbox/MCheckbox.vue +60 -101
  24. package/src/components/checkboxgroup/MCheckboxGroup.spec.ts +78 -0
  25. package/src/components/checkboxgroup/MCheckboxGroup.stories.ts +61 -0
  26. package/src/components/checkboxgroup/MCheckboxGroup.vue +97 -0
  27. package/src/components/datepicker/MDatepicker.spec.ts +95 -0
  28. package/src/components/datepicker/MDatepicker.stories.ts +75 -0
  29. package/src/components/datepicker/MDatepicker.vue +114 -0
  30. package/src/components/divider/MDivider.spec.ts +57 -0
  31. package/src/components/divider/MDivider.stories.ts +64 -0
  32. package/src/components/divider/MDivider.vue +56 -0
  33. package/src/components/drawer/MDrawer.spec.ts +100 -0
  34. package/src/components/drawer/MDrawer.stories.ts +128 -0
  35. package/src/components/drawer/MDrawer.vue +140 -0
  36. package/src/components/field/MField.spec.ts +166 -0
  37. package/src/components/field/MField.stories.ts +369 -0
  38. package/src/components/field/MField.vue +78 -61
  39. package/src/components/fieldgroup/MFieldGroup.spec.ts +165 -0
  40. package/src/components/fieldgroup/MFieldGroup.stories.ts +416 -0
  41. package/src/components/fieldgroup/MFieldGroup.vue +79 -0
  42. package/src/components/flag/MFlag.spec.ts +46 -0
  43. package/src/components/flag/MFlag.stories.ts +46 -0
  44. package/src/components/flag/MFlag.vue +28 -39
  45. package/src/components/iconbutton/MIconButton.spec.ts +108 -0
  46. package/src/components/iconbutton/MIconButton.stories.ts +74 -0
  47. package/src/components/iconbutton/MIconButton.vue +73 -0
  48. package/src/components/link/MLink.spec.ts +154 -0
  49. package/src/components/link/MLink.stories.ts +89 -0
  50. package/src/components/link/MLink.vue +86 -120
  51. package/src/components/loader/MLoader.spec.ts +104 -0
  52. package/src/components/loader/MLoader.stories.ts +43 -0
  53. package/src/components/loader/MLoader.vue +66 -55
  54. package/src/components/loadingoverlay/MLoadingOverlay.spec.ts +37 -0
  55. package/src/components/loadingoverlay/MLoadingOverlay.stories.ts +40 -0
  56. package/src/components/loadingoverlay/MLoadingOverlay.vue +28 -0
  57. package/src/components/modal/MModal.spec.ts +103 -0
  58. package/src/components/modal/MModal.stories.ts +127 -0
  59. package/src/components/modal/MModal.vue +111 -159
  60. package/src/components/numberbadge/MNumberBadge.spec.ts +56 -0
  61. package/src/components/numberbadge/MNumberBadge.stories.ts +48 -0
  62. package/src/components/numberbadge/MNumberBadge.vue +45 -0
  63. package/src/components/overlay/MOverlay.spec.ts +51 -0
  64. package/src/components/overlay/MOverlay.stories.ts +35 -0
  65. package/src/components/overlay/MOverlay.vue +27 -19
  66. package/src/components/pagination/MPagination.spec.ts +123 -0
  67. package/src/components/pagination/MPagination.stories.ts +83 -0
  68. package/src/components/pagination/MPagination.vue +120 -140
  69. package/src/components/passwordinput/MPasswordInput.spec.ts +104 -0
  70. package/src/components/passwordinput/MPasswordInput.stories.ts +75 -0
  71. package/src/components/passwordinput/MPasswordInput.vue +126 -77
  72. package/src/components/pincode/MPincode.spec.ts +126 -0
  73. package/src/components/pincode/MPincode.stories.ts +68 -0
  74. package/src/components/pincode/MPincode.vue +148 -0
  75. package/src/components/quantityselector/MQuantitySelector.spec.ts +262 -0
  76. package/src/components/quantityselector/MQuantitySelector.stories.ts +89 -0
  77. package/src/components/quantityselector/MQuantitySelector.vue +159 -148
  78. package/src/components/radio/MRadio.spec.ts +104 -0
  79. package/src/components/radio/MRadio.stories.ts +68 -0
  80. package/src/components/radio/MRadio.vue +56 -39
  81. package/src/components/radiogroup/MRadioGroup.spec.ts +54 -0
  82. package/src/components/radiogroup/MRadioGroup.stories.ts +61 -0
  83. package/src/components/radiogroup/MRadioGroup.vue +79 -0
  84. package/src/components/select/MSelect.spec.ts +114 -0
  85. package/src/components/select/MSelect.stories.ts +101 -0
  86. package/src/components/select/MSelect.vue +77 -119
  87. package/src/components/statusbadge/MStatusBadge.stories.ts +45 -0
  88. package/src/components/statusbadge/MStatusBadge.vue +40 -0
  89. package/src/components/statusbadge/MstatusBadge.spec.ts +16 -0
  90. package/src/components/statusdot/MStatusDot.spec.ts +51 -0
  91. package/src/components/statusdot/MStatusDot.stories.ts +48 -0
  92. package/src/components/statusdot/MStatusDot.vue +36 -0
  93. package/src/components/statusnotification/MStatusNotification.spec.ts +103 -0
  94. package/src/components/statusnotification/MStatusNotification.stories.ts +89 -0
  95. package/src/components/statusnotification/MStatusNotification.vue +106 -0
  96. package/src/components/tabs/MTabs.stories.ts +104 -0
  97. package/src/components/tabs/MTabs.vue +113 -0
  98. package/src/components/tabs/Mtabs.spec.ts +149 -0
  99. package/src/components/tag/MTag.spec.ts +107 -0
  100. package/src/components/tag/MTag.stories.ts +75 -0
  101. package/src/components/tag/MTag.vue +151 -0
  102. package/src/components/textarea/MTextArea.spec.ts +112 -0
  103. package/src/components/textarea/MTextArea.stories.ts +67 -0
  104. package/src/components/textarea/MTextArea.vue +81 -43
  105. package/src/components/textinput/MTextInput.spec.ts +121 -0
  106. package/src/components/textinput/MTextInput.stories.ts +107 -0
  107. package/src/components/textinput/MTextInput.vue +127 -47
  108. package/src/components/toggle/MToggle.spec.ts +99 -0
  109. package/src/components/toggle/MToggle.stories.ts +68 -0
  110. package/src/components/toggle/MToggle.vue +63 -103
  111. package/src/components/togglegroup/MToggleGroup.spec.ts +78 -0
  112. package/src/components/togglegroup/MToggleGroup.stories.ts +61 -0
  113. package/src/components/togglegroup/MToggleGroup.vue +97 -0
  114. package/src/components/usingIcons.mdx +35 -0
  115. package/src/components/usingPresets.mdx +128 -0
  116. package/src/main.ts +32 -0
  117. package/dist/demo.html +0 -1
  118. package/dist/mozaic-vue.adeo.css +0 -47
  119. package/dist/mozaic-vue.adeo.umd.js +0 -31341
  120. package/dist/mozaic-vue.common.js +0 -31331
  121. package/dist/mozaic-vue.common.js.map +0 -1
  122. package/dist/mozaic-vue.umd.js +0 -31342
  123. package/dist/mozaic-vue.umd.js.map +0 -1
  124. package/dist/mozaic-vue.umd.min.js +0 -4
  125. package/dist/mozaic-vue.umd.min.js.map +0 -1
  126. package/postinstall.js +0 -3
  127. package/src/components/accordion/MAccordion.vue +0 -128
  128. package/src/components/accordion/index.js +0 -7
  129. package/src/components/autocomplete/MAutocomplete.vue +0 -380
  130. package/src/components/autocomplete/index.js +0 -7
  131. package/src/components/badge/MBadge.vue +0 -43
  132. package/src/components/badge/index.js +0 -7
  133. package/src/components/breadcrumb/index.js +0 -7
  134. package/src/components/button/index.js +0 -7
  135. package/src/components/card/MCard.vue +0 -78
  136. package/src/components/card/index.js +0 -7
  137. package/src/components/checkbox/MCheckboxGroup.vue +0 -163
  138. package/src/components/checkbox/index.js +0 -12
  139. package/src/components/container/MContainer.vue +0 -33
  140. package/src/components/container/index.js +0 -7
  141. package/src/components/datatable/MDataTable.vue +0 -651
  142. package/src/components/datatable/MDataTableHeader.vue +0 -55
  143. package/src/components/datatable/MDataTableTop.vue +0 -35
  144. package/src/components/datatable/helpers.js +0 -132
  145. package/src/components/datatable/index.js +0 -12
  146. package/src/components/dropdown/MDropdown.vue +0 -317
  147. package/src/components/dropdown/index.js +0 -7
  148. package/src/components/field/index.js +0 -7
  149. package/src/components/fileuploader/MFileResult.vue +0 -149
  150. package/src/components/fileuploader/MFileUploader.vue +0 -142
  151. package/src/components/fileuploader/index.js +0 -7
  152. package/src/components/flag/index.js +0 -7
  153. package/src/components/heading/MHeading.vue +0 -75
  154. package/src/components/heading/index.js +0 -7
  155. package/src/components/hero/MHero.vue +0 -93
  156. package/src/components/hero/index.js +0 -7
  157. package/src/components/icon/MIcon.vue +0 -136
  158. package/src/components/icon/index.js +0 -7
  159. package/src/components/index.js +0 -44
  160. package/src/components/layer/MLayer.vue +0 -208
  161. package/src/components/layer/index.js +0 -7
  162. package/src/components/link/index.js +0 -7
  163. package/src/components/listbox/MListBox.vue +0 -146
  164. package/src/components/listbox/MListBoxActions.vue +0 -251
  165. package/src/components/listbox/index.js +0 -12
  166. package/src/components/loader/index.js +0 -7
  167. package/src/components/modal/index.js +0 -7
  168. package/src/components/notification/MNotification.vue +0 -110
  169. package/src/components/notification/index.js +0 -7
  170. package/src/components/optionbutton/MOptionButton.vue +0 -67
  171. package/src/components/optionbutton/index.js +0 -7
  172. package/src/components/optioncard/MOptionCard.vue +0 -132
  173. package/src/components/optioncard/index.js +0 -7
  174. package/src/components/optiongroup/MOptionGroup.vue +0 -18
  175. package/src/components/optiongroup/index.js +0 -7
  176. package/src/components/overlay/MOverlayLoader.vue +0 -43
  177. package/src/components/overlay/index.js +0 -12
  178. package/src/components/pagination/index.js +0 -7
  179. package/src/components/passwordinput/index.js +0 -7
  180. package/src/components/phonenumber/MPhoneNumber.vue +0 -398
  181. package/src/components/phonenumber/index.js +0 -7
  182. package/src/components/progressbar/MProgress.vue +0 -102
  183. package/src/components/progressbar/index.js +0 -7
  184. package/src/components/quantityselector/index.js +0 -7
  185. package/src/components/radio/MRadioGroup.vue +0 -111
  186. package/src/components/radio/index.js +0 -12
  187. package/src/components/ratingstars/MStarsInput.vue +0 -119
  188. package/src/components/ratingstars/MStarsResult.vue +0 -89
  189. package/src/components/ratingstars/index.js +0 -12
  190. package/src/components/select/index.js +0 -7
  191. package/src/components/stepper/MStepper.vue +0 -111
  192. package/src/components/stepper/index.js +0 -7
  193. package/src/components/tabs/MTab.vue +0 -204
  194. package/src/components/tabs/index.js +0 -7
  195. package/src/components/tags/MTag.vue +0 -175
  196. package/src/components/tags/index.js +0 -7
  197. package/src/components/textarea/index.js +0 -7
  198. package/src/components/textinput/MTextInputField.vue +0 -105
  199. package/src/components/textinput/MTextInputIcon.vue +0 -42
  200. package/src/components/textinput/index.js +0 -7
  201. package/src/components/toggle/index.js +0 -7
  202. package/src/components/tooltip/MTooltip.vue +0 -42
  203. package/src/components/tooltip/index.js +0 -7
  204. package/src/index.js +0 -63
  205. package/src/shims-tsx.d.ts +0 -13
  206. package/src/shims.vue.d.ts +0 -4
  207. package/src/tokens/adeo/android/colors.xml +0 -452
  208. package/src/tokens/adeo/android/font_dimens.xml +0 -18
  209. package/src/tokens/adeo/css/_variables.scss +0 -446
  210. package/src/tokens/adeo/css/root.scss +0 -448
  211. package/src/tokens/adeo/ios/StyleDictionaryColor.h +0 -460
  212. package/src/tokens/adeo/ios/StyleDictionaryColor.m +0 -472
  213. package/src/tokens/adeo/ios/StyleDictionaryColor.swift +0 -455
  214. package/src/tokens/adeo/ios/StyleDictionarySize.h +0 -69
  215. package/src/tokens/adeo/ios/StyleDictionarySize.m +0 -70
  216. package/src/tokens/adeo/ios/StyleDictionarySize.swift +0 -71
  217. package/src/tokens/adeo/js/tokens.js +0 -544
  218. package/src/tokens/adeo/js/tokensObject.js +0 -11733
  219. package/src/tokens/adeo/scss/_tokens.scss +0 -1522
  220. package/src/utils/mozaicClasses.js +0 -16
  221. package/src/utils/theme.validator.js +0 -19
  222. package/types/index.d.ts +0 -104
@@ -0,0 +1,51 @@
1
+ import { mount } from '@vue/test-utils';
2
+ import { describe, it, expect } from 'vitest';
3
+ import MOverlay from './MOverlay.vue';
4
+
5
+ describe('MOverlay.vue', () => {
6
+ it('should be visible when isVisible is true', () => {
7
+ const wrapper = mount(MOverlay, {
8
+ props: {
9
+ isVisible: true,
10
+ },
11
+ });
12
+
13
+ expect(wrapper.classes()).toContain('is-visible');
14
+ });
15
+
16
+ it('should not be visible when isVisible is false', () => {
17
+ const wrapper = mount(MOverlay, {
18
+ props: {
19
+ isVisible: false,
20
+ },
21
+ });
22
+
23
+ expect(wrapper.classes()).not.toContain('is-visible');
24
+ });
25
+
26
+ it('should apply the dialog label for accessibility', () => {
27
+ const label = 'Overlay Dialog';
28
+ const wrapper = mount(MOverlay, {
29
+ props: {
30
+ dialogLabel: label,
31
+ },
32
+ });
33
+
34
+ expect(
35
+ wrapper.find('div[role="dialog"]').attributes('aria-labelledby'),
36
+ ).toBe(label);
37
+ });
38
+
39
+ it('should render the content inside the overlay', () => {
40
+ const wrapper = mount(MOverlay, {
41
+ props: {
42
+ isVisible: true,
43
+ },
44
+ slots: {
45
+ default: 'Overlay Content',
46
+ },
47
+ });
48
+
49
+ expect(wrapper.text()).toContain('Overlay Content');
50
+ });
51
+ });
@@ -0,0 +1,35 @@
1
+ import type { Meta, StoryObj } from '@storybook/vue3-vite';
2
+ import MOverlay from './MOverlay.vue';
3
+
4
+ const meta: Meta<typeof MOverlay> = {
5
+ title: 'Overlay/Overlay',
6
+ component: MOverlay,
7
+ parameters: {
8
+ layout: 'fullscreen',
9
+ docs: {
10
+ story: { height: '400px' },
11
+ description: {
12
+ component:
13
+ 'An overlay component is a UI element that appears above the main content to display additional information or interactions, often blocking or dimming the background.',
14
+ },
15
+ },
16
+ },
17
+ args: {
18
+ isVisible: true,
19
+ },
20
+ render: (args) => ({
21
+ components: { MOverlay },
22
+ setup() {
23
+ return { args };
24
+ },
25
+ template: `
26
+ <MOverlay v-bind="args">
27
+ <template v-if="${'default' in args}" v-slot>${args.default}</template>
28
+ </MOverlay>
29
+ `,
30
+ }),
31
+ };
32
+ export default meta;
33
+ type Story = StoryObj<typeof MOverlay>;
34
+
35
+ export const Default: Story = {};
@@ -1,28 +1,36 @@
1
1
  <template>
2
2
  <div class="mc-overlay" :class="{ 'is-visible': isVisible }">
3
- <!-- @slot Use this slot to insert a centered content inside the overlay -->
4
- <slot />
3
+ <div role="dialog" tabindex="-1" :aria-labelledby="dialogLabel">
4
+ <slot />
5
+ </div>
5
6
  </div>
6
7
  </template>
7
8
 
8
- <script>
9
- export default {
10
- name: 'MOverlay',
9
+ <script setup lang="ts">
10
+ import type { VNode } from 'vue';
11
11
 
12
- props: {
13
- /**
14
- * Define if the overlay is visible or not
15
- * @values false, true
16
- */
17
- isVisible: {
18
- type: Boolean,
19
- default: false,
20
- },
21
- },
22
- };
12
+ /**
13
+ * An overlay component is a UI element that appears above the main content to display additional information or interactions, often blocking or dimming the background.
14
+ */
15
+ defineProps<{
16
+ /**
17
+ * Controls the visibility of the overlay.
18
+ */
19
+ isVisible?: boolean;
20
+ /**
21
+ * Accessible label for the overlay dialog.
22
+ */
23
+ dialogLabel?: string;
24
+ }>();
25
+
26
+ defineSlots<{
27
+ /**
28
+ * Use this slot to insert a centered content inside the overlay
29
+ */
30
+ default?: VNode;
31
+ }>();
23
32
  </script>
24
33
 
25
- <style lang="scss">
26
- @import 'settings-tools/all-settings';
27
- @import 'components/c.overlay';
34
+ <style lang="scss" scoped>
35
+ @use '@mozaic-ds/styles/components/overlay';
28
36
  </style>
@@ -0,0 +1,123 @@
1
+ import { describe, it, expect } from 'vitest';
2
+ import { mount } from '@vue/test-utils';
3
+ import MPagination from './MPagination.vue';
4
+
5
+ const options = [
6
+ { value: 1, text: 'Page 1' },
7
+ { value: 2, text: 'Page 2' },
8
+ { value: 3, text: 'Page 3' },
9
+ ];
10
+
11
+ describe('MPagination component', () => {
12
+ it('renders correctly with select when compact is false', () => {
13
+ const wrapper = mount(MPagination, {
14
+ props: {
15
+ id: 'test-pagination',
16
+ modelValue: 2,
17
+ options,
18
+ compact: false,
19
+ selectLabel: 'Select page',
20
+ },
21
+ });
22
+
23
+ // Check buttons rendered as MButton (not MIconButton)
24
+ expect(wrapper.findAllComponents({ name: 'MButton' }).length).toBe(2);
25
+ expect(wrapper.findAllComponents({ name: 'MIconButton' }).length).toBe(0);
26
+
27
+ // Check select is present
28
+ expect(wrapper.find('select').exists()).toBe(true);
29
+ });
30
+
31
+ it('renders icon buttons only when compact is true', () => {
32
+ const wrapper = mount(MPagination, {
33
+ props: {
34
+ id: 'test-pagination',
35
+ modelValue: 2,
36
+ options,
37
+ compact: true,
38
+ },
39
+ });
40
+
41
+ // MIconButton rendered for previous and next
42
+ expect(wrapper.findAllComponents({ name: 'MIconButton' }).length).toBe(2);
43
+ // No MButton when compact
44
+ expect(wrapper.findAllComponents({ name: 'MButton' }).length).toBe(0);
45
+
46
+ // No select rendered
47
+ expect(wrapper.find('select').exists()).toBe(false);
48
+ });
49
+
50
+ it('disables previous button if on first page', async () => {
51
+ const wrapper = mount(MPagination, {
52
+ props: {
53
+ id: 'test-pagination',
54
+ modelValue: 1,
55
+ options,
56
+ },
57
+ });
58
+ const prevButton = wrapper.findAllComponents({ name: 'MButton' })[0];
59
+ expect(prevButton.attributes('disabled')).toBeDefined();
60
+ });
61
+
62
+ it('disables next button if on last page', async () => {
63
+ const wrapper = mount(MPagination, {
64
+ props: {
65
+ id: 'test-pagination',
66
+ modelValue: 3,
67
+ options,
68
+ },
69
+ });
70
+ const buttons = wrapper.findAllComponents({ name: 'MButton' });
71
+ const nextButton = buttons[buttons.length - 1];
72
+ expect(nextButton.attributes('disabled')).toBeDefined();
73
+ });
74
+
75
+ it('clicking previous button updates the modelValue', async () => {
76
+ const wrapper = mount(MPagination, {
77
+ props: {
78
+ id: 'test-pagination',
79
+ modelValue: 2,
80
+ options,
81
+ },
82
+ });
83
+
84
+ await wrapper.findAllComponents({ name: 'MButton' })[0].trigger('click');
85
+ // Should emit update:modelValue with value 1 (previous page)
86
+ expect(wrapper.emitted('update:modelValue')).toBeTruthy();
87
+ expect(wrapper.emitted('update:modelValue')![0]).toEqual([1]);
88
+ });
89
+
90
+ it('clicking next button updates the modelValue', async () => {
91
+ const wrapper = mount(MPagination, {
92
+ props: {
93
+ id: 'test-pagination',
94
+ modelValue: 2,
95
+ options,
96
+ },
97
+ });
98
+
99
+ const buttons = wrapper.findAllComponents({ name: 'MButton' });
100
+ await buttons[buttons.length - 1].trigger('click');
101
+
102
+ expect(wrapper.emitted('update:modelValue')).toBeTruthy();
103
+ expect(wrapper.emitted('update:modelValue')![0]).toEqual([3]);
104
+ });
105
+
106
+ it('emits update:modelValue when select value changes', async () => {
107
+ const wrapper = mount(MPagination, {
108
+ props: {
109
+ id: 'test-pagination',
110
+ modelValue: 1,
111
+ options,
112
+ compact: false,
113
+ selectLabel: 'Select page',
114
+ },
115
+ });
116
+
117
+ const select = wrapper.find('select');
118
+ await select.setValue('3');
119
+
120
+ expect(wrapper.emitted('update:modelValue')).toBeTruthy();
121
+ expect(wrapper.emitted('update:modelValue')![0]).toEqual([3]);
122
+ });
123
+ });
@@ -0,0 +1,83 @@
1
+ import type { Meta, StoryObj } from '@storybook/vue3-vite';
2
+ import { action } from 'storybook/actions';
3
+
4
+ import MPagination from './MPagination.vue';
5
+
6
+ const meta: Meta<typeof MPagination> = {
7
+ title: 'Navigation/Pagination',
8
+ component: MPagination,
9
+ parameters: {
10
+ docs: {
11
+ description: {
12
+ component:
13
+ 'Pagination is a navigation component that allows users to browse through large sets of content by dividing it into discrete pages. It typically includes previous and next buttons, numeric page selectors, or dropdowns to jump between pages efficiently. Pagination improves usability and performance in content-heavy applications such as tables, search results, and articles by preventing long scrolls and reducing page load times.',
14
+ },
15
+ },
16
+ },
17
+ args: {
18
+ id: 'paginationId',
19
+ modelValue: 10,
20
+ options: [
21
+ {
22
+ text: 'Page 1 of 99',
23
+ value: 1,
24
+ },
25
+ {
26
+ text: 'Page 2 of 99',
27
+ value: 2,
28
+ },
29
+ {
30
+ text: 'Page 3 of 99',
31
+ value: 3,
32
+ },
33
+ {
34
+ text: 'Page 10 of 99',
35
+ value: 10,
36
+ },
37
+ {
38
+ text: 'Page 99 of 99',
39
+ value: 99,
40
+ },
41
+ ],
42
+ selectLabel: 'Select page',
43
+ },
44
+ render: (args) => ({
45
+ components: { MPagination },
46
+ setup() {
47
+ const handleUpdate = action('update:modelValue');
48
+
49
+ return { args, handleUpdate };
50
+ },
51
+ template: `
52
+ <MPagination
53
+ v-bind="args"
54
+ @update:modelValue="handleUpdate"
55
+ />
56
+ `,
57
+ }),
58
+ };
59
+ export default meta;
60
+ type Story = StoryObj<typeof MPagination>;
61
+
62
+ export const Default: Story = {};
63
+
64
+ export const First: Story = {
65
+ args: {
66
+ id: 'firstId',
67
+ modelValue: 1,
68
+ },
69
+ };
70
+
71
+ export const Last: Story = {
72
+ args: {
73
+ id: 'lastId',
74
+ modelValue: 99,
75
+ },
76
+ };
77
+
78
+ export const Compact: Story = {
79
+ args: {
80
+ id: 'compactId',
81
+ compact: true,
82
+ },
83
+ };
@@ -1,162 +1,142 @@
1
1
  <template>
2
- <div class="mc-pagination" :class="{ 'mc-pagination--light': light }">
3
- <component
4
- :is="hrefPrev ? 'a' : 'button'"
5
- v-if="value > 1"
6
- :type="hrefPrev ? null : 'button'"
7
- :href="hrefPrev"
8
- class="mc-pagination__button"
9
- aria-label="Previous Page"
10
- :disabled="disabled"
11
- @click="previousPage(currentPage - 1)"
2
+ <nav class="mc-pagination" role="navigation" aria-label="pagination">
3
+ <MButton
4
+ v-if="!compact"
5
+ icon-position="only"
6
+ aria-label="Previous page"
7
+ :disabled="isFirstPage"
8
+ @click="previous"
12
9
  >
13
- <m-icon name="ArrowArrowLeft24" class="mc-pagination__button-icon" />
14
- </component>
10
+ <template #icon><ChevronLeft24 /></template>
11
+ </MButton>
12
+ <MIconButton
13
+ v-else
14
+ outlined
15
+ aria-label="Previous page"
16
+ :disabled="isFirstPage"
17
+ @click="previous"
18
+ >
19
+ <template #icon><ChevronLeft24 /></template>
20
+ </MIconButton>
15
21
 
16
- <div v-if="!light" class="mc-pagination__field">
17
- <m-select
18
- :id="`selectpagination${selectId}`"
19
- v-model="currentPage"
20
- :options="formattedOptions"
21
- :disabled="disabled"
22
+ <div v-if="!compact" class="mc-pagination__field">
23
+ <MSelect
22
24
  class="mc-pagination__select"
23
- @change="changePage(Number($event))"
24
- >
25
- <template #text="{ option }">
26
- <slot name="text" :option="{ ...option, length }">
27
- {{ option.text }}
28
- </slot>
29
- </template>
30
- </m-select>
25
+ :id="id"
26
+ v-model="currentValue"
27
+ :options="options"
28
+ @update:model-value="emit('update:modelValue', Number($event))"
29
+ :aria-label="selectLabel"
30
+ ></MSelect>
31
31
  </div>
32
32
 
33
- <component
34
- :is="hrefNext ? 'a' : 'button'"
35
- v-if="value < length"
36
- :type="hrefNext ? null : 'button'"
37
- :href="hrefNext"
38
- class="mc-pagination__button"
39
- aria-label="Next Page"
40
- :disabled="disabled"
41
- @click="nextPage(currentPage + 1)"
33
+ <span v-if="compact" class="mc-pagination__label" aria-current="page">
34
+ {{ options.find((option) => option.value === currentValue)?.text }}
35
+ </span>
36
+
37
+ <MButton
38
+ v-if="!compact"
39
+ icon-position="only"
40
+ aria-label="Next page"
41
+ :disabled="isLastPage"
42
+ @click="next"
42
43
  >
43
- <m-icon name="ArrowArrowRight24" class="mc-pagination__button-icon" />
44
- </component>
45
- </div>
44
+ <template #icon><ChevronRight24 /></template>
45
+ </MButton>
46
+ <MIconButton
47
+ v-else
48
+ outlined
49
+ aria-label="Next page"
50
+ :disabled="isLastPage"
51
+ @click="next"
52
+ >
53
+ <template #icon><ChevronRight24 /></template>
54
+ </MIconButton>
55
+ </nav>
46
56
  </template>
47
57
 
48
- <script>
49
- import MIcon from '../icon/MIcon.vue';
58
+ <script setup lang="ts">
59
+ import { computed, ref, watch } from 'vue';
60
+ import MButton from '../button/MButton.vue';
50
61
  import MSelect from '../select/MSelect.vue';
62
+ import ChevronLeft24 from '@mozaic-ds/icons-vue/src/components/ChevronLeft24/ChevronLeft24.vue';
63
+ import ChevronRight24 from '@mozaic-ds/icons-vue/src/components/ChevronRight24/ChevronRight24.vue';
64
+ import MIconButton from '../iconbutton/MIconButton.vue';
65
+ /**
66
+ * Pagination is a navigation component that allows users to browse through large sets of content by dividing it into discrete pages. It typically includes previous and next buttons, numeric page selectors, or dropdowns to jump between pages efficiently. Pagination improves usability and performance in content-heavy applications such as tables, search results, and articles by preventing long scrolls and reducing page load times.
67
+ */
68
+ const props = defineProps<{
69
+ /**
70
+ * A unique identifier for the pagination.
71
+ */
72
+ id: string;
73
+ /**
74
+ * The current value of the selected page.
75
+ */
76
+ modelValue: number;
77
+ /**
78
+ * If `true`, display a compact version without the select.
79
+ */
80
+ compact?: boolean;
81
+ /**
82
+ * Define the available choices for the pagination select element.
83
+ */
84
+ options: Array<{
85
+ id?: string;
86
+ text: string;
87
+ value: number;
88
+ }>;
89
+ /**
90
+ * Accessible label for the select of the pagination.
91
+ */
92
+ selectLabel?: string;
93
+ }>();
51
94
 
52
- export default {
53
- name: 'MPagination',
54
-
55
- components: {
56
- MIcon,
57
- MSelect,
58
- },
95
+ const emit = defineEmits<{
96
+ /**
97
+ * Emits when the pagination value changes, updating the modelValue prop.
98
+ */
99
+ (on: 'update:modelValue', value: number): void;
100
+ }>();
59
101
 
60
- props: {
61
- length: {
62
- type: Number,
63
- default: 0,
64
- },
65
- value: {
66
- type: Number,
67
- default: 0,
68
- },
69
- pageLabel: {
70
- type: String,
71
- default: 'sur',
72
- },
73
- light: {
74
- type: Boolean,
75
- default: false,
76
- },
77
- hrefPrev: {
78
- type: String,
79
- default: null,
80
- },
81
- hrefNext: {
82
- type: String,
83
- default: null,
84
- },
85
- disabled: {
86
- type: Boolean,
87
- default: false,
88
- },
89
- },
102
+ const currentValue = ref(props.modelValue);
90
103
 
91
- data: function () {
92
- return {
93
- selectId: null,
94
- currentPage: Number(this.value),
95
- };
96
- },
104
+ watch(currentValue, (newVal) => {
105
+ if (newVal !== props.modelValue) {
106
+ emit('update:modelValue', newVal);
107
+ }
108
+ });
97
109
 
98
- computed: {
99
- formattedOptions() {
100
- const pageOptions = [];
110
+ const currentIndex = computed(() =>
111
+ props.options.findIndex((opt) => opt.value === currentValue.value),
112
+ );
101
113
 
102
- for (let i = 1; i <= this.length; i++) {
103
- pageOptions.push({
104
- text: `${i} ${this.pageLabel} ${this.length}`,
105
- value: i,
106
- index: i,
107
- });
108
- }
114
+ const isFirstPage = computed(() => currentIndex.value === 0);
115
+ const isLastPage = computed(
116
+ () => currentIndex.value === props.options.length - 1,
117
+ );
109
118
 
110
- if (this.currentPage > this.length) {
111
- this.changePage(this.length);
112
- }
113
-
114
- return pageOptions;
115
- },
116
- },
117
-
118
- watch: {
119
- value: {
120
- handler(newPage) {
121
- this.currentPage = newPage;
122
- },
123
- },
124
- },
125
-
126
- created() {
127
- this.selectId = this._uid;
128
- },
119
+ const previous = () => {
120
+ const currentIndex = props.options.findIndex(
121
+ (opt) => opt.value === currentValue.value,
122
+ );
123
+ if (currentIndex > 0) {
124
+ currentValue.value = props.options[currentIndex - 1].value;
125
+ emit('update:modelValue', props.options[currentIndex - 1].value);
126
+ }
127
+ };
129
128
 
130
- methods: {
131
- hasPreviousPageListener() {
132
- return this.$listeners && this.$listeners['on-previous-page'];
133
- },
134
- hasNextPageListener() {
135
- return this.$listeners && this.$listeners['on-next-page'];
136
- },
137
- changePage(newPage) {
138
- this.$emit('on-update-page', newPage);
139
- this.$emit('update:value',newPage);
140
- },
141
- previousPage(newPage) {
142
- if (this.hasPreviousPageListener()) {
143
- this.$emit('on-previous-page', newPage);
144
- } else {
145
- this.changePage(newPage);
146
- }
147
- },
148
- nextPage(newPage) {
149
- if (this.hasNextPageListener()) {
150
- this.$emit('on-next-page', newPage);
151
- } else {
152
- this.changePage(newPage);
153
- }
154
- },
155
- },
129
+ const next = () => {
130
+ const currentIndex = props.options.findIndex(
131
+ (opt) => opt.value === currentValue.value,
132
+ );
133
+ if (currentIndex < props.options.length - 1) {
134
+ currentValue.value = props.options[currentIndex + 1].value;
135
+ emit('update:modelValue', props.options[currentIndex + 1].value);
136
+ }
156
137
  };
157
138
  </script>
158
139
 
159
140
  <style lang="scss" scoped>
160
- @import 'settings-tools/_all-settings';
161
- @import 'components/_c.pagination';
141
+ @use '@mozaic-ds/styles/components/pagination';
162
142
  </style>