Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
42 changes: 42 additions & 0 deletions e2e-tests/tests/selection/right-click-selection.spec.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,42 @@
import { test, expect } from '@playwright/test';
import { goToPageAndWaitForEditor } from '../helpers.js';

// TODO: Add Firefox to playwright.config.js projects to catch Firefox-specific regressions.
// This test was added for SD-1623 where Firefox clears selection on right-click mousedown
// when preventDefault() is called, unlike Chrome/Safari.
test.describe('Right-click selection preservation', () => {
test('preserves text selection after right-click @SD-1623', async ({ page }) => {
await goToPageAndWaitForEditor(page);

const editor = page.locator('div.super-editor').first();
await editor.click();

// Type some text to select
const testText = 'Hello World';
await page.keyboard.type(testText);

// Select all the text we just typed
await page.keyboard.press('ControlOrMeta+a');

// Verify text is selected before right-click
const selectionBefore = await page.evaluate(() => window.getSelection()?.toString());
expect(selectionBefore).toContain(testText);

// Right-click on the selected text
const textElement = editor.getByText(testText);
await textElement.click({ button: 'right' });

// Wait a moment for the selection handlers to run
await page.waitForTimeout(100);

// Verify selection is preserved - check for either:
// 1. Native selection still present, OR
// 2. Visual selection decoration applied (sd-custom-selection class)
const selectionAfter = await page.evaluate(() => window.getSelection()?.toString());
const hasVisualSelection = await editor.locator('.sd-custom-selection').count();

// Either the native selection should be preserved OR the visual decoration should be shown
const isSelectionPreserved = selectionAfter.includes(testText) || hasVisualSelection > 0;
expect(isSelectionPreserved).toBe(true);
});
});
Original file line number Diff line number Diff line change
Expand Up @@ -187,7 +187,9 @@ export const CustomSelection = Extension.create({
return false;
}

event.preventDefault(); // Prevent default right-click behavior
// Note: Do NOT call event.preventDefault() here.
// Firefox clears native selection when preventDefault is called on mousedown.
// The contextmenu handler already prevents the native menu.
const { selection } = view.state;
if (!selection.empty) {
// Ensure selection stays visible for right-click/context menu
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -267,6 +267,32 @@ describe('CustomSelection plugin', () => {
expect(firstDeco?.to).toBe(6);
});

it('does not call preventDefault on right-click mousedown to preserve Firefox selection', () => {
const { plugin, view, editor } = createEnvironment();

const mouseDownEvent = {
button: 2,
preventDefault: vi.fn(),
target: editor.options.element,
type: 'mousedown',
};

plugin.props.handleDOMEvents.mousedown(view, mouseDownEvent);

// preventDefault should NOT be called for right-click mousedown
// because Firefox clears native selection when preventDefault is called
expect(mouseDownEvent.preventDefault).not.toHaveBeenCalled();

// But selection should still be preserved in plugin state
expect(view.dispatch).toHaveBeenCalled();
const dispatchedTr = view.dispatch.mock.calls[0][0];
expect(dispatchedTr.getMeta(CustomSelectionPluginKey)).toMatchObject({
focused: true,
preservedSelection: expect.any(Object),
showVisualSelection: true,
});
});

it('keeps selection visible when toolbar elements retain focus', () => {
const { plugin, view, editor } = createEnvironment();

Expand Down
Loading