Storybook #4
@@ -39,3 +39,6 @@ yarn-error.log*
|
|||||||
# typescript
|
# typescript
|
||||||
*.tsbuildinfo
|
*.tsbuildinfo
|
||||||
next-env.d.ts
|
next-env.d.ts
|
||||||
|
|
||||||
|
*storybook.log
|
||||||
|
storybook-static
|
||||||
|
|||||||
@@ -0,0 +1,31 @@
|
|||||||
|
/** @type { import('@storybook/nextjs-vite').StorybookConfig } */
|
||||||
|
const config = {
|
||||||
|
stories: [
|
||||||
|
"../stories/**/*.mdx",
|
||||||
|
"../stories/**/*.stories.@(js|jsx|mjs|ts|tsx)",
|
||||||
|
],
|
||||||
|
addons: [
|
||||||
|
"@chromatic-com/storybook",
|
||||||
|
"@storybook/addon-docs",
|
||||||
|
"@storybook/addon-onboarding",
|
||||||
|
"@storybook/addon-a11y",
|
||||||
|
"@storybook/addon-vitest",
|
||||||
|
],
|
||||||
|
framework: {
|
||||||
|
name: "@storybook/nextjs-vite",
|
||||||
|
options: {},
|
||||||
|
},
|
||||||
|
staticDirs: ["../public"],
|
||||||
|
async viteFinal(cfg) {
|
||||||
|
// Ensure esbuild treats .js as JSX during dep pre-bundling
|
||||||
|
cfg.optimizeDeps ??= {};
|
||||||
|
cfg.optimizeDeps.esbuildOptions ??= {};
|
||||||
|
cfg.optimizeDeps.esbuildOptions.loader = {
|
||||||
|
...(cfg.optimizeDeps.esbuildOptions.loader || {}),
|
||||||
|
".js": "jsx",
|
||||||
|
".ts": "tsx",
|
||||||
|
};
|
||||||
|
return cfg;
|
||||||
|
},
|
||||||
|
};
|
||||||
|
export default config;
|
||||||
@@ -0,0 +1,63 @@
|
|||||||
|
import "../app/globals.css";
|
||||||
|
|
||||||
|
/** @type { import('@storybook/nextjs-vite').Preview } */
|
||||||
|
const preview = {
|
||||||
|
parameters: {
|
||||||
|
nextjs: { appDirectory: true },
|
||||||
|
controls: {
|
||||||
|
matchers: {
|
||||||
|
color: /(background|color)$/i,
|
||||||
|
date: /Date$/i,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
|
||||||
|
a11y: {
|
||||||
|
// 'todo' - show a11y violations in the test UI only
|
||||||
|
// 'error' - fail CI on a11y violations
|
||||||
|
// 'off' - skip a11y checks entirely
|
||||||
|
test: "todo",
|
||||||
|
},
|
||||||
|
|
||||||
|
backgrounds: {
|
||||||
|
default: "dark",
|
||||||
|
values: [
|
||||||
|
{
|
||||||
|
name: "dark",
|
||||||
|
value: "#000000",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "light",
|
||||||
|
value: "#ffffff",
|
||||||
|
},
|
||||||
|
],
|
||||||
|
},
|
||||||
|
|
||||||
|
viewport: {
|
||||||
|
defaultViewport: "md",
|
||||||
|
viewports: {
|
||||||
|
xsm: {
|
||||||
|
name: "XSmall (≤429px)",
|
||||||
|
styles: { width: "429px", height: "800px" },
|
||||||
|
},
|
||||||
|
sm: {
|
||||||
|
name: "Small (≥430px)",
|
||||||
|
styles: { width: "430px", height: "800px" },
|
||||||
|
},
|
||||||
|
md: {
|
||||||
|
name: "Medium (≥640px)",
|
||||||
|
styles: { width: "640px", height: "800px" },
|
||||||
|
},
|
||||||
|
lg: {
|
||||||
|
name: "Large (≥1024px)",
|
||||||
|
styles: { width: "1024px", height: "800px" },
|
||||||
|
},
|
||||||
|
xl: {
|
||||||
|
name: "XLarge (≥1440px)",
|
||||||
|
styles: { width: "1440px", height: "900px" },
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
};
|
||||||
|
|
||||||
|
export default preview;
|
||||||
@@ -0,0 +1,7 @@
|
|||||||
|
import * as a11yAddonAnnotations from "@storybook/addon-a11y/preview";
|
||||||
|
import { setProjectAnnotations } from '@storybook/nextjs-vite';
|
||||||
|
import * as projectAnnotations from './preview';
|
||||||
|
|
||||||
|
// This is an important step to apply the right configuration when testing your stories.
|
||||||
|
// More info at: https://storybook.js.org/docs/api/portable-stories/portable-stories-vitest#setprojectannotations
|
||||||
|
setProjectAnnotations([a11yAddonAnnotations, projectAnnotations]);
|
||||||
@@ -109,12 +109,17 @@ export default function Header({ onToggle }) {
|
|||||||
|
|
||||||
{/* Navigation Links - Consistent center positioning */}
|
{/* Navigation Links - Consistent center positioning */}
|
||||||
<div className="flex items-center">
|
<div className="flex items-center">
|
||||||
<div className="block sm:hidden -me-[2px]">
|
{/* XSmall breakpoint - Navigation items moved to right section */}
|
||||||
<MenuBar size="default">{renderNavigationItems("xsmall")}</MenuBar>
|
<div className="block sm:hidden">
|
||||||
|
{/* Empty for XSmall - navigation moved to right */}
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
{/* Small breakpoint - All items grouped together, centered */}
|
||||||
<div className="hidden sm:block md:hidden">
|
<div className="hidden sm:block md:hidden">
|
||||||
<MenuBar size="default">{renderNavigationItems("xsmall")}</MenuBar>
|
<MenuBar size="default">
|
||||||
|
{renderNavigationItems("xsmall")}
|
||||||
|
{renderLoginButton("xsmall")}
|
||||||
|
</MenuBar>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div className="hidden md:block lg:hidden">
|
<div className="hidden md:block lg:hidden">
|
||||||
@@ -132,17 +137,18 @@ export default function Header({ onToggle }) {
|
|||||||
|
|
||||||
{/* Authentication Elements - Consistent right alignment across all breakpoints */}
|
{/* Authentication Elements - Consistent right alignment across all breakpoints */}
|
||||||
<div className="flex items-center">
|
<div className="flex items-center">
|
||||||
{/* XSmall and Small breakpoints */}
|
{/* XSmall breakpoint - All navigation items + Create Rule button */}
|
||||||
<div className="block sm:hidden">
|
<div className="block sm:hidden">
|
||||||
<div className="flex items-center gap-[var(--spacing-scale-004)]">
|
<div className="flex items-center gap-[var(--spacing-scale-001)]">
|
||||||
|
{renderNavigationItems("xsmall")}
|
||||||
{renderLoginButton("xsmall")}
|
{renderLoginButton("xsmall")}
|
||||||
{renderCreateRuleButton("xsmall", "small", "small")}
|
{renderCreateRuleButton("xsmall", "small", "small")}
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
{/* Small breakpoint - Only Create Rule button */}
|
||||||
<div className="hidden sm:block md:hidden">
|
<div className="hidden sm:block md:hidden">
|
||||||
<div className="flex items-center gap-[var(--spacing-scale-004)]">
|
<div className="flex items-center gap-[var(--spacing-scale-004)]">
|
||||||
{renderLoginButton("xsmall")}
|
|
||||||
{renderCreateRuleButton("xsmall", "small", "small")}
|
{renderCreateRuleButton("xsmall", "small", "small")}
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|||||||
@@ -1,6 +1,3 @@
|
|||||||
@tailwind base;
|
|
||||||
@tailwind components;
|
|
||||||
@tailwind utilities;
|
|
||||||
@import "./tailwind.css";
|
@import "./tailwind.css";
|
||||||
|
|
||||||
body {
|
body {
|
||||||
|
|||||||
@@ -5,6 +5,11 @@
|
|||||||
|
|
||||||
@import "tailwindcss";
|
@import "tailwindcss";
|
||||||
|
|
||||||
|
/* Make sure Tailwind scans stories and .storybook files */
|
||||||
|
@source "../stories/**/*";
|
||||||
|
@source "../app/**/*";
|
||||||
|
@source "../.storybook/**/*";
|
||||||
|
|
||||||
@theme inline {
|
@theme inline {
|
||||||
/* Custom breakpoints */
|
/* Custom breakpoints */
|
||||||
--breakpoint-xsm: 429px;
|
--breakpoint-xsm: 429px;
|
||||||
|
|||||||
+7
-7
@@ -1,3 +1,6 @@
|
|||||||
|
// For more info, see https://github.com/storybookjs/eslint-plugin-storybook#configuration-flat-config-format
|
||||||
|
import storybook from "eslint-plugin-storybook";
|
||||||
|
|
||||||
import { dirname } from "path";
|
import { dirname } from "path";
|
||||||
import { fileURLToPath } from "url";
|
import { fileURLToPath } from "url";
|
||||||
import { FlatCompat } from "@eslint/eslintrc";
|
import { FlatCompat } from "@eslint/eslintrc";
|
||||||
@@ -9,12 +12,9 @@ const compat = new FlatCompat({
|
|||||||
baseDirectory: __dirname,
|
baseDirectory: __dirname,
|
||||||
});
|
});
|
||||||
|
|
||||||
const eslintConfig = [
|
const eslintConfig = [...compat.extends("next/core-web-vitals"), {
|
||||||
...compat.extends("next/core-web-vitals"),
|
files: ["**/*.js", "**/*.jsx", "**/*.mjs"],
|
||||||
{
|
ignores: ["**/*.ts", "**/*.tsx"],
|
||||||
files: ["**/*.js", "**/*.jsx", "**/*.mjs"],
|
}, ...storybook.configs["flat/recommended"]];
|
||||||
ignores: ["**/*.ts", "**/*.tsx"],
|
|
||||||
},
|
|
||||||
];
|
|
||||||
|
|
||||||
export default eslintConfig;
|
export default eslintConfig;
|
||||||
|
|||||||
Generated
+3858
-141
File diff suppressed because it is too large
Load Diff
+18
-2
@@ -7,7 +7,9 @@
|
|||||||
"build": "next build",
|
"build": "next build",
|
||||||
"start": "next start",
|
"start": "next start",
|
||||||
"lint": "next lint",
|
"lint": "next lint",
|
||||||
"postinstall": "npm rebuild lightningcss"
|
"postinstall": "npm rebuild lightningcss",
|
||||||
|
"storybook": "storybook dev -p 6006",
|
||||||
|
"build-storybook": "storybook build"
|
||||||
},
|
},
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"next": "15.2.4",
|
"next": "15.2.4",
|
||||||
@@ -15,11 +17,25 @@
|
|||||||
"react-dom": "^19.0.0"
|
"react-dom": "^19.0.0"
|
||||||
},
|
},
|
||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
|
"@chromatic-com/storybook": "^4.1.0",
|
||||||
"@eslint/eslintrc": "^3",
|
"@eslint/eslintrc": "^3",
|
||||||
|
"@storybook/addon-a11y": "^9.1.2",
|
||||||
|
"@storybook/addon-docs": "^9.1.2",
|
||||||
|
"@storybook/addon-onboarding": "^9.1.2",
|
||||||
|
"@storybook/addon-styling-webpack": "^2.0.0",
|
||||||
|
"@storybook/addon-viewport": "^9.0.8",
|
||||||
|
"@storybook/addon-vitest": "^9.1.2",
|
||||||
|
"@storybook/nextjs-vite": "^9.1.2",
|
||||||
"@tailwindcss/postcss": "^4.1.11",
|
"@tailwindcss/postcss": "^4.1.11",
|
||||||
|
"@vitest/browser": "^3.2.4",
|
||||||
|
"@vitest/coverage-v8": "^3.2.4",
|
||||||
"eslint": "^9",
|
"eslint": "^9",
|
||||||
"eslint-config-next": "15.2.4",
|
"eslint-config-next": "15.2.4",
|
||||||
|
"eslint-plugin-storybook": "^9.1.2",
|
||||||
|
"playwright": "^1.54.2",
|
||||||
"postcss": "^8.5.6",
|
"postcss": "^8.5.6",
|
||||||
"tailwindcss": "^4.0.0"
|
"storybook": "^9.1.2",
|
||||||
|
"tailwindcss": "^4.0.0",
|
||||||
|
"vitest": "^3.2.4"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -0,0 +1,139 @@
|
|||||||
|
import Avatar from "../app/components/Avatar";
|
||||||
|
|
||||||
|
export default {
|
||||||
|
title: "Components/Avatar",
|
||||||
|
component: Avatar,
|
||||||
|
parameters: {
|
||||||
|
layout: "centered",
|
||||||
|
docs: {
|
||||||
|
description: {
|
||||||
|
component:
|
||||||
|
"A simple avatar component that displays user profile images with multiple size variants. Supports custom images and alt text for accessibility.",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
argTypes: {
|
||||||
|
src: {
|
||||||
|
control: { type: "text" },
|
||||||
|
description: "The source URL of the avatar image",
|
||||||
|
},
|
||||||
|
alt: {
|
||||||
|
control: { type: "text" },
|
||||||
|
description: "Alt text for accessibility",
|
||||||
|
},
|
||||||
|
size: {
|
||||||
|
control: { type: "select" },
|
||||||
|
options: ["small", "medium", "large", "xlarge"],
|
||||||
|
description: "The size of the avatar",
|
||||||
|
},
|
||||||
|
className: {
|
||||||
|
control: { type: "text" },
|
||||||
|
description: "Additional CSS classes",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
tags: ["autodocs"],
|
||||||
|
};
|
||||||
|
|
||||||
|
export const Default = {
|
||||||
|
args: {
|
||||||
|
src: "/assets/Avatar_1.png",
|
||||||
|
alt: "User Avatar",
|
||||||
|
size: "medium",
|
||||||
|
},
|
||||||
|
};
|
||||||
|
|
||||||
|
export const Sizes = {
|
||||||
|
args: {
|
||||||
|
src: "/assets/Avatar_1.png",
|
||||||
|
alt: "User Avatar",
|
||||||
|
},
|
||||||
|
render: (args) => (
|
||||||
|
<div className="space-y-4">
|
||||||
|
<div className="space-x-4">
|
||||||
|
<Avatar {...args} size="small" />
|
||||||
|
<Avatar {...args} size="medium" />
|
||||||
|
<Avatar {...args} size="large" />
|
||||||
|
<Avatar {...args} size="xlarge" />
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
),
|
||||||
|
parameters: {
|
||||||
|
docs: {
|
||||||
|
description: {
|
||||||
|
story: "Different size variants available for the avatar component.",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
};
|
||||||
|
|
||||||
|
export const DifferentAvatars = {
|
||||||
|
args: {
|
||||||
|
size: "large",
|
||||||
|
},
|
||||||
|
render: (args) => (
|
||||||
|
<div className="space-y-4">
|
||||||
|
<div className="space-x-4">
|
||||||
|
<Avatar {...args} src="/assets/Avatar_1.png" alt="User 1" />
|
||||||
|
<Avatar {...args} src="/assets/Avatar_2.png" alt="User 2" />
|
||||||
|
<Avatar {...args} src="/assets/Avatar_3.png" alt="User 3" />
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
),
|
||||||
|
parameters: {
|
||||||
|
docs: {
|
||||||
|
description: {
|
||||||
|
story: "Different avatar images available in the project.",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
};
|
||||||
|
|
||||||
|
export const AllSizesWithDifferentAvatars = {
|
||||||
|
args: {},
|
||||||
|
render: () => (
|
||||||
|
<div className="space-y-6">
|
||||||
|
<div>
|
||||||
|
<h3 className="text-white font-semibold mb-3">Small Size</h3>
|
||||||
|
<div className="space-x-4">
|
||||||
|
<Avatar size="small" src="/assets/Avatar_1.png" alt="User 1" />
|
||||||
|
<Avatar size="small" src="/assets/Avatar_2.png" alt="User 2" />
|
||||||
|
<Avatar size="small" src="/assets/Avatar_3.png" alt="User 3" />
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div>
|
||||||
|
<h3 className="text-white font-semibold mb-3">Medium Size</h3>
|
||||||
|
<div className="space-x-4">
|
||||||
|
<Avatar size="medium" src="/assets/Avatar_1.png" alt="User 1" />
|
||||||
|
<Avatar size="medium" src="/assets/Avatar_2.png" alt="User 2" />
|
||||||
|
<Avatar size="medium" src="/assets/Avatar_3.png" alt="User 3" />
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div>
|
||||||
|
<h3 className="text-white font-semibold mb-3">Large Size</h3>
|
||||||
|
<div className="space-x-4">
|
||||||
|
<Avatar size="large" src="/assets/Avatar_1.png" alt="User 1" />
|
||||||
|
<Avatar size="large" src="/assets/Avatar_2.png" alt="User 2" />
|
||||||
|
<Avatar size="large" src="/assets/Avatar_3.png" alt="User 3" />
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div>
|
||||||
|
<h3 className="text-white font-semibold mb-3">XLarge Size</h3>
|
||||||
|
<div className="space-x-4">
|
||||||
|
<Avatar size="xlarge" src="/assets/Avatar_1.png" alt="User 1" />
|
||||||
|
<Avatar size="xlarge" src="/assets/Avatar_2.png" alt="User 2" />
|
||||||
|
<Avatar size="xlarge" src="/assets/Avatar_3.png" alt="User 3" />
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
),
|
||||||
|
parameters: {
|
||||||
|
docs: {
|
||||||
|
description: {
|
||||||
|
story: "Complete overview of all avatar sizes with different user images.",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
};
|
||||||
@@ -0,0 +1,206 @@
|
|||||||
|
import AvatarContainer from "../app/components/AvatarContainer";
|
||||||
|
import Avatar from "../app/components/Avatar";
|
||||||
|
|
||||||
|
export default {
|
||||||
|
title: "Components/AvatarContainer",
|
||||||
|
component: AvatarContainer,
|
||||||
|
parameters: {
|
||||||
|
layout: "centered",
|
||||||
|
docs: {
|
||||||
|
description: {
|
||||||
|
component:
|
||||||
|
"A container component that groups multiple avatars together with overlapping spacing. Useful for displaying multiple users or team members in a compact format.",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
argTypes: {
|
||||||
|
size: {
|
||||||
|
control: { type: "select" },
|
||||||
|
options: ["small", "medium", "large", "xlarge"],
|
||||||
|
description: "The size of the avatar container and its children",
|
||||||
|
},
|
||||||
|
className: {
|
||||||
|
control: { type: "text" },
|
||||||
|
description: "Additional CSS classes",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
tags: ["autodocs"],
|
||||||
|
};
|
||||||
|
|
||||||
|
export const Default = {
|
||||||
|
args: {
|
||||||
|
size: "medium",
|
||||||
|
},
|
||||||
|
render: (args) => (
|
||||||
|
<AvatarContainer {...args}>
|
||||||
|
<Avatar size={args.size} src="/assets/Avatar_1.png" alt="User 1" />
|
||||||
|
<Avatar size={args.size} src="/assets/Avatar_2.png" alt="User 2" />
|
||||||
|
<Avatar size={args.size} src="/assets/Avatar_3.png" alt="User 3" />
|
||||||
|
</AvatarContainer>
|
||||||
|
),
|
||||||
|
};
|
||||||
|
|
||||||
|
export const Sizes = {
|
||||||
|
args: {},
|
||||||
|
render: () => (
|
||||||
|
<div className="space-y-6">
|
||||||
|
<div>
|
||||||
|
<h3 className="text-white font-semibold mb-3">Small Size</h3>
|
||||||
|
<AvatarContainer size="small">
|
||||||
|
<Avatar size="small" src="/assets/Avatar_1.png" alt="User 1" />
|
||||||
|
<Avatar size="small" src="/assets/Avatar_2.png" alt="User 2" />
|
||||||
|
<Avatar size="small" src="/assets/Avatar_3.png" alt="User 3" />
|
||||||
|
</AvatarContainer>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div>
|
||||||
|
<h3 className="text-white font-semibold mb-3">Medium Size</h3>
|
||||||
|
<AvatarContainer size="medium">
|
||||||
|
<Avatar size="medium" src="/assets/Avatar_1.png" alt="User 1" />
|
||||||
|
<Avatar size="medium" src="/assets/Avatar_2.png" alt="User 2" />
|
||||||
|
<Avatar size="medium" src="/assets/Avatar_3.png" alt="User 3" />
|
||||||
|
</AvatarContainer>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div>
|
||||||
|
<h3 className="text-white font-semibold mb-3">Large Size</h3>
|
||||||
|
<AvatarContainer size="large">
|
||||||
|
<Avatar size="large" src="/assets/Avatar_1.png" alt="User 1" />
|
||||||
|
<Avatar size="large" src="/assets/Avatar_2.png" alt="User 2" />
|
||||||
|
<Avatar size="large" src="/assets/Avatar_3.png" alt="User 3" />
|
||||||
|
</AvatarContainer>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div>
|
||||||
|
<h3 className="text-white font-semibold mb-3">XLarge Size</h3>
|
||||||
|
<AvatarContainer size="xlarge">
|
||||||
|
<Avatar size="xlarge" src="/assets/Avatar_1.png" alt="User 1" />
|
||||||
|
<Avatar size="xlarge" src="/assets/Avatar_2.png" alt="User 2" />
|
||||||
|
<Avatar size="xlarge" src="/assets/Avatar_3.png" alt="User 3" />
|
||||||
|
</AvatarContainer>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
),
|
||||||
|
parameters: {
|
||||||
|
docs: {
|
||||||
|
description: {
|
||||||
|
story:
|
||||||
|
"Different size variants of the avatar container with overlapping spacing.",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
};
|
||||||
|
|
||||||
|
export const DifferentGroupSizes = {
|
||||||
|
args: {},
|
||||||
|
render: () => (
|
||||||
|
<div className="space-y-6">
|
||||||
|
<div>
|
||||||
|
<h3 className="text-white font-semibold mb-3">2 Users</h3>
|
||||||
|
<AvatarContainer size="large">
|
||||||
|
<Avatar size="large" src="/assets/Avatar_1.png" alt="User 1" />
|
||||||
|
<Avatar size="large" src="/assets/Avatar_2.png" alt="User 2" />
|
||||||
|
</AvatarContainer>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div>
|
||||||
|
<h3 className="text-white font-semibold mb-3">3 Users</h3>
|
||||||
|
<AvatarContainer size="large">
|
||||||
|
<Avatar size="large" src="/assets/Avatar_1.png" alt="User 1" />
|
||||||
|
<Avatar size="large" src="/assets/Avatar_2.png" alt="User 2" />
|
||||||
|
<Avatar size="large" src="/assets/Avatar_3.png" alt="User 3" />
|
||||||
|
</AvatarContainer>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div>
|
||||||
|
<h3 className="text-white font-semibold mb-3">4 Users</h3>
|
||||||
|
<AvatarContainer size="large">
|
||||||
|
<Avatar size="large" src="/assets/Avatar_1.png" alt="User 1" />
|
||||||
|
<Avatar size="large" src="/assets/Avatar_2.png" alt="User 2" />
|
||||||
|
<Avatar size="large" src="/assets/Avatar_3.png" alt="User 3" />
|
||||||
|
<Avatar size="large" src="/assets/Avatar_1.png" alt="User 4" />
|
||||||
|
</AvatarContainer>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div>
|
||||||
|
<h3 className="text-white font-semibold mb-3">5 Users</h3>
|
||||||
|
<AvatarContainer size="large">
|
||||||
|
<Avatar size="large" src="/assets/Avatar_1.png" alt="User 1" />
|
||||||
|
<Avatar size="large" src="/assets/Avatar_2.png" alt="User 2" />
|
||||||
|
<Avatar size="large" src="/assets/Avatar_3.png" alt="User 3" />
|
||||||
|
<Avatar size="large" src="/assets/Avatar_1.png" alt="User 4" />
|
||||||
|
<Avatar size="large" src="/assets/Avatar_2.png" alt="User 5" />
|
||||||
|
</AvatarContainer>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
),
|
||||||
|
parameters: {
|
||||||
|
docs: {
|
||||||
|
description: {
|
||||||
|
story:
|
||||||
|
"Avatar containers with different numbers of users to show the overlapping effect.",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
};
|
||||||
|
|
||||||
|
export const InContext = {
|
||||||
|
args: {},
|
||||||
|
render: () => (
|
||||||
|
<div className="min-h-screen bg-[var(--color-surface-default-primary)] p-8">
|
||||||
|
<div className="max-w-4xl mx-auto">
|
||||||
|
<h2 className="text-white font-semibold mb-6">
|
||||||
|
Avatar Container in Context
|
||||||
|
</h2>
|
||||||
|
|
||||||
|
<div className="space-y-8">
|
||||||
|
<div className="bg-[var(--color-surface-default-secondary)] p-6 rounded-lg">
|
||||||
|
<h3 className="text-white font-semibold mb-4">Team Members</h3>
|
||||||
|
<div className="flex items-center space-x-4">
|
||||||
|
<AvatarContainer size="medium">
|
||||||
|
<Avatar size="medium" src="/assets/Avatar_1.png" alt="User 1" />
|
||||||
|
<Avatar size="medium" src="/assets/Avatar_2.png" alt="User 2" />
|
||||||
|
<Avatar size="medium" src="/assets/Avatar_3.png" alt="User 3" />
|
||||||
|
</AvatarContainer>
|
||||||
|
<span className="text-white">3 team members</span>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div className="bg-[var(--color-surface-default-secondary)] p-6 rounded-lg">
|
||||||
|
<h3 className="text-white font-semibold mb-4">
|
||||||
|
Project Contributors
|
||||||
|
</h3>
|
||||||
|
<div className="flex items-center space-x-4">
|
||||||
|
<AvatarContainer size="large">
|
||||||
|
<Avatar size="large" src="/assets/Avatar_1.png" alt="User 1" />
|
||||||
|
<Avatar size="large" src="/assets/Avatar_2.png" alt="User 2" />
|
||||||
|
<Avatar size="large" src="/assets/Avatar_3.png" alt="User 3" />
|
||||||
|
<Avatar size="large" src="/assets/Avatar_1.png" alt="User 4" />
|
||||||
|
</AvatarContainer>
|
||||||
|
<span className="text-white">4 contributors</span>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div className="bg-[var(--color-surface-default-secondary)] p-6 rounded-lg">
|
||||||
|
<h3 className="text-white font-semibold mb-4">Small Team</h3>
|
||||||
|
<div className="flex items-center space-x-4">
|
||||||
|
<AvatarContainer size="small">
|
||||||
|
<Avatar size="small" src="/assets/Avatar_1.png" alt="User 1" />
|
||||||
|
<Avatar size="small" src="/assets/Avatar_2.png" alt="User 2" />
|
||||||
|
</AvatarContainer>
|
||||||
|
<span className="text-white">2 members</span>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
),
|
||||||
|
parameters: {
|
||||||
|
docs: {
|
||||||
|
description: {
|
||||||
|
story:
|
||||||
|
"Avatar containers used in realistic contexts to show how they integrate with other UI elements.",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
};
|
||||||
@@ -0,0 +1,180 @@
|
|||||||
|
import Button from "../app/components/Button";
|
||||||
|
|
||||||
|
export default {
|
||||||
|
title: "Components/Button",
|
||||||
|
component: Button,
|
||||||
|
parameters: {
|
||||||
|
layout: "centered",
|
||||||
|
docs: {
|
||||||
|
description: {
|
||||||
|
component:
|
||||||
|
"A versatile button component with multiple variants, sizes, and states. Can render as a button or link with full accessibility support. Includes focus states with keyboard navigation - use Tab key to test focus indicators.",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
argTypes: {
|
||||||
|
variant: {
|
||||||
|
control: { type: "select" },
|
||||||
|
options: ["default", "secondary"],
|
||||||
|
description: "The visual style variant of the button",
|
||||||
|
},
|
||||||
|
size: {
|
||||||
|
control: { type: "select" },
|
||||||
|
options: ["xsmall", "small", "large", "xlarge"],
|
||||||
|
description: "The size of the button",
|
||||||
|
},
|
||||||
|
disabled: {
|
||||||
|
control: { type: "boolean" },
|
||||||
|
description: "Whether the button is disabled",
|
||||||
|
},
|
||||||
|
href: {
|
||||||
|
control: { type: "text" },
|
||||||
|
description: "If provided, renders as a link instead of a button",
|
||||||
|
},
|
||||||
|
onClick: { action: "clicked" },
|
||||||
|
},
|
||||||
|
tags: ["autodocs"],
|
||||||
|
};
|
||||||
|
|
||||||
|
export const Default = {
|
||||||
|
args: {
|
||||||
|
children: "Button",
|
||||||
|
},
|
||||||
|
};
|
||||||
|
|
||||||
|
export const Variants = {
|
||||||
|
args: {
|
||||||
|
children: "Button",
|
||||||
|
size: "large",
|
||||||
|
},
|
||||||
|
render: (args) => (
|
||||||
|
<div className="space-y-4">
|
||||||
|
<div className="space-x-4">
|
||||||
|
<Button {...args} variant="default">
|
||||||
|
Default
|
||||||
|
</Button>
|
||||||
|
<Button {...args} variant="secondary">
|
||||||
|
Secondary
|
||||||
|
</Button>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
),
|
||||||
|
parameters: {
|
||||||
|
docs: {
|
||||||
|
description: {
|
||||||
|
story: "Different visual variants of the button component.",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
};
|
||||||
|
|
||||||
|
export const Sizes = {
|
||||||
|
args: {
|
||||||
|
children: "Button",
|
||||||
|
variant: "default",
|
||||||
|
},
|
||||||
|
render: (args) => (
|
||||||
|
<div className="space-y-4">
|
||||||
|
<div className="space-x-4">
|
||||||
|
<Button {...args} size="xsmall">
|
||||||
|
XSmall
|
||||||
|
</Button>
|
||||||
|
<Button {...args} size="small">
|
||||||
|
Small
|
||||||
|
</Button>
|
||||||
|
<Button {...args} size="large">
|
||||||
|
Large
|
||||||
|
</Button>
|
||||||
|
<Button {...args} size="xlarge">
|
||||||
|
XLarge
|
||||||
|
</Button>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
),
|
||||||
|
parameters: {
|
||||||
|
docs: {
|
||||||
|
description: {
|
||||||
|
story: "Different sizes available for the button component.",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
};
|
||||||
|
|
||||||
|
export const States = {
|
||||||
|
args: {
|
||||||
|
children: "Button",
|
||||||
|
size: "large",
|
||||||
|
variant: "default",
|
||||||
|
},
|
||||||
|
render: (args) => (
|
||||||
|
<div className="space-y-4">
|
||||||
|
<div className="space-x-4">
|
||||||
|
<Button {...args}>Normal</Button>
|
||||||
|
<Button {...args} disabled>
|
||||||
|
Disabled
|
||||||
|
</Button>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
),
|
||||||
|
parameters: {
|
||||||
|
docs: {
|
||||||
|
description: {
|
||||||
|
story: "Different states of the button component.",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
};
|
||||||
|
|
||||||
|
export const AllVariants = {
|
||||||
|
args: {},
|
||||||
|
render: () => (
|
||||||
|
<div className="space-y-6">
|
||||||
|
<div>
|
||||||
|
<h3 className="text-white font-semibold mb-3">Default Variant</h3>
|
||||||
|
<div className="space-x-4">
|
||||||
|
<Button size="xsmall">XSmall</Button>
|
||||||
|
<Button size="small">Small</Button>
|
||||||
|
<Button size="large">Large</Button>
|
||||||
|
<Button size="xlarge">XLarge</Button>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div>
|
||||||
|
<h3 className="text-white font-semibold mb-3">Secondary Variant</h3>
|
||||||
|
<div className="space-x-4">
|
||||||
|
<Button variant="secondary" size="xsmall">
|
||||||
|
XSmall
|
||||||
|
</Button>
|
||||||
|
<Button variant="secondary" size="small">
|
||||||
|
Small
|
||||||
|
</Button>
|
||||||
|
<Button variant="secondary" size="large">
|
||||||
|
Large
|
||||||
|
</Button>
|
||||||
|
<Button variant="secondary" size="xlarge">
|
||||||
|
XLarge
|
||||||
|
</Button>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div>
|
||||||
|
<h3 className="text-white font-semibold mb-3">Disabled States</h3>
|
||||||
|
<div className="space-x-4">
|
||||||
|
<Button size="large" disabled>
|
||||||
|
Default Disabled
|
||||||
|
</Button>
|
||||||
|
<Button variant="secondary" size="large" disabled>
|
||||||
|
Secondary Disabled
|
||||||
|
</Button>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
),
|
||||||
|
parameters: {
|
||||||
|
docs: {
|
||||||
|
description: {
|
||||||
|
story: "Complete overview of all button variants, sizes, and states.",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
};
|
||||||
@@ -0,0 +1,74 @@
|
|||||||
|
import Footer from "../app/components/Footer";
|
||||||
|
|
||||||
|
export default {
|
||||||
|
title: "Components/Footer",
|
||||||
|
component: Footer,
|
||||||
|
parameters: {
|
||||||
|
layout: "fullscreen",
|
||||||
|
docs: {
|
||||||
|
description: {
|
||||||
|
component:
|
||||||
|
"The main footer with responsive layout, branding section, navigation links, and legal information. Features different logo sizes and layout changes across breakpoints.",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
tags: ["autodocs"],
|
||||||
|
};
|
||||||
|
|
||||||
|
export const Default = {
|
||||||
|
args: {},
|
||||||
|
parameters: {
|
||||||
|
docs: {
|
||||||
|
description: {
|
||||||
|
story:
|
||||||
|
"Use the Viewport toolbar to see how the footer adapts to different screen sizes. The layout changes from stacked to side-by-side and logo sizes adjust.",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
};
|
||||||
|
|
||||||
|
// Story to show the footer in a realistic page context
|
||||||
|
export const InPageContext = {
|
||||||
|
args: {},
|
||||||
|
render: () => (
|
||||||
|
<div className="min-h-screen bg-[var(--color-surface-default-primary)]">
|
||||||
|
<main className="p-8">
|
||||||
|
<div className="max-w-4xl mx-auto">
|
||||||
|
<h1 className="text-2xl font-bold text-white mb-4">
|
||||||
|
Example Page Content
|
||||||
|
</h1>
|
||||||
|
<p className="text-white mb-4">
|
||||||
|
This demonstrates how the footer looks in a realistic page context.
|
||||||
|
The footer maintains its responsive behavior while providing
|
||||||
|
navigation and branding information.
|
||||||
|
</p>
|
||||||
|
<div className="grid grid-cols-1 md:grid-cols-2 lg:grid-cols-3 gap-4">
|
||||||
|
{[1, 2, 3, 4, 5, 6].map((i) => (
|
||||||
|
<div
|
||||||
|
key={i}
|
||||||
|
className="bg-[var(--color-surface-default-secondary)] p-4 rounded-lg"
|
||||||
|
>
|
||||||
|
<h3 className="text-white font-semibold mb-2">
|
||||||
|
Content Block {i}
|
||||||
|
</h3>
|
||||||
|
<p className="text-[var(--color-content-default-secondary)] text-sm">
|
||||||
|
This is example content to show how the footer integrates with
|
||||||
|
page content.
|
||||||
|
</p>
|
||||||
|
</div>
|
||||||
|
))}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</main>
|
||||||
|
<Footer />
|
||||||
|
</div>
|
||||||
|
),
|
||||||
|
parameters: {
|
||||||
|
docs: {
|
||||||
|
description: {
|
||||||
|
story:
|
||||||
|
"The footer integrated into a full page layout to show how it works in context.",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
};
|
||||||
@@ -0,0 +1,77 @@
|
|||||||
|
import Header from "../app/components/Header";
|
||||||
|
|
||||||
|
export default {
|
||||||
|
title: "Components/Header",
|
||||||
|
component: Header,
|
||||||
|
parameters: {
|
||||||
|
layout: "fullscreen",
|
||||||
|
docs: {
|
||||||
|
description: {
|
||||||
|
component:
|
||||||
|
"The main navigation header with responsive behavior across different breakpoints.",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
argTypes: {
|
||||||
|
onToggle: { action: "toggled" },
|
||||||
|
},
|
||||||
|
tags: ["autodocs"],
|
||||||
|
};
|
||||||
|
|
||||||
|
export const Default = {
|
||||||
|
args: {},
|
||||||
|
parameters: {
|
||||||
|
docs: {
|
||||||
|
description: {
|
||||||
|
story:
|
||||||
|
"Use the Viewport toolbar to change the iframe width and see how the header adapts to different screen sizes. The header shows different layouts for mobile, tablet, and desktop breakpoints.",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
};
|
||||||
|
|
||||||
|
// Story to show the header in a realistic page context
|
||||||
|
export const InPageContext = {
|
||||||
|
args: {},
|
||||||
|
render: () => (
|
||||||
|
<div className="min-h-screen bg-[var(--color-surface-default-primary)]">
|
||||||
|
<Header />
|
||||||
|
<main className="p-8">
|
||||||
|
<div className="max-w-4xl mx-auto">
|
||||||
|
<h1 className="text-2xl font-bold text-white mb-4">
|
||||||
|
Example Page Content
|
||||||
|
</h1>
|
||||||
|
<p className="text-white mb-4">
|
||||||
|
This demonstrates how the header looks in a realistic page context.
|
||||||
|
The header maintains its responsive behavior while providing
|
||||||
|
navigation for the page content.
|
||||||
|
</p>
|
||||||
|
<div className="grid grid-cols-1 md:grid-cols-2 lg:grid-cols-3 gap-4">
|
||||||
|
{[1, 2, 3, 4, 5, 6].map((i) => (
|
||||||
|
<div
|
||||||
|
key={i}
|
||||||
|
className="bg-[var(--color-surface-default-secondary)] p-4 rounded-lg"
|
||||||
|
>
|
||||||
|
<h3 className="text-white font-semibold mb-2">
|
||||||
|
Content Block {i}
|
||||||
|
</h3>
|
||||||
|
<p className="text-[var(--color-content-default-secondary)] text-sm">
|
||||||
|
This is example content to show how the header integrates with
|
||||||
|
page content.
|
||||||
|
</p>
|
||||||
|
</div>
|
||||||
|
))}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</main>
|
||||||
|
</div>
|
||||||
|
),
|
||||||
|
parameters: {
|
||||||
|
docs: {
|
||||||
|
description: {
|
||||||
|
story:
|
||||||
|
"The header integrated into a full page layout to show how it works in context.",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
};
|
||||||
@@ -0,0 +1,38 @@
|
|||||||
|
import HeaderTab from "../app/components/HeaderTab";
|
||||||
|
import Logo from "../app/components/Logo";
|
||||||
|
|
||||||
|
export default {
|
||||||
|
title: "Components/HeaderTab",
|
||||||
|
component: HeaderTab,
|
||||||
|
parameters: {
|
||||||
|
layout: "centered",
|
||||||
|
docs: {
|
||||||
|
description: {
|
||||||
|
component:
|
||||||
|
"A header tab container with decorative Union images and responsive behavior. Used to wrap content in the header with consistent styling and responsive breakpoint transitions.",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
argTypes: {
|
||||||
|
stretch: {
|
||||||
|
control: { type: "boolean" },
|
||||||
|
description: "Whether the tab should stretch to fill available space",
|
||||||
|
},
|
||||||
|
className: {
|
||||||
|
control: { type: "text" },
|
||||||
|
description: "Additional CSS classes",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
tags: ["autodocs"],
|
||||||
|
};
|
||||||
|
|
||||||
|
export const Default = {
|
||||||
|
args: {
|
||||||
|
stretch: false,
|
||||||
|
},
|
||||||
|
render: (args) => (
|
||||||
|
<HeaderTab {...args}>
|
||||||
|
<Logo size="homeHeaderMd" />
|
||||||
|
</HeaderTab>
|
||||||
|
),
|
||||||
|
};
|
||||||
@@ -0,0 +1,72 @@
|
|||||||
|
import HomeHeader from "../app/components/HomeHeader";
|
||||||
|
|
||||||
|
export default {
|
||||||
|
title: "Components/HomeHeader",
|
||||||
|
component: HomeHeader,
|
||||||
|
parameters: {
|
||||||
|
layout: "fullscreen",
|
||||||
|
docs: {
|
||||||
|
description: {
|
||||||
|
component:
|
||||||
|
"The home page header with transparent background, HeaderTab wrapper, and responsive behavior. Features a toggle to switch between home header and regular header.",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
tags: ["autodocs"],
|
||||||
|
};
|
||||||
|
|
||||||
|
export const Default = {
|
||||||
|
args: {},
|
||||||
|
parameters: {
|
||||||
|
docs: {
|
||||||
|
description: {
|
||||||
|
story:
|
||||||
|
"Use the Viewport toolbar to see how the home header adapts to different screen sizes. The header has a transparent background and uses HeaderTab for the left section.",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
};
|
||||||
|
|
||||||
|
// Story to show the home header in a realistic home page context
|
||||||
|
export const InHomePageContext = {
|
||||||
|
args: {},
|
||||||
|
render: () => (
|
||||||
|
<div className="min-h-screen bg-gradient-to-b from-[var(--color-surface-default-primary)] to-[var(--color-surface-default-secondary)]">
|
||||||
|
<HomeHeader />
|
||||||
|
<main className="p-8">
|
||||||
|
<div className="max-w-4xl mx-auto text-center">
|
||||||
|
<h1 className="text-4xl font-bold text-white mb-4">
|
||||||
|
Welcome to CommunityRule
|
||||||
|
</h1>
|
||||||
|
<p className="text-xl text-[var(--color-content-default-secondary)] mb-8">
|
||||||
|
This demonstrates how the home header looks in a realistic home page
|
||||||
|
context. The header maintains its transparent background and
|
||||||
|
responsive behavior.
|
||||||
|
</p>
|
||||||
|
<div className="grid grid-cols-1 md:grid-cols-2 lg:grid-cols-3 gap-6">
|
||||||
|
{[1, 2, 3].map((i) => (
|
||||||
|
<div
|
||||||
|
key={i}
|
||||||
|
className="bg-[var(--color-surface-default-secondary)] p-6 rounded-lg border border-[var(--border-color-default-tertiary)]"
|
||||||
|
>
|
||||||
|
<h3 className="text-white font-semibold mb-3">Feature {i}</h3>
|
||||||
|
<p className="text-[var(--color-content-default-secondary)]">
|
||||||
|
This is example content to show how the home header integrates
|
||||||
|
with home page content.
|
||||||
|
</p>
|
||||||
|
</div>
|
||||||
|
))}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</main>
|
||||||
|
</div>
|
||||||
|
),
|
||||||
|
parameters: {
|
||||||
|
docs: {
|
||||||
|
description: {
|
||||||
|
story:
|
||||||
|
"The home header integrated into a full home page layout with gradient background to show the transparent header effect.",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
};
|
||||||
@@ -0,0 +1,269 @@
|
|||||||
|
import Logo from "../app/components/Logo";
|
||||||
|
|
||||||
|
export default {
|
||||||
|
title: "Components/Logo",
|
||||||
|
component: Logo,
|
||||||
|
parameters: {
|
||||||
|
layout: "centered",
|
||||||
|
docs: {
|
||||||
|
description: {
|
||||||
|
component:
|
||||||
|
"The CommunityRule logo component with multiple size variants for different contexts (header, footer, home header). Can display with or without text and adapts colors based on context.",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
argTypes: {
|
||||||
|
size: {
|
||||||
|
control: { type: "select" },
|
||||||
|
options: [
|
||||||
|
"default",
|
||||||
|
"homeHeaderXsmall",
|
||||||
|
"homeHeaderSm",
|
||||||
|
"homeHeaderMd",
|
||||||
|
"homeHeaderLg",
|
||||||
|
"homeHeaderXl",
|
||||||
|
"header",
|
||||||
|
"headerMd",
|
||||||
|
"headerLg",
|
||||||
|
"headerXl",
|
||||||
|
"footer",
|
||||||
|
"footerLg",
|
||||||
|
],
|
||||||
|
description: "The size variant of the logo",
|
||||||
|
},
|
||||||
|
showText: {
|
||||||
|
control: { type: "boolean" },
|
||||||
|
description: "Whether to show the text portion of the logo",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
tags: ["autodocs"],
|
||||||
|
};
|
||||||
|
|
||||||
|
export const Default = {
|
||||||
|
args: {
|
||||||
|
size: "default",
|
||||||
|
showText: true,
|
||||||
|
},
|
||||||
|
};
|
||||||
|
|
||||||
|
export const Sizes = {
|
||||||
|
args: {
|
||||||
|
showText: true,
|
||||||
|
},
|
||||||
|
render: (args) => (
|
||||||
|
<div className="space-y-6">
|
||||||
|
<div>
|
||||||
|
<h3 className="text-white font-semibold mb-3">Default Sizes</h3>
|
||||||
|
<div className="space-x-4">
|
||||||
|
<Logo {...args} size="default" />
|
||||||
|
<Logo {...args} size="header" />
|
||||||
|
<Logo {...args} size="footer" />
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div>
|
||||||
|
<h3 className="text-white font-semibold mb-3">Header Sizes</h3>
|
||||||
|
<div className="space-x-4">
|
||||||
|
<Logo {...args} size="header" />
|
||||||
|
<Logo {...args} size="headerMd" />
|
||||||
|
<Logo {...args} size="headerLg" />
|
||||||
|
<Logo {...args} size="headerXl" />
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div>
|
||||||
|
<h3 className="text-white font-semibold mb-3">Home Header Sizes</h3>
|
||||||
|
<div className="space-x-4">
|
||||||
|
<Logo {...args} size="homeHeaderXsmall" />
|
||||||
|
<Logo {...args} size="homeHeaderSm" />
|
||||||
|
<Logo {...args} size="homeHeaderMd" />
|
||||||
|
<Logo {...args} size="homeHeaderLg" />
|
||||||
|
<Logo {...args} size="homeHeaderXl" />
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div>
|
||||||
|
<h3 className="text-white font-semibold mb-3">Footer Sizes</h3>
|
||||||
|
<div className="space-x-4">
|
||||||
|
<Logo {...args} size="footer" />
|
||||||
|
<Logo {...args} size="footerLg" />
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
),
|
||||||
|
parameters: {
|
||||||
|
docs: {
|
||||||
|
description: {
|
||||||
|
story:
|
||||||
|
"Different size variants available for the logo component across different contexts.",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
};
|
||||||
|
|
||||||
|
export const IconOnly = {
|
||||||
|
args: {
|
||||||
|
size: "default",
|
||||||
|
showText: false,
|
||||||
|
},
|
||||||
|
render: (args) => (
|
||||||
|
<div className="space-y-6">
|
||||||
|
<div>
|
||||||
|
<h3 className="text-white font-semibold mb-3">
|
||||||
|
Icon Only - Default Sizes
|
||||||
|
</h3>
|
||||||
|
<div className="space-x-4">
|
||||||
|
<Logo {...args} size="default" />
|
||||||
|
<Logo {...args} size="header" />
|
||||||
|
<Logo {...args} size="footer" />
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div>
|
||||||
|
<h3 className="text-white font-semibold mb-3">
|
||||||
|
Icon Only - Header Sizes
|
||||||
|
</h3>
|
||||||
|
<div className="space-x-4">
|
||||||
|
<Logo {...args} size="header" />
|
||||||
|
<Logo {...args} size="headerMd" />
|
||||||
|
<Logo {...args} size="headerLg" />
|
||||||
|
<Logo {...args} size="headerXl" />
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div>
|
||||||
|
<h3 className="text-white font-semibold mb-3">
|
||||||
|
Icon Only - Home Header Sizes
|
||||||
|
</h3>
|
||||||
|
<div className="space-x-4">
|
||||||
|
<Logo {...args} size="homeHeaderXsmall" />
|
||||||
|
<Logo {...args} size="homeHeaderSm" />
|
||||||
|
<Logo {...args} size="homeHeaderMd" />
|
||||||
|
<Logo {...args} size="homeHeaderLg" />
|
||||||
|
<Logo {...args} size="homeHeaderXl" />
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
),
|
||||||
|
parameters: {
|
||||||
|
docs: {
|
||||||
|
description: {
|
||||||
|
story:
|
||||||
|
"Logo variants with only the icon, no text. Useful for compact spaces.",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
};
|
||||||
|
|
||||||
|
export const HomeHeaderContext = {
|
||||||
|
args: {},
|
||||||
|
render: () => (
|
||||||
|
<div className="min-h-screen bg-gradient-to-b from-[var(--color-surface-default-primary)] to-[var(--color-surface-default-secondary)] p-8">
|
||||||
|
<div className="max-w-4xl mx-auto">
|
||||||
|
<h2 className="text-white font-semibold mb-6">
|
||||||
|
Home Header Context (White Text)
|
||||||
|
</h2>
|
||||||
|
<div className="space-y-4">
|
||||||
|
<div className="flex items-center space-x-4">
|
||||||
|
<span className="text-white text-sm w-24">XSmall:</span>
|
||||||
|
<Logo size="homeHeaderXsmall" />
|
||||||
|
</div>
|
||||||
|
<div className="flex items-center space-x-4">
|
||||||
|
<span className="text-white text-sm w-24">Small:</span>
|
||||||
|
<Logo size="homeHeaderSm" />
|
||||||
|
</div>
|
||||||
|
<div className="flex items-center space-x-4">
|
||||||
|
<span className="text-white text-sm w-24">Medium:</span>
|
||||||
|
<Logo size="homeHeaderMd" />
|
||||||
|
</div>
|
||||||
|
<div className="flex items-center space-x-4">
|
||||||
|
<span className="text-white text-sm w-24">Large:</span>
|
||||||
|
<Logo size="homeHeaderLg" />
|
||||||
|
</div>
|
||||||
|
<div className="flex items-center space-x-4">
|
||||||
|
<span className="text-white text-sm w-24">XLarge:</span>
|
||||||
|
<Logo size="homeHeaderXl" />
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
),
|
||||||
|
parameters: {
|
||||||
|
docs: {
|
||||||
|
description: {
|
||||||
|
story:
|
||||||
|
"Home header context showing white text variants. These are used on dark/transparent backgrounds.",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
};
|
||||||
|
|
||||||
|
export const HeaderContext = {
|
||||||
|
args: {},
|
||||||
|
render: () => (
|
||||||
|
<div className="min-h-screen bg-[var(--color-surface-default-primary)] p-8">
|
||||||
|
<div className="max-w-4xl mx-auto">
|
||||||
|
<h2 className="text-white font-semibold mb-6">
|
||||||
|
Header Context (Dark Text)
|
||||||
|
</h2>
|
||||||
|
<div className="space-y-4">
|
||||||
|
<div className="flex items-center space-x-4">
|
||||||
|
<span className="text-white text-sm w-24">Default:</span>
|
||||||
|
<Logo size="header" />
|
||||||
|
</div>
|
||||||
|
<div className="flex items-center space-x-4">
|
||||||
|
<span className="text-white text-sm w-24">Medium:</span>
|
||||||
|
<Logo size="headerMd" />
|
||||||
|
</div>
|
||||||
|
<div className="flex items-center space-x-4">
|
||||||
|
<span className="text-white text-sm w-24">Large:</span>
|
||||||
|
<Logo size="headerLg" />
|
||||||
|
</div>
|
||||||
|
<div className="flex items-center space-x-4">
|
||||||
|
<span className="text-white text-sm w-24">XLarge:</span>
|
||||||
|
<Logo size="headerXl" />
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
),
|
||||||
|
parameters: {
|
||||||
|
docs: {
|
||||||
|
description: {
|
||||||
|
story:
|
||||||
|
"Header context showing dark text variants. These are used on light backgrounds.",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
};
|
||||||
|
|
||||||
|
export const FooterContext = {
|
||||||
|
args: {},
|
||||||
|
render: () => (
|
||||||
|
<div className="min-h-screen bg-[var(--color-surface-default-primary)] p-8">
|
||||||
|
<div className="max-w-4xl mx-auto">
|
||||||
|
<h2 className="text-white font-semibold mb-6">
|
||||||
|
Footer Context (Larger Sizes)
|
||||||
|
</h2>
|
||||||
|
<div className="space-y-4">
|
||||||
|
<div className="flex items-center space-x-4">
|
||||||
|
<span className="text-white text-sm w-24">Default:</span>
|
||||||
|
<Logo size="footer" />
|
||||||
|
</div>
|
||||||
|
<div className="flex items-center space-x-4">
|
||||||
|
<span className="text-white text-sm w-24">Large:</span>
|
||||||
|
<Logo size="footerLg" />
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
),
|
||||||
|
parameters: {
|
||||||
|
docs: {
|
||||||
|
description: {
|
||||||
|
story:
|
||||||
|
"Footer context showing larger size variants for footer placement.",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
};
|
||||||
@@ -0,0 +1,92 @@
|
|||||||
|
import MenuBar from "../app/components/MenuBar";
|
||||||
|
import MenuBarItem from "../app/components/MenuBarItem";
|
||||||
|
|
||||||
|
export default {
|
||||||
|
title: "Components/MenuBar",
|
||||||
|
component: MenuBar,
|
||||||
|
parameters: {
|
||||||
|
layout: "centered",
|
||||||
|
docs: {
|
||||||
|
description: {
|
||||||
|
component:
|
||||||
|
"A navigation menu bar container that groups MenuBarItem components together. Provides consistent spacing and layout for navigation menus with multiple size variants.",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
argTypes: {
|
||||||
|
size: {
|
||||||
|
control: { type: "select" },
|
||||||
|
options: ["xsmall", "default", "medium", "large"],
|
||||||
|
description: "The size of the menu bar and its children",
|
||||||
|
},
|
||||||
|
className: {
|
||||||
|
control: { type: "text" },
|
||||||
|
description: "Additional CSS classes",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
tags: ["autodocs"],
|
||||||
|
};
|
||||||
|
|
||||||
|
export const Default = {
|
||||||
|
args: {
|
||||||
|
size: "default",
|
||||||
|
},
|
||||||
|
render: (args) => (
|
||||||
|
<MenuBar {...args}>
|
||||||
|
<MenuBarItem size="large">Home</MenuBarItem>
|
||||||
|
<MenuBarItem size="large">About</MenuBarItem>
|
||||||
|
<MenuBarItem size="large">Contact</MenuBarItem>
|
||||||
|
</MenuBar>
|
||||||
|
),
|
||||||
|
};
|
||||||
|
|
||||||
|
export const Sizes = {
|
||||||
|
args: {},
|
||||||
|
render: () => (
|
||||||
|
<div className="space-y-6">
|
||||||
|
<div>
|
||||||
|
<h3 className="text-white font-semibold mb-3">XSmall Size</h3>
|
||||||
|
<MenuBar size="xsmall">
|
||||||
|
<MenuBarItem size="xsmall">Home</MenuBarItem>
|
||||||
|
<MenuBarItem size="xsmall">About</MenuBarItem>
|
||||||
|
<MenuBarItem size="xsmall">Contact</MenuBarItem>
|
||||||
|
</MenuBar>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div>
|
||||||
|
<h3 className="text-white font-semibold mb-3">Default Size</h3>
|
||||||
|
<MenuBar size="default">
|
||||||
|
<MenuBarItem size="large">Home</MenuBarItem>
|
||||||
|
<MenuBarItem size="large">About</MenuBarItem>
|
||||||
|
<MenuBarItem size="large">Contact</MenuBarItem>
|
||||||
|
</MenuBar>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div>
|
||||||
|
<h3 className="text-white font-semibold mb-3">Medium Size</h3>
|
||||||
|
<MenuBar size="medium">
|
||||||
|
<MenuBarItem size="large">Home</MenuBarItem>
|
||||||
|
<MenuBarItem size="large">About</MenuBarItem>
|
||||||
|
<MenuBarItem size="large">Contact</MenuBarItem>
|
||||||
|
</MenuBar>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div>
|
||||||
|
<h3 className="text-white font-semibold mb-3">Large Size</h3>
|
||||||
|
<MenuBar size="large">
|
||||||
|
<MenuBarItem size="large">Home</MenuBarItem>
|
||||||
|
<MenuBarItem size="large">About</MenuBarItem>
|
||||||
|
<MenuBarItem size="large">Contact</MenuBarItem>
|
||||||
|
</MenuBar>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
),
|
||||||
|
parameters: {
|
||||||
|
docs: {
|
||||||
|
description: {
|
||||||
|
story:
|
||||||
|
"Different size variants of the menu bar with consistent spacing and layout.",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
};
|
||||||
@@ -0,0 +1,184 @@
|
|||||||
|
import MenuBarItem from "../app/components/MenuBarItem";
|
||||||
|
|
||||||
|
export default {
|
||||||
|
title: "Components/MenuBarItem",
|
||||||
|
component: MenuBarItem,
|
||||||
|
parameters: {
|
||||||
|
layout: "centered",
|
||||||
|
docs: {
|
||||||
|
description: {
|
||||||
|
component:
|
||||||
|
"A navigation menu item component with multiple variants, sizes, and states. Can render as a link or disabled span with full accessibility support. Includes focus states with keyboard navigation - use Tab key to test focus indicators.",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
argTypes: {
|
||||||
|
variant: {
|
||||||
|
control: { type: "select" },
|
||||||
|
options: ["default", "home"],
|
||||||
|
description: "The visual style variant of the menu item",
|
||||||
|
},
|
||||||
|
size: {
|
||||||
|
control: { type: "select" },
|
||||||
|
options: [
|
||||||
|
"xsmall",
|
||||||
|
"xsmallUseCases",
|
||||||
|
"homeMd",
|
||||||
|
"homeUseCases",
|
||||||
|
"large",
|
||||||
|
"largeUseCases",
|
||||||
|
"homeXlarge",
|
||||||
|
"xlarge",
|
||||||
|
],
|
||||||
|
description: "The size of the menu item",
|
||||||
|
},
|
||||||
|
disabled: {
|
||||||
|
control: { type: "boolean" },
|
||||||
|
description: "Whether the menu item is disabled",
|
||||||
|
},
|
||||||
|
href: {
|
||||||
|
control: { type: "text" },
|
||||||
|
description: "The link destination",
|
||||||
|
},
|
||||||
|
onClick: { action: "clicked" },
|
||||||
|
},
|
||||||
|
tags: ["autodocs"],
|
||||||
|
};
|
||||||
|
|
||||||
|
export const Default = {
|
||||||
|
args: {
|
||||||
|
children: "Menu Item",
|
||||||
|
size: "large",
|
||||||
|
},
|
||||||
|
};
|
||||||
|
|
||||||
|
export const Variants = {
|
||||||
|
args: {
|
||||||
|
children: "Menu Item",
|
||||||
|
size: "large",
|
||||||
|
},
|
||||||
|
render: (args) => (
|
||||||
|
<div className="space-y-4">
|
||||||
|
<div className="space-x-4">
|
||||||
|
<MenuBarItem {...args} variant="default">
|
||||||
|
Default
|
||||||
|
</MenuBarItem>
|
||||||
|
<MenuBarItem {...args} variant="home">
|
||||||
|
Home
|
||||||
|
</MenuBarItem>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
),
|
||||||
|
parameters: {
|
||||||
|
docs: {
|
||||||
|
description: {
|
||||||
|
story: "Different visual variants of the menu item component.",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
};
|
||||||
|
|
||||||
|
export const Sizes = {
|
||||||
|
args: {
|
||||||
|
children: "Menu Item",
|
||||||
|
variant: "default",
|
||||||
|
},
|
||||||
|
render: (args) => (
|
||||||
|
<div className="space-y-4">
|
||||||
|
<div className="space-x-4">
|
||||||
|
<MenuBarItem {...args} size="xsmall">
|
||||||
|
XSmall
|
||||||
|
</MenuBarItem>
|
||||||
|
<MenuBarItem {...args} size="large">
|
||||||
|
Large
|
||||||
|
</MenuBarItem>
|
||||||
|
<MenuBarItem {...args} size="xlarge">
|
||||||
|
XLarge
|
||||||
|
</MenuBarItem>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
),
|
||||||
|
parameters: {
|
||||||
|
docs: {
|
||||||
|
description: {
|
||||||
|
story: "Different sizes available for the menu item component.",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
};
|
||||||
|
|
||||||
|
export const States = {
|
||||||
|
args: {
|
||||||
|
children: "Menu Item",
|
||||||
|
size: "large",
|
||||||
|
variant: "default",
|
||||||
|
},
|
||||||
|
render: (args) => (
|
||||||
|
<div className="space-y-4">
|
||||||
|
<div className="space-x-4">
|
||||||
|
<MenuBarItem {...args}>Normal</MenuBarItem>
|
||||||
|
<MenuBarItem {...args} disabled>
|
||||||
|
Disabled
|
||||||
|
</MenuBarItem>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
),
|
||||||
|
parameters: {
|
||||||
|
docs: {
|
||||||
|
description: {
|
||||||
|
story: "Different states of the menu item component.",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
};
|
||||||
|
|
||||||
|
export const AllVariants = {
|
||||||
|
args: {},
|
||||||
|
render: () => (
|
||||||
|
<div className="space-y-6">
|
||||||
|
<div>
|
||||||
|
<h3 className="text-white font-semibold mb-3">Default Variant</h3>
|
||||||
|
<div className="space-x-4">
|
||||||
|
<MenuBarItem size="xsmall">XSmall</MenuBarItem>
|
||||||
|
<MenuBarItem size="large">Large</MenuBarItem>
|
||||||
|
<MenuBarItem size="xlarge">XLarge</MenuBarItem>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div>
|
||||||
|
<h3 className="text-white font-semibold mb-3">Home Variant</h3>
|
||||||
|
<div className="space-x-4">
|
||||||
|
<MenuBarItem variant="home" size="xsmall">
|
||||||
|
XSmall
|
||||||
|
</MenuBarItem>
|
||||||
|
<MenuBarItem variant="home" size="large">
|
||||||
|
Large
|
||||||
|
</MenuBarItem>
|
||||||
|
<MenuBarItem variant="home" size="xlarge">
|
||||||
|
XLarge
|
||||||
|
</MenuBarItem>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div>
|
||||||
|
<h3 className="text-white font-semibold mb-3">Disabled States</h3>
|
||||||
|
<div className="space-x-4">
|
||||||
|
<MenuBarItem size="large" disabled>
|
||||||
|
Default Disabled
|
||||||
|
</MenuBarItem>
|
||||||
|
<MenuBarItem variant="home" size="large" disabled>
|
||||||
|
Home Disabled
|
||||||
|
</MenuBarItem>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
),
|
||||||
|
parameters: {
|
||||||
|
docs: {
|
||||||
|
description: {
|
||||||
|
story:
|
||||||
|
"Complete overview of all menu item variants, sizes, and states.",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
};
|
||||||
@@ -0,0 +1,35 @@
|
|||||||
|
import path from 'node:path';
|
||||||
|
import { fileURLToPath } from 'node:url';
|
||||||
|
|
||||||
|
import { defineConfig } from 'vitest/config';
|
||||||
|
|
||||||
|
import { storybookTest } from '@storybook/addon-vitest/vitest-plugin';
|
||||||
|
|
||||||
|
const dirname =
|
||||||
|
typeof __dirname !== 'undefined' ? __dirname : path.dirname(fileURLToPath(import.meta.url));
|
||||||
|
|
||||||
|
// More info at: https://storybook.js.org/docs/next/writing-tests/integrations/vitest-addon
|
||||||
|
export default defineConfig({
|
||||||
|
test: {
|
||||||
|
projects: [
|
||||||
|
{
|
||||||
|
extends: true,
|
||||||
|
plugins: [
|
||||||
|
// The plugin will run tests for the stories defined in your Storybook config
|
||||||
|
// See options at: https://storybook.js.org/docs/next/writing-tests/integrations/vitest-addon#storybooktest
|
||||||
|
storybookTest({ configDir: path.join(dirname, '.storybook') }),
|
||||||
|
],
|
||||||
|
test: {
|
||||||
|
name: 'storybook',
|
||||||
|
browser: {
|
||||||
|
enabled: true,
|
||||||
|
headless: true,
|
||||||
|
provider: 'playwright',
|
||||||
|
instances: [{ browser: 'chromium' }]
|
||||||
|
},
|
||||||
|
setupFiles: ['.storybook/vitest.setup.js'],
|
||||||
|
},
|
||||||
|
},
|
||||||
|
],
|
||||||
|
},
|
||||||
|
});
|
||||||
Reference in New Issue
Block a user