Skip to content
This repository has been archived by the owner on May 24, 2024. It is now read-only.

[terra-avatar] Changed fallback to initials from user icon for avatar #2621

Merged
merged 21 commits into from
Mar 28, 2020
Merged
Show file tree
Hide file tree
Changes from 15 commits
Commits
Show all changes
21 commits
Select commit Hold shift + click to select a range
9b6a5d4
Changed fallback to initials for avatar
supreethmr Sep 3, 2019
9382491
Merge branch 'master' into Added_Initial_fallback
supreethmr Sep 5, 2019
3e9cb4c
Merge branch 'master' into Added_Initial_fallback
supreethmr Sep 9, 2019
565f141
changes of Code review comment
supreethmr Sep 10, 2019
706b591
Made Initials as Required Prop
supreethmr Sep 16, 2019
84c6038
Merge branch 'master' into Added_Initial_fallback
supreethmr Sep 19, 2019
32352b5
Merge branch 'master' into Added_Initial_fallback
supreethmr Sep 20, 2019
9a25680
added role to initials so that screen reader reads the both intials a…
supreethmr Sep 26, 2019
197e280
Merge branch 'master' into Added_Initial_fallback
supreethmr Sep 26, 2019
919d7f4
Merge branch 'master' into Added_Initial_fallback
supreethmr Sep 27, 2019
4698f86
screenshot update
supreethmr Sep 27, 2019
64b7c72
Update packages/terra-avatar/docs/avatar.md
supreethmr Oct 15, 2019
e7b8744
removed obselete snapshots
supreethmr Oct 16, 2019
ce1f8fe
Removing Tests which were not required with new changes
supreethmr Oct 16, 2019
d8098a5
Merge branch 'master' into Added_Initial_fallback
ryanthemanuel Oct 28, 2019
87fde47
Merge branch 'master' into Added_Initial_fallback
supreethmr Nov 4, 2019
9c50d3a
Update CONTRIBUTORS.md
jeremyfuksa Nov 4, 2019
05df907
Merge remote-tracking branch 'origin/master' into Added_Initial_fallback
rm012685 Mar 27, 2020
21fdee0
Merge branch 'master' into Added_Initial_fallback
ryanthemanuel Mar 27, 2020
be42bf4
Merge branch 'master' into Added_Initial_fallback
ryanthemanuel Mar 28, 2020
900a3fd
Fix tests
rm012685 Mar 28, 2020
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 3 additions & 0 deletions packages/terra-avatar/CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,9 @@ ChangeLog

Unreleased
----------
### Breaking Changes
* Changed the fallback behavior of Avatar. Such that if the image fails to load, the avatar falls back to the initials display instead of default user icon.

### Fixed
* Fixed lint warnings for multiple empty lines

Expand Down
2 changes: 1 addition & 1 deletion packages/terra-avatar/docs/avatar.md
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
# Terra Avatar

The `Avatar` variant represents a person - it displays an image or initials in a circular frame. If neither are provided, a fallback user icon displays. This is the default export of the `terra-avatar` package.
The `Avatar` variant represents a person - it displays an image or initials in a circular frame. If valid image is not provided then the avatar falls back to displaying initials. This is the default export of the `terra-avatar` package.

## Getting Started

Expand Down
8 changes: 8 additions & 0 deletions packages/terra-avatar/docs/upgrade-guide.md
Original file line number Diff line number Diff line change
@@ -1,4 +1,12 @@
# terra-avatar Upgrade Guide
## Changes from version 2 to version 3

### Removed
* Default `user` icon as fallback icon for Avatar

### Added
* the `initials` as fallback display for Avatar when image fails to load.

## Changes from version 1 to version 2

