@hashicorp/react-components 0.0.0-canary.23632af → 0.0.0-canary.32599ff

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 (102) hide show
  1. package/package.json +6 -5
  2. package/src/components/actions/index.tsx +5 -5
  3. package/src/components/call-to-action/index.tsx +12 -10
  4. package/src/components/call-to-action/styles/variant-centered.module.css +5 -4
  5. package/src/components/call-to-action/styles/variant-compact-grid.module.css +6 -4
  6. package/src/components/call-to-action/styles/variant-compact.module.css +6 -4
  7. package/src/components/call-to-action/styles/variant-links.module.css +6 -11
  8. package/src/components/card/index.test.tsx +1 -1
  9. package/src/components/card/newsroom-card/index.test.tsx +1 -1
  10. package/src/components/card/newsroom-card/style.module.css +1 -1
  11. package/src/components/card/partner-card/index.tsx +5 -5
  12. package/src/components/card/person-card/index.tsx +3 -3
  13. package/src/components/card/primitives.tsx +20 -19
  14. package/src/components/card/resource-card/index.test.tsx +1 -1
  15. package/src/components/card/resource-card/index.tsx +3 -0
  16. package/src/components/card/style.module.css +2 -2
  17. package/src/components/card/types.ts +2 -2
  18. package/src/components/card/unified-card/index.spec.tsx +40 -0
  19. package/src/components/card/unified-card/index.tsx +5 -11
  20. package/src/components/inline-video/index.tsx +104 -0
  21. package/src/components/inline-video/style.module.css +99 -0
  22. package/src/components/intro/style.module.css +2 -2
  23. package/src/components/marketo-form/contexts/FormMetadata.ts +2 -0
  24. package/src/components/marketo-form/form/index.tsx +12 -7
  25. package/src/components/marketo-form/partials/fields/checkbox-field/index.tsx +1 -1
  26. package/src/components/marketo-form/partials/fields/email-field/index.tsx +1 -1
  27. package/src/components/marketo-form/partials/fields/form-page-url-field/index.tsx +12 -1
  28. package/src/components/marketo-form/partials/fields/hidden-field/index.tsx +1 -0
  29. package/src/components/marketo-form/partials/fields/htmltext-field/style.module.css +2 -2
  30. package/src/components/marketo-form/partials/fields/radio-buttons-field/index.tsx +1 -1
  31. package/src/components/marketo-form/partials/fields/select-field/index.tsx +1 -1
  32. package/src/components/marketo-form/partials/fields/telephone-field/index.tsx +1 -1
  33. package/src/components/marketo-form/partials/fields/text-field/index.tsx +1 -1
  34. package/src/components/marketo-form/partials/fields/textarea-field/index.tsx +1 -1
  35. package/src/components/marketo-form/types.ts +10 -12
  36. package/src/components/marketo-form/utils.ts +1 -1
  37. package/src/components/nav-panel/components/link-list/index.tsx +15 -9
  38. package/src/components/nav-panel/components/link-list/style.module.css +10 -10
  39. package/src/components/nav-panel/components/panel/column/style.module.css +3 -3
  40. package/src/components/nav-panel/components/panel/root/style.module.css +6 -6
  41. package/src/components/nav-panel/components/promo/index.tsx +15 -9
  42. package/src/components/nav-panel/components/promo/style.module.css +23 -13
  43. package/src/components/nav-panel/product-panel/index.tsx +36 -25
  44. package/src/components/nav-panel/product-panel/style.module.css +45 -41
  45. package/src/components/nav-panel/standard-panel/index.tsx +3 -0
  46. package/src/components/nav-panel/standard-panel/style.module.css +2 -2
  47. package/src/components/next-steps/index.tsx +3 -2
  48. package/src/components/next-steps/style.module.css +18 -18
  49. package/src/components/product-badge/index.tsx +1 -1
  50. package/src/components/product-badge/style.module.css +2 -2
  51. package/src/components/products-used/style.module.css +7 -7
  52. package/src/components/related-content/index.tsx +28 -29
  53. package/src/components/related-content/style.module.css +58 -60
  54. package/src/components/related-content/types.ts +2 -2
  55. package/src/components/subnav/index.tsx +12 -3
  56. package/src/components/subnav/partials/CtaLinks/github-stars-link/style.module.css +8 -12
  57. package/src/components/subnav/partials/CtaLinks/index.tsx +9 -8
  58. package/src/components/subnav/partials/CtaLinks/style.module.css +6 -6
  59. package/src/components/subnav/partials/DropdownTrigger/index.tsx +2 -2
  60. package/src/components/subnav/partials/DropdownTrigger/style.module.css +11 -8
  61. package/src/components/subnav/partials/MenuItemsDefault/index.tsx +24 -8
  62. package/src/components/subnav/partials/MenuItemsDefault/style.module.css +44 -31
  63. package/src/components/subnav/partials/MenuItemsOverflow/index.tsx +17 -4
  64. package/src/components/subnav/partials/MenuItemsOverflow/style.module.css +24 -18
  65. package/src/components/subnav/partials/TitleLink/index.tsx +6 -4
  66. package/src/components/subnav/partials/TitleLink/mktg-logos/all-community-product-logos_on-dark.ts +2 -1
  67. package/src/components/subnav/partials/TitleLink/mktg-logos/all-community-product-logos_on-light.ts +2 -1
  68. package/src/components/subnav/partials/TitleLink/style.module.css +3 -3
  69. package/src/components/subnav/partials/nav-item-text/style.module.css +2 -4
  70. package/src/components/subnav/style.module.css +5 -7
  71. package/src/lib/get-localized-link-props/index.test.ts +30 -0
  72. package/src/lib/get-localized-link-props/index.ts +45 -0
  73. package/src/lib/parse-url/index.test.ts +40 -1
  74. package/src/lib/parse-url/index.ts +75 -9
  75. package/src/lib/supported-locales/index.ts +1 -0
  76. package/src/lib/track-nav-click-event/index.ts +4 -4
  77. package/src/lib/v/index.ts +4 -5
  78. package/src/components/consent-manager/README.md +0 -73
  79. package/src/components/consent-manager/components/banner/banner.module.css +0 -23
  80. package/src/components/consent-manager/components/banner/banner.test.tsx +0 -31
  81. package/src/components/consent-manager/components/banner/banner.tsx +0 -73
  82. package/src/components/consent-manager/components/dialog/dialog.module.css +0 -145
  83. package/src/components/consent-manager/components/dialog/dialog.test.tsx +0 -194
  84. package/src/components/consent-manager/components/dialog/dialog.tsx +0 -339
  85. package/src/components/consent-manager/img/icn_close.tsx +0 -19
  86. package/src/components/consent-manager/index.test.tsx +0 -334
  87. package/src/components/consent-manager/index.tsx +0 -186
  88. package/src/components/consent-manager/loader/README.md +0 -29
  89. package/src/components/consent-manager/loader/categories.ts +0 -22
  90. package/src/components/consent-manager/loader/index.tsx +0 -94
  91. package/src/components/consent-manager/loader/presets/enterprise.ts +0 -12
  92. package/src/components/consent-manager/loader/presets/oss.ts +0 -10
  93. package/src/components/consent-manager/loader/services.ts +0 -45
  94. package/src/components/consent-manager/scripts/custom.tsx +0 -59
  95. package/src/components/consent-manager/scripts/segment.tsx +0 -87
  96. package/src/components/consent-manager/style.module.css +0 -9
  97. package/src/components/consent-manager/types.ts +0 -55
  98. package/src/components/consent-manager/util/cookies.test.ts +0 -75
  99. package/src/components/consent-manager/util/cookies.ts +0 -70
  100. package/src/components/consent-manager/util/integrations.test.ts +0 -115
  101. package/src/components/consent-manager/util/integrations.ts +0 -110
  102. package/src/components/subnav/theme.module.css +0 -50
