@hashicorp/react-components 0.0.0-nightly.ad628cf → 0.0.0-nightly.b154bf4

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.
Files changed (166) hide show
  1. package/.eslintrc.js +1 -2
  2. package/.stylelintrc.js +1 -1
  3. package/README.md +1 -1
  4. package/package.json +4 -8
  5. package/postcss.config.js +1 -1
  6. package/src/components/nav-panel/components/flight-icon/index.tsx +51 -0
  7. package/src/components/nav-panel/components/flight-icon/style.module.css +3 -0
  8. package/src/components/nav-panel/components/link/index.tsx +127 -0
  9. package/src/components/nav-panel/components/link/style.module.css +64 -0
  10. package/src/components/nav-panel/components/link-list/index.tsx +13 -65
  11. package/src/components/nav-panel/components/link-list/style.module.css +1 -50
  12. package/src/components/nav-panel/components/panel/column/style.module.css +2 -2
  13. package/src/components/nav-panel/components/panel/root/style.module.css +5 -5
  14. package/src/components/nav-panel/components/promo/index.tsx +31 -11
  15. package/src/components/nav-panel/components/promo/style.module.css +26 -12
  16. package/src/components/nav-panel/components/text/index.tsx +34 -0
  17. package/src/components/nav-panel/product-panel/index.tsx +54 -17
  18. package/src/components/nav-panel/product-panel/style.module.css +116 -36
  19. package/src/components/nav-panel/standard-panel/index.tsx +3 -0
  20. package/src/components/nav-panel/standard-panel/style.module.css +2 -2
  21. package/src/lib/get-localized-link-props/index.test.ts +30 -0
  22. package/src/lib/get-localized-link-props/index.ts +45 -0
  23. package/src/lib/parse-url/index.test.ts +35 -1
  24. package/src/lib/parse-url/index.ts +74 -9
  25. package/src/lib/send-posthog-event/index.ts +22 -0
  26. package/src/lib/supported-locales/index.ts +10 -0
  27. package/src/lib/track-nav-click-event/index.ts +4 -4
  28. package/src/components/actions/index.tsx +0 -107
  29. package/src/components/actions/style.module.css +0 -28
  30. package/src/components/actions/types.ts +0 -73
  31. package/src/components/call-to-action/index.tsx +0 -205
  32. package/src/components/call-to-action/styles/variant-centered.module.css +0 -93
  33. package/src/components/call-to-action/styles/variant-compact-grid.module.css +0 -88
  34. package/src/components/call-to-action/styles/variant-compact.module.css +0 -91
  35. package/src/components/call-to-action/styles/variant-links.module.css +0 -85
  36. package/src/components/card/README.md +0 -11
  37. package/src/components/card/card.tsx +0 -45
  38. package/src/components/card/index.test.tsx +0 -69
  39. package/src/components/card/index.tsx +0 -14
  40. package/src/components/card/newsroom-card/index.test.tsx +0 -39
  41. package/src/components/card/newsroom-card/index.tsx +0 -37
  42. package/src/components/card/newsroom-card/style.module.css +0 -12
  43. package/src/components/card/partner-card/index.test.tsx +0 -57
  44. package/src/components/card/partner-card/index.tsx +0 -69
  45. package/src/components/card/partner-card/style.module.css +0 -24
  46. package/src/components/card/person-card/index.test.tsx +0 -71
  47. package/src/components/card/person-card/index.tsx +0 -93
  48. package/src/components/card/person-card/style.module.css +0 -33
  49. package/src/components/card/primitives.tsx +0 -176
  50. package/src/components/card/promo-card/index.tsx +0 -51
  51. package/src/components/card/resource-card/index.test.tsx +0 -48
  52. package/src/components/card/resource-card/index.tsx +0 -37
  53. package/src/components/card/style.module.css +0 -145
  54. package/src/components/card/types.ts +0 -66
  55. package/src/components/card/unified-card/index.tsx +0 -64
  56. package/src/components/consent-manager/README.md +0 -73
  57. package/src/components/consent-manager/components/banner/banner.module.css +0 -23
  58. package/src/components/consent-manager/components/banner/banner.test.tsx +0 -31
  59. package/src/components/consent-manager/components/banner/banner.tsx +0 -73
  60. package/src/components/consent-manager/components/dialog/dialog.module.css +0 -145
  61. package/src/components/consent-manager/components/dialog/dialog.test.tsx +0 -194
  62. package/src/components/consent-manager/components/dialog/dialog.tsx +0 -339
  63. package/src/components/consent-manager/img/icn_close.tsx +0 -19
  64. package/src/components/consent-manager/index.test.tsx +0 -334
  65. package/src/components/consent-manager/index.tsx +0 -186
  66. package/src/components/consent-manager/loader/README.md +0 -29
  67. package/src/components/consent-manager/loader/categories.ts +0 -22
  68. package/src/components/consent-manager/loader/index.tsx +0 -94
  69. package/src/components/consent-manager/loader/presets/enterprise.ts +0 -12
  70. package/src/components/consent-manager/loader/presets/oss.ts +0 -10
  71. package/src/components/consent-manager/loader/services.ts +0 -45
  72. package/src/components/consent-manager/scripts/custom.tsx +0 -59
  73. package/src/components/consent-manager/scripts/segment.tsx +0 -87
  74. package/src/components/consent-manager/style.module.css +0 -9
  75. package/src/components/consent-manager/types.ts +0 -55
  76. package/src/components/consent-manager/util/cookies.test.ts +0 -75
  77. package/src/components/consent-manager/util/cookies.ts +0 -70
  78. package/src/components/consent-manager/util/integrations.test.ts +0 -115
  79. package/src/components/consent-manager/util/integrations.ts +0 -110
  80. package/src/components/intro/index.tsx +0 -82
  81. package/src/components/intro/style.module.css +0 -67
  82. package/src/components/intro/types.ts +0 -44
  83. package/src/components/marketo-form/CHANGELOG.md +0 -122
  84. package/src/components/marketo-form/README.md +0 -1
  85. package/src/components/marketo-form/contexts/FormMetadata.ts +0 -11
  86. package/src/components/marketo-form/docs.mdx +0 -197
  87. package/src/components/marketo-form/fixtures/index.ts +0 -185
  88. package/src/components/marketo-form/form/index.tsx +0 -369
  89. package/src/components/marketo-form/index.test.tsx +0 -320
  90. package/src/components/marketo-form/index.ts +0 -11
  91. package/src/components/marketo-form/package.original.json +0 -30
  92. package/src/components/marketo-form/partials/error-message/index.tsx +0 -12
  93. package/src/components/marketo-form/partials/error-message/style.module.css +0 -13
  94. package/src/components/marketo-form/partials/field/index.tsx +0 -90
  95. package/src/components/marketo-form/partials/field-wrapper/index.tsx +0 -26
  96. package/src/components/marketo-form/partials/fields/checkbox-field/index.tsx +0 -32
  97. package/src/components/marketo-form/partials/fields/email-field/index.tsx +0 -35
  98. package/src/components/marketo-form/partials/fields/form-page-url-field/index.tsx +0 -21
  99. package/src/components/marketo-form/partials/fields/hidden-field/index.tsx +0 -37
  100. package/src/components/marketo-form/partials/fields/htmltext-field/index.tsx +0 -21
  101. package/src/components/marketo-form/partials/fields/htmltext-field/style.module.css +0 -18
  102. package/src/components/marketo-form/partials/fields/name-field/index.tsx +0 -19
  103. package/src/components/marketo-form/partials/fields/name-field/style.module.css +0 -13
  104. package/src/components/marketo-form/partials/fields/radio-buttons-field/index.tsx +0 -48
  105. package/src/components/marketo-form/partials/fields/select-field/index.tsx +0 -39
  106. package/src/components/marketo-form/partials/fields/select-field/style.module.css +0 -5
  107. package/src/components/marketo-form/partials/fields/telephone-field/index.tsx +0 -30
  108. package/src/components/marketo-form/partials/fields/text-field/index.tsx +0 -30
  109. package/src/components/marketo-form/partials/fields/textarea-field/index.tsx +0 -29
  110. package/src/components/marketo-form/partials/label/index.tsx +0 -24
  111. package/src/components/marketo-form/partials/label/style.module.css +0 -12
  112. package/src/components/marketo-form/partials/visibility-rule/index.tsx +0 -84
  113. package/src/components/marketo-form/props.js +0 -66
  114. package/src/components/marketo-form/server/api-routes.ts +0 -109
  115. package/src/components/marketo-form/server/client.ts +0 -98
  116. package/src/components/marketo-form/server/index.ts +0 -8
  117. package/src/components/marketo-form/types.ts +0 -268
  118. package/src/components/marketo-form/utils.test.tsx +0 -103
  119. package/src/components/marketo-form/utils.ts +0 -449
  120. package/src/components/next-steps/index.tsx +0 -137
  121. package/src/components/next-steps/style.module.css +0 -270
  122. package/src/components/next-steps/types.ts +0 -63
  123. package/src/components/product-badge/index.test.tsx +0 -11
  124. package/src/components/product-badge/index.tsx +0 -42
  125. package/src/components/product-badge/style.module.css +0 -76
  126. package/src/components/products-used/index.tsx +0 -84
  127. package/src/components/products-used/style.module.css +0 -87
  128. package/src/components/products-used/types.ts +0 -31
  129. package/src/components/related-content/index.test.tsx +0 -74
  130. package/src/components/related-content/index.tsx +0 -56
  131. package/src/components/related-content/style.module.css +0 -89
  132. package/src/components/related-content/types.ts +0 -21
  133. package/src/components/subnav/helpers/areBasePathsMatching.ts +0 -13
  134. package/src/components/subnav/helpers/traverse/index.ts +0 -42
  135. package/src/components/subnav/helpers/useHeightRef.ts +0 -42
  136. package/src/components/subnav/helpers/useNavRef.ts +0 -30
  137. package/src/components/subnav/helpers/useOverflowRef.ts +0 -85
  138. package/src/components/subnav/helpers/useStuckRef.ts +0 -51
  139. package/src/components/subnav/index.tsx +0 -199
  140. package/src/components/subnav/partials/CtaLinks/github-stars-link/formatStarCount/index.test.ts +0 -26
  141. package/src/components/subnav/partials/CtaLinks/github-stars-link/formatStarCount/index.ts +0 -22
  142. package/src/components/subnav/partials/CtaLinks/github-stars-link/index.tsx +0 -87
  143. package/src/components/subnav/partials/CtaLinks/github-stars-link/parseGithubUrl/index.test.ts +0 -29
  144. package/src/components/subnav/partials/CtaLinks/github-stars-link/parseGithubUrl/index.ts +0 -32
  145. package/src/components/subnav/partials/CtaLinks/github-stars-link/style.module.css +0 -81
  146. package/src/components/subnav/partials/CtaLinks/icons/github-logo.js +0 -20
  147. package/src/components/subnav/partials/CtaLinks/icons/github.svg.js +0 -7
  148. package/src/components/subnav/partials/CtaLinks/icons/star.js +0 -20
  149. package/src/components/subnav/partials/CtaLinks/index.tsx +0 -130
  150. package/src/components/subnav/partials/CtaLinks/style.module.css +0 -45
  151. package/src/components/subnav/partials/DropdownTrigger/index.tsx +0 -35
  152. package/src/components/subnav/partials/DropdownTrigger/style.module.css +0 -39
  153. package/src/components/subnav/partials/MenuItemsDefault/index.tsx +0 -158
  154. package/src/components/subnav/partials/MenuItemsDefault/style.module.css +0 -104
  155. package/src/components/subnav/partials/MenuItemsOverflow/index.tsx +0 -143
  156. package/src/components/subnav/partials/MenuItemsOverflow/style.module.css +0 -88
  157. package/src/components/subnav/partials/TitleLink/index.tsx +0 -113
  158. package/src/components/subnav/partials/TitleLink/mktg-logos/all-community-product-logos_on-dark.ts +0 -10
  159. package/src/components/subnav/partials/TitleLink/mktg-logos/all-community-product-logos_on-light.ts +0 -10
  160. package/src/components/subnav/partials/TitleLink/mktg-logos/all-primary-logos_on-dark.ts +0 -23
  161. package/src/components/subnav/partials/TitleLink/mktg-logos/all-primary-logos_on-light.ts +0 -23
  162. package/src/components/subnav/partials/TitleLink/style.module.css +0 -43
  163. package/src/components/subnav/partials/nav-item-text/index.tsx +0 -37
  164. package/src/components/subnav/partials/nav-item-text/style.module.css +0 -13
  165. package/src/components/subnav/style.module.css +0 -66
  166. package/src/components/subnav/subnav-consts.ts +0 -1
@@ -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
@@ -1,320 +0,0 @@
1
- import { render, screen, waitFor } from '@testing-library/react'
2
- import nock from 'nock'
3
- import userEvent from '@testing-library/user-event'
4
- import MarketoForm from './'
5
- import {
6
- BASIC_FORM_PROPS,
7
- UTM_FORM_PROPS,
8
- VISIBILITY_RULE_FORM_PROPS,
9
- COOKIE_UTM_FORM_PROPS,
10
- HIDDEN_COOKIE_FIELD_FORM_PROPS,
11
- } from './fixtures'
12
- import cookie from 'js-cookie'
13
-
14
- describe('MarketoForm', () => {
15
- let originalLocation
16
- let spy
17
-
18
- beforeAll(() => {
19
- originalLocation = window.location
20
- })
21
-
22
- beforeEach(() => {
23
- nock('http://local.test')
24
- .persist()
25
- .post('/api/marketo/submit')
26
- .reply(200, { success: true, result: [] })
27
-
28
- delete window.location
29
- window.location = {
30
- // We use an absolute URL here since fetch in Jest can't use relative URLs
31
- origin: 'http://local.test',
32
- protocol: 'http:',
33
- host: 'local.test',
34
- pathname: '/testing',
35
- href: 'http://local.test/testing?utm_medium=social',
36
- search: '?utm_medium=social',
37
- }
38
-
39
- spy = vi.spyOn(global, 'fetch')
40
- })
41
-
42
- afterEach(() => {
43
- vi.resetAllMocks()
44
- spy.mockRestore()
45
- window.location = originalLocation
46
- })
47
-
48
- afterAll(() => {
49
- nock.restore()
50
- })
51
-
52
- test('renders form with provided fields', async () => {
53
- render(<MarketoForm {...BASIC_FORM_PROPS} />)
54
-
55
- const firstNameField = screen.getByLabelText(/First Name/)
56
- const lastNameField = screen.getByLabelText(/Last Name/)
57
- expect(firstNameField).toBeInTheDocument()
58
- expect(lastNameField).toBeInTheDocument()
59
- })
60
-
61
- test('submits form data', async () => {
62
- render(<MarketoForm {...BASIC_FORM_PROPS} />)
63
-
64
- const firstNameField = screen.getByLabelText(/First Name/)
65
- expect(firstNameField).toBeInTheDocument()
66
- userEvent.type(firstNameField, 'Michael')
67
-
68
- const lastNameField = screen.getByLabelText(/Last Name/)
69
- expect(lastNameField).toBeInTheDocument()
70
- userEvent.type(lastNameField, 'Burnham')
71
-
72
- const submitButton = screen.getByText('Submit')
73
- expect(submitButton).toBeInTheDocument()
74
- userEvent.click(submitButton)
75
-
76
- await waitFor(() => expect(spy).toHaveBeenCalled())
77
- expect(spy).toHaveBeenCalledWith('http://local.test/api/marketo/submit', {
78
- method: 'POST',
79
- headers: {
80
- 'Content-Type': 'application/json',
81
- },
82
- body: JSON.stringify({
83
- input: [
84
- {
85
- leadFormFields: {
86
- firstName: 'Michael',
87
- lastName: 'Burnham',
88
- },
89
- visitorData: {
90
- pageURL: 'http://local.test/testing?utm_medium=social',
91
- },
92
- },
93
- ],
94
- formId: 9999,
95
- }),
96
- })
97
- })
98
-
99
- test('submits with initial values', async () => {
100
- render(
101
- <MarketoForm
102
- {...BASIC_FORM_PROPS}
103
- initialValues={{ LastName: 'Burnham' }}
104
- />
105
- )
106
-
107
- const firstNameField = screen.getByLabelText(/First Name/)
108
- expect(firstNameField).toBeInTheDocument()
109
- userEvent.type(firstNameField, 'Michael')
110
-
111
- const submitButton = screen.getByText('Submit')
112
- expect(submitButton).toBeInTheDocument()
113
- userEvent.click(submitButton)
114
-
115
- await waitFor(() => expect(spy).toHaveBeenCalled())
116
- expect(spy).toHaveBeenCalledWith('http://local.test/api/marketo/submit', {
117
- method: 'POST',
118
- headers: {
119
- 'Content-Type': 'application/json',
120
- },
121
- body: JSON.stringify({
122
- input: [
123
- {
124
- leadFormFields: {
125
- firstName: 'Michael',
126
- lastName: 'Burnham',
127
- },
128
- visitorData: {
129
- pageURL: 'http://local.test/testing?utm_medium=social',
130
- },
131
- },
132
- ],
133
- formId: 9999,
134
- }),
135
- })
136
- })
137
-
138
- test('submits form data with UTM params', async () => {
139
- render(<MarketoForm {...UTM_FORM_PROPS} />)
140
-
141
- const firstNameField = screen.getByLabelText(/First Name/)
142
- expect(firstNameField).toBeInTheDocument()
143
- userEvent.type(firstNameField, 'Michael')
144
-
145
- const lastNameField = screen.getByLabelText(/Last Name/)
146
- expect(lastNameField).toBeInTheDocument()
147
- userEvent.type(lastNameField, 'Burnham')
148
-
149
- const submitButton = screen.getByText('Submit')
150
- expect(submitButton).toBeInTheDocument()
151
- userEvent.click(submitButton)
152
-
153
- await waitFor(() => expect(spy).toHaveBeenCalled())
154
- expect(spy).toHaveBeenCalledWith('http://local.test/api/marketo/submit', {
155
- method: 'POST',
156
- headers: {
157
- 'Content-Type': 'application/json',
158
- },
159
- body: JSON.stringify({
160
- input: [
161
- {
162
- leadFormFields: {
163
- firstName: 'Michael',
164
- lastName: 'Burnham',
165
- utm_medium__c: 'social',
166
- form_page_url__c: 'http://local.test/testing',
167
- },
168
- visitorData: {
169
- pageURL: 'http://local.test/testing?utm_medium=social',
170
- },
171
- },
172
- ],
173
- formId: 9999,
174
- }),
175
- })
176
- })
177
-
178
- test('submits form data with UTM params from cookies', async () => {
179
- cookie.get = vi.fn().mockImplementation(() => {
180
- return { utm_content: 'utm-content-cookie-val' }
181
- })
182
- render(<MarketoForm {...COOKIE_UTM_FORM_PROPS} />)
183
-
184
- const firstNameField = screen.getByLabelText(/First Name/)
185
- expect(firstNameField).toBeInTheDocument()
186
- userEvent.type(firstNameField, 'Michael')
187
-
188
- const lastNameField = screen.getByLabelText(/Last Name/)
189
- expect(lastNameField).toBeInTheDocument()
190
- userEvent.type(lastNameField, 'Burnham')
191
-
192
- const submitButton = screen.getByText('Submit')
193
- expect(submitButton).toBeInTheDocument()
194
- userEvent.click(submitButton)
195
-
196
- await waitFor(() => expect(spy).toHaveBeenCalled())
197
- expect(spy).toHaveBeenCalledWith('http://local.test/api/marketo/submit', {
198
- method: 'POST',
199
- headers: {
200
- 'Content-Type': 'application/json',
201
- },
202
- body: JSON.stringify({
203
- input: [
204
- {
205
- leadFormFields: {
206
- firstName: 'Michael',
207
- lastName: 'Burnham',
208
- utm_content__c: 'utm-content-cookie-val',
209
- },
210
- visitorData: {
211
- pageURL: 'http://local.test/testing?utm_medium=social',
212
- },
213
- },
214
- ],
215
- formId: 9999,
216
- }),
217
- })
218
- })
219
-
220
- test('renders built-in error validation messages', async () => {
221
- render(<MarketoForm {...BASIC_FORM_PROPS} />)
222
-
223
- const firstNameField = screen.getByLabelText(/First Name/)
224
- expect(firstNameField).toBeInTheDocument()
225
- userEvent.click(firstNameField)
226
-
227
- const lastNameField = screen.getByLabelText(/Last Name/)
228
- expect(lastNameField).toBeInTheDocument()
229
- userEvent.click(lastNameField)
230
-
231
- const errorMessage = await screen.findByText('This field is required.')
232
- expect(errorMessage).toBeInTheDocument()
233
- })
234
-
235
- test('renders additional error validation messages', async () => {
236
- render(
237
- <MarketoForm
238
- {...BASIC_FORM_PROPS}
239
- validateFields={async (values, errors) => {
240
- return {
241
- values,
242
- errors: {
243
- ...errors,
244
- FirstName: {
245
- message: `${errors.FirstName.message} Additional message.`,
246
- },
247
- },
248
- }
249
- }}
250
- />
251
- )
252
-
253
- const firstNameField = screen.getByLabelText(/First Name/)
254
- expect(firstNameField).toBeInTheDocument()
255
- userEvent.click(firstNameField)
256
-
257
- const lastNameField = screen.getByLabelText(/Last Name/)
258
- expect(lastNameField).toBeInTheDocument()
259
- userEvent.click(lastNameField)
260
-
261
- const errorMessage = await screen.findByText(
262
- 'This field is required. Additional message.'
263
- )
264
- expect(errorMessage).toBeInTheDocument()
265
- })
266
-
267
- test('handles simple visibility rules', async () => {
268
- render(<MarketoForm {...VISIBILITY_RULE_FORM_PROPS} />)
269
-
270
- expect(screen.queryByLabelText('Phone Number')).toBeNull()
271
-
272
- const fastTrackField = screen.getByLabelText('Have salesperson contact me')
273
- expect(fastTrackField).toBeInTheDocument()
274
- userEvent.click(fastTrackField)
275
-
276
- expect(screen.getByLabelText('Phone Number')).toBeInTheDocument()
277
- })
278
-
279
- test('submits form with cookie values for hidden fields', async () => {
280
- cookie.get = vi.fn().mockImplementation(() => {
281
- return 'hidden-cookie-field-val'
282
- })
283
- render(<MarketoForm {...HIDDEN_COOKIE_FIELD_FORM_PROPS} />)
284
-
285
- const firstNameField = screen.getByLabelText(/First Name/)
286
- expect(firstNameField).toBeInTheDocument()
287
- userEvent.type(firstNameField, 'Michael')
288
-
289
- const lastNameField = screen.getByLabelText(/Last Name/)
290
- expect(lastNameField).toBeInTheDocument()
291
- userEvent.type(lastNameField, 'Burnham')
292
-
293
- const submitButton = screen.getByText('Submit')
294
- expect(submitButton).toBeInTheDocument()
295
- userEvent.click(submitButton)
296
-
297
- await waitFor(() => expect(spy).toHaveBeenCalled())
298
- expect(spy).toHaveBeenCalledWith('http://local.test/api/marketo/submit', {
299
- method: 'POST',
300
- headers: {
301
- 'Content-Type': 'application/json',
302
- },
303
- body: JSON.stringify({
304
- input: [
305
- {
306
- leadFormFields: {
307
- firstName: 'Michael',
308
- lastName: 'Burnham',
309
- cookieHiddenField: 'hidden-cookie-field-val',
310
- },
311
- visitorData: {
312
- pageURL: 'http://local.test/testing?utm_medium=social',
313
- },
314
- },
315
- ],
316
- formId: 9999,
317
- }),
318
- })
319
- })
320
- })
@@ -1,11 +0,0 @@
1
- /**
2
- * Copyright (c) HashiCorp, Inc.
3
- * SPDX-License-Identifier: MPL-2.0
4
- */
5
-
6
- import { useFormContext, useFormState, useWatch } from 'react-hook-form'
7
- import Form, { defaultFieldGroupings } from './form'
8
- import Field from './partials/field'
9
-
10
- export { useFormContext, useFormState, useWatch, defaultFieldGroupings, Field }
11
- export default Form