wyreframe 0.2.1 → 0.3.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/index.d.ts +9 -0
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js.map +1 -1
- package/package.json +1 -1
- package/src/index.test.ts +252 -0
- package/src/index.ts +10 -0
- package/src/parser/Errors/ErrorTypes.mjs +6 -9
- package/src/parser/Errors/ErrorTypes.res +13 -10
- package/src/renderer/Renderer.gen.tsx +8 -1
- package/src/renderer/Renderer.mjs +21 -11
- package/src/renderer/Renderer.res +34 -4
package/dist/index.d.ts
CHANGED
|
@@ -99,6 +99,8 @@ export interface SceneManager {
|
|
|
99
99
|
/** Get all available scene IDs */
|
|
100
100
|
getSceneIds: () => string[];
|
|
101
101
|
}
|
|
102
|
+
/** Scene change callback type */
|
|
103
|
+
export type OnSceneChangeCallback = (fromScene: string | undefined, toScene: string) => void;
|
|
102
104
|
/** Render options */
|
|
103
105
|
export interface RenderOptions {
|
|
104
106
|
/** Theme name */
|
|
@@ -109,6 +111,13 @@ export interface RenderOptions {
|
|
|
109
111
|
injectStyles?: boolean;
|
|
110
112
|
/** Additional CSS class for container */
|
|
111
113
|
containerClass?: string;
|
|
114
|
+
/**
|
|
115
|
+
* Callback fired when navigating between scenes.
|
|
116
|
+
* Useful for implementing scene history, analytics, or state synchronization.
|
|
117
|
+
* @param fromScene - The scene ID navigating from (undefined if initial navigation)
|
|
118
|
+
* @param toScene - The scene ID navigating to
|
|
119
|
+
*/
|
|
120
|
+
onSceneChange?: OnSceneChangeCallback;
|
|
112
121
|
}
|
|
113
122
|
/** Render result */
|
|
114
123
|
export interface RenderResult {
|
package/dist/index.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"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
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AAaH,wCAAwC;AACxC,MAAM,MAAM,UAAU,GAClB,SAAS,GACT,QAAQ,GACR,QAAQ,GACR,kBAAkB,GAClB,QAAQ,GACR,kBAAkB,CAAC;AAEvB,0CAA0C;AAC1C,MAAM,MAAM,cAAc,GAAG,MAAM,GAAG,YAAY,GAAG,aAAa,GAAG,MAAM,CAAC;AAE5E,qBAAqB;AACrB,MAAM,MAAM,SAAS,GAAG,MAAM,GAAG,QAAQ,GAAG,OAAO,CAAC;AAEpD,4BAA4B;AAC5B,MAAM,MAAM,aAAa,GAAG,SAAS,GAAG,WAAW,GAAG,OAAO,CAAC;AAE9D,oCAAoC;AACpC,MAAM,WAAW,UAAU;IACzB,IAAI,EAAE,MAAM,CAAC;IACb,MAAM,EAAE,MAAM,CAAC;IACf,UAAU,CAAC,EAAE,cAAc,CAAC;CAC7B;AAED,MAAM,MAAM,MAAM,GAAG,UAAU,CAAC;AAEhC,uBAAuB;AACvB,MAAM,WAAW,UAAU;IACzB,GAAG,EAAE,KAAK,CAAC;IACX,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,QAAQ,EAAE,OAAO,EAAE,CAAC;CACrB;AAED,MAAM,WAAW,aAAa;IAC5B,GAAG,EAAE,QAAQ,CAAC;IACd,EAAE,EAAE,MAAM,CAAC;IACX,IAAI,EAAE,MAAM,CAAC;IACb,KAAK,EAAE,SAAS,CAAC;IACjB,OAAO,CAAC,EAAE,aAAa,CAAC;IACxB,OAAO,EAAE,MAAM,EAAE,CAAC;CACnB;AAED,MAAM,WAAW,YAAY;IAC3B,GAAG,EAAE,OAAO,CAAC;IACb,EAAE,EAAE,MAAM,CAAC;IACX,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,OAAO,CAAC,EAAE,MAAM,CAAC;CAClB;AAED,MAAM,WAAW,WAAW;IAC1B,GAAG,EAAE,MAAM,CAAC;IACZ,EAAE,EAAE,MAAM,CAAC;IACX,IAAI,EAAE,MAAM,CAAC;IACb,KAAK,EAAE,SAAS,CAAC;IACjB,OAAO,EAAE,MAAM,EAAE,CAAC;CACnB;AAED,MAAM,WAAW,WAAW;IAC1B,GAAG,EAAE,MAAM,CAAC;IACZ,OAAO,EAAE,MAAM,CAAC;IAChB,QAAQ,EAAE,OAAO,CAAC;IAClB,KAAK,EAAE,SAAS,CAAC;CAClB;AAED,MAAM,WAAW,eAAe;IAC9B,GAAG,EAAE,UAAU,CAAC;IAChB,OAAO,EAAE,OAAO,CAAC;IACjB,KAAK,EAAE,MAAM,CAAC;CACf;AAED,MAAM,WAAW,cAAc;IAC7B,GAAG,EAAE,SAAS,CAAC;CAChB;AAED,MAAM,WAAW,UAAU;IACzB,GAAG,EAAE,KAAK,CAAC;IACX,QAAQ,EAAE,OAAO,EAAE,CAAC;IACpB,KAAK,EAAE,SAAS,CAAC;CAClB;AAED,MAAM,WAAW,cAAc;IAC7B,GAAG,EAAE,SAAS,CAAC;IACf,IAAI,EAAE,MAAM,CAAC;IACb,QAAQ,EAAE,OAAO,EAAE,CAAC;CACrB;AAED,MAAM,MAAM,OAAO,GACf,UAAU,GACV,aAAa,GACb,YAAY,GACZ,WAAW,GACX,WAAW,GACX,eAAe,GACf,cAAc,GACd,UAAU,GACV,cAAc,CAAC;AAEnB,uBAAuB;AACvB,MAAM,WAAW,KAAK;IACpB,EAAE,EAAE,MAAM,CAAC;IACX,KAAK,EAAE,MAAM,CAAC;IACd,MAAM,EAAE,UAAU,CAAC;IACnB,UAAU,EAAE,cAAc,CAAC;IAC3B,QAAQ,EAAE,OAAO,EAAE,CAAC;CACrB;AAED,2BAA2B;AAC3B,MAAM,WAAW,GAAG;IAClB,MAAM,EAAE,KAAK,EAAE,CAAC;CACjB;AAED,8BAA8B;AAC9B,MAAM,WAAW,UAAU;IACzB,OAAO,EAAE,MAAM,CAAC;IAChB,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,MAAM,CAAC,EAAE,MAAM,CAAC;CACjB;AAED,gDAAgD;AAChD,MAAM,WAAW,YAAY;IAC3B,gCAAgC;IAChC,IAAI,EAAE,CAAC,OAAO,EAAE,MAAM,KAAK,IAAI,CAAC;IAChC,+BAA+B;IAC/B,eAAe,EAAE,MAAM,MAAM,GAAG,SAAS,CAAC;IAC1C,kCAAkC;IAClC,WAAW,EAAE,MAAM,MAAM,EAAE,CAAC;CAC7B;AAED,iCAAiC;AACjC,MAAM,MAAM,qBAAqB,GAAG,CAAC,SAAS,EAAE,MAAM,GAAG,SAAS,EAAE,OAAO,EAAE,MAAM,KAAK,IAAI,CAAC;AAE7F,qBAAqB;AACrB,MAAM,WAAW,aAAa;IAC5B,iBAAiB;IACjB,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,0CAA0C;IAC1C,WAAW,CAAC,EAAE,OAAO,CAAC;IACtB,4CAA4C;IAC5C,YAAY,CAAC,EAAE,OAAO,CAAC;IACvB,yCAAyC;IACzC,cAAc,CAAC,EAAE,MAAM,CAAC;IACxB;;;;;OAKG;IACH,aAAa,CAAC,EAAE,qBAAqB,CAAC;CACvC;AAED,oBAAoB;AACpB,MAAM,WAAW,YAAY;IAC3B,4BAA4B;IAC5B,IAAI,EAAE,WAAW,CAAC;IAClB,mCAAmC;IACnC,YAAY,EAAE,YAAY,CAAC;CAC5B;AAMD,MAAM,MAAM,kBAAkB,GAAG;IAC/B,OAAO,EAAE,IAAI,CAAC;IACd,GAAG,EAAE,GAAG,CAAC;IACT,QAAQ,EAAE,UAAU,EAAE,CAAC;CACxB,CAAC;AAEF,MAAM,MAAM,gBAAgB,GAAG;IAC7B,OAAO,EAAE,KAAK,CAAC;IACf,MAAM,EAAE,UAAU,EAAE,CAAC;CACtB,CAAC;AAEF,MAAM,MAAM,WAAW,GAAG,kBAAkB,GAAG,gBAAgB,CAAC;AAEhE,MAAM,MAAM,wBAAwB,GAAG;IACrC,OAAO,EAAE,IAAI,CAAC;IACd,YAAY,EAAE,OAAO,EAAE,CAAC;CACzB,CAAC;AAEF,MAAM,MAAM,sBAAsB,GAAG;IACnC,OAAO,EAAE,KAAK,CAAC;IACf,MAAM,EAAE,UAAU,EAAE,CAAC;CACtB,CAAC;AAEF,MAAM,MAAM,iBAAiB,GAAG,wBAAwB,GAAG,sBAAsB,CAAC;AAElF,MAAM,MAAM,qBAAqB,GAAG;IAClC,OAAO,EAAE,IAAI,CAAC;IACd,IAAI,EAAE,WAAW,CAAC;IAClB,YAAY,EAAE,YAAY,CAAC;IAC3B,GAAG,EAAE,GAAG,CAAC;IACT,QAAQ,EAAE,UAAU,EAAE,CAAC;CACxB,CAAC;AAEF,MAAM,MAAM,mBAAmB,GAAG;IAChC,OAAO,EAAE,KAAK,CAAC;IACf,MAAM,EAAE,UAAU,EAAE,CAAC;CACtB,CAAC;AAEF,MAAM,MAAM,cAAc,GAAG,qBAAqB,GAAG,mBAAmB,CAAC;AAsBzE;;;;;;;;;;;;;;;;GAgBG;AACH,wBAAgB,KAAK,CAAC,IAAI,EAAE,MAAM,GAAG,WAAW,CAS/C;AAED;;;;;;;;;;;;;;;;;;GAkBG;AACH,wBAAgB,YAAY,CAAC,IAAI,EAAE,MAAM,GAAG,GAAG,CAW9C;AAED;;;;;GAKG;AACH,wBAAgB,cAAc,CAAC,SAAS,EAAE,MAAM,GAAG,WAAW,CAS7D;AAED;;;;;GAKG;AACH,wBAAgB,iBAAiB,CAAC,GAAG,EAAE,MAAM,GAAG,iBAAiB,CAQhE;AAED;;;;;;;;;;;;;;;GAeG;AACH,wBAAgB,MAAM,CAAC,GAAG,EAAE,GAAG,EAAE,OAAO,CAAC,EAAE,aAAa,GAAG,YAAY,CAqCtE;AAED;;;;;;;;;;;;;;;;GAgBG;AACH,wBAAgB,QAAQ,CAAC,IAAI,EAAE,MAAM,EAAE,OAAO,CAAC,EAAE,aAAa,GAAG,cAAc,CAgB9E;AAED;;;;;;;;;;;;;GAaG;AACH,wBAAgB,eAAe,CAC7B,IAAI,EAAE,MAAM,EACZ,OAAO,CAAC,EAAE,aAAa,GACtB,YAAY,GAAG;IAAE,GAAG,EAAE,GAAG,CAAA;CAAE,CAK7B;AAGD,eAAO,MAAM,OAAO,EAAE,MAAuB,CAAC;AAC9C,eAAO,MAAM,cAAc,EAAE,MAA8B,CAAC;;;;;;;;;;;;AAG5D,wBAUE"}
|
package/dist/index.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AAEH,0EAA0E;AAC1E,yFAAyF;AACzF,yCAAyC;AACzC,OAAO,KAAK,MAAM,MAAM,0BAA0B,CAAC;AACnD,yCAAyC;AACzC,OAAO,KAAK,QAAQ,MAAM,8BAA8B,CAAC;
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"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"}
|
package/package.json
CHANGED
|
@@ -0,0 +1,252 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Tests for TypeScript API wrapper input validation
|
|
3
|
+
*
|
|
4
|
+
* These tests verify that render() provides helpful error messages
|
|
5
|
+
* when called with incorrect arguments (Issue #1).
|
|
6
|
+
*/
|
|
7
|
+
|
|
8
|
+
import { describe, test, expect, vi } from 'vitest';
|
|
9
|
+
import { parse, render, createUI } from './index';
|
|
10
|
+
import type { AST, ParseResult, OnSceneChangeCallback } from './index';
|
|
11
|
+
|
|
12
|
+
describe('render() input validation (Issue #1)', () => {
|
|
13
|
+
const validWireframe = `
|
|
14
|
+
@scene: test
|
|
15
|
+
|
|
16
|
+
+-------+
|
|
17
|
+
| Hello |
|
|
18
|
+
+-------+
|
|
19
|
+
`;
|
|
20
|
+
|
|
21
|
+
test('throws descriptive error when passed successful ParseResult instead of AST', () => {
|
|
22
|
+
const result = parse(validWireframe);
|
|
23
|
+
|
|
24
|
+
// Verify we have a successful parse result
|
|
25
|
+
expect(result.success).toBe(true);
|
|
26
|
+
|
|
27
|
+
// This is the common mistake: passing result instead of result.ast
|
|
28
|
+
expect(() => {
|
|
29
|
+
render(result as unknown as AST);
|
|
30
|
+
}).toThrow('render() expects an AST object, but received a ParseResult');
|
|
31
|
+
|
|
32
|
+
expect(() => {
|
|
33
|
+
render(result as unknown as AST);
|
|
34
|
+
}).toThrow('Did you forget to extract .ast?');
|
|
35
|
+
});
|
|
36
|
+
|
|
37
|
+
test('throws descriptive error when passed failed ParseResult', () => {
|
|
38
|
+
// Invalid wireframe that will fail to parse
|
|
39
|
+
const invalidWireframe = '+--incomplete';
|
|
40
|
+
const result = parse(invalidWireframe);
|
|
41
|
+
|
|
42
|
+
// This should be a failed parse
|
|
43
|
+
expect(result.success).toBe(false);
|
|
44
|
+
|
|
45
|
+
expect(() => {
|
|
46
|
+
render(result as unknown as AST);
|
|
47
|
+
}).toThrow('render() received a failed ParseResult');
|
|
48
|
+
|
|
49
|
+
expect(() => {
|
|
50
|
+
render(result as unknown as AST);
|
|
51
|
+
}).toThrow('Check parse errors before calling render');
|
|
52
|
+
});
|
|
53
|
+
|
|
54
|
+
test('throws descriptive error when passed null or undefined', () => {
|
|
55
|
+
expect(() => {
|
|
56
|
+
render(null as unknown as AST);
|
|
57
|
+
}).toThrow('render() expects an AST object with a scenes array');
|
|
58
|
+
|
|
59
|
+
expect(() => {
|
|
60
|
+
render(undefined as unknown as AST);
|
|
61
|
+
}).toThrow('render() expects an AST object with a scenes array');
|
|
62
|
+
});
|
|
63
|
+
|
|
64
|
+
test('throws descriptive error when passed object without scenes array', () => {
|
|
65
|
+
const invalidAST = { foo: 'bar' };
|
|
66
|
+
|
|
67
|
+
expect(() => {
|
|
68
|
+
render(invalidAST as unknown as AST);
|
|
69
|
+
}).toThrow('render() expects an AST object with a scenes array');
|
|
70
|
+
|
|
71
|
+
expect(() => {
|
|
72
|
+
render(invalidAST as unknown as AST);
|
|
73
|
+
}).toThrow('Did you pass ParseResult instead of ParseResult.ast?');
|
|
74
|
+
});
|
|
75
|
+
|
|
76
|
+
// Note: DOM-dependent tests are skipped in Node.js environment
|
|
77
|
+
// The renderer requires a browser or jsdom environment
|
|
78
|
+
test.skip('works correctly when passed valid AST (requires DOM)', () => {
|
|
79
|
+
const result = parse(validWireframe);
|
|
80
|
+
|
|
81
|
+
expect(result.success).toBe(true);
|
|
82
|
+
if (result.success) {
|
|
83
|
+
// This is the correct usage
|
|
84
|
+
const { root, sceneManager } = render(result.ast);
|
|
85
|
+
|
|
86
|
+
expect(root).toBeDefined();
|
|
87
|
+
expect(root).toBeInstanceOf(HTMLElement);
|
|
88
|
+
expect(sceneManager).toBeDefined();
|
|
89
|
+
expect(sceneManager.getSceneIds()).toContain('test');
|
|
90
|
+
}
|
|
91
|
+
});
|
|
92
|
+
});
|
|
93
|
+
|
|
94
|
+
describe('createUI() convenience function', () => {
|
|
95
|
+
test('handles parse errors gracefully', () => {
|
|
96
|
+
const invalidWireframe = '+--incomplete';
|
|
97
|
+
const result = createUI(invalidWireframe);
|
|
98
|
+
|
|
99
|
+
expect(result.success).toBe(false);
|
|
100
|
+
if (!result.success) {
|
|
101
|
+
expect(result.errors).toBeDefined();
|
|
102
|
+
expect(result.errors.length).toBeGreaterThan(0);
|
|
103
|
+
}
|
|
104
|
+
});
|
|
105
|
+
|
|
106
|
+
// Note: DOM-dependent tests are skipped in Node.js environment
|
|
107
|
+
test.skip('works correctly with valid wireframe (requires DOM)', () => {
|
|
108
|
+
const validWireframe = `
|
|
109
|
+
@scene: test
|
|
110
|
+
|
|
111
|
+
+-------+
|
|
112
|
+
| Hello |
|
|
113
|
+
+-------+
|
|
114
|
+
`;
|
|
115
|
+
const result = createUI(validWireframe);
|
|
116
|
+
|
|
117
|
+
expect(result.success).toBe(true);
|
|
118
|
+
if (result.success) {
|
|
119
|
+
expect(result.root).toBeDefined();
|
|
120
|
+
expect(result.sceneManager).toBeDefined();
|
|
121
|
+
expect(result.ast).toBeDefined();
|
|
122
|
+
}
|
|
123
|
+
});
|
|
124
|
+
});
|
|
125
|
+
|
|
126
|
+
describe('onSceneChange callback (Issue #2)', () => {
|
|
127
|
+
const multiSceneWireframe = `
|
|
128
|
+
@scene: login
|
|
129
|
+
|
|
130
|
+
+---------------+
|
|
131
|
+
| Login Screen |
|
|
132
|
+
+---------------+
|
|
133
|
+
|
|
134
|
+
---
|
|
135
|
+
|
|
136
|
+
@scene: dashboard
|
|
137
|
+
|
|
138
|
+
+---------------+
|
|
139
|
+
| Dashboard |
|
|
140
|
+
+---------------+
|
|
141
|
+
`;
|
|
142
|
+
|
|
143
|
+
test('OnSceneChangeCallback type is exported', () => {
|
|
144
|
+
// Verify the type is exported by using it
|
|
145
|
+
const callback: OnSceneChangeCallback = (_from, _to) => {};
|
|
146
|
+
expect(typeof callback).toBe('function');
|
|
147
|
+
});
|
|
148
|
+
|
|
149
|
+
// Note: DOM-dependent tests are skipped in Node.js environment
|
|
150
|
+
test.skip('render accepts onSceneChange option without throwing (requires DOM)', () => {
|
|
151
|
+
const result = parse(multiSceneWireframe);
|
|
152
|
+
expect(result.success).toBe(true);
|
|
153
|
+
|
|
154
|
+
if (result.success) {
|
|
155
|
+
const callback = vi.fn();
|
|
156
|
+
|
|
157
|
+
// This should not throw - just verify the API accepts the option
|
|
158
|
+
expect(() => {
|
|
159
|
+
render(result.ast, {
|
|
160
|
+
onSceneChange: callback,
|
|
161
|
+
});
|
|
162
|
+
}).not.toThrow();
|
|
163
|
+
}
|
|
164
|
+
});
|
|
165
|
+
|
|
166
|
+
test.skip('createUI accepts onSceneChange option without throwing (requires DOM)', () => {
|
|
167
|
+
const callback = vi.fn();
|
|
168
|
+
|
|
169
|
+
// Verify the API accepts the option
|
|
170
|
+
expect(() => {
|
|
171
|
+
createUI(multiSceneWireframe, {
|
|
172
|
+
onSceneChange: callback,
|
|
173
|
+
});
|
|
174
|
+
}).not.toThrow();
|
|
175
|
+
});
|
|
176
|
+
|
|
177
|
+
// Note: DOM-dependent tests are skipped in Node.js environment
|
|
178
|
+
test.skip('onSceneChange is called on initial scene load (requires DOM)', () => {
|
|
179
|
+
const result = parse(multiSceneWireframe);
|
|
180
|
+
expect(result.success).toBe(true);
|
|
181
|
+
|
|
182
|
+
if (result.success) {
|
|
183
|
+
const callback = vi.fn();
|
|
184
|
+
render(result.ast, { onSceneChange: callback });
|
|
185
|
+
|
|
186
|
+
// Should be called once for initial scene
|
|
187
|
+
expect(callback).toHaveBeenCalledTimes(1);
|
|
188
|
+
expect(callback).toHaveBeenCalledWith(undefined, 'login');
|
|
189
|
+
}
|
|
190
|
+
});
|
|
191
|
+
|
|
192
|
+
test.skip('onSceneChange is called when navigating between scenes (requires DOM)', () => {
|
|
193
|
+
const result = parse(multiSceneWireframe);
|
|
194
|
+
expect(result.success).toBe(true);
|
|
195
|
+
|
|
196
|
+
if (result.success) {
|
|
197
|
+
const callback = vi.fn();
|
|
198
|
+
const { sceneManager } = render(result.ast, { onSceneChange: callback });
|
|
199
|
+
|
|
200
|
+
// Clear mock after initial call
|
|
201
|
+
callback.mockClear();
|
|
202
|
+
|
|
203
|
+
// Navigate to dashboard
|
|
204
|
+
sceneManager.goto('dashboard');
|
|
205
|
+
|
|
206
|
+
expect(callback).toHaveBeenCalledTimes(1);
|
|
207
|
+
expect(callback).toHaveBeenCalledWith('login', 'dashboard');
|
|
208
|
+
}
|
|
209
|
+
});
|
|
210
|
+
|
|
211
|
+
test.skip('onSceneChange is not called when navigating to the same scene (requires DOM)', () => {
|
|
212
|
+
const result = parse(multiSceneWireframe);
|
|
213
|
+
expect(result.success).toBe(true);
|
|
214
|
+
|
|
215
|
+
if (result.success) {
|
|
216
|
+
const callback = vi.fn();
|
|
217
|
+
const { sceneManager } = render(result.ast, { onSceneChange: callback });
|
|
218
|
+
|
|
219
|
+
// Clear mock after initial call
|
|
220
|
+
callback.mockClear();
|
|
221
|
+
|
|
222
|
+
// Navigate to the same scene
|
|
223
|
+
sceneManager.goto('login');
|
|
224
|
+
|
|
225
|
+
expect(callback).not.toHaveBeenCalled();
|
|
226
|
+
}
|
|
227
|
+
});
|
|
228
|
+
|
|
229
|
+
test.skip('onSceneChange receives correct fromScene and toScene values (requires DOM)', () => {
|
|
230
|
+
const result = parse(multiSceneWireframe);
|
|
231
|
+
expect(result.success).toBe(true);
|
|
232
|
+
|
|
233
|
+
if (result.success) {
|
|
234
|
+
const navigationHistory: Array<{ from: string | undefined; to: string }> = [];
|
|
235
|
+
const callback: OnSceneChangeCallback = (from, to) => {
|
|
236
|
+
navigationHistory.push({ from, to });
|
|
237
|
+
};
|
|
238
|
+
|
|
239
|
+
const { sceneManager } = render(result.ast, { onSceneChange: callback });
|
|
240
|
+
|
|
241
|
+
// Navigate through scenes
|
|
242
|
+
sceneManager.goto('dashboard');
|
|
243
|
+
sceneManager.goto('login');
|
|
244
|
+
|
|
245
|
+
expect(navigationHistory).toEqual([
|
|
246
|
+
{ from: undefined, to: 'login' }, // Initial
|
|
247
|
+
{ from: 'login', to: 'dashboard' },
|
|
248
|
+
{ from: 'dashboard', to: 'login' },
|
|
249
|
+
]);
|
|
250
|
+
}
|
|
251
|
+
});
|
|
252
|
+
});
|
package/src/index.ts
CHANGED
|
@@ -146,6 +146,9 @@ export interface SceneManager {
|
|
|
146
146
|
getSceneIds: () => string[];
|
|
147
147
|
}
|
|
148
148
|
|
|
149
|
+
/** Scene change callback type */
|
|
150
|
+
export type OnSceneChangeCallback = (fromScene: string | undefined, toScene: string) => void;
|
|
151
|
+
|
|
149
152
|
/** Render options */
|
|
150
153
|
export interface RenderOptions {
|
|
151
154
|
/** Theme name */
|
|
@@ -156,6 +159,13 @@ export interface RenderOptions {
|
|
|
156
159
|
injectStyles?: boolean;
|
|
157
160
|
/** Additional CSS class for container */
|
|
158
161
|
containerClass?: string;
|
|
162
|
+
/**
|
|
163
|
+
* Callback fired when navigating between scenes.
|
|
164
|
+
* Useful for implementing scene history, analytics, or state synchronization.
|
|
165
|
+
* @param fromScene - The scene ID navigating from (undefined if initial navigation)
|
|
166
|
+
* @param toScene - The scene ID navigating to
|
|
167
|
+
*/
|
|
168
|
+
onSceneChange?: OnSceneChangeCallback;
|
|
159
169
|
}
|
|
160
170
|
|
|
161
171
|
/** Render result */
|
|
@@ -178,15 +178,12 @@ function adjustCodeLineOffset(code, offset) {
|
|
|
178
178
|
}
|
|
179
179
|
|
|
180
180
|
function adjustLineOffset(error, offset) {
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
context: error.context
|
|
188
|
-
};
|
|
189
|
-
}
|
|
181
|
+
let totalOffset = offset + 1 | 0;
|
|
182
|
+
return {
|
|
183
|
+
code: adjustCodeLineOffset(error.code, totalOffset),
|
|
184
|
+
severity: error.severity,
|
|
185
|
+
context: error.context
|
|
186
|
+
};
|
|
190
187
|
}
|
|
191
188
|
|
|
192
189
|
export {
|
|
@@ -217,18 +217,21 @@ let adjustCodeLineOffset = (code: errorCode, offset: int): errorCode => {
|
|
|
217
217
|
* Adjust the line offset in an error's position.
|
|
218
218
|
* Creates a new error with the position adjusted by the given offset.
|
|
219
219
|
*
|
|
220
|
+
* This function always adds +1 to convert from 0-indexed grid rows
|
|
221
|
+
* to 1-indexed file line numbers, plus any offset for stripped directive lines.
|
|
222
|
+
*
|
|
220
223
|
* @param error - The error to adjust
|
|
221
|
-
* @param offset - The number of lines
|
|
222
|
-
* @returns A new error with
|
|
224
|
+
* @param offset - The number of directive lines stripped before grid processing
|
|
225
|
+
* @returns A new error with 1-indexed line position
|
|
223
226
|
*/
|
|
224
227
|
let adjustLineOffset = (error: t, offset: int): t => {
|
|
225
|
-
|
|
226
|
-
|
|
227
|
-
|
|
228
|
-
|
|
229
|
-
|
|
230
|
-
|
|
231
|
-
|
|
232
|
-
|
|
228
|
+
// Always add 1 to convert from 0-indexed to 1-indexed line numbers
|
|
229
|
+
// offset accounts for stripped directive lines (e.g., @scene: login)
|
|
230
|
+
// +1 converts from 0-indexed grid rows to 1-indexed file lines
|
|
231
|
+
let totalOffset = offset + 1
|
|
232
|
+
{
|
|
233
|
+
code: adjustCodeLineOffset(error.code, totalOffset),
|
|
234
|
+
severity: error.severity,
|
|
235
|
+
context: error.context,
|
|
233
236
|
}
|
|
234
237
|
}
|
|
@@ -11,12 +11,19 @@ import type {t as ErrorTypes_t} from '../../src/parser/Errors/ErrorTypes.gen';
|
|
|
11
11
|
|
|
12
12
|
export abstract class DomBindings_element { protected opaque!: any }; /* simulate opaque types */
|
|
13
13
|
|
|
14
|
+
/** * Scene change callback type.
|
|
15
|
+
* Called when navigating between scenes.
|
|
16
|
+
* @param fromScene The scene ID navigating from (None if initial)
|
|
17
|
+
* @param toScene The scene ID navigating to */
|
|
18
|
+
export type onSceneChangeCallback = (_1:(undefined | string), _2:string) => void;
|
|
19
|
+
|
|
14
20
|
/** * Configuration for the rendering process. */
|
|
15
21
|
export type renderOptions = {
|
|
16
22
|
readonly theme: (undefined | string);
|
|
17
23
|
readonly interactive: boolean;
|
|
18
24
|
readonly injectStyles: boolean;
|
|
19
|
-
readonly containerClass: (undefined | string)
|
|
25
|
+
readonly containerClass: (undefined | string);
|
|
26
|
+
readonly onSceneChange: (undefined | onSceneChangeCallback)
|
|
20
27
|
};
|
|
21
28
|
|
|
22
29
|
/** * Scene management interface returned by render function. */
|
|
@@ -12,7 +12,8 @@ let defaultOptions = {
|
|
|
12
12
|
theme: undefined,
|
|
13
13
|
interactive: true,
|
|
14
14
|
injectStyles: true,
|
|
15
|
-
containerClass: undefined
|
|
15
|
+
containerClass: undefined,
|
|
16
|
+
onSceneChange: undefined
|
|
16
17
|
};
|
|
17
18
|
|
|
18
19
|
let defaultStyles = `
|
|
@@ -280,7 +281,7 @@ function renderScene(scene, onAction) {
|
|
|
280
281
|
return sceneEl;
|
|
281
282
|
}
|
|
282
283
|
|
|
283
|
-
function createSceneManager(scenes) {
|
|
284
|
+
function createSceneManager(scenes, onSceneChange) {
|
|
284
285
|
let currentScene = {
|
|
285
286
|
contents: undefined
|
|
286
287
|
};
|
|
@@ -290,10 +291,11 @@ function createSceneManager(scenes) {
|
|
|
290
291
|
let forwardStack = {
|
|
291
292
|
contents: []
|
|
292
293
|
};
|
|
293
|
-
let switchToScene = id => {
|
|
294
|
-
let
|
|
295
|
-
|
|
296
|
-
|
|
294
|
+
let switchToScene = (id, notifyOpt) => {
|
|
295
|
+
let notify = notifyOpt !== undefined ? notifyOpt : true;
|
|
296
|
+
let previousScene = currentScene.contents;
|
|
297
|
+
if (previousScene !== undefined) {
|
|
298
|
+
let el = scenes.get(previousScene);
|
|
297
299
|
if (el !== undefined) {
|
|
298
300
|
Primitive_option.valFromOption(el).classList.remove("active");
|
|
299
301
|
}
|
|
@@ -302,7 +304,15 @@ function createSceneManager(scenes) {
|
|
|
302
304
|
if (el$1 !== undefined) {
|
|
303
305
|
Primitive_option.valFromOption(el$1).classList.add("active");
|
|
304
306
|
currentScene.contents = id;
|
|
305
|
-
|
|
307
|
+
if (notify && (previousScene === undefined || previousScene !== id)) {
|
|
308
|
+
if (onSceneChange !== undefined) {
|
|
309
|
+
return onSceneChange(previousScene, id);
|
|
310
|
+
} else {
|
|
311
|
+
return;
|
|
312
|
+
}
|
|
313
|
+
} else {
|
|
314
|
+
return;
|
|
315
|
+
}
|
|
306
316
|
}
|
|
307
317
|
};
|
|
308
318
|
let goto = id => {
|
|
@@ -311,7 +321,7 @@ function createSceneManager(scenes) {
|
|
|
311
321
|
historyStack.contents = historyStack.contents.concat([currentId]);
|
|
312
322
|
forwardStack.contents = [];
|
|
313
323
|
}
|
|
314
|
-
switchToScene(id);
|
|
324
|
+
switchToScene(id, undefined);
|
|
315
325
|
};
|
|
316
326
|
let back = () => {
|
|
317
327
|
let history = historyStack.contents;
|
|
@@ -328,7 +338,7 @@ function createSceneManager(scenes) {
|
|
|
328
338
|
forwardStack.contents = forwardStack.contents.concat([currentId]);
|
|
329
339
|
}
|
|
330
340
|
historyStack.contents = history.slice(0, len - 1 | 0);
|
|
331
|
-
switchToScene(prevId);
|
|
341
|
+
switchToScene(prevId, undefined);
|
|
332
342
|
};
|
|
333
343
|
let forward = () => {
|
|
334
344
|
let fwdStack = forwardStack.contents;
|
|
@@ -345,7 +355,7 @@ function createSceneManager(scenes) {
|
|
|
345
355
|
historyStack.contents = historyStack.contents.concat([currentId]);
|
|
346
356
|
}
|
|
347
357
|
forwardStack.contents = fwdStack.slice(0, len - 1 | 0);
|
|
348
|
-
switchToScene(nextId);
|
|
358
|
+
switchToScene(nextId, undefined);
|
|
349
359
|
};
|
|
350
360
|
let refresh = () => {
|
|
351
361
|
let id = currentScene.contents;
|
|
@@ -451,7 +461,7 @@ function render(ast, options) {
|
|
|
451
461
|
app.appendChild(sceneEl);
|
|
452
462
|
sceneMap.set(scene.id, sceneEl);
|
|
453
463
|
});
|
|
454
|
-
let manager = createSceneManager(sceneMap);
|
|
464
|
+
let manager = createSceneManager(sceneMap, opts.onSceneChange);
|
|
455
465
|
gotoRef.contents = manager.goto;
|
|
456
466
|
backRef.contents = manager.back;
|
|
457
467
|
forwardRef.contents = manager.forward;
|
|
@@ -48,6 +48,14 @@ module DomBindings = {
|
|
|
48
48
|
// Render Options
|
|
49
49
|
// ============================================================================
|
|
50
50
|
|
|
51
|
+
/**
|
|
52
|
+
* Scene change callback type.
|
|
53
|
+
* Called when navigating between scenes.
|
|
54
|
+
* @param fromScene The scene ID navigating from (None if initial)
|
|
55
|
+
* @param toScene The scene ID navigating to
|
|
56
|
+
*/
|
|
57
|
+
type onSceneChangeCallback = (option<string>, string) => unit
|
|
58
|
+
|
|
51
59
|
/**
|
|
52
60
|
* Configuration for the rendering process.
|
|
53
61
|
*/
|
|
@@ -56,6 +64,7 @@ type renderOptions = {
|
|
|
56
64
|
interactive: bool,
|
|
57
65
|
injectStyles: bool,
|
|
58
66
|
containerClass: option<string>,
|
|
67
|
+
onSceneChange: option<onSceneChangeCallback>,
|
|
59
68
|
}
|
|
60
69
|
|
|
61
70
|
/**
|
|
@@ -66,6 +75,7 @@ let defaultOptions: renderOptions = {
|
|
|
66
75
|
interactive: true,
|
|
67
76
|
injectStyles: true,
|
|
68
77
|
containerClass: None,
|
|
78
|
+
onSceneChange: None,
|
|
69
79
|
}
|
|
70
80
|
|
|
71
81
|
// ============================================================================
|
|
@@ -418,14 +428,27 @@ let renderScene = (scene: scene, ~onAction: option<actionHandler>=?): DomBinding
|
|
|
418
428
|
// Scene Manager Implementation
|
|
419
429
|
// ============================================================================
|
|
420
430
|
|
|
421
|
-
let createSceneManager = (
|
|
431
|
+
let createSceneManager = (
|
|
432
|
+
scenes: Map.t<string, DomBindings.element>,
|
|
433
|
+
~onSceneChange: option<onSceneChangeCallback>=?,
|
|
434
|
+
): sceneManager => {
|
|
422
435
|
let currentScene = ref(None)
|
|
423
436
|
let historyStack: ref<array<string>> = ref([])
|
|
424
437
|
let forwardStack: ref<array<string>> = ref([])
|
|
425
438
|
|
|
439
|
+
// Helper to call onSceneChange callback if provided
|
|
440
|
+
let notifySceneChange = (fromScene: option<string>, toScene: string): unit => {
|
|
441
|
+
switch onSceneChange {
|
|
442
|
+
| Some(callback) => callback(fromScene, toScene)
|
|
443
|
+
| None => ()
|
|
444
|
+
}
|
|
445
|
+
}
|
|
446
|
+
|
|
426
447
|
// Internal function to switch scenes without affecting history
|
|
427
|
-
let switchToScene = (id: string): unit => {
|
|
428
|
-
|
|
448
|
+
let switchToScene = (id: string, ~notify: bool=true): unit => {
|
|
449
|
+
let previousScene = currentScene.contents
|
|
450
|
+
|
|
451
|
+
switch previousScene {
|
|
429
452
|
| Some(currentId) => {
|
|
430
453
|
switch scenes->Map.get(currentId) {
|
|
431
454
|
| Some(el) => el->DomBindings.classList->DomBindings.remove("active")
|
|
@@ -439,6 +462,13 @@ let createSceneManager = (scenes: Map.t<string, DomBindings.element>): sceneMana
|
|
|
439
462
|
| Some(el) => {
|
|
440
463
|
el->DomBindings.classList->DomBindings.add("active")
|
|
441
464
|
currentScene := Some(id)
|
|
465
|
+
// Notify callback if enabled and scene actually changed
|
|
466
|
+
if notify {
|
|
467
|
+
switch previousScene {
|
|
468
|
+
| Some(prevId) if prevId == id => () // Same scene, no notification
|
|
469
|
+
| _ => notifySceneChange(previousScene, id)
|
|
470
|
+
}
|
|
471
|
+
}
|
|
442
472
|
}
|
|
443
473
|
| None => ()
|
|
444
474
|
}
|
|
@@ -634,7 +664,7 @@ let render = (ast: ast, options: option<renderOptions>): renderResult => {
|
|
|
634
664
|
sceneMap->Map.set(scene.id, sceneEl)
|
|
635
665
|
})
|
|
636
666
|
|
|
637
|
-
let manager = createSceneManager(sceneMap)
|
|
667
|
+
let manager = createSceneManager(sceneMap, ~onSceneChange=?opts.onSceneChange)
|
|
638
668
|
|
|
639
669
|
// Now that sceneManager is created, set the refs
|
|
640
670
|
gotoRef := Some(manager.goto)
|