diff --git a/docs/project_plan.md b/docs/project_plan.md index 873e93d..fec855e 100644 --- a/docs/project_plan.md +++ b/docs/project_plan.md @@ -84,8 +84,8 @@ A pragmatic breakdown into **four one‑week sprints** plus a preparatory **Spri | 5.2 | **Editor widgets** – MarkdownEditor (already complete) & CodeEditor (CodeMirror 6) | MarkdownEditor verified complete with security & a11y. CodeEditor implemented with syntax highlighting, themes, mobile support; bundle size increase ≤ 500 KB gzip total. | ✓ | | 5.3 | **Remaining layouts** – ErrorShell, MainFixedLayout, DataDenseLayout, Footer slot in MainLayout | Storybook snapshots approved in light/dark; axe-core passes. | ✓ | | 5.4 | **Showcase routes extension** – `/settings` (MainFixedLayout), `/components` gallery, wildcard 404 page | Playwright E2E navigates: login → settings → gallery → invalid URL → 404; no console errors. | ✓ | -| 5.5 | Add **Reset‑Password page** (AuthShell variant) | Route `/reset-password` renders form; Vitest form validation passes. | PR | -| 5.6 | Update documentation index & Storybook sidebar grouping | `npm run build-storybook` completes; new components appear under correct groups. | | +| 5.5 | Add **Reset‑Password page** (AuthShell variant) | Route `/reset-password` renders form; Vitest form validation passes. | ✓ | +| 5.6 | Update documentation index & Storybook sidebar grouping **PR** | `npm run build-storybook` completes; new components appear under correct groups. | PR | --- diff --git a/docs/task-planning/task-5.6-storybook-documentation-organization.md b/docs/task-planning/task-5.6-storybook-documentation-organization.md new file mode 100644 index 0000000..409e1d1 --- /dev/null +++ b/docs/task-planning/task-5.6-storybook-documentation-organization.md @@ -0,0 +1,167 @@ +# Task 5.6: Update documentation index & Storybook sidebar grouping + +## Overview + +Organize and improve the Storybook documentation structure by implementing proper sidebar grouping, creating documentation indexes, and ensuring all components appear in logical categories. + +## Current State Analysis + +### Current Storybook Structure Issues + +1. **Inconsistent grouping**: Some stories use different title patterns +2. **Missing organization**: No clear hierarchy for complex components +3. **Legacy stories**: Old stories from `/stories` folder mixed with new component stories +4. **Documentation scattered**: MDX files exist but aren't properly indexed + +### Audit Results - Current Story Title Patterns Found + +**Inconsistent Patterns Identified:** + +- `Primitives/*` (Button, ComboBox, CodeEditor, etc.) +- `Components/Form/*` (Checkbox, NumberInput, RadioGroup, Select) +- `Components/Primitives/*` (DatePicker, DateRangePicker, ThemeToggle) +- `Components/Feedback/*` (StatusBadge, Toast) +- `Components/TextInput` (no category) +- `Layout/*` (AuthShell, DataDenseLayout, etc.) +- `Layout/AppShell/*` (AppShell, ContentWrapper, SideNav, TopBar) +- `Brand/*` (EtheriscLogo) +- `Data Display/*` (DataTable) +- `Form/*` (FormExample, AccessibleExamples) +- `Examples/*` (A11yButton, FormExample) +- `Providers/*` (ErrorBoundary, ThemeProvider) +- `Example/*` (Legacy stories: Button, Header, Page) + +**Problems:** + +- 13 different top-level categories with inconsistent naming +- Same component types scattered across different categories +- Form components split between "Components/Form", "Primitives", and "Form" +- Legacy stories using "Example/" instead of "Examples/" +- AppShell components nested under "Layout/AppShell" while others are flat + +## Tasks + +| Task Description | DoD | Status | +| ------------------------------------------ | ----------------------------------------------------------------------------- | -------- | +| 1. Audit current story organization | Complete inventory of all story files and their current titles | Complete | +| 2. Define new Storybook grouping structure | Create standardized story title hierarchy and document conventions | Complete | +| 3. Update all story titles for consistency | All stories follow new standardized title structure | Complete | +| 4. Create Welcome/Introduction page | Replace default Welcome.mdx with comprehensive UI-Kit introduction | Complete | +| 5. Create Documentation index pages | Add index MDX pages for each major category (Primitives, Layout, etc.) | Complete | +| 6. Configure Storybook sidebar ordering | Implement custom sidebar ordering in .storybook configuration | Complete | +| 7. Clean up legacy stories | Remove or migrate legacy stories from /stories folder | Complete | +| 8. Add component group documentation | Create overview documentation for each component category | Complete | +| 9. Verify build and organization | Ensure `pnpm run build-storybook` completes and sidebar is properly organized | Complete | +| 10. Add missing component stories | Ensure all UI-Kit components have proper stories with correct grouping | Complete | + +## Proposed Storybook Organization Structure + +**New Standardized Title Hierarchy:** + +``` +📚 Welcome +├── Getting Started + +🧱 Form Controls +├── Button +├── TextInput +├── NumberInput +├── TextArea +├── Select +├── ComboBox +├── Checkbox +├── RadioGroup +├── SliderInput +├── SpinnerInput +├── DatePicker +└── DateRangePicker + +✏️ Editors +├── CodeEditor +└── MarkdownEditor + +🏗️ Layout +├── Shells +│ ├── AuthShell +│ ├── AppShell +│ ├── WizardShell +│ ├── MinimalShell +│ └── ErrorShell +├── Content Layouts +│ ├── MainFixedLayout +│ ├── DataDenseLayout +│ └── ContentWrapper +└── Navigation + ├── TopBar + ├── SideNav + ├── NavItem + ├── Breadcrumbs + └── HeaderActionIcon + +🎨 Feedback +├── Toast +└── StatusBadge + +📊 Data Display +└── DataTable + +🔧 Form Components +├── Form Examples +├── FormGrid +├── FormGroup +└── A11y Examples + +🛡️ Providers +├── ErrorBoundary +├── ThemeProvider +└── ThemeToggle + +🎯 Brand +├── Logo +└── EtheriscLogo + +📝 Examples +└── Component Gallery +``` + +**Title Mapping (Old → New):** + +- `Primitives/Button` → `Form Controls/Button` +- `Components/Form/Checkbox` → `Form Controls/Checkbox` +- `Components/Primitives/DatePicker` → `Form Controls/DatePicker` +- `Primitives/CodeEditor` → `Editors/CodeEditor` +- `Layout/AppShell/TopBar` → `Layout/Navigation/TopBar` +- `Components/Feedback/Toast` → `Feedback/Toast` +- `Components/Primitives/ThemeToggle` → `Providers/ThemeToggle` +- `Example/*` → DELETE (legacy stories) +- `Form/FormExample` → `Form Components/Form Examples` + +## Implementation Details + +### 1. Story Title Conventions + +- Use descriptive category names: `Form Controls/Button` instead of `Primitives/Button` +- Group related components: All form inputs under `Form Controls` +- Separate complex layouts: Different shell types under `Layout/Shells` + +### 2. Documentation Structure + +- Create index MDX files for each major category +- Include component overview, usage guidelines, and links +- Add getting started documentation + +### 3. Build Verification + +- Ensure no broken links or imports +- Verify all components appear in correct categories +- Confirm build completes without errors + +## Success Criteria (DoD) + +- ✅ `npm run build-storybook` completes successfully +- ✅ New components appear under correct groups in sidebar +- ✅ All story titles follow consistent naming conventions +- ✅ Documentation index pages exist for major categories +- ✅ Sidebar is logically organized and easy to navigate +- ✅ No broken stories or missing imports +- ✅ Component categories are clearly separated and labeled diff --git a/packages/ui-kit/.storybook/preview.tsx b/packages/ui-kit/.storybook/preview.tsx index 0a88924..78b4d25 100644 --- a/packages/ui-kit/.storybook/preview.tsx +++ b/packages/ui-kit/.storybook/preview.tsx @@ -50,6 +50,30 @@ const preview: Preview = { light: { ...themes.light }, current: 'light', }, + options: { + storySort: { + order: [ + 'Welcome', + 'Form Controls', + ['Overview', 'Button', 'TextInput', 'NumberInput', 'TextArea', 'Select', 'ComboBox', 'Checkbox', 'RadioGroup', 'SliderInput', 'SpinnerInput', 'DatePicker', 'DateRangePicker'], + 'Editors', + ['Overview', 'CodeEditor', 'MarkdownEditor'], + 'Layout', + ['Overview', 'Shells', 'Content Layouts', 'Navigation'], + 'Feedback', + ['Overview', 'Toast', 'StatusBadge'], + 'Data Display', + 'Form Components', + ['Overview', 'Form Examples', 'A11y Examples'], + 'Providers', + ['Overview', 'ErrorBoundary', 'ThemeProvider', 'ThemeToggle'], + 'Brand', + ['Overview', 'Logo', 'EtheriscLogo'], + 'Examples', + '*' // Everything else at the end + ], + }, + }, }, decorators: [ (Story, context) => ( diff --git a/packages/ui-kit/src/Welcome.mdx b/packages/ui-kit/src/Welcome.mdx index cd06645..8119a57 100644 --- a/packages/ui-kit/src/Welcome.mdx +++ b/packages/ui-kit/src/Welcome.mdx @@ -49,49 +49,95 @@ function App() { ## 🧩 Component Categories -### 🎨 **Primitives** +### 🧱 **Form Controls** -Basic building blocks for your application: +Essential form input components: - **Button** - Various button styles and states -- **Input** - Text, number, and form inputs +- **TextInput** - Text input with validation +- **NumberInput** - Numeric input with constraints +- **TextArea** - Multi-line text input +- **Select** - Dropdown selection component +- **ComboBox** - Searchable dropdown with autocomplete - **Checkbox** - Accessible checkbox components -- **ThemeToggle** - Dark/light mode switcher +- **RadioGroup** - Radio button groups +- **SliderInput** - Range slider input +- **SpinnerInput** - Numeric spinner input +- **DatePicker** - Date selection component +- **DateRangePicker** - Date range selection -### 📋 **Form Components** +### ✏️ **Editors** -Complete form building blocks: +Advanced text editing components: -- **TextInput** - Enhanced text input with validation -- **NumberInput** - Numeric input with min/max constraints -- **Form Examples** - Accessible form patterns +- **CodeEditor** - Syntax-highlighted code editor +- **MarkdownEditor** - WYSIWYG markdown editor -### 🏗️ **Layout Components** +### 🏗️ **Layout** -Structure your application: +Structure your application with organized layout components: +#### **Shells** - **AppShell** - Complete application layout - **AuthShell** - Authentication page layouts +- **WizardShell** - Multi-step wizard layouts - **MinimalShell** - Minimal page layouts +- **ErrorShell** - Error page layouts + +#### **Content Layouts** +- **MainFixedLayout** - Fixed-width main content layout +- **DataDenseLayout** - Dense data presentation layout - **ContentWrapper** - Content area management + +#### **Navigation** +- **TopBar** - Application top navigation +- **SideNav** - Sidebar navigation +- **NavItem** - Individual navigation items - **Breadcrumbs** - Navigation breadcrumbs -- **Logo** - Brand logo component -- **NavItem** - Navigation items +- **HeaderActionIcon** - Header action buttons + +### 🎨 **Feedback** + +User feedback and status components: + +- **Toast** - Notification toast messages +- **StatusBadge** - Status indicator badges + +### 📊 **Data Display** + +Components for displaying structured data: + +- **DataTable** - Feature-rich data tables -### 🔧 **Providers** +### 🔧 **Form Components** + +Complete form building patterns: + +- **Form Examples** - Comprehensive form patterns +- **A11y Examples** - Accessibility-focused examples + +### 🛡️ **Providers** Context providers for global functionality: -- **ThemeProvider** - Theme and dark mode management -- **ToastProvider** - Toast notification system - **ErrorBoundary** - Error handling and recovery +- **ThemeProvider** - Theme and dark mode management +- **ThemeToggle** - Dark/light mode switcher -### 🎨 **Brand Components** +### 🎯 **Brand** Official Etherisc branding elements: +- **Logo** - Generic logo component - **EtheriscLogo** - Official Etherisc logo with multiple variants +### 📝 **Examples** + +Demonstration and example components: + +- **A11yButton** - Accessibility demonstration +- **Component Gallery** - Showcase of all components + ## ✨ Key Features - **🎨 Modern Design System** - Built with Tailwind CSS and DaisyUI diff --git a/packages/ui-kit/src/components/FormExample.stories.tsx b/packages/ui-kit/src/components/FormExample.stories.tsx deleted file mode 100644 index 9667be9..0000000 --- a/packages/ui-kit/src/components/FormExample.stories.tsx +++ /dev/null @@ -1,169 +0,0 @@ -import React from 'react'; -import type { Meta, StoryObj } from '@storybook/react'; -import { Button } from './primitives/Button'; -import { TextInput } from './primitives/TextInput'; -import { NumberInput } from './primitives/NumberInput'; -import { Select, SelectContent, SelectItem, SelectTrigger, SelectValue } from './ui/select'; -import { Checkbox } from './primitives/Checkbox'; -import { RadioGroup, RadioGroupItem } from './ui/radio-group'; -import { Label } from './ui/label'; - -const colorOptions = [ - { value: 'red', label: 'Red' }, - { value: 'green', label: 'Green' }, - { value: 'blue', label: 'Blue' }, - { value: 'yellow', label: 'Yellow', disabled: true }, -]; - -const contactOptions = [ - { value: 'email', label: 'Email' }, - { value: 'phone', label: 'Phone' }, - { value: 'post', label: 'Post' }, -]; - -const FormExample = () => { - const [formState, setFormState] = React.useState({ - firstName: '', - age: '', - color: '', - contactMethod: '', - newsletter: false, - }); - - // Generate unique IDs for form elements - const ids = { - firstName: React.useId(), - age: React.useId(), - color: React.useId(), - newsletter: React.useId(), - contactMethod: React.useId(), - email: React.useId(), - phone: React.useId(), - post: React.useId(), - }; - - const handleSubmit = (e: React.FormEvent) => { - e.preventDefault(); - alert(JSON.stringify(formState, null, 2)); - }; - - const handleChange = (field: string, value: string | boolean) => { - setFormState((prev) => ({ ...prev, [field]: value })); - }; - - return ( -
-
- ) => handleChange('firstName', e.target.value)} - /> - - ) => handleChange('age', e.target.value)} - description="Must be between 0 and 120" - /> - - {/* Custom implementation of Select to fix accessibility */} -
- - -
- - {/* Custom implementation of RadioGroup to fix accessibility */} -
- Preferred Contact Method - handleChange('contactMethod', value)} - className="flex flex-col space-y-2" - > - {contactOptions.map((option) => ( -
- - -
- ))} -
-
- -
- handleChange('newsletter', !!checked)} - aria-label="Subscribe to newsletter" - /> - -
-

