@hashicorp/react-components 0.0.0-canary.33d6c97 → 0.0.0-canary.3cd4069

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 (164) hide show
  1. package/package.json +5 -9
  2. package/src/components/nav-panel/components/flight-icon/index.tsx +51 -0
  3. package/src/components/nav-panel/components/flight-icon/style.module.css +3 -0
  4. package/src/components/nav-panel/components/link-list/index.tsx +15 -9
  5. package/src/components/nav-panel/components/link-list/style.module.css +10 -10
  6. package/src/components/nav-panel/components/panel/column/index.tsx +15 -2
  7. package/src/components/nav-panel/components/panel/column/style.module.css +13 -3
  8. package/src/components/nav-panel/components/panel/root/index.tsx +11 -2
  9. package/src/components/nav-panel/components/panel/root/style.module.css +20 -6
  10. package/src/components/nav-panel/components/promo/index.tsx +23 -10
  11. package/src/components/nav-panel/components/promo/style.module.css +52 -16
  12. package/src/components/nav-panel/components/text/index.tsx +34 -0
  13. package/src/components/nav-panel/product-panel/index.tsx +58 -17
  14. package/src/components/nav-panel/product-panel/style.module.css +198 -41
  15. package/src/components/nav-panel/standard-panel/index.tsx +3 -0
  16. package/src/components/nav-panel/standard-panel/style.module.css +2 -2
  17. package/src/lib/get-localized-link-props/index.test.ts +30 -0
  18. package/src/lib/get-localized-link-props/index.ts +45 -0
  19. package/src/lib/parse-url/index.test.ts +40 -1
  20. package/src/lib/parse-url/index.ts +77 -9
  21. package/src/lib/send-posthog-event/index.ts +22 -0
  22. package/src/lib/supported-locales/index.ts +1 -0
  23. package/src/lib/track-nav-click-event/index.ts +4 -4
  24. package/src/lib/v/index.ts +4 -5
  25. package/src/components/actions/index.tsx +0 -107
  26. package/src/components/actions/style.module.css +0 -28
  27. package/src/components/actions/types.ts +0 -73
  28. package/src/components/call-to-action/index.tsx +0 -205
  29. package/src/components/call-to-action/styles/variant-centered.module.css +0 -93
  30. package/src/components/call-to-action/styles/variant-compact-grid.module.css +0 -88
  31. package/src/components/call-to-action/styles/variant-compact.module.css +0 -91
  32. package/src/components/call-to-action/styles/variant-links.module.css +0 -85
  33. package/src/components/card/README.md +0 -11
  34. package/src/components/card/card.tsx +0 -45
  35. package/src/components/card/index.test.tsx +0 -69
  36. package/src/components/card/index.tsx +0 -14
  37. package/src/components/card/newsroom-card/index.test.tsx +0 -39
  38. package/src/components/card/newsroom-card/index.tsx +0 -37
  39. package/src/components/card/newsroom-card/style.module.css +0 -12
  40. package/src/components/card/partner-card/index.test.tsx +0 -57
  41. package/src/components/card/partner-card/index.tsx +0 -69
  42. package/src/components/card/partner-card/style.module.css +0 -24
  43. package/src/components/card/person-card/index.test.tsx +0 -71
  44. package/src/components/card/person-card/index.tsx +0 -93
  45. package/src/components/card/person-card/style.module.css +0 -33
  46. package/src/components/card/primitives.tsx +0 -176
  47. package/src/components/card/promo-card/index.tsx +0 -51
  48. package/src/components/card/resource-card/index.test.tsx +0 -48
  49. package/src/components/card/resource-card/index.tsx +0 -37
  50. package/src/components/card/style.module.css +0 -145
  51. package/src/components/card/types.ts +0 -66
  52. package/src/components/card/unified-card/index.tsx +0 -64
  53. package/src/components/consent-manager/README.md +0 -73
  54. package/src/components/consent-manager/components/banner/banner.module.css +0 -23
  55. package/src/components/consent-manager/components/banner/banner.test.tsx +0 -31
  56. package/src/components/consent-manager/components/banner/banner.tsx +0 -73
  57. package/src/components/consent-manager/components/dialog/dialog.module.css +0 -145
  58. package/src/components/consent-manager/components/dialog/dialog.test.tsx +0 -194
  59. package/src/components/consent-manager/components/dialog/dialog.tsx +0 -339
  60. package/src/components/consent-manager/img/icn_close.tsx +0 -19
  61. package/src/components/consent-manager/index.test.tsx +0 -334
  62. package/src/components/consent-manager/index.tsx +0 -186
  63. package/src/components/consent-manager/loader/README.md +0 -29
  64. package/src/components/consent-manager/loader/categories.ts +0 -22
  65. package/src/components/consent-manager/loader/index.tsx +0 -94
  66. package/src/components/consent-manager/loader/presets/enterprise.ts +0 -12
  67. package/src/components/consent-manager/loader/presets/oss.ts +0 -10
  68. package/src/components/consent-manager/loader/services.ts +0 -45
  69. package/src/components/consent-manager/scripts/custom.tsx +0 -59
  70. package/src/components/consent-manager/scripts/segment.tsx +0 -87
  71. package/src/components/consent-manager/style.module.css +0 -9
  72. package/src/components/consent-manager/types.ts +0 -55
  73. package/src/components/consent-manager/util/cookies.test.ts +0 -75
  74. package/src/components/consent-manager/util/cookies.ts +0 -70
  75. package/src/components/consent-manager/util/integrations.test.ts +0 -115
  76. package/src/components/consent-manager/util/integrations.ts +0 -110
  77. package/src/components/intro/index.tsx +0 -82
  78. package/src/components/intro/style.module.css +0 -67
  79. package/src/components/intro/types.ts +0 -44
  80. package/src/components/marketo-form/CHANGELOG.md +0 -122
  81. package/src/components/marketo-form/README.md +0 -1
  82. package/src/components/marketo-form/contexts/FormMetadata.ts +0 -11
  83. package/src/components/marketo-form/docs.mdx +0 -197
  84. package/src/components/marketo-form/fixtures/index.ts +0 -185
  85. package/src/components/marketo-form/form/index.tsx +0 -369
  86. package/src/components/marketo-form/index.test.tsx +0 -320
  87. package/src/components/marketo-form/index.ts +0 -11
  88. package/src/components/marketo-form/package.original.json +0 -30
  89. package/src/components/marketo-form/partials/error-message/index.tsx +0 -12
  90. package/src/components/marketo-form/partials/error-message/style.module.css +0 -13
  91. package/src/components/marketo-form/partials/field/index.tsx +0 -90
  92. package/src/components/marketo-form/partials/field-wrapper/index.tsx +0 -26
  93. package/src/components/marketo-form/partials/fields/checkbox-field/index.tsx +0 -32
  94. package/src/components/marketo-form/partials/fields/email-field/index.tsx +0 -35
  95. package/src/components/marketo-form/partials/fields/form-page-url-field/index.tsx +0 -21
  96. package/src/components/marketo-form/partials/fields/hidden-field/index.tsx +0 -37
  97. package/src/components/marketo-form/partials/fields/htmltext-field/index.tsx +0 -21
  98. package/src/components/marketo-form/partials/fields/htmltext-field/style.module.css +0 -18
  99. package/src/components/marketo-form/partials/fields/name-field/index.tsx +0 -19
  100. package/src/components/marketo-form/partials/fields/name-field/style.module.css +0 -13
  101. package/src/components/marketo-form/partials/fields/radio-buttons-field/index.tsx +0 -48
  102. package/src/components/marketo-form/partials/fields/select-field/index.tsx +0 -39
  103. package/src/components/marketo-form/partials/fields/select-field/style.module.css +0 -5
  104. package/src/components/marketo-form/partials/fields/telephone-field/index.tsx +0 -30
  105. package/src/components/marketo-form/partials/fields/text-field/index.tsx +0 -30
  106. package/src/components/marketo-form/partials/fields/textarea-field/index.tsx +0 -29
  107. package/src/components/marketo-form/partials/label/index.tsx +0 -24
  108. package/src/components/marketo-form/partials/label/style.module.css +0 -12
  109. package/src/components/marketo-form/partials/visibility-rule/index.tsx +0 -84
  110. package/src/components/marketo-form/props.js +0 -66
  111. package/src/components/marketo-form/server/api-routes.ts +0 -109
  112. package/src/components/marketo-form/server/client.ts +0 -98
  113. package/src/components/marketo-form/server/index.ts +0 -8
  114. package/src/components/marketo-form/types.ts +0 -268
  115. package/src/components/marketo-form/utils.test.tsx +0 -103
  116. package/src/components/marketo-form/utils.ts +0 -445
  117. package/src/components/next-steps/index.tsx +0 -137
  118. package/src/components/next-steps/style.module.css +0 -270
  119. package/src/components/next-steps/types.ts +0 -63
  120. package/src/components/product-badge/index.test.tsx +0 -11
  121. package/src/components/product-badge/index.tsx +0 -42
  122. package/src/components/product-badge/style.module.css +0 -76
  123. package/src/components/products-used/index.tsx +0 -84
  124. package/src/components/products-used/style.module.css +0 -87
  125. package/src/components/products-used/types.ts +0 -31
  126. package/src/components/related-content/index.test.tsx +0 -74
  127. package/src/components/related-content/index.tsx +0 -56
  128. package/src/components/related-content/style.module.css +0 -89
  129. package/src/components/related-content/types.ts +0 -21
  130. package/src/components/subnav/helpers/areBasePathsMatching.ts +0 -13
  131. package/src/components/subnav/helpers/traverse/index.ts +0 -42
  132. package/src/components/subnav/helpers/useHeightRef.ts +0 -42
  133. package/src/components/subnav/helpers/useNavRef.ts +0 -30
  134. package/src/components/subnav/helpers/useOverflowRef.ts +0 -85
  135. package/src/components/subnav/helpers/useStuckRef.ts +0 -51
  136. package/src/components/subnav/index.tsx +0 -199
  137. package/src/components/subnav/partials/CtaLinks/github-stars-link/formatStarCount/index.test.ts +0 -26
  138. package/src/components/subnav/partials/CtaLinks/github-stars-link/formatStarCount/index.ts +0 -22
  139. package/src/components/subnav/partials/CtaLinks/github-stars-link/index.tsx +0 -87
  140. package/src/components/subnav/partials/CtaLinks/github-stars-link/parseGithubUrl/index.test.ts +0 -29
  141. package/src/components/subnav/partials/CtaLinks/github-stars-link/parseGithubUrl/index.ts +0 -32
  142. package/src/components/subnav/partials/CtaLinks/github-stars-link/style.module.css +0 -85
  143. package/src/components/subnav/partials/CtaLinks/icons/github-logo.js +0 -20
  144. package/src/components/subnav/partials/CtaLinks/icons/github.svg.js +0 -7
  145. package/src/components/subnav/partials/CtaLinks/icons/star.js +0 -20
  146. package/src/components/subnav/partials/CtaLinks/index.tsx +0 -130
  147. package/src/components/subnav/partials/CtaLinks/style.module.css +0 -45
  148. package/src/components/subnav/partials/DropdownTrigger/index.tsx +0 -35
  149. package/src/components/subnav/partials/DropdownTrigger/style.module.css +0 -36
  150. package/src/components/subnav/partials/MenuItemsDefault/index.tsx +0 -151
  151. package/src/components/subnav/partials/MenuItemsDefault/style.module.css +0 -91
  152. package/src/components/subnav/partials/MenuItemsOverflow/index.tsx +0 -139
  153. package/src/components/subnav/partials/MenuItemsOverflow/style.module.css +0 -82
  154. package/src/components/subnav/partials/TitleLink/index.tsx +0 -113
  155. package/src/components/subnav/partials/TitleLink/mktg-logos/all-community-product-logos_on-dark.ts +0 -9
  156. package/src/components/subnav/partials/TitleLink/mktg-logos/all-community-product-logos_on-light.ts +0 -9
  157. package/src/components/subnav/partials/TitleLink/mktg-logos/all-primary-logos_on-dark.ts +0 -23
  158. package/src/components/subnav/partials/TitleLink/mktg-logos/all-primary-logos_on-light.ts +0 -23
  159. package/src/components/subnav/partials/TitleLink/style.module.css +0 -43
  160. package/src/components/subnav/partials/nav-item-text/index.tsx +0 -37
  161. package/src/components/subnav/partials/nav-item-text/style.module.css +0 -15
  162. package/src/components/subnav/style.module.css +0 -68
  163. package/src/components/subnav/subnav-consts.ts +0 -1
  164. package/src/components/subnav/theme.module.css +0 -50
