Skip to content

Conversation

@kmcfaul
Copy link
Contributor

@kmcfaul kmcfaul commented Jan 16, 2026

What: Towards #12163

  • Added a mapping for existing PF icons to RH-ui icons based on design's google sheet, and added an additional section of mappings based on Cursor's comparison of icon names (between PF & RHui). We will want a full audit / final mapping in the future for a release.

Currently, this PR uses a CSS method to swap between two svgs nested inside a wrapper svg that contains shared metadata, for icons with mapped alternate data. When an icon lacks that alternate data, or when the set prop is specified, the previous flat svg structure is returned with the proper icon data.

Commented out in createIcon is an alternate react method, using the set prop or by detecting a global class modifier, to hotswap the svg path data / viewbox data within a single flat svg. This method will not respond to dynamic global icon set swapping via the class modifier unless the MutationObserver logic is present, which may impact performance (the icon would remain in the previous set until a user prompts a rerender of the component). However, it may be the expected case that icons are defined at build time/once in the user application and not dynamically swapped, so it may be possible to keep the react method without the MutationObserver.

I would be curious for people's thoughts about the two approaches: render two inner svgs per svg & use CSS to swap visibility, or maintain the current svg rendering and use react to swap the data (ignoring any dynamic swapping case).

To test this CSS method, you will have to inspect an element (I tested with the html element), press the + button to add a new class, select the inspector stylesheet to get access to the local css stylesheet, and then paste the following block:

  .pf-v6-icon-unified {
   display: none;
  }
  
  body.pf-v6-theme-unified {
    .pf-v6-icon-default {
     display: none;
  }
   .pf-v6-icon-unified {
     display: revert;
   }
 }

This will allow the icons to be viewed properly and toggled with a .pf-v6-theme-unified class on the body element. Without this block, both icons will be rendered simultaneously unless set is specified (which returns the flat svg instead).

Summary by CodeRabbit

  • New Features

    • Added a mapping from legacy/design icons to a unified icon set and support for alternative icon variants.
  • Refactor

    • Icon component and generation pipeline updated to accept both sources and a new render mode prop ("default" | "unified") to choose which to render.
  • Tests

    • Updated tests for multi-path icons and the new icon data shapes and props.

✏️ Tip: You can customize this high-level summary in your review settings.

@coderabbitai
Copy link

coderabbitai bot commented Jan 16, 2026

Walkthrough

Adds a pfToRhIcons mapping and threads an optional rhUiIcon through icon export generation and runtime. createIcon now accepts dual icon definitions and a new svgPathData shape; rendering selects between icon and rhUiIcon. MutationObserver-based theme-detection scaffolding is present but inactive.

Changes

