mirror of
https://github.com/penpot/penpot.git
synced 2026-03-26 11:11:16 -04:00
🐛 Fix wrong typography font size in sidebar when selecting text in Firefox (editor v2)
This commit is contained in:
@@ -291,6 +291,13 @@ export class TextNodeIterator {
|
||||
break;
|
||||
}
|
||||
}
|
||||
// The loop exits when currentNode === endNode without yielding endNode.
|
||||
// Callers (e.g. selection style merge) must visit every text/BR node in the
|
||||
// range, including the last one, or the final span is omitted (e.g. empty
|
||||
// paragraph with only <br>) and the sidebar shows "mixed" incorrectly.
|
||||
if (this.#currentNode === endNode) {
|
||||
yield this.#currentNode;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -70,4 +70,29 @@ describe("TextNodeIterator", () => {
|
||||
textNodeIterator.nextNode();
|
||||
expect(textNodeIterator.currentNode.nodeValue).toBe("Whatever");
|
||||
});
|
||||
|
||||
test("collectFrom includes the end node (iterateFrom must yield end inclusive)", () => {
|
||||
const rootNode = createRoot([
|
||||
createParagraph([createTextSpan(new Text("Hello"))]),
|
||||
createParagraph([createTextSpan(createLineBreak())]),
|
||||
]);
|
||||
const firstText = rootNode.firstChild.firstChild.firstChild;
|
||||
const br = rootNode.lastChild.firstChild.firstChild;
|
||||
const textNodeIterator = new TextNodeIterator(rootNode);
|
||||
const nodes = textNodeIterator.collectFrom(firstText, br);
|
||||
expect(nodes.length).toBe(2);
|
||||
expect(nodes[0]).toBe(firstText);
|
||||
expect(nodes[1]).toBe(br);
|
||||
});
|
||||
|
||||
test("collectFrom with identical start and end returns one node", () => {
|
||||
const rootNode = createRoot([
|
||||
createParagraph([createTextSpan(new Text("Hi"))]),
|
||||
]);
|
||||
const text = rootNode.firstChild.firstChild.firstChild;
|
||||
const textNodeIterator = new TextNodeIterator(rootNode);
|
||||
const nodes = textNodeIterator.collectFrom(text, text);
|
||||
expect(nodes.length).toBe(1);
|
||||
expect(nodes[0]).toBe(text);
|
||||
});
|
||||
});
|
||||
|
||||
@@ -403,7 +403,12 @@ export class SelectionController extends EventTarget {
|
||||
this.#updateCurrentStyle(textSpan);
|
||||
} else {
|
||||
// SELECTION.
|
||||
this.#updateCurrentStyleFrom(this.#anchorNode, this.#focusNode);
|
||||
// Use range boundaries normalized to text nodes, not anchor/focus.
|
||||
// Firefox may set anchorNode on the paragraph element and focusNode on a
|
||||
// text node for word selection; passing those to #updateCurrentStyleFrom
|
||||
// breaks TextNodeIterator and yields wrong styles (e.g. default 14px).
|
||||
const { startNode, endNode } = this.getRanges();
|
||||
this.#updateCurrentStyleFrom(startNode, endNode);
|
||||
}
|
||||
this.dispatchEvent(
|
||||
new CustomEvent("stylechange", {
|
||||
|
||||
@@ -1666,6 +1666,30 @@ describe("SelectionController", () => {
|
||||
expect(selectionController.focusAtEnd).toBeTruthy();
|
||||
})
|
||||
|
||||
test("`currentStyle` uses text span font-size when anchor is paragraph (Firefox-style word selection)", () => {
|
||||
const textEditorMock = TextEditorMock.createTextEditorMockWithParagraphs([
|
||||
createParagraph([
|
||||
createTextSpan(new Text("Hello World"), { "font-size": "36" }),
|
||||
]),
|
||||
]);
|
||||
const root = textEditorMock.root;
|
||||
const paragraph = root.firstChild;
|
||||
const textNode = paragraph.firstChild.firstChild;
|
||||
const selection = document.getSelection();
|
||||
const selectionController = new SelectionController(
|
||||
textEditorMock,
|
||||
selection,
|
||||
);
|
||||
textEditorMock.element.focus();
|
||||
// Anchor on the paragraph (child offset 0) and focus in the text node — matches
|
||||
// Firefox when double-click selects a word; anchor/focus are not both text nodes.
|
||||
selection.setBaseAndExtent(paragraph, 0, textNode, 5);
|
||||
document.dispatchEvent(new Event("selectionchange"));
|
||||
expect(selectionController.currentStyle.getPropertyValue("font-size")).toBe(
|
||||
"36px",
|
||||
);
|
||||
});
|
||||
|
||||
test("`dispose` should release every held reference", () => {
|
||||
const textEditorMock = TextEditorMock.createTextEditorMockWithParagraphs([
|
||||
createParagraphWith(["Hello, "], {
|
||||
|
||||
Reference in New Issue
Block a user