@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.
- package/LICENSE +51 -0
- package/README.md +76 -77
- package/dist/mozaic-vue.css +1 -1
- package/dist/mozaic-vue.d.ts +1664 -0
- package/dist/mozaic-vue.js +1943 -0
- package/dist/mozaic-vue.js.map +1 -0
- package/dist/mozaic-vue.umd.cjs +2 -0
- package/dist/mozaic-vue.umd.cjs.map +1 -0
- package/env.d.ts +1 -0
- package/package.json +78 -51
- package/src/components/Contributing.mdx +118 -0
- package/src/components/GettingStarted.mdx +45 -0
- package/src/components/Introduction.mdx +100 -0
- package/src/components/Support.mdx +18 -0
- package/src/components/breadcrumb/MBreadcrumb.spec.ts +105 -0
- package/src/components/breadcrumb/MBreadcrumb.stories.ts +82 -0
- package/src/components/breadcrumb/MBreadcrumb.vue +52 -55
- package/src/components/button/MButton.spec.ts +191 -0
- package/src/components/button/MButton.stories.ts +59 -0
- package/src/components/button/MButton.vue +98 -154
- package/src/components/checkbox/MCheckbox.spec.ts +104 -0
- package/src/components/checkbox/MCheckbox.stories.ts +83 -0
- package/src/components/checkbox/MCheckbox.vue +60 -101
- package/src/components/checkboxgroup/MCheckboxGroup.spec.ts +78 -0
- package/src/components/checkboxgroup/MCheckboxGroup.stories.ts +61 -0
- package/src/components/checkboxgroup/MCheckboxGroup.vue +97 -0
- package/src/components/datepicker/MDatepicker.spec.ts +95 -0
- package/src/components/datepicker/MDatepicker.stories.ts +75 -0
- package/src/components/datepicker/MDatepicker.vue +114 -0
- package/src/components/divider/MDivider.spec.ts +57 -0
- package/src/components/divider/MDivider.stories.ts +64 -0
- package/src/components/divider/MDivider.vue +56 -0
- package/src/components/drawer/MDrawer.spec.ts +100 -0
- package/src/components/drawer/MDrawer.stories.ts +128 -0
- package/src/components/drawer/MDrawer.vue +140 -0
- package/src/components/field/MField.spec.ts +166 -0
- package/src/components/field/MField.stories.ts +369 -0
- package/src/components/field/MField.vue +78 -61
- package/src/components/fieldgroup/MFieldGroup.spec.ts +165 -0
- package/src/components/fieldgroup/MFieldGroup.stories.ts +416 -0
- package/src/components/fieldgroup/MFieldGroup.vue +79 -0
- package/src/components/flag/MFlag.spec.ts +46 -0
- package/src/components/flag/MFlag.stories.ts +46 -0
- package/src/components/flag/MFlag.vue +28 -39
- package/src/components/iconbutton/MIconButton.spec.ts +108 -0
- package/src/components/iconbutton/MIconButton.stories.ts +74 -0
- package/src/components/iconbutton/MIconButton.vue +73 -0
- package/src/components/link/MLink.spec.ts +154 -0
- package/src/components/link/MLink.stories.ts +89 -0
- package/src/components/link/MLink.vue +86 -120
- package/src/components/loader/MLoader.spec.ts +104 -0
- package/src/components/loader/MLoader.stories.ts +43 -0
- package/src/components/loader/MLoader.vue +66 -55
- package/src/components/loadingoverlay/MLoadingOverlay.spec.ts +37 -0
- package/src/components/loadingoverlay/MLoadingOverlay.stories.ts +40 -0
- package/src/components/loadingoverlay/MLoadingOverlay.vue +28 -0
- package/src/components/modal/MModal.spec.ts +103 -0
- package/src/components/modal/MModal.stories.ts +127 -0
- package/src/components/modal/MModal.vue +106 -154
- package/src/components/numberbadge/MNumberBadge.spec.ts +56 -0
- package/src/components/numberbadge/MNumberBadge.stories.ts +48 -0
- package/src/components/numberbadge/MNumberBadge.vue +45 -0
- package/src/components/overlay/MOverlay.spec.ts +51 -0
- package/src/components/overlay/MOverlay.stories.ts +35 -0
- package/src/components/overlay/MOverlay.vue +27 -19
- package/src/components/pagination/MPagination.spec.ts +123 -0
- package/src/components/pagination/MPagination.stories.ts +83 -0
- package/src/components/pagination/MPagination.vue +120 -140
- package/src/components/passwordinput/MPasswordInput.spec.ts +104 -0
- package/src/components/passwordinput/MPasswordInput.stories.ts +75 -0
- package/src/components/passwordinput/MPasswordInput.vue +126 -77
- package/src/components/pincode/MPincode.spec.ts +126 -0
- package/src/components/pincode/MPincode.stories.ts +68 -0
- package/src/components/pincode/MPincode.vue +148 -0
- package/src/components/quantityselector/MQuantitySelector.spec.ts +262 -0
- package/src/components/quantityselector/MQuantitySelector.stories.ts +89 -0
- package/src/components/quantityselector/MQuantitySelector.vue +159 -148
- package/src/components/radio/MRadio.spec.ts +104 -0
- package/src/components/radio/MRadio.stories.ts +68 -0
- package/src/components/radio/MRadio.vue +56 -39
- package/src/components/radiogroup/MRadioGroup.spec.ts +54 -0
- package/src/components/radiogroup/MRadioGroup.stories.ts +61 -0
- package/src/components/radiogroup/MRadioGroup.vue +79 -0
- package/src/components/select/MSelect.spec.ts +114 -0
- package/src/components/select/MSelect.stories.ts +101 -0
- package/src/components/select/MSelect.vue +77 -119
- package/src/components/statusbadge/MStatusBadge.stories.ts +45 -0
- package/src/components/statusbadge/MStatusBadge.vue +40 -0
- package/src/components/statusbadge/MstatusBadge.spec.ts +16 -0
- package/src/components/statusdot/MStatusDot.spec.ts +51 -0
- package/src/components/statusdot/MStatusDot.stories.ts +48 -0
- package/src/components/statusdot/MStatusDot.vue +36 -0
- package/src/components/statusnotification/MStatusNotification.spec.ts +103 -0
- package/src/components/statusnotification/MStatusNotification.stories.ts +89 -0
- package/src/components/statusnotification/MStatusNotification.vue +106 -0
- package/src/components/tabs/MTabs.stories.ts +104 -0
- package/src/components/tabs/MTabs.vue +113 -0
- package/src/components/tabs/Mtabs.spec.ts +149 -0
- package/src/components/tag/MTag.spec.ts +107 -0
- package/src/components/tag/MTag.stories.ts +75 -0
- package/src/components/tag/MTag.vue +151 -0
- package/src/components/textarea/MTextArea.spec.ts +112 -0
- package/src/components/textarea/MTextArea.stories.ts +67 -0
- package/src/components/textarea/MTextArea.vue +81 -43
- package/src/components/textinput/MTextInput.spec.ts +121 -0
- package/src/components/textinput/MTextInput.stories.ts +107 -0
- package/src/components/textinput/MTextInput.vue +127 -47
- package/src/components/toggle/MToggle.spec.ts +99 -0
- package/src/components/toggle/MToggle.stories.ts +68 -0
- package/src/components/toggle/MToggle.vue +63 -103
- package/src/components/togglegroup/MToggleGroup.spec.ts +78 -0
- package/src/components/togglegroup/MToggleGroup.stories.ts +61 -0
- package/src/components/togglegroup/MToggleGroup.vue +97 -0
- package/src/components/usingIcons.mdx +35 -0
- package/src/components/usingPresets.mdx +128 -0
- package/src/main.ts +32 -0
- package/dist/demo.html +0 -1
- package/dist/mozaic-vue.adeo.css +0 -47
- package/dist/mozaic-vue.adeo.umd.js +0 -31109
- package/dist/mozaic-vue.common.js +0 -31099
- package/dist/mozaic-vue.common.js.map +0 -1
- package/dist/mozaic-vue.umd.js +0 -31110
- package/dist/mozaic-vue.umd.js.map +0 -1
- package/dist/mozaic-vue.umd.min.js +0 -4
- package/dist/mozaic-vue.umd.min.js.map +0 -1
- package/postinstall.js +0 -3
- package/src/components/accordion/MAccordion.vue +0 -128
- package/src/components/accordion/index.js +0 -7
- package/src/components/autocomplete/MAutocomplete.vue +0 -381
- package/src/components/autocomplete/index.js +0 -7
- package/src/components/badge/MBadge.vue +0 -43
- package/src/components/badge/index.js +0 -7
- package/src/components/breadcrumb/index.js +0 -7
- package/src/components/button/index.js +0 -7
- package/src/components/card/MCard.vue +0 -78
- package/src/components/card/index.js +0 -7
- package/src/components/checkbox/MCheckboxGroup.vue +0 -163
- package/src/components/checkbox/index.js +0 -12
- package/src/components/container/MContainer.vue +0 -33
- package/src/components/container/index.js +0 -7
- package/src/components/datatable/MDataTable.vue +0 -651
- package/src/components/datatable/MDataTableHeader.vue +0 -55
- package/src/components/datatable/MDataTableTop.vue +0 -35
- package/src/components/datatable/helpers.js +0 -132
- package/src/components/datatable/index.js +0 -12
- package/src/components/dropdown/MDropdown.vue +0 -317
- package/src/components/dropdown/index.js +0 -7
- package/src/components/field/index.js +0 -7
- package/src/components/fileuploader/MFileResult.vue +0 -149
- package/src/components/fileuploader/MFileUploader.vue +0 -142
- package/src/components/fileuploader/index.js +0 -7
- package/src/components/flag/index.js +0 -7
- package/src/components/heading/MHeading.vue +0 -75
- package/src/components/heading/index.js +0 -7
- package/src/components/hero/MHero.vue +0 -93
- package/src/components/hero/index.js +0 -7
- package/src/components/icon/MIcon.vue +0 -136
- package/src/components/icon/index.js +0 -7
- package/src/components/index.js +0 -44
- package/src/components/layer/MLayer.vue +0 -208
- package/src/components/layer/index.js +0 -7
- package/src/components/link/index.js +0 -7
- package/src/components/listbox/MListBox.vue +0 -146
- package/src/components/listbox/MListBoxActions.vue +0 -251
- package/src/components/listbox/index.js +0 -12
- package/src/components/loader/index.js +0 -7
- package/src/components/modal/index.js +0 -7
- package/src/components/notification/MNotification.vue +0 -110
- package/src/components/notification/index.js +0 -7
- package/src/components/optionbutton/MOptionButton.vue +0 -67
- package/src/components/optionbutton/index.js +0 -7
- package/src/components/optioncard/MOptionCard.vue +0 -132
- package/src/components/optioncard/index.js +0 -7
- package/src/components/optiongroup/MOptionGroup.vue +0 -18
- package/src/components/optiongroup/index.js +0 -7
- package/src/components/overlay/MOverlayLoader.vue +0 -43
- package/src/components/overlay/index.js +0 -12
- package/src/components/pagination/index.js +0 -7
- package/src/components/passwordinput/index.js +0 -7
- package/src/components/phonenumber/MPhoneNumber.vue +0 -398
- package/src/components/phonenumber/index.js +0 -7
- package/src/components/progressbar/MProgress.vue +0 -102
- package/src/components/progressbar/index.js +0 -7
- package/src/components/quantityselector/index.js +0 -7
- package/src/components/radio/MRadioGroup.vue +0 -111
- package/src/components/radio/index.js +0 -12
- package/src/components/ratingstars/MStarsInput.vue +0 -119
- package/src/components/ratingstars/MStarsResult.vue +0 -89
- package/src/components/ratingstars/index.js +0 -12
- package/src/components/select/index.js +0 -7
- package/src/components/stepper/MStepper.vue +0 -111
- package/src/components/stepper/index.js +0 -7
- package/src/components/tabs/MTab.vue +0 -198
- package/src/components/tabs/index.js +0 -7
- package/src/components/tags/MTag.vue +0 -173
- package/src/components/tags/index.js +0 -7
- package/src/components/textarea/index.js +0 -7
- package/src/components/textinput/MTextInputField.vue +0 -105
- package/src/components/textinput/MTextInputIcon.vue +0 -42
- package/src/components/textinput/index.js +0 -7
- package/src/components/toggle/index.js +0 -7
- package/src/components/tooltip/MTooltip.vue +0 -42
- package/src/components/tooltip/index.js +0 -7
- package/src/index.js +0 -63
- package/src/shims-tsx.d.ts +0 -13
- package/src/shims.vue.d.ts +0 -4
- package/src/tokens/adeo/android/colors.xml +0 -448
- package/src/tokens/adeo/android/font_dimens.xml +0 -18
- package/src/tokens/adeo/css/_variables.scss +0 -442
- package/src/tokens/adeo/css/root.scss +0 -444
- package/src/tokens/adeo/ios/StyleDictionaryColor.h +0 -456
- package/src/tokens/adeo/ios/StyleDictionaryColor.m +0 -468
- package/src/tokens/adeo/ios/StyleDictionaryColor.swift +0 -451
- package/src/tokens/adeo/ios/StyleDictionarySize.h +0 -69
- package/src/tokens/adeo/ios/StyleDictionarySize.m +0 -70
- package/src/tokens/adeo/ios/StyleDictionarySize.swift +0 -71
- package/src/tokens/adeo/js/tokens.js +0 -540
- package/src/tokens/adeo/js/tokensObject.js +0 -11641
- package/src/tokens/adeo/scss/_tokens.scss +0 -1514
- package/src/utils/mozaicClasses.js +0 -16
- package/src/utils/theme.validator.js +0 -19
- 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
|
-
|
|
4
|
-
|
|
3
|
+
<div role="dialog" tabindex="-1" :aria-labelledby="dialogLabel">
|
|
4
|
+
<slot />
|
|
5
|
+
</div>
|
|
5
6
|
</div>
|
|
6
7
|
</template>
|
|
7
8
|
|
|
8
|
-
<script>
|
|
9
|
-
|
|
10
|
-
name: 'MOverlay',
|
|
9
|
+
<script setup lang="ts">
|
|
10
|
+
import type { VNode } from 'vue';
|
|
11
11
|
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
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
|
-
@
|
|
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
|
-
<
|
|
3
|
-
<
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
:
|
|
8
|
-
|
|
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
|
-
<
|
|
14
|
-
</
|
|
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="!
|
|
17
|
-
<
|
|
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
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
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
|
-
<
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
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
|
-
<
|
|
44
|
-
</
|
|
45
|
-
|
|
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
|
|
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
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
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
|
-
|
|
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
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
},
|
|
104
|
+
watch(currentValue, (newVal) => {
|
|
105
|
+
if (newVal !== props.modelValue) {
|
|
106
|
+
emit('update:modelValue', newVal);
|
|
107
|
+
}
|
|
108
|
+
});
|
|
97
109
|
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
110
|
+
const currentIndex = computed(() =>
|
|
111
|
+
props.options.findIndex((opt) => opt.value === currentValue.value),
|
|
112
|
+
);
|
|
101
113
|
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
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
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
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
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
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
|
-
@
|
|
161
|
-
@import 'components/_c.pagination';
|
|
141
|
+
@use '@mozaic-ds/styles/components/pagination';
|
|
162
142
|
</style>
|