website: Use Docusaurus Frontmatter for badges (#12893)

website/docs: Reduce redundant usage of badges. Move badge logic to components.

- Fix JSX class name warning.
- Remove duplicate titles.
- Flesh out `support_level` frontmatter.
This commit is contained in:
Teffen Ellis
2025-02-19 19:03:05 +01:00
committed by GitHub
parent df2e3878d5
commit a714c781a6
214 changed files with 930 additions and 748 deletions

View File

@ -0,0 +1,19 @@
import React from "react";
/**
* Badge indicating the preview status of a feature or integration.
*/
export const PreviewBadge: React.FC = () => {
return (
<span
title="This feature is in preview and may change in the future."
aria-description="Preview badge"
role="img"
className="badge badge--preview"
>
Preview
</span>
);
};
export default PreviewBadge;

View File

@ -0,0 +1,34 @@
import React from "react";
import {
isSupportLevel,
SupportLevelToLabel,
} from "@site/remark/support-directive";
export interface SupportBadgeProps {
level: string;
}
/**
* Badge indicating the support level of a feature or integration.
*/
export const SupportBadge: React.FC<SupportBadgeProps> = ({ level }) => {
if (!isSupportLevel(level)) {
throw new TypeError(`Invalid support level: ${level}`);
}
const label = SupportLevelToLabel[level];
const className = `badge badge--support-${level}`;
return (
<span
aria-description="Support level badge"
title={`This feature is supported at the ${label} level.`}
role="img"
className={className}
>
Support level: {label}
</span>
);
};
export default SupportBadge;

View File

@ -0,0 +1,38 @@
import React from "react";
import { coerce } from "semver";
export interface AuthentikVersionProps {
semver: string;
}
/**
* Badge indicating semantic versioning of authentik required for a feature or integration.
*/
export const VersionBadge: React.FC<AuthentikVersionProps> = ({ semver }) => {
const parsed = coerce(semver);
if (!parsed) {
throw new Error(`Invalid semver version: ${semver}`);
}
const yearCutoff = new Date().getFullYear() - 2;
if (parsed.major <= yearCutoff) {
throw new Error(
`Semver version <= ${yearCutoff} is not supported: ${semver}`,
);
}
return (
<span
title={`Available in authentik ${parsed.format()} and later`}
aria-description="Version badge"
role="img"
className="badge badge--version"
>
authentik:&nbsp;{parsed.format()}+
</span>
);
};
export default VersionBadge;

View File

@ -1,4 +1,4 @@
/*#region root*/
/* #region root */
:root {
--ifm-color-primary: #fd4b2d;
@ -13,9 +13,9 @@
--ifm-navbar-link-hover-color: var(--ifm-color-gray-1000);
}
/*#endregion*/
/* #endregion */
/*#region Buttons*/
/* #region Buttons */
.button.button--outline {
color: var(--white) !important;
@ -27,9 +27,9 @@
--ifm-button-background-color: var(--white);
}
/*#endregion*/
/* #endregion */
/*#region Navbar*/
/* #region Navbar */
.navbar {
background-color: var(--ifm-color-primary);
@ -47,9 +47,9 @@
stroke: var(--white);
}
/*#endregion*/
/* #endregion */
/*#region Header*/
/* #region Header */
.header-github-link:hover {
opacity: 0.6;
@ -77,7 +77,7 @@
no-repeat;
}
/*#endregion*/
/* #endregion */
@media (min-width: 1600px) {
#__docusaurus_skipToContent_fallback > div {
@ -99,11 +99,12 @@
src: url("https://fonts.googleapis.com/css2?family=Roboto:wght@300&display=swap");
}
/* #region Containers */
body {
font-family: "Roboto", sans-serif;
}
/* Container styles */
.content {
width: 100vw;
height: 100vh;
@ -112,6 +113,10 @@ body {
justify-content: center;
}
/* #endregion */
/* #region Sidebar */
/* styling for version selector in sidebar */
.theme-doc-sidebar-menu .dropdown {
display: block;
@ -128,31 +133,75 @@ body {
margin-right: -0.5rem;
}
/* #endregion */
/* #region Navbar */
/* Nav header background color on mobile */
.navbar-sidebar__brand,
.navbar-sidebar__items {
background-color: var(--ifm-color-primary);
}
.badge--version {
--ifm-badge-background-color: var(--ifm-color-primary-contrast-background);
color: var(--ifm-color-primary-contrast-foreground);
--ifm-badge-border-color: var(--ifm-badge-background-color);
/* #endregion */
/* #region Badges */
.anchor > .badge {
font-size: 0.75rem;
vertical-align: middle;
}
.anchor > .badge {
margin-left: 0.5rem;
}
.badge--support-vendor {
--ifm-badge-background-color: var(--ifm-color-warning-contrast-background);
--ifm-badge-color: var(--ifm-color-warning-contrast-foreground);
--ifm-badge-border-color: var(--ifm-color-warning-contrast-foreground);
}
.badge--support-community {
--ifm-badge-background-color: var(--ifm-color-secondary);
--ifm-badge-border-color: var(--ifm-color-secondary-contrast-background);
--ifm-badge-color: var(--ifm-color-secondary-contrast-background);
}
.badge--support-deprecated {
--ifm-badge-background-color: var(--ifm-color-danger);
--ifm-badge-border-color: var(--ifm-color-danger-contrast-background);
--ifm-badge-color: var(--ifm-color-danger-contrast-foreground);
}
.badge--support-authentik {
--ifm-badge-background-color: var(--ifm-color-primary);
--ifm-badge-border-color: var(--ifm-color-primary-contrast-foreground);
--ifm-badge-color: var(--ifm-color-primary-contrast-foreground);
}
.badge--version {
--ifm-badge-background-color: var(--ifm-color-primary-contrast-background);
--ifm-badge-border-color: var(--ifm-color-primary-contrast-foreground);
--ifm-badge-color: var(--ifm-color-primary-contrast-foreground);
cursor: help;
}
.badge--preview {
--ifm-badge-background-color: rgb(115, 188, 247);
color: var(--ifm-color-primary-contrast-foreground);
--ifm-badge-border-color: var(--ifm-badge-background-color);
font-size: 0.75rem;
vertical-align: middle;
--ifm-badge-color: var(--ifm-color-primary-contrast-foreground);
}
/*#endregion*/
.badge-group {
display: flex;
flex-wrap: wrap;
gap: 1rem;
}
/*#region Mermaid*/
/* #endregion */
/* #region Mermaid */
.docusaurus-mermaid-container {
/* Improve contrast. */
@ -163,7 +212,7 @@ body {
paint-order: stroke;
}
}
/*#endregion*/
/* #endregion */
.markdown {
/* Remove empty table headers. */

View File

@ -0,0 +1,23 @@
import { useDoc } from "@docusaurus/plugin-content-docs/client";
/**
* Title can be declared inside md content or declared through
* front matter and added manually. To make both cases consistent,
* the added title is added under the same div.markdown block
* See https://github.com/facebook/docusaurus/pull/4882#issuecomment-853021120
*
* We render a "synthetic title" if:
* - user doesn't ask to hide it with front matter
* - the markdown content does not already contain a top-level h1 heading
*
* @vendor docusaurus
*/
export function useSyntheticTitle(): string | null {
const { metadata, frontMatter, contentTitle } = useDoc();
const shouldRender =
!frontMatter.hide_title && typeof contentTitle === "undefined";
if (!shouldRender) {
return null;
}
return metadata.title;
}

View File

@ -0,0 +1,88 @@
/**
* @file Swizzled DocItemContent component.
*
* This component is a swizzled version of the original DocItemContent component.
*
* Similar to Docusaurus' default `DocItemContent`, this component renders
* the content of a documentation page. However, it also adds support for
* support badges, and Authentik version badges.
*/
import React from "react";
import clsx from "clsx";
import { ThemeClassNames } from "@docusaurus/theme-common";
import {
DocContextValue,
useDoc,
} from "@docusaurus/plugin-content-docs/client";
import Heading from "@theme/Heading";
import MDXContent from "@theme/MDXContent";
import type { Props } from "@theme/DocItem/Content";
import { DocFrontMatter } from "@docusaurus/plugin-content-docs";
import { useSyntheticTitle } from "@site/src/hooks/title";
import { SupportBadge } from "@site/src/components/SupportBadge";
import { VersionBadge } from "@site/src/components/VersionBadge";
interface SwizzledDocFrontMatter extends DocFrontMatter {
support_level?: string;
authentik_version?: string;
authentik_preview: boolean;
authentik_enterprise: boolean;
}
interface SwizzledDocContextValue extends DocContextValue {
frontMatter: SwizzledDocFrontMatter;
}
const DocItemContent: React.FC<Props> = ({ children }) => {
const syntheticTitle = useSyntheticTitle();
const { frontMatter } = useDoc() as SwizzledDocContextValue;
const {
support_level,
authentik_version,
authentik_enterprise,
authentik_preview,
} = frontMatter;
const badges: JSX.Element[] = [];
if (authentik_version) {
badges.push(<VersionBadge semver={authentik_version} />);
}
if (support_level) {
badges.push(<SupportBadge level={support_level} />);
}
if (authentik_preview) {
badges.push(<span className="badge badge--preview">Preview</span>);
}
if (authentik_enterprise) {
badges.push(<span className="badge badge--primary">Enterprise</span>);
}
return (
<div className={clsx(ThemeClassNames.docs.docMarkdown, "markdown")}>
{syntheticTitle ? (
<header>
<Heading as="h1">{syntheticTitle}</Heading>
{badges.length ? (
<p className="badge-group">
{badges.map((badge, index) => (
<React.Fragment key={index}>
{badge}
</React.Fragment>
))}
</p>
) : null}
</header>
) : null}
<MDXContent>{children}</MDXContent>
</div>
);
};
export default DocItemContent;