Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion .github/PULL_REQUEST_TEMPLATE.md
Original file line number Diff line number Diff line change
Expand Up @@ -3,4 +3,4 @@
- [ ] briefly describe the changes in this PR
- [ ] write tests for all new functionality
- [ ] run `npm run docs` and commit changes to API.md
- [ ] update CHANGELOG.md with changes under `master` heading before merging
- [ ] update CHANGELOG.md with changes under `main` heading before merging
45 changes: 45 additions & 0 deletions .github/workflows/main.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,45 @@
on:
push:
branches: [ "main" ]
pull_request:
branches: [ "main" ]

jobs:
test:
runs-on: ubuntu-latest
timeout-minutes: 5

steps:
- uses: actions/checkout@v4

- name: Setup Node.js
uses: actions/setup-node@v4
with:
node-version: 22.x
cache: 'npm'

- uses: actions/cache@v4
with:
path: |
~/.npm
node_modules
key: ${{ runner.os }}-npm-${{ hashFiles('**/package-lock.json') }}
restore-keys: |
${{ runner.os }}-npm-

- name: Install dependencies
run: npm ci

- name: Install Firefox
run: |
sudo apt-get update
sudo apt-get install -y firefox

- name: Run tests with Smokestack and Firefox
run: |
export DISPLAY=:99
Xvfb :99 -screen 0 1024x768x24 2> /dev/null &
export MapboxAccessToken=${{ secrets.MAPBOX_ACCESS_TOKEN }}
npm test
env:
MAPBOX_ACCESS_TOKEN: ${{ secrets.MAPBOX_ACCESS_TOKEN }}
2 changes: 1 addition & 1 deletion .vscode/settings.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"editor.formatOnSave": false,
"editor.codeActionsOnSave": {
"source.fixAll.eslint": false
"source.fixAll.eslint": "never"
}
}
1 change: 1 addition & 0 deletions API.md
Original file line number Diff line number Diff line change
Expand Up @@ -128,6 +128,7 @@ A geocoder component using the [Mapbox Geocoding API][74]
* `options.routing` **[Boolean][80]** Specify whether to request additional metadata about the recommended navigation destination corresponding to the feature or not. Only applicable for address features. (optional, default `false`)
* `options.worldview` **[String][76]** Filter results to geographic features whose characteristics are defined differently by audiences belonging to various regional, cultural, or political groups. (optional, default `"us"`)
* `options.enableGeolocation` **[Boolean][80]** If `true` enable user geolocation feature. (optional, default `false`)
* `options.useBrowserFocus` **[Boolean][80]** If `true`, the geocoder will use the browser's focus event to show suggestions. If `false`, it will only highlight active suggestions and Tab will not propagate to the suggestions list. (optional, default `false`)
* `options.addressAccuracy` **(`"address"` | `"street"` | `"place"` | `"country"`)** The accuracy for the geolocation feature with which we define the address line to fill. The browser API returns the user's position with accuracy, and sometimes we can get the neighbor's address. To prevent receiving an incorrect address, you can reduce the accuracy of the definition. (optional, default `"street"`)

### Examples
Expand Down
2 changes: 2 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -1,5 +1,7 @@
## HEAD

- Introduce `useBrowserFocus` option to use the browser's native focus management instead of the geocoder's custom focus management. This is useful for accessibility.

## 5.0.3

### Features / Improvements 🚀
Expand Down
3 changes: 1 addition & 2 deletions debug/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -27,8 +27,6 @@ mapDiv.style.bottom = 0;

