import type { Meta, StoryObj } from '@storybook/vue3'
import { ref } from 'vue'
import PasswordField from './PasswordField.vue'
const meta = {
title: 'Composants/Formulaires/PasswordField',
component: PasswordField,
decorators: [
() => ({
template: '
',
}),
],
parameters: {
layout: 'fullscreen',
docs: {
description: {
component: `PasswordField est un champ de saisie sécurisé pour les mots de passe`,
},
},
},
argTypes: {
modelValue: {
control: 'text',
description: 'Valeur du champ de mot de passe',
},
variantStyle: {
control: 'select',
options: ['outlined', 'underlined'],
description: 'Style du champ (contour ou souligné)',
},
color: {
control: 'select',
options: ['primary', 'secondary', 'error', 'warning', 'success', 'info'],
description: 'Couleur principale du champ',
},
label: {
control: 'text',
description: 'Libellé du champ',
},
required: {
control: 'boolean',
description: 'Indique si le champ est obligatoire',
},
errorMessages: {
control: 'object',
description: 'Messages d\'erreur à afficher',
},
warningMessages: {
control: 'object',
description: 'Messages d\'avertissement à afficher',
},
successMessages: {
control: 'object',
description: 'Messages de succès à afficher',
},
readonly: {
control: 'boolean',
description: 'Indique si le champ est en lecture seule',
},
disabled: {
control: 'boolean',
description: 'Indique si le champ est désactivé',
},
placeholder: {
control: 'text',
description: 'Texte d\'indication affiché lorsque le champ est vide',
},
customRules: {
control: 'object',
description: 'Règles de validation personnalisées',
},
customWarningRules: {
control: 'object',
description: 'Règles d\'avertissement personnalisées',
},
customSuccessRules: {
control: 'object',
description: 'Règles de succès personnalisées',
},
showSuccessMessages: {
control: 'boolean',
description: 'Indique si les messages de succès doivent être affichés',
},
displayAsterisk: {
control: 'boolean',
description: 'Affiche un astérisque à côté du libellé pour indiquer que le champ est obligatoire',
},
isValidateOnBlur: {
control: 'boolean',
description: 'Indique si la validation doit être effectuée lors de la perte de focus',
},
bgColor: {
control: 'color',
description: 'Couleur de fond du champ',
},
autocompleteType: {
control: 'select',
options: ['current-password', 'new-password'],
description: 'Type d\'auto-complétion',
default: 'current-password',
},
},
args: {
modelValue: '',
variantStyle: 'outlined',
color: 'primary',
label: 'Mot de passe',
required: false,
errorMessages: null,
warningMessages: null,
successMessages: null,
readonly: false,
disabled: false,
placeholder: 'Entrez votre mot de passe',
customRules: [],
customWarningRules: [],
customSuccessRules: [],
showSuccessMessages: true,
displayAsterisk: false,
isValidateOnBlur: true,
bgColor: 'white',
},
} satisfies Meta
export default meta
type Story = StoryObj
/**
* Story par défaut montrant un champ de mot de passe basique.
*/
export const Default: Story = {
parameters: {
a11y: {
disable: true,
},
sourceCode: [
{
name: 'Template',
code: `
`,
},
{
name: 'Script',
code: `
`,
},
],
},
render: args => ({
components: { PasswordField },
setup() {
const password = ref(args.modelValue)
return { args, password }
},
template: `
`,
}),
}
/**
* Champ de mot de passe avec validation requise.
*/
export const Required: Story = {
parameters: {
a11y: {
disable: true,
},
sourceCode: [
{
name: 'Template',
code: `
`,
},
{
name: 'Script',
code: `
`,
},
],
},
args: {
required: true,
displayAsterisk: false,
},
}
/**
* Champ de mot de passe avec validation requise avec asterisk.
*/
export const RequiredWithAsterisk: Story = {
parameters: {
a11y: {
disable: true,
},
sourceCode: [
{
name: 'Template',
code: `
`,
},
{
name: 'Script',
code: `
`,
},
],
},
args: {
required: true,
displayAsterisk: true,
},
}
/**
* Champ de mot de passe désactivé.
*/
export const Disabled: Story = {
parameters: {
a11y: {
disable: true,
},
sourceCode: [
{
name: 'Template',
code: `
`,
},
{
name: 'Script',
code: `
`,
},
],
},
args: {
disabled: true,
modelValue: 'MonMotDePasse123',
},
}
/**
* Champ de mot de passe en lecture seule.
*/
export const ReadOnly: Story = {
parameters: {
a11y: {
disable: true,
},
sourceCode: [
{
name: 'Template',
code: `
`,
},
{
name: 'Script',
code: `
`,
},
],
},
args: {
readonly: true,
modelValue: 'MonMotDePasse123',
},
}
/**
* Champ de mot de passe avec règles de validation qui génèrent une erreur.
*/
export const WithError: Story = {
parameters: {
a11y: {
disable: true,
},
sourceCode: [
{
name: 'Template',
code: `
`,
},
{
name: 'Script',
code: `
`,
},
],
},
args: {
modelValue: 'Mdp123',
customRules: [
{
type: 'custom',
options: {
validate: (value: string) => {
if (!value || value.length < 8) {
return 'Le mot de passe doit contenir au moins 8 caractères'
}
return true
},
fieldIdentifier: 'password',
},
},
],
},
}
/**
* Champ de mot de passe avec règles de validation qui génèrent un avertissement.
*/
export const WithWarning: Story = {
parameters: {
a11y: {
disable: true,
},
sourceCode: [
{
name: 'Template',
code: `
`,
},
{
name: 'Script',
code: `
`,
},
],
},
args: {
modelValue: 'MotDePasse123',
customWarningRules: [
{
type: 'custom',
options: {
validate: (value: string) => {
const hasSpecialChar = /[!@#$%^&*(),.?":{}|<>]/.test(value)
if (!hasSpecialChar) {
return 'Le mot de passe pourrait être plus fort avec des caractères spéciaux'
}
return true
},
fieldIdentifier: 'password',
},
},
],
},
}
/**
* Champ de mot de passe avec règles de validation qui génèrent un succès.
*/
export const WithSuccess: Story = {
parameters: {
a11y: {
disable: true,
},
sourceCode: [
{
name: 'Template',
code: `
`,
},
{
name: 'Script',
code: `
`,
},
],
},
args: {
modelValue: 'MotDePasse123!@#',
customSuccessRules: [
{
type: 'custom',
options: {
validate: (value: string) => {
const hasUpperCase = /[A-Z]/.test(value)
const hasLowerCase = /[a-z]/.test(value)
const hasNumber = /[0-9]/.test(value)
const hasSpecialChar = /[!@#$%^&*(),.?":{}|<>]/.test(value)
const isLongEnough = value.length >= 8
if (hasUpperCase && hasLowerCase && hasNumber && hasSpecialChar && isLongEnough) {
return true
}
return false
},
successMessage: 'Mot de passe fort',
},
},
],
},
}
/**
* Démonstration des différents types de validation (erreurs, avertissements, succès).
*/
export const WithValidation: Story = {
parameters: {
a11y: {
disable: true,
},
sourceCode: [
{
name: 'Template',
code: `
`,
},
{
name: 'Script',
code: `
`,
},
],
},
render: args => ({
components: { PasswordField },
setup() {
const password = ref('')
// Règles personnalisées pour la validation du mot de passe
const customRules = [
{
type: 'custom',
options: {
validate: (value: string) => {
if (!value || value.length < 8) {
return 'Le mot de passe doit contenir au moins 8 caractères'
}
return true
},
fieldIdentifier: 'password',
},
},
]
const customWarningRules = [
{
type: 'custom',
options: {
validate: (value: string) => {
if (!value || !/[!@#$%^&*(),.?":{}|<>]/.test(value)) {
return 'Le mot de passe pourrait être plus fort avec des caractères spéciaux'
}
return true
},
fieldIdentifier: 'password',
},
},
]
const customSuccessRules = [
{
type: 'custom',
options: {
validate: (value: string) => {
if (value && value.length >= 12
&& /[A-Z]/.test(value)
&& /[0-9]/.test(value)
&& /[!@#$%^&*(),.?":{}|<>]/.test(value)) {
return true
}
return 'Pas encore un mot de passe fort'
},
successMessage: 'Mot de passe très sécurisé !',
fieldIdentifier: 'password',
},
},
]
return { args, password, customRules, customWarningRules, customSuccessRules }
},
template: `
Entrez un mot de passe pour voir les différents types de validation :
Conseils pour tester :
- Laissez le champ vide pour voir l'erreur de champ requis
- Entrez moins de 8 caractères pour voir l'erreur de longueur
- Entrez un mot de passe sans caractères spéciaux pour voir l'avertissement
- Entrez un mot de passe fort (12+ caractères avec majuscules, chiffres et caractères spéciaux) pour voir le message de succès
`,
}),
}
/**
* Champ de mot de passe avec règles de validation personnalisées.
*/
export const WithCustomRules: Story = {
parameters: {
a11y: {
disable: true,
},
sourceCode: [
{
name: 'Template',
code: `
`,
},
{
name: 'Script',
code: `
`,
},
],
},
render: args => ({
components: { PasswordField },
setup() {
const password = ref(args.modelValue)
// Règles personnalisées pour la validation du mot de passe
const customRules = [
{
type: 'custom',
options: {
validate: (value: string) => {
if (!value || value.length < 8) {
return 'Le mot de passe doit contenir au moins 8 caractères'
}
return true
},
fieldIdentifier: 'password',
},
},
{
type: 'custom',
options: {
validate: (value: string) => {
if (!value || !/[A-Z]/.test(value)) {
return 'Le mot de passe doit contenir au moins une lettre majuscule'
}
return true
},
fieldIdentifier: 'password',
successMessage: 'Le mot de passe est sécurisé',
},
},
{
type: 'custom',
options: {
validate: (value: string) => {
if (!value || !/[0-9]/.test(value)) {
return 'Le mot de passe doit contenir au moins un chiffre'
}
return true
},
fieldIdentifier: 'password',
},
},
]
return { args, password, customRules }
},
template: `
`,
}),
}
/**
* Démonstration de la validation de formulaire avec la méthode validateOnSubmit.
*/
export const WithFormValidation: Story = {
parameters: {
a11y: {
disable: true,
},
sourceCode: [
{
name: 'Template',
code: `
{{ formStatus }}
`,
},
{
name: 'Script',
code: `
`,
},
],
},
render: args => ({
components: { PasswordField },
setup() {
const password = ref('')
const passwordFieldRef = ref | null>(null)
const formStatus = ref('')
// Règles personnalisées pour la validation du mot de passe
const customRules = [
{
type: 'custom',
options: {
validate: (value: string) => {
if (!value || value.length < 8) {
return 'Le mot de passe doit contenir au moins 8 caractères'
}
return true
},
fieldIdentifier: 'password',
},
},
{
type: 'custom',
options: {
validate: (value: string) => {
if (!value || !/[A-Z]/.test(value)) {
return 'Le mot de passe doit contenir au moins une lettre majuscule'
}
return true
},
fieldIdentifier: 'password',
},
},
]
// Fonction de soumission du formulaire
const handleSubmit = async () => {
if (passwordFieldRef.value) {
const isValid = await passwordFieldRef.value.validateOnSubmit()
if (isValid) {
formStatus.value = 'Formulaire soumis avec succès !'
}
else {
formStatus.value = 'Erreur de validation, veuillez corriger les champs'
}
}
}
return { args, password, passwordFieldRef, customRules, handleSubmit, formStatus }
},
template: `
{{ formStatus }}
Instructions :
Ce formulaire utilise la méthode validateOnSubmit() pour valider le champ lors de la soumission.
La validation ne se fait pas à la perte de focus (isValidateOnBlur="false") mais uniquement lors du clic sur le bouton.
`,
}),
}
export const WithoutSuccessMessages: Story = {
parameters: {
a11y: {
disable: true,
},
docs: {
description: {
story: `
### Messages de succès
Cette story illustre l'utilisation de la propriété \`showSuccessMessages\` qui permet de contrôler
l'affichage des messages de succès lors de la validation. Par défaut, cette propriété est à \`true\`.
Cela peut être utile pour réduire la verbosité de l'interface lorsque les messages de succès
ne sont pas nécessaires dans certains contextes.
`,
},
},
sourceCode: [
{
name: 'Template',
code: `
`,
},
],
},
render: () => ({
components: { PasswordField },
setup() {
const value1 = ref('P@ssw0rd123')
const value2 = ref('P@ssw0rd123')
return { value1, value2 }
},
template: `
Cette démonstration compare un PasswordField avec showSuccessMessages=true (par défaut) et un avec showSuccessMessages=false.
Observations :
- Les deux champs ont la même valeur valide
- Le champ de gauche affiche un message de succès et un indicateur visuel vert
- Le champ de droite n'affiche pas de message de succès, mais conserve l'indicateur visuel
- Essayez de modifier les valeurs puis de les rendre à nouveau valides
`,
}),
}
export const DisableErrorHandling: Story = {
parameters: {
a11y: {
disable: true,
},
docs: {
description: {
story: `
### Désactivation de la gestion des erreurs
Cette story illustre l'utilisation de la propriété \`disableErrorHandling\` qui permet de désactiver complètement
la gestion et l'affichage des erreurs dans un champ, même si des règles de validation sont définies.
Cela peut être utile dans des cas particuliers où vous souhaitez définir des règles de validation
mais gérer leur affichage différemment, ou utiliser la validation uniquement au niveau du formulaire parent.
`,
},
},
sourceCode: [
{
name: 'Template',
code: `
`,
},
],
},
render: () => ({
components: { PasswordField },
setup() {
const value1 = ref('')
const value2 = ref('')
const customRules = [
{
type: 'custom',
options: {
validate: (value: string) => {
if (!value || value.length < 8) {
return 'Le mot de passe doit contenir au moins 8 caractères'
}
return true
},
fieldIdentifier: 'password',
},
},
]
return { value1, value2, customRules }
},
template: `
Cette démonstration compare un PasswordField standard et un avec \`disableErrorHandling=true\`.
Instructions :
- Cliquez dans un champ puis en dehors pour déclencher la validation
- Le champ de gauche affichera une erreur requise, mais pas celui de droite
- Vous pouvez également essayer de soumettre les deux champs pour voir la différence de comportement
`,
}),
}