@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.
Files changed (132) hide show
  1. package/dist/mozaic-vue.css +1 -1
  2. package/dist/mozaic-vue.d.ts +961 -2085
  3. package/dist/mozaic-vue.js +1253 -1143
  4. package/dist/mozaic-vue.js.map +1 -1
  5. package/dist/mozaic-vue.umd.cjs +6 -6
  6. package/dist/mozaic-vue.umd.cjs.map +1 -1
  7. package/package.json +8 -6
  8. package/src/components/BrandPresets.mdx +20 -2
  9. package/src/components/accordionlist/MAccordionList.figma.ts +16 -16
  10. package/src/components/accordionlist/MAccordionList.stories.ts +1 -1
  11. package/src/components/accordionlistitem/MAccordionListItem.figma.ts +9 -5
  12. package/src/components/accordionlistitem/MAccordionListItem.vue +4 -1
  13. package/src/components/accordionlistitem/README.md +2 -0
  14. package/src/components/actionbottombar/MActionBottomBar.figma.ts +7 -7
  15. package/src/components/actionlistbox/MActionListbox.figma.ts +11 -11
  16. package/src/components/actionlistbox/MActionListbox.spec.ts +113 -0
  17. package/src/components/actionlistbox/MActionListbox.vue +63 -5
  18. package/src/components/avatar/MAvatar.figma.ts +5 -5
  19. package/src/components/breadcrumb/MBreadcrumb.figma.ts +7 -7
  20. package/src/components/breadcrumb/MBreadcrumb.vue +1 -1
  21. package/src/components/builtinmenu/MBuiltInMenu.figma.ts +8 -5
  22. package/src/components/builtinmenu/MBuiltInMenu.spec.ts +3 -1
  23. package/src/components/button/MButton.figma.ts +21 -6
  24. package/src/components/button/MButton.spec.ts +26 -0
  25. package/src/components/button/MButton.vue +2 -0
  26. package/src/components/callout/MCallout.figma.ts +7 -7
  27. package/src/components/callout/MCallout.stories.ts +0 -3
  28. package/src/components/callout/MCallout.vue +4 -3
  29. package/src/components/callout/README.md +2 -2
  30. package/src/components/carousel/MCarousel.figma.ts +10 -10
  31. package/src/components/carousel/MCarousel.spec.ts +26 -2
  32. package/src/components/carousel/MCarousel.vue +10 -4
  33. package/src/components/checkbox/MCheckbox.figma.ts +10 -10
  34. package/src/components/checkboxgroup/MCheckboxGroup.figma.ts +7 -7
  35. package/src/components/checklistmenu/MCheckListMenu.figma.ts +8 -8
  36. package/src/components/circularprogressbar/MCircularProgressbar.figma.ts +7 -3
  37. package/src/components/combobox/MCombobox.figma.ts +10 -10
  38. package/src/components/combobox/MCombobox.vue +7 -0
  39. package/src/components/container/MContainer.figma.ts +5 -5
  40. package/src/components/datatable/DataTable.stories.ts +33 -7
  41. package/src/components/datatable/DataTableCells.stories.ts +2 -2
  42. package/src/components/datatable/DataTableEmpty.stories.ts +2 -2
  43. package/src/components/datatable/DataTableExpandable.stories.ts +2 -2
  44. package/src/components/datatable/DataTableNested.stories.ts +1 -1
  45. package/src/components/datatable/DataTableSelectable.stories.ts +2 -3
  46. package/src/components/datatable/DataTableSortable.stories.ts +1 -1
  47. package/src/components/datepicker/MDatepicker.figma.ts +3 -3
  48. package/src/components/divider/MDivider.figma.ts +3 -3
  49. package/src/components/drawer/MDrawer.figma.ts +13 -13
  50. package/src/components/drawer/MDrawer.spec.ts +102 -3
  51. package/src/components/drawer/MDrawer.vue +73 -14
  52. package/src/components/field/MField.figma.ts +9 -5
  53. package/src/components/field/MField.vue +1 -0
  54. package/src/components/fileuploader/MFileUploader.figma.ts +3 -3
  55. package/src/components/fileuploader/MFileUploader.vue +2 -2
  56. package/src/components/fileuploaderitem/MFileUploaderItem.figma.ts +7 -3
  57. package/src/components/fileuploaderitem/MFileUploaderItem.vue +2 -7
  58. package/src/components/flag/MFlag.figma.ts +3 -3
  59. package/src/components/iconbutton/MIconButton.figma.ts +16 -16
  60. package/src/components/iconbutton/MIconButton.spec.ts +15 -0
  61. package/src/components/iconbutton/MIconButton.vue +1 -0
  62. package/src/components/kpiitem/MKpiItem.figma.ts +8 -3
  63. package/src/components/kpiitem/MKpiItem.spec.ts +12 -0
  64. package/src/components/kpiitem/MKpiItem.vue +7 -1
  65. package/src/components/linearprogressbarbuffer/MLinearProgressbarBuffer.figma.ts +6 -3
  66. package/src/components/linearprogressbarpercentage/MLinearProgressbarPercentage.figma.ts +5 -3
  67. package/src/components/link/MLink.figma.ts +5 -5
  68. package/src/components/loader/MLoader.figma.ts +3 -3
  69. package/src/components/loadingoverlay/MLoadingOverlay.figma.ts +3 -3
  70. package/src/components/modal/MModal.figma.ts +12 -12
  71. package/src/components/modal/MModal.spec.ts +115 -3
  72. package/src/components/modal/MModal.vue +91 -11
  73. package/src/components/modal/README.md +1 -1
  74. package/src/components/navigationindicator/MNavigationIndicator.figma.ts +7 -3
  75. package/src/components/numberbadge/MNumberBadge.figma.ts +7 -3
  76. package/src/components/optionListbox/MOptionListbox.figma.ts +10 -10
  77. package/src/components/overlay/MOverlay.figma.ts +5 -5
  78. package/src/components/overlay/MOverlay.spec.ts +1 -1
  79. package/src/components/overlay/MOverlay.vue +1 -1
  80. package/src/components/pageheader/MPageHeader.figma.ts +3 -3
  81. package/src/components/pagination/MPagination.figma.ts +10 -10
  82. package/src/components/passwordinput/MPasswordInput.figma.ts +9 -9
  83. package/src/components/phonenumber/MPhoneNumber.figma.ts +9 -9
  84. package/src/components/phonenumber/MPhoneNumber.spec.ts +6 -2
  85. package/src/components/phonenumber/MPhoneNumber.vue +21 -15
  86. package/src/components/pincode/MPincode.figma.ts +9 -9
  87. package/src/components/popover/MPopover.figma.ts +15 -15
  88. package/src/components/quantityselector/MQuantitySelector.figma.ts +12 -12
  89. package/src/components/radio/MRadio.figma.ts +9 -9
  90. package/src/components/radiogroup/MRadioGroup.figma.ts +7 -7
  91. package/src/components/segmentedcontrol/MSegmentedControl.figma.ts +8 -8
  92. package/src/components/select/MSelect.figma.ts +11 -11
  93. package/src/components/sidebar/MSidebar.figma.ts +8 -8
  94. package/src/components/sidebar/MSidebar.vue +1 -0
  95. package/src/components/sidebarexpandableitem/MSidebarExpandableItem.figma.ts +8 -5
  96. package/src/components/sidebarexpandableitem/MSidebarExpandableItem.spec.ts +12 -0
  97. package/src/components/sidebarexpandableitem/MSidebarExpandableItem.vue +1 -0
  98. package/src/components/sidebarfooter/MSidebarFooter.figma.ts +7 -3
  99. package/src/components/sidebarheader/MSidebarHeader.figma.ts +3 -3
  100. package/src/components/sidebarnavitem/MSidebarNavItem.figma.ts +3 -3
  101. package/src/components/sidebarshortcutitem/MSidebarShortcutItem.figma.ts +3 -3
  102. package/src/components/starrating/MStarRating.figma.ts +7 -7
  103. package/src/components/statusbadge/MStatusBadge.figma.ts +3 -3
  104. package/src/components/statusdot/MStatusDot.figma.ts +3 -3
  105. package/src/components/statusmessage/MStatusMessage.figma.ts +3 -3
  106. package/src/components/statusnotification/MStatusNotification.figma.ts +7 -7
  107. package/src/components/stepperbottombar/MStepperBottomBar.figma.ts +3 -3
  108. package/src/components/steppercompact/MStepperCompact.figma.ts +8 -3
  109. package/src/components/steppercompact/MStepperCompact.spec.ts +9 -0
  110. package/src/components/steppercompact/MStepperCompact.vue +1 -1
  111. package/src/components/stepperinline/MStepperInline.figma.ts +6 -3
  112. package/src/components/stepperinline/MStepperInline.spec.ts +11 -0
  113. package/src/components/stepperinline/MStepperInline.stories.ts +5 -1
  114. package/src/components/stepperinline/MStepperInline.vue +1 -1
  115. package/src/components/stepperstacked/MStepperStacked.figma.ts +6 -3
  116. package/src/components/stepperstacked/MStepperStacked.spec.ts +13 -0
  117. package/src/components/stepperstacked/MStepperStacked.vue +1 -0
  118. package/src/components/tabs/MTabs.figma.ts +8 -8
  119. package/src/components/tag/MTag.figma.ts +3 -3
  120. package/src/components/textarea/MTextArea.figma.ts +8 -8
  121. package/src/components/textinput/MTextInput.figma.ts +12 -12
  122. package/src/components/textinput/MTextInput.vue +2 -2
  123. package/src/components/textinput/README.md +1 -1
  124. package/src/components/tile/MTile.figma.ts +5 -5
  125. package/src/components/tileclickable/MTileClickable.figma.ts +6 -6
  126. package/src/components/tileexpandable/MTileExpandable.figma.ts +6 -6
  127. package/src/components/tileselectable/MTileSelectable.figma.ts +9 -5
  128. package/src/components/toaster/MToaster.figma.ts +3 -3
  129. package/src/components/toggle/MToggle.figma.ts +9 -9
  130. package/src/components/toggle/MToggle.vue +1 -1
  131. package/src/components/togglegroup/MToggleGroup.figma.ts +7 -7
  132. 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 aria-hidden set correctly based on open prop', async () => {
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('aria-hidden')).toBe('true');
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('aria-hidden')).toBe('false');
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 :is-visible="open" dialogLabel="modalTitle" @click="onClickOverlay">
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
- :aria-hidden="!open"
11
- v-bind="$attrs"
12
- @keydown.esc="onClose"
13
- @click.stop
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 { computed, onMounted, onUnmounted, watch, type VNode } from 'vue';
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: string;
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*` | Title of the modal. | `string` | - |
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
- import { MNavigationIndicator } from '@mozaic-ds/vue';
20
- </script>
19
+ import { MNavigationIndicator } from '@mozaic-ds/vue';
20
+ </script>
21
21
 
22
- <MNavigationIndicator :steps="6" :model-value="0" player=${player}></MNavigationIndicator>`,
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
- import { MNumberBadge } from '@mozaic-ds/vue';
27
- </script>
26
+ import { MNumberBadge } from '@mozaic-ds/vue';
27
+ </script>
28
28
 
