Skip to content

Commit 4cf5421

Browse files
authored
Merge pull request #488 from netwrix/dev
dev to main
2 parents b992a48 + 6f849cd commit 4cf5421

2 files changed

Lines changed: 32 additions & 26 deletions

File tree

src/theme/SearchBar/index.js

Lines changed: 29 additions & 23 deletions
Original file line numberDiff line numberDiff line change
@@ -492,7 +492,7 @@ export default function SearchBar() {
492492
// Multi-select state for products
493493
const [selectedProducts, setSelectedProducts] = useState(() => {
494494
if (typeof window === 'undefined') return [];
495-
const saved = localStorage.getItem('docs_product_filter');
495+
const saved = sessionStorage.getItem('docs_product_filter');
496496
try {
497497
return saved ? JSON.parse(saved) : [];
498498
} catch {
@@ -507,10 +507,10 @@ export default function SearchBar() {
507507
selectedProductsRef.current = selectedProducts;
508508
}, [selectedProducts]);
509509

510-
// Sync selectedProducts to localStorage and dispatch custom event for same-tab sync
510+
// Sync selectedProducts to sessionStorage and dispatch custom event for same-tab sync
511511
useEffect(() => {
512512
if (typeof window !== 'undefined') {
513-
localStorage.setItem('docs_product_filter', JSON.stringify(selectedProducts));
513+
sessionStorage.setItem('docs_product_filter', JSON.stringify(selectedProducts));
514514
// Dispatch custom event for same-tab synchronization
515515
window.dispatchEvent(new CustomEvent('productFilterChange', {
516516
detail: {products: selectedProducts}
@@ -558,38 +558,44 @@ export default function SearchBar() {
558558
return () => clearInterval(interval);
559559
}, []);
560560

561-
// Helper to refresh search - preserve query and wait for user action
561+
// Helper to refresh search results with current filters.
562+
// DocSearch uses React-controlled inputs, so plain native input events don't trigger
563+
// its onChange handler. We use React's native HTMLInputElement value setter to bypass
564+
// React's change-tracking wrapper, then dispatch an input event so React processes it.
562565
const refreshSearch = useCallback(() => {
563566
const input =
564567
document.querySelector('.DocSearch-Input') ||
565568
document.querySelector('input[type="search"]');
566569

567-
if (input && input.value) {
568-
searchQueryRef.current = input.value;
570+
if (!input || !input.value) return;
569571

570-
// Try multiple approaches to trigger search
571-
const query = input.value;
572+
const query = input.value;
573+
searchQueryRef.current = query;
572574

573-
// Approach 1: Simulate typing by adding/removing character
574-
setTimeout(() => {
575-
if (input) {
576-
input.value = query + ' ';
577-
input.dispatchEvent(new Event('input', {bubbles: true}));
575+
// Grab the native setter before React wraps it
576+
const nativeSetter = Object.getOwnPropertyDescriptor(
577+
window.HTMLInputElement.prototype,
578+
'value',
579+
).set;
578580

579-
setTimeout(() => {
580-
input.value = query;
581-
input.dispatchEvent(new Event('input', {bubbles: true}));
582-
input.dispatchEvent(new KeyboardEvent('keydown', { key: 'Enter', bubbles: true }));
583-
}, 50);
584-
}
585-
}, 50);
586-
}
581+
// Simulate typing a trailing space and removing it.
582+
// This causes DocSearch/Autocomplete to run a new search with the updated filters.
583+
// Both events fire in the same JS task so DocSearch batches them — only the final
584+
// value (query) triggers a visible search, preventing an intermediate result flash.
585+
setTimeout(() => {
586+
nativeSetter.call(input, query + ' ');
587+
input.dispatchEvent(new Event('input', {bubbles: true}));
588+
589+
nativeSetter.call(input, query);
590+
input.dispatchEvent(new Event('input', {bubbles: true}));
591+
}, 50);
587592
}, []);
588593

589594
const onChangeProducts = useCallback((newProducts) => {
595+
selectedProductsRef.current = newProducts; // Sync ref immediately so search uses new filters
590596
setSelectedProducts(newProducts);
591-
// localStorage and event dispatch handled by useEffect
592-
}, []);
597+
refreshSearch(); // Re-run current query with updated filters
598+
}, [refreshSearch]);
593599

594600
// This is where we will portal the filters into the modal DOM.
595601
const [modalHeaderEl, setModalHeaderEl] = useState(null);

src/theme/SearchPage/index.js

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -237,7 +237,7 @@ function SearchPageContent() {
237237
const [selectedProducts, setSelectedProducts] = useState(() => {
238238
if (productsFromUrl.length > 0) return productsFromUrl;
239239
if (typeof window === 'undefined') return [];
240-
const saved = localStorage.getItem('docs_product_filter');
240+
const saved = sessionStorage.getItem('docs_product_filter');
241241
try {
242242
return saved ? JSON.parse(saved) : [];
243243
} catch {
@@ -251,10 +251,10 @@ function SearchPageContent() {
251251
const targetPageRef = useRef(null);
252252
const isInternalNavigation = useRef(false);
253253

254-
// Sync selectedProducts to localStorage and dispatch custom event for same-tab sync
254+
// Sync selectedProducts to sessionStorage and dispatch custom event for same-tab sync
255255
useEffect(() => {
256256
if (typeof window !== 'undefined') {
257-
localStorage.setItem('docs_product_filter', JSON.stringify(selectedProducts));
257+
sessionStorage.setItem('docs_product_filter', JSON.stringify(selectedProducts));
258258
// Dispatch custom event for same-tab synchronization
259259
window.dispatchEvent(new CustomEvent('productFilterChange', {
260260
detail: {products: selectedProducts}

0 commit comments

Comments
 (0)