snapshot of https://github.com/excalidraw/excalidraw.git (master, 2026-09-16; blobs > 131072B omitted)
Some checks failed
Tests / test (push) Failing after 15s
Some checks failed
Tests / test (push) Failing after 15s
This commit is contained in:
128
packages/excalidraw/tests/colorPickerHotkeys.test.ts
Normal file
128
packages/excalidraw/tests/colorPickerHotkeys.test.ts
Normal file
@@ -0,0 +1,128 @@
|
||||
import { COLOR_PALETTE } from "@excalidraw/common";
|
||||
|
||||
import type { ColorPaletteCustom } from "@excalidraw/common";
|
||||
|
||||
import { colorPickerKeyNavHandler } from "../components/ColorPicker/keyboardNavHandlers";
|
||||
|
||||
const handlerArgs = (
|
||||
key: string,
|
||||
palette: ColorPaletteCustom,
|
||||
onChange: (color: string) => void,
|
||||
excludedColors?: readonly string[],
|
||||
) => ({
|
||||
event: {
|
||||
key,
|
||||
ctrlKey: false,
|
||||
metaKey: false,
|
||||
shiftKey: false,
|
||||
preventDefault: () => {},
|
||||
stopPropagation: () => {},
|
||||
} as unknown as React.KeyboardEvent,
|
||||
activeColorPickerSection: null,
|
||||
palette,
|
||||
color: null,
|
||||
onChange,
|
||||
customColors: [],
|
||||
setActiveColorPickerSection: () => {},
|
||||
updateData: () => {},
|
||||
activeShade: 1,
|
||||
onEyeDropperToggle: () => {},
|
||||
onEscape: () => {},
|
||||
excludedColors,
|
||||
});
|
||||
|
||||
describe("color picker hotkeys", () => {
|
||||
it("reports an excluded (hidden) entry's hotkey as handled without selecting it", () => {
|
||||
// regression: returning "unhandled" let the key escape the modal and hit
|
||||
// global shortcuts (`q` toggles the tool lock; see the integration
|
||||
// regression in bucketFill.test.tsx)
|
||||
const onChange = vi.fn();
|
||||
const palette = {
|
||||
transparent: COLOR_PALETTE.transparent,
|
||||
red: "#ff0000",
|
||||
} as ColorPaletteCustom;
|
||||
|
||||
const handled = colorPickerKeyNavHandler(
|
||||
handlerArgs("q", palette, onChange, [COLOR_PALETTE.transparent]),
|
||||
);
|
||||
|
||||
expect(handled).toBe(true);
|
||||
expect(onChange).not.toHaveBeenCalled();
|
||||
});
|
||||
|
||||
it("reports a hotkey past the end of a short custom palette as handled", () => {
|
||||
// a host-provided palette with fewer entries than hotkey bindings must
|
||||
// not leak the unbound keys either
|
||||
const onChange = vi.fn();
|
||||
const palette = { red: "#ff0000" } as ColorPaletteCustom;
|
||||
|
||||
// `b` is the 15th binding — far past the single palette entry
|
||||
const handled = colorPickerKeyNavHandler(
|
||||
handlerArgs("b", palette, onChange),
|
||||
);
|
||||
|
||||
expect(handled).toBe(true);
|
||||
expect(onChange).not.toHaveBeenCalled();
|
||||
});
|
||||
|
||||
it("still selects a present, non-excluded entry", () => {
|
||||
const onChange = vi.fn();
|
||||
const palette = {
|
||||
transparent: COLOR_PALETTE.transparent,
|
||||
red: "#ff0000",
|
||||
} as ColorPaletteCustom;
|
||||
|
||||
const handled = colorPickerKeyNavHandler(
|
||||
handlerArgs("w", palette, onChange, [COLOR_PALETTE.transparent]),
|
||||
);
|
||||
|
||||
expect(handled).toBe(true);
|
||||
expect(onChange).toHaveBeenCalledWith("#ff0000");
|
||||
});
|
||||
|
||||
it("steps over an excluded entry when navigating vertically", () => {
|
||||
// three rows of five; `transparent` sits in column 0 of row 1 and is
|
||||
// excluded, so vertical moves through that cell must skip it in the
|
||||
// arrow's direction (wrapping) instead of landing on the hidden swatch
|
||||
const palette = {
|
||||
transparent: COLOR_PALETTE.transparent,
|
||||
white: "#w0",
|
||||
gray: "#g0",
|
||||
black: "#k0",
|
||||
bronze: "#b0",
|
||||
red: "#r1",
|
||||
green: "#g1",
|
||||
blue: "#b1",
|
||||
yellow: "#y1",
|
||||
orange: "#o1",
|
||||
pink: "#r2",
|
||||
grape: "#g2",
|
||||
violet: "#b2",
|
||||
cyan: "#y2",
|
||||
teal: "#o2",
|
||||
} as unknown as ColorPaletteCustom;
|
||||
const excluded = [COLOR_PALETTE.transparent];
|
||||
|
||||
// up from row 2 / column 0 wraps past the excluded cell to row 3
|
||||
let onChange = vi.fn();
|
||||
expect(
|
||||
colorPickerKeyNavHandler({
|
||||
...handlerArgs("ArrowUp", palette, onChange, excluded),
|
||||
activeColorPickerSection: "baseColors",
|
||||
color: "#r1",
|
||||
}),
|
||||
).toBe(true);
|
||||
expect(onChange).toHaveBeenCalledWith("#r2");
|
||||
|
||||
// down from row 3 / column 0 wraps past the excluded cell to row 2
|
||||
onChange = vi.fn();
|
||||
expect(
|
||||
colorPickerKeyNavHandler({
|
||||
...handlerArgs("ArrowDown", palette, onChange, excluded),
|
||||
activeColorPickerSection: "baseColors",
|
||||
color: "#r2",
|
||||
}),
|
||||
).toBe(true);
|
||||
expect(onChange).toHaveBeenCalledWith("#r1");
|
||||
});
|
||||
});
|
||||
Reference in New Issue
Block a user