Skip to content
Open
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
11 changes: 11 additions & 0 deletions lib/cql/src/cqlInput/CqlInput.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -78,4 +78,15 @@ describe("CqlInput", () => {
"This event should be propagated to the input's container, as it is not handled by the editor",
).toBe(true);
});

it("should update the rendered value when only the polarity changes", () => {
const { cqlInput } = createCqlInputContainer("+tag:one");
let callbackValue = "";
cqlInput.addEventListener(
"queryChange",
(e) => (callbackValue = e.detail.queryStr),
);
cqlInput.setAttribute("value", "-tag:one");
expect(callbackValue).toBe("-tag:one");
});
});
39 changes: 37 additions & 2 deletions lib/cql/src/cqlInput/editor/diff.ts
Original file line number Diff line number Diff line change
@@ -1,10 +1,41 @@
import { Fragment } from "prosemirror-model";
import { Fragment, Mark, Node } from "prosemirror-model";
import { IS_READ_ONLY, IS_SELECTED } from "./schema";

/**
* These diff functions are vendored to ignore attributes and markup, as we only care about markup.
* These diff functions are vendored from ProseMirror's Fragment.findDiffStart /
* findDiffEnd to customise attribute comparison.
*
* The originals use Node.sameMarkup, which compares ALL attributes. That
* causes false positives for transient editor-state attributes (IS_SELECTED,
* IS_READ_ONLY) that differ between the live document and an incoming document
* built by queryToProseMirrorDoc — see #52.
*
* We replace sameMarkup with sameContentMarkup, which skips transient attrs
* so the diff is blind to decorative state but still detects semantically
* meaningful changes like POLARITY.
*
* Source: https://github.com/ProseMirror/prosemirror-model/blob/c8c7b62645d2a8293fa6b7f52aa2b04a97821f34/src/diff.ts
*/

/** Attrs that are transient editor state, not query content. */
const TRANSIENT_ATTRS: ReadonlySet<string> = new Set([IS_SELECTED, IS_READ_ONLY]);

/**
* Like Node.sameMarkup, but ignores transient editor-state attributes.
*/
function sameContentMarkup(a: Node, b: Node): boolean {
if (a.type !== b.type || !Mark.sameSet(a.marks, b.marks)) return false;
for (const key in a.attrs) {
if (TRANSIENT_ATTRS.has(key)) continue;
if (a.attrs[key] !== b.attrs[key]) return false;
}
for (const key in b.attrs) {
if (TRANSIENT_ATTRS.has(key)) continue;
if (!(key in a.attrs)) return false;
}
return true;
}

export function findDiffStartForContent(
a: Fragment,
b: Fragment,
Expand All @@ -21,6 +52,8 @@ export function findDiffStartForContent(
continue;
}

if (!sameContentMarkup(childA, childB)) return pos;

if (childA.isText && childA.text != childB.text) {
for (let j = 0; childA.text![j] == childB.text![j]; j++) pos++;
return pos;
Expand Down Expand Up @@ -51,6 +84,8 @@ export function findDiffEndForContent(
continue;
}

if (!sameContentMarkup(childA, childB)) return { a: posA, b: posB };

if (childA.isText && childA.text != childB.text) {
let same = 0;
const minSize = Math.min(childA.text!.length, childB.text!.length);
Expand Down
11 changes: 11 additions & 0 deletions lib/cql/src/cqlInput/editor/editor.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -140,4 +140,15 @@ describe("updateEditorViewWithQueryStr", () => {

expect(docToCqlStrWithSelection(editorView.state)).toEqual("+tag:^$ ");
});

it("should update the document when only the polarity of a chip changes", () => {
const { editorView, updateEditorView } =
createEditorFromInitialState("+tag:a");

expect(docToCqlStr(editorView.state.doc)).toEqual("+tag:a ");

updateEditorView("-tag:a");

expect(docToCqlStr(editorView.state.doc)).toEqual("-tag:a ");
});
});
Loading