FN-7613: fix confirm Yes/No button selected-state visibility
Fix confirm Yes/No selected-state visibility in chat questions and add aria-pressed accessibility support. - Strengthen CSS specificity for .chat-question-response__confirm--selected so the selected style beats the global .btn/.btn:hover rules, using token-driven --cta-* colors for light/dark themes. - Add dedicated hover and focus-visible states for the selected confirm button. - Add aria-pressed to the Yes/No confirm buttons so assistive tech reflects the same selected state. - Add regression tests covering the selected visual/aria state. - Add changeset (patch) documenting the fix. Files changed: .changeset/fn-7613-confirm-selected-state.md | 7 ++++ .../app/components/ChatQuestionResponse.css | 40 ++++++++++++++++++++-- .../app/components/ChatQuestionResponse.tsx | 8 +++++ .../__tests__/ChatQuestionResponse.test.tsx | 26 ++++++++++++++ 4 files changed, 79 insertions(+), 2 deletions(-) Fusion-Task-Id: FN-7613 Fusion-Task-Lineage: d648044e-ab30-4542-9402-a7bfbfe6563e Co-authored-by: Fusion (runfusion.ai) <noreply@runfusion.ai>
This commit is contained in:
7
.changeset/fn-7613-confirm-selected-state.md
Normal file
7
.changeset/fn-7613-confirm-selected-state.md
Normal file
@@ -0,0 +1,7 @@
|
||||
---
|
||||
"@runfusion/fusion": patch
|
||||
---
|
||||
|
||||
summary: Yes/No chat question buttons now show a clear selected state after clicking.
|
||||
category: fix
|
||||
dev: Strengthened `.chat-question-response__confirm--selected` CSS specificity (compound selector + dedicated hover/focus-visible rules) so the CTA-token selected fill/border beats the global `.btn`/`.btn:hover` rules; added `aria-pressed` and a regression test asserting the selected class toggles correctly between Yes/No.
|
||||
@@ -102,12 +102,48 @@
|
||||
}
|
||||
|
||||
.chat-question-response__option:hover,
|
||||
.chat-question-response__option--selected,
|
||||
.chat-question-response__confirm--selected {
|
||||
.chat-question-response__option--selected {
|
||||
border-color: var(--accent);
|
||||
background: color-mix(in srgb, var(--accent) 12%, var(--card));
|
||||
}
|
||||
|
||||
/*
|
||||
FNXC:ChatQuestionResponse 2026-07-05-00:00:
|
||||
The confirm Yes/No buttons also carry the global `.btn` class, whose base
|
||||
background and (especially) `.btn:hover` rule (specificity 0,2,0) visually
|
||||
swamped the old single-class `.chat-question-response__confirm--selected`
|
||||
rule (0,1,0), so a clicked answer never looked selected. Use a compound
|
||||
selector (`.chat-question-response__confirm.chat-question-response__confirm--selected`,
|
||||
specificity 0,2,0) plus a dedicated `:hover` override so the selected state
|
||||
beats `.btn`/`.btn:hover` in every interaction state, and keep everything
|
||||
token-driven (`--cta-*` / `--accent` via color-mix) so light/dark themes and
|
||||
the mobile column layout stay correct without hardcoded colors.
|
||||
*/
|
||||
.chat-question-response__confirm {
|
||||
border-color: var(--border);
|
||||
background: var(--card);
|
||||
color: var(--text);
|
||||
font-weight: 500;
|
||||
}
|
||||
|
||||
.chat-question-response__confirm.chat-question-response__confirm--selected {
|
||||
border-color: var(--cta-border);
|
||||
background: color-mix(in srgb, var(--cta-bg) 55%, var(--card));
|
||||
color: var(--cta-text);
|
||||
font-weight: 700;
|
||||
box-shadow: var(--cta-glow), inset 0 0 0 var(--btn-border-width) var(--cta-border);
|
||||
}
|
||||
|
||||
.chat-question-response__confirm.chat-question-response__confirm--selected:hover {
|
||||
background: color-mix(in srgb, var(--cta-bg-hover) 60%, var(--card));
|
||||
border-color: var(--cta-border-hover);
|
||||
color: var(--cta-text);
|
||||
}
|
||||
|
||||
.chat-question-response__confirm.chat-question-response__confirm--selected:focus-visible {
|
||||
box-shadow: var(--focus-ring-strong), var(--cta-glow);
|
||||
}
|
||||
|
||||
.chat-question-response__option input {
|
||||
margin: calc(var(--space-xs) / 2) 0 0;
|
||||
accent-color: var(--accent);
|
||||
|
||||
@@ -168,11 +168,18 @@ function QuestionControls({
|
||||
if (question.type === "confirm") {
|
||||
return (
|
||||
<div className="chat-question-response__confirm-group" role="group" aria-label={question.question}>
|
||||
{/*
|
||||
FNXC:ChatQuestionResponse 2026-07-05-00:00:
|
||||
Expose the confirm selection to assistive tech via aria-pressed so
|
||||
screen reader users get the same clear selected/unselected signal
|
||||
the strengthened CSS now provides visually.
|
||||
*/}
|
||||
<button
|
||||
type="button"
|
||||
className={`btn chat-question-response__confirm${value === true ? " chat-question-response__confirm--selected" : ""}`}
|
||||
data-testid={`chat-question-response-option-${question.id}-yes`}
|
||||
disabled={disabled}
|
||||
aria-pressed={value === true}
|
||||
onClick={() => setQuestionAnswer(question.id, true)}
|
||||
>
|
||||
{t("chat.questionConfirmYes", "Yes")}
|
||||
@@ -182,6 +189,7 @@ function QuestionControls({
|
||||
className={`btn chat-question-response__confirm${value === false ? " chat-question-response__confirm--selected" : ""}`}
|
||||
data-testid={`chat-question-response-option-${question.id}-no`}
|
||||
disabled={disabled}
|
||||
aria-pressed={value === false}
|
||||
onClick={() => setQuestionAnswer(question.id, false)}
|
||||
>
|
||||
{t("chat.questionConfirmNo", "No")}
|
||||
|
||||
@@ -60,6 +60,32 @@ describe("ChatQuestionResponse", () => {
|
||||
}
|
||||
});
|
||||
|
||||
it("marks the clicked confirm option as visually selected and moves the marker on re-click (FN-7613)", async () => {
|
||||
const user = userEvent.setup();
|
||||
render(<ChatQuestionResponse parsed={parsed} onSubmit={vi.fn()} />);
|
||||
|
||||
const yesButton = screen.getByTestId("chat-question-response-option-confirm-yes");
|
||||
const noButton = screen.getByTestId("chat-question-response-option-confirm-no");
|
||||
|
||||
// Neither selected before any click.
|
||||
expect(yesButton).not.toHaveClass("chat-question-response__confirm--selected");
|
||||
expect(noButton).not.toHaveClass("chat-question-response__confirm--selected");
|
||||
expect(yesButton).toHaveAttribute("aria-pressed", "false");
|
||||
expect(noButton).toHaveAttribute("aria-pressed", "false");
|
||||
|
||||
await user.click(yesButton);
|
||||
expect(yesButton).toHaveClass("chat-question-response__confirm--selected");
|
||||
expect(noButton).not.toHaveClass("chat-question-response__confirm--selected");
|
||||
expect(yesButton).toHaveAttribute("aria-pressed", "true");
|
||||
expect(noButton).toHaveAttribute("aria-pressed", "false");
|
||||
|
||||
await user.click(noButton);
|
||||
expect(noButton).toHaveClass("chat-question-response__confirm--selected");
|
||||
expect(yesButton).not.toHaveClass("chat-question-response__confirm--selected");
|
||||
expect(noButton).toHaveAttribute("aria-pressed", "true");
|
||||
expect(yesButton).toHaveAttribute("aria-pressed", "false");
|
||||
});
|
||||
|
||||
it("supports compact mode", () => {
|
||||
render(<ChatQuestionResponse parsed={{ questions: [parsed.questions[0]!] }} compact onSubmit={vi.fn()} />);
|
||||
expect(screen.getByTestId("chat-question-response")).toHaveClass("chat-question-response--compact");
|
||||
|
||||
Reference in New Issue
Block a user