From a786c9a2719a7e1d47d75221331f36f0530337b9 Mon Sep 17 00:00:00 2001 From: Florian Duros Date: Wed, 25 Jan 2023 15:34:49 +0100 Subject: [PATCH 1/3] Fix link creation with backward selection --- .../views/rooms/wysiwyg_composer/ComposerContext.ts | 2 +- .../rooms/wysiwyg_composer/hooks/useComposerFunctions.ts | 1 + .../views/rooms/wysiwyg_composer/hooks/useSelection.ts | 4 ++++ src/components/views/rooms/wysiwyg_composer/types.ts | 4 +++- .../views/rooms/wysiwyg_composer/utils/selection.ts | 9 +++++++-- .../rooms/wysiwyg_composer/SendWysiwygComposer-test.tsx | 1 + .../rooms/wysiwyg_composer/components/LinkModal-test.tsx | 1 + 7 files changed, 18 insertions(+), 4 deletions(-) diff --git a/src/components/views/rooms/wysiwyg_composer/ComposerContext.ts b/src/components/views/rooms/wysiwyg_composer/ComposerContext.ts index c8a369ead29..582c883dfee 100644 --- a/src/components/views/rooms/wysiwyg_composer/ComposerContext.ts +++ b/src/components/views/rooms/wysiwyg_composer/ComposerContext.ts @@ -20,7 +20,7 @@ import { SubSelection } from "./types"; export function getDefaultContextValue(): { selection: SubSelection } { return { - selection: { anchorNode: null, anchorOffset: 0, focusNode: null, focusOffset: 0 }, + selection: { anchorNode: null, anchorOffset: 0, focusNode: null, focusOffset: 0, isForward: true }, }; } diff --git a/src/components/views/rooms/wysiwyg_composer/hooks/useComposerFunctions.ts b/src/components/views/rooms/wysiwyg_composer/hooks/useComposerFunctions.ts index cad2e130745..7578fb7389d 100644 --- a/src/components/views/rooms/wysiwyg_composer/hooks/useComposerFunctions.ts +++ b/src/components/views/rooms/wysiwyg_composer/hooks/useComposerFunctions.ts @@ -44,6 +44,7 @@ export function useComposerFunctions( anchorOffset: anchorOffset + text.length, focusNode: ref.current.firstChild, focusOffset: focusOffset + text.length, + isForward: true, }); setContent(ref.current.innerHTML); } diff --git a/src/components/views/rooms/wysiwyg_composer/hooks/useSelection.ts b/src/components/views/rooms/wysiwyg_composer/hooks/useSelection.ts index 7f828cee391..92823773c35 100644 --- a/src/components/views/rooms/wysiwyg_composer/hooks/useSelection.ts +++ b/src/components/views/rooms/wysiwyg_composer/hooks/useSelection.ts @@ -23,11 +23,15 @@ function setSelectionContext(composerContext: ComposerContextState): void { const selection = document.getSelection(); if (selection) { + const range = selection.getRangeAt(0); + const isForward = range.startContainer === selection.anchorNode && range.startOffset === selection.anchorOffset; + composerContext.selection = { anchorNode: selection.anchorNode, anchorOffset: selection.anchorOffset, focusNode: selection.focusNode, focusOffset: selection.focusOffset, + isForward, }; } } diff --git a/src/components/views/rooms/wysiwyg_composer/types.ts b/src/components/views/rooms/wysiwyg_composer/types.ts index 6505825b286..97d3fd91a5e 100644 --- a/src/components/views/rooms/wysiwyg_composer/types.ts +++ b/src/components/views/rooms/wysiwyg_composer/types.ts @@ -19,4 +19,6 @@ export type ComposerFunctions = { insertText: (text: string) => void; }; -export type SubSelection = Pick; +export type SubSelection = Pick & { + isForward: boolean; +}; diff --git a/src/components/views/rooms/wysiwyg_composer/utils/selection.ts b/src/components/views/rooms/wysiwyg_composer/utils/selection.ts index e6a594451bb..ca515d06eef 100644 --- a/src/components/views/rooms/wysiwyg_composer/utils/selection.ts +++ b/src/components/views/rooms/wysiwyg_composer/utils/selection.ts @@ -19,9 +19,14 @@ import { SubSelection } from "../types"; export function setSelection(selection: SubSelection): Promise { if (selection.anchorNode && selection.focusNode) { const range = new Range(); - range.setStart(selection.anchorNode, selection.anchorOffset); - range.setEnd(selection.focusNode, selection.focusOffset); + if (selection.isForward) { + range.setStart(selection.anchorNode, selection.anchorOffset); + range.setEnd(selection.focusNode, selection.focusOffset); + } else { + range.setStart(selection.focusNode, selection.focusOffset); + range.setEnd(selection.anchorNode, selection.anchorOffset); + } document.getSelection()?.removeAllRanges(); document.getSelection()?.addRange(range); } diff --git a/test/components/views/rooms/wysiwyg_composer/SendWysiwygComposer-test.tsx b/test/components/views/rooms/wysiwyg_composer/SendWysiwygComposer-test.tsx index 7f04bf21ffb..cd60c108689 100644 --- a/test/components/views/rooms/wysiwyg_composer/SendWysiwygComposer-test.tsx +++ b/test/components/views/rooms/wysiwyg_composer/SendWysiwygComposer-test.tsx @@ -299,6 +299,7 @@ describe("SendWysiwygComposer", () => { anchorOffset: 2, focusNode: textNode, focusOffset: 2, + isForward: true, }); // the event is not automatically fired by jest document.dispatchEvent(new CustomEvent("selectionchange")); diff --git a/test/components/views/rooms/wysiwyg_composer/components/LinkModal-test.tsx b/test/components/views/rooms/wysiwyg_composer/components/LinkModal-test.tsx index 527f7e7b3d6..852354f0ba5 100644 --- a/test/components/views/rooms/wysiwyg_composer/components/LinkModal-test.tsx +++ b/test/components/views/rooms/wysiwyg_composer/components/LinkModal-test.tsx @@ -35,6 +35,7 @@ describe("LinkModal", () => { anchorNode: null, focusOffset: 3, anchorOffset: 4, + isForward: true, }; const customRender = (isTextEnabled: boolean, onClose: () => void, isEditing = false) => { From 54831de3dc2428996ff81c20dc5e95d096dc92a1 Mon Sep 17 00:00:00 2001 From: Florian Duros Date: Wed, 25 Jan 2023 17:19:05 +0100 Subject: [PATCH 2/3] Add cypress test around link in RTE --- cypress/e2e/composer/composer.spec.ts | 22 +++++++++++++++++++++- 1 file changed, 21 insertions(+), 1 deletion(-) diff --git a/cypress/e2e/composer/composer.spec.ts b/cypress/e2e/composer/composer.spec.ts index 289d865ba64..48467ac6564 100644 --- a/cypress/e2e/composer/composer.spec.ts +++ b/cypress/e2e/composer/composer.spec.ts @@ -98,7 +98,7 @@ describe("Composer", () => { }); }); - describe("WYSIWYG", () => { + describe("Rich text editor", () => { beforeEach(() => { cy.enableLabsFeature("feature_wysiwyg_composer"); cy.initTestUser(homeserver, "Janet").then(() => { @@ -165,5 +165,25 @@ describe("Composer", () => { cy.contains(".mx_EventTile_body", "my message 3"); }); }); + + describe("links", () => { + it("create link with a forward selection", () => { + // Type a message + cy.get("div[contenteditable=true]").type("my message 0{selectAll}"); + + // Open link modal + cy.get('button[aria-label="Link"]').click(); + // Fill the link field + cy.get('input[label="Link"]').type("https://matrix.org/"); + // Click on save + cy.get('button[type="submit"]').click(); + // Send the message + cy.get('div[aria-label="Send message"]').click(); + + // It was sent + cy.contains(".mx_EventTile_body a", "my message 0"); + cy.get(".mx_EventTile_body a").should("have.attr", "href").and("include", "https://matrix.org/"); + }); + }); }); }); From 28c6583de6bf4b81c285b4a23ba083a68ccb39ce Mon Sep 17 00:00:00 2001 From: Florian Duros Date: Thu, 26 Jan 2023 10:37:49 +0100 Subject: [PATCH 3/3] Add test around backward selection --- .../SendWysiwygComposer-test.tsx | 26 +++++++++++++++++++ 1 file changed, 26 insertions(+) diff --git a/test/components/views/rooms/wysiwyg_composer/SendWysiwygComposer-test.tsx b/test/components/views/rooms/wysiwyg_composer/SendWysiwygComposer-test.tsx index cd60c108689..aa39de7a573 100644 --- a/test/components/views/rooms/wysiwyg_composer/SendWysiwygComposer-test.tsx +++ b/test/components/views/rooms/wysiwyg_composer/SendWysiwygComposer-test.tsx @@ -309,6 +309,32 @@ describe("SendWysiwygComposer", () => { // Then await waitFor(() => expect(screen.getByRole("textbox")).toHaveTextContent(/wo🦫rd/)); }); + + it("Should add an emoji when a word is selected", async () => { + // When + screen.getByRole("textbox").focus(); + screen.getByRole("textbox").innerHTML = "word"; + fireEvent.input(screen.getByRole("textbox"), { + data: "word", + inputType: "insertText", + }); + + const textNode = screen.getByRole("textbox").firstChild; + await setSelection({ + anchorNode: textNode, + anchorOffset: 3, + focusNode: textNode, + focusOffset: 2, + isForward: false, + }); + // the event is not automatically fired by jest + document.dispatchEvent(new CustomEvent("selectionchange")); + + emojiButton.click(); + + // Then + await waitFor(() => expect(screen.getByRole("textbox")).toHaveTextContent(/wo🦫d/)); + }); }, ); });