From c5b91a3fe2d5346d15b2209edafe714aa712b6f4 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?R=C3=A9my=20Lenoir?= Date: Tue, 17 Sep 2024 15:56:01 +0200 Subject: [PATCH 1/8] chore: remove extra component from story --- packages/components/avatar/stories/Avatar.stories.tsx | 1 - 1 file changed, 1 deletion(-) diff --git a/packages/components/avatar/stories/Avatar.stories.tsx b/packages/components/avatar/stories/Avatar.stories.tsx index 1a4814ebb7..3fb1a37354 100644 --- a/packages/components/avatar/stories/Avatar.stories.tsx +++ b/packages/components/avatar/stories/Avatar.stories.tsx @@ -90,7 +90,6 @@ export const Overview: Story = (args) => { - From c2b279e5d95345c0b994466d8c8e742f6f422987 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?R=C3=A9my=20Lenoir?= Date: Tue, 17 Sep 2024 15:57:05 +0200 Subject: [PATCH 2/8] chore: add loading state to all --- .../components/avatar/stories/Avatar.stories.tsx | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/packages/components/avatar/stories/Avatar.stories.tsx b/packages/components/avatar/stories/Avatar.stories.tsx index 3fb1a37354..a7152a13e2 100644 --- a/packages/components/avatar/stories/Avatar.stories.tsx +++ b/packages/components/avatar/stories/Avatar.stories.tsx @@ -85,14 +85,14 @@ export const Overview: Story = (args) => { gap="spacingS" marginBottom="spacingM" > - - - + + + - - - - + + + + From 0d375e0eee87413f4f56bef129614203b7086f15 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?R=C3=A9my=20Lenoir?= Date: Wed, 18 Sep 2024 16:07:34 +0200 Subject: [PATCH 3/8] fix(Avatar): avatar size + loading skeleton --- .../avatar/src/Avatar/Avatar.styles.ts | 27 ++++++------ .../components/avatar/src/Avatar/Avatar.tsx | 17 ++++---- .../components/avatar/src/Avatar/utils.ts | 43 +++++++++++++++++++ .../src/AvatarGroup/AvatarGroup.styles.ts | 2 +- .../avatar/stories/Avatar.stories.tsx | 15 ++++++- 5 files changed, 80 insertions(+), 24 deletions(-) diff --git a/packages/components/avatar/src/Avatar/Avatar.styles.ts b/packages/components/avatar/src/Avatar/Avatar.styles.ts index e3e04f0204..2d0bdb206e 100644 --- a/packages/components/avatar/src/Avatar/Avatar.styles.ts +++ b/packages/components/avatar/src/Avatar/Avatar.styles.ts @@ -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]; @@ -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); @@ -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 { @@ -50,7 +48,7 @@ export const getAvatarStyles = ({ alignItems: 'center', justifyContent: 'center', fontStretch: 'semi-condensed', - fontSize: `${getInitialsFontSize(sizePixels)}px`, + fontSize: `${getInitialsFontSize(size)}px`, }), image: css({ borderRadius, @@ -58,10 +56,13 @@ export const getAvatarStyles = ({ }), root: css({ borderRadius, - height: sizePixels, + height: finalSize, overflow: 'hidden', position: 'relative', - width: sizePixels, + width: finalSize, + svg: { + borderRadius, + }, '&::after': { borderRadius, bottom: 0, diff --git a/packages/components/avatar/src/Avatar/Avatar.tsx b/packages/components/avatar/src/Avatar/Avatar.tsx index a2397db0c0..a73b58d686 100644 --- a/packages/components/avatar/src/Avatar/Avatar.tsx +++ b/packages/components/avatar/src/Avatar/Avatar.tsx @@ -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'; @@ -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']; /** @@ -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 = (
)} {!!icon && {icon}} diff --git a/packages/components/avatar/src/Avatar/utils.ts b/packages/components/avatar/src/Avatar/utils.ts index a7e1277f86..495c990451 100644 --- a/packages/components/avatar/src/Avatar/utils.ts +++ b/packages/components/avatar/src/Avatar/utils.ts @@ -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 = { @@ -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`; +} diff --git a/packages/components/avatar/src/AvatarGroup/AvatarGroup.styles.ts b/packages/components/avatar/src/AvatarGroup/AvatarGroup.styles.ts index 43d44dc263..5c70958350 100644 --- a/packages/components/avatar/src/AvatarGroup/AvatarGroup.styles.ts +++ b/packages/components/avatar/src/AvatarGroup/AvatarGroup.styles.ts @@ -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 { diff --git a/packages/components/avatar/stories/Avatar.stories.tsx b/packages/components/avatar/stories/Avatar.stories.tsx index a7152a13e2..9ff2388c0d 100644 --- a/packages/components/avatar/stories/Avatar.stories.tsx +++ b/packages/components/avatar/stories/Avatar.stories.tsx @@ -49,6 +49,17 @@ export const Overview: Story = (args) => { size="large" icon={} /> + } + /> + } + /> = (args) => { + + @@ -186,7 +199,7 @@ export const BorderColors: Story = (args) => { {/* prettier-ignore */} {/* prettier-ignore */} - + {/* prettier-ignore */} {/* prettier-ignore */} From 6e04018264b4d643ae85840fc9f899e21fec8a26 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?R=C3=A9my=20Lenoir?= Date: Fri, 20 Sep 2024 10:24:23 +0200 Subject: [PATCH 4/8] refactor: use template literal typing --- .../components/avatar/src/Avatar/Avatar.tsx | 11 +++++++--- .../components/avatar/src/Avatar/utils.ts | 20 ++++++++++--------- .../avatar/stories/Avatar.stories.tsx | 8 ++++---- 3 files changed, 23 insertions(+), 16 deletions(-) diff --git a/packages/components/avatar/src/Avatar/Avatar.tsx b/packages/components/avatar/src/Avatar/Avatar.tsx index a73b58d686..d2959cb54b 100644 --- a/packages/components/avatar/src/Avatar/Avatar.tsx +++ b/packages/components/avatar/src/Avatar/Avatar.tsx @@ -10,7 +10,12 @@ import { } from '@contentful/f36-tooltip'; import { getAvatarStyles } from './Avatar.styles'; -import { getSizeInPixels, type Size, type ColorVariant } from './utils'; +import { + getSizeInPixels, + type ColorVariant, + type Size, + type SizeInPixel, +} from './utils'; export type Variant = 'app' | 'user'; @@ -21,10 +26,10 @@ export interface AvatarProps extends CommonProps { */ isLoading?: boolean; /** - * Use the available sizes or a numerical custom one, e.g. '52' or '52px' + * Use the available sizes or a numerical custom one, e.g. '52px' * @default 'medium' */ - size?: Size | string; + size?: Size | SizeInPixel; initials?: string; src?: ImageProps['src']; /** diff --git a/packages/components/avatar/src/Avatar/utils.ts b/packages/components/avatar/src/Avatar/utils.ts index 495c990451..f9b7a5eeb5 100644 --- a/packages/components/avatar/src/Avatar/utils.ts +++ b/packages/components/avatar/src/Avatar/utils.ts @@ -4,6 +4,7 @@ import { AvatarProps } from './Avatar'; export const SIZES = ['tiny', 'small', 'medium', 'large'] as const; export type Size = (typeof SIZES)[number]; +export type SizeInPixel = `${number}px`; export type ColorVariant = keyof typeof avatarColorMap; @@ -55,13 +56,16 @@ export const isSizeVariant = (size: string): size is Size => { * @param size * @returns the variant size value in pixels */ -export const convertSizeToPixels = (size: AvatarProps['size']) => - ({ +export const convertSizeToPixels = (size: AvatarProps['size']): SizeInPixel => { + const sizes: Record = { tiny: '20px', small: '24px', medium: '32px', large: '48px', - }[size]); + }; + + return sizes[size]; +}; /** * Utility function to convert the given size variant/custom size to pixels @@ -69,10 +73,8 @@ export const convertSizeToPixels = (size: AvatarProps['size']) => * @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`; +export function getSizeInPixels( + size: AvatarProps['size'], +): AvatarProps['size'] { + return isSizeVariant(size) ? convertSizeToPixels(size) : size; } diff --git a/packages/components/avatar/stories/Avatar.stories.tsx b/packages/components/avatar/stories/Avatar.stories.tsx index 9ff2388c0d..4ebb62ddcb 100644 --- a/packages/components/avatar/stories/Avatar.stories.tsx +++ b/packages/components/avatar/stories/Avatar.stories.tsx @@ -51,12 +51,12 @@ export const Overview: Story = (args) => { /> } /> } /> @@ -100,8 +100,8 @@ export const Overview: Story = (args) => { - - + + From 77e17248a201a22169128f6a3612119eeb9e001e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?R=C3=A9my=20Lenoir?= Date: Fri, 20 Sep 2024 14:43:08 +0200 Subject: [PATCH 5/8] chore: add broken src example --- .../avatar/stories/Avatar.stories.tsx | 22 +++++++++++++++++++ 1 file changed, 22 insertions(+) diff --git a/packages/components/avatar/stories/Avatar.stories.tsx b/packages/components/avatar/stories/Avatar.stories.tsx index 4ebb62ddcb..703c95f6ab 100644 --- a/packages/components/avatar/stories/Avatar.stories.tsx +++ b/packages/components/avatar/stories/Avatar.stories.tsx @@ -108,6 +108,28 @@ export const Overview: Story = (args) => { + + With a broken source, the loading skeleton is also rendered + + + + + + + + + + + + + + + Indicator properties From 666cdcf708793fed63710200e3504fefdfe80d33 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?R=C3=A9my=20Lenoir?= Date: Fri, 20 Sep 2024 14:45:10 +0200 Subject: [PATCH 6/8] refactor: adjust typing --- packages/components/avatar/src/Avatar/utils.ts | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/packages/components/avatar/src/Avatar/utils.ts b/packages/components/avatar/src/Avatar/utils.ts index f9b7a5eeb5..b3758df525 100644 --- a/packages/components/avatar/src/Avatar/utils.ts +++ b/packages/components/avatar/src/Avatar/utils.ts @@ -73,8 +73,6 @@ export const convertSizeToPixels = (size: AvatarProps['size']): SizeInPixel => { * @param size * @returns The variant or custom size in pixels, e.g. '32px' */ -export function getSizeInPixels( - size: AvatarProps['size'], -): AvatarProps['size'] { +export function getSizeInPixels(size: AvatarProps['size']): SizeInPixel { return isSizeVariant(size) ? convertSizeToPixels(size) : size; } From 99c07bd2c8ab5c4ec21f4ebc3cf02564ddbe7839 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?R=C3=A9my=20Lenoir?= Date: Fri, 20 Sep 2024 15:02:13 +0200 Subject: [PATCH 7/8] chore: add fix changelog --- .changeset/fuzzy-rules-pump.md | 5 +++++ 1 file changed, 5 insertions(+) create mode 100644 .changeset/fuzzy-rules-pump.md diff --git a/.changeset/fuzzy-rules-pump.md b/.changeset/fuzzy-rules-pump.md new file mode 100644 index 0000000000..9caa4f04f9 --- /dev/null +++ b/.changeset/fuzzy-rules-pump.md @@ -0,0 +1,5 @@ +--- +"@contentful/f36-avatar": patch +--- + +Avatar size and loading skeleton From 2ae8735e8db23ef2cd9cd96a7ee65d553b9ffc29 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?R=C3=A9my=20Lenoir?= Date: Fri, 20 Sep 2024 15:02:25 +0200 Subject: [PATCH 8/8] chore: add feat changelog --- .changeset/hot-ears-crash.md | 5 +++++ 1 file changed, 5 insertions(+) create mode 100644 .changeset/hot-ears-crash.md diff --git a/.changeset/hot-ears-crash.md b/.changeset/hot-ears-crash.md new file mode 100644 index 0000000000..2b0d624ac7 --- /dev/null +++ b/.changeset/hot-ears-crash.md @@ -0,0 +1,5 @@ +--- +"@contentful/f36-avatar": feat +--- + +Allow custom size