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

QF-993 Search as you type #2251

Open
wants to merge 18 commits into
base: testing
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from 3 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
35 changes: 28 additions & 7 deletions src/components/CommandBar/CommandBarBody/index.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@ import CommandsList, { Command } from '../CommandsList';

import styles from './CommandBarBody.module.scss';

import { getNewSearchResults } from '@/api';
import DataFetcher from '@/components/DataFetcher';
import TarteelAttribution from '@/components/TarteelAttribution/TarteelAttribution';
import VoiceSearchBodyContainer from '@/components/TarteelVoiceSearch/BodyContainer';
Expand All @@ -18,8 +19,9 @@ import useDebounce from '@/hooks/useDebounce';
import IconSearch from '@/icons/search.svg';
import { selectRecentNavigations } from '@/redux/slices/CommandBar/state';
import { selectIsCommandBarVoiceFlowStarted } from '@/redux/slices/voiceSearch';
import { SearchMode } from '@/types/Search/SearchRequestParams';
import SearchQuerySource from '@/types/SearchQuerySource';
import { makeSearchResultsUrl } from '@/utils/apiPaths';
import { makeNewSearchResultsUrl } from '@/utils/apiPaths';
import { areArraysEqual } from '@/utils/array';
import { logButtonClick, logTextSearchQuery } from '@/utils/eventLogger';
import { SearchResponse } from 'types/ApiResponses';
Expand All @@ -28,27 +30,27 @@ import { SearchNavigationType } from 'types/SearchNavigationResult';
const NAVIGATE_TO = [
{
name: 'Juz 1',
key: 1,
key: '1',
resultType: SearchNavigationType.JUZ,
},
{
name: 'Hizb 1',
key: 1,
key: '1',
resultType: SearchNavigationType.HIZB,
},
{
name: 'Rub el Hizb 1',
key: 1,
key: '1',
resultType: SearchNavigationType.RUB_EL_HIZB,
},
{
name: 'Page 1',
key: 1,
key: '1',
resultType: SearchNavigationType.PAGE,
},
{
name: 'Surah Yasin',
key: 36,
key: '36',
resultType: SearchNavigationType.SURAH,
},
{
Expand Down Expand Up @@ -112,6 +114,15 @@ const CommandBarBody: React.FC = () => {
[recentNavigations, t],
);

const quickSearchFetcher = useCallback(() => {
return getNewSearchResults({
mode: SearchMode.Quick,
query: searchQuery,
getText: 1,
highlight: 1,
});
}, [searchQuery]);

/**
* This function will be used by DataFetcher and will run only when there is no API error
* or the connections is offline. When we receive the response from DataFetcher,
Expand Down Expand Up @@ -198,8 +209,18 @@ const CommandBarBody: React.FC = () => {
<VoiceSearchBodyContainer isCommandBar />
) : (
<DataFetcher
queryKey={searchQuery ? makeSearchResultsUrl({ query: searchQuery }) : null}
queryKey={
searchQuery
? makeNewSearchResultsUrl({
mode: SearchMode.Quick,
query: searchQuery,
getText: 1,
highlight: 1,
})
: null
}
render={dataFetcherRender}
fetcher={quickSearchFetcher}
/>
)}
</div>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -24,25 +24,26 @@ $itemHeight: calc(1.5 * var(--spacing-mega));
display: flex;
align-items: center;
justify-content: space-between;
height: $itemHeight;
min-height: $itemHeight;
font-size: var(--font-size-normal);
padding-block-start: 0;
padding-block-end: 0;
padding-inline-start: var(--spacing-xsmall);
padding-inline-end: var(--spacing-xsmall);
cursor: pointer;
color: var(--color-text-faded);
white-space: nowrap;
// white-space: nowrap;
osamasayed marked this conversation as resolved.
Show resolved Hide resolved
position: relative;
z-index: var(--z-index-default);
&.selected {
color: var(--color-text-default);
background: var(--color-background-alternative-faint);
}
}

.highlight {
transition: transform var(--transition-fast);
height: $itemHeight;
min-height: $itemHeight;
width: 100%;
position: absolute;
border-radius: var(--border-radius-rounded);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,11 @@
}

.name {
white-space: nowrap;
overflow: hidden;
text-overflow: ellipsis;
// white-space: nowrap;
// overflow: hidden;
// text-overflow: ellipsis;
osamasayed marked this conversation as resolved.
Show resolved Hide resolved
em {
font-weight: var(--font-weight-bold);
text-decoration: underline;
}
}
44 changes: 34 additions & 10 deletions src/components/CommandBar/CommandsList/CommandPrefix/index.tsx
Original file line number Diff line number Diff line change
@@ -1,31 +1,55 @@
import React from 'react';
/* eslint-disable react/no-danger */
import React, { useContext } from 'react';

import useTranslation from 'next-translate/useTranslation';

import styles from './CommandPrefix.module.scss';

import DataContext from '@/contexts/DataContext';
import NavigateIcon from '@/icons/east.svg';
import { getSearchNavigationResult } from '@/utils/search';
import { SearchNavigationType } from 'types/SearchNavigationResult';

interface Props {
name: string;
type: SearchNavigationType;
isVoiceSearch: boolean;
navigationKey: string | number;
}

const CommandPrefix: React.FC<Props> = ({ name, type }) => {
const { t } = useTranslation('common');
const CommandPrefix: React.FC<Props> = ({ name, type, isVoiceSearch, navigationKey }) => {
const { t, lang } = useTranslation('common');
const chapterData = useContext(DataContext);
const getName = () => {
if (isVoiceSearch) return name;

if (type === SearchNavigationType.SEARCH_PAGE) {
return t('search-for', {
searchQuery: name,
});
}

const navigation = getSearchNavigationResult(
chapterData,
{ resultType: type, key: navigationKey, name },
t,
lang,
true,
);
return navigation?.name;
};

return (
<div className={styles.container}>
<span className={styles.commandPrefix}>
<NavigateIcon />
</span>
<p className={styles.name}>
{type === SearchNavigationType.SEARCH_PAGE
? t('search-for', {
searchQuery: name,
})
: name}
</p>
<p
dangerouslySetInnerHTML={{
__html: getName(),
}}
className={styles.name}
/>
</div>
);
};
Expand Down
20 changes: 13 additions & 7 deletions src/components/CommandBar/CommandsList/index.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -26,8 +26,10 @@ import { SearchNavigationResult } from 'types/SearchNavigationResult';

export interface Command extends SearchNavigationResult {
group: string;
name: string;
index?: number;
isClearable?: boolean;
isVoiceSearch?: boolean;
}

interface Props {
Expand Down Expand Up @@ -133,7 +135,6 @@ const CommandsList: React.FC<Props> = ({ commandGroups: { groups, numberOfComman
return (
<ul role="listbox">
<div
className={styles.highlight}
style={{
transform: highlightOffset ? `translateY(${highlightOffset}px)` : `translateY(100%)`,
}}
Expand All @@ -147,24 +148,29 @@ const CommandsList: React.FC<Props> = ({ commandGroups: { groups, numberOfComman
</div>
<ul role="group" aria-labelledby={commandGroup}>
{groups[commandGroup].map((command) => {
const { name, resultType } = command;
const isSelected = selectedCommandIndex === command.index;
const { name, resultType, key, index, isVoiceSearch } = command;
const isSelected = selectedCommandIndex === index;
return (
<li
ref={isSelected ? selectedItemRef : null}
role="option"
aria-selected={isSelected}
key={command.index}
key={index}
className={classNames(styles.command, { [styles.selected]: isSelected })}
onClick={() => navigateToLink(command)}
onMouseOver={() => setSelectedCommandIndex(command.index)}
onMouseOver={() => setSelectedCommandIndex(index)}
>
<CommandPrefix name={name} type={resultType} />
<CommandPrefix
navigationKey={key}
isVoiceSearch={isVoiceSearch}
name={name}
type={resultType}
/>
<div className={styles.keyboardInputContainer}>
<CommandControl
isClearable={command.isClearable}
isSelected={isSelected}
commandKey={command.key}
commandKey={key}
onRemoveCommandClicked={onRemoveCommandClicked}
/>
</div>
Expand Down
40 changes: 25 additions & 15 deletions src/components/Navbar/SearchDrawer/SearchDrawer.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -8,17 +8,20 @@ import { shallowEqual, useDispatch, useSelector } from 'react-redux';

import SearchDrawerHeader from './Header';

import { getNewSearchResults } from '@/api';
import Drawer, { DrawerType } from '@/components/Navbar/Drawer';
import Spinner from '@/dls/Spinner/Spinner';
import useDebounce from '@/hooks/useDebounce';
import useFocus from '@/hooks/useFocusElement';
import { selectNavbar } from '@/redux/slices/navbar';
import { selectSelectedTranslations } from '@/redux/slices/QuranReader/translations';
import { selectIsSearchDrawerVoiceFlowStarted } from '@/redux/slices/voiceSearch';
import { SearchMode } from '@/types/Search/SearchRequestParams';
import SearchService from '@/types/Search/SearchService';
import SearchQuerySource from '@/types/SearchQuerySource';
import { areArraysEqual } from '@/utils/array';
import { logButtonClick } from '@/utils/eventLogger';
import { addToSearchHistory, searchGetResults } from '@/utils/search';
import { logButtonClick, logTextSearchQuery } from '@/utils/eventLogger';
import { addToSearchHistory } from '@/utils/search';
import { SearchResponse } from 'types/ApiResponses';

const SearchBodyContainer = dynamic(() => import('@/components/Search/SearchBodyContainer'), {
Expand All @@ -33,8 +36,6 @@ const VoiceSearchBodyContainer = dynamic(
},
);

const FIRST_PAGE_NUMBER = 1;
const PAGE_SIZE = 10;
const DEBOUNCING_PERIOD_MS = 1000;

const SearchDrawer: React.FC = () => {
Expand All @@ -60,17 +61,26 @@ const SearchDrawer: React.FC = () => {
// only when the search query has a value we call the API.
if (debouncedSearchQuery) {
addToSearchHistory(dispatch, debouncedSearchQuery, SearchQuerySource.SearchDrawer);
searchGetResults(
SearchQuerySource.SearchDrawer,
debouncedSearchQuery,
FIRST_PAGE_NUMBER,
PAGE_SIZE,
setIsSearching,
setHasError,
setSearchResult,
null,
selectedTranslations?.length && selectedTranslations.join(','),
);
setIsSearching(true);
logTextSearchQuery(debouncedSearchQuery, SearchQuerySource.SearchDrawer);
getNewSearchResults({
mode: SearchMode.Quick,
query: debouncedSearchQuery,
getText: 1,
highlight: 1,
})
.then((response) => {
setSearchResult({
...response,
service: SearchService.KALIMAT,
});
})
.catch(() => {
setHasError(true);
})
.finally(() => {
setIsSearching(false);
});
}
}, [debouncedSearchQuery, selectedTranslations, dispatch]);

Expand Down
16 changes: 15 additions & 1 deletion src/components/Search/NavigationItem/index.tsx
Original file line number Diff line number Diff line change
@@ -1,7 +1,10 @@
/* eslint-disable react/no-danger */
import React, { useContext } from 'react';

import useTranslation from 'next-translate/useTranslation';

import KalimatNavigationSearchResultItem from '../SearchResults/KalimatNavigationSearchResultItem';

import DataContext from '@/contexts/DataContext';
import Button from '@/dls/Button/Button';
import SearchService from '@/types/Search/SearchService';
Expand Down Expand Up @@ -33,7 +36,7 @@ const NavigationItem: React.FC<Props> = ({
);

const result = isKalimatService
? getSearchNavigationResult(chaptersData, navigation, t, lang)
? getSearchNavigationResult(chaptersData, navigation, t, lang, true)
: navigation;
const getKalimatResultSuffix = () => {
if (navigation.resultType === SearchNavigationType.SURAH) {
Expand All @@ -57,6 +60,17 @@ const NavigationItem: React.FC<Props> = ({
});
};

if (isKalimatService && result.resultType === SearchNavigationType.AYAH) {
return (
<KalimatNavigationSearchResultItem
key={result.key}
name={result.name}
resultKey={result.key}
source={isSearchDrawer ? SearchQuerySource.SearchDrawer : SearchQuerySource.SearchPage}
/>
);
}

return (
<Button onClick={onNavigationItemClicked} href={url} suffix={suffix}>
{result.name}
Expand Down
Loading