Skip to content

Conversation

@dlabaj
Copy link
Contributor

@dlabaj dlabaj commented Feb 4, 2026

Closes issue #173 by adding support for PF Icons.

Summary by CodeRabbit

  • New Features

    • New API endpoints to list icons (with metadata and optional filter) and to return individual icon SVGs; includes prerendered icon index and SVG assets for reliability and performance.
    • Utilities to enumerate, filter, parse, and render icons to SVG.
  • Documentation

    • OpenAPI/spec updated to document the new icon endpoints.
  • Tests

    • Comprehensive tests for listing, filtering, SVG retrieval, and error cases.
  • Chores

    • Added react-icons and cleaned up unrelated dev dependencies.

@cloudflare-workers-and-pages
Copy link

cloudflare-workers-and-pages bot commented Feb 4, 2026

Deploying patternfly-doc-core with  Cloudflare Pages  Cloudflare Pages

Latest commit: a8654f0
Status: ✅  Deploy successful!
Preview URL: https://bacd433f.patternfly-doc-core.pages.dev
Branch Preview URL: https://icons-api.patternfly-doc-core.pages.dev

View logs

@coderabbitai
Copy link
Contributor

coderabbitai bot commented Feb 4, 2026

Note

Reviews paused

It looks like this branch is under active development. To avoid overwhelming you with review comments due to an influx of new commits, CodeRabbit has automatically paused this review. You can configure this behavior by changing the reviews.auto_review.auto_pause_after_reviewed_commits setting.

Use the following commands to manage reviews:

  • @coderabbitai resume to resume automatic reviews.
  • @coderabbitai review to trigger a single review.

Use the checkboxes below for quick actions:

  • ▶️ Resume reviews
  • 🔍 Trigger review

Walkthrough

Adds react-icons integration plus APIs to list/filter icons and serve per-icon SVGs, pre-rendered index and per-set SVG assets, utilities to load/filter/render icons, OpenAPI/API index updates, SVG response helper, and comprehensive tests.

Changes

Cohort / File(s) Summary
Dependencies
package.json
Added react-icons dependency; dev deps unchanged overall.
API Routes — runtime
src/pages/api/[version]/icons/index.ts, src/pages/api/[version]/icons/[iconName].ts, src/pages/api/index.ts
New GET handlers (export prerender = false, GET) for listing/filtering icons and returning per-icon SVG; API index updated to advertise endpoints; input validation and 400/404/500 error responses.
OpenAPI / Spec
src/pages/api/openapi.json.ts
Added OpenAPI paths for GET /{version}/icons and GET /{version}/icons/{icon-name} with params, responses, and operationIds.
Icons Utilities
src/utils/icons/reactIcons.ts, src/utils/icons/fetch.ts
New react-icons integration and metadata: IconMetadata, dynamic set imports, getAllIcons, filterIcons, getIconSvg, getIconSvgsForSet, parseIconId; fetch helpers fetchIconsIndex and fetchIconSvgs for prerendered assets.
API Helpers
src/utils/apiHelpers.ts
Extended getHeaders to include image/svg+xml and added createSvgResponse(content, status) to produce SVG responses.
Pre-rendered Index / SVG assets
src/pages/iconsIndex.json.ts, src/pages/iconsSvgs/[setId].json.ts
Added prerendered icons index (prerender = true) and per-set SVG JSON routes with getStaticPaths and GET to serve prebuilt SVG maps.
Tests
src/__tests__/pages/api/__tests__/[version]/icons/index.test.ts, src/__tests__/pages/api/__tests__/[version]/icons/[iconName].test.ts
New test suites covering listing, filtering (case-insensitive), empty results, SVG retrieval, and error cases (400/404/500); mocks for react-icons utilities and index/SVG fetches.

Sequence Diagram(s)

