NEW Add version banners at the top of docs pages

This commit is contained in:
Guy Sartorelli 2022-07-22 12:18:04 +12:00
parent 4b9c88219b
commit 4a08724c29
7 changed files with 131 additions and 7 deletions

View File

@ -14,7 +14,7 @@ class IconExtractor {
const matches = content.match(/icon(Brand)?: ([a-zA-Z0-9_-]+)/); const matches = content.match(/icon(Brand)?: ([a-zA-Z0-9_-]+)/);
if (matches) { if (matches) {
const isBrand = typeof matches[1] !== 'undefined'; const isBrand = typeof matches[1] !== 'undefined';
selectors.push(isBrand ? `fab` : `fas`); selectors.push(isBrand ? 'fab' : 'fas');
selectors.push(`fa-${matches[2]}`); selectors.push(`fa-${matches[2]}`);
} }
return selectors; return selectors;
@ -29,6 +29,12 @@ const whitelist = [
// Syntax highlighting // Syntax highlighting
'pre', 'pre',
'code', 'code',
// Icon classes used in nodes.ts
'far',
'fa-calendar',
'fa-check-circle',
'fa-times-circle',
]; ];
const whitelistPatterns = [ const whitelistPatterns = [

View File

@ -11,13 +11,14 @@ interface LayoutProps {
} }
} }
const Layout: StatelessComponent<LayoutProps> = ({ children, pageContext: { slug } }) => { const Layout: StatelessComponent<LayoutProps> = ({ children, pageContext: { slug } }) => {
const { setCurrentPath, getVersionPath, getCurrentVersion, getCurrentNode, getDefaultVersion } = useHierarchy(); const { setCurrentPath, getVersionPath, getCurrentVersion, getCurrentNode, getDefaultVersion, getVersionMessage } = useHierarchy();
const [isToggled, setSidebarOpen] = useState(false); const [isToggled, setSidebarOpen] = useState(false);
const handleNavigate = () => setSidebarOpen(false); const handleNavigate = () => setSidebarOpen(false);
setCurrentPath(slug); setCurrentPath(slug);
const ver = getCurrentVersion(); const ver = getCurrentVersion();
const currentNode = getCurrentNode(); const currentNode = getCurrentNode();
const versionMessage = getVersionMessage();
return ( return (
<> <>
@ -37,7 +38,10 @@ const Layout: StatelessComponent<LayoutProps> = ({ children, pageContext: { slug
<div className="docs-content"> <div className="docs-content">
<div className="container"> <div className="container">
<article role="main" className="docs-article"> <article role="main" className="docs-article">
<>
{versionMessage}
{children} {children}
</>
</article> </article>
</div> </div>
</div> </div>

View File

@ -15,6 +15,7 @@ import {
getVersionPath, getVersionPath,
setCurrentPath, setCurrentPath,
getDefaultVersion, getDefaultVersion,
getVersionMessage,
} from '../utils/nodes'; } from '../utils/nodes';
const NodeProvider: StatelessComponent<{}> = ({ children, pageContext: { slug } }): ReactElement => { const NodeProvider: StatelessComponent<{}> = ({ children, pageContext: { slug } }): ReactElement => {
@ -61,6 +62,7 @@ const NodeProvider: StatelessComponent<{}> = ({ children, pageContext: { slug }
getVersionPath, getVersionPath,
setCurrentPath, setCurrentPath,
getDefaultVersion, getDefaultVersion,
getVersionMessage,
}}> }}>
{children} {children}
</NodeContext.Provider> </NodeContext.Provider>

View File

@ -1,4 +1,4 @@
import { useContext } from 'react'; import { useContext, ReactElement } from 'react';
import NodeContext from '../contexts/NodeContext'; import NodeContext from '../contexts/NodeContext';
import { SilverstripeDocument } from '../types'; import { SilverstripeDocument } from '../types';
@ -15,6 +15,7 @@ interface NodeFunctions {
getVersionPath(currentNode: SilverstripeDocument, version: number|string): string; getVersionPath(currentNode: SilverstripeDocument, version: number|string): string;
setCurrentPath(slug: string): undefined; setCurrentPath(slug: string): undefined;
getDefaultVersion(): string; getDefaultVersion(): string;
getVersionMessage(): ReactElement | ReactElement[] | string | null;
}; };
const useHierarchy = (): NodeFunctions => { const useHierarchy = (): NodeFunctions => {

View File

@ -384,6 +384,60 @@
} }
} }
.callout-block--version {
border-left-width: 6px;
padding-top: 1rem;
padding-bottom: 1rem;
.callout-version-title {
display: flex;
font-size: 1rem;
font-weight: $font-weight-bold;
color: lighten($theme-text-color-primary, 15%);
.callout-version-stability {
display: flex;
align-items: center;
margin-left: auto;
font-size: 1.125rem;
}
.callout-version-stability-text {
text-transform: uppercase;
font-size: 0.875rem;
margin-left: 10px;
}
}
.callout-version-content {
padding-top: 10px;
}
&.callout-block-success {
.callout-version-stability {
color: darken($theme-success-color, 15%);
}
}
&.callout-block-info {
.callout-version-stability {
color: darken($theme-info-color, 15%);
}
}
&.callout-block-warning {
.callout-version-stability {
color: darken($theme-warning-color, 15%);
}
}
&.callout-block-danger {
.callout-version-stability {
color: darken($theme-danger-color, 15%);
}
}
}
.cta-section { .cta-section {
.container { .container {

View File

@ -1,6 +1,7 @@
import parse from 'html-react-parser';
import { ReactElement } from 'react';
import { SilverstripeDocument } from '../types'; import { SilverstripeDocument } from '../types';
import sortFiles from './sortFiles'; import sortFiles from './sortFiles';
import { node } from 'prop-types';
let __nodes: SilverstripeDocument[]; let __nodes: SilverstripeDocument[];
let __currentVersion: string | null = null; let __currentVersion: string | null = null;
@ -149,17 +150,65 @@ const getDefaultVersion = (): string => '4';
*/ */
const getCurrentVersion = (): string => __currentVersion || getDefaultVersion(); const getCurrentVersion = (): string => __currentVersion || getDefaultVersion();
/**
* Get a message to display for all pages on this version's docs
*/
const getVersionMessage = (): ReactElement | ReactElement[] | string | null => {
const EOL = [
'3',
];
const PRE_RELEASE = [
'5',
];
const version = getCurrentVersion();
const stablePath = getVersionPath(getCurrentNode(), getDefaultVersion());
// Output the appropriate message and styling
function makeMessage(style: string, icon: string, stability: string, message: string|null): string {
let template = `<div id="version-callout" class="callout-block callout-block--version callout-block-${style}">
<div class="callout-version-title">Version ${version}<span class="callout-version-stability">
<i class="far fa-${icon}"></i><span class="callout-version-stability-text">${stability}</span></span>
</div>`;
if (message) {
template += `<div class="callout-version-content">
This version of Silverstripe CMS ${message}.
<a href="${stablePath}">Go to documentation for the most recent stable version.</a>
</div>`;
}
template += '</div>';
return template;
}
// Return the correct message for the current version
if (EOL.includes(version)) {
return parse(makeMessage('danger', 'times-circle', 'end of life', 'will not recieve any additional bug fixes or documentation updates'));
}
if (PRE_RELEASE.includes(version)) {
return parse(makeMessage(
'warning',
'calendar',
'pre-stable',
'has not yet been given a stable release. See <a target="_blank" href="https://www.silverstripe.org/software/roadmap/">the release roadmap</a> for more information'
));
}
return parse(makeMessage('success', 'check-circle', 'supported', null));
};
/** /**
* Gets the path in another version * Gets the path in another version
* @param currentNode * @param currentNode
* @param version * @param version
*/ */
const getVersionPath = (currentNode: SilverstripeDocument, version: number): string => { const getVersionPath = (currentNode: SilverstripeDocument|null, version: number|string): string => {
const newPath = currentNode.slug.replace(/^\/en\/[0-9]+\//, `/en/${version}/`); const basePath = `/en/${version}`;
if (!currentNode) {
return basePath;
}
const newPath = currentNode.slug.replace(/^\/en\/[0-9]+\//, `${basePath}/`);
const otherNode = getNodes().find(n => n.slug === newPath); const otherNode = getNodes().find(n => n.slug === newPath);
return otherNode ? otherNode.slug : `/en/${version}`; return otherNode ? otherNode.slug : basePath;
}; };
/** /**
@ -185,4 +234,5 @@ export {
getVersionPath, getVersionPath,
setCurrentPath, setCurrentPath,
getDefaultVersion, getDefaultVersion,
getVersionMessage,
}; };

View File

@ -65,6 +65,13 @@ const rewriteHeaders = (domNode: DomElement): ReactElement | false => {
domNode.children?.push(anchor); domNode.children?.push(anchor);
if (domNode.name === 'h1') {
if (!domNode.attribs) {
domNode.attribs = {};
}
domNode.attribs['aria-details'] = 'version-callout';
}
return domToReact([domNode]); return domToReact([domNode]);
} }