chore(deps): dependencies 2024-11-04 (#2335)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
This commit is contained in:
parent
6579023a2f
commit
aa666deccc
84 changed files with 25314 additions and 20402 deletions
|
|
@ -1,12 +1,6 @@
|
|||
version: 2.1
|
||||
|
||||
aliases:
|
||||
- &install_yarn_version
|
||||
name: Install specific Yarn version
|
||||
command: |
|
||||
curl -o- -L https://yarnpkg.com/install.sh | bash -s -- --version 1.22.5
|
||||
echo 'export PATH="$HOME/.yarn/bin:$HOME/.config/yarn/global/node_modules/.bin:$PATH"' >> $BASH_ENV
|
||||
|
||||
- &restore_yarn_cache
|
||||
name: Restore Yarn cache
|
||||
keys:
|
||||
|
|
@ -16,11 +10,12 @@ aliases:
|
|||
name: Save Yarn cache
|
||||
key: yarn-packages-{{ checksum "yarn.lock" }}
|
||||
paths:
|
||||
- ~/.cache/yarn
|
||||
- .yarn/cache
|
||||
- .yarn/unplugged
|
||||
|
||||
- &run_yarn_install
|
||||
name: Install dependencies
|
||||
command: yarn install --frozen-lockfile
|
||||
command: yarn install --immutable
|
||||
|
||||
- &restore_dist_folders
|
||||
name: Restore dist folders
|
||||
|
|
@ -59,7 +54,6 @@ jobs:
|
|||
<<: *defaults
|
||||
steps:
|
||||
- checkout
|
||||
- run: *install_yarn_version
|
||||
- restore_cache: *restore_yarn_cache
|
||||
- run: *run_yarn_install
|
||||
- save_cache: *save_yarn_cache
|
||||
|
|
@ -88,7 +82,6 @@ jobs:
|
|||
steps:
|
||||
- checkout
|
||||
- *attach_workspace
|
||||
- run: *install_yarn_version
|
||||
- restore_cache: *restore_yarn_cache
|
||||
- run: *run_yarn_install
|
||||
- save_cache: *save_yarn_cache
|
||||
|
|
@ -101,7 +94,6 @@ jobs:
|
|||
steps:
|
||||
- checkout
|
||||
- *attach_workspace
|
||||
- run: *install_yarn_version
|
||||
- restore_cache: *restore_yarn_cache
|
||||
- run: *run_yarn_install
|
||||
- save_cache: *save_yarn_cache
|
||||
|
|
@ -114,7 +106,6 @@ jobs:
|
|||
steps:
|
||||
- checkout
|
||||
- *attach_workspace
|
||||
- run: *install_yarn_version
|
||||
- restore_cache: *restore_yarn_cache
|
||||
- run: *run_yarn_install
|
||||
- save_cache: *save_yarn_cache
|
||||
|
|
@ -127,20 +118,18 @@ jobs:
|
|||
steps:
|
||||
- checkout
|
||||
- *attach_workspace
|
||||
- run: *install_yarn_version
|
||||
- restore_cache: *restore_yarn_cache
|
||||
- run: *run_yarn_install
|
||||
- save_cache: *save_yarn_cache
|
||||
- run: *restore_dist_folders
|
||||
- run:
|
||||
name: Unit tests
|
||||
command: yarn run test --maxWorkers=4
|
||||
command: yarn run test
|
||||
release:
|
||||
<<: *defaults
|
||||
steps:
|
||||
- checkout
|
||||
- *attach_workspace
|
||||
- run: *install_yarn_version
|
||||
- restore_cache: *restore_yarn_cache
|
||||
- run: *run_yarn_install
|
||||
- save_cache: *save_yarn_cache
|
||||
|
|
|
|||
|
|
@ -1,5 +0,0 @@
|
|||
node_modules
|
||||
dist
|
||||
/examples
|
||||
build
|
||||
.cache
|
||||
94
.eslintrc.js
94
.eslintrc.js
|
|
@ -1,94 +0,0 @@
|
|||
/* eslint-disable import/no-commonjs */
|
||||
module.exports = {
|
||||
extends: ['algolia', 'algolia/jest', 'algolia/react', 'algolia/typescript'],
|
||||
globals: {
|
||||
__DEV__: false,
|
||||
},
|
||||
settings: {
|
||||
react: {
|
||||
pragma: 'React',
|
||||
version: 'detect',
|
||||
},
|
||||
'import/resolver': {
|
||||
node: {
|
||||
extensions: ['.js', '.ts', '.tsx'],
|
||||
},
|
||||
},
|
||||
},
|
||||
rules: {
|
||||
'no-param-reassign': 0,
|
||||
'valid-jsdoc': 0,
|
||||
'no-shadow': 0,
|
||||
'prefer-template': 0,
|
||||
'jest/no-disabled-tests': 0,
|
||||
'react/prop-types': 0,
|
||||
'react/no-unescaped-entities': 0,
|
||||
'eslint-comments/disable-enable-pair': ['error', { allowWholeFile: true }],
|
||||
'import/extensions': 0,
|
||||
'no-unused-expressions': 0,
|
||||
complexity: 0,
|
||||
'import/order': [
|
||||
'error',
|
||||
{
|
||||
alphabetize: {
|
||||
order: 'asc',
|
||||
caseInsensitive: true,
|
||||
},
|
||||
'newlines-between': 'always',
|
||||
groups: ['builtin', 'external', 'parent', 'sibling', 'index'],
|
||||
pathGroups: [
|
||||
{
|
||||
pattern: '@/**/*',
|
||||
group: 'parent',
|
||||
position: 'before',
|
||||
},
|
||||
],
|
||||
pathGroupsExcludedImportTypes: ['builtin'],
|
||||
},
|
||||
],
|
||||
|
||||
// TMP
|
||||
'@typescript-eslint/explicit-function-return-type': ['off'],
|
||||
'react/function-component-definition': ['off'],
|
||||
'react/jsx-filename-extension': ['off'],
|
||||
'jsdoc/check-examples': ['off'],
|
||||
},
|
||||
overrides: [
|
||||
{
|
||||
files: ['**/rollup.config.js', 'stories/**/*', '**/__tests__/**'],
|
||||
rules: {
|
||||
'import/no-extraneous-dependencies': 0,
|
||||
},
|
||||
},
|
||||
{
|
||||
files: ['cypress/**/*'],
|
||||
plugins: ['cypress'],
|
||||
env: {
|
||||
'cypress/globals': true,
|
||||
},
|
||||
rules: {
|
||||
'jest/expect-expect': 0,
|
||||
'spaced-comment': 0,
|
||||
'@typescript-eslint/triple-slash-reference': 0,
|
||||
},
|
||||
},
|
||||
{
|
||||
files: [
|
||||
'scripts/**/*',
|
||||
'*.config.js',
|
||||
'packages/website/plugins/**/*',
|
||||
'packages/website/sidebars.js',
|
||||
],
|
||||
rules: {
|
||||
'import/no-commonjs': 0,
|
||||
},
|
||||
},
|
||||
{
|
||||
files: ['packages/website/**/*'],
|
||||
rules: {
|
||||
'import/no-unresolved': 0,
|
||||
'import/no-extraneous-dependencies': 0,
|
||||
},
|
||||
},
|
||||
],
|
||||
};
|
||||
5
.gitignore
vendored
5
.gitignore
vendored
|
|
@ -22,3 +22,8 @@ dist/
|
|||
# Cypress Video and Screenshots output
|
||||
cypress/screenshots/
|
||||
cypress/videos/
|
||||
|
||||
**/.yarn/*
|
||||
!**/.yarn/releases
|
||||
!**/.yarn/plugins
|
||||
!**/.yarn/patches
|
||||
|
|
|
|||
|
|
@ -7,7 +7,6 @@
|
|||
"stylelint-config-standard",
|
||||
"stylelint-config-sass-guidelines",
|
||||
"stylelint-order",
|
||||
"stylelint-a11y/recommended",
|
||||
"stylelint-prettier/recommended"
|
||||
],
|
||||
"rules": {
|
||||
|
|
@ -33,4 +32,4 @@
|
|||
}
|
||||
]
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
934
.yarn/releases/yarn-4.5.1.cjs
vendored
Executable file
934
.yarn/releases/yarn-4.5.1.cjs
vendored
Executable file
File diff suppressed because one or more lines are too long
5
.yarnrc.yml
Normal file
5
.yarnrc.yml
Normal file
|
|
@ -0,0 +1,5 @@
|
|||
nodeLinker: node-modules
|
||||
|
||||
enableGlobalCache: false
|
||||
|
||||
yarnPath: .yarn/releases/yarn-4.5.1.cjs
|
||||
|
|
@ -1,40 +0,0 @@
|
|||
module.exports = (api) => {
|
||||
const isTest = api.env('test');
|
||||
const modules = isTest ? 'commonjs' : false;
|
||||
const targets = {};
|
||||
|
||||
if (isTest) {
|
||||
targets.node = true;
|
||||
} else {
|
||||
targets.browsers = ['last 2 versions', 'ie >= 9'];
|
||||
}
|
||||
|
||||
return {
|
||||
presets: [
|
||||
'@babel/preset-typescript',
|
||||
[
|
||||
'@babel/preset-env',
|
||||
{
|
||||
modules,
|
||||
targets,
|
||||
},
|
||||
],
|
||||
],
|
||||
plugins: clean([
|
||||
'@babel/plugin-transform-react-jsx',
|
||||
!isTest && [
|
||||
'inline-replace-variables',
|
||||
{
|
||||
__DEV__: {
|
||||
type: 'node',
|
||||
replacement: "process.env.NODE_ENV === 'development'",
|
||||
},
|
||||
},
|
||||
],
|
||||
]),
|
||||
};
|
||||
};
|
||||
|
||||
function clean(config) {
|
||||
return config.filter(Boolean);
|
||||
}
|
||||
|
|
@ -10,7 +10,7 @@
|
|||
},
|
||||
{
|
||||
"path": "packages/docsearch-js/dist/umd/index.js",
|
||||
"maxSize": "35 kB"
|
||||
"maxSize": "35.05 kB"
|
||||
}
|
||||
]
|
||||
}
|
||||
|
|
|
|||
|
|
@ -32,7 +32,7 @@ describe('Start', () => {
|
|||
});
|
||||
|
||||
it('Open modal with forward slash key shortcut', () => {
|
||||
cy.get('body').type('/');
|
||||
cy.get('body').wait(1000).type('/');
|
||||
cy.modalIsVisibleAndFocused();
|
||||
});
|
||||
});
|
||||
|
|
@ -97,7 +97,7 @@ describe('Search', () => {
|
|||
const currentURL = cy.url();
|
||||
|
||||
cy.typeQueryMatching();
|
||||
cy.get('.DocSearch-Hits #docsearch-item-1 > a').click({ force: true });
|
||||
cy.get('.DocSearch-Hits #docsearch-hits0-item-1 > a').click({ force: true });
|
||||
cy.on('url:changed', (newUrl) => {
|
||||
expect(newUrl).not.equal(currentURL);
|
||||
});
|
||||
|
|
@ -114,38 +114,30 @@ describe('Recent and Favorites', () => {
|
|||
cy.visit(Cypress.config().baseUrl!);
|
||||
cy.openModal();
|
||||
cy.typeQueryMatching();
|
||||
cy.get('#docsearch-item-0 > a').click({ force: true }).wait(1000);
|
||||
cy.get('#docsearch-hits0-item-0 > a').click({ force: true }).wait(1000);
|
||||
cy.openModal();
|
||||
cy.contains('Recent').should('be.visible');
|
||||
});
|
||||
|
||||
it('Recent search is displayed after visiting a result', () => {
|
||||
cy.get('#docsearch-item-0').should('be.visible');
|
||||
cy.get('#docsearch-recentSearches-item-0').should('be.visible');
|
||||
});
|
||||
|
||||
it('Recent search can be deleted', () => {
|
||||
cy.get('#docsearch-item-0')
|
||||
.find('[title="Remove this search from history"]')
|
||||
.trigger('click');
|
||||
cy.get('#docsearch-recentSearches-item-0').find('[title="Remove this search from history"]').trigger('click');
|
||||
cy.contains('No recent searches').should('be.visible');
|
||||
});
|
||||
|
||||
it('Recent search can be favorited', () => {
|
||||
cy.get('#docsearch-item-0')
|
||||
.find('[title="Save this search"]')
|
||||
.trigger('click');
|
||||
cy.get('#docsearch-recentSearches-item-0').find('[title="Save this search"]').trigger('click');
|
||||
cy.contains('Favorite').should('be.visible');
|
||||
cy.get('#docsearch-item-0').should('be.visible');
|
||||
cy.get('#docsearch-favoriteSearches-item-0').should('be.visible');
|
||||
});
|
||||
|
||||
it('Favorite can be deleted', () => {
|
||||
cy.get('#docsearch-item-0')
|
||||
.find('[title="Save this search"]')
|
||||
.trigger('click');
|
||||
cy.get('#docsearch-recentSearches-item-0').find('[title="Save this search"]').trigger('click');
|
||||
cy.contains('Favorite').should('be.visible');
|
||||
cy.get('#docsearch-item-0')
|
||||
.find('[title="Remove this search from favorites"]')
|
||||
.trigger('click');
|
||||
cy.get('#docsearch-favoriteSearches-item-0').find('[title="Remove this search from favorites"]').trigger('click');
|
||||
cy.contains('No recent searches').should('be.visible');
|
||||
});
|
||||
});
|
||||
|
|
|
|||
97
eslint.config.mjs
Normal file
97
eslint.config.mjs
Normal file
|
|
@ -0,0 +1,97 @@
|
|||
import algolia from 'eslint-config-algolia/flat/base.js';
|
||||
import algoliaReact from 'eslint-config-algolia/flat/react.js';
|
||||
import algoliaTypescript from 'eslint-config-algolia/flat/typescript.js';
|
||||
import cypresss from 'eslint-plugin-cypress/flat'; // eslint-disable-line import/no-unresolved
|
||||
import reactRefresh from 'eslint-plugin-react-refresh';
|
||||
import tseslint from 'typescript-eslint'; // eslint-disable-line import/no-unresolved
|
||||
|
||||
export default [
|
||||
...algolia,
|
||||
...algoliaReact,
|
||||
...algoliaTypescript,
|
||||
{
|
||||
ignores: ['**/node_modules/', '**/dist/', '**/build/', '.yarn/', '**/.docusaurus'],
|
||||
},
|
||||
{
|
||||
plugins: {
|
||||
'react-refresh': reactRefresh,
|
||||
},
|
||||
languageOptions: {
|
||||
parserOptions: {
|
||||
projectService: true,
|
||||
},
|
||||
},
|
||||
settings: {
|
||||
react: {
|
||||
pragma: 'React',
|
||||
version: 'detect',
|
||||
},
|
||||
'import/resolver': {
|
||||
node: {
|
||||
extensions: ['.js', '.ts', '.tsx'],
|
||||
},
|
||||
},
|
||||
},
|
||||
rules: {
|
||||
'no-param-reassign': 0,
|
||||
'valid-jsdoc': 0,
|
||||
'no-shadow': 0,
|
||||
'prefer-template': 0,
|
||||
'react/prop-types': 0,
|
||||
'react/no-unescaped-entities': 0,
|
||||
'import/extensions': 0,
|
||||
'no-unused-expressions': 0,
|
||||
complexity: 0,
|
||||
'import/order': [
|
||||
'error',
|
||||
{
|
||||
alphabetize: {
|
||||
order: 'asc',
|
||||
caseInsensitive: true,
|
||||
},
|
||||
'newlines-between': 'always',
|
||||
groups: ['builtin', 'external', 'parent', 'sibling', 'index'],
|
||||
pathGroups: [
|
||||
{
|
||||
pattern: '@/**/*',
|
||||
group: 'parent',
|
||||
position: 'before',
|
||||
},
|
||||
],
|
||||
pathGroupsExcludedImportTypes: ['builtin'],
|
||||
},
|
||||
],
|
||||
|
||||
// TMP
|
||||
'react/function-component-definition': ['off'],
|
||||
'react/jsx-filename-extension': ['off'],
|
||||
'jsdoc/check-examples': ['off'],
|
||||
},
|
||||
},
|
||||
{
|
||||
files: ['**/*.js'],
|
||||
},
|
||||
tseslint.configs.disableTypeChecked,
|
||||
{
|
||||
files: ['cypress/**/*'],
|
||||
plugins: {
|
||||
cypresss,
|
||||
},
|
||||
rules: {
|
||||
'@typescript-eslint/triple-slash-reference': 0,
|
||||
},
|
||||
},
|
||||
{
|
||||
files: ['packages/website/**/*'],
|
||||
rules: {
|
||||
'import/no-unresolved': 0,
|
||||
'import/no-extraneous-dependencies': 0,
|
||||
},
|
||||
},
|
||||
{
|
||||
files: ['examples/demo/**/*'],
|
||||
rules: {
|
||||
'react/react-in-jsx-scope': 0,
|
||||
},
|
||||
},
|
||||
];
|
||||
|
|
@ -12,7 +12,9 @@
|
|||
},
|
||||
"dependencies": {
|
||||
"@docsearch/css": "3.6.3",
|
||||
"@docsearch/react": "3.6.3"
|
||||
"@docsearch/react": "3.6.3",
|
||||
"react": "^18.0.0",
|
||||
"react-dom": "^18.0.0"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@vitejs/plugin-react": "^4.3.3",
|
||||
|
|
|
|||
|
|
@ -3,16 +3,11 @@ import { DocSearch } from '@docsearch/react';
|
|||
import './App.css';
|
||||
import '@docsearch/css/dist/style.css';
|
||||
|
||||
function App() {
|
||||
function App(): JSX.Element {
|
||||
return (
|
||||
<div>
|
||||
<h1>DocSearch v3 - React</h1>
|
||||
<DocSearch
|
||||
indexName="vuejs"
|
||||
appId="ML0LEBN7FQ"
|
||||
apiKey="21cf9df0734770a2448a9da64a700c22"
|
||||
insights={true}
|
||||
/>
|
||||
<DocSearch indexName="vuejs" appId="ML0LEBN7FQ" apiKey="21cf9df0734770a2448a9da64a700c22" insights={true} />
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,10 +1,10 @@
|
|||
import { StrictMode } from 'react';
|
||||
import { createRoot } from 'react-dom/client';
|
||||
|
||||
import App from './App.js';
|
||||
import App from './App.tsx';
|
||||
|
||||
createRoot(document.getElementById('root')!).render(
|
||||
<StrictMode>
|
||||
<App />
|
||||
</StrictMode>
|
||||
</StrictMode>,
|
||||
);
|
||||
|
|
|
|||
|
|
@ -3,10 +3,13 @@
|
|||
"tsBuildInfoFile": "./node_modules/.tmp/tsconfig.app.tsbuildinfo",
|
||||
"target": "ES2020",
|
||||
"useDefineForClassFields": true,
|
||||
"lib": ["ES2020", "DOM", "DOM.Iterable"],
|
||||
"lib": [
|
||||
"ES2020",
|
||||
"DOM",
|
||||
"DOM.Iterable"
|
||||
],
|
||||
"module": "ESNext",
|
||||
"skipLibCheck": true,
|
||||
|
||||
/* Bundler mode */
|
||||
"moduleResolution": "Bundler",
|
||||
"allowImportingTsExtensions": true,
|
||||
|
|
@ -14,7 +17,6 @@
|
|||
"moduleDetection": "force",
|
||||
"noEmit": true,
|
||||
"jsx": "react-jsx",
|
||||
|
||||
/* Linting */
|
||||
"strict": true,
|
||||
"noUnusedLocals": true,
|
||||
|
|
@ -22,5 +24,8 @@
|
|||
"noFallthroughCasesInSwitch": true,
|
||||
"noUncheckedSideEffectImports": true
|
||||
},
|
||||
"include": ["src"]
|
||||
"include": [
|
||||
"src",
|
||||
"vite.config.ts"
|
||||
],
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,7 +1,7 @@
|
|||
import { defineConfig } from 'vite'
|
||||
import react from '@vitejs/plugin-react'
|
||||
import react from '@vitejs/plugin-react';
|
||||
import { defineConfig } from 'vite';
|
||||
|
||||
// https://vite.dev/config/
|
||||
export default defineConfig({
|
||||
plugins: [react()],
|
||||
})
|
||||
});
|
||||
|
|
|
|||
|
|
@ -3,16 +3,18 @@
|
|||
"target": "ES2020",
|
||||
"useDefineForClassFields": true,
|
||||
"module": "ESNext",
|
||||
"lib": ["ES2020", "DOM", "DOM.Iterable"],
|
||||
"lib": [
|
||||
"ES2020",
|
||||
"DOM",
|
||||
"DOM.Iterable"
|
||||
],
|
||||
"skipLibCheck": true,
|
||||
|
||||
/* Bundler mode */
|
||||
"moduleResolution": "Bundler",
|
||||
"allowImportingTsExtensions": true,
|
||||
"isolatedModules": true,
|
||||
"moduleDetection": "force",
|
||||
"noEmit": true,
|
||||
|
||||
/* Linting */
|
||||
"strict": true,
|
||||
"noUnusedLocals": true,
|
||||
|
|
@ -20,5 +22,8 @@
|
|||
"noFallthroughCasesInSwitch": true,
|
||||
"noUncheckedSideEffectImports": true
|
||||
},
|
||||
"include": ["src"]
|
||||
"include": [
|
||||
"src",
|
||||
"vite.config.ts"
|
||||
]
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,16 +0,0 @@
|
|||
module.exports = {
|
||||
preset: 'ts-jest',
|
||||
testMatch: ['<rootDir>/packages/docsearch-*/src/__tests__/*.test.(ts|tsx)'],
|
||||
transformIgnorePatterns: [
|
||||
'<rootDir>/node_modules/',
|
||||
'<rootDir>/packages/docsearch-*/node_modules/',
|
||||
],
|
||||
transform: {
|
||||
'^.+\\.(tsx|ts|js)$': 'babel-jest',
|
||||
},
|
||||
setupFilesAfterEnv: ['<rootDir>/scripts/jest/setupTests.ts'],
|
||||
testEnvironment: 'jsdom',
|
||||
globals: {
|
||||
__DEV__: true,
|
||||
},
|
||||
};
|
||||
120
package.json
120
package.json
|
|
@ -1,30 +1,23 @@
|
|||
{
|
||||
"name": "@docsearch/monorepo",
|
||||
"private": true,
|
||||
"workspaces": {
|
||||
"packages": [
|
||||
"packages/*",
|
||||
"examples/*"
|
||||
],
|
||||
"nohoist": [
|
||||
"**/html-minifier-terser"
|
||||
]
|
||||
},
|
||||
"workspaces": [
|
||||
"packages/*",
|
||||
"examples/*"
|
||||
],
|
||||
"scripts": {
|
||||
"build:clean": "lerna run build:clean --scope @docsearch/*",
|
||||
"build:esm": "lerna run build:esm --scope @docsearch/*",
|
||||
"build:types": "lerna run build:types --scope @docsearch/*",
|
||||
"build:umd": "lerna run build:umd --scope @docsearch/*",
|
||||
"build": "lerna run build --scope @docsearch/*",
|
||||
"build:clean": "lerna run build:clean",
|
||||
"build:types": "lerna run build:types",
|
||||
"build": "lerna run build",
|
||||
"cy:clean": "rm -rf cypress/screenshots",
|
||||
"cy:info": "cypress info",
|
||||
"cy:run:chrome": "yarn run cy:run --browser chrome",
|
||||
"cy:run:edge": "yarn run cy:run --browser edge",
|
||||
"cy:run:firefox": "yarn run cy:run --browser firefox",
|
||||
"cy:run": "start-server-and-test 'yarn website:test' http://localhost:3000 'cypress run --spec 'cypress/e2e/**/*' --headless'",
|
||||
"cy:run": "start-server-and-test 'yarn website:test' http://localhost:3000 'cypress run --headless'",
|
||||
"cy:verify": "cypress verify",
|
||||
"lint:css": "stylelint **/src/**/*.css",
|
||||
"lint": "eslint --ext .js,.ts,.tsx .",
|
||||
"lint": "eslint .",
|
||||
"playground:build": "yarn workspace @docsearch/react-example build",
|
||||
"playground:start": "yarn workspace @docsearch/react-example dev",
|
||||
"playground-js:start": "yarn workspace @docsearch/js-example dev",
|
||||
|
|
@ -32,67 +25,64 @@
|
|||
"start": "yarn run watch",
|
||||
"test:size": "bundlesize",
|
||||
"test:types": "tsc --noEmit",
|
||||
"test": "jest",
|
||||
"watch": "lerna run watch --parallel --scope @docsearch/*",
|
||||
"test": "vitest",
|
||||
"watch": "lerna run watch --parallel",
|
||||
"website:build": "yarn workspace @docsearch/website build",
|
||||
"website:start": "yarn workspace @docsearch/website start --host 0.0.0.0",
|
||||
"website:test": "yarn workspace @docsearch/website start --no-open"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@babel/plugin-transform-react-jsx": "7.23.4",
|
||||
"@babel/preset-env": "7.23.5",
|
||||
"@babel/preset-typescript": "7.23.3",
|
||||
"@rollup/plugin-babel": "^6.0.4",
|
||||
"@rollup/plugin-json": "4.1.0",
|
||||
"@rollup/plugin-node-resolve": "11.2.1",
|
||||
"@rollup/plugin-replace": "2.3.3",
|
||||
"@testing-library/dom": "9.3.1",
|
||||
"@testing-library/jest-dom": "5.16.5",
|
||||
"@testing-library/react": "14.1.2",
|
||||
"@types/jest": "^29.0.0",
|
||||
"@babel/plugin-transform-react-jsx": "7.25.9",
|
||||
"@babel/preset-env": "7.26.0",
|
||||
"@babel/preset-typescript": "7.26.0",
|
||||
"@eslint-community/eslint-plugin-eslint-comments": "4.4.1",
|
||||
"@rollup/plugin-babel": "6.0.4",
|
||||
"@rollup/plugin-json": "6.1.0",
|
||||
"@rollup/plugin-node-resolve": "15.3.0",
|
||||
"@rollup/plugin-replace": "6.0.1",
|
||||
"@rollup/plugin-terser": "0.4.4",
|
||||
"@stylistic/eslint-plugin": "2.10.1",
|
||||
"@testing-library/dom": "10.4.0",
|
||||
"@types/react": "^18.0.0",
|
||||
"@types/react-dom": "^18.0.0",
|
||||
"@typescript-eslint/eslint-plugin": "5.41.0",
|
||||
"@typescript-eslint/parser": "5.41.0",
|
||||
"babel-plugin-inline-replace-variables": "1.3.1",
|
||||
"babel-plugin-module-resolver": "4.0.0",
|
||||
"@typescript-eslint/eslint-plugin": "8.12.2",
|
||||
"@typescript-eslint/parser": "8.12.2",
|
||||
"babel-plugin-module-resolver": "5.0.2",
|
||||
"bundlesize2": "0.0.31",
|
||||
"cssnano": "4.1.10",
|
||||
"cssnano": "7.0.6",
|
||||
"cypress": "13.15.1",
|
||||
"eslint": "8.55.0",
|
||||
"eslint-config-algolia": "20.0.0",
|
||||
"eslint-config-prettier": "8.10.0",
|
||||
"eslint": "8.57.1",
|
||||
"eslint-config-algolia": "23.2.1",
|
||||
"eslint-config-prettier": "9.1.0",
|
||||
"eslint-plugin-cypress": "4.1.0",
|
||||
"eslint-plugin-eslint-comments": "3.2.0",
|
||||
"eslint-plugin-import": "2.29.0",
|
||||
"eslint-plugin-jest": "27.6.0",
|
||||
"eslint-plugin-jsdoc": "39.4.0",
|
||||
"eslint-plugin-jsx-a11y": "6.6.1",
|
||||
"eslint-plugin-prettier": "4.2.1",
|
||||
"eslint-plugin-react": "7.31.10",
|
||||
"eslint-plugin-react-hooks": "4.6.0",
|
||||
"jest": "29.6.1",
|
||||
"jest-environment-jsdom": "29.6.1",
|
||||
"eslint-plugin-import": "2.31.0",
|
||||
"eslint-plugin-jsdoc": "50.4.3",
|
||||
"eslint-plugin-jsx-a11y": "6.10.2",
|
||||
"eslint-plugin-prettier": "5.2.1",
|
||||
"eslint-plugin-react": "7.37.2",
|
||||
"eslint-plugin-react-hooks": "5.0.0",
|
||||
"eslint-plugin-react-refresh": "0.4.14",
|
||||
"jsdom": "25.0.1",
|
||||
"lerna": "8.1.9",
|
||||
"postcss": "8.4.25",
|
||||
"prettier": "2.7.1",
|
||||
"react": "18.2.0",
|
||||
"react-dom": "18.2.0",
|
||||
"rollup": "1.32.1",
|
||||
"rollup-plugin-dts": "^6.1.1",
|
||||
"rollup-plugin-filesize": "9.1.2",
|
||||
"rollup-plugin-terser": "7.0.2",
|
||||
"postcss": "8.4.47",
|
||||
"prettier": "3.3.3",
|
||||
"react": "18.3.1",
|
||||
"react-dom": "18.3.1",
|
||||
"rollup": "4.24.4",
|
||||
"rollup-plugin-dts": "6.1.1",
|
||||
"rollup-plugin-filesize": "10.0.0",
|
||||
"shipjs": "0.26.4",
|
||||
"start-server-and-test": "1.15.5",
|
||||
"stylelint": "14.16.1",
|
||||
"stylelint-a11y": "1.2.3",
|
||||
"stylelint-config-sass-guidelines": "9.0.1",
|
||||
"stylelint-config-standard": "28.0.0",
|
||||
"stylelint-no-unsupported-browser-features": "5.0.4",
|
||||
"stylelint-order": "5.0.0",
|
||||
"stylelint-prettier": "3.0.0",
|
||||
"ts-jest": "28.0.8",
|
||||
"start-server-and-test": "2.0.8",
|
||||
"stylelint": "16.10.0",
|
||||
"stylelint-config-sass-guidelines": "12.1.0",
|
||||
"stylelint-config-standard": "36.0.1",
|
||||
"stylelint-no-unsupported-browser-features": "8.0.2",
|
||||
"stylelint-order": "6.0.4",
|
||||
"stylelint-prettier": "5.0.2",
|
||||
"typescript": "5.6.3",
|
||||
"typescript-eslint": "8.12.2",
|
||||
"vitest": "2.1.4",
|
||||
"watch": "1.0.2"
|
||||
}
|
||||
},
|
||||
"packageManager": "yarn@4.5.1"
|
||||
}
|
||||
|
|
|
|||
|
|
@ -13,12 +13,8 @@ const pkg = require('./package.json');
|
|||
const readFile = util.promisify(fs.readFile);
|
||||
|
||||
function getBundleBanner(_pkg) {
|
||||
const lastCommitHash = execSync('git rev-parse --short HEAD')
|
||||
.toString()
|
||||
.trim();
|
||||
const version = process.env.SHIPJS
|
||||
? _pkg.version
|
||||
: `${_pkg.version} (UNRELEASED ${lastCommitHash})`;
|
||||
const lastCommitHash = execSync('git rev-parse --short HEAD').toString().trim();
|
||||
const version = process.env.SHIPJS ? _pkg.version : `${_pkg.version} (UNRELEASED ${lastCommitHash})`;
|
||||
const authors = '© Algolia, Inc. and contributors';
|
||||
|
||||
return `/*! ${_pkg.name} ${version} | MIT License | ${authors} | ${_pkg.homepage} */`;
|
||||
|
|
@ -71,20 +67,14 @@ async function buildStyle() {
|
|||
|
||||
fs.writeFile(
|
||||
'dist/style.css',
|
||||
[
|
||||
getBundleBanner(pkg),
|
||||
[variablesOutput.css, buttonOutput.css, modalOutput.css].join(''),
|
||||
].join('\n'),
|
||||
() => true
|
||||
[getBundleBanner(pkg), [variablesOutput.css, buttonOutput.css, modalOutput.css].join('')].join('\n'),
|
||||
() => true,
|
||||
);
|
||||
|
||||
fs.writeFile(
|
||||
'dist/style.scss',
|
||||
[
|
||||
getBundleBanner(pkg),
|
||||
[variablesOutput.css, buttonOutput.css, modalOutput.css].join(''),
|
||||
].join('\n'),
|
||||
() => true
|
||||
[getBundleBanner(pkg), [variablesOutput.css, buttonOutput.css, modalOutput.css].join('')].join('\n'),
|
||||
() => true,
|
||||
);
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -1,6 +1,5 @@
|
|||
module.exports = (api) => {
|
||||
export default (api) => {
|
||||
const isTest = api.env('test');
|
||||
const modules = isTest ? 'commonjs' : false;
|
||||
const targets = {};
|
||||
|
||||
if (isTest) {
|
||||
|
|
@ -15,7 +14,6 @@ module.exports = (api) => {
|
|||
[
|
||||
'@babel/preset-env',
|
||||
{
|
||||
modules,
|
||||
targets,
|
||||
},
|
||||
],
|
||||
|
|
@ -24,12 +24,15 @@
|
|||
"build:clean": "rm -rf ./dist",
|
||||
"build:types": "tsc -p ./tsconfig.declaration.json --outDir ./dist/esm/types",
|
||||
"build:clean-types": "rm -rf ./dist/esm/types",
|
||||
"build": "yarn build:clean && yarn build:types && rollup --config && yarn build:clean-types",
|
||||
"build": "yarn build:clean && yarn build:types && rollup --config --bundleConfigAsCjs && yarn build:clean-types",
|
||||
"on:change": "yarn build",
|
||||
"watch": "watch \"yarn on:change\" --ignoreDirectoryPattern \"/dist/\""
|
||||
},
|
||||
"dependencies": {
|
||||
"@docsearch/react": "3.6.3",
|
||||
"preact": "^10.0.0"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@rollup/plugin-replace": "6.0.1"
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -27,6 +27,7 @@ export default [
|
|||
plugins: [
|
||||
...plugins,
|
||||
replace({
|
||||
preventAssignment: true,
|
||||
'process.env.NODE_ENV': JSON.stringify('production'),
|
||||
}),
|
||||
],
|
||||
|
|
|
|||
|
|
@ -2,10 +2,7 @@ import type { DocSearchProps as DocSearchComponentProps } from '@docsearch/react
|
|||
import { DocSearch, version } from '@docsearch/react';
|
||||
import React, { render } from 'preact/compat';
|
||||
|
||||
function getHTMLElement(
|
||||
value: HTMLElement | string,
|
||||
environment: DocSearchProps['environment'] = window
|
||||
): HTMLElement {
|
||||
function getHTMLElement(value: HTMLElement | string, environment: DocSearchProps['environment'] = window): HTMLElement {
|
||||
if (typeof value === 'string') {
|
||||
return environment.document.querySelector<HTMLElement>(value)!;
|
||||
}
|
||||
|
|
@ -18,18 +15,16 @@ interface DocSearchProps extends DocSearchComponentProps {
|
|||
environment?: typeof window;
|
||||
}
|
||||
|
||||
export function docsearch(props: DocSearchProps) {
|
||||
export function docsearch(props: DocSearchProps): void {
|
||||
render(
|
||||
<DocSearch
|
||||
{...props}
|
||||
transformSearchClient={(searchClient) => {
|
||||
searchClient.addAlgoliaAgent('docsearch.js', version);
|
||||
|
||||
return props.transformSearchClient
|
||||
? props.transformSearchClient(searchClient)
|
||||
: searchClient;
|
||||
return props.transformSearchClient ? props.transformSearchClient(searchClient) : searchClient;
|
||||
}}
|
||||
/>,
|
||||
getHTMLElement(props.container, props.environment)
|
||||
getHTMLElement(props.container, props.environment),
|
||||
);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,6 +1,5 @@
|
|||
module.exports = (api) => {
|
||||
export default (api) => {
|
||||
const isTest = api.env('test');
|
||||
const modules = isTest ? 'commonjs' : 'auto';
|
||||
const targets = {};
|
||||
|
||||
if (isTest) {
|
||||
|
|
@ -15,7 +14,6 @@ module.exports = (api) => {
|
|||
[
|
||||
'@babel/preset-env',
|
||||
{
|
||||
modules,
|
||||
targets,
|
||||
},
|
||||
],
|
||||
|
|
@ -27,15 +27,21 @@
|
|||
"build:clean": "rm -rf ./dist",
|
||||
"build:clean-types": "rm -rf ./dist/esm/types",
|
||||
"build:types": "tsc -p ./tsconfig.declaration.json --outDir ./dist/esm/types",
|
||||
"build": "yarn build:clean && yarn build:types && rollup --config && yarn build:clean-types",
|
||||
"build": "yarn build:clean && yarn build:types && rollup --config --bundleConfigAsCjs && yarn build:clean-types",
|
||||
"on:change": "yarn build",
|
||||
"watch": "watch \"yarn on:change\" --ignoreDirectoryPattern \"/dist/\""
|
||||
},
|
||||
"dependencies": {
|
||||
"@algolia/autocomplete-core": "1.9.3",
|
||||
"@algolia/autocomplete-core": "1.17.6",
|
||||
"@algolia/autocomplete-preset-algolia": "1.17.6",
|
||||
"@docsearch/css": "3.6.3",
|
||||
"algoliasearch": "^5.11.0"
|
||||
"algoliasearch": "^5.12.0"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@rollup/plugin-replace": "6.0.1",
|
||||
"@testing-library/jest-dom": "6.6.3",
|
||||
"@testing-library/react": "16.0.1",
|
||||
"vitest": "2.1.4"
|
||||
},
|
||||
"peerDependencies": {
|
||||
"@types/react": ">= 16.8.0 < 19.0.0",
|
||||
|
|
|
|||
|
|
@ -26,6 +26,7 @@ export default [
|
|||
plugins: [
|
||||
...plugins,
|
||||
replace({
|
||||
preventAssignment: true,
|
||||
'process.env.NODE_ENV': JSON.stringify('production'),
|
||||
}),
|
||||
],
|
||||
|
|
|
|||
|
|
@ -8,7 +8,7 @@ type AlgoliaLogoProps = {
|
|||
translations?: AlgoliaLogoTranslations;
|
||||
};
|
||||
|
||||
export function AlgoliaLogo({ translations = {} }: AlgoliaLogoProps) {
|
||||
export function AlgoliaLogo({ translations = {} }: AlgoliaLogoProps): JSX.Element {
|
||||
const { searchByText = 'Search by' } = translations;
|
||||
|
||||
return (
|
||||
|
|
@ -28,21 +28,13 @@ export function AlgoliaLogo({ translations = {} }: AlgoliaLogoProps) {
|
|||
viewBox="0 0 2196.2 500"
|
||||
>
|
||||
<defs>
|
||||
<style>{`.cls-1,.cls-2{fill:#003dff;}.cls-2{fill-rule:evenodd;}`}</style>
|
||||
<style>{'.cls-1,.cls-2{fill:#003dff;}.cls-2{fill-rule:evenodd;}'}</style>
|
||||
</defs>
|
||||
<path
|
||||
className="cls-2"
|
||||
d="M1070.38,275.3V5.91c0-3.63-3.24-6.39-6.82-5.83l-50.46,7.94c-2.87,.45-4.99,2.93-4.99,5.84l.17,273.22c0,12.92,0,92.7,95.97,95.49,3.33,.1,6.09-2.58,6.09-5.91v-40.78c0-2.96-2.19-5.51-5.12-5.84-34.85-4.01-34.85-47.57-34.85-54.72Z"
|
||||
/>
|
||||
<rect
|
||||
className="cls-1"
|
||||
x="1845.88"
|
||||
y="104.73"
|
||||
width="62.58"
|
||||
height="277.9"
|
||||
rx="5.9"
|
||||
ry="5.9"
|
||||
/>
|
||||
<rect className="cls-1" x="1845.88" y="104.73" width="62.58" height="277.9" rx="5.9" ry="5.9" />
|
||||
<path
|
||||
className="cls-2"
|
||||
d="M1851.78,71.38h50.77c3.26,0,5.9-2.64,5.9-5.9V5.9c0-3.62-3.24-6.39-6.82-5.83l-50.77,7.95c-2.87,.45-4.99,2.92-4.99,5.83v51.62c0,3.26,2.64,5.9,5.9,5.9Z"
|
||||
|
|
|
|||
|
|
@ -1,7 +1,4 @@
|
|||
import type {
|
||||
AutocompleteState,
|
||||
AutocompleteOptions,
|
||||
} from '@algolia/autocomplete-core';
|
||||
import type { AutocompleteState, AutocompleteOptions } from '@algolia/autocomplete-core';
|
||||
import type { SearchClient } from 'algoliasearch';
|
||||
import type { SearchQuery, LiteClient } from 'algoliasearch/lite';
|
||||
import React from 'react';
|
||||
|
|
@ -9,11 +6,7 @@ import { createPortal } from 'react-dom';
|
|||
|
||||
import { DocSearchButton } from './DocSearchButton';
|
||||
import { DocSearchModal } from './DocSearchModal';
|
||||
import type {
|
||||
DocSearchHit,
|
||||
InternalDocSearchHit,
|
||||
StoredDocSearchHit,
|
||||
} from './types';
|
||||
import type { DocSearchHit, InternalDocSearchHit, StoredDocSearchHit } from './types';
|
||||
import { useDocSearchKeyboardEvents } from './useDocSearchKeyboardEvents';
|
||||
|
||||
import type { ButtonTranslations, ModalTranslations } from '.';
|
||||
|
|
@ -31,16 +24,9 @@ export interface DocSearchProps {
|
|||
searchParameters?: SearchQuery;
|
||||
maxResultsPerGroup?: number;
|
||||
transformItems?: (items: DocSearchHit[]) => DocSearchHit[];
|
||||
hitComponent?: (props: {
|
||||
hit: InternalDocSearchHit | StoredDocSearchHit;
|
||||
children: React.ReactNode;
|
||||
}) => JSX.Element;
|
||||
resultsFooterComponent?: (props: {
|
||||
state: AutocompleteState<InternalDocSearchHit>;
|
||||
}) => JSX.Element | null;
|
||||
transformSearchClient?: <T extends LiteClient | SearchClient>(
|
||||
searchClient: T
|
||||
) => T;
|
||||
hitComponent?: (props: { hit: InternalDocSearchHit | StoredDocSearchHit; children: React.ReactNode }) => JSX.Element;
|
||||
resultsFooterComponent?: (props: { state: AutocompleteState<InternalDocSearchHit> }) => JSX.Element | null;
|
||||
transformSearchClient?: <T extends LiteClient | SearchClient>(searchClient: T) => T;
|
||||
disableUserPersonalization?: boolean;
|
||||
initialQuery?: string;
|
||||
navigator?: AutocompleteOptions<InternalDocSearchHit>['navigator'];
|
||||
|
|
@ -49,12 +35,10 @@ export interface DocSearchProps {
|
|||
insights?: AutocompleteOptions<InternalDocSearchHit>['insights'];
|
||||
}
|
||||
|
||||
export function DocSearch(props: DocSearchProps) {
|
||||
export function DocSearch(props: DocSearchProps): JSX.Element {
|
||||
const searchButtonRef = React.useRef<HTMLButtonElement>(null);
|
||||
const [isOpen, setIsOpen] = React.useState(false);
|
||||
const [initialQuery, setInitialQuery] = React.useState<string | undefined>(
|
||||
props?.initialQuery || undefined
|
||||
);
|
||||
const [initialQuery, setInitialQuery] = React.useState<string | undefined>(props?.initialQuery || undefined);
|
||||
|
||||
const onOpen = React.useCallback(() => {
|
||||
setIsOpen(true);
|
||||
|
|
@ -70,7 +54,7 @@ export function DocSearch(props: DocSearchProps) {
|
|||
setIsOpen(true);
|
||||
setInitialQuery(event.key);
|
||||
},
|
||||
[setIsOpen, setInitialQuery]
|
||||
[setIsOpen, setInitialQuery],
|
||||
);
|
||||
|
||||
useDocSearchKeyboardEvents({
|
||||
|
|
@ -83,11 +67,7 @@ export function DocSearch(props: DocSearchProps) {
|
|||
|
||||
return (
|
||||
<>
|
||||
<DocSearchButton
|
||||
ref={searchButtonRef}
|
||||
translations={props?.translations?.button}
|
||||
onClick={onOpen}
|
||||
/>
|
||||
<DocSearchButton ref={searchButtonRef} translations={props?.translations?.button} onClick={onOpen} />
|
||||
|
||||
{isOpen &&
|
||||
createPortal(
|
||||
|
|
@ -98,7 +78,7 @@ export function DocSearch(props: DocSearchProps) {
|
|||
translations={props?.translations?.modal}
|
||||
onClose={onClose}
|
||||
/>,
|
||||
document.body
|
||||
document.body,
|
||||
)}
|
||||
</>
|
||||
);
|
||||
|
|
|
|||
|
|
@ -15,65 +15,49 @@ export type DocSearchButtonProps = React.ComponentProps<'button'> & {
|
|||
const ACTION_KEY_DEFAULT = 'Ctrl' as const;
|
||||
const ACTION_KEY_APPLE = '⌘' as const;
|
||||
|
||||
function isAppleDevice() {
|
||||
function isAppleDevice(): boolean {
|
||||
return /(Mac|iPhone|iPod|iPad)/i.test(navigator.platform);
|
||||
}
|
||||
|
||||
export const DocSearchButton = React.forwardRef<
|
||||
HTMLButtonElement,
|
||||
DocSearchButtonProps
|
||||
>(({ translations = {}, ...props }, ref) => {
|
||||
const { buttonText = 'Search', buttonAriaLabel = 'Search' } = translations;
|
||||
export const DocSearchButton = React.forwardRef<HTMLButtonElement, DocSearchButtonProps>(
|
||||
({ translations = {}, ...props }, ref) => {
|
||||
const { buttonText = 'Search', buttonAriaLabel = 'Search' } = translations;
|
||||
|
||||
const [key, setKey] = useState<
|
||||
typeof ACTION_KEY_APPLE | typeof ACTION_KEY_DEFAULT | null
|
||||
>(null);
|
||||
const [key, setKey] = useState<typeof ACTION_KEY_APPLE | typeof ACTION_KEY_DEFAULT | null>(null);
|
||||
|
||||
useEffect(() => {
|
||||
if (typeof navigator !== 'undefined') {
|
||||
isAppleDevice() ? setKey(ACTION_KEY_APPLE) : setKey(ACTION_KEY_DEFAULT);
|
||||
}
|
||||
}, []);
|
||||
useEffect(() => {
|
||||
if (typeof navigator !== 'undefined') {
|
||||
isAppleDevice() ? setKey(ACTION_KEY_APPLE) : setKey(ACTION_KEY_DEFAULT);
|
||||
}
|
||||
}, []);
|
||||
|
||||
return (
|
||||
<button
|
||||
type="button"
|
||||
className="DocSearch DocSearch-Button"
|
||||
aria-label={buttonAriaLabel}
|
||||
{...props}
|
||||
ref={ref}
|
||||
>
|
||||
<span className="DocSearch-Button-Container">
|
||||
<SearchIcon />
|
||||
<span className="DocSearch-Button-Placeholder">{buttonText}</span>
|
||||
</span>
|
||||
return (
|
||||
<button type="button" className="DocSearch DocSearch-Button" aria-label={buttonAriaLabel} {...props} ref={ref}>
|
||||
<span className="DocSearch-Button-Container">
|
||||
<SearchIcon />
|
||||
<span className="DocSearch-Button-Placeholder">{buttonText}</span>
|
||||
</span>
|
||||
|
||||
<span className="DocSearch-Button-Keys">
|
||||
{key !== null && (
|
||||
<>
|
||||
<DocSearchButtonKey
|
||||
reactsToKey={
|
||||
key === ACTION_KEY_DEFAULT ? ACTION_KEY_DEFAULT : 'Meta'
|
||||
}
|
||||
>
|
||||
{key === ACTION_KEY_DEFAULT ? <ControlKeyIcon /> : key}
|
||||
</DocSearchButtonKey>
|
||||
<DocSearchButtonKey reactsToKey="k">K</DocSearchButtonKey>
|
||||
</>
|
||||
)}
|
||||
</span>
|
||||
</button>
|
||||
);
|
||||
});
|
||||
<span className="DocSearch-Button-Keys">
|
||||
{key !== null && (
|
||||
<>
|
||||
<DocSearchButtonKey reactsToKey={key === ACTION_KEY_DEFAULT ? ACTION_KEY_DEFAULT : 'Meta'}>
|
||||
{key === ACTION_KEY_DEFAULT ? <ControlKeyIcon /> : key}
|
||||
</DocSearchButtonKey>
|
||||
<DocSearchButtonKey reactsToKey="k">K</DocSearchButtonKey>
|
||||
</>
|
||||
)}
|
||||
</span>
|
||||
</button>
|
||||
);
|
||||
},
|
||||
);
|
||||
|
||||
type DocSearchButtonKeyProps = {
|
||||
reactsToKey?: string;
|
||||
};
|
||||
|
||||
function DocSearchButtonKey({
|
||||
reactsToKey,
|
||||
children,
|
||||
}: React.PropsWithChildren<DocSearchButtonKeyProps>) {
|
||||
function DocSearchButtonKey({ reactsToKey, children }: React.PropsWithChildren<DocSearchButtonKeyProps>): JSX.Element {
|
||||
const [isKeyDown, setIsKeyDown] = useState(false);
|
||||
|
||||
useEffect(() => {
|
||||
|
|
@ -81,13 +65,13 @@ function DocSearchButtonKey({
|
|||
return undefined;
|
||||
}
|
||||
|
||||
function handleKeyDown(e: KeyboardEvent) {
|
||||
function handleKeyDown(e: KeyboardEvent): void {
|
||||
if (e.key === reactsToKey) {
|
||||
setIsKeyDown(true);
|
||||
}
|
||||
}
|
||||
|
||||
function handleKeyUp(e: KeyboardEvent) {
|
||||
function handleKeyUp(e: KeyboardEvent): void {
|
||||
if (
|
||||
e.key === reactsToKey ||
|
||||
// keyup doesn't fire when Command is held down,
|
||||
|
|
@ -102,20 +86,14 @@ function DocSearchButtonKey({
|
|||
window.addEventListener('keydown', handleKeyDown);
|
||||
window.addEventListener('keyup', handleKeyUp);
|
||||
|
||||
return () => {
|
||||
return (): void => {
|
||||
window.removeEventListener('keydown', handleKeyDown);
|
||||
window.removeEventListener('keyup', handleKeyUp);
|
||||
};
|
||||
}, [reactsToKey]);
|
||||
|
||||
return (
|
||||
<kbd
|
||||
className={
|
||||
isKeyDown
|
||||
? 'DocSearch-Button-Key DocSearch-Button-Key--pressed'
|
||||
: 'DocSearch-Button-Key'
|
||||
}
|
||||
>
|
||||
<kbd className={isKeyDown ? 'DocSearch-Button-Key DocSearch-Button-Key--pressed' : 'DocSearch-Button-Key'}>
|
||||
{children}
|
||||
</kbd>
|
||||
);
|
||||
|
|
|
|||
|
|
@ -1,7 +1,4 @@
|
|||
import {
|
||||
type AlgoliaInsightsHit,
|
||||
createAutocomplete,
|
||||
} from '@algolia/autocomplete-core';
|
||||
import { type AlgoliaInsightsHit, createAutocomplete } from '@algolia/autocomplete-core';
|
||||
import type { SearchResponse } from 'algoliasearch/lite';
|
||||
import React from 'react';
|
||||
|
||||
|
|
@ -15,22 +12,11 @@ import { ScreenState } from './ScreenState';
|
|||
import type { SearchBoxTranslations } from './SearchBox';
|
||||
import { SearchBox } from './SearchBox';
|
||||
import { createStoredSearches } from './stored-searches';
|
||||
import type {
|
||||
DocSearchHit,
|
||||
DocSearchState,
|
||||
InternalDocSearchHit,
|
||||
StoredDocSearchHit,
|
||||
} from './types';
|
||||
import type { DocSearchHit, DocSearchState, InternalDocSearchHit, StoredDocSearchHit } from './types';
|
||||
import { useSearchClient } from './useSearchClient';
|
||||
import { useTouchEvents } from './useTouchEvents';
|
||||
import { useTrapFocus } from './useTrapFocus';
|
||||
import {
|
||||
groupBy,
|
||||
identity,
|
||||
noop,
|
||||
removeHighlightTags,
|
||||
isModifierEvent,
|
||||
} from './utils';
|
||||
import { groupBy, identity, noop, removeHighlightTags, isModifierEvent } from './utils';
|
||||
|
||||
export type ModalTranslations = Partial<{
|
||||
searchBox: SearchBoxTranslations;
|
||||
|
|
@ -54,7 +40,7 @@ export function DocSearchModal({
|
|||
onClose = noop,
|
||||
transformItems = identity,
|
||||
hitComponent = Hit,
|
||||
resultsFooterComponent = () => null,
|
||||
resultsFooterComponent = (): JSX.Element | null => null,
|
||||
navigator,
|
||||
initialScrollY = 0,
|
||||
transformSearchClient = identity,
|
||||
|
|
@ -63,15 +49,9 @@ export function DocSearchModal({
|
|||
translations = {},
|
||||
getMissingResultsUrl,
|
||||
insights = false,
|
||||
}: DocSearchModalProps) {
|
||||
const {
|
||||
footer: footerTranslations,
|
||||
searchBox: searchBoxTranslations,
|
||||
...screenStateTranslations
|
||||
} = translations;
|
||||
const [state, setState] = React.useState<
|
||||
DocSearchState<InternalDocSearchHit>
|
||||
>({
|
||||
}: DocSearchModalProps): JSX.Element {
|
||||
const { footer: footerTranslations, searchBox: searchBoxTranslations, ...screenStateTranslations } = translations;
|
||||
const [state, setState] = React.useState<DocSearchState<InternalDocSearchHit>>({
|
||||
query: '',
|
||||
collections: [],
|
||||
completion: null,
|
||||
|
|
@ -88,20 +68,16 @@ export function DocSearchModal({
|
|||
const inputRef = React.useRef<HTMLInputElement | null>(null);
|
||||
const snippetLength = React.useRef<number>(10);
|
||||
const initialQueryFromSelection = React.useRef(
|
||||
typeof window !== 'undefined'
|
||||
? window.getSelection()!.toString().slice(0, MAX_QUERY_SIZE)
|
||||
: ''
|
||||
).current;
|
||||
const initialQuery = React.useRef(
|
||||
initialQueryFromProp || initialQueryFromSelection
|
||||
typeof window !== 'undefined' ? window.getSelection()!.toString().slice(0, MAX_QUERY_SIZE) : '',
|
||||
).current;
|
||||
const initialQuery = React.useRef(initialQueryFromProp || initialQueryFromSelection).current;
|
||||
|
||||
const searchClient = useSearchClient(appId, apiKey, transformSearchClient);
|
||||
const favoriteSearches = React.useRef(
|
||||
createStoredSearches<StoredDocSearchHit>({
|
||||
key: `__DOCSEARCH_FAVORITE_SEARCHES__${indexName}`,
|
||||
limit: 10,
|
||||
})
|
||||
}),
|
||||
).current;
|
||||
const recentSearches = React.useRef(
|
||||
createStoredSearches<StoredDocSearchHit>({
|
||||
|
|
@ -109,7 +85,7 @@ export function DocSearchModal({
|
|||
// We display 7 recent searches and there's no favorites, but only
|
||||
// 4 when there are favorites.
|
||||
limit: favoriteSearches.getAll().length === 0 ? 7 : 4,
|
||||
})
|
||||
}),
|
||||
).current;
|
||||
|
||||
const saveRecentSearch = React.useCallback(
|
||||
|
|
@ -122,22 +98,16 @@ export function DocSearchModal({
|
|||
const search = item.type === 'content' ? item.__docsearch_parent : item;
|
||||
|
||||
// We save the recent search only if it's not favorited.
|
||||
if (
|
||||
search &&
|
||||
favoriteSearches
|
||||
.getAll()
|
||||
.findIndex((x) => x.objectID === search.objectID) === -1
|
||||
) {
|
||||
if (search && favoriteSearches.getAll().findIndex((x) => x.objectID === search.objectID) === -1) {
|
||||
recentSearches.add(search);
|
||||
}
|
||||
},
|
||||
[favoriteSearches, recentSearches, disableUserPersonalization]
|
||||
[favoriteSearches, recentSearches, disableUserPersonalization],
|
||||
);
|
||||
|
||||
const sendItemClickEvent = React.useCallback(
|
||||
(item: InternalDocSearchHit) => {
|
||||
if (!state.context.algoliaInsightsPlugin || !item.__autocomplete_id)
|
||||
return;
|
||||
if (!state.context.algoliaInsightsPlugin || !item.__autocomplete_id) return;
|
||||
|
||||
const insightsItem = item as AlgoliaInsightsHit;
|
||||
|
||||
|
|
@ -149,184 +119,164 @@ export function DocSearchModal({
|
|||
queryID: insightsItem.__autocomplete_queryID,
|
||||
};
|
||||
|
||||
state.context.algoliaInsightsPlugin.insights.clickedObjectIDsAfterSearch(
|
||||
insightsClickParams
|
||||
);
|
||||
state.context.algoliaInsightsPlugin.insights.clickedObjectIDsAfterSearch(insightsClickParams);
|
||||
},
|
||||
[state.context.algoliaInsightsPlugin]
|
||||
[state.context.algoliaInsightsPlugin],
|
||||
);
|
||||
|
||||
const autocomplete = React.useMemo(
|
||||
() =>
|
||||
createAutocomplete<
|
||||
InternalDocSearchHit,
|
||||
React.FormEvent<HTMLFormElement>,
|
||||
React.MouseEvent,
|
||||
React.KeyboardEvent
|
||||
>({
|
||||
id: 'docsearch',
|
||||
defaultActiveItemId: 0,
|
||||
placeholder,
|
||||
openOnFocus: true,
|
||||
initialState: {
|
||||
query: initialQuery,
|
||||
context: {
|
||||
searchSuggestions: [],
|
||||
createAutocomplete<InternalDocSearchHit, React.FormEvent<HTMLFormElement>, React.MouseEvent, React.KeyboardEvent>(
|
||||
{
|
||||
id: 'docsearch',
|
||||
defaultActiveItemId: 0,
|
||||
placeholder,
|
||||
openOnFocus: true,
|
||||
initialState: {
|
||||
query: initialQuery,
|
||||
context: {
|
||||
searchSuggestions: [],
|
||||
},
|
||||
},
|
||||
},
|
||||
insights,
|
||||
navigator,
|
||||
onStateChange(props) {
|
||||
setState(props.state);
|
||||
},
|
||||
getSources({ query, state: sourcesState, setContext, setStatus }) {
|
||||
if (!query) {
|
||||
if (disableUserPersonalization) {
|
||||
return [];
|
||||
insights,
|
||||
navigator,
|
||||
onStateChange(props) {
|
||||
setState(props.state);
|
||||
},
|
||||
getSources({ query, state: sourcesState, setContext, setStatus }) {
|
||||
if (!query) {
|
||||
if (disableUserPersonalization) {
|
||||
return [];
|
||||
}
|
||||
|
||||
return [
|
||||
{
|
||||
sourceId: 'recentSearches',
|
||||
onSelect({ item, event }): void {
|
||||
saveRecentSearch(item);
|
||||
|
||||
if (!isModifierEvent(event)) {
|
||||
onClose();
|
||||
}
|
||||
},
|
||||
getItemUrl({ item }): string {
|
||||
return item.url;
|
||||
},
|
||||
getItems(): InternalDocSearchHit[] {
|
||||
return recentSearches.getAll() as InternalDocSearchHit[];
|
||||
},
|
||||
},
|
||||
{
|
||||
sourceId: 'favoriteSearches',
|
||||
onSelect({ item, event }): void {
|
||||
saveRecentSearch(item);
|
||||
|
||||
if (!isModifierEvent(event)) {
|
||||
onClose();
|
||||
}
|
||||
},
|
||||
getItemUrl({ item }): string {
|
||||
return item.url;
|
||||
},
|
||||
getItems(): InternalDocSearchHit[] {
|
||||
return favoriteSearches.getAll() as InternalDocSearchHit[];
|
||||
},
|
||||
},
|
||||
];
|
||||
}
|
||||
|
||||
return [
|
||||
{
|
||||
sourceId: 'recentSearches',
|
||||
onSelect({ item, event }) {
|
||||
saveRecentSearch(item);
|
||||
const insightsActive = Boolean(insights);
|
||||
|
||||
if (!isModifierEvent(event)) {
|
||||
onClose();
|
||||
}
|
||||
},
|
||||
getItemUrl({ item }) {
|
||||
return item.url;
|
||||
},
|
||||
getItems() {
|
||||
return recentSearches.getAll() as InternalDocSearchHit[];
|
||||
},
|
||||
},
|
||||
{
|
||||
sourceId: 'favoriteSearches',
|
||||
onSelect({ item, event }) {
|
||||
saveRecentSearch(item);
|
||||
|
||||
if (!isModifierEvent(event)) {
|
||||
onClose();
|
||||
}
|
||||
},
|
||||
getItemUrl({ item }) {
|
||||
return item.url;
|
||||
},
|
||||
getItems() {
|
||||
return favoriteSearches.getAll() as InternalDocSearchHit[];
|
||||
},
|
||||
},
|
||||
];
|
||||
}
|
||||
|
||||
const insightsActive = Boolean(insights);
|
||||
|
||||
return searchClient
|
||||
.search<DocSearchHit>({
|
||||
requests: [
|
||||
{
|
||||
query,
|
||||
indexName,
|
||||
attributesToRetrieve: [
|
||||
'hierarchy.lvl0',
|
||||
'hierarchy.lvl1',
|
||||
'hierarchy.lvl2',
|
||||
'hierarchy.lvl3',
|
||||
'hierarchy.lvl4',
|
||||
'hierarchy.lvl5',
|
||||
'hierarchy.lvl6',
|
||||
'content',
|
||||
'type',
|
||||
'url',
|
||||
],
|
||||
attributesToSnippet: [
|
||||
`hierarchy.lvl1:${snippetLength.current}`,
|
||||
`hierarchy.lvl2:${snippetLength.current}`,
|
||||
`hierarchy.lvl3:${snippetLength.current}`,
|
||||
`hierarchy.lvl4:${snippetLength.current}`,
|
||||
`hierarchy.lvl5:${snippetLength.current}`,
|
||||
`hierarchy.lvl6:${snippetLength.current}`,
|
||||
`content:${snippetLength.current}`,
|
||||
],
|
||||
snippetEllipsisText: '…',
|
||||
highlightPreTag: '<mark>',
|
||||
highlightPostTag: '</mark>',
|
||||
hitsPerPage: 20,
|
||||
clickAnalytics: insightsActive,
|
||||
...searchParameters,
|
||||
},
|
||||
],
|
||||
})
|
||||
.catch((error) => {
|
||||
// The Algolia `RetryError` happens when all the servers have
|
||||
// failed, meaning that there's no chance the response comes
|
||||
// back. This is the right time to display an error.
|
||||
// See https://github.com/algolia/algoliasearch-client-javascript/blob/2ffddf59bc765cd1b664ee0346b28f00229d6e12/packages/transporter/src/errors/createRetryError.ts#L5
|
||||
if (error.name === 'RetryError') {
|
||||
setStatus('error');
|
||||
}
|
||||
|
||||
throw error;
|
||||
})
|
||||
.then(({ results }) => {
|
||||
const firstResult = results[0] as SearchResponse<DocSearchHit>;
|
||||
const { hits, nbHits } = firstResult;
|
||||
const sources = groupBy<DocSearchHit>(
|
||||
hits,
|
||||
(hit) => removeHighlightTags(hit),
|
||||
maxResultsPerGroup
|
||||
);
|
||||
|
||||
// We store the `lvl0`s to display them as search suggestions
|
||||
// in the "no results" screen.
|
||||
if (
|
||||
(sourcesState.context.searchSuggestions as any[]).length <
|
||||
Object.keys(sources).length
|
||||
) {
|
||||
setContext({
|
||||
searchSuggestions: Object.keys(sources),
|
||||
});
|
||||
}
|
||||
|
||||
setContext({ nbHits });
|
||||
|
||||
let insightsParams = {};
|
||||
|
||||
if (insightsActive) {
|
||||
insightsParams = {
|
||||
__autocomplete_indexName: indexName,
|
||||
__autocomplete_queryID: firstResult.queryID,
|
||||
__autocomplete_algoliaCredentials: {
|
||||
appId,
|
||||
apiKey,
|
||||
return searchClient
|
||||
.search<DocSearchHit>({
|
||||
requests: [
|
||||
{
|
||||
query,
|
||||
indexName,
|
||||
attributesToRetrieve: [
|
||||
'hierarchy.lvl0',
|
||||
'hierarchy.lvl1',
|
||||
'hierarchy.lvl2',
|
||||
'hierarchy.lvl3',
|
||||
'hierarchy.lvl4',
|
||||
'hierarchy.lvl5',
|
||||
'hierarchy.lvl6',
|
||||
'content',
|
||||
'type',
|
||||
'url',
|
||||
],
|
||||
attributesToSnippet: [
|
||||
`hierarchy.lvl1:${snippetLength.current}`,
|
||||
`hierarchy.lvl2:${snippetLength.current}`,
|
||||
`hierarchy.lvl3:${snippetLength.current}`,
|
||||
`hierarchy.lvl4:${snippetLength.current}`,
|
||||
`hierarchy.lvl5:${snippetLength.current}`,
|
||||
`hierarchy.lvl6:${snippetLength.current}`,
|
||||
`content:${snippetLength.current}`,
|
||||
],
|
||||
snippetEllipsisText: '…',
|
||||
highlightPreTag: '<mark>',
|
||||
highlightPostTag: '</mark>',
|
||||
hitsPerPage: 20,
|
||||
clickAnalytics: insightsActive,
|
||||
...searchParameters,
|
||||
},
|
||||
};
|
||||
}
|
||||
],
|
||||
})
|
||||
.catch((error) => {
|
||||
// The Algolia `RetryError` happens when all the servers have
|
||||
// failed, meaning that there's no chance the response comes
|
||||
// back. This is the right time to display an error.
|
||||
// See https://github.com/algolia/algoliasearch-client-javascript/blob/2ffddf59bc765cd1b664ee0346b28f00229d6e12/packages/transporter/src/errors/createRetryError.ts#L5
|
||||
if (error.name === 'RetryError') {
|
||||
setStatus('error');
|
||||
}
|
||||
|
||||
return Object.values<DocSearchHit[]>(sources).map(
|
||||
(items, index) => {
|
||||
throw error;
|
||||
})
|
||||
.then(({ results }) => {
|
||||
const firstResult = results[0] as SearchResponse<DocSearchHit>;
|
||||
const { hits, nbHits } = firstResult;
|
||||
const sources = groupBy<DocSearchHit>(hits, (hit) => removeHighlightTags(hit), maxResultsPerGroup);
|
||||
|
||||
// We store the `lvl0`s to display them as search suggestions
|
||||
// in the "no results" screen.
|
||||
if ((sourcesState.context.searchSuggestions as any[]).length < Object.keys(sources).length) {
|
||||
setContext({
|
||||
searchSuggestions: Object.keys(sources),
|
||||
});
|
||||
}
|
||||
|
||||
setContext({ nbHits });
|
||||
|
||||
let insightsParams = {};
|
||||
|
||||
if (insightsActive) {
|
||||
insightsParams = {
|
||||
__autocomplete_indexName: indexName,
|
||||
__autocomplete_queryID: firstResult.queryID,
|
||||
__autocomplete_algoliaCredentials: {
|
||||
appId,
|
||||
apiKey,
|
||||
},
|
||||
};
|
||||
}
|
||||
|
||||
return Object.values<DocSearchHit[]>(sources).map((items, index) => {
|
||||
return {
|
||||
sourceId: `hits${index}`,
|
||||
onSelect({ item, event }) {
|
||||
onSelect({ item, event }): void {
|
||||
saveRecentSearch(item);
|
||||
|
||||
if (!isModifierEvent(event)) {
|
||||
onClose();
|
||||
}
|
||||
},
|
||||
getItemUrl({ item }) {
|
||||
getItemUrl({ item }): string {
|
||||
return item.url;
|
||||
},
|
||||
getItems() {
|
||||
return Object.values(
|
||||
groupBy(
|
||||
items,
|
||||
(item) => item.hierarchy.lvl1,
|
||||
maxResultsPerGroup
|
||||
)
|
||||
)
|
||||
getItems(): InternalDocSearchHit[] {
|
||||
return Object.values(groupBy(items, (item) => item.hierarchy.lvl1, maxResultsPerGroup))
|
||||
.map(transformItems)
|
||||
.map((groupedHits) =>
|
||||
groupedHits.map((item) => {
|
||||
|
|
@ -334,9 +284,7 @@ export function DocSearchModal({
|
|||
|
||||
const potentialParent = groupedHits.find(
|
||||
(siblingItem) =>
|
||||
siblingItem.type === 'lvl1' &&
|
||||
siblingItem.hierarchy.lvl1 ===
|
||||
item.hierarchy.lvl1
|
||||
siblingItem.type === 'lvl1' && siblingItem.hierarchy.lvl1 === item.hierarchy.lvl1,
|
||||
) as InternalDocSearchHit | undefined;
|
||||
|
||||
if (item.type !== 'lvl1' && potentialParent) {
|
||||
|
|
@ -348,16 +296,16 @@ export function DocSearchModal({
|
|||
__docsearch_parent: parent,
|
||||
...insightsParams,
|
||||
};
|
||||
})
|
||||
}),
|
||||
)
|
||||
.flat();
|
||||
},
|
||||
};
|
||||
}
|
||||
);
|
||||
});
|
||||
});
|
||||
});
|
||||
},
|
||||
},
|
||||
}),
|
||||
),
|
||||
[
|
||||
indexName,
|
||||
searchParameters,
|
||||
|
|
@ -375,7 +323,7 @@ export function DocSearchModal({
|
|||
insights,
|
||||
appId,
|
||||
apiKey,
|
||||
]
|
||||
],
|
||||
);
|
||||
|
||||
const { getEnvironmentProps, getRootProps, refresh } = autocomplete;
|
||||
|
|
@ -391,7 +339,7 @@ export function DocSearchModal({
|
|||
React.useEffect(() => {
|
||||
document.body.classList.add('DocSearch--active');
|
||||
|
||||
return () => {
|
||||
return (): void => {
|
||||
document.body.classList.remove('DocSearch--active');
|
||||
|
||||
// IE11 doesn't support `scrollTo` so we check that the method exists
|
||||
|
|
@ -435,7 +383,7 @@ export function DocSearchModal({
|
|||
// because all mobile browsers don't compute their height the same way.
|
||||
// See https://css-tricks.com/the-trick-to-viewport-units-on-mobile/
|
||||
React.useEffect(() => {
|
||||
function setFullViewportHeight() {
|
||||
function setFullViewportHeight(): void {
|
||||
if (modalRef.current) {
|
||||
const vh = window.innerHeight * 0.01;
|
||||
modalRef.current.style.setProperty('--docsearch-vh', `${vh}px`);
|
||||
|
|
@ -446,7 +394,7 @@ export function DocSearchModal({
|
|||
|
||||
window.addEventListener('resize', setFullViewportHeight);
|
||||
|
||||
return () => {
|
||||
return (): void => {
|
||||
window.removeEventListener('resize', setFullViewportHeight);
|
||||
};
|
||||
}, []);
|
||||
|
|
@ -480,10 +428,7 @@ export function DocSearchModal({
|
|||
state={state}
|
||||
autoFocus={initialQuery.length === 0}
|
||||
inputRef={inputRef}
|
||||
isFromSelection={
|
||||
Boolean(initialQuery) &&
|
||||
initialQuery === initialQueryFromSelection
|
||||
}
|
||||
isFromSelection={Boolean(initialQuery) && initialQuery === initialQueryFromSelection}
|
||||
translations={searchBoxTranslations}
|
||||
onClose={onClose}
|
||||
/>
|
||||
|
|
|
|||
|
|
@ -11,11 +11,9 @@ type ErrorScreenProps = {
|
|||
translations?: ErrorScreenTranslations;
|
||||
};
|
||||
|
||||
export function ErrorScreen({ translations = {} }: ErrorScreenProps) {
|
||||
const {
|
||||
titleText = 'Unable to fetch results',
|
||||
helpText = 'You might want to check your network connection.',
|
||||
} = translations;
|
||||
export function ErrorScreen({ translations = {} }: ErrorScreenProps): JSX.Element {
|
||||
const { titleText = 'Unable to fetch results', helpText = 'You might want to check your network connection.' } =
|
||||
translations;
|
||||
return (
|
||||
<div className="DocSearch-ErrorScreen">
|
||||
<div className="DocSearch-Screen-Icon">
|
||||
|
|
|
|||
|
|
@ -22,23 +22,17 @@ interface CommandIconProps {
|
|||
ariaLabel: string;
|
||||
}
|
||||
|
||||
function CommandIcon(props: CommandIconProps) {
|
||||
function CommandIcon(props: CommandIconProps): JSX.Element {
|
||||
return (
|
||||
<svg width="15" height="15" aria-label={props.ariaLabel} role="img">
|
||||
<g
|
||||
fill="none"
|
||||
stroke="currentColor"
|
||||
strokeLinecap="round"
|
||||
strokeLinejoin="round"
|
||||
strokeWidth="1.2"
|
||||
>
|
||||
<g fill="none" stroke="currentColor" strokeLinecap="round" strokeLinejoin="round" strokeWidth="1.2">
|
||||
{props.children}
|
||||
</g>
|
||||
</svg>
|
||||
);
|
||||
}
|
||||
|
||||
export function Footer({ translations = {} }: FooterProps) {
|
||||
export function Footer({ translations = {} }: FooterProps): JSX.Element {
|
||||
const {
|
||||
selectText = 'to select',
|
||||
selectKeyAriaLabel = 'Enter key',
|
||||
|
|
|
|||
|
|
@ -7,6 +7,6 @@ interface HitProps {
|
|||
children: React.ReactNode;
|
||||
}
|
||||
|
||||
export function Hit({ hit, children }: HitProps) {
|
||||
export function Hit({ hit, children }: HitProps): JSX.Element {
|
||||
return <a href={hit.url}>{children}</a>;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -11,25 +11,18 @@ export type NoResultsScreenTranslations = Partial<{
|
|||
reportMissingResultsLinkText: string;
|
||||
}>;
|
||||
|
||||
type NoResultsScreenProps = Omit<
|
||||
ScreenStateProps<InternalDocSearchHit>,
|
||||
'translations'
|
||||
> & {
|
||||
type NoResultsScreenProps = Omit<ScreenStateProps<InternalDocSearchHit>, 'translations'> & {
|
||||
translations?: NoResultsScreenTranslations;
|
||||
};
|
||||
|
||||
export function NoResultsScreen({
|
||||
translations = {},
|
||||
...props
|
||||
}: NoResultsScreenProps) {
|
||||
export function NoResultsScreen({ translations = {}, ...props }: NoResultsScreenProps): JSX.Element {
|
||||
const {
|
||||
noResultsText = 'No results for',
|
||||
suggestedQueryText = 'Try searching for',
|
||||
reportMissingResultsText = 'Believe this query should return results?',
|
||||
reportMissingResultsLinkText = 'Let us know.',
|
||||
} = translations;
|
||||
const searchSuggestions: string[] | undefined = props.state.context
|
||||
.searchSuggestions as string[];
|
||||
const searchSuggestions: string[] | undefined = props.state.context.searchSuggestions as string[];
|
||||
|
||||
return (
|
||||
<div className="DocSearch-NoResults">
|
||||
|
|
@ -62,7 +55,7 @@ export function NoResultsScreen({
|
|||
</button>
|
||||
</li>,
|
||||
],
|
||||
[]
|
||||
[],
|
||||
)}
|
||||
</ul>
|
||||
</div>
|
||||
|
|
@ -71,11 +64,7 @@ export function NoResultsScreen({
|
|||
{props.getMissingResultsUrl && (
|
||||
<p className="DocSearch-Help">
|
||||
{`${reportMissingResultsText} `}
|
||||
<a
|
||||
href={props.getMissingResultsUrl({ query: props.state.query })}
|
||||
target="_blank"
|
||||
rel="noopener noreferrer"
|
||||
>
|
||||
<a href={props.getMissingResultsUrl({ query: props.state.query })} target="_blank" rel="noopener noreferrer">
|
||||
{reportMissingResultsLinkText}
|
||||
</a>
|
||||
</p>
|
||||
|
|
|
|||
|
|
@ -1,8 +1,4 @@
|
|||
import type {
|
||||
AutocompleteApi,
|
||||
AutocompleteState,
|
||||
BaseItem,
|
||||
} from '@algolia/autocomplete-core';
|
||||
import type { AutocompleteApi, AutocompleteState, BaseItem } from '@algolia/autocomplete-core';
|
||||
import React from 'react';
|
||||
|
||||
import type { DocSearchProps } from './DocSearch';
|
||||
|
|
@ -10,12 +6,7 @@ import { Snippet } from './Snippet';
|
|||
import type { InternalDocSearchHit, StoredDocSearchHit } from './types';
|
||||
|
||||
interface ResultsProps<TItem extends BaseItem>
|
||||
extends AutocompleteApi<
|
||||
TItem,
|
||||
React.FormEvent,
|
||||
React.MouseEvent,
|
||||
React.KeyboardEvent
|
||||
> {
|
||||
extends AutocompleteApi<TItem, React.FormEvent, React.MouseEvent, React.KeyboardEvent> {
|
||||
title: string;
|
||||
collection: AutocompleteState<TItem>['collections'][0];
|
||||
renderIcon: (props: { item: TItem; index: number }) => React.ReactNode;
|
||||
|
|
@ -28,9 +19,7 @@ interface ResultsProps<TItem extends BaseItem>
|
|||
hitComponent: DocSearchProps['hitComponent'];
|
||||
}
|
||||
|
||||
export function Results<TItem extends StoredDocSearchHit>(
|
||||
props: ResultsProps<TItem>
|
||||
) {
|
||||
export function Results<TItem extends StoredDocSearchHit>(props: ResultsProps<TItem>): JSX.Element | null {
|
||||
if (!props.collection || props.collection.items.length === 0) {
|
||||
return null;
|
||||
}
|
||||
|
|
@ -41,14 +30,7 @@ export function Results<TItem extends StoredDocSearchHit>(
|
|||
|
||||
<ul {...props.getListProps()}>
|
||||
{props.collection.items.map((item, index) => {
|
||||
return (
|
||||
<Result
|
||||
key={[props.title, item.objectID].join(':')}
|
||||
item={item}
|
||||
index={index}
|
||||
{...props}
|
||||
/>
|
||||
);
|
||||
return <Result key={[props.title, item.objectID].join(':')} item={item} index={index} {...props} />;
|
||||
})}
|
||||
</ul>
|
||||
</section>
|
||||
|
|
@ -69,18 +51,18 @@ function Result<TItem extends StoredDocSearchHit>({
|
|||
onItemClick,
|
||||
collection,
|
||||
hitComponent,
|
||||
}: ResultProps<TItem>) {
|
||||
}: ResultProps<TItem>): JSX.Element {
|
||||
const [isDeleting, setIsDeleting] = React.useState(false);
|
||||
const [isFavoriting, setIsFavoriting] = React.useState(false);
|
||||
const action = React.useRef<(() => void) | null>(null);
|
||||
const Hit = hitComponent!;
|
||||
|
||||
function runDeleteTransition(cb: () => void) {
|
||||
function runDeleteTransition(cb: () => void): void {
|
||||
setIsDeleting(true);
|
||||
action.current = cb;
|
||||
}
|
||||
|
||||
function runFavoriteTransition(cb: () => void) {
|
||||
function runFavoriteTransition(cb: () => void): void {
|
||||
setIsFavoriting(true);
|
||||
action.current = cb;
|
||||
}
|
||||
|
|
@ -89,8 +71,7 @@ function Result<TItem extends StoredDocSearchHit>({
|
|||
<li
|
||||
className={[
|
||||
'DocSearch-Hit',
|
||||
(item as unknown as InternalDocSearchHit).__docsearch_parent &&
|
||||
'DocSearch-Hit--Child',
|
||||
(item as unknown as InternalDocSearchHit).__docsearch_parent && 'DocSearch-Hit--Child',
|
||||
isDeleting && 'DocSearch-Hit--deleting',
|
||||
isFavoriting && 'DocSearch-Hit--favoriting',
|
||||
]
|
||||
|
|
@ -115,18 +96,8 @@ function Result<TItem extends StoredDocSearchHit>({
|
|||
|
||||
{item.hierarchy[item.type] && item.type === 'lvl1' && (
|
||||
<div className="DocSearch-Hit-content-wrapper">
|
||||
<Snippet
|
||||
className="DocSearch-Hit-title"
|
||||
hit={item}
|
||||
attribute="hierarchy.lvl1"
|
||||
/>
|
||||
{item.content && (
|
||||
<Snippet
|
||||
className="DocSearch-Hit-path"
|
||||
hit={item}
|
||||
attribute="content"
|
||||
/>
|
||||
)}
|
||||
<Snippet className="DocSearch-Hit-title" hit={item} attribute="hierarchy.lvl1" />
|
||||
{item.content && <Snippet className="DocSearch-Hit-path" hit={item} attribute="content" />}
|
||||
</div>
|
||||
)}
|
||||
|
||||
|
|
@ -137,31 +108,15 @@ function Result<TItem extends StoredDocSearchHit>({
|
|||
item.type === 'lvl5' ||
|
||||
item.type === 'lvl6') && (
|
||||
<div className="DocSearch-Hit-content-wrapper">
|
||||
<Snippet
|
||||
className="DocSearch-Hit-title"
|
||||
hit={item}
|
||||
attribute={`hierarchy.${item.type}`}
|
||||
/>
|
||||
<Snippet
|
||||
className="DocSearch-Hit-path"
|
||||
hit={item}
|
||||
attribute="hierarchy.lvl1"
|
||||
/>
|
||||
<Snippet className="DocSearch-Hit-title" hit={item} attribute={`hierarchy.${item.type}`} />
|
||||
<Snippet className="DocSearch-Hit-path" hit={item} attribute="hierarchy.lvl1" />
|
||||
</div>
|
||||
)}
|
||||
|
||||
{item.type === 'content' && (
|
||||
<div className="DocSearch-Hit-content-wrapper">
|
||||
<Snippet
|
||||
className="DocSearch-Hit-title"
|
||||
hit={item}
|
||||
attribute="content"
|
||||
/>
|
||||
<Snippet
|
||||
className="DocSearch-Hit-path"
|
||||
hit={item}
|
||||
attribute="hierarchy.lvl1"
|
||||
/>
|
||||
<Snippet className="DocSearch-Hit-title" hit={item} attribute="content" />
|
||||
<Snippet className="DocSearch-Hit-path" hit={item} attribute="hierarchy.lvl1" />
|
||||
</div>
|
||||
)}
|
||||
|
||||
|
|
|
|||
|
|
@ -6,12 +6,9 @@ import type { ScreenStateProps } from './ScreenState';
|
|||
import type { InternalDocSearchHit } from './types';
|
||||
import { removeHighlightTags } from './utils';
|
||||
|
||||
type ResultsScreenProps = Omit<
|
||||
ScreenStateProps<InternalDocSearchHit>,
|
||||
'translations'
|
||||
>;
|
||||
type ResultsScreenProps = Omit<ScreenStateProps<InternalDocSearchHit>, 'translations'>;
|
||||
|
||||
export function ResultsScreen(props: ResultsScreenProps) {
|
||||
export function ResultsScreen(props: ResultsScreenProps): JSX.Element {
|
||||
return (
|
||||
<div className="DocSearch-Dropdown-Container">
|
||||
{props.state.collections.map((collection) => {
|
||||
|
|
@ -38,8 +35,7 @@ export function ResultsScreen(props: ResultsScreenProps) {
|
|||
strokeLinecap="round"
|
||||
strokeLinejoin="round"
|
||||
>
|
||||
{item.__docsearch_parent !==
|
||||
collection.items[index + 1]?.__docsearch_parent ? (
|
||||
{item.__docsearch_parent !== collection.items[index + 1]?.__docsearch_parent ? (
|
||||
<path d="M8 6v21M20 27H8.3" />
|
||||
) : (
|
||||
<path d="M8 6v42M20 27H8.3" />
|
||||
|
|
|
|||
|
|
@ -1,8 +1,4 @@
|
|||
import type {
|
||||
AutocompleteApi,
|
||||
AutocompleteState,
|
||||
BaseItem,
|
||||
} from '@algolia/autocomplete-core';
|
||||
import type { AutocompleteApi, AutocompleteState, BaseItem } from '@algolia/autocomplete-core';
|
||||
import React from 'react';
|
||||
|
||||
import type { DocSearchProps } from './DocSearch';
|
||||
|
|
@ -23,19 +19,11 @@ export type ScreenStateTranslations = Partial<{
|
|||
}>;
|
||||
|
||||
export interface ScreenStateProps<TItem extends BaseItem>
|
||||
extends AutocompleteApi<
|
||||
TItem,
|
||||
React.FormEvent,
|
||||
React.MouseEvent,
|
||||
React.KeyboardEvent
|
||||
> {
|
||||
extends AutocompleteApi<TItem, React.FormEvent, React.MouseEvent, React.KeyboardEvent> {
|
||||
state: AutocompleteState<TItem>;
|
||||
recentSearches: StoredSearchPlugin<StoredDocSearchHit>;
|
||||
favoriteSearches: StoredSearchPlugin<StoredDocSearchHit>;
|
||||
onItemClick: (
|
||||
item: InternalDocSearchHit,
|
||||
event: KeyboardEvent | MouseEvent
|
||||
) => void;
|
||||
onItemClick: (item: InternalDocSearchHit, event: KeyboardEvent | MouseEvent) => void;
|
||||
inputRef: React.MutableRefObject<HTMLInputElement | null>;
|
||||
hitComponent: DocSearchProps['hitComponent'];
|
||||
indexName: DocSearchProps['indexName'];
|
||||
|
|
@ -51,27 +39,14 @@ export const ScreenState = React.memo(
|
|||
return <ErrorScreen translations={translations?.errorScreen} />;
|
||||
}
|
||||
|
||||
const hasCollections = props.state.collections.some(
|
||||
(collection) => collection.items.length > 0
|
||||
);
|
||||
const hasCollections = props.state.collections.some((collection) => collection.items.length > 0);
|
||||
|
||||
if (!props.state.query) {
|
||||
return (
|
||||
<StartScreen
|
||||
{...props}
|
||||
hasCollections={hasCollections}
|
||||
translations={translations?.startScreen}
|
||||
/>
|
||||
);
|
||||
return <StartScreen {...props} hasCollections={hasCollections} translations={translations?.startScreen} />;
|
||||
}
|
||||
|
||||
if (hasCollections === false) {
|
||||
return (
|
||||
<NoResultsScreen
|
||||
{...props}
|
||||
translations={translations?.noResultsScreen}
|
||||
/>
|
||||
);
|
||||
return <NoResultsScreen {...props} translations={translations?.noResultsScreen} />;
|
||||
}
|
||||
|
||||
return <ResultsScreen {...props} />;
|
||||
|
|
@ -81,9 +56,6 @@ export const ScreenState = React.memo(
|
|||
// avoid UI flashes:
|
||||
// - Empty screen → Results screen
|
||||
// - NoResults screen → NoResults screen with another query
|
||||
return (
|
||||
nextProps.state.status === 'loading' ||
|
||||
nextProps.state.status === 'stalled'
|
||||
);
|
||||
}
|
||||
return nextProps.state.status === 'loading' || nextProps.state.status === 'stalled';
|
||||
},
|
||||
);
|
||||
|
|
|
|||
|
|
@ -1,7 +1,4 @@
|
|||
import type {
|
||||
AutocompleteApi,
|
||||
AutocompleteState,
|
||||
} from '@algolia/autocomplete-core';
|
||||
import type { AutocompleteApi, AutocompleteState } from '@algolia/autocomplete-core';
|
||||
import type { MutableRefObject } from 'react';
|
||||
import React from 'react';
|
||||
|
||||
|
|
@ -20,12 +17,7 @@ export type SearchBoxTranslations = Partial<{
|
|||
}>;
|
||||
|
||||
interface SearchBoxProps
|
||||
extends AutocompleteApi<
|
||||
InternalDocSearchHit,
|
||||
React.FormEvent,
|
||||
React.MouseEvent,
|
||||
React.KeyboardEvent
|
||||
> {
|
||||
extends AutocompleteApi<InternalDocSearchHit, React.FormEvent, React.MouseEvent, React.KeyboardEvent> {
|
||||
state: AutocompleteState<InternalDocSearchHit>;
|
||||
autoFocus: boolean;
|
||||
inputRef: MutableRefObject<HTMLInputElement | null>;
|
||||
|
|
@ -34,7 +26,7 @@ interface SearchBoxProps
|
|||
translations?: SearchBoxTranslations;
|
||||
}
|
||||
|
||||
export function SearchBox({ translations = {}, ...props }: SearchBoxProps) {
|
||||
export function SearchBox({ translations = {}, ...props }: SearchBoxProps): JSX.Element {
|
||||
const {
|
||||
resetButtonTitle = 'Clear the query',
|
||||
resetButtonAriaLabel = 'Clear the query',
|
||||
|
|
@ -69,9 +61,7 @@ export function SearchBox({ translations = {}, ...props }: SearchBoxProps) {
|
|||
>
|
||||
<label className="DocSearch-MagnifierLabel" {...props.getLabelProps()}>
|
||||
<SearchIcon />
|
||||
<span className="DocSearch-VisuallyHiddenForAccessibility">
|
||||
{searchInputLabel}
|
||||
</span>
|
||||
<span className="DocSearch-VisuallyHiddenForAccessibility">{searchInputLabel}</span>
|
||||
</label>
|
||||
|
||||
<div className="DocSearch-LoadingIndicator">
|
||||
|
|
@ -99,12 +89,7 @@ export function SearchBox({ translations = {}, ...props }: SearchBoxProps) {
|
|||
</button>
|
||||
</form>
|
||||
|
||||
<button
|
||||
className="DocSearch-Cancel"
|
||||
type="reset"
|
||||
aria-label={cancelButtonAriaLabel}
|
||||
onClick={props.onClose}
|
||||
>
|
||||
<button className="DocSearch-Cancel" type="reset" aria-label={cancelButtonAriaLabel} onClick={props.onClose}>
|
||||
{cancelButtonText}
|
||||
</button>
|
||||
</>
|
||||
|
|
|
|||
|
|
@ -23,13 +23,11 @@ export function Snippet<TItem extends StoredDocSearchHit>({
|
|||
attribute,
|
||||
tagName = 'span',
|
||||
...rest
|
||||
}: SnippetProps<TItem>) {
|
||||
}: SnippetProps<TItem>): JSX.Element {
|
||||
return createElement(tagName, {
|
||||
...rest,
|
||||
dangerouslySetInnerHTML: {
|
||||
__html:
|
||||
getPropertyByPath(hit, `_snippetResult.${attribute}.value`) ||
|
||||
getPropertyByPath(hit, attribute),
|
||||
__html: getPropertyByPath(hit, `_snippetResult.${attribute}.value`) || getPropertyByPath(hit, attribute),
|
||||
},
|
||||
});
|
||||
}
|
||||
|
|
|
|||
|
|
@ -14,15 +14,12 @@ export type StartScreenTranslations = Partial<{
|
|||
removeFavoriteSearchButtonTitle: string;
|
||||
}>;
|
||||
|
||||
type StartScreenProps = Omit<
|
||||
ScreenStateProps<InternalDocSearchHit>,
|
||||
'translations'
|
||||
> & {
|
||||
type StartScreenProps = Omit<ScreenStateProps<InternalDocSearchHit>, 'translations'> & {
|
||||
hasCollections: boolean;
|
||||
translations?: StartScreenTranslations;
|
||||
};
|
||||
|
||||
export function StartScreen({ translations = {}, ...props }: StartScreenProps) {
|
||||
export function StartScreen({ translations = {}, ...props }: StartScreenProps): JSX.Element | null {
|
||||
const {
|
||||
recentSearchesTitle = 'Recent',
|
||||
noRecentSearchesText = 'No recent searches',
|
||||
|
|
@ -58,11 +55,7 @@ export function StartScreen({ translations = {}, ...props }: StartScreenProps) {
|
|||
<RecentIcon />
|
||||
</div>
|
||||
)}
|
||||
renderAction={({
|
||||
item,
|
||||
runFavoriteTransition,
|
||||
runDeleteTransition,
|
||||
}) => (
|
||||
renderAction={({ item, runFavoriteTransition, runDeleteTransition }) => (
|
||||
<>
|
||||
<div className="DocSearch-Hit-action">
|
||||
<button
|
||||
|
|
|
|||
|
|
@ -1,15 +1,14 @@
|
|||
import { render, act, fireEvent, screen } from '@testing-library/react';
|
||||
import { render, act, fireEvent, screen, cleanup } from '@testing-library/react';
|
||||
import React from 'react';
|
||||
import { describe, it, expect, afterEach } from 'vitest';
|
||||
|
||||
import '@testing-library/jest-dom';
|
||||
import '@testing-library/jest-dom/vitest';
|
||||
|
||||
import { DocSearch as DocSearchComponent } from '../DocSearch';
|
||||
import type { DocSearchProps } from '../DocSearch';
|
||||
|
||||
function DocSearch(props: Partial<DocSearchProps>) {
|
||||
return (
|
||||
<DocSearchComponent appId="woo" apiKey="foo" indexName="bar" {...props} />
|
||||
);
|
||||
function DocSearch(props: Partial<DocSearchProps>): JSX.Element {
|
||||
return <DocSearchComponent appId="woo" apiKey="foo" indexName="bar" {...props} />;
|
||||
}
|
||||
|
||||
// mock empty response
|
||||
|
|
@ -36,6 +35,10 @@ function noResultSearch(_queries: any, _requestOptions?: any): Promise<any> {
|
|||
describe('api', () => {
|
||||
const docSearchSelector = '.DocSearch';
|
||||
|
||||
afterEach(() => {
|
||||
cleanup();
|
||||
});
|
||||
|
||||
it('renders with minimal parameters', () => {
|
||||
render(<DocSearch />);
|
||||
|
||||
|
|
@ -52,15 +55,11 @@ describe('api', () => {
|
|||
buttonAriaLabel: 'Recherche',
|
||||
},
|
||||
}}
|
||||
/>
|
||||
/>,
|
||||
);
|
||||
expect(document.querySelector(docSearchSelector)).toBeInTheDocument();
|
||||
expect(
|
||||
document.querySelector('.DocSearch-Button-Placeholder')?.innerHTML
|
||||
).toBe('Recherche');
|
||||
expect(
|
||||
document.querySelector('.DocSearch-Button')?.getAttribute('aria-label')
|
||||
).toBe('Recherche');
|
||||
expect(document.querySelector('.DocSearch-Button-Placeholder')?.innerHTML).toBe('Recherche');
|
||||
expect(document.querySelector('.DocSearch-Button')?.getAttribute('aria-label')).toBe('Recherche');
|
||||
});
|
||||
|
||||
it('overrides the default DocSearchModal startScreen text', async () => {
|
||||
|
|
@ -73,7 +72,7 @@ describe('api', () => {
|
|||
},
|
||||
},
|
||||
}}
|
||||
/>
|
||||
/>,
|
||||
);
|
||||
|
||||
expect(document.querySelector(docSearchSelector)).toBeInTheDocument();
|
||||
|
|
@ -99,14 +98,13 @@ describe('api', () => {
|
|||
modal: {
|
||||
noResultsScreen: {
|
||||
noResultsText: 'Pas de résultats pour',
|
||||
reportMissingResultsText:
|
||||
'Ouvrez une issue sur docsearch-configs',
|
||||
reportMissingResultsText: 'Ouvrez une issue sur docsearch-configs',
|
||||
reportMissingResultsLinkText: 'Lien du repo',
|
||||
},
|
||||
},
|
||||
}}
|
||||
getMissingResultsUrl={() => 'algolia.com'}
|
||||
/>
|
||||
/>,
|
||||
);
|
||||
|
||||
expect(document.querySelector(docSearchSelector)).toBeInTheDocument();
|
||||
|
|
@ -122,13 +120,11 @@ describe('api', () => {
|
|||
});
|
||||
|
||||
expect(screen.getByText(/Pas de résultats pour/)).toBeInTheDocument();
|
||||
expect(
|
||||
screen.getByText(/Ouvrez une issue sur docsearch-configs/)
|
||||
).toBeInTheDocument();
|
||||
expect(screen.getByText(/Ouvrez une issue sur docsearch-configs/)).toBeInTheDocument();
|
||||
expect(
|
||||
screen.getByRole('link', {
|
||||
name: 'Lien du repo',
|
||||
})
|
||||
}),
|
||||
).toBeInTheDocument();
|
||||
});
|
||||
|
||||
|
|
@ -146,31 +142,21 @@ describe('api', () => {
|
|||
},
|
||||
},
|
||||
}}
|
||||
/>
|
||||
/>,
|
||||
);
|
||||
|
||||
await act(async () => {
|
||||
fireEvent.click(await screen.findByText('Search'));
|
||||
});
|
||||
|
||||
const searchInputLabel = document.querySelector(
|
||||
'.DocSearch-MagnifierLabel'
|
||||
);
|
||||
const searchInputLabel = document.querySelector('.DocSearch-MagnifierLabel');
|
||||
|
||||
expect(document.querySelector(docSearchSelector)).toBeInTheDocument();
|
||||
|
||||
expect(document.querySelector('.DocSearch-Cancel')?.innerHTML).toBe(
|
||||
'Annuler'
|
||||
);
|
||||
expect(
|
||||
document.querySelector('.DocSearch-Cancel')?.getAttribute('aria-label')
|
||||
).toBe('Annuler');
|
||||
expect(
|
||||
document.querySelector('.DocSearch-Reset')?.getAttribute('title')
|
||||
).toBe('Effacer');
|
||||
expect(
|
||||
document.querySelector('.DocSearch-Reset')?.getAttribute('aria-label')
|
||||
).toBe('Effacer');
|
||||
expect(document.querySelector('.DocSearch-Cancel')?.innerHTML).toBe('Annuler');
|
||||
expect(document.querySelector('.DocSearch-Cancel')?.getAttribute('aria-label')).toBe('Annuler');
|
||||
expect(document.querySelector('.DocSearch-Reset')?.getAttribute('title')).toBe('Effacer');
|
||||
expect(document.querySelector('.DocSearch-Reset')?.getAttribute('aria-label')).toBe('Effacer');
|
||||
expect(searchInputLabel?.textContent).toBe('Recherche');
|
||||
});
|
||||
|
||||
|
|
@ -191,7 +177,7 @@ describe('api', () => {
|
|||
},
|
||||
},
|
||||
}}
|
||||
/>
|
||||
/>,
|
||||
);
|
||||
|
||||
expect(document.querySelector(docSearchSelector)).toBeInTheDocument();
|
||||
|
|
@ -205,24 +191,14 @@ describe('api', () => {
|
|||
expect(screen.getByText('Pour naviguer')).toBeInTheDocument();
|
||||
expect(screen.getByText('Pour selectionner')).toBeInTheDocument();
|
||||
expect(
|
||||
document.querySelector(
|
||||
'.DocSearch-Commands-Key > svg[aria-label="Touche d\'échappement"]'
|
||||
)
|
||||
document.querySelector('.DocSearch-Commands-Key > svg[aria-label="Touche d\'échappement"]'),
|
||||
).toBeInTheDocument();
|
||||
expect(
|
||||
document.querySelector(
|
||||
'.DocSearch-Commands-Key > svg[aria-label="Flèche vers le haut"]'
|
||||
)
|
||||
document.querySelector('.DocSearch-Commands-Key > svg[aria-label="Flèche vers le haut"]'),
|
||||
).toBeInTheDocument();
|
||||
expect(document.querySelector('.DocSearch-Commands-Key > svg[aria-label="Flèche le bas"]')).toBeInTheDocument();
|
||||
expect(
|
||||
document.querySelector(
|
||||
'.DocSearch-Commands-Key > svg[aria-label="Flèche le bas"]'
|
||||
)
|
||||
).toBeInTheDocument();
|
||||
expect(
|
||||
document.querySelector(
|
||||
'.DocSearch-Commands-Key > svg[aria-label="Touche d\'entrée"]'
|
||||
)
|
||||
document.querySelector('.DocSearch-Commands-Key > svg[aria-label="Touche d\'entrée"]'),
|
||||
).toBeInTheDocument();
|
||||
});
|
||||
});
|
||||
|
|
@ -237,7 +213,7 @@ describe('api', () => {
|
|||
search: noResultSearch,
|
||||
};
|
||||
}}
|
||||
/>
|
||||
/>,
|
||||
);
|
||||
|
||||
expect(document.querySelector(docSearchSelector)).toBeInTheDocument();
|
||||
|
|
@ -253,9 +229,7 @@ describe('api', () => {
|
|||
});
|
||||
|
||||
expect(screen.getByText(/No results for/)).toBeInTheDocument();
|
||||
expect(
|
||||
document.querySelector('.DocSearch-Help a')
|
||||
).not.toBeInTheDocument();
|
||||
expect(document.querySelector('.DocSearch-Help a')).not.toBeInTheDocument();
|
||||
});
|
||||
|
||||
it('render the link to the repository', async () => {
|
||||
|
|
@ -267,10 +241,8 @@ describe('api', () => {
|
|||
search: noResultSearch,
|
||||
};
|
||||
}}
|
||||
getMissingResultsUrl={({ query }) =>
|
||||
`https://github.com/algolia/docsearch/issues/new?title=${query}`
|
||||
}
|
||||
/>
|
||||
getMissingResultsUrl={({ query }) => `https://github.com/algolia/docsearch/issues/new?title=${query}`}
|
||||
/>,
|
||||
);
|
||||
|
||||
await act(async () => {
|
||||
|
|
@ -286,9 +258,7 @@ describe('api', () => {
|
|||
expect(screen.getByText(/No results for/)).toBeInTheDocument();
|
||||
const link = document.querySelector('.DocSearch-Help a');
|
||||
expect(link).toBeInTheDocument();
|
||||
expect(link?.getAttribute('href')).toBe(
|
||||
'https://github.com/algolia/docsearch/issues/new?title=q'
|
||||
);
|
||||
expect(link?.getAttribute('href')).toBe('https://github.com/algolia/docsearch/issues/new?title=q');
|
||||
});
|
||||
});
|
||||
});
|
||||
|
|
|
|||
|
|
@ -1,6 +1,6 @@
|
|||
import React from 'react';
|
||||
|
||||
export function ControlKeyIcon() {
|
||||
export function ControlKeyIcon(): JSX.Element {
|
||||
return (
|
||||
<svg width="15" height="15" className="DocSearch-Control-Key-Icon">
|
||||
<path
|
||||
|
|
|
|||
|
|
@ -1,6 +1,6 @@
|
|||
import React from 'react';
|
||||
|
||||
export function ErrorIcon() {
|
||||
export function ErrorIcon(): JSX.Element {
|
||||
return (
|
||||
<svg
|
||||
width="40"
|
||||
|
|
|
|||
|
|
@ -1,6 +1,6 @@
|
|||
import React from 'react';
|
||||
|
||||
export function GoToExternal() {
|
||||
export function GoToExternal(): JSX.Element {
|
||||
return (
|
||||
<svg width="20" height="20">
|
||||
<path
|
||||
|
|
|
|||
|
|
@ -1,6 +1,6 @@
|
|||
import React from 'react';
|
||||
|
||||
export function LoadingIcon() {
|
||||
export function LoadingIcon(): JSX.Element {
|
||||
return (
|
||||
<svg viewBox="0 0 38 38" stroke="currentColor" strokeOpacity=".5">
|
||||
<g fill="none" fillRule="evenodd">
|
||||
|
|
|
|||
|
|
@ -1,6 +1,6 @@
|
|||
import React from 'react';
|
||||
|
||||
export function NoResultsIcon() {
|
||||
export function NoResultsIcon(): JSX.Element {
|
||||
return (
|
||||
<svg
|
||||
width="40"
|
||||
|
|
|
|||
|
|
@ -1,15 +1,9 @@
|
|||
import React from 'react';
|
||||
|
||||
export function RecentIcon() {
|
||||
export function RecentIcon(): JSX.Element {
|
||||
return (
|
||||
<svg width="20" height="20" viewBox="0 0 20 20">
|
||||
<g
|
||||
stroke="currentColor"
|
||||
fill="none"
|
||||
fillRule="evenodd"
|
||||
strokeLinecap="round"
|
||||
strokeLinejoin="round"
|
||||
>
|
||||
<g stroke="currentColor" fill="none" fillRule="evenodd" strokeLinecap="round" strokeLinejoin="round">
|
||||
<path d="M3.18 6.6a8.23 8.23 0 1112.93 9.94h0a8.23 8.23 0 01-11.63 0" />
|
||||
<path d="M6.44 7.25H2.55V3.36M10.45 6v5.6M10.45 11.6L13 13" />
|
||||
</g>
|
||||
|
|
|
|||
|
|
@ -1,6 +1,6 @@
|
|||
import React from 'react';
|
||||
|
||||
export function ResetIcon() {
|
||||
export function ResetIcon(): JSX.Element {
|
||||
return (
|
||||
<svg width="20" height="20" viewBox="0 0 20 20">
|
||||
<path
|
||||
|
|
|
|||
|
|
@ -1,14 +1,8 @@
|
|||
import React from 'react';
|
||||
|
||||
export function SearchIcon() {
|
||||
export function SearchIcon(): JSX.Element {
|
||||
return (
|
||||
<svg
|
||||
width="20"
|
||||
height="20"
|
||||
className="DocSearch-Search-Icon"
|
||||
viewBox="0 0 20 20"
|
||||
aria-hidden="true"
|
||||
>
|
||||
<svg width="20" height="20" className="DocSearch-Search-Icon" viewBox="0 0 20 20" aria-hidden="true">
|
||||
<path
|
||||
d="M14.386 14.386l4.0877 4.0877-4.0877-4.0877c-2.9418 2.9419-7.7115 2.9419-10.6533 0-2.9419-2.9418-2.9419-7.7115 0-10.6533 2.9418-2.9419 7.7115-2.9419 10.6533 0 2.9419 2.9418 2.9419 7.7115 0 10.6533z"
|
||||
stroke="currentColor"
|
||||
|
|
|
|||
|
|
@ -1,20 +1,9 @@
|
|||
import React from 'react';
|
||||
|
||||
export function SelectIcon() {
|
||||
export function SelectIcon(): JSX.Element {
|
||||
return (
|
||||
<svg
|
||||
className="DocSearch-Hit-Select-Icon"
|
||||
width="20"
|
||||
height="20"
|
||||
viewBox="0 0 20 20"
|
||||
>
|
||||
<g
|
||||
stroke="currentColor"
|
||||
fill="none"
|
||||
fillRule="evenodd"
|
||||
strokeLinecap="round"
|
||||
strokeLinejoin="round"
|
||||
>
|
||||
<svg className="DocSearch-Hit-Select-Icon" width="20" height="20" viewBox="0 0 20 20">
|
||||
<g stroke="currentColor" fill="none" fillRule="evenodd" strokeLinecap="round" strokeLinejoin="round">
|
||||
<path d="M18 3v4c0 2-2 4-4 4H2" />
|
||||
<path d="M8 17l-6-6 6-6" />
|
||||
</g>
|
||||
|
|
|
|||
|
|
@ -14,7 +14,7 @@ const LvlIcon: React.FC = () => {
|
|||
);
|
||||
};
|
||||
|
||||
export function SourceIcon(props: { type: string }) {
|
||||
export function SourceIcon(props: { type: string }): JSX.Element {
|
||||
switch (props.type) {
|
||||
case 'lvl1':
|
||||
return <LvlIcon />;
|
||||
|
|
@ -25,7 +25,7 @@ export function SourceIcon(props: { type: string }) {
|
|||
}
|
||||
}
|
||||
|
||||
function AnchorIcon() {
|
||||
function AnchorIcon(): JSX.Element {
|
||||
return (
|
||||
<svg width="20" height="20" viewBox="0 0 20 20">
|
||||
<path
|
||||
|
|
@ -40,7 +40,7 @@ function AnchorIcon() {
|
|||
);
|
||||
}
|
||||
|
||||
function ContentIcon() {
|
||||
function ContentIcon(): JSX.Element {
|
||||
return (
|
||||
<svg width="20" height="20" viewBox="0 0 20 20">
|
||||
<path
|
||||
|
|
|
|||
|
|
@ -1,6 +1,6 @@
|
|||
import React from 'react';
|
||||
|
||||
export function StarIcon() {
|
||||
export function StarIcon(): JSX.Element {
|
||||
return (
|
||||
<svg width="20" height="20" viewBox="0 0 20 20">
|
||||
<path
|
||||
|
|
|
|||
|
|
@ -1,6 +1,6 @@
|
|||
import type { DocSearchHit, StoredDocSearchHit } from './types';
|
||||
|
||||
function isLocalStorageSupported() {
|
||||
function isLocalStorageSupported(): boolean {
|
||||
const key = '__TEST_KEY__';
|
||||
|
||||
try {
|
||||
|
|
@ -8,23 +8,24 @@ function isLocalStorageSupported() {
|
|||
localStorage.removeItem(key);
|
||||
|
||||
return true;
|
||||
} catch (error) {
|
||||
} catch {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
// eslint-disable-next-line @typescript-eslint/explicit-function-return-type
|
||||
function createStorage<TItem>(key: string) {
|
||||
if (isLocalStorageSupported() === false) {
|
||||
return {
|
||||
setItem() {},
|
||||
getItem() {
|
||||
setItem(): void {},
|
||||
getItem(): TItem[] {
|
||||
return [];
|
||||
},
|
||||
};
|
||||
}
|
||||
|
||||
return {
|
||||
setItem(item: TItem[]) {
|
||||
setItem(item: TItem[]): void {
|
||||
return window.localStorage.setItem(key, JSON.stringify(item));
|
||||
},
|
||||
getItem(): TItem[] {
|
||||
|
|
@ -54,13 +55,10 @@ export function createStoredSearches<TItem extends StoredDocSearchHit>({
|
|||
let items = storage.getItem().slice(0, limit);
|
||||
|
||||
return {
|
||||
add(item: TItem) {
|
||||
const { _highlightResult, _snippetResult, ...hit } =
|
||||
item as unknown as DocSearchHit;
|
||||
add(item: TItem): void {
|
||||
const { _highlightResult, _snippetResult, ...hit } = item as unknown as DocSearchHit;
|
||||
|
||||
const isQueryAlreadySaved = items.findIndex(
|
||||
(x) => x.objectID === hit.objectID
|
||||
);
|
||||
const isQueryAlreadySaved = items.findIndex((x) => x.objectID === hit.objectID);
|
||||
|
||||
if (isQueryAlreadySaved > -1) {
|
||||
items.splice(isQueryAlreadySaved, 1);
|
||||
|
|
@ -71,12 +69,12 @@ export function createStoredSearches<TItem extends StoredDocSearchHit>({
|
|||
|
||||
storage.setItem(items);
|
||||
},
|
||||
remove(item: TItem) {
|
||||
remove(item: TItem): void {
|
||||
items = items.filter((x) => x.objectID !== item.objectID);
|
||||
|
||||
storage.setItem(items);
|
||||
},
|
||||
getAll() {
|
||||
getAll(): TItem[] {
|
||||
return items;
|
||||
},
|
||||
};
|
||||
|
|
|
|||
|
|
@ -1,12 +1,4 @@
|
|||
type ContentType =
|
||||
| 'content'
|
||||
| 'lvl0'
|
||||
| 'lvl1'
|
||||
| 'lvl2'
|
||||
| 'lvl3'
|
||||
| 'lvl4'
|
||||
| 'lvl5'
|
||||
| 'lvl6';
|
||||
type ContentType = 'content' | 'lvl0' | 'lvl1' | 'lvl2' | 'lvl3' | 'lvl4' | 'lvl5' | 'lvl6';
|
||||
|
||||
interface DocSearchHitAttributeHighlightResult {
|
||||
value: string;
|
||||
|
|
|
|||
|
|
@ -11,7 +11,6 @@ interface DocSearchContext extends AutocompleteContext {
|
|||
};
|
||||
}
|
||||
|
||||
export interface DocSearchState<TItem extends BaseItem>
|
||||
extends AutocompleteState<TItem> {
|
||||
export interface DocSearchState<TItem extends BaseItem> extends AutocompleteState<TItem> {
|
||||
context: DocSearchContext;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,6 +1,3 @@
|
|||
import type { DocSearchHit } from './DocSearchHit';
|
||||
|
||||
export type StoredDocSearchHit = Omit<
|
||||
DocSearchHit,
|
||||
'_highlightResult' | '_snippetResult'
|
||||
>;
|
||||
export type StoredDocSearchHit = Omit<DocSearchHit, '_highlightResult' | '_snippetResult'>;
|
||||
|
|
|
|||
|
|
@ -12,12 +12,7 @@ function isEditingContent(event: KeyboardEvent): boolean {
|
|||
const element = event.target as HTMLElement;
|
||||
const tagName = element.tagName;
|
||||
|
||||
return (
|
||||
element.isContentEditable ||
|
||||
tagName === 'INPUT' ||
|
||||
tagName === 'SELECT' ||
|
||||
tagName === 'TEXTAREA'
|
||||
);
|
||||
return element.isContentEditable || tagName === 'INPUT' || tagName === 'SELECT' || tagName === 'TEXTAREA';
|
||||
}
|
||||
|
||||
export function useDocSearchKeyboardEvents({
|
||||
|
|
@ -26,17 +21,16 @@ export function useDocSearchKeyboardEvents({
|
|||
onClose,
|
||||
onInput,
|
||||
searchButtonRef,
|
||||
}: UseDocSearchKeyboardEventsProps) {
|
||||
}: UseDocSearchKeyboardEventsProps): void {
|
||||
React.useEffect(() => {
|
||||
function onKeyDown(event: KeyboardEvent) {
|
||||
function onKeyDown(event: KeyboardEvent): void {
|
||||
if (
|
||||
(event.code === 'Escape' && isOpen) ||
|
||||
// The `Cmd+K` shortcut both opens and closes the modal.
|
||||
// We need to check for `event.key` because it can be `undefined` with
|
||||
// Chrome's autofill feature.
|
||||
// See https://github.com/paperjs/paper.js/issues/1398
|
||||
(event.key?.toLowerCase() === 'k' &&
|
||||
(event.metaKey || event.ctrlKey)) ||
|
||||
(event.key?.toLowerCase() === 'k' && (event.metaKey || event.ctrlKey)) ||
|
||||
// The `/` shortcut opens but doesn't close the modal because it's
|
||||
// a character.
|
||||
(!isEditingContent(event) && event.key === '/' && !isOpen)
|
||||
|
|
@ -54,11 +48,7 @@ export function useDocSearchKeyboardEvents({
|
|||
return;
|
||||
}
|
||||
|
||||
if (
|
||||
searchButtonRef &&
|
||||
searchButtonRef.current === document.activeElement &&
|
||||
onInput
|
||||
) {
|
||||
if (searchButtonRef && searchButtonRef.current === document.activeElement && onInput) {
|
||||
if (/[a-zA-Z0-9]/.test(String.fromCharCode(event.keyCode))) {
|
||||
onInput(event);
|
||||
}
|
||||
|
|
@ -67,7 +57,7 @@ export function useDocSearchKeyboardEvents({
|
|||
|
||||
window.addEventListener('keydown', onKeyDown);
|
||||
|
||||
return () => {
|
||||
return (): void => {
|
||||
window.removeEventListener('keydown', onKeyDown);
|
||||
};
|
||||
}, [isOpen, onOpen, onClose, onInput, searchButtonRef]);
|
||||
|
|
|
|||
|
|
@ -7,7 +7,7 @@ import { version } from './version';
|
|||
export function useSearchClient(
|
||||
appId: string,
|
||||
apiKey: string,
|
||||
transformSearchClient: (searchClient: LiteClient) => LiteClient
|
||||
transformSearchClient: (searchClient: LiteClient) => LiteClient,
|
||||
): LiteClient {
|
||||
const searchClient = React.useMemo(() => {
|
||||
const client = liteClient(appId, apiKey);
|
||||
|
|
@ -18,10 +18,7 @@ export function useSearchClient(
|
|||
// it would also be sent on a DocSearch.js integration.
|
||||
// We therefore only add the `docsearch-react` user agent if `docsearch.js`
|
||||
// is not present.
|
||||
if (
|
||||
/docsearch.js \(.*\)/.test(client.transporter.algoliaAgent.value) ===
|
||||
false
|
||||
) {
|
||||
if (/docsearch.js \(.*\)/.test(client.transporter.algoliaAgent.value) === false) {
|
||||
client.addAlgoliaAgent('docsearch-react', version);
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -13,7 +13,7 @@ export function useTouchEvents({
|
|||
panelElement,
|
||||
formElement,
|
||||
inputElement,
|
||||
}: UseTouchEventsProps) {
|
||||
}: UseTouchEventsProps): void {
|
||||
React.useEffect(() => {
|
||||
if (!(panelElement && formElement && inputElement)) {
|
||||
return undefined;
|
||||
|
|
@ -28,7 +28,7 @@ export function useTouchEvents({
|
|||
window.addEventListener('touchstart', onTouchStart);
|
||||
window.addEventListener('touchmove', onTouchMove);
|
||||
|
||||
return () => {
|
||||
return (): void => {
|
||||
window.removeEventListener('touchstart', onTouchStart);
|
||||
window.removeEventListener('touchmove', onTouchMove);
|
||||
};
|
||||
|
|
|
|||
|
|
@ -4,19 +4,19 @@ interface UseTrapFocusProps {
|
|||
container: HTMLElement | null;
|
||||
}
|
||||
|
||||
export function useTrapFocus({ container }: UseTrapFocusProps) {
|
||||
export function useTrapFocus({ container }: UseTrapFocusProps): void {
|
||||
React.useEffect(() => {
|
||||
if (!container) {
|
||||
return undefined;
|
||||
}
|
||||
|
||||
const focusableElements = container.querySelectorAll<HTMLElement>(
|
||||
'a[href]:not([disabled]), button:not([disabled]), input:not([disabled])'
|
||||
'a[href]:not([disabled]), button:not([disabled]), input:not([disabled])',
|
||||
);
|
||||
const firstElement = focusableElements[0];
|
||||
const lastElement = focusableElements[focusableElements.length - 1];
|
||||
|
||||
function trapFocus(event: KeyboardEvent) {
|
||||
function trapFocus(event: KeyboardEvent): void {
|
||||
if (event.key !== 'Tab') {
|
||||
return;
|
||||
}
|
||||
|
|
@ -34,7 +34,7 @@ export function useTrapFocus({ container }: UseTrapFocusProps) {
|
|||
|
||||
container.addEventListener('keydown', trapFocus);
|
||||
|
||||
return () => {
|
||||
return (): void => {
|
||||
container.removeEventListener('keydown', trapFocus);
|
||||
};
|
||||
}, [container]);
|
||||
|
|
|
|||
|
|
@ -1,7 +1,7 @@
|
|||
export function groupBy<TValue extends Record<string, unknown>>(
|
||||
values: TValue[],
|
||||
predicate: (value: TValue) => string,
|
||||
maxResultsPerGroup?: number
|
||||
maxResultsPerGroup?: number,
|
||||
): Record<string, TValue[]> {
|
||||
return values.reduce<Record<string, TValue[]>>((acc, item) => {
|
||||
const key = predicate(item);
|
||||
|
|
|
|||
|
|
@ -2,16 +2,8 @@
|
|||
* Detect when an event is modified with a special key to let the browser
|
||||
* trigger its default behavior.
|
||||
*/
|
||||
export function isModifierEvent<TEvent extends KeyboardEvent | MouseEvent>(
|
||||
event: TEvent
|
||||
): boolean {
|
||||
export function isModifierEvent<TEvent extends KeyboardEvent | MouseEvent>(event: TEvent): boolean {
|
||||
const isMiddleClick = (event as MouseEvent).button === 1;
|
||||
|
||||
return (
|
||||
isMiddleClick ||
|
||||
event.altKey ||
|
||||
event.ctrlKey ||
|
||||
event.metaKey ||
|
||||
event.shiftKey
|
||||
);
|
||||
return isMiddleClick || event.altKey || event.ctrlKey || event.metaKey || event.shiftKey;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -3,9 +3,7 @@ import type { DocSearchHit, InternalDocSearchHit } from '../types';
|
|||
const regexHighlightTags = /(<mark>|<\/mark>)/g;
|
||||
const regexHasHighlightTags = RegExp(regexHighlightTags.source);
|
||||
|
||||
export function removeHighlightTags(
|
||||
hit: DocSearchHit | InternalDocSearchHit
|
||||
): string {
|
||||
export function removeHighlightTags(hit: DocSearchHit | InternalDocSearchHit): string {
|
||||
const internalDocSearchHit = hit as InternalDocSearchHit;
|
||||
|
||||
if (!internalDocSearchHit.__docsearch_parent && !hit._highlightResult) {
|
||||
|
|
@ -20,7 +18,5 @@ export function removeHighlightTags(
|
|||
return hit.hierarchy.lvl0;
|
||||
}
|
||||
|
||||
return lvl0.value && regexHasHighlightTags.test(lvl0.value)
|
||||
? lvl0.value.replace(regexHighlightTags, '')
|
||||
: lvl0.value;
|
||||
return lvl0.value && regexHasHighlightTags.test(lvl0.value) ? lvl0.value.replace(regexHighlightTags, '') : lvl0.value;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,3 +1,3 @@
|
|||
module.exports = {
|
||||
export default {
|
||||
presets: [require.resolve('@docusaurus/core/lib/babel/preset')],
|
||||
};
|
||||
|
|
@ -1,178 +0,0 @@
|
|||
/* eslint-disable @typescript-eslint/no-var-requires */
|
||||
const { themes } = require('prism-react-renderer');
|
||||
|
||||
// With JSDoc @type annotations, IDEs can provide config autocompletion
|
||||
/** @type {import('@docusaurus/types').DocusaurusConfig} */
|
||||
(
|
||||
module.exports = {
|
||||
title: 'DocSearch by Algolia',
|
||||
tagline:
|
||||
'The best search experience for docs, integrated in minutes, for free.',
|
||||
url: 'https://docsearch.algolia.com',
|
||||
baseUrl: '/',
|
||||
favicon: 'img/favicon.ico',
|
||||
organizationName: 'Algolia',
|
||||
projectName: 'DocSearch',
|
||||
onBrokenLinks: 'throw',
|
||||
onBrokenMarkdownLinks: 'throw',
|
||||
presets: [
|
||||
[
|
||||
'@docusaurus/preset-classic',
|
||||
/** @type {import('@docusaurus/preset-classic').Options} */
|
||||
({
|
||||
docs: {
|
||||
path: 'docs',
|
||||
sidebarPath: 'sidebars.js',
|
||||
editUrl:
|
||||
'https://github.com/algolia/docsearch/edit/main/packages/website/',
|
||||
versions: {
|
||||
current: {
|
||||
label: 'current',
|
||||
},
|
||||
},
|
||||
lastVersion: 'current',
|
||||
showLastUpdateAuthor: true,
|
||||
showLastUpdateTime: true,
|
||||
},
|
||||
theme: {
|
||||
customCss: require.resolve('./src/css/custom.css'),
|
||||
},
|
||||
}),
|
||||
],
|
||||
],
|
||||
plugins: ['my-loaders', 'tailwind-loader'],
|
||||
themeConfig:
|
||||
/** @type {import('@docusaurus/preset-classic').ThemeConfig} */
|
||||
({
|
||||
algolia: {
|
||||
appId: 'R2IYF7ETH7',
|
||||
apiKey: '599cec31baffa4868cae4e79f180729b',
|
||||
indexName: 'docsearch',
|
||||
contextualSearch: true,
|
||||
},
|
||||
metadata: [
|
||||
{
|
||||
name: 'google-site-verification',
|
||||
content: '23yIHmCD_xnJb_6e3s-w7M29Kydahk-d86ObMWOrvRg',
|
||||
},
|
||||
],
|
||||
navbar: {
|
||||
hideOnScroll: true,
|
||||
logo: {
|
||||
alt: 'DocSearch x Algolia',
|
||||
src: 'img/docsearch-x-algolia-logo-light-mode.png',
|
||||
srcDark: 'img/docsearch-x-algolia-logo-dark-mode.png',
|
||||
className: 'docsearch-nav-logo',
|
||||
},
|
||||
items: [
|
||||
// left
|
||||
{
|
||||
label: 'Docs',
|
||||
to: 'docs/what-is-docsearch',
|
||||
position: 'left',
|
||||
},
|
||||
{
|
||||
label: 'Apply',
|
||||
to: 'apply',
|
||||
position: 'left',
|
||||
},
|
||||
// right
|
||||
{
|
||||
type: 'docsVersionDropdown',
|
||||
position: 'right',
|
||||
},
|
||||
{
|
||||
href: 'https://github.com/algolia/docsearch',
|
||||
position: 'right',
|
||||
className: 'header-github-link',
|
||||
},
|
||||
],
|
||||
},
|
||||
colorMode: {
|
||||
defaultMode: 'light',
|
||||
disableSwitch: false,
|
||||
respectPrefersColorScheme: true,
|
||||
},
|
||||
announcementBar: {
|
||||
content:
|
||||
'⭐️ If you like DocSearch, give it a star on <a target="_blank" rel="noopener noreferrer" href="https://github.com/algolia/docsearch">GitHub</a>! ⭐️',
|
||||
},
|
||||
footer: {
|
||||
links: [
|
||||
{
|
||||
title: 'Docs',
|
||||
items: [
|
||||
{
|
||||
label: 'Getting Started',
|
||||
to: 'docs/what-is-docsearch',
|
||||
},
|
||||
{
|
||||
label: 'FAQ',
|
||||
to: 'docs/docsearch-program',
|
||||
},
|
||||
{
|
||||
label: 'DocSearch v3',
|
||||
to: 'docs/docsearch-v3',
|
||||
},
|
||||
],
|
||||
},
|
||||
{
|
||||
title: 'DocSearch',
|
||||
items: [
|
||||
{
|
||||
label: 'Apply',
|
||||
to: 'apply',
|
||||
},
|
||||
{
|
||||
label: 'Issues',
|
||||
to: 'https://github.com/algolia/docsearch/issues',
|
||||
},
|
||||
{
|
||||
label: 'Privacy',
|
||||
to: 'https://www.algolia.com/policies/privacy/',
|
||||
},
|
||||
],
|
||||
},
|
||||
{
|
||||
title: 'Community',
|
||||
items: [
|
||||
{
|
||||
label: 'Discord',
|
||||
to: 'https://discord.com/invite/W7kYfh7FKQ',
|
||||
},
|
||||
],
|
||||
},
|
||||
{
|
||||
title: 'Social',
|
||||
items: [
|
||||
{
|
||||
label: 'GitHub',
|
||||
to: 'https://github.com/algolia/docsearch',
|
||||
},
|
||||
{
|
||||
label: 'Twitter',
|
||||
to: 'https://twitter.com/docsearch_',
|
||||
},
|
||||
{
|
||||
label: 'Algolia Blog',
|
||||
to: 'https://algolia.com/blog/',
|
||||
},
|
||||
],
|
||||
},
|
||||
],
|
||||
logo: {
|
||||
alt: 'Algolia',
|
||||
src: 'img/Algolia-logo-blue.svg',
|
||||
srcDark: 'img/Algolia-logo-white.svg',
|
||||
width: 200,
|
||||
},
|
||||
copyright: 'DocSearch 2015-now • Designed and built by Algolia',
|
||||
},
|
||||
image: 'img/og_image.png',
|
||||
prism: {
|
||||
theme: themes.github,
|
||||
darkTheme: themes.dracula,
|
||||
},
|
||||
}),
|
||||
}
|
||||
);
|
||||
176
packages/website/docusaurus.config.mjs
Normal file
176
packages/website/docusaurus.config.mjs
Normal file
|
|
@ -0,0 +1,176 @@
|
|||
import { themes } from 'prism-react-renderer';
|
||||
|
||||
import myLoaders from './plugins/my-loaders.mjs';
|
||||
import tailwindLoader from './plugins/tailwind-loader.mjs';
|
||||
|
||||
// With JSDoc @type annotations, IDEs can provide config autocompletion
|
||||
/** @type {import('@docusaurus/types').DocusaurusConfig} */
|
||||
export default {
|
||||
title: 'DocSearch by Algolia',
|
||||
tagline: 'The best search experience for docs, integrated in minutes, for free.',
|
||||
url: 'https://docsearch.algolia.com',
|
||||
baseUrl: '/',
|
||||
favicon: 'img/favicon.ico',
|
||||
organizationName: 'Algolia',
|
||||
projectName: 'DocSearch',
|
||||
onBrokenLinks: 'throw',
|
||||
onBrokenMarkdownLinks: 'throw',
|
||||
presets: [
|
||||
[
|
||||
'@docusaurus/preset-classic',
|
||||
/** @type {import('@docusaurus/preset-classic').Options} */
|
||||
({
|
||||
docs: {
|
||||
path: 'docs',
|
||||
sidebarPath: 'sidebars.js',
|
||||
editUrl: 'https://github.com/algolia/docsearch/edit/main/packages/website/',
|
||||
versions: {
|
||||
current: {
|
||||
label: 'current',
|
||||
},
|
||||
},
|
||||
lastVersion: 'current',
|
||||
showLastUpdateAuthor: true,
|
||||
showLastUpdateTime: true,
|
||||
},
|
||||
theme: {
|
||||
customCss: require.resolve('./src/css/custom.css'),
|
||||
},
|
||||
}),
|
||||
],
|
||||
],
|
||||
plugins: [myLoaders, tailwindLoader],
|
||||
themeConfig:
|
||||
/** @type {import('@docusaurus/preset-classic').ThemeConfig} */
|
||||
({
|
||||
algolia: {
|
||||
appId: 'R2IYF7ETH7',
|
||||
apiKey: '599cec31baffa4868cae4e79f180729b',
|
||||
indexName: 'docsearch',
|
||||
contextualSearch: true,
|
||||
},
|
||||
metadata: [
|
||||
{
|
||||
name: 'google-site-verification',
|
||||
content: '23yIHmCD_xnJb_6e3s-w7M29Kydahk-d86ObMWOrvRg',
|
||||
},
|
||||
],
|
||||
navbar: {
|
||||
hideOnScroll: true,
|
||||
logo: {
|
||||
alt: 'DocSearch x Algolia',
|
||||
src: 'img/docsearch-x-algolia-logo-light-mode.png',
|
||||
srcDark: 'img/docsearch-x-algolia-logo-dark-mode.png',
|
||||
className: 'docsearch-nav-logo',
|
||||
},
|
||||
items: [
|
||||
// left
|
||||
{
|
||||
label: 'Docs',
|
||||
to: 'docs/what-is-docsearch',
|
||||
position: 'left',
|
||||
},
|
||||
{
|
||||
label: 'Apply',
|
||||
to: 'apply',
|
||||
position: 'left',
|
||||
},
|
||||
// right
|
||||
{
|
||||
type: 'docsVersionDropdown',
|
||||
position: 'right',
|
||||
},
|
||||
{
|
||||
href: 'https://github.com/algolia/docsearch',
|
||||
position: 'right',
|
||||
className: 'header-github-link',
|
||||
},
|
||||
],
|
||||
},
|
||||
colorMode: {
|
||||
defaultMode: 'light',
|
||||
disableSwitch: false,
|
||||
respectPrefersColorScheme: true,
|
||||
},
|
||||
announcementBar: {
|
||||
content:
|
||||
'⭐️ If you like DocSearch, give it a star on <a target="_blank" rel="noopener noreferrer" href="https://github.com/algolia/docsearch">GitHub</a>! ⭐️',
|
||||
},
|
||||
footer: {
|
||||
links: [
|
||||
{
|
||||
title: 'Docs',
|
||||
items: [
|
||||
{
|
||||
label: 'Getting Started',
|
||||
to: 'docs/what-is-docsearch',
|
||||
},
|
||||
{
|
||||
label: 'FAQ',
|
||||
to: 'docs/docsearch-program',
|
||||
},
|
||||
{
|
||||
label: 'DocSearch v3',
|
||||
to: 'docs/docsearch-v3',
|
||||
},
|
||||
],
|
||||
},
|
||||
{
|
||||
title: 'DocSearch',
|
||||
items: [
|
||||
{
|
||||
label: 'Apply',
|
||||
to: 'apply',
|
||||
},
|
||||
{
|
||||
label: 'Issues',
|
||||
to: 'https://github.com/algolia/docsearch/issues',
|
||||
},
|
||||
{
|
||||
label: 'Privacy',
|
||||
to: 'https://www.algolia.com/policies/privacy/',
|
||||
},
|
||||
],
|
||||
},
|
||||
{
|
||||
title: 'Community',
|
||||
items: [
|
||||
{
|
||||
label: 'Discord',
|
||||
to: 'https://discord.com/invite/W7kYfh7FKQ',
|
||||
},
|
||||
],
|
||||
},
|
||||
{
|
||||
title: 'Social',
|
||||
items: [
|
||||
{
|
||||
label: 'GitHub',
|
||||
to: 'https://github.com/algolia/docsearch',
|
||||
},
|
||||
{
|
||||
label: 'Twitter',
|
||||
to: 'https://twitter.com/docsearch_',
|
||||
},
|
||||
{
|
||||
label: 'Algolia Blog',
|
||||
to: 'https://algolia.com/blog/',
|
||||
},
|
||||
],
|
||||
},
|
||||
],
|
||||
logo: {
|
||||
alt: 'Algolia',
|
||||
src: 'img/Algolia-logo-blue.svg',
|
||||
srcDark: 'img/Algolia-logo-white.svg',
|
||||
width: 200,
|
||||
},
|
||||
copyright: 'DocSearch 2015-now • Designed and built by Algolia',
|
||||
},
|
||||
image: 'img/og_image.png',
|
||||
prism: {
|
||||
theme: themes.github,
|
||||
darkTheme: themes.dracula,
|
||||
},
|
||||
}),
|
||||
};
|
||||
|
|
@ -16,26 +16,21 @@
|
|||
"write-heading-ids": "docusaurus write-heading-ids"
|
||||
},
|
||||
"dependencies": {
|
||||
"@algolia/ui-library": "4.0.0-beta.71",
|
||||
"@algolia/ui-library": "5.86.0",
|
||||
"@docsearch/react": "3.6.3",
|
||||
"@docusaurus/core": "3.5.2",
|
||||
"@docusaurus/preset-classic": "3.5.2",
|
||||
"@docusaurus/core": "3.6.0",
|
||||
"@docusaurus/preset-classic": "3.6.0",
|
||||
"@mdx-js/react": "^3.1.0",
|
||||
"clsx": "^1.1.1",
|
||||
"file-loader": "6.2.0",
|
||||
"my-loaders": "file:plugins/my-loaders",
|
||||
"postcss": "8.4.25",
|
||||
"postcss-import": "15.0.0",
|
||||
"postcss-preset-env": "7.8.2",
|
||||
"postcss": "8.4.47",
|
||||
"postcss-import": "16.1.0",
|
||||
"postcss-preset-env": "10.0.9",
|
||||
"prism-react-renderer": "2.4.0",
|
||||
"react": "18.2.0",
|
||||
"react-dom": "18.2.0",
|
||||
"tailwind-loader": "file:plugins/tailwind-loader",
|
||||
"url-loader": "4.1.1"
|
||||
"react": "^18.0.0",
|
||||
"react-dom": "^18.0.0"
|
||||
},
|
||||
"devDependencies": {
|
||||
"postcss-loader": "6.2.1",
|
||||
"tailwindcss": "npm:@tailwindcss/postcss7-compat"
|
||||
"tailwindcss": "3.4.14"
|
||||
},
|
||||
"browserslist": {
|
||||
"production": [
|
||||
|
|
|
|||
|
|
@ -1,4 +1,4 @@
|
|||
module.exports = function () {
|
||||
export default function () {
|
||||
return {
|
||||
name: 'loaders',
|
||||
configureWebpack() {
|
||||
|
|
@ -15,4 +15,4 @@ module.exports = function () {
|
|||
};
|
||||
},
|
||||
};
|
||||
};
|
||||
}
|
||||
|
|
@ -1,19 +1,22 @@
|
|||
/* eslint-disable @typescript-eslint/no-var-requires */
|
||||
module.exports = function () {
|
||||
import postcssImport from 'postcss-import';
|
||||
import postcssPresetEnv from 'postcss-preset-env';
|
||||
import tailwindcss from 'tailwindcss';
|
||||
|
||||
export default function () {
|
||||
return {
|
||||
name: 'postcss-tailwindcss-loader',
|
||||
configurePostCss(postcssOptions) {
|
||||
postcssOptions.plugins.push(
|
||||
require('postcss-import'),
|
||||
require('tailwindcss'),
|
||||
require('postcss-preset-env')({
|
||||
postcssImport,
|
||||
tailwindcss,
|
||||
postcssPresetEnv({
|
||||
autoprefixer: {
|
||||
flexbox: 'no-2009',
|
||||
},
|
||||
stage: 4,
|
||||
})
|
||||
}),
|
||||
);
|
||||
return postcssOptions;
|
||||
},
|
||||
};
|
||||
};
|
||||
}
|
||||
|
|
@ -9,7 +9,7 @@
|
|||
* Create as many sidebars as you want.
|
||||
*/
|
||||
|
||||
module.exports = {
|
||||
export default {
|
||||
docs: [
|
||||
{
|
||||
type: 'category',
|
||||
|
|
|
|||
|
|
@ -1,12 +1,4 @@
|
|||
import {
|
||||
Button,
|
||||
Card,
|
||||
Heading1,
|
||||
InlineLink,
|
||||
Input,
|
||||
LabelText,
|
||||
Text,
|
||||
} from '@algolia/ui-library';
|
||||
import { Button, Card, Heading1, InlineLink, Input, LabelText, Text } from '@algolia/ui-library';
|
||||
import { useBaseUrlUtils } from '@docusaurus/useBaseUrl';
|
||||
import React, { useState } from 'react';
|
||||
|
||||
|
|
@ -68,7 +60,7 @@ function ApplyForm() {
|
|||
setState({
|
||||
status: 'failed',
|
||||
message: 'Unable to submit your request.',
|
||||
})
|
||||
}),
|
||||
);
|
||||
};
|
||||
|
||||
|
|
@ -79,10 +71,7 @@ function ApplyForm() {
|
|||
<>
|
||||
<Heading1 className="apply-text">URL Already Submitted!</Heading1>
|
||||
<br />
|
||||
<Text
|
||||
className="uil-pv-8 uil-d-block apply-text"
|
||||
aria-label="Request has already been processed"
|
||||
>
|
||||
<Text className="uil-pv-8 uil-d-block apply-text" aria-label="Request has already been processed">
|
||||
{state.message}
|
||||
</Text>
|
||||
</>
|
||||
|
|
@ -90,13 +79,9 @@ function ApplyForm() {
|
|||
<>
|
||||
<Heading1 className="apply-text">Thank You!</Heading1>
|
||||
<br />
|
||||
<Text
|
||||
className="uil-pv-8 uil-d-block apply-text"
|
||||
aria-label="Request will be processed"
|
||||
>
|
||||
{state.message} We'll get back to you at <strong>{email}</strong>{' '}
|
||||
with the snippet you'll need to integrate into{' '}
|
||||
<InlineLink href={url}>{url}</InlineLink>.
|
||||
<Text className="uil-pv-8 uil-d-block apply-text" aria-label="Request will be processed">
|
||||
{state.message} We'll get back to you at <strong>{email}</strong> with the snippet you'll need to
|
||||
integrate into <InlineLink href={url}>{url}</InlineLink>.
|
||||
</Text>
|
||||
|
||||
<Text aria-label="recommendations" className="apply-text">
|
||||
|
|
@ -137,12 +122,7 @@ function ApplyForm() {
|
|||
We'll scrape pages at this address and index the content on Algolia.
|
||||
</Text>
|
||||
|
||||
<LabelText
|
||||
tag="label"
|
||||
htmlFor="email"
|
||||
key="email"
|
||||
className="apply-text"
|
||||
>
|
||||
<LabelText tag="label" htmlFor="email" key="email" className="apply-text">
|
||||
Email
|
||||
<Input
|
||||
required={true}
|
||||
|
|
@ -157,16 +137,11 @@ function ApplyForm() {
|
|||
</LabelText>
|
||||
|
||||
<Text small={true} className="uil-pv-8 uil-d-block apply-text">
|
||||
We'll send you the snippet you'll have to integrate into your website
|
||||
and grant access to your Algolia application.
|
||||
We'll send you the snippet you'll have to integrate into your website and grant access to your Algolia
|
||||
application.
|
||||
</Text>
|
||||
|
||||
<LabelText
|
||||
tag="label"
|
||||
htmlFor="repoURL"
|
||||
key="repoURL"
|
||||
className="apply-text"
|
||||
>
|
||||
<LabelText tag="label" htmlFor="repoURL" key="repoURL" className="apply-text">
|
||||
Repository URL (Optional)
|
||||
<Input
|
||||
required={false}
|
||||
|
|
@ -181,12 +156,7 @@ function ApplyForm() {
|
|||
</LabelText>
|
||||
|
||||
<div className="uil-ph-32 uil-d-flex uil-fxd-column">
|
||||
<LabelText
|
||||
className="uil-pt-12 apply-text"
|
||||
tag="label"
|
||||
htmlFor="public"
|
||||
key="public"
|
||||
>
|
||||
<LabelText className="uil-pt-12 apply-text" tag="label" htmlFor="public" key="public">
|
||||
<input
|
||||
required={true}
|
||||
id="public"
|
||||
|
|
@ -195,16 +165,10 @@ function ApplyForm() {
|
|||
type="checkbox"
|
||||
className="uil-mr-8"
|
||||
/>
|
||||
My website is a publicly available developer documentation or a
|
||||
technical blog.
|
||||
My website is a publicly available developer documentation or a technical blog.
|
||||
</LabelText>
|
||||
|
||||
<LabelText
|
||||
className="uil-pt-12 apply-text"
|
||||
tag="label"
|
||||
htmlFor="owner"
|
||||
key="owner"
|
||||
>
|
||||
<LabelText className="uil-pt-12 apply-text" tag="label" htmlFor="owner" key="owner">
|
||||
<input
|
||||
required={true}
|
||||
id="owner"
|
||||
|
|
@ -215,20 +179,12 @@ function ApplyForm() {
|
|||
type="checkbox"
|
||||
className="uil-mr-8"
|
||||
/>
|
||||
I am the owner of the website, or otherwise have obtained and
|
||||
continue to maintain any required consents necessary to use
|
||||
DocSearch on the requested domain. And I have{' '}
|
||||
<InlineLink href={withBaseUrl('docs/who-can-apply')}>
|
||||
read the checklist.
|
||||
</InlineLink>
|
||||
I am the owner of the website, or otherwise have obtained and continue to maintain any required consents
|
||||
necessary to use DocSearch on the requested domain. And I have{' '}
|
||||
<InlineLink href={withBaseUrl('docs/who-can-apply')}>read the checklist.</InlineLink>
|
||||
</LabelText>
|
||||
|
||||
<LabelText
|
||||
className="uil-pt-12 apply-text"
|
||||
tag="label"
|
||||
htmlFor="opensource"
|
||||
key="opensource"
|
||||
>
|
||||
<LabelText className="uil-pt-12 apply-text" tag="label" htmlFor="opensource" key="opensource">
|
||||
<input
|
||||
required={true}
|
||||
id="opensource"
|
||||
|
|
@ -238,10 +194,7 @@ function ApplyForm() {
|
|||
className="uil-mr-8"
|
||||
/>
|
||||
I understand that this is a Free Service as described in Algolia's{' '}
|
||||
<InlineLink href="https://www.algolia.com/policies/terms/">
|
||||
Terms of Service
|
||||
</InlineLink>{' '}
|
||||
and Algolia's{' '}
|
||||
<InlineLink href="https://www.algolia.com/policies/terms/">Terms of Service</InlineLink> and Algolia's{' '}
|
||||
<InlineLink href="https://www.algolia.com/policies/free-services-terms/">
|
||||
Special Free Services Terms
|
||||
</InlineLink>
|
||||
|
|
@ -262,8 +215,7 @@ function ApplyForm() {
|
|||
|
||||
<Text small={true} className="uil-ta-center">
|
||||
<InlineLink href="https://www.algolia.com/policies/privacy/">
|
||||
Refer to Algolia's Privacy Policy for more information on how we use
|
||||
and protect your data
|
||||
Refer to Algolia's Privacy Policy for more information on how we use and protect your data
|
||||
</InlineLink>
|
||||
</Text>
|
||||
|
||||
|
|
@ -271,10 +223,7 @@ function ApplyForm() {
|
|||
<strong>
|
||||
Only apply if you don't have a DocSearch application yet. <br />
|
||||
For support requests, make sure to first{' '}
|
||||
<InlineLink href="/docs/docsearch-program#support">
|
||||
read our policy
|
||||
</InlineLink>
|
||||
.
|
||||
<InlineLink href="/docs/docsearch-program#support">read our policy</InlineLink>.
|
||||
</strong>{' '}
|
||||
</Text>
|
||||
</form>
|
||||
|
|
|
|||
|
|
@ -32,19 +32,10 @@ function Home() {
|
|||
}
|
||||
background="curves"
|
||||
cta={[
|
||||
<Button
|
||||
key="get-started"
|
||||
href={withBaseUrl('docs/what-is-docsearch')}
|
||||
>
|
||||
<Button key="get-started" href={withBaseUrl('docs/what-is-docsearch')}>
|
||||
Get started
|
||||
</Button>,
|
||||
<Button
|
||||
key="apply"
|
||||
href={withBaseUrl('apply')}
|
||||
background="blue"
|
||||
color="white"
|
||||
className="apply-button"
|
||||
>
|
||||
<Button key="apply" href={withBaseUrl('apply')} background="blue" color="white" className="apply-button">
|
||||
Apply
|
||||
</Button>,
|
||||
]}
|
||||
|
|
@ -71,10 +62,7 @@ function Home() {
|
|||
<div className="max-w-4xl mx-auto">
|
||||
<dl className="rounded-lg shadow-xl lg:grid lg:grid-cols-3 showcase">
|
||||
<div className="flex flex-col border-b p-6 text-center lg:border-0 showcase-border">
|
||||
<dt
|
||||
className="order-2 mt-2 text-lg leading-6 font-medium text-description"
|
||||
id="item-1"
|
||||
>
|
||||
<dt className="order-2 mt-2 text-lg leading-6 font-medium text-description" id="item-1">
|
||||
Free Service
|
||||
</dt>
|
||||
<dd
|
||||
|
|
@ -88,17 +76,11 @@ function Home() {
|
|||
<dt className="order-2 mt-2 text-lg leading-6 font-medium text-description">
|
||||
Searches per month
|
||||
</dt>
|
||||
<dd className="order-1 text-5xl leading-none font-extrabold text-algolia">
|
||||
+170M
|
||||
</dd>
|
||||
<dd className="order-1 text-5xl leading-none font-extrabold text-algolia">+170M</dd>
|
||||
</div>
|
||||
<div className="flex flex-col border-t p-6 text-center lg:border-0 lg:border-l showcase-border">
|
||||
<dt className="order-2 mt-2 text-lg leading-6 font-medium text-description">
|
||||
DocSearch Live
|
||||
</dt>
|
||||
<dd className="order-1 text-5xl leading-none font-extrabold text-algolia">
|
||||
+7000
|
||||
</dd>
|
||||
<dt className="order-2 mt-2 text-lg leading-6 font-medium text-description">DocSearch Live</dt>
|
||||
<dd className="order-1 text-5xl leading-none font-extrabold text-algolia">+7000</dd>
|
||||
</div>
|
||||
</dl>
|
||||
</div>
|
||||
|
|
@ -111,10 +93,7 @@ function Home() {
|
|||
return a.name.localeCompare(b.name);
|
||||
})
|
||||
.map(({ name, href, image }) => (
|
||||
<div
|
||||
key={href}
|
||||
className="col-span-1 flex justify-center py-2 px-2 text-center"
|
||||
>
|
||||
<div key={href} className="col-span-1 flex justify-center py-2 px-2 text-center">
|
||||
<a
|
||||
href={href}
|
||||
rel="noreferrer"
|
||||
|
|
@ -126,9 +105,7 @@ function Home() {
|
|||
src={withBaseUrl(image)}
|
||||
alt={`Discover DocSearch on the ${name} documentation`}
|
||||
/>
|
||||
<div className="text-description uppercase text-xs py-2 font-semibold">
|
||||
{name}
|
||||
</div>
|
||||
<div className="text-description uppercase text-xs py-2 font-semibold">{name}</div>
|
||||
</a>
|
||||
</div>
|
||||
))}
|
||||
|
|
@ -144,8 +121,7 @@ function Home() {
|
|||
Solve docs challenges with a search engine
|
||||
</h3>
|
||||
<p className="mt-4 max-w-3xl mx-auto text-center text-xl leading-7 text-description">
|
||||
Docs are only helpful when your users can find answers easily.
|
||||
Enter DocSearch.
|
||||
Docs are only helpful when your users can find answers easily. Enter DocSearch.
|
||||
</p>
|
||||
</div>
|
||||
|
||||
|
|
@ -155,11 +131,7 @@ function Home() {
|
|||
<div className="flex">
|
||||
<div className="flex-shrink-0">
|
||||
<div className="flex items-center justify-center h-12 w-12 rounded-md bg-algolia text-white">
|
||||
<svg
|
||||
viewBox="0 0 20 20"
|
||||
fill="currentColor"
|
||||
className="search w-6 h-6"
|
||||
>
|
||||
<svg viewBox="0 0 20 20" fill="currentColor" className="search w-6 h-6">
|
||||
<path
|
||||
fillRule="evenodd"
|
||||
d="M8 4a4 4 0 100 8 4 4 0 000-8zM2 8a6 6 0 1110.89 3.476l4.817 4.817a1 1 0 01-1.414 1.414l-4.816-4.816A6 6 0 012 8z"
|
||||
|
|
@ -169,14 +141,11 @@ function Home() {
|
|||
</div>
|
||||
</div>
|
||||
<div className="ml-4">
|
||||
<h4 className="text-lg leading-6 font-medium">
|
||||
Made for Developer docs
|
||||
</h4>
|
||||
<h4 className="text-lg leading-6 font-medium">Made for Developer docs</h4>
|
||||
<p className="mt-2 text-base leading-6 text-description">
|
||||
Initially created to fulfill our own documentation
|
||||
needs, DocSearch eventually became a community project
|
||||
for open source docs. But, now it is available for
|
||||
any/all kinds of developer documentation.
|
||||
Initially created to fulfill our own documentation needs, DocSearch eventually became a
|
||||
community project for open source docs. But, now it is available for any/all kinds of developer
|
||||
documentation.
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
|
|
@ -185,12 +154,7 @@ function Home() {
|
|||
<div className="flex">
|
||||
<div className="flex-shrink-0">
|
||||
<div className="flex items-center justify-center h-12 w-12 rounded-md bg-algolia text-white">
|
||||
<svg
|
||||
fill="none"
|
||||
viewBox="0 0 24 24"
|
||||
stroke="currentColor"
|
||||
className="user-group w-6 h-6"
|
||||
>
|
||||
<svg fill="none" viewBox="0 0 24 24" stroke="currentColor" className="user-group w-6 h-6">
|
||||
<path
|
||||
strokeLinecap="round"
|
||||
strokeLinejoin="round"
|
||||
|
|
@ -201,14 +165,10 @@ function Home() {
|
|||
</div>
|
||||
</div>
|
||||
<div className="ml-4">
|
||||
<h4 className="text-lg leading-6 font-medium">
|
||||
Customizable and fast
|
||||
</h4>
|
||||
<h4 className="text-lg leading-6 font-medium">Customizable and fast</h4>
|
||||
<p className="mt-2 text-base leading-6 text-description">
|
||||
DocSearch understands how the user input fits into the
|
||||
context of your project and instantly presents the most
|
||||
relevant content with fewer interactions than any other
|
||||
method.
|
||||
DocSearch understands how the user input fits into the context of your project and instantly
|
||||
presents the most relevant content with fewer interactions than any other method.
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
|
|
@ -217,12 +177,7 @@ function Home() {
|
|||
<div className="flex">
|
||||
<div className="flex-shrink-0">
|
||||
<div className="flex items-center justify-center h-12 w-12 rounded-md bg-algolia text-white">
|
||||
<svg
|
||||
fill="none"
|
||||
viewBox="0 0 24 24"
|
||||
stroke="currentColor"
|
||||
className="device-mobile w-6 h-6"
|
||||
>
|
||||
<svg fill="none" viewBox="0 0 24 24" stroke="currentColor" className="device-mobile w-6 h-6">
|
||||
<path
|
||||
strokeLinecap="round"
|
||||
strokeLinejoin="round"
|
||||
|
|
@ -233,13 +188,10 @@ function Home() {
|
|||
</div>
|
||||
</div>
|
||||
<div className="ml-4">
|
||||
<h4 className="text-lg leading-6 font-medium">
|
||||
Mobile design
|
||||
</h4>
|
||||
<h4 className="text-lg leading-6 font-medium">Mobile design</h4>
|
||||
<p className="mt-2 text-base leading-6 text-description">
|
||||
With a design very close to the native experience on
|
||||
mobile, we leverage users acquaintance with the
|
||||
interaction patterns of each OS.
|
||||
With a design very close to the native experience on mobile, we leverage users acquaintance with
|
||||
the interaction patterns of each OS.
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
|
|
@ -258,9 +210,8 @@ function Home() {
|
|||
How it works
|
||||
</h2>
|
||||
<p className="mt-4 max-w-2xl text-xl leading-7 text-gray-500 lg:mx-auto">
|
||||
We scrape your documentation or technical blog, configure the
|
||||
Algolia application and send you the snippet you'll have to
|
||||
integrate. It's that simple.
|
||||
We scrape your documentation or technical blog, configure the Algolia application and send you the
|
||||
snippet you'll have to integrate. It's that simple.
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
|
|
@ -279,9 +230,7 @@ function Home() {
|
|||
/>
|
||||
</div>
|
||||
<div className="mt-10 lg:mt-0 p-4">
|
||||
<h5 className="text-lg leading-6 font-medium text-gray-900">
|
||||
1. Scraping
|
||||
</h5>
|
||||
<h5 className="text-lg leading-6 font-medium text-gray-900">1. Scraping</h5>
|
||||
<p className="mt-2 text-base leading-6 text-gray-600">
|
||||
We leverage the{' '}
|
||||
<InlineLink
|
||||
|
|
@ -304,12 +253,10 @@ function Home() {
|
|||
/>
|
||||
</div>
|
||||
<div>
|
||||
<h5 className="text-lg leading-6 font-medium text-gray-900">
|
||||
2. Configuration
|
||||
</h5>
|
||||
<h5 className="text-lg leading-6 font-medium text-gray-900">2. Configuration</h5>
|
||||
<p className="mt-2 text-base leading-6 text-gray-600">
|
||||
You don't need to configure any settings or even have an
|
||||
Algolia account. We take care of this for you!
|
||||
You don't need to configure any settings or even have an Algolia account. We take care of this
|
||||
for you!
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
|
|
@ -323,13 +270,10 @@ function Home() {
|
|||
/>
|
||||
</div>
|
||||
<div>
|
||||
<h5 className="text-lg leading-6 font-medium text-gray-900">
|
||||
3. Implementation
|
||||
</h5>
|
||||
<h5 className="text-lg leading-6 font-medium text-gray-900">3. Implementation</h5>
|
||||
<p className="mt-2 text-base leading-6 text-gray-600">
|
||||
We'll send you a small snippet to integrate DocSearch to
|
||||
your website and an invite to your fully configured
|
||||
Algolia application.
|
||||
We'll send you a small snippet to integrate DocSearch to your website and an invite to your
|
||||
fully configured Algolia application.
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
|
|
@ -348,9 +292,8 @@ function Home() {
|
|||
Overview of DocSearch
|
||||
</h4>
|
||||
<p className="mt-3 text-lg leading-7 text-description">
|
||||
Search doesn't have to be painful — when a user searches
|
||||
through your docs, they'll get a frictionless modal dialog to
|
||||
work in. And it's a Free Service!
|
||||
Search doesn't have to be painful — when a user searches through your docs, they'll get a frictionless
|
||||
modal dialog to work in. And it's a Free Service!
|
||||
</p>
|
||||
|
||||
<ul className="mt-10">
|
||||
|
|
@ -358,12 +301,7 @@ function Home() {
|
|||
<div className="flex">
|
||||
<div className="flex-shrink-0">
|
||||
<div className="flex items-center justify-center h-12 w-12 rounded-md bg-algolia text-white">
|
||||
<svg
|
||||
fill="none"
|
||||
viewBox="0 0 24 24"
|
||||
stroke="currentColor"
|
||||
className="sparkles w-6 h-6"
|
||||
>
|
||||
<svg fill="none" viewBox="0 0 24 24" stroke="currentColor" className="sparkles w-6 h-6">
|
||||
<path
|
||||
strokeLinecap="round"
|
||||
strokeLinejoin="round"
|
||||
|
|
@ -374,15 +312,11 @@ function Home() {
|
|||
</div>
|
||||
</div>
|
||||
<div className="ml-4">
|
||||
<h5 className="text-lg leading-6 font-medium">
|
||||
All featured search box
|
||||
</h5>
|
||||
<h5 className="text-lg leading-6 font-medium">All featured search box</h5>
|
||||
<p className="mt-2 text-base leading-6 text-description">
|
||||
The conversation start here: you want to have the
|
||||
state-of-the-art search box to represent your voice.
|
||||
DocSearch comes with our extensive experience in
|
||||
making this search box respects all the best
|
||||
practices.
|
||||
The conversation start here: you want to have the state-of-the-art search box to represent
|
||||
your voice. DocSearch comes with our extensive experience in making this search box respects
|
||||
all the best practices.
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
|
|
@ -391,12 +325,7 @@ function Home() {
|
|||
<div className="flex">
|
||||
<div className="flex-shrink-0">
|
||||
<div className="flex items-center justify-center h-12 w-12 rounded-md bg-algolia text-white">
|
||||
<svg
|
||||
fill="none"
|
||||
viewBox="0 0 24 24"
|
||||
stroke="currentColor"
|
||||
className="menu-alt2 w-6 h-6"
|
||||
>
|
||||
<svg fill="none" viewBox="0 0 24 24" stroke="currentColor" className="menu-alt2 w-6 h-6">
|
||||
<path
|
||||
strokeLinecap="round"
|
||||
strokeLinejoin="round"
|
||||
|
|
@ -407,12 +336,10 @@ function Home() {
|
|||
</div>
|
||||
</div>
|
||||
<div className="ml-4">
|
||||
<h5 className="text-lg leading-6 font-medium">
|
||||
Hierarchical display
|
||||
</h5>
|
||||
<h5 className="text-lg leading-6 font-medium">Hierarchical display</h5>
|
||||
<p className="mt-2 text-base leading-6 text-description">
|
||||
DocSearch organizes the search results into chunks
|
||||
that reflect how your documentation is structured.
|
||||
DocSearch organizes the search results into chunks that reflect how your documentation is
|
||||
structured.
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
|
|
@ -424,11 +351,7 @@ function Home() {
|
|||
<img
|
||||
className="relative mx-auto rounded-lg shadow-lg image-rendering-crisp"
|
||||
src={withBaseUrl(
|
||||
`img/assets/${
|
||||
colorMode === 'dark'
|
||||
? 'docsearch-shadow-dark'
|
||||
: 'docsearch-shadow'
|
||||
}.png`
|
||||
`img/assets/${colorMode === 'dark' ? 'docsearch-shadow-dark' : 'docsearch-shadow'}.png`,
|
||||
)}
|
||||
alt="docsearch-modal"
|
||||
/>
|
||||
|
|
@ -459,14 +382,10 @@ function Home() {
|
|||
</div>
|
||||
</div>
|
||||
<div className="ml-4">
|
||||
<h5 className="text-lg leading-6 font-medium">
|
||||
Inputs and shortcuts
|
||||
</h5>
|
||||
<h5 className="text-lg leading-6 font-medium">Inputs and shortcuts</h5>
|
||||
<p className="mt-2 text-base leading-6 text-description">
|
||||
It looks almost similar to a search input but it's a
|
||||
button. When you click/touch or use the keyboard
|
||||
shortcut, it opens a modal dropdown and focuses the
|
||||
search input.
|
||||
It looks almost similar to a search input but it's a button. When you click/touch or use the
|
||||
keyboard shortcut, it opens a modal dropdown and focuses the search input.
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
|
|
@ -491,15 +410,11 @@ function Home() {
|
|||
</div>
|
||||
</div>
|
||||
<div className="ml-4">
|
||||
<h5 className="text-lg leading-6 font-medium">
|
||||
Always sized and positioned correctly
|
||||
</h5>
|
||||
<h5 className="text-lg leading-6 font-medium">Always sized and positioned correctly</h5>
|
||||
<p className="mt-2 text-base leading-6 text-description">
|
||||
The modal experience leverages behavior of most
|
||||
popular Integrated Developement Environements (IDEs)
|
||||
or native experience on mobile. An opaque layer is
|
||||
making sure we keep the context of the documentation
|
||||
but remove all visual pollution.
|
||||
The modal experience leverages behavior of most popular Integrated Developement
|
||||
Environements (IDEs) or native experience on mobile. An opaque layer is making sure we keep
|
||||
the context of the documentation but remove all visual pollution.
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
|
|
@ -535,12 +450,7 @@ function Home() {
|
|||
<div className="flex">
|
||||
<div className="flex-shrink-0">
|
||||
<div className="flex items-center justify-center h-12 w-12 rounded-md bg-white text-algolia">
|
||||
<svg
|
||||
fill="none"
|
||||
viewBox="0 0 24 24"
|
||||
stroke="currentColor"
|
||||
className="chip w-6 h-6"
|
||||
>
|
||||
<svg fill="none" viewBox="0 0 24 24" stroke="currentColor" className="chip w-6 h-6">
|
||||
<path
|
||||
strokeLinecap="round"
|
||||
strokeLinejoin="round"
|
||||
|
|
@ -551,13 +461,10 @@ function Home() {
|
|||
</div>
|
||||
</div>
|
||||
<div className="ml-4">
|
||||
<h5 className="text-lg leading-6 font-medium text-white">
|
||||
Highly efficient
|
||||
</h5>
|
||||
<h5 className="text-lg leading-6 font-medium text-white">Highly efficient</h5>
|
||||
<p className="mt-2 text-base leading-6 text-gray-300">
|
||||
Most of our search queries take less than 20ms, so
|
||||
Algolia can run a new search on every keystroke and
|
||||
instantly highlight relevant content to your users.
|
||||
Most of our search queries take less than 20ms, so Algolia can run a new search on every
|
||||
keystroke and instantly highlight relevant content to your users.
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
|
|
@ -566,12 +473,7 @@ function Home() {
|
|||
<div className="flex">
|
||||
<div className="flex-shrink-0">
|
||||
<div className="flex items-center justify-center h-12 w-12 rounded-md bg-white text-algolia">
|
||||
<svg
|
||||
fill="none"
|
||||
viewBox="0 0 24 24"
|
||||
stroke="currentColor"
|
||||
className="chat w-6 h-6"
|
||||
>
|
||||
<svg fill="none" viewBox="0 0 24 24" stroke="currentColor" className="chat w-6 h-6">
|
||||
<path
|
||||
strokeLinecap="round"
|
||||
strokeLinejoin="round"
|
||||
|
|
@ -582,12 +484,10 @@ function Home() {
|
|||
</div>
|
||||
</div>
|
||||
<div className="ml-4">
|
||||
<h5 className="text-lg leading-6 font-medium text-white">
|
||||
Search as you type
|
||||
</h5>
|
||||
<h5 className="text-lg leading-6 font-medium text-white">Search as you type</h5>
|
||||
<p className="mt-2 text-base leading-6 text-gray-300">
|
||||
Instantly magnify relevant content to your users from
|
||||
the first keystroke, thanks to Algolia speed.
|
||||
Instantly magnify relevant content to your users from the first keystroke, thanks to Algolia
|
||||
speed.
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
|
|
@ -601,12 +501,7 @@ function Home() {
|
|||
<div className="flex">
|
||||
<div className="flex-shrink-0">
|
||||
<div className="flex items-center justify-center h-12 w-12 rounded-md bg-white text-algolia">
|
||||
<svg
|
||||
fill="none"
|
||||
viewBox="0 0 24 24"
|
||||
stroke="currentColor"
|
||||
className="backspace w-6 h-6"
|
||||
>
|
||||
<svg fill="none" viewBox="0 0 24 24" stroke="currentColor" className="backspace w-6 h-6">
|
||||
<path
|
||||
strokeLinecap="round"
|
||||
strokeLinejoin="round"
|
||||
|
|
@ -621,9 +516,8 @@ function Home() {
|
|||
Detailed and straightforward analytics
|
||||
</h5>
|
||||
<p className="mt-2 text-base leading-6 text-gray-300">
|
||||
Use our analytics to keep an eye on your search
|
||||
performance and to make sure that your users are
|
||||
finding what they're searching for.
|
||||
Use our analytics to keep an eye on your search performance and to make sure that your users
|
||||
are finding what they're searching for.
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
|
|
@ -632,12 +526,7 @@ function Home() {
|
|||
<div className="flex">
|
||||
<div className="flex-shrink-0">
|
||||
<div className="flex items-center justify-center h-12 w-12 rounded-md bg-white text-algolia">
|
||||
<svg
|
||||
fill="none"
|
||||
viewBox="0 0 24 24"
|
||||
stroke="currentColor"
|
||||
className="chart-bar w-6 h-6"
|
||||
>
|
||||
<svg fill="none" viewBox="0 0 24 24" stroke="currentColor" className="chart-bar w-6 h-6">
|
||||
<path
|
||||
strokeLinecap="round"
|
||||
strokeLinejoin="round"
|
||||
|
|
@ -648,13 +537,10 @@ function Home() {
|
|||
</div>
|
||||
</div>
|
||||
<div className="ml-4">
|
||||
<h5 className="text-lg leading-6 font-medium text-white">
|
||||
Typo tolerant
|
||||
</h5>
|
||||
<h5 className="text-lg leading-6 font-medium text-white">Typo tolerant</h5>
|
||||
<p className="mt-2 text-base leading-6 text-gray-300">
|
||||
Algolia knows what your users mean to search for right
|
||||
out-of-the-box, so they won't be spending time
|
||||
correcting typos.
|
||||
Algolia knows what your users mean to search for right out-of-the-box, so they won't be
|
||||
spending time correcting typos.
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
|
|
@ -673,9 +559,8 @@ function Home() {
|
|||
It's all about keyboards
|
||||
</h3>
|
||||
<p className="mt-4 max-w-3xl mx-auto text-center text-xl text-description lg:mx-auto">
|
||||
Once users become familiar with a system they will become better
|
||||
and faster at doing what they need to. DocSearch offers
|
||||
accelerators to experts.
|
||||
Once users become familiar with a system they will become better and faster at doing what they need to.
|
||||
DocSearch offers accelerators to experts.
|
||||
</p>
|
||||
<div className="pt-12 items-center justify-center flex">
|
||||
<img
|
||||
|
|
@ -692,12 +577,7 @@ function Home() {
|
|||
<div className="flex">
|
||||
<div className="flex-shrink-0">
|
||||
<div className="flex items-center justify-center h-12 w-12 rounded-md bg-algolia text-white">
|
||||
<svg
|
||||
className="h-6 w-6"
|
||||
fill="none"
|
||||
viewBox="0 0 24 24"
|
||||
stroke="currentColor"
|
||||
>
|
||||
<svg className="h-6 w-6" fill="none" viewBox="0 0 24 24" stroke="currentColor">
|
||||
<path
|
||||
strokeLinecap="round"
|
||||
strokeLinejoin="round"
|
||||
|
|
@ -708,13 +588,10 @@ function Home() {
|
|||
</div>
|
||||
</div>
|
||||
<div className="ml-4">
|
||||
<h4 className="text-lg leading-6 font-medium">
|
||||
Keyboard Shortcut & Navigation
|
||||
</h4>
|
||||
<h4 className="text-lg leading-6 font-medium">Keyboard Shortcut & Navigation</h4>
|
||||
<p className="mt-2 text-base leading-6 text-description">
|
||||
We aim at keeping the experience as smooth as your
|
||||
interaction with an IDE, which explains the Ctrl+K (⌘+K
|
||||
on macOS) keyboard shortcut to open the search modal.
|
||||
We aim at keeping the experience as smooth as your interaction with an IDE, which explains the
|
||||
Ctrl+K (⌘+K on macOS) keyboard shortcut to open the search modal.
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
|
|
@ -723,12 +600,7 @@ function Home() {
|
|||
<div className="flex">
|
||||
<div className="flex-shrink-0">
|
||||
<div className="flex items-center justify-center h-12 w-12 rounded-md bg-algolia text-white">
|
||||
<svg
|
||||
className="h-6 w-6"
|
||||
fill="none"
|
||||
viewBox="0 0 24 24"
|
||||
stroke="currentColor"
|
||||
>
|
||||
<svg className="h-6 w-6" fill="none" viewBox="0 0 24 24" stroke="currentColor">
|
||||
<path
|
||||
strokeLinecap="round"
|
||||
strokeLinejoin="round"
|
||||
|
|
@ -739,15 +611,11 @@ function Home() {
|
|||
</div>
|
||||
</div>
|
||||
<div className="ml-4">
|
||||
<h4 className="text-lg leading-6 font-medium">
|
||||
Accessibility
|
||||
</h4>
|
||||
<h4 className="text-lg leading-6 font-medium">Accessibility</h4>
|
||||
<p className="mt-2 text-base leading-6 text-description">
|
||||
We have released this version with our best efforts on
|
||||
addressing accessibility issues and we are willing to
|
||||
make further changes. We'd like to work closely with an
|
||||
expert in accessibility. Please contact us if you are
|
||||
interested.
|
||||
We have released this version with our best efforts on addressing accessibility issues and we
|
||||
are willing to make further changes. We'd like to work closely with an expert in accessibility.
|
||||
Please contact us if you are interested.
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
|
|
|
|||
|
|
@ -12,11 +12,7 @@ function ApplyPage() {
|
|||
description="The easiest way to add search to your documentation - Powered by Algolia"
|
||||
>
|
||||
<div className="uil-pb-24">
|
||||
<Hero
|
||||
id="hero-apply"
|
||||
title={<DocSearchLogo width="100%" />}
|
||||
background="curves"
|
||||
/>
|
||||
<Hero id="hero-apply" title={<DocSearchLogo width="100%" />} background="curves" />
|
||||
<ApplyForm />
|
||||
</div>
|
||||
</Layout>
|
||||
|
|
|
|||
|
|
@ -1,5 +1,5 @@
|
|||
module.exports = {
|
||||
purge: ['./src/**/*.html', './src/**/*.js', './src/**/*.tsx'],
|
||||
export default {
|
||||
content: ['./src/**/*.html', './src/**/*.js', './src/**/*.tsx'],
|
||||
corePlugins: { preflight: false, container: false },
|
||||
important: '#tailwind',
|
||||
theme: {
|
||||
|
|
@ -1,11 +1,20 @@
|
|||
{
|
||||
"$schema": "https://docs.renovatebot.com/renovate-schema.json",
|
||||
"extends": ["config:js-lib", "algolia"],
|
||||
"baseBranches": ["chore/renovateBaseBranch"],
|
||||
"extends": [
|
||||
"config:js-lib",
|
||||
"github>algolia/renovate-config-algolia"
|
||||
],
|
||||
"baseBranches": [
|
||||
"chore/renovateBaseBranch"
|
||||
],
|
||||
"packageRules": [
|
||||
{
|
||||
"packagePatterns": ["^@types"],
|
||||
"depTypeList": ["devDependencies"],
|
||||
"packagePatterns": [
|
||||
"^@types"
|
||||
],
|
||||
"depTypeList": [
|
||||
"devDependencies"
|
||||
],
|
||||
"rangeStrategy": "replace"
|
||||
}
|
||||
],
|
||||
|
|
|
|||
|
|
@ -2,12 +2,13 @@ import { babel } from '@rollup/plugin-babel';
|
|||
import json from '@rollup/plugin-json';
|
||||
import resolve from '@rollup/plugin-node-resolve';
|
||||
import replace from '@rollup/plugin-replace';
|
||||
import dts from 'rollup-plugin-dts';
|
||||
import terser from '@rollup/plugin-terser';
|
||||
import { dts } from 'rollup-plugin-dts';
|
||||
import filesize from 'rollup-plugin-filesize';
|
||||
import { terser } from 'rollup-plugin-terser';
|
||||
|
||||
export const plugins = [
|
||||
replace({
|
||||
preventAssignment: true,
|
||||
__DEV__: JSON.stringify(process.env.NODE_ENV === 'development'),
|
||||
}),
|
||||
json(),
|
||||
|
|
|
|||
|
|
@ -1,12 +1,8 @@
|
|||
import { execSync } from 'child_process';
|
||||
|
||||
export function getBundleBanner(pkg) {
|
||||
const lastCommitHash = execSync('git rev-parse --short HEAD')
|
||||
.toString()
|
||||
.trim();
|
||||
const version = process.env.SHIPJS
|
||||
? pkg.version
|
||||
: `${pkg.version} (UNRELEASED ${lastCommitHash})`;
|
||||
const lastCommitHash = execSync('git rev-parse --short HEAD').toString().trim();
|
||||
const version = process.env.SHIPJS ? pkg.version : `${pkg.version} (UNRELEASED ${lastCommitHash})`;
|
||||
const authors = '© Algolia, Inc. and contributors';
|
||||
|
||||
return `/*! ${pkg.name} ${version} | MIT License | ${authors} | ${pkg.homepage} */`;
|
||||
|
|
|
|||
|
|
@ -1,31 +0,0 @@
|
|||
type MatchMediaProps = Partial<{
|
||||
matches: boolean;
|
||||
media: string;
|
||||
onchange: null;
|
||||
addListener: jest.Mock;
|
||||
removeListener: jest.Mock;
|
||||
addEventListener: jest.Mock;
|
||||
removeEventListener: jest.Mock;
|
||||
dispatchEvent: jest.Mock;
|
||||
}>;
|
||||
|
||||
const createMatchMedia = (props: MatchMediaProps) => {
|
||||
return jest.fn((query) => ({
|
||||
matches: false,
|
||||
media: query,
|
||||
onchange: null,
|
||||
addListener: jest.fn(),
|
||||
removeListener: jest.fn(),
|
||||
addEventListener: jest.fn(),
|
||||
removeEventListener: jest.fn(),
|
||||
dispatchEvent: jest.fn(),
|
||||
...props,
|
||||
}));
|
||||
};
|
||||
|
||||
Object.defineProperty(window, 'scrollTo', jest.fn());
|
||||
|
||||
Object.defineProperty(window, 'matchMedia', {
|
||||
writable: true,
|
||||
value: createMatchMedia({ matches: true }),
|
||||
});
|
||||
30
scripts/setupTests.ts
Normal file
30
scripts/setupTests.ts
Normal file
|
|
@ -0,0 +1,30 @@
|
|||
import type { Mock } from 'vitest';
|
||||
import { vi } from 'vitest';
|
||||
|
||||
type MatchMediaProps = Partial<{
|
||||
matches: boolean;
|
||||
media: string;
|
||||
onchange: null;
|
||||
addListener: Mock;
|
||||
removeListener: Mock;
|
||||
addEventListener: Mock;
|
||||
removeEventListener: Mock;
|
||||
dispatchEvent: Mock;
|
||||
}>;
|
||||
|
||||
const createMatchMedia = (props: MatchMediaProps): Mock => {
|
||||
return vi.fn((query) => ({
|
||||
matches: false,
|
||||
media: query,
|
||||
onchange: null,
|
||||
addListener: vi.fn(),
|
||||
removeListener: vi.fn(),
|
||||
addEventListener: vi.fn(),
|
||||
removeEventListener: vi.fn(),
|
||||
dispatchEvent: vi.fn(),
|
||||
...props,
|
||||
}));
|
||||
};
|
||||
|
||||
vi.stubGlobal('scrollTo', vi.fn());
|
||||
vi.stubGlobal('matchMedia', createMatchMedia({ matches: true }));
|
||||
|
|
@ -1,14 +1,9 @@
|
|||
/* eslint-disable @typescript-eslint/no-var-requires */
|
||||
const fs = require('fs');
|
||||
const path = require('path');
|
||||
import fs from 'fs';
|
||||
import path from 'path';
|
||||
|
||||
const packages = [
|
||||
'packages/docsearch-css',
|
||||
'packages/docsearch-react',
|
||||
'packages/docsearch-js',
|
||||
];
|
||||
const packages = ['packages/docsearch-css', 'packages/docsearch-react', 'packages/docsearch-js'];
|
||||
|
||||
module.exports = {
|
||||
export default {
|
||||
monorepo: {
|
||||
mainVersionFile: 'lerna.json',
|
||||
// We rely on Lerna to bump our dependencies.
|
||||
|
|
@ -20,9 +15,7 @@ module.exports = {
|
|||
},
|
||||
versionUpdated({ exec, dir, version }) {
|
||||
// Update package dependencies
|
||||
exec(
|
||||
`yarn lerna version ${version} --exact --no-git-tag-version --no-push --yes`
|
||||
);
|
||||
exec(`yarn lerna version ${version} --exact --no-git-tag-version --no-push --yes`);
|
||||
|
||||
// Ship.js reads JSON and writes with `fs.writeFileSync(JSON.stringify(json, null, 2))`
|
||||
// which causes a lint error in the `lerna.json` file.
|
||||
|
|
@ -30,7 +23,7 @@ module.exports = {
|
|||
|
||||
fs.writeFileSync(
|
||||
path.resolve(dir, 'packages', 'docsearch-react', 'src', 'version.ts'),
|
||||
`export const version = '${version}';\n`
|
||||
`export const version = '${version}';\n`,
|
||||
);
|
||||
},
|
||||
};
|
||||
|
|
|
|||
|
|
@ -17,7 +17,6 @@
|
|||
"exclude": [
|
||||
"**/__fixtures__/**/*",
|
||||
"**/__mocks__/**/*",
|
||||
"**/__tests__/**/*",
|
||||
"**/dist",
|
||||
"**/node_modules",
|
||||
"cypress",
|
||||
|
|
|
|||
8
vite.config.mts
Normal file
8
vite.config.mts
Normal file
|
|
@ -0,0 +1,8 @@
|
|||
import { defineConfig } from 'vitest/config';
|
||||
|
||||
export default defineConfig({
|
||||
test: {
|
||||
environment: 'jsdom',
|
||||
setupFiles: ['./scripts/setupTests.ts'],
|
||||
}
|
||||
});
|
||||
Loading…
Reference in a new issue