@multiplatform.one/table 7.29.1 → 7.30.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.
Files changed (35) hide show
  1. package/dist/cjs/components/DataTableCell/index.cjs +7 -0
  2. package/dist/cjs/components/DataTableCell/index.native.js +9 -0
  3. package/dist/cjs/components/DataTableCell/index.native.js.map +1 -1
  4. package/dist/cjs/components/KnobDialog.cjs +8 -9
  5. package/dist/cjs/components/KnobDialog.native.js +8 -9
  6. package/dist/cjs/components/KnobDialog.native.js.map +1 -1
  7. package/dist/cjs/components/TableInput/index.cjs +139 -111
  8. package/dist/esm/components/DataTableCell/index.mjs +7 -0
  9. package/dist/esm/components/DataTableCell/index.mjs.map +1 -1
  10. package/dist/esm/components/DataTableCell/index.native.js +9 -0
  11. package/dist/esm/components/DataTableCell/index.native.js.map +1 -1
  12. package/dist/esm/components/KnobDialog.mjs +1 -2
  13. package/dist/esm/components/KnobDialog.mjs.map +1 -1
  14. package/dist/esm/components/KnobDialog.native.js +1 -2
  15. package/dist/esm/components/KnobDialog.native.js.map +1 -1
  16. package/dist/esm/components/TableInput/index.mjs +140 -112
  17. package/dist/esm/components/TableInput/index.mjs.map +1 -1
  18. package/dist/jsx/components/DataTableCell/index.mjs +7 -0
  19. package/dist/jsx/components/DataTableCell/index.mjs.map +1 -1
  20. package/dist/jsx/components/DataTableCell/index.native.js +9 -0
  21. package/dist/jsx/components/DataTableCell/index.native.js.map +1 -1
  22. package/dist/jsx/components/KnobDialog.mjs +1 -2
  23. package/dist/jsx/components/KnobDialog.mjs.map +1 -1
  24. package/dist/jsx/components/KnobDialog.native.js +8 -9
  25. package/dist/jsx/components/KnobDialog.native.js.map +1 -1
  26. package/dist/jsx/components/TableInput/index.mjs +140 -112
  27. package/dist/jsx/components/TableInput/index.mjs.map +1 -1
  28. package/package.json +12 -12
  29. package/src/components/DataTableCell/cellDraftPolicy.spec.tsx +60 -0
  30. package/src/components/DataTableCell/index.tsx +10 -1
  31. package/src/components/KnobDialog.tsx +1 -2
  32. package/src/components/TableInput/index.tsx +137 -138
  33. package/src/components/TableInput/tableInput.cellIdentity.spec.tsx +196 -0
  34. package/types/components/KnobDialog.d.ts.map +1 -1
  35. package/types/components/TableInput/index.d.ts.map +1 -1
@@ -35,7 +35,13 @@ import {
35
35
  useResolvedValidators,
36
36
  type AnyFormApi,
37
37
  } from "@multiplatform.one/forms";
38
- import { flexRender, getCoreRowModel, useReactTable, type ColumnDef } from "@tanstack/react-table";
38
+ import {
39
+ flexRender,
40
+ getCoreRowModel,
41
+ useReactTable,
42
+ type CellContext,
43
+ type ColumnDef,
44
+ } from "@tanstack/react-table";
39
45
  import { TrashIcon, PlusIcon } from "@phosphor-icons/react";
40
46
  import { useTranslation } from "react-i18next";
41
47
  import { Table, getTableCellPadding } from "@multiplatform.one/table-primitives";
