Merge branch 'feat/v3' of github.com:algolia/docsearch into feat/v3
This commit is contained in:
commit
bbeb2f4cee
6 changed files with 139 additions and 120 deletions
|
|
@ -1,61 +1,51 @@
|
|||
<!DOCTYPE html>
|
||||
<html lang="en">
|
||||
|
||||
<head>
|
||||
|
||||
<head>
|
||||
<meta charset="utf-8" />
|
||||
<title>DocSearch playground</title>
|
||||
<link rel="stylesheet" href="https://cdn.jsdelivr.net/bootstrap/3.3.6/css/bootstrap.min.css" />
|
||||
<!-- at the end of the HEAD -->
|
||||
<link rel="stylesheet" href="http://127.0.0.1:8080/docsearch.min.css" />
|
||||
|
||||
</head>
|
||||
|
||||
<body>
|
||||
</head>
|
||||
|
||||
<body>
|
||||
<div class="container">
|
||||
<div class="mt-1">
|
||||
<h1> Search thanks to the assets served at http://127.0.0.1:8080/ </h1>
|
||||
<p>Do not forget to hard refresh: cmd + maj + R</p>
|
||||
|
||||
</div>
|
||||
<div class="col-md-12">
|
||||
|
||||
<div class="input-group">
|
||||
<input type="search" class="form-control" id="q">
|
||||
</div>
|
||||
</div>
|
||||
<div class="mt-1">
|
||||
<p>2</p>
|
||||
|
||||
</div>
|
||||
<div class="col-md-12">
|
||||
|
||||
<div class="input-group">
|
||||
<input type="search" class="form-control" id="z">
|
||||
</div>
|
||||
<div class="mt-1">
|
||||
<h1>Assets are served from http://127.0.0.1:8080/ </h1>
|
||||
</div>
|
||||
<div class="col-md-12">
|
||||
<div class="input-group">
|
||||
<input type="search" placeholder="DocSearch input" class="form-control" id="q">
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
|
||||
<!-- at the end of the BODY -->
|
||||
<script type="text/javascript" src="http://127.0.0.1:8080/docsearch.min.js"></script>
|
||||
<script type="text/javascript"> docsearch({
|
||||
apiKey: '25626fae796133dc1e734c6bcaaeac3c',
|
||||
indexName: 'docsearch',
|
||||
inputSelector: '#q,#z',
|
||||
debug: true // Set debug to true if you want to inspect the dropdown
|
||||
});
|
||||
<script type="text/javascript" src="http://127.0.0.1:8080/docsearch.js"></script>
|
||||
<script type="text/javascript">
|
||||
docsearch({
|
||||
apiKey: '25626fae796133dc1e734c6bcaaeac3c',
|
||||
indexName: 'docsearch',
|
||||
inputSelector: '#q',
|
||||
handleSelected(input, event, suggestion) {
|
||||
console.info(input);
|
||||
console.info(event);
|
||||
console.info(suggestion);
|
||||
},
|
||||
debug: true // Set debug to true if you want to inspect the dropdown
|
||||
});
|
||||
</script>
|
||||
<style>
|
||||
.container {
|
||||
margin: 10%;
|
||||
}
|
||||
|
||||
.input-group {
|
||||
width: 60%;
|
||||
margin: auto;
|
||||
margin-top: 10%;
|
||||
}
|
||||
.container {
|
||||
margin: 10%;
|
||||
}
|
||||
|
||||
.input-group {
|
||||
width: 60%;
|
||||
margin: auto;
|
||||
margin-top: 10%;
|
||||
}
|
||||
</style>
|
||||
</body>
|
||||
</body>
|
||||
|
||||
</html>
|
||||
|
|
|
|||
|
|
@ -3,9 +3,9 @@
|
|||
yarn run build:css
|
||||
|
||||
echo "Find all files at http://127.0.0.1:8080/";
|
||||
open scripts/playground.html
|
||||
parallelshell \
|
||||
'webpack --config webpack.serve.config.babel.js -w' \
|
||||
'onchange "./src/styles/*.scss" -- yarn run build:css' \
|
||||
'live-server ./dist/cdn' \
|
||||
'live-server --no-browser ./dist/cdn' \
|
||||
"${BROWSER} scripts/playground.html"
|
||||
|
||||
|
|
|
|||
|
|
@ -3,5 +3,6 @@ set -e
|
|||
jest \
|
||||
--no-cache \
|
||||
--watch \
|
||||
--no-watchman \
|
||||
./src/lib/
|
||||
|
||||
|
|
|
|||
|
|
@ -64,7 +64,7 @@ class DocSearch {
|
|||
this.apiKey = apiKey;
|
||||
this.appId = appId;
|
||||
this.indexName = indexName;
|
||||
this.inputs = DocSearch.getInputsFromSelector(inputSelector);
|
||||
this.input = DocSearch.getInputFromSelector(inputSelector);
|
||||
this.algoliaOptions = { hitsPerPage: 5, ...algoliaOptions };
|
||||
const autocompleteOptionsDebug =
|
||||
autocompleteOptions && autocompleteOptions.debug
|
||||
|
|
@ -79,48 +79,50 @@ class DocSearch {
|
|||
this.autocompleteOptions.cssClasses.prefix || 'ds';
|
||||
|
||||
// eslint-disable-next-line no-param-reassign
|
||||
handleSelected = handleSelected || this.handleSelected;
|
||||
|
||||
this.isSimpleLayout = layout === 'simple';
|
||||
|
||||
this.client = algoliasearch(this.appId, this.apiKey);
|
||||
this.client.addAlgoliaAgent(`docsearch.js ${version}`);
|
||||
|
||||
for (let i = 0; i < this.inputs.length; i++) {
|
||||
let uniqInput = this.inputs[i];
|
||||
if (uniqInput) {
|
||||
if (enhancedSearchInput) {
|
||||
uniqInput = DocSearch.injectSearchBox(uniqInput);
|
||||
}
|
||||
this.autocomplete = autocomplete(uniqInput, autocompleteOptions, [
|
||||
{
|
||||
source: this.getAutocompleteSource(transformData, queryHook),
|
||||
templates: {
|
||||
suggestion: DocSearch.getSuggestionTemplate(this.isSimpleLayout),
|
||||
footer: templates.footer,
|
||||
empty: DocSearch.getEmptyTemplate(),
|
||||
},
|
||||
},
|
||||
]);
|
||||
if (handleSelected) {
|
||||
let handleSelectedFn = null;
|
||||
if (typeof handleSelected === 'function') {
|
||||
handleSelectedFn = handleSelected;
|
||||
} else {
|
||||
handleSelectedFn = this.handleSelected;
|
||||
}
|
||||
this.autocomplete.on(
|
||||
'autocomplete:selected',
|
||||
handleSelectedFn.bind(null, this.autocomplete.autocomplete)
|
||||
);
|
||||
}
|
||||
this.autocomplete.on(
|
||||
'autocomplete:shown',
|
||||
this.handleShown.bind(null, uniqInput)
|
||||
);
|
||||
if (enhancedSearchInput) {
|
||||
this.input = DocSearch.injectSearchBox(this.input);
|
||||
}
|
||||
|
||||
if (enhancedSearchInput) {
|
||||
DocSearch.bindSearchBoxEvent();
|
||||
}
|
||||
}
|
||||
this.autocomplete = autocomplete(this.input, autocompleteOptions, [
|
||||
{
|
||||
source: this.getAutocompleteSource(transformData, queryHook),
|
||||
templates: {
|
||||
suggestion: DocSearch.getSuggestionTemplate(this.isSimpleLayout),
|
||||
footer: templates.footer,
|
||||
empty: DocSearch.getEmptyTemplate(),
|
||||
},
|
||||
},
|
||||
]);
|
||||
|
||||
// If user defined its own handleSelected, we prevent clicks on suggestions
|
||||
// link to do anything
|
||||
if (handleSelected) {
|
||||
$('.algolia-autocomplete').on('click', '.ds-suggestions a', event => {
|
||||
event.preventDefault();
|
||||
});
|
||||
}
|
||||
|
||||
// Click on suggestions will follow the link, but keyboard navigation still
|
||||
// need the handleSelected
|
||||
this.autocomplete.on(
|
||||
'autocomplete:selected',
|
||||
handleSelected.bind(null, this.autocomplete.autocomplete)
|
||||
);
|
||||
|
||||
this.autocomplete.on(
|
||||
'autocomplete:shown',
|
||||
this.handleShown.bind(null, this.input)
|
||||
);
|
||||
|
||||
if (enhancedSearchInput) {
|
||||
DocSearch.bindSearchBoxEvent();
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -143,7 +145,7 @@ class DocSearch {
|
|||
);
|
||||
}
|
||||
|
||||
if (!DocSearch.getInputsFromSelector(args.inputSelector)) {
|
||||
if (!DocSearch.getInputFromSelector(args.inputSelector)) {
|
||||
throw new Error(
|
||||
`Error: No input element in the page matches ${args.inputSelector}`
|
||||
);
|
||||
|
|
@ -179,26 +181,14 @@ class DocSearch {
|
|||
|
||||
/**
|
||||
* Returns the matching input from a CSS selector, null if none matches
|
||||
* @function getInputsFromSelector
|
||||
* @function getInputFromSelector
|
||||
* @param {string} selector CSS selector that matches the search
|
||||
* input of the page
|
||||
* @returns {void}
|
||||
*/
|
||||
static getInputsFromSelector(selector) {
|
||||
if (!selector.length) {
|
||||
return null;
|
||||
} else {
|
||||
const selectors = selector.split(',');
|
||||
if (selectors.length === 1) {
|
||||
const input = $(selector).filter('input');
|
||||
return input.length ? [$(input[0])] : null;
|
||||
} else {
|
||||
return selectors.map(s => {
|
||||
const input = $(s).filter('input');
|
||||
return input.length ? $(input[0]) : null;
|
||||
});
|
||||
}
|
||||
}
|
||||
static getInputFromSelector(selector) {
|
||||
const input = $(selector).filter('input');
|
||||
return input.length ? $(input[0]) : null;
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
|
|||
|
|
@ -46,7 +46,7 @@ describe('DocSearch', () => {
|
|||
};
|
||||
|
||||
sinon.spy(DocSearch, 'checkArguments');
|
||||
sinon.stub(DocSearch, 'getInputsFromSelector').returns(true);
|
||||
sinon.stub(DocSearch, 'getInputFromSelector').returns(true);
|
||||
|
||||
DocSearch.__Rewire__('algoliasearch', AlgoliaSearch);
|
||||
DocSearch.__Rewire__('autocomplete', AutoComplete);
|
||||
|
|
@ -54,7 +54,7 @@ describe('DocSearch', () => {
|
|||
|
||||
afterEach(() => {
|
||||
DocSearch.checkArguments.restore();
|
||||
DocSearch.getInputsFromSelector.restore();
|
||||
DocSearch.getInputFromSelector.restore();
|
||||
DocSearch.__ResetDependency__('algoliasearch');
|
||||
DocSearch.__ResetDependency__('autocomplete');
|
||||
});
|
||||
|
|
@ -136,13 +136,13 @@ describe('DocSearch', () => {
|
|||
it('should pass the input element as an instance property', () => {
|
||||
// Given
|
||||
const options = defaultOptions;
|
||||
DocSearch.getInputsFromSelector.returns($('<span>foo</span>'));
|
||||
DocSearch.getInputFromSelector.returns($('<span>foo</span>'));
|
||||
|
||||
// When
|
||||
const actual = new DocSearch(options);
|
||||
|
||||
// Then
|
||||
const $inputs = actual.inputs;
|
||||
const $inputs = actual.input;
|
||||
expect($inputs.text()).toEqual('foo');
|
||||
expect($inputs[0].tagName).toEqual('SPAN');
|
||||
});
|
||||
|
|
@ -194,7 +194,7 @@ describe('DocSearch', () => {
|
|||
autocompleteOptions: { anOption: '44' },
|
||||
};
|
||||
const $input = $('<input name="foo" />');
|
||||
DocSearch.getInputsFromSelector.returns([$input]);
|
||||
DocSearch.getInputFromSelector.returns($input);
|
||||
|
||||
// When
|
||||
new DocSearch(options);
|
||||
|
|
@ -211,7 +211,7 @@ describe('DocSearch', () => {
|
|||
});
|
||||
it('should listen to the selected and shown event of autocomplete', () => {
|
||||
// Given
|
||||
const options = { ...defaultOptions, handleSelected: true };
|
||||
const options = { ...defaultOptions, handleSelected() {} };
|
||||
|
||||
// When
|
||||
new DocSearch(options);
|
||||
|
|
@ -229,8 +229,8 @@ describe('DocSearch', () => {
|
|||
});
|
||||
|
||||
afterEach(() => {
|
||||
if (DocSearch.getInputsFromSelector.restore) {
|
||||
DocSearch.getInputsFromSelector.restore();
|
||||
if (DocSearch.getInputFromSelector.restore) {
|
||||
DocSearch.getInputFromSelector.restore();
|
||||
}
|
||||
});
|
||||
|
||||
|
|
@ -262,7 +262,7 @@ describe('DocSearch', () => {
|
|||
apiKey: 'apiKey',
|
||||
indexName: 'indexName',
|
||||
};
|
||||
sinon.stub(DocSearch, 'getInputsFromSelector').returns(false);
|
||||
sinon.stub(DocSearch, 'getInputFromSelector').returns(false);
|
||||
|
||||
// When
|
||||
expect(() => {
|
||||
|
|
@ -271,10 +271,10 @@ describe('DocSearch', () => {
|
|||
});
|
||||
});
|
||||
|
||||
describe('getInputsFromSelector', () => {
|
||||
let getInputsFromSelector;
|
||||
describe('getInputFromSelector', () => {
|
||||
let getInputFromSelector;
|
||||
beforeEach(() => {
|
||||
getInputsFromSelector = DocSearch.getInputsFromSelector;
|
||||
getInputFromSelector = DocSearch.getInputFromSelector;
|
||||
});
|
||||
|
||||
it('should return null if no element matches the selector', () => {
|
||||
|
|
@ -282,7 +282,7 @@ describe('DocSearch', () => {
|
|||
const selector = '.i-do-not-exist > at #all';
|
||||
|
||||
// When
|
||||
const actual = getInputsFromSelector(selector);
|
||||
const actual = getInputFromSelector(selector);
|
||||
|
||||
// Then
|
||||
expect(actual).toEqual(null);
|
||||
|
|
@ -292,7 +292,7 @@ describe('DocSearch', () => {
|
|||
const selector = '.i-am-a-span';
|
||||
|
||||
// When
|
||||
const actual = getInputsFromSelector(selector);
|
||||
const actual = getInputFromSelector(selector);
|
||||
|
||||
// Then
|
||||
expect(actual).toEqual(null);
|
||||
|
|
@ -302,10 +302,10 @@ describe('DocSearch', () => {
|
|||
const selector = '#input';
|
||||
|
||||
// When
|
||||
const actual = getInputsFromSelector(selector);
|
||||
const actual = getInputFromSelector(selector);
|
||||
|
||||
// Then
|
||||
expect($.zepto.isZ(actual[0])).toBe(true);
|
||||
expect($.zepto.isZ(actual)).toBe(true);
|
||||
});
|
||||
});
|
||||
|
||||
|
|
@ -390,13 +390,12 @@ describe('DocSearch', () => {
|
|||
});
|
||||
|
||||
describe('handleSelected', () => {
|
||||
it('should change the location', () => {
|
||||
it('should change the location if no handleSelected specified', () => {
|
||||
// Given
|
||||
const options = {
|
||||
apiKey: 'key',
|
||||
indexName: 'foo',
|
||||
inputSelector: '#input',
|
||||
handleSelected: true,
|
||||
};
|
||||
|
||||
// When
|
||||
|
|
@ -412,6 +411,45 @@ describe('DocSearch', () => {
|
|||
resolve();
|
||||
});
|
||||
});
|
||||
it('should call the custom handleSelected if defined', () => {
|
||||
// Given
|
||||
const customHandleSelected = jest.fn();
|
||||
const options = {
|
||||
apiKey: 'key',
|
||||
indexName: 'foo',
|
||||
inputSelector: '#input',
|
||||
handleSelected: customHandleSelected,
|
||||
};
|
||||
const expectedInput = expect.objectContaining({
|
||||
open: expect.any(Function),
|
||||
});
|
||||
const expectedEvent = expect.objectContaining({
|
||||
type: 'autocomplete:selected'
|
||||
});
|
||||
const expectedSuggestion = expect.objectContaining({
|
||||
url: 'https://website.com/doc/page',
|
||||
});
|
||||
|
||||
// When
|
||||
const ds = new DocSearch(options);
|
||||
ds.autocomplete.trigger('autocomplete:selected', {
|
||||
url: 'https://website.com/doc/page',
|
||||
});
|
||||
|
||||
return new Promise(resolve => {
|
||||
expect(customHandleSelected).toHaveBeenCalledWith(
|
||||
expectedInput,
|
||||
expectedEvent,
|
||||
expectedSuggestion,
|
||||
);
|
||||
resolve();
|
||||
});
|
||||
});
|
||||
xit('should prevent all clicks on links if a custom handleSelected is specified', () => {
|
||||
// TODO
|
||||
// If handleSelected, we target one link, we manually trigger a click on
|
||||
// it, and we check that preventDefault is called on the event
|
||||
});
|
||||
});
|
||||
|
||||
describe('handleShown', () => {
|
||||
|
|
|
|||
|
|
@ -10,7 +10,7 @@ const templates = {
|
|||
{{#isCategoryHeader}}${suggestionPrefix}__main{{/isCategoryHeader}}
|
||||
{{#isSubCategoryHeader}}${suggestionPrefix}__secondary{{/isSubCategoryHeader}}
|
||||
"
|
||||
target="_blank" aria-label="Link to the result"
|
||||
aria-label="Link to the result"
|
||||
href="{{{url}}}"
|
||||
>
|
||||
<div class="${suggestionPrefix}--category-header">
|
||||
|
|
|
|||
Loading…
Reference in a new issue