Skip to content

Commit

Permalink
Fix primefaces#3647: Image fallback if there is an error
Browse files Browse the repository at this point in the history
  • Loading branch information
melloware committed Nov 15, 2022
1 parent 4fc8068 commit d9383c2
Show file tree
Hide file tree
Showing 5 changed files with 90 additions and 55 deletions.
18 changes: 12 additions & 6 deletions api-generator/components/avatar.js
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,18 @@ const AvatarProps = [
default: 'null',
description: 'Defines the image to display.'
},
{
name: 'imageAlt',
type: 'any',
default: 'null',
description: 'It specifies an alternate text for an image, if the image cannot be displayed.'
},
{
name: 'imageFallback',
type: 'string',
default: 'default',
description: 'Defines a fallback image or URL if the main image fails to load. If "default" will fallback to label then icon.'
},
{
name: 'size',
type: 'string',
Expand All @@ -34,12 +46,6 @@ const AvatarProps = [
type: 'any',
default: 'null',
description: 'Template of the content.'
},
{
name: 'imageAlt',
type: 'any',
default: 'null',
description: 'It specifies an alternate text for an image, if the image cannot be displayed.'
}
];

Expand Down
64 changes: 36 additions & 28 deletions components/doc/avatar/index.js
Original file line number Diff line number Diff line change
@@ -1,9 +1,9 @@
import React, { memo } from 'react';
import Link from 'next/link';
import { TabView, TabPanel } from '../../lib/tabview/TabView';
import { useLiveEditorTabs } from '../common/liveeditor';
import React, { memo } from 'react';
import { TabPanel, TabView } from '../../lib/tabview/TabView';
import { CodeHighlight } from '../common/codehighlight';
import { DevelopmentSection } from '../common/developmentsection';
import { useLiveEditorTabs } from '../common/liveeditor';

