Skip to content

Commit 5f18745

Browse files
committed
Fix tab groups
1 parent 7cc289e commit 5f18745

File tree

7 files changed

+213
-9
lines changed

7 files changed

+213
-9
lines changed

docs/source/syntax/tabs.md

Lines changed: 123 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,11 @@ title: Tabs
44

55
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.
66

7-
## Syntax
7+
## Tabs Simple
8+
9+
### Example
10+
11+
#### Syntax
812

913
```markdown
1014
::::{tab-set}
@@ -20,6 +24,8 @@ This is where the content for tab #2 goes.
2024
::::
2125
```
2226

27+
#### Example
28+
2329
::::{tab-set}
2430

2531
:::{tab-item} Tab #1 title
@@ -32,6 +38,120 @@ This is where the content for tab #2 goes.
3238

3339
::::
3440

41+
---
42+
43+
## Tab Groups
44+
45+
Tabs can be grouped together by setting the `group` attribute to the same value for each `tab-set`.
46+
This allows for multiple sets of tabs to be controlled together.
47+
48+
You need to set both the `group` and `sync` attributes to the same value for each `tab-item` to sync the tabs.
49+
50+
This means tabs with the same group and the same sync value will be selected together.
51+
52+
### Example
53+
54+
In the following example we have three tab sets, but only the first two are grouped together.
55+
Hence, the first two tab sets will be in sync, but the third tab set will not be in sync with the first two.
56+
57+
#### Syntax
58+
```markdown
59+
::::{tab-set}
60+
:group: languages // This is the group name
61+
:::{tab-item} Java
62+
:sync: java // This is the sync name
63+
Content for Java tab
64+
:::
65+
66+
:::{tab-item} Golang
67+
:sync: golang
68+
Content for Golang tab
69+
:::
70+
71+
:::{tab-item} C#
72+
:sync: csharp
73+
Content for C# tab
74+
:::
75+
76+
::::
77+
78+
::::{tab-set}
79+
:group: languages
80+
:::{tab-item} Java
81+
:sync: java
82+
Content for Java tab
83+
:::
84+
85+
:::{tab-item} Golang
86+
:sync: golang
87+
Content for Golang tab
88+
:::
89+
90+
:::{tab-item} C#
91+
:sync: csharp
92+
Content for C# tab
93+
:::
94+
95+
::::
96+
```
97+
98+
#### Result
99+
100+
::::{tab-set}
101+
:group: languages
102+
:::{tab-item} Java
103+
:sync: java
104+
Content for Java tab
105+
:::
106+
107+
:::{tab-item} Golang
108+
:sync: golang
109+
Content for Golang tab
110+
:::
111+
112+
:::{tab-item} C#
113+
:sync: csharp
114+
Content for C# tab
115+
:::
116+
117+
::::
118+
119+
::::{tab-set}
120+
:group: languages
121+
:::{tab-item} Java
122+
:sync: java
123+
Other content for Java tab
124+
:::
125+
126+
:::{tab-item} Golang
127+
:sync: golang
128+
Other content for Golang tab
129+
:::
130+
131+
:::{tab-item} C#
132+
:sync: csharp
133+
Other content for C# tab
134+
:::
135+
::::
136+
137+
::::{tab-set}
138+
:::{tab-item} Java
139+
:sync: java
140+
Other content for Java tab that is not in the same group
141+
:::
142+
143+
:::{tab-item} Golang
144+
:sync: golang
145+
Other content for Golang tab that is not in the same group
146+
:::
147+
148+
:::{tab-item} C#
149+
:sync: csharp
150+
Other content for Golang tab that is not in the same group
151+
:::
152+
153+
::::
154+
35155
## Asciidoc syntax
36156

37157
`````asciidoc
@@ -79,4 +199,5 @@ This is where the content for tab #1 goes.
79199
This is where the content for tab #2 goes.
80200
// end::reg-config[]
81201
----
82-
```
202+
```
203+
`````

src/Elastic.Markdown/Myst/Directives/DirectiveHtmlRenderer.cs

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -191,7 +191,9 @@ private void WriteTabItem(HtmlRenderer renderer, TabItemBlock block)
191191
{
192192
Index = block.Index,
193193
Title = block.Title,
194-
TabSetIndex = block.TabSetIndex
194+
TabSetIndex = block.TabSetIndex,
195+
SyncKey = block.SyncKey,
196+
TabSetGroupKey = block.TabSetGroupKey
195197
});
196198
RenderRazorSlice(slice, renderer, block);
197199
}

src/Elastic.Markdown/Myst/Directives/TabSetBlock.cs

Lines changed: 9 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@
33
// See the LICENSE file in the project root for more information
44

55
using Elastic.Markdown.Diagnostics;
6+
using Elastic.Markdown.Slices.Directives;
67

78
namespace Elastic.Markdown.Myst.Directives;
89

@@ -12,6 +13,8 @@ public class TabSetBlock(DirectiveBlockParser parser, ParserContext context)
1213
public override string Directive => "tab-set";
1314

1415
public int Index { get; set; }
16+
public string? GetGroupKey() => Prop("group");
17+
1518
public override void FinalizeAndValidate(ParserContext context) => Index = FindIndex();
1619

