diff --git a/apps/desktop/src/components/grid/DataGrid.vue b/apps/desktop/src/components/grid/DataGrid.vue index 8ef8edf4b..000af2d20 100644 --- a/apps/desktop/src/components/grid/DataGrid.vue +++ b/apps/desktop/src/components/grid/DataGrid.vue @@ -8683,18 +8683,15 @@ function copySubmenu(): ContextMenuItem { items.push({ label: labels.insertNoPkMerged, action: () => copyRowAsInsertWithoutPrimaryKeys("merged"), - disabled: !canCopyRowAsInsertWithoutPrimaryKeys.value, }); items.push({ label: labels.insertNoPkRowByRow, action: () => copyRowAsInsertWithoutPrimaryKeys("row-by-row"), - disabled: !canCopyRowAsInsertWithoutPrimaryKeys.value, }); } else { items.push({ label: labels.insertNoPk, action: () => copyRowAsInsertWithoutPrimaryKeys(), - disabled: !canCopyRowAsInsertWithoutPrimaryKeys.value, }); } } diff --git a/packages/app-tests/useDataGridExport.test.ts b/packages/app-tests/useDataGridExport.test.ts index 75ec16961..a8f354ed5 100644 --- a/packages/app-tests/useDataGridExport.test.ts +++ b/packages/app-tests/useDataGridExport.test.ts @@ -390,9 +390,7 @@ test("copy row as INSERT refreshes prepared SQL after row data changes", async ( isDirtyCol: [false, false], status: "", }; - apiMock.buildDataGridCopyInsertStatement - .mockResolvedValueOnce("INSERT INTO users (id, name) VALUES (1, 'before');") - .mockResolvedValueOnce("INSERT INTO users (id, name) VALUES (1, 'after');"); + apiMock.buildDataGridCopyInsertStatement.mockResolvedValueOnce("INSERT INTO users (id, name) VALUES (1, 'before');").mockResolvedValueOnce("INSERT INTO users (id, name) VALUES (1, 'after');"); const composable = useDataGridExport({ columns: computed(() => ["id", "name"]), displayItems: computed(() => [row]), @@ -426,14 +424,61 @@ test("copy row as INSERT refreshes prepared SQL after row data changes", async ( await composable.copyRowAsInsert(); assert.equal(apiMock.buildDataGridCopyInsertStatement.mock.calls.length, 2); - assert.deepEqual(apiMock.buildDataGridCopyInsertStatement.mock.calls.map((call) => call[0].rows), [ - [[1, "before"]], - [[1, "after"]], - ]); - assert.deepEqual(clipboardMock.copyToClipboard.mock.calls.map((call) => call[0]), [ - "INSERT INTO users (id, name) VALUES (1, 'before');", - "INSERT INTO users (id, name) VALUES (1, 'after');", - ]); + assert.deepEqual( + apiMock.buildDataGridCopyInsertStatement.mock.calls.map((call) => call[0].rows), + [[[1, "before"]], [[1, "after"]]], + ); + assert.deepEqual( + clipboardMock.copyToClipboard.mock.calls.map((call) => call[0]), + ["INSERT INTO users (id, name) VALUES (1, 'before');", "INSERT INTO users (id, name) VALUES (1, 'after');"], + ); +}); + +test("copy row as INSERT works without prior prefetch (first context-menu click)", async () => { + const contextCell = ref({ rowId: 1, rowIndex: 0, col: 0 }); + const row = { + id: 1, + data: [1, "alice"], + isNew: false, + isDeleted: false, + isDirtyCol: [false, false], + status: "", + }; + apiMock.buildDataGridCopyInsertStatement.mockResolvedValueOnce("INSERT INTO users (id, name) VALUES (1, 'alice');"); + const composable = useDataGridExport({ + columns: computed(() => ["id", "name"]), + displayItems: computed(() => [row]), + sql: computed(() => undefined), + tableMeta: computed(() => ({ + tableName: "users", + primaryKeys: ["id"], + })), + databaseType: computed(() => "mysql"), + connectionId: computed(() => "conn-1"), + database: computed(() => "db"), + context: computed(() => "table-data"), + sourceColumns: computed(() => undefined), + columnTypes: computed(() => undefined), + whereInput: computed(() => undefined), + orderBy: computed(() => undefined), + exportBatchSize: computed(() => 1000), + hasCellSelection: computed(() => false), + selectedCells: computed(() => ({ columns: [], rows: [] })), + selectedRange: computed(() => null), + contextCell, + getRowItem: () => row, + selectedRowIds: ref(new Set()), + hasRowSelection: computed(() => false), + }); + + assert.equal(composable.canCopyRowAsInsert.value, true); + assert.equal(composable.canCopyPreparedInsert(false), false); + + await composable.copyRowAsInsert(); + + assert.equal(apiMock.buildDataGridCopyInsertStatement.mock.calls.length, 1); + assert.equal(clipboardMock.copyToClipboard.mock.calls[0][0], "INSERT INTO users (id, name) VALUES (1, 'alice');"); + assert.equal(composable.canCopyPreparedInsert(false), true); }); test("default data grid export file names use sanitized base names and compact local timestamps", () => {