Skip to content

feat(mobile-expo): CardRenderer section component#327

Open
up-tandem wants to merge 1 commit intomainfrom
feat/312-card-renderer
Open

feat(mobile-expo): CardRenderer section component#327
up-tandem wants to merge 1 commit intomainfrom
feat/312-card-renderer

Conversation

@up-tandem
Copy link
Contributor

@up-tandem up-tandem commented Mar 10, 2026

Summary

  • Replaces the Card stub with a real renderer
  • Displays optional media image, title, and description
  • Tappable via Pressable when link is present (opens URL via Linking)
  • Supports default and featured variant styling (larger image, bolder title, stronger shadow for featured)
  • Graceful handling of missing optional fields (no image if media absent, non-tappable if no link)
  • Accessible labels on interactive elements

Contracts Changed

No

Regeneration Required

No

Validation

  • pnpm --filter @forge/expo lint — passes with 0 warnings
  • pnpm --filter @forge/expo test -- --testPathPattern=CardRenderer — 5/5 tests pass
  • pnpm --filter @forge/expo test -- --testPathPattern=SectionDispatcher — 11/11 tests pass (no regressions)

Resolves #312

🤖 Generated with Claude Code

Summary by CodeRabbit

  • New Features

    • Introduced improved card component with support for media, title, description, and optional links for navigation.
    • Added featured variant styling for enhanced visual presentation.
    • Enhanced accessibility with labels and roles for interactive cards.
  • Tests

    • Added comprehensive unit tests covering card rendering across multiple configurations.

Replace the Card stub with a real renderer displaying media image,
title, description, and tappable link. Supports default and featured
variant styling with shadow/elevation differences.

Resolves #312

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
@coderabbitai
Copy link

coderabbitai bot commented Mar 10, 2026

Walkthrough

Introduces a production CardRenderer component for React Native Expo that renders cards with optional media, title, description, and tappable links. Supports default and featured variants with corresponding styling. Replaces an existing stub component. Includes comprehensive unit tests covering various configurations.

Changes

Cohort / File(s) Summary
CardRenderer Implementation
mobile/expo/src/components/sections/CardRenderer.tsx, mobile/expo/src/components/sections/CardRenderer.test.tsx
New component renders cards with optional media, title, description, and link wrapping in Pressable. Supports featured variant affecting image height, text sizing, and shadows. Includes accessibility labels. Test suite verifies rendering across minimal fields, featured variant, and non-interactive states.
Stub Removal & Import Updates
mobile/expo/src/components/sections/CardStub.tsx, mobile/expo/src/components/sections/SectionDispatcher.tsx, mobile/expo/src/components/sections/index.ts
Removes deprecated CardStub placeholder. Updates two import statements to source CardRenderer from new production file instead of stub.

Estimated code review effort

🎯 3 (Moderate) | ⏱️ ~25 minutes

