import type { Meta, StoryObj } from '@storybook/vue3' import NirField from '../NirField.vue' import SyForm from '../../Customs/SyForm/SyForm.vue' import { onMounted, ref } from 'vue' import { fn } from '@storybook/test' import { VBtn, VForm } from 'vuetify/components' const meta: Meta = { title: 'Composants/Formulaires/NirField/Validation', component: NirField, parameters: { layout: 'fullscreen', }, args: { 'onUpdate:modelValue': fn(), }, } as Meta export default meta type Story = StoryObj const VALID_NIR = '184027512345674' const INVALID_NIR = '199012345678' export const WithError: Story = { parameters: { docs: { description: { story: 'Un NIR invalide est présaisi et la validation est déclenchée au chargement.', }, }, sourceCode: [ { name: 'Template', code: ` `, }, { name: 'Script', code: ` `, }, ], }, args: { label: 'Identifiant assuré', required: true, showSuccessMessages: true, }, render: args => ({ components: { NirField }, setup() { const value = ref(INVALID_NIR) const nirRef = ref<{ validateOnSubmit: () => Promise } | null>(null) onMounted(() => { nirRef.value?.validateOnSubmit() }) return { args, value, nirRef } }, template: `
`, }), } export const WithWarning: Story = { parameters: { docs: { description: { story: 'Un avertissement non bloquant s\'affiche si le NIR commence par 1 (homme). Le champ reste valide.', }, }, sourceCode: [ { name: 'Template', code: ` `, }, { name: 'Script', code: ` `, }, ], }, args: { label: 'Identifiant assuré', }, render: args => ({ components: { NirField }, setup() { const value = ref(VALID_NIR) const nirRef = ref<{ validateOnSubmit: () => Promise } | null>(null) onMounted(() => { nirRef.value?.validateOnSubmit() }) return { args, value, nirRef } }, template: `
`, }), } export const WithSuccess: Story = { parameters: { docs: { description: { story: 'Un NIR valide est présaisi et les messages de succès s\'affichent au chargement.', }, }, sourceCode: [ { name: 'Template', code: ` `, }, { name: 'Script', code: ` `, }, ], }, args: { label: 'Identifiant assuré', required: true, showSuccessMessages: true, }, render: args => ({ components: { NirField }, setup() { const value = ref(VALID_NIR) const nirRef = ref<{ validateOnSubmit: () => Promise } | null>(null) onMounted(() => { nirRef.value?.validateOnSubmit() }) return { args, value, nirRef } }, template: `
`, }), } export const DisableErrorHandling: Story = { parameters: { sourceCode: [ { name: 'Template', code: ` `, }, { name: 'Script', code: ` `, }, ], }, render: () => ({ components: { NirField }, setup() { const value1 = ref('') const value2 = ref('') return { value1, value2 } }, template: `
`, }), } export const CustomRules: Story = { args: { customRulesPrecedence: true, customNumberRules: [ { type: 'custom', options: { validate: (value: string) => { if (!value) return true const valueWithoutSpaces = value.replace(/\s/g, '') if (valueWithoutSpaces.length !== 13) { return 'Le numéro de sécurité sociale doit contenir 13 caractères.' } if (!/^[12]/.test(valueWithoutSpaces)) { return 'Le premier chiffre doit être 1 (homme) ou 2 (femme).' } const anneeNaissance = valueWithoutSpaces.substring(1, 3) if (!/^[0-9]{2}$/.test(anneeNaissance)) { return 'Les chiffres 2 et 3 doivent représenter l\'année de naissance.' } const moisNaissance = valueWithoutSpaces.substring(3, 5) if (!/^(0[1-9]|1[0-2])$/.test(moisNaissance)) { return 'Les chiffres 4 et 5 doivent représenter un mois valide (01-12).' } const departement = valueWithoutSpaces.substring(5, 7) if (!((/^[0-9]{2}$/.test(departement) && departement !== '00') || departement === '2A' || departement === '2B' || departement === '99')) { return 'Les chiffres 6 et 7 doivent représenter un département valide.' } const codeCommune = valueWithoutSpaces.substring(7, 10) if (!/^[0-9]{3}$/.test(codeCommune)) { return 'Les chiffres 8 à 10 doivent représenter un code commune ou pays valide.' } const numeroOrdre = valueWithoutSpaces.substring(10, 13) if (!/^[0-9]{3}$/.test(numeroOrdre)) { return 'Les chiffres 11 à 13 doivent représenter un numéro d\'ordre valide.' } return true }, message: 'Le numéro de sécurité sociale est invalide.', successMessage: 'Le numéro de sécurité sociale est valide.', fieldIdentifier: 'Numéro de sécurité sociale', }, }, ], }, parameters: { docs: { description: { story: 'Règle personnalisée avec prévalence (`customRulesPrecedence: true`) qui valide le format du NIR selon les règles officielles françaises : sexe, année, mois, département, commune, numéro d\'ordre.', }, }, sourceCode: [ { name: 'Template', code: ` `, }, { name: 'Script', code: ` `, }, ], }, render: args => ({ components: { NirField }, setup() { const value = ref('') return { args, value } }, template: `
Valeur actuelle : {{ value }}
`, }), } export const NoValidateOnBlur: Story = { parameters: { docs: { description: { story: 'Avec `isValidateOnBlur: false`, la validation se déclenche à chaque modification plutôt qu\'au seul moment du blur. Les boutons permettent de simuler une valeur programmatique avec validation immédiate.', }, }, sourceCode: [ { name: 'Template', code: ` `, }, { name: 'Script', code: ` `, }, ], }, args: { label: 'Identifiant assuré', isValidateOnBlur: false, showSuccessMessages: true, required: true, }, render: args => ({ components: { NirField, VBtn }, setup() { const value = ref(null) const nirRef = ref<{ validateOnSubmit: () => Promise } | null>(null) const handleChange = (newValue: string | null) => { value.value = newValue } const applyValue = async (newValue: string | null) => { value.value = newValue await nirRef.value?.validateOnSubmit() } return { args, value, nirRef, handleChange, applyValue, VALID_NIR, INVALID_NIR } }, template: `
Valeur invalide Valeur valide Réinitialiser
`, }), } export const SyFormValidation: Story = { parameters: { docs: { description: { story: 'Le NirField s\'intègre automatiquement dans un SyForm via `useValidatable`. La soumission du formulaire déclenche la validation sans configuration supplémentaire.', }, }, sourceCode: [ { name: 'Template', code: ` `, }, { name: 'Script', code: ` `, }, ], }, args: { label: 'Identifiant assuré', required: true, displayAsterisk: true, showSuccessMessages: true, }, render: args => ({ components: { NirField, SyForm, VBtn }, setup() { const value = ref('') const onSubmit = (event: { isValid: boolean }) => { if (event.isValid) { alert(`Formulaire valide : ${value.value}`) } else { alert('Formulaire invalide.') } } return { args, value, onSubmit } }, template: `
Soumettre
`, }), } export const VFormValidation: Story = { parameters: { docs: { description: { story: 'Intégration avec `VForm` natif Vuetify. La soumission appelle `validateOnSubmit()` manuellement sur le champ pour déclencher la validation Synapse.', }, }, sourceCode: [ { name: 'Template', code: ` `, }, { name: 'Script', code: ` `, }, ], }, args: { label: 'Identifiant assuré', required: true, displayAsterisk: true, showSuccessMessages: true, }, render: args => ({ components: { NirField, VBtn, VForm }, setup() { const value = ref('') const nirRef = ref<{ validateOnSubmit: () => Promise } | null>(null) async function onSubmit() { const isValid = await nirRef.value?.validateOnSubmit() if (isValid) { alert(`Formulaire valide : ${value.value}`) } else { alert('Formulaire invalide.') } } return { args, value, nirRef, onSubmit } }, template: `
Soumettre
`, }), } export const SyFormVuetifyValidation: Story = { parameters: { docs: { description: { story: 'Validation native Vuetify via `useVuetifyValidation`. Les règles sont au format Vuetify (fonctions retournant `true` ou un message d\'erreur), passées via `numberRules`/`keyRules`. Soumettez le formulaire pour déclencher la validation.', }, }, sourceCode: [ { name: 'Template', code: ` `, }, { name: 'Script', code: ` `, }, ], }, args: { label: 'Identifiant assuré', useVuetifyValidation: true, }, render: args => ({ components: { NirField, SyForm, VBtn }, setup() { const value = ref('') const onSubmit = (event: { isValid: boolean }) => { if (event.isValid) { alert(`Formulaire valide : ${value.value}`) } else { alert('Formulaire invalide.') } } return { args, value, onSubmit } }, template: `
Soumettre
`, }), } export const VFormAndVuetifyValidation: Story = { parameters: { docs: { description: { story: 'Validation native Vuetify (`useVuetifyValidation`) intégrée dans un `VForm` natif (sans SyForm). La soumission déclenche la validation via `form.validate()`.', }, }, sourceCode: [ { name: 'Template', code: ` `, }, { name: 'Script', code: ` `, }, ], }, args: { label: 'Identifiant assuré', useVuetifyValidation: true, }, render: args => ({ components: { NirField, VBtn, VForm }, setup() { const value = ref('') const formRef = ref | null>(null) async function onSubmit() { const result = await formRef.value?.validate() if (result?.valid) { alert(`Formulaire valide : ${value.value}`) } else { alert('Formulaire invalide.') } } return { args, value, formRef, onSubmit } }, template: `
Soumettre
`, }), }