@mozaic-ds/vue 2.18.0 → 2.19.1
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/dist/mozaic-vue.css +1 -1
- package/dist/mozaic-vue.d.ts +961 -2085
- package/dist/mozaic-vue.js +1253 -1143
- package/dist/mozaic-vue.js.map +1 -1
- package/dist/mozaic-vue.umd.cjs +6 -6
- package/dist/mozaic-vue.umd.cjs.map +1 -1
- package/package.json +8 -6
- package/src/components/BrandPresets.mdx +20 -2
- package/src/components/accordionlist/MAccordionList.figma.ts +16 -16
- package/src/components/accordionlist/MAccordionList.stories.ts +1 -1
- package/src/components/accordionlistitem/MAccordionListItem.figma.ts +9 -5
- package/src/components/accordionlistitem/MAccordionListItem.vue +4 -1
- package/src/components/accordionlistitem/README.md +2 -0
- package/src/components/actionbottombar/MActionBottomBar.figma.ts +7 -7
- package/src/components/actionlistbox/MActionListbox.figma.ts +11 -11
- package/src/components/actionlistbox/MActionListbox.spec.ts +113 -0
- package/src/components/actionlistbox/MActionListbox.vue +63 -5
- package/src/components/avatar/MAvatar.figma.ts +5 -5
- package/src/components/breadcrumb/MBreadcrumb.figma.ts +7 -7
- package/src/components/breadcrumb/MBreadcrumb.vue +1 -1
- package/src/components/builtinmenu/MBuiltInMenu.figma.ts +8 -5
- package/src/components/builtinmenu/MBuiltInMenu.spec.ts +3 -1
- package/src/components/button/MButton.figma.ts +21 -6
- package/src/components/button/MButton.spec.ts +26 -0
- package/src/components/button/MButton.vue +2 -0
- package/src/components/callout/MCallout.figma.ts +7 -7
- package/src/components/callout/MCallout.stories.ts +0 -3
- package/src/components/callout/MCallout.vue +4 -3
- package/src/components/callout/README.md +2 -2
- package/src/components/carousel/MCarousel.figma.ts +10 -10
- package/src/components/carousel/MCarousel.spec.ts +26 -2
- package/src/components/carousel/MCarousel.vue +10 -4
- package/src/components/checkbox/MCheckbox.figma.ts +10 -10
- package/src/components/checkboxgroup/MCheckboxGroup.figma.ts +7 -7
- package/src/components/checklistmenu/MCheckListMenu.figma.ts +8 -8
- package/src/components/circularprogressbar/MCircularProgressbar.figma.ts +7 -3
- package/src/components/combobox/MCombobox.figma.ts +10 -10
- package/src/components/combobox/MCombobox.vue +7 -0
- package/src/components/container/MContainer.figma.ts +5 -5
- package/src/components/datatable/DataTable.stories.ts +33 -7
- package/src/components/datatable/DataTableCells.stories.ts +2 -2
- package/src/components/datatable/DataTableEmpty.stories.ts +2 -2
- package/src/components/datatable/DataTableExpandable.stories.ts +2 -2
- package/src/components/datatable/DataTableNested.stories.ts +1 -1
- package/src/components/datatable/DataTableSelectable.stories.ts +2 -3
- package/src/components/datatable/DataTableSortable.stories.ts +1 -1
- package/src/components/datepicker/MDatepicker.figma.ts +3 -3
- package/src/components/divider/MDivider.figma.ts +3 -3
- package/src/components/drawer/MDrawer.figma.ts +13 -13
- package/src/components/drawer/MDrawer.spec.ts +102 -3
- package/src/components/drawer/MDrawer.vue +73 -14
- package/src/components/field/MField.figma.ts +9 -5
- package/src/components/field/MField.vue +1 -0
- package/src/components/fileuploader/MFileUploader.figma.ts +3 -3
- package/src/components/fileuploader/MFileUploader.vue +2 -2
- package/src/components/fileuploaderitem/MFileUploaderItem.figma.ts +7 -3
- package/src/components/fileuploaderitem/MFileUploaderItem.vue +2 -7
- package/src/components/flag/MFlag.figma.ts +3 -3
- package/src/components/iconbutton/MIconButton.figma.ts +16 -16
- package/src/components/iconbutton/MIconButton.spec.ts +15 -0
- package/src/components/iconbutton/MIconButton.vue +1 -0
- package/src/components/kpiitem/MKpiItem.figma.ts +8 -3
- package/src/components/kpiitem/MKpiItem.spec.ts +12 -0
- package/src/components/kpiitem/MKpiItem.vue +7 -1
- package/src/components/linearprogressbarbuffer/MLinearProgressbarBuffer.figma.ts +6 -3
- package/src/components/linearprogressbarpercentage/MLinearProgressbarPercentage.figma.ts +5 -3
- package/src/components/link/MLink.figma.ts +5 -5
- package/src/components/loader/MLoader.figma.ts +3 -3
- package/src/components/loadingoverlay/MLoadingOverlay.figma.ts +3 -3
- package/src/components/modal/MModal.figma.ts +12 -12
- package/src/components/modal/MModal.spec.ts +115 -3
- package/src/components/modal/MModal.vue +91 -11
- package/src/components/modal/README.md +1 -1
- package/src/components/navigationindicator/MNavigationIndicator.figma.ts +7 -3
- package/src/components/numberbadge/MNumberBadge.figma.ts +7 -3
- package/src/components/optionListbox/MOptionListbox.figma.ts +10 -10
- package/src/components/overlay/MOverlay.figma.ts +5 -5
- package/src/components/overlay/MOverlay.spec.ts +1 -1
- package/src/components/overlay/MOverlay.vue +1 -1
- package/src/components/pageheader/MPageHeader.figma.ts +3 -3
- package/src/components/pagination/MPagination.figma.ts +10 -10
- package/src/components/passwordinput/MPasswordInput.figma.ts +9 -9
- package/src/components/phonenumber/MPhoneNumber.figma.ts +9 -9
- package/src/components/phonenumber/MPhoneNumber.spec.ts +6 -2
- package/src/components/phonenumber/MPhoneNumber.vue +21 -15
- package/src/components/pincode/MPincode.figma.ts +9 -9
- package/src/components/popover/MPopover.figma.ts +15 -15
- package/src/components/quantityselector/MQuantitySelector.figma.ts +12 -12
- package/src/components/radio/MRadio.figma.ts +9 -9
- package/src/components/radiogroup/MRadioGroup.figma.ts +7 -7
- package/src/components/segmentedcontrol/MSegmentedControl.figma.ts +8 -8
- package/src/components/select/MSelect.figma.ts +11 -11
- package/src/components/sidebar/MSidebar.figma.ts +8 -8
- package/src/components/sidebar/MSidebar.vue +1 -0
- package/src/components/sidebarexpandableitem/MSidebarExpandableItem.figma.ts +8 -5
- package/src/components/sidebarexpandableitem/MSidebarExpandableItem.spec.ts +12 -0
- package/src/components/sidebarexpandableitem/MSidebarExpandableItem.vue +1 -0
- package/src/components/sidebarfooter/MSidebarFooter.figma.ts +7 -3
- package/src/components/sidebarheader/MSidebarHeader.figma.ts +3 -3
- package/src/components/sidebarnavitem/MSidebarNavItem.figma.ts +3 -3
- package/src/components/sidebarshortcutitem/MSidebarShortcutItem.figma.ts +3 -3
- package/src/components/starrating/MStarRating.figma.ts +7 -7
- package/src/components/statusbadge/MStatusBadge.figma.ts +3 -3
- package/src/components/statusdot/MStatusDot.figma.ts +3 -3
- package/src/components/statusmessage/MStatusMessage.figma.ts +3 -3
- package/src/components/statusnotification/MStatusNotification.figma.ts +7 -7
- package/src/components/stepperbottombar/MStepperBottomBar.figma.ts +3 -3
- package/src/components/steppercompact/MStepperCompact.figma.ts +8 -3
- package/src/components/steppercompact/MStepperCompact.spec.ts +9 -0
- package/src/components/steppercompact/MStepperCompact.vue +1 -1
- package/src/components/stepperinline/MStepperInline.figma.ts +6 -3
- package/src/components/stepperinline/MStepperInline.spec.ts +11 -0
- package/src/components/stepperinline/MStepperInline.stories.ts +5 -1
- package/src/components/stepperinline/MStepperInline.vue +1 -1
- package/src/components/stepperstacked/MStepperStacked.figma.ts +6 -3
- package/src/components/stepperstacked/MStepperStacked.spec.ts +13 -0
- package/src/components/stepperstacked/MStepperStacked.vue +1 -0
- package/src/components/tabs/MTabs.figma.ts +8 -8
- package/src/components/tag/MTag.figma.ts +3 -3
- package/src/components/textarea/MTextArea.figma.ts +8 -8
- package/src/components/textinput/MTextInput.figma.ts +12 -12
- package/src/components/textinput/MTextInput.vue +2 -2
- package/src/components/textinput/README.md +1 -1
- package/src/components/tile/MTile.figma.ts +5 -5
- package/src/components/tileclickable/MTileClickable.figma.ts +6 -6
- package/src/components/tileexpandable/MTileExpandable.figma.ts +6 -6
- package/src/components/tileselectable/MTileSelectable.figma.ts +9 -5
- package/src/components/toaster/MToaster.figma.ts +3 -3
- package/src/components/toggle/MToggle.figma.ts +9 -9
- package/src/components/toggle/MToggle.vue +1 -1
- package/src/components/togglegroup/MToggleGroup.figma.ts +7 -7
- package/src/components/tooltip/MTooltip.figma.ts +10 -5
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import { describe, it, expect } from 'vitest';
|
|
2
|
+
import { nextTick } from 'vue';
|
|
2
3
|
import { mount } from '@vue/test-utils';
|
|
3
4
|
import MModal from './MModal.vue';
|
|
4
5
|
|
|
@@ -77,17 +78,17 @@ describe('MModal component', () => {
|
|
|
77
78
|
expect(wrapper.find('.link-slot').exists()).toBe(true);
|
|
78
79
|
});
|
|
79
80
|
|
|
80
|
-
it('has
|
|
81
|
+
it('has inert attribute set correctly based on open prop', async () => {
|
|
81
82
|
const wrapper = mount(MModal, {
|
|
82
83
|
props: { open: false, title: 'Title' },
|
|
83
84
|
global: { stubs },
|
|
84
85
|
});
|
|
85
86
|
|
|
86
|
-
expect(wrapper.find('.mc-modal').attributes('
|
|
87
|
+
expect(wrapper.find('.mc-modal').attributes('inert')).toBeDefined();
|
|
87
88
|
|
|
88
89
|
await wrapper.setProps({ open: true });
|
|
89
90
|
|
|
90
|
-
expect(wrapper.find('.mc-modal').attributes('
|
|
91
|
+
expect(wrapper.find('.mc-modal').attributes('inert')).toBeUndefined();
|
|
91
92
|
});
|
|
92
93
|
|
|
93
94
|
it('adds "is-open" class when open is true', async () => {
|
|
@@ -160,4 +161,115 @@ describe('MModal component', () => {
|
|
|
160
161
|
expect(document.body.style.overflow).toBe('');
|
|
161
162
|
expect(document.documentElement.style.overflow).toBe('');
|
|
162
163
|
});
|
|
164
|
+
|
|
165
|
+
it('has role="dialog" on the modal element', () => {
|
|
166
|
+
const wrapper = mount(MModal, {
|
|
167
|
+
props: { open: true, title: 'Title' },
|
|
168
|
+
global: { stubs },
|
|
169
|
+
});
|
|
170
|
+
|
|
171
|
+
expect(wrapper.find('.mc-modal').attributes('role')).toBe('dialog');
|
|
172
|
+
});
|
|
173
|
+
|
|
174
|
+
it('sets aria-modal based on open prop', async () => {
|
|
175
|
+
const wrapper = mount(MModal, {
|
|
176
|
+
props: { open: false, title: 'Title' },
|
|
177
|
+
global: { stubs },
|
|
178
|
+
});
|
|
179
|
+
|
|
180
|
+
expect(wrapper.find('.mc-modal').attributes('aria-modal')).toBe('false');
|
|
181
|
+
|
|
182
|
+
await wrapper.setProps({ open: true });
|
|
183
|
+
|
|
184
|
+
expect(wrapper.find('.mc-modal').attributes('aria-modal')).toBe('true');
|
|
185
|
+
});
|
|
186
|
+
|
|
187
|
+
it('links aria-labelledby to the title element', () => {
|
|
188
|
+
const wrapper = mount(MModal, {
|
|
189
|
+
props: { open: true, title: 'Accessible Title' },
|
|
190
|
+
global: { stubs },
|
|
191
|
+
});
|
|
192
|
+
|
|
193
|
+
const modal = wrapper.find('.mc-modal');
|
|
194
|
+
const labelledBy = modal.attributes('aria-labelledby');
|
|
195
|
+
expect(labelledBy).toMatch(/^modalTitle-/);
|
|
196
|
+
|
|
197
|
+
const title = wrapper.find(`#${labelledBy}`);
|
|
198
|
+
expect(title.exists()).toBe(true);
|
|
199
|
+
expect(title.text()).toBe('Accessible Title');
|
|
200
|
+
});
|
|
201
|
+
|
|
202
|
+
it('closes when Escape key is pressed', async () => {
|
|
203
|
+
const wrapper = mount(MModal, {
|
|
204
|
+
props: { open: true, title: 'Title' },
|
|
205
|
+
global: { stubs },
|
|
206
|
+
});
|
|
207
|
+
|
|
208
|
+
await wrapper.find('.mc-modal').trigger('keydown', { key: 'Escape' });
|
|
209
|
+
|
|
210
|
+
const emitted = wrapper.emitted('update:open');
|
|
211
|
+
expect(emitted).toBeTruthy();
|
|
212
|
+
expect(emitted![emitted!.length - 1]).toEqual([false]);
|
|
213
|
+
});
|
|
214
|
+
|
|
215
|
+
it('moves focus to the modal when opened', async () => {
|
|
216
|
+
const wrapper = mount(MModal, {
|
|
217
|
+
props: { open: false, title: 'Title' },
|
|
218
|
+
global: { stubs },
|
|
219
|
+
attachTo: document.body,
|
|
220
|
+
});
|
|
221
|
+
|
|
222
|
+
await wrapper.setProps({ open: true });
|
|
223
|
+
await nextTick();
|
|
224
|
+
|
|
225
|
+
expect(document.activeElement).toBe(wrapper.find('.mc-modal').element);
|
|
226
|
+
|
|
227
|
+
wrapper.unmount();
|
|
228
|
+
});
|
|
229
|
+
|
|
230
|
+
it('traps focus from last to first element on Tab', async () => {
|
|
231
|
+
const wrapper = mount(MModal, {
|
|
232
|
+
props: { open: true, title: 'Title', closable: true },
|
|
233
|
+
slots: {
|
|
234
|
+
default: '<button data-test="last-focusable">Last</button>',
|
|
235
|
+
},
|
|
236
|
+
global: { stubs },
|
|
237
|
+
attachTo: document.body,
|
|
238
|
+
});
|
|
239
|
+
|
|
240
|
+
await nextTick();
|
|
241
|
+
|
|
242
|
+
const closeButton = wrapper.find('button.mc-modal__close');
|
|
243
|
+
const lastFocusable = wrapper.find('[data-test="last-focusable"]');
|
|
244
|
+
|
|
245
|
+
(lastFocusable.element as HTMLButtonElement).focus();
|
|
246
|
+
await lastFocusable.trigger('keydown', { key: 'Tab' });
|
|
247
|
+
|
|
248
|
+
expect(document.activeElement).toBe(closeButton.element);
|
|
249
|
+
|
|
250
|
+
wrapper.unmount();
|
|
251
|
+
});
|
|
252
|
+
|
|
253
|
+
it('traps focus from first to last element on Shift+Tab', async () => {
|
|
254
|
+
const wrapper = mount(MModal, {
|
|
255
|
+
props: { open: true, title: 'Title', closable: true },
|
|
256
|
+
slots: {
|
|
257
|
+
default: '<button data-test="last-focusable">Last</button>',
|
|
258
|
+
},
|
|
259
|
+
global: { stubs },
|
|
260
|
+
attachTo: document.body,
|
|
261
|
+
});
|
|
262
|
+
|
|
263
|
+
await nextTick();
|
|
264
|
+
|
|
265
|
+
const closeButton = wrapper.find('button.mc-modal__close');
|
|
266
|
+
const lastFocusable = wrapper.find('[data-test="last-focusable"]');
|
|
267
|
+
|
|
268
|
+
(closeButton.element as HTMLButtonElement).focus();
|
|
269
|
+
await closeButton.trigger('keydown', { key: 'Tab', shiftKey: true });
|
|
270
|
+
|
|
271
|
+
expect(document.activeElement).toBe(lastFocusable.element);
|
|
272
|
+
|
|
273
|
+
wrapper.unmount();
|
|
274
|
+
});
|
|
163
275
|
});
|
|
@@ -1,23 +1,38 @@
|
|
|
1
1
|
<template>
|
|
2
|
-
<MOverlay
|
|
2
|
+
<MOverlay
|
|
3
|
+
:is-visible="open"
|
|
4
|
+
:dialogLabel="`modalTitle-${id}`"
|
|
5
|
+
@click="onClickOverlay"
|
|
6
|
+
>
|
|
3
7
|
<section
|
|
8
|
+
ref="modalRef"
|
|
4
9
|
class="mc-modal"
|
|
5
10
|
:class="classObject"
|
|
6
11
|
role="dialog"
|
|
7
|
-
aria-labelledby="modalTitle"
|
|
8
|
-
:aria-modal="open ? 'true' : 'false'"
|
|
12
|
+
:aria-labelledby="`modalTitle-${id}`"
|
|
9
13
|
tabindex="-1"
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
+
v-bind="{
|
|
15
|
+
...$attrs,
|
|
16
|
+
...(open
|
|
17
|
+
? {
|
|
18
|
+
'aria-modal': 'true',
|
|
19
|
+
onKeydown: onKeydown,
|
|
20
|
+
onClick: (event: MouseEvent) => {
|
|
21
|
+
event.stopPropagation();
|
|
22
|
+
},
|
|
23
|
+
}
|
|
24
|
+
: {
|
|
25
|
+
'aria-modal': 'false',
|
|
26
|
+
inert: true,
|
|
27
|
+
}),
|
|
28
|
+
}"
|
|
14
29
|
>
|
|
15
30
|
<div class="mc-modal__dialog" role="document">
|
|
16
31
|
<header class="mc-modal__header">
|
|
17
32
|
<span v-if="$slots.icon" class="mc-modal__icon">
|
|
18
33
|
<slot name="icon" />
|
|
19
34
|
</span>
|
|
20
|
-
<h2 class="mc-modal__title" id="modalTitle">
|
|
35
|
+
<h2 v-if="title" class="mc-modal__title" :id="`modalTitle-${id}`">
|
|
21
36
|
{{ title }}
|
|
22
37
|
</h2>
|
|
23
38
|
<MIconButton
|
|
@@ -48,7 +63,16 @@
|
|
|
48
63
|
</template>
|
|
49
64
|
|
|
50
65
|
<script setup lang="ts">
|
|
51
|
-
import {
|
|
66
|
+
import {
|
|
67
|
+
computed,
|
|
68
|
+
onMounted,
|
|
69
|
+
onUnmounted,
|
|
70
|
+
ref,
|
|
71
|
+
watch,
|
|
72
|
+
nextTick,
|
|
73
|
+
type VNode,
|
|
74
|
+
useId,
|
|
75
|
+
} from 'vue';
|
|
52
76
|
import { Cross24 } from '@mozaic-ds/icons-vue';
|
|
53
77
|
import MIconButton from '../iconbutton/MIconButton.vue';
|
|
54
78
|
import MOverlay from '../overlay/MOverlay.vue';
|
|
@@ -64,7 +88,7 @@ const props = withDefaults(
|
|
|
64
88
|
/**
|
|
65
89
|
* Title of the modal.
|
|
66
90
|
*/
|
|
67
|
-
title
|
|
91
|
+
title?: string;
|
|
68
92
|
/**
|
|
69
93
|
* Description of the modal.
|
|
70
94
|
*/
|
|
@@ -107,6 +131,19 @@ defineSlots<{
|
|
|
107
131
|
footer?: VNode;
|
|
108
132
|
}>();
|
|
109
133
|
|
|
134
|
+
const id = useId();
|
|
135
|
+
|
|
136
|
+
const modalRef = ref<HTMLElement | null>(null);
|
|
137
|
+
|
|
138
|
+
const FOCUSABLE_SELECTOR = [
|
|
139
|
+
'a[href]',
|
|
140
|
+
'button:not([disabled])',
|
|
141
|
+
'input:not([disabled])',
|
|
142
|
+
'select:not([disabled])',
|
|
143
|
+
'textarea:not([disabled])',
|
|
144
|
+
'[tabindex]:not([tabindex="-1"])',
|
|
145
|
+
].join(', ');
|
|
146
|
+
|
|
110
147
|
const classObject = computed(() => {
|
|
111
148
|
return {
|
|
112
149
|
'is-open': props.open,
|
|
@@ -131,12 +168,16 @@ const unlockScroll = () => {
|
|
|
131
168
|
onMounted(() => {
|
|
132
169
|
watch(
|
|
133
170
|
() => props.open,
|
|
134
|
-
(isOpen) => {
|
|
171
|
+
async (isOpen) => {
|
|
135
172
|
emit('update:open', isOpen);
|
|
136
173
|
if (props.scroll === false) {
|
|
137
174
|
if (isOpen) lockScroll();
|
|
138
175
|
else unlockScroll();
|
|
139
176
|
}
|
|
177
|
+
if (isOpen) {
|
|
178
|
+
await nextTick();
|
|
179
|
+
modalRef.value?.focus();
|
|
180
|
+
}
|
|
140
181
|
},
|
|
141
182
|
{ immediate: true },
|
|
142
183
|
);
|
|
@@ -156,6 +197,45 @@ const onClose = () => {
|
|
|
156
197
|
emit('update:open', false);
|
|
157
198
|
};
|
|
158
199
|
|
|
200
|
+
const onKeydown = (event: KeyboardEvent) => {
|
|
201
|
+
if (event.key === 'Escape') {
|
|
202
|
+
onClose();
|
|
203
|
+
return;
|
|
204
|
+
}
|
|
205
|
+
|
|
206
|
+
if (event.key !== 'Tab' || !props.open || !isClient) return;
|
|
207
|
+
|
|
208
|
+
const modalElement = modalRef.value;
|
|
209
|
+
if (!modalElement) return;
|
|
210
|
+
|
|
211
|
+
const focusableElements = Array.from(
|
|
212
|
+
modalElement.querySelectorAll<HTMLElement>(FOCUSABLE_SELECTOR),
|
|
213
|
+
).filter((element) => !element.hasAttribute('disabled'));
|
|
214
|
+
|
|
215
|
+
if (!focusableElements.length) {
|
|
216
|
+
event.preventDefault();
|
|
217
|
+
modalElement.focus();
|
|
218
|
+
return;
|
|
219
|
+
}
|
|
220
|
+
|
|
221
|
+
const firstFocusable = focusableElements[0];
|
|
222
|
+
const lastFocusable = focusableElements[focusableElements.length - 1];
|
|
223
|
+
const activeElement = document.activeElement as HTMLElement | null;
|
|
224
|
+
|
|
225
|
+
if (event.shiftKey) {
|
|
226
|
+
if (activeElement === firstFocusable || activeElement === modalElement) {
|
|
227
|
+
event.preventDefault();
|
|
228
|
+
lastFocusable.focus();
|
|
229
|
+
}
|
|
230
|
+
return;
|
|
231
|
+
}
|
|
232
|
+
|
|
233
|
+
if (activeElement === lastFocusable || activeElement === modalElement) {
|
|
234
|
+
event.preventDefault();
|
|
235
|
+
firstFocusable.focus();
|
|
236
|
+
}
|
|
237
|
+
};
|
|
238
|
+
|
|
159
239
|
const emit = defineEmits<{
|
|
160
240
|
/**
|
|
161
241
|
* Emits when the modal display changes, updating the modelValue prop.
|
|
@@ -8,7 +8,7 @@ A modal is a dialog window that appears on top of the main content, requiring us
|
|
|
8
8
|
| Name | Description | Type | Default |
|
|
9
9
|
| --- | --- | --- | --- |
|
|
10
10
|
| `open` | if `true`, display the modal. | `boolean` | - |
|
|
11
|
-
| `title
|
|
11
|
+
| `title` | Title of the modal. | `string` | - |
|
|
12
12
|
| `description` | Description of the modal. | `string` | - |
|
|
13
13
|
| `closable` | if `true`, display the close button. | `boolean` | `true` |
|
|
14
14
|
| `scroll` | if `false`, lock the scroll when open. | `boolean` | `true` |
|
|
@@ -16,9 +16,13 @@ figma.connect(
|
|
|
16
16
|
},
|
|
17
17
|
example: ({ player }) =>
|
|
18
18
|
html`<script setup>
|
|
19
|
-
|
|
20
|
-
|
|
19
|
+
import { MNavigationIndicator } from '@mozaic-ds/vue';
|
|
20
|
+
</script>
|
|
21
21
|
|
|
22
|
-
|
|
22
|
+
<MNavigationIndicator
|
|
23
|
+
:steps="6"
|
|
24
|
+
:model-value="0"
|
|
25
|
+
player=${player}
|
|
26
|
+
></MNavigationIndicator>`,
|
|
23
27
|
},
|
|
24
28
|
);
|
|
@@ -23,9 +23,13 @@ figma.connect(
|
|
|
23
23
|
},
|
|
24
24
|
example: ({ label, appearance, size }) =>
|
|
25
25
|
html`<script setup>
|
|
26
|
-
|
|
27
|
-
|
|
26
|
+
import { MNumberBadge } from '@mozaic-ds/vue';
|
|
27
|
+
</script>
|
|
28
28
|
|
|
29
|
-
|
|
29
|
+
<MNumberBadge
|
|
30
|
+
:label=${label}
|
|
31
|
+
appearance=${appearance}
|
|
32
|
+
size=${size}
|
|
33
|
+
></MNumberBadge>`,
|
|
30
34
|
},
|
|
31
35
|
);
|
|
@@ -21,16 +21,16 @@ figma.connect(
|
|
|
21
21
|
},
|
|
22
22
|
example: ({ search, actions }) =>
|
|
23
23
|
html`<script setup>
|
|
24
|
-
|
|
25
|
-
|
|
24
|
+
import { MOptionListbox } from '@mozaic-ds/vue';
|
|
25
|
+
</script>
|
|
26
26
|
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
27
|
+
<MOptionListbox
|
|
28
|
+
id="option-listbox"
|
|
29
|
+
v-model="selected"
|
|
30
|
+
multiple
|
|
31
|
+
search=${search}
|
|
32
|
+
actions=${actions}
|
|
33
|
+
:options="[{ label: 'Option 1', value: '1' }, { label: 'Option 2', value: '2' }]"
|
|
34
|
+
/>`,
|
|
35
35
|
},
|
|
36
36
|
);
|
|
@@ -10,11 +10,11 @@ figma.connect(
|
|
|
10
10
|
props: {},
|
|
11
11
|
example: () =>
|
|
12
12
|
html`<script setup>
|
|
13
|
-
|
|
14
|
-
|
|
13
|
+
import { MOverlay } from '@mozaic-ds/vue';
|
|
14
|
+
</script>
|
|
15
15
|
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
16
|
+
<MOverlay is-visible dialog-label="Overlay">
|
|
17
|
+
<p>Overlay content</p>
|
|
18
|
+
</MOverlay>`,
|
|
19
19
|
},
|
|
20
20
|
);
|
|
@@ -13,9 +13,9 @@ figma.connect(
|
|
|
13
13
|
},
|
|
14
14
|
example: ({ title, shadow }) =>
|
|
15
15
|
html`<script setup>
|
|
16
|
-
|
|
17
|
-
|
|
16
|
+
import { MPageHeader } from '@mozaic-ds/vue';
|
|
17
|
+
</script>
|
|
18
18
|
|
|
19
|
-
|
|
19
|
+
<MPageHeader title=${title} shadow=${shadow} />`,
|
|
20
20
|
},
|
|
21
21
|
);
|
|
@@ -15,20 +15,20 @@ figma.connect(
|
|
|
15
15
|
},
|
|
16
16
|
example: ({ compact }) =>
|
|
17
17
|
html`<script setup>
|
|
18
|
-
|
|
19
|
-
|
|
18
|
+
import { MPagination } from '@mozaic-ds/vue';
|
|
19
|
+
</script>
|
|
20
20
|
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
21
|
+
<MPagination
|
|
22
|
+
id="pagination-id"
|
|
23
|
+
:model-value="1"
|
|
24
|
+
:compact=${compact}
|
|
25
|
+
:options="[
|
|
26
26
|
{ text: 'Page 1 of 99', value: 1 },
|
|
27
27
|
{ text: 'Page 2 of 99', value: 2 },
|
|
28
28
|
{ text: 'Page 99 of 99', value: 99 },
|
|
29
29
|
]"
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
30
|
+
select-label="Select page"
|
|
31
|
+
aria-label="pagination"
|
|
32
|
+
></MPagination>`,
|
|
33
33
|
},
|
|
34
34
|
);
|
|
@@ -16,15 +16,15 @@ figma.connect(
|
|
|
16
16
|
},
|
|
17
17
|
example: ({ isInvalid, isClearable }) =>
|
|
18
18
|
html`<script setup>
|
|
19
|
-
|
|
20
|
-
|
|
19
|
+
import { MPasswordInput } from '@mozaic-ds/vue';
|
|
20
|
+
</script>
|
|
21
21
|
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
22
|
+
<MPasswordInput
|
|
23
|
+
id="password-input-id"
|
|
24
|
+
:is-invalid=${isInvalid}
|
|
25
|
+
:is-clearable=${isClearable}
|
|
26
|
+
placeholder="Enter your password"
|
|
27
|
+
model-value=""
|
|
28
|
+
></MPasswordInput>`,
|
|
29
29
|
},
|
|
30
30
|
);
|
|
@@ -33,15 +33,15 @@ figma.connect(
|
|
|
33
33
|
},
|
|
34
34
|
example: ({ size, disabled, readonly, isInvalid }) =>
|
|
35
35
|
html`<script setup>
|
|
36
|
-
|
|
37
|
-
|
|
36
|
+
import { MPhoneNumber } from '@mozaic-ds/vue';
|
|
37
|
+
</script>
|
|
38
38
|
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
39
|
+
<MPhoneNumber
|
|
40
|
+
id="phone-number-id"
|
|
41
|
+
size=${size}
|
|
42
|
+
disabled=${disabled}
|
|
43
|
+
readonly=${readonly}
|
|
44
|
+
:is-invalid=${isInvalid}
|
|
45
|
+
></MPhoneNumber>`,
|
|
46
46
|
},
|
|
47
47
|
);
|
|
@@ -67,7 +67,9 @@ describe('MPhoneNumber', () => {
|
|
|
67
67
|
describe('Country Selection', () => {
|
|
68
68
|
it('should render country selector and flag by default', () => {
|
|
69
69
|
expect(
|
|
70
|
-
wrapper
|
|
70
|
+
wrapper
|
|
71
|
+
.find('.mc-phone-number-input__select .mc-select__control')
|
|
72
|
+
.exists(),
|
|
71
73
|
).toBe(true);
|
|
72
74
|
expect(wrapper.find('.mc-phone-number-input__flag').exists()).toBe(true);
|
|
73
75
|
});
|
|
@@ -197,7 +199,9 @@ describe('MPhoneNumber', () => {
|
|
|
197
199
|
wrapper = mount(MPhoneNumber, {
|
|
198
200
|
props: { ...defaultProps, size: 's' },
|
|
199
201
|
});
|
|
200
|
-
expect(
|
|
202
|
+
expect(
|
|
203
|
+
wrapper.find('.mc-phone-number-input__select').classes(),
|
|
204
|
+
).toContain('mc-select--s');
|
|
201
205
|
expect(wrapper.find('.mc-phone-number-input__input').classes()).toContain(
|
|
202
206
|
'mc-text-input--s',
|
|
203
207
|
);
|
|
@@ -4,25 +4,31 @@
|
|
|
4
4
|
class="mc-phone-number-input__select-wrapper"
|
|
5
5
|
:class="selectWrapperClass"
|
|
6
6
|
>
|
|
7
|
-
<
|
|
8
|
-
id="selectComponentId"
|
|
9
|
-
v-model="selectedCountry"
|
|
10
|
-
name="selectComponentName"
|
|
7
|
+
<div
|
|
11
8
|
class="mc-select mc-phone-number-input__select"
|
|
12
9
|
:class="sizeSelectClass"
|
|
13
|
-
:disabled="isDisabled"
|
|
14
|
-
:readonly="isReadOnly"
|
|
15
10
|
>
|
|
16
|
-
<
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
:
|
|
11
|
+
<select
|
|
12
|
+
id="selectComponentId"
|
|
13
|
+
class="mc-select__control"
|
|
14
|
+
v-model="selectedCountry"
|
|
15
|
+
name="selectComponentName"
|
|
16
|
+
:disabled="isDisabled"
|
|
17
|
+
:readonly="isReadOnly"
|
|
22
18
|
>
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
19
|
+
<option value="" selected hidden></option>
|
|
20
|
+
<option
|
|
21
|
+
v-for="country in countries"
|
|
22
|
+
:key="country"
|
|
23
|
+
:value="country"
|
|
24
|
+
:data-flag="country.toLowerCase()"
|
|
25
|
+
>
|
|
26
|
+
{{ getCountryName(country) }} (+{{
|
|
27
|
+
getCountryCallingCode(country)
|
|
28
|
+
}})
|
|
29
|
+
</option>
|
|
30
|
+
</select>
|
|
31
|
+
</div>
|
|
26
32
|
|
|
27
33
|
<div class="mc-phone-number-input__select-display">
|
|
28
34
|
<div class="mc-phone-number-input__flag">
|
|
@@ -27,15 +27,15 @@ figma.connect(
|
|
|
27
27
|
},
|
|
28
28
|
example: ({ disabled, readonly, isInvalid }) =>
|
|
29
29
|
html`<script setup>
|
|
30
|
-
|
|
31
|
-
|
|
30
|
+
import { MPincode } from '@mozaic-ds/vue';
|
|
31
|
+
</script>
|
|
32
32
|
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
33
|
+
<MPincode
|
|
34
|
+
id="pincode-id"
|
|
35
|
+
disabled=${disabled}
|
|
36
|
+
readonly=${readonly}
|
|
37
|
+
:is-invalid=${isInvalid}
|
|
38
|
+
aria-label="Enter your code"
|
|
39
|
+
></MPincode>`,
|
|
40
40
|
},
|
|
41
41
|
);
|
|
@@ -22,21 +22,21 @@ figma.connect(
|
|
|
22
22
|
},
|
|
23
23
|
example: ({ appearance, size, pointer, closable }) =>
|
|
24
24
|
html`<script setup>
|
|
25
|
-
|
|
26
|
-
|
|
25
|
+
import { MPopover, MButton } from '@mozaic-ds/vue';
|
|
26
|
+
</script>
|
|
27
27
|
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
28
|
+
<MPopover
|
|
29
|
+
id="popover-id"
|
|
30
|
+
title="Popover title"
|
|
31
|
+
description="Popover description"
|
|
32
|
+
:pointer=${pointer}
|
|
33
|
+
:closable=${closable}
|
|
34
|
+
appearance=${appearance}
|
|
35
|
+
size=${size}
|
|
36
|
+
>
|
|
37
|
+
<template #activator="{ id }">
|
|
38
|
+
<MButton :popovertarget="id">Open</MButton>
|
|
39
|
+
</template>
|
|
40
|
+
</MPopover>`,
|
|
41
41
|
},
|
|
42
42
|
);
|