@hashicorp/react-components 0.0.0-nightly.9ce2a10 → 0.0.0-nightly.a120cb6
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.
- package/package.json +4 -8
- package/src/components/nav-panel/components/link-list/index.tsx +15 -9
- package/src/components/nav-panel/components/link-list/style.module.css +10 -10
- package/src/components/nav-panel/components/panel/column/style.module.css +3 -3
- package/src/components/nav-panel/components/panel/root/style.module.css +6 -6
- package/src/components/nav-panel/components/promo/index.tsx +12 -9
- package/src/components/nav-panel/components/promo/style.module.css +13 -12
- package/src/components/nav-panel/product-panel/index.tsx +25 -25
- package/src/components/nav-panel/product-panel/style.module.css +62 -43
- package/src/components/nav-panel/standard-panel/index.tsx +3 -0
- package/src/components/nav-panel/standard-panel/style.module.css +2 -2
- package/src/lib/get-localized-link-props/index.test.ts +30 -0
- package/src/lib/get-localized-link-props/index.ts +45 -0
- package/src/lib/parse-url/index.test.ts +40 -1
- package/src/lib/parse-url/index.ts +75 -9
- package/src/lib/supported-locales/index.ts +1 -0
- package/src/lib/track-nav-click-event/index.ts +4 -4
- package/src/lib/v/index.ts +4 -5
- package/src/components/actions/index.tsx +0 -107
- package/src/components/actions/style.module.css +0 -28
- package/src/components/actions/types.ts +0 -73
- package/src/components/call-to-action/index.tsx +0 -205
- package/src/components/call-to-action/styles/variant-centered.module.css +0 -93
- package/src/components/call-to-action/styles/variant-compact-grid.module.css +0 -88
- package/src/components/call-to-action/styles/variant-compact.module.css +0 -91
- package/src/components/call-to-action/styles/variant-links.module.css +0 -85
- package/src/components/card/README.md +0 -11
- package/src/components/card/card.tsx +0 -45
- package/src/components/card/index.test.tsx +0 -69
- package/src/components/card/index.tsx +0 -14
- package/src/components/card/newsroom-card/index.test.tsx +0 -39
- package/src/components/card/newsroom-card/index.tsx +0 -37
- package/src/components/card/newsroom-card/style.module.css +0 -12
- package/src/components/card/partner-card/index.test.tsx +0 -57
- package/src/components/card/partner-card/index.tsx +0 -69
- package/src/components/card/partner-card/style.module.css +0 -24
- package/src/components/card/person-card/index.test.tsx +0 -71
- package/src/components/card/person-card/index.tsx +0 -93
- package/src/components/card/person-card/style.module.css +0 -33
- package/src/components/card/primitives.tsx +0 -176
- package/src/components/card/promo-card/index.tsx +0 -51
- package/src/components/card/resource-card/index.test.tsx +0 -48
- package/src/components/card/resource-card/index.tsx +0 -37
- package/src/components/card/style.module.css +0 -145
- package/src/components/card/types.ts +0 -66
- package/src/components/card/unified-card/index.tsx +0 -64
- package/src/components/consent-manager/README.md +0 -73
- package/src/components/consent-manager/components/banner/banner.module.css +0 -23
- package/src/components/consent-manager/components/banner/banner.test.tsx +0 -31
- package/src/components/consent-manager/components/banner/banner.tsx +0 -73
- package/src/components/consent-manager/components/dialog/dialog.module.css +0 -145
- package/src/components/consent-manager/components/dialog/dialog.test.tsx +0 -194
- package/src/components/consent-manager/components/dialog/dialog.tsx +0 -339
- package/src/components/consent-manager/img/icn_close.tsx +0 -19
- package/src/components/consent-manager/index.test.tsx +0 -334
- package/src/components/consent-manager/index.tsx +0 -186
- package/src/components/consent-manager/loader/README.md +0 -29
- package/src/components/consent-manager/loader/categories.ts +0 -22
- package/src/components/consent-manager/loader/index.tsx +0 -94
- package/src/components/consent-manager/loader/presets/enterprise.ts +0 -12
- package/src/components/consent-manager/loader/presets/oss.ts +0 -10
- package/src/components/consent-manager/loader/services.ts +0 -45
- package/src/components/consent-manager/scripts/custom.tsx +0 -59
- package/src/components/consent-manager/scripts/segment.tsx +0 -87
- package/src/components/consent-manager/style.module.css +0 -9
- package/src/components/consent-manager/types.ts +0 -55
- package/src/components/consent-manager/util/cookies.test.ts +0 -75
- package/src/components/consent-manager/util/cookies.ts +0 -70
- package/src/components/consent-manager/util/integrations.test.ts +0 -115
- package/src/components/consent-manager/util/integrations.ts +0 -110
- package/src/components/intro/index.tsx +0 -82
- package/src/components/intro/style.module.css +0 -67
- package/src/components/intro/types.ts +0 -44
- package/src/components/marketo-form/CHANGELOG.md +0 -122
- package/src/components/marketo-form/README.md +0 -1
- package/src/components/marketo-form/contexts/FormMetadata.ts +0 -11
- package/src/components/marketo-form/docs.mdx +0 -197
- package/src/components/marketo-form/fixtures/index.ts +0 -185
- package/src/components/marketo-form/form/index.tsx +0 -369
- package/src/components/marketo-form/index.test.tsx +0 -320
- package/src/components/marketo-form/index.ts +0 -11
- package/src/components/marketo-form/package.original.json +0 -30
- package/src/components/marketo-form/partials/error-message/index.tsx +0 -12
- package/src/components/marketo-form/partials/error-message/style.module.css +0 -13
- package/src/components/marketo-form/partials/field/index.tsx +0 -90
- package/src/components/marketo-form/partials/field-wrapper/index.tsx +0 -26
- package/src/components/marketo-form/partials/fields/checkbox-field/index.tsx +0 -32
- package/src/components/marketo-form/partials/fields/email-field/index.tsx +0 -35
- package/src/components/marketo-form/partials/fields/form-page-url-field/index.tsx +0 -21
- package/src/components/marketo-form/partials/fields/hidden-field/index.tsx +0 -37
- package/src/components/marketo-form/partials/fields/htmltext-field/index.tsx +0 -21
- package/src/components/marketo-form/partials/fields/htmltext-field/style.module.css +0 -18
- package/src/components/marketo-form/partials/fields/name-field/index.tsx +0 -19
- package/src/components/marketo-form/partials/fields/name-field/style.module.css +0 -13
- package/src/components/marketo-form/partials/fields/radio-buttons-field/index.tsx +0 -48
- package/src/components/marketo-form/partials/fields/select-field/index.tsx +0 -39
- package/src/components/marketo-form/partials/fields/select-field/style.module.css +0 -5
- package/src/components/marketo-form/partials/fields/telephone-field/index.tsx +0 -30
- package/src/components/marketo-form/partials/fields/text-field/index.tsx +0 -30
- package/src/components/marketo-form/partials/fields/textarea-field/index.tsx +0 -29
- package/src/components/marketo-form/partials/label/index.tsx +0 -24
- package/src/components/marketo-form/partials/label/style.module.css +0 -12
- package/src/components/marketo-form/partials/visibility-rule/index.tsx +0 -84
- package/src/components/marketo-form/props.js +0 -66
- package/src/components/marketo-form/server/api-routes.ts +0 -109
- package/src/components/marketo-form/server/client.ts +0 -98
- package/src/components/marketo-form/server/index.ts +0 -8
- package/src/components/marketo-form/types.ts +0 -268
- package/src/components/marketo-form/utils.test.tsx +0 -103
- package/src/components/marketo-form/utils.ts +0 -449
- package/src/components/next-steps/index.tsx +0 -137
- package/src/components/next-steps/style.module.css +0 -270
- package/src/components/next-steps/types.ts +0 -63
- package/src/components/product-badge/index.test.tsx +0 -11
- package/src/components/product-badge/index.tsx +0 -42
- package/src/components/product-badge/style.module.css +0 -76
- package/src/components/products-used/index.tsx +0 -84
- package/src/components/products-used/style.module.css +0 -87
- package/src/components/products-used/types.ts +0 -31
- package/src/components/related-content/index.test.tsx +0 -74
- package/src/components/related-content/index.tsx +0 -56
- package/src/components/related-content/style.module.css +0 -89
- package/src/components/related-content/types.ts +0 -21
- package/src/components/subnav/helpers/areBasePathsMatching.ts +0 -13
- package/src/components/subnav/helpers/traverse/index.ts +0 -42
- package/src/components/subnav/helpers/useHeightRef.ts +0 -42
- package/src/components/subnav/helpers/useNavRef.ts +0 -30
- package/src/components/subnav/helpers/useOverflowRef.ts +0 -85
- package/src/components/subnav/helpers/useStuckRef.ts +0 -51
- package/src/components/subnav/index.tsx +0 -199
- package/src/components/subnav/partials/CtaLinks/github-stars-link/formatStarCount/index.test.ts +0 -26
- package/src/components/subnav/partials/CtaLinks/github-stars-link/formatStarCount/index.ts +0 -22
- package/src/components/subnav/partials/CtaLinks/github-stars-link/index.tsx +0 -87
- package/src/components/subnav/partials/CtaLinks/github-stars-link/parseGithubUrl/index.test.ts +0 -29
- package/src/components/subnav/partials/CtaLinks/github-stars-link/parseGithubUrl/index.ts +0 -32
- package/src/components/subnav/partials/CtaLinks/github-stars-link/style.module.css +0 -85
- package/src/components/subnav/partials/CtaLinks/icons/github-logo.js +0 -20
- package/src/components/subnav/partials/CtaLinks/icons/github.svg.js +0 -7
- package/src/components/subnav/partials/CtaLinks/icons/star.js +0 -20
- package/src/components/subnav/partials/CtaLinks/index.tsx +0 -130
- package/src/components/subnav/partials/CtaLinks/style.module.css +0 -45
- package/src/components/subnav/partials/DropdownTrigger/index.tsx +0 -35
- package/src/components/subnav/partials/DropdownTrigger/style.module.css +0 -36
- package/src/components/subnav/partials/MenuItemsDefault/index.tsx +0 -151
- package/src/components/subnav/partials/MenuItemsDefault/style.module.css +0 -91
- package/src/components/subnav/partials/MenuItemsOverflow/index.tsx +0 -139
- package/src/components/subnav/partials/MenuItemsOverflow/style.module.css +0 -82
- package/src/components/subnav/partials/TitleLink/index.tsx +0 -113
- package/src/components/subnav/partials/TitleLink/mktg-logos/all-community-product-logos_on-dark.ts +0 -9
- package/src/components/subnav/partials/TitleLink/mktg-logos/all-community-product-logos_on-light.ts +0 -9
- package/src/components/subnav/partials/TitleLink/mktg-logos/all-primary-logos_on-dark.ts +0 -23
- package/src/components/subnav/partials/TitleLink/mktg-logos/all-primary-logos_on-light.ts +0 -23
- package/src/components/subnav/partials/TitleLink/style.module.css +0 -43
- package/src/components/subnav/partials/nav-item-text/index.tsx +0 -37
- package/src/components/subnav/partials/nav-item-text/style.module.css +0 -15
- package/src/components/subnav/style.module.css +0 -68
- package/src/components/subnav/subnav-consts.ts +0 -1
- package/src/components/subnav/theme.module.css +0 -50
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
@custom-media --dev-dot-viewport (min-width:
|
|
1
|
+
@custom-media --dev-dot-viewport (min-width: 924px);
|
|
2
2
|
|
|
3
3
|
.productPanel {
|
|
4
4
|
height: 100%;
|
|
@@ -26,7 +26,7 @@
|
|
|
26
26
|
list-style: none;
|
|
27
27
|
padding: 0;
|
|
28
28
|
display: grid;
|
|
29
|
-
margin: var(--
|
|
29
|
+
margin: var(--mds-spacing-07) 0 0;
|
|
30
30
|
|
|
31
31
|
@media (--large) {
|
|
32
32
|
@nest .noSide & {
|
|
@@ -48,27 +48,27 @@
|
|
|
48
48
|
|
|
49
49
|
&:first-child {
|
|
50
50
|
border-bottom: 1px solid var(--token-color-border-primary);
|
|
51
|
-
margin-bottom: var(--
|
|
52
|
-
padding-bottom: var(--
|
|
51
|
+
margin-bottom: var(--mds-spacing-05);
|
|
52
|
+
padding-bottom: var(--mds-spacing-05);
|
|
53
53
|
}
|
|
54
54
|
}
|
|
55
55
|
|
|
56
56
|
.eyebrow {
|
|
57
|
-
padding-bottom: var(--
|
|
57
|
+
padding-bottom: var(--mds-spacing-04);
|
|
58
58
|
color: var(--token-color-foreground-strong);
|
|
59
|
-
font-weight: var(--font-weight-
|
|
59
|
+
font-weight: var(--mds-typography-font-weight-semibold);
|
|
60
60
|
|
|
61
61
|
@media (--large) {
|
|
62
62
|
@nest .noSide & {
|
|
63
63
|
padding-bottom: 0;
|
|
64
|
-
padding-left: var(--
|
|
64
|
+
padding-left: var(--mds-spacing-06);
|
|
65
65
|
}
|
|
66
66
|
}
|
|
67
67
|
|
|
68
68
|
@media (--dev-dot-viewport) {
|
|
69
69
|
@nest .hasSide & {
|
|
70
70
|
padding-bottom: 0;
|
|
71
|
-
padding-left: var(--
|
|
71
|
+
padding-left: var(--mds-spacing-06);
|
|
72
72
|
}
|
|
73
73
|
}
|
|
74
74
|
}
|
|
@@ -82,54 +82,60 @@
|
|
|
82
82
|
|
|
83
83
|
@media (--large) {
|
|
84
84
|
@nest .noSide & {
|
|
85
|
-
padding: var(--
|
|
86
|
-
var(--
|
|
87
|
-
grid-auto-flow:
|
|
85
|
+
padding: var(--mds-spacing-06) var(--mds-spacing-05)
|
|
86
|
+
var(--mds-spacing-04);
|
|
87
|
+
grid-auto-flow: row;
|
|
88
88
|
grid-template-columns: repeat(2, 300px);
|
|
89
|
-
grid-template-rows: repeat(
|
|
89
|
+
grid-template-rows: repeat(2, max-content);
|
|
90
90
|
gap: 0;
|
|
91
91
|
}
|
|
92
92
|
}
|
|
93
93
|
|
|
94
94
|
@media (--dev-dot-viewport) {
|
|
95
95
|
@nest .hasSide & {
|
|
96
|
-
padding: var(--
|
|
97
|
-
var(--
|
|
98
|
-
grid-auto-flow:
|
|
96
|
+
padding: var(--mds-spacing-06) var(--mds-spacing-05)
|
|
97
|
+
var(--mds-spacing-04);
|
|
98
|
+
grid-auto-flow: row;
|
|
99
99
|
grid-template-columns: repeat(2, 300px);
|
|
100
|
-
grid-template-rows: repeat(
|
|
100
|
+
grid-template-rows: repeat(2, max-content);
|
|
101
101
|
gap: 0;
|
|
102
102
|
}
|
|
103
103
|
}
|
|
104
|
+
|
|
105
|
+
& li:last-child {
|
|
106
|
+
& .productWrapper {
|
|
107
|
+
margin-bottom: 0;
|
|
108
|
+
}
|
|
109
|
+
}
|
|
104
110
|
}
|
|
105
111
|
|
|
106
112
|
/* NavProduct styles */
|
|
107
113
|
|
|
108
114
|
.productWrapper {
|
|
109
115
|
display: flex;
|
|
110
|
-
margin-bottom: var(--
|
|
111
|
-
gap: var(--
|
|
116
|
+
margin-bottom: var(--mds-spacing-05);
|
|
117
|
+
gap: var(--mds-spacing-05);
|
|
112
118
|
|
|
113
119
|
@media (--large) {
|
|
114
120
|
@nest .noSide & {
|
|
115
|
-
padding: var(--
|
|
121
|
+
padding: var(--mds-spacing-03) var(--mds-spacing-05);
|
|
116
122
|
margin-bottom: 0;
|
|
117
123
|
|
|
118
124
|
&:hover {
|
|
119
125
|
background-color: var(--token-color-surface-interactive-hover);
|
|
120
|
-
border-radius: var(--
|
|
126
|
+
border-radius: var(--mds-spacing-01);
|
|
121
127
|
}
|
|
122
128
|
}
|
|
123
129
|
}
|
|
124
130
|
|
|
125
131
|
@media (--dev-dot-viewport) {
|
|
126
132
|
@nest .hasSide & {
|
|
127
|
-
padding: var(--
|
|
133
|
+
padding: var(--mds-spacing-03) var(--mds-spacing-05);
|
|
128
134
|
margin-bottom: 0;
|
|
129
135
|
|
|
130
136
|
&:hover {
|
|
131
137
|
background-color: var(--token-color-surface-interactive-hover);
|
|
132
|
-
border-radius: var(--
|
|
138
|
+
border-radius: var(--mds-spacing-01);
|
|
133
139
|
}
|
|
134
140
|
}
|
|
135
141
|
}
|
|
@@ -146,8 +152,8 @@
|
|
|
146
152
|
@nest .noSide & {
|
|
147
153
|
& svg {
|
|
148
154
|
@media (--large) {
|
|
149
|
-
height: var(--
|
|
150
|
-
width: var(--
|
|
155
|
+
height: var(--mds-spacing-08);
|
|
156
|
+
width: var(--mds-spacing-08);
|
|
151
157
|
}
|
|
152
158
|
}
|
|
153
159
|
}
|
|
@@ -155,8 +161,8 @@
|
|
|
155
161
|
@nest .hasSide & {
|
|
156
162
|
& svg {
|
|
157
163
|
@media (--dev-dot-viewport) {
|
|
158
|
-
height: var(--
|
|
159
|
-
width: var(--
|
|
164
|
+
height: var(--mds-spacing-08);
|
|
165
|
+
width: var(--mds-spacing-08);
|
|
160
166
|
}
|
|
161
167
|
}
|
|
162
168
|
}
|
|
@@ -191,15 +197,15 @@
|
|
|
191
197
|
|
|
192
198
|
@media (--large) {
|
|
193
199
|
@nest .noSide & {
|
|
194
|
-
padding: var(--
|
|
195
|
-
var(--
|
|
200
|
+
padding: var(--mds-spacing-07) 0 var(--mds-spacing-09)
|
|
201
|
+
var(--mds-spacing-04);
|
|
196
202
|
}
|
|
197
203
|
}
|
|
198
204
|
|
|
199
205
|
@media (--dev-dot-viewport) {
|
|
200
206
|
@nest .hasSide & {
|
|
201
|
-
padding: var(--
|
|
202
|
-
var(--
|
|
207
|
+
padding: var(--mds-spacing-07) 0 var(--mds-spacing-09)
|
|
208
|
+
var(--mds-spacing-04);
|
|
203
209
|
}
|
|
204
210
|
}
|
|
205
211
|
}
|
|
@@ -209,10 +215,10 @@
|
|
|
209
215
|
background: var(--token-color-surface-faint);
|
|
210
216
|
display: flex;
|
|
211
217
|
flex-direction: column;
|
|
212
|
-
gap: var(--
|
|
218
|
+
gap: var(--mds-spacing-05);
|
|
213
219
|
margin: 0;
|
|
214
|
-
padding: var(--
|
|
215
|
-
var(--
|
|
220
|
+
padding: var(--mds-spacing-07) var(--mds-spacing-04)
|
|
221
|
+
var(--mds-spacing-09) var(--mds-spacing-07);
|
|
216
222
|
}
|
|
217
223
|
|
|
218
224
|
.sidePanelItems {
|
|
@@ -221,26 +227,24 @@
|
|
|
221
227
|
padding: 0;
|
|
222
228
|
display: grid;
|
|
223
229
|
grid-template-columns: 1fr;
|
|
224
|
-
gap: var(--
|
|
230
|
+
gap: var(--mds-spacing-07);
|
|
225
231
|
|
|
226
232
|
@media (--dev-dot-viewport) {
|
|
227
|
-
gap: var(--
|
|
233
|
+
gap: var(--mds-spacing-05);
|
|
228
234
|
}
|
|
229
235
|
}
|
|
230
236
|
|
|
231
237
|
/* Lifted from .productWrapper */
|
|
232
238
|
.sidePanelItem {
|
|
233
239
|
display: flex;
|
|
234
|
-
|
|
235
|
-
gap: var(--hdsplus-spacing-05);
|
|
240
|
+
gap: var(--mds-spacing-05);
|
|
236
241
|
|
|
237
242
|
@media (--dev-dot-viewport) {
|
|
238
|
-
padding: var(--
|
|
239
|
-
margin-bottom: 0;
|
|
243
|
+
padding: var(--mds-spacing-03) var(--mds-spacing-05);
|
|
240
244
|
|
|
241
245
|
&:hover {
|
|
242
246
|
background-color: var(--token-color-surface-interactive-hover);
|
|
243
|
-
border-radius: var(--
|
|
247
|
+
border-radius: var(--mds-spacing-01);
|
|
244
248
|
}
|
|
245
249
|
}
|
|
246
250
|
}
|
|
@@ -249,8 +253,8 @@
|
|
|
249
253
|
packages/react-components/src/components/nav-panel/components/link-list/style.module.css */
|
|
250
254
|
.iconBackground {
|
|
251
255
|
background: var(--token-color-foreground-high-contrast);
|
|
252
|
-
height: var(--
|
|
253
|
-
width: var(--
|
|
256
|
+
height: var(--mds-spacing-08);
|
|
257
|
+
width: var(--mds-spacing-08);
|
|
254
258
|
display: flex;
|
|
255
259
|
align-items: center;
|
|
256
260
|
justify-content: center;
|
|
@@ -276,13 +280,28 @@ packages/react-components/src/components/nav-panel/components/link-list/style.mo
|
|
|
276
280
|
|
|
277
281
|
/* Branding exception classnames */
|
|
278
282
|
|
|
283
|
+
/* Allows switching to a dark-mode-specific color for Terraform */
|
|
284
|
+
.terraform {
|
|
285
|
+
& use {
|
|
286
|
+
fill: var(--token-color-terraform-brand);
|
|
287
|
+
|
|
288
|
+
@nest [data-theme='dark'] & {
|
|
289
|
+
fill: var(--token-color-terraform-foreground);
|
|
290
|
+
}
|
|
291
|
+
}
|
|
292
|
+
|
|
293
|
+
& path {
|
|
294
|
+
fill: inherit;
|
|
295
|
+
}
|
|
296
|
+
}
|
|
297
|
+
|
|
279
298
|
/* Styles for HCP Vault mimic the spec for `product-square` icons,
|
|
280
299
|
while allowing the symbol inside the icon border to change color
|
|
281
300
|
based on the site's theme
|
|
282
301
|
*/
|
|
283
302
|
.hcpVaultSecrets,
|
|
284
303
|
.hcpVaultRadar {
|
|
285
|
-
padding: var(--
|
|
304
|
+
padding: var(--mds-spacing-01);
|
|
286
305
|
border: 2px solid var(--token-color-vault-brand);
|
|
287
306
|
border-radius: 3px;
|
|
288
307
|
|
|
@@ -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)
|
|
@@ -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
|
})
|
|
@@ -1,9 +1,46 @@
|
|
|
1
|
+
export const hashicorpDestinations: string[] = [
|
|
2
|
+
'app.terraform.io',
|
|
3
|
+
'boundaryproject.io',
|
|
4
|
+
'cloud.hashicorp.com',
|
|
5
|
+
'consul.io',
|
|
6
|
+
'developer.hashicorp.com',
|
|
7
|
+
'hashicorp.com',
|
|
8
|
+
'nomadproject.io',
|
|
9
|
+
'packer.io',
|
|
10
|
+
'portal.cloud.hashicorp.com',
|
|
11
|
+
'registry.terraform.io',
|
|
12
|
+
'terraform.io',
|
|
13
|
+
'vagrantup.com',
|
|
14
|
+
'vaultproject.io',
|
|
15
|
+
'waypointproject.io',
|
|
16
|
+
] as const
|
|
17
|
+
|
|
18
|
+
const containsHashiCorpDestination = (str: string): boolean =>
|
|
19
|
+
hashicorpDestinations.some(function (destination) {
|
|
20
|
+
return str.indexOf(destination) >= 0
|
|
21
|
+
})
|
|
22
|
+
|
|
23
|
+
/**
|
|
24
|
+
* This function determines if a `url` is a HashiCorp link (relative URLs, hashes,
|
|
25
|
+
* or a url with a HashiCorp domain) or a non-HashiCorp link.
|
|
26
|
+
*
|
|
27
|
+
* @param {string | undefined} url - The URL to be parsed. It may be a relative URL, an anchor, or a full external/internal URL.
|
|
28
|
+
*
|
|
29
|
+
* @returns {{ linkType: 'inbound' | 'outbound', href: string }}
|
|
30
|
+
* An object with two properties:
|
|
31
|
+
* - `linkType`: Indicates whether the URL is 'inbound' or 'outbound'. Can be used to conditionally
|
|
32
|
+
* render the value of the target attribute.
|
|
33
|
+
* - `href`: The parsed href (may include pathname, search, and hash for internal URLs).
|
|
34
|
+
*
|
|
35
|
+
* @throws {Error} Throws an error if the URL is undefined or invalid.
|
|
36
|
+
*/
|
|
1
37
|
export function parseUrl(url: string | undefined): {
|
|
2
|
-
linkType: 'inbound' | 'outbound'
|
|
38
|
+
linkType: 'inbound' | 'outbound' | ''
|
|
3
39
|
href: string
|
|
4
40
|
} {
|
|
5
41
|
if (!url) {
|
|
6
|
-
|
|
42
|
+
console.log('No url to parse, returning empty string')
|
|
43
|
+
return { linkType: '', href: '' }
|
|
7
44
|
}
|
|
8
45
|
|
|
9
46
|
if (isRelative(url) || isAnchor(url)) {
|
|
@@ -12,21 +49,50 @@ export function parseUrl(url: string | undefined): {
|
|
|
12
49
|
|
|
13
50
|
if (isInternal(url)) {
|
|
14
51
|
try {
|
|
15
|
-
const { pathname, search } = new URL(url)
|
|
16
|
-
const href = pathname + search
|
|
52
|
+
const { pathname, search, hash } = new URL(url)
|
|
53
|
+
const href = pathname + search + hash
|
|
17
54
|
return { linkType: 'inbound', href }
|
|
18
55
|
} catch (error) {
|
|
19
|
-
|
|
56
|
+
console.log(`Invalid href: ${url}`)
|
|
57
|
+
return { linkType: '', href: '' }
|
|
20
58
|
}
|
|
21
59
|
}
|
|
22
60
|
|
|
23
61
|
return { linkType: 'outbound', href: url }
|
|
24
62
|
}
|
|
25
63
|
|
|
26
|
-
const isRelative = (url) => url.startsWith('/')
|
|
27
|
-
const isAnchor = (url) => url.startsWith('#')
|
|
64
|
+
const isRelative = (url: string) => url.startsWith('/')
|
|
65
|
+
const isAnchor = (url: string) => url.startsWith('#')
|
|
28
66
|
|
|
29
|
-
|
|
67
|
+
/**
|
|
68
|
+
* Determines whether the provided url uses hashicorp.com or www.hashicorp.com as the hostname.
|
|
69
|
+
*
|
|
70
|
+
* @param {string} url - The URL to check.
|
|
71
|
+
* @returns {boolean} - Returns true if the link uses a HashiCorp domain, otherwise false.
|
|
72
|
+
*/
|
|
73
|
+
|
|
74
|
+
export const isInternal = (url: string): boolean => {
|
|
30
75
|
const hostname = new URL(url).hostname
|
|
31
|
-
return hostname === 'hashicorp.com'
|
|
76
|
+
return hostname === 'hashicorp.com' || hostname === 'www.hashicorp.com'
|
|
77
|
+
}
|
|
78
|
+
|
|
79
|
+
/**
|
|
80
|
+
* Determines whether the provided url uses a HashiCorp hostname. This is used in several
|
|
81
|
+
* components to open a new tab for non-HashiCorp links.
|
|
82
|
+
*
|
|
83
|
+
* @param {string} url - The URL to check.
|
|
84
|
+
* @returns {boolean} - Returns true if the link uses a HashiCorp domain, otherwise false.
|
|
85
|
+
*/
|
|
86
|
+
|
|
87
|
+
export const hasHashiCorpDestination = (url: string): boolean => {
|
|
88
|
+
// Fast return in case a relative URL or anchor is passed in
|
|
89
|
+
if (isRelative(url) || isAnchor(url)) return true
|
|
90
|
+
try {
|
|
91
|
+
const hostname = new URL(url).hostname
|
|
92
|
+
const isHashiCorpDestination = containsHashiCorpDestination(hostname)
|
|
93
|
+
return isHashiCorpDestination
|
|
94
|
+
} catch (e) {
|
|
95
|
+
console.error('Error: `url` is not a valid URL', e)
|
|
96
|
+
return false
|
|
97
|
+
}
|
|
32
98
|
}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export const allSupportedLocales = ['en', 'de', 'es', 'fr', 'ja', 'ko', 'pt']
|
|
@@ -1,8 +1,8 @@
|
|
|
1
|
-
import
|
|
1
|
+
import sendPosthogEvent from '@web/utils/send-posthog-event'
|
|
2
2
|
|
|
3
3
|
/**
|
|
4
|
-
* Pushes a 'Nav link click' custom event to
|
|
5
|
-
* A wrapper around the
|
|
4
|
+
* Pushes a 'Nav link click' custom event to PostHog.
|
|
5
|
+
* A wrapper around the PostHog custom event method.
|
|
6
6
|
*
|
|
7
7
|
* @param {string} name - The name of the nav link
|
|
8
8
|
* @param {string} href - The href for the nav link
|
|
@@ -14,7 +14,7 @@ export const trackNavClickEvent = (
|
|
|
14
14
|
href: string,
|
|
15
15
|
section: string
|
|
16
16
|
) => {
|
|
17
|
-
|
|
17
|
+
sendPosthogEvent('Nav link click', {
|
|
18
18
|
name,
|
|
19
19
|
href,
|
|
20
20
|
section,
|
package/src/lib/v/index.ts
CHANGED
|
@@ -3,9 +3,8 @@ import { vercelStegaSplit } from '@vercel/stega'
|
|
|
3
3
|
/**
|
|
4
4
|
* Takes in a Vercel Visual Editing string and returns the string without
|
|
5
5
|
* hidden data.
|
|
6
|
+
*
|
|
6
7
|
*/
|
|
7
|
-
export
|
|
8
|
-
|
|
9
|
-
):
|
|
10
|
-
return vercelStegaSplit(s).cleaned as VercelVEString
|
|
11
|
-
}
|
|
8
|
+
export const vercelSplit = <VercelString>(
|
|
9
|
+
original: VercelString
|
|
10
|
+
): VercelString => vercelStegaSplit(original as string).cleaned as VercelString
|
|
@@ -1,107 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Copyright (c) HashiCorp, Inc.
|
|
3
|
-
* SPDX-License-Identifier: MPL-2.0
|
|
4
|
-
*/
|
|
5
|
-
|
|
6
|
-
import classNames from 'classnames'
|
|
7
|
-
import Button from '@hashicorp/react-design-system-components/src/components/button'
|
|
8
|
-
import StandaloneLink from '@hashicorp/react-design-system-components/src/components/standalone-link'
|
|
9
|
-
import { IconArrowRight16 } from '@hashicorp/flight-icons/svg-react/arrow-right-16'
|
|
10
|
-
import { IconArrowRight24 } from '@hashicorp/flight-icons/svg-react/arrow-right-24'
|
|
11
|
-
import type { ActionsProps } from './types'
|
|
12
|
-
import s from './style.module.css'
|
|
13
|
-
import { ComponentProps } from 'react'
|
|
14
|
-
|
|
15
|
-
const hasButton = (item) => item.type === 'button' || item.type === undefined
|
|
16
|
-
const hasStandaloneLink = (item) => item.type === 'standalone-link'
|
|
17
|
-
|
|
18
|
-
export default function Actions({
|
|
19
|
-
appearance = 'light',
|
|
20
|
-
layout = 'inline',
|
|
21
|
-
alignment = 'left',
|
|
22
|
-
theme = 'primary',
|
|
23
|
-
size = 'large',
|
|
24
|
-
ctas,
|
|
25
|
-
}: ActionsProps) {
|
|
26
|
-
if (ctas.length < 1 || ctas.length > 2) {
|
|
27
|
-
throw new Error(
|
|
28
|
-
`Actions expects at least 1 cta, no more than 2. You provided ${ctas.length}.`
|
|
29
|
-
)
|
|
30
|
-
}
|
|
31
|
-
let mixed = false
|
|
32
|
-
// If CTAs contain a button and standalone link
|
|
33
|
-
// increase the column gap
|
|
34
|
-
if (ctas.some(hasButton) && ctas.some(hasStandaloneLink)) {
|
|
35
|
-
mixed = true
|
|
36
|
-
}
|
|
37
|
-
return (
|
|
38
|
-
<div
|
|
39
|
-
className={classNames(
|
|
40
|
-
s.actions,
|
|
41
|
-
s[layout],
|
|
42
|
-
s[alignment],
|
|
43
|
-
mixed && s.mixed
|
|
44
|
-
)}
|
|
45
|
-
data-testid="actions"
|
|
46
|
-
>
|
|
47
|
-
{ctas.map((cta, index) => {
|
|
48
|
-
// The first CTA and second CTA should always
|
|
49
|
-
// have the `primary` and `secondary` variations respectively.
|
|
50
|
-
|
|
51
|
-
const standaloneLinkVariant: ComponentProps<
|
|
52
|
-
typeof StandaloneLink
|
|
53
|
-
>['color'] =
|
|
54
|
-
index === 0
|
|
55
|
-
? 'primary'
|
|
56
|
-
: appearance === 'dark'
|
|
57
|
-
? 'secondary-inverted'
|
|
58
|
-
: 'secondary'
|
|
59
|
-
const isStandaloneLink = cta?.type === 'standalone-link'
|
|
60
|
-
|
|
61
|
-
if (isStandaloneLink) {
|
|
62
|
-
return (
|
|
63
|
-
<StandaloneLink
|
|
64
|
-
// eslint-disable-next-line react/no-array-index-key
|
|
65
|
-
key={index}
|
|
66
|
-
text={cta.title}
|
|
67
|
-
icon={
|
|
68
|
-
size === 'large' ? (
|
|
69
|
-
<IconArrowRight24
|
|
70
|
-
width="100%"
|
|
71
|
-
height="100%"
|
|
72
|
-
style={{ display: 'block' }}
|
|
73
|
-
/>
|
|
74
|
-
) : (
|
|
75
|
-
<IconArrowRight16
|
|
76
|
-
width="100%"
|
|
77
|
-
height="100%"
|
|
78
|
-
style={{ display: 'block' }}
|
|
79
|
-
/>
|
|
80
|
-
)
|
|
81
|
-
}
|
|
82
|
-
iconPosition="trailing"
|
|
83
|
-
href={cta.href}
|
|
84
|
-
color={standaloneLinkVariant}
|
|
85
|
-
size={size}
|
|
86
|
-
onClick={cta?.onClick}
|
|
87
|
-
data-testid={`standaloneLink-${index}`}
|
|
88
|
-
/>
|
|
89
|
-
)
|
|
90
|
-
} else {
|
|
91
|
-
return (
|
|
92
|
-
<Button
|
|
93
|
-
// eslint-disable-next-line react/no-array-index-key
|
|
94
|
-
key={index}
|
|
95
|
-
text={cta.title}
|
|
96
|
-
href={cta.href}
|
|
97
|
-
onClick={cta.onClick}
|
|
98
|
-
size={size}
|
|
99
|
-
color={theme}
|
|
100
|
-
data-testid={`button-${index}`}
|
|
101
|
-
/>
|
|
102
|
-
)
|
|
103
|
-
}
|
|
104
|
-
})}
|
|
105
|
-
</div>
|
|
106
|
-
)
|
|
107
|
-
}
|