tf-checkout-react 1.0.50 → 1.0.51
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/components/billing-info-container/utils.d.ts +6 -0
- package/dist/components/common/SelectField.d.ts +20 -0
- package/dist/tf-checkout-react.cjs.development.css +3 -3
- package/dist/tf-checkout-react.cjs.development.js +203 -54
- package/dist/tf-checkout-react.cjs.development.js.map +1 -1
- package/dist/tf-checkout-react.cjs.production.min.js +1 -1
- package/dist/tf-checkout-react.cjs.production.min.js.map +1 -1
- package/dist/tf-checkout-react.esm.js +211 -62
- package/dist/tf-checkout-react.esm.js.map +1 -1
- package/dist/types/billing-info-data.d.ts +3 -0
- package/dist/utils/formikErrorFocus.d.ts +2 -0
- package/package.json +1 -1
- package/src/.DS_Store +0 -0
- package/src/components/.DS_Store +0 -0
- package/src/components/billing-info-container/index.tsx +54 -28
- package/src/components/billing-info-container/style.css +4 -0
- package/src/components/billing-info-container/utils.ts +44 -0
- package/src/components/common/CustomField.tsx +4 -1
- package/src/components/common/SelectField.tsx +89 -0
- package/src/components/paymentContainer/style.css +0 -8
- package/src/components/stripePayment/style.css +4 -0
- package/src/components/waitingList/index.tsx +2 -1
- package/src/types/billing-info-data.ts +3 -0
- package/src/utils/formikErrorFocus.ts +24 -0
|
@@ -7,6 +7,7 @@ export interface IGroupItem {
|
|
|
7
7
|
required?: boolean;
|
|
8
8
|
component?: ReactNode | JSX.Element | HTMLElement;
|
|
9
9
|
onValidate?: (value: any) => void;
|
|
10
|
+
uniqueId?: string;
|
|
10
11
|
[key: string]: any;
|
|
11
12
|
}
|
|
12
13
|
export interface IFieldData {
|
|
@@ -15,10 +16,12 @@ export interface IFieldData {
|
|
|
15
16
|
groupLabel?: string | JSX.Element | HTMLElement;
|
|
16
17
|
groupLabelClassName?: string;
|
|
17
18
|
id: number;
|
|
19
|
+
uniqueId?: string;
|
|
18
20
|
}
|
|
19
21
|
export interface IBillingInfoData {
|
|
20
22
|
id: number | string;
|
|
21
23
|
fields: IFieldData[];
|
|
22
24
|
label?: string | JSX.Element;
|
|
23
25
|
labelClassName?: string;
|
|
26
|
+
uniqueId?: string;
|
|
24
27
|
}
|
package/package.json
CHANGED
package/src/.DS_Store
ADDED
|
Binary file
|
|
Binary file
|
|
@@ -30,9 +30,11 @@ import {
|
|
|
30
30
|
import { LoginModal } from '../loginModal'
|
|
31
31
|
import { RegisterModal } from '../registerModal'
|
|
32
32
|
import {
|
|
33
|
+
assingUniqueIds,
|
|
33
34
|
createCheckoutDataBody,
|
|
34
35
|
createRegisterFormData,
|
|
35
36
|
getInitialValues,
|
|
37
|
+
getValidateFunctions,
|
|
36
38
|
setLoggedUserData,
|
|
37
39
|
} from './utils'
|
|
38
40
|
import { CustomField } from '../common/CustomField'
|
|
@@ -41,10 +43,11 @@ import { CheckboxField } from '../common/CheckboxField'
|
|
|
41
43
|
import { CircularProgress } from '@mui/material'
|
|
42
44
|
import { nanoid } from 'nanoid'
|
|
43
45
|
import { getQueryVariable } from '../../utils/getQueryVariable'
|
|
46
|
+
import { SelectField } from '../common/SelectField'
|
|
47
|
+
import { ErrorFocus } from '../../utils/formikErrorFocus'
|
|
44
48
|
|
|
45
49
|
// const TTFLOGO = require('./logo-ttf.png')
|
|
46
50
|
|
|
47
|
-
|
|
48
51
|
export interface IBillingInfoPage {
|
|
49
52
|
data?: IBillingInfoData[];
|
|
50
53
|
ticketHoldersFields?: IBillingInfoData;
|
|
@@ -52,7 +55,7 @@ export interface IBillingInfoPage {
|
|
|
52
55
|
values: FormikValues,
|
|
53
56
|
formikHelpers: FormikHelpers<FormikValues>,
|
|
54
57
|
eventId: any,
|
|
55
|
-
res: any
|
|
58
|
+
res: any
|
|
56
59
|
) => void;
|
|
57
60
|
|
|
58
61
|
onRegisterSuccess?: (value: {
|
|
@@ -143,7 +146,7 @@ const LogicRunner: FC<{
|
|
|
143
146
|
lastName: parsedData?.last_name,
|
|
144
147
|
email: parsedData?.email || '',
|
|
145
148
|
phone: parsedData?.phone || '',
|
|
146
|
-
confirmEmail: '',
|
|
149
|
+
confirmEmail: parsedData?.email || '',
|
|
147
150
|
state: parsedData?.state || '',
|
|
148
151
|
street_address: parsedData?.street_address || '',
|
|
149
152
|
country: parsedData?.country || 1,
|
|
@@ -199,6 +202,10 @@ export const BillingInfoContainer = ({
|
|
|
199
202
|
? window.localStorage.getItem('access_token') || ''
|
|
200
203
|
: ''
|
|
201
204
|
|
|
205
|
+
const [dataWithUniqueIds, setDataWithUniqueIds] = useState<
|
|
206
|
+
IBillingInfoData[]
|
|
207
|
+
>(data)
|
|
208
|
+
|
|
202
209
|
const [cartInfoData, setCartInfo] = useState<any>({})
|
|
203
210
|
const [countries, setCountries] = useState<any>([])
|
|
204
211
|
const [states, setStates] = useState<any>([])
|
|
@@ -234,7 +241,11 @@ export const BillingInfoContainer = ({
|
|
|
234
241
|
const showTicketHolderName = getQueryVariable('names_required') === 'true'
|
|
235
242
|
const eventId = getQueryVariable('event_id')
|
|
236
243
|
const optedInFieldValue: boolean = _get(cartInfoData, 'optedIn', false)
|
|
237
|
-
const
|
|
244
|
+
const hideTtfOptIn: boolean = _get(cartInfoData, 'hide_ttf_opt_in', true)
|
|
245
|
+
|
|
246
|
+
if (!_get(dataWithUniqueIds, '[0].uniqueId')) {
|
|
247
|
+
setDataWithUniqueIds(assingUniqueIds(data))
|
|
248
|
+
}
|
|
238
249
|
|
|
239
250
|
const getQuantity = (cart: any = []) => {
|
|
240
251
|
let qty: any = 0
|
|
@@ -276,7 +287,9 @@ export const BillingInfoContainer = ({
|
|
|
276
287
|
const cartInfo = _get(res, 'data.data.attributes')
|
|
277
288
|
setCartInfo(cartInfo)
|
|
278
289
|
const { cart = [] } = cartInfo
|
|
279
|
-
setTicketsQuantity(
|
|
290
|
+
setTicketsQuantity(
|
|
291
|
+
new Array(getQuantity(cart)).fill(null).map(() => nanoid())
|
|
292
|
+
)
|
|
280
293
|
onGetCartSuccess(res.data)
|
|
281
294
|
} catch (e) {
|
|
282
295
|
if (axios.isAxiosError(e)) {
|
|
@@ -314,7 +327,7 @@ export const BillingInfoContainer = ({
|
|
|
314
327
|
<>
|
|
315
328
|
<Formik
|
|
316
329
|
initialValues={getInitialValues(
|
|
317
|
-
|
|
330
|
+
dataWithUniqueIds,
|
|
318
331
|
{
|
|
319
332
|
...initialValues,
|
|
320
333
|
email: emailLogged,
|
|
@@ -347,7 +360,12 @@ export const BillingInfoContainer = ({
|
|
|
347
360
|
showDOB
|
|
348
361
|
)
|
|
349
362
|
const res = await postOnCheckout(checkoutBody, access_token)
|
|
350
|
-
handleSubmit(
|
|
363
|
+
handleSubmit(
|
|
364
|
+
values,
|
|
365
|
+
formikHelpers as FormikHelpers<any>,
|
|
366
|
+
eventId,
|
|
367
|
+
res
|
|
368
|
+
)
|
|
351
369
|
return
|
|
352
370
|
}
|
|
353
371
|
|
|
@@ -416,7 +434,12 @@ export const BillingInfoContainer = ({
|
|
|
416
434
|
checkoutBody,
|
|
417
435
|
access_token_register
|
|
418
436
|
)
|
|
419
|
-
handleSubmit(
|
|
437
|
+
handleSubmit(
|
|
438
|
+
values,
|
|
439
|
+
formikHelpers as FormikHelpers<any>,
|
|
440
|
+
eventId,
|
|
441
|
+
res
|
|
442
|
+
)
|
|
420
443
|
} catch (e) {
|
|
421
444
|
if (axios.isAxiosError(e)) {
|
|
422
445
|
if (e.response?.data.error === 'invalid_token') {
|
|
@@ -434,6 +457,7 @@ export const BillingInfoContainer = ({
|
|
|
434
457
|
>
|
|
435
458
|
{(props: FormikProps<any>) => (
|
|
436
459
|
<Form onSubmit={props.handleSubmit}>
|
|
460
|
+
<ErrorFocus />
|
|
437
461
|
<LogicRunner
|
|
438
462
|
values={props.values}
|
|
439
463
|
setStates={setStates}
|
|
@@ -467,7 +491,7 @@ export const BillingInfoContainer = ({
|
|
|
467
491
|
</div>
|
|
468
492
|
</div>
|
|
469
493
|
)}
|
|
470
|
-
{_map(
|
|
494
|
+
{_map(dataWithUniqueIds, item => {
|
|
471
495
|
const { label, labelClassName, fields } = item
|
|
472
496
|
if (
|
|
473
497
|
label === 'Ticket Holders' &&
|
|
@@ -477,7 +501,7 @@ export const BillingInfoContainer = ({
|
|
|
477
501
|
return null
|
|
478
502
|
}
|
|
479
503
|
return (
|
|
480
|
-
<React.Fragment key={
|
|
504
|
+
<React.Fragment key={item.uniqueId}>
|
|
481
505
|
<p className={labelClassName}>{label}</p>
|
|
482
506
|
{_map(fields, group => {
|
|
483
507
|
const {
|
|
@@ -487,7 +511,7 @@ export const BillingInfoContainer = ({
|
|
|
487
511
|
groupLabelClassName,
|
|
488
512
|
} = group
|
|
489
513
|
return (
|
|
490
|
-
<React.Fragment key={
|
|
514
|
+
<React.Fragment key={group.uniqueId}>
|
|
491
515
|
{!isLoggedIn && (
|
|
492
516
|
<div className={groupLabelClassName}>
|
|
493
517
|
{groupLabel}
|
|
@@ -511,7 +535,7 @@ export const BillingInfoContainer = ({
|
|
|
511
535
|
if (el.name === 'holderAge' && !showDOB) {
|
|
512
536
|
return false
|
|
513
537
|
}
|
|
514
|
-
if (el.name === '
|
|
538
|
+
if (el.name === 'ttf_opt_in' && hideTtfOptIn) {
|
|
515
539
|
return false
|
|
516
540
|
}
|
|
517
541
|
return true
|
|
@@ -520,10 +544,14 @@ export const BillingInfoContainer = ({
|
|
|
520
544
|
['password', 'confirmPassword'].includes(
|
|
521
545
|
element.name
|
|
522
546
|
) && isLoggedIn ? null : (
|
|
523
|
-
<React.Fragment key={
|
|
524
|
-
{element.name === 'email'
|
|
525
|
-
|
|
526
|
-
|
|
547
|
+
<React.Fragment key={element.uniqueId}>
|
|
548
|
+
{element.name === 'email' ? (
|
|
549
|
+
<div className="email-checking">
|
|
550
|
+
{`IMPORTANT: Please double check that your
|
|
551
|
+
email address is correct. It's where we
|
|
552
|
+
send your confirmation and e-tickets to!`}
|
|
553
|
+
</div>
|
|
554
|
+
) : null}
|
|
527
555
|
<div className={element.className}>
|
|
528
556
|
{element.component ? (
|
|
529
557
|
element.component
|
|
@@ -532,17 +560,15 @@ export const BillingInfoContainer = ({
|
|
|
532
560
|
name={element.name}
|
|
533
561
|
label={element.label}
|
|
534
562
|
type={element.type}
|
|
535
|
-
validate={
|
|
536
|
-
element
|
|
537
|
-
|
|
538
|
-
: () => {},
|
|
539
|
-
element.onValidate
|
|
540
|
-
? element.onValidate
|
|
541
|
-
: () => {}
|
|
563
|
+
validate={getValidateFunctions(
|
|
564
|
+
element,
|
|
565
|
+
states
|
|
542
566
|
)}
|
|
543
567
|
component={
|
|
544
568
|
element.type === 'checkbox'
|
|
545
569
|
? CheckboxField
|
|
570
|
+
: element.type === 'select'
|
|
571
|
+
? SelectField
|
|
546
572
|
: CustomField
|
|
547
573
|
}
|
|
548
574
|
selectOptions={
|
|
@@ -567,21 +593,21 @@ export const BillingInfoContainer = ({
|
|
|
567
593
|
)
|
|
568
594
|
})}
|
|
569
595
|
{showTicketHolderName && (
|
|
570
|
-
<React.Fragment
|
|
596
|
+
<React.Fragment>
|
|
571
597
|
<div className="ticket-holders-fields">
|
|
572
598
|
<p>{ticketHoldersFields.label}</p>
|
|
573
599
|
{_map(ticketsQuantity, (_item, index) => (
|
|
574
|
-
<div key={
|
|
600
|
+
<div key={_item}>
|
|
575
601
|
<h5>Ticket {index + 1}</h5>
|
|
576
602
|
{_map(ticketHoldersFields.fields, group => {
|
|
577
603
|
const { groupClassname, groupItems } = group
|
|
578
604
|
return (
|
|
579
|
-
<div key={
|
|
605
|
+
<div key={group.uniqueId}>
|
|
580
606
|
<div className={groupClassname}>
|
|
581
607
|
{_map(groupItems, element => (
|
|
582
608
|
<div
|
|
583
609
|
className={element.className}
|
|
584
|
-
key={
|
|
610
|
+
key={element.uniqueId}
|
|
585
611
|
>
|
|
586
612
|
<Field
|
|
587
613
|
name={`${element.name}-${index}`}
|
|
@@ -613,7 +639,7 @@ export const BillingInfoContainer = ({
|
|
|
613
639
|
</div>
|
|
614
640
|
</React.Fragment>
|
|
615
641
|
)}
|
|
616
|
-
<div className="button-container"
|
|
642
|
+
<div className="button-container">
|
|
617
643
|
<LoadingButton
|
|
618
644
|
type="submit"
|
|
619
645
|
variant="contained"
|
|
@@ -1,8 +1,13 @@
|
|
|
1
1
|
import _map from 'lodash/map'
|
|
2
|
+
import _get from 'lodash/get'
|
|
2
3
|
import _forEach from 'lodash/forEach'
|
|
3
4
|
import _flatMapDeep from 'lodash/flatMapDeep'
|
|
5
|
+
import _isArray from 'lodash/isArray'
|
|
4
6
|
|
|
5
7
|
import { ENV } from '../../env'
|
|
8
|
+
import { IGroupItem } from '../../types'
|
|
9
|
+
import { combineValidators, requiredValidator } from '../../validators'
|
|
10
|
+
import { nanoid } from 'nanoid'
|
|
6
11
|
|
|
7
12
|
export interface ILoggedInValues {
|
|
8
13
|
emailLogged?: string;
|
|
@@ -81,6 +86,7 @@ export const setLoggedUserData = (data: IUserData) => ({
|
|
|
81
86
|
first_name: data.firstName,
|
|
82
87
|
last_name: data.lastName,
|
|
83
88
|
email: data.email,
|
|
89
|
+
confirmEmail: data.email,
|
|
84
90
|
city: data?.city || '',
|
|
85
91
|
country: data?.country || '',
|
|
86
92
|
phone: data?.phone || '',
|
|
@@ -150,3 +156,41 @@ export const createCheckoutDataBody = (
|
|
|
150
156
|
}
|
|
151
157
|
return body
|
|
152
158
|
}
|
|
159
|
+
|
|
160
|
+
export const getValidateFunctions = (
|
|
161
|
+
element: IGroupItem,
|
|
162
|
+
states: Array<{ [key: string]: any }>
|
|
163
|
+
) => {
|
|
164
|
+
const validationFunctions: any[] = []
|
|
165
|
+
|
|
166
|
+
if (element.required) {
|
|
167
|
+
if (
|
|
168
|
+
element.name !== 'state' ||
|
|
169
|
+
(element.name === 'state' && states.length)
|
|
170
|
+
) {
|
|
171
|
+
validationFunctions.push(requiredValidator)
|
|
172
|
+
}
|
|
173
|
+
}
|
|
174
|
+
|
|
175
|
+
if (element.onValidate) {
|
|
176
|
+
validationFunctions.push(element.onValidate)
|
|
177
|
+
}
|
|
178
|
+
|
|
179
|
+
return combineValidators(...validationFunctions)
|
|
180
|
+
}
|
|
181
|
+
|
|
182
|
+
export const assingUniqueIds = (data: any): any => {
|
|
183
|
+
if (_get(data[0], 'uniqueId')) {
|
|
184
|
+
return data
|
|
185
|
+
}
|
|
186
|
+
|
|
187
|
+
return _map(data, (item: any) => {
|
|
188
|
+
_forEach(item, (itemValue: string, key) => {
|
|
189
|
+
if (_isArray(itemValue)) {
|
|
190
|
+
item[key] = assingUniqueIds(itemValue)
|
|
191
|
+
}
|
|
192
|
+
})
|
|
193
|
+
|
|
194
|
+
return { ...item, uniqueId: nanoid() }
|
|
195
|
+
})
|
|
196
|
+
}
|
|
@@ -2,6 +2,7 @@ import React from 'react'
|
|
|
2
2
|
import TextField from '@mui/material/TextField'
|
|
3
3
|
import _map from 'lodash/map'
|
|
4
4
|
import _get from 'lodash/get'
|
|
5
|
+
import _includes from 'lodash/includes'
|
|
5
6
|
|
|
6
7
|
import { FieldInputProps, FormikProps } from 'formik'
|
|
7
8
|
import { makeStyles } from '@mui/styles'
|
|
@@ -46,8 +47,10 @@ export const CustomField = ({
|
|
|
46
47
|
}: ICustomField & IOtherProps) => {
|
|
47
48
|
const isSelectField = type === 'select'
|
|
48
49
|
const isShrink = Boolean(field.value) || type === 'date' || type === 'select'
|
|
49
|
-
const isTouched = Boolean(_get(touched, field.name))
|
|
50
50
|
const error = _get(errors, field.name)
|
|
51
|
+
const isTouched =
|
|
52
|
+
Boolean(_get(touched, field.name)) ||
|
|
53
|
+
(_includes(field.name, 'holder') && !!error)
|
|
51
54
|
const classes = useStyles()
|
|
52
55
|
|
|
53
56
|
return (
|
|
@@ -0,0 +1,89 @@
|
|
|
1
|
+
import React from 'react'
|
|
2
|
+
import Select from '@mui/material/Select'
|
|
3
|
+
import _map from 'lodash/map'
|
|
4
|
+
import _get from 'lodash/get'
|
|
5
|
+
|
|
6
|
+
import { FieldInputProps, FormikProps } from 'formik'
|
|
7
|
+
import { makeStyles } from '@mui/styles'
|
|
8
|
+
import { FormControl, InputLabel, FormHelperText } from '@mui/material'
|
|
9
|
+
|
|
10
|
+
export interface ISelectOption {
|
|
11
|
+
label: string | number;
|
|
12
|
+
value?: string | number;
|
|
13
|
+
[key: string]: any;
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
export interface ISelectField {
|
|
17
|
+
label: string;
|
|
18
|
+
|
|
19
|
+
field: FieldInputProps<any>;
|
|
20
|
+
form: FormikProps<any>;
|
|
21
|
+
theme: 'dark' | 'light';
|
|
22
|
+
|
|
23
|
+
// optional
|
|
24
|
+
type?: string;
|
|
25
|
+
selectOptions?: ISelectOption[];
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
interface IOtherProps {
|
|
29
|
+
[key: string]: any;
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
const useStyles = makeStyles({
|
|
33
|
+
input: {
|
|
34
|
+
'&::placeholder': {
|
|
35
|
+
color: 'gray',
|
|
36
|
+
},
|
|
37
|
+
},
|
|
38
|
+
})
|
|
39
|
+
|
|
40
|
+
export const SelectField = ({
|
|
41
|
+
label,
|
|
42
|
+
type = 'text',
|
|
43
|
+
field,
|
|
44
|
+
selectOptions = [] as ISelectOption[],
|
|
45
|
+
form: { touched, errors },
|
|
46
|
+
theme,
|
|
47
|
+
}: ISelectField & IOtherProps) => {
|
|
48
|
+
const isTouched = Boolean(_get(touched, field.name))
|
|
49
|
+
const error = _get(errors, field.name)
|
|
50
|
+
const classes = useStyles()
|
|
51
|
+
|
|
52
|
+
return (
|
|
53
|
+
<FormControl fullWidth={true}>
|
|
54
|
+
<InputLabel htmlFor={field.name} error={!!error && isTouched}>
|
|
55
|
+
{label}
|
|
56
|
+
</InputLabel>
|
|
57
|
+
<Select
|
|
58
|
+
id={field.name}
|
|
59
|
+
label={label}
|
|
60
|
+
type={type}
|
|
61
|
+
fullWidth={true}
|
|
62
|
+
error={!!error && isTouched}
|
|
63
|
+
inputProps={{
|
|
64
|
+
id: field.name,
|
|
65
|
+
classes: {
|
|
66
|
+
input: classes.input,
|
|
67
|
+
},
|
|
68
|
+
}}
|
|
69
|
+
native={true}
|
|
70
|
+
className={theme}
|
|
71
|
+
MenuProps={{ className: theme }}
|
|
72
|
+
{...field}
|
|
73
|
+
>
|
|
74
|
+
{_map(selectOptions, option => (
|
|
75
|
+
<option
|
|
76
|
+
key={option.value}
|
|
77
|
+
value={option.value}
|
|
78
|
+
disabled={option.disabled}
|
|
79
|
+
>
|
|
80
|
+
{option.label}
|
|
81
|
+
</option>
|
|
82
|
+
))}
|
|
83
|
+
</Select>
|
|
84
|
+
{isTouched && error ? (
|
|
85
|
+
<FormHelperText error={!!error && isTouched}>{error}</FormHelperText>
|
|
86
|
+
) : null}
|
|
87
|
+
</FormControl>
|
|
88
|
+
)
|
|
89
|
+
}
|
|
@@ -10,6 +10,7 @@ import {
|
|
|
10
10
|
emailValidator,
|
|
11
11
|
} from '../../validators'
|
|
12
12
|
import { ENV } from '../../env'
|
|
13
|
+
import { ErrorFocus } from '../../utils/formikErrorFocus'
|
|
13
14
|
|
|
14
15
|
import './style.css'
|
|
15
16
|
|
|
@@ -58,7 +59,6 @@ const WaitingList = ({ tickets = {} }: WaitingListProps) => {
|
|
|
58
59
|
setShowSuccessMessage(true)
|
|
59
60
|
}
|
|
60
61
|
} catch (error) {
|
|
61
|
-
console.log(error)
|
|
62
62
|
} finally {
|
|
63
63
|
setLoading(false)
|
|
64
64
|
}
|
|
@@ -85,6 +85,7 @@ const WaitingList = ({ tickets = {} }: WaitingListProps) => {
|
|
|
85
85
|
onSubmit={handleSubmit}
|
|
86
86
|
>
|
|
87
87
|
<Form>
|
|
88
|
+
<ErrorFocus />
|
|
88
89
|
{showTicketsField && (
|
|
89
90
|
<>
|
|
90
91
|
<div className="field-item">
|
|
@@ -10,6 +10,7 @@ export interface IGroupItem {
|
|
|
10
10
|
required?: boolean;
|
|
11
11
|
component?: ReactNode | JSX.Element | HTMLElement;
|
|
12
12
|
onValidate?: (value: any) => void;
|
|
13
|
+
uniqueId?: string;
|
|
13
14
|
|
|
14
15
|
// aditional props
|
|
15
16
|
[key: string]: any;
|
|
@@ -23,6 +24,7 @@ export interface IFieldData {
|
|
|
23
24
|
groupLabel?: string | JSX.Element | HTMLElement;
|
|
24
25
|
groupLabelClassName?: string;
|
|
25
26
|
id: number;
|
|
27
|
+
uniqueId?: string;
|
|
26
28
|
}
|
|
27
29
|
export interface IBillingInfoData {
|
|
28
30
|
id: number | string;
|
|
@@ -31,4 +33,5 @@ export interface IBillingInfoData {
|
|
|
31
33
|
// optional
|
|
32
34
|
label?: string | JSX.Element;
|
|
33
35
|
labelClassName?: string;
|
|
36
|
+
uniqueId?: string;
|
|
34
37
|
}
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
import { connect, FormikContextType } from 'formik'
|
|
2
|
+
import { Component } from 'react'
|
|
3
|
+
|
|
4
|
+
interface IProps {
|
|
5
|
+
formik: FormikContextType<any>;
|
|
6
|
+
}
|
|
7
|
+
|
|
8
|
+
class ErrorFocusInternal extends Component<IProps> {
|
|
9
|
+
public componentDidUpdate(prevProps: IProps) {
|
|
10
|
+
const { isSubmitting, isValidating, errors } = prevProps.formik
|
|
11
|
+
const keys = Object.keys(errors)
|
|
12
|
+
if (keys.length > 0 && isSubmitting && !isValidating) {
|
|
13
|
+
const selector = `[name="${keys[0]}"]`
|
|
14
|
+
const errorElement = document.querySelector(selector) as HTMLElement
|
|
15
|
+
if (errorElement) {
|
|
16
|
+
errorElement.focus()
|
|
17
|
+
}
|
|
18
|
+
}
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
public render = () => null;
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
export const ErrorFocus = connect<{}>(ErrorFocusInternal)
|