diff --git a/packages/web-shared/components/Searchbar/Autocomplete.js b/packages/web-shared/components/Searchbar/Autocomplete.js index 4efa3501..03464d5e 100644 --- a/packages/web-shared/components/Searchbar/Autocomplete.js +++ b/packages/web-shared/components/Searchbar/Autocomplete.js @@ -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', @@ -57,124 +29,12 @@ const recentSearchesPlugin = createLocalStorageRecentSearchesPlugin({ }, }); -// Query Suggestion Item Render -function QuerySuggestionItem({ item, autocomplete, handleActionPress }) { - return ( - -
-
- -
-
-
- -
-
-
-
- -
-
- ); -} - -// 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 ( - -
-
- -
-
-
- -
-
-
-
- - -
-
- ); -} - 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); @@ -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, @@ -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); @@ -424,135 +264,6 @@ export default function Autocomplete({ ) : null} - - {autocompleteState.isOpen &&
} - {isResultsEmpty && - autocompleteState.collections.some((collection) => - ['pages', 'content'].includes(collection.source.sourceId) - ) && ( - - No results found - - )} - {autocompleteState.isOpen && - autocompleteState.collections.map((collection, index) => { - const { source, items } = collection; - // Rendering of Query Suggestions - if ( - ['querySuggestionsPlugin', 'recentSearchesPlugin'].includes( - collection.source.sourceId - ) - ) { - return ( -
- {collection.source.sourceId === 'querySuggestionsPlugin' && !inputProps.value && ( - - Trending Searches - - )} - {collection.source.sourceId === 'recentSearchesPlugin' && ( - - Search History - - )} - -
- ); - } - - // Rendering of regular items - - return autocompleteState.query !== '' ? ( -
- -
- ) : null; - })} - {autocompleteState.isOpen && autocompleteState.query === '' && searchState.searchFeed ? ( - - - - ) : null} -
); } diff --git a/packages/web-shared/components/Searchbar/Search.styles.js b/packages/web-shared/components/Searchbar/Search.styles.js index b8aca0b9..d139ac8a 100644 --- a/packages/web-shared/components/Searchbar/Search.styles.js +++ b/packages/web-shared/components/Searchbar/Search.styles.js @@ -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} @@ -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 { @@ -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%; diff --git a/packages/web-shared/components/Searchbar/SearchResults.js b/packages/web-shared/components/Searchbar/SearchResults.js new file mode 100644 index 00000000..e55ea65b --- /dev/null +++ b/packages/web-shared/components/Searchbar/SearchResults.js @@ -0,0 +1,298 @@ +import React, { useEffect, useState, createElement, Fragment } from 'react'; +import { useSearchParams } from 'react-router-dom'; + +import '@algolia/autocomplete-theme-classic'; +import { parseAlgoliaHitHighlight } from '@algolia/autocomplete-preset-algolia'; + +import { useSearchState } from '../../providers/SearchProvider'; +import { FeatureFeedProvider } from '../../providers'; +import Feed from '../FeatureFeed'; +import { ResourceCard, Box } from '../../ui-kit'; + +import { getURLFromType } from '../../utils'; +import { add as addBreadcrumb, useBreadcrumbDispatch } from '../../providers/BreadcrumbProvider'; +import { open as openModal, set as setModal, useModal } from '../../providers/ModalProvider'; +import { ClockCounterClockwise, MagnifyingGlass, CaretRight, X } from 'phosphor-react'; + +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; + }) + ); +} + +// Query Suggestion Item Render +function QuerySuggestionItem({ item, autocomplete, handleActionPress }) { + return ( + +
+
+ +
+
+
+ +
+
+
+
+ +
+
+ ); +} + +// 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 ( + +
+
+ +
+
+
+ +
+
+
+
+ + +
+
+ ); +} + +const SearchResults = ({ autocompleteState, autocomplete }) => { + const searchState = useSearchState(); + const [searchParams, setSearchParams] = useSearchParams(); + const dispatchBreadcrumb = useBreadcrumbDispatch(); + const [state, dispatch] = useModal(); + + const [isResultsEmpty, setIsResultsEmpty] = useState(false); + + 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]); + + 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; + }; + + if (!autocompleteState || !autocomplete) { + return null; + } + + // Makes SSR consistent on aria aspects + const inputProps = autocomplete.getInputProps({}); + + return ( + + {autocompleteState.isOpen &&
} + {isResultsEmpty && + autocompleteState.collections.some((collection) => + ['pages', 'content'].includes(collection.source.sourceId) + ) && ( + + No results found + + )} + {autocompleteState.isOpen && + autocompleteState.collections.map((collection, index) => { + const { source, items } = collection; + // Rendering of Query Suggestions + if ( + ['querySuggestionsPlugin', 'recentSearchesPlugin'].includes(collection.source.sourceId) + ) { + return ( +
+ {collection.source.sourceId === 'querySuggestionsPlugin' && !inputProps.value && ( + + Trending Searches + + )} + {collection.source.sourceId === 'recentSearchesPlugin' && ( + + Search History + + )} + +
+ ); + } + + // Rendering of regular items + + return autocompleteState.query !== '' ? ( +
+ +
+ ) : null; + })} + {autocompleteState.isOpen && autocompleteState.query === '' && searchState.searchFeed ? ( + + + + ) : null} +
+ ); +}; + +export default SearchResults; diff --git a/packages/web-shared/components/Searchbar/Searchbar.js b/packages/web-shared/components/Searchbar/Searchbar.js index 68e139ee..55868294 100644 --- a/packages/web-shared/components/Searchbar/Searchbar.js +++ b/packages/web-shared/components/Searchbar/Searchbar.js @@ -9,6 +9,7 @@ import { useSearchState } from '../../providers/SearchProvider'; import Profile from '../Profile'; import Autocomplete from '../Searchbar/Autocomplete'; +import SearchResults from '../Searchbar/SearchResults'; import Styled from './Search.styles'; @@ -122,39 +123,41 @@ const Searchbar = (props = {}) => { {...props} > - - - - - {isMobile && autocompleteState.isOpen ? ( - - ) : ( - - )} - - - - - {showTextPrompt ? textPrompt : null} - - - - - {currentUser?.profile?.photo?.uri ? ( - - ) : ( - - - - )} + + + + + + {isMobile && autocompleteState.isOpen ? ( + + ) : ( + + )} + + + + + {showTextPrompt ? textPrompt : null} + + + + + {currentUser?.profile?.photo?.uri ? ( + + ) : ( + + + + )} + + - {showProfile ? : null} );