@mozaic-ds/vue 2.13.0 → 2.14.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 +1088 -378
- package/dist/mozaic-vue.js +2662 -1854
- package/dist/mozaic-vue.js.map +1 -1
- package/dist/mozaic-vue.umd.cjs +5 -5
- package/dist/mozaic-vue.umd.cjs.map +1 -1
- package/package.json +4 -4
- package/src/components/actionlistbox/MActionListbox.spec.ts +53 -59
- package/src/components/actionlistbox/MActionListbox.stories.ts +22 -1
- package/src/components/actionlistbox/MActionListbox.vue +91 -28
- package/src/components/actionlistbox/README.md +15 -0
- package/src/components/breadcrumb/MBreadcrumb.vue +5 -0
- package/src/components/button/README.md +4 -0
- package/src/components/checkbox/README.md +2 -0
- package/src/components/divider/README.md +4 -0
- package/src/components/iconbutton/MIconButton.stories.ts +12 -0
- package/src/components/iconbutton/MIconButton.vue +13 -1
- package/src/components/iconbutton/README.md +27 -0
- package/src/components/loader/README.md +2 -0
- package/src/components/navigationindicator/MNavigationIndicator.spec.ts +152 -0
- package/src/components/navigationindicator/MNavigationIndicator.stories.ts +41 -0
- package/src/components/navigationindicator/MNavigationIndicator.vue +132 -0
- package/src/components/navigationindicator/README.md +37 -0
- package/src/components/pageheader/MPageHeader.spec.ts +142 -0
- package/src/components/pageheader/MPageHeader.stories.ts +125 -0
- package/src/components/pageheader/MPageHeader.vue +133 -0
- package/src/components/pageheader/README.md +46 -0
- package/src/components/popover/MPopover.spec.ts +106 -0
- package/src/components/popover/MPopover.stories.ts +126 -0
- package/src/components/popover/MPopover.vue +131 -0
- package/src/components/popover/README.md +42 -0
- package/src/components/radio/README.md +2 -0
- package/src/components/select/MSelect.spec.ts +2 -1
- package/src/components/select/MSelect.vue +30 -25
- package/src/components/sidebar/MSidebar.const.ts +6 -0
- package/src/components/sidebar/MSidebar.spec.ts +110 -0
- package/src/components/sidebar/MSidebar.stories.ts +108 -0
- package/src/components/sidebar/MSidebar.vue +124 -0
- package/src/components/sidebar/README.md +59 -0
- package/src/components/sidebar/stories/DefaultCase.stories.vue +120 -0
- package/src/components/sidebar/stories/README.md +27 -0
- package/src/components/sidebar/stories/WithExpandOnly.stories.vue +112 -0
- package/src/components/sidebar/stories/WithProfileInfoOnly.stories.vue +119 -0
- package/src/components/sidebar/stories/WithSingleLevel.stories.vue +98 -0
- package/src/components/sidebar/use-floating-item.composable.ts +135 -0
- package/src/components/sidebar/use-floating-item.spec.ts +251 -0
- package/src/components/sidebarexpandableitem/MSidebarExpandableItem.spec.ts +151 -0
- package/src/components/sidebarexpandableitem/MSidebarExpandableItem.vue +113 -0
- package/src/components/sidebarexpandableitem/README.md +36 -0
- package/src/components/sidebarfooter/MSidebarFooter.spec.ts +276 -0
- package/src/components/sidebarfooter/MSidebarFooter.vue +201 -0
- package/src/components/sidebarfooter/README.md +52 -0
- package/src/components/sidebarfooter/_MSidebarFooterMenu.vue +64 -0
- package/src/components/sidebarheader/MSidebarHeader.vue +36 -0
- package/src/components/sidebarheader/README.md +31 -0
- package/src/components/sidebarnavitem/MSidebarNavItem.spec.ts +127 -0
- package/src/components/sidebarnavitem/MSidebarNavItem.vue +113 -0
- package/src/components/sidebarnavitem/README.md +56 -0
- package/src/components/sidebarshortcutitem/MSidebarShortcutItem.spec.ts +59 -0
- package/src/components/sidebarshortcutitem/MSidebarShortcutItem.vue +52 -0
- package/src/components/sidebarshortcutitem/README.md +32 -0
- package/src/components/sidebarshortcuts/MSidebarShortcuts.spec.ts +87 -0
- package/src/components/sidebarshortcuts/MSidebarShortcuts.vue +101 -0
- package/src/components/sidebarshortcuts/README.md +36 -0
- package/src/components/statusbadge/README.md +12 -0
- package/src/components/textinput/MTextInput.stories.ts +13 -1
- package/src/components/textinput/MTextInput.vue +12 -0
- package/src/components/textinput/README.md +3 -1
- package/src/components/tile/MTile.spec.ts +61 -0
- package/src/components/tile/MTile.stories.ts +102 -0
- package/src/components/tile/MTile.vue +68 -0
- package/src/components/tile/README.md +19 -0
- package/src/components/tileclickable/MTileClickable.spec.ts +130 -0
- package/src/components/tileclickable/MTileClickable.stories.ts +60 -0
- package/src/components/tileclickable/MTileClickable.vue +106 -0
- package/src/components/tileclickable/README.md +30 -0
- package/src/components/tileexpandable/MTileExpandable.spec.ts +121 -0
- package/src/components/tileexpandable/MTileExpandable.stories.ts +50 -0
- package/src/components/tileexpandable/MTileExpandable.vue +131 -0
- package/src/components/tileexpandable/README.md +36 -0
- package/src/components/tileselectable/MTileSelectable.spec.ts +177 -0
- package/src/components/tileselectable/MTileSelectable.stories.ts +55 -0
- package/src/components/tileselectable/MTileSelectable.vue +142 -0
- package/src/components/tileselectable/README.md +44 -0
- package/src/components/toaster/README.md +1 -1
- package/src/components/tooltip/MTooltip.vue +5 -0
- package/src/components/tooltip/README.md +16 -1
- package/src/main.ts +12 -2
- package/src/utils/use-is-mobile.composable.ts +20 -0
- package/src/utils/use-is-mobile.spec.ts +70 -0
|
@@ -0,0 +1,133 @@
|
|
|
1
|
+
<template>
|
|
2
|
+
<div
|
|
3
|
+
:class="{
|
|
4
|
+
'mc-page-header': true,
|
|
5
|
+
'mc-page-header--with-shadow': shadow,
|
|
6
|
+
}"
|
|
7
|
+
>
|
|
8
|
+
<div class="mc-page-header__top-wrapper">
|
|
9
|
+
<div class="mc-page-header__top-content">
|
|
10
|
+
<MIconButton
|
|
11
|
+
v-if="backButton"
|
|
12
|
+
ghost
|
|
13
|
+
aria-label="Back button"
|
|
14
|
+
@click="emit('back')"
|
|
15
|
+
>
|
|
16
|
+
<template #icon>
|
|
17
|
+
<ArrowBack24 />
|
|
18
|
+
</template>
|
|
19
|
+
</MIconButton>
|
|
20
|
+
|
|
21
|
+
<div class="mc-page-header__content-wrapper">
|
|
22
|
+
<span class="mc-page-header__title">
|
|
23
|
+
{{ title }}
|
|
24
|
+
</span>
|
|
25
|
+
|
|
26
|
+
<div
|
|
27
|
+
v-if="status || extraInfo"
|
|
28
|
+
class="mc-page-header__info-wrapper"
|
|
29
|
+
>
|
|
30
|
+
<MStatusBadge
|
|
31
|
+
v-if="statusLabel && status"
|
|
32
|
+
:label="statusLabel"
|
|
33
|
+
:status="status"
|
|
34
|
+
/>
|
|
35
|
+
|
|
36
|
+
<span v-if="extraInfo" class="mc-page-header__extra-info">
|
|
37
|
+
{{ extraInfo }}
|
|
38
|
+
</span>
|
|
39
|
+
</div>
|
|
40
|
+
|
|
41
|
+
<slot name="content" />
|
|
42
|
+
</div>
|
|
43
|
+
</div>
|
|
44
|
+
|
|
45
|
+
<div class="mc-page-header__actions-wrapper">
|
|
46
|
+
<MIconButton
|
|
47
|
+
class="mc-page-header__burger-menu"
|
|
48
|
+
ghost
|
|
49
|
+
aria-label="Burger menu"
|
|
50
|
+
@click="emit('toggle-menu')"
|
|
51
|
+
>
|
|
52
|
+
<template #icon>
|
|
53
|
+
<Menu24 />
|
|
54
|
+
</template>
|
|
55
|
+
</MIconButton>
|
|
56
|
+
|
|
57
|
+
<div class="mc-page-header__actions-content">
|
|
58
|
+
<slot name="actions"/>
|
|
59
|
+
</div>
|
|
60
|
+
</div>
|
|
61
|
+
</div>
|
|
62
|
+
|
|
63
|
+
<div class="mc-page-header__tabs">
|
|
64
|
+
<slot name="tabs"/>
|
|
65
|
+
</div>
|
|
66
|
+
</div>
|
|
67
|
+
</template>
|
|
68
|
+
|
|
69
|
+
<script setup lang="ts">
|
|
70
|
+
import { type VNode } from 'vue';
|
|
71
|
+
import { ArrowBack24, Menu24 } from '@mozaic-ds/icons-vue';
|
|
72
|
+
import MIconButton from '../iconbutton/MIconButton.vue';
|
|
73
|
+
import MStatusBadge from '../statusbadge/MStatusBadge.vue';
|
|
74
|
+
/**
|
|
75
|
+
* The Page Header is a fundamental component that structures the top part of an interface, serving as a cognitive anchor point for users. It establishes page context, facilitates navigation, and provides the main actions available within the current scope.
|
|
76
|
+
*/
|
|
77
|
+
defineProps<{
|
|
78
|
+
/**
|
|
79
|
+
* Main title displayed in the page header.
|
|
80
|
+
*/
|
|
81
|
+
title: string;
|
|
82
|
+
/**
|
|
83
|
+
* Enables a drop shadow under the page header.
|
|
84
|
+
*/
|
|
85
|
+
shadow?: boolean;
|
|
86
|
+
/**
|
|
87
|
+
* Displays a back icon button in the header.
|
|
88
|
+
*/
|
|
89
|
+
backButton?: boolean;
|
|
90
|
+
/**
|
|
91
|
+
* Component status.
|
|
92
|
+
*/
|
|
93
|
+
status?: 'info' | 'success' | 'warning' | 'error' | 'neutral';
|
|
94
|
+
/**
|
|
95
|
+
* Label text displayed inside the status badge.
|
|
96
|
+
*/
|
|
97
|
+
statusLabel?: string;
|
|
98
|
+
/**
|
|
99
|
+
* Additional information text shown below the title.
|
|
100
|
+
*/
|
|
101
|
+
extraInfo?: string;
|
|
102
|
+
}>();
|
|
103
|
+
|
|
104
|
+
const emit = defineEmits<{
|
|
105
|
+
/**
|
|
106
|
+
* Emits when the back button is clicked.
|
|
107
|
+
*/
|
|
108
|
+
(on: 'back'): void;
|
|
109
|
+
/**
|
|
110
|
+
* Emits when the burger menu button is clicked.
|
|
111
|
+
*/
|
|
112
|
+
(on: 'toggle-menu'): void;
|
|
113
|
+
}>();
|
|
114
|
+
|
|
115
|
+
defineSlots<{
|
|
116
|
+
/**
|
|
117
|
+
* Use this slot to insert action items in the top content.
|
|
118
|
+
*/
|
|
119
|
+
actions?: VNode;
|
|
120
|
+
/**
|
|
121
|
+
* Use this slot to insert any content below the Page Header top content.
|
|
122
|
+
*/
|
|
123
|
+
content?: VNode;
|
|
124
|
+
/**
|
|
125
|
+
* Use this slot to insert a tab in the header.
|
|
126
|
+
*/
|
|
127
|
+
tabs?: VNode;
|
|
128
|
+
}>();
|
|
129
|
+
</script>
|
|
130
|
+
|
|
131
|
+
<style lang="scss" scoped>
|
|
132
|
+
@use '@mozaic-ds/styles/components/page-header';
|
|
133
|
+
</style>
|
|
@@ -0,0 +1,46 @@
|
|
|
1
|
+
# MPageHeader
|
|
2
|
+
|
|
3
|
+
The Page Header is a fundamental component that structures the top part of an interface, serving as a cognitive anchor point for users. It establishes page context, facilitates navigation, and provides the main actions available within the current scope.
|
|
4
|
+
|
|
5
|
+
|
|
6
|
+
## Props
|
|
7
|
+
|
|
8
|
+
| Name | Description | Type | Default |
|
|
9
|
+
| --- | --- | --- | --- |
|
|
10
|
+
| `title*` | Main title displayed in the page header. | `string` | - |
|
|
11
|
+
| `shadow` | Enables a drop shadow under the page header. | `boolean` | - |
|
|
12
|
+
| `backButton` | Displays a back icon button in the header. | `boolean` | - |
|
|
13
|
+
| `status` | Component status. | `"info"` `"warning"` `"error"` `"success"` `"neutral"` | - |
|
|
14
|
+
| `statusLabel` | Label text displayed inside the status badge. | `string` | - |
|
|
15
|
+
| `extraInfo` | Additional information text shown below the title. | `string` | - |
|
|
16
|
+
|
|
17
|
+
## Slots
|
|
18
|
+
|
|
19
|
+
| Name | Description |
|
|
20
|
+
| --- | --- |
|
|
21
|
+
| `actions` | Use this slot to insert action items in the top content. |
|
|
22
|
+
| `content` | Use this slot to insert any content below the Page Header top content. |
|
|
23
|
+
| `tabs` | Use this slot to insert a tab in the header. |
|
|
24
|
+
|
|
25
|
+
## Events
|
|
26
|
+
|
|
27
|
+
| Name | Description | Type |
|
|
28
|
+
| --- | --- | --- |
|
|
29
|
+
| `back` | Emits when the back button is clicked. | [] |
|
|
30
|
+
| `toggle-menu` | Emits when the burger menu button is clicked. | [] |
|
|
31
|
+
|
|
32
|
+
## Dependencies
|
|
33
|
+
|
|
34
|
+
### Depends on
|
|
35
|
+
|
|
36
|
+
- [MIconButton](../iconbutton)
|
|
37
|
+
- [MStatusBadge](../statusbadge)
|
|
38
|
+
|
|
39
|
+
### Graph
|
|
40
|
+
|
|
41
|
+
```mermaid
|
|
42
|
+
graph TD;
|
|
43
|
+
MPageHeader --> MIconButton
|
|
44
|
+
MPageHeader --> MStatusBadge
|
|
45
|
+
style MPageHeader fill:#008240,stroke:#333,stroke-width:4px
|
|
46
|
+
```
|
|
@@ -0,0 +1,106 @@
|
|
|
1
|
+
import { h } from 'vue';
|
|
2
|
+
import { mount } from '@vue/test-utils';
|
|
3
|
+
import { describe, it, expect } from 'vitest';
|
|
4
|
+
import MPopover from './MPopover.vue';
|
|
5
|
+
import MIconButton from '../iconbutton/MIconButton.vue';
|
|
6
|
+
|
|
7
|
+
describe('MPopover.vue', () => {
|
|
8
|
+
it('applies classes based on props', () => {
|
|
9
|
+
const wrapper = mount(MPopover, {
|
|
10
|
+
props: {
|
|
11
|
+
appearance: 'inverse',
|
|
12
|
+
position: 'right',
|
|
13
|
+
pointer: false,
|
|
14
|
+
size: 'l',
|
|
15
|
+
},
|
|
16
|
+
});
|
|
17
|
+
|
|
18
|
+
const rootDiv = wrapper.find('.mc-popover');
|
|
19
|
+
expect(rootDiv.exists()).toBe(true);
|
|
20
|
+
expect(rootDiv.classes()).toContain('mc-popover');
|
|
21
|
+
expect(rootDiv.classes()).toContain('mc-popover--inverse');
|
|
22
|
+
expect(rootDiv.classes()).toContain('mc-popover--right');
|
|
23
|
+
expect(rootDiv.classes()).toContain('mc-popover--no-pointer');
|
|
24
|
+
expect(rootDiv.classes()).toContain('mc-popover--l');
|
|
25
|
+
});
|
|
26
|
+
|
|
27
|
+
it('sets aria-labelledby and aria-describedby when title/description provided', () => {
|
|
28
|
+
const wrapper = mount(MPopover, {
|
|
29
|
+
props: { title: 'My title', description: 'My description' },
|
|
30
|
+
});
|
|
31
|
+
|
|
32
|
+
const wrapperEl = wrapper.find('.mc-popover__wrapper');
|
|
33
|
+
const id = wrapperEl.attributes('id');
|
|
34
|
+
expect(id).toBeTruthy();
|
|
35
|
+
expect(wrapperEl.attributes('aria-labelledby')).toBe(`${id}-title`);
|
|
36
|
+
expect(wrapperEl.attributes('aria-describedby')).toBe(`${id}-description`);
|
|
37
|
+
});
|
|
38
|
+
|
|
39
|
+
it('renders title and description', () => {
|
|
40
|
+
const wrapper = mount(MPopover, {
|
|
41
|
+
props: { title: 'T', description: 'D' },
|
|
42
|
+
});
|
|
43
|
+
|
|
44
|
+
const wrapperEl = wrapper.find('.mc-popover__wrapper');
|
|
45
|
+
const id = wrapperEl.attributes('id');
|
|
46
|
+
|
|
47
|
+
const title = wrapper.find(`#${id}-title`);
|
|
48
|
+
const description = wrapper.find(`#${id}-description`);
|
|
49
|
+
expect(title.exists()).toBe(true);
|
|
50
|
+
expect(title.text()).toBe('T');
|
|
51
|
+
expect(description.exists()).toBe(true);
|
|
52
|
+
expect(description.text()).toBe('D');
|
|
53
|
+
});
|
|
54
|
+
|
|
55
|
+
it('renders default slot content', () => {
|
|
56
|
+
const wrapper = mount(MPopover, {
|
|
57
|
+
slots: { default: '<div class="slot-content">Hello</div>' },
|
|
58
|
+
});
|
|
59
|
+
|
|
60
|
+
expect(wrapper.find('.slot-content').exists()).toBe(true);
|
|
61
|
+
expect(wrapper.find('.slot-content').text()).toBe('Hello');
|
|
62
|
+
});
|
|
63
|
+
|
|
64
|
+
it('renders footer slot when provided', () => {
|
|
65
|
+
const wrapper = mount(MPopover, {
|
|
66
|
+
slots: { footer: '<button class="footer-button">OK</button>' },
|
|
67
|
+
});
|
|
68
|
+
|
|
69
|
+
const footer = wrapper.find('.mc-popover__footer');
|
|
70
|
+
expect(footer.exists()).toBe(true);
|
|
71
|
+
const button = footer.find('button');
|
|
72
|
+
expect(button.exists()).toBe(true);
|
|
73
|
+
expect(button.text()).toBe('OK');
|
|
74
|
+
});
|
|
75
|
+
|
|
76
|
+
it('renders close button when closable is true and binds id', () => {
|
|
77
|
+
const wrapper = mount(MPopover, { props: { closable: true } });
|
|
78
|
+
|
|
79
|
+
const closeButton = wrapper.findComponent(MIconButton);
|
|
80
|
+
expect(closeButton.exists()).toBe(true);
|
|
81
|
+
|
|
82
|
+
const wrapperEl = wrapper.find('.mc-popover__wrapper');
|
|
83
|
+
const id = wrapperEl.attributes('id');
|
|
84
|
+
expect(closeButton.attributes('popovertarget')).toBe(id);
|
|
85
|
+
expect(closeButton.attributes('aria-label')).toBe('Close button');
|
|
86
|
+
});
|
|
87
|
+
|
|
88
|
+
it('activator slot receives id and can bind popovertarget', () => {
|
|
89
|
+
const wrapper = mount(MPopover, {
|
|
90
|
+
slots: {
|
|
91
|
+
activator: (slotProps: { id: string }) =>
|
|
92
|
+
h(
|
|
93
|
+
'button',
|
|
94
|
+
{ popovertarget: slotProps.id, class: 'activator' },
|
|
95
|
+
'Open',
|
|
96
|
+
),
|
|
97
|
+
},
|
|
98
|
+
});
|
|
99
|
+
|
|
100
|
+
const wrapperEl = wrapper.find('.mc-popover__wrapper');
|
|
101
|
+
const id = wrapperEl.attributes('id');
|
|
102
|
+
const activator = wrapper.find('.activator');
|
|
103
|
+
expect(activator.exists()).toBe(true);
|
|
104
|
+
expect(activator.attributes('popovertarget')).toBe(id);
|
|
105
|
+
});
|
|
106
|
+
});
|
|
@@ -0,0 +1,126 @@
|
|
|
1
|
+
import type { Meta, StoryObj } from '@storybook/vue3-vite';
|
|
2
|
+
import { action } from 'storybook/actions';
|
|
3
|
+
|
|
4
|
+
import MPopover from './MPopover.vue';
|
|
5
|
+
import MButton from '../button/MButton.vue';
|
|
6
|
+
import MLink from '../link/MLink.vue';
|
|
7
|
+
import { ExternalLink24 } from '@mozaic-ds/icons-vue';
|
|
8
|
+
|
|
9
|
+
const meta: Meta<typeof MPopover> = {
|
|
10
|
+
title: 'Overlay/Popover',
|
|
11
|
+
component: MPopover,
|
|
12
|
+
tags: ['v2'],
|
|
13
|
+
parameters: {
|
|
14
|
+
layout: 'centered',
|
|
15
|
+
docs: {
|
|
16
|
+
description: {
|
|
17
|
+
component:
|
|
18
|
+
'A popover is a small overlay that appears above other content, typically triggered by a user interaction such as clicking or hovering over an element. It is used to display contextual information, additional actions, or interactive content without navigating away from the main interface. Popovers often include a title, description, and action buttons, and they automatically close when clicking outside or selecting an action.',
|
|
19
|
+
},
|
|
20
|
+
},
|
|
21
|
+
},
|
|
22
|
+
args: {
|
|
23
|
+
title: 'Title',
|
|
24
|
+
description: 'Description',
|
|
25
|
+
footer: `
|
|
26
|
+
<MButton
|
|
27
|
+
size="s"
|
|
28
|
+
aria-label="Popover action button"
|
|
29
|
+
>
|
|
30
|
+
Button label
|
|
31
|
+
</MButton>
|
|
32
|
+
`,
|
|
33
|
+
activator: `
|
|
34
|
+
<MButton :popovertarget="id">
|
|
35
|
+
Activator
|
|
36
|
+
</MButton>
|
|
37
|
+
`,
|
|
38
|
+
},
|
|
39
|
+
render: (args) => ({
|
|
40
|
+
components: { MPopover, MButton, MLink, ExternalLink24 },
|
|
41
|
+
setup() {
|
|
42
|
+
const handleAction = action('action');
|
|
43
|
+
|
|
44
|
+
return { args, handleAction };
|
|
45
|
+
},
|
|
46
|
+
template: `
|
|
47
|
+
<MPopover
|
|
48
|
+
v-bind="args"
|
|
49
|
+
@action="handleAction"
|
|
50
|
+
>
|
|
51
|
+
<template v-if="${'footer' in args}" v-slot:footer>${args.footer}</template>
|
|
52
|
+
<template #activator="{ id }" v-slot:activator>${args.activator}</template>
|
|
53
|
+
</MPopover>
|
|
54
|
+
`,
|
|
55
|
+
}),
|
|
56
|
+
};
|
|
57
|
+
export default meta;
|
|
58
|
+
type Story = StoryObj<typeof MPopover>;
|
|
59
|
+
|
|
60
|
+
export const Default: Story = {};
|
|
61
|
+
|
|
62
|
+
export const Inverse = {
|
|
63
|
+
args: {
|
|
64
|
+
appearance: 'inverse',
|
|
65
|
+
footer: `
|
|
66
|
+
<MLink appearance="inverse" href="#" size="s" icon-position="right">
|
|
67
|
+
Stand-alone link
|
|
68
|
+
<template #icon><ExternalLink24 /></template>
|
|
69
|
+
</MLink>
|
|
70
|
+
<MButton appearance="inverse" size="s" aria-label="Popover action button">
|
|
71
|
+
Button label
|
|
72
|
+
</MButton>
|
|
73
|
+
`,
|
|
74
|
+
},
|
|
75
|
+
};
|
|
76
|
+
|
|
77
|
+
export const ValidationOnly = {
|
|
78
|
+
args: {
|
|
79
|
+
footer: `
|
|
80
|
+
<MButton size="s" aria-label="Popover action button">
|
|
81
|
+
Button label
|
|
82
|
+
</MButton>
|
|
83
|
+
`,
|
|
84
|
+
},
|
|
85
|
+
};
|
|
86
|
+
|
|
87
|
+
export const TwoOptions = {
|
|
88
|
+
args: {
|
|
89
|
+
footer: `
|
|
90
|
+
<MButton size="s" outlined aria-label="Popover action button">
|
|
91
|
+
Button label
|
|
92
|
+
</MButton>
|
|
93
|
+
<MButton size="s" aria-label="Popover action button">
|
|
94
|
+
Button label
|
|
95
|
+
</MButton>
|
|
96
|
+
`,
|
|
97
|
+
},
|
|
98
|
+
};
|
|
99
|
+
|
|
100
|
+
export const Link = {
|
|
101
|
+
args: {
|
|
102
|
+
footer: `
|
|
103
|
+
<MLink href="#" size="s" icon-position="right">
|
|
104
|
+
Stand-alone link
|
|
105
|
+
<template #icon><ExternalLink24 /></template>
|
|
106
|
+
</MLink>
|
|
107
|
+
<MButton
|
|
108
|
+
size="s"
|
|
109
|
+
aria-label="Popover action button"
|
|
110
|
+
>
|
|
111
|
+
Button label
|
|
112
|
+
</MButton>
|
|
113
|
+
`,
|
|
114
|
+
},
|
|
115
|
+
};
|
|
116
|
+
|
|
117
|
+
export const Navigation = {
|
|
118
|
+
args: {
|
|
119
|
+
footer: `
|
|
120
|
+
<MLink href="#" size="s" icon-position="right">
|
|
121
|
+
Stand-alone link
|
|
122
|
+
<template #icon><ExternalLink24 /></template>
|
|
123
|
+
</MLink>
|
|
124
|
+
`,
|
|
125
|
+
},
|
|
126
|
+
};
|
|
@@ -0,0 +1,131 @@
|
|
|
1
|
+
<template>
|
|
2
|
+
<div
|
|
3
|
+
:class="{
|
|
4
|
+
'mc-popover': true,
|
|
5
|
+
[`mc-popover--${appearance}`]: true,
|
|
6
|
+
[`mc-popover--${position}`]: true,
|
|
7
|
+
'mc-popover--no-pointer': !pointer,
|
|
8
|
+
[`mc-popover--${size}`]: true,
|
|
9
|
+
}"
|
|
10
|
+
>
|
|
11
|
+
<div
|
|
12
|
+
:id="id"
|
|
13
|
+
class="mc-popover__wrapper"
|
|
14
|
+
popover
|
|
15
|
+
:aria-labelledby="title && `${id}-title`"
|
|
16
|
+
:aria-describedby="description && `${id}-description`"
|
|
17
|
+
>
|
|
18
|
+
<div class="mc-popover__content">
|
|
19
|
+
<div v-if="title || description" class="mc-popover__headings">
|
|
20
|
+
<p v-if="title" :id="`${id}-title`" class="mc-popover__title">
|
|
21
|
+
{{ title }}
|
|
22
|
+
</p>
|
|
23
|
+
<p
|
|
24
|
+
v-if="description"
|
|
25
|
+
:id="`${id}-description`"
|
|
26
|
+
class="mc-popover__description"
|
|
27
|
+
>
|
|
28
|
+
{{ description }}
|
|
29
|
+
</p>
|
|
30
|
+
</div>
|
|
31
|
+
|
|
32
|
+
<div v-if="$slots.default">
|
|
33
|
+
<slot />
|
|
34
|
+
</div>
|
|
35
|
+
|
|
36
|
+
<MIconButton
|
|
37
|
+
v-if="closable"
|
|
38
|
+
ghost
|
|
39
|
+
:popovertarget="id"
|
|
40
|
+
:appearance="appearance"
|
|
41
|
+
icon-position="only"
|
|
42
|
+
aria-label="Close button"
|
|
43
|
+
class="mc-popover__close"
|
|
44
|
+
>
|
|
45
|
+
<template #icon>
|
|
46
|
+
<Cross20 />
|
|
47
|
+
</template>
|
|
48
|
+
</MIconButton>
|
|
49
|
+
|
|
50
|
+
<footer v-if="$slots.footer" class="mc-popover__footer">
|
|
51
|
+
<slot name="footer" />
|
|
52
|
+
</footer>
|
|
53
|
+
</div>
|
|
54
|
+
</div>
|
|
55
|
+
</div>
|
|
56
|
+
|
|
57
|
+
<slot name="activator" v-bind="{ id }" />
|
|
58
|
+
</template>
|
|
59
|
+
|
|
60
|
+
<script setup lang="ts">
|
|
61
|
+
import { useId, type VNode } from 'vue';
|
|
62
|
+
import { Cross20 } from '@mozaic-ds/icons-vue';
|
|
63
|
+
import MIconButton from '../iconbutton/MIconButton.vue';
|
|
64
|
+
/**
|
|
65
|
+
* A popover is a small overlay that appears above other content, typically triggered by a user interaction such as clicking or hovering over an element. It is used to display contextual information, additional actions, or interactive content without navigating away from the main interface. Popovers often include a title, description, and action buttons, and they automatically close when clicking outside or selecting an action.
|
|
66
|
+
*/
|
|
67
|
+
withDefaults(
|
|
68
|
+
defineProps<{
|
|
69
|
+
/**
|
|
70
|
+
* Allows to define the popover appearance.
|
|
71
|
+
*/
|
|
72
|
+
appearance?: 'standard' | 'inverse';
|
|
73
|
+
/**
|
|
74
|
+
* Defines the preferred position of the popover relative to its activator.
|
|
75
|
+
*/
|
|
76
|
+
position?: 'top' | 'right' | 'bottom' | 'left';
|
|
77
|
+
/**
|
|
78
|
+
* Controls the visibility of the popover pointer (arrow).
|
|
79
|
+
*/
|
|
80
|
+
pointer?: boolean;
|
|
81
|
+
/**
|
|
82
|
+
* Sets the overall size of the popover.
|
|
83
|
+
*/
|
|
84
|
+
size?: 's' | 'm' | 'l';
|
|
85
|
+
/**
|
|
86
|
+
* Displays a close icon button inside the popover.
|
|
87
|
+
* Clicking the button will close the popover.
|
|
88
|
+
*/
|
|
89
|
+
closable?: boolean;
|
|
90
|
+
/**
|
|
91
|
+
* Title displayed at the top of the popover.
|
|
92
|
+
*/
|
|
93
|
+
title?: string;
|
|
94
|
+
/**
|
|
95
|
+
* Supplementary text displayed below the title.
|
|
96
|
+
*/
|
|
97
|
+
description?: string;
|
|
98
|
+
}>(),
|
|
99
|
+
{
|
|
100
|
+
closable: true,
|
|
101
|
+
pointer: true,
|
|
102
|
+
appearance: 'standard',
|
|
103
|
+
position: 'top',
|
|
104
|
+
size: 's',
|
|
105
|
+
},
|
|
106
|
+
);
|
|
107
|
+
|
|
108
|
+
defineSlots<{
|
|
109
|
+
/**
|
|
110
|
+
* Main content of the popover.
|
|
111
|
+
*/
|
|
112
|
+
default?: () => VNode[];
|
|
113
|
+
/**
|
|
114
|
+
* Activator element that triggers the popover.
|
|
115
|
+
* Must contain a single <button> or element with [type="button"] attribute.
|
|
116
|
+
* Receives the popover `id` as a slot prop and must bind it
|
|
117
|
+
* to the `popovertarget` attribute.
|
|
118
|
+
*/
|
|
119
|
+
activator?: (props: { id: string }) => VNode[];
|
|
120
|
+
/**
|
|
121
|
+
* Use this slot to insert buttons or link in the footer.
|
|
122
|
+
*/
|
|
123
|
+
footer?: VNode;
|
|
124
|
+
}>();
|
|
125
|
+
|
|
126
|
+
const id = useId();
|
|
127
|
+
</script>
|
|
128
|
+
|
|
129
|
+
<style lang="scss" scoped>
|
|
130
|
+
@use '@mozaic-ds/styles/components/popover';
|
|
131
|
+
</style>
|
|
@@ -0,0 +1,42 @@
|
|
|
1
|
+
# MPopover
|
|
2
|
+
|
|
3
|
+
A popover is a small overlay that appears above other content, typically triggered by a user interaction such as clicking or hovering over an element. It is used to display contextual information, additional actions, or interactive content without navigating away from the main interface. Popovers often include a title, description, and action buttons, and they automatically close when clicking outside or selecting an action.
|
|
4
|
+
|
|
5
|
+
|
|
6
|
+
## Props
|
|
7
|
+
|
|
8
|
+
| Name | Description | Type | Default |
|
|
9
|
+
| --- | --- | --- | --- |
|
|
10
|
+
| `appearance` | Allows to define the popover appearance. | `"standard"` `"inverse"` | `"standard"` |
|
|
11
|
+
| `position` | Defines the preferred position of the popover relative to its activator. | `"bottom"` `"top"` `"left"` `"right"` | `"top"` |
|
|
12
|
+
| `pointer` | Controls the visibility of the popover pointer (arrow). | `boolean` | `true` |
|
|
13
|
+
| `size` | Sets the overall size of the popover. | `"s"` `"m"` `"l"` | `"s"` |
|
|
14
|
+
| `closable` | Displays a close icon button inside the popover.
|
|
15
|
+
Clicking the button will close the popover. | `boolean` | `true` |
|
|
16
|
+
| `title` | Title displayed at the top of the popover. | `string` | - |
|
|
17
|
+
| `description` | Supplementary text displayed below the title. | `string` | - |
|
|
18
|
+
|
|
19
|
+
## Slots
|
|
20
|
+
|
|
21
|
+
| Name | Description |
|
|
22
|
+
| --- | --- |
|
|
23
|
+
| `default` | Main content of the popover. |
|
|
24
|
+
| `activator` | Activator element that triggers the popover.
|
|
25
|
+
Must contain a single <button> or element with [type="button"] attribute.
|
|
26
|
+
Receives the popover `id` as a slot prop and must bind it
|
|
27
|
+
to the `popovertarget` attribute. |
|
|
28
|
+
| `footer` | Use this slot to insert buttons or link in the footer. |
|
|
29
|
+
|
|
30
|
+
## Dependencies
|
|
31
|
+
|
|
32
|
+
### Depends on
|
|
33
|
+
|
|
34
|
+
- [MIconButton](../iconbutton)
|
|
35
|
+
|
|
36
|
+
### Graph
|
|
37
|
+
|
|
38
|
+
```mermaid
|
|
39
|
+
graph TD;
|
|
40
|
+
MPopover --> MIconButton
|
|
41
|
+
style MPopover fill:#008240,stroke:#333,stroke-width:4px
|
|
42
|
+
```
|
|
@@ -25,11 +25,13 @@ A radio button is a selection control that allows users to choose a single optio
|
|
|
25
25
|
### Used By
|
|
26
26
|
|
|
27
27
|
- [MRadioGroup](../radiogroup)
|
|
28
|
+
- [MTileSelectable](../tileselectable)
|
|
28
29
|
|
|
29
30
|
### Graph
|
|
30
31
|
|
|
31
32
|
```mermaid
|
|
32
33
|
graph TD;
|
|
33
34
|
MRadioGroup --> MRadio
|
|
35
|
+
MTileSelectable --> MRadio
|
|
34
36
|
style MRadio fill:#008240,stroke:#333,stroke-width:4px
|
|
35
37
|
```
|
|
@@ -92,9 +92,10 @@ describe('Select.vue', () => {
|
|
|
92
92
|
},
|
|
93
93
|
});
|
|
94
94
|
|
|
95
|
+
const selectWrapper = wrapper.find('.mc-select');
|
|
95
96
|
const select = wrapper.find('select');
|
|
96
97
|
|
|
97
|
-
expect(
|
|
98
|
+
expect(selectWrapper.classes()).toContain('mc-select--s');
|
|
98
99
|
|
|
99
100
|
expect(select.classes()).toContain('is-invalid');
|
|
100
101
|
});
|
|
@@ -1,29 +1,35 @@
|
|
|
1
1
|
<template>
|
|
2
|
-
<
|
|
3
|
-
:
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
:value="modelValue"
|
|
7
|
-
:class="classObject"
|
|
8
|
-
:disabled="disabled"
|
|
9
|
-
v-bind="$attrs"
|
|
10
|
-
@change="
|
|
11
|
-
emit('update:modelValue', ($event.target as HTMLSelectElement).value)
|
|
12
|
-
"
|
|
2
|
+
<div :class="{
|
|
3
|
+
'mc-select': true,
|
|
4
|
+
[`mc-select--${size}`]: props.size && props.size != 'm',
|
|
5
|
+
}"
|
|
13
6
|
>
|
|
14
|
-
<
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
:
|
|
20
|
-
:
|
|
21
|
-
v-bind="
|
|
22
|
-
|
|
7
|
+
<select
|
|
8
|
+
:id="id"
|
|
9
|
+
class="mc-select__control"
|
|
10
|
+
:class="classObject"
|
|
11
|
+
:name="name"
|
|
12
|
+
:value="modelValue"
|
|
13
|
+
:disabled="disabled"
|
|
14
|
+
v-bind="$attrs"
|
|
15
|
+
@change="
|
|
16
|
+
emit('update:modelValue', ($event.target as HTMLSelectElement).value)
|
|
17
|
+
"
|
|
23
18
|
>
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
19
|
+
<option v-if="placeholder" value="" disabled>
|
|
20
|
+
-- {{ placeholder }} --
|
|
21
|
+
</option>
|
|
22
|
+
<option
|
|
23
|
+
v-for="(option, index) in options"
|
|
24
|
+
:key="index"
|
|
25
|
+
:value="option.value"
|
|
26
|
+
v-bind="option.attributes"
|
|
27
|
+
:disabled="option.disabled"
|
|
28
|
+
>
|
|
29
|
+
{{ option.text }}
|
|
30
|
+
</option>
|
|
31
|
+
</select>
|
|
32
|
+
</div>
|
|
27
33
|
</template>
|
|
28
34
|
|
|
29
35
|
<script setup lang="ts">
|
|
@@ -84,8 +90,7 @@ const props = withDefaults(
|
|
|
84
90
|
|
|
85
91
|
const classObject = computed(() => {
|
|
86
92
|
return {
|
|
87
|
-
|
|
88
|
-
'mc-select--readonly': props.readonly,
|
|
93
|
+
'mc-select__control--readonly': props.readonly,
|
|
89
94
|
'is-invalid': props.isInvalid,
|
|
90
95
|
};
|
|
91
96
|
});
|
|
@@ -0,0 +1,6 @@
|
|
|
1
|
+
import type { InjectionKey, Ref } from 'vue';
|
|
2
|
+
|
|
3
|
+
export const EXPANDED_SIDEBAR_KEY = Symbol() as InjectionKey<Ref<boolean>>;
|
|
4
|
+
export const TOGGLE_SIDEBAR_KEY = Symbol() as InjectionKey<() => void>;
|
|
5
|
+
export const SUB_ITEM_KEY = Symbol() as InjectionKey<boolean>;
|
|
6
|
+
export const STACKED_SHORTCUTS_KEY = Symbol() as InjectionKey<boolean>;
|