chore: catch up Storybook preview, coverage, and story hygiene
Stories were throwing without AuthModal/CreateFlow providers, autodocs was a no-op, and several DS/create-flow screens had no stories.
This commit is contained in:
+22
-16
@@ -28,16 +28,17 @@ Do **not** colocate `*.stories.*` next to components. The Storybook config
|
|||||||
|
|
||||||
# File naming
|
# File naming
|
||||||
|
|
||||||
- `<ComponentName>.stories.js` — matches 69/70 existing files.
|
- `<ComponentName>.stories.js` — default; keep `.js` unless the story
|
||||||
- Use `.tsx` only when the story genuinely needs types (rare; prefer JS to
|
genuinely needs types.
|
||||||
match the codebase convention).
|
- Use `.tsx` only when the story uses `Meta` / `StoryObj` (rare).
|
||||||
- Variants get a suffix: `Button.visual.stories.js`,
|
- Variants get a suffix: `Button.visual.stories.js`,
|
||||||
`Footer.responsive.stories.js`.
|
`Footer.responsive.stories.js`.
|
||||||
|
|
||||||
# Default export shape (CSF2)
|
# Default export shape (CSF3)
|
||||||
|
|
||||||
```javascript
|
```javascript
|
||||||
import MyComponent from "../../app/components/<area>/MyComponent";
|
import MyComponent from "../../app/components/<area>/MyComponent";
|
||||||
|
import { CHIP_PALETTE_OPTIONS } from "../../lib/propNormalization";
|
||||||
|
|
||||||
export default {
|
export default {
|
||||||
title: "Components/<SubFolder>/MyComponent",
|
title: "Components/<SubFolder>/MyComponent",
|
||||||
@@ -51,31 +52,32 @@ export default {
|
|||||||
},
|
},
|
||||||
},
|
},
|
||||||
argTypes: {
|
argTypes: {
|
||||||
variant: {
|
palette: {
|
||||||
control: { type: "select" },
|
control: { type: "select" },
|
||||||
options: ["filled", "outline"],
|
options: [...CHIP_PALETTE_OPTIONS],
|
||||||
description: "The variant (Figma prop)",
|
description: "The palette (Figma prop)",
|
||||||
},
|
},
|
||||||
onClick: { action: "clicked" },
|
onClick: { action: "clicked" },
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
|
|
||||||
export const Default = { args: { variant: "filled" } };
|
export const Default = { args: { palette: "default" } };
|
||||||
```
|
```
|
||||||
|
|
||||||
## Title hierarchy
|
## Title hierarchy
|
||||||
|
|
||||||
- Design-system components → `Components/<SubFolder>/<Name>` (e.g.
|
- Design-system components → `Components/<SubFolder>/<Name>` (e.g.
|
||||||
`Components/Controls/Checkbox`).
|
`Components/Controls/Checkbox`).
|
||||||
- Pages → `Pages/<PageName>` (folder: `stories/pages/`).
|
- Create-flow screens → `Pages/Create Flow/<Step>` (folder: `stories/pages/`).
|
||||||
- Create flow shared pieces → `Create Flow/<Name>`.
|
- Create flow shared pieces → `Create Flow/<Name>`.
|
||||||
|
|
||||||
## `argTypes`
|
## `argTypes`
|
||||||
|
|
||||||
For every Figma enum prop (`variant`, `size`, `state`, `mode`, `palette`,
|
For every Figma enum prop (`variant`, `size`, `state`, `mode`, `palette`,
|
||||||
…) expose a `select` control listing the **lowercase** option set, sourced
|
…) expose a `select` control listing the option set, sourced from the matching
|
||||||
from the matching `*_OPTIONS` const in `lib/propNormalization.ts`. See
|
`*_OPTIONS` const in `lib/propNormalization.ts`. See
|
||||||
`.cursor/rules/component-props.mdc`.
|
`.cursor/rules/component-props.mdc`. Spread with `[...FOO_OPTIONS]` so the
|
||||||
|
control receives a mutable array.
|
||||||
|
|
||||||
# Rely on the global preview — don't re-wrap
|
# Rely on the global preview — don't re-wrap
|
||||||
|
|
||||||
@@ -83,8 +85,12 @@ from the matching `*_OPTIONS` const in `lib/propNormalization.ts`. See
|
|||||||
|
|
||||||
- `MessagesProvider` with `messages/en` → access copy via `useMessages()`
|
- `MessagesProvider` with `messages/en` → access copy via `useMessages()`
|
||||||
inside stories exactly like app code. Never hard-code user-facing strings.
|
inside stories exactly like app code. Never hard-code user-facing strings.
|
||||||
- `app/globals.css` + `.font-inter` wrapper → design tokens and fonts are
|
- `AuthModalProvider` and `CreateFlowProvider` (same stack as
|
||||||
already present.
|
`tests/utils/test-utils.tsx`) so `Top` and create-flow screens can mount.
|
||||||
|
- `app/globals.css` + Inter / Bricolage Grotesque / Space Grotesk CSS
|
||||||
|
variables + `.font-inter` wrapper.
|
||||||
|
- Dark canvas default and Figma breakpoints (`sm` 430, `md` 640, `lg` 1024,
|
||||||
|
`xl` 1440).
|
||||||
|
|
||||||
Do **not** add your own `MessagesProvider`, font wrapper, or token setup in a
|
Do **not** add your own `MessagesProvider`, font wrapper, or token setup in a
|
||||||
story. If you need a new global, update `preview.js`.
|
story. If you need a new global, update `preview.js`.
|
||||||
@@ -92,8 +98,8 @@ story. If you need a new global, update `preview.js`.
|
|||||||
# Interaction tests (`play`)
|
# Interaction tests (`play`)
|
||||||
|
|
||||||
Use `storybook/test` for interaction assertions — not `@testing-library/*`
|
Use `storybook/test` for interaction assertions — not `@testing-library/*`
|
||||||
directly. This matches `Checkbox.stories.js` and stays compatible with the
|
directly. This matches `Checkbox.stories.js`. Storybook is documentation;
|
||||||
Vitest portable-stories runner in `.storybook/vitest.setup.js`.
|
Vitest component tests remain the source of truth.
|
||||||
|
|
||||||
```javascript
|
```javascript
|
||||||
import { within, userEvent, expect } from "storybook/test";
|
import { within, userEvent, expect } from "storybook/test";
|
||||||
|
|||||||
@@ -1,7 +1,13 @@
|
|||||||
@import url("https://fonts.googleapis.com/css2?family=Inter:wght@400;500;600;700&display=swap");
|
@import url("https://fonts.googleapis.com/css2?family=Inter:wght@400;500;600;700&family=Bricolage+Grotesque:wght@400;500;700;800&family=Space+Grotesk:wght@400;500;700&display=swap");
|
||||||
|
|
||||||
:root {
|
:root {
|
||||||
--font-inter:
|
--font-inter:
|
||||||
"Inter", ui-sans-serif, system-ui, -apple-system, "Segoe UI", Roboto,
|
"Inter", ui-sans-serif, system-ui, -apple-system, "Segoe UI", Roboto,
|
||||||
"Helvetica Neue", Arial, "Noto Sans", "Apple Color Emoji", "Segoe UI Emoji";
|
"Helvetica Neue", Arial, "Noto Sans", "Apple Color Emoji", "Segoe UI Emoji";
|
||||||
|
--font-bricolage-grotesque:
|
||||||
|
"Bricolage Grotesque", ui-sans-serif, system-ui, -apple-system, "Segoe UI",
|
||||||
|
Roboto, "Helvetica Neue", Arial;
|
||||||
|
--font-space-grotesk:
|
||||||
|
"Space Grotesk", ui-sans-serif, system-ui, -apple-system, "Segoe UI",
|
||||||
|
Roboto, "Helvetica Neue", Arial;
|
||||||
}
|
}
|
||||||
|
|||||||
+1
-23
@@ -4,32 +4,10 @@ module.exports = {
|
|||||||
"../stories/**/*.mdx",
|
"../stories/**/*.mdx",
|
||||||
"../stories/**/*.stories.@(js|jsx|mjs|ts|tsx)",
|
"../stories/**/*.stories.@(js|jsx|mjs|ts|tsx)",
|
||||||
],
|
],
|
||||||
addons: [
|
addons: ["@storybook/addon-docs", "@storybook/addon-a11y"],
|
||||||
// Removed @storybook/addon-essentials due to version mismatch with Storybook 10.x
|
|
||||||
// Using individual addons instead. Interaction helpers import from storybook/test
|
|
||||||
// (bundled with storybook@10); @storybook/addon-interactions was merged into SB 8 core.
|
|
||||||
"@storybook/addon-a11y",
|
|
||||||
],
|
|
||||||
framework: {
|
framework: {
|
||||||
name: "@storybook/nextjs",
|
name: "@storybook/nextjs",
|
||||||
options: {},
|
options: {},
|
||||||
},
|
},
|
||||||
staticDirs: ["../public"],
|
staticDirs: ["../public"],
|
||||||
|
|
||||||
// Webpack configuration to resolve Next.js modules for Next.js 16 compatibility
|
|
||||||
async webpackFinal(config) {
|
|
||||||
// Ensure Next.js modules are resolved correctly
|
|
||||||
config.resolve = config.resolve || {};
|
|
||||||
config.resolve.alias = {
|
|
||||||
...(config.resolve.alias || {}),
|
|
||||||
};
|
|
||||||
|
|
||||||
// Ensure node_modules are resolved
|
|
||||||
config.resolve.modules = [
|
|
||||||
...(config.resolve.modules || []),
|
|
||||||
"node_modules",
|
|
||||||
];
|
|
||||||
|
|
||||||
return config;
|
|
||||||
},
|
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -1,10 +1,43 @@
|
|||||||
import "../app/globals.css";
|
import "../app/globals.css";
|
||||||
import "./fonts.css";
|
import "./fonts.css";
|
||||||
|
import { MINIMAL_VIEWPORTS } from "storybook/viewport";
|
||||||
|
import { AuthModalProvider } from "../app/contexts/AuthModalContext";
|
||||||
import { MessagesProvider } from "../app/contexts/MessagesContext";
|
import { MessagesProvider } from "../app/contexts/MessagesContext";
|
||||||
|
import { CreateFlowProvider } from "../app/(app)/create/context/CreateFlowContext";
|
||||||
import messages from "../messages/en/index";
|
import messages from "../messages/en/index";
|
||||||
|
|
||||||
|
/** Figma / Tailwind breakpoints from `app/tailwind.css`. */
|
||||||
|
const dsViewports = {
|
||||||
|
sm: {
|
||||||
|
name: "sm 430",
|
||||||
|
styles: { width: "430px", height: "932px" },
|
||||||
|
},
|
||||||
|
md: {
|
||||||
|
name: "md 640",
|
||||||
|
styles: { width: "640px", height: "1024px" },
|
||||||
|
},
|
||||||
|
lg: {
|
||||||
|
name: "lg 1024",
|
||||||
|
styles: { width: "1024px", height: "768px" },
|
||||||
|
},
|
||||||
|
xl: {
|
||||||
|
name: "xl 1440",
|
||||||
|
styles: { width: "1440px", height: "900px" },
|
||||||
|
},
|
||||||
|
// Aliases used by existing page stories
|
||||||
|
mobile1: {
|
||||||
|
name: "sm 430",
|
||||||
|
styles: { width: "430px", height: "932px" },
|
||||||
|
},
|
||||||
|
desktop: {
|
||||||
|
name: "xl 1440",
|
||||||
|
styles: { width: "1440px", height: "900px" },
|
||||||
|
},
|
||||||
|
};
|
||||||
|
|
||||||
/** @type { import('@storybook/react').Preview } */
|
/** @type { import('@storybook/react').Preview } */
|
||||||
const preview = {
|
const preview = {
|
||||||
|
tags: ["autodocs"],
|
||||||
parameters: {
|
parameters: {
|
||||||
controls: {
|
controls: {
|
||||||
matchers: {
|
matchers: {
|
||||||
@@ -12,13 +45,32 @@ const preview = {
|
|||||||
date: /Date$/i,
|
date: /Date$/i,
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
|
backgrounds: {
|
||||||
|
options: {
|
||||||
|
dark: { name: "Dark", value: "#000000" },
|
||||||
|
light: { name: "Light", value: "#ffffff" },
|
||||||
|
},
|
||||||
|
},
|
||||||
|
viewport: {
|
||||||
|
options: {
|
||||||
|
...MINIMAL_VIEWPORTS,
|
||||||
|
...dsViewports,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
initialGlobals: {
|
||||||
|
backgrounds: { value: "dark" },
|
||||||
},
|
},
|
||||||
decorators: [
|
decorators: [
|
||||||
(Story) => (
|
(Story) => (
|
||||||
<MessagesProvider messages={messages}>
|
<MessagesProvider messages={messages}>
|
||||||
|
<AuthModalProvider>
|
||||||
|
<CreateFlowProvider>
|
||||||
<div className="font-inter">
|
<div className="font-inter">
|
||||||
<Story />
|
<Story />
|
||||||
</div>
|
</div>
|
||||||
|
</CreateFlowProvider>
|
||||||
|
</AuthModalProvider>
|
||||||
</MessagesProvider>
|
</MessagesProvider>
|
||||||
),
|
),
|
||||||
],
|
],
|
||||||
|
|||||||
@@ -1,7 +0,0 @@
|
|||||||
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]);
|
|
||||||
@@ -25,7 +25,6 @@
|
|||||||
"@types/mdx",
|
"@types/mdx",
|
||||||
"eslint-config-next",
|
"eslint-config-next",
|
||||||
"typescript-eslint",
|
"typescript-eslint",
|
||||||
"@storybook/nextjs-vite",
|
|
||||||
"@eslint/js",
|
"@eslint/js",
|
||||||
"@next/eslint-plugin-next",
|
"@next/eslint-plugin-next",
|
||||||
"eslint-plugin-react",
|
"eslint-plugin-react",
|
||||||
|
|||||||
Generated
+82
-1
@@ -30,6 +30,7 @@
|
|||||||
"@lhci/cli": "^0.15.1",
|
"@lhci/cli": "^0.15.1",
|
||||||
"@playwright/test": "^1.55.0",
|
"@playwright/test": "^1.55.0",
|
||||||
"@storybook/addon-a11y": "^10.2.0",
|
"@storybook/addon-a11y": "^10.2.0",
|
||||||
|
"@storybook/addon-docs": "^10.2.0",
|
||||||
"@storybook/nextjs": "^10.2.0",
|
"@storybook/nextjs": "^10.2.0",
|
||||||
"@storybook/react": "^10.2.0",
|
"@storybook/react": "^10.2.0",
|
||||||
"@svgr/webpack": "^8.1.0",
|
"@svgr/webpack": "^8.1.0",
|
||||||
@@ -5911,6 +5912,35 @@
|
|||||||
"storybook": "^10.4.1"
|
"storybook": "^10.4.1"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"node_modules/@storybook/addon-docs": {
|
||||||
|
"version": "10.4.1",
|
||||||
|
"resolved": "https://registry.npmjs.org/@storybook/addon-docs/-/addon-docs-10.4.1.tgz",
|
||||||
|
"integrity": "sha512-IYqUdjoZe4VO2LFZlKL/gwy7DsQSWCq6hX+zc1MBmZo04yycDASk1tte57n9pdlW3ajw9yYMF/+lVBi+xQjyvw==",
|
||||||
|
"dev": true,
|
||||||
|
"license": "MIT",
|
||||||
|
"dependencies": {
|
||||||
|
"@mdx-js/react": "^3.0.0",
|
||||||
|
"@storybook/csf-plugin": "10.4.1",
|
||||||
|
"@storybook/icons": "^2.0.2",
|
||||||
|
"@storybook/react-dom-shim": "10.4.1",
|
||||||
|
"react": "^16.8.0 || ^17.0.0 || ^18.0.0 || ^19.0.0",
|
||||||
|
"react-dom": "^16.8.0 || ^17.0.0 || ^18.0.0 || ^19.0.0",
|
||||||
|
"ts-dedent": "^2.0.0"
|
||||||
|
},
|
||||||
|
"funding": {
|
||||||
|
"type": "opencollective",
|
||||||
|
"url": "https://opencollective.com/storybook"
|
||||||
|
},
|
||||||
|
"peerDependencies": {
|
||||||
|
"@types/react": "^16.8.0 || ^17.0.0 || ^18.0.0 || ^19.0.0",
|
||||||
|
"storybook": "^10.4.1"
|
||||||
|
},
|
||||||
|
"peerDependenciesMeta": {
|
||||||
|
"@types/react": {
|
||||||
|
"optional": true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
"node_modules/@storybook/builder-webpack5": {
|
"node_modules/@storybook/builder-webpack5": {
|
||||||
"version": "10.4.1",
|
"version": "10.4.1",
|
||||||
"resolved": "https://registry.npmjs.org/@storybook/builder-webpack5/-/builder-webpack5-10.4.1.tgz",
|
"resolved": "https://registry.npmjs.org/@storybook/builder-webpack5/-/builder-webpack5-10.4.1.tgz",
|
||||||
@@ -6017,6 +6047,41 @@
|
|||||||
"storybook": "^10.4.1"
|
"storybook": "^10.4.1"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"node_modules/@storybook/csf-plugin": {
|
||||||
|
"version": "10.4.1",
|
||||||
|
"resolved": "https://registry.npmjs.org/@storybook/csf-plugin/-/csf-plugin-10.4.1.tgz",
|
||||||
|
"integrity": "sha512-WdPepGBxDGOUDjYd8KxMtcf+us/2PAcnBczl77XtrnxxHNs0jWesxKkiJ9yiuGrge4BPhDeAj6rxjbBoaHxLBA==",
|
||||||
|
"dev": true,
|
||||||
|
"license": "MIT",
|
||||||
|
"dependencies": {
|
||||||
|
"unplugin": "^2.3.5"
|
||||||
|
},
|
||||||
|
"funding": {
|
||||||
|
"type": "opencollective",
|
||||||
|
"url": "https://opencollective.com/storybook"
|
||||||
|
},
|
||||||
|
"peerDependencies": {
|
||||||
|
"esbuild": "*",
|
||||||
|
"rollup": "*",
|
||||||
|
"storybook": "^10.4.1",
|
||||||
|
"vite": "*",
|
||||||
|
"webpack": "*"
|
||||||
|
},
|
||||||
|
"peerDependenciesMeta": {
|
||||||
|
"esbuild": {
|
||||||
|
"optional": true
|
||||||
|
},
|
||||||
|
"rollup": {
|
||||||
|
"optional": true
|
||||||
|
},
|
||||||
|
"vite": {
|
||||||
|
"optional": true
|
||||||
|
},
|
||||||
|
"webpack": {
|
||||||
|
"optional": true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
"node_modules/@storybook/global": {
|
"node_modules/@storybook/global": {
|
||||||
"version": "5.0.0",
|
"version": "5.0.0",
|
||||||
"resolved": "https://registry.npmjs.org/@storybook/global/-/global-5.0.0.tgz",
|
"resolved": "https://registry.npmjs.org/@storybook/global/-/global-5.0.0.tgz",
|
||||||
@@ -6192,7 +6257,7 @@
|
|||||||
"webpack": ">= 4"
|
"webpack": ">= 4"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/@storybook/react/node_modules/@storybook/react-dom-shim": {
|
"node_modules/@storybook/react-dom-shim": {
|
||||||
"version": "10.4.1",
|
"version": "10.4.1",
|
||||||
"resolved": "https://registry.npmjs.org/@storybook/react-dom-shim/-/react-dom-shim-10.4.1.tgz",
|
"resolved": "https://registry.npmjs.org/@storybook/react-dom-shim/-/react-dom-shim-10.4.1.tgz",
|
||||||
"integrity": "sha512-6QFqfDNH4DMrt7yHKRfpqRopsVUc/Az+sXIdJ39IetYnHUxL3nW4NVaPc6uy/8Qi8urzUyEXL/nn7cpSIP2aPQ==",
|
"integrity": "sha512-6QFqfDNH4DMrt7yHKRfpqRopsVUc/Az+sXIdJ39IetYnHUxL3nW4NVaPc6uy/8Qi8urzUyEXL/nn7cpSIP2aPQ==",
|
||||||
@@ -23811,6 +23876,22 @@
|
|||||||
"node": ">= 0.8"
|
"node": ">= 0.8"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"node_modules/unplugin": {
|
||||||
|
"version": "2.3.11",
|
||||||
|
"resolved": "https://registry.npmjs.org/unplugin/-/unplugin-2.3.11.tgz",
|
||||||
|
"integrity": "sha512-5uKD0nqiYVzlmCRs01Fhs2BdkEgBS3SAVP6ndrBsuK42iC2+JHyxM05Rm9G8+5mkmRtzMZGY8Ct5+mliZxU/Ww==",
|
||||||
|
"dev": true,
|
||||||
|
"license": "MIT",
|
||||||
|
"dependencies": {
|
||||||
|
"@jridgewell/remapping": "^2.3.5",
|
||||||
|
"acorn": "^8.15.0",
|
||||||
|
"picomatch": "^4.0.3",
|
||||||
|
"webpack-virtual-modules": "^0.6.2"
|
||||||
|
},
|
||||||
|
"engines": {
|
||||||
|
"node": ">=18.12.0"
|
||||||
|
}
|
||||||
|
},
|
||||||
"node_modules/unrs-resolver": {
|
"node_modules/unrs-resolver": {
|
||||||
"version": "1.12.2",
|
"version": "1.12.2",
|
||||||
"resolved": "https://registry.npmjs.org/unrs-resolver/-/unrs-resolver-1.12.2.tgz",
|
"resolved": "https://registry.npmjs.org/unrs-resolver/-/unrs-resolver-1.12.2.tgz",
|
||||||
|
|||||||
+1
-2
@@ -17,9 +17,7 @@
|
|||||||
"lint": "eslint . --ext .js,.jsx,.ts,.tsx --max-warnings 9999",
|
"lint": "eslint . --ext .js,.jsx,.ts,.tsx --max-warnings 9999",
|
||||||
"postinstall": "npm rebuild lightningcss && prisma generate",
|
"postinstall": "npm rebuild lightningcss && prisma generate",
|
||||||
"storybook": "storybook dev -p 6006",
|
"storybook": "storybook dev -p 6006",
|
||||||
"storybook:github": "STORYBOOK_BASE_PATH=true storybook dev -p 6006",
|
|
||||||
"storybook:build": "storybook build",
|
"storybook:build": "storybook build",
|
||||||
"storybook:build:github": "STORYBOOK_BASE_PATH=true storybook build",
|
|
||||||
"knip": "knip --include files,exports --exclude duplicates",
|
"knip": "knip --include files,exports --exclude duplicates",
|
||||||
"test": "vitest run --coverage",
|
"test": "vitest run --coverage",
|
||||||
"test:watch": "vitest",
|
"test:watch": "vitest",
|
||||||
@@ -69,6 +67,7 @@
|
|||||||
"@lhci/cli": "^0.15.1",
|
"@lhci/cli": "^0.15.1",
|
||||||
"@playwright/test": "^1.55.0",
|
"@playwright/test": "^1.55.0",
|
||||||
"@storybook/addon-a11y": "^10.2.0",
|
"@storybook/addon-a11y": "^10.2.0",
|
||||||
|
"@storybook/addon-docs": "^10.2.0",
|
||||||
"@storybook/nextjs": "^10.2.0",
|
"@storybook/nextjs": "^10.2.0",
|
||||||
"@storybook/react": "^10.2.0",
|
"@storybook/react": "^10.2.0",
|
||||||
"@svgr/webpack": "^8.1.0",
|
"@svgr/webpack": "^8.1.0",
|
||||||
|
|||||||
@@ -1,4 +1,9 @@
|
|||||||
import Button from "../../app/components/buttons/Button";
|
import Button from "../../app/components/buttons/Button";
|
||||||
|
import {
|
||||||
|
BUTTON_PALETTE_OPTIONS,
|
||||||
|
BUTTON_TYPE_OPTIONS,
|
||||||
|
SIZE_OPTIONS,
|
||||||
|
} from "../../lib/propNormalization";
|
||||||
|
|
||||||
export default {
|
export default {
|
||||||
title: "Components/Buttons/Button",
|
title: "Components/Buttons/Button",
|
||||||
@@ -15,17 +20,17 @@ export default {
|
|||||||
argTypes: {
|
argTypes: {
|
||||||
buttonType: {
|
buttonType: {
|
||||||
control: { type: "select" },
|
control: { type: "select" },
|
||||||
options: ["filled", "outline", "ghost", "danger"],
|
options: [...BUTTON_TYPE_OPTIONS],
|
||||||
description: "The button type (Figma prop)",
|
description: "The button type (Figma prop)",
|
||||||
},
|
},
|
||||||
palette: {
|
palette: {
|
||||||
control: { type: "select" },
|
control: { type: "select" },
|
||||||
options: ["default", "inverse"],
|
options: [...BUTTON_PALETTE_OPTIONS],
|
||||||
description: "The button palette (Figma prop)",
|
description: "The button palette (Figma prop)",
|
||||||
},
|
},
|
||||||
size: {
|
size: {
|
||||||
control: { type: "select" },
|
control: { type: "select" },
|
||||||
options: ["xsmall", "small", "medium", "large", "xlarge"],
|
options: [...SIZE_OPTIONS],
|
||||||
description: "The size of the button",
|
description: "The size of the button",
|
||||||
},
|
},
|
||||||
disabled: {
|
disabled: {
|
||||||
|
|||||||
@@ -33,7 +33,7 @@ export const Default = {
|
|||||||
|
|
||||||
export const InParagraph = {
|
export const InParagraph = {
|
||||||
render: () => (
|
render: () => (
|
||||||
<p className="max-w-md font-inter text-[14px] leading-[20px] text-[color:var(--color-content-default-primary,#fff)]">
|
<p className="max-w-md text-small-paragraph text-[var(--color-content-default-primary)]">
|
||||||
Share a bit more detail so the group can weigh in. You can always{" "}
|
Share a bit more detail so the group can weigh in. You can always{" "}
|
||||||
<InlineTextButton onClick={() => {}}>expand this later</InlineTextButton>{" "}
|
<InlineTextButton onClick={() => {}}>expand this later</InlineTextButton>{" "}
|
||||||
if you need more room.
|
if you need more room.
|
||||||
|
|||||||
@@ -37,10 +37,10 @@ export const Default = {
|
|||||||
<Icon
|
<Icon
|
||||||
name="number"
|
name="number"
|
||||||
size={32}
|
size={32}
|
||||||
className="text-[var(--color-content-default-brand-primary,#fefcc9)]"
|
className="text-[var(--color-content-default-brand-primary)]"
|
||||||
/>
|
/>
|
||||||
</span>
|
</span>
|
||||||
<span className="w-full text-center font-inter text-[14px] font-medium leading-[18px] text-[var(--color-content-default-brand-primary,#fefcc9)]">
|
<span className="w-full text-center text-small-paragraph font-medium text-[var(--color-content-default-brand-primary)]">
|
||||||
Number
|
Number
|
||||||
</span>
|
</span>
|
||||||
</Vertical>
|
</Vertical>
|
||||||
@@ -54,10 +54,10 @@ export const Disabled = {
|
|||||||
<Icon
|
<Icon
|
||||||
name="number"
|
name="number"
|
||||||
size={32}
|
size={32}
|
||||||
className="text-[var(--color-content-default-brand-primary,#fefcc9)]"
|
className="text-[var(--color-content-default-brand-primary)]"
|
||||||
/>
|
/>
|
||||||
</span>
|
</span>
|
||||||
<span className="w-full text-center font-inter text-[14px] font-medium leading-[18px] text-[var(--color-content-default-brand-primary,#fefcc9)]">
|
<span className="w-full text-center text-small-paragraph font-medium text-[var(--color-content-default-brand-primary)]">
|
||||||
Number
|
Number
|
||||||
</span>
|
</span>
|
||||||
</Vertical>
|
</Vertical>
|
||||||
|
|||||||
@@ -1,7 +1,5 @@
|
|||||||
import React, { useState } from "react";
|
import React, { useState } from "react";
|
||||||
import AddCustomField from "../../app/components/controls/AddCustomField";
|
import AddCustomField from "../../app/components/controls/AddCustomField";
|
||||||
import { MessagesProvider } from "../../app/contexts/MessagesContext";
|
|
||||||
import messages from "../../messages/en/index";
|
|
||||||
|
|
||||||
/** Figma: Add Custom Field — node `20235:12994` (Community Rule System). */
|
/** Figma: Add Custom Field — node `20235:12994` (Community Rule System). */
|
||||||
export default {
|
export default {
|
||||||
@@ -9,11 +7,9 @@ export default {
|
|||||||
component: AddCustomField,
|
component: AddCustomField,
|
||||||
decorators: [
|
decorators: [
|
||||||
(Story) => (
|
(Story) => (
|
||||||
<MessagesProvider messages={messages}>
|
|
||||||
<div className="w-[min(100%,546px)] bg-[var(--color-surface-default-primary)] p-6">
|
<div className="w-[min(100%,546px)] bg-[var(--color-surface-default-primary)] p-6">
|
||||||
<Story />
|
<Story />
|
||||||
</div>
|
</div>
|
||||||
</MessagesProvider>
|
|
||||||
),
|
),
|
||||||
],
|
],
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -1,4 +1,9 @@
|
|||||||
import Chip from "../../app/components/controls/Chip";
|
import Chip from "../../app/components/controls/Chip";
|
||||||
|
import {
|
||||||
|
CHIP_PALETTE_OPTIONS,
|
||||||
|
CHIP_SIZE_OPTIONS,
|
||||||
|
CHIP_STATE_OPTIONS,
|
||||||
|
} from "../../lib/propNormalization";
|
||||||
|
|
||||||
export default {
|
export default {
|
||||||
title: "Components/Controls/Chip",
|
title: "Components/Controls/Chip",
|
||||||
@@ -13,17 +18,17 @@ export default {
|
|||||||
},
|
},
|
||||||
state: {
|
state: {
|
||||||
control: "select",
|
control: "select",
|
||||||
options: ["unselected", "selected", "disabled", "custom"],
|
options: [...CHIP_STATE_OPTIONS],
|
||||||
description: "Visual state of the chip",
|
description: "Visual state of the chip",
|
||||||
},
|
},
|
||||||
palette: {
|
palette: {
|
||||||
control: "select",
|
control: "select",
|
||||||
options: ["default", "inverse"],
|
options: [...CHIP_PALETTE_OPTIONS],
|
||||||
description: "Color palette of the chip",
|
description: "Color palette of the chip",
|
||||||
},
|
},
|
||||||
size: {
|
size: {
|
||||||
control: "select",
|
control: "select",
|
||||||
options: ["s", "m"],
|
options: [...CHIP_SIZE_OPTIONS],
|
||||||
description: "Size of the chip",
|
description: "Size of the chip",
|
||||||
},
|
},
|
||||||
disabled: {
|
disabled: {
|
||||||
|
|||||||
@@ -44,43 +44,46 @@ const Template = (args) => {
|
|||||||
};
|
};
|
||||||
|
|
||||||
// Default story
|
// Default story
|
||||||
export const Default = Template.bind({});
|
export const Default = {
|
||||||
Default.args = {
|
args: {
|
||||||
label: "Default Select Input",
|
label: "Default Select Input",
|
||||||
placeholder: "Choose an option",
|
placeholder: "Choose an option",
|
||||||
state: "default",
|
state: "default",
|
||||||
};
|
},
|
||||||
|
render: Template,
|
||||||
// States
|
}; // States
|
||||||
export const Active = Template.bind({});
|
export const Active = {
|
||||||
Active.args = {
|
args: {
|
||||||
label: "Active State",
|
label: "Active State",
|
||||||
placeholder: "Choose an option",
|
placeholder: "Choose an option",
|
||||||
state: "active",
|
state: "active",
|
||||||
|
},
|
||||||
|
render: Template,
|
||||||
};
|
};
|
||||||
|
export const Focus = {
|
||||||
export const Focus = Template.bind({});
|
args: {
|
||||||
Focus.args = {
|
|
||||||
label: "Focus State",
|
label: "Focus State",
|
||||||
placeholder: "Choose an option",
|
placeholder: "Choose an option",
|
||||||
state: "focus",
|
state: "focus",
|
||||||
|
},
|
||||||
|
render: Template,
|
||||||
};
|
};
|
||||||
|
export const Error = {
|
||||||
export const Error = Template.bind({});
|
args: {
|
||||||
Error.args = {
|
|
||||||
label: "Error State",
|
label: "Error State",
|
||||||
placeholder: "Choose an option",
|
placeholder: "Choose an option",
|
||||||
error: true,
|
error: true,
|
||||||
|
},
|
||||||
|
render: Template,
|
||||||
};
|
};
|
||||||
|
export const Disabled = {
|
||||||
export const Disabled = Template.bind({});
|
args: {
|
||||||
Disabled.args = {
|
|
||||||
label: "Disabled State",
|
label: "Disabled State",
|
||||||
placeholder: "Choose an option",
|
placeholder: "Choose an option",
|
||||||
disabled: true,
|
disabled: true,
|
||||||
};
|
},
|
||||||
|
render: Template,
|
||||||
// Interactive example
|
}; // Interactive example
|
||||||
export const Interactive = (args) => {
|
export const Interactive = (args) => {
|
||||||
const [value, setValue] = useState("");
|
const [value, setValue] = useState("");
|
||||||
|
|
||||||
|
|||||||
@@ -39,32 +39,36 @@ export default {
|
|||||||
|
|
||||||
const Template = (args) => <Switch {...args} />;
|
const Template = (args) => <Switch {...args} />;
|
||||||
|
|
||||||
export const Default = Template.bind({});
|
export const Default = {
|
||||||
Default.args = {
|
args: {
|
||||||
propSwitch: false,
|
propSwitch: false,
|
||||||
text: "Switch label",
|
text: "Switch label",
|
||||||
|
},
|
||||||
|
render: Template,
|
||||||
};
|
};
|
||||||
|
export const Checked = {
|
||||||
export const Checked = Template.bind({});
|
args: {
|
||||||
Checked.args = {
|
|
||||||
propSwitch: true,
|
propSwitch: true,
|
||||||
text: "Switch label",
|
text: "Switch label",
|
||||||
|
},
|
||||||
|
render: Template,
|
||||||
};
|
};
|
||||||
|
export const Focus = {
|
||||||
export const Focus = Template.bind({});
|
args: {
|
||||||
Focus.args = {
|
|
||||||
propSwitch: false,
|
propSwitch: false,
|
||||||
state: "focus",
|
state: "focus",
|
||||||
text: "Switch label",
|
text: "Switch label",
|
||||||
|
},
|
||||||
|
render: Template,
|
||||||
};
|
};
|
||||||
|
export const FocusChecked = {
|
||||||
export const FocusChecked = Template.bind({});
|
args: {
|
||||||
FocusChecked.args = {
|
|
||||||
propSwitch: true,
|
propSwitch: true,
|
||||||
state: "focus",
|
state: "focus",
|
||||||
text: "Switch label",
|
text: "Switch label",
|
||||||
|
},
|
||||||
|
render: Template,
|
||||||
};
|
};
|
||||||
|
|
||||||
export const States = () => (
|
export const States = () => (
|
||||||
<div className="space-y-4">
|
<div className="space-y-4">
|
||||||
<div className="space-y-2">
|
<div className="space-y-2">
|
||||||
|
|||||||
@@ -1,5 +1,11 @@
|
|||||||
import React from "react";
|
import React from "react";
|
||||||
import TextArea from "../../app/components/controls/TextArea";
|
import TextArea from "../../app/components/controls/TextArea";
|
||||||
|
import {
|
||||||
|
INPUT_STATE_OPTIONS,
|
||||||
|
LABEL_VARIANT_OPTIONS,
|
||||||
|
SMALL_MEDIUM_LARGE_OPTIONS,
|
||||||
|
TEXT_AREA_APPEARANCE_OPTIONS,
|
||||||
|
} from "../../lib/propNormalization";
|
||||||
|
|
||||||
export default {
|
export default {
|
||||||
title: "Components/Controls/TextArea",
|
title: "Components/Controls/TextArea",
|
||||||
@@ -10,15 +16,19 @@ export default {
|
|||||||
argTypes: {
|
argTypes: {
|
||||||
size: {
|
size: {
|
||||||
control: { type: "select" },
|
control: { type: "select" },
|
||||||
options: ["small", "medium", "large"],
|
options: [...SMALL_MEDIUM_LARGE_OPTIONS],
|
||||||
},
|
},
|
||||||
labelVariant: {
|
labelVariant: {
|
||||||
control: { type: "select" },
|
control: { type: "select" },
|
||||||
options: ["default", "horizontal"],
|
options: [...LABEL_VARIANT_OPTIONS],
|
||||||
|
},
|
||||||
|
appearance: {
|
||||||
|
control: { type: "select" },
|
||||||
|
options: [...TEXT_AREA_APPEARANCE_OPTIONS],
|
||||||
},
|
},
|
||||||
state: {
|
state: {
|
||||||
control: { type: "select" },
|
control: { type: "select" },
|
||||||
options: ["default", "active", "hover", "focus", "error"],
|
options: [...INPUT_STATE_OPTIONS],
|
||||||
},
|
},
|
||||||
disabled: {
|
disabled: {
|
||||||
control: { type: "boolean" },
|
control: { type: "boolean" },
|
||||||
@@ -31,47 +41,52 @@ export default {
|
|||||||
|
|
||||||
const Template = (args) => <TextArea {...args} />;
|
const Template = (args) => <TextArea {...args} />;
|
||||||
|
|
||||||
export const Default = Template.bind({});
|
export const Default = {
|
||||||
Default.args = {
|
args: {
|
||||||
label: "Text Area",
|
label: "Text Area",
|
||||||
placeholder: "Enter text...",
|
placeholder: "Enter text...",
|
||||||
value: "",
|
value: "",
|
||||||
|
},
|
||||||
|
render: Template,
|
||||||
};
|
};
|
||||||
|
export const WithValue = {
|
||||||
export const WithValue = Template.bind({});
|
args: {
|
||||||
WithValue.args = {
|
|
||||||
label: "Text Area",
|
label: "Text Area",
|
||||||
placeholder: "Enter text...",
|
placeholder: "Enter text...",
|
||||||
value:
|
value:
|
||||||
"This is some sample text content that demonstrates how the text area looks with content.",
|
"This is some sample text content that demonstrates how the text area looks with content.",
|
||||||
|
},
|
||||||
|
render: Template,
|
||||||
};
|
};
|
||||||
|
export const Small = {
|
||||||
export const Small = Template.bind({});
|
args: {
|
||||||
Small.args = {
|
|
||||||
size: "small",
|
size: "small",
|
||||||
label: "Small Text Area",
|
label: "Small Text Area",
|
||||||
placeholder: "Enter text...",
|
placeholder: "Enter text...",
|
||||||
value: "",
|
value: "",
|
||||||
|
},
|
||||||
|
render: Template,
|
||||||
};
|
};
|
||||||
|
export const Medium = {
|
||||||
export const Medium = Template.bind({});
|
args: {
|
||||||
Medium.args = {
|
|
||||||
size: "medium",
|
size: "medium",
|
||||||
label: "Medium Text Area",
|
label: "Medium Text Area",
|
||||||
placeholder: "Enter text...",
|
placeholder: "Enter text...",
|
||||||
value: "",
|
value: "",
|
||||||
|
},
|
||||||
|
render: Template,
|
||||||
};
|
};
|
||||||
|
export const Large = {
|
||||||
export const Large = Template.bind({});
|
args: {
|
||||||
Large.args = {
|
|
||||||
size: "large",
|
size: "large",
|
||||||
label: "Large Text Area",
|
label: "Large Text Area",
|
||||||
placeholder: "Enter text...",
|
placeholder: "Enter text...",
|
||||||
value: "",
|
value: "",
|
||||||
|
},
|
||||||
|
render: Template,
|
||||||
};
|
};
|
||||||
|
export const Embedded = {
|
||||||
export const Embedded = Template.bind({});
|
args: {
|
||||||
Embedded.args = {
|
|
||||||
label: "Section content",
|
label: "Section content",
|
||||||
placeholder: "Enter text...",
|
placeholder: "Enter text...",
|
||||||
value:
|
value:
|
||||||
@@ -79,16 +94,18 @@ Embedded.args = {
|
|||||||
appearance: "embedded",
|
appearance: "embedded",
|
||||||
size: "large",
|
size: "large",
|
||||||
rows: 4,
|
rows: 4,
|
||||||
|
},
|
||||||
|
render: Template,
|
||||||
};
|
};
|
||||||
|
export const HorizontalLabel = {
|
||||||
export const HorizontalLabel = Template.bind({});
|
args: {
|
||||||
HorizontalLabel.args = {
|
|
||||||
labelVariant: "horizontal",
|
labelVariant: "horizontal",
|
||||||
label: "Horizontal Label",
|
label: "Horizontal Label",
|
||||||
placeholder: "Enter text...",
|
placeholder: "Enter text...",
|
||||||
value: "",
|
value: "",
|
||||||
|
},
|
||||||
|
render: Template,
|
||||||
};
|
};
|
||||||
|
|
||||||
export const AllSizes = () => (
|
export const AllSizes = () => (
|
||||||
<div className="space-y-6">
|
<div className="space-y-6">
|
||||||
<div className="space-y-4">
|
<div className="space-y-4">
|
||||||
|
|||||||
@@ -1,5 +1,9 @@
|
|||||||
import React from "react";
|
import React from "react";
|
||||||
import TextInput from "../../app/components/controls/TextInput";
|
import TextInput from "../../app/components/controls/TextInput";
|
||||||
|
import {
|
||||||
|
INPUT_STATE_OPTIONS,
|
||||||
|
TEXT_INPUT_SIZE_OPTIONS,
|
||||||
|
} from "../../lib/propNormalization";
|
||||||
|
|
||||||
export default {
|
export default {
|
||||||
title: "Components/Controls/TextInput",
|
title: "Components/Controls/TextInput",
|
||||||
@@ -10,11 +14,11 @@ export default {
|
|||||||
argTypes: {
|
argTypes: {
|
||||||
inputSize: {
|
inputSize: {
|
||||||
control: { type: "select" },
|
control: { type: "select" },
|
||||||
options: ["small", "medium", "Small", "Medium"],
|
options: [...TEXT_INPUT_SIZE_OPTIONS],
|
||||||
},
|
},
|
||||||
state: {
|
state: {
|
||||||
control: { type: "select" },
|
control: { type: "select" },
|
||||||
options: ["default", "active", "hover", "focus", "error", "disabled"],
|
options: [...INPUT_STATE_OPTIONS],
|
||||||
},
|
},
|
||||||
disabled: {
|
disabled: {
|
||||||
control: { type: "boolean" },
|
control: { type: "boolean" },
|
||||||
@@ -37,75 +41,80 @@ export default {
|
|||||||
const Template = (args) => <TextInput {...args} />;
|
const Template = (args) => <TextInput {...args} />;
|
||||||
|
|
||||||
// Default story
|
// Default story
|
||||||
export const Default = Template.bind({});
|
export const Default = {
|
||||||
Default.args = {
|
args: {
|
||||||
label: "Default Text Input",
|
label: "Default Text Input",
|
||||||
placeholder: "Enter text...",
|
placeholder: "Enter text...",
|
||||||
inputSize: "medium",
|
inputSize: "medium",
|
||||||
state: "default",
|
state: "default",
|
||||||
};
|
},
|
||||||
|
render: Template,
|
||||||
// Size variants
|
}; // Size variants
|
||||||
export const Small = Template.bind({});
|
export const Small = {
|
||||||
Small.args = {
|
args: {
|
||||||
label: "Small Text Input",
|
label: "Small Text Input",
|
||||||
placeholder: "Small size",
|
placeholder: "Small size",
|
||||||
inputSize: "small",
|
inputSize: "small",
|
||||||
state: "default",
|
state: "default",
|
||||||
|
},
|
||||||
|
render: Template,
|
||||||
};
|
};
|
||||||
|
export const Medium = {
|
||||||
export const Medium = Template.bind({});
|
args: {
|
||||||
Medium.args = {
|
|
||||||
label: "Medium Text Input",
|
label: "Medium Text Input",
|
||||||
placeholder: "Medium size",
|
placeholder: "Medium size",
|
||||||
inputSize: "medium",
|
inputSize: "medium",
|
||||||
state: "default",
|
state: "default",
|
||||||
};
|
},
|
||||||
|
render: Template,
|
||||||
// States
|
}; // States
|
||||||
export const Active = Template.bind({});
|
export const Active = {
|
||||||
Active.args = {
|
args: {
|
||||||
label: "Active State",
|
label: "Active State",
|
||||||
placeholder: "Active input",
|
placeholder: "Active input",
|
||||||
inputSize: "medium",
|
inputSize: "medium",
|
||||||
state: "active",
|
state: "active",
|
||||||
|
},
|
||||||
|
render: Template,
|
||||||
};
|
};
|
||||||
|
export const Hover = {
|
||||||
export const Hover = Template.bind({});
|
args: {
|
||||||
Hover.args = {
|
|
||||||
label: "Hover State",
|
label: "Hover State",
|
||||||
placeholder: "Hover input",
|
placeholder: "Hover input",
|
||||||
inputSize: "medium",
|
inputSize: "medium",
|
||||||
state: "hover",
|
state: "hover",
|
||||||
|
},
|
||||||
|
render: Template,
|
||||||
};
|
};
|
||||||
|
export const Focus = {
|
||||||
export const Focus = Template.bind({});
|
args: {
|
||||||
Focus.args = {
|
|
||||||
label: "Focus State",
|
label: "Focus State",
|
||||||
placeholder: "Focused input",
|
placeholder: "Focused input",
|
||||||
inputSize: "medium",
|
inputSize: "medium",
|
||||||
state: "focus",
|
state: "focus",
|
||||||
|
},
|
||||||
|
render: Template,
|
||||||
};
|
};
|
||||||
|
export const Error = {
|
||||||
export const Error = Template.bind({});
|
args: {
|
||||||
Error.args = {
|
|
||||||
label: "Error State",
|
label: "Error State",
|
||||||
placeholder: "Error input",
|
placeholder: "Error input",
|
||||||
inputSize: "medium",
|
inputSize: "medium",
|
||||||
state: "default",
|
state: "default",
|
||||||
error: true,
|
error: true,
|
||||||
|
},
|
||||||
|
render: Template,
|
||||||
};
|
};
|
||||||
|
export const Disabled = {
|
||||||
export const Disabled = Template.bind({});
|
args: {
|
||||||
Disabled.args = {
|
|
||||||
label: "Disabled State",
|
label: "Disabled State",
|
||||||
placeholder: "Disabled input",
|
placeholder: "Disabled input",
|
||||||
inputSize: "medium",
|
inputSize: "medium",
|
||||||
state: "default",
|
state: "default",
|
||||||
disabled: true,
|
disabled: true,
|
||||||
};
|
},
|
||||||
|
render: Template,
|
||||||
// Interactive example
|
}; // Interactive example
|
||||||
export const Interactive = (args) => {
|
export const Interactive = (args) => {
|
||||||
const [value, setValue] = React.useState("");
|
const [value, setValue] = React.useState("");
|
||||||
|
|
||||||
|
|||||||
@@ -44,22 +44,24 @@ export const States = () => (
|
|||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
|
|
||||||
export const WithText = Template.bind({});
|
export const WithText = {
|
||||||
WithText.args = {
|
args: {
|
||||||
label: "Text Toggle",
|
label: "Text Toggle",
|
||||||
checked: false,
|
checked: false,
|
||||||
showText: true,
|
showText: true,
|
||||||
text: "Toggle",
|
text: "Toggle",
|
||||||
|
},
|
||||||
|
render: Template,
|
||||||
};
|
};
|
||||||
|
export const WithIcon = {
|
||||||
export const WithIcon = Template.bind({});
|
args: {
|
||||||
WithIcon.args = {
|
|
||||||
label: "Icon Toggle",
|
label: "Icon Toggle",
|
||||||
checked: false,
|
checked: false,
|
||||||
showIcon: true,
|
showIcon: true,
|
||||||
icon: "I",
|
icon: "I",
|
||||||
|
},
|
||||||
|
render: Template,
|
||||||
};
|
};
|
||||||
|
|
||||||
export const Interactive = () => {
|
export const Interactive = () => {
|
||||||
const [checked, setChecked] = React.useState(false);
|
const [checked, setChecked] = React.useState(false);
|
||||||
const [state, setState] = React.useState("default");
|
const [state, setState] = React.useState("default");
|
||||||
|
|||||||
@@ -1,5 +1,9 @@
|
|||||||
import React from "react";
|
import React from "react";
|
||||||
import ToggleGroup from "../../app/components/controls/ToggleGroup";
|
import ToggleGroup from "../../app/components/controls/ToggleGroup";
|
||||||
|
import {
|
||||||
|
STATE_OPTIONS,
|
||||||
|
TOGGLE_GROUP_POSITION_OPTIONS,
|
||||||
|
} from "../../lib/propNormalization";
|
||||||
|
|
||||||
export default {
|
export default {
|
||||||
title: "Components/Controls/ToggleGroup",
|
title: "Components/Controls/ToggleGroup",
|
||||||
@@ -10,11 +14,11 @@ export default {
|
|||||||
argTypes: {
|
argTypes: {
|
||||||
position: {
|
position: {
|
||||||
control: { type: "select" },
|
control: { type: "select" },
|
||||||
options: ["left", "middle", "right"],
|
options: [...TOGGLE_GROUP_POSITION_OPTIONS],
|
||||||
},
|
},
|
||||||
state: {
|
state: {
|
||||||
control: { type: "select" },
|
control: { type: "select" },
|
||||||
options: ["default", "hover", "focus", "selected"],
|
options: [...STATE_OPTIONS],
|
||||||
},
|
},
|
||||||
showText: {
|
showText: {
|
||||||
control: { type: "boolean" },
|
control: { type: "boolean" },
|
||||||
@@ -24,27 +28,30 @@ export default {
|
|||||||
|
|
||||||
const Template = (args) => <ToggleGroup {...args}>Toggle Item</ToggleGroup>;
|
const Template = (args) => <ToggleGroup {...args}>Toggle Item</ToggleGroup>;
|
||||||
|
|
||||||
export const Default = Template.bind({});
|
export const Default = {
|
||||||
Default.args = {
|
args: {
|
||||||
position: "left",
|
position: "left",
|
||||||
state: "default",
|
state: "default",
|
||||||
showText: true,
|
showText: true,
|
||||||
|
},
|
||||||
|
render: Template,
|
||||||
};
|
};
|
||||||
|
export const Middle = {
|
||||||
export const Middle = Template.bind({});
|
args: {
|
||||||
Middle.args = {
|
|
||||||
position: "middle",
|
position: "middle",
|
||||||
state: "default",
|
state: "default",
|
||||||
showText: true,
|
showText: true,
|
||||||
|
},
|
||||||
|
render: Template,
|
||||||
};
|
};
|
||||||
|
export const Right = {
|
||||||
export const Right = Template.bind({});
|
args: {
|
||||||
Right.args = {
|
|
||||||
position: "right",
|
position: "right",
|
||||||
state: "default",
|
state: "default",
|
||||||
showText: true,
|
showText: true,
|
||||||
|
},
|
||||||
|
render: Template,
|
||||||
};
|
};
|
||||||
|
|
||||||
export const States = () => (
|
export const States = () => (
|
||||||
<div className="space-y-4">
|
<div className="space-y-4">
|
||||||
<div className="space-y-2">
|
<div className="space-y-2">
|
||||||
@@ -89,22 +96,24 @@ export const Positions = () => (
|
|||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
|
|
||||||
export const WithText = Template.bind({});
|
export const WithText = {
|
||||||
WithText.args = {
|
args: {
|
||||||
position: "left",
|
position: "left",
|
||||||
state: "default",
|
state: "default",
|
||||||
showText: true,
|
showText: true,
|
||||||
children: "Active Deals",
|
children: "Active Deals",
|
||||||
|
},
|
||||||
|
render: Template,
|
||||||
};
|
};
|
||||||
|
export const WithoutText = {
|
||||||
export const WithoutText = Template.bind({});
|
args: {
|
||||||
WithoutText.args = {
|
|
||||||
position: "left",
|
position: "left",
|
||||||
state: "default",
|
state: "default",
|
||||||
showText: false,
|
showText: false,
|
||||||
children: "☰",
|
children: "☰",
|
||||||
|
},
|
||||||
|
render: Template,
|
||||||
};
|
};
|
||||||
|
|
||||||
export const WithIcons = () => (
|
export const WithIcons = () => (
|
||||||
<div className="space-y-4">
|
<div className="space-y-4">
|
||||||
<div className="space-y-2">
|
<div className="space-y-2">
|
||||||
|
|||||||
@@ -34,87 +34,97 @@ export default {
|
|||||||
const Template = (args) => <Upload {...args} />;
|
const Template = (args) => <Upload {...args} />;
|
||||||
|
|
||||||
// Default story
|
// Default story
|
||||||
export const Default = Template.bind({});
|
export const Default = {
|
||||||
Default.args = {
|
args: {
|
||||||
label: "Upload",
|
label: "Upload",
|
||||||
active: true,
|
active: true,
|
||||||
showHelpIcon: true,
|
showHelpIcon: true,
|
||||||
};
|
},
|
||||||
|
render: Template,
|
||||||
// Active state
|
}; // Active state
|
||||||
export const Active = Template.bind({});
|
export const Active = {
|
||||||
Active.args = {
|
args: {
|
||||||
label: "Upload",
|
label: "Upload",
|
||||||
active: true,
|
active: true,
|
||||||
showHelpIcon: true,
|
showHelpIcon: true,
|
||||||
};
|
},
|
||||||
Active.parameters = {
|
parameters: {
|
||||||
docs: {
|
docs: {
|
||||||
description: {
|
description: {
|
||||||
story:
|
story:
|
||||||
"Upload component in active state with white button and black text.",
|
"Upload component in active state with white button and black text.",
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
|
},
|
||||||
|
render: Template,
|
||||||
};
|
};
|
||||||
|
|
||||||
// Inactive state
|
// Inactive state
|
||||||
export const Inactive = Template.bind({});
|
export const Inactive = {
|
||||||
Inactive.args = {
|
args: {
|
||||||
label: "Upload",
|
label: "Upload",
|
||||||
active: false,
|
active: false,
|
||||||
showHelpIcon: true,
|
showHelpIcon: true,
|
||||||
};
|
},
|
||||||
Inactive.parameters = {
|
parameters: {
|
||||||
docs: {
|
docs: {
|
||||||
description: {
|
description: {
|
||||||
story:
|
story:
|
||||||
"Upload component in inactive state with dark button and gray text.",
|
"Upload component in inactive state with dark button and gray text.",
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
|
},
|
||||||
|
render: Template,
|
||||||
};
|
};
|
||||||
|
|
||||||
// Without help icon
|
// Without help icon
|
||||||
export const WithoutHelpIcon = Template.bind({});
|
export const WithoutHelpIcon = {
|
||||||
WithoutHelpIcon.args = {
|
args: {
|
||||||
label: "Upload",
|
label: "Upload",
|
||||||
active: true,
|
active: true,
|
||||||
showHelpIcon: false,
|
showHelpIcon: false,
|
||||||
};
|
},
|
||||||
WithoutHelpIcon.parameters = {
|
parameters: {
|
||||||
docs: {
|
docs: {
|
||||||
description: {
|
description: {
|
||||||
story: "Upload component without help icon.",
|
story: "Upload component without help icon.",
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
|
},
|
||||||
|
render: Template,
|
||||||
};
|
};
|
||||||
|
|
||||||
// Without label
|
// Without label
|
||||||
export const WithoutLabel = Template.bind({});
|
export const WithoutLabel = {
|
||||||
WithoutLabel.args = {
|
args: {
|
||||||
active: true,
|
active: true,
|
||||||
showHelpIcon: false,
|
showHelpIcon: false,
|
||||||
};
|
},
|
||||||
WithoutLabel.parameters = {
|
parameters: {
|
||||||
docs: {
|
docs: {
|
||||||
description: {
|
description: {
|
||||||
story: "Upload component without label.",
|
story: "Upload component without label.",
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
|
},
|
||||||
|
render: Template,
|
||||||
};
|
};
|
||||||
|
|
||||||
// Custom label
|
// Custom label
|
||||||
export const CustomLabel = Template.bind({});
|
export const CustomLabel = {
|
||||||
CustomLabel.args = {
|
args: {
|
||||||
label: "Upload Files",
|
label: "Upload Files",
|
||||||
active: true,
|
active: true,
|
||||||
showHelpIcon: true,
|
showHelpIcon: true,
|
||||||
};
|
},
|
||||||
CustomLabel.parameters = {
|
parameters: {
|
||||||
docs: {
|
docs: {
|
||||||
description: {
|
description: {
|
||||||
story: "Upload component with custom label text.",
|
story: "Upload component with custom label text.",
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
|
},
|
||||||
|
render: Template,
|
||||||
};
|
};
|
||||||
|
|
||||||
// All states comparison
|
// All states comparison
|
||||||
|
|||||||
+16
-4
@@ -1,16 +1,19 @@
|
|||||||
import React from "react";
|
import React from "react";
|
||||||
import HeaderLockup from "../../app/components/type/HeaderLockup";
|
import HeaderLockup from "../../app/components/type/HeaderLockup";
|
||||||
import InfoMessageBox from "../../app/components/controls/InfoMessageBox";
|
import InfoMessageBox from "../../app/components/controls/InfoMessageBox";
|
||||||
|
import {
|
||||||
|
HEADER_LOCKUP_JUSTIFICATION_OPTIONS,
|
||||||
|
HEADER_LOCKUP_SIZE_OPTIONS,
|
||||||
|
} from "../../lib/propNormalization";
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Compose pattern used by create-flow “decision approaches” rail: headline lockup
|
* Compose pattern used by create-flow “decision approaches” rail: headline lockup
|
||||||
* plus bordered checklist — no standalone wrapper component.
|
* plus bordered checklist — no standalone wrapper component.
|
||||||
*/
|
*/
|
||||||
export default {
|
export default {
|
||||||
title: "Components/Controls/Rail header (lockup + info box)",
|
title: "Create Flow/Rail header (lockup + info box)",
|
||||||
parameters: {
|
parameters: {
|
||||||
layout: "centered",
|
layout: "centered",
|
||||||
backgrounds: { default: "dark" },
|
|
||||||
docs: {
|
docs: {
|
||||||
description: {
|
description: {
|
||||||
component:
|
component:
|
||||||
@@ -18,14 +21,23 @@ export default {
|
|||||||
},
|
},
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
|
argTypes: {
|
||||||
|
size: {
|
||||||
|
control: { type: "select" },
|
||||||
|
options: [...HEADER_LOCKUP_SIZE_OPTIONS],
|
||||||
|
},
|
||||||
|
justification: {
|
||||||
|
control: { type: "select" },
|
||||||
|
options: [...HEADER_LOCKUP_JUSTIFICATION_OPTIONS],
|
||||||
|
},
|
||||||
|
},
|
||||||
decorators: [
|
decorators: [
|
||||||
(Story) => (
|
(Story) => (
|
||||||
<div className="bg-black p-8 max-w-lg w-full">
|
<div className="w-full max-w-lg bg-[var(--color-surface-default-primary)] p-8">
|
||||||
<Story />
|
<Story />
|
||||||
</div>
|
</div>
|
||||||
),
|
),
|
||||||
],
|
],
|
||||||
tags: ["autodocs"],
|
|
||||||
};
|
};
|
||||||
|
|
||||||
const messageItems = [
|
const messageItems = [
|
||||||
@@ -0,0 +1,73 @@
|
|||||||
|
import ListItem from "../../app/components/layout/ListItem";
|
||||||
|
import { ICON_NAME_OPTIONS } from "../../app/components/asset/icon";
|
||||||
|
|
||||||
|
export default {
|
||||||
|
title: "Components/Layout/ListItem",
|
||||||
|
component: ListItem,
|
||||||
|
parameters: {
|
||||||
|
layout: "centered",
|
||||||
|
docs: {
|
||||||
|
description: {
|
||||||
|
component:
|
||||||
|
"Icon + label menuitem row used in Popover export menus and CreateFlowTopNav action menus. Distinct from List / ListEntry data rows.",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
argTypes: {
|
||||||
|
label: { control: { type: "text" } },
|
||||||
|
leadingIcon: {
|
||||||
|
control: { type: "select" },
|
||||||
|
options: [...ICON_NAME_OPTIONS],
|
||||||
|
},
|
||||||
|
showDivider: { control: { type: "boolean" } },
|
||||||
|
variant: {
|
||||||
|
control: { type: "select" },
|
||||||
|
options: ["default", "destructive"],
|
||||||
|
},
|
||||||
|
onClick: { action: "clicked" },
|
||||||
|
},
|
||||||
|
decorators: [
|
||||||
|
(Story) => (
|
||||||
|
<div className="w-[240px] bg-[var(--color-surface-default-primary)]">
|
||||||
|
<Story />
|
||||||
|
</div>
|
||||||
|
),
|
||||||
|
],
|
||||||
|
};
|
||||||
|
|
||||||
|
export const Default = {
|
||||||
|
args: {
|
||||||
|
label: "PDF",
|
||||||
|
leadingIcon: "picture_as_pdf",
|
||||||
|
showDivider: true,
|
||||||
|
},
|
||||||
|
};
|
||||||
|
|
||||||
|
export const ExportMenu = {
|
||||||
|
render: (args) => (
|
||||||
|
<div>
|
||||||
|
<ListItem
|
||||||
|
{...args}
|
||||||
|
label="PDF"
|
||||||
|
leadingIcon="picture_as_pdf"
|
||||||
|
showDivider
|
||||||
|
/>
|
||||||
|
<ListItem {...args} label="CSV" leadingIcon="csv" showDivider />
|
||||||
|
<ListItem
|
||||||
|
{...args}
|
||||||
|
label="Markdown"
|
||||||
|
leadingIcon="markdown_copy"
|
||||||
|
showDivider={false}
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
),
|
||||||
|
};
|
||||||
|
|
||||||
|
export const Destructive = {
|
||||||
|
args: {
|
||||||
|
label: "Log out",
|
||||||
|
leadingIcon: "log_out",
|
||||||
|
showDivider: false,
|
||||||
|
variant: "destructive",
|
||||||
|
},
|
||||||
|
};
|
||||||
@@ -42,119 +42,132 @@ const Template = (args) => {
|
|||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|
||||||
export const ToastDefault = Template.bind({});
|
export const ToastDefault = {
|
||||||
ToastDefault.args = {
|
args: {
|
||||||
title: "Short alert toast message goes here",
|
title: "Short alert toast message goes here",
|
||||||
description:
|
description:
|
||||||
"Nascetur ipsum a nisi tempor cras nam neque volutpat. Aliquam id est faucibus nunc quis. Eleifend suspendisse.",
|
"Nascetur ipsum a nisi tempor cras nam neque volutpat. Aliquam id est faucibus nunc quis. Eleifend suspendisse.",
|
||||||
status: "default",
|
status: "default",
|
||||||
type: "toast",
|
type: "toast",
|
||||||
|
},
|
||||||
|
render: Template,
|
||||||
};
|
};
|
||||||
|
export const ToastPositive = {
|
||||||
export const ToastPositive = Template.bind({});
|
args: {
|
||||||
ToastPositive.args = {
|
|
||||||
title: "Short alert toast message goes here",
|
title: "Short alert toast message goes here",
|
||||||
description:
|
description:
|
||||||
"Nascetur ipsum a nisi tempor cras nam neque volutpat. Aliquam id est faucibus nunc quis. Eleifend suspendisse.",
|
"Nascetur ipsum a nisi tempor cras nam neque volutpat. Aliquam id est faucibus nunc quis. Eleifend suspendisse.",
|
||||||
status: "positive",
|
status: "positive",
|
||||||
type: "toast",
|
type: "toast",
|
||||||
|
},
|
||||||
|
render: Template,
|
||||||
};
|
};
|
||||||
|
export const ToastWarning = {
|
||||||
export const ToastWarning = Template.bind({});
|
args: {
|
||||||
ToastWarning.args = {
|
|
||||||
title: "Short alert toast message goes here",
|
title: "Short alert toast message goes here",
|
||||||
description:
|
description:
|
||||||
"Nascetur ipsum a nisi tempor cras nam neque volutpat. Aliquam id est faucibus nunc quis. Eleifend suspendisse.",
|
"Nascetur ipsum a nisi tempor cras nam neque volutpat. Aliquam id est faucibus nunc quis. Eleifend suspendisse.",
|
||||||
status: "warning",
|
status: "warning",
|
||||||
type: "toast",
|
type: "toast",
|
||||||
|
},
|
||||||
|
render: Template,
|
||||||
};
|
};
|
||||||
|
export const ToastDanger = {
|
||||||
export const ToastDanger = Template.bind({});
|
args: {
|
||||||
ToastDanger.args = {
|
|
||||||
title: "Short alert toast message goes here",
|
title: "Short alert toast message goes here",
|
||||||
description:
|
description:
|
||||||
"Nascetur ipsum a nisi tempor cras nam neque volutpat. Aliquam id est faucibus nunc quis. Eleifend suspendisse.",
|
"Nascetur ipsum a nisi tempor cras nam neque volutpat. Aliquam id est faucibus nunc quis. Eleifend suspendisse.",
|
||||||
status: "danger",
|
status: "danger",
|
||||||
type: "toast",
|
type: "toast",
|
||||||
|
},
|
||||||
|
render: Template,
|
||||||
};
|
};
|
||||||
|
export const Banner = {
|
||||||
export const Banner = Template.bind({});
|
args: {
|
||||||
Banner.args = {
|
|
||||||
title: "Short alert banner message goes here",
|
title: "Short alert banner message goes here",
|
||||||
description:
|
description:
|
||||||
"Nascetur ipsum a nisi tempor cras nam neque volutpat. Aliquam id est faucibus nunc quis. Eleifend suspendisse.",
|
"Nascetur ipsum a nisi tempor cras nam neque volutpat. Aliquam id est faucibus nunc quis. Eleifend suspendisse.",
|
||||||
status: "default",
|
status: "default",
|
||||||
type: "banner",
|
type: "banner",
|
||||||
|
},
|
||||||
|
render: Template,
|
||||||
};
|
};
|
||||||
|
export const BannerPositive = {
|
||||||
export const BannerPositive = Template.bind({});
|
args: {
|
||||||
BannerPositive.args = {
|
|
||||||
title: "Short alert banner message goes here",
|
title: "Short alert banner message goes here",
|
||||||
description:
|
description:
|
||||||
"Nascetur ipsum a nisi tempor cras nam neque volutpat. Aliquam id est faucibus nunc quis. Eleifend suspendisse.",
|
"Nascetur ipsum a nisi tempor cras nam neque volutpat. Aliquam id est faucibus nunc quis. Eleifend suspendisse.",
|
||||||
status: "positive",
|
status: "positive",
|
||||||
type: "banner",
|
type: "banner",
|
||||||
|
},
|
||||||
|
render: Template,
|
||||||
};
|
};
|
||||||
|
export const BannerWarning = {
|
||||||
export const BannerWarning = Template.bind({});
|
args: {
|
||||||
BannerWarning.args = {
|
|
||||||
title: "Short alert banner message goes here",
|
title: "Short alert banner message goes here",
|
||||||
description:
|
description:
|
||||||
"Nascetur ipsum a nisi tempor cras nam neque volutpat. Aliquam id est faucibus nunc quis. Eleifend suspendisse.",
|
"Nascetur ipsum a nisi tempor cras nam neque volutpat. Aliquam id est faucibus nunc quis. Eleifend suspendisse.",
|
||||||
status: "warning",
|
status: "warning",
|
||||||
type: "banner",
|
type: "banner",
|
||||||
|
},
|
||||||
|
render: Template,
|
||||||
};
|
};
|
||||||
|
export const BannerDanger = {
|
||||||
export const BannerDanger = Template.bind({});
|
args: {
|
||||||
BannerDanger.args = {
|
|
||||||
title: "Short alert banner message goes here",
|
title: "Short alert banner message goes here",
|
||||||
description:
|
description:
|
||||||
"Nascetur ipsum a nisi tempor cras nam neque volutpat. Aliquam id est faucibus nunc quis. Eleifend suspendisse.",
|
"Nascetur ipsum a nisi tempor cras nam neque volutpat. Aliquam id est faucibus nunc quis. Eleifend suspendisse.",
|
||||||
status: "danger",
|
status: "danger",
|
||||||
type: "banner",
|
type: "banner",
|
||||||
|
},
|
||||||
|
render: Template,
|
||||||
};
|
};
|
||||||
|
export const TitleOnly = {
|
||||||
export const TitleOnly = Template.bind({});
|
args: {
|
||||||
TitleOnly.args = {
|
|
||||||
title: "Short alert banner message goes here",
|
title: "Short alert banner message goes here",
|
||||||
status: "default",
|
status: "default",
|
||||||
type: "toast",
|
type: "toast",
|
||||||
|
},
|
||||||
|
render: Template,
|
||||||
};
|
};
|
||||||
|
export const ToastSmall = {
|
||||||
export const ToastSmall = Template.bind({});
|
args: {
|
||||||
ToastSmall.args = {
|
|
||||||
title: "Short alert toast message goes here",
|
title: "Short alert toast message goes here",
|
||||||
description:
|
description:
|
||||||
"Nascetur ipsum a nisi tempor cras nam neque volutpat. Aliquam id est faucibus nunc quis. Eleifend suspendisse.",
|
"Nascetur ipsum a nisi tempor cras nam neque volutpat. Aliquam id est faucibus nunc quis. Eleifend suspendisse.",
|
||||||
status: "default",
|
status: "default",
|
||||||
type: "toast",
|
type: "toast",
|
||||||
size: "s",
|
size: "s",
|
||||||
|
},
|
||||||
|
render: Template,
|
||||||
};
|
};
|
||||||
|
export const BannerSmall = {
|
||||||
export const BannerSmall = Template.bind({});
|
args: {
|
||||||
BannerSmall.args = {
|
|
||||||
title: "Short alert banner message goes here",
|
title: "Short alert banner message goes here",
|
||||||
description:
|
description:
|
||||||
"Nascetur ipsum a nisi tempor cras nam neque volutpat. Aliquam id est faucibus nunc quis. Eleifend suspendisse.",
|
"Nascetur ipsum a nisi tempor cras nam neque volutpat. Aliquam id est faucibus nunc quis. Eleifend suspendisse.",
|
||||||
status: "positive",
|
status: "positive",
|
||||||
type: "banner",
|
type: "banner",
|
||||||
size: "s",
|
size: "s",
|
||||||
|
},
|
||||||
|
render: Template,
|
||||||
};
|
};
|
||||||
|
|
||||||
const NoDismissTemplate = (args) => (
|
const NoDismissTemplate = (args) => (
|
||||||
<div className="p-8 max-w-[600px]">
|
<div className="p-8 max-w-[600px]">
|
||||||
<Alert {...args} />
|
<Alert {...args} />
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
export const BannerNoDismiss = NoDismissTemplate.bind({});
|
export const BannerNoDismiss = {
|
||||||
BannerNoDismiss.args = {
|
args: {
|
||||||
title: "Non-dismissible banner (no onClose)",
|
title: "Non-dismissible banner (no onClose)",
|
||||||
description: "Used when the message clears via navigation or parent state only.",
|
description:
|
||||||
|
"Used when the message clears via navigation or parent state only.",
|
||||||
status: "danger",
|
status: "danger",
|
||||||
type: "banner",
|
type: "banner",
|
||||||
size: "s",
|
size: "s",
|
||||||
|
},
|
||||||
|
render: NoDismissTemplate,
|
||||||
};
|
};
|
||||||
|
|
||||||
export const AllStatuses = () => {
|
export const AllStatuses = () => {
|
||||||
const [visible, setVisible] = useState({
|
const [visible, setVisible] = useState({
|
||||||
default: true,
|
default: true,
|
||||||
|
|||||||
@@ -54,8 +54,8 @@ const Template = (args) => {
|
|||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|
||||||
export const Default = Template.bind({});
|
export const Default = {
|
||||||
Default.args = {
|
args: {
|
||||||
isOpen: true,
|
isOpen: true,
|
||||||
title: "What do you call your group's new policy?",
|
title: "What do you call your group's new policy?",
|
||||||
description: "You can also combine or add new approaches to the list",
|
description: "You can also combine or add new approaches to the list",
|
||||||
@@ -72,10 +72,11 @@ Default.args = {
|
|||||||
backButtonText: "Back",
|
backButtonText: "Back",
|
||||||
nextButtonText: "Next",
|
nextButtonText: "Next",
|
||||||
nextButtonDisabled: false,
|
nextButtonDisabled: false,
|
||||||
|
},
|
||||||
|
render: Template,
|
||||||
};
|
};
|
||||||
|
export const WithStepper = {
|
||||||
export const WithStepper = Template.bind({});
|
args: {
|
||||||
WithStepper.args = {
|
|
||||||
isOpen: true,
|
isOpen: true,
|
||||||
title: "What do you call your group's new policy?",
|
title: "What do you call your group's new policy?",
|
||||||
description: "You can also combine or add new approaches to the list",
|
description: "You can also combine or add new approaches to the list",
|
||||||
@@ -94,10 +95,11 @@ WithStepper.args = {
|
|||||||
nextButtonDisabled: false,
|
nextButtonDisabled: false,
|
||||||
currentStep: 1,
|
currentStep: 1,
|
||||||
totalSteps: 3,
|
totalSteps: 3,
|
||||||
|
},
|
||||||
|
render: Template,
|
||||||
};
|
};
|
||||||
|
export const Step2 = {
|
||||||
export const Step2 = Template.bind({});
|
args: {
|
||||||
Step2.args = {
|
|
||||||
isOpen: true,
|
isOpen: true,
|
||||||
title: "How should conflicts be resolved?",
|
title: "How should conflicts be resolved?",
|
||||||
description: "You can also combine or add new approaches to the list",
|
description: "You can also combine or add new approaches to the list",
|
||||||
@@ -113,10 +115,11 @@ Step2.args = {
|
|||||||
nextButtonDisabled: false,
|
nextButtonDisabled: false,
|
||||||
currentStep: 2,
|
currentStep: 2,
|
||||||
totalSteps: 3,
|
totalSteps: 3,
|
||||||
|
},
|
||||||
|
render: Template,
|
||||||
};
|
};
|
||||||
|
export const Step3 = {
|
||||||
export const Step3 = Template.bind({});
|
args: {
|
||||||
Step3.args = {
|
|
||||||
isOpen: true,
|
isOpen: true,
|
||||||
title: "Final step",
|
title: "Final step",
|
||||||
description: "Review your settings",
|
description: "Review your settings",
|
||||||
@@ -134,10 +137,11 @@ Step3.args = {
|
|||||||
nextButtonDisabled: false,
|
nextButtonDisabled: false,
|
||||||
currentStep: 3,
|
currentStep: 3,
|
||||||
totalSteps: 3,
|
totalSteps: 3,
|
||||||
|
},
|
||||||
|
render: Template,
|
||||||
};
|
};
|
||||||
|
export const WithCustomHeader = {
|
||||||
export const WithCustomHeader = Template.bind({});
|
args: {
|
||||||
WithCustomHeader.args = {
|
|
||||||
isOpen: true,
|
isOpen: true,
|
||||||
headerContent: <div className="text-lg font-semibold">Custom header</div>,
|
headerContent: <div className="text-lg font-semibold">Custom header</div>,
|
||||||
children: (
|
children: (
|
||||||
@@ -151,10 +155,11 @@ WithCustomHeader.args = {
|
|||||||
showBackButton: false,
|
showBackButton: false,
|
||||||
showNextButton: true,
|
showNextButton: true,
|
||||||
nextButtonText: "Continue",
|
nextButtonText: "Continue",
|
||||||
|
},
|
||||||
|
render: Template,
|
||||||
};
|
};
|
||||||
|
export const WithoutFooter = {
|
||||||
export const WithoutFooter = Template.bind({});
|
args: {
|
||||||
WithoutFooter.args = {
|
|
||||||
isOpen: true,
|
isOpen: true,
|
||||||
title: "Simple Create Dialog",
|
title: "Simple Create Dialog",
|
||||||
description: "This create dialog has no footer buttons",
|
description: "This create dialog has no footer buttons",
|
||||||
@@ -167,13 +172,15 @@ WithoutFooter.args = {
|
|||||||
),
|
),
|
||||||
showBackButton: false,
|
showBackButton: false,
|
||||||
showNextButton: false,
|
showNextButton: false,
|
||||||
|
},
|
||||||
|
render: Template,
|
||||||
};
|
};
|
||||||
|
export const LoginYellowBackdrop = {
|
||||||
export const LoginYellowBackdrop = Template.bind({});
|
args: {
|
||||||
LoginYellowBackdrop.args = {
|
|
||||||
isOpen: true,
|
isOpen: true,
|
||||||
title: "Horizontalism",
|
title: "Horizontalism",
|
||||||
description: "Edit or add to this description to describe what this value means to your community.",
|
description:
|
||||||
|
"Edit or add to this description to describe what this value means to your community.",
|
||||||
backdropVariant: "blurredYellow",
|
backdropVariant: "blurredYellow",
|
||||||
children: (
|
children: (
|
||||||
<div className="space-y-4">
|
<div className="space-y-4">
|
||||||
@@ -186,10 +193,11 @@ LoginYellowBackdrop.args = {
|
|||||||
showNextButton: true,
|
showNextButton: true,
|
||||||
nextButtonText: "Add Value",
|
nextButtonText: "Add Value",
|
||||||
nextButtonDisabled: false,
|
nextButtonDisabled: false,
|
||||||
|
},
|
||||||
|
render: Template,
|
||||||
};
|
};
|
||||||
|
export const NextButtonDisabled = {
|
||||||
export const NextButtonDisabled = Template.bind({});
|
args: {
|
||||||
NextButtonDisabled.args = {
|
|
||||||
isOpen: true,
|
isOpen: true,
|
||||||
title: "What do you call your group's new policy?",
|
title: "What do you call your group's new policy?",
|
||||||
description: "You can also combine or add new approaches to the list",
|
description: "You can also combine or add new approaches to the list",
|
||||||
@@ -208,4 +216,6 @@ NextButtonDisabled.args = {
|
|||||||
nextButtonDisabled: true,
|
nextButtonDisabled: true,
|
||||||
currentStep: 1,
|
currentStep: 1,
|
||||||
totalSteps: 3,
|
totalSteps: 3,
|
||||||
|
},
|
||||||
|
render: Template,
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -1,6 +1,9 @@
|
|||||||
import React, { Suspense, useEffect } from "react";
|
import React, { Suspense, useEffect } from "react";
|
||||||
import Login from "../../app/components/modals/Login";
|
import Login from "../../app/components/modals/Login";
|
||||||
import LoginForm from "../../app/components/modals/Login/LoginForm";
|
import LoginForm from "../../app/components/modals/Login/LoginForm";
|
||||||
|
import messages from "../../messages/en/index";
|
||||||
|
|
||||||
|
const backToHome = messages.pages.login.backToHome;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Storybook runs outside Next.js request context; successful "Send link" needs fetch mocked
|
* Storybook runs outside Next.js request context; successful "Send link" needs fetch mocked
|
||||||
@@ -43,11 +46,11 @@ function FakeMarketingPageBehindOverlay({
|
|||||||
return (
|
return (
|
||||||
<div className="relative min-h-[100dvh] overflow-hidden">
|
<div className="relative min-h-[100dvh] overflow-hidden">
|
||||||
<div
|
<div
|
||||||
className="absolute inset-0 bg-gradient-to-br from-amber-100 via-white to-amber-50"
|
className="absolute inset-0 bg-[var(--color-surface-inverse-brand-primary)]"
|
||||||
aria-hidden
|
aria-hidden
|
||||||
/>
|
/>
|
||||||
<div className="relative z-0 px-8 py-16">
|
<div className="relative z-0 px-8 py-16">
|
||||||
<p className="font-inter max-w-md text-lg text-neutral-800">
|
<p className="max-w-md text-large-paragraph text-[var(--color-content-invert-primary)]">
|
||||||
Placeholder page content — the login overlay portals above this and
|
Placeholder page content — the login overlay portals above this and
|
||||||
uses backdrop blur (`blurredYellow`).
|
uses backdrop blur (`blurredYellow`).
|
||||||
</p>
|
</p>
|
||||||
@@ -107,13 +110,13 @@ export const HeaderOverlayBlurred = {
|
|||||||
belowCard={
|
belowCard={
|
||||||
<a
|
<a
|
||||||
href="/"
|
href="/"
|
||||||
className="font-inter font-normal text-[14px] leading-[20px] text-[var(--color-content-invert-tertiary,#2d2d2d)] text-center hover:opacity-90"
|
className="text-center text-small-paragraph text-[var(--color-content-invert-tertiary)] hover:opacity-90"
|
||||||
>
|
>
|
||||||
← Back to home
|
{backToHome}
|
||||||
</a>
|
</a>
|
||||||
}
|
}
|
||||||
>
|
>
|
||||||
<Suspense fallback={<p className="font-inter p-6">Loading…</p>}>
|
<Suspense fallback={<p className="p-6 text-small-paragraph">Loading…</p>}>
|
||||||
<LoginForm />
|
<LoginForm />
|
||||||
</Suspense>
|
</Suspense>
|
||||||
</Login>
|
</Login>
|
||||||
@@ -152,13 +155,13 @@ export const FullPageRouteSolid = {
|
|||||||
belowCard={
|
belowCard={
|
||||||
<a
|
<a
|
||||||
href="/"
|
href="/"
|
||||||
className="font-inter font-normal text-[14px] leading-[20px] text-[var(--color-content-invert-tertiary,#2d2d2d)] text-center hover:opacity-90"
|
className="text-center text-small-paragraph text-[var(--color-content-invert-tertiary)] hover:opacity-90"
|
||||||
>
|
>
|
||||||
← Back to home
|
{backToHome}
|
||||||
</a>
|
</a>
|
||||||
}
|
}
|
||||||
>
|
>
|
||||||
<Suspense fallback={<p className="font-inter p-6">Loading…</p>}>
|
<Suspense fallback={<p className="p-6 text-small-paragraph">Loading…</p>}>
|
||||||
<LoginForm />
|
<LoginForm />
|
||||||
</Suspense>
|
</Suspense>
|
||||||
</Login>
|
</Login>
|
||||||
@@ -178,15 +181,15 @@ export const ModalChromeOnly = {
|
|||||||
belowCard={
|
belowCard={
|
||||||
<a
|
<a
|
||||||
href="/"
|
href="/"
|
||||||
className="font-inter font-normal text-[14px] leading-[20px] text-[var(--color-content-invert-tertiary,#2d2d2d)] text-center hover:opacity-90"
|
className="text-center text-small-paragraph text-[var(--color-content-invert-tertiary)] hover:opacity-90"
|
||||||
>
|
>
|
||||||
← Back to home
|
{backToHome}
|
||||||
</a>
|
</a>
|
||||||
}
|
}
|
||||||
>
|
>
|
||||||
<p
|
<p
|
||||||
id="login-modal-heading"
|
id="login-modal-heading"
|
||||||
className="font-inter px-2 py-4 text-[var(--color-content-default-primary)]"
|
className="px-2 py-4 text-[var(--color-content-default-primary)]"
|
||||||
>
|
>
|
||||||
Placeholder body — use "Header overlay" or "Full-page
|
Placeholder body — use "Header overlay" or "Full-page
|
||||||
route" for the real flow.
|
route" for the real flow.
|
||||||
@@ -215,7 +218,7 @@ export const FormOnly = {
|
|||||||
],
|
],
|
||||||
render: () => (
|
render: () => (
|
||||||
<div className="mx-auto max-w-[560px] rounded-[20px] bg-[var(--color-surface-default-primary)] p-6 shadow-lg">
|
<div className="mx-auto max-w-[560px] rounded-[20px] bg-[var(--color-surface-default-primary)] p-6 shadow-lg">
|
||||||
<Suspense fallback={<p className="font-inter">Loading…</p>}>
|
<Suspense fallback={<p className="text-small-paragraph">Loading…</p>}>
|
||||||
<LoginForm />
|
<LoginForm />
|
||||||
</Suspense>
|
</Suspense>
|
||||||
</div>
|
</div>
|
||||||
|
|||||||
@@ -1,22 +1,28 @@
|
|||||||
import React, { useState } from "react";
|
import React, { useState } from "react";
|
||||||
import Share from "../../app/components/modals/Share";
|
import Share from "../../app/components/modals/Share";
|
||||||
import { MessagesProvider } from "../../app/contexts/MessagesContext";
|
|
||||||
import messages from "../../messages/en/index";
|
|
||||||
|
|
||||||
/** Figma: Modal / Share — node 22073-30884 (Community Rule System). */
|
/** Figma: Modal / Share — node 22073-30884 (Community Rule System). */
|
||||||
export default {
|
export default {
|
||||||
title: "modals/Share",
|
title: "Components/Modals/Share",
|
||||||
component: Share,
|
component: Share,
|
||||||
|
parameters: {
|
||||||
|
layout: "fullscreen",
|
||||||
|
docs: {
|
||||||
|
description: {
|
||||||
|
component:
|
||||||
|
"Share modal for published rules (copy link and channel actions).",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
};
|
};
|
||||||
|
|
||||||
function ShareStoryHost() {
|
function ShareStoryHost() {
|
||||||
const [open, setOpen] = useState(true);
|
const [open, setOpen] = useState(true);
|
||||||
return (
|
return (
|
||||||
<MessagesProvider messages={messages}>
|
|
||||||
<div className="min-h-[100dvh] bg-[var(--color-surface-inverse-brand-primary)] p-6">
|
<div className="min-h-[100dvh] bg-[var(--color-surface-inverse-brand-primary)] p-6">
|
||||||
<button
|
<button
|
||||||
type="button"
|
type="button"
|
||||||
className="rounded-md bg-white px-4 py-2 text-sm text-black"
|
className="rounded-md bg-[var(--color-surface-default-primary)] px-4 py-2 text-small-paragraph text-[var(--color-content-default-primary)]"
|
||||||
onClick={() => setOpen(true)}
|
onClick={() => setOpen(true)}
|
||||||
>
|
>
|
||||||
Open Share
|
Open Share
|
||||||
@@ -31,11 +37,9 @@ function ShareStoryHost() {
|
|||||||
onDiscordShare={() => {}}
|
onDiscordShare={() => {}}
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
</MessagesProvider>
|
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
export const Default = {
|
export const Default = {
|
||||||
name: "Modal / Share",
|
|
||||||
render: () => <ShareStoryHost />,
|
render: () => <ShareStoryHost />,
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -1,6 +1,7 @@
|
|||||||
import React from "react";
|
import React from "react";
|
||||||
import Tooltip from "../../app/components/modals/Tooltip";
|
import Tooltip from "../../app/components/modals/Tooltip";
|
||||||
import Button from "../../app/components/buttons/Button";
|
import Button from "../../app/components/buttons/Button";
|
||||||
|
import { TOOLTIP_POSITION_OPTIONS } from "../../lib/propNormalization";
|
||||||
|
|
||||||
export default {
|
export default {
|
||||||
title: "Components/Modals/Tooltip",
|
title: "Components/Modals/Tooltip",
|
||||||
@@ -8,7 +9,7 @@ export default {
|
|||||||
argTypes: {
|
argTypes: {
|
||||||
position: {
|
position: {
|
||||||
control: { type: "select" },
|
control: { type: "select" },
|
||||||
options: ["top", "bottom"],
|
options: [...TOOLTIP_POSITION_OPTIONS],
|
||||||
},
|
},
|
||||||
disabled: {
|
disabled: {
|
||||||
control: { type: "boolean" },
|
control: { type: "boolean" },
|
||||||
@@ -29,37 +30,42 @@ const Template = (args) => (
|
|||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
|
|
||||||
export const Default = Template.bind({});
|
export const Default = {
|
||||||
Default.args = {
|
args: {
|
||||||
text: "Tooltip text goes here",
|
text: "Tooltip text goes here",
|
||||||
position: "top",
|
position: "top",
|
||||||
disabled: false,
|
disabled: false,
|
||||||
|
},
|
||||||
|
render: Template,
|
||||||
};
|
};
|
||||||
|
export const Top = {
|
||||||
export const Top = Template.bind({});
|
args: {
|
||||||
Top.args = {
|
|
||||||
text: "Tooltip positioned at top",
|
text: "Tooltip positioned at top",
|
||||||
position: "top",
|
position: "top",
|
||||||
|
},
|
||||||
|
render: Template,
|
||||||
};
|
};
|
||||||
|
export const Bottom = {
|
||||||
export const Bottom = Template.bind({});
|
args: {
|
||||||
Bottom.args = {
|
|
||||||
text: "Tooltip positioned at bottom",
|
text: "Tooltip positioned at bottom",
|
||||||
position: "bottom",
|
position: "bottom",
|
||||||
|
},
|
||||||
|
render: Template,
|
||||||
};
|
};
|
||||||
|
export const Disabled = {
|
||||||
export const Disabled = Template.bind({});
|
args: {
|
||||||
Disabled.args = {
|
|
||||||
text: "This tooltip is disabled",
|
text: "This tooltip is disabled",
|
||||||
disabled: true,
|
disabled: true,
|
||||||
|
},
|
||||||
|
render: Template,
|
||||||
};
|
};
|
||||||
|
export const LongText = {
|
||||||
export const LongText = Template.bind({});
|
args: {
|
||||||
LongText.args = {
|
|
||||||
text: "This is a longer tooltip text that demonstrates how the component handles multiple words and extended content",
|
text: "This is a longer tooltip text that demonstrates how the component handles multiple words and extended content",
|
||||||
position: "top",
|
position: "top",
|
||||||
|
},
|
||||||
|
render: Template,
|
||||||
};
|
};
|
||||||
|
|
||||||
export const WithIcon = () => (
|
export const WithIcon = () => (
|
||||||
<div className="p-16 flex items-center justify-center min-h-[200px]">
|
<div className="p-16 flex items-center justify-center min-h-[200px]">
|
||||||
<Tooltip text="Tooltip with icon button" position="top">
|
<Tooltip text="Tooltip with icon button" position="top">
|
||||||
|
|||||||
@@ -1,4 +1,8 @@
|
|||||||
import MenuItem from "../../app/components/navigation/MenuItem";
|
import MenuItem from "../../app/components/navigation/MenuItem";
|
||||||
|
import {
|
||||||
|
MENU_ITEM_MODE_OPTIONS,
|
||||||
|
MENU_ITEM_SIZE_OPTIONS,
|
||||||
|
} from "../../lib/propNormalization";
|
||||||
|
|
||||||
export default {
|
export default {
|
||||||
title: "Components/Navigation/MenuItem",
|
title: "Components/Navigation/MenuItem",
|
||||||
@@ -15,12 +19,12 @@ export default {
|
|||||||
argTypes: {
|
argTypes: {
|
||||||
mode: {
|
mode: {
|
||||||
control: { type: "select" },
|
control: { type: "select" },
|
||||||
options: ["default", "inverse"],
|
options: [...MENU_ITEM_MODE_OPTIONS],
|
||||||
description: "The visual style mode of the menu item",
|
description: "The visual style mode of the menu item",
|
||||||
},
|
},
|
||||||
size: {
|
size: {
|
||||||
control: { type: "select" },
|
control: { type: "select" },
|
||||||
options: ["X Small", "Small", "Medium", "Large", "X Large"],
|
options: [...MENU_ITEM_SIZE_OPTIONS],
|
||||||
description: "The size of the menu item",
|
description: "The size of the menu item",
|
||||||
},
|
},
|
||||||
disabled: {
|
disabled: {
|
||||||
|
|||||||
@@ -0,0 +1,9 @@
|
|||||||
|
import { CommunityStructureSelectScreen } from "../../app/(app)/create/screens/select/CommunityStructureSelectScreen";
|
||||||
|
|
||||||
|
export default {
|
||||||
|
title: "Pages/Create Flow/Community structure",
|
||||||
|
component: CommunityStructureSelectScreen,
|
||||||
|
parameters: { layout: "fullscreen" },
|
||||||
|
};
|
||||||
|
|
||||||
|
export const Default = {};
|
||||||
@@ -0,0 +1,17 @@
|
|||||||
|
import { ConflictManagementScreen } from "../../app/(app)/create/screens/card/ConflictManagementScreen";
|
||||||
|
|
||||||
|
export default {
|
||||||
|
title: "Pages/Create Flow/Conflict management",
|
||||||
|
component: ConflictManagementScreen,
|
||||||
|
parameters: {
|
||||||
|
layout: "fullscreen",
|
||||||
|
docs: {
|
||||||
|
description: {
|
||||||
|
component:
|
||||||
|
"Conflict management step: card stack, customize modals, responsive layout.",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
};
|
||||||
|
|
||||||
|
export const Default = {};
|
||||||
@@ -0,0 +1,9 @@
|
|||||||
|
import { CoreValuesSelectScreen } from "../../app/(app)/create/screens/select/CoreValuesSelectScreen";
|
||||||
|
|
||||||
|
export default {
|
||||||
|
title: "Pages/Create Flow/Core values",
|
||||||
|
component: CoreValuesSelectScreen,
|
||||||
|
parameters: { layout: "fullscreen" },
|
||||||
|
};
|
||||||
|
|
||||||
|
export const Default = {};
|
||||||
@@ -1,7 +1,7 @@
|
|||||||
import { InformationalScreen } from "../../app/(app)/create/screens/informational/InformationalScreen";
|
import { InformationalScreen } from "../../app/(app)/create/screens/informational/InformationalScreen";
|
||||||
|
|
||||||
export default {
|
export default {
|
||||||
title: "Pages/Create/Informational",
|
title: "Pages/Create Flow/Informational",
|
||||||
component: InformationalScreen,
|
component: InformationalScreen,
|
||||||
parameters: { layout: "fullscreen" },
|
parameters: { layout: "fullscreen" },
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -0,0 +1,17 @@
|
|||||||
|
import { MembershipMethodsScreen } from "../../app/(app)/create/screens/card/MembershipMethodsScreen";
|
||||||
|
|
||||||
|
export default {
|
||||||
|
title: "Pages/Create Flow/Membership methods",
|
||||||
|
component: MembershipMethodsScreen,
|
||||||
|
parameters: {
|
||||||
|
layout: "fullscreen",
|
||||||
|
docs: {
|
||||||
|
description: {
|
||||||
|
component:
|
||||||
|
"Membership methods step: card stack, customize modals, responsive layout.",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
};
|
||||||
|
|
||||||
|
export const Default = {};
|
||||||
@@ -0,0 +1,67 @@
|
|||||||
|
import { useEffect, useRef } from "react";
|
||||||
|
import { PublishedStakeholdersManagePanel } from "../../app/(app)/create/screens/select/PublishedStakeholdersManagePanel";
|
||||||
|
|
||||||
|
function StakeholdersFetchMock({ children }) {
|
||||||
|
const origRef = useRef(undefined);
|
||||||
|
if (origRef.current === undefined) {
|
||||||
|
origRef.current = globalThis.fetch;
|
||||||
|
globalThis.fetch = async (input, init) => {
|
||||||
|
const url =
|
||||||
|
typeof input === "string"
|
||||||
|
? input
|
||||||
|
: input instanceof URL
|
||||||
|
? input.href
|
||||||
|
: input.url;
|
||||||
|
if (url.includes("/stakeholders")) {
|
||||||
|
if (init?.method && init.method !== "GET") {
|
||||||
|
return new Response(JSON.stringify({ ok: true }), {
|
||||||
|
status: 200,
|
||||||
|
headers: { "Content-Type": "application/json" },
|
||||||
|
});
|
||||||
|
}
|
||||||
|
return new Response(JSON.stringify({ stakeholders: [] }), {
|
||||||
|
status: 200,
|
||||||
|
headers: { "Content-Type": "application/json" },
|
||||||
|
});
|
||||||
|
}
|
||||||
|
return origRef.current(input, init);
|
||||||
|
};
|
||||||
|
}
|
||||||
|
useEffect(() => {
|
||||||
|
return () => {
|
||||||
|
if (origRef.current) {
|
||||||
|
globalThis.fetch = origRef.current;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
}, []);
|
||||||
|
return children;
|
||||||
|
}
|
||||||
|
|
||||||
|
export default {
|
||||||
|
title: "Pages/Create Flow/Published stakeholders",
|
||||||
|
component: PublishedStakeholdersManagePanel,
|
||||||
|
parameters: {
|
||||||
|
layout: "fullscreen",
|
||||||
|
docs: {
|
||||||
|
description: {
|
||||||
|
component:
|
||||||
|
"Manage invites on a published rule. Fetch is stubbed empty in this story.",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
decorators: [
|
||||||
|
(Story) => (
|
||||||
|
<StakeholdersFetchMock>
|
||||||
|
<div className="min-h-screen bg-[var(--color-surface-default-primary)] p-8">
|
||||||
|
<Story />
|
||||||
|
</div>
|
||||||
|
</StakeholdersFetchMock>
|
||||||
|
),
|
||||||
|
],
|
||||||
|
};
|
||||||
|
|
||||||
|
export const Default = {
|
||||||
|
args: {
|
||||||
|
ruleId: "storybook-rule",
|
||||||
|
},
|
||||||
|
};
|
||||||
@@ -1,7 +1,7 @@
|
|||||||
import { CommunityReviewScreen } from "../../app/(app)/create/screens/review/CommunityReviewScreen";
|
import { CommunityReviewScreen } from "../../app/(app)/create/screens/review/CommunityReviewScreen";
|
||||||
|
|
||||||
export default {
|
export default {
|
||||||
title: "Pages/Create/Review",
|
title: "Pages/Create Flow/Review",
|
||||||
component: CommunityReviewScreen,
|
component: CommunityReviewScreen,
|
||||||
parameters: { layout: "fullscreen" },
|
parameters: { layout: "fullscreen" },
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -1,7 +1,7 @@
|
|||||||
import { CommunitySizeSelectScreen } from "../../app/(app)/create/screens/select/CommunitySizeSelectScreen";
|
import { CommunitySizeSelectScreen } from "../../app/(app)/create/screens/select/CommunitySizeSelectScreen";
|
||||||
|
|
||||||
export default {
|
export default {
|
||||||
title: "Pages/Create/CommunitySize",
|
title: "Pages/Create Flow/Community size",
|
||||||
component: CommunitySizeSelectScreen,
|
component: CommunitySizeSelectScreen,
|
||||||
parameters: { layout: "fullscreen" },
|
parameters: { layout: "fullscreen" },
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -1,7 +1,7 @@
|
|||||||
import { CreateFlowTextFieldScreen } from "../../app/(app)/create/screens/text/CreateFlowTextFieldScreen";
|
import { CreateFlowTextFieldScreen } from "../../app/(app)/create/screens/text/CreateFlowTextFieldScreen";
|
||||||
|
|
||||||
export default {
|
export default {
|
||||||
title: "Pages/Create/CommunityName",
|
title: "Pages/Create Flow/Community name",
|
||||||
component: CreateFlowTextFieldScreen,
|
component: CreateFlowTextFieldScreen,
|
||||||
parameters: { layout: "fullscreen" },
|
parameters: { layout: "fullscreen" },
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -1,7 +1,7 @@
|
|||||||
import { CommunityUploadScreen } from "../../app/(app)/create/screens/upload/CommunityUploadScreen";
|
import { CommunityUploadScreen } from "../../app/(app)/create/screens/upload/CommunityUploadScreen";
|
||||||
|
|
||||||
export default {
|
export default {
|
||||||
title: "Pages/Create/CommunityUpload",
|
title: "Pages/Create Flow/Community upload",
|
||||||
component: CommunityUploadScreen,
|
component: CommunityUploadScreen,
|
||||||
parameters: { layout: "fullscreen" },
|
parameters: { layout: "fullscreen" },
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -0,0 +1,38 @@
|
|||||||
|
import FaqAccordion from "../../app/components/sections/Accordion";
|
||||||
|
import about from "../../messages/en/pages/about.json";
|
||||||
|
|
||||||
|
export default {
|
||||||
|
title: "Components/Sections/Accordion",
|
||||||
|
component: FaqAccordion,
|
||||||
|
parameters: {
|
||||||
|
layout: "fullscreen",
|
||||||
|
docs: {
|
||||||
|
description: {
|
||||||
|
component:
|
||||||
|
"About-page FAQ wrapper over layout/Accordion. Sizes: s below lg, m at lg, l at xl.",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
argTypes: {
|
||||||
|
title: { control: { type: "text" } },
|
||||||
|
size: {
|
||||||
|
control: { type: "select" },
|
||||||
|
options: ["s", "m", "l"],
|
||||||
|
},
|
||||||
|
lgSize: {
|
||||||
|
control: { type: "select" },
|
||||||
|
options: ["s", "m", "l"],
|
||||||
|
},
|
||||||
|
xlSize: {
|
||||||
|
control: { type: "select" },
|
||||||
|
options: ["s", "m", "l"],
|
||||||
|
},
|
||||||
|
},
|
||||||
|
};
|
||||||
|
|
||||||
|
export const Default = {
|
||||||
|
args: {
|
||||||
|
title: about.faq.title,
|
||||||
|
items: about.faq.items,
|
||||||
|
},
|
||||||
|
};
|
||||||
@@ -1,248 +0,0 @@
|
|||||||
import HeroBanner from "../../app/components/sections/HeroBanner";
|
|
||||||
import ContentLockup from "../../app/components/type/ContentLockup";
|
|
||||||
import HeroDecor from "../../app/components/sections/HeroBanner/HeroDecor";
|
|
||||||
|
|
||||||
export default {
|
|
||||||
title: "Systems/HeroBanner System",
|
|
||||||
parameters: {
|
|
||||||
layout: "fullscreen",
|
|
||||||
docs: {
|
|
||||||
description: {
|
|
||||||
component:
|
|
||||||
"Complete HeroBanner system showcasing all nested components working together. This demonstrates the full responsive behavior and component integration.",
|
|
||||||
},
|
|
||||||
},
|
|
||||||
},
|
|
||||||
tags: ["autodocs"],
|
|
||||||
};
|
|
||||||
|
|
||||||
export const CompleteSystem = {
|
|
||||||
render: () => (
|
|
||||||
<div className="min-h-screen bg-gray-50">
|
|
||||||
<HeroBanner
|
|
||||||
title="Collaborate"
|
|
||||||
subtitle="with clarity"
|
|
||||||
description="Help your community make important decisions in a way that reflects its unique values."
|
|
||||||
ctaText="Learn how Community Rule works"
|
|
||||||
ctaHref="#"
|
|
||||||
/>
|
|
||||||
</div>
|
|
||||||
),
|
|
||||||
parameters: {
|
|
||||||
docs: {
|
|
||||||
description: {
|
|
||||||
story:
|
|
||||||
"Complete HeroBanner system with all components integrated. Resize your browser to see responsive behavior across all breakpoints.",
|
|
||||||
},
|
|
||||||
},
|
|
||||||
},
|
|
||||||
};
|
|
||||||
|
|
||||||
export const ComponentBreakdown = {
|
|
||||||
render: () => (
|
|
||||||
<div className="space-y-12 p-8">
|
|
||||||
<div>
|
|
||||||
<h2 className="text-2xl font-bold mb-6">HeroBanner Components</h2>
|
|
||||||
|
|
||||||
<div className="space-y-8">
|
|
||||||
<div>
|
|
||||||
<h3 className="text-lg font-semibold mb-4">
|
|
||||||
1. ContentLockup Component
|
|
||||||
</h3>
|
|
||||||
<div className="bg-[var(--color-surface-default-brand-primary)] p-8 rounded-lg">
|
|
||||||
<ContentLockup
|
|
||||||
title="Collaborate"
|
|
||||||
subtitle="with clarity"
|
|
||||||
description="Help your community make important decisions in a way that reflects its unique values."
|
|
||||||
ctaText="Learn how Community Rule works"
|
|
||||||
ctaHref="#"
|
|
||||||
/>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<div>
|
|
||||||
<h3 className="text-lg font-semibold mb-4">
|
|
||||||
2. HeroDecor Component
|
|
||||||
</h3>
|
|
||||||
<div className="bg-[var(--color-surface-default-brand-primary)] p-8 rounded-lg relative overflow-hidden h-64">
|
|
||||||
<HeroDecor className="w-full h-full" />
|
|
||||||
<div className="relative z-10 text-white mt-4">
|
|
||||||
<p>Decoration appears behind content</p>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<div>
|
|
||||||
<h3 className="text-lg font-semibold mb-4">
|
|
||||||
3. Complete HeroBanner
|
|
||||||
</h3>
|
|
||||||
<HeroBanner
|
|
||||||
title="Collaborate"
|
|
||||||
subtitle="with clarity"
|
|
||||||
description="Help your community make important decisions in a way that reflects its unique values."
|
|
||||||
ctaText="Learn how Community Rule works"
|
|
||||||
ctaHref="#"
|
|
||||||
/>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
),
|
|
||||||
parameters: {
|
|
||||||
docs: {
|
|
||||||
description: {
|
|
||||||
story:
|
|
||||||
"Breakdown of individual components that make up the HeroBanner system, showing how they work together.",
|
|
||||||
},
|
|
||||||
},
|
|
||||||
},
|
|
||||||
};
|
|
||||||
|
|
||||||
export const ResponsiveBreakpoints = {
|
|
||||||
render: () => (
|
|
||||||
<div className="space-y-8 p-8">
|
|
||||||
<h2 className="text-2xl font-bold">Responsive Breakpoints</h2>
|
|
||||||
|
|
||||||
<div className="space-y-6">
|
|
||||||
<div>
|
|
||||||
<h3 className="text-lg font-semibold mb-2">XSmall (≤429px)</h3>
|
|
||||||
<div
|
|
||||||
className="border-2 border-gray-300 rounded-lg overflow-hidden"
|
|
||||||
style={{ width: "400px" }}
|
|
||||||
>
|
|
||||||
<HeroBanner
|
|
||||||
title="Collaborate"
|
|
||||||
subtitle="with clarity"
|
|
||||||
description="Help your community make important decisions in a way that reflects its unique values."
|
|
||||||
ctaText="Learn how Community Rule works"
|
|
||||||
ctaHref="#"
|
|
||||||
/>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<div>
|
|
||||||
<h3 className="text-lg font-semibold mb-2">Small (430px+)</h3>
|
|
||||||
<div
|
|
||||||
className="border-2 border-gray-300 rounded-lg overflow-hidden"
|
|
||||||
style={{ width: "600px" }}
|
|
||||||
>
|
|
||||||
<HeroBanner
|
|
||||||
title="Collaborate"
|
|
||||||
subtitle="with clarity"
|
|
||||||
description="Help your community make important decisions in a way that reflects its unique values."
|
|
||||||
ctaText="Learn how Community Rule works"
|
|
||||||
ctaHref="#"
|
|
||||||
/>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<div>
|
|
||||||
<h3 className="text-lg font-semibold mb-2">Medium (768px+)</h3>
|
|
||||||
<div
|
|
||||||
className="border-2 border-gray-300 rounded-lg overflow-hidden"
|
|
||||||
style={{ width: "900px" }}
|
|
||||||
>
|
|
||||||
<HeroBanner
|
|
||||||
title="Collaborate"
|
|
||||||
subtitle="with clarity"
|
|
||||||
description="Help your community make important decisions in a way that reflects its unique values."
|
|
||||||
ctaText="Learn how Community Rule works"
|
|
||||||
ctaHref="#"
|
|
||||||
/>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<div>
|
|
||||||
<h3 className="text-lg font-semibold mb-2">Large (1024px+)</h3>
|
|
||||||
<div
|
|
||||||
className="border-2 border-gray-300 rounded-lg overflow-hidden"
|
|
||||||
style={{ width: "1200px" }}
|
|
||||||
>
|
|
||||||
<HeroBanner
|
|
||||||
title="Collaborate"
|
|
||||||
subtitle="with clarity"
|
|
||||||
description="Help your community make important decisions in a way that reflects its unique values."
|
|
||||||
ctaText="Learn how Community Rule works"
|
|
||||||
ctaHref="#"
|
|
||||||
/>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<div>
|
|
||||||
<h3 className="text-lg font-semibold mb-2">XLarge (1440px+)</h3>
|
|
||||||
<div
|
|
||||||
className="border-2 border-gray-300 rounded-lg overflow-hidden"
|
|
||||||
style={{ width: "1600px" }}
|
|
||||||
>
|
|
||||||
<HeroBanner
|
|
||||||
title="Collaborate"
|
|
||||||
subtitle="with clarity"
|
|
||||||
description="Help your community make important decisions in a way that reflects its unique values."
|
|
||||||
ctaText="Learn how Community Rule works"
|
|
||||||
ctaHref="#"
|
|
||||||
/>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
),
|
|
||||||
parameters: {
|
|
||||||
docs: {
|
|
||||||
description: {
|
|
||||||
story:
|
|
||||||
"HeroBanner system demonstrating responsive behavior at each breakpoint. Each container simulates a different screen size.",
|
|
||||||
},
|
|
||||||
},
|
|
||||||
},
|
|
||||||
};
|
|
||||||
|
|
||||||
export const ContentVariations = {
|
|
||||||
render: () => (
|
|
||||||
<div className="space-y-8 p-8">
|
|
||||||
<h2 className="text-2xl font-bold">Content Variations</h2>
|
|
||||||
|
|
||||||
<div className="space-y-6">
|
|
||||||
<div>
|
|
||||||
<h3 className="text-lg font-semibold mb-2">Standard Content</h3>
|
|
||||||
<HeroBanner
|
|
||||||
title="Collaborate"
|
|
||||||
subtitle="with clarity"
|
|
||||||
description="Help your community make important decisions in a way that reflects its unique values."
|
|
||||||
ctaText="Learn how Community Rule works"
|
|
||||||
ctaHref="#"
|
|
||||||
/>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<div>
|
|
||||||
<h3 className="text-lg font-semibold mb-2">Alternative Content</h3>
|
|
||||||
<HeroBanner
|
|
||||||
title="Build"
|
|
||||||
subtitle="better communities"
|
|
||||||
description="Create operating manuals that help your community thrive and make decisions together."
|
|
||||||
ctaText="Get started today"
|
|
||||||
ctaHref="/signup"
|
|
||||||
/>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<div>
|
|
||||||
<h3 className="text-lg font-semibold mb-2">Long Description</h3>
|
|
||||||
<HeroBanner
|
|
||||||
title="Collaborate"
|
|
||||||
subtitle="with clarity"
|
|
||||||
description="Help your community make important decisions in a way that reflects its unique values. Our platform provides the tools and frameworks needed to build successful, sustainable communities that can navigate complex challenges together."
|
|
||||||
ctaText="Learn how Community Rule works"
|
|
||||||
ctaHref="#"
|
|
||||||
/>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
),
|
|
||||||
parameters: {
|
|
||||||
docs: {
|
|
||||||
description: {
|
|
||||||
story:
|
|
||||||
"HeroBanner system with different content variations to demonstrate flexibility and content handling.",
|
|
||||||
},
|
|
||||||
},
|
|
||||||
},
|
|
||||||
};
|
|
||||||
@@ -1,4 +1,5 @@
|
|||||||
import QuoteBlock from "../../app/components/sections/QuoteBlock";
|
import QuoteBlock from "../../app/components/sections/QuoteBlock";
|
||||||
|
import { QUOTE_BLOCK_VARIANT_OPTIONS } from "../../lib/propNormalization";
|
||||||
|
|
||||||
export default {
|
export default {
|
||||||
title: "Components/Sections/QuoteBlock",
|
title: "Components/Sections/QuoteBlock",
|
||||||
@@ -34,7 +35,7 @@ A responsive quote section component that displays inspirational governance quot
|
|||||||
argTypes: {
|
argTypes: {
|
||||||
variant: {
|
variant: {
|
||||||
control: { type: "select" },
|
control: { type: "select" },
|
||||||
options: ["compact", "standard", "extended", "statement"],
|
options: [...QUOTE_BLOCK_VARIANT_OPTIONS],
|
||||||
description: "Layout variant for different use cases",
|
description: "Layout variant for different use cases",
|
||||||
},
|
},
|
||||||
quote: {
|
quote: {
|
||||||
|
|||||||
@@ -1,23 +1,23 @@
|
|||||||
import HeaderLockup from "../../app/components/type/HeaderLockup";
|
import HeaderLockup from "../../app/components/type/HeaderLockup";
|
||||||
|
import {
|
||||||
|
HEADER_LOCKUP_JUSTIFICATION_OPTIONS,
|
||||||
|
HEADER_LOCKUP_SIZE_OPTIONS,
|
||||||
|
} from "../../lib/propNormalization";
|
||||||
|
|
||||||
export default {
|
export default {
|
||||||
title: "Components/Type/HeaderLockup",
|
title: "Components/Type/HeaderLockup",
|
||||||
component: HeaderLockup,
|
component: HeaderLockup,
|
||||||
parameters: {
|
parameters: {
|
||||||
layout: "centered",
|
layout: "centered",
|
||||||
backgrounds: {
|
|
||||||
default: "dark",
|
|
||||||
values: [{ name: "dark", value: "#000000" }],
|
|
||||||
},
|
|
||||||
},
|
},
|
||||||
argTypes: {
|
argTypes: {
|
||||||
justification: {
|
justification: {
|
||||||
control: { type: "select" },
|
control: { type: "select" },
|
||||||
options: ["left", "center", "Left", "Center"],
|
options: [...HEADER_LOCKUP_JUSTIFICATION_OPTIONS],
|
||||||
},
|
},
|
||||||
size: {
|
size: {
|
||||||
control: { type: "select" },
|
control: { type: "select" },
|
||||||
options: ["L", "M", "l", "m"],
|
options: [...HEADER_LOCKUP_SIZE_OPTIONS],
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -0,0 +1,77 @@
|
|||||||
|
import InputLabel from "../../app/components/type/InputLabel";
|
||||||
|
import {
|
||||||
|
INPUT_LABEL_PALETTE_OPTIONS,
|
||||||
|
INPUT_LABEL_SIZE_OPTIONS,
|
||||||
|
} from "../../lib/propNormalization";
|
||||||
|
|
||||||
|
export default {
|
||||||
|
title: "Components/Type/InputLabel",
|
||||||
|
component: InputLabel,
|
||||||
|
parameters: {
|
||||||
|
layout: "centered",
|
||||||
|
docs: {
|
||||||
|
description: {
|
||||||
|
component:
|
||||||
|
"Reusable form-input label with optional asterisk, help icon, and helper text. Figma Utility / InputLabel; canonical code under type/.",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
argTypes: {
|
||||||
|
label: { control: { type: "text" } },
|
||||||
|
helpIcon: { control: { type: "boolean" } },
|
||||||
|
asterisk: { control: { type: "boolean" } },
|
||||||
|
helperText: { control: { type: "boolean" } },
|
||||||
|
size: {
|
||||||
|
control: { type: "select" },
|
||||||
|
options: [...INPUT_LABEL_SIZE_OPTIONS],
|
||||||
|
},
|
||||||
|
palette: {
|
||||||
|
control: { type: "select" },
|
||||||
|
options: [...INPUT_LABEL_PALETTE_OPTIONS],
|
||||||
|
},
|
||||||
|
},
|
||||||
|
};
|
||||||
|
|
||||||
|
export const Default = {
|
||||||
|
args: {
|
||||||
|
label: "Community name",
|
||||||
|
size: "s",
|
||||||
|
palette: "default",
|
||||||
|
},
|
||||||
|
};
|
||||||
|
|
||||||
|
export const Required = {
|
||||||
|
args: {
|
||||||
|
label: "Community name",
|
||||||
|
asterisk: true,
|
||||||
|
size: "s",
|
||||||
|
palette: "default",
|
||||||
|
},
|
||||||
|
};
|
||||||
|
|
||||||
|
export const WithHelp = {
|
||||||
|
args: {
|
||||||
|
label: "Applicable scope",
|
||||||
|
helpIcon: true,
|
||||||
|
size: "m",
|
||||||
|
palette: "default",
|
||||||
|
},
|
||||||
|
};
|
||||||
|
|
||||||
|
export const OptionalHelper = {
|
||||||
|
args: {
|
||||||
|
label: "Website",
|
||||||
|
helperText: true,
|
||||||
|
size: "s",
|
||||||
|
palette: "default",
|
||||||
|
},
|
||||||
|
};
|
||||||
|
|
||||||
|
export const Inverse = {
|
||||||
|
args: {
|
||||||
|
label: "Community name",
|
||||||
|
asterisk: true,
|
||||||
|
size: "m",
|
||||||
|
palette: "inverse",
|
||||||
|
},
|
||||||
|
};
|
||||||
@@ -1,19 +1,16 @@
|
|||||||
import NumberedList from "../../app/components/type/NumberedList";
|
import NumberedList from "../../app/components/type/NumberedList";
|
||||||
|
import { NUMBERED_LIST_SIZE_OPTIONS } from "../../lib/propNormalization";
|
||||||
|
|
||||||
export default {
|
export default {
|
||||||
title: "Components/Type/NumberedList",
|
title: "Components/Type/NumberedList",
|
||||||
component: NumberedList,
|
component: NumberedList,
|
||||||
parameters: {
|
parameters: {
|
||||||
layout: "centered",
|
layout: "centered",
|
||||||
backgrounds: {
|
|
||||||
default: "dark",
|
|
||||||
values: [{ name: "dark", value: "#000000" }],
|
|
||||||
},
|
|
||||||
},
|
},
|
||||||
argTypes: {
|
argTypes: {
|
||||||
size: {
|
size: {
|
||||||
control: { type: "select" },
|
control: { type: "select" },
|
||||||
options: ["M", "S", "m", "s"],
|
options: [...NUMBERED_LIST_SIZE_OPTIONS],
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
|
|||||||
Reference in New Issue
Block a user