Skip to content

Commit 1e15cf1

Browse files
authored
fix: Fixes popover pin when highlight moves from point to group (#106)
* fix: Fixes popover pin when highlight moves from point to group * add integ regression test
1 parent 1a1762c commit 1e15cf1

File tree

9 files changed

+140
-21
lines changed

9 files changed

+140
-21
lines changed
Lines changed: 35 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,35 @@
1+
// Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved.
2+
// SPDX-License-Identifier: Apache-2.0
3+
4+
import ColumnLayout from "@cloudscape-design/components/column-layout";
5+
6+
import { CartesianChart } from "../../lib/components";
7+
import { numberFormatter } from "../common/formatters";
8+
import { useChartSettings } from "../common/page-settings";
9+
import { Page } from "../common/templates";
10+
11+
const categories = ["Jun 2019", "Jul 2019", "Aug 2019", "Sep 2019", "Oct 2019", "Nov 2019", "Dec 2019"];
12+
const costsData = [6562, 8768, 9742, 10464, 16777, 9956, 5876];
13+
const prevCostsData = [6862, 6322, 10112, 9220, 13123, 11277, 7862];
14+
const costsSeries = [
15+
{ id: "costs", name: "Costs", type: "column", data: costsData },
16+
{ id: "prev-costs", name: "Prev costs", type: "column", data: prevCostsData },
17+
] as const;
18+
19+
export default function () {
20+
const { chartProps } = useChartSettings();
21+
return (
22+
<Page title="Integration tests page for column cartesian charts">
23+
<ColumnLayout columns={2}>
24+
<CartesianChart
25+
{...chartProps.cartesian}
26+
ariaLabel="Grouped column chart"
27+
data-testid="grouped-column-chart"
28+
series={costsSeries}
29+
xAxis={{ type: "category", title: "Budget month", categories }}
30+
yAxis={{ title: "Costs (USD)", valueFormatter: numberFormatter }}
31+
/>
32+
</ColumnLayout>
33+
</Page>
34+
);
35+
}

src/core/__tests__/chart-core-navigation-cartesian.test.tsx

Lines changed: 2 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -8,8 +8,7 @@ import { vi } from "vitest";
88
import { KeyCode } from "@cloudscape-design/component-toolkit/internal";
99

1010
import "highcharts/modules/accessibility";
11-
import { CoreChartProps } from "../../../lib/components/core/interfaces";
12-
import { createChartWrapper, renderChart } from "./common";
11+
import { CoreChartTestProps, createChartWrapper, renderChart } from "./common";
1312

1413
const seriesShort2: Highcharts.SeriesOptionsType[] = [
1514
{
@@ -64,7 +63,7 @@ const seriesLong1: Highcharts.SeriesOptionsType[] = [
6463
data: range(1, 1001).map((x) => ({ x, y: -x })),
6564
},
6665
];
67-
function commonProps(invertedSetting: boolean | "random" = false, series = seriesShort2): CoreChartProps {
66+
function commonProps(invertedSetting: boolean | "random" = false, series = seriesShort2): CoreChartTestProps {
6867
const inverted = invertedSetting === "random" ? Math.random() > 0.5 : invertedSetting;
6968
return {
7069
highcharts,

src/core/__tests__/common.tsx

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -33,12 +33,12 @@ export function StatefulChart(props: CoreChartProps) {
3333
);
3434
}
3535

36-
type TestProps = Partial<CoreChartProps> & {
36+
export type CoreChartTestProps = Partial<CoreChartProps> & {
3737
onLegendItemHighlight?: () => void;
3838
i18nProvider?: Record<string, Record<string, string>>;
3939
};
4040

41-
export function renderChart({ i18nProvider, ...props }: TestProps, Component = CoreChart) {
41+
export function renderChart({ i18nProvider, ...props }: CoreChartTestProps, Component = CoreChart) {
4242
const ComponentWrapper = (props: CoreChartProps) => {
4343
return i18nProvider ? (
4444
<TestI18nProvider messages={i18nProvider}>
@@ -51,11 +51,11 @@ export function renderChart({ i18nProvider, ...props }: TestProps, Component = C
5151
const { rerender } = render(<ComponentWrapper {...props} />);
5252
return {
5353
wrapper: createChartWrapper(),
54-
rerender: (props: TestProps) => rerender(<ComponentWrapper {...props} />),
54+
rerender: (props: CoreChartTestProps) => rerender(<ComponentWrapper {...props} />),
5555
};
5656
}
5757

58-
export function renderStatefulChart(props: TestProps) {
58+
export function renderStatefulChart(props: CoreChartTestProps) {
5959
return renderChart(props, StatefulChart);
6060
}
6161

src/core/chart-api/chart-extra-tooltip.tsx

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -103,9 +103,8 @@ export class ChartExtraTooltip extends AsyncStore<ReactiveTooltipState> {
103103
function isGroupEqual(a: Highcharts.Point, b: Highcharts.Point) {
104104
return a?.x === b?.x && a?.y === b?.y && getSeriesId(a?.series) === getSeriesId(b?.series);
105105
}
106-
107106
this.set((prev) => {
108-
return isEqualArrays(prev.group, group, isGroupEqual)
107+
return prev.point === null && isEqualArrays(prev.group, group, isGroupEqual)
109108
? prev
110109
: { visible: true, pinned: false, point: null, group };
111110
});
Lines changed: 38 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,38 @@
1+
// Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved.
2+
// SPDX-License-Identifier: Apache-2.0
3+
4+
import { expect, test } from "vitest";
5+
6+
import "@cloudscape-design/components/test-utils/selectors";
7+
import "../../lib/components/test-utils/selectors";
8+
import createWrapper from "../../lib/components/test-utils/selectors";
9+
import { setupTest } from "../utils";
10+
11+
const w = createWrapper();
12+
13+
test(
14+
"pins chart tooltip after hovering chart point and then chart point group",
15+
setupTest("#/01-cartesian-chart/column-chart-test", async (page) => {
16+
const chart = w.findCartesianHighcharts('[data-testid="grouped-column-chart"]');
17+
const point = chart.find('[aria-label="Jul 2019 6.32K, Prev costs"]');
18+
const expectedTooltipContent = ["Jul 2019\nCosts\n8.77K\nPrev costs\n6.32K"];
19+
20+
const pointBox = await page.getBoundingBox(point.toSelector());
21+
const pointCenter = [pointBox.left + pointBox.width / 2, pointBox.top + pointBox.height / 2];
22+
23+
// Hover on the 2nd point in group.
24+
await page.moveCursorTo(pointCenter[0], pointCenter[1]);
25+
await expect(page.getElementsText(chart.findTooltip().toSelector())).resolves.toEqual(expectedTooltipContent);
26+
await expect(page.isExisting(chart.findTooltip().findDismissButton().toSelector())).resolves.toBe(false);
27+
28+
// Hover above the point (on the group).
29+
await page.moveCursorBy(0, -pointBox.height);
30+
await expect(page.getElementsText(chart.findTooltip().toSelector())).resolves.toEqual(expectedTooltipContent);
31+
await expect(page.isExisting(chart.findTooltip().findDismissButton().toSelector())).resolves.toBe(false);
32+
33+
// Clicking on the group should pin the tooltip.
34+
await page.clickHere();
35+
await expect(page.getElementsText(chart.findTooltip().toSelector())).resolves.toEqual(expectedTooltipContent);
36+
await expect(page.isExisting(chart.findTooltip().findDismissButton().toSelector())).resolves.toBe(true);
37+
}),
38+
);

test/functional/index.test.ts

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,6 @@
33

44
import { expect, test } from "vitest";
55

6-
import { BasePageObject } from "@cloudscape-design/browser-test-tools/page-objects";
76
import createWrapper from "@cloudscape-design/components/test-utils/selectors";
87

98
import "../../lib/components/test-utils/selectors";
@@ -13,7 +12,7 @@ const wrapper = createWrapper();
1312

1413
test(
1514
"index page",
16-
setupTest("#", BasePageObject, async (page) => {
15+
setupTest("#", async (page) => {
1716
await expect(page.getText("h1")).resolves.toBe("Welcome!");
1817
await expect(page.getElementsCount(wrapper.findLink().toSelector())).resolves.toBeGreaterThan(5);
1918
}),

test/functional/selectors.test.ts

Lines changed: 1 addition & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -3,8 +3,6 @@
33

44
import { expect, test } from "vitest";
55

6-
import { BasePageObject } from "@cloudscape-design/browser-test-tools/page-objects";
7-
86
import "@cloudscape-design/components/test-utils/selectors";
97
import "../../lib/components/test-utils/selectors";
108
import createWrapper from "../../lib/components/test-utils/selectors";
@@ -14,7 +12,7 @@ const w = createWrapper();
1412

1513
test(
1614
"root selectors",
17-
setupTest("#/05-demos/website-playground-examples", BasePageObject, async (page) => {
15+
setupTest("#/05-demos/website-playground-examples", async (page) => {
1816
await expect(page.getElementsCount(w.findCartesianHighcharts().toSelector())).resolves.toBe(11);
1917
await expect(page.getElementsCount(w.findAllCartesianHighcharts().toSelector())).resolves.toBe(11);
2018
await expect(page.getElementsCount(w.findAllCartesianHighcharts().get(1).toSelector())).resolves.toBe(11);

test/utils.ts

Lines changed: 56 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,12 +1,20 @@
11
// Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved.
22
// SPDX-License-Identifier: Apache-2.0
33

4-
import { BasePageObject } from "@cloudscape-design/browser-test-tools/page-objects";
4+
import { BasePageObject, ScreenshotPageObject } from "@cloudscape-design/browser-test-tools/page-objects";
55
import useBrowser from "@cloudscape-design/browser-test-tools/use-browser";
66

7-
export function setupTest<P extends BasePageObject & { init?(): Promise<void> }>(
8-
url: string,
7+
export function setupTest(url: string, test: (page: ChartPageObject) => Promise<void>) {
8+
return setupTestBase(ChartPageObject, url, test);
9+
}
10+
11+
export function setupScreenshotTest(url: string, test: (page: ScreenshotPageObject) => Promise<void>) {
12+
return setupTestBase(ScreenshotPageObject, url, test);
13+
}
14+
15+
function setupTestBase<P extends BasePageObject & { init?(): Promise<void> }>(
916
PageClass: new (browser: WebdriverIO.Browser) => P,
17+
url: string,
1018
test: (page: P) => Promise<void>,
1119
) {
1220
return useBrowser({}, async (browser) => {
@@ -22,3 +30,48 @@ export function setupTest<P extends BasePageObject & { init?(): Promise<void> }>
2230
await test(page);
2331
});
2432
}
33+
34+
class ChartPageObject extends BasePageObject {
35+
async moveCursorTo(x: number, y: number) {
36+
await this.browser.performActions([
37+
{
38+
type: "pointer",
39+
id: "event",
40+
parameters: { pointerType: "mouse" },
41+
actions: [
42+
{ type: "pointerMove", duration: 100, origin: "viewport", x, y },
43+
{ type: "pause", duration: 150 },
44+
],
45+
},
46+
]);
47+
}
48+
49+
async moveCursorBy(xOffset: number, yOffset: number) {
50+
await this.browser.performActions([
51+
{
52+
type: "pointer",
53+
id: "event",
54+
parameters: { pointerType: "mouse" },
55+
actions: [
56+
{ type: "pointerMove", duration: 100, origin: "pointer", x: xOffset, y: yOffset },
57+
{ type: "pause", duration: 150 },
58+
],
59+
},
60+
]);
61+
}
62+
63+
async clickHere() {
64+
await this.browser.performActions([
65+
{
66+
type: "pointer",
67+
id: "event",
68+
parameters: { pointerType: "mouse" },
69+
actions: [
70+
{ type: "pointerDown", origin: "pointer", button: 0, duration: 20 },
71+
{ type: "pointerUp", origin: "pointer", button: 0, duration: 20 },
72+
{ type: "pause", duration: 150 },
73+
],
74+
},
75+
]);
76+
}
77+
}

test/visual/index.test.ts

Lines changed: 2 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -4,9 +4,7 @@
44
import path from "path";
55
import { expect, test } from "vitest";
66

7-
import { ScreenshotPageObject } from "@cloudscape-design/browser-test-tools/page-objects";
8-
9-
import { setupTest } from "../utils";
7+
import { setupScreenshotTest } from "../utils";
108

119
const pagesMap = import.meta.glob("../../pages/**/*.page.tsx", { as: "raw" });
1210
const allPages = Object.keys(pagesMap)
@@ -18,7 +16,7 @@ const rtlPages = allPages
1816
.map((page) => page + "&direction=rtl");
1917

2018
test.each([...allPages, ...rtlPages])("matches snapshot for %s", (route) =>
21-
setupTest(route, ScreenshotPageObject, async (page) => {
19+
setupScreenshotTest(route, async (page) => {
2220
const hasScreenshotArea = await page.isExisting(".screenshot-area");
2321
if (hasScreenshotArea) {
2422
await page.waitForJsTimers(100);

0 commit comments

Comments
 (0)