Skip to content

Commit c38805b

Browse files
authored
Merge pull request #52 from olliethedev/feat/function-registry
feat: functions support
2 parents 5e2b46a + a354bee commit c38805b

File tree

59 files changed

+4307
-566
lines changed

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

59 files changed

+4307
-566
lines changed

__tests__/block-definitions.test.tsx

Lines changed: 8 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,7 @@ import {
44
getBlocksByCategory,
55
getBlockCategories,
66
getAllBlocks,
7-
BlockDefinition,
7+
type BlockDefinition,
88
} from "../lib/ui-builder/registry/block-definitions";
99

1010
describe("blockDefinitions", () => {
@@ -116,7 +116,7 @@ describe("getBlockCategories", () => {
116116

117117
it("should have no duplicate categories", () => {
118118
const categories = getBlockCategories();
119-
const uniqueCategories = [...new Set(categories)];
119+
const uniqueCategories = Array.from(new Set(categories));
120120
expect(categories.length).toBe(uniqueCategories.length);
121121
});
122122
});
@@ -143,12 +143,13 @@ describe("specific block templates", () => {
143143
it("should have a Card as the root template", () => {
144144
const block = blockDefinitions["login-01"];
145145
expect(block).toBeDefined();
146-
expect(block.template.type).toBe("Card");
146+
expect(block?.template.type).toBe("Card");
147147
});
148148

149149
it("should have required login form elements", () => {
150150
const block = blockDefinitions["login-01"];
151-
const templateStr = JSON.stringify(block.template);
151+
expect(block).toBeDefined();
152+
const templateStr = JSON.stringify(block!.template);
152153

153154
// Check for key form elements
154155
expect(templateStr).toContain("CardHeader");
@@ -162,9 +163,9 @@ describe("specific block templates", () => {
162163
it("should have dashboard stats cards", () => {
163164
const block = blockDefinitions["dashboard-01"];
164165
expect(block).toBeDefined();
165-
expect(block.category).toBe("dashboard");
166+
expect(block?.category).toBe("dashboard");
166167

167-
const templateStr = JSON.stringify(block.template);
168+
const templateStr = JSON.stringify(block!.template);
168169
expect(templateStr).toContain("Card");
169170
expect(templateStr).toContain("CardTitle");
170171
});
@@ -174,7 +175,7 @@ describe("specific block templates", () => {
174175
it("should use SidebarProvider as root", () => {
175176
const block = blockDefinitions["sidebar-01"];
176177
expect(block).toBeDefined();
177-
expect(block.template.type).toBe("SidebarProvider");
178+
expect(block?.template.type).toBe("SidebarProvider");
178179
});
179180
});
180181
});

__tests__/breakpoint-classname-control.test.tsx

Lines changed: 24 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -273,8 +273,9 @@ describe("BreakpointClassNameControl", () => {
273273

274274
const itemControls = screen.getAllByTestId("item-control-input");
275275
const baseInput = itemControls[0];
276+
expect(baseInput).toBeDefined();
276277

277-
fireEvent.change(baseInput, { target: { value: "w-full p-4" } });
278+
fireEvent.change(baseInput!, { target: { value: "w-full p-4" } });
278279

279280
await waitFor(() => {
280281
expect(mockOnChange).toHaveBeenLastCalledWith("w-full p-4");
@@ -287,8 +288,9 @@ describe("BreakpointClassNameControl", () => {
287288

288289
const itemControls = screen.getAllByTestId("item-control-input");
289290
const baseInput = itemControls[0];
291+
expect(baseInput).toBeDefined();
290292

291-
fireEvent.change(baseInput, { target: { value: "w-full" } });
293+
fireEvent.change(baseInput!, { target: { value: "w-full" } });
292294

293295
await waitFor(() => {
294296
expect(mockOnChange).toHaveBeenCalledWith("w-full md:w-8");
@@ -301,8 +303,9 @@ describe("BreakpointClassNameControl", () => {
301303

302304
const itemControls = screen.getAllByTestId("item-control-input");
303305
const baseInput = itemControls[0];
306+
expect(baseInput).toBeDefined();
304307

305-
fireEvent.change(baseInput, { target: { value: "w-full" } });
308+
fireEvent.change(baseInput!, { target: { value: "w-full" } });
306309

307310
await waitFor(() => {
308311
expect(mockOnChange).toHaveBeenCalledWith("w-full lg:w-12 sm:w-2");
@@ -317,8 +320,9 @@ describe("BreakpointClassNameControl", () => {
317320

318321
const itemControls = screen.getAllByTestId("item-control-input");
319322
const mdInput = itemControls[1];
323+
expect(mdInput).toBeDefined();
320324

321-
fireEvent.change(mdInput, { target: { value: "w-8 h-8" } });
325+
fireEvent.change(mdInput!, { target: { value: "w-8 h-8" } });
322326

323327
await waitFor(() => {
324328
expect(mockOnChange).toHaveBeenLastCalledWith("md:w-8 md:h-8");
@@ -331,8 +335,9 @@ describe("BreakpointClassNameControl", () => {
331335

332336
const itemControls = screen.getAllByTestId("item-control-input");
333337
const mdInput = itemControls[1];
338+
expect(mdInput).toBeDefined();
334339

335-
fireEvent.change(mdInput, { target: { value: "w-8" } });
340+
fireEvent.change(mdInput!, { target: { value: "w-8" } });
336341

337342
await waitFor(() => {
338343
expect(mockOnChange).toHaveBeenCalledWith("w-full p-4 md:w-8");
@@ -345,8 +350,9 @@ describe("BreakpointClassNameControl", () => {
345350

346351
const itemControls = screen.getAllByTestId("item-control-input");
347352
const mdInput = itemControls[1];
353+
expect(mdInput).toBeDefined();
348354

349-
fireEvent.change(mdInput, { target: { value: "w-8" } });
355+
fireEvent.change(mdInput!, { target: { value: "w-8" } });
350356

351357
await waitFor(() => {
352358
expect(mockOnChange).toHaveBeenCalledWith("w-full md:w-8 lg:w-12 sm:w-2");
@@ -359,8 +365,9 @@ describe("BreakpointClassNameControl", () => {
359365

360366
const itemControls = screen.getAllByTestId("item-control-input");
361367
const mdInput = itemControls[1];
368+
expect(mdInput).toBeDefined();
362369

363-
fireEvent.change(mdInput, { target: { value: "" } });
370+
fireEvent.change(mdInput!, { target: { value: "" } });
364371

365372
await waitFor(() => {
366373
expect(mockOnChange).toHaveBeenCalledWith("w-full");
@@ -425,8 +432,10 @@ describe("BreakpointClassNameControl", () => {
425432
render(<BreakpointClassNameControl value="" onChange={mockOnChange} />);
426433

427434
const itemControls = screen.getAllByTestId("item-control-input");
428-
fireEvent.change(itemControls[0], { target: { value: "w-full p-4" } });
429-
fireEvent.change(itemControls[1], { target: { value: "w-8 h-8" } });
435+
expect(itemControls[0]).toBeDefined();
436+
expect(itemControls[1]).toBeDefined();
437+
fireEvent.change(itemControls[0]!, { target: { value: "w-full p-4" } });
438+
fireEvent.change(itemControls[1]!, { target: { value: "w-8 h-8" } });
430439

431440
await waitFor(() => {
432441
// Should normalize spaces
@@ -439,7 +448,8 @@ describe("BreakpointClassNameControl", () => {
439448
render(<BreakpointClassNameControl value="lg:w-12" onChange={mockOnChange} />);
440449

441450
const itemControls = screen.getAllByTestId("item-control-input");
442-
fireEvent.change(itemControls[0], { target: { value: "w-full" } });
451+
expect(itemControls[0]).toBeDefined();
452+
fireEvent.change(itemControls[0]!, { target: { value: "w-full" } });
443453

444454
await waitFor(() => {
445455
expect(mockOnChange).toHaveBeenLastCalledWith("w-full lg:w-12");
@@ -505,12 +515,14 @@ describe("BreakpointClassNameControl", () => {
505515

506516
// Start with base tab
507517
const itemControls = screen.getAllByTestId("item-control-input");
508-
fireEvent.change(itemControls[0], { target: { value: "p-4" } });
518+
expect(itemControls[0]).toBeDefined();
519+
expect(itemControls[1]).toBeDefined();
520+
fireEvent.change(itemControls[0]!, { target: { value: "p-4" } });
509521

510522
// Switch to md tab
511523
const mdTrigger = screen.getByTestId("md-tab-trigger");
512524
await user.click(mdTrigger);
513-
fireEvent.change(itemControls[1], { target: { value: "p-8" } });
525+
fireEvent.change(itemControls[1]!, { target: { value: "p-8" } });
514526

515527
// Use multiselect to add more classes
516528
const multiselectInput = screen.getByTestId("multiselect-input");

__tests__/children-searchable-select.test.tsx

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@ import React from "react";
22
import { render, screen, fireEvent, waitFor } from "@testing-library/react";
33
import userEvent from "@testing-library/user-event";
44
import { ChildrenSearchableSelect } from "@/components/ui/ui-builder/internal/form-fields/children-searchable-select";
5-
import { ComponentLayer } from "@/components/ui/ui-builder/types";
5+
import type { ComponentLayer } from "@/components/ui/ui-builder/types";
66

77
// Mock the layer store
88
const mockSelectLayer = jest.fn();

__tests__/code-panel.test.tsx

Lines changed: 14 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,7 @@ import { render, screen, fireEvent, waitFor } from '@testing-library/react';
33
import { CodePanel } from '@/components/ui/ui-builder/components/code-panel';
44
import { useLayerStore } from '@/lib/ui-builder/store/layer-store';
55
import { useEditorStore } from '@/lib/ui-builder/store/editor-store';
6-
import { ComponentLayer } from '@/components/ui/ui-builder/types';
6+
import type { ComponentLayer } from '@/components/ui/ui-builder/types';
77
import { pageLayerToCode } from '@/components/ui/ui-builder/internal/utils/templates';
88

99
// Mock dependencies
@@ -92,6 +92,7 @@ describe('CodePanel', () => {
9292
mockUseEditorStore.mockImplementation((selector) => {
9393
const state = {
9494
registry: mockComponentRegistry,
95+
functionRegistry: undefined,
9596
} as any;
9697
return selector(state);
9798
});
@@ -137,7 +138,8 @@ export default Page;
137138
expect(mockPageLayerToCode).toHaveBeenCalledWith(
138139
mockPage,
139140
mockComponentRegistry,
140-
mockVariables
141+
mockVariables,
142+
undefined
141143
);
142144
});
143145

@@ -164,7 +166,8 @@ export default Page;
164166
expect(mockPageLayerToCode).toHaveBeenCalledWith(
165167
mockPage,
166168
mockComponentRegistry,
167-
[]
169+
[],
170+
undefined
168171
);
169172
});
170173

@@ -202,7 +205,8 @@ export default Page;
202205
expect(mockPageLayerToCode).toHaveBeenCalledWith(
203206
pageWithoutVars,
204207
mockComponentRegistry,
205-
mockVariables
208+
mockVariables,
209+
undefined
206210
);
207211
});
208212

@@ -219,7 +223,8 @@ export default Page;
219223
expect(mockPageLayerToCode).toHaveBeenCalledWith(
220224
mockPage,
221225
mockComponentRegistry,
222-
mockVariables
226+
mockVariables,
227+
undefined
223228
);
224229

225230
// The serialized data should show separate sections for variables and layers
@@ -241,7 +246,8 @@ export default Page;
241246
expect(mockPageLayerToCode).toHaveBeenCalledWith(
242247
mockPage,
243248
mockComponentRegistry,
244-
mockVariables
249+
mockVariables,
250+
undefined
245251
);
246252
});
247253

@@ -263,7 +269,8 @@ export default Page;
263269
expect(mockPageLayerToCode).toHaveBeenCalledWith(
264270
mockPage,
265271
mockComponentRegistry,
266-
[]
272+
[],
273+
undefined
267274
);
268275
});
269276
});

__tests__/component-registry.test.tsx

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -3,8 +3,8 @@
33
import {
44
generateFieldOverrides
55
} from "@/lib/ui-builder/store/editor-utils";
6-
import { ComponentLayer, RegistryEntry } from '@/components/ui/ui-builder/types';
7-
import { FieldConfigItem } from "@/components/ui/auto-form/types";
6+
import type { ComponentLayer, RegistryEntry } from '@/components/ui/ui-builder/types';
7+
import type { FieldConfigItem } from "@/components/ui/auto-form/types";
88
import { complexComponentDefinitions } from "@/lib/ui-builder/registry/complex-component-definitions";
99
import { primitiveComponentDefinitions } from "@/lib/ui-builder/registry/primitive-component-definitions";
1010

__tests__/config-panel.test.tsx

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,7 @@ import { render, screen, fireEvent, waitFor } from '@testing-library/react';
44
import userEvent from '@testing-library/user-event';
55
import { ConfigPanel } from '@/components/ui/ui-builder/internal/config-panel';
66
import { useLayerStore } from '@/lib/ui-builder/store/layer-store';
7-
import { ComponentLayer } from '@/components/ui/ui-builder/types';
7+
import type { ComponentLayer } from '@/components/ui/ui-builder/types';
88

99
// Mock the layer store
1010
jest.mock('@/lib/ui-builder/store/layer-store');

__tests__/editor-panel.test.tsx

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,7 @@ import { render, screen, fireEvent, waitFor } from "@testing-library/react";
44
import EditorPanel from "@/components/ui/ui-builder/internal/editor-panel";
55
import { useLayerStore } from "@/lib/ui-builder/store/layer-store";
66
import { useEditorStore } from "@/lib/ui-builder/store/editor-store";
7-
import { ComponentLayer, RegistryEntry } from "@/components/ui/ui-builder/types";
7+
import type { ComponentLayer, RegistryEntry } from "@/components/ui/ui-builder/types";
88
import { z } from "zod";
99

1010
// Mock dependencies
@@ -1345,7 +1345,9 @@ describe("EditorPanel", () => {
13451345
const { unmount } = renderEditorPanel();
13461346

13471347
const transformDiv = screen.getByTestId("transform-component");
1348-
expect(transformDiv).toHaveClass(expectedClasses[index]);
1348+
const expectedClass = expectedClasses[index];
1349+
expect(expectedClass).toBeDefined();
1350+
expect(transformDiv).toHaveClass(expectedClass!);
13491351
expect(transformDiv).toHaveClass("relative");
13501352

13511353
unmount();

__tests__/editor-store.test.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
import { renderHook, act } from '@testing-library/react';
22
import { useEditorStore } from '@/lib/ui-builder/store/editor-store';
3-
import { ComponentRegistry } from '@/components/ui/ui-builder/types';
3+
import type { ComponentRegistry } from '@/components/ui/ui-builder/types';
44
import { z } from 'zod';
55

66
describe('EditorStore', () => {

__tests__/editor-utils.test.ts

Lines changed: 7 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -3,9 +3,9 @@ import {
33
isPrimitiveComponent,
44
isCustomComponent
55
} from '@/lib/ui-builder/store/editor-utils';
6-
import { ComponentRegistry, ComponentLayer, RegistryEntry } from '@/components/ui/ui-builder/types';
7-
import { FieldConfigItem } from "@/components/ui/auto-form/types";
8-
import { ComponentType as ReactComponentType } from "react";
6+
import type { ComponentRegistry, ComponentLayer, RegistryEntry } from '@/components/ui/ui-builder/types';
7+
import type { FieldConfigItem } from "@/components/ui/auto-form/types";
8+
import type { ComponentType as ReactComponentType } from "react";
99
import { z } from 'zod';
1010

1111
describe('Editor Utils', () => {
@@ -269,8 +269,10 @@ describe('Editor Utils', () => {
269269

270270
const overrides = generateFieldOverrides(mockRegistry, complexLayer);
271271

272-
expect(overrides.label.label).toBe('Label for Complex Button');
273-
expect(overrides.disabled.description).toBe('Disable state for complex-1');
272+
expect(overrides.label).toBeDefined();
273+
expect(overrides.disabled).toBeDefined();
274+
expect(overrides.label!.label).toBe('Label for Complex Button');
275+
expect(overrides.disabled!.description).toBe('Disable state for complex-1');
274276
});
275277
});
276278

__tests__/element-selector.test.tsx

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
import React from "react";
22
import { render, screen, fireEvent, waitFor } from "@testing-library/react";
33
import { ElementSelector } from "@/components/ui/ui-builder/internal/components/element-selector";
4-
import { ComponentLayer } from '@/components/ui/ui-builder/types';
4+
import type { ComponentLayer } from '@/components/ui/ui-builder/types';
55

66
// Mock ResizeObserver
77
const mockResizeObserver = {

0 commit comments

Comments
 (0)