This is an automated email from the ASF dual-hosted git repository.

100pah pushed a commit to branch release-dev
in repository https://gitbox.apache.org/repos/asf/echarts.git

commit be4cc54ae18ee1464d61a4eb2f69f54f06f8013c
Author: 100pah <[email protected]>
AuthorDate: Sun May 10 01:40:59 2026 +0800

    fix regression: Fix appendData broken by previous commits.
---
 src/chart/lines/LinesSeries.ts        |   9 ++
 src/coord/axisStatistics.ts           |   7 +-
 test/appendData2.html                 | 218 +++++++++++++++++++++++++++++++
 test/appendData3.html                 | 236 ++++++++++++++++++++++++++++++++++
 test/lib/reset.css                    |   3 +
 test/lib/testHelper.js                |  75 +++++++++--
 test/runTest/actions/__meta__.json    |   2 +
 test/runTest/actions/appendData2.json |   1 +
 test/runTest/actions/appendData3.json |   1 +
 9 files changed, 542 insertions(+), 10 deletions(-)

diff --git a/src/chart/lines/LinesSeries.ts b/src/chart/lines/LinesSeries.ts
index bb35b6fdf..0a8f673e5 100644
--- a/src/chart/lines/LinesSeries.ts
+++ b/src/chart/lines/LinesSeries.ts
@@ -352,6 +352,15 @@ class LinesSeriesModel extends 
SeriesModel<LinesSeriesOption> {
         return createTooltipMarkup('nameValue', {
             name: itemName,
             value,
+            // NOTE: here `value` may be `coords` (an 2D-array) if ec option 
is like
+            //  series: {
+            //      type: 'lines',
+            //      data: [
+            //          [[2.122232, 100.1133], [5.122232, 104.1133]],
+            //          [[22.122232, 0.1133], [25.122232, 4.1133]],
+            //      ]
+            //  }
+            // Do not display `value` in that case.
             noValue: value == null || isNaN(value as number)
         });
     }
diff --git a/src/coord/axisStatistics.ts b/src/coord/axisStatistics.ts
index f45c463ed..3011d6593 100644
--- a/src/coord/axisStatistics.ts
+++ b/src/coord/axisStatistics.ts
@@ -484,8 +484,11 @@ export function requireAxisStatistics(
 
     callOnlyOnce(registers, function () {
         
registers.registerProcessor(registers.PRIORITY.PROCESSOR.AXIS_STATISTICS, {
-            // Theoretically, `appendData` requires to re-calculate them.
-            dirtyOnOverallProgress: true,
+            // NOTE: Theoretically, `appendData` requires 
`dirtyOnOverallProgress: true` here to re-calculate them.
+            // But this OVERALL_STAGE_TASK is applied to all series (no 
`getTargetSeries` specified),
+            // `dirtyOnOverallProgress: true` can cause irrelevant series 
(e.g., series on geo)
+            // to be re-rendered when `appendData` is called, which cause 
`appendData` meaningless,
+            // thereby not setting `dirtyOnOverallProgress: true`.
             overallReset: performAxisStatisticsOnOverallReset
         });
     });
