Storybook stories for the `AnnouncementBar` component, covering SSR-seeded, client-fetched, and admin preview rendering paths across multiple visual configurations. ## Key Components | Export | Description | |---|---| | `Default` | Basic announcement with icon and description | | `WithCTA` | Announcement with a styled call-to-action button (new tab) | | `WithoutCloseButton` | Non-dismissible bar (`dismissible={false}`) | | `WithCTASameTab` | CTA that navigates within the same tab | | `DarkBackground` | Validates contrast-aware text color via `pickReadableTextColor` | | `BlueBackground` / `GreenBackground` | Color theme variants | | `WithOpenFrameLogo` | Uses the `openframe-logo` named icon | | `WithPNGIcon` | Icon sourced from an external `icon_url` | | `LongContent` | Truncation behavior under long titles and descriptions | | `PreviewMode` | Admin live-preview — renders bar inert; no storage/fetch side effects | | `NoAnnouncement` | Null announcement; bar renders nothing | | `ClientOnlyEmbed` | Mounts an `EndpointsRuntimeContext` provider and mocks `window.fetch` to simulate client-only embedding without SSR | ## Usage Example ```typescript // Replicate the SSR-seeded path used by most stories import { AnnouncementBar } from '../components/announcement-bar' export const WithCTA: Story = { args: { initialAnnouncement: { id: 'example-1', title: 'OpenFrame v2.0 is here!', description: 'Explore the new dashboard.', background_color: '#FFD951', icon_name: 'star', is_active: true, cta_enabled: true, cta_text: 'Learn More', cta_url: 'https://example.com', cta_target: '_blank', }, }, } // Replicate the client-only embed path (mocks fetch, no SSR prop) export const ClientOnlyEmbed: Story = { args: {}, decorators: [ (Story) => { window.fetch = async (input) => { if (input.toString().includes('/api/announcements/active')) { return new Response(JSON.stringify({ announcement: { ...} }), { status: 200, headers: { 'Content-Type': 'application/json' }, }) } return originalFetch(input) } return ( ) }, ], } ``` ## Notes - Every story's decorator calls `clearAnnouncementDismissals(getAppType())` before rendering, ensuring each story starts with the bar visible regardless of prior interactions. - Without an `EndpointsRuntimeContext` provider mounted, the bar never issues a fetch — stories remain self-contained and require no network mocking unless explicitly testing the embed path (`ClientOnlyEmbed`). - Source: [`AnnouncementBar.stories.tsx`](https://github.com/flamingo-stack/openframe-oss-lib/blob/main/AnnouncementBar.stories.tsx)