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

fix: Stop double scroll issue #151

Merged
merged 3 commits into from
Jan 29, 2024
Merged
Show file tree
Hide file tree
Changes from 2 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
299 changes: 5 additions & 294 deletions packages/web-shared/components/Searchbar/Autocomplete.js
Original file line number Diff line number Diff line change
@@ -1,49 +1,21 @@
import React, { useEffect, useState, useRef, useMemo, createElement, Fragment } from 'react';
import { useSearchParams } from 'react-router-dom';
import { useNavigate } from 'react-router-dom';
import { ClockCounterClockwise, MagnifyingGlass, CaretDown, CaretRight, X } from 'phosphor-react';
import React, { useEffect, useRef, useMemo } from 'react';
import { X } from 'phosphor-react';

import algoliasearch from 'algoliasearch/lite';
import { createAutocomplete } from '@algolia/autocomplete-core';
import { getAlgoliaResults, parseAlgoliaHitHighlight } from '@algolia/autocomplete-preset-algolia';
import { getAlgoliaResults } from '@algolia/autocomplete-preset-algolia';
import { createQuerySuggestionsPlugin } from '@algolia/autocomplete-plugin-query-suggestions';
import { createLocalStorageRecentSearchesPlugin } from '@algolia/autocomplete-plugin-recent-searches';
import '@algolia/autocomplete-theme-classic';

import { FeatureFeedProvider } from '../../providers';
import Feed from '../FeatureFeed';
import { ResourceCard, Box } from '../../ui-kit';

import { useSearchState } from '../../providers/SearchProvider';
import { getURLFromType } from '../../utils';
import Styled from './Search.styles';
import { add as addBreadcrumb, useBreadcrumbDispatch } from '../../providers/BreadcrumbProvider';
import { open as openModal, set as setModal, useModal } from '../../providers/ModalProvider';

const MOBILE_BREAKPOINT = 428;
const appId = "Z0GWPR8XBE";
const apiKey = "251ec8d76f6c62ac793c1337b39bda58";
const appId = 'Z0GWPR8XBE';
const apiKey = '251ec8d76f6c62ac793c1337b39bda58';
const searchClient = algoliasearch(appId, apiKey);

function Hit({ hit }) {
return hit?.title;
}

// Highlight text render
function Highlight({ hit, attribute, tagName = 'mark' }) {
return createElement(
Fragment,
{},
parseAlgoliaHitHighlight({ hit, attribute }).map(({ value, isHighlighted }, index) => {
if (isHighlighted) {
return createElement(tagName, { key: index }, value);
}

return value;
})
);
}

