Require badge props in <Avatar>
to ensure no missing spots
This commit is contained in:
parent
7affe313f0
commit
e030b3d18c
20 changed files with 94 additions and 46 deletions
|
@ -16,6 +16,7 @@ import type { AvatarColorType } from '../types/Colors';
|
|||
import { AvatarColors } from '../types/Colors';
|
||||
import { StorybookThemeContext } from '../../.storybook/StorybookThemeContext';
|
||||
import { getFakeBadge } from '../test-both/helpers/getFakeBadge';
|
||||
import { ThemeType } from '../types/Util';
|
||||
|
||||
const i18n = setupI18n('en', enMessages);
|
||||
|
||||
|
@ -64,7 +65,7 @@ const createProps = (overrideProps: Partial<Props> = {}): Props => ({
|
|||
sharedGroupNames: [],
|
||||
size: 80,
|
||||
title: overrideProps.title || '',
|
||||
theme: overrideProps.theme,
|
||||
theme: overrideProps.theme || ThemeType.light,
|
||||
});
|
||||
|
||||
const sizes: Array<Props['size']> = [112, 96, 80, 52, 32, 28];
|
||||
|
|
|
@ -49,7 +49,6 @@ type BadgePlacementType = { bottom: number; right: number };
|
|||
|
||||
export type Props = {
|
||||
avatarPath?: string;
|
||||
badge?: BadgeType;
|
||||
blur?: AvatarBlur;
|
||||
color?: AvatarColorType;
|
||||
loading?: boolean;
|
||||
|
@ -63,7 +62,6 @@ export type Props = {
|
|||
profileName?: string;
|
||||
sharedGroupNames: Array<string>;
|
||||
size: AvatarSize;
|
||||
theme?: ThemeType;
|
||||
title: string;
|
||||
unblurredAvatarPath?: string;
|
||||
searchResult?: boolean;
|
||||
|
@ -75,7 +73,11 @@ export type Props = {
|
|||
innerRef?: React.Ref<HTMLDivElement>;
|
||||
|
||||
i18n: LocalizerType;
|
||||
} & Pick<React.HTMLProps<HTMLDivElement>, 'className'>;
|
||||
} & (
|
||||
| { badge: undefined; theme?: ThemeType }
|
||||
| { badge: BadgeType; theme: ThemeType }
|
||||
) &
|
||||
Pick<React.HTMLProps<HTMLDivElement>, 'className'>;
|
||||
|
||||
const BADGE_PLACEMENT_BY_SIZE = new Map<number, BadgePlacementType>([
|
||||
[28, { bottom: -4, right: -2 }],
|
||||
|
@ -290,8 +292,6 @@ export const Avatar: FunctionComponent<Props> = ({
|
|||
);
|
||||
}
|
||||
}
|
||||
} else if (badge && !theme) {
|
||||
log.error('<Avatar> requires a theme if a badge is provided');
|
||||
}
|
||||
|
||||
return (
|
||||
|
|
|
@ -13,6 +13,8 @@ import type { AvatarColorType } from '../types/Colors';
|
|||
import { AvatarColors } from '../types/Colors';
|
||||
import { setupI18n } from '../util/setupI18n';
|
||||
import enMessages from '../../_locales/en/messages.json';
|
||||
import { StorybookThemeContext } from '../../.storybook/StorybookThemeContext';
|
||||
import { getFakeBadge } from '../test-both/helpers/getFakeBadge';
|
||||
|
||||
const i18n = setupI18n('en', enMessages);
|
||||
|
||||
|
@ -29,9 +31,10 @@ const conversationTypeMap: Record<string, Props['conversationType']> = {
|
|||
group: 'group',
|
||||
};
|
||||
|
||||
const createProps = (overrideProps: Partial<Props> = {}): Props => ({
|
||||
const useProps = (overrideProps: Partial<Props> = {}): Props => ({
|
||||
acceptedMessageRequest: true,
|
||||
avatarPath: text('avatarPath', overrideProps.avatarPath || ''),
|
||||
badge: overrideProps.badge,
|
||||
color: select('color', colorMap, overrideProps.color || AvatarColors[0]),
|
||||
conversationType: select(
|
||||
'conversationType',
|
||||
|
@ -52,19 +55,29 @@ const createProps = (overrideProps: Partial<Props> = {}): Props => ({
|
|||
size: 80,
|
||||
startUpdate: action('startUpdate'),
|
||||
style: {},
|
||||
theme: React.useContext(StorybookThemeContext),
|
||||
title: text('title', overrideProps.title || ''),
|
||||
});
|
||||
|
||||
const stories = storiesOf('Components/Avatar Popup', module);
|
||||
|
||||
stories.add('Avatar Only', () => {
|
||||
const props = createProps();
|
||||
const props = useProps();
|
||||
|
||||
return <AvatarPopup {...props} />;
|
||||
});
|
||||
|
||||
stories.add('Has badge', () => {
|
||||
const props = useProps({
|
||||
badge: getFakeBadge(),
|
||||
title: 'Janet Yellen',
|
||||
});
|
||||
|
||||
return <AvatarPopup {...props} />;
|
||||
});
|
||||
|
||||
stories.add('Title', () => {
|
||||
const props = createProps({
|
||||
const props = useProps({
|
||||
title: 'My Great Title',
|
||||
});
|
||||
|
||||
|
@ -72,7 +85,7 @@ stories.add('Title', () => {
|
|||
});
|
||||
|
||||
stories.add('Profile Name', () => {
|
||||
const props = createProps({
|
||||
const props = useProps({
|
||||
profileName: 'Sam Neill',
|
||||
});
|
||||
|
||||
|
@ -80,7 +93,7 @@ stories.add('Profile Name', () => {
|
|||
});
|
||||
|
||||
stories.add('Phone Number', () => {
|
||||
const props = createProps({
|
||||
const props = useProps({
|
||||
profileName: 'Sam Neill',
|
||||
phoneNumber: '(555) 867-5309',
|
||||
});
|
||||
|
@ -89,7 +102,7 @@ stories.add('Phone Number', () => {
|
|||
});
|
||||
|
||||
stories.add('Update Available', () => {
|
||||
const props = createProps({
|
||||
const props = useProps({
|
||||
hasPendingUpdate: true,
|
||||
});
|
||||
|
||||
|
|
|
@ -8,10 +8,11 @@ import type { Props as AvatarProps } from './Avatar';
|
|||
import { Avatar } from './Avatar';
|
||||
import { useRestoreFocus } from '../hooks/useRestoreFocus';
|
||||
|
||||
import type { LocalizerType } from '../types/Util';
|
||||
import type { LocalizerType, ThemeType } from '../types/Util';
|
||||
|
||||
export type Props = {
|
||||
readonly i18n: LocalizerType;
|
||||
readonly theme: ThemeType;
|
||||
|
||||
hasPendingUpdate: boolean;
|
||||
startUpdate: () => unknown;
|
||||
|
|
|
@ -47,6 +47,7 @@ export const CallNeedPermissionScreen: React.FC<Props> = ({
|
|||
<Avatar
|
||||
acceptedMessageRequest={conversation.acceptedMessageRequest}
|
||||
avatarPath={conversation.avatarPath}
|
||||
badge={undefined}
|
||||
color={conversation.color || AvatarColors[0]}
|
||||
noteToSelf={false}
|
||||
conversationType="direct"
|
||||
|
|
|
@ -327,6 +327,7 @@ export const CallScreen: React.FC<PropsType> = ({
|
|||
<Avatar
|
||||
acceptedMessageRequest
|
||||
avatarPath={me.avatarPath}
|
||||
badge={undefined}
|
||||
color={me.color || AvatarColors[0]}
|
||||
noteToSelf={false}
|
||||
conversationType="direct"
|
||||
|
@ -364,6 +365,7 @@ export const CallScreen: React.FC<PropsType> = ({
|
|||
<Avatar
|
||||
acceptedMessageRequest
|
||||
avatarPath={me.avatarPath}
|
||||
badge={undefined}
|
||||
color={me.color || AvatarColors[0]}
|
||||
noteToSelf={false}
|
||||
conversationType="direct"
|
||||
|
|
|
@ -99,6 +99,7 @@ export const CallingParticipantsList = React.memo(
|
|||
participant.acceptedMessageRequest
|
||||
}
|
||||
avatarPath={participant.avatarPath}
|
||||
badge={undefined}
|
||||
color={participant.color}
|
||||
conversationType="direct"
|
||||
i18n={i18n}
|
||||
|
|
|
@ -51,6 +51,7 @@ const NoVideo = ({
|
|||
<Avatar
|
||||
acceptedMessageRequest={acceptedMessageRequest}
|
||||
avatarPath={avatarPath}
|
||||
badge={undefined}
|
||||
color={color || AvatarColors[0]}
|
||||
noteToSelf={false}
|
||||
conversationType="direct"
|
||||
|
|
|
@ -164,6 +164,7 @@ export const CallingPreCallInfo: FunctionComponent<PropsType> = ({
|
|||
<div className="module-CallingPreCallInfo">
|
||||
<Avatar
|
||||
avatarPath={conversation.avatarPath}
|
||||
badge={undefined}
|
||||
color={conversation.color}
|
||||
acceptedMessageRequest={conversation.acceptedMessageRequest}
|
||||
conversationType={conversation.type}
|
||||
|
|
|
@ -52,6 +52,7 @@ export const ContactPill: FunctionComponent<PropsType> = ({
|
|||
<Avatar
|
||||
acceptedMessageRequest={acceptedMessageRequest}
|
||||
avatarPath={avatarPath}
|
||||
badge={undefined}
|
||||
color={color}
|
||||
noteToSelf={false}
|
||||
conversationType="direct"
|
||||
|
|
|
@ -70,6 +70,7 @@ function renderAvatar(
|
|||
<Avatar
|
||||
acceptedMessageRequest={acceptedMessageRequest}
|
||||
avatarPath={avatarPath}
|
||||
badge={undefined}
|
||||
color={color || AvatarColors[0]}
|
||||
noteToSelf={false}
|
||||
conversationType="direct"
|
||||
|
|
|
@ -319,6 +319,7 @@ export const GroupCallRemoteParticipant: React.FC<PropsType> = React.memo(
|
|||
<Avatar
|
||||
acceptedMessageRequest={acceptedMessageRequest}
|
||||
avatarPath={avatarPath}
|
||||
badge={undefined}
|
||||
color={color || AvatarColors[0]}
|
||||
noteToSelf={false}
|
||||
conversationType="direct"
|
||||
|
|
|
@ -78,6 +78,7 @@ export const GroupV2JoinDialog = React.memo((props: PropsType) => {
|
|||
<Avatar
|
||||
acceptedMessageRequest={false}
|
||||
avatarPath={avatar ? avatar.url : undefined}
|
||||
badge={undefined}
|
||||
blur={AvatarBlur.NoBlur}
|
||||
loading={avatar && !avatar.url}
|
||||
conversationType="group"
|
||||
|
|
|
@ -230,6 +230,7 @@ export const IncomingCallBar = (props: PropsType): JSX.Element | null => {
|
|||
<Avatar
|
||||
acceptedMessageRequest={acceptedMessageRequest}
|
||||
avatarPath={avatarPath}
|
||||
badge={undefined}
|
||||
color={color || AvatarColors[0]}
|
||||
noteToSelf={false}
|
||||
conversationType={conversationType}
|
||||
|
|
|
@ -56,6 +56,7 @@ export const LeftPaneSearchInput = forwardRef<HTMLInputElement, PropsType>(
|
|||
<Avatar
|
||||
acceptedMessageRequest={searchConversation.acceptedMessageRequest}
|
||||
avatarPath={searchConversation.avatarPath}
|
||||
badge={undefined}
|
||||
color={searchConversation.color}
|
||||
conversationType={searchConversation.type}
|
||||
i18n={i18n}
|
||||
|
|
|
@ -673,6 +673,7 @@ function LightboxHeader({
|
|||
<Avatar
|
||||
acceptedMessageRequest={conversation.acceptedMessageRequest}
|
||||
avatarPath={conversation.avatarPath}
|
||||
badge={undefined}
|
||||
color={conversation.color}
|
||||
conversationType={conversation.type}
|
||||
i18n={i18n}
|
||||
|
|
|
@ -37,6 +37,7 @@ storiesOf('Components/Conversation/ConversationHero', module)
|
|||
<Wrapper
|
||||
about={getAbout()}
|
||||
acceptedMessageRequest
|
||||
badge={undefined}
|
||||
i18n={i18n}
|
||||
isMe={false}
|
||||
title={getTitle()}
|
||||
|
@ -65,6 +66,7 @@ storiesOf('Components/Conversation/ConversationHero', module)
|
|||
<Wrapper
|
||||
about={getAbout()}
|
||||
acceptedMessageRequest
|
||||
badge={undefined}
|
||||
i18n={i18n}
|
||||
isMe={false}
|
||||
title={getTitle()}
|
||||
|
@ -92,6 +94,7 @@ storiesOf('Components/Conversation/ConversationHero', module)
|
|||
<Wrapper
|
||||
about={getAbout()}
|
||||
acceptedMessageRequest
|
||||
badge={undefined}
|
||||
i18n={i18n}
|
||||
isMe={false}
|
||||
title={getTitle()}
|
||||
|
@ -114,6 +117,7 @@ storiesOf('Components/Conversation/ConversationHero', module)
|
|||
<Wrapper
|
||||
about={getAbout()}
|
||||
acceptedMessageRequest
|
||||
badge={undefined}
|
||||
i18n={i18n}
|
||||
isMe={false}
|
||||
title={getTitle()}
|
||||
|
@ -136,6 +140,7 @@ storiesOf('Components/Conversation/ConversationHero', module)
|
|||
<Wrapper
|
||||
about={getAbout()}
|
||||
acceptedMessageRequest
|
||||
badge={undefined}
|
||||
i18n={i18n}
|
||||
isMe={false}
|
||||
title={getTitle()}
|
||||
|
@ -158,6 +163,7 @@ storiesOf('Components/Conversation/ConversationHero', module)
|
|||
<Wrapper
|
||||
about={getAbout()}
|
||||
acceptedMessageRequest
|
||||
badge={undefined}
|
||||
i18n={i18n}
|
||||
isMe={false}
|
||||
title={getTitle()}
|
||||
|
@ -180,6 +186,7 @@ storiesOf('Components/Conversation/ConversationHero', module)
|
|||
<Wrapper
|
||||
about={getAbout()}
|
||||
acceptedMessageRequest
|
||||
badge={undefined}
|
||||
i18n={i18n}
|
||||
isMe={false}
|
||||
title={text('title', 'Cayce Bollard (profile)')}
|
||||
|
@ -202,6 +209,7 @@ storiesOf('Components/Conversation/ConversationHero', module)
|
|||
<Wrapper
|
||||
about={getAbout()}
|
||||
acceptedMessageRequest
|
||||
badge={undefined}
|
||||
i18n={i18n}
|
||||
isMe={false}
|
||||
title={text('title', '+1 (646) 327-2700')}
|
||||
|
@ -226,6 +234,7 @@ storiesOf('Components/Conversation/ConversationHero', module)
|
|||
isMe={false}
|
||||
title={text('title', 'Unknown contact')}
|
||||
acceptedMessageRequest
|
||||
badge={undefined}
|
||||
avatarPath={getAvatarPath()}
|
||||
name={text('name', '')}
|
||||
profileName={text('profileName', '')}
|
||||
|
@ -247,6 +256,7 @@ storiesOf('Components/Conversation/ConversationHero', module)
|
|||
isMe={false}
|
||||
title={text('title', 'Unknown contact')}
|
||||
acceptedMessageRequest={false}
|
||||
badge={undefined}
|
||||
avatarPath={getAvatarPath()}
|
||||
name={text('name', '')}
|
||||
profileName={text('profileName', '')}
|
||||
|
@ -265,6 +275,7 @@ storiesOf('Components/Conversation/ConversationHero', module)
|
|||
<div style={{ width: '480px' }}>
|
||||
<Wrapper
|
||||
acceptedMessageRequest
|
||||
badge={undefined}
|
||||
i18n={i18n}
|
||||
isMe={false}
|
||||
title={text('title', 'NYC Rock Climbers')}
|
||||
|
@ -284,6 +295,7 @@ storiesOf('Components/Conversation/ConversationHero', module)
|
|||
<div style={{ width: '480px' }}>
|
||||
<Wrapper
|
||||
acceptedMessageRequest
|
||||
badge={undefined}
|
||||
i18n={i18n}
|
||||
isMe={false}
|
||||
title={text('title', 'NYC Rock Climbers')}
|
||||
|
@ -303,6 +315,7 @@ storiesOf('Components/Conversation/ConversationHero', module)
|
|||
<div style={{ width: '480px' }}>
|
||||
<Wrapper
|
||||
acceptedMessageRequest
|
||||
badge={undefined}
|
||||
i18n={i18n}
|
||||
isMe={false}
|
||||
title={text('title', 'NYC Rock Climbers')}
|
||||
|
@ -323,6 +336,7 @@ storiesOf('Components/Conversation/ConversationHero', module)
|
|||
<div style={{ width: '480px' }}>
|
||||
<Wrapper
|
||||
acceptedMessageRequest
|
||||
badge={undefined}
|
||||
i18n={i18n}
|
||||
isMe={false}
|
||||
title={text('title', 'NYC Rock Climbers')}
|
||||
|
@ -343,6 +357,7 @@ storiesOf('Components/Conversation/ConversationHero', module)
|
|||
<div style={{ width: '480px' }}>
|
||||
<Wrapper
|
||||
acceptedMessageRequest
|
||||
badge={undefined}
|
||||
i18n={i18n}
|
||||
isMe={false}
|
||||
title={text('title', 'Unknown group')}
|
||||
|
@ -362,6 +377,7 @@ storiesOf('Components/Conversation/ConversationHero', module)
|
|||
<div style={{ width: '480px' }}>
|
||||
<Wrapper
|
||||
acceptedMessageRequest
|
||||
badge={undefined}
|
||||
i18n={i18n}
|
||||
isMe
|
||||
title={getTitle()}
|
||||
|
|
|
@ -423,6 +423,7 @@ const renderHeroRow = () => {
|
|||
<ConversationHero
|
||||
about={getAbout()}
|
||||
acceptedMessageRequest
|
||||
badge={undefined}
|
||||
i18n={i18n}
|
||||
isMe={false}
|
||||
title={getTitle()}
|
||||
|
|
|
@ -49,6 +49,7 @@ export function renderAvatar({
|
|||
<Avatar
|
||||
acceptedMessageRequest={false}
|
||||
avatarPath={avatarPath}
|
||||
badge={undefined}
|
||||
blur={AvatarBlur.NoBlur}
|
||||
color={AvatarColors[0]}
|
||||
conversationType="direct"
|
||||
|
|
|
@ -67,37 +67,37 @@ type PropsType = {
|
|||
);
|
||||
|
||||
export const BaseConversationListItem: FunctionComponent<PropsType> =
|
||||
React.memo(function BaseConversationListItem({
|
||||
acceptedMessageRequest,
|
||||
avatarPath,
|
||||
badge,
|
||||
checked,
|
||||
color,
|
||||
conversationType,
|
||||
disabled,
|
||||
headerDate,
|
||||
headerName,
|
||||
i18n,
|
||||
id,
|
||||
isMe,
|
||||
isNoteToSelf,
|
||||
isUsernameSearchResult,
|
||||
isSelected,
|
||||
markedUnread,
|
||||
messageStatusIcon,
|
||||
messageText,
|
||||
messageTextIsAlwaysFullSize,
|
||||
name,
|
||||
onClick,
|
||||
phoneNumber,
|
||||
profileName,
|
||||
sharedGroupNames,
|
||||
shouldShowSpinner,
|
||||
theme,
|
||||
title,
|
||||
unblurredAvatarPath,
|
||||
unreadCount,
|
||||
}) {
|
||||
React.memo(function BaseConversationListItem(props) {
|
||||
const {
|
||||
acceptedMessageRequest,
|
||||
avatarPath,
|
||||
checked,
|
||||
color,
|
||||
conversationType,
|
||||
disabled,
|
||||
headerDate,
|
||||
headerName,
|
||||
i18n,
|
||||
id,
|
||||
isMe,
|
||||
isNoteToSelf,
|
||||
isUsernameSearchResult,
|
||||
isSelected,
|
||||
markedUnread,
|
||||
messageStatusIcon,
|
||||
messageText,
|
||||
messageTextIsAlwaysFullSize,
|
||||
name,
|
||||
onClick,
|
||||
phoneNumber,
|
||||
profileName,
|
||||
sharedGroupNames,
|
||||
shouldShowSpinner,
|
||||
title,
|
||||
unblurredAvatarPath,
|
||||
unreadCount,
|
||||
} = props;
|
||||
|
||||
const identifier = id ? cleanId(id) : undefined;
|
||||
const htmlId = useMemo(() => uuid(), []);
|
||||
const isUnread = isConversationUnread({ markedUnread, unreadCount });
|
||||
|
@ -145,7 +145,6 @@ export const BaseConversationListItem: FunctionComponent<PropsType> =
|
|||
<Avatar
|
||||
acceptedMessageRequest={acceptedMessageRequest}
|
||||
avatarPath={avatarPath}
|
||||
badge={badge}
|
||||
color={color}
|
||||
conversationType={conversationType}
|
||||
noteToSelf={isAvatarNoteToSelf}
|
||||
|
@ -155,11 +154,14 @@ export const BaseConversationListItem: FunctionComponent<PropsType> =
|
|||
name={name}
|
||||
phoneNumber={phoneNumber}
|
||||
profileName={profileName}
|
||||
theme={theme}
|
||||
title={title}
|
||||
sharedGroupNames={sharedGroupNames}
|
||||
size={AvatarSize.FORTY_EIGHT}
|
||||
unblurredAvatarPath={unblurredAvatarPath}
|
||||
// This is here to appease the type checker.
|
||||
{...(props.badge
|
||||
? { badge: props.badge, theme: props.theme }
|
||||
: { badge: undefined })}
|
||||
/>
|
||||
<div
|
||||
className={classNames(
|
||||
|
|
Loading…
Reference in a new issue