diff --git a/public/packets.js b/public/packets.js index bbca46ec..20013458 100644 --- a/public/packets.js +++ b/public/packets.js @@ -59,6 +59,12 @@ function updatePacketsUrl() { history.replaceState(null, '', '#/packets' + buildPacketsQuery(savedTimeWindowMin, RegionFilter.getRegionParam())); + // Update clear-filters button visibility + var cb = document.getElementById('clearFiltersBtn'); + if (cb) { + var active = !!(filters.hash || filters.node || filters.observer || filters.channel || filters.type || filters._filterExpr || filters.myNodes) || !!RegionFilter.getRegionParam(); + cb.style.display = active ? '' : 'none'; + } } let filtersBuilt = false; @@ -785,6 +791,7 @@
+
@@ -1065,6 +1072,57 @@ this.textContent = bar.classList.contains('filters-expanded') ? 'Filters ▴' : 'Filters ▾'; }); + // --- Clear filters button --- + const clearBtn = document.getElementById('clearFiltersBtn'); + if (clearBtn) clearBtn.addEventListener('click', function() { + // Reset filters object + filters.hash = undefined; + filters.node = undefined; + filters.nodeName = undefined; + filters.observer = undefined; + filters.channel = undefined; + filters.type = undefined; + filters._filterExpr = undefined; + filters._packetFilter = null; + filters.myNodes = false; + _observerFilterSet = null; + + // Clear localStorage filter entries + localStorage.removeItem('meshcore-observer-filter'); + localStorage.removeItem('meshcore-type-filter'); + + // Reset DOM inputs + document.getElementById('fHash').value = ''; + document.getElementById('fNode').value = ''; + var pfInput = document.getElementById('packetFilterInput'); + if (pfInput) { pfInput.value = ''; pfInput.classList.remove('filter-active', 'filter-error'); } + var pfError = document.getElementById('packetFilterError'); + if (pfError) pfError.style.display = 'none'; + var pfCount = document.getElementById('packetFilterCount'); + if (pfCount) pfCount.style.display = 'none'; + document.getElementById('fChannel').value = ''; + document.getElementById('fMyNodes').classList.remove('active'); + + // Reset observer multi-select + var obMenu = document.getElementById('observerMenu'); + if (obMenu) obMenu.querySelectorAll('input[type=checkbox]').forEach(function(cb) { cb.checked = false; }); + document.getElementById('observerTrigger').textContent = 'All Observers ▾'; + + // Reset type multi-select + var typeMenu = document.getElementById('typeMenu'); + if (typeMenu) typeMenu.querySelectorAll('input[type=checkbox]').forEach(function(cb) { cb.checked = false; }); + document.getElementById('typeTrigger').textContent = 'All Types ▾'; + + // Reset region filter + RegionFilter.setSelected([]); + + // Update URL and reload + updatePacketsUrl(); + loadPackets(); + }); + // Show clear button if page loaded with active filters (e.g. from URL params) + updatePacketsUrl(); + // Filter event listeners document.getElementById('fHash').value = filters.hash || ''; document.getElementById('fHash').addEventListener('input', debounce((e) => { filters.hash = e.target.value || undefined; updatePacketsUrl(); loadPackets(); }, 300)); diff --git a/test-clear-filters.js b/test-clear-filters.js new file mode 100644 index 00000000..906f3562 --- /dev/null +++ b/test-clear-filters.js @@ -0,0 +1,42 @@ +/* test-clear-filters.js — unit test for clear-filters button (#964) */ +'use strict'; + +const assert = require('assert'); +const fs = require('fs'); +const path = require('path'); + +console.log('--- test-clear-filters.js ---'); + +const src = fs.readFileSync(path.join(__dirname, 'public', 'packets.js'), 'utf-8'); + +// Test 1: button HTML present +assert(src.includes('clearFiltersBtn'), 'clearFiltersBtn ID should exist'); +assert(src.includes('✕ Clear'), 'Clear button label should exist'); + +// Test 2: clear handler resets all filter keys +for (const k of ['hash', 'node', 'observer', 'channel', 'type', '_filterExpr']) { + assert(src.includes(`filters.${k} = undefined`), `should reset filters.${k}`); +} +assert(src.includes('filters._packetFilter = null'), 'should reset _packetFilter'); +assert(src.includes('filters.myNodes = false'), 'should reset myNodes'); + +// Test 3: clears localStorage +assert(src.includes("localStorage.removeItem('meshcore-observer-filter')"), 'clear observer localStorage'); +assert(src.includes("localStorage.removeItem('meshcore-type-filter')"), 'clear type localStorage'); + +// Test 4: resets RegionFilter +assert(src.includes('RegionFilter.setSelected([])'), 'reset RegionFilter'); + +// Test 5: visibility toggle in updatePacketsUrl +assert(src.includes("cb.style.display = active ?"), 'toggle clear btn visibility'); + +// Test 6: resets DOM inputs +assert(src.includes("getElementById('fHash').value = ''"), 'clear hash input'); +assert(src.includes("getElementById('fNode').value = ''"), 'clear node input'); +assert(src.includes("getElementById('fChannel').value = ''"), 'clear channel select'); + +// Test 7: resets multi-select checkboxes +assert(src.includes('observerMenu'), 'clear observer checkboxes'); +assert(src.includes('typeMenu'), 'clear type checkboxes'); + +console.log('All 7 tests passed ✅');