diff --git a/test/appendData2.html b/test/appendData2.html
new file mode 100644
index 000000000..6788c5d5e
--- /dev/null
+++ b/test/appendData2.html
@@ -0,0 +1,218 @@
+<!DOCTYPE html>
+<!--
+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.
+-->
+
+
+<html>
+    <head>
+        <meta charset="utf-8">
+        <meta name="viewport" content="width=device-width, initial-scale=1" />
+        <script src="lib/simpleRequire.js"></script>
+        <script src="lib/config.js"></script>
+        <script src="lib/jquery.min.js"></script>
+        <script src="lib/facePrint.js"></script>
+        <script src="lib/testHelper.js"></script>
+        <script src="lib/canteen.js"></script>
+        <link rel="stylesheet" href="lib/reset.css" />
+    </head>
+    <body>
+
+        <div id="main0"></div>
+        <div id="record"></div>
+
+        <script>
+
+        var chart;
+        var recordContainer = document.getElementById('record');
+
+        testHelper.controlFrame({
+            pauseAt: 1,
+            onFrame: function (frameNumber) {
+                testHelper.printCanvasLayerDrawOperationsOnFrame(chart, 
frameNumber, recordContainer);
+            }
+        });
+
+        require([
+            'echarts',
+        ], function (echarts) {
+
+            const _ctx = {
+                useDataZoom: {
+                    value: false,
+                    values: [false, true],
+                },
+                hoverLayerThreshold: {
+                    value: undefined,
+                    values: [undefined, 300, 1000, 0],
+                },
+                br0: {},
+                large: {
+                    text: 'series.large:',
+                    value: false,
+                    values: [true, false],
+                },
+                largeThreshold: {
+                    text: 'series.largeThreshold:',
+                    value: undefined,
+                    values: [undefined, 1, 1000],
+                },
+                br1: {},
+                progressive: {
+                    text: 'series.progressive:',
+                    value: 1,
+                    values: [1, 1e4],
+                },
+                progressiveThreshold: {
+                    text: 'series.progressiveThreshold:',
+                    value: 1,
+                    values: [undefined, 1, 1000],
+                },
+                br2: {},
+                progressiveChunkMode: {
+                    text: 'series.progressiveChunkMode:',
+                    value: undefined,
+                    values: [undefined, 'sequential', 'mod']
+                }
+            };
+
+            var _iBatchCount;
+            var _initXMaxTimes;
+            var _xMax;
+            var _xColLen;
+            var _yCurr;
+            var _currI;
+
+            resetMeta();
+
+            function resetMeta() {
+                _iBatchCount = 1e4;
+                _initXMaxTimes = 5;
+                _xMax = _iBatchCount * _initXMaxTimes;
+                _xColLen = 1e2;
+                _yCurr = 100;
+                _currI = 0;
+            }
+
+            function createSingleBatchData() {
+                var seriesData = [];
+                for (var len = _currI + _iBatchCount; _currI < len; _currI++) {
+                    seriesData.push([_currI, _yCurr * (_currI % _xColLen)]);
+                }
+                return seriesData;
+            }
+
+            function createOption() {
+
+                var largeSettingsSeries = {};
+                if (_ctx.large.value != null) {
+                    largeSettingsSeries.large = _ctx.large.value;
+                }
+                if (_ctx.largeThreshold.value != null) {
+                    largeSettingsSeries.largeThreshold = 
_ctx.largeThreshold.value;
+                }
+                if (_ctx.progressive.value != null) {
+                    largeSettingsSeries.progressive = _ctx.progressive.value;
+                }
+                if (_ctx.progressiveThreshold.value != null) {
+                    largeSettingsSeries.progressiveThreshold = 
_ctx.progressiveThreshold.value;
+                }
+                if (_ctx.progressiveChunkMode.value != null) {
+                    largeSettingsSeries.progressiveChunkMode = 
_ctx.progressiveChunkMode.value;
+                }
+
+                if (_ctx.hoverLayerThreshold.value != null) {
+                    largeSettingsSeries.hoverLayerThreshold = 
_ctx.hoverLayerThreshold.value;
+                }
+
+                var initData = createSingleBatchData();
+
+                var option = {
+                    animation: false,
+                    tooltip: {},
+                    grid: {
+                        top: 20,
+                        bottom: 100,
+                        left: 10,
+                        right: 10,
+                    },
+                    xAxis: {
+                        max: _xMax
+                    },
+                    yAxis: {},
+                    series: [{
+                        type: 'scatter',
+                        ...largeSettingsSeries,
+                        // name: 'data',
+                        data: initData,
+                        symbol: 'circle',
+                        symbolSize: 6,
+                        itemStyle: {
+                            borderColor: '#111',
+                            borderWidth: 1
+                        }
+                    }]
+                };
+
+                if (_ctx.useDataZoom.value) {
+                    option.dataZoom = [
+                        {type: 'slider'},
+                        {type: 'inside'}
+                    ]
+                }
+
+                return option;
+            }
+
+            function updateChart() {
+                resetMeta();
+                chart.setOption(createOption(), {notMerge: true});
+            }
+
+            chart = testHelper.create(echarts, 'main0', {
+                title: [
+                    'Cartesian appendData',
+                    'Test: Click btn appendData **before** progressive 
rendering finished;',
+                    'Test: Click btn appendData **after** the init data 
rendered.',
+                    'Expect: rendered part should be **retained** if no 
dataZoom, otherwise discarded (PENDING).',
+                ],
+                option: createOption(),
+                height: 350,
+                // recordCanvas: true
+                inputsStyle: 'compact',
+                inputs: [
+                    {
+                        type: 'button',
+                        text: 'appendData',
+                        onclick() {
+                            var newData = createSingleBatchData();
+                            chart.appendData({
+                                seriesIndex: 0,
+                                data: newData
+                            });
+                        }
+                    },
+                    ...testHelper.createInputsSimply(_ctx, updateChart)
+                ]
+            });
+        });
+        </script>
+
+
+    </body>
+</html>
diff --git a/test/appendData3.html b/test/appendData3.html
new file mode 100644
index 000000000..372e1c0e0
--- /dev/null
+++ b/test/appendData3.html
@@ -0,0 +1,236 @@
+<!DOCTYPE html>
+<!--
+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.
+-->
+
+
+<html>
+    <head>
+        <meta charset="utf-8">
+        <meta name="viewport" content="width=device-width, initial-scale=1" />
+        <script src="lib/simpleRequire.js"></script>
+        <script src="lib/config.js"></script>
+        <script src="lib/jquery.min.js"></script>
+        <script src="lib/facePrint.js"></script>
+        <script src="lib/testHelper.js"></script>
+        <script src="lib/canteen.js"></script>
+        <link rel="stylesheet" href="lib/reset.css" />
+    </head>
+    <body>
+
+        <div id="main0"></div>
+        <div id="record"></div>
+
+        <script>
+
+        var chart;
+        var recordContainer = document.getElementById('record');
+
+        testHelper.controlFrame({
+            pauseAt: 1,
+            onFrame: function (frameNumber) {
+                testHelper.printCanvasLayerDrawOperationsOnFrame(chart, 
frameNumber, recordContainer);
+            }
+        });
+
+        require([
+            'echarts',
+            'map/js/world'
+        ], function (echarts) {
+
+            const _ctx = {
+                seriesType: {
+                    value: 'lines',
+                    values: ['lines', 'scatter']
+                },
+                hoverLayerThreshold: {
+                    value: undefined,
+                    values: [undefined, 300, 1000, 0],
+                },
+                br0: {},
+                large: {
+                    text: 'series.large:',
+                    value: false,
+                    values: [true, false],
+                },
+                largeThreshold: {
+                    text: 'series.largeThreshold:',
+                    value: undefined,
+                    values: [undefined, 1, 1000],
+                },
+                br1: {},
+                progressive: {
+                    text: 'series.progressive:',
+                    value: 1,
+                    values: [1, 1e2],
+                },
+                progressiveThreshold: {
+                    text: 'series.progressiveThreshold:',
+                    value: 1,
+                    values: [undefined, 1, 1000],
+                },
+                br2: {},
+                progressiveChunkMode: {
+                    text: 'series.progressiveChunkMode:',
+                    value: undefined,
+                    values: [undefined, 'sequential', 'mod']
+                }
+            };
+
+            var _iBatchCount;
+            var _initXMaxTimes;
+            var _xMax;
+            var _xColLen;
+            var _currI;
+            var _lng0;
+            var _lat0;
+            var _lngStep;
+            var _latStep;
+
+            resetMeta();
+
+            function resetMeta() {
+                _iBatchCount = 1e3;
+                _initXMaxTimes = 5;
+                _xMax = _iBatchCount * _initXMaxTimes;
+                _xColLen = 1e2;
+                _currI = 0;
+                _lng0 = -250;
+                _lat0 = -80;
+                _lngStep = 0.1;
+                _latStep = 2;
+            }
+
+            function createSingleBatchData() {
+                var seriesData = [];
+                for (var len = _currI + _iBatchCount; _currI < len; _currI++) {
+                    if (_ctx.seriesType.value === 'lines') {
+                        seriesData.push([
+                            [_lng0 + _currI * _lngStep, _lat0 + _latStep * 
(_currI % _xColLen)],
+                            [_lng0 + _currI * _lngStep + 5, _lat0 + _latStep * 
(_currI % _xColLen) + 3],
+                        ]);
+                    }
+                    else if (_ctx.seriesType.value === 'scatter') {
+                        seriesData.push(
+                            [_lng0 + _currI * _lngStep, _lat0 + _latStep * 
(_currI % _xColLen)],
+                        );
+                    }
+                }
+                return seriesData;
+            }
+
+            function createOption() {
+
+                var largeSettingsSeries = {};
+                if (_ctx.large.value != null) {
+                    largeSettingsSeries.large = _ctx.large.value;
+                }
+                if (_ctx.largeThreshold.value != null) {
+                    largeSettingsSeries.largeThreshold = 
_ctx.largeThreshold.value;
+                }
+                if (_ctx.progressive.value != null) {
+                    largeSettingsSeries.progressive = _ctx.progressive.value;
+                }
+                if (_ctx.progressiveThreshold.value != null) {
+                    largeSettingsSeries.progressiveThreshold = 
_ctx.progressiveThreshold.value;
+                }
+                if (_ctx.progressiveChunkMode.value != null) {
+                    largeSettingsSeries.progressiveChunkMode = 
_ctx.progressiveChunkMode.value;
+                }
+
+                if (_ctx.hoverLayerThreshold.value != null) {
+                    largeSettingsSeries.hoverLayerThreshold = 
_ctx.hoverLayerThreshold.value;
+                }
+
+                var initData = createSingleBatchData();
+
+                var option = {
+                    animation: false,
+                    tooltip: {},
+                    geo: {
+                        map: 'world',
+                        left: 0,
+                        right: 0,
+                        zoom: 0.5,
+                        roam: true,
+                        silent: true,
+                        itemStyle: {
+                            borderColor: '#aaa',
+                            color: '#eee'
+                        }
+                    },
+                    series: [{
+                        type: _ctx.seriesType.value,
+                        ...largeSettingsSeries,
+                        coordinateSystem: 'geo',
+                        data: initData,
+                        ...(function () {
+                            return _ctx.seriesType.value === 'scatter'
+                                ? {
+                                    symbol: 'circle',
+                                    symbolSize: 6,
+                                }
+                                : {}
+                        }),
+                        itemStyle: {
+                            borderColor: '#111',
+                            borderWidth: 1
+                        }
+                    }]
+                };
+
+                return option;
+            }
+
+            function updateChart() {
+                resetMeta();
+                chart.setOption(createOption(), {notMerge: true});
+            }
+
+            chart = testHelper.create(echarts, 'main0', {
+                title: [
+                    'geo appendData',
+                    'Test: Click btn appendData **before** progressive 
rendering finished;',
+                    'Test: Click btn appendData **after** the init data 
rendered.',
+                    'Expect: rendered part should be **retained**.',
+                ],
+                option: createOption(),
+                height: 350,
+                // recordCanvas: true
+                inputsStyle: 'compact',
+                inputs: [
+                    {
+                        type: 'button',
+                        text: 'appendData',
+                        onclick() {
+                            var newData = createSingleBatchData();
+                            chart.appendData({
+                                seriesIndex: 0,
+                                data: newData
+                            });
+                        }
+                    },
+                    ...testHelper.createInputsSimply(_ctx, updateChart)
+                ]
+            });
+        });
+        </script>
+
+
+    </body>
+</html>
diff --git a/test/lib/reset.css b/test/lib/reset.css
index dda25620a..2c74e17f1 100644
--- a/test/lib/reset.css
+++ b/test/lib/reset.css
@@ -384,6 +384,9 @@ td.test-data-table-key {
     padding: 5px;
     z-index: 99999;
 }
