Skip to content
This repository was archived by the owner on Mar 7, 2026. It is now read-only.
Closed
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
13 changes: 10 additions & 3 deletions .cursor/rules/coding.mdc
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,13 @@ alwaysApply: true
- Make sure you understand docs/planning.md and docs/project_plan.md
- Before starting coding, always present a plan for approval.
- Tasks are in docs/project_plan.md
- Each task will be implemented via a feature branch and a PR.
- Before PR is submitted, the task is marked with "PR" in docs/project_plan.md.
- After task is completed (PR merged), the task is marked with a checkmark in docs/project_plan.md
- Each task will be implemented via a feature branch and a PR to the develop branch.
- Always follow the following recipe for implementing tasks:
1. Start implementing after the user has explicitly told so.
2. After finishing implementing, perform all necessary tests and check if the DoD for this task is met.
3. Report on test results and DoD criteria and ask user if a PR should be submitted.
4. If the user approves submitting a PR, and before PR is submitted, the task is marked with "PR" in docs/project_plan.md.
5. After PR ist submitted, report to the user and wait for manual instructions.
6. The user will then review and merge the PR or ask for updates.
7. Pull the repo again to check if the PR has been merged.
8. After task is completed (PR merged), the task is marked with a checkmark in docs/project_plan.md
4 changes: 2 additions & 2 deletions .github/workflows/ci.yml
Original file line number Diff line number Diff line change
Expand Up @@ -2,9 +2,9 @@ name: CI

on:
push:
branches: [main]
branches: [main, develop]
pull_request:
branches: [main]
branches: [main, develop]

jobs:
install:
Expand Down
4 changes: 2 additions & 2 deletions .github/workflows/docker-test.yml
Original file line number Diff line number Diff line change
Expand Up @@ -2,9 +2,9 @@ name: Docker Test

on:
push:
branches: [main]
branches: [main, develop]
pull_request:
branches: [main]
branches: [main, develop]

jobs:
build-and-test:
Expand Down
3 changes: 2 additions & 1 deletion docs/project_plan.md
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@ A pragmatic breakdown into **four one‑week sprints** plus a preparatory **Spri
| 0.4 | Commit Husky hooks (commitlint, lint‑staged). | Attempting to commit code with ESLint errors is blocked locally. | ✓ |
| 0.5 | Seed Changesets & automatic versioning. | Merging PR increments `package.json version` and creates a changelog file. | ✓ |
| 0.6 | **Docker/Dokku infra** – Add multi‑stage `Dockerfile`, `Procfile`; CI job builds image & pushes to test Dokku app. | `gh workflow run docker-test` builds & deploys; Dokku reports container running, health‑check 200. | ✓ |
| 0.7 | **Update GitHub Actions** – Configure CI workflows to run on develop branch and PRs. | CI workflows run on both main and develop branches, as well as PRs targeting these branches. | PR |

---

Expand All @@ -32,7 +33,7 @@ A pragmatic breakdown into **four one‑week sprints** plus a preparatory **Spri
| 1.2b | Install and configure Storybook addon‑docs. | Documentation tab shows component documentation. | ✓ |
| 1.2c | Configure and verify Storybook addon‑a11y. | axe‑a11y addon shows zero violations. | ✓ |
| 1.3 | Add global theme bridging (`theme.css` ↔ DaisyUI). | Cypress visual diff (light/dark) matches golden images. | ✓ |
| 1.4 | Create first **AuthShell** layout with logo slot. | Rendered via Storybook; Playwright snapshot approved. | |
| 1.4 | Create first **AuthShell** layout with logo slot. | Rendered via Storybook; Playwright snapshot approved. | PR |
| 1.5 | Document design tokens (`DESIGN_TOKENS.md`). | File exists; CI step `tokens-check` verifies presence of each CSS var. | |

