@hashicorp/react-components 0.0.0-canary.f090ec2 → 0.0.0-canary.f4b4be1

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 +68 -17
  14. package/src/components/nav-panel/product-panel/style.module.css +208 -44
  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,30 +89,46 @@ 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" className={s.sidePanelText}>
71
100
  {sidePanel.label}
72
- </TextPlus.Body>
101
+ </Text>
102
+
103
+ {/* ! TODO Replace the below list with the <LinkList /> component,
104
+ That component will require a refactor to support greater customization
105
+ Ticket:
106
+ https://app.asana.com/0/1206176289707208/1208162145034615/f
107
+ */}
73
108
  <ul className={s.sidePanelItems}>
74
109
  {sidePanel.navItems.map(({ icon, title, description, url }) => {
75
110
  return (
76
111
  <li key={title}>
77
- <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
+ >
78
118
  <div className={s.iconBackground}>
79
119
  <FlightIcon
80
- size={16}
81
120
  name={icon}
82
121
  color="var(--token-color-foreground-faint)"
83
122
  />
84
123
  </div>
85
124
  <div className={s.sidePanelItemContent}>
86
- <TextPlus.Body size="200" weight="semibold" tag="span">
125
+ <Text
126
+ size="200"
127
+ weight="semibold"
128
+ className={s.sidePanelText}
129
+ >
87
130
  {title}
88
- </TextPlus.Body>
131
+ </Text>
89
132
  <span className={s.sidePanelItemDesc}>{description}</span>
90
133
  </div>
91
134
  </Link>
@@ -100,16 +143,19 @@ const ProductPanel = ({
100
143
  }
101
144
 
102
145
  export interface NavProductProps {
146
+ locale?: string
103
147
  url: string
104
148
  product: Products | string
105
149
  description: string
106
150
  }
107
151
 
108
- function NavProduct({ url, product, description }: NavProductProps) {
152
+ function NavProduct({ locale, url, product, description }: NavProductProps) {
109
153
  return (
110
154
  <NavigationMenu.Link asChild>
111
155
  <Link
112
- href={parseUrl(url).href}
156
+ aria-label={`${product} - ${description}`}
157
+ {...getLocalizedLinkProps(url, locale)}
158
+ locale={locale ? locale : undefined}
113
159
  onClickCapture={() => {
114
160
  trackNavClickEvent(product, url, 'products')
115
161
  }}
@@ -119,6 +165,11 @@ function NavProduct({ url, product, description }: NavProductProps) {
119
165
  <div className={s.productLogo}>
120
166
  <FlightIcon
121
167
  name={PRODUCT_LOGO_MAP.get(product) ?? 'hashicorp-fill-color'}
168
+ className={
169
+ PRODUCT_LOGO_EXCEPTION_CLASSNAMES[product]
170
+ ? s[PRODUCT_LOGO_EXCEPTION_CLASSNAMES[product]]
171
+ : undefined
172
+ }
122
173
  />
123
174
  </div>
124
175
  <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,29 @@
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) var(--mds-spacing-04);
113
+ grid-auto-flow: row;
114
+ grid-template-columns: repeat(2, 300px);
115
+ grid-template-rows: repeat(2, max-content);
116
+ gap: 0;
117
+ }
118
+ }
119
+
120
+ @media (--dev-dot-viewport) {
121
+ @nest .hasSide & {
122
+ padding: var(--mds-spacing-06) var(--mds-spacing-05) var(--mds-spacing-04);
123
+ grid-auto-flow: row;
124
+ grid-template-columns: repeat(2, 300px);
125
+ grid-template-rows: repeat(2, max-content);
126
+ gap: 0;
127
+ }
128
+ }
129
+
130
+ & li:last-child {
131
+ & .productWrapper {
132
+ margin-bottom: 0;
133
+ }
57
134
  }
58
135
  }
59
136
 
@@ -61,16 +138,30 @@
61
138
 
62
139
  .productWrapper {
63
140
  display: flex;
64
- margin-bottom: var(--hdsplus-spacing-05);
65
- gap: var(--hdsplus-spacing-05);
141
+ margin-bottom: var(--mds-spacing-05);
142
+ gap: var(--mds-spacing-05);
66
143
 
67
144
  @media (--large) {
68
- padding: var(--hdsplus-spacing-05);
69
- margin-bottom: 0;
145
+ @nest .noSide & {
146
+ padding: var(--mds-spacing-03) var(--mds-spacing-05);
147
+ margin-bottom: 0;
70
148
 
71
- &:hover {
72
- background-color: var(--token-color-surface-interactive-hover);
73
- border-radius: var(--hdsplus-spacing-01);
149
+ &:hover {
150
+ background-color: var(--token-color-surface-interactive-hover);
151
+ border-radius: var(--mds-spacing-01);
152
+ }
153
+ }
154
+ }
155
+
156
+ @media (--dev-dot-viewport) {
157
+ @nest .hasSide & {
158
+ padding: var(--mds-spacing-03) var(--mds-spacing-05);
159
+ margin-bottom: 0;
160
+
161
+ &:hover {
162
+ background-color: var(--token-color-surface-interactive-hover);
163
+ border-radius: var(--mds-spacing-01);
164
+ }
74
165
  }
75
166
  }
76
167
  }
@@ -81,10 +172,23 @@
81
172
  & svg {
82
173
  height: 42px;
83
174
  width: 42px;
175
+ }
84
176
 
85
- @media (--large) {
86
- height: var(--hdsplus-spacing-08);
87
- width: var(--hdsplus-spacing-08);
177
+ @nest .noSide & {
178
+ & svg {
179
+ @media (--large) {
180
+ height: var(--mds-spacing-08);
181
+ width: var(--mds-spacing-08);
182
+ }
183
+ }
184
+ }
185
+
186
+ @nest .hasSide & {
187
+ & svg {
188
+ @media (--dev-dot-viewport) {
189
+ height: var(--mds-spacing-08);
190
+ width: var(--mds-spacing-08);
191
+ }
88
192
  }
89
193
  }
90
194
  }
@@ -116,21 +220,42 @@
116
220
  justify-content: space-between;
117
221
  flex-direction: column;
118
222
 
223
+ @nest .topPromo & {
224
+ justify-content: flex-start;
225
+ }
226
+
119
227
  @media (--large) {
120
- padding: var(--hdsplus-spacing-05) 0 var(--hdsplus-spacing-05)
121
- var(--hdsplus-spacing-04);
228
+ @nest .noSide & {
229
+ padding: var(--mds-spacing-07) 0 var(--mds-spacing-09)
230
+ var(--mds-spacing-04);
231
+ }
232
+
233
+ @nest .topPromo & {
234
+ padding-bottom: var(--mds-spacing-04);
235
+ }
236
+ }
237
+
238
+ @media (--dev-dot-viewport) {
239
+ @nest .hasSide & {
240
+ padding: var(--mds-spacing-07) 0 var(--mds-spacing-09)
241
+ var(--mds-spacing-04);
242
+ }
243
+
244
+ @nest .topPromo & {
245
+ padding-bottom: var(--mds-spacing-04);
246
+ }
122
247
  }
123
248
  }
124
249
 
125
250
  .sidePanel {
126
251
  height: 100%;
127
- background: var(--token-color-surface-strong);
252
+ background: var(--token-color-surface-faint);
128
253
  display: flex;
129
254
  flex-direction: column;
130
- gap: var(--hdsplus-spacing-05);
255
+ gap: var(--mds-spacing-05);
131
256
  margin: 0;
132
- padding: var(--hdsplus-spacing-05) var(--hdsplus-spacing-07)
133
- var(--hdsplus-spacing-09) var(--hdsplus-spacing-07);
257
+ padding: var(--mds-spacing-07) var(--mds-spacing-04) var(--mds-spacing-09)
258
+ var(--mds-spacing-07);
134
259
  }
135
260
 
136
261
  .sidePanelItems {
@@ -139,26 +264,24 @@
139
264
  padding: 0;
140
265
  display: grid;
141
266
  grid-template-columns: 1fr;
142
- gap: var(--hdsplus-spacing-07);
267
+ gap: var(--mds-spacing-07);
143
268
 
144
- @media (--large) {
145
- gap: var(--hdsplus-spacing-05);
269
+ @media (--dev-dot-viewport) {
270
+ gap: var(--mds-spacing-05);
146
271
  }
147
272
  }
148
273
 
149
274
  /* Lifted from .productWrapper */
150
275
  .sidePanelItem {
151
276
  display: flex;
152
- margin-bottom: var(--hdsplus-spacing-05);
153
- gap: var(--hdsplus-spacing-05);
277
+ gap: var(--mds-spacing-05);
154
278
 
155
- @media (--large) {
156
- padding: var(--hdsplus-spacing-03) var(--hdsplus-spacing-05);
157
- margin-bottom: 0;
279
+ @media (--dev-dot-viewport) {
280
+ padding: var(--mds-spacing-03) var(--mds-spacing-05);
158
281
 
159
282
  &:hover {
160
- background-color: var(--token-color-surface-interactive-active);
161
- border-radius: var(--hdsplus-spacing-01);
283
+ background-color: var(--token-color-surface-interactive-hover);
284
+ border-radius: var(--mds-spacing-01);
162
285
  }
163
286
  }
164
287
  }
@@ -166,15 +289,16 @@
166
289
  /* Lifted from:
167
290
  packages/react-components/src/components/nav-panel/components/link-list/style.module.css */
168
291
  .iconBackground {
169
- background: var(--token-color-page-primary);
170
- height: var(--hdsplus-spacing-08);
171
- width: var(--hdsplus-spacing-08);
292
+ background: var(--token-color-foreground-high-contrast);
293
+ height: var(--mds-spacing-08);
294
+ width: var(--mds-spacing-08);
172
295
  display: flex;
173
296
  align-items: center;
174
297
  justify-content: center;
175
298
  border-radius: 3px;
176
299
  flex: none;
177
300
  margin-top: 3px;
301
+ border: 1px solid var(--token-color-border-primary);
178
302
  }
179
303
 
180
304
  /* Lifted from .productTextContent */
@@ -190,3 +314,43 @@ packages/react-components/src/components/nav-panel/components/link-list/style.mo
190
314
  line-height: 18px;
191
315
  color: var(--token-color-foreground-primary);
192
316
  }
317
+
318
+ /* Branding exception classnames */
319
+
320
+ /* Allows switching to a dark-mode-specific color for Terraform */
321
+ .terraform {
322
+ & use {
323
+ fill: var(--token-color-terraform-brand);
324
+
325
+ @nest [data-theme='dark'] & {
326
+ fill: var(--token-color-terraform-foreground);
327
+ }
328
+ }
329
+
330
+ & path {
331
+ fill: inherit;
332
+ }
333
+ }
334
+
335
+ /* Styles for HCP Vault mimic the spec for `product-square` icons,
336
+ while allowing the symbol inside the icon border to change color
337
+ based on the site's theme
338
+ */
339
+ .hcpVaultSecrets,
340
+ .hcpVaultRadar {
341
+ padding: var(--mds-spacing-01);
342
+ border: 2px solid var(--token-color-vault-brand);
343
+ border-radius: 3px;
344
+
345
+ & use {
346
+ fill: var(--token-color-foreground-strong);
347
+ }
348
+
349
+ & path {
350
+ fill: inherit;
351
+ }
352
+ }
353
+
354
+ .sidePanelText {
355
+ font-weight: var(--mds-typography-font-weight-semibold);
356
+ }
@@ -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
  })