@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.
Files changed (90) hide show
  1. package/dist/mozaic-vue.css +1 -1
  2. package/dist/mozaic-vue.d.ts +1088 -378
  3. package/dist/mozaic-vue.js +2662 -1854
  4. package/dist/mozaic-vue.js.map +1 -1
  5. package/dist/mozaic-vue.umd.cjs +5 -5
  6. package/dist/mozaic-vue.umd.cjs.map +1 -1
  7. package/package.json +4 -4
  8. package/src/components/actionlistbox/MActionListbox.spec.ts +53 -59
  9. package/src/components/actionlistbox/MActionListbox.stories.ts +22 -1
  10. package/src/components/actionlistbox/MActionListbox.vue +91 -28
  11. package/src/components/actionlistbox/README.md +15 -0
  12. package/src/components/breadcrumb/MBreadcrumb.vue +5 -0
  13. package/src/components/button/README.md +4 -0
  14. package/src/components/checkbox/README.md +2 -0
  15. package/src/components/divider/README.md +4 -0
  16. package/src/components/iconbutton/MIconButton.stories.ts +12 -0
  17. package/src/components/iconbutton/MIconButton.vue +13 -1
  18. package/src/components/iconbutton/README.md +27 -0
  19. package/src/components/loader/README.md +2 -0
  20. package/src/components/navigationindicator/MNavigationIndicator.spec.ts +152 -0
  21. package/src/components/navigationindicator/MNavigationIndicator.stories.ts +41 -0
  22. package/src/components/navigationindicator/MNavigationIndicator.vue +132 -0
  23. package/src/components/navigationindicator/README.md +37 -0
  24. package/src/components/pageheader/MPageHeader.spec.ts +142 -0
  25. package/src/components/pageheader/MPageHeader.stories.ts +125 -0
  26. package/src/components/pageheader/MPageHeader.vue +133 -0
  27. package/src/components/pageheader/README.md +46 -0
  28. package/src/components/popover/MPopover.spec.ts +106 -0
  29. package/src/components/popover/MPopover.stories.ts +126 -0
  30. package/src/components/popover/MPopover.vue +131 -0
  31. package/src/components/popover/README.md +42 -0
  32. package/src/components/radio/README.md +2 -0
  33. package/src/components/select/MSelect.spec.ts +2 -1
  34. package/src/components/select/MSelect.vue +30 -25
  35. package/src/components/sidebar/MSidebar.const.ts +6 -0
  36. package/src/components/sidebar/MSidebar.spec.ts +110 -0
  37. package/src/components/sidebar/MSidebar.stories.ts +108 -0
  38. package/src/components/sidebar/MSidebar.vue +124 -0
  39. package/src/components/sidebar/README.md +59 -0
  40. package/src/components/sidebar/stories/DefaultCase.stories.vue +120 -0
  41. package/src/components/sidebar/stories/README.md +27 -0
  42. package/src/components/sidebar/stories/WithExpandOnly.stories.vue +112 -0
  43. package/src/components/sidebar/stories/WithProfileInfoOnly.stories.vue +119 -0
  44. package/src/components/sidebar/stories/WithSingleLevel.stories.vue +98 -0
  45. package/src/components/sidebar/use-floating-item.composable.ts +135 -0
  46. package/src/components/sidebar/use-floating-item.spec.ts +251 -0
  47. package/src/components/sidebarexpandableitem/MSidebarExpandableItem.spec.ts +151 -0
  48. package/src/components/sidebarexpandableitem/MSidebarExpandableItem.vue +113 -0
  49. package/src/components/sidebarexpandableitem/README.md +36 -0
  50. package/src/components/sidebarfooter/MSidebarFooter.spec.ts +276 -0
  51. package/src/components/sidebarfooter/MSidebarFooter.vue +201 -0
  52. package/src/components/sidebarfooter/README.md +52 -0
  53. package/src/components/sidebarfooter/_MSidebarFooterMenu.vue +64 -0
  54. package/src/components/sidebarheader/MSidebarHeader.vue +36 -0
  55. package/src/components/sidebarheader/README.md +31 -0
  56. package/src/components/sidebarnavitem/MSidebarNavItem.spec.ts +127 -0
  57. package/src/components/sidebarnavitem/MSidebarNavItem.vue +113 -0
  58. package/src/components/sidebarnavitem/README.md +56 -0
  59. package/src/components/sidebarshortcutitem/MSidebarShortcutItem.spec.ts +59 -0
  60. package/src/components/sidebarshortcutitem/MSidebarShortcutItem.vue +52 -0
  61. package/src/components/sidebarshortcutitem/README.md +32 -0
  62. package/src/components/sidebarshortcuts/MSidebarShortcuts.spec.ts +87 -0
  63. package/src/components/sidebarshortcuts/MSidebarShortcuts.vue +101 -0
  64. package/src/components/sidebarshortcuts/README.md +36 -0
  65. package/src/components/statusbadge/README.md +12 -0
  66. package/src/components/textinput/MTextInput.stories.ts +13 -1
  67. package/src/components/textinput/MTextInput.vue +12 -0
  68. package/src/components/textinput/README.md +3 -1
  69. package/src/components/tile/MTile.spec.ts +61 -0
  70. package/src/components/tile/MTile.stories.ts +102 -0
  71. package/src/components/tile/MTile.vue +68 -0
  72. package/src/components/tile/README.md +19 -0
  73. package/src/components/tileclickable/MTileClickable.spec.ts +130 -0
  74. package/src/components/tileclickable/MTileClickable.stories.ts +60 -0
  75. package/src/components/tileclickable/MTileClickable.vue +106 -0
  76. package/src/components/tileclickable/README.md +30 -0
  77. package/src/components/tileexpandable/MTileExpandable.spec.ts +121 -0
  78. package/src/components/tileexpandable/MTileExpandable.stories.ts +50 -0
  79. package/src/components/tileexpandable/MTileExpandable.vue +131 -0
  80. package/src/components/tileexpandable/README.md +36 -0
  81. package/src/components/tileselectable/MTileSelectable.spec.ts +177 -0
  82. package/src/components/tileselectable/MTileSelectable.stories.ts +55 -0
  83. package/src/components/tileselectable/MTileSelectable.vue +142 -0
  84. package/src/components/tileselectable/README.md +44 -0
  85. package/src/components/toaster/README.md +1 -1
  86. package/src/components/tooltip/MTooltip.vue +5 -0
  87. package/src/components/tooltip/README.md +16 -1
  88. package/src/main.ts +12 -2
  89. package/src/utils/use-is-mobile.composable.ts +20 -0
  90. package/src/utils/use-is-mobile.spec.ts +70 -0
