Skip to content

Commit

Permalink
refactor: keep using Avatar component as a default avatar everywhere
Browse files Browse the repository at this point in the history
  • Loading branch information
MartinCupela committed Nov 8, 2024
1 parent 398cc6d commit dc8d82e
Show file tree
Hide file tree
Showing 13 changed files with 49 additions and 70 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@ title: ChannelListContext

The context value is provided by `ChannelListContextProvider` which wraps the contents rendered by [`ChannelList`](../core-components/channel-list.mdx). It exposes API that the default and custom components rendered by `ChannelList` can take advantage of. The components that can consume the context are customizable via `ChannelListProps`:

- `ChannelAvatar` - component used to display channel image
- `Avatar` - component used to display channel image
- `ChannelSearch` - renders channel search input and results
- `EmptyStateIndicator` - rendered when the channels query returns and empty array
- `LoadingErrorIndicator` - rendered when the channels query fails
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -131,14 +131,6 @@ The [default `BaseImage` component](../../utility-components/base-image) tries t
| --------- | ----------------------------------------------------------------- |
| component | <GHComponentLink text='BaseImage' path='/Gallery/BaseImage.tsx'/> |

### ChannelAvatar

Custom UI component to display avatar for a channel in ChannelHeader.

| Type | Default |
| --------- | ------------------------------------------------------------------------ |
| component | <GHComponentLink text='ChannelAvatar' path='/Avatar/ChannelAvatar.tsx'/> |

### CooldownTimer

Custom UI component to display the slow mode cooldown timer.
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -187,11 +187,11 @@ list from incrementing the list.

### Avatar

Custom UI component to display the channel avatar. The default avatar component for `ChannelList` is `ChannelAvatar`.
Custom UI component to display the channel avatar. The default avatar component for `ChannelList` is `Avatar`.

| Type | Default |
| --------- | ----------------------------------------------------------------- |
| component | <GHComponentLink text='Avatar' path='/Avatar/ChannelAvatar.tsx'/> |
| Type | Default |
| --------- | ---------------------------------------------------------- |
| component | <GHComponentLink text='Avatar' path='/Avatar/Avatar.tsx'/> |

### channelRenderFilterFn

Expand Down
8 changes: 0 additions & 8 deletions docusaurus/docs/React/components/core-components/channel.mdx
Original file line number Diff line number Diff line change
Expand Up @@ -190,14 +190,6 @@ Custom UI component to display a user's avatar.
| --------- | ---------------------------------------------------------- |
| component | <GHComponentLink text='Avatar' path='/Avatar/Avatar.tsx'/> |

### ChannelAvatar

Custom UI component to display avatar for a channel in ChannelHeader.

| Type | Default |
| --------- | ------------------------------------------------------------------------ |
| component | <GHComponentLink text='ChannelAvatar' path='/Avatar/ChannelAvatar.tsx'/> |

### channelQueryOptions

Optional configuration parameters used for the initial channel query. Applied only if the value of `channel.initialized` is false. If the channel instance has already been initialized (channel has been queried), then the channel query will be skipped and channelQueryOptions will not be applied.
Expand Down
36 changes: 14 additions & 22 deletions docusaurus/docs/React/components/utility-components/avatar.mdx
Original file line number Diff line number Diff line change
Expand Up @@ -3,28 +3,17 @@ id: avatar
title: Avatar
---

The SDK supports variety of avatar component types. Different approach is taken to display a channel avatar and an avatar of a message author.
Semantically we can speak about two types of avatars in the SDK. One type is the avatar that represents the channel and the other representing another user. The SDK exports the follwing avatar components:

The channel avatar accounts for the fact that the channel may contain more than two members and thus become a group channel. Therefore, it renders a `GroupAvatar` component in case of more than two channel members and `Avatar` in case of two or less channel members.
On the other hand, messages use the avatars for a specific single user and thus using the `Avatar` component exclusively.
- `Avatar` - displays single image or name initials in case image is not available
- `GroupAvatar` - displays images or name initials as a fallback in a 2x2 grid
- `ChannelAvatar` - renders `GroupAvatar` in case a channel has more than two members and `Avatar` otherwise

The `Avatar` component displays an image, with fallback to the first letter of the optional name prop. The `GroupAvatar` displays up to four `Avatar` components in a 2x2 grid.
By default, all the SDK components use `Avatar` to display channel resp. user avatar. However, it makes sense to override the default in `ChannelList` resp. `ChannelPreview` and `ChannelHeader` as those avatars may represent a group of users .

