@@ -7,8 +7,16 @@ import {addSuccessMessage} from 'app/actionCreators/indicator';
7
7
import AsyncComponent from 'app/components/asyncComponent' ;
8
8
import FieldFromConfig from 'app/views/settings/components/forms/fieldFromConfig' ;
9
9
import Form from 'app/views/settings/components/forms/form' ;
10
+ import { FieldValue } from 'app/views/settings/components/forms/model' ;
10
11
import SentryTypes from 'app/sentryTypes' ;
11
12
import { t } from 'app/locale' ;
13
+ import {
14
+ Group ,
15
+ Integration ,
16
+ PlatformExternalIssue ,
17
+ IntegrationIssueConfig ,
18
+ IssueConfigField ,
19
+ } from 'app/types' ;
12
20
13
21
const MESSAGES_BY_ACTION = {
14
22
link : t ( 'Successfully linked issue.' ) ,
@@ -20,7 +28,19 @@ const SUBMIT_LABEL_BY_ACTION = {
20
28
create : t ( 'Create Issue' ) ,
21
29
} ;
22
30
23
- class ExternalIssueForm extends AsyncComponent {
31
+ type Props = {
32
+ group : Group ;
33
+ integration : Integration ;
34
+ action : 'create' | 'link' ;
35
+ onSubmitSuccess : ( externalIssue : PlatformExternalIssue ) => void ;
36
+ } & AsyncComponent [ 'props' ] ;
37
+
38
+ type State = {
39
+ integrationDetails : IntegrationIssueConfig ;
40
+ dynamicFieldValues ?: { [ key : string ] : FieldValue } ;
41
+ } & AsyncComponent [ 'state' ] ;
42
+
43
+ class ExternalIssueForm extends AsyncComponent < Props , State > {
24
44
static propTypes = {
25
45
group : SentryTypes . Group . isRequired ,
26
46
integration : PropTypes . object . isRequired ,
@@ -30,7 +50,7 @@ class ExternalIssueForm extends AsyncComponent {
30
50
31
51
shouldRenderBadRequests = true ;
32
52
33
- getEndpoints ( ) {
53
+ getEndpoints ( ) : [ string , string ] [ ] {
34
54
const { group, integration, action} = this . props ;
35
55
return [
36
56
[
@@ -40,7 +60,7 @@ class ExternalIssueForm extends AsyncComponent {
40
60
] ;
41
61
}
42
62
43
- onSubmitSuccess = data => {
63
+ onSubmitSuccess = ( data : PlatformExternalIssue ) => {
44
64
addSuccessMessage ( MESSAGES_BY_ACTION [ this . props . action ] ) ;
45
65
this . props . onSubmitSuccess ( data ) ;
46
66
} ;
@@ -71,17 +91,19 @@ class ExternalIssueForm extends AsyncComponent {
71
91
} ) ;
72
92
} ;
73
93
74
- getDynamicFields ( integrationDetails ) {
94
+ getDynamicFields ( integrationDetails ?: IntegrationIssueConfig ) {
75
95
integrationDetails = integrationDetails || this . state . integrationDetails ;
76
96
const { action} = this . props ;
77
- const config = integrationDetails [ `${ action } IssueConfig` ] ;
97
+ const config : IssueConfigField [ ] = integrationDetails [ `${ action } IssueConfig` ] ;
78
98
79
- return config
80
- . filter ( field => field . updatesForm )
81
- . reduce ( ( a , field ) => ( { ...a , [ field . name ] : field . default } ) , { } ) ;
99
+ return Object . fromEntries (
100
+ config
101
+ . filter ( ( field : IssueConfigField ) => field . updatesForm )
102
+ . map ( ( field : IssueConfigField ) => [ field . name , field . default ] )
103
+ ) ;
82
104
}
83
105
84
- onFieldChange = ( label , value ) => {
106
+ onFieldChange = ( label : string , value : FieldValue ) => {
85
107
const dynamicFields = this . getDynamicFields ( ) ;
86
108
if ( label in dynamicFields ) {
87
109
const dynamicFieldValues = this . state . dynamicFieldValues || { } ;
@@ -99,7 +121,7 @@ class ExternalIssueForm extends AsyncComponent {
99
121
}
100
122
} ;
101
123
102
- getOptions = ( field , input ) => {
124
+ getOptions = ( field : IssueConfigField , input : string ) => {
103
125
return new Promise ( ( resolve , reject ) => {
104
126
if ( ! input ) {
105
127
const options = ( field . choices || [ ] ) . map ( ( [ value , label ] ) => ( { value, label} ) ) ;
@@ -116,14 +138,18 @@ class ExternalIssueForm extends AsyncComponent {
116
138
} ;
117
139
118
140
debouncedOptionLoad = debounce (
119
- async ( field , input , cb ) => {
141
+ async (
142
+ field : IssueConfigField ,
143
+ input : string ,
144
+ cb : ( err : Error | null , result ?) => void
145
+ ) => {
120
146
const query = queryString . stringify ( {
121
147
...this . state . dynamicFieldValues ,
122
148
field : field . name ,
123
149
query : input ,
124
150
} ) ;
125
151
126
- const url = field . url ;
152
+ const url = field . url || '' ;
127
153
const separator = url . includes ( '?' ) ? '&' : '?' ;
128
154
// We can't use the API client here since the URL is not scoped under the
129
155
// API endpoints (which the client prefixes)
@@ -138,10 +164,10 @@ class ExternalIssueForm extends AsyncComponent {
138
164
{ trailing : true }
139
165
) ;
140
166
141
- getFieldProps = field =>
167
+ getFieldProps = ( field : IssueConfigField ) =>
142
168
field . url
143
169
? {
144
- loadOptions : input => this . getOptions ( field , input ) ,
170
+ loadOptions : ( input : string ) => this . getOptions ( field , input ) ,
145
171
async : true ,
146
172
cache : false ,
147
173
onSelectResetsInput : false ,
@@ -154,7 +180,7 @@ class ExternalIssueForm extends AsyncComponent {
154
180
renderBody ( ) {
155
181
const { integrationDetails} = this . state ;
156
182
const { action, group, integration} = this . props ;
157
- const config = integrationDetails [ `${ action } IssueConfig` ] ;
183
+ const config : IssueConfigField [ ] = integrationDetails [ `${ action } IssueConfig` ] ;
158
184
159
185
const initialData = { } ;
160
186
config . forEach ( field => {
0 commit comments