1720
private int _index = -1;
@@ -32,7 +35,7 @@ public class TabItemBlock(DirectiveBlockParser parser, ParserContext context)
3235
public string Title { get; private set; } = default!;
3336
public int Index { get; private set; }
3437
public int TabSetIndex { get; private set; }
35-
38+
public string? TabSetGroupKey { get; private set; }
3639
public string? SyncKey { get; private set; }
3740
public bool Selected { get; private set; }
3841

@@ -43,7 +46,11 @@ public override void FinalizeAndValidate(ParserContext context)
4346

4447
Title = Arguments ?? "{undefined}";
4548
Index = Parent!.IndexOf(this);
46-
TabSetIndex = Parent is TabSetBlock tb ? tb.FindIndex() : -1;
49+
50+
var tabSet = Parent as TabSetBlock;
51+
52+
TabSetIndex = tabSet?.FindIndex() ?? -1;
53+
TabSetGroupKey = tabSet?.GetGroupKey(); // ?? "default"
4754

4855
SyncKey = Prop("sync");
4956
Selected = PropBool("selected");
Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
@inherits RazorSlice<TabItemViewModel>
2-
<input checked="@(Model.Index == 0 ? "checked" : "")" id="sd-tab-item-@Model.Index" name="sd-tab-set-@Model.TabSetIndex" type="radio">
3-
<label class="sd-tab-label" for="sd-tab-item-@Model.Index">@Model.Title</label>
2+
<input checked="@(Model.Index == 0 ? "checked" : "")" id="sd-tab-item-@Model.TabSetIndex-@Model.Index" name="sd-tab-set-@Model.TabSetIndex" type="radio">
3+
<label class="sd-tab-label" data-sync-id="@Model.SyncKey" data-sync-group="@Model.TabSetGroupKey" for="sd-tab-item-@Model.TabSetIndex-@Model.Index">@Model.Title</label>
44
<div class="sd-tab-content docutils">
55
[CONTENT]
66
</div>
Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
11
@inherits RazorSlice<TabSetViewModel>
22
<div class="sd-tab-set docutils">
33
[CONTENT]
4-
</div>
4+
</div>

src/Elastic.Markdown/Slices/Directives/_ViewModels.cs

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -36,8 +36,9 @@ public class TabItemViewModel
3636
public required int Index { get; init; }
3737
public required int TabSetIndex { get; init; }
3838
public required string Title { get; init; }
39+
public required string? SyncKey { get; init; }
40+
public required string? TabSetGroupKey { get; init; }
3941
}
40-
4142
public class IncludeViewModel
4243
{
4344
public required string Html { get; init; }

tests/Elastic.Markdown.Tests/Directives/TabTests.cs

Lines changed: 73 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@
33
// See the LICENSE file in the project root for more information
44
using Elastic.Markdown.Myst.Directives;
55
using FluentAssertions;
6+
using Markdig;
67
using Xunit.Abstractions;
78

89
namespace Elastic.Markdown.Tests.Directives;
@@ -92,3 +93,75 @@ public void ParsesMultipleTabSets()
9293
}
9394
}
9495
}
96+
97+
public class GroupTabTests(ITestOutputHelper output) : DirectiveTest<TabSetBlock>(output,
98+
"""
99+
::::{tab-set}
100+
:group: languages
101+
:::{tab-item} Java
102+
:sync: java
103+
Content for Java tab
104+
:::
105+
106+
:::{tab-item} Golang
107+
:sync: golang
108+
Content for Golang tab
109+
:::
110+
111+
:::{tab-item} C#
112+
:sync: csharp
113+
Content for C# tab
114+
:::
115+
116+
::::
117+
118+
::::{tab-set}
119+
:group: languages
120+
:::{tab-item} Java
121+
:sync: java
122+
Content for Java tab
123+
:::
124+
125+
:::{tab-item} Golang
126+
:sync: golang
127+
Content for Golang tab
128+
:::
129+
130+
:::{tab-item} C#
131+
:sync: csharp
132+
Content for C# tab
133+
:::
134+
135+
::::
136+
"""
137+
)
138+
{
139+
[Fact]
140+
public void ParsesMultipleTabSets()
141+
{
142+
var sets = Document.OfType<TabSetBlock>().ToArray();
143+
sets.Length.Should().Be(2);
144+
for (var s = 0; s < sets.Length; s++)
145+
{
146+
var items = sets[s].OfType<TabItemBlock>().ToArray();
147+
items.Should().NotBeNull().And.HaveCount(3);
148+
for (var i = 0; i < items.Length; i++)
149+
{
150+
items[i].Index.Should().Be(i);
151+
items[i].TabSetIndex.Should().Be(s);
152+
}
153+
}
154+
}
155+
156+
[Fact]
157+
public void ParsesGroup()
158+
{
159+
var sets = Document.OfType<TabSetBlock>().ToArray();
160+
sets.Length.Should().Be(2);
161+
162+
foreach (var t in sets)
163+
{
164+
t.GetGroupKey().Should().Be("languages");
165+
}
166+
}
167+
}

0 commit comments

Comments
 (0)