Commit Graph
16 Commits
Author SHA1 Message Date
Scott Bishel a8f8a6b523 Documentation updates for some missed areas. (#39511) 2026-10-30 07:29:44 -04:00
mattermost-docs-ai[bot]andmattermost-docs-ai[bot] 1ac3eb8c28 docs: AI draft for MM-71344 Plugin API: expose cluster node identity (#39401)
Co-authored-by: mattermost-docs-ai[bot] <41898282+github-actions[bot]@users.noreply.github.com>
2026-10-26 07:55:39 +01:00
79e7448565 Lay dialog elements out as a grid, and let a dialog pick its width (#39082)
* Lay dialog elements out as a grid, and let a dialog pick its width

An Interactive Dialog could already group elements under a heading with a
collapsible container, but it could only stack them vertically. A form
asking for the same handful of fields across several records — a status
per service, a quantity per line item — therefore read as one labelled
block per record, repeating every label, with nothing lining up.

Two changes, both additive.

A grid layout mode
------------------
Setting SubType "grid" on a collapsible lays its children out as a table
rather than stacking them: each child container becomes a row, and that
row's children become its cells. This is a layout over ordinary elements,
not a new element type, which is what keeps it cheap:

  - a cell is a real element, so Type, SubType, Default, Optional and
    MaxLength all work per cell with no new plumbing
  - a cell's value arrives in the submission as an ordinary top-level
    field, because collapsible children already flatten that way
  - an error returned for a cell lands under that one cell, because the
    Errors map is already keyed by element name

So neither the submission model nor the error model changes. The webapp
carries the subtype into CollapsibleConfig.layout, and AppsFormGrid
renders the subtree.

The layout rules worth knowing: column headers come from the first row, a
short row is padded on the right rather than shifted, the first element
of each row is a sticky, still-editable row header, and each cell's label
is hidden visually but kept for assistive technology. A child of the
container that is not itself a container cannot be a row, so it renders
under the table rather than being dropped.

A width tier on the dialog
--------------------------
A grid rarely fits in 600px, which is what every dialog has been. Nothing
ever enforced that width: the apps form renders a react-bootstrap Modal
with no size prop, so it inherited Bootstrap's default, and
dialogClassName was hardcoded to classes that set no width at all.

Dialog.Size takes small, medium or large. Empty means small, so no
existing dialog changes — and an unset size adds no class at all rather
than an explicit small one, so the rendered markup is also unchanged.
Tiers rather than pixels keep it themeable and stop an integration asking
for 2000px; .modal-dialog's max-width still applies, so every tier
shrinks on a narrow viewport instead of overflowing. medium reuses the
existing modal-lg rather than widening it, because Mattermost
deliberately narrows Bootstrap's 900px modal-lg to 700px and other
consumers depend on that.

Width sits on the dialog rather than in server config because the
integration is the party that knows how much content the form holds; one
global width cannot suit both a two-field confirmation and a nine-column
grid. Because a refresh response replaces the contents of the open
dialog, returning a different size resizes it in place.

Tests and docs
--------------
Unit tests cover the grid's layout rules, the subtype-to-layout mapping
(including an unrecognised subtype falling back to stacked), size
surviving each hop from the server response to the modal class, and
Dialog.Size validation including that size is omitted from the JSON when
unset.

The Cypress spec drives the webhook server, which gains a grid dialog in
its own module. It covers the table structure, the editable sticky row
header, adding a row in place with typed values preserved, an error
landing on the one offending cell, the width tier resizing the open modal
without losing input, and a button cell opening a child dialog for its
row.

Collapsible elements were never documented, and the grid is a layout mode
on that container, so both are documented here, along with dialog width.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>

* Keep the grid spec's weight cell on whole numbers

MM-T5700D typed 12.5 into the weight cell and asserted it survived the
refresh. It did not: the assertion got 12, and the spec was the one
failing unit in cypress-full-enterprise.

The grid is not at fault. The weight cell is subtype "number", which
renders an <input type="number">, and TextSetting reports those through
parseInt rather than parseFloat:

    props.onChange(props.id, parseInt(event.target.value, 10));

so 12.5 becomes 12 before it reaches the submission at all. The webhook
server folds values back with String(...).trim() and coerces nothing, so
the fraction was already gone by the time the state round tripped. That
parseInt predates this branch by two years and lives in a file this
branch does not touch, so fixing it belongs in its own change: it would
alter the value type every number setting in the app emits, the system
console among them.

The test's subject is that half-finished input survives a refresh, and a
whole number demonstrates that just as well. The weight placeholder drops
from "0.0" to "0" for the same reason, since advertising decimals the
field discards is worse than not offering them.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>

* Stop the grid dropping cells, and tighten two loose edges

Three fixes from a review pass over the grid, none of which change how a
well-formed grid renders.

A row longer than the first kept its cells
------------------------------------------
Column count came from the first row, and the body iterated those columns,
so a cell beyond that width was never rendered. That was deliberate and
tested, but the consequence was not: flattenFields recurses into every
collapsible child regardless of layout, and the submit check runs over all
of them, so a required cell in an over-long row was validated while having
no input on screen. The result was a dialog that would not submit and could
not say why, because the error had nowhere to render.

The table is now as wide as the widest row. A heading for a widened column
comes from the first row that has a cell in that position, and a short row
is still padded on the right rather than shifted. For a grid whose rows all
match, which is what the docs ask for, this is exactly the old behaviour.

Keying the headers on the cell name rather than the label also stops two
columns that share a label from colliding, and drops the two array-index
key warnings the file used to produce.

The width transition applied to nearly every modal
--------------------------------------------------
The resize easing was scoped to .modal-dialog.a11y__modal, a class carried
by about a hundred modals across the app, so all of them picked up a width
transition from a form they have nothing to do with. The dialog now carries
an apps-form-modal class and the transition hangs off that instead.

An unknown collapsible subtype passed the server
------------------------------------------------
Dialog.Size rejects anything outside its three tiers, but a collapsible's
SubType was not checked at all, so "gird" validated cleanly, reached the
client and silently rendered as stacked. validateCollapsible now rejects
any subtype other than "grid". It lives there rather than in isValid's type
switch because nesting recurses into validateCollapsible directly to keep
depth tracking and never passes back through that switch, so a nested
container would otherwise be missed.

This is safe to tighten: collapsible appears only in v12.0.0-rc1 and rc2
and has never been in a GA release, so no shipped integration can be
relying on a subtype that was previously ignored.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>

* Give the dialog width section a sequential heading level

The version note under "Dialog width" was an h5 directly beneath an h2,
a three-level jump that trips markdownlint's heading-increment rule and
gives the page a heading outline with two levels missing from it.

It is now an h3, which is the correct next level.

The file has four other heading jumps of the same shape, all under the
11.1 sections and all predating this branch, so they are left alone.
That does mean this section's version note now renders one size larger
than the equivalent note in the neighbouring "Multi-step dialogs" and
"Dynamic field refresh" sections, which are still h5 under an h2.
Making the whole file consistent is a docs change in its own right
rather than something to smuggle into a feature branch.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>

* Address review: keep the slide, keep the section, say what the server does

Every apps form lost its slide-in animation. Bootstrap's `transition:
transform 0.3s ease-out` sits on `.modal.fade .modal-dialog`, exactly as
specific as the new `.modal .modal-dialog.apps-form-modal` rule and earlier
in the cascade, so naming only `width` replaced it rather than adding to it
— and every apps form carries the class, not just a grid. Both transitions
are listed now.

The three width tiers were the same width between 768px and 991px, because
`medium` borrowed `.modal-lg`, whose 700px Mattermost sets app-wide only
above 992px. Both wide tiers get a class of their own and start at
Bootstrap's own 768px breakpoint, so a dialog's width no longer moves when
an unrelated rule does, and the tiers differ wherever a dialog is not
already fluid.

A grid container was rendering its own subtree, which dropped the three
things a collapsible says about itself: `collapsed`, `borderless` and
`display_name` were all ignored, including in the docs' own grid example.
The grid now lays out inside the same CollapsibleSection the stacked layout
uses, so only the arrangement of the children differs.

Accessibility and ragged input, in the grid itself:
- the table takes the container's name through a clipped `<caption>`, and
  each row keeps its `display_name` as the row's accessible name
- a required cell's marker was only in the clipped per-cell label, so the
  cell looked optional until submission failed; the column heading carries
  it too
- rows that are all empty fell through to an empty table instead of the
  fallback
- two cells sharing a name in one row collided on their React key and cost
  the row a cell; the keys carry the column index
- the sticky first column could cover the cell that just took focus while
  tabbing sideways (`scroll-padding-inline-start`)

The docs and a Go comment said an unrecognised `size` or collapsible
subtype was rejected when the dialog opens. It is not:
App.OpenInteractiveDialog logs what IsValid returns and opens the dialog
anyway, and the client falls back to the default width and to a stacked
layout. Both now say that, and tests pin it on the open-dialog path rather
than only on Dialog and DialogElement in isolation. `12.0` in the two new
sections is `12.1`: v12.0.0 is tagged and has neither feature.

Cells are matched to columns by position, so a row that omits an element in
the middle shifts the rest left under the wrong heading. The docs claimed a
mismatch always showed as a gap; they now say what actually happens and
what to do about it.

Also: the grid spec measured nothing it claimed to — `overflow-x: auto` is
set unconditionally, and the width check only read a class name. It now
measures the modal at each tier, checks the sticky column by scrolling the
table, checks the clip on the per-cell labels rather than `not.be.visible`
(which passes for a clipped element either way), waits on
`cy.intercept`/`cy.wait` like action_button_spec rather than on fixed
delays, and covers a window too narrow for the tier. The MM-T5700A–H IDs
were invented and are gone; 942 specs in the repo carry no TestRail ID.

Jest gains the gaps: required cells and per-cell errors, a collapsed grid,
a grid nested in a grid through AppsForm, unique keys, an unknown size, and
the router and adapter hops that carry `size`.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>

* Tell the truth about a required cell and about what a refresh keeps

Two from CodeRabbit, both real.

A column heading carried the required marker of whichever row happened to
supply the heading. If the first row's cell was optional and a later row's was
not, the heading stayed unmarked and the later cell looked optional, because
its own label is clipped. A heading speaks for the whole column, so it is now
marked only when every cell in that column is required; when rows disagree the
marker moves to the cells that actually carry it, hidden from assistive
technology since each cell's clipped label already says so.

The dialog width section promised that "values the user has already entered are
preserved as usual" across a refresh. getDerivedStateFromProps merges the
replacement form's initial values over the current ones, so a field that comes
back without its value echoed as `default` is cleared. The grid section already
said to echo them; the width section now says so too.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>

* Point the sticky test at a window the grid actually overflows

Both of the spec's scroll assertions were false, which is how a real assertion
differs from the `overflow-x: auto` check it replaced: that one passed because
the stylesheet sets it unconditionally.

The sample grid is four columns and squeezes to about 500px. Every width tier
on a desktop window leaves more room than that -- 510px inside the 600px tier,
610 inside 700, 810 inside 900 -- so the table always fits and never scrolls.
The sticky test now narrows the window below that floor, where there is
genuinely something for the row header to stay put against, and the width test
no longer claims the grid fails to fit at the narrow tier, because it does fit.

Verified against a local server built from this branch: 9 passing.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>

* Say what a refresh actually does to an unechoed field

Not every field type is cleared: initFormValues falls back to `field.value ||
defaultValue`, which is empty for an ordinary field but `false` for a `bool`
and the current time for a required `datetime`. "Will clear" overstated it.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>

* Drop the blank line left behind by the removed assertion

Taking the overflow assertion out of the width test left its block padded,
which @stylistic/padded-blocks rejects.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>

* Re-run CI

Empty commit. playwright-full failed on b437b625 with four tests that each
passed and failed against this same code in the same run, so this is to see
them run again rather than to change anything.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>

* Give the apps form dialog its own width class

The width tiers put apps-form-modal on the modal's dialogClassName, but
that class is already on the outer .modal, so every open dialog matched
.apps-form-modal twice and the stacked-dialog E2E specs counted double.
Hang the width styles off apps-form-modal-dialog instead.

The grid button-cell spec also counted #appsModal and looked up the
parent's #appsModalLabel, neither of which a hidden parent dialog has.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

---------

Co-authored-by: Jack Benning <jack.benning@mattermost.com>
Co-authored-by: Claude Opus 5 <noreply@anthropic.com>
Co-authored-by: Jack Benning <jack@Jacks-MacBook-Pro.local>
2026-10-21 11:18:48 -04:00
Combs7th d0d6f36df1 Docs: Increase interactive dialog title max length to 80 (#39278) 2026-10-20 20:41:11 -07:00
Scott Bishel e7e0ee99d2 Docs: interactive dialog checkbox elements and per-channel join/leave messages (#39260)
* update documentation for checkbox group/matrix and join/leave channel settings

* update docs from coderabbit review
2026-10-19 11:43:43 -07:00
Eva SarafianouandCursor bf379803cf docs: replace animated GIF docs with H.264 Video (#39009)
* docs: encode animated GIFs to H.264 and drop unused duplicates

Add a reproducible ffmpeg encode pipeline and commit 64 MP4s next to their GIF sources. Delete the unreferenced poll.gif pair and the two orphan interactive_message.gif copies. Page rewrites wait on the Video MDX component.

Co-authored-by: Cursor <cursoragent@cursor.com>

* docs: stop tracking encode-gif-video bytecode

Co-authored-by: Cursor <cursoragent@cursor.com>

* docs: re-encode GIF videos when the manifest CRF changes

Skip was ignoring CRF, so a quality-pass patch was a no-op without --force. Record the CRF that produced each MP4 and treat a mismatch as stale.

Co-authored-by: Cursor <cursoragent@cursor.com>

* docs: address CodeRabbit findings on gif encode verify/review

Verify committed MP4 digests and accept --only; crop GIF refs for
review SSIM; guard the encode idempotency test before writing.

Co-authored-by: Cursor <cursoragent@cursor.com>

* docs: drop the one-shot GIF encode pipeline from the repo

The MP4s are the committed product. We do not expect more GIFs, so the
encoder, manifest, and tests are throwaway rather than a maintained
pipeline.

Co-authored-by: Cursor <cursoragent@cursor.com>

* docs: add Video MDX component and postbuild markup check

Silent H.264 screen captures replace animated GIFs; no autoplay or poster.
Authored form wraps src in useBaseUrl so check:base-url-attrs stays clean.

Co-authored-by: Cursor <cursoragent@cursor.com>

* docs: replace animated GIF references with Video and drop source GIFs

Rewrite 63 media refs to <Video> with carried-over alt text and ffprobe
aspect ratios; delete the 62 converted GIFs under static/images.

Co-authored-by: Cursor <cursoragent@cursor.com>

* docs: parse unquoted attributes in check-video-markup

Docusaurus emits unquoted HTML attrs when values have no spaces; the
postbuild guard must accept that form or every video fails the scan.

Co-authored-by: Cursor <cursoragent@cursor.com>

* docs: fix Video reduced-motion SSR loop and stray alt prefix

Default loop to false until hydration so reduced-motion users never
get a looping attribute before JS runs; drop the accidental "alt:"
prefix from customize-your-channel-sidebar Video alt text.

Co-authored-by: Cursor <cursoragent@cursor.com>

* Trim leading white frames from message-formatting-toolbar video

The clip opened on a blank frame, so the player showed white until
playback started. Cut from the first content frame instead of adding
posters.

Co-authored-by: Cursor <cursoragent@cursor.com>

* Cut message-formatting-toolbar after fade-in completes

0.15s still landed on a washed-out frame; start at 0.3s where the UI
is full contrast for the player preview.

Co-authored-by: Cursor <cursoragent@cursor.com>

---------

Co-authored-by: Cursor <cursoragent@cursor.com>
2026-10-13 21:53:37 +03:00
Eva SarafianouandCursor c57df45d9c docs: self-host fonts, images, and OpenAPI icons; block build egress (#39092)
* docs: self-host fonts, images, and OpenAPI icons; block build egress

Stop hotlinking GitHub/user-images and Google Fonts on the docs site, replace
embedded video iframes with links, and fail CI when a build would fetch
off-origin resources (closed allowlist for the security-updates feed).

Co-authored-by: Cursor <cursoragent@cursor.com>

* docs: address CodeRabbit egress review findings

Scan an offline (no-Algolia) rebuild in Docs CI, tighten ATTR/getAttr
boundaries, flag remote template-literal prefixes before ${, and add
alt text for the self-hosted plugin-management image.

Co-authored-by: Cursor <cursoragent@cursor.com>

* docs: replace egress fixture tree with in-memory unit tests

Keep scanner coverage without committing dozens of tiny HTML/CSS/JS
fixture files. scanSources takes string maps; CLI smoke uses a temp dir.

Co-authored-by: Cursor <cursoragent@cursor.com>

* docs: keep YouTube/Wistia embeds on hosted site

Restore in-page players for the public docs. Allowlist those hosts in
check-egress; the offline packager strips them for airgap.

Co-authored-by: Cursor <cursoragent@cursor.com>

* docs: simplify hosted-embed allowlist to base domains

Co-authored-by: Cursor <cursoragent@cursor.com>

* docs: tighten hosted-embed allowlist to player kinds and paths

Only iframe/script/img player URLs pass; fetch() and other loads to
YouTube/Wistia hosts still fail egress.

Co-authored-by: Cursor <cursoragent@cursor.com>

* docs: allow Wistia swatch preload in hosted-embed egress check

The organize-conversations embed emits link[rel=preload] for the
swatch; kind-scoped allowlisting missed it and failed Docs CI.

Co-authored-by: Cursor <cursoragent@cursor.com>

* docs: use getAttr boundary for script hasSrc check

\b matches inside data-src, so the old hasSrc could skip scanning an
inline script body. Align with ATTR/getAttr and cover the case in tests.

Co-authored-by: Cursor <cursoragent@cursor.com>

---------

Co-authored-by: Cursor <cursoragent@cursor.com>
2026-10-13 17:58:54 +03:00
Eva SarafianouandCursor 9e386cb993 docs: resolve raw HTML link and image paths through useBaseUrl (#38958)
* docs: resolve raw HTML link and image paths through useBaseUrl

Docusaurus prepends `baseUrl` to markdown-syntax links (`[text](/path)`) but
never to site-absolute paths written in raw HTML/JSX attributes. Those values
ship verbatim, which is correct only while `baseUrl` is `/`.

Convert the 221 raw `<a href="/...">` anchors across 22 files under docs/main
and the 3 raw `<img src="/img/...">` tags in docs/develop to
`useBaseUrl('/...')`, matching how images in the same table rows already
resolve their paths. Link targets, link text and table structure are unchanged.

Add scripts/check-base-url-attrs.mjs and run it as the first step of
`prebuild` so the build fails fast on any new occurrence.

* docs(site): match CommonMark fence closing in baseUrl attr check

Toggle on any fence-like line could exit a block early; track the
opening marker and only close with the same character of sufficient length.

Co-authored-by: Cursor <cursoragent@cursor.com>

* docs(site): also flag JSX string-literal href/src in baseUrl check

Quoted attributes alone miss href={"/path"} / src={'/path'}, which
bypass baseUrl the same way. Scan both forms through one validation path.

Co-authored-by: Cursor <cursoragent@cursor.com>

---------

Co-authored-by: Cursor <cursoragent@cursor.com>
2026-10-04 11:01:13 +02:00
e97df5b6e1 [MM-70687] Expose host JSX runtimes to web app plugins (#38489)
* chore(webapp): upgrade to React 19

Bump react, react-dom and react-is to 19.2.8 across the webapp
workspaces along with their @types packages, and upgrade the React
ecosystem packages that needed newer releases to support React 19:

- @mui/material, @mui/base and @mui/styled-engine-sc
- @floating-ui/react
- react-day-picker
- @types/react-transition-group, @types/styled-components

Replace the unmaintained react-beautiful-dnd with its actively
maintained fork @hello-pangea/dnd, which is API compatible, ships its
own types and supports React 19. This drops both the
@types/react-beautiful-dnd dependency and the local patch that forced
react-redux 9 compatibility.

React 19 removes the legacy ReactDOM.render entry point, so the app now
always mounts through createRoot and the webapp side of the
EnableConcurrentReact feature flag is gone.

Apply the types-react-codemod scoped-jsx and
react-element-default-any-props presets to account for React 19 removing
the global JSX namespace and defaulting ReactElement props to unknown,
plus the manual type fixes those changes surfaced.

Co-authored-by: Jesse Hallam <lieut-data@users.noreply.github.com>

* chore(webapp): pin react at the workspace root so a single copy resolves

With react bumped to 19 only inside the workspaces, npm kept an older
react at the hoisted root to satisfy the peer requirements of packages
such as react-intl, leaving the app with two React copies. Declare react,
react-dom and their types at the webapp root the same way react-intl is
already declared, which is the existing pattern for singleton packages
shared across workspaces, and regenerate the lockfile.

Co-authored-by: Jesse Hallam <lieut-data@users.noreply.github.com>

* fix(webapp): read a tooltip child's ref from its props

React 19 moved ref into props and made element.ref throw a deprecation error on
access. Read children.props.ref instead, and apply the merged ref after spreading
the child's props so the child's own ref no longer overwrites it.

Co-authored-by: Jesse Hallam <lieut-data@users.noreply.github.com>

* chore(webapp): resolve a single react-is that matches React 19

react-is 18 does not recognize React 19's react.transitional.element symbol,
so every consumer that inspects elements through it - pretty-format's snapshot
serializer and styled-components among them - silently failed its checks.

Co-authored-by: Jesse Hallam <lieut-data@users.noreply.github.com>

* test(webapp): expect no legacy context argument on component calls

React 19 drops legacy context, so it now hands function components undefined
as the second argument instead of an empty context object.

Co-authored-by: Jesse Hallam <lieut-data@users.noreply.github.com>

* test(webapp): re-record snapshots for React 19 and dependency updates

The recorded output changes because React 19 keeps ref in props and omits an
empty value attribute, @hello-pangea/dnd uses data-rfd-* in place of
data-rbd-*, styled-components and MUI generate different class hashes, and the
image asset mock no longer resolves to an empty string.

Co-authored-by: Jesse Hallam <lieut-data@users.noreply.github.com>

* fix(webapp): keep createRoot available to plugins on window.ReactDOM

React 19 serves createRoot and hydrateRoot only from react-dom/client. Plugins
are built against React 18 and reach them through react-dom's root entry, which
react-dom 18's client.js re-exports from, so every plugin that mounts its own
root got undefined instead of a function. Because a plugin's initialize call is
not guarded, the throw took the whole web app down with it: with plugins enabled
nothing rendered at all, which failed essentially the entire e2e suite.

Exposing react-dom and react-dom/client as one object keeps both surfaces
available to plugins built against either version.

Co-authored-by: Jesse Hallam <lieut-data@users.noreply.github.com>

* test(e2e): wait for lazy views to mount instead of racing them

React 19 throttles revealing a Suspense boundary's content for ~300ms once a
fallback has been shown, so the first navigation to a lazily-loaded chunk
commits later than it did on React 18. Two specs sampled state within that
window:

- last_viewed_spec asserted on the team's last viewed type immediately after
  clicking Threads, but that is recorded when GlobalThreads mounts. Wait for
  the view instead, which is the condition the assertion depends on.
- channel_name_tooltips_spec reached the direct message search box via
  cy.focused(), which resolved to the button that opened the modal before
  focus moved into it. Select the input directly, as every other spec does.

Both specs pass on React 18 and React 19 with these changes.

Co-authored-by: Jesse Hallam <lieut-data@users.noreply.github.com>

* fix(threads): guard clear() so it cannot undo a later navigation

useThreadRouting's clear() unconditionally replaced the URL with the threads
route of the team captured when the callback was created. GlobalThreads calls it
from an effect that fires once its threads finish loading, so a navigation that
landed between the render and that effect flush was silently undone.

React 19 makes this reachable in practice: its Suspense fallback-reveal throttle
delays the lazily loaded threads view by ~300ms, widening the window enough that
switching teams right after opening Threads bounced back to the previous team.

The test file moves to .tsx so it can render the hook inside a route match.

Co-authored-by: Jesse Hallam <lieut-data@users.noreply.github.com>

* fix(webapp): preserve live history and React 18 plugin compatibility

* DO NOT MERGE: run React 19 StrictMode E2E discovery

Port Jesse Hallam's discovery tooling from #38235 onto the React 19
upgrade, adapting the development runtime loader and warning detector.
Force StrictMode and E2E exception detection on for this experiment.
Keep NODE_ENV=production outside React and preserve component names.

Both E2E self-tests require instrumentation in discovery mode and use a
React 19 warning. Local Playwright and Cypress self-tests, 13 diagnostic
unit tests, the production build, webapp style/types, and Playwright
checks pass. Cypress lint passes; its typecheck reports errors in
unchanged bot-account and collapsed-thread specs.

Revert this entire commit after collecting the full CI/E2E results.

* Revert "DO NOT MERGE: run React 19 StrictMode E2E discovery"

This reverts commit 564757d380.

* fix(webapp): cover upstream React 19 types and stabilize ranked policy tests

* test(webapp): increase convert GM modal test timeout

* MM-70612 Log plugin React DOM root calls

* MM-70612 Use real script elements in plugin lifecycle tests

* guard against plugin failures (sync and async); don't hang webapp

* MM-70678 Sample plugin startup failure reports at 5%

* export jsx-runtime; tests; docs

* MM-70687 Simplify JSX runtime docs to require Mattermost 12.0

* fix(webapp): deactivate superseded plugin scripts

---------

Co-authored-by: Cursor Agent <cursoragent@cursor.com>
Co-authored-by: Jesse Hallam <lieut-data@users.noreply.github.com>
2026-09-14 16:23:30 +00:00
Christopher PoileandMattermost Build afc8def801 [MM-70573] Propagate context through plugin HTTP (#38354)
* add context-aware plugin requests; tests

* improve comments

* fix PluginHTTP stream cleanup

* close unexpected PluginHTTP test response

* synchronize plugin response writer shutdown

* linting

* address context-aware plugin HTTP review feedback

---------

Co-authored-by: Mattermost Build <build@mattermost.com>
2026-09-11 17:26:26 -04:00
Eva SarafianouandCursor b9c2bd733f docs: restore generated plugin SDK reference pages (#37788)
* docs: restore generated plugin SDK reference pages

Reimplements the Hugo-era plugingodocs/pluginjsdocs/pluginmanifestdocs
shortcode pipeline natively in Docusaurus, so the server plugin SDK,
web app plugin SDK, and manifest reference pages render full generated
content again instead of "Generated content (migrating)" placeholders.

Two new Go generators (gen-plugin-godocs, gen-plugin-manifest-docs) and
one Node generator (gen-plugin-jsdocs.mjs) read server/public/plugin,
server/public/model, and webapp/channels/src/plugins/registry.ts
directly from this monorepo and emit gitignored JSON consumed by new
PluginGoDocs/PluginGoExample/PluginJsDocs/PluginManifestDocs React
components, wired into prestart/prebuild alongside the existing
sidebar/OpenAPI generators.

Co-authored-by: Cursor <cursoragent@cursor.com>

* docs: remove migration plan doc from this PR

Was a handoff/status doc for the implementing agent, not meant to ship
as part of the change itself.

Co-authored-by: Cursor <cursoragent@cursor.com>

* docs: make plugin-godocs/manifest-docs builds atomic

build:plugin-godocs and build:plugin-manifest-docs redirected straight
into data/plugin-*.json, which doesn't exist on a clean checkout (the
redirection itself would fail before the generator ever ran) and, on a
subsequent failed run, would truncate a previously-good JSON file
before failing. Create data/ up front, write to a .tmp file, and only
mv it into place once the generator exits successfully.

Co-authored-by: Cursor <cursoragent@cursor.com>

* docs: fix review findings in plugin doc generators

gen-plugin-manifest-docs:
- jsonFieldName now excludes unexported fields (matching encoding/json)
  and falls back to the Go field name when there's no tag or the tag's
  name component is empty (e.g. json:",omitempty"), instead of
  dropping the field entirely.
- exprTypeDocs's SelectorExpr case now resolves a qualified type only
  when its package qualifier actually points at the model package
  (checked against the declaring file's imports), instead of matching
  any x.Sel identifier by name alone regardless of which package it
  qualifies.

gen-plugin-godocs:
- The example-code loop now falls back to example.Code when
  example.Play is nil (go/doc leaves Play nil when it can't synthesize
  a whole runnable program), and propagates printer.Fprint errors
  instead of discarding them.

No output change for the current server/public/plugin or
server/public/model content — verified via a full regen.

Co-authored-by: Cursor <cursoragent@cursor.com>

* docs: drop legacy-repo comment references, align jsdocs generator parser

Remove "port of the old mattermost-developer-documentation Hugo
shortcode" comments from the plugin doc generators/components now that
they're fully native to this monorepo, and switch gen-plugin-jsdocs.mjs
from the TypeScript compiler API to @typescript-eslint/typescript-estree
to match the old script's parser/shape, while keeping the more robust
reArg-declared-parameter-name and node-scoped comment-attachment logic.

Co-authored-by: Cursor <cursoragent@cursor.com>

* docs: bump plugin doc generators' go.mod to go 1.26

go 1.23 was arbitrarily low; align with a current Go version. go run
auto-downloads a matching toolchain on older local installs, so this
doesn't reintroduce a dependency on server/public/go.mod's version.

Co-authored-by: Cursor <cursoragent@cursor.com>

* docs: handle RestElement in gen-plugin-jsdocs parameter extraction

paramNamesFromPattern silently dropped rest parameters/destructured rest
members (e.g. (a, ...rest) or {a, ...rest}), since RestElement matched
none of its type checks. No effect on today's registry.ts output (no
rest patterns currently used there), but keeps the extraction correct if
one is ever introduced.

Co-authored-by: Cursor <cursoragent@cursor.com>

* docs: simplify plugin doc generators README note

Co-authored-by: Cursor <cursoragent@cursor.com>

* docs: check stdout write errors in plugin doc generators

os.Stdout.Write's return errors were discarded, so a failed/partial
write (e.g. broken pipe, disk full) would still exit 0. Combined with
the npm scripts' > file.tmp && mv pattern, a truncated write could be
treated as a successful generation. Fatal on either write failing.

Co-authored-by: Cursor <cursoragent@cursor.com>

* docs: flatten Server/Webapp plugin SDK reference sidebar nesting

server/server-reference.md and webapp/webapp-reference.md were the only
file in their respective folders, so the sidebar generator (which only
collapses a folder into a single entry when it has an index.md) rendered
them as a "Server" > "Server plugin SDK reference" category with one
child instead of one flat entry, unlike every sibling reference folder
(rest-api/index.md, bot-accounts/index.md, etc.).

Rename both to index.md to match that convention, and update the ~20
cross-referencing links (many with #anchor fragments) that pointed at
the old /reference/server/server-reference and /reference/webapp/webapp-
reference paths.

Co-authored-by: Cursor <cursoragent@cursor.com>

---------

Co-authored-by: Cursor <cursoragent@cursor.com>
2026-08-21 15:21:21 +03:00
Eva Sarafianou bcc9ce5e4a docs(P14): reconcile developer docs drift through Mattermost Blocks (#38043)
* docs(P14): reconcile developer docs drift through Mattermost Blocks

* docs: fix developer docs MDX conversion

* docs: clarify developer redirect filtering

* docs: remove partial developer redirects
2026-08-20 12:27:30 +03:00
Scott Bishel 78d120399f MM-68396: Remove deprecated dialog date/datetime fields for v12.0 (#37759)
* Drop top-level min_date/max_date/time_interval and allow_manual_time_entry;
require datetime_config (and manual_time_entry). Update docs, tests, and e2e fixtures accordingly.

* update important-upgrade-notes.rst per Doc Impact Analysis
2026-08-18 10:58:59 -06:00
Eva SarafianouandCursor 6417c9342a docs: port P13/P14 content drift into monorepo (#37590)
* docs(P13c): add missing manage-plugins.mdx to Administration Guide Configure

source/administration-guide/configure/manage-plugins.rst existed in the
Sphinx repo pre-fork but was never carried over during the initial P2
move or the P13c Configure reconciliation pass. Port it verbatim
(RST -> MDX) with links/anchors verified against the current docs/main
tree (mmctl-command-line-tool, plugins, plugins-configuration-settings,
popular-integrations, environment-configuration-settings#maximum-file-size,
air-gapped-deployment).

#### Release Note
```release-note
NONE
```

Co-authored-by: Cursor <cursoragent@cursor.com>

* docs(P13/P14): port remaining content drift into monorepo

Ports outstanding content-only drift identified in docs-experimental
phase-P13/P14: V11.9 docs major release content, Azure Blob Storage
guide, ID-attribute case-sensitivity notes, ranked attribute types and
attribute value masking for ABAC, plugin management guide linkage, and
the P14 markdown action buttons reference page with its
interactive-messages cross-reference. Excludes all automation/workflow
files, which are handled separately.

Co-authored-by: Cursor <cursoragent@cursor.com>

* docs(sidebar): link top-level category headers to their landing page

buildCategory() only recognized a literal index.md(x) filename as a
category's landing page, but Overview, Deployment Guide, Administration
Guide, Security & Compliance, End User Guide, Integrations Guide, Get
Help, and Use Case Guide all use a *-index.md(x) filename instead (to
avoid collisions when flattening for URL stability). As a result,
clicking most top-level sidebar categories only expanded/collapsed the
list instead of navigating to their overview page. Recognize the
*-index.md(x) convention as a fallback so these categories link
correctly, matching the existing Deployment Guide/Overview behavior.

Co-authored-by: Cursor <cursoragent@cursor.com>

* docs(P13/P14): fix blog link typo and restore dropped Open Source Components entry

Audit against Sphinx source (docs/source/product-overview/) found two
drops from the 7e915c99f8 content-drift port:

- mattermost-v11-changelog.mdx: blog link slug had an erroneous "-0"
  (mattermost-v11-9-0-is-now-available -> mattermost-v11-9-is-now-available),
  likely copy-pasted from the v11.8.0 entry above it.
- mobile-app-changelog.mdx: the 2.42.0 release entry's "Open Source
  Components" subsection (voximplant/react-native-foreground-service
  removal) was dropped entirely during the RST->MDX port.

#### Release Note
```release-note
NONE
```

Co-authored-by: Cursor <cursoragent@cursor.com>

* docs(fix): remove standalone SQL comment line breaking MDX parse

A lone "--" comment line inside a <pre><code> SQL block was parsed by
remark as a setext heading underline, splitting the surrounding
paragraph and leaving the <code> tag unclosed. CI caught this in the
docusaurus build. Merge the comment into the surrounding paragraph.

Co-authored-by: Cursor <cursoragent@cursor.com>

* docs(fix): escape curly braces in JSONB path literal breaking SSG render

'{options}' inside a SQL <pre><code> block was parsed by MDX as a JSX
expression referencing an undefined "options" identifier, causing
"ReferenceError: options is not defined" during static site
generation. Escape the braces the same way already done for other
JSONB path literals elsewhere in this file. Verified with a full local
docusaurus build.

Co-authored-by: Cursor <cursoragent@cursor.com>

* docs(sidebar): reuse the landing-file resolver for nested category sorting/labels

The prior fix only applied the index.md(x) / *-index.md(x) fallback to
a category's own link resolution. Nested category sort keys and the
category label fallback still assumed a literal index.mdx, so a
sub-directory using the *-index.md(x) naming convention would sort by
its default position (9999) instead of its declared sidebar_position,
and its category label would fall back to a humanized directory name
instead of the landing page's title. Factor the resolver into a shared
findIndexFile() helper and use it in both places.

Co-authored-by: Cursor <cursoragent@cursor.com>

---------

Co-authored-by: Cursor <cursoragent@cursor.com>
2026-07-21 13:40:08 +03:00
Eva SarafianouandCursor 70b62d9e3f docs: fix broken images, cross-links, and layout overflow after docs-experimental migration (#37418)
* docs: restore missing e2e/mobile push-notification images

These developer-docs images existed in mattermost-developer-documentation's
static assets but were never copied into the monorepo during the docs
migration, leaving broken image links in e2e-cheatsheets.md and the mobile
push-notification pages. Also fixes e2e-cheatsheets.md's image paths, which
used Hugo-style relative paths that don't resolve under Docusaurus.

Co-authored-by: Cursor <cursoragent@cursor.com>

* docs: fix broken outgoing webhooks screenshot path

The image reference used an absolute path left over from the old Hugo
site (/integrate/faq/images/...), which doesn't resolve under Docusaurus
since no matching file exists under site/static. The image already lives
co-located in docs/develop/integrate/faq/images/, so point to it with a
relative path instead.

Co-authored-by: Cursor <cursoragent@cursor.com>

* docs: delete Azure AKS deployment guide page

The page's screenshots never made it into the monorepo during migration
and don't exist under any known source (docs, docs-experimental, or
mattermost-developer-documentation). Remove the page rather than ship
broken images; drop it from the PDF book manifest and repoint its legacy
redirect to the general Kubernetes deployment guide accordingly.

Co-authored-by: Cursor <cursoragent@cursor.com>

* docs: fix heading levels on Kubernetes deploy guides

deploy-k8s.mdx and deploy-k8s-oke.mdx used H1 (#) for their top-level
in-page sections (Installation steps, Common Errors, etc.) instead of
H2. Docusaurus' contentTitle extraction grabs the first H1 it finds
anywhere in the doc, which stole the page title slot from the actual
frontmatter title, so no title rendered at the top of the page. The
flat H1/H2 mix also meant TOC entries for install steps rendered as a
flat list at the page's TOC root instead of nested under their section,
which made the TOC sidebar overflow/misrender on narrower/zoomed
viewports. Bump every heading in both files one level (H1->H2->H3->H4)
so the page title renders and the TOC nests correctly.

Co-authored-by: Cursor <cursoragent@cursor.com>

* docs: fix TOC/layout horizontal overflow on wide code blocks

The Kubernetes prerequisites page's TOC sidebar overflowed off-screen at
higher browser zoom levels. Root cause: `.docMainContainer` has `max-width:
none !important` (intentional, so OpenAPI's 2-column layout gets full
width), but that removes the container's only width ceiling. Combined with
the default flexbox `min-width: auto` on both the main container (in its
outer flex row with the left sidebar) and the doc content column (in the
inner content/TOC row), a sufficiently wide code block's intrinsic
min-content size could drag the whole row wider than the viewport instead
of scrolling within itself. Add `min-width: 0` at both flex levels so
`overflow-x: auto` on code blocks and tables actually contains overflow
locally, and make the responsive table-scroll CSS apply unconditionally
instead of only under the 996px breakpoint (zoom shrinks effective layout
width without re-triggering the media query).

Also shorten the illustrative base64 example values in the AWS Aurora
Postgres secret example, which were long enough on their own to be a
secondary contributor to the same overflow.

Co-authored-by: Cursor <cursoragent@cursor.com>

* docs: restore additional missing images found via full static-resolution scan

Docusaurus's build-time image validator doesn't reliably warn on every
broken absolute-path reference, so npm run build alone missed these.
A direct scan of every image reference in docs/develop and docs/main
against the filesystem turned up 38 more genuinely missing images
(desktop architecture, VPN/Pritunl setup, docker dev environment,
i18n-extract tooling, MVP program, onboarding, schema migration guide,
plugin best-practices, and interactive-message screenshots) that were
never copied over from mattermost-developer-documentation during
migration. Two references (interactive-dialog-date.png and
interactive-dialog-datetime.png) remain unresolved because no source
image exists anywhere in the legacy repo.

Co-authored-by: Cursor <cursoragent@cursor.com>

* docs: fix broken cross-links after .md to .mdx migration

Several Calls admin guide pages and the air-gapped quick-start runbook
were renamed from .md to .mdx during migration, but their internal
cross-links still pointed at the old .md filenames (and, for
setup-tls, the old flattened path), so Docusaurus couldn't resolve
them. Found via a full scan of every relative markdown link under
docs/main and docs/develop against the filesystem, since the build's
own "couldn't be resolved" warnings don't reliably surface every
broken link.

Co-authored-by: Cursor <cursoragent@cursor.com>

* docs: remove broken date/datetime element screenshots

These screenshots were never captured before migration (broken on
the currently live docs.mattermost.com too), and no source image
exists anywhere in the legacy repos to restore. Remove the broken
references rather than leave a dead image link.

Co-authored-by: Cursor <cursoragent@cursor.com>

* docs: move newly-restored desktop architecture images to static/

These 4 images were restored co-located next to their content files,
matching an existing (if inconsistent) convention elsewhere in
docs/develop. Since these are new additions rather than pre-existing
working references, move them to docs/site/static/ instead of
perpetuating the co-located pattern. Repo-wide normalization of the
other ~70 existing co-located image references is left for a separate
follow-up PR.

Co-authored-by: Cursor <cursoragent@cursor.com>

* docs: remove deleted AKS page from sidebar generator's manual grouping

master (#37402) fixed the sidebar generator's SRC path but this
branch's deletion of deploy-k8s-aks.mdx still left a dangling
reference in the Deployment Guide's manual Kubernetes grouping,
which fails Docusaurus's sidebar-doc-id validation at build time.

Co-authored-by: Cursor <cursoragent@cursor.com>

---------

Co-authored-by: Cursor <cursoragent@cursor.com>
2026-07-09 18:13:08 +03:00
Eva SarafianouandCursor 1d3bbc638b feat(docs): move docs-experimental content into monorepo docs/ (#37330)
* feat(docs): move docs-experimental content into monorepo docs/

Co-authored-by: Cursor <cursoragent@cursor.com>

* Remove plan related files and comments

* Cleanup comments, remove generated files

---------

Co-authored-by: Cursor <cursoragent@cursor.com>
2026-07-02 18:42:57 +03:00