Merge branch 'input' into 'main'
Input See merge request cellule-financiere-pmo/design-system/visua-vue!5
This commit is contained in:
commit
6c561029cd
|
@ -2,7 +2,8 @@
|
|||
// import VButtonView from '../template/VButtonView.vue'
|
||||
// import VButtonGroupView from '../template/VButtonGroupView.vue';
|
||||
// import VLinkView from '../template/VLinkView.vue';
|
||||
import VAccordionView from '../template/VAccordionView.vue';
|
||||
// import VAccordionView from '../template/VAccordionView.vue';
|
||||
import VInputView from '../template/VInputView.vue';
|
||||
</script>
|
||||
|
||||
|
||||
|
@ -10,5 +11,6 @@ import VAccordionView from '../template/VAccordionView.vue';
|
|||
<!-- <VButtonView/> -->
|
||||
<!-- <VButtonGroupView/> -->
|
||||
<!-- <VLinkView/> -->
|
||||
<VAccordionView/>
|
||||
<!-- <VAccordionView/> -->
|
||||
<VInputView/>
|
||||
</template>
|
||||
|
|
|
@ -1,2 +1,9 @@
|
|||
@import './primevue-style/button.css';
|
||||
@import './primevue-style/accordion.css';
|
||||
@import './primevue-style/message.css';
|
||||
@import './primevue-style/input.css';
|
||||
@import './primevue-style/password.css';
|
||||
@import './primevue-style/textarea.css';
|
||||
@import './primevue-style/divider.css';
|
||||
@import './primevue-style/various.css';
|
||||
@import './primevue-style/form.css';
|
||||
|
|
11
src/assets/style/primevue-style/divider.css
Normal file
11
src/assets/style/primevue-style/divider.css
Normal file
|
@ -0,0 +1,11 @@
|
|||
:root{
|
||||
--p-divider-vertical-margin: 0 1rem;
|
||||
--p-divider-vertical-padding: 0.5rem 0;
|
||||
--p-divider-vertical-content-padding: 0.5rem 0;
|
||||
--p-divider-horizontal-margin: 1rem 0;
|
||||
--p-divider-horizontal-padding: 0 1rem;
|
||||
--p-divider-horizontal-content-padding: 0 0.5rem;
|
||||
--p-divider-content-background: var(--background-default-grey);
|
||||
--p-divider-content-color: var(--text-label-grey);
|
||||
--p-divider-border-color: var(--border-default-grey);
|
||||
}
|
27
src/assets/style/primevue-style/form.css
Normal file
27
src/assets/style/primevue-style/form.css
Normal file
|
@ -0,0 +1,27 @@
|
|||
:root {
|
||||
--p-form-field-background: var(--input-background);
|
||||
--p-form-field-disabled-background: var(--input-disabled-background);
|
||||
--p-form-field-border-color: var(--input-border-color);
|
||||
--p-form-field-hover-border-color: var(--input-border-color);
|
||||
--p-form-field-focus-border-color: var(--input-border-color);
|
||||
--p-form-field-color: var(--input-color);
|
||||
--p-form-field-disabled-color: var(--input-disabled-color);
|
||||
--p-form-field-placeholder-color: var(--input-color);
|
||||
--p-form-field-icon-color: var(--input-color);
|
||||
--p-form-field-padding-x: var(--input-padding-x);
|
||||
--p-form-field-padding-y: var(--input-padding-y);
|
||||
--p-form-field-border-radius: var(--input-border-raduis);
|
||||
--p-form-field-transition-duration: var(--transition-duration);
|
||||
--p-form-field-focus-ring-width: 0;
|
||||
--p-form-field-focus-ring-style: none;
|
||||
--p-form-field-focus-ring-color: transparent;
|
||||
--p-form-field-focus-ring-offset: 0;
|
||||
--p-form-field-focus-ring-shadow: none;
|
||||
--p-form-field-lg-font-size: 1.125rem;
|
||||
--p-form-field-lg-padding-x: 0.875rem;
|
||||
--p-form-field-lg-padding-y: 0.625rem;
|
||||
--p-form-field-sm-font-size: 0.875rem;
|
||||
--p-form-field-sm-padding-x: 0.625rem;
|
||||
--p-form-field-sm-padding-y: 0.375rem;
|
||||
--p-form-field-invalid-border-color: var(--border-plain-error);
|
||||
}
|
18
src/assets/style/primevue-style/input.css
Normal file
18
src/assets/style/primevue-style/input.css
Normal file
|
@ -0,0 +1,18 @@
|
|||
:root{
|
||||
--p-inputtext-background: var(--input-background);
|
||||
--p-inputtext-disabled-background: var(--input-disabled-background);
|
||||
--p-inputtext-border-color: var(--input-border-color);
|
||||
--p-inputtext-hover-border-color: var(--input-border-color);
|
||||
--p-inputtext-focus-border-color: var(--input-border-color);
|
||||
--p-inputtext-color: var(--input-color);
|
||||
--p-inputtext-disabled-color: var(--input-disabled-color);
|
||||
--p-inputtext-placeholder-color: var(--input-color);
|
||||
--p-inputtext-padding-x: var(--input-padding-x);
|
||||
--p-inputtext-padding-y: var(--input-padding-y);
|
||||
--p-inputtext-border-radius: var(--input-border-raduis);
|
||||
--p-inputtext-focus-ring-color: var(--focus-color);
|
||||
--p-inputtext-focus-ring-width: var(--focus-width);
|
||||
--p-inputtext-focus-ring-style: var(--focus-style);
|
||||
--p-inputtext-focus-ring-offset: var(--focus-offset);
|
||||
--p-inputtext-invalid-border-color: var(--border-plain-error);
|
||||
}
|
87
src/assets/style/primevue-style/message.css
Normal file
87
src/assets/style/primevue-style/message.css
Normal file
|
@ -0,0 +1,87 @@
|
|||
:root {
|
||||
--p-message-simple-content-padding: 0;
|
||||
--p-message-outlined-border-width: var(--border-width);
|
||||
--p-message-close-icon-size: 1rem;
|
||||
--p-message-close-icon-lg-size: 1.125rem;
|
||||
--p-message-close-icon-sm-size: 0.875rem;
|
||||
--p-message-close-button-width: 1.75rem;
|
||||
--p-message-close-button-height: 1.75rem;
|
||||
--p-message-close-button-border-radius: 0px;
|
||||
--p-message-close-button-focus-ring-width: var(--focus-width);
|
||||
--p-message-close-button-focus-ring-style: var(--focus-style);
|
||||
--p-message-close-button-focus-ring-offset: var(--focus-offset);
|
||||
--p-message-icon-size: calc(0.125rem + var(--text-body-MD-standard-text-Regular-size));
|
||||
--p-message-icon-lg-size: calc(0.125rem + var(--text-body-MD-standard-text-Regular-size));
|
||||
--p-message-icon-sm-size: calc(0.125rem + var(--text-body-XS-mention-text-Regular-size));
|
||||
--p-message-text-font-size: var(--text-body-MD-standard-text-Regular-size);
|
||||
--p-message-text-font-weight: var(--text-body-XS-mention-text-Regular-weight);
|
||||
--p-message-text-lg-font-size: var(--text-body-MD-standard-text-Regular-size);
|
||||
--p-message-text-sm-font-size: var(--text-body-XS-mention-text-Regular-size);
|
||||
--p-message-content-padding: 0.5rem 0.75rem;
|
||||
--p-message-content-gap: 0.5rem;
|
||||
--p-message-content-lg-padding: 0.625rem 0.875rem;
|
||||
--p-message-content-sm-padding: 0.375rem 0.625rem;
|
||||
--p-message-border-radius: 0px;
|
||||
--p-message-border-width: var(--border-width);
|
||||
--p-message-transition-duration: var(--transition-duration);
|
||||
/* --p-message-contrast-background:
|
||||
--p-message-contrast-border-color:
|
||||
--p-message-contrast-color:
|
||||
--p-message-contrast-shadow: */
|
||||
--p-message-contrast-simple-color: var(--text-action-high-blue-france);
|
||||
/* --p-message-contrast-outlined-color:
|
||||
--p-message-contrast-outlined-border-color:
|
||||
--p-message-contrast-close-button-hover-background:
|
||||
--p-message-contrast-close-button-focus-ring-color:
|
||||
--p-message-contrast-close-button-focus-ring-shadow: */
|
||||
--p-message-secondary-background: none;
|
||||
--p-message-secondary-border-color: none;
|
||||
--p-message-secondary-color: none;
|
||||
--p-message-secondary-shadow: none;
|
||||
--p-message-secondary-simple-color: var(--text-mention-grey);
|
||||
--p-message-secondary-outlined-color: none;
|
||||
--p-message-secondary-outlined-border-color: none;
|
||||
--p-message-secondary-close-button-hover-background: none;
|
||||
--p-message-secondary-close-button-focus-ring-color: none;
|
||||
--p-message-secondary-close-button-focus-ring-shadow: none;
|
||||
--p-message-error-background: color-mix(in srgb,var(--background-contrast-error),transparent 5%);
|
||||
--p-message-error-border-color: var(--border-plain-error);
|
||||
--p-message-error-color: var(--text-default-grey);
|
||||
--p-message-error-shadow: none;
|
||||
--p-message-error-simple-color: var(--text-default-error);
|
||||
--p-message-error-outlined-color: none;
|
||||
--p-message-error-outlined-border-color: none;
|
||||
--p-message-error-close-button-hover-background: none;
|
||||
--p-message-error-close-button-focus-ring-color: none;
|
||||
--p-message-error-close-button-focus-ring-shadow: none;
|
||||
--p-message-warn-background: color-mix(in srgb,var(--background-contrast-warning),transparent 5%);
|
||||
--p-message-warn-border-color: var(--border-plain-warning);
|
||||
--p-message-warn-color: var(--text-default-grey);
|
||||
--p-message-warn-shadow: none;
|
||||
--p-message-warn-simple-color: var(--text-default-warning);
|
||||
--p-message-warn-outlined-color: none;
|
||||
--p-message-warn-outlined-border-color: none;
|
||||
--p-message-warn-close-button-hover-background: none;
|
||||
--p-message-warn-close-button-focus-ring-color: none;
|
||||
--p-message-warn-close-button-focus-ring-shadow: none;
|
||||
--p-message-success-background: color-mix(in srgb,var(--background-contrast-success),transparent 5%);
|
||||
--p-message-success-border-color: var(--border-plain-success);
|
||||
--p-message-success-color: var(--text-default-grey);
|
||||
--p-message-success-shadow: none;
|
||||
--p-message-success-simple-color: var(--text-default-success);
|
||||
--p-message-success-outlined-color: var(none);
|
||||
--p-message-success-outlined-border-color: var(none);
|
||||
--p-message-success-close-button-hover-background: none;
|
||||
--p-message-success-close-button-focus-ring-color: none;
|
||||
--p-message-success-close-button-focus-ring-shadow: none;
|
||||
--p-message-info-background: color-mix(in srgb,var(--background-contrast-info),transparent 5%);
|
||||
--p-message-info-border-color: var(--border-plain-info);
|
||||
--p-message-info-color: var(--text-default-grey);
|
||||
--p-message-info-shadow: none;
|
||||
--p-message-info-simple-color: var(--text-default-info);
|
||||
--p-message-info-outlined-color: none;
|
||||
--p-message-info-outlined-border-color: none;
|
||||
--p-message-info-close-button-hover-background: none;
|
||||
--p-message-info-close-button-focus-ring-color: none;
|
||||
--p-message-info-close-button-focus-ring-shadow: none;
|
||||
}
|
16
src/assets/style/primevue-style/password.css
Normal file
16
src/assets/style/primevue-style/password.css
Normal file
|
@ -0,0 +1,16 @@
|
|||
:root{
|
||||
--p-password-meter-background: var(--background-contrast-grey);
|
||||
--p-password-meter-border-radius: 0.75rem;
|
||||
--p-password-meter-height: var(--input-padding-y);
|
||||
--p-password-icon-color: var(--input-color);
|
||||
--p-password-overlay-background: var(--input-background);
|
||||
--p-password-overlay-border-color: var(--input-border-color);
|
||||
--p-password-overlay-border-radius: 0px;
|
||||
--p-password-overlay-color: var(--input-color);
|
||||
--p-password-overlay-padding: 0.75rem;
|
||||
--p-password-overlay-shadow: var(--shadow);
|
||||
--p-password-content-gap: 0.125rem;
|
||||
--p-password-strength-weak-background: var(--background-action-high-error);
|
||||
--p-password-strength-medium-background: var(--background-action-high-warning);
|
||||
--p-password-strength-strong-background: var(--background-action-high-success);
|
||||
}
|
17
src/assets/style/primevue-style/textarea.css
Normal file
17
src/assets/style/primevue-style/textarea.css
Normal file
|
@ -0,0 +1,17 @@
|
|||
:root{
|
||||
--p-textarea-background: var(--input-background);
|
||||
--p-textarea-disabled-background: var(--input-disabled-background);
|
||||
--p-textarea-border-color: var(--input-border-color);
|
||||
--p-textarea-hover-border-color: var(--input-border-color);
|
||||
--p-textarea-focus-border-color: var(--input-border-color);
|
||||
--p-textarea-color: var(--input-color);
|
||||
--p-textarea-disabled-color: var(--input-disabled-color);
|
||||
--p-textarea-placeholder-color: var(--input-color);
|
||||
--p-textarea-padding-x: var(--input-padding-x);
|
||||
--p-textarea-padding-y: var(--input-padding-y);
|
||||
--p-textarea-border-radius: var(--input-border-raduis);
|
||||
--p-textarea-focus-ring-color: var(--focus-color);
|
||||
--p-textarea-focus-ring-width: var(--focus-width);
|
||||
--p-textarea-focus-ring-style: var(--focus-style);
|
||||
--p-textarea-focus-ring-offset: var(--focus-offset);
|
||||
}
|
6
src/assets/style/primevue-style/various.css
Normal file
6
src/assets/style/primevue-style/various.css
Normal file
|
@ -0,0 +1,6 @@
|
|||
:root{
|
||||
--p-icon-size: 1rem;
|
||||
--p-text-muted-color: var(--text-action-high-grey);
|
||||
--p-transition-duration: var(--transition-duration);
|
||||
--p-anchor-gutter: 2px;
|
||||
}
|
15
src/components/divider/VDivider.vue
Normal file
15
src/components/divider/VDivider.vue
Normal file
|
@ -0,0 +1,15 @@
|
|||
<script setup lang="ts">
|
||||
import Divider from 'primevue/divider';
|
||||
import type { DividerProps } from 'primevue/divider';
|
||||
|
||||
export type IVDivider = Partial<Omit<DividerProps, 'dt' | 'pt' | 'ptOptions' | 'unstyled'>>
|
||||
const props = withDefaults(defineProps<IVDivider>(), {
|
||||
align: undefined,
|
||||
layout: 'horizontal',
|
||||
type: 'solid',
|
||||
})
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<Divider :align="props.align" :layout="props.layout" :type="props.type"/>
|
||||
</template>
|
18
src/components/group/IVGroup.type.ts
Normal file
18
src/components/group/IVGroup.type.ts
Normal file
|
@ -0,0 +1,18 @@
|
|||
/**
|
||||
* Interface representing the props for the Group component.
|
||||
*/
|
||||
export default interface IVGroup {
|
||||
/**
|
||||
* Defines the visual style or status of the group.
|
||||
* - `'default'`: Standard appearance.
|
||||
* - `'error'`: Indicates an error state.
|
||||
* - `'success'`: Indicates a successful state.
|
||||
* - `undefined`: No specific type applied.
|
||||
*/
|
||||
type: 'default' | 'error' | 'success' | undefined;
|
||||
/**
|
||||
* If true, disables the group component, making it non-interactive.
|
||||
* Optional.
|
||||
*/
|
||||
disabled?: boolean;
|
||||
}
|
34
src/components/group/VGroup.vue
Normal file
34
src/components/group/VGroup.vue
Normal file
|
@ -0,0 +1,34 @@
|
|||
<script setup lang="ts">
|
||||
import type IVGroup from './IVGroup.type';
|
||||
|
||||
const props = withDefaults(defineProps<IVGroup>(), {
|
||||
type: 'default',
|
||||
disabled: false,
|
||||
})
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<div :class="['container', {
|
||||
'error': type === 'error',
|
||||
'success': type === 'success',
|
||||
'disabled': props.disabled,
|
||||
}]"
|
||||
>
|
||||
<slot/>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<style lang="css" scoped>
|
||||
.container {
|
||||
width: 100%;
|
||||
height: fit-content;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
align-items: start;
|
||||
padding: 0 0 0 0.75rem;
|
||||
}
|
||||
|
||||
.container.error {border-left: 2px solid var(--border-plain-error);}
|
||||
.container.success {border-left: 2px solid var(--border-plain-success);}
|
||||
.container.disabled {border: none;}
|
||||
</style>
|
28
src/components/hint/IVHint.type.ts
Normal file
28
src/components/hint/IVHint.type.ts
Normal file
|
@ -0,0 +1,28 @@
|
|||
/**
|
||||
* Interface representing the properties of the Hint component.
|
||||
*/
|
||||
export default interface IVHint {
|
||||
/**
|
||||
* The title or main message displayed in the hint.
|
||||
*/
|
||||
title: string;
|
||||
|
||||
/**
|
||||
* Determines whether an icon should be displayed alongside the hint.
|
||||
* Optional. Defaults to false if not provided.
|
||||
*/
|
||||
icon?: boolean;
|
||||
|
||||
/**
|
||||
* Specifies the type of hint to display.
|
||||
* Can be one of the following: 'info', 'warning', 'alert', 'success', 'active', or an empty string.
|
||||
* Optional.
|
||||
*/
|
||||
type?: 'info' | 'warning' | 'alert' | 'success' | 'active' | '';
|
||||
|
||||
/**
|
||||
* Indicates whether the hint is disabled.
|
||||
* Optional. When true, the hint may appear inactive or be hidden.
|
||||
*/
|
||||
disabled?: boolean;
|
||||
}
|
59
src/components/hint/VHint.vue
Normal file
59
src/components/hint/VHint.vue
Normal file
|
@ -0,0 +1,59 @@
|
|||
<script setup lang="ts">
|
||||
import Message from 'primevue/message';
|
||||
import type IVHint from './IVHint.type';
|
||||
import { computed } from 'vue';
|
||||
import styles from '@visua/typography.module.css';
|
||||
|
||||
const props = withDefaults(defineProps<IVHint>(), {
|
||||
title: '',
|
||||
icon: false,
|
||||
type: '',
|
||||
disabled: false,
|
||||
})
|
||||
|
||||
const iconClass = computed(() => {
|
||||
switch (props.type) {
|
||||
case 'alert': return 'ri-spam-fill';
|
||||
case 'warning' : return 'ri-alert-fill';
|
||||
case 'success' : return 'ri-checkbox-circle-fill';
|
||||
case 'info': return 'ri-information-fill';
|
||||
default:
|
||||
return undefined;
|
||||
}
|
||||
});
|
||||
|
||||
const severity = computed(() => {
|
||||
switch (props.type) {
|
||||
case 'alert': return 'error';
|
||||
case 'warning' : return 'warn';
|
||||
case 'success' : return 'success';
|
||||
case 'info' : return 'info';
|
||||
case 'active': return 'contrast'
|
||||
default:
|
||||
return 'secondary';
|
||||
}
|
||||
});
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<Message
|
||||
variant="simple"
|
||||
:severity="severity"
|
||||
:class="[styles['text-body-XS-mention-text-Regular'], {'disabled': props.disabled }]"
|
||||
:icon="props.icon ? iconClass: undefined"
|
||||
:closable="false"
|
||||
size="small"
|
||||
role="alert"
|
||||
>
|
||||
{{ props.title }}
|
||||
</Message>
|
||||
</template>
|
||||
|
||||
<style scoped>
|
||||
.p-message.disabled {color: var(--text-disabled-grey);}
|
||||
|
||||
.p-message-icon{
|
||||
display: flex;
|
||||
align-items: center;
|
||||
}
|
||||
</style>
|
274
src/components/input/IVInput.type.ts
Normal file
274
src/components/input/IVInput.type.ts
Normal file
|
@ -0,0 +1,274 @@
|
|||
import type { HintedString, Nullable} from '@primevue/core';
|
||||
import type { HTMLAttributes, InputHTMLAttributes } from 'vue';
|
||||
|
||||
/**
|
||||
* Interface representing the props for the InputText component.
|
||||
*/
|
||||
export interface IInputText {
|
||||
/**
|
||||
* The unique identifier for the input element.
|
||||
*/
|
||||
id?: string;
|
||||
|
||||
/**
|
||||
* The ID used to associate the input with a description element for accessibility.
|
||||
*/
|
||||
descriptionId?: string;
|
||||
|
||||
/**
|
||||
* Optional hint text displayed below the input.
|
||||
*/
|
||||
hint?: string;
|
||||
|
||||
/**
|
||||
* Indicates whether the input is in an invalid state.
|
||||
*/
|
||||
isInvalid?: boolean;
|
||||
|
||||
/**
|
||||
* Indicates whether the input is in a valid state.
|
||||
*/
|
||||
isValid?: boolean;
|
||||
|
||||
/**
|
||||
* If true, renders a <textarea> instead of a standard input.
|
||||
*/
|
||||
isTextarea?: boolean;
|
||||
|
||||
/**
|
||||
* If true, wraps the input in an additional wrapper element.
|
||||
*/
|
||||
isWithWrapper?: boolean;
|
||||
|
||||
/**
|
||||
* Controls the visibility of the label. If false, the label is visually hidden.
|
||||
*/
|
||||
labelVisible?: boolean;
|
||||
|
||||
/**
|
||||
* The text content of the label associated with the input.
|
||||
*/
|
||||
label?: string;
|
||||
|
||||
/**
|
||||
* Custom CSS class applied to the label element.
|
||||
*/
|
||||
labelClass?: string;
|
||||
|
||||
/**
|
||||
* The bound value of the input, supporting string or null.
|
||||
*/
|
||||
modelValue?: string | null;
|
||||
|
||||
/**
|
||||
* Custom CSS class applied to the wrapper element.
|
||||
*/
|
||||
wrapperClass?: string;
|
||||
|
||||
/**
|
||||
* If true, the input type is set to "password" to mask the input content.
|
||||
*/
|
||||
isPassword?: boolean;
|
||||
|
||||
/**
|
||||
* If true, disables the input field.
|
||||
*/
|
||||
disabled?: boolean;
|
||||
|
||||
/**
|
||||
* Placeholder text displayed inside the input when it's empty.
|
||||
*/
|
||||
placeholder?: string;
|
||||
|
||||
/**
|
||||
* Error message(s) displayed when the input is invalid.
|
||||
* Can be a single string or an array of strings.
|
||||
*/
|
||||
errorMessage?: string | string[];
|
||||
|
||||
/**
|
||||
* Success message(s) displayed when the input is valid.
|
||||
* Can be a single string or an array of strings.
|
||||
*/
|
||||
validMessage?: string | string[];
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Defines valid properties in Password component.
|
||||
*/
|
||||
export interface PasswordProps {
|
||||
/**
|
||||
* Value of the component.
|
||||
*/
|
||||
modelValue?: Nullable<string>;
|
||||
/**
|
||||
* The default value for the input when not controlled by `modelValue`.
|
||||
*/
|
||||
defaultValue?: Nullable<string>;
|
||||
/**
|
||||
* The name attribute for the element, typically used in form submissions.
|
||||
*/
|
||||
name?: string | undefined;
|
||||
/**
|
||||
* Text to prompt password entry. Defaults to PrimeVue Locale configuration.
|
||||
*/
|
||||
promptLabel?: string | undefined;
|
||||
/**
|
||||
* Regex for a medium level password.
|
||||
* @defaultValue ^(((?=.*[a-z])(?=.*[A-Z]))|((?=.*[a-z])(?=.*[0-9]))|((?=.*[A-Z])(?=.*[0-9])))(?=.{6,})
|
||||
*/
|
||||
mediumRegex?: string | RegExp | undefined;
|
||||
/**
|
||||
* Regex for a strong level password.
|
||||
* @defaultValue ^(?=.*[a-z])(?=.*[A-Z])(?=.*[0-9])(?=.{8,})
|
||||
*/
|
||||
strongRegex?: string | RegExp | undefined;
|
||||
/**
|
||||
* Text for a weak password. Defaults to PrimeVue Locale configuration.
|
||||
*/
|
||||
weakLabel?: string | undefined;
|
||||
/**
|
||||
* Text for a medium password. Defaults to PrimeVue Locale configuration.
|
||||
*/
|
||||
mediumLabel?: string | undefined;
|
||||
/**
|
||||
* Text for a strong password. Defaults to PrimeVue Locale configuration.
|
||||
*/
|
||||
strongLabel?: string | undefined;
|
||||
/**
|
||||
* Whether to show the strength indicator or not.
|
||||
* @defaultValue true
|
||||
*/
|
||||
feedback?: boolean | undefined;
|
||||
/**
|
||||
* A valid query selector or an HTMLElement to specify where the overlay gets attached.
|
||||
* @defaultValue body
|
||||
*/
|
||||
appendTo?: HintedString<'body' | 'self'> | undefined | HTMLElement;
|
||||
/**
|
||||
* Whether to show an icon to display the password as plain text.
|
||||
* @defaultValue false
|
||||
*/
|
||||
toggleMask?: boolean | undefined;
|
||||
/**
|
||||
* Icon to hide displaying the password as plain text.
|
||||
*/
|
||||
maskIcon?: string | undefined;
|
||||
/**
|
||||
* Icon to show displaying the password as plain text.
|
||||
*/
|
||||
unmaskIcon?: string | undefined;
|
||||
/**
|
||||
* Defines the size of the component.
|
||||
*/
|
||||
size?: HintedString<'small' | 'large'> | undefined;
|
||||
/**
|
||||
* When present, it specifies that the component should have invalid state style.
|
||||
* @defaultValue false
|
||||
*/
|
||||
invalid?: boolean | undefined;
|
||||
/**
|
||||
* When present, it specifies that the component should be disabled.
|
||||
* @defaultValue false
|
||||
*/
|
||||
disabled?: boolean | undefined;
|
||||
/**
|
||||
* Specifies the input variant of the component.
|
||||
* @defaultValue null
|
||||
*/
|
||||
variant?: HintedString<'outlined' | 'filled'> | undefined | null;
|
||||
/**
|
||||
* Placeholder text for the input.
|
||||
*/
|
||||
placeholder?: string | undefined;
|
||||
/**
|
||||
* When present, it specifies that an input field must be filled out before submitting the form.
|
||||
* @defaultValue false
|
||||
*/
|
||||
required?: boolean | undefined;
|
||||
/**
|
||||
* Spans 100% width of the container when enabled.
|
||||
* @defaultValue null
|
||||
*/
|
||||
fluid?: boolean | undefined;
|
||||
/**
|
||||
* When present, it specifies that an input element should automatically get focus when the page loads.
|
||||
* @defaultValue null
|
||||
*/
|
||||
autofocus?: boolean | undefined;
|
||||
/**
|
||||
* Identifier of the underlying input element.
|
||||
*/
|
||||
inputId?: string | undefined;
|
||||
/**
|
||||
* Inline style of the input field.
|
||||
*/
|
||||
inputStyle?: object | undefined;
|
||||
/**
|
||||
* Style class of the input field.
|
||||
*/
|
||||
inputClass?: string | object | undefined;
|
||||
/**
|
||||
* Used to pass all properties of the HTMLInputElement to the focusable input element inside the component.
|
||||
*/
|
||||
inputProps?: InputHTMLAttributes | undefined;
|
||||
/**
|
||||
* Identifier of the underlying overlay panel element.
|
||||
*/
|
||||
panelId?: string | undefined;
|
||||
/**
|
||||
* Style class of the overlay panel.
|
||||
*/
|
||||
panelClass?: string | object | undefined;
|
||||
/**
|
||||
* Inline style of the overlay panel.
|
||||
*/
|
||||
panelStyle?: object | undefined;
|
||||
/**
|
||||
* Used to pass all properties of the HTMLDivElement to the overlay panel inside the component.
|
||||
*/
|
||||
panelProps?: HTMLAttributes | undefined;
|
||||
/**
|
||||
* Identifier of the underlying overlay element.
|
||||
*/
|
||||
overlayId?: string | undefined;
|
||||
/**
|
||||
* Style class of the overlay.
|
||||
*/
|
||||
overlayClass?: string | object | undefined;
|
||||
/**
|
||||
* Inline style of the overlay.
|
||||
*/
|
||||
overlayStyle?: object | undefined;
|
||||
/**
|
||||
* Used to pass all properties of the HTMLDivElement to the overlay inside the component.
|
||||
*/
|
||||
overlayProps?: HTMLAttributes | undefined;
|
||||
/**
|
||||
* Establishes relationships between the component and label(s) where its value should be one or more element IDs.
|
||||
*/
|
||||
ariaLabelledby?: string | undefined;
|
||||
/**
|
||||
* Establishes a string value that labels the component.
|
||||
*/
|
||||
ariaLabel?: string | undefined;
|
||||
/**
|
||||
* Form control object, typically used for handling validation and form state.
|
||||
*/
|
||||
}
|
||||
|
||||
/**
|
||||
* Extended interface for the VInputText component.
|
||||
* Combines selected properties from PasswordProps and IInputText,
|
||||
* with additional support for password hints.
|
||||
*/
|
||||
export default interface IVInput
|
||||
extends Partial<Omit<PasswordProps, 'modelValue'>>,
|
||||
Partial<Omit<IInputText, 'wrapperClass' | 'labelClass'>> {
|
||||
/**
|
||||
* Hint(s) displayed to guide the user when entering a password.
|
||||
* Can be a single string or an array of strings.
|
||||
*/
|
||||
passwordHint?: string | Array<string>;
|
||||
}
|
301
src/components/input/VInput.vue
Normal file
301
src/components/input/VInput.vue
Normal file
|
@ -0,0 +1,301 @@
|
|||
<script setup lang="ts">
|
||||
import InputText from 'primevue/inputtext';
|
||||
import Textarea from 'primevue/textarea';
|
||||
import VHint from '../hint/VHint.vue';
|
||||
import VDivider from '../divider/VDivider.vue';
|
||||
import VLabel from '../label/VLabel.vue';
|
||||
import type IVInput from './IVInput.type';
|
||||
import { computed, useAttrs, useId, ref, watch } from 'vue';
|
||||
import styles from '@visua/typography.module.css';
|
||||
import Password from 'primevue/password';
|
||||
|
||||
const props = withDefaults(defineProps<IVInput>(), {
|
||||
id: () => useId(),
|
||||
descriptionId: undefined,
|
||||
hint: '',
|
||||
label: '',
|
||||
modelValue: '',
|
||||
wrapperClass: '',
|
||||
isInvalid: false,
|
||||
isValid: false,
|
||||
isTextarea: false,
|
||||
labelVisible: false,
|
||||
isPassword: false,
|
||||
weakLabel: 'Faible',
|
||||
mediumLabel: 'Moyen',
|
||||
strongLabel: 'Fort',
|
||||
promptLabel: 'Entrez un mot de passe',
|
||||
toggleMask: false,
|
||||
placeholder: '',
|
||||
passwordHint: undefined,
|
||||
disabled: false,
|
||||
errorMessage: '',
|
||||
validMessage: '',
|
||||
feedback: false,
|
||||
})
|
||||
const attrs = useAttrs()
|
||||
|
||||
const emit = defineEmits([
|
||||
'update:modelValue',
|
||||
'value-change',
|
||||
'change'
|
||||
])
|
||||
|
||||
const localModelValue = ref(props.modelValue);
|
||||
watch(() => props.modelValue, (newVal) => {
|
||||
if(localModelValue.value !== newVal) {
|
||||
localModelValue.value = newVal;
|
||||
}
|
||||
});
|
||||
watch(localModelValue, (newVal) => {
|
||||
if(props.modelValue !== newVal){
|
||||
emit('update:modelValue', newVal);
|
||||
}
|
||||
});
|
||||
|
||||
|
||||
|
||||
const labelState = computed(() => {
|
||||
if(!props.isInvalid && !props.isValid && !props.disabled) return 'default';
|
||||
else if(!props.isInvalid && props.isValid && !props.disabled) return 'success';
|
||||
else if(props.isInvalid && !props.isValid && !props.disabled) return 'error';
|
||||
else return undefined
|
||||
})
|
||||
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<div class="main-container">
|
||||
<VLabel
|
||||
v-if="props.labelVisible"
|
||||
:for="props.id"
|
||||
:label="props.label"
|
||||
:required="!props.disabled"
|
||||
:disabled="props.disabled"
|
||||
:type="labelState"
|
||||
:hint="props.hint"
|
||||
>
|
||||
<template #required-type v-if="props.required">
|
||||
<slot name="required-type"/>
|
||||
</template>
|
||||
</VLabel>
|
||||
<!-- Password -->
|
||||
<Password
|
||||
v-if="props.isPassword"
|
||||
:id="props.id"
|
||||
v-bind="attrs"
|
||||
class="p-password"
|
||||
:class="[styles['text-body-MD-standard-text-Regular'], {
|
||||
'error': props.isInvalid && !props.isValid && !props.disabled || !!props.errorMessage,
|
||||
'success': !props.isInvalid && props.isValid && !props.disabled && !props.errorMessage || !!props.validMessage,
|
||||
'disabled': props.disabled,
|
||||
}]"
|
||||
:disabled="props.disabled"
|
||||
:aria-disabled="props.disabled"
|
||||
:aria-describedby="descriptionId || undefined"
|
||||
:weakLabel="props.weakLabel"
|
||||
:mediumLabel="props.mediumLabel"
|
||||
:strongLabel="props.strongLabel"
|
||||
:promptLabel="props.promptLabel"
|
||||
:toggleMask="props.toggleMask && !props.disabled"
|
||||
:placeholder="props.placeholder"
|
||||
:feedback="props.feedback"
|
||||
:fluid="true"
|
||||
v-model:model-value="localModelValue"
|
||||
@update:model-value="emit('update:modelValue', $event)"
|
||||
@valueChange="emit('value-change', $event)"
|
||||
@change="emit('change', $event)"
|
||||
>
|
||||
<template #header>
|
||||
<div
|
||||
:class="styles['text-body-MD-standard-text-Medium']"
|
||||
style="padding-bottom: 0.5rem;"
|
||||
>Niveau de complexité</div>
|
||||
</template>
|
||||
<template v-if="props.passwordHint !== undefined" #footer>
|
||||
<VDivider/>
|
||||
<VHint title="Votre mot de passe doit contenir au moins :"/>
|
||||
<div
|
||||
role="alert"
|
||||
aria-live="polite"
|
||||
>
|
||||
<template v-if="Array.isArray(props.passwordHint)">
|
||||
<VHint
|
||||
v-for="message in props.passwordHint"
|
||||
:key="`password-hint-${message}`"
|
||||
:id="descriptionId"
|
||||
:data-testid="descriptionId"
|
||||
:title="message"
|
||||
type="info"
|
||||
icon
|
||||
/>
|
||||
</template>
|
||||
<VHint
|
||||
v-else-if="props.passwordHint"
|
||||
:title="props.passwordHint"
|
||||
:id="descriptionId"
|
||||
:data-testid="descriptionId"
|
||||
type="info"
|
||||
icon
|
||||
/>
|
||||
</div>
|
||||
</template>
|
||||
</Password>
|
||||
|
||||
<!-- Textarea -->
|
||||
<Textarea
|
||||
v-else-if="props.isTextarea"
|
||||
:id="props.id"
|
||||
v-bind="attrs"
|
||||
:class="[styles['text-body-MD-standard-text-Regular'], {
|
||||
'p-textarea': true,
|
||||
'error': props.isInvalid && !props.isValid && !props.disabled || !!props.errorMessage,
|
||||
'success': !props.isInvalid && props.isValid && !props.disabled && !props.errorMessage || !!props.validMessage,
|
||||
}]"
|
||||
:disabled="props.disabled"
|
||||
:aria-disabled="props.disabled"
|
||||
:aria-describedby="descriptionId || undefined"
|
||||
:placeholder="props.placeholder"
|
||||
@valueChange="emit('value-change', $event)"
|
||||
/>
|
||||
|
||||
<!-- InputText -->
|
||||
<InputText
|
||||
v-else
|
||||
:id="props.id"
|
||||
v-bind="attrs"
|
||||
:class="[styles['text-body-MD-standard-text-Regular'], {
|
||||
'p-inputtext': true,
|
||||
'error': props.isInvalid && !props.isValid && !props.disabled || !!props.errorMessage,
|
||||
'success': !props.isInvalid && props.isValid && !props.disabled && !props.errorMessage || !!props.validMessage,
|
||||
}]"
|
||||
:disabled="props.disabled"
|
||||
:aria-disabled="props.disabled"
|
||||
:aria-describedby="descriptionId || undefined"
|
||||
:placeholder="props.placeholder"
|
||||
v-model:model-value="localModelValue"
|
||||
@update:model-value="emit('update:modelValue', $event)"
|
||||
@valueChange="emit('value-change', $event)"
|
||||
/>
|
||||
<div
|
||||
role="alert"
|
||||
aria-live="polite"
|
||||
>
|
||||
<template v-if="Array.isArray(props.errorMessage)">
|
||||
<VHint
|
||||
v-for="message in props.errorMessage"
|
||||
:id="descriptionId"
|
||||
:key="message"
|
||||
:data-testid="descriptionId"
|
||||
:title="message"
|
||||
type="alert"
|
||||
icon
|
||||
/>
|
||||
</template>
|
||||
<VHint
|
||||
v-else-if="errorMessage"
|
||||
:id="descriptionId"
|
||||
:key="`error-${errorMessage}`"
|
||||
:data-testid="descriptionId"
|
||||
:title="`${errorMessage}`"
|
||||
type="alert"
|
||||
icon
|
||||
/>
|
||||
<template v-if="Array.isArray(props.validMessage)">
|
||||
<VHint
|
||||
v-for="message in props.validMessage"
|
||||
:id="descriptionId"
|
||||
:key="message"
|
||||
:data-testid="descriptionId"
|
||||
:title="message"
|
||||
type="success"
|
||||
icon
|
||||
/>
|
||||
</template>
|
||||
<VHint
|
||||
v-else-if="validMessage"
|
||||
:id="descriptionId"
|
||||
:key="`error-${validMessage}`"
|
||||
:data-testid="descriptionId"
|
||||
:title="`${validMessage}`"
|
||||
type="success"
|
||||
icon
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<style lang="css" scoped>
|
||||
|
||||
.main-container {
|
||||
width: 100%;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
align-items: start;
|
||||
gap: 0.5rem;
|
||||
}
|
||||
|
||||
.p-textarea {
|
||||
min-height: 5.5rem;
|
||||
max-height: fit-content;
|
||||
}
|
||||
|
||||
.p-password,
|
||||
.p-inputtext { height: 2.5rem;}
|
||||
|
||||
.p-password,
|
||||
.p-textarea,
|
||||
.p-inputtext {width: inherit;}
|
||||
|
||||
|
||||
.p-textarea.success, .p-textarea.success:focus-visible, .p-textarea.success:hover,
|
||||
.p-inputtext.success, .p-inputtext.success:focus-visible, .p-inputtext.success:hover {
|
||||
border: var(--large-border-width) solid var(--border-plain-success);
|
||||
}
|
||||
|
||||
|
||||
.p-password.success{
|
||||
--p-inputtext-border-color: transparent;
|
||||
--p-inputtext-hover-border-color: transparent;
|
||||
--p-inputtext-focus-border-color: transparent;
|
||||
border-radius: var(--input-border-raduis);
|
||||
border: var(--large-border-width) solid var(--border-plain-success);
|
||||
}
|
||||
|
||||
.p-password.error{
|
||||
--p-inputtext-border-color: transparent;
|
||||
--p-inputtext-hover-border-color: transparent;
|
||||
--p-inputtext-focus-border-color: transparent;
|
||||
border-radius: var(--input-border-raduis);
|
||||
border: var(--large-border-width) solid var(--border-plain-error);
|
||||
}
|
||||
|
||||
.p-textarea.error, .p-textarea.error:focus-visible, .p-textarea.error:hover,
|
||||
.p-inputtext.error, .p-inputtext.error:focus-visible, .p-inputtext.error:hover {border: var(--large-border-width) solid var(--border-plain-error);}
|
||||
|
||||
|
||||
.p-textarea:disabled,
|
||||
.p-inputtext:disabled {
|
||||
border: var(--border-width) solid var(--border-disabled-grey);
|
||||
color: var(--text-disabled-grey);
|
||||
background-color: var(--background-disabled-grey);
|
||||
--p-inputtext-placeholder-color: var(--text-disabled-grey);
|
||||
--p-form-field-placeholder-color: var(--text-disabled-grey);
|
||||
--p-textarea-placeholder-color: var(--text-disabled-grey);
|
||||
--p-form-field-border-color: var(--border-disabled-grey);
|
||||
--p-inputtext-border-color: var(--border-disabled-grey);
|
||||
-webkit-user-select: none;
|
||||
user-select: none;
|
||||
}
|
||||
|
||||
.p-password.disabled{
|
||||
--p-inputtext-border-color: var(--border-disabled-grey);
|
||||
--p-inputtext-hover-border-color: var(--border-disabled-grey);
|
||||
--p-inputtext-focus-border-color: var(--border-disabled-grey);
|
||||
--p-inputtext-placeholder-color: var(--text-disabled-grey);
|
||||
--p-form-field-placeholder-color: var(--text-disabled-grey);
|
||||
-webkit-user-select: none;
|
||||
user-select: none;
|
||||
}
|
||||
|
||||
</style>
|
46
src/components/label/IVLabel.type.ts
Normal file
46
src/components/label/IVLabel.type.ts
Normal file
|
@ -0,0 +1,46 @@
|
|||
/**
|
||||
* Interface representing the props for the Label component.
|
||||
*/
|
||||
export default interface IVLabel {
|
||||
/**
|
||||
* The text content displayed as the label.
|
||||
*/
|
||||
label: string;
|
||||
|
||||
/**
|
||||
* The visual style of the label, indicating its semantic meaning.
|
||||
* - `default`: Standard label appearance.
|
||||
* - `error`: Indicates an error state.
|
||||
* - `success`: Indicates a successful or valid state.
|
||||
*
|
||||
* @default 'default'
|
||||
*/
|
||||
type?: 'default' | 'error' | 'success' | undefined;
|
||||
|
||||
/**
|
||||
* Whether the label is disabled. When true, the label appears inactive or grayed out.
|
||||
*
|
||||
* @default false
|
||||
*/
|
||||
disabled?: boolean;
|
||||
|
||||
/**
|
||||
* Optional hint text displayed alongside or below the label to provide additional context.
|
||||
*/
|
||||
hint?: string;
|
||||
|
||||
/**
|
||||
* Indicates whether the associated field is required.
|
||||
*
|
||||
* @default false
|
||||
*/
|
||||
required?: boolean;
|
||||
|
||||
/**
|
||||
* The ID of the input element that this label is associated with.
|
||||
* This helps improve accessibility by linking the label to its corresponding input.
|
||||
*/
|
||||
for: string;
|
||||
|
||||
}
|
||||
|
47
src/components/label/VLabel.vue
Normal file
47
src/components/label/VLabel.vue
Normal file
|
@ -0,0 +1,47 @@
|
|||
<script setup lang="ts">
|
||||
import type IVLabel from './IVLabel.type';
|
||||
import styles from '@visua/typography.module.css';
|
||||
import VHint from '../hint/VHint.vue';
|
||||
|
||||
const props = withDefaults(defineProps<IVLabel>(), {
|
||||
type: 'default',
|
||||
disabled: false,
|
||||
required: false,
|
||||
hint: undefined,
|
||||
});
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<label
|
||||
:for="props.for"
|
||||
:class="[styles['text-body-MD-standard-text-Regular'], {
|
||||
'label': props.type === 'default',
|
||||
'success': props.type === 'success',
|
||||
'error': props.type === 'error',
|
||||
'disabled': props.disabled,
|
||||
}]"
|
||||
:aria-label="props.label"
|
||||
:aria-disabled="props.disabled"
|
||||
>
|
||||
{{ props.label }}
|
||||
<slot name="required-type">
|
||||
<span
|
||||
v-if="props.required"
|
||||
:class="{ 'required': props.required }"
|
||||
>*</span>
|
||||
</slot>
|
||||
<VHint
|
||||
v-if="props.hint"
|
||||
:title="props.hint"
|
||||
:disabled="props.disabled"
|
||||
/>
|
||||
</label>
|
||||
</template>
|
||||
|
||||
<style lang="css" scoped>
|
||||
.label {color: var(--text-label-grey);}
|
||||
.success {color: var(--text-default-success);}
|
||||
.error {color: var(--text-default-error);}
|
||||
.disabled {color: var(--text-disabled-grey);}
|
||||
.required {color: var(--minor-red-marianne);}
|
||||
</style>
|
50
test/VGroup.spec.ts
Normal file
50
test/VGroup.spec.ts
Normal file
|
@ -0,0 +1,50 @@
|
|||
import { describe, test, expect } from 'vitest'
|
||||
import { mount } from '@vue/test-utils'
|
||||
import VGroup from '../src/components/group/VGroup.vue'
|
||||
|
||||
describe('VGroup', () => {
|
||||
test('renders default slot content', () => {
|
||||
const wrapper = mount(VGroup, {
|
||||
props: {type: undefined},
|
||||
slots: {
|
||||
default: '<span>Test Content</span>',
|
||||
},
|
||||
})
|
||||
expect(wrapper.html()).toContain('Test Content')
|
||||
})
|
||||
|
||||
test('applies default class when no type is specified', () => {
|
||||
const wrapper = mount(VGroup)
|
||||
expect(wrapper.classes()).toContain('container')
|
||||
expect(wrapper.classes()).not.toContain('error')
|
||||
expect(wrapper.classes()).not.toContain('success')
|
||||
})
|
||||
|
||||
test('applies "error" class when type is "error"', () => {
|
||||
const wrapper = mount(VGroup, {
|
||||
props: { type: 'error' },
|
||||
})
|
||||
expect(wrapper.classes()).toContain('error')
|
||||
})
|
||||
|
||||
test('applies "success" class when type is "success"', () => {
|
||||
const wrapper = mount(VGroup, {
|
||||
props: { type: 'success' },
|
||||
})
|
||||
expect(wrapper.classes()).toContain('success')
|
||||
})
|
||||
|
||||
test('applies "disabled" class when disabled is true', () => {
|
||||
const wrapper = mount(VGroup, {
|
||||
props: { disabled: true, type: undefined },
|
||||
})
|
||||
expect(wrapper.classes()).toContain('disabled')
|
||||
})
|
||||
|
||||
test('does not apply "disabled" class when disabled is false', () => {
|
||||
const wrapper = mount(VGroup, {
|
||||
props: { disabled: false, type: undefined },
|
||||
})
|
||||
expect(wrapper.classes()).not.toContain('disabled')
|
||||
})
|
||||
})
|
190
test/VInput.spec.ts
Normal file
190
test/VInput.spec.ts
Normal file
|
@ -0,0 +1,190 @@
|
|||
import { describe, it, expect } from 'vitest'
|
||||
import { mount } from '@vue/test-utils'
|
||||
import VInput from '../src/components/input/VInput.vue'
|
||||
|
||||
const globalConfig = {
|
||||
global: {
|
||||
mocks: {
|
||||
$primevue: {
|
||||
config: {}
|
||||
}
|
||||
},
|
||||
stubs: {
|
||||
VHint: true,
|
||||
VDivider: true,
|
||||
Password: true,
|
||||
Textarea: true,
|
||||
VLabel: true,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
const noStubVLabelConfig = {
|
||||
...globalConfig.global,
|
||||
stubs: {
|
||||
...globalConfig.global.stubs,
|
||||
VLabel: false,
|
||||
}
|
||||
}
|
||||
|
||||
const noStubTextareaConfig = {
|
||||
...globalConfig.global,
|
||||
stubs: {
|
||||
...globalConfig.global.stubs,
|
||||
Textarea: false,
|
||||
}
|
||||
}
|
||||
|
||||
const noStubPasswordConfig = {
|
||||
...globalConfig.global,
|
||||
stubs: {
|
||||
...globalConfig.global.stubs,
|
||||
Textarea: false,
|
||||
}
|
||||
}
|
||||
|
||||
describe('VInput.vue', () => {
|
||||
it('renders InputText by default', () => {
|
||||
const wrapper = mount(VInput, globalConfig)
|
||||
expect(wrapper.find('input').exists()).toBe(true)
|
||||
})
|
||||
|
||||
it('renders VLabel when labelVisible is true and passes the label prop', () => {
|
||||
const wrapper = mount(VInput, {
|
||||
props: {
|
||||
labelVisible: true,
|
||||
label: 'Username'
|
||||
},
|
||||
global: noStubVLabelConfig
|
||||
})
|
||||
|
||||
const labelComponent = wrapper.findComponent({ name: 'VLabel' })
|
||||
expect(labelComponent.exists()).toBe(true)
|
||||
expect(labelComponent.props('label')).toBe('Username')
|
||||
expect(wrapper.text()).toContain('Username')
|
||||
})
|
||||
|
||||
it('does not render VLabel when labelVisible is false', () => {
|
||||
const wrapper = mount(VInput, {
|
||||
props: {
|
||||
labelVisible: false,
|
||||
label: 'Username'
|
||||
},
|
||||
global: noStubVLabelConfig
|
||||
})
|
||||
|
||||
const labelComponent = wrapper.findComponent({ name: 'VLabel' })
|
||||
expect(labelComponent.exists()).toBe(false)
|
||||
})
|
||||
|
||||
// Test : update:modelValue from InputText
|
||||
it('emits update:modelValue when input value changes', async () => {
|
||||
const wrapper = mount(VInput, {
|
||||
props: {
|
||||
modelValue: ''
|
||||
},
|
||||
global: globalConfig.global
|
||||
})
|
||||
|
||||
const input = wrapper.find('input')
|
||||
await input.setValue('new value')
|
||||
|
||||
expect(wrapper.emitted('update:modelValue')).toBeTruthy()
|
||||
expect(wrapper.emitted('update:modelValue')![0]).toEqual(['new value'])
|
||||
})
|
||||
|
||||
// Test : value-change from InputText
|
||||
it('emits value-change when InputText emits valueChange', async () => {
|
||||
const wrapper = mount(VInput, {
|
||||
props: {
|
||||
modelValue: ''
|
||||
},
|
||||
global: globalConfig.global
|
||||
})
|
||||
|
||||
const inputText = wrapper.findComponent({ name: 'InputText' })
|
||||
inputText.vm.$emit('valueChange', 'changed')
|
||||
|
||||
await wrapper.vm.$nextTick()
|
||||
|
||||
expect(wrapper.emitted('value-change')).toBeTruthy()
|
||||
expect(wrapper.emitted('value-change')![0]).toEqual(['changed'])
|
||||
})
|
||||
|
||||
// Test : value-change from Textarea
|
||||
it('emits value-change when Textarea emits valueChange', async () => {
|
||||
const wrapper = mount(VInput, {
|
||||
props: {
|
||||
isTextarea: true,
|
||||
modelValue: ''
|
||||
},
|
||||
global: noStubTextareaConfig
|
||||
})
|
||||
|
||||
const textarea = wrapper.findComponent({ name: 'Textarea' })
|
||||
textarea.vm.$emit('valueChange', 'textarea changed')
|
||||
|
||||
await wrapper.vm.$nextTick()
|
||||
|
||||
expect(wrapper.emitted('value-change')).toBeTruthy()
|
||||
expect(wrapper.emitted('value-change')![0]).toEqual(['textarea changed'])
|
||||
})
|
||||
|
||||
// Test : value-change from Password
|
||||
it('emits value-change when Password emits valueChange', async () => {
|
||||
const wrapper = mount(VInput, {
|
||||
props: {
|
||||
isPassword: true,
|
||||
modelValue: ''
|
||||
},
|
||||
global: noStubPasswordConfig
|
||||
})
|
||||
|
||||
const password = wrapper.findComponent({ name: 'Password' })
|
||||
password.vm.$emit('valueChange', 'password changed')
|
||||
|
||||
await wrapper.vm.$nextTick()
|
||||
|
||||
expect(wrapper.emitted('value-change')).toBeTruthy()
|
||||
expect(wrapper.emitted('value-change')![0]).toEqual(['password changed'])
|
||||
})
|
||||
|
||||
// Test : update:modelValue from Password
|
||||
it('emits update:modelValue when Password value changes', async () => {
|
||||
const wrapper = mount(VInput, {
|
||||
props: {
|
||||
isPassword: true,
|
||||
modelValue: ''
|
||||
},
|
||||
global: noStubPasswordConfig
|
||||
})
|
||||
|
||||
const password = wrapper.findComponent({ name: 'Password' })
|
||||
password.vm.$emit('update:modelValue', 'new-password')
|
||||
|
||||
await wrapper.vm.$nextTick()
|
||||
|
||||
expect(wrapper.emitted('update:modelValue')).toBeTruthy()
|
||||
expect(wrapper.emitted('update:modelValue')![0]).toEqual(['new-password'])
|
||||
})
|
||||
|
||||
// Test : change from Password
|
||||
it('emits change when Password emits change', async () => {
|
||||
const wrapper = mount(VInput, {
|
||||
props: {
|
||||
isPassword: true,
|
||||
modelValue: ''
|
||||
},
|
||||
global: noStubPasswordConfig
|
||||
})
|
||||
|
||||
const password = wrapper.findComponent({ name: 'Password' })
|
||||
password.vm.$emit('change', 'password-changed')
|
||||
|
||||
await wrapper.vm.$nextTick()
|
||||
|
||||
expect(wrapper.emitted('change')).toBeTruthy()
|
||||
expect(wrapper.emitted('change')![0]).toEqual(['password-changed'])
|
||||
})
|
||||
|
||||
})
|
Loading…
Reference in New Issue
Block a user