29
- <MNumberBadge :label=${label} appearance=${appearance} size=${size}></MNumberBadge>`,
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
- import { MOptionListbox } from '@mozaic-ds/vue';
25
- </script>
24
+ import { MOptionListbox } from '@mozaic-ds/vue';
25
+ </script>
26
26
 
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
- />`,
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
- import { MOverlay } from '@mozaic-ds/vue';
14
- </script>
13
+ import { MOverlay } from '@mozaic-ds/vue';
14
+ </script>
15
15
 
16
- <MOverlay is-visible dialog-label="Overlay">
17
- <p>Overlay content</p>
18
- </MOverlay>`,
16
+ <MOverlay is-visible dialog-label="Overlay">
17
+ <p>Overlay content</p>
18
+ </MOverlay>`,
19
19
  },
20
20
  );
@@ -32,7 +32,7 @@ describe('MOverlay.vue', () => {
32
32
  });
33
33
 
34
34
  expect(
35
- wrapper.find('div[role="dialog"]').attributes('aria-labelledby'),
35
+ wrapper.find('div[aria-labelledby]').attributes('aria-labelledby'),
36
36
  ).toBe(label);
37
37
  });
38
38
 
@@ -1,6 +1,6 @@
1
1
  <template>
2
2
  <div class="mc-overlay" :class="{ 'is-visible': isVisible }">
