feat(HAI-105): polish dashboard UI styles and migrate inline styles to CSS

- Enhance CSS custom properties and add form control styling
- Polish settings layout, sidebar, and auth card styles
- Add checkbox styling, modal refinements, and migrate inline styles from SettingsModal
- Add structural test assertions for new CSS classes in SettingsModal
- Expand styles.css with ~170 new lines of maintainable, token-based CSS
This commit is contained in:
Dustin Byrne
2026-03-26 20:48:56 -04:00
parent 5e76a03287
commit a1e824015b
3 changed files with 237 additions and 32 deletions

View File

@@ -373,6 +373,69 @@ describe("SettingsModal", () => {
expect(addToast).toHaveBeenCalledWith("Logged out", "success");
});
it("auth status badges have proper class names", async () => {
(fetchAuthStatus as ReturnType<typeof vi.fn>).mockResolvedValueOnce({
providers: [
{ id: "anthropic", name: "Anthropic", authenticated: true },
{ id: "github", name: "GitHub", authenticated: false },
],
});
render(<SettingsModal onClose={onClose} addToast={addToast} />);
await waitFor(() => expect(fetchSettings).toHaveBeenCalled());
fireEvent.click(screen.getByText("Authentication"));
await waitFor(() => expect(fetchAuthStatus).toHaveBeenCalled());
const authBadge = screen.getByTestId("auth-status-anthropic");
expect(authBadge.className).toContain("auth-status-badge");
expect(authBadge.className).toContain("authenticated");
const unauthBadge = screen.getByTestId("auth-status-github");
expect(unauthBadge.className).toContain("auth-status-badge");
expect(unauthBadge.className).toContain("not-authenticated");
});
it("auth provider rows use auth-provider-row class", async () => {
render(<SettingsModal onClose={onClose} addToast={addToast} />);
await waitFor(() => expect(fetchSettings).toHaveBeenCalled());
fireEvent.click(screen.getByText("Authentication"));
await waitFor(() => expect(fetchAuthStatus).toHaveBeenCalled());
const providerRow = screen.getByText("Anthropic").closest(".auth-provider-row");
expect(providerRow).toBeTruthy();
});
it("model section renders select element", async () => {
render(<SettingsModal onClose={onClose} addToast={addToast} />);
await waitFor(() => expect(fetchSettings).toHaveBeenCalled());
fireEvent.click(screen.getByText("Model"));
await waitFor(() => expect(fetchModels).toHaveBeenCalled());
const select = screen.getByLabelText("Default Model") as HTMLSelectElement;
expect(select.tagName).toBe("SELECT");
});
it("checkbox labels use checkbox-label class", async () => {
render(<SettingsModal onClose={onClose} addToast={addToast} />);
await waitFor(() => expect(fetchSettings).toHaveBeenCalled());
fireEvent.click(screen.getByText("Scheduling"));
const label = screen.getByText("Serialize tasks with overlapping files");
expect(label.className).toContain("checkbox-label");
});
it("no inline style attributes remain on SettingsModal elements", async () => {
const { container } = render(<SettingsModal onClose={onClose} addToast={addToast} />);
await waitFor(() => expect(fetchSettings).toHaveBeenCalled());
// Check that no elements in the settings content have inline styles
const elementsWithStyle = container.querySelectorAll("[style]");
expect(elementsWithStyle.length).toBe(0);
});
it("shows loading state during login", async () => {
// Make loginProvider hang
(loginProvider as ReturnType<typeof vi.fn>).mockReturnValue(new Promise(() => {}));