Skip to content

MM-69835 Update to React 19 and rebuild - #13

Open
hmhealey wants to merge 2 commits into
MM-69835-4from
MM-69835-5
Open

MM-69835 Update to React 19 and rebuild#13
hmhealey wants to merge 2 commits into
MM-69835-4from
MM-69835-5

Conversation

@hmhealey

@hmhealey hmhealey commented Aug 4, 2026

Copy link
Copy Markdown
Member

Summary

We check the compiled code into this repo because we don't publish our fork to NPM. I pulled that out until the end for ease of review

Ticket Link

https://mattermost.atlassian.net/browse/MM-69835

@hmhealey

hmhealey commented Aug 4, 2026

Copy link
Copy Markdown
Member Author

@coderabbitai Review this

@coderabbitai

coderabbitai Bot commented Aug 4, 2026

Copy link
Copy Markdown
🧩 Analysis chain

🏁 Script executed:

#!/bin/bash
set -euo pipefail

echo '== Pull request metadata =='
gh pr view 13 --repo mattermost/react-bootstrap --json number,title,baseRefName,headRefName,files,additions,deletions --jq '{number,title,base:.baseRefName,head:.headRefName,additions,deletions,files:[.files[].path]}'

echo
echo '== Changed-file summary =='
git diff --stat "origin/$(gh pr view 13 --repo mattermost/react-bootstrap --json baseRefName --jq .baseRefName)"...HEAD

echo
echo '== Changed source and metadata files =='
git diff --name-only "origin/$(gh pr view 13 --repo mattermost/react-bootstrap --json baseRefName --jq .baseRefName)"...HEAD | sed -n '1,240p'

Length of output: 3327


🏁 Script executed:

#!/bin/bash
set -euo pipefail

repo="mattermost/react-bootstrap"
pr="13"

echo '== Dependency manifest patch =='
gh api --paginate "repos/$repo/pulls/$pr/files?per_page=100" \
  --jq '.[] | select(.filename == "package.json") | .patch'

echo
echo '== Lockfile header and dependency-related changes =='
gh api --paginate "repos/$repo/pulls/$pr/files?per_page=100" \
  --jq '.[] | select(.filename == "yarn.lock") | .patch' | sed -n '1,360p'

echo
echo '== Generated files with React API changes =='
for file in \
  'es/Collapse.js' \
  'es/Fade.js' \
  'es/Overlay.js' \
  'es/OverlayTrigger.js' \
  'es/Dropdown.js' \
  'es/Modal.js' \
  'es/utils/mergeRefs.js' \
  'lib/Collapse.js' \
  'lib/OverlayTrigger.js'
do
  echo "--- $file ---"
  gh api --paginate "repos/$repo/pulls/$pr/files?per_page=100" \
    --jq --arg file "$file" '.[] | select(.filename == $file) | .patch'
done

Length of output: 6808


🏁 Script executed:

#!/bin/bash
set -euo pipefail

repo="mattermost/react-bootstrap"
base="MM-69835-4"
head="MM-69835-5"
workdir="$(mktemp -d)"
trap 'rm -rf "$workdir"' EXIT

fetch_file() {
  local ref="$1"
  local file="$2"
  local destination="$3"
  gh api "repos/$repo/contents/$file?ref=$ref" --jq '.content' | tr -d '\n' | base64 -d > "$destination"
}

for file in package.json es/Collapse.js es/Fade.js es/Overlay.js es/OverlayTrigger.js es/Dropdown.js es/Modal.js es/utils/mergeRefs.js; do
  safe_name="${file//\//_}"
  fetch_file "$base" "$file" "$workdir/base_$safe_name"
  fetch_file "$head" "$file" "$workdir/head_$safe_name"
done

echo '== package.json: package contract and scripts =='
python3 - "$workdir/base_package.json" "$workdir/head_package.json" <<'PY'
import json, sys
for label, path in zip(("base", "head"), sys.argv[1:]):
    p = json.load(open(path))
    print(f"-- {label} --")
    for key in ("version", "main", "module", "browser", "peerDependencies", "dependencies", "devDependencies", "scripts"):
        print(f"{key}: {json.dumps(p.get(key), indent=2, sort_keys=True)}")
PY

