Skip to content

Commit

Permalink
Merge pull request #1079 from CPatchane/fallback_AppIcon
Browse files Browse the repository at this point in the history
Handle fallback icon in AppIcon
  • Loading branch information
CPatchane authored Jul 1, 2019
2 parents 68d3d22 + 9a8bf05 commit e3fb76b
Show file tree
Hide file tree
Showing 4 changed files with 92 additions and 2 deletions.
20 changes: 20 additions & 0 deletions react/AppIcon/Readme.md
Original file line number Diff line number Diff line change
Expand Up @@ -54,3 +54,23 @@ initialState = { fetchIcon: fetchIcon1 };

## `domain` and `secure` props
If the `fetchIcon` is missing, the `<AppIcon />` component needs the `domain` prop (litteraly the cozy domain) and the secure prop, to know which protocol use between `http` or `https`.

### Provide `fallbackIcon`

You can provide an `<Icon />` `icon` props to fallback when the AppIcon fetched is broken or the `fetchIcon` function errored.

```jsx
const fetchIcon = () => 'https://placeholder.pics/svg/100/7DC4FF/Test%20Icon'
const fetchIconBroken = () => 'blahblahblah'

const brokeFetchIcon = () => setState({ fetchIcon: fetchIconBroken })

initialState = { fetchIcon };

<div>
<button onClick={brokeFetchIcon}>Break it</button>
<div style={{ width: '100px' }}>
<AppIcon fetchIcon={state.fetchIcon} fallbackIcon='warning' />
</div>
</div>
```
14 changes: 12 additions & 2 deletions react/AppIcon/index.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ import styles from './styles.styl'

import Icon from '../Icon'
import palette from '../palette'
import { iconPropType } from '../Icon'

import { getPreloaded, preload } from './Preloader'
import { AppDoctype } from '../proptypes'
Expand All @@ -24,6 +25,7 @@ export class AppIcon extends Component {
status: preloaded ? DONE : FETCHING
}
this.isUnmounting = false
this.handleError = this.handleError.bind(this)
}

componentWillUnmount() {
Expand Down Expand Up @@ -62,8 +64,13 @@ export class AppIcon extends Component {
}
}

handleError() {
this.setState({ status: ERRORED })
}

render() {
const { alt, className } = this.props
const { alt, className, fallbackIcon } = this.props

const { icon, status } = this.state
switch (status) {
case FETCHING:
Expand All @@ -82,6 +89,7 @@ export class AppIcon extends Component {
alt={alt}
className={cx(styles['c-app-icon'], className)}
src={icon}
onError={this.handleError}
/>
)
case ERRORED:
Expand All @@ -94,7 +102,7 @@ export class AppIcon extends Component {
className
)}
height="100%"
icon="cube"
icon={fallbackIcon || 'cube'}
width="100%"
color={palette['coolGrey']}
/>
Expand All @@ -107,6 +115,8 @@ AppIcon.propTypes = {
alt: PropTypes.string,
/** Required if fetchIcon is not provided */
app: PropTypes.oneOfType([AppDoctype, PropTypes.string]),
/** Icon to fallback on error (optional), default cube icon */
fallbackIcon: iconPropType,
/** Custom implementation of how to fetch icon */
fetchIcon: PropTypes.func,
className: PropTypes.string,
Expand Down
35 changes: 35 additions & 0 deletions react/AppIcon/test/AppIcon.spec.js
Original file line number Diff line number Diff line change
Expand Up @@ -76,6 +76,41 @@ describe('AppIcon component', () => {
)
})

it(`renders provided fallbackIcon when fetch error occurs`, async done => {
const wrapper = shallow(
<AppIcon
app={app}
fetchIcon={failureFetchIcon}
onReady={() => {
wrapper.update()
const component = wrapper.getElement()
expect(component).toMatchSnapshot()
expect(failureFetchIcon).toHaveBeenCalledWith(app, undefined, secure)
expect(console.error).toHaveBeenCalledTimes(0)
done()
}}
secure={secure}
fallbackIcon="warning"
/>
)
})

it(`renders provided fallbackIcon on img error`, async done => {
const wrapper = shallow(
<AppIcon
fetchIcon={() => 'notagoodurl'}
onReady={() => {
wrapper.simulate('error')
wrapper.update()
const component = wrapper.getElement()
expect(component).toMatchSnapshot()
done()
}}
fallbackIcon="warning"
/>
)
})

it(`uses Preloader.preload when no fetchIcon method is provided`, async done => {
jest.mock('../Preloader')
const AppIcon = require('../').default
Expand Down
25 changes: 25 additions & 0 deletions react/AppIcon/test/__snapshots__/AppIcon.spec.js.snap
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@ exports[`AppIcon component renders as loading 1`] = `
exports[`AppIcon component renders correctly 1`] = `
<img
className="styles__c-app-icon___2_O40"
onError={[Function]}
src="http://cozy.tools/apps/test/icon"
/>
`;
Expand All @@ -27,13 +28,37 @@ exports[`AppIcon component renders default when fetch error occurs 1`] = `
exports[`AppIcon component renders immediately when icon is alredy preloaded 1`] = `
<img
className="styles__c-app-icon___2_O40"
onError={[Function]}
src="http://cozy.tools/apps/test/icon"
/>
`;

exports[`AppIcon component renders provided fallbackIcon on img error 1`] = `
<Icon
className="styles__c-app-icon___2_O40 styles__c-app-icon-default___3CEmt"
color="var(--coolGrey)"
height="100%"
icon="warning"
spin={false}
width="100%"
/>
`;

exports[`AppIcon component renders provided fallbackIcon when fetch error occurs 1`] = `
<Icon
className="styles__c-app-icon___2_O40 styles__c-app-icon-default___3CEmt"
color="var(--coolGrey)"
height="100%"
icon="warning"
spin={false}
width="100%"
/>
`;

exports[`AppIcon component uses Preloader.preload when no fetchIcon method is provided 1`] = `
<img
className="styles__c-app-icon___2_O40"
onError={[Function]}
src="http://cozy.tools/apps/test/icon"
/>
`;

0 comments on commit e3fb76b

Please sign in to comment.