Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

chore(deps): update dependency @algolia/autocomplete-js to v1.0.0-alpha.45 #267

Merged
merged 7 commits into from
Apr 21, 2021
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 2 additions & 0 deletions .eslintrc.js
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,8 @@ module.exports = {
'no-console': 'off',
'no-continue': 'off',
'no-loop-func': 'off',
// eslint-disable-next-line no-warning-comments
'no-undef': 'warn', // TODO: find how to remove "ESLint: 'JSX' is not defined." errors properly
'consistent-return': 'off',
'@typescript-eslint/no-unused-vars': 'warn',

Expand Down
2 changes: 1 addition & 1 deletion frontend/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,7 @@
"postinstall": "[ -d dist/ ] || npm run build"
},
"devDependencies": {
"@algolia/autocomplete-js": "1.0.0-alpha.44",
"@algolia/autocomplete-js": "1.0.0-alpha.45",
"@algolia/autocomplete-preset-algolia": "1.0.0-alpha.45",
"@algolia/autocomplete-theme-classic": "1.0.0-alpha.45",
"@algolia/transporter": "4.8.6",
Expand Down
66 changes: 4 additions & 62 deletions frontend/src/AutocompleteWrapper.ts
Original file line number Diff line number Diff line change
@@ -1,25 +1,19 @@
import {
autocomplete,
highlightHit,
snippetHit,
} from '@algolia/autocomplete-js';
import { autocomplete } from '@algolia/autocomplete-js';
import type {
VNode,
AutocompleteApi,
AutocompleteSource,
SourceTemplates,
} from '@algolia/autocomplete-js';
import type { HighlightedHit } from '@algolia/autocomplete-preset-algolia';
import { getAlgoliaHits } from '@algolia/autocomplete-preset-algolia';
import type { Hit } from '@algolia/client-search';
import algoliasearch from 'algoliasearch/lite';
import type { SearchClient } from 'algoliasearch/lite';

// @ts-expect-error
import { version } from '../package.json';

import { templates } from './templates';
import type { Options, AlgoliaRecord, HighlightedHierarchy } from './types';
import type { Options, AlgoliaRecord } from './types';

class AutocompleteWrapper {
private options;
Expand Down Expand Up @@ -94,13 +88,8 @@ class AutocompleteWrapper {
header() {
return '';
},
item({ item }) {
return templates.item(
item,
highlightHit({ hit: item, attribute: 'title' }),
getSuggestionSnippet(item),
getHighlightedHierarchy(item)
);
item({ item, components }) {
return templates.item(item, components);
},
footer() {
if (poweredBy) {
Expand Down Expand Up @@ -155,53 +144,6 @@ class AutocompleteWrapper {
}
}

function getSuggestionSnippet(hit: Hit<AlgoliaRecord>): Array<string | VNode> {
// If they are defined as `searchableAttributes`, 'description' and 'content' are always
// present in the `_snippetResult`, even if they don't match.
// So we need to have 1 check on the presence and 1 check on the match
const description = hit._snippetResult?.description;
const content = hit._snippetResult?.content;

// Take in priority props that matches the search
if (description && description.matchLevel === 'full') {
return snippetHit({ hit, attribute: 'description' });
}
if (content && content.matchLevel === 'full') {
return snippetHit({ hit, attribute: 'content' });
}

// Otherwise take the prop that was at least correctly returned
if (description && !content) {
return snippetHit({ hit, attribute: 'description' });
}
if (content) {
return snippetHit({ hit, attribute: 'content' });
}

// Otherwise raw value or empty
const res = hit.description || hit.content || '';
return [res];
}

function getHighlightedHierarchy(
hit: Hit<AlgoliaRecord>
): HighlightedHierarchy | null {
if (!hit.hierarchy) {
return null;
}
const highlightedHierarchy: HighlightedHierarchy = {};
for (let i = 0; i <= 6; ++i) {
if (!hit.hierarchy[`lvl${i}`]) {
continue;
}
highlightedHierarchy[`lvl${i}`] = highlightHit({
hit,
attribute: ['hierarchy', `lvl${i}`],
});
}
return highlightedHierarchy;
}

function addCss(
css: string,
$mainStyle: HTMLElement | null = null
Expand Down
13 changes: 9 additions & 4 deletions frontend/src/index.scss
Original file line number Diff line number Diff line change
Expand Up @@ -40,12 +40,9 @@ $font-size: $font-size-m;
.aa-Panel {
min-width: 350px;
z-index: 1100;
margin-top: $size-xs;

&::after {
background-image: none;
}
.aa-PanelLayout {
margin-top: $size-xs;
padding-top: 0;
padding-bottom: 0;
background-color: var(--color-background);
Expand All @@ -54,6 +51,10 @@ $font-size: $font-size-m;
border-left: solid 1px var(--color-selected);
}
}

.aa-GradientBottom {
background-image: none;
}
}

.aa-Autocomplete, .aa-DetachedFormContainer {
Expand Down Expand Up @@ -87,7 +88,11 @@ $font-size: $font-size-m;
}
}
.aa-InputWrapperSuffix {
height: var(--height);

.aa-ClearButton {
padding: 0;

&:hover,
&:focus {
color: var(--color-text);
Expand Down
92 changes: 66 additions & 26 deletions frontend/src/templates.tsx
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
import type { VNode } from '@algolia/autocomplete-js';
import type { AutocompleteComponents, VNode } from '@algolia/autocomplete-js';
import type { Hit } from '@algolia/client-search';

import type { AlgoliaRecord, HighlightedHierarchy } from './types';
import type { AlgoliaRecord } from './types';

export const templates = {
poweredBy: ({ hostname }: { hostname: string }): VNode => {
Expand All @@ -19,13 +20,11 @@ export const templates = {
},

item: (
record: AlgoliaRecord,
title: Array<string | VNode>,
description: Array<string | VNode>,
hierarchy: HighlightedHierarchy | null
): VNode => {
hit: AlgoliaRecord,
components: AutocompleteComponents
): JSX.Element => {
return (
<a href={record.url}>
<a href={hit.url}>
<div className="aa-ItemContent">
<div className="aa-ItemIcon">
<svg width="20" height="20" viewBox="0 0 20 20">
Expand All @@ -39,15 +38,19 @@ export const templates = {
</svg>
</div>
<div>
<div className="aa-ItemTitle">{hierarchy?.lvl0 ?? title}</div>
{hierarchy && (
<div className="aa-ItemTitle">
{hit.hierarchy?.lvl0 ?? (
<components.Highlight hit={hit} attribute="title" />
)}
</div>
{hit.hierarchy && (
<div className="aa-ItemHierarchy">
{hierarchyToBreadcrumbVNodes(hierarchy)}
{hierarchyToBreadcrumbs(hit, components)}
</div>
)}
{description && (
<div className="aa-ItemDescription">{description}</div>
)}
<div className="aa-ItemDescription">
{getSuggestionSnippet(hit, components)}
</div>
</div>
</div>
</a>
Expand All @@ -56,26 +59,63 @@ export const templates = {
};

/**
* Transform a highlighted hierarchy object into an array of VNode[].
* Transform a highlighted hierarchy object into an array of Highlighted elements.
* 3 levels max are returned.
*
* @param hierarchy - An highlighted hierarchy, i.e. { lvl0: (string | VNode)[], lvl1: (string | VNode)[], lvl2: (string | VNode)[], ... }.
* @returns An array of VNode[], representing of the highlighted hierarchy starting from lvl1.
* Between each VNode[] we insert a ' > ' character to render them as breadcrumbs eventually.
* @param hit - A record with a hierarchy field ( { lvl0: string, lvl1: string, lvl2: string, ... } ).
* @param components - Autocomplete components.
* @returns An array of JSX.Elements | string, representing of the highlighted hierarchy starting from lvl1.
* Between each element, we insert a ' > ' character to render them as breadcrumbs eventually.
*/
function hierarchyToBreadcrumbVNodes(
hierarchy: HighlightedHierarchy
): Array<string | Array<string | VNode>> {
const breadcrumbVNodeArray: Array<string | Array<string | VNode>> = [];
function hierarchyToBreadcrumbs(
hit: Hit<AlgoliaRecord>,
components: AutocompleteComponents
): Array<JSX.Element | string> {
const breadcrumbArray: Array<JSX.Element | string> = [];
let addedLevels = 0;
if (!hit.hierarchy) {
return breadcrumbArray;
}
for (let i = 1; i < 7 && addedLevels < 3; ++i) {
if (hierarchy[`lvl${i}`] && hierarchy[`lvl${i}`].length > 0) {
if (hit.hierarchy[`lvl${i}`] && hit.hierarchy[`lvl${i}`].length > 0) {
if (addedLevels > 0) {
breadcrumbVNodeArray.push(' > ');
breadcrumbArray.push(' > ');
}
breadcrumbVNodeArray.push(hierarchy[`lvl${i}`]);
breadcrumbArray.push(
<components.Highlight hit={hit} attribute="description" />
);
++addedLevels;
}
}
return breadcrumbVNodeArray;
return breadcrumbArray;
}

function getSuggestionSnippet(
hit: Hit<AlgoliaRecord>,
components: AutocompleteComponents
): JSX.Element | string {
// If they are defined as `searchableAttributes`, 'description' and 'content' are always
// present in the `_snippetResult`, even if they don't match.
// So we need to have 1 check on the presence and 1 check on the match
const description = hit._snippetResult?.description;
const content = hit._snippetResult?.content;

// Take in priority props that matches the search
if (description && description.matchLevel === 'full') {
return <components.Snippet hit={hit} attribute="description" />;
}
if (content && content.matchLevel === 'full') {
return <components.Snippet hit={hit} attribute="content" />;
}

// Otherwise take the prop that was at least correctly returned
if (description && !content) {
return <components.Snippet hit={hit} attribute="description" />;
}
if (content) {
return <components.Snippet hit={hit} attribute="content" />;
}

// Otherwise raw value or empty
return hit.description || hit.content || '';
}
3 changes: 0 additions & 3 deletions frontend/src/types/record.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,3 @@
import type { VNode } from '@algolia/autocomplete-js';

export type AlgoliaRecord = {
objectID: string;

Expand All @@ -25,4 +23,3 @@ export type AlgoliaRecord = {
};

export type Hierarchy = { [lvl: string]: string };
export type HighlightedHierarchy = { [lvl: string]: Array<string | VNode> };
1 change: 1 addition & 0 deletions public/index.html
Original file line number Diff line number Diff line change
Expand Up @@ -179,6 +179,7 @@ <h2>Test content</h2>
colorSourceIcon: '#d6d6e7'
},
detached: { mediaQuery: '(max-width: 900px)' },
debug: true,
});`
.split('\n')
.map((l) => l.replace(/^\s{8}/, ''))
Expand Down
36 changes: 12 additions & 24 deletions yarn.lock
Original file line number Diff line number Diff line change
Expand Up @@ -2,42 +2,30 @@
# yarn lockfile v1


"@algolia/autocomplete-core@1.0.0-alpha.44":
version "1.0.0-alpha.44"
resolved "https://registry.yarnpkg.com/@algolia/autocomplete-core/-/autocomplete-core-1.0.0-alpha.44.tgz#e626dba45f5f3950d6beb0ab055395ef0f7e8bb2"
integrity sha512-2iMXthldMIDXtlbg9omRKLgg1bLo2ZzINAEqwhNjUeyj1ceEyL1ck6FY0VnJpf2LsjmNthHCz2BuFk+nYUeDNA==
"@algolia/autocomplete-core@1.0.0-alpha.45":
version "1.0.0-alpha.45"
resolved "https://registry.yarnpkg.com/@algolia/autocomplete-core/-/autocomplete-core-1.0.0-alpha.45.tgz#424f8e2cfca6c4c3682fa02ce7d122ae2eb0f0d1"
integrity sha512-Lol3IZSscUmZgkrq6DCcvImj1YW4NNHr8IkcARHTDsJvKo+G+mu7LrBLjUD/XEQZy2MAE0JbxrkShecdEpdjTw==
dependencies:
"@algolia/autocomplete-shared" "1.0.0-alpha.44"
"@algolia/autocomplete-shared" "1.0.0-alpha.45"

"@algolia/autocomplete-js@1.0.0-alpha.44":
version "1.0.0-alpha.44"
resolved "https://registry.yarnpkg.com/@algolia/autocomplete-js/-/autocomplete-js-1.0.0-alpha.44.tgz#a252bdbf7ab662dedcc05cfe53e318d6becd7bee"
integrity sha512-KmWhIvO/T5yS+kelZQrVMgAGKZKozoFHQM8VMrXK3a77i1uqTYFkg70HFIsiQ9kRGjB/EA0exNtm3/BwGkIIkw==
"@algolia/autocomplete-js@1.0.0-alpha.45":
version "1.0.0-alpha.45"
resolved "https://registry.yarnpkg.com/@algolia/autocomplete-js/-/autocomplete-js-1.0.0-alpha.45.tgz#b3835cbec368438f37637f065a11b375a92e300f"
integrity sha512-HAN3HzIstVGoL+ghtXtbg0i81Wn6rXag+CCdmniUuPIVTxsGLeSu4DK6AkZRpxoR8ozW+OeUj7tMnjziUZdhqw==
dependencies:
"@algolia/autocomplete-core" "1.0.0-alpha.44"
"@algolia/autocomplete-preset-algolia" "1.0.0-alpha.44"
"@algolia/autocomplete-shared" "1.0.0-alpha.44"
"@algolia/autocomplete-core" "1.0.0-alpha.45"
"@algolia/autocomplete-preset-algolia" "1.0.0-alpha.45"
"@algolia/autocomplete-shared" "1.0.0-alpha.45"
preact "^10.0.0"

"@algolia/autocomplete-preset-algolia@1.0.0-alpha.44":
version "1.0.0-alpha.44"
resolved "https://registry.yarnpkg.com/@algolia/autocomplete-preset-algolia/-/autocomplete-preset-algolia-1.0.0-alpha.44.tgz#0ea0b255d0be10fbe262e281472dd6e4619b62ba"
integrity sha512-DCHwo5ovzg9k2ejUolGNTLFnIA7GpsrkbNJTy1sFbMnYfBmeK8egZPZnEl7lBTr27OaZu7IkWpTepLVSztZyng==
dependencies:
"@algolia/autocomplete-shared" "1.0.0-alpha.44"

"@algolia/autocomplete-preset-algolia@1.0.0-alpha.45":
version "1.0.0-alpha.45"
resolved "https://registry.yarnpkg.com/@algolia/autocomplete-preset-algolia/-/autocomplete-preset-algolia-1.0.0-alpha.45.tgz#da23c1cc799893a58b23726508080a9a3f845b0e"
integrity sha512-IrTwyNKAiwz59/IYsn57rR6/BD71T5xA9tZ2jeX0QMyMlMpz24wqIUe7JdGkAlhcxw3H1qundjdwFQ8kISjCDA==
dependencies:
"@algolia/autocomplete-shared" "1.0.0-alpha.45"

"@algolia/autocomplete-shared@1.0.0-alpha.44":
version "1.0.0-alpha.44"
resolved "https://registry.yarnpkg.com/@algolia/autocomplete-shared/-/autocomplete-shared-1.0.0-alpha.44.tgz#db13902ad1667e455711b77d08cae1a0feafaa48"
integrity sha512-2oQZPERYV+yNx/yoVWYjZZdOqsitJ5dfxXJjL18yczOXH6ujnsq+DTczSrX+RjzjQdVeJ1UAG053EJQF/FOiMg==

"@algolia/autocomplete-shared@1.0.0-alpha.45":
version "1.0.0-alpha.45"
resolved "https://registry.yarnpkg.com/@algolia/autocomplete-shared/-/autocomplete-shared-1.0.0-alpha.45.tgz#3540dbc31f3e6f0e976409d568939783d18b948e"
Expand Down