# @cio/storybook Storybook for ClassroomIO UI components. ## Development To start the Storybook development server: ```bash pnpm dev ``` ## Build To build the Storybook: ```bash pnpm build ``` ## Build Storybook To build the Storybook (alternative command): ```bash pnpm build-storybook ``` ## Upload To upload the built Storybook to Cloudflare R2: ```bash pnpm upload:storybook ``` **Note:** This requires the build to be completed first. Make sure you have the following environment variables set: - `CLOUDFLARE_ACCESS_KEY` - `CLOUDFLARE_SECRET_ACCESS_KEY` - `CLOUDFLARE_ACCOUNT_ID` ## Publish To build and upload the Storybook in one command: ```bash pnpm storybook:publish ``` This will build the Storybook and then upload it to R2. ## How to Create Storybook Stories This guide documents the patterns and conventions for creating Storybook stories in the ClassroomIO UI component library. ### Table of Contents - [File Structure](#file-structure) - [Basic Story Structure](#basic-story-structure) - [Import Patterns](#import-patterns) - [Story Definition](#story-definition) - [Template Snippets](#template-snippets) - [Fields File](#fields-file) - [Component Categorization](#component-categorization) - [CSS Class Conventions](#css-class-conventions) - [LMS Content Guidelines](#lms-content-guidelines) - [Examples](#examples) ### File Structure #### Directory Organization Stories are organized into two main categories: - **Atoms** (`src/atoms/`): Basic, indivisible UI components (Button, Input, Badge, etc.) - **Molecules** (`src/molecules/`): Composite components made of multiple atoms (Card, Field, ButtonGroup, etc.) #### File Naming 1. Create a directory matching the component name (kebab-case) 2. Create a story file: `{component-name}.stories.svelte` 3. Optionally create a fields file: `fields.ts` (for controls configuration) **Example:** ```bash src/molecules/hover-card/ ├── hover-card.stories.svelte └── fields.ts ``` ### Basic Story Structure Every story file follows this structure: ```svelte {#snippet template()} {/snippet} ``` ### Import Patterns #### Component Imports - **Namespace imports** for multi-part components: ```svelte import * as HoverCard from '@cio/ui/base/hover-card'; import * as Card from '@cio/ui/base/card'; import * as Field from '@cio/ui/base/field'; ``` - **Named imports** for single components: ```svelte import {Button} from '@cio/ui/base/button'; import {Input} from '@cio/ui/base/input'; import {Avatar} from '@cio/ui/base/avatar'; ``` #### Icon Imports Use Lucide Svelte icons: ```svelte import CalendarDaysIcon from '@lucide/svelte/icons/calendar-days'; import UsersIcon from '@lucide/svelte/icons/users'; import BookOpenIcon from '@lucide/svelte/icons/book-open'; ``` ### Story Definition #### Title Format - **Atoms**: `'Atom/ComponentName'` (e.g., `'Atom/Button'`) - **Molecules**: `'Molecules/ComponentName'` (e.g., `'Molecules/HoverCard'`) #### Component Reference - For multi-part components, reference the `Root` component: ```svelte component: HoverCard.Root component: Card.Root ``` - For single components, reference directly: ```svelte component: Button component: Input ``` #### Parameters Standard parameters configuration: ```svelte parameters: { layout: 'centered', controls: { include: FIELDS // Only if using fields.ts } } ``` #### Tags Always include `tags: ['autodocs']` for automatic documentation generation. ### Template Snippets #### Basic Template ```svelte {#snippet template()} {/snippet} ``` #### Template with Args (for controls) For atoms that need to pass args to controls: ```svelte {#snippet template(args)} {/snippet} ``` #### Multiple Stories Create multiple stories to showcase different use cases: ```svelte {#snippet template()} {/snippet} {#snippet template()} {/snippet} {#snippet template()} {/snippet} ``` ### Fields File Create a `fields.ts` file to control which props appear in Storybook controls: ```typescript export const FIELDS = ['class'] as string[]; ``` Common fields: - `'class'` - CSS classes - `'variant'` - Component variants - `'size'` - Component sizes - `'disabled'` - Disabled state - Add more as needed based on component props ### Component Categorization #### Atoms Basic, indivisible components: - Button, Input, Badge, Avatar, Label, Checkbox, Switch, etc. - Single-purpose components - Usually don't compose other components #### Molecules Composite components: - Card, Field, ButtonGroup, HoverCard, Empty, etc. - Made of multiple atoms or other components - More complex functionality **Guideline**: If a component uses multiple other components together, it's likely a molecule. ### CSS Class Conventions #### UI Prefix Use the `ui:` prefix for utility classes to scope them properly: ```svelte
{/snippet} ``` #### Example 2: Multi-Part Molecule (HoverCard) ```svelte {#snippet template()} Sarah Johnson
SJ

Sarah Johnson

Senior Full-Stack Developer & Instructor

Teaching since January 2020
{/snippet}
``` #### Example 3: Complex Molecule with State (Field) ```svelte {#snippet template()}
Enroll Student Add a new student to your course Student Email
{/snippet}
``` ### Checklist When creating a new story, ensure: - [ ] File is in the correct directory (`atoms/` or `molecules/`) - [ ] File is named `{component-name}.stories.svelte` - [ ] Imports use `@cio/ui/base/{component-name}` pattern - [ ] Title follows `'Category/ComponentName'` format - [ ] Component reference is correct (Root for multi-part, direct for single) - [ ] `tags: ['autodocs']` is included - [ ] All utility classes use `ui:` prefix - [ ] Content is LMS-related (courses, students, instructors, etc.) - [ ] Multiple stories showcase different use cases - [ ] `fields.ts` is created if controls are needed - [ ] State variables use `$state()` for reactivity - [ ] Icons are imported from `@lucide/svelte/icons/` ### Additional Notes - **State Management**: Use Svelte 5 runes (`$state()`, `$derived()`) for reactive state - **Layout**: Most stories use `layout: 'centered'` in parameters - **Styling**: Prefer Tailwind utility classes with `ui:` prefix - **Accessibility**: Include proper ARIA labels and semantic HTML - **Documentation**: Story names should be descriptive and reflect the use case ### Resources - [Storybook Svelte CSF Addon](https://storybook.js.org/addons/@storybook/addon-svelte-csf) - [Bits UI Documentation](https://bits-ui.com/docs/components) - [Lucide Icons](https://lucide.dev/icons/)