Updated docs, added visual regression test

This commit is contained in:
saurabhg772244
2025-02-21 19:45:14 +05:30
parent 8857e77495
commit 16573d97f5
8 changed files with 63 additions and 31 deletions

View File

@@ -2,4 +2,4 @@
'mermaid': patch
---
Free fontawesome icons are now embeded as svg inside diagram. Pro icons will still be using <i> tag.
Registered icons are now embedded as SVGs inside diagram. If an icon is not available in the registered icons it will still use <i> tag

View File

@@ -0,0 +1,32 @@
import { imgSnapshotTest } from '../../helpers/util.ts';
const themes = ['default', 'forest', 'dark', 'base', 'neutral'];
themes.forEach((theme, index) => {
describe('Flowchart Icon', () => {
it(`${index + 1}-icon: verify if icons are working from fontawesome library ${theme} theme`, () => {
imgSnapshotTest(
`flowchart TD
A("fab:fa-twitter Twitter") --> B("fab:fa-facebook Facebook")
B --> C("fa:fa-coffee Coffee")
C --> D("fa:fa-car Car")
D --> E("fab:fa-github GitHub")
`,
{ theme }
);
});
});
});
themes.forEach((theme, index) => {
describe('Flowchart Icon', () => {
it(`${index + 1}-icon: verify if registered icons are working on ${theme} theme`, () => {
imgSnapshotTest(
`flowchart TD
A("fa:fa-bell Bell")
`,
{ theme }
);
});
});
});

View File