3
- <div role="dialog" tabindex="-1" :aria-labelledby="dialogLabel">
3
+ <div tabindex="-1" :aria-labelledby="dialogLabel">
4
4
  <slot />
5
5
  </div>
6
6
  </div>
@@ -13,9 +13,9 @@ figma.connect(
13
13
  },
14
14
  example: ({ title, shadow }) =>
15
15
  html`<script setup>
16
- import { MPageHeader } from '@mozaic-ds/vue';
17
- </script>
16
+ import { MPageHeader } from '@mozaic-ds/vue';
17
+ </script>
18
18
 
19
- <MPageHeader title=${title} shadow=${shadow} />`,
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
- import { MPagination } from '@mozaic-ds/vue';
19
- </script>
18
+ import { MPagination } from '@mozaic-ds/vue';
19
+ </script>
20
20
 
21
- <MPagination
22
- id="pagination-id"
23
- :model-value="1"
24
- :compact=${compact}
25
- :options="[
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
- select-label="Select page"
31
- aria-label="pagination"
32
- ></MPagination>`,
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
- import { MPasswordInput } from '@mozaic-ds/vue';
20
- </script>
19
+ import { MPasswordInput } from '@mozaic-ds/vue';
20
+ </script>
21
21
 
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>`,
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
- import { MPhoneNumber } from '@mozaic-ds/vue';
37
- </script>
36
+ import { MPhoneNumber } from '@mozaic-ds/vue';
37
+ </script>
38
38
 
