Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
31 changes: 31 additions & 0 deletions CLAUDE.md
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@ Datalayer Core - Python SDK and CLI for the Datalayer AI Platform. Hybrid Python
**Python**: `pip install -e .[test]` | `pytest datalayer_core/tests/` | `mypy datalayer_core/`
**TypeScript Library**: `npm install` | `npm run build:lib` | `npm run lint` | `npm run test`
**Examples**: `npm run example` (starts dev server at http://localhost:3000/)
**Storybook**: `npm run storybook` (starts on http://localhost:6006/) | `npm run build-storybook`
**Code Quality**: `npm run check` | `npm run check:fix` | `npm run lint` | `npm run format` | `npm run type-check`
**Docs**: `cd docs && make build` | `npm run typedoc` (generates TypeScript API docs)
**Make**: `make build` | `make start` | `make docs`
Expand Down Expand Up @@ -64,6 +65,36 @@ Datalayer Core - Python SDK and CLI for the Datalayer AI Platform. Hybrid Python
- Ensure things always build after changes
- Run also npm run format/lint/type-check to ensure all is working properly

## Storybook Component Testing

**Component Story Structure:**
- All UI components have `.stories.tsx` files colocated with their source code
- Stories follow the pattern: `ComponentName.tsx` → `ComponentName.stories.tsx`
- Each story file includes multiple variants showcasing different states
- Interactive controls allow testing component props in real-time

**Running Storybook:**
```bash
npm run storybook # Start dev server (http://localhost:6006)
npm run build-storybook # Build static Storybook
```

**Component Coverage:**
All 50+ UI components have comprehensive stories including:
- Avatars, Banners, Buttons, Checkout, Confetti
- Context, Display, ECharts, Flashes, IAM
- Icons, Labels, Landings, NavBar, NBGrader
- Notebooks, Primer, Progress, Runtimes, Screenshot
- Snapshots, Snippets, Storage, Students, SubNav
- Tables, TextReveal, Tokens, Toolbars, Users

**Story Best Practices:**
- Mock external dependencies (API calls, services)
- Provide realistic test data
- Include edge cases (loading, error, empty states)
- Use TypeScript for type safety
- Follow existing patterns in the codebase

## Running Examples

**Start the examples server:**
Expand Down
29 changes: 29 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -225,6 +225,35 @@ pip install -e .[test]
npm install
```

### Storybook Component Development

Datalayer Core includes comprehensive Storybook coverage for all UI components. Each component has its own `.stories.tsx` file located next to the component source code.

```bash
# Start Storybook development server
npm run storybook # Runs on http://localhost:6006

# Build static Storybook
npm run build-storybook

# Run Storybook on a different port
npm run storybook -- --port 6007
```

**Component Story Structure:**
- Stories are colocated with components (e.g., `Button.tsx` → `Button.stories.tsx`)
- All UI components have comprehensive test coverage
- Multiple story variants showcase different component states
- Interactive controls for testing component props

**Available Component Categories:**
- Avatars, Banners, Buttons, Checkout, Confetti
- Context, Display, ECharts, Flashes, IAM
- Icons, Labels, Landings, NavBar, NBGrader
- Notebooks, Primer, Progress, Runtimes, Screenshot
- Snapshots, Snippets, Storage, Students, SubNav
- Tables, TextReveal, Tokens, Toolbars, Users

### Code Quality

This project maintains high code quality standards with automated linting, formatting, and type checking:
Expand Down
2 changes: 0 additions & 2 deletions examples/nextjs-notebook/src/app/environments/page.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -36,7 +36,6 @@ export default function EnvironmentsPage() {
// Redirect to welcome page if no token
router.push('/welcome');
}
// eslint-disable-next-line react-hooks/exhaustive-deps
}, [token, router]);

const fetchEnvironments = async () => {
Expand Down Expand Up @@ -142,7 +141,6 @@ export default function EnvironmentsPage() {
}}
>
{imageUrl ? (
// eslint-disable-next-line
<img
src={imageUrl}
alt={displayName}
Expand Down
2 changes: 1 addition & 1 deletion examples/nextjs-notebook/src/app/welcome/page.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -69,7 +69,7 @@ export default function WelcomePage() {
>
<Box sx={{ textAlign: 'center', mb: 6 }}>
<Box sx={{ display: 'flex', justifyContent: 'center', mb: 4 }}>
{/* eslint-disable-next-line */}
{}
<img
src="/favicon.png"
alt="Datalayer Logo"
Expand Down
2 changes: 1 addition & 1 deletion examples/nextjs-notebook/src/components/Footer.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -38,7 +38,7 @@ export default function Footer() {
{/* Datalayer Info */}
<Box>
<Box sx={{ mb: 3 }}>
{/* eslint-disable-next-line */}
{}
<img
src="https://assets.datalayer.tech/datalayer-25.svg"
alt="Datalayer"
Expand Down
40 changes: 39 additions & 1 deletion src/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -8,14 +8,14 @@ This directory contains the comprehensive TypeScript/React frontend architecture
src/
├── api/ # API layer and Jupyter integration
├── components/ # React component library (70+ components)
│ └── *.stories.tsx # Storybook stories colocated with components
├── config/ # Configuration utilities
├── examples/ # Usage examples
├── hooks/ # Custom React hooks (25+ hooks)
├── i18n/ # Internationalization
├── models/ # TypeScript type definitions (70+ models)
├── routes/ # Routing configuration
├── state/ # Zustand state management
├── stories/ # Storybook stories
├── theme/ # Theme and styling
├── utils/ # Utility functions (20+ utilities)
└── mocks/ # Testing mocks
Expand Down Expand Up @@ -45,6 +45,44 @@ npm run test
npm run build
```

## 📚 Storybook

All UI components have comprehensive Storybook coverage with interactive documentation and testing capabilities.

### Running Storybook

```bash
# Start Storybook dev server (default port 6006)
npm run storybook

# Build static Storybook
npm run build-storybook

# Run on different port
npm run storybook -- --port 6007
```

### Story Organization

Stories are colocated with their components for better maintainability:

- `components/buttons/Button.tsx` → `components/buttons/Button.stories.tsx`
- Each story file contains multiple variants showcasing different states
- Interactive controls allow real-time prop testing
- Comprehensive mocking for external dependencies

### Available Components in Storybook

All 50+ UI components are documented with stories:

**Core UI**: Avatars, Buttons, Icons, Labels, Tokens
**Layout**: Banners, Display, NavBar, SubNav, Toolbars
**Data**: Tables, Progress, Charts (ECharts)
**Specialized**: Notebooks, Runtimes, Storage, Snapshots
**User Features**: IAM, Context, Students, Users
**Feedback**: Flashes, Confetti, TextReveal
**Advanced**: NBGrader, Screenshot, Snippets

## 🧩 Components

### UI Components
Expand Down
69 changes: 69 additions & 0 deletions src/components/avatars/UserProfileAvatar.stories.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,69 @@
/*
* Copyright (c) 2023-2025 Datalayer, Inc.
* Distributed under the terms of the Modified BSD License.
*/

import type { Meta, StoryObj } from '@storybook/react-vite';
import { UserProfileAvatar } from './UserProfileAvatar';

const meta = {
title: 'Datalayer/UserProfileAvatar',
component: UserProfileAvatar,
tags: ['autodocs'],
parameters: {
layout: 'centered',
},
} satisfies Meta<typeof UserProfileAvatar>;

export default meta;
type Story = StoryObj<typeof meta>;

export const Default: Story = {
args: {
user: {
id: '1',
username: 'johndoe',
email: '[email protected]',
avatarUrl: 'https://github.com/octocat.png',
firstName: 'John',
lastName: 'Doe',
},
size: 100,
},
};

export const WithClick: Story = {
args: {
user: {
id: '1',
username: 'johndoe',
email: '[email protected]',
avatarUrl: 'https://github.com/octocat.png',
firstName: 'John',
lastName: 'Doe',
},
size: 100,
onClick: () => alert('Avatar clicked!'),
},
};

export const SmallSize: Story = {
args: {
user: {
id: '1',
username: 'johndoe',
email: '[email protected]',
avatarUrl: 'https://github.com/octocat.png',
firstName: 'John',
lastName: 'Doe',
},
size: 40,
},
};

export const Loading: Story = {
args: {
user: undefined,
size: 100,
},
};
22 changes: 22 additions & 0 deletions src/components/banners/NoAutomationBanner.stories.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
/*
* Copyright (c) 2023-2025 Datalayer, Inc.
* Distributed under the terms of the Modified BSD License.
*/

import type { Meta, StoryObj } from '@storybook/react-vite';

import { NoAutomationBanner } from './NoAutomationBanner';

const meta = {
title: 'Datalayer/Banners/NoAutomationBanner',
component: NoAutomationBanner,
tags: ['autodocs'],
parameters: {
layout: 'padded',
},
} satisfies Meta<typeof NoAutomationBanner>;

export default meta;
type Story = StoryObj<typeof meta>;

export const Default: Story = {};
65 changes: 65 additions & 0 deletions src/components/buttons/DownloadCSVButton.stories.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,65 @@
/*
* Copyright (c) 2023-2025 Datalayer, Inc.
* Distributed under the terms of the Modified BSD License.
*/

import type { Meta, StoryObj } from '@storybook/react-vite';

import { DownloadCSVButton } from './DownloadCSVButton';

const meta = {
title: 'Datalayer/Buttons/DownloadCSVButton',
component: DownloadCSVButton,
tags: ['autodocs'],
parameters: {
layout: 'centered',
},
argTypes: {
variant: {
control: 'select',
options: ['default', 'primary', 'invisible', 'danger', 'link'],
},
},
} satisfies Meta<typeof DownloadCSVButton>;

export default meta;
type Story = StoryObj<typeof meta>;

const sampleData = {
users: [
{ name: 'Alice', age: 30, email: '[email protected]' },
{ name: 'Bob', age: 25, email: '[email protected]' },
{ name: 'Charlie', age: 35, email: '[email protected]' },
],
};

export const Default: Story = {
args: {
data: sampleData,
fileName: 'sample-data',
variant: 'default',
},
};

export const Primary: Story = {
args: {
data: sampleData,
fileName: 'sample-data',
variant: 'primary',
},
};

export const Danger: Story = {
args: {
data: sampleData,
fileName: 'sample-data',
variant: 'danger',
},
};

export const NoData: Story = {
args: {
fileName: 'empty-data',
variant: 'default',
},
};
Loading