🚥 Pre-merge checks | ✅ 4 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 0.00% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (4 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title accurately and concisely summarizes the main change: introducing a real CardRenderer component for mobile Expo, replacing the stub.
Linked Issues check ✅ Passed All coding requirements from issue #312 are met: CardRenderer renders title, description, media (when present), supports link wrapping with Pressable for navigation, implements default and featured variants with distinct styling, handles missing optional fields gracefully, replaces the stub in SectionDispatcher, and includes accessibility attributes.
Out of Scope Changes check ✅ Passed All changes directly support the CardRenderer implementation. The new component, test file, removal of stub, and import updates are all in scope and necessary for issue #312 resolution.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

✨ Finishing Touches
  • 📝 Generate docstrings (stacked PR)
  • 📝 Generate docstrings (commit on current branch)
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Post copyable unit tests in a comment
  • Commit unit tests in branch feat/312-card-renderer

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands and usage tips.

Copy link

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

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

🧹 Nitpick comments (2)
mobile/expo/src/components/sections/CardRenderer.test.tsx (1)

21-26: Tests only verify createElement doesn't throw, not actual rendering.

Using createElement without a test renderer means these tests don't execute the component's render logic or verify the DOM output. Runtime errors in JSX (e.g., accessing properties on null) won't be caught.

Consider using @testing-library/react-native's render to actually mount the component and optionally assert on rendered content.

♻️ Example with actual rendering
-import { createElement } from "react"
+import { render, screen } from "@testing-library/react-native"

 import type { CardSection } from "../../lib/sectionModels"
 import { CardRenderer } from "./CardRenderer"

 // ... baseSection fixture ...

 describe("CardRenderer", () => {
   it("renders without throwing with all fields", () => {
-    expect(() =>
-      createElement(CardRenderer, { section: baseSection }),
-    ).not.toThrow()
+    render(<CardRenderer section={baseSection} />)
+    expect(screen.getByText("Explore the Easter Story")).toBeTruthy()
   })
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@mobile/expo/src/components/sections/CardRenderer.test.tsx` around lines 21 -
26, The test currently only calls createElement(CardRenderer, { section:
baseSection }) which doesn't mount or execute rendering logic; replace the
no-throw check with an actual render using `@testing-library/react-native`'s
render(CardRenderer with props) (or render(<CardRenderer section={baseSection}
/>)) and add at least one assertion that verifies rendered output (e.g.,
queryByText or getByTestId) to catch runtime JSX errors and validate UI; update
imports to include render from "@testing-library/react-native" and reference
CardRenderer and baseSection in the new test.
mobile/expo/src/components/sections/CardRenderer.tsx (1)

13-17: Consider handling Linking.openURL errors.

Linking.openURL can reject if the URL scheme is unsupported or malformed. Silently ignoring the promise may leave users without feedback when a link fails to open.

♻️ Suggested improvement with error handling
 const handlePress = () => {
   if (link) {
-    void Linking.openURL(link)
+    Linking.openURL(link).catch((err) => {
+      console.warn("CardRenderer: failed to open link", link, err)
+    })
   }
 }
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@mobile/expo/src/components/sections/CardRenderer.tsx` around lines 13 - 17,
The handlePress function currently calls Linking.openURL(link) without handling
rejections; update handlePress to await or attach a catch to
Linking.openURL(link) and handle errors (e.g., call Alert.alert or
processLogger.error and optionally fallback behavior) so users receive feedback
when the URL fails to open; reference the handlePress function and the
Linking.openURL call when making this change.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Nitpick comments:
In `@mobile/expo/src/components/sections/CardRenderer.test.tsx`:
- Around line 21-26: The test currently only calls createElement(CardRenderer, {
section: baseSection }) which doesn't mount or execute rendering logic; replace
the no-throw check with an actual render using `@testing-library/react-native`'s
render(CardRenderer with props) (or render(<CardRenderer section={baseSection}
/>)) and add at least one assertion that verifies rendered output (e.g.,
queryByText or getByTestId) to catch runtime JSX errors and validate UI; update
imports to include render from "@testing-library/react-native" and reference
CardRenderer and baseSection in the new test.

In `@mobile/expo/src/components/sections/CardRenderer.tsx`:
- Around line 13-17: The handlePress function currently calls
Linking.openURL(link) without handling rejections; update handlePress to await
or attach a catch to Linking.openURL(link) and handle errors (e.g., call
Alert.alert or processLogger.error and optionally fallback behavior) so users
receive feedback when the URL fails to open; reference the handlePress function
and the Linking.openURL call when making this change.

ℹ️ Review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro

Run ID: 052e8143-18f0-4a35-9266-85f60eb010ae

📥 Commits

Reviewing files that changed from the base of the PR and between 2bc0bcd and 24dda85.

📒 Files selected for processing (5)
  • mobile/expo/src/components/sections/CardRenderer.test.tsx
  • mobile/expo/src/components/sections/CardRenderer.tsx
  • mobile/expo/src/components/sections/CardStub.tsx
  • mobile/expo/src/components/sections/SectionDispatcher.tsx
  • mobile/expo/src/components/sections/index.ts
💤 Files with no reviewable changes (1)
  • mobile/expo/src/components/sections/CardStub.tsx

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.

feat(mobile-expo): CardRenderer section component

1 participant