+.control-frame-btn-panel-avoid-occlusion {
+    opacity: 0.2;
+}
 .control-frame-btn-panel .control-frame-info {
     display: block;
     color: #fff;
diff --git a/test/lib/testHelper.js b/test/lib/testHelper.js
index 54e17bb63..43d531558 100644
--- a/test/lib/testHelper.js
+++ b/test/lib/testHelper.js
@@ -296,12 +296,6 @@
         var recordVideoContainer = document.createElement('div');
         var boundingRectsContainer = document.createElement('div');
 
-        titleContainer.setAttribute('title', dom.getAttribute('id'));
-        titleContainer.addEventListener('click', function () {
-            // Convenient for locating test case code.
-            console.log('[TEST CASE DOM ID]: ' + dom.getAttribute('id'));
-        });
-
         titleContainer.className = 'test-title';
         dom.className = 'test-chart-block';
         left.className = 'test-chart-block-left';
@@ -335,7 +329,7 @@
         dom.appendChild(left);
         dom.parentNode.insertBefore(titleContainer, dom);
 
-        initTestTitle(opt, titleContainer);
+        initTestTitle(opt, titleContainer, dom);
 
         var chart = testHelper.createChart(echarts, chartContainer, 
opt.option, opt, opt.setOptionOpts, errMsgPrefix);
 
@@ -359,7 +353,7 @@
         return chart;
     };
 
