Skip to main content

Channel Search

How-to Guide for Customizing ChannelSearch#

In this example, we will customize the ChannelSearch component. Though this component can be used standalone, we will do our customization when it's established in the ChannelList. Here is a guide that outlines how to override the default DropdownContainer, SearchResultItem, and SearchResultsHeader components, as well as use several other useful props.

note

A complete override of this component is possible by utilizing the ChannelSearch prop on ChannelList.

Step 1 - Setting Up For Success#

To make the search component visible in the ChannelList, you need to use the showChannelSearch prop. At this point, the search component is ready to go with all of the default UI and functionality.

Let's allow the query to search for channels in addition to the default query of just users by setting the searchForChannels prop. You'll notice that all props for ChannelSearch are drilled to the component via the additionalChannelSearchProps prop on ChannelList. With just this one additional prop, the search functionality is fully functional and expanded from just the default search of users.

<ChannelList showChannelSearch additionalChannelSearchProps={{ searchForChannels: true }} />

Step 2 - Customizing the DropdownContainer#

Since we've enabled the search to also search for channels, let's customize the dropdown results container to fit a design that separates the result by type.

const CustomDropdown = (props: DropdownContainerProps) => {
const { results, focusedUser, selectResult, SearchResultItem } = props;

let items: ChannelOrUserResponse[] = results.filter((x) => x.cid);
let users: ChannelOrUserResponse[] = results.filter((x) => !x.cid);

return (
<div>
<p>Channels</p>
{!items.length && <p>No Channels...</p>}
{items.map((result, index) => (
<SearchResultItem
focusedUser={focusedUser}
index={index}
key={index}
result={result}
selectResult={selectResult}
/>
))}
<p>Users</p>
{!users.length && <p>No Users...</p>}
{users.map((result, index) => (
<SearchResultItem
focusedUser={focusedUser}
index={index}
key={index}
result={result}
selectResult={selectResult}
/>
))}
</div>
);
};
const DropDown = (props: DropdownContainerProps) => <CustomDropdown {...props} />;
const additionalProps = {
DropdownContainer: DropDown
searchForChannels: true,
}

<ChannelList
additionalChannelSearchProps={additionalProps}
showChannelSearch
/>

Step 3 - Customizing the Result Component#

The separation by the type of result, is a huge UI improvement for users, but the above code is still using the default component for each result. Let's create our own component with more information. We've also added a Type Guard so we know what type we're displaying.

  const isChannel = (
output: Channel | UserResponse,
): output is Channel =>
(output as Channel).cid != null;

const CustomResultItem = (props: SearchResultItemProps) => {
const { focusedUser, index, result, selectResult } = props;

const focused = focusedUser === index;

if (isChannel(result)) {
const channel = result;
const { member_count } = channel?.data;

return (
<div
className={`str-chat__channel-search-result ${focused ? 'focused' : ''}`}
onClick={() => selectResult(result)}
>
<div className='result-hashtag'>#</div>
<p className='channel-search__result-text'>{channel?.data?.name}, ({member_count} member{member_count === 1 ? '' : 's'})</p>
</div>
);
} else {
return (
<div
className={`str-chat__channel-search-result ${focused ? 'focused' : ''}`}
onClick={() => selectResult(result)}
>
<Avatar image={result.image} />
{result.id}
{result.online && <p className='user-online'> Online Now!</p>}
</div>
);
}
}
const SearchResult = (props: SearchResultItemProps) => <CustomResultItem {...props} />;

const additionalProps = {
searchForChannels: true,
SearchResultItem: SearchResult
}

<ChannelList
additionalChannelSearchProps={additionalProps}
showChannelSearch
/>

Step 4 - Changing the Positioning and Adding A Header#

For fun, let's add the popupResults prop to change the dropdown to be absolutely positioned instead of inline. Let's also add a quick header component.

const SearchResultsHeader = () => {
return <div className='channel-search-header'>So many search results!</div>
}

const additionalProps = {
popupResults: true,
searchForChannels: true,
SearchResultsHeader: SearchResultsHeader,
}

<ChannelList
additionalChannelSearchProps={additionalProps}
showChannelSearch
/>

