129 lines
3.8 KiB
TypeScript
129 lines
3.8 KiB
TypeScript
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");
|
|
});
|
|
});
|