1
0
Fork 0

feat(main): Add main wrapper

This commit is contained in:
Pixelastic 2015-11-26 16:10:06 +01:00
parent d65cb99f9a
commit bdac9dd817
8 changed files with 180 additions and 7375 deletions

View file

@ -1,7 +1,7 @@
import documentationSearch from '../index.js';
documentationSearch({
apiKey: 'aaa',
indexName: 'bbb',
inputSelector: 'ccc'
apiKey: '7cad300559017ed67717c9c6cba2666d',
indexName: 'stripe',
inputSelector: '#search-input'
});

View file

@ -1,11 +1 @@
/* global google */
function documentationSearch({
apiKey,
indexName,
inputSelector
}) {
console.info(apiKey, indexName, inputSelector);
}
export default documentationSearch;
module.exports = require('./src/lib/main.js');

7360
npm-shrinkwrap.json generated

File diff suppressed because it is too large Load diff

View file

@ -36,6 +36,11 @@
},
"peerDependencies": {},
"dependencies": {
"lodash": "^3.10.1"
"algoliasearch": "^3.9.2",
"autocomplete.js": "^0.13.0",
"hogan.js": "^3.0.2",
"lodash": "^3.10.1",
"npm-zepto": "^1.1.7",
"to-factory": "^1.0.0"
}
}

View file

@ -0,0 +1,129 @@
import Hogan from 'hogan.js';
import algoliasearch from 'algoliasearch';
import autocomplete from 'autocomplete.js';
import groupBy from 'lodash/collection/groupBy';
import templates from './templates.js';
import utils from './utils.js';
import $ from 'npm-zepto';
/**
* Adds an autocomplete dropdown to an input fields
* @function documentationSearch
* @param {string} options.apiKey Read-only API key
* @param {string} options.indexName Name of the index to target
* @param {string} options.inputSelector CSS selector that targets the input
* @param {Object} [options.algoliaOptions] Options to pass the underlying Algolia client
* @param {Object} [options.autocompleteOptions] Options to pass to the underlying autocomplete instance
* @return {Object}
*/
const usage = `Usage:
documentationSearch({
apiKey,
indexName,
inputSelector,
[ options.{hint,debug} ]
})`;
class DocumentationSearch {
constructor({
apiKey,
indexName,
inputSelector,
algoliaOptions = {
hitsPerPage: 5
},
autocompleteOptions = {
debug: true,
hint: false
}
}) {
this.checkArguments({apiKey, indexName, inputSelector, algoliaOptions, autocompleteOptions});
this.client = algoliasearch('BH4D9OD16A', this.apiKey);
this.autocomplete = autocomplete(this.input, autocompleteOptions, [{
source: this.getSource(),
templates: {
suggestion: this.getSuggestionTemplate()
}
}]);
this.autocomplete.on('autocomplete:selected', this.handleSelected);
}
// TEST:
// - Usage error if no apiKey or no indexName
// - Error if nothing matches
// - Error if matches are not input
// - apiKey nad indexName are set
// - input is set to the Zepto-wrapped inputSelector
checkArguments(args) {
if (!args.apiKey || !args.indexName) {
throw new Error(usage);
}
const input = $(args.inputSelector).filter('input');
if (input.length === 0) {
throw new Error(`Error: No input element in the page matches ${args.inputSelector}`);
}
this.apiKey = args.apiKey;
this.indexName = args.indexName;
this.input = input;
this.algoliaOptions = args.algoliaOptions;
this.autocompleteOptions = args.autocompleteOptions;
}
// Returns a `source` method to be used by `autocomplete`. This will query the
// Algolia index.
getSource() {
return (query, callback) => {
this.client.search([{
indexName: this.indexName,
query: query,
params: this.algoliaOptions
}]).then((data) => {
callback(this.formatHits(data.results[0].hits));
});
};
}
// Given a list of hits returned by the API, will reformat them to be used in
// a Hogan template
formatHits(hits) {
// Group hits by category / subcategory
var groupedHits = groupBy(hits, 'category');
groupedHits.each((list, category) => {
var groupedHitsBySubCategory = groupBy(list, 'subcategory');
var flattenedHits = utils.flattenObject(groupedHitsBySubCategory, 'isSubcategoryHeader');
groupedHits[category] = flattenedHits;
});
groupedHits = utils.flattenObject(groupedHits, 'isCategoryHeader');
// Translate hits into smaller objects to be send to the template
return groupedHits.map((hit) => {
return {
isCategoryHeader: hit.isCategoryHeader,
isSubcategoryHeader: hit.isSubcategoryHeader,
category: hit._highlightResult.category ? hit._highlightResult.category.value : hit.category,
subcategory: hit._highlightResult.subcategory ? hit._highlightResult.subcategory.value : hit.category,
title: hit._highlightResult.display_title ? hit._highlightResult.display_title.value : hit.display_title,
text: hit._snippetResult ? hit._snippetResult.text.value : hit.text,
url: hit.url
};
});
}
getSuggestionTemplate() {
const template = Hogan.compile(templates.suggestion);
return (suggestion) => {
return template.render(suggestion);
};
}
handleSelected(event, suggestion) {
this.autocomplete.autocomplete.setVal('');
window.location.href = suggestion.url;
}
}
export default DocumentationSearch;

6
src/lib/main.js Normal file
View file

@ -0,0 +1,6 @@
import toFactory from 'to-factory';
import DocumentationSearch from './DocumentationSearch';
let documentationSearch = toFactory(DocumentationSearch);
export default documentationSearch;

20
src/lib/templates.js Normal file
View file

@ -0,0 +1,20 @@
let prefix = 'documentationsearch--suggestion';
let templates = {
suggestion: `
<div class="${prefix} {{#isCategoryHeader}}${prefix}__main{{/isCategoryHeader}} {{#isSubcategoryHeader}}${prefix}__secondary{{/isSubcategoryHeader}}">
<div class="${prefix}--category-header">{{{category}}}</div>
<div class="${prefix}--wrapper">
<div class="${prefix}--subcategory">{{{subcategory}}}</div>
<div class="${prefix}--content">
<div class="${prefix}--subcategory">{{{subcategory}}}</div>
<div class="${prefix}--title">{{{title}}}</div>
<div class="${prefix}--text">{{{text}}}</div>
</div>
</div>
</div>
`
};
export default templates;

15
src/lib/utils.js Normal file
View file

@ -0,0 +1,15 @@
import values from 'lodash/object/values';
import flatten from 'lodash/array/flatten';
let utils = {
// Flatten all values into one array, marking the first element with
// `flagName`
flattenObject(o, flagName) {
values(o).map((value) => {
value[0][flagName] = true;
});
return flatten(values);
}
};
export default utils;