fix(widgets): bind pending table layouts
This commit is contained in:
2 files changed
+113
-17
No files matched your search
@@ -0,0 +1,83 @@
|
||||
import { act, createElement } from "react";
|
||||
import { createRoot } from "react-dom/client";
|
||||
import { afterEach, beforeAll, beforeEach, describe, expect, test, vi } from "vitest";
|
||||
|
||||
import type { Root } from "react-dom/client";
|
||||
import type { DataTableColumn } from "mantine-datatable";
|
||||
|
||||
const tableState = vi.hoisted(() => ({
|
||||
columnsOrder: ["name", "state"],
|
||||
columnsWidth: [] as Record<string, string | number>[],
|
||||
}));
|
||||
|
||||
vi.mock("mantine-datatable", () => ({
|
||||
useDataTableColumns: ({ columns }: { columns: DataTableColumn<unknown>[] }) => ({
|
||||
effectiveColumns: columns,
|
||||
columnsOrder: tableState.columnsOrder,
|
||||
columnsWidth: tableState.columnsWidth,
|
||||
setColumnsOrder: vi.fn(),
|
||||
setMultipleColumnWidths: vi.fn(),
|
||||
}),
|
||||
}));
|
||||
|
||||
import { usePersistedTableLayout } from "./use-persisted-table-layout";
|
||||
|
||||
const columns: DataTableColumn<{ name: string; state: string }>[] = [{ accessor: "name" }, { accessor: "state" }];
|
||||
const columnAccessors = ["name", "state"] as const;
|
||||
|
||||
const LayoutHarness = ({ itemId, onLayoutChange }: { itemId: string; onLayoutChange: (layout: unknown) => void }) => {
|
||||
usePersistedTableLayout({
|
||||
columns,
|
||||
columnAccessors,
|
||||
columnOrder: "",
|
||||
columnWidths: "",
|
||||
itemId,
|
||||
storeKeyPrefix: "test-table",
|
||||
onLayoutChange,
|
||||
});
|
||||
return null;
|
||||
};
|
||||
|
||||
describe("usePersistedTableLayout", () => {
|
||||
let container: HTMLDivElement;
|
||||
let root: Root;
|
||||
|
||||
beforeAll(() => {
|
||||
Object.defineProperty(globalThis, "IS_REACT_ACT_ENVIRONMENT", { value: true, writable: true });
|
||||
});
|
||||
|
||||
beforeEach(() => {
|
||||
vi.useFakeTimers();
|
||||
tableState.columnsOrder = ["name", "state"];
|
||||
tableState.columnsWidth = [];
|
||||
container = document.createElement("div");
|
||||
root = createRoot(container);
|
||||
});
|
||||
|
||||
afterEach(async () => {
|
||||
await act(async () => root.unmount());
|
||||
vi.useRealTimers();
|
||||
});
|
||||
|
||||
test("flushes a pending layout through the callback for its originating item", async () => {
|
||||
const firstItemLayoutChange = vi.fn();
|
||||
const secondItemLayoutChange = vi.fn();
|
||||
|
||||
await act(async () => {
|
||||
root.render(createElement(LayoutHarness, { itemId: "first", onLayoutChange: firstItemLayoutChange }));
|
||||
});
|
||||
|
||||
tableState.columnsOrder = ["state", "name"];
|
||||
await act(async () => {
|
||||
root.render(createElement(LayoutHarness, { itemId: "first", onLayoutChange: firstItemLayoutChange }));
|
||||
});
|
||||
expect(firstItemLayoutChange).not.toHaveBeenCalled();
|
||||
|
||||
await act(async () => {
|
||||
root.render(createElement(LayoutHarness, { itemId: "second", onLayoutChange: secondItemLayoutChange }));
|
||||
});
|
||||
|
||||
expect(firstItemLayoutChange).toHaveBeenCalledWith({ columnOrder: JSON.stringify(["state", "name"]) });
|
||||
expect(secondItemLayoutChange).not.toHaveBeenCalled();
|
||||
});
|
||||
});
|
||||
@@ -9,6 +9,12 @@ interface TableLayoutOptions extends Record<string, unknown> {
|
||||
columnWidths?: string;
|
||||
}
|
||||
|
||||
interface PendingTableLayout {
|
||||
layout: TableLayoutOptions;
|
||||
onLayoutChange: (layout: TableLayoutOptions) => void;
|
||||
storeKey: string;
|
||||
}
|
||||
|
||||
interface UseTableLayoutPersistenceProps {
|
||||
boardId: string | undefined;
|
||||
hasChangeAccess: boolean;
|
||||
@@ -168,25 +174,19 @@ export const usePersistedTableLayout = <T>({
|
||||
visibleAccessorSet,
|
||||
]);
|
||||
|
||||
const pendingLayout = useRef<TableLayoutOptions>({});
|
||||
const pendingLayout = useRef<PendingTableLayout | undefined>(undefined);
|
||||
const saveTimeout = useRef<number | undefined>(undefined);
|
||||
const onLayoutChangeRef = useRef(onLayoutChange);
|
||||
useEffect(() => {
|
||||
onLayoutChangeRef.current = onLayoutChange;
|
||||
}, [onLayoutChange]);
|
||||
const flushPendingLayout = useCallback(() => {
|
||||
const layout = pendingLayout.current;
|
||||
pendingLayout.current = {};
|
||||
const pending = pendingLayout.current;
|
||||
pendingLayout.current = undefined;
|
||||
window.clearTimeout(saveTimeout.current);
|
||||
saveTimeout.current = undefined;
|
||||
if (Object.keys(layout).length > 0) onLayoutChangeRef.current(layout);
|
||||
if (pending) pending.onLayoutChange(pending.layout);
|
||||
}, []);
|
||||
useEffect(
|
||||
() => () => {
|
||||
window.clearTimeout(saveTimeout.current);
|
||||
flushPendingLayout();
|
||||
},
|
||||
[flushPendingLayout],
|
||||
);
|
||||
useEffect(() => {
|
||||
if (pendingLayout.current && pendingLayout.current.storeKey !== storeKey) flushPendingLayout();
|
||||
}, [flushPendingLayout, storeKey]);
|
||||
useEffect(() => () => flushPendingLayout(), [flushPendingLayout]);
|
||||
useEffect(() => {
|
||||
if (!hydrated.current) return;
|
||||
|
||||
@@ -211,10 +211,23 @@ export const usePersistedTableLayout = <T>({
|
||||
newLayout.columnWidths = JSON.stringify(widthMap);
|
||||
}
|
||||
|
||||
pendingLayout.current = { ...pendingLayout.current, ...newLayout };
|
||||
if (pendingLayout.current && pendingLayout.current.storeKey !== storeKey) flushPendingLayout();
|
||||
pendingLayout.current = pendingLayout.current
|
||||
? { ...pendingLayout.current, layout: { ...pendingLayout.current.layout, ...newLayout } }
|
||||
: { layout: newLayout, onLayoutChange, storeKey };
|
||||
window.clearTimeout(saveTimeout.current);
|
||||
saveTimeout.current = window.setTimeout(flushPendingLayout, layoutSaveDelay);
|
||||
}, [columnAccessors, columnsOrder, columnsWidth, flushPendingLayout, savedOrder, savedWidths, visibleAccessorSet]);
|
||||
}, [
|
||||
columnAccessors,
|
||||
columnsOrder,
|
||||
columnsWidth,
|
||||
flushPendingLayout,
|
||||
onLayoutChange,
|
||||
savedOrder,
|
||||
savedWidths,
|
||||
storeKey,
|
||||
visibleAccessorSet,
|
||||
]);
|
||||
|
||||
return { effectiveColumns, storeKey };
|
||||
};
|
||||
Reference in new issue
Block a user