diff --git a/docusaurus/docs/React/custom-code-examples/channel-search.mdx b/docusaurus/docs/React/custom-code-examples/channel-search.mdx index 7490bdf8b7..6d47b9b865 100644 --- a/docusaurus/docs/React/custom-code-examples/channel-search.mdx +++ b/docusaurus/docs/React/custom-code-examples/channel-search.mdx @@ -339,3 +339,92 @@ const customSearchFunction = async (props: ChannelSearchFunctionParams, event: { additionalChannelSearchProps={{searchFunction: customSearchFunction}} /> ``` + +### Adding menu + +As of the version 10.0.0, users can add app menu into the `SearchBar`. In case you would like to display menu button next to the search input, you can do that by adding [`AppMenu` component](../utility-components/channel-search.mdx/#appmenu) to the `ChannelSearch` props. The display of `AppMenu` is then toggled by clicking on the menu button. `AppMenu` can be rendered as a drop-down or even a modal. In our example we will render a drop-down menu. + +:::caution +The SDK does not provide any default `AppMenu` component and so you will have to write your CSS for it to be styled correctly. +::: + +```tsx +import React, { useCallback } from 'react'; +import type { AppMenuProps } from 'stream-chat-react'; + +import './AppMenu.scss'; + +export const AppMenu = ({close}: AppMenuProps) => { + + const handleSelect = useCallback(() => { + // custom logic... + close?.(); + }, [close]); + + return ( +
+ +
+ ); +} +``` + +```scss +.str-chat__channel-search-bar-button.str-chat__channel-search-bar-button--menu { + position: relative; +} + +.app-menu { + &__container { + position: absolute; + top: 50px; + left: 10px; + background-color: white; + border-radius: 5px; + box-shadow: 0 0 8px var(--str-chat__box-shadow-color); + } + + &__item-list { + list-style: none; + margin: 0; + padding: 0; + } + + &__item { + list-style: none; + margin: 0; + padding: .5rem 1rem; + + &:hover { + background-color: lightgrey; + cursor: pointer; + } + } +} +``` + +```jsx +import { AppMenu } from './components/AppMenu'; + +const App = () => ( + + + + + + + + + + + +); +``` diff --git a/docusaurus/docs/React/utility-components/channel-search.mdx b/docusaurus/docs/React/utility-components/channel-search.mdx index 2e635f4be8..19ecef47d3 100644 --- a/docusaurus/docs/React/utility-components/channel-search.mdx +++ b/docusaurus/docs/React/utility-components/channel-search.mdx @@ -158,11 +158,11 @@ The `ChannelSearch` offers possibility to keep the search results open meanwhile ### AppMenu -Application menu / drop-down to be displayed when clicked on [`MenuIcon`](./#menuicon). Prop is consumed only by the [`SearchBar` component](./#searchbar). The `SearchBar` component is rendered with `themeVersion` `'2'` only. No default component provided by the SDK. The library does not provide any CSS for `AppMenu`. +Application menu / drop-down to be displayed when clicked on [`MenuIcon`](./#menuicon). Prop is consumed only by the [`SearchBar` component](./#searchbar). The `SearchBar` component is only available with the use of the [theming v2](../theming/introduction.mdx). No default component is provided by the SDK. The library does not provide any CSS for `AppMenu`. Consult the customization tutorial on how to [add AppMenu to your application](../custom-code-examples/channel-search.mdx/#adding-menu). The component is passed a prop `close`, which is a function that can be called to hide the app menu (e.g. on menu item selection). -| Type | Default | -| ------------------- | ------------ | -| `React.ComponentType` | `undefined` | +| Type | Default | +|-----------------------|-------------| +| `React.ComponentType` | `undefined` | ### channelType diff --git a/src/components/ChannelSearch/SearchBar.tsx b/src/components/ChannelSearch/SearchBar.tsx index 83d679767f..40aa584900 100644 --- a/src/components/ChannelSearch/SearchBar.tsx +++ b/src/components/ChannelSearch/SearchBar.tsx @@ -16,6 +16,10 @@ import { } from './icons'; import { SearchInput as DefaultSearchInput, SearchInputProps } from './SearchInput'; +export type AppMenuProps = { + close?: () => void; +}; + type SearchBarButtonProps = { className?: string; onClick?: MouseEventHandler; @@ -48,7 +52,7 @@ export type SearchBarController = { export type AdditionalSearchBarProps = { /** Application menu to be displayed when clicked on MenuIcon */ - AppMenu?: React.ComponentType; + AppMenu?: React.ComponentType; /** Custom icon component used to clear the input value on click. Displayed within the search input wrapper. */ ClearInputIcon?: React.ComponentType; /** Custom icon component used to terminate the search UI session on click. */ @@ -133,6 +137,8 @@ export const SearchBar = (props: SearchBarProps) => { inputProps.inputRef.current?.focus(); }, []); + const closeAppMenu = useCallback(() => setMenuIsOpen(false), []); + return (
{inputIsFocused ? ( @@ -172,7 +178,7 @@ export const SearchBar = (props: SearchBarProps) => {
{menuIsOpen && AppMenu && (
- +
)} diff --git a/src/components/ChannelSearch/__tests__/SearchBar.test.js b/src/components/ChannelSearch/__tests__/SearchBar.test.js index 977b4deea5..9632443b14 100644 --- a/src/components/ChannelSearch/__tests__/SearchBar.test.js +++ b/src/components/ChannelSearch/__tests__/SearchBar.test.js @@ -22,7 +22,12 @@ jest.spyOn(window, 'getComputedStyle').mockReturnValue({ let client; const inputText = new Date().getTime().toString(); -const AppMenu = () =>
AppMenu
; +const AppMenu = ({ close }) => ( +
+ AppMenu +
+
+); const ClearInputIcon = () =>
CustomClearInputIcon
; const MenuIcon = () =>
CustomMenuIcon
; const SearchInputIcon = () =>
CustomSearchInputIcon
; @@ -301,6 +306,30 @@ describe('SearchBar', () => { }); }); + it('should close the app menu on menu item click', async () => { + await act(() => { + renderComponent({ + client, + props: { AppMenu }, + searchParams: { disabled: false }, + }); + }); + const menuIcon = screen.queryByTestId('menu-icon'); + await act(() => { + fireEvent.click(menuIcon); + }); + + const menuItem = screen.queryByTestId('menu-item'); + + await act(() => { + fireEvent.click(menuItem); + }); + + await waitFor(() => { + expect(screen.queryByText('AppMenu')).not.toBeInTheDocument(); + }); + }); + it.each([ [ 'on click outside', diff --git a/src/components/ChannelSearch/index.ts b/src/components/ChannelSearch/index.ts index e8561aae22..ee626c4f79 100644 --- a/src/components/ChannelSearch/index.ts +++ b/src/components/ChannelSearch/index.ts @@ -1,4 +1,5 @@ export * from './ChannelSearch'; +export * from './SearchBar'; export * from './SearchInput'; export * from './SearchResults'; export * from './utils';