mirror of
https://github.com/mattermost/mattermost.git
synced 2026-08-27 05:37:15 -05:00
Fix docs migration rendering issues (Mermaid, tables, callouts, version filter) (#37511)
* Enable Mermaid rendering for release policy Gantt chart.
Docusaurus requires the theme-mermaid plugin and standard ```mermaid fences instead of Sphinx MyST ```{mermaid} syntax.
Co-authored-by: Cursor <cursoragent@cursor.com>
* Fix clipped text in important upgrade notes table.
Convert the migrated RST grid table to a two-column layout and restore proper table cell display so long upgrade notes wrap instead of being cut off.
Co-authored-by: Cursor <cursoragent@cursor.com>
* Fix PostgreSQL version policy table formatting.
Replace the broken Sphinx grid-table markup with a standard HTML table so version, release date, and minimum PostgreSQL version render correctly.
Co-authored-by: Cursor <cursoragent@cursor.com>
* Fix callout rendering in Linux deployment guides.
Replace unrendered ::: admonition syntax with the project's Note, Tip, and Important MDX components in the RHEL and tarball install guides.
Co-authored-by: Cursor <cursoragent@cursor.com>
* Add version filter to important upgrade notes page.
Port the Sphinx version-filter widget to a React component so admins can narrow upgrade notes by source and target version during the docs migration.
Co-authored-by: Cursor <cursoragent@cursor.com>
* Fix UpgradeNotesFilter TypeScript row element typing.
Use querySelectorAll<HTMLTableRowElement> so table row refs satisfy the RowRef type during docs-site typecheck.
Co-authored-by: Cursor <cursoragent@cursor.com>
* Improve release policy Gantt chart readability.
Port the Sphinx Mermaid styling so done and active bars render blue, ESR bars render red, and task labels use larger bold white text inside the bars.
Co-authored-by: Cursor <cursoragent@cursor.com>
* Fix upgrade notes filter and table layout.
Wait for the table to mount before collecting versions, hide whole version groups to avoid rowspan breakage, and restore a fixed two-column table layout so note text is no longer clipped.
Co-authored-by: Cursor <cursoragent@cursor.com>
* Fix markdown table header column alignment.
Restore native table layout for standard docs tables so thead and tbody share column widths; keep the upgrade-notes wrapper for wide tables that need horizontal scroll.
Co-authored-by: Cursor <cursoragent@cursor.com>
* Fix illegible release policy Gantt chart text.
Mermaid renders the Gantt SVG at a viewBox sized to its own internal
layout, then Docusaurus scales it down (~0.57x) to fit the docs
content column. Task/section labels configured at 14px were rendering
on-screen at ~7px, unreadable without manually zooming the browser.
Scale up the diagram's font size and spacing to compensate.
Co-authored-by: Cursor <cursoragent@cursor.com>
* Fix header line wrap and cell overflow in upgrade notes table
Force a line break in the header label and constrain header/cell
styling so the two-line header no longer overlaps itself, and long
cell content (including SQL code blocks) wraps within the column
instead of overflowing the table.
Co-authored-by: Cursor <cursoragent@cursor.com>
* Make Mermaid Gantt outside labels readable in light mode.
Co-authored-by: Cursor <cursoragent@cursor.com>
---------
Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
File diff suppressed because one or more lines are too long
@@ -8,12 +8,14 @@ sidebar_label: "Red Hat (RHEL)"
|
||||
|
||||
Install Mattermost Server on Red Hat Enterprise Linux (RHEL), Rocky Linux, AlmaLinux, Oracle Linux 7+, or CentOS Stream. RHEL doesn't have a signed APT-style repository, so this guide installs from the release tarball with manual systemd setup, then walks through the security configuration RHEL deployments typically need (SELinux contexts, firewalld rules, fapolicyd allow rules).
|
||||
|
||||
:::info Minimum requirements
|
||||
<Note title="Minimum requirements">
|
||||
|
||||
- **Operating system**: RHEL 7+, Rocky / Alma 8+, Oracle Linux 7+, CentOS Stream 8+.
|
||||
- **Hardware**: 1 vCPU and 2 GB RAM (supports up to ~1,000 users).
|
||||
- **Database**: [PostgreSQL 14+](/deployment-guide/postgres-migration).
|
||||
- **Network**: TCP 80/443 inbound (TLS), 8065 inbound (System Console), 10025 outbound (SMTP relay if used).
|
||||
:::
|
||||
|
||||
</Note>
|
||||
|
||||
## Step 1: Get a PostgreSQL database
|
||||
|
||||
@@ -73,9 +75,11 @@ sudo chown -R mattermost:mattermost /opt/mattermost
|
||||
sudo chmod -R g+w /opt/mattermost
|
||||
```
|
||||
|
||||
:::note Custom paths and users
|
||||
<Note title="Custom paths and users">
|
||||
|
||||
If you use a path other than `/opt/mattermost` or a user/group name other than `mattermost`, use that name in every step that follows.
|
||||
:::
|
||||
|
||||
</Note>
|
||||
|
||||
Create the systemd unit file at `/lib/systemd/system/mattermost.service`:
|
||||
|
||||
@@ -197,9 +201,11 @@ sudo semodule -i mattermost_policy.pp
|
||||
sudo systemctl restart mattermost
|
||||
```
|
||||
|
||||
:::tip Testing-only fallback
|
||||
<Tip title="Testing-only fallback">
|
||||
|
||||
For debugging, you can temporarily switch SELinux to permissive mode with `sudo setenforce 0`. Re-enable enforcement with `sudo setenforce 1` once contexts are correct. Don't ship a production deployment in permissive mode.
|
||||
:::
|
||||
|
||||
</Tip>
|
||||
|
||||
**References**
|
||||
|
||||
@@ -310,9 +316,11 @@ allow perm=execute exe=/usr/lib/systemd/systemd trust=1 : dir=/opt/rtcd/ all tru
|
||||
|
||||
Then reload fapolicyd as above. See [RTCD setup and configuration](/administration-guide/configure/calls-deployment-guide) for the full rtcd install path.
|
||||
|
||||
:::note Rule numbering
|
||||
<Note title="Rule numbering">
|
||||
|
||||
fapolicyd rules are evaluated in order. Your allow rules must be numbered lower than the deny rule that's blocking Mattermost — `80-` is usually safe with a stock configuration. If denials persist, re-check the rule number from `fapolicyd --debug` and renumber accordingly.
|
||||
:::
|
||||
|
||||
</Note>
|
||||
|
||||
**Reference**: [Mattermost and fapolicyd](https://support.mattermost.com/hc/en-us/articles/12167526545172-Mattermost-and-fapolicyd) support article.
|
||||
|
||||
@@ -330,9 +338,11 @@ sudo rm /lib/systemd/system/mattermost.service
|
||||
sudo userdel mattermost
|
||||
```
|
||||
|
||||
:::important Back up before removing
|
||||
<Important title="Back up before removing">
|
||||
|
||||
`/opt/mattermost` contains `config/`, `logs/`, `plugins/`, `client/plugins/`, and `data/`. Back these up before running `rm -rf` if you may need to restore.
|
||||
:::
|
||||
|
||||
</Important>
|
||||
|
||||
## Next steps
|
||||
|
||||
|
||||
@@ -13,16 +13,20 @@ Install Mattermost Server on any 64-bit Linux distribution directly from the rel
|
||||
- Custom paths, users, or systemd unit configurations
|
||||
- Environments where you control every version pin and don't want a package manager auto-upgrade
|
||||
|
||||
:::tip Use a packaged install if it fits
|
||||
If you're on **Ubuntu / Debian**, use the [APT-based install](./deploy-ubuntu) for automatic security updates. If you're on **RHEL / Rocky / Alma / Oracle**, use the [RHEL install](./deploy-rhel) — it covers SELinux, firewalld, and fapolicyd configuration too.
|
||||
:::
|
||||
<Tip title="Use a packaged install if it fits">
|
||||
|
||||
If you're on **Ubuntu / Debian**, use the [APT-based install](./deploy-ubuntu) for automatic security updates. If you're on **RHEL / Rocky / Alma / Oracle**, use the [RHEL install](./deploy-rhel) — it covers SELinux, firewalld, and fapolicyd configuration too.
|
||||
|
||||
</Tip>
|
||||
|
||||
<Note title="Minimum requirements">
|
||||
|
||||
:::info Minimum requirements
|
||||
- **Operating system**: any 64-bit Linux distribution with `systemd` (or a comparable service manager).
|
||||
- **Hardware**: 1 vCPU and 2 GB RAM (supports up to ~1,000 users).
|
||||
- **Database**: [PostgreSQL 14+](/deployment-guide/postgres-migration).
|
||||
- **Network**: TCP 80/443 inbound (TLS), 8065 inbound (System Console), 10025 outbound (SMTP relay if used).
|
||||
:::
|
||||
|
||||
</Note>
|
||||
|
||||
## Step 1: Get a PostgreSQL database
|
||||
|
||||
@@ -62,9 +66,11 @@ Enterprise and Team Edition releases are listed in the [version archive](/produc
|
||||
</TabItem>
|
||||
</Tabs>
|
||||
|
||||
:::note Air-gapped installs
|
||||
<Note title="Air-gapped installs">
|
||||
|
||||
In an air-gapped environment, stage the tarball on your operator workstation, verify the SHA-256 and PGP signature, then transfer it across the boundary. See [Air-Gapped Operations → Quick-Start Runbook](/deployment-guide/air-gapped-operations/quick-start-runbook) for the full procedure.
|
||||
:::
|
||||
|
||||
</Note>
|
||||
|
||||
## Step 4: Install Mattermost Server
|
||||
|
||||
@@ -79,9 +85,11 @@ sudo chown -R mattermost:mattermost /opt/mattermost
|
||||
sudo chmod -R g+w /opt/mattermost
|
||||
```
|
||||
|
||||
:::note Custom paths and users
|
||||
<Note title="Custom paths and users">
|
||||
|
||||
If you use a path other than `/opt/mattermost` or a user/group name other than `mattermost`, use that name in every step that follows.
|
||||
:::
|
||||
|
||||
</Note>
|
||||
|
||||
Create the systemd unit file at `/lib/systemd/system/mattermost.service`:
|
||||
|
||||
@@ -106,14 +114,16 @@ LimitNOFILE=49152
|
||||
WantedBy=multi-user.target
|
||||
```
|
||||
|
||||
:::note Local database
|
||||
<Note title="Local database">
|
||||
|
||||
If PostgreSQL is on the same host, add to the `[Unit]` section so Mattermost won't start before the database is ready:
|
||||
|
||||
```ini
|
||||
After=postgresql.service
|
||||
BindsTo=postgresql.service
|
||||
```
|
||||
:::
|
||||
|
||||
</Note>
|
||||
|
||||
Reload systemd:
|
||||
|
||||
@@ -171,9 +181,11 @@ sudo rm /lib/systemd/system/mattermost.service
|
||||
sudo userdel mattermost
|
||||
```
|
||||
|
||||
:::important Back up before removing
|
||||
<Important title="Back up before removing">
|
||||
|
||||
`/opt/mattermost` contains `config/`, `logs/`, `plugins/`, `client/plugins/`, and `data/`. Back these up before running `rm -rf` if you may need to restore.
|
||||
:::
|
||||
|
||||
</Important>
|
||||
|
||||
## Next steps
|
||||
|
||||
|
||||
@@ -196,39 +196,39 @@ Mattermost v10.6 is not an [Extended Support Release (ESR)](/product-overview/re
|
||||
|
||||
When a PostgreSQL version reaches its end of life (EOL), Mattermost will require a newer version starting with the next scheduled ESR release. This means the following future PostgreSQL minimum version increases as follows:
|
||||
|
||||
<table style={{width: '98%'}}>
|
||||
<colgroup>
|
||||
<col style={{width: '50%'}} />
|
||||
<col style={{width: '15%'}} />
|
||||
<col style={{width: '27%'}} />
|
||||
<col style={{width: '3%'}} />
|
||||
</colgroup>
|
||||
<table>
|
||||
<thead>
|
||||
<tr>
|
||||
<th>Mattermost Version</th>
|
||||
<th>Release Date</th>
|
||||
<th>Minimum PostgreSQL Version</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
<tr>
|
||||
<td colspan="4"><blockquote>
|
||||
<p><strong>Mattermost Version</strong> | <strong>Release Date</strong> | <strong>Minimum PostgreSQL Version</strong> |</p>
|
||||
</blockquote>
|
||||
<dl>
|
||||
<dt>============================================================+==================+================================+</dt>
|
||||
<dd>
|
||||
<p><a href="mm-ref:release-v9-11-extended-support-release">v9.11 ESR</a> | 2024-8-15 | 11.x</p>
|
||||
</dd>
|
||||
</dl></td>
|
||||
<td><a href="/product-overview/unsupported-legacy-releases#release-v9-11-extended-support-release">v9.11 ESR</a></td>
|
||||
<td>2024-8-15</td>
|
||||
<td>11.x</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td colspan="4"><a href="mm-ref:release-v10.5-extended-support-release">v10.5 ESR</a> | 2025-2-15 | 11.x</td>
|
||||
<td><a href="/product-overview/mattermost-v10-changelog#release-v10-5-extended-support-release">v10.5 ESR</a></td>
|
||||
<td>2025-2-15</td>
|
||||
<td>11.x</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td colspan="4"><a href="mm-ref:release-v10.6-feature-release">v10.6</a> | 2025-3-15 | 13.x</td>
|
||||
<td><a href="/product-overview/mattermost-v10-changelog#release-v10-6-feature-release">v10.6</a></td>
|
||||
<td>2025-3-15</td>
|
||||
<td>13.x</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td colspan="4"><a href="mm-ref:release-v10.11-extended-support-release">v10.11 ESR</a>| 2025-8-15 | 13.x</td>
|
||||
<td><a href="/product-overview/mattermost-v10-changelog#release-v10-11-extended-support-release">v10.11 ESR</a></td>
|
||||
<td>2025-8-15</td>
|
||||
<td>13.x</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td><a href="mm-ref:release-v11.7-extended-support-release">v11.7 ESR</a></td>
|
||||
<td><a href="/product-overview/mattermost-v11-changelog#release-v11-7-extended-support-release">v11.7 ESR</a></td>
|
||||
<td>2026-5-15</td>
|
||||
<td>14.x (EOL 2026-11-12)</td>
|
||||
<td></td>
|
||||
</tr>
|
||||
</tbody>
|
||||
</table>
|
||||
|
||||
@@ -42,8 +42,32 @@ We highly recommend working with your Mattermost Account Team to plan for a migr
|
||||
|
||||
</Important>
|
||||
|
||||
```{mermaid}
|
||||
<div className="release-policy-gantt">
|
||||
|
||||
```mermaid
|
||||
---
|
||||
config:
|
||||
gantt:
|
||||
fontSize: 22
|
||||
sectionFontSize: 22
|
||||
barHeight: 38
|
||||
barGap: 6
|
||||
topPadding: 22
|
||||
leftPadding: 60
|
||||
gridLineStartPadding: 22
|
||||
themeVariables:
|
||||
sectionBkgColor: '#ffffff'
|
||||
altSectionBkgColor: '#ffffff'
|
||||
tertiaryColor: '#ffffff'
|
||||
primaryColor: '#0984e3'
|
||||
doneTaskBkgColor: '#0984e3'
|
||||
activeTaskBkgColor: '#0984e3'
|
||||
critBkgColor: '#d63031'
|
||||
taskTextColor: '#ffffff'
|
||||
taskTextDarkColor: '#ffffff'
|
||||
taskTextLightColor: '#ffffff'
|
||||
lineColor: '#0745a1'
|
||||
---
|
||||
gantt
|
||||
dateFormat YYYY-MM-DD
|
||||
axisFormat %b %y
|
||||
@@ -64,6 +88,8 @@ gantt
|
||||
v11.9 :active, 2026-07-16, 2026-10-15
|
||||
```
|
||||
|
||||
</div>
|
||||
|
||||
**Timeline Legend:**
|
||||
The chart above shows both release dates and end-of-life dates for each version. ESRs provide longer-term stability for organizations preferring less frequent updates.
|
||||
- 🔵 **Blue bars**: Regular feature releases (monthly releases with standard support lifecycle)
|
||||
|
||||
@@ -55,6 +55,7 @@ const config: Config = {
|
||||
onBrokenLinks: 'warn',
|
||||
|
||||
markdown: {
|
||||
mermaid: true,
|
||||
hooks: {
|
||||
onBrokenMarkdownLinks: 'warn',
|
||||
onBrokenMarkdownImages: 'warn',
|
||||
@@ -156,7 +157,7 @@ const config: Config = {
|
||||
|
||||
// Theme for the API endpoint pages (parameter tables, request/response
|
||||
// schemas, code-sample picker). Layered on the classic preset theme.
|
||||
themes: ['docusaurus-theme-openapi-docs'],
|
||||
themes: ['docusaurus-theme-openapi-docs', '@docusaurus/theme-mermaid'],
|
||||
|
||||
themeConfig: {
|
||||
image: 'img/brand/logo-horizontal-denim.svg',
|
||||
@@ -241,6 +242,41 @@ const config: Config = {
|
||||
darkTheme: prismThemes.dracula,
|
||||
additionalLanguages: ['bash', 'powershell', 'json', 'yaml', 'go', 'python'],
|
||||
},
|
||||
mermaid: {
|
||||
theme: {light: 'base', dark: 'dark'},
|
||||
options: {
|
||||
themeVariables: {
|
||||
fontSize: '14px',
|
||||
primaryColor: '#0984e3',
|
||||
primaryTextColor: '#ffffff',
|
||||
primaryBorderColor: '#2d3436',
|
||||
lineColor: '#0745a1',
|
||||
secondaryColor: '#0984e3',
|
||||
tertiaryColor: '#d63031',
|
||||
mainBkg: '#ffffff',
|
||||
textColor: '#2d3436',
|
||||
taskBkgColor: '#0984e3',
|
||||
taskTextColor: '#ffffff',
|
||||
taskTextLightColor: '#ffffff',
|
||||
taskTextDarkColor: '#ffffff',
|
||||
activeTaskBkgColor: '#0984e3',
|
||||
activeTaskBorderColor: '#2d3436',
|
||||
doneTaskBkgColor: '#0984e3',
|
||||
doneTaskBorderColor: '#2d3436',
|
||||
critBkgColor: '#d63031',
|
||||
critBorderColor: '#2d3436',
|
||||
},
|
||||
gantt: {
|
||||
fontSize: 14,
|
||||
sectionFontSize: 14,
|
||||
barHeight: 28,
|
||||
barGap: 6,
|
||||
topPadding: 50,
|
||||
leftPadding: 75,
|
||||
gridLineStartPadding: 35,
|
||||
},
|
||||
},
|
||||
},
|
||||
// Code-sample tabs shown on every endpoint page, in display order.
|
||||
// First entry is the default selected tab (curl).
|
||||
languageTabs: [
|
||||
|
||||
Generated
+1118
-2
File diff suppressed because it is too large
Load Diff
@@ -25,6 +25,7 @@
|
||||
"@docusaurus/faster": "3.10.1",
|
||||
"@docusaurus/plugin-client-redirects": "^3.10.1",
|
||||
"@docusaurus/preset-classic": "3.10.1",
|
||||
"@docusaurus/theme-mermaid": "^3.10.1",
|
||||
"@mdx-js/react": "^3.0.0",
|
||||
"clsx": "^2.0.0",
|
||||
"docusaurus-plugin-openapi-docs": "^5.0.2",
|
||||
|
||||
@@ -0,0 +1,224 @@
|
||||
import React, {useCallback, useEffect, useRef, useState} from 'react';
|
||||
import styles from './styles.module.css';
|
||||
|
||||
type VersionParts = {major: number; minor: number};
|
||||
type RowRef = {row: HTMLTableRowElement; version: string};
|
||||
|
||||
function sortVersions(versions: string[]): string[] {
|
||||
return [...versions].sort((a, b) => {
|
||||
const [majorA, minorA] = a.split('.').map(Number);
|
||||
const [majorB, minorB] = b.split('.').map(Number);
|
||||
if (majorA !== majorB) {
|
||||
return majorB - majorA;
|
||||
}
|
||||
return minorB - minorA;
|
||||
});
|
||||
}
|
||||
|
||||
function parseVersion(version: string): VersionParts {
|
||||
if (version === 'all') {
|
||||
return {major: 0, minor: 0};
|
||||
}
|
||||
const [major, minor] = version.split('.').map(Number);
|
||||
return {major, minor};
|
||||
}
|
||||
|
||||
function isVersionRelevant(
|
||||
version: string,
|
||||
sourceVersion: string,
|
||||
targetVersion: string,
|
||||
): boolean {
|
||||
const rowV = parseVersion(version);
|
||||
const sourceV = parseVersion(sourceVersion);
|
||||
const targetV = parseVersion(targetVersion);
|
||||
|
||||
return (
|
||||
(sourceVersion === 'all' ||
|
||||
rowV.major > sourceV.major ||
|
||||
(rowV.major === sourceV.major && rowV.minor >= sourceV.minor)) &&
|
||||
(targetVersion === 'all' ||
|
||||
rowV.major < targetV.major ||
|
||||
(rowV.major === targetV.major && rowV.minor <= targetV.minor))
|
||||
);
|
||||
}
|
||||
|
||||
function collectRows(table: HTMLTableElement): {rows: RowRef[]; versions: string[]} {
|
||||
const rows: RowRef[] = [];
|
||||
const versions: string[] = [];
|
||||
let lastVersion: string | null = null;
|
||||
|
||||
table.querySelectorAll<HTMLTableRowElement>('tbody tr').forEach((tr) => {
|
||||
const cells = tr.querySelectorAll('td');
|
||||
if (cells.length > 1) {
|
||||
const versionText = cells[0].textContent?.trim() ?? '';
|
||||
const versionMatch = versionText.match(/^v(\d+\.\d+)$/);
|
||||
if (versionMatch) {
|
||||
const version = versionMatch[1];
|
||||
if (!versions.includes(version)) {
|
||||
versions.push(version);
|
||||
}
|
||||
lastVersion = version;
|
||||
rows.push({row: tr, version});
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
if (lastVersion && cells.length >= 1) {
|
||||
rows.push({row: tr, version: lastVersion});
|
||||
}
|
||||
});
|
||||
|
||||
rows.forEach(({row, version}) => {
|
||||
row.dataset.version = version;
|
||||
});
|
||||
|
||||
return {rows, versions: sortVersions(versions)};
|
||||
}
|
||||
|
||||
function findUpgradeNotesTable(): HTMLTableElement | null {
|
||||
return document.querySelector<HTMLTableElement>('.upgrade-notes-table');
|
||||
}
|
||||
|
||||
export default function UpgradeNotesFilter() {
|
||||
const rowsRef = useRef<RowRef[]>([]);
|
||||
const [versions, setVersions] = useState<string[]>([]);
|
||||
const [sourceVersion, setSourceVersion] = useState('all');
|
||||
const [targetVersion, setTargetVersion] = useState('all');
|
||||
const [error, setError] = useState<string | null>(null);
|
||||
const [ready, setReady] = useState(false);
|
||||
|
||||
const initializeRows = useCallback(() => {
|
||||
const table = findUpgradeNotesTable();
|
||||
if (!table || table.querySelectorAll('tbody tr').length === 0) {
|
||||
return false;
|
||||
}
|
||||
|
||||
const {rows, versions: foundVersions} = collectRows(table);
|
||||
if (rows.length === 0 || foundVersions.length === 0) {
|
||||
return false;
|
||||
}
|
||||
|
||||
rowsRef.current = rows;
|
||||
setVersions(foundVersions);
|
||||
setReady(true);
|
||||
return true;
|
||||
}, []);
|
||||
|
||||
useEffect(() => {
|
||||
if (initializeRows()) {
|
||||
return undefined;
|
||||
}
|
||||
|
||||
const timer = window.setInterval(() => {
|
||||
if (initializeRows()) {
|
||||
window.clearInterval(timer);
|
||||
}
|
||||
}, 100);
|
||||
|
||||
return () => window.clearInterval(timer);
|
||||
}, [initializeRows]);
|
||||
|
||||
const showAllRows = () => {
|
||||
rowsRef.current.forEach(({row}) => row.classList.remove('filtered'));
|
||||
};
|
||||
|
||||
const applyFilter = () => {
|
||||
setError(null);
|
||||
|
||||
if (!ready || rowsRef.current.length === 0) {
|
||||
if (!initializeRows()) {
|
||||
setError('Error: Upgrade notes table is still loading. Try again in a moment.');
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
if (sourceVersion === 'all' && targetVersion === 'all') {
|
||||
showAllRows();
|
||||
return;
|
||||
}
|
||||
|
||||
const sourceV = parseVersion(sourceVersion);
|
||||
const targetV = parseVersion(targetVersion);
|
||||
|
||||
if (
|
||||
targetV.major < sourceV.major ||
|
||||
(targetV.major === sourceV.major && targetV.minor < sourceV.minor)
|
||||
) {
|
||||
setError('Error: Target version must be greater than or equal to source version.');
|
||||
return;
|
||||
}
|
||||
|
||||
const versionVisibility = new Map<string, boolean>();
|
||||
versions.forEach((version) => {
|
||||
versionVisibility.set(
|
||||
version,
|
||||
isVersionRelevant(version, sourceVersion, targetVersion),
|
||||
);
|
||||
});
|
||||
|
||||
rowsRef.current.forEach(({row, version}) => {
|
||||
row.classList.toggle('filtered', !versionVisibility.get(version));
|
||||
});
|
||||
};
|
||||
|
||||
const resetFilter = () => {
|
||||
setSourceVersion('all');
|
||||
setTargetVersion('all');
|
||||
setError(null);
|
||||
showAllRows();
|
||||
};
|
||||
|
||||
return (
|
||||
<div className={styles.filters}>
|
||||
<h3 className={styles.title}>Filter Upgrade Notes</h3>
|
||||
<p className={styles.description}>
|
||||
Select source and target versions to see only relevant upgrade notes
|
||||
</p>
|
||||
<div className={styles.controls}>
|
||||
<label className={styles.field} htmlFor="source-version">
|
||||
From version:
|
||||
<select
|
||||
id="source-version"
|
||||
value={sourceVersion}
|
||||
onChange={(event) => setSourceVersion(event.target.value)}
|
||||
disabled={!ready}>
|
||||
<option value="all">All versions</option>
|
||||
{versions.map((version) => (
|
||||
<option key={`source-${version}`} value={version}>
|
||||
v{version}
|
||||
</option>
|
||||
))}
|
||||
</select>
|
||||
</label>
|
||||
|
||||
<label className={styles.field} htmlFor="target-version">
|
||||
To version:
|
||||
<select
|
||||
id="target-version"
|
||||
value={targetVersion}
|
||||
onChange={(event) => setTargetVersion(event.target.value)}
|
||||
disabled={!ready}>
|
||||
<option value="all">All versions</option>
|
||||
{versions.map((version) => (
|
||||
<option key={`target-${version}`} value={version}>
|
||||
v{version}
|
||||
</option>
|
||||
))}
|
||||
</select>
|
||||
</label>
|
||||
|
||||
<button
|
||||
type="button"
|
||||
className={styles.applyButton}
|
||||
onClick={applyFilter}
|
||||
disabled={!ready}>
|
||||
Apply Filter
|
||||
</button>
|
||||
<button type="button" className={styles.resetButton} onClick={resetFilter}>
|
||||
Reset
|
||||
</button>
|
||||
</div>
|
||||
{error ? <div className={styles.error}>{error}</div> : null}
|
||||
</div>
|
||||
);
|
||||
}
|
||||
@@ -0,0 +1,82 @@
|
||||
.filters {
|
||||
margin: 1.5rem 0;
|
||||
padding: 1rem 1.25rem;
|
||||
border: 1px solid var(--mm-border-subtle);
|
||||
border-radius: 4px;
|
||||
background: var(--mm-bg-subtle);
|
||||
}
|
||||
|
||||
.title {
|
||||
margin: 0 0 0.35rem;
|
||||
font-family: var(--mm-font-heading);
|
||||
font-size: 1.1rem;
|
||||
}
|
||||
|
||||
.description {
|
||||
margin: 0 0 1rem;
|
||||
color: var(--mm-text-secondary);
|
||||
}
|
||||
|
||||
.controls {
|
||||
display: flex;
|
||||
flex-wrap: wrap;
|
||||
gap: 0.75rem 1rem;
|
||||
align-items: end;
|
||||
}
|
||||
|
||||
.field {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
gap: 0.35rem;
|
||||
font-size: 0.9rem;
|
||||
}
|
||||
|
||||
.field select {
|
||||
min-width: 10rem;
|
||||
padding: 0.5rem 0.65rem;
|
||||
border: 1px solid var(--mm-border-subtle);
|
||||
border-radius: 4px;
|
||||
background: var(--mm-bg-surface);
|
||||
color: var(--mm-text-primary);
|
||||
}
|
||||
|
||||
.applyButton,
|
||||
.resetButton {
|
||||
padding: 0.55rem 1rem;
|
||||
border: none;
|
||||
border-radius: 4px;
|
||||
cursor: pointer;
|
||||
font-weight: 600;
|
||||
}
|
||||
|
||||
.applyButton {
|
||||
background: var(--mm-denim-400);
|
||||
color: var(--mm-color-white);
|
||||
}
|
||||
|
||||
.applyButton:hover {
|
||||
background: var(--mm-color-denim);
|
||||
}
|
||||
|
||||
.resetButton {
|
||||
background: transparent;
|
||||
color: var(--mm-text-primary);
|
||||
border: 1px solid var(--mm-border-subtle);
|
||||
}
|
||||
|
||||
.resetButton:hover {
|
||||
background: var(--mm-bg-surface);
|
||||
}
|
||||
|
||||
.error {
|
||||
margin-top: 0.75rem;
|
||||
padding: 0.65rem 0.75rem;
|
||||
border-left: 4px solid #e74c3c;
|
||||
border-radius: 4px;
|
||||
background: #fadbd8;
|
||||
color: #922b21;
|
||||
}
|
||||
|
||||
:global(.upgrade-notes-table tr.filtered) {
|
||||
display: none;
|
||||
}
|
||||
@@ -487,18 +487,93 @@ blockquote {
|
||||
text-decoration: none;
|
||||
}
|
||||
|
||||
/* Tables: scroll horizontally within themselves instead of overflowing the
|
||||
* viewport. Applied unconditionally (not just under the mobile breakpoint
|
||||
* below) since a wide table can force the same horizontal-overflow problem
|
||||
* at any viewport width — e.g. at higher browser zoom levels, where the
|
||||
* effective layout width shrinks but the media query doesn't re-trigger. */
|
||||
/* Tables: keep native table layout so header and body columns stay aligned.
|
||||
* Wide tables (e.g. upgrade notes) use a dedicated wrapper class instead. */
|
||||
.markdown table {
|
||||
display: block;
|
||||
display: table;
|
||||
width: 100%;
|
||||
table-layout: auto;
|
||||
border-collapse: collapse;
|
||||
}
|
||||
|
||||
.upgrade-notes-table-wrapper {
|
||||
width: 100%;
|
||||
max-width: 100%;
|
||||
overflow-x: auto;
|
||||
-webkit-overflow-scrolling: touch;
|
||||
}
|
||||
|
||||
.markdown table.upgrade-notes-table {
|
||||
display: table;
|
||||
width: 100%;
|
||||
min-width: 0;
|
||||
table-layout: fixed;
|
||||
}
|
||||
|
||||
.markdown table.upgrade-notes-table thead {
|
||||
display: table-header-group;
|
||||
}
|
||||
|
||||
.markdown table.upgrade-notes-table tbody {
|
||||
display: table-row-group;
|
||||
}
|
||||
|
||||
.markdown table.upgrade-notes-table thead th {
|
||||
text-transform: none;
|
||||
letter-spacing: normal;
|
||||
font-family: var(--mm-font-sans);
|
||||
font-weight: 700;
|
||||
font-size: 0.85rem;
|
||||
line-height: 1.35;
|
||||
padding: 0.5rem 0.65rem;
|
||||
}
|
||||
|
||||
.markdown table.upgrade-notes-table th,
|
||||
.markdown table.upgrade-notes-table td {
|
||||
vertical-align: top;
|
||||
padding: 0.5rem 0.65rem;
|
||||
min-width: 0;
|
||||
overflow-wrap: break-word;
|
||||
word-break: break-word;
|
||||
}
|
||||
|
||||
.markdown table.upgrade-notes-table th:first-child,
|
||||
.markdown table.upgrade-notes-table td:first-child {
|
||||
width: 25%;
|
||||
}
|
||||
|
||||
.markdown table.upgrade-notes-table th:last-child,
|
||||
.markdown table.upgrade-notes-table td:last-child {
|
||||
width: 75%;
|
||||
max-width: 0;
|
||||
}
|
||||
|
||||
.markdown table.upgrade-notes-table td:first-child:not(:last-child) {
|
||||
white-space: nowrap;
|
||||
}
|
||||
|
||||
.markdown table.upgrade-notes-table tbody tr:nth-child(even) {
|
||||
background: transparent;
|
||||
}
|
||||
|
||||
.markdown table.upgrade-notes-table tbody td {
|
||||
border-bottom: 1px solid var(--mm-border-subtle);
|
||||
}
|
||||
|
||||
.markdown table.upgrade-notes-table pre,
|
||||
.markdown table.upgrade-notes-table .sourceCode {
|
||||
max-width: 100%;
|
||||
margin: 0.5rem 0 0;
|
||||
overflow-x: auto;
|
||||
}
|
||||
|
||||
.markdown table.upgrade-notes-table pre,
|
||||
.markdown table.upgrade-notes-table pre code,
|
||||
.markdown table.upgrade-notes-table code {
|
||||
white-space: pre-wrap;
|
||||
word-break: break-word;
|
||||
}
|
||||
|
||||
/* === Mobile / responsive === */
|
||||
|
||||
@media (max-width: 996px) {
|
||||
@@ -551,6 +626,93 @@ blockquote {
|
||||
margin: 2.5rem 0 1.5rem;
|
||||
}
|
||||
|
||||
/* === Release policy Gantt chart (scoped — no global mermaid overrides) === */
|
||||
|
||||
.release-policy-gantt {
|
||||
margin: 1.25rem 0;
|
||||
/* On narrow viewports, scroll horizontally instead of shrinking the
|
||||
* chart below a legible size. */
|
||||
overflow-x: auto;
|
||||
}
|
||||
|
||||
.release-policy-gantt .docusaurus-mermaid-container {
|
||||
margin: 0;
|
||||
background: var(--mm-color-white);
|
||||
border: 2px solid #0745a1;
|
||||
border-radius: 4px;
|
||||
padding: 0.35rem 0.5rem 0.25rem;
|
||||
}
|
||||
|
||||
[data-theme='dark'] .release-policy-gantt .docusaurus-mermaid-container {
|
||||
background: #1e1e1e;
|
||||
border-color: #555555;
|
||||
}
|
||||
|
||||
/* Hide mermaid's semi-transparent gantt row backgrounds (pink tint in light mode) */
|
||||
.release-policy-gantt .docusaurus-mermaid-container svg rect.section,
|
||||
.release-policy-gantt .docusaurus-mermaid-container svg rect.grid-panel {
|
||||
opacity: 0 !important;
|
||||
}
|
||||
|
||||
.release-policy-gantt .docusaurus-mermaid-container svg {
|
||||
background: transparent;
|
||||
}
|
||||
|
||||
.release-policy-gantt .docusaurus-mermaid-container svg line {
|
||||
stroke: #0745a1;
|
||||
}
|
||||
|
||||
.release-policy-gantt .docusaurus-mermaid-container svg rect.task {
|
||||
fill: #0984e3 !important;
|
||||
stroke: #2d3436 !important;
|
||||
stroke-width: 2px !important;
|
||||
}
|
||||
|
||||
.release-policy-gantt .docusaurus-mermaid-container svg rect[class*='crit'] {
|
||||
fill: #d63031 !important;
|
||||
stroke: #2d3436 !important;
|
||||
stroke-width: 2px !important;
|
||||
}
|
||||
|
||||
.release-policy-gantt .docusaurus-mermaid-container .taskText {
|
||||
fill: #ffffff !important;
|
||||
font-weight: 700 !important;
|
||||
font-size: 22px !important;
|
||||
}
|
||||
|
||||
.release-policy-gantt .docusaurus-mermaid-container .taskTextOutsideLeft,
|
||||
.release-policy-gantt .docusaurus-mermaid-container .taskTextOutsideRight {
|
||||
fill: #2d3436 !important;
|
||||
font-weight: 700 !important;
|
||||
font-size: 22px !important;
|
||||
}
|
||||
|
||||
.release-policy-gantt .docusaurus-mermaid-container .sectionTitle,
|
||||
.release-policy-gantt .docusaurus-mermaid-container text[class*='tick'],
|
||||
.release-policy-gantt .docusaurus-mermaid-container text[class*='label'] {
|
||||
fill: #2d3436 !important;
|
||||
font-weight: 600 !important;
|
||||
font-size: 22px !important;
|
||||
}
|
||||
|
||||
[data-theme='dark'] .release-policy-gantt .docusaurus-mermaid-container svg rect.task {
|
||||
stroke: #ffffff !important;
|
||||
}
|
||||
|
||||
[data-theme='dark'] .release-policy-gantt .docusaurus-mermaid-container svg rect[class*='crit'] {
|
||||
stroke: #ffffff !important;
|
||||
}
|
||||
|
||||
[data-theme='dark'] .release-policy-gantt .docusaurus-mermaid-container text {
|
||||
fill: #ffffff !important;
|
||||
}
|
||||
|
||||
.release-policy-gantt .docusaurus-mermaid-container > svg {
|
||||
display: block;
|
||||
width: 100%;
|
||||
height: auto;
|
||||
}
|
||||
|
||||
/* (Marigold strip is now the navbar's own border-bottom.) */
|
||||
|
||||
/* === Pages with a Hero hide breadcrumbs + prev/next pagination ===
|
||||
|
||||
@@ -17,6 +17,7 @@ import Eyebrow from '@site/src/components/Eyebrow';
|
||||
import StatStrip from '@site/src/components/StatStrip';
|
||||
import MethodLegend from '@site/src/components/MethodLegend';
|
||||
import CardGrid from '@site/src/components/CardGrid';
|
||||
import UpgradeNotesFilter from '@site/src/components/UpgradeNotesFilter';
|
||||
// Globally available so migrated developer docs (Hugo `tabs` shortcode)
|
||||
// can use them without imports.
|
||||
import Tabs from '@theme/Tabs';
|
||||
@@ -43,6 +44,7 @@ export default {
|
||||
StatStrip,
|
||||
MethodLegend,
|
||||
CardGrid,
|
||||
UpgradeNotesFilter,
|
||||
Tabs,
|
||||
TabItem,
|
||||
};
|
||||
|
||||
Reference in New Issue
Block a user