frontend-app-api: fix icon element check

Signed-off-by: Patrik Oldsberg <poldsberg@gmail.com>
This commit is contained in:
Patrik Oldsberg
2026-02-11 23:33:17 +01:00
parent d6de6069aa
commit ac6e46cbaa
2 changed files with 33 additions and 6 deletions
@@ -14,7 +14,7 @@
* limitations under the License.
*/
import { createElement } from 'react';
import { createElement, memo, forwardRef } from 'react';
import { DefaultIconsApi } from './DefaultIconsApi';
describe('DefaultIconsApi', () => {
@@ -107,4 +107,31 @@ describe('DefaultIconsApi', () => {
expect(warnSpy).not.toHaveBeenCalled();
});
it('should treat React.memo components as IconComponent', () => {
const warnSpy = jest.spyOn(console, 'warn').mockImplementation(() => {});
const MemoIcon = memo(() => createElement('svg'));
const api = new DefaultIconsApi({ myIcon: MemoIcon });
const el = api.icon('myIcon');
expect(el).toBeTruthy();
// @ts-expect-error accessing internal React element structure
expect(el.type).toBe(MemoIcon);
expect(warnSpy).toHaveBeenCalledWith(expect.stringContaining('myIcon'));
});
it('should treat React.forwardRef components as IconComponent', () => {
const warnSpy = jest.spyOn(console, 'warn').mockImplementation(() => {});
const RefIcon = forwardRef(() => createElement('svg'));
// @ts-expect-error forwardRef is not strictly IconComponent but should be handled
const api = new DefaultIconsApi({ myIcon: RefIcon });
const el = api.icon('myIcon');
expect(el).toBeTruthy();
// @ts-expect-error accessing internal React element structure
expect(el.type).toBe(RefIcon);
expect(warnSpy).toHaveBeenCalledWith(expect.stringContaining('myIcon'));
});
});
@@ -19,7 +19,7 @@ import {
IconElement,
IconsApi,
} from '@backstage/frontend-plugin-api';
import { createElement } from 'react';
import { createElement, isValidElement } from 'react';
/**
* Implementation for the {@link IconsApi}
@@ -35,11 +35,11 @@ export class DefaultIconsApi implements IconsApi {
this.#icons = new Map(
Object.entries(icons).map(([key, value]) => {
if (typeof value === 'function') {
deprecatedKeys.push(key);
return [key, createElement(value)];
if (value === null || isValidElement(value)) {
return [key, value];
}
return [key, value];
deprecatedKeys.push(key);
return [key, createElement(value as IconComponent)];
}),
);