From 9f7b1be67b98989dd19b92ab663fd317d322b350 Mon Sep 17 00:00:00 2001 From: Yufei Huang Date: Wed, 31 May 2023 17:20:48 +0800 Subject: [PATCH 1/2] fix: rerender mermaid diagrams on tab switch --- samples/seed/articles/markdown.md | 9 +++++++++ templates/modern/src/markdown.ts | 27 ++++++++++++++++++++++----- 2 files changed, 31 insertions(+), 5 deletions(-) diff --git a/samples/seed/articles/markdown.md b/samples/seed/articles/markdown.md index 97887cf64ab..6b3db875bd3 100644 --- a/samples/seed/articles/markdown.md +++ b/samples/seed/articles/markdown.md @@ -124,6 +124,15 @@ TypeScript content for Windows... REST API content, independent of platform... +```mermaid +flowchart LR + +A[Hard] -->|Text| B(Round) +B --> C{Decision} +C -->|One| D[Result 1] +C -->|Two| E[Result 2] +``` + --- Notice how changing the Linux/Windows selection above changes the content in the .NET and TypeScript tabs. This is because the tab group defines two versions for each .NET and TypeScript, where the Windows/Linux selection above determines which version is shown for .NET/TypeScript. Here's the markup that shows how this is done: diff --git a/templates/modern/src/markdown.ts b/templates/modern/src/markdown.ts index b0aefa9b9b3..896b4278368 100644 --- a/templates/modern/src/markdown.ts +++ b/templates/modern/src/markdown.ts @@ -32,24 +32,36 @@ async function renderMath() { } } +let mermaidRenderCount = 0 + /** * Render mermaid diagrams. */ async function renderMermaid() { - const diagrams = document.querySelectorAll('pre code.lang-mermaid') + const diagrams = document.querySelectorAll('pre code.lang-mermaid') if (diagrams.length <= 0) { return } const { default: mermaid } = await import('mermaid') const theme = getTheme() === 'dark' ? 'dark' : 'default' - mermaid.initialize(Object.assign({ startOnLoad: false, deterministicIds: true, theme }, window.docfx.mermaid)) + // Turn off deterministic ids on re-render + const deterministicIds = mermaidRenderCount === 0 + mermaid.initialize(Object.assign({ startOnLoad: false, deterministicIds, theme }, window.docfx.mermaid)) + mermaidRenderCount++ + + const nodes = [] diagrams.forEach(e => { - e.parentElement.classList.add('mermaid') - e.parentElement.innerHTML = e.innerHTML + // Rerender when elements becomes visible due to https://github.com/mermaid-js/mermaid/issues/1846 + if (e.offsetParent) { + nodes.push(e.parentElement) + e.parentElement.classList.add('mermaid') + e.parentElement.innerHTML = e.innerHTML + } }) - await mermaid.run() + + await mermaid.run({ nodes }) } /** @@ -379,6 +391,7 @@ function renderTabs() { } updateTabsQueryStringParam(state) } + notifyContentUpdated() const top = info.anchor.getBoundingClientRect().top if (top !== originalTop && event instanceof MouseEvent) { window.scrollTo(0, window.pageYOffset + top - originalTop) @@ -434,4 +447,8 @@ function renderTabs() { document.querySelectorAll('div.tabGroup>ul>li>a').forEach(e => e.classList.add('nav-link')) document.querySelectorAll('div.tabGroup>section').forEach(e => e.classList.add('card')) } + + function notifyContentUpdated() { + renderMermaid() + } } From f146a94d22e8eaca50ee1fb3d702e2f2ee0a2a85 Mon Sep 17 00:00:00 2001 From: yufeih Date: Wed, 31 May 2023 09:50:24 +0000 Subject: [PATCH 2/2] test(snapshot): update snapshots for de17b53bf03970341af10010e831600a7b3fff3c --- .../articles/markdown.html.view.verified.json | 4 ++-- .../SamplesTest.Seed/index.verified.json | 2 +- ...-windows-2Ctypescript-markdown-extensions.verified.html | 7 +++++++ 3 files changed, 10 insertions(+), 3 deletions(-) diff --git a/test/docfx.Snapshot.Tests/SamplesTest.Seed/articles/markdown.html.view.verified.json b/test/docfx.Snapshot.Tests/SamplesTest.Seed/articles/markdown.html.view.verified.json index ad7e7ca0045..774548f24d0 100644 --- a/test/docfx.Snapshot.Tests/SamplesTest.Seed/articles/markdown.html.view.verified.json +++ b/test/docfx.Snapshot.Tests/SamplesTest.Seed/articles/markdown.html.view.verified.json @@ -1,5 +1,5 @@ { - "conceptual": "\n

