import type { Meta, StoryObj } from '@storybook/web-components-vite'; import { html } from 'lit'; import './index.ts'; import '../identifier/index.ts'; import type { USAFooter, FooterSection } from './usa-footer.js'; const meta: Meta = { title: 'Navigation/Footer', component: 'usa-footer', parameters: { layout: 'padded', docs: { description: { component: ` The USWDS footer provides consistent site information and links across websites. It supports multiple variants (slim, medium, big) and includes sections for navigation links and agency information. ## Important: Footer + Identifier Architecture **The footer and identifier are separate components in USWDS:** - \`\` - Contains navigation links and agency contact information - \`\` - The black section with agency identification and required links **Correct Usage:** \`\`\`html \`\`\` **Why Separate?** The identifier can be used on any page (not just with footers), and keeping them separate follows USWDS architecture and prevents spacing issues. This web component implementation maintains full USWDS compliance while providing a modern custom element API. `, }, }, }, argTypes: { variant: { control: 'select', options: ['slim', 'medium', 'big'], description: 'Footer size variant', }, agencyName: { control: 'text', description: 'Name of the organization', }, sections: { control: 'object', description: 'Array of footer navigation sections with links', }, }, }; export default meta; type Story = StoryObj; // Sample data for stories const basicSections: FooterSection[] = [ { title: 'About', links: [ { label: 'Our Mission', href: '/about/mission' }, { label: 'Leadership', href: '/about/leadership' }, { label: 'History', href: '/about/history' }, ], }, { title: 'Services', links: [ { label: 'Digital Services', href: '/services/digital' }, { label: 'Consulting', href: '/services/consulting' }, { label: 'Training', href: '/services/training' }, ], }, ]; const extendedSections: FooterSection[] = [ { title: 'About Us', links: [ { label: 'Our Mission', href: '/about/mission' }, { label: 'Leadership', href: '/about/leadership' }, { label: 'History', href: '/about/history' }, { label: 'Careers', href: '/about/careers' }, ], }, { title: 'Services', links: [ { label: 'Digital Services', href: '/services/digital' }, { label: 'Consulting', href: '/services/consulting' }, { label: 'Training', href: '/services/training' }, { label: 'Support', href: '/services/support' }, ], }, { title: 'Resources', links: [ { label: 'Documentation', href: '/resources/docs' }, { label: 'Downloads', href: '/resources/downloads' }, { label: 'FAQ', href: '/resources/faq' }, ], }, { title: 'Contact', links: [ { label: 'Contact Us', href: '/contact' }, { label: 'Office Locations', href: '/contact/locations' }, { label: 'Media Inquiries', href: '/contact/media' }, ], }, ]; export const Default: Story = { args: { variant: 'medium', agencyName: 'Example Organization', sections: basicSections, }, render: (args) => html` `, }; export const Slim: Story = { args: { variant: 'slim', agencyName: 'Organization Name', sections: [], }, render: (args) => html` `, }; export const Medium: Story = { args: { variant: 'medium', agencyName: 'Example Corporation', sections: basicSections, }, render: (args) => html` `, }; export const Big: Story = { args: { variant: 'big', agencyName: 'Example Web Services Inc', sections: extendedSections, }, render: (args) => html` `, }; export const MinimalFooter: Story = { args: { variant: 'slim', agencyName: 'Simple Organization', sections: [], }, render: (args) => html` `, }; export const FooterOnly: Story = { args: { variant: 'medium', agencyName: 'Test Organization', sections: basicSections, }, parameters: { docs: { description: { story: 'Footer without an identifier component. The identifier is optional and can be omitted.', }, }, }, }; export const NavigationOnly: Story = { args: { variant: 'medium', agencyName: '', sections: basicSections, }, parameters: { docs: { description: { story: 'Footer with navigation only, no agency name or identifier.', }, }, }, }; export const IdentifierOnly: Story = { render: () => html` `, parameters: { docs: { description: { story: 'Identifier component used independently without a footer. This demonstrates that the identifier can be used on any page, not just with footers.', }, }, }, }; export const ComplexFooter: Story = { args: { variant: 'big', agencyName: 'Comprehensive Services Corporation', sections: extendedSections, }, render: (args) => html` `, }; export const SingleSection: Story = { args: { variant: 'medium', agencyName: 'Single Section Organization', sections: [ { title: 'Quick Links', links: [ { label: 'Home', href: '/' }, { label: 'About', href: '/about' }, { label: 'Contact', href: '/contact' }, ], }, ], }, render: (args) => html` `, }; export const InteractiveDemo: Story = { args: { variant: 'big', agencyName: 'Interactive Demo Organization', sections: extendedSections, }, render: (args) => html` { console.log('Footer link clicked:', e.detail); alert(`Footer link clicked: ${e.detail.label} (${e.detail.href})`); e.preventDefault(); // Prevent navigation in Storybook }} >
Custom Footer Content Slot

This content is inserted via the default slot and can include any custom HTML.

{ console.log('Identifier link clicked:', e.detail); alert(`Identifier link clicked: ${e.detail.text} (${e.detail.href})`); e.preventDefault(); }} >

Footer + Identifier Testing Guide

Try these interactions:

  • Click any footer link to see the footer custom event
  • Click any identifier link to see the identifier custom event
  • Use keyboard navigation (Tab, Enter)
  • Test with different screen sizes
  • Verify accessibility with screen readers
  • Check footer variants in the controls panel

USWDS Footer Variants:

  • Slim: Minimal footer with identifier links only
  • Medium: Standard footer with navigation sections
  • Big: Extended footer with multiple navigation sections

Important Architecture Note:

The <usa-identifier> is a separate component from <usa-footer>. They should be placed one after another on the page, not nested.

`, }; export const CustomContent: Story = { parameters: { controls: { disable: true }, docs: { description: { story: 'Demonstrates using the default slot for custom content.', }, }, }, render: () => html`

This is custom slotted content.

Slots allow you to provide your own HTML content to the component.

`, };