Skip to content

Commit a0aaa3a

Browse files
authored
fix(onTextInput): Ensure onTextInput is triggered by tab character (#479)
Change the event manager to delegate insertion of tab key to the input handler rather than simply inserting the string. Also: Lot of cleanup to tests. Fixes #400
1 parent 6036b90 commit a0aaa3a

File tree

6 files changed

+156
-197
lines changed

6 files changed

+156
-197
lines changed

src/js/editor/event-manager.js

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,6 @@ import {
66
} from 'mobiledoc-kit/utils/parse-utils';
77
import { filter, forEach } from 'mobiledoc-kit/utils/array-utils';
88
import Key from 'mobiledoc-kit/utils/key';
9-
import { TAB } from 'mobiledoc-kit/utils/characters';
109
import TextInputHandler from 'mobiledoc-kit/editor/text-input-handler';
1110
import SelectionManager from 'mobiledoc-kit/editor/selection-manager';
1211
import Browser from 'mobiledoc-kit/utils/browser';
@@ -173,8 +172,9 @@ export default class EventManager {
173172
editor.handleNewline(event);
174173
break;
175174
case key.isTab():
175+
// Handle tab here because it does not fire a `keypress` event
176176
event.preventDefault();
177-
editor.insertText(TAB);
177+
this._textInputHandler.handle(key.toString());
178178
break;
179179
}
180180
}

