Skip to content

Commit 3d2fd6f

Browse files
authored
feat(react-icons): update to create static SVGs (#12154)
* feat(react-icons): update to create static SVGs * chore: update docs * chore: add build:esm script to compile ts before icon generation * chore: lint * chore: remove linter changes * chore: docs update
1 parent cdb6a26 commit 3d2fd6f

File tree

3 files changed

+83
-4
lines changed

3 files changed

+83
-4
lines changed

packages/react-icons/README.md

Lines changed: 26 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -10,7 +10,7 @@ import TimesIcon from '@patternfly/react-icons/dist/esm/icons/times-icon';
1010
const closeIcon = <TimesIcon />;
1111
```
1212

13-
For a list of the available icons please refer to the [PatternFly react docs](https://react-staging.patternfly.org/icons)
13+
For a list of the available icons please refer to the [PatternFly react docs](https://pf-react-staging.patternfly.org/icons)
1414

1515
## Styling icons
1616

@@ -68,3 +68,28 @@ module.exports = {
6868
]
6969
}
7070
```
71+
72+
## Static SVGs
73+
74+
All icons are also available as static SVG files in `@patternfly/react-icons/dist/static`. The static SVGs include all the same attributes as the React components (viewBox, class names, etc.) to ensure visual consistency.
75+
76+
Static SVGs are useful when you need to:
77+
- Use icons in non-React contexts, such as static HTML
78+
- Reference icons via URL or file path
79+
80+
### Usage
81+
82+
You can import or reference static SVG files directly:
83+
84+
```jsx
85+
// In HTML
86+
<img src="/icons/static/times-icon.svg" alt="Close" />
87+
88+
// In CSS
89+
.close-icon {
90+
background-image: url('/icons/static/times-icon.svg');
91+
}
92+
93+
// Direct file path
94+
import timesIcon from '@patternfly/react-icons/dist/static/times-icon.svg';
95+
```

packages/react-icons/package.json

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -26,8 +26,9 @@
2626
"homepage": "https://github.com/patternfly/patternfly-react#readme",
2727
"scripts": {
2828
"build:single:packages": "node ../../scripts/build-single-packages.mjs --config single-packages.config.json",
29+
"build:esm": "tsc --build tsconfig.json",
2930
"clean": "rimraf dist src/icons src/index.js src/index.d.ts",
30-
"generate": "rimraf dist/esm/icons dist/js/icons && node scripts/writeIcons.mjs"
31+
"generate": "rimraf dist/esm/icons dist/js/icons && yarn build:esm && node scripts/writeIcons.mjs"
3132
},
3233
"devDependencies": {
3334
"@fortawesome/free-brands-svg-icons": "^5.15.4",

packages/react-icons/scripts/writeIcons.mjs

Lines changed: 55 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,11 +1,18 @@
11
import { join } from 'path';
2-
import { outputFileSync } from 'fs-extra/esm';
2+
import { outputFileSync, ensureDirSync } from 'fs-extra/esm';
33
import { generateIcons } from './generateIcons.mjs';
4+
import { createElement } from 'react';
5+
import { renderToString } from 'react-dom/server';
46

57
import * as url from 'url';
68
const __dirname = url.fileURLToPath(new URL('.', import.meta.url));
79

10+
// Import createIcon from compiled dist (build:esm must run first)
11+
const createIconModule = await import('../dist/esm/createIcon.js');
12+
const createIcon = createIconModule.createIcon;
13+
814
const outDir = join(__dirname, '../dist');
15+
const staticDir = join(outDir, 'static');
916

1017
const removeSnake = (s) => s.toUpperCase().replace('-', '').replace('_', '');
1118
const toCamel = (s) => `${s[0].toUpperCase()}${s.substr(1).replace(/([-_][\w])/gi, removeSnake)}`;
@@ -72,6 +79,50 @@ export default ${jsName};
7279
outputFileSync(join(outDir, 'esm/icons', filename), text);
7380
};
7481

82+
/**
83+
* Generates a static SVG string from icon data using createIcon
84+
* @param {string} iconName The name of the icon
85+
* @param {object} icon The icon data object
86+
* @returns {string} Static SVG markup
87+
*/
88+
function generateStaticSVG(iconName, icon) {
89+
const jsName = `${toCamel(iconName)}Icon`;
90+
91+
// Create icon component using createIcon
92+
const IconComponent = createIcon({
93+
name: jsName,
94+
width: icon.width,
95+
height: icon.height,
96+
svgPath: icon.svgPathData,
97+
xOffset: icon.xOffset || 0,
98+
yOffset: icon.yOffset || 0,
99+
svgClassName: icon.svgClassName
100+
});
101+
102+
// Render the component to string
103+
const svgString = renderToString(createElement(IconComponent));
104+
105+
// Convert React's className to class for static SVG
106+
return svgString.replace(/className=/g, 'class=');
107+
}
108+
109+
/**
110+
* Writes static SVG files to dist/static directory
111+
* @param {object} icons icons from generateIcons
112+
*/
113+
function writeStaticSVGs(icons) {
114+
ensureDirSync(staticDir);
115+
116+
Object.entries(icons).forEach(([iconName, icon]) => {
117+
const svgContent = generateStaticSVG(iconName, icon);
118+
const svgFileName = `${iconName}.svg`;
119+
outputFileSync(join(staticDir, svgFileName), svgContent, 'utf-8');
120+
});
121+
122+
// eslint-disable-next-line no-console
123+
console.log(`Wrote ${Object.keys(icons).length} static SVG files to ${staticDir}`);
124+
}
125+
75126
/**
76127
* Writes CJS and ESM icons to `dist` directory
77128
*
@@ -114,4 +165,6 @@ ${index
114165
console.log('Wrote', index.length * 3 + 3, 'icon files.');
115166
}
116167

117-
writeIcons(generateIcons());
168+
const icons = generateIcons();
169+
writeIcons(icons);
170+
writeStaticSVGs(icons);

0 commit comments

Comments
 (0)