Skip to content

Commit ec54b4e

Browse files
authored
Applies switch compontent (#1941)
* Add applies-switch component * Simplify docs * Fix styling * Run prettier * Fix cursor * Align applies-item styling with tab-item * Remove "bla" * Bett algorithm to generate a sync key * Remove duplicate files * Explain automatic grouping a bit better * Fix markdown example
1 parent abfa6a4 commit ec54b4e

18 files changed

+756
-5
lines changed

docs/_docset.yml

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -92,6 +92,7 @@ toc:
9292
- file: headings.md
9393
- file: admonitions.md
9494
- file: applies.md
95+
- file: applies-switch.md
9596
- file: automated_settings.md
9697
- file: code.md
9798
- file: comments.md
@@ -156,4 +157,4 @@ toc:
156157
- file: bar.md
157158
- folder: baz
158159
children:
159-
- file: qux.md
160+
- file: qux.md

docs/syntax/applies-switch.md

Lines changed: 147 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,147 @@
1+
# Applies switch
2+
3+
The applies-switch directive creates tabbed content where each tab displays an applies_to badge instead of a text title. This is useful for showing content that varies by deployment type, version, or other applicability criteria.
4+
5+
## Basic usage
6+
7+
::::::{tab-set}
8+
:::::{tab-item} Output
9+
10+
::::{applies-switch}
11+
12+
:::{applies-item} stack:
13+
Content for Stack
14+
:::
15+
16+
:::{applies-item} serverless:
17+
Content for Serverless
18+
:::
19+
20+
::::
21+
22+
:::::
23+
:::::{tab-item} Markdown
24+
25+
```markdown
26+
::::{applies-switch}
27+
28+
:::{applies-item} stack:
29+
Content for Stack
30+
:::
31+
32+
:::{applies-item} serverless:
33+
Content for Serverless
34+
:::
35+
36+
::::
37+
```
38+
:::::
39+
::::::
40+
41+
## Multiple `applies_to` definitions
42+
43+
You can specify multiple `applies_to` definitions in a single `applies-item` using YAML object notation with curly braces `{}`.
44+
This is useful when content applies to multiple deployment types or versions simultaneously.
45+
46+
::::::{tab-set}
47+
:::::{tab-item} Output
48+
49+
::::{applies-switch}
50+
51+
:::{applies-item} { ece:, ess: }
52+
Content for ECE and ECH
53+
:::
54+
55+
:::{applies-item} serverless:
56+
Content for Serverless
57+
:::
58+
59+
::::
60+
61+
:::::
62+
:::::{tab-item} Markdown
63+
64+
```markdown
65+
::::{applies-switch}
66+
67+
:::{applies-item} { ece:, ess: }
68+
Content for ECE and ECH
69+
:::
70+
71+
:::{applies-item} serverless:
72+
Content for Serverless
73+
:::
74+
75+
::::
76+
```
77+
:::::
78+
::::::
79+
80+
## Automatic grouping
81+
82+
All applies switches on a page automatically sync together. When you select an applies_to definition in one switch, all other switches will switch to the same applies_to definition.
83+
84+
The format of the applies_to definition doesn't matter - `stack: preview 9.1`, `{ "stack": "preview 9.1" }`, and `{ stack: "preview 9.1" }` all identify the same content and will sync together.
85+
86+
In the following example, both switch sets are automatically grouped and will stay in sync.
87+
88+
::::::{tab-set}
89+
:::::{tab-item} Output
90+
91+
::::{applies-switch}
92+
:::{applies-item} { "stack": "preview 9.0" }
93+
Content for 9.0 version
94+
:::
95+
:::{applies-item} { "stack": "ga 9.1" }
96+
Content for 9.1 version
97+
:::
98+
::::
99+
100+
::::{applies-switch}
101+
:::{applies-item} stack: preview 9.0
102+
Other content for 9.0 version
103+
:::
104+
:::{applies-item} stack: ga 9.1
105+
Other content for 9.1 version
106+
:::
107+
::::
108+
109+
:::::
110+
:::::{tab-item} Markdown
111+
112+
```markdown
113+
::::{applies-switch}
114+
:::{applies-item} { "stack": "preview 9.0" }
115+
Content for 9.0 version
116+
:::
117+
:::{applies-item} { "stack": "ga 9.1" }
118+
Content for 9.1 version
119+
:::
120+
::::
121+
122+
::::{applies-switch}
123+
:::{applies-item} stack: preview 9.0
124+
Other content for 9.0 version
125+
:::
126+
:::{applies-item} stack: ga 9.1
127+
Other content for 9.1 version
128+
:::
129+
::::
130+
```
131+
:::::
132+
::::::
133+
134+
## Supported `applies_to` definitions
135+
136+
The `applies-item` directive accepts any valid applies_to definition that would work with the `{applies_to}` role.
137+
138+
See the [](applies.md) page for more details on valid `applies_to` definitions.
139+
140+
## When to use
141+
142+
Use applies switches when:
143+
144+
- Content varies significantly by deployment type, version, or other applicability criteria
145+
- You want to show applies_to badges as tab titles instead of text
146+
- You need to group related content that differs by applicability
147+
- You want to provide a clear visual indication of what each content section applies to
Lines changed: 88 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,88 @@
1+
// TODO: refactor to typescript. this was copied from the tabs implementation
2+
3+
// @ts-check
4+
5+
// Extra JS capability for selected applies switches to be synced
6+
// The selection is stored in local storage so that it persists across page loads.
7+
8+
const as_id_to_elements = {}
9+
const storageKeyPrefix = 'sphinx-design-applies-switch-id-'
10+
11+
function create_key(el: HTMLElement) {
12+
const syncId = el.getAttribute('data-sync-id')
13+
const syncGroup = el.getAttribute('data-sync-group')
14+
if (!syncId || !syncGroup) return null
15+
return [syncGroup, syncId, syncGroup + '--' + syncId]
16+
}
17+
18+
/**
19+
* Initialize the applies switch selection.
20+
*
21+
*/
22+
function ready() {
23+
// Find all applies switches with sync data
24+
25+
const groups = []
26+
27+
document.querySelectorAll('.applies-switch-label').forEach((label) => {
28+
if (label instanceof HTMLElement) {
29+
const data = create_key(label)
30+
if (data) {
31+
const [group, id, key] = data
32+
33+
// add click event listener
34+
label.onclick = onAppliesSwitchLabelClick
35+
36+
// store map of key to elements
37+
if (!as_id_to_elements[key]) {
38+
as_id_to_elements[key] = []
39+
}
40+
as_id_to_elements[key].push(label)
41+
42+
if (groups.indexOf(group) === -1) {
43+
groups.push(group)
44+
// Check if a specific switch has been selected via URL parameter
45+
const switchParam = new URLSearchParams(
46+
window.location.search
47+
).get(group)
48+
if (switchParam) {
49+
window.sessionStorage.setItem(
50+
storageKeyPrefix + group,
51+
switchParam
52+
)
53+
}
54+
}
55+
56+
// Check is a specific switch has been selected previously
57+
const previousId = window.sessionStorage.getItem(
58+
storageKeyPrefix + group
59+
)
60+
if (previousId === id) {
61+
;(
62+
label.previousElementSibling as HTMLInputElement
63+
).checked = true
64+
}
65+
}
66+
}
67+
})
68+
}
69+
70+
/**
71+
* Activate other switches with the same sync id.
72+
*
73+
* @this {HTMLElement} - The element that was clicked.
74+
*/
75+
function onAppliesSwitchLabelClick() {
76+
const data = create_key(this)
77+
if (!data) return
78+
const [group, id, key] = data
79+
for (const label of as_id_to_elements[key]) {
80+
if (label === this) continue
81+
label.previousElementSibling.checked = true
82+
}
83+
window.sessionStorage.setItem(storageKeyPrefix + group, id)
84+
}
85+
86+
export function initAppliesSwitch() {
87+
ready()
88+
}

src/Elastic.Documentation.Site/Assets/main.ts

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,4 @@
1+
import { initAppliesSwitch } from './applies-switch'
12
import { initCopyButton } from './copybutton'
23
import { initHighlight } from './hljs'
34
import { initImageCarousel } from './image-carousel'
@@ -21,6 +22,7 @@ document.addEventListener('htmx:load', function (event) {
2122
initHighlight()
2223
initCopyButton()
2324
initTabs()
25+
initAppliesSwitch()
2426

2527
// We do this so that the navigation is not initialized twice
2628
if (isLazyLoadNavigationEnabled) {
Lines changed: 64 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,64 @@
1+
@layer components {
2+
.applies-switch {
3+
@apply relative mt-4 flex flex-wrap overflow-hidden;
4+
5+
.applies-switch-label {
6+
@apply text-ink-light border-grey-20 z-20 -mb-[1px] flex cursor-pointer items-center border-1 px-3 py-2;
7+
&:not(:nth-of-type(1)) {
8+
margin-left: -1px;
9+
}
10+
11+
&:hover {
12+
@apply bg-grey-10 border-b-1 border-b-black text-black;
13+
}
14+
}
15+
16+
.applies-item {
17+
@apply cursor-pointer text-inherit;
18+
19+
&:hover {
20+
@apply text-inherit;
21+
}
22+
.applicable-info {
23+
@apply cursor-pointer border-none bg-transparent p-0;
24+
25+
&:not(:last-child):after {
26+
content: ',';
27+
}
28+
}
29+
.applicable-name,
30+
.applicable-meta {
31+
@apply text-base;
32+
}
33+
}
34+
35+
.applies-switch-input {
36+
@apply absolute opacity-0;
37+
}
38+
39+
.applies-switch-content {
40+
@apply border-grey-20 z-0 order-99 hidden w-full border-1 px-6 pt-2 pb-6;
41+
}
42+
43+
.applies-switch-input:checked
44+
+ .applies-switch-label
45+
+ .applies-switch-content {
46+
@apply block;
47+
}
48+
49+
.applies-switch-input:checked + .applies-switch-label,
50+
.applies-switch-label:active {
51+
@apply border-b-blue-elastic text-blue-elastic border-b-1;
52+
}
53+
54+
.applies-switch-input:focus-visible + .applies-switch-label {
55+
outline: var(--outline-size) var(--outline-style)
56+
var(--outline-color);
57+
outline-offset: var(--outline-offset, var(--outline-size));
58+
}
59+
60+
.applies-switch-fallback {
61+
@apply text-sm font-medium;
62+
}
63+
}
64+
}

src/Elastic.Documentation.Site/Assets/open-details-with-anchor.ts

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
import { UAParser } from 'ua-parser-js'
22

3-
const { getBrowser } = new UAParser()
3+
const { browser } = UAParser()
44

55
// This is a fix for anchors in details elements in non-Chrome browsers.
66
export function openDetailsWithAnchor() {
@@ -9,7 +9,6 @@ export function openDetailsWithAnchor() {
99
if (target) {
1010
const closestDetails = target.closest('details')
1111
if (closestDetails) {
12-
const browser = getBrowser()
1312
if (browser.name !== 'Chrome') {
1413
closestDetails.open = true
1514
target.scrollIntoView({

src/Elastic.Documentation.Site/Assets/styles.css

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@
77
@import './markdown/typography.css';
88
@import './markdown/list.css';
99
@import './markdown/tabs.css';
10+
@import './markdown/applies-switch.css';
1011
@import './markdown/code.css';
1112
@import './markdown/icons.css';
1213
@import './markdown/kbd.css';

src/Elastic.Markdown/Elastic.Markdown.csproj

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -43,4 +43,9 @@
4343
<EmbeddedResource Include="Myst\Roles\Icons\svgs\*.svg" />
4444
</ItemGroup>
4545

46+
<ItemGroup>
47+
<UpToDateCheckInput Remove="Myst\Directives\AppliesSwitch\AppliesItemView.cshtml" />
48+
<UpToDateCheckInput Remove="Myst\Directives\AppliesSwitch\AppliesSwitchView.cshtml" />
49+
</ItemGroup>
50+
4651
</Project>

src/Elastic.Markdown/Myst/Components/ApplicableToComponent.cshtml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@
22

33
@foreach (var item in Model.GetApplicabilityItems())
44
{
5-
<span class="applicable-info" data-tippy-content="@(new HtmlString(item.RenderData.TooltipText))">
5+
<span class="applicable-info" data-tippy-content="@(new HtmlString(Model.ShowTooltip ? item.RenderData.TooltipText : string.Empty))">
66
<span class="applicable-name">@item.Key</span>
77

88
@if (!string.IsNullOrEmpty(item.Key) && (item.RenderData.ShowLifecycleName || item.RenderData.ShowVersion || !string.IsNullOrEmpty(item.RenderData.BadgeLifecycleText)))

src/Elastic.Markdown/Myst/Components/ApplicableToViewModel.cs

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,8 @@ public class ApplicableToViewModel
1313
private readonly ApplicabilityRenderer _applicabilityRenderer = new();
1414

1515
public required bool Inline { get; init; }
16+
17+
public bool ShowTooltip { get; init; } = true;
1618
public required ApplicableTo AppliesTo { get; init; }
1719
public required VersionsConfiguration VersionsConfig { get; init; }
1820

@@ -162,4 +164,3 @@ private IEnumerable<ApplicabilityItem> CombineItemsByKey(List<ApplicabilityItem>
162164

163165

164166
}
165-

0 commit comments

Comments
 (0)