From 324ffd5e407a0aa79b5095a967bc9b5d74417edc Mon Sep 17 00:00:00 2001 From: JC Brand Date: Tue, 14 Aug 2018 20:15:38 +0200 Subject: [PATCH] Let `@` trigger autocomplete with all possible options shown --- spec/autocomplete.js | 59 ++++++++++++++++++++++++++++-------- src/converse-autocomplete.js | 54 +++++++++++---------------------- src/utils/core.js | 3 +- 3 files changed, 66 insertions(+), 50 deletions(-) diff --git a/spec/autocomplete.js b/spec/autocomplete.js index 7fdd7a16c..492252574 100644 --- a/spec/autocomplete.js +++ b/spec/autocomplete.js @@ -15,6 +15,50 @@ return describe("A groupchat textarea", function () { + it("shows all autocompletion options when the user presses @", + mock.initConverseWithPromises( + null, ['rosterGroupsFetched'], {}, + function (done, _converse) { + + test_utils.openAndEnterChatRoom(_converse, 'lounge', 'localhost', 'tom') + .then(() => { + const view = _converse.chatboxviews.get('lounge@localhost'); + + ['dick', 'harry'].forEach((nick) => { + _converse.connection._dataRecv(test_utils.createRequest( + $pres({ + 'to': 'tom@localhost/resource', + 'from': `lounge@localhost/${nick}` + }) + .c('x', {xmlns: Strophe.NS.MUC_USER}) + .c('item', { + 'affiliation': 'none', + 'jid': `${nick}@localhost/resource`, + 'role': 'participant' + }))); + }); + + // Test that pressing @ brings up all options + const textarea = view.el.querySelector('textarea.chat-textarea'); + const at_event = { + 'target': textarea, + 'preventDefault': _.noop, + 'stopPropagation': _.noop, + 'keyCode': 50 + }; + view.keyPressed(at_event); + textarea.value = '@'; + view.keyUp(at_event); + + expect(view.el.querySelectorAll('.suggestion-box__results li').length).toBe(3); + expect(view.el.querySelector('.suggestion-box__results li[aria-selected="true"]').textContent).toBe('tom'); + expect(view.el.querySelector('.suggestion-box__results li:first-child').textContent).toBe('tom'); + expect(view.el.querySelector('.suggestion-box__results li:nth-child(2)').textContent).toBe('dick'); + expect(view.el.querySelector('.suggestion-box__results li:nth-child(3)').textContent).toBe('harry'); + done(); + }).catch(_.partial(console.error, _)); + })); + it("autocompletes when the user presses tab", mock.initConverseWithPromises( null, ['rosterGroupsFetched'], {}, @@ -102,7 +146,7 @@ 'stopPropagation': _.noop, 'keyCode': 13 // Enter }); - expect(textarea.value).toBe('hello s some2'); + expect(textarea.value).toBe('hello s some2 '); // Test that pressing tab twice selects presence = $pres({ @@ -122,18 +166,7 @@ view.keyPressed(tab_event); view.keyUp(tab_event); - expect(textarea.value).toBe('hello z3r0'); - - // Test that pressing @ brings up all options - const at_event = { - 'target': textarea, - 'preventDefault': _.noop, - 'stopPropagation': _.noop, - 'keyCode': 50 - }; - view.keyPressed(at_event); - view.keyUp(at_event); - textarea.value = 'hello z3r0 and @'; + expect(textarea.value).toBe('hello z3r0 '); done(); }).catch(_.partial(console.error, _)); diff --git a/src/converse-autocomplete.js b/src/converse-autocomplete.js index 1e09f1008..b819ee79f 100644 --- a/src/converse-autocomplete.js +++ b/src/converse-autocomplete.js @@ -193,8 +193,7 @@ if (this.auto_first && this.index === -1) { this.goto(0); } - - helpers.fire(this.input, "suggestion-box-open"); + this.trigger("suggestion-box-open"); } destroy () { @@ -250,18 +249,11 @@ } if (selected) { - const suggestion = this.suggestions[this.index], - allowed = helpers.fire(this.input, "suggestion-box-select", { - 'text': suggestion, - 'origin': origin || selected - }); - - if (allowed) { - this.insertValue(suggestion); - this.close({'reason': 'select'}); - this.auto_completing = false; - this.trigger("suggestion-box-selectcomplete", {'text': suggestion}); - } + const suggestion = this.suggestions[this.index]; + this.insertValue(suggestion); + this.close({'reason': 'select'}); + this.auto_completing = false; + this.trigger("suggestion-box-selectcomplete", {'text': suggestion}); } } @@ -309,17 +301,20 @@ return; } - let value = this.input.value; - if (this.match_current_word) { - value = u.getCurrentWord(this.input); - } - const list = typeof this._list === "function" ? this._list() : this._list; - if (list.length > 0 && ( - (value.length >= this.min_chars) || - (this.trigger_on_at && ev.keyCode === value.startsWith('@')) - )) { + if (list.length === 0) { + return; + } + let value = this.match_current_word ? u.getCurrentWord(this.input) : this.input.value; + + let ignore_min_chars = false; + if (this.trigger_on_at && value.startsWith('@')) { + ignore_min_chars = true; + value = value.slice('1'); + } + + if ((value.length >= this.min_chars) || ignore_min_chars) { this.index = -1; // Populate list with options that match this.ul.innerHTML = ""; @@ -402,19 +397,6 @@ } }, - fire (target, type, properties) { - const evt = document.createEvent("HTMLEvents"); - evt.initEvent(type, true, true ); - - for (var j in properties) { - if (!Object.prototype.hasOwnProperty.call(properties, j)) { - continue; - } - evt[j] = properties[j]; - } - return target.dispatchEvent(evt); - }, - regExpEscape (s) { return s.replace(/[-\\^$*+?.()|[\]{}]/g, "\\$&"); } diff --git a/src/utils/core.js b/src/utils/core.js index 07c295803..e1a754583 100644 --- a/src/utils/core.js +++ b/src/utils/core.js @@ -825,7 +825,8 @@ const cursor = input.selectionEnd || undefined, current_word = _.last(input.value.slice(0, cursor).split(' ')), value = input.value; - input.value = value.slice(0, cursor - current_word.length) + new_value + value.slice(cursor); + input.value = value.slice(0, cursor - current_word.length) + `${new_value} ` + value.slice(cursor); + input.selectionEnd = cursor - current_word.length + new_value.length + 1; }; u.isVisible = function (el) {