Skip to content

Commit

Permalink
feat(main): Add main wrapper
Browse files Browse the repository at this point in the history
  • Loading branch information
pixelastic committed Nov 26, 2015
1 parent d65cb99 commit bdac9dd
Show file tree
Hide file tree
Showing 8 changed files with 180 additions and 7,375 deletions.
6 changes: 3 additions & 3 deletions dev/app.js
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
import documentationSearch from '../index.js';

documentationSearch({
apiKey: 'aaa',
indexName: 'bbb',
inputSelector: 'ccc'
apiKey: '7cad300559017ed67717c9c6cba2666d',
indexName: 'stripe',
inputSelector: '#search-input'
});
12 changes: 1 addition & 11 deletions index.js
Original file line number Diff line number Diff line change
@@ -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');
7,360 changes: 0 additions & 7,360 deletions npm-shrinkwrap.json

This file was deleted.

7 changes: 6 additions & 1 deletion package.json
Original file line number Diff line number Diff line change
Expand Up @@ -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"
}
}
129 changes: 129 additions & 0 deletions src/lib/DocumentationSearch.js
Original file line number Diff line number Diff line change
@@ -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 changes: 6 additions & 0 deletions src/lib/main.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
import toFactory from 'to-factory';
import DocumentationSearch from './DocumentationSearch';

let documentationSearch = toFactory(DocumentationSearch);

export default documentationSearch;
20 changes: 20 additions & 0 deletions src/lib/templates.js
Original file line number Diff line number Diff line change
@@ -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 changes: 15 additions & 0 deletions src/lib/utils.js
Original file line number Diff line number Diff line change
@@ -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;

0 comments on commit bdac9dd

Please sign in to comment.