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:
19
website/src/components/PreviewBadge.tsx
Normal file
19
website/src/components/PreviewBadge.tsx
Normal 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;
|
||||
34
website/src/components/SupportBadge.tsx
Normal file
34
website/src/components/SupportBadge.tsx
Normal 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;
|
||||
38
website/src/components/VersionBadge.tsx
Normal file
38
website/src/components/VersionBadge.tsx
Normal 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: {parsed.format()}+
|
||||
</span>
|
||||
);
|
||||
};
|
||||
|
||||
export default VersionBadge;
|
||||
@ -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. */
|
||||
|
||||
23
website/src/hooks/title.ts
Normal file
23
website/src/hooks/title.ts
Normal 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;
|
||||
}
|
||||
88
website/src/theme/DocItem/Content/index.tsx
Normal file
88
website/src/theme/DocItem/Content/index.tsx
Normal 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;
|
||||
Reference in New Issue
Block a user