FN-8725: label pending authentication accounts

Add visible labels and responsive spacing for pending credential account fields.

- Associate provider-specific account-name labels with pending credential inputs.
- Preserve account labels through API-key saving and OAuth login actions.
- Cover labeled pending fields for API-key and OAuth credential instances.

Files changed:
 .../AuthenticationSection.instances.test.tsx       | 24 +++++++++++++++++++---
 .../settings/sections/AuthenticationSection.css    |  4 ++++
 .../settings/sections/AuthenticationSection.tsx    | 11 +++++++++-
 3 files changed, 35 insertions(+), 4 deletions(-)

Fusion-Task-Id: FN-8725

Fusion-Task-Lineage: df0b6f7d-d1fb-471a-a277-60c794f6e7a7

Co-authored-by: Fusion (runfusion.ai) <noreply@runfusion.ai>
This commit is contained in:
gsxdsm
2026-08-01 22:36:59 -07:00
parent df0b295454
commit 4c0ead498a
3 changed files with 35 additions and 4 deletions

View File

@@ -39,10 +39,12 @@ function renderSection(providers: AuthProvider[]) {
}
describe("AuthenticationSection credential instances", () => {
it("keeps a single account card free of instance chrome", () => {
it("keeps a single account card free of pending account field chrome", () => {
renderSection([{ id: "brave", name: "Brave", authenticated: true, type: "api_key", instances: [{ instanceId: "default", authenticated: true, isDefault: true, type: "api_key" }] }]);
expect(screen.queryByTestId("auth-instances-brave")).not.toBeInTheDocument();
expect(screen.getByText("Add another account")).toBeInTheDocument();
expect(screen.queryByLabelText("Account name")).not.toBeInTheDocument();
expect(screen.queryByTestId("auth-pending-instance-brave")).not.toBeInTheDocument();
});
it("scopes every multi-instance API-key save to its account and pending row", () => {
@@ -58,12 +60,18 @@ describe("AuthenticationSection credential instances", () => {
fireEvent.click(screen.getByText("Add another account"));
const pending = screen.getByTestId("auth-pending-instance-brave");
const accountName = within(pending).getByLabelText("Account name");
expect(accountName).toHaveAttribute("id", "auth-pending-instance-brave-label");
expect(accountName.closest(".auth-pending-instance-field")).toBeInTheDocument();
fireEvent.change(accountName, { target: { value: "Work" } });
expect(accountName).toHaveValue("Work");
fireEvent.change(within(pending).getByPlaceholderText("Enter API key"), { target: { value: "pending-key" } });
fireEvent.click(within(pending).getByText("Save"));
expect(handlers.handleSaveApiKey).toHaveBeenLastCalledWith("brave", "acct-new", undefined);
expect(handlers.handleSaveApiKey).toHaveBeenLastCalledWith("brave", "acct-new", "Work");
expect(within(pending).getByText("Cancel")).toBeInTheDocument();
});
it("binds OAuth instance actions to the selected instance", () => {
it("binds OAuth instance actions to the selected instance and labels pending accounts", () => {
const handlers = renderSection([{ id: "github-copilot", name: "GitHub", authenticated: true, type: "oauth", instances: [
{ instanceId: "default", authenticated: true, isDefault: true, type: "oauth" },
{ instanceId: "acct-two", authenticated: true, isDefault: false, type: "oauth" },
@@ -71,5 +79,15 @@ describe("AuthenticationSection credential instances", () => {
const row = within(screen.getByTestId("auth-instances-github-copilot")).getByText("acct-two").closest(".auth-instance-row") as HTMLElement;
fireEvent.click(within(row).getByText("Logout"));
expect(handlers.handleLogout).toHaveBeenCalledWith("github-copilot", "acct-two");
fireEvent.click(screen.getByText("Add another account"));
const pending = screen.getByTestId("auth-pending-instance-github-copilot");
const accountName = within(pending).getByLabelText("Account name");
expect(accountName).toHaveAttribute("id", "auth-pending-instance-github-copilot-label");
fireEvent.change(accountName, { target: { value: "Personal" } });
expect(accountName).toHaveValue("Personal");
expect(within(pending).getByText("Cancel")).toBeInTheDocument();
fireEvent.click(within(pending).getByText("Login"));
expect(handlers.handleLogin).toHaveBeenCalledWith("github-copilot", "acct-new", "Personal");
});
});

View File

@@ -6,8 +6,12 @@ Settings modal so account controls stay reachable without introducing a second c
.auth-instance-controls { display: flex; flex-direction: column; gap: var(--space-sm); margin-top: var(--space-sm); }
.auth-instance-list { display: flex; flex-direction: column; gap: var(--space-xs); }
.auth-instance-row, .auth-instance-pending { display: flex; align-items: center; flex-wrap: wrap; gap: var(--space-xs); }
.auth-instance-pending { gap: var(--space-sm); padding: var(--space-sm); }
.auth-pending-instance-field { display: flex; flex: 1 1 var(--space-2xl); flex-direction: column; gap: var(--space-xs); }
.auth-pending-instance-label { color: var(--text-muted); font-size: var(--font-size-xs); font-weight: 600; }
.auth-instance-row > span { flex: 1; min-width: 0; }
@media (max-width: 768px) {
.auth-instance-row, .auth-instance-pending { align-items: stretch; }
.auth-instance-row > .btn, .auth-instance-pending > .btn { flex: 1; }
.auth-pending-instance-field { flex-basis: 100%; }
}

View File

@@ -254,7 +254,16 @@ export function AuthenticationSection({ auth, form, setForm }: AuthenticationSec
})}
</div>}
{pending && <div className="auth-instance-pending" data-testid={`auth-pending-instance-${provider.id}`}>
<input className="input" aria-label={t("settings.auth.accountLabel", "Account name")} value={pending.label} onChange={(event) => setPendingInstances((current) => ({ ...current, [provider.id]: { ...pending, label: event.target.value } }))} />
{/*
FNXC:ProviderAuth 2026-08-02-05:27:
Each pending credential needs a visible Account name label associated with its provider-specific input. The padded field keeps its purpose separate from credential actions at desktop and mobile widths without changing instance identity or save/login payloads.
*/}
<div className="auth-pending-instance-field">
<label className="auth-pending-instance-label" htmlFor={`auth-pending-instance-${provider.id}-label`}>
{t("settings.auth.accountLabel", "Account name")}
</label>
<input id={`auth-pending-instance-${provider.id}-label`} className="input" value={pending.label} onChange={(event) => setPendingInstances((current) => ({ ...current, [provider.id]: { ...pending, label: event.target.value } }))} />
</div>
{providerSupportsApiKey(provider)
? renderApiKeySection(provider, pending.instanceId, pending.label, true)
: renderAvailableOAuthActions(provider, pending.instanceId, pending.label || undefined)}