Skip to content

[pull] master from mattermost:master - #954

Merged
pull[bot] merged 8 commits into
code:masterfrom
mattermost:master
Sep 1, 2026
Merged

[pull] master from mattermost:master#954
pull[bot] merged 8 commits into
code:masterfrom
mattermost:master

Conversation

@pull

@pull pull Bot commented Sep 1, 2026

Copy link
Copy Markdown

See Commits and Changes for more details.


Created by pull[bot] (v2.0.0-alpha.4)

Can you help keep this open source service alive? 💖 Please sponsor : )

isacikgoz and others added 8 commits September 1, 2026 17:00
i18n-ci-template.yml existed to fail any PR that touched a non-English
translation file unless it came from the weblate account, back when
translations arrived exclusively through Weblate.

Translations can now be authored in-repo and land in the same PR as the
English string they translate, so that gate rejects exactly the change it
should be letting through. The changed-file detection stays -- a direct edit to
a locale catalog is still worth surfacing on the PR -- but it emits a warning
annotation instead of failing, and the weblate-account bypass is gone with
nothing left to bypass.
…add/remove on Save (#38056)

* [MM-69866] Add Applies-to resource picker (Users, Channels, Posts) to New attribute

Adds an "Applies to" Card to the Global Attributes "New attribute" page,
letting a sysadmin pick which resources (Users, Channels, Posts) an
attribute applies to before saving. Each selected resource becomes its
own linked PropertyField, created serially after the template so a
partial failure can be attributed to a specific resource and rolled
back deterministically (linked fields deleted before the template,
per the server's deletion-order protection).

A `user`-scoped linked field shares its namespace with Custom Profile
Attributes, so name conflicts and the shared 20-field cap are newly
reachable failure modes -- both get distinct, actionable banners
instead of a generic failure message.

Client-only change; no server, migration, or Client4 changes required.

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

* Split the Applies-to row into one component per resource type

Replaces the single resourceType-parameterized AttributeAppliesToItem
with three dedicated components (User/Channel/Post), each hardcoding
its own icon, label, and testids instead of looking them up by a prop.
All three share one prop signature (AttributeAppliesToItemProps,
exported from attribute_applies_to_constants.tsx) so the parent's
Record<ResourceObjectType, ComponentType<...>> lookup map fails to
compile if any of the three drift from it.

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

* Fix Prettier formatting in the global_attributes E2E files

npm run check for e2e-tests/playwright runs lint && prettier && tsc,
and CI's prettier --check step was failing on both files -- they'd
only been checked against webapp's ESLint config locally, not this
package's Prettier config.

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

* Move the Applies-to row's Remove action into the expanded state

The collapsed row no longer has any remove affordance -- the only way
to remove a resource is to expand it first, then click "Remove
resource" in the header (still a sibling of the toggle, not nested
inside its clickable area). Styled per the design prototype: a plain
text button, transparent by default, --error-text colored, with a
rgba(--error-text-color-rgb, 0.08) hover tint -- the same pattern
already used by OrphanedFieldDeleteButton (system_properties) rather
than a new one-off style.

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

* Add 12px right margin to the Applies-to row's Remove button

Matches the design prototype's spacing between the button and the
row's right edge.

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

* Rename applies-to constants file to .ts

The file has no JSX, so the .tsx extension was misleading.

* Polish the Applies-to card empty state and expanded rows

Match the design: updated copy and type, primary/tertiary add buttons, header-only open tint, and a 176px form row in the expanded body.

* Drop bottom padding on the last Applies-to body row

It stacked with the container padding and looked doubled.

* Size Unique name label, value, and Edit to 12px

The caption and Edit link were 14px against a 12px label; shrink the edit input to match.

* Treat Unique name click-away as Done

Opening Edit then clicking away left the input open and froze auto-derivation. Blur now uses the same commit path as Done, matching the channel URL field.

* Lock Type to Text while an external source is linked

LDAP and SAML only sync on text fields; disabling the type menu until the last chip is removed keeps that invariant in the UI.

* Show linked sources on the Options line as Synced with chips

Once a source is selected the chips replace the Text help copy; the divider stays until then so the unlinked state still separates Options from the add-source trigger.

* Order flex container properties to satisfy stylelint.

* Show CPA banners only for Users and copy leftover-template rollback.

Channels and Posts name conflicts used User Attribute wording, and a leftover template after a linked-field rollback looked like a clean save. E2E cleanup now rediscovers template and linked fields instead of relying on the success-path list.

* Use Compass Button for Applies-to row chrome.

Override quaternary styling so the accordion header stays body-text chrome, and keep Remove as tertiary destructive.

* Await hanging creates before unmount-save negative asserts.

waitFor(() => Promise.resolve()) returns on the first check and can pass before finalizeSave, so the mount guard was not actually load-bearing.

* Drop unused PropertyField and ResourceObjectType imports from the Playwright spec.

* [MM-69864][MM-69870] Edit existing attributes and persist Applies-to add/remove on Save

Open the Definition form for managed attributes, PATCH on Save, and keep Applies-to add/remove local until Save diffs against the linked fields loaded at mount.

Co-authored-by: Julien Tant <JulienTant@users.noreply.github.com>

* Wait for deferred Edit navigation in listing tests and satisfy no-negated-condition.

Co-authored-by: Julien Tant <JulienTant@users.noreply.github.com>

* Format Playwright global attributes edit tests with Prettier.

Co-authored-by: Julien Tant <JulienTant@users.noreply.github.com>

* Split Global Attributes Playwright specs into listing and form files.

Flag-off access-gate coverage stays in the listing spec so the form spec never turns GlobalAttributes off.

Co-authored-by: Julien Tant <JulienTant@users.noreply.github.com>

* Use dedicated type-lock tooltip copy and skip Applies-to focus on edit hydrate.

The external-source lock was showing the accessible name as a tooltip. Edit
hydration of all three resource types was treated as adding the last type and
stole Display name autofocus.

Co-authored-by: Julien Tant <JulienTant@users.noreply.github.com>

* Fix Applies-to removal data-loss and lock Name while resources are linked.

Addresses review feedback: Save deleted removed linked fields before
PATCHing the template, so a PATCH failure unrelated to type (e.g. a name
conflict) still lost every value on the removed resource. Save now orders
DELETE/PATCH based on whether the type actually changed -- PATCH first
when it didn't, so a failure aborts before anything is deleted -- and
warns with a confirmation dialog before removing any resource with a
persisted linked field. Also locks Unique name editing while a linked
field is still persisted, since the server does not propagate a rename
onto it.

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

* Report partial-save, not "nothing else was saved", when the removal DELETE fails after a successful PATCH.

deleteRemovedLinkedFields is called both before and after the template
PATCH depending on typeChanged, but reused the same error copy for both --
"nothing else was saved" is only true for the before-PATCH case. Give the
after-PATCH case its own applies_to_remove_partial_save error kind/message
so the banner doesn't contradict the update that actually went through.

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

* Disable linking a new external source while an attribute still applies to a resource

* Fix Unique name silently unpinning after a no-op Edit/Done round-trip in edit mode.

handleDoneClick's "did the committed value change from the current auto-slug"
heuristic is correct for create mode (a coincidental match keeps live
derivation on) but was also applying in edit mode, where the loaded name
must stay pinned regardless. When the persisted name happened to equal the
current auto-slug, opening Edit and clicking Done with no real change
would unpin it, and the next Display name edit would silently re-slug the
Unique name out from under the admin.

Reported by Lindy on the PR. Adds Jest and Playwright regression coverage;
verified the Playwright tests reproduce the exact symptom when the fix is
reverted.

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

---------

Co-authored-by: Claude Sonnet 5 <noreply@anthropic.com>
Co-authored-by: Mattermost Build <build@mattermost.com>
Co-authored-by: Cursor Agent <cursoragent@cursor.com>
Co-authored-by: Julien Tant <JulienTant@users.noreply.github.com>
* webapp: remove string refs from the file preview and multiselect lists

String refs are rejected under StrictMode. Neither of these was read: the file
preview row is looked up through its data-client-id attribute, and the
multiselect list only needs a ref on the selected row.

Co-authored-by: Jesse Hallam <lieut-data@users.noreply.github.com>
(cherry picked from commit 0c718a4)

* webapp: remove the remaining string refs from selector modal rows

Each of these list rows fell back to a string ref for the unselected case,
which strict mode reports because string refs can't be attached twice and
don't survive a render being restarted. The unselected row never needs a ref,
and nothing read the one AddUsersToRoleModal was setting.

Co-authored-by: Jesse Hallam <lieut-data@users.noreply.github.com>
(cherry picked from commit e727ac0)

---------

Co-authored-by: Cursor Agent <cursoragent@cursor.com>
Co-authored-by: Jesse Hallam <lieut-data@users.noreply.github.com>
…-dom (#38240)

* webapp: stop bundling a second copy of react-dom into @mattermost/components

react-dom is only a devDependency of the package, so rollup inlined the whole
renderer (both the development and production builds) into dist/index.esm.js
rather than treating it as external. The web app therefore loaded two react-dom
instances: the shared singleton, and the one TourTipBackdrop's createPortal call
pulled in. Two renderers sharing one tree is a hazard under concurrent rendering,
and the duplicate also cost 1.2MB of bundle.

dist/index.esm.js drops from 1,264,396 to 83,919 bytes.

Co-authored-by: Jesse Hallam <lieut-data@users.noreply.github.com>
(cherry picked from commit 9d42d25)

* webapp: take react-bootstrap's unsafe lifecycle fix

TabContent used UNSAFE_componentWillReceiveProps and Dropdown used
UNSAFE_componentWillUpdate. React reports both under StrictMode, and neither is
safe to interrupt under concurrent rendering. Every tab strip in the web app
mounts TabContent, so this reached the emoji picker, drafts, scheduled posts and
the admin console.

The fix is mattermost/react-bootstrap#14. TabContent's only job in that lifecycle
was clearing animation bookkeeping when animation is turned off, which is derived
state, and Dropdown reads focus out of the DOM before the menu is removed, which
is what getSnapshotBeforeUpdate exists for.

Both workspaces that depend on react-bootstrap have to move together. Pinning
only channels leaves platform/components on the old commit, which stops npm
hoisting the two into one copy and puts a second, unfixed copy in the tree.

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

* webapp: repin react-bootstrap to the merged commit

mattermost/react-bootstrap#14 landed on 0.34 as a squash merge, d693ec8. The
branch was pinned to 797ff61, the pull request head, which is now unreachable
from any branch there and will disappear whenever the fork is pruned.

Content is unchanged: 797ff61 and d693ec8 share the tree e029175 and the parent
c177015, so the lockfile's integrity hash is untouched. Only the resolved commit
moves, in both workspaces that depend on react-bootstrap.

---------

Co-authored-by: Cursor Agent <cursoragent@cursor.com>
Co-authored-by: Jesse Hallam <lieut-data@users.noreply.github.com>
* webapp: stop react-transition-group from calling findDOMNode

Every CSSTransition and Transition in the web app was rendered without a
nodeRef, so react-transition-group looked its DOM node up with findDOMNode.
React 18 warns about that under StrictMode and React 19 removes the API
entirely.

Where the transitioning element was already ours, the existing ref is reused.
MenuWrapperAnimation and the mobile right drawer animate arbitrary children,
so those gained a wrapper element to hang the ref on; both wrappers are static
boxes, so the positioned content inside them is unaffected.

Passing nodeRef also changes the transition callback signature: the node is no
longer the first argument, so AutoHeightSwitcher reads it from its own ref.

Co-authored-by: Jesse Hallam <lieut-data@users.noreply.github.com>
(cherry picked from commit d8ef488)

* webapp: update snapshots for the menu animation wrapper

Co-authored-by: Jesse Hallam <lieut-data@users.noreply.github.com>
(cherry picked from commit c16e038)

---------

Co-authored-by: Cursor Agent <cursoragent@cursor.com>
Co-authored-by: Jesse Hallam <lieut-data@users.noreply.github.com>
… safe to remount (#38239)

* webapp: let PerformanceReporterController be unmounted

The effect assumed the component would never unmount and logged an error if it
did, so it had no cleanup. React re-runs effects on every mount, and StrictMode
mounts twice on purpose, which left an orphaned PerformanceReporter behind on
every page load: a PerformanceObserver still observing, a report timer still
running, and a visibilitychange listener still attached, all duplicating the
telemetry the live reporter sends.

The reporter now disconnects on cleanup and ignores web vitals afterwards, since
web-vitals callbacks can't be unregistered.

Co-authored-by: Jesse Hallam <lieut-data@users.noreply.github.com>
(cherry picked from commit 8587f31)

* webapp: replace UNSAFE_componentWillReceiveProps in PermissionSystemSchemeSettings

The last unsafe lifecycle left in the web app, and the one StrictMode reports
when the system scheme page is opened. componentWillReceiveProps runs during the
render phase, so an interrupted render can invoke it more than once per commit;
the equivalent check in componentDidUpdate runs once, after the commit.

Co-authored-by: Jesse Hallam <lieut-data@users.noreply.github.com>
(cherry picked from commit 68f324d)

---------

Co-authored-by: Cursor Agent <cursoragent@cursor.com>
Co-authored-by: Jesse Hallam <lieut-data@users.noreply.github.com>
* Load sidebar DM/GM profiles once channels have loaded

* Add e2e coverage for sidebar DM/GM profiles on load

* Revert "Load sidebar DM/GM profiles once channels have loaded"

This reverts commit b562945.

* Gate sidebar profile loading on the channels and memberships too

* Fix sidebar profile initialization load race

* Batch the initial sidebar load flags with their data

---------

Co-authored-by: Mattermost Build <build@mattermost.com>
@pull pull Bot locked and limited conversation to collaborators Sep 1, 2026
@pull pull Bot added the ⤵️ pull label Sep 1, 2026
@pull
pull Bot merged commit ff806bf into code:master Sep 1, 2026
8 of 9 checks passed
Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants