@mozaic-ds/vue 2.16.0 → 2.18.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/dist/mozaic-vue.css +2 -1
- package/dist/mozaic-vue.d.ts +258 -137
- package/dist/mozaic-vue.js +14054 -10878
- package/dist/mozaic-vue.js.map +1 -1
- package/dist/mozaic-vue.umd.cjs +7 -25
- package/dist/mozaic-vue.umd.cjs.map +1 -1
- package/package.json +22 -11
- package/src/components/BrandPresets.mdx +2 -2
- package/src/components/Migration.mdx +651 -0
- package/src/components/accordionlist/MAccordionList.figma.ts +43 -0
- package/src/components/accordionlistitem/MAccordionListItem.figma.ts +27 -0
- package/src/components/accordionlistitem/MAccordionListItem.spec.ts +22 -3
- package/src/components/accordionlistitem/MAccordionListItem.vue +38 -28
- package/src/components/actionbottombar/MActionBottomBar.figma.ts +24 -0
- package/src/components/actionlistbox/MActionListbox.figma.ts +30 -0
- package/src/components/avatar/MAvatar.figma.ts +31 -0
- package/src/components/breadcrumb/MBreadcrumb.figma.ts +31 -0
- package/src/components/builtinmenu/MBuiltInMenu.figma.ts +23 -0
- package/src/components/builtinmenu/MBuiltInMenu.spec.ts +30 -1
- package/src/components/builtinmenu/MBuiltInMenu.vue +26 -17
- package/src/components/builtinmenu/README.md +2 -0
- package/src/components/button/MButton.figma.ts +41 -0
- package/src/components/callout/MCallout.figma.ts +29 -0
- package/src/components/callout/MCallout.spec.ts +35 -0
- package/src/components/callout/MCallout.vue +22 -4
- package/src/components/callout/README.md +2 -0
- package/src/components/carousel/MCarousel.figma.ts +32 -0
- package/src/components/checkbox/MCheckbox.figma.ts +45 -0
- package/src/components/checkboxgroup/MCheckboxGroup.figma.ts +30 -0
- package/src/components/checklistmenu/MCheckListMenu.figma.ts +29 -0
- package/src/components/checklistmenu/MCheckListMenu.spec.ts +12 -1
- package/src/components/checklistmenu/MCheckListMenu.vue +6 -0
- package/src/components/checklistmenu/README.md +2 -0
- package/src/components/circularprogressbar/MCircularProgressbar.figma.ts +31 -0
- package/src/components/combobox/MCombobox.figma.ts +48 -0
- package/src/components/combobox/MCombobox.spec.ts +1 -1
- package/src/components/combobox/MCombobox.vue +18 -9
- package/src/components/combobox/README.md +2 -2
- package/src/components/container/MContainer.figma.ts +30 -0
- package/src/components/datatable/DataTable.stories.ts +277 -0
- package/src/components/datatable/DataTableCells.stories.ts +251 -0
- package/src/components/datatable/DataTableEmpty.stories.ts +102 -0
- package/src/components/datatable/DataTableExpandable.stories.ts +95 -0
- package/src/components/datatable/DataTableNested.stories.ts +96 -0
- package/src/components/datatable/DataTableSelectable.stories.ts +124 -0
- package/src/components/datatable/DataTableSortable.stories.ts +164 -0
- package/src/components/datatable/MDataTable.types.ts +54 -0
- package/src/components/datatable/assets/styles.scss +10 -0
- package/src/components/datatable/datatable.mdx +63 -0
- package/src/components/datatable/tools/data.js +8 -0
- package/src/components/datatable/tools/data.json +2018 -0
- package/src/components/datatable/utils.js +19 -0
- package/src/components/datepicker/MDatepicker.figma.ts +20 -0
- package/src/components/divider/MDivider.figma.ts +30 -0
- package/src/components/drawer/MDrawer.figma.ts +37 -0
- package/src/components/drawer/README.md +1 -1
- package/src/components/field/MField.figma.ts +30 -0
- package/src/components/fileuploader/MFileUploader.figma.ts +23 -0
- package/src/components/fileuploaderitem/MFileUploaderItem.figma.ts +27 -0
- package/src/components/flag/MFlag.figma.ts +26 -0
- package/src/components/iconbutton/MIconButton.figma.ts +54 -0
- package/src/components/kpiitem/MKpiItem.figma.ts +33 -0
- package/src/components/linearprogressbarbuffer/MLinearProgressbarBuffer.figma.ts +31 -0
- package/src/components/linearprogressbarpercentage/MLinearProgressbarPercentage.figma.ts +26 -0
- package/src/components/link/MLink.figma.ts +32 -0
- package/src/components/loader/MLoader.figma.ts +30 -0
- package/src/components/loadingoverlay/MLoadingOverlay.figma.ts +18 -0
- package/src/components/modal/MModal.figma.ts +27 -0
- package/src/components/navigationindicator/MNavigationIndicator.figma.ts +24 -0
- package/src/components/navigationindicator/MNavigationIndicator.spec.ts +75 -18
- package/src/components/navigationindicator/MNavigationIndicator.vue +10 -12
- package/src/components/numberbadge/MNumberBadge.figma.ts +31 -0
- package/src/components/optionListbox/MOptionListbox.figma.ts +36 -0
- package/src/components/optionListbox/MOptionListbox.vue +34 -19
- package/src/components/optionListbox/README.md +1 -1
- package/src/components/overlay/MOverlay.figma.ts +20 -0
- package/src/components/pageheader/MPageHeader.figma.ts +21 -0
- package/src/components/pagination/MPagination.figma.ts +34 -0
- package/src/components/passwordinput/MPasswordInput.figma.ts +30 -0
- package/src/components/phonenumber/MPhoneNumber.figma.ts +47 -0
- package/src/components/pincode/MPincode.figma.ts +41 -0
- package/src/components/pincode/MPincode.spec.ts +1 -4
- package/src/components/pincode/MPincode.vue +11 -15
- package/src/components/popover/MPopover.figma.ts +42 -0
- package/src/components/popover/MPopover.spec.ts +126 -0
- package/src/components/popover/MPopover.vue +36 -1
- package/src/components/quantityselector/MQuantitySelector.figma.ts +50 -0
- package/src/components/radio/MRadio.figma.ts +40 -0
- package/src/components/radiogroup/MRadioGroup.figma.ts +30 -0
- package/src/components/segmentedcontrol/MSegmentedControl.figma.ts +33 -0
- package/src/components/segmentedcontrol/MSegmentedControl.spec.ts +92 -0
- package/src/components/segmentedcontrol/MSegmentedControl.vue +61 -2
- package/src/components/select/MSelect.figma.ts +49 -0
- package/src/components/sidebar/MSidebar.figma.ts +28 -0
- package/src/components/sidebarexpandableitem/MSidebarExpandableItem.figma.ts +19 -0
- package/src/components/sidebarfooter/MSidebarFooter.figma.ts +21 -0
- package/src/components/sidebarheader/MSidebarHeader.figma.ts +18 -0
- package/src/components/sidebarnavitem/MSidebarNavItem.figma.ts +23 -0
- package/src/components/sidebarshortcutitem/MSidebarShortcutItem.figma.ts +20 -0
- package/src/components/starrating/MStarRating.figma.ts +35 -0
- package/src/components/starrating/MStarRating.spec.ts +19 -22
- package/src/components/starrating/MStarRating.vue +3 -2
- package/src/components/statusbadge/MStatusBadge.figma.ts +27 -0
- package/src/components/statusdot/MStatusDot.figma.ts +31 -0
- package/src/components/statusmessage/MStatusMessage.figma.ts +28 -0
- package/src/components/statusmessage/MStatusMessage.spec.ts +15 -0
- package/src/components/statusmessage/MStatusMessage.stories.ts +4 -0
- package/src/components/statusmessage/MStatusMessage.vue +7 -0
- package/src/components/statusmessage/README.md +2 -0
- package/src/components/statusnotification/MStatusNotification.figma.ts +29 -0
- package/src/components/stepperbottombar/MStepperBottomBar.figma.ts +20 -0
- package/src/components/steppercompact/MStepperCompact.figma.ts +21 -0
- package/src/components/stepperinline/MStepperInline.figma.ts +23 -0
- package/src/components/stepperstacked/MStepperStacked.figma.ts +23 -0
- package/src/components/tabs/MTabs.figma.ts +33 -0
- package/src/components/tabs/MTabs.vue +90 -4
- package/src/components/tabs/Mtabs.spec.ts +162 -0
- package/src/components/tag/MTag.figma.ts +26 -0
- package/src/components/tag/MTag.stories.ts +13 -3
- package/src/components/tag/MTag.vue +11 -1
- package/src/components/tag/README.md +6 -0
- package/src/components/textarea/MTextArea.figma.ts +28 -0
- package/src/components/textinput/MTextInput.figma.ts +51 -0
- package/src/components/tile/MTile.figma.ts +31 -0
- package/src/components/tileclickable/MTileClickable.figma.ts +31 -0
- package/src/components/tileexpandable/MTileExpandable.figma.ts +31 -0
- package/src/components/tileselectable/MTileSelectable.figma.ts +29 -0
- package/src/components/toaster/MToaster.figma.ts +25 -0
- package/src/components/toggle/MToggle.figma.ts +39 -0
- package/src/components/togglegroup/MToggleGroup.figma.ts +30 -0
- package/src/components/tooltip/MTooltip.figma.ts +29 -0
- package/src/main.ts +1 -0
|
@@ -1,9 +1,5 @@
|
|
|
1
1
|
<template>
|
|
2
|
-
<
|
|
3
|
-
class="mc-navigation-indicator"
|
|
4
|
-
role="navigation"
|
|
5
|
-
aria-label="Navigations steps"
|
|
6
|
-
>
|
|
2
|
+
<nav class="mc-navigation-indicator" aria-label="Navigation steps">
|
|
7
3
|
<ul class="mc-navigation-indicator__list">
|
|
8
4
|
<li
|
|
9
5
|
v-for="(_step, index) in steps"
|
|
@@ -11,14 +7,14 @@
|
|
|
11
7
|
class="mc-navigation-indicator__item"
|
|
12
8
|
>
|
|
13
9
|
<button
|
|
10
|
+
type="button"
|
|
14
11
|
:class="{
|
|
15
12
|
'mc-navigation-indicator__button': true,
|
|
16
13
|
'mc-navigation-indicator__button--active': active === index,
|
|
17
14
|
}"
|
|
18
|
-
aria-label="
|
|
15
|
+
:aria-label="getStepAriaLabel(index)"
|
|
19
16
|
:aria-current="active === index && 'step'"
|
|
20
17
|
@click="setActiveStep(index)"
|
|
21
|
-
@keydown="onKeydown($event, index)"
|
|
22
18
|
></button>
|
|
23
19
|
</li>
|
|
24
20
|
</ul>
|
|
@@ -43,14 +39,14 @@
|
|
|
43
39
|
size="s"
|
|
44
40
|
ghost
|
|
45
41
|
@click="emit('action')"
|
|
46
|
-
aria-label="
|
|
42
|
+
:aria-label="props.action === 'pause' ? 'Pause' : 'Resume'"
|
|
47
43
|
>
|
|
48
44
|
<template #icon>
|
|
49
45
|
<component :is="actionIcon" />
|
|
50
46
|
</template>
|
|
51
47
|
</MIconButton>
|
|
52
48
|
</template>
|
|
53
|
-
</
|
|
49
|
+
</nav>
|
|
54
50
|
</template>
|
|
55
51
|
|
|
56
52
|
<script setup lang="ts">
|
|
@@ -120,10 +116,12 @@ function setActiveStep(step: number) {
|
|
|
120
116
|
active.value = step;
|
|
121
117
|
}
|
|
122
118
|
|
|
123
|
-
function
|
|
124
|
-
if (
|
|
125
|
-
|
|
119
|
+
function getStepAriaLabel(step: number) {
|
|
120
|
+
if (active.value === step) {
|
|
121
|
+
return `Current step, ${step + 1} of ${props.steps}`;
|
|
126
122
|
}
|
|
123
|
+
|
|
124
|
+
return `Go to step ${step + 1} of ${props.steps}`;
|
|
127
125
|
}
|
|
128
126
|
</script>
|
|
129
127
|
|
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Code Connect mapping for MNumberBadge
|
|
3
|
+
* Links Figma Number badge (ADS2) to @mozaic-ds/vue
|
|
4
|
+
*/
|
|
5
|
+
import figma, { html } from '@figma/code-connect/html';
|
|
6
|
+
|
|
7
|
+
figma.connect(
|
|
8
|
+
'https://www.figma.com/design/Zyh9RyabNaqkjbuFWP9Aqj/%E2%9C%A8-Components--ADS2---Stable-version-?node-id=5-13889',
|
|
9
|
+
{
|
|
10
|
+
props: {
|
|
11
|
+
label: figma.string('Count (number only)'),
|
|
12
|
+
appearance: figma.enum('Appearance', {
|
|
13
|
+
Standard: 'standard',
|
|
14
|
+
Accent: 'accent',
|
|
15
|
+
Inverse: 'inverse',
|
|
16
|
+
Danger: 'danger',
|
|
17
|
+
}),
|
|
18
|
+
size: figma.enum('Size', {
|
|
19
|
+
'S (16px)': 's',
|
|
20
|
+
'M (24px)': 'm',
|
|
21
|
+
'L (32px)': 'm',
|
|
22
|
+
}),
|
|
23
|
+
},
|
|
24
|
+
example: ({ label, appearance, size }) =>
|
|
25
|
+
html`<script setup>
|
|
26
|
+
import { MNumberBadge } from '@mozaic-ds/vue';
|
|
27
|
+
</script>
|
|
28
|
+
|
|
29
|
+
<MNumberBadge :label=${label} appearance=${appearance} size=${size}></MNumberBadge>`,
|
|
30
|
+
},
|
|
31
|
+
);
|
|
@@ -0,0 +1,36 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Code Connect mapping for MOptionListbox
|
|
3
|
+
* Links Figma _option listbox / multi-select to @mozaic-ds/vue
|
|
4
|
+
*/
|
|
5
|
+
import figma, { html } from '@figma/code-connect/html';
|
|
6
|
+
|
|
7
|
+
figma.connect(
|
|
8
|
+
'https://www.figma.com/design/Zyh9RyabNaqkjbuFWP9Aqj/%E2%9C%A8-Components--ADS2---Stable-version-?node-id=20140-26198',
|
|
9
|
+
{
|
|
10
|
+
props: {
|
|
11
|
+
search: figma.boolean('Has a search'),
|
|
12
|
+
actions: figma.boolean('Has actions (select / unselect all)'),
|
|
13
|
+
hasSections: figma.enum('Has sections', {
|
|
14
|
+
False: false,
|
|
15
|
+
True: true,
|
|
16
|
+
}),
|
|
17
|
+
hasAdditionalInfo: figma.enum('Has additional info', {
|
|
18
|
+
False: false,
|
|
19
|
+
True: true,
|
|
20
|
+
}),
|
|
21
|
+
},
|
|
22
|
+
example: ({ search, actions }) =>
|
|
23
|
+
html`<script setup>
|
|
24
|
+
import { MOptionListbox } from '@mozaic-ds/vue';
|
|
25
|
+
</script>
|
|
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
|
+
/>`,
|
|
35
|
+
},
|
|
36
|
+
);
|
|
@@ -82,23 +82,23 @@
|
|
|
82
82
|
"
|
|
83
83
|
>
|
|
84
84
|
<div class="mc-option-listbox__label">
|
|
85
|
-
<slot
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
</span>
|
|
85
|
+
<slot name="item" v-bind="{ item }">
|
|
86
|
+
<div class="mc-option-listbox__content">
|
|
87
|
+
<span
|
|
88
|
+
:class="
|
|
89
|
+
item.type === 'section'
|
|
90
|
+
? 'mc-option-listbox__section-title'
|
|
91
|
+
: 'mc-option-listbox__text'
|
|
92
|
+
"
|
|
93
|
+
>
|
|
94
|
+
{{ item.label }}
|
|
95
|
+
</span>
|
|
97
96
|
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
97
|
+
<span v-if="item.content" class="mc-option-listbox__additional">
|
|
98
|
+
{{ item.content }}
|
|
99
|
+
</span>
|
|
100
|
+
</div>
|
|
101
|
+
</slot>
|
|
102
102
|
|
|
103
103
|
<div class="mc-option-listbox__spacer"></div>
|
|
104
104
|
|
|
@@ -137,7 +137,22 @@ import {
|
|
|
137
137
|
Less20,
|
|
138
138
|
Check20,
|
|
139
139
|
} from '@mozaic-ds/icons-vue';
|
|
140
|
-
|
|
140
|
+
|
|
141
|
+
function debounce<T extends (...args: unknown[]) => unknown>(
|
|
142
|
+
fn: T,
|
|
143
|
+
wait: number,
|
|
144
|
+
): (...args: Parameters<T>) => void {
|
|
145
|
+
let timeoutId: ReturnType<typeof setTimeout> | null = null;
|
|
146
|
+
|
|
147
|
+
return function (this: unknown, ...args: Parameters<T>) {
|
|
148
|
+
if (timeoutId) {
|
|
149
|
+
clearTimeout(timeoutId);
|
|
150
|
+
}
|
|
151
|
+
timeoutId = setTimeout(() => {
|
|
152
|
+
fn.apply(this, args);
|
|
153
|
+
}, wait);
|
|
154
|
+
};
|
|
155
|
+
}
|
|
141
156
|
|
|
142
157
|
/**
|
|
143
158
|
* An Option Listbox is a customizable, accessible listbox component designed to power dropdowns and comboboxes with advanced selection capabilities. It supports single or multiple selection, optional search, grouped options with section headers, and full keyboard navigation.
|
|
@@ -228,9 +243,9 @@ const emit = defineEmits<{
|
|
|
228
243
|
|
|
229
244
|
defineSlots<{
|
|
230
245
|
/**
|
|
231
|
-
* Use this slot to
|
|
246
|
+
* Use this slot to customize the content of each item.
|
|
232
247
|
*/
|
|
233
|
-
|
|
248
|
+
item(props: { item: ListboxOption }): VNode;
|
|
234
249
|
}>();
|
|
235
250
|
|
|
236
251
|
const listboxEl = useTemplateRef('listboxEl');
|
|
@@ -24,7 +24,7 @@ An Option Listbox is a customizable, accessible listbox component designed to po
|
|
|
24
24
|
|
|
25
25
|
| Name | Description |
|
|
26
26
|
| --- | --- |
|
|
27
|
-
| `
|
|
27
|
+
| `item` | Use this slot to customize the content of each item. |
|
|
28
28
|
|
|
29
29
|
## Events
|
|
30
30
|
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Code Connect mapping for MOverlay
|
|
3
|
+
* Links Figma Overlay (ADS2) to @mozaic-ds/vue
|
|
4
|
+
*/
|
|
5
|
+
import figma, { html } from '@figma/code-connect/html';
|
|
6
|
+
|
|
7
|
+
figma.connect(
|
|
8
|
+
'https://www.figma.com/design/Zyh9RyabNaqkjbuFWP9Aqj/%E2%9C%A8-Components--ADS2---Stable-version-?node-id=6-19511',
|
|
9
|
+
{
|
|
10
|
+
props: {},
|
|
11
|
+
example: () =>
|
|
12
|
+
html`<script setup>
|
|
13
|
+
import { MOverlay } from '@mozaic-ds/vue';
|
|
14
|
+
</script>
|
|
15
|
+
|
|
16
|
+
<MOverlay is-visible dialog-label="Overlay">
|
|
17
|
+
<p>Overlay content</p>
|
|
18
|
+
</MOverlay>`,
|
|
19
|
+
},
|
|
20
|
+
);
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Code Connect mapping for MPageHeader
|
|
3
|
+
* Links Figma Page header (ADS2) to @mozaic-ds/vue
|
|
4
|
+
*/
|
|
5
|
+
import figma, { html } from '@figma/code-connect/html';
|
|
6
|
+
|
|
7
|
+
figma.connect(
|
|
8
|
+
'https://www.figma.com/design/Zyh9RyabNaqkjbuFWP9Aqj/%E2%9C%A8-Components--ADS2---Stable-version-?node-id=16419-62764',
|
|
9
|
+
{
|
|
10
|
+
props: {
|
|
11
|
+
title: figma.string('Title'),
|
|
12
|
+
shadow: figma.boolean('Has shadow'),
|
|
13
|
+
},
|
|
14
|
+
example: ({ title, shadow }) =>
|
|
15
|
+
html`<script setup>
|
|
16
|
+
import { MPageHeader } from '@mozaic-ds/vue';
|
|
17
|
+
</script>
|
|
18
|
+
|
|
19
|
+
<MPageHeader title=${title} shadow=${shadow} />`,
|
|
20
|
+
},
|
|
21
|
+
);
|
|
@@ -0,0 +1,34 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Code Connect mapping for MPagination
|
|
3
|
+
* Links Figma Pagination (ADS2) to @mozaic-ds/vue
|
|
4
|
+
*/
|
|
5
|
+
import figma, { html } from '@figma/code-connect/html';
|
|
6
|
+
|
|
7
|
+
figma.connect(
|
|
8
|
+
'https://www.figma.com/design/Zyh9RyabNaqkjbuFWP9Aqj/%E2%9C%A8-Components--ADS2---Stable-version-?node-id=6-11558',
|
|
9
|
+
{
|
|
10
|
+
props: {
|
|
11
|
+
compact: figma.enum('Compact mode', {
|
|
12
|
+
True: true,
|
|
13
|
+
False: false,
|
|
14
|
+
}),
|
|
15
|
+
},
|
|
16
|
+
example: ({ compact }) =>
|
|
17
|
+
html`<script setup>
|
|
18
|
+
import { MPagination } from '@mozaic-ds/vue';
|
|
19
|
+
</script>
|
|
20
|
+
|
|
21
|
+
<MPagination
|
|
22
|
+
id="pagination-id"
|
|
23
|
+
:model-value="1"
|
|
24
|
+
:compact=${compact}
|
|
25
|
+
:options="[
|
|
26
|
+
{ text: 'Page 1 of 99', value: 1 },
|
|
27
|
+
{ text: 'Page 2 of 99', value: 2 },
|
|
28
|
+
{ text: 'Page 99 of 99', value: 99 },
|
|
29
|
+
]"
|
|
30
|
+
select-label="Select page"
|
|
31
|
+
aria-label="pagination"
|
|
32
|
+
></MPagination>`,
|
|
33
|
+
},
|
|
34
|
+
);
|
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Code Connect mapping for MPasswordInput
|
|
3
|
+
* Links Figma _password input / base to @mozaic-ds/vue
|
|
4
|
+
*/
|
|
5
|
+
import figma, { html } from '@figma/code-connect/html';
|
|
6
|
+
|
|
7
|
+
figma.connect(
|
|
8
|
+
'https://www.figma.com/design/Zyh9RyabNaqkjbuFWP9Aqj/%E2%9C%A8-Components--ADS2---Stable-version-?node-id=6-29957',
|
|
9
|
+
{
|
|
10
|
+
props: {
|
|
11
|
+
isInvalid: figma.enum('Is invalid', {
|
|
12
|
+
True: true,
|
|
13
|
+
False: false,
|
|
14
|
+
}),
|
|
15
|
+
isClearable: figma.boolean('Is clearable'),
|
|
16
|
+
},
|
|
17
|
+
example: ({ isInvalid, isClearable }) =>
|
|
18
|
+
html`<script setup>
|
|
19
|
+
import { MPasswordInput } from '@mozaic-ds/vue';
|
|
20
|
+
</script>
|
|
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>`,
|
|
29
|
+
},
|
|
30
|
+
);
|
|
@@ -0,0 +1,47 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Code Connect mapping for MPhoneNumber
|
|
3
|
+
* Links Figma _phone number input / base to @mozaic-ds/vue
|
|
4
|
+
*/
|
|
5
|
+
import figma, { html } from '@figma/code-connect/html';
|
|
6
|
+
|
|
7
|
+
figma.connect(
|
|
8
|
+
'https://www.figma.com/design/Zyh9RyabNaqkjbuFWP9Aqj/%E2%9C%A8-Components--ADS2---Stable-version-?node-id=5022-21080',
|
|
9
|
+
{
|
|
10
|
+
props: {
|
|
11
|
+
size: figma.enum('Size', {
|
|
12
|
+
S: 's',
|
|
13
|
+
'M (default)': 'm',
|
|
14
|
+
}),
|
|
15
|
+
disabled: figma.enum('State', {
|
|
16
|
+
Disabled: true,
|
|
17
|
+
Default: false,
|
|
18
|
+
Hovered: false,
|
|
19
|
+
Focused: false,
|
|
20
|
+
'Read-only': false,
|
|
21
|
+
}),
|
|
22
|
+
readonly: figma.enum('State', {
|
|
23
|
+
'Read-only': true,
|
|
24
|
+
Default: false,
|
|
25
|
+
Hovered: false,
|
|
26
|
+
Focused: false,
|
|
27
|
+
Disabled: false,
|
|
28
|
+
}),
|
|
29
|
+
isInvalid: figma.enum('Is invalid', {
|
|
30
|
+
True: true,
|
|
31
|
+
False: false,
|
|
32
|
+
}),
|
|
33
|
+
},
|
|
34
|
+
example: ({ size, disabled, readonly, isInvalid }) =>
|
|
35
|
+
html`<script setup>
|
|
36
|
+
import { MPhoneNumber } from '@mozaic-ds/vue';
|
|
37
|
+
</script>
|
|
38
|
+
|
|
39
|
+
<MPhoneNumber
|
|
40
|
+
id="phone-number-id"
|
|
41
|
+
size=${size}
|
|
42
|
+
disabled=${disabled}
|
|
43
|
+
readonly=${readonly}
|
|
44
|
+
:is-invalid=${isInvalid}
|
|
45
|
+
></MPhoneNumber>`,
|
|
46
|
+
},
|
|
47
|
+
);
|
|
@@ -0,0 +1,41 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Code Connect mapping for MPincode
|
|
3
|
+
* Links Figma Pincode input (ADS2) to @mozaic-ds/vue
|
|
4
|
+
*/
|
|
5
|
+
import figma, { html } from '@figma/code-connect/html';
|
|
6
|
+
|
|
7
|
+
figma.connect(
|
|
8
|
+
'https://www.figma.com/design/Zyh9RyabNaqkjbuFWP9Aqj/%E2%9C%A8-Components--ADS2---Stable-version-?node-id=12443-35984',
|
|
9
|
+
{
|
|
10
|
+
props: {
|
|
11
|
+
disabled: figma.enum('State', {
|
|
12
|
+
Disabled: true,
|
|
13
|
+
Default: false,
|
|
14
|
+
Hovered: false,
|
|
15
|
+
'Read-only': false,
|
|
16
|
+
}),
|
|
17
|
+
readonly: figma.enum('State', {
|
|
18
|
+
'Read-only': true,
|
|
19
|
+
Default: false,
|
|
20
|
+
Disabled: false,
|
|
21
|
+
Hovered: false,
|
|
22
|
+
}),
|
|
23
|
+
isInvalid: figma.enum('Is invalid', {
|
|
24
|
+
True: true,
|
|
25
|
+
False: false,
|
|
26
|
+
}),
|
|
27
|
+
},
|
|
28
|
+
example: ({ disabled, readonly, isInvalid }) =>
|
|
29
|
+
html`<script setup>
|
|
30
|
+
import { MPincode } from '@mozaic-ds/vue';
|
|
31
|
+
</script>
|
|
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>`,
|
|
40
|
+
},
|
|
41
|
+
);
|
|
@@ -93,10 +93,7 @@ describe('MPincode component', () => {
|
|
|
93
93
|
},
|
|
94
94
|
});
|
|
95
95
|
|
|
96
|
-
|
|
97
|
-
for (const input of inputs) {
|
|
98
|
-
expect(input.classes()).toContain('is-invalid');
|
|
99
|
-
}
|
|
96
|
+
expect(wrapper.classes()).toContain('is-invalid');
|
|
100
97
|
});
|
|
101
98
|
|
|
102
99
|
it('disables inputs when disabled is true', () => {
|
|
@@ -1,5 +1,14 @@
|
|
|
1
1
|
<template>
|
|
2
|
-
<div
|
|
2
|
+
<div
|
|
3
|
+
class="mc-pincode-input"
|
|
4
|
+
:class="{
|
|
5
|
+
'mc-pincode-input': true,
|
|
6
|
+
'mc-pincode-input__disabled': disabled,
|
|
7
|
+
'mc-pincode-input__readonly': readonly,
|
|
8
|
+
'is-invalid': isInvalid,
|
|
9
|
+
}"
|
|
10
|
+
@paste="onPaste"
|
|
11
|
+
>
|
|
3
12
|
<input
|
|
4
13
|
v-for="(digit, index) in otp"
|
|
5
14
|
:key="index"
|
|
@@ -12,7 +21,6 @@
|
|
|
12
21
|
autocomplete="one-time-code"
|
|
13
22
|
:name="name || `pincode-${id}`"
|
|
14
23
|
class="mc-pincode-input__control"
|
|
15
|
-
:class="classObject"
|
|
16
24
|
:disabled="disabled"
|
|
17
25
|
:readonly="readonly"
|
|
18
26
|
:value="digit"
|
|
@@ -25,13 +33,7 @@
|
|
|
25
33
|
</template>
|
|
26
34
|
|
|
27
35
|
<script setup lang="ts">
|
|
28
|
-
import {
|
|
29
|
-
ref,
|
|
30
|
-
computed,
|
|
31
|
-
watch,
|
|
32
|
-
nextTick,
|
|
33
|
-
type ComponentPublicInstance,
|
|
34
|
-
} from 'vue';
|
|
36
|
+
import { ref, watch, nextTick, type ComponentPublicInstance } from 'vue';
|
|
35
37
|
/**
|
|
36
38
|
* A pincode input is a specialized input field used to enter short numeric codes, such as verification codes, security PINs, or authentication tokens. It typically separates each digit into individual fields to improve readability and ease of entry. This component is commonly used in two-factor authentication (2FA), password recovery, and secure access flows, ensuring a structured and user-friendly experience.<br><br> To put a label, requierement text, help text or to apply a valid or invalid message, the examples are available in the [Field section](/docs/form-elements-field--docs#input).
|
|
37
39
|
*/
|
|
@@ -71,12 +73,6 @@ const props = withDefaults(
|
|
|
71
73
|
},
|
|
72
74
|
);
|
|
73
75
|
|
|
74
|
-
const classObject = computed(() => {
|
|
75
|
-
return {
|
|
76
|
-
'is-invalid': props.isInvalid,
|
|
77
|
-
};
|
|
78
|
-
});
|
|
79
|
-
|
|
80
76
|
const emit = defineEmits<{
|
|
81
77
|
/**
|
|
82
78
|
* Emits when the pincode value changes, updating the modelValue prop.
|
|
@@ -0,0 +1,42 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Code Connect mapping for MPopover
|
|
3
|
+
* Links Figma Popover (ADS2) to @mozaic-ds/vue
|
|
4
|
+
*/
|
|
5
|
+
import figma, { html } from '@figma/code-connect/html';
|
|
6
|
+
|
|
7
|
+
figma.connect(
|
|
8
|
+
'https://www.figma.com/design/Zyh9RyabNaqkjbuFWP9Aqj/%E2%9C%A8-Components--ADS2---Stable-version-?node-id=9680-7268',
|
|
9
|
+
{
|
|
10
|
+
props: {
|
|
11
|
+
appearance: figma.enum('Appareance', {
|
|
12
|
+
Standard: 'standard',
|
|
13
|
+
Inverse: 'inverse',
|
|
14
|
+
}),
|
|
15
|
+
size: figma.enum('Size', {
|
|
16
|
+
'S (default)': 's',
|
|
17
|
+
M: 'm',
|
|
18
|
+
L: 'l',
|
|
19
|
+
}),
|
|
20
|
+
pointer: figma.boolean('Has pointer'),
|
|
21
|
+
closable: figma.boolean('Is closable'),
|
|
22
|
+
},
|
|
23
|
+
example: ({ appearance, size, pointer, closable }) =>
|
|
24
|
+
html`<script setup>
|
|
25
|
+
import { MPopover, MButton } from '@mozaic-ds/vue';
|
|
26
|
+
</script>
|
|
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>`,
|
|
41
|
+
},
|
|
42
|
+
);
|
|
@@ -103,4 +103,130 @@ describe('MPopover.vue', () => {
|
|
|
103
103
|
expect(activator.exists()).toBe(true);
|
|
104
104
|
expect(activator.attributes('popovertarget')).toBe(id);
|
|
105
105
|
});
|
|
106
|
+
|
|
107
|
+
describe('Focus management (toggle)', () => {
|
|
108
|
+
it('focuses the first focusable element inside when opened', async () => {
|
|
109
|
+
const wrapper = mount(MPopover, {
|
|
110
|
+
attachTo: document.body,
|
|
111
|
+
props: { closable: true },
|
|
112
|
+
});
|
|
113
|
+
|
|
114
|
+
const popoverEl = wrapper.find('.mc-popover__wrapper').element;
|
|
115
|
+
const toggleEvent = new Event('toggle') as ToggleEvent;
|
|
116
|
+
Object.defineProperty(toggleEvent, 'newState', { value: 'open' });
|
|
117
|
+
popoverEl.dispatchEvent(toggleEvent);
|
|
118
|
+
await wrapper.vm.$nextTick();
|
|
119
|
+
|
|
120
|
+
const firstFocusable = popoverEl.querySelector<HTMLElement>(
|
|
121
|
+
'button, [href], input, select, textarea, [tabindex]:not([tabindex="-1"])',
|
|
122
|
+
);
|
|
123
|
+
expect(document.activeElement).toBe(firstFocusable);
|
|
124
|
+
wrapper.unmount();
|
|
125
|
+
});
|
|
126
|
+
|
|
127
|
+
it('focuses the popover container when opened without focusable content', async () => {
|
|
128
|
+
const wrapper = mount(MPopover, {
|
|
129
|
+
attachTo: document.body,
|
|
130
|
+
props: {
|
|
131
|
+
closable: false,
|
|
132
|
+
title: 'Information',
|
|
133
|
+
description: 'Read-only content',
|
|
134
|
+
},
|
|
135
|
+
});
|
|
136
|
+
|
|
137
|
+
const popoverEl = wrapper.find('.mc-popover__wrapper')
|
|
138
|
+
.element as HTMLElement;
|
|
139
|
+
const toggleEvent = new Event('toggle') as ToggleEvent;
|
|
140
|
+
Object.defineProperty(toggleEvent, 'newState', { value: 'open' });
|
|
141
|
+
popoverEl.dispatchEvent(toggleEvent);
|
|
142
|
+
await wrapper.vm.$nextTick();
|
|
143
|
+
|
|
144
|
+
expect(document.activeElement).toBe(popoverEl);
|
|
145
|
+
wrapper.unmount();
|
|
146
|
+
});
|
|
147
|
+
|
|
148
|
+
it('returns focus to the trigger when closed with focus inside the popover', async () => {
|
|
149
|
+
const wrapper = mount(MPopover, {
|
|
150
|
+
attachTo: document.body,
|
|
151
|
+
props: { closable: true },
|
|
152
|
+
slots: {
|
|
153
|
+
activator: (slotProps: { id: string }) =>
|
|
154
|
+
h('button', {
|
|
155
|
+
popovertarget: slotProps.id,
|
|
156
|
+
class: 'trigger-btn',
|
|
157
|
+
}),
|
|
158
|
+
},
|
|
159
|
+
});
|
|
160
|
+
|
|
161
|
+
// Simulate focus being on the close button (inside the popover)
|
|
162
|
+
const closeBtn = wrapper.findComponent(MIconButton).find('button')
|
|
163
|
+
.element as HTMLElement;
|
|
164
|
+
closeBtn.focus();
|
|
165
|
+
|
|
166
|
+
const popoverEl = wrapper.find('.mc-popover__wrapper').element;
|
|
167
|
+
const toggleEvent = new Event('toggle') as ToggleEvent;
|
|
168
|
+
Object.defineProperty(toggleEvent, 'newState', { value: 'closed' });
|
|
169
|
+
popoverEl.dispatchEvent(toggleEvent);
|
|
170
|
+
await wrapper.vm.$nextTick();
|
|
171
|
+
|
|
172
|
+
const trigger = wrapper.find('.trigger-btn').element as HTMLElement;
|
|
173
|
+
expect(document.activeElement).toBe(trigger);
|
|
174
|
+
wrapper.unmount();
|
|
175
|
+
});
|
|
176
|
+
|
|
177
|
+
it('returns focus to the trigger when closed with focus on document.body', async () => {
|
|
178
|
+
const wrapper = mount(MPopover, {
|
|
179
|
+
attachTo: document.body,
|
|
180
|
+
slots: {
|
|
181
|
+
activator: (slotProps: { id: string }) =>
|
|
182
|
+
h('button', {
|
|
183
|
+
popovertarget: slotProps.id,
|
|
184
|
+
class: 'trigger-btn',
|
|
185
|
+
}),
|
|
186
|
+
},
|
|
187
|
+
});
|
|
188
|
+
|
|
189
|
+
// Simulate browser moving focus to body after popover collapses
|
|
190
|
+
(document.activeElement as HTMLElement)?.blur?.();
|
|
191
|
+
|
|
192
|
+
const popoverEl = wrapper.find('.mc-popover__wrapper').element;
|
|
193
|
+
const toggleEvent = new Event('toggle') as ToggleEvent;
|
|
194
|
+
Object.defineProperty(toggleEvent, 'newState', { value: 'closed' });
|
|
195
|
+
popoverEl.dispatchEvent(toggleEvent);
|
|
196
|
+
await wrapper.vm.$nextTick();
|
|
197
|
+
|
|
198
|
+
const trigger = wrapper.find('.trigger-btn').element as HTMLElement;
|
|
199
|
+
expect(document.activeElement).toBe(trigger);
|
|
200
|
+
wrapper.unmount();
|
|
201
|
+
});
|
|
202
|
+
|
|
203
|
+
it('does not steal focus when closed by an outside click (focus already on another element)', async () => {
|
|
204
|
+
const wrapper = mount(MPopover, {
|
|
205
|
+
attachTo: document.body,
|
|
206
|
+
slots: {
|
|
207
|
+
activator: (slotProps: { id: string }) =>
|
|
208
|
+
h('button', {
|
|
209
|
+
popovertarget: slotProps.id,
|
|
210
|
+
class: 'trigger-btn',
|
|
211
|
+
}),
|
|
212
|
+
},
|
|
213
|
+
});
|
|
214
|
+
|
|
215
|
+
// Simulate user clicking an outside input
|
|
216
|
+
const outsideInput = document.createElement('input');
|
|
217
|
+
document.body.appendChild(outsideInput);
|
|
218
|
+
outsideInput.focus();
|
|
219
|
+
|
|
220
|
+
const popoverEl = wrapper.find('.mc-popover__wrapper').element;
|
|
221
|
+
const toggleEvent = new Event('toggle') as ToggleEvent;
|
|
222
|
+
Object.defineProperty(toggleEvent, 'newState', { value: 'closed' });
|
|
223
|
+
popoverEl.dispatchEvent(toggleEvent);
|
|
224
|
+
await wrapper.vm.$nextTick();
|
|
225
|
+
|
|
226
|
+
// Focus should remain on the outside input, not be stolen by the trigger
|
|
227
|
+
expect(document.activeElement).toBe(outsideInput);
|
|
228
|
+
outsideInput.remove();
|
|
229
|
+
wrapper.unmount();
|
|
230
|
+
});
|
|
231
|
+
});
|
|
106
232
|
});
|