Step 5 - The Final Code#

.channel-search__result-user {
display: flex;
align-items: center;
margin-left: 12px;
}

.result-hashtag {
margin: 12px;
display: flex;
justify-content: center;
font-family: Helvetica Neue, sans-serif;
font-weight: bold;
color: #005fff;
}

.channel-search__result-text {
width: 100%;
font-family: Helvetica Neue, sans-serif;
font-weight: 500;
font-size: 14px;
line-height: 120%;
color: #2c2c30;
}

.user-online {
color: red;
font-size: 10px;
padding-top: 2px;
padding-left: 5px;
}

.channel-search-header {
font-weight: 700;
font-size: 16px;
}
  const CustomDropdown = (props: DropdownContainerProps) => {
const { results, focusedUser, selectResult, SearchResultItem } = props;

let items: ChannelOrUserResponse[] = results.filter(x => x.cid);
let users: ChannelOrUserResponse[] = results.filter(x => !x.cid);

return (
<div>
<p>Channels</p>
{!items.length && <p>No Channels...</p>}
{items.map((result, index) => (
<SearchResultItem
focusedUser={focusedUser}
index={index}
key={index}
result={result}
selectResult={selectResult}
/>
))}
<p>Users</p>
{!users.length && <p>No Users...</p>}
{users.map((result, index) => (
<SearchResultItem
focusedUser={focusedUser}
index={index}
key={index}
result={result}
selectResult={selectResult}
/>
))}
</div>
);
};

const isChannel = (
output: Channel | UserResponse,
): output is Channel =>
(output as Channel).cid != null;

const CustomResultItem = (props: SearchResultItemProps) => {
const { focusedUser, index, result, selectResult } = props;

const focused = focusedUser === index;

if (isChannel(result)) {
const channel = result;
const members = channel?.data?.member_count;

return (
<div
className={`str-chat__channel-search-result ${focused ? 'focused' : ''}`}
onClick={() => selectResult(result)}
>
<div className='result-hashtag'>#</div>
<p className='channel-search__result-text'>{channel?.data?.name}, ({members} member{members === 1 ? '' : 's'})</p>
</div>
);
} else {
return (
<div
className={`str-chat__channel-search-result ${focused ? 'focused' : ''}`}
onClick={() => selectResult(result)}
>
<Avatar image={result?.image} />
{result.id}
{result.online && <p className='user-online'> Online Now!</p>}
</div>
);
}
}

const SearchResultsHeader = () => {
return <div className='channel-search-header'>So many search results!</div>
}
  const DropDown = (props: DropdownContainerProps) => <CustomDropdown {...props} />;
const SearchResult = (props: SearchResultItemProps) => <CustomResultItem {...props} />;

const additionalProps = {
DropdownContainer: DropDown,
popupResults: true,
searchForChannels: true,
SearchResultsHeader: SearchResultsHeader,
SearchResultItem: SearchResult
}

return (
<Chat client={chatClient}>
<ChannelList
additionalChannelSearchProps={additionalProps}
showChannelSearch
/>
<Channel>
<Window>
<ChannelHeader />
<MessageList />
<MessageInput />
</Window>
<Thread />
</Channel>
</Chat>
)
};

Result:#

Custom Channel Search UI Component for Chat

The searchFunction Prop:#

By default the ChannelSearch component searches just for users. Use the searchForChannels prop to also search for channels.

To override the search method, completely use the searchFunction prop. This prop is useful, say, when you want to search just for channels and for only channels that the current logged in user is a member of. See the example below for this.

const customSearchFunction = async (props: ChannelSearchFunctionParams, event: { target: { value: SetStateAction<string>; }; }) => {
const { setResults, setResultsOpen, setSearching, setQuery } = props;
const { client } = useChatContext();

const filters = {
name: { $autocomplete: event.target.value },
members: { $in: client.userID }
}

setSearching(true);
setQuery(event.target.value);
const channels = await chatClient.queryChannels(filters);
setResults(channels);
setResultsOpen(true);
setSearching(false);
};

<ChannelList
showChannelSearch
additionalChannelSearchProps={{searchFunction: customSearchFunction}}
/>

Did you find this page helpful?