Update stories to match new component organization
This commit is contained in:
@@ -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),
|
||||
},
|
||||
};
|
||||
@@ -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",
|
||||
},
|
||||
},
|
||||
},
|
||||
};
|
||||
@@ -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.
|
||||
`,
|
||||
},
|
||||
},
|
||||
},
|
||||
};
|
||||
@@ -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.",
|
||||
},
|
||||
},
|
||||
},
|
||||
};
|
||||
@@ -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.",
|
||||
},
|
||||
},
|
||||
},
|
||||
};
|
||||
@@ -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.",
|
||||
},
|
||||
},
|
||||
},
|
||||
};
|
||||
@@ -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.",
|
||||
},
|
||||
},
|
||||
},
|
||||
};
|
||||
@@ -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.",
|
||||
},
|
||||
},
|
||||
},
|
||||
};
|
||||
@@ -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",
|
||||
},
|
||||
};
|
||||
@@ -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.",
|
||||
},
|
||||
},
|
||||
},
|
||||
};
|
||||
@@ -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.",
|
||||
},
|
||||
},
|
||||
},
|
||||
};
|
||||
@@ -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.",
|
||||
},
|
||||
},
|
||||
},
|
||||
};
|
||||
Reference in New Issue
Block a user