@@ -0,0 +1,59 @@
1
+ import { mount } from '@vue/test-utils';
2
+ import { describe, it, expect } from 'vitest';
3
+ import { defineComponent } from 'vue';
4
+ import MSidebarShortcutItem from './MSidebarShortcutItem.vue';
5
+ import {
6
+ EXPANDED_SIDEBAR_KEY,
7
+ STACKED_SHORTCUTS_KEY,
8
+ } from '../sidebar/MSidebar.const';
9
+
10
+ const TestIcon = defineComponent({
11
+ name: 'TestIcon',
12
+ template: '<svg data-testid="test-icon"></svg>',
13
+ });
14
+
15
+ describe('MSidebarShortcutItem', () => {
16
+ it('renders label and href', () => {
17
+ const wrapper = mount(MSidebarShortcutItem, {
18
+ props: { label: 'Dashboard', href: '/dashboard' },
19
+ global: {
20
+ provide: {
21
+ [EXPANDED_SIDEBAR_KEY]: true,
22
+ [STACKED_SHORTCUTS_KEY]: true,
23
+ },
24
+ },
25
+ });
26
+ const link = wrapper.find('a.mc-sidebar__shortcut-link');
27
+ expect(link.exists()).toBe(true);
28
+ expect(link.attributes('href')).toBe('/dashboard');
29
+ expect(wrapper.text()).toContain('Dashboard');
30
+ });
31
+
32
+ it('renders icon when provided', () => {
33
+ const wrapper = mount(MSidebarShortcutItem, {
34
+ props: { label: 'WithIcon', href: '#', icon: TestIcon },
35
+ global: {
36
+ provide: {
37
+ [EXPANDED_SIDEBAR_KEY]: true,
38
+ [STACKED_SHORTCUTS_KEY]: true,
39
+ },
40
+ },
41
+ });
42
+ expect(wrapper.find('.mc-sidebar__shortcut-icon').exists()).toBe(true);
43
+ expect(wrapper.find('[data-testid="test-icon"]').exists()).toBe(true);
44
+ });
45
+
46
+ it('does not render icon when not provided', () => {
47
+ const wrapper = mount(MSidebarShortcutItem, {
48
+ props: { label: 'NoIcon', href: '#' },
49
+ global: {
50
+ provide: {
51
+ [EXPANDED_SIDEBAR_KEY]: true,
52
+ [STACKED_SHORTCUTS_KEY]: true,
53
+ },
54
+ },
55
+ });
56
+ expect(wrapper.find('.mc-sidebar__shortcut-icon').exists()).toBe(false);
57
+ expect(wrapper.find('svg').exists()).toBe(false);
58
+ });
59
+ });
@@ -0,0 +1,52 @@
1
+ <template>
2
+ <li
3
+ :class="[
4
+ 'mc-sidebar__shortcut-item',
5
+ !stacked && expanded
6
+ ? 'mc-sidebar__shortcut-item--grid'
7
+ : 'mc-sidebar__shortcut-item--stacked',
8
+ ]"
9
+ role="menuitem"
10
+ >
11
+ <a :href="props.href" class="mc-sidebar__shortcut-link">
12
+ <component
13
+ v-if="props.icon"
14
+ :is="props.icon"
15
+ class="mc-sidebar__shortcut-icon"
16
+ />
17
+ <span class="mc-sidebar__shortcut-label">{{ props.label }}</span>
18
+ </a>
19
+ </li>
20
+ </template>
21
+
22
+ <script setup lang="ts">
23
+ import { inject, type Component } from 'vue';
24
+ import {
25
+ EXPANDED_SIDEBAR_KEY,
26
+ STACKED_SHORTCUTS_KEY,
27
+ } from '../sidebar/MSidebar.const';
28
+ /**
29
+ * SidebarShortcutItem is a component that renders a sidebar shortcut link with an optional icon and label, providing quick access to a specific URL.
30
+ */
31
+ const props = defineProps<{
32
+ /**
33
+ * The text label displayed for the shortcut item.
34
+ */
35
+ label: string;
36
+ /**
37
+ * The URL the shortcut item links to.
38
+ */
39
+ href: string;
40
+ /**
41
+ * Optional icon component displayed alongside the label.
42
+ */
43
+ icon?: Component;
44
+ }>();
45
+
46
+ const stacked = inject(STACKED_SHORTCUTS_KEY);
47
+ const expanded = inject(EXPANDED_SIDEBAR_KEY);
48
+ </script>
49
+
50
+ <style lang="scss" scoped>
51
+ @use '@mozaic-ds/styles/components/sidebar';
52
+ </style>
@@ -0,0 +1,32 @@
1
+ # MSidebarShortcutItem
2
+
3
+ SidebarShortcutItem is a component that renders a sidebar shortcut link with an optional icon and label, providing quick access to a specific URL.
4
+
5
+
6
+ ## Props
7
+
8
+ | Name | Description | Type | Default |
9
+ | --- | --- | --- | --- |
10
+ | `label*` | The text label displayed for the shortcut item. | `string` | - |
11
+ | `href*` | The URL the shortcut item links to. | `string` | - |
12
+ | `icon` | Optional icon component displayed alongside the label. | `Component` | - |
13
+
14
+ ## Dependencies
15
+
16
+ ### Used By
17
+
18
+ - [DefaultCase.stories](../sidebar/stories)
19
+ - [WithExpandOnly.stories](../sidebar/stories)
20
+ - [WithProfileInfoOnly.stories](../sidebar/stories)
21
+ - [WithSingleLevel.stories](../sidebar/stories)
22
+
23
+ ### Graph
24
+
25
+ ```mermaid
26
+ graph TD;
27
+ DefaultCase.stories --> MSidebarShortcutItem
28
+ WithExpandOnly.stories --> MSidebarShortcutItem
29
+ WithProfileInfoOnly.stories --> MSidebarShortcutItem
30
+ WithSingleLevel.stories --> MSidebarShortcutItem
31
+ style MSidebarShortcutItem fill:#008240,stroke:#333,stroke-width:4px
32
+ ```
@@ -0,0 +1,87 @@
1
+ import { mount } from '@vue/test-utils';
2
+ import { describe, it, expect, vi, beforeEach } from 'vitest';
3
+ import { nextTick, ref } from 'vue';
4
+
5
+ const floatingItemIsDisplayed = ref(false);
6
+ const showFloatingItem = vi.fn(() => (floatingItemIsDisplayed.value = true));
7
+ const hideFloatingItem = vi.fn(() => (floatingItemIsDisplayed.value = false));
8
+ const onTriggerKeydown = vi.fn();
9
+ const onListboxKeydown = vi.fn();
10
+
11
+ vi.mock('../sidebar/use-floating-item.composable', () => {
12
+ return {
13
+ useFloatingItem: () => ({
14
+ floatingItemIsDisplayed,
15
+ showFloatingItem,
16
+ hideFloatingItem,
17
+ onTriggerKeydown,
18
+ onListboxKeydown,
19
+ }),
20
+ };
21
+ });
22
+
23
+ import MSidebarShortcuts from './MSidebarShortcuts.vue';
24
+ import { EXPANDED_SIDEBAR_KEY } from '../sidebar/MSidebar.const';
25
+
26
+ beforeEach(() => {
27
+ vi.clearAllMocks();
28
+ });
29
+
30
+ describe('MSidebarShortcuts', () => {
31
+ it('renders expanded view when expanded is true', () => {
32
+ const wrapper = mount(MSidebarShortcuts, {
33
+ props: { menuLabel: 'Shortcuts' },
34
+ global: {
35
+ provide: { [EXPANDED_SIDEBAR_KEY as symbol]: true },
36
+ components: { ViewGridX424: { template: '<svg />' } },
37
+ },
38
+ });
39
+
40
+ expect(wrapper.find('section').exists()).toBe(true);
41
+ expect(wrapper.find('.mc-sidebar__trigger').exists()).toBe(false);
42
+ });
43
+
44
+ it('renders trigger when collapsed and responds to interactions', async () => {
45
+ const wrapper = mount(MSidebarShortcuts, {
46
+ props: { menuLabel: 'Shortcuts' },
47
+ attachTo: document.body,
48
+ global: {
49
+ provide: { [EXPANDED_SIDEBAR_KEY as symbol]: false },
50
+ components: { ViewGridX424: { template: '<svg />' } },
51
+ stubs: {
52
+ Teleport: true,
53
+ },
54
+ },
55
+ });
56
+
57
+ const trigger = wrapper.find('.mc-sidebar__trigger');
58
+ expect(trigger.exists()).toBe(true);
59
+
60
+ await trigger.trigger('mouseenter');
61
+ expect(showFloatingItem).toHaveBeenCalled();
62
+ await nextTick();
63
+ expect(trigger.attributes('aria-expanded')).toBe('true');
64
+
65
+ await trigger.trigger('mouseleave');
66
+ expect(hideFloatingItem).toHaveBeenCalled();
67
+ await nextTick();
68
+ expect(trigger.attributes('aria-expanded')).toBe('false');
69
+
70
+ await trigger.trigger('focus');
71
+ expect(showFloatingItem).toHaveBeenCalled();
72
+
73
+ await trigger.trigger('blur');
74
+ expect(hideFloatingItem).toHaveBeenCalled();
75
+
76
+ await trigger.trigger('keydown', { key: 'ArrowDown' });
77
+ expect(onTriggerKeydown).toHaveBeenCalled();
78
+
79
+ const floating = wrapper.find('.mc-sidebar__floating-item');
80
+ expect(floating.exists()).toBe(true);
81
+ await floating.trigger('keydown', { key: 'Escape' });
82
+ expect(onListboxKeydown).toHaveBeenCalled();
83
+
84
+ await floating.trigger('mouseleave');
85
+ expect(hideFloatingItem).toHaveBeenCalled();
86
+ });
87
+ });
@@ -0,0 +1,101 @@
1
+ <template>
2
+ <section v-if="expanded">
3
+ <nav role="navigation" aria-label="shortcut items">
4
+ <ul
5
+ :class="{
6
+ 'mc-sidebar__shortcut-list': true,
7
+ 'mc-sidebar__shortcut-list--stacked': props.stacked,
8
+ 'mc-sidebar__shortcut-list--grid': !props.stacked,
9
+ }"
10
+ role="menu"
11
+ >
12
+ <slot name="default" />
13
+ </ul>
14
+ </nav>
15
+ </section>
16
+
17
+ <div v-else class="mc-sidebar__shortcut-wrapper">
18
+ <button
19
+ ref="trigger"
20
+ :id="`trigger-${id}`"
21
+ class="mc-sidebar__shortcut-item mc-sidebar__trigger"
22
+ aria-label="Sidebar shortcuts"
23
+ aria-haspopup="menu"
24
+ :aria-expanded="floatingItemIsDisplayed"
25
+ :aria-controls="`listbox-${id}`"
26
+ @mouseenter="showFloatingItem"
27
+ @focus="showFloatingItem"
28
+ @keydown="onTriggerKeydown"
29
+ @mouseleave="hideFloatingItem"
30
+ @blur="hideFloatingItem"
31
+ >
32
+ <ViewGridX424 />
33
+ </button>
34
+
35
+ <div
36
+ ref="listbox"
37
+ :id="`listbox-${id}`"
38
+ tabindex="0"
39
+ role="menu"
40
+ class="mc-sidebar__floating-item mc-sidebar__floating-item--hidden"
41
+ @keydown="onListboxKeydown"
42
+ @mouseleave="hideFloatingItem"
43
+ @blur="hideFloatingItem"
44
+ >
45
+ <div class="mc-sidebar__listbox">
46
+ <ul
47
+ class="mc-sidebar__shortcut-list mc-sidebar__shortcut-list--floating mc-sidebar__shortcut-list mc-sidebar__shortcut-list--stacked"
48
+ >
49
+ <slot name="default" />
50
+ </ul>
51
+ </div>
52
+ </div>
53
+ </div>
54
+ </template>
55
+
56
+ <script setup lang="ts">
57
+ import { ViewGridX424 } from '@mozaic-ds/icons-vue';
58
+ import { inject, provide, useId, useTemplateRef, type VNode } from 'vue';
59
+ import { useFloatingItem } from '../sidebar/use-floating-item.composable';
60
+ import {
61
+ EXPANDED_SIDEBAR_KEY,
62
+ STACKED_SHORTCUTS_KEY,
63
+ } from '../sidebar/MSidebar.const';
64
+ /**
65
+ * SidebarShortcuts is a component that displays a list of sidebar shortcut items. It supports stacked or grid layouts, shows a floating overlay when the sidebar is collapsed, and allows custom shortcut items via slots.
66
+ */
67
+ const props = defineProps<{
68
+ /**
69
+ * Determines the layout of the sidebar shortcuts. When true, the shortcuts are displayed in a vertical stacked list. When false or not provided, the shortcuts are arranged in a grid layout.
70
+ */
71
+ stacked?: boolean;
72
+ }>();
73
+
74
+ defineSlots<{
75
+ /**
76
+ * Slot for sidebar shortcut items. Should contain one or more `MSidebarShortcutItem` components.
77
+ */
78
+ default: VNode[];
79
+ }>();
80
+
81
+ const expanded = inject(EXPANDED_SIDEBAR_KEY);
82
+
83
+ const id = useId();
84
+
85
+ const trigger = useTemplateRef('trigger');
86
+ const listbox = useTemplateRef('listbox');
87
+
88
+ const {
89
+ floatingItemIsDisplayed,
90
+ hideFloatingItem,
91
+ showFloatingItem,
92
+ onTriggerKeydown,
93
+ onListboxKeydown,
94
+ } = useFloatingItem(trigger, listbox);
95
+
96
+ provide(STACKED_SHORTCUTS_KEY, props.stacked);
97
+ </script>
98
+
99
+ <style lang="scss" scoped>
100
+ @use '@mozaic-ds/styles/components/sidebar';
101
+ </style>
@@ -0,0 +1,36 @@
1
+ # MSidebarShortcuts
2
+
3
+ SidebarShortcuts is a component that displays a list of sidebar shortcut items. It supports stacked or grid layouts, shows a floating overlay when the sidebar is collapsed, and allows custom shortcut items via slots.
4
+
5
+
6
+ ## Props
7
+
8
+ | Name | Description | Type | Default |
9
+ | --- | --- | --- | --- |
10
+ | `stacked` | Determines the layout of the sidebar shortcuts. When true, the shortcuts are displayed in a vertical stacked list. When false or not provided, the shortcuts are arranged in a grid layout. | `boolean` | - |
11
+
12
+ ## Slots
13
+
14
+ | Name | Description |
15
+ | --- | --- |
16
+ | `default` | Slot for sidebar shortcut items. Should contain one or more `MSidebarShortcutItem` components. |
17
+
18
+ ## Dependencies
19
+
20
+ ### Used By
21
+
22
+ - [DefaultCase.stories](../sidebar/stories)
23
+ - [WithExpandOnly.stories](../sidebar/stories)
24
+ - [WithProfileInfoOnly.stories](../sidebar/stories)
25
+ - [WithSingleLevel.stories](../sidebar/stories)
26
+
27
+ ### Graph
28
+
29
+ ```mermaid
30
+ graph TD;
31
+ DefaultCase.stories --> MSidebarShortcuts
32
+ WithExpandOnly.stories --> MSidebarShortcuts
33
+ WithProfileInfoOnly.stories --> MSidebarShortcuts
34
+ WithSingleLevel.stories --> MSidebarShortcuts
35
+ style MSidebarShortcuts fill:#008240,stroke:#333,stroke-width:4px
36
+ ```
@@ -23,3 +23,15 @@ graph TD;
23
23
  MStatusBadge --> MStatusDot