sequenceDiagram
    participant Client as Client
    participant API as Icons API Route
    participant Index as Icons Index (iconsIndex.json / fetchApiIndex)
    participant Utils as ReactIcons Utils
    participant RN as node_modules/react-icons

    Client->>API: GET /api/{version}/icons[?filter] or /api/{version}/icons/{set_icon}
    API->>Index: fetchIconsIndex(origin)
    Index-->>API: icons index (list)
    alt version exists
        API->>Utils: filterIcons or getIconSvg
        Utils->>RN: dynamic import icon set
        RN-->>Utils: icon components / metadata
        Utils-->>API: icons list or SVG markup
        API-->>Client: 200 JSON or image/svg+xml
    else version missing
        API-->>Client: 400 or 404 error
    end
Loading

Estimated code review effort

🎯 3 (Moderate) | ⏱️ ~25 minutes

Possibly related issues

Possibly related PRs

Suggested reviewers

  • wise-king-sullyman
  • kmcfaul
  • thatblindgeye
🚥 Pre-merge checks | ✅ 2 | ❌ 1
❌ Failed checks (1 warning)
Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 66.67% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (2 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title accurately describes the main change: adding API endpoints for retrieving icons, which is the core objective of this PR.

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

✨ Finishing touches
  • 📝 Generate docstrings
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Post copyable unit tests in a comment
  • Commit unit tests in branch icons-api

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
Contributor

@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.

Actionable comments posted: 1

🤖 Fix all issues with AI agents
In `@src/__tests__/pages/api/__tests__/`[version]/icons/index.test.ts:
- Line 100: The test's assertion calls expect(body.icons.every((i: { name:
string }) => i.name.includes('circle'))) but never asserts a value, so it always
passes; update the assertion to actually check the boolean (e.g., call
.toBe(true) or .toBeTruthy()) on the expression using body.icons and the
existing predicate to ensure the test fails when no icon names include "circle".
🧹 Nitpick comments (9)
src/utils/icons/reactIcons.ts (3)

50-54: Static analysis ReDoS warning is a false positive in this context.

The regex at line 51 is constructed from setId values that originate from IconsManifest (react-icons library internal data), not from user input. The pattern ^${setPrefix} is a simple anchored prefix match without problematic quantifiers, making ReDoS practically impossible here.

However, if you want to be extra defensive, you could sanitize the input or use a literal string match instead:

♻️ Optional: Use string operations instead of regex
   } else {
     const setPrefix = setId.charAt(0).toUpperCase() + setId.slice(1)
-    const prefix = new RegExp(`^${setPrefix}`, 'i')
-    base = base.replace(prefix, '')
+    if (base.toLowerCase().startsWith(setPrefix.toLowerCase())) {
+      base = base.slice(setPrefix.length)
+    }
   }

82-84: Consider logging failed icon set loads for debugging.

Silent failures make troubleshooting difficult. While graceful degradation is appropriate, logging would help identify issues during development or when icon sets are unexpectedly unavailable.

♻️ Optional: Add debug logging
-    } catch {
-      // Skip sets that fail to load
+    } catch (error) {
+      // Skip sets that fail to load, but log for debugging
+      console.debug(`Failed to load icon set '${setId}':`, error)
     }

62-88: Consider caching getAllIcons() result to avoid repeated dynamic imports.

getAllIcons() performs dynamic imports for every icon set on each call. For a production API, this could be expensive if called frequently. Consider memoizing the result since icon sets don't change at runtime.

♻️ Proposed caching implementation
+let cachedIcons: IconMetadata[] | null = null
+
 export async function getAllIcons(): Promise<IconMetadata[]> {
+  if (cachedIcons) {
+    return cachedIcons
+  }
+
   const icons: IconMetadata[] = []

   for (const setId of ICON_SET_IDS) {
     // ... existing code ...
   }

+  cachedIcons = icons
   return icons
 }
src/__tests__/pages/api/__tests__/[version]/icons/[iconName].test.ts (1)

17-24: Mock duplicates production logic - consider importing the real function.

