@mozaic-ds/vue 2.12.0 → 2.13.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 +702 -361
- package/dist/mozaic-vue.js +2791 -2428
- 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 +7 -6
- package/src/components/{usingPresets.mdx → BrandPresets.mdx} +2 -2
- package/src/components/Changelog.mdx +19 -0
- package/src/components/Color.mdx +226 -0
- package/src/components/Contributing.mdx +12 -6
- package/src/components/GettingStarted.mdx +1 -1
- package/src/components/Icon.stories.ts +134 -0
- package/src/components/Welcome.mdx +49 -0
- package/src/components/accordionlist/MAccordionList.spec.ts +136 -0
- package/src/components/accordionlist/MAccordionList.stories.ts +123 -0
- package/src/components/accordionlist/MAccordionList.vue +91 -0
- package/src/components/accordionlist/README.md +24 -0
- package/src/components/accordionlist/m-accordion-list.const.ts +9 -0
- package/src/components/accordionlistitem/MAccordionListItem.spec.ts +123 -0
- package/src/components/accordionlistitem/MAccordionListItem.vue +95 -0
- package/src/components/accordionlistitem/README.md +23 -0
- package/src/components/actionbottombar/MActionBottomBar.spec.ts +52 -0
- package/src/components/actionbottombar/MActionBottomBar.stories.ts +162 -0
- package/src/components/actionbottombar/MActionBottomBar.vue +45 -0
- package/src/components/actionbottombar/README.md +31 -0
- package/src/components/actionlistbox/MActionListbox.spec.ts +134 -0
- package/src/components/actionlistbox/MActionListbox.stories.ts +74 -0
- package/src/components/actionlistbox/MActionListbox.vue +89 -0
- package/src/components/actionlistbox/README.md +25 -0
- package/src/components/avatar/MAvatar.stories.ts +1 -1
- package/src/components/breadcrumb/README.md +14 -0
- package/src/components/builtinmenu/MBuiltInMenu.stories.ts +2 -1
- package/src/components/builtinmenu/MBuiltInMenu.vue +1 -1
- package/src/components/builtinmenu/README.md +14 -0
- package/src/components/button/MButton.spec.ts +1 -1
- package/src/components/button/MButton.stories.ts +165 -5
- package/src/components/button/README.md +33 -1
- package/src/components/callout/MCallout.spec.ts +7 -6
- package/src/components/callout/MCallout.stories.ts +1 -2
- package/src/components/carousel/MCarousel.spec.ts +1 -2
- package/src/components/carousel/MCarousel.stories.ts +2 -1
- package/src/components/carousel/MCarousel.vue +1 -2
- package/src/components/carousel/README.md +14 -0
- package/src/components/checkbox/README.md +14 -0
- package/src/components/checkboxgroup/README.md +14 -0
- package/src/components/checklistmenu/MCheckListMenu.spec.ts +1 -1
- package/src/components/checklistmenu/MCheckListMenu.stories.ts +1 -0
- package/src/components/checklistmenu/MCheckListMenu.vue +1 -1
- package/src/components/checklistmenu/README.md +14 -0
- package/src/components/circularprogressbar/README.md +15 -1
- package/src/components/datepicker/MDatepicker.vue +1 -1
- package/src/components/divider/README.md +22 -0
- package/src/components/drawer/MDrawer.vue +1 -2
- package/src/components/drawer/README.md +16 -0
- package/src/components/field/README.md +14 -0
- package/src/components/fileuploader/MFileUploader.spec.ts +304 -0
- package/src/components/fileuploader/MFileUploader.stories.ts +123 -0
- package/src/components/fileuploader/MFileUploader.vue +314 -0
- package/src/components/fileuploader/README.md +58 -0
- package/src/components/fileuploaderitem/MFileUploaderItem.spec.ts +91 -0
- package/src/components/fileuploaderitem/MFileUploaderItem.vue +180 -0
- package/src/components/fileuploaderitem/README.md +58 -0
- package/src/components/flag/README.md +1 -1
- package/src/components/iconbutton/MIconButton.spec.ts +1 -1
- package/src/components/iconbutton/MIconButton.stories.ts +116 -7
- package/src/components/iconbutton/README.md +25 -1
- package/src/components/kpiitem/MKpiItem.vue +5 -3
- package/src/components/linearprogressbarbuffer/README.md +14 -0
- package/src/components/link/MLink.stories.ts +1 -2
- package/src/components/link/README.md +14 -0
- package/src/components/loader/README.md +20 -0
- package/src/components/loadingoverlay/README.md +14 -0
- package/src/components/modal/MModal.stories.ts +1 -2
- package/src/components/modal/MModal.vue +1 -1
- package/src/components/modal/README.md +16 -0
- package/src/components/numberbadge/README.md +17 -1
- package/src/components/overlay/README.md +16 -0
- package/src/components/pagination/MPagination.vue +1 -2
- package/src/components/pagination/README.md +18 -0
- package/src/components/passwordinput/MPasswordInput.vue +1 -1
- package/src/components/passwordinput/README.md +14 -0
- package/src/components/phonenumber/MPhoneNumber.spec.ts +7 -6
- package/src/components/phonenumber/MPhoneNumber.vue +1 -1
- package/src/components/quantityselector/MQuantitySelector.vue +1 -2
- package/src/components/radio/README.md +14 -0
- package/src/components/radiogroup/README.md +14 -0
- package/src/components/select/README.md +14 -0
- package/src/components/starrating/MStarRating.spec.ts +1 -2
- package/src/components/starrating/MStarRating.vue +1 -3
- package/src/components/statusbadge/README.md +14 -0
- package/src/components/statusdot/README.md +14 -0
- package/src/components/statusmessage/MStatusMessage.spec.ts +6 -4
- package/src/components/statusmessage/MStatusMessage.vue +6 -4
- package/src/components/statusmessage/README.md +14 -0
- package/src/components/statusnotification/MStatusNotification.spec.ts +6 -4
- package/src/components/statusnotification/MStatusNotification.stories.ts +1 -1
- package/src/components/statusnotification/MStatusNotification.vue +7 -5
- package/src/components/statusnotification/README.md +14 -0
- package/src/components/stepperbottombar/MStepperBottomBar.spec.ts +134 -0
- package/src/components/stepperbottombar/MStepperBottomBar.stories.ts +72 -0
- package/src/components/stepperbottombar/MStepperBottomBar.vue +131 -0
- package/src/components/stepperbottombar/README.md +40 -0
- package/src/components/steppercompact/README.md +14 -0
- package/src/components/stepperinline/MStepperInline.vue +1 -2
- package/src/components/tabs/MTabs.stories.ts +1 -1
- package/src/components/tabs/README.md +16 -0
- package/src/components/tag/MTag.vue +1 -1
- package/src/components/tag/README.md +14 -0
- package/src/components/textinput/MTextInput.spec.ts +1 -1
- package/src/components/textinput/MTextInput.stories.ts +1 -1
- package/src/components/textinput/MTextInput.vue +1 -1
- package/src/components/toaster/MToaster.spec.ts +6 -4
- package/src/components/toaster/MToaster.vue +7 -5
- package/src/components/toaster/README.md +16 -0
- package/src/components/toggle/README.md +14 -0
- package/src/components/togglegroup/README.md +14 -0
- package/src/main.ts +5 -0
- package/src/components/Introduction.mdx +0 -100
- package/src/components/Support.mdx +0 -18
- package/src/components/usingIcons.mdx +0 -35
|
@@ -0,0 +1,123 @@
|
|
|
1
|
+
import type { Meta, StoryObj } from '@storybook/vue3-vite';
|
|
2
|
+
import { action } from 'storybook/actions';
|
|
3
|
+
import MAccordionList from './MAccordionList.vue';
|
|
4
|
+
import MAccordionListItem from '../accordionlistitem/MAccordionListItem.vue';
|
|
5
|
+
import { Wrench32, Project32, Sharpening32 } from '@mozaic-ds/icons-vue';
|
|
6
|
+
|
|
7
|
+
const meta: Meta<typeof MAccordionList> = {
|
|
8
|
+
title: 'Content/Accordion list',
|
|
9
|
+
component: MAccordionList,
|
|
10
|
+
parameters: {
|
|
11
|
+
docs: {
|
|
12
|
+
description: {
|
|
13
|
+
component:
|
|
14
|
+
'An Accordion List is a component that organizes content into collapsible sections, allowing users to show or hide related information within a vertically stacked layout. This structure helps optimize space and improve readability by displaying only relevant content when needed. Accordion Lists are commonly used in FAQs, settings menus, and structured content navigation to enhance user experience and reduce visual clutter.',
|
|
15
|
+
},
|
|
16
|
+
},
|
|
17
|
+
},
|
|
18
|
+
args: {
|
|
19
|
+
modelValue: ['1'],
|
|
20
|
+
default: `
|
|
21
|
+
<MAccordionListItem id="1" title="How does the delivery work?">
|
|
22
|
+
<p>
|
|
23
|
+
Delivery is completed within 2 to 5 business days. You will receive a confirmation email with a tracking number as soon as your order is shipped.
|
|
24
|
+
</p>
|
|
25
|
+
</MAccordionListItem>
|
|
26
|
+
|
|
27
|
+
<MAccordionListItem id="2" title="What payment methods are accepted?">
|
|
28
|
+
<p>
|
|
29
|
+
We accept credit cards (Visa, Mastercard, American Express), PayPal, and bank transfers.
|
|
30
|
+
</p>
|
|
31
|
+
</MAccordionListItem>
|
|
32
|
+
|
|
33
|
+
<MAccordionListItem id="3" title="Can I return an item?">
|
|
34
|
+
<p>
|
|
35
|
+
Yes, you have 30 days to return an item in its original condition. Return shipping costs are at your expense unless the product is defective.
|
|
36
|
+
</p>
|
|
37
|
+
</MAccordionListItem>
|
|
38
|
+
`,
|
|
39
|
+
},
|
|
40
|
+
render: (args) => ({
|
|
41
|
+
components: {
|
|
42
|
+
MAccordionList,
|
|
43
|
+
MAccordionListItem,
|
|
44
|
+
Wrench32,
|
|
45
|
+
Project32,
|
|
46
|
+
Sharpening32,
|
|
47
|
+
},
|
|
48
|
+
setup() {
|
|
49
|
+
const handleUpdate = action('update:modelValue');
|
|
50
|
+
|
|
51
|
+
return { args, Wrench32, Project32, Sharpening32, handleUpdate };
|
|
52
|
+
},
|
|
53
|
+
template: `
|
|
54
|
+
<MAccordionList v-bind="args" @update:modelValue="handleUpdate">
|
|
55
|
+
${args.default}
|
|
56
|
+
</MAccordionList>
|
|
57
|
+
`,
|
|
58
|
+
}),
|
|
59
|
+
};
|
|
60
|
+
export default meta;
|
|
61
|
+
type Story = StoryObj<typeof MAccordionList>;
|
|
62
|
+
|
|
63
|
+
export const Default: Story = {};
|
|
64
|
+
|
|
65
|
+
export const Ghost: Story = {
|
|
66
|
+
args: {
|
|
67
|
+
appearance: 'ghost',
|
|
68
|
+
},
|
|
69
|
+
};
|
|
70
|
+
|
|
71
|
+
export const WithIcons: Story = {
|
|
72
|
+
args: {
|
|
73
|
+
default: `
|
|
74
|
+
<MAccordionListItem id="1" title="How does the delivery work?" :icon="Wrench32">
|
|
75
|
+
<p>
|
|
76
|
+
Delivery is completed within 2 to 5 business days. You will receive a confirmation email with a tracking number as soon as your order is shipped.
|
|
77
|
+
</p>
|
|
78
|
+
</MAccordionListItem>
|
|
79
|
+
|
|
80
|
+
<MAccordionListItem id="2" title="What payment methods are accepted?" :icon="Project32">
|
|
81
|
+
<p>
|
|
82
|
+
We accept credit cards (Visa, Mastercard, American Express), PayPal, and bank transfers.
|
|
83
|
+
</p>
|
|
84
|
+
</MAccordionListItem>
|
|
85
|
+
|
|
86
|
+
<MAccordionListItem id="3" title="Can I return an item?" :icon="Sharpening32">
|
|
87
|
+
<p>
|
|
88
|
+
Yes, you have 30 days to return an item in its original condition. Return shipping costs are at your expense unless the product is defective.
|
|
89
|
+
</p>
|
|
90
|
+
</MAccordionListItem>
|
|
91
|
+
`,
|
|
92
|
+
},
|
|
93
|
+
};
|
|
94
|
+
|
|
95
|
+
export const WithSubtitle: Story = {
|
|
96
|
+
args: {
|
|
97
|
+
default: `
|
|
98
|
+
<MAccordionListItem id="1" title="How does the delivery work?" subtitle="Shipping & tracking information">
|
|
99
|
+
<p>
|
|
100
|
+
Delivery is completed within 2 to 5 business days. You will receive a confirmation email with a tracking number as soon as your order is shipped.
|
|
101
|
+
</p>
|
|
102
|
+
</MAccordionListItem>
|
|
103
|
+
|
|
104
|
+
<MAccordionListItem id="2" title="What payment methods are accepted?" subtitle="Payments & billing options">
|
|
105
|
+
<p>
|
|
106
|
+
We accept credit cards (Visa, Mastercard, American Express), PayPal, and bank transfers.
|
|
107
|
+
</p>
|
|
108
|
+
</MAccordionListItem>
|
|
109
|
+
|
|
110
|
+
<MAccordionListItem id="3" title="Can I return an item?" subtitle="Returns & refund policy">
|
|
111
|
+
<p>
|
|
112
|
+
Yes, you have 30 days to return an item in its original condition. Return shipping costs are at your expense unless the product is defective.
|
|
113
|
+
</p>
|
|
114
|
+
</MAccordionListItem>
|
|
115
|
+
`,
|
|
116
|
+
},
|
|
117
|
+
};
|
|
118
|
+
|
|
119
|
+
export const WithSingleBehavior: Story = {
|
|
120
|
+
args: {
|
|
121
|
+
behavior: 'single',
|
|
122
|
+
},
|
|
123
|
+
};
|
|
@@ -0,0 +1,91 @@
|
|
|
1
|
+
<template>
|
|
2
|
+
<div class="mc-accordion" :class="classObject">
|
|
3
|
+
<slot />
|
|
4
|
+
</div>
|
|
5
|
+
</template>
|
|
6
|
+
|
|
7
|
+
<script setup lang="ts">
|
|
8
|
+
import { computed, provide, type VNode } from 'vue';
|
|
9
|
+
import {
|
|
10
|
+
AccordionStateKey,
|
|
11
|
+
AccordionToggleFnKey,
|
|
12
|
+
} from './m-accordion-list.const';
|
|
13
|
+
|
|
14
|
+
/**
|
|
15
|
+
* An Accordion List is a component that organizes content into collapsible sections, allowing users to show or hide related information within a vertically stacked layout. This structure helps optimize space and improve readability by displaying only relevant content when needed. Accordion Lists are commonly used in FAQs, settings menus, and structured content navigation to enhance user experience and reduce visual clutter.
|
|
16
|
+
*/
|
|
17
|
+
|
|
18
|
+
const props = withDefaults(
|
|
19
|
+
defineProps<{
|
|
20
|
+
/**
|
|
21
|
+
* Defines the visual appearance of the button.
|
|
22
|
+
*/
|
|
23
|
+
appearance?: 'standard' | 'ghost';
|
|
24
|
+
/**
|
|
25
|
+
* Component behavior: `multiple` allows all items to be open simultaneously, `single` ensures only one item is open at a time. For native HTML implementation, the `single` behavior uses the HTML5 `name` attribute on `<details>` elements for browser-native exclusive behavior..
|
|
26
|
+
*/
|
|
27
|
+
behavior?: 'multiple' | 'single';
|
|
28
|
+
/**
|
|
29
|
+
* IDs of the Accordion items that are currently open. Updating this array controls which sections are expanded.
|
|
30
|
+
*/
|
|
31
|
+
modelValue?: string[];
|
|
32
|
+
}>(),
|
|
33
|
+
{
|
|
34
|
+
appearance: 'standard',
|
|
35
|
+
behavior: 'multiple',
|
|
36
|
+
},
|
|
37
|
+
);
|
|
38
|
+
|
|
39
|
+
const classObject = computed(() => {
|
|
40
|
+
return {
|
|
41
|
+
[`mc-accordion--${props.appearance}`]:
|
|
42
|
+
props.appearance && props.appearance != 'standard',
|
|
43
|
+
};
|
|
44
|
+
});
|
|
45
|
+
|
|
46
|
+
const emit = defineEmits<{
|
|
47
|
+
/**
|
|
48
|
+
* Emitted when the accordion items state changes.
|
|
49
|
+
*/
|
|
50
|
+
(on: 'update:modelValue', value: string[]): void;
|
|
51
|
+
}>();
|
|
52
|
+
|
|
53
|
+
defineSlots<{
|
|
54
|
+
/**
|
|
55
|
+
* Use this slot to display accordion items.
|
|
56
|
+
*/
|
|
57
|
+
default: VNode;
|
|
58
|
+
}>();
|
|
59
|
+
|
|
60
|
+
const openIds = computed({
|
|
61
|
+
get() {
|
|
62
|
+
return props.modelValue || [];
|
|
63
|
+
},
|
|
64
|
+
set(value: string[]) {
|
|
65
|
+
emit('update:modelValue', value);
|
|
66
|
+
},
|
|
67
|
+
});
|
|
68
|
+
|
|
69
|
+
function toggleItem(id: string) {
|
|
70
|
+
if (props.behavior === 'single') {
|
|
71
|
+
openIds.value = openIds.value?.[0] === id ? [] : [id];
|
|
72
|
+
} else {
|
|
73
|
+
const set = new Set([...openIds.value]);
|
|
74
|
+
|
|
75
|
+
if (openIds.value?.includes(id)) {
|
|
76
|
+
set.delete(id);
|
|
77
|
+
} else {
|
|
78
|
+
set.add(id);
|
|
79
|
+
}
|
|
80
|
+
|
|
81
|
+
openIds.value = [...set];
|
|
82
|
+
}
|
|
83
|
+
}
|
|
84
|
+
|
|
85
|
+
provide(AccordionStateKey, openIds);
|
|
86
|
+
provide(AccordionToggleFnKey, toggleItem);
|
|
87
|
+
</script>
|
|
88
|
+
|
|
89
|
+
<style lang="scss" scoped>
|
|
90
|
+
@use '@mozaic-ds/styles/components/accordion-list';
|
|
91
|
+
</style>
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
# MAccordionList
|
|
2
|
+
|
|
3
|
+
An Accordion List is a component that organizes content into collapsible sections, allowing users to show or hide related information within a vertically stacked layout. This structure helps optimize space and improve readability by displaying only relevant content when needed. Accordion Lists are commonly used in FAQs, settings menus, and structured content navigation to enhance user experience and reduce visual clutter.
|
|
4
|
+
|
|
5
|
+
|
|
6
|
+
## Props
|
|
7
|
+
|
|
8
|
+
| Name | Description | Type | Default |
|
|
9
|
+
| --- | --- | --- | --- |
|
|
10
|
+
| `appearance` | Defines the visual appearance of the button. | `"standard"` `"ghost"` | `"standard"` |
|
|
11
|
+
| `behavior` | Component behavior: `multiple` allows all items to be open simultaneously, `single` ensures only one item is open at a time. For native HTML implementation, the `single` behavior uses the HTML5 `name` attribute on `<details>` elements for browser-native exclusive behavior.. | `"multiple"` `"single"` | `"multiple"` |
|
|
12
|
+
| `modelValue` | IDs of the Accordion items that are currently open. Updating this array controls which sections are expanded. | `string[]` | - |
|
|
13
|
+
|
|
14
|
+
## Slots
|
|
15
|
+
|
|
16
|
+
| Name | Description |
|
|
17
|
+
| --- | --- |
|
|
18
|
+
| `default` | Use this slot to display accordion items. |
|
|
19
|
+
|
|
20
|
+
## Events
|
|
21
|
+
|
|
22
|
+
| Name | Description | Type |
|
|
23
|
+
| --- | --- | --- |
|
|
24
|
+
| `update:modelValue` | Emitted when the accordion items state changes. | [value: string[]] |
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
import type { InjectionKey, WritableComputedRef } from 'vue';
|
|
2
|
+
|
|
3
|
+
export const AccordionStateKey = Symbol() as InjectionKey<
|
|
4
|
+
WritableComputedRef<string[], string[]>
|
|
5
|
+
>;
|
|
6
|
+
|
|
7
|
+
export const AccordionToggleFnKey = Symbol() as InjectionKey<
|
|
8
|
+
(id: string) => void
|
|
9
|
+
>;
|
|
@@ -0,0 +1,123 @@
|
|
|
1
|
+
import { describe, it, expect, vi } from 'vitest';
|
|
2
|
+
import { mount } from '@vue/test-utils';
|
|
3
|
+
import { defineComponent, ref, type Component } from 'vue';
|
|
4
|
+
import MAccordionListItem from './MAccordionListItem.vue';
|
|
5
|
+
import {
|
|
6
|
+
AccordionStateKey,
|
|
7
|
+
AccordionToggleFnKey,
|
|
8
|
+
} from '../accordionlist/m-accordion-list.const';
|
|
9
|
+
|
|
10
|
+
type Props = {
|
|
11
|
+
id: string;
|
|
12
|
+
title: string;
|
|
13
|
+
subtitle?: string;
|
|
14
|
+
content?: string;
|
|
15
|
+
icon?: Component;
|
|
16
|
+
};
|
|
17
|
+
|
|
18
|
+
describe('MAccordionListItem', () => {
|
|
19
|
+
function mountItem(props: Props, openIds: string[] = []) {
|
|
20
|
+
const state = ref(openIds);
|
|
21
|
+
const toggle = vi.fn();
|
|
22
|
+
|
|
23
|
+
const wrapper = mount(MAccordionListItem, {
|
|
24
|
+
props,
|
|
25
|
+
global: {
|
|
26
|
+
provide: {
|
|
27
|
+
[AccordionStateKey as symbol]: state,
|
|
28
|
+
[AccordionToggleFnKey as symbol]: toggle,
|
|
29
|
+
},
|
|
30
|
+
},
|
|
31
|
+
});
|
|
32
|
+
|
|
33
|
+
return { wrapper, state, toggle };
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
it('renders title and content', () => {
|
|
37
|
+
const { wrapper } = mountItem({
|
|
38
|
+
id: '1',
|
|
39
|
+
title: 'My Title',
|
|
40
|
+
content: 'My Content',
|
|
41
|
+
});
|
|
42
|
+
|
|
43
|
+
expect(wrapper.find('.mc-accordion__title span').text()).toBe('My Title');
|
|
44
|
+
expect(wrapper.find('.mc-accordion__content p').text()).toBe('My Content');
|
|
45
|
+
});
|
|
46
|
+
|
|
47
|
+
it('renders subtitle if provided', () => {
|
|
48
|
+
const { wrapper } = mountItem({
|
|
49
|
+
id: '1',
|
|
50
|
+
title: 'Title',
|
|
51
|
+
subtitle: 'Subtitle',
|
|
52
|
+
});
|
|
53
|
+
|
|
54
|
+
expect(wrapper.find('.mc-accordion__subtitle').text()).toBe('Subtitle');
|
|
55
|
+
});
|
|
56
|
+
|
|
57
|
+
it('renders slot content instead of default content', () => {
|
|
58
|
+
const wrapper = mount(MAccordionListItem, {
|
|
59
|
+
props: { id: '1', title: 'Title', content: 'Default' },
|
|
60
|
+
slots: { default: '<div class="slot-content">Custom Slot</div>' },
|
|
61
|
+
global: {
|
|
62
|
+
provide: {
|
|
63
|
+
[AccordionStateKey as symbol]: ref([]),
|
|
64
|
+
[AccordionToggleFnKey as symbol]: vi.fn(),
|
|
65
|
+
},
|
|
66
|
+
},
|
|
67
|
+
});
|
|
68
|
+
|
|
69
|
+
expect(wrapper.find('.slot-content').exists()).toBe(true);
|
|
70
|
+
expect(wrapper.find('p').exists()).toBe(false);
|
|
71
|
+
});
|
|
72
|
+
|
|
73
|
+
it('sets aria-expanded correctly based on openIds', async () => {
|
|
74
|
+
const { wrapper, state } = mountItem({ id: 'item-1', title: 'Title' }, []);
|
|
75
|
+
|
|
76
|
+
const button = wrapper.find('button');
|
|
77
|
+
expect(button.attributes('aria-expanded')).toBe('false');
|
|
78
|
+
|
|
79
|
+
state.value.push('item-1');
|
|
80
|
+
await wrapper.vm.$nextTick();
|
|
81
|
+
|
|
82
|
+
expect(button.attributes('aria-expanded')).toBe('true');
|
|
83
|
+
});
|
|
84
|
+
|
|
85
|
+
it('sets aria-hidden correctly for content', async () => {
|
|
86
|
+
const { wrapper, state } = mountItem({ id: 'item-1', title: 'Title' }, []);
|
|
87
|
+
|
|
88
|
+
const content = wrapper.find('.mc-accordion__content');
|
|
89
|
+
expect(content.attributes('aria-hidden')).toBe('true');
|
|
90
|
+
|
|
91
|
+
state.value.push('item-1');
|
|
92
|
+
await wrapper.vm.$nextTick();
|
|
93
|
+
expect(content.attributes('aria-hidden')).toBe('false');
|
|
94
|
+
});
|
|
95
|
+
|
|
96
|
+
it('calls toggleItem when button is clicked', async () => {
|
|
97
|
+
const { wrapper, toggle } = mountItem({ id: 'item-1', title: 'Title' });
|
|
98
|
+
|
|
99
|
+
await wrapper.find('button').trigger('click');
|
|
100
|
+
expect(toggle).toHaveBeenCalledOnce();
|
|
101
|
+
expect(toggle).toHaveBeenCalledWith('item-1');
|
|
102
|
+
});
|
|
103
|
+
|
|
104
|
+
it('renders icon if provided', () => {
|
|
105
|
+
const DummyIcon = defineComponent({
|
|
106
|
+
template: '<svg class="dummy-icon" />',
|
|
107
|
+
});
|
|
108
|
+
const { wrapper } = mountItem({ id: '1', title: 'Title', icon: DummyIcon });
|
|
109
|
+
|
|
110
|
+
expect(wrapper.find('.mc-accordion__icon').exists()).toBe(true);
|
|
111
|
+
expect(wrapper.find('svg.dummy-icon').exists()).toBe(true);
|
|
112
|
+
});
|
|
113
|
+
|
|
114
|
+
it('sets correct aria-controls and aria-labelledby attributes', () => {
|
|
115
|
+
const { wrapper } = mountItem({ id: '42', title: 'Title' });
|
|
116
|
+
const button = wrapper.find('button');
|
|
117
|
+
const content = wrapper.find('.mc-accordion__content');
|
|
118
|
+
|
|
119
|
+
expect(button.attributes('aria-controls')).toBe('content-42');
|
|
120
|
+
expect(content.attributes('aria-labelledby')).toBe('accordion-42');
|
|
121
|
+
expect(content.attributes('role')).toBe('region');
|
|
122
|
+
});
|
|
123
|
+
});
|
|
@@ -0,0 +1,95 @@
|
|
|
1
|
+
<template>
|
|
2
|
+
<div class="mc-accordion__item">
|
|
3
|
+
<div class="mc-accordion__header">
|
|
4
|
+
<h2 class="mc-accordion__title">
|
|
5
|
+
<button
|
|
6
|
+
:id="`accordion-${id}`"
|
|
7
|
+
class="mc-accordion__trigger"
|
|
8
|
+
:aria-controls="`content-${id}`"
|
|
9
|
+
:aria-expanded="open"
|
|
10
|
+
@click="toggleItem(id)"
|
|
11
|
+
>
|
|
12
|
+
<component v-if="icon" :is="icon" class="mc-accordion__icon" />
|
|
13
|
+
|
|
14
|
+
<div class="mc-accordion__title-wrapper">
|
|
15
|
+
<span>{{ title }}</span>
|
|
16
|
+
|
|
17
|
+
<span v-if="subtitle" class="mc-accordion__subtitle">
|
|
18
|
+
{{ subtitle }}
|
|
19
|
+
</span>
|
|
20
|
+
</div>
|
|
21
|
+
</button>
|
|
22
|
+
</h2>
|
|
23
|
+
</div>
|
|
24
|
+
<div
|
|
25
|
+
:id="`content-${id}`"
|
|
26
|
+
class="mc-accordion__content"
|
|
27
|
+
:aria-hidden="!open"
|
|
28
|
+
:aria-labelledby="`accordion-${id}`"
|
|
29
|
+
role="region"
|
|
30
|
+
>
|
|
31
|
+
<slot>
|
|
32
|
+
<p v-if="content">{{ content }}</p>
|
|
33
|
+
</slot>
|
|
34
|
+
</div>
|
|
35
|
+
</div>
|
|
36
|
+
</template>
|
|
37
|
+
|
|
38
|
+
<script setup lang="ts">
|
|
39
|
+
import {
|
|
40
|
+
computed,
|
|
41
|
+
inject,
|
|
42
|
+
type Component,
|
|
43
|
+
type ComputedRef,
|
|
44
|
+
type Ref,
|
|
45
|
+
type VNode,
|
|
46
|
+
} from 'vue';
|
|
47
|
+
import {
|
|
48
|
+
AccordionStateKey,
|
|
49
|
+
AccordionToggleFnKey,
|
|
50
|
+
} from '../accordionlist/m-accordion-list.const';
|
|
51
|
+
|
|
52
|
+
const props = defineProps<{
|
|
53
|
+
/**
|
|
54
|
+
* A unique identifier for the accordion item.
|
|
55
|
+
* It links the trigger button (`aria-controls`) to its associated content (`aria-labelledby`),
|
|
56
|
+
* ensuring accessibility and tracking the open/closed state.
|
|
57
|
+
* If no ID is provided, a unique one is generated automatically.
|
|
58
|
+
*/
|
|
59
|
+
id: string;
|
|
60
|
+
/**
|
|
61
|
+
* The main heading of the accordion item. This is the primary text visible to users in the collapsed state and acts as the trigger for expanding or collapsing the content.
|
|
62
|
+
*/
|
|
63
|
+
title: string;
|
|
64
|
+
/**
|
|
65
|
+
* An optional secondary heading displayed below the title. It provides additional context or detail about the content of the accordion item.
|
|
66
|
+
*/
|
|
67
|
+
subtitle?: string;
|
|
68
|
+
/**
|
|
69
|
+
* The main content of the accordion item. This is the information revealed when the accordion is expanded, typically containing text, HTML, or other elements.
|
|
70
|
+
*/
|
|
71
|
+
content?: string;
|
|
72
|
+
/**
|
|
73
|
+
* Icon component to display before the item title.
|
|
74
|
+
*/
|
|
75
|
+
icon?: Component;
|
|
76
|
+
}>();
|
|
77
|
+
|
|
78
|
+
defineSlots<{
|
|
79
|
+
/**
|
|
80
|
+
* Use this slot to display custom content.
|
|
81
|
+
*/
|
|
82
|
+
default: VNode;
|
|
83
|
+
}>();
|
|
84
|
+
|
|
85
|
+
const openIds = inject<Ref<string[]> | ComputedRef<string[]>>(
|
|
86
|
+
AccordionStateKey,
|
|
87
|
+
)!;
|
|
88
|
+
const toggleItem = inject<(id: string) => void>(AccordionToggleFnKey)!;
|
|
89
|
+
|
|
90
|
+
const open = computed(() => openIds.value.includes(props.id));
|
|
91
|
+
</script>
|
|
92
|
+
|
|
93
|
+
<style lang="scss" scoped>
|
|
94
|
+
@use '@mozaic-ds/styles/components/accordion-list';
|
|
95
|
+
</style>
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
# MAccordionListItem
|
|
2
|
+
|
|
3
|
+
A unique identifier for the accordion item. It links the trigger button (`aria-controls`) to its associated content (`aria-labelledby`), ensuring accessibility and tracking the open/closed state. If no ID is provided, a unique one is generated automatically.
|
|
4
|
+
|
|
5
|
+
|
|
6
|
+
## Props
|
|
7
|
+
|
|
8
|
+
| Name | Description | Type | Default |
|
|
9
|
+
| --- | --- | --- | --- |
|
|
10
|
+
| `id*` | A unique identifier for the accordion item.
|
|
11
|
+
It links the trigger button (`aria-controls`) to its associated content (`aria-labelledby`),
|
|
12
|
+
ensuring accessibility and tracking the open/closed state.
|
|
13
|
+
If no ID is provided, a unique one is generated automatically. | `string` | - |
|
|
14
|
+
| `title*` | The main heading of the accordion item. This is the primary text visible to users in the collapsed state and acts as the trigger for expanding or collapsing the content. | `string` | - |
|
|
15
|
+
| `subtitle` | An optional secondary heading displayed below the title. It provides additional context or detail about the content of the accordion item. | `string` | - |
|
|
16
|
+
| `content` | The main content of the accordion item. This is the information revealed when the accordion is expanded, typically containing text, HTML, or other elements. | `string` | - |
|
|
17
|
+
| `icon` | Icon component to display before the item title. | `Component` | - |
|
|
18
|
+
|
|
19
|
+
## Slots
|
|
20
|
+
|
|
21
|
+
| Name | Description |
|
|
22
|
+
| --- | --- |
|
|
23
|
+
| `default` | Use this slot to display custom content. |
|
|
@@ -0,0 +1,52 @@
|
|
|
1
|
+
import { mount } from '@vue/test-utils';
|
|
2
|
+
import { describe, it, expect } from 'vitest';
|
|
3
|
+
import MActionBottomBar from './MActionBottomBar.vue';
|
|
4
|
+
import MDivider from '../divider/MDivider.vue';
|
|
5
|
+
|
|
6
|
+
describe('ActionBottomBar component', () => {
|
|
7
|
+
it('renders the component root element', () => {
|
|
8
|
+
const wrapper = mount(MActionBottomBar);
|
|
9
|
+
expect(wrapper.classes()).toContain('mc-action-bottom-bar');
|
|
10
|
+
});
|
|
11
|
+
|
|
12
|
+
it('applies the shadow class when shadow prop is true', () => {
|
|
13
|
+
const wrapper = mount(MActionBottomBar, {
|
|
14
|
+
props: {
|
|
15
|
+
shadow: true,
|
|
16
|
+
},
|
|
17
|
+
});
|
|
18
|
+
|
|
19
|
+
expect(wrapper.classes()).toContain('mc-action-bottom-bar--shadow');
|
|
20
|
+
});
|
|
21
|
+
|
|
22
|
+
it('does not apply the shadow class when shadow prop is false', () => {
|
|
23
|
+
const wrapper = mount(MActionBottomBar, {
|
|
24
|
+
props: {
|
|
25
|
+
shadow: false,
|
|
26
|
+
},
|
|
27
|
+
});
|
|
28
|
+
|
|
29
|
+
expect(wrapper.classes()).not.toContain('mc-action-bottom-bar--shadow');
|
|
30
|
+
});
|
|
31
|
+
|
|
32
|
+
it('renders the left container element', () => {
|
|
33
|
+
const wrapper = mount(MActionBottomBar);
|
|
34
|
+
const left = wrapper.find('.mc-action-bottom-bar__left');
|
|
35
|
+
|
|
36
|
+
expect(left.exists()).toBe(true);
|
|
37
|
+
});
|
|
38
|
+
|
|
39
|
+
it('renders the right container element', () => {
|
|
40
|
+
const wrapper = mount(MActionBottomBar);
|
|
41
|
+
const right = wrapper.find('.mc-action-bottom-bar__right');
|
|
42
|
+
|
|
43
|
+
expect(right.exists()).toBe(true);
|
|
44
|
+
});
|
|
45
|
+
|
|
46
|
+
it('renders the divider component', () => {
|
|
47
|
+
const wrapper = mount(MActionBottomBar);
|
|
48
|
+
const divider = wrapper.findComponent(MDivider);
|
|
49
|
+
|
|
50
|
+
expect(divider.exists()).toBe(true);
|
|
51
|
+
});
|
|
52
|
+
});
|