@stackshift-ui/avatar 6.0.13-beta.2 → 7.0.0-beta.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "@stackshift-ui/avatar",
3
3
  "description": "",
4
- "version": "6.0.13-beta.2",
4
+ "version": "7.0.0-beta.0",
5
5
  "private": false,
6
6
  "sideEffects": false,
7
7
  "main": "./dist/index.js",
@@ -14,6 +14,8 @@
14
14
  "author": "WebriQ <info@webriq.com>",
15
15
  "devDependencies": {
16
16
  "@testing-library/react": "^16.0.1",
17
+ "@testing-library/jest-dom": "^6.5.0",
18
+ "@testing-library/user-event": "^14.6.1",
17
19
  "@types/node": "^22.7.0",
18
20
  "@types/react": "^18.3.9",
19
21
  "@types/react-dom": "^18.3.0",
@@ -29,22 +31,23 @@
29
31
  "typescript": "^5.6.2",
30
32
  "vite-tsconfig-paths": "^5.0.1",
31
33
  "vitest": "^2.1.1",
32
- "@stackshift-ui/eslint-config": "6.0.10-beta.2",
33
- "@stackshift-ui/typescript-config": "6.0.10-beta.2"
34
+ "@stackshift-ui/typescript-config": "6.0.10",
35
+ "@stackshift-ui/eslint-config": "6.0.10"
34
36
  },
35
37
  "dependencies": {
38
+ "@radix-ui/react-avatar": "^1.1.10",
36
39
  "classnames": "^2.5.1",
37
- "@stackshift-ui/system": "6.0.11-beta.2",
38
- "@stackshift-ui/image": "6.0.11-beta.2",
39
- "@stackshift-ui/scripts": "6.0.10-beta.2",
40
- "@stackshift-ui/flex": "6.0.11-beta.2"
40
+ "@stackshift-ui/scripts": "6.1.0-beta.0",
41
+ "@stackshift-ui/flex": "7.0.0-beta.0",
42
+ "@stackshift-ui/image": "6.1.0-beta.0",
43
+ "@stackshift-ui/system": "6.1.0-beta.0"
41
44
  },
42
45
  "peerDependencies": {
46
+ "@stackshift-ui/system": ">=6.1.0-beta.0",
43
47
  "@types/react": "16.8 - 19",
44
48
  "next": "10 - 14",
45
49
  "react": "16.8 - 19",
46
- "react-dom": "16.8 - 19",
47
- "@stackshift-ui/system": ">=6.0.11-beta.2"
50
+ "react-dom": "16.8 - 19"
48
51
  },
