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]
