@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,68 @@
|
|
|
1
|
+
<template>
|
|
2
|
+
<div
|
|
3
|
+
:class="{
|
|
4
|
+
'mc-tile': true,
|
|
5
|
+
[`mc-tile--${appearance}`]: props.appearance !== 'primary',
|
|
6
|
+
'mc-tile--bordered': props.bordered,
|
|
7
|
+
}"
|
|
8
|
+
>
|
|
9
|
+
<div class="mc-tile__header">
|
|
10
|
+
<div class="mc-tile__body">
|
|
11
|
+
<slot />
|
|
12
|
+
</div>
|
|
13
|
+
|
|
14
|
+
<div v-if="slots.action" class="mc-tile__action">
|
|
15
|
+
<slot name="action" />
|
|
16
|
+
</div>
|
|
17
|
+
</div>
|
|
18
|
+
|
|
19
|
+
<div v-if="$slots.details" class="mc-tile__content">
|
|
20
|
+
<slot name="details" />
|
|
21
|
+
</div>
|
|
22
|
+
</div>
|
|
23
|
+
</template>
|
|
24
|
+
|
|
25
|
+
<script setup lang="ts">
|
|
26
|
+
import type { VNode } from 'vue';
|
|
27
|
+
|
|
28
|
+
/**
|
|
29
|
+
* A tile is a container component used to group related content and actions within a structured layout. It provides a clickable or static area that can display text, images, icons, or interactive elements. Tiles are commonly used to present key information, navigate to detailed views, or trigger specific actions in dashboards, cards, and grid-based layouts. Their adaptable design allows them to accommodate various content types while maintaining a consistent and organized interface.
|
|
30
|
+
*/
|
|
31
|
+
|
|
32
|
+
const props = withDefaults(
|
|
33
|
+
defineProps<{
|
|
34
|
+
/**
|
|
35
|
+
* Defines the visual style of the tile.
|
|
36
|
+
*/
|
|
37
|
+
appearance?: 'primary' | 'secondary' | 'inverse';
|
|
38
|
+
|
|
39
|
+
/**
|
|
40
|
+
* Adds a border around the tile.
|
|
41
|
+
*/
|
|
42
|
+
bordered?: boolean;
|
|
43
|
+
}>(),
|
|
44
|
+
{
|
|
45
|
+
appearance: 'primary',
|
|
46
|
+
bordered: true,
|
|
47
|
+
},
|
|
48
|
+
);
|
|
49
|
+
|
|
50
|
+
const slots = defineSlots<{
|
|
51
|
+
/**
|
|
52
|
+
* Default slot for the main content of the tile.
|
|
53
|
+
*/
|
|
54
|
+
default?: VNode[];
|
|
55
|
+
/**
|
|
56
|
+
* Named slot for additional content shown below the main content.
|
|
57
|
+
*/
|
|
58
|
+
details?: VNode[];
|
|
59
|
+
/**
|
|
60
|
+
* Named slot for action element, such as a button or link.
|
|
61
|
+
*/
|
|
62
|
+
action?: VNode[];
|
|
63
|
+
}>();
|
|
64
|
+
</script>
|
|
65
|
+
|
|
66
|
+
<style lang="scss" scoped>
|
|
67
|
+
@use '@mozaic-ds/styles/components/tile';
|
|
68
|
+
</style>
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
# MTile
|
|
2
|
+
|
|
3
|
+
A tile is a container component used to group related content and actions within a structured layout. It provides a clickable or static area that can display text, images, icons, or interactive elements. Tiles are commonly used to present key information, navigate to detailed views, or trigger specific actions in dashboards, cards, and grid-based layouts. Their adaptable design allows them to accommodate various content types while maintaining a consistent and organized interface.
|
|
4
|
+
|
|
5
|
+
|
|
6
|
+
## Props
|
|
7
|
+
|
|
8
|
+
| Name | Description | Type | Default |
|
|
9
|
+
| --- | --- | --- | --- |
|
|
10
|
+
| `appearance` | Defines the visual style of the tile. | `"inverse"` `"primary"` `"secondary"` | `"primary"` |
|
|
11
|
+
| `bordered` | Adds a border around the tile. | `boolean` | `true` |
|
|
12
|
+
|
|
13
|
+
## Slots
|
|
14
|
+
|
|
15
|
+
| Name | Description |
|
|
16
|
+
| --- | --- |
|
|
17
|
+
| `default` | Default slot for the main content of the tile. |
|
|
18
|
+
| `details` | Named slot for additional content shown below the main content. |
|
|
19
|
+
| `action` | Named slot for action element, such as a button or link. |
|
|
@@ -0,0 +1,130 @@
|
|
|
1
|
+
import { describe, it, expect } from 'vitest';
|
|
2
|
+
import { mount } from '@vue/test-utils';
|
|
3
|
+
import MTileClickable from './MTileClickable.vue';
|
|
4
|
+
|
|
5
|
+
describe('MTileClickable', () => {
|
|
6
|
+
it('renders as a button by default', () => {
|
|
7
|
+
const wrapper = mount(MTileClickable);
|
|
8
|
+
|
|
9
|
+
expect(wrapper.element.tagName).toBe('BUTTON');
|
|
10
|
+
expect(wrapper.attributes('type')).toBe('button');
|
|
11
|
+
});
|
|
12
|
+
|
|
13
|
+
it('renders as a link when href is provided', () => {
|
|
14
|
+
const wrapper = mount(MTileClickable, {
|
|
15
|
+
props: {
|
|
16
|
+
href: 'https://example.com',
|
|
17
|
+
},
|
|
18
|
+
});
|
|
19
|
+
|
|
20
|
+
expect(wrapper.element.tagName).toBe('A');
|
|
21
|
+
expect(wrapper.attributes('href')).toBe('https://example.com');
|
|
22
|
+
});
|
|
23
|
+
|
|
24
|
+
it('emits action event when clicked as a button', async () => {
|
|
25
|
+
const wrapper = mount(MTileClickable);
|
|
26
|
+
|
|
27
|
+
await wrapper.trigger('click');
|
|
28
|
+
|
|
29
|
+
expect(wrapper.emitted('action')).toBeTruthy();
|
|
30
|
+
expect(wrapper.emitted('action')?.length).toBe(1);
|
|
31
|
+
});
|
|
32
|
+
|
|
33
|
+
it('does not emit action event when rendered as a link', async () => {
|
|
34
|
+
const wrapper = mount(MTileClickable, {
|
|
35
|
+
props: {
|
|
36
|
+
href: 'https://example.com',
|
|
37
|
+
},
|
|
38
|
+
});
|
|
39
|
+
|
|
40
|
+
await wrapper.trigger('click');
|
|
41
|
+
|
|
42
|
+
expect(wrapper.emitted('action')).toBeFalsy();
|
|
43
|
+
});
|
|
44
|
+
|
|
45
|
+
it('applies appearance modifier when appearance is not primary', () => {
|
|
46
|
+
const wrapper = mount(MTileClickable, {
|
|
47
|
+
props: {
|
|
48
|
+
appearance: 'secondary',
|
|
49
|
+
},
|
|
50
|
+
});
|
|
51
|
+
|
|
52
|
+
expect(wrapper.classes()).toContain('mc-tile--secondary');
|
|
53
|
+
});
|
|
54
|
+
|
|
55
|
+
it('does not apply appearance modifier when appearance is primary', () => {
|
|
56
|
+
const wrapper = mount(MTileClickable, {
|
|
57
|
+
props: {
|
|
58
|
+
appearance: 'primary',
|
|
59
|
+
},
|
|
60
|
+
});
|
|
61
|
+
|
|
62
|
+
expect(wrapper.classes()).not.toContain('mc-tile--primary');
|
|
63
|
+
});
|
|
64
|
+
|
|
65
|
+
it('applies bordered class when bordered is true', () => {
|
|
66
|
+
const wrapper = mount(MTileClickable, {
|
|
67
|
+
props: {
|
|
68
|
+
bordered: true,
|
|
69
|
+
},
|
|
70
|
+
});
|
|
71
|
+
|
|
72
|
+
expect(wrapper.classes()).toContain('mc-tile--bordered');
|
|
73
|
+
});
|
|
74
|
+
|
|
75
|
+
it('does not apply bordered class when bordered is false', () => {
|
|
76
|
+
const wrapper = mount(MTileClickable, {
|
|
77
|
+
props: {
|
|
78
|
+
bordered: false,
|
|
79
|
+
},
|
|
80
|
+
});
|
|
81
|
+
|
|
82
|
+
expect(wrapper.classes()).not.toContain('mc-tile--bordered');
|
|
83
|
+
});
|
|
84
|
+
|
|
85
|
+
it('applies icon right position class when iconPosition is right', () => {
|
|
86
|
+
const wrapper = mount(MTileClickable, {
|
|
87
|
+
props: {
|
|
88
|
+
iconPosition: 'right',
|
|
89
|
+
},
|
|
90
|
+
});
|
|
91
|
+
|
|
92
|
+
expect(wrapper.classes()).toContain('mc-tile--action-right');
|
|
93
|
+
});
|
|
94
|
+
|
|
95
|
+
it('does not apply right position class when iconPosition is bottom', () => {
|
|
96
|
+
const wrapper = mount(MTileClickable, {
|
|
97
|
+
props: {
|
|
98
|
+
iconPosition: 'bottom',
|
|
99
|
+
},
|
|
100
|
+
});
|
|
101
|
+
|
|
102
|
+
expect(wrapper.classes()).not.toContain('mc-tile--action-right');
|
|
103
|
+
});
|
|
104
|
+
|
|
105
|
+
it('applies external link behavior class when href is set', () => {
|
|
106
|
+
const wrapper = mount(MTileClickable, {
|
|
107
|
+
props: {
|
|
108
|
+
href: 'https://example.com',
|
|
109
|
+
},
|
|
110
|
+
});
|
|
111
|
+
|
|
112
|
+
expect(wrapper.classes()).toContain('mc-tile--external-link');
|
|
113
|
+
});
|
|
114
|
+
|
|
115
|
+
it('applies navigate behavior class when to is set', () => {
|
|
116
|
+
const wrapper = mount(MTileClickable, {
|
|
117
|
+
props: {
|
|
118
|
+
to: '/dashboard',
|
|
119
|
+
},
|
|
120
|
+
});
|
|
121
|
+
|
|
122
|
+
expect(wrapper.classes()).toContain('mc-tile--navigate');
|
|
123
|
+
});
|
|
124
|
+
|
|
125
|
+
it('applies open behavior class when neither href nor to is set', () => {
|
|
126
|
+
const wrapper = mount(MTileClickable);
|
|
127
|
+
|
|
128
|
+
expect(wrapper.classes()).toContain('mc-tile--open');
|
|
129
|
+
});
|
|
130
|
+
});
|
|
@@ -0,0 +1,60 @@
|
|
|
1
|
+
import type { Meta, StoryObj } from '@storybook/vue3-vite';
|
|
2
|
+
import { action } from 'storybook/actions';
|
|
3
|
+
import MTileClickable from './MTileClickable.vue';
|
|
4
|
+
|
|
5
|
+
const meta: Meta<typeof MTileClickable> = {
|
|
6
|
+
title: 'Content/Tile/Clickable',
|
|
7
|
+
component: MTileClickable,
|
|
8
|
+
parameters: {
|
|
9
|
+
docs: {
|
|
10
|
+
description: {
|
|
11
|
+
component:
|
|
12
|
+
'A tile is a container component used to group related content and actions within a structured layout. It provides a clickable or static area that can display text, images, icons, or interactive elements. Tiles are commonly used to present key information, navigate to detailed views, or trigger specific actions in dashboards, cards, and grid-based layouts. Their adaptable design allows them to accommodate various content types while maintaining a consistent and organized interface.',
|
|
13
|
+
},
|
|
14
|
+
},
|
|
15
|
+
},
|
|
16
|
+
args: {
|
|
17
|
+
appearance: 'primary',
|
|
18
|
+
iconPosition: 'right',
|
|
19
|
+
bordered: true,
|
|
20
|
+
default: `
|
|
21
|
+
<div class="placeholder-slot">
|
|
22
|
+
Insert a form element here to replace this slot.
|
|
23
|
+
</div>
|
|
24
|
+
`,
|
|
25
|
+
},
|
|
26
|
+
render: (args) => ({
|
|
27
|
+
components: { MTileClickable },
|
|
28
|
+
setup() {
|
|
29
|
+
const handleAction = action('action');
|
|
30
|
+
return { args, handleAction };
|
|
31
|
+
},
|
|
32
|
+
template: `
|
|
33
|
+
<MTileClickable v-bind="args" @action="handleAction">
|
|
34
|
+
${args.default}
|
|
35
|
+
</MTileClickable>
|
|
36
|
+
`,
|
|
37
|
+
}),
|
|
38
|
+
};
|
|
39
|
+
export default meta;
|
|
40
|
+
type Story = StoryObj<typeof MTileClickable>;
|
|
41
|
+
|
|
42
|
+
export const Default: Story = {};
|
|
43
|
+
|
|
44
|
+
export const WithBottomAction: Story = {
|
|
45
|
+
args: {
|
|
46
|
+
iconPosition: 'bottom',
|
|
47
|
+
},
|
|
48
|
+
};
|
|
49
|
+
|
|
50
|
+
export const ExternalLink: Story = {
|
|
51
|
+
args: {
|
|
52
|
+
href: '#',
|
|
53
|
+
},
|
|
54
|
+
};
|
|
55
|
+
|
|
56
|
+
export const InternalNavigation: Story = {
|
|
57
|
+
args: {
|
|
58
|
+
to: 'routeName',
|
|
59
|
+
},
|
|
60
|
+
};
|
|
@@ -0,0 +1,106 @@
|
|
|
1
|
+
<template>
|
|
2
|
+
<component
|
|
3
|
+
:is="rootTag"
|
|
4
|
+
:class="{
|
|
5
|
+
'mc-tile mc-tile--clickable': true,
|
|
6
|
+
[`mc-tile--${props.appearance}`]: props.appearance !== 'primary',
|
|
7
|
+
[behaviorClass]: true,
|
|
8
|
+
'mc-tile--bordered': props.bordered,
|
|
9
|
+
'mc-tile--action-right': props.iconPosition === 'right',
|
|
10
|
+
}"
|
|
11
|
+
:href="props.href"
|
|
12
|
+
:target="props.target"
|
|
13
|
+
:type="rootTag === 'button' && 'button'"
|
|
14
|
+
v-bind="rootTag === 'button' ? { onclick: () => emit('action') } : {}"
|
|
15
|
+
>
|
|
16
|
+
<div class="mc-tile__body">
|
|
17
|
+
<slot />
|
|
18
|
+
</div>
|
|
19
|
+
|
|
20
|
+
<div class="mc-tile__action">
|
|
21
|
+
<div class="mc-tile__action-icon">
|
|
22
|
+
<slot name="icon" />
|
|
23
|
+
</div>
|
|
24
|
+
</div>
|
|
25
|
+
</component>
|
|
26
|
+
</template>
|
|
27
|
+
|
|
28
|
+
<script setup lang="ts">
|
|
29
|
+
import { computed, type VNode } from 'vue';
|
|
30
|
+
|
|
31
|
+
/**
|
|
32
|
+
* A tile is a container component used to group related content and actions within a structured layout. It provides a clickable or static area that can display text, images, icons, or interactive elements. Tiles are commonly used to present key information, navigate to detailed views, or trigger specific actions in dashboards, cards, and grid-based layouts. Their adaptable design allows them to accommodate various content types while maintaining a consistent and organized interface.
|
|
33
|
+
*/
|
|
34
|
+
|
|
35
|
+
const props = withDefaults(
|
|
36
|
+
defineProps<{
|
|
37
|
+
/**
|
|
38
|
+
* Defines the visual style of the tile.
|
|
39
|
+
*/
|
|
40
|
+
appearance?: 'primary' | 'secondary' | 'inverse';
|
|
41
|
+
/**
|
|
42
|
+
* Adds a border around the tile.
|
|
43
|
+
*/
|
|
44
|
+
bordered?: boolean;
|
|
45
|
+
/**
|
|
46
|
+
* If set, the tile becomes an external link (`<a>`).
|
|
47
|
+
* Used to navigate to a full URL.
|
|
48
|
+
*/
|
|
49
|
+
href?: string;
|
|
50
|
+
/**
|
|
51
|
+
* If set, the tile becomes an internal link (Vue Router route).
|
|
52
|
+
* Used for navigation within the app.
|
|
53
|
+
*/
|
|
54
|
+
to?: string;
|
|
55
|
+
/**
|
|
56
|
+
* Where to open the link.
|
|
57
|
+
*/
|
|
58
|
+
target?: '_self' | '_blank' | '_parent' | '_top';
|
|
59
|
+
/**
|
|
60
|
+
* Position of the icon relative to the content.
|
|
61
|
+
*/
|
|
62
|
+
iconPosition?: 'bottom' | 'right';
|
|
63
|
+
}>(),
|
|
64
|
+
{
|
|
65
|
+
appearance: 'primary',
|
|
66
|
+
iconPosition: 'right',
|
|
67
|
+
bordered: true,
|
|
68
|
+
},
|
|
69
|
+
);
|
|
70
|
+
|
|
71
|
+
const emit = defineEmits<{
|
|
72
|
+
/**
|
|
73
|
+
* Emits when the action button is triggered.
|
|
74
|
+
*/
|
|
75
|
+
(on: 'action'): void;
|
|
76
|
+
}>();
|
|
77
|
+
|
|
78
|
+
defineSlots<{
|
|
79
|
+
/**
|
|
80
|
+
* Default slot for the main content of the tile.
|
|
81
|
+
*/
|
|
82
|
+
default?: VNode[];
|
|
83
|
+
/**
|
|
84
|
+
* Named slot for the action icon.
|
|
85
|
+
*/
|
|
86
|
+
icon?: VNode[];
|
|
87
|
+
}>();
|
|
88
|
+
|
|
89
|
+
const rootTag = computed(() => {
|
|
90
|
+
if (props.href) return 'a';
|
|
91
|
+
if (props.to) return 'router-link';
|
|
92
|
+
|
|
93
|
+
return 'button';
|
|
94
|
+
});
|
|
95
|
+
|
|
96
|
+
const behaviorClass = computed(() => {
|
|
97
|
+
if (props.href) return 'mc-tile--external-link';
|
|
98
|
+
if (props.to) return 'mc-tile--navigate';
|
|
99
|
+
|
|
100
|
+
return 'mc-tile--open';
|
|
101
|
+
});
|
|
102
|
+
</script>
|
|
103
|
+
|
|
104
|
+
<style lang="scss" scoped>
|
|
105
|
+
@use '@mozaic-ds/styles/components/tile';
|
|
106
|
+
</style>
|
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
# MTileClickable
|
|
2
|
+
|
|
3
|
+
A tile is a container component used to group related content and actions within a structured layout. It provides a clickable or static area that can display text, images, icons, or interactive elements. Tiles are commonly used to present key information, navigate to detailed views, or trigger specific actions in dashboards, cards, and grid-based layouts. Their adaptable design allows them to accommodate various content types while maintaining a consistent and organized interface.
|
|
4
|
+
|
|
5
|
+
|
|
6
|
+
## Props
|
|
7
|
+
|
|
8
|
+
| Name | Description | Type | Default |
|
|
9
|
+
| --- | --- | --- | --- |
|
|
10
|
+
| `appearance` | Defines the visual style of the tile. | `"inverse"` `"primary"` `"secondary"` | `"primary"` |
|
|
11
|
+
| `bordered` | Adds a border around the tile. | `boolean` | `true` |
|
|
12
|
+
| `href` | If set, the tile becomes an external link (`<a>`).
|
|
13
|
+
Used to navigate to a full URL. | `string` | - |
|
|
14
|
+
| `to` | If set, the tile becomes an internal link (Vue Router route).
|
|
15
|
+
Used for navigation within the app. | `string` | - |
|
|
16
|
+
| `target` | Where to open the link. | `"_self"` `"_blank"` `"_parent"` `"_top"` | - |
|
|
17
|
+
| `iconPosition` | Position of the icon relative to the content. | `"right"` `"bottom"` | `"right"` |
|
|
18
|
+
|
|
19
|
+
## Slots
|
|
20
|
+
|
|
21
|
+
| Name | Description |
|
|
22
|
+
| --- | --- |
|
|
23
|
+
| `default` | Default slot for the main content of the tile. |
|
|
24
|
+
| `icon` | Named slot for the action icon. |
|
|
25
|
+
|
|
26
|
+
## Events
|
|
27
|
+
|
|
28
|
+
| Name | Description | Type |
|
|
29
|
+
| --- | --- | --- |
|
|
30
|
+
| `action` | Emits when the action button is triggered. | [] |
|
|
@@ -0,0 +1,121 @@
|
|
|
1
|
+
import { describe, it, expect } from 'vitest';
|
|
2
|
+
import { mount } from '@vue/test-utils';
|
|
3
|
+
import MTileExpandable from './MTileExpandable.vue';
|
|
4
|
+
|
|
5
|
+
describe('MTileExpandable', () => {
|
|
6
|
+
it('applies appearance modifier when not primary', () => {
|
|
7
|
+
const wrapper = mount(MTileExpandable, {
|
|
8
|
+
props: { appearance: 'secondary' },
|
|
9
|
+
});
|
|
10
|
+
|
|
11
|
+
expect(wrapper.classes()).toContain('mc-tile--secondary');
|
|
12
|
+
});
|
|
13
|
+
|
|
14
|
+
it('applies bordered class when bordered is true', () => {
|
|
15
|
+
const wrapper = mount(MTileExpandable, {
|
|
16
|
+
props: { bordered: true },
|
|
17
|
+
});
|
|
18
|
+
|
|
19
|
+
expect(wrapper.classes()).toContain('mc-tile--bordered');
|
|
20
|
+
});
|
|
21
|
+
|
|
22
|
+
it('applies trigger mode class when trigger is not container', () => {
|
|
23
|
+
const wrapper = mount(MTileExpandable, {
|
|
24
|
+
props: { trigger: 'icon' },
|
|
25
|
+
});
|
|
26
|
+
|
|
27
|
+
expect(wrapper.classes()).toContain('mc-tile--trigger-icon');
|
|
28
|
+
});
|
|
29
|
+
|
|
30
|
+
it('does not apply trigger mode class when trigger is container', () => {
|
|
31
|
+
const wrapper = mount(MTileExpandable, {
|
|
32
|
+
props: { trigger: 'container' },
|
|
33
|
+
});
|
|
34
|
+
|
|
35
|
+
expect(
|
|
36
|
+
wrapper.classes().some((c) => c.startsWith('mc-tile--trigger-')),
|
|
37
|
+
).toBe(false);
|
|
38
|
+
});
|
|
39
|
+
|
|
40
|
+
it('renders container as button when trigger is container', async () => {
|
|
41
|
+
const wrapper = mount(MTileExpandable, {
|
|
42
|
+
props: { trigger: 'container' },
|
|
43
|
+
});
|
|
44
|
+
|
|
45
|
+
const header = wrapper.find('.mc-tile__header');
|
|
46
|
+
expect(header.element.tagName).toBe('BUTTON');
|
|
47
|
+
|
|
48
|
+
expect(header.attributes('aria-controls')).toBeTruthy();
|
|
49
|
+
expect(header.attributes('aria-expanded')).toBe('false');
|
|
50
|
+
|
|
51
|
+
await header.trigger('click');
|
|
52
|
+
expect(header.attributes('aria-expanded')).toBe('true');
|
|
53
|
+
});
|
|
54
|
+
|
|
55
|
+
it('renders icon button when trigger is icon', async () => {
|
|
56
|
+
const wrapper = mount(MTileExpandable, {
|
|
57
|
+
props: { trigger: 'icon' },
|
|
58
|
+
});
|
|
59
|
+
|
|
60
|
+
const iconButton = wrapper.findComponent({ name: 'MIconButton' });
|
|
61
|
+
expect(iconButton.exists()).toBe(true);
|
|
62
|
+
|
|
63
|
+
const header = wrapper.find('.mc-tile__header');
|
|
64
|
+
expect(header.element.tagName).toBe('DIV');
|
|
65
|
+
expect(wrapper.find('.mc-tile__content').attributes('aria-hidden')).toBe(
|
|
66
|
+
'true',
|
|
67
|
+
);
|
|
68
|
+
|
|
69
|
+
await iconButton.trigger('click');
|
|
70
|
+
expect(wrapper.find('.mc-tile__content').attributes('aria-hidden')).toBe(
|
|
71
|
+
'false',
|
|
72
|
+
);
|
|
73
|
+
});
|
|
74
|
+
|
|
75
|
+
it('renders action button when trigger is button', async () => {
|
|
76
|
+
const wrapper = mount(MTileExpandable, {
|
|
77
|
+
props: { trigger: 'button', label: 'Expand' },
|
|
78
|
+
});
|
|
79
|
+
|
|
80
|
+
const button = wrapper.findComponent({ name: 'MButton' });
|
|
81
|
+
expect(button.exists()).toBe(true);
|
|
82
|
+
expect(button.text()).toContain('Expand');
|
|
83
|
+
|
|
84
|
+
await button.trigger('click');
|
|
85
|
+
expect(wrapper.find('.mc-tile__content').attributes('aria-hidden')).toBe(
|
|
86
|
+
'false',
|
|
87
|
+
);
|
|
88
|
+
});
|
|
89
|
+
|
|
90
|
+
it('toggles open state when clicking header in container mode', async () => {
|
|
91
|
+
const wrapper = mount(MTileExpandable, {
|
|
92
|
+
props: { trigger: 'container' },
|
|
93
|
+
});
|
|
94
|
+
|
|
95
|
+
const header = wrapper.find('.mc-tile__header');
|
|
96
|
+
expect(wrapper.find('.mc-tile__content').attributes('aria-hidden')).toBe(
|
|
97
|
+
'true',
|
|
98
|
+
);
|
|
99
|
+
|
|
100
|
+
await header.trigger('click');
|
|
101
|
+
expect(wrapper.find('.mc-tile__content').attributes('aria-hidden')).toBe(
|
|
102
|
+
'false',
|
|
103
|
+
);
|
|
104
|
+
|
|
105
|
+
await header.trigger('click');
|
|
106
|
+
expect(wrapper.find('.mc-tile__content').attributes('aria-hidden')).toBe(
|
|
107
|
+
'true',
|
|
108
|
+
);
|
|
109
|
+
});
|
|
110
|
+
|
|
111
|
+
it('content slot has id matching aria-controls', () => {
|
|
112
|
+
const wrapper = mount(MTileExpandable, {
|
|
113
|
+
props: { trigger: 'container' },
|
|
114
|
+
});
|
|
115
|
+
|
|
116
|
+
const header = wrapper.find('.mc-tile__header');
|
|
117
|
+
const content = wrapper.find('.mc-tile__content');
|
|
118
|
+
|
|
119
|
+
expect(content.attributes('id')).toBe(header.attributes('aria-controls'));
|
|
120
|
+
});
|
|
121
|
+
});
|
|
@@ -0,0 +1,50 @@
|
|
|
1
|
+
import type { Meta, StoryObj } from '@storybook/vue3-vite';
|
|
2
|
+
import MTileExpandable from './MTileExpandable.vue';
|
|
3
|
+
|
|
4
|
+
const meta: Meta<typeof MTileExpandable> = {
|
|
5
|
+
title: 'Content/Tile/Expandable',
|
|
6
|
+
component: MTileExpandable,
|
|
7
|
+
parameters: {
|
|
8
|
+
docs: {
|
|
9
|
+
description: {
|
|
10
|
+
component:
|
|
11
|
+
'A tile is a container component used to group related content and actions within a structured layout. It provides a clickable or static area that can display text, images, icons, or interactive elements. Tiles are commonly used to present key information, navigate to detailed views, or trigger specific actions in dashboards, cards, and grid-based layouts. Their adaptable design allows them to accommodate various content types while maintaining a consistent and organized interface.',
|
|
12
|
+
},
|
|
13
|
+
},
|
|
14
|
+
},
|
|
15
|
+
args: {
|
|
16
|
+
appearance: 'primary',
|
|
17
|
+
label: 'See more',
|
|
18
|
+
trigger: 'icon',
|
|
19
|
+
bordered: true,
|
|
20
|
+
default: `
|
|
21
|
+
<div class="placeholder-slot">
|
|
22
|
+
Click on the expand button to see details.
|
|
23
|
+
</div>
|
|
24
|
+
`,
|
|
25
|
+
details: `
|
|
26
|
+
<div class="placeholder-slot">
|
|
27
|
+
Insert a form element here to replace this slot.
|
|
28
|
+
</div>
|
|
29
|
+
`,
|
|
30
|
+
},
|
|
31
|
+
render: (args) => ({
|
|
32
|
+
components: { MTileExpandable },
|
|
33
|
+
setup() {
|
|
34
|
+
return { args };
|
|
35
|
+
},
|
|
36
|
+
template: `
|
|
37
|
+
<MTileExpandable v-bind="args">
|
|
38
|
+
${args.default}
|
|
39
|
+
|
|
40
|
+
<template #details>
|
|
41
|
+
${args.details}
|
|
42
|
+
</template>
|
|
43
|
+
</MTileExpandable>
|
|
44
|
+
`,
|
|
45
|
+
}),
|
|
46
|
+
};
|
|
47
|
+
export default meta;
|
|
48
|
+
type Story = StoryObj<typeof MTileExpandable>;
|
|
49
|
+
|
|
50
|
+
export const Default: Story = {};
|