This is an automated email from the ASF dual-hosted git repository.
hainenber pushed a commit to branch master
in repository https://gitbox.apache.org/repos/asf/superset.git
The following commit(s) were added to refs/heads/master by this push:
new 2e72080a163 chore(deps): bump echarts to 6.1.0 with locale and
containLabel guards (#42315) (#42321)
2e72080a163 is described below
commit 2e72080a16397f636d2d76fe12d293150ab56fcc
Author: Evan Rusackas <[email protected]>
AuthorDate: Thu Jul 23 07:48:11 2026 -0700
chore(deps): bump echarts to 6.1.0 with locale and containLabel guards
(#42315) (#42321)
Co-authored-by: Claude Code <[email protected]>
Co-authored-by: Amin Ghadersohi <[email protected]>
---
superset-frontend/package-lock.json | 16 +--
superset-frontend/package.json | 2 +-
.../plugin-chart-echarts/src/components/Echart.tsx | 79 +--------------
.../src/components/echartsLocale.ts | 108 +++++++++++++++++++++
.../test/components/echartsLocale.test.ts | 82 ++++++++++++++++
5 files changed, 204 insertions(+), 83 deletions(-)
diff --git a/superset-frontend/package-lock.json
b/superset-frontend/package-lock.json
index 633e19f08de..e2441d76d4c 100644
--- a/superset-frontend/package-lock.json
+++ b/superset-frontend/package-lock.json
@@ -91,7 +91,7 @@
"dayjs": "^1.11.21",
"dom-to-image-more": "^3.10.2",
"dom-to-pdf": "^0.3.2",
- "echarts": "^5.6.0",
+ "echarts": "^6.1.0",
"fast-glob": "^3.3.2",
"fs-extra": "^11.3.6",
"fuse.js": "^7.5.0",
@@ -19176,13 +19176,13 @@
}
},
"node_modules/echarts": {
- "version": "5.6.0",
- "resolved": "https://registry.npmjs.org/echarts/-/echarts-5.6.0.tgz",
- "integrity":
"sha512-oTbVTsXfKuEhxftHqL5xprgLoc0k7uScAwtryCgWF6hPYFLRwOUHiFmHGCBKP5NPFNkDVopOieyUqYGH8Fa3kA==",
+ "version": "6.1.0",
+ "resolved": "https://registry.npmjs.org/echarts/-/echarts-6.1.0.tgz",
+ "integrity":
"sha512-q0yaFPggC9FUdsWH4blavRWFmxdrIodbkoKNAjJudAI6CA9gNPxHtV2RcZNEepZVlk4yvBYkOkbk6HIVpIyHZA==",
"license": "Apache-2.0",
"dependencies": {
"tslib": "2.3.0",
- "zrender": "5.6.1"
+ "zrender": "6.1.0"
}
},
"node_modules/echarts/node_modules/tslib": {
@@ -43754,9 +43754,9 @@
}
},
"node_modules/zrender": {
- "version": "5.6.1",
- "resolved": "https://registry.npmjs.org/zrender/-/zrender-5.6.1.tgz",
- "integrity":
"sha512-OFXkDJKcrlx5su2XbzJvj/34Q3m6PvyCZkVPHGYpcCJ52ek4U/ymZyfuV1nKE23AyBJ51E/6Yr0mhZ7xGTO4ag==",
+ "version": "6.1.0",
+ "resolved": "https://registry.npmjs.org/zrender/-/zrender-6.1.0.tgz",
+ "integrity":
"sha512-oEGMDB6pOP2S6OwRR4PdVv610zrjnA3Bh+JnSG12fYJlBKjtNAoEb5fSUoCOOINlH96I2fU38/A2UpRKs67xYQ==",
"license": "BSD-3-Clause",
"dependencies": {
"tslib": "2.3.0"
diff --git a/superset-frontend/package.json b/superset-frontend/package.json
index ea36ce764aa..2abbf32923e 100644
--- a/superset-frontend/package.json
+++ b/superset-frontend/package.json
@@ -176,7 +176,7 @@
"dayjs": "^1.11.21",
"dom-to-image-more": "^3.10.2",
"dom-to-pdf": "^0.3.2",
- "echarts": "^5.6.0",
+ "echarts": "^6.1.0",
"fast-glob": "^3.3.2",
"fs-extra": "^11.3.6",
"fuse.js": "^7.5.0",
diff --git
a/superset-frontend/plugins/plugin-chart-echarts/src/components/Echart.tsx
b/superset-frontend/plugins/plugin-chart-echarts/src/components/Echart.tsx
index 2c5c9efa2e0..4eba1d22a4a 100644
--- a/superset-frontend/plugins/plugin-chart-echarts/src/components/Echart.tsx
+++ b/superset-frontend/plugins/plugin-chart-echarts/src/components/Echart.tsx
@@ -63,7 +63,7 @@ import {
MarkAreaComponent,
MarkLineComponent,
} from 'echarts/components';
-import { LabelLayout } from 'echarts/features';
+import { LabelLayout, LegacyGridContainLabel } from 'echarts/features';
import {
EchartsHandler,
EchartsProps,
@@ -72,6 +72,7 @@ import {
} from '../types';
import { DEFAULT_LOCALE } from '../constants';
import { mergeEchartsThemeOverrides } from '../utils/themeOverrides';
+import { loadLocale } from './echartsLocale';
// Define this interface here to avoid creating a dependency back to
superset-frontend,
// TODO: to move the type to @superset-ui/core
@@ -119,81 +120,11 @@ use([
TitleComponent,
VisualMapComponent,
LabelLayout,
+ // Superset chart options rely on `grid.containLabel`, which echarts 6
+ // ignores (clipping axis labels) unless this legacy feature is registered.
+ LegacyGridContainLabel,
]);
-// Explicit per-locale imports rather than a template-literal dynamic
-// import: a computed import makes bundlers build a "context module" over
-// echarts/i18n, and resolving that directory through the echarts package's
-// `exports` map fails intermittently in webpack incremental builds
-// ("Package path ./i18n is exported ... but no valid target file was
-// found"). A static map is also the only thing that lets bundlers
-// code-split exactly the locales listed here. Keys are Superset locales
-// uppercased (see LANGUAGES in superset/config.py); values point at the
-// echarts bundle, whose naming differs for some locales (Slovenian is
-// langSI, Brazilian Portuguese is langPT-br). Only locales that echarts
-// 5.6.0 ships a bundle for are listed — Greek (langEL) and Latvian
-// (langLV) were added in echarts 6 and must stay out until this
-// dependency is upgraded again. Superset locales absent from this map
-// fall back to English.
-//
-// The "-obj" suffix is required and must not be dropped. echarts ships two
-// UMD builds per locale: langXX.js self-registers under echarts' own key and
-// exports nothing, while langXX-obj.js exports the locale object. Importing
-// the plain langXX.js yields an empty object, and registering that erases the
-// locale's `time` section, so every time-axis label crashes echarts'
-// formatTime with "Cannot read properties of null" — it evaluates
-// `month[u - 1]` eagerly for every template, even "{yyyy}". Registering the
-// object ourselves is also what lets Superset locale keys that differ from
-// echarts' own (SL -> SI, PT_BR -> PT-br) resolve at init time.
-type EChartsLocaleOption = Parameters<typeof registerLocale>[1];
-
-const LOCALE_LOADERS: Record<
- string,
- () => Promise<{ default: EChartsLocaleOption }>
-> = {
- AR: () => import('echarts/i18n/langAR-obj.js'),
- CS: () => import('echarts/i18n/langCS-obj.js'),
- DE: () => import('echarts/i18n/langDE-obj.js'),
- EN: () => import('echarts/i18n/langEN-obj.js'),
- ES: () => import('echarts/i18n/langES-obj.js'),
- FA: () => import('echarts/i18n/langFA-obj.js'),
- FI: () => import('echarts/i18n/langFI-obj.js'),
- FR: () => import('echarts/i18n/langFR-obj.js'),
- HU: () => import('echarts/i18n/langHU-obj.js'),
- IT: () => import('echarts/i18n/langIT-obj.js'),
- JA: () => import('echarts/i18n/langJA-obj.js'),
- KO: () => import('echarts/i18n/langKO-obj.js'),
- NL: () => import('echarts/i18n/langNL-obj.js'),
- PL: () => import('echarts/i18n/langPL-obj.js'),
- RO: () => import('echarts/i18n/langRO-obj.js'),
- PT_BR: () => import('echarts/i18n/langPT-br-obj.js'),
- RU: () => import('echarts/i18n/langRU-obj.js'),
- SL: () => import('echarts/i18n/langSI-obj.js'),
- SV: () => import('echarts/i18n/langSV-obj.js'),
- TH: () => import('echarts/i18n/langTH-obj.js'),
- TR: () => import('echarts/i18n/langTR-obj.js'),
- UK: () => import('echarts/i18n/langUK-obj.js'),
- VI: () => import('echarts/i18n/langVI-obj.js'),
- ZH: () => import('echarts/i18n/langZH-obj.js'),
-};
-
-const loadLocale = async (locale: string) => {
- const loader = LOCALE_LOADERS[locale];
- if (!loader) {
- // Locale not supported in ECharts
- return undefined;
- }
- try {
- const localeObj = (await loader()).default;
- // registerLocale replaces any built-in entry under the same key, so a
- // partial object would leave echarts formatting against missing data.
- // Fall back to the built-in locale instead of registering a broken one.
- return localeObj?.time ? localeObj : undefined;
- } catch {
- return undefined;
- }
-};
-
// Report/thumbnail screenshots use standalone="true" (charts) or 3 (reports);
// live embeds use 1/2 and keep animation. See superset/utils/screenshots.py.
export function isReportScreenshotMode(): boolean {
diff --git
a/superset-frontend/plugins/plugin-chart-echarts/src/components/echartsLocale.ts
b/superset-frontend/plugins/plugin-chart-echarts/src/components/echartsLocale.ts
new file mode 100644
index 00000000000..77f0e751539
--- /dev/null
+++
b/superset-frontend/plugins/plugin-chart-echarts/src/components/echartsLocale.ts
@@ -0,0 +1,108 @@
+/**
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+import type { registerLocale } from 'echarts/core';
+
+export type EChartsLocaleOption = Parameters<typeof registerLocale>[1];
+
+// Import the `lang*-obj.js` locale bundles, NOT the plain `lang*.js` ones.
+// The plain files are side-effect UMD modules: they call
+// `registerLocale` against `require('echarts/lib/echarts')` — a second,
+// separate echarts instance from the treeshaken `echarts/core` the app
+// uses — and export nothing. Under webpack's CommonJS interop that gives
+// `.default === {}`, and registering that empty object under a builtin
+// locale name ('EN'/'ZH') wipes out the builtin definitions: every
+// time-axis chart then crashes in echarts' time formatter reading
+// `time.month` from the empty locale. The `-obj` variants export the
+// locale object itself, with no side effects and no duplicate echarts.
+//
+// Explicit per-locale imports rather than a template-literal dynamic
+// import: a computed import makes bundlers build a "context module" over
+// echarts/i18n, and resolving that directory through the echarts package's
+// `exports` map fails intermittently in webpack incremental builds
+// ("Package path ./i18n is exported ... but no valid target file was
+// found"). A static map is also the only thing that lets bundlers
+// code-split exactly the locales listed here. Keys are Superset locales
+// uppercased (see LANGUAGES in superset/config.py); values point at the
+// echarts bundle, whose naming differs for some locales (Slovenian is
+// langSI, Brazilian Portuguese is langPT-br). Superset locales absent
+// from this map fall back to English.
+export const LOCALE_LOADERS: Record<
+ string,
+ () => Promise<{ default: EChartsLocaleOption }>
+> = {
+ AR: () => import('echarts/i18n/langAR-obj.js'),
+ CS: () => import('echarts/i18n/langCS-obj.js'),
+ DE: () => import('echarts/i18n/langDE-obj.js'),
+ EL: () => import('echarts/i18n/langEL-obj.js'),
+ EN: () => import('echarts/i18n/langEN-obj.js'),
+ ES: () => import('echarts/i18n/langES-obj.js'),
+ FA: () => import('echarts/i18n/langFA-obj.js'),
+ FI: () => import('echarts/i18n/langFI-obj.js'),
+ FR: () => import('echarts/i18n/langFR-obj.js'),
+ HU: () => import('echarts/i18n/langHU-obj.js'),
+ IT: () => import('echarts/i18n/langIT-obj.js'),
+ JA: () => import('echarts/i18n/langJA-obj.js'),
+ KO: () => import('echarts/i18n/langKO-obj.js'),
+ LV: () => import('echarts/i18n/langLV-obj.js'),
+ NL: () => import('echarts/i18n/langNL-obj.js'),
+ PL: () => import('echarts/i18n/langPL-obj.js'),
+ RO: () => import('echarts/i18n/langRO-obj.js'),
+ PT_BR: () => import('echarts/i18n/langPT-br-obj.js'),
+ RU: () => import('echarts/i18n/langRU-obj.js'),
+ SL: () => import('echarts/i18n/langSI-obj.js'),
+ SV: () => import('echarts/i18n/langSV-obj.js'),
+ TH: () => import('echarts/i18n/langTH-obj.js'),
+ TR: () => import('echarts/i18n/langTR-obj.js'),
+ UK: () => import('echarts/i18n/langUK-obj.js'),
+ VI: () => import('echarts/i18n/langVI-obj.js'),
+ ZH: () => import('echarts/i18n/langZH-obj.js'),
+};
+
+/**
+ * Resolve the ECharts locale object for a Superset locale, or undefined
+ * when the locale is unsupported or the bundle yields no usable content.
+ * Returning undefined makes the caller skip `registerLocale`, so echarts
+ * keeps its builtin locale definitions instead of having them overwritten
+ * by an empty object.
+ */
+export const loadLocale = async (
+ locale: string,
+): Promise<EChartsLocaleOption | undefined> => {
+ const loader = LOCALE_LOADERS[locale];
+ if (!loader) {
+ // Locale not supported in ECharts
+ return undefined;
+ }
+ try {
+ const mod = await loader();
+ // Depending on the module system, the locale object is either the
+ // module's default export or the module namespace itself.
+ const localeObj = mod.default ?? (mod as unknown as EChartsLocaleOption);
+ // registerLocale replaces any builtin entry under the same key, and
+ // `time` is the section whose absence crashes every time-axis chart, so
+ // a locale without it (an empty or partial bundle) must not be
+ // registered. Fall back to the builtin locale instead.
+ if (localeObj?.time) {
+ return localeObj;
+ }
+ } catch {
+ // fall through to the builtin locale
+ }
+ return undefined;
+};
diff --git
a/superset-frontend/plugins/plugin-chart-echarts/test/components/echartsLocale.test.ts
b/superset-frontend/plugins/plugin-chart-echarts/test/components/echartsLocale.test.ts
new file mode 100644
index 00000000000..380b9284f3a
--- /dev/null
+++
b/superset-frontend/plugins/plugin-chart-echarts/test/components/echartsLocale.test.ts
@@ -0,0 +1,82 @@
+/**
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+import { init, use, registerLocale } from 'echarts/core';
+import { LineChart } from 'echarts/charts';
+import { SVGRenderer } from 'echarts/renderers';
+import { GridComponent } from 'echarts/components';
+import { LOCALE_LOADERS, loadLocale } from
'../../src/components/echartsLocale';
+
+type LocaleWithTime = {
+ time?: { month?: string[]; dayOfWeek?: string[] };
+};
+
+test('loadLocale returns undefined for locales echarts does not ship', async
() => {
+ expect(await loadLocale('XX')).toBeUndefined();
+});
+
+test.each(Object.keys(LOCALE_LOADERS))(
+ 'locale %s loads a locale object with time names',
+ async localeKey => {
+ const localeObj = (await loadLocale(localeKey)) as LocaleWithTime;
+ // A locale object without content would clobber echarts' builtin
+ // locales when registered; missing `time` names crash every
+ // time-axis chart inside echarts' time formatter (see #42314
+ // discussion — "Cannot read properties of null (reading '0')").
+ expect(localeObj).toBeDefined();
+ expect(localeObj.time?.month).toHaveLength(12);
+ expect(localeObj.time?.dayOfWeek).toHaveLength(7);
+ },
+);
+
+test('registering the loaded EN locale keeps time-axis charts rendering',
async () => {
+ use([SVGRenderer, LineChart, GridComponent]);
+ const localeObj = await loadLocale('EN');
+ expect(localeObj).toBeDefined();
+ registerLocale('EN', localeObj!);
+
+ const chart = init(null, null, {
+ renderer: 'svg',
+ ssr: true,
+ width: 400,
+ height: 300,
+ locale: 'EN',
+ });
+ try {
+ chart.setOption(
+ {
+ xAxis: { type: 'time' },
+ yAxis: { type: 'value' },
+ series: [
+ {
+ type: 'line',
+ data: [
+ [new Date(1965, 0, 1).getTime(), 100],
+ [new Date(1985, 0, 1).getTime(), 200],
+ [new Date(2005, 0, 1).getTime(), 150],
+ ],
+ },
+ ],
+ },
+ { notMerge: true, lazyUpdate: false },
+ );
+ expect(chart.renderToSVGString()).toContain('<svg');
+ } finally {
+ chart.dispose();
+ }
+});