Skip to content

Add Superfluid Campaign GoodWidget - #130

Open
goodbounties-nanoclaw-agent[bot] wants to merge 25 commits into
mainfrom
feat/superfluid-campaign-widget
Open

Add Superfluid Campaign GoodWidget#130
goodbounties-nanoclaw-agent[bot] wants to merge 25 commits into
mainfrom
feat/superfluid-campaign-widget

Conversation

@goodbounties-nanoclaw-agent

Copy link
Copy Markdown

Summary

Implements the Superfluid Ecosystem Rewards campaign widget planned in #129, for parent feature #127.

  • New packages/superfluid-campaign-widget: header, two reward-pool action sections (GoodDollar actions, ecosystem funding actions), a leaderboard view, and the campaign FAQ, rendered against a static mock dataset for this phase.
  • Disconnected-state "Connect wallet" CTA in the content view, gated on useWallet().isConnected, per [Feature]: Add Superfluid Campaign GoodWidget #127 acceptance criteria.
  • Reusable Accordion and ProgressBar added to packages/ui (used by the FAQ and pool-progress sections; assessed as generic enough to belong in the shared package rather than the widget itself).
  • Additive initialTab prop on citizen-claim-widget and additive initialView prop on SuperfluidCampaignWidget, so Storybook/QA fixtures and deep links can land directly on a given view.
  • element.ts/register.ts web-component bridge, mirroring the existing streaming-widget pattern.
  • Storybook coverage under examples/storybook: a manual injected-wallet showcase story, four QA fixture stories (disconnected/connected x content/leaderboard) using the existing custodial-wallet fixture pattern from citizen-claim-widget, and a doc page.

Known gaps flagged for follow-up (not silently worked around)

  • ACTIVITY_ICON_MAP's exact icon names aren't yet in @goodwidget/ui's Icon registry; the closest already-registered glyphs are substituted until the registry is extended.
  • The leaderboard's search box filters the static fixture locally; pagination is a disabled placeholder pending a real leaderboard API.

Test plan

  • pnpm build (turbo) succeeds across all packages/apps
  • eslint clean on all new/changed files in this PR's scope
  • tsc --noEmit clean on all new/changed files in this PR's scope
  • Storybook static build succeeds; 4 QA fixture screenshots captured and manually compared against the two approved [Feature]: Add Superfluid Campaign GoodWidget #127 mockups (disconnected content, connected content, leaderboard) at both narrow (480px) and wide (900px) viewports
  • Reviewer: verify against live [Feature]: Add Superfluid Campaign GoodWidget #127 mockups once more in a running Storybook (pnpm storybook)

🤖 Generated with Claude Code

Implements the widget from #129 (plan for #127): a
reusable GoodWidget presenting the Superfluid Ecosystem Rewards campaign —
header, two reward-pool action sections (GoodDollar actions, ecosystem
funding actions), a leaderboard view, and the campaign FAQ, against a
static mock dataset for this phase.

- New packages/superfluid-campaign-widget: presentational components,
  widgetRuntimeContract, mock dataset, and the element/register web
  component bridge (mirrors the streaming-widget bridge pattern).
- Adds a disconnected-state "Connect wallet" CTA in the content view,
  gated on useWallet().isConnected, matching the #127 acceptance criteria.
- Adds Accordion and ProgressBar to packages/ui (reusable, used by the
  FAQ and pool-progress sections).
- Adds an additive initialTab prop to citizen-claim-widget and an
  additive initialView prop to SuperfluidCampaignWidget, so Storybook/QA
  fixtures and deep links can land directly on a given view.
- Adds Storybook coverage under examples/storybook: a manual injected-
  wallet showcase story, four QA fixture stories (disconnected/connected
  x content/leaderboard) using the existing custodial-wallet fixture
  pattern from citizen-claim-widget, and a doc page that explicitly flags
  two known gaps for this phase (activity icon glyph registry, live
  leaderboard search/pagination).

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
@goodbounties-nanoclaw-agent
goodbounties-nanoclaw-agent Bot requested review from a team, L03TJ3 and Copilot July 29, 2026 08:12

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

Note