49
52
  "peerDependenciesMeta": {
50
53
  "next": {
@@ -1,24 +1,76 @@
1
- import { cleanup, render, screen } from "@testing-library/react";
2
- import { afterEach, describe, test } from "vitest";
3
- import { Avatar } from "./avatar";
4
-
5
- describe.concurrent("avatar", () => {
6
- afterEach(cleanup);
7
-
8
- test("Common: Avatar - test if renders without errors", ({ expect }) => {
9
- const clx = "avatar-class";
10
-
11
- render(
12
- <Avatar
13
- className={clx}
14
- src="https://via.placeholder.com/150"
15
- alt="Sample Avatar"
16
- text="StackShift"
17
- />,
18
- );
19
- const elements = screen.getAllByTestId("div");
20
- elements.forEach(element => {
21
- expect(element.classList).toBeDefined();
1
+ import { fireEvent, render, screen } from "@testing-library/react";
2
+ import { beforeEach } from "node:test";
3
+ import React from "react";
4
+ import { afterEach, describe, expect, test, vi } from "vitest";
5
+ import { Avatar, AvatarFallback, AvatarImage } from "./avatar";
6
+
7
+ describe("avatar", () => {
8
+ afterEach(() => {
9
+ vi.clearAllMocks();
10
+ });
11
+
12
+ beforeEach(() => {
13
+ vi.mock("./avatar", async () => {
14
+ const actual = await vi.importActual("./avatar");
15
+
16
+ return {
17
+ ...actual,
18
+ AvatarImage: (props: React.ImgHTMLAttributes<HTMLImageElement>) => {
19
+ return <img {...props} />;
20
+ },
21
+ };
22
22
  });
23
23
  });
24
+
25
+ test("Common: Avatar - test if renders image when src is provided", () => {
26
+ const loadMock = vi.fn();
27
+
28
+ const { unmount } = render(
29
+ <Avatar data-testid="avatar" className="avatar-class">
30
+ <AvatarImage
31
+ data-testid="avatar-image"
32
+ onLoad={loadMock}
33
+ src="https://github.com/shadcn.png"
34
+ alt="@shadcn"
35
+ />
36
+ <AvatarFallback>CN</AvatarFallback>
37
+ </Avatar>,
38
+ );
39
+
40
+ const image = screen.getByRole("img");
41
+ fireEvent.load(image);
42
+ expect(loadMock).toHaveBeenCalled();
43
+
44
+ const avatar = screen.getByTestId("avatar");
45
+ expect(avatar).toHaveClass("avatar-class");
46
+ expect(image).toHaveAttribute("src", "https://github.com/shadcn.png");
47
+ expect(image).toHaveAttribute("alt", "@shadcn");
48
+ unmount();
49
+ });
50
+
51
+ test("Common: Avatar - test if renders fallback when image fails to load", () => {
52
+ const loadMock = vi.fn();
53
+ const errorMock = vi.fn();
54
+
55
+ const { unmount } = render(
56
+ <Avatar data-testid="avatar" className="avatar-class">
57
+ <AvatarImage
58
+ data-testid="avatar-image"
59
+ onLoad={loadMock}
60
+ onError={errorMock}
61
+ src="https://github.com/shadcn.png"
62
+ alt="@shadcn"
63
+ />
64
+ <AvatarFallback>CN</AvatarFallback>
65
+ </Avatar>,
66
+ );
67
+
68
+ const image = screen.getByRole("img");
69
+ fireEvent.error(image);
70
+ expect(errorMock).toHaveBeenCalled();
71
+
72
+ const fallback = screen.getByText("CN");
73
+ expect(fallback).toBeInTheDocument();
74
+ unmount();
75
+ });
24
76
  });
package/src/avatar.tsx CHANGED
@@ -1,76 +1,63 @@
1
- import { Flex } from "@stackshift-ui/flex";
2
- import { Image } from "@stackshift-ui/image";
3
- import { DefaultComponent, useStackShiftUIComponents } from "@stackshift-ui/system";
4
- import cn from "classnames";
5
- import type { ElementType, HTMLProps, ReactNode } from "react";
6
- import { useState } from "react";
1
+ import * as AvatarPrimitive from "@radix-ui/react-avatar";
2
+ import { cn, DefaultComponent, useStackShiftUIComponents } from "@stackshift-ui/system";
3
+ import * as React from "react";
7
4
 
8
- type ImageSize = "sm" | "md" | "lg" | "xl";
5
+ const displayNameAvatar = "Avatar";
6
+ const displayNameAvatarImage = "AvatarImage";
7
+ const displayNameAvatarFallback = "AvatarFallback";
9
8
 
10
- export interface AvatarProps extends Omit<HTMLProps<HTMLElement>, "as" | "size"> {
11
- src: string;
12
- alt: string;
13
- size?: ImageSize | number;
14
- text?: string;
15
- children?: ReactNode;
16
- className?: string;
17
- as?: ElementType;
18
- }
9
+ const Avatar = React.forwardRef<
10
+ React.ElementRef<typeof AvatarPrimitive.Root>,
11
+ React.ComponentPropsWithoutRef<typeof AvatarPrimitive.Root>
12
+ >(({ className, ...props }, ref) => {
13
+ const { [displayNameAvatar]: Component = DefaultComponent } = useStackShiftUIComponents();
19
14
 
20
- const displayName = "Avatar";
21
-
22
- export const Avatar: React.FC<AvatarProps> = ({
23
- src,
24
- alt = "image",
25
- size = "sm",
26
- text,
27
- children,
28
- className,
29
- as,
30
- ...props
31
- }) => {
32
- const components = useStackShiftUIComponents();
33
- const { [displayName]: Component = DefaultComponent } = components;
15
+ return (
16
+ <Component
17
+ as={AvatarPrimitive.Root}
18
+ ref={ref}
19
+ className={cn("relative flex h-10 w-10 shrink-0 overflow-hidden rounded-full", className)}
20
+ {...props}
21
+ />
22
+ );
23
+ });
24
+ Avatar.displayName = displayNameAvatar;
34
25
 
35
- const [loaded, setLoaded] = useState(false);
36
- const sizeMap = {
37
- sm: 40,
38
- md: 80,
39
- lg: 120,
40
- xl: 160,
41
- };
42
- const avatarSize = typeof size === "number" ? `${size}px` : `${sizeMap[size]}px`;
43
- const initials = text ? text?.split(" ")?.reduce((acc, curr) => acc + curr[0], "") : "AB";
44
- const baseClass = `relative flex rounded-full aspect-square overflow-hidden border-2 border-solid border-primary`;
26
+ const AvatarImage = React.forwardRef<
27
+ React.ElementRef<typeof AvatarPrimitive.Image>,
28
+ React.ComponentPropsWithoutRef<typeof AvatarPrimitive.Image>
29
+ >(({ className, ...props }, ref) => {
30
+ const { [displayNameAvatarImage]: Component = DefaultComponent } = useStackShiftUIComponents();
45
31
 
46
32
  return (
47
33
  <Component
48
- as={as}
49
- className={cn(baseClass, className)}
50
- style={{ maxWidth: avatarSize }}
34
+ as={AvatarPrimitive.Image}
35
+ ref={ref}
36
+ className={cn("aspect-square h-full w-full", className)}
51
37
  {...props}
52
- data-testid={displayName}>
53
- {(!loaded || !src) && (
54
- <Flex align="center" justify="center" className="w-full h-full bg-primary">
55
- <p
56
- style={{
57
- fontSize: `calc(${avatarSize}/2)`,
58
- }}
59
- className="text-white">
60
- {initials}
61
- </p>
62
- </Flex>
63
- )}
64
- {src && (
65
- <Image
66
- className="object-cover object-center w-[100%] h-[100%]"
67
- src={src}
68
- alt={alt}
69
- width={100}
70
- height={100}
71
- onLoad={() => setLoaded(true)}
72
- />
38
+ />
39
+ );
40
+ });
41
+ AvatarImage.displayName = displayNameAvatarImage;
42
+
43
+ const AvatarFallback = React.forwardRef<
44
+ React.ElementRef<typeof AvatarPrimitive.Fallback>,
45
+ React.ComponentPropsWithoutRef<typeof AvatarPrimitive.Fallback>
46
+ >(({ className, ...props }, ref) => {
47
+ const { [displayNameAvatarFallback]: Component = DefaultComponent } = useStackShiftUIComponents();
48
+
49
+ return (
50
+ <Component
51
+ as={AvatarPrimitive.Fallback}
52
+ ref={ref}
53
+ className={cn(
54
+ "flex h-full w-full items-center justify-center rounded-full bg-muted",
55
+ className,
73
56
  )}
74
- </Component>
57
+ {...props}
58
+ />
75
59
  );
76
- };
60
+ });
61
+ AvatarFallback.displayName = displayNameAvatarFallback;
62
+
63
+ export { Avatar, AvatarFallback, AvatarImage };
@@ -0,0 +1,4 @@
1
+ import '@testing-library/jest-dom';
2
+
3
+ export { };
4
+