fix: cmd/ctrl native browser behavior blocked in inputs (#4589)

* fix: cmd/ctrl native browser behavior blocked in inputs

* add basic test for fontSize increase/decrease via keyboard

* add tests for fontSize resizing via keyboard outside wysiwyg

* Update src/element/textWysiwyg.test.tsx

Co-authored-by: Aakansha Doshi <aakansha1216@gmail.com>

* Update src/tests/resize.test.tsx

Co-authored-by: Aakansha Doshi <aakansha1216@gmail.com>

* Update src/tests/resize.test.tsx

Co-authored-by: Aakansha Doshi <aakansha1216@gmail.com>

Co-authored-by: Aakansha Doshi <aakansha1216@gmail.com>
This commit is contained in:
David Luzar 2022-01-13 19:53:22 +01:00 committed by GitHub
parent dba71e358d
commit 1fd2fe56ee
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
6 changed files with 83 additions and 17 deletions

View File

@ -25,7 +25,7 @@ export const actionCut = register({
name: "cut", name: "cut",
perform: (elements, appState, data, app) => { perform: (elements, appState, data, app) => {
actionCopy.perform(elements, appState, data, app); actionCopy.perform(elements, appState, data, app);
return actionDeleteSelected.perform(elements, appState, data, app); return actionDeleteSelected.perform(elements, appState);
}, },
contextItemLabel: "labels.cut", contextItemLabel: "labels.cut",
keyTest: (event) => event[KEYS.CTRL_OR_CMD] && event.code === CODES.X, keyTest: (event) => event[KEYS.CTRL_OR_CMD] && event.code === CODES.X,

View File

@ -2,7 +2,9 @@ import { Action } from "./types";
export let actions: readonly Action[] = []; export let actions: readonly Action[] = [];
export const register = (action: Action): Action => { export const register = <T extends Action>(action: T) => {
actions = actions.concat(action); actions = actions.concat(action);
return action; return action as T & {
keyTest?: unknown extends T["keyTest"] ? never : T["keyTest"];
};
}; };

View File

@ -1649,10 +1649,7 @@ class App extends React.Component<AppProps, AppState> {
} }
if ( if (
(isWritableElement(event.target) && (isWritableElement(event.target) && event.key !== KEYS.ESCAPE) ||
event.key !== KEYS.ESCAPE &&
// handle cmd/ctrl-modifier shortcuts even inside inputs
!event[KEYS.CTRL_OR_CMD]) ||
// case: using arrows to move between buttons // case: using arrows to move between buttons
(isArrowKey(event.key) && isInputLike(event.target)) (isArrowKey(event.key) && isInputLike(event.target))
) { ) {
@ -1993,6 +1990,7 @@ class App extends React.Component<AppProps, AppState> {
}), }),
element, element,
excalidrawContainer: this.excalidrawContainerRef.current, excalidrawContainer: this.excalidrawContainerRef.current,
app: this,
}); });
// deselect all other elements when inserting text // deselect all other elements when inserting text
this.deselectElements(); this.deselectElements();

View File

