feat: add basic setup for storybook
This commit is contained in:
parent
20476b8687
commit
f7013ae546
14
packages/react/.eslintrc.cjs
Normal file
14
packages/react/.eslintrc.cjs
Normal file
@ -0,0 +1,14 @@
|
||||
module.exports = {
|
||||
root: true,
|
||||
env: { browser: true, es2020: true },
|
||||
extends: ['eslint:recommended', 'plugin:@typescript-eslint/recommended', 'plugin:react-hooks/recommended', 'plugin:storybook/recommended'],
|
||||
ignorePatterns: ['dist', '.eslintrc.cjs'],
|
||||
parser: '@typescript-eslint/parser',
|
||||
plugins: ['react-refresh'],
|
||||
rules: {
|
||||
'react-refresh/only-export-components': [
|
||||
'warn',
|
||||
{ allowConstantExport: true },
|
||||
],
|
||||
},
|
||||
}
|
26
packages/react/.gitignore
vendored
Normal file
26
packages/react/.gitignore
vendored
Normal file
@ -0,0 +1,26 @@
|
||||
# Logs
|
||||
logs
|
||||
*.log
|
||||
npm-debug.log*
|
||||
yarn-debug.log*
|
||||
yarn-error.log*
|
||||
pnpm-debug.log*
|
||||
lerna-debug.log*
|
||||
|
||||
node_modules
|
||||
dist
|
||||
dist-ssr
|
||||
*.local
|
||||
|
||||
# Editor directories and files
|
||||
.vscode/*
|
||||
!.vscode/extensions.json
|
||||
.idea
|
||||
.DS_Store
|
||||
*.suo
|
||||
*.ntvs*
|
||||
*.njsproj
|
||||
*.sln
|
||||
*.sw?
|
||||
|
||||
*storybook.log
|
30
packages/react/.storybook/main.ts
Normal file
30
packages/react/.storybook/main.ts
Normal file
@ -0,0 +1,30 @@
|
||||
import type { StorybookConfig } from "@storybook/react-vite";
|
||||
|
||||
import { join, dirname } from "path";
|
||||
|
||||
/**
|
||||
* This function is used to resolve the absolute path of a package.
|
||||
* It is needed in projects that use Yarn PnP or are set up within a monorepo.
|
||||
*/
|
||||
function getAbsolutePath(value: string): any {
|
||||
return dirname(require.resolve(join(value, "package.json")));
|
||||
}
|
||||
const config: StorybookConfig = {
|
||||
stories: [
|
||||
"../stories/**/*.mdx",
|
||||
"../stories/**/*.stories.@(js|jsx|mjs|ts|tsx)",
|
||||
],
|
||||
addons: [
|
||||
getAbsolutePath("@storybook/addon-onboarding"),
|
||||
getAbsolutePath("@storybook/addon-links"),
|
||||
getAbsolutePath("@storybook/addon-essentials"),
|
||||
getAbsolutePath("@chromatic-com/storybook"),
|
||||
getAbsolutePath("@storybook/addon-interactions"),
|
||||
"@storybook/addon-themes"
|
||||
],
|
||||
framework: {
|
||||
name: getAbsolutePath("@storybook/react-vite"),
|
||||
options: {},
|
||||
},
|
||||
};
|
||||
export default config;
|
105
packages/react/.storybook/preview.tsx
Normal file
105
packages/react/.storybook/preview.tsx
Normal file
@ -0,0 +1,105 @@
|
||||
import React from "react";
|
||||
import type { Preview } from "@storybook/react";
|
||||
import type { DecoratorFunction, Renderer } from "@storybook/types";
|
||||
import { useEffect, addons, useParameter } from "@storybook/preview-api";
|
||||
import "../src/tailwind.css";
|
||||
import {
|
||||
Controls,
|
||||
Description,
|
||||
Primary,
|
||||
Stories,
|
||||
Subtitle,
|
||||
Title,
|
||||
} from "@storybook/blocks";
|
||||
|
||||
const classStringToArray = (classString: string) =>
|
||||
classString.split(" ").filter(Boolean);
|
||||
|
||||
const withThemeByClassName = <TRenderer extends Renderer = any>(
|
||||
themes: Record<string, string>,
|
||||
defaultTheme: string,
|
||||
parentSelector: string
|
||||
): DecoratorFunction<TRenderer> => {
|
||||
addons.getChannel().emit("storybook/themes/REGISTER_THEMES", {
|
||||
defaultTheme,
|
||||
themes: Object.keys(themes),
|
||||
});
|
||||
|
||||
return (storyFn, context) => {
|
||||
const { themeOverride } = useParameter<{ themeOverride?: string }>(
|
||||
"themes",
|
||||
{}
|
||||
) as { themeOverride?: string };
|
||||
const selected = context.globals["theme"] || "";
|
||||
|
||||
useEffect(() => {
|
||||
const selectedThemeName = themeOverride || selected || defaultTheme;
|
||||
const parentElement = document.querySelectorAll(parentSelector);
|
||||
|
||||
if (!parentElement) {
|
||||
return;
|
||||
}
|
||||
|
||||
Object.entries(themes)
|
||||
.filter(([themeName]) => themeName !== selectedThemeName)
|
||||
.forEach(([_, className]) => {
|
||||
const classes = classStringToArray(className);
|
||||
if (classes.length > 0) {
|
||||
parentElement.forEach((element) =>
|
||||
element.classList.remove(...classes)
|
||||
);
|
||||
}
|
||||
});
|
||||
|
||||
const newThemeClasses = classStringToArray(themes[selectedThemeName]);
|
||||
|
||||
if (newThemeClasses.length > 0) {
|
||||
parentElement.forEach((element) => {
|
||||
console.log(element);
|
||||
element.classList.add(...newThemeClasses);
|
||||
});
|
||||
}
|
||||
}, [themeOverride, selected, parentSelector]);
|
||||
|
||||
return storyFn();
|
||||
};
|
||||
};
|
||||
|
||||
export const decorators = [
|
||||
withThemeByClassName(
|
||||
{
|
||||
light: "light",
|
||||
dark: "dark",
|
||||
},
|
||||
"dark",
|
||||
"html,.sbdocs.sbdocs-preview"
|
||||
),
|
||||
];
|
||||
|
||||
const autoDocsTemplate = () => (
|
||||
<>
|
||||
<Title />
|
||||
<Subtitle />
|
||||
<Description />
|
||||
<Primary />
|
||||
<Controls />
|
||||
<Stories />
|
||||
</>
|
||||
);
|
||||
|
||||
const preview: Preview = {
|
||||
parameters: {
|
||||
/*controls: {
|
||||
matchers: {
|
||||
color: /(background|color)$/i,
|
||||
date: /Date$/i,
|
||||
},
|
||||
},*/
|
||||
docs: {
|
||||
page: autoDocsTemplate,
|
||||
},
|
||||
},
|
||||
tags: ["autodocs"],
|
||||
};
|
||||
|
||||
export default preview;
|
46
packages/react/package.json
Normal file
46
packages/react/package.json
Normal file
@ -0,0 +1,46 @@
|
||||
{
|
||||
"name": "react",
|
||||
"private": true,
|
||||
"version": "0.0.0",
|
||||
"type": "module",
|
||||
"scripts": {
|
||||
"dev": "vite",
|
||||
"build": "tsc && vite build",
|
||||
"lint": "eslint . --ext ts,tsx --report-unused-disable-directives --max-warnings 0",
|
||||
"preview": "vite preview",
|
||||
"storybook": "storybook dev -p 6006",
|
||||
"build-storybook": "storybook build"
|
||||
},
|
||||
"dependencies": {
|
||||
"react": "^18.2.0",
|
||||
"react-dom": "^18.2.0",
|
||||
"tailwind-merge": "^2.3.0"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@chromatic-com/storybook": "^1.3.5",
|
||||
"@storybook/addon-essentials": "^8.1.0",
|
||||
"@storybook/addon-interactions": "^8.1.0",
|
||||
"@storybook/addon-links": "^8.1.0",
|
||||
"@storybook/addon-onboarding": "^8.1.0",
|
||||
"@storybook/addon-themes": "^8.1.0",
|
||||
"@storybook/blocks": "^8.1.0",
|
||||
"@storybook/react": "^8.1.0",
|
||||
"@storybook/react-vite": "^8.1.0",
|
||||
"@storybook/test": "^8.1.0",
|
||||
"@types/react": "^18.2.66",
|
||||
"@types/react-dom": "^18.2.22",
|
||||
"@typescript-eslint/eslint-plugin": "^7.2.0",
|
||||
"@typescript-eslint/parser": "^7.2.0",
|
||||
"@vitejs/plugin-react": "^4.2.1",
|
||||
"autoprefixer": "^10.4.19",
|
||||
"eslint": "^8.57.0",
|
||||
"eslint-plugin-react-hooks": "^4.6.0",
|
||||
"eslint-plugin-react-refresh": "^0.4.6",
|
||||
"eslint-plugin-storybook": "^0.8.0",
|
||||
"postcss": "^8.4.38",
|
||||
"storybook": "^8.1.0",
|
||||
"tailwindcss": "^3.4.3",
|
||||
"typescript": "^5.2.2",
|
||||
"vite": "^5.2.0"
|
||||
}
|
||||
}
|
35
packages/react/shared.ts
Normal file
35
packages/react/shared.ts
Normal file
@ -0,0 +1,35 @@
|
||||
import { twMerge } from "tailwind-merge";
|
||||
|
||||
export function vcn<V extends Record<string, Record<string, string>>>({
|
||||
base,
|
||||
variants,
|
||||
defaults,
|
||||
}: {
|
||||
base: string;
|
||||
variants: V;
|
||||
defaults: { [VariantKey in keyof V]: keyof V[VariantKey] };
|
||||
}): (
|
||||
variantProps: Partial<typeof defaults> & { className?: string }
|
||||
) => string {
|
||||
return ({ className, ...variantProps }) => {
|
||||
return twMerge(
|
||||
base,
|
||||
...(
|
||||
Object.entries(defaults) as [keyof V, keyof V[keyof V]][]
|
||||
).map<string>(
|
||||
([variantKey, defaultValue]) =>
|
||||
variants[variantKey][
|
||||
(variantProps as unknown as Partial<typeof defaults>)[variantKey] ??
|
||||
defaultValue
|
||||
]
|
||||
),
|
||||
className
|
||||
);
|
||||
};
|
||||
}
|
||||
|
||||
export type VariantProps<F extends ReturnType<typeof vcn>> = F extends (
|
||||
props: infer P
|
||||
) => string
|
||||
? P
|
||||
: never;
|
37
packages/react/src/tailwind.css
Normal file
37
packages/react/src/tailwind.css
Normal file
@ -0,0 +1,37 @@
|
||||
@tailwind base;
|
||||
@tailwind components;
|
||||
@tailwind utilities;
|
||||
|
||||
@layer base {
|
||||
:root {
|
||||
--dark-bg-color: #000;
|
||||
--dark-text-color: #fff;
|
||||
--dark-btn-bg-color: #2f2f2f;
|
||||
--dark-btn-text-color: #fff;
|
||||
--dark-code-bg-color: #1f1f1f;
|
||||
}
|
||||
|
||||
body {
|
||||
@apply bg-white dark:bg-[var(--dark-bg-color)] text-black dark:text-[var(--dark-text-color)] transition-colors duration-300;
|
||||
}
|
||||
.sbdocs.sbdocs-preview.dark {
|
||||
@apply dark:bg-[var(--dark-bg-color)] dark:text-[var(--dark-text-color)] transition-colors duration-300;
|
||||
|
||||
& .sb-bar {
|
||||
@apply dark:bg-[var(--dark-bg-color)] dark:text-[var(--dark-text-color)];
|
||||
}
|
||||
|
||||
& .sb-bar button, .docs-story > div:nth-child(2) > button, .docs-story + div > div:nth-child(2) > button {
|
||||
@apply dark:bg-[var(--dark-btn-bg-color)] dark:text-[var(--dark-btn-text-color)];
|
||||
}
|
||||
|
||||
& .prismjs {
|
||||
@apply pb-8;
|
||||
|
||||
& > div {
|
||||
@apply dark:bg-[var(--dark-code-bg-color)] rounded-md p-4;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
9
packages/react/tailwind.config.js
Normal file
9
packages/react/tailwind.config.js
Normal file
@ -0,0 +1,9 @@
|
||||
/** @type {import('tailwindcss').Config} */
|
||||
module.exports = {
|
||||
content: ["./{components,stories,src}/**/*.{js,jsx,ts,tsx,css}"],
|
||||
darkMode: "selector",
|
||||
theme: {
|
||||
extend: {},
|
||||
},
|
||||
plugins: [],
|
||||
};
|
25
packages/react/tsconfig.json
Normal file
25
packages/react/tsconfig.json
Normal file
@ -0,0 +1,25 @@
|
||||
{
|
||||
"compilerOptions": {
|
||||
"target": "ES2020",
|
||||
"useDefineForClassFields": true,
|
||||
"lib": ["ES2020", "DOM", "DOM.Iterable"],
|
||||
"module": "ESNext",
|
||||
"skipLibCheck": true,
|
||||
|
||||
/* Bundler mode */
|
||||
"moduleResolution": "bundler",
|
||||
"allowImportingTsExtensions": true,
|
||||
"resolveJsonModule": true,
|
||||
"isolatedModules": true,
|
||||
"noEmit": true,
|
||||
"jsx": "react-jsx",
|
||||
|
||||
/* Linting */
|
||||
"strict": true,
|
||||
"noUnusedLocals": true,
|
||||
"noUnusedParameters": true,
|
||||
"noFallthroughCasesInSwitch": true
|
||||
},
|
||||
"include": ["components", "stories", "./stories.ts", "src"],
|
||||
"references": [{ "path": "./tsconfig.node.json" }]
|
||||
}
|
11
packages/react/tsconfig.node.json
Normal file
11
packages/react/tsconfig.node.json
Normal file
@ -0,0 +1,11 @@
|
||||
{
|
||||
"compilerOptions": {
|
||||
"composite": true,
|
||||
"skipLibCheck": true,
|
||||
"module": "ESNext",
|
||||
"moduleResolution": "bundler",
|
||||
"allowSyntheticDefaultImports": true,
|
||||
"strict": true
|
||||
},
|
||||
"include": ["vite.config.ts"]
|
||||
}
|
13
packages/react/vite.config.ts
Normal file
13
packages/react/vite.config.ts
Normal file
@ -0,0 +1,13 @@
|
||||
import { defineConfig } from "vite";
|
||||
import react from "@vitejs/plugin-react";
|
||||
import tailwindcss from "tailwindcss";
|
||||
|
||||
// https://vitejs.dev/config/
|
||||
export default defineConfig({
|
||||
plugins: [react()],
|
||||
css: {
|
||||
postcss: {
|
||||
plugins: [tailwindcss()],
|
||||
},
|
||||
},
|
||||
});
|
Loading…
x
Reference in New Issue
Block a user