FN-5797: fix usage indicator show hidden button behavior

Prevent hidden-window controls in UsageIndicator from acting as implicit form submitters.

- Set both window-level Hide and provider-level Show hidden buttons to `type="button"`.
- Preserve hidden-window reveal behavior without submitting parent forms.
- Add a regression test covering hide/show persistence and rerender re-sync behavior.

Files changed:
 .changeset/fn-5797-show-hidden-fix.md              |  8 +++++
 packages/dashboard/app/components/UsageIndicator.tsx    |  2 ++
 packages/dashboard/app/components/__tests__/UsageIndicator.test.tsx   | 40 ++++++++++++++++++++++
 3 files changed, 50 insertions(+)

Fusion-Task-Id: FN-5797

Fusion-Task-Lineage: 9072bb68-b86b-4ec7-8b1f-0e6b706cbb5f
This commit is contained in:
gsxdsm
2026-05-31 21:08:41 -07:00
parent c710c5af4c
commit 944c03d495
3 changed files with 50 additions and 0 deletions

View File

@@ -0,0 +1,8 @@
---
"@runfusion/fusion": patch
---
Fixes the UsageIndicator popup hidden-window recovery flow by preventing hide/show controls from acting as implicit form-submit buttons.
- Sets the per-window hide control and provider-level **Show hidden (N)** control to `type="button"` so they do not trigger parent form submits.
- Adds a regression test that verifies clicking **Show hidden** reveals hidden windows, persists the unhidden state, and remains correct after rerender/state re-sync.

View File

@@ -238,6 +238,7 @@ function UsageWindowRow({ window, viewMode, isHidden, onToggleHidden }: UsageWin
{!isHidden && <span className="usage-window-percentage">{headerText}</span>}
{!isHidden && (
<button
type="button"
className="btn-icon usage-window-hide-btn"
onClick={onToggleHidden}
aria-label={`Hide ${window.label}`}
@@ -444,6 +445,7 @@ function ProviderCard({
<span className="usage-provider-name">{provider.name}</span>
{hiddenCount > 0 && (
<button
type="button"
className="btn btn-sm usage-show-hidden-btn"
onClick={() => onShowAllHidden(provider.name)}
data-testid="usage-show-hidden-btn"

View File

@@ -979,6 +979,46 @@ describe("UsageIndicator", () => {
expect(screen.getByRole("button", { name: "Hide Weekly" })).toBeInTheDocument();
});
it("show hidden remains revealed across rerender and does not submit parent forms", () => {
mockUseUsageData.mockReturnValue({
providers: mockProviders,
loading: false,
error: null,
lastUpdated: new Date(),
refresh: mockRefresh,
});
const onSubmit = vi.fn((event: Event) => {
event.preventDefault();
});
const { rerender } = render(
<form onSubmit={onSubmit}>
<UsageIndicator isOpen={true} onClose={mockOnClose} projectId={TEST_PROJECT_ID} />
</form>
);
fireEvent.click(screen.getByRole("button", { name: "Hide Session (5h)" }));
expect(localStorage.getItem(USAGE_HIDDEN_WINDOWS_KEY)).toBe(
JSON.stringify({ Anthropic: ["Session (5h)"] })
);
fireEvent.click(screen.getByTestId("usage-show-hidden-btn"));
expect(onSubmit).not.toHaveBeenCalled();
expect(localStorage.getItem(USAGE_HIDDEN_WINDOWS_KEY)).toBe(JSON.stringify({}));
expect(screen.getByText("Session (5h)").closest(".usage-window")).not.toHaveClass("usage-window--hidden");
rerender(
<form onSubmit={onSubmit}>
<UsageIndicator isOpen={true} onClose={mockOnClose} projectId={TEST_PROJECT_ID} />
</form>
);
expect(screen.queryByTestId("usage-show-hidden-btn")).not.toBeInTheDocument();
expect(screen.getByText("Session (5h)").closest(".usage-window")).not.toHaveClass("usage-window--hidden");
});
it("does not show provider-level show hidden button when no windows are hidden", () => {
mockUseUsageData.mockReturnValue({
providers: mockProviders,