import type { Meta, StoryObj } from '@storybook/vue3-vite'
import SyCheckbox from '@/components/Customs/SyCheckbox/SyCheckbox.vue'
import { ref } from 'vue'
import { fn } from 'storybook/test'
import { getValidationDocumentation } from '@/composables/unifyValidation/documentationValidationProps'
import { useTriStateCheckboxGroup } from './triStateCheckboxGroup'
const meta = {
title: 'Composants/Formulaires/SyCheckbox',
component: SyCheckbox,
decorators: [
() => ({
template: '
',
}),
],
parameters: {
layout: 'fullscreen',
controls: { exclude: ['modelValue', 'errorMessages', 'warningMessages', 'successMessages', 'onUpdate:modelValue', 'onUpdate:indeterminate', 'undefined'] },
docs: {
description: {
component: `SyCheckbox est un composant de case à cocher tri-état qui étend le composant VCheckbox de Vuetify avec des fonctionnalités supplémentaires comme la validation personnalisée et l'état indéterminé.`,
},
},
},
argTypes: {
...getValidationDocumentation(),
'locales': {
description: 'Surcharge des chaînes affichées à l\'utilisateur (messages de validation). 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: `{
requiredField: (label: string) => string,
}` },
category: 'props',
},
},
'modelValue': { control: 'boolean' },
'label': {
description: 'Texte affiché comme label de la case à cocher',
control: 'text',
},
'helpText': {
description: 'Texte d\'aide affiché sous la case (masqué quand un message de validation est présent)',
control: 'text',
},
'color': {
control: 'select',
options: ['primary', 'success', 'error', 'warning'],
description: 'Couleur de la case à cocher',
},
'indeterminate': {
description: 'État indéterminé de la case à cocher',
control: 'boolean',
},
'hideDetails': {
description: 'Masque les détails (messages d\'erreur, etc.)',
control: 'boolean',
},
'density': {
control: 'select',
options: ['default', 'comfortable', 'compact'],
description: 'Densité de la case à cocher',
},
'value': {
description: 'Valeur associée à la case à cocher, utile lorsqu\'elle fait partie d\'un groupe de cases à cocher ou en mode `multiple`',
control: 'text',
table: {
type: { summary: 'unknown' },
defaultValue: { summary: 'undefined' },
},
},
'multiple': {
description: 'Active le mode sélection multiple : `modelValue` est alors un tableau dans lequel `value` est ajouté ou retiré. Inféré automatiquement quand `modelValue` est un tableau.',
control: 'boolean',
table: {
type: { summary: 'boolean' },
defaultValue: { summary: 'false' },
},
},
'trueValue': {
description: 'Valeur émise lorsque la case à cocher est cochée',
control: 'text',
table: {
type: { summary: 'unknown' },
defaultValue: { summary: 'undefined (replie sur true, ou value en mode multiple)' },
},
},
'falseValue': {
description: 'Valeur émise lorsque la case à cocher est décochée',
control: 'text',
table: {
type: { summary: 'unknown' },
defaultValue: { summary: 'undefined (replie sur false)' },
},
},
'cycleIndeterminate': {
description: 'Inclut l\'état indéterminé dans la rotation du parent et remplace l\'ancienne prop controlsIds',
control: 'boolean',
},
'displayAsterisk': {
description: 'Afficher l\'astérisque (*) pour indiquer un champ obligatoire',
control: 'boolean',
},
'update:modelValue': {
action: 'update:modelValue',
description: 'Événement émis lorsque la valeur de la case à cocher devient true ou false. Remplace l\'ancien événement change.',
table: {
category: 'events',
type: {
summary: 'boolean',
},
},
},
'update:indeterminate': {
action: 'update:indeterminate',
description: 'Événement émis lorsque l\'état indéterminé de la case à cocher change',
table: {
category: 'events',
type: {
summary: 'boolean',
},
},
},
},
} as Meta
export default meta
type Story = StoryObj
export const Default: Story = {
parameters: {
sourceCode: [
{
name: 'Template',
code: ``,
},
],
},
args: {
'onUpdate:modelValue': fn(),
'onUpdate:indeterminate': fn(),
},
render: args => ({
components: { SyCheckbox },
setup() {
const checked = ref(false)
return { args, checked }
},
template: ``,
}),
}
export const Required: Story = {
args: {
...Default.args,
required: true,
isValidateOnBlur: false,
},
render: args => ({
components: { SyCheckbox },
setup() {
const checked = ref(false)
return { args, checked }
},
template: ``,
}),
parameters: {
sourceCode: [
{
name: 'Template',
code: ``,
},
],
docs: {
description: {
story: `
### Case à cocher obligatoire
Cette case à cocher est marquée comme obligatoire, ce qui déclenchera une validation si elle n'est pas cochée.
`,
},
},
},
}
export const Indeterminate: Story = {
args: {
...Default.args,
indeterminate: true,
},
render: args => ({
components: { SyCheckbox },
setup() {
const checked = ref(false)
const indeterminate = ref(true)
return { args, checked, indeterminate }
},
template: ``,
}),
parameters: {
sourceCode: [
{
name: 'Template',
code: `
`,
},
{
name: 'Script',
code: `
`,
},
],
docs: {
description: {
story: `
### Case à cocher avec état indéterminé
Cette case à cocher est dans un état indéterminé, généralement utilisé lorsque certains éléments d'un groupe sont sélectionnés mais pas tous.
`,
},
},
},
}
export const HelpText: Story = {
args: {
...Default.args,
helpText: 'Cochez cette case pour accepter les conditions générales.',
},
render: args => ({
components: { SyCheckbox },
setup() {
const checked = ref(false)
return { args, checked }
},
template: ``,
}),
parameters: {
sourceCode: [
{
name: 'Template',
code: ``,
},
],
docs: {
description: {
story: `
### Case à cocher avec texte d'aide
Un texte d'aide (\`helpText\`) s'affiche sous la case pour guider l'utilisateur, tant qu'aucun message de validation (erreur, avertissement, succès) n'est présent.
`,
},
},
},
}
export const WithCycleIndeterminate: Story = {
args: Default.args,
parameters: {
sourceCode: [
{
name: 'Template',
code: `
`,
},
{
name: 'Script',
code: `
`,
},
],
docs: {
description: {
story: `
### Case à cocher avec contrôle d'éléments enfants
Cette case à cocher contrôle un groupe d'éléments enfants. L'application synchronise les valeurs des enfants, dérive l'état \`indeterminate\` du parent et applique \`update:modelValue\` à tous les enfants.
L'événement \`update:modelValue\` remplace l'ancien événement \`change\`. La prop \`controlsIds\` n'est plus utilisée par SyCheckbox ; le groupe applicatif porte la structure et les relations nécessaires autour des cases à cocher.
Dans cet exemple, la prop \`cycleIndeterminate\` est activée pour permettre au parent de réintroduire la dernière sélection partielle dans sa rotation. Cette sélection partielle mémorisée est supprimée si l'utilisateur coche ou décoche individuellement tous les enfants. Sans cette prop, le parent contrôlant des enfants reste binaire : depuis non coché ou indéterminé, l'activation coche tous les enfants ; depuis coché, elle les décoche tous.
`,
},
},
},
render: args => ({
components: { SyCheckbox },
setup() {
const group = useTriStateCheckboxGroup(3)
const childrenIds = group.childrenChecked.map((_, index) => `child-${index + 1}`)
return {
args,
...group,
childrenIds,
}
},
template: `
`,
}),
}
export const ValidationRules: Story = {
args: Default.args,
parameters: {
sourceCode: [
{
name: 'Template',
code: `
`,
},
{
name: 'Script',
code: `
`,
},
],
docs: {
description: {
story: `
### Case à cocher avec règles de validation personnalisées
Cette case à cocher utilise des règles de validation personnalisées pour vérifier si elle est cochée.
`,
},
},
},
render: args => ({
components: { SyCheckbox },
setup() {
const checked = ref(false)
return {
args,
checked,
rules: [
{
type: 'custom',
options: {
message: 'Cette case doit être cochée pour continuer.',
validate: (value: boolean) => value === true,
},
},
],
isValidateOnBlur: false,
}
},
template: `
`,
}),
}
export const DisabledState: Story = {
args: {
...Default.args,
disabled: true,
},
render: args => ({
components: { SyCheckbox },
setup() {
const checked = ref(true)
return { args, checked }
},
template: ``,
}),
parameters: {
sourceCode: [
{
name: 'Template',
code: ``,
},
],
docs: {
description: {
story: `
### Case à cocher désactivée
Cette case à cocher est désactivée et ne peut pas être modifiée par l'utilisateur.
`,
},
},
},
}
export const ReadonlyState: Story = {
args: {
...Default.args,
readonly: true,
},
render: args => ({
components: { SyCheckbox },
setup() {
const checked = ref(true)
return { args, checked }
},
template: ``,
}),
parameters: {
sourceCode: [
{
name: 'Template',
code: ``,
},
],
docs: {
description: {
story: `
### Case à cocher en lecture seule
Cette case à cocher est en lecture seule et ne peut pas être modifiée par l'utilisateur, mais elle n'est pas visuellement désactivée comme la version disabled.
`,
},
},
},
}
export const DifferentDensities: Story = {
args: Default.args,
parameters: {
sourceCode: [
{
name: 'Template',
code: `
`,
},
{
name: 'Script',
code: `
`,
},
],
docs: {
description: {
story: `
### Sélection multiple
Quand \`v-model\` est un tableau, le composant bascule automatiquement en mode multiple (comme le \`VCheckbox\` de Vuetify). La prop \`value\` de chaque case est alors ajoutée ou retirée du tableau.
La prop \`multiple\` peut être passée explicitement pour forcer ce comportement, mais elle est inférée dès que \`modelValue\` est un tableau.
`,
},
},
},
render: args => ({
components: { SyCheckbox },
setup() {
const selected = ref([])
return { selected, args }
},
template: `