Skip to content
Closed
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
14 changes: 13 additions & 1 deletion packages/ariakit/src/style.css
Original file line number Diff line number Diff line change
Expand Up @@ -38,7 +38,19 @@
inset 0 1px 1px 1px var(--shadow);
}

.bn-toolbar .bn-ak-popover {
/* Menus and popovers are portalled into the editor container, so they are
siblings of the positioned UI (side menu, toolbars, …) that opened them
instead of descendants, and must stack above it. The UI elements sit at
`--bn-ui-base-z-index` + 10…90 (see the React controllers); Ariakit's own
stylesheet gives these a fixed 50. */
.bn-ariakit .bn-ak-menu,
.bn-ariakit .bn-ak-popover {
z-index: calc(var(--bn-ui-base-z-index, 0) + 100);
}

/* Form popovers (link, caption, rename) stack their fields; lists keep the
tighter spacing. */
.bn-ariakit .bn-form-popover {
gap: 0.5rem;
}

Expand Down
22 changes: 22 additions & 0 deletions packages/mantine/src/BlockNoteView.browser.test.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -94,6 +94,26 @@ function Harness(props: { setup: Setup }) {
);
}

/**
* Waits until no commit has landed for a while. Mounting schedules an update
* from an effect that can commit after `act` has returned, and on Linux WebKit
* whether it does varies from mount to mount; measuring only once things have
* settled makes every mount count the same set of commits.
*/
async function settle() {
let last = commits;
for (let i = 0; i < 20; i++) {
await act(async () => {
await new Promise((resolve) => setTimeout(resolve, 50));
});
if (commits === last) {
return;
}
last = commits;
}
throw new Error("commits did not settle");
}

