From e40cbd016bbf472e83ceefa2239f7f51cc4b6592 Mon Sep 17 00:00:00 2001 From: Matthieu Dumont Date: Fri, 23 Dec 2016 16:16:07 +0100 Subject: [PATCH] feat(appendTo): new parameter --- src/angular/directive.js | 3 +- src/autocomplete/css.js | 12 + src/autocomplete/dataset.js | 5 +- src/autocomplete/dropdown.js | 41 +++- src/autocomplete/typeahead.js | 54 ++++- src/jquery/plugin.js | 3 +- src/standalone/index.js | 3 +- test/integration/test.html | 39 +-- test/integration/test.js | 430 ++++++++++++++++++---------------- test/playground.html | 4 +- test/playground_angular.html | 2 +- test/playground_jquery.html | 2 + test/unit/typeahead_spec.js | 14 ++ 13 files changed, 371 insertions(+), 241 deletions(-) diff --git a/src/angular/directive.js b/src/angular/directive.js index 21cee165b..ca46a581b 100644 --- a/src/angular/directive.js +++ b/src/angular/directive.js @@ -75,7 +75,8 @@ angular.module('algolia.autocomplete', []) debug: scope.options.debug, cssClasses: scope.options.cssClasses, datasets: scope.datasets, - keyboardShortcuts: scope.options.keyboardShortcuts + keyboardShortcuts: scope.options.keyboardShortcuts, + appendTo: scope.options.appendTo }); } diff --git a/src/autocomplete/css.js b/src/autocomplete/css.js index 7bc627ea4..ede78af9c 100644 --- a/src/autocomplete/css.js +++ b/src/autocomplete/css.js @@ -61,6 +61,18 @@ var css = { cursor: 'cursor', dataset: 'dataset', empty: 'empty' + }, + appendTo: { + wrapper: { + position: 'absolute', + zIndex: '100', + display: 'none' + }, + input: {}, + inputWithNoHint: {}, + dropdown: { + display: 'block' + } } }; diff --git a/src/autocomplete/dataset.js b/src/autocomplete/dataset.js index fdef19b28..6725df1ee 100644 --- a/src/autocomplete/dataset.js +++ b/src/autocomplete/dataset.js @@ -37,6 +37,7 @@ function Dataset(o) { this.templates = getTemplates(o.templates, this.displayFn); + this.css = _.mixin({}, css, o.appendTo ? css.appendTo : {}); this.cssClasses = _.mixin({}, css.defaultClasses, o.cssClasses || {}); var clazz = _.className(this.cssClasses.prefix, this.cssClasses.dataset); @@ -126,7 +127,7 @@ _.mixin(Dataset.prototype, EventEmitter, { var suggestionsHtml = html.suggestions. replace('%PREFIX%', this.cssClasses.prefix). replace('%SUGGESTIONS%', this.cssClasses.suggestions); - $suggestions = DOM.element(suggestionsHtml).css(css.suggestions); + $suggestions = DOM.element(suggestionsHtml).css(this.css.suggestions); // jQuery#append doesn't support arrays as the first argument // until version 1.8, see http://bugs.jquery.com/ticket/11231 @@ -147,7 +148,7 @@ _.mixin(Dataset.prototype, EventEmitter, { $el.data(datasetKey, that.name); $el.data(valueKey, that.displayFn(suggestion) || undefined); // this led to undefined return value $el.data(datumKey, JSON.stringify(suggestion)); - $el.children().each(function() { DOM.element(this).css(css.suggestionChild); }); + $el.children().each(function() { DOM.element(this).css(self.css.suggestionChild); }); return $el; } diff --git a/src/autocomplete/dropdown.js b/src/autocomplete/dropdown.js index d5fe8bd39..74103a30f 100644 --- a/src/autocomplete/dropdown.js +++ b/src/autocomplete/dropdown.js @@ -31,8 +31,10 @@ function Dropdown(o) { this.isOpen = false; this.isEmpty = true; this.minLength = o.minLength || 0; - this.cssClasses = _.mixin({}, css.defaultClasses, o.cssClasses || {}); this.templates = {}; + this.appendTo = o.appendTo || false; + this.css = _.mixin({}, css, o.appendTo ? css.appendTo : {}); + this.cssClasses = o.cssClasses = _.mixin({}, css.defaultClasses, o.cssClasses || {}); // bound functions onSuggestionClick = _.bind(this._onSuggestionClick, this); @@ -45,6 +47,11 @@ function Dropdown(o) { .on('mouseenter.aa', cssClass, onSuggestionMouseEnter) .on('mouseleave.aa', cssClass, onSuggestionMouseLeave); + this.$input = o.input; + this.$wrapper = o.wrapper; + + this.$container = o.appendTo ? this.$wrapper : this.$menu; + if (o.templates && o.templates.header) { this.templates.header = _.templatify(o.templates.header); this.$menu.prepend(this.templates.header()); @@ -73,6 +80,11 @@ function Dropdown(o) { this.templates.footer = _.templatify(o.templates.footer); this.$menu.append(this.templates.footer()); } + + var self = this; + DOM.element(window).resize(function() { + self._redraw(); + }); } // instance methods @@ -162,17 +174,38 @@ _.mixin(Dropdown.prototype, EventEmitter, { }, _hide: function() { - this.$menu.hide(); + this.$container.hide(); }, _show: function() { // can't use jQuery#show because $menu is a span element we want // display: block; not dislay: inline; - this.$menu.css('display', 'block'); + this.$container.css('display', 'block'); + + this._redraw(); this.trigger('shown'); }, + _redraw: function redraw() { + if (!this.isOpen || !this.appendTo) return; + + var inputRect = this.$input[0].getBoundingClientRect(); + + this.$wrapper.css('width', inputRect.width + 'px'); + this.$wrapper.css('top', 0 + 'px'); + this.$wrapper.css('left', 0 + 'px'); + + var wrapperRect = this.$wrapper[0].getBoundingClientRect(); + + var top = inputRect.bottom - wrapperRect.top; + this.$wrapper.css('top', top + 'px'); + var left = inputRect.left - wrapperRect.left; + this.$wrapper.css('left', left + 'px'); + + this.trigger('redrawn'); + }, + _getSuggestions: function getSuggestions() { return this.$menu.find(_.className(this.cssClasses.prefix, this.cssClasses.suggestion)); }, @@ -272,7 +305,7 @@ _.mixin(Dropdown.prototype, EventEmitter, { }, setLanguageDirection: function setLanguageDirection(dir) { - this.$menu.css(dir === 'ltr' ? css.ltr : css.rtl); + this.$menu.css(dir === 'ltr' ? this.css.ltr : this.css.rtl); }, moveCursorUp: function moveCursorUp() { diff --git a/src/autocomplete/typeahead.js b/src/autocomplete/typeahead.js index 83687b3ad..6a500bd27 100644 --- a/src/autocomplete/typeahead.js +++ b/src/autocomplete/typeahead.js @@ -31,12 +31,22 @@ function Typeahead(o) { this.autoselectOnBlur = !!o.autoselectOnBlur; this.openOnFocus = !!o.openOnFocus; this.minLength = _.isNumber(o.minLength) ? o.minLength : 1; + + o.hint = !!o.hint; + + if (o.hint && o.appendTo) { + throw new Error('[autocomplete.js] hint and appendTo options can\'t be used at the same time'); + } + + this.css = o.css = _.mixin({}, css, o.appendTo ? css.appendTo : {}); this.cssClasses = o.cssClasses = _.mixin({}, css.defaultClasses, o.cssClasses || {}); - this.$node = buildDom(o); - $menu = this.$node.find(_.className(this.cssClasses.prefix, this.cssClasses.dropdownMenu)); - $input = this.$node.find(_.className(this.cssClasses.prefix, this.cssClasses.input)); - $hint = this.$node.find(_.className(this.cssClasses.prefix, this.cssClasses.hint)); + var domElts = buildDom(o); + + this.$node = domElts.wrapper; + $menu = domElts.menu; + $input = domElts.input; + $hint = domElts.hint; if (o.dropdownMenuContainer) { DOM.element(o.dropdownMenuContainer) @@ -64,7 +74,16 @@ function Typeahead(o) { this.eventBus = o.eventBus || new EventBus({el: $input}); - this.dropdown = new Typeahead.Dropdown({menu: $menu, datasets: o.datasets, templates: o.templates, cssClasses: this.cssClasses, minLength: this.minLength}) + this.dropdown = new Typeahead.Dropdown({ + input: $input, + appendTo: o.appendTo, + wrapper: this.$node, + menu: $menu, + datasets: o.datasets, + templates: o.templates, + cssClasses: o.cssClasses, + minLength: this.minLength + }) .onSync('suggestionClicked', this._onSuggestionClicked, this) .onSync('cursorMoved', this._onCursorMoved, this) .onSync('cursorRemoved', this._onCursorRemoved, this) @@ -439,21 +458,21 @@ function buildDom(options) { var $hint; $input = DOM.element(options.input); - $wrapper = DOM.element(html.wrapper.replace('%ROOT%', options.cssClasses.root)).css(css.wrapper); + $wrapper = DOM.element(html.wrapper.replace('%ROOT%', options.cssClasses.root)).css(options.css.wrapper); // override the display property with the table-cell value // if the parent element is a table and the original input was a block // -> https://github.com/algolia/autocomplete.js/issues/16 - if ($input.css('display') === 'block' && $input.parent().css('display') === 'table') { + if (!options.appendTo && $input.css('display') === 'block' && $input.parent().css('display') === 'table') { $wrapper.css('display', 'table-cell'); } var dropdownHtml = html.dropdown. replace('%PREFIX%', options.cssClasses.prefix). replace('%DROPDOWN_MENU%', options.cssClasses.dropdownMenu); - $dropdown = DOM.element(dropdownHtml).css(css.dropdown); + $dropdown = DOM.element(dropdownHtml).css(options.css.dropdown); if (options.templates && options.templates.dropdownMenu) { $dropdown.html(_.templatify(options.templates.dropdownMenu)()); } - $hint = $input.clone().css(css.hint).css(getBackgroundStyles($input)); + $hint = $input.clone().css(options.css.hint).css(getBackgroundStyles($input)); $hint .val('') @@ -477,7 +496,7 @@ function buildDom(options) { $input .addClass(_.className(options.cssClasses.prefix, options.cssClasses.input, true)) .attr({autocomplete: 'off', spellcheck: false}) - .css(options.hint ? css.input : css.inputWithNoHint); + .css(options.hint ? options.css.input : options.css.inputWithNoHint); // ie7 does not like it when dir is set to auto try { @@ -488,11 +507,20 @@ function buildDom(options) { // ignore } - return $input - .wrap($wrapper) - .parent() + $wrapper = options.appendTo + ? $wrapper.appendTo(DOM.element(options.appendTo).eq(0)).eq(0) + : $input.wrap($wrapper).parent(); + + $wrapper .prepend(options.hint ? $hint : null) .append($dropdown); + + return { + wrapper: $wrapper, + input: $input, + hint: $hint, + menu: $dropdown + }; } function getBackgroundStyles($el) { diff --git a/src/jquery/plugin.js b/src/jquery/plugin.js index a9b239001..2b09d2a01 100644 --- a/src/jquery/plugin.js +++ b/src/jquery/plugin.js @@ -62,7 +62,8 @@ methods = { debug: o.debug, cssClasses: o.cssClasses, datasets: datasets, - keyboardShortcuts: o.keyboardShortcuts + keyboardShortcuts: o.keyboardShortcuts, + appendTo: o.appendTo }); $input.data(typeaheadKey, typeahead); diff --git a/src/standalone/index.js b/src/standalone/index.js index 2d4681bcc..53fe964a0 100644 --- a/src/standalone/index.js +++ b/src/standalone/index.js @@ -46,7 +46,8 @@ function autocomplete(selector, options, datasets, typeaheadObject) { debug: options.debug, cssClasses: options.cssClasses, datasets: datasets, - keyboardShortcuts: options.keyboardShortcuts + keyboardShortcuts: options.keyboardShortcuts, + appendTo: options.appendTo }); $input.data(typeaheadKey, typeahead); diff --git a/test/integration/test.html b/test/integration/test.html index bf783d464..2737a753b 100644 --- a/test/integration/test.html +++ b/test/integration/test.html @@ -95,24 +95,33 @@ 'this is a very long value so deal with it otherwise you gonna have a hard time' ]; - $('#states').autocomplete({ }, [ - { - displayKey: 'name', - source: function(q, cb) { - var res = []; - if (!q) { - cb([]); - return; - } - for (var i = 0; i < states.length; ++i) { - if (states[i].toLowerCase().indexOf(q.toLowerCase()) === 0) { - res.push({ name: states[i] }); + function buildAutocomplete (options) { + if (options === undefined) options = { }; + if (window.autocomplete) { + window.autocomplete.autocomplete.destroy(); + window.autocomplete = null; + } + window.autocomplete = $('#states').autocomplete(options, [ + { + displayKey: 'name', + source: function(q, cb) { + var res = []; + if (!q) { + cb([]); + return; + } + for (var i = 0; i < states.length; ++i) { + if (states[i].toLowerCase().indexOf(q.toLowerCase()) === 0) { + res.push({ name: states[i] }); + } } + cb(res); } - cb(res); } - } - ]); + ]); + } + + buildAutocomplete(); diff --git a/test/integration/test.js b/test/integration/test.js index 7926b8067..dc4ca0015 100644 --- a/test/integration/test.js +++ b/test/integration/test.js @@ -99,303 +99,329 @@ describe('jquery-typeahead.js', function() { }); }); - describe('on blur', function() { - it('should close dropdown', function(done) { - driver.run(function*() { - yield input.click(); - yield input.type('mi'); - expect(yield dropdown.isDisplayed()).to.equal(true); + function testSuite () { + describe('on blur', function() { + it('should close dropdown', function(done) { + driver.run(function*() { + yield input.click(); + yield input.type('mi'); + expect(yield dropdown.isDisplayed()).to.equal(true); + + yield body.click(); + expect(yield dropdown.isDisplayed()).to.equal(false); + + done(); + }); + }); + + it('should clear hint', function(done) { + driver.run(function*() { + yield input.click(); + yield input.type('mi'); + expect(yield hint.getValue()).to.equal('michigan'); - yield body.click(); - expect(yield dropdown.isDisplayed()).to.equal(false); + yield body.click(); + expect(yield hint.getValue()).to.equal(''); - done(); + done(); + }); }); }); - it('should clear hint', function(done) { - driver.run(function*() { - yield input.click(); - yield input.type('mi'); - expect(yield hint.getValue()).to.equal('michigan'); + describe('on query change', function() { + it('should open dropdown if suggestions', function(done) { + driver.run(function*() { + yield input.click(); + yield input.type('mi'); - yield body.click(); - expect(yield hint.getValue()).to.equal(''); + expect(yield dropdown.isDisplayed()).to.equal(true); - done(); + done(); + }); }); - }); - }); - describe('on query change', function() { - it('should open dropdown if suggestions', function(done) { - driver.run(function*() { - yield input.click(); - yield input.type('mi'); + it('should position the dropdown correctly', function(done) { + driver.run(function*() { + yield input.click(); + yield input.type('mi'); + + var inputPos = yield input.getLocation(); + var dropdownPos = yield dropdown.getLocation(); - expect(yield dropdown.isDisplayed()).to.equal(true); + expect(inputPos.x >= dropdownPos.x - 2 && inputPos.x <= dropdownPos.x + 2).to.equal(true); + expect(dropdownPos.y > inputPos.y).to.equal(true); - done(); + done(); + }); }); - }); - it('should close dropdown if no suggestions', function(done) { - driver.run(function*() { - yield input.click(); - yield input.type('huh?'); + it('should close dropdown if no suggestions', function(done) { + driver.run(function*() { + yield input.click(); + yield input.type('huh?'); - expect(yield dropdown.isDisplayed()).to.equal(false); + expect(yield dropdown.isDisplayed()).to.equal(false); - done(); + done(); + }); }); - }); - it('should render suggestions if suggestions', function(done) { - driver.run(function*() { - var suggestions; + it('should render suggestions if suggestions', function(done) { + driver.run(function*() { + var suggestions; - yield input.click(); - yield input.type('mi'); + yield input.click(); + yield input.type('mi'); - suggestions = yield dropdown.elementsByClassName('aa-suggestion'); + suggestions = yield dropdown.elementsByClassName('aa-suggestion'); - expect(suggestions).to.have.length('4'); - expect(yield suggestions[0].text()).to.equal('Michigan'); - expect(yield suggestions[1].text()).to.equal('Minnesota'); - expect(yield suggestions[2].text()).to.equal('Mississippi'); - expect(yield suggestions[3].text()).to.equal('Missouri'); + expect(suggestions).to.have.length('4'); + expect(yield suggestions[0].text()).to.equal('Michigan'); + expect(yield suggestions[1].text()).to.equal('Minnesota'); + expect(yield suggestions[2].text()).to.equal('Mississippi'); + expect(yield suggestions[3].text()).to.equal('Missouri'); - done(); + done(); + }); }); - }); - it('should show hint if top suggestion is a match', function(done) { - driver.run(function*() { - yield input.click(); - yield input.type('mi'); + it('should show hint if top suggestion is a match', function(done) { + driver.run(function*() { + yield input.click(); + yield input.type('mi'); - expect(yield hint.getValue()).to.equal('michigan'); + expect(yield hint.getValue()).to.equal('michigan'); - done(); + done(); + }); }); - }); - it('should not show hint if top suggestion is not a match', function(done) { - driver.run(function*() { - yield input.click(); - yield input.type('ham'); + it('should not show hint if top suggestion is not a match', function(done) { + driver.run(function*() { + yield input.click(); + yield input.type('ham'); - expect(yield hint.getValue()).to.equal(''); + expect(yield hint.getValue()).to.equal(''); - done(); + done(); + }); }); - }); - it('should not show hint if there is query overflow', function(done) { - driver.run(function*() { - yield input.click(); - yield input.type('this is a very long value so deal with it otherwise'); + it('should not show hint if there is query overflow', function(done) { + driver.run(function*() { + yield input.click(); + yield input.type('this is a very long value so deal with it otherwise'); - expect(yield hint.getValue()).to.equal(''); + expect(yield hint.getValue()).to.equal(''); - done(); + done(); + }); }); }); - }); - describe('on up arrow', function() { - it('should cycle through suggestions', function(done) { - driver.run(function*() { - var suggestions; + describe('on up arrow', function() { + it('should cycle through suggestions', function(done) { + driver.run(function*() { + var suggestions; - yield input.click(); - yield input.type('mi'); + yield input.click(); + yield input.type('mi'); - suggestions = yield dropdown.elementsByClassName('aa-suggestion'); + suggestions = yield dropdown.elementsByClassName('aa-suggestion'); - yield input.type(wd.SPECIAL_KEYS['Up arrow']); - expect(yield input.getValue()).to.equal('Missouri'); - expect(yield suggestions[3].getAttribute('class')).to.equal('aa-suggestion aa-cursor'); + yield input.type(wd.SPECIAL_KEYS['Up arrow']); + expect(yield input.getValue()).to.equal('Missouri'); + expect(yield suggestions[3].getAttribute('class')).to.equal('aa-suggestion aa-cursor'); - yield input.type(wd.SPECIAL_KEYS['Up arrow']); - expect(yield input.getValue()).to.equal('Mississippi'); - expect(yield suggestions[2].getAttribute('class')).to.equal('aa-suggestion aa-cursor'); + yield input.type(wd.SPECIAL_KEYS['Up arrow']); + expect(yield input.getValue()).to.equal('Mississippi'); + expect(yield suggestions[2].getAttribute('class')).to.equal('aa-suggestion aa-cursor'); - yield input.type(wd.SPECIAL_KEYS['Up arrow']); - expect(yield input.getValue()).to.equal('Minnesota'); - expect(yield suggestions[1].getAttribute('class')).to.equal('aa-suggestion aa-cursor'); + yield input.type(wd.SPECIAL_KEYS['Up arrow']); + expect(yield input.getValue()).to.equal('Minnesota'); + expect(yield suggestions[1].getAttribute('class')).to.equal('aa-suggestion aa-cursor'); - yield input.type(wd.SPECIAL_KEYS['Up arrow']); - expect(yield input.getValue()).to.equal('Michigan'); - expect(yield suggestions[0].getAttribute('class')).to.equal('aa-suggestion aa-cursor'); + yield input.type(wd.SPECIAL_KEYS['Up arrow']); + expect(yield input.getValue()).to.equal('Michigan'); + expect(yield suggestions[0].getAttribute('class')).to.equal('aa-suggestion aa-cursor'); - yield input.type(wd.SPECIAL_KEYS['Up arrow']); - expect(yield input.getValue()).to.equal('mi'); - expect(yield suggestions[0].getAttribute('class')).to.equal('aa-suggestion'); - expect(yield suggestions[1].getAttribute('class')).to.equal('aa-suggestion'); - expect(yield suggestions[2].getAttribute('class')).to.equal('aa-suggestion'); - expect(yield suggestions[3].getAttribute('class')).to.equal('aa-suggestion'); + yield input.type(wd.SPECIAL_KEYS['Up arrow']); + expect(yield input.getValue()).to.equal('mi'); + expect(yield suggestions[0].getAttribute('class')).to.equal('aa-suggestion'); + expect(yield suggestions[1].getAttribute('class')).to.equal('aa-suggestion'); + expect(yield suggestions[2].getAttribute('class')).to.equal('aa-suggestion'); + expect(yield suggestions[3].getAttribute('class')).to.equal('aa-suggestion'); - done(); + done(); + }); }); }); - }); - describe('on down arrow', function() { - it('should cycle through suggestions', function(done) { - driver.run(function*() { - var suggestions; + describe('on down arrow', function() { + it('should cycle through suggestions', function(done) { + driver.run(function*() { + var suggestions; - yield input.click(); - yield input.type('mi'); + yield input.click(); + yield input.type('mi'); - suggestions = yield dropdown.elementsByClassName('aa-suggestion'); + suggestions = yield dropdown.elementsByClassName('aa-suggestion'); - yield input.type(wd.SPECIAL_KEYS['Down arrow']); - expect(yield input.getValue()).to.equal('Michigan'); - expect(yield suggestions[0].getAttribute('class')).to.equal('aa-suggestion aa-cursor'); + yield input.type(wd.SPECIAL_KEYS['Down arrow']); + expect(yield input.getValue()).to.equal('Michigan'); + expect(yield suggestions[0].getAttribute('class')).to.equal('aa-suggestion aa-cursor'); - yield input.type(wd.SPECIAL_KEYS['Down arrow']); - expect(yield input.getValue()).to.equal('Minnesota'); - expect(yield suggestions[1].getAttribute('class')).to.equal('aa-suggestion aa-cursor'); + yield input.type(wd.SPECIAL_KEYS['Down arrow']); + expect(yield input.getValue()).to.equal('Minnesota'); + expect(yield suggestions[1].getAttribute('class')).to.equal('aa-suggestion aa-cursor'); - yield input.type(wd.SPECIAL_KEYS['Down arrow']); - expect(yield input.getValue()).to.equal('Mississippi'); - expect(yield suggestions[2].getAttribute('class')).to.equal('aa-suggestion aa-cursor'); + yield input.type(wd.SPECIAL_KEYS['Down arrow']); + expect(yield input.getValue()).to.equal('Mississippi'); + expect(yield suggestions[2].getAttribute('class')).to.equal('aa-suggestion aa-cursor'); - yield input.type(wd.SPECIAL_KEYS['Down arrow']); - expect(yield input.getValue()).to.equal('Missouri'); - expect(yield suggestions[3].getAttribute('class')).to.equal('aa-suggestion aa-cursor'); + yield input.type(wd.SPECIAL_KEYS['Down arrow']); + expect(yield input.getValue()).to.equal('Missouri'); + expect(yield suggestions[3].getAttribute('class')).to.equal('aa-suggestion aa-cursor'); - yield input.type(wd.SPECIAL_KEYS['Down arrow']); - expect(yield input.getValue()).to.equal('mi'); - expect(yield suggestions[0].getAttribute('class')).to.equal('aa-suggestion'); - expect(yield suggestions[1].getAttribute('class')).to.equal('aa-suggestion'); - expect(yield suggestions[2].getAttribute('class')).to.equal('aa-suggestion'); - expect(yield suggestions[3].getAttribute('class')).to.equal('aa-suggestion'); + yield input.type(wd.SPECIAL_KEYS['Down arrow']); + expect(yield input.getValue()).to.equal('mi'); + expect(yield suggestions[0].getAttribute('class')).to.equal('aa-suggestion'); + expect(yield suggestions[1].getAttribute('class')).to.equal('aa-suggestion'); + expect(yield suggestions[2].getAttribute('class')).to.equal('aa-suggestion'); + expect(yield suggestions[3].getAttribute('class')).to.equal('aa-suggestion'); - done(); + done(); + }); }); }); - }); - describe('on escape', function() { - it('should close dropdown', function(done) { - driver.run(function*() { - yield input.click(); - yield input.type('mi'); - expect(yield dropdown.isDisplayed()).to.equal(true); + describe('on escape', function() { + it('should close dropdown', function(done) { + driver.run(function*() { + yield input.click(); + yield input.type('mi'); + expect(yield dropdown.isDisplayed()).to.equal(true); - yield input.type(wd.SPECIAL_KEYS['Escape']); - expect(yield dropdown.isDisplayed()).to.equal(false); + yield input.type(wd.SPECIAL_KEYS['Escape']); + expect(yield dropdown.isDisplayed()).to.equal(false); - done(); + done(); + }); }); - }); - it('should clear hint', function(done) { - driver.run(function*() { - yield input.click(); - yield input.type('mi'); - expect(yield hint.getValue()).to.equal('michigan'); + it('should clear hint', function(done) { + driver.run(function*() { + yield input.click(); + yield input.type('mi'); + expect(yield hint.getValue()).to.equal('michigan'); - yield input.type(wd.SPECIAL_KEYS['Escape']); - expect(yield hint.getValue()).to.equal(''); + yield input.type(wd.SPECIAL_KEYS['Escape']); + expect(yield hint.getValue()).to.equal(''); - done(); + done(); + }); }); }); - }); - describe('on tab', function() { - it('should autocomplete if hint is present', function(done) { - driver.run(function*() { - yield input.click(); - yield input.type('mi'); + describe('on tab', function() { + it('should autocomplete if hint is present', function(done) { + driver.run(function*() { + yield input.click(); + yield input.type('mi'); - yield input.type(wd.SPECIAL_KEYS['Tab']); - expect(yield input.getValue()).to.equal('Michigan'); + yield input.type(wd.SPECIAL_KEYS['Tab']); + expect(yield input.getValue()).to.equal('Michigan'); - done(); + done(); + }); }); - }); - it('should select if cursor is on suggestion', function(done) { - driver.run(function*() { - var suggestions; + it('should select if cursor is on suggestion', function(done) { + driver.run(function*() { + var suggestions; - yield input.click(); - yield input.type('mi'); + yield input.click(); + yield input.type('mi'); - suggestions = yield dropdown.elementsByClassName('aa-suggestion'); - yield input.type(wd.SPECIAL_KEYS['Down arrow']); - yield input.type(wd.SPECIAL_KEYS['Down arrow']); - yield input.type(wd.SPECIAL_KEYS['Tab']); + suggestions = yield dropdown.elementsByClassName('aa-suggestion'); + yield input.type(wd.SPECIAL_KEYS['Down arrow']); + yield input.type(wd.SPECIAL_KEYS['Down arrow']); + yield input.type(wd.SPECIAL_KEYS['Tab']); - expect(yield dropdown.isDisplayed()).to.equal(false); - expect(yield input.getValue()).to.equal('Minnesota'); + expect(yield dropdown.isDisplayed()).to.equal(false); + expect(yield input.getValue()).to.equal('Minnesota'); - done(); + done(); + }); }); }); - }); - describe('on right arrow', function() { - it('should autocomplete if hint is present', function(done) { - driver.run(function*() { - yield input.click(); - yield input.type('mi'); + describe('on right arrow', function() { + it('should autocomplete if hint is present', function(done) { + driver.run(function*() { + yield input.click(); + yield input.type('mi'); - yield input.type(wd.SPECIAL_KEYS['Right arrow']); - expect(yield input.getValue()).to.equal('Michigan'); + yield input.type(wd.SPECIAL_KEYS['Right arrow']); + expect(yield input.getValue()).to.equal('Michigan'); - done(); + done(); + }); }); }); - }); - describe('on suggestion click', function() { - it('should select suggestion', function(done) { - if (browser[0] === 'firefox') { - // crazy Firefox issue, skip it - done(); - return; - } - driver.run(function*() { - var suggestions; + describe('on suggestion click', function() { + it('should select suggestion', function(done) { + if (browser[0] === 'firefox') { + // crazy Firefox issue, skip it + done(); + return; + } + driver.run(function*() { + var suggestions; - yield input.click(); - yield input.type('mi'); + yield input.click(); + yield input.type('mi'); - suggestions = yield dropdown.elementsByClassName('aa-suggestion'); - yield suggestions[1].click(); + suggestions = yield dropdown.elementsByClassName('aa-suggestion'); + yield suggestions[1].click(); - expect(yield dropdown.isDisplayed()).to.equal(false); - expect(yield input.getValue()).to.equal('Minnesota'); + expect(yield dropdown.isDisplayed()).to.equal(false); + expect(yield input.getValue()).to.equal('Minnesota'); - done(); + done(); + }); }); }); - }); - describe('on enter', function() { - it('should select if cursor is on suggestion', function(done) { - driver.run(function*() { - var suggestions; + describe('on enter', function() { + it('should select if cursor is on suggestion', function(done) { + driver.run(function*() { + var suggestions; - yield input.click(); - yield input.type('mi'); + yield input.click(); + yield input.type('mi'); - suggestions = yield dropdown.elementsByClassName('aa-suggestion'); - yield input.type(wd.SPECIAL_KEYS['Down arrow']); - yield input.type(wd.SPECIAL_KEYS['Down arrow']); - yield input.type(wd.SPECIAL_KEYS['Return']); + suggestions = yield dropdown.elementsByClassName('aa-suggestion'); + yield input.type(wd.SPECIAL_KEYS['Down arrow']); + yield input.type(wd.SPECIAL_KEYS['Down arrow']); + yield input.type(wd.SPECIAL_KEYS['Return']); - expect(yield dropdown.isDisplayed()).to.equal(false); - expect(yield input.getValue()).to.equal('Minnesota'); + expect(yield dropdown.isDisplayed()).to.equal(false); + expect(yield input.getValue()).to.equal('Minnesota'); - done(); + done(); + }); }); }); + } + + testSuite(); + describe('appendTo', function () { + before('all', function*() { + yield this.execute("buildAutocomplete({hint: false, appendTo: 'body'})"); + }); + + testSuite(); }); }); diff --git a/test/playground.html b/test/playground.html index f1bb92c3c..e9a5a6613 100644 --- a/test/playground.html +++ b/test/playground.html @@ -50,7 +50,7 @@

Multi-sections auto-complete

var actors = client.initIndex('actors'); var movies = client.initIndex('movies'); - autocomplete('#contacts, #contacts1', { hint: false, templates: { empty: 'empty' }, autoselect: true }, [ + autocomplete('#contacts, #contacts1', { hint: false, templates: { empty: 'empty' }, autoselect: true, appendTo: 'body' }, [ { source: autocomplete.sources.hits(index, { hitsPerPage: 5 }), displayKey: 'name', @@ -72,6 +72,8 @@

Multi-sections auto-complete

autocomplete('#contacts2', { debug: true, keyboardShortcuts: [191, 's'], + hint: false, + appendTo: 'h4', templates: { empty: function(data) { return 'No results for "' + data.query.replace(/[^-_'a-zA-Z0-9 ]/, ' ') + '"'; diff --git a/test/playground_angular.html b/test/playground_angular.html index 398a9cbd8..5e64d62c9 100644 --- a/test/playground_angular.html +++ b/test/playground_angular.html @@ -12,7 +12,7 @@

Simple auto-complete

- + Go
diff --git a/test/playground_jquery.html b/test/playground_jquery.html index 61dd875ad..849325080 100644 --- a/test/playground_jquery.html +++ b/test/playground_jquery.html @@ -98,6 +98,8 @@

Tabbed auto-complete

$('#contacts2').autocomplete({ debug: true, + hint: false, + appendTo: 'body', templates: { dropdownMenu: '#my-custom-menu-template', footer: '' diff --git a/test/unit/typeahead_spec.js b/test/unit/typeahead_spec.js index 9ddd75f0b..f07f970c6 100644 --- a/test/unit/typeahead_spec.js +++ b/test/unit/typeahead_spec.js @@ -40,6 +40,20 @@ describe('Typeahead', function() { this.dropdown = this.view.dropdown; }); + + describe('appendTo', function() { + it('should throw if used with hint', function(done) { + expect(function() { + return new Typeahead({ + input: this.$input, + hint: true, + appendTo: 'body' + }); + }).toThrow(); + done(); + }); + }); + describe('when dropdown triggers suggestionClicked', function() { beforeEach(function() { this.dropdown.getDatumForSuggestion.and.returnValue(testDatum);