From 9e768b0cbf66b5c4c556aba063b98bd500c0cc47 Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Mon, 6 Jul 2026 05:38:23 +0000 Subject: [PATCH 1/2] Initial plan From 060082a513e38758ea4fc7b3fd6a1556ab252f3f Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Mon, 6 Jul 2026 05:45:22 +0000 Subject: [PATCH 2/2] fix: ensure canvas repeater uses builder async props --- .../canvas/static/static-canvas.test.tsx | 82 +++++++++++++++++++ .../canvas/static/static-canvas.tsx | 32 ++++---- src/hooks/async-props/use-async-props.ts | 4 +- 3 files changed, 103 insertions(+), 15 deletions(-) create mode 100644 src/core/components/canvas/static/static-canvas.test.tsx diff --git a/src/core/components/canvas/static/static-canvas.test.tsx b/src/core/components/canvas/static/static-canvas.test.tsx new file mode 100644 index 00000000..6d16571c --- /dev/null +++ b/src/core/components/canvas/static/static-canvas.test.tsx @@ -0,0 +1,82 @@ +import { Provider, createStore, useAtomValue } from "jotai"; +import React from "react"; +import { describe, expect, it, vi } from "vitest"; +import { chaiBuilderPropsAtom } from "~/atoms/builder"; +import { builderStore } from "~/atoms/store"; +import { render, screen } from "@testing-library/react"; +import StaticCanvas from "./static-canvas"; + +vi.mock("~/core/components/canvas/dnd/drag-and-drop/hooks", () => ({ + useDragAndDrop: () => ({ onDragOver: vi.fn(), onDrop: vi.fn(), onDragEnd: vi.fn() }), + useDropIndicator: () => ({ isVisible: false, isEmpty: false }), +})); +vi.mock("~/core/components/canvas/static/add-block-at-bottom", () => ({ + AddBlockAtBottom: () => null, +})); +vi.mock("~/core/components/canvas/static/chai-canvas", () => ({ + Canvas: ({ children }: { children: React.ReactNode }) => <>{children}, +})); +vi.mock("~/core/components/canvas/static/head-tags", () => ({ + HeadTags: () => null, +})); +vi.mock("~/core/components/canvas/static/resizable-canvas-wrapper", () => ({ + ResizableCanvasWrapper: ({ children }: { children: React.ReactNode }) => <>{children}, +})); +vi.mock("~/core/components/canvas/static/use-canvas-scale", () => ({ + useCanvasScale: () => ({}), +})); +vi.mock("~/core/components/canvas/static/canvas-events-watcher", () => ({ + CanvasEventsWatcher: () => null, +})); +vi.mock("~/core/components/canvas/block-floating-actions", () => ({ + BlockSelectionHighlighter: () => null, +})); +vi.mock("~/core/components/canvas/keyboar-handler", () => ({ + KeyboardHandler: () => null, +})); +vi.mock("~/components/ui/skeleton", () => ({ + Skeleton: () => null, +})); +vi.mock("~/core/frame", () => ({ + ChaiFrame: ({ children }: { children: React.ReactNode }) => <>{children}, +})); +vi.mock("~/hooks/use-builder-prop", () => ({ + useBuilderProp: (_key: string, defaultValue: unknown) => defaultValue, +})); +vi.mock("~/hooks/use-canvas-iframe", () => ({ + useCanvasIframe: () => [null, vi.fn()], +})); +vi.mock("~/hooks/use-highlight-blockId", () => ({ + useHighlightBlockId: () => ["", vi.fn()], +})); +vi.mock("~/hooks/use-screen-size-width", () => ({ + useCanvasDisplayWidth: () => [1024], +})); +vi.mock("react-wrap-balancer", () => ({ + Provider: ({ children }: { children: React.ReactNode }) => <>{children}, +})); +vi.mock("~/core/components/canvas/static/static-blocks-renderer", () => ({ + StaticBlocksRenderer: () => { + const props = useAtomValue(chaiBuilderPropsAtom); + const hasProvider = typeof props?.getBlockAsyncProps === "function"; + return
{hasProvider ? "ready" : "missing"}
; + }, +})); + +describe("StaticCanvas", () => { + it("uses builderStore for iframe canvas subtree atoms", () => { + const asyncProvider = vi.fn(); + builderStore.set(chaiBuilderPropsAtom, { getBlockAsyncProps: asyncProvider } as any); + + const isolatedStore = createStore(); + isolatedStore.set(chaiBuilderPropsAtom, null); + + render( + + + , + ); + + expect(screen.getByTestId("canvas-provider-flag").textContent).toBe("ready"); + }); +}); diff --git a/src/core/components/canvas/static/static-canvas.tsx b/src/core/components/canvas/static/static-canvas.tsx index e8c0c817..2c74b76e 100644 --- a/src/core/components/canvas/static/static-canvas.tsx +++ b/src/core/components/canvas/static/static-canvas.tsx @@ -1,8 +1,10 @@ // @ts-nochecks +import { Provider as JotaiProvider } from "jotai"; import { isEmpty } from "lodash-es"; import { useCallback, useEffect, useMemo, useRef, useState } from "react"; -import { Provider } from "react-wrap-balancer"; +import { Provider as BalancerProvider } from "react-wrap-balancer"; +import { builderStore } from "~/atoms/store"; import { Skeleton } from "~/components/ui/skeleton"; import { BlockSelectionHighlighter } from "~/core/components/canvas/block-floating-actions"; import { useDragAndDrop, useDropIndicator } from "~/core/components/canvas/dnd/drag-and-drop/hooks"; @@ -73,19 +75,21 @@ const StaticCanvas = () => { - - - {loadingCanvas ? ( -
- -
- ) : ( - - )} - -
- -
+ + + + {loadingCanvas ? ( +
+ +
+ ) : ( + + )} + +
+ +
+
{dropIndicator.isVisible && (
({}); + type BlockAsyncProps = { status: "idle" | "loading" | "loaded" | "error"; props: Record; @@ -30,7 +32,7 @@ export const useAsyncProps = ( mockDataProvider?: (args: { block: ChaiBlock }) => object, ) => { const updateRuntimeProps = useUpdateBlocksPropsRealtime(); - const getAsyncBlockProps = useBuilderProp("getBlockAsyncProps", async (_args: { block: ChaiBlock }) => ({})); + const getAsyncBlockProps = useBuilderProp("getBlockAsyncProps", CHAI_ASYNC_PROPS_STUB); const setBlockRepeaterDataAtom = useSetAtom(blockRepeaterDataAtom); const depsString = JSON.stringify([block?._id, ...values(pick(block, dependencies ?? []))]); const isCollectionRepeater = block?._type === "Repeater" && startsWith(block.repeaterItems, `{{${COLLECTION_PREFIX}`);