24
24
  style MStatusBadge fill:#008240,stroke:#333,stroke-width:4px
25
25
  ```
26
+
27
+ ### Used By
28
+
29
+ - [MPageHeader](../pageheader)
30
+
31
+ ### Graph
32
+
33
+ ```mermaid
34
+ graph TD;
35
+ MPageHeader --> MStatusBadge
36
+ style MStatusBadge fill:#008240,stroke:#333,stroke-width:4px
37
+ ```
@@ -33,7 +33,7 @@ const meta: Meta<typeof MTextInput> = {
33
33
  return { args, modelValue, handleUpdate };
34
34
  },
35
35
  template: `
36
- <MTextInput
36
+ <MTextInput
37
37
  v-bind="{ ...args, modelValue }"
38
38
  @update:modelValue="handleUpdate"
39
39
  />
@@ -111,3 +111,15 @@ export const Invalid: Story = {
111
111
  isInvalid: true,
112
112
  },
113
113
  };
114
+
115
+ export const WithPrefix = {
116
+ args: {
117
+ prefix: 'Prefix',
118
+ },
119
+ };
120
+
121
+ export const WithSuffix = {
122
+ args: {
123
+ suffix: 'Suffix',
124
+ },
125
+ };
@@ -4,6 +4,8 @@
4
4
  <slot name="icon" />
