From 55facb7b2495805c700f7e8405e038cfb6be7eb3 Mon Sep 17 00:00:00 2001 From: Jake Harding Date: Tue, 19 Feb 2013 22:58:44 -0800 Subject: [PATCH 1/7] Remove typeahead.css. --- src/css/typeahead.css | 79 ------------------------------------------- 1 file changed, 79 deletions(-) delete mode 100644 src/css/typeahead.css diff --git a/src/css/typeahead.css b/src/css/typeahead.css deleted file mode 100644 index af45426e..00000000 --- a/src/css/typeahead.css +++ /dev/null @@ -1,79 +0,0 @@ -/* - * Twitter Typeahead - * https://github.com/twitter/typeahead - * Copyright 2013 Twitter, Inc. and other contributors; Licensed MIT - */ - -.twitter-typeahead { - position: relative !important; - display: inline-block; - *display: inline; - *zoom: 1; -} - -.tt-query { - position: relative !important; - /* for unknown reasons, this fixes alignment issues in ie7 */ - *margin-top: -1px !important; - vertical-align: top !important; - background-color: transparent !important; - /* ie6-8 doesn't fire hover and click events for transparent elements - for a workaround, use a 1x1 transparent gif */ - background-image: url(data:image/gif;base64,R0lGODlhAQABAIAAAAAAAP///yH5BAEAAAAALAAAAAABAAEAAAIBRAA7) !important; -} - -.tt-hint { - position: absolute !important; - top: 0 !important; - left: 0 !important; - border-color: transparent !important; - -webkit-box-shadow: none !important; - -moz-box-shadow: none !important; - box-shadow: none !important; -} - -.tt-dropdown-menu, -.tt-suggestions, -.tt-suggestion { - padding: 0; - margin: 0; - list-style: none; -} - -.tt-dropdown-menu { - position: absolute; - top: 100%; - left: 0; - z-index: 100; /* TODO: need default z-index, should be configurable */ - display: none; -} - -.tt-dropdown-menu.tt-is-open { - display: block; -} - -.tt-dropdown-menu.tt-is-empty { - display: none; -} - -.tt-suggestion { - display: block; - white-space: nowrap; - cursor: pointer; -} - -.tt-suggestion * { - white-space: normal; -} - -/* rtl support */ -/* ----------- */ - -.twitter-typeahead.tt-rtl { - direction: rtl; -} - -.twitter-typeahead.tt-rtl .tt-dropdown-menu { - left: auto; - right: 0; -} From 38f9fe0680726c054140e92863f396ea4dc2a218 Mon Sep 17 00:00:00 2001 From: Jake Harding Date: Wed, 20 Feb 2013 19:57:31 -0800 Subject: [PATCH 2/7] Remove typeahead.css related tasks. --- Gruntfile.js | 37 +------------------------------------ package.json | 1 - 2 files changed, 1 insertion(+), 37 deletions(-) diff --git a/Gruntfile.js b/Gruntfile.js index 5cb5584c..c9bdc534 100644 --- a/Gruntfile.js +++ b/Gruntfile.js @@ -32,35 +32,7 @@ module.exports = function(grunt) { ' */\n\n' ].join('\n'), - less: { - css: { - src: 'src/css/typeahead.css', - dest: '<%= buildDir %>/typeahead.css' - }, - cssmin: { - options: { yuicompress: true }, - src: 'src/css/typeahead.css', - dest: '<%= buildDir %>/typeahead.min.css' - } - }, - concat: { - css: { - options: { - banner: '<%= banner %>', - stripBanners: true - }, - src: '<%= less.css.dest %>', - dest: '<%= less.css.dest %>' - }, - cssmin: { - options: { - banner: '<%= banner %>', - stripBanners: true - }, - src: '<%= less.cssmin.dest %>', - dest: '<%= less.cssmin.dest %>' - }, js: { src: ['src/js/intro.js', jsFiles, 'src/js/outro.js'], dest: '<%= buildDir %>/typeahead.js' @@ -115,10 +87,6 @@ module.exports = function(grunt) { js: { files: jsFiles, tasks: 'build:js' - }, - css: { - files: '<%= less.css.src %>', - tasks: 'build:css' } }, @@ -187,9 +155,7 @@ module.exports = function(grunt) { // ------- grunt.registerTask('default', 'build'); - grunt.registerTask('build', ['build:js', 'build:css']); - grunt.registerTask('build:js', ['concat:js', 'concat:jsmin', 'sed:version', 'uglify']); - grunt.registerTask('build:css', ['less', 'concat:css', 'concat:cssmin']); + grunt.registerTask('build', ['concat:js', 'concat:jsmin', 'sed:version', 'uglify']); grunt.registerTask('server', 'connect:server'); grunt.registerTask('lint', 'jshint'); grunt.registerTask('test', 'jasmine:js'); @@ -200,7 +166,6 @@ module.exports = function(grunt) { grunt.loadNpmTasks('grunt-sed'); grunt.loadNpmTasks('grunt-exec'); - grunt.loadNpmTasks('grunt-contrib-less'); grunt.loadNpmTasks('grunt-contrib-watch'); grunt.loadNpmTasks('grunt-contrib-clean'); grunt.loadNpmTasks('grunt-contrib-uglify'); diff --git a/package.json b/package.json index a32df4b6..b3c0cb4a 100644 --- a/package.json +++ b/package.json @@ -6,7 +6,6 @@ "grunt": "~0.4", "grunt-sed": "~0.1", "grunt-exec": "~0.4", - "grunt-contrib-less": "~0.5", "grunt-contrib-watch": "~0.2", "grunt-contrib-jshint": "~0.1", "grunt-contrib-uglify": "~0.1", From ae29dea547f6fe3fbb0bd455f8a91b229665807f Mon Sep 17 00:00:00 2001 From: Jake Harding Date: Wed, 20 Feb 2013 21:09:07 -0800 Subject: [PATCH 3/7] Apply styling though js. Fixes #2. --- src/js/dropdown_view.js | 63 ++++++-- src/js/typeahead_view.js | 100 +++++++++--- test/dropdown_view_spec.js | 294 +++++++++++++++++++----------------- test/typeahead_view_spec.js | 34 ++--- 4 files changed, 296 insertions(+), 195 deletions(-) diff --git a/src/js/dropdown_view.js b/src/js/dropdown_view.js index 8e2967bb..a6d46b64 100644 --- a/src/js/dropdown_view.js +++ b/src/js/dropdown_view.js @@ -5,6 +5,20 @@ */ var DropdownView = (function() { + var html = { + suggestionsList: '
' + }, + + css = { + suggestion: [ + 'white-space: nowrap;', + 'cursor: pointer;' + ].join(''), + + suggestionChild: [ + 'white-space: normal;' + ].join('') + }; // constructor // ----------- @@ -12,7 +26,9 @@ var DropdownView = (function() { function DropdownView(o) { utils.bindAll(this); - this.isMouseOverDropdown; + this.isOpen = false; + this.isEmpty = true; + this.isMouseOverDropdown = false; this.$menu = $(o.menu) .on('mouseenter', this._handleMouseenter) @@ -45,8 +61,8 @@ var DropdownView = (function() { _moveCursor: function(increment) { var $suggestions, $cur, nextIndex, $underCursor; - // don't bother moving the cursor if the menu is hidden - if (!this.$menu.hasClass('tt-is-open')) { + // don't bother moving the cursor if the menu is closed or empty + if (!this.isVisible()) { return; } @@ -81,6 +97,10 @@ var DropdownView = (function() { // public methods // -------------- + isVisible: function() { + return this.isOpen && !this.isEmpty; + }, + hideUnlessMouseIsOverDropdown: function() { // this helps detect the scenario a blur event has triggered // this function. we don't want to hide the menu in that case @@ -92,9 +112,11 @@ var DropdownView = (function() { }, hide: function() { - if (this.$menu.hasClass('tt-is-open')) { + if (this.isOpen) { + this.isOpen = false; + this.$menu - .removeClass('tt-is-open') + .hide() .find('.tt-suggestions > .tt-suggestion') .removeClass('tt-is-under-cursor'); @@ -103,15 +125,20 @@ var DropdownView = (function() { }, show: function() { - if (!this.$menu.hasClass('tt-is-open')) { - this.$menu.addClass('tt-is-open'); + if (!this.isOpen) { + this.isOpen = true; + + !this.isEmpty && this.$menu.show(); this.trigger('show'); } }, - isOpen: function() { - return this.$menu.hasClass('tt-is-open'); + setLanguageDirection: function(dir) { + var ltrCss = { left: '0', right: 'auto' }, + rtlCss = { left: 'auto', right:' 0' }; + + dir === 'ltr' ? this.$menu.css(ltrCss) : this.$menu.css(rtlCss); }, moveCursorUp: function() { @@ -147,8 +174,9 @@ var DropdownView = (function() { // first time rendering suggestions for this dataset if ($dataset.length === 0) { - $dataset = $('
    1. ') + $dataset = $('
      ') .addClass(datasetClassName) + .append(html.suggestionsList) .appendTo(this.$menu); } @@ -158,14 +186,20 @@ var DropdownView = (function() { this.clearSuggestions(dataset.name); if (suggestions.length > 0) { - this.$menu.removeClass('tt-is-empty'); + this.isOpen && this.$menu.show(); + this.isEmpty = false; utils.each(suggestions, function(i, suggestion) { elBuilder.innerHTML = dataset.template.render(suggestion); el = elBuilder.firstChild; + el.setAttribute('style', css.suggestion); el.setAttribute('data-value', suggestion.value); + for (var len = el.children.length; len--;) { + el.children[len].setAttribute('style', css.suggestionChild); + } + fragment.appendChild(el); }); } @@ -184,8 +218,11 @@ var DropdownView = (function() { $suggestions.empty(); - // add empty class if the dropdwn menu is empty - this._getSuggestions().length === 0 && this.$menu.addClass('tt-is-empty'); + + if (this._getSuggestions().length === 0) { + this.$menu.hide(); + this.isEmpty = true; + } } }); diff --git a/src/js/typeahead_view.js b/src/js/typeahead_view.js index e4a28f4d..80df7e69 100644 --- a/src/js/typeahead_view.js +++ b/src/js/typeahead_view.js @@ -5,12 +5,50 @@ */ var TypeaheadView = (function() { - var html = { - wrapper: '', - hint: '', - dropdown: '
        ' - }; + wrapper: '', + hint: '', + dropdown: '
        ' + }, + + css = { + wrapper: [ + 'position: relative;', + 'display: inline-block;', + '*display: inline;', + '*zoom: 1;' + ].join(''), + + hint: [ + 'position: absolute;', + 'top: 0;', + 'left: 0;', + 'border-color: transparent;', + '-webkit-box-shadow: none;', + '-moz-box-shadow: none;', + 'box-shadow: none;' + ].join(''), + + query: [ + 'position: relative;', + 'vertical-align: top;', + 'background-color: transparent;', + // ie6-8 doesn't fire hover and click events for elements with + // transparent backgrounds, for a workaround, use 1x1 transparent gif + 'background-image: url(data:image/gif;base64,R0lGODlhAQABAIAAAAAAAP///yH5BAEAAAAALAAAAAABAAEAAAIBRAA7);', + // not sure why ie7 won't play nice + '*margin-top: -1px;' + ].join(''), + + dropdown: [ + 'position: absolute;', + 'top: 100%;', + 'left: 0;', + // TODO: should this be configurable? + 'z-index: 100;', + 'display: none;' + ].join('') + }; // constructor // ----------- @@ -18,12 +56,13 @@ var TypeaheadView = (function() { function TypeaheadView(o) { utils.bindAll(this); - this.$node = wrapInput(o.input); + this.$node = buildDomStructure(o.input); this.datasets = o.datasets; + this.dir = null; // precompile the templates utils.each(this.datasets, function(key, dataset) { - var parentTemplate = '
      1. %body
      2. '; + var parentTemplate = '
        %body
        '; if (dataset.template) { dataset.template = dataset.engine @@ -95,10 +134,12 @@ var TypeaheadView = (function() { }, _setLanguageDirection: function() { - var dirClassName = 'tt-' + this.inputView.getLanguageDirection(); + var dir = this.inputView.getLanguageDirection(); - if (!this.$node.hasClass(dirClassName)) { - this.$node.removeClass('tt-ltr tt-rtl').addClass(dirClassName); + if (dir !== this.dir) { + this.dir = dir; + this.$node.css('direction', dir); + this.dropdownView.setLanguageDirection(dir); } }, @@ -110,7 +151,7 @@ var TypeaheadView = (function() { beginsWithQuery, match; - if (hint && this.dropdownView.isOpen()) { + if (hint && this.dropdownView.isVisible()) { inputValue = this.inputView.getInputValue(); query = inputValue .replace(/\s{2,}/g, ' ') // condense whitespace @@ -212,26 +253,37 @@ var TypeaheadView = (function() { return TypeaheadView; - function wrapInput(input) { - var $input = $(input), - $hint = $(html.hint).css({ - 'background-color': $input.css('background-color') - }); - - if ($input.length === 0) { - return null; - } + function buildDomStructure(input) { + var $wrapper = $(html.wrapper), + $dropdown = $(html.dropdown), + $input = $(input), + $hint = $(html.hint); + + $wrapper = $wrapper.attr('style', css.wrapper); + $dropdown = $dropdown.attr('style', css.dropdown); + + $hint + .attr({ + style: css.hint, + type: 'text', + autocomplete: false, + spellcheck: false, + disabled: true + }) + .css('background-color', $input.css('background-color')); + + $input + .addClass('tt-query') + .attr({ style: css.query, autocomplete: false, spellcheck: false }); // ie7 does not like it when dir is set to auto, // it does not like it one bit try { !$input.attr('dir') && $input.attr('dir', 'auto'); } catch (e) {} return $input - .attr({ autocomplete: false, spellcheck: false }) - .addClass('tt-query') - .wrap(html.wrapper) + .wrap($wrapper) .parent() .prepend($hint) - .append(html.dropdown); + .append($dropdown); } })(); diff --git a/test/dropdown_view_spec.js b/test/dropdown_view_spec.js index e1ade1ce..28ddbc0b 100644 --- a/test/dropdown_view_spec.js +++ b/test/dropdown_view_spec.js @@ -1,15 +1,5 @@ describe('DropdownView', function() { - var fixture = [ - '
          ', - '
        1. ', - '
            ', - '
          1. one
          2. ', - '
          3. two
          4. ', - '
          5. three
          6. ', - '
          ', - '
        2. ', - '
        ' - ].join('\n'); + var fixture = ''; beforeEach(function() { var $fixtures; @@ -18,13 +8,14 @@ describe('DropdownView', function() { $fixtures = $('#jasmine-fixtures'); this.$menu = $fixtures.find('.tt-dropdown-menu'); - this.$testSet = $fixtures.find('.tt-dataset-test > .tt-suggestions'); this.dropdownView = new DropdownView({ menu: this.$menu }); }); - describe('when mouseenter', function() { + describe('on mouseenter', function() { beforeEach(function() { + renderTestDataset(this.dropdownView, true); + this.$menu.mouseleave().mouseenter(); }); @@ -33,7 +24,7 @@ describe('DropdownView', function() { }); }); - describe('when mouseleave', function() { + describe('on mouseleave', function() { beforeEach(function() { this.$menu.mouseenter().mouseleave(); }); @@ -43,47 +34,48 @@ describe('DropdownView', function() { }); }); - describe('when mouseover suggestion', function() { - var spy, $suggestion1, $suggestion2, $suggestion3; - + describe('on suggestion mouseover', function() { beforeEach(function() { - this.dropdownView.on('cursorOn', spy = jasmine.createSpy()); + this.dropdownView.on('cursorOn', this.spy = jasmine.createSpy()); + + this.$testDataset = renderTestDataset(this.dropdownView, true); - $suggestion1 = this.$testSet.find('.tt-suggestion:nth-child(1)'); - $suggestion2 = this.$testSet.find('.tt-suggestion:nth-child(2)'); - $suggestion3 = this.$testSet.find('.tt-suggestion:nth-child(3)'); + this.$suggestion1 = this.$testDataset.find('.tt-suggestion:nth-child(1)'); + this.$suggestion2 = this.$testDataset.find('.tt-suggestion:nth-child(2)'); + this.$suggestion3 = this.$testDataset.find('.tt-suggestion:nth-child(3)'); // start with suggestion1 highlighted - $suggestion1.addClass('tt-is-under-cursor'); + this.$suggestion1.addClass('tt-is-under-cursor'); - $suggestion2.mouseover(); + this.$suggestion2.mouseover(); }); it('should add tt-is-under-cursor class to suggestion', function() { - expect($suggestion2).toHaveClass('tt-is-under-cursor'); + expect(this.$suggestion2).toHaveClass('tt-is-under-cursor'); }); it('should remove tt-is-under-cursor class from other suggestions', function() { - expect($suggestion1).not.toHaveClass('tt-is-under-cursor'); - expect($suggestion3).not.toHaveClass('tt-is-under-cursor'); + expect(this.$suggestion1).not.toHaveClass('tt-is-under-cursor'); + expect(this.$suggestion3).not.toHaveClass('tt-is-under-cursor'); }); }); - describe('when click suggestion', function() { - var spy, $suggestion; + describe('on suggestion click', function() { beforeEach(function() { - this.dropdownView.on('select', spy = jasmine.createSpy()); + this.dropdownView.on('select', this.spy = jasmine.createSpy()); + this.$testDataset = renderTestDataset(this.dropdownView, true); - $suggestion = this.$testSet + this.$suggestion = this.$testDataset .data('query', 'test query') .data('dataset', 'test dataset') - .find('.tt-suggestion:nth-child(1)').click(); + .find('.tt-suggestion:nth-child(1)') + .click(); }); it('should trigger select', function() { - expect(spy).toHaveBeenCalledWith({ + expect(this.spy).toHaveBeenCalledWith({ type: 'select', data: { query: 'test query', @@ -123,122 +115,117 @@ describe('DropdownView', function() { }); describe('#hide', function() { - var spy; - - beforeEach(function() { - this.dropdownView.on('hide', spy = jasmine.createSpy()); - }); - - describe('if menu has tt-is-open class', function() { + describe('if open', function() { beforeEach(function() { - this.$menu.addClass('tt-is-open') + renderTestDataset(this.dropdownView, true); + this.dropdownView.on('hide', this.spy = jasmine.createSpy()); + + this.$menu .find('.tt-suggestions > .tt-suggestion') .addClass('.tt-is-under-cursor'); this.dropdownView.hide(); }); - it('should remove tt-is-open class', function() { - expect(this.$menu).not.toHaveClass('tt-is-open'); + it('should hide menu', function() { + expect(this.$menu).toBeHidden(); }); it('should remove tt-is-under-cursor class from suggestions', function() { - var $suggestions = this.$menu.find('.tt-suggestions > .tt-suggestion'); + var $suggestions = this.$menu.find('.tt-suggestion'); + expect($suggestions).not.toHaveClass('tt-is-under-cursor'); }); it('should trigger hide', function() { - expect(spy).toHaveBeenCalled(); + expect(this.spy).toHaveBeenCalled(); }); }); - describe('if menu does not have tt-is-open class', function() { + describe('if not open', function() { beforeEach(function() { - this.$menu.removeClass('tt-is-open'); + renderTestDataset(this.dropdownView, false); + this.dropdownView.on('hide', this.spy = jasmine.createSpy()); this.dropdownView.hide(); }); - it('should not add the tt-is-open class', function() { - expect(this.$menu).not.toHaveClass('tt-is-open'); + it('should keep menu hidden', function() { + expect(this.$menu).toBeHidden(); }); it('should not trigger hide', function() { - expect(spy).not.toHaveBeenCalled(); + expect(this.spy).not.toHaveBeenCalled(); }); }); }); describe('#show', function() { - var spy; - - beforeEach(function() { - this.dropdownView.on('show', spy = jasmine.createSpy()); - }); - - describe('if menu has tt-is-open class', function() { + describe('if open', function() { beforeEach(function() { - this.$menu.addClass('tt-is-open'); + renderTestDataset(this.dropdownView, true); + this.dropdownView.on('show', this.spy = jasmine.createSpy()); this.dropdownView.show(); }); - it('should not remove tt-is-open class', function() { - expect(this.$menu).toHaveClass('tt-is-open'); + it('should keep menu visible', function() { + expect(this.$menu).toBeVisible(); }); it('should not trigger show', function() { - expect(spy).not.toHaveBeenCalled(); + expect(this.spy).not.toHaveBeenCalled(); }); }); - describe('if menu does not have tt-is-open class', function() { + describe('if not open', function() { beforeEach(function() { - this.$menu.removeClass('tt-is-open'); + renderTestDataset(this.dropdownView, false); + this.dropdownView.on('show', this.spy = jasmine.createSpy()); this.dropdownView.show(); }); - it('should add the tt-is-open class', function() { - expect(this.$menu).toHaveClass('tt-is-open'); + it('should make menu visible', function() { + expect(this.$menu).toBeVisible(); }); it('should trigger show', function() { - expect(spy).toHaveBeenCalled(); + expect(this.spy).toHaveBeenCalled(); }); }); }); describe('#moveCursorUp', function() { - var cursorOnSpy, cursorOffSpy; - beforeEach(function() { - this.dropdownView.on('cursorOn', cursorOnSpy = jasmine.createSpy()); - this.dropdownView.on('cursorOff', cursorOffSpy = jasmine.createSpy()); + this.dropdownView + .on('cursorOn', this.cursorOnSpy = jasmine.createSpy()) + .on('cursorOff', this.cursorOffSpy = jasmine.createSpy()); }); - describe('if menu is closed', function() { + describe('if not visible', function() { beforeEach(function() { - this.dropdownView.hide(); + renderTestDataset(this.dropdownView, false); + this.dropdownView.moveCursorUp(); }); it('should not move the cursor to any suggestion', function() { - expect(this.$testSet.find('.under-cursor')).not.toExist(); + expect(this.$menu.find('.tt-is-under-cursor')).not.toExist(); }); it('should not trigger cursorOn', function() { - expect(cursorOnSpy).not.toHaveBeenCalled(); + expect(this.cursorOnSpy).not.toHaveBeenCalled(); }); it('should not trigger cursorOff', function() { - expect(cursorOffSpy).not.toHaveBeenCalled(); + expect(this.cursorOffSpy).not.toHaveBeenCalled(); }); }); - describe('if menu is open', function() { + describe('if visible', function() { beforeEach(function() { - this.dropdownView.show(); + renderTestDataset(this.dropdownView, true); }); describe('if no suggestion is under the cursor', function() { @@ -247,21 +234,22 @@ describe('DropdownView', function() { }); it('should move cursor to last suggestion', function() { - var $lastSuggestion = this.$testSet.find('.tt-suggestion').last(), - $suggestionUnderCursor = this.$testSet - .find('.tt-is-under-cursor'); + var $lastSuggestion = this.$menu + .find('.tt-suggestion') + .last(), + $suggestionUnderCursor = this.$menu.find('.tt-is-under-cursor'); expect($lastSuggestion).toBe($suggestionUnderCursor); }); it('should trigger cursorOn', function() { - expect(cursorOnSpy).toHaveBeenCalled(); + expect(this.cursorOnSpy).toHaveBeenCalled(); }); }); describe('if the last suggestion is under the cursor', function() { beforeEach(function() { - this.$testSet + this.$menu .find('.tt-suggestion') .last() .addClass('tt-is-under-cursor'); @@ -270,9 +258,8 @@ describe('DropdownView', function() { }); it('should move cursor to previous suggestion', function() { - var $suggestionUnderCursor = this.$testSet - .find('.tt-is-under-cursor'), - $prevSuggestion = this.$testSet + var $suggestionUnderCursor = this.$menu.find('.tt-is-under-cursor'), + $prevSuggestion = this.$menu .find('.tt-suggestion') .last() .prev(); @@ -281,13 +268,13 @@ describe('DropdownView', function() { }); it('should trigger cursorOn', function() { - expect(cursorOnSpy).toHaveBeenCalled(); + expect(this.cursorOnSpy).toHaveBeenCalled(); }); }); describe('if the first suggestion is under the cursor', function() { beforeEach(function() { - this.$testSet + this.$menu .find('.tt-suggestion') .first() .addClass('tt-is-under-cursor'); @@ -296,50 +283,48 @@ describe('DropdownView', function() { }); it('should remove the cursor', function() { - var $suggestionUnderCursor = this.$testSet - .find('.tt-is-under-cursor'); + var $suggestionUnderCursor = this.$menu.find('.tt-is-under-cursor'); expect($suggestionUnderCursor).not.toExist(); }); it('should trigger cursorOff', function() { - expect(cursorOffSpy).toHaveBeenCalled(); + expect(this.cursorOffSpy).toHaveBeenCalled(); }); }); }); }); describe('#moveCursorDown', function() { - var cursorOnSpy, cursorOffSpy; - beforeEach(function() { - this.dropdownView.on('cursorOn', cursorOnSpy = jasmine.createSpy()); - this.dropdownView.on('cursorOff', cursorOffSpy = jasmine.createSpy()); + this.dropdownView + .on('cursorOn', this.cursorOnSpy = jasmine.createSpy()) + .on('cursorOff', this.cursorOffSpy = jasmine.createSpy()); }); - describe('if menu is closed', function() { + describe('if not visible', function() { beforeEach(function() { - this.dropdownView.hide(); + renderTestDataset(this.dropdownView, false); this.dropdownView.moveCursorDown(); }); it('should not move the cursor to any suggestion', function() { - expect(this.$testSet.find('.under-cursor')).not.toExist(); + expect(this.$menu.find('.tt-is-under-cursor')).not.toExist(); }); it('should not trigger cursorOn', function() { - expect(cursorOnSpy).not.toHaveBeenCalled(); + expect(this.cursorOnSpy).not.toHaveBeenCalled(); }); it('should not trigger cursorOff', function() { - expect(cursorOffSpy).not.toHaveBeenCalled(); + expect(this.cursorOffSpy).not.toHaveBeenCalled(); }); }); - describe('if menu is open', function() { + describe('if visible', function() { beforeEach(function() { - this.dropdownView.show(); + renderTestDataset(this.dropdownView, true); }); describe('if no suggestion is under the cursor', function() { @@ -348,21 +333,22 @@ describe('DropdownView', function() { }); it('should move cursor to first suggestion', function() { - var $firstSuggestion = this.$testSet.find('.tt-suggestion').first(), - $suggestionUnderCursor = this.$testSet + var $firstSuggestion = this.$menu + .find('.tt-suggestion').first(), + $suggestionUnderCursor = this.$menu .find('.tt-is-under-cursor'); expect($firstSuggestion).toBe($suggestionUnderCursor); }); it('should trigger cursorOn', function() { - expect(cursorOnSpy).toHaveBeenCalled(); + expect(this.cursorOnSpy).toHaveBeenCalled(); }); }); describe('if the first suggestion is under the cursor', function() { beforeEach(function() { - this.$testSet + this.$menu .find('.tt-suggestion') .first() .addClass('tt-is-under-cursor'); @@ -371,9 +357,8 @@ describe('DropdownView', function() { }); it('should move cursor to next suggestion', function() { - var $suggestionUnderCursor = this.$testSet - .find('.tt-is-under-cursor'), - $nextSuggestion = this.$testSet + var $suggestionUnderCursor = this.$menu.find('.tt-is-under-cursor'), + $nextSuggestion = this.$menu .find('.tt-suggestion') .first() .next(); @@ -382,13 +367,13 @@ describe('DropdownView', function() { }); it('should trigger cursorOn', function() { - expect(cursorOnSpy).toHaveBeenCalled(); + expect(this.cursorOnSpy).toHaveBeenCalled(); }); }); describe('if the last suggestion is under the cursor', function() { beforeEach(function() { - this.$testSet + this.$menu .find('.tt-suggestion') .last() .addClass('tt-is-under-cursor'); @@ -397,20 +382,23 @@ describe('DropdownView', function() { }); it('should remove the cursor', function() { - var $suggestionUnderCursor = this.$testSet - .find('.tt-is-under-cursor'); + var $suggestionUnderCursor = this.$menu.find('.tt-is-under-cursor'); expect($suggestionUnderCursor).not.toExist(); }); it('should trigger cursorOff', function() { - expect(cursorOffSpy).toHaveBeenCalled(); + expect(this.cursorOffSpy).toHaveBeenCalled(); }); }); }); }); describe('#getSuggestionUnderCursor', function() { + beforeEach(function() { + this.$testDataset = renderTestDataset(this.dropdownView, true); + }); + describe('if no suggestion is under the cursor', function() { it('should return null', function() { expect(this.dropdownView.getSuggestionUnderCursor()).toBeNull(); @@ -418,45 +406,50 @@ describe('DropdownView', function() { }); describe('if suggestion is under the cursor', function() { - var $suggestion; - - beforeEach(function() { - $suggestion = this.$testSet - .find('.tt-suggestion') - .first() - .addClass('tt-is-under-cursor'); - }); - it('should return obj with data about suggestion under the cursor', function() { - var suggestionData = this.dropdownView.getSuggestionUnderCursor(); + var $suggestion = this.$menu + .find('.tt-suggestion') + .first() + .addClass('tt-is-under-cursor'), + suggestionData = this.dropdownView.getSuggestionUnderCursor(); expect(suggestionData.value).toBe($suggestion.data('value')); - expect(suggestionData.query).toBe(this.$testSet.data('query')); - expect(suggestionData.dataset).toBe(this.$testSet.data('dataset')); + expect(suggestionData.query).toBe(this.$testDataset.data('query')); + expect(suggestionData.dataset).toBe(this.$testDataset.data('dataset')); }); }); }); describe('#getFirstSuggestion', function() { + beforeEach(function() { + this.$testDataset = renderTestDataset(this.dropdownView, true); + }); + it('should return obj with data about suggestion under the cursor', function() { var $firstSuggestion = this.dropdownView._getSuggestions().first(), suggestionData = this.dropdownView.getFirstSuggestion(); expect(suggestionData.value).toBe($firstSuggestion.data('value')); - expect(suggestionData.query).toBe(this.$testSet.data('query')); - expect(suggestionData.dataset).toBe(this.$testSet.data('dataset')); + expect(suggestionData.query).toBe(this.$testDataset.data('query')); + expect(suggestionData.dataset).toBe(this.$testDataset.data('dataset')); }); }); describe('#renderSuggestions', function() { var template = { - render: function(c) { return '

        ' + c.value + '

        '; } + render: function(c) { + return '
      3. ' + c.value + '

      4. '; + } }, mockNewDataset = { name: 'new', template: template }, mockOldDataset = { name: 'test', template: template }; + beforeEach(function() { + this.$testDataset = renderTestDataset(this.dropdownView, true); + }); + describe('if new dataset', function() { beforeEach(function() { this.dropdownView.renderSuggestions('query', mockNewDataset, []); @@ -486,10 +479,9 @@ describe('DropdownView', function() { }); describe('if there are suggestions', function() { - var spy; - beforeEach(function() { - this.dropdownView.on('suggestionsRender', spy = jasmine.createSpy()); + this.dropdownView + .on('suggestionsRender', this.spy = jasmine.createSpy()); spyOn(this.dropdownView, 'clearSuggestions').andCallThrough(); @@ -504,17 +496,13 @@ describe('DropdownView', function() { expect(this.dropdownView.clearSuggestions).toHaveBeenCalledWith('test'); }); - it('should remove tt-is-empty class from menu', function() { - expect(this.dropdownView.$menu).not.toHaveClass('tt-is-empty'); - }); - it('should update data values on list', function() { - expect(this.$testSet).toHaveData('query', 'query'); - expect(this.$testSet).toHaveData('dataset', 'test'); + expect(this.$testDataset).toHaveData('query', 'query'); + expect(this.$testDataset).toHaveData('dataset', 'test'); }); it('should overwrite previous suggestions', function() { - var $suggestions = this.$testSet.children(); + var $suggestions = this.$testDataset.children(); expect($suggestions.length).toBe(1); expect($suggestions.first()).toHaveText('i am a value'); @@ -522,13 +510,14 @@ describe('DropdownView', function() { }); it('should trigger suggestionsRender', function() { - expect(spy).toHaveBeenCalled(); + expect(this.spy).toHaveBeenCalled(); }); }); }); describe('#clearSuggestions', function() { beforeEach(function() { + renderTestDataset(this.dropdownView, true); this.dropdownView.clearSuggestions(); }); @@ -536,8 +525,33 @@ describe('DropdownView', function() { expect(this.$menu.find('.tt-suggestion')).not.toExist(); }); - it('should add tt-is-empty class to menu', function() { - expect(this.$menu).toHaveClass('tt-is-empty'); + it('should hide menu', function() { + expect(this.$menu).toBeHidden(); }); }); + + // helper functions + // ---------------- + + function renderTestDataset(view, open) { + var mockQuery = 'test q', + mockDataset = { + name: 'test' , + template: { + render: function(c) { + return '
      5. ' + c.value + '

      6. '; + } + } + }, + mockSuggestions = [ + { value: 'one' }, + { value: 'two' }, + { value: 'three' } + ]; + + view.renderSuggestions(mockQuery, mockDataset, mockSuggestions); + open && view.show(); + + return $('#jasmine-fixtures .tt-dataset-test > .tt-suggestions'); + } }); diff --git a/test/typeahead_view_spec.js b/test/typeahead_view_spec.js index 0d617932..844c54cd 100644 --- a/test/typeahead_view_spec.js +++ b/test/typeahead_view_spec.js @@ -149,17 +149,16 @@ describe('TypeaheadView', function() { describe('if language direction has changed', function() { beforeEach(function() { - this.typeaheadView.$node - .removeClass('tt-ltr tt-rtl') - .addClass('tt-ltr'); - + this.typeaheadView.dir = 'ltr'; this.inputView.getLanguageDirection.andReturn('rtl'); + this.inputView.trigger('whitespaceChange'); }); - it('should update langauge class name', function() { - expect(this.typeaheadView.$node).toHaveClass('tt-rtl'); - expect(this.typeaheadView.$node).not.toHaveClass('tt-ltr'); + it('should update styling', function() { + expect(this.typeaheadView.$node).toHaveCss({ direction: 'rtl' }); + expect(this.dropdownView.setLanguageDirection) + .toHaveBeenCalledWith('rtl'); }); }); }); @@ -188,17 +187,16 @@ describe('TypeaheadView', function() { describe('if language direction has changed', function() { beforeEach(function() { - this.typeaheadView.$node - .removeClass('tt-ltr tt-rtl') - .addClass('tt-ltr'); - + this.typeaheadView.dir = 'ltr'; this.inputView.getLanguageDirection.andReturn('rtl'); + this.inputView.trigger('queryChange'); }); - it('should update langauge class name', function() { - expect(this.typeaheadView.$node).toHaveClass('tt-rtl'); - expect(this.typeaheadView.$node).not.toHaveClass('tt-ltr'); + it('should update styling', function() { + expect(this.typeaheadView.$node).toHaveCss({ direction: 'rtl' }); + expect(this.dropdownView.setLanguageDirection) + .toHaveBeenCalledWith('rtl'); }); }); }); @@ -368,9 +366,9 @@ describe('TypeaheadView', function() { // ------------ function _updateHintSpecHelper(view, eventType) { - describe('if dropdown menu is closed', function() { + describe('if dropdown menu is not visible', function() { it('should not show hint', function() { - this.dropdownView.isOpen.andReturn(false); + this.dropdownView.isVisible.andReturn(false); this.inputView.getInputValue.andReturn('san '); this.dropdownView.getFirstSuggestion .andReturn({ value: 'desert sand' }); @@ -383,7 +381,7 @@ describe('TypeaheadView', function() { describe('if top suggestion\'s value begins with query', function() { it('should show hint', function() { - this.dropdownView.isOpen.andReturn(true); + this.dropdownView.isVisible.andReturn(true); this.inputView.getInputValue.andReturn('san '); this.dropdownView.getFirstSuggestion .andReturn({ value: 'san francisco' }); @@ -398,7 +396,7 @@ describe('TypeaheadView', function() { describe('if top suggestion\'s value does not begin with query', function() { it('should not show hint', function() { - this.dropdownView.isOpen.andReturn(true); + this.dropdownView.isVisible.andReturn(true); this.inputView.getInputValue.andReturn('san '); this.dropdownView.getFirstSuggestion .andReturn({ value: 'desert sand' }); From 2c0a635cf465e13b7e67160f12ce37f5fe4e0032 Mon Sep 17 00:00:00 2001 From: Jake Harding Date: Wed, 20 Feb 2013 21:39:04 -0800 Subject: [PATCH 4/7] Move js files from src/js/ to src/. --- Gruntfile.js | 26 +++++++++++++------------- src/{js => }/dataset.js | 0 src/{js => }/dropdown_view.js | 0 src/{js => }/event_target.js | 0 src/{js => }/input_view.js | 0 src/{js => }/intro.js | 0 src/{js => }/outro.js | 0 src/{js => }/persistent_storage.js | 0 src/{js => }/request_cache.js | 0 src/{js => }/transport.js | 0 src/{js => }/typeahead.js | 0 src/{js => }/typeahead_view.js | 0 src/{js => }/utils.js | 0 src/{js => }/version.js | 0 14 files changed, 13 insertions(+), 13 deletions(-) rename src/{js => }/dataset.js (100%) rename src/{js => }/dropdown_view.js (100%) rename src/{js => }/event_target.js (100%) rename src/{js => }/input_view.js (100%) rename src/{js => }/intro.js (100%) rename src/{js => }/outro.js (100%) rename src/{js => }/persistent_storage.js (100%) rename src/{js => }/request_cache.js (100%) rename src/{js => }/transport.js (100%) rename src/{js => }/typeahead.js (100%) rename src/{js => }/typeahead_view.js (100%) rename src/{js => }/utils.js (100%) rename src/{js => }/version.js (100%) diff --git a/Gruntfile.js b/Gruntfile.js index c9bdc534..fe5e807a 100644 --- a/Gruntfile.js +++ b/Gruntfile.js @@ -5,17 +5,17 @@ // 4. grunt publish_assets var jsFiles = [ - 'src/js/version.js', - 'src/js/utils.js', - 'src/js/event_target.js', - 'src/js/persistent_storage.js', - 'src/js/request_cache.js', - 'src/js/transport.js', - 'src/js/dataset.js', - 'src/js/input_view.js', - 'src/js/dropdown_view.js', - 'src/js/typeahead_view.js', - 'src/js/typeahead.js' + 'src/version.js', + 'src/utils.js', + 'src/event_target.js', + 'src/persistent_storage.js', + 'src/request_cache.js', + 'src/transport.js', + 'src/dataset.js', + 'src/input_view.js', + 'src/dropdown_view.js', + 'src/typeahead_view.js', + 'src/typeahead.js' ]; module.exports = function(grunt) { @@ -34,11 +34,11 @@ module.exports = function(grunt) { concat: { js: { - src: ['src/js/intro.js', jsFiles, 'src/js/outro.js'], + src: ['src/intro.js', jsFiles, 'src/outro.js'], dest: '<%= buildDir %>/typeahead.js' }, jsmin: { - src: ['src/js/intro.js', jsFiles, 'src/js/outro.js'], + src: ['src/intro.js', jsFiles, 'src/outro.js'], dest: '<%= buildDir %>/typeahead.min.js' } }, diff --git a/src/js/dataset.js b/src/dataset.js similarity index 100% rename from src/js/dataset.js rename to src/dataset.js diff --git a/src/js/dropdown_view.js b/src/dropdown_view.js similarity index 100% rename from src/js/dropdown_view.js rename to src/dropdown_view.js diff --git a/src/js/event_target.js b/src/event_target.js similarity index 100% rename from src/js/event_target.js rename to src/event_target.js diff --git a/src/js/input_view.js b/src/input_view.js similarity index 100% rename from src/js/input_view.js rename to src/input_view.js diff --git a/src/js/intro.js b/src/intro.js similarity index 100% rename from src/js/intro.js rename to src/intro.js diff --git a/src/js/outro.js b/src/outro.js similarity index 100% rename from src/js/outro.js rename to src/outro.js diff --git a/src/js/persistent_storage.js b/src/persistent_storage.js similarity index 100% rename from src/js/persistent_storage.js rename to src/persistent_storage.js diff --git a/src/js/request_cache.js b/src/request_cache.js similarity index 100% rename from src/js/request_cache.js rename to src/request_cache.js diff --git a/src/js/transport.js b/src/transport.js similarity index 100% rename from src/js/transport.js rename to src/transport.js diff --git a/src/js/typeahead.js b/src/typeahead.js similarity index 100% rename from src/js/typeahead.js rename to src/typeahead.js diff --git a/src/js/typeahead_view.js b/src/typeahead_view.js similarity index 100% rename from src/js/typeahead_view.js rename to src/typeahead_view.js diff --git a/src/js/utils.js b/src/utils.js similarity index 100% rename from src/js/utils.js rename to src/utils.js diff --git a/src/js/version.js b/src/version.js similarity index 100% rename from src/js/version.js rename to src/version.js From 8cba83b4773e35eac93602d78f664b54a2ad57ce Mon Sep 17 00:00:00 2001 From: Jake Harding Date: Fri, 22 Feb 2013 00:25:42 -0800 Subject: [PATCH 5/7] Use spans for dd menu suggestions list. Use jQuery#css. --- src/dropdown_view.js | 39 ++++++++++----------- src/typeahead_view.js | 79 +++++++++++++++++++++---------------------- src/utils.js | 4 ++- 3 files changed, 60 insertions(+), 62 deletions(-) diff --git a/src/dropdown_view.js b/src/dropdown_view.js index a6d46b64..a7a65982 100644 --- a/src/dropdown_view.js +++ b/src/dropdown_view.js @@ -6,18 +6,12 @@ var DropdownView = (function() { var html = { - suggestionsList: '
        ' + suggestionsList: '' }, - css = { - suggestion: [ - 'white-space: nowrap;', - 'cursor: pointer;' - ].join(''), - - suggestionChild: [ - 'white-space: normal;' - ].join('') + suggestionsList: { display: 'block' }, + suggestion: { whiteSpace: 'nowrap', cursor: 'pointer' }, + suggestionChild: { whiteSpace: 'normal' } }; // constructor @@ -128,7 +122,7 @@ var DropdownView = (function() { if (!this.isOpen) { this.isOpen = true; - !this.isEmpty && this.$menu.show(); + !this.isEmpty && this.$menu.css('display', 'block'); this.trigger('show'); } @@ -167,16 +161,19 @@ var DropdownView = (function() { renderSuggestions: function(query, dataset, suggestions) { var datasetClassName = 'tt-dataset-' + dataset.name, + $suggestionsList, $dataset = this.$menu.find('.' + datasetClassName), elBuilder, fragment, - el; + $el; // first time rendering suggestions for this dataset if ($dataset.length === 0) { + $suggestionsList = $(html.suggestionsList).css(css.suggestionsList); + $dataset = $('
        ') .addClass(datasetClassName) - .append(html.suggestionsList) + .append($suggestionsList) .appendTo(this.$menu); } @@ -186,21 +183,21 @@ var DropdownView = (function() { this.clearSuggestions(dataset.name); if (suggestions.length > 0) { - this.isOpen && this.$menu.show(); + this.isOpen && this.$menu.css('display', 'block'); this.isEmpty = false; utils.each(suggestions, function(i, suggestion) { elBuilder.innerHTML = dataset.template.render(suggestion); - el = elBuilder.firstChild; - el.setAttribute('style', css.suggestion); - el.setAttribute('data-value', suggestion.value); + $el = $(elBuilder.firstChild) + .css(css.suggestion) + .data('value', suggestion.value); - for (var len = el.children.length; len--;) { - el.children[len].setAttribute('style', css.suggestionChild); - } + $el.children().each(function() { + $(this).css(css.suggestionChild); + }); - fragment.appendChild(el); + fragment.appendChild($el[0]); }); } diff --git a/src/typeahead_view.js b/src/typeahead_view.js index 80df7e69..684ddc84 100644 --- a/src/typeahead_view.js +++ b/src/typeahead_view.js @@ -8,48 +8,46 @@ var TypeaheadView = (function() { var html = { wrapper: '', hint: '', - dropdown: '
        ' + dropdown: '' }, - css = { - wrapper: [ - 'position: relative;', - 'display: inline-block;', - '*display: inline;', - '*zoom: 1;' - ].join(''), - - hint: [ - 'position: absolute;', - 'top: 0;', - 'left: 0;', - 'border-color: transparent;', - '-webkit-box-shadow: none;', - '-moz-box-shadow: none;', - 'box-shadow: none;' - ].join(''), - - query: [ - 'position: relative;', - 'vertical-align: top;', - 'background-color: transparent;', + wrapper: { + position: 'relative', + display: 'inline-block' + }, + hint: { + position: 'absolute', + top: '0', + left: '0', + borderColor: 'transparent', + boxShadow: 'none' + }, + query: { + position: 'relative', + verticalAlign: 'top', + backgroundColor: 'transparent', // ie6-8 doesn't fire hover and click events for elements with // transparent backgrounds, for a workaround, use 1x1 transparent gif - 'background-image: url(data:image/gif;base64,R0lGODlhAQABAIAAAAAAAP///yH5BAEAAAAALAAAAAABAAEAAAIBRAA7);', - // not sure why ie7 won't play nice - '*margin-top: -1px;' - ].join(''), - - dropdown: [ - 'position: absolute;', - 'top: 100%;', - 'left: 0;', + backgroundImage: 'url(data:image/gif;base64,R0lGODlhAQABAIAAAAAAAP///yH5BAEAAAAALAAAAAABAAEAAAIBRAA7)' + }, + dropdown: { + position: 'absolute', + top: '100%', + left: '0', // TODO: should this be configurable? - 'z-index: 100;', - 'display: none;' - ].join('') + zIndex: '100', + display: 'none' + } }; + // ie7 specific styling + if (utils.isMsie() && utils.isMsie() <= 7) { + utils.mixin(css.wrapper, { display: 'inline', zoom: '1' }); + // if someone can tell me why this is necessary to align + // the hint with the query in ie7, i'll send you $5 - @JakeHarding + utils.mixin(css.query, { marginTop: '-1px' }); + } + // constructor // ----------- @@ -259,22 +257,23 @@ var TypeaheadView = (function() { $input = $(input), $hint = $(html.hint); - $wrapper = $wrapper.attr('style', css.wrapper); - $dropdown = $dropdown.attr('style', css.dropdown); + $wrapper = $wrapper.css(css.wrapper); + $dropdown = $dropdown.css(css.dropdown); $hint .attr({ - style: css.hint, type: 'text', autocomplete: false, spellcheck: false, disabled: true }) - .css('background-color', $input.css('background-color')); + .css(css.hint) + .css('background', $input.css('background')); $input .addClass('tt-query') - .attr({ style: css.query, autocomplete: false, spellcheck: false }); + .attr({ autocomplete: false, spellcheck: false }) + .css(css.query); // ie7 does not like it when dir is set to auto, // it does not like it one bit diff --git a/src/utils.js b/src/utils.js index 55077a6e..e4ff6f6a 100644 --- a/src/utils.js +++ b/src/utils.js @@ -6,7 +6,9 @@ var utils = { isMsie: function() { - return (/msie [\w.]+/i).test(navigator.userAgent); + var match = /(msie) ([\w.]+)/i.exec(navigator.userAgent); + + return match ? parseInt(match[2], 10) : false; }, isString: function(obj) { return typeof obj === 'string'; }, From 135bd753e3091af3830ddd73c8d2e2b9c099afa5 Mon Sep 17 00:00:00 2001 From: Jake Harding Date: Fri, 22 Feb 2013 19:03:25 -0800 Subject: [PATCH 6/7] Update method names. --- src/dropdown_view.js | 33 +++++++++++++++--------- src/typeahead_view.js | 24 +++++++++--------- test/dropdown_view_spec.js | 50 ++++++++++++++++++------------------- test/typeahead_view_spec.js | 36 +++++++++++++------------- 4 files changed, 76 insertions(+), 67 deletions(-) diff --git a/src/dropdown_view.js b/src/dropdown_view.js index a7a65982..ab3d20de 100644 --- a/src/dropdown_view.js +++ b/src/dropdown_view.js @@ -52,6 +52,16 @@ var DropdownView = (function() { this.trigger('select', formatDataForSuggestion($(e.currentTarget))); }, + _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'); + }, + + _hide: function() { + this.$menu.hide(); + }, + _moveCursor: function(increment) { var $suggestions, $cur, nextIndex, $underCursor; @@ -95,36 +105,35 @@ var DropdownView = (function() { return this.isOpen && !this.isEmpty; }, - hideUnlessMouseIsOverDropdown: function() { + closeUnlessMouseIsOverDropdown: function() { // this helps detect the scenario a blur event has triggered - // this function. we don't want to hide the menu in that case + // this function. we don't want to close the menu in that case // because it'll prevent the probable associated click event // from being fired if (!this.isMouseOverDropdown) { - this.hide(); + this.close(); } }, - hide: function() { + close: function() { if (this.isOpen) { this.isOpen = false; + this._hide(); this.$menu - .hide() .find('.tt-suggestions > .tt-suggestion') .removeClass('tt-is-under-cursor'); - this.trigger('hide'); + this.trigger('close'); } }, - show: function() { + open: function() { if (!this.isOpen) { this.isOpen = true; + !this.isEmpty && this._show(); - !this.isEmpty && this.$menu.css('display', 'block'); - - this.trigger('show'); + this.trigger('open'); } }, @@ -183,8 +192,8 @@ var DropdownView = (function() { this.clearSuggestions(dataset.name); if (suggestions.length > 0) { - this.isOpen && this.$menu.css('display', 'block'); this.isEmpty = false; + this.isOpen && this._show(); utils.each(suggestions, function(i, suggestion) { elBuilder.innerHTML = dataset.template.render(suggestion); @@ -217,8 +226,8 @@ var DropdownView = (function() { if (this._getSuggestions().length === 0) { - this.$menu.hide(); this.isEmpty = true; + this._hide(); } } }); diff --git a/src/typeahead_view.js b/src/typeahead_view.js index 684ddc84..3f18bedc 100644 --- a/src/typeahead_view.js +++ b/src/typeahead_view.js @@ -95,24 +95,24 @@ var TypeaheadView = (function() { .on('cursorOff', this._setInputValueToQuery) .on('cursorOff', this._updateHint) .on('suggestionsRender', this._updateHint) - .on('show', this._updateHint) - .on('hide', this._clearHint); + .on('open', this._updateHint) + .on('close', this._clearHint); this.inputView - .on('focus', this._showDropdown) - .on('blur', this._hideDropdown) + .on('focus', this._openDropdown) + .on('blur', this._closeDropdown) .on('blur', this._setInputValueToQuery) .on('enter', this._handleSelection) .on('queryChange', this._clearHint) .on('queryChange', this._clearSuggestions) .on('queryChange', this._getSuggestions) .on('whitespaceChange', this._updateHint) - .on('queryChange whitespaceChange', this._showDropdown) + .on('queryChange whitespaceChange', this._openDropdown) .on('queryChange whitespaceChange', this._setLanguageDirection) - .on('esc', this._hideDropdown) + .on('esc', this._closeDropdown) .on('esc', this._setInputValueToQuery) .on('up down', this._moveDropdownCursor) - .on('up down', this._showDropdown) + .on('up down', this._openDropdown) .on('tab', this._setPreventDefaultValueForTab) .on('tab left right', this._autocomplete); } @@ -178,13 +178,13 @@ var TypeaheadView = (function() { this.inputView.setInputValue(e.data.value, true); }, - _showDropdown: function() { - this.dropdownView.show(); + _openDropdown: function() { + this.dropdownView.open(); }, - _hideDropdown: function(e) { + _closeDropdown: function(e) { this.dropdownView[e.type === 'blur' ? - 'hideUnlessMouseIsOverDropdown' : 'hide'](); + 'closeUnlessMouseIsOverDropdown' : 'close'](); }, _moveDropdownCursor: function(e) { @@ -207,7 +207,7 @@ var TypeaheadView = (function() { // focus is not a synchronous event in ie, so we deal with it byClick && utils.isMsie() ? - setTimeout(this.dropdownView.hide, 0) : this.dropdownView.hide(); + setTimeout(this.dropdownView.close, 0) : this.dropdownView.close(); } }, diff --git a/test/dropdown_view_spec.js b/test/dropdown_view_spec.js index 28ddbc0b..1839cc00 100644 --- a/test/dropdown_view_spec.js +++ b/test/dropdown_view_spec.js @@ -86,48 +86,48 @@ describe('DropdownView', function() { }); }); - describe('#hideUnlessMouseIsOverDropdown', function() { + describe('#closeUnlessMouseIsOverDropdown', function() { beforeEach(function() { - spyOn(this.dropdownView, 'hide'); + spyOn(this.dropdownView, 'close'); }); describe('if isMouseOverDropdown is true', function() { beforeEach(function() { this.dropdownView.isMouseOverDropdown = true; - this.dropdownView.hideUnlessMouseIsOverDropdown(); + this.dropdownView.closeUnlessMouseIsOverDropdown(); }); - it('should not call hide', function() { - expect(this.dropdownView.hide).not.toHaveBeenCalled(); + it('should not call close', function() { + expect(this.dropdownView.close).not.toHaveBeenCalled(); }); }); describe('if isMouseOverDropdown is false', function() { beforeEach(function() { this.dropdownView.isMouseOverDropdown = false; - this.dropdownView.hideUnlessMouseIsOverDropdown(); + this.dropdownView.closeUnlessMouseIsOverDropdown(); }); - it('should call hide', function() { - expect(this.dropdownView.hide).toHaveBeenCalled(); + it('should call close', function() { + expect(this.dropdownView.close).toHaveBeenCalled(); }); }); }); - describe('#hide', function() { + describe('#close', function() { describe('if open', function() { beforeEach(function() { renderTestDataset(this.dropdownView, true); - this.dropdownView.on('hide', this.spy = jasmine.createSpy()); + this.dropdownView.on('close', this.spy = jasmine.createSpy()); this.$menu .find('.tt-suggestions > .tt-suggestion') .addClass('.tt-is-under-cursor'); - this.dropdownView.hide(); + this.dropdownView.close(); }); - it('should hide menu', function() { + it('should close menu', function() { expect(this.$menu).toBeHidden(); }); @@ -137,7 +137,7 @@ describe('DropdownView', function() { expect($suggestions).not.toHaveClass('tt-is-under-cursor'); }); - it('should trigger hide', function() { + it('should trigger close', function() { expect(this.spy).toHaveBeenCalled(); }); }); @@ -145,35 +145,35 @@ describe('DropdownView', function() { describe('if not open', function() { beforeEach(function() { renderTestDataset(this.dropdownView, false); - this.dropdownView.on('hide', this.spy = jasmine.createSpy()); + this.dropdownView.on('close', this.spy = jasmine.createSpy()); - this.dropdownView.hide(); + this.dropdownView.close(); }); it('should keep menu hidden', function() { expect(this.$menu).toBeHidden(); }); - it('should not trigger hide', function() { + it('should not trigger close', function() { expect(this.spy).not.toHaveBeenCalled(); }); }); }); - describe('#show', function() { + describe('#open', function() { describe('if open', function() { beforeEach(function() { renderTestDataset(this.dropdownView, true); - this.dropdownView.on('show', this.spy = jasmine.createSpy()); + this.dropdownView.on('open', this.spy = jasmine.createSpy()); - this.dropdownView.show(); + this.dropdownView.open(); }); it('should keep menu visible', function() { expect(this.$menu).toBeVisible(); }); - it('should not trigger show', function() { + it('should not trigger open', function() { expect(this.spy).not.toHaveBeenCalled(); }); }); @@ -181,16 +181,16 @@ describe('DropdownView', function() { describe('if not open', function() { beforeEach(function() { renderTestDataset(this.dropdownView, false); - this.dropdownView.on('show', this.spy = jasmine.createSpy()); + this.dropdownView.on('open', this.spy = jasmine.createSpy()); - this.dropdownView.show(); + this.dropdownView.open(); }); it('should make menu visible', function() { expect(this.$menu).toBeVisible(); }); - it('should trigger show', function() { + it('should trigger open', function() { expect(this.spy).toHaveBeenCalled(); }); }); @@ -525,7 +525,7 @@ describe('DropdownView', function() { expect(this.$menu.find('.tt-suggestion')).not.toExist(); }); - it('should hide menu', function() { + it('should close menu', function() { expect(this.$menu).toBeHidden(); }); }); @@ -550,7 +550,7 @@ describe('DropdownView', function() { ]; view.renderSuggestions(mockQuery, mockDataset, mockSuggestions); - open && view.show(); + open && view.open(); return $('#jasmine-fixtures .tt-dataset-test > .tt-suggestions'); } diff --git a/test/typeahead_view_spec.js b/test/typeahead_view_spec.js index 844c54cd..4146ff81 100644 --- a/test/typeahead_view_spec.js +++ b/test/typeahead_view_spec.js @@ -50,8 +50,8 @@ describe('TypeaheadView', function() { expect(this.inputView.focus).toHaveBeenCalled(); }); - it('should hide dropdown', function() { - expect(this.dropdownView.hide).toHaveBeenCalled(); + it('should close dropdown', function() { + expect(this.dropdownView.close).toHaveBeenCalled(); }); }); @@ -85,9 +85,9 @@ describe('TypeaheadView', function() { _updateHintSpecHelper('dropdownView', 'suggestionsRender'); }); - describe('when dropdownView triggers hide', function() { + describe('when dropdownView triggers close', function() { beforeEach(function() { - this.dropdownView.trigger('hide'); + this.dropdownView.trigger('close'); }); it('should clear hint', function() { @@ -105,8 +105,8 @@ describe('TypeaheadView', function() { this.inputView.trigger('blur'); }); - it('should hide dropdown unless mouse is over it', function() { - expect(this.dropdownView.hideUnlessMouseIsOverDropdown) + it('should close dropdown unless mouse is over it', function() { + expect(this.dropdownView.closeUnlessMouseIsOverDropdown) .toHaveBeenCalled(); }); @@ -134,17 +134,17 @@ describe('TypeaheadView', function() { expect(this.spy).toHaveBeenCalled(); }); - it('should hide dropdown', function() { - expect(this.dropdownView.hide).toHaveBeenCalled(); + it('should close dropdown', function() { + expect(this.dropdownView.close).toHaveBeenCalled(); }); }); describe('when inputView triggers whitespaceChange', function() { _updateHintSpecHelper('inputView', 'whitespaceChange'); - it('should show the dropdown menu', function() { + it('should open the dropdown menu', function() { this.inputView.trigger('whitespaceChange'); - expect(this.dropdownView.show).toHaveBeenCalled(); + expect(this.dropdownView.open).toHaveBeenCalled(); }); describe('if language direction has changed', function() { @@ -164,9 +164,9 @@ describe('TypeaheadView', function() { }); describe('when inputView triggers queryChange', function() { - it('should show the dropdown menu', function() { + it('should open the dropdown menu', function() { this.inputView.trigger('queryChange'); - expect(this.dropdownView.show).toHaveBeenCalled(); + expect(this.dropdownView.open).toHaveBeenCalled(); }); it('should clear hint', function() { @@ -206,8 +206,8 @@ describe('TypeaheadView', function() { this.inputView.trigger('focus'); }); - it('should show the dropdown menu', function() { - expect(this.dropdownView.show).toHaveBeenCalled(); + it('should open the dropdown menu', function() { + expect(this.dropdownView.open).toHaveBeenCalled(); }); }); @@ -218,8 +218,8 @@ describe('TypeaheadView', function() { this.inputView.trigger('esc'); }); - it('should hide dropdown', function() { - expect(this.dropdownView.hide).toHaveBeenCalled(); + it('should close dropdown', function() { + expect(this.dropdownView.close).toHaveBeenCalled(); }); it('should reset input value to user query', function() { @@ -235,8 +235,8 @@ describe('TypeaheadView', function() { this.inputView.trigger(eventType); }); - it('should show the dropdown menu', function() { - expect(this.dropdownView.show).toHaveBeenCalled(); + it('should open the dropdown menu', function() { + expect(this.dropdownView.open).toHaveBeenCalled(); }); it('should move cursor ' + eventType, function() { From e208114d0b06eef7d57f5be26dbe531a1ff882c2 Mon Sep 17 00:00:00 2001 From: Jake Harding Date: Fri, 22 Feb 2013 21:02:53 -0800 Subject: [PATCH 7/7] Use past tense for view events. --- src/dropdown_view.js | 17 ++++---- src/input_view.js | 10 ++--- src/typeahead_view.js | 59 +++++++++++++------------- test/dropdown_view_spec.js | 82 ++++++++++++++++++------------------ test/input_view_spec.js | 34 ++++++++------- test/typeahead_view_spec.js | 83 +++++++++++++++++++------------------ 6 files changed, 147 insertions(+), 138 deletions(-) diff --git a/src/dropdown_view.js b/src/dropdown_view.js index ab3d20de..5a51cad8 100644 --- a/src/dropdown_view.js +++ b/src/dropdown_view.js @@ -44,12 +44,15 @@ var DropdownView = (function() { }, _handleMouseover: function(e) { + var $suggestion = $(e.currentTarget); + this._getSuggestions().removeClass('tt-is-under-cursor'); - $(e.currentTarget).addClass('tt-is-under-cursor'); + $suggestion.addClass('tt-is-under-cursor'); }, _handleSelection: function(e) { - this.trigger('select', formatDataForSuggestion($(e.currentTarget))); + var $suggestion = $(e.currentTarget); + this.trigger('suggestionSelected', formatDataForSuggestion($suggestion)); }, _show: function() { @@ -80,7 +83,7 @@ var DropdownView = (function() { nextIndex = (nextIndex + 1) % ($suggestions.length + 1) - 1; if (nextIndex === -1) { - this.trigger('cursorOff'); + this.trigger('cursorRemoved'); return; } @@ -91,7 +94,7 @@ var DropdownView = (function() { } $underCursor = $suggestions.eq(nextIndex).addClass('tt-is-under-cursor'); - this.trigger('cursorOn', { value: $underCursor.data('value') }); + this.trigger('cursorMoved', { value: $underCursor.data('value') }); }, _getSuggestions: function() { @@ -124,7 +127,7 @@ var DropdownView = (function() { .find('.tt-suggestions > .tt-suggestion') .removeClass('tt-is-under-cursor'); - this.trigger('close'); + this.trigger('closed'); } }, @@ -133,7 +136,7 @@ var DropdownView = (function() { this.isOpen = true; !this.isEmpty && this._show(); - this.trigger('open'); + this.trigger('opened'); } }, @@ -214,7 +217,7 @@ var DropdownView = (function() { .data({ query: query, dataset: dataset.name }) .append(fragment); - this.trigger('suggestionsRender'); + this.trigger('suggestionsRendered'); }, clearSuggestions: function(datasetName) { diff --git a/src/input_view.js b/src/input_view.js index beb2252d..21483735 100644 --- a/src/input_view.js +++ b/src/input_view.js @@ -57,11 +57,11 @@ var InputView = (function() { // --------------- _handleFocus: function() { - this.trigger('focus'); + this.trigger('focused'); }, _handleBlur: function() { - this.trigger('blur'); + this.trigger('blured'); }, _handleSpecialKeyEvent: function(e) { @@ -69,7 +69,7 @@ var InputView = (function() { var keyCode = this.specialKeyCodeMap[e.which || e.keyCode]; if (keyCode) { - this.trigger(keyCode.event, e); + this.trigger(keyCode.event + 'Keyed', e); keyCode.preventDefault && e.preventDefault(); } }, @@ -81,11 +81,11 @@ var InputView = (function() { this.query.length !== inputValue.length : false; if (isSameQueryExceptWhitespace) { - this.trigger('whitespaceChange', { value: this.query }); + this.trigger('whitespaceChanged', { value: this.query }); } else if (!isSameQuery) { - this.trigger('queryChange', { value: this.query = inputValue }); + this.trigger('queryChanged', { value: this.query = inputValue }); } }, diff --git a/src/typeahead_view.js b/src/typeahead_view.js index 3f18bedc..ae6ecd48 100644 --- a/src/typeahead_view.js +++ b/src/typeahead_view.js @@ -89,32 +89,32 @@ var TypeaheadView = (function() { }); this.dropdownView - .on('select', this._handleSelection) - .on('cursorOn', this._clearHint) - .on('cursorOn', this._setInputValueToSuggestionUnderCursor) - .on('cursorOff', this._setInputValueToQuery) - .on('cursorOff', this._updateHint) - .on('suggestionsRender', this._updateHint) - .on('open', this._updateHint) - .on('close', this._clearHint); + .on('suggestionSelected', this._handleSelection) + .on('cursorMoved', this._clearHint) + .on('cursorMoved', this._setInputValueToSuggestionUnderCursor) + .on('cursorRemoved', this._setInputValueToQuery) + .on('cursorRemoved', this._updateHint) + .on('suggestionsRendered', this._updateHint) + .on('opened', this._updateHint) + .on('closed', this._clearHint); this.inputView - .on('focus', this._openDropdown) - .on('blur', this._closeDropdown) - .on('blur', this._setInputValueToQuery) - .on('enter', this._handleSelection) - .on('queryChange', this._clearHint) - .on('queryChange', this._clearSuggestions) - .on('queryChange', this._getSuggestions) - .on('whitespaceChange', this._updateHint) - .on('queryChange whitespaceChange', this._openDropdown) - .on('queryChange whitespaceChange', this._setLanguageDirection) - .on('esc', this._closeDropdown) - .on('esc', this._setInputValueToQuery) - .on('up down', this._moveDropdownCursor) - .on('up down', this._openDropdown) - .on('tab', this._setPreventDefaultValueForTab) - .on('tab left right', this._autocomplete); + .on('focused', this._openDropdown) + .on('blured', this._closeDropdown) + .on('blured', this._setInputValueToQuery) + .on('enterKeyed', this._handleSelection) + .on('queryChanged', this._clearHint) + .on('queryChanged', this._clearSuggestions) + .on('queryChanged', this._getSuggestions) + .on('whitespaceChanged', this._updateHint) + .on('queryChanged whitespaceChanged', this._openDropdown) + .on('queryChanged whitespaceChanged', this._setLanguageDirection) + .on('escKeyed', this._closeDropdown) + .on('escKeyed', this._setInputValueToQuery) + .on('upKeyed downKeyed', this._moveDropdownCursor) + .on('upKeyed downKeyed', this._openDropdown) + .on('tabKeyed', this._setPreventDefaultValueForTab) + .on('tabKeyed leftKeyed rightKeyed', this._autocomplete); } utils.mixin(TypeaheadView.prototype, EventTarget, { @@ -183,16 +183,17 @@ var TypeaheadView = (function() { }, _closeDropdown: function(e) { - this.dropdownView[e.type === 'blur' ? + this.dropdownView[e.type === 'blured' ? 'closeUnlessMouseIsOverDropdown' : 'close'](); }, _moveDropdownCursor: function(e) { - this.dropdownView[e.type === 'up' ? 'moveCursorUp' : 'moveCursorDown'](); + this.dropdownView[e.type === 'upKeyed' ? + 'moveCursorUp' : 'moveCursorDown'](); }, _handleSelection: function(e) { - var byClick = e.type === 'select', + var byClick = e.type === 'suggestionSelected', suggestionData = byClick ? e.data : this.dropdownView.getSuggestionUnderCursor(); @@ -232,10 +233,10 @@ var TypeaheadView = (function() { _autocomplete: function(e) { var isCursorAtEnd, ignoreEvent, query, hint; - if (e.type === 'right' || e.type === 'left') { + if (e.type === 'rightKeyed' || e.type === 'leftKeyed') { isCursorAtEnd = this.inputView.isCursorAtEnd(); ignoreEvent = this.inputView.getLanguageDirection() === 'ltr' ? - e.type === 'left' : e.type === 'right'; + e.type === 'leftKeyed' : e.type === 'rightKeyed'; if (!isCursorAtEnd || ignoreEvent) { return; } } diff --git a/test/dropdown_view_spec.js b/test/dropdown_view_spec.js index 1839cc00..39c9d2c3 100644 --- a/test/dropdown_view_spec.js +++ b/test/dropdown_view_spec.js @@ -36,7 +36,7 @@ describe('DropdownView', function() { describe('on suggestion mouseover', function() { beforeEach(function() { - this.dropdownView.on('cursorOn', this.spy = jasmine.createSpy()); + this.dropdownView.on('cursorMoved', this.spy = jasmine.createSpy()); this.$testDataset = renderTestDataset(this.dropdownView, true); @@ -64,7 +64,9 @@ describe('DropdownView', function() { describe('on suggestion click', function() { beforeEach(function() { - this.dropdownView.on('select', this.spy = jasmine.createSpy()); + this.dropdownView + .on('suggestionSelected', this.spy = jasmine.createSpy()); + this.$testDataset = renderTestDataset(this.dropdownView, true); this.$suggestion = this.$testDataset @@ -74,9 +76,9 @@ describe('DropdownView', function() { .click(); }); - it('should trigger select', function() { + it('should trigger suggestionSelected', function() { expect(this.spy).toHaveBeenCalledWith({ - type: 'select', + type: 'suggestionSelected', data: { query: 'test query', dataset: 'test dataset', @@ -118,7 +120,7 @@ describe('DropdownView', function() { describe('if open', function() { beforeEach(function() { renderTestDataset(this.dropdownView, true); - this.dropdownView.on('close', this.spy = jasmine.createSpy()); + this.dropdownView.on('closed', this.spy = jasmine.createSpy()); this.$menu .find('.tt-suggestions > .tt-suggestion') @@ -137,7 +139,7 @@ describe('DropdownView', function() { expect($suggestions).not.toHaveClass('tt-is-under-cursor'); }); - it('should trigger close', function() { + it('should trigger closed', function() { expect(this.spy).toHaveBeenCalled(); }); }); @@ -145,7 +147,7 @@ describe('DropdownView', function() { describe('if not open', function() { beforeEach(function() { renderTestDataset(this.dropdownView, false); - this.dropdownView.on('close', this.spy = jasmine.createSpy()); + this.dropdownView.on('closed', this.spy = jasmine.createSpy()); this.dropdownView.close(); }); @@ -154,7 +156,7 @@ describe('DropdownView', function() { expect(this.$menu).toBeHidden(); }); - it('should not trigger close', function() { + it('should not trigger closed', function() { expect(this.spy).not.toHaveBeenCalled(); }); }); @@ -164,7 +166,7 @@ describe('DropdownView', function() { describe('if open', function() { beforeEach(function() { renderTestDataset(this.dropdownView, true); - this.dropdownView.on('open', this.spy = jasmine.createSpy()); + this.dropdownView.on('opened', this.spy = jasmine.createSpy()); this.dropdownView.open(); }); @@ -173,7 +175,7 @@ describe('DropdownView', function() { expect(this.$menu).toBeVisible(); }); - it('should not trigger open', function() { + it('should not trigger opened', function() { expect(this.spy).not.toHaveBeenCalled(); }); }); @@ -181,7 +183,7 @@ describe('DropdownView', function() { describe('if not open', function() { beforeEach(function() { renderTestDataset(this.dropdownView, false); - this.dropdownView.on('open', this.spy = jasmine.createSpy()); + this.dropdownView.on('opened', this.spy = jasmine.createSpy()); this.dropdownView.open(); }); @@ -190,7 +192,7 @@ describe('DropdownView', function() { expect(this.$menu).toBeVisible(); }); - it('should trigger open', function() { + it('should trigger opened', function() { expect(this.spy).toHaveBeenCalled(); }); }); @@ -199,8 +201,8 @@ describe('DropdownView', function() { describe('#moveCursorUp', function() { beforeEach(function() { this.dropdownView - .on('cursorOn', this.cursorOnSpy = jasmine.createSpy()) - .on('cursorOff', this.cursorOffSpy = jasmine.createSpy()); + .on('cursorMoved', this.cursorMovedSpy = jasmine.createSpy()) + .on('cursorRemoved', this.cursorRemovedSpy = jasmine.createSpy()); }); describe('if not visible', function() { @@ -214,12 +216,12 @@ describe('DropdownView', function() { expect(this.$menu.find('.tt-is-under-cursor')).not.toExist(); }); - it('should not trigger cursorOn', function() { - expect(this.cursorOnSpy).not.toHaveBeenCalled(); + it('should not trigger cursorMoved', function() { + expect(this.cursorMovedSpy).not.toHaveBeenCalled(); }); - it('should not trigger cursorOff', function() { - expect(this.cursorOffSpy).not.toHaveBeenCalled(); + it('should not trigger cursorRemoved', function() { + expect(this.cursorRemovedSpy).not.toHaveBeenCalled(); }); }); @@ -242,8 +244,8 @@ describe('DropdownView', function() { expect($lastSuggestion).toBe($suggestionUnderCursor); }); - it('should trigger cursorOn', function() { - expect(this.cursorOnSpy).toHaveBeenCalled(); + it('should trigger cursorMoved', function() { + expect(this.cursorMovedSpy).toHaveBeenCalled(); }); }); @@ -267,8 +269,8 @@ describe('DropdownView', function() { expect($prevSuggestion).toBe($suggestionUnderCursor); }); - it('should trigger cursorOn', function() { - expect(this.cursorOnSpy).toHaveBeenCalled(); + it('should trigger cursorMoved', function() { + expect(this.cursorMovedSpy).toHaveBeenCalled(); }); }); @@ -288,8 +290,8 @@ describe('DropdownView', function() { expect($suggestionUnderCursor).not.toExist(); }); - it('should trigger cursorOff', function() { - expect(this.cursorOffSpy).toHaveBeenCalled(); + it('should trigger cursorRemoved', function() { + expect(this.cursorRemovedSpy).toHaveBeenCalled(); }); }); }); @@ -298,8 +300,8 @@ describe('DropdownView', function() { describe('#moveCursorDown', function() { beforeEach(function() { this.dropdownView - .on('cursorOn', this.cursorOnSpy = jasmine.createSpy()) - .on('cursorOff', this.cursorOffSpy = jasmine.createSpy()); + .on('cursorMoved', this.cursorMovedSpy = jasmine.createSpy()) + .on('cursorRemoved', this.cursorRemovedSpy = jasmine.createSpy()); }); describe('if not visible', function() { @@ -313,12 +315,12 @@ describe('DropdownView', function() { expect(this.$menu.find('.tt-is-under-cursor')).not.toExist(); }); - it('should not trigger cursorOn', function() { - expect(this.cursorOnSpy).not.toHaveBeenCalled(); + it('should not trigger cursorMoved', function() { + expect(this.cursorMovedSpy).not.toHaveBeenCalled(); }); - it('should not trigger cursorOff', function() { - expect(this.cursorOffSpy).not.toHaveBeenCalled(); + it('should not trigger cursorRemoved', function() { + expect(this.cursorRemovedSpy).not.toHaveBeenCalled(); }); }); @@ -341,8 +343,8 @@ describe('DropdownView', function() { expect($firstSuggestion).toBe($suggestionUnderCursor); }); - it('should trigger cursorOn', function() { - expect(this.cursorOnSpy).toHaveBeenCalled(); + it('should trigger cursorMoved', function() { + expect(this.cursorMovedSpy).toHaveBeenCalled(); }); }); @@ -366,8 +368,8 @@ describe('DropdownView', function() { expect($nextSuggestion).toBe($suggestionUnderCursor); }); - it('should trigger cursorOn', function() { - expect(this.cursorOnSpy).toHaveBeenCalled(); + it('should trigger cursorMoved', function() { + expect(this.cursorMovedSpy).toHaveBeenCalled(); }); }); @@ -387,8 +389,8 @@ describe('DropdownView', function() { expect($suggestionUnderCursor).not.toExist(); }); - it('should trigger cursorOff', function() { - expect(this.cursorOffSpy).toHaveBeenCalled(); + it('should trigger cursorRemoved', function() { + expect(this.cursorRemovedSpy).toHaveBeenCalled(); }); }); }); @@ -462,7 +464,7 @@ describe('DropdownView', function() { describe('if there are no suggestions', function() { beforeEach(function() { - this.dropdownView.on('suggestionsRender', spy = jasmine.createSpy()); + this.dropdownView.on('suggestionsRendered', spy = jasmine.createSpy()); spyOn(this.dropdownView, 'clearSuggestions'); @@ -473,7 +475,7 @@ describe('DropdownView', function() { expect(this.dropdownView.clearSuggestions).toHaveBeenCalledWith('test'); }); - it('should trigger suggestionsRender', function() { + it('should trigger suggestionsRendered', function() { expect(spy).toHaveBeenCalled(); }); }); @@ -481,7 +483,7 @@ describe('DropdownView', function() { describe('if there are suggestions', function() { beforeEach(function() { this.dropdownView - .on('suggestionsRender', this.spy = jasmine.createSpy()); + .on('suggestionsRendered', this.spy = jasmine.createSpy()); spyOn(this.dropdownView, 'clearSuggestions').andCallThrough(); @@ -509,7 +511,7 @@ describe('DropdownView', function() { expect($suggestions.first()).toHaveData('value', 'i am a value'); }); - it('should trigger suggestionsRender', function() { + it('should trigger suggestionsRendered', function() { expect(this.spy).toHaveBeenCalled(); }); }); diff --git a/test/input_view_spec.js b/test/input_view_spec.js index ce6b1437..bb2cfc14 100644 --- a/test/input_view_spec.js +++ b/test/input_view_spec.js @@ -28,23 +28,25 @@ describe('InputView', function() { describe('when input gains focus', function() { beforeEach(function() { - spyOnEvent(this.$input, 'focus'); + this.inputView.on('focused', this.spy = jasmine.createSpy()); + this.$input.blur().focus(); }); - it('should trigger focus', function() { - expect('focus').toHaveBeenTriggeredOn(this.$input); + it('should trigger focused', function() { + expect(this.spy).toHaveBeenCalled(); }); }); describe('when query loses focus', function() { beforeEach(function() { - spyOnEvent(this.$input, 'blur'); + this.inputView.on('blured', this.spy = jasmine.createSpy()); + this.$input.focus().blur(); }); - it('should trigger blur', function() { - expect('blur').toHaveBeenTriggeredOn(this.$input); + it('should trigger blured', function() { + expect(this.spy).toHaveBeenCalled(); }); }); @@ -57,7 +59,7 @@ describe('InputView', function() { this.spies = {}; keys.forEach(function(key) { - that.inputView.on(key, that.spies[key] = jasmine.createSpy()); + that.inputView.on(key + 'Keyed', that.spies[key] = jasmine.createSpy()); }); }); @@ -68,7 +70,7 @@ describe('InputView', function() { simulateKeyEvent(this.$input, 'keydown', KEY_MAP[key]); }); - it('should trigger ' + key, function() { + it('should trigger ' + key + 'Keyed', function() { expect(this.spies[key]).toHaveBeenCalled(); }); }); @@ -77,8 +79,8 @@ describe('InputView', function() { describe('when input', function() { beforeEach(function() { - this.inputView.on('queryChange', this.qcSpy = jasmine.createSpy()); - this.inputView.on('whitespaceChange', this.wcSpy = jasmine.createSpy()); + this.inputView.on('queryChanged', this.qcSpy = jasmine.createSpy()); + this.inputView.on('whitespaceChanged', this.wcSpy = jasmine.createSpy()); }); describe('if query changed', function() { @@ -89,11 +91,11 @@ describe('InputView', function() { simulateKeyEvent(this.$input, 'input', KEY_MAP.NORMAL); }); - it('should trigger queryChange', function() { + it('should trigger queryChanged', function() { expect(this.qcSpy).toHaveBeenCalled(); }); - it('should not trigger whitespaceChange', function() { + it('should not trigger whitespaceChanged', function() { expect(this.wcSpy).not.toHaveBeenCalled(); }); @@ -110,11 +112,11 @@ describe('InputView', function() { simulateKeyEvent(this.$input, 'input', KEY_MAP.NORMAL); }); - it('should trigger whitespaceChange', function() { + it('should trigger whitespaceChanged', function() { expect(this.wcSpy).toHaveBeenCalled(); }); - it('should not trigger queryChange', function() { + it('should not trigger queryChanged', function() { expect(this.qcSpy).not.toHaveBeenCalled(); }); @@ -131,11 +133,11 @@ describe('InputView', function() { simulateKeyEvent(this.$input, 'input', KEY_MAP.NORMAL); }); - it('should not trigger queryChange', function() { + it('should not trigger queryChanged', function() { expect(this.qcSpy).not.toHaveBeenCalled(); }); - it('should not trigger whitespaceChange', function() { + it('should not trigger whitespaceChanged', function() { expect(this.wcSpy).not.toHaveBeenCalled(); }); }); diff --git a/test/typeahead_view_spec.js b/test/typeahead_view_spec.js index 4146ff81..5abdac34 100644 --- a/test/typeahead_view_spec.js +++ b/test/typeahead_view_spec.js @@ -36,9 +36,10 @@ describe('TypeaheadView', function() { // handlers triggered by dropdownView events // ----------------------------------------- - describe('when dropdownView triggers select', function() { + describe('when dropdownView triggers suggestionSelected', function() { beforeEach(function() { - this.dropdownView.trigger('select', { value: 'i am selected' }); + this.dropdownView + .trigger('suggestionSelected', { value: 'i am selected' }); }); it('should update input value', function() { @@ -55,9 +56,9 @@ describe('TypeaheadView', function() { }); }); - describe('when dropdownView triggers cursorOn', function() { + describe('when dropdownView triggers cursorMoved', function() { beforeEach(function() { - this.dropdownView.trigger('cursorOn', { value: 'i am hint' }); + this.dropdownView.trigger('cursorMoved', { value: 'i am hint' }); }); it('should clear hint', function() { @@ -70,24 +71,24 @@ describe('TypeaheadView', function() { }); }); - describe('when dropdownView triggers cursorOff', function() { + describe('when dropdownView triggers cursorRemoved', function() { it('should reset input value to user query', function() { this.inputView.getQuery.andReturn('san '); - this.dropdownView.trigger('cursorOff'); + this.dropdownView.trigger('cursorRemoved'); expect(this.inputView.setInputValue).toHaveBeenCalledWith('san '); }); - _updateHintSpecHelper('dropdownView', 'cursorOff'); + _updateHintSpecHelper('dropdownView', 'cursorRemoved'); }); - describe('when dropdownView triggers suggestionsRender', function() { - _updateHintSpecHelper('dropdownView', 'suggestionsRender'); + describe('when dropdownView triggers suggestionsRendered', function() { + _updateHintSpecHelper('dropdownView', 'suggestionsRendered'); }); - describe('when dropdownView triggers close', function() { + describe('when dropdownView triggers closed', function() { beforeEach(function() { - this.dropdownView.trigger('close'); + this.dropdownView.trigger('closed'); }); it('should clear hint', function() { @@ -98,11 +99,11 @@ describe('TypeaheadView', function() { // handlers triggered by inputView events // -------------------------------------- - describe('when inputView triggers blur', function() { + describe('when inputView triggers blured', function() { beforeEach(function() { this.inputView.getQuery.andReturn('reset'); - this.inputView.trigger('blur'); + this.inputView.trigger('blured'); }); it('should close dropdown unless mouse is over it', function() { @@ -115,14 +116,14 @@ describe('TypeaheadView', function() { }); }); - describe('when inputView triggers enter', function() { + describe('when inputView triggers enterKeyed', function() { beforeEach(function() { this.spy = jasmine.createSpy(); this.dropdownView.getSuggestionUnderCursor .andReturn({ value: 'i am selected' }); - this.inputView.trigger('enter', { preventDefault: this.spy }); + this.inputView.trigger('enterKeyed', { preventDefault: this.spy }); }); it('should update input value', function() { @@ -139,11 +140,11 @@ describe('TypeaheadView', function() { }); }); - describe('when inputView triggers whitespaceChange', function() { - _updateHintSpecHelper('inputView', 'whitespaceChange'); + describe('when inputView triggers whitespaceChanged', function() { + _updateHintSpecHelper('inputView', 'whitespaceChanged'); it('should open the dropdown menu', function() { - this.inputView.trigger('whitespaceChange'); + this.inputView.trigger('whitespaceChanged'); expect(this.dropdownView.open).toHaveBeenCalled(); }); @@ -152,7 +153,7 @@ describe('TypeaheadView', function() { this.typeaheadView.dir = 'ltr'; this.inputView.getLanguageDirection.andReturn('rtl'); - this.inputView.trigger('whitespaceChange'); + this.inputView.trigger('whitespaceChanged'); }); it('should update styling', function() { @@ -163,25 +164,25 @@ describe('TypeaheadView', function() { }); }); - describe('when inputView triggers queryChange', function() { + describe('when inputView triggers queryChanged', function() { it('should open the dropdown menu', function() { - this.inputView.trigger('queryChange'); + this.inputView.trigger('queryChanged'); expect(this.dropdownView.open).toHaveBeenCalled(); }); it('should clear hint', function() { - this.inputView.trigger('queryChange'); + this.inputView.trigger('queryChanged'); expect(this.inputView.setHintValue).toHaveBeenCalledWith(''); }); it('should clear suggestions', function() { - this.inputView.trigger('queryChange'); + this.inputView.trigger('queryChanged'); expect(this.dropdownView.clearSuggestions).toHaveBeenCalled(); }); it('should call dropdownView.renderSuggestions for each dataset', function() { - this.inputView.trigger('queryChange'); + this.inputView.trigger('queryChanged'); expect(this.dropdownView.renderSuggestions.callCount).toBe(3); }); @@ -190,7 +191,7 @@ describe('TypeaheadView', function() { this.typeaheadView.dir = 'ltr'; this.inputView.getLanguageDirection.andReturn('rtl'); - this.inputView.trigger('queryChange'); + this.inputView.trigger('queryChanged'); }); it('should update styling', function() { @@ -201,9 +202,9 @@ describe('TypeaheadView', function() { }); }); - describe('when inputView triggers focus', function() { + describe('when inputView triggers focused', function() { beforeEach(function() { - this.inputView.trigger('focus'); + this.inputView.trigger('focused'); }); it('should open the dropdown menu', function() { @@ -211,11 +212,11 @@ describe('TypeaheadView', function() { }); }); - describe('when inputView triggers esc', function() { + describe('when inputView triggers escKeyed', function() { beforeEach(function() { this.inputView.getQuery.andReturn('reset'); - this.inputView.trigger('esc'); + this.inputView.trigger('escKeyed'); }); it('should close dropdown', function() { @@ -232,7 +233,7 @@ describe('TypeaheadView', function() { describe('when inputView triggers ' + eventType, function() { beforeEach(function() { - this.inputView.trigger(eventType); + this.inputView.trigger(eventType + 'Keyed'); }); it('should open the dropdown menu', function() { @@ -245,12 +246,12 @@ describe('TypeaheadView', function() { }); }); - describe('when inputView triggers tab', function() { + describe('when inputView triggers tabKeyed', function() { describe('if hint is empty string', function() { beforeEach(function() { this.inputView.getHintValue.andReturn(''); - this.inputView.trigger('tab'); + this.inputView.trigger('tabKeyed'); }); it('should not update input value', function() { @@ -263,7 +264,7 @@ describe('TypeaheadView', function() { this.inputView.getQuery.andReturn('app'); this.inputView.getHintValue.andReturn('apple'); - this.inputView.trigger('tab'); + this.inputView.trigger('tabKeyed'); }); it('should update input value', function() { @@ -272,7 +273,7 @@ describe('TypeaheadView', function() { }); }); - describe('when inputView triggers left', function() { + describe('when inputView triggers leftKeyed', function() { beforeEach(function() { this.inputView.getQuery.andReturn('app'); this.inputView.getHintValue.andReturn('apple'); @@ -284,7 +285,7 @@ describe('TypeaheadView', function() { beforeEach(function() { this.inputView.getLanguageDirection.andReturn('ltr'); - this.inputView.trigger('left'); + this.inputView.trigger('leftKeyed'); }); it('should not update input value', function() { @@ -296,7 +297,7 @@ describe('TypeaheadView', function() { beforeEach(function() { this.inputView.getLanguageDirection.andReturn('rtl'); - this.inputView.trigger('left'); + this.inputView.trigger('leftKeyed'); }); it('should update value of input', function() { @@ -308,7 +309,7 @@ describe('TypeaheadView', function() { beforeEach(function() { this.inputView.isCursorAtEnd.andReturn(false); - this.inputView.trigger('left'); + this.inputView.trigger('leftKeyed'); }); it('should not update input value', function() { @@ -317,7 +318,7 @@ describe('TypeaheadView', function() { }); }); - describe('when inputView triggers right', function() { + describe('when inputView triggers rightKeyed', function() { beforeEach(function() { this.inputView.getQuery.andReturn('app'); this.inputView.getHintValue.andReturn('apple'); @@ -329,7 +330,7 @@ describe('TypeaheadView', function() { beforeEach(function() { this.inputView.getLanguageDirection.andReturn('ltr'); - this.inputView.trigger('right'); + this.inputView.trigger('rightKeyed'); }); it('should update input value', function() { @@ -341,7 +342,7 @@ describe('TypeaheadView', function() { beforeEach(function() { this.inputView.getLanguageDirection.andReturn('rtl'); - this.inputView.trigger('right'); + this.inputView.trigger('rightKeyed'); }); it('should not update input value', function() { @@ -353,7 +354,7 @@ describe('TypeaheadView', function() { beforeEach(function() { this.inputView.isCursorAtEnd.andReturn(false); - this.inputView.trigger('right'); + this.inputView.trigger('rightKeyed'); }); it('should not update input value', function() {