### Props
Expand Down
25 changes: 21 additions & 4 deletions packages/terra-avatar/src/common/AvatarUtils.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -60,17 +60,33 @@ const getColorVariant = (hashValue) => {
/**
* Render placeholder.
*/
const generateImagePlaceholder = (alt, isAriaHidden, variant) => {
const generateImagePlaceholder = (avatarParams) => {
const {
alt, variant, isAriaHidden,
} = avatarParams;
const avatarIconClassNames = cx(['icon', variant]);

return <span className={avatarIconClassNames} role="img" aria-label={alt} alt={alt} aria-hidden={isAriaHidden} />;
};

/**
* Render placeholder.
*/
const generateInitials = (avatarParams) => {
const {
alt, initials, isAriaHidden,
} = avatarParams;
const avatarTextClassNames = cx('initials');
return <span className={avatarTextClassNames} role="img" alt={alt} aria-label={alt} aria-hidden={isAriaHidden}>{initials.toUpperCase()}</span>;
};

/**
* Render image with placeholder.
*/
const generateImage = (image, alt, isAriaHidden, variant, handleFallback) => {
const icon = generateImagePlaceholder(alt, isAriaHidden, variant);
const generateImage = (avatarParams) => {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

same here

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

updated here

const {
alt, image, variant, handleFallback,
} = avatarParams;
const icon = (variant === AVATAR_VARIANTS.USER) ? generateInitials(avatarParams) : generateImagePlaceholder(avatarParams);
return <TerraImage className={cx('image')} src={image} placeholder={icon} alt={alt} onError={handleFallback} fit="cover" />;
};

Expand Down Expand Up @@ -110,6 +126,7 @@ export {
getColorVariant,
generateImagePlaceholder,
generateImage,
generateInitials,
validateColor,
setColor,
};
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@ const propTypes = { color: PropTypes.string };
const AvatarColorVariants = ({
...props
}) => (
<Avatar alt="User" color={props.color} />
<Avatar alt="Joe Shane" initials="JS" color={props.color} />
);

AvatarColorVariants.propTypes = propTypes;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@ import Avatar from 'terra-avatar';
import exampleAvatarImage from '../../../assets/150x150.jpg';

const AvatarImage = () => (
<Avatar image={exampleAvatarImage} alt="Deep Space" />
<Avatar image={exampleAvatarImage} initials="JS" alt="Deep Space" />
);

export default AvatarImage;
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@ import React from 'react';
import Avatar from 'terra-avatar';

const AvatarImage = () => (
<Avatar alt="Patient #1" isDeceased />
<Avatar alt="Patient #1" isDeceased initials="JS" />
);

export default AvatarImage;
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@ import Avatar from 'terra-avatar';
import exampleAvatarImage from '../../../assets/150x150.jpg';

const AvatarSize = () => (
<Avatar alt="User" image={exampleAvatarImage} size="2em" />
<Avatar alt="User" image={exampleAvatarImage} size="2em" initials="JS" />
);

export default AvatarSize;
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@ import React from 'react';
import Avatar from 'terra-avatar';

const AvatarUser = () => (
<Avatar alt="The Last Airbender" />
<Avatar alt="The Last Airbender" initials="JS" />
);

export default AvatarUser;
Original file line number Diff line number Diff line change
Expand Up @@ -2,4 +2,4 @@ import React from 'react';
import Avatar from '../../../../index';
import exampleAvatarImage from '../../../assets/150x150.jpg';

export default () => <Avatar image={exampleAvatarImage} alt="User" id="image-avatar" />;
export default () => <Avatar image={exampleAvatarImage} alt="User" id="image-avatar" initials="JS" />;
Original file line number Diff line number Diff line change
Expand Up @@ -2,4 +2,4 @@ import React from 'react';
import Avatar from '../../../../index';
import exampleAvatarImage from '../../../assets/200x133.jpg';

export default () => <Avatar image={exampleAvatarImage} alt="User" id="image-avatar" />;
export default () => <Avatar image={exampleAvatarImage} alt="User" id="image-avatar" initials="JS" />;
Original file line number Diff line number Diff line change
Expand Up @@ -2,4 +2,4 @@ import React from 'react';
import Avatar from '../../../../index';
import exampleAvatarImage from '../../../assets/170x251.jpg';

export default () => <Avatar image={exampleAvatarImage} alt="User" id="image-avatar" />;
export default () => <Avatar image={exampleAvatarImage} alt="User" id="image-avatar" initials="JS" />;
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@ const cx = classNames.bind(styles);
export default () => (
<div>
<div className={cx('avatar-wrapper')}>
<Avatar id="image-avatar" image={exampleAvatarImage} alt="placeholder" />
<Avatar id="image-avatar" image={exampleAvatarImage} alt="placeholder" initials="JS" />
</div>
<div className={cx('avatar-wrapper')}>
<Facility id="facility-avatar" alt="facility" />
Expand Down

This file was deleted.

Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
import React from 'react';
import Avatar from '../../../../index';

export default () => <Avatar image="invalid-image-url" initials="JD" alt="John Doe" id="invalid-image-avatar" />;
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import React from 'react';
import Avatar from '../../../../index';

export default () => <Avatar id="is-deceased-avatar" alt="user" isDeceased />;
export default () => <Avatar id="is-deceased-avatar" alt="user" isDeceased initials="JS" />;
Original file line number Diff line number Diff line change
Expand Up @@ -2,4 +2,4 @@ import React from 'react';
import Avatar from '../../../../index';
import exampleAvatarImage from '../../../assets/150x150.jpg';

export default () => <Avatar id="is-deceased-image-avatar" alt="user" image={exampleAvatarImage} isDeceased />;
export default () => <Avatar id="is-deceased-image-avatar" alt="user" image={exampleAvatarImage} isDeceased initials="JS" />;
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import React from 'react';
import Avatar from '../../../../index';

export default () => <Avatar id="one-initial-avatar" initials="J" alt="user" />;
export default () => <Avatar id="one-initial-avatar" initials="J" alt="John" />;
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import React from 'react';
import Avatar from '../../../../index';

export default () => <Avatar id="two-initials-avatar" initials="JS" alt="user" color="two" />;
export default () => <Avatar id="two-initials-avatar" initials="JSS" alt="Joe S Shane" color="two" />;

This file was deleted.

20 changes: 12 additions & 8 deletions packages/terra-avatar/src/variants/Avatar.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@ import PropTypes from 'prop-types';
import classNames from 'classnames/bind';
import styles from '../common/Avatar.module.scss';
import {
AVATAR_VARIANTS, generateImagePlaceholder, generateImage, setColor,
AVATAR_VARIANTS, generateInitials, generateImage, setColor,
} from '../common/AvatarUtils';

const cx = classNames.bind(styles);
Expand All @@ -30,7 +30,7 @@ const propTypes = {
/**
* One or two letters to display.
*/
initials: PropTypes.string,
initials: PropTypes.string.isRequired,
/**
* Whether to hide avatar from the accessibility tree.
*/
Expand All @@ -49,7 +49,6 @@ const defaultProps = {
color: 'auto',
hashValue: undefined,
image: undefined,
initials: undefined,
isAriaHidden: false,
isDeceased: false,
size: undefined,
Expand Down Expand Up @@ -84,14 +83,19 @@ class Avatar extends React.Component {
} = this.props;

let avatarContent;
const avatarParams = {
image,
alt,
isAriaHidden,
variant: AVATAR_VARIANTS.USER,
handleFallback: this.handleFallback,
initials: (initials.length > 2) ? initials.slice(0, 2) : initials,
};

if (image) {
avatarContent = generateImage(image, alt, isAriaHidden, AVATAR_VARIANTS.USER, this.handleFallback);
} else if (initials && (initials.length === 1 || initials.length === 2)) {
const avatarTextClassNames = cx('initials');
avatarContent = <span className={avatarTextClassNames} alt={alt} aria-label={alt} aria-hidden={isAriaHidden}>{initials.toUpperCase()}</span>;
avatarContent = generateImage(avatarParams);
} else {
avatarContent = generateImagePlaceholder(alt, isAriaHidden, AVATAR_VARIANTS.USER);
avatarContent = generateInitials(avatarParams);
}

const attributes = { ...customProps };
Expand Down
12 changes: 10 additions & 2 deletions packages/terra-avatar/src/variants/Facility.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -73,10 +73,18 @@ class Facility extends React.Component {

let facilityContent;

const facilityParams = {
image,
alt,
isAriaHidden,
variant: AVATAR_VARIANTS.FACILITY,
handleFallback: this.handleFallback,
};

if (image) {
facilityContent = generateImage(image, alt, isAriaHidden, AVATAR_VARIANTS.FACILITY, this.handleFallback);
facilityContent = generateImage(facilityParams);
} else {
facilityContent = generateImagePlaceholder(alt, isAriaHidden, AVATAR_VARIANTS.FACILITY);
facilityContent = generateImagePlaceholder(facilityParams);
}
const attributes = { ...customProps };
const customStyles = size ? ({ fontSize: size, ...attributes.style }) : attributes.style;
Expand Down
22 changes: 11 additions & 11 deletions packages/terra-avatar/tests/jest/Avatar.test.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@ import exampleProfilePhoto from '../../src/terra-dev-site/assets/150x150.jpg';
describe('Avatar', () => {
// Snapshot Tests
it('should render a default avatar', () => {
const avatar = <Avatar alt="user" />;
const avatar = <Avatar initials="JS" alt="user" />;
const wrapper = render(avatar);
expect(wrapper).toMatchSnapshot();
});
Expand All @@ -22,26 +22,26 @@ describe('Avatar', () => {
expect(wrapper).toMatchSnapshot();
});

it('should render fallback user avatar when more than two initials are passed in', () => {
it('should render two initials avatar when more than two initials are passed in', () => {
const avatar = <Avatar initials="JJJ" alt="user" />;
const wrapper = render(avatar);
expect(wrapper).toMatchSnapshot();
});

it('should render image avatar when image is passed in', () => {
const avatar = <Avatar image={exampleProfilePhoto} alt="placeholder" />;
const avatar = <Avatar image={exampleProfilePhoto} alt="placeholder" initials="JS" />;
const wrapper = render(avatar);
expect(wrapper).toMatchSnapshot();
});

it('should render fallback user avatar when invalid image is passed in', () => {
const avatar = <Avatar image="https://path/to/invalid_image.jpg" alt="placeholder" />;
it('should render fallback initials when invalid image is passed in with initials', () => {
const avatar = <Avatar image="https://path/to/invalid_image.jpg" initials="JD" alt="placeholder" />;
const wrapper = render(avatar);
expect(wrapper).toMatchSnapshot();
});

it('should render an avatar with provided size', () => {
const avatar = <Avatar alt="user" size="5em" />;
const avatar = <Avatar alt="user" size="5em" initials="JS" />;
const wrapper = render(avatar);
expect(wrapper).toMatchSnapshot();
});
Expand All @@ -53,31 +53,31 @@ describe('Avatar', () => {
});

it('should render an avatar with color variant one', () => {
const avatar = <Avatar alt="user" color="one" />;
const avatar = <Avatar alt="user" color="one" initials="JS" />;
const wrapper = render(avatar);
expect(wrapper).toMatchSnapshot();
});

it('should render an avatar with color variant netural', () => {
const avatar = <Avatar alt="user" color="neutral" />;
const avatar = <Avatar alt="user" color="neutral" initials="JS" />;
const wrapper = render(avatar);
expect(wrapper).toMatchSnapshot();
});

it('should render an avatar with an automated color variant, based on a hashValue', () => {
const avatar = <Avatar alt="user" hashValue="alternative hash" />;
const avatar = <Avatar alt="user" hashValue="alternative hash" initials="JS" />;
const wrapper = render(avatar);
expect(wrapper).toMatchSnapshot();
});

it('should render an isDeceased avatar', () => {
const avatar = <Avatar alt="user" isDeceased />;
const avatar = <Avatar alt="user" isDeceased initials="JS" />;
const wrapper = render(avatar);
expect(wrapper).toMatchSnapshot();
});

it('should render an avatar with custom props', () => {
const avatar = <Avatar alt="user" id="custom props avatar" />;
const avatar = <Avatar alt="user" id="custom props avatar" initials="JS" />;
const wrapper = render(avatar);
expect(wrapper).toMatchSnapshot();
});
Expand Down
Loading