From 2be73d44eb623b11bab5cfc9f8a1b91e402bfce2 Mon Sep 17 00:00:00 2001 From: mcoker Date: Tue, 25 Nov 2025 09:54:07 -0600 Subject: [PATCH 1/6] feat(react-icons): update to create static SVGs --- packages/react-icons/scripts/writeIcons.mjs | 54 ++++++++++++++++++++- 1 file changed, 52 insertions(+), 2 deletions(-) diff --git a/packages/react-icons/scripts/writeIcons.mjs b/packages/react-icons/scripts/writeIcons.mjs index c468e987080..d3fa1ff3dd6 100644 --- a/packages/react-icons/scripts/writeIcons.mjs +++ b/packages/react-icons/scripts/writeIcons.mjs @@ -1,11 +1,15 @@ import { join } from 'path'; -import { outputFileSync } from 'fs-extra/esm'; +import { outputFileSync, ensureDirSync } from 'fs-extra/esm'; import { generateIcons } from './generateIcons.mjs'; +import { createIcon } from '../dist/esm/createIcon.js'; +import React from 'react'; +import { renderToString } from 'react-dom/server'; import * as url from 'url'; const __dirname = url.fileURLToPath(new URL('.', import.meta.url)); const outDir = join(__dirname, '../dist'); +const staticDir = join(outDir, 'static'); const removeSnake = (s) => s.toUpperCase().replace('-', '').replace('_', ''); const toCamel = (s) => `${s[0].toUpperCase()}${s.substr(1).replace(/([-_][\w])/gi, removeSnake)}`; @@ -72,6 +76,50 @@ export default ${jsName}; outputFileSync(join(outDir, 'esm/icons', filename), text); }; +/** + * Generates a static SVG string from icon data using createIcon + * @param {string} iconName The name of the icon + * @param {object} icon The icon data object + * @returns {string} Static SVG markup + */ +function generateStaticSVG(iconName, icon) { + const jsName = `${toCamel(iconName)}Icon`; + + // Create icon component using createIcon + const IconComponent = createIcon({ + name: jsName, + width: icon.width, + height: icon.height, + svgPath: icon.svgPathData, + xOffset: icon.xOffset || 0, + yOffset: icon.yOffset || 0, + svgClassName: icon.svgClassName + }); + + // Render the component to string + const svgString = renderToString(React.createElement(IconComponent)); + + // Convert React's className to class for static SVG + return svgString.replace(/className=/g, 'class='); +} + +/** + * Writes static SVG files to dist/static directory + * @param {object} icons icons from generateIcons + */ +function writeStaticSVGs(icons) { + ensureDirSync(staticDir); + + Object.entries(icons).forEach(([iconName, icon]) => { + const svgContent = generateStaticSVG(iconName, icon); + const svgFileName = `${iconName}.svg`; + outputFileSync(join(staticDir, svgFileName), svgContent, 'utf-8'); + }); + + // eslint-disable-next-line no-console + console.log(`Wrote ${Object.keys(icons).length} static SVG files to ${staticDir}`); +} + /** * Writes CJS and ESM icons to `dist` directory * @@ -114,4 +162,6 @@ ${index console.log('Wrote', index.length * 3 + 3, 'icon files.'); } -writeIcons(generateIcons()); +const icons = generateIcons(); +writeIcons(icons); +writeStaticSVGs(icons); From d8f71ef0d1e2990fe6339a8860d2cdc5ffa3c346 Mon Sep 17 00:00:00 2001 From: mcoker Date: Tue, 25 Nov 2025 10:45:08 -0600 Subject: [PATCH 2/6] chore: update docs --- packages/react-icons/README.md | 29 ++++++++++++++++++++++++++++- 1 file changed, 28 insertions(+), 1 deletion(-) diff --git a/packages/react-icons/README.md b/packages/react-icons/README.md index eabb4ed8152..84d69a8f1ee 100644 --- a/packages/react-icons/README.md +++ b/packages/react-icons/README.md @@ -10,7 +10,7 @@ import TimesIcon from '@patternfly/react-icons/dist/esm/icons/times-icon'; const closeIcon = ; ``` -For a list of the available icons please refer to the [PatternFly react docs](https://react-staging.patternfly.org/icons) +For a list of the available icons please refer to the [PatternFly react docs](https://pf-react-staging.patternfly.org/icons) ## Styling icons @@ -68,3 +68,30 @@ module.exports = { ] } ``` + +## Static SVGs + +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.) and are generated using the same `createIcon` function to ensure visual consistency. + +Static SVGs are useful when you need to: +- Use icons in non-React contexts (HTML emails, static sites, etc.) +- Embed icons directly in HTML without JavaScript +- Reference icons via URL or file path +- Use icons in build tools that process static assets + +### Usage + +You can import or reference static SVG files directly: + +```jsx +// In HTML +Close + +// In CSS +.close-icon { + background-image: url('@patternfly/react-icons/dist/static/times-icon.svg'); +} + +// Direct file path +import timesIcon from '@patternfly/react-icons/dist/static/times-icon.svg'; +``` From 13c8a8b2f210272762e26a8dafe50892d533d0a5 Mon Sep 17 00:00:00 2001 From: mcoker Date: Tue, 25 Nov 2025 11:36:44 -0600 Subject: [PATCH 3/6] chore: add build:esm script to compile ts before icon generation --- packages/react-icons/package.json | 3 ++- packages/react-icons/scripts/writeIcons.mjs | 5 ++++- 2 files changed, 6 insertions(+), 2 deletions(-) diff --git a/packages/react-icons/package.json b/packages/react-icons/package.json index 2c71f8d8114..7e43022c552 100644 --- a/packages/react-icons/package.json +++ b/packages/react-icons/package.json @@ -26,8 +26,9 @@ "homepage": "https://github.com/patternfly/patternfly-react#readme", "scripts": { "build:single:packages": "node ../../scripts/build-single-packages.mjs --config single-packages.config.json", + "build:esm": "tsc --build tsconfig.json", "clean": "rimraf dist src/icons src/index.js src/index.d.ts", - "generate": "rimraf dist/esm/icons dist/js/icons && node scripts/writeIcons.mjs" + "generate": "rimraf dist/esm/icons dist/js/icons && yarn build:esm && node scripts/writeIcons.mjs" }, "devDependencies": { "@fortawesome/free-brands-svg-icons": "^5.15.4", diff --git a/packages/react-icons/scripts/writeIcons.mjs b/packages/react-icons/scripts/writeIcons.mjs index d3fa1ff3dd6..57280b09128 100644 --- a/packages/react-icons/scripts/writeIcons.mjs +++ b/packages/react-icons/scripts/writeIcons.mjs @@ -1,13 +1,16 @@ import { join } from 'path'; import { outputFileSync, ensureDirSync } from 'fs-extra/esm'; import { generateIcons } from './generateIcons.mjs'; -import { createIcon } from '../dist/esm/createIcon.js'; import React from 'react'; import { renderToString } from 'react-dom/server'; import * as url from 'url'; const __dirname = url.fileURLToPath(new URL('.', import.meta.url)); +// Import createIcon from compiled dist (build:esm must run first) +const createIconModule = await import('../dist/esm/createIcon.js'); +const createIcon = createIconModule.createIcon; + const outDir = join(__dirname, '../dist'); const staticDir = join(outDir, 'static'); From 187d52f8df91511be0f287b6786322ec6a64e778 Mon Sep 17 00:00:00 2001 From: mcoker Date: Tue, 25 Nov 2025 13:19:23 -0600 Subject: [PATCH 4/6] chore: lint --- eslint.config.mjs | 3 ++- packages/react-icons/scripts/writeIcons.mjs | 6 +++--- 2 files changed, 5 insertions(+), 4 deletions(-) diff --git a/eslint.config.mjs b/eslint.config.mjs index 567da2b099c..a326731b2d6 100644 --- a/eslint.config.mjs +++ b/eslint.config.mjs @@ -12,11 +12,12 @@ import tseslint from 'typescript-eslint'; export default [ { ignores: [ + '**/node_modules', '**/dist', '**/css', + '**/coverage', 'packages/react-core/src/helpers/Popper/thirdparty', 'packages/react-docs/patternfly-docs/generated', - 'packages/react-docs/coverage', '.history/*', 'packages/react-docs/static', 'packages/react-docs/public', diff --git a/packages/react-icons/scripts/writeIcons.mjs b/packages/react-icons/scripts/writeIcons.mjs index 57280b09128..a4c58a700f4 100644 --- a/packages/react-icons/scripts/writeIcons.mjs +++ b/packages/react-icons/scripts/writeIcons.mjs @@ -1,7 +1,7 @@ import { join } from 'path'; import { outputFileSync, ensureDirSync } from 'fs-extra/esm'; import { generateIcons } from './generateIcons.mjs'; -import React from 'react'; +import { createElement } from 'react'; import { renderToString } from 'react-dom/server'; import * as url from 'url'; @@ -87,7 +87,7 @@ export default ${jsName}; */ function generateStaticSVG(iconName, icon) { const jsName = `${toCamel(iconName)}Icon`; - + // Create icon component using createIcon const IconComponent = createIcon({ name: jsName, @@ -100,7 +100,7 @@ function generateStaticSVG(iconName, icon) { }); // Render the component to string - const svgString = renderToString(React.createElement(IconComponent)); + const svgString = renderToString(createElement(IconComponent)); // Convert React's className to class for static SVG return svgString.replace(/className=/g, 'class='); From 0fe536355dbc43457b481b4d85c07b1347fc4a74 Mon Sep 17 00:00:00 2001 From: mcoker Date: Tue, 25 Nov 2025 13:37:16 -0600 Subject: [PATCH 5/6] chore: remove linter changes --- eslint.config.mjs | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/eslint.config.mjs b/eslint.config.mjs index a326731b2d6..567da2b099c 100644 --- a/eslint.config.mjs +++ b/eslint.config.mjs @@ -12,12 +12,11 @@ import tseslint from 'typescript-eslint'; export default [ { ignores: [ - '**/node_modules', '**/dist', '**/css', - '**/coverage', 'packages/react-core/src/helpers/Popper/thirdparty', 'packages/react-docs/patternfly-docs/generated', + 'packages/react-docs/coverage', '.history/*', 'packages/react-docs/static', 'packages/react-docs/public', From d5dcec36df7b1762409b9d150664119df99ed90f Mon Sep 17 00:00:00 2001 From: mcoker Date: Tue, 2 Dec 2025 13:04:23 -0600 Subject: [PATCH 6/6] chore: docs update --- packages/react-icons/README.md | 10 ++++------ 1 file changed, 4 insertions(+), 6 deletions(-) diff --git a/packages/react-icons/README.md b/packages/react-icons/README.md index 84d69a8f1ee..82211004774 100644 --- a/packages/react-icons/README.md +++ b/packages/react-icons/README.md @@ -71,13 +71,11 @@ module.exports = { ## Static SVGs -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.) and are generated using the same `createIcon` function to ensure visual consistency. +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. Static SVGs are useful when you need to: -- Use icons in non-React contexts (HTML emails, static sites, etc.) -- Embed icons directly in HTML without JavaScript +- Use icons in non-React contexts, such as static HTML - Reference icons via URL or file path -- Use icons in build tools that process static assets ### Usage @@ -85,11 +83,11 @@ You can import or reference static SVG files directly: ```jsx // In HTML -Close +Close // In CSS .close-icon { - background-image: url('@patternfly/react-icons/dist/static/times-icon.svg'); + background-image: url('/icons/static/times-icon.svg'); } // Direct file path