echo
echo '== Targeted generated-code diffs: imports, hooks, refs, ReactDOM, transition callbacks =='
for file in es/Collapse.js es/Fade.js es/Overlay.js es/OverlayTrigger.js es/Dropdown.js es/Modal.js es/utils/mergeRefs.js; do
  safe_name="${file//\//_}"
  echo "--- $file ---"
  diff -U3 "$workdir/base_$safe_name" "$workdir/head_$safe_name" \
    | grep -E '^(@@|[+-].*(React|react-dom|findDOMNode|use[A-Z]|ref|Ref|Transition|createElement|defaultProps|propTypes))' \
    || true
done

echo
echo '== Full diff summaries =='
for file in es/Collapse.js es/Fade.js es/Overlay.js es/OverlayTrigger.js es/Dropdown.js es/Modal.js es/utils/mergeRefs.js; do
  safe_name="${file//\//_}"
  echo "--- $file ---"
  diff -u "$workdir/base_$safe_name" "$workdir/head_$safe_name" | diffstat 2>/dev/null || \
    diff -u "$workdir/base_$safe_name" "$workdir/head_$safe_name" | sed -n '1,30p'
done

Length of output: 2326


🏁 Script executed:

#!/bin/bash
set -euo pipefail

repo="mattermost/react-bootstrap"
pr="13"

echo '== Pull request commit references =='
gh api "repos/$repo/pulls/$pr" \
  --jq '{base_sha:.base.sha, base_label:.base.label, head_sha:.head.sha, head_label:.head.label, merge_commit_sha:.merge_commit_sha, state:.state}'

echo
echo '== Pull request commits =='
gh api --paginate "repos/$repo/pulls/$pr/commits?per_page=100" \
  --jq '.[] | {sha, parents:[.parents[].sha], message:(.commit.message | split("\n")[0])}'

echo
echo '== Confirm selected files exist at the head commit =='
head_sha="$(gh api "repos/$repo/pulls/$pr" --jq '.head.sha')"
for file in package.json es/Collapse.js es/Fade.js es/Overlay.js es/OverlayTrigger.js es/Dropdown.js es/Modal.js es/utils/mergeRefs.js; do
  printf '%s: ' "$file"
  gh api "repos/$repo/contents/$file?ref=$head_sha" --jq '.sha'
done

Length of output: 2137


🏁 Script executed:

#!/bin/bash
set -euo pipefail

repo="mattermost/react-bootstrap"
base="a182b78abc9eef0cdbe708f3508a00beb94e4cc2"
head="177c152a9d7801dab0f00a278bbf6dae2ba0bc0f"
workdir="$(mktemp -d)"
trap 'rm -rf "$workdir"' EXIT

fetch_file() {
  local ref="$1" file="$2" output="$3"
  gh api "repos/$repo/contents/$file?ref=$ref" --jq '.content' \
    | tr -d '\n' | base64 --decode > "$output"
}

files=(
  package.json
  es/Collapse.js es/Fade.js es/Overlay.js es/OverlayTrigger.js
  es/Dropdown.js es/DropdownMenu.js es/Modal.js es/utils/mergeRefs.js
)

for file in "${files[@]}"; do
  safe="${file//\//_}"
  fetch_file "$base" "$file" "$workdir/base_$safe"
  fetch_file "$head" "$file" "$workdir/head_$safe"
done

echo '== package.json contract and test scripts =='
python3 - "$workdir/base_package.json" "$workdir/head_package.json" <<'PY'
import json, sys
for label, path in zip(("base", "head"), sys.argv[1:]):
    package = json.load(open(path))
    print(f"-- {label} --")
    for key in ("version", "main", "module", "browser", "peerDependencies", "dependencies", "devDependencies", "scripts"):
        print(f"{key}: {json.dumps(package.get(key), indent=2, sort_keys=True)}")
PY

echo
echo '== All changed lines in selected modules =='
for file in "${files[@]:1}"; do
  safe="${file//\//_}"
  echo "--- $file ---"
  diff -u "$workdir/base_$safe" "$workdir/head_$safe" | sed -n '1,260p' || true
done

echo
echo '== React 19-sensitive calls in rebuilt ESM output =='
grep -nE 'findDOMNode|ReactDOM|createPortal|createRoot|forwardRef|defaultProps|UNSAFE_|componentWill|ref=|use[A-Z][A-Za-z]*\(' \
  "$workdir/head_es_Collapse.js" \
  "$workdir/head_es_Fade.js" \
  "$workdir/head_es_Overlay.js" \
  "$workdir/head_es_OverlayTrigger.js" \
  "$workdir/head_es_Dropdown.js" \
  "$workdir/head_es_DropdownMenu.js" \
  "$workdir/head_es_Modal.js" \
  "$workdir/head_es_utils_mergeRefs.js" \
  || true

