@@ -5,22 +5,42 @@ export interface SVGPathObject {
55 className ?: string ;
66}
77
8- export interface IconDefinition {
8+ export interface IconDefinitionBase {
99 name ?: string ;
1010 width : number ;
1111 height : number ;
12- svgPathData : string | SVGPathObject [ ] ;
1312 xOffset ?: number ;
1413 yOffset ?: number ;
1514 svgClassName ?: string ;
1615}
1716
17+ /** Icon metadata using the current `svgPathData` field name. */
18+ export interface IconDefinitionWithSvgPathData extends IconDefinitionBase {
19+ svgPathData : string | SVGPathObject [ ] ;
20+ }
21+
22+ /**
23+ * @deprecated Use {@link IconDefinitionWithSvgPathData} with `svgPathData` instead.
24+ */
25+ export interface IconDefinitionWithSvgPath extends IconDefinitionBase {
26+ svgPath : string | SVGPathObject [ ] ;
27+ }
28+
29+ /** Describes SVG path content for one icon variant (default or rh-ui). */
30+ export type IconDefinition = IconDefinitionWithSvgPathData | IconDefinitionWithSvgPath ;
31+
1832export interface CreateIconProps {
1933 name ?: string ;
2034 icon ?: IconDefinition ;
2135 rhUiIcon ?: IconDefinition | null ;
2236}
2337
38+ /**
39+ * @deprecated The previous `createIcon` accepted a flat {@link IconDefinition} with top-level
40+ * `svgPath`. Pass {@link CreateIconProps} with a nested `icon` field instead.
41+ */
42+ export type LegacyFlatIconDefinition = IconDefinition ;
43+
2444export interface SVGIconProps extends Omit < React . HTMLProps < SVGElement > , 'ref' > {
2545 title ?: string ;
2646 className ?: string ;
@@ -30,7 +50,56 @@ export interface SVGIconProps extends Omit<React.HTMLProps<SVGElement>, 'ref'> {
3050
3151let currentId = 0 ;
3252
33- const createSvg = ( icon : IconDefinition , iconClassName : string ) => {
53+ function resolveSvgPathData ( icon : IconDefinition ) : string | SVGPathObject [ ] {
54+ if ( 'svgPathData' in icon && icon . svgPathData !== undefined ) {
55+ return icon . svgPathData ;
56+ }
57+ if ( 'svgPath' in icon && icon . svgPath !== undefined ) {
58+ return icon . svgPath ;
59+ }
60+ throw new Error ( '@patternfly/react-icons: IconDefinition must define svgPathData or svgPath' ) ;
61+ }
62+
63+ function normalizeIconDefinition ( icon : IconDefinition ) : IconDefinitionWithSvgPathData {
64+ return {
65+ name : icon . name ,
66+ width : icon . width ,
67+ height : icon . height ,
68+ svgPathData : resolveSvgPathData ( icon ) ,
69+ xOffset : icon . xOffset ,
70+ yOffset : icon . yOffset ,
71+ svgClassName : icon . svgClassName
72+ } ;
73+ }
74+
75+ function isNestedCreateIconProps ( arg : object ) : arg is CreateIconProps {
76+ return 'icon' in arg || 'rhUiIcon' in arg ;
77+ }
78+
79+ /** Props after resolving legacy `svgPath` and flat `createIcon` arguments. */
80+ interface NormalizedCreateIconProps {
81+ name ?: string ;
82+ icon ?: IconDefinitionWithSvgPathData ;
83+ rhUiIcon : IconDefinitionWithSvgPathData | null ;
84+ }
85+
86+ function normalizeCreateIconArg ( arg : CreateIconProps | LegacyFlatIconDefinition ) : NormalizedCreateIconProps {
87+ if ( isNestedCreateIconProps ( arg ) ) {
88+ const p = arg as CreateIconProps ;
89+ return {
90+ name : p . name ,
91+ icon : p . icon !== undefined && p . icon !== null ? normalizeIconDefinition ( p . icon ) : undefined ,
92+ rhUiIcon : p . rhUiIcon != null ? normalizeIconDefinition ( p . rhUiIcon ) : null
93+ } ;
94+ }
95+ return {
96+ name : ( arg as LegacyFlatIconDefinition ) . name ,
97+ icon : normalizeIconDefinition ( arg as IconDefinition ) ,
98+ rhUiIcon : null
99+ } ;
100+ }
101+
102+ const createSvg = ( icon : IconDefinitionWithSvgPathData , iconClassName : string ) => {
34103 const { xOffset, yOffset, width, height, svgPathData, svgClassName } = icon ?? { } ;
35104 const _xOffset = xOffset ?? 0 ;
36105 const _yOffset = yOffset ?? 0 ;
@@ -62,18 +131,38 @@ const createSvg = (icon: IconDefinition, iconClassName: string) => {
62131} ;
63132
64133/**
65- * Factory to create Icon class components for consumers
134+ * Builds a React **class** component that renders a PatternFly SVG icon (`role="img"`, optional `<title>` for a11y).
135+ *
136+ * **Argument shape — pick one:**
137+ *
138+ * 1. **`CreateIconProps` (preferred)** — `{ name?, icon?, rhUiIcon? }`. Dimensions and path data sit on `icon`
139+ * (and optionally on `rhUiIcon` for Red Hat UI–mapped icons). If the object **has an `icon` or `rhUiIcon` key**
140+ * (including `rhUiIcon: null`), this shape is assumed.
141+ *
142+ * 2. **Legacy flat `IconDefinition`** — the same fields as `icon`, but at the **top level** (no nested `icon`).
143+ * Still accepted so existing callers are not broken. Prefer migrating to `CreateIconProps`.
144+ *
145+ * **Path data on each `IconDefinition`:** use `svgPathData` (string or {@link SVGPathObject}[]). The old name
146+ * `svgPath` is deprecated but still read; `svgPathData` wins if both are present.
147+ *
148+ * **Default vs RH UI rendering:** If `rhUiIcon` is set and the consumer does **not** pass `set` on the component,
149+ * the output is an outer `<svg.pf-v6-svg>` containing **two** inner `<svg>`s (default + rh-ui) so CSS can swap
150+ * which variant is visible. If `set` is `"default"` or `"rh-ui"`, a **single** flat `<svg>` is rendered for that
151+ * variant. Requesting `set="rh-ui"` when there is no `rhUiIcon` falls back to the default glyph and logs a
152+ * `console.warn` (see implementation).
153+ *
154+ * @param arg Icon configuration: either {@link CreateIconProps} (nested `icon` / `rhUiIcon`) or a legacy flat
155+ * {@link LegacyFlatIconDefinition}. Runtime detection follows the rules in **Argument shape** above.
156+ * @returns A `ComponentClass<SVGIconProps>` — render it as `<YourIcon />` or with `title`, `className`, `set`, etc.
66157 */
67- export function createIcon ( { name, icon, rhUiIcon = null } : CreateIconProps ) : React . ComponentClass < SVGIconProps > {
158+ export function createIcon ( arg : CreateIconProps | LegacyFlatIconDefinition ) : React . ComponentClass < SVGIconProps > {
159+ const { name, icon, rhUiIcon = null } = normalizeCreateIconArg ( arg ) ;
160+
68161 return class SVGIcon extends Component < SVGIconProps > {
69162 static displayName = name ;
70163
71164 id = `icon-title-${ currentId ++ } ` ;
72165
73- constructor ( props : SVGIconProps ) {
74- super ( props ) ;
75- }
76-
77166 render ( ) {
78167 const { title, className : propsClassName , set, ...props } = this . props ;
79168
@@ -92,8 +181,10 @@ export function createIcon({ name, icon, rhUiIcon = null }: CreateIconProps): Re
92181 }
93182
94183 if ( ( set === undefined && rhUiIcon === null ) || set !== undefined ) {
95- const iconData = set !== undefined && set === 'rh-ui' && rhUiIcon !== null ? rhUiIcon : icon ;
96- const { xOffset, yOffset, width, height, svgPathData, svgClassName } = iconData ?? { } ;
184+ const iconData : IconDefinitionWithSvgPathData | undefined =
185+ set !== undefined && set === 'rh-ui' && rhUiIcon !== null ? rhUiIcon : icon ;
186+ const { xOffset, yOffset, width, height, svgPathData, svgClassName } =
187+ iconData ?? ( { } as Partial < IconDefinitionWithSvgPathData > ) ;
97188 const _xOffset = xOffset ?? 0 ;
98189 const _yOffset = yOffset ?? 0 ;
99190 const viewBox = [ _xOffset , _yOffset , width , height ] . join ( ' ' ) ;
0 commit comments