From 4cd486bad9cc78e027a041043b9f629a4de2a4ce Mon Sep 17 00:00:00 2001 From: interstellarmt <787982239@qq.com> Date: Sun, 20 Sep 2026 14:47:24 +0800 Subject: [PATCH 1/8] fix(tooltip): prevent XSS in default HTML rendering --- __tests__/unit/util/string.spec.ts | 12 +++++++++++- src/ui/tooltip/index.ts | 28 ++++++++++++++++++++++++---- src/util/string.ts | 12 ++++++++++++ 3 files changed, 47 insertions(+), 5 deletions(-) diff --git a/__tests__/unit/util/string.spec.ts b/__tests__/unit/util/string.spec.ts index b47282731..0b0409503 100644 --- a/__tests__/unit/util/string.spec.ts +++ b/__tests__/unit/util/string.spec.ts @@ -1,6 +1,16 @@ -import { toUppercaseFirstLetter, toLowercaseFirstLetter, addPrefix, removePrefix } from '../../../src/util/string'; +import { + escapeHtml, + toUppercaseFirstLetter, + toLowercaseFirstLetter, + addPrefix, + removePrefix, +} from '../../../src/util/string'; describe('string', () => { + it('escapeHtml', () => { + expect(escapeHtml(``)).toBe('<img title="'&">'); + }); + it('toUppercaseFirstLetter', () => { expect(toUppercaseFirstLetter('hello')).toBe('Hello'); expect(toUppercaseFirstLetter('')).toBe(''); diff --git a/src/ui/tooltip/index.ts b/src/ui/tooltip/index.ts index d6ab1d634..727ac53cc 100644 --- a/src/ui/tooltip/index.ts +++ b/src/ui/tooltip/index.ts @@ -1,12 +1,32 @@ import { substitute, createDOM } from '@antv/util'; import { Component } from '../../core'; import { Group } from '../../shapes'; -import { BBox, applyStyleSheet, replaceChildren } from '../../util'; +import { BBox, applyStyleSheet, escapeHtml, replaceChildren } from '../../util'; import { getClassNames, getDefaultTooltipStyle } from './constant'; import type { TooltipOptions, TooltipPosition, TooltipStyleProps } from './types'; export type { TooltipStyleProps, TooltipOptions }; +let colorStyle: CSSStyleDeclaration; + +function sanitizeColor(color: unknown) { + const value = String(color); + colorStyle ||= document.createElement('span').style; + colorStyle.color = ''; + colorStyle.color = value; + return colorStyle.color ? escapeHtml(colorStyle.color) : 'black'; +} + +function sanitizeDatum(datum: Record) { + const sanitized: Record = {}; + Object.keys(datum).forEach((key) => { + const value = datum[key]; + if (key === 'color') sanitized[key] = sanitizeColor(value); + else sanitized[key] = value === undefined ? value : escapeHtml(value); + }); + return sanitized; +} + export class Tooltip extends Component { public static tag = 'tooltip'; @@ -30,7 +50,7 @@ export class Tooltip extends Component { const { data, template } = this.attributes; return data.map(({ name = '', color = 'black', index, ...rest }, idx) => { const datum = { name, color, index: index ?? idx, ...rest }; - return createDOM(substitute(template.item!, datum)) as HTMLElement; + return createDOM(substitute(template.item!, sanitizeDatum(datum))) as HTMLElement; }); } @@ -57,7 +77,7 @@ export class Tooltip extends Component { prefixCls: '', container: `
`, title: `
`, - item: `
  • + item: `
  • {name} @@ -146,7 +166,7 @@ export class Tooltip extends Component { else { if (title) { container.innerHTML = template.title!; - container.getElementsByClassName(CLASS_NAME.TITLE)[0].innerHTML = title; + container.getElementsByClassName(CLASS_NAME.TITLE)[0].textContent = String(title); } else container.getElementsByClassName(CLASS_NAME.TITLE)?.[0]?.remove(); const itemsElements = this.HTMLTooltipItemsElements; const ul = document.createElement('ul'); diff --git a/src/util/string.ts b/src/util/string.ts index 0f4456e2b..bbfc4e3db 100644 --- a/src/util/string.ts +++ b/src/util/string.ts @@ -1,3 +1,15 @@ +const HTML_ESCAPE_MAP: Record = { + '&': '&', + '<': '<', + '>': '>', + '"': '"', + "'": ''', +}; + +export function escapeHtml(value: unknown): string { + return String(value).replace(/[&<>"']/g, (character) => HTML_ESCAPE_MAP[character]); +} + export function toUppercaseFirstLetter(string: string) { return string.toString().charAt(0).toUpperCase() + string.toString().slice(1); } From 1d8d0c2d5a4dcb49adf7c09ea4eeda29fb7d9159 Mon Sep 17 00:00:00 2001 From: interstellarmt <787982239@qq.com> Date: Sun, 20 Sep 2026 15:23:48 +0800 Subject: [PATCH 2/8] =?UTF-8?q?fix:=20=E4=BF=AE=E5=A4=8D=20CR=20=E9=97=AE?= =?UTF-8?q?=E9=A2=98?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- __tests__/unit/ui/tooltip/security.spec.ts | 149 +++++++++++++++++++++ src/ui/tooltip/index.ts | 2 +- src/util/string.ts | 15 +-- 3 files changed, 156 insertions(+), 10 deletions(-) create mode 100644 __tests__/unit/ui/tooltip/security.spec.ts diff --git a/__tests__/unit/ui/tooltip/security.spec.ts b/__tests__/unit/ui/tooltip/security.spec.ts new file mode 100644 index 000000000..21b7d0a20 --- /dev/null +++ b/__tests__/unit/ui/tooltip/security.spec.ts @@ -0,0 +1,149 @@ +import { Tooltip } from '../../../../src/ui/tooltip'; + +describe('Tooltip security', () => { + it('escapes default content when update renders title and items', () => { + const tooltip = new Tooltip({ + style: { + container: { x: 0, y: 0 }, + bounding: null, + }, + }); + const title = ''; + const name = ''; + const value = '">\''; + const index = '0" onmouseover="alert(4)'; + const data = [ + { + name, + value, + index, + color: 'red; background-image: url(javascript:alert(5))', + }, + ]; + + tooltip.update({ + title, + data, + }); + + const element = tooltip.HTMLTooltipElement; + const nameElement = element.querySelector('.tooltip-list-item-name-label'); + const valueElement = element.querySelector('.tooltip-list-item-value'); + const markerElement = element.querySelector('.tooltip-list-item-marker') as HTMLElement; + const itemElement = element.querySelector('.tooltip-list-item'); + + expect(element.querySelector('.tooltip-title')?.textContent).toBe(title); + expect(nameElement?.textContent).toBe(name); + expect(valueElement?.textContent).toBe(value); + expect(nameElement?.getAttribute('title')).toBe(name); + expect(valueElement?.getAttribute('title')).toBe(value); + expect(itemElement?.getAttribute('data-index')).toBe(index); + expect(markerElement.style.background).toBe('black'); + expect(markerElement.style.backgroundImage).toBe(''); + expect(element.querySelector('img, svg, script')).toBeNull(); + expect(element.querySelector('[onerror], [onload], [onmouseover]')).toBeNull(); + expect(tooltip.attributes.title).toBe(title); + expect(tooltip.attributes.data[0]).toMatchObject({ name, value, index }); + + tooltip.update({ title, data }); + expect(element.querySelector('.tooltip-title')?.textContent).toBe(title); + expect(element.querySelector('.tooltip-title')?.innerHTML).not.toContain('&lt;'); + expect(element.querySelector('.tooltip-list-item-name-label')?.textContent).toBe(name); + expect(element.querySelector('.tooltip-list-item-value')?.textContent).toBe(value); + expect(element.querySelector('.tooltip-list-item')?.getAttribute('data-index')).toBe(index); + }); + + it('keeps default structure and normal content unchanged', () => { + const tooltip = new Tooltip({ + style: { + container: { x: 0, y: 0 }, + bounding: null, + title: 'Category A', + data: [{ name: 'Sales', value: 275, color: '#5b8ff9' }], + }, + }); + const element = tooltip.HTMLTooltipElement; + + expect(element.querySelector('.tooltip-title')?.textContent).toBe('Category A'); + expect(element.querySelector('.tooltip-list-item-name-label')?.textContent).toBe('Sales'); + expect(element.querySelector('.tooltip-list-item-value')?.textContent).toBe('275'); + expect((element.querySelector('.tooltip-list-item-marker') as HTMLElement).style.background).not.toBe('black'); + expect(element.querySelectorAll('ul > li')).toHaveLength(1); + }); + + it('rejects CSS declarations and attribute injection in colors', () => { + const tooltip = new Tooltip({ + style: { + container: { x: 0, y: 0 }, + bounding: null, + data: [ + { name: 'CSS declaration', value: 1, color: 'red; background-image: url(javascript:alert(1))' }, + { name: 'Attribute injection', value: 2, color: 'red" onmouseover="alert(2)' }, + ], + }, + }); + const element = tooltip.HTMLTooltipElement; + const markers = Array.from(element.querySelectorAll('.tooltip-list-item-marker')) as HTMLElement[]; + + expect(markers).toHaveLength(2); + markers.forEach((marker) => { + expect(marker.style.background).toBe('black'); + expect(marker.style.backgroundImage).toBe(''); + }); + expect(element.querySelector('[onmouseover]')).toBeNull(); + }); + + it('preserves static HTML in a custom template while escaping its dynamic data', () => { + const tooltip = new Tooltip({ + style: { + container: { x: 0, y: 0 }, + bounding: null, + title: '', + data: [{ name: '', value: '', index: '0" onmouseover="alert(1)' as any }], + template: { + title: '
    ', + item: `
  • + {name} + {value} +
  • `, + }, + }, + }); + const element = tooltip.HTMLTooltipElement; + + expect(element.querySelector('.tooltip-title')).toBeInstanceOf(HTMLElement); + expect(element.querySelector('.custom-item')).toBeInstanceOf(HTMLElement); + expect(element.querySelector('.custom-name')?.textContent).toBe(''); + expect(element.querySelector('.custom-value')?.textContent).toBe(''); + expect(element.querySelector('.custom-item')?.getAttribute('data-index')).toBe('0" onmouseover="alert(1)'); + expect(element.querySelector('[onmouseover]')).toBeNull(); + }); + + it('preserves explicit string custom content', () => { + const tooltip = new Tooltip({ + style: { + container: { x: 0, y: 0 }, + bounding: null, + }, + }); + + tooltip.update({ content: 'Custom HTML' }); + + expect(tooltip.HTMLTooltipElement.querySelector('[data-custom-content="string"]')?.tagName).toBe('STRONG'); + }); + + it('preserves explicit HTMLElement custom content', () => { + const tooltip = new Tooltip({ + style: { + container: { x: 0, y: 0 }, + bounding: null, + }, + }); + const content = document.createElement('strong'); + content.textContent = ''; + + tooltip.update({ content }); + + expect(tooltip.HTMLTooltipElement.firstElementChild).toBe(content); + }); +}); diff --git a/src/ui/tooltip/index.ts b/src/ui/tooltip/index.ts index 727ac53cc..0e4ff569e 100644 --- a/src/ui/tooltip/index.ts +++ b/src/ui/tooltip/index.ts @@ -166,7 +166,7 @@ export class Tooltip extends Component { else { if (title) { container.innerHTML = template.title!; - container.getElementsByClassName(CLASS_NAME.TITLE)[0].textContent = String(title); + container.getElementsByClassName(CLASS_NAME.TITLE)[0].innerHTML = escapeHtml(title); } else container.getElementsByClassName(CLASS_NAME.TITLE)?.[0]?.remove(); const itemsElements = this.HTMLTooltipItemsElements; const ul = document.createElement('ul'); diff --git a/src/util/string.ts b/src/util/string.ts index bbfc4e3db..2897657ae 100644 --- a/src/util/string.ts +++ b/src/util/string.ts @@ -1,13 +1,10 @@ -const HTML_ESCAPE_MAP: Record = { - '&': '&', - '<': '<', - '>': '>', - '"': '"', - "'": ''', -}; - export function escapeHtml(value: unknown): string { - return String(value).replace(/[&<>"']/g, (character) => HTML_ESCAPE_MAP[character]); + return String(value) + .replace(/&/g, '&') + .replace(//g, '>') + .replace(/"/g, '"') + .replace(/'/g, '''); } export function toUppercaseFirstLetter(string: string) { From 3f86930a812c0e989adcfbb1ea72caa39bf8ff76 Mon Sep 17 00:00:00 2001 From: interstellarmt <787982239@qq.com> Date: Sun, 20 Sep 2026 16:00:45 +0800 Subject: [PATCH 3/8] =?UTF-8?q?fix:=20=E7=AE=80=E5=8C=96=E4=BB=A3=E7=A0=81?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- __tests__/unit/ui/tooltip/security.spec.ts | 17 +++++++---------- src/ui/tooltip/index.ts | 14 +------------- src/util/string.ts | 15 +++++++++------ 3 files changed, 17 insertions(+), 29 deletions(-) diff --git a/__tests__/unit/ui/tooltip/security.spec.ts b/__tests__/unit/ui/tooltip/security.spec.ts index 21b7d0a20..3f80a5148 100644 --- a/__tests__/unit/ui/tooltip/security.spec.ts +++ b/__tests__/unit/ui/tooltip/security.spec.ts @@ -17,7 +17,7 @@ describe('Tooltip security', () => { name, value, index, - color: 'red; background-image: url(javascript:alert(5))', + color: 'red" onmouseover="alert(5)', }, ]; @@ -38,8 +38,7 @@ describe('Tooltip security', () => { expect(nameElement?.getAttribute('title')).toBe(name); expect(valueElement?.getAttribute('title')).toBe(value); expect(itemElement?.getAttribute('data-index')).toBe(index); - expect(markerElement.style.background).toBe('black'); - expect(markerElement.style.backgroundImage).toBe(''); + expect(markerElement.getAttribute('onmouseover')).toBeNull(); expect(element.querySelector('img, svg, script')).toBeNull(); expect(element.querySelector('[onerror], [onload], [onmouseover]')).toBeNull(); expect(tooltip.attributes.title).toBe(title); @@ -71,14 +70,14 @@ describe('Tooltip security', () => { expect(element.querySelectorAll('ul > li')).toHaveLength(1); }); - it('rejects CSS declarations and attribute injection in colors', () => { + it('prevents attribute injection through colors', () => { const tooltip = new Tooltip({ style: { container: { x: 0, y: 0 }, bounding: null, data: [ - { name: 'CSS declaration', value: 1, color: 'red; background-image: url(javascript:alert(1))' }, - { name: 'Attribute injection', value: 2, color: 'red" onmouseover="alert(2)' }, + { name: 'Attribute injection', value: 1, color: 'red" onmouseover="alert(1)' }, + { name: 'Element injection', value: 2, color: 'red">/g, '>') - .replace(/"/g, '"') - .replace(/'/g, '''); + return String(value).replace(/[&<>"']/g, (character) => HTML_ESCAPE_MAP[character]); } export function toUppercaseFirstLetter(string: string) { From 996f8e5f52c782f31ed23e33968d613afd2787f7 Mon Sep 17 00:00:00 2001 From: interstellarmt <787982239@qq.com> Date: Sun, 20 Sep 2026 17:21:53 +0800 Subject: [PATCH 4/8] =?UTF-8?q?chore:=20=E6=9B=B4=E6=96=B0=E6=B5=8B?= =?UTF-8?q?=E8=AF=95=E6=88=AA=E5=9B=BE?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../snapshots/AxisLinearLabelRender1.svg | 8 +- .../snapshots/AxisLinearLabelRender2.svg | 8 +- .../snapshots/AxisLinearLabelRender3.svg | 8 +- .../LayoutFlexAlignItemsFlexCenter.svg | 10 +- .../snapshots/LayoutFlexAlignItemsFlexEnd.svg | 10 +- .../LayoutFlexAlignItemsFlexStart.svg | 8 +- .../snapshots/LayoutFlexDirectionColumn.svg | 18 +-- .../snapshots/LayoutFlexDirectionRow.svg | 18 +-- .../LayoutFlexJustifyContentCenter.svg | 10 +- .../LayoutFlexJustifyContentFlexEnd.svg | 10 +- .../LayoutFlexJustifyContentFlexStart.svg | 8 +- .../LayoutFlexManipulateChildren.svg | 6 +- .../integration/snapshots/LayoutFlexNest.svg | 4 +- .../snapshots/LayoutFlexPositionBottom.svg | 2 +- .../LayoutFlexPositionBottomLeft.svg | 2 +- .../LayoutFlexPositionBottomRight.svg | 2 +- .../snapshots/LayoutFlexPositionCenter.svg | 2 +- .../snapshots/LayoutFlexPositionLeft.svg | 2 +- .../snapshots/LayoutFlexPositionRight.svg | 2 +- .../snapshots/LayoutFlexPositionTop.svg | 2 +- .../snapshots/LayoutFlexPositionTopRight.svg | 2 +- .../snapshots/LayoutFlexUpdateLayout.svg | 138 +++++++++--------- .../integration/snapshots/LayoutLegend2.svg | 6 +- .../integration/snapshots/LayoutLegend3.svg | 6 +- .../integration/snapshots/LayoutLegend5.svg | 6 +- .../integration/snapshots/LayoutLegend6.svg | 6 +- .../integration/snapshots/LayoutLegend8.svg | 6 +- .../integration/snapshots/LayoutLegend9.svg | 6 +- .../snapshots/LayoutLegendMultiple.svg | 12 +- .../snapshots/LayoutUpdateAttr.svg | 10 +- 30 files changed, 169 insertions(+), 169 deletions(-) diff --git a/__tests__/integration/snapshots/AxisLinearLabelRender1.svg b/__tests__/integration/snapshots/AxisLinearLabelRender1.svg index f47f4fd78..ec66a5498 100644 --- a/__tests__/integration/snapshots/AxisLinearLabelRender1.svg +++ b/__tests__/integration/snapshots/AxisLinearLabelRender1.svg @@ -56,7 +56,7 @@ - +
    Tick 0 @@ -67,7 +67,7 @@ - +
    Tick 1 @@ -78,7 +78,7 @@ - +
    Tick 2 @@ -89,7 +89,7 @@ - +
    Tick 3 diff --git a/__tests__/integration/snapshots/AxisLinearLabelRender2.svg b/__tests__/integration/snapshots/AxisLinearLabelRender2.svg index 4a328d73f..293cc94f2 100644 --- a/__tests__/integration/snapshots/AxisLinearLabelRender2.svg +++ b/__tests__/integration/snapshots/AxisLinearLabelRender2.svg @@ -56,7 +56,7 @@ - +
    Tick 0 @@ -67,7 +67,7 @@ - +
    Tick 1 @@ -78,7 +78,7 @@ - +
    Tick 2 @@ -89,7 +89,7 @@ - +
    Tick 3 diff --git a/__tests__/integration/snapshots/AxisLinearLabelRender3.svg b/__tests__/integration/snapshots/AxisLinearLabelRender3.svg index d07e6ac45..74c3d628d 100644 --- a/__tests__/integration/snapshots/AxisLinearLabelRender3.svg +++ b/__tests__/integration/snapshots/AxisLinearLabelRender3.svg @@ -56,7 +56,7 @@ - +
    Tick text is very long 0 @@ -67,7 +67,7 @@ - +
    Tick text is very long 1 @@ -78,7 +78,7 @@ - +
    Tick text is very long 2 @@ -89,7 +89,7 @@ - +
    Tick text is very long 3 diff --git a/__tests__/integration/snapshots/LayoutFlexAlignItemsFlexCenter.svg b/__tests__/integration/snapshots/LayoutFlexAlignItemsFlexCenter.svg index 4bf91b3dd..9b1ceaa1b 100644 --- a/__tests__/integration/snapshots/LayoutFlexAlignItemsFlexCenter.svg +++ b/__tests__/integration/snapshots/LayoutFlexAlignItemsFlexCenter.svg @@ -126,19 +126,19 @@ - + - + - + - + - + diff --git a/__tests__/integration/snapshots/LayoutFlexAlignItemsFlexEnd.svg b/__tests__/integration/snapshots/LayoutFlexAlignItemsFlexEnd.svg index c1c014555..9b1ceaa1b 100644 --- a/__tests__/integration/snapshots/LayoutFlexAlignItemsFlexEnd.svg +++ b/__tests__/integration/snapshots/LayoutFlexAlignItemsFlexEnd.svg @@ -126,19 +126,19 @@ - + - + - + - + - + diff --git a/__tests__/integration/snapshots/LayoutFlexAlignItemsFlexStart.svg b/__tests__/integration/snapshots/LayoutFlexAlignItemsFlexStart.svg index 76b3b2799..9b1ceaa1b 100644 --- a/__tests__/integration/snapshots/LayoutFlexAlignItemsFlexStart.svg +++ b/__tests__/integration/snapshots/LayoutFlexAlignItemsFlexStart.svg @@ -129,16 +129,16 @@ - + - + - + - + diff --git a/__tests__/integration/snapshots/LayoutFlexDirectionColumn.svg b/__tests__/integration/snapshots/LayoutFlexDirectionColumn.svg index 7a0e56b14..2509e36c6 100644 --- a/__tests__/integration/snapshots/LayoutFlexDirectionColumn.svg +++ b/__tests__/integration/snapshots/LayoutFlexDirectionColumn.svg @@ -129,31 +129,31 @@ - + - + - + - + - + - + - + - + - + diff --git a/__tests__/integration/snapshots/LayoutFlexDirectionRow.svg b/__tests__/integration/snapshots/LayoutFlexDirectionRow.svg index 83b30b78a..2509e36c6 100644 --- a/__tests__/integration/snapshots/LayoutFlexDirectionRow.svg +++ b/__tests__/integration/snapshots/LayoutFlexDirectionRow.svg @@ -129,31 +129,31 @@ - + - + - + - + - + - + - + - + - + diff --git a/__tests__/integration/snapshots/LayoutFlexJustifyContentCenter.svg b/__tests__/integration/snapshots/LayoutFlexJustifyContentCenter.svg index 483d16936..9b1ceaa1b 100644 --- a/__tests__/integration/snapshots/LayoutFlexJustifyContentCenter.svg +++ b/__tests__/integration/snapshots/LayoutFlexJustifyContentCenter.svg @@ -126,19 +126,19 @@ - + - + - + - + - + diff --git a/__tests__/integration/snapshots/LayoutFlexJustifyContentFlexEnd.svg b/__tests__/integration/snapshots/LayoutFlexJustifyContentFlexEnd.svg index 9251c4c21..9b1ceaa1b 100644 --- a/__tests__/integration/snapshots/LayoutFlexJustifyContentFlexEnd.svg +++ b/__tests__/integration/snapshots/LayoutFlexJustifyContentFlexEnd.svg @@ -126,19 +126,19 @@ - + - + - + - + - + diff --git a/__tests__/integration/snapshots/LayoutFlexJustifyContentFlexStart.svg b/__tests__/integration/snapshots/LayoutFlexJustifyContentFlexStart.svg index 76b3b2799..9b1ceaa1b 100644 --- a/__tests__/integration/snapshots/LayoutFlexJustifyContentFlexStart.svg +++ b/__tests__/integration/snapshots/LayoutFlexJustifyContentFlexStart.svg @@ -129,16 +129,16 @@ - + - + - + - + diff --git a/__tests__/integration/snapshots/LayoutFlexManipulateChildren.svg b/__tests__/integration/snapshots/LayoutFlexManipulateChildren.svg index 7f578cd2a..d5f56342b 100644 --- a/__tests__/integration/snapshots/LayoutFlexManipulateChildren.svg +++ b/__tests__/integration/snapshots/LayoutFlexManipulateChildren.svg @@ -126,13 +126,13 @@ - + - + - + diff --git a/__tests__/integration/snapshots/LayoutFlexNest.svg b/__tests__/integration/snapshots/LayoutFlexNest.svg index c88112e4c..d2133b61e 100644 --- a/__tests__/integration/snapshots/LayoutFlexNest.svg +++ b/__tests__/integration/snapshots/LayoutFlexNest.svg @@ -127,10 +127,10 @@ - + - + diff --git a/__tests__/integration/snapshots/LayoutFlexPositionBottom.svg b/__tests__/integration/snapshots/LayoutFlexPositionBottom.svg index aa93d6f9f..bb57d0dc2 100644 --- a/__tests__/integration/snapshots/LayoutFlexPositionBottom.svg +++ b/__tests__/integration/snapshots/LayoutFlexPositionBottom.svg @@ -126,7 +126,7 @@ - + diff --git a/__tests__/integration/snapshots/LayoutFlexPositionBottomLeft.svg b/__tests__/integration/snapshots/LayoutFlexPositionBottomLeft.svg index 91b6f20bd..bb57d0dc2 100644 --- a/__tests__/integration/snapshots/LayoutFlexPositionBottomLeft.svg +++ b/__tests__/integration/snapshots/LayoutFlexPositionBottomLeft.svg @@ -126,7 +126,7 @@ - + diff --git a/__tests__/integration/snapshots/LayoutFlexPositionBottomRight.svg b/__tests__/integration/snapshots/LayoutFlexPositionBottomRight.svg index fd467d34f..bb57d0dc2 100644 --- a/__tests__/integration/snapshots/LayoutFlexPositionBottomRight.svg +++ b/__tests__/integration/snapshots/LayoutFlexPositionBottomRight.svg @@ -126,7 +126,7 @@ - + diff --git a/__tests__/integration/snapshots/LayoutFlexPositionCenter.svg b/__tests__/integration/snapshots/LayoutFlexPositionCenter.svg index 6c4bbd0e9..bb57d0dc2 100644 --- a/__tests__/integration/snapshots/LayoutFlexPositionCenter.svg +++ b/__tests__/integration/snapshots/LayoutFlexPositionCenter.svg @@ -126,7 +126,7 @@ - + diff --git a/__tests__/integration/snapshots/LayoutFlexPositionLeft.svg b/__tests__/integration/snapshots/LayoutFlexPositionLeft.svg index 71b4d9299..bb57d0dc2 100644 --- a/__tests__/integration/snapshots/LayoutFlexPositionLeft.svg +++ b/__tests__/integration/snapshots/LayoutFlexPositionLeft.svg @@ -126,7 +126,7 @@ - + diff --git a/__tests__/integration/snapshots/LayoutFlexPositionRight.svg b/__tests__/integration/snapshots/LayoutFlexPositionRight.svg index 9fa8fb7e1..bb57d0dc2 100644 --- a/__tests__/integration/snapshots/LayoutFlexPositionRight.svg +++ b/__tests__/integration/snapshots/LayoutFlexPositionRight.svg @@ -126,7 +126,7 @@ - + diff --git a/__tests__/integration/snapshots/LayoutFlexPositionTop.svg b/__tests__/integration/snapshots/LayoutFlexPositionTop.svg index 2ee023869..bb57d0dc2 100644 --- a/__tests__/integration/snapshots/LayoutFlexPositionTop.svg +++ b/__tests__/integration/snapshots/LayoutFlexPositionTop.svg @@ -126,7 +126,7 @@ - + diff --git a/__tests__/integration/snapshots/LayoutFlexPositionTopRight.svg b/__tests__/integration/snapshots/LayoutFlexPositionTopRight.svg index 0fc6a00a0..bb57d0dc2 100644 --- a/__tests__/integration/snapshots/LayoutFlexPositionTopRight.svg +++ b/__tests__/integration/snapshots/LayoutFlexPositionTopRight.svg @@ -126,7 +126,7 @@ - + diff --git a/__tests__/integration/snapshots/LayoutFlexUpdateLayout.svg b/__tests__/integration/snapshots/LayoutFlexUpdateLayout.svg index b8eed9028..72891fc7c 100644 --- a/__tests__/integration/snapshots/LayoutFlexUpdateLayout.svg +++ b/__tests__/integration/snapshots/LayoutFlexUpdateLayout.svg @@ -532,10 +532,10 @@ - + - + @@ -545,14 +545,14 @@ - - + + - + - + @@ -562,14 +562,14 @@ - - + + - + - + @@ -579,14 +579,14 @@ - - + + - + - + @@ -596,14 +596,14 @@ - - + + - + - + @@ -613,14 +613,14 @@ - - + + - + - + @@ -630,14 +630,14 @@ - - + + - + - + @@ -647,14 +647,14 @@ - - + + - + - + @@ -664,14 +664,14 @@ - - + + - + - + @@ -681,14 +681,14 @@ - + - + - + @@ -698,14 +698,14 @@ - - + + - + - + @@ -715,14 +715,14 @@ - - + + - + - + @@ -732,14 +732,14 @@ - - + + - + - + @@ -749,14 +749,14 @@ - - + + - + - + @@ -766,14 +766,14 @@ - - + + - + - + @@ -783,14 +783,14 @@ - - + + - + - + @@ -800,14 +800,14 @@ - - + + - + - + @@ -817,14 +817,14 @@ - - + + - + - + diff --git a/__tests__/integration/snapshots/LayoutLegend2.svg b/__tests__/integration/snapshots/LayoutLegend2.svg index 1796bad82..c2f7be105 100644 --- a/__tests__/integration/snapshots/LayoutLegend2.svg +++ b/__tests__/integration/snapshots/LayoutLegend2.svg @@ -1,7 +1,7 @@ - - + + @@ -11,7 +11,7 @@ - + diff --git a/__tests__/integration/snapshots/LayoutLegend3.svg b/__tests__/integration/snapshots/LayoutLegend3.svg index 0e817915c..271360b0e 100644 --- a/__tests__/integration/snapshots/LayoutLegend3.svg +++ b/__tests__/integration/snapshots/LayoutLegend3.svg @@ -1,7 +1,7 @@ - - + + @@ -11,7 +11,7 @@ - + diff --git a/__tests__/integration/snapshots/LayoutLegend5.svg b/__tests__/integration/snapshots/LayoutLegend5.svg index e81bae165..342022c6c 100644 --- a/__tests__/integration/snapshots/LayoutLegend5.svg +++ b/__tests__/integration/snapshots/LayoutLegend5.svg @@ -1,7 +1,7 @@ - - + + @@ -11,7 +11,7 @@ - + diff --git a/__tests__/integration/snapshots/LayoutLegend6.svg b/__tests__/integration/snapshots/LayoutLegend6.svg index e53e30d82..0ebb0ccb2 100644 --- a/__tests__/integration/snapshots/LayoutLegend6.svg +++ b/__tests__/integration/snapshots/LayoutLegend6.svg @@ -1,7 +1,7 @@ - - + + @@ -11,7 +11,7 @@ - + diff --git a/__tests__/integration/snapshots/LayoutLegend8.svg b/__tests__/integration/snapshots/LayoutLegend8.svg index ac81e2768..4394f5c2d 100644 --- a/__tests__/integration/snapshots/LayoutLegend8.svg +++ b/__tests__/integration/snapshots/LayoutLegend8.svg @@ -1,7 +1,7 @@ - - + + @@ -11,7 +11,7 @@ - + diff --git a/__tests__/integration/snapshots/LayoutLegend9.svg b/__tests__/integration/snapshots/LayoutLegend9.svg index f114659dd..0642ba192 100644 --- a/__tests__/integration/snapshots/LayoutLegend9.svg +++ b/__tests__/integration/snapshots/LayoutLegend9.svg @@ -1,7 +1,7 @@ - - + + @@ -11,7 +11,7 @@ - + diff --git a/__tests__/integration/snapshots/LayoutLegendMultiple.svg b/__tests__/integration/snapshots/LayoutLegendMultiple.svg index 4b45b6d3c..317000bb5 100644 --- a/__tests__/integration/snapshots/LayoutLegendMultiple.svg +++ b/__tests__/integration/snapshots/LayoutLegendMultiple.svg @@ -1,10 +1,10 @@ - - + + - - + + @@ -14,7 +14,7 @@ - + @@ -165,7 +165,7 @@ - + diff --git a/__tests__/integration/snapshots/LayoutUpdateAttr.svg b/__tests__/integration/snapshots/LayoutUpdateAttr.svg index c09ea3bf1..4ffc4532a 100644 --- a/__tests__/integration/snapshots/LayoutUpdateAttr.svg +++ b/__tests__/integration/snapshots/LayoutUpdateAttr.svg @@ -206,19 +206,19 @@ - + - + - + - + - + From f5036debf24b51d3935c699c96123b2d49aad92f Mon Sep 17 00:00:00 2001 From: interstellarmt <787982239@qq.com> Date: Mon, 21 Sep 2026 10:31:25 +0800 Subject: [PATCH 5/8] =?UTF-8?q?fix:=20=E5=85=BC=E5=AE=B9=20G=20=E6=96=B0?= =?UTF-8?q?=E7=89=88=E6=9C=AC=E5=AF=BC=E8=87=B4=E7=9A=84=20TS=20=E6=8A=A5?= =?UTF-8?q?=E9=94=99=E9=97=AE=E9=A2=98?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/ui/legend/types.ts | 6 +++++- src/ui/scrollbar/index.ts | 12 ++++++------ src/ui/select/select.ts | 2 +- src/ui/slider/index.ts | 4 ++-- 4 files changed, 14 insertions(+), 10 deletions(-) diff --git a/src/ui/legend/types.ts b/src/ui/legend/types.ts index 0cc69d85e..fe58a8e18 100644 --- a/src/ui/legend/types.ts +++ b/src/ui/legend/types.ts @@ -67,5 +67,9 @@ export type ContinuousStyleProps = LegendBaseStyleProps & export type ContinuousOptions = ComponentOptions; -export type CategoryStyleProps = LegendBaseStyleProps & CategoryItemsStyleProps; +export type CategoryStyleProps = LegendBaseStyleProps & + CategoryItemsStyleProps & { + dx?: number; + dy?: number; + }; export type CategoryOptions = ComponentOptions; diff --git a/src/ui/scrollbar/index.ts b/src/ui/scrollbar/index.ts index 799814c56..d0d54242d 100644 --- a/src/ui/scrollbar/index.ts +++ b/src/ui/scrollbar/index.ts @@ -148,8 +148,8 @@ export class Scrollbar extends Component { value: newValue, }, }; - this.dispatchEvent(new CustomEvent('scroll', evtVal)); - this.dispatchEvent(new CustomEvent('valuechange', evtVal)); + this.dispatchEvent(new CustomEvent('scroll', evtVal) as any); + this.dispatchEvent(new CustomEvent('valuechange', evtVal) as any); }; public bindEvents() { @@ -194,18 +194,18 @@ export class Scrollbar extends Component { } private onThumbMouseenter = (e: CustomEvent) => { - this.dispatchEvent(new CustomEvent('thumbMouseenter', { detail: e.detail })); + this.dispatchEvent(new CustomEvent('thumbMouseenter', { detail: e.detail }) as any); }; private onTrackMouseenter = (e: CustomEvent) => { - this.dispatchEvent(new CustomEvent('trackMouseenter', { detail: e.detail })); + this.dispatchEvent(new CustomEvent('trackMouseenter', { detail: e.detail }) as any); }; private onThumbMouseleave = (e: CustomEvent) => { - this.dispatchEvent(new CustomEvent('thumbMouseleave', { detail: e.detail })); + this.dispatchEvent(new CustomEvent('thumbMouseleave', { detail: e.detail }) as any); }; private onTrackMouseleave = (e: CustomEvent) => { - this.dispatchEvent(new CustomEvent('trackMouseleave', { detail: e.detail })); + this.dispatchEvent(new CustomEvent('trackMouseleave', { detail: e.detail }) as any); }; } diff --git a/src/ui/select/select.ts b/src/ui/select/select.ts index 57316875f..7af748ae1 100644 --- a/src/ui/select/select.ts +++ b/src/ui/select/select.ts @@ -165,7 +165,7 @@ export class Select extends Component { onClick: (value, option, item) => { this.setValue(value); onSelect?.(value, option, item); - this.dispatchEvent(new CustomEvent('change', { detail: { value, option, item } })); + this.dispatchEvent(new CustomEvent('change', { detail: { value, option, item } }) as any); hide(this.dropdown); }, }, diff --git a/src/ui/slider/index.ts b/src/ui/slider/index.ts index 8f7a3339c..37f893713 100644 --- a/src/ui/slider/index.ts +++ b/src/ui/slider/index.ts @@ -535,7 +535,7 @@ export class Slider extends Component { private dispatchCustomEvent(target: Selection, event: string, name: string) { target.on(event, (e: MouseEvent) => { e.stopPropagation(); - this.dispatchEvent(new CustomEvent(name, { detail: e })); + this.dispatchEvent(new CustomEvent(name, { detail: e }) as any); }); } @@ -627,7 +627,7 @@ export class Slider extends Component { const evt = new CustomEvent('valuechange', { detail: { oldValue: internalOldValue, value }, }); - this.dispatchEvent(evt); + this.dispatchEvent(evt as any); onChange?.(value); }; } From 688bdff02927117377da97c5012e2af6de70cdc2 Mon Sep 17 00:00:00 2001 From: interstellarmt <787982239@qq.com> Date: Mon, 21 Sep 2026 13:56:01 +0800 Subject: [PATCH 6/8] =?UTF-8?q?Revert=20"chore:=20=E6=9B=B4=E6=96=B0?= =?UTF-8?q?=E6=B5=8B=E8=AF=95=E6=88=AA=E5=9B=BE"?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit This reverts commit 996f8e5f52c782f31ed23e33968d613afd2787f7. --- .../LayoutFlexAlignItemsFlexCenter.svg | 10 +- .../snapshots/LayoutFlexAlignItemsFlexEnd.svg | 10 +- .../LayoutFlexAlignItemsFlexStart.svg | 8 +- .../snapshots/LayoutFlexDirectionColumn.svg | 18 +-- .../snapshots/LayoutFlexDirectionRow.svg | 18 +-- .../LayoutFlexJustifyContentCenter.svg | 10 +- .../LayoutFlexJustifyContentFlexEnd.svg | 10 +- .../LayoutFlexJustifyContentFlexStart.svg | 8 +- .../LayoutFlexManipulateChildren.svg | 6 +- .../integration/snapshots/LayoutFlexNest.svg | 4 +- .../snapshots/LayoutFlexPositionBottom.svg | 2 +- .../LayoutFlexPositionBottomLeft.svg | 2 +- .../LayoutFlexPositionBottomRight.svg | 2 +- .../snapshots/LayoutFlexPositionCenter.svg | 2 +- .../snapshots/LayoutFlexPositionLeft.svg | 2 +- .../snapshots/LayoutFlexPositionRight.svg | 2 +- .../snapshots/LayoutFlexPositionTop.svg | 2 +- .../snapshots/LayoutFlexPositionTopRight.svg | 2 +- .../snapshots/LayoutFlexUpdateLayout.svg | 138 +++++++++--------- .../snapshots/LayoutUpdateAttr.svg | 10 +- 20 files changed, 133 insertions(+), 133 deletions(-) diff --git a/__tests__/integration/snapshots/LayoutFlexAlignItemsFlexCenter.svg b/__tests__/integration/snapshots/LayoutFlexAlignItemsFlexCenter.svg index 9b1ceaa1b..4bf91b3dd 100644 --- a/__tests__/integration/snapshots/LayoutFlexAlignItemsFlexCenter.svg +++ b/__tests__/integration/snapshots/LayoutFlexAlignItemsFlexCenter.svg @@ -126,19 +126,19 @@ - + - + - + - + - + diff --git a/__tests__/integration/snapshots/LayoutFlexAlignItemsFlexEnd.svg b/__tests__/integration/snapshots/LayoutFlexAlignItemsFlexEnd.svg index 9b1ceaa1b..c1c014555 100644 --- a/__tests__/integration/snapshots/LayoutFlexAlignItemsFlexEnd.svg +++ b/__tests__/integration/snapshots/LayoutFlexAlignItemsFlexEnd.svg @@ -126,19 +126,19 @@ - + - + - + - + - + diff --git a/__tests__/integration/snapshots/LayoutFlexAlignItemsFlexStart.svg b/__tests__/integration/snapshots/LayoutFlexAlignItemsFlexStart.svg index 9b1ceaa1b..76b3b2799 100644 --- a/__tests__/integration/snapshots/LayoutFlexAlignItemsFlexStart.svg +++ b/__tests__/integration/snapshots/LayoutFlexAlignItemsFlexStart.svg @@ -129,16 +129,16 @@ - + - + - + - + diff --git a/__tests__/integration/snapshots/LayoutFlexDirectionColumn.svg b/__tests__/integration/snapshots/LayoutFlexDirectionColumn.svg index 2509e36c6..7a0e56b14 100644 --- a/__tests__/integration/snapshots/LayoutFlexDirectionColumn.svg +++ b/__tests__/integration/snapshots/LayoutFlexDirectionColumn.svg @@ -129,31 +129,31 @@ - + - + - + - + - + - + - + - + - + diff --git a/__tests__/integration/snapshots/LayoutFlexDirectionRow.svg b/__tests__/integration/snapshots/LayoutFlexDirectionRow.svg index 2509e36c6..83b30b78a 100644 --- a/__tests__/integration/snapshots/LayoutFlexDirectionRow.svg +++ b/__tests__/integration/snapshots/LayoutFlexDirectionRow.svg @@ -129,31 +129,31 @@ - + - + - + - + - + - + - + - + - + diff --git a/__tests__/integration/snapshots/LayoutFlexJustifyContentCenter.svg b/__tests__/integration/snapshots/LayoutFlexJustifyContentCenter.svg index 9b1ceaa1b..483d16936 100644 --- a/__tests__/integration/snapshots/LayoutFlexJustifyContentCenter.svg +++ b/__tests__/integration/snapshots/LayoutFlexJustifyContentCenter.svg @@ -126,19 +126,19 @@ - + - + - + - + - + diff --git a/__tests__/integration/snapshots/LayoutFlexJustifyContentFlexEnd.svg b/__tests__/integration/snapshots/LayoutFlexJustifyContentFlexEnd.svg index 9b1ceaa1b..9251c4c21 100644 --- a/__tests__/integration/snapshots/LayoutFlexJustifyContentFlexEnd.svg +++ b/__tests__/integration/snapshots/LayoutFlexJustifyContentFlexEnd.svg @@ -126,19 +126,19 @@ - + - + - + - + - + diff --git a/__tests__/integration/snapshots/LayoutFlexJustifyContentFlexStart.svg b/__tests__/integration/snapshots/LayoutFlexJustifyContentFlexStart.svg index 9b1ceaa1b..76b3b2799 100644 --- a/__tests__/integration/snapshots/LayoutFlexJustifyContentFlexStart.svg +++ b/__tests__/integration/snapshots/LayoutFlexJustifyContentFlexStart.svg @@ -129,16 +129,16 @@ - + - + - + - + diff --git a/__tests__/integration/snapshots/LayoutFlexManipulateChildren.svg b/__tests__/integration/snapshots/LayoutFlexManipulateChildren.svg index d5f56342b..7f578cd2a 100644 --- a/__tests__/integration/snapshots/LayoutFlexManipulateChildren.svg +++ b/__tests__/integration/snapshots/LayoutFlexManipulateChildren.svg @@ -126,13 +126,13 @@ - + - + - + diff --git a/__tests__/integration/snapshots/LayoutFlexNest.svg b/__tests__/integration/snapshots/LayoutFlexNest.svg index d2133b61e..c88112e4c 100644 --- a/__tests__/integration/snapshots/LayoutFlexNest.svg +++ b/__tests__/integration/snapshots/LayoutFlexNest.svg @@ -127,10 +127,10 @@ - + - + diff --git a/__tests__/integration/snapshots/LayoutFlexPositionBottom.svg b/__tests__/integration/snapshots/LayoutFlexPositionBottom.svg index bb57d0dc2..aa93d6f9f 100644 --- a/__tests__/integration/snapshots/LayoutFlexPositionBottom.svg +++ b/__tests__/integration/snapshots/LayoutFlexPositionBottom.svg @@ -126,7 +126,7 @@ - + diff --git a/__tests__/integration/snapshots/LayoutFlexPositionBottomLeft.svg b/__tests__/integration/snapshots/LayoutFlexPositionBottomLeft.svg index bb57d0dc2..91b6f20bd 100644 --- a/__tests__/integration/snapshots/LayoutFlexPositionBottomLeft.svg +++ b/__tests__/integration/snapshots/LayoutFlexPositionBottomLeft.svg @@ -126,7 +126,7 @@ - + diff --git a/__tests__/integration/snapshots/LayoutFlexPositionBottomRight.svg b/__tests__/integration/snapshots/LayoutFlexPositionBottomRight.svg index bb57d0dc2..fd467d34f 100644 --- a/__tests__/integration/snapshots/LayoutFlexPositionBottomRight.svg +++ b/__tests__/integration/snapshots/LayoutFlexPositionBottomRight.svg @@ -126,7 +126,7 @@ - + diff --git a/__tests__/integration/snapshots/LayoutFlexPositionCenter.svg b/__tests__/integration/snapshots/LayoutFlexPositionCenter.svg index bb57d0dc2..6c4bbd0e9 100644 --- a/__tests__/integration/snapshots/LayoutFlexPositionCenter.svg +++ b/__tests__/integration/snapshots/LayoutFlexPositionCenter.svg @@ -126,7 +126,7 @@ - + diff --git a/__tests__/integration/snapshots/LayoutFlexPositionLeft.svg b/__tests__/integration/snapshots/LayoutFlexPositionLeft.svg index bb57d0dc2..71b4d9299 100644 --- a/__tests__/integration/snapshots/LayoutFlexPositionLeft.svg +++ b/__tests__/integration/snapshots/LayoutFlexPositionLeft.svg @@ -126,7 +126,7 @@ - + diff --git a/__tests__/integration/snapshots/LayoutFlexPositionRight.svg b/__tests__/integration/snapshots/LayoutFlexPositionRight.svg index bb57d0dc2..9fa8fb7e1 100644 --- a/__tests__/integration/snapshots/LayoutFlexPositionRight.svg +++ b/__tests__/integration/snapshots/LayoutFlexPositionRight.svg @@ -126,7 +126,7 @@ - + diff --git a/__tests__/integration/snapshots/LayoutFlexPositionTop.svg b/__tests__/integration/snapshots/LayoutFlexPositionTop.svg index bb57d0dc2..2ee023869 100644 --- a/__tests__/integration/snapshots/LayoutFlexPositionTop.svg +++ b/__tests__/integration/snapshots/LayoutFlexPositionTop.svg @@ -126,7 +126,7 @@ - + diff --git a/__tests__/integration/snapshots/LayoutFlexPositionTopRight.svg b/__tests__/integration/snapshots/LayoutFlexPositionTopRight.svg index bb57d0dc2..0fc6a00a0 100644 --- a/__tests__/integration/snapshots/LayoutFlexPositionTopRight.svg +++ b/__tests__/integration/snapshots/LayoutFlexPositionTopRight.svg @@ -126,7 +126,7 @@ - + diff --git a/__tests__/integration/snapshots/LayoutFlexUpdateLayout.svg b/__tests__/integration/snapshots/LayoutFlexUpdateLayout.svg index 72891fc7c..b8eed9028 100644 --- a/__tests__/integration/snapshots/LayoutFlexUpdateLayout.svg +++ b/__tests__/integration/snapshots/LayoutFlexUpdateLayout.svg @@ -532,10 +532,10 @@ - + - + @@ -545,14 +545,14 @@ - - + + - + - + @@ -562,14 +562,14 @@ - - + + - + - + @@ -579,14 +579,14 @@ - - + + - + - + @@ -596,14 +596,14 @@ - - + + - + - + @@ -613,14 +613,14 @@ - - + + - + - + @@ -630,14 +630,14 @@ - - + + - + - + @@ -647,14 +647,14 @@ - - + + - + - + @@ -664,14 +664,14 @@ - - + + - + - + @@ -681,14 +681,14 @@ - + - + - + @@ -698,14 +698,14 @@ - - + + - + - + @@ -715,14 +715,14 @@ - - + + - + - + @@ -732,14 +732,14 @@ - - + + - + - + @@ -749,14 +749,14 @@ - - + + - + - + @@ -766,14 +766,14 @@ - - + + - + - + @@ -783,14 +783,14 @@ - - + + - + - + @@ -800,14 +800,14 @@ - - + + - + - + @@ -817,14 +817,14 @@ - - + + - + - + diff --git a/__tests__/integration/snapshots/LayoutUpdateAttr.svg b/__tests__/integration/snapshots/LayoutUpdateAttr.svg index 4ffc4532a..c09ea3bf1 100644 --- a/__tests__/integration/snapshots/LayoutUpdateAttr.svg +++ b/__tests__/integration/snapshots/LayoutUpdateAttr.svg @@ -206,19 +206,19 @@ - + - + - + - + - + From ba030223b96a205f684cc7944b752c3c0230ad0f Mon Sep 17 00:00:00 2001 From: interstellarmt <787982239@qq.com> Date: Mon, 21 Sep 2026 14:38:52 +0800 Subject: [PATCH 7/8] =?UTF-8?q?fix:=20=E5=8F=82=E8=80=83=20g2=20=E8=A7=A6?= =?UTF-8?q?=E5=8F=91=E4=B8=A4=E6=AC=A1=20RAF?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- __tests__/integration/canvas.ts | 36 +++++++++++++++++++++++++++++++-- 1 file changed, 34 insertions(+), 2 deletions(-) diff --git a/__tests__/integration/canvas.ts b/__tests__/integration/canvas.ts index 2807c4b71..f1714b5e4 100644 --- a/__tests__/integration/canvas.ts +++ b/__tests__/integration/canvas.ts @@ -2,7 +2,7 @@ import type { DisplayObject } from '@antv/g'; import { Canvas, CanvasEvent, resetEntityCounter } from '@antv/g'; import { Renderer } from '@antv/g-svg'; import { OffscreenCanvasContext, measureText } from './utils/offscreen-canvas-context'; -import { setMockMeasureTextWidth } from '../../src'; +import { Layout, setMockMeasureTextWidth } from '../../src'; export function createGCanvas(width: number, height: number) { resetEntityCounter(); @@ -38,6 +38,12 @@ export function sleep(n: number) { }); } +function nextFrame(canvas: Canvas) { + return new Promise((resolve) => { + canvas.requestAnimationFrame(() => resolve()); + }); +} + export async function renderCanvas(gshape: DisplayObject, wait = 300) { const bbox = gshape.getBBox(); const width = gshape.attributes.width || bbox.x + bbox.width || 400; @@ -48,8 +54,34 @@ export async function renderCanvas(gshape: DisplayObject, wait = 300) { canvas.addEventListener(CanvasEvent.READY, async () => { canvas.appendChild(gshape); - // Wait for the next tick. + // Wait for components, animations and G's mutation queue to settle. await sleep(wait); + + // Layout nodes are populated before being connected to the canvas, so + // their insertion events can run before layout has measurable children. + // Trigger a final layout after mounting, as G2 does during rendering. + let layoutTriggered = false; + canvas.getRoot().forEach((node) => { + if ( + node instanceof Layout && + node.children.length > 0 && + node.children.every((child) => child.attributes.transform === undefined) + ) { + node.layout(); + layoutTriggered = true; + } + }); + + // Flush the layout mutations before serializing the SVG. The explicit + // render keeps this deterministic in Jest, where RAF scheduling differs + // from a browser's render loop. + if (layoutTriggered) { + canvas.render(); + + // Match G2's render completion: wait two frames before taking a snapshot. + await nextFrame(canvas); + await nextFrame(canvas); + } resolve(canvas); }); }); From 7421800ae98ac71edd48f42bcaa6d6c8ef0957c3 Mon Sep 17 00:00:00 2001 From: interstellarmt <787982239@qq.com> Date: Mon, 21 Sep 2026 15:21:47 +0800 Subject: [PATCH 8/8] =?UTF-8?q?fix:=20=E4=BF=AE=E5=A4=8D=E5=9D=90=E6=A0=87?= =?UTF-8?q?=E8=BD=B4=E5=81=8F=E5=B7=AE=E7=9A=84=20bug?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../integration/snapshots/AxisLinearLabelRender1.svg | 8 ++++---- .../integration/snapshots/AxisLinearLabelRender2.svg | 8 ++++---- .../integration/snapshots/AxisLinearLabelRender3.svg | 8 ++++---- src/ui/axis/guides/labels.ts | 7 ------- 4 files changed, 12 insertions(+), 19 deletions(-) diff --git a/__tests__/integration/snapshots/AxisLinearLabelRender1.svg b/__tests__/integration/snapshots/AxisLinearLabelRender1.svg index ec66a5498..09a7e90bf 100644 --- a/__tests__/integration/snapshots/AxisLinearLabelRender1.svg +++ b/__tests__/integration/snapshots/AxisLinearLabelRender1.svg @@ -56,7 +56,7 @@ - +
    Tick 0 @@ -67,7 +67,7 @@ - +
    Tick 1 @@ -78,7 +78,7 @@ - +
    Tick 2 @@ -89,7 +89,7 @@ - +
    Tick 3 diff --git a/__tests__/integration/snapshots/AxisLinearLabelRender2.svg b/__tests__/integration/snapshots/AxisLinearLabelRender2.svg index 293cc94f2..267c7fb5f 100644 --- a/__tests__/integration/snapshots/AxisLinearLabelRender2.svg +++ b/__tests__/integration/snapshots/AxisLinearLabelRender2.svg @@ -56,7 +56,7 @@ - +
    Tick 0 @@ -67,7 +67,7 @@ - +
    Tick 1 @@ -78,7 +78,7 @@ - +
    Tick 2 @@ -89,7 +89,7 @@ - +
    Tick 3 diff --git a/__tests__/integration/snapshots/AxisLinearLabelRender3.svg b/__tests__/integration/snapshots/AxisLinearLabelRender3.svg index 74c3d628d..1241f8763 100644 --- a/__tests__/integration/snapshots/AxisLinearLabelRender3.svg +++ b/__tests__/integration/snapshots/AxisLinearLabelRender3.svg @@ -56,7 +56,7 @@ - +
    Tick text is very long 0 @@ -67,7 +67,7 @@ - +
    Tick text is very long 1 @@ -78,7 +78,7 @@ - +
    Tick text is very long 2 @@ -89,7 +89,7 @@ - +
    Tick text is very long 3 diff --git a/src/ui/axis/guides/labels.ts b/src/ui/axis/guides/labels.ts index 2597caef2..dab70cf9a 100644 --- a/src/ui/axis/guides/labels.ts +++ b/src/ui/axis/guides/labels.ts @@ -241,13 +241,6 @@ function renderLabel( ...labelStyle, }); - // For HTML labels, adjust x position to center align. - if (label.nodeName === 'html') { - const bbox = label.getBBox(); - const currentX = label.style.x || 0; - label.attr('x', currentX - bbox.width / 2); - } - container.attr(groupStyle); return label; }