var map = new mapboxgl.Map({
container: mapDiv,
// update to Standard after fix of GLJS-624
style: 'mapbox://styles/mapbox/streets-v12',
center: [-79.4512, 43.6568],
zoom: 13
});
Expand Down Expand Up @@ -75,6 +73,7 @@ var coordinatesGeocoder = function(query) {
var geocoder = new MapboxGeocoder({
accessToken: mapboxgl.accessToken,
trackProximity: true,
useBrowserFocus: true,
enableGeolocation: true,
localGeocoder: function(query) {
return coordinatesGeocoder(query);
Expand Down
132 changes: 131 additions & 1 deletion lib/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -75,6 +75,7 @@ function getFooterNode() {
* @param {Boolean} [options.routing=false] Specify whether to request additional metadata about the recommended navigation destination corresponding to the feature or not. Only applicable for address features.
* @param {String} [options.worldview="us"] Filter results to geographic features whose characteristics are defined differently by audiences belonging to various regional, cultural, or political groups.
* @param {Boolean} [options.enableGeolocation=false] If `true` enable user geolocation feature.
* @param {Boolean} [options.useBrowserFocus=false] If `true`, the geocoder will use the browser's focus event to show suggestions. If `false`, it will only highlight active suggestions and Tab will not propagate to the suggestions list.
* @param {('address'|'street'|'place'|'country')} [options.addressAccuracy="street"] The accuracy for the geolocation feature with which we define the address line to fill. The browser API returns the user's position with accuracy, and sometimes we can get the neighbor's address. To prevent receiving an incorrect address, you can reduce the accuracy of the definition.
* @example
* var geocoder = new MapboxGeocoder({ accessToken: mapboxgl.accessToken });
Expand Down Expand Up @@ -110,6 +111,7 @@ MapboxGeocoder.prototype = {
clearOnBlur: false,
enableGeolocation: false,
addressAccuracy: 'street',
useBrowserFocus: false,
getItemValue: function(item) {
return item.place_name
},
Expand Down Expand Up @@ -211,6 +213,8 @@ MapboxGeocoder.prototype = {
this._clear = this._clear.bind(this);
this._clearOnBlur = this._clearOnBlur.bind(this);
this._geolocateUser = this._geolocateUser.bind(this);
this._onSuggestionItemFocus = this._onSuggestionItemFocus.bind(this);
this._onSuggestionItemKeyDown = this._onSuggestionItemKeydown.bind(this);

var el = (this.container = document.createElement('div'));
el.className = 'mapboxgl-ctrl-geocoder mapboxgl-ctrl';
Expand Down Expand Up @@ -261,7 +265,6 @@ MapboxGeocoder.prototype = {

el.appendChild(searchIcon);
el.appendChild(this._inputEl);
el.appendChild(actions);

if (this.options.enableGeolocation && this.geolocation.isSupport()) {
this._geolocateEl = document.createElement('button');
Expand All @@ -277,20 +280,85 @@ MapboxGeocoder.prototype = {
}

var typeahead = this._typeahead = new Typeahead(this._inputEl, [], {
hideOnBlur: !this.options.useBrowserFocus,
filter: false,
minLength: this.options.minLength,
limit: this.options.limit
});

// To preserve tab navigation order
el.insertBefore(actions, typeahead.list.wrapper);

this.setRenderFunction(this.options.render);
typeahead.getItemValue = this.options.getItemValue;

const handleKeyDownTypeahead = this._typeahead.handleKeyDown.bind(this._typeahead);
const handleKeyUpTypeahead = this._typeahead.handleKeyUp.bind(this._typeahead);

this._typeahead.handleKeyUp

if (this.options.useBrowserFocus) {
this._typeahead.handleKeyDown = function(e) {
if (e.keyCode === 9 && !typeahead.list.isEmpty()) {
return;
}
// Arrow down
if (e.keyCode === 40) {
this._typeahead.list.active = 0;
this._typeahead.list.element.querySelectorAll('li').forEach(function(item) {
item.classList.remove('active');
});
this._typeahead.list.element.querySelectorAll('li')[0].classList.add('active');
this._typeahead.list.element.querySelectorAll('li')[0].focus();
return;
// Arrow up
} else if (e.keyCode === 38) {
this._typeahead.list.active = typeahead.list.items.length - 1;
this._typeahead.list.element.querySelectorAll('li').forEach(function(item) {
item.classList.remove('active');
});
this._typeahead.list.element.querySelectorAll('li')[this._typeahead.list.active].classList.add('active');
this._typeahead.list.element.querySelectorAll('li')[this._typeahead.list.active].focus();
return;
}
handleKeyDownTypeahead(e);
}.bind(this);

this._typeahead.handleKeyUp = function(e) {
if (e && e.keyCode === 16) {
e.preventDefault();
return;
}

handleKeyUpTypeahead(e);
}
}

// Add support for footer.
var parentDraw = typeahead.list.draw;
var footerNode = this._footerNode = getFooterNode();
var self = this;
typeahead.list.draw = function() {
if (self.options.useBrowserFocus) {
typeahead.list.element.querySelectorAll('li').forEach(function(item) {
item.removeEventListener('focus', self._onSuggestionItemFocus);
item.removeEventListener('keydown', self._onSuggestionItemKeyDown);
});
}
parentDraw.call(this);

if (self.options.useBrowserFocus) {
typeahead.list.element.querySelectorAll('li').forEach(function(item, index) {
if (index === 0) {
item.focus();
}
item.setAttribute('data-index', index);
item.tabIndex = 0;
item.addEventListener('focus', this._onSuggestionItemFocus);
item.addEventListener('keydown', this._onSuggestionItemKeyDown);
}.bind(self));
}

footerNode.addEventListener('mousedown', function() {
this.selectingListItem = true;
}.bind(this));
Expand Down Expand Up @@ -319,6 +387,62 @@ MapboxGeocoder.prototype = {
return el;
},

_onSuggestionItemKeydown(e) {
const keyCode = e.keyCode;
if (keyCode === 9) {
return;
}

if (keyCode === 13) {
e.preventDefault();
const activeItem = this._typeahead.list.element.querySelector('.active');
if (activeItem) {
const itemIndex = activeItem.getAttribute('data-index');
const item = this._typeahead.list.items[itemIndex];
if (item) {
this._typeahead.value(item.original);
this._typeahead.list.hide();
}
}
} else if (keyCode === 38 || keyCode === 40) {
const items = this._typeahead.list.element.querySelectorAll('li');
if (items.length > 0) {
if (keyCode === 38) { // Arrow up
if (this._typeahead.list.active > 0) {
e.preventDefault();
this._typeahead.list.active--;
} else {
this._typeahead.el.focus();
return;
}
} else if (keyCode === 40) { // Arrow down
e.preventDefault();
if (this._typeahead.list.active < items.length - 1) {
this._typeahead.list.active++;
} else {
return;
}
}
items.forEach(function(item) {
item.classList.remove('active');
});
const activeItem = items[this._typeahead.list.active];
if (activeItem) {
activeItem.classList.add('active');
activeItem.focus();
}
}
}
},

_onSuggestionItemFocus(e) {
this._typeahead.list.active = e.target.getAttribute('data-index');
this._typeahead.list.element.querySelectorAll('li').forEach(function(item) {
item.classList.remove('active');
});
e.target.classList.add('active');
},

_geolocateUser: function () {
this._hideGeolocateButton();
this._showLoadingIcon();
Expand Down Expand Up @@ -821,6 +945,12 @@ MapboxGeocoder.prototype = {
clear: function(ev) {
this._clear(ev);
this._inputEl.focus();
// We don't hide on blur when using browser focus
// because the list is hidden on blur by default.
// So we hide it here manually.
if (this.options.useBrowserFocus) {
this._typeahead.list.hide();
}
},


Expand Down
Loading