feat(FN-1358): replace text input with always-dropdown in MessageComposer

- Replace text input with always-open dropdown in MessageComposer component
- Update MessageComposer tests to cover dropdown behavior
- Remove .message-composer-input from shared CSS rule
This commit is contained in:
gsxdsm
2026-04-09 16:35:41 -07:00
parent 704f262282
commit 69a4507113
3 changed files with 36 additions and 31 deletions

View File

@@ -19,6 +19,8 @@ interface MessageComposerProps {
onCancel: () => void;
/** Toast notification callback */
addToast?: (msg: string, type?: "success" | "error") => void;
/** Loading state for agents (shows placeholder) */
isLoadingAgents?: boolean;
}
const MAX_CONTENT_LENGTH = 2000;
@@ -32,6 +34,7 @@ export function MessageComposer({
onSend,
onCancel,
addToast,
isLoadingAgents = false,
}: MessageComposerProps) {
const [toId, setToId] = useState(recipient?.id ?? "");
const [toType, setToType] = useState<ParticipantType>(recipient?.type ?? "agent");
@@ -94,32 +97,23 @@ export function MessageComposer({
<label className="message-composer-label" htmlFor="message-recipient">
To:
</label>
{agents.length > 0 ? (
<select
id="message-recipient"
className="message-composer-select"
value={toId}
onChange={(e) => handleAgentSelect(e.target.value)}
data-testid="message-composer-recipient"
>
<option value="">Select agent</option>
{agents.map((agent) => (
<option key={agent.id} value={agent.id}>
{agent.name || agent.id}
</option>
))}
</select>
) : (
<input
id="message-recipient"
className="message-composer-input"
type="text"
placeholder="Recipient ID"
value={toId}
onChange={(e) => setToId(e.target.value)}
data-testid="message-composer-recipient"
/>
)}
<select
id="message-recipient"
className="message-composer-select"
value={toId}
onChange={(e) => handleAgentSelect(e.target.value)}
disabled={isLoadingAgents || agents.length === 0}
data-testid="message-composer-recipient"
>
<option value="">
{isLoadingAgents ? "Loading agents…" : agents.length === 0 ? "No agents available" : "Select agent…"}
</option>
{agents.map((agent) => (
<option key={agent.id} value={agent.id}>
{agent.name || agent.id}
</option>
))}
</select>
</div>
)}

View File

@@ -69,10 +69,22 @@ describe("MessageComposer", () => {
expect(select.tagName).toBe("SELECT");
});
it("shows text input when no agents provided", () => {
it("disables recipient select when agents list is empty", () => {
render(<MessageComposer {...defaultProps} />);
const input = screen.getByTestId("message-composer-recipient");
expect(input.tagName).toBe("INPUT");
const select = screen.getByTestId("message-composer-recipient");
expect(select).toBeDefined();
expect(select.tagName).toBe("SELECT");
expect(select.hasAttribute("disabled")).toBe(true);
expect(select.textContent).toContain("No agents available");
});
it("shows loading state in recipient select", () => {
render(<MessageComposer {...defaultProps} isLoadingAgents={true} />);
const select = screen.getByTestId("message-composer-recipient");
expect(select).toBeDefined();
expect(select.tagName).toBe("SELECT");
expect(select.hasAttribute("disabled")).toBe(true);
expect(select.textContent).toContain("Loading agents…");
});
it("disables send button when content is empty", () => {