Skip to content

Commit b630b73

Browse files
refactor: enhance icon system following FabIcon pattern
1 parent 01d4713 commit b630b73

File tree

5 files changed

+233
-144
lines changed

5 files changed

+233
-144
lines changed

workspaces/bulk-import/.changeset/configurable-instructions-section.md

Lines changed: 20 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,7 @@ This change introduces a fully configurable "Import to Red Hat Developer Hub" in
99
**New Features:**
1010

1111
- **Configurable Steps**: Define custom workflow steps via `app-config.yaml` with custom text and icons
12-
- **Icon Support**: Support for both built-in theme-aware icons and custom URL-based icons
12+
- **Enhanced Icon Support**: Comprehensive icon system supporting Backstage system icons, Material Design icons, SVG strings, URLs, and legacy built-in icons
1313
- **Dynamic Layout**: Steps automatically adjust width for optimal space usage (≤6 steps fill width, >6 steps scroll horizontally)
1414
- **User Preferences**: Collapsed/expanded state persisted in localStorage per user
1515
- **Universal Display**: Instructions section now shows for both PR flow and scaffolder flow
@@ -26,13 +26,23 @@ bulkImport:
2626
instructionsDefaultExpanded: true
2727

2828
# Custom workflow steps
29-
instructionsSteps:
30-
- id: 'step1'
31-
text: 'Choose your source control platform'
32-
icon:
33-
type: 'builtin' # or "url"
34-
source: 'approval-tool' # icon name or URL
35-
- id: 'step2'
36-
text: 'Configure without icon'
37-
# Steps without icons show text only
29+
instructionsSteps:
30+
- id: 'step1'
31+
text: 'Choose your source control platform'
32+
icon: 'kind:component' # Backstage system icon
33+
- id: 'step2'
34+
text: 'Browse repositories'
35+
icon: 'search' # Material Design icon
36+
- id: 'step3'
37+
text: 'Custom SVG icon'
38+
icon: '<svg xmlns="http://www.w3.org/2000/svg">...</svg>' # SVG string
39+
- id: 'step4'
40+
text: 'External icon'
41+
icon: 'https://example.com/icon.png' # URL
42+
- id: 'step5'
43+
text: 'Legacy built-in icon'
44+
icon: 'approval-tool' # Legacy format (backward compatible)
45+
- id: 'step6'
46+
text: 'No icon step'
47+
# Steps without icons show text only
3848
```

workspaces/bulk-import/plugins/bulk-import/config.d.ts

Lines changed: 7 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -69,23 +69,15 @@ export interface Config {
6969
text: string;
7070

7171
/**
72-
* Icon configuration
72+
* Icon configuration - supports multiple formats:
73+
* - Backstage system icons: 'kind:component', 'kind:api', etc.
74+
* - Material Design icons: 'settings', 'home', 'build', etc.
75+
* - SVG strings: '<svg xmlns="http://www.w3.org/2000/svg">...</svg>'
76+
* - URLs: 'https://example.com/icon.png', '/assets/icon.svg'
77+
* - Data URIs: 'data:image/svg+xml;base64,...'
7378
* @visibility frontend
7479
*/
75-
icon?: {
76-
/**
77-
* Icon type: 'builtin' for predefined icons, 'url' for custom images
78-
* @visibility frontend
79-
*/
80-
type: 'builtin' | 'url';
81-
82-
/**
83-
* For builtin: icon name (e.g., 'approval-tool', 'choose-repositories', 'generate-cataloginfo', 'edit-pullrequest', 'track-status')
84-
* For url: full URL to the icon image
85-
* @visibility frontend
86-
*/
87-
source: string;
88-
};
80+
icon?: string;
8981
}>;
9082
};
9183
}

workspaces/bulk-import/plugins/bulk-import/src/components/AddRepositories/ConfigurableInstructions.tsx

Lines changed: 9 additions & 111 deletions
Original file line numberDiff line numberDiff line change
@@ -26,93 +26,13 @@ import Typography from '@mui/material/Typography';
2626

2727
import { useInstructionsConfig, useInstructionsPreference } from '../../hooks';
2828
import { useTranslation } from '../../hooks/useTranslation';
29-
import { Illustrations } from './Illustrations';
30-
31-
interface ConfigurableInstructionsProps {
32-
// No props needed - everything comes from configuration
33-
}
34-
35-
/**
36-
* Enhanced Illustrations component that supports custom URL icons
37-
*/
38-
const ConfigurableIllustration = ({
39-
iconClassname,
40-
iconText,
41-
}: {
42-
iconClassname: string;
43-
iconText: string;
44-
}) => {
45-
// If no icon is configured, show only text
46-
if (!iconClassname) {
47-
return (
48-
<div>
49-
{/* Empty space to maintain consistent layout with icon steps */}
50-
<Typography
51-
component="span"
52-
style={{
53-
justifyContent: 'center',
54-
display: 'flex',
55-
height: '100px',
56-
}}
57-
>
58-
{/* Empty space where icon would be */}
59-
</Typography>
60-
<Typography
61-
style={{
62-
maxWidth: '150px',
63-
textAlign: 'center',
64-
}}
65-
>
66-
{iconText}
67-
</Typography>
68-
</div>
69-
);
70-
}
71-
72-
// Check if this is a custom URL icon
73-
const isCustomUrl = iconClassname.startsWith('custom-url:');
74-
75-
if (isCustomUrl) {
76-
const imageUrl = iconClassname.replace('custom-url:', '');
77-
return (
78-
<div>
79-
<Typography
80-
component="span"
81-
style={{
82-
justifyContent: 'center',
83-
display: 'flex',
84-
}}
85-
>
86-
<img
87-
src={imageUrl}
88-
alt={iconText}
89-
height="100px"
90-
style={{ objectFit: 'contain' }}
91-
/>
92-
</Typography>
93-
<Typography
94-
style={{
95-
maxWidth: '150px',
96-
textAlign: 'center',
97-
}}
98-
>
99-
{iconText}
100-
</Typography>
101-
</div>
102-
);
103-
}
104-
105-
// Use the existing Illustrations component for built-in icons
106-
return <Illustrations iconClassname={iconClassname} iconText={iconText} />;
107-
};
29+
import { InstructionIcon } from './InstructionIcon';
10830

10931
/**
11032
* Configurable instructions component that displays the "How does it work" section
11133
* based on configuration from app-config.yaml and user preferences
11234
*/
113-
export const ConfigurableInstructions: React.FC<
114-
ConfigurableInstructionsProps
115-
> = () => {
35+
export const ConfigurableInstructions = () => {
11636
const theme = useTheme();
11737
const { t } = useTranslation();
11838
const config = useInstructionsConfig();
@@ -122,31 +42,12 @@ export const ConfigurableInstructions: React.FC<
12242

12343
// Build the list of steps based on configuration from app-config.yaml
12444
const steps = useMemo(() => {
125-
return config.steps.map(configStep => {
126-
let iconClassname = '';
127-
128-
if (configStep.icon) {
129-
if (configStep.icon.type === 'builtin') {
130-
// For builtin icons, construct the classname based on theme
131-
const iconName = configStep.icon.source;
132-
iconClassname =
133-
theme.palette.mode === 'dark'
134-
? `icon-${iconName}-white`
135-
: `icon-${iconName}-black`;
136-
} else if (configStep.icon.type === 'url') {
137-
// For URL icons, we'll use the URL directly in a special way
138-
iconClassname = `custom-url:${configStep.icon.source}`;
139-
}
140-
}
141-
// No fallback - leave iconClassname empty if no icon is configured
142-
143-
return {
144-
id: configStep.id,
145-
text: configStep.text,
146-
iconClassname,
147-
};
148-
});
149-
}, [config.steps, theme.palette.mode]);
45+
return config.steps.map(configStep => ({
46+
id: configStep.id,
47+
text: configStep.text,
48+
icon: configStep.icon,
49+
}));
50+
}, [config.steps]);
15051

15152
// Don't render if disabled or no steps configured
15253
if (!config.enabled || steps.length === 0) {
@@ -217,10 +118,7 @@ export const ConfigurableInstructions: React.FC<
217118
justifyContent: 'flex-start',
218119
}}
219120
>
220-
<ConfigurableIllustration
221-
iconClassname={step.iconClassname}
222-
iconText={step.text}
223-
/>
121+
<InstructionIcon icon={step.icon} text={step.text} />
224122
</Box>
225123
))}
226124
</Box>

0 commit comments

Comments
 (0)