Length of output: 2244


🏁 Script executed:

#!/bin/bash
set -euo pipefail

base="a182b78abc9eef0cdbe708f3508a00beb94e4cc2"
dependency_commit="b0055d2c5d2a903a00408cc525f9b4b090ef24e4"
head="177c152a9d7801dab0f00a278bbf6dae2ba0bc0f"
repo="mattermost/react-bootstrap"

echo '== Local object availability =='
for revision in "$base" "$dependency_commit" "$head"; do
  if git cat-file -e "${revision}^{commit}" 2>/dev/null; then
    echo "$revision: present"
  else
    echo "$revision: unavailable"
  fi
done

echo
echo '== Direct-parent commit availability through GitHub =='
for revision in "$dependency_commit" "$head"; do
  printf '%s: ' "$revision"
  gh api "repos/$repo/commits/$revision" --jq '{sha,parents:[.parents[].sha],files_changed:(.files | length)}'
done

echo
echo '== Isolated rebuild diff, when the direct parent is available locally =='
if git cat-file -e "${dependency_commit}^{commit}" 2>/dev/null && git cat-file -e "${head}^{commit}" 2>/dev/null; then
  git diff --stat "$dependency_commit" "$head"
  echo
  git diff -- package.json "$dependency_commit" "$head" || true
  echo
  git diff -U5 "$dependency_commit" "$head" -- \
    es/Collapse.js es/Fade.js es/Overlay.js es/OverlayTrigger.js es/Dropdown.js es/DropdownMenu.js es/Modal.js es/utils/mergeRefs.js \
    | sed -n '1,1200p'
fi

echo
echo '== Head package manifest through GitHub API =='
gh api "repos/$repo/contents/package.json?ref=$head" --jq '.content' | tr -d '\n' | base64 --decode \
  | python3 -c 'import json,sys; p=json.load(sys.stdin); print(json.dumps({k:p.get(k) for k in ("peerDependencies","dependencies","devDependencies","scripts")}, indent=2, sort_keys=True))'

Length of output: 39359


🏁 Script executed:

#!/bin/bash
set -euo pipefail

head="177c152a9d7801dab0f00a278bbf6dae2ba0bc0f"

echo '== Exact locations in changed ESM files =='
nl -ba es/Overlay.js | sed -n '76,122p'
echo
nl -ba es/OverlayTrigger.js | sed -n '270,310p'
echo
nl -ba es/Dropdown.js | sed -n '280,350p'
echo
nl -ba es/Modal.js | sed -n '205,255p'

echo
echo '== Equivalent generated output occurrences =='
rg -n -C2 "display: 'content'|target: null|isOverflowing\(document\.body\)" \
  es lib amd/react-bootstrap.js dist/react-bootstrap.js

echo
echo '== Public prop declarations and existing test coverage =='
rg -n -C4 "target:|container:|container\s*=|<Overlay|<Modal|OverlayTrigger" \
  src test README.md docs 2>/dev/null || true

echo
echo '== Changed test files =='
git diff --name-only a182b78abc9eef0cdbe708f3508a00beb94e4cc2 177c152a9d7801dab0f00a278bbf6dae2ba0bc0f -- test src || true

Length of output: 1251


🏁 Script executed:

#!/bin/bash
set -euo pipefail

echo '== Exact locations in changed ESM files =='
cat -n es/Overlay.js | sed -n '76,122p'
echo
cat -n es/OverlayTrigger.js | sed -n '270,310p'
echo
cat -n es/Dropdown.js | sed -n '280,350p'
echo
cat -n es/Modal.js | sed -n '205,255p'

echo
echo '== Equivalent generated output occurrences =='
rg -n -C2 "display: 'content'|target: null|isOverflowing\(document\.body\)" \
  es lib amd/react-bootstrap.js dist/react-bootstrap.js || true

echo
echo '== Public prop declarations and existing test coverage =='
rg -n -C4 "target:|container:|container\s*=|<Overlay|<Modal|OverlayTrigger" \
  src test README.md docs 2>/dev/null || true

echo
echo '== Changed test or source files =='
git diff --name-only a182b78abc9eef0cdbe708f3508a00beb94e4cc2 177c152a9d7801dab0f00a278bbf6dae2ba0bc0f -- test src || true

Length of output: 43725


