mirror of
https://github.com/MBombeck/HealthLog.git
synced 2026-11-14 06:40:50 +00:00
fix(coach): keep the answer details open on Enter and Space
Focus opens the details tooltip, and the click that Enter or Space then fires toggled it shut again. A click with no pointer-down before it is a keyboard activation and now always opens; a tap or a mouse click still toggles, and Escape closes it.
This commit is contained in:
1 parent
11b6119482
commit
b2c188bc46
3 files changed
+44
-10
No files matched your search
@@ -50,7 +50,11 @@ vi.mock("../read-aloud", async (importOriginal) => {
|
||||
});
|
||||
|
||||
import { ChatBubble, areChatBubblePropsEqual } from "../chat-bubble";
|
||||
import { answerInfoLines, messageTimeText } from "../message-actions";
|
||||
import {
|
||||
answerInfoLines,
|
||||
answerInfoOpenAfterClick,
|
||||
messageTimeText,
|
||||
} from "../message-actions";
|
||||
|
||||
function render(node: React.ReactNode, locale: "en" | "de" = "en") {
|
||||
return renderToStaticMarkup(
|
||||
@@ -302,3 +306,15 @@ describe("answerInfoLines", () => {
|
||||
]);
|
||||
});
|
||||
});
|
||||
|
||||
describe("answerInfoOpenAfterClick", () => {
|
||||
it("opens on Enter or Space, never closes, though focus opened it already", () => {
|
||||
// A keyboard activation fires no pointer-down.
|
||||
expect(answerInfoOpenAfterClick(null)).toBe(true);
|
||||
});
|
||||
|
||||
it("toggles on a tap or a click from the state at pointer-down", () => {
|
||||
expect(answerInfoOpenAfterClick(false)).toBe(true);
|
||||
expect(answerInfoOpenAfterClick(true)).toBe(false);
|
||||
});
|
||||
});
|
||||
@@ -180,15 +180,28 @@ function TryAgainButton({ onRegenerate }: { onRegenerate: () => void }) {
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Whether the details tooltip is open after a click on its icon, given its
|
||||
* state when the pointer went down (`null`: no pointer went down, so Enter
|
||||
* or Space activated the button). A tap or a mouse click toggles from that
|
||||
* state, before the primitive's own pointer-down close ran. A keyboard
|
||||
* activation always opens: focus has already opened the tooltip, and a
|
||||
* toggle would close it on Enter. Escape is the keyboard's way out.
|
||||
*/
|
||||
export function answerInfoOpenAfterClick(
|
||||
openAtPointerDown: boolean | null,
|
||||
): boolean {
|
||||
return openAtPointerDown === null ? true : !openAtPointerDown;
|
||||
}
|
||||
|
||||
/**
|
||||
* The model and token count of an answer, behind an info icon. A tooltip,
|
||||
* opened the three ways a reader can reach it: hovering the icon, focusing
|
||||
* it from the keyboard, and tapping it on a touch screen, where there is no
|
||||
* hover. A second tap, a tap elsewhere or Escape closes it.
|
||||
*
|
||||
* The tap is handled here because the tooltip primitive closes on every
|
||||
* click: the click toggles from the state the tooltip had when the pointer
|
||||
* went down, before the primitive's own pointer-down close ran.
|
||||
* The click is handled here because the tooltip primitive closes on every
|
||||
* click (`answerInfoOpenAfterClick` decides instead).
|
||||
*/
|
||||
function AnswerInfoButton({ lines }: { lines: string[] }) {
|
||||
const { t } = useTranslations();
|
||||
@@ -210,9 +223,8 @@ function AnswerInfoButton({ lines }: { lines: string[] }) {
|
||||
onClick={(event) => {
|
||||
// Keep the primitive from closing it again on this click.
|
||||
event.preventDefault();
|
||||
const was = openAtPointerDown.current;
|
||||
setOpen(answerInfoOpenAfterClick(openAtPointerDown.current));
|
||||
openAtPointerDown.current = null;
|
||||
setOpen(was === null ? (current) => !current : !was);
|
||||
}}
|
||||
>
|
||||
<Info className="size-4" aria-hidden="true" />
|
||||
|
||||
Reference in new issue
Block a user