From 20e1ac0e67136a3a95bd7915344944a3a3c4fe8b Mon Sep 17 00:00:00 2001 From: Saravana Kumar Date: Sat, 30 May 2026 21:04:04 +0000 Subject: [PATCH] feat(admin-web): sync product context changes --- .../src/__tests__/product-context.test.tsx | 82 +++++++++++++++++++ .../admin-web/src/lib/product-context.tsx | 17 +++- 2 files changed, 98 insertions(+), 1 deletion(-) create mode 100644 dashboards/admin-web/src/__tests__/product-context.test.tsx diff --git a/dashboards/admin-web/src/__tests__/product-context.test.tsx b/dashboards/admin-web/src/__tests__/product-context.test.tsx new file mode 100644 index 00000000..82d3bfa5 --- /dev/null +++ b/dashboards/admin-web/src/__tests__/product-context.test.tsx @@ -0,0 +1,82 @@ +// @vitest-environment happy-dom + +import { afterEach, beforeEach, describe, expect, it } from 'vitest'; +import { act } from 'react'; +import { createRoot, type Root } from 'react-dom/client'; + +import { ProductProvider, useProduct } from '@/lib/product-context'; + +function ProductProbe() { + const { productId, productName, setProductId } = useProduct(); + return ( +
+ {productId} + {productName} + +
+ ); +} + +describe('ProductProvider', () => { + let container: HTMLDivElement; + let root: Root; + + beforeEach(() => { + (globalThis as unknown as { IS_REACT_ACT_ENVIRONMENT: boolean }).IS_REACT_ACT_ENVIRONMENT = + true; + localStorage.clear(); + container = document.createElement('div'); + document.body.appendChild(container); + root = createRoot(container); + }); + + afterEach(() => { + act(() => root.unmount()); + container.remove(); + localStorage.clear(); + }); + + it('persists changes and emits a same-tab product-changed event', () => { + let eventCount = 0; + window.addEventListener('admin:product-changed', () => { + eventCount += 1; + }); + + act(() => { + root.render( + + + + ); + }); + + act(() => { + container.querySelector('button')!.dispatchEvent(new MouseEvent('click', { bubbles: true })); + }); + + expect(container.querySelector('[data-testid="product-id"]')?.textContent).toBe('nomgap'); + expect(container.querySelector('[data-testid="product-name"]')?.textContent).toBe('NomGap'); + expect(localStorage.getItem('admin_selected_product')).toBe('nomgap'); + expect(eventCount).toBe(1); + }); + + it('syncs provider state when another admin component updates localStorage', () => { + act(() => { + root.render( + + + + ); + }); + + act(() => { + localStorage.setItem('admin_selected_product', 'mindlyst'); + window.dispatchEvent(new Event('admin:product-changed')); + }); + + expect(container.querySelector('[data-testid="product-id"]')?.textContent).toBe('mindlyst'); + expect(container.querySelector('[data-testid="product-name"]')?.textContent).toBe('MindLyst'); + }); +}); diff --git a/dashboards/admin-web/src/lib/product-context.tsx b/dashboards/admin-web/src/lib/product-context.tsx index fb1b1299..c69b6d81 100644 --- a/dashboards/admin-web/src/lib/product-context.tsx +++ b/dashboards/admin-web/src/lib/product-context.tsx @@ -1,9 +1,10 @@ 'use client'; -import { createContext, useContext, useState, useCallback, type ReactNode } from 'react'; +import { createContext, useContext, useState, useCallback, useEffect, type ReactNode } from 'react'; import { KNOWN_PRODUCTS, PRODUCT_ID } from '@/lib/product-constants'; const STORAGE_KEY = 'admin_selected_product'; +const PRODUCT_CHANGED_EVENT = 'admin:product-changed'; interface ProductContextValue { productId: string; @@ -22,10 +23,24 @@ function getInitialProduct(): string { export function ProductProvider({ children }: { children: ReactNode }) { const [productId, setProductIdState] = useState(getInitialProduct); + useEffect(() => { + function syncSelectedProduct() { + setProductIdState(getInitialProduct()); + } + + window.addEventListener(PRODUCT_CHANGED_EVENT, syncSelectedProduct); + window.addEventListener('storage', syncSelectedProduct); + return () => { + window.removeEventListener(PRODUCT_CHANGED_EVENT, syncSelectedProduct); + window.removeEventListener('storage', syncSelectedProduct); + }; + }, []); + const setProductId = useCallback((id: string) => { setProductIdState(id); if (typeof window !== 'undefined') { localStorage.setItem(STORAGE_KEY, id); + window.dispatchEvent(new Event(PRODUCT_CHANGED_EVENT)); } }, []);