diff --git a/docs/source/syntax/tabs.md b/docs/source/syntax/tabs.md index e9950d894..eb143b31c 100644 --- a/docs/source/syntax/tabs.md +++ b/docs/source/syntax/tabs.md @@ -4,7 +4,11 @@ title: Tabs Tabbed content is created using the `tab-set` directive with individual `tab-item` blocks for each tab's content. You can embed other directives, like admonitions directly in tabs. -## Syntax +## Tabs Simple + +### Example + +#### Syntax ```markdown ::::{tab-set} @@ -20,6 +24,8 @@ This is where the content for tab #2 goes. :::: ``` +#### Example + ::::{tab-set} :::{tab-item} Tab #1 title @@ -32,6 +38,124 @@ This is where the content for tab #2 goes. :::: +--- + +## Tab Groups + +Tabs can be grouped together by setting the `group` attribute to the same value for each `tab-set`. +This allows for multiple sets of tabs to be controlled together. + +You need to set both the `group` and `sync` attributes to the same value for each `tab-item` to sync the tabs. + +This means tabs with the same group and the same sync value will be selected together. + +### Example + +In the following example we have three tab sets, but only the first two are grouped together. +Hence, the first two tab sets will be in sync, but the third tab set will not be in sync with the first two. + +#### Syntax +```markdown +::::{tab-set} +:group: languages // This is the group name +:::{tab-item} Java +:sync: java // This is the sync name +Content for Java tab +::: + +:::{tab-item} Golang +:sync: golang +Content for Golang tab +::: + +:::{tab-item} C# +:sync: csharp +Content for C# tab +::: + +:::: + +::::{tab-set} +:group: languages +:::{tab-item} Java +:sync: java +Content for Java tab +::: + +:::{tab-item} Golang +:sync: golang +Content for Golang tab +::: + +:::{tab-item} C# +:sync: csharp +Content for C# tab +::: + +:::: +``` + +#### Result + +##### Grouped Tabs + +::::{tab-set} +:group: languages +:::{tab-item} Java +:sync: java +Content for Java tab +::: + +:::{tab-item} Golang +:sync: golang +Content for Golang tab +::: + +:::{tab-item} C# +:sync: csharp +Content for C# tab +::: + +:::: + +::::{tab-set} +:group: languages +:::{tab-item} Java +:sync: java +Other content for Java tab +::: + +:::{tab-item} Golang +:sync: golang +Other content for Golang tab +::: + +:::{tab-item} C# +:sync: csharp +Other content for C# tab +::: +:::: + +##### Ungrouped Tabs + +::::{tab-set} +:::{tab-item} Java +:sync: java +Other content for Java tab that is not in the same group +::: + +:::{tab-item} Golang +:sync: golang +Other content for Golang tab that is not in the same group +::: + +:::{tab-item} C# +:sync: csharp +Other content for Golang tab that is not in the same group +::: + +:::: + ## Asciidoc syntax `````asciidoc @@ -79,4 +203,5 @@ This is where the content for tab #1 goes. This is where the content for tab #2 goes. // end::reg-config[] ---- -``` \ No newline at end of file +``` +````` diff --git a/src/Elastic.Markdown/Myst/Directives/DirectiveHtmlRenderer.cs b/src/Elastic.Markdown/Myst/Directives/DirectiveHtmlRenderer.cs index 1815f0f02..fc6a89691 100644 --- a/src/Elastic.Markdown/Myst/Directives/DirectiveHtmlRenderer.cs +++ b/src/Elastic.Markdown/Myst/Directives/DirectiveHtmlRenderer.cs @@ -191,7 +191,9 @@ private void WriteTabItem(HtmlRenderer renderer, TabItemBlock block) { Index = block.Index, Title = block.Title, - TabSetIndex = block.TabSetIndex + TabSetIndex = block.TabSetIndex, + SyncKey = block.SyncKey, + TabSetGroupKey = block.TabSetGroupKey }); RenderRazorSlice(slice, renderer, block); } diff --git a/src/Elastic.Markdown/Myst/Directives/TabSetBlock.cs b/src/Elastic.Markdown/Myst/Directives/TabSetBlock.cs index 06069f10b..f884844dc 100644 --- a/src/Elastic.Markdown/Myst/Directives/TabSetBlock.cs +++ b/src/Elastic.Markdown/Myst/Directives/TabSetBlock.cs @@ -3,6 +3,7 @@ // See the LICENSE file in the project root for more information using Elastic.Markdown.Diagnostics; +using Elastic.Markdown.Slices.Directives; namespace Elastic.Markdown.Myst.Directives; @@ -12,6 +13,8 @@ public class TabSetBlock(DirectiveBlockParser parser, ParserContext context) public override string Directive => "tab-set"; public int Index { get; set; } + public string? GetGroupKey() => Prop("group"); + public override void FinalizeAndValidate(ParserContext context) => Index = FindIndex(); private int _index = -1; @@ -32,7 +35,7 @@ public class TabItemBlock(DirectiveBlockParser parser, ParserContext context) public string Title { get; private set; } = default!; public int Index { get; private set; } public int TabSetIndex { get; private set; } - + public string? TabSetGroupKey { get; private set; } public string? SyncKey { get; private set; } public bool Selected { get; private set; } @@ -43,7 +46,11 @@ public override void FinalizeAndValidate(ParserContext context) Title = Arguments ?? "{undefined}"; Index = Parent!.IndexOf(this); - TabSetIndex = Parent is TabSetBlock tb ? tb.FindIndex() : -1; + + var tabSet = Parent as TabSetBlock; + + TabSetIndex = tabSet?.FindIndex() ?? -1; + TabSetGroupKey = tabSet?.GetGroupKey(); SyncKey = Prop("sync"); Selected = PropBool("selected"); diff --git a/src/Elastic.Markdown/Slices/Directives/TabItem.cshtml b/src/Elastic.Markdown/Slices/Directives/TabItem.cshtml index fea4cb398..181a101c9 100644 --- a/src/Elastic.Markdown/Slices/Directives/TabItem.cshtml +++ b/src/Elastic.Markdown/Slices/Directives/TabItem.cshtml @@ -1,6 +1,6 @@ @inherits RazorSlice - - + +
[CONTENT]
diff --git a/src/Elastic.Markdown/Slices/Directives/TabSet.cshtml b/src/Elastic.Markdown/Slices/Directives/TabSet.cshtml index c63698e3e..d11bb9532 100644 --- a/src/Elastic.Markdown/Slices/Directives/TabSet.cshtml +++ b/src/Elastic.Markdown/Slices/Directives/TabSet.cshtml @@ -1,4 +1,4 @@ @inherits RazorSlice
[CONTENT] -
\ No newline at end of file + diff --git a/src/Elastic.Markdown/Slices/Directives/_ViewModels.cs b/src/Elastic.Markdown/Slices/Directives/_ViewModels.cs index de3cde0cf..853596226 100644 --- a/src/Elastic.Markdown/Slices/Directives/_ViewModels.cs +++ b/src/Elastic.Markdown/Slices/Directives/_ViewModels.cs @@ -36,8 +36,9 @@ public class TabItemViewModel public required int Index { get; init; } public required int TabSetIndex { get; init; } public required string Title { get; init; } + public required string? SyncKey { get; init; } + public required string? TabSetGroupKey { get; init; } } - public class IncludeViewModel { public required string Html { get; init; } diff --git a/tests/Elastic.Markdown.Tests/Directives/TabTests.cs b/tests/Elastic.Markdown.Tests/Directives/TabTests.cs index a4f7dbdb5..735f8f997 100644 --- a/tests/Elastic.Markdown.Tests/Directives/TabTests.cs +++ b/tests/Elastic.Markdown.Tests/Directives/TabTests.cs @@ -3,6 +3,7 @@ // See the LICENSE file in the project root for more information using Elastic.Markdown.Myst.Directives; using FluentAssertions; +using Markdig; using Xunit.Abstractions; namespace Elastic.Markdown.Tests.Directives; @@ -92,3 +93,86 @@ public void ParsesMultipleTabSets() } } } + +public class GroupTabTests(ITestOutputHelper output) : DirectiveTest(output, + """ + ::::{tab-set} + :group: languages + :::{tab-item} Java + :sync: java + Content for Java tab + ::: + + :::{tab-item} Golang + :sync: golang + Content for Golang tab + ::: + + :::{tab-item} C# + :sync: csharp + Content for C# tab + ::: + + :::: + + ::::{tab-set} + :group: languages + :::{tab-item} Java + :sync: java + Content for Java tab + ::: + + :::{tab-item} Golang + :sync: golang + Content for Golang tab + ::: + + :::{tab-item} C# + :sync: csharp + Content for C# tab + ::: + + :::: + """ +) +{ + [Fact] + public void ParsesMultipleTabSets() + { + var sets = Document.OfType().ToArray(); + sets.Length.Should().Be(2); + for (var s = 0; s < sets.Length; s++) + { + var items = sets[s].OfType().ToArray(); + items.Should().NotBeNull().And.HaveCount(3); + for (var i = 0; i < items.Length; i++) + { + items[i].Index.Should().Be(i); + items[i].TabSetIndex.Should().Be(s); + } + } + } + + [Fact] + public void ParsesGroup() + { + var sets = Document.OfType().ToArray(); + sets.Length.Should().Be(2); + + foreach (var t in sets) + { + t.GetGroupKey().Should().Be("languages"); + } + } + + [Fact] + public void ParsesSyncKey() + { + var set = Document.OfType().First(); + var items = set.OfType().ToArray(); + items.Should().HaveCount(3); + items[0].SyncKey.Should().Be("java"); + items[1].SyncKey.Should().Be("golang"); + items[2].SyncKey.Should().Be("csharp"); + } +}