@mozaic-ds/vue 2.19.1 → 2.20.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 +1 -1
- package/dist/mozaic-vue.d.ts +512 -296
- package/dist/mozaic-vue.js +1952 -1678
- 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 +6 -3
- package/src/components/Catalog.scss +84 -0
- package/src/components/Catalog.stories.ts +146 -0
- package/src/components/GettingStarted.mdx +258 -2
- package/src/components/Migration.mdx +4135 -532
- package/src/components/Welcome.mdx +1 -1
- package/src/components/carousel/MCarousel.spec.ts +174 -55
- package/src/components/carousel/MCarousel.vue +106 -33
- package/src/components/checkboxgroup/MCheckboxGroup.vue +2 -9
- package/src/components/combobox/MCombobox.spec.ts +144 -0
- package/src/components/combobox/MCombobox.stories.ts +2 -1
- package/src/components/combobox/MCombobox.vue +68 -38
- package/src/components/combobox/README.md +6 -0
- package/src/components/datatable/DataTable.stories.ts +2 -2
- package/src/components/datatable/DataTableCells.stories.ts +1 -0
- package/src/components/datatable/DataTableEmpty.stories.ts +1 -0
- package/src/components/datatable/DataTableExpandable.stories.ts +1 -0
- package/src/components/datatable/DataTableNested.stories.ts +1 -0
- package/src/components/datatable/DataTablePageable.stories.ts +194 -0
- package/src/components/datatable/DataTableSelectable.stories.ts +1 -0
- package/src/components/datatable/DataTableSortable.stories.ts +1 -0
- package/src/components/datepicker/MDatepicker.stories.ts +2 -0
- package/src/components/datepicker/MDatepicker.vue +17 -11
- package/src/components/divider/README.md +2 -0
- package/src/components/drawer/MDrawer.vue +7 -5
- package/src/components/link/MLink.stories.ts +18 -0
- package/src/components/link/MLink.vue +20 -4
- package/src/components/link/README.md +2 -1
- package/src/components/optionListbox/MOptionListbox.spec.ts +78 -1
- package/src/components/optionListbox/MOptionListbox.types.ts +7 -0
- package/src/components/optionListbox/MOptionListbox.vue +31 -217
- package/src/components/optionListbox/README.md +2 -0
- package/src/components/optionListbox/use-listbox-navigation.composable.ts +147 -0
- package/src/components/optionListbox/use-listbox-selection.composable.ts +149 -0
- package/src/components/pageheader/MPageHeader.spec.ts +17 -3
- package/src/components/pageheader/MPageHeader.stories.ts +37 -41
- package/src/components/pageheader/MPageHeader.vue +26 -16
- package/src/components/pageheader/README.md +2 -1
- package/src/components/pageheadercontext/MPageHeaderContext.spec.ts +155 -0
- package/src/components/pageheadercontext/MPageHeaderContext.vue +126 -0
- package/src/components/pageheadercontext/README.md +33 -0
- package/src/components/pageheaderfeature/MPageHeaderFeature.spec.ts +39 -0
- package/src/components/pageheaderfeature/MPageHeaderFeature.vue +40 -0
- package/src/components/pageheaderfeature/README.md +17 -0
- package/src/components/radiogroup/MRadioGroup.stories.ts +6 -0
- package/src/components/radiogroup/MRadioGroup.vue +2 -9
- package/src/components/sidebar/MSidebar.const.ts +2 -0
- package/src/components/sidebar/MSidebar.stories.ts +33 -0
- package/src/components/sidebar/MSidebar.vue +10 -1
- package/src/components/sidebar/README.md +1 -0
- package/src/components/sidebar/stories/DefaultCase.stories.vue +12 -11
- package/src/components/sidebar/use-floating-item.composable.ts +1 -1
- package/src/components/sidebarexpandableitem/MSidebarExpandableItem.vue +13 -7
- package/src/components/sidebarexpandableitem/README.md +2 -1
- package/src/components/sidebarfooter/MSidebarFooter.vue +16 -9
- package/src/components/sidebarfooter/_MSidebarFooterMenu.vue +10 -2
- package/src/components/sidebarnavitem/MSidebarNavItem.vue +36 -5
- package/src/components/sidebarnavitem/README.md +1 -0
- package/src/components/sidebarshortcutitem/MSidebarShortcutItem.vue +24 -3
- package/src/components/sidebarshortcutitem/README.md +3 -1
- package/src/components/sidebarshortcuts/MSidebarShortcuts.vue +6 -1
- package/src/components/sidebarshortcuts/README.md +12 -0
- package/src/components/statusbadge/MStatusBadge.stories.ts +5 -1
- package/src/components/statusbadge/MStatusBadge.vue +7 -1
- package/src/components/statusbadge/MstatusBadge.spec.ts +31 -0
- package/src/components/statusbadge/README.md +1 -0
- package/src/components/statusmessage/MStatusMessage.spec.ts +4 -4
- package/src/components/statusmessage/MStatusMessage.vue +5 -10
- package/src/components/statusnotification/MStatusNotification.spec.ts +9 -9
- package/src/components/statusnotification/MStatusNotification.vue +9 -8
- package/src/components/tabs/MTabs.stories.ts +30 -1
- package/src/components/tabs/MTabs.vue +12 -3
- package/src/components/tabs/Mtabs.spec.ts +19 -0
- package/src/components/tabs/README.md +1 -0
- package/src/components/tileselectable/MTileSelectable.spec.ts +69 -0
- package/src/components/tileselectable/MTileSelectable.stories.ts +8 -2
- package/src/components/tileselectable/MTileSelectable.vue +18 -1
- package/src/components/tileselectable/README.md +1 -0
- package/src/main.ts +3 -0
- package/src/utils/debounce.ts +15 -0
- package/src/components/BrandPresets.mdx +0 -146
- package/src/components/DarkMode.mdx +0 -115
|
@@ -17,6 +17,18 @@ SidebarShortcuts is a component that displays a list of sidebar shortcut items.
|
|
|
17
17
|
|
|
18
18
|
## Dependencies
|
|
19
19
|
|
|
20
|
+
### Depends on
|
|
21
|
+
|
|
22
|
+
- [MDivider](../divider)
|
|
23
|
+
|
|
24
|
+
### Graph
|
|
25
|
+
|
|
26
|
+
```mermaid
|
|
27
|
+
graph TD;
|
|
28
|
+
MSidebarShortcuts --> MDivider
|
|
29
|
+
style MSidebarShortcuts fill:#008240,stroke:#333,stroke-width:4px
|
|
30
|
+
```
|
|
31
|
+
|
|
20
32
|
### Used By
|
|
21
33
|
|
|
22
34
|
- [DefaultCase.stories](../sidebar/stories)
|
|
@@ -12,7 +12,7 @@ const meta: Meta<typeof MStatusBadge> = {
|
|
|
12
12
|
},
|
|
13
13
|
},
|
|
14
14
|
},
|
|
15
|
-
args: { label: 'Badge label' },
|
|
15
|
+
args: { label: 'Badge label', size: 'm' },
|
|
16
16
|
render: (args) => ({
|
|
17
17
|
components: { MStatusBadge },
|
|
18
18
|
setup() {
|
|
@@ -28,6 +28,10 @@ type Story = StoryObj<typeof MStatusBadge>;
|
|
|
28
28
|
|
|
29
29
|
export const Info: Story = {};
|
|
30
30
|
|
|
31
|
+
export const Small: Story = {
|
|
32
|
+
args: { size: 's' },
|
|
33
|
+
};
|
|
34
|
+
|
|
31
35
|
export const Success: Story = {
|
|
32
36
|
args: { status: 'success' },
|
|
33
37
|
};
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
<template>
|
|
2
2
|
<div class="mc-status-badge" :class="classObject">
|
|
3
|
-
<MStatusDot :status="status" />
|
|
3
|
+
<MStatusDot :status="status" :size="size" />
|
|
4
4
|
<span class="mc-status-badge__label">{{ label }}</span>
|
|
5
5
|
</div>
|
|
6
6
|
</template>
|
|
@@ -17,12 +17,17 @@ const props = withDefaults(
|
|
|
17
17
|
* Content of the status badge
|
|
18
18
|
*/
|
|
19
19
|
label: string;
|
|
20
|
+
/**
|
|
21
|
+
* Allows to define the status badge size
|
|
22
|
+
*/
|
|
23
|
+
size?: 's' | 'm';
|
|
20
24
|
/**
|
|
21
25
|
* Allows to define the status badge type
|
|
22
26
|
*/
|
|
23
27
|
status?: 'info' | 'success' | 'warning' | 'error' | 'neutral';
|
|
24
28
|
}>(),
|
|
25
29
|
{
|
|
30
|
+
size: 'm',
|
|
26
31
|
status: 'info',
|
|
27
32
|
},
|
|
28
33
|
);
|
|
@@ -31,6 +36,7 @@ const classObject = computed(() => {
|
|
|
31
36
|
return {
|
|
32
37
|
[`mc-status-badge--${props.status}`]:
|
|
33
38
|
props.status && props.status != 'info',
|
|
39
|
+
[`mc-status-badge--${props.size}`]: props.size && props.size != 'm',
|
|
34
40
|
};
|
|
35
41
|
});
|
|
36
42
|
</script>
|
|
@@ -13,4 +13,35 @@ describe('MStatusBadge component', () => {
|
|
|
13
13
|
|
|
14
14
|
expect(wrapper.text()).toContain('Badge Label');
|
|
15
15
|
});
|
|
16
|
+
|
|
17
|
+
describe('size', () => {
|
|
18
|
+
it('does not add a size modifier class when size is "m" (default)', () => {
|
|
19
|
+
const wrapper = mount(MStatusBadge, { props: { label: 'Badge' } });
|
|
20
|
+
expect(wrapper.classes()).not.toContain('mc-status-badge--m');
|
|
21
|
+
});
|
|
22
|
+
|
|
23
|
+
it('adds the "mc-status-badge--s" class when size is "s"', () => {
|
|
24
|
+
const wrapper = mount(MStatusBadge, {
|
|
25
|
+
props: { label: 'Badge', size: 's' },
|
|
26
|
+
});
|
|
27
|
+
expect(wrapper.classes()).toContain('mc-status-badge--s');
|
|
28
|
+
});
|
|
29
|
+
});
|
|
30
|
+
|
|
31
|
+
describe('status', () => {
|
|
32
|
+
it('does not add a status modifier class when status is "info" (default)', () => {
|
|
33
|
+
const wrapper = mount(MStatusBadge, { props: { label: 'Badge' } });
|
|
34
|
+
expect(wrapper.classes()).not.toContain('mc-status-badge--info');
|
|
35
|
+
});
|
|
36
|
+
|
|
37
|
+
it.each(['success', 'warning', 'error', 'neutral'] as const)(
|
|
38
|
+
'adds the "mc-status-badge--%s" class when status is "%s"',
|
|
39
|
+
(status) => {
|
|
40
|
+
const wrapper = mount(MStatusBadge, {
|
|
41
|
+
props: { label: 'Badge', status },
|
|
42
|
+
});
|
|
43
|
+
expect(wrapper.classes()).toContain(`mc-status-badge--${status}`);
|
|
44
|
+
},
|
|
45
|
+
);
|
|
46
|
+
});
|
|
16
47
|
});
|
|
@@ -8,6 +8,7 @@ A Status Badge is used to indicate the current status of an element, providing a
|
|
|
8
8
|
| Name | Description | Type | Default |
|
|
9
9
|
| --- | --- | --- | --- |
|
|
10
10
|
| `label*` | Content of the status badge | `string` | - |
|
|
11
|
+
| `size` | Allows to define the status badge size | `"s"` `"m"` | `"m"` |
|
|
11
12
|
| `status` | Allows to define the status badge type | `"info"` `"warning"` `"error"` `"success"` `"neutral"` | `"info"` |
|
|
12
13
|
|
|
13
14
|
## Dependencies
|
|
@@ -4,8 +4,8 @@ import MStatusMessage from './MStatusMessage.vue';
|
|
|
4
4
|
|
|
5
5
|
import {
|
|
6
6
|
InfoCircleFilled24,
|
|
7
|
-
|
|
8
|
-
|
|
7
|
+
WarningTriangleFilled24,
|
|
8
|
+
ErrorFilled24,
|
|
9
9
|
CheckCircleFilled24,
|
|
10
10
|
} from '@mozaic-ds/icons-vue';
|
|
11
11
|
|
|
@@ -56,7 +56,7 @@ describe('MStatusMessage.vue', () => {
|
|
|
56
56
|
props: { status: 'warning', label: 'Warning' },
|
|
57
57
|
});
|
|
58
58
|
|
|
59
|
-
expect(wrapper.findComponent(
|
|
59
|
+
expect(wrapper.findComponent(WarningTriangleFilled24).exists()).toBe(true);
|
|
60
60
|
});
|
|
61
61
|
|
|
62
62
|
it('renders the error icon when status is error', () => {
|
|
@@ -64,7 +64,7 @@ describe('MStatusMessage.vue', () => {
|
|
|
64
64
|
props: { status: 'error', label: 'Error' },
|
|
65
65
|
});
|
|
66
66
|
|
|
67
|
-
expect(wrapper.findComponent(
|
|
67
|
+
expect(wrapper.findComponent(ErrorFilled24).exists()).toBe(true);
|
|
68
68
|
});
|
|
69
69
|
|
|
70
70
|
it('adds correct modifier class for non-info statuses', () => {
|
|
@@ -15,8 +15,8 @@
|
|
|
15
15
|
import { computed } from 'vue';
|
|
16
16
|
import {
|
|
17
17
|
InfoCircleFilled24,
|
|
18
|
-
|
|
19
|
-
|
|
18
|
+
WarningTriangleFilled24,
|
|
19
|
+
ErrorFilled24,
|
|
20
20
|
CheckCircleFilled24,
|
|
21
21
|
} from '@mozaic-ds/icons-vue';
|
|
22
22
|
import MLoader from '../loader/MLoader.vue';
|
|
@@ -29,12 +29,7 @@ const props = withDefaults(
|
|
|
29
29
|
* Allows to define the status message style.
|
|
30
30
|
*/
|
|
31
31
|
status?:
|
|
32
|
-
| '
|
|
33
|
-
| 'success'
|
|
34
|
-
| 'warning'
|
|
35
|
-
| 'error'
|
|
36
|
-
| 'neutral'
|
|
37
|
-
| 'inprogress';
|
|
32
|
+
'info' | 'success' | 'warning' | 'error' | 'neutral' | 'inprogress';
|
|
38
33
|
/**
|
|
39
34
|
* Allows to define the status message appearance.
|
|
40
35
|
* The accent status is used to highlight a specific message.
|
|
@@ -64,9 +59,9 @@ const iconComponent = computed(() => {
|
|
|
64
59
|
case 'success':
|
|
65
60
|
return CheckCircleFilled24;
|
|
66
61
|
case 'warning':
|
|
67
|
-
return
|
|
62
|
+
return WarningTriangleFilled24;
|
|
68
63
|
case 'error':
|
|
69
|
-
return
|
|
64
|
+
return ErrorFilled24;
|
|
70
65
|
case 'info':
|
|
71
66
|
default:
|
|
72
67
|
return InfoCircleFilled24;
|
|
@@ -1,11 +1,11 @@
|
|
|
1
1
|
import { mount } from '@vue/test-utils';
|
|
2
|
-
import { describe,
|
|
2
|
+
import { describe, expect, it } from 'vitest';
|
|
3
3
|
import MStatusNotification from './MStatusNotification.vue';
|
|
4
4
|
import {
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
5
|
+
InfoCircleFilled24,
|
|
6
|
+
WarningTriangleFilled24,
|
|
7
|
+
ErrorFilled24,
|
|
8
|
+
CheckCircleFilled24,
|
|
9
9
|
} from '@mozaic-ds/icons-vue';
|
|
10
10
|
|
|
11
11
|
describe('MStatusNotification.vue', () => {
|
|
@@ -19,7 +19,7 @@ describe('MStatusNotification.vue', () => {
|
|
|
19
19
|
|
|
20
20
|
expect(wrapper.text()).toContain('Test Title');
|
|
21
21
|
expect(wrapper.text()).toContain('Test Description');
|
|
22
|
-
expect(wrapper.findComponent(
|
|
22
|
+
expect(wrapper.findComponent(InfoCircleFilled24).exists()).toBe(true);
|
|
23
23
|
expect(wrapper.classes()).toContain('mc-status-notification');
|
|
24
24
|
});
|
|
25
25
|
|
|
@@ -31,7 +31,7 @@ describe('MStatusNotification.vue', () => {
|
|
|
31
31
|
status: 'success',
|
|
32
32
|
},
|
|
33
33
|
});
|
|
34
|
-
expect(wrapperSuccess.findComponent(
|
|
34
|
+
expect(wrapperSuccess.findComponent(CheckCircleFilled24).exists()).toBe(
|
|
35
35
|
true,
|
|
36
36
|
);
|
|
37
37
|
|
|
@@ -42,7 +42,7 @@ describe('MStatusNotification.vue', () => {
|
|
|
42
42
|
status: 'warning',
|
|
43
43
|
},
|
|
44
44
|
});
|
|
45
|
-
expect(wrapperWarning.findComponent(
|
|
45
|
+
expect(wrapperWarning.findComponent(WarningTriangleFilled24).exists()).toBe(
|
|
46
46
|
true,
|
|
47
47
|
);
|
|
48
48
|
|
|
@@ -53,7 +53,7 @@ describe('MStatusNotification.vue', () => {
|
|
|
53
53
|
status: 'error',
|
|
54
54
|
},
|
|
55
55
|
});
|
|
56
|
-
expect(wrapperError.findComponent(
|
|
56
|
+
expect(wrapperError.findComponent(ErrorFilled24).exists()).toBe(true);
|
|
57
57
|
});
|
|
58
58
|
|
|
59
59
|
it('should show the close button if closable prop is true', () => {
|
|
@@ -36,12 +36,13 @@
|
|
|
36
36
|
import { computed, type VNode } from 'vue';
|
|
37
37
|
import {
|
|
38
38
|
Cross20,
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
39
|
+
InfoCircleFilled24,
|
|
40
|
+
WarningTriangleFilled24,
|
|
41
|
+
ErrorFilled24,
|
|
42
|
+
CheckCircleFilled24,
|
|
43
43
|
} from '@mozaic-ds/icons-vue';
|
|
44
44
|
import MIconButton from '../iconbutton/MIconButton.vue';
|
|
45
|
+
|
|
45
46
|
/**
|
|
46
47
|
* A Status Notification is used to draw the user’s attention to important information that needs to be acknowledged. It often provides feedback on a process, highlights a status update, or alerts users about an issue. Notifications are typically triggered by user actions or system events and are designed to be easily noticeable while maintaining a non-intrusive experience.
|
|
47
48
|
*/
|
|
@@ -86,14 +87,14 @@ const classObject = computed(() => {
|
|
|
86
87
|
const iconComponent = computed(() => {
|
|
87
88
|
switch (props.status) {
|
|
88
89
|
case 'success':
|
|
89
|
-
return
|
|
90
|
+
return CheckCircleFilled24;
|
|
90
91
|
case 'warning':
|
|
91
|
-
return
|
|
92
|
+
return WarningTriangleFilled24;
|
|
92
93
|
case 'error':
|
|
93
|
-
return
|
|
94
|
+
return ErrorFilled24;
|
|
94
95
|
case 'info':
|
|
95
96
|
default:
|
|
96
|
-
return
|
|
97
|
+
return InfoCircleFilled24;
|
|
97
98
|
}
|
|
98
99
|
});
|
|
99
100
|
|
|
@@ -2,7 +2,7 @@ import type { Meta, StoryObj } from '@storybook/vue3-vite';
|
|
|
2
2
|
import { action } from 'storybook/actions';
|
|
3
3
|
|
|
4
4
|
import Mtabs from './MTabs.vue';
|
|
5
|
-
import { ChevronRight24 } from '@mozaic-ds/icons-vue';
|
|
5
|
+
import { ChevronRight20, ChevronRight24 } from '@mozaic-ds/icons-vue';
|
|
6
6
|
|
|
7
7
|
const meta: Meta<typeof Mtabs> = {
|
|
8
8
|
title: 'Navigation/Tabs',
|
|
@@ -17,6 +17,7 @@ const meta: Meta<typeof Mtabs> = {
|
|
|
17
17
|
},
|
|
18
18
|
args: {
|
|
19
19
|
modelValue: 'label1',
|
|
20
|
+
size: 'm',
|
|
20
21
|
tabs: [
|
|
21
22
|
{
|
|
22
23
|
id: 'label1',
|
|
@@ -57,6 +58,34 @@ type Story = StoryObj<typeof Mtabs>;
|
|
|
57
58
|
|
|
58
59
|
export const Default: Story = {};
|
|
59
60
|
|
|
61
|
+
export const Small: Story = {
|
|
62
|
+
args: {
|
|
63
|
+
size: 's',
|
|
64
|
+
tabs: [
|
|
65
|
+
{
|
|
66
|
+
id: 'label1',
|
|
67
|
+
label: 'Label',
|
|
68
|
+
icon: ChevronRight20,
|
|
69
|
+
},
|
|
70
|
+
{
|
|
71
|
+
id: 'label2',
|
|
72
|
+
label: 'Label',
|
|
73
|
+
icon: ChevronRight20,
|
|
74
|
+
},
|
|
75
|
+
{
|
|
76
|
+
id: 'label3',
|
|
77
|
+
label: 'Label',
|
|
78
|
+
icon: ChevronRight20,
|
|
79
|
+
},
|
|
80
|
+
{
|
|
81
|
+
id: 'label4',
|
|
82
|
+
label: 'Label',
|
|
83
|
+
icon: ChevronRight20,
|
|
84
|
+
},
|
|
85
|
+
],
|
|
86
|
+
},
|
|
87
|
+
};
|
|
88
|
+
|
|
60
89
|
export const Icons: Story = {
|
|
61
90
|
args: {
|
|
62
91
|
tabs: [
|
|
@@ -23,9 +23,12 @@
|
|
|
23
23
|
@click="onClickTab(index, tab.id)"
|
|
24
24
|
@keydown="onKeydown($event, index)"
|
|
25
25
|
>
|
|
26
|
-
<
|
|
27
|
-
|
|
28
|
-
|
|
26
|
+
<component
|
|
27
|
+
v-if="tab.icon"
|
|
28
|
+
:is="tab.icon"
|
|
29
|
+
class="mc-tabs__icon"
|
|
30
|
+
aria-hidden="true"
|
|
31
|
+
/>
|
|
29
32
|
<div class="mc-tabs__label">
|
|
30
33
|
<span>{{ tab.label }}</span>
|
|
31
34
|
</div>
|
|
@@ -52,6 +55,10 @@ const props = withDefaults(
|
|
|
52
55
|
* A description indicating the purpose of the set of tabs. Useful for improving the accessibility of the component.
|
|
53
56
|
*/
|
|
54
57
|
description?: string;
|
|
58
|
+
/**
|
|
59
|
+
* Defines the size of the tab.
|
|
60
|
+
*/
|
|
61
|
+
size?: 's' | 'm';
|
|
55
62
|
/**
|
|
56
63
|
* If `true`, the divider will appear.
|
|
57
64
|
*/
|
|
@@ -95,6 +102,7 @@ const props = withDefaults(
|
|
|
95
102
|
}>(),
|
|
96
103
|
{
|
|
97
104
|
modelValue: 0,
|
|
105
|
+
size: 'm',
|
|
98
106
|
divider: true,
|
|
99
107
|
},
|
|
100
108
|
);
|
|
@@ -102,6 +110,7 @@ const props = withDefaults(
|
|
|
102
110
|
const classObject = computed(() => {
|
|
103
111
|
return {
|
|
104
112
|
'mc-tabs--centered': props.centered,
|
|
113
|
+
[`mc-tabs--${props.size}`]: props.size !== 'm',
|
|
105
114
|
};
|
|
106
115
|
});
|
|
107
116
|
|
|
@@ -103,6 +103,25 @@ describe('MTabs.vue', () => {
|
|
|
103
103
|
});
|
|
104
104
|
});
|
|
105
105
|
|
|
106
|
+
it('does not add size class when size is "m" (default)', () => {
|
|
107
|
+
const wrapper = mount(MTabs, { props: { tabs } });
|
|
108
|
+
expect(wrapper.classes()).not.toContain('mc-tabs--m');
|
|
109
|
+
expect(wrapper.classes()).not.toContain('mc-tabs--s');
|
|
110
|
+
});
|
|
111
|
+
|
|
112
|
+
it('adds mc-tabs--s class when size is "s" and removes it when changed to "m"', async () => {
|
|
113
|
+
const wrapper = mount(MTabs, {
|
|
114
|
+
props: { tabs, size: 's' },
|
|
115
|
+
});
|
|
116
|
+
|
|
117
|
+
expect(wrapper.classes()).toContain('mc-tabs--s');
|
|
118
|
+
|
|
119
|
+
await wrapper.setProps({ size: 'm' });
|
|
120
|
+
|
|
121
|
+
expect(wrapper.classes()).not.toContain('mc-tabs--s');
|
|
122
|
+
expect(wrapper.classes()).not.toContain('mc-tabs--m');
|
|
123
|
+
});
|
|
124
|
+
|
|
106
125
|
it('adds divider and centered classes based on props', async () => {
|
|
107
126
|
const wrapper = mount(MTabs, {
|
|
108
127
|
props: {
|
|
@@ -8,6 +8,7 @@ Tabs are a navigation component that allows users to switch between different se
|
|
|
8
8
|
| Name | Description | Type | Default |
|
|
9
9
|
| --- | --- | --- | --- |
|
|
10
10
|
| `description` | A description indicating the purpose of the set of tabs. Useful for improving the accessibility of the component. | `string` | - |
|
|
11
|
+
| `size` | Defines the size of the tab. | `"s"` `"m"` | `"m"` |
|
|
11
12
|
| `divider` | If `true`, the divider will appear. | `boolean` | `true` |
|
|
12
13
|
| `centered` | If `true`, the tabs of the component will be centered. | `boolean` | - |
|
|
13
14
|
| `modelValue` | Defines the currently active tab.
|
|
@@ -174,4 +174,73 @@ describe('MTileSelectable', () => {
|
|
|
174
174
|
|
|
175
175
|
expect(wrapper.find('.mc-tile__content').exists()).toBe(false);
|
|
176
176
|
});
|
|
177
|
+
|
|
178
|
+
it('emits update:modelValue when card is clicked and clickable is true', async () => {
|
|
179
|
+
const wrapper = mount(MTileSelectable, {
|
|
180
|
+
props: {
|
|
181
|
+
modelValue: false,
|
|
182
|
+
trigger: 'container',
|
|
183
|
+
},
|
|
184
|
+
});
|
|
185
|
+
|
|
186
|
+
await wrapper.find('.mc-tile').trigger('click');
|
|
187
|
+
|
|
188
|
+
expect(wrapper.emitted('update:modelValue')).toBeTruthy();
|
|
189
|
+
expect(wrapper.emitted('update:modelValue')![0]).toEqual([true]);
|
|
190
|
+
});
|
|
191
|
+
|
|
192
|
+
it('does not emit update:modelValue when card is clicked but clickable is false', async () => {
|
|
193
|
+
const wrapper = mount(MTileSelectable, {
|
|
194
|
+
props: {
|
|
195
|
+
modelValue: false,
|
|
196
|
+
},
|
|
197
|
+
});
|
|
198
|
+
|
|
199
|
+
await wrapper.find('.mc-tile').trigger('click');
|
|
200
|
+
|
|
201
|
+
expect(wrapper.emitted('update:modelValue')).toBeFalsy();
|
|
202
|
+
});
|
|
203
|
+
|
|
204
|
+
it('toggles off when card is clicked and already selected with clickable and checkbox', async () => {
|
|
205
|
+
const wrapper = mount(MTileSelectable, {
|
|
206
|
+
props: {
|
|
207
|
+
modelValue: true,
|
|
208
|
+
trigger: 'container',
|
|
209
|
+
inputType: 'checkbox',
|
|
210
|
+
},
|
|
211
|
+
});
|
|
212
|
+
|
|
213
|
+
await wrapper.find('.mc-tile').trigger('click');
|
|
214
|
+
|
|
215
|
+
expect(wrapper.emitted('update:modelValue')![0]).toEqual([false]);
|
|
216
|
+
});
|
|
217
|
+
|
|
218
|
+
it('always sets true when card is clicked with clickable and radio', async () => {
|
|
219
|
+
const wrapper = mount(MTileSelectable, {
|
|
220
|
+
props: {
|
|
221
|
+
modelValue: false,
|
|
222
|
+
trigger: 'container',
|
|
223
|
+
inputType: 'radio',
|
|
224
|
+
},
|
|
225
|
+
});
|
|
226
|
+
|
|
227
|
+
await wrapper.find('.mc-tile').trigger('click');
|
|
228
|
+
|
|
229
|
+
expect(wrapper.emitted('update:modelValue')![0]).toEqual([true]);
|
|
230
|
+
});
|
|
231
|
+
|
|
232
|
+
it('does not double-toggle when the input itself is clicked with clickable enabled', async () => {
|
|
233
|
+
const wrapper = mount(MTileSelectable, {
|
|
234
|
+
props: {
|
|
235
|
+
modelValue: false,
|
|
236
|
+
trigger: 'container',
|
|
237
|
+
inputType: 'checkbox',
|
|
238
|
+
},
|
|
239
|
+
});
|
|
240
|
+
|
|
241
|
+
const input = wrapper.find('input[type="checkbox"]');
|
|
242
|
+
await input.trigger('click');
|
|
243
|
+
// Clicking the input itself should not invoke the card click handler
|
|
244
|
+
expect(wrapper.emitted('update:modelValue')).toBeFalsy();
|
|
245
|
+
});
|
|
177
246
|
});
|
|
@@ -42,14 +42,20 @@ type Story = StoryObj<typeof MTileSelectable>;
|
|
|
42
42
|
|
|
43
43
|
export const Default: Story = {};
|
|
44
44
|
|
|
45
|
-
export const
|
|
45
|
+
export const Radio: Story = {
|
|
46
46
|
args: {
|
|
47
47
|
inputType: 'radio',
|
|
48
48
|
},
|
|
49
49
|
};
|
|
50
50
|
|
|
51
|
-
export const
|
|
51
|
+
export const Left: Story = {
|
|
52
52
|
args: {
|
|
53
53
|
inputPosition: 'left',
|
|
54
54
|
},
|
|
55
55
|
};
|
|
56
|
+
|
|
57
|
+
export const ClickableContainer: Story = {
|
|
58
|
+
args: {
|
|
59
|
+
trigger: 'container',
|
|
60
|
+
},
|
|
61
|
+
};
|
|
@@ -8,7 +8,11 @@
|
|
|
8
8
|
'mc-tile--input-center': props.centered,
|
|
9
9
|
'mc-tile--no-input': props.hideInput,
|
|
10
10
|
}"
|
|
11
|
-
v-bind="
|
|
11
|
+
v-bind="
|
|
12
|
+
(props.hideInput || props.trigger === 'container') && {
|
|
13
|
+
onClick: handleCardClick,
|
|
14
|
+
}
|
|
15
|
+
"
|
|
12
16
|
>
|
|
13
17
|
<div v-if="$slots.details" class="mc-tile__header">
|
|
14
18
|
<div class="mc-tile__body">
|
|
@@ -22,6 +26,7 @@
|
|
|
22
26
|
:id="`checkbox-${id}`"
|
|
23
27
|
name="tile-select-group"
|
|
24
28
|
aria-label="Tile checkbox"
|
|
29
|
+
@click.stop
|
|
25
30
|
/>
|
|
26
31
|
|
|
27
32
|
<MRadio
|
|
@@ -30,6 +35,7 @@
|
|
|
30
35
|
:id="`radio-${id}`"
|
|
31
36
|
name="tile-select-group"
|
|
32
37
|
aria-label="Tile radio button"
|
|
38
|
+
@click.stop
|
|
33
39
|
/>
|
|
34
40
|
</div>
|
|
35
41
|
</div>
|
|
@@ -46,6 +52,7 @@
|
|
|
46
52
|
:id="`checkbox-${id}`"
|
|
47
53
|
name="tile-select-group"
|
|
48
54
|
aria-label="Tile checkbox"
|
|
55
|
+
@click.stop
|
|
49
56
|
/>
|
|
50
57
|
|
|
51
58
|
<MRadio
|
|
@@ -54,6 +61,7 @@
|
|
|
54
61
|
:id="`radio-${id}`"
|
|
55
62
|
name="tile-select-group"
|
|
56
63
|
aria-label="Tile radio button"
|
|
64
|
+
@click.stop
|
|
57
65
|
/>
|
|
58
66
|
</div>
|
|
59
67
|
</template>
|
|
@@ -99,6 +107,10 @@ const props = withDefaults(
|
|
|
99
107
|
* If `true`, hides the input and makes the whole tile selectable.
|
|
100
108
|
*/
|
|
101
109
|
hideInput?: boolean;
|
|
110
|
+
/**
|
|
111
|
+
* Determines how the tile expansion is triggered.
|
|
112
|
+
*/
|
|
113
|
+
trigger?: 'container' | 'input';
|
|
102
114
|
}>(),
|
|
103
115
|
{
|
|
104
116
|
inputType: 'checkbox',
|
|
@@ -135,6 +147,11 @@ const checked = computed({
|
|
|
135
147
|
emit('update:modelValue', value);
|
|
136
148
|
},
|
|
137
149
|
});
|
|
150
|
+
|
|
151
|
+
function handleCardClick() {
|
|
152
|
+
if (props.inputType === 'radio' && checked.value) return;
|
|
153
|
+
checked.value = !checked.value;
|
|
154
|
+
}
|
|
138
155
|
</script>
|
|
139
156
|
|
|
140
157
|
<style lang="scss" scoped>
|
|
@@ -13,6 +13,7 @@ A tile is a container component used to group related content and actions within
|
|
|
13
13
|
| `inputPosition` | Position of the input relative to the tile content. | `"left"` `"right"` | `"right"` |
|
|
14
14
|
| `centered` | Vertical alignment of the input. | `boolean` | - |
|
|
15
15
|
| `hideInput` | If `true`, hides the input and makes the whole tile selectable. | `boolean` | - |
|
|
16
|
+
| `trigger` | Determines how the tile expansion is triggered. | `"input"` `"container"` | - |
|
|
16
17
|
|
|
17
18
|
## Slots
|
|
18
19
|
|
package/src/main.ts
CHANGED
|
@@ -32,8 +32,11 @@ export { default as MLoadingOverlay } from './components/loadingoverlay/MLoading
|
|
|
32
32
|
export { default as MModal } from './components/modal/MModal.vue';
|
|
33
33
|
export { default as MNavigationIndicator } from './components/navigationindicator/MNavigationIndicator.vue';
|
|
34
34
|
export { default as MNumberBadge } from './components/numberbadge/MNumberBadge.vue';
|
|
35
|
+
export { default as MOptionListbox } from './components/optionListbox/MOptionListbox.vue';
|
|
35
36
|
export { default as MOverlay } from './components/overlay/MOverlay.vue';
|
|
36
37
|
export { default as MPageHeader } from './components/pageheader/MPageHeader.vue';
|
|
38
|
+
export { default as MPageHeaderContext } from './components/pageheadercontext/MPageHeaderContext.vue';
|
|
39
|
+
export { default as MPageHeaderFeature } from './components/pageheaderfeature/MPageHeaderFeature.vue';
|
|
37
40
|
export { default as MPagination } from './components/pagination/MPagination.vue';
|
|
38
41
|
export { default as MPasswordInput } from './components/passwordinput/MPasswordInput.vue';
|
|
39
42
|
export { default as MPhoneNumber } from './components/phonenumber/MPhoneNumber.vue';
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
export function debounce<T extends (...args: unknown[]) => unknown>(
|
|
2
|
+
fn: T,
|
|
3
|
+
wait: number,
|
|
4
|
+
): (...args: Parameters<T>) => void {
|
|
5
|
+
let timeoutId: ReturnType<typeof setTimeout> | null = null;
|
|
6
|
+
|
|
7
|
+
return function (this: unknown, ...args: Parameters<T>) {
|
|
8
|
+
if (timeoutId) {
|
|
9
|
+
clearTimeout(timeoutId);
|
|
10
|
+
}
|
|
11
|
+
timeoutId = setTimeout(() => {
|
|
12
|
+
fn.apply(this, args);
|
|
13
|
+
}, wait);
|
|
14
|
+
};
|
|
15
|
+
}
|