@agent-native/toolkit 0.217.0 → 0.218.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/agent-native.eject.json +21 -0
- package/dist/app/agent-page/AgentContextTab.d.ts.map +1 -1
- package/dist/app/agent-page/AgentContextTab.js +1 -0
- package/dist/app/agent-page/AgentContextTab.js.map +1 -1
- package/dist/app/agentkit/react/components.d.ts.map +1 -1
- package/dist/app/agentkit/react/components.js +1 -1
- package/dist/app/agentkit/react/components.js.map +1 -1
- package/dist/app/agentkit/react/context.d.ts +4 -1
- package/dist/app/agentkit/react/context.d.ts.map +1 -1
- package/dist/app/agentkit/react/context.js +3 -1
- package/dist/app/agentkit/react/context.js.map +1 -1
- package/dist/app/agentkit/react/data-chart-layout.d.ts +30 -0
- package/dist/app/agentkit/react/data-chart-layout.d.ts.map +1 -0
- package/dist/app/agentkit/react/data-chart-layout.js +2 -0
- package/dist/app/agentkit/react/data-chart-layout.js.map +1 -0
- package/dist/app/agentkit/react/index.d.ts +1 -0
- package/dist/app/agentkit/react/index.d.ts.map +1 -1
- package/dist/app/agentkit/react/index.js +1 -0
- package/dist/app/agentkit/react/index.js.map +1 -1
- package/dist/app/agentkit/react/root.d.ts +3 -1
- package/dist/app/agentkit/react/root.d.ts.map +1 -1
- package/dist/app/agentkit/react/root.js +2 -2
- package/dist/app/agentkit/react/root.js.map +1 -1
- package/dist/app/agentkit/react/styles.css +18 -0
- package/dist/app/agentkit/react/transcript-layout.d.ts +2 -0
- package/dist/app/agentkit/react/transcript-layout.d.ts.map +1 -1
- package/dist/app/agentkit/react/transcript-layout.js.map +1 -1
- package/dist/app/chat/AgentKitAssistantChat.d.ts.map +1 -1
- package/dist/app/chat/AgentKitAssistantChat.js +2 -2
- package/dist/app/chat/AgentKitAssistantChat.js.map +1 -1
- package/dist/app/chat/MultiTabAssistantChat.d.ts.map +1 -1
- package/dist/app/chat/MultiTabAssistantChat.js +51 -93
- package/dist/app/chat/MultiTabAssistantChat.js.map +1 -1
- package/dist/app/chat/agentkit-chat/action-widget.d.ts.map +1 -1
- package/dist/app/chat/agentkit-chat/action-widget.js +3 -1
- package/dist/app/chat/agentkit-chat/action-widget.js.map +1 -1
- package/dist/app/chat/agentkit-chat/rail.d.ts +1 -1
- package/dist/app/chat/agentkit-chat/rail.d.ts.map +1 -1
- package/dist/app/chat/agentkit-chat/rail.js +1 -1
- package/dist/app/chat/agentkit-chat/rail.js.map +1 -1
- package/dist/app/chat/chat/index.d.ts +2 -1
- package/dist/app/chat/chat/index.d.ts.map +1 -1
- package/dist/app/chat/chat/index.js +1 -1
- package/dist/app/chat/chat/index.js.map +1 -1
- package/dist/app/chat/chat/surface-types.d.ts +3 -0
- package/dist/app/chat/chat/surface-types.d.ts.map +1 -1
- package/dist/app/chat/chat/surface-types.js.map +1 -1
- package/dist/app/chat/chat/tool-call-display.d.ts +3 -1
- package/dist/app/chat/chat/tool-call-display.d.ts.map +1 -1
- package/dist/app/chat/chat/tool-call-display.js +4 -3
- package/dist/app/chat/chat/tool-call-display.js.map +1 -1
- package/dist/app/chat/chat/tool-render-registry.d.ts +2 -0
- package/dist/app/chat/chat/tool-render-registry.d.ts.map +1 -1
- package/dist/app/chat/chat/tool-render-registry.js.map +1 -1
- package/dist/app/chat/chat/widgets/DataChartRenderer.d.ts +3 -1
- package/dist/app/chat/chat/widgets/DataChartRenderer.d.ts.map +1 -1
- package/dist/app/chat/chat/widgets/DataChartRenderer.js +116 -26
- package/dist/app/chat/chat/widgets/DataChartRenderer.js.map +1 -1
- package/dist/app/chat/chat/widgets/DataChartWidget.d.ts +3 -1
- package/dist/app/chat/chat/widgets/DataChartWidget.d.ts.map +1 -1
- package/dist/app/chat/chat/widgets/DataChartWidget.js +21 -2
- package/dist/app/chat/chat/widgets/DataChartWidget.js.map +1 -1
- package/dist/app/chat/chat/widgets/DataInsightsWidget.d.ts +3 -1
- package/dist/app/chat/chat/widgets/DataInsightsWidget.d.ts.map +1 -1
- package/dist/app/chat/chat/widgets/DataInsightsWidget.js +2 -2
- package/dist/app/chat/chat/widgets/DataInsightsWidget.js.map +1 -1
- package/dist/app/chat/chat/widgets/DataWidgetDisplayControls.d.ts +1 -1
- package/dist/app/chat/chat/widgets/DataWidgetDisplayControls.d.ts.map +1 -1
- package/dist/app/chat/chat/widgets/DataWidgetDisplayControls.js +3 -1
- package/dist/app/chat/chat/widgets/DataWidgetDisplayControls.js.map +1 -1
- package/dist/app/chat/chat/widgets/builtin-tool-renderers.d.ts.map +1 -1
- package/dist/app/chat/chat/widgets/builtin-tool-renderers.js +2 -2
- package/dist/app/chat/chat/widgets/builtin-tool-renderers.js.map +1 -1
- package/dist/app/chat/chat-first/chat-history.d.ts +13 -2
- package/dist/app/chat/chat-first/chat-history.d.ts.map +1 -1
- package/dist/app/chat/chat-first/chat-history.js +24 -3
- package/dist/app/chat/chat-first/chat-history.js.map +1 -1
- package/dist/app/chat/ownership-history/ChatOwnerAvatar.d.ts +6 -0
- package/dist/app/chat/ownership-history/ChatOwnerAvatar.d.ts.map +1 -0
- package/dist/app/chat/ownership-history/ChatOwnerAvatar.js +9 -0
- package/dist/app/chat/ownership-history/ChatOwnerAvatar.js.map +1 -0
- package/dist/app/chat/ownership-history/index.d.ts +11 -0
- package/dist/app/chat/ownership-history/index.d.ts.map +1 -0
- package/dist/app/chat/ownership-history/index.js +22 -0
- package/dist/app/chat/ownership-history/index.js.map +1 -0
- package/dist/app/chat/ownership-history/rows.d.ts +27 -0
- package/dist/app/chat/ownership-history/rows.d.ts.map +1 -0
- package/dist/app/chat/ownership-history/rows.js +101 -0
- package/dist/app/chat/ownership-history/rows.js.map +1 -0
- package/dist/app/chat/ownership-history/useOwnershipChatHistory.d.ts +52 -0
- package/dist/app/chat/ownership-history/useOwnershipChatHistory.d.ts.map +1 -0
- package/dist/app/chat/ownership-history/useOwnershipChatHistory.js +497 -0
- package/dist/app/chat/ownership-history/useOwnershipChatHistory.js.map +1 -0
- package/dist/app/i18n/catalogs/ar-SA.d.ts.map +1 -1
- package/dist/app/i18n/catalogs/ar-SA.js +3 -0
- package/dist/app/i18n/catalogs/ar-SA.js.map +1 -1
- package/dist/app/i18n/catalogs/de-DE.d.ts.map +1 -1
- package/dist/app/i18n/catalogs/de-DE.js +3 -0
- package/dist/app/i18n/catalogs/de-DE.js.map +1 -1
- package/dist/app/i18n/catalogs/en-US.d.ts +3 -0
- package/dist/app/i18n/catalogs/en-US.d.ts.map +1 -1
- package/dist/app/i18n/catalogs/en-US.js +3 -0
- package/dist/app/i18n/catalogs/en-US.js.map +1 -1
- package/dist/app/i18n/catalogs/es-ES.d.ts.map +1 -1
- package/dist/app/i18n/catalogs/es-ES.js +3 -0
- package/dist/app/i18n/catalogs/es-ES.js.map +1 -1
- package/dist/app/i18n/catalogs/fr-FR.d.ts.map +1 -1
- package/dist/app/i18n/catalogs/fr-FR.js +3 -0
- package/dist/app/i18n/catalogs/fr-FR.js.map +1 -1
- package/dist/app/i18n/catalogs/hi-IN.d.ts.map +1 -1
- package/dist/app/i18n/catalogs/hi-IN.js +3 -0
- package/dist/app/i18n/catalogs/hi-IN.js.map +1 -1
- package/dist/app/i18n/catalogs/ja-JP.d.ts.map +1 -1
- package/dist/app/i18n/catalogs/ja-JP.js +3 -0
- package/dist/app/i18n/catalogs/ja-JP.js.map +1 -1
- package/dist/app/i18n/catalogs/ko-KR.d.ts.map +1 -1
- package/dist/app/i18n/catalogs/ko-KR.js +3 -0
- package/dist/app/i18n/catalogs/ko-KR.js.map +1 -1
- package/dist/app/i18n/catalogs/pt-BR.d.ts.map +1 -1
- package/dist/app/i18n/catalogs/pt-BR.js +3 -0
- package/dist/app/i18n/catalogs/pt-BR.js.map +1 -1
- package/dist/app/i18n/catalogs/zh-CN.d.ts.map +1 -1
- package/dist/app/i18n/catalogs/zh-CN.js +3 -0
- package/dist/app/i18n/catalogs/zh-CN.js.map +1 -1
- package/dist/app/i18n/catalogs/zh-TW.d.ts.map +1 -1
- package/dist/app/i18n/catalogs/zh-TW.js +3 -0
- package/dist/app/i18n/catalogs/zh-TW.js.map +1 -1
- package/dist/app/sharing/ShareButton.d.ts.map +1 -1
- package/dist/app/sharing/ShareButton.js +9 -8
- package/dist/app/sharing/ShareButton.js.map +1 -1
- package/dist/chat-history/ChatHistoryList.d.ts +16 -3
- package/dist/chat-history/ChatHistoryList.d.ts.map +1 -1
- package/dist/chat-history/ChatHistoryList.js +25 -15
- package/dist/chat-history/ChatHistoryList.js.map +1 -1
- package/dist/chat-history.css +109 -9
- package/package.json +10 -3
- package/src/app/agent-page/AgentContextTab.spec.ts +50 -2
- package/src/app/agent-page/AgentContextTab.tsx +1 -0
- package/src/app/agentkit/react/components.interaction.spec.tsx +2 -0
- package/src/app/agentkit/react/components.spec.tsx +4 -1
- package/src/app/agentkit/react/components.tsx +3 -0
- package/src/app/agentkit/react/context.tsx +6 -0
- package/src/app/agentkit/react/data-chart-layout.ts +25 -0
- package/src/app/agentkit/react/index.ts +1 -0
- package/src/app/agentkit/react/root.tsx +4 -0
- package/src/app/agentkit/react/styles.css +18 -0
- package/src/app/agentkit/react/transcript-layout.ts +2 -0
- package/src/app/chat/AgentKitAssistantChat.tsx +2 -0
- package/src/app/chat/MultiTabAssistantChat.spec.tsx +270 -3
- package/src/app/chat/MultiTabAssistantChat.tsx +79 -156
- package/src/app/chat/agentkit-chat/action-widget.tsx +3 -0
- package/src/app/chat/agentkit-chat/rail.ts +11 -0
- package/src/app/chat/chat/index.ts +10 -0
- package/src/app/chat/chat/surface-types.ts +3 -0
- package/src/app/chat/chat/tool-call-display.spec.tsx +29 -0
- package/src/app/chat/chat/tool-call-display.tsx +7 -0
- package/src/app/chat/chat/tool-render-registry.tsx +3 -0
- package/src/app/chat/chat/widgets/DataChartRenderer.spec.tsx +377 -17
- package/src/app/chat/chat/widgets/DataChartRenderer.tsx +232 -63
- package/src/app/chat/chat/widgets/DataChartWidget.spec.tsx +67 -0
- package/src/app/chat/chat/widgets/DataChartWidget.tsx +39 -5
- package/src/app/chat/chat/widgets/DataInsightsWidget.tsx +13 -2
- package/src/app/chat/chat/widgets/DataWidgetDisplayControls.tsx +6 -4
- package/src/app/chat/chat/widgets/builtin-tool-renderers.tsx +5 -1
- package/src/app/chat/chat-first/chat-history.spec.tsx +79 -0
- package/src/app/chat/chat-first/chat-history.tsx +75 -22
- package/src/app/chat/ownership-history/ChatOwnerAvatar.tsx +27 -0
- package/src/app/chat/ownership-history/index.tsx +67 -0
- package/src/app/chat/ownership-history/ownership-history.spec.tsx +3146 -0
- package/src/app/chat/ownership-history/rows.ts +156 -0
- package/src/app/chat/ownership-history/useOwnershipChatHistory.ts +709 -0
- package/src/app/i18n/catalogs/ar-SA.ts +3 -0
- package/src/app/i18n/catalogs/de-DE.ts +3 -0
- package/src/app/i18n/catalogs/en-US.ts +3 -0
- package/src/app/i18n/catalogs/es-ES.ts +3 -0
- package/src/app/i18n/catalogs/fr-FR.ts +3 -0
- package/src/app/i18n/catalogs/hi-IN.ts +3 -0
- package/src/app/i18n/catalogs/ja-JP.ts +3 -0
- package/src/app/i18n/catalogs/ko-KR.ts +3 -0
- package/src/app/i18n/catalogs/pt-BR.ts +3 -0
- package/src/app/i18n/catalogs/zh-CN.ts +3 -0
- package/src/app/i18n/catalogs/zh-TW.ts +3 -0
- package/src/app/sharing/ShareButton.tsx +12 -10
- package/src/chat-history/ChatHistoryList.spec.tsx +51 -0
- package/src/chat-history/ChatHistoryList.tsx +87 -26
- package/src/chat-history/ChatHistoryRailSections.spec.tsx +12 -4
- package/src/chat-history.css +109 -9
|
@@ -1,5 +1,16 @@
|
|
|
1
1
|
export {
|
|
2
2
|
useChatThreads,
|
|
3
|
+
useChatThreadList,
|
|
4
|
+
notifyChatThreadsUpdated,
|
|
5
|
+
type ChatThreadListRow,
|
|
6
|
+
type ChatThreadAccessBasis,
|
|
7
|
+
type ChatThreadOwnership,
|
|
8
|
+
type ChatThreadVisibility,
|
|
9
|
+
type ChatThreadListRefreshOutcome,
|
|
10
|
+
type ChatThreadScope,
|
|
11
|
+
type UseChatThreadsOptions,
|
|
12
|
+
type UseChatThreadListOptions,
|
|
13
|
+
type UseChatThreadListResult,
|
|
3
14
|
type ChatThreadSummary,
|
|
4
15
|
} from "@agent-native/core/client/agent-chat";
|
|
5
16
|
export {
|
|
@@ -54,6 +54,7 @@ export type {
|
|
|
54
54
|
AgentChatSurfaceKind,
|
|
55
55
|
} from "./surface-types.js";
|
|
56
56
|
export type { AgentChatTranscriptLayout } from "../../agentkit/react/transcript-layout.js";
|
|
57
|
+
export type { AgentChatDataChartLayout } from "../../agentkit/react/data-chart-layout.js";
|
|
57
58
|
export type { AgentChatWorkedForRowLayout } from "../../agentkit/react/transcript-layout.js";
|
|
58
59
|
export type {
|
|
59
60
|
MultiTabAssistantChatProps,
|
|
@@ -222,6 +223,15 @@ export {
|
|
|
222
223
|
} from "@agent-native/core/client/agent-chat";
|
|
223
224
|
export {
|
|
224
225
|
useChatThreads,
|
|
226
|
+
useChatThreadList,
|
|
227
|
+
notifyChatThreadsUpdated,
|
|
228
|
+
type ChatThreadListRow,
|
|
229
|
+
type ChatThreadAccessBasis,
|
|
230
|
+
type ChatThreadOwnership,
|
|
231
|
+
type ChatThreadVisibility,
|
|
232
|
+
type ChatThreadListRefreshOutcome,
|
|
233
|
+
type UseChatThreadListOptions,
|
|
234
|
+
type UseChatThreadListResult,
|
|
225
235
|
type ChatThreadScope,
|
|
226
236
|
type ChatThreadSnapshot,
|
|
227
237
|
type ChatThreadSummary,
|
|
@@ -27,6 +27,7 @@ import type { ExportedMessageRepository } from "@assistant-ui/react";
|
|
|
27
27
|
import type * as React from "react";
|
|
28
28
|
|
|
29
29
|
import type { AgentKitWorkedForSummaryRenderProps } from "../../agentkit/react/context.js";
|
|
30
|
+
import type { AgentChatDataChartLayout } from "../../agentkit/react/data-chart-layout.js";
|
|
30
31
|
import type { AgentMessageContentOrderResolver } from "../../agentkit/react/message-content-order.js";
|
|
31
32
|
import type {
|
|
32
33
|
AgentChatTranscriptLayout,
|
|
@@ -244,6 +245,8 @@ export interface AssistantChatProps {
|
|
|
244
245
|
emptyStateFooter?: React.ReactNode;
|
|
245
246
|
/** Optional transcript geometry. Omitted values keep the shared defaults. */
|
|
246
247
|
transcriptLayout?: AgentChatTranscriptLayout;
|
|
248
|
+
/** Optional sizing and typography for built-in chat data charts. */
|
|
249
|
+
dataChartLayout?: AgentChatDataChartLayout;
|
|
247
250
|
/** Optional elapsed-work row geometry. Omitted values keep the shared defaults. */
|
|
248
251
|
workedForRowLayout?: AgentChatWorkedForRowLayout;
|
|
249
252
|
/** Optional duration text for the shared completed-work row. */
|
|
@@ -352,6 +352,35 @@ describe("ToolCallDisplay native renderers", () => {
|
|
|
352
352
|
expect(container.textContent).not.toContain("Ada");
|
|
353
353
|
});
|
|
354
354
|
|
|
355
|
+
it("passes the app chart layout to regular tool renderers", () => {
|
|
356
|
+
const layout = { maxWidth: 760, labelFontSize: 14 };
|
|
357
|
+
registerToolRenderer({
|
|
358
|
+
id: "app.chart-layout",
|
|
359
|
+
match: "app-chart-layout",
|
|
360
|
+
Component: ({ context }) => (
|
|
361
|
+
<div data-chart-layout-width={context.dataChartLayout?.maxWidth} />
|
|
362
|
+
),
|
|
363
|
+
});
|
|
364
|
+
|
|
365
|
+
act(() => {
|
|
366
|
+
root.render(
|
|
367
|
+
<ToolCallDisplay
|
|
368
|
+
toolName="app-chart-layout"
|
|
369
|
+
args={{}}
|
|
370
|
+
result="ready"
|
|
371
|
+
isRunning={false}
|
|
372
|
+
dataChartLayout={layout}
|
|
373
|
+
/>,
|
|
374
|
+
);
|
|
375
|
+
});
|
|
376
|
+
|
|
377
|
+
expect(
|
|
378
|
+
container
|
|
379
|
+
.querySelector("[data-chart-layout-width]")
|
|
380
|
+
?.getAttribute("data-chart-layout-width"),
|
|
381
|
+
).toBe("760");
|
|
382
|
+
});
|
|
383
|
+
|
|
355
384
|
it("renders table-only data insight payloads without a chart", async () => {
|
|
356
385
|
await act(async () => {
|
|
357
386
|
root.render(
|
|
@@ -47,6 +47,7 @@ import React, {
|
|
|
47
47
|
useRef,
|
|
48
48
|
} from "react";
|
|
49
49
|
|
|
50
|
+
import type { AgentChatDataChartLayout } from "../../agentkit/react/data-chart-layout.js";
|
|
50
51
|
import {
|
|
51
52
|
workedForRowChevronStyle,
|
|
52
53
|
workedForRowSummaryStyle,
|
|
@@ -647,6 +648,7 @@ export function ToolCallDisplay({
|
|
|
647
648
|
isLatestRunning = isRunning,
|
|
648
649
|
isActiveTail,
|
|
649
650
|
workedForRowLayout,
|
|
651
|
+
dataChartLayout,
|
|
650
652
|
formatWorkedForDuration,
|
|
651
653
|
}: {
|
|
652
654
|
toolName: string;
|
|
@@ -670,6 +672,7 @@ export function ToolCallDisplay({
|
|
|
670
672
|
repeatCount?: number;
|
|
671
673
|
isActiveTail?: boolean;
|
|
672
674
|
workedForRowLayout?: AgentChatWorkedForRowLayout;
|
|
675
|
+
dataChartLayout?: AgentChatDataChartLayout;
|
|
673
676
|
formatWorkedForDuration?: (durationMs: number) => string;
|
|
674
677
|
/** @deprecated Use isActiveTail. */
|
|
675
678
|
isLatestRunning?: boolean;
|
|
@@ -758,6 +761,7 @@ export function ToolCallDisplay({
|
|
|
758
761
|
repeatCount={repeatCount}
|
|
759
762
|
context={builderContext}
|
|
760
763
|
workedForRowLayout={workedForRowLayout}
|
|
764
|
+
dataChartLayout={dataChartLayout}
|
|
761
765
|
formatWorkedForDuration={formatWorkedForDuration}
|
|
762
766
|
/>,
|
|
763
767
|
);
|
|
@@ -782,6 +786,7 @@ function ToolCallDisplayGeneric({
|
|
|
782
786
|
repeatCount,
|
|
783
787
|
context,
|
|
784
788
|
workedForRowLayout,
|
|
789
|
+
dataChartLayout,
|
|
785
790
|
formatWorkedForDuration,
|
|
786
791
|
}: {
|
|
787
792
|
toolName: string;
|
|
@@ -805,6 +810,7 @@ function ToolCallDisplayGeneric({
|
|
|
805
810
|
repeatCount?: number;
|
|
806
811
|
context?: string;
|
|
807
812
|
workedForRowLayout?: AgentChatWorkedForRowLayout;
|
|
813
|
+
dataChartLayout?: AgentChatDataChartLayout;
|
|
808
814
|
formatWorkedForDuration?: (durationMs: number) => string;
|
|
809
815
|
}) {
|
|
810
816
|
const t = useT();
|
|
@@ -919,6 +925,7 @@ function ToolCallDisplayGeneric({
|
|
|
919
925
|
resultJson: parsedResult,
|
|
920
926
|
isRunning,
|
|
921
927
|
isActiveTail,
|
|
928
|
+
dataChartLayout,
|
|
922
929
|
...(typeof toolCallId === "string"
|
|
923
930
|
? { widgetId: `${toolCallId}:chat-ui` }
|
|
924
931
|
: {}),
|
|
@@ -1,6 +1,8 @@
|
|
|
1
1
|
import type { ActionChatUIConfig } from "@agent-native/core/action-ui";
|
|
2
2
|
import type { ComponentType } from "react";
|
|
3
3
|
|
|
4
|
+
import type { AgentChatDataChartLayout } from "../../agentkit/react/data-chart-layout.js";
|
|
5
|
+
|
|
4
6
|
export interface ToolRendererContext {
|
|
5
7
|
toolName: string;
|
|
6
8
|
args: Record<string, unknown>;
|
|
@@ -15,6 +17,7 @@ export interface ToolRendererContext {
|
|
|
15
17
|
isRunning: boolean;
|
|
16
18
|
isActiveTail?: boolean;
|
|
17
19
|
chatUI?: ActionChatUIConfig;
|
|
20
|
+
dataChartLayout?: AgentChatDataChartLayout;
|
|
18
21
|
}
|
|
19
22
|
|
|
20
23
|
export interface ToolRendererProps {
|
|
@@ -59,6 +59,302 @@ describe("DataChartRenderer theme colors", () => {
|
|
|
59
59
|
expect(markup).toContain('stroke="hsl(var(--chart-grid, var(--border)))"');
|
|
60
60
|
});
|
|
61
61
|
|
|
62
|
+
it("applies app chart sizing, plot insets, typography, and integer tick labels", () => {
|
|
63
|
+
const markup = renderToStaticMarkup(
|
|
64
|
+
<DataChartRenderer
|
|
65
|
+
chart={{
|
|
66
|
+
type: "line",
|
|
67
|
+
xKey: "day",
|
|
68
|
+
series: [{ key: "value", label: "Value" }],
|
|
69
|
+
data: [
|
|
70
|
+
{ day: "First", value: 0 },
|
|
71
|
+
{ day: "Last", value: 1 },
|
|
72
|
+
],
|
|
73
|
+
valueAxisTickInterval: 0.1,
|
|
74
|
+
}}
|
|
75
|
+
layout={{
|
|
76
|
+
plotInsets: { top: 6, right: 8, bottom: 29 },
|
|
77
|
+
labelFontFamily: "IBM Plex Sans",
|
|
78
|
+
labelFontSize: 14,
|
|
79
|
+
labelTone: "foreground",
|
|
80
|
+
}}
|
|
81
|
+
/>,
|
|
82
|
+
);
|
|
83
|
+
|
|
84
|
+
expect(markup).toContain('font-family="IBM Plex Sans"');
|
|
85
|
+
expect(markup).toContain('font-size="14"');
|
|
86
|
+
expect(markup).toMatch(
|
|
87
|
+
/<text[^>]*fill="hsl\(var\(--foreground\)\)"[^>]*>First<\/text>/,
|
|
88
|
+
);
|
|
89
|
+
expect(markup).toMatch(/<line x1="[^"]+" x2="632" y1="6" y2="6"/);
|
|
90
|
+
expect(markup).toMatch(/<line x1="[^"]+" x2="632" y1="331" y2="331"/);
|
|
91
|
+
expect(markup).toContain(">1</text>");
|
|
92
|
+
expect(markup).not.toContain(">1.0</text>");
|
|
93
|
+
});
|
|
94
|
+
|
|
95
|
+
it("preserves one-decimal ticks without an app layout override", () => {
|
|
96
|
+
const markup = renderToStaticMarkup(
|
|
97
|
+
<DataChartRenderer
|
|
98
|
+
chart={{
|
|
99
|
+
type: "line",
|
|
100
|
+
xKey: "day",
|
|
101
|
+
series: [{ key: "value", label: "Value" }],
|
|
102
|
+
data: [
|
|
103
|
+
{ day: "First", value: 0 },
|
|
104
|
+
{ day: "Last", value: 1 },
|
|
105
|
+
],
|
|
106
|
+
valueAxisTickInterval: 0.1,
|
|
107
|
+
}}
|
|
108
|
+
/>,
|
|
109
|
+
);
|
|
110
|
+
|
|
111
|
+
expect(markup).toContain(">0.0</text>");
|
|
112
|
+
expect(markup).toContain(">1.0</text>");
|
|
113
|
+
});
|
|
114
|
+
|
|
115
|
+
it("keeps positive value labels outside horizontal bars with a right inset", () => {
|
|
116
|
+
const markup = renderToStaticMarkup(
|
|
117
|
+
<DataChartRenderer
|
|
118
|
+
chart={{
|
|
119
|
+
type: "bar",
|
|
120
|
+
layout: "horizontal",
|
|
121
|
+
xKey: "day",
|
|
122
|
+
series: [{ key: "value", label: "Value" }],
|
|
123
|
+
data: [{ day: "Monday", value: 3 }],
|
|
124
|
+
showValueLabels: true,
|
|
125
|
+
valueAxisTickInterval: 0.2,
|
|
126
|
+
}}
|
|
127
|
+
layout={{ plotInsets: { right: 8 } }}
|
|
128
|
+
/>,
|
|
129
|
+
);
|
|
130
|
+
|
|
131
|
+
const plotRightEdge = Number(
|
|
132
|
+
markup.match(/<line x1="[\d.]+" x2="([\d.]+)" y1="14" y2="326"/)?.[1],
|
|
133
|
+
);
|
|
134
|
+
expect(plotRightEdge).toBeLessThanOrEqual(632);
|
|
135
|
+
expect(markup).toContain('fill="hsl(var(--muted-foreground))"');
|
|
136
|
+
const maximumBarValueLabel = markup.match(
|
|
137
|
+
/<text[^>]*x="([\d.]+)" y="[^"]+" fill="hsl\(var\(--muted-foreground\)\)" font-size="11" font-weight="400" text-anchor="start">3<\/text>/,
|
|
138
|
+
);
|
|
139
|
+
expect(maximumBarValueLabel).not.toBeNull();
|
|
140
|
+
expect(Number(maximumBarValueLabel?.[1])).toBeGreaterThan(plotRightEdge);
|
|
141
|
+
expect(Number(maximumBarValueLabel?.[1]) + 8).toBeLessThanOrEqual(640);
|
|
142
|
+
});
|
|
143
|
+
|
|
144
|
+
it("rejects inset combinations that leave no positive plot area", () => {
|
|
145
|
+
const renderWithInsets = (plotInsets: { right?: number; top?: number }) =>
|
|
146
|
+
renderToStaticMarkup(
|
|
147
|
+
<DataChartRenderer
|
|
148
|
+
chart={{
|
|
149
|
+
type: "line",
|
|
150
|
+
xKey: "day",
|
|
151
|
+
series: [{ key: "value", label: "Value" }],
|
|
152
|
+
data: [{ day: "Monday", value: 1 }],
|
|
153
|
+
}}
|
|
154
|
+
layout={{ plotInsets }}
|
|
155
|
+
/>,
|
|
156
|
+
);
|
|
157
|
+
|
|
158
|
+
expect(() => renderWithInsets({ right: 600 })).toThrow(
|
|
159
|
+
"positive chart plot area",
|
|
160
|
+
);
|
|
161
|
+
expect(() => renderWithInsets({ top: 400 })).toThrow(
|
|
162
|
+
"positive chart plot area",
|
|
163
|
+
);
|
|
164
|
+
});
|
|
165
|
+
|
|
166
|
+
it("keeps a vertical bar chart's final category label inside the SVG", () => {
|
|
167
|
+
const markup = renderToStaticMarkup(
|
|
168
|
+
<DataChartRenderer
|
|
169
|
+
chart={{
|
|
170
|
+
type: "bar",
|
|
171
|
+
xKey: "day",
|
|
172
|
+
series: [{ key: "value", label: "Value" }],
|
|
173
|
+
data: Array.from({ length: 10 }, (_, index) => ({
|
|
174
|
+
day: index === 9 ? "Last category label" : `Day ${index}`,
|
|
175
|
+
value: index,
|
|
176
|
+
})),
|
|
177
|
+
}}
|
|
178
|
+
layout={{ plotInsets: { right: 8 } }}
|
|
179
|
+
/>,
|
|
180
|
+
);
|
|
181
|
+
const finalCategory = markup.match(
|
|
182
|
+
/<text[^>]*x="([\d.]+)" y="348"[^>]*text-anchor="end">Last category\.\.\.<\/text>/,
|
|
183
|
+
);
|
|
184
|
+
|
|
185
|
+
expect(finalCategory).not.toBeNull();
|
|
186
|
+
expect(Number(finalCategory?.[1])).toBeLessThanOrEqual(632);
|
|
187
|
+
});
|
|
188
|
+
|
|
189
|
+
it("keeps a long first category label inside the SVG", () => {
|
|
190
|
+
const markup = renderToStaticMarkup(
|
|
191
|
+
<DataChartRenderer
|
|
192
|
+
chart={{
|
|
193
|
+
type: "line",
|
|
194
|
+
xKey: "day",
|
|
195
|
+
series: [{ key: "value", label: "Value" }],
|
|
196
|
+
data: [
|
|
197
|
+
{ day: "First category label", value: 0 },
|
|
198
|
+
{ day: "Last", value: 1 },
|
|
199
|
+
],
|
|
200
|
+
}}
|
|
201
|
+
/>,
|
|
202
|
+
);
|
|
203
|
+
|
|
204
|
+
expect(markup).toMatch(
|
|
205
|
+
/<text[^>]*x="46" y="348"[^>]*text-anchor="start">First categor\.\.\.<\/text>/,
|
|
206
|
+
);
|
|
207
|
+
});
|
|
208
|
+
|
|
209
|
+
it("keeps top vertical-bar value labels above the bar at a zero inset", () => {
|
|
210
|
+
const markup = renderToStaticMarkup(
|
|
211
|
+
<DataChartRenderer
|
|
212
|
+
chart={{
|
|
213
|
+
type: "bar",
|
|
214
|
+
xKey: "day",
|
|
215
|
+
series: [{ key: "value", label: "Value" }],
|
|
216
|
+
data: [{ day: "Monday", value: 1 }],
|
|
217
|
+
showValueLabels: true,
|
|
218
|
+
}}
|
|
219
|
+
layout={{ plotInsets: { top: 0 } }}
|
|
220
|
+
/>,
|
|
221
|
+
);
|
|
222
|
+
const valueLabel = markup.match(
|
|
223
|
+
/<text(?=[^>]*data-value-label="true")(?=[^>]*>1<\/text>)[^>]*y="([\d.]+)"[^>]*>/,
|
|
224
|
+
);
|
|
225
|
+
const bar = markup.match(/<rect[^>]*y="([\d.]+)"/);
|
|
226
|
+
|
|
227
|
+
expect(valueLabel).not.toBeNull();
|
|
228
|
+
expect(Number(valueLabel?.[1])).toBeGreaterThanOrEqual(11);
|
|
229
|
+
expect(bar).not.toBeNull();
|
|
230
|
+
expect(Number(bar?.[1]) - Number(valueLabel?.[1])).toBeGreaterThanOrEqual(
|
|
231
|
+
5,
|
|
232
|
+
);
|
|
233
|
+
});
|
|
234
|
+
|
|
235
|
+
it("keeps final vertical-bar value labels inside the SVG at a small right inset", () => {
|
|
236
|
+
const markup = renderToStaticMarkup(
|
|
237
|
+
<DataChartRenderer
|
|
238
|
+
chart={{
|
|
239
|
+
type: "bar",
|
|
240
|
+
xKey: "day",
|
|
241
|
+
series: [{ key: "value", label: "Value" }],
|
|
242
|
+
data: Array.from({ length: 60 }, (_, index) => ({
|
|
243
|
+
day: `Day ${index}`,
|
|
244
|
+
value: index === 59 ? 1_234_567 : 0,
|
|
245
|
+
})),
|
|
246
|
+
showValueLabels: true,
|
|
247
|
+
}}
|
|
248
|
+
layout={{ plotInsets: { right: 8 } }}
|
|
249
|
+
/>,
|
|
250
|
+
);
|
|
251
|
+
const valueLabels = Array.from(
|
|
252
|
+
markup.matchAll(
|
|
253
|
+
/<text(?=[^>]*data-value-label="true")([^>]*)>([^<]+)<\/text>/g,
|
|
254
|
+
),
|
|
255
|
+
);
|
|
256
|
+
const finalValueLabel = valueLabels.at(-1);
|
|
257
|
+
const finalLabelX = Number(
|
|
258
|
+
finalValueLabel?.[1].match(/\bx="([\d.]+)"/)?.[1],
|
|
259
|
+
);
|
|
260
|
+
const finalLabelWidth =
|
|
261
|
+
Array.from(finalValueLabel?.[2] ?? "").length * 11 * 0.65;
|
|
262
|
+
|
|
263
|
+
expect(finalValueLabel).toBeDefined();
|
|
264
|
+
expect(finalLabelX + finalLabelWidth / 2).toBeLessThanOrEqual(640);
|
|
265
|
+
});
|
|
266
|
+
|
|
267
|
+
it("preserves the top value-axis label when the configured top inset is zero", () => {
|
|
268
|
+
const markup = renderToStaticMarkup(
|
|
269
|
+
<DataChartRenderer
|
|
270
|
+
chart={{
|
|
271
|
+
type: "line",
|
|
272
|
+
xKey: "day",
|
|
273
|
+
series: [{ key: "value", label: "Value" }],
|
|
274
|
+
data: [
|
|
275
|
+
{ day: "Monday", value: 0 },
|
|
276
|
+
{ day: "Tuesday", value: 1 },
|
|
277
|
+
],
|
|
278
|
+
valueAxisTickInterval: 0.5,
|
|
279
|
+
}}
|
|
280
|
+
layout={{ labelFontSize: 12, plotInsets: { top: 0 } }}
|
|
281
|
+
/>,
|
|
282
|
+
);
|
|
283
|
+
const topTick = markup.match(
|
|
284
|
+
/<text(?=[^>]*data-value-axis-tick="true")[^>]*y="([\d.]+)"[^>]*>1<\/text>/,
|
|
285
|
+
);
|
|
286
|
+
|
|
287
|
+
expect(topTick).not.toBeNull();
|
|
288
|
+
expect(Number(topTick?.[1])).toBeGreaterThanOrEqual(12);
|
|
289
|
+
});
|
|
290
|
+
|
|
291
|
+
it("keeps horizontal axis tick labels inside the chart at zero edge insets", () => {
|
|
292
|
+
const markup = renderToStaticMarkup(
|
|
293
|
+
<DataChartRenderer
|
|
294
|
+
chart={{
|
|
295
|
+
type: "bar",
|
|
296
|
+
layout: "horizontal",
|
|
297
|
+
xKey: "day",
|
|
298
|
+
series: [{ key: "value", label: "Value" }],
|
|
299
|
+
data: [{ day: "Monday", value: 3 }],
|
|
300
|
+
valueAxisTickInterval: 1,
|
|
301
|
+
xAxisTitle: "Value",
|
|
302
|
+
}}
|
|
303
|
+
layout={{ plotInsets: { right: 0, bottom: 0 } }}
|
|
304
|
+
/>,
|
|
305
|
+
);
|
|
306
|
+
const finalTick = Array.from(
|
|
307
|
+
markup.matchAll(
|
|
308
|
+
/<text[^>]*x="([\d.]+)" y="([\d.]+)"[^>]*>([^<]+)<\/text>/g,
|
|
309
|
+
),
|
|
310
|
+
)
|
|
311
|
+
.filter((match) => match[3] === "3")
|
|
312
|
+
.slice(-1)[0];
|
|
313
|
+
|
|
314
|
+
expect(finalTick).toBeDefined();
|
|
315
|
+
expect(Number(finalTick?.[1])).toBeLessThan(640);
|
|
316
|
+
expect(Number(finalTick?.[1]) + 4).toBeLessThanOrEqual(640);
|
|
317
|
+
expect(Number(finalTick?.[2])).toBeLessThan(340);
|
|
318
|
+
});
|
|
319
|
+
|
|
320
|
+
it("keeps vertical axis ticks and category labels clear at a zero bottom inset", () => {
|
|
321
|
+
const markup = renderToStaticMarkup(
|
|
322
|
+
<DataChartRenderer
|
|
323
|
+
chart={{
|
|
324
|
+
type: "line",
|
|
325
|
+
xKey: "day",
|
|
326
|
+
series: [{ key: "value", label: "Value" }],
|
|
327
|
+
data: [
|
|
328
|
+
{ day: "Monday", value: 0 },
|
|
329
|
+
{ day: "Tuesday", value: 1 },
|
|
330
|
+
],
|
|
331
|
+
valueAxisTickInterval: 0.5,
|
|
332
|
+
}}
|
|
333
|
+
layout={{ labelFontSize: 12, plotInsets: { bottom: 0 } }}
|
|
334
|
+
/>,
|
|
335
|
+
);
|
|
336
|
+
const minimumTick = markup.match(
|
|
337
|
+
/<text(?=[^>]*data-value-axis-tick="true")(?=[^>]*>0<\/text>)[^>]*y="([\d.]+)"/,
|
|
338
|
+
);
|
|
339
|
+
const categoryLabel = markup.match(
|
|
340
|
+
/<text[^>]*y="([\d.]+)"[^>]*>Tuesday<\/text>/,
|
|
341
|
+
);
|
|
342
|
+
const plotBaseline = Math.max(
|
|
343
|
+
...Array.from(
|
|
344
|
+
markup.matchAll(/<line[^>]*y1="([\d.]+)" y2="([\d.]+)"/g),
|
|
345
|
+
(match) =>
|
|
346
|
+
match[1] === match[2] ? Number(match[1]) : Number.NEGATIVE_INFINITY,
|
|
347
|
+
),
|
|
348
|
+
);
|
|
349
|
+
|
|
350
|
+
expect(minimumTick).not.toBeNull();
|
|
351
|
+
expect(Number(minimumTick?.[1]) + 3).toBeLessThanOrEqual(360);
|
|
352
|
+
expect(categoryLabel).not.toBeNull();
|
|
353
|
+
expect(Number(categoryLabel?.[1]) - plotBaseline).toBeGreaterThanOrEqual(
|
|
354
|
+
12,
|
|
355
|
+
);
|
|
356
|
+
});
|
|
357
|
+
|
|
62
358
|
it("uses the requested chart content height", () => {
|
|
63
359
|
const markup = renderToStaticMarkup(
|
|
64
360
|
<DataChartRenderer chart={{ ...chart, contentHeight: 400 }} />,
|
|
@@ -217,7 +513,7 @@ describe("DataChartRenderer theme colors", () => {
|
|
|
217
513
|
expect(numericLabels.at(-1)).toBe("99");
|
|
218
514
|
const tickLabelPositions = Array.from(
|
|
219
515
|
markup.matchAll(
|
|
220
|
-
/<text
|
|
516
|
+
/<text[^>]*x="([\d.]+)"[^>]*text-anchor="middle">\d+<\/text>/g,
|
|
221
517
|
),
|
|
222
518
|
(match) => Number(match[1]),
|
|
223
519
|
);
|
|
@@ -290,7 +586,7 @@ describe("DataChartRenderer theme colors", () => {
|
|
|
290
586
|
);
|
|
291
587
|
const valueTickLabels = Array.from(
|
|
292
588
|
markup.matchAll(
|
|
293
|
-
/<text
|
|
589
|
+
/<text[^>]*x="([\d.]+)" y="340"[^>]*text-anchor="middle">([^<]+)<\/text>/g,
|
|
294
590
|
),
|
|
295
591
|
(match) => ({ x: Number(match[1]), label: match[2] ?? "" }),
|
|
296
592
|
);
|
|
@@ -321,7 +617,7 @@ describe("DataChartRenderer theme colors", () => {
|
|
|
321
617
|
);
|
|
322
618
|
const valueTickLabels = Array.from(
|
|
323
619
|
markup.matchAll(
|
|
324
|
-
/<text
|
|
620
|
+
/<text[^>]*x="([\d.]+)" y="340"[^>]*text-anchor="middle">([^<]+)<\/text>/g,
|
|
325
621
|
),
|
|
326
622
|
(match) => ({ x: Number(match[1]), label: match[2] ?? "" }),
|
|
327
623
|
);
|
|
@@ -344,7 +640,7 @@ describe("DataChartRenderer theme colors", () => {
|
|
|
344
640
|
);
|
|
345
641
|
const valueTickLabels = Array.from(
|
|
346
642
|
markup.matchAll(
|
|
347
|
-
/<text
|
|
643
|
+
/<text[^>]*x="([\d.]+)" y="340"[^>]*text-anchor="middle">([^<]+)<\/text>/g,
|
|
348
644
|
),
|
|
349
645
|
(match) => ({ x: Number(match[1]), label: match[2] ?? "" }),
|
|
350
646
|
);
|
|
@@ -379,7 +675,7 @@ describe("DataChartRenderer theme colors", () => {
|
|
|
379
675
|
);
|
|
380
676
|
const valueLabels = Array.from(
|
|
381
677
|
markup.matchAll(
|
|
382
|
-
/<text
|
|
678
|
+
/<text[^>]*x="([\d.-]+)" y="([\d.]+)"[^>]*font-size="24"[^>]*text-anchor="(start|end)">([^<]+)<\/text>/g,
|
|
383
679
|
),
|
|
384
680
|
(match) => ({
|
|
385
681
|
x: Number(match[1]),
|
|
@@ -426,7 +722,9 @@ describe("DataChartRenderer theme colors", () => {
|
|
|
426
722
|
/>,
|
|
427
723
|
);
|
|
428
724
|
const tickLabels = Array.from(
|
|
429
|
-
markup.matchAll(
|
|
725
|
+
markup.matchAll(
|
|
726
|
+
/<text(?=[^>]*data-value-axis-tick="true")[^>]*>([^<]+)<\/text>/g,
|
|
727
|
+
),
|
|
430
728
|
(match) => match[1],
|
|
431
729
|
);
|
|
432
730
|
|
|
@@ -454,7 +752,9 @@ describe("DataChartRenderer theme colors", () => {
|
|
|
454
752
|
/>,
|
|
455
753
|
);
|
|
456
754
|
const tickLabels = Array.from(
|
|
457
|
-
markup.matchAll(
|
|
755
|
+
markup.matchAll(
|
|
756
|
+
/<text(?=[^>]*data-value-axis-tick="true")[^>]*>([^<]+)<\/text>/g,
|
|
757
|
+
),
|
|
458
758
|
(match) => match[1],
|
|
459
759
|
);
|
|
460
760
|
|
|
@@ -466,6 +766,40 @@ describe("DataChartRenderer theme colors", () => {
|
|
|
466
766
|
]);
|
|
467
767
|
});
|
|
468
768
|
|
|
769
|
+
it("keeps integer data labels compact while axis ticks retain interval precision", () => {
|
|
770
|
+
const markup = renderToStaticMarkup(
|
|
771
|
+
<DataChartRenderer
|
|
772
|
+
chart={{
|
|
773
|
+
type: "line",
|
|
774
|
+
xKey: "day",
|
|
775
|
+
series: [{ key: "value", label: "Value" }],
|
|
776
|
+
data: [
|
|
777
|
+
{ day: "Monday", value: 0 },
|
|
778
|
+
{ day: "Tuesday", value: 7 },
|
|
779
|
+
],
|
|
780
|
+
showValueLabels: true,
|
|
781
|
+
valueAxisTickInterval: 0.5,
|
|
782
|
+
}}
|
|
783
|
+
/>,
|
|
784
|
+
);
|
|
785
|
+
const axisLabels = Array.from(
|
|
786
|
+
markup.matchAll(
|
|
787
|
+
/<text(?=[^>]*data-value-axis-tick="true")[^>]*>([^<]+)<\/text>/g,
|
|
788
|
+
),
|
|
789
|
+
(match) => match[1],
|
|
790
|
+
);
|
|
791
|
+
const valueLabels = Array.from(
|
|
792
|
+
markup.matchAll(
|
|
793
|
+
/<text(?=[^>]*data-value-label="true")[^>]*>([^<]+)<\/text>/g,
|
|
794
|
+
),
|
|
795
|
+
(match) => match[1],
|
|
796
|
+
);
|
|
797
|
+
|
|
798
|
+
expect(axisLabels).toContain("7.0");
|
|
799
|
+
expect(valueLabels).toContain("7");
|
|
800
|
+
expect(valueLabels).not.toContain("7.0");
|
|
801
|
+
});
|
|
802
|
+
|
|
469
803
|
it("compacts wide vertical ticks while keeping them clear of the axis title", () => {
|
|
470
804
|
const markup = renderToStaticMarkup(
|
|
471
805
|
<DataChartRenderer
|
|
@@ -484,7 +818,7 @@ describe("DataChartRenderer theme colors", () => {
|
|
|
484
818
|
);
|
|
485
819
|
const tickLabels = Array.from(
|
|
486
820
|
markup.matchAll(
|
|
487
|
-
/<text
|
|
821
|
+
/<text[^>]*x="([\d.]+)" y="([\d.]+)"[^>]*text-anchor="end">([^<]+)<\/text>/g,
|
|
488
822
|
),
|
|
489
823
|
(match) => ({
|
|
490
824
|
x: Number(match[1]),
|
|
@@ -503,7 +837,7 @@ describe("DataChartRenderer theme colors", () => {
|
|
|
503
837
|
Number(plotBounds?.[2]) - Number(plotBounds?.[1]),
|
|
504
838
|
).toBeGreaterThanOrEqual(32);
|
|
505
839
|
const title = markup.match(
|
|
506
|
-
/<text
|
|
840
|
+
/<text[^>]*x="([\d.]+)" y="[\d.]+" transform="rotate\(-90 [\d.]+ [\d.]+\)"[^>]*font-size="([\d.]+)"[^>]*>Revenue<\/text>/,
|
|
507
841
|
);
|
|
508
842
|
expect(title).not.toBeNull();
|
|
509
843
|
const titleRight = Number(title?.[1]) + Number(title?.[2]) / 2;
|
|
@@ -563,7 +897,7 @@ describe("DataChartRenderer theme colors", () => {
|
|
|
563
897
|
);
|
|
564
898
|
const horizontalLabels = Array.from(
|
|
565
899
|
horizontalMarkup.matchAll(
|
|
566
|
-
/<text
|
|
900
|
+
/<text[^>]*x="([\d.]+)" y="340"[^>]*text-anchor="middle">([^<]+)<\/text>/g,
|
|
567
901
|
),
|
|
568
902
|
(match) => ({ x: Number(match[1]), label: match[2] ?? "" }),
|
|
569
903
|
);
|
|
@@ -602,7 +936,7 @@ describe("DataChartRenderer theme colors", () => {
|
|
|
602
936
|
);
|
|
603
937
|
const verticalLabels = Array.from(
|
|
604
938
|
verticalMarkup.matchAll(
|
|
605
|
-
/<text
|
|
939
|
+
/<text[^>]*x="[\d.]+" y="([\d.]+)"[^>]*text-anchor="end">([^<]+)<\/text>/g,
|
|
606
940
|
),
|
|
607
941
|
(match) => ({ y: Number(match[1]), label: match[2] ?? "" }),
|
|
608
942
|
).sort((first, second) => first.y - second.y);
|
|
@@ -619,6 +953,32 @@ describe("DataChartRenderer theme colors", () => {
|
|
|
619
953
|
).toBe(true);
|
|
620
954
|
});
|
|
621
955
|
|
|
956
|
+
it("keeps line value labels inside the canvas with no bottom inset", () => {
|
|
957
|
+
const markup = renderToStaticMarkup(
|
|
958
|
+
<DataChartRenderer
|
|
959
|
+
chart={{
|
|
960
|
+
type: "line",
|
|
961
|
+
xKey: "day",
|
|
962
|
+
series: [{ key: "value", label: "Value" }],
|
|
963
|
+
data: [
|
|
964
|
+
{ day: "First", value: 0 },
|
|
965
|
+
{ day: "Last", value: 1 },
|
|
966
|
+
],
|
|
967
|
+
showValueLabels: true,
|
|
968
|
+
}}
|
|
969
|
+
layout={{ plotInsets: { bottom: 0 } }}
|
|
970
|
+
/>,
|
|
971
|
+
);
|
|
972
|
+
const minimumValueLabelY = Array.from(
|
|
973
|
+
markup.matchAll(
|
|
974
|
+
/<text(?=[^>]*data-value-label="true")(?=[^>]*>0<\/text>)[^>]*y="([\d.]+)"[^>]*>/g,
|
|
975
|
+
),
|
|
976
|
+
(match) => Number(match[1]),
|
|
977
|
+
).filter((y) => y < 360);
|
|
978
|
+
|
|
979
|
+
expect(minimumValueLabelY.some((y) => y <= 356)).toBe(true);
|
|
980
|
+
});
|
|
981
|
+
|
|
622
982
|
it("reserves room for custom decimal value-axis labels", () => {
|
|
623
983
|
const markup = renderToStaticMarkup(
|
|
624
984
|
<DataChartRenderer
|
|
@@ -632,7 +992,7 @@ describe("DataChartRenderer theme colors", () => {
|
|
|
632
992
|
/>,
|
|
633
993
|
);
|
|
634
994
|
const tickLabels = Array.from(
|
|
635
|
-
markup.matchAll(/<text
|
|
995
|
+
markup.matchAll(/<text[^>]*x="([\d.]+)"[^>]*>(0\.000[123])<\/text>/g),
|
|
636
996
|
(match) => ({ x: Number(match[1]), label: match[2] }),
|
|
637
997
|
);
|
|
638
998
|
|
|
@@ -752,10 +1112,10 @@ describe("DataChartRenderer theme colors", () => {
|
|
|
752
1112
|
/>,
|
|
753
1113
|
);
|
|
754
1114
|
const categoryLabel = markup.match(
|
|
755
|
-
/<text
|
|
1115
|
+
/<text[^>]*x="([\d.]+)" y="[^"]+" fill="hsl\(var\(--muted-foreground\)\)" font-size="10" text-anchor="end">A very long c\.\.\.<\/text>/,
|
|
756
1116
|
);
|
|
757
1117
|
const largestValueLabel = markup.match(
|
|
758
|
-
/<text
|
|
1118
|
+
/<text[^>]*x="([\d.]+)" y="[^"]+" fill="hsl\(var\(--muted-foreground\)\)" font-size="11" font-weight="400" text-anchor="start">123,456<\/text>/,
|
|
759
1119
|
);
|
|
760
1120
|
|
|
761
1121
|
expect(categoryLabel).not.toBeNull();
|
|
@@ -779,10 +1139,10 @@ describe("DataChartRenderer theme colors", () => {
|
|
|
779
1139
|
);
|
|
780
1140
|
const negativeBarX = markup.match(/<rect x="([\d.]+)"/);
|
|
781
1141
|
const negativeBarLabel = markup.match(
|
|
782
|
-
/<text
|
|
1142
|
+
/<text[^>]*x="([\d.]+)"[^>]*text-anchor="end">-123,456<\/text>/,
|
|
783
1143
|
);
|
|
784
1144
|
const categoryLabel = markup.match(
|
|
785
|
-
/<text
|
|
1145
|
+
/<text[^>]*x="([\d.]+)" y="[^"]+" fill="hsl\(var\(--muted-foreground\)\)" font-size="11" text-anchor="end">Monday<\/text>/,
|
|
786
1146
|
);
|
|
787
1147
|
|
|
788
1148
|
expect(negativeBarX).not.toBeNull();
|
|
@@ -839,7 +1199,7 @@ describe("DataChartRenderer theme colors", () => {
|
|
|
839
1199
|
/>,
|
|
840
1200
|
);
|
|
841
1201
|
const finalValueLabel = markup.match(
|
|
842
|
-
/<text
|
|
1202
|
+
/<text[^>]*x="([\d.]+)" y="[\d.]+" fill="hsl\(var\(--muted-foreground\)\)" font-size="11" font-weight="400" text-anchor="end">123,456,789<\/text>/,
|
|
843
1203
|
);
|
|
844
1204
|
|
|
845
1205
|
expect(finalValueLabel).not.toBeNull();
|