// Recent Searches Index Definition
const recentSearchesPlugin = createLocalStorageRecentSearchesPlugin({
key: 'navbar',
Expand All @@ -57,124 +29,12 @@ const recentSearchesPlugin = createLocalStorageRecentSearchesPlugin({
},
});

// Query Suggestion Item Render
function QuerySuggestionItem({ item, autocomplete, handleActionPress }) {
return (
<Box className="aa-ItemWrapper" px="xs">
<div className="aa-ItemContent">
<div className="aa-ItemIcon aa-ItemIcon--noBorder">
<MagnifyingGlass size={24} weight="bold" />
</div>
<div className="aa-ItemContentBody">
<div className="aa-ItemContentTitle">
<Hit hit={item} />
</div>
</div>
</div>
<div className="aa-ItemActions">
<button
className="aa-ItemActionButton"
title={`Fill query with "${item.title}"`}
onClick={(event) => {
event.preventDefault();
event.stopPropagation();
autocomplete.setQuery(item.title);
autocomplete.refresh();
}}
>
<CaretRight size={24} weight="bold" />
</button>
</div>
</Box>
);
}

// Recent Search Item Render
function PastQueryItem({ item, autocomplete }) {
function onRemove(id) {
recentSearchesPlugin.data.removeItem(id);
autocomplete.refresh();
}

function onTapAhead(item) {
autocomplete.setQuery(item.label);
autocomplete.setIsOpen(true);
autocomplete.refresh();
}
return (
<Box className="aa-ItemWrapper" px="xs">
<div className="aa-ItemContent">
<div className="aa-ItemIcon aa-ItemIcon--noBorder">
<ClockCounterClockwise size={24} weight="bold" />
</div>
<div className="aa-ItemContentBody">
<div className="aa-ItemContentTitle">
<Highlight hit={item} attribute="label" />
</div>
</div>
</div>
<div className="aa-ItemActions">
<button
className="aa-ItemActionButton"
title="Remove this search"
onClick={(event) => {
event.preventDefault();
event.stopPropagation();
onRemove(item.id);
}}
>
<svg viewBox="0 0 24 24" fill="currentColor">
<path d="M18 7v13c0 0.276-0.111 0.525-0.293 0.707s-0.431 0.293-0.707 0.293h-10c-0.276 0-0.525-0.111-0.707-0.293s-0.293-0.431-0.293-0.707v-13zM17 5v-1c0-0.828-0.337-1.58-0.879-2.121s-1.293-0.879-2.121-0.879h-4c-0.828 0-1.58 0.337-2.121 0.879s-0.879 1.293-0.879 2.121v1h-4c-0.552 0-1 0.448-1 1s0.448 1 1 1h1v13c0 0.828 0.337 1.58 0.879 2.121s1.293 0.879 2.121 0.879h10c0.828 0 1.58-0.337 2.121-0.879s0.879-1.293 0.879-2.121v-13h1c0.552 0 1-0.448 1-1s-0.448-1-1-1zM9 5v-1c0-0.276 0.111-0.525 0.293-0.707s0.431-0.293 0.707-0.293h4c0.276 0 0.525 0.111 0.707 0.293s0.293 0.431 0.293 0.707v1zM9 11v6c0 0.552 0.448 1 1 1s1-0.448 1-1v-6c0-0.552-0.448-1-1-1s-1 0.448-1 1zM13 11v6c0 0.552 0.448 1 1 1s1-0.448 1-1v-6c0-0.552-0.448-1-1-1s-1 0.448-1 1z" />
</svg>
</button>
<button
className="aa-ItemActionButton"
title={`Fill query with "${item.label}"`}
onClick={(event) => {
event.preventDefault();
event.stopPropagation();
onTapAhead(item);
}}
>
<CaretRight size={24} weight="bold" />
</button>
</div>
</Box>
);
}

export default function Autocomplete({
autocompleteState,
setAutocompleteState,
setShowTextPrompt,
getAutocompleteInstance,
}) {
const [searchParams, setSearchParams] = useSearchParams();
const dispatchBreadcrumb = useBreadcrumbDispatch();
const [state, dispatch] = useModal();

const [isResultsEmpty, setIsResultsEmpty] = useState(false);

const handleActionPress = (item) => {
if (searchParams.get('id') !== getURLFromType(item)) {
dispatchBreadcrumb(
addBreadcrumb({
url: `?id=${getURLFromType(item)}`,
title: item.title,
})
);
setSearchParams(`?id=${getURLFromType(item)}`);
}
if (state.modal) {
const url = getURLFromType(item);
dispatch(setModal(url));
dispatch(openModal());
}
};
const handleStaticActionPress = (item) => {
window.location.href = item.url;
};
const navigate = useNavigate();
const searchState = useSearchState();
const inputRef = useRef(null);

Expand Down Expand Up @@ -227,15 +87,6 @@ export default function Autocomplete({
autocomplete.refresh();
};

const handlePanelDropdown = () => {
const updatedAutocompleteState = { ...autocompleteState };
updatedAutocompleteState.isOpen = !updatedAutocompleteState.isOpen;
setAutocompleteState(updatedAutocompleteState);

autocomplete.setIsOpen(!autocompleteState.isOpen);
inputRef.current?.[autocompleteState.isOpen ? 'blur' : 'focus']();
};

// Query Suggesion Index Definition
const querySuggestionsPlugin = createQuerySuggestionsPlugin({
searchClient,
Expand Down Expand Up @@ -394,17 +245,6 @@ export default function Autocomplete({
};
}, [autocompleteState.isOpen, autocomplete, setShowTextPrompt]);

useEffect(() => {
const pagesItems =
autocompleteState.collections.find((collection) => collection.source.sourceId === 'pages')
?.items || [];
const contentItems =
autocompleteState.collections.find((collection) => collection.source.sourceId === 'content')
?.items || [];

setIsResultsEmpty(pagesItems.length === 0 && contentItems.length === 0);
}, [autocompleteState.collections]);

useEffect(() => {
if (getAutocompleteInstance) {
getAutocompleteInstance(autocomplete);
Expand All @@ -424,135 +264,6 @@ export default function Autocomplete({
</div>
) : null}
</form>
<Box
id="panel"
className="aa-Panel"
dropdown={autocompleteState.isOpen}
{...autocomplete.getPanelProps({})}
>
{autocompleteState.isOpen && <div id="panel-top"></div>}
{isResultsEmpty &&
autocompleteState.collections.some((collection) =>
['pages', 'content'].includes(collection.source.sourceId)
) && (
<Box
padding="xs"
fontWeight="500"
color="base.gray"
textAlign="center"
fontStyle="italic"
>
No results found
</Box>
)}
{autocompleteState.isOpen &&
autocompleteState.collections.map((collection, index) => {
const { source, items } = collection;
// Rendering of Query Suggestions
if (
['querySuggestionsPlugin', 'recentSearchesPlugin'].includes(
collection.source.sourceId
)
) {
return (
<div key={`source-${index}`} className="aa-Source">
{collection.source.sourceId === 'querySuggestionsPlugin' && !inputProps.value && (
<Box padding="xs" fontWeight="600" color="base.gray">
Trending Searches
</Box>
)}
{collection.source.sourceId === 'recentSearchesPlugin' && (
<Box padding="xs" fontWeight="600" color="base.gray">
Search History
</Box>
)}
<ul className="aa-List" {...autocomplete.getListProps()}>
{items.map((item, index) => (
<li
key={`${item.objectID ? item.objectID : index}-${
collection.source.sourceId
}`}
className="aa-Item"
{...autocomplete.getItemProps({
item,
source,
})}
>
{collection.source.sourceId === 'querySuggestionsPlugin' && (
<QuerySuggestionItem
item={item}
autocomplete={autocomplete}
handleActionPress={handleActionPress}
{...autocomplete.getItemProps({
item,
source,
})}
/>
)}
{collection.source.sourceId === 'recentSearchesPlugin' && (
<PastQueryItem
item={item}
autocomplete={autocomplete}
{...autocomplete.getItemProps({
item,
source,
})}
/>
)}
</li>
))}
</ul>
</div>
);
}

// Rendering of regular items

return autocompleteState.query !== '' ? (
<div key={`source-${index}`} className="aa-Source">
<ul className="aa-List" {...autocomplete.getListProps()}>
{items.map((item) => (
<Box
as="li"
borderRadius="0"
padding="0"
key={item.objectID}
className="aa-Item"
{...autocomplete.getItemProps({
item,
source,
})}
>
<ResourceCard
leadingAsset={item?.coverImage}
title={item?.title}
onClick={() => {
if (collection.source.sourceId === 'pages') {
return handleStaticActionPress(item);
}
return handleActionPress(item);
}}
background="none"
/>
</Box>
))}
</ul>
</div>
) : null;
})}
{autocompleteState.isOpen && autocompleteState.query === '' && searchState.searchFeed ? (
<Box className="empty-feed">
<FeatureFeedProvider
Component={Feed}
options={{
variables: {
itemId: searchState.searchFeed,
},
}}
/>
</Box>
) : null}
</Box>
</div>
);
}
8 changes: 4 additions & 4 deletions packages/web-shared/components/Searchbar/Search.styles.js
Original file line number Diff line number Diff line change
Expand Up @@ -48,12 +48,9 @@ const showPanel = ({ dropdown }) => {
};

const Wrapper = withTheme(styled.div`
align-items: center;
background: ${themeGet('colors.base.white')};
box-shadow: ${themeGet('shadows.medium')};
display: flex;
height: 60px;
justify-content: space-between;
width: 100%;
z-index: 100;
${showDropdown}
Expand All @@ -72,6 +69,9 @@ const Wrapper = withTheme(styled.div`
overflow-x: hidden;
border-radius: 0px 0px 15px 15px;
${showPanel}
position: relative;
display: inline-block;
margin: 0px;
}

.aa-Form:focus-within {
Expand All @@ -91,12 +91,12 @@ const TextPrompt = withTheme(styled.div`
display: flex;
overflow: hidden;
pointer-events: none;
position: absolute;
text-overflow: ellipsis;
top: 50%;
transform: translate(0, -50%);
white-space: nowrap;
width: 100%;
position: absolute;

@media screen and (max-width: ${themeGet('breakpoints.md')}) {
max-width: 70%;
Expand Down
Loading
Loading