This is an automated email from the ASF dual-hosted git repository.
potiuk pushed a commit to branch main
in repository https://gitbox.apache.org/repos/asf/airflow-site.git
The following commit(s) were added to refs/heads/main by this push:
new e619dc7796 Add filter input and current-version highlight to docs
version selector (#1580)
e619dc7796 is described below
commit e619dc7796bcfc3a506565bb57fb7f50d4e3aacb
Author: Andrew Chang <[email protected]>
AuthorDate: Wed Jul 1 15:04:38 2026 +0000
Add filter input and current-version highlight to docs version selector
(#1580)
* Add filter input and current-version highlight to docs version selector
The docs version selector dropdown grows with every Airflow release and
users
arriving from a search engine cannot tell at a glance whether they are
reading
Airflow 2 or Airflow 3 documentation. This change:
- Adds a filter input at the top of the dropdown; typing narrows the list by
version prefix so ``1.`` shows the 1.x line without matching ``3.11.x``
as a
substring.
- Highlights the version the user is currently viewing so switching does not
require scanning the entire list.
- On the main ``apache-airflow`` package, prefixes the toggle label with the
major line (e.g. ``Airflow 3 · 3.4.0``) so old-major docs are visually
distinct from current ones. Provider packages keep their existing label.
Closes https://github.com/apache/airflow/issues/51455.
* Update sphinx_airflow_theme/LATEST_VERSION to 0.3.12
* Update theme file hash and __init__ version to match LATEST_VERSION.txt
The previous version bump only updated LATEST_VERSION.txt; the pre-commit
check-sphinx-theme-version hook also expects .theme_files_hash and
__init__.py to be in sync.
* Bump theme version to 0.3.13 to match CI-computed hash
The previous hash was computed locally with static/_gen/ build artefacts
present, but static/_gen/ is gitignored and never present on CI when the
check-sphinx-theme-version prek hook runs. That caused an infinite bump
loop between local commits and CI complaints.
The hash committed here is computed with static/_gen/ removed, matching
what CI sees on a clean checkout.
---
landing-pages/site/assets/scss/_dropdown.scss | 41 +++++++++-
landing-pages/src/js/versionSelector.js | 93 ++++++++++++++++++++--
sphinx_airflow_theme/.theme_files_hash | 2 +-
sphinx_airflow_theme/LATEST_VERSION.txt | 2 +-
.../sphinx_airflow_theme/__init__.py | 2 +-
.../sphinx_airflow_theme/version-selector.html | 12 ++-
6 files changed, 141 insertions(+), 11 deletions(-)
diff --git a/landing-pages/site/assets/scss/_dropdown.scss
b/landing-pages/site/assets/scss/_dropdown.scss
index 51ec38b460..060f34c135 100644
--- a/landing-pages/site/assets/scss/_dropdown.scss
+++ b/landing-pages/site/assets/scss/_dropdown.scss
@@ -35,7 +35,46 @@
}
.dropdown-menu {
- max-height: 100%;
+ max-height: 60vh;
overflow-y: auto;
+ min-width: 220px;
+ }
+
+ .version-filter-wrapper {
+ padding: 8px 12px;
+ position: sticky;
+ top: 0;
+ background: inherit;
+ z-index: 1;
+ border-bottom: 1px solid map-get($colors, brownish-grey);
+ }
+
+ .version-filter {
+ width: 100%;
+ padding: 4px 8px;
+ border: 1px solid map-get($colors, brownish-grey);
+ border-radius: 4px;
+ font-size: 14px;
+ background: transparent;
+ color: inherit;
+
+ &:focus {
+ outline: none;
+ border-color: map-get($colors, cerulean-blue-aa);
+ box-shadow: 0 0 0 2px rgba(map-get($colors, cerulean-blue-aa), 0.2);
+ }
+ }
+
+ .dropdown-item.active {
+ font-weight: 600;
+ background-color: rgba(map-get($colors, cerulean-blue-aa), 0.1);
+ color: inherit;
+ }
+
+ .version-empty-state {
+ padding: 8px 12px;
+ color: map-get($colors, brownish-grey);
+ font-style: italic;
+ font-size: 13px;
}
}
diff --git a/landing-pages/src/js/versionSelector.js
b/landing-pages/src/js/versionSelector.js
index bd9ddd24c3..d59d328527 100644
--- a/landing-pages/src/js/versionSelector.js
+++ b/landing-pages/src/js/versionSelector.js
@@ -19,6 +19,9 @@
import {compareVersion} from "./sortVersions";
+const AIRFLOW_PACKAGE = "apache-airflow";
+const STABLE_KEY = "stable";
+
const getCurrentPageInfo = () => {
const [, , currentPackageName, currentVersion, ...pagePathParts] =
document.location.pathname.split("/");
@@ -26,25 +29,103 @@ const getCurrentPageInfo = () => {
return {currentVersion, currentPackageName, pagePath};
};
+// Only the main airflow package gets the "Airflow N" major prefix; providers
have self-explanatory versioning.
+const updateToggleLabel = (versionSelector, currentPackageName,
currentVersion, stableVersion) => {
+ if (currentPackageName !== AIRFLOW_PACKAGE) {
+ return;
+ }
+ const resolvedVersion = currentVersion === STABLE_KEY ? stableVersion :
currentVersion;
+ const major = resolvedVersion.split(".")[0];
+ if (!/^\d+$/.test(major)) {
+ return;
+ }
+ const label = versionSelector.querySelector(".version-label");
+ const version = versionSelector.querySelector(".version");
+ if (label && version) {
+ label.textContent = `Airflow ${major} · `;
+ version.textContent = resolvedVersion;
+ }
+};
+
const updateVersionSelector = (versionSelector, packageAllVersions,
stableVersion) => {
const templateText =
versionSelector.querySelector("#version-item-template").innerText;
let templateElement = document.createElement("div");
templateElement.innerHTML = templateText;
templateElement = templateElement.firstElementChild;
- const dropdownMenu = versionSelector.querySelector(".dropdown-menu");
+ const versionList = versionSelector.querySelector(".version-list");
+ const filterInput = versionSelector.querySelector(".version-filter");
+ const emptyState = versionSelector.querySelector(".version-empty-state");
- const {currentPackageName, pagePath} = getCurrentPageInfo();
+ const {currentPackageName, currentVersion, pagePath} = getCurrentPageInfo();
+ updateToggleLabel(versionSelector, currentPackageName, currentVersion,
stableVersion);
- const appendNewVersionLink = (targetVersion, label) => {
+ const appendNewVersionLink = (targetVersion, label, matchVersion) => {
const newElement = templateElement.cloneNode(true);
const newDocsLink =
`/docs/${currentPackageName}/${targetVersion}/${pagePath}`;
newElement.setAttribute("href", newDocsLink);
+ newElement.setAttribute("data-version", targetVersion);
+ // Filter compares against ``data-match-version`` — for the "stable" alias
this holds the actual version.
+ newElement.setAttribute("data-match-version", matchVersion);
newElement.innerText = label;
- dropdownMenu.appendChild(newElement);
+ if (targetVersion === currentVersion) {
+ newElement.classList.add("active");
+ newElement.setAttribute("aria-current", "true");
+ }
+ versionList.appendChild(newElement);
+ };
+
+ appendNewVersionLink(STABLE_KEY, `Stable (${stableVersion})`, stableVersion);
+ packageAllVersions.forEach((version) => appendNewVersionLink(version,
version, version));
+
+ if (!filterInput) {
+ return;
+ }
+
+ const dropdownItems = () =>
Array.from(versionList.querySelectorAll(".dropdown-item"));
+
+ const applyFilter = (rawQuery) => {
+ const query = rawQuery.trim().toLowerCase();
+ let visibleCount = 0;
+ dropdownItems().forEach((item) => {
+ const matchVersion = (item.getAttribute("data-match-version") ||
"").toLowerCase();
+ // Prefix match so typing "1." shows 1.x releases (not 3.11.x which
contains "1." as substring).
+ const match = query === "" || matchVersion.startsWith(query);
+ item.hidden = !match;
+ if (match) {
+ visibleCount += 1;
+ }
+ });
+ if (emptyState) {
+ emptyState.hidden = visibleCount > 0;
+ }
};
- appendNewVersionLink("stable", `Stable (${stableVersion})`);
- packageAllVersions.forEach((version) => appendNewVersionLink(version,
version));
+
+ filterInput.addEventListener("input", (event) =>
applyFilter(event.target.value));
+
+ const toggle = versionSelector.querySelector(".dropdown-toggle");
+ if (toggle) {
+ toggle.addEventListener("click", () => {
+ window.requestAnimationFrame(() => filterInput.focus());
+ });
+ }
+
+ filterInput.addEventListener("keydown", (event) => {
+ if (event.key === "Escape") {
+ if (filterInput.value) {
+ filterInput.value = "";
+ applyFilter("");
+ } else if (toggle) {
+ toggle.click();
+ }
+ } else if (event.key === "Enter") {
+ const firstVisible = dropdownItems().find((item) => !item.hidden);
+ if (firstVisible) {
+ event.preventDefault();
+ firstVisible.click();
+ }
+ }
+ });
};
const runVersionSelector = () => {
diff --git a/sphinx_airflow_theme/.theme_files_hash
b/sphinx_airflow_theme/.theme_files_hash
index 032fbb02b6..540ee79e5e 100644
--- a/sphinx_airflow_theme/.theme_files_hash
+++ b/sphinx_airflow_theme/.theme_files_hash
@@ -1 +1 @@
-421d922d1ea1d4d45fa1acd23c8ecfa2780266f036b1a91f78ba4af7a9a3d0e8
+d21a543dd8af8cb4b4646a185f29fd46a2276d30c3b902ad7b92201d5ee9a29a
diff --git a/sphinx_airflow_theme/LATEST_VERSION.txt
b/sphinx_airflow_theme/LATEST_VERSION.txt
index 208059121d..e4737652ca 100644
--- a/sphinx_airflow_theme/LATEST_VERSION.txt
+++ b/sphinx_airflow_theme/LATEST_VERSION.txt
@@ -1 +1 @@
-0.3.11
+0.3.13
diff --git a/sphinx_airflow_theme/sphinx_airflow_theme/__init__.py
b/sphinx_airflow_theme/sphinx_airflow_theme/__init__.py
index 056e44df13..3ac2f3aad8 100644
--- a/sphinx_airflow_theme/sphinx_airflow_theme/__init__.py
+++ b/sphinx_airflow_theme/sphinx_airflow_theme/__init__.py
@@ -18,7 +18,7 @@
from os import path
from sphinx.application import Sphinx
-__version__ = '0.3.11'
+__version__ = '0.3.13'
__version_full__ = __version__
diff --git a/sphinx_airflow_theme/sphinx_airflow_theme/version-selector.html
b/sphinx_airflow_theme/sphinx_airflow_theme/version-selector.html
index f6267f78a9..c0b8779ce3 100644
--- a/sphinx_airflow_theme/sphinx_airflow_theme/version-selector.html
+++ b/sphinx_airflow_theme/sphinx_airflow_theme/version-selector.html
@@ -23,7 +23,17 @@
<span class="version-label">Version: </span><span class="version">{{
version }}</span>
</a>
<div class="dropdown-menu" aria-labelledby="versionDropdown">
-
+ <div class="version-filter-wrapper">
+ <input
+ type="search"
+ class="version-filter"
+ placeholder="Type to search versions..."
+ aria-label="Filter versions"
+ autocomplete="off"
+ >
+ </div>
+ <div class="version-list"></div>
+ <div class="version-empty-state" hidden>No matching versions</div>
</div>
<script type="application/x-template" id="version-item-template">
<a class="dropdown-item"></a>