src/js/utils/key.js

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,6 @@
11
import Keycodes from './keycodes';
2+
import { TAB } from 'mobiledoc-kit/utils/characters';
3+
24
/**
35
* @typedef Direction
46
* @enum {number}
@@ -74,6 +76,7 @@ const Key = class Key {
7476
}
7577

7678
toString() {
79+
if (this.isTab()) { return TAB; }
7780
return String.fromCharCode(this.charCode);
7881
}
7982

tests/acceptance/basic-editor-test.js

Lines changed: 16 additions & 91 deletions
Original file line numberDiff line numberDiff line change
@@ -110,13 +110,7 @@ test('typing in empty post correctly adds a section to it', (assert) => {
110110
});
111111

112112
test('typing when on the end of a card is blocked', (assert) => {
113-
const mobiledoc = Helpers.mobiledoc.build(({post, cardSection}) => {
114-
return post([
115-
cardSection('my-card')
116-
]);
117-
});
118-
editor = new Editor({mobiledoc, cards});
119-
editor.render(editorElement);
113+
editor = Helpers.editor.buildFromText('[my-card]', {element: editorElement, cards});
120114

121115
let endingZWNJ = $('#editor')[0].firstChild.lastChild;
122116
Helpers.dom.moveCursorTo(editor, endingZWNJ, 0);
@@ -128,13 +122,7 @@ test('typing when on the end of a card is blocked', (assert) => {
128122
});
129123

130124
test('typing when on the start of a card is blocked', (assert) => {
131-
const mobiledoc = Helpers.mobiledoc.build(({post, cardSection}) => {
132-
return post([
133-
cardSection('my-card')
134-
]);
135-
});
136-
editor = new Editor({mobiledoc, cards});
137-
editor.render(editorElement);
125+
editor = Helpers.editor.buildFromText('[my-card]', {element: editorElement, cards});
138126

139127
let startingZWNJ = $('#editor')[0].firstChild.firstChild;
140128
Helpers.dom.moveCursorTo(editor, startingZWNJ, 0);
@@ -146,105 +134,42 @@ test('typing when on the start of a card is blocked', (assert) => {
146134
});
147135

148136
test('typing tab enters a tab character', (assert) => {
149-
let done = assert.async();
150-
assert.expect(3);
137+
editor = Helpers.editor.buildFromText('|', {element: editorElement});
151138

152-
let mobiledoc = Helpers.mobiledoc.build(({post}) => post());
153-
editor = new Editor({mobiledoc});
154-
editor.render(editorElement);
155-
156-
assert.hasElement('#editor');
157-
assert.hasNoElement('#editor p');
158-
159-
Helpers.dom.moveCursorTo(editor, $('#editor')[0]);
160139
Helpers.dom.insertText(editor, TAB);
161140
Helpers.dom.insertText(editor, 'Y');
162-
Helpers.wait(() => {
163-
let expectedPost = Helpers.postAbstract.build(({post, markupSection, marker}) => {
164-
return post([
165-
markupSection('p', [
166-
marker(`${TAB}Y`)
167-
])
168-
]);
169-
});
170-
assert.postIsSimilar(editor.post, expectedPost);
171-
done();
172-
});
141+
142+
let {post: expected} = Helpers.postAbstract.buildFromText(`${TAB}Y`);
143+
assert.postIsSimilar(editor.post, expected);
173144
});
174145

175146
// see https://github.com/bustlelabs/mobiledoc-kit/issues/215
176147
test('select-all and type text works ok', (assert) => {
177-
let done = assert.async();
178-
const mobiledoc = Helpers.mobiledoc.build(({post, markupSection, marker}) => {
179-
return post([
180-
markupSection('p', [marker('abc')])
181-
]);
182-
});
183-
editor = new Editor({mobiledoc, cards});
184-
editor.render(editorElement);
185-
186-
Helpers.dom.moveCursorTo(editor, editorElement.firstChild, 0);
187-
let post = editor.post;
188-
editor.selectRange(post.toRange());
148+
editor = Helpers.editor.buildFromText('<abc>', {element: editorElement});
189149

190150
assert.selectedText('abc', 'precond - abc is selected');
191151
assert.hasElement('#editor p:contains(abc)', 'precond - renders p');
192152

193-
Helpers.wait(() => {
194-
Helpers.dom.insertText(editor, 'X');
195-
196-
Helpers.wait(function() {
197-
assert.hasNoElement('#editor p:contains(abc)', 'replaces existing text');
198-
assert.hasElement('#editor p:contains(X)', 'inserts text');
199-
done();
200-
});
201-
});
153+
Helpers.dom.insertText(editor, 'X');
202154

155+
assert.hasNoElement('#editor p:contains(abc)', 'replaces existing text');
156+
assert.hasElement('#editor p:contains(X)', 'inserts text');
203157
});
204158

205159
test('typing enter splits lines, sets cursor', (assert) => {
206-
let done = assert.async();
207-
let mobiledoc = Helpers.mobiledoc.build(({post, markupSection, marker}) => {
208-
return post([
209-
markupSection('p', [ marker('hihey') ])
210-
]);
211-
});
212-
editor = new Editor({mobiledoc});
213-
editor.render(editorElement);
160+
editor = Helpers.editor.buildFromText('hi|hey', {element: editorElement});
214161

215-
assert.hasElement('#editor p');
162+
assert.hasElement('#editor p:contains(hihey)');
216163

217-
Helpers.dom.moveCursorTo(editor, $('#editor p')[0].firstChild, 2);
218164
Helpers.dom.insertText(editor, ENTER);
219-
Helpers.wait(() => {
220-
let expectedPost = Helpers.postAbstract.build(({post, markupSection, marker}) => {
221-
return post([
222-
markupSection('p', [
223-
marker(`hi`)
224-
]),
225-
markupSection('p', [
226-
marker(`hey`)
227-
])
228-
]);
229-
});
230-
assert.postIsSimilar(editor.post, expectedPost, 'correctly encoded');
231-
let expectedRange = editor.post.sections.tail.headPosition().toRange();
232-
assert.ok(expectedRange.isEqual(editor.range), 'range is at start of new section');
233-
done();
234-
});
165+
let {post: expected, range: expectedRange} = Helpers.postAbstract.buildFromText(['hi','|hey']);
166+
assert.postIsSimilar(editor.post, expected, 'correctly encoded');
167+
assert.rangeIsEqual(editor.range, Helpers.editor.retargetRange(expectedRange, editor.post));
235168
});
236169

237170
// see https://github.com/bustlelabs/mobiledoc-kit/issues/306
238171
test('adding/removing bold text between two bold markers works', (assert) => {
239-
editor = Helpers.mobiledoc.renderInto(editorElement, ({post, markupSection, marker, markup}) => {
240-
return post([
241-
markupSection('p', [
242-
marker('abc', [markup('b')]),
243-
marker('123', []),
244-
marker('def', [markup('b')])
245-
])
246-
]);
247-
});
172+
editor = Helpers.editor.buildFromText('*abc*123*def*', {element: editorElement});
248173

249174
// preconditions
250175
assert.hasElement('#editor b:contains(abc)');

tests/acceptance/editor-input-handlers-test.js

Lines changed: 38 additions & 44 deletions
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,11 @@
11
import Helpers from '../test-helpers';
22
import Range from 'mobiledoc-kit/utils/cursor/range';
33
import { NO_BREAK_SPACE } from 'mobiledoc-kit/renderers/editor-dom';
4+
import { TAB } from 'mobiledoc-kit/utils/characters';
45

56
const { module, test } = Helpers;
7+
const { editor: { buildFromText } } = Helpers;
8+
const { postAbstract: { DEFAULT_ATOM_NAME } } = Helpers;
69

710
let editor, editorElement;
811

@@ -12,6 +15,12 @@ function renderEditor(...args) {
1215
return editor;
1316
}
1417

18+
let atom = {
19+
name: DEFAULT_ATOM_NAME,
20+
type: 'dom',
21+
render() {}
22+
};
23+
1524
module('Acceptance: Editor: Text Input Handlers', {
1625
beforeEach() {
1726
editorElement = $('#editor')[0];
@@ -67,9 +76,7 @@ headerTests.forEach(({text, toInsert, headerTagName}) => {
6776
});
6877

6978
test(`typing "${text}" but not "${toInsert}" does not convert to ${headerTagName}`, (assert) => {
70-
renderEditor(({post, markupSection, marker}) => {
71-
return post([markupSection('p',[marker(text)])]);
72-
});
79+
editor = buildFromText(text, {element: editorElement});
7380
assert.hasElement('#editor p', 'precond - has p');
7481
Helpers.dom.insertText(editor, 'X');
7582

@@ -109,9 +116,7 @@ test('typing "* " at start of markup section does not remove it', (assert) => {
109116
return post([markupSection('p',[marker('*abc')])]);
110117
});
111118

112-
let position = editor.post.sections.head.headPosition();
113-
position.offset = 1;
114-
editor.selectRange(position);
119+
editor.selectRange(editor.post.sections.head.toPosition(1));
115120

116121
Helpers.dom.insertText(editor, ' ');
117122
assert.hasElement('#editor p:contains(* abc)', 'p is still there');
@@ -134,9 +139,7 @@ test('typing "* " inside of a list section does not create a new list section',
134139
});
135140

136141
test('typing "1 " converts to ol > li', (assert) => {
137-
renderEditor(({post, markupSection, marker}) => {
138-
return post([markupSection('p', [marker('1')])]);
139-
});
142+
editor = buildFromText(['1|'], {element: editorElement});
140143
Helpers.dom.insertText(editor, ' ');
141144
assert.hasNoElement('#editor p', 'p is gone');
142145
assert.hasElement('#editor ol > li', 'p -> "ol > li"');
@@ -159,9 +162,7 @@ test('typing "1 " converts to ol > li', (assert) => {
159162
});
160163

161164
test('typing "1. " converts to ol > li', (assert) => {
162-
renderEditor(({post, markupSection, marker}) => {
163-
return post([markupSection('p', [marker('1.')])]);
164-
});
165+
editor = buildFromText('1.|', {element: editorElement});
165166
Helpers.dom.insertText(editor, ' ');
166167
assert.hasNoElement('#editor p', 'p is gone');
167168
assert.hasElement('#editor ol > li', 'p -> "ol > li"');
@@ -171,15 +172,7 @@ test('typing "1. " converts to ol > li', (assert) => {
171172
});
172173

173174
test('an input handler will trigger anywhere in the text', (assert) => {
174-
let atom = {
175-
name: 'mention',
176-
type: 'dom',
177-
render() {}
178-
};
179-
180-
renderEditor(({post, markupSection, marker, atom}) => {
181-
return post([markupSection('p', [atom('mention', 'bob'), marker('abc'), atom('mention', 'sue')])]);
182-
}, {atoms: [atom]});
175+
editor = buildFromText('@abc@', {element: editorElement, atoms: [atom]});
183176

184177
let expandCount = 0;
185178
let lastMatches;
@@ -198,7 +191,7 @@ test('an input handler will trigger anywhere in the text', (assert) => {
198191
assert.deepEqual(lastMatches, ['@'], 'correct match at start');
199192

200193
// middle
201-
editor.selectRange(Range.create(editor.post.sections.head, '@'.length + 1 + 'ab'.length));
194+
editor.selectRange(editor.post.sections.head.toPosition('@'.length + 1 + 'ab'.length));
202195
Helpers.dom.insertText(editor, '@');
203196
assert.equal(expandCount, 2, 'expansion was run at middle');
204197
assert.deepEqual(lastMatches, ['@'], 'correct match at middle');
@@ -211,15 +204,7 @@ test('an input handler will trigger anywhere in the text', (assert) => {
211204
});
212205

213206
test('an input handler can provide a `match` instead of `text`', (assert) => {
214-
let atom = {
215-
name: 'mention',
216-
type: 'dom',
217-
render() {}
218-
};
219-
220-
renderEditor(({post, markupSection, marker, atom}) => {
221-
return post([markupSection('p', [atom('mention', 'bob'), marker('abc'), atom('mention', 'sue')])]);
222-
}, {atoms: [atom]});
207+
editor = buildFromText('@abc@', {element: editorElement, atoms: [atom]});
223208

224209
let expandCount = 0;
225210
let lastMatches;
@@ -239,7 +224,7 @@ test('an input handler can provide a `match` instead of `text`', (assert) => {
239224
assert.deepEqual(lastMatches, regex.exec('abX'), 'correct match at start');
240225

241226
// middle
242-
editor.selectRange(Range.create(editor.post.sections.head, 'abX'.length + 1 + 'ab'.length));
227+
editor.selectRange(editor.post.sections.head.toPosition('abX'.length + 1 + 'ab'.length));
243228
Helpers.dom.insertText(editor, '..X');
244229
assert.equal(expandCount, 2, 'expansion was run at middle');
245230
assert.deepEqual(lastMatches, regex.exec('..X'), 'correct match at middle');
@@ -252,15 +237,7 @@ test('an input handler can provide a `match` instead of `text`', (assert) => {
252237
});
253238

254239
test('an input handler can provide a `match` that matches at start and end', (assert) => {
255-
let atom = {
256-
name: 'mention',
257-
type: 'dom',
258-
render() {}
259-
};
260-
261-
renderEditor(({post, markupSection, marker, atom}) => {
262-
return post([markupSection('p', [atom('mention', 'bob'), marker('abc'), atom('mention', 'sue')])]);
263-
}, {atoms: [atom]});
240+
editor = Helpers.editor.buildFromText(['@abc@'], {element: editorElement, atoms: [atom]});
264241

265242
let expandCount = 0;
266243
let lastMatches;
@@ -274,18 +251,35 @@ test('an input handler can provide a `match` that matches at start and end', (as
274251
});
275252

276253
// at start
277-
editor.selectRange(new Range(editor.post.headPosition()));
254+
editor.selectRange(editor.post.headPosition());
278255
Helpers.dom.insertText(editor, '123');
279256
assert.equal(expandCount, 1, 'expansion was run at start');
280257
assert.deepEqual(lastMatches, regex.exec('123'), 'correct match at start');
281258

282259
// middle
283-
editor.selectRange(Range.create(editor.post.sections.head, '123'.length + 2));
260+
editor.selectRange(editor.post.sections.head.toPosition('123'.length+2));
284261
Helpers.dom.insertText(editor, '123');
285262
assert.equal(expandCount, 1, 'expansion was not run at middle');
286263

287264
// end
288-
editor.selectRange(new Range(editor.post.tailPosition()));
265+
editor.selectRange(editor.post.tailPosition());
289266
Helpers.dom.insertText(editor, '123');
290267
assert.equal(expandCount, 1, 'expansion was not run at end');
291268
});
269+
270+
// See https://github.com/bustlelabs/mobiledoc-kit/issues/400
271+
test('input handler can be triggered by TAB', (assert) => {
272+
editor = Helpers.editor.buildFromText('abc|', {element: editorElement});
273+
274+
let didMatch;
275+
editor.onTextInput({
276+
match: /abc\t/,
277+
run() {
278+
didMatch = true;
279+
}
280+
});
281+
282+
Helpers.dom.insertText(editor, TAB);
283+
284+
assert.ok(didMatch);
285+
});

0 commit comments

Comments
 (0)