async function mount(setup: Setup, strict: boolean) {
editor = BlockNoteEditor.create();
commits = 0;
Expand All @@ -103,6 +123,7 @@ async function mount(setup: Setup, strict: boolean) {
await act(async () => {
root!.render(strict ? <StrictMode>{tree}</StrictMode> : tree);
});
await settle();
if (!container.querySelector(".bn-editor")) {
throw new Error("editor did not mount");
}
Expand Down Expand Up @@ -152,6 +173,7 @@ describe.each([{ strict: false }, { strict: true }])(
await act(async () => {
rerenderParent();
});
await settle();
const cost = {
commits: commits - before.commits,
childRenders: childRenders - before.childRenders,
Expand Down
25 changes: 7 additions & 18 deletions packages/mantine/src/blocknoteStyles.css
Original file line number Diff line number Diff line change
Expand Up @@ -216,15 +216,16 @@ on touch devices (e.g. the mobile formatting toolbar). */
height: 40px;
}

.bn-toolbar .mantine-Menu-item {
/* Menus are portalled into the editor container, so they can't be styled
through the toolbar, side menu or table handle that opened them; scope on
the dropdowns themselves instead. */
.bn-mantine .bn-select .mantine-Menu-item,
.bn-mantine .bn-menu-dropdown .mantine-Menu-item,
.bn-mantine .bn-table-handle-menu .mantine-Menu-item {
font-size: 12px;
height: 30px;
}

.bn-toolbar .mantine-Menu-item:hover {
background-color: var(--bn-colors-hovered-background);
}

.bn-mantine .bn-form-popover {
background-color: var(--bn-colors-menu-background);
border: var(--bn-border);
Expand Down Expand Up @@ -494,12 +495,6 @@ on touch devices (e.g. the mobile formatting toolbar). */
overflow: visible;
}

.bn-side-menu .mantine-Menu-item,
.bn-table-handle-menu .mantine-Menu-item {
font-size: 12px;
height: 30px;
}

.bn-side-menu .mantine-UnstyledButton-root:not(.mantine-Menu-item) {
background-color: transparent;
}
Expand All @@ -519,10 +514,8 @@ on touch devices (e.g. the mobile formatting toolbar). */
display: flex;
}

.bn-side-menu .mantine-Menu-dropdown {
.bn-mantine .bn-drag-handle-menu {
min-width: 100px;
padding: 2px;
position: absolute;
}

/* Image Panel styling*/
Expand Down Expand Up @@ -736,10 +729,6 @@ on touch devices (e.g. the mobile formatting toolbar). */
color: var(--bn-colors-disabled-text);
}

.bn-mantine .bn-action-toolbar .mantine-Menu-itemLabel {
font-size: 12px;
}

/* Badge styling */
.bn-mantine .bn-badge-group {
display: flex;
Expand Down
14 changes: 11 additions & 3 deletions tests/src/end-to-end/comments/comments.test.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,12 @@ import {
sleep,
waitForSelector,
} from "../../utils/editor.js";
import { clickAt, getRect, moveMouseOverElement } from "../../utils/mouse.js";
import {
clickAt,
clickElement,
getRect,
moveMouseOverElement,
} from "../../utils/mouse.js";

/** Double-clicks the centre of an element via the real Playwright mouse. */
async function doubleClickElement(element: Element) {
Expand Down Expand Up @@ -123,7 +128,9 @@ describe("Check Comments functionality", () => {
await expectElement(
await waitForSelector(LINK_BUTTON_SELECTOR),
).toBeVisible();
await userEvent.click(await waitForSelector(LINK_BUTTON_SELECTOR));
// A real press, not `userEvent.click`: the popover's input must win the
// focus race against the toolbar's focus trap (see `clickElement`).
await clickElement(await waitForSelector(LINK_BUTTON_SELECTOR));

await userEvent.keyboard("https://example.com");
await userEvent.keyboard("{Enter}");
Expand Down Expand Up @@ -154,7 +161,8 @@ describe("Check Comments functionality", () => {
await expectElement(
await waitForSelector(LINK_BUTTON_SELECTOR),
).toBeVisible();
await userEvent.click(await waitForSelector(LINK_BUTTON_SELECTOR));
// A real press, not `userEvent.click` (see `clickElement`).
await clickElement(await waitForSelector(LINK_BUTTON_SELECTOR));

await userEvent.keyboard("https://example.com");
await userEvent.keyboard("{Enter}");
Expand Down
10 changes: 8 additions & 2 deletions tests/src/end-to-end/theming/theming.test.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,11 @@ import {
sleep,
waitForSelector,
} from "../../utils/editor.js";
import { moveMouseOverElement, mouseSequence } from "../../utils/mouse.js";
import {
clickElement,
moveMouseOverElement,
mouseSequence,
} from "../../utils/mouse.js";
import { executeSlashCommand } from "../../utils/slashmenu.js";

// Vitest browser mode has no per-test `colorScheme` knob (the playwright
Expand Down Expand Up @@ -67,7 +71,9 @@ describe("Check Dark Theme is Automatically Applied", () => {
await userEvent.keyboard("Paragraph");
await userEvent.keyboard("{Shift>}{Home}{/Shift}");

await userEvent.click(await waitForSelector(LINK_BUTTON_SELECTOR));
// A real press, not `userEvent.click`: the popover's input must win the
// focus race against the toolbar's focus trap (see `clickElement`).
await clickElement(await waitForSelector(LINK_BUTTON_SELECTOR));

await sleep(500);
await userEvent.keyboard("link");
Expand Down
4 changes: 3 additions & 1 deletion tests/src/utils/const.ts
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,9 @@ export const TABLE_SELECTOR = `[data-content-type="table"]`;
export const DRAG_HANDLE_SELECTOR = `[data-test="dragHandle"]`;
export const DRAG_HANDLE_ADD_SELECTOR = `[data-test="dragHandleAdd"]`;

export const DRAG_HANDLE_MENU_SELECTOR = `.bn-side-menu > .bn-menu-dropdown`;
// The menu is portalled into the editor container, so it is not a descendant
// of the side menu that opens it; match it by its own class.
export const DRAG_HANDLE_MENU_SELECTOR = `.bn-drag-handle-menu`;
export const SLASH_MENU_SELECTOR = `.bn-suggestion-menu`;
export const EMOJI_PICKER_SELECTOR = `.bn-grid-suggestion-menu`;

Expand Down
30 changes: 27 additions & 3 deletions tests/src/utils/mouse.ts
Original file line number Diff line number Diff line change
Expand Up @@ -45,9 +45,33 @@ export function mouseSequence(actions: MouseAction[]): Promise<void> {
return runMouse(actions);
}

/** Single (or multi-) click at iframe-relative coordinates. */
export function clickAt(x: number, y: number, clickCount = 1): Promise<void> {
return runMouse([{ type: "click", x, y, clickCount }]);
/**
* Single (or multi-) click at iframe-relative coordinates. `delay` is the time
* the button is held between mousedown and mouseup (Playwright's default is 0).
*/
export function clickAt(
x: number,
y: number,
clickCount = 1,
delay?: number,
): Promise<void> {
return runMouse([{ type: "click", x, y, clickCount, delay }]);
}

/**
* Clicks the centre of an element with the real mouse, holding the button for
* `delay` ms. Use this instead of `userEvent.click` where an instantaneous
* press behaves differently from a human one: a toolbar button that opens a
* popover arms the toolbar's focus trap on mousedown, and with a 0 ms press
* the trap's deferred focus move lands after the popover's input has taken
* focus, stealing it back. Any real press (≥ ~40 ms) lets the input win.
*/
export function clickElement(
selectorOrElement: string | Element,
{ delay = 50 }: { delay?: number } = {},
): Promise<void> {
const { x, y } = center(getRect(selectorOrElement));
return clickAt(x, y, 1, delay);
}

/** Moves the mouse to the centre of an element (e.g. to reveal hover UI). */
Expand Down
10 changes: 9 additions & 1 deletion tests/src/utils/positionalMouse.ts
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,14 @@ export type MouseAction =
| { type: "move"; x: number; y: number; steps?: number }
| { type: "down" }
| { type: "up" }
| { type: "click"; x: number; y: number; clickCount?: number };
| {
type: "click";
x: number;
y: number;
clickCount?: number;
/** Time between mousedown and mouseup, like a real press (default 0). */
delay?: number;
};

/**
* Browser-side signature of the {@link positionalMouse} command below, i.e. what
Expand Down Expand Up @@ -74,6 +81,7 @@ export const positionalMouse: BrowserCommand<MouseAction[]> = async (
case "click":
await page.mouse.click(offsetX + action.x, offsetY + action.y, {
clickCount: action.clickCount ?? 1,
delay: action.delay,
});
break;
}
Expand Down
Loading