Expose builder chip selection beyond color, raise unselected contrast, and make the five-value limit visible.

Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
adilallo
2026-09-10 09:04:06 -06:00
co-authored by Cursor
parent 95e1937419
commit 64a2ef5a00
16 changed files with 266 additions and 51 deletions
@@ -125,4 +125,32 @@ describe("ApplicableScopeField behavior", () => {
expect(screen.queryByAltText("Help")).not.toBeInTheDocument();
});
it("exposes selected state on chips and keeps unselected chips enabled", () => {
renderWithProviders(<ApplicableScopeField {...baseProps} />);
const selected = screen.getByRole("button", { name: /Deselect Finance/i });
const unselected = screen.getByRole("button", {
name: /Select Operations/i,
});
expect(selected).toHaveAttribute("aria-pressed", "true");
expect(selected).toBeEnabled();
expect(unselected).toHaveAttribute("aria-pressed", "false");
expect(unselected).toBeEnabled();
});
it("uses a real disabled state when readOnly", () => {
renderWithProviders(<ApplicableScopeField {...baseProps} readOnly />);
expect(
screen.getByRole("button", { name: /Deselect Finance/i }),
).toBeDisabled();
expect(
screen.getByRole("button", { name: /Select Operations/i }),
).toBeDisabled();
expect(
screen.queryByRole("button", { name: /Add Applicable Scope/i }),
).not.toBeInTheDocument();
});
});
+18 -1
View File
@@ -1,8 +1,11 @@
import { describe } from "vitest";
import { describe, it, expect } from "vitest";
import { screen } from "@testing-library/react";
import "@testing-library/jest-dom/vitest";
import {
componentTestSuite,
type ComponentTestSuiteConfig,
} from "../utils/componentTestSuite";
import { renderWithProviders as render } from "../utils/test-utils";
import Chip from "../../app/components/controls/Chip";
type Props = React.ComponentProps<typeof Chip>;
@@ -30,4 +33,18 @@ const config: ComponentTestSuiteConfig<Props> = {
describe("Chip", () => {
componentTestSuite<Props>(config);
it("exposes aria-pressed false when unselected", () => {
render(<Chip label="Worker cooperative" state="unselected" />);
const chip = screen.getByRole("button", { name: "Worker cooperative" });
expect(chip).toHaveAttribute("aria-pressed", "false");
expect(chip.querySelector("svg")).toBeNull();
});
it("exposes aria-pressed and a check mark when selected", () => {
render(<Chip label="Worker cooperative" state="selected" />);
const chip = screen.getByRole("button", { name: "Worker cooperative" });
expect(chip).toHaveAttribute("aria-pressed", "true");
expect(chip.querySelector("svg[aria-hidden]")).not.toBeNull();
});
});
@@ -0,0 +1,16 @@
import { describe, it, expect } from "vitest";
import { screen } from "@testing-library/react";
import "@testing-library/jest-dom/vitest";
import { renderWithProviders } from "../utils/test-utils";
import { CommunityStructureSelectScreen } from "../../app/(app)/create/screens/select/CommunityStructureSelectScreen";
describe("CommunityStructureSelectScreen", () => {
it("does not render dummy help icons", () => {
renderWithProviders(<CommunityStructureSelectScreen />);
expect(screen.getByText("Organization Type")).toBeInTheDocument();
expect(screen.getByText("Scale")).toBeInTheDocument();
expect(screen.getByText("Maturity")).toBeInTheDocument();
expect(screen.queryByAltText("Help")).not.toBeInTheDocument();
});
});
@@ -389,4 +389,38 @@ describe("CoreValuesSelectScreen", () => {
expect(countCustomChips(CUSTOM_LABEL)).toBe(1);
});
});
describe("five-value limit", () => {
async function addPresetValue(label: string) {
fireEvent.click(screen.getByText(label));
const dialog = await screen.findByRole("dialog");
fireEvent.click(within(dialog).getByRole("button", { name: "Add Value" }));
await waitFor(() => {
expect(screen.queryByRole("dialog")).not.toBeInTheDocument();
});
}
it("shows a counter, disables remaining chips, and announces the limit", async () => {
renderWithProviders(<CoreValuesSelectScreen />);
expect(screen.getByText("0 of 5")).toBeInTheDocument();
await addPresetValue("Accessibility");
await addPresetValue("Accountability");
await addPresetValue("Adaptability");
await addPresetValue("Agency");
await addPresetValue("Altruism");
expect(screen.getByText("5 of 5")).toBeInTheDocument();
expect(
screen.getByText(
"Five of five values selected. Remaining values are unavailable until one is removed.",
),
).toBeInTheDocument();
expect(
screen.getByRole("button", { name: "Anti-oppression" }),
).toBeDisabled();
expect(screen.getByRole("button", { name: "Add value" })).toBeDisabled();
expect(screen.getByRole("button", { name: /^add$/i })).toBeDisabled();
});
});
});
+38
View File
@@ -123,6 +123,44 @@ describe("MultiSelect behaviour specifics", () => {
expect(helpIcon).toBeInTheDocument();
});
it("does not show a help icon by default", () => {
render(<MultiSelect options={defaultChipOptions} label="Test Label" />);
expect(screen.queryByAltText("Help")).not.toBeInTheDocument();
});
it("disables leftover chips and add when maxSelections is reached", async () => {
const handleChipClick = vi.fn();
const handleAddClick = vi.fn();
const options = [
{ id: "1", label: "One", state: "selected" as const },
{ id: "2", label: "Two", state: "selected" as const },
{ id: "3", label: "Three", state: "unselected" as const },
];
render(
<MultiSelect
options={options}
onChipClick={handleChipClick}
onAddClick={handleAddClick}
addButton
addButtonText="Add option"
maxSelections={2}
selectionCountText="2 of 2"
limitReachedAnnouncement="Limit reached"
/>,
);
expect(screen.getByText("2 of 2")).toBeInTheDocument();
expect(screen.getByText("Limit reached")).toBeInTheDocument();
expect(screen.getByRole("button", { name: "One" })).toBeEnabled();
expect(screen.getByRole("button", { name: "Three" })).toBeDisabled();
expect(screen.getByRole("button", { name: "Add option" })).toBeDisabled();
await userEvent.click(screen.getByRole("button", { name: "Three" }));
await userEvent.click(screen.getByRole("button", { name: "Add option" }));
expect(handleChipClick).not.toHaveBeenCalled();
expect(handleAddClick).not.toHaveBeenCalled();
});
it("renders add button text when provided", () => {
render(
<MultiSelect