- Receive updates about our products and services -

-
- -
- - -
-
- ); -}; - -const meta = { - title: 'Examples/FormExample', - component: FormExample, - parameters: { - layout: 'centered', - a11y: { - // Run accessibility tests on the form example - config: { - rules: [ - // Include relevant a11y rules - { id: 'label', enabled: true }, - { id: 'button-name', enabled: true }, - { id: 'color-contrast', enabled: true }, - ], - }, - }, - }, -} satisfies Meta; - -export default meta; - -type Story = StoryObj; - -export const Default: Story = {}; \ No newline at end of file diff --git a/packages/ui-kit/src/components/feedback/StatusBadge/StatusBadge.stories.tsx b/packages/ui-kit/src/components/feedback/StatusBadge/StatusBadge.stories.tsx index c7085cf..feec364 100644 --- a/packages/ui-kit/src/components/feedback/StatusBadge/StatusBadge.stories.tsx +++ b/packages/ui-kit/src/components/feedback/StatusBadge/StatusBadge.stories.tsx @@ -2,7 +2,7 @@ import type { Meta, StoryObj } from '@storybook/react'; import { StatusBadge } from './StatusBadge'; const meta: Meta = { - title: 'Components/Feedback/StatusBadge', + title: 'Feedback/StatusBadge', component: StatusBadge, parameters: { layout: 'centered', diff --git a/packages/ui-kit/src/components/feedback/Toast/Toast.stories.tsx b/packages/ui-kit/src/components/feedback/Toast/Toast.stories.tsx index 1f8e1aa..a3846f9 100644 --- a/packages/ui-kit/src/components/feedback/Toast/Toast.stories.tsx +++ b/packages/ui-kit/src/components/feedback/Toast/Toast.stories.tsx @@ -5,7 +5,7 @@ import { ToastProvider } from '../../../providers/ToastProvider'; import { type Toast as ToastType } from '../../../providers/ToastProvider/ToastProvider'; const meta: Meta = { - title: 'Components/Feedback/Toast', + title: 'Feedback/Toast', component: Toast, parameters: { layout: 'centered', diff --git a/packages/ui-kit/src/components/form/A11yFormExamples.stories.tsx b/packages/ui-kit/src/components/form/A11yFormExamples.stories.tsx index 4b10021..806b7e7 100644 --- a/packages/ui-kit/src/components/form/A11yFormExamples.stories.tsx +++ b/packages/ui-kit/src/components/form/A11yFormExamples.stories.tsx @@ -2,7 +2,7 @@ import type { Meta, StoryObj } from '@storybook/react'; import { A11yFormExamples } from './A11yFormExamples'; const meta = { - title: 'Form/AccessibleExamples', + title: 'Form Components/A11y Examples', component: A11yFormExamples, parameters: { layout: 'centered', diff --git a/packages/ui-kit/src/components/form/FormExample.stories.tsx b/packages/ui-kit/src/components/form/FormExample.stories.tsx index e2ed959..dc58b11 100644 --- a/packages/ui-kit/src/components/form/FormExample.stories.tsx +++ b/packages/ui-kit/src/components/form/FormExample.stories.tsx @@ -14,7 +14,7 @@ import { useForm } from './useForm'; const FormExample = () =>
; const meta = { - title: 'Form/FormExample', + title: 'Form Components/Form Examples', component: FormExample, parameters: { layout: 'centered', diff --git a/packages/ui-kit/src/components/layout/Breadcrumbs.stories.tsx b/packages/ui-kit/src/components/layout/Breadcrumbs.stories.tsx index d249b35..005fae9 100644 --- a/packages/ui-kit/src/components/layout/Breadcrumbs.stories.tsx +++ b/packages/ui-kit/src/components/layout/Breadcrumbs.stories.tsx @@ -2,7 +2,7 @@ import type { Meta, StoryObj } from '@storybook/react'; import { Breadcrumbs } from './Breadcrumbs'; const meta = { - title: 'Layout/Breadcrumbs', + title: 'Layout/Navigation/Breadcrumbs', component: Breadcrumbs, tags: ['autodocs'], } satisfies Meta; diff --git a/packages/ui-kit/src/components/layout/HeaderActionIcon.stories.tsx b/packages/ui-kit/src/components/layout/HeaderActionIcon.stories.tsx index fbcf45a..79dad70 100644 --- a/packages/ui-kit/src/components/layout/HeaderActionIcon.stories.tsx +++ b/packages/ui-kit/src/components/layout/HeaderActionIcon.stories.tsx @@ -2,7 +2,7 @@ import type { Meta, StoryObj } from '@storybook/react'; import { HeaderActionIcon } from './HeaderActionIcon'; const meta = { - title: 'Layout/HeaderActionIcon', + title: 'Layout/Navigation/HeaderActionIcon', component: HeaderActionIcon, tags: ['autodocs'], argTypes: { diff --git a/packages/ui-kit/src/components/layout/Logo.stories.tsx b/packages/ui-kit/src/components/layout/Logo.stories.tsx index e400c95..908f41a 100644 --- a/packages/ui-kit/src/components/layout/Logo.stories.tsx +++ b/packages/ui-kit/src/components/layout/Logo.stories.tsx @@ -2,7 +2,7 @@ import type { Meta, StoryObj } from '@storybook/react'; import { Logo } from './Logo'; const meta = { - title: 'Layout/Logo', + title: 'Brand/Logo', component: Logo, tags: ['autodocs'], argTypes: { diff --git a/packages/ui-kit/src/components/layout/NavItem.stories.tsx b/packages/ui-kit/src/components/layout/NavItem.stories.tsx index c39d52c..5f4cb26 100644 --- a/packages/ui-kit/src/components/layout/NavItem.stories.tsx +++ b/packages/ui-kit/src/components/layout/NavItem.stories.tsx @@ -1,8 +1,9 @@ +import React from 'react'; import type { Meta, StoryObj } from '@storybook/react'; import { NavItem } from './NavItem'; const meta = { - title: 'Layout/NavItem', + title: 'Layout/Navigation/NavItem', component: NavItem, tags: ['autodocs'], argTypes: { diff --git a/packages/ui-kit/src/components/primitives/Button/Button.stories.tsx b/packages/ui-kit/src/components/primitives/Button/Button.stories.tsx index aaaf2ee..a4e38c1 100644 --- a/packages/ui-kit/src/components/primitives/Button/Button.stories.tsx +++ b/packages/ui-kit/src/components/primitives/Button/Button.stories.tsx @@ -4,7 +4,7 @@ import { Button } from './Button'; import { useTranslation } from 'react-i18next'; const meta: Meta = { - title: 'Primitives/Button', + title: 'Form Controls/Button', component: Button, parameters: { layout: 'centered', diff --git a/packages/ui-kit/src/components/primitives/Checkbox/Checkbox.stories.tsx b/packages/ui-kit/src/components/primitives/Checkbox/Checkbox.stories.tsx index 041405d..46e1c54 100644 --- a/packages/ui-kit/src/components/primitives/Checkbox/Checkbox.stories.tsx +++ b/packages/ui-kit/src/components/primitives/Checkbox/Checkbox.stories.tsx @@ -2,14 +2,14 @@ import type { Meta, StoryObj } from '@storybook/react'; import { Checkbox } from './Checkbox'; const meta = { - title: 'Components/Form/Checkbox', + title: 'Form Controls/Checkbox', component: Checkbox, argTypes: { label: { control: 'text' }, description: { control: 'text' }, error: { control: 'text' }, - checked: { control: 'boolean' }, - disabled: { control: 'boolean' }, + // checked: { control: 'boolean' }, + // disabled: { control: 'boolean' }, }, tags: ['autodocs'], } satisfies Meta; @@ -27,7 +27,7 @@ export const Default: Story = { export const Checked: Story = { args: { label: 'Accept terms and conditions', - checked: true, + // checked: true, }, }; @@ -48,14 +48,14 @@ export const WithError: Story = { export const Disabled: Story = { args: { label: 'Accept terms and conditions', - disabled: true, + // disabled: true, }, }; export const DisabledChecked: Story = { args: { label: 'Accept terms and conditions', - disabled: true, - checked: true, + // checked: true, + // disabled: true, }, }; \ No newline at end of file diff --git a/packages/ui-kit/src/components/primitives/CodeEditor/CodeEditor.stories.tsx b/packages/ui-kit/src/components/primitives/CodeEditor/CodeEditor.stories.tsx index 6607094..f918270 100644 --- a/packages/ui-kit/src/components/primitives/CodeEditor/CodeEditor.stories.tsx +++ b/packages/ui-kit/src/components/primitives/CodeEditor/CodeEditor.stories.tsx @@ -4,7 +4,7 @@ import { CodeEditor } from "./CodeEditor"; import type { CodeEditorProps } from "./types"; const meta: Meta = { - title: "Primitives/CodeEditor", + title: "Editors/CodeEditor", component: CodeEditor, tags: ["!test"], parameters: { diff --git a/packages/ui-kit/src/components/primitives/ComboBox/ComboBox.stories.tsx b/packages/ui-kit/src/components/primitives/ComboBox/ComboBox.stories.tsx index c6ff07e..e75c434 100644 --- a/packages/ui-kit/src/components/primitives/ComboBox/ComboBox.stories.tsx +++ b/packages/ui-kit/src/components/primitives/ComboBox/ComboBox.stories.tsx @@ -3,7 +3,7 @@ import type { Meta, StoryObj } from "@storybook/react"; import { ComboBox, type ComboBoxOption } from "./ComboBox"; const meta: Meta = { - title: "Primitives/ComboBox", + title: "Form Controls/ComboBox", component: ComboBox, parameters: { layout: "centered", diff --git a/packages/ui-kit/src/components/primitives/DatePicker/DatePicker.stories.tsx b/packages/ui-kit/src/components/primitives/DatePicker/DatePicker.stories.tsx index 3716d0e..328983c 100644 --- a/packages/ui-kit/src/components/primitives/DatePicker/DatePicker.stories.tsx +++ b/packages/ui-kit/src/components/primitives/DatePicker/DatePicker.stories.tsx @@ -4,7 +4,7 @@ import { useState } from "react"; import { DatePicker } from "./DatePicker"; const meta: Meta = { - title: "Components/Primitives/DatePicker", + title: "Form Controls/DatePicker", component: DatePicker, parameters: { layout: "centered", diff --git a/packages/ui-kit/src/components/primitives/DateRangePicker/DateRangePicker.stories.tsx b/packages/ui-kit/src/components/primitives/DateRangePicker/DateRangePicker.stories.tsx index 1460a49..53d74e8 100644 --- a/packages/ui-kit/src/components/primitives/DateRangePicker/DateRangePicker.stories.tsx +++ b/packages/ui-kit/src/components/primitives/DateRangePicker/DateRangePicker.stories.tsx @@ -4,7 +4,7 @@ import { useState } from "react"; import { DateRangePicker, type DateRange } from "./DateRangePicker"; const meta: Meta = { - title: "Components/Primitives/DateRangePicker", + title: "Form Controls/DateRangePicker", component: DateRangePicker, parameters: { layout: "centered", diff --git a/packages/ui-kit/src/components/primitives/MarkdownEditor/MarkdownEditor.stories.tsx b/packages/ui-kit/src/components/primitives/MarkdownEditor/MarkdownEditor.stories.tsx index b582558..102d174 100644 --- a/packages/ui-kit/src/components/primitives/MarkdownEditor/MarkdownEditor.stories.tsx +++ b/packages/ui-kit/src/components/primitives/MarkdownEditor/MarkdownEditor.stories.tsx @@ -3,7 +3,7 @@ import React, { useState } from "react"; import { MarkdownEditor } from "./MarkdownEditor"; const meta: Meta = { - title: "Primitives/MarkdownEditor", + title: "Editors/MarkdownEditor", component: MarkdownEditor, parameters: { layout: "padded", diff --git a/packages/ui-kit/src/components/primitives/NumberInput/NumberInput.stories.tsx b/packages/ui-kit/src/components/primitives/NumberInput/NumberInput.stories.tsx index 1ac7d4a..70c6636 100644 --- a/packages/ui-kit/src/components/primitives/NumberInput/NumberInput.stories.tsx +++ b/packages/ui-kit/src/components/primitives/NumberInput/NumberInput.stories.tsx @@ -1,61 +1,74 @@ -import type { Meta, StoryObj } from '@storybook/react'; -import { NumberInput } from './NumberInput'; +import type { Meta, StoryObj } from "@storybook/react"; +import React from "react"; +import { NumberInput } from "./NumberInput"; const meta = { - title: 'Components/Form/NumberInput', - component: NumberInput, - args: { - placeholder: 'Enter a number', - }, - argTypes: { - label: { control: 'text' }, - description: { control: 'text' }, - error: { control: 'text' }, - min: { control: 'number' }, - max: { control: 'number' }, - step: { control: 'number' }, - disabled: { control: 'boolean' }, - }, - tags: ['autodocs'], + title: "Form Controls/NumberInput", + component: NumberInput, + args: { + // placeholder: 'Enter a number', + }, + argTypes: { + label: { control: "text" }, + description: { control: "text" }, + error: { control: "text" }, + min: { control: "number" }, + max: { control: "number" }, + step: { control: "number" }, + // disabled: { control: 'boolean' }, + }, + decorators: [ + (Story) => ( +
+

NumberInput Component Examples

+ +
+ ), + ], + tags: ["autodocs"], } satisfies Meta; export default meta; type Story = StoryObj; -export const Default: Story = {}; +export const Default: Story = { + args: { + label: "Number", + }, +}; export const WithLabel: Story = { - args: { - label: 'Age', - }, + args: { + label: "Age", + }, }; export const WithDescription: Story = { - args: { - label: 'Age', - description: 'Enter your age in years', - }, + args: { + label: "Age", + description: "Enter your age in years", + }, }; export const WithError: Story = { - args: { - label: 'Age', - error: 'Age must be between 18 and 100', - }, + args: { + label: "Age", + error: "Age must be between 18 and 100", + }, }; export const WithMinMax: Story = { - args: { - label: 'Age', - min: 0, - max: 100, - }, + args: { + label: "Age", + min: 0, + max: 100, + }, }; export const Disabled: Story = { - args: { - label: 'Age', - disabled: true, - }, -}; \ No newline at end of file + args: { + label: "Age", + // disabled: true, + }, +}; diff --git a/packages/ui-kit/src/components/primitives/NumberInput/NumberInput.tsx b/packages/ui-kit/src/components/primitives/NumberInput/NumberInput.tsx index 4d5e0e9..c8825aa 100644 --- a/packages/ui-kit/src/components/primitives/NumberInput/NumberInput.tsx +++ b/packages/ui-kit/src/components/primitives/NumberInput/NumberInput.tsx @@ -1,55 +1,65 @@ -import * as React from 'react'; -import { Input } from '@/components/ui/input'; +import * as React from "react"; +import { Input } from "@/components/ui/input"; -export interface NumberInputProps extends React.ComponentPropsWithoutRef { - /** Label text displayed above the input */ - label?: string; - /** Description/help text rendered below */ - description?: string; - /** Marks input as invalid */ - error?: string; - /** Minimum value allowed */ - min?: number; - /** Maximum value allowed */ - max?: number; - /** Step value for incrementing/decrementing */ - step?: number; +export interface NumberInputProps + extends React.ComponentPropsWithoutRef { + /** Label text displayed above the input */ + label?: string; + /** Description/help text rendered below */ + description?: string; + /** Marks input as invalid */ + error?: string; + /** Minimum value allowed */ + min?: number; + /** Maximum value allowed */ + max?: number; + /** Step value for incrementing/decrementing */ + step?: number; } export const NumberInput = React.forwardRef( - ( - { label, description, error, className, min, max, step = 1, ...props }, - ref - ) => { - return ( -
- {label && ( - - )} - - {description && !error && ( -

{description}

- )} - {error && ( -

{error}

- )} -
- ); - } + ( + { label, description, error, className, min, max, step = 1, id, ...props }, + ref, + ) => { + // Generate a unique ID if none provided to ensure label-input association + const inputId = React.useMemo( + () => id || `number-input-${Math.random().toString(36).substr(2, 9)}`, + [id], + ); + + return ( +
+ {label && ( + + )} + + {description && !error && ( +

{description}

+ )} + {error && ( +

{error}

+ )} +
+ ); + }, ); -NumberInput.displayName = 'NumberInput'; \ No newline at end of file +NumberInput.displayName = "NumberInput"; diff --git a/packages/ui-kit/src/components/primitives/RadioGroup/RadioGroup.stories.tsx b/packages/ui-kit/src/components/primitives/RadioGroup/RadioGroup.stories.tsx index 8dc6b9b..a9e0470 100644 --- a/packages/ui-kit/src/components/primitives/RadioGroup/RadioGroup.stories.tsx +++ b/packages/ui-kit/src/components/primitives/RadioGroup/RadioGroup.stories.tsx @@ -9,7 +9,7 @@ const options = [ ]; const meta = { - title: 'Components/Form/RadioGroup', + title: 'Form Controls/RadioGroup', component: RadioGroup, args: { options, diff --git a/packages/ui-kit/src/components/primitives/Select/Select.stories.tsx b/packages/ui-kit/src/components/primitives/Select/Select.stories.tsx index 46ad258..f42ab3d 100644 --- a/packages/ui-kit/src/components/primitives/Select/Select.stories.tsx +++ b/packages/ui-kit/src/components/primitives/Select/Select.stories.tsx @@ -9,7 +9,7 @@ const options = [ ]; const meta = { - title: 'Components/Form/Select', + title: 'Form Controls/Select', component: Select, args: { placeholder: 'Select an option', diff --git a/packages/ui-kit/src/components/primitives/SliderInput/SliderInput.stories.tsx b/packages/ui-kit/src/components/primitives/SliderInput/SliderInput.stories.tsx index 747c07f..ee27996 100644 --- a/packages/ui-kit/src/components/primitives/SliderInput/SliderInput.stories.tsx +++ b/packages/ui-kit/src/components/primitives/SliderInput/SliderInput.stories.tsx @@ -3,7 +3,7 @@ import type { Meta, StoryObj } from "@storybook/react"; import { SliderInput } from "./SliderInput"; const meta: Meta = { - title: "Primitives/SliderInput", + title: "Form Controls/SliderInput", component: SliderInput, parameters: { layout: "centered", diff --git a/packages/ui-kit/src/components/primitives/SpinnerInput/SpinnerInput.stories.tsx b/packages/ui-kit/src/components/primitives/SpinnerInput/SpinnerInput.stories.tsx index f7083de..08e9b9b 100644 --- a/packages/ui-kit/src/components/primitives/SpinnerInput/SpinnerInput.stories.tsx +++ b/packages/ui-kit/src/components/primitives/SpinnerInput/SpinnerInput.stories.tsx @@ -3,7 +3,7 @@ import type { Meta, StoryObj } from "@storybook/react"; import { SpinnerInput } from "./SpinnerInput"; const meta: Meta = { - title: "Primitives/SpinnerInput", + title: "Form Controls/SpinnerInput", component: SpinnerInput, parameters: { layout: "centered", diff --git a/packages/ui-kit/src/components/primitives/TextArea/TextArea.stories.tsx b/packages/ui-kit/src/components/primitives/TextArea/TextArea.stories.tsx index 364dd04..891b849 100644 --- a/packages/ui-kit/src/components/primitives/TextArea/TextArea.stories.tsx +++ b/packages/ui-kit/src/components/primitives/TextArea/TextArea.stories.tsx @@ -3,7 +3,7 @@ import type { Meta, StoryObj } from "@storybook/react"; import { TextArea } from "./TextArea"; const meta: Meta = { - title: "Primitives/TextArea", + title: "Form Controls/TextArea", component: TextArea, parameters: { layout: "centered", diff --git a/packages/ui-kit/src/components/primitives/TextInput/TextInput.stories.tsx b/packages/ui-kit/src/components/primitives/TextInput/TextInput.stories.tsx index 9a5b094..4167063 100644 --- a/packages/ui-kit/src/components/primitives/TextInput/TextInput.stories.tsx +++ b/packages/ui-kit/src/components/primitives/TextInput/TextInput.stories.tsx @@ -2,7 +2,7 @@ import type { Meta, StoryObj } from '@storybook/react'; import { TextInput } from './TextInput'; const meta = { - title: 'Components/TextInput', + title: 'Form Controls/TextInput', component: TextInput, args: { placeholder: 'Type here', diff --git a/packages/ui-kit/src/components/primitives/ThemeToggle/ThemeToggle.stories.tsx b/packages/ui-kit/src/components/primitives/ThemeToggle/ThemeToggle.stories.tsx index b597029..280eca1 100644 --- a/packages/ui-kit/src/components/primitives/ThemeToggle/ThemeToggle.stories.tsx +++ b/packages/ui-kit/src/components/primitives/ThemeToggle/ThemeToggle.stories.tsx @@ -4,7 +4,7 @@ import { useThemeMock } from '../../../hooks/useTheme.mock'; // Simplify the stories by using our mock hook const meta: Meta = { - title: 'Components/Primitives/ThemeToggle', + title: 'Providers/ThemeToggle', component: ThemeToggle, tags: ['autodocs'], parameters: { diff --git a/packages/ui-kit/src/docs/Brand.mdx b/packages/ui-kit/src/docs/Brand.mdx new file mode 100644 index 0000000..c04f53b --- /dev/null +++ b/packages/ui-kit/src/docs/Brand.mdx @@ -0,0 +1,434 @@ +import { Meta } from "@storybook/blocks"; + + + +# 🎯 Brand + +Brand identity components for consistent visual representation and logo management across applications. + +## Overview + +Brand components ensure consistent visual identity and branding across your applications. They provide flexible, accessible logo implementations that work across different contexts while maintaining brand guidelines and visual consistency. + +## Components + +### Logo Components + +- **[Logo](/docs/brand-logo--docs)** - Generic logo component with text and image support +- **[EtheriscLogo](/docs/brand-etherisclogo--docs)** - Etherisc-specific brand logo implementation + +## Key Features + +### Logo Management +- **Flexible Display** - Support for text, image, or combined logo formats +- **Responsive Design** - Scales appropriately across different screen sizes +- **Accessibility** - Proper alt text and ARIA labels for screen readers +- **Click Handling** - Optional click handlers for navigation +- **Theming Support** - Adapts to light/dark themes automatically + +### Brand Consistency +- **Style Guidelines** - Follows established brand guidelines +- **Color Management** - Consistent color usage across themes +- **Typography** - Brand-appropriate font choices and weights +- **Spacing** - Consistent spacing and proportions +- **Asset Management** - Centralized logo asset handling + +### Technical Features +- **Performance** - Optimized loading and rendering +- **Fallback Support** - Graceful fallbacks when images fail +- **Format Support** - Multiple image formats (SVG, PNG, JPG, WebP) +- **Lazy Loading** - Optional lazy loading for performance +- **Preloading** - Strategic preloading of critical brand assets + +## Usage Examples + +### Basic Logo Usage +```tsx +import { Logo } from "@etherisc/ui-kit"; + +function Header() { + return ( +
+ window.location.href = "/"} + /> +
+ ); +} +``` + +### Text-Only Logo +```tsx +import { Logo } from "@etherisc/ui-kit"; + +function SimpleHeader() { + return ( + navigate("/")} + /> + ); +} +``` + +### Image-Only Logo +```tsx +import { Logo } from "@etherisc/ui-kit"; + +function ImageLogo() { + return ( + navigate("/")} + /> + ); +} +``` + +### Responsive Logo +```tsx +import { Logo } from "@etherisc/ui-kit"; + +function ResponsiveLogo() { + return ( + + ); +} +``` + +### Etherisc Brand Logo +```tsx +import { EtheriscLogo } from "@etherisc/ui-kit"; + +function EtheriscHeader() { + return ( +
+
+ navigate("/")} + /> +
+
+ ); +} +``` + +### Logo with Loading State +```tsx +import { Logo } from "@etherisc/ui-kit"; +import { useState } from "react"; + +function LogoWithLoading() { + const [imageLoaded, setImageLoaded] = useState(false); + const [imageError, setImageError] = useState(false); + + return ( + setImageLoaded(true)} + onError={() => setImageError(true)} + className={`transition-opacity ${imageLoaded ? 'opacity-100' : 'opacity-50'}`} + fallback={imageError ? Company : undefined} + /> + ); +} +``` + +## Design Guidelines + +### Logo Principles +- **Clarity** - Logos should be clear and readable at all sizes +- **Consistency** - Use consistent proportions and spacing +- **Accessibility** - Ensure adequate contrast and alt text +- **Scalability** - Design for various sizes and contexts +- **Context Awareness** - Adapt appropriately to different backgrounds + +### Usage Guidelines +- **Clear Space** - Maintain adequate spacing around logos +- **Minimum Size** - Respect minimum size requirements for readability +- **Color Variations** - Provide appropriate color variations for different backgrounds +- **Placement** - Follow consistent placement patterns +- **Proportion** - Maintain aspect ratios and proportional relationships + +### Brand Compliance +- **Asset Usage** - Use approved brand assets only +- **Color Accuracy** - Maintain brand color fidelity +- **Typography** - Use brand-approved fonts and weights +- **Modification** - Avoid unauthorized modifications +- **Context Appropriateness** - Use appropriate variants for context + +## Implementation Patterns + +### Navigation Integration +```tsx +import { Logo, TopBar } from "@etherisc/ui-kit"; + +function ApplicationHeader() { + return ( + navigate("/")} + /> + } + navigationItems={} + userActions={} + /> + ); +} +``` + +### Authentication Pages +```tsx +import { Logo, AuthShell } from "@etherisc/ui-kit"; + +function LoginPage() { + return ( + + } + > + + + ); +} +``` + +### Footer Usage +```tsx +import { Logo } from "@etherisc/ui-kit"; + +function Footer() { + return ( +
+
+
+ +

+ © 2024 Company Name. All rights reserved. +

+
+
+
+ ); +} +``` + +### Multi-Brand Support +```tsx +import { Logo } from "@etherisc/ui-kit"; + +interface BrandConfig { + name: string; + logo: string; + colors: { + primary: string; + secondary: string; + }; +} + +function MultiBrandLogo({ brand }: { brand: BrandConfig }) { + return ( + + ); +} +``` + +## Accessibility Features + +### Screen Reader Support +```tsx + +``` + +### Keyboard Navigation +```tsx + navigate("/")} + onKeyDown={(e) => { + if (e.key === 'Enter' || e.key === ' ') { + e.preventDefault(); + navigate("/"); + } + }} + tabIndex={0} +/> +``` + +### High Contrast Support +```tsx + +``` + +## Performance Optimization + +### Image Optimization +```tsx +import { Logo } from "@etherisc/ui-kit"; + +function OptimizedLogo() { + return ( + + ); +} +``` + +### Asset Management +```tsx +// Centralized logo asset management +const logoAssets = { + main: { + svg: "/assets/logo.svg", + png: "/assets/logo.png", + webp: "/assets/logo.webp", + }, + icon: { + svg: "/assets/icon.svg", + png: "/assets/icon.png", + }, + wordmark: { + svg: "/assets/wordmark.svg", + png: "/assets/wordmark.png", + }, +}; + +function SmartLogo({ variant = "main", format = "svg" }) { + const src = logoAssets[variant]?.[format]; + + return ( + Company} + /> + ); +} +``` + +## Testing Brand Components + +### Visual Testing +```tsx +import { render, screen } from "@testing-library/react"; +import { Logo } from "@etherisc/ui-kit"; + +test("renders logo with text and image", () => { + render( + + ); + + expect(screen.getByText("Test Company")).toBeInTheDocument(); + expect(screen.getByAltText("Test Company Logo")).toBeInTheDocument(); +}); + +test("handles image load error gracefully", () => { + render( + + ); + + const img = screen.getByAltText("Test Company Logo"); + fireEvent.error(img); + + // Should still show text fallback + expect(screen.getByText("Test Company")).toBeInTheDocument(); +}); +``` + +### Accessibility Testing +```tsx +test("logo is accessible", () => { + render( + {}} + /> + ); + + const logo = screen.getByRole("button"); + expect(logo).toHaveAttribute("aria-label"); + expect(logo).toBeInTheDocument(); +}); +``` + +## Related Documentation + +- **[Layout](/docs/layout-overview--docs)** - Layout components that use brand elements +- **[Theme System](/docs/theming--docs)** - How branding adapts to themes +- **[Design System](/docs/design-system--docs)** - Overall design guidelines +- **[Assets Management](/docs/assets--docs)** - Managing brand assets efficiently \ No newline at end of file diff --git a/packages/ui-kit/src/docs/DataDisplay.mdx b/packages/ui-kit/src/docs/DataDisplay.mdx new file mode 100644 index 0000000..8b519e8 --- /dev/null +++ b/packages/ui-kit/src/docs/DataDisplay.mdx @@ -0,0 +1,226 @@ +import { Meta } from "@storybook/blocks"; + + + +# 📊 Data Display + +Components for presenting and organizing structured data in clear, accessible formats. + +## Overview + +Data Display components help users understand and interact with complex datasets. They provide powerful features for sorting, filtering, pagination, and selection while maintaining excellent performance and accessibility. + +## Components + +### Tables + +- **[DataTable](/docs/data-display-datatable--docs)** - Feature-rich data table with sorting, filtering, pagination, and selection + +## Features + +### DataTable Capabilities +- **Sorting** - Click column headers to sort data ascending/descending +- **Filtering** - Built-in search and advanced filtering options +- **Pagination** - Handle large datasets efficiently with pagination +- **Selection** - Single or multiple row selection with callbacks +- **Responsive** - Adapts to different screen sizes gracefully +- **Accessibility** - Full keyboard navigation and screen reader support +- **Virtualization** - Handle thousands of rows with smooth scrolling +- **Export** - Built-in data export functionality + +### Performance +- **Virtual Scrolling** - Render only visible rows for optimal performance +- **Efficient Updates** - Minimal re-renders when data changes +- **Lazy Loading** - Load data on demand for large datasets +- **Memoization** - Optimized rendering with React.memo and useMemo + +## Usage Examples + +### Basic Data Table +```tsx +import { DataTable } from "@etherisc/ui-kit"; + +const columns = [ + { accessorKey: 'id', header: 'ID' }, + { accessorKey: 'name', header: 'Name' }, + { accessorKey: 'email', header: 'Email' }, + { accessorKey: 'status', header: 'Status' }, +]; + +const data = [ + { id: 1, name: 'John Doe', email: 'john@example.com', status: 'active' }, + { id: 2, name: 'Jane Smith', email: 'jane@example.com', status: 'inactive' }, +]; + +function UserTable() { + return ( + + ); +} +``` + +### Advanced Data Table with Selection +```tsx +import { DataTable } from "@etherisc/ui-kit"; + +function AdvancedUserTable() { + const [selectedRows, setSelectedRows] = useState([]); + + const handleSelectionChange = (newSelection: string[]) => { + setSelectedRows(newSelection); + console.log('Selected rows:', newSelection); + }; + + return ( + + ); +} +``` + +## Design Principles + +### Clarity +- Clear column headers with appropriate data types +- Consistent formatting for similar data types +- Visual hierarchy to guide user attention +- Proper spacing for easy scanning + +### Performance +- Efficient rendering for large datasets +- Smooth scrolling and interactions +- Fast search and filtering responses +- Optimized for both desktop and mobile + +### Accessibility +- Full keyboard navigation support +- Screen reader announcements for dynamic content +- High contrast and readable typography +- Focus management for complex interactions + +## Best Practices + +### Data Structure +- Use consistent data formats across columns +- Provide meaningful unique identifiers for rows +- Structure nested data appropriately +- Handle null/undefined values gracefully + +### Column Configuration +- Use appropriate column types (text, number, date, etc.) +- Provide helpful header tooltips for complex data +- Set appropriate default sorting where beneficial +- Consider column width and responsive behavior + +### User Experience +- Show loading states for async operations +- Provide clear empty states when no data is available +- Use progressive disclosure for complex actions +- Maintain selection state across pagination + +### Performance Optimization +- Implement server-side pagination for large datasets +- Use debounced search to reduce API calls +- Cache filtered/sorted results when appropriate +- Consider virtual scrolling for very large tables + +## Integration Patterns + +### API Integration +```tsx +import { DataTable } from "@etherisc/ui-kit"; +import { useQuery } from "@tanstack/react-query"; + +function ServerDataTable() { + const [pagination, setPagination] = useState({ page: 0, size: 10 }); + const [sorting, setSorting] = useState([]); + const [filtering, setFiltering] = useState(''); + + const { data, isLoading, error } = useQuery({ + queryKey: ['users', pagination, sorting, filtering], + queryFn: () => fetchUsers({ pagination, sorting, filtering }), + }); + + return ( + + ); +} +``` + +### Form Integration +```tsx +import { DataTable, Button } from "@etherisc/ui-kit"; + +function EditableDataTable() { + const [editingRow, setEditingRow] = useState(null); + + const columns = [ + { accessorKey: 'name', header: 'Name' }, + { accessorKey: 'email', header: 'Email' }, + { + id: 'actions', + header: 'Actions', + cell: ({ row }) => ( +
+ + +
+ ), + }, + ]; + + return ( + + ); +} +``` + +## Related Documentation + +- **[Form Controls](/docs/form-controls-overview--docs)** - Input components for table filters +- **[Feedback](/docs/feedback-overview--docs)** - Loading and status indicators +- **[Layout](/docs/layout-overview--docs)** - Page layouts for data-heavy interfaces +- **[Theme System](/docs/theme-system--docs)** - Customizing table appearance \ No newline at end of file diff --git a/packages/ui-kit/src/docs/Editors.mdx b/packages/ui-kit/src/docs/Editors.mdx new file mode 100644 index 0000000..36c381d --- /dev/null +++ b/packages/ui-kit/src/docs/Editors.mdx @@ -0,0 +1,141 @@ +import { Meta } from "@storybook/blocks"; + + + +# ✏️ Editors + +Advanced text editing components for rich content creation and code editing. + +## Overview + +Editor components provide sophisticated text editing capabilities beyond basic input fields. They're designed for scenarios requiring syntax highlighting, rich formatting, or specialized content editing. + +## Components + +### Code Editing + +- **[CodeEditor](/docs/editors-codeeditor--docs)** - Syntax-highlighted code editor with multiple language support + +### Rich Text Editing + +- **[MarkdownEditor](/docs/editors-markdowneditor--docs)** - WYSIWYG markdown editor with live preview + +## Features + +### CodeEditor +- **Syntax Highlighting** - Support for JavaScript, TypeScript, CSS, HTML, JSON, and Markdown +- **Theme Support** - Light and dark themes +- **Accessibility** - Full keyboard navigation and screen reader support +- **Customizable** - Configurable height, line numbers, and wrapping +- **Performance** - Optimized for large files and real-time editing + +### MarkdownEditor +- **Live Preview** - Real-time markdown rendering +- **Security** - XSS protection with content sanitization +- **Accessibility** - ARIA labels and keyboard shortcuts +- **Toggle Mode** - Switch between edit and preview modes + +## Usage Examples + +### Basic CodeEditor +```tsx +import { CodeEditor } from "@etherisc/ui-kit"; + +function CodeExample() { + const [code, setCode] = useState('console.log("Hello, World!");'); + + return ( + + ); +} +``` + +### MarkdownEditor with Validation +```tsx +import { MarkdownEditor } from "@etherisc/ui-kit"; + +function DocumentEditor() { + const [content, setContent] = useState("# Welcome\n\nStart writing..."); + + return ( + + ); +} +``` + +## Best Practices + +### Performance +- Use appropriate height constraints for large content +- Implement debounced onChange handlers for real-time features +- Consider lazy loading for multiple editors on one page + +### Accessibility +- Provide clear labels and descriptions +- Ensure keyboard navigation works properly +- Test with screen readers + +### User Experience +- Provide clear visual feedback for different modes +- Include helpful keyboard shortcuts +- Show loading states for large content + +## Security Considerations + +### MarkdownEditor +- Content is automatically sanitized to prevent XSS attacks +- Script tags and event handlers are removed +- Safe HTML elements and attributes are preserved + +### CodeEditor +- Code content is treated as plain text +- No automatic code execution +- Safe for displaying user-generated code + +## Integration + +### Form Libraries +Both editors work seamlessly with popular form libraries: + +```tsx +// React Hook Form example +import { useForm, Controller } from "react-hook-form"; +import { CodeEditor } from "@etherisc/ui-kit"; + +function FormWithCodeEditor() { + const { control, handleSubmit } = useForm(); + + return ( +
+ ( + + )} + /> + + ); +} +``` + +## Related Documentation + +- **[Form Controls](/docs/form-controls-overview--docs)** - Basic input components +- **[Theme System](/docs/theme-system--docs)** - Customizing editor appearance +- **[Accessibility Guidelines](/docs/accessibility--docs)** - Making editors accessible \ No newline at end of file diff --git a/packages/ui-kit/src/docs/Feedback.mdx b/packages/ui-kit/src/docs/Feedback.mdx new file mode 100644 index 0000000..2118098 --- /dev/null +++ b/packages/ui-kit/src/docs/Feedback.mdx @@ -0,0 +1,190 @@ +import { Meta } from "@storybook/blocks"; + + + +# 🎨 Feedback + +User feedback and status communication components for providing clear system responses and status indicators. + +## Overview + +Feedback components help communicate system status, user actions results, and important information to users. They're essential for creating responsive and informative user interfaces. + +## Components + +### Notifications + +- **[Toast](/docs/feedback-toast--docs)** - Temporary notification messages with multiple variants + +### Status Indicators + +- **[StatusBadge](/docs/feedback-statusbadge--docs)** - Visual status indicators with semantic colors + +## Design Principles + +### Clear Communication +- Use appropriate colors and icons for different message types +- Provide concise, actionable messaging +- Ensure messages are easily scannable + +### Accessibility +- Proper color contrast for all status variants +- Screen reader announcements for dynamic content +- Keyboard navigation support where applicable + +### Consistent Behavior +- Standardized timing for temporary notifications +- Consistent positioning and animation +- Predictable interaction patterns + +## Usage Examples + +### Toast Notifications +```tsx +import { Toast, useToast } from "@etherisc/ui-kit"; + +function NotificationExample() { + const { addToast } = useToast(); + + const showSuccess = () => { + addToast({ + variant: "success", + title: "Success!", + description: "Your changes have been saved.", + }); + }; + + const showError = () => { + addToast({ + variant: "error", + title: "Error", + description: "Something went wrong. Please try again.", + }); + }; + + return ( +
+ + +
+ ); +} +``` + +### Status Badges +```tsx +import { StatusBadge } from "@etherisc/ui-kit"; + +function OrderStatus({ status }: { status: string }) { + const getStatusVariant = (status: string) => { + switch (status) { + case "delivered": return "success"; + case "processing": return "pending"; + case "cancelled": return "error"; + default: return "neutral"; + } + }; + + return ( + + {status.charAt(0).toUpperCase() + status.slice(1)} + + ); +} +``` + +## Feedback Patterns + +### Success Feedback +- Use green colors and checkmark icons +- Provide clear confirmation of completed actions +- Include relevant details about what was accomplished + +### Error Feedback +- Use red colors and warning icons +- Explain what went wrong in user-friendly terms +- Provide actionable steps to resolve the issue + +### Warning Feedback +- Use yellow/orange colors and alert icons +- Highlight potential issues or important information +- Guide users toward safer or better choices + +### Information Feedback +- Use blue colors and info icons +- Provide helpful context or additional information +- Enhance user understanding without being intrusive + +## Best Practices + +### Toast Notifications +- Keep messages concise and scannable +- Use appropriate timing (3-5 seconds for success, longer for errors) +- Provide dismiss actions for persistent messages +- Avoid overwhelming users with too many simultaneous toasts + +### Status Badges +- Use consistent color coding across your application +- Provide clear, descriptive labels +- Consider icon usage for better visual recognition +- Ensure badges are appropriately sized for their context + +### Accessibility +- Ensure sufficient color contrast for all variants +- Provide text alternatives for color-coded information +- Use ARIA live regions for dynamic status updates +- Test with screen readers and keyboard navigation + +### Performance +- Implement efficient toast queuing systems +- Use CSS animations for smooth transitions +- Avoid blocking the main thread with feedback animations + +## Integration Patterns + +### Form Validation +```tsx +import { StatusBadge, Toast } from "@etherisc/ui-kit"; + +function FormWithFeedback() { + const [status, setStatus] = useState("idle"); + + const handleSubmit = async (data) => { + setStatus("pending"); + try { + await submitForm(data); + setStatus("success"); + addToast({ + variant: "success", + title: "Form submitted successfully!" + }); + } catch (error) { + setStatus("error"); + addToast({ + variant: "error", + title: "Submission failed", + description: error.message + }); + } + }; + + return ( +
+ {/* Form fields */} +
+ + + {status} + +
+
+ ); +} +``` + +## Related Documentation + +- **[Form Controls](/docs/form-controls-overview--docs)** - Input components that work with feedback +- **[Layout](/docs/layout-overview--docs)** - Layout components for organizing feedback +- **[Theme System](/docs/theme-system--docs)** - Customizing feedback appearance +- **[Accessibility Guidelines](/docs/accessibility--docs)** - Making feedback accessible \ No newline at end of file diff --git a/packages/ui-kit/src/docs/FormComponents.mdx b/packages/ui-kit/src/docs/FormComponents.mdx new file mode 100644 index 0000000..3e6640a --- /dev/null +++ b/packages/ui-kit/src/docs/FormComponents.mdx @@ -0,0 +1,389 @@ +import { Meta } from "@storybook/blocks"; + + + +# 🔧 Form Components + +Higher-level form components and patterns for building complex, accessible forms with validation and state management. + +## Overview + +Form Components provide comprehensive solutions for form handling, validation, and user experience. They combine multiple form controls into cohesive patterns and provide integration with popular form libraries. + +## Components + +### Form Foundations + +- **[Form Examples](/docs/form-components-form-examples--docs)** - Complete form patterns and integration examples +- **[A11y Examples](/docs/form-components-a11y-examples--docs)** - Accessibility-focused form implementations + +### Layout & Organization + +- **FormGrid** - Responsive grid system for form layouts +- **FormGroup** - Logical grouping of related form fields +- **FieldSet** - Accessible field grouping with legends + +## Key Features + +### Form Integration +- **React Hook Form** - Full integration with react-hook-form +- **Validation** - Built-in validation with Zod schema support +- **Error Handling** - Comprehensive error display and management +- **State Management** - Efficient form state and field tracking + +### Accessibility Features +- **Screen Reader Support** - Proper ARIA labels and descriptions +- **Keyboard Navigation** - Full keyboard accessibility +- **Error Announcements** - Dynamic error announcements +- **Focus Management** - Logical focus order and trap patterns + +### User Experience +- **Progressive Enhancement** - Works with and without JavaScript +- **Real-time Validation** - Immediate feedback on user input +- **Smart Defaults** - Sensible default behaviors and values +- **Responsive Design** - Mobile-friendly form layouts + +## Form Patterns + +### Basic Registration Form +```tsx +import { Form, FormGrid, TextField, SelectField, CheckboxField } from "@etherisc/ui-kit"; +import { useForm } from "react-hook-form"; +import { z } from "zod"; + +const schema = z.object({ + firstName: z.string().min(1, "First name is required"), + lastName: z.string().min(1, "Last name is required"), + email: z.string().email("Invalid email address"), + role: z.enum(["user", "admin", "moderator"]), + agreeToTerms: z.boolean().refine(val => val, "You must agree to terms"), +}); + +function RegistrationForm() { + const form = useForm({ + resolver: zodResolver(schema), + }); + + const handleSubmit = (data) => { + console.log("Form submitted:", data); + }; + + return ( +
+ + + + + + + + + + + + + + ); +} +``` + +### Complex Multi-Step Form +```tsx +import { Form, FormGrid, WizardShell } from "@etherisc/ui-kit"; + +function MultiStepForm() { + const [currentStep, setCurrentStep] = useState(0); + const form = useForm(); + + const steps = [ + { + id: "personal", + title: "Personal Information", + content: ( + + + + + + + ), + }, + { + id: "address", + title: "Address Information", + content: ( + + + + + + + ), + }, + { + id: "review", + title: "Review & Submit", + content: , + }, + ]; + + return ( + setCurrentStep(prev => prev + 1)} + onPrevious={() => setCurrentStep(prev => prev - 1)} + > +
+ {steps[currentStep].content} +
+
+ ); +} +``` + +### Dynamic Field Arrays +```tsx +import { Form, FormGrid, TextField, Button } from "@etherisc/ui-kit"; +import { useFieldArray } from "react-hook-form"; + +function DynamicContactForm() { + const form = useForm({ + defaultValues: { + contacts: [{ name: "", email: "", phone: "" }], + }, + }); + + const { fields, append, remove } = useFieldArray({ + control: form.control, + name: "contacts", + }); + + return ( +
+ {fields.map((field, index) => ( +
+

Contact {index + 1}

+ + + + + + + {fields.length > 1 && ( + + )} +
+ ))} + + +
+ ); +} +``` + +## Validation Patterns + +### Schema-Based Validation +```tsx +import { z } from "zod"; + +// Define validation schema +const userSchema = z.object({ + username: z.string() + .min(3, "Username must be at least 3 characters") + .max(20, "Username cannot exceed 20 characters") + .regex(/^[a-zA-Z0-9_]+$/, "Username can only contain letters, numbers, and underscores"), + + password: z.string() + .min(8, "Password must be at least 8 characters") + .regex(/(?=.*[a-z])/, "Password must contain at least one lowercase letter") + .regex(/(?=.*[A-Z])/, "Password must contain at least one uppercase letter") + .regex(/(?=.*\d)/, "Password must contain at least one number"), + + confirmPassword: z.string(), + + age: z.number() + .min(18, "Must be at least 18 years old") + .max(120, "Age cannot exceed 120"), + + interests: z.array(z.string()) + .min(1, "Please select at least one interest"), +}).refine((data) => data.password === data.confirmPassword, { + message: "Passwords don't match", + path: ["confirmPassword"], +}); + +type UserFormData = z.infer; +``` + +### Custom Validation Rules +```tsx +import { Form } from "@etherisc/ui-kit"; + +function CustomValidationForm() { + const form = useForm({ + resolver: zodResolver(schema), + mode: "onChange", // Real-time validation + }); + + // Custom async validation + const validateUsernameUnique = async (username: string) => { + const response = await fetch(`/api/users/check/${username}`); + const isUnique = await response.json(); + return isUnique || "Username is already taken"; + }; + + return ( +
+ + {/* Other fields */} + + ); +} +``` + +## Accessibility Guidelines + +### ARIA Labels and Descriptions +```tsx + +``` + +### Error Handling +```tsx + +``` + +### Field Grouping +```tsx +
+ Contact Preferences + +
+``` + +## Performance Optimization + +### Form Optimization +- **Field-level validation** - Validate individual fields instead of entire form +- **Debounced validation** - Reduce validation calls for real-time feedback +- **Conditional rendering** - Show/hide fields based on form state +- **Memoization** - Prevent unnecessary re-renders of form components + +### Large Forms +- **Virtual scrolling** - For forms with many fields +- **Progressive loading** - Load form sections on demand +- **State management** - Efficient handling of large form state +- **Persistence** - Save form progress automatically + +## Testing Strategies + +### Form Testing +```tsx +import { render, screen, fireEvent, waitFor } from "@testing-library/react"; +import { RegistrationForm } from "./RegistrationForm"; + +test("validates required fields", async () => { + render(); + + fireEvent.click(screen.getByRole("button", { name: /register/i })); + + await waitFor(() => { + expect(screen.getByText("First name is required")).toBeInTheDocument(); + expect(screen.getByText("Email is required")).toBeInTheDocument(); + }); +}); + +test("submits form with valid data", async () => { + const onSubmit = jest.fn(); + render(); + + fireEvent.change(screen.getByLabelText(/first name/i), { + target: { value: "John" }, + }); + fireEvent.change(screen.getByLabelText(/email/i), { + target: { value: "john@example.com" }, + }); + + fireEvent.click(screen.getByRole("button", { name: /register/i })); + + await waitFor(() => { + expect(onSubmit).toHaveBeenCalledWith({ + firstName: "John", + email: "john@example.com", + }); + }); +}); +``` + +## Related Documentation + +- **[Form Controls](/docs/form-controls-overview--docs)** - Individual input components +- **[Layout](/docs/layout-overview--docs)** - Form layout components and shells +- **[Feedback](/docs/feedback-overview--docs)** - Error states and validation feedback +- **[Accessibility Guide](/docs/accessibility--docs)** - Complete accessibility guidelines \ No newline at end of file diff --git a/packages/ui-kit/src/docs/FormControls.mdx b/packages/ui-kit/src/docs/FormControls.mdx new file mode 100644 index 0000000..8dd23c8 --- /dev/null +++ b/packages/ui-kit/src/docs/FormControls.mdx @@ -0,0 +1,132 @@ +import { Meta } from "@storybook/blocks"; + + + +# 🧱 Form Controls + +Essential form input components that provide the building blocks for creating accessible and user-friendly forms. + +## Overview + +Form Controls are the fundamental input components that users interact with to provide data. Each component is designed with accessibility, validation, and user experience in mind. + +## Components + +### Basic Inputs + +- **[Button](/docs/form-controls-button--docs)** - Various button styles and states for actions +- **[TextInput](/docs/form-controls-textinput--docs)** - Single-line text input with validation support +- **[TextArea](/docs/form-controls-textarea--docs)** - Multi-line text input for longer content +- **[NumberInput](/docs/form-controls-numberinput--docs)** - Numeric input with min/max constraints + +### Selection Components + +- **[Select](/docs/form-controls-select--docs)** - Dropdown selection component +- **[ComboBox](/docs/form-controls-combobox--docs)** - Searchable dropdown with autocomplete +- **[Checkbox](/docs/form-controls-checkbox--docs)** - Single or multiple selection checkboxes +- **[RadioGroup](/docs/form-controls-radiogroup--docs)** - Mutually exclusive option selection + +### Advanced Inputs + +- **[SliderInput](/docs/form-controls-sliderinput--docs)** - Range slider for numeric values +- **[SpinnerInput](/docs/form-controls-spinnerinput--docs)** - Numeric input with increment/decrement buttons +- **[DatePicker](/docs/form-controls-datepicker--docs)** - Date selection with calendar popup +- **[DateRangePicker](/docs/form-controls-daterangepicker--docs)** - Date range selection + +## Design Principles + +### Accessibility First +- All components follow WCAG 2.1 AA guidelines +- Proper ARIA labels and descriptions +- Keyboard navigation support +- Screen reader compatibility + +### Consistent API +- Standardized prop names across components +- Consistent error handling and validation +- Uniform styling and theming + +### Validation Support +- Built-in error state styling +- Accessible error messaging +- Integration with form libraries + +## Usage Guidelines + +### Form Structure +```tsx +import { TextInput, Button } from "@etherisc/ui-kit"; + +function ContactForm() { + return ( +
+ + + + ); +} +``` + +### Validation Example +```tsx +import { TextInput } from "@etherisc/ui-kit"; + +function ValidatedInput() { + const [email, setEmail] = useState(""); + const [error, setError] = useState(""); + + const validateEmail = (value: string) => { + if (!value.includes("@")) { + setError("Please enter a valid email address"); + } else { + setError(""); + } + }; + + return ( + { + setEmail(e.target.value); + validateEmail(e.target.value); + }} + error={error} + /> + ); +} +``` + +## Best Practices + +### Labels and Descriptions +- Always provide clear, descriptive labels +- Use description text for additional context +- Keep labels concise but informative + +### Error Handling +- Provide specific, actionable error messages +- Show errors immediately after user interaction +- Use consistent error styling across forms + +### Responsive Design +- Components adapt to different screen sizes +- Touch-friendly targets on mobile devices +- Appropriate spacing and sizing + +### Performance +- Components are optimized for fast rendering +- Minimal re-renders during user interaction +- Efficient event handling + +## Related Documentation + +- **[Form Components](/docs/form-components-overview--docs)** - Complete form patterns and examples +- **[Accessibility Examples](/docs/form-components-a11y-examples--docs)** - Accessibility-focused implementations +- **[Theme System](/docs/theme-system--docs)** - Customizing component appearance \ No newline at end of file diff --git a/packages/ui-kit/src/docs/Layout.mdx b/packages/ui-kit/src/docs/Layout.mdx new file mode 100644 index 0000000..b0da43d --- /dev/null +++ b/packages/ui-kit/src/docs/Layout.mdx @@ -0,0 +1,192 @@ +import { Meta } from "@storybook/blocks"; + + + +# 🏗️ Layout + +Comprehensive layout components for structuring your application's interface and navigation. + +## Overview + +Layout components provide the structural foundation for your application, from complete page shells to navigation elements and content organization. They're designed to be flexible, responsive, and accessible. + +## Component Categories + +### Shells + +Complete page layout templates for different application contexts: + +- **[AppShell](/docs/layout-shells-appshell--docs)** - Full application layout with header, sidebar, and content areas +- **[AuthShell](/docs/layout-shells-authshell--docs)** - Authentication and login page layouts +- **[WizardShell](/docs/layout-shells-wizardshell--docs)** - Multi-step wizard and form layouts +- **[MinimalShell](/docs/layout-shells-minimalshell--docs)** - Simple, minimal page layouts +- **[ErrorShell](/docs/layout-shells-errorshell--docs)** - Error page and status layouts + +### Content Layouts + +Specialized layouts for organizing main content areas: + +- **[MainFixedLayout](/docs/layout-content-layouts-mainfixedlayout--docs)** - Fixed-width main content layout +- **[DataDenseLayout](/docs/layout-content-layouts-datadenselayout--docs)** - Dense data presentation layout +- **[ContentWrapper](/docs/layout-content-layouts-contentwrapper--docs)** - Flexible content area management + +### Navigation + +Components for application navigation and wayfinding: + +- **[TopBar](/docs/layout-navigation-topbar--docs)** - Application top navigation bar +- **[SideNav](/docs/layout-navigation-sidenav--docs)** - Collapsible sidebar navigation +- **[NavItem](/docs/layout-navigation-navitem--docs)** - Individual navigation items +- **[Breadcrumbs](/docs/layout-navigation-breadcrumbs--docs)** - Hierarchical navigation breadcrumbs +- **[HeaderActionIcon](/docs/layout-navigation-headeractionicon--docs)** - Header action buttons and icons + +## Design Principles + +### Responsive Design +- Mobile-first approach with progressive enhancement +- Flexible layouts that adapt to different screen sizes +- Touch-friendly navigation on mobile devices + +### Accessibility +- Semantic HTML structure with proper landmarks +- Keyboard navigation support +- Screen reader compatibility with ARIA labels + +### Performance +- Efficient rendering with minimal layout shifts +- Optimized for fast initial page loads +- Smooth animations and transitions + +## Usage Patterns + +### Basic Application Layout +```tsx +import { AppShell, TopBar, SideNav } from "@etherisc/ui-kit"; + +function App() { + return ( + } + userActions={} + /> + } + sideNav={ + + } + > +
+ {/* Your main content */} +
+
+ ); +} +``` + +### Authentication Layout +```tsx +import { AuthShell } from "@etherisc/ui-kit"; + +function LoginPage() { + return ( + } + width="md" + footer={} + > + + + ); +} +``` + +### Content with Navigation +```tsx +import { ContentWrapper, Breadcrumbs } from "@etherisc/ui-kit"; + +function ProductPage() { + const breadcrumbs = [ + { label: "Home", href: "/" }, + { label: "Products", href: "/products" }, + { label: "Laptop", isActive: true } + ]; + + return ( + } + footer={} + > + + + ); +} +``` + +## Layout Patterns + +### Application Shell Pattern +The AppShell provides a complete application layout with: +- Fixed header with branding and user actions +- Collapsible sidebar navigation +- Main content area with proper spacing +- Responsive behavior across devices + +### Wizard Pattern +The WizardShell is perfect for multi-step processes: +- Step indicator showing progress +- Navigation between steps +- Consistent layout across steps +- Accessible form progression + +### Error Handling Pattern +The ErrorShell provides consistent error pages: +- Clear error messaging +- Helpful action buttons +- Consistent branding +- Accessible error communication + +## Responsive Behavior + +### Breakpoints +- **Mobile**: < 768px - Collapsed navigation, stacked layouts +- **Tablet**: 768px - 1024px - Adaptive navigation, flexible content +- **Desktop**: > 1024px - Full navigation, optimized layouts + +### Navigation Adaptation +- Mobile: Hamburger menu with overlay navigation +- Tablet: Collapsible sidebar with icons +- Desktop: Full sidebar with labels and icons + +## Best Practices + +### Layout Hierarchy +- Use semantic HTML elements (header, nav, main, aside) +- Maintain consistent spacing and alignment +- Provide clear visual hierarchy + +### Navigation Design +- Keep navigation items organized and logical +- Use consistent iconography and labeling +- Provide clear active states and feedback + +### Content Organization +- Use appropriate content layouts for different data types +- Maintain consistent margins and padding +- Ensure content is scannable and well-structured + +### Performance Optimization +- Lazy load non-critical layout components +- Use CSS Grid and Flexbox for efficient layouts +- Minimize layout shifts during loading + +## Related Documentation + +- **[Form Controls](/docs/form-controls-overview--docs)** - Input components for forms +- **[Brand](/docs/brand-overview--docs)** - Logo and branding components +- **[Theme System](/docs/theme-system--docs)** - Customizing layout appearance +- **[Accessibility Guidelines](/docs/accessibility--docs)** - Making layouts accessible \ No newline at end of file diff --git a/packages/ui-kit/src/docs/Providers.mdx b/packages/ui-kit/src/docs/Providers.mdx new file mode 100644 index 0000000..269e172 --- /dev/null +++ b/packages/ui-kit/src/docs/Providers.mdx @@ -0,0 +1,448 @@ +import { Meta } from "@storybook/blocks"; + + + +# 🛡️ Providers + +Context providers and system-level components that manage application-wide state and behavior. + +## Overview + +Providers are foundational components that wrap your application to provide essential functionality like error handling, theming, internationalization, and other cross-cutting concerns. They ensure consistent behavior and state management across your entire application. + +## Components + +### Error Management + +- **[ErrorBoundary](/docs/providers-errorboundary--docs)** - React error boundary for graceful error handling and recovery + +### Theme System + +- **[ThemeProvider](/docs/providers-themeprovider--docs)** - Manages light/dark theme state and CSS custom properties +- **[ThemeToggle](/docs/providers-themetoggle--docs)** - Interactive component for theme switching + +### Additional Providers + +- **I18nProvider** - Internationalization and localization support +- **ToastProvider** - Global toast notification management +- **AuthProvider** - Authentication state management + +## Key Features + +### Error Boundary +- **Error Catching** - Catches JavaScript errors anywhere in the component tree +- **Fallback UI** - Displays custom error fallback components +- **Error Reporting** - Integrates with error reporting services (Sentry, etc.) +- **Recovery Options** - Provides ways for users to recover from errors +- **Development Mode** - Enhanced error display for development + +### Theme Management +- **Theme Persistence** - Remembers user theme preference across sessions +- **System Theme Detection** - Respects user's OS theme preference +- **CSS Custom Properties** - Dynamic theme switching via CSS variables +- **Component Integration** - All components automatically respect theme changes +- **Dark Mode Support** - Full dark mode implementation + +### Internationalization +- **Multi-language Support** - Support for multiple languages and locales +- **Dynamic Loading** - Load translations on demand +- **Pluralization** - Handle plural forms correctly +- **Date/Time Formatting** - Locale-aware date and time formatting +- **Number Formatting** - Currency and number formatting per locale + +## Usage Examples + +### Basic Provider Setup +```tsx +import { + ThemeProvider, + ErrorBoundary, + I18nProvider, + ToastProvider +} from "@etherisc/ui-kit"; + +function App() { + return ( + + + + + + + + + + ); +} +``` + +### Advanced Error Boundary Configuration +```tsx +import { ErrorBoundary } from "@etherisc/ui-kit"; + +function App() { + const handleError = (error: Error, errorInfo: React.ErrorInfo) => { + // Log to error reporting service + console.error("Application error:", error, errorInfo); + + // Report to Sentry, LogRocket, etc. + if (process.env.NODE_ENV === "production") { + Sentry.captureException(error, { + contexts: { + react: { + componentStack: errorInfo.componentStack, + }, + }, + }); + } + }; + + const ErrorFallback = ({ error, retry }: { error: Error; retry: () => void }) => ( +
+

Something went wrong

+
+ Error details +
{error.message}
+
+ +
+ ); + + return ( + + + + ); +} +``` + +### Theme Provider with Custom Configuration +```tsx +import { ThemeProvider } from "@etherisc/ui-kit"; + +function App() { + return ( + + + + {/* Theme toggle can be used anywhere in the tree */} + + + ); +} +``` + +### Custom Theme Toggle +```tsx +import { useTheme } from "@etherisc/ui-kit"; + +function CustomThemeToggle() { + const { theme, setTheme, systemTheme } = useTheme(); + + return ( +
+ + + +
+ ); +} +``` + +### Internationalization Setup +```tsx +import { I18nProvider } from "@etherisc/ui-kit"; + +const resources = { + en: { + translation: { + welcome: "Welcome", + button: { + submit: "Submit", + cancel: "Cancel", + }, + }, + }, + de: { + translation: { + welcome: "Willkommen", + button: { + submit: "Senden", + cancel: "Abbrechen", + }, + }, + }, +}; + +function App() { + return ( + + + + ); +} + +// Usage in components +function MyComponent() { + const { t, i18n } = useTranslation(); + + return ( +
+

