Skip to content

Commit 11848ee

Browse files
committed
feat(avatar): Added ability to pass props to <img>
Closes #908
1 parent 49319e6 commit 11848ee

File tree

2 files changed

+98
-6
lines changed

2 files changed

+98
-6
lines changed

packages/avatar/src/Avatar.tsx

Lines changed: 51 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,8 @@
1-
import React, { forwardRef, HTMLAttributes } from "react";
1+
import React, { forwardRef, HTMLAttributes, ImgHTMLAttributes } from "react";
22
import cn from "classnames";
3-
import { bem } from "@react-md/utils";
3+
import { bem, PropsWithRef } from "@react-md/utils";
4+
5+
type ImgAttributes = ImgHTMLAttributes<HTMLImageElement>;
46

57
export interface AvatarProps extends HTMLAttributes<HTMLSpanElement> {
68
/**
@@ -21,6 +23,29 @@ export interface AvatarProps extends HTMLAttributes<HTMLSpanElement> {
2123
*/
2224
alt?: string;
2325

26+
/**
27+
* An optional `referrerPolicy` to provide to the `<img>` element if the `src`
28+
* or `imgProps` props are provided.
29+
*
30+
* @see https://developer.mozilla.org/en-US/docs/Web/HTML/Element/img#attr-referrerpolicy
31+
*
32+
* @since 2.2.0
33+
*/
34+
referrerPolicy?: ImgAttributes["referrerPolicy"];
35+
36+
/**
37+
* An optional object of image props and ref that can be used to create an
38+
* image within the `Avatar`. This can be useful to add a custom `style`
39+
* or`className` to the `<img>` element if that additional customization is
40+
* needed.
41+
*
42+
* Note: The values in this object will override the `src`, `alt`, and
43+
* `referrerPolicy` root level avatar props if they exist on this object.
44+
*
45+
* @since 2.2.0
46+
*/
47+
imgProps?: PropsWithRef<ImgAttributes, HTMLImageElement>;
48+
2449
/**
2550
* An optional color to apply to the avatar. This will apply a className of
2651
* `rmd-avatar--${color}`, so only the keys from the `$rmd-avatar-colors` Map
@@ -40,12 +65,29 @@ const block = bem("rmd-avatar");
4065
* avatar more unique.
4166
*/
4267
const Avatar = forwardRef<HTMLSpanElement, AvatarProps>(function Avatar(
43-
{ className, children, src, alt = "", color = "", ...props },
68+
{
69+
className,
70+
children,
71+
src,
72+
alt = "",
73+
color = "",
74+
imgProps,
75+
referrerPolicy,
76+
...props
77+
},
4478
ref
4579
) {
4680
let img;
47-
if (src) {
48-
img = <img src={src} alt={alt} className={block("image")} />;
81+
if (src || imgProps) {
82+
img = (
83+
<img
84+
src={src}
85+
alt={alt}
86+
referrerPolicy={referrerPolicy}
87+
{...imgProps}
88+
className={cn(block("image"), imgProps?.className)}
89+
/>
90+
);
4991
}
5092

5193
return (
@@ -70,6 +112,10 @@ if (process.env.NODE_ENV !== "production") {
70112
color: PropTypes.string,
71113
className: PropTypes.string,
72114
children: PropTypes.node,
115+
// Note: The MDN website has a lot more values, but this is what Typescript
116+
// says is valid at the time of writing this
117+
referrerPolicy: PropTypes.oneOf(["no-referrer", "origin", "unsafe-url"]),
118+
imgProps: PropTypes.object,
73119
};
74120
} catch (e) {}
75121
}

packages/avatar/src/__tests__/Avatar.tsx

Lines changed: 47 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
import React from "react";
1+
import React, { createRef } from "react";
22
import { render } from "@testing-library/react";
33

44
import Avatar from "../Avatar";
@@ -89,4 +89,50 @@ describe("Avatar", () => {
8989
rerender(<Avatar color="red" src="https://example.com" />);
9090
expect(container).toMatchSnapshot();
9191
});
92+
93+
it("should pass the referrerPolicy to the img element when the src prop was provided", () => {
94+
const { getByAltText, rerender } = render(
95+
<Avatar referrerPolicy="no-referrer">A</Avatar>
96+
);
97+
98+
expect(() => getByAltText("")).toThrow();
99+
100+
rerender(<Avatar referrerPolicy="no-referrer" src="https://example.com" />);
101+
const img = getByAltText("");
102+
expect(img).toHaveAttribute("referrerpolicy", "no-referrer");
103+
});
104+
105+
it("should render an img element if the imgProps are provided", () => {
106+
const imgProps = { src: "https://example.com" };
107+
const { getByAltText } = render(<Avatar imgProps={imgProps} />);
108+
109+
expect(getByAltText("")).not.toBeNull();
110+
});
111+
112+
it('should correctly merge the imgProps with the "src", "alt", and "referrerPolicy" props', () => {
113+
const props = {
114+
src: "https://example.com",
115+
alt: "",
116+
referrerPolicy: "no-referrer" as const,
117+
imgProps: {
118+
alt: "An Image",
119+
className: "custom",
120+
referrerPolicy: "origin" as const,
121+
},
122+
};
123+
124+
const { getByAltText } = render(<Avatar {...props} />);
125+
const img = getByAltText("An Image");
126+
127+
expect(img).toHaveAttribute("src", props.src);
128+
expect(img.className).toContain("custom");
129+
expect(img).toHaveAttribute("referrerpolicy", "origin");
130+
});
131+
132+
it("should allow for a ref to be passed to the img element with the imgProps", () => {
133+
const ref = createRef<HTMLImageElement>();
134+
render(<Avatar src="https://example.com" imgProps={{ ref }} />);
135+
136+
expect(ref.current).toBeInstanceOf(HTMLImageElement);
137+
});
92138
});

0 commit comments

Comments
 (0)