@mozaic-ds/vue 1.0.0-rc.3 → 2.1.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/LICENSE +51 -0
- package/README.md +76 -77
- package/dist/mozaic-vue.css +1 -1
- package/dist/mozaic-vue.d.ts +1664 -0
- package/dist/mozaic-vue.js +1943 -0
- package/dist/mozaic-vue.js.map +1 -0
- package/dist/mozaic-vue.umd.cjs +2 -0
- package/dist/mozaic-vue.umd.cjs.map +1 -0
- package/env.d.ts +1 -0
- package/package.json +78 -51
- package/src/components/Contributing.mdx +118 -0
- package/src/components/GettingStarted.mdx +45 -0
- package/src/components/Introduction.mdx +100 -0
- package/src/components/Support.mdx +18 -0
- package/src/components/breadcrumb/MBreadcrumb.spec.ts +105 -0
- package/src/components/breadcrumb/MBreadcrumb.stories.ts +82 -0
- package/src/components/breadcrumb/MBreadcrumb.vue +52 -55
- package/src/components/button/MButton.spec.ts +191 -0
- package/src/components/button/MButton.stories.ts +59 -0
- package/src/components/button/MButton.vue +98 -154
- package/src/components/checkbox/MCheckbox.spec.ts +104 -0
- package/src/components/checkbox/MCheckbox.stories.ts +83 -0
- package/src/components/checkbox/MCheckbox.vue +60 -101
- package/src/components/checkboxgroup/MCheckboxGroup.spec.ts +78 -0
- package/src/components/checkboxgroup/MCheckboxGroup.stories.ts +61 -0
- package/src/components/checkboxgroup/MCheckboxGroup.vue +97 -0
- package/src/components/datepicker/MDatepicker.spec.ts +95 -0
- package/src/components/datepicker/MDatepicker.stories.ts +75 -0
- package/src/components/datepicker/MDatepicker.vue +114 -0
- package/src/components/divider/MDivider.spec.ts +57 -0
- package/src/components/divider/MDivider.stories.ts +64 -0
- package/src/components/divider/MDivider.vue +56 -0
- package/src/components/drawer/MDrawer.spec.ts +100 -0
- package/src/components/drawer/MDrawer.stories.ts +128 -0
- package/src/components/drawer/MDrawer.vue +140 -0
- package/src/components/field/MField.spec.ts +166 -0
- package/src/components/field/MField.stories.ts +369 -0
- package/src/components/field/MField.vue +78 -61
- package/src/components/fieldgroup/MFieldGroup.spec.ts +165 -0
- package/src/components/fieldgroup/MFieldGroup.stories.ts +416 -0
- package/src/components/fieldgroup/MFieldGroup.vue +79 -0
- package/src/components/flag/MFlag.spec.ts +46 -0
- package/src/components/flag/MFlag.stories.ts +46 -0
- package/src/components/flag/MFlag.vue +28 -39
- package/src/components/iconbutton/MIconButton.spec.ts +108 -0
- package/src/components/iconbutton/MIconButton.stories.ts +74 -0
- package/src/components/iconbutton/MIconButton.vue +73 -0
- package/src/components/link/MLink.spec.ts +154 -0
- package/src/components/link/MLink.stories.ts +89 -0
- package/src/components/link/MLink.vue +86 -120
- package/src/components/loader/MLoader.spec.ts +104 -0
- package/src/components/loader/MLoader.stories.ts +43 -0
- package/src/components/loader/MLoader.vue +66 -55
- package/src/components/loadingoverlay/MLoadingOverlay.spec.ts +37 -0
- package/src/components/loadingoverlay/MLoadingOverlay.stories.ts +40 -0
- package/src/components/loadingoverlay/MLoadingOverlay.vue +28 -0
- package/src/components/modal/MModal.spec.ts +103 -0
- package/src/components/modal/MModal.stories.ts +127 -0
- package/src/components/modal/MModal.vue +111 -159
- package/src/components/numberbadge/MNumberBadge.spec.ts +56 -0
- package/src/components/numberbadge/MNumberBadge.stories.ts +48 -0
- package/src/components/numberbadge/MNumberBadge.vue +45 -0
- package/src/components/overlay/MOverlay.spec.ts +51 -0
- package/src/components/overlay/MOverlay.stories.ts +35 -0
- package/src/components/overlay/MOverlay.vue +27 -19
- package/src/components/pagination/MPagination.spec.ts +123 -0
- package/src/components/pagination/MPagination.stories.ts +83 -0
- package/src/components/pagination/MPagination.vue +120 -140
- package/src/components/passwordinput/MPasswordInput.spec.ts +104 -0
- package/src/components/passwordinput/MPasswordInput.stories.ts +75 -0
- package/src/components/passwordinput/MPasswordInput.vue +126 -77
- package/src/components/pincode/MPincode.spec.ts +126 -0
- package/src/components/pincode/MPincode.stories.ts +68 -0
- package/src/components/pincode/MPincode.vue +148 -0
- package/src/components/quantityselector/MQuantitySelector.spec.ts +262 -0
- package/src/components/quantityselector/MQuantitySelector.stories.ts +89 -0
- package/src/components/quantityselector/MQuantitySelector.vue +159 -148
- package/src/components/radio/MRadio.spec.ts +104 -0
- package/src/components/radio/MRadio.stories.ts +68 -0
- package/src/components/radio/MRadio.vue +56 -39
- package/src/components/radiogroup/MRadioGroup.spec.ts +54 -0
- package/src/components/radiogroup/MRadioGroup.stories.ts +61 -0
- package/src/components/radiogroup/MRadioGroup.vue +79 -0
- package/src/components/select/MSelect.spec.ts +114 -0
- package/src/components/select/MSelect.stories.ts +101 -0
- package/src/components/select/MSelect.vue +77 -119
- package/src/components/statusbadge/MStatusBadge.stories.ts +45 -0
- package/src/components/statusbadge/MStatusBadge.vue +40 -0
- package/src/components/statusbadge/MstatusBadge.spec.ts +16 -0
- package/src/components/statusdot/MStatusDot.spec.ts +51 -0
- package/src/components/statusdot/MStatusDot.stories.ts +48 -0
- package/src/components/statusdot/MStatusDot.vue +36 -0
- package/src/components/statusnotification/MStatusNotification.spec.ts +103 -0
- package/src/components/statusnotification/MStatusNotification.stories.ts +89 -0
- package/src/components/statusnotification/MStatusNotification.vue +106 -0
- package/src/components/tabs/MTabs.stories.ts +104 -0
- package/src/components/tabs/MTabs.vue +113 -0
- package/src/components/tabs/Mtabs.spec.ts +149 -0
- package/src/components/tag/MTag.spec.ts +107 -0
- package/src/components/tag/MTag.stories.ts +75 -0
- package/src/components/tag/MTag.vue +151 -0
- package/src/components/textarea/MTextArea.spec.ts +112 -0
- package/src/components/textarea/MTextArea.stories.ts +67 -0
- package/src/components/textarea/MTextArea.vue +81 -43
- package/src/components/textinput/MTextInput.spec.ts +121 -0
- package/src/components/textinput/MTextInput.stories.ts +107 -0
- package/src/components/textinput/MTextInput.vue +127 -47
- package/src/components/toggle/MToggle.spec.ts +99 -0
- package/src/components/toggle/MToggle.stories.ts +68 -0
- package/src/components/toggle/MToggle.vue +63 -103
- 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/components/usingIcons.mdx +35 -0
- package/src/components/usingPresets.mdx +128 -0
- package/src/main.ts +32 -0
- package/dist/demo.html +0 -1
- package/dist/mozaic-vue.adeo.css +0 -47
- package/dist/mozaic-vue.adeo.umd.js +0 -31341
- package/dist/mozaic-vue.common.js +0 -31331
- package/dist/mozaic-vue.common.js.map +0 -1
- package/dist/mozaic-vue.umd.js +0 -31342
- package/dist/mozaic-vue.umd.js.map +0 -1
- package/dist/mozaic-vue.umd.min.js +0 -4
- package/dist/mozaic-vue.umd.min.js.map +0 -1
- package/postinstall.js +0 -3
- package/src/components/accordion/MAccordion.vue +0 -128
- package/src/components/accordion/index.js +0 -7
- package/src/components/autocomplete/MAutocomplete.vue +0 -380
- package/src/components/autocomplete/index.js +0 -7
- package/src/components/badge/MBadge.vue +0 -43
- package/src/components/badge/index.js +0 -7
- package/src/components/breadcrumb/index.js +0 -7
- package/src/components/button/index.js +0 -7
- package/src/components/card/MCard.vue +0 -78
- package/src/components/card/index.js +0 -7
- package/src/components/checkbox/MCheckboxGroup.vue +0 -163
- package/src/components/checkbox/index.js +0 -12
- package/src/components/container/MContainer.vue +0 -33
- package/src/components/container/index.js +0 -7
- package/src/components/datatable/MDataTable.vue +0 -651
- package/src/components/datatable/MDataTableHeader.vue +0 -55
- package/src/components/datatable/MDataTableTop.vue +0 -35
- package/src/components/datatable/helpers.js +0 -132
- package/src/components/datatable/index.js +0 -12
- package/src/components/dropdown/MDropdown.vue +0 -317
- package/src/components/dropdown/index.js +0 -7
- package/src/components/field/index.js +0 -7
- package/src/components/fileuploader/MFileResult.vue +0 -149
- package/src/components/fileuploader/MFileUploader.vue +0 -142
- package/src/components/fileuploader/index.js +0 -7
- package/src/components/flag/index.js +0 -7
- package/src/components/heading/MHeading.vue +0 -75
- package/src/components/heading/index.js +0 -7
- package/src/components/hero/MHero.vue +0 -93
- package/src/components/hero/index.js +0 -7
- package/src/components/icon/MIcon.vue +0 -136
- package/src/components/icon/index.js +0 -7
- package/src/components/index.js +0 -44
- package/src/components/layer/MLayer.vue +0 -208
- package/src/components/layer/index.js +0 -7
- package/src/components/link/index.js +0 -7
- package/src/components/listbox/MListBox.vue +0 -146
- package/src/components/listbox/MListBoxActions.vue +0 -251
- package/src/components/listbox/index.js +0 -12
- package/src/components/loader/index.js +0 -7
- package/src/components/modal/index.js +0 -7
- package/src/components/notification/MNotification.vue +0 -110
- package/src/components/notification/index.js +0 -7
- package/src/components/optionbutton/MOptionButton.vue +0 -67
- package/src/components/optionbutton/index.js +0 -7
- package/src/components/optioncard/MOptionCard.vue +0 -132
- package/src/components/optioncard/index.js +0 -7
- package/src/components/optiongroup/MOptionGroup.vue +0 -18
- package/src/components/optiongroup/index.js +0 -7
- package/src/components/overlay/MOverlayLoader.vue +0 -43
- package/src/components/overlay/index.js +0 -12
- package/src/components/pagination/index.js +0 -7
- package/src/components/passwordinput/index.js +0 -7
- package/src/components/phonenumber/MPhoneNumber.vue +0 -398
- package/src/components/phonenumber/index.js +0 -7
- package/src/components/progressbar/MProgress.vue +0 -102
- package/src/components/progressbar/index.js +0 -7
- package/src/components/quantityselector/index.js +0 -7
- package/src/components/radio/MRadioGroup.vue +0 -111
- package/src/components/radio/index.js +0 -12
- package/src/components/ratingstars/MStarsInput.vue +0 -119
- package/src/components/ratingstars/MStarsResult.vue +0 -89
- package/src/components/ratingstars/index.js +0 -12
- package/src/components/select/index.js +0 -7
- package/src/components/stepper/MStepper.vue +0 -111
- package/src/components/stepper/index.js +0 -7
- package/src/components/tabs/MTab.vue +0 -204
- package/src/components/tabs/index.js +0 -7
- package/src/components/tags/MTag.vue +0 -175
- package/src/components/tags/index.js +0 -7
- package/src/components/textarea/index.js +0 -7
- package/src/components/textinput/MTextInputField.vue +0 -105
- package/src/components/textinput/MTextInputIcon.vue +0 -42
- package/src/components/textinput/index.js +0 -7
- package/src/components/toggle/index.js +0 -7
- package/src/components/tooltip/MTooltip.vue +0 -42
- package/src/components/tooltip/index.js +0 -7
- package/src/index.js +0 -63
- package/src/shims-tsx.d.ts +0 -13
- package/src/shims.vue.d.ts +0 -4
- package/src/tokens/adeo/android/colors.xml +0 -452
- package/src/tokens/adeo/android/font_dimens.xml +0 -18
- package/src/tokens/adeo/css/_variables.scss +0 -446
- package/src/tokens/adeo/css/root.scss +0 -448
- package/src/tokens/adeo/ios/StyleDictionaryColor.h +0 -460
- package/src/tokens/adeo/ios/StyleDictionaryColor.m +0 -472
- package/src/tokens/adeo/ios/StyleDictionaryColor.swift +0 -455
- package/src/tokens/adeo/ios/StyleDictionarySize.h +0 -69
- package/src/tokens/adeo/ios/StyleDictionarySize.m +0 -70
- package/src/tokens/adeo/ios/StyleDictionarySize.swift +0 -71
- package/src/tokens/adeo/js/tokens.js +0 -544
- package/src/tokens/adeo/js/tokensObject.js +0 -11733
- package/src/tokens/adeo/scss/_tokens.scss +0 -1522
- package/src/utils/mozaicClasses.js +0 -16
- package/src/utils/theme.validator.js +0 -19
- package/types/index.d.ts +0 -104
|
@@ -1,54 +1,92 @@
|
|
|
1
1
|
<template>
|
|
2
2
|
<textarea
|
|
3
|
+
:id="id"
|
|
3
4
|
class="mc-textarea"
|
|
4
|
-
:class="
|
|
5
|
-
{ 'is-valid': isValid, 'is-invalid': isInvalid },
|
|
6
|
-
cssFieldElementClass,
|
|
7
|
-
]"
|
|
5
|
+
:class="classObject"
|
|
8
6
|
:aria-invalid="isInvalid"
|
|
9
|
-
:value="
|
|
10
|
-
|
|
11
|
-
|
|
7
|
+
:value="modelValue"
|
|
8
|
+
:name="name"
|
|
9
|
+
:placeholder="placeholder"
|
|
10
|
+
:disabled="disabled"
|
|
11
|
+
:minlength="minLength"
|
|
12
|
+
:maxlength="maxLength"
|
|
13
|
+
:rows="rows"
|
|
14
|
+
:readonly="readonly"
|
|
15
|
+
v-bind="$attrs"
|
|
16
|
+
@input="
|
|
17
|
+
emit('update:modelValue', ($event.target as HTMLInputElement).value)
|
|
18
|
+
"
|
|
12
19
|
/>
|
|
13
20
|
</template>
|
|
14
21
|
|
|
15
|
-
<script>
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
22
|
+
<script setup lang="ts">
|
|
23
|
+
import { computed } from 'vue';
|
|
24
|
+
|
|
25
|
+
/**
|
|
26
|
+
* A textarea is a form element for multi-line text input, ideal for longer content like comments or descriptions.
|
|
27
|
+
*/
|
|
28
|
+
const props = withDefaults(
|
|
29
|
+
defineProps<{
|
|
30
|
+
/**
|
|
31
|
+
* A unique identifier for the textarea, used to associate the label with the form element.
|
|
32
|
+
*/
|
|
33
|
+
id: string;
|
|
34
|
+
/**
|
|
35
|
+
* The name attribute for the textarea element, used for form submission.
|
|
36
|
+
*/
|
|
37
|
+
name?: string;
|
|
38
|
+
/**
|
|
39
|
+
* The current value of the textarea field.
|
|
40
|
+
*/
|
|
41
|
+
modelValue?: string | number;
|
|
42
|
+
/**
|
|
43
|
+
* Text displayed when the textarea is empty.
|
|
44
|
+
*/
|
|
45
|
+
placeholder?: string;
|
|
46
|
+
/**
|
|
47
|
+
* If `true`, the textarea is marked as invalid.
|
|
48
|
+
*/
|
|
49
|
+
isInvalid?: boolean;
|
|
50
|
+
/**
|
|
51
|
+
* If `true`, the textarea is disabled and non-interactive.
|
|
52
|
+
*/
|
|
53
|
+
disabled?: boolean;
|
|
54
|
+
/**
|
|
55
|
+
* The number of visible text lines in the textarea.
|
|
56
|
+
*/
|
|
57
|
+
rows?: number;
|
|
58
|
+
/**
|
|
59
|
+
* Minimum number of characters required for the textarea.
|
|
60
|
+
*/
|
|
61
|
+
minLength?: number;
|
|
62
|
+
/**
|
|
63
|
+
* Maximum number of characters allowed in the textarea.
|
|
64
|
+
*/
|
|
65
|
+
maxLength?: number;
|
|
66
|
+
/**
|
|
67
|
+
* If `true`, the textarea is read-only (cannot be edited).
|
|
68
|
+
*/
|
|
69
|
+
readonly?: boolean;
|
|
70
|
+
}>(),
|
|
71
|
+
{
|
|
72
|
+
rows: 2,
|
|
47
73
|
},
|
|
48
|
-
|
|
74
|
+
);
|
|
75
|
+
|
|
76
|
+
const classObject = computed(() => {
|
|
77
|
+
return {
|
|
78
|
+
'is-invalid': props.isInvalid,
|
|
79
|
+
};
|
|
80
|
+
});
|
|
81
|
+
|
|
82
|
+
const emit = defineEmits<{
|
|
83
|
+
/**
|
|
84
|
+
* Emits when the textarea value changes, updating the modelValue prop.
|
|
85
|
+
*/
|
|
86
|
+
(on: 'update:modelValue', value: string | number): void;
|
|
87
|
+
}>();
|
|
49
88
|
</script>
|
|
50
89
|
|
|
51
|
-
<style lang="scss">
|
|
52
|
-
@
|
|
53
|
-
@import 'components/_c.textarea';
|
|
90
|
+
<style lang="scss" scoped>
|
|
91
|
+
@use '@mozaic-ds/styles/components/textarea';
|
|
54
92
|
</style>
|
|
@@ -0,0 +1,121 @@
|
|
|
1
|
+
import { mount } from '@vue/test-utils';
|
|
2
|
+
import { describe, it, expect } from 'vitest';
|
|
3
|
+
import MTextInput from './MTextInput.vue';
|
|
4
|
+
import Search24 from '@mozaic-ds/icons-vue/src/components/Search24/Search24.vue';
|
|
5
|
+
|
|
6
|
+
describe('MTextInput component', () => {
|
|
7
|
+
it('should render correctly with the given props', () => {
|
|
8
|
+
const wrapper = mount(MTextInput, {
|
|
9
|
+
props: {
|
|
10
|
+
id: 'input-id',
|
|
11
|
+
modelValue: 'test',
|
|
12
|
+
placeholder: 'Enter text',
|
|
13
|
+
inputType: 'text',
|
|
14
|
+
size: 's',
|
|
15
|
+
},
|
|
16
|
+
});
|
|
17
|
+
|
|
18
|
+
const input = wrapper.find('input');
|
|
19
|
+
|
|
20
|
+
expect(input.attributes('id')).toBe('input-id');
|
|
21
|
+
expect(input.attributes('placeholder')).toBe('Enter text');
|
|
22
|
+
expect(input.attributes('type')).toBe('text');
|
|
23
|
+
expect(wrapper.classes()).toContain('mc-text-input--s');
|
|
24
|
+
});
|
|
25
|
+
|
|
26
|
+
it('should update modelValue when input value changes', async () => {
|
|
27
|
+
const wrapper = mount(MTextInput, {
|
|
28
|
+
props: {
|
|
29
|
+
id: 'input-id',
|
|
30
|
+
modelValue: 'initial value',
|
|
31
|
+
},
|
|
32
|
+
});
|
|
33
|
+
|
|
34
|
+
const input = wrapper.find('input');
|
|
35
|
+
await input.setValue('new value');
|
|
36
|
+
|
|
37
|
+
expect(wrapper.emitted()['update:modelValue']).toBeTruthy();
|
|
38
|
+
expect(wrapper.emitted()['update:modelValue'][0]).toEqual(['new value']);
|
|
39
|
+
});
|
|
40
|
+
|
|
41
|
+
it('should render Search24 icon when passed to icon slot', () => {
|
|
42
|
+
const wrapper = mount(MTextInput, {
|
|
43
|
+
props: {
|
|
44
|
+
id: 'input-id',
|
|
45
|
+
modelValue: 'test',
|
|
46
|
+
},
|
|
47
|
+
slots: {
|
|
48
|
+
icon: Search24,
|
|
49
|
+
},
|
|
50
|
+
});
|
|
51
|
+
|
|
52
|
+
const iconWrapper = wrapper.find('.mc-text-input__icon');
|
|
53
|
+
expect(iconWrapper.exists()).toBe(true);
|
|
54
|
+
expect(iconWrapper.findComponent(Search24).exists()).toBe(true);
|
|
55
|
+
});
|
|
56
|
+
|
|
57
|
+
it('should render the clear button when isClearable is true and modelValue is not empty', () => {
|
|
58
|
+
const wrapper = mount(MTextInput, {
|
|
59
|
+
props: {
|
|
60
|
+
id: 'input-id',
|
|
61
|
+
modelValue: 'text content',
|
|
62
|
+
isClearable: true,
|
|
63
|
+
},
|
|
64
|
+
});
|
|
65
|
+
|
|
66
|
+
const clearButton = wrapper.find('.mc-controls-options__button');
|
|
67
|
+
expect(clearButton.exists()).toBe(true);
|
|
68
|
+
});
|
|
69
|
+
|
|
70
|
+
it('should clear the input value when the clear button is clicked', async () => {
|
|
71
|
+
const wrapper = mount(MTextInput, {
|
|
72
|
+
props: {
|
|
73
|
+
id: 'input-id',
|
|
74
|
+
modelValue: 'text content',
|
|
75
|
+
isClearable: true,
|
|
76
|
+
},
|
|
77
|
+
});
|
|
78
|
+
|
|
79
|
+
const clearButton = wrapper.find('.mc-controls-options__button');
|
|
80
|
+
await clearButton.trigger('click');
|
|
81
|
+
|
|
82
|
+
expect(wrapper.vm.modelValue).toBe('');
|
|
83
|
+
expect(wrapper.emitted()['update:modelValue'][0]).toEqual(['']);
|
|
84
|
+
});
|
|
85
|
+
|
|
86
|
+
it('should not render the clear button if isClearable is false', () => {
|
|
87
|
+
const wrapper = mount(MTextInput, {
|
|
88
|
+
props: {
|
|
89
|
+
id: 'input-id',
|
|
90
|
+
modelValue: 'text content',
|
|
91
|
+
isClearable: false,
|
|
92
|
+
},
|
|
93
|
+
});
|
|
94
|
+
|
|
95
|
+
const clearButton = wrapper.find('.mc-controls-options__button');
|
|
96
|
+
expect(clearButton.exists()).toBe(false);
|
|
97
|
+
});
|
|
98
|
+
|
|
99
|
+
it('should apply the correct classes based on validation props', () => {
|
|
100
|
+
const wrapper = mount(MTextInput, {
|
|
101
|
+
props: {
|
|
102
|
+
id: 'input-id',
|
|
103
|
+
modelValue: 'text content',
|
|
104
|
+
isInvalid: true,
|
|
105
|
+
},
|
|
106
|
+
});
|
|
107
|
+
|
|
108
|
+
expect(wrapper.classes()).toContain('is-invalid');
|
|
109
|
+
});
|
|
110
|
+
|
|
111
|
+
it('should not apply is-invalid if not specified', () => {
|
|
112
|
+
const wrapper = mount(MTextInput, {
|
|
113
|
+
props: {
|
|
114
|
+
id: 'input-id',
|
|
115
|
+
modelValue: 'text content',
|
|
116
|
+
},
|
|
117
|
+
});
|
|
118
|
+
|
|
119
|
+
expect(wrapper.classes()).not.toContain('is-invalid');
|
|
120
|
+
});
|
|
121
|
+
});
|
|
@@ -0,0 +1,107 @@
|
|
|
1
|
+
import type { Meta, StoryObj } from '@storybook/vue3-vite';
|
|
2
|
+
import { action } from 'storybook/actions';
|
|
3
|
+
|
|
4
|
+
import MTextInput from './MTextInput.vue';
|
|
5
|
+
import Search24 from '@mozaic-ds/icons-vue/src/components/Search24/Search24.vue';
|
|
6
|
+
|
|
7
|
+
const meta: Meta<typeof MTextInput> = {
|
|
8
|
+
title: 'Form Elements/TextInput',
|
|
9
|
+
component: MTextInput,
|
|
10
|
+
parameters: {
|
|
11
|
+
docs: {
|
|
12
|
+
description: {
|
|
13
|
+
component:
|
|
14
|
+
'A text input is a single-line input that allows users to enter and edit short text-based content. It is commonly used for names, email addresses, search queries, and form entries. Text Inputs often include placeholders, validation rules, and assistive text to guide users and ensure accurate data entry.<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).',
|
|
15
|
+
},
|
|
16
|
+
},
|
|
17
|
+
},
|
|
18
|
+
args: {
|
|
19
|
+
id: 'textInputId',
|
|
20
|
+
placeholder: 'Placeholder',
|
|
21
|
+
},
|
|
22
|
+
render: (args) => ({
|
|
23
|
+
components: { MTextInput, Search24 },
|
|
24
|
+
setup() {
|
|
25
|
+
const handleUpdate = action('update:modelValue');
|
|
26
|
+
|
|
27
|
+
return { args, handleUpdate };
|
|
28
|
+
},
|
|
29
|
+
template: `
|
|
30
|
+
<MTextInput
|
|
31
|
+
v-bind="args"
|
|
32
|
+
@update:modelValue="handleUpdate"
|
|
33
|
+
/>
|
|
34
|
+
`,
|
|
35
|
+
}),
|
|
36
|
+
};
|
|
37
|
+
export default meta;
|
|
38
|
+
type Story = StoryObj<typeof MTextInput>;
|
|
39
|
+
|
|
40
|
+
export const WithValue: Story = {
|
|
41
|
+
args: {
|
|
42
|
+
id: 'withValueId',
|
|
43
|
+
modelValue: 'Value of the input component',
|
|
44
|
+
isClearable: true,
|
|
45
|
+
},
|
|
46
|
+
};
|
|
47
|
+
|
|
48
|
+
export const Default: Story = {};
|
|
49
|
+
|
|
50
|
+
export const Small: Story = {
|
|
51
|
+
args: {
|
|
52
|
+
id: 'smallId',
|
|
53
|
+
size: 's',
|
|
54
|
+
},
|
|
55
|
+
};
|
|
56
|
+
|
|
57
|
+
export const WithIcon: Story = {
|
|
58
|
+
args: {
|
|
59
|
+
id: 'withIconId',
|
|
60
|
+
icon: `
|
|
61
|
+
<template v-slot:icon>
|
|
62
|
+
<Search24/>
|
|
63
|
+
</template>
|
|
64
|
+
`,
|
|
65
|
+
},
|
|
66
|
+
render: (args) => ({
|
|
67
|
+
components: { MTextInput, Search24 },
|
|
68
|
+
setup() {
|
|
69
|
+
return { args };
|
|
70
|
+
},
|
|
71
|
+
template: `
|
|
72
|
+
<MTextInput v-bind="args">
|
|
73
|
+
${args.icon}
|
|
74
|
+
</MTextInput>
|
|
75
|
+
`,
|
|
76
|
+
}),
|
|
77
|
+
};
|
|
78
|
+
|
|
79
|
+
export const minValue: Story = {
|
|
80
|
+
args: {
|
|
81
|
+
id: 'minValueId',
|
|
82
|
+
inputType: 'number',
|
|
83
|
+
modelValue: '4',
|
|
84
|
+
min: '3',
|
|
85
|
+
},
|
|
86
|
+
};
|
|
87
|
+
|
|
88
|
+
export const Disabled: Story = {
|
|
89
|
+
args: {
|
|
90
|
+
id: 'disableId',
|
|
91
|
+
disabled: true,
|
|
92
|
+
},
|
|
93
|
+
};
|
|
94
|
+
|
|
95
|
+
export const ReadOnly: Story = {
|
|
96
|
+
args: {
|
|
97
|
+
id: 'readonlyId',
|
|
98
|
+
readonly: true,
|
|
99
|
+
},
|
|
100
|
+
};
|
|
101
|
+
|
|
102
|
+
export const Invalid: Story = {
|
|
103
|
+
args: {
|
|
104
|
+
id: 'invalidId',
|
|
105
|
+
isInvalid: true,
|
|
106
|
+
},
|
|
107
|
+
};
|
|
@@ -1,58 +1,138 @@
|
|
|
1
1
|
<template>
|
|
2
|
-
<div
|
|
3
|
-
v-if="icon"
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
:
|
|
13
|
-
|
|
2
|
+
<div class="mc-text-input" :class="classObject">
|
|
3
|
+
<span v-if="$slots.icon" class="mc-text-input__icon">
|
|
4
|
+
<slot name="icon" />
|
|
5
|
+
</span>
|
|
6
|
+
|
|
7
|
+
<input
|
|
8
|
+
:id="id"
|
|
9
|
+
class="mc-text-input__control"
|
|
10
|
+
:value="modelValue"
|
|
11
|
+
:type="inputType"
|
|
12
|
+
:name="name"
|
|
13
|
+
:placeholder="placeholder"
|
|
14
|
+
:disabled="disabled"
|
|
15
|
+
:aria-invalid="isInvalid"
|
|
16
|
+
:readonly="readonly"
|
|
17
|
+
v-bind="$attrs"
|
|
18
|
+
@input="
|
|
19
|
+
emit('update:modelValue', ($event.target as HTMLInputElement).value)
|
|
20
|
+
"
|
|
14
21
|
/>
|
|
15
|
-
</div>
|
|
16
22
|
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
23
|
+
<div v-if="isClearable && modelValue" class="mc-controls-options">
|
|
24
|
+
<button
|
|
25
|
+
type="button"
|
|
26
|
+
class="mc-controls-options__button"
|
|
27
|
+
@click="clearValue"
|
|
28
|
+
>
|
|
29
|
+
<CrossCircleFilled24
|
|
30
|
+
class="mc-controls-options__icon"
|
|
31
|
+
aria-hidden="true"
|
|
32
|
+
/>
|
|
33
|
+
<span class="mc-controls-options__label">{clearLabel}</span>
|
|
34
|
+
</button>
|
|
35
|
+
</div>
|
|
36
|
+
</div>
|
|
25
37
|
</template>
|
|
26
38
|
|
|
27
|
-
<script>
|
|
28
|
-
import
|
|
29
|
-
import
|
|
39
|
+
<script setup lang="ts">
|
|
40
|
+
import { computed, ref, type VNode } from 'vue';
|
|
41
|
+
import CrossCircleFilled24 from '@mozaic-ds/icons-vue/src/components/CrossCircleFilled24/CrossCircleFilled24.vue';
|
|
30
42
|
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
43
|
+
/**
|
|
44
|
+
* Inputs are used to create input fields with text on a single line. Their states depends on the user interaction or the context.
|
|
45
|
+
*/
|
|
46
|
+
const props = withDefaults(
|
|
47
|
+
defineProps<{
|
|
48
|
+
/**
|
|
49
|
+
* A unique identifier for the input element, used to associate the label with the form element.
|
|
50
|
+
*/
|
|
51
|
+
id: string;
|
|
52
|
+
/**
|
|
53
|
+
* The name attribute for the input element, typically used for form submission.
|
|
54
|
+
*/
|
|
55
|
+
name?: string;
|
|
56
|
+
/**
|
|
57
|
+
* The current value of the input field.
|
|
58
|
+
*/
|
|
59
|
+
modelValue?: string | number;
|
|
60
|
+
/**
|
|
61
|
+
* A placeholder text to show in the input when it is empty.
|
|
62
|
+
*/
|
|
63
|
+
placeholder?: string;
|
|
64
|
+
/**
|
|
65
|
+
* Defines the type of input
|
|
66
|
+
*/
|
|
67
|
+
inputType?:
|
|
68
|
+
| 'date'
|
|
69
|
+
| 'email'
|
|
70
|
+
| 'number'
|
|
71
|
+
| 'password'
|
|
72
|
+
| 'search'
|
|
73
|
+
| 'tel'
|
|
74
|
+
| 'text';
|
|
75
|
+
/**
|
|
76
|
+
* If `true`, applies an invalid state to the input.
|
|
77
|
+
*/
|
|
78
|
+
isInvalid?: boolean;
|
|
79
|
+
/**
|
|
80
|
+
* If `true`, disables the input, making it non-interactive.
|
|
81
|
+
*/
|
|
82
|
+
disabled?: boolean;
|
|
83
|
+
/**
|
|
84
|
+
* Determines the size of the input
|
|
85
|
+
*/
|
|
86
|
+
size?: 's' | 'm';
|
|
87
|
+
/**
|
|
88
|
+
* If `true`, the input is read-only (cannot be edited).
|
|
89
|
+
*/
|
|
90
|
+
readonly?: boolean;
|
|
91
|
+
/**
|
|
92
|
+
* If `true`, a clear button will appear when the input has a value.
|
|
93
|
+
*/
|
|
94
|
+
isClearable?: boolean;
|
|
95
|
+
/**
|
|
96
|
+
* The label text for the clear button
|
|
97
|
+
*/
|
|
98
|
+
clearLabel?: string;
|
|
99
|
+
}>(),
|
|
100
|
+
{
|
|
101
|
+
inputType: 'text',
|
|
102
|
+
size: 'm',
|
|
103
|
+
clearLabel: 'clear content',
|
|
37
104
|
},
|
|
105
|
+
);
|
|
38
106
|
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
107
|
+
defineSlots<{
|
|
108
|
+
/**
|
|
109
|
+
* Use this slot to insert an icon in the input
|
|
110
|
+
*/
|
|
111
|
+
icon?: VNode;
|
|
112
|
+
}>();
|
|
44
113
|
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
},
|
|
114
|
+
const classObject = computed(() => {
|
|
115
|
+
return {
|
|
116
|
+
[`mc-text-input--${props.size}`]: props.size && props.size != 'm',
|
|
117
|
+
'is-invalid': props.isInvalid,
|
|
118
|
+
};
|
|
119
|
+
});
|
|
120
|
+
|
|
121
|
+
const modelValue = ref(props.modelValue);
|
|
122
|
+
const clearValue = () => {
|
|
123
|
+
modelValue.value = '';
|
|
124
|
+
emit('update:modelValue', '');
|
|
57
125
|
};
|
|
126
|
+
|
|
127
|
+
const emit = defineEmits<{
|
|
128
|
+
/**
|
|
129
|
+
* Emits when the input value changes, updating the `modelValue` prop.
|
|
130
|
+
*/
|
|
131
|
+
(on: 'update:modelValue', value: string | number): void;
|
|
132
|
+
}>();
|
|
58
133
|
</script>
|
|
134
|
+
|
|
135
|
+
<style lang="scss" scoped>
|
|
136
|
+
@use '@mozaic-ds/styles/components/controls-options';
|
|
137
|
+
@use '@mozaic-ds/styles/components/text-input';
|
|
138
|
+
</style>
|
|
@@ -0,0 +1,99 @@
|
|
|
1
|
+
import { mount } from '@vue/test-utils';
|
|
2
|
+
import { describe, it, expect } from 'vitest';
|
|
3
|
+
import MToggle from './MToggle.vue';
|
|
4
|
+
|
|
5
|
+
describe('MToggle Component', () => {
|
|
6
|
+
it('should render the toggle with default props', () => {
|
|
7
|
+
const wrapper = mount(MToggle, {
|
|
8
|
+
props: {
|
|
9
|
+
id: 'test-toggle',
|
|
10
|
+
},
|
|
11
|
+
});
|
|
12
|
+
|
|
13
|
+
const input = wrapper.find('input');
|
|
14
|
+
expect(input.exists()).toBe(true);
|
|
15
|
+
expect(input.attributes().id).toBe('test-toggle');
|
|
16
|
+
expect(input.classes()).toContain('mc-toggle__input');
|
|
17
|
+
});
|
|
18
|
+
|
|
19
|
+
it('should bind and update modelValue (v-model)', async () => {
|
|
20
|
+
const wrapper = mount(MToggle, {
|
|
21
|
+
props: {
|
|
22
|
+
id: 'test-toggle',
|
|
23
|
+
modelValue: false,
|
|
24
|
+
},
|
|
25
|
+
});
|
|
26
|
+
|
|
27
|
+
const input = wrapper.find('input');
|
|
28
|
+
expect(input.element.checked).toBe(false);
|
|
29
|
+
|
|
30
|
+
await input.setChecked(true);
|
|
31
|
+
|
|
32
|
+
const emittedEvent = wrapper.emitted()['update:modelValue'];
|
|
33
|
+
expect(emittedEvent).toBeDefined();
|
|
34
|
+
expect(emittedEvent![0]).toEqual([true]);
|
|
35
|
+
});
|
|
36
|
+
|
|
37
|
+
it('is disabled when the disabled prop is true', () => {
|
|
38
|
+
const wrapper = mount(MToggle, {
|
|
39
|
+
props: {
|
|
40
|
+
id: 'test-toggle',
|
|
41
|
+
disabled: true,
|
|
42
|
+
},
|
|
43
|
+
});
|
|
44
|
+
|
|
45
|
+
const checkbox = wrapper.find('input');
|
|
46
|
+
expect(checkbox.element.disabled).toBe(true);
|
|
47
|
+
});
|
|
48
|
+
|
|
49
|
+
it('should render the label if provided', () => {
|
|
50
|
+
const wrapper = mount(MToggle, {
|
|
51
|
+
props: {
|
|
52
|
+
id: 'test-toggle',
|
|
53
|
+
label: 'Enable feature',
|
|
54
|
+
},
|
|
55
|
+
});
|
|
56
|
+
|
|
57
|
+
const label = wrapper.find('.mc-toggle__label');
|
|
58
|
+
expect(label.exists()).toBe(true);
|
|
59
|
+
expect(label.text()).toBe('Enable feature');
|
|
60
|
+
});
|
|
61
|
+
|
|
62
|
+
it('should not render the label if not provided', () => {
|
|
63
|
+
const wrapper = mount(MToggle, {
|
|
64
|
+
props: {
|
|
65
|
+
id: 'test-toggle',
|
|
66
|
+
},
|
|
67
|
+
});
|
|
68
|
+
|
|
69
|
+
const label = wrapper.find('.mc-toggle__label');
|
|
70
|
+
expect(label.exists()).toBe(false);
|
|
71
|
+
});
|
|
72
|
+
|
|
73
|
+
it('should apply the correct size class based on the size prop', () => {
|
|
74
|
+
const wrapper = mount(MToggle, {
|
|
75
|
+
props: {
|
|
76
|
+
id: 'test-toggle',
|
|
77
|
+
size: 'm',
|
|
78
|
+
},
|
|
79
|
+
});
|
|
80
|
+
|
|
81
|
+
expect(wrapper.classes()).toContain('mc-toggle--m');
|
|
82
|
+
});
|
|
83
|
+
|
|
84
|
+
it('should emit updated modelValue on toggle change', async () => {
|
|
85
|
+
const wrapper = mount(MToggle, {
|
|
86
|
+
props: {
|
|
87
|
+
id: 'test-toggle',
|
|
88
|
+
modelValue: false,
|
|
89
|
+
},
|
|
90
|
+
});
|
|
91
|
+
|
|
92
|
+
const input = wrapper.find('input');
|
|
93
|
+
await input.setChecked(true);
|
|
94
|
+
|
|
95
|
+
const emittedEvent = wrapper.emitted()['update:modelValue'];
|
|
96
|
+
expect(emittedEvent).toBeDefined();
|
|
97
|
+
expect(emittedEvent![0]).toEqual([true]);
|
|
98
|
+
});
|
|
99
|
+
});
|