const AvatarDoc = memo(() => {
const sources = {
Expand Down Expand Up @@ -102,10 +102,10 @@ export class AvatarDemo extends Component {
<div className="col-12 md:col-4">
<div className="card">
<h5>Image - Badge</h5>
<Avatar className="p-overlay-badge" image="demo/images/organization/walter.jpg" size="xlarge">
<Badge value="4" severity="danger" />
</Avatar>
<h5>Gravatar, Letter Avatar, Fallback</h5>
<Avatar id="gravatar" image=""https://www.gravatar.com/avatar/05dfd4b41340d09cae045235eb0893c3?d=mp" className="mr-2" size="xlarge" />
<Avatar id="letter-avatar" image="https://ui-avatars.com/api/?name=Optimus+Prime&color=ffffff&background=ff9900" className="mr-2" size="xlarge" />
<Avatar id="fallback" image="invalid.jpg" imageFallback="https://ui-avatars.com/api/?name=Fall+Back" className="mr-2" size="xlarge" />
</div>
</div>
</div>
Expand Down Expand Up @@ -209,11 +209,11 @@ export const AvatarDemo = () => {
<div className="col-12 md:col-4">
<div className="card">
<h5>Image - Badge</h5>
<Avatar className="p-overlay-badge" image="images/organization/walter.jpg" size="xlarge">
<Badge value="4" severity="danger" />
</Avatar>
</div>
<h5>Gravatar, Letter Avatar, Fallback</h5>
<Avatar id="gravatar" image=""https://www.gravatar.com/avatar/05dfd4b41340d09cae045235eb0893c3?d=mp" className="mr-2" size="xlarge" />
<Avatar id="letter-avatar" image="https://ui-avatars.com/api/?name=Optimus+Prime&color=ffffff&background=ff9900" className="mr-2" size="xlarge" />
<Avatar id="fallback" image="invalid.jpg" imageFallback="https://ui-avatars.com/api/?name=Fall+Back" className="mr-2" size="xlarge" />
</div>
</div>
</div>
</div>
Expand Down Expand Up @@ -315,11 +315,12 @@ export const AvatarDemo = () => {
<div className="col-12 md:col-4">
<div className="card">
<h5>Image - Badge</h5>
<Avatar className="p-overlay-badge" image="images/organization/walter.jpg" size="xlarge">
<Badge value="4" severity="danger" />
</Avatar>
</div>
<h5>Gravatar, Letter Avatar, Fallback</h5>
<Avatar id="gravatar" image=""https://www.gravatar.com/avatar/05dfd4b41340d09cae045235eb0893c3?d=mp" className="mr-2" size="xlarge" />
<Avatar id="letter-avatar" image="https://ui-avatars.com/api/?name=Optimus+Prime&color=ffffff&background=ff9900" className="mr-2" size="xlarge" />
<Avatar id="fallback-label" image="invalid1.jpg" label="P" className="mr-2" size="xlarge" style={{ backgroundColor: '#2196F3', color: '#ffffff' }} />
<Avatar id="fallback-url" image="invalid2.jpg" imageFallback="https://ui-avatars.com/api/?name=Fall+Back" className="mr-2" size="xlarge" />
</div>
</div>
</div>
</div>
Expand Down Expand Up @@ -426,11 +427,12 @@ const AvatarDemo = () => {
<div className="col-12 md:col-4">
<div className="card">
<h5>Image - Badge</h5>
<Avatar className="p-overlay-badge" image="images/organization/walter.jpg" size="xlarge">
<Badge value="4" severity="danger" />
</Avatar>
</div>
<h5>Gravatar, Letter Avatar, Fallback</h5>
<Avatar id="gravatar" image=""https://www.gravatar.com/avatar/05dfd4b41340d09cae045235eb0893c3?d=mp" className="mr-2" size="xlarge" />
<Avatar id="letter-avatar" image="https://ui-avatars.com/api/?name=Optimus+Prime&color=ffffff&background=ff9900" className="mr-2" size="xlarge" />
<Avatar id="fallback-label" image="invalid1.jpg" label="P" className="mr-2" size="xlarge" style={{ backgroundColor: '#2196F3', color: '#ffffff' }} />
<Avatar id="fallback-url" image="invalid2.jpg" imageFallback="https://ui-avatars.com/api/?name=Fall+Back" className="mr-2" size="xlarge" />
</div>
</div>
</div>
</div>
Expand Down Expand Up @@ -554,6 +556,18 @@ import { AvatarGroup } from 'primereact/avatargroup';
<td>null</td>
<td>Defines the image to display.</td>
</tr>
<tr>
<td>imageAlt</td>
<td>any</td>
<td>null</td>
<td>It specifies an alternate text for an image, if the image cannot be displayed.</td>
</tr>
<tr>
<td>imageFallback</td>
<td>string</td>
<td>default</td>
<td>Defines a fallback image or URL if the main image fails to load. If "default" will fallback to label then icon.</td>
</tr>
<tr>
<td>size</td>
<td>string</td>
Expand All @@ -572,12 +586,6 @@ import { AvatarGroup } from 'primereact/avatargroup';
<td>null</td>
<td>Template of the content.</td>
</tr>
<tr>
<td>imageAlt</td>
<td>any</td>
<td>null</td>
<td>It specifies an alternate text for an image, if the image cannot be displayed.</td>
</tr>
</tbody>
</table>
</div>
Expand Down
35 changes: 26 additions & 9 deletions components/lib/avatar/Avatar.js
Original file line number Diff line number Diff line change
Expand Up @@ -3,10 +3,11 @@ import { classNames, IconUtils, ObjectUtils } from '../utils/Utils';

export const Avatar = React.forwardRef((props, ref) => {
const elementRef = React.useRef(null);
const [imageFailed, setImageFailed] = React.useState(false);

const createContent = () => {
if (props.image) {
return <img src={props.image} alt={props.imageAlt} onError={props.onImageError}></img>;
if (props.image && !imageFailed) {
return <img src={props.image} alt={props.imageAlt} onError={onImageError}></img>;
} else if (props.label) {
return <span className="p-avatar-text">{props.label}</span>;
} else if (props.icon) {
Expand All @@ -16,6 +17,21 @@ export const Avatar = React.forwardRef((props, ref) => {
return null;
};

const onImageError = (event) => {
if (props.imageFallback === 'default') {
if (!props.onImageError) {
// fallback to label or icon
setImageFailed(true);
event.target.src = null;
}
} else {
// try fallback as an image
event.target.src = props.imageFallback;
}

props.onImageError && props.onImageError(event);
};

React.useImperativeHandle(ref, () => ({
props,
getElement: () => elementRef.current
Expand All @@ -25,7 +41,7 @@ export const Avatar = React.forwardRef((props, ref) => {
const containerClassName = classNames(
'p-avatar p-component',
{
'p-avatar-image': props.image != null,
'p-avatar-image': props.image !== null && !imageFailed,
'p-avatar-circle': props.shape === 'circle',
'p-avatar-lg': props.size === 'large',
'p-avatar-xl': props.size === 'xlarge',
Expand All @@ -47,14 +63,15 @@ export const Avatar = React.forwardRef((props, ref) => {
Avatar.displayName = 'Avatar';
Avatar.defaultProps = {
__TYPE: 'Avatar',
label: null,
className: null,
icon: null,
image: null,
size: 'normal',
imageAlt: 'avatar',
imageFallback: 'default',
label: null,
onImageError: null,
shape: 'square',
size: 'normal',
style: null,
className: null,
template: null,
imageAlt: 'avatar',
onImageError: null
template: null
};
9 changes: 5 additions & 4 deletions components/lib/avatar/avatar.d.ts
Original file line number Diff line number Diff line change
Expand Up @@ -8,16 +8,17 @@ type AvatarShapeType = 'square' | 'circle';
type AvatarTemplateType = React.ReactNode | ((props: AvatarProps) => React.ReactNode);

export interface AvatarProps extends Omit<React.DetailedHTMLProps<React.HTMLAttributes<HTMLDivElement>, HTMLDivElement>, 'ref'> {
label?: string;
children?: React.ReactNode;
icon?: IconType<AvatarProps>;
image?: string;
size?: AvatarSizeType;
imageAlt?: string;
imageFallback?: string;
label?: string;
shape?: AvatarShapeType;
size?: AvatarSizeType;
template?: AvatarTemplateType;
imageAlt?: string;
onImageError?(event: React.SyntheticEvent): void;
onClick?(event: React.MouseEvent<HTMLElement>): void;
children?: React.ReactNode;
}

export declare class Avatar extends React.Component<AvatarProps, any> {
Expand Down
19 changes: 11 additions & 8 deletions pages/avatar/index.js
Original file line number Diff line number Diff line change
@@ -1,11 +1,11 @@
import getConfig from 'next/config';
import Head from 'next/head';
import React from 'react';
import AvatarDoc from '../../components/doc/avatar';
import { DocActions } from '../../components/doc/common/docactions';
import { Avatar } from '../../components/lib/avatar/Avatar';
import { AvatarGroup } from '../../components/lib/avatargroup/AvatarGroup';
import { Badge } from '../../components/lib/badge/Badge';
import AvatarDoc from '../../components/doc/avatar';
import { DocActions } from '../../components/doc/common/docactions';
import Head from 'next/head';
import getConfig from 'next/config';

const AvatarDemo = () => {
const contextPath = getConfig().publicRuntimeConfig.contextPath;
Expand Down Expand Up @@ -109,10 +109,13 @@ const AvatarDemo = () => {

<div className="col-12 md:col-4">
<div className="card">
<h5>Image - Badge</h5>
<Avatar className="p-overlay-badge" image={`${contextPath}/images/organization/walter.jpg`} size="xlarge">
<Badge value="4" severity="danger" />
</Avatar>
<h5>Gravatar, Letter Avatar, Fallback</h5>
<div className="flex align-content-center">
<Avatar id="gravatar" image={`https://www.gravatar.com/avatar/05dfd4b41340d09cae045235eb0893c3?d=mp`} className="flex align-items-center justify-content-center mr-2" size="xlarge" />
<Avatar id="letter-avatar" image={`https://ui-avatars.com/api/?name=Optimus+Prime&color=ffffff&background=ff9900`} className="flex align-items-center justify-content-center mr-2" size="xlarge" />
<Avatar id="fallback-label" image={`invalid1.jpg`} label="P" className="flex align-items-center justify-content-center mr-2" size="xlarge" style={{ backgroundColor: '#2196F3', color: '#ffffff' }} />
<Avatar id="fallback-url" image={`invalid2.jpg`} imageFallback={`https://ui-avatars.com/api/?name=Fall+Back`} className="flex align-items-center justify-content-center mr-2" size="xlarge" />
</div>
</div>
</div>
</div>
Expand Down

0 comments on commit d9383c2

Please sign in to comment.