11import { render , screen } from '@testing-library/react' ;
2- import { IconDefinition , CreateIconProps , createIcon , SVGPathObject } from '../createIcon' ;
2+ import { IconDefinition , CreateIconProps , createIcon , createIconBase , SVGPathObject } from '../createIcon' ;
3+
4+ /** Mirrors the non-exported argument type of {@link createIconBase} for tests. */
5+ type CreateIconBaseProps = Parameters < typeof createIconBase > [ 0 ] ;
36
47const multiPathIcon : IconDefinition = {
58 name : 'IconName' ,
@@ -28,24 +31,24 @@ const rhStandardIcon: IconDefinition = {
2831 svgClassName : 'pf-v6-icon-rh-standard'
2932} ;
3033
31- const iconDef : CreateIconProps = {
34+ const iconDef : CreateIconBaseProps = {
3235 name : 'SinglePathIconName' ,
3336 icon : singlePathIcon
3437} ;
3538
36- const iconDefWithArrayPath : CreateIconProps = {
39+ const iconDefWithArrayPath : CreateIconBaseProps = {
3740 name : 'MultiPathIconName' ,
3841 icon : multiPathIcon
3942} ;
4043
41- const iconDefWithRhStandard : CreateIconProps = {
44+ const iconDefWithRhStandard : CreateIconBaseProps = {
4245 name : 'RhStandardIconName' ,
4346 icon : rhStandardIcon
4447} ;
4548
46- const SVGIcon = createIcon ( iconDef ) ;
47- const SVGArrayIcon = createIcon ( iconDefWithArrayPath ) ;
48- const RhStandardIcon = createIcon ( iconDefWithRhStandard ) ;
49+ const SVGIcon = createIconBase ( iconDef ) ;
50+ const SVGArrayIcon = createIconBase ( iconDefWithArrayPath ) ;
51+ const RhStandardIcon = createIconBase ( iconDefWithRhStandard ) ;
4952
5053test ( 'sets correct viewBox' , ( ) => {
5154 render ( < SVGIcon /> ) ;
@@ -57,7 +60,23 @@ test('sets correct viewBox', () => {
5760
5861test ( 'sets correct svgPath if string' , ( ) => {
5962 render ( < SVGIcon /> ) ;
60- expect ( screen . getByRole ( 'img' , { hidden : true } ) . querySelector ( 'path' ) ) . toHaveAttribute ( 'd' , iconDef . svgPath ) ;
63+ expect ( screen . getByRole ( 'img' , { hidden : true } ) . querySelector ( 'path' ) ) . toHaveAttribute (
64+ 'd' ,
65+ singlePathIcon . svgPathData
66+ ) ;
67+ } ) ;
68+
69+ test ( 'accepts flat createIcon({ svgPath }) shape' , ( ) => {
70+ const legacyDef : CreateIconProps = {
71+ name : 'LegacyIcon' ,
72+ width : 10 ,
73+ height : 20 ,
74+ svgPath : 'legacy-path' ,
75+ svgClassName : 'legacy-svg'
76+ } ;
77+ const LegacySVGIcon = createIcon ( legacyDef ) ;
78+ render ( < LegacySVGIcon /> ) ;
79+ expect ( screen . getByRole ( 'img' , { hidden : true } ) . querySelector ( 'path' ) ) . toHaveAttribute ( 'd' , 'legacy-path' ) ;
6180} ) ;
6281
6382test ( 'sets correct svgClassName by default' , ( ) => {
@@ -127,3 +146,90 @@ test('additional props should be spread to the root svg element', () => {
127146 render ( < SVGIcon data-testid = "icon" /> ) ;
128147 expect ( screen . getByTestId ( 'icon' ) ) . toBeInTheDocument ( ) ;
129148} ) ;
149+
150+ describe ( 'rh-ui mapping: nested SVGs, set prop, and warnings' , ( ) => {
151+ const defaultPath = 'M0 0-default' ;
152+ const rhUiPath = 'M0 0-rh-ui' ;
153+
154+ const defaultIconDef : IconDefinition = {
155+ name : 'DefaultVariant' ,
156+ width : 16 ,
157+ height : 16 ,
158+ svgPathData : defaultPath
159+ } ;
160+
161+ const rhUiIconDef : IconDefinition = {
162+ name : 'RhUiVariant' ,
163+ width : 16 ,
164+ height : 16 ,
165+ svgPathData : rhUiPath
166+ } ;
167+
168+ const dualConfig : CreateIconBaseProps = {
169+ name : 'DualMappedIcon' ,
170+ icon : defaultIconDef ,
171+ rhUiIcon : rhUiIconDef
172+ } ;
173+
174+ const DualMappedIcon = createIconBase ( dualConfig ) ;
175+
176+ test ( 'renders two nested inner svgs when rhUiIcon is set and `set` is omitted (swap layout)' , ( ) => {
177+ render ( < DualMappedIcon /> ) ;
178+ const root = screen . getByRole ( 'img' , { hidden : true } ) ;
179+ expect ( root ) . toHaveClass ( 'pf-v6-svg' ) ;
180+ const innerSvgs = root . querySelectorAll ( ':scope > svg' ) ;
181+ expect ( innerSvgs ) . toHaveLength ( 2 ) ;
182+ expect ( root ?. querySelector ( '.pf-v6-icon-default path' ) ) . toHaveAttribute ( 'd' , defaultPath ) ;
183+ expect ( root ?. querySelector ( '.pf-v6-icon-rh-ui path' ) ) . toHaveAttribute ( 'd' , rhUiPath ) ;
184+ } ) ;
185+
186+ test ( 'set="default" renders a single flat svg using the default icon paths' , ( ) => {
187+ render ( < DualMappedIcon set = "default" /> ) ;
188+ const root = screen . getByRole ( 'img' , { hidden : true } ) ;
189+ expect ( root . querySelectorAll ( ':scope > svg' ) ) . toHaveLength ( 0 ) ;
190+ expect ( root ) . toHaveAttribute ( 'viewBox' , '0 0 16 16' ) ;
191+ expect ( root . querySelector ( 'path' ) ) . toHaveAttribute ( 'd' , defaultPath ) ;
192+ expect ( root . querySelectorAll ( 'svg' ) ) . toHaveLength ( 0 ) ;
193+ } ) ;
194+
195+ test ( 'set="rh-ui" renders a single flat svg using the rh-ui icon paths' , ( ) => {
196+ render ( < DualMappedIcon set = "rh-ui" /> ) ;
197+ const root = screen . getByRole ( 'img' , { hidden : true } ) ;
198+ expect ( root . querySelectorAll ( ':scope > svg' ) ) . toHaveLength ( 0 ) ;
199+ expect ( root . querySelector ( 'path' ) ) . toHaveAttribute ( 'd' , rhUiPath ) ;
200+ expect ( root . querySelectorAll ( 'svg' ) ) . toHaveLength ( 0 ) ;
201+ } ) ;
202+
203+ test ( 'set="rh-ui" with no rhUiIcon mapping falls back to default and warns' , ( ) => {
204+ const warnSpy = jest . spyOn ( console , 'warn' ) . mockImplementation ( ( ) => { } ) ;
205+ try {
206+ const IconNoRhMapping = createIconBase ( {
207+ name : 'NoRhMappingIcon' ,
208+ icon : defaultIconDef ,
209+ rhUiIcon : null
210+ } ) ;
211+
212+ render ( < IconNoRhMapping set = "rh-ui" /> ) ;
213+
214+ expect ( warnSpy ) . toHaveBeenCalledWith (
215+ 'Set "rh-ui" was provided for NoRhMappingIcon, but no rh-ui icon data exists for this icon. The default icon will be rendered.'
216+ ) ;
217+ const root = screen . getByRole ( 'img' , { hidden : true } ) ;
218+ expect ( root . querySelector ( 'path' ) ) . toHaveAttribute ( 'd' , defaultPath ) ;
219+ expect ( root . querySelectorAll ( 'svg' ) ) . toHaveLength ( 0 ) ;
220+ } finally {
221+ warnSpy . mockRestore ( ) ;
222+ }
223+ } ) ;
224+
225+ test ( 'warns when createIconBase omits icon' , ( ) => {
226+ const warnSpy = jest . spyOn ( console , 'warn' ) . mockImplementation ( ( ) => { } ) ;
227+ createIconBase ( {
228+ name : 'MissingDefaultIcon' ,
229+ rhUiIcon : null
230+ } ) ;
231+ expect ( warnSpy ) . toHaveBeenCalledWith (
232+ '@patternfly/react-icons: createIconBase is missing an `icon` definition (name: MissingDefaultIcon).'
233+ ) ;
234+ } ) ;
235+ } ) ;
0 commit comments