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

Commit

Permalink
[terra-avatar] Changed fallback to initials from user icon for avatar (
Browse files Browse the repository at this point in the history
…#2621)

* Changed fallback to initials for avatar

* changes of Code review comment

* Made Initials as Required Prop

* added role to initials so that screen reader reads the both intials and alt value

* screenshot update

* Update packages/terra-avatar/docs/avatar.md

Co-Authored-By: Matt Henkes <mjhenkes@gmail.com>

* removed obselete snapshots

* Removing Tests which were not required with new changes

* Update CONTRIBUTORS.md

* Fix tests

Co-authored-by: Matt Henkes <mjhenkes@gmail.com>
Co-authored-by: Ryan Manuel <rfmanuel@gmail.com>
Co-authored-by: Jeremy Fuksa <hello@orangefla.me>
Co-authored-by: Manuel,Ryan <Ryan.Manuel@Cerner.com>
  • Loading branch information
5 people authored Mar 28, 2020
1 parent ed26635 commit ef3c65e
Show file tree
Hide file tree
Showing 31 changed files with 134 additions and 88 deletions.
1 change: 1 addition & 0 deletions packages/terra-avatar/CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ 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.
* To Make all User avatars available in same variant `generic` subcomponent has been added.
* `generic` subcomponent replaces the `sharedUser` subcomponent.
* `generic` subcomponent contains a `variant` prop that toggles between `single-user`, `shared-user`, and `provider` user avatar.
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) => {
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 @@ -14,7 +14,7 @@ import AvatarPropsTable from 'terra-avatar/src/variants/Avatar?dev-site-props-ta

# 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 a 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
Original file line number Diff line number Diff line change
Expand Up @@ -8,14 +8,17 @@ import { Badge } from 'terra-avatar/package.json?dev-site-package';
### Reason for upgrade
* To group all user avatars in single variant `shared-user` has been replaced with `generic` sub-component
* To keep adding new user avatars like `provider` easier new `generic` sub-component has been added with all user avatars as variants.
* Providing more standard fallbacks

### Removed
* `Shared User` subcomponent From Avatar
* Default `user` icon as fallback icon for Avatar

### Added
* The `generic` subcomponent that replaces the `sharedUser` subcomponent with a new `variant` prop that can be `single-user`, `shared-user`, or `provider`.
* `variant` prop will take values for sub-variants `single-user`, `shared-user` and `provider`.
* `user` avatar has been moved into `generic` sub-comopnent with varaint name `single-user`.
* the `initials` as fallback display for Avatar when image fails to load.

### Steps to uplift to V3
1. Use a named export for Generic variant.
Expand Down
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

0 comments on commit ef3c65e

Please sign in to comment.