Fixes for scatterplot
Test and deploy / deploy (push) Successful in 31s

This commit is contained in:
2026-07-29 10:06:39 -05:00
parent 3c7abc1cbc
commit cf56445915
8 changed files with 77 additions and 347 deletions
+9 -1
View File
@@ -1,5 +1,11 @@
import { loadIssuesForCheckedPaths } from './issues.js';
import { getCheckedFileRefs, initializeTree, renderProductTree, restoreTreeState } from './tree.js';
import {
getCheckedFileRefs,
initializeTree,
renderProductTree,
restoreTreeState,
selectAddressedRelease
} from './tree.js';
import { applyIssueSearchFilter, getIssueTypeFilters, initializeUI } from './ui.js';
import { expandFilePrefixLevel } from './product-tree-model.js';
@@ -26,6 +32,8 @@ function loadProductTree() {
productsData = expandFilePrefixLevel(data);
await renderProductTree(productsData);
restoreTreeState();
const params = new URLSearchParams(window.location.search);
selectAddressedRelease(params.get('product'), params.get('release'));
refreshIssuesForCurrentSelection();
})
.catch(error => console.error('Error loading products:', error));
+30 -2
View File
@@ -34,6 +34,10 @@ const productCache = new Map();
let currentProduct = null;
let renderedPoints = [];
let hoveredPoint = null;
const initialParams = new URLSearchParams(window.location.search);
const initialProduct = initialParams.get('product') || 'PAN-OS';
const initialTrain = initialParams.get('train') || '11.1';
let restoreInitialSelection = true;
function releaseTrain(version) {
return version.match(/^\d+\.\d+/)?.[0] ?? version;
@@ -48,6 +52,14 @@ function fillSelect(select, options) {
}));
}
function updatePlotQuery() {
if (!currentProduct || !trainSelect.value) return;
const url = new URL(window.location.href);
url.searchParams.set('product', currentProduct.product);
url.searchParams.set('train', trainSelect.value);
history.replaceState(null, '', url);
}
async function loadProduct(filename) {
if (!productCache.has(filename)) {
productCache.set(filename, fetch(`data/statistics/${filename}`).then(response => {
@@ -167,7 +179,7 @@ function drawPlot() {
renderedPoints.forEach(point => {
context.beginPath();
const hovered = point.id === hoveredPoint?.id && point.release === hoveredPoint?.release;
const hovered = point.issueNumber === hoveredPoint?.issueNumber;
context.arc(point.x, point.y, hovered ? 5 : 3, 0, Math.PI * 2);
context.fillStyle = hovered ? colors.hover : colors.point;
context.globalAlpha = hovered ? 1 : 0.72;
@@ -212,12 +224,20 @@ canvas?.addEventListener('pointerleave', () => {
canvas?.addEventListener('click', event => {
const point = pointNear(event);
if (point) window.location.href = `index.html?issue=${encodeURIComponent(point.id)}`;
if (point) {
const params = new URLSearchParams({
issue: point.id,
product: currentProduct.product,
release: point.release
});
window.location.href = `index.html?${params}`;
}
});
trainSelect?.addEventListener('change', () => {
hoveredPoint = null;
tooltip.hidden = true;
updatePlotQuery();
drawPlot();
});
@@ -225,6 +245,12 @@ productSelect?.addEventListener('change', async () => {
try {
currentProduct = await loadProduct(productSelect.value);
fillSelect(trainSelect, Array.from(new Set(currentProduct.releases.map(releaseTrain))).reverse());
if (restoreInitialSelection && initialProduct === currentProduct.product && initialTrain &&
Array.from(trainSelect.options).some(option => option.value === initialTrain)) {
trainSelect.value = initialTrain;
}
restoreInitialSelection = false;
updatePlotQuery();
drawPlot();
} catch (error) {
summary.textContent = error.message;
@@ -245,6 +271,8 @@ if (canvas) {
option.textContent = product;
return option;
}));
const requestedProduct = products.find(([product]) => product === initialProduct);
if (requestedProduct) productSelect.value = requestedProduct[1];
productSelect.dispatchEvent(new Event('change'));
})
.catch(error => {
+18
View File
@@ -62,6 +62,24 @@ export function getCheckedFileRefs() {
};
}
export function selectAddressedRelease(product, release) {
if (!productTree || !product || !release) return false;
const node = findAddressedReleaseNode(productTree.nodesById.values(), product, release);
if (!node) return false;
productTree.setSelectedIds([node.id]);
pushTreeState();
return true;
}
export function findAddressedReleaseNode(nodes, product, release) {
const filename = `${release}.md`;
return Array.from(nodes).find(candidate =>
candidate.metadata?.path?.[0] === product &&
candidate.metadata?.addressed?.includes(filename)
);
}
// State restoration now happens when setData renders the generic tree.
export function restoreTreeState() {}
+4
View File
@@ -65,6 +65,10 @@ function initializeIssueSearch() {
function initializeIssueTypeFilters(onSelectionChange) {
const addressedFilter = document.getElementById('filter-addressed');
const knownFilter = document.getElementById('filter-known');
if (new URLSearchParams(window.location.search).has('release')) {
issueTypeFilters.addressed = true;
saveTypeFilters();
}
// Restore checkbox state from persisted filters
addressedFilter.checked = issueTypeFilters.addressed;