diff options
author | muhamedsalih-tw <104364298+muhamedsalih-tw@users.noreply.github.com> | 2022-11-01 06:42:12 +0530 |
---|---|---|
committer | GitHub <noreply@github.com> | 2022-11-01 01:12:12 +0000 |
commit | 85d1aac4cd70e79d5ab64684dea09e92b17ed2c2 (patch) | |
tree | a006a2eb5c58b9351219d8a85d57a04c5c73787a /src/components | |
parent | refactor: convert global app to typescript (#723) (diff) | |
download | ferdium-app-85d1aac4cd70e79d5ab64684dea09e92b17ed2c2.tar.gz ferdium-app-85d1aac4cd70e79d5ab64684dea09e92b17ed2c2.tar.zst ferdium-app-85d1aac4cd70e79d5ab64684dea09e92b17ed2c2.zip |
Transform ChangeServer components tree to typescript (#725)
Diffstat (limited to 'src/components')
-rw-r--r-- | src/components/auth/ChangeServer.tsx (renamed from src/components/auth/ChangeServer.jsx) | 101 | ||||
-rw-r--r-- | src/components/auth/Login.tsx | 10 | ||||
-rw-r--r-- | src/components/settings/account/AccountDashboard.tsx | 2 | ||||
-rw-r--r-- | src/components/settings/recipes/RecipesDashboard.tsx | 4 | ||||
-rw-r--r-- | src/components/ui/Infobox.tsx (renamed from src/components/ui/Infobox.js) | 90 | ||||
-rw-r--r-- | src/components/ui/Input.tsx | 21 | ||||
-rw-r--r-- | src/components/ui/Select.tsx (renamed from src/components/ui/Select.js) | 77 | ||||
-rw-r--r-- | src/components/ui/infobox/index.tsx | 88 | ||||
-rw-r--r-- | src/components/ui/select/index.tsx | 151 |
9 files changed, 276 insertions, 268 deletions
diff --git a/src/components/auth/ChangeServer.jsx b/src/components/auth/ChangeServer.tsx index 8f4b85fbb..d8509f599 100644 --- a/src/components/auth/ChangeServer.jsx +++ b/src/components/auth/ChangeServer.tsx | |||
@@ -1,8 +1,8 @@ | |||
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 { defineMessages, injectIntl } from 'react-intl'; | 3 | import { defineMessages, injectIntl, WrappedComponentProps } from 'react-intl'; |
5 | import { mdiArrowLeftCircle } from '@mdi/js'; | 4 | import { mdiArrowLeftCircle } from '@mdi/js'; |
5 | import { noop } from 'lodash'; | ||
6 | import Form from '../../lib/Form'; | 6 | import Form from '../../lib/Form'; |
7 | import Input from '../ui/Input'; | 7 | import Input from '../ui/Input'; |
8 | import Select from '../ui/Select'; | 8 | import Select from '../ui/Select'; |
@@ -38,49 +38,53 @@ const messages = defineMessages({ | |||
38 | }, | 38 | }, |
39 | }); | 39 | }); |
40 | 40 | ||
41 | class ChangeServer extends Component { | 41 | interface IProps extends WrappedComponentProps { |
42 | static propTypes = { | 42 | onSubmit: (...args: any[]) => void; |
43 | onSubmit: PropTypes.func.isRequired, | 43 | server: string; |
44 | server: PropTypes.string.isRequired, | 44 | } |
45 | }; | ||
46 | 45 | ||
47 | ferdiumServer = LIVE_FERDIUM_API; | 46 | @observer |
47 | class ChangeServer extends Component<IProps> { | ||
48 | ferdiumServer: string = LIVE_FERDIUM_API; | ||
48 | 49 | ||
49 | franzServer = LIVE_FRANZ_API; | 50 | franzServer: string = LIVE_FRANZ_API; |
50 | 51 | ||
51 | defaultServers = [this.ferdiumServer, this.franzServer]; | 52 | defaultServers: string[] = [LIVE_FERDIUM_API, LIVE_FRANZ_API]; |
52 | 53 | ||
53 | form = (() => { | 54 | form: Form; |
54 | const { intl } = this.props; | 55 | |
55 | return new Form( | 56 | constructor(props: IProps) { |
56 | { | 57 | super(props); |
57 | fields: { | 58 | |
58 | server: { | 59 | this.form = new Form({ |
59 | label: intl.formatMessage(messages.label), | 60 | fields: { |
60 | value: this.props.server, | 61 | server: { |
61 | options: [ | 62 | label: this.props.intl.formatMessage(messages.label), |
62 | { value: this.ferdiumServer, label: 'Ferdium (Default)' }, | 63 | value: this.props.server, |
63 | { value: this.franzServer, label: 'Franz' }, | 64 | options: [ |
64 | { | 65 | { value: this.ferdiumServer, label: 'Ferdium (Default)' }, |
65 | value: this.defaultServers.includes(this.props.server) | 66 | { value: this.franzServer, label: 'Franz' }, |
66 | ? '' | 67 | { |
67 | : this.props.server, | 68 | value: this.defaultServers.includes(this.props.server) |
68 | label: 'Custom', | 69 | ? '' |
69 | }, | 70 | : this.props.server, |
70 | ], | 71 | label: 'Custom', |
71 | }, | 72 | }, |
72 | customServer: { | 73 | ], |
73 | label: intl.formatMessage(messages.customServerLabel), | 74 | }, |
74 | value: '', | 75 | customServer: { |
75 | validators: [url, required], | 76 | label: this.props.intl.formatMessage(messages.customServerLabel), |
76 | }, | 77 | placeholder: this.props.intl.formatMessage( |
78 | messages.customServerLabel, | ||
79 | ), | ||
80 | value: '', | ||
81 | validators: [url, required], | ||
77 | }, | 82 | }, |
78 | }, | 83 | }, |
79 | intl, | 84 | }); |
80 | ); | 85 | } |
81 | })(); | ||
82 | 86 | ||
83 | componentDidMount() { | 87 | componentDidMount(): void { |
84 | if (this.defaultServers.includes(this.props.server)) { | 88 | if (this.defaultServers.includes(this.props.server)) { |
85 | this.form.$('server').value = this.props.server; | 89 | this.form.$('server').value = this.props.server; |
86 | } else { | 90 | } else { |
@@ -89,7 +93,7 @@ class ChangeServer extends Component { | |||
89 | } | 93 | } |
90 | } | 94 | } |
91 | 95 | ||
92 | submit(e) { | 96 | submit(e: FormEvent<HTMLElement>): void { |
93 | e.preventDefault(); | 97 | e.preventDefault(); |
94 | this.form.submit({ | 98 | this.form.submit({ |
95 | onSuccess: form => { | 99 | onSuccess: form => { |
@@ -106,9 +110,10 @@ class ChangeServer extends Component { | |||
106 | }); | 110 | }); |
107 | } | 111 | } |
108 | 112 | ||
109 | render() { | 113 | render(): ReactElement { |
110 | const { form } = this; | 114 | const { form } = this; |
111 | const { intl } = this.props; | 115 | const { intl } = this.props; |
116 | |||
112 | return ( | 117 | return ( |
113 | <div className="auth__container"> | 118 | <div className="auth__container"> |
114 | <form className="franz-form auth__form" onSubmit={e => this.submit(e)}> | 119 | <form className="franz-form auth__form" onSubmit={e => this.submit(e)}> |
@@ -123,21 +128,13 @@ class ChangeServer extends Component { | |||
123 | )} | 128 | )} |
124 | <Select field={form.$('server')} /> | 129 | <Select field={form.$('server')} /> |
125 | {!this.defaultServers.includes(form.$('server').value) && ( | 130 | {!this.defaultServers.includes(form.$('server').value) && ( |
126 | <Input | 131 | <Input placeholder="Custom Server" field={form.$('customServer')} /> |
127 | placeholder="Custom Server" | ||
128 | onChange={e => { | ||
129 | this.form.$('customServer').value = this.form | ||
130 | .$('customServer') | ||
131 | .value.replace(/\/$/, ''); | ||
132 | this.submit(e); | ||
133 | }} | ||
134 | field={form.$('customServer')} | ||
135 | /> | ||
136 | )} | 132 | )} |
137 | <Button | 133 | <Button |
138 | type="submit" | 134 | type="submit" |
139 | className="auth__button" | 135 | className="auth__button" |
140 | label={intl.formatMessage(globalMessages.submit)} | 136 | label={intl.formatMessage(globalMessages.submit)} |
137 | onClick={noop} | ||
141 | /> | 138 | /> |
142 | </form> | 139 | </form> |
143 | <div className="auth__help"> | 140 | <div className="auth__help"> |
@@ -150,4 +147,4 @@ class ChangeServer extends Component { | |||
150 | } | 147 | } |
151 | } | 148 | } |
152 | 149 | ||
153 | export default injectIntl(observer(ChangeServer)); | 150 | export default injectIntl(ChangeServer); |
diff --git a/src/components/auth/Login.tsx b/src/components/auth/Login.tsx index 65381fe04..66a567fe4 100644 --- a/src/components/auth/Login.tsx +++ b/src/components/auth/Login.tsx | |||
@@ -1,5 +1,5 @@ | |||
1 | import { Component, FormEvent, ReactElement } from 'react'; | 1 | import { Component, FormEvent, ReactElement } from 'react'; |
2 | import { observer, inject } from 'mobx-react'; | 2 | import { observer } from 'mobx-react'; |
3 | import { defineMessages, injectIntl, WrappedComponentProps } from 'react-intl'; | 3 | import { defineMessages, injectIntl, WrappedComponentProps } from 'react-intl'; |
4 | import { mdiArrowLeftCircle } from '@mdi/js'; | 4 | import { mdiArrowLeftCircle } from '@mdi/js'; |
5 | import { noop } from 'lodash'; | 5 | import { noop } from 'lodash'; |
@@ -13,10 +13,7 @@ import Input from '../ui/Input'; | |||
13 | import Button from '../ui/button'; | 13 | import Button from '../ui/button'; |
14 | import Link from '../ui/Link'; | 14 | import Link from '../ui/Link'; |
15 | import { H1 } from '../ui/headline'; | 15 | import { H1 } from '../ui/headline'; |
16 | import { | 16 | import { GlobalError } from '../../@types/ferdium-components.types'; |
17 | GlobalError, | ||
18 | StoresProps, | ||
19 | } from '../../@types/ferdium-components.types'; | ||
20 | 17 | ||
21 | const messages = defineMessages({ | 18 | const messages = defineMessages({ |
22 | headline: { | 19 | headline: { |
@@ -65,7 +62,7 @@ const messages = defineMessages({ | |||
65 | }, | 62 | }, |
66 | }); | 63 | }); |
67 | 64 | ||
68 | interface IProps extends Partial<StoresProps>, WrappedComponentProps { | 65 | interface IProps extends WrappedComponentProps { |
69 | onSubmit: (...args: any[]) => void; | 66 | onSubmit: (...args: any[]) => void; |
70 | isSubmitting: boolean; | 67 | isSubmitting: boolean; |
71 | isTokenExpired: boolean; | 68 | isTokenExpired: boolean; |
@@ -76,7 +73,6 @@ interface IProps extends Partial<StoresProps>, WrappedComponentProps { | |||
76 | error: GlobalError; | 73 | error: GlobalError; |
77 | } | 74 | } |
78 | 75 | ||
79 | @inject('actions') | ||
80 | @observer | 76 | @observer |
81 | class Login extends Component<IProps> { | 77 | class Login extends Component<IProps> { |
82 | form: Form; | 78 | form: Form; |
diff --git a/src/components/settings/account/AccountDashboard.tsx b/src/components/settings/account/AccountDashboard.tsx index b0debdaf2..163b0a160 100644 --- a/src/components/settings/account/AccountDashboard.tsx +++ b/src/components/settings/account/AccountDashboard.tsx | |||
@@ -6,7 +6,7 @@ import { H1, H2 } from '../../ui/headline'; | |||
6 | 6 | ||
7 | import Loader from '../../ui/Loader'; | 7 | import Loader from '../../ui/Loader'; |
8 | import Button from '../../ui/button'; | 8 | import Button from '../../ui/button'; |
9 | import Infobox from '../../ui/infobox'; | 9 | import Infobox from '../../ui/infobox/index'; |
10 | import { LOCAL_SERVER, LIVE_FRANZ_API } from '../../../config'; | 10 | import { LOCAL_SERVER, LIVE_FRANZ_API } from '../../../config'; |
11 | import User from '../../../models/User'; | 11 | import User from '../../../models/User'; |
12 | 12 | ||
diff --git a/src/components/settings/recipes/RecipesDashboard.tsx b/src/components/settings/recipes/RecipesDashboard.tsx index fc687bc79..7b7ba19b1 100644 --- a/src/components/settings/recipes/RecipesDashboard.tsx +++ b/src/components/settings/recipes/RecipesDashboard.tsx | |||
@@ -8,7 +8,7 @@ import Button from '../../ui/button'; | |||
8 | import Input from '../../ui/input/index'; | 8 | import Input from '../../ui/input/index'; |
9 | import { H1, H2, H3 } from '../../ui/headline'; | 9 | import { H1, H2, H3 } from '../../ui/headline'; |
10 | import SearchInput from '../../ui/SearchInput'; | 10 | import SearchInput from '../../ui/SearchInput'; |
11 | import Infobox from '../../ui/infobox'; | 11 | import Infobox from '../../ui/infobox/index'; |
12 | import RecipeItem from './RecipeItem'; | 12 | import RecipeItem from './RecipeItem'; |
13 | import Loader from '../../ui/Loader'; | 13 | import Loader from '../../ui/Loader'; |
14 | import Appear from '../../ui/effects/Appear'; | 14 | import Appear from '../../ui/effects/Appear'; |
@@ -166,7 +166,7 @@ class RecipesDashboard extends Component<IProps, IState> { | |||
166 | <Infobox | 166 | <Infobox |
167 | type="success" | 167 | type="success" |
168 | icon="checkbox-marked-circle-outline" | 168 | icon="checkbox-marked-circle-outline" |
169 | dismissable | 169 | dismissible |
170 | > | 170 | > |
171 | {intl.formatMessage(messages.servicesSuccessfulAddedInfo)} | 171 | {intl.formatMessage(messages.servicesSuccessfulAddedInfo)} |
172 | </Infobox> | 172 | </Infobox> |
diff --git a/src/components/ui/Infobox.js b/src/components/ui/Infobox.tsx index 8fb80d87f..1fc24816a 100644 --- a/src/components/ui/Infobox.js +++ b/src/components/ui/Infobox.tsx | |||
@@ -1,11 +1,11 @@ | |||
1 | import { Component } from 'react'; | 1 | import { Component, MouseEventHandler, ReactElement, ReactNode } from 'react'; |
2 | import PropTypes from 'prop-types'; | ||
3 | import { observer } from 'mobx-react'; | ||
4 | import classnames from 'classnames'; | 2 | import classnames from 'classnames'; |
5 | import Loader from 'react-loader'; | 3 | import Loader from 'react-loader'; |
6 | import { defineMessages, injectIntl } from 'react-intl'; | 4 | import { defineMessages, injectIntl, WrappedComponentProps } from 'react-intl'; |
7 | import { mdiAlert, mdiCheckboxMarkedCircleOutline, mdiClose } from '@mdi/js'; | 5 | import { mdiAlert, mdiCheckboxMarkedCircleOutline, mdiClose } from '@mdi/js'; |
8 | import Icon from '../ui/icon'; | 6 | import { noop } from 'lodash'; |
7 | import { observer } from 'mobx-react'; | ||
8 | import Icon from './icon'; | ||
9 | 9 | ||
10 | const icons = { | 10 | const icons = { |
11 | 'checkbox-marked-circle-outline': mdiCheckboxMarkedCircleOutline, | 11 | 'checkbox-marked-circle-outline': mdiCheckboxMarkedCircleOutline, |
@@ -19,55 +19,51 @@ const messages = defineMessages({ | |||
19 | }, | 19 | }, |
20 | }); | 20 | }); |
21 | 21 | ||
22 | // Can this file be merged into the './infobox/index.tsx' file? | 22 | interface IProps extends WrappedComponentProps { |
23 | class Infobox extends Component { | 23 | children: ReactNode; |
24 | static propTypes = { | 24 | icon?: string; |
25 | // eslint-disable-next-line react/forbid-prop-types | 25 | type?: string; |
26 | children: PropTypes.any.isRequired, | 26 | ctaLabel?: string; |
27 | icon: PropTypes.string, | 27 | ctaLoading?: boolean; |
28 | type: PropTypes.string, | 28 | dismissible?: boolean; |
29 | ctaOnClick: PropTypes.func, | 29 | ctaOnClick?: MouseEventHandler<HTMLButtonElement>; |
30 | ctaLabel: PropTypes.string, | 30 | onDismiss?: () => void; |
31 | ctaLoading: PropTypes.bool, | 31 | onSeen?: () => void; |
32 | dismissable: PropTypes.bool, | 32 | } |
33 | onDismiss: PropTypes.func, | ||
34 | onSeen: PropTypes.func, | ||
35 | }; | ||
36 | 33 | ||
37 | static defaultProps = { | 34 | interface IState { |
38 | icon: '', | 35 | dismissed: boolean; |
39 | type: 'primary', | 36 | } |
40 | dismissable: false, | ||
41 | ctaOnClick: () => null, | ||
42 | ctaLabel: '', | ||
43 | ctaLoading: false, | ||
44 | onDismiss: () => null, | ||
45 | onSeen: () => null, | ||
46 | }; | ||
47 | 37 | ||
48 | state = { | 38 | // Can this file be merged into the './infobox/index.tsx' file? |
49 | dismissed: false, | 39 | @observer |
50 | }; | 40 | class Infobox extends Component<IProps, IState> { |
41 | constructor(props: IProps) { | ||
42 | super(props); | ||
43 | |||
44 | this.state = { | ||
45 | dismissed: false, | ||
46 | }; | ||
47 | } | ||
51 | 48 | ||
52 | componentDidMount() { | 49 | componentDidMount(): void { |
53 | const { onSeen } = this.props; | 50 | const { onSeen = noop } = this.props; |
54 | if (onSeen) onSeen(); | 51 | onSeen(); |
55 | } | 52 | } |
56 | 53 | ||
57 | render() { | 54 | render(): ReactElement | null { |
58 | const { | 55 | const { |
59 | children, | 56 | children, |
60 | icon, | 57 | icon = '', |
61 | type, | 58 | type = 'primary', |
62 | ctaLabel, | 59 | dismissible = false, |
63 | ctaLoading, | 60 | ctaOnClick = noop, |
64 | ctaOnClick, | 61 | ctaLabel = '', |
65 | dismissable, | 62 | ctaLoading = false, |
66 | onDismiss, | 63 | onDismiss = noop, |
64 | intl, | ||
67 | } = this.props; | 65 | } = this.props; |
68 | 66 | ||
69 | const { intl } = this.props; | ||
70 | |||
71 | if (this.state.dismissed) { | 67 | if (this.state.dismissed) { |
72 | return null; | 68 | return null; |
73 | } | 69 | } |
@@ -94,7 +90,7 @@ class Infobox extends Component { | |||
94 | {ctaLabel} | 90 | {ctaLabel} |
95 | </button> | 91 | </button> |
96 | )} | 92 | )} |
97 | {dismissable && ( | 93 | {dismissible && ( |
98 | <button | 94 | <button |
99 | type="button" | 95 | type="button" |
100 | onClick={() => { | 96 | onClick={() => { |
@@ -112,4 +108,4 @@ class Infobox extends Component { | |||
112 | } | 108 | } |
113 | } | 109 | } |
114 | 110 | ||
115 | export default injectIntl(observer(Infobox)); | 111 | export default injectIntl(Infobox); |
diff --git a/src/components/ui/Input.tsx b/src/components/ui/Input.tsx index 78b3a9200..c22dc5838 100644 --- a/src/components/ui/Input.tsx +++ b/src/components/ui/Input.tsx | |||
@@ -1,8 +1,16 @@ | |||
1 | import { ChangeEvent, Component, createRef, RefObject } from 'react'; | 1 | import { |
2 | ChangeEvent, | ||
3 | ChangeEventHandler, | ||
4 | Component, | ||
5 | createRef, | ||
6 | ReactElement, | ||
7 | RefObject, | ||
8 | } from 'react'; | ||
2 | import { observer } from 'mobx-react'; | 9 | import { observer } from 'mobx-react'; |
3 | import classnames from 'classnames'; | 10 | import classnames from 'classnames'; |
4 | import { defineMessages, injectIntl, WrappedComponentProps } from 'react-intl'; | 11 | import { defineMessages, injectIntl, WrappedComponentProps } from 'react-intl'; |
5 | import { mdiEye, mdiEyeOff } from '@mdi/js'; | 12 | import { mdiEye, mdiEyeOff } from '@mdi/js'; |
13 | import { noop } from 'lodash'; | ||
6 | import { scorePassword as scorePasswordFunc } from '../../helpers/password-helpers'; | 14 | import { scorePassword as scorePasswordFunc } from '../../helpers/password-helpers'; |
7 | import Icon from './icon'; | 15 | import Icon from './icon'; |
8 | import { Field } from '../../@types/mobx-form.types'; | 16 | import { Field } from '../../@types/mobx-form.types'; |
@@ -23,6 +31,8 @@ interface IProps extends WrappedComponentProps { | |||
23 | scorePassword?: boolean; | 31 | scorePassword?: boolean; |
24 | prefix?: string; | 32 | prefix?: string; |
25 | suffix?: string; | 33 | suffix?: string; |
34 | placeholder?: string; | ||
35 | onChange?: ChangeEventHandler<HTMLInputElement>; | ||
26 | } | 36 | } |
27 | 37 | ||
28 | interface IState { | 38 | interface IState { |
@@ -53,14 +63,17 @@ class Input extends Component<IProps, IState> { | |||
53 | } | 63 | } |
54 | 64 | ||
55 | onChange(e: ChangeEvent<HTMLInputElement>): void { | 65 | onChange(e: ChangeEvent<HTMLInputElement>): void { |
56 | const { field, scorePassword } = this.props; | 66 | const { field, scorePassword, onChange = noop } = this.props; |
57 | 67 | ||
58 | if (field.onChange) { | 68 | if (field.onChange) { |
69 | onChange(e); | ||
59 | field.onChange(e); | 70 | field.onChange(e); |
60 | } | 71 | } |
61 | 72 | ||
62 | if (scorePassword) { | 73 | if (scorePassword) { |
63 | this.setState({ passwordScore: scorePasswordFunc(field.value) }); | 74 | this.setState({ |
75 | passwordScore: scorePasswordFunc(field.value as string), | ||
76 | }); | ||
64 | } | 77 | } |
65 | } | 78 | } |
66 | 79 | ||
@@ -70,7 +83,7 @@ class Input extends Component<IProps, IState> { | |||
70 | } | 83 | } |
71 | } | 84 | } |
72 | 85 | ||
73 | render() { | 86 | render(): ReactElement { |
74 | const { | 87 | const { |
75 | field, | 88 | field, |
76 | className = null, | 89 | className = null, |
diff --git a/src/components/ui/Select.js b/src/components/ui/Select.tsx index ca5ec9964..1d69a9acf 100644 --- a/src/components/ui/Select.js +++ b/src/components/ui/Select.tsx | |||
@@ -1,40 +1,41 @@ | |||
1 | import { createRef, Component } from 'react'; | 1 | import { |
2 | import PropTypes from 'prop-types'; | 2 | createRef, |
3 | Component, | ||
4 | ReactElement, | ||
5 | RefObject, | ||
6 | ChangeEvent, | ||
7 | } from 'react'; | ||
3 | import { observer } from 'mobx-react'; | 8 | import { observer } from 'mobx-react'; |
4 | import { Field } from 'mobx-react-form'; | ||
5 | import classnames from 'classnames'; | 9 | import classnames from 'classnames'; |
10 | import { Field } from '../../@types/mobx-form.types'; | ||
6 | 11 | ||
7 | // Can this file be merged into the './select/index.tsx' file? | 12 | interface IProps { |
8 | class Select extends Component { | 13 | field: Field; |
9 | static propTypes = { | 14 | className?: string; |
10 | field: PropTypes.instanceOf(Field).isRequired, | 15 | showLabel?: boolean; |
11 | className: PropTypes.string, | 16 | disabled?: boolean; |
12 | showLabel: PropTypes.bool, | 17 | multiple?: boolean; |
13 | disabled: PropTypes.bool, | 18 | } |
14 | multiple: PropTypes.bool, | ||
15 | }; | ||
16 | 19 | ||
17 | static defaultProps = { | 20 | // Can this file be merged into the './select/index.tsx' file? |
18 | className: null, | 21 | @observer |
19 | showLabel: true, | 22 | class Select extends Component<IProps> { |
20 | disabled: false, | 23 | private element: RefObject<HTMLSelectElement> = |
21 | multiple: false, | 24 | createRef<HTMLSelectElement>(); |
22 | }; | ||
23 | 25 | ||
24 | constructor(props) { | 26 | constructor(props: IProps) { |
25 | super(props); | 27 | super(props); |
26 | |||
27 | this.element = createRef(); | ||
28 | } | 28 | } |
29 | 29 | ||
30 | multipleChange() { | 30 | multipleChange(e: ChangeEvent<HTMLSelectElement>): void { |
31 | const element = this.element.current; | 31 | e.preventDefault(); |
32 | 32 | if (!this.element.current) { | |
33 | const result = []; | 33 | return; |
34 | const options = element && element.options; | 34 | } |
35 | 35 | const result: string[] = []; | |
36 | const { options } = this.element.current; | ||
36 | for (const option of options) { | 37 | for (const option of options) { |
37 | if (option.selected) { | 38 | if (option.selected && (option.value || option.text)) { |
38 | result.push(option.value || option.text); | 39 | result.push(option.value || option.text); |
39 | } | 40 | } |
40 | } | 41 | } |
@@ -43,8 +44,14 @@ class Select extends Component { | |||
43 | field.value = result; | 44 | field.value = result; |
44 | } | 45 | } |
45 | 46 | ||
46 | render() { | 47 | render(): ReactElement { |
47 | const { field, className, showLabel, disabled, multiple } = this.props; | 48 | const { |
49 | field, | ||
50 | className = null, | ||
51 | showLabel = true, | ||
52 | disabled = false, | ||
53 | multiple = false, | ||
54 | } = this.props; | ||
48 | 55 | ||
49 | let selected = field.value; | 56 | let selected = field.value; |
50 | 57 | ||
@@ -74,7 +81,11 @@ class Select extends Component { | |||
74 | </label> | 81 | </label> |
75 | )} | 82 | )} |
76 | <select | 83 | <select |
77 | onChange={multiple ? e => this.multipleChange(e) : field.onChange} | 84 | onChange={ |
85 | multiple | ||
86 | ? (e: ChangeEvent<HTMLSelectElement>) => this.multipleChange(e) | ||
87 | : field.onChange | ||
88 | } | ||
78 | id={field.id} | 89 | id={field.id} |
79 | defaultValue={selected} | 90 | defaultValue={selected} |
80 | className="franz-form__select" | 91 | className="franz-form__select" |
@@ -82,7 +93,7 @@ class Select extends Component { | |||
82 | multiple={multiple} | 93 | multiple={multiple} |
83 | ref={this.element} | 94 | ref={this.element} |
84 | > | 95 | > |
85 | {field.options.map(type => ( | 96 | {field.options!.map(type => ( |
86 | <option | 97 | <option |
87 | key={type.value} | 98 | key={type.value} |
88 | value={type.value} | 99 | value={type.value} |
@@ -98,4 +109,4 @@ class Select extends Component { | |||
98 | } | 109 | } |
99 | } | 110 | } |
100 | 111 | ||
101 | export default observer(Select); | 112 | export default Select; |
diff --git a/src/components/ui/infobox/index.tsx b/src/components/ui/infobox/index.tsx index ad59ea81e..3b878a9de 100644 --- a/src/components/ui/infobox/index.tsx +++ b/src/components/ui/infobox/index.tsx | |||
@@ -1,32 +1,14 @@ | |||
1 | import { mdiClose } from '@mdi/js'; | 1 | import { mdiClose } from '@mdi/js'; |
2 | import classnames from 'classnames'; | 2 | import classnames from 'classnames'; |
3 | import { Component, ReactNode } from 'react'; | 3 | import { noop } from 'lodash'; |
4 | import injectStyle, { WithStylesProps } from 'react-jss'; | 4 | import { Component, ReactElement, ReactNode } from 'react'; |
5 | 5 | import withStyles, { WithStylesProps } from 'react-jss'; | |
6 | import { Theme } from '../../../themes'; | 6 | import { Theme } from '../../../themes'; |
7 | import Icon from '../icon'; | 7 | import Icon from '../icon'; |
8 | 8 | ||
9 | interface IProps extends WithStylesProps<typeof styles> { | ||
10 | children: ReactNode; | ||
11 | icon?: string; | ||
12 | type?: string; | ||
13 | dismissable?: boolean; | ||
14 | ctaLabel?: string; | ||
15 | |||
16 | className?: string; | ||
17 | onDismiss?: () => void; | ||
18 | onUnmount?: () => void; | ||
19 | ctaOnClick?: () => void; | ||
20 | } | ||
21 | |||
22 | interface IState { | ||
23 | isDismissing: boolean; | ||
24 | dismissed: boolean; | ||
25 | } | ||
26 | |||
27 | const buttonStyles = (theme: Theme) => { | 9 | const buttonStyles = (theme: Theme) => { |
28 | const styles = {}; | 10 | const styles = {}; |
29 | Object.keys(theme.styleTypes).map(style => { | 11 | for (const style of Object.keys(theme.styleTypes)) { |
30 | Object.assign(styles, { | 12 | Object.assign(styles, { |
31 | [style]: { | 13 | [style]: { |
32 | background: theme.styleTypes[style].accent, | 14 | background: theme.styleTypes[style].accent, |
@@ -38,7 +20,7 @@ const buttonStyles = (theme: Theme) => { | |||
38 | }, | 20 | }, |
39 | }, | 21 | }, |
40 | }); | 22 | }); |
41 | }); | 23 | } |
42 | 24 | ||
43 | return styles; | 25 | return styles; |
44 | }; | 26 | }; |
@@ -108,23 +90,35 @@ const styles = (theme: Theme) => ({ | |||
108 | ...buttonStyles(theme), | 90 | ...buttonStyles(theme), |
109 | }); | 91 | }); |
110 | 92 | ||
93 | interface IProps extends WithStylesProps<typeof styles> { | ||
94 | children: ReactNode; | ||
95 | icon?: string; | ||
96 | type?: string; | ||
97 | dismissible?: boolean; | ||
98 | ctaLabel?: string; | ||
99 | className?: string; | ||
100 | onDismiss?: () => void; | ||
101 | onUnmount?: () => void; | ||
102 | ctaOnClick?: () => void; | ||
103 | } | ||
104 | |||
105 | interface IState { | ||
106 | isDismissing: boolean; | ||
107 | dismissed: boolean; | ||
108 | } | ||
109 | |||
111 | class InfoboxComponent extends Component<IProps, IState> { | 110 | class InfoboxComponent extends Component<IProps, IState> { |
112 | public static defaultProps = { | 111 | constructor(props: IProps) { |
113 | type: 'primary', | 112 | super(props); |
114 | dismissable: false, | 113 | |
115 | ctaOnClick: () => {}, | 114 | this.state = { |
116 | onDismiss: () => {}, | 115 | isDismissing: false, |
117 | ctaLabel: '', | 116 | dismissed: false, |
118 | className: '', | 117 | }; |
119 | }; | 118 | } |
120 | 119 | ||
121 | state = { | 120 | dismiss(): void { |
122 | isDismissing: false, | 121 | const { onDismiss = noop } = this.props; |
123 | dismissed: false, | ||
124 | }; | ||
125 | |||
126 | dismiss() { | ||
127 | const { onDismiss } = this.props; | ||
128 | 122 | ||
129 | this.setState({ | 123 | this.setState({ |
130 | isDismissing: true, | 124 | isDismissing: true, |
@@ -146,16 +140,16 @@ class InfoboxComponent extends Component<IProps, IState> { | |||
146 | if (onUnmount) onUnmount(); | 140 | if (onUnmount) onUnmount(); |
147 | } | 141 | } |
148 | 142 | ||
149 | render() { | 143 | render(): ReactElement | null { |
150 | const { | 144 | const { |
151 | classes, | 145 | classes, |
152 | children, | 146 | children, |
153 | icon, | 147 | icon, |
154 | type, | 148 | type = 'primary', |
155 | ctaLabel, | 149 | dismissible = false, |
156 | ctaOnClick, | 150 | ctaOnClick = noop, |
157 | dismissable, | 151 | ctaLabel = '', |
158 | className, | 152 | className = '', |
159 | } = this.props; | 153 | } = this.props; |
160 | 154 | ||
161 | const { isDismissing, dismissed } = this.state; | 155 | const { isDismissing, dismissed } = this.state; |
@@ -186,7 +180,7 @@ class InfoboxComponent extends Component<IProps, IState> { | |||
186 | {ctaLabel} | 180 | {ctaLabel} |
187 | </button> | 181 | </button> |
188 | )} | 182 | )} |
189 | {dismissable && ( | 183 | {dismissible && ( |
190 | <button | 184 | <button |
191 | type="button" | 185 | type="button" |
192 | onClick={this.dismiss.bind(this)} | 186 | onClick={this.dismiss.bind(this)} |
@@ -201,4 +195,4 @@ class InfoboxComponent extends Component<IProps, IState> { | |||
201 | } | 195 | } |
202 | } | 196 | } |
203 | 197 | ||
204 | export default injectStyle(styles, { injectTheme: true })(InfoboxComponent); | 198 | export default withStyles(styles, { injectTheme: true })(InfoboxComponent); |
diff --git a/src/components/ui/select/index.tsx b/src/components/ui/select/index.tsx index 805836130..b22c15320 100644 --- a/src/components/ui/select/index.tsx +++ b/src/components/ui/select/index.tsx | |||
@@ -5,47 +5,15 @@ import { | |||
5 | } from '@mdi/js'; | 5 | } from '@mdi/js'; |
6 | import Icon from '@mdi/react'; | 6 | import Icon from '@mdi/react'; |
7 | import classnames from 'classnames'; | 7 | import classnames from 'classnames'; |
8 | import { ChangeEvent, Component, createRef } from 'react'; | 8 | import { ChangeEvent, Component, createRef, ReactElement } from 'react'; |
9 | import injectStyle, { WithStylesProps } from 'react-jss'; | 9 | import withStyles, { WithStylesProps } from 'react-jss'; |
10 | 10 | import { noop } from 'lodash'; | |
11 | import { Theme } from '../../../themes'; | 11 | import { Theme } from '../../../themes'; |
12 | import { IFormField } from '../typings/generic'; | 12 | import { IFormField } from '../typings/generic'; |
13 | |||
14 | import Error from '../error'; | 13 | import Error from '../error'; |
15 | import Label from '../label'; | 14 | import Label from '../label'; |
16 | import Wrapper from '../wrapper'; | 15 | import Wrapper from '../wrapper'; |
17 | 16 | ||
18 | interface IOptions { | ||
19 | [index: string]: string; | ||
20 | } | ||
21 | |||
22 | interface IData { | ||
23 | [index: string]: string; | ||
24 | } | ||
25 | |||
26 | interface IProps extends IFormField, WithStylesProps<typeof styles> { | ||
27 | actionText: string; | ||
28 | className?: string; | ||
29 | inputClassName?: string; | ||
30 | defaultValue?: string; | ||
31 | disabled?: boolean; | ||
32 | id?: string; | ||
33 | name: string; | ||
34 | options: IOptions; | ||
35 | value: string; | ||
36 | onChange: (event: ChangeEvent<HTMLInputElement>) => void; | ||
37 | showSearch: boolean; | ||
38 | data: IData; | ||
39 | } | ||
40 | |||
41 | interface IState { | ||
42 | open: boolean; | ||
43 | value: string; | ||
44 | needle: string; | ||
45 | selected: number; | ||
46 | options: IOptions; | ||
47 | } | ||
48 | |||
49 | let popupTransition: string = 'none'; | 17 | let popupTransition: string = 'none'; |
50 | let toggleTransition: string = 'none'; | 18 | let toggleTransition: string = 'none'; |
51 | 19 | ||
@@ -149,22 +117,38 @@ const styles = (theme: Theme) => ({ | |||
149 | input: {}, | 117 | input: {}, |
150 | }); | 118 | }); |
151 | 119 | ||
152 | class SelectComponent extends Component<IProps> { | 120 | interface IOptions { |
153 | public static defaultProps = { | 121 | [index: string]: string; |
154 | onChange: () => {}, | 122 | } |
155 | showLabel: true, | ||
156 | disabled: false, | ||
157 | error: '', | ||
158 | }; | ||
159 | |||
160 | state = { | ||
161 | open: false, | ||
162 | value: '', | ||
163 | needle: '', | ||
164 | selected: 0, | ||
165 | options: null, | ||
166 | }; | ||
167 | 123 | ||
124 | interface IData { | ||
125 | [index: string]: string; | ||
126 | } | ||
127 | |||
128 | interface IProps extends IFormField, WithStylesProps<typeof styles> { | ||
129 | actionText: string; | ||
130 | className?: string; | ||
131 | inputClassName?: string; | ||
132 | defaultValue?: string; | ||
133 | disabled?: boolean; | ||
134 | id?: string; | ||
135 | name: string; | ||
136 | options: IOptions; | ||
137 | value: string; | ||
138 | onChange: (event: ChangeEvent<HTMLInputElement> | string) => void; | ||
139 | showSearch: boolean; | ||
140 | data: IData; | ||
141 | } | ||
142 | |||
143 | interface IState { | ||
144 | open: boolean; | ||
145 | value: string; | ||
146 | needle: string; | ||
147 | selected: number; | ||
148 | options: IOptions | null; | ||
149 | } | ||
150 | |||
151 | class SelectComponent extends Component<IProps, IState> { | ||
168 | private componentRef = createRef<HTMLDivElement>(); | 152 | private componentRef = createRef<HTMLDivElement>(); |
169 | 153 | ||
170 | private inputRef = createRef<HTMLInputElement>(); | 154 | private inputRef = createRef<HTMLInputElement>(); |
@@ -175,9 +159,12 @@ class SelectComponent extends Component<IProps> { | |||
175 | 159 | ||
176 | private activeOptionRef = createRef<HTMLDivElement>(); | 160 | private activeOptionRef = createRef<HTMLDivElement>(); |
177 | 161 | ||
178 | private keyListener: any; | 162 | private keyListener: (e: KeyboardEvent) => void; |
179 | 163 | ||
180 | static getDerivedStateFromProps(nextProps: IProps, prevState: IProps) { | 164 | static getDerivedStateFromProps( |
165 | nextProps: IProps, | ||
166 | prevState: IProps, | ||
167 | ): Partial<IState> { | ||
181 | if (nextProps.value && nextProps.value !== prevState.value) { | 168 | if (nextProps.value && nextProps.value !== prevState.value) { |
182 | return { | 169 | return { |
183 | value: nextProps.value, | 170 | value: nextProps.value, |
@@ -189,7 +176,22 @@ class SelectComponent extends Component<IProps> { | |||
189 | }; | 176 | }; |
190 | } | 177 | } |
191 | 178 | ||
192 | componentDidUpdate() { | 179 | constructor(props: IProps) { |
180 | super(props); | ||
181 | |||
182 | this.state = { | ||
183 | open: false, | ||
184 | value: '', | ||
185 | needle: '', | ||
186 | selected: 0, | ||
187 | options: null, | ||
188 | }; | ||
189 | |||
190 | this.keyListener = noop; | ||
191 | this.arrowKeysHandler = this.arrowKeysHandler.bind(this); | ||
192 | } | ||
193 | |||
194 | componentDidUpdate(): void { | ||
193 | const { open } = this.state; | 195 | const { open } = this.state; |
194 | 196 | ||
195 | if (this.searchInputRef && this.searchInputRef.current && open) { | 197 | if (this.searchInputRef && this.searchInputRef.current && open) { |
@@ -197,22 +199,20 @@ class SelectComponent extends Component<IProps> { | |||
197 | } | 199 | } |
198 | } | 200 | } |
199 | 201 | ||
200 | componentDidMount() { | 202 | componentDidMount(): void { |
201 | if (this.inputRef && this.inputRef.current) { | 203 | if (this.inputRef && this.inputRef.current) { |
202 | const { data } = this.props; | 204 | const { data } = this.props; |
203 | 205 | ||
204 | if (data) { | 206 | if (data) { |
205 | Object.keys(data).map( | 207 | for (const key of Object.keys(data)) |
206 | // eslint-disable-next-line no-return-assign | 208 | this.inputRef.current!.dataset[key] = data[key]; |
207 | key => (this.inputRef.current!.dataset[key] = data[key]), | ||
208 | ); | ||
209 | } | 209 | } |
210 | } | 210 | } |
211 | 211 | ||
212 | window.addEventListener('keydown', this.arrowKeysHandler.bind(this), false); | 212 | window.addEventListener('keydown', this.arrowKeysHandler, false); |
213 | } | 213 | } |
214 | 214 | ||
215 | componentWillMount() { | 215 | UNSAFE_componentWillMount(): void { |
216 | const { value } = this.props; | 216 | const { value } = this.props; |
217 | 217 | ||
218 | if (this.componentRef && this.componentRef.current) { | 218 | if (this.componentRef && this.componentRef.current) { |
@@ -231,17 +231,16 @@ class SelectComponent extends Component<IProps> { | |||
231 | this.setFilter(); | 231 | this.setFilter(); |
232 | } | 232 | } |
233 | 233 | ||
234 | componentWillUnmount() { | 234 | componentWillUnmount(): void { |
235 | // eslint-disable-next-line unicorn/no-invalid-remove-event-listener | 235 | window.removeEventListener('keydown', this.arrowKeysHandler); |
236 | window.removeEventListener('keydown', this.arrowKeysHandler.bind(this)); | ||
237 | } | 236 | } |
238 | 237 | ||
239 | setFilter(needle = '') { | 238 | setFilter(needle = ''): void { |
240 | const { options } = this.props; | 239 | const { options } = this.props; |
241 | 240 | ||
242 | let filteredOptions = {}; | 241 | let filteredOptions = {}; |
243 | if (needle) { | 242 | if (needle) { |
244 | Object.keys(options).map(key => { | 243 | for (const key of Object.keys(options)) { |
245 | if ( | 244 | if ( |
246 | key.toLocaleLowerCase().startsWith(needle.toLocaleLowerCase()) || | 245 | key.toLocaleLowerCase().startsWith(needle.toLocaleLowerCase()) || |
247 | options[key] | 246 | options[key] |
@@ -252,7 +251,7 @@ class SelectComponent extends Component<IProps> { | |||
252 | [`${key}`]: options[key], | 251 | [`${key}`]: options[key], |
253 | }); | 252 | }); |
254 | } | 253 | } |
255 | }); | 254 | } |
256 | } else { | 255 | } else { |
257 | filteredOptions = options; | 256 | filteredOptions = options; |
258 | } | 257 | } |
@@ -264,7 +263,7 @@ class SelectComponent extends Component<IProps> { | |||
264 | }); | 263 | }); |
265 | } | 264 | } |
266 | 265 | ||
267 | select(key: string) { | 266 | select(key: string): void { |
268 | this.setState(() => ({ | 267 | this.setState(() => ({ |
269 | value: key, | 268 | value: key, |
270 | open: false, | 269 | open: false, |
@@ -273,11 +272,11 @@ class SelectComponent extends Component<IProps> { | |||
273 | this.setFilter(); | 272 | this.setFilter(); |
274 | 273 | ||
275 | if (this.props.onChange) { | 274 | if (this.props.onChange) { |
276 | this.props.onChange(key as any); | 275 | this.props.onChange(key); |
277 | } | 276 | } |
278 | } | 277 | } |
279 | 278 | ||
280 | arrowKeysHandler(e: KeyboardEvent) { | 279 | arrowKeysHandler(e: KeyboardEvent): void { |
281 | const { selected, open, options } = this.state; | 280 | const { selected, open, options } = this.state; |
282 | 281 | ||
283 | if (!open) return; | 282 | if (!open) return; |
@@ -329,22 +328,22 @@ class SelectComponent extends Component<IProps> { | |||
329 | } | 328 | } |
330 | } | 329 | } |
331 | 330 | ||
332 | render() { | 331 | render(): ReactElement { |
333 | const { | 332 | const { |
334 | actionText, | 333 | actionText, |
335 | classes, | 334 | classes, |
336 | className, | 335 | className, |
337 | defaultValue, | 336 | defaultValue, |
338 | disabled, | ||
339 | error, | ||
340 | id, | 337 | id, |
341 | inputClassName, | 338 | inputClassName, |
342 | name, | 339 | name, |
343 | label, | 340 | label, |
344 | showLabel, | ||
345 | showSearch, | 341 | showSearch, |
346 | onChange, | ||
347 | required, | 342 | required, |
343 | onChange = noop, | ||
344 | showLabel = true, | ||
345 | disabled = false, | ||
346 | error = '', | ||
348 | } = this.props; | 347 | } = this.props; |
349 | 348 | ||
350 | const { open, needle, value, selected, options } = this.state; | 349 | const { open, needle, value, selected, options } = this.state; |
@@ -440,6 +439,8 @@ class SelectComponent extends Component<IProps> { | |||
440 | })} | 439 | })} |
441 | onMouseOver={() => this.setState({ selected: i })} | 440 | onMouseOver={() => this.setState({ selected: i })} |
442 | ref={selected === i ? this.activeOptionRef : null} | 441 | ref={selected === i ? this.activeOptionRef : null} |
442 | onKeyUp={noop} | ||
443 | onFocus={noop} | ||
443 | > | 444 | > |
444 | {options![key]} | 445 | {options![key]} |
445 | </div> | 446 | </div> |
@@ -463,4 +464,4 @@ class SelectComponent extends Component<IProps> { | |||
463 | } | 464 | } |
464 | } | 465 | } |
465 | 466 | ||
466 | export default injectStyle(styles, { injectTheme: true })(SelectComponent); | 467 | export default withStyles(styles, { injectTheme: true })(SelectComponent); |