diff --git a/apps/www/src/content/docs/components/avatar/index.mdx b/apps/www/src/content/docs/components/avatar/index.mdx index 9ff19f09e..1b9b07d7f 100644 --- a/apps/www/src/content/docs/components/avatar/index.mdx +++ b/apps/www/src/content/docs/components/avatar/index.mdx @@ -2,6 +2,7 @@ title: Avatar description: An image element with a fallback for representing the user. source: packages/raystack/components/avatar +tag: new --- import { diff --git a/apps/www/src/content/docs/components/avatar/props.ts b/apps/www/src/content/docs/components/avatar/props.ts index 0180ca048..c8db83473 100644 --- a/apps/www/src/content/docs/components/avatar/props.ts +++ b/apps/www/src/content/docs/components/avatar/props.ts @@ -44,8 +44,14 @@ export interface AvatarProps { | 'crimson' | 'gold'; - /** Boolean to merge props onto child element */ - asChild?: boolean; + /** + * Allows you to replace the component's HTML element with a different tag, + * or compose it with another component. Accepts a ReactElement or a function + * that returns the element to render. + * + * @remarks `ReactElement | function` + */ + render?: React.ReactElement; /** Additional CSS class names */ className?: string; diff --git a/packages/raystack/components/avatar/__tests__/avatar.test.tsx b/packages/raystack/components/avatar/__tests__/avatar.test.tsx index d8ff32a3c..641060525 100644 --- a/packages/raystack/components/avatar/__tests__/avatar.test.tsx +++ b/packages/raystack/components/avatar/__tests__/avatar.test.tsx @@ -1,5 +1,5 @@ import { render, screen } from '@testing-library/react'; -import { describe, expect, it, vi } from 'vitest'; +import { afterAll, beforeAll, describe, expect, it, vi } from 'vitest'; import { Avatar, AvatarGroup } from '../avatar'; import styles from '../avatar.module.css'; import { getAvatarColor } from '../utils'; @@ -312,6 +312,8 @@ describe('Avatar', () => { class MockImage extends EventTarget { _src: string = ''; + _complete: boolean = false; + onload: (() => void) | null = null; constructor() { super(); @@ -327,20 +329,27 @@ class MockImage extends EventTarget { return; } this._src = src; - this.onSrcChange(); + // Simulate async image loading + setTimeout(() => { + this._complete = true; + // Call onload callback if set + if (this.onload) { + this.onload(); + } + // Also dispatch the event + this.dispatchEvent(new Event('load')); + }, 0); } get complete() { - return !this.src; + return this._complete; } get naturalWidth() { - return this.complete ? 300 : 0; + return this._complete ? 300 : 0; } - onSrcChange() { - setTimeout(() => { - this.dispatchEvent(new Event('load')); - }, 100); + get naturalHeight() { + return this._complete ? 300 : 0; } } diff --git a/packages/raystack/components/avatar/avatar.tsx b/packages/raystack/components/avatar/avatar.tsx index 838c47619..ee0a7170c 100644 --- a/packages/raystack/components/avatar/avatar.tsx +++ b/packages/raystack/components/avatar/avatar.tsx @@ -1,14 +1,12 @@ -import { VariantProps, cva, cx } from 'class-variance-authority'; -import { Avatar as AvatarPrimitive } from 'radix-ui'; +import { Avatar as AvatarPrimitive } from '@base-ui/react/avatar'; +import { cva, cx, VariantProps } from 'class-variance-authority'; import { ComponentPropsWithoutRef, - ElementRef, - ReactElement, - ReactNode, forwardRef, - isValidElement + isValidElement, + ReactElement, + ReactNode } from 'react'; -import { Box } from '../box'; import styles from './avatar.module.css'; import { AVATAR_COLORS } from './utils'; @@ -133,15 +131,17 @@ const image = cva(styles.image); * @desc Recursively get the avatar props even if it's * wrapped in another component like Tooltip, Flex, etc. */ -export const getAvatarProps = (element: ReactElement): AvatarProps => { - const { props } = element; +export const getAvatarProps = ( + element: ReactElement +): AvatarProps => { + const props = element.props as AvatarProps & { children?: ReactNode }; if (element.type === Avatar) { return props; } if (props.children) { - if (isValidElement(props.children)) { + if (isValidElement(props.children)) { return getAvatarProps(props.children); } } @@ -149,7 +149,7 @@ export const getAvatarProps = (element: ReactElement): AvatarProps => { }; export interface AvatarProps - extends ComponentPropsWithoutRef, + extends AvatarPrimitive.Root.Props, VariantProps { size?: 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13; src?: string; @@ -157,30 +157,28 @@ export interface AvatarProps fallback?: ReactNode; variant?: 'solid' | 'soft'; color?: AVATAR_COLORS; - asChild?: boolean; className?: string; } -const AvatarRoot = forwardRef< - ElementRef, - AvatarProps ->( +const AvatarRoot = forwardRef( ( { className, alt, src, fallback, size, radius, variant, color, ...props }, ref ) => ( - - - - - {fallback} - - - + + + + {fallback} + + ) ); diff --git a/packages/raystack/components/avatar/index.tsx b/packages/raystack/components/avatar/index.tsx index 70db771c1..80be713d5 100644 --- a/packages/raystack/components/avatar/index.tsx +++ b/packages/raystack/components/avatar/index.tsx @@ -1,2 +1,2 @@ -export { Avatar, AvatarGroup } from "./avatar"; -export { getAvatarColor } from "./utils"; +export { Avatar, AvatarGroup } from './avatar'; +export { getAvatarColor } from './utils';