5
5
  </span>
6
6
 
7
+ <span v-if="prefix" class="mc-text-input__addon">{{ prefix }}</span>
8
+
7
9
  <input
8
10
  :id="id"
9
11
  class="mc-text-input__control"
@@ -20,6 +22,8 @@
20
22
  "
21
23
  />
22
24
 
25
+ <span v-if="suffix" class="mc-text-input__addon">{{ suffix }}</span>
26
+
23
27
  <div v-if="isClearable && modelValue" class="mc-controls-options">
24
28
  <button
25
29
  type="button"
@@ -96,6 +100,14 @@ const props = withDefaults(
96
100
  * The label text for the clear button.
97
101
  */
98
102
  clearLabel?: string;
103
+ /**
104
+ * Displays additional text inside the component, before the input.
105
+ */
106
+ prefix?: string;
107
+ /**
108
+ * Displays additional text inside the component, after the input.
109
+ */
110
+ suffix?: string;
99
111
  }>(),
100
112
  {
101
113
  inputType: 'text',
@@ -11,13 +11,15 @@ A text input is a single-line input that allows users to enter and edit short te
11
11
  | `name` | The name attribute for the input element, typically used for form submission. | `string` | - |
12
12
  | `modelValue` | The current value of the input field. | `string` `number` | - |
13
13
  | `placeholder` | A placeholder text to show in the input when it is empty. | `string` | - |
14
- | `inputType` | Defines the type of input. | `"number"` `"text"` `"date"` `"email"` `"password"` `"search"` `"tel"` | `"text"` |
14
+ | `inputType` | Defines the type of input. | `"number"` `"text"` `"search"` `"date"` `"email"` `"password"` `"tel"` | `"text"` |
15
15
  | `isInvalid` | If `true`, applies an invalid state to the input. | `boolean` | - |
16
16
  | `disabled` | If `true`, disables the input, making it non-interactive. | `boolean` | - |
17
17
  | `size` | Determines the size of the input. | `"s"` `"m"` | `"m"` |
18
18
  | `readonly` | If `true`, the input is read-only (cannot be edited). | `boolean` | - |
19
19
  | `isClearable` | If `true`, a clear button will appear when the input has a value. | `boolean` | - |
20
20
  | `clearLabel` | The label text for the clear button. | `string` | `"clear content"` |
21
+ | `prefix` | Displays additional text inside the component, before the input. | `string` | - |
22
+ | `suffix` | Displays additional text inside the component, after the input. | `string` | - |
21
23
 
22
24
  ## Slots
23
25
 
@@ -0,0 +1,61 @@
1
+ import { describe, it, expect } from 'vitest';
2
+ import { mount } from '@vue/test-utils';
3
+ import MTile from './MTile.vue';
4
+
5
+ describe('MTile', () => {
6
+ it('does not apply appearance modifier when appearance is primary', () => {
7
+ const wrapper = mount(MTile, {
8
+ props: {
9
+ appearance: 'primary',
10
+ },
11
+ });
12
+
13
+ expect(wrapper.classes()).not.toContain('mc-tile--primary');
14
+ });
15
+
16
+ it('applies appearance modifier when appearance is not primary', () => {
17
+ const wrapper = mount(MTile, {
18
+ props: {
19
+ appearance: 'secondary',
20
+ },
21
+ });
22
+
23
+ expect(wrapper.classes()).toContain('mc-tile--secondary');
24
+ });
25
+
26
+ it('applies bordered class when bordered is true', () => {
27
+ const wrapper = mount(MTile, {
28
+ props: {
29
+ bordered: true,
30
+ },
31
+ });
32
+
33
+ expect(wrapper.classes()).toContain('mc-tile--bordered');
34
+ });
35
+
36
+ it('does not apply bordered class when bordered is false', () => {
37
+ const wrapper = mount(MTile, {
38
+ props: {
39
+ bordered: false,
40
+ },
41
+ });
42
+
43
+ expect(wrapper.classes()).not.toContain('mc-tile--bordered');
44
+ });
45
+
46
+ it('does not render details container when slot is missing', () => {
47
+ const wrapper = mount(MTile);
48
+
49
+ expect(wrapper.find('.mc-tile__content').exists()).toBe(false);
50
+ });
51
+
52
+ it('renders details container when slot is provided', () => {
53
+ const wrapper = mount(MTile, {
54
+ slots: {
55
+ details: '<span>Details</span>',
56
+ },
57
+ });
58
+
59
+ expect(wrapper.find('.mc-tile__content').exists()).toBe(true);
60
+ });
61
+ });
@@ -0,0 +1,102 @@
1
+ import type { Meta, StoryObj } from '@storybook/vue3-vite';
2
+ import MTile from './MTile.vue';
3
+ import MIconButton from '../iconbutton/MIconButton.vue';
4
+ import {
5
+ OptionsVertical24,
6
+ Copy20,
7
+ ArrowTopRight20,
8
+ Download20,
9
+ Trash20,
10
+ } from '@mozaic-ds/icons-vue';
11
+ import MActionListbox from '../actionlistbox/MActionListbox.vue';
12
+
13
+ const meta: Meta<typeof MTile> = {
14
+ title: 'Content/Tile/Base',
15
+ component: MTile,
16
+ parameters: {
17
+ docs: {
18
+ description: {
19
+ component:
20
+ '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.',
21
+ },
22
+ },
23
+ },
24
+ args: {
25
+ appearance: 'primary',
26
+ bordered: true,
27
+ default: `
28
+ <div class="placeholder-slot">
29
+ Insert a form element here to replace this slot.
30
+ </div>
31
+ `,
32
+ },
33
+ render: (args) => ({
34
+ components: {
35
+ MTile,
36
+ MIconButton,
37
+ OptionsVertical24,
38
+ MActionListbox,
39
+ Copy20,
40
+ ArrowTopRight20,
41
+ Download20,
42
+ Trash20,
43
+ },
44
+ setup() {
45
+ const items = [
46
+ {
47
+ label: 'Duplicate',
48
+ icon: Copy20,
49
+ disabled: true,
50
+ },
51
+ {
52
+ label: 'Move to...',
53
+ icon: ArrowTopRight20,
54
+ },
55
+ {
56
+ label: 'Download',
57
+ icon: Download20,
58
+ },
59
+ {
60
+ label: 'Delete',
61
+ icon: Trash20,
62
+ appearance: 'danger',
63
+ divider: true,
64
+ },
65
+ ];
66
+ return { args, items };
67
+ },
68
+ template: `
69
+ <MTile v-bind="args">
70
+ ${args.default}
71
+ <template v-if="args.action" #action>
72
+ ${args.action}
73
+ </template>
74
+ </MTile>
75
+ `,
76
+ }),
77
+ };
78
+ export default meta;
79
+ type Story = StoryObj<typeof MTile>;
80
+
81
+ export const Default: Story = {};
82
+
83
+ export const WithAction: Story = {
84
+ args: {
85
+ action: `
86
+ <MActionListbox position="left" :items="items">
87
+ <template #activator="{id}">
88
+ <MIconButton
89
+ :popovertarget="id"
90
+ ghost
91
+ class="mc-tile__action-button"
92
+ aria-label="Action button"
93
+ >
94
+ <template #icon>
95
+ <OptionsVertical24 />
96
+ </template>
97
+ </MIconButton>
98
+ </template>
99
+ </MActionListbox>
100
+ `,
101
+ },
102
+ };