@@ -6,6 +6,10 @@
href="https://fonts.googleapis.com/css?family=Noto+Sans+SC&display=swap"
rel="stylesheet"
/>
<link
rel="stylesheet"
href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/6.7.2/css/all.min.css"
/>
<style>
svg {
border: 2px solid darkred;

View File

@@ -51,7 +51,7 @@ const contentLoaded = async function () {
mermaid.registerLayoutLoaders(layouts);
mermaid.initialize(graphObj.mermaid);
const staticBellIconPack = {
prefix: 'fa6-regular',
prefix: 'fa',
icons: {
bell: {
body: '<path fill="currentColor" d="M224 0c-17.7 0-32 14.3-32 32v19.2C119 66 64 130.6 64 208v25.4c0 45.4-15.5 89.5-43.8 124.9L5.3 377c-5.8 7.2-6.9 17.1-2.9 25.4S14.8 416 24 416h400c9.2 0 17.6-5.3 21.6-13.6s2.9-18.2-2.9-25.4l-14.9-18.6c-28.3-35.5-43.8-79.6-43.8-125V208c0-77.4-55-142-128-156.8V32c0-17.7-14.3-32-32-32m0 96c61.9 0 112 50.1 112 112v25.4c0 47.9 13.9 94.6 39.7 134.6H72.3c25.8-40 39.7-86.7 39.7-134.6V208c0-61.9 50.1-112 112-112m64 352H160c0 17 6.7 33.3 18.7 45.3S207 512 224 512s33.3-6.7 45.3-18.7S288 465 288 448"/>',

View File

@@ -1916,9 +1916,13 @@ If a class is named default it will be assigned to all classes without specific
## Basic support for fontawesome
It is possible to add icons from fontawesome.
It is possible to add icons from fontawesome and registered icon pack.
The icons are accessed via the syntax fa:#icon class name#.
Mermaid supports icons from registered icon packs. Follow the instructions provided [here](../config/icons.md) to register your icon packs.
The registered icons can be accessed via the syntax #registered icon pack name#:#icon name#.
The fontawesome icons are accessed via the syntax fa:#icon class name#.
```mermaid-example
flowchart TD

View File

@@ -1231,9 +1231,13 @@ If a class is named default it will be assigned to all classes without specific
## Basic support for fontawesome
It is possible to add icons from fontawesome.
It is possible to add icons from fontawesome and registered icon pack.
The icons are accessed via the syntax fa:#icon class name#.
Mermaid supports icons from registered icon packs. Follow the instructions provided [here](../config/icons.md) to register your icon packs.
The registered icons can be accessed via the syntax #registered icon pack name#:#icon name#.
The fontawesome icons are accessed via the syntax fa:#icon class name#.
```mermaid-example
flowchart TD

View File

@@ -6,7 +6,7 @@ describe('replaceIconSubstring', () => {
it('converts FontAwesome icon notations to HTML tags', async () => {
const input = 'This is an icon: fa:fa-user and fab:fa-github';
const output = await replaceIconSubstring(input);
const expected = `This is an icon: <i class='fa fa-user'></i> and <i class='fa-brands fa-github'></i>`;
const expected = `This is an icon: <i class='fa fa-user'></i> and <i class='fab fa-github'></i>`;
expect(output).toEqual(expected);
});
@@ -19,7 +19,7 @@ describe('replaceIconSubstring', () => {
it('correctly processes multiple FontAwesome icon notations in one string', async () => {
const input = 'Icons galore: fa:fa-arrow-right, fak:fa-truck, fas:fa-home';
const output = await replaceIconSubstring(input);
const expected = `Icons galore: <i class='fa fa-arrow-right'></i>, <i class='fak fa-truck'></i>, <i class='fa-solid fa-home'></i>`;
const expected = `Icons galore: <i class='fa fa-arrow-right'></i>, <i class='fak fa-truck'></i>, <i class='fas fa-home'></i>`;
expect(output).toEqual(expected);
});
@@ -33,7 +33,7 @@ describe('replaceIconSubstring', () => {
it('correctly process the registered icons', async () => {
const staticBellIconPack = {
prefix: 'fa6-regular',
prefix: 'fa',
icons: {
bell: {
body: '<path fill="currentColor" d="M224 0c-17.7 0-32 14.3-32 32v19.2C119 66 64 130.6 64 208v25.4c0 45.4-15.5 89.5-43.8 124.9L5.3 377c-5.8 7.2-6.9 17.1-2.9 25.4S14.8 416 24 416h400c9.2 0 17.6-5.3 21.6-13.6s2.9-18.2-2.9-25.4l-14.9-18.6c-28.3-35.5-43.8-79.6-43.8-125V208c0-77.4-55-142-128-156.8V32c0-17.7-14.3-32-32-32m0 96c61.9 0 112 50.1 112 112v25.4c0 47.9 13.9 94.6 39.7 134.6H72.3c25.8-40 39.7-86.7 39.7-134.6V208c0-61.9 50.1-112 112-112m64 352H160c0 17 6.7 33.3 18.7 45.3S207 512 224 512s33.3-6.7 45.3-18.7S288 465 288 448"/>',

View File

@@ -181,19 +181,12 @@ function updateTextContentAndStyles(tspan: any, wrappedLine: MarkdownWord[]) {
/**
* Convert fontawesome labels into fontawesome icons by using a regex pattern
* @param text - The raw string to convert
* @returns string with fontawesome icons as i tags if they are from pro pack and as svg if they are from free pack
* @returns string with fontawesome icons as svg if the icon is registered otherwise as i tags
*/
export async function replaceIconSubstring(text) {
const iconRegex = /(fas|fab|far|fa|fal|fak|fad):fa-([a-z-]+)/g;
const classNameMap = {
fas: 'fa-solid',
fab: 'fa-brands',
far: 'fa-regular',
fa: 'fa',
fal: 'fa-light',
fad: 'fa-duotone',
fak: 'fak',
} as const;
export async function replaceIconSubstring(text: string) {
// The letters 'bklrs' stand for possible endings of the fontawesome prefix (e.g. 'fab' for brands, 'fak' for fa-kit) // cspell: disable-line
const iconRegex = /(fa[bklrs]?):fa-([\w-]+)/g; // cspell: disable-line
const matches = [...text.matchAll(iconRegex)];
if (matches.length === 0) {
return text;
@@ -203,19 +196,14 @@ export async function replaceIconSubstring(text) {
for (const match of matches) {
const [fullMatch, prefix, iconName] = match;
const className = classNameMap[prefix];
const registeredIconName = `${prefix}:${iconName}`;
try {
const isFreeIcon = await isIconAvailable(registeredIconName);
if (!isFreeIcon) {
log.warn(`Icon ${registeredIconName} is a pro icon.`);
newText = newText.replace(fullMatch, `<i class='${className} fa-${iconName}'></i>`);
continue;
}
const faIcon = await getIconSVG(registeredIconName, undefined, { class: 'label-icon' });
if (faIcon) {
const isIconAvail = await isIconAvailable(registeredIconName);
if (isIconAvail) {
const faIcon = await getIconSVG(registeredIconName, undefined, { class: 'label-icon' });
newText = newText.replace(fullMatch, faIcon);
} else {
newText = newText.replace(fullMatch, `<i class='${fullMatch.replace(':', ' ')}'></i>`);
}
} catch (error) {
log.error(`Error processing ${registeredIconName}:`, error);