mirror of
https://github.com/l0ng-ai/openidea.git
synced 2026-11-13 04:34:21 -10:00
Compare commits
1
Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
2bb14c9704 |
No files matched your search
@@ -200,6 +200,9 @@ function Pager({ tab }: { tab: ResultTab }) {
|
||||
);
|
||||
}
|
||||
|
||||
/** Unsubmitted edits by the rows they were made on, kept while another result tab is shown. */
|
||||
const keptEdits = new WeakMap<DbQueryResult, PendingEdits>();
|
||||
|
||||
function Result({ tab }: { tab: ResultTab }) {
|
||||
const [view, setView] = useState<"table" | "chart">("table");
|
||||
const [panel, setPanel] = useState<DataPanel | null>(null);
|
||||
@@ -275,14 +278,19 @@ function Result({ tab }: { tab: ResultTab }) {
|
||||
sources.some((s) => s !== null);
|
||||
|
||||
// The pending edits belong to the result they were made on: fresh rows (a
|
||||
// re-run, another page) start over, without an effect.
|
||||
const [pending, setPending] = useState<{ outcome: DbQueryResult | null; edits: PendingEdits }>({
|
||||
outcome: null,
|
||||
edits: emptyEdits(),
|
||||
});
|
||||
// re-run, another page) start over, without an effect. They outlive the pane,
|
||||
// which remounts when another result tab is selected.
|
||||
const [pending, setPending] = useState<{ outcome: DbQueryResult | null; edits: PendingEdits }>(() => ({
|
||||
outcome: r ?? null,
|
||||
edits: (r && keptEdits.get(r)) ?? emptyEdits(),
|
||||
}));
|
||||
const edits = pending.outcome === r ? pending.edits : emptyEdits();
|
||||
const setEdits = (f: (e: PendingEdits) => PendingEdits) =>
|
||||
setPending((previous) => ({ outcome: r, edits: f(previous.outcome === r ? previous.edits : emptyEdits()) }));
|
||||
setPending((previous) => {
|
||||
const next = f(previous.outcome === r ? previous.edits : emptyEdits());
|
||||
if (r) keptEdits.set(r, next);
|
||||
return { outcome: r ?? null, edits: next };
|
||||
});
|
||||
const loaded = r?.rows.length ?? 0;
|
||||
const rowCount = loaded + edits.inserts.length;
|
||||
const value = useCallback(
|
||||
|
||||
@@ -235,6 +235,29 @@ describe("editable result grid", () => {
|
||||
expect(backend.applyEdits).not.toHaveBeenCalled();
|
||||
});
|
||||
|
||||
it("keeps unsubmitted edits while another result tab is shown", async () => {
|
||||
const first = show("SELECT * FROM users WHERE id < 10", [{ name: "id" }, { name: "name" }], [["1", "Ann"]]);
|
||||
const second: ResultTab = {
|
||||
...first,
|
||||
id: nextId++,
|
||||
title: "Result 2",
|
||||
outcome: { ...first.outcome, result: { ...first.outcome.result! } },
|
||||
};
|
||||
useDatabase.setState((s) => ({ results: [...s.results, second] }));
|
||||
await act(async () => render(<ResultsView />));
|
||||
fireEvent.doubleClick(await screen.findByRole("gridcell", { name: "Ann" }));
|
||||
const input = screen.getByRole("textbox", { name: "Edit name" });
|
||||
fireEvent.change(input, { target: { value: "Bob" } });
|
||||
fireEvent.keyDown(input, { key: "Enter" });
|
||||
|
||||
await act(async () => useDatabase.setState({ activeResult: second.id }));
|
||||
expect(screen.getByRole("button", { name: "Submit" }).hasAttribute("disabled")).toBe(true);
|
||||
await act(async () => useDatabase.setState({ activeResult: first.id }));
|
||||
|
||||
expect(screen.getByRole("gridcell", { name: "Bob" })).toBeTruthy();
|
||||
expect(screen.getByRole("button", { name: "Submit" }).hasAttribute("disabled")).toBe(false);
|
||||
});
|
||||
|
||||
it("keeps an aliased column read-only while the plain ones stay editable", async () => {
|
||||
show("SELECT id, name AS n FROM users", [{ name: "id" }, { name: "n" }], [["1", "Ann"]]);
|
||||
await act(async () => render(<ResultsView />));
|
||||
|
||||
Reference in new issue
Block a user