import type { Meta, StoryObj } from '@storybook/vue3-vite'
import PeriodField from './PeriodField.vue'
import { ref } from 'vue'
// Type pour les méthodes exposées par PeriodField
type ValidationMessage = { type: string, message: string }[]
interface PeriodFieldExpose {
validateOnSubmit: () => boolean
errors: Record
successes: Record
warnings: Record
isValid: boolean
}
const meta: Meta = {
title: 'Composants/Formulaires/PeriodField',
component: PeriodField,
decorators: [
() => ({
template: '
',
}),
],
parameters: {
layout: 'fullscreen',
controls: { exclude: ['modelValue'] },
},
argTypes: {
locales: {
description: 'Surcharge des chaînes affichées à l\'utilisateur (messages de validation des dates de début et de fin). Les valeurs par défaut sont définies dans le fichier `locales.ts` du composant. La prop accepte un objet partiel : seules les clés renseignées surchargent les valeurs par défaut, le reste est conservé.',
control: 'object',
table: {
type: { summary: 'object', detail: `{
fromAfterTo: string,
fromValid: string,
fromRequired: string,
fromFilled: string,
toBeforeFrom: string,
toValid: string,
toRequired: string,
toFilled: string,
}` },
category: 'props',
},
},
modelValue: {
control: 'text',
description: 'Valeur du champ',
table: {
type: {
summary: 'object',
},
},
},
placeholderFrom: {
control: 'text',
description: 'Placeholder pour la date de début',
},
placeholderTo: {
control: 'text',
description: 'Placeholder pour la date de fin',
},
format: {
control: 'select',
options: ['DD/MM/YYYY', 'MM/DD/YYYY', 'YYYY-MM-DD'],
description: 'Format d\'affichage de la date',
},
dateFormatReturn: {
control: 'select',
options: ['', 'DD/MM/YYYY', 'MM/DD/YYYY', 'YYYY-MM-DD'],
description: 'Format de la date pour la valeur de retour',
},
isOutlined: {
control: 'boolean',
description: 'Affiche le champ en contour',
},
required: {
control: 'boolean',
description: 'Champ obligatoire',
},
displayIcon: {
control: 'boolean',
description: 'Affiche l\'icône calendrier',
},
displayAppendIcon: {
control: 'boolean',
description: 'Icône à la fin du champ',
},
disabled: {
control: 'boolean',
description: 'Désactive le champ',
},
noIcon: {
control: 'boolean',
description: 'Masque toutes les icônes',
},
noCalendar: {
control: 'boolean',
description: 'Désactive le calendrier',
},
customRules: {
control: 'object',
description: 'Règles de validation',
},
customWarningRules: {
control: 'object',
description: 'Règles d\'avertissement',
},
disableErrorHandling: {
control: 'boolean',
description: 'Désactive la gestion des erreurs',
},
showSuccessMessages: {
control: 'boolean',
description: 'Affiche les messages de succès',
},
errors: {
description: 'Tableaux réactif contenant tous les messages d\'erreur. Combine les erreurs injectées via errorMessages et celles générées par la validation. Les doublons sont supprimés et le tableau est limité selon maxErrors. Accessible via template ref du composant.',
table: {
type: { summary: `{
fromDate: Readonly[>,
toDate: Readonly][>
}` },
category: 'expose',
},
},
warnings: {
description: 'Tableaux réactif contenant tous les messages d\'avertissement. Combine les avertissements injectés via warningMessages et ceux générés par customWarningRules. Les doublons sont supprimés et le tableau est limité selon maxErrors. Accessible via template ref du composant.',
table: {
type: { summary: `{
fromDate: Readonly][>,
toDate: Readonly][>
}` },
category: 'expose',
},
},
successes: {
description: 'Tableaux réactif contenant tous les messages de succès. Combine les succès injectés via successMessages et ceux générés par customSuccessRules. Les doublons sont supprimés et le tableau est limité selon maxErrors. Accessible via template ref du composant.',
table: {
type: { summary: `{
fromDate: Readonly][>,
toDate: Readonly][>
}` },
category: 'expose',
},
},
isValid: {
description: 'Vrai si le champ est valide.',
table: {
type: { summary: 'boolean' },
category: 'expose',
},
},
validateOnSubmit: {
description: 'Fonction qui permet de déclencher la validation du champ.',
table: {
type: { summary: '() => promise' },
category: 'expose',
},
},
clearValidation: {
description: 'Fonction qui permet de réinitialiser la validation du champ.',
table: {
type: { summary: '() => void' },
category: 'expose',
},
},
},
} as Meta
export default meta
type Story = StoryObj
export const Default: Story = {
parameters: {
sourceCode: [
{
name: 'Template',
code: `
]Période sélectionnée :
{{ selectedPeriod }}
`,
},
{
name: 'Script',
code: `
`,
},
],
},
args: {
placeholderFrom: 'Début',
placeholderTo: 'Fin',
format: 'DD/MM/YYYY',
dateFormatReturn: '',
required: false,
displayIcon: true,
displayAppendIcon: false,
disabled: false,
noIcon: false,
modelValue: { from: null, to: null },
},
render: args => ({
components: { PeriodField: PeriodField },
setup() {
const value = ref({ from: null, to: null })
return { args, value }
},
template: `
Période sélectionnée : {{ value }}
`,
}),
}
export const CustomPlaceholders: Story = {
parameters: {
sourceCode: [
{
name: 'Template',
code: `
Période sélectionnée : {{ selectedPeriod }}
`,
},
{
name: 'Script',
code: `
`,
},
],
},
args: {
placeholderFrom: 'Début de la période',
placeholderTo: 'Fin de la période',
format: 'DD/MM/YYYY',
dateFormatReturn: '',
required: false,
displayIcon: true,
displayAppendIcon: false,
disabled: false,
noIcon: false,
modelValue: { from: null, to: null },
},
render: args => ({
components: { PeriodField: PeriodField },
setup() {
const value = ref({ from: null, to: null })
return { args, value }
},
template: `
Période sélectionnée : {{ value }}
`,
}),
}
export const Disabled: Story = {
parameters: {
sourceCode: [
{
name: 'Template',
code: `
`,
},
{
name: 'Script',
code: `
`,
},
],
},
args: {
placeholderFrom: 'Début',
placeholderTo: 'Fin',
format: 'DD/MM/YYYY',
dateFormatReturn: '',
required: false,
displayIcon: true,
displayAppendIcon: false,
disabled: true,
noIcon: false,
modelValue: { from: null, to: null },
},
render: args => ({
components: { PeriodField: PeriodField },
setup() {
const value = ref({ from: null, to: null })
return { args, value }
},
template: `
`,
}),
}
export const Required: Story = {
parameters: {
sourceCode: [
{
name: 'Template',
code: `
Période sélectionnée : {{ selectedPeriod }}
`,
},
{
name: 'Script',
code: `
`,
},
],
},
args: {
placeholderFrom: 'Début',
placeholderTo: 'Fin',
format: 'DD/MM/YYYY',
dateFormatReturn: '',
required: true,
displayIcon: true,
displayAppendIcon: false,
disabled: false,
noIcon: false,
modelValue: { from: null, to: null },
},
render: args => ({
components: { PeriodField: PeriodField },
setup() {
const value = ref({ from: null, to: null })
return { args, value }
},
template: `
Période sélectionnée : {{ value }}
`,
}),
}
export const AppendIcon: Story = {
parameters: {
sourceCode: [
{
name: 'Template',
code: `
Période sélectionnée : {{ selectedPeriod }}
`,
},
{
name: 'Script',
code: `
`,
},
],
},
args: {
placeholderFrom: 'Début',
placeholderTo: 'Fin',
format: 'DD/MM/YYYY',
required: true,
displayIcon: true,
displayAppendIcon: true,
disabled: false,
noIcon: false,
modelValue: { from: null, to: null },
},
render: args => ({
components: { PeriodField: PeriodField },
setup() {
const value = ref({ from: null, to: null })
return { args, value }
},
template: `
Période sélectionnée : {{ value }}
`,
}),
}
export const WithoutIcon: Story = {
parameters: {
sourceCode: [
{
name: 'Template',
code: `
Période sélectionnée : {{ selectedPeriod }}
`,
},
{
name: 'Script',
code: `
`,
},
],
},
args: {
placeholderFrom: 'Début',
placeholderTo: 'Fin',
format: 'DD/MM/YYYY',
dateFormatReturn: '',
required: true,
displayIcon: false,
displayAppendIcon: true,
disabled: false,
noIcon: false,
modelValue: { from: null, to: null },
},
render: args => ({
components: { PeriodField: PeriodField },
setup() {
const value = ref({ from: null, to: null })
return { args, value }
},
template: `
Période sélectionnée : {{ value }}
`,
}),
}
export const WithError: Story = {
parameters: {
sourceCode: [
{
name: 'Template',
code: `
Période sélectionnée : {{ selectedPeriod }}
`,
},
{
name: 'Script',
code: `
`,
},
],
},
args: {
placeholderFrom: 'Début',
placeholderTo: 'Fin',
format: 'DD/MM/YYYY',
dateFormatReturn: '',
required: true,
displayIcon: false,
displayAppendIcon: true,
disabled: false,
noIcon: false,
modelValue: { from: '01/01/2100', to: '05/01/2100' },
customRules: [
{ type: 'notAfterToday', options: { message: 'La date ne peut pas être après aujourd\'hui' } },
],
},
render: args => ({
components: { PeriodField: PeriodField },
setup() {
const value = ref({ from: '01/01/2100', to: '05/01/2100' })
return { args, value }
},
template: `
Période sélectionnée : {{ value }}
`,
}),
}
export const WithWarning: Story = {
parameters: {
sourceCode: [
{
name: 'Template',
code: `
Période sélectionnée : {{ selectedPeriod }}
`,
},
{
name: 'Script',
code: `
`,
},
],
},
args: {
placeholderFrom: 'Début',
placeholderTo: 'Fin',
format: 'DD/MM/YYYY',
dateFormatReturn: '',
required: true,
displayIcon: false,
displayAppendIcon: true,
disabled: false,
noIcon: false,
modelValue: { from: '20/12/2023', to: '21/12/2023' },
customWarningRules: [
{
type: 'notBeforeDate', options: {
warningMessage: 'Attention : les dates sont antérieures au 02/01/2031',
date: '02/01/2031',
isWarning: true,
},
},
],
},
render: args => ({
components: { PeriodField: PeriodField },
setup() {
const value = ref({ from: '20/12/2023', to: '21/12/2023' })
return { args, value }
},
template: `
Période sélectionnée : {{ value }}
`,
}),
}
export const WithSuccess: Story = {
parameters: {
sourceCode: [
{
name: 'Template',
code: `
Période sélectionnée : {{ selectedPeriod }}
`,
},
{
name: 'Script',
code: `
`,
},
],
},
args: {
placeholderFrom: 'Début',
placeholderTo: 'Fin',
format: 'DD/MM/YYYY',
dateFormatReturn: '',
displayIcon: false,
displayAppendIcon: true,
disabled: false,
noIcon: false,
showSuccessMessages: true,
modelValue: { from: '22/01/2024', to: '23/01/2024' },
customRules: [
{
type: 'notWeekend',
options: {
message: 'La date ne peut pas être un weekend',
successMessage: 'La date n\'est pas un week-end',
},
},
],
},
render: args => ({
components: { PeriodField: PeriodField },
setup() {
const value = ref({ from: '22/01/2024', to: '23/01/2024' })
return { args, value }
},
template: `
Période sélectionnée : {{ value }}
`,
}),
}
export const DifferentFormats: Story = {
parameters: {
sourceCode: [
{
name: 'Template',
code: `
`,
},
{
name: 'Script',
code: `
`,
},
],
},
args: {
placeholderFrom: 'Début',
placeholderTo: 'Fin',
format: 'DD/MM/YYYY',
dateFormatReturn: '',
required: false,
displayIcon: true,
displayAppendIcon: false,
disabled: false,
noIcon: false,
modelValue: { from: '12/10/2023', to: '15/10/2023' },
},
render: args => ({
components: { PeriodField: PeriodField },
setup() {
const value1 = ref({ from: '12/10/2023', to: '15/10/2023' })
const value2 = ref({ from: '10/12/2023', to: '10/15/2023' })
const value3 = ref({ from: '2023-10-12', to: '2023-10-15' })
const value4 = ref({ from: '12-10-23', to: '15-10-23' })
const value5 = ref({ from: '12.10.2023', to: '15.10.2023' })
return { args, value1, value2, value3, value4, value5 }
},
template: `
`,
}),
}
export const WithDateFormatReturn: Story = {
parameters: {
sourceCode: [
{
name: 'Template',
code: `
Période de retour : {{ value1 }}
Période de retour : {{ value2 }}
Période de retour : {{ value3 }}
`,
},
{
name: 'Script',
code: `
`,
},
],
},
args: {
placeholderFrom: 'Début',
placeholderTo: 'Fin',
format: 'DD/MM/YYYY',
dateFormatReturn: '',
required: false,
displayIcon: true,
displayAppendIcon: false,
disabled: false,
noIcon: false,
modelValue: { from: null, to: null },
},
render: args => ({
components: { PeriodField: PeriodField },
setup() {
const value1 = ref({ from: '12/10/2023', to: '15/10/2023' })
const value2 = ref({ from: '12/10/2023', to: '15/10/2023' })
const value3 = ref({ from: '12/10/2023', to: '15/10/2023' })
const valueReturn1 = ref({ from: '12/10/2023', to: '15/10/2023' })
const valueReturn2 = ref({ from: '10/12/2023', to: '10/15/2023' })
const valueReturn3 = ref({ from: '2023/12/10', to: '2023/15/10' })
return { args, value1, value2, value3, valueReturn1, valueReturn2, valueReturn3 }
},
template: `
Période de retour : {{ valueReturn1 }}
Période de retour : {{ valueReturn2 }}
Période de retour : {{ valueReturn3 }}
`,
}),
}
export const ValidationBehaviors: Story = {
parameters: {
sourceCode: [
{
code: `
Par défaut (avec validation)
Validation désactivée (disableErrorHandling=true)
Messages de succès désactivés (showSuccessMessages=false)
Validation et messages de succès désactivés
`,
},
],
},
render: args => ({
components: { PeriodField },
setup() {
const periodDefault = ref({ from: null, to: null })
const periodNoErrors = ref({ from: null, to: null })
const periodNoSuccess = ref({ from: null, to: null })
const periodNoValidation = ref({ from: null, to: null })
return {
args,
periodDefault,
periodNoErrors,
periodNoSuccess,
periodNoValidation,
}
},
template: `
Par défaut (avec validation)
Validation désactivée (disableErrorHandling=true)
Messages de succès désactivés (showSuccessMessages=false)
Validation et messages de succès désactivés
`,
}),
}
export const FormValidation: Story = {
parameters: {
sourceCode: [
{
name: 'Template',
code: `
Formulaire avec validation
Valider
Formulaire envoyé avec succès !
{{ formData }}
`,
},
{
name: 'Script',
code: `
`,
},
],
},
render: () => ({
components: { PeriodField },
setup() {
// Référence au formulaire
const formRef = ref(null)
const periodFieldRef = ref(null)
const isSubmitting = ref(false)
const isFormSubmitted = ref(false)
const formData = ref(null)
const formStatus = ref('')
// Données du formulaire
const form = ref({
period: { from: null, to: null },
})
// Règles de validation pour le champ période
const periodRules = [
{ type: 'notAfterToday', options: { message: 'La date ne peut pas être après aujourdhui' } },
]
// Soumission du formulaire
const submitForm = async (): Promise => {
if (periodFieldRef.value) {
const isValid = await periodFieldRef.value.validateOnSubmit()
if (isValid) {
formStatus.value = 'Formulaire soumis avec succès !'
isFormSubmitted.value = true
formData.value = JSON.stringify(form.value)
}
else {
formStatus.value = 'Erreur de validation, veuillez corriger les champs'
}
}
}
return {
formRef,
periodFieldRef,
isSubmitting,
isFormSubmitted,
formData,
form,
periodRules,
formStatus,
submitForm,
}
},
template: `
Formulaire avec validation
Valider
{{ formStatus }}
{{ formData }}
`,
}),
}