Markdown is a lightweight markup language with plain text formatting syntax. Docfx supports CommonMark compliant Markdown parsed through the Markdig parsing engine.

\n

Alerts

\n
\n
Note
\n

Information the user should notice even if skimming.

\n
\n
\n
Tip
\n

Optional information to help a user be more successful.

\n
\n
\n
Important
\n

Essential information required for user success.

\n
\n
\n
Caution
\n

Negative potential consequences of an action.

\n
\n
\n
Warning
\n

Dangerous certain consequences of an action.

\n
\n
\n
MY TODO
\n

This is a TODO.

\n
\n

Image

\n

\"alt-text\"

\n

Mermaid Diagrams

\n

Flowchart

\n
flowchart LR\n\nA[Hard] -->|Text| B(Round)\nB --> C{Decision}\nC -->|One| D[Result 1]\nC -->|Two| E[Result 2]\n
\n

Code Snippet

\n

The example highlights lines 2, line 5 to 7 and lines 9 to the end of the file.

\n
using System;\nusing Azure;\nusing Azure.Storage;\nusing Azure.Storage.Blobs;\n\nclass Program\n{\n    static void Main(string[] args)\n    {\n        // Define the connection string for the storage account\n        string connectionString = "DefaultEndpointsProtocol=https;AccountName=<your-account-name>;AccountKey=<your-account-key>;EndpointSuffix=core.windows.net";\n\n        // Create a new BlobServiceClient using the connection string\n        var blobServiceClient = new BlobServiceClient(connectionString);\n\n        // Create a new container\n        var container = blobServiceClient.CreateBlobContainer("mycontainer");\n\n        // Upload a file to the container\n        using (var fileStream = File.OpenRead("path/to/file.txt"))\n        {\n            container.UploadBlob("file.txt", fileStream);\n        }\n\n        // Download the file from the container\n        var downloadedBlob = container.GetBlobClient("file.txt").Download();\n        using (var fileStream = File.OpenWrite("path/to/downloaded-file.txt"))\n        {\n            downloadedBlob.Value.Content.CopyTo(fileStream);\n        }\n    }\n}\n

Math Expressions

\n

This sentence uses $ delimiters to show math inline: \\(\\sqrt{3x-1}+(1+x)^2\\)

\n

The Cauchy-Schwarz Inequality

\n

\\(\\left( \\sum_{k=1}^n a_k b_k \\right)^2 \\leq \\left( \\sum_{k=1}^n a_k^2 \\right) \\left( \\sum_{k=1}^n b_k^2 \\right)\\)

\n

This expression uses \\$ to display a dollar sign: \\(\\sqrt{\\$4}\\)

\n

To split $100 in half, we calculate \\(100/2\\)

\n

Custom Syntax Highlighting

\n
resource storageAccount 'Microsoft.Storage/storageAccounts@2021-06-01' = {\n  name: 'hello'\n  // (...)\n}\n
\n

Tabs

\n
\n\n
\n\n

Content for Linux...

\n
\n\n
\n\n

The above tab group was created with the following syntax:

\n
# [Linux](#tab/linux)\n\nContent for Linux...\n\n# [Windows](#tab/windows)\n\nContent for Windows...\n\n---\n
\n

Tabs are indicated by using a specific link syntax within a Markdown header. The syntax can be described as follows:

\n
# [Tab Display Name](#tab/tab-id)\n
\n

A tab starts with a Markdown header, #, and is followed by a Markdown link [](). The text of the link will become the text of the tab header, displayed to the customer. In order for the header to be recognized as a tab, the link itself must start with #tab/ and be followed by an ID representing the content of the tab. The ID is used to sync all same-ID tabs across the page. Using the above example, when a user selects a tab with the link #tab/windows, all tabs with the link #tab/windows on the page will be selected.

\n

Dependent tabs

\n

It's possible to make the selection in one set of tabs dependent on the selection in another set of tabs. Here's an example of that in action:

\n
\n\n
\n\n

.NET content for Linux...

\n
\n\n\n\n\n
\n\n

Notice how changing the Linux/Windows selection above changes the content in the .NET and TypeScript tabs. This is because the tab group defines two versions for each .NET and TypeScript, where the Windows/Linux selection above determines which version is shown for .NET/TypeScript. Here's the markup that shows how this is done:

\n
# [.NET](#tab/dotnet/linux)\n\n.NET content for Linux...\n\n# [.NET](#tab/dotnet/windows)\n\n.NET content for Windows...\n\n# [TypeScript](#tab/typescript/linux)\n\nTypeScript content for Linux...\n\n# [TypeScript](#tab/typescript/windows)\n\nTypeScript content for Windows...\n\n# [REST API](#tab/rest)\n\nREST API content, independent of platform...\n\n---\n
\n", + "conceptual": "\n

Markdown is a lightweight markup language with plain text formatting syntax. Docfx supports CommonMark compliant Markdown parsed through the Markdig parsing engine.

\n

Alerts

\n
\n
Note
\n

Information the user should notice even if skimming.

\n
\n
\n
Tip
\n

Optional information to help a user be more successful.

\n
\n
\n
Important
\n

Essential information required for user success.

\n
\n
\n
Caution
\n

Negative potential consequences of an action.

\n
\n
\n
Warning
\n

Dangerous certain consequences of an action.

\n
\n
\n
MY TODO
\n

This is a TODO.

\n
\n

Image

\n

\"alt-text\"

\n

Mermaid Diagrams

\n

Flowchart

\n
flowchart LR\n\nA[Hard] -->|Text| B(Round)\nB --> C{Decision}\nC -->|One| D[Result 1]\nC -->|Two| E[Result 2]\n
\n

Code Snippet

\n

The example highlights lines 2, line 5 to 7 and lines 9 to the end of the file.

\n
using System;\nusing Azure;\nusing Azure.Storage;\nusing Azure.Storage.Blobs;\n\nclass Program\n{\n    static void Main(string[] args)\n    {\n        // Define the connection string for the storage account\n        string connectionString = "DefaultEndpointsProtocol=https;AccountName=<your-account-name>;AccountKey=<your-account-key>;EndpointSuffix=core.windows.net";\n\n        // Create a new BlobServiceClient using the connection string\n        var blobServiceClient = new BlobServiceClient(connectionString);\n\n        // Create a new container\n        var container = blobServiceClient.CreateBlobContainer("mycontainer");\n\n        // Upload a file to the container\n        using (var fileStream = File.OpenRead("path/to/file.txt"))\n        {\n            container.UploadBlob("file.txt", fileStream);\n        }\n\n        // Download the file from the container\n        var downloadedBlob = container.GetBlobClient("file.txt").Download();\n        using (var fileStream = File.OpenWrite("path/to/downloaded-file.txt"))\n        {\n            downloadedBlob.Value.Content.CopyTo(fileStream);\n        }\n    }\n}\n

Math Expressions

\n

This sentence uses $ delimiters to show math inline: \\(\\sqrt{3x-1}+(1+x)^2\\)

\n

The Cauchy-Schwarz Inequality

\n

\\(\\left( \\sum_{k=1}^n a_k b_k \\right)^2 \\leq \\left( \\sum_{k=1}^n a_k^2 \\right) \\left( \\sum_{k=1}^n b_k^2 \\right)\\)

\n

This expression uses \\$ to display a dollar sign: \\(\\sqrt{\\$4}\\)

\n

To split $100 in half, we calculate \\(100/2\\)

\n

Custom Syntax Highlighting

\n
resource storageAccount 'Microsoft.Storage/storageAccounts@2021-06-01' = {\n  name: 'hello'\n  // (...)\n}\n
\n

Tabs

\n
\n\n
\n\n

Content for Linux...

\n
\n\n
\n\n

The above tab group was created with the following syntax:

\n
# [Linux](#tab/linux)\n\nContent for Linux...\n\n# [Windows](#tab/windows)\n\nContent for Windows...\n\n---\n
\n

Tabs are indicated by using a specific link syntax within a Markdown header. The syntax can be described as follows:

\n
# [Tab Display Name](#tab/tab-id)\n
\n

A tab starts with a Markdown header, #, and is followed by a Markdown link [](). The text of the link will become the text of the tab header, displayed to the customer. In order for the header to be recognized as a tab, the link itself must start with #tab/ and be followed by an ID representing the content of the tab. The ID is used to sync all same-ID tabs across the page. Using the above example, when a user selects a tab with the link #tab/windows, all tabs with the link #tab/windows on the page will be selected.

\n

Dependent tabs

\n

It's possible to make the selection in one set of tabs dependent on the selection in another set of tabs. Here's an example of that in action:

\n
\n\n
\n\n

.NET content for Linux...

\n
\n\n\n\n\n
\n\n

Notice how changing the Linux/Windows selection above changes the content in the .NET and TypeScript tabs. This is because the tab group defines two versions for each .NET and TypeScript, where the Windows/Linux selection above determines which version is shown for .NET/TypeScript. Here's the markup that shows how this is done:

\n
# [.NET](#tab/dotnet/linux)\n\n.NET content for Linux...\n\n# [.NET](#tab/dotnet/windows)\n\n.NET content for Windows...\n\n# [TypeScript](#tab/typescript/linux)\n\nTypeScript content for Linux...\n\n# [TypeScript](#tab/typescript/windows)\n\nTypeScript content for Windows...\n\n# [REST API](#tab/rest)\n\nREST API content, independent of platform...\n\n---\n
\n", "type": "Conceptual", "source": { "remote": { @@ -27,7 +27,7 @@ "_enableSearch": true, "rawTitle": "

Markdown

", "title": "Markdown", - "wordCount": 554.0, + "wordCount": 570.0, "_key": "articles/markdown.md", "_navKey": "~/toc.yml", "_navPath": "toc.html", diff --git a/test/docfx.Snapshot.Tests/SamplesTest.Seed/index.verified.json b/test/docfx.Snapshot.Tests/SamplesTest.Seed/index.verified.json index 17a26b881ad..ac6b54328b5 100644 --- a/test/docfx.Snapshot.Tests/SamplesTest.Seed/index.verified.json +++ b/test/docfx.Snapshot.Tests/SamplesTest.Seed/index.verified.json @@ -252,7 +252,7 @@ "articles/markdown.html": { "href": "articles/markdown.html", "title": "Markdown | docfx seed website", - "keywords": "Markdown Markdown is a lightweight markup language with plain text formatting syntax. Docfx supports CommonMark compliant Markdown parsed through the Markdig parsing engine. Alerts Note Information the user should notice even if skimming. Tip Optional information to help a user be more successful. Important Essential information required for user success. Caution Negative potential consequences of an action. Warning Dangerous certain consequences of an action. MY TODO This is a TODO. Image Mermaid Diagrams Flowchart flowchart LR A[Hard] -->|Text| B(Round) B --> C{Decision} C -->|One| D[Result 1] C -->|Two| E[Result 2] Code Snippet The example highlights lines 2, line 5 to 7 and lines 9 to the end of the file. using System; using Azure; using Azure.Storage; using Azure.Storage.Blobs; class Program { static void Main(string[] args) { // Define the connection string for the storage account string connectionString = \"DefaultEndpointsProtocol=https;AccountName=;AccountKey=;EndpointSuffix=core.windows.net\"; // Create a new BlobServiceClient using the connection string var blobServiceClient = new BlobServiceClient(connectionString); // Create a new container var container = blobServiceClient.CreateBlobContainer(\"mycontainer\"); // Upload a file to the container using (var fileStream = File.OpenRead(\"path/to/file.txt\")) { container.UploadBlob(\"file.txt\", fileStream); } // Download the file from the container var downloadedBlob = container.GetBlobClient(\"file.txt\").Download(); using (var fileStream = File.OpenWrite(\"path/to/downloaded-file.txt\")) { downloadedBlob.Value.Content.CopyTo(fileStream); } } } Math Expressions This sentence uses $ delimiters to show math inline: \\(\\sqrt{3x-1}+(1+x)^2\\) The Cauchy-Schwarz Inequality \\(\\left( \\sum_{k=1}^n a_k b_k \\right)^2 \\leq \\left( \\sum_{k=1}^n a_k^2 \\right) \\left( \\sum_{k=1}^n b_k^2 \\right)\\) This expression uses \\$ to display a dollar sign: \\(\\sqrt{\\$4}\\) To split $100 in half, we calculate \\(100/2\\) Custom Syntax Highlighting resource storageAccount 'Microsoft.Storage/storageAccounts@2021-06-01' = { name: 'hello' // (...) } Tabs Linux Windows Content for Linux... Content for Windows... The above tab group was created with the following syntax: # [Linux](#tab/linux) Content for Linux... # [Windows](#tab/windows) Content for Windows... --- Tabs are indicated by using a specific link syntax within a Markdown header. The syntax can be described as follows: # [Tab Display Name](#tab/tab-id) A tab starts with a Markdown header, #, and is followed by a Markdown link [](). The text of the link will become the text of the tab header, displayed to the customer. In order for the header to be recognized as a tab, the link itself must start with #tab/ and be followed by an ID representing the content of the tab. The ID is used to sync all same-ID tabs across the page. Using the above example, when a user selects a tab with the link #tab/windows, all tabs with the link #tab/windows on the page will be selected. Dependent tabs It's possible to make the selection in one set of tabs dependent on the selection in another set of tabs. Here's an example of that in action: .NET .NET TypeScript TypeScript REST API .NET content for Linux... .NET content for Windows... TypeScript content for Linux... TypeScript content for Windows... REST API content, independent of platform... Notice how changing the Linux/Windows selection above changes the content in the .NET and TypeScript tabs. This is because the tab group defines two versions for each .NET and TypeScript, where the Windows/Linux selection above determines which version is shown for .NET/TypeScript. Here's the markup that shows how this is done: # [.NET](#tab/dotnet/linux) .NET content for Linux... # [.NET](#tab/dotnet/windows) .NET content for Windows... # [TypeScript](#tab/typescript/linux) TypeScript content for Linux... # [TypeScript](#tab/typescript/windows) TypeScript content for Windows... # [REST API](#tab/rest) REST API content, independent of platform... ---" + "keywords": "Markdown Markdown is a lightweight markup language with plain text formatting syntax. Docfx supports CommonMark compliant Markdown parsed through the Markdig parsing engine. Alerts Note Information the user should notice even if skimming. Tip Optional information to help a user be more successful. Important Essential information required for user success. Caution Negative potential consequences of an action. Warning Dangerous certain consequences of an action. MY TODO This is a TODO. Image Mermaid Diagrams Flowchart flowchart LR A[Hard] -->|Text| B(Round) B --> C{Decision} C -->|One| D[Result 1] C -->|Two| E[Result 2] Code Snippet The example highlights lines 2, line 5 to 7 and lines 9 to the end of the file. using System; using Azure; using Azure.Storage; using Azure.Storage.Blobs; class Program { static void Main(string[] args) { // Define the connection string for the storage account string connectionString = \"DefaultEndpointsProtocol=https;AccountName=;AccountKey=;EndpointSuffix=core.windows.net\"; // Create a new BlobServiceClient using the connection string var blobServiceClient = new BlobServiceClient(connectionString); // Create a new container var container = blobServiceClient.CreateBlobContainer(\"mycontainer\"); // Upload a file to the container using (var fileStream = File.OpenRead(\"path/to/file.txt\")) { container.UploadBlob(\"file.txt\", fileStream); } // Download the file from the container var downloadedBlob = container.GetBlobClient(\"file.txt\").Download(); using (var fileStream = File.OpenWrite(\"path/to/downloaded-file.txt\")) { downloadedBlob.Value.Content.CopyTo(fileStream); } } } Math Expressions This sentence uses $ delimiters to show math inline: \\(\\sqrt{3x-1}+(1+x)^2\\) The Cauchy-Schwarz Inequality \\(\\left( \\sum_{k=1}^n a_k b_k \\right)^2 \\leq \\left( \\sum_{k=1}^n a_k^2 \\right) \\left( \\sum_{k=1}^n b_k^2 \\right)\\) This expression uses \\$ to display a dollar sign: \\(\\sqrt{\\$4}\\) To split $100 in half, we calculate \\(100/2\\) Custom Syntax Highlighting resource storageAccount 'Microsoft.Storage/storageAccounts@2021-06-01' = { name: 'hello' // (...) } Tabs Linux Windows Content for Linux... Content for Windows... The above tab group was created with the following syntax: # [Linux](#tab/linux) Content for Linux... # [Windows](#tab/windows) Content for Windows... --- Tabs are indicated by using a specific link syntax within a Markdown header. The syntax can be described as follows: # [Tab Display Name](#tab/tab-id) A tab starts with a Markdown header, #, and is followed by a Markdown link [](). The text of the link will become the text of the tab header, displayed to the customer. In order for the header to be recognized as a tab, the link itself must start with #tab/ and be followed by an ID representing the content of the tab. The ID is used to sync all same-ID tabs across the page. Using the above example, when a user selects a tab with the link #tab/windows, all tabs with the link #tab/windows on the page will be selected. Dependent tabs It's possible to make the selection in one set of tabs dependent on the selection in another set of tabs. Here's an example of that in action: .NET .NET TypeScript TypeScript REST API .NET content for Linux... .NET content for Windows... TypeScript content for Linux... TypeScript content for Windows... REST API content, independent of platform... flowchart LR A[Hard] -->|Text| B(Round) B --> C{Decision} C -->|One| D[Result 1] C -->|Two| E[Result 2] Notice how changing the Linux/Windows selection above changes the content in the .NET and TypeScript tabs. This is because the tab group defines two versions for each .NET and TypeScript, where the Windows/Linux selection above determines which version is shown for .NET/TypeScript. Here's the markup that shows how this is done: # [.NET](#tab/dotnet/linux) .NET content for Linux... # [.NET](#tab/dotnet/windows) .NET content for Windows... # [TypeScript](#tab/typescript/linux) TypeScript content for Linux... # [TypeScript](#tab/typescript/windows) TypeScript content for Windows... # [REST API](#tab/rest) REST API content, independent of platform... ---" }, "index.html": { "href": "index.html", diff --git a/test/docfx.Snapshot.Tests/SamplesTest.SeedHtml/html/articles-markdown.html-tabs-windows-2Ctypescript-markdown-extensions.verified.html b/test/docfx.Snapshot.Tests/SamplesTest.SeedHtml/html/articles-markdown.html-tabs-windows-2Ctypescript-markdown-extensions.verified.html index 68c6a3dd8a8..d4cca50dc73 100644 --- a/test/docfx.Snapshot.Tests/SamplesTest.SeedHtml/html/articles-markdown.html-tabs-windows-2Ctypescript-markdown-extensions.verified.html +++ b/test/docfx.Snapshot.Tests/SamplesTest.SeedHtml/html/articles-markdown.html-tabs-windows-2Ctypescript-markdown-extensions.verified.html @@ -461,6 +461,13 @@

Dependent tabs