You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@superset.apache.org by jl...@apache.org on 2026/10/21 13:32:05 UTC
(superset) 01/02: test(partition): add failing guard for tooltip right-edge overflow
This is an automated email from the ASF dual-hosted git repository.
sadpandajoe pushed a commit to branch fix-partition-tooltip-right-edge
in repository https://gitbox.apache.org/repos/asf/superset.git
commit af4d5b3ba42c860849c425efed21e868612e15d7
Author: Joe Li <jo...@preset.io>
AuthorDate: Wed Oct 21 13:22:09 2026 +0000
test(partition): add failing guard for tooltip right-edge overflow
Partition.ts positions the tooltip at cursorX + 15 without checking the
chart width, so hovering near the right edge pushes it outside the chart
where it is clipped. Add a jsdom test that drives the real mousemove
handler and asserts the tooltip stays within the chart width.
Also allow d3-hierarchy (ESM-only) through jest's babel transform so
Partition.ts can be imported by tests.
---
superset-frontend/jest.config.js | 2 +-
.../plugin-chart-partition/test/Partition.test.ts | 120 +++++++++++++++++++++
2 files changed, 121 insertions(+), 1 deletion(-)
diff --git a/superset-frontend/jest.config.js b/superset-frontend/jest.config.js
index 1ddc9590c23..298e3950135 100644
--- a/superset-frontend/jest.config.js
+++ b/superset-frontend/jest.config.js
@@ -112,7 +112,7 @@ export default {
// @deck.gl/maplibre@9.4.0 ships ESM-only (no dist/*.cjs), unlike every
// other @deck.gl/* package - allowed through so babel-jest can convert
// its import/export syntax (see the moduleNameMapper entry above).
- 'node_modules/(?!@ant-design/(colors|fast-color)|@formatjs/.*|d3-(array|interpolate|color|time|scale|time-format|format|selection)|internmap|@mapbox/tiny-sdf|@deck.gl/maplibre|remark-gfm|(?!@ngrx|(?!deck.gl)|d3-scale)|markdown-table|micromark-*.|decode-named-character-reference|character-entities|mdast-util-*.|unist-util-*.|ccount|escape-string-regexp|nanoid|uuid|@rjsf/*.|@x0k/.*|echarts|zrender|fetch-mock|pretty-ms|parse-ms|ol|@babel/runtime|@emotion|cheerio|cheerio/lib|parse5|dom-s [...]
+ 'node_modules/(?!@ant-design/(colors|fast-color)|@formatjs/.*|d3-(array|interpolate|color|time|scale|time-format|format|selection|hierarchy)|internmap|@mapbox/tiny-sdf|@deck.gl/maplibre|remark-gfm|(?!@ngrx|(?!deck.gl)|d3-scale)|markdown-table|micromark-*.|decode-named-character-reference|character-entities|mdast-util-*.|unist-util-*.|ccount|escape-string-regexp|nanoid|uuid|@rjsf/*.|@x0k/.*|echarts|zrender|fetch-mock|pretty-ms|parse-ms|ol|@babel/runtime|@emotion|cheerio|cheerio/lib|pa [...]
],
transform: {
'^.+\\.(js|jsx|ts|tsx)$': 'babel-jest',
diff --git a/superset-frontend/plugins/plugin-chart-partition/test/Partition.test.ts b/superset-frontend/plugins/plugin-chart-partition/test/Partition.test.ts
new file mode 100644
index 00000000000..3f97d8cf592
--- /dev/null
+++ b/superset-frontend/plugins/plugin-chart-partition/test/Partition.test.ts
@@ -0,0 +1,120 @@
+/**
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+import Partition from '../src/Partition';
+
+const CHART_WIDTH = 400;
+const CHART_HEIGHT = 300;
+const TOOLTIP_WIDTH = 120;
+const TOOLTIP_OFFSET = 15;
+
+const props = {
+ width: CHART_WIDTH,
+ height: CHART_HEIGHT,
+ data: [
+ {
+ name: 'sum__num',
+ val: 100,
+ children: [
+ { name: 'boy', val: 60 },
+ { name: 'girl', val: 40 },
+ ],
+ },
+ ],
+ colorScheme: 'supersetColors',
+ dateTimeFormat: '%Y-%m-%d',
+ equalDateSize: true,
+ levels: ['gender'],
+ metrics: ['sum__num'],
+ numberFormat: ',d',
+ partitionLimit: 0,
+ partitionThreshold: 0,
+ timeSeriesOption: 'not_time',
+ useLogScale: false,
+ useRichTooltip: false,
+ sliceId: 1,
+};
+
+let container: HTMLDivElement;
+let rectSpy: jest.SpyInstance;
+let offsetWidthSpy: jest.SpyInstance;
+
+beforeEach(() => {
+ container = document.createElement('div');
+ document.body.appendChild(container);
+ // jsdom performs no layout: give the container a fixed box so the
+ // pointer coordinates d3.mouse() derives from clientX are deterministic.
+ rectSpy = jest
+ .spyOn(HTMLElement.prototype, 'getBoundingClientRect')
+ .mockImplementation(function getRect(this: HTMLElement) {
+ const isTooltip = this.classList.contains('partition-tooltip');
+ const width = isTooltip ? TOOLTIP_WIDTH : CHART_WIDTH;
+
+ return {
+ x: 0,
+ y: 0,
+ top: 0,
+ left: 0,
+ right: width,
+ bottom: CHART_HEIGHT,
+ width,
+ height: CHART_HEIGHT,
+ toJSON: () => ({}),
+ } as DOMRect;
+ });
+ offsetWidthSpy = jest
+ .spyOn(HTMLElement.prototype, 'offsetWidth', 'get')
+ .mockImplementation(function getOffsetWidth(this: HTMLElement) {
+ return this.classList.contains('partition-tooltip')
+ ? TOOLTIP_WIDTH
+ : CHART_WIDTH;
+ });
+});
+
+afterEach(() => {
+ rectSpy.mockRestore();
+ offsetWidthSpy.mockRestore();
+ container.remove();
+});
+
+function hoverAt(clientX: number, clientY: number): HTMLElement {
+ Partition(container, props);
+ const node = container.querySelector('svg g rect') as SVGRectElement;
+ node.dispatchEvent(
+ new MouseEvent('mousemove', { bubbles: true, clientX, clientY }),
+ );
+
+ return container.querySelector('.partition-tooltip') as HTMLElement;
+}
+
+test('places the tooltip right of the cursor when there is room', () => {
+ const tooltip = hoverAt(50, 100);
+
+ expect(tooltip.style.left).toBe(`${50 + TOOLTIP_OFFSET}px`);
+ expect(tooltip.style.top).toBe('100px');
+});
+
+test('keeps the tooltip inside the chart when hovering near the right edge', () => {
+ const cursorX = CHART_WIDTH - 10;
+ const tooltip = hoverAt(cursorX, 100);
+
+ const left = parseFloat(tooltip.style.left);
+ expect(left + TOOLTIP_WIDTH).toBeLessThanOrEqual(CHART_WIDTH);
+ // shifted to the left of the cursor rather than overlapping it
+ expect(left + TOOLTIP_WIDTH).toBeLessThanOrEqual(cursorX);
+});