## Customizing Avatar component
## Customizing avatar component

The SDK's default `Avatar` component is used by the following components:

- `ChannelSearch` results for users
- `Message`
- `QuotesMessage`
- `MesageStatus`
- `QuotedMessagePreview` in message composer
- Suggestion items for user mentions
- `Poll`
- Message `Reactions`
- `ThreadList`

Passing your custom avatar component to `Channel` prop `Avatar` overrides the avatar for all the above components.
Passing your custom avatar component to `Channel` prop `Avatar` overrides the avatar for all the `Channel` component's children.

Here's an example of using the `Avatar` component within a custom preview component:

Expand All @@ -39,13 +28,14 @@ const Avatar = (props: AvatarProps) => {
<Channel Avatar={Avatar} />;
```

## Customizing Channel Avatar
## Customizing channel avatar

You can also take advantage of the `Avatar` prop on the `ChannelHeader` and `ChannelList` components to override just that aspect of these components specifically, see the example below.

An example of overriding just the `Avatar` component in the default `ChannelPreview` component.

```tsx
import { ChannelList } from 'stream-chat-react';
import type { ChannelAvatarProps } from 'stream-chat-react';

const CustomChannelAvatar = (props: ChannelAvatarProps) => {
Expand All @@ -55,19 +45,21 @@ const CustomChannelAvatar = (props: ChannelAvatarProps) => {
<ChannelList Avatar={CustomChannelAvatar} />;
```

To override channel avatar in `ChannelHeader` we need to provide our component to `Channel` prop `ChannelAvatar`:
To override the channel avatar in `ChannelHeader` we need to provide it prop `Avatar`:

```tsx
import { Channel } from 'stream-chat-react';
import { ChannelHeader } from 'stream-chat-react';
import type { ChannelAvatarProps } from 'stream-chat-react';

const CustomChannelAvatar = (props: ChannelAvatarProps) => {
return <div>Custom Channel Avatar</div>;
};

<Channel ChannelAvatar={CustomChannelAvatar} />;
<ChannelHeader Avatar={CustomChannelAvatar} />;
```

Also, we can take the advantage of existing SDK's `ChannelAvatar` and pass it to both `ChannelHeader` and `ChannelList` as described above.

## Avatar Props

### className
Expand Down
3 changes: 0 additions & 3 deletions src/components/Channel/Channel.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -112,7 +112,6 @@ type ChannelPropsForwardedToComponentContext<
| 'AutocompleteSuggestionList'
| 'Avatar'
| 'BaseImage'
| 'ChannelAvatar'
| 'CooldownTimer'
| 'CustomMessageActionsList'
| 'DateSeparator'
Expand Down Expand Up @@ -1235,7 +1234,6 @@ const ChannelInner = <
AutocompleteSuggestionList: props.AutocompleteSuggestionList,
Avatar: props.Avatar,
BaseImage: props.BaseImage,
ChannelAvatar: props.ChannelAvatar,
CooldownTimer: props.CooldownTimer,
CustomMessageActionsList: props.CustomMessageActionsList,
DateSeparator: props.DateSeparator,
Expand Down Expand Up @@ -1295,7 +1293,6 @@ const ChannelInner = <
props.AutocompleteSuggestionList,
props.Avatar,
props.BaseImage,
props.ChannelAvatar,
props.CooldownTimer,
props.CustomMessageActionsList,
props.DateSeparator,
Expand Down
6 changes: 3 additions & 3 deletions src/components/ChannelHeader/ChannelHeader.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@ import React from 'react';

import { MenuIcon as DefaultMenuIcon } from './icons';

import { ChannelAvatar, ChannelAvatarProps } from '../Avatar';
import { ChannelAvatarProps, Avatar as DefaultAvatar } from '../Avatar';
import { useChannelPreviewInfo } from '../ChannelPreview/hooks/useChannelPreviewInfo';

import { useChannelStateContext } from '../../context/ChannelStateContext';
Expand All @@ -12,7 +12,7 @@ import { useTranslationContext } from '../../context/TranslationContext';
import type { DefaultStreamChatGenerics } from '../../types/types';

export type ChannelHeaderProps = {
/** UI component to display a user's avatar, defaults to and accepts same props as: [Avatar](https://github.com/GetStream/stream-chat-react/blob/master/src/components/Avatar/Avatar.tsx) */
/** UI component to display an avatar, defaults to [Avatar](https://github.com/GetStream/stream-chat-react/blob/master/src/components/Avatar/Avatar.tsx) component and accepts the same props as: [ChannelAvatar](https://github.com/GetStream/stream-chat-react/blob/master/src/components/Avatar/ChannelAvatar.tsx) */
Avatar?: React.ComponentType<ChannelAvatarProps>;
/** Manually set the image to render, defaults to the Channel image */
image?: string;
Expand All @@ -33,7 +33,7 @@ export const ChannelHeader = <
props: ChannelHeaderProps,
) => {
const {
Avatar = ChannelAvatar,
Avatar = DefaultAvatar,
MenuIcon = DefaultMenuIcon,
image: overrideImage,
live,
Expand Down
17 changes: 11 additions & 6 deletions src/components/ChannelHeader/__tests__/ChannelHeader.test.js
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,7 @@ import {
} from '../../../mock-builders';
import { toHaveNoViolations } from 'jest-axe';
import { axe } from '../../../../axe-helper';
import { ChannelAvatar } from '../../Avatar';
expect.extend(toHaveNoViolations);

const AVATAR_IMG_TEST_ID = 'avatar-img';
Expand Down Expand Up @@ -194,6 +195,10 @@ describe('ChannelHeader', () => {
});

describe('group channel', () => {
const props = {
Avatar: ChannelAvatar,
};

const getChannelState = (memberCount, channelData) => {
const users = Array.from({ length: memberCount }, generateUser);
const members = users.map((user) => generateMember({ user }));
Expand All @@ -216,7 +221,7 @@ describe('ChannelHeader', () => {
channelsData: [channelState],
customUser: ownUser,
});
await renderComponentBase({ channel, client });
await renderComponentBase({ channel, client, props });
await waitFor(() => {
const avatarImages = screen.getAllByTestId(AVATAR_IMG_TEST_ID);
expect(avatarImages).toHaveLength(4);
Expand All @@ -237,7 +242,7 @@ describe('ChannelHeader', () => {
client,
} = await initClientWithChannels({ channelsData: [channelState] });
const updatedAttribute = { name: 'new-name' };
await renderComponentBase({ channel, client });
await renderComponentBase({ channel, client, props });

await waitFor(() => {
expect(screen.queryByText(updatedAttribute.name)).not.toBeInTheDocument();
Expand All @@ -263,7 +268,7 @@ describe('ChannelHeader', () => {
customUser: ownUser,
});
const updatedAttribute = { image: 'new-image' };
await renderComponentBase({ channel, client });
await renderComponentBase({ channel, client, props });
await waitFor(() => {
const avatarImages = screen.getAllByTestId(AVATAR_IMG_TEST_ID);
expect(avatarImages).toHaveLength(3);
Expand Down Expand Up @@ -295,7 +300,7 @@ describe('ChannelHeader', () => {
customUser: ownUser,
});
const updatedAttribute = { image: 'new-image' };
await renderComponentBase({ channel, client });
await renderComponentBase({ channel, client, props });
await waitFor(() => {
const avatarImages = screen.getAllByTestId(AVATAR_IMG_TEST_ID);
expect(avatarImages).toHaveLength(3);
Expand Down Expand Up @@ -327,7 +332,7 @@ describe('ChannelHeader', () => {
customUser: ownUser,
});
const updatedAttribute = { custom: 'new-custom' };
await renderComponentBase({ channel, client });
await renderComponentBase({ channel, client, props });

await waitFor(() => {
expect(screen.queryByText(updatedAttribute.custom)).not.toBeInTheDocument();
Expand Down Expand Up @@ -362,7 +367,7 @@ describe('ChannelHeader', () => {
customUser: ownUser,
});
const updatedAttribute = { custom: 'new-custom' };
await renderComponentBase({ channel, client });
await renderComponentBase({ channel, client, props });

await waitFor(() => {
expect(screen.queryByText(updatedAttribute.custom)).not.toBeInTheDocument();
Expand Down
6 changes: 3 additions & 3 deletions src/components/ChannelList/ChannelList.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,7 @@ import { useNotificationRemovedFromChannelListener } from './hooks/useNotificati
import { CustomQueryChannelsFn, usePaginatedChannels } from './hooks/usePaginatedChannels';
import { useUserPresenceChangedListener } from './hooks/useUserPresenceChangedListener';
import { MAX_QUERY_CHANNELS_LIMIT, moveChannelUp } from './utils';
import { ChannelAvatar } from '../Avatar';
import { Avatar as DefaultAvatar } from '../Avatar';
import { ChannelPreview, ChannelPreviewUIComponentProps } from '../ChannelPreview/ChannelPreview';
import {
ChannelSearchProps,
Expand Down Expand Up @@ -54,7 +54,7 @@ export type ChannelListProps<
* to false, which will prevent channels not in the list from incrementing the list. The default is true.
*/
allowNewMessagesFromUnfilteredChannels?: boolean;
/** Custom UI component to display channel avatar, defaults to and accepts same props as: [ChannelAvatar](https://github.com/GetStream/stream-chat-react/blob/master/src/components/Avatar/ChannelAvatar.tsx) */
/** UI component to display an avatar, defaults to [Avatar](https://github.com/GetStream/stream-chat-react/blob/master/src/components/Avatar/Avatar.tsx) component and accepts the same props as: [ChannelAvatar](https://github.com/GetStream/stream-chat-react/blob/master/src/components/Avatar/ChannelAvatar.tsx) */
Avatar?: React.ComponentType<ChannelAvatarProps>;
/** Optional function to filter channels prior to loading in the DOM. Do not use any complex or async logic that would delay the loading of the ChannelList. We recommend using a pure function with array methods like filter/sort/reduce. */
channelRenderFilterFn?: (
Expand Down Expand Up @@ -166,7 +166,7 @@ const UnMemoizedChannelList = <
) => {
const {
additionalChannelSearchProps,
Avatar = ChannelAvatar,
Avatar = DefaultAvatar,
allowNewMessagesFromUnfilteredChannels,
channelRenderFilterFn,
ChannelSearch = DefaultChannelSearch,
Expand Down
2 changes: 1 addition & 1 deletion src/components/ChannelPreview/ChannelPreview.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -48,7 +48,7 @@ export type ChannelPreviewProps<
channel: Channel<StreamChatGenerics>;
/** Current selected channel object */
activeChannel?: Channel<StreamChatGenerics>;
/** Custom UI component to display user avatar, defaults to and accepts same props as: [Avatar](https://github.com/GetStream/stream-chat-react/blob/master/src/components/Avatar/Avatar.tsx) */
/** UI component to display an avatar, defaults to [Avatar](https://github.com/GetStream/stream-chat-react/blob/master/src/components/Avatar/Avatar.tsx) component and accepts the same props as: [ChannelAvatar](https://github.com/GetStream/stream-chat-react/blob/master/src/components/Avatar/ChannelAvatar.tsx) */
Avatar?: React.ComponentType<ChannelAvatarProps<StreamChatGenerics>>;
/** Forces the update of preview component on channel update */
channelUpdateCount?: number;
Expand Down
4 changes: 2 additions & 2 deletions src/components/ChannelPreview/ChannelPreviewMessenger.tsx
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
import React, { useRef } from 'react';
import clsx from 'clsx';
import { ChannelAvatar } from '../Avatar/ChannelAvatar';
import { Avatar as DefaultAvatar } from '../Avatar';

import type { ChannelPreviewUIComponentProps } from './ChannelPreview';
import type { DefaultStreamChatGenerics } from '../../types/types';
Expand All @@ -12,7 +12,7 @@ const UnMemoizedChannelPreviewMessenger = <
) => {
const {
active,
Avatar = ChannelAvatar,
Avatar = DefaultAvatar,
channel,
className: customClassName = '',
displayImage,
Expand Down
16 changes: 10 additions & 6 deletions src/components/ChannelPreview/__tests__/ChannelPreview.test.js
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ import React from 'react';
import { act, render, screen, waitFor } from '@testing-library/react';
import '@testing-library/jest-dom';

import { ChannelAvatar } from '../../Avatar';
import { ChannelPreview } from '../ChannelPreview';
import { Chat } from '../../Chat';

Expand Down Expand Up @@ -601,6 +602,9 @@ describe('ChannelPreview', () => {
});

describe('group channel', () => {
const channelPreviewProps = {
Avatar: ChannelAvatar,
};
const channelName = 'channel-name';
const channelState = getChannelState(3, { channel: { name: channelName } });

Expand All @@ -614,7 +618,7 @@ describe('ChannelPreview', () => {
channelsData: [channelState],
customUser: ownUser,
});
await renderComponent({ channel, client });
await renderComponent({ channel, channelPreviewProps, client });
await waitFor(() => {
const avatarImages = screen.getAllByTestId(AVATAR_IMG_TEST_ID);
expect(avatarImages).toHaveLength(4);
Expand All @@ -635,7 +639,7 @@ describe('ChannelPreview', () => {
client,
} = await initClientWithChannels({ channelsData: [channelState] });
const updatedAttribute = { name: 'new-name' };
await renderComponent({ channel, client });
await renderComponent({ channel, channelPreviewProps, client });

await waitFor(() => {
expect(screen.queryByText(updatedAttribute.name)).not.toBeInTheDocument();
Expand All @@ -661,7 +665,7 @@ describe('ChannelPreview', () => {
customUser: ownUser,
});
const updatedAttribute = { image: 'new-image' };
await renderComponent({ channel, client });
await renderComponent({ channel, channelPreviewProps, client });
await waitFor(() => {
const avatarImages = screen.getAllByTestId(AVATAR_IMG_TEST_ID);
expect(avatarImages).toHaveLength(3);
Expand Down Expand Up @@ -693,7 +697,7 @@ describe('ChannelPreview', () => {
customUser: ownUser,
});
const updatedAttribute = { image: 'new-image' };
await renderComponent({ channel, client });
await renderComponent({ channel, channelPreviewProps, client });
await waitFor(() => {
const avatarImages = screen.getAllByTestId(AVATAR_IMG_TEST_ID);
expect(avatarImages).toHaveLength(3);
Expand Down Expand Up @@ -725,7 +729,7 @@ describe('ChannelPreview', () => {
customUser: ownUser,
});
const updatedAttribute = { custom: 'new-custom' };
await renderComponent({ channel, client });
await renderComponent({ channel, channelPreviewProps, client });

await waitFor(() => {
expect(screen.queryByText(updatedAttribute.custom)).not.toBeInTheDocument();
Expand Down Expand Up @@ -760,7 +764,7 @@ describe('ChannelPreview', () => {
customUser: ownUser,
});
const updatedAttribute = { custom: 'new-custom' };
await renderComponent({ channel, client });
await renderComponent({ channel, channelPreviewProps, client });

await waitFor(() => {
expect(screen.queryByText(updatedAttribute.custom)).not.toBeInTheDocument();
Expand Down
3 changes: 0 additions & 3 deletions src/context/ComponentContext.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,6 @@ import {
AttachmentProps,
AvatarProps,
BaseImageProps,
ChannelAvatarProps,
CooldownTimerProps,
CustomMessageActionsListProps,
DateSeparatorProps,
Expand Down Expand Up @@ -78,8 +77,6 @@ export type ComponentContextValue<
Avatar?: React.ComponentType<AvatarProps<StreamChatGenerics>>;
/** Custom UI component to display <img/> elements resp. a fallback in case of load error, defaults to and accepts same props as: [BaseImage](https://github.com/GetStream/stream-chat-react/blob/master/src/components/Gallery/BaseImage.tsx) */
BaseImage?: React.ComponentType<BaseImageProps>;
/** Custom UI component to display avatar for a channel in ChannelHeader: [ChannelAvatar](https://github.com/GetStream/stream-chat-react/blob/master/src/components/Avatar/ChannelAvatar.tsx) */
ChannelAvatar?: React.ComponentType<ChannelAvatarProps>;
/** Custom UI component to display the slow mode cooldown timer, defaults to and accepts same props as: [CooldownTimer](https://github.com/GetStream/stream-chat-react/blob/master/src/components/MessageInput/CooldownTimer.tsx) */
CooldownTimer?: React.ComponentType<CooldownTimerProps>;
/** Custom UI component to render set of buttons to be displayed in the MessageActionsBox, defaults to and accepts same props as: [CustomMessageActionsList](https://github.com/GetStream/stream-chat-react/blob/master/src/components/MessageActions/CustomMessageActionsList.tsx) */
Expand Down

0 comments on commit dc8d82e

Please sign in to comment.