import type { Meta, StoryObj } from '@storybook/vue3-vite' import { computed, ref } from 'vue' import CpCheckbox from '@/components/CpCheckbox.vue' import { docCellStyle, docLabelStyle, docRowWrapStyle } from '@/stories/documentationStyles' const checkboxColors = ['accent', 'blue'] as const const meta = { title: 'Atoms/CpCheckbox', component: CpCheckbox, argTypes: { modelValue: { control: 'boolean', description: 'The checkbox value', }, checkboxValue: { control: 'text', description: 'Value when used in a group', }, checkboxLabel: { control: 'text', description: 'Label text for the checkbox', }, isDisabled: { control: 'boolean', description: 'Whether the checkbox is disabled', }, groupName: { control: 'text', description: 'Name attribute for checkbox group', }, color: { control: 'select', options: checkboxColors, description: 'Color variant of the checkbox', }, reverseLabel: { control: 'boolean', description: 'Whether to show label before the checkbox', }, autofocus: { control: 'boolean', description: 'Whether to autofocus the checkbox', }, helper: { control: 'text', description: 'Helper text to display below the label', }, indeterminate: { control: 'boolean', description: 'Whether the checkbox is in an indeterminate state', }, size: { control: 'select', options: ['md', 'lg'], description: 'Size variant of the checkbox', }, }, } satisfies Meta export default meta type Story = StoryObj /** * Default checkbox. Use the controls to experiment with each prop in isolation. */ export const Default: Story = { args: { checkboxLabel: 'checkbox label', modelValue: false, isDisabled: false, color: 'accent', reverseLabel: false, autofocus: false, helper: '', indeterminate: false, }, render: (args) => ({ components: { CpCheckbox }, setup() { const value = ref(args.modelValue) return { args, value } }, template: `
`, }), } /* -------------------------------------------------------------------------- */ /* States */ /* -------------------------------------------------------------------------- */ /** * Every state available for the checkbox, compared side by side. */ export const States: Story = { parameters: { controls: { disable: true } }, render: () => ({ components: { CpCheckbox }, setup() { return { docCellStyle, docLabelStyle, docRowWrapStyle } }, template: `
Unchecked
Checked
Indeterminate
Disabled
Disabled checked
`, }), } /* -------------------------------------------------------------------------- */ /* Colors */ /* -------------------------------------------------------------------------- */ /** * Color variants rendered side by side. */ export const Colors: Story = { parameters: { controls: { disable: true } }, render: () => ({ components: { CpCheckbox }, setup() { return { checkboxColors, docCellStyle, docLabelStyle, docRowWrapStyle } }, template: `
{{ color }}
`, }), } /* -------------------------------------------------------------------------- */ /* Variants */ /* -------------------------------------------------------------------------- */ /** * Add a helper text below the label. */ export const WithHelper: Story = { args: { ...Default.args, helper: 'This is a helper text that provides additional information', }, } /** * Display the label before the checkbox using `reverseLabel`. */ export const Reversed: Story = { args: { ...Default.args, reverseLabel: true, }, } /* -------------------------------------------------------------------------- */ /* Group patterns */ /* -------------------------------------------------------------------------- */ /** * Parent-child checkbox pattern using the `indeterminate` state to represent * a partial selection. */ export const ParentChildGroup: Story = { parameters: { controls: { disable: true } }, render: () => ({ components: { CpCheckbox }, setup() { const childOptions = ref([ { id: 'email', label: 'Email notifications', checked: false }, { id: 'push', label: 'Push notifications', checked: true }, { id: 'sms', label: 'SMS notifications', checked: false }, ]) const parentState = computed(() => { const checkedCount = childOptions.value.filter((option) => option.checked).length const totalCount = childOptions.value.length if (checkedCount === 0) return { checked: false, indeterminate: false } if (checkedCount === totalCount) return { checked: true, indeterminate: false } return { checked: false, indeterminate: true } }) const handleParentChange = (value: boolean) => childOptions.value.forEach((option) => (option.checked = value)) const handleChildChange = (childId: string) => { const child = childOptions.value.find((option) => option.id === childId) if (child) { child.checked = !child.checked } } return { childOptions, parentState, handleParentChange, handleChildChange, } }, template: `
Parent-Child Checkbox Example
Current state: Indeterminate (some selected) All selected None selected
`, }), } /** * Multiple checkboxes bound to the same v-model array act as a group. */ export const CheckboxGroup: Story = { parameters: { controls: { disable: true } }, render: () => ({ components: { CpCheckbox }, setup() { const selectedValues = ref(['option1']) return { selectedValues } }, template: `
`, }), } /** * Replace the label with custom content using the default slot. */ export const CustomContent: Story = { args: { ...Default.args, checkboxLabel: '', }, render: (args) => ({ components: { CpCheckbox }, setup() { const value = ref(args.modelValue) return { args, value } }, template: `
Custom Content With multiple lines and styling
`, }), }