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
Original file line number Diff line number Diff line change
@@ -0,0 +1,66 @@
import {EditorState, TextSelection} from 'prosemirror-state';
import {builders} from 'prosemirror-test-builder';

import {ExtensionsManager} from '../../../../core';
import {BaseNode, BaseSchemaSpecs} from '../../../base/specs';
import {LinkAttr, LinkSpecs, linkMarkName, linkType} from '../LinkSpecs';

Check failure on line 6 in packages/editor/src/extensions/markdown/Link/actions/linkEnhanceActions.test.ts

View workflow job for this annotation

GitHub Actions / Verify Files

'linkMarkName' is defined but never used. Allowed unused vars must match /^_/u

import {addEmptyLink} from './linkEnhanceActions';

const {schema} = new ExtensionsManager({
extensions: (builder) => builder.use(BaseSchemaSpecs, {}).use(LinkSpecs),
}).buildDeps();

const {doc, p} = builders<'doc' | 'p'>(schema, {
doc: {nodeType: BaseNode.Doc},
p: {nodeType: BaseNode.Paragraph},
});

function createState(content: ReturnType<typeof doc>) {
return EditorState.create({schema, doc: content});
}

describe('addEmptyLink', () => {
it('should add link mark to a single-character selection', () => {
const state = createState(doc(p('a')));
// Select "a" (positions 1-2)
const tr = state.tr.setSelection(TextSelection.create(state.doc, 1, 2));
const stateWithSelection = state.apply(tr);

let dispatched: EditorState | undefined;
addEmptyLink(stateWithSelection, (resultTr) => {
dispatched = stateWithSelection.apply(resultTr);
});

expect(dispatched).toBeDefined();
// The link mark should be active after the command
const storedMarks = dispatched!.storedMarks;
const linkMark = storedMarks?.find((m) => m.type === linkType(schema));
expect(linkMark).toBeDefined();
expect(linkMark!.attrs[LinkAttr.IsPlaceholder]).toBe(true);
});

it('should add link mark to a multi-character selection', () => {
const state = createState(doc(p('hello')));
// Select "hello" (positions 1-6)
const tr = state.tr.setSelection(TextSelection.create(state.doc, 1, 6));
const stateWithSelection = state.apply(tr);

let dispatched: EditorState | undefined;
addEmptyLink(stateWithSelection, (resultTr) => {
dispatched = stateWithSelection.apply(resultTr);
});

expect(dispatched).toBeDefined();
// Verify link mark is on the text
const textNode = dispatched!.doc.firstChild!.firstChild!;
const linkMark = textNode.marks.find((m) => m.type === linkType(schema));
expect(linkMark).toBeDefined();
});

it('should not activate on empty selection', () => {
const state = createState(doc(p('hello')));
const result = addEmptyLink(state);
expect(result).toBe(false);
});
});
Original file line number Diff line number Diff line change
Expand Up @@ -42,7 +42,16 @@ export const addEmptyLink: Command = (state, dispatch) => {
} else {
const selectedText = state.doc.textBetween($from.pos, $to.pos);
const countOfWhitespacesAtEnd = selectedText.length - selectedText.trimEnd().length;
tr.setSelection(TextSelection.create(tr.doc, $to.pos - countOfWhitespacesAtEnd - 1));
const pos = $to.pos - countOfWhitespacesAtEnd - 1;
tr.setSelection(TextSelection.create(tr.doc, pos));
// For short selections (e.g. 1 character), the cursor lands at the
// left edge of the non-inclusive link mark, so $from.marks() won't
// include it and the tooltip won't appear. Explicitly store the
// marks from the adjacent text node so isMarkActive() sees the link.
const nodeMarks = tr.doc.resolve(pos).nodeAfter?.marks;
if (nodeMarks) {
tr.setStoredMarks(nodeMarks);
}
}
dispatch?.(tr);
return true;
Expand Down
Loading