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:
@@ -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");
|
||||
});
|
||||
});
|
||||
|
||||
@@ -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%; }
|
||||
}
|
||||
|
||||
@@ -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)}
|
||||
|
||||
Reference in New Issue
Block a user