mirror of
https://github.com/mermaid-js/mermaid.git
synced 2025-08-15 06:19:24 +02:00
Updated docs, added visual regression test
This commit is contained in:
@@ -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
|
||||
|
32
cypress/integration/rendering/flowchart-icon.spec.js
Normal file
32
cypress/integration/rendering/flowchart-icon.spec.js
Normal 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 }
|
||||
);
|
||||
});
|
||||
});
|
||||
});
|
@@ -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;
|
||||
|
@@ -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"/>',
|
||||
|
@@ -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
|
||||
|
@@ -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
|
||||
|
@@ -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"/>',
|
||||
|
@@ -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 isIconAvail = await isIconAvailable(registeredIconName);
|
||||
if (isIconAvail) {
|
||||
const faIcon = await getIconSVG(registeredIconName, undefined, { class: 'label-icon' });
|
||||
if (faIcon) {
|
||||
newText = newText.replace(fullMatch, faIcon);
|
||||
} else {
|
||||
newText = newText.replace(fullMatch, `<i class='${fullMatch.replace(':', ' ')}'></i>`);
|
||||
}
|
||||
} catch (error) {
|
||||
log.error(`Error processing ${registeredIconName}:`, error);
|
||||
|
Reference in New Issue
Block a user