diff --git a/frontend/src/components/PluginsPanel.jsx b/frontend/src/components/PluginsPanel.jsx index 90f80e2..22c72b2 100644 --- a/frontend/src/components/PluginsPanel.jsx +++ b/frontend/src/components/PluginsPanel.jsx @@ -72,7 +72,7 @@ export default function PluginsPanel({ sessionId, onClose }) { } }); - // State for contextual menu and action toast feedback (#605) + // State for contextual menu and action toast feedback (#602, #605) const [activeMenuId, setActiveMenuId] = useState(null); const [notification, setNotification] = useState(""); @@ -188,7 +188,7 @@ export default function PluginsPanel({ sessionId, onClose }) { } }, [input, sessionId]); - // Close contextual action menu on global click or Escape key (#605) + // Close contextual action menu on global click or Escape key (#602, #605) useEffect(() => { const handleGlobalClick = () => setActiveMenuId(null); const handleKeyDown = (e) => { @@ -395,7 +395,7 @@ export default function PluginsPanel({ sessionId, onClose }) { /> - {/* Plugin selector with favorite & pin toggles (#601), compatibility badges (#597), & contextual menus (#605) */} + {/* Plugin selector with favorite & pin toggles (#601), compatibility badges (#597), & contextual menus (#602, #605) */}
{filteredPlugins.length > 0 ? ( filteredPlugins.map((p) => { @@ -426,7 +426,7 @@ export default function PluginsPanel({ sessionId, onClose }) { {isPinned ? "★" : "☆"} - {/* Context Menu Trigger Button */} + {/* Context Menu Trigger Button (#602) */} - {/* Context Dropdown Menu (#605) */} + {/* Context Dropdown Menu (#602, #605) */} {isMenuOpen && (
e.stopPropagation()} diff --git a/frontend/src/components/PluginsPanel.test.jsx b/frontend/src/components/PluginsPanel.test.jsx index cecb3e5..94bc845 100644 --- a/frontend/src/components/PluginsPanel.test.jsx +++ b/frontend/src/components/PluginsPanel.test.jsx @@ -319,6 +319,44 @@ describe("PluginsPanel Export & Share Suite (#605)", () => { }); }); +describe("PluginsPanel Favorite & Pin Support (#601)", () => { + beforeEach(() => { + localStorage.clear(); + vi.clearAllMocks(); + api.getPlugins.mockResolvedValue({ plugins: mockPluginsList }); + api.getPluginLogs.mockResolvedValue({ logs: [] }); + }); + + afterEach(() => { + cleanup(); + }); + + test("toggles pin state and saves to localStorage", async () => { + render(); + + await waitFor(() => { + expect(screen.getByText("Summarizer")).toBeInTheDocument(); + }); + + const pinBtn = screen.getByLabelText("Pin Summarizer"); + fireEvent.click(pinBtn); + + expect(screen.getByLabelText("Unpin Summarizer")).toBeInTheDocument(); + expect(JSON.parse(localStorage.getItem("plugins-panel-pinned:session-601"))).toContain("summarizer"); + }); + + test("restores pinned favorites from localStorage on mount", async () => { + localStorage.setItem("plugins-panel-pinned:session-601", JSON.stringify(["summarizer"])); + + render(); + + await waitFor(() => { + expect(screen.getByLabelText("Unpin Summarizer")).toBeInTheDocument(); + expect(screen.getByLabelText("Pin Calculator")).toBeInTheDocument(); + }); + }); +}); + describe("PluginsPanel View State & Persistence Suite (#592)", () => { let store = {};