diff --git a/src/autocomplete/typeahead.js b/src/autocomplete/typeahead.js index 551187c9d..b262a7f05 100644 --- a/src/autocomplete/typeahead.js +++ b/src/autocomplete/typeahead.js @@ -544,6 +544,17 @@ function buildDom(options) { type: $input.attr('type') }); + // Use ariaLabelledBy option if specified + var ariaLabelledBy = options.ariaLabelledBy; + if (ariaLabelledBy === false) { + // If it is explicity false, null the field + ariaLabelledBy = null; + } else if (!ariaLabelledBy && $input.attr('placeholder')) { + // If a placeholder is set, label this field with itself, which in this case, + // is an explicit pointer to use the placeholder attribute value. + ariaLabelledBy = $input.attr('id'); + } + $input .addClass(_.className(options.cssClasses.prefix, options.cssClasses.input, true)) .attr({ @@ -562,9 +573,7 @@ function buildDom(options) { options.datasets[0] && options.datasets[0].displayKey ? 'both' : 'list'), // Indicates whether the dropdown it controls is currently expanded or collapsed 'aria-expanded': 'false', - // If a placeholder is set, label this field with itself, which in this case, - // is an explicit pointer to use the placeholder attribute value. - 'aria-labelledby': ($input.attr('placeholder') ? $input.attr('id') : null), + 'aria-labelledby': ariaLabelledBy, // Explicitly point to the listbox, // which is a list of suggestions (aka options) 'aria-owns': options.listboxId diff --git a/test/unit/typeahead_spec.js b/test/unit/typeahead_spec.js index c727e7a30..95cc22252 100644 --- a/test/unit/typeahead_spec.js +++ b/test/unit/typeahead_spec.js @@ -94,7 +94,6 @@ describe('Typeahead', function() { var that = this; waitsForAndRuns(function() { return that.dropdown.close.calls.count(); }, done, 100); }); - }); describe('when dropdown triggers suggestionClicked with undefined displayKey', function() { @@ -871,17 +870,69 @@ describe('Typeahead', function() { }); describe('when set autoWidth option', function() { - it ('should set default to true', function() { + it('should set default to true', function() { this.dropdown.trigger('redrawn'); expect(this.view.autoWidth).toBeTruthy(); expect(/\d{3}px/.test(this.view.$node[0].style.width)).toBeTruthy(); }); - it ('should not put width style when autoWidth is false', function() { + it('should not put width style when autoWidth is false', function() { this.view.autoWidth = false; this.dropdown.trigger('redrawn'); expect(this.view.autoWidth).toBeFalsy(); expect(this.view.$node[0].style.width).toBeFalsy(); }); }); + + describe('when ariaLabelledBy is set', function() { + beforeEach(function() { + this.view.destroy(); + }); + + describe('when set to a specific id', function() { + it('should set aria-labelledby to the specified id', function() { + this.view = new Typeahead({ + input: this.$input, + ariaLabelledBy: 'custom-id-attr' + }); + + expect(this.$input.attr('aria-labelledby')).toBe('custom-id-attr'); + }); + }); + + describe('when set to false', function() { + it('should set aria-labelledby to null', function() { + this.view = new Typeahead({ + input: this.$input, + ariaLabelledBy: false + }); + + expect(this.$input.attr('aria-labelledby')).toBeUndefined(); + }); + }); + + describe('when not set', function() { + beforeEach(function() { + this.$input.attr('id', 'custom-input-id'); + }); + + it('should set aria-labelledby to null if no placeholder specified', function() { + this.view = new Typeahead({ + input: this.$input + }); + + expect(this.$input.attr('aria-labelledby')).toBeUndefined(); + }); + + it('should set aria-labelledby to the input id if a placeholder is specified', function() { + this.$input.attr('placeholder', 'custom placeholder'); + + this.view = new Typeahead({ + input: this.$input + }); + + expect(this.$input.attr('aria-labelledby')).toBe('custom-input-id'); + }); + }); + }); });