{t("welcome")}

+ +
+ ); +} +``` + +## Provider Composition Patterns + +### HOC Pattern +```tsx +import { withErrorBoundary } from "@etherisc/ui-kit"; + +const SafeComponent = withErrorBoundary(MyComponent, { + fallback: ({ error, retry }) => ( +
+

Error in MyComponent: {error.message}

+ +
+ ), + onError: (error) => console.error(error), +}); +``` + +### Hook-based Usage +```tsx +import { useTheme, useErrorHandler } from "@etherisc/ui-kit"; + +function MyComponent() { + const { theme, setTheme } = useTheme(); + const handleError = useErrorHandler(); + + const handleAsyncOperation = async () => { + try { + await someAsyncOperation(); + } catch (error) { + handleError(error); + } + }; + + return ( +
+ + +
+ ); +} +``` + +### Context Composition +```tsx +import { + ThemeProvider, + ErrorBoundary, + I18nProvider, + AuthProvider, + ToastProvider +} from "@etherisc/ui-kit"; + +// Create a composite provider +function AppProviders({ children }: { children: React.ReactNode }) { + return ( + + + + + + {children} + + + + + + ); +} + +function App() { + return ( + + + + } /> + } /> + + + + ); +} +``` + +## Performance Considerations + +### Provider Optimization +- **Context Splitting** - Split contexts to minimize re-renders +- **Memoization** - Use useMemo and useCallback for provider values +- **Selective Updates** - Only update relevant parts of context +- **Provider Composition** - Compose providers efficiently + +### Error Boundary Performance +```tsx +import { memo } from "react"; + +const OptimizedErrorBoundary = memo(({ children, ...props }) => ( + + {children} + +)); + +// Use multiple boundaries for isolation +function App() { + return ( + +
+ + + + + + + + ); +} +``` + +## Testing Providers + +### Error Boundary Testing +```tsx +import { render, screen } from "@testing-library/react"; +import { ErrorBoundary } from "@etherisc/ui-kit"; + +const ThrowError = ({ shouldThrow }: { shouldThrow: boolean }) => { + if (shouldThrow) { + throw new Error("Test error"); + } + return
No error
; +}; + +test("catches and displays error", () => { + const onError = jest.fn(); + + render( + + + + ); + + expect(screen.getByText(/something went wrong/i)).toBeInTheDocument(); + expect(onError).toHaveBeenCalledWith( + expect.any(Error), + expect.any(Object) + ); +}); +``` + +### Theme Provider Testing +```tsx +import { render, screen, fireEvent } from "@testing-library/react"; +import { ThemeProvider, useTheme } from "@etherisc/ui-kit"; + +const ThemeConsumer = () => { + const { theme, setTheme } = useTheme(); + return ( +
+ Current theme: {theme} + +
+ ); +}; + +test("provides theme context", () => { + render( + + + + ); + + expect(screen.getByText("Current theme: light")).toBeInTheDocument(); + + fireEvent.click(screen.getByText("Set Dark")); + + expect(screen.getByText("Current theme: dark")).toBeInTheDocument(); +}); +``` + +## Security Considerations + +### Error Information Exposure +```tsx +const ProductionErrorBoundary = ({ children }) => { + const isProduction = process.env.NODE_ENV === "production"; + + const fallback = ({ error }) => ( +
+

Something went wrong

+ {!isProduction && ( +
+ Error details (dev only) +
{error.stack}
+
+ )} +
+ ); + + return ( + + {children} + + ); +}; +``` + +### Theme Security +- **CSS Injection Prevention** - Validate theme values +- **XSS Protection** - Sanitize custom theme properties +- **Local Storage Security** - Validate stored theme preferences + +## Related Documentation + +- **[Theme System](/docs/theming--docs)** - Complete theming documentation +- **[Error Handling](/docs/error-handling--docs)** - Error handling strategies +- **[Internationalization](/docs/i18n--docs)** - I18n implementation guide +- **[Performance](/docs/performance--docs)** - Performance optimization techniques \ No newline at end of file diff --git a/packages/ui-kit/src/layout/AppShell/AppShell.stories.tsx b/packages/ui-kit/src/layout/AppShell/AppShell.stories.tsx index 074fdca..09c146f 100644 --- a/packages/ui-kit/src/layout/AppShell/AppShell.stories.tsx +++ b/packages/ui-kit/src/layout/AppShell/AppShell.stories.tsx @@ -24,7 +24,7 @@ import type { NavItem } from "./SideNav"; import type { BreadcrumbItem } from "./Breadcrumbs"; const meta: Meta = { - title: "Layout/AppShell/AppShell", + title: "Layout/Shells/AppShell", component: AppShell, parameters: { layout: "fullscreen", diff --git a/packages/ui-kit/src/layout/AppShell/ContentWrapper.stories.tsx b/packages/ui-kit/src/layout/AppShell/ContentWrapper.stories.tsx index 39c8f54..1dd7561 100644 --- a/packages/ui-kit/src/layout/AppShell/ContentWrapper.stories.tsx +++ b/packages/ui-kit/src/layout/AppShell/ContentWrapper.stories.tsx @@ -3,7 +3,7 @@ import { ContentWrapper } from './ContentWrapper'; import type { BreadcrumbItem } from './Breadcrumbs'; const meta: Meta = { - title: 'Layout/AppShell/ContentWrapper', + title: 'Layout/Content Layouts/ContentWrapper', component: ContentWrapper, parameters: { layout: 'padded', diff --git a/packages/ui-kit/src/layout/AppShell/SideNav.stories.tsx b/packages/ui-kit/src/layout/AppShell/SideNav.stories.tsx index 02b7c4e..a7ec6cb 100644 --- a/packages/ui-kit/src/layout/AppShell/SideNav.stories.tsx +++ b/packages/ui-kit/src/layout/AppShell/SideNav.stories.tsx @@ -1,9 +1,10 @@ +import React from 'react'; import type { Meta, StoryObj } from '@storybook/react'; import { SideNav, NavItem } from './SideNav'; import { HomeIcon, LayersIcon, SettingsIcon, UsersIcon, BarChartIcon, FileTextIcon } from 'lucide-react'; const meta: Meta = { - title: 'Layout/AppShell/SideNav', + title: 'Layout/Navigation/SideNav', component: SideNav, parameters: { layout: 'centered', diff --git a/packages/ui-kit/src/layout/AppShell/TopBar.stories.tsx b/packages/ui-kit/src/layout/AppShell/TopBar.stories.tsx index 9018843..2938a4f 100644 --- a/packages/ui-kit/src/layout/AppShell/TopBar.stories.tsx +++ b/packages/ui-kit/src/layout/AppShell/TopBar.stories.tsx @@ -1,16 +1,17 @@ +import React from 'react'; import type { Meta, StoryObj } from '@storybook/react'; import { TopBar } from './TopBar'; import { Logo } from '../../components/layout/Logo'; import { HeaderActionIcon } from '../../components/layout/HeaderActionIcon'; import { Button } from '../../components/primitives/Button/Button'; import { ThemeToggle } from '../../components/primitives/ThemeToggle/ThemeToggle'; -import { Avatar, AvatarFallback, AvatarImage } from '@/components/ui/avatar'; +import { Avatar, AvatarFallback, AvatarImage } from '../../components/ui/avatar'; // Icons import { BellIcon, HelpCircleIcon, Settings } from 'lucide-react'; const meta: Meta = { - title: 'Layout/AppShell/TopBar', + title: 'Layout/Navigation/TopBar', component: TopBar, parameters: { layout: 'fullscreen', diff --git a/packages/ui-kit/src/layout/AuthShell/AuthShell.stories.tsx b/packages/ui-kit/src/layout/AuthShell/AuthShell.stories.tsx index 76b9203..31d78a0 100644 --- a/packages/ui-kit/src/layout/AuthShell/AuthShell.stories.tsx +++ b/packages/ui-kit/src/layout/AuthShell/AuthShell.stories.tsx @@ -3,7 +3,7 @@ import { AuthShell } from './AuthShell'; import { Button } from '@/components/primitives/Button'; const meta: Meta = { - title: 'Layout/AuthShell', + title: 'Layout/Shells/AuthShell', component: AuthShell, parameters: { layout: 'fullscreen', diff --git a/packages/ui-kit/src/layout/DataDenseLayout/DataDenseLayout.stories.tsx b/packages/ui-kit/src/layout/DataDenseLayout/DataDenseLayout.stories.tsx index d99fd5a..7b79d2f 100644 --- a/packages/ui-kit/src/layout/DataDenseLayout/DataDenseLayout.stories.tsx +++ b/packages/ui-kit/src/layout/DataDenseLayout/DataDenseLayout.stories.tsx @@ -4,7 +4,7 @@ import { DataDenseLayout } from "./DataDenseLayout"; import { Button } from "../../components/primitives/Button"; const meta: Meta = { - title: "Layout/DataDenseLayout", + title: "Layout/Content Layouts/DataDenseLayout", component: DataDenseLayout, parameters: { layout: "fullscreen", diff --git a/packages/ui-kit/src/layout/ErrorShell/ErrorShell.stories.tsx b/packages/ui-kit/src/layout/ErrorShell/ErrorShell.stories.tsx index 0f69fb6..59022b7 100644 --- a/packages/ui-kit/src/layout/ErrorShell/ErrorShell.stories.tsx +++ b/packages/ui-kit/src/layout/ErrorShell/ErrorShell.stories.tsx @@ -4,7 +4,7 @@ import { ErrorShell } from "./ErrorShell"; import { Button } from "../../components/primitives/Button"; const meta: Meta = { - title: "Layout/ErrorShell", + title: "Layout/Shells/ErrorShell", component: ErrorShell, parameters: { layout: "fullscreen", diff --git a/packages/ui-kit/src/layout/MainFixedLayout/MainFixedLayout.stories.tsx b/packages/ui-kit/src/layout/MainFixedLayout/MainFixedLayout.stories.tsx index 7acf557..617a65e 100644 --- a/packages/ui-kit/src/layout/MainFixedLayout/MainFixedLayout.stories.tsx +++ b/packages/ui-kit/src/layout/MainFixedLayout/MainFixedLayout.stories.tsx @@ -4,7 +4,7 @@ import { MainFixedLayout } from "./MainFixedLayout"; import { Button } from "../../components/primitives/Button"; const meta: Meta = { - title: "Layout/MainFixedLayout", + title: "Layout/Content Layouts/MainFixedLayout", component: MainFixedLayout, parameters: { layout: "fullscreen", diff --git a/packages/ui-kit/src/layout/MinimalShell/MinimalShell.stories.tsx b/packages/ui-kit/src/layout/MinimalShell/MinimalShell.stories.tsx index b7da648..a226fd4 100644 --- a/packages/ui-kit/src/layout/MinimalShell/MinimalShell.stories.tsx +++ b/packages/ui-kit/src/layout/MinimalShell/MinimalShell.stories.tsx @@ -5,7 +5,7 @@ import { Logo } from '../../components/layout/Logo'; import { AlertTriangleIcon, AlertCircleIcon, CheckCircleIcon } from 'lucide-react'; const meta: Meta = { - title: 'Layout/MinimalShell', + title: 'Layout/Shells/MinimalShell', component: MinimalShell, parameters: { layout: 'fullscreen', diff --git a/packages/ui-kit/src/layout/WizardShell/WizardShell.stories.tsx b/packages/ui-kit/src/layout/WizardShell/WizardShell.stories.tsx index 682d081..dbbf2c3 100644 --- a/packages/ui-kit/src/layout/WizardShell/WizardShell.stories.tsx +++ b/packages/ui-kit/src/layout/WizardShell/WizardShell.stories.tsx @@ -4,7 +4,7 @@ import { Button } from '../../components/primitives/Button/Button'; import { Logo } from '../../components/layout/Logo'; const meta: Meta = { - title: 'Layout/WizardShell', + title: 'Layout/Shells/WizardShell', component: WizardShell, parameters: { layout: 'fullscreen', diff --git a/packages/ui-kit/src/stories/Button.jsx b/packages/ui-kit/src/stories/Button.jsx deleted file mode 100644 index 15dde39..0000000 --- a/packages/ui-kit/src/stories/Button.jsx +++ /dev/null @@ -1,50 +0,0 @@ -import React from 'react'; -import PropTypes from 'prop-types'; -import './button.css'; - -/** - * Primary UI component for user interaction - */ -export const Button = ({ primary, backgroundColor, size, label, ...props }) => { - const mode = primary ? 'storybook-button--primary' : 'storybook-button--secondary'; - return ( - - ); -}; - -Button.propTypes = { - /** - * Is this the principal call to action on the page? - */ - primary: PropTypes.bool, - /** - * What background color to use - */ - backgroundColor: PropTypes.string, - /** - * How large should the button be? - */ - size: PropTypes.oneOf(['small', 'medium', 'large']), - /** - * Button contents - */ - label: PropTypes.string.isRequired, - /** - * Optional click handler - */ - onClick: PropTypes.func, -}; - -Button.defaultProps = { - backgroundColor: null, - primary: false, - size: 'medium', - onClick: undefined, -}; diff --git a/packages/ui-kit/src/stories/Button.stories.js b/packages/ui-kit/src/stories/Button.stories.js deleted file mode 100644 index 97b9ec0..0000000 --- a/packages/ui-kit/src/stories/Button.stories.js +++ /dev/null @@ -1,48 +0,0 @@ -import { fn } from '@storybook/test'; -import { Button } from './Button'; - -// More on how to set up stories at: https://storybook.js.org/docs/writing-stories#default-export -export default { - title: 'Example/Button', - component: Button, - parameters: { - // Optional parameter to center the component in the Canvas. More info: https://storybook.js.org/docs/configure/story-layout - layout: 'centered', - }, - // This component will have an automatically generated Autodocs entry: https://storybook.js.org/docs/writing-docs/autodocs - tags: ['autodocs'], - // More on argTypes: https://storybook.js.org/docs/api/argtypes - argTypes: { - backgroundColor: { control: 'color' }, - }, - // Use `fn` to spy on the onClick arg, which will appear in the actions panel once invoked: https://storybook.js.org/docs/essentials/actions#action-args - args: { onClick: fn() }, -}; - -// More on writing stories with args: https://storybook.js.org/docs/writing-stories/args -export const Primary = { - args: { - primary: true, - label: 'Button', - }, -}; - -export const Secondary = { - args: { - label: 'Button', - }, -}; - -export const Large = { - args: { - size: 'large', - label: 'Button', - }, -}; - -export const Small = { - args: { - size: 'small', - label: 'Button', - }, -}; diff --git a/packages/ui-kit/src/stories/Configure.mdx b/packages/ui-kit/src/stories/Configure.mdx deleted file mode 100644 index 5157090..0000000 --- a/packages/ui-kit/src/stories/Configure.mdx +++ /dev/null @@ -1,364 +0,0 @@ -import { Meta } from "@storybook/blocks"; - -import Github from "./assets/github.svg"; -import Discord from "./assets/discord.svg"; -import Youtube from "./assets/youtube.svg"; -import Tutorials from "./assets/tutorials.svg"; -import Styling from "./assets/styling.png"; -import Context from "./assets/context.png"; -import Assets from "./assets/assets.png"; -import Docs from "./assets/docs.png"; -import Share from "./assets/share.png"; -import FigmaPlugin from "./assets/figma-plugin.png"; -import Testing from "./assets/testing.png"; -import Accessibility from "./assets/accessibility.png"; -import Theming from "./assets/theming.png"; -import AddonLibrary from "./assets/addon-library.png"; - -export const RightArrow = () => - - - - - -
-
- # Configure your project - - Because Storybook works separately from your app, you'll need to configure it for your specific stack and setup. Below, explore guides for configuring Storybook with popular frameworks and tools. If you get stuck, learn how you can ask for help from our community. -
-
-
- A wall of logos representing different styling technologies -

Add styling and CSS

-

Like with web applications, there are many ways to include CSS within Storybook. Learn more about setting up styling within Storybook.

- Learn more -
-
- An abstraction representing the composition of data for a component -

Provide context and mocking

-

Often when a story doesn't render, it's because your component is expecting a specific environment or context (like a theme provider) to be available.

- Learn more -
-
- A representation of typography and image assets -
-

Load assets and resources

-

To link static files (like fonts) to your projects and stories, use the - `staticDirs` configuration option to specify folders to load when - starting Storybook.

- Learn more -
-
-
-
-
-
- # Do more with Storybook - - Now that you know the basics, let's explore other parts of Storybook that will improve your experience. This list is just to get you started. You can customise Storybook in many ways to fit your needs. -
- -
-
-
- A screenshot showing the autodocs tag being set, pointing a docs page being generated -

Autodocs

-

Auto-generate living, - interactive reference documentation from your components and stories.

- Learn more -
-
- A browser window showing a Storybook being published to a chromatic.com URL -

Publish to Chromatic

-

Publish your Storybook to review and collaborate with your entire team.

- Learn more -
-
- Windows showing the Storybook plugin in Figma -

Figma Plugin

-

Embed your stories into Figma to cross-reference the design and live - implementation in one place.

- Learn more -
-
- Screenshot of tests passing and failing -

Testing

-

Use stories to test a component in all its variations, no matter how - complex.

- Learn more -
-
- Screenshot of accessibility tests passing and failing -

Accessibility

-

Automatically test your components for a11y issues as you develop.

- Learn more -
-
- Screenshot of Storybook in light and dark mode -

Theming

-

Theme Storybook's UI to personalize it to your project.

- Learn more -
-
-
-
-
-
-

Addons

-

Integrate your tools with Storybook to connect workflows.

- Discover all addons -
-
- Integrate your tools with Storybook to connect workflows. -
-
- -
-
- Github logo - Join our contributors building the future of UI development. - - Star on GitHub -
-
- Discord logo -
- Get support and chat with frontend developers. - - Join Discord server -
-
-
- Youtube logo -
- Watch tutorials, feature previews and interviews. - - Watch on YouTube -
-
-
- A book -

Follow guided walkthroughs on for key workflows.

- - Discover tutorials -
-
- - diff --git a/packages/ui-kit/src/stories/Header.jsx b/packages/ui-kit/src/stories/Header.jsx deleted file mode 100644 index 39e5226..0000000 --- a/packages/ui-kit/src/stories/Header.jsx +++ /dev/null @@ -1,59 +0,0 @@ -import React from 'react'; -import PropTypes from 'prop-types'; - -import { Button } from './Button'; -import './header.css'; - -export const Header = ({ user, onLogin, onLogout, onCreateAccount }) => ( -
-
-
- - - - - - - -

Acme

-
-
- {user ? ( - <> - - Welcome, {user.name}! - -
-
-
-); - -Header.propTypes = { - user: PropTypes.shape({ - name: PropTypes.string.isRequired, - }), - onLogin: PropTypes.func.isRequired, - onLogout: PropTypes.func.isRequired, - onCreateAccount: PropTypes.func.isRequired, -}; - -Header.defaultProps = { - user: null, -}; diff --git a/packages/ui-kit/src/stories/Header.stories.js b/packages/ui-kit/src/stories/Header.stories.js deleted file mode 100644 index 17323e6..0000000 --- a/packages/ui-kit/src/stories/Header.stories.js +++ /dev/null @@ -1,28 +0,0 @@ -import { Header } from './Header'; -import { fn } from '@storybook/test'; - -export default { - title: 'Example/Header', - component: Header, - // This component will have an automatically generated Autodocs entry: https://storybook.js.org/docs/writing-docs/autodocs - tags: ['autodocs'], - parameters: { - // More on how to position stories at: https://storybook.js.org/docs/configure/story-layout - layout: 'fullscreen', - }, - args: { - onLogin: fn(), - onLogout: fn(), - onCreateAccount: fn(), - }, -}; - -export const LoggedIn = { - args: { - user: { - name: 'Jane Doe', - }, - }, -}; - -export const LoggedOut = {}; diff --git a/packages/ui-kit/src/stories/Page.jsx b/packages/ui-kit/src/stories/Page.jsx deleted file mode 100644 index b7c5317..0000000 --- a/packages/ui-kit/src/stories/Page.jsx +++ /dev/null @@ -1,69 +0,0 @@ -import React from 'react'; - -import { Header } from './Header'; -import './page.css'; - -export const Page = () => { - const [user, setUser] = React.useState(); - - return ( -
-
setUser({ name: 'Jane Doe' })} - onLogout={() => setUser(undefined)} - onCreateAccount={() => setUser({ name: 'Jane Doe' })} - /> - -
-

Pages in Storybook

-

- We recommend building UIs with a{' '} - - component-driven - {' '} - process starting with atomic components and ending with pages. -

-

- Render pages with mock data. This makes it easy to build and review page states without - needing to navigate to them in your app. Here are some handy patterns for managing page - data in Storybook: -

-
    -
  • - Use a higher-level connected component. Storybook helps you compose such data from the - "args" of child component stories -
  • -
  • - Assemble data in the page component from your services. You can mock these services out - using Storybook. -
  • -
-

- Get a guided tutorial on component-driven development at{' '} - - Storybook tutorials - - . Read more in the{' '} - - docs - - . -

-
- Tip Adjust the width of the canvas with the{' '} - - - - - - Viewports addon in the toolbar -
-
-
- ); -}; diff --git a/packages/ui-kit/src/stories/Page.stories.js b/packages/ui-kit/src/stories/Page.stories.js deleted file mode 100644 index 9be87c4..0000000 --- a/packages/ui-kit/src/stories/Page.stories.js +++ /dev/null @@ -1,28 +0,0 @@ -import { within, userEvent, expect } from '@storybook/test'; - -import { Page } from './Page'; - -export default { - title: 'Example/Page', - component: Page, - parameters: { - // More on how to position stories at: https://storybook.js.org/docs/configure/story-layout - layout: 'fullscreen', - }, -}; - -export const LoggedOut = {}; - -// More on interaction testing: https://storybook.js.org/docs/writing-tests/interaction-testing -export const LoggedIn = { - play: async ({ canvasElement }) => { - const canvas = within(canvasElement); - const loginButton = canvas.getByRole('button', { name: /Log in/i }); - await expect(loginButton).toBeInTheDocument(); - await userEvent.click(loginButton); - await expect(loginButton).not.toBeInTheDocument(); - - const logoutButton = canvas.getByRole('button', { name: /Log out/i }); - await expect(logoutButton).toBeInTheDocument(); - }, -}; diff --git a/packages/ui-kit/src/stories/button.css b/packages/ui-kit/src/stories/button.css deleted file mode 100644 index 96f75bf..0000000 --- a/packages/ui-kit/src/stories/button.css +++ /dev/null @@ -1,35 +0,0 @@ -.storybook-button { - font-family: 'Nunito Sans', 'Helvetica Neue', Helvetica, Arial, sans-serif; - font-weight: 700; - border: 0; - border-radius: 3em; - cursor: pointer; - display: inline-block; - line-height: 1; -} - -.storybook-button--primary { - color: white; - background-color: #0b76c9; -} - -.storybook-button--secondary { - color: #333; - background-color: transparent; - box-shadow: rgba(0, 0, 0, 0.15) 0px 0px 0px 1px inset; -} - -.storybook-button--small { - font-size: 12px; - padding: 10px 16px; -} - -.storybook-button--medium { - font-size: 14px; - padding: 11px 20px; -} - -.storybook-button--large { - font-size: 16px; - padding: 12px 24px; -} \ No newline at end of file diff --git a/packages/ui-kit/src/stories/header.css b/packages/ui-kit/src/stories/header.css deleted file mode 100644 index d9a7052..0000000 --- a/packages/ui-kit/src/stories/header.css +++ /dev/null @@ -1,32 +0,0 @@ -.storybook-header { - font-family: 'Nunito Sans', 'Helvetica Neue', Helvetica, Arial, sans-serif; - border-bottom: 1px solid rgba(0, 0, 0, 0.1); - padding: 15px 20px; - display: flex; - align-items: center; - justify-content: space-between; -} - -.storybook-header svg { - display: inline-block; - vertical-align: top; -} - -.storybook-header h1 { - font-weight: 700; - font-size: 20px; - line-height: 1; - margin: 6px 0 6px 10px; - display: inline-block; - vertical-align: top; -} - -.storybook-header button + button { - margin-left: 10px; -} - -.storybook-header .welcome { - color: #333; - font-size: 14px; - margin-right: 10px; -} diff --git a/packages/ui-kit/src/stories/page.css b/packages/ui-kit/src/stories/page.css deleted file mode 100644 index d4ff547..0000000 --- a/packages/ui-kit/src/stories/page.css +++ /dev/null @@ -1,75 +0,0 @@ -.storybook-page { - font-family: 'Nunito Sans', 'Helvetica Neue', Helvetica, Arial, sans-serif; - font-size: 14px; - line-height: 24px; - padding: 48px 20px; - margin: 0 auto; - max-width: 600px; - color: #333; -} - -.storybook-page h2 { - font-weight: 700; - font-size: 32px; - line-height: 1; - margin: 0 0 4px; - display: inline-block; - vertical-align: top; -} - -.storybook-page p { - margin: 1em 0; -} - -.storybook-page a { - text-decoration: none; - color: #0a6bb8; - font-weight: 500; -} - -.storybook-page .high-contrast { - color: #085394; - font-weight: 800; -} - -.storybook-page ul { - padding-left: 30px; - margin: 1em 0; -} - -.storybook-page li { - margin-bottom: 8px; -} - -.storybook-page .tip { - display: inline-block; - border-radius: 1em; - font-size: 11px; - line-height: 12px; - font-weight: 700; - background: #e5f5dd; - color: #2e7a1a; - padding: 4px 12px; - margin-right: 10px; - vertical-align: top; -} - -.storybook-page .tip-wrapper { - font-size: 13px; - line-height: 20px; - margin-top: 40px; - margin-bottom: 40px; -} - -.storybook-page .tip-wrapper svg { - display: inline-block; - height: 12px; - width: 12px; - margin-right: 4px; - vertical-align: top; - margin-top: 3px; -} - -.storybook-page .tip-wrapper svg path { - fill: #0a6bb8; -} \ No newline at end of file