@agent-native/toolkit 0.15.1 → 0.16.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/README.md +26 -0
- package/agent-native.eject.json +16 -0
- package/dist/composer/PromptComposer.d.ts +7 -1
- package/dist/composer/PromptComposer.d.ts.map +1 -1
- package/dist/composer/PromptComposer.js +2 -2
- package/dist/composer/PromptComposer.js.map +1 -1
- package/dist/composer/TiptapComposer.d.ts +19 -1
- package/dist/composer/TiptapComposer.d.ts.map +1 -1
- package/dist/composer/TiptapComposer.js +27 -4
- package/dist/composer/TiptapComposer.js.map +1 -1
- package/dist/data-grid/DataGrid.d.ts +92 -0
- package/dist/data-grid/DataGrid.d.ts.map +1 -0
- package/dist/data-grid/DataGrid.js +315 -0
- package/dist/data-grid/DataGrid.js.map +1 -0
- package/dist/data-grid/DataGrid.spec.d.ts +2 -0
- package/dist/data-grid/DataGrid.spec.d.ts.map +1 -0
- package/dist/data-grid/DataGrid.spec.js +134 -0
- package/dist/data-grid/DataGrid.spec.js.map +1 -0
- package/dist/data-grid/index.d.ts +2 -0
- package/dist/data-grid/index.d.ts.map +1 -0
- package/dist/data-grid/index.js +2 -0
- package/dist/data-grid/index.js.map +1 -0
- package/dist/sharing/SharePrimitives.d.ts +14 -3
- package/dist/sharing/SharePrimitives.d.ts.map +1 -1
- package/dist/sharing/SharePrimitives.js +11 -3
- package/dist/sharing/SharePrimitives.js.map +1 -1
- package/dist/sharing/index.d.ts +1 -1
- package/dist/sharing/index.d.ts.map +1 -1
- package/dist/sharing/index.js +1 -1
- package/dist/sharing/index.js.map +1 -1
- package/package.json +15 -1
- package/src/composer/PromptComposer.tsx +13 -0
- package/src/composer/TiptapComposer.tsx +107 -1
- package/src/data-grid/DataGrid.spec.tsx +243 -0
- package/src/data-grid/DataGrid.tsx +754 -0
- package/src/data-grid/index.ts +15 -0
- package/src/sharing/SharePrimitives.tsx +47 -8
- package/src/sharing/index.ts +4 -0
package/dist/sharing/index.js
CHANGED
|
@@ -1,3 +1,3 @@
|
|
|
1
|
-
export { ShareAgentsSection, ShareCopyRow, ShareTrigger, } from "./SharePrimitives.js";
|
|
1
|
+
export { ShareAgentsSection, ShareCopyRow, ShareDisclosureSection, SharePeopleSection, ShareTrigger, } from "./SharePrimitives.js";
|
|
2
2
|
export { VisibilityBadge, } from "./VisibilityBadge.js";
|
|
3
3
|
//# sourceMappingURL=index.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.js","sourceRoot":"","sources":["../../src/sharing/index.ts"],"names":[],"mappings":"AAAA,OAAO,EACL,kBAAkB,EAElB,YAAY,EAEZ,YAAY,GAEb,MAAM,sBAAsB,CAAC;AAC9B,OAAO,EACL,eAAe,GAEhB,MAAM,sBAAsB,CAAC"}
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../../src/sharing/index.ts"],"names":[],"mappings":"AAAA,OAAO,EACL,kBAAkB,EAElB,YAAY,EAEZ,sBAAsB,EAEtB,kBAAkB,EAElB,YAAY,GAEb,MAAM,sBAAsB,CAAC;AAC9B,OAAO,EACL,eAAe,GAEhB,MAAM,sBAAsB,CAAC"}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@agent-native/toolkit",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.16.0",
|
|
4
4
|
"description": "Reusable app-building UI and helpers for Agent-Native apps.",
|
|
5
5
|
"homepage": "https://github.com/BuilderIO/agent-native#readme",
|
|
6
6
|
"bugs": {
|
|
@@ -75,6 +75,20 @@
|
|
|
75
75
|
"import": "./dist/dashboard/*.js",
|
|
76
76
|
"default": "./dist/dashboard/*.js"
|
|
77
77
|
},
|
|
78
|
+
"./data-grid": {
|
|
79
|
+
"types": "./dist/data-grid/index.d.ts",
|
|
80
|
+
"browser": "./dist/data-grid/index.js",
|
|
81
|
+
"development": "./dist/data-grid/index.js",
|
|
82
|
+
"import": "./dist/data-grid/index.js",
|
|
83
|
+
"default": "./dist/data-grid/index.js"
|
|
84
|
+
},
|
|
85
|
+
"./data-grid/*": {
|
|
86
|
+
"types": "./dist/data-grid/*.d.ts",
|
|
87
|
+
"browser": "./dist/data-grid/*.js",
|
|
88
|
+
"development": "./dist/data-grid/*.js",
|
|
89
|
+
"import": "./dist/data-grid/*.js",
|
|
90
|
+
"default": "./dist/data-grid/*.js"
|
|
91
|
+
},
|
|
78
92
|
"./chat-history": {
|
|
79
93
|
"types": "./dist/chat-history/index.d.ts",
|
|
80
94
|
"browser": "./dist/chat-history/index.js",
|
|
@@ -47,6 +47,7 @@ import {
|
|
|
47
47
|
import {
|
|
48
48
|
DEFAULT_VOICE_DICTATION_ENABLED,
|
|
49
49
|
TiptapComposer,
|
|
50
|
+
type ComposerAgentOption,
|
|
50
51
|
type ComposerSubmitIntent,
|
|
51
52
|
type TiptapComposerHandle,
|
|
52
53
|
type TiptapComposerSubmitOptions,
|
|
@@ -149,6 +150,12 @@ export interface PromptComposerProps {
|
|
|
149
150
|
selectedEffort?: ReasoningEffort;
|
|
150
151
|
onModelChange?: (model: string, engine: string) => void;
|
|
151
152
|
onEffortChange?: (effort: ReasoningEffort) => void;
|
|
153
|
+
/** Local or hosted agent runtimes shown above the model list. */
|
|
154
|
+
availableAgents?: ComposerAgentOption[];
|
|
155
|
+
/** Selected agent runtime identifier. */
|
|
156
|
+
selectedAgent?: string;
|
|
157
|
+
/** Callback when the user picks an agent runtime. */
|
|
158
|
+
onAgentChange?: (agent: string) => void;
|
|
152
159
|
/**
|
|
153
160
|
* Enable server-backed model/provider status checks. Defaults off when the
|
|
154
161
|
* host supplies model state and callbacks, otherwise on.
|
|
@@ -515,6 +522,9 @@ function PromptComposerInner({
|
|
|
515
522
|
selectedEffort,
|
|
516
523
|
onModelChange,
|
|
517
524
|
onEffortChange,
|
|
525
|
+
availableAgents,
|
|
526
|
+
selectedAgent,
|
|
527
|
+
onAgentChange,
|
|
518
528
|
modelStatusChecksEnabled,
|
|
519
529
|
onTextChange,
|
|
520
530
|
onConnectProvider,
|
|
@@ -689,10 +699,13 @@ function PromptComposerInner({
|
|
|
689
699
|
selectedModel={composerModel}
|
|
690
700
|
selectedEffort={composerEffort}
|
|
691
701
|
availableModels={composerModelGroups}
|
|
702
|
+
availableAgents={availableAgents}
|
|
703
|
+
selectedAgent={selectedAgent}
|
|
692
704
|
showAutoModelOption={showAutoModelOption}
|
|
693
705
|
modelListLoading={composerModelListLoading}
|
|
694
706
|
onModelChange={handleModelChange}
|
|
695
707
|
onEffortChange={handleEffortChange}
|
|
708
|
+
onAgentChange={onAgentChange}
|
|
696
709
|
providerConnectStatusEnabled={resolvedModelStatusChecksEnabled}
|
|
697
710
|
onConnectProvider={onConnectProvider}
|
|
698
711
|
onConnectLocalRuntime={onConnectLocalRuntime}
|
|
@@ -652,6 +652,19 @@ function ComposerModeChip({
|
|
|
652
652
|
|
|
653
653
|
type ExecMode = "build" | "plan";
|
|
654
654
|
|
|
655
|
+
export interface ComposerAgentOption {
|
|
656
|
+
/** Stable host-defined identifier for the agent runtime. */
|
|
657
|
+
id: string;
|
|
658
|
+
/** Human-readable runtime name shown in the picker. */
|
|
659
|
+
label: string;
|
|
660
|
+
/** Optional short detail shown below the runtime name. */
|
|
661
|
+
description?: string;
|
|
662
|
+
/** Whether this runtime can be selected right now. */
|
|
663
|
+
configured?: boolean;
|
|
664
|
+
/** Optional status text such as "Installed" or "Sign in". */
|
|
665
|
+
statusLabel?: string;
|
|
666
|
+
}
|
|
667
|
+
|
|
655
668
|
export interface TiptapComposerProps {
|
|
656
669
|
placeholder?: string;
|
|
657
670
|
disabled?: boolean;
|
|
@@ -744,6 +757,12 @@ export interface TiptapComposerProps {
|
|
|
744
757
|
onModelChange?: (model: string, engine: string) => void;
|
|
745
758
|
/** Callback when user picks an effort */
|
|
746
759
|
onEffortChange?: (effort: ReasoningEffort) => void;
|
|
760
|
+
/** Local or hosted agent runtimes shown above the model list. */
|
|
761
|
+
availableAgents?: ComposerAgentOption[];
|
|
762
|
+
/** Selected agent runtime identifier. Defaults to the built-in agent. */
|
|
763
|
+
selectedAgent?: string;
|
|
764
|
+
/** Callback when the user picks an agent runtime. */
|
|
765
|
+
onAgentChange?: (agent: string) => void;
|
|
747
766
|
/**
|
|
748
767
|
* Disable Builder/provider status polling for hosts that supply provider
|
|
749
768
|
* state through another channel, such as Electron IPC.
|
|
@@ -1225,10 +1244,13 @@ function ModelSelector({
|
|
|
1225
1244
|
model,
|
|
1226
1245
|
effort,
|
|
1227
1246
|
engines,
|
|
1247
|
+
agents,
|
|
1248
|
+
selectedAgent,
|
|
1228
1249
|
showAutoModelOption = true,
|
|
1229
1250
|
modelListLoading = false,
|
|
1230
1251
|
onChange,
|
|
1231
1252
|
onEffortChange,
|
|
1253
|
+
onAgentChange,
|
|
1232
1254
|
providerConnectStatusEnabled = true,
|
|
1233
1255
|
onConnectProvider,
|
|
1234
1256
|
onConnectLocalRuntime,
|
|
@@ -1236,6 +1258,8 @@ function ModelSelector({
|
|
|
1236
1258
|
}: {
|
|
1237
1259
|
model: string;
|
|
1238
1260
|
effort?: ReasoningEffort;
|
|
1261
|
+
agents?: ComposerAgentOption[];
|
|
1262
|
+
selectedAgent?: string;
|
|
1239
1263
|
engines: Array<{
|
|
1240
1264
|
engine: string;
|
|
1241
1265
|
label: string;
|
|
@@ -1248,6 +1272,7 @@ function ModelSelector({
|
|
|
1248
1272
|
modelListLoading?: boolean;
|
|
1249
1273
|
onChange: (model: string, engine: string) => void;
|
|
1250
1274
|
onEffortChange?: (effort: ReasoningEffort) => void;
|
|
1275
|
+
onAgentChange?: (agent: string) => void;
|
|
1251
1276
|
providerConnectStatusEnabled?: boolean;
|
|
1252
1277
|
onConnectProvider?: () => void;
|
|
1253
1278
|
onConnectLocalRuntime?: (engine: string) => void;
|
|
@@ -1286,6 +1311,10 @@ function ModelSelector({
|
|
|
1286
1311
|
),
|
|
1287
1312
|
[reasoning?.label, t],
|
|
1288
1313
|
);
|
|
1314
|
+
const selectedAgentOption = agents?.find(
|
|
1315
|
+
(agent) => agent.id === (selectedAgent ?? "default"),
|
|
1316
|
+
);
|
|
1317
|
+
const selectedAgentLabel = selectedAgentOption?.label ?? "Default";
|
|
1289
1318
|
|
|
1290
1319
|
// Collapse non-selected families by default. The family containing the
|
|
1291
1320
|
// currently-selected model stays expanded so the user sees their pick at
|
|
@@ -1391,11 +1420,17 @@ function ModelSelector({
|
|
|
1391
1420
|
defaultValue: "Reasoning",
|
|
1392
1421
|
})}: ${effortLabel(selectedEffort)}`
|
|
1393
1422
|
: ""
|
|
1423
|
+
}${
|
|
1424
|
+
selectedAgentOption && selectedAgentOption.id !== "default"
|
|
1425
|
+
? `. Agent: ${selectedAgentLabel}`
|
|
1426
|
+
: ""
|
|
1394
1427
|
}`}
|
|
1395
1428
|
className="agent-composer-model-button flex min-w-0 max-w-[10.5rem] shrink items-center gap-1 rounded-md px-2 py-1 text-[12px] font-medium text-muted-foreground hover:bg-accent/50 hover:text-foreground"
|
|
1396
1429
|
>
|
|
1397
1430
|
<span className="min-w-0 truncate">
|
|
1398
|
-
{
|
|
1431
|
+
{selectedAgentOption && selectedAgentOption.id !== "default"
|
|
1432
|
+
? selectedAgentLabel
|
|
1433
|
+
: compactComposerModelName(model, t)}
|
|
1399
1434
|
</span>
|
|
1400
1435
|
{effortOptions.length > 0 && (
|
|
1401
1436
|
<span className="agent-composer-model-effort min-w-0 shrink truncate text-muted-foreground/70">
|
|
@@ -1414,6 +1449,71 @@ function ModelSelector({
|
|
|
1414
1449
|
className="z-[260] box-border w-72 overflow-y-auto rounded-lg border-border p-0 py-1 shadow-lg"
|
|
1415
1450
|
style={MODEL_SELECTOR_POPOVER_STYLE}
|
|
1416
1451
|
>
|
|
1452
|
+
{agents && agents.length > 0 && (
|
|
1453
|
+
<>
|
|
1454
|
+
<div className="px-3 pb-1 pt-2 text-[11px] font-medium uppercase tracking-wide text-muted-foreground">
|
|
1455
|
+
Agents
|
|
1456
|
+
</div>
|
|
1457
|
+
{agents.map((agent) => {
|
|
1458
|
+
const isSelected = agent.id === (selectedAgent ?? "default");
|
|
1459
|
+
const isConfigured = agent.configured !== false;
|
|
1460
|
+
const canConnect =
|
|
1461
|
+
!isConfigured && Boolean(onConnectLocalRuntime);
|
|
1462
|
+
const statusLabel =
|
|
1463
|
+
agent.statusLabel ??
|
|
1464
|
+
(!isConfigured ? "Unavailable" : undefined);
|
|
1465
|
+
return (
|
|
1466
|
+
<div
|
|
1467
|
+
key={agent.id}
|
|
1468
|
+
className="flex items-center hover:bg-accent/30"
|
|
1469
|
+
>
|
|
1470
|
+
<button
|
|
1471
|
+
type="button"
|
|
1472
|
+
disabled={!isConfigured || !onAgentChange}
|
|
1473
|
+
aria-current={isSelected ? "true" : undefined}
|
|
1474
|
+
onClick={() => {
|
|
1475
|
+
if (!isConfigured) return;
|
|
1476
|
+
onAgentChange?.(agent.id);
|
|
1477
|
+
setOpen(false);
|
|
1478
|
+
}}
|
|
1479
|
+
className={`flex min-w-0 flex-1 items-center gap-3 px-3 py-1.5 text-start ${
|
|
1480
|
+
isConfigured
|
|
1481
|
+
? "hover:bg-accent/50"
|
|
1482
|
+
: "cursor-default opacity-50"
|
|
1483
|
+
}`}
|
|
1484
|
+
>
|
|
1485
|
+
<span className="min-w-0 flex-1">
|
|
1486
|
+
<span className="block truncate text-[13px] text-foreground">
|
|
1487
|
+
{agent.label}
|
|
1488
|
+
</span>
|
|
1489
|
+
{agent.description && (
|
|
1490
|
+
<span className="block truncate text-[11px] text-muted-foreground">
|
|
1491
|
+
{agent.description}
|
|
1492
|
+
</span>
|
|
1493
|
+
)}
|
|
1494
|
+
</span>
|
|
1495
|
+
{isSelected && isConfigured && (
|
|
1496
|
+
<IconCheck className="h-3.5 w-3.5 shrink-0 text-primary" />
|
|
1497
|
+
)}
|
|
1498
|
+
</button>
|
|
1499
|
+
{!isConfigured && statusLabel && (
|
|
1500
|
+
<button
|
|
1501
|
+
type="button"
|
|
1502
|
+
disabled={!canConnect}
|
|
1503
|
+
className="ms-auto max-w-[6rem] shrink-0 truncate px-3 py-1.5 text-end text-[10px] text-muted-foreground/70 hover:text-foreground disabled:cursor-default"
|
|
1504
|
+
onClick={() => {
|
|
1505
|
+
if (canConnect) connectLocalRuntime(agent.id);
|
|
1506
|
+
}}
|
|
1507
|
+
>
|
|
1508
|
+
{statusLabel}
|
|
1509
|
+
</button>
|
|
1510
|
+
)}
|
|
1511
|
+
</div>
|
|
1512
|
+
);
|
|
1513
|
+
})}
|
|
1514
|
+
<div className="my-1 border-t border-border" />
|
|
1515
|
+
</>
|
|
1516
|
+
)}
|
|
1417
1517
|
{showBuilderCta && (
|
|
1418
1518
|
<>
|
|
1419
1519
|
<button
|
|
@@ -1771,6 +1871,9 @@ export function TiptapComposer({
|
|
|
1771
1871
|
modelListLoading,
|
|
1772
1872
|
onModelChange,
|
|
1773
1873
|
onEffortChange,
|
|
1874
|
+
availableAgents,
|
|
1875
|
+
selectedAgent,
|
|
1876
|
+
onAgentChange,
|
|
1774
1877
|
providerConnectStatusEnabled,
|
|
1775
1878
|
onConnectProvider,
|
|
1776
1879
|
onConnectLocalRuntime,
|
|
@@ -3276,10 +3379,13 @@ export function TiptapComposer({
|
|
|
3276
3379
|
model={selectedModel}
|
|
3277
3380
|
effort={selectedEffort}
|
|
3278
3381
|
engines={availableModels}
|
|
3382
|
+
agents={availableAgents}
|
|
3383
|
+
selectedAgent={selectedAgent}
|
|
3279
3384
|
showAutoModelOption={showAutoModelOption}
|
|
3280
3385
|
modelListLoading={modelListLoading}
|
|
3281
3386
|
onChange={onModelChange}
|
|
3282
3387
|
onEffortChange={onEffortChange}
|
|
3388
|
+
onAgentChange={onAgentChange}
|
|
3283
3389
|
providerConnectStatusEnabled={providerConnectStatusEnabled}
|
|
3284
3390
|
onConnectProvider={onConnectProvider}
|
|
3285
3391
|
onConnectLocalRuntime={onConnectLocalRuntime}
|
|
@@ -0,0 +1,243 @@
|
|
|
1
|
+
// @vitest-environment happy-dom
|
|
2
|
+
|
|
3
|
+
import { act } from "react";
|
|
4
|
+
import { createRoot, type Root } from "react-dom/client";
|
|
5
|
+
import { afterEach, beforeEach, describe, expect, it, vi } from "vitest";
|
|
6
|
+
|
|
7
|
+
import {
|
|
8
|
+
DataGrid,
|
|
9
|
+
dataGridColumnTemplate,
|
|
10
|
+
type DataGridColumn,
|
|
11
|
+
} from "./index.js";
|
|
12
|
+
|
|
13
|
+
interface Row {
|
|
14
|
+
id: string;
|
|
15
|
+
name: string;
|
|
16
|
+
status: string;
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
const columns: readonly DataGridColumn<Row>[] = [
|
|
20
|
+
{
|
|
21
|
+
id: "name",
|
|
22
|
+
label: "Name",
|
|
23
|
+
width: 180,
|
|
24
|
+
getValue: (row) => row.name,
|
|
25
|
+
renderCell: ({ value }) => String(value ?? ""),
|
|
26
|
+
},
|
|
27
|
+
{
|
|
28
|
+
id: "status",
|
|
29
|
+
label: "Status",
|
|
30
|
+
width: 120,
|
|
31
|
+
getValue: (row) => row.status,
|
|
32
|
+
renderCell: ({ value }) => String(value ?? ""),
|
|
33
|
+
renderEditor: ({ value, onCommit }) => (
|
|
34
|
+
<input
|
|
35
|
+
aria-label="Status editor"
|
|
36
|
+
defaultValue={String(value ?? "")}
|
|
37
|
+
onKeyDown={(event) => {
|
|
38
|
+
if (event.key === "Enter") onCommit(event.currentTarget.value);
|
|
39
|
+
}}
|
|
40
|
+
/>
|
|
41
|
+
),
|
|
42
|
+
},
|
|
43
|
+
];
|
|
44
|
+
|
|
45
|
+
const rows: readonly Row[] = [
|
|
46
|
+
{ id: "row-1", name: "First", status: "Open" },
|
|
47
|
+
{ id: "row-2", name: "Second", status: "Closed" },
|
|
48
|
+
];
|
|
49
|
+
|
|
50
|
+
describe("data-grid", () => {
|
|
51
|
+
it("builds a bounded template and includes the selection column only when requested", () => {
|
|
52
|
+
expect(dataGridColumnTemplate(columns, { name: 40 }, "none")).toBe(
|
|
53
|
+
"96px 120px",
|
|
54
|
+
);
|
|
55
|
+
expect(dataGridColumnTemplate(columns, { name: 220 }, "multiple")).toBe(
|
|
56
|
+
"40px 220px 120px",
|
|
57
|
+
);
|
|
58
|
+
});
|
|
59
|
+
|
|
60
|
+
describe("DataGrid", () => {
|
|
61
|
+
let container: HTMLDivElement;
|
|
62
|
+
let root: Root;
|
|
63
|
+
|
|
64
|
+
beforeEach(() => {
|
|
65
|
+
vi.stubGlobal("IS_REACT_ACT_ENVIRONMENT", true);
|
|
66
|
+
container = document.createElement("div");
|
|
67
|
+
document.body.appendChild(container);
|
|
68
|
+
root = createRoot(container);
|
|
69
|
+
});
|
|
70
|
+
|
|
71
|
+
afterEach(() => {
|
|
72
|
+
act(() => root.unmount());
|
|
73
|
+
container.remove();
|
|
74
|
+
vi.restoreAllMocks();
|
|
75
|
+
vi.unstubAllGlobals();
|
|
76
|
+
});
|
|
77
|
+
|
|
78
|
+
it("moves the active cell with arrows and commits an editor value", () => {
|
|
79
|
+
const onActiveCellChange = vi.fn();
|
|
80
|
+
const onCellCommit = vi.fn();
|
|
81
|
+
|
|
82
|
+
act(() => {
|
|
83
|
+
root.render(
|
|
84
|
+
<DataGrid
|
|
85
|
+
rows={rows}
|
|
86
|
+
columns={columns}
|
|
87
|
+
getRowId={(row) => row.id}
|
|
88
|
+
onActiveCellChange={onActiveCellChange}
|
|
89
|
+
onCellCommit={onCellCommit}
|
|
90
|
+
/>,
|
|
91
|
+
);
|
|
92
|
+
});
|
|
93
|
+
|
|
94
|
+
const firstNameCell = container.querySelector<HTMLElement>(
|
|
95
|
+
'[data-data-grid-row-id="row-1"][data-data-grid-column-id="name"]',
|
|
96
|
+
);
|
|
97
|
+
expect(firstNameCell?.getAttribute("tabindex")).toBe("0");
|
|
98
|
+
|
|
99
|
+
act(() => {
|
|
100
|
+
firstNameCell?.dispatchEvent(
|
|
101
|
+
new KeyboardEvent("keydown", { bubbles: true, key: "ArrowRight" }),
|
|
102
|
+
);
|
|
103
|
+
});
|
|
104
|
+
expect(onActiveCellChange).toHaveBeenCalledWith({
|
|
105
|
+
rowId: "row-1",
|
|
106
|
+
columnId: "status",
|
|
107
|
+
editing: false,
|
|
108
|
+
});
|
|
109
|
+
|
|
110
|
+
const statusCell = container.querySelector<HTMLElement>(
|
|
111
|
+
'[data-data-grid-row-id="row-1"][data-data-grid-column-id="status"]',
|
|
112
|
+
);
|
|
113
|
+
act(() => {
|
|
114
|
+
statusCell?.dispatchEvent(
|
|
115
|
+
new KeyboardEvent("keydown", { bubbles: true, key: "Enter" }),
|
|
116
|
+
);
|
|
117
|
+
});
|
|
118
|
+
expect(
|
|
119
|
+
container.querySelector('input[aria-label="Status editor"]'),
|
|
120
|
+
).not.toBeNull();
|
|
121
|
+
|
|
122
|
+
const input = container.querySelector<HTMLInputElement>(
|
|
123
|
+
'input[aria-label="Status editor"]',
|
|
124
|
+
);
|
|
125
|
+
expect(input).not.toBeNull();
|
|
126
|
+
if (input) {
|
|
127
|
+
input.value = "In progress";
|
|
128
|
+
act(() => {
|
|
129
|
+
input.dispatchEvent(
|
|
130
|
+
new KeyboardEvent("keydown", { bubbles: true, key: "Enter" }),
|
|
131
|
+
);
|
|
132
|
+
});
|
|
133
|
+
}
|
|
134
|
+
expect(onCellCommit).toHaveBeenCalledWith({
|
|
135
|
+
row: rows[0],
|
|
136
|
+
rowIndex: 0,
|
|
137
|
+
column: columns[1],
|
|
138
|
+
previousValue: "Open",
|
|
139
|
+
value: "In progress",
|
|
140
|
+
});
|
|
141
|
+
});
|
|
142
|
+
|
|
143
|
+
it("supports controlled multi-row selection", () => {
|
|
144
|
+
const onSelectedRowIdsChange = vi.fn();
|
|
145
|
+
|
|
146
|
+
act(() => {
|
|
147
|
+
root.render(
|
|
148
|
+
<DataGrid
|
|
149
|
+
rows={rows}
|
|
150
|
+
columns={columns}
|
|
151
|
+
getRowId={(row) => row.id}
|
|
152
|
+
selection="multiple"
|
|
153
|
+
onSelectedRowIdsChange={onSelectedRowIdsChange}
|
|
154
|
+
/>,
|
|
155
|
+
);
|
|
156
|
+
});
|
|
157
|
+
|
|
158
|
+
const rowCheckbox = container.querySelector<HTMLInputElement>(
|
|
159
|
+
'[aria-label="Select row row-1"]',
|
|
160
|
+
);
|
|
161
|
+
act(() => rowCheckbox?.click());
|
|
162
|
+
expect(onSelectedRowIdsChange).toHaveBeenCalledTimes(1);
|
|
163
|
+
expect(Array.from(onSelectedRowIdsChange.mock.calls[0][0])).toEqual([
|
|
164
|
+
"row-1",
|
|
165
|
+
]);
|
|
166
|
+
});
|
|
167
|
+
|
|
168
|
+
it("reports pointer-based column resizing through the app callback", () => {
|
|
169
|
+
const onColumnWidthsChange = vi.fn();
|
|
170
|
+
|
|
171
|
+
act(() => {
|
|
172
|
+
root.render(
|
|
173
|
+
<DataGrid
|
|
174
|
+
rows={rows}
|
|
175
|
+
columns={columns}
|
|
176
|
+
getRowId={(row) => row.id}
|
|
177
|
+
onColumnWidthsChange={onColumnWidthsChange}
|
|
178
|
+
/>,
|
|
179
|
+
);
|
|
180
|
+
});
|
|
181
|
+
|
|
182
|
+
const resizeHandle = container.querySelector<HTMLButtonElement>(
|
|
183
|
+
'[aria-label="Resize name column"]',
|
|
184
|
+
);
|
|
185
|
+
expect(resizeHandle).not.toBeNull();
|
|
186
|
+
|
|
187
|
+
act(() => {
|
|
188
|
+
resizeHandle?.dispatchEvent(
|
|
189
|
+
new PointerEvent("pointerdown", {
|
|
190
|
+
bubbles: true,
|
|
191
|
+
button: 0,
|
|
192
|
+
clientX: 100,
|
|
193
|
+
}),
|
|
194
|
+
);
|
|
195
|
+
document.dispatchEvent(
|
|
196
|
+
new PointerEvent("pointermove", {
|
|
197
|
+
bubbles: true,
|
|
198
|
+
clientX: 180,
|
|
199
|
+
}),
|
|
200
|
+
);
|
|
201
|
+
document.dispatchEvent(
|
|
202
|
+
new PointerEvent("pointerup", { bubbles: true, clientX: 180 }),
|
|
203
|
+
);
|
|
204
|
+
});
|
|
205
|
+
|
|
206
|
+
expect(onColumnWidthsChange).toHaveBeenLastCalledWith({ name: 260 });
|
|
207
|
+
});
|
|
208
|
+
|
|
209
|
+
it("lets an app own the outer header and body while sharing the grid template", () => {
|
|
210
|
+
const renderHeader = vi.fn(({ gridTemplateColumns }) => (
|
|
211
|
+
<div data-testid="custom-header" style={{ gridTemplateColumns }} />
|
|
212
|
+
));
|
|
213
|
+
const renderBody = vi.fn(({ gridTemplateColumns }) => (
|
|
214
|
+
<div data-testid="custom-body" style={{ gridTemplateColumns }} />
|
|
215
|
+
));
|
|
216
|
+
|
|
217
|
+
act(() => {
|
|
218
|
+
root.render(
|
|
219
|
+
<DataGrid
|
|
220
|
+
rows={rows}
|
|
221
|
+
columns={columns}
|
|
222
|
+
getRowId={(row) => row.id}
|
|
223
|
+
renderHeader={renderHeader}
|
|
224
|
+
renderBody={renderBody}
|
|
225
|
+
/>,
|
|
226
|
+
);
|
|
227
|
+
});
|
|
228
|
+
|
|
229
|
+
expect(
|
|
230
|
+
container.querySelector("[data-testid=custom-header]"),
|
|
231
|
+
).not.toBeNull();
|
|
232
|
+
expect(
|
|
233
|
+
container.querySelector("[data-testid=custom-body]"),
|
|
234
|
+
).not.toBeNull();
|
|
235
|
+
expect(renderHeader).toHaveBeenCalledWith(
|
|
236
|
+
expect.objectContaining({ gridTemplateColumns: "180px 120px" }),
|
|
237
|
+
);
|
|
238
|
+
expect(renderBody).toHaveBeenCalledWith(
|
|
239
|
+
expect.objectContaining({ rows, columns }),
|
|
240
|
+
);
|
|
241
|
+
});
|
|
242
|
+
});
|
|
243
|
+
});
|