@@ -53,6 +59,7 @@ import { RowDetailEditor } from "./RowDetailEditor";
53
59
  import {
54
60
  childRowAttrs,
55
61
  childTableKeyAction,
62
+ columnKey,
56
63
  generateRowId,
57
64
  isEmptyRows,
58
65
  SKELETON_ROW_COUNT,
@@ -61,6 +68,113 @@ import {
61
68
 
62
69
  const chromeColumnIds = new Set(["_reorder", "_rowNumber", "_detail", "_actions"]);
63
70
 
71
+ /**
72
+ * What the cell renderers read at render time. It rides on TanStack's table
73
+ * meta so the column definitions depend on the schema alone.
74
+ */
75
+ interface GridMeta<TData extends DataTableData> {
76
+ readOnly: boolean;
77
+ compact: boolean;
78
+ canDelete: boolean;
79
+ deleteColor: string;
80
+ knobProps: ReturnType<typeof useResolvedKnobs>["knobProps"];
81
+ sortable: ReturnType<typeof useSortableRows<TData>>;
82
+ detailColumns: DataTableColumn<TData>[];
83
+ isCellHidden?: ChildCellHidden<TData>;
84
+ t: (key: string, options?: Record<string, unknown>) => string;
85
+ changeCell: (rowIndex: number, columnId: string, value: unknown) => void;
86
+ deleteRow: (rowIndex: number) => void;
87
+ commitRowDetail: (rowIndex: number, next: TData, changed: string[]) => void;
88
+ }
89
+
90
+ type GridCellProps<TData extends DataTableData> = CellContext<TData, unknown>;
91
+
92
+ const gridMeta = <TData extends DataTableData>({ table }: GridCellProps<TData>) =>
93
+ table.options.meta as GridMeta<TData>;
94
+
95
+ // flexRender mounts each column's `cell` as a component, so these renderers
96
+ // live at module scope: a renderer created per render remounts every cell on
97
+ // a row commit, and the focused control goes with it (MPO-471, MPO-475).
98
+ function ReorderCell<TData extends DataTableData>(props: GridCellProps<TData>) {
99
+ const { row } = props;
100
+ const { sortable, t, compact } = gridMeta(props);
101
+ return (
102
+ <SortableHandle
103
+ label={sortable.labels[row.index] ?? t("Row {{row}}", { row: row.index + 1 })}
104
+ itemId={String(row.original.id ?? row.id)}
105
+ compact={compact}
106
+ onPointerDown={(e) => sortable.onHandlePointerDown(row.index, e)}
107
+ onKeyDown={(e) => sortable.onHandleKeyDown(row.index, e)}
108
+ />
109
+ );
110
+ }
111
+
112
+ function RowNumberCell<TData extends DataTableData>(props: GridCellProps<TData>) {
113
+ const { knobProps } = gridMeta(props);
114
+ return (
115
+ <Text color="$color10" fontSize={knobProps.label.fontSize}>
116
+ {props.row.index + 1}
117
+ </Text>
118
+ );
119
+ }
120
+
121
+ function DataCell<TData extends DataTableData>(props: GridCellProps<TData>) {
122
+ const { row, column } = props;
123
+ const { isCellHidden, readOnly, compact, changeCell } = gridMeta(props);
124
+ const columnId = column.id;
125
+ if (isCellHidden?.(row.original, columnId, row.index)) return null;
126
+ const col = column.columnDef as DataTableColumn<TData>;
127
+ return (
128
+ <DataTableCell
129
+ value={row.original[columnId as keyof TData]}
130
+ onChange={(newValue) => changeCell(row.index, columnId, newValue)}
131
+ field={(col.field || "input") as DataTableField}
132
+ fieldProps={col.fieldProps as DataTableFieldProps | undefined}
133
+ readOnly={readOnly}
134
+ compact={compact}
135
+ columnId={columnId}
136
+ columnLabel={typeof col.header === "string" ? col.header : undefined}
137
+ rowIndex={row.index}
138
+ rowData={row.original}
139
+ />
140
+ );
141
+ }
142
+
143
+ function DetailCell<TData extends DataTableData>(props: GridCellProps<TData>) {
144
+ const { row } = props;
145
+ const { detailColumns, readOnly, compact, isCellHidden, commitRowDetail } = gridMeta(props);
146
+ return (
147
+ <RowDetailEditor
148
+ row={row.original}
149
+ rowIndex={row.index}
150
+ columns={detailColumns}
151
+ readOnly={readOnly}
152
+ compact={compact}
153
+ isCellHidden={isCellHidden}
154
+ onCommit={(next, changed) => commitRowDetail(row.index, next, changed)}
155
+ />
156
+ );
157
+ }
158
+
159
+ function DeleteCell<TData extends DataTableData>(props: GridCellProps<TData>) {
160
+ const { row } = props;
161
+ const { canDelete, compact, knobProps, deleteColor, deleteRow, t } = gridMeta(props);
162
+ return (
163
+ <Button
164
+ chromeless
165
+ compact={compact}
166
+ onPress={() => deleteRow(row.index)}
167
+ disabled={!canDelete}
168
+ hitSlop={knobProps.nestedControl.hitSlop}
169
+ aria-label={withInterp(t("Delete row {{row}}", { row: row.index + 1 }), {
170
+ row: row.index + 1,
171
+ })}
172
+ >
173
+ <TrashIcon size={16} color={deleteColor} />
174
+ </Button>
175
+ );
176
+ }
177
+
64
178
  export interface TableInputProps<TData extends DataTableData = DataTableData> {
65
179
  /** Data array for the table (ignored in favor of form state in the form branch) */
66
180
  data?: TData[];
@@ -272,7 +386,6 @@ function TableInputInner<TData extends DataTableData>({
272
386
  const pendingFocusRef = useRef<string | null>(null);
273
387
  const gridRef = useRef<HTMLElement | null>(null);
274
388
  const reorderEnabled = enableReorder && !readOnly && !skeleton;
275
- const Detail = RowDetailEditor<TData>;
276
389
 
277
390
  const firstDataKey = useMemo(() => {
278
391
  const first = columns[0];
@@ -307,13 +420,6 @@ function TableInputInner<TData extends DataTableData>({
307
420
  rowSelector: `[${childRowAttrs.row}]`,
308
421
  });
309
422
 
310
- const handlePointerRef = useRef(sortable.onHandlePointerDown);
311
- handlePointerRef.current = sortable.onHandlePointerDown;
312
- const handleKeyRef = useRef(sortable.onHandleKeyDown);
313
- handleKeyRef.current = sortable.onHandleKeyDown;
314
- const labelsRef = useRef(sortable.labels);
315
- labelsRef.current = sortable.labels;
316
-
317
423
  const lastDataColumnId = useMemo(() => {
318
424
  const last = columns[columns.length - 1];
319
425
  if (!last) return null;
@@ -363,34 +469,6 @@ function TableInputInner<TData extends DataTableData>({
363
469
  },
364
470
  [rows, commit, onCellChange],
365
471
  );
366
- const rowDetailCommitRef = useRef(handleRowDetailCommit);
367
- rowDetailCommitRef.current = handleRowDetailCommit;
368
-
369
- // flexRender mounts each `cell` as a component, so a fresh renderer on every
370
- // row commit remounted the pencil under its closing dialog, and the dialog
371
- // then had no connected trigger to return focus to (MPO-471).
372
- const detailColumn = useMemo<ColumnDef<TData, unknown> | null>(
373
- () =>
374
- rowDetail
375
- ? {
376
- id: "_detail",
377
- header: "",
378
- size: chromePx + 16,
379
- cell: ({ row }) => (
380
- <Detail
381
- row={row.original}
382
- rowIndex={row.index}
383
- columns={detailColumns}
384
- readOnly={readOnly}
385
- compact={compact}
386
- isCellHidden={isCellHidden}
387
- onCommit={(next, changed) => rowDetailCommitRef.current(row.index, next, changed)}
388
- />
389
- ),
390
- }
391
- : null,
392
- [rowDetail, chromePx, detailColumns, readOnly, compact, isCellHidden],
393
- );
394
472
 
395
473
  useEffect(() => {
396
474
  const id = pendingFocusRef.current;
@@ -456,114 +534,34 @@ function TableInputInner<TData extends DataTableData>({
456
534
 
457
535
  const tableColumns = useMemo(() => {
458
536
  const cols: ColumnDef<TData, unknown>[] = [];
537
+ const chrome = (id: string, header: string, size: number, cell: ColumnDef<TData>["cell"]) =>
538
+ cols.push({ id, header, size, cell });
459
539
 
460
- if (reorderEnabled) {
461
- cols.push({
462
- id: "_reorder",
463
- header: "",
464
- size: chromePx + 16,
465
- cell: ({ row }) => (
466
- <SortableHandle
467
- label={labelsRef.current[row.index] ?? t("Row {{row}}", { row: row.index + 1 })}
468
- itemId={String((row.original as DataTableData).id ?? row.id)}
469
- compact={compact}
470
- onPointerDown={(e) => handlePointerRef.current(row.index, e)}
471
- onKeyDown={(e) => handleKeyRef.current(row.index, e)}
472
- />
473
- ),
474
- });
475
- }
476
-
477
- if (showRowNumbers) {
478
- cols.push({
479
- id: "_rowNumber",
480
- header: "#",
481
- size: Math.max(36, chromePx),
482
- cell: ({ row }) => (
483
- <Text color="$color10" fontSize={knobProps.label.fontSize}>
484
- {row.index + 1}
485
- </Text>
486
- ),
487
- });
488
- }
489
-
490
- columns.forEach((col) => {
491
- const columnId =
492
- typeof col.accessorKey === "string" ? col.accessorKey : col.id || String(col.accessorKey);
493
-
494
- cols.push({
495
- ...col,
496
- id: columnId,
497
- cell: ({ row }) => {
498
- if (isCellHidden?.(row.original, columnId, row.index)) return null;
499
- const value = row.original[columnId as keyof TData];
500
- const field = (col.field || "input") as DataTableField;
501
- const fieldProps = col.fieldProps as DataTableFieldProps | undefined;
502
-
503
- return (
504
- <DataTableCell
505
- value={value}
506
- onChange={(newValue) => handleCellChange(row.index, columnId, newValue)}
507
- field={field}
508
- fieldProps={fieldProps}
509
- readOnly={readOnly}
510
- compact={compact}
511
- columnId={columnId}
512
- columnLabel={typeof col.header === "string" ? col.header : undefined}
513
- rowIndex={row.index}
514
- rowData={row.original}
515
- />
516
- );
517
- },
518
- } as ColumnDef<TData, unknown>);
519
- });
520
-
521
- if (detailColumn) cols.push(detailColumn);
522
-
523
- if (showDeleteRow && !readOnly) {
524
- cols.push({
525
- id: "_actions",
526
- header: "",
527
- size: chromePx + 16,
528
- cell: ({ row }) => {
529
- const canDelete = !minRows || rows.length > minRows;
530
- return (
531
- <Button
532
- chromeless
533
- compact={compact}
534
- onPress={() => handleDeleteRow(row.index)}
535
- disabled={!canDelete}
536
- hitSlop={knobProps.nestedControl.hitSlop}
537
- aria-label={withInterp(t("Delete row {{row}}", { row: row.index + 1 }), {
538
- row: row.index + 1,
539
- })}
540
- >
541
- <TrashIcon size={16} color={theme.red10?.val ?? "currentColor"} />
542
- </Button>
543
- );
544
- },
545
- });
540
+ if (reorderEnabled) chrome("_reorder", "", chromePx + 16, ReorderCell<TData>);
541
+ if (showRowNumbers) chrome("_rowNumber", "#", Math.max(36, chromePx), RowNumberCell<TData>);
542
+ for (const col of columns) {
543
+ cols.push({ ...col, id: columnKey(col), cell: DataCell<TData> } as ColumnDef<TData, unknown>);
546
544
  }
545
+ if (rowDetail) chrome("_detail", "", chromePx + 16, DetailCell<TData>);
546
+ if (showDeleteRow && !readOnly) chrome("_actions", "", chromePx + 16, DeleteCell<TData>);
547
547
 
548
548
  return cols;
549
- }, [
550
- columns,
551
- showRowNumbers,
552
- showDeleteRow,
549
+ }, [columns, reorderEnabled, showRowNumbers, rowDetail, showDeleteRow, readOnly, chromePx]);
550
+
551
+ const meta: GridMeta<TData> = {
553
552
  readOnly,
554
- handleCellChange,
555
- handleDeleteRow,
556
- rows.length,
557
- minRows,
558
- t,
559
553
  compact,
560
- chromePx,
561
- knobProps.label.fontSize,
562
- knobProps.nestedControl.hitSlop,
563
- reorderEnabled,
564
- detailColumn,
554
+ canDelete: !minRows || rows.length > minRows,
555
+ deleteColor: theme.red10?.val ?? "currentColor",
556
+ knobProps,
557
+ sortable,
558
+ detailColumns,
565
559
  isCellHidden,
566
- ]);
560
+ t,
561
+ changeCell: handleCellChange,
562
+ deleteRow: handleDeleteRow,
563
+ commitRowDetail: handleRowDetailCommit,
564
+ };
567
565
 
568
566
  const table = useReactTable({
569
567
  data: rows,
@@ -572,6 +570,7 @@ function TableInputInner<TData extends DataTableData>({
572
570
  // Stable row identity: keeps in-cell editor drafts pinned to their data
573
571
  // row across deletes instead of following the positional index.
574
572
  getRowId: (row, index) => String((row as DataTableData).id ?? index),
573
+ meta,
575
574
  });
576
575
 
577
576
  // Empty state flips to flex so a single auto-width cell fills the row
@@ -0,0 +1,196 @@
1
+ /**
2
+ * A row commit re-renders the grid's cells in place (MPO-475). TanStack's
3
+ * flexRender mounts each column's `cell` as a component, so a renderer that
4
+ * changes identity on a commit remounts every cell, and focus that a Tab or
5
+ * Enter just placed in the grid lands on a detached node.
6
+ */
7
+
8
+ import React from "react";
9
+ import { renderWithProviders } from "@multiplatform.one/test-utils";
10
+ import { useForm } from "@multiplatform.one/forms";
11
+ import { act, fireEvent, screen, waitFor, within } from "@testing-library/react";
12
+ import userEvent from "@testing-library/user-event";
13
+ import { describe, expect, it, vi } from "vitest";
14
+ import type { DataTableColumn, DataTableData } from "../../types";
15
+ import { ChildTable } from "../ChildTable";
16
+ import { TableInput } from "./index";
17
+
18
+ interface Line extends DataTableData {
19
+ id: string;
20
+ item: string;
21
+ qty: number;
22
+ }
23
+
24
+ const initial = (): Line[] => [
25
+ { id: "a", item: "Bracket", qty: 12 },
26
+ { id: "b", item: "Bolts", qty: 3 },
27
+ ];
28
+
29
+ const columns: DataTableColumn<Line>[] = [
30
+ { accessorKey: "item", header: "Item", field: "input" },
31
+ { accessorKey: "qty", header: "Qty", field: "stepper", fieldProps: { min: 0 } },
32
+ ];
33
+
34
+ function Harness({
35
+ onChange,
36
+ freshSchema = false,
37
+ }: {
38
+ onChange?: (rows: Line[]) => void;
39
+ freshSchema?: boolean;
40
+ }) {
41
+ const [rows, setRows] = React.useState(initial);
42
+ return (
43
+ <ChildTable<Line>
44
+ value={rows}
45
+ columns={freshSchema ? columns.map((col) => ({ ...col })) : columns}
46
+ isCellHidden={freshSchema ? () => false : undefined}
47
+ rowDetail
48
+ onChange={(next) => {
49
+ setRows(next);
50
+ onChange?.(next);
51
+ }}
52
+ />
53
+ );
54
+ }
55
+
56
+ function FormHarness({ onChange }: { onChange?: (rows: Line[]) => void }) {
57
+ const form = useForm({ defaultValues: { lines: initial() } });
58
+ return (
59
+ <TableInput<Line>
60
+ form={form as any}
61
+ name="lines"
62
+ columns={columns}
63
+ rowDetail
64
+ onChange={onChange}
65
+ />
66
+ );
67
+ }
68
+
69
+ function cell(rowIndex: number, label: string) {
70
+ const row = document.querySelector<HTMLElement>(`[data-child-row-index="${rowIndex}"]`);
71
+ if (!row) throw new Error(`no grid row ${rowIndex}`);
72
+ return within(row).getByLabelText(label) as HTMLInputElement;
73
+ }
74
+
75
+ function gridControls() {
76
+ return [cell(0, "Item"), cell(0, "Qty"), cell(1, "Item"), cell(1, "Qty")];
77
+ }
78
+
79
+ function expectConnected(nodes: HTMLElement[]) {
80
+ expect(nodes.map((node) => node.isConnected)).toEqual(nodes.map(() => true));
81
+ }
82
+
83
+ const flushBlurTimer = () => act(() => new Promise<void>((resolve) => setTimeout(resolve, 15)));
84
+
85
+ describe("TableInput cells survive a row commit (MPO-475)", () => {
86
+ it("Enter commits an edit and focus stays on the same connected input", async () => {
87
+ const onChange = vi.fn();
88
+ renderWithProviders(<Harness onChange={onChange} />);
89
+ const before = gridControls();
90
+ const item = cell(0, "Item");
91
+
92
+ await userEvent.click(item);
93
+ await userEvent.type(item, "X");
94
+ fireEvent.keyDown(item, { key: "Enter" });
95
+
96
+ expect(onChange).toHaveBeenCalledTimes(1);
97
+ expect(onChange.mock.calls[0][0][0].item).toBe("BracketX");
98
+ expectConnected(before);
99
+ expect(document.activeElement).toBe(item);
100
+ });
101
+
102
+ it("Tab after an edit commits and lands on the next cell's connected control", async () => {
103
+ const onChange = vi.fn();
104
+ renderWithProviders(<Harness onChange={onChange} />);
105
+ const before = gridControls();
106
+ const item = cell(0, "Item");
107
+ const qty = cell(0, "Qty");
108
+
109
+ await userEvent.click(item);
110
+ await userEvent.type(item, "X");
111
+ await userEvent.tab();
112
+
113
+ expect(onChange).toHaveBeenCalledTimes(1);
114
+ expect(onChange.mock.calls[0][0][0].item).toBe("BracketX");
115
+ expectConnected(before);
116
+ expect(document.activeElement).toBe(qty);
117
+ });
118
+
119
+ it("Shift+Tab after a Stepper edit commits and lands on the previous cell's connected control", async () => {
120
+ const onChange = vi.fn();
121
+ renderWithProviders(<Harness onChange={onChange} />);
122
+ const before = gridControls();
123
+ const item = cell(0, "Item");
124
+ const qty = cell(0, "Qty");
125
+
126
+ await userEvent.click(qty);
127
+ await userEvent.clear(qty);
128
+ await userEvent.type(qty, "7");
129
+ await userEvent.tab({ shift: true });
130
+ await flushBlurTimer();
131
+
132
+ expect(onChange).toHaveBeenCalledTimes(1);
133
+ expect(onChange.mock.calls[0][0][0].qty).toBe(7);
134
+ expectConnected(before);
135
+ expect(document.activeElement).toBe(item);
136
+ });
137
+
138
+ it("a commit in one row leaves the other row's cells connected in the form branch", async () => {
139
+ const onChange = vi.fn();
140
+ renderWithProviders(<FormHarness onChange={onChange} />);
141
+ const before = gridControls();
142
+ const item = cell(1, "Item");
143
+
144
+ await userEvent.click(item);
145
+ await userEvent.type(item, "X");
146
+ fireEvent.keyDown(item, { key: "Enter" });
147
+
148
+ expect(onChange).toHaveBeenCalledTimes(1);
149
+ expect(onChange.mock.calls[0][0][1].item).toBe("BoltsX");
150
+ expectConnected(before);
151
+ expect(document.activeElement).toBe(item);
152
+ });
153
+
154
+ it("a caller that rebuilds columns and isCellHidden every render keeps the cells mounted", async () => {
155
+ const onChange = vi.fn();
156
+ renderWithProviders(<Harness onChange={onChange} freshSchema />);
157
+ const before = gridControls();
158
+ const item = cell(0, "Item");
159
+
160
+ await userEvent.click(item);
161
+ await userEvent.type(item, "X");
162
+ await userEvent.tab();
163
+
164
+ expect(onChange).toHaveBeenCalledTimes(1);
165
+ expectConnected(before);
166
+ expect(document.activeElement).toBe(cell(0, "Qty"));
167
+ });
168
+
169
+ it("a row detail Done leaves the grid mounted and shows the new values in its cells", async () => {
170
+ const onChange = vi.fn();
171
+ renderWithProviders(<Harness onChange={onChange} />);
172
+ const before = gridControls();
173
+
174
+ fireEvent.click(screen.getByRole("button", { name: "Edit row 2" }));
175
+ const dialog = await waitFor(() => screen.getByRole("dialog", { name: "Row 2" }));
176
+ const detailItem = within(dialog).getByLabelText("Item") as HTMLInputElement;
177
+ fireEvent.focus(detailItem);
178
+ fireEvent.change(detailItem, { target: { value: "Hinge" } });
179
+ fireEvent.blur(detailItem);
180
+ const detailQty = within(dialog).getByLabelText("Qty") as HTMLInputElement;
181
+ fireEvent.focus(detailQty);
182
+ fireEvent.change(detailQty, { target: { value: "9" } });
183
+ fireEvent.blur(detailQty);
184
+ await flushBlurTimer();
185
+ fireEvent.click(within(dialog).getByRole("button", { name: "Done" }));
186
+
187
+ expect(onChange).toHaveBeenCalledTimes(1);
188
+ expect(onChange.mock.calls[0][0][1]).toMatchObject({ item: "Hinge", qty: 9 });
189
+ await waitFor(() => expect(screen.queryByRole("dialog", { name: "Row 2" })).toBeNull(), {
190
+ timeout: 3000,
191
+ });
192
+ expectConnected(before.slice(0, 2));
193
+ expect(cell(1, "Item").value).toBe("Hinge");
194
+ expect(cell(1, "Qty").value).toBe("9");
195
+ });
196
+ });
@@ -1 +1 @@
1
- {"version":3,"file":"KnobDialog.d.ts","sourceRoot":"","sources":["../../src/components/KnobDialog.tsx"],"names":[],"mappings":"AAAA;;;;;;GAMG;AAIH,OAAO,KAAK,KAAK,MAAM,OAAO,CAAC;AAG/B,MAAM,WAAW,eAAe;IAC9B,IAAI,EAAE,OAAO,CAAC;IACd,YAAY,EAAE,CAAC,IAAI,EAAE,OAAO,KAAK,IAAI,CAAC;IACtC,KAAK,CAAC,EAAE,OAAO,CAAC;IAChB,QAAQ,EAAE,KAAK,CAAC,SAAS,CAAC;CAC3B;AAED,wBAAgB,UAAU,CAAC,EAAE,IAAI,EAAE,YAAY,EAAE,KAAY,EAAE,QAAQ,EAAE,EAAE,eAAe,2CAMzF;AAED,MAAM,WAAW,sBAAsB;IACrC,QAAQ,EAAE,KAAK,CAAC,SAAS,CAAC;IAC1B,QAAQ,CAAC,EAAE,MAAM,CAAC;CACnB;AAED,wBAAgB,iBAAiB,CAAC,EAAE,QAAQ,EAAE,QAAQ,EAAE,EAAE,sBAAsB,2CAoC/E;AAGD,eAAO,MAAM,iBAAiB;;;;wFAAiB,CAAC;AAChD,eAAO,MAAM,eAAe;;;;;;;;;;6CAAe,CAAC;AAC5C,eAAO,MAAM,qBAAqB;;;;;;;;;;6CAAqB,CAAC;AACxD,eAAO,MAAM,eAAe;;;;;;;;wFAAe,CAAC"}
1
+ {"version":3,"file":"KnobDialog.d.ts","sourceRoot":"","sources":["../../src/components/KnobDialog.tsx"],"names":[],"mappings":"AAAA;;;;;;GAMG;AAIH,OAAO,KAAK,KAAK,MAAM,OAAO,CAAC;AAE/B,MAAM,WAAW,eAAe;IAC9B,IAAI,EAAE,OAAO,CAAC;IACd,YAAY,EAAE,CAAC,IAAI,EAAE,OAAO,KAAK,IAAI,CAAC;IACtC,KAAK,CAAC,EAAE,OAAO,CAAC;IAChB,QAAQ,EAAE,KAAK,CAAC,SAAS,CAAC;CAC3B;AAED,wBAAgB,UAAU,CAAC,EAAE,IAAI,EAAE,YAAY,EAAE,KAAY,EAAE,QAAQ,EAAE,EAAE,eAAe,2CAMzF;AAED,MAAM,WAAW,sBAAsB;IACrC,QAAQ,EAAE,KAAK,CAAC,SAAS,CAAC;IAC1B,QAAQ,CAAC,EAAE,MAAM,CAAC;CACnB;AAED,wBAAgB,iBAAiB,CAAC,EAAE,QAAQ,EAAE,QAAQ,EAAE,EAAE,sBAAsB,2CAoC/E;AAGD,eAAO,MAAM,iBAAiB;;;;wFAAiB,CAAC;AAChD,eAAO,MAAM,eAAe;;;;;;;;;;6CAAe,CAAC;AAC5C,eAAO,MAAM,qBAAqB;;;;;;;;;;6CAAqB,CAAC;AACxD,eAAO,MAAM,eAAe;;;;;;;;wFAAe,CAAC"}
@@ -1 +1 @@
1
- {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../src/components/TableInput/index.tsx"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;GAqBG;AAQH,OAAO,EAML,KAAK,UAAU,EAChB,MAAM,0BAA0B,CAAC;AAOlC,OAAO,KAAK,EACV,eAAe,EACf,aAAa,EACb,mBAAmB,EACnB,cAAc,EACf,MAAM,aAAa,CAAC;AAIrB,OAAO,EAML,KAAK,eAAe,EACrB,MAAM,UAAU,CAAC;AAIlB,MAAM,WAAW,eAAe,CAAC,KAAK,SAAS,aAAa,GAAG,aAAa;IAC1E,mFAAmF;IACnF,IAAI,CAAC,EAAE,KAAK,EAAE,CAAC;IACf,4DAA4D;IAC5D,OAAO,EAAE,eAAe,CAAC,KAAK,CAAC,EAAE,CAAC;IAClC,0FAA0F;IAC1F,QAAQ,CAAC,EAAE,CAAC,IAAI,EAAE,KAAK,EAAE,KAAK,IAAI,CAAC;IACnC,sEAAsE;IACtE,YAAY,CAAC,EAAE,CAAC,IAAI,EAAE,KAAK,EAAE,KAAK,IAAI,CAAC;IACvC,iFAAiF;IACjF,YAAY,CAAC,EAAE,CAAC,QAAQ,EAAE,MAAM,EAAE,QAAQ,EAAE,MAAM,EAAE,KAAK,EAAE,OAAO,KAAK,IAAI,CAAC;IAC5E,oDAAoD;IACpD,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,2EAA2E;IAC3E,IAAI,CAAC,EAAE,UAAU,CAAC;IAClB,iEAAiE;IACjE,UAAU,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC;IACrC,mDAAmD;IACnD,QAAQ,CAAC,EAAE,OAAO,CAAC;IACnB,2DAA2D;IAC3D,QAAQ,CAAC,EAAE,OAAO,CAAC;IACnB,qCAAqC;IACrC,QAAQ,CAAC,EAAE,OAAO,CAAC;IACnB,kCAAkC;IAClC,cAAc,CAAC,EAAE,OAAO,CAAC;IACzB,qCAAqC;IACrC,UAAU,CAAC,EAAE,OAAO,CAAC;IACrB,yCAAyC;IACzC,aAAa,CAAC,EAAE,OAAO,CAAC;IACxB,6BAA6B;IAC7B,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB,6BAA6B;IAC7B,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB,kCAAkC;IAClC,UAAU,CAAC,EAAE,OAAO,CAAC,KAAK,CAAC,CAAC;IAC5B,0BAA0B;IAC1B,YAAY,CAAC,EAAE,MAAM,CAAC;IACtB,uBAAuB;IACvB,YAAY,CAAC,EAAE,MAAM,CAAC;IACtB;;;OAGG;IACH,OAAO,CAAC,EAAE,OAAO,CAAC;IAClB;;;OAGG;IACH,aAAa,CAAC,EAAE,OAAO,CAAC;IACxB,qEAAqE;IACrE,SAAS,CAAC,EAAE,OAAO,CAAC;IACpB,6DAA6D;IAC7D,aAAa,CAAC,EAAE,eAAe,CAAC,KAAK,CAAC,EAAE,CAAC;IACzC,kEAAkE;IAClE,YAAY,CAAC,EAAE,eAAe,CAAC,KAAK,CAAC,CAAC;CACvC;AAED,wBAAgB,UAAU,CAAC,KAAK,SAAS,aAAa,GAAG,aAAa,EAAE,EACtE,IAAS,EACT,OAAO,EACP,QAAQ,EACR,YAAY,EACZ,YAAY,EACZ,IAAI,EACJ,IAAI,EAAE,QAAQ,EACd,UAAU,EACV,QAAQ,EACR,QAAgB,EAChB,QAAgB,EAChB,cAAqB,EACrB,UAAiB,EACjB,aAAoB,EACpB,OAAO,EACP,OAAO,EACP,UAAU,EACV,YAAY,EACZ,YAAY,EAAE,SAAS,EACvB,OAAc,EACd,aAAqB,EACrB,SAAiB,EACjB,aAAa,EACb,YAAY,GACb,EAAE,eAAe,CAAC,KAAK,CAAC,2CA0ExB;AAogBD,YAAY,EAAE,eAAe,EAAE,aAAa,EAAE,cAAc,EAAE,mBAAmB,EAAE,CAAC"}
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../src/components/TableInput/index.tsx"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;GAqBG;AAQH,OAAO,EAML,KAAK,UAAU,EAChB,MAAM,0BAA0B,CAAC;AAalC,OAAO,KAAK,EACV,eAAe,EACf,aAAa,EACb,mBAAmB,EACnB,cAAc,EACf,MAAM,aAAa,CAAC;AAIrB,OAAO,EAOL,KAAK,eAAe,EACrB,MAAM,UAAU,CAAC;AA+GlB,MAAM,WAAW,eAAe,CAAC,KAAK,SAAS,aAAa,GAAG,aAAa;IAC1E,mFAAmF;IACnF,IAAI,CAAC,EAAE,KAAK,EAAE,CAAC;IACf,4DAA4D;IAC5D,OAAO,EAAE,eAAe,CAAC,KAAK,CAAC,EAAE,CAAC;IAClC,0FAA0F;IAC1F,QAAQ,CAAC,EAAE,CAAC,IAAI,EAAE,KAAK,EAAE,KAAK,IAAI,CAAC;IACnC,sEAAsE;IACtE,YAAY,CAAC,EAAE,CAAC,IAAI,EAAE,KAAK,EAAE,KAAK,IAAI,CAAC;IACvC,iFAAiF;IACjF,YAAY,CAAC,EAAE,CAAC,QAAQ,EAAE,MAAM,EAAE,QAAQ,EAAE,MAAM,EAAE,KAAK,EAAE,OAAO,KAAK,IAAI,CAAC;IAC5E,oDAAoD;IACpD,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,2EAA2E;IAC3E,IAAI,CAAC,EAAE,UAAU,CAAC;IAClB,iEAAiE;IACjE,UAAU,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC;IACrC,mDAAmD;IACnD,QAAQ,CAAC,EAAE,OAAO,CAAC;IACnB,2DAA2D;IAC3D,QAAQ,CAAC,EAAE,OAAO,CAAC;IACnB,qCAAqC;IACrC,QAAQ,CAAC,EAAE,OAAO,CAAC;IACnB,kCAAkC;IAClC,cAAc,CAAC,EAAE,OAAO,CAAC;IACzB,qCAAqC;IACrC,UAAU,CAAC,EAAE,OAAO,CAAC;IACrB,yCAAyC;IACzC,aAAa,CAAC,EAAE,OAAO,CAAC;IACxB,6BAA6B;IAC7B,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB,6BAA6B;IAC7B,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB,kCAAkC;IAClC,UAAU,CAAC,EAAE,OAAO,CAAC,KAAK,CAAC,CAAC;IAC5B,0BAA0B;IAC1B,YAAY,CAAC,EAAE,MAAM,CAAC;IACtB,uBAAuB;IACvB,YAAY,CAAC,EAAE,MAAM,CAAC;IACtB;;;OAGG;IACH,OAAO,CAAC,EAAE,OAAO,CAAC;IAClB;;;OAGG;IACH,aAAa,CAAC,EAAE,OAAO,CAAC;IACxB,qEAAqE;IACrE,SAAS,CAAC,EAAE,OAAO,CAAC;IACpB,6DAA6D;IAC7D,aAAa,CAAC,EAAE,eAAe,CAAC,KAAK,CAAC,EAAE,CAAC;IACzC,kEAAkE;IAClE,YAAY,CAAC,EAAE,eAAe,CAAC,KAAK,CAAC,CAAC;CACvC;AAED,wBAAgB,UAAU,CAAC,KAAK,SAAS,aAAa,GAAG,aAAa,EAAE,EACtE,IAAS,EACT,OAAO,EACP,QAAQ,EACR,YAAY,EACZ,YAAY,EACZ,IAAI,EACJ,IAAI,EAAE,QAAQ,EACd,UAAU,EACV,QAAQ,EACR,QAAgB,EAChB,QAAgB,EAChB,cAAqB,EACrB,UAAiB,EACjB,aAAoB,EACpB,OAAO,EACP,OAAO,EACP,UAAU,EACV,YAAY,EACZ,YAAY,EAAE,SAAS,EACvB,OAAc,EACd,aAAqB,EACrB,SAAiB,EACjB,aAAa,EACb,YAAY,GACb,EAAE,eAAe,CAAC,KAAK,CAAC,2CA0ExB;AAiZD,YAAY,EAAE,eAAe,EAAE,aAAa,EAAE,cAAc,EAAE,mBAAmB,EAAE,CAAC"}