mirror of
https://github.com/bulwarkmail/webmail.git
synced 2026-10-31 00:12:39 +00:00
fix: let Enter press the tour's Skip and Back buttons, and mirror its arrow keys in right-to-left languages
This commit is contained in:
@@ -88,3 +88,43 @@ describe('tour targets', () => {
|
||||
expect(ids({ supportsContacts: false })).not.toContain('nav-contacts');
|
||||
});
|
||||
});
|
||||
|
||||
describe('tour keyboard', () => {
|
||||
function showStep() {
|
||||
anchors('<a data-tour="one" data-size></a>');
|
||||
render(<TourOverlay />);
|
||||
act(() => { vi.advanceTimersByTime(400); });
|
||||
}
|
||||
|
||||
it('lets Enter press the focused Skip button instead of going to the next step', () => {
|
||||
showStep();
|
||||
const skip = screen.getByRole('button', { name: 'tour.skip' });
|
||||
skip.focus();
|
||||
act(() => { fireEvent.keyDown(skip, { key: 'Enter' }); });
|
||||
expect(tour.nextStep).not.toHaveBeenCalled();
|
||||
});
|
||||
|
||||
it('still goes on with Enter when no button has the focus', () => {
|
||||
showStep();
|
||||
act(() => { fireEvent.keyDown(document.body, { key: 'Enter' }); });
|
||||
expect(tour.nextStep).toHaveBeenCalledTimes(1);
|
||||
});
|
||||
|
||||
it('leaves Enter and the arrows to a field being typed in', () => {
|
||||
showStep();
|
||||
const field = document.createElement('input');
|
||||
hosts[0].appendChild(field);
|
||||
act(() => { fireEvent.keyDown(field, { key: 'Enter' }); });
|
||||
act(() => { fireEvent.keyDown(field, { key: 'ArrowRight' }); });
|
||||
expect(tour.nextStep).not.toHaveBeenCalled();
|
||||
});
|
||||
|
||||
it('mirrors the arrows in right-to-left languages', () => {
|
||||
document.documentElement.setAttribute('dir', 'rtl');
|
||||
showStep();
|
||||
act(() => { fireEvent.keyDown(document.body, { key: 'ArrowLeft' }); });
|
||||
expect(tour.nextStep).toHaveBeenCalledTimes(1);
|
||||
act(() => { fireEvent.keyDown(document.body, { key: 'ArrowRight' }); });
|
||||
expect(tour.prevStep).toHaveBeenCalledTimes(1);
|
||||
});
|
||||
});
|
||||
|
||||
@@ -6,6 +6,8 @@ import { useTranslations } from "next-intl";
|
||||
import { useTour } from "./tour-provider";
|
||||
import { cn } from "@/lib/utils";
|
||||
import { useFocusTrap } from "@/hooks/use-focus-trap";
|
||||
import { isEditableEventTarget } from "@/lib/keyboard";
|
||||
import { isDocumentRTL } from "@/i18n/direction";
|
||||
|
||||
interface Rect {
|
||||
top: number;
|
||||
@@ -211,7 +213,7 @@ export function TourOverlay() {
|
||||
pendingTimerRef.current = null;
|
||||
}
|
||||
};
|
||||
}, [step, currentStep]); // eslint-disable-line react-hooks/exhaustive-deps
|
||||
}, [step, currentStep]);
|
||||
|
||||
// Recalculate on resize/scroll (debounced)
|
||||
useEffect(() => {
|
||||
@@ -232,14 +234,20 @@ export function TourOverlay() {
|
||||
};
|
||||
}, [visible, updatePosition]);
|
||||
|
||||
// Keyboard navigation
|
||||
// Keyboard navigation. Enter on a focused button (Skip, Back) and keys
|
||||
// typed into a field (the search step) are theirs, not the tour's.
|
||||
useEffect(() => {
|
||||
const handler = (e: KeyboardEvent) => {
|
||||
if (e.key === "ArrowRight" || e.key === "Enter") {
|
||||
const onControl = e.target instanceof HTMLElement && !!e.target.closest("button, a[href]");
|
||||
const typing = isEditableEventTarget(e);
|
||||
// The arrow pointing the way you read goes on: → in LTR, ← in RTL.
|
||||
const forward = isDocumentRTL() ? "ArrowLeft" : "ArrowRight";
|
||||
const back = isDocumentRTL() ? "ArrowRight" : "ArrowLeft";
|
||||
if ((e.key === forward && !typing) || (e.key === "Enter" && !onControl && !typing)) {
|
||||
e.preventDefault();
|
||||
e.stopPropagation();
|
||||
goNext();
|
||||
} else if (e.key === "ArrowLeft") {
|
||||
} else if (e.key === back && !typing) {
|
||||
e.preventDefault();
|
||||
e.stopPropagation();
|
||||
goPrev();
|
||||
|
||||
Reference in New Issue
Block a user