Skip to content

Commit

Permalink
do additional clean up work of the content search component
Browse files Browse the repository at this point in the history
  • Loading branch information
fabiankaegy committed Jun 7, 2024
1 parent 77ede04 commit 2ef2087
Show file tree
Hide file tree
Showing 3 changed files with 159 additions and 61 deletions.
80 changes: 24 additions & 56 deletions components/content-search/index.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -7,21 +7,19 @@ import styled from '@emotion/styled';
import {useMergeRefs} from '@wordpress/compose';
import SearchItem, { Suggestion } from './SearchItem';
import { StyledComponentContext } from '../styled-components-context';

import type { SearchResult, ContentSearchProps } from './types';
import type { ContentSearchProps } from './types';
import type { WP_REST_API_User, WP_REST_API_Post, WP_REST_API_Term } from 'wp-types';

import {
QueryClient,
QueryClientProvider,
useInfiniteQuery,
} from '@tanstack/react-query';
import { useOnClickOutside } from '../../hooks/use-on-click-outside';

import { normalizeResults } from './utils';

const queryClient = new QueryClient();

const NAMESPACE = 'tenup-content-search';

// Equalize height of list icons to match loader in order to reduce jumping.
const listMinHeight = '46px';

Expand Down Expand Up @@ -60,7 +58,6 @@ const StyledSearchControl = styled(SearchControl)`
width: 100%;
`;


