chore(stack update): update the whole docsearch stack (#173)
* chore(stack update): update the whole docsearch stack In an effort to reduce our stack diversity I am moving docsearch to: - latest eslint - prettier - webpack 2 - babel 6 (babel 7 is soon to be released, we were on babel 5) - babel-preset-env (support: IE >=9, Two latest version of evergreen browsers). - jest * chore(contrib): remove develop branch, cc @redox
This commit is contained in:
parent
25dbb4d7d3
commit
3c98ea5882
29 changed files with 3857 additions and 2274 deletions
13
.babelrc
13
.babelrc
|
|
@ -1,8 +1,15 @@
|
|||
{
|
||||
"presets": [
|
||||
["env", {
|
||||
"targets": {
|
||||
"browsers": ["last 2 versions", "ie >= 9"]
|
||||
}
|
||||
}],
|
||||
"stage-3"
|
||||
],
|
||||
"env": {
|
||||
"test": {
|
||||
"plugins": ["babel-plugin-rewire"]
|
||||
}
|
||||
},
|
||||
"stage": 2
|
||||
},
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,3 +0,0 @@
|
|||
{
|
||||
"extends": "algolia"
|
||||
}
|
||||
3
.eslintrc.js
Normal file
3
.eslintrc.js
Normal file
|
|
@ -0,0 +1,3 @@
|
|||
module.exports = {
|
||||
extends: ['algolia'],
|
||||
};
|
||||
|
|
@ -5,13 +5,10 @@ before_install:
|
|||
- git config --global user.name "algobot"
|
||||
- rvm install 2.2.0
|
||||
- rvm use 2.2.0
|
||||
before_cache: npm prune
|
||||
script: "./scripts/test-ci"
|
||||
after_script: "./scripts/travis-after-script"
|
||||
branches:
|
||||
only:
|
||||
- master
|
||||
- develop
|
||||
sudo: false
|
||||
cache:
|
||||
yarn: true
|
||||
|
|
|
|||
|
|
@ -1,7 +1,7 @@
|
|||
Hi (future) collaborator!
|
||||
|
||||
**tl;dr;**
|
||||
- submit pull requests to develop branch
|
||||
- submit pull requests to master branch
|
||||
- use [conventional changelog](https://github.com/ajoslin/conventional-changelog/blob/master/conventions/angular.md) commit style messages
|
||||
- squash your commits
|
||||
- have fun
|
||||
|
|
@ -26,9 +26,6 @@ Hi (future) collaborator!
|
|||
- [Releasing](#releasing)
|
||||
- [Releasing v1](#releasing-v1)
|
||||
- [Beta releases](#beta-releases)
|
||||
- [Hotfixes](#hotfixes)
|
||||
- [Releasing hotfixes](#releasing-hotfixes)
|
||||
- [Documentation updates](#documentation-updates)
|
||||
|
||||
<!-- END doctoc generated TOC please keep comment here to allow auto update -->
|
||||
|
||||
|
|
@ -36,10 +33,6 @@ Hi (future) collaborator!
|
|||
|
||||
Have a fix or a new feature? [Search for corresponding issues](https://github.com/algolia/docsearch/issues) first then create a new one.
|
||||
|
||||
Always check the status of the [develop branch](https://github.com/algolia/docsearch/tree/develop) for the freshest code.
|
||||
|
||||
Always submit pull requests to the develop branch.
|
||||
|
||||
# Development workflow
|
||||
|
||||
## Local example
|
||||
|
|
@ -141,7 +134,7 @@ Example:
|
|||
|
||||
# When are issues closed?
|
||||
|
||||
Once the a fix is done, having the fix in the `develop` branch is not sufficient, it needs to be part of a release for us to close the issue.
|
||||
Once the a fix is done, having the fix in the `master` branch is not sufficient, it needs to be part of a release for us to close the issue.
|
||||
|
||||
So that you never ask yourself "Is this released?".
|
||||
|
||||
|
|
@ -159,8 +152,6 @@ You must be on the master branch.
|
|||
npm run release
|
||||
```
|
||||
|
||||
This task will merge develop into master.
|
||||
|
||||
## Releasing v1
|
||||
|
||||
```sh
|
||||
|
|
@ -169,40 +160,21 @@ git pull
|
|||
npm run release
|
||||
```
|
||||
|
||||
We use the `v1` branch as a way to push fixes to the first version of DocSearch. Never merge `master` or `develop` into `v1` and
|
||||
We use the `v1` branch as a way to push fixes to the first version of DocSearch. Do not merge `master` into `v1` and
|
||||
vice versa.
|
||||
|
||||
## Beta releases
|
||||
|
||||
You must be on the develop branch.
|
||||
You must be on the master branch.
|
||||
|
||||
```sh
|
||||
npm run release:beta
|
||||
```
|
||||
|
||||
This task will release a beta version of what is currently in develop branch.
|
||||
This task will release a beta version of what is currently in master branch.
|
||||
|
||||
It will not update the `latest` tag of the npm release nor update jsDelivr /2/.
|
||||
|
||||
# Hotfixes
|
||||
|
||||
All our work is done on the develop branch but it could be necessary to push a hotfix to the master
|
||||
branch and do a patch release. To fix a very important bug.
|
||||
|
||||
For this, you should:
|
||||
- add `hotfix` to the commit message **body**
|
||||
- submit your pull request to the master branch
|
||||
|
||||
## Releasing hotfixes
|
||||
|
||||
You must be on the master branch.
|
||||
|
||||
This task will not merge develop in master, only release current master.
|
||||
|
||||
```sh
|
||||
HOTFIX=1 npm run release
|
||||
```
|
||||
|
||||
## Documentation updates
|
||||
|
||||
If you have important documentation update to release without wanting to release
|
||||
|
|
|
|||
|
|
@ -4,7 +4,7 @@ docsearch({
|
|||
apiKey: 'e3d767b736584dbe6d4c35f7cf7d4633',
|
||||
indexName: 'react-native',
|
||||
inputSelector: '#search-input',
|
||||
debug: true
|
||||
debug: true,
|
||||
});
|
||||
|
||||
document.getElementById('search-input').focus();
|
||||
|
|
|
|||
1
index.js
1
index.js
|
|
@ -1 +1,2 @@
|
|||
/* eslint-disable import/no-commonjs */
|
||||
module.exports = require('./src/lib/main.js');
|
||||
|
|
|
|||
77
package.json
77
package.json
|
|
@ -15,54 +15,65 @@
|
|||
"release": "./scripts/release",
|
||||
"release:beta": "./scripts/release-beta",
|
||||
"lint": "eslint .",
|
||||
"test": "BABEL_ENV=test mocha --timeout 6000 --reporter dot ./test/helpers.js ./test/*-test.js",
|
||||
"test:watch": "BABEL_ENV=test mocha --reporter min --watch ./test/helpers.js ./test/*-test.js",
|
||||
"test:coverage": "./scripts/test-coverage",
|
||||
"gh-pages": "./scripts/gh-pages"
|
||||
"test": "jest --coverage",
|
||||
"test:watch": "jest --watch",
|
||||
"gh-pages": "./scripts/gh-pages",
|
||||
"precommit": "lint-staged"
|
||||
},
|
||||
"lint-staged": {
|
||||
"*.js": [
|
||||
"prettier --write --single-quote --trailing-comma es5",
|
||||
"git add"
|
||||
]
|
||||
},
|
||||
"files": [
|
||||
"dist/"
|
||||
],
|
||||
"author": "Algolia <support@algolia.com> (https://github.com/algolia/)",
|
||||
"license": "MIT",
|
||||
"repository": "algolia/docsearch",
|
||||
"devDependencies": {
|
||||
"autoprefixer": "^6.3.6",
|
||||
"babel": "^5.8.29",
|
||||
"babel-core": "^5.8.29",
|
||||
"babel-eslint": "^4.1.3",
|
||||
"babel-istanbul": "^0.5.9",
|
||||
"babel-loader": "^5.3.2",
|
||||
"babel-plugin-rewire": "^0.1.22",
|
||||
"conventional-changelog": "^0.5.1",
|
||||
"coveralls": "^2.11.9",
|
||||
"autoprefixer": "^6.7.7",
|
||||
"babel-cli": "^6.24.0",
|
||||
"babel-core": "^6.24.0",
|
||||
"babel-eslint": "^7.1.1",
|
||||
"babel-istanbul": "^0.12.2",
|
||||
"babel-loader": "^6.4.1",
|
||||
"babel-plugin-rewire": "^1.0.0",
|
||||
"babel-preset-env": "^1.2.2",
|
||||
"babel-preset-stage-3": "^6.22.0",
|
||||
"conventional-changelog-cli": "^1.3.1",
|
||||
"cssnano": "^3.7.0",
|
||||
"doctoc": "^0.15.0",
|
||||
"eslint": "^1.6.0",
|
||||
"eslint-config-airbnb": "^0.1.0",
|
||||
"eslint-config-algolia": "^4.7.0",
|
||||
"doctoc": "^1.3.0",
|
||||
"eslint": "^3.17.1",
|
||||
"eslint-config-algolia": "^6.0.1",
|
||||
"eslint-config-prettier": "^1.5.0",
|
||||
"eslint-import-resolver-webpack": "^0.8.1",
|
||||
"eslint-plugin-algolia": "^1.5.0",
|
||||
"eslint-plugin-react": "^3.5.1",
|
||||
"eslint-plugin-import": "^2.2.0",
|
||||
"eslint-plugin-jest": "^19.0.1",
|
||||
"expect": "^1.20.1",
|
||||
"gh-pages": "^0.12.0",
|
||||
"jsdom": "^7.1.1",
|
||||
"json": "^9.0.4",
|
||||
"mocha": "^2.5.3",
|
||||
"mocha-jsdom": "^1.1.0",
|
||||
"mocha-lcov-reporter": "^1.2.0",
|
||||
"jest": "^19.0.2",
|
||||
"jsdom": "^9.12.0",
|
||||
"json": "^9.0.6",
|
||||
"mversion": "^1.10.1",
|
||||
"nd": "^1.2.0",
|
||||
"node-sass": "^3.7.0",
|
||||
"onchange": "^2.5.0",
|
||||
"postcss-cli": "^2.5.2",
|
||||
"pretty-bytes": "^2.0.1",
|
||||
"node-sass": "^4.5.0",
|
||||
"onchange": "^3.2.1",
|
||||
"postcss-cli": "^3.0.0",
|
||||
"prettier": "^0.22.0",
|
||||
"pretty-bytes-cli": "^2.0.0",
|
||||
"semver": "^5.1.0",
|
||||
"sinon": "^1.17.4",
|
||||
"uglify-js": "^2.6.2",
|
||||
"webpack": "^1.13.1",
|
||||
"webpack-dev-server": "^1.14.1"
|
||||
"sinon": "^2.0.0",
|
||||
"uglify-js": "^2.8.13",
|
||||
"webpack": "^2.2.1",
|
||||
"webpack-dev-server": "^2.4.2"
|
||||
},
|
||||
"peerDependencies": {},
|
||||
"dependencies": {
|
||||
"algoliasearch": "^3.18.0",
|
||||
"autocomplete.js": "^0.25.0",
|
||||
"algoliasearch": "^3.22.1",
|
||||
"autocomplete.js": "^0.27.0",
|
||||
"hogan.js": "^3.0.2",
|
||||
"to-factory": "^1.0.0"
|
||||
}
|
||||
|
|
|
|||
|
|
@ -7,18 +7,17 @@ LICENSE="/*! ${NAME} ${VERSION:-UNRELEASED} | © Algolia | github.com/algolia/do
|
|||
DIST_DIR="dist/cdn"
|
||||
DIST_FILE="$DIST_DIR/${NAME}.css"
|
||||
DIST_FILE_MIN="$DIST_DIR/${NAME}.min.css"
|
||||
DIST_FILE_MAP="$DIST_DIR/${NAME}.min.css.map"
|
||||
|
||||
mkdir -p "$DIST_DIR"
|
||||
|
||||
# ./dist/cdn/docsearch.css
|
||||
echo "$LICENSE" > "$DIST_FILE";
|
||||
node-sass --output-style expanded ./src/styles/main.scss \
|
||||
| postcss --use autoprefixer \
|
||||
>> "$DIST_FILE"
|
||||
| postcss --use autoprefixer -o "$DIST_FILE"
|
||||
|
||||
# ./dist/cdn/docsearch.min.css
|
||||
postcss --use cssnano "$DIST_FILE" \
|
||||
>> "$DIST_FILE_MIN"
|
||||
postcss "$DIST_FILE" --use cssnano --map "$DIST_FILE_MAP" -o "$DIST_FILE_MIN"
|
||||
|
||||
gzip_size=$(gzip -9 < $DIST_FILE_MIN | wc -c | pretty-bytes)
|
||||
echo "=> $DIST_FILE_MIN gzipped will weight $gzip_size-bytes"
|
||||
|
|
|
|||
|
|
@ -8,24 +8,30 @@ import semver from 'semver';
|
|||
import currentVersion from '../src/lib/version.js';
|
||||
|
||||
if (!process.env.VERSION) {
|
||||
throw new Error('bump: Usage is VERSION=MAJOR.MINOR.PATCH scripts/bump-package-version.js');
|
||||
throw new Error(
|
||||
'bump: Usage is VERSION=MAJOR.MINOR.PATCH scripts/bump-package-version.js'
|
||||
);
|
||||
}
|
||||
let newVersion = process.env.VERSION;
|
||||
const newVersion = process.env.VERSION;
|
||||
|
||||
if (!semver.valid(newVersion)) {
|
||||
throw new Error('bump: Provided new version (' + newVersion + ') is not a valid version per semver');
|
||||
throw new Error(
|
||||
`bump: Provided new version (${newVersion}) is not a valid version per semver`
|
||||
);
|
||||
}
|
||||
|
||||
if (semver.gte(currentVersion, newVersion)) {
|
||||
throw new Error('bump: Provided new version is not higher than current version (' + newVersion + ' <= ' + currentVersion + ')');
|
||||
throw new Error(
|
||||
`bump: Provided new version is not higher than current version (${newVersion} <= ${currentVersion})`
|
||||
);
|
||||
}
|
||||
|
||||
console.log('Bumping ' + newVersion);
|
||||
console.log(`Bumping ${newVersion}`);
|
||||
|
||||
console.log('..Updating src/lib/version.js');
|
||||
|
||||
let versionFile = path.join(__dirname, '../src/lib/version.js');
|
||||
let newContent = "export default '" + newVersion + "';\n";
|
||||
const versionFile = path.join(__dirname, '../src/lib/version.js');
|
||||
const newContent = `export default '${newVersion}';\n`;
|
||||
fs.writeFileSync(versionFile, newContent);
|
||||
|
||||
console.log('..Updating package.json, npm-shrinwrap.json');
|
||||
|
|
|
|||
11
scripts/dev
11
scripts/dev
|
|
@ -2,13 +2,6 @@
|
|||
|
||||
npm run build:css
|
||||
|
||||
# /bundle.js in memory
|
||||
webpack-dev-server \
|
||||
--config webpack.dev.config.babel.js \
|
||||
--hot \
|
||||
--inline \
|
||||
--no-info & \
|
||||
# rebuild docsearch.css and docsearch.min.css
|
||||
onchange './src/styles/*.scss' -- npm run build:css & \
|
||||
|
||||
webpack-dev-server --config webpack.dev.config.babel.js &
|
||||
onchange './src/styles/*.scss' -- npm run build:css &
|
||||
wait
|
||||
|
|
|
|||
|
|
@ -24,12 +24,6 @@ printf "\n\nRelease: update working tree"
|
|||
git pull origin master
|
||||
git fetch origin --tags
|
||||
|
||||
if [ $HOTFIX == '0' ]; then
|
||||
printf "\n\nRelease: merge develop branch"
|
||||
git fetch origin develop
|
||||
git merge origin/develop
|
||||
fi
|
||||
|
||||
printf "Release: yarn"
|
||||
yarn
|
||||
|
||||
|
|
@ -79,13 +73,6 @@ printf "\n\nRelease: push to github, publish on npm"
|
|||
git push origin master
|
||||
git push origin --tags
|
||||
npm publish
|
||||
# also update beta
|
||||
npm dist-tag add docsearch.js@$newVersion beta
|
||||
git checkout develop
|
||||
git pull origin develop
|
||||
git merge master
|
||||
git push origin develop
|
||||
git checkout master
|
||||
|
||||
printf "Release:
|
||||
Package was published to npm.
|
||||
|
|
|
|||
|
|
@ -1,88 +0,0 @@
|
|||
#!/usr/bin/env bash
|
||||
|
||||
set -e # exit when error
|
||||
|
||||
if [[ -n $(npm owner add `npm whoami`) ]]; then
|
||||
printf "Beta release: Not an owner of the npm repo, ask for it"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
currentBranch=`git rev-parse --abbrev-ref HEAD`
|
||||
if [ $currentBranch != 'develop' ]; then
|
||||
printf "Beta release: You must be on develop"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
if [[ -n $(git status --porcelain) ]]; then
|
||||
printf "Beta release: Working tree is not clean (git status)"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
printf "\n\nBeta release: update working tree"
|
||||
git pull origin develop
|
||||
git fetch origin --tags
|
||||
|
||||
printf "Beta release: yarn"
|
||||
yarn
|
||||
|
||||
currentVersion=`cat package.json | json version`
|
||||
|
||||
# if not on a beta version already
|
||||
if [[ $currentVersion != *"-beta"* || $CHOOSE_BUMP ]]
|
||||
then
|
||||
# header
|
||||
printf "\n\nBeta release: current version is $currentVersion"
|
||||
printf "\nBeta release: a changelog will be generated only if a fix/feat/performance/breaking token is found in git log"
|
||||
printf "\nBeta release: you must choose a new ve.rs.ion (semver)"
|
||||
printf "\nBeta release: press q to exit the next screen\n\n"
|
||||
|
||||
# preview changelog
|
||||
read -p "=> Beta release: press [ENTER] to view changes since latest version.."
|
||||
|
||||
# nd (markdown renderer in terminal)
|
||||
# has some output going on to stderr because of node v4, no big deal
|
||||
conventional-changelog -p angular | nd | less -r 2>/dev/null
|
||||
|
||||
# choose and bump new version
|
||||
# printf "\n\nBeta release: Please enter the new chosen version > "
|
||||
printf "\n=> Beta release: please type the new chosen version (-beta will be automatically appended) > "
|
||||
read -e newVersion
|
||||
newVersion="$newVersion-beta.0"
|
||||
else
|
||||
# when already on a beta version, let's bump the beta-.X
|
||||
newVersion=`semver ${currentVersion} -i prerelease -preid beta`
|
||||
|
||||
read -p "
|
||||
=> Beta release: The new version will be ${newVersion},
|
||||
press [ENTER] to continue, CTRL+C to abort.
|
||||
You can force specifying a different bump by using CHOOSE_BUMP=1 npm run release-beta"
|
||||
fi
|
||||
|
||||
VERSION=$newVersion babel-node ./scripts/bump-package-version.js
|
||||
|
||||
# build new version
|
||||
NODE_ENV=production VERSION=$newVersion npm run build
|
||||
|
||||
# regenerate readme TOC
|
||||
printf "\n\nBeta release: generate TOCS"
|
||||
npm run doctoc
|
||||
|
||||
# add a timestamp fake file to dist/ to avoid having beta released published to jsdelivr automatically
|
||||
# https://github.com/jsdelivr/jsdelivr/pull/8107#issuecomment-159562676
|
||||
# You will have to close the open PR afterwards
|
||||
timestamp=$(date "+%Y.%m.%d-%H.%M.%S")
|
||||
tmpfile="dist/avoid-jsdelivr-beta-publish-$timestamp"
|
||||
touch $tmpfile
|
||||
|
||||
# git add and tag
|
||||
commitMessage="v$newVersion\n\n$changelog"
|
||||
git add src/lib/version.js package.json CHANGELOG.md CONTRIBUTING.md README.md
|
||||
printf "$commitMessage" | git commit --file -
|
||||
|
||||
printf "\n\nBeta release: almost done, check everything in another terminal tab.\n"
|
||||
read -p "=> Beta release: when ready, press [ENTER] to push to github and publish the package to the beta channel"
|
||||
|
||||
printf "\n\nBeta release: push to github, publish on npm"
|
||||
git push origin develop
|
||||
npm publish --tag beta
|
||||
rm $tmpfile
|
||||
|
|
@ -2,8 +2,7 @@
|
|||
|
||||
set -ev # exit when error
|
||||
|
||||
./scripts/validate-pr-done-on-develop
|
||||
npm run test:coverage
|
||||
npm run test
|
||||
npm prune
|
||||
NODE_ENV=production npm run build
|
||||
./scripts/validate-commit-msgs
|
||||
|
|
|
|||
|
|
@ -1,5 +0,0 @@
|
|||
#!/usr/bin/env bash
|
||||
# Will push coverage info to coveralls. Should be ran from Travis, in the
|
||||
# `after_script` section
|
||||
|
||||
$(npm bin)/coveralls < ./coverage/lcov.info
|
||||
|
|
@ -25,7 +25,7 @@ fi
|
|||
|
||||
if [ "$RANGE" == "" ]; then
|
||||
# locally
|
||||
RANGE="origin/develop..HEAD"
|
||||
RANGE="origin/master..HEAD"
|
||||
elif [[ "$RANGE" == *...* ]]; then
|
||||
# Travis sends the ranges with 3 dots. Git only wants 2.
|
||||
RANGE=`echo $TRAVIS_COMMIT_RANGE | sed 's/\.\.\./../'`
|
||||
|
|
|
|||
|
|
@ -1,19 +0,0 @@
|
|||
#!/usr/bin/env bash
|
||||
|
||||
set -e # exit when error
|
||||
|
||||
COMMIT_MSG=`git log --format=%B --no-merges -n 1`
|
||||
|
||||
if [ "$TRAVIS_PULL_REQUEST" == "false" ]; then
|
||||
MSG="No need to check pull request done on develop branch when not in a pull request"
|
||||
EXIT=0
|
||||
elif [[ "$COMMIT_MSG" =~ "hotfix" ]] && [ "$TRAVIS_BRANCH" == 'master' ]; then
|
||||
MSG="Hotfix submitted to master, good"
|
||||
EXIT=0
|
||||
elif [ "$TRAVIS_BRANCH" != 'develop' ]; then
|
||||
MSG="Pull request must be done on develop branch"
|
||||
EXIT=1
|
||||
fi
|
||||
|
||||
echo $MSG;
|
||||
exit $EXIT;
|
||||
|
|
@ -29,65 +29,81 @@ const usage = `Usage:
|
|||
[ autocompleteOptions.{hint,debug} ]
|
||||
})`;
|
||||
class DocSearch {
|
||||
constructor({
|
||||
apiKey,
|
||||
indexName,
|
||||
inputSelector,
|
||||
appId = 'BH4D9OD16A',
|
||||
debug = false,
|
||||
algoliaOptions = {},
|
||||
autocompleteOptions = {
|
||||
debug: false,
|
||||
hint: false,
|
||||
autoselect: true
|
||||
},
|
||||
transformData = false,
|
||||
handleSelected = false,
|
||||
enhancedSearchInput = false,
|
||||
layout = 'collumns'
|
||||
}) {
|
||||
DocSearch.checkArguments({apiKey, indexName, inputSelector, debug, algoliaOptions,
|
||||
autocompleteOptions, transformData, handleSelected, enhancedSearchInput, layout});
|
||||
constructor(
|
||||
{
|
||||
apiKey,
|
||||
indexName,
|
||||
inputSelector,
|
||||
appId = 'BH4D9OD16A',
|
||||
debug = false,
|
||||
algoliaOptions = {},
|
||||
autocompleteOptions = {
|
||||
debug: false,
|
||||
hint: false,
|
||||
autoselect: true,
|
||||
},
|
||||
transformData = false,
|
||||
handleSelected = false,
|
||||
enhancedSearchInput = false,
|
||||
layout = 'collumns',
|
||||
}
|
||||
) {
|
||||
DocSearch.checkArguments({
|
||||
apiKey,
|
||||
indexName,
|
||||
inputSelector,
|
||||
debug,
|
||||
algoliaOptions,
|
||||
autocompleteOptions,
|
||||
transformData,
|
||||
handleSelected,
|
||||
enhancedSearchInput,
|
||||
layout,
|
||||
});
|
||||
|
||||
this.apiKey = apiKey;
|
||||
this.appId = appId;
|
||||
this.indexName = indexName;
|
||||
this.input = DocSearch.getInputFromSelector(inputSelector);
|
||||
this.algoliaOptions = {hitsPerPage: 5, ...algoliaOptions};
|
||||
const autocompleteOptionsDebug = autocompleteOptions && autocompleteOptions.debug ?
|
||||
autocompleteOptions.debug : false;
|
||||
this.algoliaOptions = { hitsPerPage: 5, ...algoliaOptions };
|
||||
const autocompleteOptionsDebug = autocompleteOptions &&
|
||||
autocompleteOptions.debug
|
||||
? autocompleteOptions.debug
|
||||
: false;
|
||||
autocompleteOptions.debug = debug || autocompleteOptionsDebug;
|
||||
this.autocompleteOptions = autocompleteOptions;
|
||||
this.autocompleteOptions.cssClasses = {
|
||||
prefix: 'ds'
|
||||
prefix: 'ds',
|
||||
};
|
||||
|
||||
handleSelected = handleSelected || this.handleSelected;
|
||||
|
||||
this.isSimpleLayout = (layout === 'simple');
|
||||
this.isSimpleLayout = layout === 'simple';
|
||||
|
||||
this.client = algoliasearch(this.appId, this.apiKey);
|
||||
this.client.addAlgoliaAgent('docsearch.js ' + version);
|
||||
this.client.addAlgoliaAgent(`docsearch.js ${version}`);
|
||||
|
||||
if (enhancedSearchInput) {
|
||||
this.input = DocSearch.injectSearchBox(this.input);
|
||||
}
|
||||
|
||||
this.autocomplete = autocomplete(this.input, autocompleteOptions, [{
|
||||
source: this.getAutocompleteSource(transformData),
|
||||
templates: {
|
||||
suggestion: DocSearch.getSuggestionTemplate(this.isSimpleLayout),
|
||||
footer: templates.footer,
|
||||
empty: DocSearch.getEmptyTemplate()
|
||||
}
|
||||
}]);
|
||||
this.autocomplete = autocomplete(this.input, autocompleteOptions, [
|
||||
{
|
||||
source: this.getAutocompleteSource(transformData),
|
||||
templates: {
|
||||
suggestion: DocSearch.getSuggestionTemplate(this.isSimpleLayout),
|
||||
footer: templates.footer,
|
||||
empty: DocSearch.getEmptyTemplate(),
|
||||
},
|
||||
},
|
||||
]);
|
||||
this.autocomplete.on(
|
||||
'autocomplete:selected',
|
||||
handleSelected.bind(null, this.autocomplete.autocomplete)
|
||||
);
|
||||
this.autocomplete.on(
|
||||
'autocomplete:shown',
|
||||
this.handleShown.bind(null, this.input)
|
||||
this.handleShown.bind(null, this.input)
|
||||
);
|
||||
|
||||
if (enhancedSearchInput) {
|
||||
|
|
@ -107,16 +123,17 @@ class DocSearch {
|
|||
}
|
||||
|
||||
if (!DocSearch.getInputFromSelector(args.inputSelector)) {
|
||||
throw new Error(`Error: No input element in the page matches ${args.inputSelector}`);
|
||||
throw new Error(
|
||||
`Error: No input element in the page matches ${args.inputSelector}`
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
static injectSearchBox(input) {
|
||||
input.before(templates.searchBox);
|
||||
var new_input = input.prev().prev().find('input');
|
||||
const newInput = input.prev().prev().find('input');
|
||||
input.remove();
|
||||
|
||||
return new_input;
|
||||
return newInput;
|
||||
}
|
||||
|
||||
static bindSearchBoxEvent() {
|
||||
|
|
@ -126,9 +143,9 @@ class DocSearch {
|
|||
autocomplete.autocomplete.setVal('');
|
||||
});
|
||||
|
||||
$('input#docsearch').on('keyup', function() {
|
||||
var searchbox = document.querySelector('input#docsearch');
|
||||
var reset = document.querySelector('.searchbox [type="reset"]');
|
||||
$('input#docsearch').on('keyup', () => {
|
||||
const searchbox = document.querySelector('input#docsearch');
|
||||
const reset = document.querySelector('.searchbox [type="reset"]');
|
||||
reset.className = 'searchbox__reset';
|
||||
if (searchbox.value.length === 0) {
|
||||
reset.className += ' hide';
|
||||
|
|
@ -144,7 +161,7 @@ class DocSearch {
|
|||
* @returns {void}
|
||||
*/
|
||||
static getInputFromSelector(selector) {
|
||||
let input = $(selector).filter('input');
|
||||
const input = $(selector).filter('input');
|
||||
return input.length ? $(input[0]) : null;
|
||||
}
|
||||
|
||||
|
|
@ -158,86 +175,104 @@ class DocSearch {
|
|||
*/
|
||||
getAutocompleteSource(transformData) {
|
||||
return (query, callback) => {
|
||||
this.client.search([{
|
||||
indexName: this.indexName,
|
||||
query: query,
|
||||
params: this.algoliaOptions
|
||||
}]).then((data) => {
|
||||
let hits = data.results[0].hits;
|
||||
if (transformData) {
|
||||
hits = transformData(hits) || hits;
|
||||
}
|
||||
callback(DocSearch.formatHits(hits));
|
||||
});
|
||||
this.client
|
||||
.search([
|
||||
{
|
||||
indexName: this.indexName,
|
||||
query,
|
||||
params: this.algoliaOptions,
|
||||
},
|
||||
])
|
||||
.then(data => {
|
||||
let hits = data.results[0].hits;
|
||||
if (transformData) {
|
||||
hits = transformData(hits) || hits;
|
||||
}
|
||||
callback(DocSearch.formatHits(hits));
|
||||
});
|
||||
};
|
||||
}
|
||||
|
||||
// Given a list of hits returned by the API, will reformat them to be used in
|
||||
// a Hogan template
|
||||
static formatHits(receivedHits) {
|
||||
let clonedHits = utils.deepClone(receivedHits);
|
||||
let hits = clonedHits.map((hit) => {
|
||||
const clonedHits = utils.deepClone(receivedHits);
|
||||
const hits = clonedHits.map(hit => {
|
||||
if (hit._highlightResult) {
|
||||
hit._highlightResult = utils.mergeKeyWithParent(hit._highlightResult, 'hierarchy');
|
||||
hit._highlightResult = utils.mergeKeyWithParent(
|
||||
hit._highlightResult,
|
||||
'hierarchy'
|
||||
);
|
||||
}
|
||||
return utils.mergeKeyWithParent(hit, 'hierarchy');
|
||||
});
|
||||
|
||||
// Group hits by category / subcategory
|
||||
var groupedHits = utils.groupBy(hits, 'lvl0');
|
||||
let groupedHits = utils.groupBy(hits, 'lvl0');
|
||||
$.each(groupedHits, (level, collection) => {
|
||||
const groupedHitsByLvl1 = utils.groupBy(collection, 'lvl1');
|
||||
const flattenedHits = utils.flattenAndFlagFirst(groupedHitsByLvl1, 'isSubCategoryHeader');
|
||||
const flattenedHits = utils.flattenAndFlagFirst(
|
||||
groupedHitsByLvl1,
|
||||
'isSubCategoryHeader'
|
||||
);
|
||||
groupedHits[level] = flattenedHits;
|
||||
});
|
||||
groupedHits = utils.flattenAndFlagFirst(groupedHits, 'isCategoryHeader');
|
||||
|
||||
// Translate hits into smaller objects to be send to the template
|
||||
return groupedHits.map((hit) => {
|
||||
return groupedHits.map(hit => {
|
||||
const url = DocSearch.formatURL(hit);
|
||||
const category = utils.getHighlightedValue(hit, 'lvl0');
|
||||
const subcategory = utils.getHighlightedValue(hit, 'lvl1') || category;
|
||||
const displayTitle = utils.compact([
|
||||
utils.getHighlightedValue(hit, 'lvl2') || subcategory,
|
||||
utils.getHighlightedValue(hit, 'lvl3'),
|
||||
utils.getHighlightedValue(hit, 'lvl4'),
|
||||
utils.getHighlightedValue(hit, 'lvl5'),
|
||||
utils.getHighlightedValue(hit, 'lvl6')
|
||||
]).join('<span class="aa-suggestion-title-separator" aria-hidden="true"> › </span>');
|
||||
const displayTitle = utils
|
||||
.compact([
|
||||
utils.getHighlightedValue(hit, 'lvl2') || subcategory,
|
||||
utils.getHighlightedValue(hit, 'lvl3'),
|
||||
utils.getHighlightedValue(hit, 'lvl4'),
|
||||
utils.getHighlightedValue(hit, 'lvl5'),
|
||||
utils.getHighlightedValue(hit, 'lvl6'),
|
||||
])
|
||||
.join(
|
||||
'<span class="aa-suggestion-title-separator" aria-hidden="true"> › </span>'
|
||||
);
|
||||
const text = utils.getSnippetedValue(hit, 'content');
|
||||
const isTextOrSubcatoryNonEmpty = (subcategory && subcategory !== '') ||
|
||||
(displayTitle && displayTitle !== '');
|
||||
const isLvl1EmptyOrDuplicate = !subcategory || subcategory === '' || subcategory === category;
|
||||
const isLvl2 = displayTitle && displayTitle !== '' && displayTitle !== subcategory;
|
||||
const isLvl1 = !isLvl2 && (subcategory && subcategory !== '' && subcategory !== category);
|
||||
const isLvl1EmptyOrDuplicate = !subcategory ||
|
||||
subcategory === '' ||
|
||||
subcategory === category;
|
||||
const isLvl2 = displayTitle &&
|
||||
displayTitle !== '' &&
|
||||
displayTitle !== subcategory;
|
||||
const isLvl1 = !isLvl2 &&
|
||||
(subcategory && subcategory !== '' && subcategory !== category);
|
||||
const isLvl0 = !isLvl1 && !isLvl2;
|
||||
|
||||
return {
|
||||
isLvl0: isLvl0,
|
||||
isLvl1: isLvl1,
|
||||
isLvl2: isLvl2,
|
||||
isLvl1EmptyOrDuplicate: isLvl1EmptyOrDuplicate,
|
||||
isLvl0,
|
||||
isLvl1,
|
||||
isLvl2,
|
||||
isLvl1EmptyOrDuplicate,
|
||||
isCategoryHeader: hit.isCategoryHeader,
|
||||
isSubCategoryHeader: hit.isSubCategoryHeader,
|
||||
isTextOrSubcatoryNonEmpty: isTextOrSubcatoryNonEmpty,
|
||||
category: category,
|
||||
subcategory: subcategory,
|
||||
isTextOrSubcatoryNonEmpty,
|
||||
category,
|
||||
subcategory,
|
||||
title: displayTitle,
|
||||
text: text,
|
||||
url: url
|
||||
text,
|
||||
url,
|
||||
};
|
||||
});
|
||||
}
|
||||
|
||||
static formatURL(hit) {
|
||||
const {url, anchor} = hit;
|
||||
const { url, anchor } = hit;
|
||||
if (url) {
|
||||
const containsAnchor = url.indexOf('#') !== -1;
|
||||
if (containsAnchor) return url;
|
||||
else if (anchor) return `${hit.url}#${hit.anchor}`;
|
||||
return url;
|
||||
}
|
||||
else if (anchor) return `#${hit.anchor}`;
|
||||
} else if (anchor) return `#${hit.anchor}`;
|
||||
/* eslint-disable */
|
||||
console.warn('no anchor nor url for : ', JSON.stringify(hit));
|
||||
/* eslint-enable */
|
||||
|
|
@ -245,17 +280,15 @@ class DocSearch {
|
|||
}
|
||||
|
||||
static getEmptyTemplate() {
|
||||
return (args) => {
|
||||
return Hogan.compile(templates.empty).render(args);
|
||||
};
|
||||
return args => Hogan.compile(templates.empty).render(args);
|
||||
}
|
||||
|
||||
static getSuggestionTemplate(isSimpleLayout) {
|
||||
const stringTemplate = isSimpleLayout ? templates.suggestionSimple : templates.suggestion;
|
||||
const stringTemplate = isSimpleLayout
|
||||
? templates.suggestionSimple
|
||||
: templates.suggestion;
|
||||
const template = Hogan.compile(stringTemplate);
|
||||
return (suggestion) => {
|
||||
return template.render(suggestion);
|
||||
};
|
||||
return suggestion => template.render(suggestion);
|
||||
}
|
||||
|
||||
handleSelected(input, event, suggestion) {
|
||||
|
|
@ -271,10 +304,12 @@ class DocSearch {
|
|||
middleOfWindow = 900;
|
||||
}
|
||||
|
||||
const alignClass = middleOfInput - middleOfWindow >= 0 ?
|
||||
'algolia-autocomplete-right' : 'algolia-autocomplete-left';
|
||||
const otherAlignClass = middleOfInput - middleOfWindow < 0 ?
|
||||
'algolia-autocomplete-right' : 'algolia-autocomplete-left';
|
||||
const alignClass = middleOfInput - middleOfWindow >= 0
|
||||
? 'algolia-autocomplete-right'
|
||||
: 'algolia-autocomplete-left';
|
||||
const otherAlignClass = middleOfInput - middleOfWindow < 0
|
||||
? 'algolia-autocomplete-right'
|
||||
: 'algolia-autocomplete-left';
|
||||
|
||||
const autocompleteWrapper = $('.algolia-autocomplete');
|
||||
if (!autocompleteWrapper.hasClass(alignClass)) {
|
||||
|
|
@ -288,5 +323,3 @@ class DocSearch {
|
|||
}
|
||||
|
||||
export default DocSearch;
|
||||
|
||||
|
||||
|
|
|
|||
File diff suppressed because it is too large
Load diff
|
|
@ -1,31 +1,19 @@
|
|||
/* eslint-env mocha */
|
||||
|
||||
import jsdom from 'mocha-jsdom';
|
||||
import expect from 'expect';
|
||||
import utils from '../utils.js';
|
||||
|
||||
describe('utils', () => {
|
||||
var utils;
|
||||
jsdom();
|
||||
|
||||
before(() => {
|
||||
// We need to load utils from here as it depends on Zepto, which itself
|
||||
// needs jsdom to be called before being loaded.
|
||||
utils = require('../src/lib/utils.js');
|
||||
});
|
||||
|
||||
describe('mergeKeyWithParent', () => {
|
||||
it('should move all subkeys to parent', () => {
|
||||
// Given
|
||||
let input = {
|
||||
const input = {
|
||||
name: 'foo',
|
||||
hierarchy: {
|
||||
lvl0: 'bar',
|
||||
lvl1: 'baz'
|
||||
}
|
||||
lvl1: 'baz',
|
||||
},
|
||||
};
|
||||
|
||||
// When
|
||||
let actual = utils.mergeKeyWithParent(input, 'hierarchy');
|
||||
const actual = utils.mergeKeyWithParent(input, 'hierarchy');
|
||||
|
||||
// Then
|
||||
expect(actual.lvl0).toEqual('bar');
|
||||
|
|
@ -33,59 +21,60 @@ describe('utils', () => {
|
|||
});
|
||||
it('should delete the attribute', () => {
|
||||
// Given
|
||||
let input = {
|
||||
const input = {
|
||||
name: 'foo',
|
||||
hierarchy: {
|
||||
lvl0: 'bar',
|
||||
lvl1: 'baz'
|
||||
}
|
||||
lvl1: 'baz',
|
||||
},
|
||||
};
|
||||
|
||||
// When
|
||||
let actual = utils.mergeKeyWithParent(input, 'hierarchy');
|
||||
const actual = utils.mergeKeyWithParent(input, 'hierarchy');
|
||||
|
||||
// Then
|
||||
expect(actual.hierarchy).toEqual(undefined);
|
||||
});
|
||||
|
||||
it('should overwrite key if present', () => {
|
||||
// Given
|
||||
let input = {
|
||||
const input = {
|
||||
name: 'foo',
|
||||
lvl0: 42,
|
||||
hierarchy: {
|
||||
lvl0: 'bar',
|
||||
lvl1: 'baz'
|
||||
}
|
||||
lvl1: 'baz',
|
||||
},
|
||||
};
|
||||
|
||||
// When
|
||||
let actual = utils.mergeKeyWithParent(input, 'hierarchy');
|
||||
const actual = utils.mergeKeyWithParent(input, 'hierarchy');
|
||||
|
||||
// Then
|
||||
expect(actual.lvl0).toNotEqual(42);
|
||||
expect(actual.lvl0).not.toEqual(42);
|
||||
expect(actual.lvl0).toEqual('bar');
|
||||
});
|
||||
it('should do nothing if no such key', () => {
|
||||
// Given
|
||||
let input = {
|
||||
name: 'foo'
|
||||
const input = {
|
||||
name: 'foo',
|
||||
};
|
||||
|
||||
// When
|
||||
let actual = utils.mergeKeyWithParent(input, 'hierarchy');
|
||||
const actual = utils.mergeKeyWithParent(input, 'hierarchy');
|
||||
|
||||
// Then
|
||||
expect(actual).toBe(input);
|
||||
});
|
||||
it('should throw an error if key is no an object', () => {
|
||||
// Given
|
||||
let input = {
|
||||
const input = {
|
||||
name: 'foo',
|
||||
hierarchy: 42
|
||||
hierarchy: 42,
|
||||
};
|
||||
|
||||
// When
|
||||
let actual = utils.mergeKeyWithParent(input, 'hierarchy');
|
||||
const actual = utils.mergeKeyWithParent(input, 'hierarchy');
|
||||
|
||||
// Then
|
||||
expect(actual).toBe(input);
|
||||
|
|
@ -95,59 +84,59 @@ describe('utils', () => {
|
|||
describe('groupBy', () => {
|
||||
it('group by specified key', () => {
|
||||
// Given
|
||||
let input = [
|
||||
{name: 'Tim', category: 'devs'},
|
||||
{name: 'Vincent', category: 'devs'},
|
||||
{name: 'Ben', category: 'sales'},
|
||||
{name: 'Jeremy', category: 'sales'},
|
||||
{name: 'AlexS', category: 'devs'},
|
||||
{name: 'AlexK', category: 'sales'}
|
||||
const input = [
|
||||
{ name: 'Tim', category: 'devs' },
|
||||
{ name: 'Vincent', category: 'devs' },
|
||||
{ name: 'Ben', category: 'sales' },
|
||||
{ name: 'Jeremy', category: 'sales' },
|
||||
{ name: 'AlexS', category: 'devs' },
|
||||
{ name: 'AlexK', category: 'sales' },
|
||||
];
|
||||
|
||||
// When
|
||||
let actual = utils.groupBy(input, 'category');
|
||||
const actual = utils.groupBy(input, 'category');
|
||||
|
||||
// Expect
|
||||
expect(actual).toEqual({
|
||||
devs: [
|
||||
{name: 'Tim', category: 'devs'},
|
||||
{name: 'Vincent', category: 'devs'},
|
||||
{name: 'AlexS', category: 'devs'}
|
||||
{ name: 'Tim', category: 'devs' },
|
||||
{ name: 'Vincent', category: 'devs' },
|
||||
{ name: 'AlexS', category: 'devs' },
|
||||
],
|
||||
sales: [
|
||||
{name: 'Ben', category: 'sales'},
|
||||
{name: 'Jeremy', category: 'sales'},
|
||||
{name: 'AlexK', category: 'sales'}
|
||||
]
|
||||
{ name: 'Ben', category: 'sales' },
|
||||
{ name: 'Jeremy', category: 'sales' },
|
||||
{ name: 'AlexK', category: 'sales' },
|
||||
],
|
||||
});
|
||||
});
|
||||
it('group by key considering lowercase forms', () => {
|
||||
// Given
|
||||
let input = [
|
||||
{name: 'Tim', category: 'devs'},
|
||||
{name: 'Vincent', category: 'DeVs'}
|
||||
const input = [
|
||||
{ name: 'Tim', category: 'devs' },
|
||||
{ name: 'Vincent', category: 'DeVs' },
|
||||
];
|
||||
|
||||
// When
|
||||
let actual = utils.groupBy(input, 'category');
|
||||
const actual = utils.groupBy(input, 'category');
|
||||
|
||||
// Expect
|
||||
expect(actual).toEqual({
|
||||
devs: [
|
||||
{name: 'Tim', category: 'devs'},
|
||||
{name: 'Vincent', category: 'DeVs'}
|
||||
]
|
||||
{ name: 'Tim', category: 'devs' },
|
||||
{ name: 'Vincent', category: 'DeVs' },
|
||||
],
|
||||
});
|
||||
});
|
||||
it('throw an error if key does not exist', () => {
|
||||
// Given
|
||||
let input = [
|
||||
{name: 'Tim'},
|
||||
{name: 'Vincent'},
|
||||
{name: 'Ben'},
|
||||
{name: 'Jeremy'},
|
||||
{name: 'AlexS'},
|
||||
{name: 'AlexK'}
|
||||
const input = [
|
||||
{ name: 'Tim' },
|
||||
{ name: 'Vincent' },
|
||||
{ name: 'Ben' },
|
||||
{ name: 'Jeremy' },
|
||||
{ name: 'AlexS' },
|
||||
{ name: 'AlexK' },
|
||||
];
|
||||
|
||||
// When
|
||||
|
|
@ -160,20 +149,17 @@ describe('utils', () => {
|
|||
describe('values', () => {
|
||||
it('should extract all values', () => {
|
||||
// Given
|
||||
let input = {
|
||||
const input = {
|
||||
foo: 42,
|
||||
bar: true,
|
||||
baz: 'yep'
|
||||
baz: 'yep',
|
||||
};
|
||||
|
||||
// Given
|
||||
let actual = utils.values(input);
|
||||
const actual = utils.values(input);
|
||||
|
||||
// Then
|
||||
expect(actual.length).toEqual(3);
|
||||
expect(actual).toInclude(42);
|
||||
expect(actual).toInclude(true);
|
||||
expect(actual).toInclude('yep');
|
||||
expect(actual).toEqual([42, true, 'yep']);
|
||||
});
|
||||
});
|
||||
|
||||
|
|
@ -181,10 +167,10 @@ describe('utils', () => {
|
|||
// flatten values
|
||||
it('should flatten array on level deep', () => {
|
||||
// Given
|
||||
let input = [1, 2, [3, 4], [5, 6]];
|
||||
const input = [1, 2, [3, 4], [5, 6]];
|
||||
|
||||
// Given
|
||||
let actual = utils.flatten(input);
|
||||
const actual = utils.flatten(input);
|
||||
|
||||
// Then
|
||||
expect(actual).toEqual([1, 2, 3, 4, 5, 6]);
|
||||
|
|
@ -194,30 +180,30 @@ describe('utils', () => {
|
|||
describe('flattenAndFlagFirst', () => {
|
||||
it('should flatten all values', () => {
|
||||
// Given
|
||||
let input = {
|
||||
const input = {
|
||||
devs: [
|
||||
{name: 'Tim', category: 'dev'},
|
||||
{name: 'Vincent', category: 'dev'},
|
||||
{name: 'AlexS', category: 'dev'}
|
||||
{ name: 'Tim', category: 'dev' },
|
||||
{ name: 'Vincent', category: 'dev' },
|
||||
{ name: 'AlexS', category: 'dev' },
|
||||
],
|
||||
sales: [
|
||||
{name: 'Ben', category: 'sales'},
|
||||
{name: 'Jeremy', category: 'sales'},
|
||||
{name: 'AlexK', category: 'sales'}
|
||||
]
|
||||
{ name: 'Ben', category: 'sales' },
|
||||
{ name: 'Jeremy', category: 'sales' },
|
||||
{ name: 'AlexK', category: 'sales' },
|
||||
],
|
||||
};
|
||||
|
||||
// When
|
||||
let actual = utils.flattenAndFlagFirst(input, 'isTop');
|
||||
const actual = utils.flattenAndFlagFirst(input, 'isTop');
|
||||
|
||||
// Then
|
||||
expect(actual).toEqual([
|
||||
{name: 'Tim', category: 'dev', isTop: true},
|
||||
{name: 'Vincent', category: 'dev', isTop: false},
|
||||
{name: 'AlexS', category: 'dev', isTop: false},
|
||||
{name: 'Ben', category: 'sales', isTop: true},
|
||||
{name: 'Jeremy', category: 'sales', isTop: false},
|
||||
{name: 'AlexK', category: 'sales', isTop: false}
|
||||
{ name: 'Tim', category: 'dev', isTop: true },
|
||||
{ name: 'Vincent', category: 'dev', isTop: false },
|
||||
{ name: 'AlexS', category: 'dev', isTop: false },
|
||||
{ name: 'Ben', category: 'sales', isTop: true },
|
||||
{ name: 'Jeremy', category: 'sales', isTop: false },
|
||||
{ name: 'AlexK', category: 'sales', isTop: false },
|
||||
]);
|
||||
});
|
||||
});
|
||||
|
|
@ -225,10 +211,10 @@ describe('utils', () => {
|
|||
describe('compact', () => {
|
||||
it('should clear all falsy elements from the array', () => {
|
||||
// Given
|
||||
let input = [42, false, null, undefined, '', [], 'foo'];
|
||||
const input = [42, false, null, undefined, '', [], 'foo'];
|
||||
|
||||
// When
|
||||
let actual = utils.compact(input);
|
||||
const actual = utils.compact(input);
|
||||
|
||||
// Then
|
||||
expect(actual).toEqual([42, [], 'foo']);
|
||||
|
|
@ -238,44 +224,44 @@ describe('utils', () => {
|
|||
describe('getHighlightedValue', () => {
|
||||
it('should return the highlighted version if exists', () => {
|
||||
// Given
|
||||
let input = {
|
||||
const input = {
|
||||
_highlightResult: {
|
||||
text: {
|
||||
value: '<mark>foo</mark>'
|
||||
}
|
||||
value: '<mark>foo</mark>',
|
||||
},
|
||||
},
|
||||
text: 'foo'
|
||||
text: 'foo',
|
||||
};
|
||||
|
||||
// When
|
||||
let actual = utils.getHighlightedValue(input, 'text');
|
||||
const actual = utils.getHighlightedValue(input, 'text');
|
||||
|
||||
// Then
|
||||
expect(actual).toEqual('<mark>foo</mark>');
|
||||
});
|
||||
it('should return the default key if no highlighted value', () => {
|
||||
// Given
|
||||
let input = {
|
||||
const input = {
|
||||
_highlightResult: {
|
||||
text: { }
|
||||
text: {},
|
||||
},
|
||||
text: 'foo'
|
||||
text: 'foo',
|
||||
};
|
||||
|
||||
// When
|
||||
let actual = utils.getHighlightedValue(input, 'text');
|
||||
const actual = utils.getHighlightedValue(input, 'text');
|
||||
|
||||
// Then
|
||||
expect(actual).toEqual('foo');
|
||||
});
|
||||
it('should return the default key if no highlight results', () => {
|
||||
// Given
|
||||
let input = {
|
||||
text: 'foo'
|
||||
const input = {
|
||||
text: 'foo',
|
||||
};
|
||||
|
||||
// When
|
||||
let actual = utils.getHighlightedValue(input, 'text');
|
||||
const actual = utils.getHighlightedValue(input, 'text');
|
||||
|
||||
// Then
|
||||
expect(actual).toEqual('foo');
|
||||
|
|
@ -285,64 +271,64 @@ describe('utils', () => {
|
|||
describe('getSnippetedValue', () => {
|
||||
it('should return the key value if no snippet returned', () => {
|
||||
// Given
|
||||
let input = {
|
||||
text: 'Foo'
|
||||
const input = {
|
||||
text: 'Foo',
|
||||
};
|
||||
|
||||
// When
|
||||
let actual = utils.getSnippetedValue(input, 'text');
|
||||
const actual = utils.getSnippetedValue(input, 'text');
|
||||
|
||||
// Then
|
||||
expect(actual).toEqual('Foo');
|
||||
});
|
||||
it('should return the key value if no snippet for this key', () => {
|
||||
// Given
|
||||
let input = {
|
||||
const input = {
|
||||
_snippetResult: {
|
||||
content: {
|
||||
value: '<mark>Bar</mark>'
|
||||
}
|
||||
value: '<mark>Bar</mark>',
|
||||
},
|
||||
},
|
||||
text: 'Foo',
|
||||
content: 'Bar'
|
||||
content: 'Bar',
|
||||
};
|
||||
|
||||
// When
|
||||
let actual = utils.getSnippetedValue(input, 'text');
|
||||
const actual = utils.getSnippetedValue(input, 'text');
|
||||
|
||||
// Then
|
||||
expect(actual).toEqual('Foo');
|
||||
});
|
||||
it('should add ellipsis at the start if snippet does not start with a capital letter', () => {
|
||||
// Given
|
||||
let input = {
|
||||
const input = {
|
||||
_snippetResult: {
|
||||
text: {
|
||||
value: 'this is the <mark>end</mark> of a sentence.'
|
||||
}
|
||||
value: 'this is the <mark>end</mark> of a sentence.',
|
||||
},
|
||||
},
|
||||
text: 'this is the end of a sentence.'
|
||||
text: 'this is the end of a sentence.',
|
||||
};
|
||||
|
||||
// When
|
||||
let actual = utils.getSnippetedValue(input, 'text');
|
||||
const actual = utils.getSnippetedValue(input, 'text');
|
||||
|
||||
// Then
|
||||
expect(actual).toEqual('…this is the <mark>end</mark> of a sentence.');
|
||||
});
|
||||
it('should add ellipsis at the end if snippet does not end with a terminal point', () => {
|
||||
// Given
|
||||
let input = {
|
||||
const input = {
|
||||
_snippetResult: {
|
||||
text: {
|
||||
value: 'This is an <mark>finished</mark> sentence'
|
||||
}
|
||||
value: 'This is an <mark>finished</mark> sentence',
|
||||
},
|
||||
},
|
||||
text: 'This is an <mark>finished</mark> sentence'
|
||||
text: 'This is an <mark>finished</mark> sentence',
|
||||
};
|
||||
|
||||
// When
|
||||
let actual = utils.getSnippetedValue(input, 'text');
|
||||
const actual = utils.getSnippetedValue(input, 'text');
|
||||
|
||||
// Then
|
||||
expect(actual).toEqual('This is an <mark>finished</mark> sentence…');
|
||||
|
|
@ -352,33 +338,33 @@ describe('utils', () => {
|
|||
describe('deepClone', () => {
|
||||
it('should create an object with the exact same value', () => {
|
||||
// Given
|
||||
let input = {
|
||||
const input = {
|
||||
foo: {
|
||||
bar: 'baz'
|
||||
}
|
||||
bar: 'baz',
|
||||
},
|
||||
};
|
||||
|
||||
// When
|
||||
let actual = utils.deepClone(input);
|
||||
const actual = utils.deepClone(input);
|
||||
|
||||
// Then
|
||||
expect(actual.foo.bar).toEqual('baz');
|
||||
});
|
||||
it('should not change the initial object', () => {
|
||||
// Given
|
||||
let input = {
|
||||
const input = {
|
||||
foo: {
|
||||
bar: 'baz'
|
||||
}
|
||||
bar: 'baz',
|
||||
},
|
||||
};
|
||||
|
||||
// When
|
||||
let actual = utils.deepClone(input);
|
||||
const actual = utils.deepClone(input);
|
||||
input.foo.bar = 42;
|
||||
|
||||
// Then
|
||||
expect(input.foo.bar).toEqual(42);
|
||||
expect(actual.foo.bar).toNotEqual(42);
|
||||
expect(actual.foo.bar).not.toEqual(42);
|
||||
expect(actual.foo.bar).toEqual('baz');
|
||||
});
|
||||
});
|
||||
|
|
@ -85,29 +85,28 @@ const templates = {
|
|||
`,
|
||||
searchBox: `
|
||||
<form novalidate="novalidate" onsubmit="return false;" class="searchbox">
|
||||
<div role="search" class="searchbox__wrapper">
|
||||
<input id="docsearch" type="search" name="search" placeholder="Search the docs" autocomplete="off" required="required" class="searchbox__input">
|
||||
<button type="submit" title="Submit your search query." class="searchbox__submit" >
|
||||
<svg width=12 height=12 role="img" aria-label="Search">
|
||||
<use xmlns:xlink="http://www.w3.org/1999/xlink" xlink:href="#sbx-icon-search-13"></use>
|
||||
</svg>
|
||||
</button>
|
||||
<button type="reset" title="Clear the search query." class="searchbox__reset hide">
|
||||
<svg width=12 height=12 role="img" aria-label="Reset">
|
||||
<use xmlns:xlink="http://www.w3.org/1999/xlink" xlink:href="#sbx-icon-clear-3"></use>
|
||||
</svg>
|
||||
</button>
|
||||
</div>
|
||||
<div role="search" class="searchbox__wrapper">
|
||||
<input id="docsearch" type="search" name="search" placeholder="Search the docs" autocomplete="off" required="required" class="searchbox__input"/>
|
||||
<button type="submit" title="Submit your search query." class="searchbox__submit" >
|
||||
<svg width=12 height=12 role="img" aria-label="Search">
|
||||
<use xmlns:xlink="http://www.w3.org/1999/xlink" xlink:href="#sbx-icon-search-13"></use>
|
||||
</svg>
|
||||
</button>
|
||||
<button type="reset" title="Clear the search query." class="searchbox__reset hide">
|
||||
<svg width=12 height=12 role="img" aria-label="Reset">
|
||||
<use xmlns:xlink="http://www.w3.org/1999/xlink" xlink:href="#sbx-icon-clear-3"></use>
|
||||
</svg>
|
||||
</button>
|
||||
</div>
|
||||
</form>
|
||||
|
||||
<div class="svg-icons" style="height: 0; width: 0; position: absolute; visibility: hidden">
|
||||
<svg xmlns="http://www.w3.org/2000/svg">
|
||||
<symbol id="sbx-icon-clear-3" viewBox="0 0 40 40"><path d="M16.228 20L1.886 5.657 0 3.772 3.772 0l1.885 1.886L20 16.228 34.343 1.886 36.228 0 40 3.772l-1.886 1.885L23.772 20l14.342 14.343L40 36.228 36.228 40l-1.885-1.886L20 23.772 5.657 38.114 3.772 40 0 36.228l1.886-1.885L16.228 20z" fill-rule="evenodd"/></symbol>
|
||||
<symbol id="sbx-icon-search-13" viewBox="0 0 40 40"><path d="M26.806 29.012a16.312 16.312 0 0 1-10.427 3.746C7.332 32.758 0 25.425 0 16.378 0 7.334 7.333 0 16.38 0c9.045 0 16.378 7.333 16.378 16.38 0 3.96-1.406 7.593-3.746 10.426L39.547 37.34c.607.608.61 1.59-.004 2.203a1.56 1.56 0 0 1-2.202.004L26.807 29.012zm-10.427.627c7.322 0 13.26-5.938 13.26-13.26 0-7.324-5.938-13.26-13.26-13.26-7.324 0-13.26 5.936-13.26 13.26 0 7.322 5.936 13.26 13.26 13.26z" fill-rule="evenodd"/></symbol>
|
||||
</svg>
|
||||
<svg xmlns="http://www.w3.org/2000/svg">
|
||||
<symbol id="sbx-icon-clear-3" viewBox="0 0 40 40"><path d="M16.228 20L1.886 5.657 0 3.772 3.772 0l1.885 1.886L20 16.228 34.343 1.886 36.228 0 40 3.772l-1.886 1.885L23.772 20l14.342 14.343L40 36.228 36.228 40l-1.885-1.886L20 23.772 5.657 38.114 3.772 40 0 36.228l1.886-1.885L16.228 20z" fill-rule="evenodd"></symbol>
|
||||
<symbol id="sbx-icon-search-13" viewBox="0 0 40 40"><path d="M26.806 29.012a16.312 16.312 0 0 1-10.427 3.746C7.332 32.758 0 25.425 0 16.378 0 7.334 7.333 0 16.38 0c9.045 0 16.378 7.333 16.378 16.38 0 3.96-1.406 7.593-3.746 10.426L39.547 37.34c.607.608.61 1.59-.004 2.203a1.56 1.56 0 0 1-2.202.004L26.807 29.012zm-10.427.627c7.322 0 13.26-5.938 13.26-13.26 0-7.324-5.938-13.26-13.26-13.26-7.324 0-13.26 5.936-13.26 13.26 0 7.322 5.936 13.26 13.26 13.26z" fill-rule="evenodd"></symbol>
|
||||
</svg>
|
||||
</div>
|
||||
`
|
||||
`,
|
||||
};
|
||||
|
||||
export default templates;
|
||||
|
||||
|
|
|
|||
|
|
@ -1,6 +1,6 @@
|
|||
import $ from './zepto.js';
|
||||
|
||||
let utils = {
|
||||
const utils = {
|
||||
/*
|
||||
* Move the content of an object key one level higher.
|
||||
* eg.
|
||||
|
|
@ -29,7 +29,7 @@ let utils = {
|
|||
if (typeof object[property] !== 'object') {
|
||||
return object;
|
||||
}
|
||||
let newObject = $.extend({}, object, object[property]);
|
||||
const newObject = $.extend({}, object, object[property]);
|
||||
delete newObject[property];
|
||||
return newObject;
|
||||
},
|
||||
|
|
@ -65,7 +65,7 @@ let utils = {
|
|||
* @throws Error when one of the element does not have the specified property
|
||||
*/
|
||||
groupBy(collection, property) {
|
||||
let newCollection = {};
|
||||
const newCollection = {};
|
||||
$.each(collection, (index, item) => {
|
||||
if (item[property] === undefined) {
|
||||
throw new Error(`[groupBy]: Object has no key ${property}`);
|
||||
|
|
@ -83,7 +83,7 @@ let utils = {
|
|||
});
|
||||
return newCollection;
|
||||
},
|
||||
/*
|
||||
/*
|
||||
* Return an array of all the values of the specified object
|
||||
* eg.
|
||||
* values({
|
||||
|
|
@ -99,7 +99,7 @@ let utils = {
|
|||
values(object) {
|
||||
return Object.keys(object).map(key => object[key]);
|
||||
},
|
||||
/*
|
||||
/*
|
||||
* Flattens an array
|
||||
* eg.
|
||||
* flatten([1, 2, [3, 4], [5, 6]])
|
||||
|
|
@ -109,19 +109,19 @@ let utils = {
|
|||
* @return {array}
|
||||
*/
|
||||
flatten(array) {
|
||||
let results = [];
|
||||
array.forEach((value) => {
|
||||
const results = [];
|
||||
array.forEach(value => {
|
||||
if (!Array.isArray(value)) {
|
||||
results.push(value);
|
||||
return;
|
||||
}
|
||||
value.forEach((subvalue) => {
|
||||
value.forEach(subvalue => {
|
||||
results.push(subvalue);
|
||||
});
|
||||
});
|
||||
return results;
|
||||
},
|
||||
/*
|
||||
/*
|
||||
* Flatten all values of an object into an array, marking each first element of
|
||||
* each group with a specific flag
|
||||
* eg.
|
||||
|
|
@ -151,15 +151,14 @@ let utils = {
|
|||
* @return {array}
|
||||
*/
|
||||
flattenAndFlagFirst(object, flag) {
|
||||
let values = this.values(object).map(collection => {
|
||||
return collection.map((item, index) => {
|
||||
item[flag] = (index === 0);
|
||||
const values = this.values(object).map(collection =>
|
||||
collection.map((item, index) => {
|
||||
item[flag] = index === 0;
|
||||
return item;
|
||||
});
|
||||
});
|
||||
}));
|
||||
return this.flatten(values);
|
||||
},
|
||||
/*
|
||||
/*
|
||||
* Removes all empty strings, null, false and undefined elements array
|
||||
* eg.
|
||||
* compact([42, false, null, undefined, '', [], 'foo']);
|
||||
|
|
@ -169,7 +168,7 @@ let utils = {
|
|||
* @return {array}
|
||||
*/
|
||||
compact(array) {
|
||||
let results = [];
|
||||
const results = [];
|
||||
array.forEach(value => {
|
||||
if (!value) {
|
||||
return;
|
||||
|
|
@ -197,18 +196,22 @@ let utils = {
|
|||
* @return {string}
|
||||
**/
|
||||
getHighlightedValue(object, property) {
|
||||
if (object._highlightResult
|
||||
&& object._highlightResult.hierarchy_camel
|
||||
&& object._highlightResult.hierarchy_camel[property]
|
||||
&& object._highlightResult.hierarchy_camel[property].matchLevel
|
||||
&& object._highlightResult.hierarchy_camel[property].matchLevel !== 'none'
|
||||
&& object._highlightResult.hierarchy_camel[property].value) {
|
||||
if (
|
||||
object._highlightResult &&
|
||||
object._highlightResult.hierarchy_camel &&
|
||||
object._highlightResult.hierarchy_camel[property] &&
|
||||
object._highlightResult.hierarchy_camel[property].matchLevel &&
|
||||
object._highlightResult.hierarchy_camel[property].matchLevel !== 'none' &&
|
||||
object._highlightResult.hierarchy_camel[property].value
|
||||
) {
|
||||
return object._highlightResult.hierarchy_camel[property].value;
|
||||
}
|
||||
if (object._highlightResult
|
||||
&& object._highlightResult
|
||||
&& object._highlightResult[property]
|
||||
&& object._highlightResult[property].value) {
|
||||
if (
|
||||
object._highlightResult &&
|
||||
object._highlightResult &&
|
||||
object._highlightResult[property] &&
|
||||
object._highlightResult[property].value
|
||||
) {
|
||||
return object._highlightResult[property].value;
|
||||
}
|
||||
return object[property];
|
||||
|
|
@ -234,9 +237,11 @@ let utils = {
|
|||
* @return {string}
|
||||
**/
|
||||
getSnippetedValue(object, property) {
|
||||
if (!object._snippetResult
|
||||
|| !object._snippetResult[property]
|
||||
|| !object._snippetResult[property].value) {
|
||||
if (
|
||||
!object._snippetResult ||
|
||||
!object._snippetResult[property] ||
|
||||
!object._snippetResult[property].value
|
||||
) {
|
||||
return object[property];
|
||||
}
|
||||
let snippet = object._snippetResult[property].value;
|
||||
|
|
@ -249,7 +254,7 @@ let utils = {
|
|||
}
|
||||
return snippet;
|
||||
},
|
||||
/*
|
||||
/*
|
||||
* Deep clone an object.
|
||||
* Note: This will not clone functions and dates
|
||||
* @param {object} object Object to clone
|
||||
|
|
@ -257,7 +262,7 @@ let utils = {
|
|||
*/
|
||||
deepClone(object) {
|
||||
return JSON.parse(JSON.stringify(object));
|
||||
}
|
||||
},
|
||||
};
|
||||
|
||||
export default utils;
|
||||
|
|
|
|||
|
|
@ -1,41 +0,0 @@
|
|||
/* eslint-env mocha */
|
||||
global.ddescribe = describe.only;
|
||||
global.xdescribe = describe.skip;
|
||||
global.iit = it.only;
|
||||
global.xit = it.skip;
|
||||
|
||||
/**
|
||||
* Runs `runFunction` only when `escapeFunction` returns true. Timeout after
|
||||
* `escapeTime`ms.
|
||||
* @function waitForAndRun
|
||||
* @param {function} escapeFunction Function to wait for. Once it returns
|
||||
* `true`, we execute `runFunction`
|
||||
* @param {function} runFunction Function to run once `escapeFunction` returns
|
||||
* `true.
|
||||
* @param {number} escapeTime Time (in ms) after which we timeout.
|
||||
* @returns {void}
|
||||
*/
|
||||
function waitForAndRun(escapeFunction, runFunction, escapeTime) {
|
||||
// check the escapeFunction every millisecond so as soon as it is met we can escape the function
|
||||
var interval = setInterval(function() {
|
||||
if (escapeFunction()) {
|
||||
clearMe();
|
||||
runFunction();
|
||||
}
|
||||
}, 1);
|
||||
// in case we never reach the escapeFunction, we will time out
|
||||
// at the escapeTime
|
||||
var timeOut = setTimeout(function() {
|
||||
clearMe();
|
||||
runFunction();
|
||||
}, escapeTime);
|
||||
// clear the interval and the timeout
|
||||
function clearMe() {
|
||||
clearInterval(interval);
|
||||
clearTimeout(timeOut);
|
||||
}
|
||||
}
|
||||
|
||||
module.exports = {
|
||||
waitForAndRun
|
||||
};
|
||||
|
|
@ -1 +0,0 @@
|
|||
--compilers js:babel/register
|
||||
|
|
@ -1,32 +1,29 @@
|
|||
import webpack from 'webpack';
|
||||
import {join} from 'path';
|
||||
import { join } from 'path';
|
||||
|
||||
export default {
|
||||
entry: './index.js',
|
||||
devtool: 'source-map',
|
||||
output: {
|
||||
path: './dist/cdn',
|
||||
path: join(__dirname, 'dist/cdn'),
|
||||
filename: 'docsearch.js',
|
||||
library: 'docsearch',
|
||||
libraryTarget: 'umd'
|
||||
libraryTarget: 'umd',
|
||||
},
|
||||
module: {
|
||||
loaders: [{
|
||||
test: /\.js$/, exclude: /node_modules/, loader: 'babel'
|
||||
}]
|
||||
},
|
||||
resolve: {
|
||||
fallback: [join(__dirname, '..', 'node_modules')]
|
||||
},
|
||||
// same issue, for loaders like babel
|
||||
resolveLoader: {
|
||||
fallback: [join(__dirname, '..', 'node_modules')]
|
||||
rules: [
|
||||
{
|
||||
test: /\.js$/,
|
||||
exclude: /node_modules/,
|
||||
loader: 'babel-loader',
|
||||
},
|
||||
],
|
||||
},
|
||||
plugins: [
|
||||
new webpack.DefinePlugin({
|
||||
'process.env': {
|
||||
NODE_ENV: JSON.stringify(process.env.NODE_ENV)
|
||||
}
|
||||
})
|
||||
]
|
||||
NODE_ENV: JSON.stringify(process.env.NODE_ENV),
|
||||
},
|
||||
}),
|
||||
],
|
||||
};
|
||||
|
|
|
|||
|
|
@ -1,35 +1,24 @@
|
|||
import path from 'path';
|
||||
import { join } from 'path';
|
||||
|
||||
export default {
|
||||
entry: './dev/app.js',
|
||||
devtool: 'source-map',
|
||||
output: {
|
||||
path: './dev/',
|
||||
filename: 'bundle.js'
|
||||
path: join(__dirname, 'dev/'),
|
||||
filename: 'bundle.js',
|
||||
},
|
||||
module: {
|
||||
loaders: [{
|
||||
test: /\.js$/,
|
||||
include: [
|
||||
path.resolve(__dirname, 'src'),
|
||||
path.resolve(__dirname, 'dev'),
|
||||
path.resolve(__dirname, 'scripts')
|
||||
], loader: 'babel'
|
||||
}]
|
||||
rules: [
|
||||
{
|
||||
test: /\.js$/,
|
||||
exclude: /node_modules/,
|
||||
loader: 'babel-loader',
|
||||
},
|
||||
],
|
||||
},
|
||||
devServer: {
|
||||
contentBase: 'dev/',
|
||||
host: '0.0.0.0',
|
||||
compress: true
|
||||
compress: true,
|
||||
},
|
||||
// when module not found, find locally first
|
||||
// helps fixing the npm link not working with webpack
|
||||
// http://stackoverflow.com/a/33722844/147079
|
||||
resolve: {
|
||||
fallback: [path.join(__dirname, '..', 'node_modules')]
|
||||
},
|
||||
// same issue, for loaders like babel
|
||||
resolveLoader: {
|
||||
fallback: [path.join(__dirname, '..', 'node_modules')]
|
||||
}
|
||||
};
|
||||
|
|
|
|||
|
|
@ -1,40 +1,14 @@
|
|||
import webpack from 'webpack';
|
||||
import {join} from 'path';
|
||||
import baseConfig from './webpack.config.jsdelivr.babel';
|
||||
import { join } from 'path';
|
||||
|
||||
export default {
|
||||
entry: './index.js',
|
||||
devtool: 'source-map',
|
||||
output: {
|
||||
path: './dist/cdn',
|
||||
filename: 'docsearch.js',
|
||||
library: 'docsearch',
|
||||
libraryTarget: 'umd'
|
||||
},
|
||||
module: {
|
||||
loaders: [{
|
||||
test: /\.js$/, exclude: /node_modules/, loader: 'babel'
|
||||
}]
|
||||
},
|
||||
...baseConfig,
|
||||
devServer: {
|
||||
contentBase: 'dist/cdn',
|
||||
contentBase: join(__dirname, 'dist/cdn'),
|
||||
host: '0.0.0.0',
|
||||
compress: true
|
||||
compress: true,
|
||||
hot: true,
|
||||
inline: true,
|
||||
noInfo: true,
|
||||
},
|
||||
// when module not found, find locally first
|
||||
// helps fixing the npm link not working with webpack
|
||||
// http://stackoverflow.com/a/33722844/147079
|
||||
resolve: {
|
||||
fallback: [join(__dirname, '..', 'node_modules')]
|
||||
},
|
||||
// same issue, for loaders like babel
|
||||
resolveLoader: {
|
||||
fallback: [join(__dirname, '..', 'node_modules')]
|
||||
},
|
||||
plugins: [
|
||||
new webpack.DefinePlugin({
|
||||
'process.env': {
|
||||
NODE_ENV: JSON.stringify(process.env.NODE_ENV)
|
||||
}
|
||||
})
|
||||
]
|
||||
};
|
||||
|
|
|
|||
Loading…
Reference in a new issue