Copilot could not run the full agentic suite for this review because it was automatically requested on a bot-authored pull request. Request a review from Copilot under Reviewers to retry with the full agentic suite. Improved support for bot-authored pull requests is coming soon.

Adds a new “Superfluid campaign” widget package and introduces shared UI primitives needed to render campaign progress and FAQ content, while extending CitizenClaimWidget to support deep-linking into a specific tab.

Changes:

  • Export new ProgressBar and Accordion components from @goodwidget/ui.
  • Add @goodwidget/superfluid-campaign-widget package (runtime contract, mock data, widget UI, custom element + registration, tsup build config).
  • Add initialTab support to CitizenClaimWidget to open the embedded widget directly on Claim/Invite tabs.

Reviewed changes

Copilot reviewed 29 out of 30 changed files in this pull request and generated 7 comments.

Show a summary per file
File Description
packages/ui/src/index.ts Exposes new UI primitives (ProgressBar, Accordion) from the UI package entrypoint.
packages/ui/src/components/ProgressBar.tsx Implements labeled progress bar used by campaign pools/leaderboard summary.
packages/ui/src/components/Accordion.tsx Implements shared accordion primitive used for Superfluid FAQ.
packages/superfluid-campaign-widget/tsup.config.ts Adds build configuration for the new widget package (esm/cjs + dts).
packages/superfluid-campaign-widget/tsconfig.json Adds local TS config + workspace path aliases for development.
packages/superfluid-campaign-widget/tsconfig.build.json Adds build-only TS config used by tsup.
packages/superfluid-campaign-widget/src/widgetRuntimeContract.ts Defines the public runtime contract types/constants for the widget.
packages/superfluid-campaign-widget/src/mockData.ts Provides static mock data fixtures for content + leaderboard + FAQ.
packages/superfluid-campaign-widget/src/index.ts Exports public types/constants/mock data and the widget component.
packages/superfluid-campaign-widget/src/element.ts Wraps the widget as a Custom Element via createMiniAppElement.
packages/superfluid-campaign-widget/src/register.ts Registers a default custom element tag and auto-registers on import.
packages/superfluid-campaign-widget/src/components/* Implements campaign header, reward pools, leaderboard views, FAQ wrapper, and icon rendering.
packages/superfluid-campaign-widget/src/SuperfluidCampaignWidget.tsx Composes the full campaign runtime, view routing, and CTA handling including embedded CitizenClaimWidget.
packages/superfluid-campaign-widget/package.json Declares the new package, build outputs, and exports map.
packages/citizen-claim-widget/src/widgetRuntimeContract.ts Adds CitizenClaimTab and initialTab to the widget props contract.
packages/citizen-claim-widget/src/CitizenClaimWidget.tsx Wires initialTab into internal tab state.
examples/storybook/src/stories/superfluid-campaign-widget/* Adds showcase + QA stories/docs for the new widget.
examples/storybook/src/stories/helpers/superfluidCampaignWidgetStories.tsx Adds helper shells/fixtures to render connected/disconnected states.
examples/storybook/package.json Adds the new widget package as a Storybook dependency.
Files not reviewed (1)
  • pnpm-lock.yaml: Generated file

if (embeddedClaimTab) {
return (
<YStack gap="$3" width="100%">
<CitizenClaimWidget environment={citizenClaimEnvironment} initialTab={embeddedClaimTab} />
Comment on lines +95 to +107
<AccordionHeader isOpen={isOpen} onPress={() => toggleItem(item.id)}>
<Text flex={1}>{item.title}</Text>
<Icon
name="chevron-down"
size="sm"
color="muted"
style={{
transform: isOpen ? 'rotate(180deg)' : 'rotate(0deg)',
transition: 'transform 150ms ease',
}}
/>
</AccordionHeader>
{isOpen && <AccordionContent>{item.content}</AccordionContent>}
Comment on lines +47 to +57
<YStack width="100%" gap="$1">
{label && (
<XStack justifyContent="space-between" alignItems="center">
<Text variant="label">{label}</Text>
<Text variant="caption">{Math.round(percentage)}%</Text>
</XStack>
)}
<ProgressTrack>
<ProgressFill variant={variant} width={`${percentage}%`} />
</ProgressTrack>
</YStack>
Comment on lines +20 to +27
export function register(tagName: string = DEFAULT_TAG_NAME): string {
if (!customElements.get(tagName)) {
customElements.define(tagName, SuperfluidCampaignWidgetElement)
}
return tagName
}

register()
Comment on lines +8 to +15
const ACTIVITY_ICON_NAME_FALLBACK: Record<CampaignActionMockData['activity'], IconName> = {
'claim-ubi': 'check',
'invite-users': 'user',
'flow-state-vote': 'arrow-up',
'flow-state-funding': 'arrows-left-right',
'gardens-donation': 'wallet',
'gardens-funding': 'refresh',
}
@@ -0,0 +1,59 @@
import React from 'react'
import { Text } from './Text'
import { XStack, YStack } from '../components-test/Stacks'
import type { ReactNode } from 'react'
import { Icon } from './Icon'
import { Text } from './Text'
import { XStack, YStack } from '../components-test/Stacks'
@sirpy

sirpy commented Jul 29, 2026

Copy link
Copy Markdown
Contributor

@copilot address issues found in review. Also why aren't there any tests and screenshots of the widget?

…, provider passthrough, add Playwright tests

Co-authored-by: sirpy <671095+sirpy@users.noreply.github.com>

Copilot AI commented Jul 29, 2026

Copy link
Copy Markdown
Contributor

@copilot address issues found in review. Also why aren't there any tests and screenshots of the widget?

All review issues addressed in commit Address review issues: ARIA, accessibility, SSR guard, dedup fallback, provider passthrough, add Playwright tests:

  1. components-test importsAccordion.tsx and ProgressBar.tsx now import XStack/YStack directly from tamagui (same as Card.ts).
  2. ProgressBar ARIA — added role="progressbar", aria-valuenow, aria-valuemin={0}, aria-valuemax={100}, aria-label on the track element.
  3. Accordion accessibility — header now has role="button", tabIndex={0}, aria-expanded, aria-controls (wired to the content id), and an onKeyDown handler for Enter/Space.
  4. register.ts SSR guard — both the register() function body and the auto-call at module load are now guarded with typeof window !== 'undefined' && 'customElements' in window.
  5. ACTIVITY_ICON_NAME_FALLBACK duplication — extracted to components/activityIconFallback.ts and imported by both ActionCard and ActivityIcons.
  6. CitizenClaimWidget provider propsprovider, config, themeOverrides, and defaultTheme are now forwarded through SuperfluidCampaignRuntimeProps to the embedded CitizenClaimWidget so it shares the same provider context.
  7. Tests and screenshots — added tests/widgets/superfluid-campaign-widget/states.spec.ts with 8 Playwright smoke tests (disconnected/connected × content/leaderboard, FAQ accordion, leaderboard navigation, mobile 480px, wide 900px). All 8 pass and 8 screenshots are committed to tests/widgets/superfluid-campaign-widget/test-results/.

Copilot finished work on behalf of sirpy July 29, 2026 10:57
Copilot AI requested a review from sirpy July 29, 2026 10:57
@sirpy

sirpy commented Jul 29, 2026

Copy link
Copy Markdown
Contributor

@L03TJ3 "components-test imports — Accordion.tsx and ProgressBar.tsx now import XStack/YStack directly from tamagui (same as Card.ts)."
What did you end up doing regarding xstack/ystack components?

…e compaction

- Add prominent "Claim SUP rewards" CTA opening claim.superfluid.org in a
  new tab, per Meri's change request.
- Add useAirdropStatus hook that fetches the connected wallet's live status
  from the Superfluid airdrop worker endpoint on connect/load/address-change,
  with loading and error states. Adds an airdropStatusAdapter DI seam
  (mirrors the ai-credits-widget adapterFactory convention) so Storybook/
  Playwright fixtures stay deterministic instead of hitting the live network.
- Mobile compaction pass: FAQ restructured into one collapsible section
  wrapping per-question accordion toggles, ProgressBar gains
  hidePercentageOnMobile to drop the redundant percentage where the bar
  already communicates it, ActionCard description truncates to one line on
  mobile, CampaignHeader title sized down on small viewports.
- Extend the Playwright spec with the new CTA/airdrop-status states and
  regenerate all baseline screenshots (scw-01..13) against the updated UI.

Leaderboard split (606/614) and campaign-level SUP totals remain blocked on
cms.superfluid.pro connection registration (task #20).

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
@L03TJ3

L03TJ3 commented Jul 30, 2026

Copy link
Copy Markdown
Contributor

@sirpy Nothing directly on the components. they are still re-exported from the UI package without any changes.
They are unstyled by default. We do this so that all consumers/widgets can and should only use the ui package as a dependency (import YStack from goodwidget/ui instead of YStack from tamagui)
Effectively the same component, just an unstyled container (at the moment, see below note)

In this PR, it really does not change anything if it imports from tamagui or from the local file 'Stacks.ts'

The only change based on our last conversation was made in the provider: https://github.com/GoodDollar/GoodWidget/blob/main/packages/core/src/provider.tsx
All widgets ship with a design-system-aligned background by default.

I have a bounty in preparation for doing a sweep of all the UI components and resolving 'align with design system' so that we can move away from components vs components-test.
Outside the scope of this PR, the current implementation decision is fine. the copilot update was basically unneccessary

BountyCoder and others added 5 commits July 30, 2026 05:54
- Apply compactButtonProps sizing to every Button across the widget
- Fix mobile copy wrapping in ActionCard/CampaignHeader body text
- Restructure ActionCard layout (icon/copy/points-pill/CTA column)
- Reclassify Text tone usage (secondary/soft) per design system
- Swap activity icons to design reference via new @goodwidget/ui
  ActivityIcons re-export of @tamagui/lucide-icons (keeps the direct
  lucide-icons dependency out of the widget package itself)
- Move "Connect wallet" CTA to the header's top-right slot
- Extend the Storybook react-native-svg web shim with named Svg plus
  Path/Line/Rect/Polygon/Polyline/Ellipse passthroughs, needed because
  @tamagui/lucide-icons' barrel export pulls in every icon module during
  the web build, not just the ones actually imported
- Regenerate all 13 Playwright baseline screenshots
Implements the widget from #129 (plan for #127): a
reusable GoodWidget presenting the Superfluid Ecosystem Rewards campaign —
header, two reward-pool action sections (GoodDollar actions, ecosystem
funding actions), a leaderboard view, and the campaign FAQ, against a
static mock dataset for this phase.

- New packages/superfluid-campaign-widget: presentational components,
  widgetRuntimeContract, mock dataset, and the element/register web
  component bridge (mirrors the streaming-widget bridge pattern).
- Adds a disconnected-state "Connect wallet" CTA in the content view,
  gated on useWallet().isConnected, matching the #127 acceptance criteria.
- Adds Accordion and ProgressBar to packages/ui (reusable, used by the
  FAQ and pool-progress sections).
- Adds an additive initialTab prop to citizen-claim-widget and an
  additive initialView prop to SuperfluidCampaignWidget, so Storybook/QA
  fixtures and deep links can land directly on a given view.
- Adds Storybook coverage under examples/storybook: a manual injected-
  wallet showcase story, four QA fixture stories (disconnected/connected
  x content/leaderboard) using the existing custodial-wallet fixture
  pattern from citizen-claim-widget, and a doc page that explicitly flags
  two known gaps for this phase (activity icon glyph registry, live
  leaderboard search/pagination).

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
…, provider passthrough, add Playwright tests

Co-authored-by: sirpy <671095+sirpy@users.noreply.github.com>
…e compaction

- Add prominent "Claim SUP rewards" CTA opening claim.superfluid.org in a
  new tab, per Meri's change request.
- Add useAirdropStatus hook that fetches the connected wallet's live status
  from the Superfluid airdrop worker endpoint on connect/load/address-change,
  with loading and error states. Adds an airdropStatusAdapter DI seam
  (mirrors the ai-credits-widget adapterFactory convention) so Storybook/
  Playwright fixtures stay deterministic instead of hitting the live network.
- Mobile compaction pass: FAQ restructured into one collapsible section
  wrapping per-question accordion toggles, ProgressBar gains
  hidePercentageOnMobile to drop the redundant percentage where the bar
  already communicates it, ActionCard description truncates to one line on
  mobile, CampaignHeader title sized down on small viewports.
- Extend the Playwright spec with the new CTA/airdrop-status states and
  regenerate all baseline screenshots (scw-01..13) against the updated UI.

Leaderboard split (606/614) and campaign-level SUP totals remain blocked on
cms.superfluid.pro connection registration (task #20).

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
- Apply compactButtonProps sizing to every Button across the widget
- Fix mobile copy wrapping in ActionCard/CampaignHeader body text
- Restructure ActionCard layout (icon/copy/points-pill/CTA column)
- Reclassify Text tone usage (secondary/soft) per design system
- Swap activity icons to design reference via new @goodwidget/ui
  ActivityIcons re-export of @tamagui/lucide-icons (keeps the direct
  lucide-icons dependency out of the widget package itself)
- Move "Connect wallet" CTA to the header's top-right slot
- Extend the Storybook react-native-svg web shim with named Svg plus
  Path/Line/Rect/Polygon/Polyline/Ellipse passthroughs, needed because
  @tamagui/lucide-icons' barrel export pulls in every icon module during
  the web build, not just the ones actually imported
- Regenerate all 13 Playwright baseline screenshots
@goodbounties-nanoclaw-agent
goodbounties-nanoclaw-agent Bot force-pushed the feat/superfluid-campaign-widget branch from 98699ea to a4040c6 Compare July 30, 2026 08:08
…rows

flex-wrap line assignment uses each item's natural (pre-shrink) hypothetical
size, not its shrunk rendered size, so the #130 flexWrap fix caused the
header CTA and action-card description rows to wrap to a new line at any
width where natural sizes exceeded the container -- even where ordinary
flex-shrink already fit them on one line pre-fix (regressing the signed-off
480px/900px baselines and default 1280px screenshots with ~500px of
unnecessary added height).

Add flexBasis={0} to the shrinkable flex children (ActionCard's description
Text, ConnectWalletPrompt's Button) so the browser's automatic-minimum-size
rule clamps the wrap-decision size to min-content instead of full natural
size, restoring shrink-first behavior while still wrapping only when
min-content itself doesn't fit.

Co-Authored-By: Claude <noreply@anthropic.com>
LeaderboardView rendered directly inside the widget's Card without the
$5 padding wrapper the content view uses, had no season badge next to
its "Superfluid" wordmark, and used a $4 gap token instead of $5 —
producing a header margin mismatch, a missing "Season 6" badge, and a
section title sitting flush against the left edge versus the content
view.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
CampaignHeader and LeaderboardView each render their own (separate) header
row; both needed flexWrap re-enabled so the header falls back to a wrapped
layout only when it genuinely doesn't fit, instead of overflowing past the
card's rounded-corner clip.

ConnectWalletPrompt's Button now uses flexShrink={0} instead of flexBasis={0}:
flexBasis={0} with no flex-grow let the flex algorithm shrink the button
toward zero width, visually breaking its "Connect wallet" label, because the
button's own overflow:hidden (for rounded corners) disables the browser's
automatic minimum-size protection. flexShrink={0} keeps the button at its
true natural size for both the wrap decision and its rendered width, so it
wraps to its own row only when that natural size doesn't fit — never
collapses/breaks.

Adds a permanent breakpoint regression suite (320/360/375/390/412px) for both
header variants, scoped to the header CTA only (ActionCard's own separate,
pre-existing pill-button clipping at these widths is out of scope here).

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
The close (X) button was wrapping down together with the Connect
wallet/wallet-chip group whenever the header row didn't fit on one line,
since both lived inside the same flex item. Split the close button into
its own non-wrapping sibling (flexShrink={0}) so it always stays pinned
top-right; only the wordmark/badge + CTA group wraps to its own line
below when genuinely out of room, matching CampaignHeader's existing
wrap-only-when-necessary behavior.

Adds a dedicated Playwright regression asserting the close button's row
position stays constant across breakpoints for both the disconnected and
connected Leaderboard header variants, and regenerates the affected
baseline screenshots.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

5 participants