@@ -1,16 +1,17 @@
1
+ import classNames from 'classnames'
1
2
  import Link from 'next/link'
2
3
  import * as NavPanel from '../components/panel'
3
4
  import Promo, { type PromoProps } from '../components/promo'
5
+ import { getLocalizedLinkProps } from '../../../lib/get-localized-link-props'
4
6
  import { Products } from '@hashicorp/platform-product-meta'
5
- import FlightIcon from '@hashicorp/react-design-system-components/src/components/flight-icon'
6
- import { TextPlus } from '@hashicorp/react-design-system-components/src/components/text'
7
- import { parseUrl } from '../../../lib/parse-url'
7
+ import FlightIcon from '../components/flight-icon'
8
+ import { Text } from '../components/text'
8
9
  import { trackNavClickEvent } from '../../../lib/track-nav-click-event'
9
10
  import * as NavigationMenu from '@radix-ui/react-navigation-menu'
10
11
  import s from './style.module.css'
11
12
 
12
13
  const PRODUCT_LOGO_MAP = new Map([
13
- ['Terraform', 'terraform-fill-color'],
14
+ ['Terraform', 'terraform-fill'],
14
15
  ['Packer', 'packer-fill-color'],
15
16
  ['Vault', 'vault-fill-color'],
16
17
  ['Boundary', 'boundary-fill-color'],
@@ -18,9 +19,18 @@ const PRODUCT_LOGO_MAP = new Map([
18
19
  ['Nomad', 'nomad-fill-color'],
19
20
  ['Waypoint', 'waypoint-fill-color'],
20
21
  ['Vagrant', 'vagrant-fill-color'],
22
+ ['HCP Vault Secrets', 'vault-secrets'],
23
+ ['HCP Vault Radar', 'vault-radar'],
21
24
  ])
22
25
 
26
+ const PRODUCT_LOGO_EXCEPTION_CLASSNAMES = {
27
+ Terraform: 'terraform',
28
+ 'HCP Vault Secrets': 'hcpVaultSecrets',
29
+ 'HCP Vault Radar': 'hcpVaultRadar',
30
+ }
31
+
23
32
  export interface ProductsPanelProps {
33
+ locale?: string
24
34
  productCategories: any
25
35
  promo?: PromoProps
26
36
  sidePanel?: {
@@ -32,27 +42,44 @@ export interface ProductsPanelProps {
32
42
  url: string
33
43
  }[]
34
44
  }
45
+ isPromoOnTop?: boolean
35
46
  }
36
47
 
37
48
  const ProductPanel = ({
49
+ locale,
38
50
  productCategories,
39
51
  promo,
40
52
  sidePanel,
53
+ isPromoOnTop = false,
41
54
  }: ProductsPanelProps) => {
42
55
  return (
43
- <NavPanel.Root className={s.productPanel}>
56
+ <NavPanel.Root
57
+ className={classNames(s.productPanel, {
58
+ [s.hasSide]: sidePanel,
59
+ [s.noSide]: !sidePanel,
60
+ [s.topPromo]: isPromoOnTop,
61
+ })}
62
+ hasSidePanel={Boolean(sidePanel)}
63
+ >
44
64
  <div className={s.mainPanel}>
45
- <NavPanel.Column>
65
+ {promo && isPromoOnTop ? (
66
+ <Promo
67
+ {...promo}
68
+ hasSidePanel={Boolean(sidePanel)}
69
+ isPromoOnTop={isPromoOnTop}
70
+ />
71
+ ) : null}
72
+ <NavPanel.Column hasSidePanel={Boolean(sidePanel)}>
46
73
  <ul className={s.productCategories}>
47
74
  {productCategories.map(({ title, products }) => {
48
75
  return (
49
76
  <li key={title} className={s.productCategory}>
50
- <TextPlus.Body className={s.eyebrow}>{title}</TextPlus.Body>
77
+ <Text className={s.eyebrow}>{title}</Text>
51
78
  <ul className={s.productList}>
52
79
  {products.map((product) => {
53
80
  return (
54
81
  <li key={product.url}>
55
- <NavProduct {...product} />
82
+ <NavProduct locale={locale} {...product} />
56
83
  </li>
57
84
  )
58
85
  })}
@@ -62,14 +89,16 @@ const ProductPanel = ({
62
89
  })}
63
90
  </ul>
64
91
  </NavPanel.Column>
65
- {promo ? <Promo {...promo} /> : null}
92
+ {promo && !isPromoOnTop ? (
93
+ <Promo {...promo} hasSidePanel={Boolean(sidePanel)} />
94
+ ) : null}
66
95
  </div>
67
96
 
68
97
  {sidePanel ? (
69
98
  <NavPanel.Column className={s.sidePanel}>
70
- <TextPlus.Body size="200" weight="semibold">
99
+ <Text size="200" weight="semibold">
71
100
  {sidePanel.label}
72
- </TextPlus.Body>
101
+ </Text>
73
102
 
74
103
  {/* ! TODO Replace the below list with the <LinkList /> component,
75
104
  That component will require a refactor to support greater customization
@@ -80,18 +109,22 @@ const ProductPanel = ({
80
109
  {sidePanel.navItems.map(({ icon, title, description, url }) => {
81
110
  return (
82
111
  <li key={title}>
83
- <Link href={parseUrl(url).href} className={s.sidePanelItem}>
112
+ <Link
113
+ aria-label={`${title} - ${description}`}
114
+ {...getLocalizedLinkProps(url, locale)}
115
+ locale={locale ? locale : undefined}
116
+ className={classNames(s.sidePanelItem, s.focusIndicator)}
117
+ >
84
118
  <div className={s.iconBackground}>
85
119
  <FlightIcon
86
- size={16}
87
120
  name={icon}
88
121
  color="var(--token-color-foreground-faint)"
89
122
  />
90
123
  </div>
91
124
  <div className={s.sidePanelItemContent}>
92
- <TextPlus.Body size="200" weight="semibold" tag="span">
125
+ <Text size="200" weight="semibold">
93
126
  {title}
94
- </TextPlus.Body>
127
+ </Text>
95
128
  <span className={s.sidePanelItemDesc}>{description}</span>
96
129
  </div>
97
130
  </Link>
@@ -106,16 +139,19 @@ const ProductPanel = ({
106
139
  }
107
140
 
108
141
  export interface NavProductProps {
142
+ locale?: string
109
143
  url: string
110
144
  product: Products | string
111
145
  description: string
112
146
  }
113
147
 
114
- function NavProduct({ url, product, description }: NavProductProps) {
148
+ function NavProduct({ locale, url, product, description }: NavProductProps) {
115
149
  return (
116
150
  <NavigationMenu.Link asChild>
117
151
  <Link
118
- href={parseUrl(url).href}
152
+ aria-label={`${product} - ${description}`}
153
+ {...getLocalizedLinkProps(url, locale)}
154
+ locale={locale ? locale : undefined}
119
155
  onClickCapture={() => {
120
156
  trackNavClickEvent(product, url, 'products')
121
157
  }}
@@ -125,6 +161,11 @@ function NavProduct({ url, product, description }: NavProductProps) {
125
161
  <div className={s.productLogo}>
126
162
  <FlightIcon
127
163
  name={PRODUCT_LOGO_MAP.get(product) ?? 'hashicorp-fill-color'}
164
+ className={
165
+ PRODUCT_LOGO_EXCEPTION_CLASSNAMES[product]
166
+ ? s[PRODUCT_LOGO_EXCEPTION_CLASSNAMES[product]]
167
+ : undefined
168
+ }
128
169
  />
129
170
  </div>
130
171
  <div className={s.productTextContent}>
@@ -1,11 +1,47 @@
1
+ @custom-media --dev-dot-viewport (min-width: 924px);
2
+
1
3
  .productPanel {
2
4
  height: 100%;
3
5
 
6
+ /* Temporarily included for devdot compatability
7
+ https://app.asana.com/1/90955849329269/project/1209320521286316/task/1210514680913728?focus=true */
8
+ --mds-spacing-01: 0.25rem;
9
+ --mds-spacing-02: 0.375rem;
10
+ --mds-spacing-03: 0.5rem;
11
+ --mds-spacing-04: 0.75rem;
12
+ --mds-spacing-05: 1rem;
13
+ --mds-spacing-06: 1.25rem;
14
+ --mds-spacing-07: 1.5rem;
15
+ --mds-spacing-08: 2rem;
16
+ --mds-spacing-09: 2.5rem;
17
+ --mds-spacing-10: 3rem;
18
+ --mds-spacing-11: 3.5rem;
19
+ --mds-spacing-12: 4rem;
20
+ --mds-spacing-13: 4.5rem;
21
+ --mds-spacing-14: 5.5rem;
22
+ --mds-spacing-15: 6rem;
23
+ --mds-spacing-16: 8rem;
24
+ --mds-spacing-17: 8.5rem;
25
+ --mds-typography-body-200-font-size: 0.875rem;
26
+ --mds-typography-font-weight-regular: 400;
27
+ --mds-typography-font-weight-semibold: 600;
28
+
4
29
  @media (--large) {
5
- display: grid;
6
- grid-template-rows: 1fr;
7
- grid-template-columns: max-content max-content;
8
- padding: 0;
30
+ &.noSide {
31
+ display: grid;
32
+ grid-template-rows: 1fr;
33
+ grid-template-columns: max-content max-content;
34
+ padding: 0;
35
+ }
36
+ }
37
+
38
+ @media (--dev-dot-viewport) {
39
+ &.hasSide {
40
+ display: grid;
41
+ grid-template-rows: 1fr;
42
+ grid-template-columns: max-content max-content;
43
+ padding: 0;
44
+ }
9
45
  }
10
46
  }
11
47
 
@@ -13,10 +49,22 @@
13
49
  list-style: none;
14
50
  padding: 0;
15
51
  display: grid;
16
- margin: var(--hdsplus-spacing-06) 0 0;
52
+ margin: var(--mds-spacing-07) 0 0;
53
+
54
+ @nest .topPromo & {
55
+ margin-bottom: var(--mds-spacing-07);
56
+ }
17
57
 
18
58
  @media (--large) {
19
- margin: 0;
59
+ @nest .noSide & {
60
+ margin: 0;
61
+ }
62
+ }
63
+
64
+ @media (--dev-dot-viewport) {
65
+ @nest .hasSide & {
66
+ margin: 0;
67
+ }
20
68
  }
21
69
  }
22
70
 
@@ -27,18 +75,28 @@
27
75
 
28
76
  &:first-child {
29
77
  border-bottom: 1px solid var(--token-color-border-primary);
30
- margin-bottom: var(--hdsplus-spacing-05);
31
- padding-bottom: var(--hdsplus-spacing-05);
78
+ margin-bottom: var(--mds-spacing-05);
79
+ padding-bottom: var(--mds-spacing-05);
32
80
  }
33
81
  }
34
82
 
35
83
  .eyebrow {
36
- padding-bottom: var(--hdsplus-spacing-04);
84
+ padding-bottom: var(--mds-spacing-04);
37
85
  color: var(--token-color-foreground-strong);
38
- font-weight: var(--font-weight-semi-bold);
86
+ font-weight: var(--mds-typography-font-weight-semibold);
39
87
 
40
88
  @media (--large) {
41
- padding: 0 0 var(--hdsplus-spacing-01) var(--hdsplus-spacing-04);
89
+ @nest .noSide & {
90
+ padding-bottom: 0;
91
+ padding-left: var(--mds-spacing-06);
92
+ }
93
+ }
94
+
95
+ @media (--dev-dot-viewport) {
96
+ @nest .hasSide & {
97
+ padding-bottom: 0;
98
+ padding-left: var(--mds-spacing-06);
99
+ }
42
100
  }
43
101
  }
44
102
 
@@ -50,10 +108,31 @@
50
108
  padding: 0;
51
109
 
52
110
  @media (--large) {
53
- grid-auto-flow: row;
54
- grid-template-columns: repeat(2, 300px);
55
- grid-template-rows: repeat(2, max-content);
56
- gap: 0;
111
+ @nest .noSide & {
112
+ padding: var(--mds-spacing-06) var(--mds-spacing-05)
113
+ var(--mds-spacing-04);
114
+ grid-auto-flow: row;
115
+ grid-template-columns: repeat(2, 300px);
116
+ grid-template-rows: repeat(2, max-content);
117
+ gap: 0;
118
+ }
119
+ }
120
+
121
+ @media (--dev-dot-viewport) {
122
+ @nest .hasSide & {
123
+ padding: var(--mds-spacing-06) var(--mds-spacing-05)
124
+ var(--mds-spacing-04);
125
+ grid-auto-flow: row;
126
+ grid-template-columns: repeat(2, 300px);
127
+ grid-template-rows: repeat(2, max-content);
128
+ gap: 0;
129
+ }
130
+ }
131
+
132
+ & li:last-child {
133
+ & .productWrapper {
134
+ margin-bottom: 0;
135
+ }
57
136
  }
58
137
  }
59
138
 
@@ -61,16 +140,30 @@
61
140
 
62
141
  .productWrapper {
63
142
  display: flex;
64
- margin-bottom: var(--hdsplus-spacing-05);
65
- gap: var(--hdsplus-spacing-05);
143
+ margin-bottom: var(--mds-spacing-05);
144
+ gap: var(--mds-spacing-05);
66
145
 
67
146
  @media (--large) {
68
- padding: var(--hdsplus-spacing-05);
69
- margin-bottom: 0;
147
+ @nest .noSide & {
148
+ padding: var(--mds-spacing-03) var(--mds-spacing-05);
149
+ margin-bottom: 0;
70
150
 
71
- &:hover {
72
- background-color: var(--token-color-surface-interactive-hover);
73
- border-radius: var(--hdsplus-spacing-01);
151
+ &:hover {
152
+ background-color: var(--token-color-surface-interactive-hover);
153
+ border-radius: var(--mds-spacing-01);
154
+ }
155
+ }
156
+ }
157
+
158
+ @media (--dev-dot-viewport) {
159
+ @nest .hasSide & {
160
+ padding: var(--mds-spacing-03) var(--mds-spacing-05);
161
+ margin-bottom: 0;
162
+
163
+ &:hover {
164
+ background-color: var(--token-color-surface-interactive-hover);
165
+ border-radius: var(--mds-spacing-01);
166
+ }
74
167
  }
75
168
  }
76
169
  }
@@ -81,10 +174,23 @@
81
174
  & svg {
82
175
  height: 42px;
83
176
  width: 42px;
177
+ }
178
+
179
+ @nest .noSide & {
180
+ & svg {
181
+ @media (--large) {
182
+ height: var(--mds-spacing-08);
183
+ width: var(--mds-spacing-08);
184
+ }
185
+ }
186
+ }
84
187
 
85
- @media (--large) {
86
- height: var(--hdsplus-spacing-08);
87
- width: var(--hdsplus-spacing-08);
188
+ @nest .hasSide & {
189
+ & svg {
190
+ @media (--dev-dot-viewport) {
191
+ height: var(--mds-spacing-08);
192
+ width: var(--mds-spacing-08);
193
+ }
88
194
  }
89
195
  }
90
196
  }
@@ -117,8 +223,25 @@
117
223
  flex-direction: column;
118
224
 
119
225
  @media (--large) {
120
- padding: var(--hdsplus-spacing-05) 0 var(--hdsplus-spacing-05)
121
- var(--hdsplus-spacing-04);
226
+ @nest .noSide & {
227
+ padding: var(--mds-spacing-07) 0 var(--mds-spacing-09)
228
+ var(--mds-spacing-04);
229
+ }
230
+
231
+ @nest .topPromo & {
232
+ padding-bottom: var(--mds-spacing-04);
233
+ }
234
+ }
235
+
236
+ @media (--dev-dot-viewport) {
237
+ @nest .hasSide & {
238
+ padding: var(--mds-spacing-07) 0 var(--mds-spacing-09)
239
+ var(--mds-spacing-04);
240
+ }
241
+
242
+ @nest .topPromo & {
243
+ padding-bottom: var(--mds-spacing-04);
244
+ }
122
245
  }
123
246
  }
124
247
 
@@ -127,10 +250,10 @@
127
250
  background: var(--token-color-surface-faint);
128
251
  display: flex;
129
252
  flex-direction: column;
130
- gap: var(--hdsplus-spacing-05);
253
+ gap: var(--mds-spacing-05);
131
254
  margin: 0;
132
- padding: var(--hdsplus-spacing-05) var(--hdsplus-spacing-07)
133
- var(--hdsplus-spacing-09) var(--hdsplus-spacing-07);
255
+ padding: var(--mds-spacing-07) var(--mds-spacing-04)
256
+ var(--mds-spacing-09) var(--mds-spacing-07);
134
257
  }
135
258
 
136
259
  .sidePanelItems {
@@ -139,26 +262,24 @@
139
262
  padding: 0;
140
263
  display: grid;
141
264
  grid-template-columns: 1fr;
142
- gap: var(--hdsplus-spacing-07);
265
+ gap: var(--mds-spacing-07);
143
266
 
144
- @media (--large) {
145
- gap: var(--hdsplus-spacing-05);
267
+ @media (--dev-dot-viewport) {
268
+ gap: var(--mds-spacing-05);
146
269
  }
147
270
  }
148
271
 
149
272
  /* Lifted from .productWrapper */
150
273
  .sidePanelItem {
151
274
  display: flex;
152
- margin-bottom: var(--hdsplus-spacing-05);
153
- gap: var(--hdsplus-spacing-05);
275
+ gap: var(--mds-spacing-05);
154
276
 
155
- @media (--large) {
156
- padding: var(--hdsplus-spacing-03) var(--hdsplus-spacing-05);
157
- margin-bottom: 0;
277
+ @media (--dev-dot-viewport) {
278
+ padding: var(--mds-spacing-03) var(--mds-spacing-05);
158
279
 
159
280
  &:hover {
160
281
  background-color: var(--token-color-surface-interactive-hover);
161
- border-radius: var(--hdsplus-spacing-01);
282
+ border-radius: var(--mds-spacing-01);
162
283
  }
163
284
  }
164
285
  }
@@ -167,8 +288,8 @@
167
288
  packages/react-components/src/components/nav-panel/components/link-list/style.module.css */
168
289
  .iconBackground {
169
290
  background: var(--token-color-foreground-high-contrast);
170
- height: var(--hdsplus-spacing-08);
171
- width: var(--hdsplus-spacing-08);
291
+ height: var(--mds-spacing-08);
292
+ width: var(--mds-spacing-08);
172
293
  display: flex;
173
294
  align-items: center;
174
295
  justify-content: center;
@@ -191,3 +312,39 @@ packages/react-components/src/components/nav-panel/components/link-list/style.mo
191
312
  line-height: 18px;
192
313
  color: var(--token-color-foreground-primary);
193
314
  }
315
+
316
+ /* Branding exception classnames */
317
+
318
+ /* Allows switching to a dark-mode-specific color for Terraform */
319
+ .terraform {
320
+ & use {
321
+ fill: var(--token-color-terraform-brand);
322
+
323
+ @nest [data-theme='dark'] & {
324
+ fill: var(--token-color-terraform-foreground);
325
+ }
326
+ }
327
+
328
+ & path {
329
+ fill: inherit;
330
+ }
331
+ }
332
+
333
+ /* Styles for HCP Vault mimic the spec for `product-square` icons,
334
+ while allowing the symbol inside the icon border to change color
335
+ based on the site's theme
336
+ */
337
+ .hcpVaultSecrets,
338
+ .hcpVaultRadar {
339
+ padding: var(--mds-spacing-01);
340
+ border: 2px solid var(--token-color-vault-brand);
341
+ border-radius: 3px;
342
+
343
+ & use {
344
+ fill: var(--token-color-foreground-strong);
345
+ }
346
+
347
+ & path {
348
+ fill: inherit;
349
+ }
350
+ }
@@ -12,12 +12,14 @@ interface StandardPanelProps {
12
12
  navData: NavItem[]
13
13
  promo?: PromoProps
14
14
  sectionName?: string
15
+ locale?: any
15
16
  }
16
17
 
17
18
  const StandardPanel = ({
18
19
  navData,
19
20
  promo,
20
21
  sectionName = '',
22
+ locale,
21
23
  }: StandardPanelProps) => {
22
24
  return (
23
25
  <NavPanel.Root>
@@ -26,6 +28,7 @@ const StandardPanel = ({
26
28
  return (
27
29
  <NavPanel.Column key={stableIdx}>
28
30
  <LinkList
31
+ locale={locale}
29
32
  links={navItems}
30
33
  linkClickHandler={(text, href) => {
31
34
  trackNavClickEvent(text, href, sectionName)
@@ -1,8 +1,8 @@
1
1
  .columns {
2
2
  display: flex;
3
- gap: var(--hdsplus-spacing-03);
3
+ gap: var(--mds-spacing-03);
4
4
  flex-direction: column;
5
- margin-top: var(--hdsplus-spacing-06);
5
+ margin-top: var(--mds-spacing-07);
6
6
 
7
7
  @media (--large) {
8
8
  flex-direction: row;
@@ -0,0 +1,30 @@
1
+ import { getLocalizedLinkProps } from '.'
2
+
3
+ describe('getLocalizedLinkProps', () => {
4
+ it('does not format localized homepage', () => {
5
+ const localizedLinkProps = getLocalizedLinkProps('/en', 'ja')
6
+ expect(localizedLinkProps.href).toBe('/en')
7
+ expect(localizedLinkProps.hrefLang).toBe('en')
8
+ })
9
+
10
+ it('does not format localized path hrefs', () => {
11
+ const localizedLinkProps = getLocalizedLinkProps('/ja/solutions', 'de')
12
+ expect(localizedLinkProps.href).toBe('/ja/solutions')
13
+ expect(localizedLinkProps.hrefLang).toBe('ja')
14
+ })
15
+
16
+ it('does format paths that start with a locale string', () => {
17
+ const localizedLinkProps = getLocalizedLinkProps(
18
+ '/deconstructing-cloud',
19
+ 'en'
20
+ )
21
+ expect(localizedLinkProps.href).toBe('/en/deconstructing-cloud')
22
+ expect(localizedLinkProps.hrefLang).toBe('en')
23
+ })
24
+
25
+ it('does not format external paths', () => {
26
+ const localizedLinkProps = getLocalizedLinkProps('https://google.com', 'ja')
27
+ expect(localizedLinkProps.href).toBe('https://google.com')
28
+ expect(localizedLinkProps.hrefLang).toBe(undefined)
29
+ })
30
+ })
@@ -0,0 +1,45 @@
1
+ import { parseUrl } from '../parse-url'
2
+ import { allSupportedLocales } from '../supported-locales'
3
+
4
+ export const getLocalizedLinkProps = (
5
+ url: string,
6
+ locale?: string
7
+ ): {
8
+ href: string
9
+ hrefLang: string | undefined
10
+ } => {
11
+ const parsedHref = parseUrl(url).href
12
+
13
+ // example: /en
14
+ const hrefIsLocalizedHomepage = allSupportedLocales.includes(
15
+ parsedHref.substring(1)
16
+ )
17
+
18
+ // true example: /en/solutions
19
+ // false example: /entry-point
20
+ const hrefIsLocalized =
21
+ allSupportedLocales.includes(parsedHref.substring(1, 3)) &&
22
+ parsedHref[3] === '/'
23
+
24
+ // If the href is already localized:
25
+ // - do not format the href
26
+ // - use the locale from the href for the hrefLang
27
+ if (hrefIsLocalizedHomepage || hrefIsLocalized) {
28
+ return {
29
+ href: parsedHref,
30
+ hrefLang: parsedHref.substring(1, 3),
31
+ }
32
+ }
33
+
34
+ if (parsedHref.startsWith('/') && locale) {
35
+ return {
36
+ href: `/${locale}${parsedHref}`,
37
+ hrefLang: locale,
38
+ }
39
+ }
40
+
41
+ return {
42
+ href: parsedHref,
43
+ hrefLang: undefined,
44
+ }
45
+ }
@@ -1,4 +1,4 @@
1
- import { parseUrl } from '.'
1
+ import { parseUrl, hasHashiCorpDestination } from '.'
2
2
 
3
3
  describe('parseUrl', () => {
4
4
  it('detects hashicorp.com URLs as internal', () => {
@@ -7,6 +7,12 @@ describe('parseUrl', () => {
7
7
  expect(url.linkType).toBe('inbound')
8
8
  })
9
9
 
10
+ it('detects www.hashicorp.com URLs as internal', () => {
11
+ const url = parseUrl('https://hashicorp.com/foo')
12
+ expect(url.href).toBe('/foo')
13
+ expect(url.linkType).toBe('inbound')
14
+ })
15
+
10
16
  it('detects URLs not containing hashicorp.com as external', () => {
11
17
  const externalUrl = 'https://google.com/example'
12
18
  const url = parseUrl(externalUrl)
@@ -27,4 +33,37 @@ describe('parseUrl', () => {
27
33
  )
28
34
  expect(url.href).toBe('/resources?type=case-study&products=terraform')
29
35
  })
36
+
37
+ it('leaves URL hash intact', () => {
38
+ const url = parseUrl('https://hashicorp.com/about#leadership')
39
+ expect(url.href).toBe('/about#leadership')
40
+ })
41
+
42
+ it('leaves HashiCorp subdomain URLs intact', () => {
43
+ const url = parseUrl('https://developer.hashicorp.com/terraform')
44
+ expect(url.href).toBe('https://developer.hashicorp.com/terraform')
45
+ })
46
+ })
47
+
48
+ describe('hasHashiCorpDestination', () => {
49
+ it('returns true for supported hashicorp domains', () => {
50
+ const isHashiCorpDestination = hasHashiCorpDestination(
51
+ 'https://portal.cloud.hashicorp.com/foo'
52
+ )
53
+ expect(isHashiCorpDestination).toBe(true)
54
+ })
55
+ it('returns false for external domains', () => {
56
+ const isHashiCorpDestination = hasHashiCorpDestination(
57
+ 'https://foo.com/bar'
58
+ )
59
+ expect(isHashiCorpDestination).toBe(false)
60
+ })
61
+ it('returns true for relative links', () => {
62
+ const isHashiCorpDestination = hasHashiCorpDestination('/bar')
63
+ expect(isHashiCorpDestination).toBe(true)
64
+ })
65
+ it('returns true for hashes', () => {
66
+ const isHashiCorpDestination = hasHashiCorpDestination('#someHash')
67
+ expect(isHashiCorpDestination).toBe(true)
68
+ })
30
69
  })