From ff7346a4f20957959021e8de363b2549a55891ec Mon Sep 17 00:00:00 2001 From: Katy DeCorah Date: Wed, 20 Mar 2019 15:54:23 -0400 Subject: [PATCH] fix focus trap (#220) * do not clear if user is tabbing through * Update CHANGELOG.md * build out logic to make this function testable * test there is no focus trap * wrap debounce around function in addeventlistener to properly test on _onKeyDown function --- CHANGELOG.md | 1 + lib/index.js | 25 +++++++++++++------------ test/test.ui.js | 35 +++++++++++++++++++++++++++++++++++ 3 files changed, 49 insertions(+), 12 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 739b92fa..43fd3f60 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -7,6 +7,7 @@ - Upgrade dev dependencies - Remove hardcoded IDs in bounding box exception list - Fix duplicate event bug +- Fix trapped focus [#220](https://github.com/mapbox/mapbox-gl-geocoder/issues/220) ## v3.1.6 - Resolve npm publish failure diff --git a/lib/index.js b/lib/index.js index ee08757b..7d8c1c96 100644 --- a/lib/index.js +++ b/lib/index.js @@ -98,7 +98,7 @@ MapboxGeocoder.prototype = { this._inputEl.type = 'text'; this._inputEl.placeholder = this._getPlaceholderText(); - this._inputEl.addEventListener('keydown', this._onKeyDown); + this._inputEl.addEventListener('keydown', debounce(this._onKeyDown, 200)); this._inputEl.addEventListener('change', this._onChange); var actions = document.createElement('div'); @@ -148,16 +148,17 @@ MapboxGeocoder.prototype = { return this; }, - _onKeyDown: debounce(function(e) { - + _onKeyDown: function(e) { + // if target has shadowRoot, then get the actual active element inside the shadowRoot - var target = e.target.shadowRoot + var target = e.target && e.target.shadowRoot ? e.target.shadowRoot.activeElement : e.target; - if (!target.value) { + var value = target ? target.value : ''; + if (!value) { this.fresh = true; // the user has removed all the text - this._clear(e); + if (e.keyCode !== 9) this._clear(e); return (this._clearEl.style.display = 'none'); } @@ -168,7 +169,7 @@ MapboxGeocoder.prototype = { if (target.value.length >= this.options.minLength) { this._geocode(target.value); } - }, 200), + }, _onChange: function() { if (this._inputEl.value) this._clearEl.style.display = 'block'; @@ -399,9 +400,9 @@ MapboxGeocoder.prototype = { /** * Get the language to use in UI elements and when making search requests - * + * * Look first at the explicitly set options otherwise use the browser's language settings - * + * * @returns {String} The language used by the geocoder */ getLanguage: function(){ @@ -409,13 +410,13 @@ MapboxGeocoder.prototype = { return this.options.language || browserLocale; }, - /** + /** * Get the text to use as the search bar placeholder - * + * * If placeholder is provided in options, then use options.placeholder * Otherwise, if language is provided in options, then use the localized string of the first language if available * Otherwise use the default - * + * * @returns {String} the value to use as the search bar placeholder * @private */ diff --git a/test/test.ui.js b/test/test.ui.js index 4b89500b..bf6b227b 100644 --- a/test/test.ui.js +++ b/test/test.ui.js @@ -4,6 +4,7 @@ var once = require('lodash.once'); var MapboxGeocoder = require('../lib/index'); var mapboxgl = require('mapbox-gl'); var test = require('tape'); +var sinon = require('sinon'); mapboxgl.accessToken = process.env.MapboxAccessToken; @@ -123,5 +124,39 @@ test('Geocoder#inputControl', function(tt) { t.end(); }) + tt.test('_clear is not called on keydown (tab), no focus trap', function(t){ + t.plan(3); + setup({}); + + var inputEl = container.querySelector('.mapboxgl-ctrl-geocoder input'); + var focusSpy = sinon.spy(inputEl, 'focus'); + inputEl.focus(); + t.equal(focusSpy.called, true, 'input is focused'); + var keySpy = sinon.spy(geocoder,'_onKeyDown'); + var clearSpy = sinon.spy(geocoder, '_clear'); + geocoder._onKeyDown(new KeyboardEvent('keydown',{ code: 9, keyCode: 9 })); + t.equal(keySpy.called, true, '_onKeyDown called'); + t.equal(clearSpy.called, false, '_clear should not be called'); + + t.end(); + }); + + tt.test('_clear is called on keydown (not tab)', function(t){ + t.plan(3); + setup({}); + + var inputEl = container.querySelector('.mapboxgl-ctrl-geocoder input'); + var focusSpy = sinon.spy(inputEl, 'focus'); + inputEl.focus(); + t.equal(focusSpy.called, true, 'input is focused'); + var keySpy = sinon.spy(geocoder,'_onKeyDown'); + var clearSpy = sinon.spy(geocoder, '_clear'); + geocoder._onKeyDown(new KeyboardEvent('keydown',{ code: 1, keyCode: 1 })); + t.equal(keySpy.called, true, '_onKeyDown called'); + t.equal(clearSpy.called, true, '_clear should be called'); + + t.end(); + }); + tt.end(); });