fix: paste styles shortcut (#4886)

Co-authored-by: Aakansha Doshi <aakansha1216@gmail.com>
This commit is contained in:
David Luzar 2022-03-09 10:59:44 +01:00 committed by GitHub
parent 1849ff6ee2
commit 20de06ef50
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
6 changed files with 99 additions and 31 deletions

View File

@ -7,7 +7,7 @@ import { distributeElements, Distribution } from "../disitrubte";
import { getNonDeletedElements } from "../element"; import { getNonDeletedElements } from "../element";
import { ExcalidrawElement } from "../element/types"; import { ExcalidrawElement } from "../element/types";
import { t } from "../i18n"; import { t } from "../i18n";
import { CODES } from "../keys"; import { CODES, KEYS } from "../keys";
import { getSelectedElements, isSomeElementSelected } from "../scene"; import { getSelectedElements, isSomeElementSelected } from "../scene";
import { AppState } from "../types"; import { AppState } from "../types";
import { arrayToMap, getShortcutKey } from "../utils"; import { arrayToMap, getShortcutKey } from "../utils";
@ -49,7 +49,8 @@ export const distributeHorizontally = register({
commitToHistory: true, commitToHistory: true,
}; };
}, },
keyTest: (event) => event.altKey && event.code === CODES.H, keyTest: (event) =>
!event[KEYS.CTRL_OR_CMD] && event.altKey && event.code === CODES.H,
PanelComponent: ({ elements, appState, updateData }) => ( PanelComponent: ({ elements, appState, updateData }) => (
<ToolButton <ToolButton
hidden={!enableActionGroup(elements, appState)} hidden={!enableActionGroup(elements, appState)}
@ -77,7 +78,8 @@ export const distributeVertically = register({
commitToHistory: true, commitToHistory: true,
}; };
}, },
keyTest: (event) => event.altKey && event.code === CODES.V, keyTest: (event) =>
!event[KEYS.CTRL_OR_CMD] && event.altKey && event.code === CODES.V,
PanelComponent: ({ elements, appState, updateData }) => ( PanelComponent: ({ elements, appState, updateData }) => (
<ToolButton <ToolButton
hidden={!enableActionGroup(elements, appState)} hidden={!enableActionGroup(elements, appState)}

View File

@ -0,0 +1,71 @@
import ExcalidrawApp from "../excalidraw-app";
import { t } from "../i18n";
import { CODES } from "../keys";
import { API } from "../tests/helpers/api";
import { Keyboard, Pointer, UI } from "../tests/helpers/ui";
import { fireEvent, render, screen } from "../tests/test-utils";
import { copiedStyles } from "./actionStyles";
const { h } = window;
const mouse = new Pointer("mouse");
describe("actionStyles", () => {
beforeEach(async () => {
await render(<ExcalidrawApp />);
});
it("should copy & paste styles via keyboard", () => {
UI.clickTool("rectangle");
mouse.down(10, 10);
mouse.up(20, 20);
UI.clickTool("rectangle");
mouse.down(10, 10);
mouse.up(20, 20);
// Change some styles of second rectangle
UI.clickLabeledElement("Stroke");
UI.clickLabeledElement(t("colors.c92a2a"));
UI.clickLabeledElement("Background");
UI.clickLabeledElement(t("colors.e64980"));
// Fill style
fireEvent.click(screen.getByTitle("Cross-hatch"));
// Stroke width
fireEvent.click(screen.getByTitle("Bold"));
// Stroke style
fireEvent.click(screen.getByTitle("Dotted"));
// Roughness
fireEvent.click(screen.getByTitle("Cartoonist"));
// Opacity
fireEvent.change(screen.getByLabelText("Opacity"), {
target: { value: "60" },
});
mouse.reset();
API.setSelectedElements([h.elements[1]]);
Keyboard.withModifierKeys({ ctrl: true, alt: true }, () => {
Keyboard.codeDown(CODES.C);
});
const secondRect = JSON.parse(copiedStyles);
expect(secondRect.id).toBe(h.elements[1].id);
mouse.reset();
// Paste styles to first rectangle
API.setSelectedElements([h.elements[0]]);
Keyboard.withModifierKeys({ ctrl: true, alt: true }, () => {
Keyboard.codeDown(CODES.V);
});
const firstRect = API.getSelectedElement();
expect(firstRect.id).toBe(h.elements[0].id);
expect(firstRect.strokeColor).toBe("#c92a2a");
expect(firstRect.backgroundColor).toBe("#e64980");
expect(firstRect.fillStyle).toBe("cross-hatch");
expect(firstRect.strokeWidth).toBe(2); // Bold: 2
expect(firstRect.strokeStyle).toBe("dotted");
expect(firstRect.roughness).toBe(2); // Cartoonist: 2
expect(firstRect.opacity).toBe(60);
});
});

View File

@ -91,6 +91,9 @@ export class ActionManager implements ActionsManagerInterface {
); );
if (data.length !== 1) { if (data.length !== 1) {
if (data.length > 1) {
console.warn("Canceling as multiple actions match this shortcut", data);
}
return false; return false;
} }

View File

@ -40,14 +40,6 @@ const queryContextMenu = () => {
return GlobalTestState.renderResult.container.querySelector(".context-menu"); return GlobalTestState.renderResult.container.querySelector(".context-menu");
}; };
const clickLabeledElement = (label: string) => {
const element = document.querySelector(`[aria-label='${label}']`);
if (!element) {
throw new Error(`No labeled element found: ${label}`);
}
fireEvent.click(element);
};
// Unmount ReactDOM from root // Unmount ReactDOM from root
ReactDOM.unmountComponentAtNode(document.getElementById("root")!); ReactDOM.unmountComponentAtNode(document.getElementById("root")!);
@ -312,10 +304,10 @@ describe("contextMenu element", () => {
mouse.up(20, 20); mouse.up(20, 20);
// Change some styles of second rectangle // Change some styles of second rectangle
clickLabeledElement("Stroke"); UI.clickLabeledElement("Stroke");
clickLabeledElement(t("colors.c92a2a")); UI.clickLabeledElement(t("colors.c92a2a"));
clickLabeledElement("Background"); UI.clickLabeledElement("Background");
clickLabeledElement(t("colors.e64980")); UI.clickLabeledElement(t("colors.e64980"));
// Fill style // Fill style
fireEvent.click(screen.getByTitle("Cross-hatch")); fireEvent.click(screen.getByTitle("Cross-hatch"));
// Stroke width // Stroke width

View File

@ -221,6 +221,14 @@ export class UI {
fireEvent.click(GlobalTestState.renderResult.getByToolName(toolName)); fireEvent.click(GlobalTestState.renderResult.getByToolName(toolName));
}; };
static clickLabeledElement = (label: string) => {
const element = document.querySelector(`[aria-label='${label}']`);
if (!element) {
throw new Error(`No labeled element found: ${label}`);
}
fireEvent.click(element);
};
/** /**
* Creates an Excalidraw element, and returns a proxy that wraps it so that * Creates an Excalidraw element, and returns a proxy that wraps it so that
* accessing props will return the latest ones from the object existing in * accessing props will return the latest ones from the object existing in

View File

@ -26,14 +26,6 @@ const mouse = new Pointer("mouse");
const finger1 = new Pointer("touch", 1); const finger1 = new Pointer("touch", 1);
const finger2 = new Pointer("touch", 2); const finger2 = new Pointer("touch", 2);
const clickLabeledElement = (label: string) => {
const element = document.querySelector(`[aria-label='${label}']`);
if (!element) {
throw new Error(`No labeled element found: ${label}`);
}
fireEvent.click(element);
};
/** /**
* This is always called at the end of your test, so usually you don't need to call it. * This is always called at the end of your test, so usually you don't need to call it.
* However, if you have a long test, you might want to call it during the test so it's easier * However, if you have a long test, you might want to call it during the test so it's easier
@ -168,10 +160,10 @@ describe("regression tests", () => {
mouse.down(10, 10); mouse.down(10, 10);
mouse.up(10, 10); mouse.up(10, 10);
clickLabeledElement("Background"); UI.clickLabeledElement("Background");
clickLabeledElement(t("colors.fa5252")); UI.clickLabeledElement(t("colors.fa5252"));
clickLabeledElement("Stroke"); UI.clickLabeledElement("Stroke");
clickLabeledElement(t("colors.5f3dc4")); UI.clickLabeledElement(t("colors.5f3dc4"));
expect(API.getSelectedElement().backgroundColor).toBe("#fa5252"); expect(API.getSelectedElement().backgroundColor).toBe("#fa5252");
expect(API.getSelectedElement().strokeColor).toBe("#5f3dc4"); expect(API.getSelectedElement().strokeColor).toBe("#5f3dc4");
}); });
@ -952,8 +944,8 @@ describe("regression tests", () => {
UI.clickTool("rectangle"); UI.clickTool("rectangle");
// change background color since default is transparent // change background color since default is transparent
// and transparent elements can't be selected by clicking inside of them // and transparent elements can't be selected by clicking inside of them
clickLabeledElement("Background"); UI.clickLabeledElement("Background");
clickLabeledElement(t("colors.fa5252")); UI.clickLabeledElement(t("colors.fa5252"));
mouse.down(); mouse.down();
mouse.up(1000, 1000); mouse.up(1000, 1000);
@ -1059,8 +1051,8 @@ describe("regression tests", () => {
mouse.up(10, 10); mouse.up(10, 10);
expect(screen.queryByText(/fill/i)).toBeNull(); expect(screen.queryByText(/fill/i)).toBeNull();
clickLabeledElement("Background"); UI.clickLabeledElement("Background");
clickLabeledElement(t("colors.fa5252")); UI.clickLabeledElement(t("colors.fa5252"));
// select rectangle // select rectangle
mouse.reset(); mouse.reset();
mouse.click(); mouse.click();