Skip to content

Commit

Permalink
fix(Avatar): avatar size + loading skeleton
Browse files Browse the repository at this point in the history
  • Loading branch information
cf-remylenoir committed Sep 18, 2024
1 parent c2b279e commit 0d375e0
Show file tree
Hide file tree
Showing 5 changed files with 80 additions and 24 deletions.
27 changes: 14 additions & 13 deletions packages/components/avatar/src/Avatar/Avatar.styles.ts
Original file line number Diff line number Diff line change
@@ -1,7 +1,12 @@
import { css } from 'emotion';
import tokens from '@contentful/f36-tokens';
import { type AvatarProps } from './Avatar';
import { applyMuted, avatarColorMap, type ColorVariant } from './utils';
import {
applyMuted,
avatarColorMap,
getSizeInPixels,
type ColorVariant,
} from './utils';

export const getColorVariantStyles = (colorVariant: ColorVariant) => {
const colorToken: string = avatarColorMap[colorVariant];
Expand All @@ -16,14 +21,6 @@ export const getColorVariantStyles = (colorVariant: ColorVariant) => {
};
};

export const convertSizeToPixels = (size: AvatarProps['size']) =>
({
tiny: '20px',
small: '24px',
medium: '32px',
large: '48px',
}[size]);

const getInitialsFontSize = (sizePixels: string) =>
Math.round(Number(sizePixels.replace('px', '')) / 2);

Expand All @@ -37,7 +34,8 @@ export const getAvatarStyles = ({
colorVariant: ColorVariant;
}) => {
const borderRadius = variant === 'app' ? tokens.borderRadiusSmall : '100%';
const sizePixels = convertSizeToPixels(size);
const finalSize = getSizeInPixels(size);

const isMuted = colorVariant === 'muted';

return {
Expand All @@ -50,18 +48,21 @@ export const getAvatarStyles = ({
alignItems: 'center',
justifyContent: 'center',
fontStretch: 'semi-condensed',
fontSize: `${getInitialsFontSize(sizePixels)}px`,
fontSize: `${getInitialsFontSize(size)}px`,
}),
image: css({
borderRadius,
display: 'block',
}),
root: css({
borderRadius,
height: sizePixels,
height: finalSize,
overflow: 'hidden',
position: 'relative',
width: sizePixels,
width: finalSize,
svg: {
borderRadius,
},
'&::after': {
borderRadius,
bottom: 0,
Expand Down
17 changes: 8 additions & 9 deletions packages/components/avatar/src/Avatar/Avatar.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -9,10 +9,8 @@ import {
type WithEnhancedContent,
} from '@contentful/f36-tooltip';

import { convertSizeToPixels, getAvatarStyles } from './Avatar.styles';
import type { ColorVariant } from './utils';

export type Size = 'tiny' | 'small' | 'medium' | 'large';
import { getAvatarStyles } from './Avatar.styles';
import { getSizeInPixels, type Size, type ColorVariant } from './utils';

export type Variant = 'app' | 'user';

Expand All @@ -23,9 +21,10 @@ export interface AvatarProps extends CommonProps {
*/
isLoading?: boolean;
/**
* Use the available sizes or a numerical custom one, e.g. '52' or '52px'
* @default 'medium'
*/
size?: Size;
size?: Size | string;
initials?: string;
src?: ImageProps['src'];
/**
Expand Down Expand Up @@ -64,8 +63,8 @@ function _Avatar(
) {
// Only render the fallback when `src` is undefined or an empty string
const isFallback = Boolean(!isLoading && !src);
const styles = getAvatarStyles({ size, variant, colorVariant });
const sizePixels = convertSizeToPixels(size);
const finalSize = getSizeInPixels(size);
const styles = getAvatarStyles({ size: finalSize, variant, colorVariant });

const content = (
<div
Expand All @@ -84,9 +83,9 @@ function _Avatar(
<Image
alt={alt}
className={styles.image}
height={sizePixels}
height={finalSize}
src={src}
width={sizePixels}
width={finalSize}
/>
)}
{!!icon && <span className={styles.overlayIcon}>{icon}</span>}
Expand Down
43 changes: 43 additions & 0 deletions packages/components/avatar/src/Avatar/utils.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,10 @@
import tokens from '@contentful/f36-tokens';

import { AvatarProps } from './Avatar';

export const SIZES = ['tiny', 'small', 'medium', 'large'] as const;
export type Size = (typeof SIZES)[number];

export type ColorVariant = keyof typeof avatarColorMap;

export const avatarColorMap = {
Expand Down Expand Up @@ -33,3 +38,41 @@ export function applyMuted(color: string): string {
// Eventually we should use `color-mix`
// return `color-mix(in srgb, ${color}, ${tokens.colorWhite} 50%)`;
}

/**
* Type guard for size variants
*
* @param size
* @returns true/false if the size is a valid size variant
*/
export const isSizeVariant = (size: string): size is Size => {
return SIZES.includes(size as Size);
};

/**
* Converts the variant size to pixels
*
* @param size
* @returns the variant size value in pixels
*/
export const convertSizeToPixels = (size: AvatarProps['size']) =>
({
tiny: '20px',
small: '24px',
medium: '32px',
large: '48px',
}[size]);

/**
* Utility function to convert the given size variant/custom size to pixels
*
* @param size
* @returns The variant or custom size in pixels, e.g. '32px'
*/
export function getSizeInPixels(size: AvatarProps['size']): string {
return isSizeVariant(size)
? convertSizeToPixels(size)
: size.includes('px')
? size
: `${size}px`;
}
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
import { css } from 'emotion';
import tokens from '@contentful/f36-tokens';
import { type AvatarProps } from '../Avatar/';
import { convertSizeToPixels } from '../Avatar/Avatar.styles';
import { convertSizeToPixels } from '../Avatar/utils';

export const getAvatarGroupStyles = (size: AvatarProps['size']) => {
return {
Expand Down
15 changes: 14 additions & 1 deletion packages/components/avatar/stories/Avatar.stories.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -49,6 +49,17 @@ export const Overview: Story<AvatarProps> = (args) => {
size="large"
icon={<CheckCircleIcon variant="positive" />}
/>
<Avatar
{...args}
size="75"
icon={<CheckCircleIcon variant="positive" />}
/>
<Avatar
{...args}
size="75"
variant="app"
icon={<CheckCircleIcon variant="positive" />}
/>
<Avatar
{...args}
size="large"
Expand Down Expand Up @@ -89,6 +100,8 @@ export const Overview: Story<AvatarProps> = (args) => {
<Avatar isLoading size="small" variant="user" />
<Avatar isLoading size="medium" variant="user" />
<Avatar isLoading size="large" variant="user" />
<Avatar isLoading size="75" variant="user" />
<Avatar isLoading size="75" variant="app" />
<Avatar isLoading size="large" variant="app" />
<Avatar isLoading size="medium" variant="app" />
<Avatar isLoading size="small" variant="app" />
Expand Down Expand Up @@ -186,7 +199,7 @@ export const BorderColors: Story<AvatarProps> = (args) => {
{/* prettier-ignore */}
<Avatar {...argsNoSrc} colorVariant={color} size="small" variant="app" />
{/* prettier-ignore */}
<Avatar {...argsNoSrc} colorVariant={color}size="tiny" variant="app" />
<Avatar {...argsNoSrc} colorVariant={color} size="tiny" variant="app" />
{/* prettier-ignore */}
<Avatar {...args} colorVariant={color} size="tiny" />
{/* prettier-ignore */}
Expand Down

0 comments on commit 0d375e0

Please sign in to comment.