-    function initTestTitle(opt, titleContainer) {
+    function initTestTitle(opt, titleContainer, dom) {
         var optTitle = opt.title;
         if (optTitle) {
             if (optTitle instanceof Array) {
@@ -371,6 +365,30 @@
                     .replace(/\n/g, '<br>')
                 + '</div>';
         }
+
+        titleContainer.setAttribute('title', dom.getAttribute('id'));
+        titleContainer.addEventListener('click', function () {
+            // Convenient for locating test case code.
+            console.log('[TEST CASE DOM ID]: ' + dom.getAttribute('id'));
+        });
+
+        var hovering = false;
+        titleContainer.addEventListener('mouseover', function () {
+            if (!hovering) {
+                hovering = true;
+                if (_controlFrameSingleton) {
+                    cssAddClass(_controlFrameSingleton.btnPanel, 
'control-frame-btn-panel-avoid-occlusion');
+                }
+            }
+        });
+        titleContainer.addEventListener('mouseout', function (event) {
+            if (hovering) {
+                hovering = false;
+                if (_controlFrameSingleton) {
+                    cssRemoveClass(_controlFrameSingleton.btnPanel, 
'control-frame-btn-panel-avoid-occlusion');
+                }
+            }
+        });
     }
 
     function initUpdateInfo(opt, chart, infoContainer) {
@@ -2060,6 +2078,7 @@
 
 
     var _dummyRequestAnimationFrameMounted = false;
+    var _controlFrameSingleton = null;
 
     /**
      * Usage:
@@ -2073,6 +2092,11 @@
      * @param {Function} [opt.onFrame]
      */
     testHelper.controlFrame = function (opt) {
+        if (_controlFrameSingleton) {
+            throw new Error('`testHelper.controlFrame` can only be called 
once.');
+        }
+        _controlFrameSingleton = {};
+
         opt = opt || {};
         var pauseAt = opt.pauseAt;
         pauseAt == null && (pauseAt = 0);
@@ -2111,6 +2135,7 @@
             btnEl.addEventListener('click', button.onclick);
             btnPanel.appendChild(btnEl);
         }
+        _controlFrameSingleton.btnPanel = btnPanel;
 
         if (_dummyRequestAnimationFrameMounted) {
             throw new Error('Do not support `controlFrame` twice');
@@ -2475,6 +2500,40 @@
             : null;
     };
 
+    function containsDOMElement(parent, child, includeSelf) {
+        if (!includeSelf && child) {
+            child = child.parentNode;
+        }
+        while (child) {
+            if (child === parent) {
+                return true;
+            }
+            child = child.parentNode;
+        }
+        return false;
+    }
+
+    function cssHasClass(el, className) {
+        return cssNormalizeClass(el.className).indexOf(className) >= 0;
+    }
+
+    function cssAddClass(el, className) {
+        if (!cssHasClass(el, className)) {
+            el.className = cssNormalizeClass(el.className + ' ' + className);
+        }
+    }
+
+    function cssRemoveClass(el, className) {
+        var classes = (' ' + el.className + ' ').replace(' ' + className + ' 
', function () {
+            return ' '; // Use a cb to String.prototype.replace to avoid `$` 
issue.
+        });
+        el.className = cssNormalizeClass(classes);
+    }
+
+    function cssNormalizeClass(classes) {
+        return classes.replace(/^\s+|\s+$/g, '').replace(/\s+/g, ' ');
+    }
+
     /**
      * JSON.stringify(obj, null, 2) will vertically layout array, which takes 
too much space.
      * Can print like:
diff --git a/test/runTest/actions/__meta__.json 
b/test/runTest/actions/__meta__.json
index 915b7f8c1..c0dc3b70d 100644
--- a/test/runTest/actions/__meta__.json
+++ b/test/runTest/actions/__meta__.json
@@ -1,6 +1,8 @@
 {
   "allZero": 1,
   "appendData": 4,
+  "appendData2": 1,
+  "appendData3": 1,
   "area-large": 2,
   "area-stack": 2,
   "area2": 1,
diff --git a/test/runTest/actions/appendData2.json 
b/test/runTest/actions/appendData2.json
new file mode 100644
index 000000000..11560a520
--- /dev/null
+++ b/test/runTest/actions/appendData2.json
@@ -0,0 +1 @@
+[{"name":"Action 
1","ops":[{"type":"mousemove","time":538,"x":391,"y":90},{"type":"mousemove","time":737,"x":247,"y":88},{"type":"mousemove","time":938,"x":120,"y":61},{"type":"mousemove","time":1144,"x":84,"y":45},{"type":"mousemove","time":1354,"x":64,"y":43},{"type":"mousedown","time":1504,"x":60,"y":40},{"type":"mousemove","time":1560,"x":60,"y":40},{"type":"mouseup","time":1627,"x":60,"y":40},{"time":1628,"delay":400,"type":"screenshot-auto"},{"type":"mousemove","time":2120,"x":60,"
 [...]
\ No newline at end of file
diff --git a/test/runTest/actions/appendData3.json 
b/test/runTest/actions/appendData3.json
new file mode 100644
index 000000000..d26d7a50c
--- /dev/null
+++ b/test/runTest/actions/appendData3.json
@@ -0,0 +1 @@
+[{"name":"Action 
1","ops":[{"type":"mousemove","time":387,"x":242,"y":170},{"type":"mousemove","time":586,"x":111,"y":98},{"type":"mousemove","time":786,"x":49,"y":61},{"type":"mousemove","time":986,"x":51,"y":46},{"type":"mousemove","time":1192,"x":52,"y":40},{"type":"mousedown","time":1293,"x":52,"y":40},{"type":"mouseup","time":1435,"x":52,"y":40},{"time":1436,"delay":400,"type":"screenshot-auto"},{"type":"mousemove","time":1753,"x":52,"y":41},{"type":"mousemove","time":1962,"x":53,"y
 [...]
\ No newline at end of file


---------------------------------------------------------------------
To unsubscribe, e-mail: [email protected]
For additional commands, e-mail: [email protected]

Reply via email to