@mozaic-ds/vue 1.0.0-beta.4 → 1.0.0-beta.7
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/README.md +33 -166
- package/dist/mozaic-vue.css +1 -1
- package/dist/mozaic-vue.d.ts +347 -65
- package/dist/mozaic-vue.js +670 -328
- package/dist/mozaic-vue.js.map +1 -1
- package/dist/mozaic-vue.umd.cjs +1 -1
- package/dist/mozaic-vue.umd.cjs.map +1 -1
- package/package.json +3 -2
- package/src/components/GettingStarted.mdx +16 -5
- package/src/components/Introduction.mdx +35 -9
- package/src/components/Support.mdx +1 -1
- package/src/components/breadcrumb/MBreadcrumb.spec.ts +105 -0
- package/src/components/breadcrumb/MBreadcrumb.stories.ts +84 -0
- package/src/components/breadcrumb/MBreadcrumb.vue +70 -0
- package/src/components/button/MButton.stories.ts +1 -1
- package/src/components/checkbox/MCheckbox.stories.ts +1 -1
- package/src/components/checkboxgroup/MCheckboxGroup.stories.ts +1 -1
- package/src/components/checkboxgroup/MCheckboxGroup.vue +2 -2
- package/src/components/field/MField.stories.ts +1 -1
- package/src/components/fieldgroup/MFieldGroup.stories.ts +175 -26
- package/src/components/iconbutton/MIconButton.stories.ts +1 -1
- package/src/components/link/MLink.vue +1 -1
- package/src/components/loader/MLoader.stories.ts +1 -1
- package/src/components/numberbadge/MNumberBadge.spec.ts +56 -0
- package/src/components/{badge/MBadge.stories.ts → numberbadge/MNumberBadge.stories.ts} +8 -8
- package/src/components/{badge/MBadge.vue → numberbadge/MNumberBadge.vue} +4 -4
- package/src/components/passwordinput/MPasswordInput.spec.ts +104 -0
- package/src/components/passwordinput/MPasswordInput.stories.ts +75 -0
- package/src/components/passwordinput/MPasswordInput.vue +149 -0
- package/src/components/quantityselector/MQuantitySelector.stories.ts +1 -1
- package/src/components/radio/MRadio.stories.ts +1 -1
- package/src/components/radiogroup/MRadioGroup.stories.ts +1 -1
- package/src/components/select/MSelect.stories.ts +1 -1
- package/src/components/statusbadge/MStatusBadge.stories.ts +5 -5
- package/src/components/statusbadge/MStatusBadge.vue +6 -6
- package/src/components/statusdot/MStatusDot.spec.ts +51 -0
- package/src/components/statusdot/MStatusDot.stories.ts +48 -0
- package/src/components/{statusbadge → statusdot}/MStatusDot.vue +8 -4
- package/src/components/statusnotification/MStatusNotification.spec.ts +99 -0
- package/src/components/statusnotification/MStatusNotification.stories.ts +96 -0
- package/src/components/statusnotification/MStatusNotification.vue +106 -0
- package/src/components/textarea/MTextArea.stories.ts +1 -1
- package/src/components/textinput/MTextInput.stories.ts +1 -1
- package/src/components/toggle/MToggle.stories.ts +2 -2
- package/src/components/togglegroup/MToggleGroup.spec.ts +78 -0
- package/src/components/togglegroup/MToggleGroup.stories.ts +61 -0
- package/src/components/togglegroup/MToggleGroup.vue +97 -0
- package/src/main.ts +22 -39
- package/src/components/badge/MBadge.spec.ts +0 -16
|
@@ -0,0 +1,104 @@
|
|
|
1
|
+
import { mount } from '@vue/test-utils';
|
|
2
|
+
import { describe, it, expect } from 'vitest';
|
|
3
|
+
import MPasswordInput from './MPasswordInput.vue';
|
|
4
|
+
|
|
5
|
+
describe('MPasswordInput.vue', () => {
|
|
6
|
+
it('renders correctly with default props', () => {
|
|
7
|
+
const wrapper = mount(MPasswordInput, {
|
|
8
|
+
props: {
|
|
9
|
+
id: 'password-input',
|
|
10
|
+
},
|
|
11
|
+
});
|
|
12
|
+
|
|
13
|
+
const input = wrapper.find('input');
|
|
14
|
+
const toggleButton = wrapper.find('button[role="switch"]');
|
|
15
|
+
|
|
16
|
+
expect(input.exists()).toBe(true);
|
|
17
|
+
expect(input.attributes('type')).toBe('password');
|
|
18
|
+
|
|
19
|
+
expect(toggleButton.exists()).toBe(true);
|
|
20
|
+
expect(toggleButton.text()).toBe('Show');
|
|
21
|
+
});
|
|
22
|
+
|
|
23
|
+
it('toggles password visibility on button click', async () => {
|
|
24
|
+
const wrapper = mount(MPasswordInput, {
|
|
25
|
+
props: {
|
|
26
|
+
id: 'password-input',
|
|
27
|
+
},
|
|
28
|
+
});
|
|
29
|
+
|
|
30
|
+
const toggleButton = wrapper.find('button[role="switch"]');
|
|
31
|
+
const input = wrapper.find('input');
|
|
32
|
+
|
|
33
|
+
expect(input.attributes('type')).toBe('password');
|
|
34
|
+
|
|
35
|
+
await toggleButton.trigger('click');
|
|
36
|
+
|
|
37
|
+
expect(input.attributes('type')).toBe('text');
|
|
38
|
+
expect(toggleButton.text()).toBe('Hide');
|
|
39
|
+
|
|
40
|
+
await toggleButton.trigger('click');
|
|
41
|
+
|
|
42
|
+
expect(input.attributes('type')).toBe('password');
|
|
43
|
+
expect(toggleButton.text()).toBe('Show');
|
|
44
|
+
});
|
|
45
|
+
|
|
46
|
+
it('should update modelValue when input value changes', async () => {
|
|
47
|
+
const wrapper = mount(MPasswordInput, {
|
|
48
|
+
props: {
|
|
49
|
+
id: 'password-input',
|
|
50
|
+
modelValue: '',
|
|
51
|
+
},
|
|
52
|
+
});
|
|
53
|
+
|
|
54
|
+
const input = wrapper.find('input');
|
|
55
|
+
await input.setValue('newPassword123');
|
|
56
|
+
|
|
57
|
+
expect(wrapper.emitted()['update:modelValue']).toBeTruthy();
|
|
58
|
+
expect(wrapper.emitted()['update:modelValue'][0]).toEqual([
|
|
59
|
+
'newPassword123',
|
|
60
|
+
]);
|
|
61
|
+
});
|
|
62
|
+
|
|
63
|
+
it('clears the input when the clear button is clicked', async () => {
|
|
64
|
+
const wrapper = mount(MPasswordInput, {
|
|
65
|
+
props: {
|
|
66
|
+
id: 'password-input',
|
|
67
|
+
modelValue: 'somePassword',
|
|
68
|
+
isClearable: true,
|
|
69
|
+
clearLabel: 'Clear content',
|
|
70
|
+
},
|
|
71
|
+
});
|
|
72
|
+
|
|
73
|
+
const clearButton = wrapper.find('.mc-controls-options__button');
|
|
74
|
+
|
|
75
|
+
await clearButton.trigger('click');
|
|
76
|
+
|
|
77
|
+
expect(wrapper.emitted()['update:modelValue']).toBeTruthy();
|
|
78
|
+
expect(wrapper.emitted()['update:modelValue'][0]).toEqual(['']);
|
|
79
|
+
});
|
|
80
|
+
|
|
81
|
+
it('disables the input when disabled prop is passed', () => {
|
|
82
|
+
const wrapper = mount(MPasswordInput, {
|
|
83
|
+
props: {
|
|
84
|
+
id: 'password-input',
|
|
85
|
+
disabled: true,
|
|
86
|
+
},
|
|
87
|
+
});
|
|
88
|
+
|
|
89
|
+
const input = wrapper.find('input');
|
|
90
|
+
|
|
91
|
+
expect(input.attributes('disabled')).toBeDefined();
|
|
92
|
+
});
|
|
93
|
+
|
|
94
|
+
it('applies invalid state when isInvalid prop is true', () => {
|
|
95
|
+
const wrapper = mount(MPasswordInput, {
|
|
96
|
+
props: {
|
|
97
|
+
id: 'password-input',
|
|
98
|
+
isInvalid: true,
|
|
99
|
+
},
|
|
100
|
+
});
|
|
101
|
+
|
|
102
|
+
expect(wrapper.classes()).toContain('is-invalid');
|
|
103
|
+
});
|
|
104
|
+
});
|
|
@@ -0,0 +1,75 @@
|
|
|
1
|
+
import type { Meta, StoryObj } from '@storybook/vue3';
|
|
2
|
+
import { action } from '@storybook/addon-actions';
|
|
3
|
+
|
|
4
|
+
import MPasswordInput from './MPasswordInput.vue';
|
|
5
|
+
|
|
6
|
+
const meta: Meta<typeof MPasswordInput> = {
|
|
7
|
+
title: 'Form Elements/PasswordInput',
|
|
8
|
+
component: MPasswordInput,
|
|
9
|
+
parameters: {
|
|
10
|
+
docs: {
|
|
11
|
+
description: {
|
|
12
|
+
component:
|
|
13
|
+
'A password input is a specialized input field used to securely enter and manage passwords. It typically masks the characters entered to protect sensitive information from being seen. It includes a toggle button to show or hide the password, improving usability while maintaining security. Password inputs are commonly used in login forms, account creation, and authentication flows.<br><br> To put a label, requierement text, help text or to apply a valid or invalid message, the examples are available in the [Field section](/docs/form-elements-field--docs#input).',
|
|
14
|
+
},
|
|
15
|
+
},
|
|
16
|
+
},
|
|
17
|
+
args: {
|
|
18
|
+
id: 'passwordInputId',
|
|
19
|
+
placeholder: 'Enter your password',
|
|
20
|
+
},
|
|
21
|
+
render: (args) => ({
|
|
22
|
+
components: { MPasswordInput },
|
|
23
|
+
setup() {
|
|
24
|
+
const handleUpdate = action('update:modelValue');
|
|
25
|
+
|
|
26
|
+
return { args, handleUpdate };
|
|
27
|
+
},
|
|
28
|
+
template: `
|
|
29
|
+
<MPasswordInput
|
|
30
|
+
v-bind="args"
|
|
31
|
+
@update:modelValue="handleUpdate"
|
|
32
|
+
/>
|
|
33
|
+
`,
|
|
34
|
+
}),
|
|
35
|
+
};
|
|
36
|
+
export default meta;
|
|
37
|
+
type Story = StoryObj<typeof MPasswordInput>;
|
|
38
|
+
|
|
39
|
+
export const WithValue: Story = {
|
|
40
|
+
args: {
|
|
41
|
+
id: 'withValueId',
|
|
42
|
+
modelValue: 'Magic-word-123',
|
|
43
|
+
},
|
|
44
|
+
};
|
|
45
|
+
|
|
46
|
+
export const Default: Story = {};
|
|
47
|
+
|
|
48
|
+
export const Clearable: Story = {
|
|
49
|
+
args: {
|
|
50
|
+
id: 'clearableId',
|
|
51
|
+
modelValue: 'Magic-word-123',
|
|
52
|
+
isClearable: true,
|
|
53
|
+
},
|
|
54
|
+
};
|
|
55
|
+
|
|
56
|
+
export const Disabled: Story = {
|
|
57
|
+
args: {
|
|
58
|
+
id: 'disableId',
|
|
59
|
+
disabled: true,
|
|
60
|
+
},
|
|
61
|
+
};
|
|
62
|
+
|
|
63
|
+
export const ReadOnly: Story = {
|
|
64
|
+
args: {
|
|
65
|
+
id: 'readonlyId',
|
|
66
|
+
readonly: true,
|
|
67
|
+
},
|
|
68
|
+
};
|
|
69
|
+
|
|
70
|
+
export const Invalid: Story = {
|
|
71
|
+
args: {
|
|
72
|
+
id: 'invalidId',
|
|
73
|
+
isInvalid: true,
|
|
74
|
+
},
|
|
75
|
+
};
|
|
@@ -0,0 +1,149 @@
|
|
|
1
|
+
<template>
|
|
2
|
+
<div class="mc-password-input mc-text-input" :class="classObject">
|
|
3
|
+
<input
|
|
4
|
+
class="mc-password-input__control mc-text-input__control"
|
|
5
|
+
v-model="modelValue"
|
|
6
|
+
:id="id"
|
|
7
|
+
:type="inputType"
|
|
8
|
+
:name="name"
|
|
9
|
+
:placeholder="placeholder"
|
|
10
|
+
:disabled="disabled"
|
|
11
|
+
:aria-invalid="isInvalid"
|
|
12
|
+
:readonly="readonly"
|
|
13
|
+
v-bind="$attrs"
|
|
14
|
+
@input="
|
|
15
|
+
emit('update:modelValue', ($event.target as HTMLInputElement).value)
|
|
16
|
+
"
|
|
17
|
+
/>
|
|
18
|
+
<div v-if="isClearable && modelValue" class="mc-controls-options">
|
|
19
|
+
<button
|
|
20
|
+
class="mc-controls-options__button"
|
|
21
|
+
@click="clearValue"
|
|
22
|
+
>
|
|
23
|
+
<CrossCircleFilled24
|
|
24
|
+
class="mc-controls-options__icon"
|
|
25
|
+
aria-hidden="true"
|
|
26
|
+
/>
|
|
27
|
+
<span class="mc-controls-options__label">{{ clearLabel }}</span>
|
|
28
|
+
</button>
|
|
29
|
+
</div>
|
|
30
|
+
<MButton
|
|
31
|
+
ref="button"
|
|
32
|
+
role="switch"
|
|
33
|
+
:aria-checked="ariaChecked"
|
|
34
|
+
:disabled="disabled"
|
|
35
|
+
@click="toggleVisibility"
|
|
36
|
+
size="s"
|
|
37
|
+
ghost
|
|
38
|
+
>
|
|
39
|
+
{{ isVisible ? buttonLabel.hide : buttonLabel.show }}
|
|
40
|
+
</MButton>
|
|
41
|
+
</div>
|
|
42
|
+
</template>
|
|
43
|
+
|
|
44
|
+
<script setup lang="ts">
|
|
45
|
+
import { computed, ref } from 'vue';
|
|
46
|
+
import CrossCircleFilled24 from '@mozaic-ds/icons-vue/src/components/CrossCircleFilled24/CrossCircleFilled24.vue';
|
|
47
|
+
import MButton from '../button/MButton.vue';
|
|
48
|
+
|
|
49
|
+
/**
|
|
50
|
+
* A password input is a specialized input field used to securely enter and manage passwords. It typically masks the characters entered to protect sensitive information from being seen. It includes a toggle button to show or hide the password, improving usability while maintaining security. Password inputs are commonly used in login forms, account creation, and authentication flows.
|
|
51
|
+
*/
|
|
52
|
+
const props = withDefaults(
|
|
53
|
+
defineProps<{
|
|
54
|
+
/**
|
|
55
|
+
* A unique identifier for the password input element, used to associate the label with the form element.
|
|
56
|
+
*/
|
|
57
|
+
id: string;
|
|
58
|
+
/**
|
|
59
|
+
* The name attribute for the password input element, typically used for form submission.
|
|
60
|
+
*/
|
|
61
|
+
name?: string;
|
|
62
|
+
/**
|
|
63
|
+
* The current value of the password input field.
|
|
64
|
+
*/
|
|
65
|
+
modelValue?: string | number;
|
|
66
|
+
/**
|
|
67
|
+
* A placeholder text to show in the password input when it is empty.
|
|
68
|
+
*/
|
|
69
|
+
placeholder?: string;
|
|
70
|
+
/**
|
|
71
|
+
* If `true`, applies an invalid state to the password input.
|
|
72
|
+
*/
|
|
73
|
+
isInvalid?: boolean;
|
|
74
|
+
/**
|
|
75
|
+
* If `true`, disables the password input, making it non-interactive.
|
|
76
|
+
*/
|
|
77
|
+
disabled?: boolean;
|
|
78
|
+
/**
|
|
79
|
+
* If `true`, the password input is read-only (cannot be edited).
|
|
80
|
+
*/
|
|
81
|
+
readonly?: boolean;
|
|
82
|
+
/**
|
|
83
|
+
* If `true`, a clear button will appear when the password input has a value.
|
|
84
|
+
*/
|
|
85
|
+
isClearable?: boolean;
|
|
86
|
+
/**
|
|
87
|
+
* The label text for the clear button
|
|
88
|
+
*/
|
|
89
|
+
clearLabel?: string;
|
|
90
|
+
/**
|
|
91
|
+
* Labels of the button displayed when showing or hiding the password
|
|
92
|
+
*/
|
|
93
|
+
buttonLabel?: {
|
|
94
|
+
show: string;
|
|
95
|
+
hide: string;
|
|
96
|
+
};
|
|
97
|
+
}>(),
|
|
98
|
+
{
|
|
99
|
+
clearLabel: 'Clear content',
|
|
100
|
+
buttonLabel: () => ({ show: 'Show', hide: 'Hide' }),
|
|
101
|
+
},
|
|
102
|
+
);
|
|
103
|
+
|
|
104
|
+
const classObject = computed(() => ({
|
|
105
|
+
'is-invalid': props.isInvalid,
|
|
106
|
+
}));
|
|
107
|
+
|
|
108
|
+
// Local state management
|
|
109
|
+
const modelValue = ref(props.modelValue);
|
|
110
|
+
const isVisible = ref(false);
|
|
111
|
+
|
|
112
|
+
/**
|
|
113
|
+
* Clear the input value.
|
|
114
|
+
*/
|
|
115
|
+
const clearValue = () => {
|
|
116
|
+
modelValue.value = '';
|
|
117
|
+
emit('update:modelValue', '');
|
|
118
|
+
};
|
|
119
|
+
|
|
120
|
+
/**
|
|
121
|
+
* Toggle the visibility of the password.
|
|
122
|
+
*/
|
|
123
|
+
const toggleVisibility = () => {
|
|
124
|
+
isVisible.value = !isVisible.value;
|
|
125
|
+
};
|
|
126
|
+
|
|
127
|
+
/**
|
|
128
|
+
* Compute the input type based on visibility state.
|
|
129
|
+
*/
|
|
130
|
+
const inputType = computed(() => (isVisible.value ? 'text' : 'password'));
|
|
131
|
+
|
|
132
|
+
/**
|
|
133
|
+
* Aria attributes for accessibility.
|
|
134
|
+
*/
|
|
135
|
+
const ariaChecked = computed(() => (isVisible.value ? 'true' : 'false'));
|
|
136
|
+
|
|
137
|
+
const emit = defineEmits<{
|
|
138
|
+
/**
|
|
139
|
+
* Emits when the input value changes, updating the `modelValue` prop.
|
|
140
|
+
*/
|
|
141
|
+
(on: 'update:modelValue', value: string | number): void;
|
|
142
|
+
}>();
|
|
143
|
+
</script>
|
|
144
|
+
|
|
145
|
+
<style lang="scss" scoped>
|
|
146
|
+
@use '@mozaic-ds/styles/components/controls-options';
|
|
147
|
+
@use '@mozaic-ds/styles/components/text-input';
|
|
148
|
+
@use '@mozaic-ds/styles/components/password-input';
|
|
149
|
+
</style>
|
|
@@ -10,7 +10,7 @@ const meta: Meta<typeof MQuantitySelector> = {
|
|
|
10
10
|
docs: {
|
|
11
11
|
description: {
|
|
12
12
|
component:
|
|
13
|
-
'
|
|
13
|
+
'A quantity selector is an input component that allows users to increment or decrement a numeric value, typically using plus (+) and minus (−) buttons. It provides a simple and efficient way to adjust quantities without manual typing, ensuring controlled input. This component is commonly used in e-commerce, inventory management, and settings where users need to specify amounts.',
|
|
14
14
|
},
|
|
15
15
|
},
|
|
16
16
|
},
|
|
@@ -10,7 +10,7 @@ const meta: Meta<typeof MRadio> = {
|
|
|
10
10
|
docs: {
|
|
11
11
|
description: {
|
|
12
12
|
component:
|
|
13
|
-
'A radio button is
|
|
13
|
+
'A radio button is a selection control that allows users to choose a single option from a list of mutually exclusive choices. Unlike checkboxes, only one option can be selected at a time within the same group. Radio Buttons are commonly used in forms, surveys, and settings where a single choice must be made.',
|
|
14
14
|
},
|
|
15
15
|
},
|
|
16
16
|
},
|
|
@@ -10,7 +10,7 @@ const meta: Meta<typeof MRadioGroup> = {
|
|
|
10
10
|
docs: {
|
|
11
11
|
description: {
|
|
12
12
|
component:
|
|
13
|
-
'A radio button is
|
|
13
|
+
'A radio button is a selection control that allows users to choose a single option from a list of mutually exclusive choices. Unlike checkboxes, only one option can be selected at a time within the same group. Radio Buttons are commonly used in forms, surveys, and settings where a single choice must be made.<br><br> To put a label, requierement text, help text or to apply a valid or invalid message, the examples are available in the [Field Group section](/docs/form-elements-field-group--docs#radio-group).',
|
|
14
14
|
},
|
|
15
15
|
},
|
|
16
16
|
},
|
|
@@ -10,7 +10,7 @@ const meta: Meta<typeof MSelect> = {
|
|
|
10
10
|
docs: {
|
|
11
11
|
description: {
|
|
12
12
|
component:
|
|
13
|
-
'A select
|
|
13
|
+
'A select component allows users to choose a single option from a predefined list within a native dropdown menu. It helps simplify input by displaying only relevant choices, reducing the need for manual text entry. Select components are commonly used in forms, settings, and filters where structured selection is required.<br><br> To put a label, requierement text, help text or to apply a valid or invalid message, the examples are available in the [Field section](/docs/form-elements-field--docs#select).',
|
|
14
14
|
},
|
|
15
15
|
},
|
|
16
16
|
},
|
|
@@ -29,17 +29,17 @@ type Story = StoryObj<typeof MStatusBadge>;
|
|
|
29
29
|
export const Info: Story = {};
|
|
30
30
|
|
|
31
31
|
export const Success: Story = {
|
|
32
|
-
args: {
|
|
32
|
+
args: { status: 'success' },
|
|
33
33
|
};
|
|
34
34
|
|
|
35
35
|
export const Warning: Story = {
|
|
36
|
-
args: {
|
|
36
|
+
args: { status: 'warning' },
|
|
37
37
|
};
|
|
38
38
|
|
|
39
|
-
export const
|
|
40
|
-
args: {
|
|
39
|
+
export const Error: Story = {
|
|
40
|
+
args: { status: 'error' },
|
|
41
41
|
};
|
|
42
42
|
|
|
43
43
|
export const Neutral: Story = {
|
|
44
|
-
args: {
|
|
44
|
+
args: { status: 'neutral' },
|
|
45
45
|
};
|
|
@@ -1,13 +1,13 @@
|
|
|
1
1
|
<template>
|
|
2
2
|
<div class="mc-status-badge" :class="classObject">
|
|
3
|
-
<MStatusDot :
|
|
3
|
+
<MStatusDot :status="status" />
|
|
4
4
|
<span class="mc-status-badge__label">{{ label }}</span>
|
|
5
5
|
</div>
|
|
6
6
|
</template>
|
|
7
7
|
|
|
8
8
|
<script setup lang="ts">
|
|
9
9
|
import { computed } from 'vue';
|
|
10
|
-
import MStatusDot from '
|
|
10
|
+
import MStatusDot from '../statusdot/MStatusDot.vue';
|
|
11
11
|
/**
|
|
12
12
|
* A status badge indicates the status of an entity and can evolve at any time.
|
|
13
13
|
*/
|
|
@@ -20,17 +20,17 @@ const props = withDefaults(
|
|
|
20
20
|
/**
|
|
21
21
|
* Allows to define the Status Badge style
|
|
22
22
|
*/
|
|
23
|
-
|
|
23
|
+
status?: 'info' | 'success' | 'warning' | 'error' | 'neutral';
|
|
24
24
|
}>(),
|
|
25
25
|
{
|
|
26
|
-
|
|
26
|
+
status: 'info',
|
|
27
27
|
},
|
|
28
28
|
);
|
|
29
29
|
|
|
30
30
|
const classObject = computed(() => {
|
|
31
31
|
return {
|
|
32
|
-
[`mc-status-badge--${props.
|
|
33
|
-
props.
|
|
32
|
+
[`mc-status-badge--${props.status}`]:
|
|
33
|
+
props.status && props.status != 'info',
|
|
34
34
|
};
|
|
35
35
|
});
|
|
36
36
|
</script>
|
|
@@ -0,0 +1,51 @@
|
|
|
1
|
+
import { mount } from '@vue/test-utils';
|
|
2
|
+
import { describe, it, expect } from 'vitest';
|
|
3
|
+
import MStatusDot from './MStatusDot.vue';
|
|
4
|
+
|
|
5
|
+
describe('MStatusDot.vue', () => {
|
|
6
|
+
it('renders with default status and size', () => {
|
|
7
|
+
const wrapper = mount(MStatusDot);
|
|
8
|
+
|
|
9
|
+
expect(wrapper.classes()).toContain('mc-status-dot');
|
|
10
|
+
});
|
|
11
|
+
|
|
12
|
+
it('renders with custom status', () => {
|
|
13
|
+
const wrapper = mount(MStatusDot, {
|
|
14
|
+
props: {
|
|
15
|
+
status: 'success',
|
|
16
|
+
},
|
|
17
|
+
});
|
|
18
|
+
|
|
19
|
+
expect(wrapper.classes()).toContain('mc-status-dot--success');
|
|
20
|
+
});
|
|
21
|
+
|
|
22
|
+
it('renders with custom size', () => {
|
|
23
|
+
const wrapper = mount(MStatusDot, {
|
|
24
|
+
props: {
|
|
25
|
+
size: 's',
|
|
26
|
+
},
|
|
27
|
+
});
|
|
28
|
+
|
|
29
|
+
expect(wrapper.classes()).toContain('mc-status-dot--s');
|
|
30
|
+
});
|
|
31
|
+
|
|
32
|
+
it('does not render the "info" status when a custom one is passed', () => {
|
|
33
|
+
const wrapper = mount(MStatusDot, {
|
|
34
|
+
props: {
|
|
35
|
+
status: 'warning',
|
|
36
|
+
},
|
|
37
|
+
});
|
|
38
|
+
|
|
39
|
+
expect(wrapper.classes()).not.toContain('mc-status-dot--info');
|
|
40
|
+
});
|
|
41
|
+
|
|
42
|
+
it('does not render the "m" size when a custom one is passed', () => {
|
|
43
|
+
const wrapper = mount(MStatusDot, {
|
|
44
|
+
props: {
|
|
45
|
+
size: 'l',
|
|
46
|
+
},
|
|
47
|
+
});
|
|
48
|
+
|
|
49
|
+
expect(wrapper.classes()).not.toContain('mc-status-dot--m');
|
|
50
|
+
});
|
|
51
|
+
});
|
|
@@ -0,0 +1,48 @@
|
|
|
1
|
+
import type { Meta, StoryObj } from '@storybook/vue3';
|
|
2
|
+
import MStatusDot from './MStatusDot.vue';
|
|
3
|
+
|
|
4
|
+
const meta: Meta<typeof MStatusDot> = {
|
|
5
|
+
title: 'Status/Status Dot',
|
|
6
|
+
component: MStatusDot,
|
|
7
|
+
parameters: {
|
|
8
|
+
docs: {
|
|
9
|
+
description: {
|
|
10
|
+
component:
|
|
11
|
+
'A Status dot is a small visual indicator used to represent the state or condition of an element. It is often color-coded to convey different statuses at a glance, such as availability, activity, or urgency. Status Dots are commonly found in user presence indicators, system statuses, or process tracking to provide quick, unobtrusive feedback.',
|
|
12
|
+
},
|
|
13
|
+
},
|
|
14
|
+
},
|
|
15
|
+
render: (args) => ({
|
|
16
|
+
components: { MStatusDot },
|
|
17
|
+
setup() {
|
|
18
|
+
return { args };
|
|
19
|
+
},
|
|
20
|
+
template: `
|
|
21
|
+
<MStatusDot v-bind="args"></MStatusDot>
|
|
22
|
+
`,
|
|
23
|
+
}),
|
|
24
|
+
};
|
|
25
|
+
export default meta;
|
|
26
|
+
type Story = StoryObj<typeof MStatusDot>;
|
|
27
|
+
|
|
28
|
+
export const Info: Story = {};
|
|
29
|
+
|
|
30
|
+
export const Success: Story = {
|
|
31
|
+
args: { status: 'success' },
|
|
32
|
+
};
|
|
33
|
+
|
|
34
|
+
export const Warning: Story = {
|
|
35
|
+
args: { status: 'warning' },
|
|
36
|
+
};
|
|
37
|
+
|
|
38
|
+
export const Error: Story = {
|
|
39
|
+
args: { status: 'error' },
|
|
40
|
+
};
|
|
41
|
+
|
|
42
|
+
export const Neutral: Story = {
|
|
43
|
+
args: { status: 'neutral' },
|
|
44
|
+
};
|
|
45
|
+
|
|
46
|
+
export const Size: Story = {
|
|
47
|
+
args: { size: 'l' },
|
|
48
|
+
};
|
|
@@ -12,17 +12,21 @@ const props = withDefaults(
|
|
|
12
12
|
/**
|
|
13
13
|
* Allows to define the Status Dot style
|
|
14
14
|
*/
|
|
15
|
-
|
|
15
|
+
status?: 'info' | 'success' | 'warning' | 'error' | 'neutral';
|
|
16
|
+
/**
|
|
17
|
+
* Determines the size of the Status Dot.
|
|
18
|
+
*/
|
|
19
|
+
size?: 's' | 'm' | 'l';
|
|
16
20
|
}>(),
|
|
17
21
|
{
|
|
18
|
-
|
|
22
|
+
status: 'info',
|
|
19
23
|
},
|
|
20
24
|
);
|
|
21
25
|
|
|
22
26
|
const classObject = computed(() => {
|
|
23
27
|
return {
|
|
24
|
-
[`mc-status-dot--${props.
|
|
25
|
-
|
|
28
|
+
[`mc-status-dot--${props.status}`]: props.status && props.status != 'info',
|
|
29
|
+
[`mc-status-dot--${props.size}`]: props.size && props.size != 'm',
|
|
26
30
|
};
|
|
27
31
|
});
|
|
28
32
|
</script>
|
|
@@ -0,0 +1,99 @@
|
|
|
1
|
+
import { mount } from '@vue/test-utils';
|
|
2
|
+
import { describe, it, expect } from 'vitest';
|
|
3
|
+
import MStatusNotification from './MStatusNotification.vue';
|
|
4
|
+
import InfoCircle32 from '@mozaic-ds/icons-vue/src/components/InfoCircle32/InfoCircle32.vue';
|
|
5
|
+
import CheckCircle32 from '@mozaic-ds/icons-vue/src/components/CheckCircle32/CheckCircle32.vue';
|
|
6
|
+
import WarningCircle32 from '@mozaic-ds/icons-vue/src/components/WarningCircle32/WarningCircle32.vue';
|
|
7
|
+
import CrossCircle32 from '@mozaic-ds/icons-vue/src/components/CrossCircle32/CrossCircle32.vue';
|
|
8
|
+
|
|
9
|
+
describe('MStatusNotification.vue', () => {
|
|
10
|
+
it('should render correctly with the default props', () => {
|
|
11
|
+
const wrapper = mount(MStatusNotification, {
|
|
12
|
+
props: {
|
|
13
|
+
title: 'Test Title',
|
|
14
|
+
description: 'Test Description',
|
|
15
|
+
},
|
|
16
|
+
});
|
|
17
|
+
|
|
18
|
+
expect(wrapper.text()).toContain('Test Title');
|
|
19
|
+
expect(wrapper.text()).toContain('Test Description');
|
|
20
|
+
expect(wrapper.findComponent(InfoCircle32).exists()).toBe(true); // Default icon is InfoCircle32
|
|
21
|
+
expect(wrapper.classes()).toContain('mc-status-notification');
|
|
22
|
+
});
|
|
23
|
+
|
|
24
|
+
it('should display the correct icon based on the status prop', () => {
|
|
25
|
+
const wrapperSuccess = mount(MStatusNotification, {
|
|
26
|
+
props: {
|
|
27
|
+
title: 'Success',
|
|
28
|
+
description: 'Success Description',
|
|
29
|
+
status: 'success',
|
|
30
|
+
},
|
|
31
|
+
});
|
|
32
|
+
expect(wrapperSuccess.findComponent(CheckCircle32).exists()).toBe(true);
|
|
33
|
+
|
|
34
|
+
const wrapperWarning = mount(MStatusNotification, {
|
|
35
|
+
props: {
|
|
36
|
+
title: 'Warning',
|
|
37
|
+
description: 'Warning Description',
|
|
38
|
+
status: 'warning',
|
|
39
|
+
},
|
|
40
|
+
});
|
|
41
|
+
expect(wrapperWarning.findComponent(WarningCircle32).exists()).toBe(true);
|
|
42
|
+
|
|
43
|
+
const wrapperError = mount(MStatusNotification, {
|
|
44
|
+
props: {
|
|
45
|
+
title: 'Error',
|
|
46
|
+
description: 'Error Description',
|
|
47
|
+
status: 'error',
|
|
48
|
+
},
|
|
49
|
+
});
|
|
50
|
+
expect(wrapperError.findComponent(CrossCircle32).exists()).toBe(true);
|
|
51
|
+
});
|
|
52
|
+
|
|
53
|
+
it('should show the close button if closable prop is true', () => {
|
|
54
|
+
const wrapper = mount(MStatusNotification, {
|
|
55
|
+
props: {
|
|
56
|
+
title: 'Closable Test',
|
|
57
|
+
description: 'Test Description',
|
|
58
|
+
closable: true,
|
|
59
|
+
},
|
|
60
|
+
});
|
|
61
|
+
|
|
62
|
+
expect(
|
|
63
|
+
wrapper.find('button.mc-status-notification-closable__close').exists(),
|
|
64
|
+
).toBe(true);
|
|
65
|
+
});
|
|
66
|
+
|
|
67
|
+
it('should emit a close event when the close button is clicked', async () => {
|
|
68
|
+
const wrapper = mount(MStatusNotification, {
|
|
69
|
+
props: {
|
|
70
|
+
title: 'Closable Test',
|
|
71
|
+
description: 'Test Description',
|
|
72
|
+
closable: true,
|
|
73
|
+
},
|
|
74
|
+
});
|
|
75
|
+
|
|
76
|
+
const closeButton = wrapper.find(
|
|
77
|
+
'button.mc-status-notification-closable__close',
|
|
78
|
+
);
|
|
79
|
+
await closeButton.trigger('click');
|
|
80
|
+
|
|
81
|
+
// Check if the "close" event was emitted
|
|
82
|
+
expect(wrapper.emitted()).toHaveProperty('close');
|
|
83
|
+
});
|
|
84
|
+
|
|
85
|
+
it('should render footer slot if provided', () => {
|
|
86
|
+
const footerSlotContent = '<button>Footer Button</button>';
|
|
87
|
+
const wrapper = mount(MStatusNotification, {
|
|
88
|
+
props: {
|
|
89
|
+
title: 'With Footer',
|
|
90
|
+
description: 'Description with footer',
|
|
91
|
+
},
|
|
92
|
+
slots: {
|
|
93
|
+
footer: footerSlotContent,
|
|
94
|
+
},
|
|
95
|
+
});
|
|
96
|
+
|
|
97
|
+
expect(wrapper.html()).toContain(footerSlotContent);
|
|
98
|
+
});
|
|
99
|
+
});
|