Fix dashboard grids not reacting to window resize (Babel arrow transform on gridstack)

What does this MR do and why?

This MR fixes a production bug. Analytics dashboards do not respond to window resizes after the initial page load. The feature-spec console error is only how we found it.

User-facing impact, on gitlab.com today:

  • Every page that renders GlDashboard from @gitlab/ui is affected: group and project analytics dashboards, the Value Streams Dashboard, explore analytics dashboards, compliance dashboards, contribution analytics, and Duo and SDLC trends.
  • gridstack calls onResize() with no argument from its ResizeObserver. In production that call returns at once and does nothing.
  • So the responsive column breakpoint, which collapses panels to one column below the medium width, and cell height updates never apply after render.
  • Nothing is logged and Sentry receives nothing. The defect is silent.

Root cause:

  • gridstack 12.6.0, a dependency of @gitlab/ui, has onResize(clientWidth = this.el?.clientWidth).
  • webpack compiles gridstack with babel-loader, because the webpack 4 parser cannot read ?..
  • @babel/plugin-transform-optional-chaining rewrites the default parameter into an arrow IIFE that uses this.
  • @babel/plugin-transform-arrow-functions converts that arrow and hoists var _this12 = this; into the method body.
  • Default parameters run in their own scope. They cannot see the method body's var. The parameter expression references an undeclared name.
  • This is a Babel bug. It reproduces with the repo's Babel (core 7.27.1) and with the latest Babel 7 (7.29.7).

Why tests crashed but production did not:

  • In unminified builds (CI test assets, local development) the name is _this12. It is unbound, so the call throws Uncaught ReferenceError: _this12 is not defined. The feature specs surfaced this in 70 examples across 9 analytics dashboard spec files.
  • In the production build Terser renames the alias to g. g is also the webpack module wrapper's exports parameter. The expression reads exports.el, gets undefined, and onResize returns early on its first line. I verified this by parsing the live gitlab.com chunk and resolving the binding.

Fix:

  • Move @babel/plugin-transform-arrow-functions from the global plugin list into an overrides entry with exclude: /node_modules/.
  • The plugin exists only because some of our own code calls new on arrow functions. Our code is still transformed.
  • Third-party code keeps native arrow functions. Every target browser (browserslist baseline widely available) supports them.
  • Side effect: every node_modules file that goes through babel-loader keeps arrow functions. The output is slightly smaller. There is no behaviour change in supported browsers.
  • Only webpack sends node_modules through Babel. rspack uses SWC and does not compile node_modules, so it never had this bug. A comment in the config notes that the override can move back into plugins once rspack replaces webpack.

Notes:

  • A grep found no this?.x default parameters in app/ or ee/app/ JavaScript, so our own code does not hit the same bug.
  • The babel-loader disk cache key does not include the content of babel.config.js. Locally, clear tmp/cache/webpack/babel-loader if the old output persists.
  • The console allowlist spec/support/browser_console_allowlist.yml comes from the still-open MR linked below. Nothing to remove here. When it merges, the _this\d+ is not defined entry can be dropped.

How to verify

  1. Open a group analytics dashboard in a production build. Narrow the window below the medium breakpoint (768px). Before this change the panels stay in several columns. After this change they collapse to one column.

  2. Transform gridstack with the project Babel config and inspect onResize:

    node -e '
    const babel = require("@babel/core");
    const code = babel.transformFileSync("node_modules/gridstack/dist/gridstack.js").code;
    const i = code.indexOf("onResize(clientWidth");
    console.log(code.slice(i, i + 160));
    '

    Before: onResize(clientWidth = function (_this12$el) {...}()) { var _this12 = this;

    After: onResize(clientWidth = (_this$el => ...)()) { with no hoisted _this.

  3. Run two Jest specs that render GlDashboard, which loads gridstack:

    yarn jest spec/frontend/explore/analytics_dashboards/pages/details_spec.js ee/spec/frontend/compliance_dashboard/components/dashboard/compliance_dashboard_spec.js
  4. Optional: run ee/spec/features/groups/analytics/dashboards_spec.rb against a webpack build and confirm that the console shows no _this error.

Screenshots

Group analytics dashboard "GitLab Duo and SDLC trends", rendered by a feature spec against webpack test assets. The browser starts at 1400px wide and is then resized to 600px. Panels show "Something went wrong" because the test environment has no ClickHouse; only the layout matters here.

Before (600px wide) After (600px wide)
before after
  • Before: the resize handler throws ReferenceError: _this12 is not defined. The grid keeps its three-column layout (gs-x values 0, 4, 8) in a 600px viewport.
  • After: no console error. gridstack collapses the grid to one column (every gs-x is 0).

Initial 1400px render, identical in both builds:

wide

MR acceptance checklist

This checklist encourages us to confirm any changes have been analyzed to reduce risks in quality, performance, reliability, security, and maintainability.

References

🤖 Generated with Claude Code

Edited by Miguel Rincon

Merge request reports

Loading
Loading