From 1c47cc33c4a34cc25d4e5378016ae2707990796d Mon Sep 17 00:00:00 2001 From: Yannick Lohse <contact@yannick-lohse.fr> Date: Tue, 19 Nov 2019 17:31:50 +0100 Subject: [PATCH] feat: AppIcon doesn't require secure and domain props anymore BREAKING CHANGE: From now on the AppIcon component needs to be inside a cozy-client context --- react/AppIcon/Readme.md | 3 -- react/AppIcon/index.jsx | 42 ++++++++++++------- react/AppIcon/test/AppIcon.spec.js | 36 ++++++++-------- .../__snapshots__/SmallAppItem.spec.jsx.snap | 14 +++---- 4 files changed, 53 insertions(+), 42 deletions(-) diff --git a/react/AppIcon/Readme.md b/react/AppIcon/Readme.md index 906500a368..1c8e8a4d58 100644 --- a/react/AppIcon/Readme.md +++ b/react/AppIcon/Readme.md @@ -53,9 +53,6 @@ initialState = { fetchIcon: fetchIcon1 }; </div> ``` -## `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. diff --git a/react/AppIcon/index.jsx b/react/AppIcon/index.jsx index 5a45194d38..e2370e113f 100644 --- a/react/AppIcon/index.jsx +++ b/react/AppIcon/index.jsx @@ -1,11 +1,11 @@ import React, { Component } from 'react' import PropTypes from 'prop-types' import cx from 'classnames' +import { withClient } from 'cozy-client' import styles from './styles.styl' -import Icon from '../Icon' +import Icon, { iconPropType } from '../Icon' import palette from '../palette' -import { iconPropType } from '../Icon' import { getPreloaded, preload } from './Preloader' import { AppDoctype } from '../proptypes' @@ -17,15 +17,28 @@ const FETCHING = 'fetching' export class AppIcon extends Component { constructor(props, context) { super(props, context) - const { app, domain, secure } = props - const preloaded = getPreloaded(app, domain, secure) - this.state = { - error: null, - icon: preloaded, - status: preloaded ? DONE : FETCHING - } this.isUnmounting = false this.handleError = this.handleError.bind(this) + + try { + const { app, client } = props + const cozyURL = new URL(client.getStackClient().uri) + // TODO: instead of storung domain and secure, pass down the client to the loaders (see https://github.com/cozy/cozy-ui/pull/1243#discussion_r348016827) + this.domain = cozyURL.host + this.secure = cozyURL.protocol === 'https:' + + const preloaded = getPreloaded(app, this.domain, this.secure) + this.state = { + error: null, + icon: preloaded, + status: preloaded ? DONE : FETCHING + } + } catch (error) { + this.state = { + error, + status: ERRORED + } + } } componentWillUnmount() { @@ -43,12 +56,12 @@ export class AppIcon extends Component { } async load() { - const { app, domain, fetchIcon, onReady, secure } = this.props + const { app, fetchIcon, onReady } = this.props const loadFn = fetchIcon || preload let loadedUrl let loadError try { - loadedUrl = await loadFn(app, domain, secure) + loadedUrl = await loadFn(app, this.domain, this.secure) } catch (error) { loadError = error } @@ -119,10 +132,9 @@ AppIcon.propTypes = { fallbackIcon: iconPropType, /** Custom implementation of how to fetch icon */ fetchIcon: PropTypes.func, + client: PropTypes.object.isRequired, className: PropTypes.string, - domain: PropTypes.string, - onReady: PropTypes.func, - secure: PropTypes.bool + onReady: PropTypes.func } -export default AppIcon +export default withClient(AppIcon) diff --git a/react/AppIcon/test/AppIcon.spec.js b/react/AppIcon/test/AppIcon.spec.js index b0b4b2eadb..311a58e715 100644 --- a/react/AppIcon/test/AppIcon.spec.js +++ b/react/AppIcon/test/AppIcon.spec.js @@ -4,13 +4,17 @@ import React from 'react' import { shallow } from 'enzyme' -import AppIcon from '../' +import { AppIcon } from '../' describe('AppIcon component', () => { const app = {} const domain = 'cozy.tools' const secure = true + const mockClient = { + getStackClient: () => ({ uri: `https://${domain}` }) + } + let successFetchIcon let failureFetchIcon @@ -31,12 +35,12 @@ describe('AppIcon component', () => { it(`renders as loading`, () => { const wrapper = shallow( - <AppIcon app={app} fetchIcon={successFetchIcon} secure={secure} /> + <AppIcon app={app} fetchIcon={successFetchIcon} client={mockClient} /> ) const component = wrapper.getElement() expect(component).toMatchSnapshot() - expect(successFetchIcon).toHaveBeenCalledWith(app, undefined, secure) + expect(successFetchIcon).toHaveBeenCalledWith(app, domain, secure) expect(console.error).toHaveBeenCalledTimes(0) }) @@ -45,15 +49,15 @@ describe('AppIcon component', () => { <AppIcon app={app} fetchIcon={successFetchIcon} + client={mockClient} onReady={() => { wrapper.update() const component = wrapper.getElement() expect(component).toMatchSnapshot() - expect(successFetchIcon).toHaveBeenCalledWith(app, undefined, secure) + expect(successFetchIcon).toHaveBeenCalledWith(app, domain, secure) expect(console.error).toHaveBeenCalledTimes(0) done() }} - secure={secure} /> ) }) @@ -63,15 +67,15 @@ describe('AppIcon component', () => { <AppIcon app={app} fetchIcon={failureFetchIcon} + client={mockClient} onReady={() => { wrapper.update() const component = wrapper.getElement() expect(component).toMatchSnapshot() - expect(failureFetchIcon).toHaveBeenCalledWith(app, undefined, secure) + expect(failureFetchIcon).toHaveBeenCalledWith(app, domain, secure) expect(console.error).toHaveBeenCalledTimes(0) done() }} - secure={secure} /> ) }) @@ -81,15 +85,15 @@ describe('AppIcon component', () => { <AppIcon app={app} fetchIcon={failureFetchIcon} + client={mockClient} onReady={() => { wrapper.update() const component = wrapper.getElement() expect(component).toMatchSnapshot() - expect(failureFetchIcon).toHaveBeenCalledWith(app, undefined, secure) + expect(failureFetchIcon).toHaveBeenCalledWith(app, domain, secure) expect(console.error).toHaveBeenCalledTimes(0) done() }} - secure={secure} fallbackIcon="warning" /> ) @@ -113,36 +117,35 @@ describe('AppIcon component', () => { it(`uses Preloader.preload when no fetchIcon method is provided`, async done => { jest.mock('../Preloader') - const AppIcon = require('../').default + const AppIcon = require('../').AppIcon const Preloader = require('../Preloader') const wrapper = shallow( <AppIcon app={app} + client={mockClient} onReady={() => { wrapper.update() const component = wrapper.getElement() expect(component).toMatchSnapshot() - expect(Preloader.preload).toHaveBeenCalledWith(app, undefined, secure) + expect(Preloader.preload).toHaveBeenCalledWith(app, domain, secure) expect(console.error).toHaveBeenCalledTimes(0) done() }} - secure={secure} /> ) }) it(`renders immediately when icon is alredy preloaded`, async done => { jest.mock('../Preloader') - const AppIcon = require('../').default + const AppIcon = require('../').AppIcon const Preloader = require('../Preloader') shallow( <AppIcon app={app} + client={mockClient} onReady={() => { - const wrapper = shallow( - <AppIcon app={app} domain={domain} secure={secure} /> - ) + const wrapper = shallow(<AppIcon app={app} client={mockClient} />) const component = wrapper.getElement() expect(component).toMatchSnapshot() expect(Preloader.getPreloaded).toHaveBeenCalledWith( @@ -153,7 +156,6 @@ describe('AppIcon component', () => { expect(console.error).toHaveBeenCalledTimes(0) done() }} - secure={secure} /> ) }) diff --git a/react/AppSections/components/__snapshots__/SmallAppItem.spec.jsx.snap b/react/AppSections/components/__snapshots__/SmallAppItem.spec.jsx.snap index dd383ff3d5..70331303c8 100644 --- a/react/AppSections/components/__snapshots__/SmallAppItem.spec.jsx.snap +++ b/react/AppSections/components/__snapshots__/SmallAppItem.spec.jsx.snap @@ -9,7 +9,7 @@ exports[`SmallAppItem component should render correctly an app 1`] = ` <div className="SmallAppItem__SmallAppItem-icon-wrapper___rQAvt" > - <AppIcon + <withClient(AppIcon) app={ Object { "developer": Object { @@ -55,7 +55,7 @@ exports[`SmallAppItem component should render correctly an app in maintenance 1` <div className="SmallAppItem__SmallAppItem-icon-wrapper___rQAvt" > - <AppIcon + <withClient(AppIcon) app={ Object { "developer": Object { @@ -110,7 +110,7 @@ exports[`SmallAppItem component should render correctly an app with iconToLoad ( <div className="SmallAppItem__SmallAppItem-icon-wrapper___rQAvt" > - <AppIcon + <withClient(AppIcon) app={ Object { "developer": Object { @@ -163,7 +163,7 @@ exports[`SmallAppItem component should render correctly an app with iconToLoad i <div className="SmallAppItem__SmallAppItem-icon-wrapper___rQAvt" > - <AppIcon + <withClient(AppIcon) app={ Object { "developer": Object { @@ -216,7 +216,7 @@ exports[`SmallAppItem component should render correctly an app without developer <div className="SmallAppItem__SmallAppItem-icon-wrapper___rQAvt" > - <AppIcon + <withClient(AppIcon) app={ Object { "editor": "cozy", @@ -254,7 +254,7 @@ exports[`SmallAppItem component should render correctly an installed app 1`] = ` <div className="SmallAppItem__SmallAppItem-icon-wrapper___rQAvt" > - <AppIcon + <withClient(AppIcon) app={ Object { "developer": Object { @@ -306,7 +306,7 @@ exports[`SmallAppItem component should render correctly an installed app without <div className="SmallAppItem__SmallAppItem-icon-wrapper___rQAvt" > - <AppIcon + <withClient(AppIcon) app={ Object { "developer": Object {