The parseIconId mock reimplements the actual logic, which could lead to test/implementation drift. Consider using Jest's requireActual to import the real function while mocking only what's necessary.

♻️ Proposed: Use real parseIconId implementation
 jest.mock('../../../../../../utils/icons/reactIcons', () => ({
   getIconSvg: jest.fn((setId: string, iconName: string) => {
     if (setId === 'fa' && iconName === 'FaCircle') return Promise.resolve(mockSvg)
     return Promise.resolve(null)
   }),
-  parseIconId: jest.fn((iconId: string) => {
-    const underscoreIndex = iconId.indexOf('_')
-    if (underscoreIndex <= 0) return null
-    const setId = iconId.slice(0, underscoreIndex)
-    const iconName = iconId.slice(underscoreIndex + 1)
-    if (!setId || !iconName) return null
-    return { setId, iconName }
-  }),
+  parseIconId: jest.requireActual('../../../../../../utils/icons/reactIcons').parseIconId,
 }))
src/pages/api/openapi.json.ts (2)

107-163: Consider adding 404 response for version not found.

The /{version}/icons endpoint returns 404 when the version doesn't exist (per the implementation in src/pages/api/[version]/icons/index.ts), but this isn't documented in the OpenAPI spec.

📝 Add 404 response documentation
             '200': {
               description: 'List of icons with metadata',
               // ... existing schema ...
             },
+            '404': {
+              description: 'Version not found',
+            },
           },

164-201: Consider adding 400 response for invalid icon name format.

The /{version}/icons/{icon-name} endpoint returns 400 for invalid icon name formats, but only 404 is documented. This could cause confusion for API consumers.

📝 Add 400 response documentation
           responses: {
             '200': {
               description: 'SVG markup for the icon',
               content: {
                 'image/svg+xml': {
                   schema: { type: 'string' },
                 },
               },
             },
+            '400': {
+              description: 'Invalid icon name format. Expected: {set}_{iconName}',
+            },
             '404': {
               description: 'Icon not found',
             },
           },
src/pages/api/[version]/icons/[iconName].ts (1)

17-17: Minor: Variable aliasing may cause confusion.

The destructuring { version, iconName: iconId } creates an alias, but iconName is reused later (line 53) as a different variable from parseIconId. While not a bug, this could be clearer.

♻️ Suggested clarification

Consider renaming for clarity:

-  const { version, iconName: iconId } = params
+  const { version, iconName: rawIconId } = params

And update references to iconIdrawIconId accordingly.

src/__tests__/pages/api/__tests__/[version]/icons/index.test.ts (1)

50-51: Consider using describe block and afterEach for cleaner test organization.

Tests would benefit from wrapping in a describe block and moving the repeated jest.restoreAllMocks() calls to an afterEach hook.

