tf-checkout-react 1.0.48 → 1.0.52
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 +11 -1
- package/dist/components/common/CustomField.d.ts +1 -1
- 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 +289 -133
- 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 +297 -141
- 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/components/billing-info-container/index.tsx +89 -58
- package/src/components/billing-info-container/style.css +4 -0
- package/src/components/billing-info-container/utils.ts +53 -1
- package/src/components/common/CustomField.tsx +5 -4
- 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
|
@@ -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,
|
|
@@ -165,8 +168,6 @@ const LogicRunner: FC<{
|
|
|
165
168
|
return null
|
|
166
169
|
}
|
|
167
170
|
|
|
168
|
-
const SectionContainer: FC = ({ children }) => <>{children}</>
|
|
169
|
-
|
|
170
171
|
export const BillingInfoContainer = ({
|
|
171
172
|
data = [],
|
|
172
173
|
ticketHoldersFields = {
|
|
@@ -201,6 +202,10 @@ export const BillingInfoContainer = ({
|
|
|
201
202
|
? window.localStorage.getItem('access_token') || ''
|
|
202
203
|
: ''
|
|
203
204
|
|
|
205
|
+
const [dataWithUniqueIds, setDataWithUniqueIds] = useState<
|
|
206
|
+
IBillingInfoData[]
|
|
207
|
+
>(data)
|
|
208
|
+
|
|
204
209
|
const [cartInfoData, setCartInfo] = useState<any>({})
|
|
205
210
|
const [countries, setCountries] = useState<any>([])
|
|
206
211
|
const [states, setStates] = useState<any>([])
|
|
@@ -236,7 +241,11 @@ export const BillingInfoContainer = ({
|
|
|
236
241
|
const showTicketHolderName = getQueryVariable('names_required') === 'true'
|
|
237
242
|
const eventId = getQueryVariable('event_id')
|
|
238
243
|
const optedInFieldValue: boolean = _get(cartInfoData, 'optedIn', false)
|
|
239
|
-
const
|
|
244
|
+
const hideTtfOptIn: boolean = _get(cartInfoData, 'hide_ttf_opt_in', true)
|
|
245
|
+
|
|
246
|
+
if (!_get(dataWithUniqueIds, '[0].uniqueId')) {
|
|
247
|
+
setDataWithUniqueIds(assingUniqueIds(data))
|
|
248
|
+
}
|
|
240
249
|
|
|
241
250
|
const getQuantity = (cart: any = []) => {
|
|
242
251
|
let qty: any = 0
|
|
@@ -278,7 +287,9 @@ export const BillingInfoContainer = ({
|
|
|
278
287
|
const cartInfo = _get(res, 'data.data.attributes')
|
|
279
288
|
setCartInfo(cartInfo)
|
|
280
289
|
const { cart = [] } = cartInfo
|
|
281
|
-
setTicketsQuantity(
|
|
290
|
+
setTicketsQuantity(
|
|
291
|
+
new Array(getQuantity(cart)).fill(null).map(() => nanoid())
|
|
292
|
+
)
|
|
282
293
|
onGetCartSuccess(res.data)
|
|
283
294
|
} catch (e) {
|
|
284
295
|
if (axios.isAxiosError(e)) {
|
|
@@ -316,7 +327,7 @@ export const BillingInfoContainer = ({
|
|
|
316
327
|
<>
|
|
317
328
|
<Formik
|
|
318
329
|
initialValues={getInitialValues(
|
|
319
|
-
|
|
330
|
+
dataWithUniqueIds,
|
|
320
331
|
{
|
|
321
332
|
...initialValues,
|
|
322
333
|
email: emailLogged,
|
|
@@ -349,11 +360,22 @@ export const BillingInfoContainer = ({
|
|
|
349
360
|
showDOB
|
|
350
361
|
)
|
|
351
362
|
const res = await postOnCheckout(checkoutBody, access_token)
|
|
352
|
-
handleSubmit(
|
|
363
|
+
handleSubmit(
|
|
364
|
+
values,
|
|
365
|
+
formikHelpers as FormikHelpers<any>,
|
|
366
|
+
eventId,
|
|
367
|
+
res
|
|
368
|
+
)
|
|
353
369
|
return
|
|
354
370
|
}
|
|
355
371
|
|
|
356
|
-
const
|
|
372
|
+
const checkoutBodyForRegistration = createCheckoutDataBody(
|
|
373
|
+
ticketsQuantity.length,
|
|
374
|
+
values,
|
|
375
|
+
{ emailLogged, firstNameLogged, lastNameLogged },
|
|
376
|
+
showDOB
|
|
377
|
+
)
|
|
378
|
+
const bodyFormData = createRegisterFormData(values, checkoutBodyForRegistration)
|
|
357
379
|
|
|
358
380
|
let access_token_register = null
|
|
359
381
|
try {
|
|
@@ -418,7 +440,12 @@ export const BillingInfoContainer = ({
|
|
|
418
440
|
checkoutBody,
|
|
419
441
|
access_token_register
|
|
420
442
|
)
|
|
421
|
-
handleSubmit(
|
|
443
|
+
handleSubmit(
|
|
444
|
+
values,
|
|
445
|
+
formikHelpers as FormikHelpers<any>,
|
|
446
|
+
eventId,
|
|
447
|
+
res
|
|
448
|
+
)
|
|
422
449
|
} catch (e) {
|
|
423
450
|
if (axios.isAxiosError(e)) {
|
|
424
451
|
if (e.response?.data.error === 'invalid_token') {
|
|
@@ -436,6 +463,7 @@ export const BillingInfoContainer = ({
|
|
|
436
463
|
>
|
|
437
464
|
{(props: FormikProps<any>) => (
|
|
438
465
|
<Form onSubmit={props.handleSubmit}>
|
|
466
|
+
<ErrorFocus />
|
|
439
467
|
<LogicRunner
|
|
440
468
|
values={props.values}
|
|
441
469
|
setStates={setStates}
|
|
@@ -469,8 +497,8 @@ export const BillingInfoContainer = ({
|
|
|
469
497
|
</div>
|
|
470
498
|
</div>
|
|
471
499
|
)}
|
|
472
|
-
{_map(
|
|
473
|
-
const {
|
|
500
|
+
{_map(dataWithUniqueIds, item => {
|
|
501
|
+
const { label, labelClassName, fields } = item
|
|
474
502
|
if (
|
|
475
503
|
label === 'Ticket Holders' &&
|
|
476
504
|
!showTicketHolderName &&
|
|
@@ -479,7 +507,7 @@ export const BillingInfoContainer = ({
|
|
|
479
507
|
return null
|
|
480
508
|
}
|
|
481
509
|
return (
|
|
482
|
-
<
|
|
510
|
+
<React.Fragment key={item.uniqueId}>
|
|
483
511
|
<p className={labelClassName}>{label}</p>
|
|
484
512
|
{_map(fields, group => {
|
|
485
513
|
const {
|
|
@@ -489,7 +517,7 @@ export const BillingInfoContainer = ({
|
|
|
489
517
|
groupLabelClassName,
|
|
490
518
|
} = group
|
|
491
519
|
return (
|
|
492
|
-
<
|
|
520
|
+
<React.Fragment key={group.uniqueId}>
|
|
493
521
|
{!isLoggedIn && (
|
|
494
522
|
<div className={groupLabelClassName}>
|
|
495
523
|
{groupLabel}
|
|
@@ -513,7 +541,7 @@ export const BillingInfoContainer = ({
|
|
|
513
541
|
if (el.name === 'holderAge' && !showDOB) {
|
|
514
542
|
return false
|
|
515
543
|
}
|
|
516
|
-
if (el.name === '
|
|
544
|
+
if (el.name === 'ttf_opt_in' && hideTtfOptIn) {
|
|
517
545
|
return false
|
|
518
546
|
}
|
|
519
547
|
return true
|
|
@@ -522,52 +550,56 @@ export const BillingInfoContainer = ({
|
|
|
522
550
|
['password', 'confirmPassword'].includes(
|
|
523
551
|
element.name
|
|
524
552
|
) && isLoggedIn ? null : (
|
|
525
|
-
<
|
|
526
|
-
|
|
527
|
-
|
|
528
|
-
|
|
529
|
-
|
|
530
|
-
|
|
531
|
-
|
|
532
|
-
|
|
533
|
-
|
|
534
|
-
|
|
535
|
-
|
|
536
|
-
|
|
537
|
-
|
|
538
|
-
|
|
539
|
-
|
|
540
|
-
element.
|
|
541
|
-
|
|
542
|
-
|
|
543
|
-
|
|
544
|
-
|
|
545
|
-
|
|
546
|
-
|
|
547
|
-
|
|
548
|
-
|
|
549
|
-
|
|
550
|
-
|
|
551
|
-
|
|
552
|
-
|
|
553
|
-
|
|
554
|
-
|
|
555
|
-
|
|
556
|
-
|
|
557
|
-
|
|
558
|
-
|
|
559
|
-
|
|
553
|
+
<React.Fragment key={element.uniqueId}>
|
|
554
|
+
{element.name === 'email' ? (
|
|
555
|
+
<div className="email-checking">
|
|
556
|
+
{`IMPORTANT: Please double check that your
|
|
557
|
+
email address is correct. It's where we
|
|
558
|
+
send your confirmation and e-tickets to!`}
|
|
559
|
+
</div>
|
|
560
|
+
) : null}
|
|
561
|
+
<div className={element.className}>
|
|
562
|
+
{element.component ? (
|
|
563
|
+
element.component
|
|
564
|
+
) : (
|
|
565
|
+
<Field
|
|
566
|
+
name={element.name}
|
|
567
|
+
label={element.label}
|
|
568
|
+
type={element.type}
|
|
569
|
+
validate={getValidateFunctions(
|
|
570
|
+
element,
|
|
571
|
+
states
|
|
572
|
+
)}
|
|
573
|
+
component={
|
|
574
|
+
element.type === 'checkbox'
|
|
575
|
+
? CheckboxField
|
|
576
|
+
: element.type === 'select'
|
|
577
|
+
? SelectField
|
|
578
|
+
: CustomField
|
|
579
|
+
}
|
|
580
|
+
selectOptions={
|
|
581
|
+
element.name === 'country'
|
|
582
|
+
? countries
|
|
583
|
+
: element.name === 'state'
|
|
584
|
+
? states
|
|
585
|
+
: []
|
|
586
|
+
}
|
|
587
|
+
theme={theme}
|
|
588
|
+
/>
|
|
589
|
+
)}
|
|
590
|
+
</div>
|
|
591
|
+
</React.Fragment>
|
|
560
592
|
)
|
|
561
593
|
)}
|
|
562
594
|
</div>
|
|
563
|
-
</
|
|
595
|
+
</React.Fragment>
|
|
564
596
|
)
|
|
565
597
|
})}
|
|
566
|
-
</
|
|
598
|
+
</React.Fragment>
|
|
567
599
|
)
|
|
568
600
|
})}
|
|
569
601
|
{showTicketHolderName && (
|
|
570
|
-
<
|
|
602
|
+
<React.Fragment>
|
|
571
603
|
<div className="ticket-holders-fields">
|
|
572
604
|
<p>{ticketHoldersFields.label}</p>
|
|
573
605
|
{_map(ticketsQuantity, (_item, index) => (
|
|
@@ -576,16 +608,15 @@ export const BillingInfoContainer = ({
|
|
|
576
608
|
{_map(ticketHoldersFields.fields, group => {
|
|
577
609
|
const { groupClassname, groupItems } = group
|
|
578
610
|
return (
|
|
579
|
-
<div key={group.
|
|
611
|
+
<div key={group.uniqueId}>
|
|
580
612
|
<div className={groupClassname}>
|
|
581
613
|
{_map(groupItems, element => (
|
|
582
614
|
<div
|
|
583
615
|
className={element.className}
|
|
584
|
-
key={
|
|
616
|
+
key={element.uniqueId}
|
|
585
617
|
>
|
|
586
618
|
<Field
|
|
587
619
|
name={`${element.name}-${index}`}
|
|
588
|
-
key={`${element.name}-${index}`}
|
|
589
620
|
label={element.label}
|
|
590
621
|
type={element.type}
|
|
591
622
|
required={true}
|
|
@@ -612,7 +643,7 @@ export const BillingInfoContainer = ({
|
|
|
612
643
|
</div>
|
|
613
644
|
))}
|
|
614
645
|
</div>
|
|
615
|
-
</
|
|
646
|
+
</React.Fragment>
|
|
616
647
|
)}
|
|
617
648
|
<div className="button-container">
|
|
618
649
|
<LoadingButton
|
|
@@ -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;
|
|
@@ -31,7 +36,10 @@ export const getInitialValues = (
|
|
|
31
36
|
return initialValues
|
|
32
37
|
}
|
|
33
38
|
|
|
34
|
-
export const createRegisterFormData = (
|
|
39
|
+
export const createRegisterFormData = (
|
|
40
|
+
values: IValues = {},
|
|
41
|
+
checkoutBody: { attributes: { [key: string]: any } }
|
|
42
|
+
): FormData => {
|
|
35
43
|
const bodyFormData = new FormData()
|
|
36
44
|
bodyFormData.append('first_name', values.firstName)
|
|
37
45
|
bodyFormData.append('last_name', values.lastName)
|
|
@@ -46,6 +54,11 @@ export const createRegisterFormData = (values: IValues = {}): FormData => {
|
|
|
46
54
|
'client_secret',
|
|
47
55
|
ENV.CLIENT_SECRET || 'b89c191eff22fdcf84ac9bfd88d005355a151ec2c83b26b9'
|
|
48
56
|
)
|
|
57
|
+
|
|
58
|
+
_forEach(checkoutBody.attributes, (item: any, key: string) => {
|
|
59
|
+
bodyFormData.append(key, item)
|
|
60
|
+
})
|
|
61
|
+
|
|
49
62
|
return bodyFormData
|
|
50
63
|
}
|
|
51
64
|
|
|
@@ -81,6 +94,7 @@ export const setLoggedUserData = (data: IUserData) => ({
|
|
|
81
94
|
first_name: data.firstName,
|
|
82
95
|
last_name: data.lastName,
|
|
83
96
|
email: data.email,
|
|
97
|
+
confirmEmail: data.email,
|
|
84
98
|
city: data?.city || '',
|
|
85
99
|
country: data?.country || '',
|
|
86
100
|
phone: data?.phone || '',
|
|
@@ -150,3 +164,41 @@ export const createCheckoutDataBody = (
|
|
|
150
164
|
}
|
|
151
165
|
return body
|
|
152
166
|
}
|
|
167
|
+
|
|
168
|
+
export const getValidateFunctions = (
|
|
169
|
+
element: IGroupItem,
|
|
170
|
+
states: Array<{ [key: string]: any }>
|
|
171
|
+
) => {
|
|
172
|
+
const validationFunctions: any[] = []
|
|
173
|
+
|
|
174
|
+
if (element.required) {
|
|
175
|
+
if (
|
|
176
|
+
element.name !== 'state' ||
|
|
177
|
+
(element.name === 'state' && states.length)
|
|
178
|
+
) {
|
|
179
|
+
validationFunctions.push(requiredValidator)
|
|
180
|
+
}
|
|
181
|
+
}
|
|
182
|
+
|
|
183
|
+
if (element.onValidate) {
|
|
184
|
+
validationFunctions.push(element.onValidate)
|
|
185
|
+
}
|
|
186
|
+
|
|
187
|
+
return combineValidators(...validationFunctions)
|
|
188
|
+
}
|
|
189
|
+
|
|
190
|
+
export const assingUniqueIds = (data: any): any => {
|
|
191
|
+
if (_get(data[0], 'uniqueId')) {
|
|
192
|
+
return data
|
|
193
|
+
}
|
|
194
|
+
|
|
195
|
+
return _map(data, (item: any) => {
|
|
196
|
+
_forEach(item, (itemValue: string, key) => {
|
|
197
|
+
if (_isArray(itemValue)) {
|
|
198
|
+
item[key] = assingUniqueIds(itemValue)
|
|
199
|
+
}
|
|
200
|
+
})
|
|
201
|
+
|
|
202
|
+
return { ...item, uniqueId: nanoid() }
|
|
203
|
+
})
|
|
204
|
+
}
|
|
@@ -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'
|
|
@@ -41,13 +42,14 @@ export const CustomField = ({
|
|
|
41
42
|
type = 'text',
|
|
42
43
|
field,
|
|
43
44
|
selectOptions = [] as ISelectOption[],
|
|
44
|
-
form: { touched, errors },
|
|
45
|
+
form: { touched, errors, submitCount },
|
|
45
46
|
theme,
|
|
46
47
|
}: ICustomField & IOtherProps) => {
|
|
47
48
|
const isSelectField = type === 'select'
|
|
48
|
-
const isShrink = Boolean(field.value) || type === 'date' || type === 'select'
|
|
49
|
-
const isTouched = Boolean(_get(touched, field.name))
|
|
50
49
|
const error = _get(errors, field.name)
|
|
50
|
+
const isTouched =
|
|
51
|
+
Boolean(_get(touched, field.name)) ||
|
|
52
|
+
(_includes(field.name, 'holder') && !!error && !!submitCount)
|
|
51
53
|
const classes = useStyles()
|
|
52
54
|
|
|
53
55
|
return (
|
|
@@ -59,7 +61,6 @@ export const CustomField = ({
|
|
|
59
61
|
fullWidth={true}
|
|
60
62
|
error={!!error && isTouched}
|
|
61
63
|
helperText={isTouched && error}
|
|
62
|
-
InputLabelProps={{ shrink: isShrink }}
|
|
63
64
|
InputProps={{
|
|
64
65
|
classes: {
|
|
65
66
|
input: classes.input,
|
|
@@ -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} shrink={true}>
|
|
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)
|