fix: paste styles shortcut (#4886)
Co-authored-by: Aakansha Doshi <aakansha1216@gmail.com>
This commit is contained in:
parent
1849ff6ee2
commit
20de06ef50
@ -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)}
|
||||||
|
71
src/actions/actionStyles.test.tsx
Normal file
71
src/actions/actionStyles.test.tsx
Normal 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);
|
||||||
|
});
|
||||||
|
});
|
@ -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;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -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
|
||||||
|
@ -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
|
||||||
|
@ -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();
|
||||||
|
Loading…
x
Reference in New Issue
Block a user