Cohort / File(s) Summary
Icon Generation Script
packages/react-icons/scripts/writeIcons.mjs
Imports pfToRhIcons, computes an altIcon (rhUiIcon) for each icon, updates writeCJSExport, writeESMExport, writeDTSExport signatures to accept rhUiIcon = null, and emits rhUiIcon in generated config objects.
Icon Mapping Data
packages/react-icons/src/pfToRhIcons.js
New module exporting pfToRhIcons that maps design-sheet names to rh-ui icon data using a getIconData helper; initializes a large static mapping.
Icon Creation Component
packages/react-icons/src/createIcon.tsx
Replaces svgPath with svgPathData (string
Tests
packages/react-icons/src/__tests__/createIcon.test.tsx
Tests updated to use svgPathData shape and new exported types (IconDefinition, CreateIconProps, SVGPathObject); assertions adapted for single- and multi-path icons and new className/path shapes.

Sequence Diagram(s)

sequenceDiagram
    actor User
    participant Component as Icon Component
    participant Script as writeIcons (build)
    participant Mapping as pfToRhIcons
    participant Renderer as Render Method
    participant Document
    participant Observer as MutationObserver (scaffold)

    Note over Script,Mapping: Build-time
    Script->>Mapping: import pfToRhIcons
    Script->>Script: compute altIcon (rhUiIcon) per icon
    Script-->>Component: emit icon configs including rhUiIcon

    Note over User,Component: Runtime
    User->>Component: Mount (props: set?/icon/rhUiIcon)
    activate Component
    alt MutationObserver scaffold enabled
        Component->>Observer: start observing Document classes
    end
    Component->>Renderer: render()
    activate Renderer
    Renderer->>Renderer: choose source (props.set, rhUiIcon, icon)
    Renderer->>Renderer: compute viewBox & svg paths from svgPathData
    Renderer-->>User: return SVG element
    deactivate Renderer
    deactivate Component
Loading

Estimated code review effort

🎯 4 (Complex) | ⏱️ ~45 minutes

🚥 Pre-merge checks | ✅ 3
✅ Passed checks (3 passed)
Check name Status Explanation
Title check ✅ Passed The title accurately describes the main purpose of the PR: implementing icon hotswapping functionality between PatternFly and RH-UI icons, which aligns with the substantial changes across multiple files to support dual icon rendering.
Docstring Coverage ✅ Passed Docstring coverage is 100.00% which is sufficient. The required threshold is 80.00%.
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.

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

✨ Finishing touches
  • 📝 Generate docstrings

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.

@patternfly-build
Copy link
Collaborator

patternfly-build commented Jan 16, 2026

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.

Actionable comments posted: 1

🤖 Fix all issues with AI agents
In `@packages/react-icons/src/createIcon.tsx`:
- Around line 18-22: createIcon currently expects an icon object but tests and
generateStaticSVG pass top-level SVG props, so when icon is undefined iconData
becomes undefined and renders empty SVG; update createIcon (and/or the
CreateIconProps handling) to normalize legacy callers by merging top-level
properties into the expected icon shape when props.icon is missing — e.g., if
icon is undefined, build iconData from top-level width, height, svgPath,
svgPathData, viewBox, etc., so functions like createIcon and any usage of
iconData (and generateStaticSVG) continue to work with both new and legacy call
sites.
🧹 Nitpick comments (2)
packages/react-icons/src/pfToRhIcons.js (1)

3-5: Detect missing RH UI icon mappings early.

getIconData silently returns null, so typos or missing RH UI icons won’t be obvious and swapping will quietly fail. Consider a build‑time check or warning for missing mappings (especially for the design‑sheet section) so mismatches are surfaced quickly.

packages/react-icons/src/createIcon.tsx (1)

49-71: Reduce per‑icon MutationObserver overhead.

Each icon instance attaches its own observer to <html>, which can be costly on icon‑dense screens. Consider a shared singleton observer / global theme state, and at minimum skip the observer when set is explicitly provided (and guard MutationObserver availability).

📜 Review details

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 884ad17 and faf45e4.

📒 Files selected for processing (3)
  • packages/react-icons/scripts/writeIcons.mjs
  • packages/react-icons/src/createIcon.tsx
  • packages/react-icons/src/pfToRhIcons.js
🧰 Additional context used
🧬 Code graph analysis (2)
packages/react-icons/scripts/writeIcons.mjs (1)
packages/react-icons/src/pfToRhIcons.js (2)
  • pfToRhIcons (8-316)
  • pfToRhIcons (8-316)
packages/react-icons/src/createIcon.tsx (3)
packages/react-icons/scripts/writeIcons.mjs (1)
  • createIcon (12-12)
packages/react-icons/scripts/parseRHIcons.mjs (2)
  • xOffset (72-72)
  • svgPathData (86-87)
packages/react-icons/scripts/icons/fontawesomeIcons.mjs (1)
  • width (10-10)
⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (3)
  • GitHub Check: Lint
  • GitHub Check: Build
  • GitHub Check: Build, test & deploy
🔇 Additional comments (1)
packages/react-icons/scripts/writeIcons.mjs (1)

20-28: Nice wiring of rhUiIcon through generated exports.

The CJS/ESM/DTS writers and the generator all include the alternate icon data consistently.

Also applies to: 36-45, 54-61, 125-128

✏️ Tip: You can disable this entire section by setting review_details to false in your review settings.

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.

Actionable comments posted: 2

Caution

Some comments are outside the diff and can’t be posted inline due to platform limitations.

⚠️ Outside diff range comments (1)
packages/react-icons/src/__tests__/createIcon.test.tsx (1)

44-47: Test accesses non-existent property iconDef.svgPath.

iconDef is typed as CreateIconProps, which does not have a svgPath property. This test will always pass incorrectly because iconDef.svgPath is undefined, and the assertion compares against an undefined attribute.

The correct property path is iconDef.icon?.svgPathData or simply use singlePathIcon.svgPathData.

Fix the assertion
 test('sets correct svgPath if string', () => {
   render(<SVGIcon />);
-  expect(screen.getByRole('img', { hidden: true }).querySelector('path')).toHaveAttribute('d', iconDef.svgPath);
+  expect(screen.getByRole('img', { hidden: true }).querySelector('path')).toHaveAttribute('d', singlePathIcon.svgPathData);
 });
🤖 Fix all issues with AI agents
In `@packages/react-icons/src/createIcon.tsx`:
- Around line 49-72: Multiple MutationObserver instances are created per icon in
componentDidMount, which scales poorly; replace the per-instance observer with a
module-level singleton observer (themeObserver) and a
subscribeToThemeChanges(callback) API that maintains a Set listeners; in
componentDidMount subscribe a callback that checks rhUiIcon/canUseDOM and the
this.props.set condition and calls this.setState(prev => ({ themeClassVersion:
prev.themeClassVersion + 1 })), and in componentWillUnmount unsubscribe the
callback to remove it from listeners so the singleton observer only notifies
registered icons and cleans up when none remain.
- Around line 106-112: The code in createIcon.tsx can render <path
d={undefined}> when svgPathData is undefined; update the svgPaths construction
to first check for svgPathData being null/undefined and return null (or an empty
fragment) in that case, otherwise keep the existing Array.isArray(svgPathData)
branch (mapping pathObject.className and pathObject.path with stable keys) and
the string branch; ensure the check references the svgPathData variable so no
invalid <path d={undefined}> is emitted.
♻️ Duplicate comments (2)
packages/react-icons/src/createIcon.tsx (2)

18-22: Handle legacy createIcon callers that pass top-level icon properties.

The new signature expects { name, icon, rhUiIcon }, but per the past review, existing call sites (tests and generateStaticSVG in writeIcons.mjs) may pass top-level properties like width, height, svgPathData. When icon is undefined, iconData becomes undefined at line 91, causing the SVG to render without valid dimensions or paths.

Consider adding a fallback to support legacy callers:

Suggested backward-compatible normalization
 export function createIcon({ name, icon, rhUiIcon = null }: CreateIconProps): React.ComponentClass<SVGIconProps> {
+  // Support legacy callers passing top-level icon properties
+  const normalizedIcon = icon ?? (arguments[0] as IconDefinition);
   return class SVGIcon extends Component<SVGIconProps, { themeClassVersion: number }> {

Then use normalizedIcon in place of icon throughout.


90-96: Guard against undefined iconData to prevent runtime errors.

When icon is undefined and shouldUseAltData is false, iconData becomes undefined. The destructuring at line 91 would yield undefined values for width and height, resulting in a malformed viewBox like "0 0 undefined undefined".

Add defensive guard
       const iconData = shouldUseAltData ? rhUiIcon : icon;
+      if (!iconData) {
+        // Return null or a placeholder when no icon data is available
+        return null;
+      }
       const { xOffset, yOffset, width, height, svgClassName, svgPathData } = iconData ?? {};
🧹 Nitpick comments (1)
packages/react-icons/src/createIcon.tsx (1)

131-196: Remove or extract commented-out experimental code.

The large block of commented code clutters the file. If this CSS-based approach is being evaluated, consider moving it to a separate branch, a design document, or a clearly marked experimental file.

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.

Actionable comments posted: 1

🤖 Fix all issues with AI agents
In `@packages/react-icons/src/createIcon.tsx`:
- Around line 124-142: When set === 'unified' but rhUiIcon is null the code sets
iconData to null resulting in invalid viewBox and empty path; update the logic
in createIcon (around the iconData assignment and rendering of viewBox/svgPaths)
to fall back to the default icon when rhUiIcon is missing or short-circuit
early: compute iconData as set === 'unified' ? (rhUiIcon ?? icon) : (set !==
undefined ? icon : (rhUiIcon ?? icon)), and if iconData is still falsy return
null (or render a defined fallback) before using xOffset/width/svgPathData so
you never render viewBox="0 0 undefined undefined" or a path with d={undefined}.
♻️ Duplicate comments (1)
packages/react-icons/src/createIcon.tsx (1)

49-56: Guard against undefined svgPathData to avoid <path d={undefined}>.

Both the helper and render branch fall through to a <path> when svgPathData is missing. This yields invalid SVG markup.

✅ Suggested guard
-  const svgPaths =
-    svgPathData && Array.isArray(svgPathData) ? (
+  const svgPaths =
+    !svgPathData ? null : Array.isArray(svgPathData) ? (
       svgPathData.map((pathObject, index) => (
         <path className={pathObject.className} key={`${pathObject.path}-${index}`} d={pathObject.path} />
       ))
     ) : (
       <path d={svgPathData as string} />
     );
-        const svgPaths =
-          svgPathData && Array.isArray(svgPathData) ? (
+        const svgPaths =
+          !svgPathData ? null : Array.isArray(svgPathData) ? (
             svgPathData.map((pathObject, index) => (
               <path className={pathObject.className} key={`${pathObject.path}-${index}`} d={pathObject.path} />
             ))
           ) : (
             <path d={svgPathData as string} />
           );

Also applies to: 135-142

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.

2 participants