const ContentSearch: React.FC<ContentSearchProps> = ({
onSelectItem = () => {
console.log('Select!'); // eslint-disable-line no-console
Expand All @@ -83,20 +80,6 @@ const ContentSearch: React.FC<ContentSearchProps> = ({

const searchContainer = useRef<HTMLDivElement>(null);

const filterResults = useCallback(
(results: SearchResult[]) => {
return results.filter((result: SearchResult) => {
let keep = true;

if (excludeItems.length) {
keep = excludeItems.every((item) => item.id !== result.id);
}

return keep;
});
},
[excludeItems],
);

/**
* handleSelection
Expand All @@ -106,7 +89,7 @@ const ContentSearch: React.FC<ContentSearchProps> = ({
*
* @param {number} item item
*/
const handleOnNavigate = (item: number) => {
const handleSuggestionSelection = (item: number) => {
if (item === 0) {
setSelectedItem(null);
}
Expand Down Expand Up @@ -163,34 +146,6 @@ const ContentSearch: React.FC<ContentSearchProps> = ({
[perPage, contentTypes, mode, queryFilter],
);

/**
* Depending on the mode value, this method normalizes the format
* of the result array.
*
* @param {string} mode ContentPicker mode.
* @param {SearchResult[]} result The array to be normalized.
*
* @returns {SearchResult[]} The normalizes array.
*/
const normalizeResults = useCallback(
(result: SearchResult[] = []): SearchResult[] => {
const normalizedResults = filterResults(result);

if (mode === 'user') {
return normalizedResults.map((item) => ({
id: item.id,
subtype: mode,
title: item.name || '',
type: mode,
url: item.link || '',
} as SearchResult));
}

return normalizedResults;
},
[mode, filterResults],
);

const clickOutsideRef = useOnClickOutside(() => {
setIsFocused(false);
});
Expand Down Expand Up @@ -227,8 +182,21 @@ const ContentSearch: React.FC<ContentSearchProps> = ({
10,
);

const results = await response.json();
const normalizedResults = normalizeResults(results);
let results: WP_REST_API_User[] | WP_REST_API_Post[] | WP_REST_API_Term[];

switch (mode) {
case 'user':
results = await response.json() as WP_REST_API_User[];
break;
case 'post':
results = await response.json() as WP_REST_API_Post[];
break;
case 'term':
results = await response.json() as WP_REST_API_Term[];
break;
}

const normalizedResults = normalizeResults({results, excludeItems, mode});

const hasNextPage = totalPages > pageParam;
const hasPreviousPage = pageParam > 1;
Expand All @@ -252,7 +220,7 @@ const ContentSearch: React.FC<ContentSearchProps> = ({
const hasInitialResults = fetchInitialResults && isFocused;

return (
<StyledNavigableMenu ref={mergedRef} onNavigate={handleOnNavigate} orientation="vertical">
<StyledNavigableMenu ref={mergedRef} onNavigate={handleSuggestionSelection} orientation="vertical">
<StyledSearchControl
value={searchString}
onChange={(newSearchString) => {
Expand All @@ -269,12 +237,12 @@ const ContentSearch: React.FC<ContentSearchProps> = ({

{hasSearchString || hasInitialResults ? (
<>
<List className={`${NAMESPACE}-list`}>
<List className={`tenup-content-search-list`}>
{status === 'pending' && <StyledSpinner onPointerEnterCapture={null} onPointerLeaveCapture={null} />}

{!!error || (!isFetching && !hasSearchResults) && (
<li
className={`${NAMESPACE}-list-item components-button`}
className={`tenup-content-search-list-item components-button`}
style={{
color: 'inherit',
cursor: 'default',
Expand All @@ -288,7 +256,7 @@ const ContentSearch: React.FC<ContentSearchProps> = ({
status === 'success' &&
searchResults &&
searchResults.map((item, index) => {
if (!item || !item.title.length) {
if (!item || !item?.title?.length) {
return null;
}

Expand All @@ -301,7 +269,7 @@ const ContentSearch: React.FC<ContentSearchProps> = ({
return (
<li
key={item.id}
className={`${NAMESPACE}-list-item`}
className={`tenup-content-search-list-item`}
style={{
marginBottom: '0',
}}
Expand Down
19 changes: 14 additions & 5 deletions components/content-search/types.ts
Original file line number Diff line number Diff line change
@@ -1,3 +1,8 @@
import type {
WP_REST_API_User,
WP_REST_API_Post,
WP_REST_API_Term,
} from 'wp-types';
import type { Suggestion } from './SearchItem';

export interface SearchResult {
Expand All @@ -13,8 +18,8 @@ export interface SearchResult {
export interface QueryArgs {
perPage: number;
page: number;
contentTypes: string[];
mode: string;
contentTypes: Array<string>;
mode: ContentSearchMode;
keyword: string;
}

Expand All @@ -24,17 +29,19 @@ export interface RenderItemComponentProps {
searchTerm?: string;
isSelected?: boolean;
id?: string;
contentTypes: string[];
contentTypes: Array<string>;
renderType?: (suggestion: Suggestion) => string;
}

export type ContentSearchMode = 'post' | 'user' | 'term';

export interface ContentSearchProps {
onSelectItem: (item: Suggestion) => void;
placeholder?: string;
label?: string;
hideLabelFromVision?: boolean;
contentTypes?: string[];
mode?: 'post' | 'user' | 'term';
contentTypes?: Array<string>;
mode?: ContentSearchMode;
perPage?: number;
queryFilter?: (query: string, args: QueryArgs) => string;
excludeItems?: {
Expand All @@ -44,3 +51,5 @@ export interface ContentSearchProps {
renderItem?: (props: RenderItemComponentProps) => JSX.Element;
fetchInitialResults?: boolean;
}

export type Modify<T, R> = Omit<T, keyof R> & R;
121 changes: 121 additions & 0 deletions components/content-search/utils.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,121 @@
import type { ContentSearchMode, Modify } from "./types";
import type { WP_REST_API_User, WP_REST_API_Post, WP_REST_API_Term } from "wp-types";

interface IdentifiableObject extends Object {
id: number;
};

interface FilterResultsArgs {
results: WP_REST_API_User[] | WP_REST_API_Post[] | WP_REST_API_Term[];
excludeItems: Array<IdentifiableObject>;
}

/**
* Filters results.
*
* @returns Filtered results.
*/
export const filterResults = ({ results, excludeItems }: FilterResultsArgs) => {
return results.filter((result) => {
let keep = true;

if (excludeItems.length) {
keep = excludeItems.every((item) => item.id !== result.id);
}

return keep;
});
};

interface PrepareSearchQueryArgs {
keyword: string;
page: number;
mode: ContentSearchMode;
perPage: number;
contentTypes: Array<string>;
queryFilter: (queryString: string, options: {
perPage: number;
page: number;
contentTypes: Array<string>;
mode: ContentSearchMode;
keyword: string;
}) => string;
}

/**
* Prepares a search query based on the given keyword and page number.
*
* @returns The prepared search query.
*/
export const prepareSearchQuery = ({ keyword, page, mode, perPage, contentTypes, queryFilter }: PrepareSearchQueryArgs): string => {
let searchQuery;

switch (mode) {
case 'user':
searchQuery = `wp/v2/users/?search=${keyword}`;
break;
default:
searchQuery = `wp/v2/search/?search=${keyword}&subtype=${contentTypes.join(
',',
)}&type=${mode}&_embed&per_page=${perPage}&page=${page}`;
break;
}

return queryFilter(searchQuery, {
perPage,
page,
contentTypes,
mode,
keyword,
});
};

interface NormalizeResultsArgs {
mode: ContentSearchMode;
results: WP_REST_API_Post[] | WP_REST_API_User[] | WP_REST_API_Term[]
excludeItems: Array<IdentifiableObject>;
}

/**
* Depending on the mode value, this method normalizes the format
* of the result array.
*
* @returns Normalized results.
*/
export const normalizeResults = ({ mode, results, excludeItems }: NormalizeResultsArgs): Array<{
id: number;
subtype: ContentSearchMode;
title: string;
type: ContentSearchMode;
url: string;
}> => {
const normalizedResults = filterResults({ results, excludeItems });

return normalizedResults.map((item) => {

let title: string;

switch (mode) {
case 'post':
const postItem = item as unknown as Modify<WP_REST_API_Post, { title: string }>;
title = postItem.title;
break;
case 'term':
const termItem = item as WP_REST_API_Term;
title = termItem.name;
break;
case 'user':
const userItem = item as WP_REST_API_User;
title = userItem.name;
break;
}

return {
id: item.id,
subtype: mode,
title: title,
type: mode,
url: item.link,
};
});
};

0 comments on commit 2ef2087

Please sign in to comment.