---
Expand Down
6 changes: 5 additions & 1 deletion packages/ui-kit/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -27,7 +27,10 @@
"build-storybook": "storybook build",
"cy:open": "cypress open",
"cy:run": "cypress run",
"theme-screenshots": "cypress run --spec \"cypress/e2e/theme.cy.ts\""
"theme-screenshots": "cypress run --spec \"cypress/e2e/theme.cy.ts\"",
"playwright:install": "playwright install",
"playwright:test": "playwright test",
"playwright:update-snapshots": "playwright test --update-snapshots"
},
"dependencies": {
"@radix-ui/react-slot": "^1.0.2",
Expand All @@ -43,6 +46,7 @@
"devDependencies": {
"@chromatic-com/storybook": "^1.9.0",
"@eslint/js": "^9.27.0",
"@playwright/test": "^1.52.0",
"@storybook/addon-a11y": "^8.6.14",
"@storybook/addon-docs": "^8.6.14",
"@storybook/addon-essentials": "^8.6.14",
Expand Down
31 changes: 31 additions & 0 deletions packages/ui-kit/playwright.config.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
import { defineConfig, devices } from '@playwright/test';

export default defineConfig({
testDir: './tests',
timeout: 30000,
fullyParallel: true,
forbidOnly: !!process.env.CI,
retries: process.env.CI ? 2 : 0,
workers: process.env.CI ? 1 : undefined,
reporter: 'html',

use: {
baseURL: 'http://localhost:6006', // Storybook URL
trace: 'on-first-retry',
screenshot: 'only-on-failure',
},

projects: [
{
name: 'chromium',
use: { ...devices['Desktop Chrome'] },
},
],

webServer: {
command: 'pnpm run storybook',
url: 'http://localhost:6006',
reuseExistingServer: !process.env.CI,
timeout: 120000,
},
});
102 changes: 102 additions & 0 deletions packages/ui-kit/src/layout/AuthShell/AuthShell.stories.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,102 @@
import type { Meta, StoryObj } from '@storybook/react';
import { AuthShell } from './AuthShell';
import { Button } from '@/components/primitives/Button';

const meta: Meta<typeof AuthShell> = {
title: 'Layout/AuthShell',
component: AuthShell,
parameters: {
layout: 'fullscreen',
a11y: {
disable: false,
},
},
tags: ['autodocs'],
};

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

// Sample logo component for the stories
const Logo = () => (
<div className="text-3xl font-bold text-primary">
<span className="tracking-tighter">Brand</span>
<span className="text-accent">Logo</span>
</div>
);

// Basic login form for the stories
const LoginForm = () => (
<div className="space-y-4">
<div className="text-xl font-semibold text-center mb-6">Sign in to your account</div>
<div className="space-y-2">
<label className="block text-sm font-medium">Email address</label>
<input
type="email"
placeholder="Enter your email"
className="w-full px-3 py-2 border border-input rounded-md bg-background"
/>
</div>
<div className="space-y-2">
<label className="block text-sm font-medium">Password</label>
<input
type="password"
placeholder="Enter your password"
className="w-full px-3 py-2 border border-input rounded-md bg-background"
/>
</div>
<div className="pt-2">
<Button className="w-full">Sign In</Button>
</div>
</div>
);

/**
* Default AuthShell with logo and login form
*/
export const Default: Story = {
args: {
logo: <Logo />,
children: <LoginForm />,
width: 'md',
},
};

/**
* AuthShell with logo, login form, and footer text
*/
export const WithFooter: Story = {
args: {
logo: <Logo />,
children: <LoginForm />,
footer: (
<div>
<p>Don't have an account? <a href="#" className="text-primary hover:underline">Sign up</a></p>
<p className="mt-2">© 2023 BrandName. All rights reserved.</p>
</div>
),
width: 'md',
},
};

/**
* Small variant of the AuthShell
*/
export const Small: Story = {
args: {
logo: <Logo />,
children: <LoginForm />,
width: 'sm',
},
};

/**
* Large variant of the AuthShell
*/
export const Large: Story = {
args: {
logo: <Logo />,
children: <LoginForm />,
width: 'lg',
},
};
83 changes: 83 additions & 0 deletions packages/ui-kit/src/layout/AuthShell/AuthShell.test.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,83 @@
import { render, screen } from '@testing-library/react';
import { AuthShell } from './AuthShell';

describe('AuthShell', () => {
it('renders children content', () => {
render(
<AuthShell>
<div data-testid="test-content">Test Content</div>
</AuthShell>
);

expect(screen.getByTestId('test-content')).toBeInTheDocument();
expect(screen.getByText('Test Content')).toBeInTheDocument();
});

it('renders logo when provided', () => {
render(
<AuthShell
logo={<div data-testid="test-logo">Brand Logo</div>}
>
<div>Test Content</div>
</AuthShell>
);

expect(screen.getByTestId('test-logo')).toBeInTheDocument();
expect(screen.getByText('Brand Logo')).toBeInTheDocument();
});

it('renders footer when provided', () => {
render(
<AuthShell
footer={<div data-testid="test-footer">Footer Content</div>}
>
<div>Test Content</div>
</AuthShell>
);

expect(screen.getByTestId('test-footer')).toBeInTheDocument();
expect(screen.getByText('Footer Content')).toBeInTheDocument();
});

it('applies width class based on width prop', () => {
const { container, rerender } = render(
<AuthShell width="sm">
<div>Test Content</div>
</AuthShell>
);

// Check for small width class
expect(container.querySelector('.max-w-sm')).toBeInTheDocument();

// Re-render with medium width
rerender(
<AuthShell width="md">
<div>Test Content</div>
</AuthShell>
);

// Check for medium width class
expect(container.querySelector('.max-w-md')).toBeInTheDocument();

// Re-render with large width
rerender(
<AuthShell width="lg">
<div>Test Content</div>
</AuthShell>
);

// Check for large width class
expect(container.querySelector('.max-w-lg')).toBeInTheDocument();
});

it('applies custom className when provided', () => {
const { container } = render(
<AuthShell className="custom-class">
<div>Test Content</div>
</AuthShell>
);

const authShellContainer = container.querySelector('.custom-class');
expect(authShellContainer).toBeInTheDocument();
});
});
51 changes: 51 additions & 0 deletions packages/ui-kit/src/layout/AuthShell/AuthShell.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,51 @@
import React from 'react';
import { cn } from '@/utils/cn';
import { AuthShellProps } from './types';

/**
* AuthShell is a layout component for authentication-related pages like login, signup, and password reset.
* It provides a centered container with optional logo and footer slots.
*/
export const AuthShell: React.FC<AuthShellProps> = ({
logo,
children,
footer,
className,
width = 'md',
}) => {
const containerWidthClass = {
sm: 'max-w-sm',
md: 'max-w-md',
lg: 'max-w-lg',
}[width];

return (
<div className="min-h-screen bg-background flex flex-col items-center justify-center p-4">
<div
className={cn(
'w-full flex flex-col items-center p-6 rounded-lg shadow-md bg-card text-card-foreground',
containerWidthClass,
className
)}
>
{logo && (
<div className="flex justify-center mb-6 w-full">
{logo}
</div>
)}

<div className="w-full">
{children}
</div>

{footer && (
<div className="mt-6 w-full text-center text-sm text-muted-foreground">
{footer}
</div>
)}
</div>
</div>
);
};

AuthShell.displayName = 'AuthShell';
2 changes: 2 additions & 0 deletions packages/ui-kit/src/layout/AuthShell/index.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
export { AuthShell } from './AuthShell';
export type { AuthShellProps } from './types';
32 changes: 32 additions & 0 deletions packages/ui-kit/src/layout/AuthShell/types.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
import { ReactNode } from 'react';

/**
* Props for the AuthShell component
*/
export interface AuthShellProps {
/**
* Logo to display at the top of the auth shell
*/
logo?: ReactNode;

/**
* Main content of the auth shell
*/
children: ReactNode;

/**
* Optional footer content
*/
footer?: ReactNode;

/**
* Optional additional class name for the container
*/
className?: string;

/**
* Width of the auth container
* @default 'md'
*/
width?: 'sm' | 'md' | 'lg';
}
4 changes: 2 additions & 2 deletions packages/ui-kit/src/layout/index.ts
Original file line number Diff line number Diff line change
@@ -1,2 +1,2 @@
// Layout components will be exported here
export { }
// Layout components
export * from './AuthShell';
Loading