@ -5,7 +5,10 @@ import { Keyboard, Pointer, UI } from "../tests/helpers/ui";
import { KEYS } from "../keys"; import { KEYS } from "../keys";
import { fireEvent } from "../tests/test-utils"; import { fireEvent } from "../tests/test-utils";
import { BOUND_TEXT_PADDING, FONT_FAMILY } from "../constants"; import { BOUND_TEXT_PADDING, FONT_FAMILY } from "../constants";
import { ExcalidrawTextElementWithContainer } from "./types"; import {
ExcalidrawTextElement,
ExcalidrawTextElementWithContainer,
} from "./types";
import * as textElementUtils from "./textElement"; import * as textElementUtils from "./textElement";
// Unmount ReactDOM from root // Unmount ReactDOM from root
ReactDOM.unmountComponentAtNode(document.getElementById("root")!); ReactDOM.unmountComponentAtNode(document.getElementById("root")!);
@ -16,12 +19,13 @@ const mouse = new Pointer("mouse");
describe("textWysiwyg", () => { describe("textWysiwyg", () => {
describe("Test unbounded text", () => { describe("Test unbounded text", () => {
let textarea: HTMLTextAreaElement; let textarea: HTMLTextAreaElement;
let textElement: ExcalidrawTextElement;
beforeEach(async () => { beforeEach(async () => {
await render(<ExcalidrawApp />); await render(<ExcalidrawApp />);
const element = UI.createElement("text"); textElement = UI.createElement("text");
mouse.clickOn(element); mouse.clickOn(textElement);
textarea = document.querySelector( textarea = document.querySelector(
".excalidraw-textEditorContainer > textarea", ".excalidraw-textEditorContainer > textarea",
)!; )!;
@ -171,7 +175,30 @@ describe("textWysiwyg", () => {
expect(textarea.value).toEqual(`Line#1\nLine#2`); expect(textarea.value).toEqual(`Line#1\nLine#2`);
}); });
it("should resize text via shortcuts while in wysiwyg", () => {
textarea.value = "abc def";
const origFontSize = textElement.fontSize;
textarea.dispatchEvent(
new KeyboardEvent("keydown", {
key: KEYS.CHEVRON_RIGHT,
ctrlKey: true,
shiftKey: true,
}),
);
expect(textElement.fontSize).toBe(origFontSize * 1.1);
textarea.dispatchEvent(
new KeyboardEvent("keydown", {
key: KEYS.CHEVRON_LEFT,
ctrlKey: true,
shiftKey: true,
}),
);
expect(textElement.fontSize).toBe(origFontSize);
});
}); });
describe("Test bounded text", () => { describe("Test bounded text", () => {
let rectangle: any; let rectangle: any;
const { const {

View File

@ -17,6 +17,11 @@ import {
getContainerElement, getContainerElement,
wrapText, wrapText,
} from "./textElement"; } from "./textElement";
import {
actionDecreaseFontSize,
actionIncreaseFontSize,
} from "../actions/actionProperties";
import App from "../components/App";
const normalizeText = (text: string) => { const normalizeText = (text: string) => {
return ( return (
@ -60,6 +65,7 @@ export const textWysiwyg = ({
element, element,
canvas, canvas,
excalidrawContainer, excalidrawContainer,
app,
}: { }: {
id: ExcalidrawElement["id"]; id: ExcalidrawElement["id"];
appState: AppState; appState: AppState;
@ -73,6 +79,7 @@ export const textWysiwyg = ({
element: ExcalidrawTextElement; element: ExcalidrawTextElement;
canvas: HTMLCanvasElement | null; canvas: HTMLCanvasElement | null;
excalidrawContainer: HTMLDivElement | null; excalidrawContainer: HTMLDivElement | null;
app: App;
}) => { }) => {
const textPropertiesUpdated = ( const textPropertiesUpdated = (
updatedElement: ExcalidrawTextElement, updatedElement: ExcalidrawTextElement,
@ -293,16 +300,18 @@ export const textWysiwyg = ({
} }
editable.onkeydown = (event) => { editable.onkeydown = (event) => {
if (!event[KEYS.CTRL_OR_CMD]) { event.stopPropagation();
event.stopPropagation();
} if (actionDecreaseFontSize.keyTest(event)) {
if (event.key === KEYS.ESCAPE) { app.actionManager.executeAction(actionDecreaseFontSize);
} else if (actionIncreaseFontSize.keyTest(event)) {
app.actionManager.executeAction(actionIncreaseFontSize);
} else if (event.key === KEYS.ESCAPE) {
event.preventDefault(); event.preventDefault();
submittedViaKeyboard = true; submittedViaKeyboard = true;
handleSubmit(); handleSubmit();
} else if (event.key === KEYS.ENTER && event[KEYS.CTRL_OR_CMD]) { } else if (event.key === KEYS.ENTER && event[KEYS.CTRL_OR_CMD]) {
event.preventDefault(); event.preventDefault();
event.stopPropagation();
if (event.isComposing || event.keyCode === 229) { if (event.isComposing || event.keyCode === 229) {
return; return;
} }
@ -315,7 +324,6 @@ export const textWysiwyg = ({
event.code === CODES.BRACKET_RIGHT)) event.code === CODES.BRACKET_RIGHT))
) { ) {
event.preventDefault(); event.preventDefault();
event.stopPropagation();
if (event.shiftKey || event.code === CODES.BRACKET_LEFT) { if (event.shiftKey || event.code === CODES.BRACKET_LEFT) {
outdent(); outdent();
} else { } else {

View File

@ -8,7 +8,10 @@ import {
getTransformHandles, getTransformHandles,
TransformHandleDirection, TransformHandleDirection,
} from "../element/transformHandles"; } from "../element/transformHandles";
import { ExcalidrawElement } from "../element/types"; import { ExcalidrawElement, ExcalidrawTextElement } from "../element/types";
import ExcalidrawApp from "../excalidraw-app";
import { API } from "./helpers/api";
import { KEYS } from "../keys";
const mouse = new Pointer("mouse"); const mouse = new Pointer("mouse");
@ -143,3 +146,31 @@ const resize = (
mouse.up(); mouse.up();
}); });
}; };
describe("Test text element", () => {
it("should update font size via keyboard", async () => {
await render(<ExcalidrawApp />);
const textElement = API.createElement({
type: "text",
text: "abc",
});
window.h.elements = [textElement];
API.setSelectedElements([textElement]);
const origFontSize = textElement.fontSize;
Keyboard.withModifierKeys({ shift: true, ctrl: true }, () => {
Keyboard.keyDown(KEYS.CHEVRON_RIGHT);
expect((window.h.elements[0] as ExcalidrawTextElement).fontSize).toBe(
origFontSize * 1.1,
);
Keyboard.keyDown(KEYS.CHEVRON_LEFT);
expect((window.h.elements[0] as ExcalidrawTextElement).fontSize).toBe(
origFontSize,
);
});
});
});