@hmhealey: I found two blocking regressions.

  1. Overlay discards the consumer-supplied target.
    es/Overlay.js Line 105 sets target: null after spreading props. This overrides <Overlay target={...}>. Standalone overlays cannot position against their supplied target. The same issue is in lib/Overlay.js, amd/react-bootstrap.js, and dist/react-bootstrap.js.

  2. The new wrapper elements use an invalid CSS value.
    es/Overlay.js Line 112 and es/OverlayTrigger.js Line 292 set display: 'content'. The intended value is display: 'contents'. Browsers ignore content and retain the wrapper <div> layout. This can change trigger layout and overlay positioning. The same typo is present in the CommonJS and bundle outputs.

I did not execute tests because the review sandbox does not execute repository code.

❌ Action failed

Review failed.

Note: CodeRabbit is an incremental review system and does not re-review already reviewed commits. This command is applicable only when automatic reviews are paused.

@coderabbitai

coderabbitai Bot commented Aug 4, 2026

Copy link
Copy Markdown

Review Change Stack

Caution

Review failed

Failed to post review comments.

We encountered an issue with GitHub. Use @coderabbitai full review to retry the review.

⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro

Run ID: 2d1dc5ed-3782-44a8-97bd-de416b80d4cd

📥 Commits

Reviewing files that changed from the base of the PR and between a182b78 and 177c152.

