Component cleanup

This commit is contained in:
adilallo
2026-04-29 07:20:16 -06:00
parent 252848eba9
commit e6127f1a3f
267 changed files with 2087 additions and 2196 deletions
-138
View File
@@ -1,138 +0,0 @@
import React, { useState } from "react";
import ContextMenu from "../../app/components/modals/ContextMenu/ContextMenu";
import ContextMenuItem from "../../app/components/modals/ContextMenuItem";
import ContextMenuSection from "../../app/components/modals/ContextMenu/ContextMenuSection";
import ContextMenuDivider from "../../app/components/modals/ContextMenu/ContextMenuDivider";
export default {
title: "Components/ContextMenu/ContextMenu",
component: ContextMenu,
argTypes: {
className: {
control: { type: "text" },
},
},
};
const Template = (args) => (
<ContextMenu {...args}>
<ContextMenuItem>Context Menu Item</ContextMenuItem>
<ContextMenuItem>Context Menu Item</ContextMenuItem>
<ContextMenuItem hasSubmenu>Context Menu Item</ContextMenuItem>
<ContextMenuItem hasSubmenu>Context Menu Item</ContextMenuItem>
<ContextMenuDivider />
<ContextMenuItem selected>Context Menu Item</ContextMenuItem>
<ContextMenuItem>Context Menu Item</ContextMenuItem>
<ContextMenuDivider />
<ContextMenuSection title="Section Title">
<ContextMenuItem>Context Menu Item</ContextMenuItem>
<ContextMenuItem>Context Menu Item</ContextMenuItem>
</ContextMenuSection>
</ContextMenu>
);
export const Default = Template.bind({});
export const WithCustomStyling = Template.bind({});
WithCustomStyling.args = {
className: "min-w-[250px]",
};
// Individual component stories
export const MenuItem = () => (
<div className="space-y-2">
<ContextMenuItem>Default Menu Item</ContextMenuItem>
<ContextMenuItem selected>Selected Menu Item</ContextMenuItem>
<ContextMenuItem hasSubmenu>Menu Item with Submenu</ContextMenuItem>
<ContextMenuItem disabled>Disabled Menu Item</ContextMenuItem>
</div>
);
export const MenuSection = () => (
<ContextMenu>
<ContextMenuSection title="First Section">
<ContextMenuItem>Item 1</ContextMenuItem>
<ContextMenuItem>Item 2</ContextMenuItem>
</ContextMenuSection>
<ContextMenuDivider />
<ContextMenuSection title="Second Section">
<ContextMenuItem>Item 3</ContextMenuItem>
<ContextMenuItem>Item 4</ContextMenuItem>
</ContextMenuSection>
</ContextMenu>
);
export const MenuDivider = () => (
<ContextMenu>
<ContextMenuItem>Item Above</ContextMenuItem>
<ContextMenuDivider />
<ContextMenuItem>Item Below</ContextMenuItem>
</ContextMenu>
);
export const Interactive = () => {
const [selectedItem, setSelectedItem] = useState("");
return (
<ContextMenu>
<ContextMenuItem
selected={selectedItem === "item1"}
onClick={() => setSelectedItem("item1")}
>
Context Menu Item 1
</ContextMenuItem>
<ContextMenuItem
selected={selectedItem === "item2"}
onClick={() => setSelectedItem("item2")}
>
Context Menu Item 2
</ContextMenuItem>
<ContextMenuItem
selected={selectedItem === "item3"}
onClick={() => setSelectedItem("item3")}
>
Context Menu Item 3
</ContextMenuItem>
</ContextMenu>
);
};
// Comparison stories
export const AllVariants = () => (
<div className="space-y-4">
<div>
<h3 className="text-sm font-medium mb-2">Default Items</h3>
<ContextMenu>
<ContextMenuItem>Context Menu Item</ContextMenuItem>
<ContextMenuItem>Context Menu Item</ContextMenuItem>
</ContextMenu>
</div>
<div>
<h3 className="text-sm font-medium mb-2">With Submenu Indicators</h3>
<ContextMenu>
<ContextMenuItem hasSubmenu>Context Menu Item</ContextMenuItem>
<ContextMenuItem hasSubmenu>Context Menu Item</ContextMenuItem>
</ContextMenu>
</div>
<div>
<h3 className="text-sm font-medium mb-2">With Selected Item</h3>
<ContextMenu>
<ContextMenuItem>Context Menu Item</ContextMenuItem>
<ContextMenuItem selected>Context Menu Item</ContextMenuItem>
<ContextMenuItem>Context Menu Item</ContextMenuItem>
</ContextMenu>
</div>
<div>
<h3 className="text-sm font-medium mb-2">With Sections</h3>
<ContextMenu>
<ContextMenuSection title="Section Title">
<ContextMenuItem>Context Menu Item</ContextMenuItem>
<ContextMenuItem>Context Menu Item</ContextMenuItem>
</ContextMenuSection>
</ContextMenu>
</div>
</div>
);
+2 -2
View File
@@ -1,7 +1,7 @@
import WebVitalsDashboard from "../app/components/sections/WebVitalsDashboard";
import WebVitalsDashboard from "../app/(admin)/monitor/_components/WebVitalsDashboard";
export default {
title: "Components/WebVitalsDashboard",
title: "Components/Monitor/WebVitalsDashboard",
component: WebVitalsDashboard,
parameters: {
layout: "fullscreen",
@@ -1,14 +1,14 @@
import Avatar from "../../app/components/icons/Avatar";
import Avatar from "../../app/components/asset/Avatar";
export default {
title: "Components/Icons/Avatar",
title: "Components/Asset/Avatar",
component: Avatar,
parameters: {
layout: "centered",
docs: {
description: {
component:
"A simple avatar component that displays user profile images with multiple size variants. Supports custom images and alt text for accessibility.",
"Rounded profile image primitive; stacks with **`AvatarContainer`**. Fuller DS-driven Avatar behavior is **[CR-58](https://linear.app/community-rule/issue/CR-58)**.",
},
},
},
@@ -1,15 +1,15 @@
import AvatarContainer from "../../app/components/utility/AvatarContainer";
import Avatar from "../../app/components/icons/Avatar";
import AvatarContainer from "../../app/components/asset/AvatarContainer";
import Avatar from "../../app/components/asset/Avatar";
export default {
title: "Components/Utility/AvatarContainer",
title: "Components/Asset/AvatarContainer",
component: AvatarContainer,
parameters: {
layout: "centered",
docs: {
description: {
component:
"A container component that groups multiple avatars together with overlapping spacing. Useful for displaying multiple users or team members in a compact format.",
"Stacks multiple `Avatar` children with overlapping spacing. Lives under `asset/` next to Avatar-related chrome.",
},
},
},
+3 -1
View File
@@ -1,4 +1,6 @@
import { Icon, ICON_NAME_OPTIONS } from "../../app/components/asset";
import Icon, {
ICON_NAME_OPTIONS,
} from "../../app/components/asset/icon";
export default {
title: "Components/Asset/Icon",
@@ -1,7 +1,7 @@
import Logo from "../../app/components/asset/logo";
import Logo from "../../app/components/asset/Logo";
export default {
title: "Components/Icons/Logo",
title: "Components/Asset/Logo",
component: Logo,
parameters: {
layout: "centered",
@@ -118,13 +118,13 @@ export const IconOnly = {
},
};
export const TopNavContext = {
export const TopContext = {
args: {},
render: () => (
<div className="min-h-screen bg-gradient-to-b from-[var(--color-surface-default-primary)] to-[var(--color-surface-default-secondary)] p-8">
<div className="max-w-4xl mx-auto">
<h2 className="text-white font-semibold mb-6">
TopNav Context (Responsive)
Top (navigation) responsive
</h2>
<div className="space-y-4">
<div className="flex items-center space-x-4">
@@ -143,7 +143,7 @@ export const TopNavContext = {
docs: {
description: {
story:
"TopNav context showing responsive logo sizes. Text hides on smallest breakpoint, shows on larger breakpoints.",
"`Top` (Figma Navigation / Top) logo sizes: folder-top vs header variants.",
},
},
},
@@ -1,4 +1,4 @@
import CardStack from "../../app/components/utility/CardStack";
import CardStack from "../../app/components/cards/CardStack";
const SAMPLE_CARDS = [
{
@@ -43,14 +43,14 @@ const SAMPLE_CARDS = [
];
export default {
title: "Create Flow/CardStack",
title: "Components/Cards/CardStack",
component: CardStack,
parameters: {
layout: "centered",
docs: {
description: {
component:
"Card stack for the create flow: compact grid or expanded list with toggle. Uses Card components; toggle visible only when hasMore is true.",
"Card stack for the create flow: compact grid or expanded list with toggle. Uses Selection tiles; toggle visible only when hasMore is true.",
},
},
},
@@ -1,9 +1,9 @@
import IconCard from "../../app/components/cards/IconCard";
import { getAssetPath } from "../../lib/assetUtils";
import Icon from "../../app/components/cards/Icon";
import { getAssetPath, vectorMarkPath } from "../../lib/assetUtils";
export default {
title: "Components/Cards/IconCard",
component: IconCard,
title: "Components/Cards/Icon",
component: Icon,
parameters: {
layout: "centered",
docs: {
@@ -34,7 +34,7 @@ export default {
// Worker's Coop icon
const WorkerCoopIcon = () => (
<img
src={getAssetPath("assets/Vector_WorkerCoop.svg")}
src={getAssetPath(vectorMarkPath("worker-coop"))}
alt=""
className="w-[36px] h-[36px]"
width="36"
@@ -1,8 +1,8 @@
import MiniCard from "../../app/components/cards/MiniCard";
import Mini from "../../app/components/cards/Mini";
export default {
title: "Components/Cards/MiniCard",
component: MiniCard,
title: "Components/Cards/Mini",
component: Mini,
parameters: {
layout: "centered",
},
@@ -37,25 +37,25 @@ export const Default = {
export const ColorVariants = {
render: () => (
<div className="grid grid-cols-2 gap-4">
<MiniCard
<Mini
backgroundColor="bg-[var(--color-surface-default-brand-royal)]"
labelLine1="Decision-making"
labelLine2="support"
panelContent="assets/Feature_Support.png"
/>
<MiniCard
<Mini
backgroundColor="bg-[#D1FFE2]"
labelLine1="Values alignment"
labelLine2="exercises"
panelContent="assets/Feature_Exercises.png"
/>
<MiniCard
<Mini
backgroundColor="bg-[#F4CAFF]"
labelLine1="Membership"
labelLine2="guidance"
panelContent="assets/Feature_Guidance.png"
/>
<MiniCard
<Mini
backgroundColor="bg-[#CBDDFF]"
labelLine1="Conflict resolution"
labelLine2="tools"
@@ -1,9 +1,9 @@
import RuleCard from "../../app/components/cards/RuleCard";
import Rule from "../../app/components/cards/Rule";
import Image from "next/image";
export default {
title: "Components/Cards/RuleCard",
component: RuleCard,
title: "Components/Cards/Rule",
component: Rule,
parameters: {
layout: "centered",
docs: {
@@ -305,7 +305,7 @@ export const AllVariants = {
// eslint-disable-next-line no-unused-vars
render: (_args) => (
<div className="grid grid-cols-1 md:grid-cols-2 gap-6 max-w-4xl">
<RuleCard
<Rule
title="Consensus clusters"
description="Units called Circles have the ability to decide and act on matters in their domains, which their members agree on through a Council."
backgroundColor="bg-[var(--color-surface-default-brand-lime)]"
@@ -320,7 +320,7 @@ export const AllVariants = {
}
onClick={() => console.log("Consensus clusters selected")}
/>
<RuleCard
<Rule
title="Consensus"
description="Decisions that affect the group collectively should involve participation of all participants."
backgroundColor="bg-[var(--color-surface-default-brand-rust)]"
@@ -335,7 +335,7 @@ export const AllVariants = {
}
onClick={() => console.log("Consensus selected")}
/>
<RuleCard
<Rule
title="Elected Board"
description="An elected board determines policies and organizes their implementation."
backgroundColor="bg-[var(--color-surface-default-brand-red)]"
@@ -350,7 +350,7 @@ export const AllVariants = {
}
onClick={() => console.log("Elected Board selected")}
/>
<RuleCard
<Rule
title="Petition"
description="All participants can propose and vote on proposals for the group."
backgroundColor="bg-[var(--color-surface-default-brand-teal)]"
@@ -1,8 +1,8 @@
import Card from "../../app/components/cards/Card";
import Selection from "../../app/components/cards/Selection";
export default {
title: "Components/Cards/Card",
component: Card,
title: "Components/Cards/Selection",
component: Selection,
parameters: {
layout: "centered",
docs: {
@@ -104,7 +104,7 @@ export const AllVariants = {
<p className="font-inter text-sm font-medium text-gray-600">
Horizontal + Recommended
</p>
<Card
<Selection
label="Label"
supportText="Members vote to resolve a dispute democratically."
recommended={true}
@@ -116,7 +116,7 @@ export const AllVariants = {
<p className="font-inter text-sm font-medium text-gray-600">
Horizontal + Selected
</p>
<Card
<Selection
label="Label"
supportText="Members vote to resolve a dispute democratically."
recommended={false}
@@ -128,7 +128,7 @@ export const AllVariants = {
<p className="font-inter text-sm font-medium text-gray-600">
Vertical + Recommended
</p>
<Card
<Selection
label="Label"
supportText="Invite-only"
recommended={true}
@@ -141,7 +141,7 @@ export const AllVariants = {
<p className="font-inter text-sm font-medium text-gray-600">
Vertical + Selected
</p>
<Card
<Selection
label="Label"
supportText="Invite-only"
recommended={false}
@@ -1,14 +1,14 @@
import NumberCard from "../../app/components/cards/NumberCard";
import Step from "../../app/components/cards/Step";
export default {
title: "Components/Cards/NumberCard",
component: NumberCard,
title: "Components/Cards/Step",
component: Step,
parameters: {
layout: "centered",
docs: {
description: {
component:
"Individual number card component that displays a step in a process with a numbered icon and descriptive text. Supports explicit size variants (Small, Medium, Large, XLarge) matching Figma designs, or responsive layouts when size is not specified.",
"Figma Card / Step: numbered tile with descriptive text. Supports explicit size variants (small, medium, large, xlarge) matching Figma, or responsive layouts when size is not specified.",
},
},
},
@@ -23,9 +23,9 @@ export default {
},
size: {
control: { type: "select" },
options: ["Small", "Medium", "Large", "XLarge", undefined],
options: ["small", "medium", "large", "xlarge", undefined],
description:
"Explicit size variant matching Figma designs. If not specified, uses responsive breakpoints for backward compatibility.",
"Explicit size variant matching Figma. If not specified, uses responsive breakpoints for backward compatibility.",
},
iconShape: {
control: { type: "select" },
@@ -129,34 +129,34 @@ export const AllSizes = {
<div className="space-y-8">
<div>
<h3 className="mb-4 text-lg font-semibold">Small</h3>
<NumberCard
<Step
number={1}
text="Document how your community makes decisions"
size="Small"
size="small"
/>
</div>
<div>
<h3 className="mb-4 text-lg font-semibold">Medium</h3>
<NumberCard
<Step
number={2}
text="Document how your community makes decisions"
size="Medium"
size="medium"
/>
</div>
<div>
<h3 className="mb-4 text-lg font-semibold">Large</h3>
<NumberCard
<Step
number={3}
text="Document how your community makes decisions"
size="Large"
size="large"
/>
</div>
<div>
<h3 className="mb-4 text-lg font-semibold">XLarge</h3>
<NumberCard
<Step
number={1}
text="Document how your community makes decisions"
size="XLarge"
size="xlarge"
/>
</div>
</div>
@@ -180,24 +180,16 @@ export const AllNumbers = {
},
render: (args) => (
<div className="space-y-4">
<NumberCard {...args} number={1} text="First step in the process" />
<NumberCard
{...args}
number={2}
text="Second step with different content"
/>
<NumberCard
{...args}
number={3}
text="Third and final step of the workflow"
/>
<Step {...args} number={1} text="First step in the process" />
<Step {...args} number={2} text="Second step with different content" />
<Step {...args} number={3} text="Third and final step of the workflow" />
</div>
),
parameters: {
docs: {
description: {
story:
"Shows all three numbered cards with different content to demonstrate the visual hierarchy.",
"Shows three Step tiles with different content to demonstrate the visual hierarchy.",
},
},
},
+2 -2
View File
@@ -42,11 +42,11 @@ export default {
size: {
control: "select",
options: ["XS", "S", "M", "L"],
description: "RuleCard size variant",
description: "Rule size variant",
},
ruleCardClassName: {
control: "text",
description: "Class names merged onto the inner RuleCard",
description: "Class names merged onto the inner Rule",
},
},
};
@@ -1,7 +1,7 @@
import InfoMessageBox from "../../app/components/utility/InfoMessageBox";
import InfoMessageBox from "../../app/components/controls/InfoMessageBox";
export default {
title: "Components/Utility/InfoMessageBox",
title: "Components/Controls/InfoMessageBox",
component: InfoMessageBox,
parameters: {
layout: "centered",
@@ -1,15 +1,20 @@
import DecisionMakingSidebar from "../../app/components/utility/DecisionMakingSidebar";
import React from "react";
import HeaderLockup from "../../app/components/type/HeaderLockup";
import InfoMessageBox from "../../app/components/controls/InfoMessageBox";
/**
* Compose pattern used by create-flow decision approaches rail: headline lockup
* plus bordered checklist no standalone wrapper component.
*/
export default {
title: "Components/Utility/DecisionMakingSidebar",
component: DecisionMakingSidebar,
title: "Components/Controls/Rail header (lockup + info box)",
parameters: {
layout: "centered",
backgrounds: { default: "dark" },
docs: {
description: {
component:
"HeaderLockup + InfoMessageBox for decision-making step sidebars.",
"`HeaderLockup` stacked with `InfoMessageBox` — same chrome as decision-approaches step sidebars.",
},
},
},
@@ -28,7 +33,20 @@ const messageItems = [
{ id: "c2", label: "Majority vote" },
];
const compose = (args) => (
<div className="flex w-full min-w-0 flex-col gap-3">
<HeaderLockup
title={args.title}
description={args.description}
size={args.size}
justification={args.justification}
/>
<InfoMessageBox title={args.messageBoxTitle} items={args.messageBoxItems} />
</div>
);
export const Default = {
render: compose,
args: {
title: "How does your group make decisions?",
description:
@@ -41,6 +59,7 @@ export const Default = {
};
export const Medium = {
render: compose,
args: {
title: "Decision-making",
description: "Short description.",
@@ -1,7 +1,7 @@
import ModalFooter from "../../app/components/utility/ModalFooter";
import ModalFooter from "../../app/components/modals/ModalFooter";
export default {
title: "Components/Utility/ModalFooter",
title: "Components/Modals/ModalFooter",
component: ModalFooter,
parameters: {
layout: "fullscreen",
@@ -1,7 +1,7 @@
import ModalHeader from "../../app/components/utility/ModalHeader";
import ModalHeader from "../../app/components/modals/ModalHeader";
export default {
title: "Components/Utility/ModalHeader",
title: "Components/Modals/ModalHeader",
component: ModalHeader,
parameters: {
layout: "fullscreen",
@@ -1,8 +1,8 @@
import CreateFlowFooter from "../../app/components/utility/CreateFlowFooter";
import CreateFlowFooter from "../../app/components/navigation/CreateFlowFooter";
import Button from "../../app/components/buttons/Button";
export default {
title: "Components/Utility/CreateFlowFooter",
title: "Components/Navigation/CreateFlowFooter",
component: CreateFlowFooter,
parameters: {
layout: "fullscreen",
@@ -1,7 +1,7 @@
import CreateFlowTopNav from "../../app/components/utility/CreateFlowTopNav";
import CreateFlowTopNav from "../../app/components/navigation/CreateFlowTopNav";
export default {
title: "Components/Utility/CreateFlowTopNav",
title: "Components/Navigation/CreateFlowTopNav",
component: CreateFlowTopNav,
parameters: {
layout: "fullscreen",
+101
View File
@@ -0,0 +1,101 @@
import Menu from "../../app/components/navigation/Menu";
import MenuItem from "../../app/components/navigation/MenuItem";
export default {
title: "Components/Navigation/Menu",
component: Menu,
parameters: {
layout: "centered",
docs: {
description: {
component:
"Navigation Menu (`role=\"menubar\"`) groups MenuItem children. Consistent spacing and layout for horizontal nav clusters with multiple size variants.",
},
},
},
argTypes: {
size: {
control: { type: "select" },
options: ["X Small", "Small", "Medium", "Large", "X Large"],
description: "The size of the menu and its children",
},
className: {
control: { type: "text" },
description: "Additional CSS classes",
},
},
tags: ["autodocs"],
};
export const Default = {
args: {
size: "Small",
},
render: (args) => (
<Menu {...args}>
<MenuItem size="Large">Home</MenuItem>
<MenuItem size="Large">About</MenuItem>
<MenuItem size="Large">Contact</MenuItem>
</Menu>
),
};
export const Sizes = {
args: {},
render: () => (
<div className="space-y-6">
<div>
<h3 className="text-white font-semibold mb-3">X Small Size</h3>
<Menu size="X Small">
<MenuItem size="X Small">Home</MenuItem>
<MenuItem size="X Small">About</MenuItem>
<MenuItem size="X Small">Contact</MenuItem>
</Menu>
</div>
<div>
<h3 className="text-white font-semibold mb-3">Small Size</h3>
<Menu size="Small">
<MenuItem size="Large">Home</MenuItem>
<MenuItem size="Large">About</MenuItem>
<MenuItem size="Large">Contact</MenuItem>
</Menu>
</div>
<div>
<h3 className="text-white font-semibold mb-3">Medium Size</h3>
<Menu size="Medium">
<MenuItem size="Large">Home</MenuItem>
<MenuItem size="Large">About</MenuItem>
<MenuItem size="Large">Contact</MenuItem>
</Menu>
</div>
<div>
<h3 className="text-white font-semibold mb-3">Large Size</h3>
<Menu size="Large">
<MenuItem size="Large">Home</MenuItem>
<MenuItem size="Large">About</MenuItem>
<MenuItem size="Large">Contact</MenuItem>
</Menu>
</div>
<div>
<h3 className="text-white font-semibold mb-3">X Large Size</h3>
<Menu size="X Large">
<MenuItem size="X Large">Home</MenuItem>
<MenuItem size="X Large">About</MenuItem>
<MenuItem size="X Large">Contact</MenuItem>
</Menu>
</div>
</div>
),
parameters: {
docs: {
description: {
story:
"Different size variants of the menu with consistent spacing and layout.",
},
},
},
};
-101
View File
@@ -1,101 +0,0 @@
import MenuBar from "../../app/components/navigation/MenuBar";
import MenuBarItem from "../../app/components/navigation/MenuBarItem";
export default {
title: "Components/Navigation/MenuBar",
component: MenuBar,
parameters: {
layout: "centered",
docs: {
description: {
component:
"A navigation menu bar container that groups MenuBarItem components together. Provides consistent spacing and layout for navigation menus with multiple size variants.",
},
},
},
argTypes: {
size: {
control: { type: "select" },
options: ["X Small", "Small", "Medium", "Large", "X Large"],
description: "The size of the menu bar and its children",
},
className: {
control: { type: "text" },
description: "Additional CSS classes",
},
},
tags: ["autodocs"],
};
export const Default = {
args: {
size: "Small",
},
render: (args) => (
<MenuBar {...args}>
<MenuBarItem size="Large">Home</MenuBarItem>
<MenuBarItem size="Large">About</MenuBarItem>
<MenuBarItem size="Large">Contact</MenuBarItem>
</MenuBar>
),
};
export const Sizes = {
args: {},
render: () => (
<div className="space-y-6">
<div>
<h3 className="text-white font-semibold mb-3">X Small Size</h3>
<MenuBar size="X Small">
<MenuBarItem size="X Small">Home</MenuBarItem>
<MenuBarItem size="X Small">About</MenuBarItem>
<MenuBarItem size="X Small">Contact</MenuBarItem>
</MenuBar>
</div>
<div>
<h3 className="text-white font-semibold mb-3">Small Size</h3>
<MenuBar size="Small">
<MenuBarItem size="Large">Home</MenuBarItem>
<MenuBarItem size="Large">About</MenuBarItem>
<MenuBarItem size="Large">Contact</MenuBarItem>
</MenuBar>
</div>
<div>
<h3 className="text-white font-semibold mb-3">Medium Size</h3>
<MenuBar size="Medium">
<MenuBarItem size="Large">Home</MenuBarItem>
<MenuBarItem size="Large">About</MenuBarItem>
<MenuBarItem size="Large">Contact</MenuBarItem>
</MenuBar>
</div>
<div>
<h3 className="text-white font-semibold mb-3">Large Size</h3>
<MenuBar size="Large">
<MenuBarItem size="Large">Home</MenuBarItem>
<MenuBarItem size="Large">About</MenuBarItem>
<MenuBarItem size="Large">Contact</MenuBarItem>
</MenuBar>
</div>
<div>
<h3 className="text-white font-semibold mb-3">X Large Size</h3>
<MenuBar size="X Large">
<MenuBarItem size="X Large">Home</MenuBarItem>
<MenuBarItem size="X Large">About</MenuBarItem>
<MenuBarItem size="X Large">Contact</MenuBarItem>
</MenuBar>
</div>
</div>
),
parameters: {
docs: {
description: {
story:
"Different size variants of the menu bar with consistent spacing and layout.",
},
},
},
};
@@ -1,14 +1,14 @@
import MenuBarItem from "../../app/components/navigation/MenuBarItem";
import MenuItem from "../../app/components/navigation/MenuItem";
export default {
title: "Components/Navigation/MenuBarItem",
component: MenuBarItem,
title: "Components/Navigation/MenuItem",
component: MenuItem,
parameters: {
layout: "centered",
docs: {
description: {
component:
"A navigation menu item component with multiple variants, sizes, and states. Can render as a link or disabled span with full accessibility support. Includes focus states with keyboard navigation - use Tab key to test focus indicators.",
"A navigation menu item with multiple variants, sizes, and states. Renders as a link or button (or disabled span) with accessibility support. Use Tab to test focus indicators.",
},
},
},
@@ -51,12 +51,12 @@ export const Modes = {
render: (args) => (
<div className="space-y-4">
<div className="space-x-4">
<MenuBarItem {...args} mode="default">
<MenuItem {...args} mode="default">
Default
</MenuBarItem>
<MenuBarItem {...args} mode="inverse">
</MenuItem>
<MenuItem {...args} mode="inverse">
Inverse
</MenuBarItem>
</MenuItem>
</div>
</div>
),
@@ -77,21 +77,21 @@ export const Sizes = {
render: (args) => (
<div className="space-y-4">
<div className="space-x-4">
<MenuBarItem {...args} size="X Small">
<MenuItem {...args} size="X Small">
X Small
</MenuBarItem>
<MenuBarItem {...args} size="Small">
</MenuItem>
<MenuItem {...args} size="Small">
Small
</MenuBarItem>
<MenuBarItem {...args} size="Medium">
</MenuItem>
<MenuItem {...args} size="Medium">
Medium
</MenuBarItem>
<MenuBarItem {...args} size="Large">
</MenuItem>
<MenuItem {...args} size="Large">
Large
</MenuBarItem>
<MenuBarItem {...args} size="X Large">
</MenuItem>
<MenuItem {...args} size="X Large">
X Large
</MenuBarItem>
</MenuItem>
</div>
</div>
),
@@ -113,10 +113,10 @@ export const States = {
render: (args) => (
<div className="space-y-4">
<div className="space-x-4">
<MenuBarItem {...args}>Normal</MenuBarItem>
<MenuBarItem {...args} disabled>
<MenuItem {...args}>Normal</MenuItem>
<MenuItem {...args} disabled>
Disabled
</MenuBarItem>
</MenuItem>
</div>
</div>
),
@@ -136,42 +136,42 @@ export const AllModes = {
<div>
<h3 className="text-white font-semibold mb-3">Default Mode</h3>
<div className="space-x-4">
<MenuBarItem size="X Small" mode="default">
<MenuItem size="X Small" mode="default">
X Small
</MenuBarItem>
<MenuBarItem size="Large" mode="default">
</MenuItem>
<MenuItem size="Large" mode="default">
Large
</MenuBarItem>
<MenuBarItem size="X Large" mode="default">
</MenuItem>
<MenuItem size="X Large" mode="default">
X Large
</MenuBarItem>
</MenuItem>
</div>
</div>
<div>
<h3 className="text-white font-semibold mb-3">Inverse Mode</h3>
<div className="space-x-4">
<MenuBarItem mode="inverse" size="X Small">
<MenuItem mode="inverse" size="X Small">
X Small
</MenuBarItem>
<MenuBarItem mode="inverse" size="Large">
</MenuItem>
<MenuItem mode="inverse" size="Large">
Large
</MenuBarItem>
<MenuBarItem mode="inverse" size="X Large">
</MenuItem>
<MenuItem mode="inverse" size="X Large">
X Large
</MenuBarItem>
</MenuItem>
</div>
</div>
<div>
<h3 className="text-white font-semibold mb-3">Disabled States</h3>
<div className="space-x-4">
<MenuBarItem size="Large" mode="default" disabled>
<MenuItem size="Large" mode="default" disabled>
Default Disabled
</MenuBarItem>
<MenuBarItem mode="inverse" size="Large" disabled>
</MenuItem>
<MenuItem mode="inverse" size="Large" disabled>
Inverse Disabled
</MenuBarItem>
</MenuItem>
</div>
</div>
</div>
@@ -1,8 +1,8 @@
import TopNav from "../../app/components/navigation/TopNav";
import Top from "../../app/components/navigation/Top";
export default {
title: "Components/Navigation/TopNav",
component: TopNav,
title: "Components/Navigation/Top",
component: Top,
parameters: {
layout: "fullscreen",
docs: {
@@ -117,7 +117,7 @@ export const StandardInPageContext = {
},
render: (args) => (
<div className="min-h-screen bg-[var(--color-surface-default-primary)]">
<TopNav {...args} />
<Top {...args} />
<main className="p-8">
<div className="max-w-4xl mx-auto">
<h1 className="text-2xl font-bold text-white mb-4">
@@ -168,7 +168,7 @@ export const HomePageInContext = {
},
render: (args) => (
<div className="min-h-screen bg-gradient-to-b from-[var(--color-surface-default-primary)] to-[var(--color-surface-default-secondary)]">
<TopNav {...args} />
<Top {...args} />
<main className="p-8">
<div className="max-w-4xl mx-auto text-center">
<h1 className="text-4xl font-bold text-white mb-4">
+1 -1
View File
@@ -28,7 +28,7 @@ export default {
docs: {
description: {
component:
"Completed flow: teal background, inverse HeaderLockup, CommunityRule document, optional bottom toast.",
"Completed flow: teal background, inverse HeaderLockup, CommunityRule body, optional bottom toast.",
},
},
},
+1 -1
View File
@@ -8,7 +8,7 @@ export default {
docs: {
description: {
component:
"Pre-finalize review: HeaderLockup + expanded RuleCard sections.",
"Pre-finalize review: HeaderLockup + expanded Rule sections.",
},
},
},
@@ -1,30 +1,30 @@
import NumberedCards from "../../app/components/sections/NumberedCards";
import CardSteps from "../../app/components/sections/CardSteps";
export default {
title: "Components/Sections/NumberedCards",
component: NumberedCards,
title: "Components/Sections/CardSteps",
component: CardSteps,
parameters: {
layout: "fullscreen",
docs: {
description: {
component:
"A component system for visually communicating multi-step workflows, processes, or value propositions. The component's modular design with NumberCard and SectionNumber sub-components makes it ideal for explaining any sequential process while maintaining brand consistency and accessibility standards across the design system.",
"Marketing section (Figma SectionCardSteps) that composes **cards/Step** tiles with a section header and CTA. Use for sequential explainers on the home page and similar surfaces.",
},
},
},
argTypes: {
title: {
control: { type: "text" },
description: "The main title for the section",
description: "The main title for the section (mobile single line)",
},
subtitle: {
control: { type: "text" },
description: "The subtitle text below the main title",
description: "Supporting text beside / below the title on large screens",
},
cards: {
steps: {
control: { type: "object" },
description:
"Array of card objects with text, iconShape, and iconColor properties",
"Items rendered as **Step** cards (text, optional iconShape, iconColor)",
},
},
tags: ["autodocs"],
@@ -34,7 +34,8 @@ export const Default = {
args: {
title: "How CommunityRule works",
subtitle: "Here's a quick overview of the process, from start to finish.",
cards: [
headingDesktopLines: ["How", "CommunityRule", "helps"],
steps: [
{
text: "Document how your community makes decisions",
iconShape: "blob",
@@ -58,7 +59,7 @@ export const CustomContent = {
args: {
title: "Our Process",
subtitle: "Follow these simple steps to get started with your project.",
cards: [
steps: [
{
text: "Define your project requirements and goals",
iconShape: "blob",
@@ -85,7 +86,7 @@ export const CustomContent = {
docs: {
description: {
story:
"Example with custom content and four cards to show flexibility.",
"Example with custom content and four **Step** tiles to show flexibility.",
},
},
},
@@ -1,58 +0,0 @@
import CommunityRuleDocument from "../../app/components/sections/CommunityRuleDocument";
const sampleSections = [
{
categoryName: "Decision making",
entries: [
{
title: "How proposals pass",
body: "Important decisions require unanimous agreement. Proposals pass only if no serious objections remain.",
},
{
title: "Blocks",
body: "Anyone with a serious objection may block consensus and require further discussion.",
},
],
},
{
categoryName: "Membership",
entries: [
{
title: "Joining",
body: "New members are welcomed by consensus of existing members.",
},
],
},
];
export default {
title: "Components/Sections/CommunityRuleDocument",
component: CommunityRuleDocument,
parameters: {
layout: "padded",
},
argTypes: {
sections: {
control: false,
description: "Document sections, each with a categoryName and entries.",
},
useCardStyle: {
control: "boolean",
description: "When true, wraps the document in a white card with a teal bar",
},
},
};
export const Default = {
args: {
sections: sampleSections,
useCardStyle: false,
},
};
export const CardStyle = {
args: {
sections: sampleSections,
useCardStyle: true,
},
};
+3 -3
View File
@@ -14,7 +14,7 @@ A responsive feature grid component that displays organizational tools and servi
- **Responsive Layout**: Adapts from 2x2 grid on mobile to 1x4 grid on tablet to horizontal layout on desktop
- **ContentLockup Integration**: Uses the feature variant with "Learn more" link
- **MiniCard Grid**: Four feature cards with color-coded backgrounds and icons
- **Mini grid**: Four feature tiles with color-coded backgrounds and icons
- **Accessibility**: Full keyboard navigation, focus indicators, and ARIA labels
- **Design System**: Uses design tokens for consistent spacing, colors, and typography
@@ -26,7 +26,7 @@ A responsive feature grid component that displays organizational tools and servi
## Interactive Elements
- **MiniCards**: Hover effects, focus indicators, and keyboard navigation
- **Mini tiles**: Hover effects, focus indicators, and keyboard navigation
- **Learn More Link**: Underlined link with focus states
- **Color-coded Features**: Royal, green, pink, and blue backgrounds for categorization
@@ -69,7 +69,7 @@ export const Default = {
Default FeatureGrid with standard content. This component demonstrates:
- **ContentLockup**: Feature variant with title, subtitle, and "Learn more" link
- **MiniCard Grid**: Four feature cards with different colors and icons
- **Mini grid**: Four feature tiles with different colors and icons
- **Responsive Design**: Layout adapts across mobile, tablet, and desktop breakpoints
- **Interactive States**: Hover effects and focus indicators on all interactive elements
@@ -10,7 +10,7 @@ export default {
argTypes: {
entries: {
control: false,
description: "Catalog entries to render as a 2-column grid of RuleCards",
description: "Catalog entries to render as a 2-column grid of Rules",
},
onTemplateClick: { action: "template-clicked" },
},
+1 -1
View File
@@ -44,7 +44,7 @@ export default {
docs: {
description: {
component:
"A complete template library component that displays governance patterns in a responsive grid layout. Includes SectionHeader with multi-line variant, interactive RuleCard components, and a call-to-action button. Features comprehensive accessibility, analytics tracking, and responsive design across all breakpoints.\n\n" +
"A complete template library component that displays governance patterns in a responsive grid layout. Composes **`type/SectionHeader`** (multi-line variant), interactive **`Rule`** cards, and a call-to-action button. Features comprehensive accessibility, analytics tracking, and responsive design across all breakpoints.\n\n" +
"**Testing Scenarios:**\n" +
"- **Responsive Testing**: Resize browser window to test layout adaptation from single column on mobile to 2x2 grid on larger screens\n" +
"- **Interactive Testing**: Hover over cards to see effects, use Tab to navigate between cards, and click to see analytics events in console\n" +
+85
View File
@@ -0,0 +1,85 @@
import CommunityRule from "../../app/components/type/CommunityRule";
const sampleSections = [
{
categoryName: "Decision making",
entries: [
{
title: "How proposals pass",
body: "Short opener line.\n\nImportant decisions require unanimous agreement. Proposals pass only if no serious objections remain.",
},
{
title: "Blocks",
body: "Anyone with a serious objection may block consensus and require further discussion.",
},
],
},
{
categoryName: "Membership",
entries: [
{
title: "Joining",
body: "New members are welcomed by consensus of existing members.",
},
],
},
];
export default {
title: "Components/Type/CommunityRule",
component: CommunityRule,
parameters: {
layout: "padded",
},
argTypes: {
sections: {
control: false,
description: "Rule sections, each with a categoryName and entries.",
},
useCardStyle: {
control: "boolean",
description: "When true, wraps the rule body in a white card with a teal bar",
},
},
};
export const Default = {
args: {
sections: sampleSections,
useCardStyle: false,
},
};
export const WithLabeledBlocks = {
args: {
sections: [
{
categoryName: "Membership",
entries: [
{
title: "Consensus or vote-based approval",
body: "",
blocks: [
{
label: "Eligibility & philosophy",
body: "Access to critical resources is restricted to safeguard the project.",
},
{
label: "Joining process",
body: "Volunteers who have completed two full distributions can submit a request.",
},
],
},
],
},
],
useCardStyle: false,
},
};
export const CardStyle = {
args: {
sections: sampleSections,
useCardStyle: true,
},
};
+56
View File
@@ -0,0 +1,56 @@
import Section from "../../app/components/type/Section";
import TextBlock from "../../app/components/type/TextBlock";
export default {
title: "Components/Type/Section",
component: Section,
parameters: { layout: "padded" },
argTypes: {
categoryName: { control: "text" },
showRail: { control: "boolean" },
children: { control: false },
},
};
export const Default = {
args: {
categoryName: "Values",
showRail: true,
children: (
<>
<TextBlock
title="Solidarity Forever"
body={`“Change needs all of us.”
Food Not Bombs is not a charity. It is a project of solidarity.`}
/>
<TextBlock
title="Shared Leadership"
body="Everyone coordinates, no one controls."
/>
</>
),
},
};
export const WithoutRail = {
args: {
categoryName: "Membership",
showRail: false,
children: (
<TextBlock
title="Open access"
rows={[
{
label: "Eligibility & philosophy",
body: "Anyone aligned with the mission may join.",
},
{
label: "Process",
body: "Complete two orientations, then lazy consensus.",
},
]}
/>
),
},
};
@@ -1,36 +1,35 @@
import SectionHeader from "../../app/components/sections/SectionHeader";
import SectionHeader from "../../app/components/type/SectionHeader";
export default {
title: "Components/Sections/SectionHeader",
title: "Components/Type/SectionHeader",
component: SectionHeader,
parameters: {
layout: "centered",
docs: {
description: {
component:
"A section header component that displays a title and subtitle with responsive typography and layout. Supports different title text for large breakpoints and maintains consistent spacing across all screen sizes. Includes 'default' and 'multi-line' variants with different layout behaviors.",
"Figma Type / SectionHeader ([17411:10981](https://www.figma.com/design/agv0VBLiBlcnSAaiAORgPR/Community-Rule-System?node-id=17411-10981)). Title + subtitle with responsive typography: stacked on small screens, split row from lg. **`default`**: top-aligned row; **`multi-line`**: vertically centered row on large screens (subtitle vs. title). Optional **`stackedDesktopLines`** for three-line desktop titles (CardSteps).",
},
},
},
argTypes: {
title: {
control: { type: "text" },
description: "The main title text (used for xsm and sm breakpoints)",
description: "Title for small viewports (and mobile/stacked layout)",
},
subtitle: {
control: { type: "text" },
description: "The subtitle text below the main title",
description: "Subtitle / supporting copy",
},
titleLg: {
control: { type: "text" },
description:
"The title text for lg and xl breakpoints (optional, falls back to title)",
"Optional single-line title from lg up (when `stackedDesktopLines` is not set)",
},
variant: {
control: { type: "select" },
options: ["default", "multi-line"],
description:
"The layout variant - 'default' for traditional layout, 'multi-line' for 50/50 split layout",
description: "Layout variant (Figma 1-line vs 3-line row behavior)",
},
},
tags: ["autodocs"],
@@ -56,7 +55,24 @@ export const MultiLine = {
docs: {
description: {
story:
"The multi-line variant creates a 50/50 split layout at lg and xl breakpoints, with the title on the left and subtitle on the right. This variant is used in the RuleStack component.",
"Used in **RuleStack**: 50/50 row from lg with subtitle vertically centered against the title block.",
},
},
},
};
export const MultiLineStackedTitle = {
args: {
title: "How CommunityRule works",
subtitle: "Here's a quick overview of the process, from start to finish.",
variant: "multi-line",
stackedDesktopLines: ["How", "CommunityRule", "helps"],
},
parameters: {
docs: {
description: {
story:
"Used in **CardSteps**: three-line Bricolage lockup on large screens with `stackedDesktopLines`.",
},
},
},
+35
View File
@@ -0,0 +1,35 @@
import TextBlock from "../../app/components/type/TextBlock";
export default {
title: "Components/Type/TextBlock",
component: TextBlock,
parameters: { layout: "padded" },
argTypes: {
title: { control: "text" },
body: { control: "text" },
rows: { control: false },
},
};
export const PlainBody = {
args: {
title: "Solidarity Forever",
body: "First paragraph line.\n\nSecond paragraph with more detail.",
},
};
export const LabeledRows = {
args: {
title: "Consensus or vote-based approval",
rows: [
{
label: "Eligibility & philosophy",
body: "Access to critical resources is restricted to safeguard the project.",
},
{
label: "Joining process",
body: "Volunteers who have completed two full distributions can submit a request.",
},
],
},
};
-28
View File
@@ -1,28 +0,0 @@
import Separator from "../../app/components/utility/Separator";
export default {
title: "Components/Utility/Separator",
component: Separator,
parameters: {
layout: "padded",
},
argTypes: {},
};
export const Default = {
render: () => (
<div style={{ width: 320 }}>
<Separator />
</div>
),
};
export const InContext = {
render: () => (
<div style={{ width: 320, display: "flex", flexDirection: "column", gap: 12 }}>
<p>Above the separator</p>
<Separator />
<p>Below the separator</p>
</div>
),
};