♻️ Suggested structure
+describe('GET /api/{version}/icons', () => {
+  afterEach(() => {
+    jest.restoreAllMocks()
+  })
+
 it('returns all icons with metadata', async () => {
   global.fetch = jest.fn(() =>
     ...
   expect(body.icons[0]).toHaveProperty('unicode')
-
-  jest.restoreAllMocks()
 })
 // ... other tests without individual restoreAllMocks calls
+})

Also applies to: 79-80

src/pages/api/[version]/icons/index.ts (1)

32-33: Consider pagination for large icon sets.

react-icons contains thousands of icons across all sets. Returning all icons in a single response could impact performance and client-side rendering. Consider adding pagination (limit/offset or cursor-based) for production use.

@dlabaj dlabaj changed the title WIP Feat (icons): Icons API end points for retrieving icons. feat (icons): Icons API end points for retrieving icons. Feb 6, 2026
Copy link
Contributor

@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.

Actionable comments posted: 1

🤖 Fix all issues with AI agents
In `@src/pages/api/`[version]/icons/[iconName].ts:
- Around line 42-59: The call to fetchIconsIndex (and subsequent fetchIconSvgs)
can throw and is not wrapped, causing uncontrolled 500s; wrap the body that uses
fetchIconsIndex, icons, icon, fetchIconSvgs and svg in a try/catch around the
existing logic inside the handler, and in the catch return createJsonResponse({
error: 'Internal server error', details: error?.message ?? String(error) }, 500)
(or similar) so failures from fetchIconsIndex/fetchIconSvgs produce a clean JSON
500 response instead of an unhandled exception.
🧹 Nitpick comments (2)
src/__tests__/pages/api/__tests__/[version]/icons/index.test.ts (1)

62-87: Consider cleaning up global.fetch in an afterEach block.

jest.restoreAllMocks() only restores mocks created via jest.spyOn, not direct global.fetch assignments. If a test throws before the restoreAllMocks call, the mutated global.fetch leaks. Since each test reassigns it anyway this is low risk, but an afterEach would be cleaner.

♻️ Suggested cleanup pattern
+const originalFetch = global.fetch
+
+afterEach(() => {
+  global.fetch = originalFetch
+  jest.restoreAllMocks()
+})
+
 it('returns all icons with metadata', async () => {
   global.fetch = createFetchMock()
   // ...
-  jest.restoreAllMocks()
 })
src/utils/icons/reactIcons.ts (1)

117-145: Consider the memory and build-time cost of rendering all icons in a set.

getIconSvgsForSet renders every icon in a set to SVG at build time via renderToStaticMarkup. For large sets (e.g., Material Design has ~10,000+ icons), this could be memory-intensive during the build. Since this runs at prerender time (not in Workers), it should be fine, but worth noting if build times become a concern.

Comment on lines +42 to +59
const icons = await fetchIconsIndex(url)
const icon = icons.find((i) => i.reactName === reactName)
if (!icon?.set) {
return createJsonResponse(
{ error: `Icon '${reactName}' not found` },
404,
)
}

const svgs = await fetchIconSvgs(url, icon.set)
const svg = svgs?.[reactName] ?? null

if (!svg) {
return createJsonResponse(
{ error: `Icon '${reactName}' not found in set '${icon.set}'` },
404,
)
}
Copy link
Contributor

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🟠 Major

Unhandled error from fetchIconsIndex — will produce an uncontrolled 500.

fetchIconsIndex throws on non-OK responses (see src/utils/icons/fetch.ts lines 19-23), but this call sits outside any try/catch. Compare with src/pages/api/[version]/icons/index.ts, which wraps everything in a single try/catch and returns a clean 500 JSON response.

🐛 Proposed fix: wrap the remaining logic in a try/catch
+  try {
     const icons = await fetchIconsIndex(url)
     const icon = icons.find((i) => i.reactName === reactName)
     if (!icon?.set) {
       return createJsonResponse(
         { error: `Icon '${reactName}' not found` },
         404,
       )
     }
 
     const svgs = await fetchIconSvgs(url, icon.set)
     const svg = svgs?.[reactName] ?? null
 
     if (!svg) {
       return createJsonResponse(
         { error: `Icon '${reactName}' not found in set '${icon.set}'` },
         404,
       )
     }
 
     return createSvgResponse(svg)
+  } catch (error) {
+    const details = error instanceof Error ? error.message : String(error)
+    return createJsonResponse(
+      { error: 'Failed to load icon', details },
+      500,
+    )
+  }
🤖 Prompt for AI Agents
In `@src/pages/api/`[version]/icons/[iconName].ts around lines 42 - 59, The call
to fetchIconsIndex (and subsequent fetchIconSvgs) can throw and is not wrapped,
causing uncontrolled 500s; wrap the body that uses fetchIconsIndex, icons, icon,
fetchIconSvgs and svg in a try/catch around the existing logic inside the
handler, and in the catch return createJsonResponse({ error: 'Internal server
error', details: error?.message ?? String(error) }, 500) (or similar) so
failures from fetchIconsIndex/fetchIconSvgs produce a clean JSON 500 response
instead of an unhandled exception.

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.

1 participant