Skip to content

Commit 3220534

Browse files
authored
fix(links): Ensure that CTRL+K on Windows toggles link. Fixes #452 (#453)
1 parent b75bb7d commit 3220534

File tree

3 files changed

+133
-79
lines changed

3 files changed

+133
-79
lines changed

src/js/editor/key-commands.js

Lines changed: 44 additions & 31 deletions
Original file line numberDiff line numberDiff line change
@@ -25,6 +25,44 @@ function gotoEndOfLine(editor) {
2525
});
2626
}
2727

28+
function deleteToEndOfSection(editor) {
29+
let { range } = editor;
30+
if (range.isCollapsed) {
31+
let { head, head: { section } } = range;
32+
range = head.toRange(section.tailPosition());
33+
}
34+
editor.run(postEditor => {
35+
let nextPosition = postEditor.deleteRange(range);
36+
postEditor.setRange(nextPosition);
37+
});
38+
}
39+
40+
function toggleLink(editor) {
41+
if (editor.range.isCollapsed) {
42+
return;
43+
}
44+
45+
let selectedText = editor.cursor.selectedText();
46+
let defaultUrl = '';
47+
if (selectedText.indexOf('http') !== -1) { defaultUrl = selectedText; }
48+
49+
let {range} = editor;
50+
let hasLink = editor.detectMarkupInRange(range, 'a');
51+
52+
if (hasLink) {
53+
editor.run(postEditor => postEditor.toggleMarkup('a'));
54+
} else {
55+
editor.showPrompt('Enter a URL', defaultUrl, url => {
56+
if (!url) { return; }
57+
58+
editor.run(postEditor => {
59+
let markup = postEditor.builder.createMarkup('a', {href: url});
60+
postEditor.toggleMarkup(markup);
61+
});
62+
});
63+
}
64+
}
65+
2866
export const DEFAULT_KEY_COMMANDS = [{
2967
str: 'META+B',
3068
run(editor) {
@@ -48,15 +86,11 @@ export const DEFAULT_KEY_COMMANDS = [{
4886
}, {
4987
str: 'CTRL+K',
5088
run(editor) {
51-
let { range } = editor;
52-
if (range.isCollapsed) {
53-
let { head, head: { section } } = range;
54-
range = head.toRange(section.tailPosition());
89+
if (Browser.isMac()) {
90+
return deleteToEndOfSection(editor);
91+
} else if (Browser.isWin()) {
92+
return toggleLink(editor);
5593
}
56-
editor.run(postEditor => {
57-
let nextPosition = postEditor.deleteRange(range);
58-
postEditor.setRange(nextPosition);
59-
});
6094
}
6195
}, {
6296
str: 'CTRL+A',
@@ -84,30 +118,9 @@ export const DEFAULT_KEY_COMMANDS = [{
84118
}, {
85119
str: 'META+K',
86120
run(editor) {
87-
if (editor.range.isCollapsed) {
88-
return;
89-
}
121+
return toggleLink(editor);
122+
},
90123

91-
let selectedText = editor.cursor.selectedText();
92-
let defaultUrl = '';
93-
if (selectedText.indexOf('http') !== -1) { defaultUrl = selectedText; }
94-
95-
let {range} = editor;
96-
let hasLink = editor.detectMarkupInRange(range, 'a');
97-
98-
if (hasLink) {
99-
editor.run(postEditor => postEditor.toggleMarkup('a'));
100-
} else {
101-
editor.showPrompt('Enter a URL', defaultUrl, url => {
102-
if (!url) { return; }
103-
104-
editor.run(postEditor => {
105-
let markup = postEditor.builder.createMarkup('a', {href: url});
106-
postEditor.toggleMarkup(markup);
107-
});
108-
});
109-
}
110-
}
111124
}, {
112125
str: 'META+Z',
113126
run(editor) {

tests/acceptance/editor-key-commands-test.js

Lines changed: 88 additions & 48 deletions
Original file line numberDiff line numberDiff line change
@@ -2,8 +2,9 @@ import { MODIFIERS } from 'mobiledoc-kit/utils/key';
22
import Keycodes from 'mobiledoc-kit/utils/keycodes';
33
import Helpers from '../test-helpers';
44
import Range from 'mobiledoc-kit/utils/cursor/range';
5+
import Browser from 'mobiledoc-kit/utils/browser';
56

6-
const { module, test } = Helpers;
7+
const { module, test, skip } = Helpers;
78

89
let editor, editorElement;
910

@@ -152,57 +153,59 @@ testStatefulCommand({
152153
markupName: 'em'
153154
});
154155

155-
test(`ctrl-k clears to the end of a line`, (assert) => {
156-
let initialText = 'something';
157-
editor = renderIntoAndFocusTail(({post, markupSection, marker}) => post([
158-
markupSection('p', [marker(initialText)])
159-
]));
156+
if (Browser.isMac()) {
157+
test(`[Mac] ctrl-k clears to the end of a line`, (assert) => {
158+
let initialText = 'something';
159+
editor = renderIntoAndFocusTail(({post, markupSection, marker}) => post([
160+
markupSection('p', [marker(initialText)])
161+
]));
160162

161-
assert.ok(editor.hasCursor(), 'has cursor');
163+
assert.ok(editor.hasCursor(), 'has cursor');
162164

163-
let textElement = editor.post.sections.head.markers.head.renderNode.element;
164-
Helpers.dom.moveCursorTo(editor, textElement, 4);
165-
Helpers.dom.triggerKeyCommand(editor, 'K', MODIFIERS.CTRL);
166-
167-
let changedMobiledoc = editor.serialize();
168-
let expectedMobiledoc = Helpers.mobiledoc.build(
169-
({post, markupSection, marker}) => {
170-
return post([
171-
markupSection('p', [
172-
marker('some')
173-
])
174-
]);
165+
let textElement = editor.post.sections.head.markers.head.renderNode.element;
166+
Helpers.dom.moveCursorTo(editor, textElement, 4);
167+
Helpers.dom.triggerKeyCommand(editor, 'K', MODIFIERS.CTRL);
168+
169+
let changedMobiledoc = editor.serialize();
170+
let expectedMobiledoc = Helpers.mobiledoc.build(
171+
({post, markupSection, marker}) => {
172+
return post([
173+
markupSection('p', [
174+
marker('some')
175+
])
176+
]);
177+
});
178+
assert.deepEqual(changedMobiledoc, expectedMobiledoc,
179+
'mobiledoc updated appropriately');
175180
});
176-
assert.deepEqual(changedMobiledoc, expectedMobiledoc,
177-
'mobiledoc updated appropriately');
178-
});
179181

180-
test(`ctrl-k clears selected text`, (assert) => {
181-
let initialText = 'something';
182-
editor = renderIntoAndFocusTail( ({post, markupSection, marker}) => post([
183-
markupSection('p', [marker(initialText)])
184-
]));
182+
test(`[Mac] ctrl-k clears selected text`, (assert) => {
183+
let initialText = 'something';
184+
editor = renderIntoAndFocusTail( ({post, markupSection, marker}) => post([
185+
markupSection('p', [marker(initialText)])
186+
]));
185187

186-
assert.ok(editor.hasCursor(), 'has cursor');
188+
assert.ok(editor.hasCursor(), 'has cursor');
187189

188-
let textElement = editor.post.sections.head.markers.head.renderNode.element;
189-
Helpers.dom.moveCursorTo(editor, textElement, 4, textElement, 8);
190-
Helpers.dom.triggerKeyCommand(editor, 'K', MODIFIERS.CTRL);
191-
192-
let changedMobiledoc = editor.serialize();
193-
let expectedMobiledoc = Helpers.mobiledoc.build(
194-
({post, markupSection, marker}) => {
195-
return post([
196-
markupSection('p', [
197-
marker('someg')
198-
])
199-
]);
190+
let textElement = editor.post.sections.head.markers.head.renderNode.element;
191+
Helpers.dom.moveCursorTo(editor, textElement, 4, textElement, 8);
192+
Helpers.dom.triggerKeyCommand(editor, 'K', MODIFIERS.CTRL);
193+
194+
let changedMobiledoc = editor.serialize();
195+
let expectedMobiledoc = Helpers.mobiledoc.build(
196+
({post, markupSection, marker}) => {
197+
return post([
198+
markupSection('p', [
199+
marker('someg')
200+
])
201+
]);
202+
});
203+
assert.deepEqual(changedMobiledoc, expectedMobiledoc,
204+
'mobiledoc updated appropriately');
200205
});
201-
assert.deepEqual(changedMobiledoc, expectedMobiledoc,
202-
'mobiledoc updated appropriately');
203-
});
206+
}
204207

205-
test('cmd-k links selected text', (assert) => {
208+
let toggleLinkTest = (assert, modifier) => {
206209
assert.expect(3);
207210

208211
let url = 'http://bustle.com';
@@ -218,12 +221,12 @@ test('cmd-k links selected text', (assert) => {
218221
};
219222

220223
Helpers.dom.selectText(editor ,'something', editorElement);
221-
Helpers.dom.triggerKeyCommand(editor, 'K', MODIFIERS.META);
224+
Helpers.dom.triggerKeyCommand(editor, 'K', modifier);
222225

223226
assert.hasElement(`#editor a[href="${url}"]:contains(something)`);
224-
});
227+
};
225228

226-
test('cmd-k unlinks selected text if it was already linked', (assert) => {
229+
let toggleLinkUnlinkTest = (assert, modifier) => {
227230
assert.expect(4);
228231

229232
let url = 'http://bustle.com';
@@ -240,11 +243,48 @@ test('cmd-k unlinks selected text if it was already linked', (assert) => {
240243
'precond -- has link');
241244

242245
Helpers.dom.selectText(editor ,'something', editorElement);
243-
Helpers.dom.triggerKeyCommand(editor, 'K', MODIFIERS.META);
246+
Helpers.dom.triggerKeyCommand(editor, 'K', modifier);
244247

245248
assert.hasNoElement(`#editor a[href="${url}"]:contains(something)`,
246249
'removes linked text');
247250
assert.hasElement(`#editor p:contains(something)`, 'unlinked text remains');
251+
};
252+
253+
let toggleTests = [
254+
{
255+
precondition: () => Browser.isMac(),
256+
msg: '[Mac] cmd-k links selected text',
257+
testFn: toggleLinkTest,
258+
modifier: MODIFIERS.META
259+
},
260+
{
261+
precondition: () => Browser.isMac(),
262+
msg: '[Mac] cmd-k unlinks selected text if it was already linked',
263+
testFn: toggleLinkUnlinkTest,
264+
modifier: MODIFIERS.META
265+
},
266+
{
267+
precondition: () => Browser.isWin(),
268+
msg: '[Windows] ctrl-k links selected text',
269+
testFn: toggleLinkTest,
270+
modifier: MODIFIERS.CTRL
271+
},
272+
{
273+
precondition: () => Browser.isWin(),
274+
msg: '[Windows] ctrl-k unlinks selected text if it was already linked',
275+
testFn: toggleLinkUnlinkTest,
276+
modifier: MODIFIERS.CTRL
277+
},
278+
];
279+
280+
toggleTests.forEach(({precondition, msg, testFn, modifier}) => {
281+
if (!precondition()) {
282+
skip(msg);
283+
} else {
284+
test(msg, (assert) => {
285+
testFn(assert, modifier);
286+
});
287+
}
248288
});
249289

250290
test('new key commands can be registered', (assert) => {

tests/test-helpers.js

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -48,6 +48,7 @@ export default {
4848
test,
4949
module,
5050
skipInIE11,
51+
skip,
5152
wait,
5253
postEditor: { run, renderBuiltAbstract, MockEditor }
5354
};

0 commit comments

Comments
 (0)