| <!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/draggable.js"></script> | |
| <!-- <script src="ut/lib/canteen.js"></script> --> | |
| <link rel="stylesheet" href="lib/reset.css" /> | |
| </head> | |
| <body> | |
| <style> | |
| </style> | |
| <div style=" | |
| height: 1207px; | |
| margin: 0; | |
| padding: 20px; | |
| background: #eee; | |
| overflow: hidden; | |
| box-sizing: border-box; | |
| " | |
| > | |
| This is a placeholder for y coord matching the visual test due to the visual test migration. | |
| <br> | |
| Scroll down to check the test case below. | |
| <br> | |
| ↓ ↓ ↓ | |
| </div> | |
| <div id="main_self_layout"></div> | |
| <div id="main_dim_data_edge_cases"></div> | |
| <script> | |
| var option; | |
| require([ | |
| 'echarts' | |
| ], function (echarts) { | |
| const layoutList = [ | |
| { | |
| layout: { | |
| left: 10, | |
| bottom: 10, | |
| width: 250, | |
| height: 50, | |
| }, | |
| expect: 'left-bottom', | |
| }, | |
| { | |
| layout: { | |
| right: 10, | |
| bottom: 10, | |
| width: 250, | |
| height: 50, | |
| }, | |
| expect: 'right-bottom' | |
| }, | |
| { | |
| layout: { | |
| }, | |
| expect: 'default place' | |
| }, | |
| ]; | |
| option = { | |
| backgroundColor: '#eee', | |
| matrix: [ | |
| // Layout test (r/l/t/b/w/h) | |
| ...layoutList.map(item => ({ | |
| ...item.layout, | |
| backgroundStyle: { | |
| color: '#ddd' | |
| }, | |
| x: {show: false, data: ['X1']}, | |
| y: {show: false, data: ['Y1']}, | |
| body: { | |
| label: {fontSize: 10}, | |
| data: [{ | |
| coord: [0, 0], | |
| value: `expect matrix on ${item.expect} due to\nl/r/t/b/w/h settings are:\n` | |
| + JSON.stringify(item.layout) | |
| }] | |
| } | |
| })), | |
| // z-order test | |
| ...[{ | |
| matrixLayout: { | |
| top: 10, | |
| left: 10, | |
| width: 300, | |
| height: 50, | |
| }, | |
| borderZ2: undefined, | |
| }, { | |
| matrixLayout: { | |
| top: 'center', | |
| left: 10, | |
| width: 300, | |
| height: 50, | |
| }, | |
| borderZ2: 500, | |
| }].map(({matrixLayout, borderZ2}) => ({ | |
| ...matrixLayout, | |
| backgroundStyle: { | |
| color: 'yellow', | |
| borderColor: 'orange', | |
| borderWidth: 5, | |
| shadowColor: '#111', | |
| shadowBlur: 15, | |
| }, | |
| borderZ2, | |
| x: { | |
| data: ['X1', 'X2', 'X3'], | |
| dividerLineStyle: { | |
| color: 'rgb(0,200,200)', | |
| width: 5, | |
| }, | |
| }, | |
| y: { | |
| data: ['Y1', 'Y2', 'Y3'], | |
| dividerLineStyle: { | |
| color: 'rgb(100,150,150)', | |
| width: 5, | |
| }, | |
| }, | |
| body: { | |
| label: { | |
| fontSize: 10, | |
| }, | |
| itemStyle: { | |
| borderWidth: 1, | |
| borderColor: 'blue' | |
| }, | |
| data: [{ | |
| coord: [0, 0], | |
| itemStyle: { | |
| borderColor: 'red', | |
| borderWidth: 3 | |
| }, | |
| value: 'red border', | |
| }, { | |
| coord: [[1, 2], 0], | |
| itemStyle: { | |
| borderColor: 'green', | |
| borderWidth: 3 | |
| }, | |
| mergeCells: true, | |
| value: 'green border', | |
| }, { | |
| coord: [[1, 2], 2], | |
| mergeCells: true, | |
| label: { | |
| position: 'insideRight' | |
| }, | |
| value: `borderZ2: ${borderZ2}`, | |
| }] | |
| }, | |
| z: 10 | |
| })) | |
| ] | |
| }; | |
| var chart = testHelper.create(echarts, 'main_self_layout', { | |
| height: 200, | |
| title: [ | |
| 'matrix self layout test', | |
| 'z-index test' | |
| ], | |
| option: option, | |
| }); | |
| }); | |
| </script> | |
| <script> | |
| require([ | |
| 'echarts', | |
| ], function (echarts) { | |
| const MatrixClampOption = { | |
| none: 0, | |
| all: 1, | |
| body: 2, | |
| corner: 3, | |
| }; | |
| function makeMatrixOption({x, y, xLevels, yLevels}, {xShow, yShow}) { | |
| return { | |
| top: 90, | |
| bottom: 100, | |
| left: 160, | |
| x: { | |
| show: !!xShow, | |
| data: x, | |
| levels: xLevels ? xLevels : undefined, | |
| itemStyle: {color: '#111'}, | |
| label: {color: '#eee'}, | |
| }, | |
| y: { | |
| show: !!yShow, | |
| data: y, | |
| levels: yLevels ? yLevels : undefined, | |
| itemStyle: {color: '#111'}, | |
| label: {color: '#eee'}, | |
| }, | |
| corner: { | |
| itemStyle: {color: '#333'}, | |
| label: {color: '#eee'}, | |
| } | |
| }; | |
| } | |
| function makeFailedOption(failed) { | |
| return { | |
| id: 'success_or_fail', | |
| type: 'text', | |
| ignore: failed == null, | |
| style: { | |
| text: failed ? 'Case Failed' : 'Case Succeeded', | |
| fontSize: 18, | |
| fill: failed ? 'red' : 'green', | |
| } | |
| } | |
| } | |
| const _matrixXYDataMap = { | |
| test_d: { | |
| desc: 'Different subtree have duplicated text, expect display the original text', | |
| data: { | |
| x: [{value: 'a', children: ['o', 'p', 'q']}, {value: 'b', children: ['o', 'p', 'q']}], | |
| y: [{value: 'M', children: [new Date(), /a/]}] | |
| }, | |
| testList: [{ | |
| testType: 'convertToLayout', | |
| cases: [{ | |
| input: ['o', 'Y1'], | |
| expect: {matrixXYLocatorRange: [[0, 0], [1, 1]]}, | |
| }, { | |
| input: [3, 0], | |
| expect: {matrixXYLocatorRange: [[3, 3], [0, 0]]}, | |
| }, { | |
| input: [7, 0], | |
| expect: {matrixXYLocatorRange: [[3, 5], [0, 0]]}, | |
| }, { | |
| input: [7, 0], | |
| opt: {clamp: MatrixClampOption.body}, | |
| expect: {matrixXYLocatorRange: [[3, 5], [0, 0]]}, | |
| }] | |
| }] | |
| }, | |
| test_a: { | |
| desc: 'matrix.y.data is empty array, expect no error.', | |
| data: { | |
| x: ['a', 'X2', null, 'd'], | |
| y: [], | |
| }, | |
| testList: [{ | |
| testType: 'convertToLayout', | |
| cases: [{ | |
| input: ['X2', null], | |
| expect: {matrixXYLocatorRange: [[1, 1], [NaN, NaN]]}, | |
| }, { | |
| input: [null, 0], | |
| expect: {matrixXYLocatorRange: [[NaN, NaN], [NaN, NaN]]}, | |
| }, { | |
| input: [null, null], | |
| opt: {clamp: MatrixClampOption.body}, | |
| expect: {matrixXYLocatorRange: [[0, 3], [NaN, NaN]]}, | |
| }] | |
| }] | |
| }, | |
| test_a2: { | |
| desc: 'matrix.x.data is empty array, expect no error.', | |
| data: { | |
| x: [], | |
| y: ['a', 'X2', null, 'd'], | |
| }, | |
| }, | |
| test_b: { | |
| desc: '[null] is allowed; number is illegal in matrix.x/y.data and replaced by auto-gen text.', | |
| data: { | |
| x: [null, null], | |
| y: [1, 2, 3], | |
| }, | |
| testList: [{ | |
| testType: 'convertToLayout', | |
| cases: [{ | |
| input: ['X1', 0], | |
| expect: {matrixXYLocatorRange: [[1, 1], [0, 0]]}, | |
| }, { | |
| input: [-1, -1], | |
| expect: {matrixXYLocatorRange: [[-1, -1], [-1, -1]]}, | |
| }, { | |
| input: [null, null], | |
| opt: {clamp: MatrixClampOption.body}, | |
| expect: {matrixXYLocatorRange: [[0, 1], [0, 2]]}, | |
| }, { | |
| input: [null, null], | |
| opt: {clamp: MatrixClampOption.corner}, | |
| expect: {matrixXYLocatorRange: [[-1, -1], [-1, -1]]}, | |
| }, { | |
| input: [0, 0], | |
| opt: {clamp: MatrixClampOption.corner}, | |
| expect: {matrixXYLocatorRange: [[-1, -1], [-1, -1]]}, | |
| }] | |
| }] | |
| }, | |
| test_c: { | |
| desc: 'No matrix.x/y.data, expect no error', | |
| data: { | |
| x: [], | |
| y: [], | |
| }, | |
| testList: [{ | |
| testType: 'convertToLayout', | |
| cases: [{ | |
| input: [0, 0], | |
| expect: {matrixXYLocatorRange: [[NaN, NaN], [NaN, NaN]]}, | |
| }, { | |
| input: [null, null], | |
| opt: {clamp: MatrixClampOption.body}, | |
| expect: {matrixXYLocatorRange: [[NaN, NaN], [NaN, NaN]]}, | |
| }] | |
| }] | |
| }, | |
| test_e: { | |
| desc: 'All col/row width specified but not touch or overflow the bounary', | |
| data: { | |
| x: [{value: 'a1', size: 30}, {value: 'a2', size: 30}], | |
| y: [{value: 'b1', size: 300}, {value: 'b2', size: 300}], | |
| yLevels: [{levelSize: 50}] | |
| }, | |
| }, | |
| test_f: { | |
| desc: 'collect all from series.data (has null/NaN/undefined/illegal, expect display)', | |
| data: { | |
| x: undefined, | |
| y: undefined, | |
| seriesData: [ | |
| ['fruit', undefined, 1223], | |
| ['bread', 'good', 323], | |
| ['milk', 'good', 142], | |
| [null, 'medium', 63], | |
| ['bread', 'medium', 91], | |
| ['milk', 9999, 45], | |
| ['fruit', NaN, 55], | |
| ['bread', 'bad', 15], | |
| ['milk', 'bad', 53], | |
| ], | |
| }, | |
| }, | |
| test_g: { | |
| desc: 'no series.data and no matrix.x/y', | |
| data: { | |
| x: undefined, | |
| y: undefined, | |
| seriesData: [], | |
| }, | |
| }, | |
| }; | |
| let _ctx = { | |
| xyData: 'test_d', | |
| testResult: null, // {result: unknown, failed: boolean} | |
| xShow: true, | |
| yShow: true, | |
| convertFromPixelOpt: null, | |
| }; | |
| function updateView() { | |
| const option = { | |
| matrix: makeMatrixOption(_matrixXYDataMap[_ctx.xyData].data, _ctx), | |
| series: { | |
| data: _matrixXYDataMap[_ctx.xyData].data.seriesData | |
| }, | |
| }; | |
| chart.setOption(option, {replaceMerge: 'matrix'}); | |
| chart.setOption({ | |
| graphic: { | |
| elements: [{ | |
| id: 'convert_result', | |
| style: { | |
| text: _ctx.testResult ? _ctx.testResult.result : '', | |
| } | |
| }, { | |
| id: 'desc_text', | |
| style: { | |
| text: '--- expect ---\n' + (_matrixXYDataMap[_ctx.xyData].desc || '') | |
| + '\n--- current matrix data ---\n' | |
| + testHelper.printObject( | |
| { | |
| x: { | |
| data: _matrixXYDataMap[_ctx.xyData].data.x, | |
| levels: _matrixXYDataMap[_ctx.xyData].data.xLevels, | |
| }, | |
| y: { | |
| data: _matrixXYDataMap[_ctx.xyData].data.y, | |
| levels: _matrixXYDataMap[_ctx.xyData].data.yLevels, | |
| }, | |
| }, | |
| {lineBreakMaxColumn: 150} | |
| ) | |
| + '\n--- current series data ---\n' + ( | |
| _matrixXYDataMap[_ctx.xyData].data.seriesData | |
| ? testHelper.printObject( | |
| _matrixXYDataMap[_ctx.xyData].data.seriesData, | |
| {lineBreakMaxColumn: 30} | |
| ) | |
| : 'undefined' | |
| ) | |
| } | |
| }, | |
| makeFailedOption(_ctx.testResult ? !!_ctx.testResult.failed : undefined) | |
| ] | |
| } | |
| }); | |
| } | |
| const option = { | |
| tooltip: {}, | |
| backgroundColor: '#aee', | |
| matrix: makeMatrixOption(_matrixXYDataMap[_ctx.xyData].data, _ctx), | |
| series: { | |
| type: 'scatter', | |
| coordinateSystem: 'matrix', | |
| symbolSize: 20, | |
| encode: {label: 2}, | |
| label: {show: true}, | |
| data: _matrixXYDataMap[_ctx.xyData].data.seriesData | |
| }, | |
| graphic: { | |
| elements: [{ | |
| bottom: 5, | |
| left: 'center', | |
| id: 'convert_result', | |
| type: 'text', | |
| style: { | |
| text: '', | |
| fill: 'blue', | |
| fontSize: 11, | |
| }, | |
| silent: true | |
| }, { | |
| left: 5, | |
| top: 5, | |
| id: 'desc_text', | |
| type: 'text', | |
| style: { | |
| fontSize: 12, | |
| fill: '#333', | |
| }, | |
| silent: true | |
| }, { | |
| right: 5, | |
| top: 5, | |
| id: 'success_or_fail', | |
| type: 'text', | |
| style: { | |
| fontSize: 20, | |
| fill: 'red', | |
| }, | |
| silent: true | |
| }] | |
| } | |
| }; | |
| var chart = testHelper.create(echarts, 'main_dim_data_edge_cases', { | |
| title: [ | |
| 'edge case', | |
| '**click** on matrix inside and outside to test clamp.' | |
| ], | |
| option: option, | |
| height: 400, | |
| inputsStyle: 'compact', | |
| inputs: [{ | |
| text: 'test cases:', | |
| type: 'select', | |
| options: Object.keys(_matrixXYDataMap).map(key => { | |
| let text = _matrixXYDataMap[key].desc; | |
| const MAX_STR = 100; | |
| if (text.length > MAX_STR) { | |
| text = text.slice(0, MAX_STR) + ' ...'; | |
| } | |
| return {text: text, value: key}; | |
| }), | |
| onchange() { | |
| const key = this.value; | |
| _ctx.xyData = key; | |
| _ctx.testResult = null; | |
| chart.__testHelper.switchGroup(key); | |
| updateView(); | |
| } | |
| }, { | |
| type: 'br', | |
| }, { | |
| text: 'matrix.x show:', | |
| type: 'select', | |
| values: [true, false], | |
| onchange() { | |
| _ctx.xShow = this.value; | |
| updateView(); | |
| } | |
| }, { | |
| text: 'matrix.y show:', | |
| type: 'select', | |
| values: [true, false], | |
| onchange() { | |
| _ctx.yShow = this.value; | |
| updateView(); | |
| } | |
| }, { | |
| text: 'convertFromPixel opt:', | |
| type: 'select', | |
| options: [ | |
| {value: undefined}, | |
| {text: 'clamp body', value: {clamp: MatrixClampOption.body}}, | |
| {text: 'clamp corner', value: {clamp: MatrixClampOption.corner}}, | |
| {text: 'clamp all', value: {clamp: MatrixClampOption.all}}, | |
| {text: 'clamp none', value: {clamp: MatrixClampOption.none}}, | |
| ], | |
| onchange() { | |
| _ctx.convertFromPixelOpt = this.value; | |
| } | |
| }, { | |
| type: 'groupset', | |
| inputsStyle: 'compact', | |
| inputsHeight: 70, | |
| groups: Object.keys(_matrixXYDataMap).map(key => { | |
| const textList = _matrixXYDataMap[key].testList || []; | |
| const inputs = []; | |
| textList.forEach(testItem => { | |
| if (testItem.testType === 'convertToLayout') { | |
| const cases = testItem.cases; | |
| cases.forEach(caseItem => { | |
| inputs.push({ | |
| text: `convertToLayout(${testHelper.printObject(caseItem.input)}, ${testHelper.printObject(caseItem.opt)})`, | |
| onclick() { | |
| let expect = caseItem.expect; | |
| let failed = false; | |
| let actual; | |
| try { | |
| actual = chart.convertToLayout({matrixIndex: 0}, caseItem.input, caseItem.opt); | |
| console.log(actual); | |
| if (!numEq(expect.matrixXYLocatorRange[0][0], actual.matrixXYLocatorRange[0][0]) | |
| || !numEq(expect.matrixXYLocatorRange[0][1], actual.matrixXYLocatorRange[0][1]) | |
| || !numEq(expect.matrixXYLocatorRange[1][0], actual.matrixXYLocatorRange[1][0]) | |
| || !numEq(expect.matrixXYLocatorRange[1][1], actual.matrixXYLocatorRange[1][1]) | |
| ) { | |
| failed = true; | |
| } | |
| } | |
| catch (err) { | |
| console.error(err); | |
| failed = true; | |
| } | |
| const result = 'convertToLayout result: ' + testHelper.printObject(actual); | |
| _ctx.testResult = {result, failed}; | |
| updateView(); | |
| } | |
| }); | |
| }); | |
| } | |
| else { | |
| throw new Error('illegal'); | |
| } | |
| }); | |
| return { | |
| id: key, | |
| text: '[' + key + '] test cases:', | |
| inputs, | |
| }; | |
| }) | |
| }] // End of inputs | |
| }); | |
| if (chart) { | |
| updateView(); | |
| chart.getZr().on('click', function (event) { | |
| const point = [event.offsetX, event.offsetY]; | |
| const data = chart.convertFromPixel({matrixIndex: 0}, point, _ctx.convertFromPixelOpt); | |
| const layout = chart.convertToLayout({matrixIndex: 0}, data); | |
| _ctx.testResult = { | |
| result: 'convertFromPixel result: ' + testHelper.printObject(data) + '\n' | |
| + 'Then use it convertToLayout result: ' + testHelper.printObject(layout), | |
| failed: false | |
| }; | |
| updateView(); | |
| }); | |
| } | |
| }); | |
| function eqNaN(value) { | |
| return value !== value; | |
| } | |
| function numEq(expect, actual) { | |
| return eqNaN(expect) | |
| ? eqNaN(actual) | |
| : expect === actual; | |
| } | |
| </script> | |
| </body> | |
| </html> | |