39
- <MPhoneNumber
40
- id="phone-number-id"
41
- size=${size}
42
- disabled=${disabled}
43
- readonly=${readonly}
44
- :is-invalid=${isInvalid}
45
- ></MPhoneNumber>`,
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.find('select.mc-phone-number-input__select').exists(),
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(wrapper.find('select').classes()).toContain('mc-select--s');
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
- <select
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
- <option value="" selected hidden></option>
17
- <option
18
- v-for="country in countries"
19
- :key="country"
20
- :value="country"
21
- :data-flag="country.toLowerCase()"
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
- {{ getCountryName(country) }} (+{{ getCountryCallingCode(country) }})
24
- </option>
25
- </select>
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
- import { MPincode } from '@mozaic-ds/vue';
31
- </script>
30
+ import { MPincode } from '@mozaic-ds/vue';
31
+ </script>
32
32
 
33
- <MPincode
34
- id="pincode-id"
35
- disabled=${disabled}
36
- readonly=${readonly}
37
- :is-invalid=${isInvalid}
38
- aria-label="Enter your code"
39
- ></MPincode>`,
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
- import { MPopover, MButton } from '@mozaic-ds/vue';
26
- </script>
25
+ import { MPopover, MButton } from '@mozaic-ds/vue';
26
+ </script>
27
27
 
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>`,
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
  );