@hashicorp/react-components 1.1.10 → 1.2.0
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/README.md +1 -1
- package/package.json +8 -13
- package/postcss.config.js +1 -1
- package/src/components/nav-panel/components/flight-icon/index.tsx +53 -0
- package/src/components/nav-panel/components/flight-icon/style.module.css +3 -0
- package/src/components/nav-panel/components/link/cloud-fill.svg +3 -0
- package/src/components/nav-panel/components/link/index.tsx +139 -0
- package/src/components/nav-panel/components/link/layers-fill.svg +5 -0
- package/src/components/nav-panel/components/link/shield-alert-fill.svg +3 -0
- package/src/components/nav-panel/components/link/style.module.css +64 -0
- package/src/components/nav-panel/components/link/zap-fill.svg +3 -0
- package/src/components/nav-panel/components/link-list/index.tsx +12 -64
- package/src/components/nav-panel/components/link-list/style.module.css +1 -50
- package/src/components/nav-panel/components/panel/column/style.module.css +2 -2
- package/src/components/nav-panel/components/panel/root/style.module.css +5 -5
- package/src/components/nav-panel/components/promo/index.tsx +35 -15
- package/src/components/nav-panel/components/promo/style.module.css +32 -28
- package/src/components/nav-panel/components/text/index.tsx +34 -0
- package/src/components/nav-panel/product-panel/index.tsx +71 -20
- package/src/components/nav-panel/product-panel/style.module.css +111 -53
- package/src/components/nav-panel/standard-panel/index.tsx +3 -0
- package/src/components/nav-panel/standard-panel/style.module.css +2 -2
- package/src/lib/get-localized-link-props/index.test.ts +30 -0
- package/src/lib/get-localized-link-props/index.ts +45 -0
- package/src/lib/parse-url/index.test.ts +57 -2
- package/src/lib/parse-url/index.ts +97 -13
- package/src/lib/send-posthog-event/index.ts +22 -0
- package/src/lib/supported-locales/index.ts +10 -0
- package/src/lib/track-nav-click-event/index.ts +5 -5
- package/src/lib/v/index.ts +9 -4
- package/tsconfig.json +1 -2
- package/.eslintrc.js +0 -4
- package/.stylelintrc.js +0 -3
- package/src/components/actions/index.tsx +0 -107
- package/src/components/actions/style.module.css +0 -28
- package/src/components/actions/types.ts +0 -73
- package/src/components/call-to-action/index.tsx +0 -205
- package/src/components/call-to-action/styles/variant-centered.module.css +0 -93
- package/src/components/call-to-action/styles/variant-compact-grid.module.css +0 -88
- package/src/components/call-to-action/styles/variant-compact.module.css +0 -91
- package/src/components/call-to-action/styles/variant-links.module.css +0 -85
- package/src/components/card/README.md +0 -11
- package/src/components/card/card.tsx +0 -45
- package/src/components/card/index.test.tsx +0 -69
- package/src/components/card/index.tsx +0 -14
- package/src/components/card/newsroom-card/index.test.tsx +0 -39
- package/src/components/card/newsroom-card/index.tsx +0 -37
- package/src/components/card/newsroom-card/style.module.css +0 -12
- package/src/components/card/partner-card/index.test.tsx +0 -57
- package/src/components/card/partner-card/index.tsx +0 -69
- package/src/components/card/partner-card/style.module.css +0 -24
- package/src/components/card/person-card/index.test.tsx +0 -71
- package/src/components/card/person-card/index.tsx +0 -93
- package/src/components/card/person-card/style.module.css +0 -33
- package/src/components/card/primitives.tsx +0 -176
- package/src/components/card/promo-card/index.tsx +0 -51
- package/src/components/card/resource-card/index.test.tsx +0 -48
- package/src/components/card/resource-card/index.tsx +0 -37
- package/src/components/card/style.module.css +0 -145
- package/src/components/card/types.ts +0 -66
- package/src/components/card/unified-card/index.tsx +0 -64
- package/src/components/consent-manager/README.md +0 -73
- package/src/components/consent-manager/components/banner/banner.module.css +0 -23
- package/src/components/consent-manager/components/banner/banner.test.tsx +0 -31
- package/src/components/consent-manager/components/banner/banner.tsx +0 -73
- package/src/components/consent-manager/components/dialog/dialog.module.css +0 -145
- package/src/components/consent-manager/components/dialog/dialog.test.tsx +0 -194
- package/src/components/consent-manager/components/dialog/dialog.tsx +0 -339
- package/src/components/consent-manager/img/icn_close.tsx +0 -19
- package/src/components/consent-manager/index.test.tsx +0 -334
- package/src/components/consent-manager/index.tsx +0 -186
- package/src/components/consent-manager/loader/README.md +0 -29
- package/src/components/consent-manager/loader/categories.ts +0 -22
- package/src/components/consent-manager/loader/index.tsx +0 -94
- package/src/components/consent-manager/loader/presets/enterprise.ts +0 -12
- package/src/components/consent-manager/loader/presets/oss.ts +0 -10
- package/src/components/consent-manager/loader/services.ts +0 -45
- package/src/components/consent-manager/scripts/custom.tsx +0 -59
- package/src/components/consent-manager/scripts/segment.tsx +0 -87
- package/src/components/consent-manager/style.module.css +0 -9
- package/src/components/consent-manager/types.ts +0 -55
- package/src/components/consent-manager/util/cookies.test.ts +0 -75
- package/src/components/consent-manager/util/cookies.ts +0 -70
- package/src/components/consent-manager/util/integrations.test.ts +0 -115
- package/src/components/consent-manager/util/integrations.ts +0 -110
- package/src/components/intro/index.tsx +0 -82
- package/src/components/intro/style.module.css +0 -67
- package/src/components/intro/types.ts +0 -44
- package/src/components/marketo-form/CHANGELOG.md +0 -122
- package/src/components/marketo-form/README.md +0 -1
- package/src/components/marketo-form/contexts/FormMetadata.ts +0 -11
- package/src/components/marketo-form/docs.mdx +0 -197
- package/src/components/marketo-form/fixtures/index.ts +0 -185
- package/src/components/marketo-form/form/index.tsx +0 -369
- package/src/components/marketo-form/index.test.tsx +0 -320
- package/src/components/marketo-form/index.ts +0 -11
- package/src/components/marketo-form/package.original.json +0 -30
- package/src/components/marketo-form/partials/error-message/index.tsx +0 -12
- package/src/components/marketo-form/partials/error-message/style.module.css +0 -13
- package/src/components/marketo-form/partials/field/index.tsx +0 -90
- package/src/components/marketo-form/partials/field-wrapper/index.tsx +0 -26
- package/src/components/marketo-form/partials/fields/checkbox-field/index.tsx +0 -32
- package/src/components/marketo-form/partials/fields/email-field/index.tsx +0 -35
- package/src/components/marketo-form/partials/fields/form-page-url-field/index.tsx +0 -21
- package/src/components/marketo-form/partials/fields/hidden-field/index.tsx +0 -37
- package/src/components/marketo-form/partials/fields/htmltext-field/index.tsx +0 -21
- package/src/components/marketo-form/partials/fields/htmltext-field/style.module.css +0 -18
- package/src/components/marketo-form/partials/fields/name-field/index.tsx +0 -19
- package/src/components/marketo-form/partials/fields/name-field/style.module.css +0 -13
- package/src/components/marketo-form/partials/fields/radio-buttons-field/index.tsx +0 -48
- package/src/components/marketo-form/partials/fields/select-field/index.tsx +0 -39
- package/src/components/marketo-form/partials/fields/select-field/style.module.css +0 -5
- package/src/components/marketo-form/partials/fields/telephone-field/index.tsx +0 -30
- package/src/components/marketo-form/partials/fields/text-field/index.tsx +0 -30
- package/src/components/marketo-form/partials/fields/textarea-field/index.tsx +0 -29
- package/src/components/marketo-form/partials/label/index.tsx +0 -24
- package/src/components/marketo-form/partials/label/style.module.css +0 -12
- package/src/components/marketo-form/partials/visibility-rule/index.tsx +0 -84
- package/src/components/marketo-form/props.js +0 -66
- package/src/components/marketo-form/server/api-routes.ts +0 -109
- package/src/components/marketo-form/server/client.ts +0 -98
- package/src/components/marketo-form/server/index.ts +0 -8
- package/src/components/marketo-form/types.ts +0 -268
- package/src/components/marketo-form/utils.test.tsx +0 -103
- package/src/components/marketo-form/utils.ts +0 -449
- package/src/components/next-steps/index.tsx +0 -137
- package/src/components/next-steps/style.module.css +0 -270
- package/src/components/next-steps/types.ts +0 -63
- package/src/components/product-badge/index.test.tsx +0 -11
- package/src/components/product-badge/index.tsx +0 -42
- package/src/components/product-badge/style.module.css +0 -76
- package/src/components/products-used/index.tsx +0 -84
- package/src/components/products-used/style.module.css +0 -87
- package/src/components/products-used/types.ts +0 -31
- package/src/components/related-content/index.test.tsx +0 -74
- package/src/components/related-content/index.tsx +0 -56
- package/src/components/related-content/style.module.css +0 -89
- package/src/components/related-content/types.ts +0 -21
- package/src/components/subnav/helpers/areBasePathsMatching.ts +0 -13
- package/src/components/subnav/helpers/traverse/index.ts +0 -42
- package/src/components/subnav/helpers/useHeightRef.ts +0 -42
- package/src/components/subnav/helpers/useNavRef.ts +0 -30
- package/src/components/subnav/helpers/useOverflowRef.ts +0 -85
- package/src/components/subnav/helpers/useStuckRef.ts +0 -51
- package/src/components/subnav/index.tsx +0 -199
- package/src/components/subnav/partials/CtaLinks/github-stars-link/formatStarCount/index.test.ts +0 -26
- package/src/components/subnav/partials/CtaLinks/github-stars-link/formatStarCount/index.ts +0 -22
- package/src/components/subnav/partials/CtaLinks/github-stars-link/index.tsx +0 -87
- package/src/components/subnav/partials/CtaLinks/github-stars-link/parseGithubUrl/index.test.ts +0 -29
- package/src/components/subnav/partials/CtaLinks/github-stars-link/parseGithubUrl/index.ts +0 -32
- package/src/components/subnav/partials/CtaLinks/github-stars-link/style.module.css +0 -85
- package/src/components/subnav/partials/CtaLinks/icons/github-logo.js +0 -20
- package/src/components/subnav/partials/CtaLinks/icons/github.svg.js +0 -7
- package/src/components/subnav/partials/CtaLinks/icons/star.js +0 -20
- package/src/components/subnav/partials/CtaLinks/index.tsx +0 -130
- package/src/components/subnav/partials/CtaLinks/style.module.css +0 -45
- package/src/components/subnav/partials/DropdownTrigger/index.tsx +0 -35
- package/src/components/subnav/partials/DropdownTrigger/style.module.css +0 -36
- package/src/components/subnav/partials/MenuItemsDefault/index.tsx +0 -151
- package/src/components/subnav/partials/MenuItemsDefault/style.module.css +0 -91
- package/src/components/subnav/partials/MenuItemsOverflow/index.tsx +0 -139
- package/src/components/subnav/partials/MenuItemsOverflow/style.module.css +0 -82
- package/src/components/subnav/partials/TitleLink/index.tsx +0 -113
- package/src/components/subnav/partials/TitleLink/mktg-logos/all-community-product-logos_on-dark.ts +0 -9
- package/src/components/subnav/partials/TitleLink/mktg-logos/all-community-product-logos_on-light.ts +0 -9
- package/src/components/subnav/partials/TitleLink/mktg-logos/all-primary-logos_on-dark.ts +0 -23
- package/src/components/subnav/partials/TitleLink/mktg-logos/all-primary-logos_on-light.ts +0 -23
- package/src/components/subnav/partials/TitleLink/style.module.css +0 -43
- package/src/components/subnav/partials/nav-item-text/index.tsx +0 -37
- package/src/components/subnav/partials/nav-item-text/style.module.css +0 -15
- package/src/components/subnav/style.module.css +0 -68
- package/src/components/subnav/subnav-consts.ts +0 -1
- package/src/components/subnav/theme.module.css +0 -50
|
@@ -1,185 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Copyright (c) HashiCorp, Inc.
|
|
3
|
-
* SPDX-License-Identifier: MPL-2.0
|
|
4
|
-
*/
|
|
5
|
-
|
|
6
|
-
import type { MarketoFormField } from '../types'
|
|
7
|
-
|
|
8
|
-
const BASIC_FORM_PROPS = {
|
|
9
|
-
metadata: {
|
|
10
|
-
result: [
|
|
11
|
-
{
|
|
12
|
-
id: 9999,
|
|
13
|
-
language: 'English',
|
|
14
|
-
waitingLabel: 'Submitting...',
|
|
15
|
-
buttonLabel: 'Submit',
|
|
16
|
-
},
|
|
17
|
-
],
|
|
18
|
-
},
|
|
19
|
-
fields: {
|
|
20
|
-
result: [
|
|
21
|
-
{
|
|
22
|
-
id: 'FirstName',
|
|
23
|
-
label: 'First Name',
|
|
24
|
-
dataType: 'text',
|
|
25
|
-
validationMessage: 'This field is required.',
|
|
26
|
-
rowNumber: 3,
|
|
27
|
-
columnNumber: 0,
|
|
28
|
-
required: true,
|
|
29
|
-
visibilityRules: {
|
|
30
|
-
ruleType: 'alwaysShow',
|
|
31
|
-
},
|
|
32
|
-
},
|
|
33
|
-
{
|
|
34
|
-
id: 'LastName',
|
|
35
|
-
label: 'Last Name',
|
|
36
|
-
dataType: 'text',
|
|
37
|
-
validationMessage: 'This field is required.',
|
|
38
|
-
rowNumber: 3,
|
|
39
|
-
columnNumber: 0,
|
|
40
|
-
required: true,
|
|
41
|
-
formPrefill: true,
|
|
42
|
-
visibilityRules: {
|
|
43
|
-
ruleType: 'alwaysShow',
|
|
44
|
-
},
|
|
45
|
-
},
|
|
46
|
-
] as MarketoFormField[],
|
|
47
|
-
},
|
|
48
|
-
}
|
|
49
|
-
|
|
50
|
-
const UTM_FORM_PROPS = {
|
|
51
|
-
...BASIC_FORM_PROPS,
|
|
52
|
-
fields: {
|
|
53
|
-
result: [
|
|
54
|
-
...BASIC_FORM_PROPS.fields.result,
|
|
55
|
-
{
|
|
56
|
-
id: 'utm_medium__c',
|
|
57
|
-
label: 'utm_medium:',
|
|
58
|
-
dataType: 'hidden',
|
|
59
|
-
rowNumber: 7,
|
|
60
|
-
columnNumber: 0,
|
|
61
|
-
required: false,
|
|
62
|
-
autoFill: {
|
|
63
|
-
value: '',
|
|
64
|
-
valueFrom: 'query',
|
|
65
|
-
parameterName: 'utm_medium',
|
|
66
|
-
},
|
|
67
|
-
visibilityRules: {
|
|
68
|
-
ruleType: 'alwaysShow',
|
|
69
|
-
},
|
|
70
|
-
},
|
|
71
|
-
{
|
|
72
|
-
id: 'form_page_url__c',
|
|
73
|
-
label: 'form_page_url:',
|
|
74
|
-
dataType: 'hidden',
|
|
75
|
-
rowNumber: 7,
|
|
76
|
-
columnNumber: 0,
|
|
77
|
-
required: false,
|
|
78
|
-
visibilityRules: {
|
|
79
|
-
ruleType: 'alwaysShow',
|
|
80
|
-
},
|
|
81
|
-
},
|
|
82
|
-
] as MarketoFormField[],
|
|
83
|
-
},
|
|
84
|
-
}
|
|
85
|
-
|
|
86
|
-
const COOKIE_UTM_FORM_PROPS = {
|
|
87
|
-
...BASIC_FORM_PROPS,
|
|
88
|
-
fields: {
|
|
89
|
-
result: [
|
|
90
|
-
...BASIC_FORM_PROPS.fields.result,
|
|
91
|
-
{
|
|
92
|
-
id: 'utm_content__c',
|
|
93
|
-
label: 'utm_content:',
|
|
94
|
-
dataType: 'hidden',
|
|
95
|
-
rowNumber: 7,
|
|
96
|
-
columnNumber: 0,
|
|
97
|
-
required: false,
|
|
98
|
-
autoFill: {
|
|
99
|
-
value: '',
|
|
100
|
-
valueFrom: 'query',
|
|
101
|
-
parameterName: 'utm_content',
|
|
102
|
-
},
|
|
103
|
-
visibilityRules: {
|
|
104
|
-
ruleType: 'alwaysShow',
|
|
105
|
-
},
|
|
106
|
-
},
|
|
107
|
-
] as MarketoFormField[],
|
|
108
|
-
},
|
|
109
|
-
}
|
|
110
|
-
|
|
111
|
-
const VISIBILITY_RULE_FORM_PROPS = {
|
|
112
|
-
...BASIC_FORM_PROPS,
|
|
113
|
-
fields: {
|
|
114
|
-
result: [
|
|
115
|
-
...BASIC_FORM_PROPS.fields.result,
|
|
116
|
-
{
|
|
117
|
-
id: 'Phone',
|
|
118
|
-
label: 'Phone Number',
|
|
119
|
-
dataType: 'telephone',
|
|
120
|
-
validationMessage: 'Must be a phone number.',
|
|
121
|
-
rowNumber: 6,
|
|
122
|
-
columnNumber: 0,
|
|
123
|
-
required: false,
|
|
124
|
-
formPrefill: true,
|
|
125
|
-
visibilityRules: {
|
|
126
|
-
rules: [
|
|
127
|
-
{
|
|
128
|
-
subjectField: 'fastTracktoSales',
|
|
129
|
-
operator: 'is',
|
|
130
|
-
values: ['yes'],
|
|
131
|
-
altLabel: 'Phone Number:',
|
|
132
|
-
},
|
|
133
|
-
],
|
|
134
|
-
ruleType: 'show',
|
|
135
|
-
},
|
|
136
|
-
},
|
|
137
|
-
{
|
|
138
|
-
id: 'fastTracktoSales',
|
|
139
|
-
label: 'Have salesperson contact me',
|
|
140
|
-
dataType: 'checkbox',
|
|
141
|
-
validationMessage: 'This field is required.',
|
|
142
|
-
rowNumber: 7,
|
|
143
|
-
columnNumber: 0,
|
|
144
|
-
required: false,
|
|
145
|
-
formPrefill: true,
|
|
146
|
-
fieldMetaData: {
|
|
147
|
-
initiallyChecked: false,
|
|
148
|
-
},
|
|
149
|
-
visibilityRules: {
|
|
150
|
-
ruleType: 'alwaysShow',
|
|
151
|
-
},
|
|
152
|
-
},
|
|
153
|
-
] as MarketoFormField[],
|
|
154
|
-
},
|
|
155
|
-
}
|
|
156
|
-
|
|
157
|
-
const HIDDEN_COOKIE_FIELD_FORM_PROPS = {
|
|
158
|
-
...BASIC_FORM_PROPS,
|
|
159
|
-
fields: {
|
|
160
|
-
result: [
|
|
161
|
-
...BASIC_FORM_PROPS.fields.result,
|
|
162
|
-
{
|
|
163
|
-
id: 'cookieHiddenField',
|
|
164
|
-
label: 'Cookie Value',
|
|
165
|
-
dataType: 'hidden',
|
|
166
|
-
rowNumber: 20,
|
|
167
|
-
columnNumber: 0,
|
|
168
|
-
required: false,
|
|
169
|
-
autoFill: {
|
|
170
|
-
value: '',
|
|
171
|
-
valueFrom: 'cookie',
|
|
172
|
-
parameterName: 'hidden_cookie_field',
|
|
173
|
-
},
|
|
174
|
-
},
|
|
175
|
-
] as MarketoFormField[],
|
|
176
|
-
},
|
|
177
|
-
}
|
|
178
|
-
|
|
179
|
-
export {
|
|
180
|
-
BASIC_FORM_PROPS,
|
|
181
|
-
UTM_FORM_PROPS,
|
|
182
|
-
VISIBILITY_RULE_FORM_PROPS,
|
|
183
|
-
COOKIE_UTM_FORM_PROPS,
|
|
184
|
-
HIDDEN_COOKIE_FIELD_FORM_PROPS,
|
|
185
|
-
}
|
|
@@ -1,369 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Copyright (c) HashiCorp, Inc.
|
|
3
|
-
* SPDX-License-Identifier: MPL-2.0
|
|
4
|
-
*/
|
|
5
|
-
|
|
6
|
-
import { useEffect, useMemo, useRef } from 'react'
|
|
7
|
-
import { useForm, FormProvider } from 'react-hook-form'
|
|
8
|
-
import Button from '@hashicorp/react-design-system-components/src/components/button'
|
|
9
|
-
import VisibilityRule from '../partials/visibility-rule'
|
|
10
|
-
import NameField from '../partials/fields/name-field'
|
|
11
|
-
import { FormMetadataContext } from '../contexts/FormMetadata'
|
|
12
|
-
import {
|
|
13
|
-
convertToRESTFields,
|
|
14
|
-
groupFields,
|
|
15
|
-
calculateDefaultValues,
|
|
16
|
-
segmentIdentify,
|
|
17
|
-
includesSkippedRecords,
|
|
18
|
-
STORED_FIELD_NAMES,
|
|
19
|
-
} from '../utils'
|
|
20
|
-
import type {
|
|
21
|
-
MarketoFormMetadataResponse,
|
|
22
|
-
MarketoFormFieldsResponse,
|
|
23
|
-
MarketoFormGroups,
|
|
24
|
-
MarketoFormComponents,
|
|
25
|
-
MarketoSubmissionResponse,
|
|
26
|
-
} from '../types'
|
|
27
|
-
|
|
28
|
-
interface Props {
|
|
29
|
-
/**
|
|
30
|
-
* The Marketo API response containing the metadata of the form.
|
|
31
|
-
* See {@link MarketoFormMetadataResponse} for more details.
|
|
32
|
-
*/
|
|
33
|
-
metadata: MarketoFormMetadataResponse
|
|
34
|
-
|
|
35
|
-
/**
|
|
36
|
-
* The Marketo API response containing the fields to render.
|
|
37
|
-
* See {@link MarketoFormFieldsResponse} for more details.
|
|
38
|
-
*/
|
|
39
|
-
fields: MarketoFormFieldsResponse
|
|
40
|
-
|
|
41
|
-
/**
|
|
42
|
-
* Configuration on which fields to render with a single component.
|
|
43
|
-
* See {@link MarketoFormGroups} for more details.
|
|
44
|
-
*/
|
|
45
|
-
groups?: MarketoFormGroups
|
|
46
|
-
|
|
47
|
-
/**
|
|
48
|
-
* Initial values to use for fields, primarily useful in setting values for
|
|
49
|
-
* hidden fields that don't derive their values from URL parameters. Keys are
|
|
50
|
-
* SOAP field names.
|
|
51
|
-
*/
|
|
52
|
-
initialValues?: Record<string, any>
|
|
53
|
-
|
|
54
|
-
/**
|
|
55
|
-
* Custom components to be used in lieu of the default built-in components.
|
|
56
|
-
* See {@link MarketoFormComponents} for more details.
|
|
57
|
-
*/
|
|
58
|
-
components?: MarketoFormComponents
|
|
59
|
-
|
|
60
|
-
/**
|
|
61
|
-
* Text to use for the submit button. Defaults to "Submit".
|
|
62
|
-
*/
|
|
63
|
-
submitTitle?: string
|
|
64
|
-
|
|
65
|
-
/**
|
|
66
|
-
* Additional class name to apply to the <form> element.
|
|
67
|
-
*/
|
|
68
|
-
className?: string
|
|
69
|
-
|
|
70
|
-
/**
|
|
71
|
-
* Whether to reset the form after a successful submission.
|
|
72
|
-
* Default: false
|
|
73
|
-
*/
|
|
74
|
-
resetOnSubmission?: boolean
|
|
75
|
-
|
|
76
|
-
/**
|
|
77
|
-
* Callback called when form has started submission process.
|
|
78
|
-
*/
|
|
79
|
-
onSubmitStart?: () => void
|
|
80
|
-
|
|
81
|
-
/**
|
|
82
|
-
* Callback called when form has been successfully submitted.
|
|
83
|
-
*/
|
|
84
|
-
onSubmitSuccess?: () => void
|
|
85
|
-
|
|
86
|
-
/**
|
|
87
|
-
* Callback called when form submission results in an error.
|
|
88
|
-
*/
|
|
89
|
-
onSubmitError?: () => void
|
|
90
|
-
|
|
91
|
-
/**
|
|
92
|
-
* Callback called after performing validation based on Marketo settings, but
|
|
93
|
-
* before rendering error messages. Used to perform validations that aren't
|
|
94
|
-
* supported by the Marketo API (such as checking if an email address is
|
|
95
|
-
* correctly formatted).
|
|
96
|
-
* @param values Map of field IDs to field values
|
|
97
|
-
* @param errors Map of field IDs to error messages
|
|
98
|
-
*/
|
|
99
|
-
validateFields?: (
|
|
100
|
-
values: Record<string, string | boolean>,
|
|
101
|
-
errors: Record<string, { message: string }>
|
|
102
|
-
) => Promise<{ values: any; errors: Record<string, { message: string }> }>
|
|
103
|
-
|
|
104
|
-
/**
|
|
105
|
-
* If false, the form will not persist field values in local storage.
|
|
106
|
-
* Default: true
|
|
107
|
-
*/
|
|
108
|
-
persistFieldValues?: boolean
|
|
109
|
-
|
|
110
|
-
/**
|
|
111
|
-
* Optional name of form to use for the forms aria-label.
|
|
112
|
-
*/
|
|
113
|
-
formName?: string
|
|
114
|
-
}
|
|
115
|
-
|
|
116
|
-
const defaultFieldGroupings = {
|
|
117
|
-
name: {
|
|
118
|
-
fields: ['FirstName', 'LastName'],
|
|
119
|
-
component: NameField,
|
|
120
|
-
},
|
|
121
|
-
}
|
|
122
|
-
|
|
123
|
-
function isValidEmail(value: string): boolean {
|
|
124
|
-
// Source: https://html.spec.whatwg.org/multipage/input.html#email-state-(type=email)
|
|
125
|
-
// Slightly modified to require domains instead of bare TLDs
|
|
126
|
-
/* eslint-disable-next-line no-useless-escape */
|
|
127
|
-
return /^[a-zA-Z0-9.!#$%&'*+\/=?^_`{|}~-]+@[a-zA-Z0-9](?:[a-zA-Z0-9-]{0,61}[a-zA-Z0-9])?(?:\.[a-zA-Z0-9](?:[a-zA-Z0-9-]{0,61}[a-zA-Z0-9])?)+$/i.test(
|
|
128
|
-
value
|
|
129
|
-
)
|
|
130
|
-
}
|
|
131
|
-
|
|
132
|
-
const Form = ({
|
|
133
|
-
metadata,
|
|
134
|
-
fields,
|
|
135
|
-
groups = defaultFieldGroupings,
|
|
136
|
-
initialValues,
|
|
137
|
-
components,
|
|
138
|
-
submitTitle,
|
|
139
|
-
className,
|
|
140
|
-
resetOnSubmission,
|
|
141
|
-
onSubmitStart,
|
|
142
|
-
onSubmitSuccess,
|
|
143
|
-
onSubmitError,
|
|
144
|
-
validateFields,
|
|
145
|
-
persistFieldValues = true,
|
|
146
|
-
formName,
|
|
147
|
-
}: Props) => {
|
|
148
|
-
// Track if the form has been rendered at least once, so that we know if
|
|
149
|
-
// react-hook-form has already cached values.
|
|
150
|
-
const hasBeenRendered = useRef(false)
|
|
151
|
-
|
|
152
|
-
// memoized function that groups marketo fields based on supplied groups.
|
|
153
|
-
// If a field doesn't belong to a group, it is placed in a group keyed by
|
|
154
|
-
// field.Name.
|
|
155
|
-
const groupedFields = useMemo(() => {
|
|
156
|
-
if (!('result' in fields)) {
|
|
157
|
-
return {}
|
|
158
|
-
}
|
|
159
|
-
return groupFields(fields.result, groups)
|
|
160
|
-
}, [fields, groups])
|
|
161
|
-
|
|
162
|
-
const methods = useForm({
|
|
163
|
-
mode: 'onBlur',
|
|
164
|
-
defaultValues:
|
|
165
|
-
'result' in fields
|
|
166
|
-
? calculateDefaultValues(fields.result, initialValues)
|
|
167
|
-
: {},
|
|
168
|
-
resolver: async (values: Record<string, string | boolean>) => {
|
|
169
|
-
let errors: Record<string, { message: string }> = {}
|
|
170
|
-
|
|
171
|
-
fields.result.forEach((field) => {
|
|
172
|
-
if ('required' in field && field.required) {
|
|
173
|
-
if (
|
|
174
|
-
!(field.id in values) ||
|
|
175
|
-
values[field.id] === '' ||
|
|
176
|
-
values[field.id] === false ||
|
|
177
|
-
values[field.id] === undefined ||
|
|
178
|
-
values[field.id] === null ||
|
|
179
|
-
(typeof values[field.id] === 'string' &&
|
|
180
|
-
(values[field.id] as string).trim() === '')
|
|
181
|
-
) {
|
|
182
|
-
errors[field.id] = {
|
|
183
|
-
message: field.validationMessage ?? 'This field is required.',
|
|
184
|
-
}
|
|
185
|
-
}
|
|
186
|
-
}
|
|
187
|
-
|
|
188
|
-
// Validate that select field value is an allowed value
|
|
189
|
-
if (field.dataType === 'select') {
|
|
190
|
-
if (
|
|
191
|
-
field.id in values &&
|
|
192
|
-
field.fieldMetaData.values &&
|
|
193
|
-
!field.fieldMetaData.values
|
|
194
|
-
.map((v) => v.value)
|
|
195
|
-
.includes(values[field.id] as string)
|
|
196
|
-
) {
|
|
197
|
-
errors[field.id] = {
|
|
198
|
-
message: field.validationMessage ?? 'Please select an option.',
|
|
199
|
-
}
|
|
200
|
-
}
|
|
201
|
-
}
|
|
202
|
-
|
|
203
|
-
if (field.dataType === 'email') {
|
|
204
|
-
if (field.id in values && !isValidEmail(values[field.id] as string)) {
|
|
205
|
-
errors[field.id] = {
|
|
206
|
-
message:
|
|
207
|
-
field.validationMessage ??
|
|
208
|
-
'Please enter a valid email address.',
|
|
209
|
-
}
|
|
210
|
-
}
|
|
211
|
-
}
|
|
212
|
-
})
|
|
213
|
-
|
|
214
|
-
if (validateFields) {
|
|
215
|
-
const additionalValidations = await validateFields(values, errors)
|
|
216
|
-
values = additionalValidations.values
|
|
217
|
-
errors = additionalValidations.errors
|
|
218
|
-
}
|
|
219
|
-
|
|
220
|
-
return { values, errors }
|
|
221
|
-
},
|
|
222
|
-
})
|
|
223
|
-
|
|
224
|
-
// Reset form if initialValues changes since defaultValues is cached on the
|
|
225
|
-
// first render. This handles the situation where initialValues is updated
|
|
226
|
-
// after the form is rendered, primarily when using router state which is
|
|
227
|
-
// resolved _after_ the initial render for SSG pages.
|
|
228
|
-
useEffect(() => {
|
|
229
|
-
if (hasBeenRendered.current) {
|
|
230
|
-
const storedValues = persistFieldValues
|
|
231
|
-
? JSON.parse(localStorage.getItem('marketoFormData') || '{}')
|
|
232
|
-
: null
|
|
233
|
-
const newValues = calculateDefaultValues(fields.result, {
|
|
234
|
-
...storedValues,
|
|
235
|
-
...initialValues,
|
|
236
|
-
})
|
|
237
|
-
Object.entries(newValues).forEach(([fieldId, value]) => {
|
|
238
|
-
if (!methods.getValues(fieldId)) {
|
|
239
|
-
methods.setValue(fieldId, value)
|
|
240
|
-
}
|
|
241
|
-
})
|
|
242
|
-
}
|
|
243
|
-
}, [hasBeenRendered, methods, fields, initialValues])
|
|
244
|
-
|
|
245
|
-
// Track that we've finished rendering.
|
|
246
|
-
useEffect(() => {
|
|
247
|
-
hasBeenRendered.current = true
|
|
248
|
-
}, [hasBeenRendered])
|
|
249
|
-
|
|
250
|
-
const onSubmit = async (data: Record<string, unknown>) => {
|
|
251
|
-
if (onSubmitStart) {
|
|
252
|
-
onSubmitStart()
|
|
253
|
-
}
|
|
254
|
-
const leadFormFields = convertToRESTFields(data)
|
|
255
|
-
|
|
256
|
-
segmentIdentify(leadFormFields)
|
|
257
|
-
|
|
258
|
-
const res = await fetch(`${window.location.origin}/api/marketo/submit`, {
|
|
259
|
-
method: 'POST',
|
|
260
|
-
headers: {
|
|
261
|
-
'Content-Type': 'application/json',
|
|
262
|
-
},
|
|
263
|
-
body: JSON.stringify({
|
|
264
|
-
input: [
|
|
265
|
-
{
|
|
266
|
-
leadFormFields,
|
|
267
|
-
visitorData: {
|
|
268
|
-
pageURL: window.location.href,
|
|
269
|
-
},
|
|
270
|
-
},
|
|
271
|
-
],
|
|
272
|
-
formId: metadata.result[0].id,
|
|
273
|
-
}),
|
|
274
|
-
})
|
|
275
|
-
const marketoResponse = (await res.json()) as MarketoSubmissionResponse
|
|
276
|
-
if (
|
|
277
|
-
res.status === 200 &&
|
|
278
|
-
marketoResponse.success &&
|
|
279
|
-
!includesSkippedRecords(marketoResponse)
|
|
280
|
-
) {
|
|
281
|
-
if (onSubmitSuccess) {
|
|
282
|
-
onSubmitSuccess()
|
|
283
|
-
}
|
|
284
|
-
|
|
285
|
-
// Remove fields that shouldn't be stored in local storage
|
|
286
|
-
const dataToPersist = Object.fromEntries(
|
|
287
|
-
Object.entries(data).filter(([k]) => STORED_FIELD_NAMES.includes(k))
|
|
288
|
-
)
|
|
289
|
-
localStorage.setItem('marketoFormData', JSON.stringify(dataToPersist))
|
|
290
|
-
|
|
291
|
-
if (resetOnSubmission) {
|
|
292
|
-
methods.reset()
|
|
293
|
-
}
|
|
294
|
-
} else {
|
|
295
|
-
console.error(marketoResponse)
|
|
296
|
-
if (onSubmitError) {
|
|
297
|
-
onSubmitError()
|
|
298
|
-
}
|
|
299
|
-
|
|
300
|
-
throw new Error('An error occurred while submitting form.')
|
|
301
|
-
}
|
|
302
|
-
}
|
|
303
|
-
|
|
304
|
-
if (fields.success === false || metadata.success === false) {
|
|
305
|
-
return (
|
|
306
|
-
<div>
|
|
307
|
-
<p>This form is currently unavailable. Please try again later.</p>
|
|
308
|
-
</div>
|
|
309
|
-
)
|
|
310
|
-
}
|
|
311
|
-
|
|
312
|
-
return (
|
|
313
|
-
<FormMetadataContext.Provider value={metadata.result[0]}>
|
|
314
|
-
<FormProvider {...methods}>
|
|
315
|
-
<form
|
|
316
|
-
className={className}
|
|
317
|
-
onSubmit={methods.handleSubmit(onSubmit)}
|
|
318
|
-
data-marketo-form-id={metadata.result[0].id}
|
|
319
|
-
aria-label={formName}
|
|
320
|
-
>
|
|
321
|
-
{Object.entries(groupedFields).map(([groupName, fields]) => {
|
|
322
|
-
// If this group name has a custom component defined, use that
|
|
323
|
-
// instead of the default field components.
|
|
324
|
-
if (groups && groupName in groups) {
|
|
325
|
-
const Component = groups[groupName].component
|
|
326
|
-
return (
|
|
327
|
-
<Component
|
|
328
|
-
key={fields.map((f) => f.id).join('-')}
|
|
329
|
-
fields={fields}
|
|
330
|
-
/>
|
|
331
|
-
)
|
|
332
|
-
}
|
|
333
|
-
|
|
334
|
-
if (fields.length > 1) {
|
|
335
|
-
console.warn(
|
|
336
|
-
`Multiple fields in group ${groupName} with no component defined.`
|
|
337
|
-
)
|
|
338
|
-
}
|
|
339
|
-
|
|
340
|
-
return (
|
|
341
|
-
<VisibilityRule
|
|
342
|
-
key={fields[0].id}
|
|
343
|
-
components={components}
|
|
344
|
-
field={fields[0]}
|
|
345
|
-
/>
|
|
346
|
-
)
|
|
347
|
-
})}
|
|
348
|
-
<Button
|
|
349
|
-
type="submit"
|
|
350
|
-
size="large"
|
|
351
|
-
disabled={
|
|
352
|
-
methods.formState.isSubmitting ||
|
|
353
|
-
(methods.formState.isSubmitSuccessful && !resetOnSubmission)
|
|
354
|
-
}
|
|
355
|
-
text={
|
|
356
|
-
methods.formState.isSubmitting ||
|
|
357
|
-
(methods.formState.isSubmitSuccessful && !resetOnSubmission)
|
|
358
|
-
? metadata.result[0].waitingLabel
|
|
359
|
-
: submitTitle ?? metadata.result[0].buttonLabel
|
|
360
|
-
}
|
|
361
|
-
/>
|
|
362
|
-
</form>
|
|
363
|
-
</FormProvider>
|
|
364
|
-
</FormMetadataContext.Provider>
|
|
365
|
-
)
|
|
366
|
-
}
|
|
367
|
-
|
|
368
|
-
export { defaultFieldGroupings }
|
|
369
|
-
export default Form
|