@squiz/dxp-cli-next 5.31.0-develop.4 → 5.31.0-develop.6
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/lib/page/layouts/deploy/deploy.js +44 -9
- package/lib/page/layouts/deploy/deploy.spec.js +110 -19
- package/lib/page/layouts/dev/dev.js +18 -4
- package/lib/page/layouts/dev/dev.spec.js +117 -8
- package/lib/page/layouts/validation/index.d.ts +2 -0
- package/lib/page/layouts/validation/index.js +5 -1
- package/lib/page/layouts/validation/property-consistency.d.ts +7 -0
- package/lib/page/layouts/validation/property-consistency.js +92 -0
- package/lib/page/layouts/validation/property-consistency.spec.d.ts +1 -0
- package/lib/page/layouts/validation/property-consistency.spec.js +305 -0
- package/lib/page/layouts/validation/validateLayoutFormat.d.ts +2 -0
- package/lib/page/layouts/validation/validateLayoutFormat.js +27 -0
- package/lib/page/layouts/validation/validateLayoutFormat.spec.d.ts +1 -0
- package/lib/page/layouts/validation/validateLayoutFormat.spec.js +42 -0
- package/lib/page/layouts/validation/zone-consistency.d.ts +1 -1
- package/lib/page/layouts/validation/zone-consistency.js +10 -9
- package/lib/page/layouts/validation/zone-consistency.spec.js +32 -34
- package/lib/page/utils/definitions.d.ts +347 -50
- package/lib/page/utils/definitions.js +103 -22
- package/lib/page/utils/definitions.spec.js +516 -267
- package/lib/page/utils/normalize.d.ts +8 -0
- package/lib/page/utils/normalize.js +61 -0
- package/lib/page/utils/normalize.spec.d.ts +1 -0
- package/lib/page/utils/normalize.spec.js +315 -0
- package/lib/page/utils/parse-args.d.ts +20 -4
- package/lib/page/utils/parse-args.js +48 -13
- package/lib/page/utils/parse-args.spec.js +159 -21
- package/lib/page/utils/render.d.ts +27 -9
- package/lib/page/utils/render.js +66 -12
- package/lib/page/utils/render.spec.js +14 -14
- package/lib/page/utils/server.d.ts +1 -1
- package/lib/page/utils/server.js +2 -2
- package/lib/page/utils/server.spec.js +13 -13
- package/package.json +1 -1
|
@@ -73,50 +73,50 @@ describe('parseZonesList', () => {
|
|
|
73
73
|
});
|
|
74
74
|
});
|
|
75
75
|
});
|
|
76
|
-
describe('
|
|
76
|
+
describe('parsePropertiesList', () => {
|
|
77
77
|
it('returns empty object when given empty string', () => {
|
|
78
|
-
const result = (0, parse_args_1.
|
|
78
|
+
const result = (0, parse_args_1.parsePropertiesList)('', {});
|
|
79
79
|
expect(result).toEqual({});
|
|
80
80
|
});
|
|
81
|
-
it('parses single
|
|
82
|
-
const result = (0, parse_args_1.
|
|
81
|
+
it('parses single property with equals separator', () => {
|
|
82
|
+
const result = (0, parse_args_1.parsePropertiesList)('theme=dark', {});
|
|
83
83
|
expect(result).toEqual({
|
|
84
84
|
theme: 'dark',
|
|
85
85
|
});
|
|
86
86
|
});
|
|
87
|
-
it('parses single
|
|
88
|
-
const result = (0, parse_args_1.
|
|
87
|
+
it('parses single property with colon separator', () => {
|
|
88
|
+
const result = (0, parse_args_1.parsePropertiesList)('theme:dark', {});
|
|
89
89
|
expect(result).toEqual({
|
|
90
90
|
theme: 'dark',
|
|
91
91
|
});
|
|
92
92
|
});
|
|
93
|
-
it('parses comma-separated
|
|
94
|
-
const result = (0, parse_args_1.
|
|
93
|
+
it('parses comma-separated properties with equals separator', () => {
|
|
94
|
+
const result = (0, parse_args_1.parsePropertiesList)('theme=dark,containerSize=standard,sidebarPosition=right', {});
|
|
95
95
|
expect(result).toEqual({
|
|
96
96
|
theme: 'dark',
|
|
97
97
|
containerSize: 'standard',
|
|
98
98
|
sidebarPosition: 'right',
|
|
99
99
|
});
|
|
100
100
|
});
|
|
101
|
-
it('parses comma-separated
|
|
102
|
-
const result = (0, parse_args_1.
|
|
101
|
+
it('parses comma-separated properties with colon separator', () => {
|
|
102
|
+
const result = (0, parse_args_1.parsePropertiesList)('theme:dark,containerSize:standard,sidebarPosition:right', {});
|
|
103
103
|
expect(result).toEqual({
|
|
104
104
|
theme: 'dark',
|
|
105
105
|
containerSize: 'standard',
|
|
106
106
|
sidebarPosition: 'right',
|
|
107
107
|
});
|
|
108
108
|
});
|
|
109
|
-
it('later
|
|
110
|
-
let result = (0, parse_args_1.
|
|
111
|
-
result = (0, parse_args_1.
|
|
109
|
+
it('later properties override earlier ones', () => {
|
|
110
|
+
let result = (0, parse_args_1.parsePropertiesList)('theme=light', {});
|
|
111
|
+
result = (0, parse_args_1.parsePropertiesList)('theme=dark', result);
|
|
112
112
|
expect(result).toEqual({
|
|
113
113
|
theme: 'dark',
|
|
114
114
|
});
|
|
115
115
|
});
|
|
116
|
-
it('accumulates values from multiple
|
|
117
|
-
let result = (0, parse_args_1.
|
|
118
|
-
result = (0, parse_args_1.
|
|
119
|
-
result = (0, parse_args_1.
|
|
116
|
+
it('accumulates values from multiple property flags', () => {
|
|
117
|
+
let result = (0, parse_args_1.parsePropertiesList)('theme=dark', {});
|
|
118
|
+
result = (0, parse_args_1.parsePropertiesList)('containerSize=standard', result);
|
|
119
|
+
result = (0, parse_args_1.parsePropertiesList)('sidebarPosition=right', result);
|
|
120
120
|
expect(result).toEqual({
|
|
121
121
|
theme: 'dark',
|
|
122
122
|
containerSize: 'standard',
|
|
@@ -124,19 +124,19 @@ describe('parseOptionsList', () => {
|
|
|
124
124
|
});
|
|
125
125
|
});
|
|
126
126
|
it('handles mixed syntax with equals and colons', () => {
|
|
127
|
-
const result = (0, parse_args_1.
|
|
127
|
+
const result = (0, parse_args_1.parsePropertiesList)('theme=dark,containerSize:standard', {});
|
|
128
128
|
expect(result).toEqual({
|
|
129
129
|
theme: 'dark',
|
|
130
130
|
containerSize: 'standard',
|
|
131
131
|
});
|
|
132
132
|
});
|
|
133
133
|
it('ignores malformed inputs', () => {
|
|
134
|
-
const result = (0, parse_args_1.
|
|
134
|
+
const result = (0, parse_args_1.parsePropertiesList)('malformed,theme=,=dark', {});
|
|
135
135
|
expect(result).toEqual({});
|
|
136
136
|
});
|
|
137
137
|
it('handles complex example with multiple formats', () => {
|
|
138
|
-
let result = (0, parse_args_1.
|
|
139
|
-
result = (0, parse_args_1.
|
|
138
|
+
let result = (0, parse_args_1.parsePropertiesList)('containerSize:standard', {});
|
|
139
|
+
result = (0, parse_args_1.parsePropertiesList)('sidebarPosition=right,colorTheme=dark', result);
|
|
140
140
|
expect(result).toEqual({
|
|
141
141
|
containerSize: 'standard',
|
|
142
142
|
sidebarPosition: 'right',
|
|
@@ -144,3 +144,141 @@ describe('parseOptionsList', () => {
|
|
|
144
144
|
});
|
|
145
145
|
});
|
|
146
146
|
});
|
|
147
|
+
describe('normalizeLayoutProperties', () => {
|
|
148
|
+
it('returns empty object when given no properties', () => {
|
|
149
|
+
const result = (0, parse_args_1.normalizeLayoutProperties)({}, {});
|
|
150
|
+
expect(result).toEqual({});
|
|
151
|
+
});
|
|
152
|
+
it('returns properties unchanged when layoutDefinition has no properties', () => {
|
|
153
|
+
const properties = {
|
|
154
|
+
theme: 'dark',
|
|
155
|
+
size: 'large',
|
|
156
|
+
};
|
|
157
|
+
const result = (0, parse_args_1.normalizeLayoutProperties)(properties, {});
|
|
158
|
+
expect(result).toEqual(properties);
|
|
159
|
+
});
|
|
160
|
+
it('converts string "true" to boolean true for boolean-type properties', () => {
|
|
161
|
+
const properties = {
|
|
162
|
+
showFooter: 'true',
|
|
163
|
+
};
|
|
164
|
+
const layoutDefinition = {
|
|
165
|
+
properties: {
|
|
166
|
+
showFooter: {
|
|
167
|
+
type: 'boolean',
|
|
168
|
+
},
|
|
169
|
+
},
|
|
170
|
+
};
|
|
171
|
+
const result = (0, parse_args_1.normalizeLayoutProperties)(properties, layoutDefinition);
|
|
172
|
+
expect(result).toEqual({
|
|
173
|
+
showFooter: true,
|
|
174
|
+
});
|
|
175
|
+
expect(typeof result.showFooter).toBe('boolean');
|
|
176
|
+
});
|
|
177
|
+
it('converts string "false" to boolean false for boolean-type properties', () => {
|
|
178
|
+
const properties = {
|
|
179
|
+
showFooter: 'false',
|
|
180
|
+
};
|
|
181
|
+
const layoutDefinition = {
|
|
182
|
+
properties: {
|
|
183
|
+
showFooter: {
|
|
184
|
+
type: 'boolean',
|
|
185
|
+
},
|
|
186
|
+
},
|
|
187
|
+
};
|
|
188
|
+
const result = (0, parse_args_1.normalizeLayoutProperties)(properties, layoutDefinition);
|
|
189
|
+
expect(result).toEqual({
|
|
190
|
+
showFooter: false,
|
|
191
|
+
});
|
|
192
|
+
expect(typeof result.showFooter).toBe('boolean');
|
|
193
|
+
});
|
|
194
|
+
it('leaves string properties unchanged for non-boolean types', () => {
|
|
195
|
+
const properties = {
|
|
196
|
+
theme: 'dark',
|
|
197
|
+
customTitle: 'My Title',
|
|
198
|
+
};
|
|
199
|
+
const layoutDefinition = {
|
|
200
|
+
properties: {
|
|
201
|
+
theme: {
|
|
202
|
+
type: 'string',
|
|
203
|
+
},
|
|
204
|
+
customTitle: {
|
|
205
|
+
type: 'string',
|
|
206
|
+
},
|
|
207
|
+
},
|
|
208
|
+
};
|
|
209
|
+
const result = (0, parse_args_1.normalizeLayoutProperties)(properties, layoutDefinition);
|
|
210
|
+
expect(result).toEqual(properties);
|
|
211
|
+
});
|
|
212
|
+
it('handles mixed boolean and string properties', () => {
|
|
213
|
+
const properties = {
|
|
214
|
+
showFooter: 'true',
|
|
215
|
+
showHeader: 'false',
|
|
216
|
+
theme: 'dark',
|
|
217
|
+
customTitle: 'Test',
|
|
218
|
+
};
|
|
219
|
+
const layoutDefinition = {
|
|
220
|
+
properties: {
|
|
221
|
+
showFooter: { type: 'boolean' },
|
|
222
|
+
showHeader: { type: 'boolean' },
|
|
223
|
+
theme: { type: 'string' },
|
|
224
|
+
customTitle: { type: 'string' },
|
|
225
|
+
},
|
|
226
|
+
};
|
|
227
|
+
const result = (0, parse_args_1.normalizeLayoutProperties)(properties, layoutDefinition);
|
|
228
|
+
expect(result).toEqual({
|
|
229
|
+
showFooter: true,
|
|
230
|
+
showHeader: false,
|
|
231
|
+
theme: 'dark',
|
|
232
|
+
customTitle: 'Test',
|
|
233
|
+
});
|
|
234
|
+
});
|
|
235
|
+
it('throws error for invalid boolean value', () => {
|
|
236
|
+
const properties = {
|
|
237
|
+
showFooter: 'yes',
|
|
238
|
+
};
|
|
239
|
+
const layoutDefinition = {
|
|
240
|
+
properties: {
|
|
241
|
+
showFooter: {
|
|
242
|
+
type: 'boolean',
|
|
243
|
+
},
|
|
244
|
+
},
|
|
245
|
+
};
|
|
246
|
+
expect(() => (0, parse_args_1.normalizeLayoutProperties)(properties, layoutDefinition)).toThrow('Invalid boolean value "yes" for property "showFooter". Must be "true" or "false".');
|
|
247
|
+
});
|
|
248
|
+
it('only normalizes properties that exist in layoutDefinition', () => {
|
|
249
|
+
const properties = {
|
|
250
|
+
showFooter: 'true',
|
|
251
|
+
unknownProperty: 'true',
|
|
252
|
+
};
|
|
253
|
+
const layoutDefinition = {
|
|
254
|
+
properties: {
|
|
255
|
+
showFooter: {
|
|
256
|
+
type: 'boolean',
|
|
257
|
+
},
|
|
258
|
+
},
|
|
259
|
+
};
|
|
260
|
+
const result = (0, parse_args_1.normalizeLayoutProperties)(properties, layoutDefinition);
|
|
261
|
+
expect(result).toEqual({
|
|
262
|
+
showFooter: true,
|
|
263
|
+
unknownProperty: 'true', // Left as string because not in layoutDefinition
|
|
264
|
+
});
|
|
265
|
+
});
|
|
266
|
+
it('preserves properties not present in layoutDefinition properties', () => {
|
|
267
|
+
const properties = {
|
|
268
|
+
showFooter: 'true',
|
|
269
|
+
theme: 'dark',
|
|
270
|
+
};
|
|
271
|
+
const layoutDefinition = {
|
|
272
|
+
properties: {
|
|
273
|
+
showFooter: {
|
|
274
|
+
type: 'boolean',
|
|
275
|
+
},
|
|
276
|
+
},
|
|
277
|
+
};
|
|
278
|
+
const result = (0, parse_args_1.normalizeLayoutProperties)(properties, layoutDefinition);
|
|
279
|
+
expect(result).toEqual({
|
|
280
|
+
showFooter: true,
|
|
281
|
+
theme: 'dark', // Preserved even though not in layoutDefinition
|
|
282
|
+
});
|
|
283
|
+
});
|
|
284
|
+
});
|
|
@@ -4,30 +4,48 @@ export interface LayoutDefinition {
|
|
|
4
4
|
description?: string;
|
|
5
5
|
entry?: string;
|
|
6
6
|
template?: string;
|
|
7
|
-
zones:
|
|
8
|
-
|
|
7
|
+
zones: ZoneDefinition[];
|
|
8
|
+
properties?: Record<string, PropertyDefinition>;
|
|
9
9
|
}
|
|
10
10
|
export interface ExtendedLayoutDefinition extends LayoutDefinition {
|
|
11
11
|
template: string;
|
|
12
12
|
}
|
|
13
13
|
export interface ZoneDefinition {
|
|
14
|
+
key: string;
|
|
14
15
|
displayName: string;
|
|
15
16
|
description: string;
|
|
16
|
-
minNodes?: number;
|
|
17
|
-
maxNodes?: number;
|
|
18
17
|
}
|
|
19
|
-
|
|
20
|
-
|
|
18
|
+
/**
|
|
19
|
+
* Layout option value types.
|
|
20
|
+
*/
|
|
21
|
+
export declare const LayoutOptionValueType: {
|
|
22
|
+
readonly stringEnum: "string-enum";
|
|
23
|
+
readonly boolean: "boolean";
|
|
24
|
+
readonly text: "text";
|
|
25
|
+
};
|
|
26
|
+
export declare type LayoutOptionValueType = typeof LayoutOptionValueType[keyof typeof LayoutOptionValueType];
|
|
27
|
+
export declare const LayoutOptionValueTypes: readonly ["string-enum", "boolean", "text"];
|
|
28
|
+
/**
|
|
29
|
+
* Layout property types.
|
|
30
|
+
*/
|
|
31
|
+
export declare const LayoutPropertyType: {
|
|
32
|
+
readonly string: "string";
|
|
33
|
+
readonly boolean: "boolean";
|
|
34
|
+
};
|
|
35
|
+
export declare type LayoutPropertyType = typeof LayoutPropertyType[keyof typeof LayoutPropertyType];
|
|
36
|
+
export interface PropertyDefinition {
|
|
37
|
+
title: string;
|
|
21
38
|
description: string;
|
|
22
|
-
|
|
39
|
+
type: LayoutPropertyType;
|
|
40
|
+
enum?: string[] | boolean[];
|
|
23
41
|
}
|
|
24
42
|
/**
|
|
25
43
|
* Renders a layout using Handlebars templating
|
|
26
44
|
*
|
|
27
45
|
* @param templateContent The Handlebars template string
|
|
28
46
|
* @param zoneContents Content for each zone as single concatenated strings
|
|
29
|
-
* @param
|
|
47
|
+
* @param layoutProperties Properties for the layout (can include boolean values for boolean-type properties)
|
|
30
48
|
* @param layoutDefinition Optional layout definition object to pass to the template
|
|
31
49
|
* @returns The rendered HTML
|
|
32
50
|
*/
|
|
33
|
-
export declare function renderLayout(templateContent: string, zoneContents: Record<string, string>,
|
|
51
|
+
export declare function renderLayout(templateContent: string, zoneContents: Record<string, string>, layoutProperties: Record<string, string | boolean>, layoutDefinition?: ExtendedLayoutDefinition): Promise<string>;
|
package/lib/page/utils/render.js
CHANGED
|
@@ -12,41 +12,95 @@ var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
|
12
12
|
return (mod && mod.__esModule) ? mod : { "default": mod };
|
|
13
13
|
};
|
|
14
14
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
15
|
-
exports.renderLayout = void 0;
|
|
15
|
+
exports.renderLayout = exports.LayoutPropertyType = exports.LayoutOptionValueTypes = exports.LayoutOptionValueType = void 0;
|
|
16
16
|
const handlebars_1 = __importDefault(require("handlebars"));
|
|
17
|
+
/**
|
|
18
|
+
* Layout option value types.
|
|
19
|
+
*/
|
|
20
|
+
exports.LayoutOptionValueType = {
|
|
21
|
+
stringEnum: 'string-enum',
|
|
22
|
+
boolean: 'boolean',
|
|
23
|
+
text: 'text',
|
|
24
|
+
};
|
|
25
|
+
exports.LayoutOptionValueTypes = [
|
|
26
|
+
exports.LayoutOptionValueType.stringEnum,
|
|
27
|
+
exports.LayoutOptionValueType.boolean,
|
|
28
|
+
exports.LayoutOptionValueType.text,
|
|
29
|
+
];
|
|
30
|
+
/**
|
|
31
|
+
* Layout property types.
|
|
32
|
+
*/
|
|
33
|
+
exports.LayoutPropertyType = {
|
|
34
|
+
string: 'string',
|
|
35
|
+
boolean: 'boolean',
|
|
36
|
+
};
|
|
37
|
+
/**
|
|
38
|
+
* Registers global Handlebars helpers
|
|
39
|
+
* TODO: Consider how these can be shared with page-contents-lib
|
|
40
|
+
*/
|
|
41
|
+
function registerGlobalHelpers() {
|
|
42
|
+
handlebars_1.default.registerHelper('toLowerCase', function (text) {
|
|
43
|
+
return typeof text === 'string' ? text.toLowerCase() : '';
|
|
44
|
+
});
|
|
45
|
+
handlebars_1.default.registerHelper('toUpperCase', function (text) {
|
|
46
|
+
return typeof text === 'string' ? text.toUpperCase() : '';
|
|
47
|
+
});
|
|
48
|
+
handlebars_1.default.registerHelper('ifEq', function (first, second, options) {
|
|
49
|
+
if (first === second) {
|
|
50
|
+
return options.fn(this);
|
|
51
|
+
}
|
|
52
|
+
else {
|
|
53
|
+
return options.inverse(this);
|
|
54
|
+
}
|
|
55
|
+
});
|
|
56
|
+
handlebars_1.default.registerHelper('ifNotEq', function (first, second, options) {
|
|
57
|
+
if (first !== second) {
|
|
58
|
+
return options.fn(this);
|
|
59
|
+
}
|
|
60
|
+
else {
|
|
61
|
+
return options.inverse(this);
|
|
62
|
+
}
|
|
63
|
+
});
|
|
64
|
+
handlebars_1.default.registerHelper('stringify', function (object) {
|
|
65
|
+
return JSON.stringify(object);
|
|
66
|
+
});
|
|
67
|
+
}
|
|
17
68
|
/**
|
|
18
69
|
* Renders a layout using Handlebars templating
|
|
19
70
|
*
|
|
20
71
|
* @param templateContent The Handlebars template string
|
|
21
72
|
* @param zoneContents Content for each zone as single concatenated strings
|
|
22
|
-
* @param
|
|
73
|
+
* @param layoutProperties Properties for the layout (can include boolean values for boolean-type properties)
|
|
23
74
|
* @param layoutDefinition Optional layout definition object to pass to the template
|
|
24
75
|
* @returns The rendered HTML
|
|
25
76
|
*/
|
|
26
|
-
function renderLayout(templateContent, zoneContents,
|
|
77
|
+
function renderLayout(templateContent, zoneContents, layoutProperties, layoutDefinition) {
|
|
27
78
|
return __awaiter(this, void 0, void 0, function* () {
|
|
28
79
|
try {
|
|
80
|
+
registerGlobalHelpers();
|
|
29
81
|
const template = handlebars_1.default.compile(templateContent);
|
|
30
82
|
const zones = {};
|
|
31
83
|
for (const [zoneName, content] of Object.entries(zoneContents)) {
|
|
32
84
|
// Wrap the content in SafeString to prevent HTML escaping
|
|
33
85
|
zones[zoneName] = new handlebars_1.default.SafeString(content);
|
|
34
86
|
}
|
|
35
|
-
const
|
|
36
|
-
for (const [key, value] of Object.entries(
|
|
37
|
-
const
|
|
38
|
-
if (
|
|
39
|
-
typeof
|
|
40
|
-
'selectedValue' in
|
|
41
|
-
|
|
87
|
+
const flattenedProperties = {};
|
|
88
|
+
for (const [key, value] of Object.entries(layoutProperties)) {
|
|
89
|
+
const propertyValue = value;
|
|
90
|
+
if (propertyValue &&
|
|
91
|
+
typeof propertyValue === 'object' &&
|
|
92
|
+
'selectedValue' in propertyValue) {
|
|
93
|
+
flattenedProperties[key] = propertyValue.selectedValue;
|
|
42
94
|
}
|
|
43
95
|
else {
|
|
44
|
-
|
|
96
|
+
flattenedProperties[key] = propertyValue;
|
|
45
97
|
}
|
|
46
98
|
}
|
|
47
99
|
return template({
|
|
48
100
|
zones: zones,
|
|
49
|
-
options
|
|
101
|
+
// Still provide options for existing templates that use it
|
|
102
|
+
options: flattenedProperties,
|
|
103
|
+
properties: flattenedProperties,
|
|
50
104
|
layout: layoutDefinition,
|
|
51
105
|
});
|
|
52
106
|
}
|
|
@@ -23,21 +23,21 @@ describe('renderLayout', () => {
|
|
|
23
23
|
const zoneContents = {
|
|
24
24
|
main: '<p>Content 1</p><p>Content 2</p>',
|
|
25
25
|
};
|
|
26
|
-
const
|
|
27
|
-
const result = yield (0, render_1.renderLayout)(templateContent, zoneContents,
|
|
26
|
+
const layoutProperties = {};
|
|
27
|
+
const result = yield (0, render_1.renderLayout)(templateContent, zoneContents, layoutProperties);
|
|
28
28
|
expect(result).toContain('<div class="container">');
|
|
29
29
|
expect(result).toContain('<p>Content 1</p>');
|
|
30
30
|
expect(result).toContain('<p>Content 2</p>');
|
|
31
31
|
}));
|
|
32
|
-
it('should apply layout
|
|
32
|
+
it('should apply layout properties to the template (with deprecated options support)', () => __awaiter(void 0, void 0, void 0, function* () {
|
|
33
33
|
const templateContent = '<div class="container {{options.size}}">{{zones.main}}</div>';
|
|
34
34
|
const zoneContents = {
|
|
35
35
|
main: '<p>Content</p>',
|
|
36
36
|
};
|
|
37
|
-
const
|
|
37
|
+
const layoutProperties = {
|
|
38
38
|
size: 'large',
|
|
39
39
|
};
|
|
40
|
-
const result = yield (0, render_1.renderLayout)(templateContent, zoneContents,
|
|
40
|
+
const result = yield (0, render_1.renderLayout)(templateContent, zoneContents, layoutProperties);
|
|
41
41
|
expect(result).toContain('<div class="container large">');
|
|
42
42
|
expect(result).toContain('<p>Content</p>');
|
|
43
43
|
}));
|
|
@@ -52,8 +52,8 @@ describe('renderLayout', () => {
|
|
|
52
52
|
main: '<p>Main Content</p>',
|
|
53
53
|
sidebar: '<div>Sidebar Item 1</div><div>Sidebar Item 2</div>',
|
|
54
54
|
};
|
|
55
|
-
const
|
|
56
|
-
const result = yield (0, render_1.renderLayout)(templateContent, zoneContents,
|
|
55
|
+
const layoutProperties = {};
|
|
56
|
+
const result = yield (0, render_1.renderLayout)(templateContent, zoneContents, layoutProperties);
|
|
57
57
|
expect(result).toContain('<div class="main"><p>Main Content</p></div>');
|
|
58
58
|
expect(result).toContain('<div class="sidebar"><div>Sidebar Item 1</div><div>Sidebar Item 2</div></div>');
|
|
59
59
|
}));
|
|
@@ -67,10 +67,10 @@ describe('renderLayout', () => {
|
|
|
67
67
|
const zoneContents = {
|
|
68
68
|
main: '<p>Content</p>',
|
|
69
69
|
};
|
|
70
|
-
const
|
|
71
|
-
theme:
|
|
70
|
+
const layoutProperties = {
|
|
71
|
+
theme: 'dark',
|
|
72
72
|
};
|
|
73
|
-
const result = yield (0, render_1.renderLayout)(templateContent, zoneContents,
|
|
73
|
+
const result = yield (0, render_1.renderLayout)(templateContent, zoneContents, layoutProperties);
|
|
74
74
|
expect(result).toContain('<div class="theme-dark">');
|
|
75
75
|
}));
|
|
76
76
|
it('should use SafeString to prevent HTML escaping', () => __awaiter(void 0, void 0, void 0, function* () {
|
|
@@ -98,13 +98,13 @@ describe('renderLayout', () => {
|
|
|
98
98
|
name: 'test-layout',
|
|
99
99
|
displayName: 'Test Layout',
|
|
100
100
|
template: templateContent,
|
|
101
|
-
zones:
|
|
102
|
-
|
|
101
|
+
zones: [
|
|
102
|
+
{
|
|
103
|
+
key: 'main',
|
|
103
104
|
displayName: 'Main Content',
|
|
104
105
|
description: 'Main content area',
|
|
105
|
-
minNodes: 1,
|
|
106
106
|
},
|
|
107
|
-
|
|
107
|
+
],
|
|
108
108
|
};
|
|
109
109
|
const result = yield (0, render_1.renderLayout)(templateContent, zoneContents, {}, layoutDefinition);
|
|
110
110
|
expect(result).toContain('<div class="test-layout">');
|
|
@@ -3,7 +3,7 @@ interface DevServerOptions {
|
|
|
3
3
|
configPath: string;
|
|
4
4
|
layoutDefinition: ExtendedLayoutDefinition;
|
|
5
5
|
zoneContent: Record<string, string[]>;
|
|
6
|
-
|
|
6
|
+
layoutProperties: Record<string, string | boolean>;
|
|
7
7
|
stylesheet?: string;
|
|
8
8
|
port: number;
|
|
9
9
|
openBrowser: boolean;
|
package/lib/page/utils/server.js
CHANGED
|
@@ -49,7 +49,7 @@ const logger = (0, dx_logger_lib_1.getLogger)({
|
|
|
49
49
|
});
|
|
50
50
|
function startDevServer(options) {
|
|
51
51
|
return __awaiter(this, void 0, void 0, function* () {
|
|
52
|
-
const { configPath,
|
|
52
|
+
const { configPath, layoutProperties, port, openBrowser } = options;
|
|
53
53
|
// Create HTTP server with stateless request handling
|
|
54
54
|
const server = http.createServer((req, res) => __awaiter(this, void 0, void 0, function* () {
|
|
55
55
|
const url = new URL(req.url || '/', `http://localhost:${port}`);
|
|
@@ -82,7 +82,7 @@ function startDevServer(options) {
|
|
|
82
82
|
stylesheetContent = yield fs.readFile(options.stylesheet, 'utf-8');
|
|
83
83
|
}
|
|
84
84
|
// Use the renderLayout helper
|
|
85
|
-
const html = yield (0, render_1.renderLayout)(layoutDefinition.template, zoneContents,
|
|
85
|
+
const html = yield (0, render_1.renderLayout)(layoutDefinition.template, zoneContents, layoutProperties, layoutDefinition);
|
|
86
86
|
// for hot-reload script
|
|
87
87
|
const fullHtml = `
|
|
88
88
|
<!DOCTYPE html>
|
|
@@ -76,25 +76,25 @@ describe('startDevServer', () => {
|
|
|
76
76
|
displayName: 'Test Layout',
|
|
77
77
|
description: 'A test layout',
|
|
78
78
|
entry: 'path/to/template.html',
|
|
79
|
-
template: '<div class="{{
|
|
80
|
-
zones:
|
|
81
|
-
|
|
79
|
+
template: '<div class="{{properties.size}}">{{{zones.main}}}</div>',
|
|
80
|
+
zones: [
|
|
81
|
+
{
|
|
82
|
+
key: 'main',
|
|
82
83
|
displayName: 'Main Content',
|
|
83
84
|
description: 'Main content area',
|
|
84
|
-
minNodes: 1,
|
|
85
85
|
},
|
|
86
|
-
|
|
86
|
+
{
|
|
87
|
+
key: 'sidebar',
|
|
87
88
|
displayName: 'Sidebar',
|
|
88
89
|
description: 'Sidebar content',
|
|
89
|
-
minNodes: 0,
|
|
90
|
-
maxNodes: 3,
|
|
91
90
|
},
|
|
92
|
-
|
|
93
|
-
|
|
91
|
+
],
|
|
92
|
+
properties: {
|
|
94
93
|
size: {
|
|
95
|
-
|
|
94
|
+
title: 'Size',
|
|
96
95
|
description: 'Container size',
|
|
97
|
-
|
|
96
|
+
enum: ['small', 'medium', 'large'],
|
|
97
|
+
type: 'string',
|
|
98
98
|
},
|
|
99
99
|
},
|
|
100
100
|
};
|
|
@@ -105,7 +105,7 @@ describe('startDevServer', () => {
|
|
|
105
105
|
main: ['/path/to/main.html'],
|
|
106
106
|
sidebar: ['/path/to/sidebar1.html', '/path/to/sidebar2.html'],
|
|
107
107
|
},
|
|
108
|
-
|
|
108
|
+
layoutProperties: {
|
|
109
109
|
size: 'large',
|
|
110
110
|
},
|
|
111
111
|
stylesheet: '/path/to/styles.css',
|
|
@@ -221,7 +221,7 @@ describe('startDevServer', () => {
|
|
|
221
221
|
expect(mockRenderLayout).toHaveBeenCalledWith(mockLayoutDefinition.template, expect.objectContaining({
|
|
222
222
|
main: '<p>Main content</p>',
|
|
223
223
|
sidebar: '<div>Sidebar item</div><div>Sidebar item</div>',
|
|
224
|
-
}), mockServerOptions.
|
|
224
|
+
}), mockServerOptions.layoutProperties, mockLayoutDefinition);
|
|
225
225
|
}));
|
|
226
226
|
it('should set up check-changes endpoint for client polling', () => __awaiter(void 0, void 0, void 0, function* () {
|
|
227
227
|
const mockRequest = {
|