Skip to content

Commit 0e70339

Browse files
feat: migrate avatar to base-ui (#568)
* feat: migrate checkbox to base ui * feat: update checkbox tests and docs * feat: migrate avatar to base ui * fix: avatar tests
1 parent c458960 commit 0e70339

File tree

5 files changed

+54
-40
lines changed

5 files changed

+54
-40
lines changed

apps/www/src/content/docs/components/avatar/index.mdx

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@
22
title: Avatar
33
description: An image element with a fallback for representing the user.
44
source: packages/raystack/components/avatar
5+
tag: new
56
---
67

78
import {

apps/www/src/content/docs/components/avatar/props.ts

Lines changed: 8 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -44,8 +44,14 @@ export interface AvatarProps {
4444
| 'crimson'
4545
| 'gold';
4646

47-
/** Boolean to merge props onto child element */
48-
asChild?: boolean;
47+
/**
48+
* Allows you to replace the component's HTML element with a different tag,
49+
* or compose it with another component. Accepts a ReactElement or a function
50+
* that returns the element to render.
51+
*
52+
* @remarks `ReactElement | function`
53+
*/
54+
render?: React.ReactElement;
4955

5056
/** Additional CSS class names */
5157
className?: string;

packages/raystack/components/avatar/__tests__/avatar.test.tsx

Lines changed: 17 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
import { render, screen } from '@testing-library/react';
2-
import { describe, expect, it, vi } from 'vitest';
2+
import { afterAll, beforeAll, describe, expect, it, vi } from 'vitest';
33
import { Avatar, AvatarGroup } from '../avatar';
44
import styles from '../avatar.module.css';
55
import { getAvatarColor } from '../utils';
@@ -312,6 +312,8 @@ describe('Avatar', () => {
312312

313313
class MockImage extends EventTarget {
314314
_src: string = '';
315+
_complete: boolean = false;
316+
onload: (() => void) | null = null;
315317

316318
constructor() {
317319
super();
@@ -327,20 +329,27 @@ class MockImage extends EventTarget {
327329
return;
328330
}
329331
this._src = src;
330-
this.onSrcChange();
332+
// Simulate async image loading
333+
setTimeout(() => {
334+
this._complete = true;
335+
// Call onload callback if set
336+
if (this.onload) {
337+
this.onload();
338+
}
339+
// Also dispatch the event
340+
this.dispatchEvent(new Event('load'));
341+
}, 0);
331342
}
332343

333344
get complete() {
334-
return !this.src;
345+
return this._complete;
335346
}
336347

337348
get naturalWidth() {
338-
return this.complete ? 300 : 0;
349+
return this._complete ? 300 : 0;
339350
}
340351

341-
onSrcChange() {
342-
setTimeout(() => {
343-
this.dispatchEvent(new Event('load'));
344-
}, 100);
352+
get naturalHeight() {
353+
return this._complete ? 300 : 0;
345354
}
346355
}

packages/raystack/components/avatar/avatar.tsx

Lines changed: 26 additions & 28 deletions
Original file line numberDiff line numberDiff line change
@@ -1,14 +1,12 @@
1-
import { VariantProps, cva, cx } from 'class-variance-authority';
2-
import { Avatar as AvatarPrimitive } from 'radix-ui';
1+
import { Avatar as AvatarPrimitive } from '@base-ui/react/avatar';
2+
import { cva, cx, VariantProps } from 'class-variance-authority';
33
import {
44
ComponentPropsWithoutRef,
5-
ElementRef,
6-
ReactElement,
7-
ReactNode,
85
forwardRef,
9-
isValidElement
6+
isValidElement,
7+
ReactElement,
8+
ReactNode
109
} from 'react';
11-
import { Box } from '../box';
1210
import styles from './avatar.module.css';
1311
import { AVATAR_COLORS } from './utils';
1412

@@ -133,54 +131,54 @@ const image = cva(styles.image);
133131
* @desc Recursively get the avatar props even if it's
134132
* wrapped in another component like Tooltip, Flex, etc.
135133
*/
136-
export const getAvatarProps = (element: ReactElement): AvatarProps => {
137-
const { props } = element;
134+
export const getAvatarProps = (
135+
element: ReactElement<AvatarProps>
136+
): AvatarProps => {
137+
const props = element.props as AvatarProps & { children?: ReactNode };
138138

139139
if (element.type === Avatar) {
140140
return props;
141141
}
142142

143143
if (props.children) {
144-
if (isValidElement(props.children)) {
144+
if (isValidElement<AvatarProps>(props.children)) {
145145
return getAvatarProps(props.children);
146146
}
147147
}
148148
return {};
149149
};
150150

151151
export interface AvatarProps
152-
extends ComponentPropsWithoutRef<typeof AvatarPrimitive.Root>,
152+
extends AvatarPrimitive.Root.Props,
153153
VariantProps<typeof avatar> {
154154
size?: 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13;
155155
src?: string;
156156
alt?: string;
157157
fallback?: ReactNode;
158158
variant?: 'solid' | 'soft';
159159
color?: AVATAR_COLORS;
160-
asChild?: boolean;
161160
className?: string;
162161
}
163162

164-
const AvatarRoot = forwardRef<
165-
ElementRef<typeof AvatarPrimitive.Root>,
166-
AvatarProps
167-
>(
163+
const AvatarRoot = forwardRef<HTMLSpanElement, AvatarProps>(
168164
(
169165
{ className, alt, src, fallback, size, radius, variant, color, ...props },
170166
ref
171167
) => (
172-
<Box className={styles.imageWrapper}>
173-
<AvatarPrimitive.Root
174-
ref={ref}
175-
className={cx(avatar({ size, radius, variant, color }), className)}
176-
{...props}
177-
>
178-
<AvatarPrimitive.Image className={image()} src={src} alt={alt} />
179-
<AvatarPrimitive.Fallback className={styles.fallback}>
180-
{fallback}
181-
</AvatarPrimitive.Fallback>
182-
</AvatarPrimitive.Root>
183-
</Box>
168+
<AvatarPrimitive.Root
169+
ref={ref}
170+
className={cx(
171+
styles.imageWrapper,
172+
avatar({ size, radius, variant, color }),
173+
className
174+
)}
175+
{...props}
176+
>
177+
<AvatarPrimitive.Image className={image()} src={src} alt={alt} />
178+
<AvatarPrimitive.Fallback className={styles.fallback}>
179+
{fallback}
180+
</AvatarPrimitive.Fallback>
181+
</AvatarPrimitive.Root>
184182
)
185183
);
186184

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,2 +1,2 @@
1-
export { Avatar, AvatarGroup } from "./avatar";
2-
export { getAvatarColor } from "./utils";
1+
export { Avatar, AvatarGroup } from './avatar';
2+
export { getAvatarColor } from './utils';

0 commit comments

Comments
 (0)