Repository: mui/material-ui (TypeScript)
Date: 2019-10-24 23:16:52+00:00
Task type: Bug Fix

Issue:
Avatar shows children and image
- [x] The issue is present in the latest release.
- [x] I have searched the [issues](https://github.com/mui-org/material-ui/issues) of this repository and believe that this is not a duplicate.

## Current Behavior 😯

When I pass in children and a `src` prop to the `Avatar` component, both are rendered. This is a change in behaviour that was not expected, and was caused by #17693. 

![image](https://user-images.githubusercontent.com/867941/66757592-62b35080-ee94-11e9-828b-c16f4caeae78.png)


## Expected Behavior 🤔

![image](https://user-images.githubusercontent.com/867941/66757552-54fdcb00-ee94-11e9-9d8f-3417b783f384.png)


## Steps to Reproduce 🕹

Provide children and a `src` to the Avatar component, like so:

```jsx
<Avatar src={person.avatar} className={classes.avatarPerson}>
  {person.name.substr(0, 1)}
</Avatar>
```

## Context 🔦

We use this to display user profile images, but fallback to the user name initials if the image doesn't exist.

## Your Environment 🌎


| Tech        | Version |
| ----------- | ------- |
| Material-UI | v4.2.1  |



Issue discussion:
@ajdaniel It sounds like you have to pick between one of the two approaches. Why do you mix both?
That's how it worked before. The children prop became a fallback and I didn't have to code the logic of showing or hiding the children based on the image availability, it just did that automatically.
The children prop description:

> Used to render icon or text elements inside the Avatar. src and alt props will not be used and no img will be rendered by default.
This can be an element, or just a string.

It seems that prior to #17693, the implementation behave the opposite to the documentation. 
Okay that's not a problem, I can't argue against undocumented features. I had a feeling this was the case too, but I wanted to raise the point.

Huh, perhaps we need to clarify the props in the component too.

Perhaps we can add it as a feature? Perhaps instead of children we could have a prop like `fallbackRender` which renders if the `src` or `srcSet` is empty? 
I would propose that we 1. update the prop description to match reality. 2. I wouldn't be against a customization demo per @lcswillems use case. 3. a test case to it.

Regarding your fallback proposal, `imgFallbackChildren?: boolean` has potential. However, I'm wondering if it's not better, for their developer to have the logic visible in their render methods. @mbrookes what do you think? 
@oliviertassinari I am not sure to understand what you expect from me?
Nothing, I was including you for context :) 
> `imgFallbackChildren?: boolean`

That sounds horrible! 🙀We should pick one behaviour and stick to it. If #17693 hadn't already been merged, I'd favour @ajdaniel's use case over @lcswillems', and suggest a customization demo for the latter instead.

Not sure what the best path forward is though, short of reverting #17693 and updating the documentation.
@mbrookes Why would you prefer @ajdaniel use case over mine?
@mbrookes What do you think of the behavior in the children prop description? It seems to be a 3rd viable option:

1. children < img (#17876)
1. children + img (#17693)
1. children > img (API docs)
@lcswillems Because displaying one type of content in an Avatar is the norm, whereas your requirement is more of a customisation.

@oliviertassinari Why would 3. be preferable? From my experience, text is always used as a fallback to an image, not the other way around.
The problem with `children > img` is that the avatar img behavior will have to be reimplemented.

If we do `children + img`, @ajdaniel just have to conditionally display its children if `src` is set, that is much easier.

I think @ajdaniel filled an issue just because it broke his code, not necessarily because it was the good use case.
@mbrookes Why not have a `fallbackText` property and let the `children` be used.
> That sounds horrible! 🙀

@mbrookes Yeah, I agree.

In my opinion, the fallback logic should be on the userland. I think that people should be able to infer it from reading their code:

```jsx
<Avatar src={person.avatar} className={classes.avatarPerson}>
  {person.avatar ? null : person.name.substr(0, 1)}
</Avatar>
```

Otherwise, it's not obvious what will happen if both information is present, or if one is missing.
I do agree that an avatar image is a piece of superior information to the initial letters in the most common use cases, but what if somebody wants to display both?

Now, I don't have a strong preference between 1. and 2. The avatar was designed to display the information, not to edit it. @lcswillems could write its own logic, at this point, it could be simpler to bypass the Avatar.

I would propose:
1. @mbrookes choose the preferred approach.
2. We add a test case about it.
3. We update the prop description to match reality.

```jsx
<Avatar src={person.avatar} className={classes.avatarPerson}>
  {person.avatar ? null : person.name.substr(0, 1)}
</Avatar>
```

That doesn't seem unreasonable.

> what if somebody wants to display both?

In that case should the children be displayed _over_ the image (rather than beside it)?


@mbrookes I don't know, the use case I had in mind is somebody that want to apply customizations. But maybe it's an edge case and more people would benefit from the fallback behavior.
```jsx
<Avatar src={person.avatar} className={classes.avatarPerson}>
  {person.avatar ? null : person.name.substr(0, 1)}
</Avatar>
```

If this is up for taking can I work on it? :)
Just to clarify my understanding of this thread.

Was wondering if you see fallback, covering the un-happy path, where src does not load and an `onError` occurs ?

When using `src` (or `srcSet`), I saw `children` as a fallback for the `onError` condition.
Whilst you have the `alt` prop, it does not look so great in practice, with style, `borderRadius: 50%`

Also, worth mentioning one of your other issues. not un-related to this.
https://github.com/mui-org/material-ui/issues/16161
Not sure, how valid that ask is but worth mentioning in this context.

@slipmat In practice is it frequent for the image URL to be broken? I would be in favor to ignore this problem.
@mbrookes In which direction should we go?
Ant Design works that way, although that's not a reason to follow suit.

With MUI you can pass through onError via imgProps, so there are ways around it.
Could be one to cover via the docs, if needed.
The userland fallback approach isn't too onerous. My overriding concern is that #17694 is a breaking change for developers depending on the previous (if incorrectly documented) logic.

Solution patch:
diff --git a/docs/pages/api/avatar.md b/docs/pages/api/avatar.md
--- a/docs/pages/api/avatar.md
+++ b/docs/pages/api/avatar.md
@@ -25,7 +25,7 @@ You can learn more about the difference by [reading this guide](/guides/minimizi
 | Name | Type | Default | Description |
 |:-----|:-----|:--------|:------------|
 | <span class="prop-name">alt</span> | <span class="prop-type">string</span> |  | Used in combination with `src` or `srcSet` to provide an alt attribute for the rendered `img` element. |
-| <span class="prop-name">children</span> | <span class="prop-type">node</span> |  | Used to render icon or text elements inside the Avatar. `src` and `alt` props will not be used and no `img` will be rendered by default.<br>This can be an element, or just a string. |
+| <span class="prop-name">children</span> | <span class="prop-type">node</span> |  | Used to render icon or text elements inside the Avatar if `src` is not set. This can be an element, or just a string. |
 | <span class="prop-name">classes</span> | <span class="prop-type">object</span> |  | Override or extend the styles applied to the component. See [CSS API](#css) below for more details. |
 | <span class="prop-name">component</span> | <span class="prop-type">elementType</span> | <span class="prop-default">'div'</span> | The component used for the root node. Either a string to use a DOM element or a component. |
 | <span class="prop-name">imgProps</span> | <span class="prop-type">object</span> |  | Attributes applied to the `img` element if the component is used to display an image. |
diff --git a/packages/material-ui/src/Avatar/Avatar.js b/packages/material-ui/src/Avatar/Avatar.js
--- a/packages/material-ui/src/Avatar/Avatar.js
+++ b/packages/material-ui/src/Avatar/Avatar.js
@@ -50,23 +50,22 @@ const Avatar = React.forwardRef(function Avatar(props, ref) {
     ...other
   } = props;
 
-  let children = childrenProp;
+  let children = null;
   const img = src || srcSet;
 
   if (img) {
     children = (
-      <React.Fragment>
-        <img
-          alt={alt}
-          src={src}
-          srcSet={srcSet}
-          sizes={sizes}
-          className={classes.img}
-          {...imgProps}
-        />
-        {children}
-      </React.Fragment>
+      <img
+        alt={alt}
+        src={src}
+        srcSet={srcSet}
+        sizes={sizes}
+        className={classes.img}
+        {...imgProps}
+      />
     );
+  } else {
+    children = childrenProp;
   }
 
   return (
@@ -94,10 +93,7 @@ Avatar.propTypes = {
    */
   alt: PropTypes.string,
   /**
-   * Used to render icon or text elements inside the Avatar.
-   * `src` and `alt` props will not be used and no `img` will
-   * be rendered by default.
-   *
+   * Used to render icon or text elements inside the Avatar if `src` is not set.
    * This can be an element, or just a string.
    */
   children: PropTypes.node,


Test patch:
diff --git a/packages/material-ui/src/Avatar/Avatar.test.js b/packages/material-ui/src/Avatar/Avatar.test.js
--- a/packages/material-ui/src/Avatar/Avatar.test.js
+++ b/packages/material-ui/src/Avatar/Avatar.test.js
@@ -58,6 +58,20 @@ describe('<Avatar />', () => {
     });
   });
 
+  describe('image avatar with unrendered children', () => {
+    it('should render a div containing an img, not children', () => {
+      const wrapper = mount(<Avatar src="something.jpg">MB</Avatar>);
+      assert.strictEqual(wrapper.find('img').length, 1);
+      assert.strictEqual(wrapper.text(), '');
+    });
+
+    it('should be able to add more props to the image', () => {
+      const onError = () => {};
+      const wrapper = mount(<Avatar src="something.jpg" imgProps={{ onError }} />);
+      assert.strictEqual(wrapper.find('img').props().onError, onError);
+    });
+  });
+
   describe('font icon avatar', () => {
     let wrapper;