Skip to content

Commit

Permalink
Add LocationBias component (#343)
Browse files Browse the repository at this point in the history
* Add LocationBias component

TEST=auto,manual
Tested it locally with backend change.
  • Loading branch information
bwangYext authored and vramesh1 committed Nov 15, 2019
1 parent 1424381 commit 78aa647
Show file tree
Hide file tree
Showing 11 changed files with 286 additions and 2 deletions.
17 changes: 17 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,7 @@ Outline:
- [Navigation Component](#navigation-component)
- [QASubmission Component](#qa-submission-component)
- [SpellCheck Component](#spell-check-component)
- [LocationBias Component](#location-bias-component)
4. [Analytics](#analytics)
- [Click Analytics](#click-analytics)
# Install and Setup
Expand Down Expand Up @@ -734,6 +735,22 @@ ANSWERS.addComponent('SpellCheck', {
})
```

## Location Bias Component

The location component component shows location that used for location bias and allow user to improve accuracy with HTML5 geolocation.

```html
<div class="location-bias-container"></div>
```

```js
ANSWERS.addComponent('LocationBias', {
container: '.location-bias-container',
verticalKey: 'verticalKey', // Optional, the vertical key for the search, default null
updateLocationEl: '.js-locationBias-update-location' // Optional, the element used for updating location
})
```

# Analytics

Answers will track some basic interaction analytics automatically, such as search bar impressions and Call-To-Action clicks. You may add additional, custom analytic events to templates using certain data attributes, explained below. You may also send analytics from external code with the below interface.
Expand Down
5 changes: 5 additions & 0 deletions src/core/core.js
Original file line number Diff line number Diff line change
Expand Up @@ -140,6 +140,9 @@ export default class Core {
if (data[StorageKeys.SPELL_CHECK]) {
this.globalStorage.set(StorageKeys.SPELL_CHECK, data[StorageKeys.SPELL_CHECK]);
}
if (data[StorageKeys.LOCATION_BIAS]) {
this.globalStorage.set(StorageKeys.LOCATION_BIAS, data[StorageKeys.LOCATION_BIAS]);
}
this.globalStorage.delete('skipSpellCheck');
this.globalStorage.delete('queryTrigger');
});
Expand Down Expand Up @@ -168,6 +171,7 @@ export default class Core {
search (queryString, urls) {
this.globalStorage.set(StorageKeys.UNIVERSAL_RESULTS, UniversalResults.searchLoading());
this.globalStorage.set(StorageKeys.SPELL_CHECK, {});
this.globalStorage.set(StorageKeys.LOCATION_BIAS, {});

return this._searcher
.universalSearch(queryString, {
Expand All @@ -187,6 +191,7 @@ export default class Core {
questionText: queryString
}));
this.globalStorage.set(StorageKeys.SPELL_CHECK, data[StorageKeys.SPELL_CHECK]);
this.globalStorage.set(StorageKeys.LOCATION_BIAS, data[StorageKeys.LOCATION_BIAS]);
this.globalStorage.delete('skipSpellCheck');
this.globalStorage.delete('queryTrigger');
});
Expand Down
52 changes: 52 additions & 0 deletions src/core/models/locationbias.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,52 @@
/** @module LocationBias */

/**
* LocationBias is the core state model
* to power the LocationBias component
*/
export default class LocationBias {
constructor (data) {
/**
* The location bias accuracy which are IP, DEVICE and UNKNWON
* @type {string}
*/
this.accuracy = data.accuracy || null;

/**
* The latitude used for location bias
* @type {number}
*/
this.latitude = data.latitude || null;

/**
* The longitude used for location bias
* @type {number}
*/
this.longitude = data.longitude || null;

/**
* The location display name
* @type {string}
*/
this.locationDisplayName = data.locationDisplayName || null;
}

/**
* Create a location bias model from the provided data
* @param {Object} response The location bias response
*/
static from (response) {
if (!response) {
return new LocationBias({
accuracy: 'UNKNOWN'
});
}

return new LocationBias({
accuracy: response.accuracy,
latitude: response.latitude,
longitude: response.longitude,
locationDisplayName: response.locationDisplayName
});
}
}
7 changes: 5 additions & 2 deletions src/core/search/searchdatatransformer.js
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@ import SpellCheck from '../models/spellcheck';
import StorageKeys from '../storage/storagekeys';
import DynamicFilters from '../models/dynamicfilters';
import SearchIntents from '../models/searchintents';
import LocationBias from '../models/locationbias';

/**
* A Data Transformer that takes the response object from a Search request
Expand All @@ -23,7 +24,8 @@ export default class SearchDataTransformer {
[StorageKeys.DIRECT_ANSWER]: DirectAnswer.from(response.directAnswer, formatters),
[StorageKeys.UNIVERSAL_RESULTS]: UniversalResults.from(response, urls, formatters),
[StorageKeys.INTENTS]: SearchIntents.from(response.searchIntents),
[StorageKeys.SPELL_CHECK]: SpellCheck.from(response.spellCheck)
[StorageKeys.SPELL_CHECK]: SpellCheck.from(response.spellCheck),
[StorageKeys.LOCATION_BIAS]: LocationBias.from(response.locationBias)
};
}

Expand All @@ -34,7 +36,8 @@ export default class SearchDataTransformer {
[StorageKeys.VERTICAL_RESULTS]: VerticalResults.from(data.response, formatters),
[StorageKeys.DYNAMIC_FILTERS]: DynamicFilters.from(data.response),
[StorageKeys.INTENTS]: SearchIntents.from(data.response.searchIntents),
[StorageKeys.SPELL_CHECK]: SpellCheck.from(data.response.spellCheck)
[StorageKeys.SPELL_CHECK]: SpellCheck.from(data.response.spellCheck),
[StorageKeys.LOCATION_BIAS]: LocationBias.from(data.response.locationBias)
};
}
}
1 change: 1 addition & 0 deletions src/core/storage/storagekeys.js
Original file line number Diff line number Diff line change
Expand Up @@ -24,5 +24,6 @@ export default {
SEARCH_CONFIG: 'search-config',
SEARCH_OFFSET: 'search-offset',
SPELL_CHECK: 'spell-check',
LOCATION_BIAS: 'location-bias',
SESSIONS_OPT_IN: 'sessions-opt-in'
};
1 change: 1 addition & 0 deletions src/ui/components/component.js
Original file line number Diff line number Diff line change
Expand Up @@ -230,6 +230,7 @@ export default class Component {
this.unMount();
this.mount();
} catch (e) {
console.log(e);
throw new AnswersComponentError(
'Error updating component',
this.constructor.type,
Expand Down
2 changes: 2 additions & 0 deletions src/ui/components/const.js
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@ import SearchComponent from './search/searchcomponent';
import FilterSearchComponent from './search/filtersearchcomponent';
import AutoCompleteComponent from './search/autocompletecomponent';
import SpellCheckComponent from './search/spellcheckcomponent';
import LocationBiasComponent from './search/locationbiascomponent';

import FilterBoxComponent from './filters/filterboxcomponent';
import FilterOptionsComponent from './filters/filteroptionscomponent';
Expand Down Expand Up @@ -47,6 +48,7 @@ export const COMPONENT_MANAGER = new ComponentManager()
.register(FilterSearchComponent)
.register(AutoCompleteComponent)
.register(SpellCheckComponent)
.register(LocationBiasComponent)

// Filter Components
.register(FilterBoxComponent)
Expand Down
129 changes: 129 additions & 0 deletions src/ui/components/search/locationbiascomponent.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,129 @@
import Component from '../component';
import Filter from '../../../core/models/filter';
import StorageKeys from '../../../core/storage/storagekeys';
import DOM from '../../dom/dom';

/**
* LocationBiasComponent will show the user where is used for location bias and allow user to
* improve accuracy with HTML5 geolocation.
*
* @extends Component
*/
export default class LocationBiasComponent extends Component {
constructor (config = {}, systemConfig = {}) {
super(config, systemConfig);

/**
* Recieve updates from storage based on this index
* @type {StorageKey}
*/
this.moduleId = StorageKeys.LOCATION_BIAS;

/**
* The optional vertical key for vertical search configuration
* If not provided, when location updated,
* a universal search will be triggered.
* @type {string}
*/
this._verticalKey = config.verticalKey || null;

/**
* The element used for updating location
* Optionally provided.
* @type {string} CSS selector
*/
this._updateLocationEl = config.updateLocationEl || '.js-locationBias-update-location';

this._locationDisplayName = '';

this._accuracy = '';

this._allowUpdate = true;
}

static get type () {
return 'LocationBias';
}

static defaultTemplateName () {
return 'search/locationbias';
}

onMount () {
if (!this._allowUpdate) {
return;
}
DOM.on(this._updateLocationEl, 'click', (e) => {
if ('geolocation' in navigator) {
navigator.geolocation.getCurrentPosition((position) => {
this.core.globalStorage.set(StorageKeys.GEOLOCATION, {
lat: position.coords.latitude,
lng: position.coords.longitude,
radius: position.coords.accuracy
});
this._doSearch();
}, (err) => {
if (err.code === 1) {
this.core.globalStorage.delete(StorageKeys.GEOLOCATION);
this._allowUpdate = false;
this.setState({
locationDisplayName: this._locationDisplayName,
accuracy: this._accuracy
});
}
});
}
// TODO: Should we throw error or warning here if no geolocation?
});
}

setState (data, val) {
this._locationDisplayName = data.locationDisplayName;
this._accuracy = data.accuracy;
return super.setState(Object.assign({}, data, {
locationDisplayName: this._getLocationDisplayName(data),
accuracyText: this._getAccuracyHelpText(data.accuracy),
isPreciseLocation: data.accuracy === 'DEVICE' && this._allowUpdate,
isUnknownLocation: data.accuracy === 'UNKNOWN',
shouldShow: data.accuracy !== undefined,
allowUpdate: this._allowUpdate
}, val));
}

_getLocationDisplayName (data) {
if (data.accuracy === 'UNKNOWN') {
return 'Unknown Location';
}
return data.locationDisplayName;
}

_getAccuracyHelpText (accuracy) {
switch (accuracy) {
case 'IP':
return 'based on your internet address';
case 'DEVICE':
return 'based on your device';
default:
return '';
}
}

_doSearch () {
let query = this.core.globalStorage.getState(StorageKeys.QUERY);
if (this._verticalKey) {
const allFilters = this.core.globalStorage.getAll(StorageKeys.FILTER);
const totalFilter = allFilters.length > 1
? Filter.and(...allFilters)
: allFilters[0];
const facetFilter = this.core.globalStorage.getAll(StorageKeys.FACET_FILTER)[0];
this.core.verticalSearch(this._verticalKey, {
input: query,
filter: JSON.stringify(totalFilter),
offset: this.core.globalStorage.getState(StorageKeys.SEARCH_OFFSET) || 0,
facetFilter: JSON.stringify(facetFilter)
});
} else {
this.core.search(query);
}
}
}
1 change: 1 addition & 0 deletions src/ui/sass/answers.scss
Original file line number Diff line number Diff line change
Expand Up @@ -22,3 +22,4 @@
@import "modules/AccordionResult";
@import "modules/SpellCheck";
@import "modules/Pagination";
@import "modules/LocationBias";
65 changes: 65 additions & 0 deletions src/ui/sass/modules/_LocationBias.scss
Original file line number Diff line number Diff line change
@@ -0,0 +1,65 @@
/** @define LocationBias */

.yxt-LocationBias
{
$locationBias-text-font-size: $font-size-md !default;
$locationBias-text-font-weight: $font-weight-normal !default;
$locationBias-text-color: $color-text-secondary !default;
$locationBias-bullet-size: $font-size-sm / 2 !default;
$locationBias-bullet-color-default: $locationBias-text-color !default;

padding-bottom: $base-spacing;
display: block;
text-align: center;

padding-top: $base-spacing;
padding-bottom: $base-spacing;

@include Text(
$size: $locationBias-text-font-size,
$weight: $locationBias-text-font-weight,
$color: $locationBias-text-color
);

strong
{
font-weight: $font-weight-semibold;
}

&-bullet
{
height: $locationBias-bullet-size;
width: $locationBias-bullet-size;
display: inline-block;
border-radius: 100%;
background: $locationBias-bullet-color-default;
}

&-unknownLoc
{
background-color: transparent;
border: 1px solid $locationBias-bullet-color-default;
}

&-preciseLoc
{
background: $color-brand-primary;
border: 1px solid $color-brand-primary;
}

&-locSource
{
@media (max-width: $breakpoint-mobile-max)
{
display: block;
}
}

&-updateLoc
{
@include Link-1;
cursor: pointer;
color: $color-brand-primary;
text-decoration: none;
}
}
8 changes: 8 additions & 0 deletions src/ui/templates/search/locationbias.hbs
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
<div class="yxt-LocationBias" {{#unless shouldShow}}style="display:none"{{/unless}}>
<div class="yxt-LocationBias-container">
<span class="yxt-locationBias-bullet {{#if isPreciseLocation}}yxt-locationBias-preciseLoc{{/if}}
{{#if isUnknownLocation}} yxt-LocationBias-unknownLoc {{/if}}"></span>
<strong>{{locationDisplayName}}</strong>
<span class="yxt-locationBias-locSource">{{#if accuracyText}}({{accuracyText}}){{/if}} {{#if allowUpdate}}| <a class="js-locationBias-update-location yxt-locationBias-updateLoc">Update your location</a>{{/if}}</span>
</div>
</div>

0 comments on commit 78aa647

Please sign in to comment.