⛔ Files ignored due to path filters (4)
  • amd/react-bootstrap.min.js is excluded by !**/*.min.js
  • dist/react-bootstrap.js is excluded by !**/dist/**
  • dist/react-bootstrap.min.js is excluded by !**/dist/**, !**/*.min.js
  • yarn.lock is excluded by !**/yarn.lock, !**/*.lock
📒 Files selected for processing (90)
  • amd/react-bootstrap.js
  • es/ButtonGroup.js
  • es/CarouselItem.js
  • es/Collapse.js
  • es/ControlLabel.js
  • es/Dropdown.js
  • es/DropdownMenu.js
  • es/Fade.js
  • es/FormControl.js
  • es/FormControlFeedback.js
  • es/FormGroup.js
  • es/FormGroupContext.js
  • es/MenuItem.js
  • es/Modal.js
  • es/ModalContext.js
  • es/ModalDialog.js
  • es/ModalHeader.js
  • es/Nav.js
  • es/Navbar.js
  • es/NavbarBrand.js
  • es/NavbarCollapse.js
  • es/NavbarContext.js
  • es/NavbarHeader.js
  • es/NavbarToggle.js
  • es/Overlay.js
  • es/OverlayTrigger.js
  • es/Panel.js
  • es/PanelBody.js
  • es/PanelCollapse.js
  • es/PanelContext.js
  • es/PanelFooter.js
  • es/PanelGroup.js
  • es/PanelGroupContext.js
  • es/PanelHeading.js
  • es/PanelTitle.js
  • es/PanelToggle.js
  • es/ProgressBar.js
  • es/TabContainer.js
  • es/TabContainerContext.js
  • es/TabContent.js
  • es/TabContentContext.js
  • es/TabPane.js
  • es/utils/PropTypes.js
  • es/utils/bootstrapUtils.js
  • es/utils/mergeRefs.js
  • lib/ButtonGroup.js
  • lib/CarouselItem.js
  • lib/Collapse.js
  • lib/ControlLabel.js
  • lib/Dropdown.js
  • lib/DropdownMenu.js
  • lib/Fade.js
  • lib/FormControl.js
  • lib/FormControlFeedback.js
  • lib/FormGroup.js
  • lib/FormGroupContext.js
  • lib/MenuItem.js
  • lib/Modal.js
  • lib/ModalContext.js
  • lib/ModalDialog.js
  • lib/ModalHeader.js
  • lib/Nav.js
  • lib/Navbar.js
  • lib/NavbarBrand.js
  • lib/NavbarCollapse.js
  • lib/NavbarContext.js
  • lib/NavbarHeader.js
  • lib/NavbarToggle.js
  • lib/Overlay.js
  • lib/OverlayTrigger.js
  • lib/Panel.js
  • lib/PanelBody.js
  • lib/PanelCollapse.js
  • lib/PanelContext.js
  • lib/PanelFooter.js
  • lib/PanelGroup.js
  • lib/PanelGroupContext.js
  • lib/PanelHeading.js
  • lib/PanelTitle.js
  • lib/PanelToggle.js
  • lib/ProgressBar.js
  • lib/TabContainer.js
  • lib/TabContainerContext.js
  • lib/TabContent.js
  • lib/TabContentContext.js
  • lib/TabPane.js
  • lib/utils/PropTypes.js
  • lib/utils/bootstrapUtils.js
  • lib/utils/mergeRefs.js
  • package.json
🧰 Additional context used
🪛 ast-grep (0.45.0)
es/Navbar.js

[warning] 203-203: Avoid using the initial state variable in setState
Context: setBsClass('navbar', Navbar)
Note: [CWE-710] Improper Adherence to Coding Standards. Security best practice.

(setstate-same-var)

es/TabContent.js

[warning] 158-158: Avoid using the initial state variable in setState
Context: setBsClass('tab', TabContent)
Note: [CWE-710] Improper Adherence to Coding Standards. Security best practice.

(setstate-same-var)

🪛 Biome (2.5.6)
amd/react-bootstrap.js

[error] 701-701: This comparison uses the same expression on both sides.

(lint/suspicious/noSelfCompare)

📝 Walkthrough

Walkthrough

This change updates React Bootstrap for React 19. It replaces legacy context and DOM lookup APIs with modern contexts, refs, transitions, overlays, modal rendering, and runtime validation. It updates source, compiled, AMD, and development dependency versions.

Changes

React 19 compatibility migration

Layer / File(s) Summary
Context providers and consumers
es/*Context.js, es/FormGroup.js, es/Panel*.js, es/Navbar*.js, es/Tab*.js, lib/*Context.js, lib/FormGroup.js, lib/Panel*.js, lib/Navbar*.js, lib/Tab*.js
Legacy context APIs were replaced with React context providers, contextType, and useContext.
Ref-based transitions and DOM access
es/CarouselItem.js, es/Collapse.js, es/Fade.js, es/utils/mergeRefs.js, lib/CarouselItem.js, lib/Collapse.js, lib/Fade.js, lib/utils/mergeRefs.js
findDOMNode and transition node arguments were replaced with component refs, merged refs, forwarded refs, and nodeRef.
Dropdown, overlay, and modal integration
es/Dropdown*.js, es/Overlay*.js, es/Modal*.js, lib/Dropdown*.js, lib/Overlay*.js, lib/Modal*.js, amd/react-bootstrap.js
Dropdown root-close handling, Popper positioning, overlay targets, modal rendering, backdrop styling, and dialog refs now use updated React Overlays APIs.
Runtime validation and Bootstrap prop handling
es/utils/PropTypes.js, es/utils/bootstrapUtils.js, es/ButtonGroup.js, es/MenuItem.js, es/ProgressBar.js, lib/utils/PropTypes.js, lib/utils/bootstrapUtils.js, lib/ButtonGroup.js, lib/MenuItem.js, lib/ProgressBar.js
Cross-prop and child validation moved from PropTypes declarations to development-only render-time warnings. Bootstrap defaults and allowed values are applied during rendering.
Bundle and dependency updates
amd/react-bootstrap.js, package.json
The AMD bundle includes updated React, transition, overlay, Popper, and utility modules. React development dependencies were updated from 18.3.1 to 19.2.7.

Estimated code review effort: 5 (Critical) | ~120 minutes

🚥 Pre-merge checks | ✅ 4 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 32.26% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (4 passed)
Check name Status Explanation
Title check ✅ Passed The title clearly identifies the main changes: updating to React 19 and rebuilding the compiled output.
Description check ✅ Passed The description explains the compiled-output workflow and links it to the rebuild changes.
Linked Issues check ✅ Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check ✅ Passed Check skipped because no linked issues were found for this pull request.
✨ Finishing Touches 💡 1
📝 Generate docstrings 💡
  • Create stacked PR
  • Commit on current branch
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch MM-69835-5

Warning

There were issues while running some tools. Please review the errors and either fix the tool's configuration or disable the tool if it's a critical failure.

🔧 ast-grep (0.45.0)
amd/react-bootstrap.js

ast-grep timed out on this file

🔧 ESLint

If the error stems from missing dependencies, add them to the package.json file. For unrecoverable errors (e.g., due to private dependencies), disable the tool in the CodeRabbit configuration.

package.json

Parsing error: Unexpected token, expected ";"

1 | {

2 | "name": "react-bootstrap",
| ^
3 | "version": "0.32.4",
4 | "description": "Bootstrap 3 components built with React",
5 | "repository": {


Comment @coderabbitai help to get the list of available commands.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant