@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,9 +1,45 @@
1
+ export const hashicorpDestinations: string[] = [
2
+ 'app.terraform.io',
3
+ 'boundaryproject.io',
4
+ 'cloud.hashicorp.com',
5
+ 'consul.io',
6
+ 'developer.hashicorp.com',
7
+ 'hashicorp.com',
8
+ 'nomadproject.io',
9
+ 'packer.io',
10
+ 'portal.cloud.hashicorp.com',
11
+ 'registry.terraform.io',
12
+ 'terraform.io',
13
+ 'vagrantup.com',
14
+ 'vaultproject.io',
15
+ 'waypointproject.io',
16
+ ] as const
17
+
18
+ const containsHashiCorpDestination = (str: string): boolean =>
19
+ hashicorpDestinations.some(function (destination) {
20
+ return str.indexOf(destination) >= 0
21
+ })
22
+
23
+ /**
24
+ * This function determines if a `url` is a HashiCorp link (relative URLs, hashes,
25
+ * or a url with a HashiCorp domain) or a non-HashiCorp link.
26
+ *
27
+ * @param {string | undefined} url - The URL to be parsed. It may be a relative URL, an anchor, or a full external/internal URL.
28
+ *
29
+ * @returns {{ linkType: 'inbound' | 'outbound', href: string }}
30
+ * An object with two properties:
31
+ * - `linkType`: Indicates whether the URL is 'inbound' or 'outbound'. Can be used to conditionally
32
+ * render the value of the target attribute.
33
+ * - `href`: The parsed href (may include pathname, search, and hash for internal URLs).
34
+ *
35
+ * @throws {Error} Throws an error if the URL is undefined or invalid.
36
+ */
1
37
  export function parseUrl(url: string | undefined): {
2
- linkType: 'inbound' | 'outbound'
38
+ linkType: 'inbound' | 'outbound' | ''
3
39
  href: string
4
40
  } {
5
41
  if (!url) {
6
- throw new Error('url is required')
42
+ return { linkType: '', href: '' }
7
43
  }
8
44
 
9
45
  if (isRelative(url) || isAnchor(url)) {
@@ -12,21 +48,50 @@ export function parseUrl(url: string | undefined): {
12
48
 
13
49
  if (isInternal(url)) {
14
50
  try {
15
- const { pathname, search } = new URL(url)
16
- const href = pathname + search
51
+ const { pathname, search, hash } = new URL(url)
52
+ const href = pathname + search + hash
17
53
  return { linkType: 'inbound', href }
18
54
  } catch (error) {
19
- throw new Error(`Invalid href: ${url}`)
55
+ console.log(`Invalid href: ${url}`)
56
+ return { linkType: '', href: '' }
20
57
  }
21
58
  }
22
59
 
23
60
  return { linkType: 'outbound', href: url }
24
61
  }
25
62
 
26
- const isRelative = (url) => url.startsWith('/')
27
- const isAnchor = (url) => url.startsWith('#')
63
+ const isRelative = (url: string) => url.startsWith('/')
64
+ const isAnchor = (url: string) => url.startsWith('#')
28
65
 
29
- export const isInternal = (url) => {
66
+ /**
67
+ * Determines whether the provided url uses hashicorp.com or www.hashicorp.com as the hostname.
68
+ *
69
+ * @param {string} url - The URL to check.
70
+ * @returns {boolean} - Returns true if the link uses a HashiCorp domain, otherwise false.
71
+ */
72
+
73
+ export const isInternal = (url: string): boolean => {
30
74
  const hostname = new URL(url).hostname
31
- return hostname === 'hashicorp.com'
75
+ return hostname === 'hashicorp.com' || hostname === 'www.hashicorp.com'
76
+ }
77
+
78
+ /**
79
+ * Determines whether the provided url uses a HashiCorp hostname. This is used in several
80
+ * components to open a new tab for non-HashiCorp links.
81
+ *
82
+ * @param {string} url - The URL to check.
83
+ * @returns {boolean} - Returns true if the link uses a HashiCorp domain, otherwise false.
84
+ */
85
+
86
+ export const hasHashiCorpDestination = (url: string): boolean => {
87
+ // Fast return in case a relative URL or anchor is passed in
88
+ if (isRelative(url) || isAnchor(url)) return true
89
+ try {
90
+ const hostname = new URL(url).hostname
91
+ const isHashiCorpDestination = containsHashiCorpDestination(hostname)
92
+ return isHashiCorpDestination
93
+ } catch (e) {
94
+ console.error('Error: `url` is not a valid URL', e)
95
+ return false
96
+ }
32
97
  }
@@ -0,0 +1,22 @@
1
+ import posthog from 'posthog-js'
2
+
3
+ /**
4
+ * A custom hook to send a custom event to PostHog.
5
+ *
6
+ * @param {string} eventName - The name of the custom event to be tracked in PostHog.
7
+ * @param {Record<string, any>} properties - A key-value object containing properties associated with the event.
8
+ *
9
+ * @example
10
+ * const MyComponent = () => {
11
+ * sendPosthogEvent('button_click', { buttonId: 'submit', action: 'clicked' });
12
+ * return <button id="submit">Submit</button>;
13
+ * };
14
+ */
15
+ const sendPosthogEvent = (
16
+ eventName: string,
17
+ properties: Record<string, any>
18
+ ) => {
19
+ posthog.capture(eventName, properties)
20
+ }
21
+
22
+ export default sendPosthogEvent
@@ -0,0 +1,10 @@
1
+ export const allSupportedLocales = [
2
+ 'en',
3
+ 'de',
4
+ 'es',
5
+ 'fr',
6
+ 'ja',
7
+ 'ko',
8
+ 'pt',
9
+ 'id',
10
+ ]
@@ -1,8 +1,8 @@
1
- import va from '@vercel/analytics'
1
+ import sendPosthogEvent from '../send-posthog-event'
2
2
 
3
3
  /**
4
- * Pushes a 'Nav link click' custom event to Vercel Analytics.
5
- * A wrapper around the Vercel Analytics track method.
4
+ * Pushes a 'Nav link click' custom event to PostHog.
5
+ * A wrapper around the PostHog custom event method.
6
6
  *
7
7
  * @param {string} name - The name of the nav link
8
8
  * @param {string} href - The href for the nav link
@@ -14,7 +14,7 @@ export const trackNavClickEvent = (
14
14
  href: string,
15
15
  section: string
16
16
  ) => {
17
- va.track('Nav link click', {
17
+ sendPosthogEvent('Nav link click', {
18
18
  name,
19
19
  href,
20
20
  section,
@@ -1,107 +0,0 @@
1
- /**
2
- * Copyright (c) HashiCorp, Inc.
3
- * SPDX-License-Identifier: MPL-2.0
4
- */
5
-
6
- import classNames from 'classnames'
7
- import Button from '@hashicorp/react-design-system-components/src/components/button'
8
- import StandaloneLink from '@hashicorp/react-design-system-components/src/components/standalone-link'
9
- import { IconArrowRight16 } from '@hashicorp/flight-icons/svg-react/arrow-right-16'
10
- import { IconArrowRight24 } from '@hashicorp/flight-icons/svg-react/arrow-right-24'
11
- import type { ActionsProps } from './types'
12
- import s from './style.module.css'
13
- import { ComponentProps } from 'react'
14
-
15
- const hasButton = (item) => item.type === 'button' || item.type === undefined
16
- const hasStandaloneLink = (item) => item.type === 'standalone-link'
17
-
18
- export default function Actions({
19
- appearance = 'light',
20
- layout = 'inline',
21
- alignment = 'left',
22
- theme = 'primary',
23
- size = 'large',
24
- ctas,
25
- }: ActionsProps) {
26
- if (ctas.length < 1 || ctas.length > 2) {
27
- throw new Error(
28
- `Actions expects at least 1 cta, no more than 2. You provided ${ctas.length}.`
29
- )
30
- }
31
- let mixed = false
32
- // If CTAs contain a button and standalone link
33
- // increase the column gap
34
- if (ctas.some(hasButton) && ctas.some(hasStandaloneLink)) {
35
- mixed = true
36
- }
37
- return (
38
- <div
39
- className={classNames(
40
- s.actions,
41
- s[layout],
42
- s[alignment],
43
- mixed && s.mixed
44
- )}
45
- data-testid="actions"
46
- >
47
- {ctas.map((cta, index) => {
48
- // The first CTA and second CTA should always
49
- // have the `primary` and `secondary` variations respectively.
50
-
51
- const standaloneLinkVariant: ComponentProps<
52
- typeof StandaloneLink
53
- >['color'] =
54
- index === 0
55
- ? 'primary'
56
- : appearance === 'dark'
57
- ? 'secondary-inverted'
58
- : 'secondary'
59
- const isStandaloneLink = cta?.type === 'standalone-link'
60
-
61
- if (isStandaloneLink) {
62
- return (
63
- <StandaloneLink
64
- // eslint-disable-next-line react/no-array-index-key
65
- key={index}
66
- text={cta.title}
67
- icon={
68
- size === 'large' ? (
69
- <IconArrowRight24
70
- width="100%"
71
- height="100%"
72
- style={{ display: 'block' }}
73
- />
74
- ) : (
75
- <IconArrowRight16
76
- width="100%"
77
- height="100%"
78
- style={{ display: 'block' }}
79
- />
80
- )
81
- }
82
- iconPosition="trailing"
83
- href={cta.href}
84
- color={standaloneLinkVariant}
85
- size={size}
86
- onClick={cta?.onClick}
87
- data-testid={`standaloneLink-${index}`}
88
- />
89
- )
90
- } else {
91
- return (
92
- <Button
93
- // eslint-disable-next-line react/no-array-index-key
94
- key={index}
95
- text={cta.title}
96
- href={cta.href}
97
- onClick={cta.onClick}
98
- size={size}
99
- color={theme}
100
- data-testid={`button-${index}`}
101
- />
102
- )
103
- }
104
- })}
105
- </div>
106
- )
107
- }
@@ -1,28 +0,0 @@
1
- /**
2
- * Copyright (c) HashiCorp, Inc.
3
- * SPDX-License-Identifier: MPL-2.0
4
- */
5
-
6
- .actions {
7
- display: flex;
8
- align-items: center;
9
- flex-wrap: wrap;
10
- gap: 16px 18px;
11
-
12
- &.stacked {
13
- flex-direction: column;
14
-
15
- &.left {
16
- align-items: flex-start;
17
- }
18
-
19
- &.center {
20
- align-items: center;
21
- }
22
- }
23
-
24
- &.mixed {
25
- column-gap: 24px;
26
- row-gap: 32px;
27
- }
28
- }
@@ -1,73 +0,0 @@
1
- /**
2
- * Copyright (c) HashiCorp, Inc.
3
- * SPDX-License-Identifier: MPL-2.0
4
- */
5
-
6
- import type { Products } from '@hashicorp/platform-product-meta'
7
-
8
- interface CtaProps {
9
- /**
10
- * The text that appears inside the button.
11
- */
12
- title: string
13
- /**
14
- * Where the button links to when clicked.
15
- */
16
- href: string
17
- /**
18
- * A function that will be called when the button is clicked.
19
- */
20
- onClick?: () => void
21
- }
22
-
23
- interface ButtonCtaProps extends CtaProps {
24
- type?: 'button'
25
- }
26
-
27
- interface StandaloneLinkCtaProps extends CtaProps {
28
- type: 'standalone-link'
29
- }
30
-
31
- export interface ActionsProps {
32
- /**
33
- * Display actions on light or dark background.
34
- */
35
- appearance?: 'light' | 'dark'
36
- /**
37
- * Display buttons inline or stacked by default.
38
- */
39
- layout?: 'inline' | 'stacked'
40
- /**
41
- * Display buttons inline or stacked by default.
42
- */
43
- alignment?: 'left' | 'center'
44
- /**
45
- * Render primary button with product color.
46
- */
47
- theme?:
48
- | 'primary'
49
- | 'secondary'
50
- | 'secondary-high-contrast'
51
- | 'tertiary'
52
- | 'critical'
53
- | Exclude<Products, 'hashicorp'>
54
- /**
55
- * Determines CTA button sizing.
56
- */
57
- size?: 'small' | 'medium' | 'large'
58
- /**
59
- * Array of CTAs. Minimum of one, max of two.
60
- * When both a Button and StandaloneLink are
61
- * rendered, they can only render in said order.
62
- * This is intentional and ensures adherence to a design
63
- * requirement of rendering the Button first when data
64
- * for both a Button and a StandaloneLink are provided.
65
- * @see CtaProps
66
- */
67
- ctas:
68
- | [ButtonCtaProps]
69
- | [StandaloneLinkCtaProps]
70
- | [ButtonCtaProps, ButtonCtaProps]
71
- | [StandaloneLinkCtaProps, StandaloneLinkCtaProps]
72
- | [ButtonCtaProps, StandaloneLinkCtaProps]
73
- }
@@ -1,205 +0,0 @@
1
- /**
2
- * Copyright (c) HashiCorp, Inc.
3
- * SPDX-License-Identifier: MPL-2.0
4
- */
5
-
6
- import type { ComponentProps, ReactNode } from 'react'
7
- import Button from '@hashicorp/react-design-system-components/src/components/button'
8
- import ButtonSet from '@hashicorp/react-design-system-components/src/components/button-set'
9
- import StandaloneLink from '@hashicorp/react-design-system-components/src/components/standalone-link'
10
- import { IconArrowRight24 } from '@hashicorp/flight-icons/svg-react/arrow-right-24'
11
- import { IconExternalLink24 } from '@hashicorp/flight-icons/svg-react/external-link-24'
12
- import { IconDownload24 } from '@hashicorp/flight-icons/svg-react/download-24'
13
- import { IconCornerRightDown24 } from '@hashicorp/flight-icons/svg-react/corner-right-down-24'
14
- import type { Products } from '@hashicorp/platform-product-meta'
15
- import classNames from 'classnames'
16
- import variantCentered from './styles/variant-centered.module.css'
17
- import variantCompact from './styles/variant-compact.module.css'
18
- import variantCompactGrid from './styles/variant-compact-grid.module.css'
19
- import variantLinks from './styles/variant-links.module.css'
20
-
21
- const stylesDict = {
22
- centered: variantCentered,
23
- compact: variantCompact,
24
- compactGrid: variantCompactGrid,
25
- links: variantLinks,
26
- }
27
-
28
- interface CallToActionProps {
29
- heading?: string
30
- content?: ReactNode
31
- links?: {
32
- type?: 'inbound' | 'outbound' | 'anchor' | 'download'
33
- text: string
34
- url: string
35
- }[]
36
- variant?: 'centered' | 'compact' | 'compactGrid' | 'links'
37
- product?: 'primary' | 'secondary' | Exclude<Products, 'hashicorp'>
38
- theme?: 'light' | 'gray' | 'dark' | 'brand'
39
- className?: string
40
- }
41
-
42
- function determineIconFromLinktype(
43
- linkType: 'inbound' | 'outbound' | 'anchor' | 'download'
44
- ): ReactNode {
45
- switch (linkType) {
46
- case 'inbound':
47
- return (
48
- <IconArrowRight24
49
- style={{ display: 'block' }}
50
- width="100%"
51
- height="100%"
52
- />
53
- )
54
- case 'outbound':
55
- return (
56
- <IconExternalLink24
57
- style={{ display: 'block' }}
58
- width="100%"
59
- height="100%"
60
- />
61
- )
62
- case 'download':
63
- return (
64
- <IconDownload24
65
- style={{ display: 'block' }}
66
- width="100%"
67
- height="100%"
68
- />
69
- )
70
- case 'anchor':
71
- return (
72
- <IconCornerRightDown24
73
- style={{ display: 'block' }}
74
- width="100%"
75
- height="100%"
76
- />
77
- )
78
- default:
79
- throw new Error(`Unsupported linkType: ${linkType}`)
80
- }
81
- }
82
-
83
- function CallToAction({
84
- heading,
85
- content,
86
- links,
87
- variant = 'centered',
88
- product = 'primary',
89
- theme = 'light',
90
- className,
91
- }: CallToActionProps) {
92
- const s = stylesDict[variant]
93
- if (!heading && !content) {
94
- throw new Error('<CallToAction /> requires either heading or content')
95
- }
96
- const hasLinks = links && links.length > 0
97
- if (hasLinks && links.filter((l) => !l.text || !l.url).length > 0) {
98
- throw new Error(
99
- '<CallToAction /> `links` must have both a "text" and a "url" prop'
100
- )
101
- }
102
- return (
103
- <div className={classNames(s.root, s[`theme-${theme}`], className)}>
104
- <div className={s.container}>
105
- {heading && (
106
- <h2 className={s.heading} data-testid="heading">
107
- {heading}
108
- </h2>
109
- )}
110
- <div className={s.contentAndLinks}>
111
- {content && (
112
- <div
113
- className={classNames(s.content, { [s.hasHeading]: heading })}
114
- data-testid="content"
115
- >
116
- {content}
117
- </div>
118
- )}
119
- {links && (
120
- <ButtonSet className={s.links} data-testid="links">
121
- {links.map((link, stableIdx) => {
122
- let color: ComponentProps<typeof Button>['color'] =
123
- variant === 'links'
124
- ? 'tertiary'
125
- : stableIdx === 0
126
- ? product
127
- : 'secondary'
128
-
129
- if (color === 'tertiary') {
130
- return (
131
- <StandaloneLink
132
- key={stableIdx}
133
- color="secondary"
134
- text={link.text}
135
- href={link.url}
136
- size="large"
137
- icon={
138
- <IconArrowRight24
139
- style={{ display: 'block' }}
140
- width="100%"
141
- height="100%"
142
- />
143
- }
144
- iconPosition="trailing"
145
- />
146
- )
147
- }
148
-
149
- // @ts-expect-error - TypeScript doesn't allow 'hashicorp', but
150
- // Dato might still pass it, so rewrite it to 'primary'.
151
- if (color === 'hashicorp') {
152
- color = 'primary'
153
- }
154
-
155
- if (theme === 'brand' && color === 'primary') {
156
- // `primary` doesn't work on brand, so
157
- // update it to `secondary`
158
- color = 'secondary'
159
- } else if (theme === 'brand' && color === 'secondary') {
160
- return (
161
- <StandaloneLink
162
- key={stableIdx}
163
- color="secondary-inverted"
164
- text={link.text}
165
- href={link.url}
166
- size="large"
167
- icon={
168
- <IconArrowRight24
169
- style={{ display: 'block' }}
170
- width="100%"
171
- height="100%"
172
- />
173
- }
174
- iconPosition="trailing"
175
- />
176
- )
177
- }
178
-
179
- return (
180
- <Button
181
- // eslint-disable-next-line react/no-array-index-key
182
- key={stableIdx}
183
- // className={s.button}
184
- color={color}
185
- text={link.text}
186
- href={link.url}
187
- size="large"
188
- icon={
189
- link.type
190
- ? determineIconFromLinktype(link.type)
191
- : undefined
192
- }
193
- iconPosition="trailing"
194
- />
195
- )
196
- })}
197
- </ButtonSet>
198
- )}
199
- </div>
200
- </div>
201
- </div>
202
- )
203
- }
204
-
205
- export default CallToAction
@@ -1,93 +0,0 @@
1
- /**
2
- * Copyright (c) HashiCorp, Inc.
3
- * SPDX-License-Identifier: MPL-2.0
4
- */
5
-
6
- .root {
7
- --background: var(--white);
8
- --text-color-display: var(--gray-1);
9
- --text-color-body: var(--gray-2);
10
-
11
- background: var(--background);
12
- padding: 64px 0;
13
-
14
- @media (--medium) {
15
- padding: 88px 0;
16
- }
17
- @media (--large) {
18
- padding: 128px 0;
19
- }
20
-
21
- &.theme-gray {
22
- --background: var(--gray-6);
23
- }
24
-
25
- &.theme-dark {
26
- --background: var(--black);
27
- --text-color-display: var(--white);
28
- --text-color-body: var(--gray-5);
29
- }
30
-
31
- &.theme-brand {
32
- --background: var(--brand);
33
- --text-color-display: var(--white);
34
- --text-color-body: var(--gray-6);
35
- }
36
- }
37
-
38
- .container {
39
- composes: g-grid-container from global;
40
- }
41
-
42
- .heading {
43
- composes: g-type-display-2 from global;
44
- color: var(--text-color-display);
45
- margin: 0 auto;
46
- text-align: center;
47
- max-width: 20em;
48
- }
49
-
50
- .content {
51
- composes: g-type-body-large from global;
52
- color: var(--text-color-body);
53
- margin: 0 auto;
54
- text-align: center;
55
- max-width: 40em;
56
-
57
- &.hasHeading {
58
- margin-top: 16px;
59
-
60
- @media (--large) {
61
- margin-top: 24px;
62
- }
63
- }
64
- }
65
-
66
- .links {
67
- display: flex;
68
- flex-wrap: wrap;
69
- justify-content: center;
70
-
71
- /*
72
- * Margins here account for extra 8px margin on buttons
73
- * which are needed to center and space properly regardless of whether
74
- * buttons are wrapping to multiple lines or not.
75
- */
76
- margin-top: 24px;
77
- margin-bottom: -8px;
78
-
79
- @media (--large) {
80
- margin-top: 32px;
81
- }
82
- }
83
-
84
- .button {
85
- /*
86
- * This ensures 16px between buttons at all times, while maintaining proper centering
87
- * when buttons wrap to multiple lines.
88
- * There will be an extra 8px space on all sides of the button group.
89
- * The top and bottom are accounted for by the -8px adjustment on `.action` margins.
90
- * The left and right excess is left as is - it's needed for proper centering when wrapping.
91
- */
92
- margin: 8px;
93
- }