Skip to content

Commit 45feb69

Browse files
reakaleekMpdreamz
andauthored
Add new version drop down feature flag and enhance existing version dropdown (#1361)
* Create initial versions config * Add new version dropdown feature flag and enhance existing dropdown * Comment config * Fix config and representation * Revert navigation.yml * Prettier * Remove console.logs * Fix tests * Fix eslint errors * Prettier * Remove legacy_versions from assembler 'versions' config draft * Remove commented code from TableOfContents view * Remove commented code from VersionDropdown.tsx * Revert Products.cs changes * Apply suggestions from code review Co-authored-by: Martijn Laarman <[email protected]> * Refactor naming and add comments * Add more comments * Fix merge conflicts --------- Co-authored-by: Martijn Laarman <[email protected]>
1 parent 1b88283 commit 45feb69

File tree

28 files changed

+2821
-230
lines changed

28 files changed

+2821
-230
lines changed

src/Elastic.ApiExplorer/Endpoints/EndpointView.cshtml

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,7 @@
1212
Next = null,
1313
NavigationHtml = Model.NavigationHtml,
1414
UrlPathPrefix = null,
15+
VersionDropdown = null,
1516
AllowIndexing = false,
1617
CanonicalBaseUrl = null,
1718
GoogleTagManager = new GoogleTagManagerConfiguration(),

src/Elastic.ApiExplorer/Landing/LandingView.cshtml

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,7 @@
1212
Next = null,
1313
NavigationHtml = Model.NavigationHtml,
1414
UrlPathPrefix = null,
15+
VersionDropdown = null,
1516
AllowIndexing = false,
1617
CanonicalBaseUrl = null,
1718
GoogleTagManager = new GoogleTagManagerConfiguration(),

src/Elastic.ApiExplorer/Operations/OperationView.cshtml

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,7 @@
1212
Next = null,
1313
NavigationHtml = Model.NavigationHtml,
1414
UrlPathPrefix = null,
15+
VersionDropdown = null,
1516
AllowIndexing = false,
1617
CanonicalBaseUrl = null,
1718
GoogleTagManager = new GoogleTagManagerConfiguration(),

src/Elastic.Documentation.Configuration/Assembler/AssemblyConfiguration.cs

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -67,6 +67,9 @@ private static TRepository RepositoryDefaults<TRepository>(TRepository r, string
6767
return repository;
6868
}
6969

70+
[YamlMember(Alias = "versions")]
71+
public Dictionary<string, VersionEntry> Versions { get; set; } = [];
72+
7073
[YamlMember(Alias = "narrative")]
7174
public NarrativeRepository Narrative { get; set; } = new();
7275

Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,19 @@
1+
// Licensed to Elasticsearch B.V under one or more agreements.
2+
// Elasticsearch B.V licenses this file to you under the Apache 2.0 License.
3+
// See the LICENSE file in the project root for more information
4+
5+
using YamlDotNet.Serialization;
6+
7+
namespace Elastic.Documentation.Configuration.Assembler;
8+
9+
public record VersionEntry
10+
{
11+
[YamlMember(Alias = "base")]
12+
public string? Base { get; set; }
13+
14+
[YamlMember(Alias = "current")]
15+
public string? Current { get; set; }
16+
17+
[YamlMember(Alias = "legacy_versions")]
18+
public IReadOnlyList<string> LegacyVersions { get; set; } = [];
19+
}

src/Elastic.Documentation.Configuration/Builder/FeatureFlags.cs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,7 @@ namespace Elastic.Documentation.Configuration.Builder;
77
public class FeatureFlags(Dictionary<string, bool> featureFlags)
88
{
99
public bool IsPrimaryNavEnabled => IsEnabled("primary-nav");
10-
public bool IsLandingPageEnabled => IsEnabled("landing-page");
10+
public bool IsVersionDropdownEnabled => IsEnabled("version-dropdown");
1111
private bool IsEnabled(string key)
1212
{
1313
var envKey = $"FEATURE_{key.ToUpperInvariant().Replace('-', '_')}";

src/Elastic.Documentation.Configuration/Serialization/YamlStaticContext.cs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -14,4 +14,5 @@ namespace Elastic.Documentation.Configuration.Serialization;
1414
[YamlSerializable(typeof(PublishEnvironment))]
1515
[YamlSerializable(typeof(GoogleTagManager))]
1616
[YamlSerializable(typeof(ContentSource))]
17+
[YamlSerializable(typeof(VersionEntry))]
1718
public partial class YamlStaticContext;

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

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@ import { initNav } from './pages-nav'
66
import { initSmoothScroll } from './smooth-scroll'
77
import { initTabs } from './tabs'
88
import { initTocNav } from './toc-nav'
9+
import './web-components/VersionDropdown'
910
import 'htmx-ext-head-support'
1011
import 'htmx-ext-preload'
1112
import 'htmx.org'
Lines changed: 179 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,179 @@
1+
import {
2+
EuiButton,
3+
EuiContextMenu,
4+
EuiFlexGroup,
5+
EuiFlexItem,
6+
EuiIcon,
7+
EuiPopover,
8+
EuiText,
9+
useGeneratedHtmlId,
10+
} from '@elastic/eui'
11+
import { icon as EuiIconVisualizeApp } from '@elastic/eui/es/components/icon/assets/app_visualize'
12+
import { icon as EuiIconArrowDown } from '@elastic/eui/es/components/icon/assets/arrow_down'
13+
import { icon as EuiIconArrowLeft } from '@elastic/eui/es/components/icon/assets/arrow_left'
14+
import { icon as EuiIconArrowRight } from '@elastic/eui/es/components/icon/assets/arrow_right'
15+
import { icon as EuiIconCheck } from '@elastic/eui/es/components/icon/assets/check'
16+
import { icon as EuiIconDocument } from '@elastic/eui/es/components/icon/assets/document'
17+
import { icon as EuiIconSearch } from '@elastic/eui/es/components/icon/assets/search'
18+
import { icon as EuiIconTrash } from '@elastic/eui/es/components/icon/assets/trash'
19+
import { icon as EuiIconUser } from '@elastic/eui/es/components/icon/assets/user'
20+
import { icon as EuiIconWrench } from '@elastic/eui/es/components/icon/assets/wrench'
21+
import { appendIconComponentCache } from '@elastic/eui/es/components/icon/icon'
22+
import {
23+
EuiContextMenuPanelDescriptor,
24+
EuiContextMenuPanelItemDescriptor,
25+
} from '@elastic/eui/src/components/context_menu/context_menu'
26+
import { css } from '@emotion/react'
27+
import r2wc from '@r2wc/react-to-web-component'
28+
import * as React from 'react'
29+
import { useState } from 'react'
30+
31+
// One or more icons are passed in as an object of iconKey (string): IconComponent
32+
appendIconComponentCache({
33+
arrowDown: EuiIconArrowDown,
34+
arrowLeft: EuiIconArrowLeft,
35+
arrowRight: EuiIconArrowRight,
36+
document: EuiIconDocument,
37+
search: EuiIconSearch,
38+
trash: EuiIconTrash,
39+
user: EuiIconUser,
40+
wrench: EuiIconWrench,
41+
visualizeApp: EuiIconVisualizeApp,
42+
check: EuiIconCheck,
43+
})
44+
45+
type VersionDropdownItem = {
46+
name: string
47+
href?: string
48+
children?: VersionDropdownItem[]
49+
}
50+
51+
type VersionDropdownProps = {
52+
items: VersionDropdownItem[]
53+
}
54+
55+
const VersionDropdown = ({ items }: VersionDropdownProps) => {
56+
const [isPopoverOpen, setPopover] = useState(false)
57+
58+
const contextMenuPopoverId = useGeneratedHtmlId({
59+
prefix: 'contextMenuPopover',
60+
})
61+
62+
const onButtonClick = () => {
63+
setPopover(!isPopoverOpen)
64+
}
65+
66+
const closePopover = () => {
67+
setPopover(false)
68+
}
69+
70+
const convertItems = (
71+
items: VersionDropdownItem[]
72+
): EuiContextMenuPanelItemDescriptor[] => {
73+
return items.map((item) => {
74+
return {
75+
name: item.name,
76+
href: item.href,
77+
}
78+
})
79+
}
80+
81+
const convertToPanels = (
82+
items: VersionDropdownItem[]
83+
): EuiContextMenuPanelDescriptor[] => {
84+
return items.flatMap((item, index) => {
85+
if (item.children == null) {
86+
return []
87+
} else {
88+
return {
89+
id: index + 1,
90+
title: item.name,
91+
initialFocusedItemIndex: 0,
92+
width: WIDTH,
93+
size: 's',
94+
items: item.children ? convertItems(item.children) : [],
95+
}
96+
}
97+
})
98+
}
99+
100+
const WIDTH = 175
101+
102+
const topLevelItems = items.map((item, index) => {
103+
return {
104+
name: item.name,
105+
panel: item.children?.length ? index + 1 : undefined,
106+
href: item.href,
107+
}
108+
})
109+
110+
const subpanels = convertToPanels(items)
111+
112+
const panels: EuiContextMenuPanelDescriptor[] = [
113+
{
114+
id: 0,
115+
title: (
116+
<EuiFlexGroup gutterSize="s" alignItems="center">
117+
<EuiFlexItem grow={0}>
118+
<EuiIcon type="check" />
119+
</EuiFlexItem>
120+
<EuiFlexItem grow={1}>Current (9.0+)</EuiFlexItem>
121+
</EuiFlexGroup>
122+
),
123+
width: WIDTH,
124+
size: 's',
125+
items: [
126+
...topLevelItems,
127+
{
128+
name: 'All versions',
129+
href: 'https://elastic.co',
130+
},
131+
],
132+
},
133+
...subpanels,
134+
]
135+
136+
const button = (
137+
<EuiButton
138+
iconType="arrowDown"
139+
iconSide="right"
140+
onClick={onButtonClick}
141+
size="s"
142+
color="text"
143+
style={{ borderRadius: 9999 }}
144+
>
145+
<EuiText
146+
size="xs"
147+
css={css`
148+
font-weight: 600;
149+
font-size: 0.875rem;
150+
`}
151+
>
152+
Current (9.0+)
153+
</EuiText>
154+
</EuiButton>
155+
)
156+
157+
return (
158+
<EuiPopover
159+
id={contextMenuPopoverId}
160+
button={button}
161+
isOpen={isPopoverOpen}
162+
closePopover={closePopover}
163+
panelPaddingSize="none"
164+
anchorPosition="downLeft"
165+
repositionOnScroll={true}
166+
>
167+
<EuiContextMenu initialPanelId={0} size="s" panels={panels} />
168+
</EuiPopover>
169+
)
170+
}
171+
172+
customElements.define(
173+
'version-dropdown',
174+
r2wc(VersionDropdown, {
175+
props: {
176+
items: 'json',
177+
},
178+
})
179+
)

src/Elastic.Documentation.Site/Htmx.cs

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -8,8 +8,7 @@ namespace Elastic.Documentation.Site;
88

99
public static class Htmx
1010
{
11-
public static string GetHxSelectOob(bool hasSameTopLevelGroup) => hasSameTopLevelGroup ? "#content-container,#toc-nav" : "#main-container";
12-
11+
public static string GetHxSelectOob(bool hasSameTopLevelGroup) => "#version-dropdown," + (hasSameTopLevelGroup ? "#content-container,#toc-nav" : "#main-container");
1312
public const string Preload = "mousedown";
1413
public const string HxSwap = "none";
1514
public const string HxPushUrl = "true";

0 commit comments

Comments
 (0)