Update stories to match new component organization

This commit is contained in:
adilallo
2026-02-05 22:46:16 -07:00
parent 6f178e934f
commit aef04c525a
51 changed files with 111 additions and 111 deletions
+89
View File
@@ -0,0 +1,89 @@
import AskOrganizer from "../../app/components/sections/AskOrganizer";
export default {
title: "Components/Sections/AskOrganizer",
component: AskOrganizer,
parameters: {
docs: {
description: {
component:
"The AskOrganizer component provides clear pathways for user inquiries. This component serves as a conversion point throughout the platform.",
},
},
},
argTypes: {
title: {
control: "text",
description: "The main title for the ask organizer section",
},
subtitle: {
control: "text",
description: "The subtitle text",
},
description: {
control: "text",
description: "Additional description text",
},
buttonText: {
control: "text",
description: "Text for the call-to-action button",
},
buttonHref: {
control: "text",
description: "URL for the button link",
},
variant: {
control: { type: "select" },
options: ["centered", "left-aligned", "compact", "inverse"],
description: "Layout variant for the component",
},
onContactClick: {
action: "contact clicked",
description: "Analytics callback for contact button clicks",
},
},
};
export const Default = {
args: {
title: "Still have questions?",
subtitle: "Get answers from an experienced organizer",
buttonText: "Ask an organizer",
buttonHref: "#contact",
variant: "centered",
onContactClick: (data) => console.log("Contact clicked:", data),
},
};
export const LeftAligned = {
args: {
title: "Still have questions?",
subtitle: "Get answers from an experienced organizer",
buttonText: "Ask an organizer",
buttonHref: "#contact",
variant: "left-aligned",
onContactClick: (data) => console.log("Contact clicked:", data),
},
};
export const Compact = {
args: {
title: "Still have questions?",
subtitle: "Get answers from an experienced organizer",
buttonText: "Ask an organizer",
buttonHref: "#contact",
variant: "compact",
onContactClick: (data) => console.log("Contact clicked:", data),
},
};
export const Inverse = {
args: {
title: "Still have questions?",
subtitle: "Get answers from an experienced organizer",
buttonText: "Ask an organizer",
buttonHref: "#contact",
variant: "inverse",
onContactClick: (data) => console.log("Contact clicked:", data),
},
};
+100
View File
@@ -0,0 +1,100 @@
import ContentBanner from "../../app/components/sections/ContentBanner";
const mockBlogPost = {
slug: "sample-article",
frontmatter: {
title: "Sample Article Title",
description:
"This is a sample article description that explains what the article covers.",
author: "Sample Author",
date: "2025-01-15",
thumbnail: {
horizontal: "resolving-active-conflicts-horizontal.svg",
vertical: "resolving-active-conflicts-vertical.svg",
},
banner: {
horizontal: "resolving-active-conflicts-banner.svg",
},
},
htmlContent:
"<p>This is the main content of the sample article.</p><p>It has multiple paragraphs.</p>",
};
export default {
title: "Components/Sections/ContentBanner",
component: ContentBanner,
parameters: {
docs: {
description: {
component:
"The ContentBanner component displays the header information for blog articles, including title, description, author, and date.\n\nImages: sm uses thumbnail.horizontal; md+ uses banner.horizontal when provided, otherwise falls back to thumbnail.horizontal; final fallback is assets/Content_Banner_2.svg.\n\nNote: page background colors are applied at the blog page level using a hex color from frontmatter (background.color), not inside this component. Thumbnail and banner images should be uploaded via the content pipeline to public/content/blog/ and referenced in frontmatter.",
},
},
},
argTypes: {
post: {
control: "object",
description: "Blog post object with frontmatter and content",
},
},
};
export const Default = {
args: {
post: mockBlogPost,
},
};
export const NoBannerFallbackToThumbnail = {
args: {
post: {
...mockBlogPost,
frontmatter: {
...mockBlogPost.frontmatter,
banner: undefined,
},
},
},
};
export const NoImagesFallbackToDefault = {
args: {
post: {
...mockBlogPost,
frontmatter: {
...mockBlogPost.frontmatter,
thumbnail: undefined,
banner: undefined,
},
},
},
};
export const LongTitle = {
args: {
post: {
...mockBlogPost,
frontmatter: {
...mockBlogPost.frontmatter,
title:
"This is a Very Long Article Title That Tests How the Component Handles Extended Text",
description:
"This is a longer description that tests how the component handles extended text content and ensures proper wrapping and display.",
},
},
},
};
export const DifferentAuthor = {
args: {
post: {
...mockBlogPost,
frontmatter: {
...mockBlogPost.frontmatter,
title: "Article by Different Author",
author: "Community Organizer",
date: "2025-02-20",
},
},
},
};
+81
View File
@@ -0,0 +1,81 @@
import FeatureGrid from "../../app/components/sections/FeatureGrid";
export default {
title: "Components/Sections/FeatureGrid",
component: FeatureGrid,
parameters: {
layout: "fullscreen",
docs: {
description: {
component: `
A responsive feature grid component that displays organizational tools and services in a clean card-based layout with supportive messaging and categorized feature highlights.
## Features
- **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
- **Accessibility**: Full keyboard navigation, focus indicators, and ARIA labels
- **Design System**: Uses design tokens for consistent spacing, colors, and typography
## Responsive Behavior
- **Mobile (< 768px)**: 2x2 grid layout with ContentLockup on top
- **Tablet (768px - 1024px)**: 1x4 grid layout with ContentLockup on top
- **Desktop (> 1024px)**: Horizontal layout with ContentLockup on left, 1x4 grid on right
## Interactive Elements
- **MiniCards**: 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
## Accessibility
- WCAG 2.1 AA compliant
- Keyboard navigation support
- Screen reader friendly with proper ARIA labels
- Focus management with visible indicators
`,
},
},
},
argTypes: {
title: {
control: { type: "text" },
description: "Main headline text for the ContentLockup",
},
subtitle: {
control: { type: "text" },
description: "Supporting subtitle text for the ContentLockup",
},
className: {
control: { type: "text" },
description: "Additional CSS classes for custom styling",
},
},
};
export const Default = {
args: {
title: "We've got your back, every step of the way",
subtitle:
"Use our toolkit to improve, document, and evolve your organization.",
},
parameters: {
docs: {
description: {
story: `
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
- **Responsive Design**: Layout adapts across mobile, tablet, and desktop breakpoints
- **Interactive States**: Hover effects and focus indicators on all interactive elements
The component uses a dark background (#171717) with rounded corners and proper spacing using design tokens.
`,
},
},
},
};
+57
View File
@@ -0,0 +1,57 @@
import HeroBanner from "../../app/components/sections/HeroBanner";
export default {
title: "Components/Sections/HeroBanner",
component: HeroBanner,
parameters: {
layout: "fullscreen",
docs: {
description: {
component:
"A responsive hero banner component that showcases the Community Rule branding and messaging. Adapts across multiple breakpoints with proper spacing, typography, and interactive elements. Includes background decorations and product demo integration.",
},
},
},
argTypes: {
title: {
control: { type: "text" },
description: "The main title text",
},
subtitle: {
control: { type: "text" },
description: "The subtitle text",
},
description: {
control: { type: "text" },
description: "The description text",
},
ctaText: {
control: { type: "text" },
description: "The call-to-action button text",
},
ctaHref: {
control: { type: "text" },
description: "The call-to-action button link",
},
},
tags: ["autodocs"],
};
export const Default = {
args: {
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: "#",
},
parameters: {
docs: {
description: {
story:
"Default hero banner with standard Community Rule messaging and branding.",
},
},
},
};
@@ -0,0 +1,248 @@
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.",
},
},
},
};
+58
View File
@@ -0,0 +1,58 @@
import HeroDecor from "../../app/components/sections/HeroBanner/HeroDecor";
export default {
title: "Components/Sections/HeroDecor",
component: HeroDecor,
parameters: {
layout: "centered",
docs: {
description: {
component:
"A decorative SVG component that provides background visual elements for the HeroBanner. Features grain effects and organic shapes that enhance the visual appeal without interfering with content readability.",
},
},
},
argTypes: {
className: {
control: { type: "text" },
description: "Additional CSS classes for positioning and styling",
},
},
tags: ["autodocs"],
};
export const Default = {
args: {
className: "w-[400px] h-[200px]",
},
parameters: {
docs: {
description: {
story: "Default hero decoration with standard sizing and positioning.",
},
},
},
};
export const WithBackground = {
args: {
className: "w-[600px] h-[300px]",
},
render: (args) => (
<div className="bg-[var(--color-surface-default-brand-primary)] p-8 rounded-lg relative overflow-hidden">
<HeroDecor {...args} />
<div className="relative z-10 text-white mt-4">
<h3>Content Overlay</h3>
<p>This demonstrates how the decoration appears behind content.</p>
</div>
</div>
),
parameters: {
docs: {
description: {
story:
"Hero decoration with background color to show how it integrates with content.",
},
},
},
};
+83
View File
@@ -0,0 +1,83 @@
import LogoWall from "../../app/components/sections/LogoWall";
export default {
title: "Components/Sections/LogoWall",
component: LogoWall,
parameters: {
layout: "fullscreen",
docs: {
description: {
component: `A responsive logo wall component that displays partner/sponsor logos in a grid layout. Features responsive breakpoints with different layouts and sizing for mobile, tablet, and desktop views.
## Responsive Behavior
- **Mobile**: 3 rows × 2 columns grid with 32px gaps
- **SM**: 2 rows × 3 columns grid with 48px row gap and 32px column gap
- **MD**: Single row with space-between layout and 24px gap between text and logos
- **LG**: Larger logo sizes and 64px horizontal padding
- **XL**: Largest logo sizes, 160px horizontal padding, and 14px label text
## Animations & Transitions
- **Fade-in Effect**: Logos fade in from opacity 0 to 60% after component mounts (500ms transition)
- **Hover Interactions**: Individual logos respond to hover with:
- Opacity change from 60% to 100%
- Scale transform (105% zoom)
- 500ms smooth transitions for all effects
- **Loading States**: Progressive loading with fallback timer for reliable display
## Props
- **logos** (optional): Array of logo objects with src, alt, size, and order properties. If not provided, uses default partner logos.
## Usage Examples
### Custom Logos
\`\`\`jsx
<LogoWall
logos={[
{
src: "assets/Section/Logo_CUBoulder.png",
alt: "CU Boulder",
size: "h-10 lg:h-12 xl:h-[60px]",
order: "order-1 sm:order-2"
},
{
src: "assets/Section/Logo_FoodNotBombs.png",
alt: "Food Not Bombs",
size: "h-11 lg:h-14 xl:h-[70px]",
order: "order-2 sm:order-1"
}
]}
/>
\`\`\`
### Empty State
\`\`\`jsx
<LogoWall logos={[]} />
\`\`\`
This will fall back to the default partner logos.`,
},
},
},
tags: ["autodocs"],
argTypes: {
logos: {
control: "object",
description:
"Array of logo objects with src, alt, size, and order properties. If not provided, uses default partner logos.",
},
},
};
export const Default = {
args: {},
parameters: {
docs: {
description: {
story:
"Default LogoWall with all partner logos. Displays in a 3×2 grid on mobile, 2×3 grid on small screens, single row on medium screens, and larger sizes on large screens. Features smooth fade-in animations and hover interactions.",
},
},
},
};
+92
View File
@@ -0,0 +1,92 @@
import NumberedCards from "../../app/components/sections/NumberedCards";
export default {
title: "Components/Sections/NumberedCards",
component: NumberedCards,
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.",
},
},
},
argTypes: {
title: {
control: { type: "text" },
description: "The main title for the section",
},
subtitle: {
control: { type: "text" },
description: "The subtitle text below the main title",
},
cards: {
control: { type: "object" },
description:
"Array of card objects with text, iconShape, and iconColor properties",
},
},
tags: ["autodocs"],
};
export const Default = {
args: {
title: "How CommunityRule works",
subtitle: "Here's a quick overview of the process, from start to finish.",
cards: [
{
text: "Document how your community makes decisions",
iconShape: "blob",
iconColor: "green",
},
{
text: "Build an operating manual for a successful community",
iconShape: "gear",
iconColor: "purple",
},
{
text: "Get a link to your manual for your group to review and evolve",
iconShape: "star",
iconColor: "orange",
},
],
},
};
export const CustomContent = {
args: {
title: "Our Process",
subtitle: "Follow these simple steps to get started with your project.",
cards: [
{
text: "Define your project requirements and goals",
iconShape: "blob",
iconColor: "green",
},
{
text: "Collaborate with our team to create the perfect solution",
iconShape: "gear",
iconColor: "purple",
},
{
text: "Launch and iterate based on user feedback",
iconShape: "star",
iconColor: "orange",
},
{
text: "Scale and optimize for continued success",
iconShape: "blob",
iconColor: "blue",
},
],
},
parameters: {
docs: {
description: {
story:
"Example with custom content and four cards to show flexibility.",
},
},
},
};
+146
View File
@@ -0,0 +1,146 @@
import QuoteBlock from "../../app/components/sections/QuoteBlock";
export default {
title: "Components/Sections/QuoteBlock",
component: QuoteBlock,
parameters: {
layout: "fullscreen",
docs: {
description: {
component: `
A responsive quote section component that displays inspirational governance quotes with author attribution and decorative geometric elements.
## Features
- **Three variants**: compact, standard, and extended layouts
- **Responsive design**: Adapts across all breakpoints
- **Error handling**: Graceful fallbacks for image loading failures
- **Accessibility**: WCAG 2.1 AA compliant with proper ARIA labels
- **Design system integration**: Uses design tokens for consistent styling
## Usage
\`\`\`jsx
<QuoteBlock
variant="standard"
quote="Your quote text here..."
author="Author Name"
source="Source Title"
avatarSrc="path/to/avatar.jpg"
/>
\`\`\`
`,
},
},
},
argTypes: {
variant: {
control: { type: "select" },
options: ["compact", "standard", "extended"],
description: "Layout variant for different use cases",
},
quote: {
control: { type: "text" },
description: "The quote text to display",
},
author: {
control: { type: "text" },
description: "Author name for attribution",
},
source: {
control: { type: "text" },
description: "Source title (book, article, etc.)",
},
avatarSrc: {
control: { type: "text" },
description: "Path to author avatar image",
},
fallbackAvatarSrc: {
control: { type: "text" },
description: "Fallback avatar image path",
},
onError: {
action: "error",
description: "Error callback function",
},
},
};
// Default story
export const Default = {
args: {
variant: "standard",
quote:
"The rules of decision-making must be open and available to everyone, and this can happen only if they are formalized.",
author: "Jo Freeman",
source: "The Tyranny of Structurelessness",
avatarSrc: "assets/Quote_Avatar.svg",
},
};
// All variants comparison
export const AllVariants = {
render: () => (
<div className="space-y-8 p-4">
<div>
<h3 className="text-lg font-bold mb-4">Compact Variant</h3>
<QuoteBlock
variant="compact"
quote="The rules of decision-making must be open and available to everyone."
author="Jo Freeman"
source="The Tyranny of Structurelessness"
avatarSrc="assets/Quote_Avatar.svg"
/>
</div>
<div>
<h3 className="text-lg font-bold mb-4">Standard Variant</h3>
<QuoteBlock
variant="standard"
quote="The rules of decision-making must be open and available to everyone, and this can happen only if they are formalized."
author="Jo Freeman"
source="The Tyranny of Structurelessness"
avatarSrc="assets/Quote_Avatar.svg"
/>
</div>
<div>
<h3 className="text-lg font-bold mb-4">Extended Variant</h3>
<QuoteBlock
variant="extended"
quote="The rules of decision-making must be open and available to everyone, and this can happen only if they are formalized."
author="Jo Freeman"
source="The Tyranny of Structurelessness"
avatarSrc="assets/Quote_Avatar.svg"
/>
</div>
</div>
),
parameters: {
docs: {
description: {
story:
"Side-by-side comparison of all three variants to show the differences in layout, typography, and spacing.",
},
},
},
};
// Error state simulation
export const ErrorState = {
args: {
variant: "standard",
quote:
"The rules of decision-making must be open and available to everyone, and this can happen only if they are formalized.",
author: "Jo Freeman",
source: "The Tyranny of Structurelessness",
avatarSrc: "invalid-image-path.jpg", // This will trigger error state
onError: (error) => console.log("QuoteBlock error:", error),
},
parameters: {
docs: {
description: {
story:
"Error state when avatar image fails to load. Shows initials fallback and error handling.",
},
},
},
};
+121
View File
@@ -0,0 +1,121 @@
import RelatedArticles from "../../app/components/sections/RelatedArticles";
const mockRelatedPosts = [
{
slug: "related-article-1",
frontmatter: {
title: "Related Article One",
description: "This is the first related article description.",
author: "Author One",
date: "2025-01-10",
},
},
{
slug: "related-article-2",
frontmatter: {
title: "Related Article Two",
description: "This is the second related article description.",
author: "Author Two",
date: "2025-01-12",
},
},
{
slug: "related-article-3",
frontmatter: {
title: "Related Article Three",
description: "This is the third related article description.",
author: "Author Three",
date: "2025-01-14",
},
},
];
export default {
title: "Components/Sections/RelatedArticles",
component: RelatedArticles,
parameters: {
docs: {
description: {
component:
"The RelatedArticles component displays a carousel of related blog posts with progress bars on mobile and a scrollable slider on desktop.",
},
},
},
argTypes: {
relatedPosts: {
control: "object",
description: "Array of related blog post objects",
},
currentPostSlug: {
control: "text",
description: "Slug of the current post to exclude from related articles",
},
},
};
export const Default = {
args: {
relatedPosts: mockRelatedPosts,
currentPostSlug: "current-article",
},
};
export const TwoArticles = {
args: {
relatedPosts: mockRelatedPosts.slice(0, 2),
currentPostSlug: "current-article",
},
};
export const OneArticle = {
args: {
relatedPosts: mockRelatedPosts.slice(0, 1),
currentPostSlug: "current-article",
},
};
export const Empty = {
args: {
relatedPosts: [],
currentPostSlug: "current-article",
},
};
export const LongTitles = {
args: {
relatedPosts: [
{
slug: "long-title-1",
frontmatter: {
title:
"This is a Very Long Article Title That Tests Text Wrapping and Display",
description:
"This is a longer description that tests how the component handles extended text content.",
author: "Author One",
date: "2025-01-10",
},
},
{
slug: "long-title-2",
frontmatter: {
title: "Another Very Long Article Title for Testing Purposes",
description:
"Another longer description for testing text handling in the component.",
author: "Author Two",
date: "2025-01-12",
},
},
{
slug: "long-title-3",
frontmatter: {
title: "Third Long Article Title to Complete the Set",
description:
"Final longer description to test the component's text handling capabilities.",
author: "Author Three",
date: "2025-01-14",
},
},
],
currentPostSlug: "current-article",
},
};
+39
View File
@@ -0,0 +1,39 @@
import RuleStack from "../../app/components/sections/RuleStack";
export default {
title: "Components/Sections/RuleStack",
component: RuleStack,
parameters: {
layout: "fullscreen",
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" +
"**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" +
"- **Accessibility Testing**: Use screen readers to test ARIA labels, keyboard navigation to move between cards, and verify focus indicators\n" +
"- **Custom Styling**: Add className prop to customize background or other styling",
},
},
},
argTypes: {
className: {
control: { type: "text" },
description: "Additional CSS classes for custom styling",
},
},
tags: ["autodocs"],
};
export const Default = {
args: {},
parameters: {
docs: {
description: {
story:
"The complete RuleStack component with all four governance templates, responsive grid layout, and interactive features. Test hover states, keyboard navigation, and responsive behavior across different screen sizes.",
},
},
},
};
+151
View File
@@ -0,0 +1,151 @@
import SectionHeader from "../../app/components/sections/SectionHeader";
export default {
title: "Components/Sections/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.",
},
},
},
argTypes: {
title: {
control: { type: "text" },
description: "The main title text (used for xsm and sm breakpoints)",
},
subtitle: {
control: { type: "text" },
description: "The subtitle text below the main title",
},
titleLg: {
control: { type: "text" },
description:
"The title text for lg and xl breakpoints (optional, falls back to title)",
},
variant: {
control: { type: "select" },
options: ["default", "multi-line"],
description:
"The layout variant - 'default' for traditional layout, 'multi-line' for 50/50 split layout",
},
},
tags: ["autodocs"],
};
export const Default = {
args: {
title: "How CommunityRule works",
subtitle: "Here's a quick overview of the process, from start to finish.",
titleLg: "How CommunityRule helps",
variant: "default",
},
};
export const MultiLine = {
args: {
title: "Popular templates",
subtitle:
"These are popular patterns for making decisions in mutual aid and open source communities. You can use them as they are or as a starting place for customizing your own CommunityRule.",
variant: "multi-line",
},
parameters: {
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.",
},
},
},
};
export const CustomContent = {
args: {
title: "Our Mission",
subtitle:
"We're dedicated to helping communities thrive through better decision-making processes and transparent governance structures.",
titleLg: "Building Better Communities",
variant: "default",
},
parameters: {
docs: {
description: {
story:
"Example with custom content to show the flexibility of the component.",
},
},
},
};
export const LongSubtitle = {
args: {
title: "Complex Process",
subtitle:
"This is a much longer subtitle that demonstrates how the component handles extended text content across different breakpoints and layout configurations.",
titleLg: "Complex Process Simplified",
variant: "default",
},
parameters: {
docs: {
description: {
story:
"Demonstrates how the component handles longer subtitle text across different breakpoints.",
},
},
},
};
export const ResponsiveTest = {
args: {
title: "Responsive Design",
subtitle:
"Test the responsive behavior by resizing your browser window or using the viewport controls in Storybook.",
titleLg: "Responsive Design Test",
variant: "default",
},
parameters: {
docs: {
description: {
story:
"Test the responsive behavior by resizing your browser window or using the viewport controls in Storybook.",
},
},
},
};
export const WithoutTitleLg = {
args: {
title: "Simple Header",
subtitle:
"This example doesn't specify a titleLg prop, so it will use the same title text across all breakpoints.",
variant: "default",
},
parameters: {
docs: {
description: {
story:
"Shows the component without a titleLg prop, demonstrating the fallback behavior.",
},
},
},
};
export const MultiLineResponsive = {
args: {
title: "Multi-line Responsive Test",
subtitle:
"This multi-line variant demonstrates the 50/50 split layout at larger breakpoints. Resize your browser to see how the layout adapts from stacked on mobile to side-by-side on desktop.",
variant: "multi-line",
},
parameters: {
docs: {
description: {
story:
"Test the responsive behavior of the multi-line variant. The layout changes from stacked on mobile to 50/50 split on lg and xl breakpoints.",
},
},
},
};
+78
View File
@@ -0,0 +1,78 @@
import SectionNumber from "../../app/components/sections/SectionNumber";
export default {
title: "Components/Sections/SectionNumber",
component: SectionNumber,
parameters: {
layout: "centered",
docs: {
description: {
component:
"A numbered icon component that displays a number overlaid on a PNG background image. The component uses different PNG images for numbers 1, 2, and 3, with the image extending beyond the 40px container size.",
},
},
},
argTypes: {
number: {
control: { type: "number", min: 1, max: 3 },
description: "The number to display (1, 2, or 3)",
},
},
tags: ["autodocs"],
};
export const NumberOne = {
args: {
number: 1,
},
};
export const NumberTwo = {
args: {
number: 2,
},
};
export const NumberThree = {
args: {
number: 3,
},
};
export const AllNumbers = {
render: () => (
<div className="flex space-x-4">
<SectionNumber number={1} />
<SectionNumber number={2} />
<SectionNumber number={3} />
</div>
),
parameters: {
docs: {
description: {
story:
"Shows all three numbered icons side by side to demonstrate the different PNG backgrounds.",
},
},
},
};
export const WithBackground = {
render: () => (
<div className="bg-gray-100 p-8 rounded-lg">
<div className="flex space-x-4">
<SectionNumber number={1} />
<SectionNumber number={2} />
<SectionNumber number={3} />
</div>
</div>
),
parameters: {
docs: {
description: {
story:
"Shows the numbered icons on a background to demonstrate how the PNG images extend beyond the container.",
},
},
},
};