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

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (224) hide show
  1. package/LICENSE +51 -0
  2. package/README.md +76 -77
  3. package/dist/mozaic-vue.css +1 -1
  4. package/dist/mozaic-vue.d.ts +1718 -0
  5. package/dist/mozaic-vue.js +1971 -0
  6. package/dist/mozaic-vue.js.map +1 -0
  7. package/dist/mozaic-vue.umd.cjs +2 -0
  8. package/dist/mozaic-vue.umd.cjs.map +1 -0
  9. package/env.d.ts +1 -0
  10. package/package.json +78 -51
  11. package/src/components/Contributing.mdx +118 -0
  12. package/src/components/GettingStarted.mdx +45 -0
  13. package/src/components/Introduction.mdx +100 -0
  14. package/src/components/Support.mdx +18 -0
  15. package/src/components/breadcrumb/MBreadcrumb.spec.ts +105 -0
  16. package/src/components/breadcrumb/MBreadcrumb.stories.ts +82 -0
  17. package/src/components/breadcrumb/MBreadcrumb.vue +52 -55
  18. package/src/components/button/MButton.spec.ts +191 -0
  19. package/src/components/button/MButton.stories.ts +59 -0
  20. package/src/components/button/MButton.vue +98 -154
  21. package/src/components/checkbox/MCheckbox.spec.ts +104 -0
  22. package/src/components/checkbox/MCheckbox.stories.ts +83 -0
  23. package/src/components/checkbox/MCheckbox.vue +60 -101
  24. package/src/components/checkboxgroup/MCheckboxGroup.spec.ts +78 -0
  25. package/src/components/checkboxgroup/MCheckboxGroup.stories.ts +61 -0
  26. package/src/components/checkboxgroup/MCheckboxGroup.vue +97 -0
  27. package/src/components/datepicker/MDatepicker.spec.ts +95 -0
  28. package/src/components/datepicker/MDatepicker.stories.ts +75 -0
  29. package/src/components/datepicker/MDatepicker.vue +114 -0
  30. package/src/components/divider/MDivider.spec.ts +57 -0
  31. package/src/components/divider/MDivider.stories.ts +64 -0
  32. package/src/components/divider/MDivider.vue +56 -0
  33. package/src/components/drawer/MDrawer.spec.ts +100 -0
  34. package/src/components/drawer/MDrawer.stories.ts +128 -0
  35. package/src/components/drawer/MDrawer.vue +140 -0
  36. package/src/components/field/MField.spec.ts +166 -0
  37. package/src/components/field/MField.stories.ts +369 -0
  38. package/src/components/field/MField.vue +78 -61
  39. package/src/components/fieldgroup/MFieldGroup.spec.ts +165 -0
  40. package/src/components/fieldgroup/MFieldGroup.stories.ts +416 -0
  41. package/src/components/fieldgroup/MFieldGroup.vue +79 -0
  42. package/src/components/flag/MFlag.spec.ts +46 -0
  43. package/src/components/flag/MFlag.stories.ts +46 -0
  44. package/src/components/flag/MFlag.vue +28 -39
  45. package/src/components/iconbutton/MIconButton.spec.ts +108 -0
  46. package/src/components/iconbutton/MIconButton.stories.ts +74 -0
  47. package/src/components/iconbutton/MIconButton.vue +73 -0
  48. package/src/components/link/MLink.spec.ts +154 -0
  49. package/src/components/link/MLink.stories.ts +89 -0
  50. package/src/components/link/MLink.vue +86 -120
  51. package/src/components/loader/MLoader.spec.ts +104 -0
  52. package/src/components/loader/MLoader.stories.ts +43 -0
  53. package/src/components/loader/MLoader.vue +66 -55
  54. package/src/components/loadingoverlay/MLoadingOverlay.spec.ts +37 -0
  55. package/src/components/loadingoverlay/MLoadingOverlay.stories.ts +40 -0
  56. package/src/components/loadingoverlay/MLoadingOverlay.vue +28 -0
  57. package/src/components/modal/MModal.spec.ts +103 -0
  58. package/src/components/modal/MModal.stories.ts +127 -0
  59. package/src/components/modal/MModal.vue +111 -159
  60. package/src/components/numberbadge/MNumberBadge.spec.ts +56 -0
  61. package/src/components/numberbadge/MNumberBadge.stories.ts +48 -0
  62. package/src/components/numberbadge/MNumberBadge.vue +45 -0
  63. package/src/components/overlay/MOverlay.spec.ts +51 -0
  64. package/src/components/overlay/MOverlay.stories.ts +35 -0
  65. package/src/components/overlay/MOverlay.vue +27 -19
  66. package/src/components/pagination/MPagination.spec.ts +123 -0
  67. package/src/components/pagination/MPagination.stories.ts +83 -0
  68. package/src/components/pagination/MPagination.vue +120 -140
  69. package/src/components/passwordinput/MPasswordInput.spec.ts +104 -0
  70. package/src/components/passwordinput/MPasswordInput.stories.ts +75 -0
  71. package/src/components/passwordinput/MPasswordInput.vue +126 -77
  72. package/src/components/pincode/MPincode.spec.ts +126 -0
  73. package/src/components/pincode/MPincode.stories.ts +68 -0
  74. package/src/components/pincode/MPincode.vue +148 -0
  75. package/src/components/quantityselector/MQuantitySelector.spec.ts +262 -0
  76. package/src/components/quantityselector/MQuantitySelector.stories.ts +89 -0
  77. package/src/components/quantityselector/MQuantitySelector.vue +159 -148
  78. package/src/components/radio/MRadio.spec.ts +104 -0
  79. package/src/components/radio/MRadio.stories.ts +68 -0
  80. package/src/components/radio/MRadio.vue +56 -39
  81. package/src/components/radiogroup/MRadioGroup.spec.ts +54 -0
  82. package/src/components/radiogroup/MRadioGroup.stories.ts +61 -0
  83. package/src/components/radiogroup/MRadioGroup.vue +79 -0
  84. package/src/components/select/MSelect.spec.ts +114 -0
  85. package/src/components/select/MSelect.stories.ts +101 -0
  86. package/src/components/select/MSelect.vue +77 -119
  87. package/src/components/statusbadge/MStatusBadge.stories.ts +45 -0
  88. package/src/components/statusbadge/MStatusBadge.vue +40 -0
  89. package/src/components/statusbadge/MstatusBadge.spec.ts +16 -0
  90. package/src/components/statusdot/MStatusDot.spec.ts +51 -0
  91. package/src/components/statusdot/MStatusDot.stories.ts +48 -0
  92. package/src/components/statusdot/MStatusDot.vue +36 -0
  93. package/src/components/statusnotification/MStatusNotification.spec.ts +103 -0
  94. package/src/components/statusnotification/MStatusNotification.stories.ts +89 -0
  95. package/src/components/statusnotification/MStatusNotification.vue +106 -0
  96. package/src/components/tabs/MTabs.stories.ts +104 -0
  97. package/src/components/tabs/MTabs.vue +113 -0
  98. package/src/components/tabs/Mtabs.spec.ts +149 -0
  99. package/src/components/tag/MTag.spec.ts +107 -0
  100. package/src/components/tag/MTag.stories.ts +75 -0
  101. package/src/components/tag/MTag.vue +151 -0
  102. package/src/components/textarea/MTextArea.spec.ts +112 -0
  103. package/src/components/textarea/MTextArea.stories.ts +67 -0
  104. package/src/components/textarea/MTextArea.vue +81 -43
  105. package/src/components/textinput/MTextInput.spec.ts +121 -0
  106. package/src/components/textinput/MTextInput.stories.ts +107 -0
  107. package/src/components/textinput/MTextInput.vue +127 -47
  108. package/src/components/toggle/MToggle.spec.ts +99 -0
  109. package/src/components/toggle/MToggle.stories.ts +68 -0
  110. package/src/components/toggle/MToggle.vue +63 -103
  111. package/src/components/togglegroup/MToggleGroup.spec.ts +78 -0
  112. package/src/components/togglegroup/MToggleGroup.stories.ts +61 -0
  113. package/src/components/togglegroup/MToggleGroup.vue +97 -0
  114. package/src/components/tooltip/MTooltip.spec.ts +47 -0
  115. package/src/components/tooltip/MTooltip.stories.ts +59 -0
  116. package/src/components/tooltip/MTooltip.vue +49 -32
  117. package/src/components/usingIcons.mdx +35 -0
  118. package/src/components/usingPresets.mdx +128 -0
  119. package/src/main.ts +33 -0
  120. package/dist/demo.html +0 -1
  121. package/dist/mozaic-vue.adeo.css +0 -47
  122. package/dist/mozaic-vue.adeo.umd.js +0 -31341
  123. package/dist/mozaic-vue.common.js +0 -31331
  124. package/dist/mozaic-vue.common.js.map +0 -1
  125. package/dist/mozaic-vue.umd.js +0 -31342
  126. package/dist/mozaic-vue.umd.js.map +0 -1
  127. package/dist/mozaic-vue.umd.min.js +0 -4
  128. package/dist/mozaic-vue.umd.min.js.map +0 -1
  129. package/postinstall.js +0 -3
  130. package/src/components/accordion/MAccordion.vue +0 -128
  131. package/src/components/accordion/index.js +0 -7
  132. package/src/components/autocomplete/MAutocomplete.vue +0 -380
  133. package/src/components/autocomplete/index.js +0 -7
  134. package/src/components/badge/MBadge.vue +0 -43
  135. package/src/components/badge/index.js +0 -7
  136. package/src/components/breadcrumb/index.js +0 -7
  137. package/src/components/button/index.js +0 -7
  138. package/src/components/card/MCard.vue +0 -78
  139. package/src/components/card/index.js +0 -7
  140. package/src/components/checkbox/MCheckboxGroup.vue +0 -163
  141. package/src/components/checkbox/index.js +0 -12
  142. package/src/components/container/MContainer.vue +0 -33
  143. package/src/components/container/index.js +0 -7
  144. package/src/components/datatable/MDataTable.vue +0 -651
  145. package/src/components/datatable/MDataTableHeader.vue +0 -55
  146. package/src/components/datatable/MDataTableTop.vue +0 -35
  147. package/src/components/datatable/helpers.js +0 -132
  148. package/src/components/datatable/index.js +0 -12
  149. package/src/components/dropdown/MDropdown.vue +0 -317
  150. package/src/components/dropdown/index.js +0 -7
  151. package/src/components/field/index.js +0 -7
  152. package/src/components/fileuploader/MFileResult.vue +0 -149
  153. package/src/components/fileuploader/MFileUploader.vue +0 -142
  154. package/src/components/fileuploader/index.js +0 -7
  155. package/src/components/flag/index.js +0 -7
  156. package/src/components/heading/MHeading.vue +0 -75
  157. package/src/components/heading/index.js +0 -7
  158. package/src/components/hero/MHero.vue +0 -93
  159. package/src/components/hero/index.js +0 -7
  160. package/src/components/icon/MIcon.vue +0 -136
  161. package/src/components/icon/index.js +0 -7
  162. package/src/components/index.js +0 -44
  163. package/src/components/layer/MLayer.vue +0 -208
  164. package/src/components/layer/index.js +0 -7
  165. package/src/components/link/index.js +0 -7
  166. package/src/components/listbox/MListBox.vue +0 -146
  167. package/src/components/listbox/MListBoxActions.vue +0 -251
  168. package/src/components/listbox/index.js +0 -12
  169. package/src/components/loader/index.js +0 -7
  170. package/src/components/modal/index.js +0 -7
  171. package/src/components/notification/MNotification.vue +0 -110
  172. package/src/components/notification/index.js +0 -7
  173. package/src/components/optionbutton/MOptionButton.vue +0 -67
  174. package/src/components/optionbutton/index.js +0 -7
  175. package/src/components/optioncard/MOptionCard.vue +0 -132
  176. package/src/components/optioncard/index.js +0 -7
  177. package/src/components/optiongroup/MOptionGroup.vue +0 -18
  178. package/src/components/optiongroup/index.js +0 -7
  179. package/src/components/overlay/MOverlayLoader.vue +0 -43
  180. package/src/components/overlay/index.js +0 -12
  181. package/src/components/pagination/index.js +0 -7
  182. package/src/components/passwordinput/index.js +0 -7
  183. package/src/components/phonenumber/MPhoneNumber.vue +0 -398
  184. package/src/components/phonenumber/index.js +0 -7
  185. package/src/components/progressbar/MProgress.vue +0 -102
  186. package/src/components/progressbar/index.js +0 -7
  187. package/src/components/quantityselector/index.js +0 -7
  188. package/src/components/radio/MRadioGroup.vue +0 -111
  189. package/src/components/radio/index.js +0 -12
  190. package/src/components/ratingstars/MStarsInput.vue +0 -119
  191. package/src/components/ratingstars/MStarsResult.vue +0 -89
  192. package/src/components/ratingstars/index.js +0 -12
  193. package/src/components/select/index.js +0 -7
  194. package/src/components/stepper/MStepper.vue +0 -111
  195. package/src/components/stepper/index.js +0 -7
  196. package/src/components/tabs/MTab.vue +0 -204
  197. package/src/components/tabs/index.js +0 -7
  198. package/src/components/tags/MTag.vue +0 -175
  199. package/src/components/tags/index.js +0 -7
  200. package/src/components/textarea/index.js +0 -7
  201. package/src/components/textinput/MTextInputField.vue +0 -105
  202. package/src/components/textinput/MTextInputIcon.vue +0 -42
  203. package/src/components/textinput/index.js +0 -7
  204. package/src/components/toggle/index.js +0 -7
  205. package/src/components/tooltip/index.js +0 -7
  206. package/src/index.js +0 -63
  207. package/src/shims-tsx.d.ts +0 -13
  208. package/src/shims.vue.d.ts +0 -4
  209. package/src/tokens/adeo/android/colors.xml +0 -452
  210. package/src/tokens/adeo/android/font_dimens.xml +0 -18
  211. package/src/tokens/adeo/css/_variables.scss +0 -446
  212. package/src/tokens/adeo/css/root.scss +0 -448
  213. package/src/tokens/adeo/ios/StyleDictionaryColor.h +0 -460
  214. package/src/tokens/adeo/ios/StyleDictionaryColor.m +0 -472
  215. package/src/tokens/adeo/ios/StyleDictionaryColor.swift +0 -455
  216. package/src/tokens/adeo/ios/StyleDictionarySize.h +0 -69
  217. package/src/tokens/adeo/ios/StyleDictionarySize.m +0 -70
  218. package/src/tokens/adeo/ios/StyleDictionarySize.swift +0 -71
  219. package/src/tokens/adeo/js/tokens.js +0 -544
  220. package/src/tokens/adeo/js/tokensObject.js +0 -11733
  221. package/src/tokens/adeo/scss/_tokens.scss +0 -1522
  222. package/src/utils/mozaicClasses.js +0 -16
  223. package/src/utils/theme.validator.js +0 -19
  224. package/types/index.d.ts +0 -104
@@ -0,0 +1,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>