@@ -9,22 +9,30 @@ import NavItemText from '../nav-item-text'
9
9
  import DropdownTrigger from '../DropdownTrigger'
10
10
  import * as NavigationMenu from '@radix-ui/react-navigation-menu'
11
11
  import s from './style.module.css'
12
+ import FlightIcon from '@hashicorp/react-mds/src/hds/components/flight-icon'
13
+ import { getLocalizedLinkProps } from '../../../../lib/get-localized-link-props'
12
14
 
13
15
  interface MenuItemsDefaultProps {
14
16
  menuItems: (
15
17
  | {
16
18
  text: string
17
19
  url: string
18
- submenu: { text: string; url: string; _isActiveUrl: string }[]
20
+ submenu: {
21
+ icon: string
22
+ text: string
23
+ url: string
24
+ _isActiveUrl: string
25
+ }[]
19
26
  _isActiveUrl: boolean
20
27
  }
21
28
  | 'divider'
22
29
  )[]
23
30
  activatePanel: (panel: string) => void
31
+ locale?: string
24
32
  }
25
33
 
26
34
  function MenuItemsDefault(props: MenuItemsDefaultProps) {
27
- const { menuItems, activatePanel } = props
35
+ const { menuItems, activatePanel, locale } = props
28
36
  return (
29
37
  <NavigationMenu.List className={s.root}>
30
38
  {menuItems.map((menuItem, stableIdx) => {
@@ -41,6 +49,7 @@ function MenuItemsDefault(props: MenuItemsDefaultProps) {
41
49
  text={text}
42
50
  submenu={submenu}
43
51
  activatePanel={activatePanel}
52
+ locale={locale}
44
53
  />
45
54
  ) : (
46
55
  <NavLink
@@ -50,6 +59,7 @@ function MenuItemsDefault(props: MenuItemsDefaultProps) {
50
59
  text={text}
51
60
  url={url}
52
61
  isActive={_isActiveUrl}
62
+ locale={locale}
53
63
  />
54
64
  )
55
65
  })}
@@ -69,14 +79,15 @@ interface NavLinkProps {
69
79
  text: string
70
80
  url: string
71
81
  isActive: boolean
82
+ locale?: string
72
83
  }
73
84
 
74
85
  function NavLink(props: NavLinkProps) {
75
- const { text, url, isActive } = props
86
+ const { text, url, isActive, locale } = props
76
87
  return (
77
88
  <NavigationMenu.Item className={s.listItem}>
78
89
  <NavigationMenu.Link asChild>
79
- <Link className={s.navLink} href={url}>
90
+ <Link className={s.navLink} {...getLocalizedLinkProps(url, locale)}>
80
91
  <NavItemText isActive={isActive} text={text} />
81
92
  </Link>
82
93
  </NavigationMenu.Link>
@@ -86,8 +97,9 @@ function NavLink(props: NavLinkProps) {
86
97
 
87
98
  interface NavLinkWithDropdownProps {
88
99
  text: string
89
- submenu: { text: string; url: string; _isActiveUrl: string }[]
100
+ submenu: { icon: string; text: string; url: string; _isActiveUrl: string }[]
90
101
  activatePanel: (panel: string) => void
102
+ locale?: string
91
103
  }
92
104
 
93
105
  interface NavLinkWithDropdownState {
@@ -99,7 +111,7 @@ class NavLinkWithDropdown extends React.Component<
99
111
  NavLinkWithDropdownState
100
112
  > {
101
113
  render() {
102
- const { text, submenu, activatePanel } = this.props
114
+ const { text, submenu, activatePanel, locale } = this.props
103
115
  const hasActiveChild = submenu.some((s) => s._isActiveUrl)
104
116
 
105
117
  const handleFocusOutside = () => {
@@ -128,12 +140,16 @@ class NavLinkWithDropdown extends React.Component<
128
140
  <NavigationMenu.Sub>
129
141
  <NavigationMenu.List className={s.dropdownList}>
130
142
  {submenu.map((submenuItem, stableIdx) => {
131
- const { text, url } = submenuItem
143
+ const { icon, text, url } = submenuItem
132
144
  return (
133
145
  // eslint-disable-next-line react/no-array-index-key
134
146
  <NavigationMenu.Item key={stableIdx}>
135
147
  <NavigationMenu.Link asChild>
136
- <Link className={s.submenuItem} href={url}>
148
+ <Link
149
+ className={s.submenuItem}
150
+ {...getLocalizedLinkProps(url, locale)}
151
+ >
152
+ {icon ? <FlightIcon name={icon} /> : null}
137
153
  {text}
138
154
  </Link>
139
155
  </NavigationMenu.Link>
@@ -3,11 +3,9 @@
3
3
  * SPDX-License-Identifier: MPL-2.0
4
4
  */
5
5
 
6
- /* Note: consumes from theme.module.css, as marked */
7
-
8
6
  .root {
9
7
  display: flex;
10
- padding: 0 0 0 var(--hdsplus-spacing-08);
8
+ padding: 0 0 0 var(--mds-spacing-08);
11
9
  margin: 0;
12
10
  align-items: center;
13
11
  justify-content: flex-end;
@@ -16,23 +14,22 @@
16
14
 
17
15
  .listItem {
18
16
  position: relative;
19
- white-space: nowrap;
20
17
  margin: 0;
21
18
  padding: 0;
22
19
  }
23
20
 
24
21
  .navLink {
25
- composes: hds-typography-body-200 from global;
26
- composes: hds-font-weight-medium from global;
22
+ composes: token-typography-body-200 from global;
23
+ composes: mds-typography-font-weight-medium from global;
27
24
  position: relative;
28
- padding: var(--hdsplus-spacing-04);
25
+ padding: var(--mds-spacing-04);
29
26
  display: flex;
30
27
  align-items: center;
31
28
  white-space: nowrap;
32
29
 
33
30
  &:hover {
34
- background-color: var(--menu-item-hover-color);
35
- border-radius: var(--hdsplus-spacing-01);
31
+ background-color: var(--token-color-surface-interactive-hover);
32
+ border-radius: var(--mds-spacing-01);
36
33
  }
37
34
 
38
35
  &:focus {
@@ -42,43 +39,58 @@
42
39
  }
43
40
 
44
41
  .submenuItem {
45
- composes: hds-typography-body-200 from global;
46
- composes: hds-font-weight-medium from global;
47
- display: block;
48
- color: var(--menu-item-text-color); /* from theme.module.css */
42
+ --submenu-item-background-color: transparent;
43
+
44
+ position: relative;
45
+ composes: token-typography-body-200 from global;
46
+ composes: mds-typography-font-weight-medium from global;
47
+ color: var(--token-color-foreground-primary);
48
+ display: flex;
49
+ gap: var(--mds-spacing-04);
50
+ padding: var(--mds-spacing-03);
51
+ min-width: 190px;
52
+ align-items: center;
53
+
54
+ &::before {
55
+ content: "";
56
+ position: absolute;
57
+ z-index: -1;
58
+ inset: 2px 0;
59
+ border-radius: var(--mds-spacing-01);
60
+ background-color: var(--submenu-item-background-color);
61
+ }
49
62
 
50
63
  &:hover {
51
- text-decoration: underline;
64
+ --submenu-item-background-color: var(--token-color-surface-interactive-hover);
65
+ }
66
+
67
+ @media (--large) {
68
+ max-width: 416px;
69
+ }
70
+
71
+ &:focus {
72
+ outline: 2px solid var(--token-color-focus-action-internal);
73
+ outline-style: auto;
52
74
  }
53
75
  }
54
76
 
55
77
  .submenuModal {
56
- border-radius: 4px;
57
- box-shadow: var(--token-elevation-high-box-shadow); /* from theme.module.css */
58
- background: var(--dropdown-background-color); /* from theme.module.css */
78
+ border-radius: var(--mds-spacing-03);
79
+ border: 1px solid var(--token-color-border-primary);
80
+ box-shadow: var(--token-elevation-high-box-shadow);
81
+ background: var(--token-color-surface-primary);
59
82
  z-index: 1;
60
- display: block;
61
83
  position: absolute;
62
84
  top: 100%;
63
- margin: 8px 0 0 0;
64
- padding: 24px;
85
+ margin: var(--mds-spacing-03) 0 0 0;
86
+ padding: var(--mds-spacing-04);
65
87
  left: 100%;
66
88
  transform: translateX(-100%);
67
89
  list-style: none;
68
-
69
- & li {
70
- & a:hover .text {
71
- text-decoration: underline;
72
- }
73
-
74
- & + li {
75
- margin-top: 6px;
76
- }
77
- }
78
90
  }
79
91
 
80
92
  .verticalDivider {
81
- background: var(--border-color); /* from theme.module.css */
93
+ background: var(--token-color-border-primary);
82
94
  height: 1.75rem;
83
95
  width: 1px;
84
96
  margin: 0 8px;
@@ -86,6 +98,7 @@
86
98
  }
87
99
 
88
100
  .dropdownList {
101
+ min-width: max-content;
89
102
  list-style: none;
90
103
  padding: 0;
91
104
  }
@@ -4,7 +4,7 @@
4
4
  */
5
5
 
6
6
  import React from 'react'
7
- import { v } from '../../../../lib/v'
7
+ import { vercelSplit } from '../../../../lib/v'
8
8
  import Link from 'next/link'
9
9
  import type { Products } from '@hashicorp/platform-product-meta'
10
10
  import CtaLinks, { type CtaLinkProps } from '../CtaLinks'
@@ -12,6 +12,8 @@ import DropdownTrigger from '../DropdownTrigger'
12
12
  import * as NavigationMenu from '@radix-ui/react-navigation-menu'
13
13
  import classNames from 'classnames'
14
14
  import s from './style.module.css'
15
+ import FlightIcon from '@hashicorp/react-mds/src/hds/components/flight-icon'
16
+ import { getLocalizedLinkProps } from '../../../../lib/get-localized-link-props'
15
17
 
16
18
  interface MenuItemsOverflowProps {
17
19
  menuItems: (
@@ -19,7 +21,7 @@ interface MenuItemsOverflowProps {
19
21
  | {
20
22
  text: string
21
23
  url: string
22
- submenu: { url: string; text: string }[]
24
+ submenu: { icon: string; url: string; text: string }[]
23
25
  }
24
26
  )[]
25
27
  ctaLinks: CtaLinkProps
@@ -28,6 +30,7 @@ interface MenuItemsOverflowProps {
28
30
  menuText: string
29
31
  constrainWidth?: boolean
30
32
  activatePanel: (panel: string) => void
33
+ locale?: string
31
34
  }
32
35
 
33
36
  interface MenuItemsOverflowState {
@@ -47,6 +50,7 @@ class MenuItemsOverflow extends React.Component<
47
50
  menuText,
48
51
  constrainWidth,
49
52
  activatePanel,
53
+ locale,
50
54
  } = this.props
51
55
 
52
56
  const handleFocusOutside = () => {
@@ -89,6 +93,8 @@ class MenuItemsOverflow extends React.Component<
89
93
  key={subStableIdx}
90
94
  url={subItem.url}
91
95
  text={subItem.text}
96
+ icon={subItem.icon}
97
+ locale={locale}
92
98
  />
93
99
  ))}
94
100
  <hr className={s.divider} />
@@ -120,15 +126,22 @@ class MenuItemsOverflow extends React.Component<
120
126
  }
121
127
 
122
128
  interface SubmenuItemProps {
129
+ icon?: string
123
130
  url: string
124
131
  text: string
132
+ locale?: string
125
133
  }
126
134
 
127
- function SubmenuItem({ url, text }: SubmenuItemProps) {
135
+ function SubmenuItem({ icon, url, text, locale }: SubmenuItemProps) {
128
136
  return (
129
137
  <li>
130
138
  <NavigationMenu.Link asChild>
131
- <Link className={s.submenuItem} href={url} title={v(text)}>
139
+ <Link
140
+ className={s.submenuItem}
141
+ title={vercelSplit(text)}
142
+ {...getLocalizedLinkProps(url, locale)}
143
+ >
144
+ {icon ? <FlightIcon name={icon} /> : null}
132
145
  {text}
133
146
  </Link>
134
147
  </NavigationMenu.Link>
@@ -3,8 +3,6 @@
3
3
  * SPDX-License-Identifier: MPL-2.0
4
4
  */
5
5
 
6
- /* Note: consumes from theme.module.css, as marked */
7
-
8
6
  .root {
9
7
  position: relative;
10
8
  display: flex;
@@ -12,18 +10,18 @@
12
10
  }
13
11
 
14
12
  .dropdown {
15
- background: var(--dropdown-background-color); /* from theme.module.css */
16
- border-radius: 4px;
13
+ background: var(--token-color-surface-primary);
14
+ border-radius: var(--mds-spacing-03);
17
15
  box-shadow: var(--token-elevation-high-box-shadow);
18
- border: 1px solid var(--border-color);
16
+ border: 1px solid var(--token-color-border-primary);
19
17
  margin-top: 8px;
20
18
  max-height: calc(100vh - 300%);
21
19
  overflow-y: auto;
22
- padding: var(--hdsplus-spacing-04) var(--hdsplus-spacing-07);
20
+ padding: var(--mds-spacing-04);
23
21
  position: absolute;
24
22
  right: 0;
25
23
  top: 100%;
26
- width: calc(100vw - var(--hdsplus-spacing-08));
24
+ width: calc(100vw - var(--mds-spacing-08));
27
25
  z-index: 1;
28
26
 
29
27
  @nest :global(.hashidaysConference) & {
@@ -32,7 +30,7 @@
32
30
 
33
31
  &.constrained {
34
32
  max-width: 568px;
35
- width: calc(100vw - var(--hdsplus-spacing-10));
33
+ width: calc(100vw - var(--mds-spacing-10));
36
34
 
37
35
  @media (--medium-up) {
38
36
  width: max-content;
@@ -52,27 +50,35 @@
52
50
 
53
51
  .submenuTitle {
54
52
  composes: g-type-label from global;
55
- margin: 12px 0 8px 0;
56
- color: var(--menu-item-heading-text-color); /* from theme.module.css */
53
+ margin: var(--mds-spacing-04) var(--mds-spacing-03) 8px;
54
+ color: var(--token-color-foreground-faint);
57
55
  }
58
56
 
59
57
  .divider {
60
- margin: 12px 0;
58
+ margin: var(--mds-spacing-04) var(--mds-spacing-03);
61
59
  border: 0;
62
60
  padding: 0;
63
61
  height: 1px;
64
- background: var(--border-color); /* from theme.module.css */
62
+ background: var(--token-color-border-primary);
65
63
  }
66
64
 
67
65
  .submenuItem {
68
- display: block;
69
- padding: 4px 0;
70
- color: var(--menu-item-text-color); /* from theme.module.css */
71
- font-weight: var(--font-weight-semi-bold);
72
- font-size: var(--token-typography-body-300-font-size);
66
+ composes: token-typography-body-300 from global;
67
+ composes: mds-typography-font-weight-semibold from global;
68
+ display: flex;
69
+ gap: var(--mds-spacing-04);
70
+ padding: var(--mds-spacing-02) var(--mds-spacing-03);
71
+ color: var(--token-color-foreground-primary);
72
+ border-radius: var(--mds-spacing-01);
73
+ align-items: center;
73
74
 
74
75
  &:hover {
75
- text-decoration: underline;
76
+ background-color: var(--token-color-surface-interactive-hover);
77
+ }
78
+
79
+ &:focus {
80
+ outline: 2px solid var(--token-color-focus-action-internal);
81
+ outline-style: auto;
76
82
  }
77
83
  }
78
84
 
@@ -7,7 +7,7 @@ import { forwardRef } from 'react'
7
7
  import { camelCase } from 'camel-case'
8
8
  import Link from 'next/link'
9
9
  import Image from 'next/image'
10
- import { v } from '../../../../lib/v'
10
+ import { vercelSplit } from '../../../../lib/v'
11
11
  import classNames from 'classnames'
12
12
  import * as CommunityProductLogosOnDark from './mktg-logos/all-community-product-logos_on-dark'
13
13
  import * as CommunityProductLogosOnLight from './mktg-logos/all-community-product-logos_on-light'
@@ -22,6 +22,7 @@ import WaypointLogoAttrOnDark from '@hashicorp/web-mktg-logos/src/waypoint/hashi
22
22
  import HCPTerraformLogoOnLight from '@hashicorp/web-mktg-logos/src/terraform/hcp-terraform/hcp-terraform_on-light.svg'
23
23
  import HCPTerraformLogoOnDark from '@hashicorp/web-mktg-logos/src/terraform/hcp-terraform/hcp-terraform_on-dark.svg'
24
24
  import s from './style.module.css'
25
+ import { getLocalizedLinkProps } from '../../../../lib/get-localized-link-props'
25
26
 
26
27
  const logoDict = {
27
28
  light: {
@@ -41,6 +42,7 @@ const logoDict = {
41
42
  }
42
43
 
43
44
  interface TitleLinkProps {
45
+ locale?: string
44
46
  text: string
45
47
  url: string
46
48
  product?: string
@@ -80,8 +82,8 @@ function getLogo(
80
82
  }
81
83
 
82
84
  const TitleLink = forwardRef<HTMLAnchorElement, TitleLinkProps>(
83
- ({ text, url, product, theme }, ref) => {
84
- const parsedText = v(text)
85
+ ({ text, url, product, theme, locale }, ref) => {
86
+ const parsedText = vercelSplit(text)
85
87
  const Logo = getLogo(parsedText, theme)
86
88
 
87
89
  return (
@@ -91,9 +93,9 @@ const TitleLink = forwardRef<HTMLAnchorElement, TitleLinkProps>(
91
93
  s[`brand-${product}`],
92
94
  s[camelCase(parsedText)]
93
95
  )}
94
- href={url}
95
96
  title={parsedText}
96
97
  ref={ref}
98
+ {...getLocalizedLinkProps(url, locale)}
97
99
  >
98
100
  {Logo ? (
99
101
  <Image
@@ -5,5 +5,6 @@ import packer from '@hashicorp/web-mktg-logos/src/packer/packer-community/packer
5
5
  import terraform from '@hashicorp/web-mktg-logos/src/terraform/terraform-community/terraform-community_on-dark.svg'
6
6
  import vagrant from '@hashicorp/web-mktg-logos/src/vagrant/vagrant-community/vagrant-community_on-dark.svg'
7
7
  import vault from '@hashicorp/web-mktg-logos/src/vault/vault-community/vault-community_on-dark.svg'
8
+ import waypoint from '@hashicorp/web-mktg-logos/src/waypoint/hashicorp-waypoint-attributed/hashicorp-waypoint_on-dark.svg'
8
9
 
9
- export { boundary, consul, nomad, packer, terraform, vagrant, vault }
10
+ export { boundary, consul, nomad, packer, terraform, vagrant, vault, waypoint }
@@ -5,5 +5,6 @@ import packer from '@hashicorp/web-mktg-logos/src/packer/packer-community/packer
5
5
  import terraform from '@hashicorp/web-mktg-logos/src/terraform/terraform-community/terraform-community_on-light.svg'
6
6
  import vagrant from '@hashicorp/web-mktg-logos/src/vagrant/vagrant-community/vagrant-community_on-light.svg'
7
7
  import vault from '@hashicorp/web-mktg-logos/src/vault/vault-community/vault-community_on-light.svg'
8
+ import waypoint from '@hashicorp/web-mktg-logos/src/waypoint/hashicorp-waypoint-attributed/hashicorp-waypoint_on-light.svg'
8
9
 
9
- export { boundary, consul, nomad, packer, terraform, vagrant, vault }
10
+ export { boundary, consul, nomad, packer, terraform, vagrant, vault, waypoint }
@@ -8,9 +8,9 @@
8
8
  .root {
9
9
  position: relative;
10
10
  display: inline-flex;
11
- padding-block: var(--hdsplus-spacing-04);
12
- font-weight: var(--font-weight-bold);
13
- color: var(--title-text-color); /* from theme.module.css */
11
+ padding-block: var(--mds-spacing-04);
12
+ font-weight: var(--mds-typography-font-weight-bold);
13
+ color: var(--token-color-foreground-primary); /* from theme.module.css */
14
14
  font-size: 1.25rem;
15
15
  line-height: 1.35rem;
16
16
 
@@ -3,13 +3,11 @@
3
3
  * SPDX-License-Identifier: MPL-2.0
4
4
  */
5
5
 
6
- /* Note: consumes from theme.module.css, as marked */
7
-
8
6
  .root {
9
- color: var(--menu-item-text-color); /* from theme.module.css */
7
+ color: var(--token-color-foreground-primary);
10
8
  position: relative;
11
9
 
12
10
  &.isActive {
13
- color: var(--menu-item-text-color-active);
11
+ color: var(--token-color-foreground-strong);
14
12
  }
15
13
  }
@@ -3,19 +3,17 @@
3
3
  * SPDX-License-Identifier: MPL-2.0
4
4
  */
5
5
 
6
- /* Note: consumes from theme.module.css, as marked */
7
-
8
6
  .root {
9
7
  position: sticky;
10
8
  top: -1px;
11
9
  z-index: 900;
12
- background: var(--background-color); /* from theme.module.css */
13
- border-top: 1px solid var(--border-color); /* from theme.module.css */
10
+ background: var(--token-color-surface-primary);
11
+ border-top: 1px solid var(--token-color-border-faint);
14
12
  border-bottom: 1px solid transparent;
15
13
  transition: border-bottom-color 0.8s;
16
14
 
17
15
  &.isSticky {
18
- border-bottom-color: var(--border-color); /* from theme.module.css */
16
+ border-bottom-color: var(--token-color-border-faint);
19
17
  }
20
18
 
21
19
  /*
@@ -46,7 +44,7 @@
46
44
 
47
45
  .subnavInner {
48
46
  display: grid;
49
- grid-template-columns: minmax(max-content, 1fr) min-content;
47
+ grid-template-columns: max-content min-content;
50
48
  align-items: center;
51
49
  justify-content: space-between;
52
50
  min-height: 62px;
@@ -58,7 +56,7 @@
58
56
 
59
57
  &.notConstrained {
60
58
  /* This padding should always match the horizontal padding of the main nav */
61
- padding: 0 var(--hdsplus-spacing-05);
59
+ padding: 0 var(--mds-spacing-05);
62
60
  }
63
61
  }
64
62
 
@@ -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
  })