diff --git a/src/components/breadcrumb-bar/index.tsx b/src/components/breadcrumb-bar/index.tsx index de7ae3efd6..de1079faff 100644 --- a/src/components/breadcrumb-bar/index.tsx +++ b/src/components/breadcrumb-bar/index.tsx @@ -27,17 +27,16 @@ function BreadcrumbBar({ // For now, we want to strictly require that all // breadcrumb link URLs, if present, are relative rather // than absolute links - links - .filter((l) => Boolean(l.url)) - .forEach((l) => { - if (isAbsoluteUrl(l.url)) { - throw new Error( - `Absolute URL passed to BreadcrumbBar: "${JSON.stringify( - l - )}". Please ensure all "link.url" values are relative links.` - ) - } - }) + const filteredLinks = links.filter((l) => Boolean(l.url)) + filteredLinks.forEach((l) => { + if (isAbsoluteUrl(l.url)) { + throw new Error( + `Absolute URL passed to BreadcrumbBar: "${JSON.stringify( + l + )}". Please ensure all "link.url" values are relative links.` + ) + } + }) // Now that we're sure all links are relative, // we can render the breadcrumb links @@ -47,7 +46,7 @@ function BreadcrumbBar({ { '@context': 'https://schema.org', '@type': 'BreadcrumbList', - itemListElement: links + itemListElement: filteredLinks // remove items without a url .filter((e) => !!e.url) .map((link, index) => ({ @@ -69,7 +68,7 @@ function BreadcrumbBar({ />
    - {links.map(({ title, url, isCurrentPage }) => { + {filteredLinks.map(({ title, url, isCurrentPage }) => { const cleanTitle = title.replace(/<[^>]+>/g, '') const Elem = url && !isCurrentPage ? InternalLink : 'span' diff --git a/src/components/card/components/card-description/index.tsx b/src/components/card/components/card-description/index.tsx index 0bcbc640c3..954551c669 100644 --- a/src/components/card/components/card-description/index.tsx +++ b/src/components/card/components/card-description/index.tsx @@ -11,11 +11,7 @@ import s from './card-description.module.css' const CardDescription = ({ className, text }: CardDescriptionProps) => { return (
    - + {text}
    diff --git a/src/components/docs-version-switcher/index.tsx b/src/components/docs-version-switcher/index.tsx index b29cf5aada..01fea84045 100644 --- a/src/components/docs-version-switcher/index.tsx +++ b/src/components/docs-version-switcher/index.tsx @@ -73,6 +73,12 @@ const DocsVersionSwitcher = ({ selectedOption = options.find( (option: VersionSelectItem) => option.isLatest === true ) + // In some edge cases, there may be no latest version, such as for + // versioned docs that no longer exist in the latest version. For these + // cases, fallback to selecting the first option. + if (!selectedOption) { + selectedOption = options[0] + } } const projectNameForLabel = setProjectForAriaLabel( diff --git a/src/components/truncate-max-lines/index.tsx b/src/components/truncate-max-lines/index.tsx index 77cdd0e521..09fcf54b1d 100644 --- a/src/components/truncate-max-lines/index.tsx +++ b/src/components/truncate-max-lines/index.tsx @@ -10,7 +10,6 @@ import s from './truncate-max-lines.module.css' function TruncateMaxLines({ children, className, - lineHeight, maxLines, }: TruncateMaxLinesProps) { return ( @@ -19,7 +18,6 @@ function TruncateMaxLines({ style={ { '--max-lines': maxLines, - '--line-height': lineHeight, } as React.CSSProperties } > diff --git a/src/components/truncate-max-lines/truncate-max-lines.module.css b/src/components/truncate-max-lines/truncate-max-lines.module.css index 28862ac4be..5cd039c24e 100644 --- a/src/components/truncate-max-lines/truncate-max-lines.module.css +++ b/src/components/truncate-max-lines/truncate-max-lines.module.css @@ -6,14 +6,11 @@ .root { /** * - * Expects properties to be set in JS (or in parent CSS): + * Expects property to be set in JS (or in parent CSS): * - * var(--line-height): a unitless expression of line height * var(--max-lines): a number of lines to show before truncating */ - /** Both variables are unitless, so we multiply by 1em for "x lines high" */ - max-height: calc(var(--line-height) * var(--max-lines) * 1em); overflow: hidden; text-overflow: ellipsis; diff --git a/src/components/truncate-max-lines/types.ts b/src/components/truncate-max-lines/types.ts index 2bbaa5ee79..16e833e712 100644 --- a/src/components/truncate-max-lines/types.ts +++ b/src/components/truncate-max-lines/types.ts @@ -8,6 +8,5 @@ import type { ReactNode } from 'react' export interface TruncateMaxLinesProps { children: ReactNode className?: string - lineHeight: string maxLines: number } diff --git a/src/components/tutorial-collection-cards/components/card-body/index.tsx b/src/components/tutorial-collection-cards/components/card-body/index.tsx index 43b15d8dfe..308bd3a870 100644 --- a/src/components/tutorial-collection-cards/components/card-body/index.tsx +++ b/src/components/tutorial-collection-cards/components/card-body/index.tsx @@ -9,12 +9,7 @@ import s from './card-body.module.css' function CardBody({ text }: { text: string }) { return (

    - - {text} - + {text}

    ) } diff --git a/src/lib/rehype-code-plugins.ts b/src/lib/rehype-code-plugins.ts index 9f128b7f03..73e48bf38c 100644 --- a/src/lib/rehype-code-plugins.ts +++ b/src/lib/rehype-code-plugins.ts @@ -76,6 +76,7 @@ export const rehypeCodePlugins: Pluggable[] = [ break case 'log': case 'plain-text': + case 'plaintext': case 'ebnf': case 'rego': options.lang = 'text' diff --git a/src/views/docs-view/server.ts b/src/views/docs-view/server.ts index 51ad18d38e..84424c41ed 100644 --- a/src/views/docs-view/server.ts +++ b/src/views/docs-view/server.ts @@ -36,6 +36,7 @@ import { import tutorialMap from 'data/_tutorial-map.generated.json' // Local imports +import { getValidVersions } from './utils/get-valid-versions' import { getProductUrlAdjuster } from './utils/product-url-adjusters' import { getBackToLink } from './utils/get-back-to-link' import { getDeployPreviewLoader } from './utils/get-deploy-preview-loader' @@ -384,6 +385,21 @@ export function getStaticGenerationFunctions< mainWidth: isDocsLanding ? 'wide' : 'narrow', } + /** + * Filter versions to include only those where this document exists + */ + // Construct a document path that the content API will recognize + const pathWithoutVersion = pathParts + .filter((part) => part !== versionPathPart) + .join('/') + const fullPath = `doc#${path.join(basePathForLoader, pathWithoutVersion)}` + // Filter for valid versions, fetching from the content API under the hood + const validVersions = await getValidVersions( + versions, + fullPath, + productSlugForLoader + ) + /** * Determine whether to show the version selector * @@ -392,8 +408,8 @@ export function getStaticGenerationFunctions< * (We use `v0.0.x` as a placeholder version for un-versioned documentation) */ const hasMeaningfulVersions = - versions.length > 0 && - (versions.length > 1 || versions[0].version !== 'v0.0.x') + validVersions.length > 0 && + (validVersions.length > 1 || validVersions[0].version !== 'v0.0.x') /** * We want to show "Edit on GitHub" links for public content repos only. @@ -440,7 +456,7 @@ export function getStaticGenerationFunctions< !hideVersionSelector && !isReleaseNotesPage(currentPathUnderProduct) && // toggle version dropdown hasMeaningfulVersions - ? versions + ? validVersions : null, } diff --git a/src/views/docs-view/utils/get-valid-versions.ts b/src/views/docs-view/utils/get-valid-versions.ts new file mode 100644 index 0000000000..829c93aa4a --- /dev/null +++ b/src/views/docs-view/utils/get-valid-versions.ts @@ -0,0 +1,64 @@ +/** + * Copyright (c) HashiCorp, Inc. + * SPDX-License-Identifier: MPL-2.0 + */ + +// Types +import type { VersionSelectItem } from '../loaders/remote-content' + +const CONTENT_API_URL = process.env.MKTG_CONTENT_API +const VERSIONS_ENDPOINT = '/api/content-versions' + +/** + * Given a list of all possible versions, as well as a document path and + * content repo identifier for our content API, + * Return a filter list of versions that includes only those versions + * where this document exists. + * + * To determine in which versions this document exists, we make a request + * to a content API that returns a list of strings representing known versions. + * We use this to filter out unknown versions from our incoming version list. + */ +export async function getValidVersions( + /** + * An array of version select items representing all possible versions for + * the content source repository in question (`productSlugForLoader`). + * May be undefined or empty if versioned docs are not enabled, for example + * during local preview. + */ + versions: VersionSelectItem[], + /** + * A identifier for the document, consumable by our content API. + * For markdown documents, this is `doc#` followed by the full path of the + * document within the content source repository. + */ + fullPath: string, + /** + * The product slug for the document, consumable by our content API. + * The naming here is difficult, as the actual function here is to identify + * specific content source repositories. These are often but not always + * product slugs. For example Terraform has multiple content source repos + * for different parts of the product. + */ + productSlugForLoader: string +): Promise { + // If versions are falsy or empty, we can skip the API calls and return [] + if (!versions || versions.length === 0) return [] + try { + // Build the URL to fetch known versions of this document + const validVersionsUrl = new URL(VERSIONS_ENDPOINT, CONTENT_API_URL) + validVersionsUrl.searchParams.set('product', productSlugForLoader) + validVersionsUrl.searchParams.set('fullPath', fullPath) + // Fetch known versions of this document + const response = await fetch(validVersionsUrl.toString()) + const { versions: knownVersions } = await response.json() + // Apply the filter, and return the valid versions + return versions.filter((option) => knownVersions.includes(option.version)) + } catch (error) { + console.error( + `[docs-view/server] error fetching known versions for "${productSlugForLoader}" document "${fullPath}". Falling back to showing all versions.`, + error + ) + return versions + } +} diff --git a/src/views/homepage/components/featured-content-grid/certifications-featured-card.module.css b/src/views/homepage/components/featured-content-grid/certifications-featured-card.module.css index 48ec3ca61f..e32ed6de77 100644 --- a/src/views/homepage/components/featured-content-grid/certifications-featured-card.module.css +++ b/src/views/homepage/components/featured-content-grid/certifications-featured-card.module.css @@ -27,7 +27,6 @@ right: 0; top: var(--graphic-position-top); overflow: hidden; - width: fit-content; margin: 0 auto; pointer-events: none; } diff --git a/src/views/homepage/components/featured-content-grid/hcp-featured-card.module.css b/src/views/homepage/components/featured-content-grid/hcp-featured-card.module.css index 11f2fbc0a2..e3e14381bd 100644 --- a/src/views/homepage/components/featured-content-grid/hcp-featured-card.module.css +++ b/src/views/homepage/components/featured-content-grid/hcp-featured-card.module.css @@ -6,9 +6,7 @@ .hcpCard { background-color: var(--token-color-foreground-strong); background-image: url('../../img/hcp-graphic.svg'); - background-position: center top; background-repeat: no-repeat; - background-size: cover; border: none; gap: 50px; padding-top: 88px; diff --git a/src/views/homepage/components/featured-content-grid/search-featured-card.module.css b/src/views/homepage/components/featured-content-grid/search-featured-card.module.css index a358026003..10d891cb9b 100644 --- a/src/views/homepage/components/featured-content-grid/search-featured-card.module.css +++ b/src/views/homepage/components/featured-content-grid/search-featured-card.module.css @@ -9,7 +9,6 @@ background-color: var(--token-color-foreground-strong); background-position: center top; background-repeat: no-repeat; - background-size: cover; border-radius: 10px; padding-bottom: 18px; padding-left: 24px; @@ -19,7 +18,6 @@ /* 500px with 16px base font */ @media (min-width: 31.25rem) { background-image: url('../../img/search-card-graphic.svg'); - background-position-x: 1rem; } @nest html[data-theme='dark'] & { diff --git a/src/views/homepage/components/featured-content-grid/waf-featured-card.module.css b/src/views/homepage/components/featured-content-grid/waf-featured-card.module.css index 887c8330e1..e0406f0a9a 100644 --- a/src/views/homepage/components/featured-content-grid/waf-featured-card.module.css +++ b/src/views/homepage/components/featured-content-grid/waf-featured-card.module.css @@ -18,7 +18,7 @@ left: 0; position: absolute; right: 0; - top: -25px; + top: 0; width: 100%; pointer-events: none; } diff --git a/src/views/homepage/components/page-title/page-title.module.css b/src/views/homepage/components/page-title/page-title.module.css index f22d07df8e..235d0fbd99 100644 --- a/src/views/homepage/components/page-title/page-title.module.css +++ b/src/views/homepage/components/page-title/page-title.module.css @@ -32,12 +32,15 @@ --bg-gradient-base: var(--token-color-foreground-high-contrast); background: linear-gradient( - 90deg, - rgba(136, 143, 255, 0.46) 9.03%, - rgba(223, 255, 199, 0.57) 41.12%, - rgba(255, 177, 80, 0.31) 61.68% - ), - linear-gradient(var(--bg-gradient-base), var(--bg-gradient-base)); + 78.87deg, + #b0ffff 2.19%, + #8fd0ff 16.24%, + #ddbfff 31.62%, + #ffaed0 53.79%, + #ffbeaf 67.39%, + #fff8c9 79.68%, + #fff 91.59% + ); background-clip: text; color: transparent; display: block; diff --git a/src/views/homepage/homepage.module.css b/src/views/homepage/homepage.module.css index ec0173a311..6b9856fc11 100644 --- a/src/views/homepage/homepage.module.css +++ b/src/views/homepage/homepage.module.css @@ -23,10 +23,9 @@ .background { background-color: var(--token-color-foreground-strong); background-image: url('./img/background.svg'); - background-position-x: center; + background-position-x: right; background-position-y: top; background-repeat: no-repeat; - background-size: cover; content: ''; height: 1145px; position: absolute; @@ -40,7 +39,6 @@ @nest html[data-theme='dark'] & { background-color: var(--token-color-page-primary); - background-image: url('./img/background-dark.svg'); } } diff --git a/src/views/homepage/img/background-dark.svg b/src/views/homepage/img/background-dark.svg deleted file mode 100644 index 6b8e9c19bc..0000000000 --- a/src/views/homepage/img/background-dark.svg +++ /dev/null @@ -1,52 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - diff --git a/src/views/homepage/img/background.svg b/src/views/homepage/img/background.svg index 4c8d3eac35..eb7e4e2b5b 100644 --- a/src/views/homepage/img/background.svg +++ b/src/views/homepage/img/background.svg @@ -1,61 +1,128 @@ - - - - - - - + + + - - - - + + + + + - - - - + + + + + + + + + - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/src/views/homepage/img/certifications-graphic.svg b/src/views/homepage/img/certifications-graphic.svg index a41abea1c2..1cec3582d8 100644 --- a/src/views/homepage/img/certifications-graphic.svg +++ b/src/views/homepage/img/certifications-graphic.svg @@ -1,134 +1,142 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - + + + - - - - + + + + + + + + + + + + + + + + + + - - - + + + - - - - - - - - - - - - - - - - - - - - - + + + + + + + + + + + + + + + - - - - + + + - - - - - - + + + + + - - - - - - - - - - - - - + + + + + + + + + + + + + + + + - - - + + + + - + - - + + + - - + + - - - - - - - - - + - + - + - - - + + + + + + + + + + + + + + + + + + + + + + + + + - - + + + + + + + + diff --git a/src/views/homepage/img/hcp-graphic.svg b/src/views/homepage/img/hcp-graphic.svg index 2d72f315d8..137b4cad63 100644 --- a/src/views/homepage/img/hcp-graphic.svg +++ b/src/views/homepage/img/hcp-graphic.svg @@ -1,42 +1,62 @@ - - - - - - - - + + + + + + + + + + + + + + + + - + + + + + + + + + + + + + - - + + + + + + + + + - - - - - - - - - - - - - - - - - - - - + + + + + + + + + + + + + - - + + diff --git a/src/views/homepage/img/search-card-graphic.svg b/src/views/homepage/img/search-card-graphic.svg index 7f96054e57..82467fd4cf 100644 --- a/src/views/homepage/img/search-card-graphic.svg +++ b/src/views/homepage/img/search-card-graphic.svg @@ -1,391 +1,68 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + + + + + + + + + + + + + + + + - + + + + + + + - - + + + + + + + - + - - + + + + + + + - - - + + + + + + + - - - + + + - - - + + + - - - + + + - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + + diff --git a/src/views/homepage/img/waf-graphic.svg b/src/views/homepage/img/waf-graphic.svg index 620242b357..efb190a428 100644 --- a/src/views/homepage/img/waf-graphic.svg +++ b/src/views/homepage/img/waf-graphic.svg @@ -1,170 +1,91 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + + + + + + + + + + + + + + + + + + + + + + + + + + + + - - - - - - - - - - - - - - - - - - - - + + + - - - - - - - - - - - - - - - - - - - - - - - + + + + - - - - - - - - + + + - - - + + + + - - - - - - - + + + + - - - - - - - - - - - - - - - + + + - + - - - - - - - - + + + + - - - + + + + - - - + + + - - - - + + + + - - - + + + - - - + + + + - - -