Skip to content

Commit d29ff9b

Browse files
authored
[One Workflow] Render icons from registry (#243388)
## Summary Retrieve connector icons from the `actionTypeRegistry` instead of doing cross-plugin import. ### Benefits - Adds support for connectorsV2 icons dynamically - We keep the action registry as the single source of truth for the rest of connectors ### Screenshots <img width="472" height="95" alt="Captura de pantalla 2025-11-18 a les 17 18 15" src="https://github.com/user-attachments/assets/5ca142a5-b370-4668-af99-6bb9e170b161" /> <img width="472" height="95" alt="Captura de pantalla 2025-11-18 a les 17 18 35" src="https://github.com/user-attachments/assets/ddda985d-86cf-4d20-a30e-2dbbfc8448be" /> <img width="472" height="95" alt="Captura de pantalla 2025-11-18 a les 17 18 26" src="https://github.com/user-attachments/assets/d3fcff21-6bf0-44c8-a170-b6ea353d4065" /> <img width="472" height="95" alt="Captura de pantalla 2025-11-18 a les 17 18 09" src="https://github.com/user-attachments/assets/cc510e7c-7f6e-413e-b098-7956138a572f" /> <img width="496" height="93" alt="Captura de pantalla 2025-11-18 a les 17 22 21" src="https://github.com/user-attachments/assets/f46e11e0-0edd-49bb-a975-b3fef5012533" /> ---- <img width="737" height="191" alt="Captura de pantalla 2025-11-18 a les 17 20 49" src="https://github.com/user-attachments/assets/291551ff-890c-499c-85f0-05b0172a74e7" /> <img width="737" height="191" alt="Captura de pantalla 2025-11-18 a les 17 20 44" src="https://github.com/user-attachments/assets/7a0acdc0-cd7d-44b6-80d4-880f3322a99e" /> <img width="737" height="191" alt="Captura de pantalla 2025-11-18 a les 17 20 40" src="https://github.com/user-attachments/assets/0e0d5c2c-70d2-4b2b-b78a-1fc694194ef9" /> <img width="737" height="191" alt="Captura de pantalla 2025-11-18 a les 17 19 26" src="https://github.com/user-attachments/assets/5797ce1d-33a7-4f9e-8459-3ca39d006332" /> <img width="737" height="191" alt="Captura de pantalla 2025-11-18 a les 17 19 20" src="https://github.com/user-attachments/assets/c1c1f2aa-ad6b-4b2d-b3b7-7bacc07dc805" />
1 parent fce1bae commit d29ff9b

File tree

9 files changed

+117
-395
lines changed

9 files changed

+117
-395
lines changed

src/platform/plugins/shared/workflows_management/public/shared/ui/step_icons/get_step_icon_base64.ts

Lines changed: 50 additions & 36 deletions
Original file line numberDiff line numberDiff line change
@@ -7,10 +7,9 @@
77
* License v3.0 only", or the "Server Side Public License, v 1".
88
*/
99

10-
import type { IconType } from '@elastic/eui';
10+
import { type IconType } from '@elastic/eui';
1111
import React from 'react';
1212
import { renderToStaticMarkup } from 'react-dom/server';
13-
import { getStackConnectorLogo } from '@kbn/stack-connectors-plugin/public/common/logos';
1413
import { ElasticsearchLogo } from './icons/elasticsearch.svg';
1514
import { HARDCODED_ICONS } from './icons/hardcoded_icons';
1615
import { KibanaLogo } from './icons/kibana.svg';
@@ -27,62 +26,78 @@ const DEFAULT_CONNECTOR_SVG = `<svg xmlns="http://www.w3.org/2000/svg" width="16
2726
<circle cx="8" cy="8" r="6" fill="none" stroke="currentColor" stroke-width="2"/>
2827
<circle cx="8" cy="8" r="2" fill="currentColor"/>
2928
</svg>`;
29+
const DEFAULT_CONNECTOR_DATA_URL = `data:image/svg+xml;base64,${btoa(DEFAULT_CONNECTOR_SVG)}`;
30+
31+
type LazyImageComponent = React.LazyExoticComponent<
32+
React.ComponentType<{ width: number; height: number }>
33+
> & {
34+
_payload: {
35+
_result: () => Promise<{ default: React.ComponentType<{ width: number; height: number }> }>;
36+
};
37+
};
38+
39+
/**
40+
* Type guard to check if a component is a LazyExoticComponent
41+
* LazyExoticComponent has an internal _payload property with a _load function
42+
*/
43+
function isLazyExoticComponent(component: unknown): component is LazyImageComponent {
44+
const comp = component as unknown as LazyImageComponent;
45+
return typeof comp?._payload?._result === 'function';
46+
}
47+
48+
/**
49+
* Resolve a LazyExoticComponent to its actual component
50+
* Accesses React's internal _payload._result API to resolve the lazy component
51+
*/
52+
async function resolveLazyComponent(
53+
lazyComponent: LazyImageComponent
54+
): Promise<React.ComponentType<{ width: number; height: number }>> {
55+
// Access React's internal payload to get the loader function
56+
const module = await lazyComponent._payload._result();
57+
// Return the default export (the actual component)
58+
return module.default;
59+
}
3060

3161
/**
3262
* Get data URL for a connector icon (supports SVG, PNG, and other image formats)
3363
* Returns a full data URL (e.g., "data:image/svg+xml;base64,..." or "data:image/png;base64,...")
3464
*/
3565
export async function getStepIconBase64(connector: GetStepIconBase64Params): Promise<string> {
3666
try {
37-
// Only use connector.icon if it's already a data URL
38-
if (
39-
connector.icon &&
40-
typeof connector.icon === 'string' &&
41-
connector.icon.startsWith('data:')
42-
) {
43-
return connector.icon;
67+
// The icon from action registry,
68+
if (connector.icon) {
69+
// data URL strings or lazy components supported.
70+
// built-in EUI icons are not supported (e.g. 'logoSlack', 'inference') use hardcoded icons for them instead.
71+
if (typeof connector.icon === 'string' && connector.icon.startsWith('data:')) {
72+
return connector.icon;
73+
}
74+
if (isLazyExoticComponent(connector.icon)) {
75+
const IconComponent = await resolveLazyComponent(connector.icon);
76+
return getDataUrlFromReactComponent(IconComponent);
77+
}
4478
}
4579

46-
const connectorType = connector.actionTypeId;
47-
if (connectorType === 'elasticsearch') {
80+
if (connector.actionTypeId === 'elasticsearch') {
4881
return getDataUrlFromReactComponent(ElasticsearchLogo);
4982
}
5083

51-
if (connectorType === 'kibana') {
84+
if (connector.actionTypeId === 'kibana') {
5285
return getDataUrlFromReactComponent(KibanaLogo);
5386
}
5487

55-
// Handle connectors that use EUI built-in icons instead of custom logo components
56-
if (connectorType === 'slack' || connectorType === 'slack_api') {
57-
// hardcoded slack logo - convert to data URL if it's just base64
58-
const slackIcon = HARDCODED_ICONS.slack;
59-
if (slackIcon.startsWith('data:')) {
60-
return slackIcon;
61-
}
62-
return `data:image/svg+xml;base64,${slackIcon}`;
63-
}
64-
65-
if (connectorType in HARDCODED_ICONS) {
66-
const hardcodedIcon = HARDCODED_ICONS[connectorType as keyof typeof HARDCODED_ICONS];
88+
const hardcodedIcon = HARDCODED_ICONS[connector.actionTypeId];
89+
if (hardcodedIcon) {
6790
if (hardcodedIcon.startsWith('data:')) {
6891
return hardcodedIcon;
6992
}
7093
return `data:image/svg+xml;base64,${hardcodedIcon}`;
7194
}
7295

73-
const dotConnectorType = `.${connectorType}`;
74-
// First, try to get the logo directly from stack connectors
75-
const LogoComponent = await getStackConnectorLogo(dotConnectorType);
76-
if (LogoComponent) {
77-
// LogoComponent is a React component, not an IconType, so handle it separately
78-
return getDataUrlFromReactComponent(LogoComponent);
79-
}
80-
8196
// Fallback to default icon for other connector types
82-
return `data:image/svg+xml;base64,${btoa(DEFAULT_CONNECTOR_SVG)}`;
97+
return DEFAULT_CONNECTOR_DATA_URL;
8398
} catch (error) {
8499
// Fallback to default static icon
85-
return `data:image/svg+xml;base64,${btoa(DEFAULT_CONNECTOR_SVG)}`;
100+
return DEFAULT_CONNECTOR_DATA_URL;
86101
}
87102
}
88103

@@ -122,8 +137,7 @@ function getDataUrlFromReactComponent(
122137
if (hasFillNone) {
123138
// Remove fill="none" and add currentColor fill
124139
htmlString = htmlString
125-
.replace(/fill="none"/gi, '')
126-
.replace(/fill='none'/gi, '')
140+
.replaceAll(/fill="none"/gi, '')
127141
.replace(/<svg([^>]*?)>/, '<svg$1 fill="currentColor">');
128142
}
129143
}

src/platform/plugins/shared/workflows_management/public/shared/ui/step_icons/get_step_icon_css_properties.ts

Lines changed: 0 additions & 61 deletions
This file was deleted.

src/platform/plugins/shared/workflows_management/public/shared/ui/step_icons/get_step_icon_svg.ts

Lines changed: 0 additions & 70 deletions
This file was deleted.

src/platform/plugins/shared/workflows_management/public/shared/ui/step_icons/icons/hardcoded_icons.ts

Lines changed: 5 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -21,12 +21,13 @@ import sparkles from './sparkles.svg';
2121
import user from './user.svg';
2222
import warning from './warning.svg';
2323

24-
export const HARDCODED_ICONS = {
24+
export const HARDCODED_ICONS: Record<string, string> = {
25+
'.slack': slackLogoSvg,
26+
'.slack_api': slackLogoSvg,
27+
'.email': email,
28+
'.inference': sparkles,
2529
elasticsearch: elasticsearchLogoSvg,
2630
kibana: kibanaLogoSvg,
27-
slack: slackLogoSvg,
28-
email,
29-
inference: sparkles,
3031
console,
3132
http: globe,
3233
foreach: refresh,

0 commit comments

Comments
 (0)