diff options
Diffstat (limited to 'src/components')
-rw-r--r-- | src/components/settings/services/EditServiceForm.tsx (renamed from src/components/settings/services/EditServiceForm.js) | 94 | ||||
-rw-r--r-- | src/components/ui/imageUpload/index.tsx (renamed from src/components/ui/ImageUpload.tsx) | 100 | ||||
-rw-r--r-- | src/components/ui/toggle/index.tsx | 17 |
3 files changed, 109 insertions, 102 deletions
diff --git a/src/components/settings/services/EditServiceForm.js b/src/components/settings/services/EditServiceForm.tsx index eb18b57eb..b594fcd40 100644 --- a/src/components/settings/services/EditServiceForm.js +++ b/src/components/settings/services/EditServiceForm.tsx | |||
@@ -1,25 +1,24 @@ | |||
1 | import { Component } from 'react'; | 1 | import { Component, FormEvent, ReactElement } from 'react'; |
2 | import PropTypes from 'prop-types'; | ||
3 | import { observer } from 'mobx-react'; | 2 | import { observer } from 'mobx-react'; |
4 | import { Link } from 'react-router-dom'; | 3 | import { Link } from 'react-router-dom'; |
5 | import { defineMessages, injectIntl } from 'react-intl'; | 4 | import { defineMessages, injectIntl, WrappedComponentProps } from 'react-intl'; |
6 | import normalizeUrl from 'normalize-url'; | 5 | import normalizeUrl from 'normalize-url'; |
7 | import { mdiInformation } from '@mdi/js'; | 6 | import { mdiInformation } from '@mdi/js'; |
8 | import Form from '../../../lib/Form'; | 7 | import Form from '../../../lib/Form'; |
9 | import Recipe from '../../../models/Recipe'; | ||
10 | import Service from '../../../models/Service'; | ||
11 | import Tabs from '../../ui/Tabs/Tabs'; | 8 | import Tabs from '../../ui/Tabs/Tabs'; |
12 | import TabItem from '../../ui/Tabs/TabItem'; | 9 | import TabItem from '../../ui/Tabs/TabItem'; |
13 | import Input from '../../ui/input/index'; | 10 | import Input from '../../ui/input/index'; |
14 | import Toggle from '../../ui/toggle'; | 11 | import Toggle from '../../ui/toggle'; |
15 | import Slider from '../../ui/Slider'; | 12 | import Slider from '../../ui/Slider'; |
16 | import Button from '../../ui/button'; | 13 | import Button from '../../ui/button'; |
17 | import ImageUpload from '../../ui/ImageUpload'; | 14 | import ImageUpload from '../../ui/imageUpload'; |
18 | import Select from '../../ui/Select'; | 15 | import Select from '../../ui/Select'; |
19 | import { isMac } from '../../../environment'; | 16 | import { isMac } from '../../../environment'; |
20 | import globalMessages from '../../../i18n/globalMessages'; | 17 | import globalMessages from '../../../i18n/globalMessages'; |
21 | import Icon from '../../ui/icon'; | 18 | import Icon from '../../ui/icon'; |
22 | import { H3 } from '../../ui/headline'; | 19 | import { H3 } from '../../ui/headline'; |
20 | import { IRecipe } from '../../../models/Recipe'; | ||
21 | import Service from '../../../models/Service'; | ||
23 | 22 | ||
24 | const messages = defineMessages({ | 23 | const messages = defineMessages({ |
25 | saveService: { | 24 | saveService: { |
@@ -149,36 +148,34 @@ const messages = defineMessages({ | |||
149 | }, | 148 | }, |
150 | }); | 149 | }); |
151 | 150 | ||
152 | class EditServiceForm extends Component { | 151 | interface IProps extends WrappedComponentProps { |
153 | static propTypes = { | 152 | recipe: IRecipe; |
154 | recipe: PropTypes.instanceOf(Recipe).isRequired, | 153 | service: Service | null; |
155 | service(props, propName) { | 154 | action?: string; |
156 | if (props.action === 'edit' && !(props[propName] instanceof Service)) { | 155 | form: Form; |
157 | return new Error(`'${propName}'' is expected to be of type 'Service' | 156 | onSubmit: (...args: any[]) => void; |
158 | when editing a Service`); | 157 | onDelete: () => void; |
159 | } | 158 | openRecipeFile: (recipeFile: string) => void; |
159 | isSaving: boolean; | ||
160 | isDeleting: boolean; | ||
161 | isProxyFeatureEnabled: boolean; | ||
162 | } | ||
160 | 163 | ||
161 | return null; | 164 | interface IState { |
162 | }, | 165 | isValidatingCustomUrl: boolean; |
163 | action: PropTypes.string.isRequired, | 166 | } |
164 | form: PropTypes.instanceOf(Form).isRequired, | ||
165 | onSubmit: PropTypes.func.isRequired, | ||
166 | onDelete: PropTypes.func.isRequired, | ||
167 | openRecipeFile: PropTypes.func.isRequired, | ||
168 | isSaving: PropTypes.bool.isRequired, | ||
169 | isDeleting: PropTypes.bool.isRequired, | ||
170 | isProxyFeatureEnabled: PropTypes.bool.isRequired, | ||
171 | }; | ||
172 | 167 | ||
173 | static defaultProps = { | 168 | @observer |
174 | service: {}, | 169 | class EditServiceForm extends Component<IProps, IState> { |
175 | }; | 170 | constructor(props: IProps) { |
171 | super(props); | ||
176 | 172 | ||
177 | state = { | 173 | this.state = { |
178 | isValidatingCustomUrl: false, | 174 | isValidatingCustomUrl: false, |
179 | }; | 175 | }; |
176 | } | ||
180 | 177 | ||
181 | submit(e) { | 178 | submit(e: FormEvent): void { |
182 | const { recipe } = this.props; | 179 | const { recipe } = this.props; |
183 | 180 | ||
184 | e.preventDefault(); | 181 | e.preventDefault(); |
@@ -189,7 +186,8 @@ class EditServiceForm extends Component { | |||
189 | 186 | ||
190 | const { files } = form.$('customIcon'); | 187 | const { files } = form.$('customIcon'); |
191 | if (files) { | 188 | if (files) { |
192 | values.iconFile = files[0]; | 189 | const [iconFile] = files; |
190 | values.iconFile = iconFile; | ||
193 | } | 191 | } |
194 | 192 | ||
195 | if (recipe.validateUrl && values.customUrl) { | 193 | if (recipe.validateUrl && values.customUrl) { |
@@ -219,20 +217,19 @@ class EditServiceForm extends Component { | |||
219 | }); | 217 | }); |
220 | } | 218 | } |
221 | 219 | ||
222 | render() { | 220 | render(): ReactElement { |
223 | const { | 221 | const { |
224 | recipe, | 222 | recipe, |
225 | service, | 223 | service = {} as Service, |
226 | action, | 224 | action = '', |
227 | form, | 225 | form, |
228 | isSaving, | 226 | isSaving, |
229 | isDeleting, | 227 | isDeleting, |
230 | onDelete, | 228 | onDelete, |
231 | openRecipeFile, | 229 | openRecipeFile, |
232 | isProxyFeatureEnabled, | 230 | isProxyFeatureEnabled, |
231 | intl, | ||
233 | } = this.props; | 232 | } = this.props; |
234 | const { intl } = this.props; | ||
235 | |||
236 | const { isValidatingCustomUrl } = this.state; | 233 | const { isValidatingCustomUrl } = this.state; |
237 | 234 | ||
238 | const deleteButton = isDeleting ? ( | 235 | const deleteButton = isDeleting ? ( |
@@ -283,7 +280,8 @@ class EditServiceForm extends Component { | |||
283 | name: recipe.name, | 280 | name: recipe.name, |
284 | }) | 281 | }) |
285 | : intl.formatMessage(messages.editServiceHeadline, { | 282 | : intl.formatMessage(messages.editServiceHeadline, { |
286 | name: service.name !== '' ? service.name : recipe.name, | 283 | name: |
284 | service && service.name !== '' ? service.name : recipe.name, | ||
287 | })} | 285 | })} |
288 | </span> | 286 | </span> |
289 | </div> | 287 | </div> |
@@ -295,23 +293,29 @@ class EditServiceForm extends Component { | |||
295 | {(recipe.hasTeamId || recipe.hasCustomUrl) && ( | 293 | {(recipe.hasTeamId || recipe.hasCustomUrl) && ( |
296 | <Tabs active={activeTabIndex}> | 294 | <Tabs active={activeTabIndex}> |
297 | {recipe.hasHostedOption && ( | 295 | {recipe.hasHostedOption && ( |
298 | <TabItem title={recipe.name}> | 296 | <TabItem |
297 | // title={recipe.name} // TODO - [TS DEBT] property not used inside TabItem need to check it | ||
298 | > | ||
299 | {intl.formatMessage(messages.useHostedService, { | 299 | {intl.formatMessage(messages.useHostedService, { |
300 | name: recipe.name, | 300 | name: recipe.name, |
301 | })} | 301 | })} |
302 | </TabItem> | 302 | </TabItem> |
303 | )} | 303 | )} |
304 | {recipe.hasTeamId && ( | 304 | {recipe.hasTeamId && ( |
305 | <TabItem title={intl.formatMessage(messages.tabHosted)}> | 305 | <TabItem |
306 | // title={intl.formatMessage(messages.tabHosted)} // TODO - [TS DEBT] property not used inside TabItem need to check it | ||
307 | > | ||
306 | <Input | 308 | <Input |
307 | field={form.$('team')} | 309 | {...form.$('team').bind()} |
308 | prefix={recipe.urlInputPrefix} | 310 | prefix={recipe.urlInputPrefix} |
309 | suffix={recipe.urlInputSuffix} | 311 | suffix={recipe.urlInputSuffix} |
310 | /> | 312 | /> |
311 | </TabItem> | 313 | </TabItem> |
312 | )} | 314 | )} |
313 | {recipe.hasCustomUrl && ( | 315 | {recipe.hasCustomUrl && ( |
314 | <TabItem title={intl.formatMessage(messages.tabOnPremise)}> | 316 | <TabItem |
317 | // title={intl.formatMessage(messages.tabOnPremise)} // TODO - [TS DEBT] property not used inside TabItem need to check it | ||
318 | > | ||
315 | <Input {...form.$('customUrl').bind()} /> | 319 | <Input {...form.$('customUrl').bind()} /> |
316 | {form.error === 'url-validation-error' && ( | 320 | {form.error === 'url-validation-error' && ( |
317 | <p className="franz-form__error"> | 321 | <p className="franz-form__error"> |
@@ -404,7 +408,7 @@ class EditServiceForm extends Component { | |||
404 | </div> | 408 | </div> |
405 | <div className="service-icon"> | 409 | <div className="service-icon"> |
406 | <ImageUpload | 410 | <ImageUpload |
407 | field={form.$('customIcon')} | 411 | {...form.$('customIcon').bind()} |
408 | textDelete={intl.formatMessage(messages.iconDelete)} | 412 | textDelete={intl.formatMessage(messages.iconDelete)} |
409 | textUpload={intl.formatMessage(messages.iconUpload)} | 413 | textUpload={intl.formatMessage(messages.iconUpload)} |
410 | maxSize={2_097_152} | 414 | maxSize={2_097_152} |
@@ -533,4 +537,4 @@ class EditServiceForm extends Component { | |||
533 | } | 537 | } |
534 | } | 538 | } |
535 | 539 | ||
536 | export default injectIntl(observer(EditServiceForm)); | 540 | export default injectIntl(EditServiceForm); |
diff --git a/src/components/ui/ImageUpload.tsx b/src/components/ui/imageUpload/index.tsx index 7732928f8..7a2ca747f 100644 --- a/src/components/ui/ImageUpload.tsx +++ b/src/components/ui/imageUpload/index.tsx | |||
@@ -1,15 +1,15 @@ | |||
1 | import { Component } from 'react'; | 1 | import { Component, InputHTMLAttributes } from 'react'; |
2 | import { observer } from 'mobx-react'; | 2 | import { observer } from 'mobx-react'; |
3 | import { Field } from 'mobx-react-form'; | ||
4 | import classnames from 'classnames'; | 3 | import classnames from 'classnames'; |
5 | import Dropzone from 'react-dropzone'; | 4 | import Dropzone from 'react-dropzone'; |
6 | import { mdiDelete, mdiFileImage } from '@mdi/js'; | 5 | import { mdiDelete, mdiFileImage } from '@mdi/js'; |
7 | import prettyBytes from 'pretty-bytes'; | 6 | import prettyBytes from 'pretty-bytes'; |
8 | import { isWindows } from '../../environment'; | 7 | import { noop } from 'lodash'; |
9 | import Icon from './icon'; | 8 | import { isWindows } from '../../../environment'; |
9 | import Icon from '../icon'; | ||
10 | import { IFormField } from '../typings/generic'; | ||
10 | 11 | ||
11 | type Props = { | 12 | interface IProps extends InputHTMLAttributes<HTMLInputElement>, IFormField { |
12 | field: typeof Field; | ||
13 | className: string; | 13 | className: string; |
14 | multiple: boolean; | 14 | multiple: boolean; |
15 | textDelete: string; | 15 | textDelete: string; |
@@ -19,39 +19,43 @@ type Props = { | |||
19 | maxSize?: number; | 19 | maxSize?: number; |
20 | maxFiles?: number; | 20 | maxFiles?: number; |
21 | messages: any; | 21 | messages: any; |
22 | }; | 22 | set?: (value: string) => void; |
23 | } | ||
23 | 24 | ||
24 | // Should this file be converted into the coding style similar to './toggle/index.tsx'? | 25 | interface IState { |
25 | class ImageUpload extends Component<Props> { | 26 | path: string | null; |
26 | static defaultProps = { | 27 | errorState: boolean; |
27 | multiple: false, | 28 | errorMessage: { message: string }; |
28 | maxSize: Number.POSITIVE_INFINITY, | 29 | } |
29 | maxFiles: 0, | ||
30 | }; | ||
31 | 30 | ||
32 | state = { | 31 | // TODO - drag and drop image for recipe add/edit not working from 6.2.0 need to look at it |
33 | path: null, | 32 | @observer |
34 | errorState: false, | 33 | class ImageUpload extends Component<IProps, IState> { |
35 | }; | 34 | constructor(props: IProps) { |
35 | super(props); | ||
36 | 36 | ||
37 | errorMessage = { | 37 | this.state = { |
38 | message: '', | 38 | path: null, |
39 | }; | 39 | errorState: false, |
40 | errorMessage: { | ||
41 | message: '', | ||
42 | }, | ||
43 | }; | ||
44 | } | ||
40 | 45 | ||
41 | onDropAccepted(acceptedFiles) { | 46 | onDropAccepted(acceptedFiles): void { |
42 | const { field } = this.props; | 47 | const { onDrop = noop, set = noop } = this.props; |
43 | this.setState({ errorState: false }); | 48 | this.setState({ errorState: false }); |
44 | 49 | ||
45 | for (const file of acceptedFiles) { | 50 | for (const file of acceptedFiles) { |
46 | const imgPath = isWindows ? file.path.replace(/\\/g, '/') : file.path; | 51 | const imgPath: string = isWindows |
47 | this.setState({ | 52 | ? file.path.replace(/\\/g, '/') |
48 | path: imgPath, | 53 | : file.path; |
49 | }); | 54 | this.setState({ path: imgPath }); |
50 | 55 | onDrop(file); | |
51 | this.props.field.onDrop(file); | ||
52 | } | 56 | } |
53 | 57 | ||
54 | field.set(''); | 58 | set(''); |
55 | } | 59 | } |
56 | 60 | ||
57 | onDropRejected(rejectedFiles): void { | 61 | onDropRejected(rejectedFiles): void { |
@@ -59,11 +63,11 @@ class ImageUpload extends Component<Props> { | |||
59 | for (const error of file.errors) { | 63 | for (const error of file.errors) { |
60 | if (error.code === 'file-too-large') { | 64 | if (error.code === 'file-too-large') { |
61 | this.setState({ errorState: true }); | 65 | this.setState({ errorState: true }); |
62 | this.setState( | 66 | this.setState({ |
63 | (this.errorMessage = { | 67 | errorMessage: { |
64 | message: this.props.textMaxFileSizeError, | 68 | message: this.props.textMaxFileSizeError, |
65 | }), | 69 | }, |
66 | ); | 70 | }); |
67 | } | 71 | } |
68 | } | 72 | } |
69 | } | 73 | } |
@@ -71,14 +75,16 @@ class ImageUpload extends Component<Props> { | |||
71 | 75 | ||
72 | render() { | 76 | render() { |
73 | const { | 77 | const { |
74 | field, | ||
75 | className, | 78 | className, |
76 | multiple, | 79 | multiple = false, |
77 | textDelete, | 80 | textDelete, |
78 | textUpload, | 81 | textUpload, |
79 | textMaxFileSize, | 82 | textMaxFileSize, |
80 | maxSize, | 83 | value, |
81 | maxFiles, | 84 | maxSize = Number.POSITIVE_INFINITY, |
85 | maxFiles = 0, | ||
86 | label = '', | ||
87 | set = noop, | ||
82 | } = this.props; | 88 | } = this.props; |
83 | 89 | ||
84 | const cssClasses = classnames({ | 90 | const cssClasses = classnames({ |
@@ -94,27 +100,25 @@ class ImageUpload extends Component<Props> { | |||
94 | return ( | 100 | return ( |
95 | <div className="image-upload-wrapper"> | 101 | <div className="image-upload-wrapper"> |
96 | <label className="franz-form__label" htmlFor="iconUpload"> | 102 | <label className="franz-form__label" htmlFor="iconUpload"> |
97 | {field.label} | 103 | {label} |
98 | </label> | 104 | </label> |
99 | <div className="image-upload"> | 105 | <div className="image-upload"> |
100 | {(field.value && field.value !== 'delete') || this.state.path ? ( | 106 | {(value && value !== 'delete') || this.state.path ? ( |
101 | <> | 107 | <> |
102 | <div | 108 | <div |
103 | className="image-upload__preview" | 109 | className="image-upload__preview" |
104 | style={{ | 110 | style={{ |
105 | backgroundImage: `url("${this.state.path || field.value}")`, | 111 | backgroundImage: `url("${this.state.path || value}")`, |
106 | }} | 112 | }} |
107 | /> | 113 | /> |
108 | <div className="image-upload__action"> | 114 | <div className="image-upload__action"> |
109 | <button | 115 | <button |
110 | type="button" | 116 | type="button" |
111 | onClick={() => { | 117 | onClick={() => { |
112 | if (field.value) { | 118 | if (value) { |
113 | field.set('delete'); | 119 | set('delete'); |
114 | } else { | 120 | } else { |
115 | this.setState({ | 121 | this.setState({ path: null }); |
116 | path: null, | ||
117 | }); | ||
118 | } | 122 | } |
119 | }} | 123 | }} |
120 | > | 124 | > |
@@ -152,7 +156,7 @@ class ImageUpload extends Component<Props> { | |||
152 | )} | 156 | )} |
153 | {this.state.errorState && ( | 157 | {this.state.errorState && ( |
154 | <span className="image-upload-wrapper__file-size-error"> | 158 | <span className="image-upload-wrapper__file-size-error"> |
155 | {this.errorMessage.message} | 159 | {this.state.errorMessage.message} |
156 | </span> | 160 | </span> |
157 | )} | 161 | )} |
158 | </div> | 162 | </div> |
@@ -160,4 +164,4 @@ class ImageUpload extends Component<Props> { | |||
160 | } | 164 | } |
161 | } | 165 | } |
162 | 166 | ||
163 | export default observer(ImageUpload); | 167 | export default ImageUpload; |
diff --git a/src/components/ui/toggle/index.tsx b/src/components/ui/toggle/index.tsx index fee8adbc7..828941886 100644 --- a/src/components/ui/toggle/index.tsx +++ b/src/components/ui/toggle/index.tsx | |||
@@ -1,7 +1,7 @@ | |||
1 | import classnames from 'classnames'; | 1 | import classnames from 'classnames'; |
2 | import { Property } from 'csstype'; | 2 | import { Property } from 'csstype'; |
3 | import { noop } from 'lodash'; | 3 | import { noop } from 'lodash'; |
4 | import { Component, InputHTMLAttributes } from 'react'; | 4 | import { Component, InputHTMLAttributes, ReactElement } from 'react'; |
5 | import withStyles, { WithStylesProps } from 'react-jss'; | 5 | import withStyles, { WithStylesProps } from 'react-jss'; |
6 | import { Theme } from '../../../themes'; | 6 | import { Theme } from '../../../themes'; |
7 | import Error from '../error'; | 7 | import Error from '../error'; |
@@ -10,11 +10,10 @@ import { IFormField } from '../typings/generic'; | |||
10 | import Wrapper from '../wrapper'; | 10 | import Wrapper from '../wrapper'; |
11 | 11 | ||
12 | interface IProps | 12 | interface IProps |
13 | extends Omit<InputHTMLAttributes<HTMLInputElement>, 'value'>, | 13 | extends InputHTMLAttributes<HTMLInputElement>, |
14 | IFormField, | 14 | IFormField, |
15 | WithStylesProps<typeof styles> { | 15 | WithStylesProps<typeof styles> { |
16 | className?: string; | 16 | className?: string; |
17 | value: boolean | undefined; // due to type capability between InputHTMLAttributes and mobx-react-form | ||
18 | } | 17 | } |
19 | 18 | ||
20 | const buttonTransition: string = | 19 | const buttonTransition: string = |
@@ -62,8 +61,8 @@ const styles = (theme: Theme) => ({ | |||
62 | }, | 61 | }, |
63 | }); | 62 | }); |
64 | 63 | ||
65 | class ToggleComponent extends Component<IProps> { | 64 | class Toggle extends Component<IProps> { |
66 | render() { | 65 | render(): ReactElement { |
67 | const { | 66 | const { |
68 | classes, | 67 | classes, |
69 | className, | 68 | className, |
@@ -71,7 +70,7 @@ class ToggleComponent extends Component<IProps> { | |||
71 | name = '', | 70 | name = '', |
72 | label = '', | 71 | label = '', |
73 | error = '', | 72 | error = '', |
74 | value = false, | 73 | checked = false, |
75 | showLabel = true, | 74 | showLabel = true, |
76 | disabled = false, | 75 | disabled = false, |
77 | onChange = noop, | 76 | onChange = noop, |
@@ -94,14 +93,14 @@ class ToggleComponent extends Component<IProps> { | |||
94 | <div | 93 | <div |
95 | className={classnames({ | 94 | className={classnames({ |
96 | [`${classes.button}`]: true, | 95 | [`${classes.button}`]: true, |
97 | [`${classes.buttonActive}`]: value, | 96 | [`${classes.buttonActive}`]: checked, |
98 | })} | 97 | })} |
99 | /> | 98 | /> |
100 | <input | 99 | <input |
101 | type="checkbox" | 100 | type="checkbox" |
102 | id={id} | 101 | id={id} |
103 | name={name} | 102 | name={name} |
104 | checked={value as boolean | undefined} | 103 | checked={checked} |
105 | className={classes.input} | 104 | className={classes.input} |
106 | onChange={onChange} | 105 | onChange={onChange} |
107 | disabled={disabled} | 106 | disabled={disabled} |
@@ -114,4 +113,4 @@ class ToggleComponent extends Component<IProps> { | |||
114 | } | 113 | } |
115 | } | 114 | } |
116 | 115 | ||
117 | export default withStyles(styles, { injectTheme: true })(ToggleComponent); | 116 | export default withStyles(styles, { injectTheme: true })(Toggle); |