feat: add Storybook configuration and mocks
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
parent
17ec53e08f
commit
74ee154669
48
apps/portal/.storybook/main.ts
Normal file
48
apps/portal/.storybook/main.ts
Normal file
@ -0,0 +1,48 @@
|
|||||||
|
import type { StorybookConfig } from "@storybook/react-vite";
|
||||||
|
import tailwindcss from "@tailwindcss/vite";
|
||||||
|
import path from "path";
|
||||||
|
|
||||||
|
const config: StorybookConfig = {
|
||||||
|
stories: ["../src/**/*.stories.@(ts|tsx)"],
|
||||||
|
addons: ["@storybook/addon-essentials"],
|
||||||
|
framework: {
|
||||||
|
name: "@storybook/react-vite",
|
||||||
|
options: {},
|
||||||
|
},
|
||||||
|
staticDirs: ["../public"],
|
||||||
|
viteFinal: async config => {
|
||||||
|
config.plugins = config.plugins || [];
|
||||||
|
config.plugins.push(tailwindcss());
|
||||||
|
|
||||||
|
// Ensure JSX runtime is available (auto-imports React)
|
||||||
|
config.esbuild = {
|
||||||
|
...config.esbuild,
|
||||||
|
jsx: "automatic",
|
||||||
|
};
|
||||||
|
|
||||||
|
config.resolve = config.resolve || {};
|
||||||
|
config.resolve.alias = {
|
||||||
|
...config.resolve.alias,
|
||||||
|
"@": path.resolve(__dirname, "../src"),
|
||||||
|
"next/link": path.resolve(__dirname, "mocks/next-link.tsx"),
|
||||||
|
"next/image": path.resolve(__dirname, "mocks/next-image.tsx"),
|
||||||
|
"next/navigation": path.resolve(__dirname, "mocks/next-navigation.tsx"),
|
||||||
|
};
|
||||||
|
|
||||||
|
// Disable PostCSS — @tailwindcss/vite handles CSS directly
|
||||||
|
config.css = config.css || {};
|
||||||
|
config.css.postcss = { plugins: [] };
|
||||||
|
|
||||||
|
// Polyfill process.env for Next.js code that uses it
|
||||||
|
config.define = {
|
||||||
|
...config.define,
|
||||||
|
"process.env": JSON.stringify({
|
||||||
|
NODE_ENV: "development",
|
||||||
|
}),
|
||||||
|
};
|
||||||
|
|
||||||
|
return config;
|
||||||
|
},
|
||||||
|
};
|
||||||
|
|
||||||
|
export default config;
|
||||||
24
apps/portal/.storybook/mocks/next-image.tsx
Normal file
24
apps/portal/.storybook/mocks/next-image.tsx
Normal file
@ -0,0 +1,24 @@
|
|||||||
|
import React from "react";
|
||||||
|
|
||||||
|
interface ImageProps extends React.ImgHTMLAttributes<HTMLImageElement> {
|
||||||
|
src: string;
|
||||||
|
alt: string;
|
||||||
|
width?: number;
|
||||||
|
height?: number;
|
||||||
|
fill?: boolean;
|
||||||
|
priority?: boolean;
|
||||||
|
}
|
||||||
|
|
||||||
|
const Image = ({ src, alt, width, height, fill, priority: _priority, ...props }: ImageProps) => (
|
||||||
|
// eslint-disable-next-line @next/next/no-img-element
|
||||||
|
<img
|
||||||
|
src={src}
|
||||||
|
alt={alt}
|
||||||
|
width={width}
|
||||||
|
height={height}
|
||||||
|
style={fill ? { objectFit: "cover", width: "100%", height: "100%" } : undefined}
|
||||||
|
{...props}
|
||||||
|
/>
|
||||||
|
);
|
||||||
|
|
||||||
|
export default Image;
|
||||||
13
apps/portal/.storybook/mocks/next-link.tsx
Normal file
13
apps/portal/.storybook/mocks/next-link.tsx
Normal file
@ -0,0 +1,13 @@
|
|||||||
|
import React from "react";
|
||||||
|
|
||||||
|
const Link = React.forwardRef<
|
||||||
|
HTMLAnchorElement,
|
||||||
|
React.AnchorHTMLAttributes<HTMLAnchorElement> & { href: string }
|
||||||
|
>(({ href, children, ...props }, ref) => (
|
||||||
|
<a ref={ref} href={href} {...props}>
|
||||||
|
{children}
|
||||||
|
</a>
|
||||||
|
));
|
||||||
|
Link.displayName = "Link";
|
||||||
|
|
||||||
|
export default Link;
|
||||||
30
apps/portal/.storybook/mocks/next-navigation.tsx
Normal file
30
apps/portal/.storybook/mocks/next-navigation.tsx
Normal file
@ -0,0 +1,30 @@
|
|||||||
|
export function useRouter() {
|
||||||
|
return {
|
||||||
|
push: (url: string) => {
|
||||||
|
window.location.hash = url;
|
||||||
|
},
|
||||||
|
replace: (url: string) => {
|
||||||
|
window.location.hash = url;
|
||||||
|
},
|
||||||
|
back: () => {
|
||||||
|
window.history.back();
|
||||||
|
},
|
||||||
|
forward: () => {
|
||||||
|
window.history.forward();
|
||||||
|
},
|
||||||
|
refresh: () => {},
|
||||||
|
prefetch: () => Promise.resolve(),
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
export function usePathname() {
|
||||||
|
return "/";
|
||||||
|
}
|
||||||
|
|
||||||
|
export function useSearchParams() {
|
||||||
|
return new URLSearchParams();
|
||||||
|
}
|
||||||
|
|
||||||
|
export function useParams() {
|
||||||
|
return {};
|
||||||
|
}
|
||||||
18
apps/portal/.storybook/preview.ts
Normal file
18
apps/portal/.storybook/preview.ts
Normal file
@ -0,0 +1,18 @@
|
|||||||
|
import type { Preview } from "@storybook/react";
|
||||||
|
import "../src/app/globals.css";
|
||||||
|
|
||||||
|
const preview: Preview = {
|
||||||
|
parameters: {
|
||||||
|
layout: "centered",
|
||||||
|
backgrounds: {
|
||||||
|
default: "light",
|
||||||
|
values: [
|
||||||
|
{ name: "light", value: "#f9f9f9" },
|
||||||
|
{ name: "white", value: "#ffffff" },
|
||||||
|
{ name: "dark", value: "#1a1a2e" },
|
||||||
|
],
|
||||||
|
},
|
||||||
|
},
|
||||||
|
};
|
||||||
|
|
||||||
|
export default preview;
|
||||||
Loading…
x
Reference in New Issue
Block a user