aboutsummaryrefslogtreecommitdiffstats
path: root/src/components
diff options
context:
space:
mode:
Diffstat (limited to 'src/components')
-rw-r--r--src/components/auth/Import.js87
-rw-r--r--src/components/auth/Invite.js105
-rw-r--r--src/components/auth/Pricing.js1
-rw-r--r--src/components/auth/Welcome.js1
-rw-r--r--src/components/services/content/ServiceView.js8
-rw-r--r--src/components/services/content/Services.js1
-rw-r--r--src/components/services/content/WebviewCrashHandler.js1
-rw-r--r--src/components/settings/SettingsLayout.js37
-rw-r--r--src/components/settings/services/EditServiceForm.js15
-rw-r--r--src/components/settings/settings/EditSettingsForm.js9
-rw-r--r--src/components/settings/team/TeamDashboard.js1
-rw-r--r--src/components/ui/ImageUpload.js4
-rw-r--r--src/components/ui/InfoBar.js25
-rw-r--r--src/components/ui/Infobox.js33
-rw-r--r--src/components/ui/Input.js54
-rw-r--r--src/components/ui/Tabs/TabItem.js2
-rw-r--r--src/components/ui/Tabs/Tabs.js8
17 files changed, 229 insertions, 163 deletions
diff --git a/src/components/auth/Import.js b/src/components/auth/Import.js
index 3e34c3162..3073cad73 100644
--- a/src/components/auth/Import.js
+++ b/src/components/auth/Import.js
@@ -28,7 +28,9 @@ const messages = defineMessages({
28 }, 28 },
29}); 29});
30 30
31export default @observer class Import extends Component { 31export default
32@observer
33class Import extends Component {
32 static propTypes = { 34 static propTypes = {
33 services: MobxPropTypes.arrayOrObservableArray.isRequired, 35 services: MobxPropTypes.arrayOrObservableArray.isRequired,
34 onSubmit: PropTypes.func.isRequired, 36 onSubmit: PropTypes.func.isRequired,
@@ -40,17 +42,21 @@ export default @observer class Import extends Component {
40 intl: intlShape, 42 intl: intlShape,
41 }; 43 };
42 44
43 componentWillMount() { 45 componentDidMount() {
44 const config = { 46 const config = {
45 fields: { 47 fields: {
46 import: [...this.props.services.filter(s => s.recipe).map(s => ({ 48 import: [
47 fields: { 49 ...this.props.services
48 add: { 50 .filter((s) => s.recipe)
49 default: true, 51 .map((s) => ({
50 options: s, 52 fields: {
51 }, 53 add: {
52 }, 54 default: true,
53 }))], 55 options: s,
56 },
57 },
58 })),
59 ],
54 }, 60 },
55 }; 61 };
56 62
@@ -62,9 +68,12 @@ export default @observer class Import extends Component {
62 e.preventDefault(); 68 e.preventDefault();
63 this.form.submit({ 69 this.form.submit({
64 onSuccess: (form) => { 70 onSuccess: (form) => {
65 const servicesImport = form.values().import 71 const servicesImport = form
66 .map((value, i) => !value.add || services.filter(s => s.recipe)[i]) 72 .values()
67 .filter(s => typeof s !== 'boolean'); 73 .import.map(
74 (value, i) => !value.add || services.filter((s) => s.recipe)[i],
75 )
76 .filter((s) => typeof s !== 'boolean');
68 77
69 this.props.onSubmit({ services: servicesImport }); 78 this.props.onSubmit({ services: servicesImport });
70 }, 79 },
@@ -76,37 +85,31 @@ export default @observer class Import extends Component {
76 const { intl } = this.context; 85 const { intl } = this.context;
77 const { services, isSubmitting, inviteRoute } = this.props; 86 const { services, isSubmitting, inviteRoute } = this.props;
78 87
79 const availableServices = services.filter(s => s.recipe); 88 const availableServices = services.filter((s) => s.recipe);
80 const unavailableServices = services.filter(s => !s.recipe); 89 const unavailableServices = services.filter((s) => !s.recipe);
81 90
82 return ( 91 return (
83 <div className="auth__scroll-container"> 92 <div className="auth__scroll-container">
84 <div className="auth__container auth__container--signup"> 93 <div className="auth__container auth__container--signup">
85 <form className="franz-form auth__form" onSubmit={e => this.submit(e)}> 94 <form
86 <img 95 className="franz-form auth__form"
87 src="./assets/images/logo.svg" 96 onSubmit={(e) => this.submit(e)}
88 className="auth__logo" 97 >
89 alt="" 98 <img src="./assets/images/logo.svg" className="auth__logo" alt="" />
90 /> 99 <h1>{intl.formatMessage(messages.headline)}</h1>
91 <h1>
92 {intl.formatMessage(messages.headline)}
93 </h1>
94 <table className="service-table available-services"> 100 <table className="service-table available-services">
95 <tbody> 101 <tbody>
96 {this.form.$('import').map((service, i) => ( 102 {this.form.$('import').map((service, i) => (
97 <tr 103 <tr key={service.id} className="service-table__row">
98 key={service.id}
99 className="service-table__row"
100 >
101 <td className="service-table__toggle"> 104 <td className="service-table__toggle">
102 <Toggle 105 <Toggle field={service.$('add')} showLabel={false} />
103 field={service.$('add')}
104 showLabel={false}
105 />
106 </td> 106 </td>
107 <td className="service-table__column-icon"> 107 <td className="service-table__column-icon">
108 <img 108 <img
109 src={availableServices[i].custom_icon || availableServices[i].recipe.icons.svg} 109 src={
110 availableServices[i].custom_icon
111 || availableServices[i].recipe.icons.svg
112 }
110 className={classnames({ 113 className={classnames({
111 'service-table__icon': true, 114 'service-table__icon': true,
112 'has-custom-icon': availableServices[i].custom_icon, 115 'has-custom-icon': availableServices[i].custom_icon,
@@ -125,14 +128,18 @@ export default @observer class Import extends Component {
125 </table> 128 </table>
126 {unavailableServices.length > 0 && ( 129 {unavailableServices.length > 0 && (
127 <div className="unavailable-services"> 130 <div className="unavailable-services">
128 <strong>{intl.formatMessage(messages.notSupportedHeadline)}</strong> 131 <strong>
132 {intl.formatMessage(messages.notSupportedHeadline)}
133 </strong>
129 <p> 134 <p>
130 {services.filter(s => !s.recipe).map((service, i) => ( 135 {services
131 <span key={service.id}> 136 .filter((s) => !s.recipe)
132 {service.name !== '' ? service.name : service.service} 137 .map((service, i) => (
133 {unavailableServices.length > i + 1 ? ', ' : ''} 138 <span key={service.id}>
134 </span> 139 {service.name !== '' ? service.name : service.service}
135 ))} 140 {unavailableServices.length > i + 1 ? ', ' : ''}
141 </span>
142 ))}
136 </p> 143 </p>
137 </div> 144 </div>
138 )} 145 )}
diff --git a/src/components/auth/Invite.js b/src/components/auth/Invite.js
index fd957ee73..4b4d63a6b 100644
--- a/src/components/auth/Invite.js
+++ b/src/components/auth/Invite.js
@@ -43,7 +43,9 @@ const messages = defineMessages({
43 }, 43 },
44}); 44});
45 45
46export default @observer class Invite extends Component { 46export default
47@observer
48class Invite extends Component {
47 static propTypes = { 49 static propTypes = {
48 onSubmit: PropTypes.func.isRequired, 50 onSubmit: PropTypes.func.isRequired,
49 embed: PropTypes.bool, 51 embed: PropTypes.bool,
@@ -63,36 +65,41 @@ export default @observer class Invite extends Component {
63 65
64 state = { showSuccessInfo: false }; 66 state = { showSuccessInfo: false };
65 67
66 componentWillMount() { 68 componentDidMount() {
67 const handlers = { 69 this.form = new Form(
68 onChange: () => { 70 {
69 this.setState({ showSuccessInfo: false }); 71 fields: {
70 }, 72 invite: [
71 }; 73 ...Array(3).fill({
72 74 fields: {
73 this.form = new Form({ 75 name: {
74 fields: { 76 label: this.context.intl.formatMessage(messages.nameLabel),
75 invite: [...Array(3).fill({ 77 placeholder: this.context.intl.formatMessage(
76 fields: { 78 messages.nameLabel,
77 name: { 79 ),
78 label: this.context.intl.formatMessage(messages.nameLabel), 80 onChange: () => {
79 placeholder: this.context.intl.formatMessage(messages.nameLabel), 81 this.setState({ showSuccessInfo: false });
80 handlers, 82 },
81 // related: ['invite.0.email'], // path accepted but does not work 83 // related: ['invite.0.email'], // path accepted but does not work
82 }, 84 },
83 email: { 85 email: {
84 label: this.context.intl.formatMessage(messages.emailLabel), 86 label: this.context.intl.formatMessage(messages.emailLabel),
85 placeholder: this.context.intl.formatMessage(messages.emailLabel), 87 placeholder: this.context.intl.formatMessage(
86 handlers, 88 messages.emailLabel,
87 validators: [email], 89 ),
88 }, 90 onChange: () => {
89 }, 91 this.setState({ showSuccessInfo: false });
90 })], 92 },
93 validators: [email],
94 },
95 },
96 }),
97 ],
98 },
91 }, 99 },
92 }, this.context.intl); 100 this.context.intl,
93 } 101 );
94 102
95 componentDidMount() {
96 document.querySelector('input:first-child').focus(); 103 document.querySelector('input:first-child').focus();
97 } 104 }
98 105
@@ -117,9 +124,10 @@ export default @observer class Invite extends Component {
117 const { intl } = this.context; 124 const { intl } = this.context;
118 const { embed, isInviteSuccessful, isLoadingInvite } = this.props; 125 const { embed, isInviteSuccessful, isLoadingInvite } = this.props;
119 126
120 const atLeastOneEmailAddress = form.$('invite') 127 const atLeastOneEmailAddress = form
121 .map(invite => invite.$('email').value) 128 .$('invite')
122 .some(emailValue => emailValue.trim() !== ''); 129 .map((invite) => invite.$('email').value)
130 .some((emailValue) => emailValue.trim() !== '');
123 131
124 const sendButtonClassName = classnames({ 132 const sendButtonClassName = classnames({
125 auth__button: true, 133 auth__button: true,
@@ -127,7 +135,7 @@ export default @observer class Invite extends Component {
127 }); 135 });
128 136
129 const renderForm = ( 137 const renderForm = (
130 <Fragment> 138 <>
131 {this.state.showSuccessInfo && isInviteSuccessful && ( 139 {this.state.showSuccessInfo && isInviteSuccessful && (
132 <Appear> 140 <Appear>
133 <Infobox 141 <Infobox
@@ -140,18 +148,17 @@ export default @observer class Invite extends Component {
140 </Appear> 148 </Appear>
141 )} 149 )}
142 150
143 <form className="franz-form auth__form" onSubmit={e => this.submit(e)}> 151 <form
152 className="franz-form auth__form"
153 onSubmit={(e) => this.submit(e)}
154 >
144 {!embed && ( 155 {!embed && (
145 <img 156 <img src="./assets/images/logo.svg" className="auth__logo" alt="" />
146 src="./assets/images/logo.svg"
147 className="auth__logo"
148 alt=""
149 />
150 )} 157 )}
151 <h1 className={embed && 'invite__embed'}> 158 <h1 className={embed && 'invite__embed'}>
152 {intl.formatMessage(messages.headline)} 159 {intl.formatMessage(messages.headline)}
153 </h1> 160 </h1>
154 {form.$('invite').map(invite => ( 161 {form.$('invite').map((invite) => (
155 <div className="grid" key={invite.key}> 162 <div className="grid" key={invite.key}>
156 <div className="grid__row"> 163 <div className="grid__row">
157 <Input field={invite.$('name')} showLabel={false} /> 164 <Input field={invite.$('name')} showLabel={false} />
@@ -175,17 +182,27 @@ export default @observer class Invite extends Component {
175 </Link> 182 </Link>
176 )} 183 )}
177 </form> 184 </form>
178 </Fragment> 185 </>
179 ); 186 );
180 187
181 return ( 188 return (
182 <div className={!embed ? 'auth__container auth__container--signup' : 'settings__main'}> 189 <div
190 className={
191 !embed ? 'auth__container auth__container--signup' : 'settings__main'
192 }
193 >
183 {embed && ( 194 {embed && (
184 <div className="settings__header"> 195 <div className="settings__header">
185 <h1>{this.context.intl.formatMessage(messages.settingsHeadline)}</h1> 196 <h1>
197 {this.context.intl.formatMessage(messages.settingsHeadline)}
198 </h1>
186 </div> 199 </div>
187 )} 200 )}
188 {!embed ? <div>{renderForm}</div> : <div className="settings__body invite__form">{renderForm}</div>} 201 {!embed ? (
202 <div>{renderForm}</div>
203 ) : (
204 <div className="settings__body invite__form">{renderForm}</div>
205 )}
189 </div> 206 </div>
190 ); 207 );
191 } 208 }
diff --git a/src/components/auth/Pricing.js b/src/components/auth/Pricing.js
index fecc6ba56..2fcabe54d 100644
--- a/src/components/auth/Pricing.js
+++ b/src/components/auth/Pricing.js
@@ -10,7 +10,6 @@ import { Button } from '@meetfranz/forms';
10import { FeatureItem } from '../ui/FeatureItem'; 10import { FeatureItem } from '../ui/FeatureItem';
11import { FeatureList } from '../ui/FeatureList'; 11import { FeatureList } from '../ui/FeatureList';
12 12
13
14const messages = defineMessages({ 13const messages = defineMessages({
15 headline: { 14 headline: {
16 id: 'pricing.trial.headline.pro', 15 id: 'pricing.trial.headline.pro',
diff --git a/src/components/auth/Welcome.js b/src/components/auth/Welcome.js
index 6e742e0c1..5f2fac64b 100644
--- a/src/components/auth/Welcome.js
+++ b/src/components/auth/Welcome.js
@@ -72,7 +72,6 @@ export default @inject('actions') @observer class Login extends Component {
72 <br /> 72 <br />
73 <br /> 73 <br />
74 74
75
76 <Link to={changeServerRoute}> 75 <Link to={changeServerRoute}>
77 <span style={{ 76 <span style={{
78 textAlign: 'center', 77 textAlign: 'center',
diff --git a/src/components/services/content/ServiceView.js b/src/components/services/content/ServiceView.js
index 444d5fea4..3c0fed32b 100644
--- a/src/components/services/content/ServiceView.js
+++ b/src/components/services/content/ServiceView.js
@@ -109,7 +109,7 @@ export default @inject('stores', 'actions') @observer class ServiceView extends
109 return ( 109 return (
110 <div className={webviewClasses}> 110 <div className={webviewClasses}>
111 {service.isActive && service.isEnabled && ( 111 {service.isActive && service.isEnabled && (
112 <Fragment> 112 <>
113 {service.hasCrashed && ( 113 {service.hasCrashed && (
114 <WebviewCrashHandler 114 <WebviewCrashHandler
115 name={service.recipe.name} 115 name={service.recipe.name}
@@ -131,10 +131,10 @@ export default @inject('stores', 'actions') @observer class ServiceView extends
131 edit={edit} 131 edit={edit}
132 /> 132 />
133 )} 133 )}
134 </Fragment> 134 </>
135 )} 135 )}
136 {!service.isEnabled ? ( 136 {!service.isEnabled ? (
137 <Fragment> 137 <>
138 {service.isActive && ( 138 {service.isActive && (
139 <ServiceDisabled 139 <ServiceDisabled
140 name={service.recipe.name} 140 name={service.recipe.name}
@@ -142,7 +142,7 @@ export default @inject('stores', 'actions') @observer class ServiceView extends
142 enable={enable} 142 enable={enable}
143 /> 143 />
144 )} 144 )}
145 </Fragment> 145 </>
146 ) : ( 146 ) : (
147 <> 147 <>
148 {(!service.isHibernating || service.isHibernationEnabled) ? ( 148 {(!service.isHibernating || service.isHibernationEnabled) ? (
diff --git a/src/components/services/content/Services.js b/src/components/services/content/Services.js
index 7cf02c237..caa3cf9aa 100644
--- a/src/components/services/content/Services.js
+++ b/src/components/services/content/Services.js
@@ -34,7 +34,6 @@ const messages = defineMessages({
34 }, 34 },
35}); 35});
36 36
37
38const styles = { 37const styles = {
39 confettiContainer: { 38 confettiContainer: {
40 position: 'absolute', 39 position: 'absolute',
diff --git a/src/components/services/content/WebviewCrashHandler.js b/src/components/services/content/WebviewCrashHandler.js
index 7a69dba87..b62940c06 100644
--- a/src/components/services/content/WebviewCrashHandler.js
+++ b/src/components/services/content/WebviewCrashHandler.js
@@ -43,7 +43,6 @@ export default @observer class WebviewCrashHandler extends Component {
43 43
44 countdownIntervalTimeout = ms('1s'); 44 countdownIntervalTimeout = ms('1s');
45 45
46
47 componentDidMount() { 46 componentDidMount() {
48 const { reload } = this.props; 47 const { reload } = this.props;
49 48
diff --git a/src/components/settings/SettingsLayout.js b/src/components/settings/SettingsLayout.js
index 72ba7b2e3..5b3b754fa 100644
--- a/src/components/settings/SettingsLayout.js
+++ b/src/components/settings/SettingsLayout.js
@@ -1,38 +1,55 @@
1import React, { Component } from 'react'; 1import React, { Component } from 'react';
2import PropTypes from 'prop-types'; 2import PropTypes from 'prop-types';
3import { observer } from 'mobx-react'; 3import { observer } from 'mobx-react';
4import { defineMessages, intlShape } from 'react-intl';
4 5
5import ErrorBoundary from '../util/ErrorBoundary'; 6import ErrorBoundary from '../util/ErrorBoundary';
6import { oneOrManyChildElements } from '../../prop-types'; 7import { oneOrManyChildElements } from '../../prop-types';
7import Appear from '../ui/effects/Appear'; 8import Appear from '../ui/effects/Appear';
8 9
9export default @observer class SettingsLayout extends Component { 10const messages = defineMessages({
11 closeSettings: {
12 id: 'settings.app.closeSettings',
13 defaultMessage: '!!!Close settings',
14 },
15});
16
17export default
18@observer
19class SettingsLayout extends Component {
10 static propTypes = { 20 static propTypes = {
11 navigation: PropTypes.element.isRequired, 21 navigation: PropTypes.element.isRequired,
12 children: oneOrManyChildElements.isRequired, 22 children: oneOrManyChildElements.isRequired,
13 closeSettings: PropTypes.func.isRequired, 23 closeSettings: PropTypes.func.isRequired,
14 }; 24 };
15 25
16 componentWillMount() { 26 static contextTypes = {
27 intl: intlShape,
28 };
29
30 componentDidMount() {
17 document.addEventListener('keydown', this.handleKeyDown.bind(this), false); 31 document.addEventListener('keydown', this.handleKeyDown.bind(this), false);
18 } 32 }
19 33
20 componentWillUnmount() { 34 componentWillUnmount() {
21 document.removeEventListener('keydown', this.handleKeyDown.bind(this), false); 35 document.removeEventListener(
36 'keydown',
37 this.handleKeyDown.bind(this),
38 false,
39 );
22 } 40 }
23 41
24 handleKeyDown(e) { 42 handleKeyDown(e) {
25 if (e.keyCode === 27) { // escape key 43 if (e.keyCode === 27) {
44 // escape key
26 this.props.closeSettings(); 45 this.props.closeSettings();
27 } 46 }
28 } 47 }
29 48
30 render() { 49 render() {
31 const { 50 const { navigation, children, closeSettings } = this.props;
32 navigation, 51
33 children, 52 const { intl } = this.context;
34 closeSettings,
35 } = this.props;
36 53
37 return ( 54 return (
38 <Appear transitionName="fadeIn-fast"> 55 <Appear transitionName="fadeIn-fast">
@@ -42,6 +59,7 @@ export default @observer class SettingsLayout extends Component {
42 type="button" 59 type="button"
43 className="settings-wrapper__action" 60 className="settings-wrapper__action"
44 onClick={closeSettings} 61 onClick={closeSettings}
62 aria-label={intl.formatMessage(messages.closeSettings)}
45 /> 63 />
46 <div className="settings franz-form"> 64 <div className="settings franz-form">
47 {navigation} 65 {navigation}
@@ -50,6 +68,7 @@ export default @observer class SettingsLayout extends Component {
50 type="button" 68 type="button"
51 className="settings__close mdi mdi-close" 69 className="settings__close mdi mdi-close"
52 onClick={closeSettings} 70 onClick={closeSettings}
71 aria-label={intl.formatMessage(messages.closeSettings)}
53 /> 72 />
54 </div> 73 </div>
55 </ErrorBoundary> 74 </ErrorBoundary>
diff --git a/src/components/settings/services/EditServiceForm.js b/src/components/settings/services/EditServiceForm.js
index 1bbc1f1d4..a8501670b 100644
--- a/src/components/settings/services/EditServiceForm.js
+++ b/src/components/settings/services/EditServiceForm.js
@@ -311,14 +311,14 @@ export default @observer class EditServiceForm extends Component {
311 {recipe.hasCustomUrl && ( 311 {recipe.hasCustomUrl && (
312 <TabItem title={intl.formatMessage(messages.tabOnPremise)}> 312 <TabItem title={intl.formatMessage(messages.tabOnPremise)}>
313 {user.isPremium || recipe.author.find(a => a.email === user.email) ? ( 313 {user.isPremium || recipe.author.find(a => a.email === user.email) ? (
314 <Fragment> 314 <>
315 <Input field={form.$('customUrl')} /> 315 <Input field={form.$('customUrl')} />
316 {form.error === 'url-validation-error' && ( 316 {form.error === 'url-validation-error' && (
317 <p className="franz-form__error"> 317 <p className="franz-form__error">
318 {intl.formatMessage(messages.customUrlValidationError, { name: recipe.name })} 318 {intl.formatMessage(messages.customUrlValidationError, { name: recipe.name })}
319 </p> 319 </p>
320 )} 320 )}
321 </Fragment> 321 </>
322 ) : ( 322 ) : (
323 <div className="center premium-info"> 323 <div className="center premium-info">
324 <p>{intl.formatMessage(messages.customUrlPremiumInfo)}</p> 324 <p>{intl.formatMessage(messages.customUrlPremiumInfo)}</p>
@@ -360,12 +360,12 @@ export default @observer class EditServiceForm extends Component {
360 <h3>{intl.formatMessage(messages.headlineBadges)}</h3> 360 <h3>{intl.formatMessage(messages.headlineBadges)}</h3>
361 <Toggle field={form.$('isBadgeEnabled')} /> 361 <Toggle field={form.$('isBadgeEnabled')} />
362 {recipe.hasIndirectMessages && form.$('isBadgeEnabled').value && ( 362 {recipe.hasIndirectMessages && form.$('isBadgeEnabled').value && (
363 <Fragment> 363 <>
364 <Toggle field={form.$('isIndirectMessageBadgeEnabled')} /> 364 <Toggle field={form.$('isIndirectMessageBadgeEnabled')} />
365 <p className="settings__help"> 365 <p className="settings__help">
366 {intl.formatMessage(messages.indirectMessageInfo)} 366 {intl.formatMessage(messages.indirectMessageInfo)}
367 </p> 367 </p>
368 </Fragment> 368 </>
369 )} 369 )}
370 </div> 370 </div>
371 371
@@ -389,8 +389,7 @@ export default @observer class EditServiceForm extends Component {
389 <Slider field={form.$('darkReaderContrast')} /> 389 <Slider field={form.$('darkReaderContrast')} />
390 <Slider field={form.$('darkReaderSepia')} /> 390 <Slider field={form.$('darkReaderSepia')} />
391 </> 391 </>
392 ) 392 )}
393 }
394 </div> 393 </div>
395 </div> 394 </div>
396 <div className="service-icon"> 395 <div className="service-icon">
@@ -425,7 +424,7 @@ export default @observer class EditServiceForm extends Component {
425 </h3> 424 </h3>
426 <Toggle field={form.$('proxy.isEnabled')} /> 425 <Toggle field={form.$('proxy.isEnabled')} />
427 {form.$('proxy.isEnabled').value && ( 426 {form.$('proxy.isEnabled').value && (
428 <Fragment> 427 <>
429 <div className="grid"> 428 <div className="grid">
430 <div className="grid__row"> 429 <div className="grid__row">
431 <Input field={form.$('proxy.host')} className="proxyHost" /> 430 <Input field={form.$('proxy.host')} className="proxyHost" />
@@ -449,7 +448,7 @@ export default @observer class EditServiceForm extends Component {
449 <span className="mdi mdi-information" /> 448 <span className="mdi mdi-information" />
450 {intl.formatMessage(messages.proxyInfo)} 449 {intl.formatMessage(messages.proxyInfo)}
451 </p> 450 </p>
452 </Fragment> 451 </>
453 )} 452 )}
454 </div> 453 </div>
455 </PremiumFeatureContainer> 454 </PremiumFeatureContainer>
diff --git a/src/components/settings/settings/EditSettingsForm.js b/src/components/settings/settings/EditSettingsForm.js
index 4ba7dc720..98a119bfa 100644
--- a/src/components/settings/settings/EditSettingsForm.js
+++ b/src/components/settings/settings/EditSettingsForm.js
@@ -567,7 +567,7 @@ export default @observer class EditSettingsForm extends Component {
567 condition={!isSpellcheckerIncludedInCurrentPlan} 567 condition={!isSpellcheckerIncludedInCurrentPlan}
568 gaEventInfo={{ category: 'User', event: 'upgrade', label: 'spellchecker' }} 568 gaEventInfo={{ category: 'User', event: 'upgrade', label: 'spellchecker' }}
569 > 569 >
570 <Fragment> 570 <>
571 <Toggle 571 <Toggle
572 field={form.$('enableSpellchecking')} 572 field={form.$('enableSpellchecking')}
573 /> 573 />
@@ -577,7 +577,7 @@ export default @observer class EditSettingsForm extends Component {
577 {isMac && form.$('enableSpellchecking').value && ( 577 {isMac && form.$('enableSpellchecking').value && (
578 <p>{intl.formatMessage(messages.spellCheckerLanguageInfo)}</p> 578 <p>{intl.formatMessage(messages.spellCheckerLanguageInfo)}</p>
579 )} 579 )}
580 </Fragment> 580 </>
581 </PremiumFeatureContainer> 581 </PremiumFeatureContainer>
582 <a 582 <a
583 href={FRANZ_TRANSLATION} 583 href={FRANZ_TRANSLATION}
@@ -679,18 +679,15 @@ export default @observer class EditSettingsForm extends Component {
679 <br /> 679 <br />
680 {intl.formatMessage(messages.updateStatusUpToDate)} 680 {intl.formatMessage(messages.updateStatusUpToDate)}
681 </> 681 </>
682 ) 682 )}
683 }
684 <p className="settings__message"> 683 <p className="settings__message">
685 <span className="mdi mdi-github-face" /> 684 <span className="mdi mdi-github-face" />
686 <span> 685 <span>
687 686
688
689 Ferdi is based on 687 Ferdi is based on
690 {' '} 688 {' '}
691 <a href={`${GITHUB_FRANZ_URL}/franz`} target="_blank">Franz</a> 689 <a href={`${GITHUB_FRANZ_URL}/franz`} target="_blank">Franz</a>
692 690
693
694 , a project published 691 , a project published
695 under the 692 under the
696 {' '} 693 {' '}
diff --git a/src/components/settings/team/TeamDashboard.js b/src/components/settings/team/TeamDashboard.js
index f26f4cc0c..602d6e490 100644
--- a/src/components/settings/team/TeamDashboard.js
+++ b/src/components/settings/team/TeamDashboard.js
@@ -98,7 +98,6 @@ const styles = {
98 }, 98 },
99}; 99};
100 100
101
102export default @injectSheet(styles) @observer class TeamDashboard extends Component { 101export default @injectSheet(styles) @observer class TeamDashboard extends Component {
103 static propTypes = { 102 static propTypes = {
104 isLoading: PropTypes.bool.isRequired, 103 isLoading: PropTypes.bool.isRequired,
diff --git a/src/components/ui/ImageUpload.js b/src/components/ui/ImageUpload.js
index e831eb47b..6a8c7645f 100644
--- a/src/components/ui/ImageUpload.js
+++ b/src/components/ui/ImageUpload.js
@@ -55,7 +55,7 @@ export default @observer class ImageUpload extends Component {
55 </label> 55 </label>
56 <div className="image-upload"> 56 <div className="image-upload">
57 {(field.value && field.value !== 'delete') || this.state.path ? ( 57 {(field.value && field.value !== 'delete') || this.state.path ? (
58 <Fragment> 58 <>
59 <div 59 <div
60 className="image-upload__preview" 60 className="image-upload__preview"
61 style={{ 61 style={{
@@ -80,7 +80,7 @@ export default @observer class ImageUpload extends Component {
80 </button> 80 </button>
81 <div className="image-upload__action-background" /> 81 <div className="image-upload__action-background" />
82 </div> 82 </div>
83 </Fragment> 83 </>
84 ) : ( 84 ) : (
85 <Dropzone 85 <Dropzone
86 ref={(node) => { 86 ref={(node) => {
diff --git a/src/components/ui/InfoBar.js b/src/components/ui/InfoBar.js
index 612399e9f..bd2af2296 100644
--- a/src/components/ui/InfoBar.js
+++ b/src/components/ui/InfoBar.js
@@ -3,11 +3,21 @@ import PropTypes from 'prop-types';
3import { observer } from 'mobx-react'; 3import { observer } from 'mobx-react';
4import classnames from 'classnames'; 4import classnames from 'classnames';
5import Loader from 'react-loader'; 5import Loader from 'react-loader';
6import { defineMessages, intlShape } from 'react-intl';
6 7
7// import { oneOrManyChildElements } from '../../prop-types'; 8// import { oneOrManyChildElements } from '../../prop-types';
8import Appear from './effects/Appear'; 9import Appear from './effects/Appear';
9 10
10export default @observer class InfoBar extends Component { 11const messages = defineMessages({
12 hide: {
13 id: 'infobar.hide',
14 defaultMessage: '!!!Hide',
15 },
16});
17
18export default
19@observer
20class InfoBar extends Component {
11 static propTypes = { 21 static propTypes = {
12 // eslint-disable-next-line 22 // eslint-disable-next-line
13 children: PropTypes.any.isRequired, 23 children: PropTypes.any.isRequired,
@@ -32,6 +42,10 @@ export default @observer class InfoBar extends Component {
32 onHide: () => null, 42 onHide: () => null,
33 }; 43 };
34 44
45 static contextTypes = {
46 intl: intlShape,
47 };
48
35 render() { 49 render() {
36 const { 50 const {
37 children, 51 children,
@@ -45,6 +59,8 @@ export default @observer class InfoBar extends Component {
45 onHide, 59 onHide,
46 } = this.props; 60 } = this.props;
47 61
62 const { intl } = this.context;
63
48 let transitionName = 'slideUp'; 64 let transitionName = 'slideUp';
49 if (position === 'top') { 65 if (position === 'top') {
50 transitionName = 'slideDown'; 66 transitionName = 'slideDown';
@@ -63,11 +79,7 @@ export default @observer class InfoBar extends Component {
63 <div className="info-bar__content"> 79 <div className="info-bar__content">
64 {children} 80 {children}
65 {ctaLabel && ( 81 {ctaLabel && (
66 <button 82 <button type="button" className="info-bar__cta" onClick={onClick}>
67 type="button"
68 className="info-bar__cta"
69 onClick={onClick}
70 >
71 <Loader 83 <Loader
72 loaded={!ctaLoading} 84 loaded={!ctaLoading}
73 lines={10} 85 lines={10}
@@ -84,6 +96,7 @@ export default @observer class InfoBar extends Component {
84 type="button" 96 type="button"
85 className="info-bar__close mdi mdi-close" 97 className="info-bar__close mdi mdi-close"
86 onClick={onHide} 98 onClick={onHide}
99 aria-label={intl.formatMessage(messages.hide)}
87 /> 100 />
88 )} 101 )}
89 </Appear> 102 </Appear>
diff --git a/src/components/ui/Infobox.js b/src/components/ui/Infobox.js
index 0917ee9f0..73b48b957 100644
--- a/src/components/ui/Infobox.js
+++ b/src/components/ui/Infobox.js
@@ -3,8 +3,18 @@ import PropTypes from 'prop-types';
3import { observer } from 'mobx-react'; 3import { observer } from 'mobx-react';
4import classnames from 'classnames'; 4import classnames from 'classnames';
5import Loader from 'react-loader'; 5import Loader from 'react-loader';
6import { defineMessages, intlShape } from 'react-intl';
6 7
7export default @observer class Infobox extends Component { 8const messages = defineMessages({
9 dismiss: {
10 id: 'infobox.dismiss',
11 defaultMessage: '!!!Dismiss',
12 },
13});
14
15export default
16@observer
17class Infobox extends Component {
8 static propTypes = { 18 static propTypes = {
9 children: PropTypes.any.isRequired, // eslint-disable-line 19 children: PropTypes.any.isRequired, // eslint-disable-line
10 icon: PropTypes.string, 20 icon: PropTypes.string,
@@ -28,6 +38,10 @@ export default @observer class Infobox extends Component {
28 onSeen: () => null, 38 onSeen: () => null,
29 }; 39 };
30 40
41 static contextTypes = {
42 intl: intlShape,
43 };
44
31 state = { 45 state = {
32 dismissed: false, 46 dismissed: false,
33 }; 47 };
@@ -49,6 +63,8 @@ export default @observer class Infobox extends Component {
49 onDismiss, 63 onDismiss,
50 } = this.props; 64 } = this.props;
51 65
66 const { intl } = this.context;
67
52 if (this.state.dismissed) { 68 if (this.state.dismissed) {
53 return null; 69 return null;
54 } 70 }
@@ -61,18 +77,10 @@ export default @observer class Infobox extends Component {
61 'infobox--default': !type, 77 'infobox--default': !type,
62 })} 78 })}
63 > 79 >
64 {icon && ( 80 {icon && <i className={`mdi mdi-${icon}`} />}
65 <i className={`mdi mdi-${icon}`} /> 81 <div className="infobox__content">{children}</div>
66 )}
67 <div className="infobox__content">
68 {children}
69 </div>
70 {ctaLabel && ( 82 {ctaLabel && (
71 <button 83 <button className="infobox__cta" onClick={ctaOnClick} type="button">
72 className="infobox__cta"
73 onClick={ctaOnClick}
74 type="button"
75 >
76 <Loader 84 <Loader
77 loaded={!ctaLoading} 85 loaded={!ctaLoading}
78 lines={10} 86 lines={10}
@@ -91,6 +99,7 @@ export default @observer class Infobox extends Component {
91 if (onDismiss) onDismiss(); 99 if (onDismiss) onDismiss();
92 }} 100 }}
93 className="infobox__delete mdi mdi-close" 101 className="infobox__delete mdi mdi-close"
102 aria-label={intl.formatMessage(messages.dismiss)}
94 /> 103 />
95 )} 104 )}
96 </div> 105 </div>
diff --git a/src/components/ui/Input.js b/src/components/ui/Input.js
index 4e3eb4ab8..7417fef1c 100644
--- a/src/components/ui/Input.js
+++ b/src/components/ui/Input.js
@@ -3,10 +3,20 @@ import PropTypes from 'prop-types';
3import { observer } from 'mobx-react'; 3import { observer } from 'mobx-react';
4import { Field } from 'mobx-react-form'; 4import { Field } from 'mobx-react-form';
5import classnames from 'classnames'; 5import classnames from 'classnames';
6import { defineMessages, intlShape } from 'react-intl';
6 7
7import { scorePassword as scorePasswordFunc } from '../../helpers/password-helpers'; 8import { scorePassword as scorePasswordFunc } from '../../helpers/password-helpers';
8 9
9export default @observer class Input extends Component { 10const messages = defineMessages({
11 passwordToggle: {
12 id: 'settings.app.form.passwordToggle',
13 defaultMessage: '!!!Password toggle',
14 },
15});
16
17export default
18@observer
19class Input extends Component {
10 static propTypes = { 20 static propTypes = {
11 field: PropTypes.instanceOf(Field).isRequired, 21 field: PropTypes.instanceOf(Field).isRequired,
12 className: PropTypes.string, 22 className: PropTypes.string,
@@ -28,10 +38,14 @@ export default @observer class Input extends Component {
28 suffix: '', 38 suffix: '',
29 }; 39 };
30 40
41 static contextTypes = {
42 intl: intlShape,
43 };
44
31 state = { 45 state = {
32 showPassword: false, 46 showPassword: false,
33 passwordScore: 0, 47 passwordScore: 0,
34 } 48 };
35 49
36 inputElement = null; 50 inputElement = null;
37 51
@@ -68,6 +82,8 @@ export default @observer class Input extends Component {
68 82
69 const { passwordScore } = this.state; 83 const { passwordScore } = this.state;
70 84
85 const { intl } = this.context;
86
71 let { type } = field; 87 let { type } = field;
72 if (type === 'password' && this.state.showPassword) { 88 if (type === 'password' && this.state.showPassword) {
73 type = 'text'; 89 type = 'text';
@@ -82,9 +98,7 @@ export default @observer class Input extends Component {
82 })} 98 })}
83 > 99 >
84 <div className="franz-form__input-wrapper"> 100 <div className="franz-form__input-wrapper">
85 {prefix && ( 101 {prefix && <span className="franz-form__input-prefix">{prefix}</span>}
86 <span className="franz-form__input-prefix">{prefix}</span>
87 )}
88 <input 102 <input
89 id={field.id} 103 id={field.id}
90 type={type} 104 type={type}
@@ -92,15 +106,15 @@ export default @observer class Input extends Component {
92 name={field.name} 106 name={field.name}
93 value={field.value} 107 value={field.value}
94 placeholder={field.placeholder} 108 placeholder={field.placeholder}
95 onChange={e => this.onChange(e)} 109 onChange={(e) => this.onChange(e)}
96 onBlur={field.onBlur} 110 onBlur={field.onBlur}
97 onFocus={field.onFocus} 111 onFocus={field.onFocus}
98 ref={(element) => { this.inputElement = element; }} 112 ref={(element) => {
113 this.inputElement = element;
114 }}
99 disabled={field.disabled} 115 disabled={field.disabled}
100 /> 116 />
101 {suffix && ( 117 {suffix && <span className="franz-form__input-suffix">{suffix}</span>}
102 <span className="franz-form__input-suffix">{suffix}</span>
103 )}
104 {showPasswordToggle && ( 118 {showPasswordToggle && (
105 <button 119 <button
106 type="button" 120 type="button"
@@ -110,8 +124,11 @@ export default @observer class Input extends Component {
110 'mdi-eye': !this.state.showPassword, 124 'mdi-eye': !this.state.showPassword,
111 'mdi-eye-off': this.state.showPassword, 125 'mdi-eye-off': this.state.showPassword,
112 })} 126 })}
113 onClick={() => this.setState(prevState => ({ showPassword: !prevState.showPassword }))} 127 onClick={() => this.setState((prevState) => ({
114 tabIndex="-1" 128 showPassword: !prevState.showPassword,
129 }))}
130 tabIndex={-1}
131 aria-label={intl.formatMessage(messages.passwordToggle)}
115 /> 132 />
116 )} 133 )}
117 {scorePassword && ( 134 {scorePassword && (
@@ -128,20 +145,11 @@ export default @observer class Input extends Component {
128 )} 145 )}
129 </div> 146 </div>
130 {field.label && showLabel && ( 147 {field.label && showLabel && (
131 <label 148 <label className="franz-form__label" htmlFor={field.name}>
132 className="franz-form__label"
133 htmlFor={field.name}
134 >
135 {field.label} 149 {field.label}
136 </label> 150 </label>
137 )} 151 )}
138 {field.error && ( 152 {field.error && <div className="franz-form__error">{field.error}</div>}
139 <div
140 className="franz-form__error"
141 >
142 {field.error}
143 </div>
144 )}
145 </div> 153 </div>
146 ); 154 );
147 } 155 }
diff --git a/src/components/ui/Tabs/TabItem.js b/src/components/ui/Tabs/TabItem.js
index 16881a7f7..546b05a4e 100644
--- a/src/components/ui/Tabs/TabItem.js
+++ b/src/components/ui/Tabs/TabItem.js
@@ -11,7 +11,7 @@ export default class TabItem extends Component {
11 const { children } = this.props; 11 const { children } = this.props;
12 12
13 return ( 13 return (
14 <Fragment>{children}</Fragment> 14 <>{children}</>
15 ); 15 );
16 } 16 }
17} 17}
diff --git a/src/components/ui/Tabs/Tabs.js b/src/components/ui/Tabs/Tabs.js
index 196cddc66..7ece29fbe 100644
--- a/src/components/ui/Tabs/Tabs.js
+++ b/src/components/ui/Tabs/Tabs.js
@@ -5,7 +5,9 @@ import classnames from 'classnames';
5 5
6import { oneOrManyChildElements } from '../../../prop-types'; 6import { oneOrManyChildElements } from '../../../prop-types';
7 7
8export default @observer class Tab extends Component { 8export default
9@observer
10class Tab extends Component {
9 static propTypes = { 11 static propTypes = {
10 children: oneOrManyChildElements.isRequired, 12 children: oneOrManyChildElements.isRequired,
11 active: PropTypes.number, 13 active: PropTypes.number,
@@ -15,7 +17,7 @@ export default @observer class Tab extends Component {
15 active: 0, 17 active: 0,
16 }; 18 };
17 19
18 componentWillMount() { 20 componentDidMount() {
19 this.setState({ active: this.props.active }); 21 this.setState({ active: this.props.active });
20 } 22 }
21 23
@@ -25,7 +27,7 @@ export default @observer class Tab extends Component {
25 27
26 render() { 28 render() {
27 const { children: childElements } = this.props; 29 const { children: childElements } = this.props;
28 const children = childElements.filter(c => !!c); 30 const children = childElements.filter((c) => !!c);
29 31
30 if (children.length === 1) { 32 if (children.length === 1) {
31 return <div>{children}</div>; 33 return <div>{children}</div>;